| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6b21bfc commit d3deaa4
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -341,6 +341,23 @@ describe('markets plugin', () => { | |||
| 341 | 341 | assert.ok(sorted, 'ordered by net worth, descending'); | |
| 342 | 342 | }); | |
| 343 | 343 | ||
| 344 | + it('the emitted client script actually parses (template-literal escapes bite)', async () => { | ||
| 345 | + // ui.js is one big server-side template literal, so a backslash escape | ||
| 346 | + // is consumed before the browser sees it: a regex like /\\/m\\// arrives | ||
| 347 | + // as //m// and the ENTIRE app dies with "Unexpected token". Meta-tag | ||
| 348 | + // assertions cannot see that; parsing can. | ||
| 349 | + const { renderUi } = await import('./ui.js'); | ||
| 350 | + for (const opts of [{}, { accounts: true, brand: 'x', market: { id: 'a', title: 'T', category: 'C', status: 'open', closesAt: Date.now() + 8.64e7, outcomes: ['Yes', 'No'], prices: [0.5, 0.5] } }]) { | ||
| 351 | + const html = renderUi('/markets', opts); | ||
| 352 | + const scripts = [...html.matchAll(/<script(?![^>]*src=)[^>]*>([\s\S]*?)<\/script>/g)].map((m) => m[1]); | ||
| 353 | + assert.ok(scripts.length >= 1, 'the page ships an inline script'); | ||
| 354 | + for (const src of scripts) { | ||
| 355 | + // new Function COMPILES without executing: a SyntaxError throws here. | ||
| 356 | + assert.doesNotThrow(() => new Function(src), 'inline client script must parse'); | ||
| 357 | + } | ||
| 358 | + } | ||
| 359 | + }); | ||
| 360 | + | ||
| 344 | 361 | it('exposes the category facets for topic browsing', async () => { | |
| 345 | 362 | const { categories } = await json(await call(null, 'GET', '/categories'), 200); | |
| 346 | 363 | assert.ok(Array.isArray(categories), 'a facet list is public'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1618,9 +1618,14 @@ ${ogMeta} | |||
| 1618 | 1618 | // Both server-visible forms are honoured: the path (/m/<id>) and the | |
| 1619 | 1619 | // query (?m=<id>). A fragment never reaches the server, which is why | |
| 1620 | 1620 | // shared #m/ links unfurled as the generic card. | |
| 1621 | - const byPath = /\/m\/([A-Za-z0-9_-]+)\/?$/.exec(location.pathname); | ||
| 1621 | + // NO REGEX HERE: this file is one big server-side template literal, so | ||
| 1622 | + // a backslash escape is consumed before the browser ever sees it and | ||
| 1623 | + // /\/m\// arrives as //m// — which is a syntax error that takes the | ||
| 1624 | + // whole app down. Plain string work is immune. | ||
| 1625 | + const seg = location.pathname.split('/').filter(Boolean); | ||
| 1626 | + const byPath = seg.length >= 2 && seg[seg.length - 2] === 'm' ? seg[seg.length - 1] : null; | ||
| 1622 | 1627 | const byQuery = new URLSearchParams(location.search).get('m'); | |
| 1623 | - const id = (byPath && byPath[1]) || byQuery; | ||
| 1628 | + const id = byPath || byQuery; | ||
| 1624 | 1629 | if (!id || location.hash) return; | |
| 1625 | 1630 | history.replaceState(null, '', (PREFIX || '/') + '#m/' + id); | |
| 1626 | 1631 | })(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments