From e18a74b936e43aeb7f74df433d88f294f9bcc877 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 26 Aug 2026 14:16:41 -0700 Subject: The app arrives in movements: real ESM chunks split the bundle (entry 333KB, shared chunks alongside, 61KB of gifts/NWC/hats deferred past first paint into slots that keep their hook seniority), the lazy scripts wear IIFE coats so their names can't collide with the page's — which is what silently broke the remote-signer button — nip98 moves out of the login module so minimal builds stop hauling backup relays, and the smoke test learns the front door's new steps while the regtest stack rises from its IBD nap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi --- tools/feature-registry-check.js | 51 ++++++++++++++++------------------------- tools/minimal-smoke.js | 7 +++++- tools/nostr-login-test.js | 2 +- 3 files changed, 27 insertions(+), 33 deletions(-) (limited to 'tools') diff --git a/tools/feature-registry-check.js b/tools/feature-registry-check.js index 9b7ac5e..0931156 100644 --- a/tools/feature-registry-check.js +++ b/tools/feature-registry-check.js @@ -1,40 +1,29 @@ -// Guard: src/features/index.js and build.js's ALL_FEATURES must agree. +// Guard: src/features/index.js and build.js's featureIndexSource 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). +// +// The strongest form of agreement is equality: the checked-in module must be +// code-identical (comments and whitespace aside) to what featureIndexSource +// generates for the full feature set — imports, construction order, deferred +// placeholder slots, and the deferred loader all included. // bun tools/feature-registry-check.js import { readFileSync } from 'node:fs'; +import { featureIndexSource, enabledFeatures } from '../build.js'; -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 strip = (s) => s + .replace(/\/\/[^\n]*/g, '') // line comments + .replace(/\s+/g, ' ') // whitespace runs + .replace(/, /g, ',').replace(/ ,/g, ',') + .trim(); -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] })); +const idx = readFileSync(new URL('../src/features/index.js', import.meta.url), 'utf8'); +const gen = featureIndexSource(enabledFeatures(undefined)); -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 (strip(idx) !== strip(gen)) { + console.log('x src/features/index.js does not match build.js featureIndexSource (full set)'); + console.log('\n--- generated (normalized) ---\n' + strip(gen)); + console.log('\n--- checked in (normalized) ---\n' + strip(idx)); + process.exit(1); } -if (ok) console.log(`ok ${inIndex.length} features registered consistently`); -process.exit(ok ? 0 : 1); +console.log(`ok features/index.js matches the generator (${enabledFeatures(undefined).length} features, deferred slots included)`); diff --git a/tools/minimal-smoke.js b/tools/minimal-smoke.js index bd0d588..f3f896c 100644 --- a/tools/minimal-smoke.js +++ b/tools/minimal-smoke.js @@ -29,7 +29,10 @@ check('no ark protocol code', !html.includes('Ark VTXO mailbox authorization')); check('no swap protocol code', !html.includes('swap/reverse')); check('no musig code', !html.includes('MuSig/noncecoef')); check('no silent-payment engine', !html.includes('BIP0352') && !html.includes('__SP_WORKER__')); -check('no nostr sync code', !html.includes('30078') && !html.includes('relay.coinos.io')); +// The sync EVENT KIND must be gone; the coinos relay hostname alone is not a +// sync marker anymore — recipient search (send-to-npub, core UX) carries +// PROFILE_RELAYS for profile lookups even in the minimal build. +check('no nostr sync code', !html.includes('30078')); const server = Bun.serve({ port: 5198, fetch: () => new Response(html, { headers: { 'content-type': 'text/html' } }) }); @@ -62,6 +65,8 @@ try { await sleep(400); console.log('\n[1] import + core wallet'); + await clickText('button', 'Get started'); // the front door gained a step + await sleep(300); await clickText('button', 'Import existing'); await sleep(200); await page.type('textarea', generateMnemonic(wordlist)); diff --git a/tools/nostr-login-test.js b/tools/nostr-login-test.js index 468ffb4..25983a4 100644 --- a/tools/nostr-login-test.js +++ b/tools/nostr-login-test.js @@ -58,7 +58,7 @@ check('another account cannot decrypt it', denied); console.log('\n[NIP-98 auth]'); { - const { nip98Header } = await import('../src/nostr-login.js'); + const { nip98Header } = await import('../src/nip98.js'); const { verifyEvent } = await import('nostr-tools/pure'); const { sha256: sha } = await import('@noble/hashes/sha256'); const body = JSON.stringify({ name: 'x' }); -- cgit v1.2.3