@intenque/network-sdk 0.0.0-stage → 2.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/README.md +254 -2
- package/dist/capture/index.cjs +1 -0
- package/dist/capture/index.d.cts +57 -0
- package/dist/capture/index.d.ts +57 -0
- package/dist/capture/index.js +1 -0
- package/dist/cli/index.js +1742 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/constants-LVhc0PNb.d.cts +20 -0
- package/dist/constants-LVhc0PNb.d.ts +20 -0
- package/dist/react/index.cjs +3 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +421 -0
- package/dist/react/index.d.ts +421 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -0
- package/dist/server/index.cjs +2 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +345 -0
- package/dist/server/index.d.ts +345 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -0
- package/dist/styles.css +141 -0
- package/package.json +111 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/constants.ts","../../src/cli/doctor.ts","../../src/cli/doctorBackend.ts","../../src/server/outbox.ts","../../src/server/sign.ts","../../src/server/wire.ts","../../src/server/delivery.ts","../../src/cli/shared.ts","../../src/rootPublicKeys.ts","../../src/verify/schema.ts","../../src/canonicalJson.ts","../../src/verify/base64url.ts","../../src/verify/p1363.ts","../../src/verify/verifyManifest.ts","../../src/cli/init.ts","../../src/cli/printConfig.ts","../../src/cli/syncFallback.ts","../../src/cli/main.ts"],"sourcesContent":["/**\n * The `network-sdk` bin entry: runs `main` and exits with its code. Tests import `./main.js` instead (and spawn this\n * file with the WebCrypto global removed, test/fixtures/no-webcrypto.mjs, to cover the polyfill).\n */\nimport { webcrypto } from 'node:crypto'\nimport { main } from './main.js'\nimport { log } from './shared.js'\n\n// Node 18 ships WebCrypto only behind --experimental-global-webcrypto; the verifier needs `crypto.subtle`, so give it\n// node:crypto's implementation before anything runs (Node ≥ 19 already has the global and keeps it). `main` fails fast\n// with \"WebCrypto unavailable\" when `crypto.subtle` is still missing.\n;(globalThis as { crypto?: Crypto }).crypto ??= webcrypto as Crypto\n\nmain(process.argv.slice(2)).then(\n (code) => process.exit(code),\n (err) => {\n log.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n },\n)\n","/**\n * Network-wide constants the SDK must agree on with the hub (packages/shared/src/constants.ts) — spec §4.2, §4.3,\n * §4.5, §5.3, §6.1. Kept in a dependency-free file so every entry (react, capture, server, cli) can import it without\n * pulling the others in.\n */\n\n/** Semver of this package; SDK major = manifest schema major (§6.1). Bumped by the version script. */\nexport const SDK_VERSION = '2.2.0-beta.1'\n/** `schema` this SDK reads (§4.3). A greater major → failure → fallback. */\nexport const MANIFEST_SCHEMA_MAJOR = 2\n/**\n * §6.1 compiled `DEFAULT_CDN_BASE`: the production manifest base. Decision 15: default DigitalOcean domains until the\n * agency domain exists, so this is the Spaces CDN default endpoint plus the production prefix (`network/v1`). It is\n * only the last resort for a site with no committed fallback (§4.2 URL precedence: `config.manifestUrl ??\n * (config.cdnBase ?? fallback.cdnBase ?? DEFAULT_CDN_BASE)`); a domain cutover is a publish + `sync-fallback`, not an\n * SDK release.\n */\nexport const DEFAULT_CDN_BASE =\n 'https://inr-network-public.nyc3.cdn.digitaloceanspaces.com/network/v1'\n/** §4.3 \"hard fail at publish if > 24 KB\": the verifier refuses larger input before parsing. */\nexport const MANIFEST_MAX_BYTES = 24 * 1024\n/** §4.5 SDK step 2: 7 d grace after `notAfter`. */\nexport const SIGNER_GRACE_MS = 7 * 86_400_000\n/** §4.6 SDK fetch: 4 s timeout through an AbortController. */\nexport const FETCH_TIMEOUT_MS = 4000\n/** §5.3 referral query parameter and storage key. */\nexport const REFERRAL_PARAM = 'inr'\nexport const REFERRAL_STORAGE_KEY = 'inr_ref'\n/** §5.3: a referral older than 30 d is ignored. */\nexport const REFERRAL_TTL_MS = 30 * 86_400_000\nexport const CLICK_ID_RE = /^[A-Za-z0-9_-]{32}$/\n/** §6.3 UTM defaults, used only when no manifest is loaded (the manifest's `links` win). */\nexport const UTM = { medium: 'network', campaign: 'cross-promo' } as const\n\n// §5.1 signals — the SDK-side copies of the hub's `ANALYTICS_LIMITS` / `HEADLESS_BITS` / `CLIENT_FLAGS`\n// (packages/shared); `test/signals-contract.test.ts` asserts they agree, so neither side can drift.\n/** The impression goes 10 s after the section first became visible, unless a click, `hidden` or `pagehide` came first. */\nexport const IMPRESSION_TIMER_MS = 10_000\n/**\n * Decision row 6 (\"precise claim\", §6.3): a link rewritten with `inr=<clickId>` returns to its at-rest href (UTMs only)\n * this long after the gesture ends (pointerup / pointercancel, or the click / context menu / drag that used it), or at\n * once on blur (release 2b review round 2).\n */\nexport const HREF_RESTORE_MS = 2_000\n/** `seen` = ≥ 50 % visible for ≥ 1000 ms continuous (section and card alike). */\nexport const SEEN_MS = 1000\n/** The hub's `no_section_seen` threshold (`sm < 300`, §5.2 step 5): a click re-sends the impression when it crossed it. */\nexport const NO_SECTION_SEEN_MS = 300\nexport const VISIBLE_RATIO = 0.5\n/** Every `ms` / `sm` / `fv` offset is bounded to one day, `vp.w/h` to 20 000, `items` to 24, `fl` to 8 (§2.4). */\nexport const SIGNAL_MS_MAX = 86_400_000\nexport const SIGNAL_VP_MAX = 20_000\nexport const SIGNAL_ITEMS_MAX = 24\nexport const SIGNAL_FLAGS_MAX = 8\n/** The hub refuses bodies over 4 KB (413); the SDK trims never-visible cards before it gets there. */\nexport const SIGNAL_BODY_MAX = 4096\n/**\n * Impression sends per page view (first send + re-sends at the end of later visibility episodes). Below the hub's 20\n * (`sendsMax`, beyond which a page view is `dup_pv` / suspect), so a visitor who switches tabs all afternoon never\n * turns a real page view suspect.\n */\nexport const IMPRESSION_SENDS_MAX = 10\n/** Click ids remembered per page view to send each one once (a `contextmenu` then `auxclick`, a doubled handler). */\nexport const CLICK_IDS_REMEMBERED = 64\n/** §2.4 `hd` bitmask bits (client signals); the hub treats `webdriver | headlessUa` as `bot`. */\nexport const HEADLESS_BITS = {\n webdriver: 1,\n noLanguages: 2,\n zeroPlugins: 4,\n headlessUa: 8,\n noOuterWindow: 16,\n} as const\n/**\n * §5.1 flags the SDK reports on the next impression (`fl`): the §2.4 vocabulary the directory loader can set. The\n * loader's `sig_revoked` travels as `sig_fail` (a revoked kid is a signature failure; §2.4 has no `sig_revoked`) and\n * the SDK-local `fetch_fail` / `insecure_context` are never transmitted.\n */\nexport const CLIENT_FLAGS = [\n 'sig_fail',\n 'rev_rollback',\n 'stale_cache',\n 'fallback_invalid',\n 'fallback_key_expired',\n 'sitekey_from_fallback',\n 'sdk_outdated',\n] as const\nexport type ClientFlag = (typeof CLIENT_FLAGS)[number]\n/** §4.6 fallback file path inside a site repo. */\nexport const FALLBACK_PATH = 'src/data/network.fallback.json'\nexport const CONFIG_PATH = 'src/network.config.js'\n\nexport type SiteChannel = 'production' | 'preview'\n\n/** §4.2: the alias URL for a site. */\nexport function manifestUrlFor(cdnBase: string, siteKey: string, channel: SiteChannel): string {\n return `${cdnBase.replace(/\\/+$/, '')}/${channel === 'preview' ? 'preview/' : ''}sites/${siteKey}/manifest.json`\n}\n","/**\n * `network-sdk doctor [--root <dir>] [--frontend|--backend [--mongo-uri <uri>]] [--url <base>] [--root-keys <json>]\n * [--api-url <origin>] [--preflight-path </api/route>]` — spec §6.6, import-based and layout-agnostic: scans `src/**`\n * for the SDK imports\n * and identifiers (file paths reported, nothing hardcoded), checks env on both sides when present, fetches the\n * manifest through the CDN (revision, items, size, cache headers, signature), checks the fallback (age, signer\n * `notAfter`, identity), the package.json hooks and the App Platform Build Command note, runs the CORS preflight\n * only when header mode is configured, and sends the test impression (`src: 'doctor'`, §2.4 / §6.6: signed with the\n * verified live manifest's token; the hub moves `sites.install.*` and excludes it from rollups and quality counters;\n * `--no-impression` skips it; anything but 204 is a warning, never a failure, so CI without hub access still passes).\n * With the backend env present it signs `POST /v1/conversions/ping`. `--backend` runs only the backend checks\n * (doctorBackend.ts). Exit 0 when nothing failed (warnings allowed), 1 otherwise.\n */\nimport { randomUUID } from 'node:crypto'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join, relative, resolve } from 'node:path'\nimport { FALLBACK_PATH, SDK_VERSION, type SiteChannel } from '../constants.js'\nimport type { Manifest } from '../verify/schema.js'\nimport {\n BACKEND_ENV,\n BACKEND_EXT,\n backendEnvFinding,\n backendFindings,\n envValues,\n signedPing,\n type Finding,\n} from './doctorBackend.js'\nimport {\n aliasUrl,\n CliError,\n daysBetween,\n downloadManifest,\n projectRoot,\n readConfigLiterals,\n readFallback,\n resolveRootKeys,\n str,\n verifyCommitted,\n verifyDownloaded,\n type Flags,\n} from './shared.js'\n\n/**\n * The §6.6 test impression for a verified manifest: a C.1 body with `src: 'doctor'` that saw nothing (no card, no\n * section time — the CLI renders nothing), the manifest's token unchanged, `smp: 0` for a rate-0 manifest (§5.1).\n */\nexport function doctorImpressionBody(\n m: Manifest,\n now = Date.now(),\n pv: string = randomUUID(),\n): Record<string, unknown> {\n const t = Math.round(now)\n return {\n v: 2,\n k: m.site,\n pv,\n r: m.revision,\n src: 'doctor',\n sdk: SDK_VERSION,\n tok: m.analytics.token,\n sp: m.analytics.samplePermille,\n smp: m.analytics.samplePermille > 0 ? 1 : 0,\n t,\n hd: 0,\n m: t,\n items: [],\n ss: false,\n sm: 0,\n vp: { w: 0, h: 0 },\n why: 'timer',\n }\n}\n\n/** POSTs the test impression as `text/plain` to `<analytics.endpoint>/impression` (the hub always answers 204). */\nexport async function sendDoctorImpression(\n m: Manifest,\n timeoutMs = 5000,\n): Promise<{ url: string; status: number } | { url: string; error: string }> {\n const url = `${m.analytics.endpoint.replace(/\\/+$/, '')}/impression`\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), timeoutMs)\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'content-type': 'text/plain;charset=UTF-8',\n 'user-agent': `network-sdk-doctor/${SDK_VERSION}`,\n },\n body: JSON.stringify(doctorImpressionBody(m)),\n signal: ac.signal,\n })\n return { url, status: res.status }\n } catch (err) {\n return { url, error: err instanceof Error ? err.message : String(err) }\n } finally {\n clearTimeout(timer)\n }\n}\n\nconst EXT = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'])\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'build', '.git', 'coverage'])\n\nexport function listSourceFiles(root: string, limit = 5000, ext: Set<string> = EXT): string[] {\n const out: string[] = []\n const walk = (dir: string) => {\n if (out.length >= limit) return\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const name of entries) {\n if (SKIP_DIRS.has(name)) continue\n const p = join(dir, name)\n let st\n try {\n st = statSync(p)\n } catch {\n continue\n }\n if (st.isDirectory()) walk(p)\n else if (ext.has(name.slice(name.lastIndexOf('.'))) && out.length < limit) out.push(p)\n }\n }\n walk(root)\n return out\n}\n\nexport type Scan = {\n imports: Record<'react' | 'capture' | 'server', string[]>\n identifiers: Record<string, string[]>\n headerMode: boolean\n}\n\nconst IDENTIFIERS = [\n 'DiscoverBusinesses',\n 'NetworkProvider',\n 'captureReferral',\n 'setConsent',\n 'attachReferral',\n 'attachReferralHeader',\n 'createConversionReporter',\n 'referralFromRequest',\n 'referralMiddleware',\n] as const\n\nexport function scanSources(root: string, files: string[]): Scan {\n const scan: Scan = {\n imports: { react: [], capture: [], server: [] },\n identifiers: Object.fromEntries(IDENTIFIERS.map((i) => [i, []])),\n headerMode: false,\n }\n for (const f of files) {\n let text: string\n try {\n text = readFileSync(f, 'utf8')\n } catch {\n continue\n }\n if (!text.includes('@intenque/network-sdk') && !IDENTIFIERS.some((i) => text.includes(i)))\n continue\n const rel = relative(root, f)\n for (const entry of ['react', 'capture', 'server'] as const)\n if (new RegExp(`['\"]@intenque/network-sdk/${entry}['\"]`).test(text))\n scan.imports[entry].push(rel)\n for (const id of IDENTIFIERS)\n if (new RegExp(`\\\\b${id}\\\\b`).test(text)) scan.identifiers[id]!.push(rel)\n if (/attachReferralHeader\\s*\\(/.test(text) || /mode\\s*:\\s*['\"]header['\"]/.test(text))\n scan.headerMode = true\n }\n return scan\n}\n\n/**\n * How many `gtag('config', …)` statements in `index.html` lack a `page_location` (each statement is read up to its\n * `;`); `null` when the file is missing or has no gtag config at all.\n */\nexport function gtagConfigsWithoutPageLocation(htmlPath: string): number | null {\n if (!existsSync(htmlPath)) return null\n let text: string\n try {\n text = readFileSync(htmlPath, 'utf8')\n } catch {\n return null\n }\n const configs = [...text.matchAll(/gtag\\(\\s*['\"]config['\"]/g)]\n if (configs.length === 0) return null\n let naked = 0\n for (const m of configs) {\n const start = m.index ?? 0\n const end = text.indexOf(';', start)\n const statement = text.slice(start, end === -1 ? undefined : end)\n if (!/\\bpage_location\\b/.test(statement)) naked++\n }\n return naked\n}\n\n/** Which names are set in the process env or a `.env` file — values are NEVER printed. */\nfunction envPresence(dir: string, names: readonly string[]): Record<string, boolean> {\n return Object.fromEntries(Object.entries(envValues(dir, names)).map(([k, v]) => [k, v !== '']))\n}\n\n/** `--backend`: the backend checks only, from `--root` or the cwd (no package.json or network.config.js needed). */\nasync function runBackendDoctor(flags: Flags): Promise<number> {\n const root = resolve(str(flags['root']) ?? process.cwd())\n const files = listSourceFiles(root, 5000, BACKEND_EXT)\n const scan = scanSources(root, files)\n const findings = await backendFindings(root, flags, {\n files: files.length,\n identifiers: {\n createConversionReporter: scan.identifiers['createConversionReporter']!,\n referralFromRequest: scan.identifiers['referralFromRequest']!,\n referralMiddleware: scan.identifiers['referralMiddleware']!,\n },\n })\n return finish(findings)\n}\n\nexport async function runDoctor(flags: Flags): Promise<number> {\n if (flags['backend'] === true) return runBackendDoctor(flags)\n const root = projectRoot(flags)\n const findings: Finding[] = []\n const add = (level: Finding['level'], text: string) => findings.push({ level, text })\n // frontend or auto (frontend checks, plus the backend's when its env is found)\n const backend = flags['frontend'] !== true\n\n // 1. import-based scan\n const files = listSourceFiles(join(root, existsSync(join(root, 'src')) ? 'src' : '.'))\n const scan = scanSources(root, files)\n const report = (label: string, paths: string[], required: boolean) => {\n if (paths.length > 0) add('ok', `${label}: ${paths.join(', ')}`)\n else add(required ? 'fail' : 'warn', `${label}: missing`)\n }\n report('<DiscoverBusinesses /> (react entry)', scan.identifiers['DiscoverBusinesses']!, true)\n report('captureReferral() (capture entry, main.jsx)', scan.identifiers['captureReferral']!, true)\n report('setConsent() in the consent banner', scan.identifiers['setConsent']!, false)\n report(\n `attachReferral() on the API client (${scan.headerMode ? 'HEADER mode' : 'body mode'})`,\n [...scan.identifiers['attachReferral']!, ...scan.identifiers['attachReferralHeader']!],\n false,\n )\n // GA's inline `gtag('config', …)` runs before captureReferral strips `inr`, so without an explicit page_location\n // the click id reaches GA in page_location (§5.3: no other script ever sees it).\n const naked = gtagConfigsWithoutPageLocation(join(root, 'index.html'))\n if (naked !== null)\n add(\n naked > 0 ? 'warn' : 'ok',\n naked > 0\n ? `index.html: gtag('config') without page_location (${naked}): GA reads location.href before captureReferral strips inr; pass page_location with the inr parameter removed (install guide, GA step)`\n : \"index.html: gtag('config') passes page_location (inr stripped before GA reads the URL)\",\n )\n if (backend) {\n const found = scan.identifiers['createConversionReporter']!\n if (found.length > 0)\n add('ok', `createConversionReporter() (server entry): ${found.join(', ')}`)\n else\n add(\n 'skip',\n 'createConversionReporter(): not found (doctor --backend in a split backend; required for capabilities.conversions sites)',\n )\n }\n\n // 2. config + env\n const cfg = readConfigLiterals(root)\n if (!cfg.exists) {\n add('fail', `${relative(root, cfg.path)} missing: run network-sdk init --site-key <key>`)\n return finish(findings)\n }\n const siteKey = str(flags['site-key']) ?? cfg.siteKey\n if (!siteKey) add('fail', `${relative(root, cfg.path)} has no literal siteKey`)\n else add('ok', `siteKey ${siteKey} (${relative(root, cfg.path)})`)\n const channel: SiteChannel =\n flags['channel'] === 'preview' ? 'preview' : (cfg.channel ?? 'production')\n const fe = envPresence(root, ['VITE_INR_SITE_KEY', 'VITE_INR_CHANNEL', 'VITE_INR_MANIFEST_URL'])\n add(\n 'ok',\n `frontend env: ${Object.entries(fe)\n .map(([k, v]) => `${k}=${v ? 'set' : 'unset'}`)\n .join(', ')} (optional overrides)`,\n )\n if (fe['VITE_INR_MANIFEST_URL'])\n add('warn', 'VITE_INR_MANIFEST_URL is set: staging-hub override; never for a production site')\n // auto mode: the backend env next to (or inside) the frontend, when there is one; the signed ping at the end\n let backendEnv: Record<string, string> | null = null\n if (backend) {\n const dir = existsSync(join(root, 'backend'))\n ? join(root, 'backend')\n : existsSync(join(root, '..', 'backend'))\n ? join(root, '..', 'backend')\n : root\n const env = envValues(dir, BACKEND_ENV)\n const where = relative(root, dir) || '.'\n if (Object.values(env).every((v) => v === ''))\n add('skip', `backend env not set in ${where} (run doctor --backend in the backend)`)\n else {\n const { finding, complete } = backendEnvFinding(env, where)\n findings.push(finding)\n if (complete) backendEnv = env\n }\n }\n\n // 3. root keys, live manifest\n let roots\n try {\n // diagnostic only: without --root-keys and without compiled production roots, read the committed literal\n const f =\n !str(flags['root-keys']) && cfg.manifestRootKeys\n ? { ...flags, 'root-keys': cfg.manifestRootKeys }\n : flags\n roots = resolveRootKeys(f).roots\n add(\n f === flags ? 'ok' : 'warn',\n `root keys: ${roots.map((r) => r.kid).join(', ')}${f === flags ? '' : ' (from manifestRootKeys in network.config.js: a development override, not the compiled production root)'}`,\n )\n } catch (err) {\n add('fail', err instanceof Error ? err.message : String(err))\n return finish(findings)\n }\n if (!siteKey) return finish(findings)\n const expect = { site: siteKey, channel }\n\n const fb = readFallback(root)\n let trusted: Manifest | null = null\n if (!fb.exists) add('fail', `${FALLBACK_PATH} missing (run network-sdk sync-fallback)`)\n else if (fb.json === null) add('fail', `${FALLBACK_PATH} is not valid JSON`)\n else {\n const r = await verifyCommitted(fb.json, roots, expect)\n const site = (fb.json as { site?: unknown }).site\n if (site !== siteKey)\n add(\n 'fail',\n `${FALLBACK_PATH} site ${String(site)} ≠ ${siteKey} (§4.6: sync-fallback overwrites it; commit the result)`,\n )\n else if (!r.ok) add('fail', `${FALLBACK_PATH} fails the identity check: ${r.reason}`)\n else {\n trusted = r.manifest\n const age = daysBetween(new Date(trusted.publishedAt), new Date())\n const left = daysBetween(new Date(), new Date(trusted.signer.notAfter))\n add(\n 'ok',\n `fallback: revision ${trusted.revision}, ${trusted.businesses.length} items, published ${trusted.publishedAt} (${age} d ago), signer ${trusted.sig.kid} valid until ${trusted.signer.notAfter}`,\n )\n if (left < 60)\n add(\n 'warn',\n `fallback signer notAfter is in ${left} d (< 60 d): redeploy after the next publish so sync-fallback refreshes it`,\n )\n }\n }\n\n const url = str(flags['url'])\n ? aliasUrl(str(flags['url']), siteKey, channel)\n : (cfg.manifestUrl ?? aliasUrl(cfg.cdnBase ?? undefined, siteKey, channel))\n let live: Manifest | null = null\n const dl = await downloadManifest(url)\n if (!dl.ok) add('fail', `manifest fetch failed: ${dl.detail} (${url})`)\n else {\n const h = (n: string) => dl.headers.get(n) ?? '—'\n add(\n 'ok',\n `manifest ${url}: HTTP ${dl.status}, ${dl.bytes} bytes${dl.bytes > 10 * 1024 ? ' (> 10 KB target)' : ''}`,\n )\n add(\n 'ok',\n `headers: content-type ${h('content-type')}; cache-control ${h('cache-control')}; x-amz-meta-revision ${h('x-amz-meta-revision')}; x-amz-meta-manifest-hash ${h('x-amz-meta-manifest-hash').slice(0, 16)}…; age ${h('age')}`,\n )\n const v = await verifyDownloaded(dl.text, roots, expect, trusted?.signer.revokedKids ?? [])\n if (!v.ok) add('fail', `signature: ${v.reason}`)\n else {\n const m = v.manifest\n live = m\n add(\n 'ok',\n `signature OK: revision ${m.revision}, ${m.businesses.length} items, publishedAt ${m.publishedAt}, signer ${m.sig.kid} (root ${m.signer.rootKid}), minSdkVersion ${m.minSdkVersion}, analytics.enabled ${m.analytics.enabled}`,\n )\n if (trusted && trusted.revision < m.revision)\n add(\n 'warn',\n `fallback revision ${trusted.revision} is behind live ${m.revision}: the next build refreshes it (sync-fallback)`,\n )\n if (trusted && trusted.revision > m.revision)\n add(\n 'warn',\n `fallback revision ${trusted.revision} is AHEAD of live ${m.revision}: stale CDN edge or rollback in flight (the SDK would render the fallback with rev_rollback)`,\n )\n }\n }\n\n // 4. package.json hooks + App Platform note\n const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as {\n scripts?: Record<string, string>\n dependencies?: Record<string, string>\n }\n const scripts = pkg.scripts ?? {}\n const hasPrebuild = /network:sync|sync-fallback/.test(scripts['prebuild'] ?? '')\n const inlined = /sync-fallback|network:sync/.test(scripts['build'] ?? '')\n if (hasPrebuild || inlined)\n add(\n 'ok',\n `package.json: sync-fallback runs in ${[hasPrebuild && 'prebuild', inlined && 'build'].filter(Boolean).join(' and ')}`,\n )\n else\n add(\n 'warn',\n 'package.json lacks the prebuild hook / an inlined sync-fallback in build: the fallback will go stale',\n )\n add(\n 'ok',\n \"App Platform: the frontend component's Build Command must be `npm run build` (prebuild/inlined sync-fallback run only then)\",\n )\n const dep = pkg.dependencies?.['@intenque/network-sdk']\n add(\n dep ? 'ok' : 'warn',\n `@intenque/network-sdk ${dep ?? 'is not a dependency'} (CLI ${SDK_VERSION})`,\n )\n\n // 5. CORS preflight (header mode only)\n if (scan.headerMode) {\n const api = str(flags['api-url'])\n if (!api)\n add(\n 'warn',\n 'header mode detected: pass --api-url <backend origin> [--preflight-path /api/book] to check the CORS preflight',\n )\n else {\n const target = new URL(str(flags['preflight-path']) ?? '/', api).href\n try {\n const res = await fetch(target, {\n method: 'OPTIONS',\n headers: {\n Origin: 'https://doctor.invalid',\n 'Access-Control-Request-Method': 'POST',\n 'Access-Control-Request-Headers': 'x-inr-ref, x-inr-ref-scope, x-inr-ref-first',\n },\n })\n const allowed = (res.headers.get('access-control-allow-headers') ?? '').toLowerCase()\n const ok =\n ['x-inr-ref', 'x-inr-ref-scope', 'x-inr-ref-first'].every((h) => allowed.includes(h)) ||\n allowed === '*'\n add(\n ok ? 'ok' : 'fail',\n `CORS preflight ${target}: ${res.status}, allow-headers \"${allowed || '—'}\"${ok ? '' : ' — append X-INR-Ref, X-INR-Ref-Scope, X-INR-Ref-First to allowedHeaders or use body mode'}`,\n )\n } catch (err) {\n add(\n 'fail',\n `CORS preflight ${target} failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n }\n } else add('ok', 'body mode (default): no CORS change needed')\n\n // 6. test impression (§6.6): a 204 means the hub received it (with a valid token it moves `install.*`)\n if (flags['no-impression'] === true) add('skip', 'test impression (src: doctor): --no-impression')\n else if (!live)\n add('skip', 'test impression (src: doctor): no verified live manifest to take the token from')\n else {\n const r = await sendDoctorImpression(live)\n if ('error' in r) add('warn', `test impression (src: doctor) → ${r.url}: ${r.error}`)\n else\n add(\n r.status === 204 ? 'ok' : 'warn',\n `test impression (src: doctor) → ${r.url}: HTTP ${r.status}${r.status === 204 ? '' : ' (expected 204)'}`,\n )\n }\n // 7. signed ping (§6.6), when the backend env is here\n if (backendEnv) findings.push(await signedPing(backendEnv as Parameters<typeof signedPing>[0]))\n else add('skip', 'signed POST /v1/conversions/ping: no backend env here (doctor --backend)')\n return finish(findings)\n}\n\nfunction finish(findings: Finding[]): number {\n const icon = { ok: '✓', warn: '!', fail: '✗', skip: '–' }\n for (const f of findings) console.log(` ${icon[f.level]} ${f.text}`)\n const fails = findings.filter((f) => f.level === 'fail').length\n const warns = findings.filter((f) => f.level === 'warn').length\n console.log(`network-sdk doctor: ${fails} failed, ${warns} warnings`)\n if (fails > 0) throw new CliError(`${fails} check(s) failed`)\n return 0\n}\n","/**\n * `network-sdk doctor --backend` — spec §6.6 as amended by the Phase 3 addendum (\"`doctor --backend`\", \"doctor signed\n * ping and index check\"). For split repos: the root is `--root` or the cwd (package.json optional), frontend checks\n * are skipped, and nothing needs `src/network.config.js`. Checks: Node ≥ 18; an import scan of the backend sources\n * (`createConversionReporter`, and `referralFromRequest` or `referralMiddleware`); the SDK dependency; the four `INR_*`\n * names (process.env, else `<root>/.env`), printed as set/unset only; a signed `POST /v1/conversions/ping`; with\n * `--mongo-uri`, the three outbox helper indexes through the site's own `mongodb` driver. No env value and no Mongo\n * URI is ever printed (network errors print their code only: a DNS message would carry the hub's host name).\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { SDK_VERSION } from '../constants.js'\nimport { estimateOffsetMs, hubOrigin } from '../server/delivery.js'\nimport { OUTBOX_DEFAULT_COLLECTION, OUTBOX_INDEXES } from '../server/outbox.js'\nimport { signRequest } from '../server/sign.js'\nimport { KEY_ID_RE, SIGNED_PATHS, SITE_KEY_RE, SITE_SECRET_RE } from '../server/wire.js'\nimport { str, type Flags } from './shared.js'\n\nexport type Finding = { level: 'ok' | 'warn' | 'fail' | 'skip'; text: string }\n\nexport const BACKEND_ENV = ['INR_HUB_URL', 'INR_SITE_KEY', 'INR_KEY_ID', 'INR_SITE_SECRET'] as const\ntype BackendEnv = Record<(typeof BACKEND_ENV)[number], string>\n\n/** Backend sources: every JS / TS module flavour (no JSX). */\nexport const BACKEND_EXT = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'])\nconst PING_TIMEOUT_MS = 5000\n\n/** `KEY=value` lines of a `.env` file: no expansion; one pair of surrounding quotes is removed. */\nexport function readEnvFile(path: string): Record<string, string> {\n const out: Record<string, string> = {}\n if (!existsSync(path)) return out\n for (const line of readFileSync(path, 'utf8').split(/\\r?\\n/)) {\n const m = line.match(/^\\s*(?:export\\s+)?([A-Za-z0-9_]+)\\s*=\\s*(.*)$/)\n if (!m) continue\n const v = m[2]!.trim()\n out[m[1]!] = /^(['\"]).*\\1$/.test(v) ? v.slice(1, -1) : v\n }\n return out\n}\n\n/** Each name's value from process.env, else `<dir>/.env` ('' when neither has it). */\nexport function envValues(dir: string, names: readonly string[]): Record<string, string> {\n const file = readEnvFile(join(dir, '.env'))\n return Object.fromEntries(names.map((n) => [n, process.env[n] || file[n] || '']))\n}\n\nconst VALID: Record<keyof BackendEnv, (v: string) => boolean> = {\n INR_HUB_URL: (v) => hubOrigin(v) !== null,\n INR_SITE_KEY: (v) => SITE_KEY_RE.test(v),\n INR_KEY_ID: (v) => KEY_ID_RE.test(v),\n INR_SITE_SECRET: (v) => SITE_SECRET_RE.test(v),\n}\n\n/** The env line (names only) and whether all four are present and well-formed. */\nexport function backendEnvFinding(\n env: Record<string, string>,\n where: string,\n): { finding: Finding; complete: boolean } {\n const state = BACKEND_ENV.map((n) => {\n const v = env[n] ?? ''\n return [n, !v ? 'unset' : VALID[n](v) ? 'set' : 'set (invalid)'] as const\n })\n const complete = state.every(([, s]) => s === 'set')\n return {\n complete,\n finding: {\n level: complete ? 'ok' : 'fail',\n text: `backend env (${where}): ${state.map(([n, s]) => `${n}=${s}`).join(', ')}`,\n },\n }\n}\n\n/** A transport failure's code only (ENOTFOUND, ECONNREFUSED, CERT_HAS_EXPIRED…): its message can name the host. */\nconst transportCode = (err: unknown): string => {\n const e = err as { name?: string; cause?: { code?: unknown } }\n if (e?.name === 'AbortError' || e?.name === 'TimeoutError')\n return `timeout (${PING_TIMEOUT_MS / 1000} s)`\n return typeof e?.cause?.code === 'string' ? e.cause.code : 'connection failed'\n}\n\n/** The signed ping (§6.6): `{}` to `<INR_HUB_URL>/v1/conversions/ping`, 5 s, one attempt, redirects refused. */\nexport async function signedPing(env: BackendEnv): Promise<Finding> {\n const label = `signed POST ${SIGNED_PATHS.ping}`\n const hub = hubOrigin(env.INR_HUB_URL)!\n const body = '{}'\n const { headers } = signRequest({\n secret: env.INR_SITE_SECRET,\n siteKey: env.INR_SITE_KEY,\n keyId: env.INR_KEY_ID,\n method: 'POST',\n path: SIGNED_PATHS.ping,\n body,\n })\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), PING_TIMEOUT_MS)\n const tSend = Date.now()\n try {\n const res = await fetch(hub + SIGNED_PATHS.ping, {\n method: 'POST',\n headers: { ...headers, 'User-Agent': `network-sdk-doctor/${SDK_VERSION}` },\n body,\n redirect: 'error',\n signal: ac.signal,\n })\n const tRecv = Date.now()\n let code: string | null = null\n try {\n const json = JSON.parse((await res.text()).slice(0, 8192)) as {\n error?: { code?: unknown }\n code?: unknown\n }\n const c = json?.error?.code ?? json?.code\n code = typeof c === 'string' ? c : null\n } catch {\n code = null\n }\n const s = res.status\n if (s === 204) return { level: 'ok', text: `${label}: 204 (key id and secret accepted)` }\n if (s === 401) return { level: 'fail', text: `${label}: 401, key id or secret rejected` }\n if (s === 400 && code === 'TIMESTAMP_SKEW') {\n const offset = estimateOffsetMs(res.headers.get('date'), tSend, tRecv)\n return {\n level: 'fail',\n text: `${label}: TIMESTAMP_SKEW, this host's clock is ${\n offset === null\n ? 'off by an unknown amount'\n : `${Math.abs(Math.round(offset / 1000))} s ${offset > 0 ? 'behind' : 'ahead of'} the hub`\n } (fix NTP)`,\n }\n }\n if (s === 404) return { level: 'fail', text: `${label}: 404, wrong INR_HUB_URL?` }\n if (s === 429 || s === 503)\n return { level: 'warn', text: `${label}: ${s}${code ? ` ${code}` : ''}, retry later` }\n return { level: 'fail', text: `${label}: HTTP ${s}${code ? ` ${code}` : ''}` }\n } catch (err) {\n return { level: 'fail', text: `${label}: network error (${transportCode(err)})` }\n } finally {\n clearTimeout(timer)\n }\n}\n\ntype IndexInfo = { key: Record<string, unknown>; unique?: boolean; expireAfterSeconds?: number }\ntype MongoDriver = {\n MongoClient: new (\n uri: string,\n options: Record<string, unknown>,\n ) => {\n connect: () => Promise<unknown>\n db: () => { collection: (name: string) => { indexes: () => Promise<IndexInfo[]> } }\n close: () => Promise<void>\n }\n}\n\n/** The three helper indexes on the site's outbox collection, read with the site's own `mongodb` (never printed). */\nexport async function checkOutboxIndexes(\n root: string,\n uri: string,\n collection: string,\n): Promise<Finding> {\n const label = `outbox indexes (${collection})`\n let driver: MongoDriver\n try {\n driver = createRequire(join(root, 'package.json'))('mongodb') as MongoDriver\n } catch {\n return { level: 'fail', text: `${label}: the mongodb driver is not installed under --root` }\n }\n const client = new driver.MongoClient(uri, { serverSelectionTimeoutMS: 5000 })\n try {\n await client.connect()\n const found = await client.db().collection(collection).indexes()\n const missing = OUTBOX_INDEXES.filter(([key, options]) => {\n const ix = found.find((i) => JSON.stringify(i.key) === JSON.stringify(key))\n return (\n !ix ||\n ix.expireAfterSeconds !== options['expireAfterSeconds'] ||\n Boolean(ix.unique) !== Boolean(options['unique'])\n )\n })\n if (missing.length === 0)\n return {\n level: 'ok',\n text: `${label}: createdAt TTL 14 d, nextAttemptAt, unique (eventType, eventId)`,\n }\n return {\n level: 'fail',\n text: `${label}: missing or different: ${missing.map(([k]) => Object.keys(k).join('+')).join(', ')} (start the backend once with the reporter enabled; it creates them)`,\n }\n } catch (err) {\n const e = err as { codeName?: unknown; name?: unknown }\n const why = typeof e?.codeName === 'string' ? e.codeName : String(e?.name ?? 'error')\n return { level: 'fail', text: `${label}: ${why}` }\n } finally {\n await client.close().catch(() => undefined)\n }\n}\n\nexport type BackendScan = {\n files: number\n identifiers: Record<\n 'createConversionReporter' | 'referralFromRequest' | 'referralMiddleware',\n string[]\n >\n}\n\n/** Every backend check, in order (the caller prints them and sets the exit code). */\nexport async function backendFindings(\n root: string,\n flags: Flags,\n scan: BackendScan,\n): Promise<Finding[]> {\n const out: Finding[] = []\n const add = (level: Finding['level'], text: string) => out.push({ level, text })\n const major = Number(process.versions.node.split('.')[0])\n add(major >= 18 ? 'ok' : 'fail', `Node ${process.versions.node}${major >= 18 ? '' : ' (< 18)'}`)\n\n const ids = scan.identifiers\n add(\n ids.createConversionReporter.length > 0 ? 'ok' : 'fail',\n `createConversionReporter() (server entry): ${ids.createConversionReporter.join(', ') || 'missing'}`,\n )\n const ref = [...new Set([...ids.referralFromRequest, ...ids.referralMiddleware])]\n add(\n ref.length > 0 ? 'ok' : 'fail',\n `referralFromRequest() / referralMiddleware(): ${ref.join(', ') || 'missing'}`,\n )\n add(\n 'ok',\n `${scan.files} source file(s) scanned (node_modules, dist, build, coverage, .git skipped)`,\n )\n\n const pkgPath = join(root, 'package.json')\n if (!existsSync(pkgPath)) add('skip', 'package.json: none (optional for --backend)')\n else {\n let dep: string | undefined\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as {\n dependencies?: Record<string, string>\n }\n dep = pkg.dependencies?.['@intenque/network-sdk']\n } catch {\n add('warn', 'package.json is not valid JSON')\n }\n if (!dep) add('warn', `@intenque/network-sdk is not a dependency (CLI ${SDK_VERSION})`)\n else if (/^(file|link):/.test(dep))\n add('warn', `@intenque/network-sdk ${dep}: a local path; a deploy build cannot install it`)\n else add('ok', `@intenque/network-sdk ${dep} (CLI ${SDK_VERSION})`)\n }\n\n const env = envValues(root, [...BACKEND_ENV, 'INR_OUTBOX_COLLECTION'])\n const { finding, complete } = backendEnvFinding(\n env,\n existsSync(join(root, '.env')) ? 'process env, .env' : 'process env',\n )\n out.push(finding)\n if (complete) out.push(await signedPing(env as BackendEnv))\n else add('skip', `signed POST ${SIGNED_PATHS.ping}: needs all four INR_* values`)\n\n const uri = str(flags['mongo-uri'])\n if (!uri) add('skip', 'outbox indexes: pass --mongo-uri <uri> to check them')\n else\n out.push(\n await checkOutboxIndexes(\n root,\n uri,\n env['INR_OUTBOX_COLLECTION'] || OUTBOX_DEFAULT_COLLECTION,\n ),\n )\n return out\n}\n","/**\n * Mongo outbox for conversion reports — spec §6.5: collection `inrConversionOutbox` (TTL 14 d, matching the hub's\n * `occurredAt` lower bound), one row per `(eventType, eventId)`, storing the UNSIGNED body (every attempt re-signs\n * with a fresh timestamp and nonce). `nextAttemptAt` is the lease (addendum \"Claim and lease\"); `unauthorizedSince`\n * carries the 24 h 401 rule. The site passes its own `mongoose` (7 or 8; the model API used is identical on both); the\n * SDK has no runtime dependency on it. The schema declares no index and has `autoIndex: false`: `ensureOutboxIndexes`\n * creates the three helper indexes itself (addendum \"Outbox row v2 and indexes\"). The bounded in-memory spill holds\n * reports whose enqueue failed or timed out (addendum \"`report()`\").\n */\nexport const OUTBOX_DEFAULT_COLLECTION = 'inrConversionOutbox'\nexport const OUTBOX_TTL_SECONDS = 14 * 86_400\n/** The in-memory spill bound; beyond it the oldest entry is dropped as `memory_full`. */\nexport const SPILL_MAX = 1000\n\n/** The last failed attempt: HTTP status (0 for transport failures and missing config) and the hub's error code. */\nexport type OutboxError = { status: number; code: string | null }\n\nexport type OutboxEntry = {\n eventType: string\n eventId: string\n body: Record<string, unknown>\n attempts: number\n nextAttemptAt: Date\n lastStatus: number | null\n lastError: OutboxError | null\n /** Set by the first 401 (or missing config), cleared by any other outcome. */\n unauthorizedSince: Date | null\n createdAt: Date\n}\n\nexport type OutboxKey = Pick<OutboxEntry, 'eventType' | 'eventId'>\n\n/** What a retried attempt writes back; `attempts` is the number of attempts this sender made. */\nexport type ReschedulePatch = {\n nextAttemptAt: Date\n lastStatus: number\n lastError: OutboxError\n attempts: number\n /** The 401 / missing-config clock: kept at its earliest value; `null` clears it. */\n unauthorizedAt: Date | null\n}\n\n/** The subset of mongoose the outbox touches (so tests can pass a stub and the SDK stays dependency-free). */\nexport type MongooseLike = {\n Schema: new (definition: Record<string, unknown>, options?: Record<string, unknown>) => unknown\n models: Record<string, unknown>\n model: (name: string, schema?: unknown, collection?: string) => unknown\n}\n\ntype Thenable<T> = Promise<T> | { exec: () => Promise<T> }\ntype Filter = Record<string, unknown>\n\n/** The model methods used (addendum \"Mongoose 7 and 8\"). */\nexport type OutboxModelLike = {\n updateOne: (filter: Filter, update: Filter, options?: Filter) => Thenable<unknown>\n findOneAndUpdate?: (filter: Filter, update: Filter, options?: Filter) => Thenable<unknown>\n deleteOne?: (filter: Filter) => Thenable<unknown>\n deleteMany?: (filter: Filter) => Thenable<unknown>\n countDocuments: (filter?: Filter) => Thenable<number>\n collection?: { createIndex: (keys: Filter, options?: Filter) => Promise<unknown> }\n}\n\nexport function outboxSchemaDefinition(): Record<string, unknown> {\n return {\n eventType: { type: String, required: true },\n eventId: { type: String, required: true },\n body: { type: Object, required: true },\n attempts: { type: Number, default: 0 },\n nextAttemptAt: { type: Date, default: Date.now },\n lastStatus: { type: Number, default: null },\n lastError: { type: Object, default: null },\n // no default and never stored as null: `$min` keeps a null (BSON orders it before every date)\n unauthorizedSince: { type: Date },\n createdAt: { type: Date, default: Date.now },\n }\n}\n\n/** The three helper indexes (default names), created by `ensureOutboxIndexes`, never by `autoIndex`. */\nexport const OUTBOX_INDEXES: readonly [Filter, Filter][] = [\n [{ createdAt: 1 }, { expireAfterSeconds: OUTBOX_TTL_SECONDS }],\n [{ nextAttemptAt: 1 }, {}],\n [{ eventType: 1, eventId: 1 }, { unique: true }],\n]\n\n/** Builds (or reuses) the outbox model on the given mongoose instance. */\nexport function outboxModel(\n mongoose: MongooseLike,\n collection: string = OUTBOX_DEFAULT_COLLECTION,\n): OutboxModelLike {\n const name = `InrOutbox_${collection}`\n const existing = mongoose.models[name]\n if (existing) return existing as OutboxModelLike\n const schema = new mongoose.Schema(outboxSchemaDefinition(), {\n versionKey: false,\n autoIndex: false,\n })\n return mongoose.model(name, schema, collection) as OutboxModelLike\n}\n\nconst indexed = new WeakSet<object>()\nconst indexWarned = new WeakSet<object>()\nconst indexing = new WeakMap<object, Promise<void>>()\n/**\n * Creates the helper indexes (`collection.createIndex`, so `autoIndex: false` sites get them too): each one on its\n * own, so a conflict on one leaves the others created. Called at construction and at every drain tick until all three\n * succeed (a reporter built before `mongoose.connect()` times out its first try). Failures are one warn\n * `INR_OUTBOX_INDEX_FAILED` per process and model; reporting goes on without them.\n */\nexport function ensureOutboxIndexes(\n model: OutboxModelLike,\n warn: (message: string) => void,\n): Promise<void> {\n if (indexed.has(model)) return Promise.resolve()\n let running = indexing.get(model)\n if (!running) {\n running = (async () => {\n // one after the other (concurrent builds on a new collection contend), each failure on its own\n let failed = false\n for (const [keys, options] of OUTBOX_INDEXES)\n await Promise.resolve()\n .then(() => model.collection!.createIndex(keys, options))\n .catch(() => (failed = true))\n if (!failed) indexed.add(model)\n else if (!indexWarned.has(model)) {\n indexWarned.add(model)\n warn('INR_OUTBOX_INDEX_FAILED')\n }\n })().finally(() => indexing.delete(model))\n indexing.set(model, running)\n }\n return running\n}\n\nconst run = <T>(r: Thenable<T>): Promise<T> =>\n 'exec' in r && typeof r.exec === 'function' ? r.exec() : (r as Promise<T>)\n\nexport type Outbox = {\n /**\n * Idempotent on `(eventType, eventId)`: inserts the entry's whole state once; a repeat leaves the existing row\n * alone. Resolves `true` when this call inserted the row.\n */\n enqueue: (entry: OutboxEntry) => Promise<boolean>\n /** Success or a drop. */\n remove: (key: OutboxKey) => Promise<void>\n /** A retried attempt. Never creates a row. */\n reschedule: (key: OutboxKey, patch: ReschedulePatch) => Promise<void>\n /** An in-process chain renews its row's lease before each retry. */\n renew: (key: OutboxKey, until: Date) => Promise<void>\n /** The drain claim: the most overdue due row, its lease moved to `until` and `attempts + 1`; null when none. */\n claim: (now: Date, until: Date) => Promise<OutboxEntry | null>\n /** `forget()`: deletes every row whose body names the click id (as `clickId` or `firstClickId`). */\n purge: (clickId: string) => Promise<void>\n pending: () => Promise<number>\n}\n\nconst keyOf = (k: OutboxKey): OutboxKey => ({ eventType: k.eventType, eventId: k.eventId })\n\n/** A stored row as the drainer uses it: a Phase 1c string `lastError` reads as `{ status: 0, code }`. */\nexport function rowOf(doc: Record<string, unknown>): OutboxEntry {\n const e = doc['lastError']\n return {\n ...(doc as OutboxEntry),\n lastError: typeof e === 'string' ? { status: 0, code: e } : ((e as OutboxError) ?? null),\n unauthorizedSince: (doc['unauthorizedSince'] as Date | undefined) ?? null,\n }\n}\n\nexport function createOutbox(model: OutboxModelLike): Outbox {\n return {\n async enqueue(entry) {\n const { eventType, eventId, unauthorizedSince, ...rest } = entry\n const state = unauthorizedSince ? { ...rest, unauthorizedSince } : rest\n const r = (await run(\n model.updateOne({ eventType, eventId }, { $setOnInsert: state }, { upsert: true }),\n )) as { matchedCount?: number } | null\n return !(Number(r?.matchedCount) > 0)\n },\n async remove(key) {\n await run(model.deleteOne!(keyOf(key)))\n },\n async reschedule(key, p) {\n const update: Record<string, unknown> = {\n $set: { nextAttemptAt: p.nextAttemptAt, lastStatus: p.lastStatus, lastError: p.lastError },\n ...(p.attempts > 0 ? { $inc: { attempts: p.attempts } } : {}),\n // $min sets a missing field and never moves an earlier 401 clock forward\n ...(p.unauthorizedAt\n ? { $min: { unauthorizedSince: p.unauthorizedAt } }\n : { $unset: { unauthorizedSince: 1 } }),\n }\n await run(model.updateOne(keyOf(key), update))\n },\n async renew(key, until) {\n await run(model.updateOne(keyOf(key), { $set: { nextAttemptAt: until } }))\n },\n async claim(now, until) {\n const doc = (await run(\n model.findOneAndUpdate!(\n { nextAttemptAt: { $lte: now } },\n { $set: { nextAttemptAt: until }, $inc: { attempts: 1 } },\n { sort: { nextAttemptAt: 1 }, new: true, lean: true },\n ),\n )) as Record<string, unknown> | null\n return doc ? rowOf(doc) : null\n },\n async purge(clickId) {\n await run(\n model.deleteMany!({ $or: [{ 'body.clickId': clickId }, { 'body.firstClickId': clickId }] }),\n )\n },\n pending: () => run(model.countDocuments({})),\n }\n}\n\n/** Whether a queued body names the click id (forget's purge). */\nexport const names = (e: OutboxEntry, clickId: string): boolean =>\n e.body['clickId'] === clickId || e.body['firstClickId'] === clickId\n\nconst same = (a: OutboxKey, b: OutboxKey): boolean =>\n a.eventType === b.eventType && a.eventId === b.eventId\n\n/** Applies a reschedule patch to an in-memory row (memory outbox and spill). */\nexport function applyPatch(e: OutboxEntry, p: ReschedulePatch): void {\n e.nextAttemptAt = p.nextAttemptAt\n e.lastStatus = p.lastStatus\n e.lastError = p.lastError\n e.attempts += p.attempts\n e.unauthorizedSince = p.unauthorizedAt\n ? new Date(Math.min(e.unauthorizedSince?.getTime() ?? Infinity, p.unauthorizedAt.getTime()))\n : null\n}\n\n/** In-memory outbox for sites without Mongo (tests, `enabled: false`): lost on restart, by design. */\nexport function createMemoryOutbox(): Outbox & { entries: OutboxEntry[] } {\n const entries: OutboxEntry[] = []\n const find = (key: OutboxKey) => entries.find((x) => same(x, key))\n return {\n entries,\n async enqueue(entry) {\n if (find(entry)) return false\n entries.push({ ...entry })\n return true\n },\n async remove(key) {\n const i = entries.findIndex((e) => same(e, key))\n if (i >= 0) entries.splice(i, 1)\n },\n async reschedule(key, p) {\n const e = find(key)\n if (e) applyPatch(e, p)\n },\n async renew(key, until) {\n const e = find(key)\n if (e) e.nextAttemptAt = until\n },\n async claim(now, until) {\n let due: OutboxEntry | undefined\n for (const e of entries)\n if (e.nextAttemptAt <= now && (!due || e.nextAttemptAt < due.nextAttemptAt)) due = e\n if (!due) return null\n due.nextAttemptAt = until\n due.attempts += 1\n return { ...due }\n },\n async purge(clickId) {\n for (let i = entries.length - 1; i >= 0; i--)\n if (names(entries[i]!, clickId)) entries.splice(i, 1)\n },\n pending: async () => entries.length,\n }\n}\n\n/** Reports whose enqueue failed or timed out, oldest first, at most `max`. */\nexport type Spill = {\n /** Adds the entry unless its key is already spilled; returns the oldest entry it evicted, if any. */\n add: (entry: OutboxEntry) => OutboxEntry | null\n has: (key: OutboxKey) => boolean\n get: (key: OutboxKey) => OutboxEntry | undefined\n delete: (key: OutboxKey) => void\n entries: () => OutboxEntry[]\n readonly size: number\n}\n\nexport function createSpill(max: number = SPILL_MAX): Spill {\n const map = new Map<string, OutboxEntry>()\n const k = (e: OutboxKey): string => `${e.eventType}\\u0000${e.eventId}`\n return {\n add(entry) {\n if (map.has(k(entry))) return null\n map.set(k(entry), entry)\n if (map.size <= max) return null\n const [oldest, evicted] = map.entries().next().value as [string, OutboxEntry]\n map.delete(oldest)\n return evicted\n },\n has: (key) => map.has(k(key)),\n get: (key) => map.get(k(key)),\n delete: (key) => void map.delete(k(key)),\n entries: () => [...map.values()],\n get size() {\n return map.size\n },\n }\n}\n\n/** Moves every spilled entry, with its current state, into the outbox (the drainer runs this first on each tick). */\nexport async function moveSpill(spill: Spill, outbox: Outbox): Promise<number> {\n let moved = 0\n for (const e of spill.entries()) {\n await outbox.enqueue(e)\n spill.delete(e)\n moved++\n }\n return moved\n}\n","/**\n * Conversion request signing — spec §5.4 as amended by the 2.2 changelog (addendum \"HMAC base string\"):\n * `X-INR-Signature: v1=<hex HMAC-SHA256(secret, timestamp + \".\" + nonce + \".\" + siteKey + \".\" + keyId + \".\" + METHOD\n * + \".\" + path + \".\" + sha256hex(rawBody))>` with `X-INR-Site`, `X-INR-Key-Id`, `X-INR-Timestamp` (unix seconds),\n * `X-INR-Nonce` (uuid v4). Mirrors packages/shared `hmacBaseV1` without importing it; the committed vectors\n * (packages/shared/fixtures/hmac-v1.vectors.json) pin both. Exported for sites with non-Node backends to port (§6.5).\n * Node-only.\n */\nimport { createHash, createHmac, randomUUID, timingSafeEqual } from 'node:crypto'\n\nexport type SignInput = {\n secret: string\n siteKey: string\n keyId: string\n method: string\n /** Path only (`/v1/conversions`, `/v1/conversions/ping`, `/v1/referrals/<clickId>`, `/v1/landings`). */\n path: string\n /** The exact bytes sent (`''` for an empty body). */\n body: string | Uint8Array\n /** Unix seconds; defaults to now. */\n timestamp?: number\n /** uuid v4; defaults to a fresh one. Every attempt re-signs with a fresh nonce (§6.5). */\n nonce?: string\n}\n\nexport type SignedHeaders = {\n 'Content-Type': 'application/json'\n 'X-INR-Site': string\n 'X-INR-Key-Id': string\n 'X-INR-Timestamp': string\n 'X-INR-Nonce': string\n 'X-INR-Signature': string\n}\n\nexport function sha256Hex(data: string | Uint8Array): string {\n return createHash('sha256').update(data).digest('hex')\n}\n\n/** The v1 HMAC base string: the identifiers are bound, so a signature never verifies under another site or key. */\nexport function hmacBase(\n timestamp: number,\n nonce: string,\n siteKey: string,\n keyId: string,\n method: string,\n path: string,\n body: string | Uint8Array,\n): string {\n return `${timestamp}.${nonce}.${siteKey}.${keyId}.${method.toUpperCase()}.${path}.${sha256Hex(body)}`\n}\n\nexport function signRequest(input: SignInput): {\n headers: SignedHeaders\n timestamp: number\n nonce: string\n signature: string\n} {\n if (!input.secret) throw new Error('signRequest: secret is required')\n const timestamp = input.timestamp ?? Math.floor(Date.now() / 1000)\n const nonce = input.nonce ?? randomUUID()\n const mac = createHmac('sha256', input.secret)\n .update(\n hmacBase(timestamp, nonce, input.siteKey, input.keyId, input.method, input.path, input.body),\n 'utf8',\n )\n .digest('hex')\n const signature = `v1=${mac}`\n return {\n headers: {\n 'Content-Type': 'application/json',\n 'X-INR-Site': input.siteKey,\n 'X-INR-Key-Id': input.keyId,\n 'X-INR-Timestamp': String(timestamp),\n 'X-INR-Nonce': nonce,\n 'X-INR-Signature': signature,\n },\n timestamp,\n nonce,\n signature,\n }\n}\n\n/** Constant-time check of a `v1=<hex>` signature (what the hub does; here for tests and ports). */\nexport function verifySignature(\n secret: string,\n input: {\n timestamp: number\n nonce: string\n siteKey: string\n keyId: string\n method: string\n path: string\n body: string | Uint8Array\n },\n signature: string,\n): boolean {\n if (!/^v1=[0-9a-f]{64}$/.test(signature)) return false\n const { timestamp, nonce, siteKey, keyId, method, path, body } = input\n const expected = createHmac('sha256', secret)\n .update(hmacBase(timestamp, nonce, siteKey, keyId, method, path, body), 'utf8')\n .digest('hex')\n return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature.slice(3), 'hex'))\n}\n","/**\n * The conversion wire contract as the SDK mirrors it — Phase 3 addendum \"ConversionBodySchema home and SDK mirror\",\n * \"EventId\", \"Link tables\", \"Per-type body rules\", \"`fee_charged`\", \"Site secret format\", \"`keyId`\". packages/shared\n * is the source; the server entry never imports it (zero runtime deps), and test/conversions-contract.test.ts fails\n * when either side changes alone.\n */\n\n/** §5.5 canonical conversion types plus `fee_charged` (R2(c)): 12 entries, in the hub's order. */\nexport const CONVERSION_TYPES = [\n 'booking_created',\n 'booking_paid',\n 'contact_submitted',\n 'quote_requested',\n 'call_clicked',\n 'signup',\n 'purchase',\n 'booking_cancelled',\n 'booking_refunded',\n 'booking_no_show',\n 'chargeback',\n 'fee_charged',\n] as const\nexport type ConversionType = (typeof CONVERSION_TYPES)[number]\n\n/** §5.5 reversal targets. */\nexport const REVERSAL_TARGETS = {\n booking_cancelled: ['booking_created'],\n booking_no_show: ['booking_created'],\n booking_refunded: ['booking_paid'],\n chargeback: ['booking_paid', 'purchase'],\n} as const satisfies Partial<Record<ConversionType, readonly ConversionType[]>>\n/** Every type that carries `reverses`; a fee names the no-show or cancel it belongs to (it is not a reversal). */\nexport const LINK_TARGETS = {\n ...REVERSAL_TARGETS,\n fee_charged: ['booking_no_show', 'booking_cancelled'],\n} as const satisfies Partial<Record<ConversionType, readonly ConversionType[]>>\nexport type LinkedType = keyof typeof LINK_TARGETS\nexport const REVERSAL_TYPES = Object.freeze(\n Object.keys(REVERSAL_TARGETS) as (keyof typeof REVERSAL_TARGETS)[],\n)\n\ntype AmountRule = 'null' | 'optional' | 'required' | 'positive'\n/** Per-type amount rules: `null` forbids, `optional` allows null or 0…1e9, `required` 0…1e9, `positive` 1…1e9. */\nexport const CONVERSION_BODY_RULES = {\n booking_created: { value: 'optional', fee: 'null' },\n booking_paid: { value: 'required', fee: 'null' },\n contact_submitted: { value: 'null', fee: 'null' },\n quote_requested: { value: 'optional', fee: 'null' },\n call_clicked: { value: 'null', fee: 'null' },\n signup: { value: 'optional', fee: 'null' },\n purchase: { value: 'optional', fee: 'null' },\n booking_cancelled: { value: 'null', fee: 'optional' },\n booking_refunded: { value: 'positive', fee: 'null' },\n booking_no_show: { value: 'null', fee: 'optional' },\n chargeback: { value: 'positive', fee: 'null' },\n fee_charged: { value: 'null', fee: 'positive' },\n} as const satisfies Record<ConversionType, { value: AmountRule; fee: AmountRule }>\n\n/** `valueCents` / `feeCents` upper bound. */\nexport const AMOUNT_MAX_CENTS = 1_000_000_000\n/** A lower-case site prefix, `:`, an id without `.`, `@`, `+`, `:` or whitespace; ≤ 64 in all; never `erased:`. */\nexport const EVENT_ID_RE = /^(?=.{1,64}$)(?!erased:)[a-z][a-z0-9_]{0,31}:[A-Za-z0-9_-]{1,62}$/\nexport const CURRENCY_RE = /^[A-Z]{3}$/\n/** `INR_SITE_SECRET`: `'inrs1_' + base64url(32 bytes)`; the HMAC key is the string's UTF-8 bytes. */\nexport const SITE_SECRET_RE = /^inrs1_[A-Za-z0-9_-]{43}$/\nexport const KEY_ID_RE = /^[0-9a-f]{8}$/\n/** `X-INR-Site`: a slug of ≤ 48 characters. */\nexport const SITE_KEY_RE = /^(?=.{1,48}$)[a-z0-9]+(-[a-z0-9]+)*$/\n/** `sdk` on the wire and on the model. */\nexport const SDK_FIELD_MAX = 40\n\n/** The literal path each signed endpoint signs (forget appends the click id). */\nexport const SIGNED_PATHS = {\n conversion: '/v1/conversions',\n ping: '/v1/conversions/ping',\n landing: '/v1/landings',\n forget: '/v1/referrals/',\n} as const\n\nexport const isLinkedType = (t: string): t is LinkedType => Object.hasOwn(LINK_TARGETS, t)\n\n/** Whether an amount satisfies its per-type rule (integer 0…1e9 or null, then the rule). */\nexport function amountOk(rule: AmountRule, v: number | null): boolean {\n if (v === null) return rule === 'null' || rule === 'optional'\n if (!(Number.isInteger(v) && v >= (rule === 'positive' ? 1 : 0) && v <= AMOUNT_MAX_CENTS))\n return false\n return rule !== 'null'\n}\n","/**\n * Signed delivery to the hub — Phase 3 addendum \"SDK server\": Transport, Response classification, In-process attempts,\n * TIMESTAMP_SKEW, 401 and missing config. One attempt = resolve config, sign `JSON.stringify(body)` with a fresh\n * timestamp and nonce, POST it, classify the answer; an attempt's outcome is written back to the outbox (and the\n * spill) by `settle`. The outbox drainer (drain.ts) reuses `attempt` and `settle`; `forget()` and `landingRelay()`\n * reuse `sendSigned`.\n */\nimport { SDK_VERSION } from '../constants.js'\nimport {\n applyPatch,\n type Outbox,\n type OutboxEntry,\n type OutboxError,\n type ReschedulePatch,\n type Spill,\n} from './outbox.js'\nimport { signRequest } from './sign.js'\nimport { SIGNED_PATHS } from './wire.js'\n\nconst SEC = 1000\n// Module constants rather than a property bag: the minifier renames them (server entry size budget).\n/** Retries after attempt 1 (§6.5 \"2 s, 8 s, 30 s\"); a longer Retry-After replaces the next delay. */\nexport const RETRY_DELAYS_MS = [2 * SEC, 8 * SEC, 30 * SEC] as const\n/** A Retry-After above this ends the in-process chain. */\nexport const IN_PROCESS_MAX_DELAY_MS = 30 * SEC\n/** Cap on a Retry-After written to `nextAttemptAt`. */\nexport const RETRY_AFTER_CAP_MS = 3600 * SEC\n/** `nextAttemptAt` after the in-process retries. */\nexport const AFTER_RETRIES_MS = 60 * SEC\n/** 401 / missing config: next try in 10 min; dropped once 24 h have passed since the first. */\nexport const UNAUTHORIZED_RETRY_MS = 10 * 60 * SEC\nexport const UNAUTHORIZED_MAX_MS = 24 * 3600 * SEC\n/** The lease `report()` writes with the row (the drainer claims rows whose lease ran out). */\nexport const LEASE_MS = 150 * SEC\nexport const TIMEOUT_DEFAULT_MS = 3000\nconst TIMEOUT_MIN_MS = 500\nconst TIMEOUT_MAX_MS = 30_000\nexport const RESPONSE_MAX_BYTES = 8192\n/** Outcome writes give up waiting after this (the row's lease covers a write that never lands). */\nconst WRITE_TIMEOUT_MS = 2000\n/** A skew estimate is used only within ±24 h. */\nexport const SKEW_MAX_OFFSET_MS = 24 * 3600 * SEC\n\nexport type Logger = {\n warn: (message: string, meta?: Record<string, unknown>) => void\n info?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** `INR_HUB_URL` as an origin (one trailing `/` stripped): `https:`, or `http:` for loopback only; else null. */\nexport function hubOrigin(raw: string | undefined): string | null {\n if (!raw) return null\n const s = raw.endsWith('/') ? raw.slice(0, -1) : raw\n if (!/^[a-z][a-z0-9+.-]*:\\/\\/[^/?#]+$/i.test(s)) return null\n let u: URL\n try {\n u = new URL(s)\n } catch {\n return null\n }\n if (u.pathname !== '/' || u.username || u.password) return null\n if (u.protocol === 'https:') return u.origin\n if (u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname))\n return u.origin\n return null\n}\n\n/** `timeoutMs ?? INR_TIMEOUT_MS ?? 3000`, clamped to [500, 30 000] ms (a blank env value is unset, not 0). */\nexport function timeoutMsOf(opt: number | undefined, envValue: string | undefined): number {\n const s = envValue?.trim()\n const n = opt ?? (s ? Number(s) : undefined)\n const v = n === undefined || !Number.isFinite(n) ? TIMEOUT_DEFAULT_MS : n\n return Math.min(TIMEOUT_MAX_MS, Math.max(TIMEOUT_MIN_MS, v))\n}\n\nexport type Classification = 'success' | 'terminal' | 'retryable' | 'skew' | 'unauthorized'\n\n/**\n * The §6.5 matrix (status first, then code; a missing body is classified by status). Status 0 is a transport failure\n * (network error, timeout).\n */\nexport function classifyResponse(status: number, code: string | null): Classification {\n if (status >= 200 && status < 300) return 'success'\n if (status === 409) return code === 'NONCE_REUSED' ? 'success' : 'terminal'\n if (status === 400) return code === 'TIMESTAMP_SKEW' ? 'skew' : 'terminal'\n if (status === 401) return 'unauthorized'\n if (status === 0 || status === 408 || status === 425 || status === 429) return 'retryable'\n if (status >= 500 && status < 600) return 'retryable'\n return 'terminal'\n}\n\n/** `Retry-After` as delta seconds or an HTTP-date, in ms from `nowMs`; null when absent or unparseable. */\nexport function parseRetryAfter(value: string | null | undefined, nowMs: number): number | null {\n if (!value) return null\n const v = value.trim()\n if (/^\\d+$/.test(v)) return Number(v) * SEC\n const at = Date.parse(v)\n return Number.isNaN(at) ? null : Math.max(0, at - nowMs)\n}\n\n/** Process-wide estimate of `hubClock − Date.now()`, shared by every reporter; never persisted. */\nlet clockOffsetMs = 0\nexport const clockOffset = {\n get: (): number => clockOffsetMs,\n /** Tests only. */\n reset: (): void => {\n clockOffsetMs = 0\n },\n}\n/** NTP-style: the hub's `Date` (second resolution, so + 500 ms) against the request's midpoint. */\nexport function estimateOffsetMs(\n dateHeader: string | null,\n tSend: number,\n tRecv: number,\n): number | null {\n const hub = dateHeader ? Date.parse(dateHeader) : NaN\n if (Number.isNaN(hub)) return null\n const offset = Math.round(hub + 500 - (tSend + tRecv) / 2)\n return Math.abs(offset) <= SKEW_MAX_OFFSET_MS ? offset : null\n}\n\nexport type DeliveryConfig = { hubUrl: string; siteKey: string; keyId: string; secret: string }\n\nexport type SendResult = {\n status: number\n code: string | null\n retryAfterMs: number | null\n date: string | null\n tSend: number\n tRecv: number\n}\n\n/** At most `max` bytes of the answer (enough for the error envelope); the rest is never read. */\nasync function readCapped(res: Response, max: number): Promise<string> {\n const reader = res.body?.getReader()\n if (!reader) return ''\n const dec = new TextDecoder()\n let text = ''\n for (let n = 0; n < max;) {\n const { done, value } = await reader.read()\n if (done) break\n text += dec.decode(value.subarray(0, max - n), { stream: true })\n n += value.byteLength\n }\n reader.cancel().catch(() => undefined)\n return text\n}\n\nfunction errorCodeOf(text: string): string | null {\n try {\n const json = JSON.parse(text) as { error?: { code?: unknown }; code?: unknown } | null\n const code = json?.error?.code ?? json?.code ?? null\n return typeof code === 'string' ? code : null\n } catch {\n return null\n }\n}\n\n/** One signed request. Never throws: transport failures come back as status 0 with code NETWORK / TIMEOUT. */\nexport async function sendSigned(\n cfg: DeliveryConfig,\n req: SignedReq,\n timeoutMs: number,\n): Promise<SendResult> {\n const timestamp = Math.floor((Date.now() + clockOffsetMs) / 1000)\n const { headers } = signRequest({ ...cfg, ...req, timestamp })\n const ctl = new AbortController()\n const timer = setTimeout(() => ctl.abort(), timeoutMs)\n ;(timer as { unref?: () => void }).unref?.()\n const tSend = Date.now()\n try {\n const res = await fetch(cfg.hubUrl + req.path, {\n method: req.method,\n headers: { ...headers, 'User-Agent': `intenque-network-sdk/${SDK_VERSION}` },\n body: req.body,\n redirect: 'error',\n signal: ctl.signal,\n })\n const tRecv = Date.now()\n const text = await readCapped(res, RESPONSE_MAX_BYTES).catch(() => '')\n return {\n status: res.status,\n code: errorCodeOf(text),\n retryAfterMs: parseRetryAfter(res.headers.get('retry-after'), tRecv),\n date: res.headers.get('date'),\n tSend,\n tRecv,\n }\n } catch {\n const tRecv = Date.now()\n const code = ctl.signal.aborted ? 'TIMEOUT' : 'NETWORK'\n return { status: 0, code, retryAfterMs: null, date: null, tSend, tRecv }\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport type AttemptOutcome =\n | {\n kind: Exclude<Classification, 'skew'>\n status: number\n code: string | null\n retryAfterMs: number | null\n }\n | { kind: 'skew'; status: 400; code: 'TIMESTAMP_SKEW'; offsetMs: number | null }\n /** No global fetch: nothing sent, the row stays queued. */\n | { kind: 'nofetch' }\n\nexport type DropReason = 'terminal' | 'unauthorized_24h' | 'expired' | 'memory_full'\n/** How a settled attempt counts in `drainNow()`; null when nothing was written (no fetch). */\nexport type Settled = 'sent' | 'retried' | 'dropped' | null\n\nexport type SignedReq = { method: 'POST' | 'DELETE'; path: string; body: string }\n\nexport type Delivery = {\n /** One signed request (forget, relay): config re-read, the skew re-sign, never throws. */\n request: (req: SignedReq) => Promise<AttemptOutcome>\n /** One attempt for a queued body, including the one immediate re-signed retry after TIMESTAMP_SKEW. */\n attempt: (entry: OutboxEntry) => Promise<AttemptOutcome>\n /**\n * Writes an attempt's outcome back (`retryInMs`: a retryable answer's delay). `attempts` = attempts this sender made\n * since it last wrote the row (0 after a drain claim, which counted its own).\n */\n settle: (\n entry: OutboxEntry,\n out: AttemptOutcome,\n attempts: number,\n retryInMs?: number,\n ) => Promise<Settled>\n /** Deletes the row and spill entry, then the `inr_outbox_dropped_total` warn line. */\n drop: (entry: OutboxEntry, error: OutboxError, reason: DropReason) => Promise<'dropped'>\n /** Attempt 1, then up to 3 retries in process; the final outcome is settled. */\n chain: (entry: OutboxEntry) => Promise<Settled>\n}\n\nexport type DeliveryDeps = {\n /** Resolved at every attempt (env re-read, rotation picked up); null when a value is missing or invalid. */\n config: () => DeliveryConfig | null\n timeoutMs: number\n outbox: Outbox\n spill: Spill\n logger: Logger\n}\n\n/** Drain backoff: `min(60 s × 2^max(0, attempts − 4), 1800 s) × U(0.8, 1.2)`. */\nexport function drainBackoffMs(attempts: number): number {\n const base = Math.min(60 * SEC * 2 ** Math.max(0, attempts - 4), 1800 * SEC)\n return Math.round(base * (0.8 + Math.random() * 0.4))\n}\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n const t = setTimeout(resolve, ms)\n ;(t as { unref?: () => void }).unref?.()\n })\n\nexport function createDelivery(deps: DeliveryDeps): Delivery {\n const { outbox, spill, logger } = deps\n const once = new Set<string>()\n const warnOnce = (code: string): void => {\n if (once.has(code)) return\n once.add(code)\n logger.warn(code)\n }\n\n const outcomeOf = (r: SendResult): AttemptOutcome => {\n const kind = classifyResponse(r.status, r.code)\n if (kind === 'skew')\n return {\n kind,\n status: 400,\n code: 'TIMESTAMP_SKEW',\n offsetMs: estimateOffsetMs(r.date, r.tSend, r.tRecv),\n }\n return { kind, status: r.status, code: r.code, retryAfterMs: r.retryAfterMs }\n }\n\n /** One signed request with the current config, plus the one immediate re-signed retry after TIMESTAMP_SKEW. */\n async function request(req: SignedReq): Promise<AttemptOutcome> {\n const cfg = deps.config()\n // missing config sends nothing and follows the 401 path (addendum \"401 and missing config\")\n if (!cfg)\n return { kind: 'unauthorized', status: 0, code: 'INR_ENV_MISSING', retryAfterMs: null }\n if (typeof globalThis.fetch !== 'function') {\n warnOnce('INR_NO_FETCH')\n return { kind: 'nofetch' }\n }\n let out = outcomeOf(await sendSigned(cfg, req, deps.timeoutMs))\n if (out.kind === 'skew' && out.offsetMs !== null) {\n clockOffsetMs = out.offsetMs\n out = outcomeOf(await sendSigned(cfg, req, deps.timeoutMs))\n if (out.kind === 'skew' && out.offsetMs !== null) clockOffsetMs = out.offsetMs\n }\n return out\n }\n const attempt = (entry: OutboxEntry): Promise<AttemptOutcome> =>\n request({ method: 'POST', path: SIGNED_PATHS.conversion, body: JSON.stringify(entry.body) })\n\n /** An outbox write, errors ignored and bounded: a hung Mongo cannot pin a finished chain. */\n const ignore = (write: () => Promise<unknown>): Promise<void> => {\n let timer: ReturnType<typeof setTimeout> | undefined\n return Promise.race([\n Promise.resolve().then(write),\n new Promise((resolve) => {\n timer = setTimeout(resolve, WRITE_TIMEOUT_MS)\n }),\n ]).then(\n () => clearTimeout(timer),\n () => clearTimeout(timer),\n )\n }\n\n const remove = async (entry: OutboxEntry): Promise<void> => {\n spill.delete(entry)\n await ignore(() => outbox.remove(entry))\n }\n const drop = async (\n entry: OutboxEntry,\n error: OutboxError,\n reason: DropReason,\n ): Promise<'dropped'> => {\n await remove(entry)\n logger.warn('inr_outbox_dropped_total', {\n eventType: entry.eventType,\n eventId: entry.eventId,\n status: error.status,\n code: error.code,\n reason,\n })\n return 'dropped'\n }\n const reschedule = async (entry: OutboxEntry, patch: ReschedulePatch): Promise<void> => {\n const spilled = spill.get(entry)\n if (spilled) applyPatch(spilled, patch)\n await ignore(() => outbox.reschedule(entry, patch))\n }\n\n async function settle(\n entry: OutboxEntry,\n out: AttemptOutcome,\n attempts: number,\n retryInMs: number = AFTER_RETRIES_MS,\n ): Promise<Settled> {\n if (out.kind === 'nofetch') return null\n const now = Date.now()\n const error: OutboxError = { status: out.status, code: out.code }\n const retry = async (patch: ReschedulePatch): Promise<Settled> => {\n await reschedule(entry, patch)\n return 'retried'\n }\n switch (out.kind) {\n case 'success':\n await remove(entry)\n return 'sent'\n case 'terminal':\n return drop(entry, error, 'terminal')\n case 'unauthorized': {\n const since = entry.unauthorizedSince?.getTime()\n if (since !== undefined && now - since >= UNAUTHORIZED_MAX_MS) {\n logger.warn('INR_UNAUTHORIZED', {\n eventType: entry.eventType,\n eventId: entry.eventId,\n ...error,\n })\n return drop(entry, error, 'unauthorized_24h')\n }\n return retry({\n nextAttemptAt: new Date(now + UNAUTHORIZED_RETRY_MS),\n lastStatus: out.status,\n lastError: error,\n attempts,\n unauthorizedAt: new Date(now),\n })\n }\n case 'skew':\n logger.warn('INR_CLOCK_SKEW', { offsetMs: out.offsetMs })\n return retry({\n nextAttemptAt: new Date(now + drainBackoffMs(entry.attempts + attempts)),\n lastStatus: 400,\n lastError: error,\n attempts,\n unauthorizedAt: null,\n })\n case 'retryable':\n return retry({\n nextAttemptAt: new Date(now + retryInMs),\n lastStatus: out.status,\n lastError: error,\n attempts,\n unauthorizedAt: null,\n })\n }\n }\n\n async function chain(entry: OutboxEntry): Promise<Settled> {\n const delays = RETRY_DELAYS_MS\n for (let i = 0; ; i++) {\n const out = await attempt(entry)\n if (out.kind !== 'retryable') return settle(entry, out, i + 1)\n const retryAfter =\n out.retryAfterMs === null ? null : Math.min(out.retryAfterMs, RETRY_AFTER_CAP_MS)\n if (i === delays.length)\n return settle(entry, out, i + 1, Math.max(AFTER_RETRIES_MS, retryAfter ?? 0))\n const delay = Math.max(delays[i]!, retryAfter ?? 0)\n // a Retry-After above 30 s (BREAKER_OPEN's 60) goes straight to the outbox\n if (delay > IN_PROCESS_MAX_DELAY_MS) return settle(entry, out, i + 1, retryAfter!)\n await sleep(delay)\n // renew the lease so no drain tick claims the row while this chain still owns it\n const until = new Date(Date.now() + LEASE_MS)\n const spilled = spill.get(entry)\n if (spilled) spilled.nextAttemptAt = until\n await ignore(() => outbox.renew(entry, until))\n }\n }\n\n return { request, attempt, settle, drop, chain }\n}\n","/**\n * CLI plumbing shared by `init`, `sync-fallback`, `doctor`, `print-config` — spec §6.6. Node-only, no dependencies.\n * Verification uses the SDK's own WebCrypto verifier, so the CLI accepts exactly what the browser accepts. Node ≥ 19\n * has `globalThis.crypto.subtle`; on Node 18 the bin entry (`index.ts`) polyfills it from `node:crypto`, and\n * `requireWebCrypto` fails fast when it is still missing.\n */\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport {\n CONFIG_PATH,\n DEFAULT_CDN_BASE,\n FALLBACK_PATH,\n MANIFEST_MAX_BYTES,\n manifestUrlFor,\n type SiteChannel,\n} from '../constants.js'\nimport { rootPublicKeys } from '../rootPublicKeys.js'\nimport { parsePublicJwk, KID_RE } from '../verify/schema.js'\nimport {\n subtle,\n verifyManifest,\n verifyManifestBytes,\n type Manifest,\n type ManifestVerifyResult,\n type RootKey,\n} from '../verify/index.js'\n\nexport class CliError extends Error {\n constructor(\n message: string,\n public readonly exitCode: 1 | 2 = 1,\n ) {\n super(message)\n }\n}\n\n/** The verifier needs `crypto.subtle` (Node ≥ 19, or Node 18 through the bin entry's `node:crypto` polyfill). */\nexport function requireWebCrypto(): void {\n if (subtle() === null)\n throw new CliError('WebCrypto unavailable (Node >= 18 with webcrypto required)')\n}\n\nexport type Flags = Record<string, string | boolean | undefined>\n\n/** `--key value`, `--key=value`, `--flag` (boolean), positionals. */\nexport function parseArgs(argv: readonly string[]): { positional: string[]; flags: Flags } {\n const flags: Flags = {}\n const positional: string[] = []\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!\n if (!a.startsWith('--')) {\n positional.push(a)\n continue\n }\n const eq = a.indexOf('=')\n if (eq > 0) {\n flags[a.slice(2, eq)] = a.slice(eq + 1)\n continue\n }\n const key = a.slice(2)\n const next = argv[i + 1]\n if (next !== undefined && !next.startsWith('--')) {\n flags[key] = next\n i++\n } else flags[key] = true\n }\n return { positional, flags }\n}\n\nexport const str = (v: string | boolean | undefined): string | undefined =>\n typeof v === 'string' && v.length > 0 ? v : undefined\n\nexport const log = {\n info: (m: string) => console.log(`network-sdk: ${m}`),\n ok: (m: string) => console.log(`network-sdk: ✓ ${m}`),\n warn: (m: string) => console.warn(`network-sdk: warning: ${m}`),\n error: (m: string) => console.error(`network-sdk: error: ${m}`),\n}\n\nexport function projectRoot(flags: Flags): string {\n const root = resolve(str(flags['root']) ?? process.cwd())\n if (!existsSync(join(root, 'package.json')))\n throw new CliError(\n `no package.json in ${root} (run from the site's frontend directory or pass --root <dir>)`,\n )\n return root\n}\n\nexport function readJsonFile<T = unknown>(path: string): T | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as T\n } catch {\n return null\n }\n}\n\nexport function writeFileAtomic(path: string, content: string): void {\n mkdirSync(dirname(path), { recursive: true })\n const tmp = `${path}.tmp-${process.pid}`\n writeFileSync(tmp, content)\n renameSync(tmp, path)\n}\n\n/**\n * `--root-keys` is inline JSON `[{ kid, jwk }]` (the `MANIFEST_ROOT_PUBLIC_KEYS` format). Without it the compiled\n * production `rootPublicKeys` apply; an empty compiled list is a configuration error, not a silent skip.\n */\nexport function resolveRootKeys(flags: Flags): { roots: RootKey[]; explicit: boolean } {\n const raw = str(flags['root-keys'])\n if (!raw) {\n if (rootPublicKeys.length === 0)\n throw new CliError(\n 'no production root key is compiled into this SDK version; pass --root-keys \\'[{\"kid\":…,\"jwk\":{…}}]\\' (the hub\\'s MANIFEST_ROOT_PUBLIC_KEYS value)',\n )\n return { roots: [...rootPublicKeys], explicit: false }\n }\n let json: unknown\n try {\n json = JSON.parse(raw)\n } catch {\n throw new CliError('--root-keys must be a JSON array of { kid, jwk }')\n }\n if (!Array.isArray(json) || json.length === 0 || json.length > 8)\n throw new CliError('--root-keys must be a non-empty JSON array of { kid, jwk }')\n const roots: RootKey[] = []\n for (const e of json as unknown[]) {\n if (!e || typeof e !== 'object')\n throw new CliError('--root-keys: each entry must be { kid, jwk }')\n const { kid, jwk } = e as { kid?: unknown; jwk?: unknown }\n if (typeof kid !== 'string' || !KID_RE.test(kid))\n throw new CliError(`--root-keys: invalid kid ${String(kid)}`)\n let parsed\n try {\n parsed = parsePublicJwk(jwk)\n } catch {\n throw new CliError(`--root-keys: invalid jwk for ${kid}`)\n }\n if (roots.some((r) => r.kid === kid)) throw new CliError(`--root-keys: duplicate kid ${kid}`)\n roots.push({ kid, jwk: parsed })\n }\n return { roots, explicit: true }\n}\n\n/** The literals `sync-fallback` needs from `src/network.config.js` (§4.6: \"the literal siteKey, §6.2\"). */\nexport type ConfigLiterals = {\n path: string\n exists: boolean\n siteKey: string | null\n channel: SiteChannel | null\n cdnBase: string | null\n manifestUrl: string | null\n consentMode: string | null\n /** The committed `manifestRootKeys` literal (one line, as `init --root-keys` writes it); `doctor` only. */\n manifestRootKeys: string | null\n}\n\nexport function readConfigLiterals(root: string): ConfigLiterals {\n const path = join(root, CONFIG_PATH)\n const out: ConfigLiterals = {\n path,\n exists: existsSync(path),\n siteKey: null,\n channel: null,\n cdnBase: null,\n manifestUrl: null,\n consentMode: null,\n manifestRootKeys: null,\n }\n if (!out.exists) return out\n const text = readFileSync(path, 'utf8')\n const lit = (key: string, re: RegExp) => {\n const line = text.match(new RegExp(`\\\\b${key}\\\\s*:([^\\\\n]*)`))\n if (!line) return null\n const m = line[1]!.match(re)\n return m ? m[1]! : null\n }\n out.siteKey = lit('siteKey', /['\"]([a-z0-9]+(?:-[a-z0-9]+)*)['\"]/)\n const ch = lit('channel', /['\"](production|preview)['\"]/)\n out.channel = ch === 'preview' || ch === 'production' ? ch : null\n out.cdnBase = lit('cdnBase', /['\"](https?:\\/\\/[^'\"]+)['\"]/)\n out.manifestUrl = lit('manifestUrl', /['\"](https?:\\/\\/[^'\"]+)['\"]/)\n out.consentMode = lit('consentMode', /['\"]([a-z-]+)['\"]/)\n out.manifestRootKeys = lit('manifestRootKeys', /(\\[.*\\])/)\n return out\n}\n\nexport type Download =\n | { ok: true; text: string; bytes: number; status: number; headers: Headers; url: string }\n | { ok: false; kind: 'network' | 'http' | 'size'; status?: number; detail: string; url: string }\n\nexport async function downloadManifest(\n url: string,\n cache: RequestCache = 'no-cache',\n): Promise<Download> {\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), 10_000)\n try {\n const res = await fetch(url, {\n cache,\n signal: ac.signal,\n headers: { 'Cache-Control': 'no-cache' },\n })\n if (!res.ok)\n return { ok: false, kind: 'http', status: res.status, detail: `HTTP ${res.status}`, url }\n const buf = new Uint8Array(await res.arrayBuffer())\n if (buf.length > MANIFEST_MAX_BYTES)\n return { ok: false, kind: 'size', detail: `${buf.length} bytes > ${MANIFEST_MAX_BYTES}`, url }\n const text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(buf)\n return { ok: true, text, bytes: buf.length, status: res.status, headers: res.headers, url }\n } catch (err) {\n return {\n ok: false,\n kind: 'network',\n detail: err instanceof Error ? err.message : String(err),\n url,\n }\n } finally {\n clearTimeout(timer)\n }\n}\n\nexport type Identity = { site: string; channel: SiteChannel }\n\n/** Downloaded bytes: the full live check (window applies; revocation list from the trusted committed fallback). */\nexport function verifyDownloaded(\n text: string,\n roots: readonly RootKey[],\n expect: Identity,\n revokedKids: readonly string[],\n): Promise<ManifestVerifyResult> {\n return verifyManifestBytes(text, { roots, now: new Date(), expect, source: 'live', revokedKids })\n}\n\n/** The committed fallback: §4.6 identity check (site, channel, root, signature; window skipped). */\nexport function verifyCommitted(\n fb: unknown,\n roots: readonly RootKey[],\n expect: Identity,\n): Promise<ManifestVerifyResult> {\n return verifyManifest(fb, { roots, now: new Date(), expect, source: 'fallback' })\n}\n\nexport function readFallback(root: string): {\n path: string\n json: unknown | null\n exists: boolean\n} {\n const path = join(root, FALLBACK_PATH)\n const exists = existsSync(path)\n return { path, exists, json: exists ? readJsonFile(path) : null }\n}\n\n/** The alias URL for a site: `--url <base>` wins, then (sync-fallback) the production base only. */\nexport function aliasUrl(base: string | undefined, site: string, channel: SiteChannel): string {\n return manifestUrlFor(base ?? DEFAULT_CDN_BASE, site, channel)\n}\n\nexport const NO_MANIFEST_MESSAGE = (siteKey: string) =>\n `site ${siteKey} has no published manifest; Publish in the admin first`\n\n/** Pretty-printed for review diffs; the served (canonical) key order is kept. Verification canonicalises anyway. */\nexport function fallbackFileContent(parsed: unknown): string {\n return `${JSON.stringify(parsed, null, 2)}\\n`\n}\n\nexport type { Manifest }\n\nexport function daysBetween(a: Date, b: Date): number {\n return Math.round((b.getTime() - a.getTime()) / 86_400_000)\n}\n","/**\n * Trusted root public keys — spec §4.5 (`rootPublicKeys`, \"an array to allow overlap\").\n *\n * The root private key is generated offline by the owner (`npm run keys -- root:new`) and never reaches the hub; the\n * public JWK printed by that command is what goes here. The hub holds the same list in `MANIFEST_ROOT_PUBLIC_KEYS`\n * and refuses to certify or sign with a key the SDK would reject.\n *\n * HOW PRODUCTION ROOTS ARE ADDED (release step, see docs/install-guide.md \"Adding a production root\"):\n * 1. `npm run keys -- root:new --kid root-<year> --out <file>` on the owner's machine; store the private file in\n * the password manager (+ printed backup).\n * 2. Append `{ kid: 'root-<year>', jwk: { kty: 'EC', crv: 'P-256', x, y } }` to `rootPublicKeys` below.\n * 3. Release an SDK **minor** (`npm version minor` / changeset); every site picks it up as an ordinary dependency\n * bump. Removing an old root is a **major** (§4.5 \"Root rotation\").\n * 4. Add the same entry to `MANIFEST_ROOT_PUBLIC_KEYS` on the hub, certify the manifest key under the new root.\n *\n * `rootPublicKeys` is EMPTY up to 2.2.0-beta.1: no production root exists yet (nothing is published in production). A\n * site that does not pass `manifestRootKeys` therefore trusts no manifest and renders nothing — the correct result\n * for an environment that has no signed content. The development root below is exported separately and is only ever\n * referenced explicitly (`network-sdk init --root-keys …` writes it into `manifestRootKeys` of the site's\n * `network.config.js`); it must never be merged into `rootPublicKeys`, because its private key lives on a developer\n * machine and the dev bucket (`network-dev/v1`) is not production content.\n */\nexport type RootPublicKey = {\n kid: string\n jwk: { kty: 'EC'; crv: 'P-256'; x: string; y: string }\n}\n\n/** Production roots (none yet; see the file comment). */\nexport const rootPublicKeys: readonly RootPublicKey[] = []\n\n/**\n * The DEVELOPMENT root (hub `.env` `MANIFEST_ROOT_PUBLIC_KEYS`, dev bucket `network-dev/v1`, kid `root-dev-a01e0d`).\n * Public key, not a secret. Used by the test suite and by `init --root-keys` for the JCM pilot; never trusted by\n * default.\n */\nexport const DEV_ROOT_PUBLIC_KEYS: readonly RootPublicKey[] = [\n {\n kid: 'root-dev-a01e0d',\n jwk: {\n kty: 'EC',\n crv: 'P-256',\n x: '4dO5Ybuh3aMQMYvVUYd82CZ2y-tM2wM4SeunZvxMKHQ',\n y: 'Tw6MyboNkggXo9jV7_Tng8tCGNMin7Dxuept8AY0Anc',\n },\n },\n]\n","/**\n * Strict schema-2 manifest validator — a hand-written port of packages/shared `ManifestSchema` (spec §4.3) so the\n * browser bundle carries no zod. Every object is strict (unknown keys reject), every number is an integer, absent\n * optional values are `null`, string limits count Unicode code points (zod 4 `.max()` semantics). Differences from the\n * hub's schema are listed in docs/spec-addendum (Phase 1c):\n * - destination/base URLs: no public-suffix check (the hub uses tldts; 50 KB is not worth it in a homepage bundle);\n * the SDK still requires `https:`, a dotted lowercase hostname, no IP literal, no userinfo, no port (or 443),\n * and the string to be its own normalisation.\n * - dates: `YYYY-MM-DDTHH:mm:ss(.sss)?Z` only (KNOWN-ISSUES 21(b)); the worker emits `toISOString()`.\n */\nimport { MANIFEST_SCHEMA_MAJOR } from '../constants.js'\n\nexport type PublicJwk = { kty: 'EC'; crv: 'P-256'; x: string; y: string }\nexport type ManifestImage = { src: string; src2x: string; alt: string; width: 640; height: 360 }\nexport type Instrumentation = 'none' | 'landings' | 'conversions'\nexport type ManifestBusiness = {\n id: string\n idx: number\n name: string\n description: string\n url: string\n domain: string\n image: ManifestImage\n category: string | null\n location: string | null\n region: string | null\n tags: string[]\n pinned: boolean\n position: number\n instrumentation: Instrumentation\n}\nexport type ManifestSigner = {\n kid: string\n jwk: PublicJwk\n notBefore: string\n notAfter: string\n rootKid: string\n rootSig: string\n revokedKids: string[]\n}\nexport type ManifestSig = { alg: 'ES256'; kid: string; value: string }\nexport type ManifestAnalytics = {\n enabled: boolean\n endpoint: string\n impressions: boolean\n clicks: boolean\n landings: boolean\n samplePermille: number\n token: string\n}\nexport type ManifestLinks = {\n utmMedium: string\n utmCampaign: string\n utmSource: string\n refParam: string\n nofollow: boolean\n}\nexport type Manifest = {\n schema: 2\n revision: number\n publishedAt: string\n site: string\n siteIndex: number\n channel: 'production' | 'preview'\n cdnBase: string\n minSdkVersion: string\n analytics: ManifestAnalytics\n links: ManifestLinks\n section: { title: string; subtitle: string | null }\n businesses: ManifestBusiness[]\n signer: ManifestSigner\n sig: ManifestSig\n}\n\n// Limits (packages/shared directory.ts LISTING_LIMITS, SLUG_MAX, MAX_ITEMS_CEILING)\nexport const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/\nconst SLUG_MAX = 48\nexport const MAX_ITEMS_CEILING = 24\nexport const KID_RE = /^[a-z0-9][a-z0-9-]{1,63}$/\n/** 64 bytes → 86 chars, last one with 4 spare bits zero; 32 bytes → 43 chars, 2 spare bits zero. */\nexport const P1363_RE = /^[A-Za-z0-9_-]{85}[AQgw]$/\nexport const COORD_RE = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/\nexport const ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/\nconst TOKEN_RE = /^[a-z0-9-]+\\.\\d+\\.\\d{8}\\.[0-9a-f]{12}$/\nconst HOST_RE = /^[a-z0-9.-]+$/\n\nclass Invalid extends Error {}\nconst fail = (): never => {\n throw new Invalid()\n}\n\ntype Rec = Record<string, unknown>\nfunction obj(v: unknown, keys: readonly string[]): Rec {\n if (v === null || typeof v !== 'object' || Array.isArray(v)) fail()\n const o = v as Rec\n for (const k of Object.keys(o)) if (!keys.includes(k)) fail()\n for (const k of keys) if (!(k in o)) fail()\n return o\n}\n/**\n * String length in Unicode code points, like zod 4's `.min()` / `.max()` (an emoji counts once, not twice as in\n * `String.prototype.length`); otherwise a hub-signed manifest full of astral characters would fail here.\n */\nfunction codePoints(s: string): number {\n let n = 0\n for (const _ of s) n++\n return n\n}\nfunction str(v: unknown, min: number, max: number): string {\n if (typeof v !== 'string') fail()\n const n = codePoints(v as string)\n if (n < min || n > max) fail()\n return v as string\n}\nfunction re(v: unknown, r: RegExp, max = 1024): string {\n const s = str(v, 0, max)\n if (!r.test(s)) fail()\n return s\n}\nfunction int(v: unknown, min: number, max: number): number {\n if (typeof v !== 'number' || !Number.isInteger(v) || v < min || v > max) fail()\n return v as number\n}\nfunction bool(v: unknown): boolean {\n if (typeof v !== 'boolean') fail()\n return v as boolean\n}\nfunction nullable<T>(v: unknown, f: (x: unknown) => T): T | null {\n return v === null ? null : f(v)\n}\nfunction literal<T extends string | number>(v: unknown, lit: T): T {\n if (v !== lit) fail()\n return lit\n}\nfunction oneOf<T extends string>(v: unknown, list: readonly T[]): T {\n if (typeof v !== 'string' || !list.includes(v as T)) fail()\n return v as T\n}\n\n/** `cdnBase`, `analytics.endpoint`, image URLs: absolute; `http:` only for a local development host. */\nexport function baseUrlProblem(v: string): string | null {\n let u: URL\n try {\n u = new URL(v)\n } catch {\n return 'must be an absolute URL'\n }\n const local = u.hostname === 'localhost' || u.hostname === '127.0.0.1'\n if (u.protocol !== 'https:' && !(u.protocol === 'http:' && local)) return 'must be https'\n if (u.username || u.password || u.search || u.hash) return 'no userinfo, query or fragment'\n return null\n}\nfunction baseUrl(v: unknown): string {\n const s = str(v, 1, 1024)\n if (baseUrlProblem(s) !== null) fail()\n return s\n}\n\n/** §2.2 `url`: absolute `https:`, ≤ 200, public-looking hostname, no IP literal / userinfo / port, normalised. */\nexport function destinationUrlProblem(v: string): string | null {\n if (v.length > 200) return 'must be at most 200 characters'\n let u: URL\n try {\n u = new URL(v)\n } catch {\n return 'must be an absolute URL'\n }\n if (u.protocol !== 'https:') return 'must use https'\n if (u.username || u.password) return 'must not contain credentials'\n if (u.port !== '' && u.port !== '443') return 'port must be 443'\n const host = u.hostname\n if (host.startsWith('[') || /^\\d+(\\.\\d+){3}$/.test(host)) return 'IP addresses are not allowed'\n if (!HOST_RE.test(host) || !host.includes('.') || host.startsWith('.') || host.endsWith('.'))\n return 'invalid hostname'\n if (u.toString() !== v) return 'must be normalised'\n return null\n}\nfunction destinationUrl(v: unknown): string {\n const s = str(v, 1, 200)\n if (destinationUrlProblem(s) !== null) fail()\n return s\n}\n\nexport function parsePublicJwk(v: unknown): PublicJwk {\n const o = obj(v, ['kty', 'crv', 'x', 'y'])\n return {\n kty: literal(o['kty'], 'EC'),\n crv: literal(o['crv'], 'P-256'),\n x: re(o['x'], COORD_RE),\n y: re(o['y'], COORD_RE),\n }\n}\n\nfunction image(v: unknown): ManifestImage {\n const o = obj(v, ['src', 'src2x', 'alt', 'width', 'height'])\n return {\n src: baseUrl(o['src']),\n src2x: baseUrl(o['src2x']),\n alt: str(o['alt'], 1, 120),\n width: literal(o['width'], 640),\n height: literal(o['height'], 360),\n }\n}\n\nconst BUSINESS_KEYS = [\n 'id',\n 'idx',\n 'name',\n 'description',\n 'url',\n 'domain',\n 'image',\n 'category',\n 'location',\n 'region',\n 'tags',\n 'pinned',\n 'position',\n 'instrumentation',\n] as const\n\nfunction business(v: unknown): ManifestBusiness {\n const o = obj(v, BUSINESS_KEYS)\n const name = str(o['name'], 2, 60)\n if (/^[=+@]/.test(name)) fail()\n const domain = re(o['domain'], HOST_RE, 253)\n if (domain.length < 1 || domain.startsWith('www.')) fail()\n const tags = o['tags']\n if (!Array.isArray(tags) || tags.length > 10) fail()\n return {\n id: re(o['id'], SLUG_RE, SLUG_MAX),\n idx: int(o['idx'], 1, 65535),\n name,\n description: str(o['description'], 1, 140),\n url: destinationUrl(o['url']),\n domain,\n image: image(o['image']),\n category: nullable(o['category'], (x) => str(x, 0, 40)),\n location: nullable(o['location'], (x) => str(x, 0, 60)),\n region: nullable(o['region'], (x) => str(x, 0, 40)),\n tags: (tags as unknown[]).map((t) => str(t, 0, 24)),\n pinned: bool(o['pinned']),\n position: int(o['position'], 0, MAX_ITEMS_CEILING - 1),\n instrumentation: oneOf(o['instrumentation'], ['none', 'landings', 'conversions'] as const),\n }\n}\n\nfunction signer(v: unknown): ManifestSigner {\n const o = obj(v, ['kid', 'jwk', 'notBefore', 'notAfter', 'rootKid', 'rootSig', 'revokedKids'])\n const revoked = o['revokedKids']\n if (!Array.isArray(revoked)) fail()\n return {\n kid: re(o['kid'], KID_RE),\n jwk: parsePublicJwk(o['jwk']),\n notBefore: re(o['notBefore'], ISO_DATE_RE),\n notAfter: re(o['notAfter'], ISO_DATE_RE),\n rootKid: re(o['rootKid'], KID_RE),\n rootSig: re(o['rootSig'], P1363_RE),\n revokedKids: (revoked as unknown[]).map((k) => re(k, KID_RE)),\n }\n}\n\nconst MANIFEST_KEYS = [\n 'schema',\n 'revision',\n 'publishedAt',\n 'site',\n 'siteIndex',\n 'channel',\n 'cdnBase',\n 'minSdkVersion',\n 'analytics',\n 'links',\n 'section',\n 'businesses',\n 'signer',\n 'sig',\n] as const\n\n/** Returns the typed manifest, or `null` when the input is not a valid schema-2 manifest. Never throws. */\nexport function parseManifest(input: unknown): Manifest | null {\n try {\n const o = obj(input, MANIFEST_KEYS)\n const a = obj(o['analytics'], [\n 'enabled',\n 'endpoint',\n 'impressions',\n 'clicks',\n 'landings',\n 'samplePermille',\n 'token',\n ])\n const l = obj(o['links'], ['utmMedium', 'utmCampaign', 'utmSource', 'refParam', 'nofollow'])\n const sec = obj(o['section'], ['title', 'subtitle'])\n const sig = obj(o['sig'], ['alg', 'kid', 'value'])\n const list = o['businesses']\n if (!Array.isArray(list) || list.length > MAX_ITEMS_CEILING) fail()\n return {\n schema: literal(o['schema'], MANIFEST_SCHEMA_MAJOR as 2),\n revision: int(o['revision'], 1, Number.MAX_SAFE_INTEGER),\n publishedAt: re(o['publishedAt'], ISO_DATE_RE),\n site: re(o['site'], SLUG_RE, SLUG_MAX),\n siteIndex: int(o['siteIndex'], 1, 65535),\n channel: oneOf(o['channel'], ['production', 'preview'] as const),\n cdnBase: baseUrl(o['cdnBase']),\n minSdkVersion: re(o['minSdkVersion'], /^\\d+\\.\\d+\\.\\d+$/),\n analytics: {\n enabled: bool(a['enabled']),\n endpoint: baseUrl(a['endpoint']),\n impressions: bool(a['impressions']),\n clicks: bool(a['clicks']),\n landings: bool(a['landings']),\n samplePermille: int(a['samplePermille'], 0, 1000),\n token: re(a['token'], TOKEN_RE),\n },\n links: {\n utmMedium: str(l['utmMedium'], 1, 40),\n utmCampaign: str(l['utmCampaign'], 1, 40),\n utmSource: re(l['utmSource'], SLUG_RE, SLUG_MAX),\n refParam: re(l['refParam'], /^[a-z]{1,8}$/),\n nofollow: bool(l['nofollow']),\n },\n section: {\n title: str(sec['title'], 1, 60),\n subtitle: nullable(sec['subtitle'], (x) => str(x, 0, 120)),\n },\n businesses: (list as unknown[]).map(business),\n signer: signer(o['signer']),\n sig: {\n alg: literal(sig['alg'], 'ES256'),\n kid: re(sig['kid'], KID_RE),\n value: re(sig['value'], P1363_RE),\n },\n }\n } catch {\n return null\n }\n}\n","/** Browser-safe copy of packages/shared canonicalJson (spec §4.5); both run the same vectors in CI. */\nexport class CanonicalJsonError extends Error {\n constructor(\n message: string,\n public readonly path: string,\n ) {\n super(`${message} at ${path || '$'}`)\n this.name = 'CanonicalJsonError'\n }\n}\nexport function canonicalJson(value: unknown): string {\n return serialise(value, '$')\n}\nfunction serialise(v: unknown, path: string): string {\n if (v === null) return 'null'\n switch (typeof v) {\n case 'boolean':\n return v ? 'true' : 'false'\n case 'string':\n return JSON.stringify(v)\n case 'number':\n if (!Number.isSafeInteger(v))\n throw new CanonicalJsonError(`non-integer or unsafe number ${String(v)}`, path)\n return String(v)\n case 'object': {\n if (Array.isArray(v)) return `[${v.map((x, i) => serialise(x, `${path}[${i}]`)).join(',')}]`\n const proto = Object.getPrototypeOf(v)\n if (proto !== Object.prototype && proto !== null)\n throw new CanonicalJsonError('unsupported object type', path)\n const obj = v as Record<string, unknown>\n return `{${Object.keys(obj)\n .sort()\n .map((k) => {\n const child = obj[k]\n if (child === undefined)\n throw new CanonicalJsonError(`undefined value for key \"${k}\" (use null)`, path)\n return `${JSON.stringify(k)}:${serialise(child, `${path}.${k}`)}`\n })\n .join(',')}}`\n }\n default:\n throw new CanonicalJsonError(`unsupported type ${typeof v}`, path)\n }\n}\n","/**\n * Strict base64url without padding (browser-safe, no Buffer) — spec §4.5 \"Signature encoding\", KNOWN-ISSUES 21(c).\n * A string is accepted only when it is THE canonical encoding of exactly `bytes` bytes: right length, alphabet only,\n * and zero spare low bits in the last character, so two strings can never decode to the same value.\n */\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'\nconst LOOKUP: Record<string, number> = {}\nfor (let i = 0; i < 64; i++) LOOKUP[ALPHABET[i]!] = i\n\nexport function isBase64urlAlphabet(s: string): boolean {\n return /^[A-Za-z0-9_-]*$/.test(s)\n}\n\n/** Decodes a canonical base64url string of exactly `bytes` bytes; `null` for anything else. */\nexport function decodeCanonicalBase64url(\n s: unknown,\n bytes: number,\n): Uint8Array<ArrayBuffer> | null {\n if (typeof s !== 'string' || !isBase64urlAlphabet(s)) return null\n if (s.length !== Math.ceil((bytes * 4) / 3)) return null\n const out = new Uint8Array(bytes)\n let acc = 0\n let bits = 0\n let o = 0\n for (let i = 0; i < s.length; i++) {\n acc = ((acc << 6) | LOOKUP[s[i]!]!) & 0xffff\n bits += 6\n if (bits >= 8) {\n bits -= 8\n if (o >= bytes) return null\n out[o++] = (acc >> bits) & 0xff\n }\n }\n if (o !== bytes) return null\n // spare bits (the low `bits` bits of `acc`) must be zero\n if (bits > 0 && (acc & ((1 << bits) - 1)) !== 0) return null\n return out\n}\n\nexport function encodeBase64url(bytes: Uint8Array): string {\n let s = ''\n let acc = 0\n let bits = 0\n for (const b of bytes) {\n acc = ((acc << 8) | b) & 0xffff\n bits += 8\n while (bits >= 6) {\n bits -= 6\n s += ALPHABET[(acc >> bits) & 63]\n }\n }\n if (bits > 0) s += ALPHABET[(acc << (6 - bits)) & 63]\n return s\n}\n\nexport function bytesToHex(b: Uint8Array): string {\n let h = ''\n for (const x of b) h += x.toString(16).padStart(2, '0')\n return h\n}\n","/**\n * ECDSA P-256 / SHA-256 with IEEE P1363 (`r ‖ s`, 64 bytes) signatures through WebCrypto — spec §4.5, KNOWN-ISSUES\n * 21(c). WebCrypto accepts the high-S twin of every signature and does not care how the bytes were encoded, so the\n * strict checks of packages/shared `verifyP1363` are re-implemented here BEFORE `crypto.subtle.verify` runs:\n * canonical base64url of exactly 64 bytes, `1 ≤ r < n`, `1 ≤ s ≤ n/2` (low-S). Never throws.\n */\nimport { bytesToHex, decodeCanonicalBase64url } from './base64url.js'\n\n/** The order n of the P-256 group. */\nexport const P256_N = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551')\nconst HALF_N = P256_N >> 1n\n\nexport type PublicJwk = { kty: 'EC'; crv: 'P-256'; x: string; y: string }\n\n/**\n * `crypto.subtle` when the context has it (secure contexts; Node ≥ 19, or Node 18 once the CLI entry polyfills it from\n * `node:crypto`); `null` on `http://<lan-ip>`.\n */\nexport function subtle(): SubtleCrypto | null {\n const c = (globalThis as { crypto?: Crypto }).crypto\n return c && c.subtle ? c.subtle : null\n}\n\n/** The structural checks alone (no key, no hashing): used by the verifier and by the tests. */\nexport function p1363Problem(signature: unknown): 'encoding' | 'range' | null {\n const sig = decodeCanonicalBase64url(signature, 64)\n if (!sig) return 'encoding'\n const r = BigInt(`0x${bytesToHex(sig.subarray(0, 32))}`)\n const s = BigInt(`0x${bytesToHex(sig.subarray(32))}`)\n if (r === 0n || r >= P256_N || s === 0n || s > HALF_N) return 'range'\n return null\n}\n\nconst keyCache = new WeakMap<object, Promise<CryptoKey>>()\n\nasync function importKey(jwk: PublicJwk, api: SubtleCrypto): Promise<CryptoKey> {\n const hit = keyCache.get(jwk)\n if (hit) return hit\n const p = api.importKey(\n 'jwk',\n { kty: 'EC', crv: 'P-256', x: jwk.x, y: jwk.y, ext: true },\n { name: 'ECDSA', namedCurve: 'P-256' },\n false,\n ['verify'],\n )\n keyCache.set(jwk, p)\n return p\n}\n\n/**\n * Verify `signature` (base64url P1363) over the UTF-8 bytes of `data` with a P-256 public JWK. Returns `false` on any\n * malformed input, on a high-S twin, on a non-canonical encoding and when WebCrypto is unavailable.\n */\nexport async function verifyP1363(\n jwk: PublicJwk,\n data: string,\n signature: unknown,\n): Promise<boolean> {\n try {\n const api = subtle()\n if (!api) return false\n if (p1363Problem(signature) !== null) return false\n const sig = decodeCanonicalBase64url(signature, 64)!\n const key = await importKey(jwk, api)\n return await api.verify(\n { name: 'ECDSA', hash: 'SHA-256' },\n key,\n sig,\n new TextEncoder().encode(data),\n )\n } catch {\n return false\n }\n}\n","/**\n * The SDK manifest verifier — spec §4.5 (steps 1–4 and the `source: 'fallback'` exception), §4.6 (identity),\n * KNOWN-ISSUES 21(c). Mirrors packages/shared `verifyManifest` / `verifyManifestBytes` (the reference, Node) check for\n * check so both sides agree on accept/reject for the shared vectors; WebCrypto replaces node:crypto, and the strict\n * low-S / canonical-encoding / canonical-bytes checks are done here because WebCrypto does not do them. Never throws.\n */\nimport { canonicalJson } from '../canonicalJson.js'\nimport { MANIFEST_MAX_BYTES, SIGNER_GRACE_MS } from '../constants.js'\nimport { verifyP1363, type PublicJwk } from './p1363.js'\nimport { parseManifest, type Manifest, type ManifestSigner } from './schema.js'\n\nexport type RootKey = { kid: string; jwk: PublicJwk }\nexport type ManifestVerifyReason =\n | 'malformed'\n | 'identity'\n | 'root_unknown'\n | 'root_sig'\n | 'window'\n | 'kid_mismatch'\n | 'sig'\n | 'revoked'\nexport type ManifestVerifyResult =\n { ok: true; manifest: Manifest } | { ok: false; reason: ManifestVerifyReason }\n\nexport type VerifyManifestOptions = {\n /** Trusted root public keys (`rootPublicKeys` or `manifestRootKeys`); empty → `root_unknown`. */\n roots: readonly RootKey[]\n /** Must be a finite date. */\n now: Date\n /** `site`, `channel` and `links.utmSource` must match. */\n expect: { site: string; channel: 'production' | 'preview' }\n /** `live`: window applies and the trusted `revokedKids` is required. `fallback`: only the window is skipped. */\n source: 'live' | 'fallback'\n revokedKids?: readonly string[]\n}\n\n/** §4.5: `rootSig` covers `canonical({ kid, jwk, notBefore, notAfter, rootKid })`. */\nexport function certificatePayload(s: ManifestSigner): string {\n return canonicalJson({\n kid: s.kid,\n jwk: { kty: s.jwk.kty, crv: s.jwk.crv, x: s.jwk.x, y: s.jwk.y },\n notBefore: s.notBefore,\n notAfter: s.notAfter,\n rootKid: s.rootKid,\n })\n}\n\n/** The manifest minus `sig`: the bytes `sig.value` covers. */\nexport function signedPayload(m: Manifest): string {\n const { sig: _sig, ...payload } = m\n return canonicalJson(payload)\n}\n\nconst isKidList = (v: unknown): v is readonly string[] =>\n Array.isArray(v) && v.every((k) => typeof k === 'string')\n\n/** Date.parse of the strict `…Z` form only (the schema already enforces the shape); NaN otherwise. */\nfunction parseIso(s: string): number {\n return Date.parse(s)\n}\n\nexport async function verifyManifest(\n input: unknown,\n opts: VerifyManifestOptions,\n): Promise<ManifestVerifyResult> {\n try {\n if (!opts || typeof opts !== 'object') return { ok: false, reason: 'malformed' }\n const t = opts.now instanceof Date ? opts.now.getTime() : NaN\n if (!Number.isFinite(t)) return { ok: false, reason: 'malformed' }\n if (opts.source !== 'live' && opts.source !== 'fallback')\n return { ok: false, reason: 'malformed' }\n if (\n !opts.expect ||\n typeof opts.expect.site !== 'string' ||\n typeof opts.expect.channel !== 'string'\n )\n return { ok: false, reason: 'malformed' }\n if (opts.source === 'live' && !isKidList(opts.revokedKids))\n return { ok: false, reason: 'malformed' }\n if (opts.revokedKids !== undefined && !isKidList(opts.revokedKids))\n return { ok: false, reason: 'malformed' }\n const m = parseManifest(input)\n if (!m) return { ok: false, reason: 'malformed' }\n // (0) identity — §4.6: the manifest must be the one this site asked for\n if (\n m.site !== opts.expect.site ||\n m.channel !== opts.expect.channel ||\n m.links.utmSource !== opts.expect.site\n )\n return { ok: false, reason: 'identity' }\n const s = m.signer\n // (1) root certificate\n const roots = Array.isArray(opts.roots) ? opts.roots : []\n const root = roots.find((r) => r && r.kid === s.rootKid)\n if (!root) return { ok: false, reason: 'root_unknown' }\n if (!(await verifyP1363(root.jwk, certificatePayload(s), s.rootSig)))\n return { ok: false, reason: 'root_sig' }\n // (2) time window — live only (§4.5 fallback exception)\n const notBefore = parseIso(s.notBefore)\n const notAfter = parseIso(s.notAfter)\n if (!Number.isFinite(notBefore) || !Number.isFinite(notAfter) || notAfter < notBefore)\n return { ok: false, reason: 'malformed' }\n if (opts.source === 'live' && (t < notBefore || t > notAfter + SIGNER_GRACE_MS))\n return { ok: false, reason: 'window' }\n // (3) manifest signature\n if (m.sig.kid !== s.kid) return { ok: false, reason: 'kid_mismatch' }\n if (!(await verifyP1363(s.jwk, signedPayload(m), m.sig.value)))\n return { ok: false, reason: 'sig' }\n // (4) revocation: the trusted list (the bundled fallback's) and the signer's own\n if ((opts.revokedKids ?? []).includes(m.sig.kid) || s.revokedKids.includes(m.sig.kid))\n return { ok: false, reason: 'revoked' }\n return { ok: true, manifest: m }\n } catch {\n return { ok: false, reason: 'malformed' }\n }\n}\n\n/**\n * The bytes entry point for a fetched manifest: refuses input over 24 KB before decoding or parsing, decodes fatal\n * UTF-8 **keeping** a leading BOM (which then fails to parse), requires `canonicalJson(JSON.parse(raw)) === raw`\n * (no whitespace, sorted keys, no duplicate keys, no alternative escapes), then `verifyManifest`.\n */\nexport async function verifyManifestBytes(\n raw: string | Uint8Array,\n opts: VerifyManifestOptions,\n): Promise<ManifestVerifyResult> {\n try {\n let text: string\n if (typeof raw === 'string') {\n if (\n raw.length > MANIFEST_MAX_BYTES ||\n new TextEncoder().encode(raw).length > MANIFEST_MAX_BYTES\n )\n return { ok: false, reason: 'malformed' }\n text = raw\n } else {\n if (!(raw instanceof Uint8Array) || raw.length > MANIFEST_MAX_BYTES)\n return { ok: false, reason: 'malformed' }\n text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(raw)\n }\n const parsed: unknown = JSON.parse(text)\n if (canonicalJson(parsed) !== text) return { ok: false, reason: 'malformed' }\n return await verifyManifest(parsed, opts)\n } catch {\n return { ok: false, reason: 'malformed' }\n }\n}\n","/**\n * `network-sdk init --site-key <key> [--url <cdn-base>] [--root-keys <json>] [--channel production|preview]\n * [--root <dir>] [--force]` — spec §6.6: writes `src/network.config.js`, downloads AND verifies\n * `src/data/network.fallback.json` (exit 1 with the §4.6 message when no manifest exists and no committed fallback\n * passes the identity check), adds `\"network:sync\"` + `\"prebuild\"` to package.json and inlines `sync-fallback` into\n * an existing `build` script (App Platform skips `prebuild` when the Build Command is overridden), then prints the\n * lines to paste. Non-interactive: the theme \"prompt\" of §6.6 is a detection (Tailwind config, custom colours,\n * `lucide-react`, a `getConsent` export) with sensible defaults, so it runs unattended in CI (spec addendum).\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { CONFIG_PATH, FALLBACK_PATH, SDK_VERSION, type SiteChannel } from '../constants.js'\nimport { SLUG_RE } from '../verify/schema.js'\nimport {\n aliasUrl,\n CliError,\n downloadManifest,\n fallbackFileContent,\n log,\n NO_MANIFEST_MESSAGE,\n projectRoot,\n readFallback,\n resolveRootKeys,\n str,\n verifyCommitted,\n verifyDownloaded,\n writeFileAtomic,\n type Flags,\n} from './shared.js'\n\ntype Detected = {\n tailwind: boolean\n colours: Record<string, string>\n lucide: boolean\n consentImport: string | null\n}\n\nexport function detectSite(root: string): Detected {\n const pkg = (() => {\n try {\n return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as Record<string, unknown>\n } catch {\n return {}\n }\n })()\n const deps = {\n ...((pkg['dependencies'] as Record<string, string>) ?? {}),\n ...((pkg['devDependencies'] as Record<string, string>) ?? {}),\n }\n const twFile = [\n 'tailwind.config.js',\n 'tailwind.config.cjs',\n 'tailwind.config.mjs',\n 'tailwind.config.ts',\n ]\n .map((f) => join(root, f))\n .find((p) => existsSync(p))\n const colours: Record<string, string> = {}\n if (twFile) {\n const text = readFileSync(twFile, 'utf8')\n for (const m of text.matchAll(/\\b([A-Za-z][\\w-]*)\\s*:\\s*['\"](#[0-9a-fA-F]{3,8})['\"]/g))\n colours[m[1]!] = m[2]!\n }\n const tracker = join(root, 'src/api/tracker.js')\n const consentImport =\n existsSync(tracker) && /export\\s+function\\s+getConsent\\b/.test(readFileSync(tracker, 'utf8'))\n ? `import { getConsent } from './api/tracker'`\n : null\n return {\n tailwind: !!twFile || 'tailwindcss' in deps,\n colours,\n lucide: 'lucide-react' in deps,\n consentImport,\n }\n}\n\n/** §6.2 example theme, adapted to the colours the site's Tailwind config defines. */\nexport function tailwindTheme(colours: Record<string, string>): Record<string, string> {\n const gold = 'gold' in colours ? 'gold' : '[#ffcb50]'\n const white = 'mycustomwhite' in colours ? 'mycustomwhite' : 'white'\n return {\n section: 'py-16 px-4 bg-black',\n container: 'max-w-6xl mx-auto',\n heading: `text-${white} text-3xl font-bold text-center`,\n accentWord: `text-${gold}`,\n subheading: 'text-gray-300 text-center mt-2',\n grid: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mt-10',\n card: 'bg-white rounded-2xl shadow-md hover:shadow-xl transition-all duration-300 overflow-hidden flex flex-col',\n imageBox: 'aspect-video bg-gray-100 overflow-hidden',\n image: 'w-full h-full object-cover',\n placeholder:\n 'w-full h-full flex items-center justify-center text-5xl font-bold text-gray-400 bg-gray-200',\n body: 'flex flex-col flex-grow gap-2 p-5',\n name: 'text-xl font-semibold text-black',\n meta: 'text-sm text-gray-500',\n description: 'text-gray-700 line-clamp-3 flex-grow',\n button: `self-start mt-2 inline-flex items-center gap-2 bg-${gold} text-black font-semibold rounded-full px-5 py-2 hover:bg-black hover:text-${gold} transition-all`,\n buttonIcon: 'w-4 h-4',\n }\n}\n\nexport function renderConfig(input: {\n siteKey: string\n channel: SiteChannel\n cdnBase: string | undefined\n rootKeysJson: string | undefined\n detected: Detected\n}): string {\n const { siteKey, channel, cdnBase, rootKeysJson, detected } = input\n const theme = detected.tailwind ? tailwindTheme(detected.colours) : null\n const lines: string[] = [\n `// Written by \\`network-sdk init\\` (@intenque/network-sdk ${SDK_VERSION}) — the only per-site file (spec §6.2).`,\n `// Everything else (title, items, UTM values, analytics on/off, images) comes from the signed manifest.`,\n `import { defineNetworkConfig } from '@intenque/network-sdk/react'`,\n `import fallback from './data/network.fallback.json'`,\n ]\n if (detected.consentImport)\n lines.push(`${detected.consentImport} // consent adapter: get() => 'granted' | 'denied' | null`)\n lines.push(\n ``,\n `export default defineNetworkConfig({`,\n ` siteKey: import.meta.env.VITE_INR_SITE_KEY || '${siteKey}', // the literal is what sync-fallback reads (§4.6)`,\n ` channel: import.meta.env.VITE_INR_CHANNEL || '${channel}',`,\n )\n if (cdnBase)\n lines.push(\n ` // Development CDN base written by \\`init --url\\`; remove when the production CDN is live (DEFAULT_CDN_BASE).`,\n ` cdnBase: '${cdnBase}',`,\n )\n lines.push(\n ` manifestUrl: import.meta.env.VITE_INR_MANIFEST_URL, // separate staging hub only (with manifestRootKeys)`,\n )\n if (rootKeysJson)\n lines.push(\n ` // Root public keys written by \\`init --root-keys\\` (dev root). Remove when the SDK ships the production root.`,\n ` manifestRootKeys: ${rootKeysJson},`,\n )\n lines.push(\n ` fallback, // written by \\`network-sdk sync-fallback\\` in prebuild; also the rollback floor`,\n detected.consentImport\n ? ` consent: { get: getConsent },`\n : ` consent: undefined, // TODO: { get: () => 'granted' | 'denied' | null }`,\n ` consentMode: 'session-default', // 'require-consent' for sites with EU/UK visitors (§5.9)`,\n ` dev: import.meta.env.DEV,`,\n )\n if (theme) {\n lines.push(\n ` unstyled: true, // Tailwind classes below; drop this line and import '@intenque/network-sdk/styles.css' instead`,\n )\n lines.push(` theme: {`)\n for (const [k, v] of Object.entries(theme)) lines.push(` ${k}: '${v}',`)\n lines.push(` },`)\n } else {\n lines.push(\n ` // No Tailwind detected: import '@intenque/network-sdk/styles.css' in main.jsx and tune the --inr-* variables.`,\n )\n }\n lines.push(`})`, ``)\n return lines.join('\\n')\n}\n\ntype Scripts = Record<string, string>\n\nexport function patchScripts(\n scripts: Scripts,\n syncCommand: string,\n): { scripts: Scripts; changes: string[] } {\n const out = { ...scripts }\n const changes: string[] = []\n if (out['network:sync'] !== syncCommand) {\n out['network:sync'] = syncCommand\n changes.push('\"network:sync\"')\n }\n if (!out['prebuild']) {\n out['prebuild'] = 'npm run network:sync'\n changes.push('\"prebuild\"')\n } else if (\n !out['prebuild'].includes('network:sync') &&\n !out['prebuild'].includes('sync-fallback')\n ) {\n out['prebuild'] = `npm run network:sync && ${out['prebuild']}`\n changes.push('\"prebuild\" (prepended)')\n }\n const build = out['build']\n if (build && !build.includes('sync-fallback') && !build.includes('network:sync')) {\n out['build'] = `${syncCommand} && ${build}`\n changes.push('\"build\" (sync-fallback inlined)')\n }\n return { scripts: out, changes }\n}\n\nexport async function runInit(flags: Flags): Promise<number> {\n const siteKey = str(flags['site-key'])\n if (!siteKey || !SLUG_RE.test(siteKey) || siteKey.length > 48)\n throw new CliError(\n 'usage: network-sdk init --site-key <site-key> [--url <cdn-base>] [--root-keys <json>] [--channel production|preview]',\n )\n const root = projectRoot(flags)\n const channel: SiteChannel = flags['channel'] === 'preview' ? 'preview' : 'production'\n const base = str(flags['url'])\n const { roots, explicit } = resolveRootKeys(flags)\n const expect = { site: siteKey, channel }\n const url = aliasUrl(base, siteKey, channel)\n const detected = detectSite(root)\n\n // 1. fallback: download + verify\n const committed = readFallback(root)\n const committedOk =\n committed.exists && committed.json !== null\n ? (await verifyCommitted(committed.json, roots, expect)).ok\n : false\n log.info(`downloading ${url}`)\n const dl = await downloadManifest(url)\n if (!dl.ok) {\n if (committedOk)\n log.warn(`download failed (${dl.detail}); keeping the committed ${FALLBACK_PATH}`)\n else throw new CliError(`${NO_MANIFEST_MESSAGE(siteKey)} (${dl.detail} at ${url})`)\n } else {\n const trustedRevoked =\n committedOk && committed.json\n ? ((committed.json as { signer: { revokedKids: string[] } }).signer.revokedKids ?? [])\n : []\n const v = await verifyDownloaded(dl.text, roots, expect, trustedRevoked)\n if (!v.ok)\n throw new CliError(\n `downloaded manifest failed verification (${v.reason}) at ${url}; not written`,\n )\n writeFileAtomic(committed.path, fallbackFileContent(JSON.parse(dl.text)))\n log.ok(\n `wrote ${FALLBACK_PATH} (revision ${v.manifest.revision}, ${v.manifest.businesses.length} items, ${dl.bytes} bytes, signer ${v.manifest.sig.kid})`,\n )\n }\n\n // 2. network.config.js\n const configPath = join(root, CONFIG_PATH)\n if (existsSync(configPath) && flags['force'] !== true)\n log.warn(`${CONFIG_PATH} exists; left unchanged (use --force to rewrite)`)\n else {\n writeFileAtomic(\n configPath,\n renderConfig({\n siteKey,\n channel,\n cdnBase: base,\n rootKeysJson: explicit ? JSON.stringify(roots) : undefined,\n detected,\n }),\n )\n log.ok(\n `wrote ${CONFIG_PATH} (${detected.tailwind ? 'Tailwind theme slots' : 'styles.css'}${detected.consentImport ? ', consent from src/api/tracker.js' : ''}${detected.lucide ? ', lucide-react available' : ''})`,\n )\n }\n\n // 3. package.json scripts\n const pkgPath = join(root, 'package.json')\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as Record<string, unknown>\n const syncFlags = [\n base ? `--url ${base}` : '',\n explicit ? `--root-keys '${JSON.stringify(roots)}'` : '',\n channel === 'preview' ? '--channel preview' : '',\n ]\n .filter(Boolean)\n .join(' ')\n const syncCommand = `network-sdk sync-fallback${syncFlags ? ` ${syncFlags}` : ''}`\n const { scripts, changes } = patchScripts((pkg['scripts'] as Scripts) ?? {}, syncCommand)\n if (changes.length > 0) {\n pkg['scripts'] = scripts\n writeFileAtomic(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`)\n log.ok(`package.json scripts updated: ${changes.join(', ')}`)\n } else log.ok('package.json scripts already up to date')\n if (base || explicit)\n log.warn(\n 'dev flags (--url / --root-keys) are inlined into the sync scripts; re-run init without them once the production CDN and root are live',\n )\n\n // 4. the lines to paste\n console.log(`\nNext steps (spec §6.7):\n main.jsx (before ReactDOM.createRoot, before any tracker or GA snippet):\n import { captureReferral } from '@intenque/network-sdk/capture'\n import { NetworkProvider } from '@intenque/network-sdk/react'\n import networkConfig from './network.config'\n captureReferral(networkConfig.capture)\n …render(<NetworkProvider config={networkConfig}><App /></NetworkProvider>)\n Home page (after <ReviewCarousel />):\n import { DiscoverBusinesses } from '@intenque/network-sdk/react'\n <DiscoverBusinesses />\n ConsentBanner (next to grantConsent() / denyConsent()):\n import { setConsent } from '@intenque/network-sdk/capture' → setConsent('granted') / setConsent('denied')\n API client (api/axios.js):\n import { attachReferral } from '@intenque/network-sdk/capture' → attachReferral(api)\n App Platform: the frontend component's Build Command must be \\`npm run build\\` (so sync-fallback runs on every deploy).\n Then: npx network-sdk doctor\n`)\n return 0\n}\n","/** `network-sdk print-config [--root <dir>]` — spec §6.6: the effective config literals without secrets. */\nimport { relative } from 'node:path'\nimport { DEFAULT_CDN_BASE, SDK_VERSION } from '../constants.js'\nimport {\n aliasUrl,\n log,\n projectRoot,\n readConfigLiterals,\n readFallback,\n type Flags,\n} from './shared.js'\n\nexport function runPrintConfig(flags: Flags): number {\n const root = projectRoot(flags)\n const cfg = readConfigLiterals(root)\n const fb = readFallback(root)\n const j = fb.json as Record<string, unknown> | null\n const channel = cfg.channel ?? 'production'\n const out = {\n sdkVersion: SDK_VERSION,\n configFile: cfg.exists ? relative(root, cfg.path) : null,\n siteKey: cfg.siteKey,\n channel,\n cdnBase:\n cfg.cdnBase ?? (j && typeof j['cdnBase'] === 'string' ? j['cdnBase'] : DEFAULT_CDN_BASE),\n manifestUrl:\n cfg.manifestUrl ??\n (cfg.siteKey\n ? aliasUrl(cfg.cdnBase ?? (j?.['cdnBase'] as string | undefined), cfg.siteKey, channel)\n : null),\n consentMode: cfg.consentMode ?? 'session-default',\n fallback: fb.exists\n ? {\n file: relative(root, fb.path),\n revision: j?.['revision'] ?? null,\n publishedAt: j?.['publishedAt'] ?? null,\n site: j?.['site'] ?? null,\n channel: j?.['channel'] ?? null,\n items: Array.isArray(j?.['businesses']) ? j!['businesses'].length : null,\n signer: (j?.['sig'] as { kid?: string } | undefined)?.kid ?? null,\n analyticsEnabled:\n (j?.['analytics'] as { enabled?: boolean } | undefined)?.enabled ?? null,\n }\n : null,\n }\n if (!cfg.exists) log.warn('src/network.config.js not found')\n console.log(JSON.stringify(out, null, 2))\n return 0\n}\n","/**\n * `network-sdk sync-fallback [--url <base>] [--root-keys <json>] [--channel …] [--allow-empty] [--root <dir>]` —\n * spec §4.6 / §6.6. Runs in `prebuild` (and inlined in `build`) on every site deploy:\n * - site key = the literal `siteKey` in `src/network.config.js`; channel = `--channel`, else the committed\n * fallback's `channel`, else production first then `/preview/`;\n * - downloads from the PRODUCTION CDN only (`VITE_INR_MANIFEST_URL` / `manifestRootKeys` are ignored unless\n * `--url` / `--root-keys` are explicit), verifies the signature;\n * - refuses a lower `revision` than a committed file that passes the identity check (and overwrites one that does\n * not, with a warning);\n * - NETWORK failure: keeps the committed file, warns, exit 0. CONFIGURATION failure (no resolvable siteKey, no\n * committed fallback and nothing published, `fallback.site ≠ siteKey` after the write): exit 1.\n * - `--allow-empty` is the only way to write an empty manifest (an explicit placeholder the SDK renders as nothing),\n * and only when no trusted committed file exists: it never replaces signed content (warn, exit 0).\n */\nimport { FALLBACK_PATH, type SiteChannel } from '../constants.js'\nimport type { Manifest } from '../verify/schema.js'\nimport {\n aliasUrl,\n CliError,\n downloadManifest,\n fallbackFileContent,\n log,\n NO_MANIFEST_MESSAGE,\n projectRoot,\n readConfigLiterals,\n readFallback,\n readJsonFile,\n resolveRootKeys,\n str,\n verifyCommitted,\n verifyDownloaded,\n writeFileAtomic,\n type Download,\n type Flags,\n} from './shared.js'\n\nexport function emptyPlaceholder(site: string, channel: SiteChannel): Record<string, unknown> {\n return {\n schema: 2,\n revision: 0,\n site,\n channel,\n businesses: [],\n placeholder: 'written by network-sdk sync-fallback --allow-empty',\n }\n}\n\nexport async function runSyncFallback(flags: Flags): Promise<number> {\n const root = projectRoot(flags)\n const cfg = readConfigLiterals(root)\n if (!cfg.exists)\n throw new CliError(`${cfg.path} not found; run \\`network-sdk init --site-key <key>\\` first`)\n const siteKey = str(flags['site-key']) ?? cfg.siteKey\n if (!siteKey)\n throw new CliError(\n `cannot resolve the site key: ${cfg.path} has no literal siteKey (e.g. siteKey: 'jcm-detailing')`,\n )\n const { roots } = resolveRootKeys(flags)\n const base = str(flags['url'])\n if (!base && (process.env['VITE_INR_MANIFEST_URL'] || process.env['INR_MANIFEST_URL']))\n log.info(\n 'VITE_INR_MANIFEST_URL is set but ignored: sync-fallback reads the production CDN unless --url is explicit (§4.6)',\n )\n\n const committed = readFallback(root)\n const committedJson = committed.json as (Manifest & Record<string, unknown>) | null\n const committedChannel: SiteChannel | null =\n committedJson &&\n (committedJson['channel'] === 'preview' || committedJson['channel'] === 'production')\n ? (committedJson['channel'] as SiteChannel)\n : null\n const explicitChannel: SiteChannel | null =\n flags['channel'] === 'preview'\n ? 'preview'\n : flags['channel'] === 'production'\n ? 'production'\n : null\n const channels: SiteChannel[] = explicitChannel\n ? [explicitChannel]\n : committedChannel\n ? [committedChannel]\n : ['production', 'preview']\n\n // identity check of the committed file (§4.6): only a trusted file is a floor\n let trusted: Manifest | null = null\n if (committed.exists) {\n if (committedJson === null)\n log.warn(`${FALLBACK_PATH} is not valid JSON; it will be overwritten`)\n else {\n const r = await verifyCommitted(committedJson, roots, {\n site: siteKey,\n channel: channels[0]!,\n })\n if (r.ok) trusted = r.manifest\n else if (isPlaceholder(committedJson))\n log.info(`${FALLBACK_PATH} is the --allow-empty placeholder`)\n else\n log.warn(\n `${FALLBACK_PATH} fails the identity check (${r.reason}: site ${String(committedJson['site'])}, channel ${String(committedJson['channel'])}); it will be overwritten`,\n )\n }\n }\n\n // download: first channel that answers\n let dl: Download | null = null\n let channel: SiteChannel = channels[0]!\n for (const ch of channels) {\n channel = ch\n dl = await downloadManifest(aliasUrl(base, siteKey, ch))\n // an HTTP error (404) on the production alias → try /preview/; a network error is final\n if (dl.ok || dl.kind !== 'http') break\n }\n if (!dl) throw new CliError('internal: no download attempted')\n\n if (!dl.ok) {\n const allowEmpty = flags['allow-empty'] === true\n // a trusted committed file always wins over the placeholder: --allow-empty never discards signed content\n if (trusted) {\n log.warn(\n `${dl.detail} at ${dl.url}; keeping the committed ${FALLBACK_PATH} (revision ${trusted.revision})${allowEmpty ? '; --allow-empty does not replace a trusted fallback' : ''}`,\n )\n return 0\n }\n if (committed.exists && committedJson && isPlaceholder(committedJson)) {\n log.warn(`${dl.detail} at ${dl.url}; keeping the committed empty placeholder`)\n return 0\n }\n if (allowEmpty) {\n writeFileAtomic(committed.path, fallbackFileContent(emptyPlaceholder(siteKey, channel)))\n log.warn(\n `${dl.detail} at ${dl.url}; wrote an EMPTY placeholder (--allow-empty): the section renders nothing`,\n )\n return 0\n }\n // no usable committed file and nothing downloadable: a repo bug or an unpublished site, never fail-soft\n throw new CliError(\n `${NO_MANIFEST_MESSAGE(siteKey)} (${dl.detail} at ${dl.url}; no committed ${FALLBACK_PATH} passes the identity check)`,\n )\n }\n\n const v = await verifyDownloaded(\n dl.text,\n roots,\n { site: siteKey, channel },\n trusted?.signer.revokedKids ?? [],\n )\n if (!v.ok) {\n if (trusted) {\n log.warn(\n `downloaded manifest failed verification (${v.reason}) at ${dl.url}; keeping the committed file`,\n )\n return 0\n }\n throw new CliError(\n `downloaded manifest failed verification (${v.reason}) at ${dl.url} and no trusted committed ${FALLBACK_PATH} exists`,\n )\n }\n const live = v.manifest\n if (trusted && live.revision < trusted.revision) {\n log.warn(\n `live revision ${live.revision} is lower than the committed revision ${trusted.revision}; refusing to downgrade (a stale CDN edge or a rollback in flight)`,\n )\n return 0\n }\n if (trusted && live.revision === trusted.revision) {\n log.ok(`${FALLBACK_PATH} already at revision ${live.revision}`)\n return 0\n }\n writeFileAtomic(committed.path, fallbackFileContent(JSON.parse(dl.text)))\n // configuration failure after the write: must never ship a wrong-site fallback\n const check = readJsonFile<{ site?: unknown }>(committed.path)\n if (!check || check.site !== siteKey)\n throw new CliError(`${FALLBACK_PATH} site ${String(check?.site)} ≠ ${siteKey} after the write`)\n log.ok(\n `wrote ${FALLBACK_PATH}: revision ${live.revision} (${trusted ? `was ${trusted.revision}` : 'new'}), ${live.businesses.length} items, ${dl.bytes} bytes, ${channel} channel, signer ${live.sig.kid}`,\n )\n return 0\n}\n\nfunction isPlaceholder(o: Record<string, unknown>): boolean {\n return (\n o['revision'] === 0 &&\n Array.isArray(o['businesses']) &&\n o['businesses'].length === 0 &&\n !('sig' in o)\n )\n}\n","/**\n * `network-sdk` CLI — spec §6.6: `init`, `sync-fallback`, `doctor`, `print-config`. Exit codes: 0 ok, 1 failure\n * (configuration, verification, doctor fail), 2 usage. Secrets are never printed (doctor reports env names as\n * set/unset only).\n */\nimport { SDK_VERSION } from '../constants.js'\nimport { runDoctor } from './doctor.js'\nimport { runInit } from './init.js'\nimport { runPrintConfig } from './printConfig.js'\nimport { CliError, log, parseArgs, requireWebCrypto } from './shared.js'\nimport { runSyncFallback } from './syncFallback.js'\n\nconst HELP = `network-sdk ${SDK_VERSION} — @intenque/network-sdk CLI (spec §6.6)\n\n network-sdk init --site-key <key> [--url <cdn-base>] [--root-keys <json>] [--channel production|preview] [--root <dir>] [--force]\n network-sdk sync-fallback [--url <cdn-base>] [--root-keys <json>] [--channel …] [--allow-empty] [--root <dir>]\n network-sdk doctor [--frontend] [--url <cdn-base>] [--root-keys <json>] [--api-url <origin>] [--preflight-path <path>] [--no-impression] [--root <dir>]\n network-sdk doctor --backend [--mongo-uri <uri>] [--root <dir>]\n network-sdk print-config [--root <dir>]\n\n --url development / staging CDN base (sync-fallback otherwise reads the production CDN only, §4.6)\n --root-keys inline JSON [{ kid, jwk }] (the hub's MANIFEST_ROOT_PUBLIC_KEYS value); otherwise the compiled production roots\n --mongo-uri checks the outbox indexes with the site's own mongodb driver (never printed)\n`\n\nexport async function main(argv: string[]): Promise<number> {\n const { positional, flags } = parseArgs(argv)\n const cmd = positional[0]\n if (!cmd || cmd === 'help' || flags['help'] === true) {\n console.log(HELP)\n return cmd ? 0 : 2\n }\n if (flags['version'] === true || cmd === '--version') {\n console.log(SDK_VERSION)\n return 0\n }\n try {\n // init / sync-fallback / doctor verify signatures; fail fast with one clear message instead of \"root_sig\" noise\n if (cmd === 'init' || cmd === 'sync-fallback' || cmd === 'doctor') requireWebCrypto()\n switch (cmd) {\n case 'init':\n return await runInit(flags)\n case 'sync-fallback':\n return await runSyncFallback(flags)\n case 'doctor':\n return await runDoctor(flags)\n case 'print-config':\n return runPrintConfig(flags)\n default:\n log.error(`unknown command \"${cmd}\"`)\n console.log(HELP)\n return 2\n }\n } catch (err) {\n if (err instanceof CliError) {\n log.error(err.message)\n return err.exitCode\n }\n log.error(err instanceof Error ? err.message : String(err))\n return 1\n }\n}\n"],"mappings":"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","names":["randomUUID","existsSync","readFileSync","join","resolve","existsSync","readFileSync","join","obj","str","re","existsSync","readFileSync","join","str","randomUUID","join","readFileSync","existsSync","resolve","str","existsSync","readFileSync","join","readFileSync","join","existsSync","str","relative","relative","str"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-wide constants the SDK must agree on with the hub (packages/shared/src/constants.ts) — spec §4.2, §4.3,
|
|
3
|
+
* §4.5, §5.3, §6.1. Kept in a dependency-free file so every entry (react, capture, server, cli) can import it without
|
|
4
|
+
* pulling the others in.
|
|
5
|
+
*/
|
|
6
|
+
/** Semver of this package; SDK major = manifest schema major (§6.1). Bumped by the version script. */
|
|
7
|
+
declare const SDK_VERSION = "2.2.0-beta.1";
|
|
8
|
+
/** `schema` this SDK reads (§4.3). A greater major → failure → fallback. */
|
|
9
|
+
declare const MANIFEST_SCHEMA_MAJOR = 2;
|
|
10
|
+
/**
|
|
11
|
+
* §6.1 compiled `DEFAULT_CDN_BASE`: the production manifest base. Decision 15: default DigitalOcean domains until the
|
|
12
|
+
* agency domain exists, so this is the Spaces CDN default endpoint plus the production prefix (`network/v1`). It is
|
|
13
|
+
* only the last resort for a site with no committed fallback (§4.2 URL precedence: `config.manifestUrl ??
|
|
14
|
+
* (config.cdnBase ?? fallback.cdnBase ?? DEFAULT_CDN_BASE)`); a domain cutover is a publish + `sync-fallback`, not an
|
|
15
|
+
* SDK release.
|
|
16
|
+
*/
|
|
17
|
+
declare const DEFAULT_CDN_BASE = "https://inr-network-public.nyc3.cdn.digitaloceanspaces.com/network/v1";
|
|
18
|
+
type SiteChannel = 'production' | 'preview';
|
|
19
|
+
|
|
20
|
+
export { DEFAULT_CDN_BASE as D, MANIFEST_SCHEMA_MAJOR as M, type SiteChannel as S, SDK_VERSION as a };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network-wide constants the SDK must agree on with the hub (packages/shared/src/constants.ts) — spec §4.2, §4.3,
|
|
3
|
+
* §4.5, §5.3, §6.1. Kept in a dependency-free file so every entry (react, capture, server, cli) can import it without
|
|
4
|
+
* pulling the others in.
|
|
5
|
+
*/
|
|
6
|
+
/** Semver of this package; SDK major = manifest schema major (§6.1). Bumped by the version script. */
|
|
7
|
+
declare const SDK_VERSION = "2.2.0-beta.1";
|
|
8
|
+
/** `schema` this SDK reads (§4.3). A greater major → failure → fallback. */
|
|
9
|
+
declare const MANIFEST_SCHEMA_MAJOR = 2;
|
|
10
|
+
/**
|
|
11
|
+
* §6.1 compiled `DEFAULT_CDN_BASE`: the production manifest base. Decision 15: default DigitalOcean domains until the
|
|
12
|
+
* agency domain exists, so this is the Spaces CDN default endpoint plus the production prefix (`network/v1`). It is
|
|
13
|
+
* only the last resort for a site with no committed fallback (§4.2 URL precedence: `config.manifestUrl ??
|
|
14
|
+
* (config.cdnBase ?? fallback.cdnBase ?? DEFAULT_CDN_BASE)`); a domain cutover is a publish + `sync-fallback`, not an
|
|
15
|
+
* SDK release.
|
|
16
|
+
*/
|
|
17
|
+
declare const DEFAULT_CDN_BASE = "https://inr-network-public.nyc3.cdn.digitaloceanspaces.com/network/v1";
|
|
18
|
+
type SiteChannel = 'production' | 'preview';
|
|
19
|
+
|
|
20
|
+
export { DEFAULT_CDN_BASE as D, MANIFEST_SCHEMA_MAJOR as M, type SiteChannel as S, SDK_VERSION as a };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var Je=react.createContext(null),Q=react.createContext(null),ee=react.createContext(null);function $n(e){return jsxRuntime.jsx(Je.Provider,{value:e.config,children:e.children})}function V(e){let n=react.useContext(Je);return e??n}var F="2.2.0-beta.1",We=2,pe="https://inr-network-public.nyc3.cdn.digitaloceanspaces.com/network/v1";var ge={medium:"network",campaign:"cross-promo"},Xe=1e4,ne=2e3,ye=1e3,ze=300,ke=.5,Ye=864e5,qe=2e4,be=24,Ze=8,he=4096,Qe=10,en=64,j={webdriver:1,noLanguages:2,zeroPlugins:4,headlessUa:8,noOuterWindow:16},nn=["sig_fail","rev_rollback","stale_cache","fallback_invalid","fallback_key_expired","sitekey_from_fallback","sdk_outdated"];function tn(e,n,t){return `${e.replace(/\/+$/,"")}/${t==="preview"?"preview/":""}sites/${n}/manifest.json`}var te="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";function Fn(e){let n="";for(let t=0;t<e.length;t+=3){let r=e[t]<<16|(e[t+1]??0)<<8|(e[t+2]??0);n+=te[r>>18]+te[r>>12&63],t+1<e.length&&(n+=te[r>>6&63]),t+2<e.length&&(n+=te[r&63]);}return n}function re(e){try{return crypto.getRandomValues(new Uint8Array(e))}catch{return null}}function rn(e=re){let n=e(16);if(!n||n.length<16)return null;n[6]=n[6]&15|64,n[8]=n[8]&63|128;let t=Array.from(n.subarray(0,16),r=>r.toString(16).padStart(2,"0")).join("");return `${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}function on(e,n,t,r=re){let i=g=>Number.isInteger(g)&&g>=0&&g<=65535,o=Math.floor(t/6e4);if(!i(e)||!i(n)||!(o>=0&&o<=4294967295))return null;let s=r(16);if(!s||s.length<16)return null;let a=new Uint8Array(24);return a[0]=e>>>8,a[1]=e&255,a[2]=n>>>8,a[3]=n&255,a[4]=o>>>24,a[5]=o>>>16&255,a[6]=o>>>8&255,a[7]=o&255,a.set(s.subarray(0,16),8),Fn(a)}function ve(e,n){let t=e.indexOf("#"),r=t<0?e:e.slice(0,t),i=t<0?"":e.slice(t),o=r.indexOf("?"),s=o<0?[]:r.slice(o+1).split("&").filter(a=>a!==""&&a.split("=")[0]!=="inr");return s.push(`${"inr"}=${n}`),`${o<0?r:r.slice(0,o)}?${s.join("&")}${i}`}var we=[],jn=[{kid:"root-dev-a01e0d",jwk:{kty:"EC",crv:"P-256",x:"4dO5Ybuh3aMQMYvVUYd82CZ2y-tM2wM4SeunZvxMKHQ",y:"Tw6MyboNkggXo9jV7_Tng8tCGNMin7Dxuept8AY0Anc"}}];var Hn=["section","container","heading","accentWord","subheading","grid","card","imageBox","image","placeholder","body","name","meta","description","button","buttonIcon"],sn=e=>e!==null&&typeof e=="object"&&!Array.isArray(e);function an(e){return !!e&&/^(localhost|.+\.localhost|127\.0\.0\.1|\[::1\]|::1)$/i.test(e)}function Gn(e){try{return !!e&&an(new URL(e).hostname)}catch{return false}}function Se(e,n,t){return e.channel!=="preview"&&(e.dev||an(n))&&!Gn(t)}function Jn(e){let n=sn(e.fallback)?e.fallback:null,t=n&&typeof n.site=="string"?n.site:"",r=typeof e.siteKey=="string"&&e.siteKey?e.siteKey:t,i=e.channel==="preview"?"preview":"production",o=n&&typeof n.cdnBase=="string"?n.cdnBase:void 0,s=n&&sn(n.analytics)?n.analytics:null,a=s&&typeof s.endpoint=="string"?s.endpoint:null,g=e.dev===true,p=Se({dev:g,channel:i},void 0,a)?null:a,y=e.consentMode==="require-consent"?"require-consent":"session-default";return {siteKey:r,siteKeyFromFallback:!e.siteKey&&!!t,channel:i,manifestUrl:e.manifestUrl??tn(e.cdnBase??o??pe,r,i),fallback:e.fallback,roots:e.manifestRootKeys??we,consent:e.consent,consentMode:y,theme:e.theme??{},unstyled:e.unstyled===true,verifySignature:e.verifySignature!==false,allowInsecureFallback:e.allowInsecureFallback===true,dev:g,onEvent:e.onEvent,capture:{siteKey:r,endpoint:p,consent:e.consent,consentMode:y}}}function ln(e,n,t){let r=o=>{try{t?.(o);}catch{}return o};try{if(location.protocol=="https:"&&/^http:/i.test(e))return r(!1)}catch{}try{if(navigator.sendBeacon(e,n))return r(!0)}catch{}let i=o=>fetch(e,{method:"POST",mode:"no-cors",keepalive:o,credentials:"omit",body:n});try{return i(!0).then(()=>r(!0),()=>{try{if(document.visibilityState=="visible")return void i(!1).then(()=>r(!0),()=>r(!1))}catch{}r(!1);}),!0}catch{return r(false)}}var dn={now:()=>Date.now(),random:re,roll:()=>Math.random(),send:ln,later:(e,n)=>{let t=setTimeout(e,n);return ()=>clearTimeout(t)},doc:()=>typeof document>"u"?void 0:document,win:()=>typeof window>"u"?void 0:window,nav:()=>typeof navigator>"u"?void 0:navigator,host:()=>typeof location>"u"?void 0:location.hostname,IO:()=>typeof IntersectionObserver=="function"?IntersectionObserver:void 0},cn=e=>({el:e,inter:false,since:null,ms:0,seen:false,fv:null}),un=e=>Math.min(Ye,Math.max(0,Math.round(e))),fn=e=>typeof e=="number"&&e>0?Math.min(qe,Math.round(e)):0,Wn=.005,Xn=new Set(nn);function zn(e,n){let t=0;try{e&&(e.webdriver===!0&&(t|=j.webdriver),(!e.languages||e.languages.length===0)&&(t|=j.noLanguages),e.plugins&&e.plugins.length===0&&(t|=j.zeroPlugins),/HeadlessChrome/.test(e.userAgent??"")&&(t|=j.headlessUa)),n&&n.outerWidth===0&&n.outerHeight===0&&(t|=j.noOuterWindow);}catch{}return t}function Yn(e){let n=[];for(let t of e){let r=t==="sig_revoked"?"sig_fail":t;Xn.has(r)&&!n.includes(r)&&n.push(r);}return n.slice(0,Ze)}var R=e=>{try{e();}catch{}};function mn(e,n){let t=rn(n.random);if(!t)return null;let r=e.manifest.analytics,i=Math.round(n.now()),o=r.enabled&&r.impressions&&r.samplePermille>0&&n.roll()*1e3<r.samplePermille?1:0,s=zn(n.nav(),n.win()),a=Yn(e.flags),g=e.rr!==null&&(a.includes("rev_rollback")||a.includes("stale_cache"))?e.rr:null,p=()=>{try{return e.withhold?.()===!0}catch{return true}},y=new Map,b=cn(null),c=[],k=[],h=false,v=0,f="",x="",d=null,w=null,S=false,I=false,L=false,fe=true,Ve=(l,m,u)=>{if(m)l.since===null&&(l.since=u,l.fv===null&&(l.fv=un(u-i)));else if(l.since!==null){let E=Math.max(0,u-l.since);l.ms+=E,E>=ye&&(l.seen=true),l.since=null;}},Fe=(l,m)=>{if(!l)return {seen:false,ms:0,fv:null};let u=l.since===null?0:Math.max(0,m-l.since);return {seen:l.seen||u>=ye,ms:un(l.ms+u),fv:l.fv}},N=()=>{let l=n.now(),m=L&&fe&&n.doc()?.visibilityState==="visible",u=false;for(let E of y.values()){let T=m&&E.inter&&E.el!==null;u=u||T,Ve(E,T,l);}Ve(b,m&&(b.inter||u),l),b.fv!==null&&!h&&!w&&!S&&(w=n.later(()=>R(()=>{w=null,S=!0,N(),K("timer");}),Xe));},On=l=>{for(let m of l){let u=b.el===m.target?b:[...y.values()].find(E=>E.el===m.target);u&&(u.inter=m.isIntersecting&&m.intersectionRatio>=ke-Wn);}N();},je=l=>({v:2,k:e.manifest.site,pv:t,r:e.manifest.revision,src:e.src,sdk:F,tok:r.token,sp:r.samplePermille,smp:o,t:Math.round(l),hd:p()?0:s,m:i}),He=(l,m,u=null)=>n.send(`${r.endpoint.replace(/\/+$/,"")}/${l}`,JSON.stringify(m),E=>R(()=>e.onSignal(l,E,u))),Bn=(l,m)=>JSON.stringify([l.seen,l.ms>=ze,m.map(u=>[u.seen,u.fv!==null])]),K=l=>{if(!L||!d||v>=Qe||h&&l==="timer")return false;let m=n.now(),u=Fe(b,m);if(b.fv===null||u.ms<1)return false;let E=k.map((U,de)=>({id:U,pos:de,...Fe(y.get(U),m)})),T=JSON.stringify([u,E]);if(h&&T===f)return false;let z=Bn(u,E);if(h&&l==="click"&&z===x)return false;w?.(),w=null,h=true,v+=1,f=T,x=z;let O=n.win(),$={...je(m),items:E,ss:u.seen,sm:u.ms,vp:p()?{w:0,h:0}:{w:fn(O?.innerWidth),h:fn(O?.innerHeight)},why:l};if(a.length&&($.fl=a),g!==null&&($.rr=g),JSON.stringify($).length>he){let U=E.filter(de=>de.fv!==null);for($.items=U;JSON.stringify($).length>he&&U.length;)U.pop();}return He("impression",$,l),true},Ge=()=>{let l=n.IO(),m=n.doc(),u=n.win();if(!I||L||!l||!m)return;L=true,d??=new l(O=>R(()=>On(O)),{threshold:ke}),b.el&&d.observe(b.el);for(let O of y.values())O.el&&d.observe(O.el);let E=()=>R(()=>{N(),m.visibilityState==="hidden"&&K("hidden");}),T=()=>R(()=>{N(),K("pagehide"),fe=!1,N();}),z=()=>R(()=>{fe=!0,N();});m.addEventListener("visibilitychange",E),u?.addEventListener("pagehide",T),u?.addEventListener("pageshow",z),c.push(()=>m.removeEventListener("visibilitychange",E),()=>u?.removeEventListener("pagehide",T),()=>u?.removeEventListener("pageshow",z)),N();};return {pv:t,m:i,smp:o,hd:s,get sent(){return h},setOrder(l){k=l.slice(0,be);},pos(l){let m=k.indexOf(l.id);return m>=0?m:Math.min(Math.max(0,l.position),be-1)},observe(l,m){R(()=>{let u=y.get(l);u&&u.el===m||(u?.el&&d?.unobserve(u.el),u?(u.el=m,u.inter=!1,N()):m&&y.set(l,cn(m)),m&&d&&L&&d.observe(m));});},start(l){R(()=>{l&&(b.el=l);let m=n.doc();if(!(I||o===0||!m))if(I=!0,m.prerendering){let u=()=>R(Ge);m.addEventListener("prerenderingchange",u,{once:!0}),c.push(()=>m.removeEventListener("prerenderingchange",u));}else Ge();});},stop(){R(()=>{if(I){N(),K("hidden"),I=!1,L=!1,w?.(),w=null,d?.disconnect();for(let l of c.splice(0))l();N();}});},common:je,post:He,flushForClick(){return R(()=>{N(),K("click");}),h},flush:K}}var qn=5*6e4;function pn(e,n,t=dn){let r=c=>{try{e()?.onEvent?.(c);}catch{}},i=n.manifest,o=n.source,s=null;try{let c=e(),k=c!==null&&Se(c,t.host?.(),i?.analytics.endpoint??null);i&&!k&&i.analytics.enabled&&(o==="live"||o==="fallback")&&(s=mn({manifest:i,src:o,flags:n.flags,rr:n.rr,onSignal:(h,v,f)=>r({type:"signal",payload:f?{kind:h,sent:v,why:f}:{kind:h,sent:v}}),withhold:()=>{let h=e();return h?.consentMode==="require-consent"&&!(h.consent?.get()==="granted"&&t.nav()?.globalPrivacyControl!==!0)}},t));}catch{s=null;}let a=s!==null&&i!==null&&i.analytics.clicks,g=new Map,p=[],y=c=>i?on(i.siteIndex,c.idx,t.now(),t.random):null,b=c=>{try{c();}catch{}};return {page:s,rendered(c){r({type:"impression",payload:{ids:c,revision:i?.revision??null}});},pointerDown(c,k){let h=null;return b(()=>{if(!a)return;let v=y(c);v&&(g.set(c.id,{id:v,at:t.now()}),c.instrumentation!=="none"&&(h=ve(k,v)));}),h},click(c,k,h,v){let f=k;return b(()=>{if(!a||!s)return;let x=g.get(c.id);g.delete(c.id);let d=x&&t.now()-x.at<=qn?x.id:y(c);if(!d||(c.instrumentation!=="none"&&(f=ve(k,d)),p.includes(d)))return;p.push(d),p.length>en&&p.shift();let w=s.flushForClick();s.post("click",{...s.common(t.now()),c:d,b:c.id,pos:s.pos(c),btn:v,via:h,ip:w});}),r({type:"click",payload:{id:c.id,href:k,via:h}}),f},observe:(c,k)=>b(()=>s?.observe(c,k)),setOrder:c=>b(()=>s?.setOrder(c)),start:c=>b(()=>s?.start(c)),stop:()=>b(()=>s?.stop())}}var Zn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",gn={};for(let e=0;e<64;e++)gn[Zn[e]]=e;function Qn(e){return /^[A-Za-z0-9_-]*$/.test(e)}function xe(e,n){if(typeof e!="string"||!Qn(e)||e.length!==Math.ceil(n*4/3))return null;let t=new Uint8Array(n),r=0,i=0,o=0;for(let s=0;s<e.length;s++)if(r=(r<<6|gn[e[s]])&65535,i+=6,i>=8){if(i-=8,o>=n)return null;t[o++]=r>>i&255;}return o!==n||i>0&&(r&(1<<i)-1)!==0?null:t}function Ee(e){let n="";for(let t of e)n+=t.toString(16).padStart(2,"0");return n}var kn=BigInt("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"),et=kn>>1n;function Me(){let e=globalThis.crypto;return e&&e.subtle?e.subtle:null}function nt(e){let n=xe(e,64);if(!n)return "encoding";let t=BigInt(`0x${Ee(n.subarray(0,32))}`),r=BigInt(`0x${Ee(n.subarray(32))}`);return t===0n||t>=kn||r===0n||r>et?"range":null}var yn=new WeakMap;async function tt(e,n){let t=yn.get(e);if(t)return t;let r=n.importKey("jwk",{kty:"EC",crv:"P-256",x:e.x,y:e.y,ext:true},{name:"ECDSA",namedCurve:"P-256"},false,["verify"]);return yn.set(e,r),r}async function _e(e,n,t){try{let r=Me();if(!r||nt(t)!==null)return !1;let i=xe(t,64),o=await tt(e,r);return await r.verify({name:"ECDSA",hash:"SHA-256"},o,i,new TextEncoder().encode(n))}catch{return false}}var ie=/^[a-z0-9]+(-[a-z0-9]+)*$/,Ce=48,Re=24,Y=/^[a-z0-9][a-z0-9-]{1,63}$/,Le=/^[A-Za-z0-9_-]{85}[AQgw]$/,Ae=/^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/,se=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/,rt=/^[a-z0-9-]+\.\d+\.\d{8}\.[0-9a-f]{12}$/,bn=/^[a-z0-9.-]+$/,Ne=class extends Error{},_=()=>{throw new Ne};function P(e,n){(e===null||typeof e!="object"||Array.isArray(e))&&_();let t=e;for(let r of Object.keys(t))n.includes(r)||_();for(let r of n)r in t||_();return t}function ot(e){let n=0;for(let t of e)n++;return n}function A(e,n,t){typeof e!="string"&&_();let r=ot(e);return (r<n||r>t)&&_(),e}function M(e,n,t=1024){let r=A(e,0,t);return n.test(r)||_(),r}function q(e,n,t){return (typeof e!="number"||!Number.isInteger(e)||e<n||e>t)&&_(),e}function H(e){return typeof e!="boolean"&&_(),e}function oe(e,n){return e===null?null:n(e)}function G(e,n){return e!==n&&_(),n}function hn(e,n){return (typeof e!="string"||!n.includes(e))&&_(),e}function vn(e){let n;try{n=new URL(e);}catch{return "must be an absolute URL"}let t=n.hostname==="localhost"||n.hostname==="127.0.0.1";return n.protocol!=="https:"&&!(n.protocol==="http:"&&t)?"must be https":n.username||n.password||n.search||n.hash?"no userinfo, query or fragment":null}function ae(e){let n=A(e,1,1024);return vn(n)!==null&&_(),n}function wn(e){if(e.length>200)return "must be at most 200 characters";let n;try{n=new URL(e);}catch{return "must be an absolute URL"}if(n.protocol!=="https:")return "must use https";if(n.username||n.password)return "must not contain credentials";if(n.port!==""&&n.port!=="443")return "port must be 443";let t=n.hostname;return t.startsWith("[")||/^\d+(\.\d+){3}$/.test(t)?"IP addresses are not allowed":!bn.test(t)||!t.includes(".")||t.startsWith(".")||t.endsWith(".")?"invalid hostname":n.toString()!==e?"must be normalised":null}function it(e){let n=A(e,1,200);return wn(n)!==null&&_(),n}function Sn(e){let n=P(e,["kty","crv","x","y"]);return {kty:G(n.kty,"EC"),crv:G(n.crv,"P-256"),x:M(n.x,Ae),y:M(n.y,Ae)}}function st(e){let n=P(e,["src","src2x","alt","width","height"]);return {src:ae(n.src),src2x:ae(n.src2x),alt:A(n.alt,1,120),width:G(n.width,640),height:G(n.height,360)}}var at=["id","idx","name","description","url","domain","image","category","location","region","tags","pinned","position","instrumentation"];function lt(e){let n=P(e,at),t=A(n.name,2,60);/^[=+@]/.test(t)&&_();let r=M(n.domain,bn,253);(r.length<1||r.startsWith("www."))&&_();let i=n.tags;return (!Array.isArray(i)||i.length>10)&&_(),{id:M(n.id,ie,Ce),idx:q(n.idx,1,65535),name:t,description:A(n.description,1,140),url:it(n.url),domain:r,image:st(n.image),category:oe(n.category,o=>A(o,0,40)),location:oe(n.location,o=>A(o,0,60)),region:oe(n.region,o=>A(o,0,40)),tags:i.map(o=>A(o,0,24)),pinned:H(n.pinned),position:q(n.position,0,Re-1),instrumentation:hn(n.instrumentation,["none","landings","conversions"])}}function ct(e){let n=P(e,["kid","jwk","notBefore","notAfter","rootKid","rootSig","revokedKids"]),t=n.revokedKids;return Array.isArray(t)||_(),{kid:M(n.kid,Y),jwk:Sn(n.jwk),notBefore:M(n.notBefore,se),notAfter:M(n.notAfter,se),rootKid:M(n.rootKid,Y),rootSig:M(n.rootSig,Le),revokedKids:t.map(r=>M(r,Y))}}var ut=["schema","revision","publishedAt","site","siteIndex","channel","cdnBase","minSdkVersion","analytics","links","section","businesses","signer","sig"];function J(e){try{let n=P(e,ut),t=P(n.analytics,["enabled","endpoint","impressions","clicks","landings","samplePermille","token"]),r=P(n.links,["utmMedium","utmCampaign","utmSource","refParam","nofollow"]),i=P(n.section,["title","subtitle"]),o=P(n.sig,["alg","kid","value"]),s=n.businesses;return (!Array.isArray(s)||s.length>Re)&&_(),{schema:G(n.schema,2),revision:q(n.revision,1,Number.MAX_SAFE_INTEGER),publishedAt:M(n.publishedAt,se),site:M(n.site,ie,Ce),siteIndex:q(n.siteIndex,1,65535),channel:hn(n.channel,["production","preview"]),cdnBase:ae(n.cdnBase),minSdkVersion:M(n.minSdkVersion,/^\d+\.\d+\.\d+$/),analytics:{enabled:H(t.enabled),endpoint:ae(t.endpoint),impressions:H(t.impressions),clicks:H(t.clicks),landings:H(t.landings),samplePermille:q(t.samplePermille,0,1e3),token:M(t.token,rt)},links:{utmMedium:A(r.utmMedium,1,40),utmCampaign:A(r.utmCampaign,1,40),utmSource:M(r.utmSource,ie,Ce),refParam:M(r.refParam,/^[a-z]{1,8}$/),nofollow:H(r.nofollow)},section:{title:A(i.title,1,60),subtitle:oe(i.subtitle,a=>A(a,0,120))},businesses:s.map(lt),signer:ct(n.signer),sig:{alg:G(o.alg,"ES256"),kid:M(o.kid,Y),value:M(o.value,Le)}}}catch{return null}}var B=class extends Error{constructor(t,r){super(`${t} at ${r||"$"}`);this.path=r;this.name="CanonicalJsonError";}path};function W(e){return Ie(e,"$")}function Ie(e,n){if(e===null)return "null";switch(typeof e){case "boolean":return e?"true":"false";case "string":return JSON.stringify(e);case "number":if(!Number.isSafeInteger(e))throw new B(`non-integer or unsafe number ${String(e)}`,n);return String(e);case "object":{if(Array.isArray(e))return `[${e.map((i,o)=>Ie(i,`${n}[${o}]`)).join(",")}]`;let t=Object.getPrototypeOf(e);if(t!==Object.prototype&&t!==null)throw new B("unsupported object type",n);let r=e;return `{${Object.keys(r).sort().map(i=>{let o=r[i];if(o===void 0)throw new B(`undefined value for key "${i}" (use null)`,n);return `${JSON.stringify(i)}:${Ie(o,`${n}.${i}`)}`}).join(",")}}`}default:throw new B(`unsupported type ${typeof e}`,n)}}function _n(e){return W({kid:e.kid,jwk:{kty:e.jwk.kty,crv:e.jwk.crv,x:e.jwk.x,y:e.jwk.y},notBefore:e.notBefore,notAfter:e.notAfter,rootKid:e.rootKid})}function Cn(e){let{sig:n,...t}=e;return W(t)}var En=e=>Array.isArray(e)&&e.every(n=>typeof n=="string");function Mn(e){return Date.parse(e)}async function Z(e,n){try{if(!n||typeof n!="object")return {ok:!1,reason:"malformed"};let t=n.now instanceof Date?n.now.getTime():NaN;if(!Number.isFinite(t))return {ok:!1,reason:"malformed"};if(n.source!=="live"&&n.source!=="fallback")return {ok:!1,reason:"malformed"};if(!n.expect||typeof n.expect.site!="string"||typeof n.expect.channel!="string")return {ok:!1,reason:"malformed"};if(n.source==="live"&&!En(n.revokedKids))return {ok:!1,reason:"malformed"};if(n.revokedKids!==void 0&&!En(n.revokedKids))return {ok:!1,reason:"malformed"};let r=J(e);if(!r)return {ok:!1,reason:"malformed"};if(r.site!==n.expect.site||r.channel!==n.expect.channel||r.links.utmSource!==n.expect.site)return {ok:!1,reason:"identity"};let i=r.signer,s=(Array.isArray(n.roots)?n.roots:[]).find(p=>p&&p.kid===i.rootKid);if(!s)return {ok:!1,reason:"root_unknown"};if(!await _e(s.jwk,_n(i),i.rootSig))return {ok:!1,reason:"root_sig"};let a=Mn(i.notBefore),g=Mn(i.notAfter);return !Number.isFinite(a)||!Number.isFinite(g)||g<a?{ok:!1,reason:"malformed"}:n.source==="live"&&(t<a||t>g+6048e5)?{ok:!1,reason:"window"}:r.sig.kid!==i.kid?{ok:!1,reason:"kid_mismatch"}:await _e(i.jwk,Cn(r),r.sig.value)?(n.revokedKids??[]).includes(r.sig.kid)||i.revokedKids.includes(r.sig.kid)?{ok:!1,reason:"revoked"}:{ok:!0,manifest:r}:{ok:!1,reason:"sig"}}catch{return {ok:false,reason:"malformed"}}}async function le(e,n){try{let t;if(typeof e=="string"){if(e.length>24576||new TextEncoder().encode(e).length>24576)return {ok:!1,reason:"malformed"};t=e;}else {if(!(e instanceof Uint8Array)||e.length>24576)return {ok:!1,reason:"malformed"};t=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(e);}let r=JSON.parse(t);return W(r)!==t?{ok:!1,reason:"malformed"}:await Z(r,n)}catch{return {ok:false,reason:"malformed"}}}var Te={businesses:[],loading:true,source:"empty",revision:null,manifest:null,flags:[],rr:null};function dt(e){if(e===null||typeof e!="object")return false;let n=e;return n.schema===2&&n.revision===0&&Array.isArray(n.businesses)&&n.businesses.length===0&&!("sig"in n)}function mt(e,n){let t=e.split("-")[0].split(".").map(Number),r=n.split("-")[0].split(".").map(Number);for(let i=0;i<3;i++){let o=t[i]??0,s=r[i]??0;if(o!==s)return o>s}return false}function Pe(e,n){let t=J(e);return t?t.site!==n.site||t.channel!==n.channel||t.links.utmSource!==n.site?{ok:false,reason:"identity"}:{ok:true,manifest:t}:{ok:false,reason:"malformed"}}async function An(e,n,t){let r=new AbortController,i=setTimeout(()=>r.abort(),4e3);try{let o=await e(n,{mode:"cors",credentials:"omit",cache:t,signal:r.signal});return o.ok?new Uint8Array(await o.arrayBuffer()):null}catch{return null}finally{clearTimeout(i);}}function Nn(e,n){return e.revision<n.revision||Date.parse(e.publishedAt)<Date.parse(n.publishedAt)}var pt=e=>e==="revoked"?"sig_revoked":"sig_fail";async function De(e,n={}){let t=[],r=null,i=n.warn??(a=>console.warn(`[network-sdk] ${a}`)),o=a=>{e.dev&&i(a);};try{let a=n.fetch??(typeof fetch=="function"?fetch.bind(globalThis):void 0),g=(n.now??(()=>new Date))(),p={site:e.siteKey,channel:e.channel},y=e.roots;if(e.siteKeyFromFallback&&t.push("sitekey_from_fallback"),!e.siteKey)return o("no siteKey and no fallback.site: rendering nothing (\xA74.6)"),s("empty",null,t);let b="full",c=Me()===null;if(!e.verifySignature)b="unverified",o("verifySignature is false: signatures are NOT checked");else if(c)if(t.push("insecure_context"),e.dev)b="unverified",i("crypto.subtle is unavailable (insecure context): signature verification skipped in dev (\xA74.6)");else if(e.allowInsecureFallback){let S=Pe(e.fallback,p);return S.ok?s("fallback",S.manifest,t):s("empty",null,t)}else return s("empty",null,t);let k=(S,I,L)=>b==="full"?Z(S,{roots:y,now:g,expect:p,source:I,revokedKids:L}):Promise.resolve(Pe(S,p)),h=(S,I)=>b==="full"?le(S,{roots:y,now:g,expect:p,source:"live",revokedKids:I}):(()=>{try{let L=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}).decode(S);return Promise.resolve(Pe(JSON.parse(L),p))}catch{return Promise.resolve({ok:!1,reason:"malformed"})}})(),v=null;if(e.fallback!==void 0&&!dt(e.fallback)){let S=await k(e.fallback,"fallback",[]);S.ok?v=S.manifest:(t.push("fallback_invalid"),o(`bundled fallback rejected (${S.reason}): floor ignored, fallback will not render (\xA74.6)`));}let f=v?v.signer.revokedKids:[],x=()=>v?(Date.parse(v.signer.notAfter)<g.getTime()&&t.push("fallback_key_expired"),s("fallback",v,t)):s("empty",null,t);if(!a)return t.push("fetch_fail"),x();let d=await An(a,e.manifestUrl,"default");if(d===null)return t.push("fetch_fail"),o(`manifest fetch failed (${e.manifestUrl}): rendering the fallback`),x();let w=await h(d,f);if(!w.ok)return t.push(pt(w.reason)),o(`live manifest rejected (${w.reason}): rendering the fallback`),x();if(v&&Nn(w.manifest,v)){d=await An(a,e.manifestUrl,"reload");let S=d===null?null:await h(d,f);if(S&&S.ok&&!Nn(S.manifest,v))t.push("stale_cache"),r=w.manifest.revision,w=S;else return t.push("rev_rollback"),r=S&&S.ok?S.manifest.revision:w.manifest.revision,o(`live manifest revision ${w.manifest.revision} is below the bundled floor ${v.revision}: rendering the fallback (rev_rollback)`),x()}return mt(w.manifest.minSdkVersion,F)&&(t.push("sdk_outdated"),o(`manifest requires SDK \u2265 ${w.manifest.minSdkVersion}, this is ${F}`)),s("live",w.manifest,t)}catch{return s("empty",null,t)}function s(a,g,p){let y=(n.hostname??gt()).replace(/^www\./,""),c={businesses:g?g.businesses.filter(k=>k.domain!==y&&k.id!==e.siteKey&&k.url.startsWith("https:")):[],loading:false,source:g?a:"empty",revision:g?g.revision:null,manifest:g,flags:p,rr:r};try{e.onEvent?.({type:"directory",payload:{source:c.source,revision:c.revision,flags:[...p]}});}catch{}return c}}function gt(){try{return typeof location<"u"?location.hostname.toLowerCase():""}catch{return ""}}var bt=e=>{if(e===null||typeof e!="object")return [typeof e];let n=e,t=n.sig&&typeof n.sig=="object"?n.sig:{};return [n.site,n.revision,n.publishedAt,t.value]};function ht(e){if(!e)return null;try{return JSON.stringify([e.siteKey,e.siteKeyFromFallback,e.channel,e.manifestUrl,e.verifySignature,e.allowInsecureFallback,e.dev,e.roots.map(n=>[n.kid,n.jwk.x,n.jwk.y]),bt(e.fallback)])}catch{return `${e.siteKey}|${e.manifestUrl}`}}function vt(e,n){return !e.loading&&e.source===n.source&&e.revision===n.revision&&e.rr===n.rr&&e.manifest?.sig.value===n.manifest?.sig.value&&e.businesses.length===n.businesses.length&&e.flags.join()===n.flags.join()}function Oe(e){let n=V(e),t=react.useRef(n);react.useEffect(()=>{t.current=n;});let r=ht(n),[i,o]=react.useState(Te);return react.useEffect(()=>{let s=t.current;if(!s){o(p=>!p.loading&&!p.manifest?p:{...Te,loading:false});return}let a=true,g={...s,onEvent:p=>t.current?.onEvent?.(p)};return De(g).then(p=>{a&&o(y=>vt(y,p)?y:p);}),()=>{a=false;}},[r]),i}function Tn(e,n,t){let r=new URLSearchParams({utm_source:n.utmSource,utm_medium:n.utmMedium,utm_campaign:n.utmCampaign,utm_content:t}).toString(),i=e.indexOf("#"),o=i>=0?e.slice(0,i):e,s=i>=0?e.slice(i):"";return `${o}${o.includes("?")?"&":"?"}${r}${s}`}function ce(e,n){try{let t=e.currentTarget;n&&t&&typeof t=="object"&&typeof t.href=="string"&&t.href!==n&&(t.href=n);}catch{}}var In=e=>e==="click"?0:e==="auxclick"?1:2,xt=["pointerDown","click","observe"];function Pn(e){return typeof e=="object"&&e!==null&&xt.every(n=>typeof e[n]=="function")}var D=e=>(...n)=>{try{e(...n);}catch{}};function Be(e,n){let t=react.useContext(Q),r=V(),i=react.useContext(ee),o=n===null?null:Pn(n)?n:Pn(i)?i:null,s=typeof n=="function"?n:null,a=e.id,g=react.useCallback(p=>{try{o?.observe(a,p);}catch{}},[o,a]);return react.useMemo(()=>{let p=t?.links??{utmSource:r?.siteKey??"",utmMedium:ge.medium,utmCampaign:ge.campaign,nofollow:false},y=Tn(e.url,p,e.id),b=null,c=()=>{b?.(),b=null;},k=(f,x)=>{c();let d={currentTarget:f.currentTarget};if(x<=0)return ce(d,y);let w=setTimeout(()=>{b=null,ce(d,y);},x);b=()=>clearTimeout(w);},h=(f,x)=>{o&&(ce(f,o.click(e,y,x,In(x))),k(f,ne));},v=(f="click",x)=>{let d=y;try{o&&(d=o.click(e,y,f,x??In(f)));}catch{d=y;}if(s)try{let w=s(e,d,f);if(typeof w=="string"&&w)return w}catch{}return d};return {href:y,linkProps:{href:y,target:"_blank",rel:`noopener noreferrer${p.nofollow?" nofollow":""}`,"aria-label":`Visit ${e.name} website (opens in a new tab)`,onPointerDown:D(f=>{c(),o&&ce(f,o.pointerDown(e,y));}),onPointerUp:D(f=>k(f,ne)),onPointerCancel:D(f=>k(f,ne)),onBlur:D(f=>k(f,0)),onClick:D(f=>h(f,"click")),onAuxClick:D(f=>{f.button===1&&h(f,"auxclick");}),onContextMenu:D(f=>h(f,"contextmenu")),onDragStart:D(f=>h(f,"drag"))},trackClick:v,observe:g}},[e,t,r,o,s,g])}var Ue="discover-businesses-heading",Dn=e=>e.replace(/[A-Z]/g,n=>`-${n.toLowerCase()}`);function Mt(e,n,t){let r=n[e];return t?r||void 0:r?`inr-${Dn(e)} ${r}`:`inr-${Dn(e)}`}function _t(e){let n=e.trimEnd().lastIndexOf(" ");return n<0?["",e]:[e.slice(0,n+1),e.slice(n+1)]}function Ct(e){let n=V(e.config),t=e.onEvent??n?.onEvent,r=react.useMemo(()=>n?t===n.onEvent?n:{...n,onEvent:t}:null,[n,t]),i=react.useRef(r);react.useEffect(()=>{i.current=r;});let o=Oe(r),s=react.useMemo(()=>pn(()=>i.current,o),[o]),a=react.useMemo(()=>{let d=e.maxItems&&e.maxItems>0?e.maxItems:o.businesses.length;return o.businesses.slice(0,d)},[o.businesses,e.maxItems]),g=react.useRef(false);react.useEffect(()=>{a.length>0&&!g.current&&(g.current=true,s.rendered(a.map(d=>d.id)));},[a,s]),react.useEffect(()=>{s.setOrder(a.map(d=>d.id));},[a,s]);let p=!!r&&!o.loading&&a.length>0&&!!o.manifest,y=react.useRef(null);if(react.useEffect(()=>{if(p)return s.start(y.current),()=>s.stop()},[s,p]),!r||o.loading||a.length===0||!o.manifest)return null;let b=e.theme?{...r.theme,...e.theme}:r.theme,c=e.unstyled??r.unstyled,k=d=>Mt(d,b,c),h=e.title??o.manifest.section.title,v=e.subtitle===void 0?o.manifest.section.subtitle:e.subtitle,[f,x]=_t(h);return jsxRuntime.jsx(Q.Provider,{value:o.manifest,children:jsxRuntime.jsx(ee.Provider,{value:s,children:jsxRuntime.jsx("section",{ref:y,className:k("section"),"aria-labelledby":Ue,"data-inr-rev":o.revision??void 0,"data-inr-source":o.source,children:jsxRuntime.jsxs("div",{className:k("container"),children:[jsxRuntime.jsxs("h2",{id:Ue,className:k("heading"),children:[f,jsxRuntime.jsx("span",{className:k("accentWord"),children:x})]}),v?jsxRuntime.jsx("p",{className:k("subheading"),children:v}):null,jsxRuntime.jsx("div",{className:k("grid"),children:a.map(d=>jsxRuntime.jsx(At,{business:d,cls:k,renderCard:e.renderCard},d.id))})]})})})})}function At(e){let{business:n,cls:t}=e,r=Be(n),[i,o]=react.useState(false);if(e.renderCard)return jsxRuntime.jsx(jsxRuntime.Fragment,{children:e.renderCard(n,r)});let s=[n.category,n.location].filter(Boolean).join(" \xB7 ");return jsxRuntime.jsxs("article",{ref:r.observe,className:t("card"),"data-inr-id":n.id,children:[jsxRuntime.jsx("div",{className:t("imageBox"),children:i?jsxRuntime.jsx("div",{className:t("placeholder"),role:"img","aria-label":n.image.alt,children:n.name.trim().charAt(0).toUpperCase()}):jsxRuntime.jsx("img",{className:t("image"),src:n.image.src,srcSet:`${n.image.src} 1x, ${n.image.src2x} 2x`,alt:n.image.alt,width:n.image.width,height:n.image.height,loading:"lazy",decoding:"async",onError:()=>o(true)})}),jsxRuntime.jsxs("div",{className:t("body"),children:[jsxRuntime.jsx("h3",{className:t("name"),children:n.name}),s?jsxRuntime.jsx("p",{className:t("meta"),children:s}):null,jsxRuntime.jsx("p",{className:t("description"),children:n.description}),jsxRuntime.jsxs("a",{...r.linkProps,className:t("button"),children:["Visit Website",jsxRuntime.jsxs("svg",{className:t("buttonIcon"),"aria-hidden":"true",focusable:"false",width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[jsxRuntime.jsx("path",{d:"M15 3h6v6"}),jsxRuntime.jsx("path",{d:"M10 14 21 3"}),jsxRuntime.jsx("path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"})]})]})]})]})}
|
|
2
|
+
exports.DEFAULT_CDN_BASE=pe;exports.DEV_ROOT_PUBLIC_KEYS=jn;exports.DiscoverBusinesses=Ct;exports.HEADING_ID=Ue;exports.MANIFEST_SCHEMA_MAJOR=We;exports.NetworkProvider=$n;exports.SDK_VERSION=F;exports.THEME_SLOTS=Hn;exports.buildHref=Tn;exports.canonicalJson=W;exports.defineNetworkConfig=Jn;exports.loadDirectory=De;exports.parseManifest=J;exports.rootPublicKeys=we;exports.useNetworkDirectory=Oe;exports.useNetworkLink=Be;exports.verifyManifest=Z;exports.verifyManifestBytes=le;//# sourceMappingURL=index.cjs.map
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|