From 0dc9765f775cfbb9222ccbc09b55c1d6113581ab Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 2 Oct 2026 08:08:26 +0000 Subject: [PATCH] The preview read is DEV-only: no built bundle can open India's withheld poster from a global previewUnreleased() is import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true, and the India tab uses the same expression inline, so every BUILT bundle compiles both away. A page, an embed or a reader's console setting the global can no longer print the withheld poster from production; only the Vite dev server (the pilot's Plugin Studio, whose element bootstrap sets it) previews it. posterWithheld()/largeSheet() take the preview state as an optional argument, so a gate can test an open preview directly. check:country-sheet-one-path arm G: a global alone does NOT open the preview outside DEV; withheld by default; an open preview prints 66x66; the India tab is gated on the DEV read; and the built tree mentions the flag 0 times (the stale ungated build failed this check, as it should). A poison for each. --- library/packet/countrySheet.js | 12 +++++---- .../country-posters/country-posters-mfe.vue | 4 +-- .../scripts/check-country-sheet-one-path.mjs | 27 +++++++++++-------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/library/packet/countrySheet.js b/library/packet/countrySheet.js index a5c3150f1..2be3ed1d9 100644 --- a/library/packet/countrySheet.js +++ b/library/packet/countrySheet.js @@ -91,16 +91,18 @@ const largeOf = (item) => (item && LARGE_SHEETS[String(item.iso3 || '').toUpperC */ // WHAT of a whole country is withheld: its POSTER, and/or its ROWS (its prayer list, packet and cards). // India's whole-country poster is withheld EVERYWHERE BY DEFAULT (it is not released yet). It prints only in an -// explicit PREVIEW context: a host page that sets globalThis.DXM_PREVIEW_UNRELEASED = true (the Plugin Studio on the -// pilot, where it is reviewed). Never by a URL parameter. Its rows (about a thousand pages) stay withheld everywhere. +// explicit PREVIEW context: the Vite DEV server (the pilot's Plugin Studio, where it is reviewed), whose element +// bootstrap sets globalThis.DXM_PREVIEW_UNRELEASED. The read is gated on import.meta.env.DEV too, so every BUILT bundle +// compiles it away: no page, embed or console can set a global and print it from production. Never by a URL +// parameter. Its rows (about a thousand pages) stay withheld everywhere. export const WITHHELD = { IND: { poster: true, rows: true, preview: true } } -export const previewUnreleased = () => typeof globalThis !== 'undefined' && globalThis.DXM_PREVIEW_UNRELEASED === true +export const previewUnreleased = () => Boolean(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true) const withheldOf = (item) => (item && !item.indiaState && WITHHELD[String(item.iso3 || '').toUpperCase()]) || null -export function posterWithheld(item) { const w = withheldOf(item); return Boolean(w?.poster) && !(w.preview && previewUnreleased()) } +export function posterWithheld(item, open = previewUnreleased()) { const w = withheldOf(item); return Boolean(w?.poster) && !(w.preview && open) } export function rowsWithheld(item) { return Boolean(withheldOf(item)?.rows) } /** The poster's withholding (the name Poster Design reads). */ export function sheetWithheld(item) { return posterWithheld(item) } -export function largeSheet(item) { return posterWithheld(item) ? null : largeOf(item) } +export function largeSheet(item, open = previewUnreleased()) { return posterWithheld(item, open) ? null : largeOf(item) } // The sheet format a request gets: a large-sheet country prints at the large size ASKED FOR when it has one (66x66, // 36x60), else at its default (60x96); a packet's page 1 stays a page; every other country gets exactly what it asked. export function sheetFormatFor(item, format) { diff --git a/plugin-designs/country-posters/country-posters-mfe.vue b/plugin-designs/country-posters/country-posters-mfe.vue index e1b5f6f09..88bc2dd64 100644 --- a/plugin-designs/country-posters/country-posters-mfe.vue +++ b/plugin-designs/country-posters/country-posters-mfe.vue @@ -35,7 +35,6 @@ import { buildPaneColumn, PANE_GRID_STYLES } from '@map/layouts/pane-wrapper/pan import { useShadowStyles } from '@map/composables/useShadowStyles.js' import ViewSelect from '@map/components/ViewSelect.vue' import { countryName } from '@map/utils/countryName.js' -import { previewUnreleased } from '@map/packet/countrySheet.js' import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js' import ShareButton from '@map/components/map-controls/ShareButton.vue' import { useI18n } from 'vue-i18n' @@ -71,7 +70,8 @@ const spec = computed(() => ({ // strip desynchronises from the scroll column. panes: [ { id: 'wall', label: t('tabs.countryPosters'), at: [0, 0], config: { profile: 'country-poster-wall' } }, - ...(previewUnreleased() ? [INDIA_PANE] : []), + // the same DEV-gated read as countrySheet.js previewUnreleased(), INLINE so every build drops the pane entirely + ...(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true ? [INDIA_PANE] : []), { id: 'map', label: t('tabs.map'), at: [2, 0], config: { profile: 'countries-map' } }, ], // The switcher lives in this element's own header, so the layout's "… below ↓" diff --git a/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs b/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs index b88a5690e..559204dd5 100644 --- a/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs +++ b/plugin-designs/country-posters/scripts/check-country-sheet-one-path.mjs @@ -17,7 +17,7 @@ // absent from the data gets no series. // // Exit 0 pass, 1 fail, 75 not measured. `--self-test` plants each defect and requires its arm to fail. -import { readFileSync, readdirSync, statSync } from 'node:fs' +import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs' import { join, relative, dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { parse as parseSfc, babelParse } from '@vue/compiler-sfc' @@ -189,18 +189,17 @@ export function armF(srcs) { export async function armG(cs, srcs, mfeSrc = '') { const fails = [] const ind = { id: 'country-ind', iso3: 'IND', label: 'India' } + // (a) by default: outside the Vite dev server (here: no import.meta.env.DEV) the preview is shut EVEN IF a page or a + // console sets the global; (b) an open preview (passed explicitly) prints the poster at 66x66. const was = globalThis.DXM_PREVIEW_UNRELEASED try { - delete globalThis.DXM_PREVIEW_UNRELEASED - if (!cs.posterWithheld(ind) || cs.largeSheet(ind)) fails.push("(a) India's whole-country poster is NOT withheld by default: it would ship to every host") - if (!cs.rowsWithheld(ind)) fails.push("India's rows are not withheld: its list/packet/cards (~1,000 pages) would print") globalThis.DXM_PREVIEW_UNRELEASED = true - const big = cs.largeSheet(ind) - if (cs.posterWithheld(ind) || !big || big.format !== '66x66') fails.push(`(b) with the preview flag India's poster does not print at 66x66 (${big && big.format})`) - if (!cs.rowsWithheld(ind)) fails.push("(b) the preview flag also released India's rows") - globalThis.DXM_PREVIEW_UNRELEASED = 'true' - if (!cs.posterWithheld(ind)) fails.push('the preview flag is honoured for a non-boolean value (only === true opens it)') + if (cs.previewUnreleased()) fails.push('(a) the preview opens outside the dev server from a global alone: a page/console could print the withheld poster from production') + if (!cs.posterWithheld(ind) || cs.largeSheet(ind)) fails.push("(a) India's whole-country poster is NOT withheld by default: it would ship to every host") } finally { if (was === undefined) delete globalThis.DXM_PREVIEW_UNRELEASED; else globalThis.DXM_PREVIEW_UNRELEASED = was } + if (!cs.rowsWithheld(ind)) fails.push("India's rows are not withheld: its list/packet/cards (~1,000 pages) would print") + const big = cs.largeSheet(ind, true) + if (cs.posterWithheld(ind, true) || !big || big.format !== '66x66') fails.push(`(b) an open preview does not print India's poster at 66x66 (${big && big.format})`) if (cs.sheetWithheld(ind) !== cs.posterWithheld(ind)) fails.push('sheetWithheld() no longer means the poster') if (cs.rowsWithheld({ iso3: 'IND', indiaState: true, id: 'india-state-gujarat' })) fails.push('an India STATE card has withheld rows (only the whole country may)') for (const [name, src] of Object.entries(srcs)) { @@ -208,7 +207,12 @@ export async function armG(cs, srcs, mfeSrc = '') { if (!/rowsWithheld\((?:c|item)\) \? (?:withheld\(\)|Promise\.reject\(keyedError\('messages\.countryPosterTooMany')/.test(src)) fails.push(`${name}: withheld ROWS are not withheld (its list/packet/cards would print)`) if (/sheetWithheld\(/.test(src)) fails.push(`${name}: still reads sheetWithheld() (ambiguous: poster or rows?)`) } - if (mfeSrc && !/\.\.\.\(previewUnreleased\(\) \? \[INDIA_PANE\] : \[\]\)/.test(mfeSrc)) fails.push('country-posters-mfe: the India tab is not gated on previewUnreleased()') + if (mfeSrc && !/\.\.\.\(import\.meta\.env && import\.meta\.env\.DEV && globalThis\.DXM_PREVIEW_UNRELEASED === true \? \[INDIA_PANE\] : \[\]\)/.test(mfeSrc)) fails.push('country-posters-mfe: the India tab is not gated on the DEV-only preview read') + // the BUILT tree never mentions the flag (every read is compiled away) + try { + const pub = join(ROOT, 'public/dx-maps') + for (const b of readdirSync(pub)) { const f = join(pub, b, `${b}.js`); if (existsSync(f) && readFileSync(f, 'utf8').includes('DXM_PREVIEW_UNRELEASED')) fails.push(`the built ${b}.js mentions DXM_PREVIEW_UNRELEASED (a preview read survived the build)`) } + } catch (_) { /* no built tree here */ } return fails } @@ -273,8 +277,9 @@ async function main() { ['the names lose their licence line', () => armD(states, areas, { ...meta, licence: '' })], ['India\'s poster released by default', async () => armG({ ...CS, posterWithheld: () => false, largeSheet: () => CS.LARGE_SHEETS.IND }, { wall: files[wall], regions: REG }, MFE)], ['the preview flag ignored', async () => armG({ ...CS, posterWithheld: () => true, largeSheet: () => null }, { wall: files[wall], regions: REG }, MFE)], + ['the preview read without DEV (a global opens production)', async () => armG({ ...CS, previewUnreleased: () => globalThis.DXM_PREVIEW_UNRELEASED === true }, { wall: files[wall], regions: REG }, MFE)], ['India\'s rows un-withheld', async () => armG({ ...CS, rowsWithheld: () => false }, { wall: files[wall], regions: REG }, MFE)], - ['the India tab shown without the preview flag', async () => armG(CS, { wall: files[wall], regions: REG }, MFE.replace('...(previewUnreleased() ? [INDIA_PANE] : []),', 'INDIA_PANE,'))], + ['the India tab shown without the preview flag', async () => armG(CS, { wall: files[wall], regions: REG }, MFE.replace('...(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true ? [INDIA_PANE] : []),', 'INDIA_PANE,'))], ['the wall prints the list of a country whose rows are withheld', async () => armG(CS, { wall: files[wall].replace('rowsWithheld(c) ? withheld() : ', ''), regions: REG })], ['a helper that mutates the shared ADM1', () => armC({ ...H, stateGeometry: (z, a, n) => { (a.features || []).forEach((f) => { if (f.properties.shapeName === n) f.properties.listId = z.id }); return H.stateGeometry(z, a, n) } }, states, JSON.parse(JSON.stringify(adm1Ind)))], ]