A single-file build inlines every import, dynamic ones included, so packet-strings.json (~115 KB minified: every language's prayer points, captions and headings) rode in every bundle that can print a packet, list or card deck. With the wall's print messages translated, country-posters crossed the 1.5 MB ceiling (1,502,226 bytes). The table is now served at <assets>/lib/packet-strings.json and fetched at the first print by library/packet/packetStrings.js, the same move as the sheet generator and ne-places.json: - Pinned like them: PACKET_STRINGS_SHA384 in poster-render.integrity.js (pin-poster-render.mjs); the text is hashed before it is parsed, and an unmatched table is not used. These strings are laid into the print document, so an unverified copy never reaches it. - English is bundled as the fallback (a named import of the JSON, so only the en block ships): with no asset base, on file://, on a network error or an integrity mismatch the print comes out in English and the console says why. A print never fails for want of this file. - generate-tree copies it into the pool AND the served allowlist; verified by regenerating the tree (the served copy is byte-identical to source). - check:poster-render-pinned, two-sided: the pin matches source and served copy, the genuine copy loads all 11 languages, a tampered copy falls back to English (mutation-tested: disabling the hash check fails it), and an unserved one falls back rather than failing. - sha384 is one shared helper (library/packet/sha384.js). Sizes: country-posters 1,378,463 (was 1,484,053), my-upg-100-list 1,143,456 (was 1,243,509), regions-dashboard 973,802 (was 1,079,448).
49 lines
2.7 KiB
JavaScript
49 lines
2.7 KiB
JavaScript
/**
|
|
* packetStrings — the text of every printed packet, list and card deck, in all 11 languages, FETCHED at print
|
|
* time from the served tree (<assets>/lib/packet-strings.json) instead of bundled.
|
|
*
|
|
* WHY. A single-file build inlines every import, dynamic ones included, so the whole table (~115 KB
|
|
* minified: every language's prayer points, captions and headings) rode in every bundle that can print —
|
|
* country-posters crossed the 1.5 MB ceiling once its wall's print messages were translated. A reader needs
|
|
* the table only when they print. Same move as the sheet generator and ne-places.json (posterSheets.js).
|
|
*
|
|
* PINNED like them (PACKET_STRINGS_SHA384): the text is hashed before it is parsed, and a table that is not
|
|
* the pinned one is not used. These strings are laid into the print document, so an unverified copy must
|
|
* never reach it.
|
|
*
|
|
* ★ ENGLISH IS BUNDLED AS THE FALLBACK, so a print never fails for want of this file: without an asset base
|
|
* (a host that sets none), on file:// where a page cannot fetch, on a network error, or on an integrity
|
|
* mismatch, the packet prints in English and the console says why. `{ en }` is a named import of the JSON, so
|
|
* only the English block is bundled; the other ten languages stay on the server.
|
|
*/
|
|
import { en as EN } from '../scripts/posters/packet-strings.json'
|
|
import { PACKET_STRINGS_SHA384 } from './poster-render.integrity.js'
|
|
import { sha384 } from './sha384.js'
|
|
|
|
// Captured when the module evaluates, as in posterSheets.js: a relative asset base resolves against the page
|
|
// that loaded the map, even if a single-page host changes its URL before the click.
|
|
const PAGE_BASE = globalThis.document?.baseURI || undefined
|
|
const ENGLISH_ONLY = Object.freeze({ en: EN })
|
|
const cache = new Map()
|
|
|
|
/** The full table ({ en, es, …, zh }), or `{ en }` when the served copy cannot be used. Never rejects. */
|
|
export function loadPacketStrings(assetsBase) {
|
|
if (!assetsBase) return Promise.resolve(ENGLISH_ONLY)
|
|
let url
|
|
try { url = new URL(`${assetsBase}/lib/packet-strings.json`, PAGE_BASE).href } catch { return Promise.resolve(ENGLISH_ONLY) }
|
|
if (!cache.has(url)) {
|
|
const p = (async () => {
|
|
const res = await fetch(url, { cache: 'force-cache' })
|
|
if (!res.ok) throw new Error(`not served (${res.status})`)
|
|
const text = await res.text()
|
|
if ((await sha384(text)) !== PACKET_STRINGS_SHA384) throw new Error('failed its integrity check')
|
|
return JSON.parse(text)
|
|
})().catch((e) => {
|
|
console.warn(`[packetStrings] ${url}: ${e?.message || e} — printing in English`)
|
|
cache.delete(url) // a later print tries again rather than inheriting one failure
|
|
return ENGLISH_ONLY
|
|
})
|
|
cache.set(url, p)
|
|
}
|
|
return cache.get(url)
|
|
}
|