Poster sections: one type registry per page; the element takes a columns attribute
- ONE REGISTRY PER PAGE. Every bundle inlines its own copy of posterTypes.js, and <dxm-poster-section> is defined by whichever bundle loads first, so with a module-level Map that element saw only its own bundle's types: type="upg100-list" rendered "none" when the country-posters bundle had defined it. The registry now lives on the page (globalThis.__DXM_POSTER_TYPES__, created once): every copy registers into it and reads from it; a name registered twice keeps the first (a dev warning; the built-in country is silent); every registration is announced on window (dxm-poster-type-registered), and PosterSectionView looks again when its type arrives after it asked. - COLUMNS (carried from the Poster Sections plugin PR so the element's library lands once): columns="1".."5" maps to profile-config.columns and to PosterWall's existing columns prop; absent or out of range is today's default, and a reader's saved per-row choice still wins. - check:poster-type-registry (gates:pre; self-test in gates:post): two independent copies of the module side by side, as two bundles on one page; a type registered through one is found through the other; first wins; announced. The self-test's per-bundle registry fails.
This commit is contained in:
parent
078378199a
commit
21e2200029
6 changed files with 152 additions and 21 deletions
81
internal/check-poster-type-registry.mjs
Normal file
81
internal/check-poster-type-registry.mjs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-poster-type-registry.mjs — the poster type registry is ONE per page, whichever bundle's copy asks.
|
||||
*
|
||||
* Every bundle inlines its own copy of library/components/poster/posterTypes.js, and <dxm-poster-section> is
|
||||
* defined by whichever bundle loads first. A module-level registry made that element see only its own bundle's
|
||||
* types: a type another bundle registered rendered "none". So the registry is page-global (globalThis).
|
||||
*
|
||||
* ASSERTS, with TWO independent copies of the module loaded side by side (as two bundles are on one page):
|
||||
* A. a type registered through copy B is found through copy A (the copy that defined the element);
|
||||
* B. the built-in 'country' is listed by both copies, once;
|
||||
* C. a name registered twice keeps the FIRST registration (the second returns false);
|
||||
* D. every registration is announced on window ('dxm-poster-type-registered', { name }), so a section that asked
|
||||
* before its type arrived can render when it does;
|
||||
* E. PosterSectionView.vue re-resolves its type on that event.
|
||||
* `--self-test`: the per-bundle (module-level Map) registry must FAIL arm A.
|
||||
*/
|
||||
import { readFileSync, writeFileSync, mkdtempSync, rmSync, mkdirSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const SRC = join(ROOT, 'library/components/poster/posterTypes.js')
|
||||
const VIEW = join(ROOT, 'library/components/poster/PosterSectionView.vue')
|
||||
|
||||
// A stand-in window that records the announcements.
|
||||
const events = []
|
||||
globalThis.window = { dispatchEvent: (e) => { events.push(e); return true } }
|
||||
globalThis.CustomEvent = class { constructor(type, init) { this.type = type; this.detail = init && init.detail } }
|
||||
|
||||
async function copies(source) {
|
||||
// Two copies = two module instances (as two IIFE bundles each inline one). Written to a scratch dir with a stub
|
||||
// for the lazily imported country adapter, so nothing else loads.
|
||||
const dir = mkdtempSync(join(tmpdir(), 'dxm-pt-'))
|
||||
mkdirSync(join(dir, 'types'))
|
||||
writeFileSync(join(dir, 'types', 'countryPosterType.js'), 'export default { items: () => [], title: () => "", actions: () => ({}) }\n')
|
||||
const code = source.replace(/import\.meta\.env\s*&&\s*import\.meta\.env\.DEV/g, 'false')
|
||||
writeFileSync(join(dir, 'a.mjs'), code); writeFileSync(join(dir, 'b.mjs'), code)
|
||||
const a = await import(pathToFileURL(join(dir, 'a.mjs')).href)
|
||||
const b = await import(pathToFileURL(join(dir, 'b.mjs')).href)
|
||||
return { a, b, done: () => rmSync(dir, { recursive: true, force: true }) }
|
||||
}
|
||||
|
||||
async function audit(source, viewSrc) {
|
||||
const fails = []
|
||||
for (const k of Object.getOwnPropertyNames(globalThis)) if (k === '__DXM_POSTER_TYPES__') delete globalThis[k]
|
||||
events.length = 0
|
||||
const { a, b, done } = await copies(source)
|
||||
try {
|
||||
const fixture = { items: () => [{ id: 'x' }], title: () => 'X', actions: () => ({}) }
|
||||
b.registerPosterType('gate-b-type', fixture)
|
||||
if (!(await a.posterType('gate-b-type'))) fails.push('A: a type registered through one bundle\'s copy is NOT found through another\'s (a per-bundle registry)')
|
||||
const na = a.posterTypeNames(), nb = b.posterTypeNames()
|
||||
if (na.filter((n) => n === 'country').length !== 1 || nb.filter((n) => n === 'country').length !== 1) fails.push(`B: 'country' is not listed exactly once by each copy (${na.join(',')} | ${nb.join(',')})`)
|
||||
const second = a.registerPosterType('gate-b-type', { items: () => [], title: () => 'Y', actions: () => ({}) })
|
||||
const kept = await b.posterType('gate-b-type')
|
||||
if (second !== false || !kept || kept.title() !== 'X') fails.push('C: a name registered twice did not keep the FIRST registration')
|
||||
if (!events.some((e) => e.type === 'dxm-poster-type-registered' && e.detail && e.detail.name === 'gate-b-type')) fails.push('D: a registration is not announced on window (dxm-poster-type-registered)')
|
||||
} finally { done() }
|
||||
if (!/dxm-poster-type-registered|TYPE_REGISTERED_EVENT/.test(viewSrc)) fails.push('E: PosterSectionView.vue does not re-resolve its type when one is registered later')
|
||||
return fails
|
||||
}
|
||||
|
||||
if (process.argv.includes('--self-test')) {
|
||||
// The per-bundle registry this gate exists to stop: a module-level Map.
|
||||
const perBundle = `const LOADERS = new Map([['country', () => import('./types/countryPosterType.js')]])
|
||||
const CACHE = new Map()
|
||||
export function registerPosterType(name, a) { LOADERS.set(name, typeof a === 'function' ? a : () => Promise.resolve({ default: a })); CACHE.delete(name); return true }
|
||||
export function posterType(name) { if (!LOADERS.has(name)) return Promise.resolve(null); if (!CACHE.has(name)) CACHE.set(name, LOADERS.get(name)().then((m) => m.default || m)); return CACHE.get(name) }
|
||||
export const posterTypeNames = () => [...LOADERS.keys()]
|
||||
`
|
||||
const f = await audit(perBundle, readFileSync(VIEW, 'utf8'))
|
||||
const ok = f.some((x) => x.startsWith('A:'))
|
||||
console.log(ok ? `✓ caught: a per-bundle registry -> ${f.find((x) => x.startsWith('A:'))}` : '✗ MISSED: the per-bundle registry passed arm A')
|
||||
process.exitCode = ok ? 0 : 1
|
||||
} else {
|
||||
const f = await audit(readFileSync(SRC, 'utf8'), readFileSync(VIEW, 'utf8'))
|
||||
if (f.length) { for (const x of f) console.log('✗ ' + x); console.log(`FAIL check:poster-type-registry (${f.length})`); process.exitCode = 1 }
|
||||
else console.log('PASS check:poster-type-registry — one registry per page: a type registered through one bundle\'s copy is found through another\'s; the first registration wins; registrations are announced and the section re-resolves')
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<script setup>
|
||||
/**
|
||||
* PosterSectionView.vue — ONE poster section on its own: the profile behind <dxm-poster-section>.
|
||||
* It reads its settings from profile-config (type, scope, collapsible, collapsed, locale, assetsBase), finds the
|
||||
* It reads its settings from profile-config (type, scope, collapsible, collapsed, locale, assetsBase, columns), finds the
|
||||
* poster TYPE in posterTypes.js, and draws a PosterWall in section-only mode: the same section a wall draws (the
|
||||
* same PosterSection, the same print actions and overlay), with no wall head around it.
|
||||
*/
|
||||
import { computed, inject, ref, watch } from 'vue'
|
||||
import { computed, inject, ref, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import PosterWall from './PosterWall.vue'
|
||||
import { posterType } from './posterTypes.js'
|
||||
import { posterType, TYPE_REGISTERED_EVENT } from './posterTypes.js'
|
||||
import { useOptionalT } from '@map/composables/useOptionalT.js'
|
||||
import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
||||
import { getApiBaseUrl } from '@map/api/apiBaseUrl.js'
|
||||
|
|
@ -18,10 +18,18 @@ const t = useOptionalT('')
|
|||
const typeName = computed(() => cfg.value.type || 'country')
|
||||
const adapter = ref(null)
|
||||
const unknownType = ref(false)
|
||||
watch(typeName, async (name) => {
|
||||
async function resolveType(name) {
|
||||
const a = await posterType(name)
|
||||
if (name !== typeName.value) return // the type changed while this one loaded
|
||||
adapter.value = a; unknownType.value = !a
|
||||
}, { immediate: true })
|
||||
}
|
||||
watch(typeName, resolveType, { immediate: true })
|
||||
// ★ A TYPE MAY ARRIVE AFTER THIS SECTION ASKED: the registry is page-global (posterTypes.js), and another bundle
|
||||
// that registers the type (upg100's lists, say) can load after the bundle that defined this element. Look again
|
||||
// when a registration of OUR type is announced.
|
||||
const onTypeRegistered = (e) => { if (e && e.detail && e.detail.name === typeName.value && !adapter.value) resolveType(typeName.value) }
|
||||
onMounted(() => { try { window.addEventListener(TYPE_REGISTERED_EVENT, onTypeRegistered) } catch (_) { /* no window */ } })
|
||||
onBeforeUnmount(() => { try { window.removeEventListener(TYPE_REGISTERED_EVENT, onTypeRegistered) } catch (_) { /* no window */ } })
|
||||
|
||||
const lang = () => cfg.value.locale || getRuntimeLocale() || detectLocale() || 'en'
|
||||
const assetsBase = computed(() => cfg.value.assetsBase || './assets')
|
||||
|
|
@ -52,6 +60,7 @@ const noneLine = computed(() => t('poster.sectionNone', { scope: String(cfg.valu
|
|||
:collapsed-by="collapsedBy"
|
||||
:instance-id="instanceId"
|
||||
:is-dark="cfg.isDark === true"
|
||||
:columns="cfg.columns || 0"
|
||||
:assets-base="assetsBase"
|
||||
:event-name="actions.eventName"
|
||||
:unit="actions.unit"
|
||||
|
|
|
|||
|
|
@ -12,21 +12,55 @@
|
|||
* actions({ assetsBase, apiBase, lang, profileConfig, t }) → the PosterWall props that print a card:
|
||||
* { records, sheet, unsupported, unit, itemCountKey, eventName, downloadPrefix, cardStocks }
|
||||
* }
|
||||
*
|
||||
* ★ ONE REGISTRY PER PAGE, NOT PER BUNDLE. Every bundle inlines its own copy of this file, and <dxm-poster-section>
|
||||
* is defined by whichever bundle loads first. With a module-level Map, that element read only ITS bundle's types,
|
||||
* so type="upg100-list" rendered "none" whenever the country-posters bundle had defined the element. So the
|
||||
* registry lives on the page (globalThis[REGISTRY_KEY], created once): every copy registers into it and reads from
|
||||
* it. A name registered twice keeps the FIRST (a dev warning says so; the built-in 'country' is registered by every
|
||||
* copy and is silent). Every registration is announced (TYPE_REGISTERED_EVENT on window), so a section that asked
|
||||
* before its type arrived renders when it does.
|
||||
*/
|
||||
const LOADERS = new Map([
|
||||
['country', () => import('./types/countryPosterType.js')],
|
||||
])
|
||||
const CACHE = new Map()
|
||||
export const REGISTRY_KEY = '__DXM_POSTER_TYPES__'
|
||||
export const TYPE_REGISTERED_EVENT = 'dxm-poster-type-registered'
|
||||
const VERSION = 1
|
||||
|
||||
/** Register (or replace) a type: an adapter object, or a function that loads one (a dynamic import). */
|
||||
function registry() {
|
||||
const g = typeof globalThis !== 'undefined' ? globalThis : {}
|
||||
let r = g[REGISTRY_KEY]
|
||||
if (!r || !(r.loaders instanceof Map) || !(r.cache instanceof Map)) {
|
||||
r = { version: VERSION, loaders: new Map(), cache: new Map() }
|
||||
try { Object.defineProperty(g, REGISTRY_KEY, { value: r, configurable: true, writable: false }) } catch (_) { g[REGISTRY_KEY] = r }
|
||||
}
|
||||
return r
|
||||
}
|
||||
const toLoader = (a) => (typeof a === 'function' ? a : () => Promise.resolve({ default: a }))
|
||||
function announce(name) {
|
||||
try { if (typeof window !== 'undefined' && typeof CustomEvent === 'function') window.dispatchEvent(new CustomEvent(TYPE_REGISTERED_EVENT, { detail: { name } })) } catch (_) { /* no window */ }
|
||||
}
|
||||
|
||||
/** Register a type: an adapter object, or a function that loads one (a dynamic import). The first registration wins. */
|
||||
export function registerPosterType(name, adapterOrLoader) {
|
||||
LOADERS.set(name, typeof adapterOrLoader === 'function' ? adapterOrLoader : () => Promise.resolve({ default: adapterOrLoader }))
|
||||
CACHE.delete(name)
|
||||
const r = registry()
|
||||
if (r.loaders.has(name)) {
|
||||
if (import.meta.env && import.meta.env.DEV && typeof console !== 'undefined') console.warn(`[dx-maps] poster type "${name}" is already registered on this page; the first registration is kept.`)
|
||||
return false
|
||||
}
|
||||
r.loaders.set(name, toLoader(adapterOrLoader))
|
||||
announce(name)
|
||||
return true
|
||||
}
|
||||
/** The adapter for a type, or null for a type nobody registered. */
|
||||
/** The adapter for a type, or null for a type nobody on this page registered (yet). */
|
||||
export function posterType(name) {
|
||||
if (!LOADERS.has(name)) return Promise.resolve(null)
|
||||
if (!CACHE.has(name)) CACHE.set(name, LOADERS.get(name)().then((m) => m.default || m))
|
||||
return CACHE.get(name)
|
||||
const r = registry()
|
||||
if (!r.loaders.has(name)) return Promise.resolve(null)
|
||||
if (!r.cache.has(name)) r.cache.set(name, r.loaders.get(name)().then((m) => m.default || m))
|
||||
return r.cache.get(name)
|
||||
}
|
||||
export const posterTypeNames = () => [...registry().loaders.keys()]
|
||||
|
||||
// The built-in type, from every copy: silent, first one kept.
|
||||
if (!registry().loaders.has('country')) {
|
||||
registry().loaders.set('country', () => import('./types/countryPosterType.js'))
|
||||
announce('country')
|
||||
}
|
||||
export const posterTypeNames = () => [...LOADERS.keys()]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// <dxm-poster-section>'s attributes → its profile-config. Pure (no Vue), so it is testable on its own.
|
||||
export const ATTRS = ['type', 'scope', 'collapsible', 'collapsed', 'locale', 'assets-base', 'api-base-url', 'is-dark']
|
||||
export const ATTRS = ['type', 'scope', 'collapsible', 'collapsed', 'locale', 'assets-base', 'api-base-url', 'is-dark', 'columns']
|
||||
export const TAG = 'dxm-poster-section'
|
||||
|
||||
export function posterSectionConfig(el) {
|
||||
|
|
@ -9,6 +9,9 @@ export function posterSectionConfig(el) {
|
|||
if (a('locale')) cfg.locale = a('locale')
|
||||
if (a('assets-base')) cfg.assetsBase = a('assets-base')
|
||||
if (a('api-base-url')) cfg.apiBaseUrl = a('api-base-url')
|
||||
// posters per row (1-5); absent = the wall's own default. A reader's saved choice still wins (PosterWall).
|
||||
const cols = Number(a('columns'))
|
||||
if (el.hasAttribute('columns') && Number.isInteger(cols) && cols >= 1 && cols <= 5) cfg.columns = cols
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
* <dxm-poster-section type="country" scope="africa" locale="ar"></dxm-poster-section>
|
||||
*
|
||||
* Attributes: type (default "country"), scope (a region / continent / list key), collapsible (default on;
|
||||
* collapsible="false" turns it off), collapsed (start shut), locale, assets-base, api-base-url, is-dark.
|
||||
* collapsible="false" turns it off), collapsed (start shut), locale, assets-base, api-base-url, is-dark, columns
|
||||
* (posters per row, 1-5).
|
||||
* They are mapped into the standard element's profile-config, so the element keeps defineMapElement's per-instance
|
||||
* i18n and store, and the profile (PosterSectionView.vue) reads them like every other profile reads its config.
|
||||
* Any bundle can call registerPosterSectionElement(); a second call is a no-op.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
"check:pin-hint-floor:self-test": "node internal/check-pin-hint-floor.mjs --self-test",
|
||||
"check:preview-flag-dev-only": "node internal/check-preview-flag-dev-only.mjs",
|
||||
"check:preview-flag-dev-only:self-test": "node internal/check-preview-flag-dev-only.mjs --self-test",
|
||||
"check:poster-type-registry": "node internal/check-poster-type-registry.mjs",
|
||||
"check:poster-type-registry:self-test": "node internal/check-poster-type-registry.mjs --self-test",
|
||||
"check:no-fleet-names": "node internal/check-no-fleet-names.mjs",
|
||||
"check:no-fleet-names:self-test": "node internal/check-no-fleet-names.mjs --self-test",
|
||||
"check:dashboard-filter-a11y": "node plugin-designs/research-dashboard/scripts/check-dashboard-filter-a11y.mjs",
|
||||
|
|
@ -352,8 +354,8 @@
|
|||
"dev": "bun x vite",
|
||||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight check:no-fleet-names:self-test check:pin-hint-floor:self-test check:poster-sizes:self-test check:shell-header:self-test check:preview-flag-dev-only:self-test",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-design-catalogue check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight check:no-fleet-names:self-test check:pin-hint-floor:self-test check:poster-sizes:self-test check:shell-header:self-test check:preview-flag-dev-only:self-test check:poster-type-registry:self-test",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-design-catalogue check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only check:poster-type-registry",
|
||||
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -471,6 +473,7 @@
|
|||
"check:coverage-consistent": "the gospel-resources surfaces must agree: no resource on an ISO RESERVED code (und/mul/mis/zxx), every resourced code NAMED, table reach recounted off the pins, no invented URL, tiers reconcile. Static, sub-second. Self-test: `bun run check:coverage-consistent:poison`, all 12 clauses (A-L) must fail. Clause L is the one that is not about consistency: it fails when the link-verification record goes stale, because 'link checked' is a claim about the present tense. Written after the same drift defect was found by hand three times.",
|
||||
"check:csv-shape": "the export must OPEN as a table: header on line 0, every row matching. Run the `check:csv-shape:self-test` script for the NEGATIVE test (forces the provenance comment back to line 0 IN MEMORY and requires this gate to REJECT it) — the header used to claim 'NEGATIVE-TESTED' with no way to re-run it.",
|
||||
"check:poster-sizes": "the shared poster-size ladder (library/constants/posterSizes.js) is right: picker order = ladder, dot-free keys, portrait only, the sheets glue up to the finished size, photo slots on exactly 66x66 + 60x96, paper prints and the rest go to a print-shop PDF, every alias resolves, paperFor() picks Letter or A4 by region. Run the `check:poster-sizes:self-test` script for the NEGATIVE test",
|
||||
"check:poster-type-registry": "the poster type registry is ONE per page: every bundle inlines its own copy of posterTypes.js and <dxm-poster-section> is defined by whichever bundle loads first, so with two independent copies loaded side by side a type registered through one is found through the other; the built-in country is listed once; a name registered twice keeps the first; every registration is announced on window (dxm-poster-type-registered) and PosterSectionView re-resolves on it. Run the `check:poster-type-registry:self-test` script for the NEGATIVE test (a per-bundle registry fails).",
|
||||
"check:preview-flag-dev-only": "the flag that previews withheld content (globalThis.DXM_PREVIEW_UNRELEASED, e.g. the whole-India poster) never reaches a built bundle: no file under public/ assigns it in any spelling a minifier writes, and every source assignment is behind import.meta.env.DEV (set only by the Vite dev server, the pilot's hot-reload preview). The built tree goes to the research landing and DX Life's /dx-maps, where the flag would show India publicly. Run the `check:preview-flag-dev-only:self-test` script for the NEGATIVE test (2 planted defects).",
|
||||
"check:pin-hint-floor": "the pin-hint pill sits at the map's bottom on a page that scrolls (the fold is not an obstacle there) and is still lifted above the fold on a full-screen page; the legend sheet and the licence credit still lift it; PinHint.vue takes its floor from pinHintFloor.js. Run the `check:pin-hint-floor:self-test` script for the NEGATIVE test",
|
||||
"check:no-fleet-names": "tracked source (not data/, public/ or the translation catalogues) names no build-team member, attributes no request to a user and carries no dated QA-round tag; an allowance is a named LINE, never a whole file. Run the `check:no-fleet-names:self-test` script for the NEGATIVE test",
|
||||
|
|
|
|||
Loading…
Reference in a new issue