@aihu/compiler 1.3.5 → 1.3.6
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/README.md +37 -7
- package/dist/index.d.ts +49 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["__dirname"],"sources":["../js/native.ts","../js/spawn-bounds.ts","../js/envelope.ts","../js/transform-memo.ts","../js/index.ts"],"sourcesContent":["/**\n * @aihu/compiler native-addon loader — the in-process compile fast path.\n *\n * A `native.ts`-style loader in the mold of packages/server/src/native.ts:\n * platform matrix → per-platform optionalDependency package → dev fallback,\n * with explicit escape hatches and a cached three-state result. The addon\n * (packages/compiler/src-native, napi-rs) exposes\n * `compileEnvelope(source, optionsJson) → envelopeJson` — one boundary\n * crossing per file — which `js/envelope.ts` routes `transform()` /\n * `compileToAst()` / `compileRouteMeta()` through.\n *\n * States:\n * - loaded: addon required successfully; compiles run in-process.\n * - disabled: `AIHU_COMPILER_NATIVE=0` — the documented escape hatch.\n * - unavailable: no addon for this platform / load failed. UNLIKE\n * @aihu/server's fail-loud contract, a failed load here is a\n * one-shot WARNING, not a throw: the CLI spawn path is a\n * byte-identical fallback that always exists, so failing the\n * whole build over a fast-path load would be strictly worse.\n * The exception is `AIHU_COMPILER_NATIVE_ADDON=<path>` — an\n * explicit override that fails loud (same doctrine as\n * AIHU_COMPILE_BIN: a pinned path that doesn't load is a\n * configuration error, and silently ignoring it hands back a\n * plausible-looking wrong backend).\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport interface CompilerNativeAddon {\n compileEnvelope(source: string, optionsJson: string): string\n /**\n * The version string baked into the addon at build time. Present since\n * @aihu/compiler-native 0.1.x; ABSENT on older addons, which is why every\n * consumer must treat a missing method as \"unknown/incompatible\" rather\n * than assuming it is there (see envelope.ts's version handshake).\n */\n compilerVersion?(): string\n}\n\nexport interface NativePlatformDescriptor {\n readonly platformId: string\n readonly packageName: string\n readonly nodeFile: string\n}\n\nfunction detectPlatform(): NativePlatformDescriptor | null {\n if (typeof process === 'undefined' || !process.platform || !process.arch) {\n return null\n }\n const key = `${process.platform}-${process.arch}`\n switch (key) {\n case 'darwin-arm64':\n return {\n platformId: 'darwin-arm64',\n packageName: '@aihu/compiler-native-darwin-arm64',\n nodeFile: 'aihu-compiler-native.darwin-arm64.node',\n }\n case 'darwin-x64':\n return {\n platformId: 'darwin-x64',\n packageName: '@aihu/compiler-native-darwin-x64',\n nodeFile: 'aihu-compiler-native.darwin-x64.node',\n }\n case 'linux-x64':\n return {\n platformId: 'linux-x64-gnu',\n packageName: '@aihu/compiler-native-linux-x64-gnu',\n nodeFile: 'aihu-compiler-native.linux-x64-gnu.node',\n }\n case 'linux-arm64':\n return {\n platformId: 'linux-arm64-gnu',\n packageName: '@aihu/compiler-native-linux-arm64-gnu',\n nodeFile: 'aihu-compiler-native.linux-arm64-gnu.node',\n }\n case 'win32-x64':\n return {\n platformId: 'win32-x64-msvc',\n packageName: '@aihu/compiler-native-win32-x64-msvc',\n nodeFile: 'aihu-compiler-native.win32-x64-msvc.node',\n }\n default:\n return null\n }\n}\n\n/**\n * Where a loaded addon came from. This decides whether the version handshake\n * in envelope.ts can say anything meaningful about it:\n *\n * - `package` — a published per-platform optionalDependency. Its release\n * version is knowable (`packageVersion`) and is exactly the\n * thing `packages/compiler/package.json` pins, so it CAN be\n * compared against the pin.\n * - `dev-build` — `src-native/aihu-compiler-native.node`, produced by\n * `scripts/build-native.ts` from THIS source tree. It has no\n * release version at all; it is trusted by construction.\n * - `override` — `AIHU_COMPILER_NATIVE_ADDON=<path>`. Gated like `package`\n * when the pinned file turns out to live inside a published\n * platform package, trusted like `dev-build` otherwise.\n */\nexport type CompilerNativeOrigin = 'package' | 'dev-build' | 'override'\n\nexport type CompilerNativeState =\n | {\n kind: 'loaded'\n addon: CompilerNativeAddon\n addonPath: string\n origin: CompilerNativeOrigin\n /**\n * The `version` of the published per-platform package the addon was\n * loaded from, or `null` when the addon did not come from one (a local\n * cargo build). NOT the crate version reported by `compilerVersion()`:\n * `src-native/Cargo.toml` is pinned at 0.1.0 and never bumped per\n * release, so `CARGO_PKG_VERSION` cannot identify a release. The npm\n * package version can, and it is what the pin in\n * `packages/compiler/package.json` is expressed in.\n */\n packageVersion: string | null\n }\n | { kind: 'disabled' }\n | { kind: 'unavailable'; error?: Error }\n\nlet _state: CompilerNativeState | null = null\nlet _warnedLoadFailure = false\n\n/**\n * Read the `version` of the published platform package that owns `addonPath`.\n *\n * Deliberately NOT a walk up to the nearest package.json: the dev candidate\n * lives at `packages/compiler/src-native/…`, whose nearest ancestor manifest is\n * `@aihu/compiler` itself (version 1.2.2) — reading that would report a\n * confidently wrong \"addon version\". Only a manifest sitting in the SAME\n * directory as the `.node`, and naming an `@aihu/compiler-native-*` package,\n * counts.\n */\nfunction readAddonPackageVersion(addonPath: string): string | null {\n try {\n const manifestPath = join(dirname(addonPath), 'package.json')\n if (!existsSync(manifestPath)) return null\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as {\n name?: unknown\n version?: unknown\n }\n if (typeof manifest.name !== 'string' || !manifest.name.startsWith('@aihu/compiler-native-')) {\n return null\n }\n return typeof manifest.version === 'string' ? manifest.version : null\n } catch {\n return null\n }\n}\n\n/**\n * The per-platform addon package for the current platform, or `null` on a\n * platform with no prebuilt addon. Exported so envelope.ts can look the pinned\n * version up in `packages/compiler/package.json`'s optionalDependencies.\n */\nexport function nativePlatformDescriptor(): NativePlatformDescriptor | null {\n return detectPlatform()\n}\n\nfunction isAddonShaped(mod: unknown): mod is CompilerNativeAddon {\n return (\n typeof mod === 'object' &&\n mod !== null &&\n typeof (mod as CompilerNativeAddon).compileEnvelope === 'function'\n )\n}\n\n/**\n * Resolve (and cache) the native compiler addon. This is the ONLY module in\n * @aihu/compiler that requires a napi `.node` file.\n */\nexport function loadCompilerNative(): CompilerNativeState {\n if (_state !== null) return _state\n\n // Escape hatch — checked before everything else.\n if (typeof process !== 'undefined' && process.env?.AIHU_COMPILER_NATIVE === '0') {\n _state = { kind: 'disabled' }\n return _state\n }\n\n const requireFn = createRequire(import.meta.url)\n\n // Explicit addon path override — fails LOUD (a pinned path that does not\n // load is a configuration error, never a silent fallthrough).\n const override = process.env?.AIHU_COMPILER_NATIVE_ADDON\n if (override) {\n let addon: unknown\n try {\n addon = requireFn(override)\n } catch (err) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON is set to '${override}', ` +\n `which failed to load: ${(err as Error).message}`,\n )\n }\n if (!isAddonShaped(addon)) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON module at '${override}' ` +\n `does not export compileEnvelope()`,\n )\n }\n _state = {\n kind: 'loaded',\n addon,\n addonPath: override,\n origin: 'override',\n packageVersion: readAddonPackageVersion(override),\n }\n return _state\n }\n\n const descriptor = detectPlatform()\n if (descriptor === null) {\n _state = { kind: 'unavailable' }\n return _state\n }\n\n // 1. Per-platform optionalDependency package (the published-consumer path).\n let resolvedPath: string | null = null\n try {\n resolvedPath = requireFn.resolve(descriptor.packageName)\n } catch {\n // Package not installed — fall through to the dev candidates.\n }\n\n // 2. Dev fallbacks: the standalone src-native build. `aihu-compiler-native.node`\n // is staged by `bun packages/compiler/scripts/build-native.ts` (which runs\n // cargo and copies the platform cdylib); the target/release candidate\n // covers a manual copy. This module lives at js/ (source) or dist/\n // (bundled) — both one level below the package root.\n const devCandidates = [\n resolve(__dirname, '../src-native/aihu-compiler-native.node'),\n resolve(__dirname, '../src-native/target/release/aihu-compiler-native.node'),\n ]\n const candidates = resolvedPath ? [resolvedPath] : devCandidates.filter((c) => existsSync(c))\n const origin: CompilerNativeOrigin = resolvedPath ? 'package' : 'dev-build'\n\n for (const candidate of candidates) {\n try {\n const addon = requireFn(candidate)\n if (isAddonShaped(addon)) {\n _state = {\n kind: 'loaded',\n addon,\n addonPath: candidate,\n origin,\n packageVersion: readAddonPackageVersion(candidate),\n }\n return _state\n }\n throw new Error(`module at ${candidate} does not export compileEnvelope()`)\n } catch (err) {\n // Present-but-broken (ABI mismatch, corrupt download, placeholder file):\n // warn ONCE, loudly, then fall back to the spawn path — which is\n // byte-identical, just slower. `AIHU_COMPILER_NATIVE=0` silences this.\n if (!_warnedLoadFailure) {\n _warnedLoadFailure = true\n console.warn(\n `[@aihu/compiler] native addon found but failed to load; falling back to ` +\n `the aihu-compile spawn path (identical output, slower).\\n` +\n ` Candidate: ${candidate}\\n` +\n ` Error: ${(err as Error).message}\\n` +\n ` Reinstall @aihu/compiler (or rebuild: cargo build --release ` +\n `--manifest-path packages/compiler/src-native/Cargo.toml), or set ` +\n `AIHU_COMPILER_NATIVE=0 to silence this warning.`,\n )\n }\n _state = { kind: 'unavailable', error: err as Error }\n return _state\n }\n }\n\n _state = { kind: 'unavailable' }\n return _state\n}\n\n/** Returns the cached state kind (resolving if needed). @internal */\nexport function _getCompilerNativeStateKind(): CompilerNativeState['kind'] {\n return loadCompilerNative().kind\n}\n\n/** Reset the cached state. Used by tests that mock detection/env. @internal */\nexport function _resetCompilerNative(): void {\n _state = null\n _warnedLoadFailure = false\n}\n\n/**\n * Force the cached loader state — the injection seam for tests that need a\n * specific addon (right version / wrong version / no `compilerVersion` at all)\n * without a Rust build. `loadCompilerNative()` returns this verbatim until\n * `_resetCompilerNative()` clears it.\n * @internal\n */\nexport function _setCompilerNativeForTest(state: CompilerNativeState | null): void {\n _state = state\n _warnedLoadFailure = false\n}\n","/**\n * spawn-bounds.ts — the bounds every `aihu-compile` subprocess must carry.\n *\n * WHY THIS EXISTS (2026-08-07): two `aihu-compile --stdin` processes were found\n * still alive after 2 days 13 hours, and an `apps/docs` vite build sat 10\n * minutes at 0.0% CPU with a wedged `aihu-css-compile` child. The css-engine\n * side was reproduced under load and both sides sampled:\n *\n * child : read (libsystem_kernel) — parked in io::stdin().read_to_string(),\n * waiting for an EOF on stdin that never arrives.\n * parent : node::SyncProcessRunner::TryInitializeAndRunLoop -> uv_run ->\n * uv__io_poll -> kevent — parked in spawnSync's own private uv loop,\n * still holding that pipe's WRITE end open (`lsof -U` confirmed the\n * parent was the only holder, so this is not an fd-inheritance leak).\n *\n * The stall is on the parent side: spawnSync's loop never delivers the writable\n * event that would finish `input` and close the write end. Crucially, with no\n * timer armed `uv__io_poll` calls kevent with NO DEADLINE — which is exactly why\n * these processes wait for days rather than minutes. Passing `timeout` arms a uv\n * timer in that same loop, giving kevent a deadline, so the loop always wakes\n * and reaps the child. Verified in a stress harness: the run that hung\n * indefinitely without `timeout` was rescued with ETIMEDOUT once it was set.\n *\n * This is intermittent and load-dependent. It is NOT a pipe-buffer capacity\n * problem — 20 MB of stdin against 200 KB each of stdout+stderr round-trips\n * cleanly on both node and bun.\n *\n * See the matching note in `packages/css-engine/src/index.ts`.\n */\n\n/**\n * Wall-clock ceiling for one `aihu-compile` invocation — a measured floor plus\n * a payload-scaled term, NOT a round number.\n *\n * The floor. Measured on this machine: the largest SFC in `apps/docs`\n * (16 KB source -> 27.7 KB of AST JSON) round-trips through the binary in 4-5\n * ms, and 24 concurrent processes x 60 compiles each never exceeded 5 ms per\n * call. 120 s is ~24,000x the measured per-call cost. That is deliberately\n * absurd headroom: it has to absorb a loaded CI runner, a cold first exec\n * paying macOS code-signature validation, and a machine thrashing swap, because\n * a timeout that trips a legitimately slow build turns a rare hang into routine\n * CI flake — strictly worse than the bug. 120 s is also short enough that a\n * human watching a build notices, which is the entire point: today's hang\n * produced no output for 10 minutes, and two children survived 2.5 days.\n *\n * The scaled term. A flat bound is the wrong shape if some future payload is\n * enormous, so the ceiling also grows at 2 ms per KB of stdin — about 370x\n * slower than the measured 5.4 MB/s throughput. Below ~60 MB of stdin the\n * floor dominates (nothing in this repo comes within three orders of magnitude\n * of that), so in practice the bound IS 120 s today; the scaling only takes\n * over in the regime where a fixed bound could genuinely be too tight.\n */\nexport const COMPILE_TIMEOUT_FLOOR_MS = 120_000\n\n/** See COMPILE_TIMEOUT_FLOOR_MS — ~370x slower than measured throughput. */\nexport const COMPILE_TIMEOUT_MS_PER_KB = 2\n\nexport const COMPILE_MAX_BUFFER = 64 * 1024 * 1024\n\nexport function compileTimeoutMs(inputBytes = 0): number {\n const scaled = Math.ceil(inputBytes / 1024) * COMPILE_TIMEOUT_MS_PER_KB\n const raw = process.env.AIHU_COMPILE_TIMEOUT_MS\n if (raw !== undefined && raw !== '') {\n const n = Number(raw)\n // An explicit override replaces the FLOOR, not the scaling: someone raising\n // the bound for a huge payload should not accidentally lose the per-byte\n // allowance, and someone lowering it for a test should still get a bound.\n if (Number.isFinite(n) && n > 0) return Math.max(n, scaled)\n }\n return Math.max(COMPILE_TIMEOUT_FLOOR_MS, scaled)\n}\n\n/**\n * The bounds fragment to spread into every `execFileSync`/`spawnSync` call that\n * runs `aihu-compile`.\n *\n * `killSignal: 'SIGKILL'` because the whole point is that nothing survives: a\n * child already wedged in read() is exactly the process that was found alive\n * 2.5 days later, and a polite SIGTERM is not a guarantee.\n */\nexport function compileSpawnBounds(inputBytes = 0): {\n timeout: number\n maxBuffer: number\n killSignal: 'SIGKILL'\n} {\n return {\n timeout: compileTimeoutMs(inputBytes),\n maxBuffer: COMPILE_MAX_BUFFER,\n killSignal: 'SIGKILL',\n }\n}\n\n/**\n * Rewrite a spawn failure into something a human can act on. Node's own\n * ETIMEDOUT/ENOBUFS errors name neither the binary nor the payload, so an\n * unannotated one reads as `spawnSync ... ETIMEDOUT` and tells the reader\n * nothing about which compile died or what to do next.\n *\n * Returns `null` when the error is an ordinary non-zero exit (a real compile\n * error), so callers keep their existing stderr-forwarding behavior.\n */\nexport function describeSpawnFailure(\n err: unknown,\n bin: string,\n args: string[],\n inputBytes: number,\n elapsedMs: number,\n): Error | null {\n const e = err as { code?: string }\n const where =\n ` binary: ${bin}\\n` +\n ` args: ${args.length > 0 ? args.join(' ') : '(none)'}\\n` +\n ` stdin: ${inputBytes} bytes\\n` +\n ` elapsed: ${elapsedMs} ms`\n\n if (e.code === 'ETIMEDOUT') {\n const ms = compileTimeoutMs(inputBytes)\n return new Error(\n `[@aihu/compiler] aihu-compile TIMED OUT after ${ms} ms and the child was killed.\\n\\n` +\n `${where}\\n\\n` +\n ` This is the known spawn stall, not a slow compile: the compiler normally\\n` +\n ` finishes in single-digit milliseconds. The child parks in read() waiting for\\n` +\n ` an EOF on stdin that the parent's spawnSync loop never delivers, so without\\n` +\n ` this timeout the build would hang at 0% CPU indefinitely (two such children\\n` +\n ` were once found still running after 2.5 days).\\n\\n` +\n ` What to do next:\\n` +\n ` - Re-run the build. The stall is intermittent and load-dependent; a retry\\n` +\n ` normally succeeds.\\n` +\n ` - If it reproduces every time, check the binary directly: ${bin} --help\\n` +\n ` and rebuild it: cargo build --release -p aihu-compiler\\n` +\n ` - If a payload genuinely needs longer than ${ms} ms, raise the bound with\\n` +\n ` AIHU_COMPILE_TIMEOUT_MS=<milliseconds>. Do not remove it.`,\n )\n }\n\n if (e.code === 'ENOBUFS') {\n return new Error(\n `[@aihu/compiler] aihu-compile produced more than the ${COMPILE_MAX_BUFFER} byte\\n` +\n ` stdout/stderr limit and the child was killed.\\n\\n` +\n `${where}\\n\\n` +\n ` An emit this large almost certainly means the input is wrong rather than a\\n` +\n ` real component. Check what is being passed in before raising the cap.`,\n )\n }\n\n return null\n}\n","/**\n * envelope.ts — backend dispatch for the single-parse envelope compile.\n *\n * Every compile request (`transform` / `compileToAst` / `compileRouteMeta`)\n * routes memo-first, then through ONE of three backends, in order:\n *\n * 1. **native addon** (packages/compiler/src-native, napi) — in-process,\n * zero spawn. Selected once per process by `_resolveCompileBackend()`.\n * 2. **envelope CLI spawn** — the legacy `aihu-compile` spawn args PLUS\n * `--envelope <options-json>`. A binary that knows the flag answers with\n * one JSON envelope (single parse, every requested artifact); the JSON\n * carries the `\"envelope\": 1` discriminant.\n * 3. **legacy per-output spawn** — an OLDER binary ignores `--envelope`\n * and answers with its normal single artifact (JS / AST JSON / route\n * JSON), which the discriminant check detects. That output is used\n * as-is, so stale binaries keep exactly their historical behavior —\n * feature detection costs zero extra spawns.\n *\n * The native addon must additionally PASS A VERSION HANDSHAKE before it is\n * selected (`_checkNativeAddonVersion`): its release version has to equal the\n * pin in `packages/compiler/package.json`'s optionalDependencies. It is a\n * published artifact and the CLI binary is not, so on any branch that changes\n * Rust the addon is stale by construction and would quietly emit pre-change\n * output. A mismatch warns once and falls back to spawn — except under an\n * explicit `AIHU_COMPILER_NATIVE_ADDON` pin, which throws.\n *\n * Backend selection is CACHED at first use (per module instance):\n * `AIHU_COMPILER_NATIVE=0` forces spawn; an explicit `AIHU_COMPILE_BIN`\n * binary pin forces spawn (working ON the compiler means\n * the pinned binary must actually run — an addon silently shadowing it would\n * reintroduce the exact quiet-wrong-answer failure the pin exists to prevent).\n * The cache means css-engine's mid-build `AIHU_COMPILE_BIN` handshake\n * (which programmatically sets the var AFTER the first transform) cannot\n * de-select an already-active native backend.\n *\n * The memo's \"binary stamp\" (transform-memo.ts `_binStamp`) generalizes to\n * whichever backend is active: the addon's `.node` file path (stat'd for\n * mtime+size, so a rebuilt addon invalidates entries) when native, the\n * resolved CLI binary path when spawning.\n */\n\nimport { execFileSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { type CompilerNativeState, loadCompilerNative, nativePlatformDescriptor } from './native.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\nimport { compileSpawnBounds, describeSpawnFailure } from './spawn-bounds.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The wire shape of a compile envelope (Rust `Envelope`, camelCase). */\nexport interface CompileEnvelope {\n envelope: number\n targets: Record<string, { js?: string; manifest?: string }>\n astJson?: string\n routeJson?: string\n diagnostics: unknown[]\n}\n\n/** Options forwarded to the Rust envelope API (Rust `EnvelopeOptions`). */\nexport interface CompileEnvelopeOptions {\n tag?: string\n path?: string\n targets?: string[]\n emits?: Array<'js' | 'ast' | 'route' | 'manifest'>\n strictTemplates?: boolean\n exprParser?: string\n}\n\nexport type CompileBackend =\n | {\n kind: 'native'\n compileEnvelope: (source: string, optionsJson: string) => string\n stampPath: string\n }\n | { kind: 'spawn' }\n\nlet _backend: CompileBackend | null = null\n\n/**\n * The addon version this source tree requires — `packages/compiler/package.json`'s\n * optionalDependencies pin for the current platform's addon package.\n *\n * That pin is bumped in the SAME commit that changes the Rust, so it is the\n * only in-repo statement of \"which addon build this JS expects\". This module\n * builds to `dist/envelope.js` and lives at `js/envelope.ts` in source — the\n * manifest is one level up either way.\n * @internal\n */\nexport function _requiredNativeAddonVersion(): string | null {\n const descriptor = nativePlatformDescriptor()\n if (descriptor === null) return null\n try {\n const manifest = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8')) as {\n optionalDependencies?: Record<string, string>\n }\n const pinned = manifest.optionalDependencies?.[descriptor.packageName]\n return typeof pinned === 'string' ? pinned : null\n } catch {\n return null\n }\n}\n\n/** The verdict of the addon⇄source version handshake. @internal */\nexport type NativeVersionVerdict =\n | { ok: true }\n | { ok: false; reason: 'missing-method' | 'version-mismatch'; actual: string; expected: string }\n\n/**\n * The backend version handshake (§19).\n *\n * ## Why this exists\n *\n * `_resolveCompileBackend()` prefers an in-process addon over the workspace CLI\n * binary. The addon is a PUBLISHED artifact; the CLI binary is built from this\n * source tree. So on any branch that changes Rust, the installed addon is stale\n * BY CONSTRUCTION — the pin it would need names a version that does not exist on\n * npm yet, `bun install` cannot fetch it, and the compile silently produces\n * pre-change output. That is a quiet wrong answer, the worst failure mode a\n * compiler has: it has already produced a \"could not reproduce\" that was nothing\n * but a stale backend.\n *\n * ## What is compared\n *\n * The addon's RELEASE version (its npm package version) against the pin. NOT the\n * string from `compilerVersion()`: that interpolates `CARGO_PKG_VERSION` from\n * `src-native/Cargo.toml`, which has read `0.1.0` since the addon landed and is\n * never bumped per release — every published addon, current or stale, reports\n * `0.1.0`, so it carries no release identity. `compilerVersion()` IS called: its\n * presence is the capability probe (a missing method means an addon old enough\n * to predate the handshake, which is a mismatch by definition), and what it\n * reports goes into the warning as a diagnostic, which is precisely the role its\n * own Rust doc comment assigns it.\n *\n * A locally built addon (`origin: 'dev-build'`, no platform-package manifest\n * beside it) has no release version and is NOT gated: it was compiled from this\n * very tree, which is the property the gate exists to establish.\n * @internal\n */\nexport function _checkNativeAddonVersion(\n state: Extract<CompilerNativeState, { kind: 'loaded' }>,\n): NativeVersionVerdict {\n const expected = _requiredNativeAddonVersion()\n // No pin (unsupported platform / unreadable manifest): nothing to check\n // against. Never block a working addon on a missing expectation.\n if (expected === null) return { ok: true }\n\n if (typeof state.addon.compilerVersion !== 'function') {\n return { ok: false, reason: 'missing-method', actual: '<no compilerVersion()>', expected }\n }\n\n let reported: string\n try {\n reported = String(state.addon.compilerVersion())\n } catch (err) {\n return {\n ok: false,\n reason: 'missing-method',\n actual: `<compilerVersion() threw: ${(err as Error).message}>`,\n expected,\n }\n }\n\n // Built from this source tree — no release version, nothing stale possible.\n if (state.packageVersion === null) return { ok: true }\n\n if (state.packageVersion !== expected) {\n return {\n ok: false,\n reason: 'version-mismatch',\n actual: `${state.packageVersion} (reports: ${reported})`,\n expected,\n }\n }\n return { ok: true }\n}\n\nfunction nativeMismatchMessage(\n state: Extract<CompilerNativeState, { kind: 'loaded' }>,\n verdict: Extract<NativeVersionVerdict, { ok: false }>,\n): string {\n const cause =\n verdict.reason === 'missing-method'\n ? `the addon does not implement compilerVersion(), so it predates this handshake`\n : `the installed addon is not the build this source requires`\n return (\n `[@aihu/compiler] native addon version mismatch — ${cause}.\\n` +\n ` Required (packages/compiler/package.json pin): ${verdict.expected}\\n` +\n ` Loaded addon: ${verdict.actual}\\n` +\n ` Addon path: ${state.addonPath}\\n` +\n ` Cause: the addon is a PUBLISHED artifact, so a branch that changes Rust\\n` +\n ` is stale by construction — the pinned version is not on npm yet and\\n` +\n ` \\`bun install\\` cannot fix it. Using it would silently compile with\\n` +\n ` pre-change codegen.`\n )\n}\n\n/**\n * Resolve (once) which backend serves compiles for this process.\n * @internal\n */\nexport function _resolveCompileBackend(): CompileBackend {\n if (_backend !== null) return _backend\n const env = typeof process !== 'undefined' ? process.env : undefined\n if (env?.AIHU_COMPILER_NATIVE === '0' || env?.AIHU_COMPILE_BIN) {\n _backend = { kind: 'spawn' }\n return _backend\n }\n const native = loadCompilerNative()\n if (native.kind !== 'loaded') {\n _backend = { kind: 'spawn' }\n return _backend\n }\n\n const verdict = _checkNativeAddonVersion(native)\n if (!verdict.ok) {\n // An EXPLICIT addon pin that mismatches is a configuration error, not a\n // fallback opportunity — same doctrine as native.ts's override load\n // failure and AIHU_COMPILE_BIN: silently ignoring a pin hands back a\n // plausible-looking wrong backend.\n if (native.origin === 'override') {\n throw new Error(\n `${nativeMismatchMessage(native, verdict)}\\n` +\n ` AIHU_COMPILER_NATIVE_ADDON pinned this addon explicitly, so this fails\\n` +\n ` rather than falling back. Unset it (the CLI spawn path is byte-identical),\\n` +\n ` set AIHU_COMPILER_NATIVE=0, or rebuild:\\n` +\n ` bun packages/compiler/scripts/build-native.ts`,\n )\n }\n console.warn(\n `${nativeMismatchMessage(native, verdict)}\\n` +\n ` Falling back to the aihu-compile spawn path (built from source,\\n` +\n ` byte-identical output, slower). Set AIHU_COMPILER_NATIVE=0 to silence\\n` +\n ` this, or build the addon from source:\\n` +\n ` bun packages/compiler/scripts/build-native.ts`,\n )\n _backend = { kind: 'spawn' }\n return _backend\n }\n\n _backend = {\n kind: 'native',\n compileEnvelope: native.addon.compileEnvelope.bind(native.addon),\n stampPath: native.addonPath,\n }\n return _backend\n}\n\n/** Reset the cached backend (tests). @internal */\nexport function _resetCompileBackend(): void {\n _backend = null\n}\n\n/**\n * The identity string the memo cache stamps entries with — the active\n * backend's file (addon `.node` path, or the resolved CLI binary path).\n * `_binStamp` stats it, so rebuilding EITHER backend invalidates entries.\n * @internal\n */\nexport function _backendStampPath(): string {\n const backend = _resolveCompileBackend()\n return backend.kind === 'native' ? backend.stampPath : resolveSpawnBinPath()\n}\n\n/**\n * CLI binary resolution for the spawn backend — env override first (the\n * css-engine handshake sets AIHU_COMPILE_BIN), then the shared resolver.\n * Call-time, never cached here (Bug 6 doctrine: the env var may be set\n * between calls).\n * @internal\n */\nexport function resolveSpawnBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n/** A backend reply: either a parsed envelope, or a legacy single artifact. */\nexport type EnvelopeReply =\n | { kind: 'envelope'; envelope: CompileEnvelope }\n | { kind: 'legacy'; output: string }\n\nfunction parseEnvelopeReply(stdout: string): EnvelopeReply {\n // Envelope replies are a single JSON object carrying the `\"envelope\"`\n // discriminant. EVERY legacy output fails this test: emitted JS is not\n // JSON (starts with a comment or import), an AST export carries\n // `astVersion` but not `envelope`, a route sidecar is a plain object (or\n // the literal `null`) without it.\n const trimmed = stdout.trim()\n if (trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed) as { envelope?: unknown }\n if (typeof parsed === 'object' && parsed !== null && parsed.envelope === 1) {\n return { kind: 'envelope', envelope: parsed as unknown as CompileEnvelope }\n }\n } catch {\n // Not JSON — legacy output.\n }\n }\n return { kind: 'legacy', output: stdout }\n}\n\n/**\n * Run one compile through the active backend.\n *\n * @param source the `.aihu` source (stdin for the spawn backend)\n * @param legacyArgs the EXACT argv the pre-envelope spawn used for this call\n * (`--stdin --tag … [--ast-json|--route-json|…]`). The spawn\n * backend appends `--envelope <json>` to it, so an older\n * binary that ignores the flag still answers the legacy\n * request correctly.\n * @param options the envelope options (must agree with `legacyArgs`)\n * @internal\n */\nexport function _compileViaBackend(\n source: string,\n legacyArgs: string[],\n options: CompileEnvelopeOptions,\n): EnvelopeReply {\n const backend = _resolveCompileBackend()\n const optionsJson = JSON.stringify(options)\n if (backend.kind === 'native') {\n const envelope = JSON.parse(backend.compileEnvelope(source, optionsJson)) as CompileEnvelope\n return { kind: 'envelope', envelope }\n }\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveSpawnBinPath()\n const spawnArgs = [...legacyArgs, '--envelope', optionsJson]\n const startedAt = Date.now()\n let stdout: string\n try {\n stdout = execFileSync(bin, spawnArgs, {\n input: source,\n encoding: 'utf8',\n ...compileSpawnBounds(source.length),\n })\n } catch (err) {\n throw describeSpawnFailure(err, bin, spawnArgs, source.length, Date.now() - startedAt) ?? err\n }\n return parseEnvelopeReply(stdout)\n}\n","/**\n * transform-memo.ts — content-addressed memo cache for `aihu-compile` spawns.\n *\n * Every `.aihu` compile is a subprocess spawn (~6ms each, ~63% pure spawn\n * overhead), and the SSG prerender pass re-compiles every file a SECOND time:\n * `prerenderClose` (packages/app/src/prerender.ts) boots a second Vite server\n * that REUSES the already-resolved plugin instances and re-runs the same\n * transforms via `ssrLoadModule` — identical source, identical flags,\n * byte-identical output recompiled. css-engine's `compileSfc` additionally\n * re-spawns the compiler per file for its AST pass (`compileToAst`).\n *\n * This module memoises the RAW STDOUT of a spawn, keyed by a SHA-256 digest of\n * everything that determines that stdout:\n *\n * kind (transform | ast | route) + file id + options fingerprint\n * + binary identity (path + mtime + size) + source content\n *\n * Because the key is content-addressed, watch-mode correctness is free: an\n * edit changes the source hash, so a stale entry is simply never looked up\n * again. The binary stamp (mtime+size) additionally invalidates entries when\n * the compiler binary itself is rebuilt mid-session (dev-workspace cargo\n * rebuilds); the stat is ~µs against a ~6ms spawn.\n *\n * Growth is bounded by FIFO eviction at `MAX_ENTRIES` — inert stale entries\n * from long dev sessions age out. Deliberately NOT cleared on Vite\n * `buildStart`: the SSG prerender's second server fires its own start-of-run\n * hooks, and a clear there would defeat the exact pass-two hits this cache\n * exists for.\n *\n * All exports are `_`-prefixed internals of `@aihu/compiler`; consumers go\n * through `transform()` / `compileToAst()` / `compileRouteMeta()`.\n */\nimport { createHash } from 'node:crypto'\nimport { statSync } from 'node:fs'\n\n/**\n * FIFO size bound. 1024 entries × a few KB of compiled output ≈ a few MB —\n * comfortably above any real app's file count (one entry per file × kind ×\n * options variant) while keeping unbounded-session growth impossible.\n * @internal\n */\nexport const _MEMO_MAX_ENTRIES = 1024\n\nconst cache = new Map<string, string>()\nlet hits = 0\nlet misses = 0\nlet seeds = 0\n\n/**\n * Identity stamp for the compiler binary: path + mtime + size when statable,\n * path alone otherwise (e.g. tests pointing AIHU_COMPILE_BIN at a fake).\n * A rebuilt-in-place binary changes mtime/size → old entries become inert.\n * @internal\n */\nfunction _binStamp(binPath: string): string {\n try {\n const st = statSync(binPath)\n return `${binPath}:${st.mtimeMs}:${st.size}`\n } catch {\n return binPath\n }\n}\n\n/** @internal */\nexport function _memoKey(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n): string {\n return createHash('sha256')\n .update(kind)\n .update('\\0')\n .update(id)\n .update('\\0')\n .update(optionsFingerprint)\n .update('\\0')\n .update(_binStamp(binPath))\n .update('\\0')\n .update(source)\n .digest('hex')\n}\n\n/**\n * Memoised spawn: returns the cached stdout for an identical\n * (kind, source, id, options, binary) tuple, otherwise runs `spawn()` and\n * caches its result. The cached value is the raw stdout STRING — callers that\n * return structured data (`compileToAst`, `compileRouteMeta`) re-parse per\n * call so a mutated result object can never poison the cache.\n * @internal\n */\nexport function _memoizedSpawn(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n spawn: () => string,\n): string {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n const hit = cache.get(key)\n if (hit !== undefined) {\n hits++\n return hit\n }\n const out = spawn()\n misses++\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n // FIFO: Map preserves insertion order; drop the oldest entry.\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, out)\n return out\n}\n\n/**\n * Seed a memo entry WITHOUT a spawn — the envelope path's sibling-artifact\n * write. One envelope compile of a file yields js + ast + route from a single\n * parse; the js answers the current call, and the ast/route strings are\n * seeded here under the exact keys `compileToAst` / `compileRouteMeta` will\n * look up, so their later calls for the same source are pure cache hits.\n * Never overwrites an existing entry; counts in the `seeds` stat, not\n * hits/misses.\n * @internal\n */\nexport function _seedMemo(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n value: string,\n): void {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n if (cache.has(key)) return\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, value)\n seeds++\n}\n\n/** Test/diagnostic hook — wipe the memo and its counters. @internal */\nexport function _clearTransformMemo(): void {\n cache.clear()\n hits = 0\n misses = 0\n seeds = 0\n}\n\n/** Test/diagnostic hook — current memo size + hit/miss/seed counters. @internal */\nexport function _transformMemoStats(): {\n size: number\n hits: number\n misses: number\n seeds: number\n} {\n return { size: cache.size, hits, misses, seeds }\n}\n","/**\n * @aihu/compiler — TypeScript wrapper around the aihu-compile Rust binary.\n *\n * Exports:\n * transform(source, id) — compile a single .aihu file to TypeScript\n * aihuCompilerPlugin() — Vite plugin that wires transform() into the build\n */\nimport { execFileSync } from 'node:child_process'\nimport { createRequire } from 'node:module'\nimport { basename, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { _backendStampPath, _compileViaBackend } from './envelope.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\nimport { compileSpawnBounds, describeSpawnFailure } from './spawn-bounds.ts'\nimport { _memoizedSpawn, _seedMemo } from './transform-memo.ts'\n\nexport type { CompileEnvelope, CompileEnvelopeOptions } from './envelope.ts'\n// Perf — in-process napi compile backend + single-parse envelope (see\n// js/envelope.ts and js/native.ts). `transform()` / `compileToAst()` /\n// `compileRouteMeta()` route memo → native addon → envelope CLI spawn →\n// legacy per-output spawn. Re-exported as internals for tests/diagnostics.\nexport {\n _compileViaBackend,\n _resetCompileBackend,\n _resolveCompileBackend,\n} from './envelope.ts'\nexport { _getCompilerNativeStateKind, _resetCompilerNative, loadCompilerNative } from './native.ts'\nexport { resolveCompilerBinary } from './resolve-binary.ts'\n// Perf — content-addressed memo over the compile spawns (see transform-memo.ts).\n// The SSG prerender re-runs every transform against a second Vite server with\n// identical inputs; the memo turns that whole second pass (and css-engine's\n// per-file `compileToAst` re-parse) into cache lookups instead of subprocess\n// spawns. Re-exported as internals so tests can reset/observe the cache.\nexport {\n _clearTransformMemo,\n _MEMO_MAX_ENTRIES,\n _transformMemoStats,\n} from './transform-memo.ts'\n\n// Binary resolution: env var override, then the per-platform optionalDependency\n// package (`@aihu/compiler-<platform>`) with a workspace `target/` dev fallback —\n// see js/resolve-binary.ts (a clone of css-engine's resolver). The published\n// @aihu/compiler tarball ships only the JS shim (bin/aihu-compile.mjs); the\n// native binary arrives via the optionalDependency packages, so there is no\n// `../bin/aihu-compile` relative path anymore.\n//\n// Bug 6 fix — resolveBinPath() is CALL-TIME, not module-load-time. The Vite\n// plugin's `_maybeCompileUtilityCss` sets `process.env.AIHU_COMPILE_BIN` so\n// that css-engine's bundled copy of `compileToAst` spawns THIS compiler's\n// binary. Prior to this fix `binPath` was a module-scope const captured at\n// import time, so the env-var assignment was always too late and `compileSfc`\n// failed with ENOENT. Re-reading on every call is essentially free (an env\n// lookup, then a memoized resolve) and makes the AIHU_COMPILE_BIN handshake\n// actually work.\nfunction resolveBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n/*\n * ── Regex hardening: js/polynomial-redos (CWE-1333) ─────────────────────────\n *\n * Most of the regexes in this file run against COMPILED MODULE TEXT, and that\n * text carries authored `.aihu` bytes verbatim: a template text node becomes\n * `leaf('<text>')` (codegen/template_emit.rs) and an `@style` body becomes\n * ``__style__.replaceSync(`<css>`)`` (codegen/emit.rs). So the strings these\n * patterns scan ARE attacker-controllable by whoever authors the `.aihu` file\n * — an untrusted PR in a monorepo, a template shipped to other developers.\n * Treat them as untrusted input, not as our own generated output.\n *\n * Three ambiguity shapes were live in this file (17 CodeQL alerts), all\n * polynomial rather than exponential — the cost comes from re-scanning, not\n * from nested quantifiers:\n *\n * A. Named-import matchers, `import\\s*\\{[^}]*\\}\\s*from '<mod>'`.\n * `[^}]` does not exclude `{`, so every one of the N `import{` offsets in\n * the subject restarts a scan that runs to the END of the string before\n * failing → O(n²). Fixed by narrowing the specifier-list class to\n * `[^{}]`, which bounds each scan to the next brace. An ES import\n * specifier list can never contain `{`, so no legitimate input changes\n * meaning — the class is strictly more correct as well as safer.\n * (Measured: 224 KB of `import{` took 2.45 s before, 0.7 ms after.)\n *\n * B. Literal prefix + lazy any-char scan, e.g.\n * ``/(__style__\\.replaceSync\\(`)[^]*?(`\\);)/``. Repeating the prefix\n * gives N start offsets, each running a fresh O(n) lazy scan → O(n²).\n * A regex cannot express \"first prefix, then first terminator\" without\n * that re-scan, so these are restructured into literal `indexOf` scans\n * (`_replaceDelimitedBody`, `_passivizeOutlet`, `_hasBaseRecipe`). The\n * rewrite is exactly equivalent: `String.replace` takes the LEFTMOST\n * match, and if no terminator follows the first prefix then none follows\n * any later prefix either — so \"first prefix + first terminator after it\"\n * is the same span the regex produced.\n *\n * C. Greedy `.*` between two literals (`/import.*from\\s*'@aihu\\/signals'/`).\n * Same re-scan blowup, one start offset per `import` on the line. Fixed\n * by anchoring to a line start (`^` + `m`), which caps the offsets at one\n * per line and makes the total linear.\n *\n * Adjacent unbounded `\\s*` runs (`\\s*;?\\s*$`) are a second, independent pump:\n * the two runs can split a whitespace tail O(n) ways when `$` never holds.\n * Rewritten as `(?:\\s*;)?\\s*$`, which matches exactly the same spans with an\n * unambiguous decomposition. And `^\\s*` under the `m` flag is a third: `\\s`\n * matches `\\n`, so every line start can scan the whole remaining file →\n * narrowed to `[^\\S\\r\\n]*` (horizontal indentation), which is what \"the import\n * line\" actually means.\n *\n * `packages/compiler/tests/regex-redos.test.ts` pins both halves: old-vs-new\n * output equality on every real compiled shape, and a wall-clock budget on the\n * adversarial inputs.\n */\n\n// Minimal VitePlugin interface — avoids importing from 'vite' at compile time.\n// Structurally compatible with Vite's Plugin type.\ninterface VitePlugin {\n readonly name: string\n enforce?: 'pre' | 'post'\n resolveId?: (\n source: string,\n importer?: string,\n ) => string | null | undefined | Promise<string | null | undefined>\n load?: (id: string) => string | null | undefined | Promise<string | null | undefined>\n transform?: (\n code: string,\n id: string,\n ) => Promise<{ code: string; map: null }> | { code: string; map: null } | null | undefined\n /** GX Phase 1 (#437-GX) — end-of-build hook; prints the extract census. */\n buildEnd?: (error?: Error) => void | Promise<void>\n}\n\n/**\n * Options for `aihuCompilerPlugin()` (Plan 3.3 — Islands).\n */\nexport interface AihuCompilerPluginOptions {\n /**\n * When `true` (default), components the compiler classified as `'static'`\n * (read from the `// @aihu:island` marker via `_parseIslandMarker()`) are\n * emitted with a minimal HTML-only registration shim that ships **zero**\n * `@aihu/runtime` and `@aihu/signals` JS to the browser. Components\n * classified as `'interactive'` retain the full runtime path.\n *\n * Setting `islands: false` opts every component back into the unified\n * runtime path (Plan 3.2 baseline behaviour).\n */\n islands?: boolean\n\n /**\n * Project-wide rendering mode applied to every `.aihu` SFC compiled\n * by this plugin instance. When set, the plugin post-processes the\n * compiled JS to inject `, { shadowMode: '<mode>' }` as the third arg\n * to the emitted `defineElement(tag, defineComponent(...))` call.\n *\n * BINARY vocabulary (DA4 #437):\n * - `'shadow'` — shadow DOM (`attachShadow({ mode: 'open' })` internally;\n * open is the only browser mode aihu's composition/hydration\n * can use). `this.shadowRoot` is the non-null root.\n * - `'light'` — **no shadow root.** The component mounts into its own\n * element (`this.shadowRoot === null`). Required for global\n * utility-class CSS frameworks like Tailwind, UnoCSS, Pico\n * that rely on the cascade.\n *\n * Per-file override: the `$shadow: 'light' | 'shadow'` macro outranks this\n * config. Unset, pages/layouts default to `'light'` and leaves to\n * `'shadow'`.\n */\n shadowMode?: 'light' | 'shadow'\n\n /**\n * Build target threaded to the compiler binary (`--target`). Defaults to the\n * compiler's `universal` target (current behaviour). Set to `'client'` for a\n * browser bundle that must NOT ship the server `__agentBinding` (policy) and\n * instead gets the policy-free `@agent` opaque-ID dispatcher + the per-instance\n * `_registerAgentDispatcher` wiring the capability bridge reads after mount.\n * See `examples/agent-driven-demo`.\n */\n target?: 'client' | 'server' | 'universal'\n\n /**\n * Directory (relative to the project root) holding layout SFCs. Default:\n * `'src/layouts'`. Files under this directory are compiled in **layout mode**:\n * their custom element is registered under the namespaced tag\n * `aihu-layout-<stem>` (a layout stem like `app` is not a valid custom-element\n * name on its own), and their `<outlet>` lowers to a **passive**\n * `data-aihu-outlet` marker rather than the reactive route-driven boundary —\n * because `@aihu/app`'s client renderer fills the marker imperatively and the\n * reactive boundary would otherwise clear it on mount.\n *\n * Kept in sync with `@aihu/router`'s `layoutTagFor()` (`virtual:aihu-layouts`).\n */\n layoutsDir?: string\n}\n\n/**\n * Find the `)` matching the `(` at `open`, skipping string literals\n * (`'…'`/`\"…\"` with `\\` escapes), template literals (including nested\n * `${ … }` interpolations, tracked with a frame stack), and `//`/`/* */`\n * comments. Returns -1 when no match is found before end of input.\n *\n * Why a lexer and not a bare paren count: on the client/universal targets the\n * ENTIRE setup body is inlined inside `defineComponent((ctx) => { … })`, and\n * that body carries user template text as string literals — `leaf('(')` for a\n * `(` text node, `{ title: '(unclosed' }` for an attribute — so a count that\n * reads parens inside strings drifts and the caller silently bails, costing\n * the component its `shadowMode`/`lightScopeId` injection entirely.\n *\n * Known miss, accepted: a regex literal in user `@state` code containing an\n * unbalanced paren (`/\\(/`) reads as division + parens. The caller validates\n * the landing site and bails to a no-op rather than corrupting.\n */\nfunction _matchParen(code: string, open: number): number {\n // Frames: 'code' (with its own `{}` depth, so a `}` inside a template\n // interpolation knows whether it closes the interpolation) or 'tpl'.\n const frames: Array<{ kind: 'code'; brace: number } | { kind: 'tpl' }> = [\n { kind: 'code', brace: 0 },\n ]\n let paren = 0\n for (let i = open; i < code.length; i++) {\n const top = frames[frames.length - 1]\n // Unreachable: the root frame never pops (a code-frame `}` pops only when\n // `frames.length > 1`, a tpl frame pops only itself) — checker appeasement.\n if (top === undefined) return -1\n const c = code[i]\n if (top.kind === 'tpl') {\n if (c === '\\\\') i++\n else if (c === '`') frames.pop()\n else if (c === '$' && code[i + 1] === '{') {\n frames.push({ kind: 'code', brace: 0 })\n i++\n }\n continue\n }\n if (c === \"'\" || c === '\"') {\n i++\n while (i < code.length && code[i] !== c) {\n if (code[i] === '\\\\') i++\n i++\n }\n } else if (c === '`') {\n frames.push({ kind: 'tpl' })\n } else if (c === '/' && code[i + 1] === '/') {\n while (i < code.length && code[i] !== '\\n') i++\n } else if (c === '/' && code[i + 1] === '*') {\n i += 2\n while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) i++\n i++\n } else if (c === '(') {\n paren++\n } else if (c === ')') {\n paren--\n if (paren === 0) return i\n } else if (c === '{') {\n top.brace++\n } else if (c === '}') {\n if (top.brace === 0 && frames.length > 1) frames.pop()\n else top.brace--\n }\n }\n return -1\n}\n\n/**\n * Inject `shadowMode: '...'` (and, for light mode, `lightScopeId: '...'` in\n * the SAME options object) into the third argument of the emitted\n * `defineElement('tag', defineComponent(...))` call — appending the options\n * object when the call has two arguments, or merging the fields into an\n * existing third argument (`$form` emits `, { formAssociated: true }`).\n * Idempotent — leaves code untouched when the call is not in a recognized\n * shape or the options already carry a `shadowMode`.\n *\n * `lightScopeId` is folded into this SAME injection rather than a second\n * independent pass, deliberately: it is only ever set when `mode === 'light'`\n * (see the call site, `index.ts`'s `transform` hook), so every call that\n * needs it ALSO needs a shadowMode injection at the exact same spot — a\n * second pass would just re-match (and fight) the text this one already\n * rewrote. Stamps `data-a` on the root element at runtime (light-DOM leaf\n * flip, LDF §10 step 3) — `packages/runtime/src/define-element.ts`'s\n * `wrapClass` reads `options.lightScopeId`.\n *\n * History, because this anchor has now been wrong twice:\n *\n * 1. The original regex anchor (`defineComponent\\([^]*\\)\\s*\\)` — greedy, and\n * `[^]` crosses newlines) ran past `defineElement` and matched the LAST\n * `)\\s*)` pair in the module. On a SERVER-target module the string\n * renderer (`__ssrString`) is emitted AFTER the registration, so for any\n * component with an `if=` the last such pair is the emitted condition,\n * which became `if ((n() > 5), { shadowMode: 'light', … })` — the comma\n * operator, always truthy, so a dead branch rendered (the SSR-child\n * review's 33-nested-hosts measurement). Same corruption landed inside\n * `__aihu_stext(...)` calls (a `(` in text content) and, on ALL targets,\n * inside `$form`'s `setFormValue(...)`. The repro was missed at first\n * because the probe regex `/if \\([^)]*shadowMode/` cannot cross the `)`\n * in `(n() > 5)` — evidence in light-scope-export.test.ts.\n *\n * 2. The first replacement — a bare balanced-paren count — read parens inside\n * string literals (`leaf('(')`), drifted, and silently bailed on\n * client/universal modules, stripping the injection those components need.\n * Hence `_matchParen`'s lexer, and the tests that pin every shape above.\n *\n * @internal\n */\nexport function _injectShadowMode(\n code: string,\n mode: 'light' | 'shadow',\n lightScopeId?: string,\n): string {\n const head = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/.exec(code)\n if (head == null) return code\n // Index of the `(` that opens defineComponent's argument list.\n const open = head.index + head[0].length - 1\n const close = _matchParen(code, open)\n if (close === -1) return code\n const fields = `shadowMode: '${mode}'${lightScopeId ? `, lightScopeId: '${lightScopeId}'` : ''}`\n const rest = code.slice(close + 1)\n // Two-argument call: `defineComponent(...)` followed directly by\n // defineElement's own `)`. Append the options object.\n if (/^\\s*\\)/.test(rest)) {\n return `${code.slice(0, close + 1)}, { ${fields} }${rest}`\n }\n // Existing third argument (`$form`'s `, { formAssociated: true }`): merge\n // the fields into it — unless a shadowMode is already present (idempotency).\n const existing = /^\\s*,\\s*\\{/.exec(rest)\n if (existing && !/^\\s*,\\s*\\{[^}]*\\bshadowMode\\b/.test(rest)) {\n const braceEnd = close + 1 + existing[0].length\n return `${code.slice(0, braceEnd)} ${fields},${code.slice(braceEnd)}`\n }\n // Unrecognized landing site (or already injected) — no-op rather than guess.\n return code\n}\n\n/**\n * Fill the Rust codegen's `__AIHU_LIGHT_SCOPE_ID__` placeholder with the\n * component's real light-DOM scope id (LDF §10 step 3).\n *\n * The server-target string renderer (`emit.rs`, wave-3) emits\n * `const __AIHU_LIGHT_SCOPE_ID__: string | undefined = undefined;` and merges\n * it into `__ssrString`'s options (`opts.lightScopeId ?? __AIHU_LIGHT_SCOPE_ID__`)\n * so a compiled light-DOM component can stamp `data-a` on its own rendered\n * root with NO caller cooperation. Whether the component actually resolves to\n * light mode is only known here in the JS layer (same reason\n * `_injectShadowMode` exists), so Rust emits the placeholder and this helper\n * replaces the literal when the mode resolved to light — exactly the wiring\n * the Rust-side comment names. Idempotent: no placeholder (client target,\n * bailed string renderer) → code returned untouched.\n *\n * @internal\n */\nexport function _injectLightScopeId(code: string, lightScopeId: string): string {\n return code.replace(\n 'const __AIHU_LIGHT_SCOPE_ID__: string | undefined = undefined',\n `const __AIHU_LIGHT_SCOPE_ID__: string | undefined = '${lightScopeId}'`,\n )\n}\n\n/**\n * Light-DOM (`shadowMode:'light'`) recipes: redirect the authored `@style`\n * block's per-instance `host.adoptedStyleSheets = [__style__]` assignment to\n * `document.adoptedStyleSheets` so the recipe's class-scoped CSS reaches the\n * global cascade (a light-DOM host has no shadow root, making the original\n * setter a silent no-op). The module-level `__style__` is shared across\n * instances; the `includes` guard keeps the global adoption idempotent.\n *\n * @internal\n */\nexport function _globalizeAuthoredStyle(code: string): string {\n // The Rust codegen emits exactly: `(ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];`\n const re = /\\(ctx\\.host as ShadowRoot\\)\\.adoptedStyleSheets\\s*=\\s*\\[__style__\\];?/\n return code.replace(\n re,\n 'if (!document.adoptedStyleSheets.includes(__style__)) document.adoptedStyleSheets = [...document.adoptedStyleSheets, __style__];',\n )\n}\n\n/**\n * Read the compiler's AUTHORITATIVE island classification from the\n * `// @aihu:island <kind>` marker the Rust codegen emits for every component\n * (`emit.rs`, wave 3c). A component is a **static** island (server-render\n * only, no client hydration) or an **interactive** island (needs the signals\n * reactivity runtime).\n *\n * This REPLACES the old `_classifyIsland` regex post-pass, which re-derived\n * the answer by scanning generated code for `signal(`/`computed(`/`effect(`/…\n * calls — a Derived-property VIOLATION (docs/plans/ssr-build-performance-\n * findings.md §5: \"the compiler answered that question when it emitted the\n * code\"). The compiler now computes the classification from the IR (the same\n * fact-set that decides which owner-context primitives to import) and records\n * it as this marker; the plugin merely reads it.\n *\n * Crucially the compiler classifies CONSERVATIVELY and knows things the regex\n * could not: e.g. a component whose own body is inert but which declares\n * `$prop`s is `interactive` (props are reactive inputs the parent drives, and\n * its options-form emit is one the static-island shim cannot lower) — the old\n * regex saw no `signal(` call and wrongly classified it `static`.\n *\n * Defaults to `'interactive'` if the marker is somehow absent (an old binary,\n * or a future emit shape): the safe default never strips the runtime out from\n * under a component that needs it.\n *\n * @internal\n */\nexport function _parseIslandMarker(compiledCode: string): 'static' | 'interactive' {\n const m = /^\\/\\/ @aihu:island (static|interactive)$/m.exec(compiledCode)\n return m?.[1] === 'static' ? 'static' : 'interactive'\n}\n\n/** Best-effort message text for an unknown thrown value. @internal */\nexport function _errMessage(err: unknown): string {\n if (err instanceof Error) return err.message\n if (typeof err === 'string') return err\n const m = (err as { message?: unknown } | null)?.message\n return typeof m === 'string' ? m : String(err)\n}\n\n/**\n * Is this `import('vite')` rejection the ONE legitimate \"there is no Vite here\"\n * case — a standalone `transform()` caller, a unit test, any non-Vite host?\n *\n * The distinction matters because the two outcomes are opposites: \"no Vite\"\n * must hand the TypeScript back untouched (the caller owns it and never asked\n * for a strip), while \"Vite is here and something broke\" must throw, because\n * un-stripped TypeScript returned into a Vite build is silent corruption that\n * only surfaces as an unrelated bundler `PARSE_ERROR` much later.\n *\n * The test is deliberately narrow: a module-resolution failure whose subject is\n * the `vite` specifier itself. Both Node and Bun report `ERR_MODULE_NOT_FOUND`\n * with a message naming the package (`Cannot find package 'vite' …`). A\n * resolution failure for something else — a broken transitive dependency of an\n * installed Vite, say — is NOT this case: Vite is present, the strip is\n * expected, and swallowing it would be the same silent corruption. So it is\n * loud.\n *\n * @internal\n */\nexport function _isViteMissing(err: unknown): boolean {\n const message = _errMessage(err)\n const code = (err as { code?: unknown } | null)?.code\n const isResolutionFailure =\n code === 'ERR_MODULE_NOT_FOUND' ||\n code === 'MODULE_NOT_FOUND' ||\n /cannot find (module|package)/i.test(message)\n if (!isResolutionFailure) return false\n // The unresolved specifier must be `vite` itself, quoted the way both\n // runtimes quote it, not merely a path that happens to contain \"vite\".\n return /['\"`]vite['\"`]/.test(message)\n}\n\n/**\n * The message for a strip that failed with Vite present — names the branch, the\n * Vite version, the environment and the file, and says why it is fatal rather\n * than swallowed.\n *\n * @internal\n */\nexport function _stripFailure(\n fn: 'transformWithOxc' | 'transformWithEsbuild',\n id: string,\n viteVersion: string,\n isServerEnv: boolean,\n err: unknown,\n): string {\n return (\n `[@aihu/compiler] TypeScript strip failed for ${id} — ` +\n `vite ${viteVersion} \\`${fn}\\` (${isServerEnv ? 'server' : 'client'} environment) threw. ` +\n 'Returning the un-stripped TypeScript would corrupt the build silently and ' +\n 'resurface as an unrelated bundler PARSE_ERROR on this file, so it fails here instead. ' +\n `Underlying error: ${_errMessage(err)}`\n )\n}\n\n/** What the Vite plugin's `transform` hook hands back after the strip. */\nexport interface StripTypesResult {\n readonly code: string\n readonly map: null\n /** Rolldown-only hint; set ONLY on the last-resort no-transform branch. */\n readonly moduleType?: 'ts'\n}\n\n/**\n * The subset of the Vite module `_stripTypes` uses — the seam tests fake.\n *\n * The trailing parameters are `any` on purpose: Vite's real signatures carry\n * version-specific option/config/watcher types (and MORE parameters on some\n * versions), and a narrower type here would make the genuine module fail to\n * satisfy the interface. Only the first two parameters and `code` on the result\n * are actually depended on.\n */\nexport interface ViteStripApi {\n readonly version?: string\n readonly transformWithOxc?: (\n code: string,\n id: string,\n // biome-ignore lint/suspicious/noExplicitAny: variance seam — see doc comment\n ...rest: any[]\n ) => Promise<{ code: string }>\n readonly transformWithEsbuild?: (\n code: string,\n id: string,\n // biome-ignore lint/suspicious/noExplicitAny: variance seam — see doc comment\n ...rest: any[]\n ) => Promise<{ code: string }>\n}\n\n/**\n * Strip TypeScript from compiler output using whichever transform the resolved\n * Vite exposes. Takes the Vite module as a PARAMETER so the branch order and\n * the failure behaviour are testable without installing four Vite versions.\n *\n * Branch order, and why:\n *\n * 1. `transformWithOxc` — Vite's own transform, needing NO separate esbuild.\n * Vite 8 made esbuild an OPTIONAL PEER while still *exporting* a\n * `transformWithEsbuild` that throws \"It is deprecated and it now requires\n * esbuild to be installed separately … migrate to `transformWithOxc`\" the\n * moment it is called. So this branch is deliberately NOT gated on the\n * environment: a fresh consumer install at vite 8 has no esbuild at all, and\n * the esbuild branch would throw on the CLIENT build — which every output\n * mode (`spa`, `static`, `ssr`) runs.\n * 2. `transformWithEsbuild` — vite 6 and 5, where `transformWithOxc` does not\n * exist (`'transformWithOxc' in vite` is literally false on 6.4.3), so those\n * versions keep taking this branch and their output is unchanged.\n * 3. Neither — hand the TypeScript to Rolldown with `moduleType: 'ts'`. A\n * forward-compatibility escape hatch for a Vite that drops both.\n *\n * Preferring oxc on vite 8 is a DELIBERATE behaviour change, not a refactor:\n * oxc lowers class fields with `useDefineForClassFields: true` (the modern-TS\n * default) where esbuild used `false`, lowers enums to a different (equivalent)\n * IIFE shape, and does not constant-inline enum member reads. Invisible for\n * compiler-generated code, observable for user-authored classes in an `.aihu`\n * script block. Pinned by `tests/strip-branch.test.ts`.\n *\n * Failures here are LOUD. Both call sites are individually wrapped so the\n * thrown error names the branch, the Vite version, the environment and the\n * file. The alternative — the swallowing `catch` this replaced — returned\n * un-stripped TypeScript that only surfaced as an unrelated `PARSE_ERROR` from\n * the bundler two hundred lines of build output later.\n *\n * @internal\n */\nexport async function _stripTypes(\n vite: ViteStripApi,\n code: string,\n id: string,\n isServerEnv: boolean,\n): Promise<StripTypesResult> {\n const viteVersion = vite.version ?? 'unknown'\n if (typeof vite.transformWithOxc === 'function') {\n try {\n const stripped = await vite.transformWithOxc(code, 'component.ts', {\n lang: 'ts',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n } catch (err) {\n throw new Error(_stripFailure('transformWithOxc', id, viteVersion, isServerEnv, err), {\n cause: err,\n })\n }\n }\n if (typeof vite.transformWithEsbuild === 'function') {\n try {\n const stripped = await vite.transformWithEsbuild(code, 'component.ts', {\n target: 'esnext',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n } catch (err) {\n throw new Error(_stripFailure('transformWithEsbuild', id, viteVersion, isServerEnv, err), {\n cause: err,\n })\n }\n }\n return { code, moduleType: 'ts', map: null }\n}\n\n/**\n * §22 — parse the `// @aihu:component-tags a,b,c` marker the Rust codegen emits\n * for every server/universal build, on the same channel as `@aihu:island` above.\n *\n * The list comes from `collect_component_tags` — the SAME walk that fills\n * `route.json`'s `components` array — so it is already sorted, de-duplicated and\n * kebab-normalized when it arrives here. Deliberately NOT re-sorted or\n * re-de-duplicated: a second normalization would be a second place the rule\n * lives, and the two would drift.\n *\n * Distinct from the `__aihu_child_tags__` derivation below: this is \"tags the\n * template references AT ALL\", not \"tags the compiled renderer will look up\".\n * See the comment on the `__aihu_referenced_tags__` export for why both exist.\n *\n * Returns `[]` when the marker is absent (an older binary, a client-target\n * compile, a future emit shape) — the caller treats that identically to \"the\n * template references nothing\".\n *\n * @internal\n */\nexport function _parseComponentTagsMarker(compiledCode: string): string[] {\n const m = /^\\/\\/ @aihu:component-tags (.+)$/m.exec(compiledCode)\n return m === null ? [] : (m[1] as string).split(',')\n}\n\n/**\n * Derive the `__aihu_child_tags__` set from SERVER-TARGET compiled code: the\n * tags the compiled string renderer will actually look up, read off the\n * `__aihu_schild('<tag>'` call sites the Rust codegen emitted. Deduped, sorted.\n *\n * THE one derivation, deliberately. There are two consumers:\n *\n * 1. `aihuCompilerPlugin`'s transform, which turns the result into the\n * `export const __aihu_child_tags__` a compiled module carries.\n * 2. `@aihu/router`'s `genSC`, which needs the SAME edge set at CODEGEN\n * time — before any module exists to read an export off — to walk from\n * the pages out to the components the server bundle must actually carry.\n *\n * `genSC` reaching for this instead of re-deriving is the whole point. The\n * `__aihu_referenced_tags__` docblock below spends a paragraph arguing that\n * deriving the runtime edge set a second way would be \"one rule written in two\n * places, and the halves would drift the first time a boundary moved\" — and\n * that argument does not stop applying because the second site happens to live\n * in another package. `readAihuLayoutComponents` (the source regex) is a THIRD,\n * differently-defined set and is not a substitute: it counts references the\n * emitter DECLINES (an attribute, children, a dynamic path), which produce no\n * call site, so `__aihu_schild` can never look them up. Measured on a\n * three-way fixture, it bundled a module whose rendered output was empty.\n *\n * Takes compiled code, not a source string: a caller with only source runs\n * `transform(src, id, { target: 'server' }).code` first — which is memoized, so\n * a file already compiled in this process costs a map lookup. Client- and\n * universal-target output carries no `__aihu_schild` call sites at all and\n * yields `[]`, which is correct: there is no server render to feed.\n *\n * @internal\n */\nexport function _deriveChildTags(compiledCode: string): string[] {\n return [\n ...new Set(\n Array.from(compiledCode.matchAll(/__aihu_schild\\('([^']+)'/g), (m) => m[1] as string),\n ),\n ].sort()\n}\n\n/**\n * GX Phase 1 (#437-GX) — parse the `// @aihu:extract read=<v> call=<v>` code\n * marker the Rust compiler emits for every server/universal build (the\n * resolved policy, the ratified default included). Phase 1 consumes it only\n * for the build census below; Phase 4 (E2) will read the SAME marker for\n * governed chunk routing.\n * @internal\n */\nexport function _parseExtractMarker(code: string): { read: string; call: string } | null {\n const m = /^\\/\\/ @aihu:extract read=(\\S+) call=(\\S+)$/m.exec(code)\n return m ? { read: m[1] as string, call: m[2] as string } : null\n}\n\n/**\n * GX Phase 1 (#437-GX) — format the per-value extract census (the DA-e census\n * pattern from #437: every build PRINTS the posture distribution, so the\n * default-vs-declared migration story stays visible rather than silent).\n * Returns the printable lines; pure so tests can assert the counts.\n * @internal\n */\nexport function _formatExtractCensus(\n census: ReadonlyMap<string, { read: string; call: string }>,\n): string[] {\n if (census.size === 0) return []\n const readCounts = new Map<string, number>()\n const callCounts = new Map<string, number>()\n for (const { read, call } of census.values()) {\n readCounts.set(read, (readCounts.get(read) ?? 0) + 1)\n callCounts.set(call, (callCounts.get(call) ?? 0) + 1)\n }\n const lines = [`[aihu] extract census — ${census.size} surface(s)`]\n for (const [value, n] of [...readCounts.entries()].sort()) lines.push(` read=${value}: ${n}`)\n for (const [value, n] of [...callCounts.entries()].sort()) lines.push(` call=${value}: ${n}`)\n return lines\n}\n\n/**\n * Extract the custom element tag name from compiler-emitted code.\n * The compiler always emits `defineElement('tag-name', ...)` as the\n * first call — pull the first string literal argument.\n * Returns `null` if no `defineElement` call is found.\n * @internal\n */\nfunction _extractElementTag(code: string): string | null {\n const m = /defineElement\\(\\s*['\"]([^'\"]+)['\"]/m.exec(code)\n return m ? (m[1] ?? null) : null\n}\n\n/**\n * §9.4 — is this compiled module a base-extending recipe, i.e. does it call\n * `defineComponent({ … base: … })` with an options object rather than a bare\n * setup function? Such a component cannot take the static-island shim, which\n * inlines `class extends HTMLElement` and has no way to honour a base class.\n *\n * Shape B of the ReDoS note at the top of this file. Equivalent to the single\n * ``/defineComponent\\(\\s*\\{[^]*?\\bbase\\s*:/`` this replaced: the head\n * sub-pattern is unchanged, and `base:` appearing after some LATER\n * `defineComponent({` implies it also appears after the first one — so testing\n * only the first head is the same predicate, without the lazy re-scan that\n * every repetition of the head literal used to restart.\n * @internal\n */\nfunction _hasBaseRecipe(code: string): boolean {\n const head = /defineComponent\\(\\s*\\{/.exec(code)\n if (head === null) return false\n const key = /\\bbase\\s*:/g\n key.lastIndex = head.index + head[0].length\n return key.test(code)\n}\n\n/** Strip trailing `/` characters via a plain scan, not a `\\/+$/`-anchored\n * regex — that shape is vulnerable to catastrophic backtracking on a long\n * run of slashes with no match (CodeQL js/polynomial-redos): the greedy `+`\n * backtracks one character at a time at EVERY starting offset before\n * failing, an O(n²) blowup (measured: ~45s on 200k slashes). */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) end--\n return s.slice(0, end)\n}\n\n/**\n * Is `rawId` a layout SFC (a `.aihu` file under the configured layouts dir)?\n * Root-independent: matches the `<layoutsDir>/` segment anywhere in the path,\n * which is sufficient because the layouts dir is a project-relative convention.\n * @internal\n */\nexport function _isLayoutFile(rawId: string, layoutsDir: string): boolean {\n const ld = trimTrailingSlashes(layoutsDir.replace(/\\\\/g, '/').replace(/^\\.?\\//, ''))\n if (!ld) return false\n return rawId.replace(/\\\\/g, '/').includes(`/${ld}/`)\n}\n\n/**\n * Layout custom-element tag for a filename stem. MUST match\n * `@aihu/router`'s `layoutTagFor()` so the generated `virtual:aihu-layouts`\n * map and the registered element agree on the tag.\n * @internal\n */\nexport function _layoutTag(stem: string): string {\n return `aihu-layout-${stem.toLowerCase()}`\n}\n\n/**\n * O1a (tag naming) — JS mirror of the Rust compiler's\n * `tags::kebab_component_tag` (packages/compiler/src/tags.rs). PascalCase→kebab,\n * else lowercase-verbatim: inserts '-' before an uppercase letter when the\n * previous char is lowercase/digit, OR (acronym boundary) the previous char is\n * uppercase and the next is lowercase; then lowercases all. Applied to the\n * file stem before it is passed as `--tag` so the JS driver's define-name\n * matches the Rust one (`UserCard.aihu` → `user-card`). Validation/erroring\n * (C450) is owned by the Rust compiler — this is the infallible transform only.\n * @internal\n */\nexport function kebabComponentTag(raw: string): string {\n let out = ''\n for (let i = 0; i < raw.length; i++) {\n // charAt (not raw[i]) returns string, never string|undefined — satisfies\n // noUncheckedIndexedAccess; charAt(i+1) yields '' past the end, matching\n // the \"no next char\" case.\n const c = raw.charAt(i)\n if (i > 0 && c >= 'A' && c <= 'Z') {\n const prev = raw.charAt(i - 1)\n const next = raw.charAt(i + 1)\n const prevLower = prev >= 'a' && prev <= 'z'\n const prevDigit = prev >= '0' && prev <= '9'\n const prevUpper = prev >= 'A' && prev <= 'Z'\n const nextLower = next >= 'a' && next <= 'z'\n if (prevLower || prevDigit || (prevUpper && nextLower)) out += '-'\n }\n out += c.toLowerCase()\n }\n return out\n}\n\nconst OUTLET_HEAD = 'const createOutletBoundary = () => {'\nconst OUTLET_PASSIVE = `const createOutletBoundary = () => branch('div', { 'data-aihu-outlet': '' }, []);`\n\n/**\n * Collapse the reactive `<outlet>` boundary the Rust codegen emits into a\n * passive `data-aihu-outlet` marker. Layout SFCs are rendered by `@aihu/app`'s\n * imperative client renderer, which fills the marker itself; the default\n * boundary's mount-time `effect()` reads `useRoute()` (null under the imperative\n * path) and clears the marker, which would wipe the page the renderer inserts.\n *\n * Anchors on the exact `const createOutletBoundary = () => { … return host; };`\n * block the codegen emits (`packages/compiler/src/codegen/emit.rs`). No-op when\n * the layout declares no `<outlet>`.\n *\n * Shape B of the ReDoS note at the top of this file: the head is located with\n * `indexOf` and the tail scanned once from there, instead of one regex whose\n * lazy `[\\s\\S]*?` re-scanned the whole module at every repetition of the head\n * literal.\n * @internal\n */\nexport function _passivizeOutlet(code: string): string {\n const head = code.indexOf(OUTLET_HEAD)\n if (head === -1) return code\n // `[^\\S\\n]*\\n` (horizontal whitespace, then the line break) rather than\n // `\\s*\\n`: the codegen emits ` return host;\\n};` and `\\s` matching `\\n`\n // made the run ambiguous. Sticky-free `g` + an explicit `lastIndex` starts\n // the single tail scan immediately after the head.\n const tailRe = /return host;[^\\S\\n]*\\n\\};/g\n tailRe.lastIndex = head + OUTLET_HEAD.length\n const tail = tailRe.exec(code)\n if (tail === null) return code\n return code.slice(0, head) + OUTLET_PASSIVE + code.slice(tail.index + tail[0].length)\n}\n\n/**\n * Instrument a compiled `.aihu` module with HMR support.\n *\n * The compiler always emits:\n *\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { ... }))\n *\n * This function:\n *\n * 1. Adds `_hmrReplace` to the `@aihu/runtime` import.\n * 2. Prepends a module-level slot variable `__aihu_setup__`.\n * 3. Rewrites the single `defineComponent(` call so the setup function\n * is captured via an assignment expression:\n * `defineComponent(__aihu_setup__ = ` (valid JS; assignment has\n * lower precedence than arrow fn, so `defineComponent` still\n * receives the function as its argument).\n * 4. Appends `export { __aihu_setup__ as default }` so that Vite's\n * `import.meta.hot.accept` callback receives the new setup via\n * `newModule.default` on hot reload.\n * 5. Appends the `import.meta.hot.accept` block, gated on `__DEV__`.\n *\n * The `__DEV__` guard ensures production bundlers (where they replace\n * `__DEV__` with `false`) dead-code-eliminate the entire HMR block.\n *\n * @internal\n */\nfunction _buildHmrCode(compiledCode: string, elementTag: string): string {\n // Step 1 — add _hmrReplace to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hmrReplace')) parts.push('_hmrReplace')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Step 2+3 — prepend slot variable and rewrite the defineComponent call.\n // Compiler emits exactly one `defineComponent(` followed by a function expr.\n // Rewrite: defineComponent(fn) → defineComponent(__aihu_setup__ = fn)\n // Assignment expression evaluates to `fn`, so defineComponent still\n // receives the setup function as its first argument unchanged.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const preamble = `let __aihu_setup__: ((ctx: any) => any) | undefined\\n`\n\n const patchedBody = withImport.replace(/\\bdefineComponent\\(/, 'defineComponent(__aihu_setup__ = ')\n\n const tag = JSON.stringify(elementTag)\n // Step 4+5 — postamble with default export and HMR acceptance.\n const postamble = `\nexport { __aihu_setup__ as default }\n\nif (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (!newModule) return\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const newSetup = (newModule as any)['default']\n if (typeof newSetup !== 'function') return\n document.querySelectorAll(${tag}).forEach((el) => {\n _hmrReplace(el as HTMLElement, newSetup)\n })\n })\n}\n`\n\n return preamble + patchedBody + postamble\n}\n\n/**\n * Rewrite an interactive-island module so its `connectedCallback` waits\n * for the element to scroll into view before mounting. Plan 3.3 — applied\n * only when the consumer adds `defer` to the custom element tag (e.g.\n * `<my-counter defer>`); the runtime helper checks the attribute and\n * either mounts immediately or registers an `IntersectionObserver`.\n *\n * Implementation: the helper is added as a `_hydrateOnVisible` import\n * from `@aihu/runtime`, and the compiler-emitted `defineElement(...)`\n * call is wrapped in a `defineElement` that intercepts `connectedCallback`\n * to honour the `defer` attribute.\n *\n * The whole indirection is tree-shaken when no `.aihu` module reaches\n * this branch, because `_hydrateOnVisible` is exported from its own\n * sibling module inside `@aihu/runtime`.\n *\n * @internal\n */\nexport function _buildDeferredHydration(compiledCode: string, elementTag: string): string {\n // Add _hydrateOnVisible to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hydrateOnVisible')) parts.push('_hydrateOnVisible')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Wrap the class returned by defineComponent BEFORE defineElement\n // consumes it. The HTML spec caches lifecycle callbacks at\n // customElements.define() time, so we MUST mutate the prototype\n // before that call — not after. We accomplish this with a synchronous\n // helper invoked between defineComponent and defineElement.\n //\n // Source pattern (compiler-emitted):\n // defineElement('tag', defineComponent((_ctx) => { ... }))\n //\n // After this rewrite:\n // defineElement('tag', __aihu_wrap_defer__(defineComponent((_ctx) => { ... })))\n //\n // …with __aihu_wrap_defer__ defined in the appended preamble.\n const patched = withImport.replace(\n /defineElement\\(\\s*('[^']+'|\"[^\"]+\")\\s*,\\s*defineComponent\\(/,\n (_m, tagLit: string) => `defineElement(${tagLit}, __aihu_wrap_defer__(defineComponent(`,\n )\n // Match the closing `))` of the defineElement call. The HMR pass may\n // have inserted `__aihu_setup__ = ` before the inner function, but\n // the trailing `))` shape is unchanged. Replace exactly one occurrence\n // by anchoring on end-of-string trim; bail if the shape does not match.\n if (patched === withImport) {\n // The expected `defineElement(<tag>, defineComponent(` shape was not\n // present (e.g. compiler output changed). Skip defer wrapping rather\n // than emit broken code.\n return compiledCode\n }\n // Add a trailing `)` to balance the extra `(` from __aihu_wrap_defer__.\n // Source shape after _buildHmrCode is:\n // defineElement('tag', defineComponent(__aihu_setup__ = (_ctx) => {...}))\n // export { __aihu_setup__ as default }\n // if (typeof __DEV__ !== ...) { ... }\n // We must close BEFORE the export line. Match the first `))` followed\n // by a newline and `export` (or end-of-string for the unwrapped case).\n let balanced = patched.replace(/\\)\\s*\\)\\s*\\nexport\\s/, ')))\\nexport ')\n if (balanced === patched) {\n // No HMR postamble — the `))` is at end-of-string.\n balanced = patched.replace(/\\)\\s*\\)\\s*$/, ')))\\n')\n }\n if (balanced === patched) {\n // Could not find the matching `))` — bail out.\n return compiledCode\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const helper = `\n// Plan 3.3 (Islands) — defer attribute support. Wraps the constructor\n// returned by defineComponent so instances bearing the \\`defer\\` attribute\n// hydrate lazily via IntersectionObserver. Bare instances retain the\n// eager Plan 3.2 hydration path.\nfunction __aihu_wrap_defer__<T extends typeof HTMLElement>(Ctor: T): T {\n const orig = (Ctor.prototype as unknown as { connectedCallback?: () => void }).connectedCallback\n if (typeof orig !== 'function') return Ctor\n ;(Ctor.prototype as unknown as { connectedCallback: () => void }).connectedCallback = function (this: HTMLElement) {\n if (this.hasAttribute('defer')) {\n _hydrateOnVisible(this, () => orig.call(this))\n } else {\n orig.call(this)\n }\n }\n return Ctor\n}\n`\n void elementTag\n return helper + balanced\n}\n\n/**\n * Build a static-island shim for a compiled module.\n *\n * The compiled module emitted by the Rust codegen has the shape:\n *\n * import { branch, leaf, slot } from '@aihu/arbor'\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { return <tree> }))\n *\n * For a static island we know `<tree>` contains no `signal(`/`computed(`\n * calls. We can therefore:\n *\n * 1. Drop the `@aihu/runtime` import (saves ~600 B gz of defineComponent\n * + defineElement + bootstrap glue).\n * 2. Replace `defineElement(tag, defineComponent(setup))` with a tiny\n * inline class that mounts the tree directly via `mount()` (which the\n * arbor barrel already exports).\n * 3. Tag the file with a `// AIHU_STATIC_ISLAND` comment so consumers\n * can audit which routes shipped zero-JS-runtime.\n *\n * Falls back to the original code if the regex shape does not match\n * (defensive: a future compiler change must opt back into static-island\n * emission explicitly rather than silently break).\n *\n * @internal\n */\nexport function _buildStaticIsland(compiledCode: string, elementTag: string): string {\n // Confirm the shape we expect: a single defineElement(...) call wrapping\n // a single defineComponent(...) call. Bail out otherwise.\n const callRe = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/\n if (!callRe.test(compiledCode)) return compiledCode\n\n // Strip the `@aihu/runtime` import line entirely — static islands\n // don't reference defineComponent/defineElement after the rewrite.\n const withoutRuntimeImport = compiledCode.replace(\n /^[^\\S\\r\\n]*import\\s*\\{[^{}]*\\}\\s*from\\s*'@aihu\\/runtime'(?:\\s*;)?\\s*$/m,\n '',\n )\n\n // Ensure `mount` is imported from @aihu/arbor (it already exposes\n // branch/leaf/slot, so we just append `mount` to the existing list).\n const withArborMount = withoutRuntimeImport.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n\n // Which tail shape this module ends in, and whether an island is safe.\n //\n // `_injectShadowMode` runs BEFORE this in the transform pipeline, so the\n // `defineElement(...)` call may already carry a third argument and no longer\n // end in `))`. Rewriting the head while the tail rewrite silently no-ops\n // produced a module with an unclosed class body and a dangling options\n // object — invalid JS, emitted with no error, which surfaced downstream as a\n // confusing `[PARSE_ERROR] … invalid JS syntax` naming the user's `.aihu`\n // file. Reachable today with `islands: true` + `shadowMode: 'shadow'`, on\n // vite 6 as well as 8.\n //\n // `shadowMode: 'shadow'` alone is safe to drop: the inline class attaches its\n // own `{ mode: 'open' }` shadow root, which is precisely what that option\n // asks for. ANY other option (`formAssociated`, `lightScopeId`, a future\n // field) carries behaviour this class does not implement, so the island is\n // DECLINED — the component keeps the ordinary `defineElement` path, exactly\n // as the \"falls back to the original code\" contract above promises.\n const TAIL_PLAIN = /\\)\\s*\\)\\s*$/\n // `_injectShadowMode`'s two-argument branch (line ~314) always emits this\n // EXACT literal shape — `, { shadowMode: 'shadow' }` with one space at each\n // junction, never a trailing comma — so the `\\s*,?\\s*` this used to have\n // around the optional comma was dead flexibility, never exercised by real\n // compiler output, and it was the ReDoS: two adjacent `\\s*` groups with\n // only an optional zero-width `,?` between them let a long non-matching\n // whitespace run split across the pair in O(n) ways per position. A single\n // `\\s*` per junction, none of them adjacent to another, matches the same\n // real input with no such ambiguity.\n const TAIL_WITH_SHADOW_ONLY = /\\),\\s*\\{\\s*shadowMode:\\s*'shadow'\\s*\\}\\)\\s*$/\n const tail = TAIL_PLAIN.test(withArborMount)\n ? TAIL_PLAIN\n : TAIL_WITH_SHADOW_ONLY.test(withArborMount)\n ? TAIL_WITH_SHADOW_ONLY\n : null\n if (tail === null) return compiledCode\n\n // Replace `defineElement('tag', defineComponent((_ctx) => { ... }))`\n // with an inline `customElements.define` whose connectedCallback mounts\n // the static tree. The setup function is captured verbatim by replacing\n // the wrapping calls with anonymous-IIFE bookends.\n const tagJson = JSON.stringify(elementTag)\n const rewritten = withArborMount\n .replace(\n /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/,\n `customElements.define(${tagJson}, class extends HTMLElement {\\n connectedCallback() {\\n const root = this.attachShadow({ mode: 'open' })\\n const __aihu_setup__ = (`,\n )\n .replace(tail, `)\\n mount(__aihu_setup__({ host: root, element: this }), root)\\n }\\n})\\n`)\n\n return `// AIHU_STATIC_ISLAND — zero @aihu/runtime references\\n${rewritten}`\n}\n\n/**\n * Compile a .aihu source string to TypeScript.\n * map is null — source maps are deferred to v1 (OQ-C8)\n *\n * B3b — when `sidecarOut` is provided, also writes the per-SFC `.aihu.ts`\n * sidecar at that path. Callers (e.g. the Vite plugin) typically pass\n * `<source-id>.ts` so `tsc --noEmit` discovers per-SFC template expressions.\n */\nexport function transform(\n source: string,\n id: string,\n options?: {\n sidecarOut?: string\n target?: 'client' | 'server' | 'universal'\n /** Override the registered custom-element tag (default: file stem). Used for layouts. */\n tag?: string\n /**\n * #486 step 4 — emit the sidecar's attribute/component-prop type layer\n * (`--strict-templates`). Affects only the type-check surface written to\n * `sidecarOut`; the compiled JS is identical either way. Default off.\n */\n strictTemplates?: boolean\n },\n): { code: string; map: null } {\n // O1a (tag naming): normalize the stem so the JS driver's define-name\n // matches the Rust compiler's (`UserCard.aihu` → `user-card`). When a\n // component-shaped (uppercase-first) stem normalizes to a hyphen-less name\n // (`Comment.aihu` → `comment`), pass the RAW stem instead so the Rust\n // compiler surfaces its C450 error — the JS never validates or errors, but\n // it must not mask the error by pre-lowercasing. An explicit `options.tag`\n // (e.g. `_layoutTag` for layouts) passes through untouched.\n const rawStem = basename(id, '.aihu')\n const kebabStem = kebabComponentTag(rawStem)\n const stem = /^[A-Z]/.test(rawStem) && !kebabStem.includes('-') ? rawStem : kebabStem\n const args = ['--stdin', '--tag', options?.tag ?? stem, '--path', id]\n if (options?.sidecarOut) {\n args.push('--sidecar-out', options.sidecarOut)\n }\n // T6 (go-public demo) — thread the build target so a client bundle gets the\n // policy-free `@agent` dispatcher (and the per-instance registration the\n // capability bridge needs) instead of the server `__agentBinding`. Defaults to\n // the compiler's `universal` target when omitted (existing behaviour).\n if (options?.target) {\n args.push('--target', options.target)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n // `sidecarOut` BYPASSES the memo AND the envelope backends: it makes the\n // spawn write a file on disk — a cache hit would silently skip that side\n // effect, and the envelope API deliberately has no file-writing emits. The\n // Vite build path never passes it, so the SSG double-compile still fully\n // hits.\n if (options?.sidecarOut) {\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveBinPath()\n const startedAt = Date.now()\n try {\n const code = execFileSync(bin, args, {\n input: source,\n encoding: 'utf8',\n ...compileSpawnBounds(source.length),\n })\n return { code, map: null }\n } catch (err) {\n const described = describeSpawnFailure(err, bin, args, source.length, Date.now() - startedAt)\n throw described ?? err\n }\n }\n // Memo key: everything that shapes the emitted code. `id` is hashed\n // separately; the fingerprint carries the explicit options (`tag` covers the\n // layout override — the default stem is a pure function of `id`). The stamp\n // is the ACTIVE backend's file identity (native addon `.node` path when\n // in-process, CLI binary path when spawning) — see js/envelope.ts.\n const stamp = _backendStampPath()\n const target = options?.target ?? 'universal'\n // Sibling-artifact seeding (the single-parse envelope win): one compile of\n // a file yields js + ast + route, and the ast/route strings are seeded into\n // the memo under the exact keys `compileToAst` / `compileRouteMeta` derive\n // for the same (source, id) — so css-engine's AST pass and the router's\n // route scan become cache hits instead of re-parses. Only sound when the\n // tag is NOT overridden: those callers derive their own stem from `id`, and\n // an explicit tag (layout mode) resolves to a different define-name.\n const seedSiblings = options?.tag === undefined\n const code = _memoizedSpawn(\n 'transform',\n source,\n id,\n `target=${options?.target ?? ''}|tag=${options?.tag ?? ''}|strict=${options?.strictTemplates === true}`,\n stamp,\n () => {\n const reply = _compileViaBackend(source, args, {\n tag: options?.tag ?? stem,\n path: id,\n targets: [target],\n emits: seedSiblings ? ['js', 'ast', 'route'] : ['js'],\n ...(options?.strictTemplates ? { strictTemplates: true } : {}),\n })\n // Legacy reply — an older binary ignored `--envelope` and answered the\n // classic single-target request; its stdout IS the compiled JS.\n if (reply.kind === 'legacy') return reply.output\n const envelope = reply.envelope\n if (seedSiblings) {\n if (envelope.astJson !== undefined) {\n _seedMemo('ast', source, id, '', stamp, envelope.astJson)\n }\n _seedMemo('route', source, id, '', stamp, envelope.routeJson ?? 'null')\n }\n const js = envelope.targets[target]?.js\n if (js === undefined) {\n throw new Error(`[@aihu/compiler] envelope reply missing js for target '${target}'`)\n }\n return js\n },\n )\n return {\n code,\n map: null, // source maps deferred to v1 (OQ-C8)\n }\n}\n\n/**\n * Escape a CSS string for safe interpolation inside a JS template literal.\n * The Rust codegen places the authored `@style` body raw inside a backtick\n * literal, so it already assumes no backticks in `@style`. css-engine output\n * (theme tokens + utility rules) likewise never contains backticks, but we\n * escape `\\`, `` ` `` and `${` defensively so a future token value can't\n * break out of the literal.\n *\n * @internal\n */\nfunction _escapeForTemplateLiteral(css: string): string {\n return css.replace(/\\\\/g, '\\\\\\\\').replace(/`/g, '\\\\`').replace(/\\$\\{/g, '\\\\${')\n}\n\n/**\n * Fold css-engine-produced scoped CSS into a compiled `.aihu` module.\n *\n * The Rust codegen emits the authored `@style` block (when present) as:\n *\n * const __style__ = new CSSStyleSheet();\n * __style__.replaceSync(`<authored css>`);\n * defineElement('tag', defineComponent((ctx) => {\n * (ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];\n * return ...\n * }))\n *\n * css-engine's `compileSfc` output is the COMPLETE per-SFC stylesheet:\n * `:host` theme tokens, the variant-resolved utility-class rules, AND the\n * folded authored `@style` block (under an `authored @style` CSS comment).\n * So it is authoritative — we adopt it as the single shadow `<style>` and\n * the authored `@style` keeps emitting through it (acceptance: \"@style still\n * emits correctly alongside\").\n *\n * Two shapes are handled:\n *\n * 1. **SFC has an `@style` block** — the Rust codegen already declared\n * `__style__` with the raw `@style` body. We REPLACE that body with the\n * css-engine output (which already CONTAINS the `@style` block) so the\n * `@style` rules are not duplicated. The existing `adoptedStyleSheets`\n * assignment is reused unchanged.\n *\n * 2. **SFC has NO `@style` block** — there is no `__style__`. We inject a\n * fresh `__style__` declaration after the last import and an\n * `adoptedStyleSheets` assignment as the first statement of the setup\n * function. The compiler emits the setup param as `_ctx` in this case;\n * we rename it to `ctx` so the injected `ctx.host` reference resolves.\n *\n * Runs on the RAW compiled output BEFORE the island / HMR / auto-wiring\n * transforms so those passes operate on the folded module uniformly:\n * - The static-island shim calls `__aihu_setup__({ host: root, ... })`\n * where `root` is the shadow root, so `ctx.host` is valid there too.\n * - The HMR / defer passes only touch the `defineElement(...)` wrapper and\n * the runtime import; they do not disturb `__style__` or the setup body.\n *\n * No-ops (returns input unchanged) when `css` is empty/whitespace.\n *\n * @internal\n */\n/**\n * Swap the text between the first `open` delimiter and the first `close`\n * delimiter that follows it, keeping both delimiters. Returns `null` when the\n * shape is absent so callers can fall through to their \"no anchor\" branch.\n *\n * Shape B of the ReDoS note at the top of this file. This is exactly what\n * ``/(<open>)[^]*?(<close>)/`` matched — `String.replace` takes the leftmost\n * match, and a `close` missing after the first `open` is missing after every\n * later one too — but as two `indexOf` scans it is linear instead of O(n²) in\n * the number of `open` repetitions an authored `@style` block can plant.\n */\nfunction _replaceDelimitedBody(\n code: string,\n open: string,\n close: string,\n body: string,\n): string | null {\n const start = code.indexOf(open)\n if (start === -1) return null\n const bodyStart = start + open.length\n const end = code.indexOf(close, bodyStart)\n if (end === -1) return null\n return code.slice(0, bodyStart) + body + code.slice(end)\n}\n\n/**\n * Fold css-engine utility CSS into the SERVER target's `__aihu_css__` export.\n *\n * The shadow-DOM sibling of `_foldCssEngineStyles`. That one rewrites the\n * client's `__style__.replaceSync(...)`; the server target has no `__style__`\n * (`CSSStyleSheet` is a DOM dependency and the Rust codegen elides it), it has\n * `export const __aihu_css__` — the string `__aihu_schild` inlines as `<style>`\n * inside a declarative shadow template.\n *\n * Without this, that template shipped the authored `@style` block ALONE: no\n * utility classes, no design tokens, no reset. A shadow child prerendered\n * partially unstyled and repainted once its chunk loaded — precisely the #754\n * failure the DSD `<style>` exists to prevent. (The step-4 scoping note claimed\n * no css-engine work was needed. That was true of the Rust emitter, which\n * applies no scoping transform, and wrong about the pipeline: the per-component\n * utility CSS is folded HERE, in the JS layer, and the server target was simply\n * never wired to it.)\n *\n * REPLACES rather than appends, for the same reason shape 1 above does: the\n * css-engine output already contains the authored `@style` rules, so appending\n * would duplicate them.\n *\n * Light-DOM components never reach this — their utilities go through the global\n * cascade via `_foldCssEngineStylesGlobal`, and their prerendered markup is\n * covered by the app stylesheet's `@scope([data-a=…])` blocks.\n */\nexport function _foldSsrCssExport(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already produced the export.\n const replaced = _replaceDelimitedBody(\n compiledCode,\n 'export const __aihu_css__ = `',\n '`',\n escaped,\n )\n if (replaced !== null) return replaced\n\n // Shape 2 — no authored @style, so the Rust codegen emitted no export at all.\n // The utility CSS still has to reach the shadow root, so declare it. Appended\n // at the end: the server artifact's exports are order-independent, and this\n // avoids guessing at an anchor the way shape 2 above has to.\n return `${compiledCode}\\nexport const __aihu_css__ = \\`${escaped}\\`\\n`\n}\n\nexport function _foldCssEngineStyles(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already declared __style__. css-engine\n // output already includes that @style block, so REPLACE the replaceSync body\n // (between the backticks) wholesale to avoid duplicating the @style rules.\n // The codegen emits `__style__.replaceSync(`<body>`);` as a single statement;\n // swap the body between the first open delimiter and the `);` that closes it.\n // `_replaceDelimitedBody` splices rather than calling `String.replace`, so a\n // `$` in the CSS can never be read as a replacement-pattern backreference.\n const replaced = _replaceDelimitedBody(compiledCode, '__style__.replaceSync(`', '`);', escaped)\n if (replaced !== null) return replaced\n\n // Shape 2 — no @style block. Inject a fresh stylesheet + adoption.\n // Bail (no-op) if the expected defineComponent setup shape is absent.\n // The setup param is NOT always `ctx`/`_ctx`: an agent component (one with an\n // exposed member) emits `(__aihu_ctx__)` so `_registerAgentServerBinding`\n // can read `__aihu_ctx__?.element`. Capture whatever the param is and inject\n // against it — a literal `(ctx)` anchor silently no-ops on agent components,\n // which shipped scoped utility CSS with no adopted stylesheet.\n const setupRe = /defineComponent\\(\\s*\\((__?[A-Za-z0-9_]+)\\)\\s*=>\\s*\\{/\n const m = setupRe.exec(compiledCode)\n if (m == null) return compiledCode\n // `_ctx` is codegen's \"unused ctx\" marker; the injected adoption uses it, so\n // normalize to `ctx`. Any other name (`ctx`, `__aihu_ctx__`) is referenced\n // elsewhere in the setup body and MUST be preserved verbatim.\n const setupParam = m[1] === '_ctx' ? 'ctx' : m[1]\n\n // Inject the module-level stylesheet declaration after the last import line.\n const lines = compiledCode.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n const decl = `const __style__ = new CSSStyleSheet();\\n__style__.replaceSync(\\`${escaped}\\`);`\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, decl)\n } else {\n lines.unshift(decl)\n }\n let withDecl = lines.join('\\n')\n\n // Inject the adoption as the first statement of the setup body, using the\n // ACTUAL setup param (`ctx` for a plain component, `__aihu_ctx__` for an\n // agent component). `_ctx` is normalized to `ctx` above since the injected\n // statement now references it.\n withDecl = withDecl.replace(\n setupRe,\n `defineComponent((${setupParam}) => {\\n (${setupParam}.host as ShadowRoot).adoptedStyleSheets = [__style__];`,\n )\n return withDecl\n}\n\n/**\n * Virtual-module prefix used by the `shadowMode === 'light'` branch to route\n * per-SFC utility CSS through Vite's built-in CSS pipeline. The plugin\n * (`aihuCompilerPlugin`) implements `resolveId` + `load` for ids matching\n * `VIRTUAL_UTILITY_PREFIX + '<hash>.css'`, returning the stored CSS body so\n * Vite hoists it into the bundle CSS asset (`dist/assets/*.css`) — NOT into\n * `host.adoptedStyleSheets`, which is a no-op when there is no shadow root.\n *\n * The trailing `.css` extension is mandatory: Vite's built-in CSS plugin keys\n * off the extension to know it should run the CSS pipeline on the module.\n *\n * @internal\n */\nexport const VIRTUAL_UTILITY_PREFIX = '\\0virtual:aihu-utility/'\n\n/**\n * djb2-style stable hash over a file id. Shared by `_hashIdForUtilityCss`\n * (virtual-CSS module keying) and `_lightScopeId` (the `data-a` scope id) so\n * the two id-derived-from-hash use sites can't drift onto different hash\n * functions.\n */\nfunction _hashId(id: string): number {\n let h = 5381\n for (let i = 0; i < id.length; i++) {\n h = ((h * 33) ^ id.charCodeAt(i)) >>> 0\n }\n return h\n}\n\n/**\n * Stable short hash for keying the virtual-CSS module per source-SFC id.\n *\n * djb2-style; collisions are tolerable here because (a) each entry stores its\n * own CSS body, so a hash collision would only matter if two distinct SFCs\n * hashed to the same key AND were processed concurrently; (b) collisions are\n * recoverable — Vite would simply load the wrong CSS for one SFC; we still\n * keyed on the unhashed id internally to avoid that. The hash only appears in\n * the bundled asset URL.\n *\n * @internal\n */\nexport function _hashIdForUtilityCss(id: string): string {\n return _hashId(id).toString(36)\n}\n\n/**\n * Deterministic 8-hex-char scope id for a light-DOM component's `data-a`\n * attribute (light-DOM leaf flip, LDF §10 step 1 / step 3). Same underlying\n * hash as `_hashIdForUtilityCss`, reformatted to a fixed-width hex string —\n * the `[data-a=\"<id>\"]` marker format LDF §11 Q4 ratifies is a hex string,\n * and a fixed width keeps every stamped attribute the same byte length.\n * Hashes the query-stripped file id (stable per file path across builds),\n * not file content — matches `_hashIdForUtilityCss`'s existing contract.\n *\n * @internal\n */\nexport function _lightScopeId(id: string): string {\n return _hashId(id).toString(16).padStart(8, '0')\n}\n\n/**\n * Bug 6 — `shadowMode === 'light'` branch.\n *\n * Routes utility CSS to Vite's CSS pipeline (which folds CSS imports into the\n * bundled `dist/assets/*.css` asset) instead of to `host.adoptedStyleSheets`\n * (a no-op on an element with no shadow root). Returns a prelude `import` that\n * the plugin's `resolveId` + `load` hooks resolve to the stored CSS body.\n *\n * The `__style__` shadow path is NOT invoked here — utility CSS for a\n * cascade-mode component MUST hit the global stylesheet, not a per-element\n * stylesheet that would be silently dropped by `HTMLElement`'s setter.\n *\n * Authored `@style` blocks still emit through the Rust codegen's `<style>`\n * node and are unaffected. (If a component opts into `shadowMode: 'light'` and\n * authors an `@style` block, the codegen still wires it through the\n * non-shadow path — that is the runtime's contract, not this hook's.)\n *\n * @internal\n */\nexport function _foldCssEngineStylesGlobal(\n compiledCode: string,\n css: string,\n id: string,\n): { code: string; virtualId: string } | null {\n if (!css.trim()) return null\n const hash = _hashIdForUtilityCss(id)\n const virtualId = `${VIRTUAL_UTILITY_PREFIX}${hash}.css`\n // Prepend the CSS import as a side-effect-only import so Vite's CSS plugin\n // hoists it into the bundle. We use the NULL-byte virtual id form\n // (Rollup/Vite convention for \"owned by this plugin\"); other plugins will\n // skip it. The compiler's transform returns this prepended code, which the\n // downstream esbuild/oxc strip leaves untouched (it's just an import).\n const prelude = `import ${JSON.stringify(virtualId)};\\n`\n return { code: prelude + compiledCode, virtualId }\n}\n\n// ─── v1.0.10a — compiler AST-export hook ─────────────────────────────────────\n//\n// Thin TS wrapper over the `aihu-compile --ast-json` flag. Returns the parsed\n// `.aihu` SFC AST in a stable, serializable shape consumed by the CSS engine's\n// AST scanner (`css-2-ast-scanner`). Mirrors the typed contract in\n// `docs/superpowers/specs/compiler-ast-export-hook.md` §4.\n\n/** Top-level AST export — one per .aihu SFC. */\nexport interface SfcAst {\n /** Resolved custom-element tag name (meta.name → route.name → file stem). */\n tag: string\n /** AST schema version — bumped on any breaking shape change (semver-tied). */\n astVersion: 1\n /** The @style block, if the SFC declared one. */\n style: SfcStyleBlock | null\n /** Parsed template tree. null when the SFC has no @template block. */\n template: SfcNode[] | null\n /** SFC-level metadata. */\n meta: SfcMeta\n /**\n * The compiler-assigned light-DOM scope id for this component's `data-a`\n * attribute, present only when it resolved to `shadowMode: 'light'`\n * (light-DOM leaf flip, LDF §10 step 1). Absent (not just `undefined`, the\n * key itself omitted on the wire) for shadow-mode components — additive,\n * mirrors `aihu-css-core`'s `SfcAst.light_scope_id: Option<String>`.\n */\n lightScopeId?: string\n}\n\nexport interface SfcStyleBlock {\n /** Verbatim CSS body of the @style block (braces stripped, $global token removed). */\n content: string\n /** 'scoped' (default) or 'global' (@style { $global ... }). */\n scope: 'scoped' | 'global'\n}\n\nexport interface SfcMeta {\n /** From @meta { name } / @route { name } / file stem — never null after resolution. */\n name: string\n}\n\n/** Discriminated union mirroring Rust `TemplateNode`. */\nexport type SfcNode =\n | { kind: 'element'; tag: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'macroElement'; name: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'text'; value: string }\n | { kind: 'interpolation'; expr: string }\n | { kind: 'ifBlock'; branches: Array<{ cond: string; body: SfcNode[] }> }\n | {\n kind: 'eachBlock'\n list: string\n item: string\n idx: string | null\n key: string | null\n body: SfcNode[]\n emptyBody: SfcNode[] | null\n }\n | { kind: 'htmlBlock'; expr: string }\n\n/** Discriminated union mirroring Rust `Attr` — the three class-forms key on `kind`. */\nexport type SfcAttr =\n | { kind: 'static'; name: string; value: string } // Form A\n | { kind: 'binding'; name: string; expr: string } // Form B\n | { kind: 'macro'; name: string; value: SfcMacroValue } // Form C (and on:/bind:/emit:/if/each/…)\n\nexport type SfcMacroValue =\n | { form: 'quoted'; value: string }\n | { form: 'curly'; expr: string }\n | { form: 'boolean' }\n\n/**\n * Parse a .aihu source string to its structured AST.\n *\n * Thin wrapper over the Rust binary (mirrors `transform()`): spawns\n * `aihu-compile --stdin --tag <stem> --ast-json`, feeds `source` on stdin, and\n * `JSON.parse`s stdout. `id` is optional and only used to derive the tag stem\n * and the `--path` arg (for `@route` C500 checks), identical to `transform()`.\n *\n * Throws on parse failure — the Rust binary exits non-zero and `execFileSync`\n * surfaces the diagnostic (same error path as `transform()`).\n */\n/**\n * Compile an SFC to its TYPE-CHECK SURFACE and return it as a string — the\n * `.aihu.ts` sidecar's content, without writing a file.\n *\n * This is the in-memory path `aihu-tsc` uses to hand `.aihu` files to TypeScript\n * as virtual files. The surface is line-preserving: line N of the returned text\n * corresponds to line N of the `.aihu` source, which is what lets a `tsc`\n * diagnostic be mapped straight back to the line the author wrote.\n *\n * Returns `''` when the SFC has no `@template` (nothing to check).\n */\nexport function compileSidecar(\n source: string,\n id?: string,\n options?: {\n /**\n * #486 step 4 — emit the attribute/component-prop type layer\n * (`--strict-templates`). Default off: the surface stays byte-identical\n * to the pre-#486 sidecar.\n */\n strictTemplates?: boolean\n /**\n * Build target threaded to the compiler binary (`--target`), same flag\n * `transform()` passes. Defaults to the binary's own default\n * (`universal`) when omitted.\n *\n * This is NOT cosmetic: `--target` changes what `compile_full_with_options`\n * produces (packages/compiler/src/bin/main.rs), which `sidecar_ts` is\n * derived from — e.g. a `target: 'client'` build elides server-only\n * artifacts. A caller that never passes this always type-checks against\n * the `universal` surface regardless of the project's actual configured\n * target, which can pass tsc on code the real build would elide or\n * reject. `islands`/`shadowMode` are deliberately NOT parameters here:\n * both are applied as JS-side post-processing on the RUNTIME JS output\n * (see `transform()`), never touch `sidecar_ts`, and have no bearing on\n * type-check accuracy.\n */\n target?: 'client' | 'server' | 'universal'\n },\n): string {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--sidecar-stdout']\n if (id) {\n args.push('--path', id)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n if (options?.target) {\n args.push('--target', options.target)\n }\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveBinPath()\n const startedAt = Date.now()\n try {\n return execFileSync(bin, args, {\n input: source,\n encoding: 'utf8',\n // Capture stderr rather than inheriting it: the compiler's warnings (unhyphenated\n // tag names, undeclared cross-block refs) belong to `aihu build`, and would\n // otherwise interleave with the type diagnostics a caller is trying to read.\n // A hard compile failure still throws, carrying the message with it.\n stdio: ['pipe', 'pipe', 'pipe'],\n ...compileSpawnBounds(source.length),\n })\n } catch (err) {\n throw describeSpawnFailure(err, bin, args, source.length, Date.now() - startedAt) ?? err\n }\n}\n\nexport function compileToAst(source: string, id?: string): SfcAst {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--ast-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): css-engine's `compileSfc`\n // re-derives the AST per file — with the memo, the second parse of an\n // unchanged file is a lookup, and a preceding `transform()` of the same\n // source has usually SEEDED this exact entry from its envelope (one parse\n // for js + ast + route). The cached value is the raw JSON string; parse per\n // call so callers can never mutate a shared AST object.\n const stamp = _backendStampPath()\n const json = _memoizedSpawn('ast', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['ast'],\n })\n if (reply.kind === 'legacy') return reply.output\n const ast = reply.envelope.astJson\n if (ast === undefined) {\n throw new Error('[@aihu/compiler] envelope reply missing astJson')\n }\n return ast\n })\n return JSON.parse(json) as SfcAst\n}\n\n/**\n * Structured `@route` metadata (the `.route.json` sidecar shape). All fields\n * optional — only what the SFC's `@route` block declares is present. `head` is\n * left opaque here (the router owns its shape).\n */\nexport interface RouteMeta {\n pattern?: string\n name?: string\n layout?: string\n middleware?: string[]\n ssr?: boolean\n params?: string[]\n head?: unknown\n /**\n * GX Phase 1 fan-out (#437-GX): the resolved `extract` policy — always\n * present in the binary's route-json output since 0.1.12 (the default is\n * recorded, never implied by absence). Typed loose here: consumers\n * normalize fail-closed (`deriveReadPolicy` in `@aihu/server`).\n */\n extract?: { read?: unknown; call?: unknown }\n /**\n * GX Phase 4 fan-out (#466): the `data:` governed-resource declaration\n * (70-governed-data-access §2.1) — present only when the route declares one\n * (0.1.14+). `type` keys the server registry's provider; `preview` lists\n * the locked-state fields (omitted when none declared). Consumers: the\n * server runtime's boot validation + generated loader, and the router Vite\n * layer's C486 sibling-loader conflict check (§4.7).\n */\n data?: { type?: string; preview?: string[] }\n}\n\n/**\n * Parse a `.aihu` source string and return its `@route` metadata, or `null`\n * when the SFC declares no `@route` block.\n *\n * Thin wrapper over the Rust binary (mirrors {@link compileToAst}): spawns\n * `aihu-compile --stdin --tag <stem> --route-json`, feeds `source` on stdin,\n * and `JSON.parse`s stdout. This is how build tools recover full route\n * metadata (`head`/`middleware`/`params`/`ssr`/`layout`) for the SPA build\n * path, where no `.route.json` sidecar is written to disk.\n *\n * Throws on parse failure (same error path as `transform()`/`compileToAst()`).\n */\nexport function compileRouteMeta(source: string, id?: string): RouteMeta | null {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--route-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): the router Vite plugin\n // calls this per route file per scan, and the SSG prerender's second server\n // triggers a full re-scan — and a preceding `transform()` of the same\n // source has usually SEEDED this entry from its envelope. Cache the raw\n // stdout; parse per call.\n const stamp = _backendStampPath()\n const out = _memoizedSpawn('route', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['route'],\n })\n if (reply.kind === 'legacy') return reply.output\n return reply.envelope.routeJson ?? 'null'\n }).trim()\n if (out === '' || out === 'null') return null\n return JSON.parse(out) as RouteMeta\n}\n\n/**\n * Inject `_setMount(mount)` + `_setSignal(signal)` auto-wiring into a compiled\n * `.aihu` module. Adds the necessary symbols to existing imports and inserts\n * the boot calls right after the last `import` statement.\n *\n * @internal\n */\nexport function _injectAutoWiring(code: string): string {\n // 1. Add `mount` to the @aihu/arbor import (or create it).\n let result: string\n if (code.includes(\"from '@aihu/arbor'\")) {\n result = code.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n } else {\n result = `import { mount } from '@aihu/arbor'\\n${code}`\n }\n\n // 2. Add `signal` to the non-type @aihu/signals import (or create it).\n // Note: `import\\s+\\{` does NOT match `import type {` (the regex needs `{` immediately\n // after whitespace, whereas `import type {` has `type` in between). No negation guard\n // is needed — the replace callback below already skips `import type` lines.\n if (/import\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result)) {\n // There IS a value import from signals — add `signal` if missing.\n result = result.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/signals'/,\n (_m: string, imports: string) => {\n // Skip type-only imports\n if (_m.startsWith('import type')) return _m\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('signal')) parts.push('signal')\n return `import { ${parts.join(', ')} } from '@aihu/signals'`\n },\n )\n } else if (!/^[^\\S\\n]*import\\b[^\\n]*from[^\\S\\n]*'@aihu\\/signals'/m.test(result)) {\n // No signals import at all — insert after arbor import\n result = result.replace(\n /import\\s*\\{[^{}]*\\}\\s*from\\s*'@aihu\\/arbor'/,\n (m: string) => `${m}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n // If only `import type { Signal }` exists, insert value import after it\n else if (\n /import\\s+type\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result) &&\n !result.match(/import\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/)\n ) {\n result = result.replace(\n /(import\\s+type\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals')/,\n (_m: string, typeImport: string) => `${typeImport}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n\n // 3. Add `_setMount`, `_setSignal` to the @aihu/runtime import.\n result = result.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_setMount')) parts.push('_setMount')\n if (!parts.includes('_setSignal')) parts.push('_setSignal')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // 4. Insert boot calls after the last `import` statement.\n const lines = result.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, '_setMount(mount)', '_setSignal(signal)', '')\n result = lines.join('\\n')\n }\n\n return result\n}\n\n/**\n * Vite plugin that compiles .aihu files to TypeScript during build and dev.\n *\n * Use `enforce: 'pre'` so the hook fires before Vite/Rollup's built-in\n * parsers attempt to process the raw .aihu content as JavaScript.\n *\n * @example\n * // vite.config.ts\n * import { aihuCompilerPlugin } from '@aihu/compiler'\n * export default { plugins: [aihuCompilerPlugin()] }\n *\n * **Known Limitation — Bun + Rollup4 ESM incompatibility (v0):**\n *\n * `bun vite build` fails in the `fixtures/vite-counter` fixture with two\n * cascading errors:\n *\n * 1. **Missing devDependency:** `vite` is declared only as an optional\n * `peerDependency` in `packages/compiler/package.json`. Bun does not\n * install optional peers automatically, so `bun vite build` exits\n * immediately with `Cannot find package 'vite'`.\n *\n * 2. **Bun + Rollup4 bridge:** Even with Vite installed, Bun processes\n * `vite.config.ts` through its own internal bundler before handing off\n * to Rollup4. When `@aihu/compiler` is resolved from the workspace\n * symlink (`dist/index.js`), Bun's ESM loader evaluates the module at\n * config-load time. The subprocess call inside `transform()` depends on\n * the Rust binary being at `../bin/aihu-compile` relative to `dist/`\n * (written by the postinstall hook). In a dev workspace where postinstall\n * has not run, this path does not exist and `execFileSync` throws. Bun surfaces\n * the error as a config-load failure, not a per-file transform error,\n * causing the entire build to abort before any `.aihu` file is\n * processed.\n *\n * **Workaround (v0):** Use `bun run integrate.ts` directly from\n * `packages/compiler/fixtures/vite-counter/`. This script calls\n * `transform()` from `@aihu/compiler` without involving Vite or Rollup.\n * Preconditions: (1) `cargo build --release` in `packages/compiler/`,\n * (2) `bun install` at the repo root.\n *\n * **v1 resolution:** Add `vite` as a `devDependency` in\n * `packages/compiler/package.json`; add a WASM or pre-built binary\n * strategy so the Rust binary is bundled with the npm package and does not\n * require a separate `cargo build --release` step.\n */\n/**\n * Minimal structural type for the `@aihu/css-engine` module surface this\n * plugin uses. Declared locally so the compiler never type-imports the\n * css-engine package (which would create a compile-time edge against an\n * optional peer that may be absent).\n *\n * @internal\n */\ninterface CssEngineModule {\n compileSfc(source: string, id?: string, lightScopeId?: string): string\n}\n\n// Memoised resolution of the optional `@aihu/css-engine` peer. `undefined`\n// = not yet attempted; `null` = attempted and unavailable (no-op path);\n// a module object = available. The dynamic import is attempted once per\n// process — repeated absence does not re-pay the resolution cost.\nlet _cssEngine: CssEngineModule | null | undefined\n\n// Whether we've already surfaced a one-shot warning that css-engine resolved\n// but `compileSfc` threw (typically: native css-core binary unresolvable in\n// the consumer's install — e.g. lockfile pins the per-platform placeholder\n// version). The transform stays non-fatal, but going fully silent leaves users\n// chasing \"why did my utility classes never emit?\". One warn per process.\nlet _cssEngineWarned = false\n\n// The optional-peer module specifier, held in a VARIABLE so TypeScript never\n// statically resolves `@aihu/css-engine`'s declarations at typecheck time.\n// css-engine depends on @aihu/compiler for its AST, so the two form a\n// circular package relationship; under CI's frozen install + moon build\n// ordering, css-engine's `dist`/`.d.ts` are not guaranteed to exist when\n// `compiler:typecheck` runs. A literal `import('@aihu/css-engine')` makes the\n// compiler emit TS2307 in that window (the `as` cast affects the RESULT type\n// only, not whether TS attempts module resolution). Resolving through this\n// variable keeps the import fully dynamic — no compile-time edge on the peer.\nconst _CSS_ENGINE_SPECIFIER = '@aihu/css-engine'\n\n/**\n * Resolve the optional CSS engine from the compiler package first, then from\n * the Vite consumer. Package managers commonly realpath a published plugin\n * into their store, where an optional peer is not a physical sibling even\n * though the application has installed it. The consumer fallback keeps the\n * integration opt-in while making published and workspace compiler builds\n * behave the same way.\n */\nasync function _loadCssEngine(): Promise<CssEngineModule | null> {\n try {\n return (await import(_CSS_ENGINE_SPECIFIER)) as unknown as CssEngineModule\n } catch {\n try {\n const fromConsumer = createRequire(join(process.cwd(), 'package.json'))\n const entry = fromConsumer.resolve(_CSS_ENGINE_SPECIFIER)\n return (await import(pathToFileURL(entry).href)) as unknown as CssEngineModule\n } catch {\n return null\n }\n }\n}\n\n/**\n * Lazily resolve `@aihu/css-engine` and compile a `.aihu` source's utility\n * classes to scoped CSS. Returns `''` when css-engine is not installed\n * (the optional-peer no-op path) or when compilation fails for any reason —\n * a CSS-engine failure MUST NOT break an otherwise-valid `.aihu` build.\n *\n * Sets `process.env.AIHU_COMPILE_BIN` to this plugin's resolved compiler\n * binary before calling `compileSfc`: css-engine re-derives the SFC AST via\n * its own bundled copy of `compileToAst`, whose binary path is resolved\n * relative to the css-engine package — which does NOT ship the compiler\n * binary. Pointing it at our `binPath` guarantees the AST css-engine parses\n * is produced by the exact same compiler this build uses.\n *\n * @internal\n */\nasync function _maybeCompileUtilityCss(\n source: string,\n id: string,\n lightScopeId?: string,\n): Promise<string> {\n if (_cssEngine === null) return ''\n // Ensure css-engine's bundled `compileToAst` spawns the SAME compiler\n // binary this plugin uses (it has no compiler binary of its own). Set\n // this BEFORE the dynamic import so that any module-load-time evaluation\n // of `process.env.AIHU_COMPILE_BIN` in css-engine's bundled dist (older\n // bundles capture this into a module-scope const at line 8 of\n // `packages/css-engine/dist/index.js`) sees the correct value. After Bug 6,\n // the source `compileToAst` resolves the bin lazily on each call, so once\n // css-engine is rebuilt this set-before-import is belt-and-braces.\n if (process.env.AIHU_COMPILE_BIN == null) {\n try {\n process.env.AIHU_COMPILE_BIN = resolveBinPath()\n } catch {\n // No CLI binary resolvable (e.g. an addon-only install). css-engine's\n // NEWER bundles route compileToAst through the same native backend and\n // never read this var; older bundles will surface their own resolver\n // error, which the compileSfc try/catch below already treats as the\n // non-fatal no-op path.\n }\n }\n if (_cssEngine === undefined) {\n // Guarded, lazy, OPTIONAL — resolving through `_CSS_ENGINE_SPECIFIER`\n // keeps TypeScript from taking a compile-time dependency on css-engine.\n _cssEngine = await _loadCssEngine()\n if (_cssEngine === null) return ''\n }\n try {\n return _cssEngine.compileSfc(source, id, lightScopeId)\n } catch (err) {\n // A css-engine compile failure is non-fatal: fall back to the no-op\n // path (utility classes don't emit) rather than aborting the build.\n // BUT — silently swallowing this means a user who clearly intends\n // css-engine to be active (the peer resolved) will never know their\n // utility classes are inert. Surface a one-shot warning with the\n // underlying error + an install/upgrade hint. Idempotent per process.\n if (!_cssEngineWarned) {\n _cssEngineWarned = true\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(\n `[@aihu/compiler] @aihu/css-engine is installed but compileSfc() failed; ` +\n `utility classes will not emit. Original error: ${msg}\\n` +\n `Hint: ensure the native css-core binary is installed ` +\n `(install/upgrade @aihu/css-engine + its per-platform optional dep, ` +\n `or run \\`cargo build --release -p aihu-css-core\\` in a dev clone).`,\n )\n }\n return ''\n }\n}\n\nexport function aihuCompilerPlugin(options?: AihuCompilerPluginOptions): VitePlugin {\n const islandsEnabled = options?.islands !== false\n const shadowMode = options?.shadowMode\n const target = options?.target\n const layoutsDir = options?.layoutsDir ?? 'src/layouts'\n\n // Bug 6 — per-instance store of virtual utility-CSS modules. Keyed by the\n // full virtual id (NUL-prefixed). Populated by the transform hook when\n // `shadowMode === 'light'` produces utility CSS; drained by the `load` hook\n // when Vite's CSS pipeline asks for the module body. Lives on the plugin\n // instance so multiple `aihuCompilerPlugin()` calls in the same build don't\n // alias each other's css.\n const utilityCssStore = new Map<string, string>()\n\n // GX Phase 1 (#437-GX) — per-instance extract census. Keyed by rawId,\n // populated in transform from the compiler's `// @aihu:extract` marker;\n // printed per-value in buildEnd. Every build prints the posture\n // distribution (the DA-e census pattern from #437).\n const extractCensus = new Map<string, { read: string; call: string }>()\n\n return {\n name: 'aihu-compiler',\n enforce: 'pre',\n buildEnd() {\n for (const line of _formatExtractCensus(extractCensus)) console.info(line)\n },\n resolveId(source) {\n // Own all `\\0virtual:aihu-utility/<hash>.css` ids so Vite's resolver\n // doesn't try to find them on disk. Returning the id verbatim is the\n // Rollup convention for \"I'll handle the load.\"\n if (source.startsWith(VIRTUAL_UTILITY_PREFIX)) return source\n return null\n },\n load(id) {\n if (!id.startsWith(VIRTUAL_UTILITY_PREFIX)) return null\n // Vite's CSS pipeline runs on the returned source because the id ends\n // in `.css` — it parses, minifies (in build), and hoists into a CSS\n // asset chunk that lands in `dist/assets/<name>-<hash>.css`.\n return utilityCssStore.get(id) ?? null\n },\n transform(code, id) {\n // Strip Vite query strings (e.g. `?import`, `?t=...`) before checking the extension.\n const rawId = id.split('?')[0]!\n if (!rawId.endsWith('.aihu')) return\n // Server-environment detection (Vite Environment API). A `.aihu` loaded\n // through an SSR module runner — the `output: 'static'` prerender's\n // `ssrLoadModule` (packages/app/src/prerender.ts), or any `vite dev` SSR\n // consumer — must NOT get the client/universal target: that emits a\n // module-level `new CSSStyleSheet()` and an unguarded `customElements`\n // registration, both of which throw in the DOM-less SSR runner\n // (`CSSStyleSheet is not defined`) and silently degrade the prerender to\n // an empty SPA shell. The `server` target guards DOM registration behind\n // `typeof customElements` and exports `__ssr` (a host-less arbor factory)\n // plus the `__aihu_ssr_string__` compiled string fast path that\n // @aihu/server's `renderToString` prefers — a pure string concatenation\n // that needs no DOM. An explicit `target` option still wins (a caller\n // that pins a target owns the consequences); we only fill the SSR default\n // when none was configured. `this.environment` is absent on pre-Environment\n // -API Vite, in which case the prior universal default stands.\n const isServerEnv =\n (this as { environment?: { config?: { consumer?: string } } })?.environment?.config\n ?.consumer === 'server'\n const effectiveTarget = target ?? (isServerEnv ? 'server' : undefined)\n return (async () => {\n // No `.aihu.ts` sidecar is written any more. Type-checking goes through\n // `aihu-tsc`, which projects each `.aihu` into the TypeScript program as a\n // VIRTUAL file — so the type-check surface never lands on disk beside the\n // source, where authors saw it, editors indexed it, and `.gitignore` had to\n // hide it. A build has no business writing type-checker inputs at all.\n //\n // Layout SFCs (under the layouts dir) compile in layout mode: a\n // namespaced `aihu-layout-<stem>` tag + a passive <outlet> marker.\n const isLayout = _isLayoutFile(rawId, layoutsDir)\n const layoutTag = isLayout ? _layoutTag(basename(rawId, '.aihu')) : undefined\n const tOpts = {\n ...(effectiveTarget ? { target: effectiveTarget } : {}),\n ...(layoutTag ? { tag: layoutTag } : {}),\n }\n const result = transform(code, rawId, tOpts)\n // GX Phase 1 (#437-GX) — record this surface's resolved extract policy\n // for the build census (client-target builds carry no marker: policy\n // never reaches client artifacts).\n const extractMarker = _parseExtractMarker(result.code)\n if (extractMarker) extractCensus.set(rawId, extractMarker)\n // §9.4 per-file shadow override: the Rust `$shadow` macro emits a leading\n // `// @aihu:shadow <mode>` marker; it wins over the plugin's global\n // shadowMode and drives BOTH _injectShadowMode and the css fold branch.\n const perFileShadow = /^\\/\\/ @aihu:shadow (light|shadow)\\b/m.exec(result.code)?.[1] as\n | 'light'\n | 'shadow'\n | undefined\n // DA4 (#437, the ratified flip) — the IMPLICIT page default: for an\n // `@route` unit with no `$shadow` pin the compiler emits the DISTINCT\n // default-marker token `// @aihu:shadow-default light`. Layout SFCs\n // (no `@route` block, so no compiler marker) get the same 'light'\n // default from `_isLayoutFile`. Precedence, ratified: `$shadow` pin >\n // plugin-global `shadowMode` config > page/layout default 'light' >\n // leaf default 'shadow' (the runtime's `?? 'shadow'` when nothing is\n // injected) — so an explicit plugin-global config still outranks the\n // implicit default, which is why this is not the pin marker.\n const perFileShadowDefault = /^\\/\\/ @aihu:shadow-default (light|shadow)\\b/m.exec(\n result.code,\n )?.[1] as 'light' | 'shadow' | undefined\n const impliedShadowDefault = perFileShadowDefault ?? (isLayout ? 'light' : undefined)\n const effectiveShadow = perFileShadow ?? shadowMode ?? impliedShadowDefault\n\n // Light-DOM leaf flip prep (LDF §10 step 1/3): a deterministic scope\n // id for this component's `data-a` attribute, only when it actually\n // resolved to light mode. `undefined` in the shadow case — mirrors\n // `SfcAst.light_scope_id: Option<String>` being `None` on the Rust\n // side. Computed BEFORE the shadowMode injection below so it can\n // ride in the SAME injected options object (`_injectShadowMode`'s\n // doc comment explains why one merged injection, not two).\n const lightScopeId = effectiveShadow === 'light' ? _lightScopeId(rawId) : undefined\n\n let compiled =\n effectiveShadow != null\n ? _injectShadowMode(result.code, effectiveShadow, lightScopeId)\n : result.code\n // LDF §10 step 3, server side: fill the Rust codegen's\n // `__AIHU_LIGHT_SCOPE_ID__` placeholder so the compiled `__ssrString`\n // stamps `data-a` on its own root by default (no-op when the target\n // carries no string renderer).\n if (lightScopeId) compiled = _injectLightScopeId(compiled, lightScopeId)\n // Light-DOM: the authored `@style` block compiled to a per-instance\n // `host.adoptedStyleSheets` assignment, but a light-DOM host has no\n // shadow root so that setter is a no-op. Redirect the module-level\n // sheet to `document.adoptedStyleSheets` (idempotent) so authored recipe\n // CSS reaches the global cascade alongside the css-engine utility CSS.\n if (effectiveShadow === 'light') compiled = _globalizeAuthoredStyle(compiled)\n if (isLayout) compiled = _passivizeOutlet(compiled)\n\n // ── css-engine hook (optional, lazy, no circular dep) ──────────────\n // @aihu/css-engine depends on @aihu/compiler (for its AST), so the\n // compiler MUST NOT hard-depend on it. It is declared an OPTIONAL\n // peerDependency and pulled in ONLY via this guarded dynamic import:\n // when present, we compile the SFC's utility classes to scoped CSS\n // and fold it into the component's shadow `<style>`; when absent the\n // import throws and we no-op (utility classes simply don't emit —\n // the pre-hook behaviour). This keeps css-engine an opt-in enhancement\n // with zero dependency cycle.\n const utilityCss = await _maybeCompileUtilityCss(code, rawId, lightScopeId)\n if (utilityCss) {\n if (effectiveShadow === 'light') {\n // Bug 6 — no shadow root → `host.adoptedStyleSheets` is a no-op.\n // Route utility CSS through Vite's CSS pipeline via a virtual\n // `.css` import so it lands in `dist/assets/*.css` and reaches the\n // global cascade. The authored `@style` block (if any) still\n // emits via the Rust codegen's normal path and is unaffected.\n const folded = _foldCssEngineStylesGlobal(compiled, utilityCss, rawId)\n if (folded) {\n utilityCssStore.set(folded.virtualId, utilityCss)\n compiled = folded.code\n }\n } else {\n // `shadowMode: 'shadow'`: fold into the\n // per-component `CSSStyleSheet` adopted by the shadow root.\n compiled = _foldCssEngineStyles(compiled, utilityCss)\n // …and into the SERVER target's `__aihu_css__`, which carries the\n // same rules into the declarative shadow template. A shadow root is\n // style-isolated, so anything missing here paints unstyled until\n // the component's chunk loads.\n if (isServerEnv) compiled = _foldSsrCssExport(compiled, utilityCss)\n }\n }\n\n const elementTag = _extractElementTag(compiled)\n\n let out: string\n\n // §9.4 — a base-extending recipe (`defineComponent({ base: X, ... })`)\n // MUST take the full defineComponent/defineElement path: the static\n // island shim inlines `class extends HTMLElement` and cannot honor a\n // base class. Force-classify it interactive regardless of signal usage.\n const hasBase = _hasBaseRecipe(compiled)\n\n // Plan 3.3 — static-island fast path. Bypasses HMR injection because\n // a component with no signals has no setup state to hot-replace.\n // Static islands strip @aihu/runtime entirely — do NOT inject auto-wiring\n // (it would reference _setMount/_setSignal as undefined identifiers).\n //\n // DA4 (#437): like `hasBase`, a light-DOM component cannot take the\n // static-island shim — the shim inlines `attachShadow({ mode: 'open' })`\n // and cannot honor `shadowMode: 'light'` (and its tail rewrite does not\n // match the injected options argument). Keep the full runtime path so\n // the injected `{ shadowMode: 'light' }` reaches defineElement.\n if (isServerEnv) {\n // Server module-runner target (the SSG prerender's `ssrLoadModule`,\n // any dev-SSR consumer). The `server` compile target already exports\n // the host-less `__ssr` factory + the `__aihu_ssr_string__` string\n // fast path and guards its own custom-element registration behind\n // `typeof customElements`. The client-only instrumentation below is\n // wrong here: HMR (`_buildHmrCode`) prepends a `let __aihu_setup__`\n // slot that COLLIDES with the server target's named\n // `const __aihu_setup__` (duplicate declaration); the static-island\n // shim, `defer` hydration, and auto-wiring all inject browser mount\n // code the SSR render never runs. Emit the server target as-is; the\n // TS-strip below still applies.\n out = compiled\n // LDF §10 step 3, server side: expose the compiler-assigned light-DOM\n // scope id so an SSR/SSG caller (e.g. `@aihu/app`'s prerender) can\n // pass it to `renderToString` as `SsrOptions.lightScopeId` and stamp\n // `data-a` on the prerendered root. This is the SAME id the client\n // transform injects into `defineElement` options and the css fold\n // wrote into the emitted `@scope([data-a=\"…\"])` blocks — exporting it\n // from the compiled module keeps a single source of truth (no\n // consumer ever re-derives the hash). Server target only: the client\n // runtime stamps at `connectedCallback` and needs no export.\n if (lightScopeId) {\n out += `\\nexport const __aihu_light_scope__ = '${lightScopeId}'\\n`\n }\n // The component's registered custom-element tag, exported for the\n // same reason and on the same channel as the scope id above.\n //\n // SSR renders a component's TEMPLATE, not the component: the output\n // is the template root (`<div class=\"dn-docs\">`), while the client\n // builds `document.createElement('aihu-layout-docs')` and puts the\n // template inside it. The two shapes therefore never match, so the\n // client cannot adopt the prerendered subtree and replaces it\n // wholesale — measured on apps/docs: 0 of 391 prerendered nodes\n // survive hydration.\n //\n // Exporting the tag lets an SSG/SSR caller wrap the render in the\n // real host element (`SsrOptions.wrapTag`). It also puts `data-a`\n // where the client puts it: `define-element.ts` stamps the HOST in\n // its constructor, and its comment already asserts \"a server-rendered\n // element already carries `data-a`\" — which only becomes true once\n // the host exists in server output.\n if (elementTag !== null) {\n out += `\\nexport const __aihu_tag__ = '${elementTag}'\\n`\n }\n // The component's RESOLVED shadow mode, on the same channel and for\n // the same single-source reason as the two exports above.\n //\n // An SSR caller rendering a nested custom element has to emit two\n // different shapes: a light-DOM component's tree is the host's own\n // children, while a shadow component's tree belongs inside a\n // `<template shadowrootmode=\"open\">` so the browser attaches a\n // declarative shadow root while parsing. Getting that wrong is not a\n // cosmetic error — light children under a host that later calls\n // `attachShadow` are discarded on upgrade (\"adopt or discard, never\n // slot-project\", define-component.ts), so the content would paint and\n // then vanish.\n //\n // `effectiveShadow` is the value that ALREADY drives both\n // `_injectShadowMode` and the css-fold branch, so exporting it keeps\n // one resolution (plugin config > per-file directive > page/layout\n // default) rather than letting a consumer re-derive from the presence\n // of `__aihu_light_scope__` — which is an inference, not a signal.\n //\n // Deliberately aihu's OWN vocabulary ('light' | 'shadow'), never the\n // DOM's ShadowRootMode ('open' | 'closed'). Those are different enums\n // that share the word \"mode\"; the translation to `shadowrootmode`\n // happens once, at serialization, in the renderer.\n // Guarded on non-null for the same reason `_injectShadowMode` is\n // (`effectiveShadow != null`, below): when no mode resolves there is\n // nothing to assert, and emitting the string 'undefined' would be a\n // lie a consumer would branch on.\n if (effectiveShadow != null) {\n out += `\\nexport const __aihu_shadow__ = '${effectiveShadow}'\\n`\n }\n // Every component tag this module's template references, on the same\n // channel as the three exports above. `buildChildRegistry`\n // (`@aihu/server`) reads it as the edge set for its cycle check over\n // the WHOLE discovered component graph — not a per-page transitive\n // walk; the caller indexes every discovered module once rather than\n // loading a subset by following these tags (see child-registry.ts's\n // module docblock for why). A cycle found there is reported, not\n // rejected: `__aihu_schild` bounds it with a depth cap and an output\n // budget, so a build no longer has to refuse a legal recursive shape\n // to stay safe.\n //\n // DERIVED FROM THE EMITTED CALLS, not re-computed from the template.\n // The set that matters is precisely the set of tags the compiled\n // renderer will look up at runtime, and reading the `__aihu_schild`\n // call sites IS that set. Deriving it a second way — walking the\n // template again and reapplying the emitter's v1 boundaries (no\n // attrs, no children, static non-root path) — would be one rule\n // written in two places, and the halves would drift the first time a\n // boundary moved. The parity test in\n // `packages/compiler/tests/light-scope-export.test.ts` (the\n // `__aihu_child_tags__ export` describe block) pins that the export\n // and the call sites agree.\n //\n // Omitted entirely when the template references no component, so a\n // consumer can treat \"no export\" and \"empty\" identically.\n //\n // This set answers ONE question — \"what will the compiled renderer\n // look up?\" — and `__aihu_referenced_tags__` below answers a different\n // one. The paragraph above argues against deriving THIS set a second\n // way; it is not an argument against a second, differently-defined\n // set, and the two must not be collapsed. See below.\n // ONE shared derivation — `_deriveChildTags`, which `@aihu/router`'s\n // `genSC` calls on the same channel to build the server-bundle\n // component registry at codegen time. See its docblock.\n const childTags = _deriveChildTags(out)\n if (childTags.length > 0) {\n out += `\\nexport const __aihu_child_tags__ = ${JSON.stringify(childTags)}\\n`\n }\n // §22 — every component tag this module's template REFERENCES, which\n // is a strictly larger set than `__aihu_child_tags__` above and exists\n // for a different consumer.\n //\n // `__aihu_child_tags__` is the runtime edge set: the tags the compiled\n // renderer will actually look up, which is why deriving it from the\n // emitted `__aihu_schild(` call sites is not just convenient but\n // CORRECT for its consumer (`buildChildRegistry`'s cycle check).\n //\n // But `@aihu/app`'s prerender reads a tag set for two DIAGNOSTICS —\n // \"is this broken component referenced by anything?\" and \"is this tag\n // resolvable?\" — and for those questions the call-site set is the\n // wrong one. A reference the emitter DECLINES under the v1 child\n // boundaries (an attribute, children, a root/dynamic path) produces no\n // call site and therefore no tag, so the component is judged\n // unreferenced and the diagnostic stays silent about a component that\n // genuinely cannot load. Observed: `apps/docs`'s `pages/index.aihu`\n // references `<weather-demo city=\"London\">`, the attribute makes the\n // emitter decline it, the page compiles to ZERO `__aihu_schild` call\n // sites — and `weather-demo.aihu` really does fail to load under SSR\n // (`new CSSStyleSheet()` at module top level) with the build saying\n // nothing.\n //\n // So: two exports, two meanings, two derivations — NOT one rule\n // written twice. This one comes from the template AST, via the\n // `// @aihu:component-tags` marker `collect_component_tags` emits (the\n // same walk that fills `route.json`'s components array), so it is\n // independent of where the emitter's boundaries happen to sit and does\n // not move when they do.\n //\n // Parsed from `compiled`, NOT `out`: the marker is a comment the Rust\n // codegen emits, and the TS-strip at the end of this hook is free to\n // drop comments. `_parseIslandMarker(compiled)` reads the same channel\n // the same way for the same reason.\n //\n // Omitted entirely when the marker is absent or empty, so \"no export\"\n // and \"empty\" mean the same thing — the rule `__aihu_child_tags__`\n // already follows.\n const referencedTags = _parseComponentTagsMarker(compiled)\n if (referencedTags.length > 0) {\n out += `\\nexport const __aihu_referenced_tags__ = ${JSON.stringify(referencedTags)}\\n`\n }\n } else if (\n islandsEnabled &&\n elementTag !== null &&\n !hasBase &&\n effectiveShadow !== 'light' &&\n _parseIslandMarker(compiled) === 'static'\n ) {\n out = _buildStaticIsland(compiled, elementTag)\n } else if (elementTag !== null) {\n // Inject HMR instrumentation. The injected block is gated on\n // `typeof __DEV__ !== 'undefined' && __DEV__` so production\n // bundlers dead-code-eliminate it when they set __DEV__ = false.\n out = _buildHmrCode(compiled, elementTag)\n // Plan 3.3 — interactive islands also gain `defer` attribute\n // support so individual instances can opt into lazy hydration.\n out = _buildDeferredHydration(out, elementTag)\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n } else {\n out = compiled\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n }\n\n // The Rust compiler emits TypeScript (type casts, import type, etc.) and\n // the injected HMR / defer helpers also contain TS generics and casts.\n // Vite does NOT re-run its TS-strip step when a plugin returns code for a\n // non-.ts ID, so we must strip types ourselves before returning.\n //\n // TWO steps with SEPARATE failure handling, and the split is the whole\n // point. `import('vite')` is the ONLY one whose failure is legitimate —\n // a standalone `transform()` caller, a unit test, any host that is not a\n // Vite build has no Vite to strip with, and handing the TypeScript back\n // untouched is the correct answer there. Everything AFTER that import\n // runs with Vite proven present, so a failure there means the STRIP\n // broke, and swallowing it returns un-stripped TypeScript that\n // resurfaces hundreds of lines later as an unrelated bundler\n // `PARSE_ERROR` naming the user's `.aihu` file. One `catch` around both\n // did exactly that. `_isViteMissing` is how the two are told apart;\n // `_stripTypes` owns the branch order and the loud failures.\n let vite: typeof import('vite')\n try {\n vite = await import('vite')\n } catch (err) {\n if (_isViteMissing(err)) return { code: out, map: null }\n throw new Error(\n `[@aihu/compiler] Could not load \\`vite\\` to strip TypeScript from ${rawId}. ` +\n 'Vite appears to be installed but failed to load, so this is NOT the ' +\n '\"running outside Vite\" case and the TypeScript must not be handed ' +\n `back un-stripped. Underlying error: ${_errMessage(err)}`,\n { cause: err },\n )\n }\n // Vite's public return type has changed across supported releases, but\n // this boundary only reads `code` after the runtime capability checks\n // in `_stripTypes`. Keep the version-specific module type out of the\n // compiler's stable seam.\n return await _stripTypes(vite as unknown as ViteStripApi, out, rawId, isServerEnv)\n })()\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["__dirname"],"sources":["../js/native.ts","../js/spawn-bounds.ts","../js/envelope.ts","../js/transform-memo.ts","../js/index.ts"],"sourcesContent":["/**\n * @aihu/compiler native-addon loader — the in-process compile fast path.\n *\n * A `native.ts`-style loader in the mold of packages/server/src/native.ts:\n * platform matrix → per-platform optionalDependency package → dev fallback,\n * with explicit escape hatches and a cached three-state result. The addon\n * (packages/compiler/src-native, napi-rs) exposes\n * `compileEnvelope(source, optionsJson) → envelopeJson` — one boundary\n * crossing per file — which `js/envelope.ts` routes `transform()` /\n * `compileToAst()` / `compileRouteMeta()` through.\n *\n * States:\n * - loaded: addon required successfully; compiles run in-process.\n * - disabled: `AIHU_COMPILER_NATIVE=0` — the documented escape hatch.\n * - unavailable: no addon for this platform / load failed. UNLIKE\n * @aihu/server's fail-loud contract, a failed load here is a\n * one-shot WARNING, not a throw: the CLI spawn path is a\n * byte-identical fallback that always exists, so failing the\n * whole build over a fast-path load would be strictly worse.\n * The exception is `AIHU_COMPILER_NATIVE_ADDON=<path>` — an\n * explicit override that fails loud (same doctrine as\n * AIHU_COMPILE_BIN: a pinned path that doesn't load is a\n * configuration error, and silently ignoring it hands back a\n * plausible-looking wrong backend).\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport interface CompilerNativeAddon {\n compileEnvelope(source: string, optionsJson: string): string\n /**\n * The version string baked into the addon at build time. Present since\n * @aihu/compiler-native 0.1.x; ABSENT on older addons, which is why every\n * consumer must treat a missing method as \"unknown/incompatible\" rather\n * than assuming it is there (see envelope.ts's version handshake).\n */\n compilerVersion?(): string\n}\n\nexport interface NativePlatformDescriptor {\n readonly platformId: string\n readonly packageName: string\n readonly nodeFile: string\n}\n\nfunction detectPlatform(): NativePlatformDescriptor | null {\n if (typeof process === 'undefined' || !process.platform || !process.arch) {\n return null\n }\n const key = `${process.platform}-${process.arch}`\n switch (key) {\n case 'darwin-arm64':\n return {\n platformId: 'darwin-arm64',\n packageName: '@aihu/compiler-native-darwin-arm64',\n nodeFile: 'aihu-compiler-native.darwin-arm64.node',\n }\n case 'darwin-x64':\n return {\n platformId: 'darwin-x64',\n packageName: '@aihu/compiler-native-darwin-x64',\n nodeFile: 'aihu-compiler-native.darwin-x64.node',\n }\n case 'linux-x64':\n return {\n platformId: 'linux-x64-gnu',\n packageName: '@aihu/compiler-native-linux-x64-gnu',\n nodeFile: 'aihu-compiler-native.linux-x64-gnu.node',\n }\n case 'linux-arm64':\n return {\n platformId: 'linux-arm64-gnu',\n packageName: '@aihu/compiler-native-linux-arm64-gnu',\n nodeFile: 'aihu-compiler-native.linux-arm64-gnu.node',\n }\n case 'win32-x64':\n return {\n platformId: 'win32-x64-msvc',\n packageName: '@aihu/compiler-native-win32-x64-msvc',\n nodeFile: 'aihu-compiler-native.win32-x64-msvc.node',\n }\n default:\n return null\n }\n}\n\n/**\n * Where a loaded addon came from. This decides whether the version handshake\n * in envelope.ts can say anything meaningful about it:\n *\n * - `package` — a published per-platform optionalDependency. Its release\n * version is knowable (`packageVersion`) and is exactly the\n * thing `packages/compiler/package.json` pins, so it CAN be\n * compared against the pin.\n * - `dev-build` — `src-native/aihu-compiler-native.node`, produced by\n * `scripts/build-native.ts` from THIS source tree. It has no\n * release version at all; it is trusted by construction.\n * - `override` — `AIHU_COMPILER_NATIVE_ADDON=<path>`. Gated like `package`\n * when the pinned file turns out to live inside a published\n * platform package, trusted like `dev-build` otherwise.\n */\nexport type CompilerNativeOrigin = 'package' | 'dev-build' | 'override'\n\nexport type CompilerNativeState =\n | {\n kind: 'loaded'\n addon: CompilerNativeAddon\n addonPath: string\n origin: CompilerNativeOrigin\n /**\n * The `version` of the published per-platform package the addon was\n * loaded from, or `null` when the addon did not come from one (a local\n * cargo build). NOT the crate version reported by `compilerVersion()`:\n * `src-native/Cargo.toml` is pinned at 0.1.0 and never bumped per\n * release, so `CARGO_PKG_VERSION` cannot identify a release. The npm\n * package version can, and it is what the pin in\n * `packages/compiler/package.json` is expressed in.\n */\n packageVersion: string | null\n }\n | { kind: 'disabled' }\n | { kind: 'unavailable'; error?: Error }\n\nlet _state: CompilerNativeState | null = null\nlet _warnedLoadFailure = false\n\n/**\n * Read the `version` of the published platform package that owns `addonPath`.\n *\n * Deliberately NOT a walk up to the nearest package.json: the dev candidate\n * lives at `packages/compiler/src-native/…`, whose nearest ancestor manifest is\n * `@aihu/compiler` itself (version 1.2.2) — reading that would report a\n * confidently wrong \"addon version\". Only a manifest sitting in the SAME\n * directory as the `.node`, and naming an `@aihu/compiler-native-*` package,\n * counts.\n */\nfunction readAddonPackageVersion(addonPath: string): string | null {\n try {\n const manifestPath = join(dirname(addonPath), 'package.json')\n if (!existsSync(manifestPath)) return null\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as {\n name?: unknown\n version?: unknown\n }\n if (typeof manifest.name !== 'string' || !manifest.name.startsWith('@aihu/compiler-native-')) {\n return null\n }\n return typeof manifest.version === 'string' ? manifest.version : null\n } catch {\n return null\n }\n}\n\n/**\n * The per-platform addon package for the current platform, or `null` on a\n * platform with no prebuilt addon. Exported so envelope.ts can look the pinned\n * version up in `packages/compiler/package.json`'s optionalDependencies.\n */\nexport function nativePlatformDescriptor(): NativePlatformDescriptor | null {\n return detectPlatform()\n}\n\nfunction isAddonShaped(mod: unknown): mod is CompilerNativeAddon {\n return (\n typeof mod === 'object' &&\n mod !== null &&\n typeof (mod as CompilerNativeAddon).compileEnvelope === 'function'\n )\n}\n\n/**\n * Resolve (and cache) the native compiler addon. This is the ONLY module in\n * @aihu/compiler that requires a napi `.node` file.\n */\nexport function loadCompilerNative(): CompilerNativeState {\n if (_state !== null) return _state\n\n // Escape hatch — checked before everything else.\n if (typeof process !== 'undefined' && process.env?.AIHU_COMPILER_NATIVE === '0') {\n _state = { kind: 'disabled' }\n return _state\n }\n\n const requireFn = createRequire(import.meta.url)\n\n // Explicit addon path override — fails LOUD (a pinned path that does not\n // load is a configuration error, never a silent fallthrough).\n const override = process.env?.AIHU_COMPILER_NATIVE_ADDON\n if (override) {\n let addon: unknown\n try {\n addon = requireFn(override)\n } catch (err) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON is set to '${override}', ` +\n `which failed to load: ${(err as Error).message}`,\n )\n }\n if (!isAddonShaped(addon)) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON module at '${override}' ` +\n `does not export compileEnvelope()`,\n )\n }\n _state = {\n kind: 'loaded',\n addon,\n addonPath: override,\n origin: 'override',\n packageVersion: readAddonPackageVersion(override),\n }\n return _state\n }\n\n const descriptor = detectPlatform()\n if (descriptor === null) {\n _state = { kind: 'unavailable' }\n return _state\n }\n\n // 1. Per-platform optionalDependency package (the published-consumer path).\n let resolvedPath: string | null = null\n try {\n resolvedPath = requireFn.resolve(descriptor.packageName)\n } catch {\n // Package not installed — fall through to the dev candidates.\n }\n\n // 2. Dev fallbacks: the standalone src-native build. `aihu-compiler-native.node`\n // is staged by `bun packages/compiler/scripts/build-native.ts` (which runs\n // cargo and copies the platform cdylib); the target/release candidate\n // covers a manual copy. This module lives at js/ (source) or dist/\n // (bundled) — both one level below the package root.\n const devCandidates = [\n resolve(__dirname, '../src-native/aihu-compiler-native.node'),\n resolve(__dirname, '../src-native/target/release/aihu-compiler-native.node'),\n ]\n const candidates = resolvedPath ? [resolvedPath] : devCandidates.filter((c) => existsSync(c))\n const origin: CompilerNativeOrigin = resolvedPath ? 'package' : 'dev-build'\n\n for (const candidate of candidates) {\n try {\n const addon = requireFn(candidate)\n if (isAddonShaped(addon)) {\n _state = {\n kind: 'loaded',\n addon,\n addonPath: candidate,\n origin,\n packageVersion: readAddonPackageVersion(candidate),\n }\n return _state\n }\n throw new Error(`module at ${candidate} does not export compileEnvelope()`)\n } catch (err) {\n // Present-but-broken (ABI mismatch, corrupt download, placeholder file):\n // warn ONCE, loudly, then fall back to the spawn path — which is\n // byte-identical, just slower. `AIHU_COMPILER_NATIVE=0` silences this.\n if (!_warnedLoadFailure) {\n _warnedLoadFailure = true\n console.warn(\n `[@aihu/compiler] native addon found but failed to load; falling back to ` +\n `the aihu-compile spawn path (identical output, slower).\\n` +\n ` Candidate: ${candidate}\\n` +\n ` Error: ${(err as Error).message}\\n` +\n ` Reinstall @aihu/compiler (or rebuild: cargo build --release ` +\n `--manifest-path packages/compiler/src-native/Cargo.toml), or set ` +\n `AIHU_COMPILER_NATIVE=0 to silence this warning.`,\n )\n }\n _state = { kind: 'unavailable', error: err as Error }\n return _state\n }\n }\n\n _state = { kind: 'unavailable' }\n return _state\n}\n\n/** Returns the cached state kind (resolving if needed). @internal */\nexport function _getCompilerNativeStateKind(): CompilerNativeState['kind'] {\n return loadCompilerNative().kind\n}\n\n/** Reset the cached state. Used by tests that mock detection/env. @internal */\nexport function _resetCompilerNative(): void {\n _state = null\n _warnedLoadFailure = false\n}\n\n/**\n * Force the cached loader state — the injection seam for tests that need a\n * specific addon (right version / wrong version / no `compilerVersion` at all)\n * without a Rust build. `loadCompilerNative()` returns this verbatim until\n * `_resetCompilerNative()` clears it.\n * @internal\n */\nexport function _setCompilerNativeForTest(state: CompilerNativeState | null): void {\n _state = state\n _warnedLoadFailure = false\n}\n","/**\n * spawn-bounds.ts — the bounds every `aihu-compile` subprocess must carry.\n *\n * WHY THIS EXISTS (2026-08-07): two `aihu-compile --stdin` processes were found\n * still alive after 2 days 13 hours, and an `apps/docs` vite build sat 10\n * minutes at 0.0% CPU with a wedged `aihu-css-compile` child. The css-engine\n * side was reproduced under load and both sides sampled:\n *\n * child : read (libsystem_kernel) — parked in io::stdin().read_to_string(),\n * waiting for an EOF on stdin that never arrives.\n * parent : node::SyncProcessRunner::TryInitializeAndRunLoop -> uv_run ->\n * uv__io_poll -> kevent — parked in spawnSync's own private uv loop,\n * still holding that pipe's WRITE end open (`lsof -U` confirmed the\n * parent was the only holder, so this is not an fd-inheritance leak).\n *\n * The stall is on the parent side: spawnSync's loop never delivers the writable\n * event that would finish `input` and close the write end. Crucially, with no\n * timer armed `uv__io_poll` calls kevent with NO DEADLINE — which is exactly why\n * these processes wait for days rather than minutes. Passing `timeout` arms a uv\n * timer in that same loop, giving kevent a deadline, so the loop always wakes\n * and reaps the child. Verified in a stress harness: the run that hung\n * indefinitely without `timeout` was rescued with ETIMEDOUT once it was set.\n *\n * This is intermittent and load-dependent. It is NOT a pipe-buffer capacity\n * problem — 20 MB of stdin against 200 KB each of stdout+stderr round-trips\n * cleanly on both node and bun.\n *\n * See the matching note in `packages/css-engine/src/index.ts`.\n */\n\n/**\n * Wall-clock ceiling for one `aihu-compile` invocation — a measured floor plus\n * a payload-scaled term, NOT a round number.\n *\n * The floor. Measured on this machine: the largest SFC in `apps/docs`\n * (16 KB source -> 27.7 KB of AST JSON) round-trips through the binary in 4-5\n * ms, and 24 concurrent processes x 60 compiles each never exceeded 5 ms per\n * call. 120 s is ~24,000x the measured per-call cost. That is deliberately\n * absurd headroom: it has to absorb a loaded CI runner, a cold first exec\n * paying macOS code-signature validation, and a machine thrashing swap, because\n * a timeout that trips a legitimately slow build turns a rare hang into routine\n * CI flake — strictly worse than the bug. 120 s is also short enough that a\n * human watching a build notices, which is the entire point: today's hang\n * produced no output for 10 minutes, and two children survived 2.5 days.\n *\n * The scaled term. A flat bound is the wrong shape if some future payload is\n * enormous, so the ceiling also grows at 2 ms per KB of stdin — about 370x\n * slower than the measured 5.4 MB/s throughput. Below ~60 MB of stdin the\n * floor dominates (nothing in this repo comes within three orders of magnitude\n * of that), so in practice the bound IS 120 s today; the scaling only takes\n * over in the regime where a fixed bound could genuinely be too tight.\n */\nexport const COMPILE_TIMEOUT_FLOOR_MS = 120_000\n\n/** See COMPILE_TIMEOUT_FLOOR_MS — ~370x slower than measured throughput. */\nexport const COMPILE_TIMEOUT_MS_PER_KB = 2\n\nexport const COMPILE_MAX_BUFFER = 64 * 1024 * 1024\n\nexport function compileTimeoutMs(inputBytes = 0): number {\n const scaled = Math.ceil(inputBytes / 1024) * COMPILE_TIMEOUT_MS_PER_KB\n const raw = process.env.AIHU_COMPILE_TIMEOUT_MS\n if (raw !== undefined && raw !== '') {\n const n = Number(raw)\n // An explicit override replaces the FLOOR, not the scaling: someone raising\n // the bound for a huge payload should not accidentally lose the per-byte\n // allowance, and someone lowering it for a test should still get a bound.\n if (Number.isFinite(n) && n > 0) return Math.max(n, scaled)\n }\n return Math.max(COMPILE_TIMEOUT_FLOOR_MS, scaled)\n}\n\n/**\n * The bounds fragment to spread into every `execFileSync`/`spawnSync` call that\n * runs `aihu-compile`.\n *\n * `killSignal: 'SIGKILL'` because the whole point is that nothing survives: a\n * child already wedged in read() is exactly the process that was found alive\n * 2.5 days later, and a polite SIGTERM is not a guarantee.\n */\nexport function compileSpawnBounds(inputBytes = 0): {\n timeout: number\n maxBuffer: number\n killSignal: 'SIGKILL'\n} {\n return {\n timeout: compileTimeoutMs(inputBytes),\n maxBuffer: COMPILE_MAX_BUFFER,\n killSignal: 'SIGKILL',\n }\n}\n\n/**\n * Rewrite a spawn failure into something a human can act on. Node's own\n * ETIMEDOUT/ENOBUFS errors name neither the binary nor the payload, so an\n * unannotated one reads as `spawnSync ... ETIMEDOUT` and tells the reader\n * nothing about which compile died or what to do next.\n *\n * Returns `null` when the error is an ordinary non-zero exit (a real compile\n * error), so callers keep their existing stderr-forwarding behavior.\n */\nexport function describeSpawnFailure(\n err: unknown,\n bin: string,\n args: string[],\n inputBytes: number,\n elapsedMs: number,\n): Error | null {\n const e = err as { code?: string }\n const where =\n ` binary: ${bin}\\n` +\n ` args: ${args.length > 0 ? args.join(' ') : '(none)'}\\n` +\n ` stdin: ${inputBytes} bytes\\n` +\n ` elapsed: ${elapsedMs} ms`\n\n if (e.code === 'ETIMEDOUT') {\n const ms = compileTimeoutMs(inputBytes)\n return new Error(\n `[@aihu/compiler] aihu-compile TIMED OUT after ${ms} ms and the child was killed.\\n\\n` +\n `${where}\\n\\n` +\n ` This is the known spawn stall, not a slow compile: the compiler normally\\n` +\n ` finishes in single-digit milliseconds. The child parks in read() waiting for\\n` +\n ` an EOF on stdin that the parent's spawnSync loop never delivers, so without\\n` +\n ` this timeout the build would hang at 0% CPU indefinitely (two such children\\n` +\n ` were once found still running after 2.5 days).\\n\\n` +\n ` What to do next:\\n` +\n ` - Re-run the build. The stall is intermittent and load-dependent; a retry\\n` +\n ` normally succeeds.\\n` +\n ` - If it reproduces every time, check the binary directly: ${bin} --help\\n` +\n ` and rebuild it: cargo build --release -p aihu-compiler\\n` +\n ` - If a payload genuinely needs longer than ${ms} ms, raise the bound with\\n` +\n ` AIHU_COMPILE_TIMEOUT_MS=<milliseconds>. Do not remove it.`,\n )\n }\n\n if (e.code === 'ENOBUFS') {\n return new Error(\n `[@aihu/compiler] aihu-compile produced more than the ${COMPILE_MAX_BUFFER} byte\\n` +\n ` stdout/stderr limit and the child was killed.\\n\\n` +\n `${where}\\n\\n` +\n ` An emit this large almost certainly means the input is wrong rather than a\\n` +\n ` real component. Check what is being passed in before raising the cap.`,\n )\n }\n\n return null\n}\n","/**\n * envelope.ts — backend dispatch for the single-parse envelope compile.\n *\n * Every compile request (`transform` / `compileToAst` / `compileRouteMeta`)\n * routes memo-first, then through ONE of three backends, in order:\n *\n * 1. **native addon** (packages/compiler/src-native, napi) — in-process,\n * zero spawn. Selected once per process by `_resolveCompileBackend()`.\n * 2. **envelope CLI spawn** — the legacy `aihu-compile` spawn args PLUS\n * `--envelope <options-json>`. A binary that knows the flag answers with\n * one JSON envelope (single parse, every requested artifact); the JSON\n * carries the `\"envelope\": 1` discriminant.\n * 3. **legacy per-output spawn** — an OLDER binary ignores `--envelope`\n * and answers with its normal single artifact (JS / AST JSON / route\n * JSON), which the discriminant check detects. That output is used\n * as-is, so stale binaries keep exactly their historical behavior —\n * feature detection costs zero extra spawns.\n *\n * The native addon must additionally PASS A VERSION HANDSHAKE before it is\n * selected (`_checkNativeAddonVersion`): its release version has to equal the\n * pin in `packages/compiler/package.json`'s optionalDependencies. It is a\n * published artifact and the CLI binary is not, so on any branch that changes\n * Rust the addon is stale by construction and would quietly emit pre-change\n * output. A mismatch warns once and falls back to spawn — except under an\n * explicit `AIHU_COMPILER_NATIVE_ADDON` pin, which throws.\n *\n * Backend selection is CACHED at first use (per module instance):\n * `AIHU_COMPILER_NATIVE=0` forces spawn; an explicit `AIHU_COMPILE_BIN`\n * binary pin forces spawn (working ON the compiler means\n * the pinned binary must actually run — an addon silently shadowing it would\n * reintroduce the exact quiet-wrong-answer failure the pin exists to prevent).\n * The cache means css-engine's mid-build `AIHU_COMPILE_BIN` handshake\n * (which programmatically sets the var AFTER the first transform) cannot\n * de-select an already-active native backend.\n *\n * The memo's \"binary stamp\" (transform-memo.ts `_binStamp`) generalizes to\n * whichever backend is active: the addon's `.node` file path (stat'd for\n * mtime+size, so a rebuilt addon invalidates entries) when native, the\n * resolved CLI binary path when spawning.\n */\n\nimport { execFileSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { type CompilerNativeState, loadCompilerNative, nativePlatformDescriptor } from './native.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\nimport { compileSpawnBounds, describeSpawnFailure } from './spawn-bounds.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/** The wire shape of a compile envelope (Rust `Envelope`, camelCase). */\nexport interface CompileEnvelope {\n envelope: number\n targets: Record<string, { js?: string; manifest?: string }>\n astJson?: string\n routeJson?: string\n diagnostics: unknown[]\n}\n\n/** Options forwarded to the Rust envelope API (Rust `EnvelopeOptions`). */\nexport interface CompileEnvelopeOptions {\n tag?: string\n path?: string\n targets?: string[]\n emits?: Array<'js' | 'ast' | 'route' | 'manifest'>\n strictTemplates?: boolean\n exprParser?: string\n}\n\nexport type CompileBackend =\n | {\n kind: 'native'\n compileEnvelope: (source: string, optionsJson: string) => string\n stampPath: string\n }\n | { kind: 'spawn' }\n\nlet _backend: CompileBackend | null = null\n\n/**\n * The addon version this source tree requires — `packages/compiler/package.json`'s\n * optionalDependencies pin for the current platform's addon package.\n *\n * That pin is bumped in the SAME commit that changes the Rust, so it is the\n * only in-repo statement of \"which addon build this JS expects\". This module\n * builds to `dist/envelope.js` and lives at `js/envelope.ts` in source — the\n * manifest is one level up either way.\n * @internal\n */\nexport function _requiredNativeAddonVersion(): string | null {\n const descriptor = nativePlatformDescriptor()\n if (descriptor === null) return null\n try {\n const manifest = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8')) as {\n optionalDependencies?: Record<string, string>\n }\n const pinned = manifest.optionalDependencies?.[descriptor.packageName]\n return typeof pinned === 'string' ? pinned : null\n } catch {\n return null\n }\n}\n\n/** The verdict of the addon⇄source version handshake. @internal */\nexport type NativeVersionVerdict =\n | { ok: true }\n | { ok: false; reason: 'missing-method' | 'version-mismatch'; actual: string; expected: string }\n\n/**\n * The backend version handshake (§19).\n *\n * ## Why this exists\n *\n * `_resolveCompileBackend()` prefers an in-process addon over the workspace CLI\n * binary. The addon is a PUBLISHED artifact; the CLI binary is built from this\n * source tree. So on any branch that changes Rust, the installed addon is stale\n * BY CONSTRUCTION — the pin it would need names a version that does not exist on\n * npm yet, `bun install` cannot fetch it, and the compile silently produces\n * pre-change output. That is a quiet wrong answer, the worst failure mode a\n * compiler has: it has already produced a \"could not reproduce\" that was nothing\n * but a stale backend.\n *\n * ## What is compared\n *\n * The addon's RELEASE version (its npm package version) against the pin. NOT the\n * string from `compilerVersion()`: that interpolates `CARGO_PKG_VERSION` from\n * `src-native/Cargo.toml`, which has read `0.1.0` since the addon landed and is\n * never bumped per release — every published addon, current or stale, reports\n * `0.1.0`, so it carries no release identity. `compilerVersion()` IS called: its\n * presence is the capability probe (a missing method means an addon old enough\n * to predate the handshake, which is a mismatch by definition), and what it\n * reports goes into the warning as a diagnostic, which is precisely the role its\n * own Rust doc comment assigns it.\n *\n * A locally built addon (`origin: 'dev-build'`, no platform-package manifest\n * beside it) has no release version and is NOT gated: it was compiled from this\n * very tree, which is the property the gate exists to establish.\n * @internal\n */\nexport function _checkNativeAddonVersion(\n state: Extract<CompilerNativeState, { kind: 'loaded' }>,\n): NativeVersionVerdict {\n const expected = _requiredNativeAddonVersion()\n // No pin (unsupported platform / unreadable manifest): nothing to check\n // against. Never block a working addon on a missing expectation.\n if (expected === null) return { ok: true }\n\n if (typeof state.addon.compilerVersion !== 'function') {\n return { ok: false, reason: 'missing-method', actual: '<no compilerVersion()>', expected }\n }\n\n let reported: string\n try {\n reported = String(state.addon.compilerVersion())\n } catch (err) {\n return {\n ok: false,\n reason: 'missing-method',\n actual: `<compilerVersion() threw: ${(err as Error).message}>`,\n expected,\n }\n }\n\n // Built from this source tree — no release version, nothing stale possible.\n if (state.packageVersion === null) return { ok: true }\n\n if (state.packageVersion !== expected) {\n return {\n ok: false,\n reason: 'version-mismatch',\n actual: `${state.packageVersion} (reports: ${reported})`,\n expected,\n }\n }\n return { ok: true }\n}\n\nfunction nativeMismatchMessage(\n state: Extract<CompilerNativeState, { kind: 'loaded' }>,\n verdict: Extract<NativeVersionVerdict, { ok: false }>,\n): string {\n const cause =\n verdict.reason === 'missing-method'\n ? `the addon does not implement compilerVersion(), so it predates this handshake`\n : `the installed addon is not the build this source requires`\n return (\n `[@aihu/compiler] native addon version mismatch — ${cause}.\\n` +\n ` Required (packages/compiler/package.json pin): ${verdict.expected}\\n` +\n ` Loaded addon: ${verdict.actual}\\n` +\n ` Addon path: ${state.addonPath}\\n` +\n ` Cause: the addon is a PUBLISHED artifact, so a branch that changes Rust\\n` +\n ` is stale by construction — the pinned version is not on npm yet and\\n` +\n ` \\`bun install\\` cannot fix it. Using it would silently compile with\\n` +\n ` pre-change codegen.`\n )\n}\n\n/**\n * Resolve (once) which backend serves compiles for this process.\n * @internal\n */\nexport function _resolveCompileBackend(): CompileBackend {\n if (_backend !== null) return _backend\n const env = typeof process !== 'undefined' ? process.env : undefined\n if (env?.AIHU_COMPILER_NATIVE === '0' || env?.AIHU_COMPILE_BIN) {\n _backend = { kind: 'spawn' }\n return _backend\n }\n const native = loadCompilerNative()\n if (native.kind !== 'loaded') {\n _backend = { kind: 'spawn' }\n return _backend\n }\n\n const verdict = _checkNativeAddonVersion(native)\n if (!verdict.ok) {\n // An EXPLICIT addon pin that mismatches is a configuration error, not a\n // fallback opportunity — same doctrine as native.ts's override load\n // failure and AIHU_COMPILE_BIN: silently ignoring a pin hands back a\n // plausible-looking wrong backend.\n if (native.origin === 'override') {\n throw new Error(\n `${nativeMismatchMessage(native, verdict)}\\n` +\n ` AIHU_COMPILER_NATIVE_ADDON pinned this addon explicitly, so this fails\\n` +\n ` rather than falling back. Unset it (the CLI spawn path is byte-identical),\\n` +\n ` set AIHU_COMPILER_NATIVE=0, or rebuild:\\n` +\n ` bun packages/compiler/scripts/build-native.ts`,\n )\n }\n console.warn(\n `${nativeMismatchMessage(native, verdict)}\\n` +\n ` Falling back to the aihu-compile spawn path (built from source,\\n` +\n ` byte-identical output, slower). Set AIHU_COMPILER_NATIVE=0 to silence\\n` +\n ` this, or build the addon from source:\\n` +\n ` bun packages/compiler/scripts/build-native.ts`,\n )\n _backend = { kind: 'spawn' }\n return _backend\n }\n\n _backend = {\n kind: 'native',\n compileEnvelope: native.addon.compileEnvelope.bind(native.addon),\n stampPath: native.addonPath,\n }\n return _backend\n}\n\n/** Reset the cached backend (tests). @internal */\nexport function _resetCompileBackend(): void {\n _backend = null\n}\n\n/**\n * The identity string the memo cache stamps entries with — the active\n * backend's file (addon `.node` path, or the resolved CLI binary path).\n * `_binStamp` stats it, so rebuilding EITHER backend invalidates entries.\n * @internal\n */\nexport function _backendStampPath(): string {\n const backend = _resolveCompileBackend()\n return backend.kind === 'native' ? backend.stampPath : resolveSpawnBinPath()\n}\n\n/**\n * CLI binary resolution for the spawn backend — env override first (the\n * css-engine handshake sets AIHU_COMPILE_BIN), then the shared resolver.\n * Call-time, never cached here (Bug 6 doctrine: the env var may be set\n * between calls).\n * @internal\n */\nexport function resolveSpawnBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n/** A backend reply: either a parsed envelope, or a legacy single artifact. */\nexport type EnvelopeReply =\n | { kind: 'envelope'; envelope: CompileEnvelope }\n | { kind: 'legacy'; output: string }\n\nfunction parseEnvelopeReply(stdout: string): EnvelopeReply {\n // Envelope replies are a single JSON object carrying the `\"envelope\"`\n // discriminant. EVERY legacy output fails this test: emitted JS is not\n // JSON (starts with a comment or import), an AST export carries\n // `astVersion` but not `envelope`, a route sidecar is a plain object (or\n // the literal `null`) without it.\n const trimmed = stdout.trim()\n if (trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed) as { envelope?: unknown }\n if (typeof parsed === 'object' && parsed !== null && parsed.envelope === 1) {\n return { kind: 'envelope', envelope: parsed as unknown as CompileEnvelope }\n }\n } catch {\n // Not JSON — legacy output.\n }\n }\n return { kind: 'legacy', output: stdout }\n}\n\n/**\n * Run one compile through the active backend.\n *\n * @param source the `.aihu` source (stdin for the spawn backend)\n * @param legacyArgs the EXACT argv the pre-envelope spawn used for this call\n * (`--stdin --tag … [--ast-json|--route-json|…]`). The spawn\n * backend appends `--envelope <json>` to it, so an older\n * binary that ignores the flag still answers the legacy\n * request correctly.\n * @param options the envelope options (must agree with `legacyArgs`)\n * @internal\n */\nexport function _compileViaBackend(\n source: string,\n legacyArgs: string[],\n options: CompileEnvelopeOptions,\n): EnvelopeReply {\n const backend = _resolveCompileBackend()\n const optionsJson = JSON.stringify(options)\n if (backend.kind === 'native') {\n const envelope = JSON.parse(backend.compileEnvelope(source, optionsJson)) as CompileEnvelope\n return { kind: 'envelope', envelope }\n }\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveSpawnBinPath()\n const spawnArgs = [...legacyArgs, '--envelope', optionsJson]\n const startedAt = Date.now()\n let stdout: string\n try {\n stdout = execFileSync(bin, spawnArgs, {\n input: source,\n encoding: 'utf8',\n ...compileSpawnBounds(source.length),\n })\n } catch (err) {\n throw describeSpawnFailure(err, bin, spawnArgs, source.length, Date.now() - startedAt) ?? err\n }\n return parseEnvelopeReply(stdout)\n}\n","/**\n * transform-memo.ts — content-addressed memo cache for `aihu-compile` spawns.\n *\n * Every `.aihu` compile is a subprocess spawn (~6ms each, ~63% pure spawn\n * overhead), and the SSG prerender pass re-compiles every file a SECOND time:\n * `prerenderClose` (packages/app/src/prerender.ts) boots a second Vite server\n * that REUSES the already-resolved plugin instances and re-runs the same\n * transforms via `ssrLoadModule` — identical source, identical flags,\n * byte-identical output recompiled. css-engine's `compileSfc` additionally\n * re-spawns the compiler per file for its AST pass (`compileToAst`).\n *\n * This module memoises the RAW STDOUT of a spawn, keyed by a SHA-256 digest of\n * everything that determines that stdout:\n *\n * kind (transform | ast | route) + file id + options fingerprint\n * + binary identity (path + mtime + size) + source content\n *\n * Because the key is content-addressed, watch-mode correctness is free: an\n * edit changes the source hash, so a stale entry is simply never looked up\n * again. The binary stamp (mtime+size) additionally invalidates entries when\n * the compiler binary itself is rebuilt mid-session (dev-workspace cargo\n * rebuilds); the stat is ~µs against a ~6ms spawn.\n *\n * Growth is bounded by FIFO eviction at `MAX_ENTRIES` — inert stale entries\n * from long dev sessions age out. Deliberately NOT cleared on Vite\n * `buildStart`: the SSG prerender's second server fires its own start-of-run\n * hooks, and a clear there would defeat the exact pass-two hits this cache\n * exists for.\n *\n * All exports are `_`-prefixed internals of `@aihu/compiler`; consumers go\n * through `transform()` / `compileToAst()` / `compileRouteMeta()`.\n */\nimport { createHash } from 'node:crypto'\nimport { statSync } from 'node:fs'\n\n/**\n * FIFO size bound. 1024 entries × a few KB of compiled output ≈ a few MB —\n * comfortably above any real app's file count (one entry per file × kind ×\n * options variant) while keeping unbounded-session growth impossible.\n * @internal\n */\nexport const _MEMO_MAX_ENTRIES = 1024\n\nconst cache = new Map<string, string>()\nlet hits = 0\nlet misses = 0\nlet seeds = 0\n\n/**\n * Identity stamp for the compiler binary: path + mtime + size when statable,\n * path alone otherwise (e.g. tests pointing AIHU_COMPILE_BIN at a fake).\n * A rebuilt-in-place binary changes mtime/size → old entries become inert.\n * @internal\n */\nfunction _binStamp(binPath: string): string {\n try {\n const st = statSync(binPath)\n return `${binPath}:${st.mtimeMs}:${st.size}`\n } catch {\n return binPath\n }\n}\n\n/** @internal */\nexport function _memoKey(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n): string {\n return createHash('sha256')\n .update(kind)\n .update('\\0')\n .update(id)\n .update('\\0')\n .update(optionsFingerprint)\n .update('\\0')\n .update(_binStamp(binPath))\n .update('\\0')\n .update(source)\n .digest('hex')\n}\n\n/**\n * Memoised spawn: returns the cached stdout for an identical\n * (kind, source, id, options, binary) tuple, otherwise runs `spawn()` and\n * caches its result. The cached value is the raw stdout STRING — callers that\n * return structured data (`compileToAst`, `compileRouteMeta`) re-parse per\n * call so a mutated result object can never poison the cache.\n * @internal\n */\nexport function _memoizedSpawn(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n spawn: () => string,\n): string {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n const hit = cache.get(key)\n if (hit !== undefined) {\n hits++\n return hit\n }\n const out = spawn()\n misses++\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n // FIFO: Map preserves insertion order; drop the oldest entry.\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, out)\n return out\n}\n\n/**\n * Seed a memo entry WITHOUT a spawn — the envelope path's sibling-artifact\n * write. One envelope compile of a file yields js + ast + route from a single\n * parse; the js answers the current call, and the ast/route strings are\n * seeded here under the exact keys `compileToAst` / `compileRouteMeta` will\n * look up, so their later calls for the same source are pure cache hits.\n * Never overwrites an existing entry; counts in the `seeds` stat, not\n * hits/misses.\n * @internal\n */\nexport function _seedMemo(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n value: string,\n): void {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n if (cache.has(key)) return\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, value)\n seeds++\n}\n\n/** Test/diagnostic hook — wipe the memo and its counters. @internal */\nexport function _clearTransformMemo(): void {\n cache.clear()\n hits = 0\n misses = 0\n seeds = 0\n}\n\n/** Test/diagnostic hook — current memo size + hit/miss/seed counters. @internal */\nexport function _transformMemoStats(): {\n size: number\n hits: number\n misses: number\n seeds: number\n} {\n return { size: cache.size, hits, misses, seeds }\n}\n","/**\n * @aihu/compiler — TypeScript wrapper around the aihu-compile Rust binary.\n *\n * Exports:\n * transform(source, id) — compile a single .aihu file to TypeScript\n * aihuCompilerPlugin() — Vite plugin that wires transform() into the build\n */\nimport { execFileSync } from 'node:child_process'\nimport { createRequire } from 'node:module'\nimport { basename, join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { _backendStampPath, _compileViaBackend } from './envelope.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\nimport { compileSpawnBounds, describeSpawnFailure } from './spawn-bounds.ts'\nimport { _memoizedSpawn, _seedMemo } from './transform-memo.ts'\n\nexport type { CompileEnvelope, CompileEnvelopeOptions } from './envelope.ts'\n// Perf — in-process napi compile backend + single-parse envelope (see\n// js/envelope.ts and js/native.ts). `transform()` / `compileToAst()` /\n// `compileRouteMeta()` route memo → native addon → envelope CLI spawn →\n// legacy per-output spawn. Re-exported as internals for tests/diagnostics.\nexport {\n _compileViaBackend,\n _resetCompileBackend,\n _resolveCompileBackend,\n} from './envelope.ts'\nexport { _getCompilerNativeStateKind, _resetCompilerNative, loadCompilerNative } from './native.ts'\nexport { resolveCompilerBinary } from './resolve-binary.ts'\n// Perf — content-addressed memo over the compile spawns (see transform-memo.ts).\n// The SSG prerender re-runs every transform against a second Vite server with\n// identical inputs; the memo turns that whole second pass (and css-engine's\n// per-file `compileToAst` re-parse) into cache lookups instead of subprocess\n// spawns. Re-exported as internals so tests can reset/observe the cache.\nexport {\n _clearTransformMemo,\n _MEMO_MAX_ENTRIES,\n _transformMemoStats,\n} from './transform-memo.ts'\n\n// Binary resolution: env var override, then the per-platform optionalDependency\n// package (`@aihu/compiler-<platform>`) with a workspace `target/` dev fallback —\n// see js/resolve-binary.ts (a clone of css-engine's resolver). The published\n// @aihu/compiler tarball ships only the JS shim (bin/aihu-compile.mjs); the\n// native binary arrives via the optionalDependency packages, so there is no\n// `../bin/aihu-compile` relative path anymore.\n//\n// Bug 6 fix — resolveBinPath() is CALL-TIME, not module-load-time. The Vite\n// plugin's `_maybeCompileUtilityCss` sets `process.env.AIHU_COMPILE_BIN` so\n// that css-engine's bundled copy of `compileToAst` spawns THIS compiler's\n// binary. Prior to this fix `binPath` was a module-scope const captured at\n// import time, so the env-var assignment was always too late and `compileSfc`\n// failed with ENOENT. Re-reading on every call is essentially free (an env\n// lookup, then a memoized resolve) and makes the AIHU_COMPILE_BIN handshake\n// actually work.\nfunction resolveBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n/*\n * ── Regex hardening: js/polynomial-redos (CWE-1333) ─────────────────────────\n *\n * Most of the regexes in this file run against COMPILED MODULE TEXT, and that\n * text carries authored `.aihu` bytes verbatim: a template text node becomes\n * `leaf('<text>')` (codegen/template_emit.rs) and an `@style` body becomes\n * ``__style__.replaceSync(`<css>`)`` (codegen/emit.rs). So the strings these\n * patterns scan ARE attacker-controllable by whoever authors the `.aihu` file\n * — an untrusted PR in a monorepo, a template shipped to other developers.\n * Treat them as untrusted input, not as our own generated output.\n *\n * Three ambiguity shapes were live in this file (17 CodeQL alerts), all\n * polynomial rather than exponential — the cost comes from re-scanning, not\n * from nested quantifiers:\n *\n * A. Named-import matchers, `import\\s*\\{[^}]*\\}\\s*from '<mod>'`.\n * `[^}]` does not exclude `{`, so every one of the N `import{` offsets in\n * the subject restarts a scan that runs to the END of the string before\n * failing → O(n²). Fixed by narrowing the specifier-list class to\n * `[^{}]`, which bounds each scan to the next brace. An ES import\n * specifier list can never contain `{`, so no legitimate input changes\n * meaning — the class is strictly more correct as well as safer.\n * (Measured: 224 KB of `import{` took 2.45 s before, 0.7 ms after.)\n *\n * B. Literal prefix + lazy any-char scan, e.g.\n * ``/(__style__\\.replaceSync\\(`)[^]*?(`\\);)/``. Repeating the prefix\n * gives N start offsets, each running a fresh O(n) lazy scan → O(n²).\n * A regex cannot express \"first prefix, then first terminator\" without\n * that re-scan, so these are restructured into literal `indexOf` scans\n * (`_replaceDelimitedBody`, `_passivizeOutlet`, `_hasBaseRecipe`). The\n * rewrite is exactly equivalent: `String.replace` takes the LEFTMOST\n * match, and if no terminator follows the first prefix then none follows\n * any later prefix either — so \"first prefix + first terminator after it\"\n * is the same span the regex produced.\n *\n * C. Greedy `.*` between two literals (`/import.*from\\s*'@aihu\\/signals'/`).\n * Same re-scan blowup, one start offset per `import` on the line. Fixed\n * by anchoring to a line start (`^` + `m`), which caps the offsets at one\n * per line and makes the total linear.\n *\n * Adjacent unbounded `\\s*` runs (`\\s*;?\\s*$`) are a second, independent pump:\n * the two runs can split a whitespace tail O(n) ways when `$` never holds.\n * Rewritten as `(?:\\s*;)?\\s*$`, which matches exactly the same spans with an\n * unambiguous decomposition. And `^\\s*` under the `m` flag is a third: `\\s`\n * matches `\\n`, so every line start can scan the whole remaining file →\n * narrowed to `[^\\S\\r\\n]*` (horizontal indentation), which is what \"the import\n * line\" actually means.\n *\n * `packages/compiler/tests/regex-redos.test.ts` pins both halves: old-vs-new\n * output equality on every real compiled shape, and a wall-clock budget on the\n * adversarial inputs.\n */\n\n// Minimal VitePlugin interface — avoids importing from 'vite' at compile time.\n// Structurally compatible with Vite's Plugin type.\ninterface VitePlugin {\n readonly name: string\n enforce?: 'pre' | 'post'\n resolveId?: (\n source: string,\n importer?: string,\n ) => string | null | undefined | Promise<string | null | undefined>\n load?: (id: string) => string | null | undefined | Promise<string | null | undefined>\n transform?: (\n code: string,\n id: string,\n ) => Promise<{ code: string; map: null }> | { code: string; map: null } | null | undefined\n /** GX Phase 1 (#437-GX) — end-of-build hook; prints the extract census. */\n buildEnd?: (error?: Error) => void | Promise<void>\n}\n\n/**\n * Options for `aihuCompilerPlugin()` (Plan 3.3 — Islands).\n */\nexport interface AihuCompilerPluginOptions {\n /**\n * Optional CSS provider for the compiled SFC.\n *\n * When supplied, the provider is the sole source of generated component\n * CSS. Its non-empty result must be the COMPLETE stylesheet for the SFC:\n * include utility rules, design tokens, and any authored `@style` rules\n * that should ship with the component. The compiler replaces its existing\n * stylesheet body with this result (or routes it through the document CSS\n * pipeline for `shadowMode: 'light'`). Returning an empty string, `null`,\n * or `undefined` means that this SFC has no provider stylesheet.\n *\n * Without this option, the compiler preserves the legacy automatic,\n * optional `@aihu/css-engine` integration.\n */\n cssProvider?: AihuCssProvider\n\n /**\n * When `true` (default), components the compiler classified as `'static'`\n * (read from the `// @aihu:island` marker via `_parseIslandMarker()`) are\n * emitted with a minimal HTML-only registration shim that ships **zero**\n * `@aihu/runtime` and `@aihu/signals` JS to the browser. Components\n * classified as `'interactive'` retain the full runtime path.\n *\n * Setting `islands: false` opts every component back into the unified\n * runtime path (Plan 3.2 baseline behaviour).\n */\n islands?: boolean\n\n /**\n * Project-wide rendering mode applied to every `.aihu` SFC compiled\n * by this plugin instance. When set, the plugin post-processes the\n * compiled JS to inject `, { shadowMode: '<mode>' }` as the third arg\n * to the emitted `defineElement(tag, defineComponent(...))` call.\n *\n * BINARY vocabulary (DA4 #437):\n * - `'shadow'` — shadow DOM (`attachShadow({ mode: 'open' })` internally;\n * open is the only browser mode aihu's composition/hydration\n * can use). `this.shadowRoot` is the non-null root.\n * - `'light'` — **no shadow root.** The component mounts into its own\n * element (`this.shadowRoot === null`). Required for global\n * utility-class CSS frameworks like Tailwind, UnoCSS, Pico\n * that rely on the cascade.\n *\n * Per-file override: the `$shadow: 'light' | 'shadow'` macro outranks this\n * config. Unset, pages/layouts default to `'light'` and leaves to\n * `'shadow'`.\n */\n shadowMode?: 'light' | 'shadow'\n\n /**\n * Build target threaded to the compiler binary (`--target`). Defaults to the\n * compiler's `universal` target (current behaviour). Set to `'client'` for a\n * browser bundle that must NOT ship the server `__agentBinding` (policy) and\n * instead gets the policy-free `@agent` opaque-ID dispatcher + the per-instance\n * `_registerAgentDispatcher` wiring the capability bridge reads after mount.\n * See `examples/agent-driven-demo`.\n */\n target?: 'client' | 'server' | 'universal'\n\n /**\n * Directory (relative to the project root) holding layout SFCs. Default:\n * `'src/layouts'`. Files under this directory are compiled in **layout mode**:\n * their custom element is registered under the namespaced tag\n * `aihu-layout-<stem>` (a layout stem like `app` is not a valid custom-element\n * name on its own), and their `<outlet>` lowers to a **passive**\n * `data-aihu-outlet` marker rather than the reactive route-driven boundary —\n * because `@aihu/app`'s client renderer fills the marker imperatively and the\n * reactive boundary would otherwise clear it on mount.\n *\n * Kept in sync with `@aihu/router`'s `layoutTagFor()` (`virtual:aihu-layouts`).\n */\n layoutsDir?: string\n}\n\n/**\n * Context passed to an explicit CSS provider for each compiled SFC.\n *\n * `shadowMode` and `target` are resolved compiler values, including their\n * runtime defaults. A provider can therefore choose a light-DOM stylesheet\n * strategy or emit target-specific CSS without parsing compiler markers.\n */\nexport interface AihuCssProviderContext {\n source: string\n id: string\n shadowMode: 'light' | 'shadow'\n target: 'client' | 'server' | 'universal'\n lightScopeId?: string\n}\n\n/**\n * Explicit CSS provider contract for `aihuCompilerPlugin`.\n *\n * A non-empty return value is authoritative and must be a complete stylesheet\n * for the SFC, including any authored styles the provider wants to preserve.\n * It may be synchronous or asynchronous. Empty/absent results skip CSS\n * folding for that SFC. The provider is an opt-in replacement for the\n * automatic `@aihu/css-engine` fallback, not an additional stylesheet layer.\n */\nexport type AihuCssProvider = (\n context: AihuCssProviderContext,\n) => string | null | undefined | Promise<string | null | undefined>\n\n/**\n * Find the `)` matching the `(` at `open`, skipping string literals\n * (`'…'`/`\"…\"` with `\\` escapes), template literals (including nested\n * `${ … }` interpolations, tracked with a frame stack), and `//`/`/* */`\n * comments. Returns -1 when no match is found before end of input.\n *\n * Why a lexer and not a bare paren count: on the client/universal targets the\n * ENTIRE setup body is inlined inside `defineComponent((ctx) => { … })`, and\n * that body carries user template text as string literals — `leaf('(')` for a\n * `(` text node, `{ title: '(unclosed' }` for an attribute — so a count that\n * reads parens inside strings drifts and the caller silently bails, costing\n * the component its `shadowMode`/`lightScopeId` injection entirely.\n *\n * Known miss, accepted: a regex literal in user `@state` code containing an\n * unbalanced paren (`/\\(/`) reads as division + parens. The caller validates\n * the landing site and bails to a no-op rather than corrupting.\n */\nfunction _matchParen(code: string, open: number): number {\n // Frames: 'code' (with its own `{}` depth, so a `}` inside a template\n // interpolation knows whether it closes the interpolation) or 'tpl'.\n const frames: Array<{ kind: 'code'; brace: number } | { kind: 'tpl' }> = [\n { kind: 'code', brace: 0 },\n ]\n let paren = 0\n for (let i = open; i < code.length; i++) {\n const top = frames[frames.length - 1]\n // Unreachable: the root frame never pops (a code-frame `}` pops only when\n // `frames.length > 1`, a tpl frame pops only itself) — checker appeasement.\n if (top === undefined) return -1\n const c = code[i]\n if (top.kind === 'tpl') {\n if (c === '\\\\') i++\n else if (c === '`') frames.pop()\n else if (c === '$' && code[i + 1] === '{') {\n frames.push({ kind: 'code', brace: 0 })\n i++\n }\n continue\n }\n if (c === \"'\" || c === '\"') {\n i++\n while (i < code.length && code[i] !== c) {\n if (code[i] === '\\\\') i++\n i++\n }\n } else if (c === '`') {\n frames.push({ kind: 'tpl' })\n } else if (c === '/' && code[i + 1] === '/') {\n while (i < code.length && code[i] !== '\\n') i++\n } else if (c === '/' && code[i + 1] === '*') {\n i += 2\n while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) i++\n i++\n } else if (c === '(') {\n paren++\n } else if (c === ')') {\n paren--\n if (paren === 0) return i\n } else if (c === '{') {\n top.brace++\n } else if (c === '}') {\n if (top.brace === 0 && frames.length > 1) frames.pop()\n else top.brace--\n }\n }\n return -1\n}\n\n/**\n * Inject `shadowMode: '...'` (and, for light mode, `lightScopeId: '...'` in\n * the SAME options object) into the third argument of the emitted\n * `defineElement('tag', defineComponent(...))` call — appending the options\n * object when the call has two arguments, or merging the fields into an\n * existing third argument (`$form` emits `, { formAssociated: true }`).\n * Idempotent — leaves code untouched when the call is not in a recognized\n * shape or the options already carry a `shadowMode`.\n *\n * `lightScopeId` is folded into this SAME injection rather than a second\n * independent pass, deliberately: it is only ever set when `mode === 'light'`\n * (see the call site, `index.ts`'s `transform` hook), so every call that\n * needs it ALSO needs a shadowMode injection at the exact same spot — a\n * second pass would just re-match (and fight) the text this one already\n * rewrote. Stamps `data-a` on the root element at runtime (light-DOM leaf\n * flip, LDF §10 step 3) — `packages/runtime/src/define-element.ts`'s\n * `wrapClass` reads `options.lightScopeId`.\n *\n * History, because this anchor has now been wrong twice:\n *\n * 1. The original regex anchor (`defineComponent\\([^]*\\)\\s*\\)` — greedy, and\n * `[^]` crosses newlines) ran past `defineElement` and matched the LAST\n * `)\\s*)` pair in the module. On a SERVER-target module the string\n * renderer (`__ssrString`) is emitted AFTER the registration, so for any\n * component with an `if=` the last such pair is the emitted condition,\n * which became `if ((n() > 5), { shadowMode: 'light', … })` — the comma\n * operator, always truthy, so a dead branch rendered (the SSR-child\n * review's 33-nested-hosts measurement). Same corruption landed inside\n * `__aihu_stext(...)` calls (a `(` in text content) and, on ALL targets,\n * inside `$form`'s `setFormValue(...)`. The repro was missed at first\n * because the probe regex `/if \\([^)]*shadowMode/` cannot cross the `)`\n * in `(n() > 5)` — evidence in light-scope-export.test.ts.\n *\n * 2. The first replacement — a bare balanced-paren count — read parens inside\n * string literals (`leaf('(')`), drifted, and silently bailed on\n * client/universal modules, stripping the injection those components need.\n * Hence `_matchParen`'s lexer, and the tests that pin every shape above.\n *\n * @internal\n */\nexport function _injectShadowMode(\n code: string,\n mode: 'light' | 'shadow',\n lightScopeId?: string,\n): string {\n const head = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/.exec(code)\n if (head == null) return code\n // Index of the `(` that opens defineComponent's argument list.\n const open = head.index + head[0].length - 1\n const close = _matchParen(code, open)\n if (close === -1) return code\n const fields = `shadowMode: '${mode}'${lightScopeId ? `, lightScopeId: '${lightScopeId}'` : ''}`\n const rest = code.slice(close + 1)\n // Two-argument call: `defineComponent(...)` followed directly by\n // defineElement's own `)`. Append the options object.\n if (/^\\s*\\)/.test(rest)) {\n return `${code.slice(0, close + 1)}, { ${fields} }${rest}`\n }\n // Existing third argument (`$form`'s `, { formAssociated: true }`): merge\n // the fields into it — unless a shadowMode is already present (idempotency).\n const existing = /^\\s*,\\s*\\{/.exec(rest)\n if (existing && !/^\\s*,\\s*\\{[^}]*\\bshadowMode\\b/.test(rest)) {\n const braceEnd = close + 1 + existing[0].length\n return `${code.slice(0, braceEnd)} ${fields},${code.slice(braceEnd)}`\n }\n // Unrecognized landing site (or already injected) — no-op rather than guess.\n return code\n}\n\n/**\n * Fill the Rust codegen's `__AIHU_LIGHT_SCOPE_ID__` placeholder with the\n * component's real light-DOM scope id (LDF §10 step 3).\n *\n * The server-target string renderer (`emit.rs`, wave-3) emits\n * `const __AIHU_LIGHT_SCOPE_ID__: string | undefined = undefined;` and merges\n * it into `__ssrString`'s options (`opts.lightScopeId ?? __AIHU_LIGHT_SCOPE_ID__`)\n * so a compiled light-DOM component can stamp `data-a` on its own rendered\n * root with NO caller cooperation. Whether the component actually resolves to\n * light mode is only known here in the JS layer (same reason\n * `_injectShadowMode` exists), so Rust emits the placeholder and this helper\n * replaces the literal when the mode resolved to light — exactly the wiring\n * the Rust-side comment names. Idempotent: no placeholder (client target,\n * bailed string renderer) → code returned untouched.\n *\n * @internal\n */\nexport function _injectLightScopeId(code: string, lightScopeId: string): string {\n return code.replace(\n 'const __AIHU_LIGHT_SCOPE_ID__: string | undefined = undefined',\n `const __AIHU_LIGHT_SCOPE_ID__: string | undefined = '${lightScopeId}'`,\n )\n}\n\n/**\n * Light-DOM (`shadowMode:'light'`) recipes: redirect the authored `@style`\n * block's per-instance `host.adoptedStyleSheets = [__style__]` assignment to\n * `document.adoptedStyleSheets` so the recipe's class-scoped CSS reaches the\n * global cascade (a light-DOM host has no shadow root, making the original\n * setter a silent no-op). The module-level `__style__` is shared across\n * instances; the `includes` guard keeps the global adoption idempotent.\n *\n * @internal\n */\nexport function _globalizeAuthoredStyle(code: string): string {\n // The Rust codegen emits exactly: `(ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];`\n const re = /\\(ctx\\.host as ShadowRoot\\)\\.adoptedStyleSheets\\s*=\\s*\\[__style__\\];?/\n return code.replace(\n re,\n 'if (!document.adoptedStyleSheets.includes(__style__)) document.adoptedStyleSheets = [...document.adoptedStyleSheets, __style__];',\n )\n}\n\n/**\n * Read the compiler's AUTHORITATIVE island classification from the\n * `// @aihu:island <kind>` marker the Rust codegen emits for every component\n * (`emit.rs`, wave 3c). A component is a **static** island (server-render\n * only, no client hydration) or an **interactive** island (needs the signals\n * reactivity runtime).\n *\n * This REPLACES the old `_classifyIsland` regex post-pass, which re-derived\n * the answer by scanning generated code for `signal(`/`computed(`/`effect(`/…\n * calls — a Derived-property VIOLATION (docs/plans/ssr-build-performance-\n * findings.md §5: \"the compiler answered that question when it emitted the\n * code\"). The compiler now computes the classification from the IR (the same\n * fact-set that decides which owner-context primitives to import) and records\n * it as this marker; the plugin merely reads it.\n *\n * Crucially the compiler classifies CONSERVATIVELY and knows things the regex\n * could not: e.g. a component whose own body is inert but which declares\n * `$prop`s is `interactive` (props are reactive inputs the parent drives, and\n * its options-form emit is one the static-island shim cannot lower) — the old\n * regex saw no `signal(` call and wrongly classified it `static`.\n *\n * Defaults to `'interactive'` if the marker is somehow absent (an old binary,\n * or a future emit shape): the safe default never strips the runtime out from\n * under a component that needs it.\n *\n * @internal\n */\nexport function _parseIslandMarker(compiledCode: string): 'static' | 'interactive' {\n const m = /^\\/\\/ @aihu:island (static|interactive)$/m.exec(compiledCode)\n return m?.[1] === 'static' ? 'static' : 'interactive'\n}\n\n/** Best-effort message text for an unknown thrown value. @internal */\nexport function _errMessage(err: unknown): string {\n if (err instanceof Error) return err.message\n if (typeof err === 'string') return err\n const m = (err as { message?: unknown } | null)?.message\n return typeof m === 'string' ? m : String(err)\n}\n\n/**\n * Is this `import('vite')` rejection the ONE legitimate \"there is no Vite here\"\n * case — a standalone `transform()` caller, a unit test, any non-Vite host?\n *\n * The distinction matters because the two outcomes are opposites: \"no Vite\"\n * must hand the TypeScript back untouched (the caller owns it and never asked\n * for a strip), while \"Vite is here and something broke\" must throw, because\n * un-stripped TypeScript returned into a Vite build is silent corruption that\n * only surfaces as an unrelated bundler `PARSE_ERROR` much later.\n *\n * The test is deliberately narrow: a module-resolution failure whose subject is\n * the `vite` specifier itself. Both Node and Bun report `ERR_MODULE_NOT_FOUND`\n * with a message naming the package (`Cannot find package 'vite' …`). A\n * resolution failure for something else — a broken transitive dependency of an\n * installed Vite, say — is NOT this case: Vite is present, the strip is\n * expected, and swallowing it would be the same silent corruption. So it is\n * loud.\n *\n * @internal\n */\nexport function _isViteMissing(err: unknown): boolean {\n const message = _errMessage(err)\n const code = (err as { code?: unknown } | null)?.code\n const isResolutionFailure =\n code === 'ERR_MODULE_NOT_FOUND' ||\n code === 'MODULE_NOT_FOUND' ||\n /cannot find (module|package)/i.test(message)\n if (!isResolutionFailure) return false\n // The unresolved specifier must be `vite` itself, quoted the way both\n // runtimes quote it, not merely a path that happens to contain \"vite\".\n return /['\"`]vite['\"`]/.test(message)\n}\n\n/**\n * The message for a strip that failed with Vite present — names the branch, the\n * Vite version, the environment and the file, and says why it is fatal rather\n * than swallowed.\n *\n * @internal\n */\nexport function _stripFailure(\n fn: 'transformWithOxc' | 'transformWithEsbuild',\n id: string,\n viteVersion: string,\n isServerEnv: boolean,\n err: unknown,\n): string {\n return (\n `[@aihu/compiler] TypeScript strip failed for ${id} — ` +\n `vite ${viteVersion} \\`${fn}\\` (${isServerEnv ? 'server' : 'client'} environment) threw. ` +\n 'Returning the un-stripped TypeScript would corrupt the build silently and ' +\n 'resurface as an unrelated bundler PARSE_ERROR on this file, so it fails here instead. ' +\n `Underlying error: ${_errMessage(err)}`\n )\n}\n\n/** What the Vite plugin's `transform` hook hands back after the strip. */\nexport interface StripTypesResult {\n readonly code: string\n readonly map: null\n /** Rolldown-only hint; set ONLY on the last-resort no-transform branch. */\n readonly moduleType?: 'ts'\n}\n\n/**\n * The subset of the Vite module `_stripTypes` uses — the seam tests fake.\n *\n * The trailing parameters are `any` on purpose: Vite's real signatures carry\n * version-specific option/config/watcher types (and MORE parameters on some\n * versions), and a narrower type here would make the genuine module fail to\n * satisfy the interface. Only the first two parameters and `code` on the result\n * are actually depended on.\n */\nexport interface ViteStripApi {\n readonly version?: string\n readonly transformWithOxc?: (\n code: string,\n id: string,\n // biome-ignore lint/suspicious/noExplicitAny: variance seam — see doc comment\n ...rest: any[]\n ) => Promise<{ code: string }>\n readonly transformWithEsbuild?: (\n code: string,\n id: string,\n // biome-ignore lint/suspicious/noExplicitAny: variance seam — see doc comment\n ...rest: any[]\n ) => Promise<{ code: string }>\n}\n\n/**\n * Strip TypeScript from compiler output using whichever transform the resolved\n * Vite exposes. Takes the Vite module as a PARAMETER so the branch order and\n * the failure behaviour are testable without installing four Vite versions.\n *\n * Branch order, and why:\n *\n * 1. `transformWithOxc` — Vite's own transform, needing NO separate esbuild.\n * Vite 8 made esbuild an OPTIONAL PEER while still *exporting* a\n * `transformWithEsbuild` that throws \"It is deprecated and it now requires\n * esbuild to be installed separately … migrate to `transformWithOxc`\" the\n * moment it is called. So this branch is deliberately NOT gated on the\n * environment: a fresh consumer install at vite 8 has no esbuild at all, and\n * the esbuild branch would throw on the CLIENT build — which every output\n * mode (`spa`, `static`, `ssr`) runs.\n * 2. `transformWithEsbuild` — vite 6 and 5, where `transformWithOxc` does not\n * exist (`'transformWithOxc' in vite` is literally false on 6.4.3), so those\n * versions keep taking this branch and their output is unchanged.\n * 3. Neither — hand the TypeScript to Rolldown with `moduleType: 'ts'`. A\n * forward-compatibility escape hatch for a Vite that drops both.\n *\n * Preferring oxc on vite 8 is a DELIBERATE behaviour change, not a refactor:\n * oxc lowers class fields with `useDefineForClassFields: true` (the modern-TS\n * default) where esbuild used `false`, lowers enums to a different (equivalent)\n * IIFE shape, and does not constant-inline enum member reads. Invisible for\n * compiler-generated code, observable for user-authored classes in an `.aihu`\n * script block. Pinned by `tests/strip-branch.test.ts`.\n *\n * Failures here are LOUD. Both call sites are individually wrapped so the\n * thrown error names the branch, the Vite version, the environment and the\n * file. The alternative — the swallowing `catch` this replaced — returned\n * un-stripped TypeScript that only surfaced as an unrelated `PARSE_ERROR` from\n * the bundler two hundred lines of build output later.\n *\n * @internal\n */\nexport async function _stripTypes(\n vite: ViteStripApi,\n code: string,\n id: string,\n isServerEnv: boolean,\n): Promise<StripTypesResult> {\n const viteVersion = vite.version ?? 'unknown'\n if (typeof vite.transformWithOxc === 'function') {\n try {\n const stripped = await vite.transformWithOxc(code, 'component.ts', {\n lang: 'ts',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n } catch (err) {\n throw new Error(_stripFailure('transformWithOxc', id, viteVersion, isServerEnv, err), {\n cause: err,\n })\n }\n }\n if (typeof vite.transformWithEsbuild === 'function') {\n try {\n const stripped = await vite.transformWithEsbuild(code, 'component.ts', {\n target: 'esnext',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n } catch (err) {\n throw new Error(_stripFailure('transformWithEsbuild', id, viteVersion, isServerEnv, err), {\n cause: err,\n })\n }\n }\n return { code, moduleType: 'ts', map: null }\n}\n\n/**\n * §22 — parse the `// @aihu:component-tags a,b,c` marker the Rust codegen emits\n * for every server/universal build, on the same channel as `@aihu:island` above.\n *\n * The list comes from `collect_component_tags` — the SAME walk that fills\n * `route.json`'s `components` array — so it is already sorted, de-duplicated and\n * kebab-normalized when it arrives here. Deliberately NOT re-sorted or\n * re-de-duplicated: a second normalization would be a second place the rule\n * lives, and the two would drift.\n *\n * Distinct from the `__aihu_child_tags__` derivation below: this is \"tags the\n * template references AT ALL\", not \"tags the compiled renderer will look up\".\n * See the comment on the `__aihu_referenced_tags__` export for why both exist.\n *\n * Returns `[]` when the marker is absent (an older binary, a client-target\n * compile, a future emit shape) — the caller treats that identically to \"the\n * template references nothing\".\n *\n * @internal\n */\nexport function _parseComponentTagsMarker(compiledCode: string): string[] {\n const m = /^\\/\\/ @aihu:component-tags (.+)$/m.exec(compiledCode)\n return m === null ? [] : (m[1] as string).split(',')\n}\n\n/**\n * Derive the `__aihu_child_tags__` set from SERVER-TARGET compiled code: the\n * tags the compiled string renderer will actually look up, read off the\n * `__aihu_schild('<tag>'` call sites the Rust codegen emitted. Deduped, sorted.\n *\n * THE one derivation, deliberately. There are two consumers:\n *\n * 1. `aihuCompilerPlugin`'s transform, which turns the result into the\n * `export const __aihu_child_tags__` a compiled module carries.\n * 2. `@aihu/router`'s `genSC`, which needs the SAME edge set at CODEGEN\n * time — before any module exists to read an export off — to walk from\n * the pages out to the components the server bundle must actually carry.\n *\n * `genSC` reaching for this instead of re-deriving is the whole point. The\n * `__aihu_referenced_tags__` docblock below spends a paragraph arguing that\n * deriving the runtime edge set a second way would be \"one rule written in two\n * places, and the halves would drift the first time a boundary moved\" — and\n * that argument does not stop applying because the second site happens to live\n * in another package. `readAihuLayoutComponents` (the source regex) is a THIRD,\n * differently-defined set and is not a substitute: it counts references the\n * emitter DECLINES (an attribute, children, a dynamic path), which produce no\n * call site, so `__aihu_schild` can never look them up. Measured on a\n * three-way fixture, it bundled a module whose rendered output was empty.\n *\n * Takes compiled code, not a source string: a caller with only source runs\n * `transform(src, id, { target: 'server' }).code` first — which is memoized, so\n * a file already compiled in this process costs a map lookup. Client- and\n * universal-target output carries no `__aihu_schild` call sites at all and\n * yields `[]`, which is correct: there is no server render to feed.\n *\n * @internal\n */\nexport function _deriveChildTags(compiledCode: string): string[] {\n return [\n ...new Set(\n Array.from(compiledCode.matchAll(/__aihu_schild\\('([^']+)'/g), (m) => m[1] as string),\n ),\n ].sort()\n}\n\n/**\n * GX Phase 1 (#437-GX) — parse the `// @aihu:extract read=<v> call=<v>` code\n * marker the Rust compiler emits for every server/universal build (the\n * resolved policy, the ratified default included). Phase 1 consumes it only\n * for the build census below; Phase 4 (E2) will read the SAME marker for\n * governed chunk routing.\n * @internal\n */\nexport function _parseExtractMarker(code: string): { read: string; call: string } | null {\n const m = /^\\/\\/ @aihu:extract read=(\\S+) call=(\\S+)$/m.exec(code)\n return m ? { read: m[1] as string, call: m[2] as string } : null\n}\n\n/**\n * GX Phase 1 (#437-GX) — format the per-value extract census (the DA-e census\n * pattern from #437: every build PRINTS the posture distribution, so the\n * default-vs-declared migration story stays visible rather than silent).\n * Returns the printable lines; pure so tests can assert the counts.\n * @internal\n */\nexport function _formatExtractCensus(\n census: ReadonlyMap<string, { read: string; call: string }>,\n): string[] {\n if (census.size === 0) return []\n const readCounts = new Map<string, number>()\n const callCounts = new Map<string, number>()\n for (const { read, call } of census.values()) {\n readCounts.set(read, (readCounts.get(read) ?? 0) + 1)\n callCounts.set(call, (callCounts.get(call) ?? 0) + 1)\n }\n const lines = [`[aihu] extract census — ${census.size} surface(s)`]\n for (const [value, n] of [...readCounts.entries()].sort()) lines.push(` read=${value}: ${n}`)\n for (const [value, n] of [...callCounts.entries()].sort()) lines.push(` call=${value}: ${n}`)\n return lines\n}\n\n/**\n * Extract the custom element tag name from compiler-emitted code.\n * The compiler always emits `defineElement('tag-name', ...)` as the\n * first call — pull the first string literal argument.\n * Returns `null` if no `defineElement` call is found.\n * @internal\n */\nfunction _extractElementTag(code: string): string | null {\n const m = /defineElement\\(\\s*['\"]([^'\"]+)['\"]/m.exec(code)\n return m ? (m[1] ?? null) : null\n}\n\n/**\n * §9.4 — is this compiled module a base-extending recipe, i.e. does it call\n * `defineComponent({ … base: … })` with an options object rather than a bare\n * setup function? Such a component cannot take the static-island shim, which\n * inlines `class extends HTMLElement` and has no way to honour a base class.\n *\n * Shape B of the ReDoS note at the top of this file. Equivalent to the single\n * ``/defineComponent\\(\\s*\\{[^]*?\\bbase\\s*:/`` this replaced: the head\n * sub-pattern is unchanged, and `base:` appearing after some LATER\n * `defineComponent({` implies it also appears after the first one — so testing\n * only the first head is the same predicate, without the lazy re-scan that\n * every repetition of the head literal used to restart.\n * @internal\n */\nfunction _hasBaseRecipe(code: string): boolean {\n const head = /defineComponent\\(\\s*\\{/.exec(code)\n if (head === null) return false\n const key = /\\bbase\\s*:/g\n key.lastIndex = head.index + head[0].length\n return key.test(code)\n}\n\n/** Strip trailing `/` characters via a plain scan, not a `\\/+$/`-anchored\n * regex — that shape is vulnerable to catastrophic backtracking on a long\n * run of slashes with no match (CodeQL js/polynomial-redos): the greedy `+`\n * backtracks one character at a time at EVERY starting offset before\n * failing, an O(n²) blowup (measured: ~45s on 200k slashes). */\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */) end--\n return s.slice(0, end)\n}\n\n/**\n * Is `rawId` a layout SFC (a `.aihu` file under the configured layouts dir)?\n * Root-independent: matches the `<layoutsDir>/` segment anywhere in the path,\n * which is sufficient because the layouts dir is a project-relative convention.\n * @internal\n */\nexport function _isLayoutFile(rawId: string, layoutsDir: string): boolean {\n const ld = trimTrailingSlashes(layoutsDir.replace(/\\\\/g, '/').replace(/^\\.?\\//, ''))\n if (!ld) return false\n return rawId.replace(/\\\\/g, '/').includes(`/${ld}/`)\n}\n\n/**\n * Layout custom-element tag for a filename stem. MUST match\n * `@aihu/router`'s `layoutTagFor()` so the generated `virtual:aihu-layouts`\n * map and the registered element agree on the tag.\n * @internal\n */\nexport function _layoutTag(stem: string): string {\n return `aihu-layout-${stem.toLowerCase()}`\n}\n\n/**\n * O1a (tag naming) — JS mirror of the Rust compiler's\n * `tags::kebab_component_tag` (packages/compiler/src/tags.rs). PascalCase→kebab,\n * else lowercase-verbatim: inserts '-' before an uppercase letter when the\n * previous char is lowercase/digit, OR (acronym boundary) the previous char is\n * uppercase and the next is lowercase; then lowercases all. Applied to the\n * file stem before it is passed as `--tag` so the JS driver's define-name\n * matches the Rust one (`UserCard.aihu` → `user-card`). Validation/erroring\n * (C450) is owned by the Rust compiler — this is the infallible transform only.\n * @internal\n */\nexport function kebabComponentTag(raw: string): string {\n let out = ''\n for (let i = 0; i < raw.length; i++) {\n // charAt (not raw[i]) returns string, never string|undefined — satisfies\n // noUncheckedIndexedAccess; charAt(i+1) yields '' past the end, matching\n // the \"no next char\" case.\n const c = raw.charAt(i)\n if (i > 0 && c >= 'A' && c <= 'Z') {\n const prev = raw.charAt(i - 1)\n const next = raw.charAt(i + 1)\n const prevLower = prev >= 'a' && prev <= 'z'\n const prevDigit = prev >= '0' && prev <= '9'\n const prevUpper = prev >= 'A' && prev <= 'Z'\n const nextLower = next >= 'a' && next <= 'z'\n if (prevLower || prevDigit || (prevUpper && nextLower)) out += '-'\n }\n out += c.toLowerCase()\n }\n return out\n}\n\nconst OUTLET_HEAD = 'const createOutletBoundary = () => {'\nconst OUTLET_PASSIVE = `const createOutletBoundary = () => branch('div', { 'data-aihu-outlet': '' }, []);`\n\n/**\n * Collapse the reactive `<outlet>` boundary the Rust codegen emits into a\n * passive `data-aihu-outlet` marker. Layout SFCs are rendered by `@aihu/app`'s\n * imperative client renderer, which fills the marker itself; the default\n * boundary's mount-time `effect()` reads `useRoute()` (null under the imperative\n * path) and clears the marker, which would wipe the page the renderer inserts.\n *\n * Anchors on the exact `const createOutletBoundary = () => { … return host; };`\n * block the codegen emits (`packages/compiler/src/codegen/emit.rs`). No-op when\n * the layout declares no `<outlet>`.\n *\n * Shape B of the ReDoS note at the top of this file: the head is located with\n * `indexOf` and the tail scanned once from there, instead of one regex whose\n * lazy `[\\s\\S]*?` re-scanned the whole module at every repetition of the head\n * literal.\n * @internal\n */\nexport function _passivizeOutlet(code: string): string {\n const head = code.indexOf(OUTLET_HEAD)\n if (head === -1) return code\n // `[^\\S\\n]*\\n` (horizontal whitespace, then the line break) rather than\n // `\\s*\\n`: the codegen emits ` return host;\\n};` and `\\s` matching `\\n`\n // made the run ambiguous. Sticky-free `g` + an explicit `lastIndex` starts\n // the single tail scan immediately after the head.\n const tailRe = /return host;[^\\S\\n]*\\n\\};/g\n tailRe.lastIndex = head + OUTLET_HEAD.length\n const tail = tailRe.exec(code)\n if (tail === null) return code\n return code.slice(0, head) + OUTLET_PASSIVE + code.slice(tail.index + tail[0].length)\n}\n\n/**\n * Instrument a compiled `.aihu` module with HMR support.\n *\n * The compiler always emits:\n *\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { ... }))\n *\n * This function:\n *\n * 1. Adds `_hmrReplace` to the `@aihu/runtime` import.\n * 2. Prepends a module-level slot variable `__aihu_setup__`.\n * 3. Rewrites the single `defineComponent(` call so the setup function\n * is captured via an assignment expression:\n * `defineComponent(__aihu_setup__ = ` (valid JS; assignment has\n * lower precedence than arrow fn, so `defineComponent` still\n * receives the function as its argument).\n * 4. Appends `export { __aihu_setup__ as default }` so that Vite's\n * `import.meta.hot.accept` callback receives the new setup via\n * `newModule.default` on hot reload.\n * 5. Appends the `import.meta.hot.accept` block, gated on `__DEV__`.\n *\n * The `__DEV__` guard ensures production bundlers (where they replace\n * `__DEV__` with `false`) dead-code-eliminate the entire HMR block.\n *\n * @internal\n */\nfunction _buildHmrCode(compiledCode: string, elementTag: string): string {\n // Step 1 — add _hmrReplace to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hmrReplace')) parts.push('_hmrReplace')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Step 2+3 — prepend slot variable and rewrite the defineComponent call.\n // Compiler emits exactly one `defineComponent(` followed by a function expr.\n // Rewrite: defineComponent(fn) → defineComponent(__aihu_setup__ = fn)\n // Assignment expression evaluates to `fn`, so defineComponent still\n // receives the setup function as its first argument unchanged.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const preamble = `let __aihu_setup__: ((ctx: any) => any) | undefined\\n`\n\n const patchedBody = withImport.replace(/\\bdefineComponent\\(/, 'defineComponent(__aihu_setup__ = ')\n\n const tag = JSON.stringify(elementTag)\n // Step 4+5 — postamble with default export and HMR acceptance.\n const postamble = `\nexport { __aihu_setup__ as default }\n\nif (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (!newModule) return\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const newSetup = (newModule as any)['default']\n if (typeof newSetup !== 'function') return\n document.querySelectorAll(${tag}).forEach((el) => {\n _hmrReplace(el as HTMLElement, newSetup)\n })\n })\n}\n`\n\n return preamble + patchedBody + postamble\n}\n\n/**\n * Rewrite an interactive-island module so its `connectedCallback` waits\n * for the element to scroll into view before mounting. Plan 3.3 — applied\n * only when the consumer adds `defer` to the custom element tag (e.g.\n * `<my-counter defer>`); the runtime helper checks the attribute and\n * either mounts immediately or registers an `IntersectionObserver`.\n *\n * Implementation: the helper is added as a `_hydrateOnVisible` import\n * from `@aihu/runtime`, and the compiler-emitted `defineElement(...)`\n * call is wrapped in a `defineElement` that intercepts `connectedCallback`\n * to honour the `defer` attribute.\n *\n * The whole indirection is tree-shaken when no `.aihu` module reaches\n * this branch, because `_hydrateOnVisible` is exported from its own\n * sibling module inside `@aihu/runtime`.\n *\n * @internal\n */\nexport function _buildDeferredHydration(compiledCode: string, elementTag: string): string {\n // Add _hydrateOnVisible to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hydrateOnVisible')) parts.push('_hydrateOnVisible')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Wrap the class returned by defineComponent BEFORE defineElement\n // consumes it. The HTML spec caches lifecycle callbacks at\n // customElements.define() time, so we MUST mutate the prototype\n // before that call — not after. We accomplish this with a synchronous\n // helper invoked between defineComponent and defineElement.\n //\n // Source pattern (compiler-emitted):\n // defineElement('tag', defineComponent((_ctx) => { ... }))\n //\n // After this rewrite:\n // defineElement('tag', __aihu_wrap_defer__(defineComponent((_ctx) => { ... })))\n //\n // …with __aihu_wrap_defer__ defined in the appended preamble.\n const patched = withImport.replace(\n /defineElement\\(\\s*('[^']+'|\"[^\"]+\")\\s*,\\s*defineComponent\\(/,\n (_m, tagLit: string) => `defineElement(${tagLit}, __aihu_wrap_defer__(defineComponent(`,\n )\n // Match the closing `))` of the defineElement call. The HMR pass may\n // have inserted `__aihu_setup__ = ` before the inner function, but\n // the trailing `))` shape is unchanged. Replace exactly one occurrence\n // by anchoring on end-of-string trim; bail if the shape does not match.\n if (patched === withImport) {\n // The expected `defineElement(<tag>, defineComponent(` shape was not\n // present (e.g. compiler output changed). Skip defer wrapping rather\n // than emit broken code.\n return compiledCode\n }\n // Add a trailing `)` to balance the extra `(` from __aihu_wrap_defer__.\n // Source shape after _buildHmrCode is:\n // defineElement('tag', defineComponent(__aihu_setup__ = (_ctx) => {...}))\n // export { __aihu_setup__ as default }\n // if (typeof __DEV__ !== ...) { ... }\n // We must close BEFORE the export line. Match the first `))` followed\n // by a newline and `export` (or end-of-string for the unwrapped case).\n let balanced = patched.replace(/\\)\\s*\\)\\s*\\nexport\\s/, ')))\\nexport ')\n if (balanced === patched) {\n // No HMR postamble — the `))` is at end-of-string.\n balanced = patched.replace(/\\)\\s*\\)\\s*$/, ')))\\n')\n }\n if (balanced === patched) {\n // Could not find the matching `))` — bail out.\n return compiledCode\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const helper = `\n// Plan 3.3 (Islands) — defer attribute support. Wraps the constructor\n// returned by defineComponent so instances bearing the \\`defer\\` attribute\n// hydrate lazily via IntersectionObserver. Bare instances retain the\n// eager Plan 3.2 hydration path.\nfunction __aihu_wrap_defer__<T extends typeof HTMLElement>(Ctor: T): T {\n const orig = (Ctor.prototype as unknown as { connectedCallback?: () => void }).connectedCallback\n if (typeof orig !== 'function') return Ctor\n ;(Ctor.prototype as unknown as { connectedCallback: () => void }).connectedCallback = function (this: HTMLElement) {\n if (this.hasAttribute('defer')) {\n _hydrateOnVisible(this, () => orig.call(this))\n } else {\n orig.call(this)\n }\n }\n return Ctor\n}\n`\n void elementTag\n return helper + balanced\n}\n\n/**\n * Build a static-island shim for a compiled module.\n *\n * The compiled module emitted by the Rust codegen has the shape:\n *\n * import { branch, leaf, slot } from '@aihu/arbor'\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { return <tree> }))\n *\n * For a static island we know `<tree>` contains no `signal(`/`computed(`\n * calls. We can therefore:\n *\n * 1. Drop the `@aihu/runtime` import (saves ~600 B gz of defineComponent\n * + defineElement + bootstrap glue).\n * 2. Replace `defineElement(tag, defineComponent(setup))` with a tiny\n * inline class that mounts the tree directly via `mount()` (which the\n * arbor barrel already exports).\n * 3. Tag the file with a `// AIHU_STATIC_ISLAND` comment so consumers\n * can audit which routes shipped zero-JS-runtime.\n *\n * Falls back to the original code if the regex shape does not match\n * (defensive: a future compiler change must opt back into static-island\n * emission explicitly rather than silently break).\n *\n * @internal\n */\nexport function _buildStaticIsland(compiledCode: string, elementTag: string): string {\n // Confirm the shape we expect: a single defineElement(...) call wrapping\n // a single defineComponent(...) call. Bail out otherwise.\n const callRe = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/\n if (!callRe.test(compiledCode)) return compiledCode\n\n // Strip the `@aihu/runtime` import line entirely — static islands\n // don't reference defineComponent/defineElement after the rewrite.\n const withoutRuntimeImport = compiledCode.replace(\n /^[^\\S\\r\\n]*import\\s*\\{[^{}]*\\}\\s*from\\s*'@aihu\\/runtime'(?:\\s*;)?\\s*$/m,\n '',\n )\n\n // Ensure `mount` is imported from @aihu/arbor (it already exposes\n // branch/leaf/slot, so we just append `mount` to the existing list).\n const withArborMount = withoutRuntimeImport.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n\n // Which tail shape this module ends in, and whether an island is safe.\n //\n // `_injectShadowMode` runs BEFORE this in the transform pipeline, so the\n // `defineElement(...)` call may already carry a third argument and no longer\n // end in `))`. Rewriting the head while the tail rewrite silently no-ops\n // produced a module with an unclosed class body and a dangling options\n // object — invalid JS, emitted with no error, which surfaced downstream as a\n // confusing `[PARSE_ERROR] … invalid JS syntax` naming the user's `.aihu`\n // file. Reachable today with `islands: true` + `shadowMode: 'shadow'`, on\n // vite 6 as well as 8.\n //\n // `shadowMode: 'shadow'` alone is safe to drop: the inline class attaches its\n // own `{ mode: 'open' }` shadow root, which is precisely what that option\n // asks for. ANY other option (`formAssociated`, `lightScopeId`, a future\n // field) carries behaviour this class does not implement, so the island is\n // DECLINED — the component keeps the ordinary `defineElement` path, exactly\n // as the \"falls back to the original code\" contract above promises.\n const TAIL_PLAIN = /\\)\\s*\\)\\s*$/\n // `_injectShadowMode`'s two-argument branch (line ~314) always emits this\n // EXACT literal shape — `, { shadowMode: 'shadow' }` with one space at each\n // junction, never a trailing comma — so the `\\s*,?\\s*` this used to have\n // around the optional comma was dead flexibility, never exercised by real\n // compiler output, and it was the ReDoS: two adjacent `\\s*` groups with\n // only an optional zero-width `,?` between them let a long non-matching\n // whitespace run split across the pair in O(n) ways per position. A single\n // `\\s*` per junction, none of them adjacent to another, matches the same\n // real input with no such ambiguity.\n const TAIL_WITH_SHADOW_ONLY = /\\),\\s*\\{\\s*shadowMode:\\s*'shadow'\\s*\\}\\)\\s*$/\n const tail = TAIL_PLAIN.test(withArborMount)\n ? TAIL_PLAIN\n : TAIL_WITH_SHADOW_ONLY.test(withArborMount)\n ? TAIL_WITH_SHADOW_ONLY\n : null\n if (tail === null) return compiledCode\n\n // Replace `defineElement('tag', defineComponent((_ctx) => { ... }))`\n // with an inline `customElements.define` whose connectedCallback mounts\n // the static tree. The setup function is captured verbatim by replacing\n // the wrapping calls with anonymous-IIFE bookends.\n const tagJson = JSON.stringify(elementTag)\n const rewritten = withArborMount\n .replace(\n /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/,\n `customElements.define(${tagJson}, class extends HTMLElement {\\n connectedCallback() {\\n const root = this.attachShadow({ mode: 'open' })\\n const __aihu_setup__ = (`,\n )\n .replace(tail, `)\\n mount(__aihu_setup__({ host: root, element: this }), root)\\n }\\n})\\n`)\n\n return `// AIHU_STATIC_ISLAND — zero @aihu/runtime references\\n${rewritten}`\n}\n\n/**\n * Compile a .aihu source string to TypeScript.\n * map is null — source maps are deferred to v1 (OQ-C8)\n *\n * B3b — when `sidecarOut` is provided, also writes the per-SFC `.aihu.ts`\n * sidecar at that path. Callers (e.g. the Vite plugin) typically pass\n * `<source-id>.ts` so `tsc --noEmit` discovers per-SFC template expressions.\n */\nexport function transform(\n source: string,\n id: string,\n options?: {\n sidecarOut?: string\n target?: 'client' | 'server' | 'universal'\n /** Override the registered custom-element tag (default: file stem). Used for layouts. */\n tag?: string\n /**\n * #486 step 4 — emit the sidecar's attribute/component-prop type layer\n * (`--strict-templates`). Affects only the type-check surface written to\n * `sidecarOut`; the compiled JS is identical either way. Default off.\n */\n strictTemplates?: boolean\n },\n): { code: string; map: null } {\n // O1a (tag naming): normalize the stem so the JS driver's define-name\n // matches the Rust compiler's (`UserCard.aihu` → `user-card`). When a\n // component-shaped (uppercase-first) stem normalizes to a hyphen-less name\n // (`Comment.aihu` → `comment`), pass the RAW stem instead so the Rust\n // compiler surfaces its C450 error — the JS never validates or errors, but\n // it must not mask the error by pre-lowercasing. An explicit `options.tag`\n // (e.g. `_layoutTag` for layouts) passes through untouched.\n const rawStem = basename(id, '.aihu')\n const kebabStem = kebabComponentTag(rawStem)\n const stem = /^[A-Z]/.test(rawStem) && !kebabStem.includes('-') ? rawStem : kebabStem\n const args = ['--stdin', '--tag', options?.tag ?? stem, '--path', id]\n if (options?.sidecarOut) {\n args.push('--sidecar-out', options.sidecarOut)\n }\n // T6 (go-public demo) — thread the build target so a client bundle gets the\n // policy-free `@agent` dispatcher (and the per-instance registration the\n // capability bridge needs) instead of the server `__agentBinding`. Defaults to\n // the compiler's `universal` target when omitted (existing behaviour).\n if (options?.target) {\n args.push('--target', options.target)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n // `sidecarOut` BYPASSES the memo AND the envelope backends: it makes the\n // spawn write a file on disk — a cache hit would silently skip that side\n // effect, and the envelope API deliberately has no file-writing emits. The\n // Vite build path never passes it, so the SSG double-compile still fully\n // hits.\n if (options?.sidecarOut) {\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveBinPath()\n const startedAt = Date.now()\n try {\n const code = execFileSync(bin, args, {\n input: source,\n encoding: 'utf8',\n ...compileSpawnBounds(source.length),\n })\n return { code, map: null }\n } catch (err) {\n const described = describeSpawnFailure(err, bin, args, source.length, Date.now() - startedAt)\n throw described ?? err\n }\n }\n // Memo key: everything that shapes the emitted code. `id` is hashed\n // separately; the fingerprint carries the explicit options (`tag` covers the\n // layout override — the default stem is a pure function of `id`). The stamp\n // is the ACTIVE backend's file identity (native addon `.node` path when\n // in-process, CLI binary path when spawning) — see js/envelope.ts.\n const stamp = _backendStampPath()\n const target = options?.target ?? 'universal'\n // Sibling-artifact seeding (the single-parse envelope win): one compile of\n // a file yields js + ast + route, and the ast/route strings are seeded into\n // the memo under the exact keys `compileToAst` / `compileRouteMeta` derive\n // for the same (source, id) — so css-engine's AST pass and the router's\n // route scan become cache hits instead of re-parses. Only sound when the\n // tag is NOT overridden: those callers derive their own stem from `id`, and\n // an explicit tag (layout mode) resolves to a different define-name.\n const seedSiblings = options?.tag === undefined\n const code = _memoizedSpawn(\n 'transform',\n source,\n id,\n `target=${options?.target ?? ''}|tag=${options?.tag ?? ''}|strict=${options?.strictTemplates === true}`,\n stamp,\n () => {\n const reply = _compileViaBackend(source, args, {\n tag: options?.tag ?? stem,\n path: id,\n targets: [target],\n emits: seedSiblings ? ['js', 'ast', 'route'] : ['js'],\n ...(options?.strictTemplates ? { strictTemplates: true } : {}),\n })\n // Legacy reply — an older binary ignored `--envelope` and answered the\n // classic single-target request; its stdout IS the compiled JS.\n if (reply.kind === 'legacy') return reply.output\n const envelope = reply.envelope\n if (seedSiblings) {\n if (envelope.astJson !== undefined) {\n _seedMemo('ast', source, id, '', stamp, envelope.astJson)\n }\n _seedMemo('route', source, id, '', stamp, envelope.routeJson ?? 'null')\n }\n const js = envelope.targets[target]?.js\n if (js === undefined) {\n throw new Error(`[@aihu/compiler] envelope reply missing js for target '${target}'`)\n }\n return js\n },\n )\n return {\n code,\n map: null, // source maps deferred to v1 (OQ-C8)\n }\n}\n\n/**\n * Escape a CSS string for safe interpolation inside a JS template literal.\n * The Rust codegen places the authored `@style` body raw inside a backtick\n * literal, so it already assumes no backticks in `@style`. Provider output\n * (theme tokens + utility rules) likewise never contains backticks, but we\n * escape `\\`, `` ` `` and `${` defensively so a future token value can't\n * break out of the literal.\n *\n * @internal\n */\nfunction _escapeForTemplateLiteral(css: string): string {\n return css.replace(/\\\\/g, '\\\\\\\\').replace(/`/g, '\\\\`').replace(/\\$\\{/g, '\\\\${')\n}\n\n/**\n * Fold provider-produced scoped CSS into a compiled `.aihu` module.\n *\n * The Rust codegen emits the authored `@style` block (when present) as:\n *\n * const __style__ = new CSSStyleSheet();\n * __style__.replaceSync(`<authored css>`);\n * defineElement('tag', defineComponent((ctx) => {\n * (ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];\n * return ...\n * }))\n *\n * The default css-engine provider and explicit `AihuCssProvider` implementations\n * return the COMPLETE per-SFC stylesheet:\n * `:host` theme tokens, the variant-resolved utility-class rules, AND the\n * folded authored `@style` block (under an `authored @style` CSS comment).\n * So it is authoritative — we adopt it as the single shadow `<style>` and\n * the authored `@style` keeps emitting through it (acceptance: \"@style still\n * emits correctly alongside\").\n *\n * Two shapes are handled:\n *\n * 1. **SFC has an `@style` block** — the Rust codegen already declared\n * `__style__` with the raw `@style` body. We REPLACE that body with the\n * provider output (which already CONTAINS the `@style` block) so the\n * `@style` rules are not duplicated. The existing `adoptedStyleSheets`\n * assignment is reused unchanged.\n *\n * 2. **SFC has NO `@style` block** — there is no `__style__`. We inject a\n * fresh `__style__` declaration after the last import and an\n * `adoptedStyleSheets` assignment as the first statement of the setup\n * function. The compiler emits the setup param as `_ctx` in this case;\n * we rename it to `ctx` so the injected `ctx.host` reference resolves.\n *\n * Runs on the RAW compiled output BEFORE the island / HMR / auto-wiring\n * transforms so those passes operate on the folded module uniformly:\n * - The static-island shim calls `__aihu_setup__({ host: root, ... })`\n * where `root` is the shadow root, so `ctx.host` is valid there too.\n * - The HMR / defer passes only touch the `defineElement(...)` wrapper and\n * the runtime import; they do not disturb `__style__` or the setup body.\n *\n * No-ops (returns input unchanged) when `css` is empty/whitespace.\n *\n * @internal\n */\n/**\n * Swap the text between the first `open` delimiter and the first `close`\n * delimiter that follows it, keeping both delimiters. Returns `null` when the\n * shape is absent so callers can fall through to their \"no anchor\" branch.\n *\n * Shape B of the ReDoS note at the top of this file. This is exactly what\n * ``/(<open>)[^]*?(<close>)/`` matched — `String.replace` takes the leftmost\n * match, and a `close` missing after the first `open` is missing after every\n * later one too — but as two `indexOf` scans it is linear instead of O(n²) in\n * the number of `open` repetitions an authored `@style` block can plant.\n */\nfunction _replaceDelimitedBody(\n code: string,\n open: string,\n close: string,\n body: string,\n): string | null {\n const start = code.indexOf(open)\n if (start === -1) return null\n const bodyStart = start + open.length\n const end = code.indexOf(close, bodyStart)\n if (end === -1) return null\n return code.slice(0, bodyStart) + body + code.slice(end)\n}\n\n/**\n * Fold css-engine utility CSS into the SERVER target's `__aihu_css__` export.\n *\n * The shadow-DOM sibling of `_foldCssStyles`. That one rewrites the\n * client's `__style__.replaceSync(...)`; the server target has no `__style__`\n * (`CSSStyleSheet` is a DOM dependency and the Rust codegen elides it), it has\n * `export const __aihu_css__` — the string `__aihu_schild` inlines as `<style>`\n * inside a declarative shadow template.\n *\n * Without this, that template shipped the authored `@style` block ALONE: no\n * utility classes, no design tokens, no reset. A shadow child prerendered\n * partially unstyled and repainted once its chunk loaded — precisely the #754\n * failure the DSD `<style>` exists to prevent. (The step-4 scoping note claimed\n * no css-engine work was needed. That was true of the Rust emitter, which\n * applies no scoping transform, and wrong about the pipeline: the per-component\n * utility CSS is folded HERE, in the JS layer, and the server target was simply\n * never wired to it.)\n *\n * REPLACES rather than appends, for the same reason shape 1 above does: the\n * Provider output already contains the authored `@style` rules, so appending\n * would duplicate them.\n *\n * Light-DOM components never reach this — their utilities go through the global\n * cascade via `_foldCssStylesGlobal`, and their prerendered markup is\n * covered by the app stylesheet's `@scope([data-a=…])` blocks.\n */\nexport function _foldSsrCssExport(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already produced the export.\n const replaced = _replaceDelimitedBody(\n compiledCode,\n 'export const __aihu_css__ = `',\n '`',\n escaped,\n )\n if (replaced !== null) return replaced\n\n // Shape 2 — no authored @style, so the Rust codegen emitted no export at all.\n // The utility CSS still has to reach the shadow root, so declare it. Appended\n // at the end: the server artifact's exports are order-independent, and this\n // avoids guessing at an anchor the way shape 2 above has to.\n return `${compiledCode}\\nexport const __aihu_css__ = \\`${escaped}\\`\\n`\n}\n\nexport function _foldCssStyles(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already declared __style__. css-engine\n // output already includes that @style block, so REPLACE the replaceSync body\n // (between the backticks) wholesale to avoid duplicating the @style rules.\n // The codegen emits `__style__.replaceSync(`<body>`);` as a single statement;\n // swap the body between the first open delimiter and the `);` that closes it.\n // `_replaceDelimitedBody` splices rather than calling `String.replace`, so a\n // `$` in the CSS can never be read as a replacement-pattern backreference.\n const replaced = _replaceDelimitedBody(compiledCode, '__style__.replaceSync(`', '`);', escaped)\n if (replaced !== null) return replaced\n\n // Shape 2 — no @style block. Inject a fresh stylesheet + adoption.\n // Bail (no-op) if the expected defineComponent setup shape is absent.\n // The setup param is NOT always `ctx`/`_ctx`: an agent component (one with an\n // exposed member) emits `(__aihu_ctx__)` so `_registerAgentServerBinding`\n // can read `__aihu_ctx__?.element`. Capture whatever the param is and inject\n // against it — a literal `(ctx)` anchor silently no-ops on agent components,\n // which shipped scoped utility CSS with no adopted stylesheet.\n const setupRe = /defineComponent\\(\\s*\\((__?[A-Za-z0-9_]+)\\)\\s*=>\\s*\\{/\n const m = setupRe.exec(compiledCode)\n if (m == null) return compiledCode\n // `_ctx` is codegen's \"unused ctx\" marker; the injected adoption uses it, so\n // normalize to `ctx`. Any other name (`ctx`, `__aihu_ctx__`) is referenced\n // elsewhere in the setup body and MUST be preserved verbatim.\n const setupParam = m[1] === '_ctx' ? 'ctx' : m[1]\n\n // Inject the module-level stylesheet declaration after the last import line.\n const lines = compiledCode.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n const decl = `const __style__ = new CSSStyleSheet();\\n__style__.replaceSync(\\`${escaped}\\`);`\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, decl)\n } else {\n lines.unshift(decl)\n }\n let withDecl = lines.join('\\n')\n\n // Inject the adoption as the first statement of the setup body, using the\n // ACTUAL setup param (`ctx` for a plain component, `__aihu_ctx__` for an\n // agent component). `_ctx` is normalized to `ctx` above since the injected\n // statement now references it.\n withDecl = withDecl.replace(\n setupRe,\n `defineComponent((${setupParam}) => {\\n (${setupParam}.host as ShadowRoot).adoptedStyleSheets = [__style__];`,\n )\n return withDecl\n}\n\n/**\n * Virtual-module prefix used by the `shadowMode === 'light'` branch to route\n * per-SFC utility CSS through Vite's built-in CSS pipeline. The plugin\n * (`aihuCompilerPlugin`) implements `resolveId` + `load` for ids matching\n * `VIRTUAL_UTILITY_PREFIX + '<hash>.css'`, returning the stored CSS body so\n * Vite hoists it into the bundle CSS asset (`dist/assets/*.css`) — NOT into\n * `host.adoptedStyleSheets`, which is a no-op when there is no shadow root.\n *\n * The trailing `.css` extension is mandatory: Vite's built-in CSS plugin keys\n * off the extension to know it should run the CSS pipeline on the module.\n *\n * @internal\n */\nexport const VIRTUAL_UTILITY_PREFIX = '\\0virtual:aihu-utility/'\n\n/**\n * djb2-style stable hash over a file id. Shared by `_hashIdForUtilityCss`\n * (virtual-CSS module keying) and `_lightScopeId` (the `data-a` scope id) so\n * the two id-derived-from-hash use sites can't drift onto different hash\n * functions.\n */\nfunction _hashId(id: string): number {\n let h = 5381\n for (let i = 0; i < id.length; i++) {\n h = ((h * 33) ^ id.charCodeAt(i)) >>> 0\n }\n return h\n}\n\n/**\n * Stable short hash for keying the virtual-CSS module per source-SFC id.\n *\n * djb2-style; collisions are tolerable here because (a) each entry stores its\n * own CSS body, so a hash collision would only matter if two distinct SFCs\n * hashed to the same key AND were processed concurrently; (b) collisions are\n * recoverable — Vite would simply load the wrong CSS for one SFC; we still\n * keyed on the unhashed id internally to avoid that. The hash only appears in\n * the bundled asset URL.\n *\n * @internal\n */\nexport function _hashIdForUtilityCss(id: string): string {\n return _hashId(id).toString(36)\n}\n\n/**\n * Deterministic 8-hex-char scope id for a light-DOM component's `data-a`\n * attribute (light-DOM leaf flip, LDF §10 step 1 / step 3). Same underlying\n * hash as `_hashIdForUtilityCss`, reformatted to a fixed-width hex string —\n * the `[data-a=\"<id>\"]` marker format LDF §11 Q4 ratifies is a hex string,\n * and a fixed width keeps every stamped attribute the same byte length.\n * Hashes the query-stripped file id (stable per file path across builds),\n * not file content — matches `_hashIdForUtilityCss`'s existing contract.\n *\n * @internal\n */\nexport function _lightScopeId(id: string): string {\n return _hashId(id).toString(16).padStart(8, '0')\n}\n\n/**\n * Bug 6 — `shadowMode === 'light'` branch.\n *\n * Routes utility CSS to Vite's CSS pipeline (which folds CSS imports into the\n * bundled `dist/assets/*.css` asset) instead of to `host.adoptedStyleSheets`\n * (a no-op on an element with no shadow root). Returns a prelude `import` that\n * the plugin's `resolveId` + `load` hooks resolve to the stored CSS body.\n *\n * The `__style__` shadow path is NOT invoked here — utility CSS for a\n * cascade-mode component MUST hit the global stylesheet, not a per-element\n * stylesheet that would be silently dropped by `HTMLElement`'s setter.\n *\n * Authored `@style` blocks still emit through the Rust codegen's `<style>`\n * node and are unaffected. (If a component opts into `shadowMode: 'light'` and\n * authors an `@style` block, the codegen still wires it through the\n * non-shadow path — that is the runtime's contract, not this hook's.)\n *\n * @internal\n */\nexport function _foldCssStylesGlobal(\n compiledCode: string,\n css: string,\n id: string,\n): { code: string; virtualId: string } | null {\n if (!css.trim()) return null\n const hash = _hashIdForUtilityCss(id)\n const virtualId = `${VIRTUAL_UTILITY_PREFIX}${hash}.css`\n // Prepend the CSS import as a side-effect-only import so Vite's CSS plugin\n // hoists it into the bundle. We use the NULL-byte virtual id form\n // (Rollup/Vite convention for \"owned by this plugin\"); other plugins will\n // skip it. The compiler's transform returns this prepended code, which the\n // downstream esbuild/oxc strip leaves untouched (it's just an import).\n const prelude = `import ${JSON.stringify(virtualId)};\\n`\n return { code: prelude + compiledCode, virtualId }\n}\n\n/** @deprecated Use `_foldCssStyles`; retained for internal consumers during the seam rollout. */\nexport const _foldCssEngineStyles = _foldCssStyles\n\n/** @deprecated Use `_foldCssStylesGlobal`; retained for internal consumers during the seam rollout. */\nexport const _foldCssEngineStylesGlobal = _foldCssStylesGlobal\n\n// ─── v1.0.10a — compiler AST-export hook ─────────────────────────────────────\n//\n// Thin TS wrapper over the `aihu-compile --ast-json` flag. Returns the parsed\n// `.aihu` SFC AST in a stable, serializable shape consumed by the CSS engine's\n// AST scanner (`css-2-ast-scanner`). Mirrors the typed contract in\n// `docs/superpowers/specs/compiler-ast-export-hook.md` §4.\n\n/** Top-level AST export — one per .aihu SFC. */\nexport interface SfcAst {\n /** Resolved custom-element tag name (meta.name → route.name → file stem). */\n tag: string\n /** AST schema version — bumped on any breaking shape change (semver-tied). */\n astVersion: 1\n /** The @style block, if the SFC declared one. */\n style: SfcStyleBlock | null\n /** Parsed template tree. null when the SFC has no @template block. */\n template: SfcNode[] | null\n /** SFC-level metadata. */\n meta: SfcMeta\n /**\n * The compiler-assigned light-DOM scope id for this component's `data-a`\n * attribute, present only when it resolved to `shadowMode: 'light'`\n * (light-DOM leaf flip, LDF §10 step 1). Absent (not just `undefined`, the\n * key itself omitted on the wire) for shadow-mode components — additive,\n * mirrors `aihu-css-core`'s `SfcAst.light_scope_id: Option<String>`.\n */\n lightScopeId?: string\n}\n\nexport interface SfcStyleBlock {\n /** Verbatim CSS body of the @style block (braces stripped, $global token removed). */\n content: string\n /** 'scoped' (default) or 'global' (@style { $global ... }). */\n scope: 'scoped' | 'global'\n}\n\nexport interface SfcMeta {\n /** From @meta { name } / @route { name } / file stem — never null after resolution. */\n name: string\n}\n\n/** Discriminated union mirroring Rust `TemplateNode`. */\nexport type SfcNode =\n | { kind: 'element'; tag: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'macroElement'; name: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'text'; value: string }\n | { kind: 'interpolation'; expr: string }\n | { kind: 'ifBlock'; branches: Array<{ cond: string; body: SfcNode[] }> }\n | {\n kind: 'eachBlock'\n list: string\n item: string\n idx: string | null\n key: string | null\n body: SfcNode[]\n emptyBody: SfcNode[] | null\n }\n | { kind: 'htmlBlock'; expr: string }\n\n/** Discriminated union mirroring Rust `Attr` — the three class-forms key on `kind`. */\nexport type SfcAttr =\n | { kind: 'static'; name: string; value: string } // Form A\n | { kind: 'binding'; name: string; expr: string } // Form B\n | { kind: 'macro'; name: string; value: SfcMacroValue } // Form C (and on:/bind:/emit:/if/each/…)\n\nexport type SfcMacroValue =\n | { form: 'quoted'; value: string }\n | { form: 'curly'; expr: string }\n | { form: 'boolean' }\n\n/**\n * Parse a .aihu source string to its structured AST.\n *\n * Thin wrapper over the Rust binary (mirrors `transform()`): spawns\n * `aihu-compile --stdin --tag <stem> --ast-json`, feeds `source` on stdin, and\n * `JSON.parse`s stdout. `id` is optional and only used to derive the tag stem\n * and the `--path` arg (for `@route` C500 checks), identical to `transform()`.\n *\n * Throws on parse failure — the Rust binary exits non-zero and `execFileSync`\n * surfaces the diagnostic (same error path as `transform()`).\n */\n/**\n * Compile an SFC to its TYPE-CHECK SURFACE and return it as a string — the\n * `.aihu.ts` sidecar's content, without writing a file.\n *\n * This is the in-memory path `aihu-tsc` uses to hand `.aihu` files to TypeScript\n * as virtual files. The surface is line-preserving: line N of the returned text\n * corresponds to line N of the `.aihu` source, which is what lets a `tsc`\n * diagnostic be mapped straight back to the line the author wrote.\n *\n * Returns `''` when the SFC has no `@template` (nothing to check).\n */\nexport function compileSidecar(\n source: string,\n id?: string,\n options?: {\n /**\n * #486 step 4 — emit the attribute/component-prop type layer\n * (`--strict-templates`). Default off: the surface stays byte-identical\n * to the pre-#486 sidecar.\n */\n strictTemplates?: boolean\n /**\n * Build target threaded to the compiler binary (`--target`), same flag\n * `transform()` passes. Defaults to the binary's own default\n * (`universal`) when omitted.\n *\n * This is NOT cosmetic: `--target` changes what `compile_full_with_options`\n * produces (packages/compiler/src/bin/main.rs), which `sidecar_ts` is\n * derived from — e.g. a `target: 'client'` build elides server-only\n * artifacts. A caller that never passes this always type-checks against\n * the `universal` surface regardless of the project's actual configured\n * target, which can pass tsc on code the real build would elide or\n * reject. `islands`/`shadowMode` are deliberately NOT parameters here:\n * both are applied as JS-side post-processing on the RUNTIME JS output\n * (see `transform()`), never touch `sidecar_ts`, and have no bearing on\n * type-check accuracy.\n */\n target?: 'client' | 'server' | 'universal'\n },\n): string {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--sidecar-stdout']\n if (id) {\n args.push('--path', id)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n if (options?.target) {\n args.push('--target', options.target)\n }\n // Bounded — an unbounded spawn here is the hang that left two\n // `aihu-compile --stdin` children alive for 2.5 days. See spawn-bounds.ts.\n const bin = resolveBinPath()\n const startedAt = Date.now()\n try {\n return execFileSync(bin, args, {\n input: source,\n encoding: 'utf8',\n // Capture stderr rather than inheriting it: the compiler's warnings (unhyphenated\n // tag names, undeclared cross-block refs) belong to `aihu build`, and would\n // otherwise interleave with the type diagnostics a caller is trying to read.\n // A hard compile failure still throws, carrying the message with it.\n stdio: ['pipe', 'pipe', 'pipe'],\n ...compileSpawnBounds(source.length),\n })\n } catch (err) {\n throw describeSpawnFailure(err, bin, args, source.length, Date.now() - startedAt) ?? err\n }\n}\n\nexport function compileToAst(source: string, id?: string): SfcAst {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--ast-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): css-engine's `compileSfc`\n // re-derives the AST per file — with the memo, the second parse of an\n // unchanged file is a lookup, and a preceding `transform()` of the same\n // source has usually SEEDED this exact entry from its envelope (one parse\n // for js + ast + route). The cached value is the raw JSON string; parse per\n // call so callers can never mutate a shared AST object.\n const stamp = _backendStampPath()\n const json = _memoizedSpawn('ast', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['ast'],\n })\n if (reply.kind === 'legacy') return reply.output\n const ast = reply.envelope.astJson\n if (ast === undefined) {\n throw new Error('[@aihu/compiler] envelope reply missing astJson')\n }\n return ast\n })\n return JSON.parse(json) as SfcAst\n}\n\n/**\n * Structured `@route` metadata (the `.route.json` sidecar shape). All fields\n * optional — only what the SFC's `@route` block declares is present. `head` is\n * left opaque here (the router owns its shape).\n */\nexport interface RouteMeta {\n pattern?: string\n name?: string\n layout?: string\n middleware?: string[]\n ssr?: boolean\n params?: string[]\n head?: unknown\n /**\n * GX Phase 1 fan-out (#437-GX): the resolved `extract` policy — always\n * present in the binary's route-json output since 0.1.12 (the default is\n * recorded, never implied by absence). Typed loose here: consumers\n * normalize fail-closed (`deriveReadPolicy` in `@aihu/server`).\n */\n extract?: { read?: unknown; call?: unknown }\n /**\n * GX Phase 4 fan-out (#466): the `data:` governed-resource declaration\n * (70-governed-data-access §2.1) — present only when the route declares one\n * (0.1.14+). `type` keys the server registry's provider; `preview` lists\n * the locked-state fields (omitted when none declared). Consumers: the\n * server runtime's boot validation + generated loader, and the router Vite\n * layer's C486 sibling-loader conflict check (§4.7).\n */\n data?: { type?: string; preview?: string[] }\n}\n\n/**\n * Parse a `.aihu` source string and return its `@route` metadata, or `null`\n * when the SFC declares no `@route` block.\n *\n * Thin wrapper over the Rust binary (mirrors {@link compileToAst}): spawns\n * `aihu-compile --stdin --tag <stem> --route-json`, feeds `source` on stdin,\n * and `JSON.parse`s stdout. This is how build tools recover full route\n * metadata (`head`/`middleware`/`params`/`ssr`/`layout`) for the SPA build\n * path, where no `.route.json` sidecar is written to disk.\n *\n * Throws on parse failure (same error path as `transform()`/`compileToAst()`).\n */\nexport function compileRouteMeta(source: string, id?: string): RouteMeta | null {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--route-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): the router Vite plugin\n // calls this per route file per scan, and the SSG prerender's second server\n // triggers a full re-scan — and a preceding `transform()` of the same\n // source has usually SEEDED this entry from its envelope. Cache the raw\n // stdout; parse per call.\n const stamp = _backendStampPath()\n const out = _memoizedSpawn('route', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['route'],\n })\n if (reply.kind === 'legacy') return reply.output\n return reply.envelope.routeJson ?? 'null'\n }).trim()\n if (out === '' || out === 'null') return null\n return JSON.parse(out) as RouteMeta\n}\n\n/**\n * Inject `_setMount(mount)` + `_setSignal(signal)` auto-wiring into a compiled\n * `.aihu` module. Adds the necessary symbols to existing imports and inserts\n * the boot calls right after the last `import` statement.\n *\n * @internal\n */\nexport function _injectAutoWiring(code: string): string {\n // 1. Add `mount` to the @aihu/arbor import (or create it).\n let result: string\n if (code.includes(\"from '@aihu/arbor'\")) {\n result = code.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n } else {\n result = `import { mount } from '@aihu/arbor'\\n${code}`\n }\n\n // 2. Add `signal` to the non-type @aihu/signals import (or create it).\n // Note: `import\\s+\\{` does NOT match `import type {` (the regex needs `{` immediately\n // after whitespace, whereas `import type {` has `type` in between). No negation guard\n // is needed — the replace callback below already skips `import type` lines.\n if (/import\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result)) {\n // There IS a value import from signals — add `signal` if missing.\n result = result.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/signals'/,\n (_m: string, imports: string) => {\n // Skip type-only imports\n if (_m.startsWith('import type')) return _m\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('signal')) parts.push('signal')\n return `import { ${parts.join(', ')} } from '@aihu/signals'`\n },\n )\n } else if (!/^[^\\S\\n]*import\\b[^\\n]*from[^\\S\\n]*'@aihu\\/signals'/m.test(result)) {\n // No signals import at all — insert after arbor import\n result = result.replace(\n /import\\s*\\{[^{}]*\\}\\s*from\\s*'@aihu\\/arbor'/,\n (m: string) => `${m}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n // If only `import type { Signal }` exists, insert value import after it\n else if (\n /import\\s+type\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result) &&\n !result.match(/import\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals'/)\n ) {\n result = result.replace(\n /(import\\s+type\\s+\\{[^{}]*\\}\\s+from\\s+'@aihu\\/signals')/,\n (_m: string, typeImport: string) => `${typeImport}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n\n // 3. Add `_setMount`, `_setSignal` to the @aihu/runtime import.\n result = result.replace(\n /import\\s*\\{([^{}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_setMount')) parts.push('_setMount')\n if (!parts.includes('_setSignal')) parts.push('_setSignal')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // 4. Insert boot calls after the last `import` statement.\n const lines = result.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, '_setMount(mount)', '_setSignal(signal)', '')\n result = lines.join('\\n')\n }\n\n return result\n}\n\n/**\n * Vite plugin that compiles .aihu files to TypeScript during build and dev.\n *\n * Use `enforce: 'pre'` so the hook fires before Vite/Rollup's built-in\n * parsers attempt to process the raw .aihu content as JavaScript.\n *\n * @example\n * // vite.config.ts\n * import { aihuCompilerPlugin } from '@aihu/compiler'\n * export default { plugins: [aihuCompilerPlugin()] }\n *\n * **Known Limitation — Bun + Rollup4 ESM incompatibility (v0):**\n *\n * `bun vite build` fails in the `fixtures/vite-counter` fixture with two\n * cascading errors:\n *\n * 1. **Missing devDependency:** `vite` is declared only as an optional\n * `peerDependency` in `packages/compiler/package.json`. Bun does not\n * install optional peers automatically, so `bun vite build` exits\n * immediately with `Cannot find package 'vite'`.\n *\n * 2. **Bun + Rollup4 bridge:** Even with Vite installed, Bun processes\n * `vite.config.ts` through its own internal bundler before handing off\n * to Rollup4. When `@aihu/compiler` is resolved from the workspace\n * symlink (`dist/index.js`), Bun's ESM loader evaluates the module at\n * config-load time. The subprocess call inside `transform()` depends on\n * the Rust binary being at `../bin/aihu-compile` relative to `dist/`\n * (written by the postinstall hook). In a dev workspace where postinstall\n * has not run, this path does not exist and `execFileSync` throws. Bun surfaces\n * the error as a config-load failure, not a per-file transform error,\n * causing the entire build to abort before any `.aihu` file is\n * processed.\n *\n * **Workaround (v0):** Use `bun run integrate.ts` directly from\n * `packages/compiler/fixtures/vite-counter/`. This script calls\n * `transform()` from `@aihu/compiler` without involving Vite or Rollup.\n * Preconditions: (1) `cargo build --release` in `packages/compiler/`,\n * (2) `bun install` at the repo root.\n *\n * **v1 resolution:** Add `vite` as a `devDependency` in\n * `packages/compiler/package.json`; add a WASM or pre-built binary\n * strategy so the Rust binary is bundled with the npm package and does not\n * require a separate `cargo build --release` step.\n */\n/**\n * Minimal structural type for the `@aihu/css-engine` module surface this\n * plugin uses. Declared locally so the compiler never type-imports the\n * css-engine package (which would create a compile-time edge against an\n * optional peer that may be absent).\n *\n * @internal\n */\ninterface CssEngineModule {\n compileSfc(source: string, id?: string, lightScopeId?: string): string\n}\n\n// Memoised resolution of the optional `@aihu/css-engine` peer. `undefined`\n// = not yet attempted; `null` = attempted and unavailable (no-op path);\n// a module object = available. The dynamic import is attempted once per\n// process — repeated absence does not re-pay the resolution cost.\nlet _cssEngine: CssEngineModule | null | undefined\n\n// Whether we've already surfaced a one-shot warning that css-engine resolved\n// but `compileSfc` threw (typically: native css-core binary unresolvable in\n// the consumer's install — e.g. lockfile pins the per-platform placeholder\n// version). The transform stays non-fatal, but going fully silent leaves users\n// chasing \"why did my utility classes never emit?\". One warn per process.\nlet _cssEngineWarned = false\n\n// The optional-peer module specifier, held in a VARIABLE so TypeScript never\n// statically resolves `@aihu/css-engine`'s declarations at typecheck time.\n// css-engine depends on @aihu/compiler for its AST, so the two form a\n// circular package relationship; under CI's frozen install + moon build\n// ordering, css-engine's `dist`/`.d.ts` are not guaranteed to exist when\n// `compiler:typecheck` runs. A literal `import('@aihu/css-engine')` makes the\n// compiler emit TS2307 in that window (the `as` cast affects the RESULT type\n// only, not whether TS attempts module resolution). Resolving through this\n// variable keeps the import fully dynamic — no compile-time edge on the peer.\nconst _CSS_ENGINE_SPECIFIER = '@aihu/css-engine'\n\n/**\n * Resolve the optional CSS engine from the compiler package first, then from\n * the Vite consumer. Package managers commonly realpath a published plugin\n * into their store, where an optional peer is not a physical sibling even\n * though the application has installed it. The consumer fallback keeps the\n * integration opt-in while making published and workspace compiler builds\n * behave the same way.\n */\nasync function _loadCssEngine(): Promise<CssEngineModule | null> {\n try {\n return (await import(_CSS_ENGINE_SPECIFIER)) as unknown as CssEngineModule\n } catch {\n try {\n const fromConsumer = createRequire(join(process.cwd(), 'package.json'))\n const entry = fromConsumer.resolve(_CSS_ENGINE_SPECIFIER)\n return (await import(pathToFileURL(entry).href)) as unknown as CssEngineModule\n } catch {\n return null\n }\n }\n}\n\n/**\n * Lazily resolve `@aihu/css-engine` and compile a `.aihu` source's utility\n * classes to scoped CSS. Returns `''` when css-engine is not installed\n * (the optional-peer no-op path) or when compilation fails for any reason —\n * a CSS-engine failure MUST NOT break an otherwise-valid `.aihu` build.\n *\n * Sets `process.env.AIHU_COMPILE_BIN` to this plugin's resolved compiler\n * binary before calling `compileSfc`: css-engine re-derives the SFC AST via\n * its own bundled copy of `compileToAst`, whose binary path is resolved\n * relative to the css-engine package — which does NOT ship the compiler\n * binary. Pointing it at our `binPath` guarantees the AST css-engine parses\n * is produced by the exact same compiler this build uses.\n *\n * @internal\n */\nasync function _maybeCompileUtilityCss(\n source: string,\n id: string,\n lightScopeId?: string,\n): Promise<string> {\n if (_cssEngine === null) return ''\n // Ensure css-engine's bundled `compileToAst` spawns the SAME compiler\n // binary this plugin uses (it has no compiler binary of its own). Set\n // this BEFORE the dynamic import so that any module-load-time evaluation\n // of `process.env.AIHU_COMPILE_BIN` in css-engine's bundled dist (older\n // bundles capture this into a module-scope const at line 8 of\n // `packages/css-engine/dist/index.js`) sees the correct value. After Bug 6,\n // the source `compileToAst` resolves the bin lazily on each call, so once\n // css-engine is rebuilt this set-before-import is belt-and-braces.\n if (process.env.AIHU_COMPILE_BIN == null) {\n try {\n process.env.AIHU_COMPILE_BIN = resolveBinPath()\n } catch {\n // No CLI binary resolvable (e.g. an addon-only install). css-engine's\n // NEWER bundles route compileToAst through the same native backend and\n // never read this var; older bundles will surface their own resolver\n // error, which the compileSfc try/catch below already treats as the\n // non-fatal no-op path.\n }\n }\n if (_cssEngine === undefined) {\n // Guarded, lazy, OPTIONAL — resolving through `_CSS_ENGINE_SPECIFIER`\n // keeps TypeScript from taking a compile-time dependency on css-engine.\n _cssEngine = await _loadCssEngine()\n if (_cssEngine === null) return ''\n }\n try {\n return _cssEngine.compileSfc(source, id, lightScopeId)\n } catch (err) {\n // A css-engine compile failure is non-fatal: fall back to the no-op\n // path (utility classes don't emit) rather than aborting the build.\n // BUT — silently swallowing this means a user who clearly intends\n // css-engine to be active (the peer resolved) will never know their\n // utility classes are inert. Surface a one-shot warning with the\n // underlying error + an install/upgrade hint. Idempotent per process.\n if (!_cssEngineWarned) {\n _cssEngineWarned = true\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(\n `[@aihu/compiler] @aihu/css-engine is installed but compileSfc() failed; ` +\n `utility classes will not emit. Original error: ${msg}\\n` +\n `Hint: ensure the native css-core binary is installed ` +\n `(install/upgrade @aihu/css-engine + its per-platform optional dep, ` +\n `or run \\`cargo build --release -p aihu-css-core\\` in a dev clone).`,\n )\n }\n return ''\n }\n}\n\n/**\n * Resolve the stylesheet source for one SFC. An explicit provider is an\n * opt-in replacement for the legacy css-engine integration; keeping the\n * fallback in this single dispatcher ensures an alternate provider never\n * needs to import `@aihu/css-engine` or pay for its optional dependency.\n */\nasync function _resolveCssStyles(\n provider: AihuCssProvider | undefined,\n context: AihuCssProviderContext,\n): Promise<string> {\n if (provider !== undefined) {\n return (await provider(context)) ?? ''\n }\n return _maybeCompileUtilityCss(context.source, context.id, context.lightScopeId)\n}\n\nexport function aihuCompilerPlugin(options?: AihuCompilerPluginOptions): VitePlugin {\n const islandsEnabled = options?.islands !== false\n const shadowMode = options?.shadowMode\n const cssProvider = options?.cssProvider\n const target = options?.target\n const layoutsDir = options?.layoutsDir ?? 'src/layouts'\n\n // Bug 6 — per-instance store of virtual utility-CSS modules. Keyed by the\n // full virtual id (NUL-prefixed). Populated by the transform hook when\n // `shadowMode === 'light'` produces utility CSS; drained by the `load` hook\n // when Vite's CSS pipeline asks for the module body. Lives on the plugin\n // instance so multiple `aihuCompilerPlugin()` calls in the same build don't\n // alias each other's css.\n const utilityCssStore = new Map<string, string>()\n\n // GX Phase 1 (#437-GX) — per-instance extract census. Keyed by rawId,\n // populated in transform from the compiler's `// @aihu:extract` marker;\n // printed per-value in buildEnd. Every build prints the posture\n // distribution (the DA-e census pattern from #437).\n const extractCensus = new Map<string, { read: string; call: string }>()\n\n return {\n name: 'aihu-compiler',\n enforce: 'pre',\n buildEnd() {\n for (const line of _formatExtractCensus(extractCensus)) console.info(line)\n },\n resolveId(source) {\n // Own all `\\0virtual:aihu-utility/<hash>.css` ids so Vite's resolver\n // doesn't try to find them on disk. Returning the id verbatim is the\n // Rollup convention for \"I'll handle the load.\"\n if (source.startsWith(VIRTUAL_UTILITY_PREFIX)) return source\n return null\n },\n load(id) {\n if (!id.startsWith(VIRTUAL_UTILITY_PREFIX)) return null\n // Vite's CSS pipeline runs on the returned source because the id ends\n // in `.css` — it parses, minifies (in build), and hoists into a CSS\n // asset chunk that lands in `dist/assets/<name>-<hash>.css`.\n return utilityCssStore.get(id) ?? null\n },\n transform(code, id) {\n // Strip Vite query strings (e.g. `?import`, `?t=...`) before checking the extension.\n const rawId = id.split('?')[0]!\n if (!rawId.endsWith('.aihu')) return\n // Server-environment detection (Vite Environment API). A `.aihu` loaded\n // through an SSR module runner — the `output: 'static'` prerender's\n // `ssrLoadModule` (packages/app/src/prerender.ts), or any `vite dev` SSR\n // consumer — must NOT get the client/universal target: that emits a\n // module-level `new CSSStyleSheet()` and an unguarded `customElements`\n // registration, both of which throw in the DOM-less SSR runner\n // (`CSSStyleSheet is not defined`) and silently degrade the prerender to\n // an empty SPA shell. The `server` target guards DOM registration behind\n // `typeof customElements` and exports `__ssr` (a host-less arbor factory)\n // plus the `__aihu_ssr_string__` compiled string fast path that\n // @aihu/server's `renderToString` prefers — a pure string concatenation\n // that needs no DOM. An explicit `target` option still wins (a caller\n // that pins a target owns the consequences); we only fill the SSR default\n // when none was configured. `this.environment` is absent on pre-Environment\n // -API Vite, in which case the prior universal default stands.\n const isServerEnv =\n (this as { environment?: { config?: { consumer?: string } } })?.environment?.config\n ?.consumer === 'server'\n const effectiveTarget = target ?? (isServerEnv ? 'server' : undefined)\n return (async () => {\n // No `.aihu.ts` sidecar is written any more. Type-checking goes through\n // `aihu-tsc`, which projects each `.aihu` into the TypeScript program as a\n // VIRTUAL file — so the type-check surface never lands on disk beside the\n // source, where authors saw it, editors indexed it, and `.gitignore` had to\n // hide it. A build has no business writing type-checker inputs at all.\n //\n // Layout SFCs (under the layouts dir) compile in layout mode: a\n // namespaced `aihu-layout-<stem>` tag + a passive <outlet> marker.\n const isLayout = _isLayoutFile(rawId, layoutsDir)\n const layoutTag = isLayout ? _layoutTag(basename(rawId, '.aihu')) : undefined\n const tOpts = {\n ...(effectiveTarget ? { target: effectiveTarget } : {}),\n ...(layoutTag ? { tag: layoutTag } : {}),\n }\n const result = transform(code, rawId, tOpts)\n // GX Phase 1 (#437-GX) — record this surface's resolved extract policy\n // for the build census (client-target builds carry no marker: policy\n // never reaches client artifacts).\n const extractMarker = _parseExtractMarker(result.code)\n if (extractMarker) extractCensus.set(rawId, extractMarker)\n // §9.4 per-file shadow override: the Rust `$shadow` macro emits a leading\n // `// @aihu:shadow <mode>` marker; it wins over the plugin's global\n // shadowMode and drives BOTH _injectShadowMode and the css fold branch.\n const perFileShadow = /^\\/\\/ @aihu:shadow (light|shadow)\\b/m.exec(result.code)?.[1] as\n | 'light'\n | 'shadow'\n | undefined\n // DA4 (#437, the ratified flip) — the IMPLICIT page default: for an\n // `@route` unit with no `$shadow` pin the compiler emits the DISTINCT\n // default-marker token `// @aihu:shadow-default light`. Layout SFCs\n // (no `@route` block, so no compiler marker) get the same 'light'\n // default from `_isLayoutFile`. Precedence, ratified: `$shadow` pin >\n // plugin-global `shadowMode` config > page/layout default 'light' >\n // leaf default 'shadow' (the runtime's `?? 'shadow'` when nothing is\n // injected) — so an explicit plugin-global config still outranks the\n // implicit default, which is why this is not the pin marker.\n const perFileShadowDefault = /^\\/\\/ @aihu:shadow-default (light|shadow)\\b/m.exec(\n result.code,\n )?.[1] as 'light' | 'shadow' | undefined\n const impliedShadowDefault = perFileShadowDefault ?? (isLayout ? 'light' : undefined)\n const effectiveShadow = perFileShadow ?? shadowMode ?? impliedShadowDefault\n // The runtime's default is shadow DOM when no marker or plugin option\n // is present. Providers receive this resolved value so they never have\n // to duplicate the compiler's precedence rules.\n const resolvedShadowMode = effectiveShadow ?? 'shadow'\n\n // Light-DOM leaf flip prep (LDF §10 step 1/3): a deterministic scope\n // id for this component's `data-a` attribute, only when it actually\n // resolved to light mode. `undefined` in the shadow case — mirrors\n // `SfcAst.light_scope_id: Option<String>` being `None` on the Rust\n // side. Computed BEFORE the shadowMode injection below so it can\n // ride in the SAME injected options object (`_injectShadowMode`'s\n // doc comment explains why one merged injection, not two).\n const lightScopeId = effectiveShadow === 'light' ? _lightScopeId(rawId) : undefined\n\n let compiled =\n effectiveShadow != null\n ? _injectShadowMode(result.code, effectiveShadow, lightScopeId)\n : result.code\n // LDF §10 step 3, server side: fill the Rust codegen's\n // `__AIHU_LIGHT_SCOPE_ID__` placeholder so the compiled `__ssrString`\n // stamps `data-a` on its own root by default (no-op when the target\n // carries no string renderer).\n if (lightScopeId) compiled = _injectLightScopeId(compiled, lightScopeId)\n // Light-DOM: the authored `@style` block compiled to a per-instance\n // `host.adoptedStyleSheets` assignment, but a light-DOM host has no\n // shadow root so that setter is a no-op. Redirect the module-level\n // sheet to `document.adoptedStyleSheets` (idempotent) so authored recipe\n // CSS reaches the global cascade alongside the css-engine utility CSS.\n if (effectiveShadow === 'light') compiled = _globalizeAuthoredStyle(compiled)\n if (isLayout) compiled = _passivizeOutlet(compiled)\n\n // ── CSS provider hook (explicit provider or legacy fallback) ───────\n // An explicit provider is called directly and never resolves the\n // optional @aihu/css-engine peer. With no provider, the guarded lazy\n // css-engine integration remains the compatibility path.\n const utilityCss = await _resolveCssStyles(cssProvider, {\n source: code,\n id: rawId,\n shadowMode: resolvedShadowMode,\n target: effectiveTarget ?? 'universal',\n ...(lightScopeId ? { lightScopeId } : {}),\n })\n if (utilityCss) {\n if (effectiveShadow === 'light') {\n // Bug 6 — no shadow root → `host.adoptedStyleSheets` is a no-op.\n // Route utility CSS through Vite's CSS pipeline via a virtual\n // `.css` import so it lands in `dist/assets/*.css` and reaches the\n // global cascade. The authored `@style` block (if any) still\n // emits via the Rust codegen's normal path and is unaffected.\n const folded = _foldCssStylesGlobal(compiled, utilityCss, rawId)\n if (folded) {\n utilityCssStore.set(folded.virtualId, utilityCss)\n compiled = folded.code\n }\n } else {\n // `shadowMode: 'shadow'`: fold into the\n // per-component `CSSStyleSheet` adopted by the shadow root.\n compiled = _foldCssStyles(compiled, utilityCss)\n // …and into the SERVER target's `__aihu_css__`, which carries the\n // same rules into the declarative shadow template. A shadow root is\n // style-isolated, so anything missing here paints unstyled until\n // the component's chunk loads.\n if (isServerEnv) compiled = _foldSsrCssExport(compiled, utilityCss)\n }\n }\n\n const elementTag = _extractElementTag(compiled)\n\n let out: string\n\n // §9.4 — a base-extending recipe (`defineComponent({ base: X, ... })`)\n // MUST take the full defineComponent/defineElement path: the static\n // island shim inlines `class extends HTMLElement` and cannot honor a\n // base class. Force-classify it interactive regardless of signal usage.\n const hasBase = _hasBaseRecipe(compiled)\n\n // Plan 3.3 — static-island fast path. Bypasses HMR injection because\n // a component with no signals has no setup state to hot-replace.\n // Static islands strip @aihu/runtime entirely — do NOT inject auto-wiring\n // (it would reference _setMount/_setSignal as undefined identifiers).\n //\n // DA4 (#437): like `hasBase`, a light-DOM component cannot take the\n // static-island shim — the shim inlines `attachShadow({ mode: 'open' })`\n // and cannot honor `shadowMode: 'light'` (and its tail rewrite does not\n // match the injected options argument). Keep the full runtime path so\n // the injected `{ shadowMode: 'light' }` reaches defineElement.\n if (isServerEnv) {\n // Server module-runner target (the SSG prerender's `ssrLoadModule`,\n // any dev-SSR consumer). The `server` compile target already exports\n // the host-less `__ssr` factory + the `__aihu_ssr_string__` string\n // fast path and guards its own custom-element registration behind\n // `typeof customElements`. The client-only instrumentation below is\n // wrong here: HMR (`_buildHmrCode`) prepends a `let __aihu_setup__`\n // slot that COLLIDES with the server target's named\n // `const __aihu_setup__` (duplicate declaration); the static-island\n // shim, `defer` hydration, and auto-wiring all inject browser mount\n // code the SSR render never runs. Emit the server target as-is; the\n // TS-strip below still applies.\n out = compiled\n // LDF §10 step 3, server side: expose the compiler-assigned light-DOM\n // scope id so an SSR/SSG caller (e.g. `@aihu/app`'s prerender) can\n // pass it to `renderToString` as `SsrOptions.lightScopeId` and stamp\n // `data-a` on the prerendered root. This is the SAME id the client\n // transform injects into `defineElement` options and the css fold\n // wrote into the emitted `@scope([data-a=\"…\"])` blocks — exporting it\n // from the compiled module keeps a single source of truth (no\n // consumer ever re-derives the hash). Server target only: the client\n // runtime stamps at `connectedCallback` and needs no export.\n if (lightScopeId) {\n out += `\\nexport const __aihu_light_scope__ = '${lightScopeId}'\\n`\n }\n // The component's registered custom-element tag, exported for the\n // same reason and on the same channel as the scope id above.\n //\n // SSR renders a component's TEMPLATE, not the component: the output\n // is the template root (`<div class=\"dn-docs\">`), while the client\n // builds `document.createElement('aihu-layout-docs')` and puts the\n // template inside it. The two shapes therefore never match, so the\n // client cannot adopt the prerendered subtree and replaces it\n // wholesale — measured on apps/docs: 0 of 391 prerendered nodes\n // survive hydration.\n //\n // Exporting the tag lets an SSG/SSR caller wrap the render in the\n // real host element (`SsrOptions.wrapTag`). It also puts `data-a`\n // where the client puts it: `define-element.ts` stamps the HOST in\n // its constructor, and its comment already asserts \"a server-rendered\n // element already carries `data-a`\" — which only becomes true once\n // the host exists in server output.\n if (elementTag !== null) {\n out += `\\nexport const __aihu_tag__ = '${elementTag}'\\n`\n }\n // The component's RESOLVED shadow mode, on the same channel and for\n // the same single-source reason as the two exports above.\n //\n // An SSR caller rendering a nested custom element has to emit two\n // different shapes: a light-DOM component's tree is the host's own\n // children, while a shadow component's tree belongs inside a\n // `<template shadowrootmode=\"open\">` so the browser attaches a\n // declarative shadow root while parsing. Getting that wrong is not a\n // cosmetic error — light children under a host that later calls\n // `attachShadow` are discarded on upgrade (\"adopt or discard, never\n // slot-project\", define-component.ts), so the content would paint and\n // then vanish.\n //\n // `effectiveShadow` is the value that ALREADY drives both\n // `_injectShadowMode` and the css-fold branch, so exporting it keeps\n // one resolution (plugin config > per-file directive > page/layout\n // default) rather than letting a consumer re-derive from the presence\n // of `__aihu_light_scope__` — which is an inference, not a signal.\n //\n // Deliberately aihu's OWN vocabulary ('light' | 'shadow'), never the\n // DOM's ShadowRootMode ('open' | 'closed'). Those are different enums\n // that share the word \"mode\"; the translation to `shadowrootmode`\n // happens once, at serialization, in the renderer.\n // Guarded on non-null for the same reason `_injectShadowMode` is\n // (`effectiveShadow != null`, below): when no mode resolves there is\n // nothing to assert, and emitting the string 'undefined' would be a\n // lie a consumer would branch on.\n if (effectiveShadow != null) {\n out += `\\nexport const __aihu_shadow__ = '${effectiveShadow}'\\n`\n }\n // Every component tag this module's template references, on the same\n // channel as the three exports above. `buildChildRegistry`\n // (`@aihu/server`) reads it as the edge set for its cycle check over\n // the WHOLE discovered component graph — not a per-page transitive\n // walk; the caller indexes every discovered module once rather than\n // loading a subset by following these tags (see child-registry.ts's\n // module docblock for why). A cycle found there is reported, not\n // rejected: `__aihu_schild` bounds it with a depth cap and an output\n // budget, so a build no longer has to refuse a legal recursive shape\n // to stay safe.\n //\n // DERIVED FROM THE EMITTED CALLS, not re-computed from the template.\n // The set that matters is precisely the set of tags the compiled\n // renderer will look up at runtime, and reading the `__aihu_schild`\n // call sites IS that set. Deriving it a second way — walking the\n // template again and reapplying the emitter's v1 boundaries (no\n // attrs, no children, static non-root path) — would be one rule\n // written in two places, and the halves would drift the first time a\n // boundary moved. The parity test in\n // `packages/compiler/tests/light-scope-export.test.ts` (the\n // `__aihu_child_tags__ export` describe block) pins that the export\n // and the call sites agree.\n //\n // Omitted entirely when the template references no component, so a\n // consumer can treat \"no export\" and \"empty\" identically.\n //\n // This set answers ONE question — \"what will the compiled renderer\n // look up?\" — and `__aihu_referenced_tags__` below answers a different\n // one. The paragraph above argues against deriving THIS set a second\n // way; it is not an argument against a second, differently-defined\n // set, and the two must not be collapsed. See below.\n // ONE shared derivation — `_deriveChildTags`, which `@aihu/router`'s\n // `genSC` calls on the same channel to build the server-bundle\n // component registry at codegen time. See its docblock.\n const childTags = _deriveChildTags(out)\n if (childTags.length > 0) {\n out += `\\nexport const __aihu_child_tags__ = ${JSON.stringify(childTags)}\\n`\n }\n // §22 — every component tag this module's template REFERENCES, which\n // is a strictly larger set than `__aihu_child_tags__` above and exists\n // for a different consumer.\n //\n // `__aihu_child_tags__` is the runtime edge set: the tags the compiled\n // renderer will actually look up, which is why deriving it from the\n // emitted `__aihu_schild(` call sites is not just convenient but\n // CORRECT for its consumer (`buildChildRegistry`'s cycle check).\n //\n // But `@aihu/app`'s prerender reads a tag set for two DIAGNOSTICS —\n // \"is this broken component referenced by anything?\" and \"is this tag\n // resolvable?\" — and for those questions the call-site set is the\n // wrong one. A reference the emitter DECLINES under the v1 child\n // boundaries (an attribute, children, a root/dynamic path) produces no\n // call site and therefore no tag, so the component is judged\n // unreferenced and the diagnostic stays silent about a component that\n // genuinely cannot load. Observed: `apps/docs`'s `pages/index.aihu`\n // references `<weather-demo city=\"London\">`, the attribute makes the\n // emitter decline it, the page compiles to ZERO `__aihu_schild` call\n // sites — and `weather-demo.aihu` really does fail to load under SSR\n // (`new CSSStyleSheet()` at module top level) with the build saying\n // nothing.\n //\n // So: two exports, two meanings, two derivations — NOT one rule\n // written twice. This one comes from the template AST, via the\n // `// @aihu:component-tags` marker `collect_component_tags` emits (the\n // same walk that fills `route.json`'s components array), so it is\n // independent of where the emitter's boundaries happen to sit and does\n // not move when they do.\n //\n // Parsed from `compiled`, NOT `out`: the marker is a comment the Rust\n // codegen emits, and the TS-strip at the end of this hook is free to\n // drop comments. `_parseIslandMarker(compiled)` reads the same channel\n // the same way for the same reason.\n //\n // Omitted entirely when the marker is absent or empty, so \"no export\"\n // and \"empty\" mean the same thing — the rule `__aihu_child_tags__`\n // already follows.\n const referencedTags = _parseComponentTagsMarker(compiled)\n if (referencedTags.length > 0) {\n out += `\\nexport const __aihu_referenced_tags__ = ${JSON.stringify(referencedTags)}\\n`\n }\n } else if (\n islandsEnabled &&\n elementTag !== null &&\n !hasBase &&\n effectiveShadow !== 'light' &&\n _parseIslandMarker(compiled) === 'static'\n ) {\n out = _buildStaticIsland(compiled, elementTag)\n } else if (elementTag !== null) {\n // Inject HMR instrumentation. The injected block is gated on\n // `typeof __DEV__ !== 'undefined' && __DEV__` so production\n // bundlers dead-code-eliminate it when they set __DEV__ = false.\n out = _buildHmrCode(compiled, elementTag)\n // Plan 3.3 — interactive islands also gain `defer` attribute\n // support so individual instances can opt into lazy hydration.\n out = _buildDeferredHydration(out, elementTag)\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n } else {\n out = compiled\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n }\n\n // The Rust compiler emits TypeScript (type casts, import type, etc.) and\n // the injected HMR / defer helpers also contain TS generics and casts.\n // Vite does NOT re-run its TS-strip step when a plugin returns code for a\n // non-.ts ID, so we must strip types ourselves before returning.\n //\n // TWO steps with SEPARATE failure handling, and the split is the whole\n // point. `import('vite')` is the ONLY one whose failure is legitimate —\n // a standalone `transform()` caller, a unit test, any host that is not a\n // Vite build has no Vite to strip with, and handing the TypeScript back\n // untouched is the correct answer there. Everything AFTER that import\n // runs with Vite proven present, so a failure there means the STRIP\n // broke, and swallowing it returns un-stripped TypeScript that\n // resurfaces hundreds of lines later as an unrelated bundler\n // `PARSE_ERROR` naming the user's `.aihu` file. One `catch` around both\n // did exactly that. `_isViteMissing` is how the two are told apart;\n // `_stripTypes` owns the branch order and the loud failures.\n let vite: typeof import('vite')\n try {\n vite = await import('vite')\n } catch (err) {\n if (_isViteMissing(err)) return { code: out, map: null }\n throw new Error(\n `[@aihu/compiler] Could not load \\`vite\\` to strip TypeScript from ${rawId}. ` +\n 'Vite appears to be installed but failed to load, so this is NOT the ' +\n '\"running outside Vite\" case and the TypeScript must not be handed ' +\n `back un-stripped. Underlying error: ${_errMessage(err)}`,\n { cause: err },\n )\n }\n // Vite's public return type has changed across supported releases, but\n // this boundary only reads `code` after the runtime capability checks\n // in `_stripTypes`. Keep the version-specific module type out of the\n // compiler's stable seam.\n return await _stripTypes(vite as unknown as ViteStripApi, out, rawId, isServerEnv)\n })()\n },\n }\n}\n"],"mappings":"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"}
|