diff options
| author | Adam Soltys <[email protected]> | 2026-08-01 10:44:20 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-01 10:44:20 -0700 |
| commit | d8d2746f16d1bdcb71e1409a90871b65117d026c (patch) | |
| tree | 900493bfb85d770e3a8cd6aca62456021d2ae44d | |
| parent | e09b76ea0ed13840426a56f031769f8d4154f34b (diff) | |
| download | coinosv3-d8d2746f16d1bdcb71e1409a90871b65117d026c.tar.gz coinosv3-d8d2746f16d1bdcb71e1409a90871b65117d026c.zip | |
Actually ship the NWC feature, and guard against the drift that hid it
build.js swaps src/features/index.js for a generated module listing only the
features in its own ALL_FEATURES registry, so registering nwc in index.js
alone was not enough: its i18n strings shipped (they live elsewhere) but its
code never entered the bundle, and the settings card silently never rendered.
- add nwc to build.js ALL_FEATURES, after ark since it drives ark's headless
pay/balance seam
- tools/feature-registry-check.js asserts the two lists agree on membership
AND on construction order, since hook precedence depends on the order
Co-Authored-By: Claude Fable 5 <[email protected]>
| -rw-r--r-- | build.js | 6 | ||||
| -rw-r--r-- | tools/feature-registry-check.js | 40 |
2 files changed, 44 insertions, 2 deletions
@@ -144,11 +144,13 @@ export async function buildSpWorker({ minify = true } = {}) { } // Optional-feature selection: HAL_FEATURES is a comma list of enabled -// features (gifts,swaps,ark,zaps,sp). Unset means all; "none"/"" means a minimal +// features (gifts,swaps,ark,zaps,sp,nwc). Unset means all; "none"/"" means a minimal // on-chain-only wallet. A build plugin swaps src/features/index.js for a // generated module that only imports the enabled features, so a disabled // feature's code (and its network endpoints) never enters the bundle. -const ALL_FEATURES = { gifts: 'giftsFeature', swaps: 'swapsFeature', ark: 'arkFeature', zaps: 'zapsFeature', sp: 'spFeature', sync: 'syncFeature' }; +// NB order matters and must match src/features/index.js — nwc sits after ark +// because it drives ark's headless pay/balance seam. +const ALL_FEATURES = { gifts: 'giftsFeature', swaps: 'swapsFeature', ark: 'arkFeature', zaps: 'zapsFeature', sp: 'spFeature', nwc: 'nwcFeature', sync: 'syncFeature' }; export function enabledFeatures(spec = process.env.HAL_FEATURES) { if (spec == null) return Object.keys(ALL_FEATURES); diff --git a/tools/feature-registry-check.js b/tools/feature-registry-check.js new file mode 100644 index 0000000..9b7ac5e --- /dev/null +++ b/tools/feature-registry-check.js @@ -0,0 +1,40 @@ +// Guard: src/features/index.js and build.js's ALL_FEATURES must agree. +// They drifted once (nwc was added to index.js only, so its i18n strings +// shipped but its code never did — the settings card silently never rendered, +// because build.js SWAPS OUT features/index.js for a generated module). +// bun tools/feature-registry-check.js +import { readFileSync } from 'node:fs'; + +const idx = readFileSync(new URL('../src/features/index.js', import.meta.url), 'utf8'); +const build = readFileSync(new URL('../build.js', import.meta.url), 'utf8'); + +const inIndex = [...idx.matchAll(/import \{ (\w+Feature) \} from '\.\/([\w-]+)\.js'/g)] + .map((m) => ({ fn: m[1], name: m[2] })); +const registry = build.match(/const ALL_FEATURES = \{([^}]*)\}/s)?.[1] || ''; +const inBuild = [...registry.matchAll(/(\w+):\s*'(\w+Feature)'/g)].map((m) => ({ name: m[1], fn: m[2] })); + +let ok = true; +for (const f of inIndex) { + if (!inBuild.find((b) => b.fn === f.fn)) { + console.log(`x ${f.fn} (${f.name}.js) is in features/index.js but NOT in build.js ALL_FEATURES — it will not ship`); + ok = false; + } +} +for (const b of inBuild) { + if (!inIndex.find((f) => f.fn === b.fn)) { + console.log(`x ${b.fn} is in build.js ALL_FEATURES but not imported in features/index.js`); + ok = false; + } +} +// Compare the ORDER FEATURES ARE CONSTRUCTED IN (hook precedence depends on +// it), not the order they happen to be imported in. +const built = [...(idx.match(/return \[([^\]]*)\]/s)?.[1] || '') + .matchAll(/(\w+Feature)\(ctx\)/g)].map((m) => m[1]); +const orderIdx = built.join(','); +const orderBuild = inBuild.map((b) => b.fn).join(','); +if (ok && orderIdx !== orderBuild) { + console.log(`x feature ORDER differs (hook precedence depends on it)\n index.js: ${orderIdx}\n build.js: ${orderBuild}`); + ok = false; +} +if (ok) console.log(`ok ${inIndex.length} features registered consistently`); +process.exit(ok ? 0 : 1); |
