@orkestrel/router 0.0.1 → 0.0.3
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 +33 -4
- package/dist/src/browser/index.d.ts +283 -4
- package/dist/src/browser/index.js +4 -300
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +893 -0
- package/dist/src/core/index.cjs.map +1 -0
- package/dist/src/core/index.d.cts +1067 -0
- package/dist/src/core/index.d.ts +1067 -8
- package/dist/src/core/index.js +33 -175
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +4 -210
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/{helpers.d.ts → index.d.cts} +166 -127
- package/dist/src/server/index.d.ts +166 -2
- package/dist/src/server/index.js +207 -0
- package/dist/src/server/index.js.map +1 -0
- package/package.json +38 -25
- package/dist/src/browser/Navigator.d.ts +0 -62
- package/dist/src/browser/factories.d.ts +0 -33
- package/dist/src/browser/helpers.d.ts +0 -76
- package/dist/src/browser/types.d.ts +0 -101
- package/dist/src/core/DispatchGroup.d.ts +0 -32
- package/dist/src/core/Dispatcher.d.ts +0 -51
- package/dist/src/core/Group.d.ts +0 -30
- package/dist/src/core/Router.d.ts +0 -42
- package/dist/src/core/constants.d.ts +0 -60
- package/dist/src/core/factories.d.ts +0 -53
- package/dist/src/core/helpers.d.ts +0 -274
- package/dist/src/core/types.d.ts +0 -445
- package/dist/src/server/types.d.ts +0 -31
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["isFunction","#error","#wire","#destroyed","#listeners","#wrappers","#surface","#parent","#entries","#compiled","#sensitive","#key","#index","#register","#parent","#emitter","#unmatched","#unmethoded","#register","#allow","#respondMatched","#respondAutoOptions"],"sources":["../../../src/core/constants.ts","../../../src/core/helpers.ts","../../../node_modules/@orkestrel/emitter/dist/src/core/index.js","../../../node_modules/@orkestrel/contract/dist/src/core/index.js","../../../src/core/Group.ts","../../../src/core/Router.ts","../../../src/core/DispatchGroup.ts","../../../src/core/Dispatcher.ts","../../../src/core/factories.ts"],"sourcesContent":["// ============================================================================\n// Core constants — the §5 centralized home for module-scope data used by the\n// matching engine and the fetch dispatcher. Every declaration here is frozen\n// and `export`ed per AGENTS §5.\n// ============================================================================\n\n/**\n * The complete set of HTTP methods a {@link import('./types.js').Dispatcher}\n * registers routes under — backs the registration guard (`add` rejects any\n * `method` outside this set) and the auto-`OPTIONS` `Allow` derivation.\n *\n * @remarks\n * A `ReadonlySet` of the seven {@link import('./types.js').Method} literals:\n * `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`. `HEAD` is\n * included even though it is never required at registration (a `GET` route\n * auto-answers `HEAD`) — it is still a valid method to register explicitly.\n *\n * @example\n * ```ts\n * METHODS.has('GET') // true\n * METHODS.has('TRACE') // false\n * ```\n */\nexport const METHODS: ReadonlySet<string> = Object.freeze(\n\tnew Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']),\n)\n\n/**\n * Specificity tier for a **literal** path segment (`/users`) — the highest\n * tier, always outranking a param or wildcard segment at the same position.\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`) when ranking candidate\n * matches left-to-right at the earliest differing segment (§4 precedence).\n *\n * @example\n * ```ts\n * TIER_LITERAL > TIER_PARAM // true\n * ```\n */\nexport const TIER_LITERAL = 2\n\n/**\n * Specificity tier for a **param** path segment (`:name`) — ranks below a\n * literal segment and above a wildcard segment at the same position.\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`) alongside {@link TIER_LITERAL}\n * and {@link TIER_WILDCARD}.\n *\n * @example\n * ```ts\n * TIER_PARAM > TIER_WILDCARD // true\n * ```\n */\nexport const TIER_PARAM = 1\n\n/**\n * Specificity tier for a **wildcard** path segment (`*name`) — the lowest\n * tier; a wildcard only ever wins against another wildcard shape (an\n * equal-specificity tie resolved by registration order).\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`).\n *\n * @example\n * ```ts\n * TIER_WILDCARD // 0\n * ```\n */\nexport const TIER_WILDCARD = 0\n","import type { CompiledPath, Method } from './types.js'\nimport { TIER_LITERAL, TIER_PARAM, TIER_WILDCARD } from './constants.js'\n\n// The PURE path-matching primitives (AGENTS §4.3 multi-word names — module scope,\n// no entity context). Every one is exported (the centralized-file rule, §5): a\n// consumer composes them directly, or reaches them through the `Router` engine.\n// They speak ONLY `string` / `RegExp` / `Record` and `decodeURIComponent` (a\n// platform global valid in Node and the browser alike) — NO DOM, NO `node:*` — so\n// the SAME engine drives a method-dimensioned server dispatcher and a method-less\n// browser navigator.\n\n/**\n * Escape every regex metacharacter in a literal string so it can be embedded\n * inside a larger `RegExp` source without being interpreted as syntax.\n *\n * @remarks\n * {@link compilePath} escapes the literal segments of a route pattern with this\n * before splicing in `:name` / `*name` capture groups, so a path like\n * `/files/:name.json` matches the `.` literally rather than as \"any character\".\n * Pure and total — never throws.\n *\n * @param value - The literal string to escape\n * @returns `value` with every regex metacharacter backslash-escaped\n *\n * @example\n * ```ts\n * escapeRegExp('a.b+c') // 'a\\\\.b\\\\+c'\n * new RegExp(`^${escapeRegExp('a.b')}$`).test('a.b') // true\n * new RegExp(`^${escapeRegExp('a.b')}$`).test('axb') // false\n * ```\n */\nexport function escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Canonicalize a route path for REGISTRY IDENTITY — strip a single trailing\n * slash, except the root `/` (and the empty pattern). The trailing-slash fold\n * {@link compilePath} normalizes a pattern through, so identity agrees with the\n * matcher.\n *\n * @remarks\n * Mirrors {@link compilePath}'s trailing-slash folding: `/users/` canonicalizes\n * to `/users` (the two compile to the same regex and match the same\n * pathnames), while the root `/` and the empty `''` are EXEMPT (a bare `/`\n * already matches `/`; stripping it would break that). Pure and total — a path\n * without a trailing slash returns unchanged.\n *\n * @param path - The route path pattern\n * @returns The canonical path (one trailing slash removed, except `/` and `''`)\n *\n * @example\n * ```ts\n * canonicalizePath('/users/') // '/users'\n * canonicalizePath('/users') // '/users'\n * canonicalizePath('/') // '/'\n * canonicalizePath('') // ''\n * ```\n */\nexport function canonicalizePath(path: string): string {\n\treturn path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path\n}\n\n/**\n * Compile a route path pattern into an anchored regex and its ordered param\n * names.\n *\n * @remarks\n * Splits the CANONICALIZED path into segments. Each `:name` segment becomes a\n * `([^/]+)` capture group; the FINAL segment may instead be `*name`, which\n * becomes a `(.+)` capture spanning the REST of the path including slashes — a\n * wildcard segment anywhere but last is a registration-time programmer error\n * and throws `TypeError` (§14 construction/registration boundary). Every regex\n * metacharacter in a literal segment is escaped first ({@link escapeRegExp}),\n * so a path like `/files/:name.json` matches the `.` literally apart from the\n * param. The regex is anchored (`^…$`), so it matches the whole pathname, not\n * a prefix.\n *\n * **Trailing slash is INSENSITIVE** (Express's `strict: false` default): a\n * single trailing slash on the request path is OPTIONAL, so `/users` matches\n * both `/users` and `/users/`, and `/users/:id` matches both `/users/me` and\n * `/users/me/`. This is NOT prefix matching — a deeper path is still a\n * distinct segment, so `/api` does not match `/api/users`. The ROOT `/` (and\n * the empty pattern `''`) are EXEMPT — they are not stripped, so `/` stays\n * `^/$` and `''` stays `^$`.\n *\n * `sensitive` (default `true`) controls case folding: `false` adds the `i`\n * regex flag, so `/Users` matches `/users`. The pattern's own casing is never\n * altered — only the matching behavior.\n *\n * @param path - The route path pattern (e.g. `/users/:id`, `/files/*rest`)\n * @param sensitive - Case-sensitive matching (default `true`)\n * @returns The {@link CompiledPath} — its `regex` + ordered `params`\n * @throws {TypeError} When a `*name` wildcard segment is not the FINAL segment\n *\n * @example\n * ```ts\n * const { regex, params } = compilePath('/users/:id/posts/:slug')\n * params // ['id', 'slug']\n * regex.exec('/users/7/posts/hello') // ['…', '7', 'hello']\n * regex.test('/users/7/posts/hello/') // true — the trailing slash is optional\n *\n * compilePath('/files/*rest').regex.test('/files/a/b.png') // true\n * compilePath('/Users', false).regex.test('/users') // true — case-insensitive\n * ```\n */\nexport function compilePath(path: string, sensitive = true): CompiledPath {\n\tconst params: string[] = []\n\t// Normalize ONE trailing slash off the pattern so `/users/` compiles like `/users`\n\t// — except the root `/` (and the empty pattern), which must keep matching `/` / `''`.\n\tconst normalized = canonicalizePath(path)\n\tconst segments = normalized.split('/')\n\tconst compiledSegments = segments.map((segment, index) => {\n\t\tconst isFinal = index === segments.length - 1\n\t\t// A wildcard-shaped segment head (`*name`) is only valid as the FINAL segment — a\n\t\t// registration-time programmer error anywhere else (§14 boundary guard).\n\t\tif (!isFinal && /^\\*[A-Za-z_]\\w*/.test(segment))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a wildcard segment (\"${segment}\") must be the final segment of a path pattern, got \"${path}\"`,\n\t\t\t)\n\t\t// Classification and compilation share ONE segment parser (§4 fix) — the tier\n\t\t// {@link classifySegment} assigns is exactly the shape compiled here.\n\t\tconst tier = classifySegment(segment, isFinal)\n\t\tif (tier === TIER_WILDCARD) {\n\t\t\tparams.push(segment.slice(1))\n\t\t\treturn '(.+)'\n\t\t}\n\t\tif (tier === TIER_PARAM) {\n\t\t\tconst match = /^:([A-Za-z_]\\w*)/.exec(segment)\n\t\t\tconst name = match?.[1] ?? ''\n\t\t\tparams.push(name)\n\t\t\treturn `([^/]+)${escapeRegExp(segment.slice(1 + name.length))}`\n\t\t}\n\t\treturn escapeRegExp(segment)\n\t})\n\tconst pattern = compiledSegments.join('/')\n\t// Allow ONE optional trailing slash before the `$` anchor (Express `strict: false`),\n\t// EXCEPT for the root `/` and the empty pattern — those anchor exactly so they keep\n\t// matching only `/` / `''` (a bare `/?` there would let `''` match `/` and vice versa).\n\tconst suffix = normalized === '/' || normalized === '' ? '' : '/?'\n\tconst flags = sensitive ? '' : 'i'\n\treturn { regex: new RegExp(`^${pattern}${suffix}$`, flags), params }\n}\n\n/**\n * URL-decode one captured param value, tolerating a malformed percent-escape —\n * the decode {@link matchPath} applies to each captured group.\n *\n * @remarks\n * A bad `%` sequence is not a reason to reject an otherwise-matching route, so\n * a `decodeURIComponent` that would throw falls back to the raw value\n * (mirroring the cookie / token boundary readers, AGENTS §14). Total — never\n * throws.\n *\n * @param value - The raw captured param value\n * @returns The URL-decoded value, or the raw value when decoding would throw\n *\n * @example\n * ```ts\n * decodeParam('a%2Fb') // 'a/b'\n * decodeParam('100%25') // '100%'\n * decodeParam('%') // '%' — malformed escape stays literal\n * ```\n */\nexport function decodeParam(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value)\n\t} catch {\n\t\t// A malformed `%` escape stays literal rather than throwing — the match still succeeds.\n\t\treturn value\n\t}\n}\n\n/**\n * Extract the URL-decoded params a compiled path captures from a concrete\n * pathname, or `undefined` when the pathname does not match.\n *\n * @remarks\n * Runs the {@link CompiledPath} `regex` against `pathname` (a single `exec`); a\n * miss returns `undefined`. On a hit it walks `params` POSITIONALLY — the\n * `n`-th param name pairs with the `n`-th capture group — and URL-decodes each\n * value with {@link decodeParam}. Returns a frozen `name → value` record (empty\n * for a parameterless path). Total — never throws.\n *\n * @param compiled - The {@link CompiledPath} from {@link compilePath}\n * @param pathname - The concrete request pathname to match (e.g. `/users/7`)\n * @returns The decoded params on a hit, or `undefined` on a miss\n *\n * @example\n * ```ts\n * const compiled = compilePath('/users/:id')\n * matchPath(compiled, '/users/7') // { id: '7' }\n * matchPath(compiled, '/users/a%2Fb') // { id: 'a/b' } — decoded\n * matchPath(compiled, '/posts/7') // undefined\n * ```\n */\nexport function matchPath(\n\tcompiled: CompiledPath,\n\tpathname: string,\n): Readonly<Record<string, string>> | undefined {\n\tconst result = compiled.regex.exec(pathname)\n\tif (result === null) return undefined\n\tconst params: Record<string, string> = {}\n\tfor (let index = 0; index < compiled.params.length; index += 1) {\n\t\tconst name = compiled.params[index]\n\t\tconst value = result[index + 1]\n\t\tif (name !== undefined && value !== undefined) params[name] = decodeParam(value)\n\t}\n\treturn Object.freeze(params)\n}\n\n/**\n * Classify one path segment into its specificity TIER — the SAME syntax\n * {@link compilePath} rewrites: a syntactically valid `:name` head is a PARAM\n * segment, a final `*name` is a WILDCARD segment, everything else (including a\n * literal segment that merely CONTAINS a `:` mid-string, e.g. `a:b`) is a\n * LITERAL segment.\n *\n * @remarks\n * This is the fix over the old engine's bug: the old classifier ranked any\n * segment `includes(':')` as a param, so a literal segment like `a:b` was\n * mis-tiered even though {@link compilePath} compiles it literally. Sharing one\n * segment parser between compilation and classification keeps the two in\n * agreement (§4 fixes). Pure and total.\n *\n * @param segment - One `/`-split path segment\n * @param isFinal - Whether `segment` is the last segment of its path (only the\n * final segment may be classified as a wildcard)\n * @returns The segment's specificity tier — {@link import('./constants.js').TIER_LITERAL},\n * {@link import('./constants.js').TIER_PARAM}, or\n * {@link import('./constants.js').TIER_WILDCARD}\n *\n * @example\n * ```ts\n * classifySegment(':id', true) // 1 — TIER_PARAM\n * classifySegment('*rest', true) // 0 — TIER_WILDCARD\n * classifySegment('a:b', true) // 2 — TIER_LITERAL — the old bug's regression case\n * classifySegment('users', false) // 2 — TIER_LITERAL\n * ```\n */\nexport function classifySegment(segment: string, isFinal: boolean): number {\n\tif (isFinal && /^\\*[A-Za-z_]\\w*$/.test(segment)) return TIER_WILDCARD\n\tif (/^:[A-Za-z_]\\w*/.test(segment)) return TIER_PARAM\n\treturn TIER_LITERAL\n}\n\n/**\n * Compute a route path's SPECIFICITY VECTOR — the per-segment type ranking\n * that breaks a tie when several registered routes match the same concrete\n * pathname.\n *\n * @remarks\n * Splits the CANONICALIZED path into segments (on `/`) and maps each to its\n * specificity tier via {@link classifySegment} — the same segment parser\n * {@link compilePath} uses, so a literal segment that merely contains a `:`\n * (e.g. `a:b`) is correctly tiered as literal rather than param (the old\n * engine's bug, fixed here). The standard route-precedence rule compares two\n * matching routes' vectors LEFT-TO-RIGHT: at the first index where the tiers\n * differ, the HIGHER tier (a literal over a param over a wildcard) is MORE\n * SPECIFIC and wins — so `/users/me` (`[2, 2]`) beats `/users/:id` (`[2, 1]`)\n * beats `/users/*rest` (`[2, 0]`) regardless of registration order. Two routes\n * that match the SAME concrete pathname necessarily have the same segment\n * count in the common case; {@link compareSpecificity} handles the general\n * case for totality.\n *\n * @param path - The route path pattern (e.g. `/users/:id`)\n * @returns The per-segment specificity tiers, in order\n *\n * @example\n * ```ts\n * computeSpecificity('/users/me') // [2, 2]\n * computeSpecificity('/users/:id') // [2, 1]\n * computeSpecificity('/files/*rest') // [2, 0]\n * computeSpecificity('/a:b') // [2] — literal, not param — the classification fix\n * ```\n */\nexport function computeSpecificity(path: string): readonly number[] {\n\tconst segments = canonicalizePath(path).split('/')\n\treturn segments.map((segment, index) => classifySegment(segment, index === segments.length - 1))\n}\n\n/**\n * Compare two route paths by SPECIFICITY — the comparator that picks the\n * most-specific matching route (literal-over-param-over-wildcard,\n * registration-order-independent).\n *\n * @remarks\n * Compares the two paths' {@link computeSpecificity} vectors LEFT-TO-RIGHT and\n * returns a standard `Array.sort` ordering: a NEGATIVE number when `a` is MORE\n * specific than `b` (so a descending-specificity sort puts `a` first),\n * positive when `b` is more specific, `0` when neither out-ranks the other\n * across the compared segments. At the first index where the tiers differ,\n * the higher tier wins; if one vector is a prefix of the other (different\n * segment counts), the LONGER, more-segmented path is treated as more\n * specific (a missing segment ranks below any real one).\n *\n * @param a - The first route path\n * @param b - The second route path\n * @returns A negative number when `a` is more specific, positive when `b` is, else `0`\n *\n * @example\n * ```ts\n * compareSpecificity('/users/me', '/users/:id') // negative — literal wins\n * compareSpecificity('/users/:id', '/users/*rest') // negative — param beats wildcard\n * compareSpecificity('/users/:id', '/users/:id') // 0 — equal specificity\n * ```\n */\nexport function compareSpecificity(a: string, b: string): number {\n\tconst left = computeSpecificity(a)\n\tconst right = computeSpecificity(b)\n\tconst length = Math.max(left.length, right.length)\n\tfor (let index = 0; index < length; index += 1) {\n\t\t// A missing segment ranks below any real one (the shorter path is less specific).\n\t\tconst tierA = left[index] ?? -1\n\t\tconst tierB = right[index] ?? -1\n\t\tif (tierA !== tierB) return tierB - tierA\n\t}\n\treturn 0\n}\n\n/**\n * Narrow a raw `request.method` string into a typed {@link Method} — total,\n * never throws.\n *\n * @remarks\n * Guarded via {@link import('./constants.js').METHODS} (the seven registrable\n * HTTP methods); any other value (an unknown verb, non-uppercase casing)\n * resolves to `undefined` rather than throwing (§14 guard totality). Pure\n * leaf shared by the `Dispatcher`'s `handle` (§5.1 unknown-verb honesty) and\n * anywhere else a raw method string needs narrowing.\n *\n * @param value - The raw `request.method` string to narrow\n * @returns The matching {@link Method}, or `undefined` when `value` is not one\n * of the seven registrable methods\n *\n * @example\n * ```ts\n * parseMethod('GET') // 'GET'\n * parseMethod('PURGE') // undefined\n * parseMethod('get') // undefined — case-sensitive\n * ```\n */\nexport function parseMethod(value: string): Method | undefined {\n\tif (\n\t\tvalue === 'GET' ||\n\t\tvalue === 'POST' ||\n\t\tvalue === 'PUT' ||\n\t\tvalue === 'PATCH' ||\n\t\tvalue === 'DELETE' ||\n\t\tvalue === 'HEAD' ||\n\t\tvalue === 'OPTIONS'\n\t)\n\t\treturn value\n\treturn undefined\n}\n\n/**\n * Join a group prefix and a route path into one `/`-prefixed path, normalizing\n * duplicate or missing joining slashes.\n *\n * @remarks\n * {@link import('./types.js').GroupInterface} / {@link import('./types.js').DispatchGroupInterface}\n * compose a prefix with each registered entry's path this way — pure string\n * composition (§4.2.2), no independent state. Both a duplicated slash\n * (`'/api/'` + `'/users'`) and a missing one (`'/api'` + `'users'`) normalize\n * to a single joining slash. An empty `prefix` returns `path` unchanged (after\n * ensuring a leading slash); an empty `path` returns `prefix` unchanged.\n * Pure and total.\n *\n * @param prefix - The group prefix (e.g. `/api`)\n * @param path - The route path being joined under the prefix (e.g. `/users`)\n * @returns The joined `/`-prefixed path\n *\n * @example\n * ```ts\n * joinPaths('/api', '/users') // '/api/users'\n * joinPaths('/api/', '/users') // '/api/users'\n * joinPaths('/api', 'users') // '/api/users'\n * joinPaths('', '/users') // '/users'\n * joinPaths('/api', '') // '/api'\n * ```\n */\nexport function joinPaths(prefix: string, path: string): string {\n\tif (prefix === '') return path.startsWith('/') ? path : `/${path}`\n\tif (path === '') return prefix\n\tconst left = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix\n\tconst right = path.startsWith('/') ? path : `/${path}`\n\treturn `${left}${right}`\n}\n","//#region src/core/helpers.ts\n/**\n* Extract the own enumerable keys of a mapped object, typed as its key union.\n*\n* @remarks\n* `Object.keys` widens its result to `string[]`, which breaks the key↔value\n* correlation a mapped type (like `EmitterHooks<TMap>`) otherwise guarantees.\n* A `for…in` push into a `keyof`-typed array narrows the result back,\n* type-safely and with no assertion.\n*\n* @typeParam T - The object shape whose keys are extracted.\n* @param object - The object to read keys from.\n* @returns The object's own enumerable keys, typed as `(keyof T)[]`.\n*\n* @example\n* ```ts\n* import { extractKeys } from '@src/core'\n*\n* const hooks = { tick: () => {}, done: () => {} }\n* extractKeys(hooks) // ['tick', 'done']\n* extractKeys({}) // []\n* ```\n*/\nfunction extractKeys(object) {\n\tconst collected = [];\n\tfor (const key in object) collected.push(key);\n\treturn collected;\n}\nObject.freeze([\n\t\"null\",\n\t\"boolean\",\n\t\"object\",\n\t\"array\",\n\t\"number\",\n\t\"integer\",\n\t\"string\"\n]);\n/** Determine whether a value is callable. */\nfunction isFunction(value) {\n\treturn typeof value === \"function\";\n}\n//#endregion\n//#region src/core/Emitter.ts\n/**\n* A typed synchronous event emitter — the foundational observable primitive of\n* the codebase (AGENTS §13). Stateful entities OWN one as a `#emitter` field and\n* expose it through `readonly emitter`; they never inherit from it.\n*\n* @typeParam TMap - The event map: each event name to the argument tuple its\n* listeners receive.\n*\n* @remarks\n* - **Synchronous.** `emit` invokes listeners in registration order, in the\n* current tick.\n* - **Listener isolation.** A throwing listener never stops its siblings: every\n* listener runs, and a throw is routed to the `error` handler\n* ({@link EmitterOptions.error}) — never rethrown. Every throwing listener\n* surfaces (not just the first), and with no `error` handler a throw is swallowed\n* silently. The `error` handler runs inside its own try/catch, so a throwing\n* error-handler is swallowed too (anti-recursion — it cannot escape or re-enter).\n* - **Per-event storage.** Listeners live in a per-event `Set`, so every public\n* method is precisely typed with no assertions.\n* - **Destroyed → no-op.** After `destroy()`, `on` / `once` / `emit` do nothing\n* and `destroyed` is `true`.\n*\n* @example\n* ```ts\n* type CounterEventMap = {\n* \ttick: readonly [count: number]\n* \tdone: readonly []\n* }\n*\n* const emitter = new Emitter<CounterEventMap>({\n* \ton: { done: () => stop() },\n* \terror: (error, event) => log(`listener for ${event} threw`, error),\n* })\n* emitter.on('tick', (count) => render(count))\n* emitter.emit('tick', 1)\n* ```\n*/\nvar Emitter = class {\n\t#destroyed = false;\n\t#listeners = {};\n\t#wrappers = {};\n\t#error;\n\tconstructor(options) {\n\t\tconst error = options?.error;\n\t\tthis.#error = isFunction(error) ? error : void 0;\n\t\tconst hooks = options?.on;\n\t\tif (hooks !== void 0) this.#wire(hooks);\n\t}\n\tget destroyed() {\n\t\treturn this.#destroyed;\n\t}\n\ton(event, handler) {\n\t\tif (this.#destroyed) return;\n\t\t(this.#listeners[event] ??= /* @__PURE__ */ new Set()).add(handler);\n\t}\n\tonce(event, handler) {\n\t\tif (this.#destroyed) return;\n\t\tconst pending = this.#wrappers[event] ??= /* @__PURE__ */ new Map();\n\t\tconst wrapper = (...args) => {\n\t\t\tthis.#listeners[event]?.delete(wrapper);\n\t\t\tconst wrappers = pending.get(handler);\n\t\t\twrappers?.delete(wrapper);\n\t\t\tif (wrappers !== void 0 && wrappers.size === 0) pending.delete(handler);\n\t\t\thandler(...args);\n\t\t};\n\t\tconst wrappers = pending.get(handler) ?? /* @__PURE__ */ new Set();\n\t\twrappers.add(wrapper);\n\t\tpending.set(handler, wrappers);\n\t\tthis.on(event, wrapper);\n\t}\n\toff(event, handler) {\n\t\tconst listeners = this.#listeners[event];\n\t\tconst wrappers = this.#wrappers[event];\n\t\tconst pending = wrappers?.get(handler);\n\t\tif (pending !== void 0) {\n\t\t\tfor (const wrapper of pending) listeners?.delete(wrapper);\n\t\t\twrappers?.delete(handler);\n\t\t}\n\t\tlisteners?.delete(handler);\n\t}\n\temit(event, ...args) {\n\t\tif (this.#destroyed) return;\n\t\tconst listeners = this.#listeners[event];\n\t\tif (listeners === void 0) return;\n\t\tfor (const handler of [...listeners]) try {\n\t\t\thandler(...args);\n\t\t} catch (error) {\n\t\t\tthis.#surface(error, event);\n\t\t}\n\t}\n\tcount(event) {\n\t\tif (event !== void 0) return this.#listeners[event]?.size ?? 0;\n\t\tlet total = 0;\n\t\tfor (const set of Object.values(this.#listeners)) total += set?.size ?? 0;\n\t\treturn total;\n\t}\n\tclear(event) {\n\t\tif (event !== void 0) {\n\t\t\tdelete this.#listeners[event];\n\t\t\tdelete this.#wrappers[event];\n\t\t\treturn;\n\t\t}\n\t\tthis.#listeners = {};\n\t\tthis.#wrappers = {};\n\t}\n\tdestroy() {\n\t\tthis.#listeners = {};\n\t\tthis.#wrappers = {};\n\t\tthis.#error = void 0;\n\t\tthis.#destroyed = true;\n\t}\n\t#surface(error, event) {\n\t\tconst handler = this.#error;\n\t\tif (handler === void 0) return;\n\t\ttry {\n\t\t\thandler(error, String(event));\n\t\t} catch {}\n\t}\n\t#wire(hooks) {\n\t\tfor (const event of extractKeys(hooks)) {\n\t\t\tconst handler = hooks[event];\n\t\t\tif (isFunction(handler)) this.on(event, handler);\n\t\t}\n\t}\n};\n//#endregion\n//#region src/core/factories.ts\n/**\n* Create a typed event emitter — the foundational observable primitive (AGENTS §13).\n*\n* @remarks\n* Prefer this over `new Emitter(...)` at call sites that only need the interface.\n* Entities that OWN an emitter (the §13 pattern) construct `new Emitter(...)` for\n* their `#emitter` field directly; this factory is the standalone entry point.\n*\n* @typeParam TMap - The event map: each event name to its listener argument tuple.\n* @param options - Optional `on` hooks (initial listeners wired at construction) and\n* an optional `error` handler for a listener's throw\n* @returns A typed {@link EmitterInterface}\n*\n* @example\n* ```ts\n* import { createEmitter } from '@src/core'\n*\n* type ClockEventMap = {\n* \ttick: readonly [at: number]\n* }\n*\n* const clock = createEmitter<ClockEventMap>({ on: { tick: (at) => log(at) } })\n* clock.emit('tick', Date.now())\n* ```\n*/\nfunction createEmitter(options) {\n\treturn new Emitter(options);\n}\n//#endregion\nexport { Emitter, createEmitter, extractKeys };\n\n//# sourceMappingURL=index.js.map","//#region src/core/constants.ts\n/**\n* The seven standard JSON Schema `type` names, frozen.\n*\n* @remarks\n* The runtime source of truth for the {@link JSONSchemaType} vocabulary. Compose\n* it with the shipped primitives instead of reaching for a bespoke guard:\n* `literalOf(...JSON_SCHEMA_TYPES)` is the guard, and\n* `parseEnum(value, JSON_SCHEMA_TYPES)` / `parseEnumField(record, path, JSON_SCHEMA_TYPES)`\n* is the parser.\n*\n* @example\n* ```ts\n* import { JSON_SCHEMA_TYPES, literalOf, parseEnumField } from '@src/core'\n*\n* const isSchemaType = literalOf(...JSON_SCHEMA_TYPES) // Guard<JSONSchemaType>\n* parseEnumField(schema, 'type', JSON_SCHEMA_TYPES) // JSONSchemaType | undefined\n* ```\n*/\nvar JSON_SCHEMA_TYPES = Object.freeze([\n\t\"null\",\n\t\"boolean\",\n\t\"object\",\n\t\"array\",\n\t\"number\",\n\t\"integer\",\n\t\"string\"\n]);\n//#endregion\n//#region src/core/validators.ts\n/** Determine whether a value is `null`. */\nfunction isNull(value) {\n\treturn value === null;\n}\n/** Determine whether a value is `undefined`. */\nfunction isUndefined(value) {\n\treturn value === void 0;\n}\n/** Determine whether a value is defined (neither `null` nor `undefined`). */\nfunction isDefined(value) {\n\treturn value !== null && value !== void 0;\n}\n/** Determine whether a value is a string. */\nfunction isString(value) {\n\treturn typeof value === \"string\";\n}\n/**\n* Determine whether a value is a number.\n*\n* @remarks\n* Includes `NaN` and `±Infinity` — use {@link isFiniteNumber} to exclude them.\n*/\nfunction isNumber(value) {\n\treturn typeof value === \"number\";\n}\n/** Determine whether a value is a finite number (excludes `NaN` and `±Infinity`). */\nfunction isFiniteNumber(value) {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n/** Determine whether a value is a finite integer (excludes `NaN`, `±Infinity`, and fractional numbers). */\nfunction isInteger(value) {\n\treturn Number.isInteger(value);\n}\n/** Determine whether a value is a boolean. */\nfunction isBoolean(value) {\n\treturn typeof value === \"boolean\";\n}\n/** Determine whether a value is exactly `true`. */\nfunction isTrue(value) {\n\treturn value === true;\n}\n/** Determine whether a value is exactly `false`. */\nfunction isFalse(value) {\n\treturn value === false;\n}\n/** Determine whether a value is a bigint. */\nfunction isBigInt(value) {\n\treturn typeof value === \"bigint\";\n}\n/** Determine whether a value is a symbol. */\nfunction isSymbol(value) {\n\treturn typeof value === \"symbol\";\n}\n/** Determine whether a value is callable. */\nfunction isFunction(value) {\n\treturn typeof value === \"function\";\n}\n/** Determine whether a value is a string or `null`. */\nfunction isNullableString(value) {\n\treturn value === null || isString(value);\n}\n/** Determine whether a value is a number or `null` (the number may be `NaN` / `±Infinity`). */\nfunction isNullableNumber(value) {\n\treturn value === null || isNumber(value);\n}\n/** Determine whether a value is a boolean or `null`. */\nfunction isNullableBoolean(value) {\n\treturn value === null || isBoolean(value);\n}\n/** Determine whether a value is a `Date`. */\nfunction isDate(value) {\n\treturn value instanceof Date;\n}\n/** Determine whether a value is a `RegExp`. */\nfunction isRegExp(value) {\n\treturn value instanceof RegExp;\n}\n/** Determine whether a value is an `Error`. */\nfunction isError(value) {\n\treturn value instanceof Error;\n}\n/** Determine whether a value is a native `Promise` (use {@link isPromiseLike} for any thenable). */\nfunction isPromise(value) {\n\treturn value instanceof Promise;\n}\n/**\n* Determine whether a value is promise-like — an object exposing callable\n* `then`, `catch`, and `finally` methods.\n*\n* @remarks\n* Accepts any object with all three methods, not only native `Promise`\n* instances. Use {@link isPromise} when you specifically need `instanceof Promise`.\n*/\nfunction isPromiseLike(value) {\n\tif (!isObject(value)) return false;\n\tconst outcome = attempt(() => {\n\t\tconst thenValue = Reflect.get(value, \"then\");\n\t\tconst catchValue = Reflect.get(value, \"catch\");\n\t\tconst finallyValue = Reflect.get(value, \"finally\");\n\t\treturn isFunction(thenValue) && isFunction(catchValue) && isFunction(finallyValue);\n\t});\n\treturn outcome.success && outcome.value;\n}\n/** Determine whether a value is an `ArrayBuffer`. */\nfunction isArrayBuffer(value) {\n\treturn value instanceof ArrayBuffer;\n}\n/**\n* Determine whether a value is a `SharedArrayBuffer`.\n*\n* @remarks\n* Guards the global existence of `SharedArrayBuffer` first — safe where it is\n* absent or disabled (e.g. a context that is not cross-origin isolated).\n*/\nfunction isSharedArrayBuffer(value) {\n\treturn typeof SharedArrayBuffer !== \"undefined\" && value instanceof SharedArrayBuffer;\n}\n/**\n* Determine whether a value implements the iterable protocol (`Symbol.iterator`).\n*\n* @remarks\n* Strings are explicitly included: a string has a callable `Symbol.iterator`\n* but is not an object, so the generic object path alone would miss it.\n*/\nfunction isIterable(value) {\n\tif (isString(value)) return true;\n\tif (!isObject(value)) return false;\n\tconst outcome = attempt(() => isFunction(Reflect.get(value, Symbol.iterator)));\n\treturn outcome.success && outcome.value;\n}\n/** Determine whether a value implements the async iterable protocol (`Symbol.asyncIterator`). */\nfunction isAsyncIterable(value) {\n\tif (!isObject(value)) return false;\n\tconst outcome = attempt(() => isFunction(Reflect.get(value, Symbol.asyncIterator)));\n\treturn outcome.success && outcome.value;\n}\n/**\n* Determine whether a value is a non-null object.\n*\n* @remarks\n* `true` for arrays, class instances, plain objects, `Map`, `Set`, etc. — use\n* {@link isRecord} when you need a plain-record check.\n*/\nfunction isObject(value) {\n\treturn typeof value === \"object\" && value !== null;\n}\n/**\n* Determine whether a value is a plain record (object literal or null-prototype),\n* not an array or class instance.\n*\n* @remarks\n* Use instead of {@link isObject} to distinguish a plain `{}` /\n* `Object.create(null)` from arrays, `Date`, `Map`, etc. The prototype-chain\n* test is realm-agnostic: rather than comparing against the current realm's\n* `Object.prototype` (which a plain object from another `vm.Context`, iframe,\n* or worker would fail), it accepts any value whose prototype is `null`, OR\n* whose prototype's own prototype is `null` — the shape every plain object\n* has in every realm, since `Object.prototype` itself always sits one step\n* above `null`. Arrays and class instances are still rejected: an array's\n* prototype chain runs through `Array.prototype` before `null`, and a class\n* instance's runs through the class's own prototype. The whole body runs\n* inside `attempt` (AGENTS §14) so a revoked `Proxy` or a hostile\n* `getPrototypeOf` trap cannot escape as a thrown error.\n*/\nfunction isRecord(value) {\n\tconst outcome = attempt(() => {\n\t\tif (!isObject(value) || isArray(value)) return false;\n\t\tconst prototype = Object.getPrototypeOf(value);\n\t\treturn prototype === null || Object.getPrototypeOf(prototype) === null;\n\t});\n\treturn outcome.success && outcome.value;\n}\n/** Determine whether a value is a `Map`. */\nfunction isMap(value) {\n\treturn value instanceof Map;\n}\n/** Determine whether a value is a `Set`. */\nfunction isSet(value) {\n\treturn value instanceof Set;\n}\n/** Determine whether a value is a `WeakMap`. */\nfunction isWeakMap(value) {\n\treturn value instanceof WeakMap;\n}\n/** Determine whether a value is a `WeakSet`. */\nfunction isWeakSet(value) {\n\treturn value instanceof WeakSet;\n}\n/** Determine whether a value is an array. */\nfunction isArray(value) {\n\treturn Array.isArray(value);\n}\n/** Determine whether a value is a `DataView`. */\nfunction isDataView(value) {\n\treturn value instanceof DataView;\n}\n/** Determine whether a value is an `ArrayBufferView` (any typed array or `DataView`). */\nfunction isArrayBufferView(value) {\n\treturn ArrayBuffer.isView(value);\n}\n/** Determine whether a value is an `Int8Array`. */\nfunction isInt8Array(value) {\n\treturn value instanceof Int8Array;\n}\n/** Determine whether a value is a `Uint8Array`. */\nfunction isUint8Array(value) {\n\treturn value instanceof Uint8Array;\n}\n/** Determine whether a value is a `Uint8ClampedArray`. */\nfunction isUint8ClampedArray(value) {\n\treturn value instanceof Uint8ClampedArray;\n}\n/** Determine whether a value is an `Int16Array`. */\nfunction isInt16Array(value) {\n\treturn value instanceof Int16Array;\n}\n/** Determine whether a value is a `Uint16Array`. */\nfunction isUint16Array(value) {\n\treturn value instanceof Uint16Array;\n}\n/** Determine whether a value is an `Int32Array`. */\nfunction isInt32Array(value) {\n\treturn value instanceof Int32Array;\n}\n/** Determine whether a value is a `Uint32Array`. */\nfunction isUint32Array(value) {\n\treturn value instanceof Uint32Array;\n}\n/** Determine whether a value is a `Float32Array`. */\nfunction isFloat32Array(value) {\n\treturn value instanceof Float32Array;\n}\n/** Determine whether a value is a `Float64Array`. */\nfunction isFloat64Array(value) {\n\treturn value instanceof Float64Array;\n}\n/**\n* Determine whether a value is a `BigInt64Array`.\n*\n* @remarks\n* Guards the global existence of `BigInt64Array` first — safe in environments\n* that pre-date the BigInt typed-array additions.\n*/\nfunction isBigInt64Array(value) {\n\treturn typeof BigInt64Array !== \"undefined\" && value instanceof BigInt64Array;\n}\n/**\n* Determine whether a value is a `BigUint64Array`.\n*\n* @remarks\n* Guards the global existence of `BigUint64Array` first — safe in environments\n* that pre-date the BigInt typed-array additions.\n*/\nfunction isBigUint64Array(value) {\n\treturn typeof BigUint64Array !== \"undefined\" && value instanceof BigUint64Array;\n}\n/** Determine whether a value is the empty string `''`. */\nfunction isEmptyString(value) {\n\treturn isString(value) && value.length === 0;\n}\n/** Determine whether a value is an empty array. */\nfunction isEmptyArray(value) {\n\treturn isArray(value) && value.length === 0;\n}\n/** Determine whether a value is an empty plain object (no own string or enumerable symbol keys). */\nfunction isEmptyObject(value) {\n\tif (!isRecord(value)) return false;\n\treturn Object.keys(value).length === 0 && enumerableSymbolCount(value) === 0;\n}\n/** Determine whether a value is an empty `Map`. */\nfunction isEmptyMap(value) {\n\treturn value instanceof Map && value.size === 0;\n}\n/** Determine whether a value is an empty `Set`. */\nfunction isEmptySet(value) {\n\treturn value instanceof Set && value.size === 0;\n}\n/** Determine whether a value is a non-empty string (at least one character). */\nfunction isNonEmptyString(value) {\n\treturn isString(value) && value.length > 0;\n}\n/** Determine whether a value is a non-empty array (at least one element). */\nfunction isNonEmptyArray(value) {\n\treturn isArray(value) && value.length > 0;\n}\n/** Determine whether a value is a non-empty plain object (at least one own string or enumerable symbol key). */\nfunction isNonEmptyObject(value) {\n\tif (!isRecord(value)) return false;\n\treturn Object.keys(value).length > 0 || enumerableSymbolCount(value) > 0;\n}\n/** Determine whether a value is a non-empty `Map` (at least one entry). */\nfunction isNonEmptyMap(value) {\n\treturn value instanceof Map && value.size > 0;\n}\n/** Determine whether a value is a non-empty `Set` (at least one element). */\nfunction isNonEmptySet(value) {\n\treturn value instanceof Set && value.size > 0;\n}\n/** Determine whether a value is a function that declares zero parameters (`Function.length === 0`). */\nfunction isZeroArg(value) {\n\treturn isFunction(value) && value.length === 0;\n}\n/**\n* Determine whether a value is a native `async function`.\n*\n* @remarks\n* Uses `constructor.name === 'AsyncFunction'` — not `instanceof`, which is\n* unreliable across realms. The `?.` keeps the guard total (§14): a function\n* whose `constructor` was nulled yields `undefined`, never a thrown `null.name`.\n*/\nfunction isAsyncFunction(value) {\n\treturn isFunction(value) && value.constructor?.name === \"AsyncFunction\";\n}\n/** Determine whether a value is a generator function (`function*`). */\nfunction isGeneratorFunction(value) {\n\treturn isFunction(value) && value.constructor?.name === \"GeneratorFunction\";\n}\n/** Determine whether a value is an async generator function (`async function*`). */\nfunction isAsyncGeneratorFunction(value) {\n\treturn isFunction(value) && value.constructor?.name === \"AsyncGeneratorFunction\";\n}\n/** Determine whether a value is a zero-argument async function. */\nfunction isZeroArgAsync(value) {\n\treturn isZeroArg(value) && isAsyncFunction(value);\n}\n/** Determine whether a value is a zero-argument generator function. */\nfunction isZeroArgGenerator(value) {\n\treturn isZeroArg(value) && isGeneratorFunction(value);\n}\n/** Determine whether a value is a zero-argument async generator function. */\nfunction isZeroArgAsyncGenerator(value) {\n\treturn isZeroArg(value) && isAsyncGeneratorFunction(value);\n}\n/**\n* Determine whether a value can be used as a `new`-target constructor.\n*\n* @remarks\n* Probes with `Reflect.construct(String, [], value)`: a real constructor\n* succeeds, while arrow functions, plain functions, and non-functions throw\n* and yield `false`. Never throws. Backs the `instanceOf` combinator.\n*/\nfunction isConstructor(value) {\n\tif (!isFunction(value)) return false;\n\ttry {\n\t\tReflect.construct(String, [], value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n/**\n* Determine whether a value is a cycle-safe JSON value.\n*\n* @remarks\n* Total guard: never throws, returns `false` for cycles, functions, `Date`\n* instances, class instances, `NaN`, and `±Infinity`. Arrays and plain records\n* are walked with an ancestor set so recursive input fails instead of hanging.\n* The whole walk runs inside `attempt` (AGENTS §14): a hostile getter on a\n* record property, or a revoked `Proxy` anywhere in the structure, is caught\n* and yields `false` instead of escaping as a thrown error.\n*\n* @param value - The value to test\n* @returns `true` when the value has a JSON representation\n*\n* @example\n* ```ts\n* isJSONValue({ nested: [1, 'x', null] }) // true\n* isJSONValue(Number.NaN) // false\n* ```\n*/\nfunction isJSONValue(value) {\n\tconst ancestors = /* @__PURE__ */ new WeakSet();\n\tconst check = (entry) => {\n\t\tif (entry === null || isString(entry) || isBoolean(entry) || isFiniteNumber(entry)) return true;\n\t\tif (Array.isArray(entry)) {\n\t\t\tif (ancestors.has(entry)) return false;\n\t\t\tancestors.add(entry);\n\t\t\tconst valid = entry.every(check);\n\t\t\tancestors.delete(entry);\n\t\t\treturn valid;\n\t\t}\n\t\tif (!isRecord(entry)) return false;\n\t\tif (ancestors.has(entry)) return false;\n\t\tancestors.add(entry);\n\t\tconst valid = Object.values(entry).every(check);\n\t\tancestors.delete(entry);\n\t\treturn valid;\n\t};\n\tconst outcome = attempt(() => check(value));\n\treturn outcome.success && outcome.value;\n}\n/**\n* Determine whether a value is a primitive JSON value.\n*\n* @remarks\n* The flat leaf of any JSON document: `null`, a string, a **finite** number, or\n* a boolean. Uses {@link isFiniteNumber} (not {@link isNumber}) because real JSON\n* carries no `NaN` / `±Infinity` — `JSON.stringify(NaN)` is `'null'`.\n*\n* The recursive {@link isJSONValue} guard is shipped and stays total with\n* cycle-safe walking. Dedicated `isJSONObject` / `isJSONSchema` validators and\n* the broad `JSONSchemaDefinition` remain omitted; compose narrower shapes with\n* the combinators and gate untrusted strings with `parseJSON` / `parseJSONAs`.\n*\n* @param value - The value to test\n* @returns `true` when `value` is `null`, a string, a finite number, or a boolean\n*\n* @example\n* ```ts\n* isJSONPrimitive(null) // true\n* isJSONPrimitive('hi') // true\n* isJSONPrimitive(42) // true\n* isJSONPrimitive(Number.NaN) // false — not representable in JSON\n* isJSONPrimitive({}) // false\n* ```\n*/\nfunction isJSONPrimitive(value) {\n\treturn isNull(value) || isString(value) || isFiniteNumber(value) || isBoolean(value);\n}\n//#endregion\n//#region src/core/helpers.ts\n/**\n* Invoke a callback and capture its outcome as a {@link Result}, never letting\n* a throw escape.\n*\n* @remarks\n* The single sanctioned never-throw boundary for the guards (AGENTS §14). The\n* `whereOf`, `lazyOf`, and `transformOf` combinators invoke caller-supplied\n* callbacks *inside* a guard body, yet a guard must NEVER throw — it returns a\n* `boolean`. This converts a throwing callback into a `Failure` so the\n* surrounding guard can treat it as a non-match instead of propagating the\n* exception, written once and shared rather than copy-pasted as ad-hoc\n* `try`/`catch`.\n*\n* @param callback - The callback to invoke with no arguments\n* @returns A `Success` carrying the return value, or a `Failure` carrying the\n* thrown reason normalised to an `Error`\n*\n* @example\n* ```ts\n* const outcome = attempt(() => predicate(value))\n* return outcome.success && outcome.value\n* ```\n*/\nfunction attempt(callback) {\n\ttry {\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tvalue: callback()\n\t\t};\n\t} catch (reason) {\n\t\tif (reason instanceof Error) return {\n\t\t\tsuccess: false,\n\t\t\terror: reason\n\t\t};\n\t\tlet message = \"Unknown thrown value\";\n\t\ttry {\n\t\t\tmessage = String(reason);\n\t\t} catch {}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: new Error(message)\n\t\t};\n\t}\n}\n/**\n* Resolve a (possibly nested) field value from a record by a key or key path.\n*\n* @remarks\n* A single `string` is ONE key (never split on `.`, so dotted keys are safe); a\n* string array descends left-to-right through nested objects. Intermediates may\n* be any object — records, class instances, or arrays indexed by string. Returns\n* `undefined` the moment a segment is missing or lands on a non-object, so the\n* lookup is total — even against a hostile getter or Proxy trap that throws on\n* read, contained via {@link attempt} so the throw never escapes.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns The resolved value, or `undefined`\n*\n* @example\n* ```ts\n* resolveField({ user: { name: 'Ada' } }, ['user', 'name']) // 'Ada'\n* resolveField({ 'a.b': 1 }, 'a.b') // 1 (one key)\n* resolveField({ a: 1 }, ['a', 'b']) // undefined\n* ```\n*/\nfunction resolveField(record, path) {\n\tconst keys = isString(path) ? [path] : path;\n\tlet current = record;\n\tfor (const key of keys) {\n\t\tif (!isObject(current)) return void 0;\n\t\tconst container = current;\n\t\tconst outcome = attempt(() => Reflect.get(container, key));\n\t\tif (!outcome.success) return void 0;\n\t\tcurrent = outcome.value;\n\t}\n\treturn current;\n}\n/**\n* Build a deterministic pseudo-random source seeded from a single number.\n*\n* @remarks\n* A mulberry32 generator — the same seed always yields the same sequence, so\n* generated seed data is reproducible across runs. Used as the default random\n* source for {@link compileGenerator}, seeded from the wall clock so casual\n* callers still get varied output without passing a source themselves.\n*\n* @param seed - The seed for the sequence\n* @returns A {@link RandomFunction} returning values in `[0, 1)`\n*\n* @example\n* ```ts\n* const random = seededRandom(42)\n* random() // always the same first value for seed 42\n* ```\n*/\nfunction seededRandom(seed) {\n\tlet state = seed >>> 0;\n\treturn () => {\n\t\tstate = state + 1831565813 >>> 0;\n\t\tlet t = state;\n\t\tt = Math.imul(t ^ t >>> 15, t | 1);\n\t\tt ^= t + Math.imul(t ^ t >>> 7, t | 61);\n\t\treturn ((t ^ t >>> 14) >>> 0) / 4294967296;\n\t};\n}\n/**\n* Count the enumerable own-symbol keys on a value.\n*\n* @remarks\n* String keys are ignored — only `Object.getOwnPropertySymbols` entries whose\n* descriptor is `enumerable` are counted. Backs the object-emptiness guards\n* (`isEmptyObject` / `isNonEmptyObject`) so a record keyed only by an\n* enumerable symbol is not mistaken for empty.\n*\n* @param value - The object to inspect\n* @returns The number of enumerable own-symbol keys\n*\n* @example\n* ```ts\n* const flag = Symbol('flag')\n* enumerableSymbolCount(Object.defineProperty({}, flag, { value: 1, enumerable: true })) // 1\n* enumerableSymbolCount({}) // 0\n* ```\n*/\nfunction enumerableSymbolCount(value) {\n\tlet count = 0;\n\tfor (const symbol of Object.getOwnPropertySymbols(value)) if (Object.getOwnPropertyDescriptor(value, symbol)?.enumerable) count += 1;\n\treturn count;\n}\n/**\n* Narrow a compiled {@link JSONSchema} down to the open `Readonly<Record<string, unknown>>` shape\n* tool definitions advertise as `parameters` — through the {@link isRecord} boundary guard, never\n* an assertion (AGENTS §14).\n*\n* @remarks\n* A `JSONSchema` is the closed contract-compiler fragment (it has no index signature), whereas a\n* tool advertises its `parameters` as an open record. The two are structurally compatible but not\n* assignable, so the schema crosses that boundary through `isRecord` — a compiled contract schema\n* is always a record, so the guard passes; the `undefined` fallback only satisfies the type's\n* optionality. This is the single sanctioned narrowing from a compiled contract schema to the open\n* tool-parameters record, so the crossing lives once rather than being copy-pasted per call site.\n*\n* @param schema - The compiled JSON Schema (a contract's `schema`)\n* @returns The schema as the open tool-parameters record, or `undefined` when it is not a record\n*\n* @example\n* ```ts\n* import { createContract, schemaToParameters } from '@src/core'\n*\n* const contract = createContract(shape)\n* const parameters = schemaToParameters(contract.schema) // the open record a tool advertises\n* ```\n*/\nfunction schemaToParameters(schema) {\n\treturn isRecord(schema) ? schema : void 0;\n}\n//#endregion\n//#region src/core/combinators.ts\nfunction arrayOf(elementGuard) {\n\treturn (value) => {\n\t\tif (!isArray(value)) return false;\n\t\tconst outcome = attempt(() => value.every(elementGuard));\n\t\treturn outcome.success && outcome.value;\n\t};\n}\nfunction tupleOf(...guards) {\n\treturn (value) => {\n\t\tif (!isArray(value)) return false;\n\t\tconst outcome = attempt(() => {\n\t\t\tif (value.length !== guards.length) return false;\n\t\t\tfor (let index = 0; index < guards.length; index += 1) {\n\t\t\t\tconst guard = guards[index];\n\t\t\t\tif (!guard?.(value[index])) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn outcome.success && outcome.value;\n\t};\n}\n/**\n* Build a guard that accepts values identical (via `Object.is`) to one of the\n* provided literal primitives.\n*\n* @example\n* ```ts\n* const isRole = literalOf('admin', 'member', 'guest')\n* isRole('admin') // true\n* isRole('owner') // false\n* ```\n*/\nfunction literalOf(...literals) {\n\treturn (value) => literals.some((literal) => Object.is(literal, value));\n}\n/**\n* Build a guard that accepts instances of the provided constructor.\n*\n* @remarks\n* Verifies that `ctor` is a real constructor (via {@link isConstructor}) first,\n* so passing an arrow function does not silently produce a broken guard.\n*\n* @example\n* ```ts\n* const isDateValue = instanceOf(Date)\n* isDateValue(new Date()) // true\n* isDateValue({}) // false\n* ```\n*/\nfunction instanceOf(ctor) {\n\treturn (value) => isConstructor(ctor) && isObject(value) && value instanceof ctor;\n}\n/**\n* Build a guard from a native `enum` or any object whose values are strings or\n* numbers.\n*\n* @example\n* ```ts\n* enum Direction { Up = 'up', Down = 'down' }\n* const isDirection = enumOf(Direction)\n* isDirection('up') // true\n* isDirection('left') // false\n* ```\n*/\nfunction enumOf(enumeration) {\n\tconst values = new Set(Object.values(enumeration));\n\treturn (value) => (isString(value) || isNumber(value)) && values.has(value);\n}\nfunction setOf(elementGuard) {\n\treturn (value) => {\n\t\tif (!isSet(value)) return false;\n\t\tconst outcome = attempt(() => {\n\t\t\tfor (const entry of value) if (!elementGuard(entry)) return false;\n\t\t\treturn true;\n\t\t});\n\t\treturn outcome.success && outcome.value;\n\t};\n}\nfunction mapOf(keyGuard, valueGuard) {\n\treturn (value) => {\n\t\tif (!isMap(value)) return false;\n\t\tconst outcome = attempt(() => {\n\t\t\tfor (const [key, entryValue] of value) if (!keyGuard(key) || !valueGuard(entryValue)) return false;\n\t\t\treturn true;\n\t\t});\n\t\treturn outcome.success && outcome.value;\n\t};\n}\n/**\n* Build a guard that accepts plain records matching a guard shape.\n*\n* @remarks\n* Three calling modes depending on the `optional` argument:\n* - **No `optional`** — all shape keys required; extra keys rejected.\n* - **`optional: K[]`** — the listed keys are optional; all others required.\n* - **`optional: true`** — every shape key is optional.\n*\n* Key presence is tested with `Object.hasOwn`, so a shape key satisfied only by\n* an inherited prototype member (`toString`, `constructor`, …) counts as absent.\n* A non-object / `null` / array input returns `false` rather than throwing. The\n* extra-key check only inspects `Object.keys` (string keys), so an extra\n* enumerable SYMBOL key is never rejected — intentional, for JSON fidelity, and\n* matches the compiled guard.\n*\n* @example\n* ```ts\n* const isUser = recordOf({ name: isString, age: isNumber })\n* isUser({ name: 'Ada', age: 36 }) // true\n* isUser({ name: 'Ada' }) // false — age missing\n*\n* const isPartial = recordOf({ name: isString, age: isNumber }, ['age'])\n* isPartial({ name: 'Ada' }) // true\n* ```\n*/\nfunction recordOf(shape, optional) {\n\tconst allowed = /* @__PURE__ */ new Set();\n\tfor (const key in shape) if (Object.prototype.hasOwnProperty.call(shape, key)) allowed.add(key);\n\tconst optionalSet = new Set(optional === true ? [...allowed] : isArray(optional) ? optional.map((key) => String(key)) : []);\n\treturn (value) => {\n\t\tif (!isRecord(value)) return false;\n\t\tconst outcome = attempt(() => {\n\t\t\tfor (const key of Object.keys(value)) if (!allowed.has(key)) return false;\n\t\t\tfor (const key in shape) {\n\t\t\t\tif (!Object.prototype.hasOwnProperty.call(shape, key)) continue;\n\t\t\t\tconst present = Object.hasOwn(value, key);\n\t\t\t\tif (!optionalSet.has(key) && !present) return false;\n\t\t\t\tif (present) {\n\t\t\t\t\tconst guard = shape[key];\n\t\t\t\t\tif (!guard(value[key])) return false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t\treturn outcome.success && outcome.value;\n\t};\n}\nfunction iterableOf(elementGuard) {\n\treturn (value) => {\n\t\tif (!isIterable(value)) return false;\n\t\tconst outcome = attempt(() => {\n\t\t\tfor (const entry of value) if (!elementGuard(entry)) return false;\n\t\t\treturn true;\n\t\t});\n\t\treturn outcome.success && outcome.value;\n\t};\n}\n/**\n* Build a guard that accepts values that are own keys of the provided object.\n*\n* @remarks\n* Membership is tested with `Object.hasOwn`, so inherited prototype-chain keys\n* (`toString`, `constructor`, …) are rejected. An own property that shadows a\n* prototype name is accepted.\n*\n* @example\n* ```ts\n* const COLORS = { red: '#f00', green: '#0f0', blue: '#00f' } as const\n* const isColorKey = keyOf(COLORS)\n* isColorKey('red') // true\n* isColorKey('purple') // false\n* isColorKey('toString') // false — inherited, not an own key\n* ```\n*/\nfunction keyOf(value) {\n\treturn (entry) => (isString(entry) || isSymbol(entry) || isNumber(entry)) && Object.hasOwn(value, entry);\n}\n/**\n* Build a new guard shape by keeping only the listed keys — the structural\n* equivalent of `Pick<T, K>`. Produces a shape for {@link recordOf}, not a guard.\n*\n* @example\n* ```ts\n* const full = { name: isString, age: isNumber, role: isString }\n* const isName = recordOf(pickOf(full, ['name']))\n* isName({ name: 'Ada' }) // true\n* ```\n*/\nfunction pickOf(shape, keys) {\n\tconst result = Object.create(null);\n\tfor (const key of keys) if (Object.prototype.hasOwnProperty.call(shape, key)) result[key] = shape[key];\n\treturn result;\n}\n/**\n* Build a new guard shape by removing the listed keys — the structural\n* equivalent of `Omit<T, K>`. Produces a shape for {@link recordOf}, not a guard.\n*\n* @example\n* ```ts\n* const full = { name: isString, age: isNumber, role: isString }\n* const isPublic = recordOf(omitOf(full, ['role']))\n* isPublic({ name: 'Ada', age: 36 }) // true\n* ```\n*/\nfunction omitOf(shape, keys) {\n\tconst skipped = /* @__PURE__ */ new Set();\n\tfor (const key of keys) skipped.add(key);\n\tconst result = Object.create(null);\n\tfor (const key in shape) {\n\t\tif (!Object.prototype.hasOwnProperty.call(shape, key)) continue;\n\t\tif (!skipped.has(key)) result[key] = shape[key];\n\t}\n\treturn result;\n}\nfunction andOf(left, right) {\n\treturn (value) => left(value) && right(value);\n}\nfunction orOf(left, right) {\n\treturn (value) => left(value) || right(value);\n}\n/**\n* Negate a guard or predicate — passes when `guard` returns `false`.\n*\n* @remarks\n* Typed as `Guard<unknown>` because `Exclude<unknown, T>` is not useful; use\n* {@link complementOf} when you need the narrowed `Exclude<TBase, TExcluded>`.\n*\n* @example\n* ```ts\n* const isNotNull = notOf(isNull)\n* ```\n*/\nfunction notOf(guard) {\n\treturn (value) => !guard(value);\n}\n/**\n* Build a guard for `Exclude<TBase, TExcluded>` — accepts values that pass\n* `base` but not `excluded`.\n*\n* @example\n* ```ts\n* const isNonEmpty = complementOf(isString, isEmptyString)\n* isNonEmpty('hi') // true\n* isNonEmpty('') // false\n* ```\n*/\nfunction complementOf(base, excluded) {\n\treturn (value) => {\n\t\tif (!base(value)) return false;\n\t\treturn !excluded(value);\n\t};\n}\nfunction unionOf(...guards) {\n\treturn (value) => guards.some((guard) => guard(value));\n}\nfunction intersectionOf(...guards) {\n\treturn (value) => guards.every((guard) => guard(value));\n}\nfunction whereOf(base, predicate) {\n\treturn (value) => {\n\t\tif (!base(value)) return false;\n\t\tconst outcome = attempt(() => predicate(value));\n\t\treturn outcome.success && outcome.value;\n\t};\n}\n/**\n* Defer guard creation until first use by calling `thunk()` on every\n* invocation.\n*\n* @remarks\n* `thunk` is called on every guard call, not cached — this lets it close over a\n* binding assigned *after* `lazyOf` is called, the primary use case for\n* self-referential recursive guards. Per §14 a throw from `thunk` (or the guard\n* it resolves to) is contained and reported as a non-match.\n*\n* A recursive guard built this way has no cycle/depth detection: a cyclic or\n* pathologically deep input is stack-bounded — the overflow is contained and the\n* guard returns `false` rather than throwing, but it is not validated correctly\n* past that bound.\n*\n* @example\n* ```ts\n* type Tree = { value: number; children: Tree[] }\n* let isTree: Guard<Tree>\n* isTree = recordOf({ value: isNumber, children: arrayOf(lazyOf(() => isTree)) })\n* ```\n*/\nfunction lazyOf(thunk) {\n\treturn (value) => {\n\t\tconst outcome = attempt(() => thunk()(value));\n\t\treturn outcome.success && outcome.value;\n\t};\n}\nfunction transformOf(base, project, target) {\n\treturn (value) => {\n\t\tif (!base(value)) return false;\n\t\tconst outcome = attempt(() => project(value));\n\t\treturn outcome.success && target(outcome.value);\n\t};\n}\n/**\n* Build a guard that accepts finite numbers within an inclusive `[min, max]`\n* range.\n*\n* @remarks\n* Refines {@link isFiniteNumber} with the bound comparison, so `NaN` /\n* `±Infinity` are rejected before any comparison runs. An absent bound never\n* constrains that side. Reused for a number's own value AND, applied to a\n* `.length`, for string and array length refinements — the single source of the\n* bound logic shared by the compiled guard and parser (compilers.ts).\n*\n* @example\n* ```ts\n* const inRange = boundsOf(1, 5)\n* inRange(3) // true\n* inRange(0) // false — below min\n* inRange(6) // false — above max\n*\n* const atLeastTwo = boundsOf(2)\n* atLeastTwo(2) // true — unbounded above\n* ```\n*/\nfunction boundsOf(min, max) {\n\treturn whereOf(isFiniteNumber, (value) => (min === void 0 || value >= min) && (max === void 0 || value <= max));\n}\n/**\n* Build a guard that accepts strings matching a regular expression.\n*\n* @example\n* ```ts\n* const isHex = matchOf(/^[0-9a-f]+$/)\n* isHex('1a2f') // true\n* isHex('xyz') // false\n* ```\n*/\nfunction matchOf(pattern) {\n\treturn whereOf(isString, (value) => pattern.test(value));\n}\n/**\n* Build a guard that accepts strings satisfying optional length and pattern\n* refinements — `min` / `max` length and a `pattern`.\n*\n* @remarks\n* Composes {@link isString} with {@link boundsOf} on the string's `.length` and\n* an inline `pattern.test` (the same refinement {@link matchOf} performs). When all three options are absent it returns\n* the bare {@link isString} guard (the unconstrained fast path), so an\n* unrefined string leaf pays no wrapping cost. The single source of the string\n* refinement shared by the compiled guard and parser (compilers.ts).\n*\n* @example\n* ```ts\n* const isSlug = stringOf({ min: 1, max: 32, pattern: /^[a-z-]+$/ })\n* isSlug('hello-world') // true\n* isSlug('') // false — below min\n* isSlug('Hello') // false — pattern miss\n*\n* stringOf() // identical to isString\n* ```\n*/\nfunction stringOf(options) {\n\tconst min = options?.min;\n\tconst max = options?.max;\n\tconst pattern = options?.pattern;\n\tif (min === void 0 && max === void 0 && pattern === void 0) return isString;\n\tconst withinLength = boundsOf(min, max);\n\treturn whereOf(isString, (value) => withinLength(value.length) && (pattern === void 0 || pattern.test(value)));\n}\n/**\n* Extend a guard to also allow `null`.\n*\n* @example\n* ```ts\n* const isNullableString = nullableOf(isString)\n* isNullableString('hi') // true\n* isNullableString(null) // true\n* isNullableString(42) // false\n* ```\n*/\nfunction nullableOf(guard) {\n\treturn (value) => value === null || guard(value);\n}\n/**\n* Extend a guard to also allow `undefined` — the optional counterpart of\n* {@link nullableOf}.\n*\n* @example\n* ```ts\n* const isOptionalString = optionalOf(isString)\n* isOptionalString('hi') // true\n* isOptionalString(undefined) // true\n* isOptionalString(null) // false\n* ```\n*/\nfunction optionalOf(guard) {\n\treturn (value) => value === void 0 || guard(value);\n}\n//#endregion\n//#region src/core/parsers.ts\n/**\n* Parse an unknown value to a string.\n*\n* @remarks\n* A string is returned unchanged; a finite number is coerced to its decimal\n* string (`42` → `'42'`). `NaN`, `±Infinity`, and every other type → `undefined`.\n*\n* @param value - The value to parse\n* @returns A string, or `undefined`\n*/\nfunction parseString(value) {\n\tif (isString(value)) return value;\n\tif (isFiniteNumber(value)) return String(value);\n}\n/**\n* Parse an unknown value to a finite number.\n*\n* @remarks\n* A finite number is returned unchanged; a non-blank numeric string is parsed\n* via `Number(...)`. `NaN`, `±Infinity`, blank/non-numeric strings, and every\n* other type → `undefined`.\n*\n* @param value - The value to parse\n* @returns A finite number, or `undefined`\n*/\nfunction parseNumber(value) {\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : void 0;\n\tif (isString(value)) {\n\t\tif (value.trim() === \"\") return void 0;\n\t\tconst parsed = Number(value);\n\t\treturn Number.isFinite(parsed) ? parsed : void 0;\n\t}\n}\n/**\n* Parse an unknown value to a finite integer.\n*\n* @remarks\n* Accepts whatever {@link parseNumber} accepts, then requires the result to have\n* no fractional part. `3.14` / `'3.14'` → `undefined`.\n*\n* @param value - The value to parse\n* @returns A finite integer, or `undefined`\n*/\nfunction parseInteger(value) {\n\tconst parsed = parseNumber(value);\n\tif (parsed === void 0) return void 0;\n\treturn Number.isInteger(parsed) ? parsed : void 0;\n}\n/**\n* Parse an unknown value to a boolean.\n*\n* @remarks\n* A boolean is returned unchanged. The strings `'true'` / `'false'` / `'1'` /\n* `'0'` and the numbers `1` / `0` coerce to the matching boolean. Everything\n* else → `undefined`.\n*\n* @param value - The value to parse\n* @returns A boolean, or `undefined`\n*/\nfunction parseBoolean(value) {\n\tif (typeof value === \"boolean\") return value;\n\tif (value === \"true\" || value === \"1\" || value === 1) return true;\n\tif (value === \"false\" || value === \"0\" || value === 0) return false;\n}\n/**\n* Parse an unknown value to `null`.\n*\n* @remarks\n* A successful parse returns `null` itself — distinct from the `undefined`\n* failure sentinel every other parser in this file uses. Only `null` passes;\n* every other value (including `undefined`) → `undefined`.\n*\n* @param value - The value to parse\n* @returns `null` on a successful parse, or `undefined`\n*/\nfunction parseNull(value) {\n\treturn isNull(value) ? value : void 0;\n}\n/**\n* Parse an unknown value to a plain record — the input reference, never cloned.\n*\n* @param value - The value to parse\n* @returns The record, or `undefined`\n*/\nfunction parseRecord(value) {\n\treturn isRecord(value) ? value : void 0;\n}\n/**\n* Parse an unknown value to an array — the input reference, never cloned —\n* optionally guarding every element.\n*\n* @remarks\n* Without a `guard`, element types are NOT verified; let `T` default to\n* `unknown` rather than asserting a specific element type.\n*\n* @param value - The value to parse\n* @param guard - Optional element guard\n* @returns The array, or `undefined`\n*/\nfunction parseArray(value, guard) {\n\tif (!isArray(value)) return void 0;\n\tif (guard !== void 0 && !value.every(guard)) return void 0;\n\treturn value;\n}\n/**\n* Parse an unknown value to a cycle-safe JSON value — the input reference,\n* never cloned.\n*\n* @remarks\n* Unlike {@link parseRecord} / {@link parseArray}, this is a DEEP gate: it\n* walks the entire tree via {@link isJSONValue} rather than checking only the\n* top-level shape. That walk is cycle-safe and total (never throws) because\n* `isJSONValue` runs its own probe inside a guard, so an adversarial\n* structure (a cycle, a hostile getter) yields `undefined` instead of hanging\n* or throwing.\n*\n* @param value - The value to parse\n* @returns The value, or `undefined` when it is not a valid JSON value\n*/\nfunction parseJSONValue(value) {\n\treturn isJSONValue(value) ? value : void 0;\n}\n/**\n* Parse an unknown value as one of the allowed literal primitives.\n*\n* @remarks\n* Pairs with {@link literalOf} — both match by `Object.is`, so the\n* `parseEnum ↔ literalOf(...allowed)` pairing covers every literal primitive\n* (string, number, or boolean), not only strings. Matching is identity, never\n* cross-type coercion: `parseEnum('1', [1])` stays `undefined`.\n*\n* @param value - The value to parse\n* @param allowed - The permitted literal values\n* @returns The matched literal (by identity), or `undefined`\n*/\nfunction parseEnum(value, allowed) {\n\tfor (const option of allowed) if (Object.is(value, option)) return option;\n}\n/**\n* Read and parse a string field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns A string, or `undefined`\n*/\nfunction parseStringField(record, path) {\n\treturn parseString(resolveField(record, path));\n}\n/**\n* Read and parse a finite-number field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns A finite number, or `undefined`\n*/\nfunction parseNumberField(record, path) {\n\treturn parseNumber(resolveField(record, path));\n}\n/**\n* Read and parse a finite-integer field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns A finite integer, or `undefined`\n*/\nfunction parseIntegerField(record, path) {\n\treturn parseInteger(resolveField(record, path));\n}\n/**\n* Read and parse a boolean field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns A boolean, or `undefined`\n*/\nfunction parseBooleanField(record, path) {\n\treturn parseBoolean(resolveField(record, path));\n}\n/**\n* Read and parse a `null` field from a record by key or nested key path.\n*\n* @remarks\n* A successful parse returns `null` itself — distinct from the `undefined`\n* failure sentinel, which also covers a missing field.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns `null` on a successful parse, or `undefined`\n*/\nfunction parseNullField(record, path) {\n\treturn parseNull(resolveField(record, path));\n}\n/**\n* Read and parse a nested record field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns A plain record, or `undefined`\n*/\nfunction parseRecordField(record, path) {\n\treturn parseRecord(resolveField(record, path));\n}\n/**\n* Read and parse an array field from a record by key or nested key path,\n* optionally guarding elements.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @param guard - Optional element guard\n* @returns An array, or `undefined`\n*/\nfunction parseArrayField(record, path, guard) {\n\treturn parseArray(resolveField(record, path), guard);\n}\n/**\n* Read and parse an enum field from a record by key or nested key path.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @param allowed - The permitted literal values\n* @returns The matched literal, or `undefined`\n*/\nfunction parseEnumField(record, path, allowed) {\n\treturn parseEnum(resolveField(record, path), allowed);\n}\n/**\n* Read and parse a JSON-value field from a record by key or nested key path.\n*\n* @remarks\n* Deep-gates the field's whole subtree via {@link parseJSONValue} — see that\n* function's remarks for why this differs from the shallow\n* {@link parseRecordField} / {@link parseArrayField}.\n*\n* @param record - The source record\n* @param path - A property key, or a key path descending into nested objects\n* @returns The value, or `undefined`\n*/\nfunction parseJSONValueField(record, path) {\n\treturn parseJSONValue(resolveField(record, path));\n}\n/**\n* Parse a JSON string, returning `undefined` instead of throwing.\n*\n* @remarks\n* The safe boundary for untrusted JSON text: a malformed string yields\n* `undefined`, never an exception. Returns `unknown` — a successful parse proves\n* nothing about shape, so narrow the result with a guard (or use\n* {@link parseJSONAs}). A large document is not walked here; parsing is shallow\n* and lazy validation is the caller's to compose.\n*\n* @param value - The JSON string to parse\n* @returns The parsed value, or `undefined` when `value` is not valid JSON\n*/\nfunction parseJSON(value) {\n\ttry {\n\t\treturn JSON.parse(value);\n\t} catch {\n\t\treturn;\n\t}\n}\n/**\n* Parse a JSON string and validate the result against a guard.\n*\n* @remarks\n* The lazy, safe path from an untrusted string to a typed `T`: parse, then check\n* the parsed value with the guard you bring — typically one composed from the\n* combinators (`recordOf`, `arrayOf`, …). Only the shape the guard inspects is\n* validated, so a large document is never walked in full unless the guard does.\n*\n* @param value - The JSON string to parse\n* @param guard - The guard for the expected shape\n* @returns The parsed value when it satisfies `guard`, otherwise `undefined`\n*\n* @example\n* ```ts\n* const isConfig = recordOf({ host: isString, tags: arrayOf(isString) })\n* parseJSONAs('{\"host\":\"localhost\",\"tags\":[\"a\"]}', isConfig) // { host: 'localhost', tags: ['a'] }\n* parseJSONAs('{\"host\":\"localhost\"}', isConfig) // undefined — guard fails\n* parseJSONAs('not json', isConfig) // undefined — never throws\n* ```\n*/\nfunction parseJSONAs(value, guard) {\n\tconst parsed = parseJSON(value);\n\tif (parsed === void 0) return void 0;\n\treturn guard(parsed) ? parsed : void 0;\n}\n//#endregion\n//#region src/core/compilers.ts\n/**\n* Validate that a {@link ContractShape} tree is well-formed — a pure recursive\n* prepass run before compilation.\n*\n* @remarks\n* Fail-fast, per AGENTS §12: a malformed shape is a programmer error, so this\n* throws a plain `Error` immediately rather than surfacing as a silently-wrong\n* guard, parser, schema, or generator later. Checks, recursively:\n*\n* - An {@link OptionalShape} is only legal as a direct object-property value —\n* `optionalShape` wrapping an array item, a union variant, another\n* optional/nullable's inner shape, `additionalProperties`, or the top-level\n* shape all throw. An object property IS the one legal placement: its value\n* is unwrapped to `.inner` before recursing, so `.inner` itself is validated\n* as a normal (non-optional-wrapping) shape.\n* - A {@link UnionShape} needs at least one variant; a {@link LiteralShape}\n* needs at least one value and rejects non-finite (`NaN` / `Infinity` /\n* `-Infinity`) number values.\n* - A bounded {@link StringShape} / {@link NumberShape} / {@link ArrayShape}\n* needs `min <= max` when both are set.\n* - An integer {@link NumberShape} (`integer: true`) needs a non-empty integer\n* range: `Math.ceil(min ?? -Infinity) <= Math.floor(max ?? Infinity)`.\n* - `null` / `json` / `raw` / `boolean` are always-valid leaves. Recursion\n* continues into array items, object properties (and `additionalProperties`\n* when it is a shape), union variants, and optional/nullable inner shapes.\n*\n* @param shape - The shape to validate\n* @throws {Error} When the shape is malformed\n*/\nfunction validateShape(shape) {\n\tswitch (shape.type) {\n\t\tcase \"string\":\n\t\t\tif (shape.min !== void 0 && shape.max !== void 0 && shape.min > shape.max) throw new Error(\"validateShape: a string shape has min greater than max\");\n\t\t\treturn;\n\t\tcase \"number\":\n\t\t\tif (shape.min !== void 0 && shape.max !== void 0 && shape.min > shape.max) throw new Error(\"validateShape: a number shape has min greater than max\");\n\t\t\tif (shape.integer === true) {\n\t\t\t\tif (Math.ceil(shape.min ?? Number.NEGATIVE_INFINITY) > Math.floor(shape.max ?? Number.POSITIVE_INFINITY)) throw new Error(\"validateShape: an integer number shape has an empty integer range\");\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"boolean\":\n\t\tcase \"null\":\n\t\tcase \"json\":\n\t\tcase \"raw\": return;\n\t\tcase \"literal\":\n\t\t\tif (shape.values.length === 0) throw new Error(\"validateShape: a literal shape needs at least one value\");\n\t\t\tfor (const value of shape.values) if (typeof value === \"number\" && !Number.isFinite(value)) throw new Error(\"validateShape: a literal shape may not contain non-finite number values\");\n\t\t\treturn;\n\t\tcase \"array\":\n\t\t\tif (shape.min !== void 0 && shape.max !== void 0 && shape.min > shape.max) throw new Error(\"validateShape: an array shape has min greater than max\");\n\t\t\tvalidateShape(shape.items);\n\t\t\treturn;\n\t\tcase \"object\": {\n\t\t\tfor (const key of Object.keys(shape.properties)) {\n\t\t\t\tconst child = shape.properties[key];\n\t\t\t\tif (child === void 0) continue;\n\t\t\t\tvalidateShape(child.type === \"optional\" ? child.inner : child);\n\t\t\t}\n\t\t\tconst extra = shape.additionalProperties;\n\t\t\tif (extra !== void 0 && extra !== true && extra !== false) validateShape(extra);\n\t\t\treturn;\n\t\t}\n\t\tcase \"union\":\n\t\t\tif (shape.variants.length === 0) throw new Error(\"validateShape: a union shape needs at least one variant\");\n\t\t\tfor (const variant of shape.variants) validateShape(variant);\n\t\t\treturn;\n\t\tcase \"optional\": throw new Error(\"validateShape: an optional shape may only appear as a direct object-property value\");\n\t\tcase \"nullable\":\n\t\t\tvalidateShape(shape.inner);\n\t\t\treturn;\n\t}\n}\n/**\n* Compile a {@link ContractShape} into a JSON Schema document.\n*\n* @remarks\n* Object shapes emit `additionalProperties: false` (unless opened) and list only\n* required keys in `required`; nullable shapes emit an `anyOf` with `{ type:\n* 'null' }`. Emission only — it never inspects a runtime value.\n*\n* @param shape - The shape to compile\n* @returns The emitted JSON Schema\n*/\nfunction compileSchema(shape) {\n\tswitch (shape.type) {\n\t\tcase \"string\": return {\n\t\t\ttype: \"string\",\n\t\t\t...shape.min !== void 0 ? { minLength: shape.min } : {},\n\t\t\t...shape.max !== void 0 ? { maxLength: shape.max } : {},\n\t\t\t...shape.pattern !== void 0 ? { pattern: shape.pattern.source } : {},\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"number\": return {\n\t\t\ttype: shape.integer === true ? \"integer\" : \"number\",\n\t\t\t...shape.min !== void 0 ? { minimum: shape.min } : {},\n\t\t\t...shape.max !== void 0 ? { maximum: shape.max } : {},\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"boolean\": return {\n\t\t\ttype: \"boolean\",\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"null\": return {\n\t\t\ttype: \"null\",\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"json\": return { ...shape.description !== void 0 ? { description: shape.description } : {} };\n\t\tcase \"literal\": return {\n\t\t\tenum: [...shape.values],\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"array\": return {\n\t\t\ttype: \"array\",\n\t\t\titems: compileSchema(shape.items),\n\t\t\t...shape.min !== void 0 ? { minItems: shape.min } : {},\n\t\t\t...shape.max !== void 0 ? { maxItems: shape.max } : {},\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"object\": {\n\t\t\tconst properties = {};\n\t\t\tconst required = [];\n\t\t\tfor (const key of Object.keys(shape.properties)) {\n\t\t\t\tconst child = shape.properties[key];\n\t\t\t\tif (child === void 0) continue;\n\t\t\t\tproperties[key] = compileSchema(child);\n\t\t\t\tif (child.type !== \"optional\") required.push(key);\n\t\t\t}\n\t\t\tconst extra = shape.additionalProperties;\n\t\t\tconst additionalProperties = extra === true ? true : extra !== void 0 && extra !== false ? compileSchema(extra) : false;\n\t\t\treturn {\n\t\t\t\ttype: \"object\",\n\t\t\t\t...Object.keys(properties).length > 0 ? { properties } : {},\n\t\t\t\t...required.length > 0 ? { required } : {},\n\t\t\t\tadditionalProperties,\n\t\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t\t};\n\t\t}\n\t\tcase \"union\": return {\n\t\t\t...shape.mode === \"oneOf\" ? { oneOf: shape.variants.map((variant) => compileSchema(variant)) } : { anyOf: shape.variants.map((variant) => compileSchema(variant)) },\n\t\t\t...shape.description !== void 0 ? { description: shape.description } : {}\n\t\t};\n\t\tcase \"optional\": return compileSchema(shape.inner);\n\t\tcase \"nullable\": return { anyOf: [compileSchema(shape.inner), { type: \"null\" }] };\n\t\tcase \"raw\": return shape.schema;\n\t}\n}\n/**\n* Compile a {@link ContractShape} into a runtime type guard.\n*\n* @remarks\n* Reuses the combinators: `literalOf` for literals, `arrayOf` for arrays,\n* `recordOf` for closed objects, `unionOf` for unions, `nullableOf` for nullable,\n* and `whereOf` for constraint refinement. Like every guard it is total — it\n* never throws (AGENTS §14).\n*\n* @param shape - The shape to compile\n* @returns A guard narrowing to the shape's inferred type\n*/\nfunction compileGuard(shape) {\n\tswitch (shape.type) {\n\t\tcase \"string\": return stringOf({\n\t\t\tmin: shape.min,\n\t\t\tmax: shape.max,\n\t\t\tpattern: shape.pattern\n\t\t});\n\t\tcase \"number\": {\n\t\t\tconst base = shape.integer === true ? isInteger : isFiniteNumber;\n\t\t\tif (shape.min === void 0 && shape.max === void 0) return base;\n\t\t\treturn shape.integer === true ? intersectionOf(isInteger, boundsOf(shape.min, shape.max)) : boundsOf(shape.min, shape.max);\n\t\t}\n\t\tcase \"boolean\": return isBoolean;\n\t\tcase \"null\": return isNull;\n\t\tcase \"json\": return isJSONValue;\n\t\tcase \"literal\": return literalOf(...shape.values);\n\t\tcase \"array\": {\n\t\t\tconst base = arrayOf(compileGuard(shape.items));\n\t\t\tif (shape.min === void 0 && shape.max === void 0) return base;\n\t\t\tconst withinLength = boundsOf(shape.min, shape.max);\n\t\t\treturn whereOf(base, (value) => withinLength(value.length));\n\t\t}\n\t\tcase \"object\": {\n\t\t\tconst map = Object.create(null);\n\t\t\tconst optionalKeys = [];\n\t\t\tfor (const key of Object.keys(shape.properties)) {\n\t\t\t\tconst child = shape.properties[key];\n\t\t\t\tif (child === void 0) continue;\n\t\t\t\tif (child.type === \"optional\") {\n\t\t\t\t\tmap[key] = compileGuard(child.inner);\n\t\t\t\t\toptionalKeys.push(key);\n\t\t\t\t} else map[key] = compileGuard(child);\n\t\t\t}\n\t\t\tconst extra = shape.additionalProperties;\n\t\t\tif (extra === void 0 || extra === false) return optionalKeys.length > 0 ? recordOf(map, optionalKeys) : recordOf(map);\n\t\t\tconst additional = extra === true ? void 0 : compileGuard(extra);\n\t\t\tconst required = Object.keys(map).filter((key) => !optionalKeys.includes(key));\n\t\t\treturn (value) => {\n\t\t\t\tif (!isRecord(value)) return false;\n\t\t\t\tfor (const key of required) if (!Object.hasOwn(value, key)) return false;\n\t\t\t\tconst outcome = attempt(() => {\n\t\t\t\t\tfor (const key of Object.keys(value)) {\n\t\t\t\t\t\tconst guard = Object.hasOwn(map, key) ? map[key] : void 0;\n\t\t\t\t\t\tif (guard !== void 0) {\n\t\t\t\t\t\t\tif (!guard(value[key])) return false;\n\t\t\t\t\t\t} else if (additional !== void 0 && !additional(value[key])) return false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t});\n\t\t\t\treturn outcome.success && outcome.value;\n\t\t\t};\n\t\t}\n\t\tcase \"union\": return unionOf(...shape.variants.map((variant) => compileGuard(variant)));\n\t\tcase \"optional\": return orOf(isUndefined, compileGuard(shape.inner));\n\t\tcase \"nullable\": return nullableOf(compileGuard(shape.inner));\n\t\tcase \"raw\": return (_value) => true;\n\t}\n}\n/**\n* Compile a {@link ContractShape} into an input parser.\n*\n* @remarks\n* Reuses the leaf parsers (`parseString` / `parseInteger` / `parseNumber` /\n* `parseBoolean` / `parseRecord`) and coerces structurally. An object fails as a\n* whole on any required-field failure; a union returns a guard-valid value\n* unchanged, otherwise the first variant that both parses and guards wins.\n*\n* After coercing a leaf, it re-applies that leaf's REFINEMENTS through the same\n* combinators `compileGuard` uses — `stringOf` for a string's length/pattern and\n* `boundsOf` for a number's value and an array's length — so a value that coerces\n* but violates a bound parses to `undefined`. The result is full parse↔guard\n* soundness (AGENTS §14): a non-`undefined` parse always satisfies the contract's\n* `is`, refinements included.\n*\n* @param shape - The shape to compile\n* @returns A parser yielding the shape's inferred type or `undefined`\n*/\nfunction compileParser(shape) {\n\tswitch (shape.type) {\n\t\tcase \"string\": {\n\t\t\tif (shape.min === void 0 && shape.max === void 0 && shape.pattern === void 0) return parseString;\n\t\t\tconst guard = stringOf({\n\t\t\t\tmin: shape.min,\n\t\t\t\tmax: shape.max,\n\t\t\t\tpattern: shape.pattern\n\t\t\t});\n\t\t\treturn (value) => {\n\t\t\t\tconst parsed = parseString(value);\n\t\t\t\treturn parsed !== void 0 && guard(parsed) ? parsed : void 0;\n\t\t\t};\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst base = shape.integer === true ? parseInteger : parseNumber;\n\t\t\tif (shape.min === void 0 && shape.max === void 0) return base;\n\t\t\tconst within = boundsOf(shape.min, shape.max);\n\t\t\treturn (value) => {\n\t\t\t\tconst parsed = base(value);\n\t\t\t\treturn parsed !== void 0 && within(parsed) ? parsed : void 0;\n\t\t\t};\n\t\t}\n\t\tcase \"boolean\": return parseBoolean;\n\t\tcase \"null\": return (value) => value === null ? null : void 0;\n\t\tcase \"json\": return (value) => isJSONValue(value) ? value : void 0;\n\t\tcase \"literal\": {\n\t\t\tconst allowed = new Set(shape.values);\n\t\t\treturn (value) => {\n\t\t\t\tif (allowed.has(value)) return value;\n\t\t\t\tif (isString(value)) {\n\t\t\t\t\tconst trimmed = value.trim();\n\t\t\t\t\tif (allowed.has(trimmed)) return trimmed;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcase \"array\": {\n\t\t\tconst item = compileParser(shape.items);\n\t\t\tconst unbounded = shape.min === void 0 && shape.max === void 0;\n\t\t\tconst withinLength = boundsOf(shape.min, shape.max);\n\t\t\treturn (value) => {\n\t\t\t\tif (!isArray(value)) return void 0;\n\t\t\t\tconst result = [];\n\t\t\t\tfor (const entry of value) {\n\t\t\t\t\tconst parsed = item(entry);\n\t\t\t\t\tif (parsed === void 0) return void 0;\n\t\t\t\t\tresult.push(parsed);\n\t\t\t\t}\n\t\t\t\treturn unbounded || withinLength(result.length) ? result : void 0;\n\t\t\t};\n\t\t}\n\t\tcase \"object\": {\n\t\t\tconst entries = [];\n\t\t\tfor (const key of Object.keys(shape.properties)) {\n\t\t\t\tconst child = shape.properties[key];\n\t\t\t\tif (child === void 0) continue;\n\t\t\t\tconst optional = child.type === \"optional\";\n\t\t\t\tentries.push({\n\t\t\t\t\tkey,\n\t\t\t\t\tparse: compileParser(optional ? child.inner : child),\n\t\t\t\t\toptional\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst known = new Set(entries.map((entry) => entry.key));\n\t\t\tconst extra = shape.additionalProperties;\n\t\t\tconst additional = extra === void 0 || extra === false || extra === true ? void 0 : compileParser(extra);\n\t\t\tconst open = extra === true || additional !== void 0;\n\t\t\treturn (value) => {\n\t\t\t\tconst record = parseRecord(value);\n\t\t\t\tif (record === void 0) return void 0;\n\t\t\t\tconst outcome = attempt(() => {\n\t\t\t\t\tconst result = Object.create(null);\n\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\tconst raw = record[entry.key];\n\t\t\t\t\t\tif (raw === void 0) {\n\t\t\t\t\t\t\tif (entry.optional) continue;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst parsed = entry.parse(raw);\n\t\t\t\t\t\tif (parsed === void 0) return void 0;\n\t\t\t\t\t\tresult[entry.key] = parsed;\n\t\t\t\t\t}\n\t\t\t\t\tif (open) for (const key of Object.keys(record)) {\n\t\t\t\t\t\tif (known.has(key)) continue;\n\t\t\t\t\t\tif (additional === void 0) result[key] = record[key];\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tconst parsed = additional(record[key]);\n\t\t\t\t\t\t\tif (parsed === void 0) return void 0;\n\t\t\t\t\t\t\tresult[key] = parsed;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t});\n\t\t\t\treturn outcome.success ? outcome.value : void 0;\n\t\t\t};\n\t\t}\n\t\tcase \"union\": {\n\t\t\tconst variants = shape.variants.map((variant) => ({\n\t\t\t\tparse: compileParser(variant),\n\t\t\t\tguard: compileGuard(variant)\n\t\t\t}));\n\t\t\treturn (value) => {\n\t\t\t\tfor (const variant of variants) if (variant.guard(value)) return value;\n\t\t\t\tfor (const variant of variants) {\n\t\t\t\t\tconst parsed = variant.parse(value);\n\t\t\t\t\tif (parsed !== void 0 && variant.guard(parsed)) return parsed;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\tcase \"optional\": {\n\t\t\tconst inner = compileParser(shape.inner);\n\t\t\treturn (value) => value === void 0 ? void 0 : inner(value);\n\t\t}\n\t\tcase \"nullable\": {\n\t\t\tconst inner = compileParser(shape.inner);\n\t\t\treturn (value) => value === null ? null : inner(value);\n\t\t}\n\t\tcase \"raw\": return (value) => value;\n\t}\n}\n/**\n* Compile a {@link ContractShape} into a deterministic seed value.\n*\n* @remarks\n* The same shape and the same `random` source always produce the same value, so\n* seed data is reproducible. Defaults to a {@link seededRandom} source seeded\n* from the wall clock when none is supplied. Throws on a degenerate empty\n* `literalShape` / `unionShape`, on a pattern-constrained `stringShape` whose\n* generated sample cannot satisfy the pattern, or on a `rawShape` (its embedded\n* schema is arbitrary and cannot be auto-generated) — a programmer error that\n* cannot generate a value (AGENTS §12). `createContract` runs\n* {@link validateShape} first, so a degenerate `literalShape` / `unionShape` /\n* bounded shape is normally caught there; these throws remain here as defense\n* for standalone `compileGenerator` use.\n*\n* @param shape - The shape to generate from\n* @param random - A seeded random source (defaults to `seededRandom(Date.now())`)\n* @returns A value matching the shape\n*/\nfunction compileGenerator(shape, random = seededRandom(Date.now())) {\n\tswitch (shape.type) {\n\t\tcase \"string\": {\n\t\t\tconst min = shape.min ?? 0;\n\t\t\tconst max = shape.max ?? Math.max(min, 12);\n\t\t\tconst length = Math.max(min, Math.min(max, 8));\n\t\t\tconst alphabet = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n\t\t\tlet value = \"\";\n\t\t\tfor (let index = 0; index < length; index += 1) value += alphabet[Math.floor(random() * 36)];\n\t\t\tif (shape.pattern !== void 0 && !shape.pattern.test(value)) throw new Error(\"compileGenerator: a pattern-constrained string shape cannot be auto-generated — supply or verify values another way\");\n\t\t\treturn value;\n\t\t}\n\t\tcase \"number\": {\n\t\t\tconst min = shape.min ?? 0;\n\t\t\tconst max = shape.max ?? 100;\n\t\t\tif (shape.integer === true) {\n\t\t\t\tconst lo = Math.ceil(min);\n\t\t\t\tconst hi = Math.floor(max);\n\t\t\t\treturn Math.floor(random() * (hi - lo + 1)) + lo;\n\t\t\t}\n\t\t\treturn random() * (max - min) + min;\n\t\t}\n\t\tcase \"boolean\": return random() >= .5;\n\t\tcase \"null\": return null;\n\t\tcase \"json\": {\n\t\t\tconst pick = Math.floor(random() * 5);\n\t\t\tif (pick === 0) return null;\n\t\t\tif (pick === 1) return random() >= .5;\n\t\t\tif (pick === 2) return Math.floor(random() * 1e3);\n\t\t\tif (pick === 3) {\n\t\t\t\tconst alphabet = \"abcdefghijklmnopqrstuvwxyz\";\n\t\t\t\tlet value = \"\";\n\t\t\t\tfor (let index = 0; index < 6; index += 1) value += alphabet[Math.floor(random() * 26)];\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\treturn { value: Math.floor(random() * 1e3) };\n\t\t}\n\t\tcase \"literal\":\n\t\t\tif (shape.values.length === 0) throw new Error(\"compileGenerator: a literal shape needs at least one value\");\n\t\t\treturn shape.values[Math.floor(random() * shape.values.length)];\n\t\tcase \"array\": {\n\t\t\tconst lo = shape.min ?? Math.min(1, shape.max ?? 1);\n\t\t\tconst hi = shape.max ?? Math.max(lo, 3);\n\t\t\tconst length = Math.floor(random() * (hi - lo + 1)) + lo;\n\t\t\tconst result = [];\n\t\t\tfor (let index = 0; index < length; index += 1) result.push(compileGenerator(shape.items, random));\n\t\t\treturn result;\n\t\t}\n\t\tcase \"object\": {\n\t\t\tconst result = {};\n\t\t\tfor (const key of Object.keys(shape.properties)) {\n\t\t\t\tconst child = shape.properties[key];\n\t\t\t\tif (child === void 0) continue;\n\t\t\t\tif (child.type === \"optional\" && random() < .3) continue;\n\t\t\t\tresult[key] = compileGenerator(child, random);\n\t\t\t}\n\t\t\tconst extra = shape.additionalProperties;\n\t\t\tif (extra !== void 0 && extra !== true && extra !== false) {\n\t\t\t\tconst count = 1 + Math.floor(random() * 2);\n\t\t\t\tfor (let index = 0; index < count; index += 1) {\n\t\t\t\t\tconst key = `key${index}`;\n\t\t\t\t\tif (Object.hasOwn(result, key)) continue;\n\t\t\t\t\tresult[key] = compileGenerator(extra, random);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t\tcase \"union\":\n\t\t\tif (shape.variants.length === 0) throw new Error(\"compileGenerator: a union shape needs at least one variant\");\n\t\t\treturn compileGenerator(shape.variants[Math.floor(random() * shape.variants.length)], random);\n\t\tcase \"optional\": return compileGenerator(shape.inner, random);\n\t\tcase \"nullable\": return random() < .2 ? null : compileGenerator(shape.inner, random);\n\t\tcase \"raw\": throw new Error(\"compileGenerator: a raw shape embeds an arbitrary JSON Schema and cannot be auto-generated — supply values another way\");\n\t}\n}\nfunction createContract(shape) {\n\tvalidateShape(shape);\n\tconst schema = compileSchema(shape);\n\tconst guard = compileGuard(shape);\n\tconst parser = compileParser(shape);\n\treturn {\n\t\tschema,\n\t\tis: guard,\n\t\tparse(value) {\n\t\t\treturn parser(value);\n\t\t},\n\t\tgenerate(random) {\n\t\t\treturn compileGenerator(shape, random);\n\t\t}\n\t};\n}\n//#endregion\n//#region src/core/shapers.ts\n/**\n* Build a string {@link StringShape}.\n*\n* @param options - Optional length (`min` / `max`), `pattern`, and `description`\n* @returns A string shape\n*\n* @example\n* ```ts\n* const name = stringShape({ min: 1, max: 80, description: 'Display name' })\n* ```\n*/\nfunction stringShape(options) {\n\treturn {\n\t\ttype: \"string\",\n\t\tmin: options?.min,\n\t\tmax: options?.max,\n\t\tpattern: options?.pattern,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a numeric {@link NumberShape}.\n*\n* @param options - Optional bounds (`min` / `max`), `integer`, and `description`\n* @returns A number shape\n*/\nfunction numberShape(options) {\n\treturn {\n\t\ttype: \"number\",\n\t\tmin: options?.min,\n\t\tmax: options?.max,\n\t\tinteger: options?.integer,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build an integer {@link NumberShape} — forces `integer: true`.\n*\n* @remarks\n* The emitted JSON Schema uses `\"type\": \"integer\"` and the guard rejects\n* fractional numbers.\n*\n* @param options - Optional bounds and `description` (no `integer` key)\n* @returns An integer number shape\n*/\nfunction integerShape(options) {\n\treturn {\n\t\ttype: \"number\",\n\t\tinteger: true,\n\t\tmin: options?.min,\n\t\tmax: options?.max,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a {@link BooleanShape}.\n*\n* @param options - Optional `description`\n* @returns A boolean shape\n*/\nfunction booleanShape(options) {\n\treturn {\n\t\ttype: \"boolean\",\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a {@link NullShape}.\n*\n* @param options - Optional `description`\n* @returns A null shape\n*/\nfunction nullShape(options) {\n\treturn {\n\t\ttype: \"null\",\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a literal shape from a fixed set of primitive values.\n*\n* @param values - The permitted literals\n* @param options - Optional `description`\n* @returns A literal shape whose `Infer` is the union of `values`\n*\n* @example\n* ```ts\n* const role = literalShape(['admin', 'member', 'guest'])\n* // Infer<typeof role> = 'admin' | 'member' | 'guest'\n*\n* const via = literalShape(['function', 'tool', 'agent'], { description: 'How to run the step.' })\n* ```\n*/\nfunction literalShape(values, options) {\n\treturn {\n\t\ttype: \"literal\",\n\t\tvalues,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build an {@link ArrayShape} from an element shape.\n*\n* @param items - The element shape\n* @param options - Optional length bounds and `description`\n* @returns An array shape\n*\n* @example\n* ```ts\n* const tags = arrayShape(stringShape(), { max: 10 })\n* ```\n*/\nfunction arrayShape(items, options) {\n\treturn {\n\t\ttype: \"array\",\n\t\titems,\n\t\tmin: options?.min,\n\t\tmax: options?.max,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build an {@link ObjectShape} from a property map.\n*\n* @remarks\n* Wrap any property in {@link optionalShape} to allow its absence. By default\n* the compiled guard rejects unknown keys; pass `additionalProperties` to open\n* the object.\n*\n* @param properties - Map of property names to child shapes\n* @param options - Optional `additionalProperties` and `description`\n* @returns An object shape\n*\n* @example\n* ```ts\n* const user = objectShape({\n* \tname: stringShape({ min: 1 }),\n* \tage: integerShape({ min: 0, max: 120 }),\n* \tbio: optionalShape(stringShape()),\n* })\n* ```\n*/\nfunction objectShape(properties, options) {\n\treturn {\n\t\ttype: \"object\",\n\t\tproperties,\n\t\tadditionalProperties: options?.additionalProperties,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build an open {@link ObjectShape} with no fixed properties — a dictionary.\n*\n* @remarks\n* Every value is validated against `values`; keys are unconstrained. Equivalent\n* to `objectShape({}, { additionalProperties: values })`.\n*\n* @param values - The shape every value must match\n* @param options - Optional `description`\n* @returns An open object shape\n*\n* @example\n* ```ts\n* const bindings = recordShape(numberShape()) // ~ Record<string, number>\n* ```\n*/\nfunction recordShape(values, options) {\n\treturn {\n\t\ttype: \"object\",\n\t\tproperties: {},\n\t\tadditionalProperties: values,\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a {@link UnionShape} from a list of variant shapes (`anyOf` in JSON Schema).\n*\n* @param variants - The candidate shapes; the first match wins at runtime\n* @returns A union shape whose `Infer` is the union of the variants\n*\n* @example\n* ```ts\n* const id = unionShape(stringShape(), integerShape())\n* // Infer<typeof id> = string | number\n* ```\n*/\nfunction unionShape(...variants) {\n\treturn {\n\t\ttype: \"union\",\n\t\tvariants\n\t};\n}\n/**\n* Build a {@link UnionShape} that emits `oneOf` (exactly one match) in JSON Schema.\n*\n* @remarks\n* Runtime behavior is identical to {@link unionShape} — only the emitted schema\n* keyword differs (`oneOf` vs `anyOf`).\n*\n* @param variants - The candidate shapes\n* @returns A union shape with `mode: 'oneOf'`\n*/\nfunction oneOfShape(...variants) {\n\treturn {\n\t\ttype: \"union\",\n\t\tvariants,\n\t\tmode: \"oneOf\"\n\t};\n}\n/**\n* Wrap a shape so it may be absent (`undefined`).\n*\n* @remarks\n* As an {@link objectShape} property, the field becomes a true optional property\n* in the inferred type.\n*\n* @param inner - The wrapped shape\n* @returns An optional shape\n*/\nfunction optionalShape(inner) {\n\treturn {\n\t\ttype: \"optional\",\n\t\tinner\n\t};\n}\n/**\n* Wrap a shape so it may be `null`.\n*\n* @param inner - The wrapped shape\n* @returns A nullable shape\n*/\nfunction nullableShape(inner) {\n\treturn {\n\t\ttype: \"nullable\",\n\t\tinner\n\t};\n}\n/**\n* Build a {@link JSONShape}.\n*\n* @remarks\n* The sound counterpart of {@link rawShape}: `rawShape` embeds an arbitrary\n* schema fragment and accepts anything at runtime, while `jsonShape` validates\n* that a value is real JSON (via {@link isJSONValue}).\n*\n* @param options - Optional `description`\n* @returns A JSON passthrough shape\n*/\nfunction jsonShape(options) {\n\treturn {\n\t\ttype: \"json\",\n\t\tdescription: options?.description\n\t};\n}\n/**\n* Build a {@link RawShape} from a JSON Schema fragment.\n*\n* @remarks\n* For values the shape DSL can't express. The compiled guard accepts any value;\n* the parser passes it through; the schema is emitted verbatim.\n*\n* @param schema - The JSON Schema fragment to embed\n* @returns A raw shape\n*/\nfunction rawShape(schema) {\n\treturn {\n\t\ttype: \"raw\",\n\t\tschema\n\t};\n}\n//#endregion\nexport { JSON_SCHEMA_TYPES, andOf, arrayOf, arrayShape, attempt, booleanShape, boundsOf, compileGenerator, compileGuard, compileParser, compileSchema, complementOf, createContract, enumOf, enumerableSymbolCount, instanceOf, integerShape, intersectionOf, isArray, isArrayBuffer, isArrayBufferView, isAsyncFunction, isAsyncGeneratorFunction, isAsyncIterable, isBigInt, isBigInt64Array, isBigUint64Array, isBoolean, isConstructor, isDataView, isDate, isDefined, isEmptyArray, isEmptyMap, isEmptyObject, isEmptySet, isEmptyString, isError, isFalse, isFiniteNumber, isFloat32Array, isFloat64Array, isFunction, isGeneratorFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isJSONPrimitive, isJSONValue, isMap, isNonEmptyArray, isNonEmptyMap, isNonEmptyObject, isNonEmptySet, isNonEmptyString, isNull, isNullableBoolean, isNullableNumber, isNullableString, isNumber, isObject, isPromise, isPromiseLike, isRecord, isRegExp, isSet, isSharedArrayBuffer, isString, isSymbol, isTrue, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isZeroArg, isZeroArgAsync, isZeroArgAsyncGenerator, isZeroArgGenerator, iterableOf, jsonShape, keyOf, lazyOf, literalOf, literalShape, mapOf, matchOf, notOf, nullShape, nullableOf, nullableShape, numberShape, objectShape, omitOf, oneOfShape, optionalOf, optionalShape, orOf, parseArray, parseArrayField, parseBoolean, parseBooleanField, parseEnum, parseEnumField, parseInteger, parseIntegerField, parseJSON, parseJSONAs, parseJSONValue, parseJSONValueField, parseNull, parseNullField, parseNumber, parseNumberField, parseRecord, parseRecordField, parseString, parseStringField, pickOf, rawShape, recordOf, recordShape, resolveField, schemaToParameters, seededRandom, setOf, stringOf, stringShape, transformOf, tupleOf, unionOf, unionShape, validateShape, whereOf };\n\n//# sourceMappingURL=index.js.map","import type { GroupInterface, RouteEntry, RouterInterface } from './types.js'\nimport { joinPaths } from './helpers.js'\n\n/**\n * A prefix-scoped registration handle over a {@link import('./Router.js').Router} —\n * pure string composition (AGENTS §4.2.2), no independent state or storage.\n *\n * @typeParam Meta - The entry payload type, matching the owning router\n *\n * @remarks\n * Every `add` composes `entry.path` as `joinPaths(prefix, entry.path)` and\n * forwards to the OWNING router, so grouped routes land in the SAME registry.\n * `group(prefix)` nests, composing prefixes via {@link joinPaths}.\n *\n * @example\n * ```ts\n * import { Router } from '@src/core'\n *\n * const router = new Router<{ readonly page: string }>()\n * const api = router.group('/api')\n * api.add({ path: '/users', meta: { page: 'list' } })\n * router.match('/api/users')?.path // '/api/users'\n * ```\n */\nexport class Group<Meta> implements GroupInterface<Meta> {\n\treadonly prefix: string\n\treadonly #parent: RouterInterface<Meta>\n\n\tconstructor(parent: RouterInterface<Meta>, prefix: string) {\n\t\tthis.#parent = parent\n\t\tthis.prefix = prefix\n\t}\n\n\tadd(entry: RouteEntry<Meta>): void\n\tadd(entries: readonly RouteEntry<Meta>[]): void\n\tadd(input: RouteEntry<Meta> | readonly RouteEntry<Meta>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tthis.#parent.add(\n\t\t\tinputs.map((entry) => ({ ...entry, path: joinPaths(this.prefix, entry.path) })),\n\t\t)\n\t}\n\n\tgroup(prefix: string): GroupInterface<Meta> {\n\t\treturn new Group<Meta>(this.#parent, joinPaths(this.prefix, prefix))\n\t}\n}\n","import type {\n\tAnswerHandler,\n\tCompiledPath,\n\tGroupInterface,\n\tRouteEntry,\n\tRouterInterface,\n\tRouterMatch,\n\tRouterOptions,\n} from './types.js'\nimport { isString } from '@orkestrel/contract'\nimport { compareSpecificity, compilePath, matchPath } from './helpers.js'\nimport { Group } from './Group.js'\n\n/**\n * The path-matching + registry engine — registers `{ path, meta, name? }`\n * entries (compiling each path once) and resolves a concrete pathname to the\n * MOST SPECIFIC matching entry. The shared machine both the `Navigator`\n * (browser) and the `Dispatcher` (core, method-dimensioned) compose.\n *\n * @typeParam Meta - The opaque payload each entry carries and a match returns\n *\n * @remarks\n * - **Registration boundary guard (§14).** `add` validates each entry's\n * `path` — `isString` plus a leading `/` — and throws `TypeError` on a\n * malformed registration; `match` stays guard-free (the hot path).\n * - **Compile-once.** Each path is compiled exactly once at registration into\n * a parallel `#compiled` array, so `match` runs only a cached `exec` per\n * candidate.\n * - **Dedup via `key`.** When `options.key` is set, an entry whose computed\n * key already exists REPLACES the prior one IN PLACE (both the `#entries`\n * and `#compiled` arrays, at the existing index) — last write wins, no\n * engine rebuild. Omitted ⇒ every entry is kept, even duplicate paths.\n * - **Groups.** `group(prefix)` returns a {@link GroupInterface} that composes\n * `prefix` onto every entry it registers, nesting via {@link joinPaths}.\n *\n * @example\n * ```ts\n * const router = new Router<{ readonly page: string }>()\n * router.add({ path: '/users/:id', meta: { page: 'profile' } })\n * router.match('/users/7') // { path: '/users/:id', params: { id: '7' }, meta: { page: 'profile' } }\n * ```\n */\nexport class Router<Meta> implements RouterInterface<Meta> {\n\treadonly #entries: RouteEntry<Meta>[] = []\n\treadonly #compiled: CompiledPath[] = []\n\treadonly #sensitive: boolean\n\treadonly #key: ((entry: RouteEntry<Meta>) => string) | undefined\n\treadonly #index: Map<string, number> = new Map()\n\n\tconstructor(options?: RouterOptions<Meta>) {\n\t\tthis.#sensitive = options?.sensitive ?? true\n\t\tthis.#key = options?.key\n\t\tif (options?.entries !== undefined) this.add(options.entries)\n\t}\n\n\tget count(): number {\n\t\treturn this.#entries.length\n\t}\n\n\tadd(entry: RouteEntry<Meta>): void\n\tadd(entries: readonly RouteEntry<Meta>[]): void\n\tadd(input: RouteEntry<Meta> | readonly RouteEntry<Meta>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tfor (const entry of inputs) this.#register(entry)\n\t}\n\n\tmatch(pathname: string, answers?: AnswerHandler<Meta>): RouterMatch<Meta> | undefined {\n\t\tlet best: { entry: RouteEntry<Meta>; params: Readonly<Record<string, string>> } | undefined\n\t\tfor (let index = 0; index < this.#entries.length; index += 1) {\n\t\t\tconst entry = this.#entries[index]\n\t\t\tconst compiled = this.#compiled[index]\n\t\t\tif (entry === undefined || compiled === undefined) continue\n\t\t\tif (answers !== undefined && !answers(entry.meta)) continue\n\t\t\tconst params = matchPath(compiled, pathname)\n\t\t\tif (params === undefined) continue\n\t\t\tif (best === undefined || compareSpecificity(entry.path, best.entry.path) < 0)\n\t\t\t\tbest = { entry, params }\n\t\t}\n\t\tif (best === undefined) return undefined\n\t\treturn {\n\t\t\tpath: best.entry.path,\n\t\t\tparams: best.params,\n\t\t\tmeta: best.entry.meta,\n\t\t\tname: best.entry.name,\n\t\t}\n\t}\n\n\tentries(): readonly RouteEntry<Meta>[]\n\tentries(pathname: string): readonly RouteEntry<Meta>[]\n\tentries(pathname?: string): readonly RouteEntry<Meta>[] {\n\t\tif (pathname === undefined) return [...this.#entries]\n\t\tconst out: RouteEntry<Meta>[] = []\n\t\tfor (let index = 0; index < this.#entries.length; index += 1) {\n\t\t\tconst entry = this.#entries[index]\n\t\t\tconst compiled = this.#compiled[index]\n\t\t\tif (entry === undefined || compiled === undefined) continue\n\t\t\tif (matchPath(compiled, pathname) !== undefined) out.push(entry)\n\t\t}\n\t\treturn out\n\t}\n\n\tgroup(prefix: string): GroupInterface<Meta> {\n\t\treturn new Group<Meta>(this, prefix)\n\t}\n\n\tclear(): void {\n\t\tthis.#entries.length = 0\n\t\tthis.#compiled.length = 0\n\t\tthis.#index.clear()\n\t}\n\n\t// Validate the registration boundary (§14: isString + leading '/'), then either replace an\n\t// existing entry IN PLACE (dedup via `#key`, last write wins) or append a new one — the\n\t// engine's compile-once invariant, kept in sync across the `#entries`/`#compiled` pair.\n\t#register(entry: RouteEntry<Meta>): void {\n\t\tif (!isString(entry.path) || !entry.path.startsWith('/'))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route path must be a string starting with \"/\", got ${JSON.stringify(entry.path)}`,\n\t\t\t)\n\t\tconst compiled = compilePath(entry.path, this.#sensitive)\n\t\tif (this.#key === undefined) {\n\t\t\tthis.#entries.push(entry)\n\t\t\tthis.#compiled.push(compiled)\n\t\t\treturn\n\t\t}\n\t\tconst key = this.#key(entry)\n\t\tconst existing = this.#index.get(key)\n\t\tif (existing !== undefined) {\n\t\t\tthis.#entries[existing] = entry\n\t\t\tthis.#compiled[existing] = compiled\n\t\t\treturn\n\t\t}\n\t\tthis.#index.set(key, this.#entries.length)\n\t\tthis.#entries.push(entry)\n\t\tthis.#compiled.push(compiled)\n\t}\n}\n","import type { DispatchGroupInterface, DispatcherInterface, RouteInput } from './types.js'\nimport { joinPaths } from './helpers.js'\n\n/**\n * A prefix-scoped registration handle over a\n * {@link import('./Dispatcher.js').Dispatcher} — the method-dimensioned\n * counterpart of `Group` (`Group.ts`).\n *\n * @typeParam TState - The consumer's opaque per-request state type, matching\n * the owning dispatcher\n *\n * @remarks\n * Every `add` composes `input.path` via {@link joinPaths} against\n * `this.prefix` and forwards to the OWNING dispatcher's `add` (its own §14\n * boundary guard still applies). Pure string composition (§4.2.2) — no\n * independent state or storage.\n *\n * @example\n * ```ts\n * import { Dispatcher } from '@src/core'\n *\n * const dispatcher = new Dispatcher()\n * const api = dispatcher.group('/api')\n * api.add({ method: 'GET', path: '/users', handler: () => new Response('ok') })\n * ```\n */\nexport class DispatchGroup<TState> implements DispatchGroupInterface<TState> {\n\treadonly prefix: string\n\treadonly #parent: DispatcherInterface<TState>\n\n\tconstructor(parent: DispatcherInterface<TState>, prefix: string) {\n\t\tthis.#parent = parent\n\t\tthis.prefix = prefix\n\t}\n\n\tadd<Path extends string>(input: RouteInput<Path, TState>): void\n\tadd(inputs: readonly RouteInput<string, TState>[]): void\n\tadd(input: RouteInput<string, TState> | readonly RouteInput<string, TState>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tthis.#parent.add(\n\t\t\tinputs.map((route) => ({ ...route, path: joinPaths(this.prefix, route.path) })),\n\t\t)\n\t}\n\n\tgroup(prefix: string): DispatchGroupInterface<TState> {\n\t\treturn new DispatchGroup<TState>(this.#parent, joinPaths(this.prefix, prefix))\n\t}\n}\n","import type {\n\tDispatchGroupInterface,\n\tDispatcherEventMap,\n\tDispatcherInterface,\n\tDispatcherOptions,\n\tDispatchResult,\n\tMethod,\n\tRouteContext,\n\tRouteEntry,\n\tRouteInput,\n\tRouteRecord,\n\tRouterInterface,\n\tRouterMatch,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isFunction, isString } from '@orkestrel/contract'\nimport { METHODS } from './constants.js'\nimport { canonicalizePath, parseMethod } from './helpers.js'\nimport { Router } from './Router.js'\nimport { DispatchGroup } from './DispatchGroup.js'\n\n/**\n * The fetch-standard, method-dimensioned dispatch entity — layers HTTP method\n * dispatch and web-standard `Request`/`Response` handling over one internal\n * `Router<RouteRecord<TState>>`. The core machine the eventual server face\n * (§7) and any fetch-native runtime consumes directly.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n *\n * @remarks\n * - **Dedup by `method + canonicalizePath`.** The underlying `Router` is\n * constructed with a `key` function so registering the same method+path\n * twice REPLACES the prior route in place (§5.1).\n * - **Registration boundary guard (§14).** `add` validates each input's\n * `handler` (`isFunction`) and `method` (must be in {@link METHODS}) —\n * throws `TypeError` on a malformed registration; path validation is\n * delegated to the underlying `Router`'s own guard. `match`/`handle` stay\n * guard-free.\n * - **Auto-`HEAD` / auto-`OPTIONS`.** A `HEAD` request with no registered\n * `HEAD` route runs the matching `GET` handler and strips the response\n * body; an `OPTIONS` request with no registered `OPTIONS` route answers\n * `204` with a derived `Allow` header.\n * - **Handler throws propagate.** `handle` never invents an error boundary —\n * a handler throw reaches the caller uncaught (§5.1).\n * - **Emitter (§13).** Owns a `#emitter` for {@link DispatcherEventMap};\n * `match`/`miss` fire AFTER resolution, before the handler/responder runs.\n *\n * @example\n * ```ts\n * const dispatcher = new Dispatcher<{ readonly userId: string }>()\n * dispatcher.add({\n * \tmethod: 'GET',\n * \tpath: '/users/:id',\n * \thandler: (request, context) => Response.json({ id: context.params.id }),\n * })\n * const response = await dispatcher.handle(new Request('http://x/users/7'), { userId: 'me' })\n * ```\n */\nexport class Dispatcher<TState = undefined> implements DispatcherInterface<TState> {\n\treadonly router: RouterInterface<RouteRecord<TState>>\n\treadonly #emitter: Emitter<DispatcherEventMap>\n\treadonly #unmatched: (request: Request) => Response | Promise<Response>\n\treadonly #unmethoded: (request: Request, allow: readonly Method[]) => Response | Promise<Response>\n\n\tconstructor(options?: DispatcherOptions<TState>) {\n\t\tthis.router = new Router<RouteRecord<TState>>({\n\t\t\tsensitive: options?.sensitive,\n\t\t\tkey: (entry) => `${entry.meta.method} ${canonicalizePath(entry.path)}`,\n\t\t})\n\t\tthis.#emitter = new Emitter<DispatcherEventMap>({ on: options?.on, error: options?.error })\n\t\tthis.#unmatched =\n\t\t\toptions?.unmatched ?? ((_request) => new Response('Not Found', { status: 404 }))\n\t\tthis.#unmethoded =\n\t\t\toptions?.unmethoded ??\n\t\t\t((_request, allow) =>\n\t\t\t\tnew Response('Method Not Allowed', {\n\t\t\t\t\tstatus: 405,\n\t\t\t\t\theaders: { Allow: allow.join(', ') },\n\t\t\t\t}))\n\t\tif (options?.routes !== undefined) this.add(options.routes)\n\t}\n\n\tget emitter(): EmitterInterface<DispatcherEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tadd<Path extends string>(input: RouteInput<Path, TState>): void\n\tadd(inputs: readonly RouteInput<string, TState>[]): void\n\tadd(input: RouteInput<string, TState> | readonly RouteInput<string, TState>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tfor (const route of inputs) this.#register(route)\n\t}\n\n\tgroup(prefix: string): DispatchGroupInterface<TState> {\n\t\treturn new DispatchGroup<TState>(this, prefix)\n\t}\n\n\tmatch(method: Method, pathname: string): DispatchResult<TState> {\n\t\tconst hit = this.router.match(pathname, (meta) => meta.method === method)\n\t\tif (hit !== undefined) return { status: 'matched', match: hit }\n\t\tif (method === 'HEAD') {\n\t\t\tconst getHit = this.router.match(pathname, (meta) => meta.method === 'GET')\n\t\t\tif (getHit !== undefined) return { status: 'matched', match: getHit }\n\t\t}\n\t\tconst allow = this.#allow(pathname)\n\t\tif (allow.length === 0) return { status: 'unmatched' }\n\t\treturn { status: 'unmethoded', allow }\n\t}\n\n\tasync handle(request: Request, state: TState): Promise<Response> {\n\t\tconst url = new URL(request.url)\n\t\tconst pathname = url.pathname\n\t\tconst requested = request.method\n\t\tconst method = parseMethod(requested)\n\t\tif (method === undefined) {\n\t\t\tconst allow = this.#allow(pathname)\n\t\t\tif (allow.length === 0) {\n\t\t\t\tthis.#emitter.emit('miss', requested, pathname, 'unmatched')\n\t\t\t\treturn this.#unmatched(request)\n\t\t\t}\n\t\t\tthis.#emitter.emit('miss', requested, pathname, 'unmethoded')\n\t\t\treturn this.#unmethoded(request, allow)\n\t\t}\n\t\tconst result = this.match(method, pathname)\n\t\tif (result.status === 'matched')\n\t\t\treturn this.#respondMatched(request, state, method, result.match, url)\n\t\tif (result.status === 'unmethoded') {\n\t\t\tif (method === 'OPTIONS') return this.#respondAutoOptions(pathname, result.allow)\n\t\t\tthis.#emitter.emit('miss', method, pathname, 'unmethoded')\n\t\t\treturn this.#unmethoded(request, result.allow)\n\t\t}\n\t\tthis.#emitter.emit('miss', method, pathname, 'unmatched')\n\t\treturn this.#unmatched(request)\n\t}\n\n\tdestroy(): void {\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// Validate the registration boundary (§14: handler function, known method), then delegate\n\t// path validation + dedup to the underlying `Router`'s own guard.\n\t#register(input: RouteInput<string, TState>): void {\n\t\tif (!isFunction(input.handler))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route handler must be a function, got ${JSON.stringify(input.handler)}`,\n\t\t\t)\n\t\tif (!isString(input.method) || !METHODS.has(input.method))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route method must be one of ${[...METHODS].join(', ')}, got ${JSON.stringify(input.method)}`,\n\t\t\t)\n\t\tthis.router.add({\n\t\t\tpath: input.path,\n\t\t\tname: input.name,\n\t\t\tmeta: { method: input.method, handler: input.handler, name: input.name },\n\t\t})\n\t}\n\n\t// The derived `Allow` set for a pathname — every distinct registered method, with `HEAD`\n\t// added whenever `GET` is present and `HEAD` is not explicitly registered (§5.1).\n\t#allow(pathname: string): readonly Method[] {\n\t\tconst entries: readonly RouteEntry<RouteRecord<TState>>[] = this.router.entries(pathname)\n\t\tconst methods = new Set<Method>()\n\t\tfor (const entry of entries) methods.add(entry.meta.method)\n\t\tif (methods.has('GET')) methods.add('HEAD')\n\t\treturn [...methods]\n\t}\n\n\t// A matched dispatch — either the winning handler runs directly, or (for a derived `HEAD`\n\t// with no explicit `HEAD` route) the `GET` handler runs and the response body is stripped.\n\tasync #respondMatched(\n\t\trequest: Request,\n\t\tstate: TState,\n\t\tmethod: Method,\n\t\tmatch: RouterMatch<RouteRecord<TState>>,\n\t\turl: URL,\n\t): Promise<Response> {\n\t\tthis.#emitter.emit('match', method, match.path)\n\t\tconst context: RouteContext<string, TState> = {\n\t\t\tparams: match.params,\n\t\t\tpattern: match.path,\n\t\t\turl,\n\t\t\tstate,\n\t\t}\n\t\tconst response = await match.meta.handler(request, context)\n\t\tif (method === 'HEAD' && match.meta.method === 'GET')\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\theaders: response.headers,\n\t\t\t})\n\t\treturn response\n\t}\n\n\t// Derived `OPTIONS` — no explicit `OPTIONS` route registered for this pathname: answer\n\t// `204` with the derived `Allow` set (adding `OPTIONS` itself, always answerable).\n\t#respondAutoOptions(pathname: string, allow: readonly Method[]): Response {\n\t\tthis.#emitter.emit('match', 'OPTIONS', pathname)\n\t\tconst headers = new Headers({ Allow: [...allow, 'OPTIONS'].join(', ') })\n\t\treturn new Response(null, { status: 204, headers })\n\t}\n}\n","import type {\n\tDispatcherInterface,\n\tDispatcherOptions,\n\tRouterInterface,\n\tRouterOptions,\n} from './types.js'\nimport { Dispatcher } from './Dispatcher.js'\nimport { Router } from './Router.js'\n\n/**\n * Create a {@link RouterInterface} — the pure path-matching + registry engine\n * shared by the browser `Navigator` and the core `Dispatcher`.\n *\n * @remarks\n * Prefer this over `new Router(...)` at call sites that only need the\n * interface; an entity that OWNS a `Router` internally (like `Dispatcher`)\n * still constructs `new Router(...)` directly.\n *\n * @typeParam Meta - The opaque payload each entry carries and a match returns\n * @param options - Optional initial `entries`, the `sensitive` case toggle\n * (default `true`), and a `key` dedup identity function\n * @returns A {@link RouterInterface}\n *\n * @example\n * ```ts\n * import { createRouter } from '@src/core'\n *\n * const router = createRouter<{ readonly page: string }>()\n * router.add({ path: '/users/:id', meta: { page: 'profile' } })\n * router.match('/users/7') // { path: '/users/:id', params: { id: '7' }, meta: { page: 'profile' } }\n * ```\n */\nexport function createRouter<Meta>(options?: RouterOptions<Meta>): RouterInterface<Meta> {\n\treturn new Router<Meta>(options)\n}\n\n/**\n * Create a {@link DispatcherInterface} — the fetch-standard, method-\n * dimensioned dispatch entity over one internal `Router<RouteRecord<TState>>`.\n *\n * @remarks\n * Prefer this over `new Dispatcher(...)` at call sites that only need the\n * interface.\n *\n * @typeParam TState - The consumer's opaque per-request state type (default\n * `undefined` for stateless use)\n * @param options - Optional initial `routes`, the `sensitive` case toggle,\n * the `unmatched`/`unmethoded` default-responder overrides, and the AGENTS\n * §13 emitter `on`/`error` wiring\n * @returns A {@link DispatcherInterface}\n *\n * @example\n * ```ts\n * import { createDispatcher } from '@src/core'\n *\n * const dispatcher = createDispatcher<{ readonly userId: string }>({\n * \troutes: [\n * \t\t{ method: 'GET', path: '/health', handler: () => new Response('ok') },\n * \t],\n * })\n * const response = await dispatcher.handle(new Request('http://x/health'), { userId: 'me' })\n * ```\n */\nexport function createDispatcher<TState = undefined>(\n\toptions?: DispatcherOptions<TState>,\n): DispatcherInterface<TState> {\n\treturn new Dispatcher<TState>(options)\n}\n"],"x_google_ignoreList":[2,3],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#parent","#entries","#compiled","#sensitive","#key","#index","#register","#parent","#emitter","#unmatched","#unmethoded","#register","#allow","#respondMatched","#respondAutoOptions"],"sources":["../../../src/core/constants.ts","../../../src/core/helpers.ts","../../../src/core/Group.ts","../../../src/core/Router.ts","../../../src/core/DispatchGroup.ts","../../../src/core/Dispatcher.ts","../../../src/core/factories.ts"],"sourcesContent":["// ============================================================================\n// Core constants — the §5 centralized home for module-scope data used by the\n// matching engine and the fetch dispatcher. Every declaration here is frozen\n// and `export`ed per AGENTS §5.\n// ============================================================================\n\n/**\n * The complete set of HTTP methods a {@link import('./types.js').Dispatcher}\n * registers routes under — backs the registration guard (`add` rejects any\n * `method` outside this set) and the auto-`OPTIONS` `Allow` derivation.\n *\n * @remarks\n * A `ReadonlySet` of the seven {@link import('./types.js').Method} literals:\n * `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`. `HEAD` is\n * included even though it is never required at registration (a `GET` route\n * auto-answers `HEAD`) — it is still a valid method to register explicitly.\n *\n * @example\n * ```ts\n * METHODS.has('GET') // true\n * METHODS.has('TRACE') // false\n * ```\n */\nexport const METHODS: ReadonlySet<string> = Object.freeze(\n\tnew Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']),\n)\n\n/**\n * Specificity tier for a **literal** path segment (`/users`) — the highest\n * tier, always outranking a param or wildcard segment at the same position.\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`) when ranking candidate\n * matches left-to-right at the earliest differing segment (§4 precedence).\n *\n * @example\n * ```ts\n * TIER_LITERAL > TIER_PARAM // true\n * ```\n */\nexport const TIER_LITERAL = 2\n\n/**\n * Specificity tier for a **param** path segment (`:name`) — ranks below a\n * literal segment and above a wildcard segment at the same position.\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`) alongside {@link TIER_LITERAL}\n * and {@link TIER_WILDCARD}.\n *\n * @example\n * ```ts\n * TIER_PARAM > TIER_WILDCARD // true\n * ```\n */\nexport const TIER_PARAM = 1\n\n/**\n * Specificity tier for a **wildcard** path segment (`*name`) — the lowest\n * tier; a wildcard only ever wins against another wildcard shape (an\n * equal-specificity tie resolved by registration order).\n *\n * @remarks\n * Consumed by `computeSpecificity` (U1 `helpers.ts`).\n *\n * @example\n * ```ts\n * TIER_WILDCARD // 0\n * ```\n */\nexport const TIER_WILDCARD = 0\n","import type { CompiledPath, Method, RouteInput } from './types.js'\nimport { TIER_LITERAL, TIER_PARAM, TIER_WILDCARD } from './constants.js'\n\n// The PURE path-matching primitives (AGENTS §4.3 multi-word names — module scope,\n// no entity context). Every one is exported (the centralized-file rule, §5): a\n// consumer composes them directly, or reaches them through the `Router` engine.\n// They speak ONLY `string` / `RegExp` / `Record` and `decodeURIComponent` (a\n// platform global valid in Node and the browser alike) — NO DOM, NO `node:*` — so\n// the SAME engine drives a method-dimensioned server dispatcher and a method-less\n// browser navigator.\n\n/**\n * Escape every regex metacharacter in a literal string so it can be embedded\n * inside a larger `RegExp` source without being interpreted as syntax.\n *\n * @remarks\n * {@link compilePath} escapes the literal segments of a route pattern with this\n * before splicing in `:name` / `*name` capture groups, so a path like\n * `/files/:name.json` matches the `.` literally rather than as \"any character\".\n * Pure and total — never throws.\n *\n * @param value - The literal string to escape\n * @returns `value` with every regex metacharacter backslash-escaped\n *\n * @example\n * ```ts\n * escapeRegExp('a.b+c') // 'a\\\\.b\\\\+c'\n * new RegExp(`^${escapeRegExp('a.b')}$`).test('a.b') // true\n * new RegExp(`^${escapeRegExp('a.b')}$`).test('axb') // false\n * ```\n */\nexport function escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Canonicalize a route path for REGISTRY IDENTITY — strip a single trailing\n * slash, except the root `/` (and the empty pattern). The trailing-slash fold\n * {@link compilePath} normalizes a pattern through, so identity agrees with the\n * matcher.\n *\n * @remarks\n * Mirrors {@link compilePath}'s trailing-slash folding: `/users/` canonicalizes\n * to `/users` (the two compile to the same regex and match the same\n * pathnames), while the root `/` and the empty `''` are EXEMPT (a bare `/`\n * already matches `/`; stripping it would break that). Pure and total — a path\n * without a trailing slash returns unchanged.\n *\n * @param path - The route path pattern\n * @returns The canonical path (one trailing slash removed, except `/` and `''`)\n *\n * @example\n * ```ts\n * canonicalizePath('/users/') // '/users'\n * canonicalizePath('/users') // '/users'\n * canonicalizePath('/') // '/'\n * canonicalizePath('') // ''\n * ```\n */\nexport function canonicalizePath(path: string): string {\n\treturn path.length > 1 && path.endsWith('/') ? path.slice(0, -1) : path\n}\n\n/**\n * Compile a route path pattern into an anchored regex and its ordered param\n * names.\n *\n * @remarks\n * Splits the CANONICALIZED path into segments. Each `:name` segment becomes a\n * `([^/]+)` capture group; the FINAL segment may instead be `*name`, which\n * becomes a `(.+)` capture spanning the REST of the path including slashes — a\n * wildcard segment anywhere but last is a registration-time programmer error\n * and throws `TypeError` (§14 construction/registration boundary). Every regex\n * metacharacter in a literal segment is escaped first ({@link escapeRegExp}),\n * so a path like `/files/:name.json` matches the `.` literally apart from the\n * param. The regex is anchored (`^…$`), so it matches the whole pathname, not\n * a prefix.\n *\n * **Trailing slash is INSENSITIVE** (Express's `strict: false` default): a\n * single trailing slash on the request path is OPTIONAL, so `/users` matches\n * both `/users` and `/users/`, and `/users/:id` matches both `/users/me` and\n * `/users/me/`. This is NOT prefix matching — a deeper path is still a\n * distinct segment, so `/api` does not match `/api/users`. The ROOT `/` (and\n * the empty pattern `''`) are EXEMPT — they are not stripped, so `/` stays\n * `^/$` and `''` stays `^$`.\n *\n * `sensitive` (default `true`) controls case folding: `false` adds the `i`\n * regex flag, so `/Users` matches `/users`. The pattern's own casing is never\n * altered — only the matching behavior.\n *\n * @param path - The route path pattern (e.g. `/users/:id`, `/files/*rest`)\n * @param sensitive - Case-sensitive matching (default `true`)\n * @returns The {@link CompiledPath} — its `regex` + ordered `params`\n * @throws {TypeError} When a `*name` wildcard segment is not the FINAL segment\n *\n * @example\n * ```ts\n * const { regex, params } = compilePath('/users/:id/posts/:slug')\n * params // ['id', 'slug']\n * regex.exec('/users/7/posts/hello') // ['…', '7', 'hello']\n * regex.test('/users/7/posts/hello/') // true — the trailing slash is optional\n *\n * compilePath('/files/*rest').regex.test('/files/a/b.png') // true\n * compilePath('/Users', false).regex.test('/users') // true — case-insensitive\n * ```\n */\nexport function compilePath(path: string, sensitive = true): CompiledPath {\n\tconst params: string[] = []\n\t// Normalize ONE trailing slash off the pattern so `/users/` compiles like `/users`\n\t// — except the root `/` (and the empty pattern), which must keep matching `/` / `''`.\n\tconst normalized = canonicalizePath(path)\n\tconst segments = normalized.split('/')\n\tconst compiledSegments = segments.map((segment, index) => {\n\t\tconst isFinal = index === segments.length - 1\n\t\t// A wildcard-shaped segment head (`*name`) is only valid as the FINAL segment — a\n\t\t// registration-time programmer error anywhere else (§14 boundary guard).\n\t\tif (!isFinal && /^\\*[A-Za-z_]\\w*/.test(segment))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a wildcard segment (\"${segment}\") must be the final segment of a path pattern, got \"${path}\"`,\n\t\t\t)\n\t\t// Classification and compilation share ONE segment parser (§4 fix) — the tier\n\t\t// {@link classifySegment} assigns is exactly the shape compiled here.\n\t\tconst tier = classifySegment(segment, isFinal)\n\t\tif (tier === TIER_WILDCARD) {\n\t\t\tparams.push(segment.slice(1))\n\t\t\treturn '(.+)'\n\t\t}\n\t\tif (tier === TIER_PARAM) {\n\t\t\tconst match = /^:([A-Za-z_]\\w*)/.exec(segment)\n\t\t\tconst name = match?.[1] ?? ''\n\t\t\tparams.push(name)\n\t\t\treturn `([^/]+)${escapeRegExp(segment.slice(1 + name.length))}`\n\t\t}\n\t\treturn escapeRegExp(segment)\n\t})\n\tconst pattern = compiledSegments.join('/')\n\t// Allow ONE optional trailing slash before the `$` anchor (Express `strict: false`),\n\t// EXCEPT for the root `/` and the empty pattern — those anchor exactly so they keep\n\t// matching only `/` / `''` (a bare `/?` there would let `''` match `/` and vice versa).\n\tconst suffix = normalized === '/' || normalized === '' ? '' : '/?'\n\tconst flags = sensitive ? '' : 'i'\n\treturn { regex: new RegExp(`^${pattern}${suffix}$`, flags), params }\n}\n\n/**\n * URL-decode one captured param value, tolerating a malformed percent-escape —\n * the decode {@link matchPath} applies to each captured group.\n *\n * @remarks\n * A bad `%` sequence is not a reason to reject an otherwise-matching route, so\n * a `decodeURIComponent` that would throw falls back to the raw value\n * (mirroring the cookie / token boundary readers, AGENTS §14). Total — never\n * throws.\n *\n * @param value - The raw captured param value\n * @returns The URL-decoded value, or the raw value when decoding would throw\n *\n * @example\n * ```ts\n * decodeParam('a%2Fb') // 'a/b'\n * decodeParam('100%25') // '100%'\n * decodeParam('%') // '%' — malformed escape stays literal\n * ```\n */\nexport function decodeParam(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value)\n\t} catch {\n\t\t// A malformed `%` escape stays literal rather than throwing — the match still succeeds.\n\t\treturn value\n\t}\n}\n\n/**\n * Extract the URL-decoded params a compiled path captures from a concrete\n * pathname, or `undefined` when the pathname does not match.\n *\n * @remarks\n * Runs the {@link CompiledPath} `regex` against `pathname` (a single `exec`); a\n * miss returns `undefined`. On a hit it walks `params` POSITIONALLY — the\n * `n`-th param name pairs with the `n`-th capture group — and URL-decodes each\n * value with {@link decodeParam}. Returns a frozen `name → value` record (empty\n * for a parameterless path). Total — never throws.\n *\n * @param compiled - The {@link CompiledPath} from {@link compilePath}\n * @param pathname - The concrete request pathname to match (e.g. `/users/7`)\n * @returns The decoded params on a hit, or `undefined` on a miss\n *\n * @example\n * ```ts\n * const compiled = compilePath('/users/:id')\n * matchPath(compiled, '/users/7') // { id: '7' }\n * matchPath(compiled, '/users/a%2Fb') // { id: 'a/b' } — decoded\n * matchPath(compiled, '/posts/7') // undefined\n * ```\n */\nexport function matchPath(\n\tcompiled: CompiledPath,\n\tpathname: string,\n): Readonly<Record<string, string>> | undefined {\n\tconst result = compiled.regex.exec(pathname)\n\tif (result === null) return undefined\n\tconst params: Record<string, string> = {}\n\tfor (let index = 0; index < compiled.params.length; index += 1) {\n\t\tconst name = compiled.params[index]\n\t\tconst value = result[index + 1]\n\t\tif (name !== undefined && value !== undefined) params[name] = decodeParam(value)\n\t}\n\treturn Object.freeze(params)\n}\n\n/**\n * Classify one path segment into its specificity TIER — the SAME syntax\n * {@link compilePath} rewrites: a syntactically valid `:name` head is a PARAM\n * segment, a final `*name` is a WILDCARD segment, everything else (including a\n * literal segment that merely CONTAINS a `:` mid-string, e.g. `a:b`) is a\n * LITERAL segment.\n *\n * @remarks\n * This is the fix over the old engine's bug: the old classifier ranked any\n * segment `includes(':')` as a param, so a literal segment like `a:b` was\n * mis-tiered even though {@link compilePath} compiles it literally. Sharing one\n * segment parser between compilation and classification keeps the two in\n * agreement (§4 fixes). Pure and total.\n *\n * @param segment - One `/`-split path segment\n * @param isFinal - Whether `segment` is the last segment of its path (only the\n * final segment may be classified as a wildcard)\n * @returns The segment's specificity tier — {@link import('./constants.js').TIER_LITERAL},\n * {@link import('./constants.js').TIER_PARAM}, or\n * {@link import('./constants.js').TIER_WILDCARD}\n *\n * @example\n * ```ts\n * classifySegment(':id', true) // 1 — TIER_PARAM\n * classifySegment('*rest', true) // 0 — TIER_WILDCARD\n * classifySegment('a:b', true) // 2 — TIER_LITERAL — the old bug's regression case\n * classifySegment('users', false) // 2 — TIER_LITERAL\n * ```\n */\nexport function classifySegment(segment: string, isFinal: boolean): number {\n\tif (isFinal && /^\\*[A-Za-z_]\\w*$/.test(segment)) return TIER_WILDCARD\n\tif (/^:[A-Za-z_]\\w*/.test(segment)) return TIER_PARAM\n\treturn TIER_LITERAL\n}\n\n/**\n * Compute a route path's SPECIFICITY VECTOR — the per-segment type ranking\n * that breaks a tie when several registered routes match the same concrete\n * pathname.\n *\n * @remarks\n * Splits the CANONICALIZED path into segments (on `/`) and maps each to its\n * specificity tier via {@link classifySegment} — the same segment parser\n * {@link compilePath} uses, so a literal segment that merely contains a `:`\n * (e.g. `a:b`) is correctly tiered as literal rather than param (the old\n * engine's bug, fixed here). The standard route-precedence rule compares two\n * matching routes' vectors LEFT-TO-RIGHT: at the first index where the tiers\n * differ, the HIGHER tier (a literal over a param over a wildcard) is MORE\n * SPECIFIC and wins — so `/users/me` (`[2, 2]`) beats `/users/:id` (`[2, 1]`)\n * beats `/users/*rest` (`[2, 0]`) regardless of registration order. Two routes\n * that match the SAME concrete pathname necessarily have the same segment\n * count in the common case; {@link compareSpecificity} handles the general\n * case for totality.\n *\n * @param path - The route path pattern (e.g. `/users/:id`)\n * @returns The per-segment specificity tiers, in order\n *\n * @example\n * ```ts\n * computeSpecificity('/users/me') // [2, 2]\n * computeSpecificity('/users/:id') // [2, 1]\n * computeSpecificity('/files/*rest') // [2, 0]\n * computeSpecificity('/a:b') // [2] — literal, not param — the classification fix\n * ```\n */\nexport function computeSpecificity(path: string): readonly number[] {\n\tconst segments = canonicalizePath(path).split('/')\n\treturn segments.map((segment, index) => classifySegment(segment, index === segments.length - 1))\n}\n\n/**\n * Compare two route paths by SPECIFICITY — the comparator that picks the\n * most-specific matching route (literal-over-param-over-wildcard,\n * registration-order-independent).\n *\n * @remarks\n * Compares the two paths' {@link computeSpecificity} vectors LEFT-TO-RIGHT and\n * returns a standard `Array.sort` ordering: a NEGATIVE number when `a` is MORE\n * specific than `b` (so a descending-specificity sort puts `a` first),\n * positive when `b` is more specific, `0` when neither out-ranks the other\n * across the compared segments. At the first index where the tiers differ,\n * the higher tier wins; if one vector is a prefix of the other (different\n * segment counts), the LONGER, more-segmented path is treated as more\n * specific (a missing segment ranks below any real one).\n *\n * @param a - The first route path\n * @param b - The second route path\n * @returns A negative number when `a` is more specific, positive when `b` is, else `0`\n *\n * @example\n * ```ts\n * compareSpecificity('/users/me', '/users/:id') // negative — literal wins\n * compareSpecificity('/users/:id', '/users/*rest') // negative — param beats wildcard\n * compareSpecificity('/users/:id', '/users/:id') // 0 — equal specificity\n * ```\n */\nexport function compareSpecificity(a: string, b: string): number {\n\tconst left = computeSpecificity(a)\n\tconst right = computeSpecificity(b)\n\tconst length = Math.max(left.length, right.length)\n\tfor (let index = 0; index < length; index += 1) {\n\t\t// A missing segment ranks below any real one (the shorter path is less specific).\n\t\tconst tierA = left[index] ?? -1\n\t\tconst tierB = right[index] ?? -1\n\t\tif (tierA !== tierB) return tierB - tierA\n\t}\n\treturn 0\n}\n\n/**\n * Narrow a raw `request.method` string into a typed {@link Method} — total,\n * never throws.\n *\n * @remarks\n * Guarded via {@link import('./constants.js').METHODS} (the seven registrable\n * HTTP methods); any other value (an unknown verb, non-uppercase casing)\n * resolves to `undefined` rather than throwing (§14 guard totality). Pure\n * leaf shared by the `Dispatcher`'s `handle` (§5.1 unknown-verb honesty) and\n * anywhere else a raw method string needs narrowing.\n *\n * @param value - The raw `request.method` string to narrow\n * @returns The matching {@link Method}, or `undefined` when `value` is not one\n * of the seven registrable methods\n *\n * @example\n * ```ts\n * parseMethod('GET') // 'GET'\n * parseMethod('PURGE') // undefined\n * parseMethod('get') // undefined — case-sensitive\n * ```\n */\nexport function parseMethod(value: string): Method | undefined {\n\tif (\n\t\tvalue === 'GET' ||\n\t\tvalue === 'POST' ||\n\t\tvalue === 'PUT' ||\n\t\tvalue === 'PATCH' ||\n\t\tvalue === 'DELETE' ||\n\t\tvalue === 'HEAD' ||\n\t\tvalue === 'OPTIONS'\n\t)\n\t\treturn value\n\treturn undefined\n}\n\n/**\n * Join a group prefix and a route path into one `/`-prefixed path, normalizing\n * duplicate or missing joining slashes.\n *\n * @remarks\n * {@link import('./types.js').GroupInterface} / {@link import('./types.js').DispatchGroupInterface}\n * compose a prefix with each registered entry's path this way — pure string\n * composition (§4.2.2), no independent state. Both a duplicated slash\n * (`'/api/'` + `'/users'`) and a missing one (`'/api'` + `'users'`) normalize\n * to a single joining slash. An empty `prefix` returns `path` unchanged (after\n * ensuring a leading slash); an empty `path` returns `prefix` unchanged.\n * Pure and total.\n *\n * @param prefix - The group prefix (e.g. `/api`)\n * @param path - The route path being joined under the prefix (e.g. `/users`)\n * @returns The joined `/`-prefixed path\n *\n * @example\n * ```ts\n * joinPaths('/api', '/users') // '/api/users'\n * joinPaths('/api/', '/users') // '/api/users'\n * joinPaths('/api', 'users') // '/api/users'\n * joinPaths('', '/users') // '/users'\n * joinPaths('/api', '') // '/api'\n * ```\n */\nexport function joinPaths(prefix: string, path: string): string {\n\tif (prefix === '') return path.startsWith('/') ? path : `/${path}`\n\tif (path === '') return prefix\n\tconst left = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix\n\tconst right = path.startsWith('/') ? path : `/${path}`\n\treturn `${left}${right}`\n}\n\n/**\n * Identity pass-through for a {@link RouteInput} that pins its `Path` generic\n * to the LITERAL registration-site string, so `context.params` types\n * correctly through {@link PathParams} without an explicit type argument.\n *\n * @remarks\n * A bare object literal handed straight to {@link import('./types.js').DispatcherInterface}'s\n * `add` already infers `Path` as a literal at that call site — but the moment\n * the object is built through an intermediate binding (a local `const route =\n * { method, path, handler }`) TypeScript widens `path` to `string` unless the\n * binding's own type is pinned. Wrapping the literal in `route(...)` supplies\n * that pin: its `const Path extends string` type parameter infers the NARROW\n * literal from the call, and the function returns its input completely\n * unchanged (same reference, no cloning, no validation) — this is a\n * compile-time typing aid only, not a construction step (contrast\n * {@link import('./factories.js')} `create*` entity factories). A\n * heterogeneous `RouteInput[]` built from several `route(...)` calls still\n * widens each element's `Path` to `string` once collected into one array\n * (§14) — the realistic ceiling this helper raises is PER-CALL typing at the\n * registration site, not a stored, still-literal-typed record.\n *\n * @typeParam Path - The route path pattern literal (drives `context.params`\n * via {@link PathParams})\n * @typeParam TState - The consumer's opaque per-request state type\n * @param input - The {@link RouteInput} to pass through unchanged\n * @returns `input`, unchanged (same reference)\n *\n * @example\n * ```ts\n * const input = route({\n * \tmethod: 'GET',\n * \tpath: '/users/:id',\n * \thandler: (_request, context) => new Response(context.params.id), // typed string\n * })\n * dispatcher.add(input)\n * ```\n */\nexport function route<const Path extends string, TState = undefined>(\n\tinput: RouteInput<Path, TState>,\n): RouteInput<Path, TState> {\n\treturn input\n}\n","import type { GroupInterface, RouteEntry, RouterInterface } from './types.js'\nimport { joinPaths } from './helpers.js'\n\n/**\n * A prefix-scoped registration handle over a {@link import('./Router.js').Router} —\n * pure string composition (AGENTS §4.2.2), no independent state or storage.\n *\n * @typeParam Meta - The entry payload type, matching the owning router\n *\n * @remarks\n * Every `add` composes `entry.path` as `joinPaths(prefix, entry.path)` and\n * forwards to the OWNING router, so grouped routes land in the SAME registry.\n * `group(prefix)` nests, composing prefixes via {@link joinPaths}.\n *\n * @example\n * ```ts\n * import { Router } from '@src/core'\n *\n * const router = new Router<{ readonly page: string }>()\n * const api = router.group('/api')\n * api.add({ path: '/users', meta: { page: 'list' } })\n * router.match('/api/users')?.path // '/api/users'\n * ```\n */\nexport class Group<Meta> implements GroupInterface<Meta> {\n\treadonly prefix: string\n\treadonly #parent: RouterInterface<Meta>\n\n\tconstructor(parent: RouterInterface<Meta>, prefix: string) {\n\t\tthis.#parent = parent\n\t\tthis.prefix = prefix\n\t}\n\n\tadd(entry: RouteEntry<Meta>): void\n\tadd(entries: readonly RouteEntry<Meta>[]): void\n\tadd(input: RouteEntry<Meta> | readonly RouteEntry<Meta>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tthis.#parent.add(\n\t\t\tinputs.map((entry) => ({ ...entry, path: joinPaths(this.prefix, entry.path) })),\n\t\t)\n\t}\n\n\tgroup(prefix: string): GroupInterface<Meta> {\n\t\treturn new Group<Meta>(this.#parent, joinPaths(this.prefix, prefix))\n\t}\n}\n","import type {\n\tAnswerHandler,\n\tCompiledPath,\n\tGroupInterface,\n\tRouteEntry,\n\tRouterInterface,\n\tRouterMatch,\n\tRouterOptions,\n} from './types.js'\nimport { isString } from '@orkestrel/contract'\nimport { compareSpecificity, compilePath, matchPath } from './helpers.js'\nimport { Group } from './Group.js'\n\n/**\n * The path-matching + registry engine — registers `{ path, meta, name? }`\n * entries (compiling each path once) and resolves a concrete pathname to the\n * MOST SPECIFIC matching entry. The shared machine both the `Navigator`\n * (browser) and the `Dispatcher` (core, method-dimensioned) compose.\n *\n * @typeParam Meta - The opaque payload each entry carries and a match returns\n *\n * @remarks\n * - **Registration boundary guard (§14).** `add` validates each entry's\n * `path` — `isString` plus a leading `/` — and throws `TypeError` on a\n * malformed registration; `match` stays guard-free (the hot path).\n * - **Compile-once.** Each path is compiled exactly once at registration into\n * a parallel `#compiled` array, so `match` runs only a cached `exec` per\n * candidate.\n * - **Dedup via `key`.** When `options.key` is set, an entry whose computed\n * key already exists REPLACES the prior one IN PLACE (both the `#entries`\n * and `#compiled` arrays, at the existing index) — last write wins, no\n * engine rebuild. Omitted ⇒ every entry is kept, even duplicate paths.\n * - **Groups.** `group(prefix)` returns a {@link GroupInterface} that composes\n * `prefix` onto every entry it registers, nesting via {@link joinPaths}.\n *\n * @example\n * ```ts\n * const router = new Router<{ readonly page: string }>()\n * router.add({ path: '/users/:id', meta: { page: 'profile' } })\n * router.match('/users/7') // { path: '/users/:id', params: { id: '7' }, meta: { page: 'profile' } }\n * ```\n */\nexport class Router<Meta> implements RouterInterface<Meta> {\n\treadonly #entries: RouteEntry<Meta>[] = []\n\treadonly #compiled: CompiledPath[] = []\n\treadonly #sensitive: boolean\n\treadonly #key: ((entry: RouteEntry<Meta>) => string) | undefined\n\treadonly #index: Map<string, number> = new Map()\n\n\tconstructor(options?: RouterOptions<Meta>) {\n\t\tthis.#sensitive = options?.sensitive ?? true\n\t\tthis.#key = options?.key\n\t\tif (options?.entries !== undefined) this.add(options.entries)\n\t}\n\n\tget count(): number {\n\t\treturn this.#entries.length\n\t}\n\n\tadd(entry: RouteEntry<Meta>): void\n\tadd(entries: readonly RouteEntry<Meta>[]): void\n\tadd(input: RouteEntry<Meta> | readonly RouteEntry<Meta>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tfor (const entry of inputs) this.#register(entry)\n\t}\n\n\tmatch(pathname: string, answers?: AnswerHandler<Meta>): RouterMatch<Meta> | undefined {\n\t\tlet best: { entry: RouteEntry<Meta>; params: Readonly<Record<string, string>> } | undefined\n\t\tfor (let index = 0; index < this.#entries.length; index += 1) {\n\t\t\tconst entry = this.#entries[index]\n\t\t\tconst compiled = this.#compiled[index]\n\t\t\tif (entry === undefined || compiled === undefined) continue\n\t\t\tif (answers !== undefined && !answers(entry.meta)) continue\n\t\t\tconst params = matchPath(compiled, pathname)\n\t\t\tif (params === undefined) continue\n\t\t\tif (best === undefined || compareSpecificity(entry.path, best.entry.path) < 0)\n\t\t\t\tbest = { entry, params }\n\t\t}\n\t\tif (best === undefined) return undefined\n\t\treturn {\n\t\t\tpath: best.entry.path,\n\t\t\tparams: best.params,\n\t\t\tmeta: best.entry.meta,\n\t\t\tname: best.entry.name,\n\t\t}\n\t}\n\n\tentries(): readonly RouteEntry<Meta>[]\n\tentries(pathname: string): readonly RouteEntry<Meta>[]\n\tentries(pathname?: string): readonly RouteEntry<Meta>[] {\n\t\tif (pathname === undefined) return [...this.#entries]\n\t\tconst out: RouteEntry<Meta>[] = []\n\t\tfor (let index = 0; index < this.#entries.length; index += 1) {\n\t\t\tconst entry = this.#entries[index]\n\t\t\tconst compiled = this.#compiled[index]\n\t\t\tif (entry === undefined || compiled === undefined) continue\n\t\t\tif (matchPath(compiled, pathname) !== undefined) out.push(entry)\n\t\t}\n\t\treturn out\n\t}\n\n\tgroup(prefix: string): GroupInterface<Meta> {\n\t\treturn new Group<Meta>(this, prefix)\n\t}\n\n\tclear(): void {\n\t\tthis.#entries.length = 0\n\t\tthis.#compiled.length = 0\n\t\tthis.#index.clear()\n\t}\n\n\t// Validate the registration boundary (§14: isString + leading '/'), then either replace an\n\t// existing entry IN PLACE (dedup via `#key`, last write wins) or append a new one — the\n\t// engine's compile-once invariant, kept in sync across the `#entries`/`#compiled` pair.\n\t#register(entry: RouteEntry<Meta>): void {\n\t\tif (!isString(entry.path) || !entry.path.startsWith('/'))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route path must be a string starting with \"/\", got ${JSON.stringify(entry.path)}`,\n\t\t\t)\n\t\tconst compiled = compilePath(entry.path, this.#sensitive)\n\t\tif (this.#key === undefined) {\n\t\t\tthis.#entries.push(entry)\n\t\t\tthis.#compiled.push(compiled)\n\t\t\treturn\n\t\t}\n\t\tconst key = this.#key(entry)\n\t\tconst existing = this.#index.get(key)\n\t\tif (existing !== undefined) {\n\t\t\tthis.#entries[existing] = entry\n\t\t\tthis.#compiled[existing] = compiled\n\t\t\treturn\n\t\t}\n\t\tthis.#index.set(key, this.#entries.length)\n\t\tthis.#entries.push(entry)\n\t\tthis.#compiled.push(compiled)\n\t}\n}\n","import type { DispatchGroupInterface, DispatcherInterface, RouteInput } from './types.js'\nimport { joinPaths } from './helpers.js'\n\n/**\n * A prefix-scoped registration handle over a\n * {@link import('./Dispatcher.js').Dispatcher} — the method-dimensioned\n * counterpart of `Group` (`Group.ts`).\n *\n * @typeParam TState - The consumer's opaque per-request state type, matching\n * the owning dispatcher\n *\n * @remarks\n * Every `add` composes `input.path` via {@link joinPaths} against\n * `this.prefix` and forwards to the OWNING dispatcher's `add` (its own §14\n * boundary guard still applies). Pure string composition (§4.2.2) — no\n * independent state or storage.\n *\n * @example\n * ```ts\n * import { Dispatcher } from '@src/core'\n *\n * const dispatcher = new Dispatcher()\n * const api = dispatcher.group('/api')\n * api.add({ method: 'GET', path: '/users', handler: () => new Response('ok') })\n * ```\n */\nexport class DispatchGroup<TState> implements DispatchGroupInterface<TState> {\n\treadonly prefix: string\n\treadonly #parent: DispatcherInterface<TState>\n\n\tconstructor(parent: DispatcherInterface<TState>, prefix: string) {\n\t\tthis.#parent = parent\n\t\tthis.prefix = prefix\n\t}\n\n\tadd<Path extends string>(input: RouteInput<Path, TState>): void\n\tadd(inputs: readonly RouteInput<string, TState>[]): void\n\tadd(input: RouteInput<string, TState> | readonly RouteInput<string, TState>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tthis.#parent.add(\n\t\t\tinputs.map((route) => ({ ...route, path: joinPaths(this.prefix, route.path) })),\n\t\t)\n\t}\n\n\tgroup(prefix: string): DispatchGroupInterface<TState> {\n\t\treturn new DispatchGroup<TState>(this.#parent, joinPaths(this.prefix, prefix))\n\t}\n}\n","import type {\n\tDispatchGroupInterface,\n\tDispatcherEventMap,\n\tDispatcherInterface,\n\tDispatcherOptions,\n\tDispatchResult,\n\tMethod,\n\tRouteContext,\n\tRouteEntry,\n\tRouteInput,\n\tRouteRecord,\n\tRouterInterface,\n\tRouterMatch,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isFunction, isString } from '@orkestrel/contract'\nimport { METHODS } from './constants.js'\nimport { canonicalizePath, parseMethod } from './helpers.js'\nimport { Router } from './Router.js'\nimport { DispatchGroup } from './DispatchGroup.js'\n\n/**\n * The fetch-standard, method-dimensioned dispatch entity — layers HTTP method\n * dispatch and web-standard `Request`/`Response` handling over one internal\n * `Router<RouteRecord<TState>>`. The core machine the eventual server face\n * (§7) and any fetch-native runtime consumes directly.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n *\n * @remarks\n * - **Dedup by `method + canonicalizePath`.** The underlying `Router` is\n * constructed with a `key` function so registering the same method+path\n * twice REPLACES the prior route in place (§5.1).\n * - **Registration boundary guard (§14).** `add` validates each input's\n * `handler` (`isFunction`) and `method` (must be in {@link METHODS}) —\n * throws `TypeError` on a malformed registration; path validation is\n * delegated to the underlying `Router`'s own guard. `match`/`handle` stay\n * guard-free.\n * - **Auto-`HEAD` / auto-`OPTIONS`.** A `HEAD` request with no registered\n * `HEAD` route runs the matching `GET` handler and strips the response\n * body; an `OPTIONS` request with no registered `OPTIONS` route answers\n * `204` with a derived `Allow` header.\n * - **Handler throws propagate.** `handle` never invents an error boundary —\n * a handler throw reaches the caller uncaught (§5.1).\n * - **Emitter (§13).** Owns a `#emitter` for {@link DispatcherEventMap};\n * `match`/`miss` fire AFTER resolution, before the handler/responder runs.\n *\n * @example\n * ```ts\n * const dispatcher = new Dispatcher<{ readonly userId: string }>()\n * dispatcher.add({\n * \tmethod: 'GET',\n * \tpath: '/users/:id',\n * \thandler: (request, context) => Response.json({ id: context.params.id }),\n * })\n * const response = await dispatcher.handle(new Request('http://x/users/7'), { userId: 'me' })\n * ```\n */\nexport class Dispatcher<TState = undefined> implements DispatcherInterface<TState> {\n\treadonly router: RouterInterface<RouteRecord<TState>>\n\treadonly #emitter: Emitter<DispatcherEventMap>\n\treadonly #unmatched: (request: Request) => Response | Promise<Response>\n\treadonly #unmethoded: (request: Request, allow: readonly Method[]) => Response | Promise<Response>\n\n\tconstructor(options?: DispatcherOptions<TState>) {\n\t\tthis.router = new Router<RouteRecord<TState>>({\n\t\t\tsensitive: options?.sensitive,\n\t\t\tkey: (entry) => `${entry.meta.method} ${canonicalizePath(entry.path)}`,\n\t\t})\n\t\tthis.#emitter = new Emitter<DispatcherEventMap>({ on: options?.on, error: options?.error })\n\t\tthis.#unmatched =\n\t\t\toptions?.unmatched ?? ((_request) => new Response('Not Found', { status: 404 }))\n\t\tthis.#unmethoded =\n\t\t\toptions?.unmethoded ??\n\t\t\t((_request, allow) =>\n\t\t\t\tnew Response('Method Not Allowed', {\n\t\t\t\t\tstatus: 405,\n\t\t\t\t\theaders: { Allow: allow.join(', ') },\n\t\t\t\t}))\n\t\tif (options?.routes !== undefined) this.add(options.routes)\n\t}\n\n\tget emitter(): EmitterInterface<DispatcherEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tadd<Path extends string>(input: RouteInput<Path, TState>): void\n\tadd(inputs: readonly RouteInput<string, TState>[]): void\n\tadd(input: RouteInput<string, TState> | readonly RouteInput<string, TState>[]): void {\n\t\tconst inputs = Array.isArray(input) ? input : [input]\n\t\tfor (const route of inputs) this.#register(route)\n\t}\n\n\tgroup(prefix: string): DispatchGroupInterface<TState> {\n\t\treturn new DispatchGroup<TState>(this, prefix)\n\t}\n\n\tmatch(method: Method, pathname: string): DispatchResult<TState> {\n\t\tconst hit = this.router.match(pathname, (meta) => meta.method === method)\n\t\tif (hit !== undefined) return { status: 'matched', match: hit }\n\t\tif (method === 'HEAD') {\n\t\t\tconst getHit = this.router.match(pathname, (meta) => meta.method === 'GET')\n\t\t\tif (getHit !== undefined) return { status: 'matched', match: getHit }\n\t\t}\n\t\tconst allow = this.#allow(pathname)\n\t\tif (allow.length === 0) return { status: 'unmatched' }\n\t\treturn { status: 'unmethoded', allow }\n\t}\n\n\tasync handle(request: Request, state: TState): Promise<Response> {\n\t\tconst url = new URL(request.url)\n\t\tconst pathname = url.pathname\n\t\tconst requested = request.method\n\t\tconst method = parseMethod(requested)\n\t\tif (method === undefined) {\n\t\t\tconst allow = this.#allow(pathname)\n\t\t\tif (allow.length === 0) {\n\t\t\t\tthis.#emitter.emit('miss', requested, pathname, 'unmatched')\n\t\t\t\treturn this.#unmatched(request)\n\t\t\t}\n\t\t\tthis.#emitter.emit('miss', requested, pathname, 'unmethoded')\n\t\t\treturn this.#unmethoded(request, allow)\n\t\t}\n\t\tconst result = this.match(method, pathname)\n\t\tif (result.status === 'matched')\n\t\t\treturn this.#respondMatched(request, state, method, result.match, url)\n\t\tif (result.status === 'unmethoded') {\n\t\t\tif (method === 'OPTIONS') return this.#respondAutoOptions(pathname, result.allow)\n\t\t\tthis.#emitter.emit('miss', method, pathname, 'unmethoded')\n\t\t\treturn this.#unmethoded(request, result.allow)\n\t\t}\n\t\tthis.#emitter.emit('miss', method, pathname, 'unmatched')\n\t\treturn this.#unmatched(request)\n\t}\n\n\tdestroy(): void {\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// Validate the registration boundary (§14: handler function, known method), then delegate\n\t// path validation + dedup to the underlying `Router`'s own guard.\n\t#register(input: RouteInput<string, TState>): void {\n\t\tif (!isFunction(input.handler))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route handler must be a function, got ${JSON.stringify(input.handler)}`,\n\t\t\t)\n\t\tif (!isString(input.method) || !METHODS.has(input.method))\n\t\t\tthrow new TypeError(\n\t\t\t\t`a route method must be one of ${[...METHODS].join(', ')}, got ${JSON.stringify(input.method)}`,\n\t\t\t)\n\t\tthis.router.add({\n\t\t\tpath: input.path,\n\t\t\tname: input.name,\n\t\t\tmeta: { method: input.method, handler: input.handler, name: input.name },\n\t\t})\n\t}\n\n\t// The derived `Allow` set for a pathname — every distinct registered method, with `HEAD`\n\t// added whenever `GET` is present and `HEAD` is not explicitly registered (§5.1).\n\t#allow(pathname: string): readonly Method[] {\n\t\tconst entries: readonly RouteEntry<RouteRecord<TState>>[] = this.router.entries(pathname)\n\t\tconst methods = new Set<Method>()\n\t\tfor (const entry of entries) methods.add(entry.meta.method)\n\t\tif (methods.has('GET')) methods.add('HEAD')\n\t\treturn [...methods]\n\t}\n\n\t// A matched dispatch — either the winning handler runs directly, or (for a derived `HEAD`\n\t// with no explicit `HEAD` route) the `GET` handler runs and the response body is stripped.\n\tasync #respondMatched(\n\t\trequest: Request,\n\t\tstate: TState,\n\t\tmethod: Method,\n\t\tmatch: RouterMatch<RouteRecord<TState>>,\n\t\turl: URL,\n\t): Promise<Response> {\n\t\tthis.#emitter.emit('match', method, match.path)\n\t\tconst context: RouteContext<string, TState> = {\n\t\t\tparams: match.params,\n\t\t\tpattern: match.path,\n\t\t\turl,\n\t\t\tstate,\n\t\t}\n\t\tconst response = await match.meta.handler(request, context)\n\t\tif (method === 'HEAD' && match.meta.method === 'GET')\n\t\t\treturn new Response(null, {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\theaders: response.headers,\n\t\t\t})\n\t\treturn response\n\t}\n\n\t// Derived `OPTIONS` — no explicit `OPTIONS` route registered for this pathname: answer\n\t// `204` with the derived `Allow` set (adding `OPTIONS` itself, always answerable).\n\t#respondAutoOptions(pathname: string, allow: readonly Method[]): Response {\n\t\tthis.#emitter.emit('match', 'OPTIONS', pathname)\n\t\tconst headers = new Headers({ Allow: [...allow, 'OPTIONS'].join(', ') })\n\t\treturn new Response(null, { status: 204, headers })\n\t}\n}\n","import type {\n\tDispatcherInterface,\n\tDispatcherOptions,\n\tRouterInterface,\n\tRouterOptions,\n} from './types.js'\nimport { Dispatcher } from './Dispatcher.js'\nimport { Router } from './Router.js'\n\n/**\n * Create a {@link RouterInterface} — the pure path-matching + registry engine\n * shared by the browser `Navigator` and the core `Dispatcher`.\n *\n * @remarks\n * Prefer this over `new Router(...)` at call sites that only need the\n * interface; an entity that OWNS a `Router` internally (like `Dispatcher`)\n * still constructs `new Router(...)` directly.\n *\n * @typeParam Meta - The opaque payload each entry carries and a match returns\n * @param options - Optional initial `entries`, the `sensitive` case toggle\n * (default `true`), and a `key` dedup identity function\n * @returns A {@link RouterInterface}\n *\n * @example\n * ```ts\n * import { createRouter } from '@src/core'\n *\n * const router = createRouter<{ readonly page: string }>()\n * router.add({ path: '/users/:id', meta: { page: 'profile' } })\n * router.match('/users/7') // { path: '/users/:id', params: { id: '7' }, meta: { page: 'profile' } }\n * ```\n */\nexport function createRouter<Meta>(options?: RouterOptions<Meta>): RouterInterface<Meta> {\n\treturn new Router<Meta>(options)\n}\n\n/**\n * Create a {@link DispatcherInterface} — the fetch-standard, method-\n * dimensioned dispatch entity over one internal `Router<RouteRecord<TState>>`.\n *\n * @remarks\n * Prefer this over `new Dispatcher(...)` at call sites that only need the\n * interface.\n *\n * @typeParam TState - The consumer's opaque per-request state type (default\n * `undefined` for stateless use)\n * @param options - Optional initial `routes`, the `sensitive` case toggle,\n * the `unmatched`/`unmethoded` default-responder overrides, and the AGENTS\n * §13 emitter `on`/`error` wiring\n * @returns A {@link DispatcherInterface}\n *\n * @example\n * ```ts\n * import { createDispatcher } from '@src/core'\n *\n * const dispatcher = createDispatcher<{ readonly userId: string }>({\n * \troutes: [\n * \t\t{ method: 'GET', path: '/health', handler: () => new Response('ok') },\n * \t],\n * })\n * const response = await dispatcher.handle(new Request('http://x/health'), { userId: 'me' })\n * ```\n */\nexport function createDispatcher<TState = undefined>(\n\toptions?: DispatcherOptions<TState>,\n): DispatcherInterface<TState> {\n\treturn new Dispatcher<TState>(options)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAuBA,IAAa,UAA+B,OAAO,uBAClD,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAO;CAAS;CAAU;CAAQ;AAAS,CAAC,CACrE;;;;;;;;;;;;;;AAeA,IAAa,eAAe;;;;;;;;;;;;;;AAe5B,IAAa,aAAa;;;;;;;;;;;;;;AAe1B,IAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;ACvC7B,SAAgB,aAAa,OAAuB;CACnD,OAAO,MAAM,QAAQ,uBAAuB,MAAM;AACnD;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,iBAAiB,MAAsB;CACtD,OAAO,KAAK,SAAS,KAAK,KAAK,SAAS,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;AACpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,SAAgB,YAAY,MAAc,YAAY,MAAoB;CACzE,MAAM,SAAmB,CAAC;CAG1B,MAAM,aAAa,iBAAiB,IAAI;CACxC,MAAM,WAAW,WAAW,MAAM,GAAG;CAwBrC,MAAM,UAvBmB,SAAS,KAAK,SAAS,UAAU;EACzD,MAAM,UAAU,UAAU,SAAS,SAAS;EAG5C,IAAI,CAAC,WAAW,kBAAkB,KAAK,OAAO,GAC7C,MAAM,IAAI,UACT,wBAAwB,QAAQ,uDAAuD,KAAK,EAC7F;EAGD,MAAM,OAAO,gBAAgB,SAAS,OAAO;EAC7C,IAAI,SAAA,GAAwB;GAC3B,OAAO,KAAK,QAAQ,MAAM,CAAC,CAAC;GAC5B,OAAO;EACR;EACA,IAAI,SAAA,GAAqB;GAExB,MAAM,OADQ,mBAAmB,KAAK,OACzB,CAAA,GAAQ,MAAM;GAC3B,OAAO,KAAK,IAAI;GAChB,OAAO,UAAU,aAAa,QAAQ,MAAM,IAAI,KAAK,MAAM,CAAC;EAC7D;EACA,OAAO,aAAa,OAAO;CAC5B,CACgB,CAAA,CAAiB,KAAK,GAAG;CAIzC,MAAM,SAAS,eAAe,OAAO,eAAe,KAAK,KAAK;CAC9D,MAAM,QAAQ,YAAY,KAAK;CAC/B,OAAO;EAAE,OAAO,IAAI,OAAO,IAAI,UAAU,OAAO,IAAI,KAAK;EAAG;CAAO;AACpE;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,YAAY,OAAuB;CAClD,IAAI;EACH,OAAO,mBAAmB,KAAK;CAChC,QAAQ;EAEP,OAAO;CACR;AACD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,UACf,UACA,UAC+C;CAC/C,MAAM,SAAS,SAAS,MAAM,KAAK,QAAQ;CAC3C,IAAI,WAAW,MAAM,OAAO,KAAA;CAC5B,MAAM,SAAiC,CAAC;CACxC,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,OAAO,QAAQ,SAAS,GAAG;EAC/D,MAAM,OAAO,SAAS,OAAO;EAC7B,MAAM,QAAQ,OAAO,QAAQ;EAC7B,IAAI,SAAS,KAAA,KAAa,UAAU,KAAA,GAAW,OAAO,QAAQ,YAAY,KAAK;CAChF;CACA,OAAO,OAAO,OAAO,MAAM;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,gBAAgB,SAAiB,SAA0B;CAC1E,IAAI,WAAW,mBAAmB,KAAK,OAAO,GAAG,OAAA;CACjD,IAAI,iBAAiB,KAAK,OAAO,GAAG,OAAA;CACpC,OAAA;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,mBAAmB,MAAiC;CACnE,MAAM,WAAW,iBAAiB,IAAI,CAAC,CAAC,MAAM,GAAG;CACjD,OAAO,SAAS,KAAK,SAAS,UAAU,gBAAgB,SAAS,UAAU,SAAS,SAAS,CAAC,CAAC;AAChG;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,mBAAmB,GAAW,GAAmB;CAChE,MAAM,OAAO,mBAAmB,CAAC;CACjC,MAAM,QAAQ,mBAAmB,CAAC;CAClC,MAAM,SAAS,KAAK,IAAI,KAAK,QAAQ,MAAM,MAAM;CACjD,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAAG;EAE/C,MAAM,QAAQ,KAAK,UAAU;EAC7B,MAAM,QAAQ,MAAM,UAAU;EAC9B,IAAI,UAAU,OAAO,OAAO,QAAQ;CACrC;CACA,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,YAAY,OAAmC;CAC9D,IACC,UAAU,SACV,UAAU,UACV,UAAU,SACV,UAAU,WACV,UAAU,YACV,UAAU,UACV,UAAU,WAEV,OAAO;AAET;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,UAAU,QAAgB,MAAsB;CAC/D,IAAI,WAAW,IAAI,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI;CAC5D,IAAI,SAAS,IAAI,OAAO;CAGxB,OAAO,GAFM,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI,SAC5C,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,MACf,OAC2B;CAC3B,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;ACvZA,IAAa,QAAb,MAAa,MAA4C;CACxD;CACA;CAEA,YAAY,QAA+B,QAAgB;EAC1D,KAAKA,UAAU;EACf,KAAK,SAAS;CACf;CAIA,IAAI,OAA6D;EAChE,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,KAAKA,QAAQ,IACZ,OAAO,KAAK,WAAW;GAAE,GAAG;GAAO,MAAM,UAAU,KAAK,QAAQ,MAAM,IAAI;EAAE,EAAE,CAC/E;CACD;CAEA,MAAM,QAAsC;EAC3C,OAAO,IAAI,MAAY,KAAKA,SAAS,UAAU,KAAK,QAAQ,MAAM,CAAC;CACpE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACHA,IAAa,SAAb,MAA2D;CAC1D,WAAwC,CAAC;CACzC,YAAqC,CAAC;CACtC;CACA;CACA,yBAAuC,IAAI,IAAI;CAE/C,YAAY,SAA+B;EAC1C,KAAKG,aAAa,SAAS,aAAa;EACxC,KAAKC,OAAO,SAAS;EACrB,IAAI,SAAS,YAAY,KAAA,GAAW,KAAK,IAAI,QAAQ,OAAO;CAC7D;CAEA,IAAI,QAAgB;EACnB,OAAO,KAAKH,SAAS;CACtB;CAIA,IAAI,OAA6D;EAChE,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,KAAK,MAAM,SAAS,QAAQ,KAAKK,UAAU,KAAK;CACjD;CAEA,MAAM,UAAkB,SAA8D;EACrF,IAAI;EACJ,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAKL,SAAS,QAAQ,SAAS,GAAG;GAC7D,MAAM,QAAQ,KAAKA,SAAS;GAC5B,MAAM,WAAW,KAAKC,UAAU;GAChC,IAAI,UAAU,KAAA,KAAa,aAAa,KAAA,GAAW;GACnD,IAAI,YAAY,KAAA,KAAa,CAAC,QAAQ,MAAM,IAAI,GAAG;GACnD,MAAM,SAAS,UAAU,UAAU,QAAQ;GAC3C,IAAI,WAAW,KAAA,GAAW;GAC1B,IAAI,SAAS,KAAA,KAAa,mBAAmB,MAAM,MAAM,KAAK,MAAM,IAAI,IAAI,GAC3E,OAAO;IAAE;IAAO;GAAO;EACzB;EACA,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;EAC/B,OAAO;GACN,MAAM,KAAK,MAAM;GACjB,QAAQ,KAAK;GACb,MAAM,KAAK,MAAM;GACjB,MAAM,KAAK,MAAM;EAClB;CACD;CAIA,QAAQ,UAAgD;EACvD,IAAI,aAAa,KAAA,GAAW,OAAO,CAAC,GAAG,KAAKD,QAAQ;EACpD,MAAM,MAA0B,CAAC;EACjC,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAKA,SAAS,QAAQ,SAAS,GAAG;GAC7D,MAAM,QAAQ,KAAKA,SAAS;GAC5B,MAAM,WAAW,KAAKC,UAAU;GAChC,IAAI,UAAU,KAAA,KAAa,aAAa,KAAA,GAAW;GACnD,IAAI,UAAU,UAAU,QAAQ,MAAM,KAAA,GAAW,IAAI,KAAK,KAAK;EAChE;EACA,OAAO;CACR;CAEA,MAAM,QAAsC;EAC3C,OAAO,IAAI,MAAY,MAAM,MAAM;CACpC;CAEA,QAAc;EACb,KAAKD,SAAS,SAAS;EACvB,KAAKC,UAAU,SAAS;EACxB,KAAKG,OAAO,MAAM;CACnB;CAKA,UAAU,OAA+B;EACxC,IAAI,CAAC,SAAS,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,GAAG,GACtD,MAAM,IAAI,UACT,wDAAwD,KAAK,UAAU,MAAM,IAAI,GAClF;EACD,MAAM,WAAW,YAAY,MAAM,MAAM,KAAKF,UAAU;EACxD,IAAI,KAAKC,SAAS,KAAA,GAAW;GAC5B,KAAKH,SAAS,KAAK,KAAK;GACxB,KAAKC,UAAU,KAAK,QAAQ;GAC5B;EACD;EACA,MAAM,MAAM,KAAKE,KAAK,KAAK;EAC3B,MAAM,WAAW,KAAKC,OAAO,IAAI,GAAG;EACpC,IAAI,aAAa,KAAA,GAAW;GAC3B,KAAKJ,SAAS,YAAY;GAC1B,KAAKC,UAAU,YAAY;GAC3B;EACD;EACA,KAAKG,OAAO,IAAI,KAAK,KAAKJ,SAAS,MAAM;EACzC,KAAKA,SAAS,KAAK,KAAK;EACxB,KAAKC,UAAU,KAAK,QAAQ;CAC7B;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;AC9GA,IAAa,gBAAb,MAAa,cAAgE;CAC5E;CACA;CAEA,YAAY,QAAqC,QAAgB;EAChE,KAAKK,UAAU;EACf,KAAK,SAAS;CACf;CAIA,IAAI,OAAiF;EACpF,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,KAAKA,QAAQ,IACZ,OAAO,KAAK,WAAW;GAAE,GAAG;GAAO,MAAM,UAAU,KAAK,QAAQ,MAAM,IAAI;EAAE,EAAE,CAC/E;CACD;CAEA,MAAM,QAAgD;EACrD,OAAO,IAAI,cAAsB,KAAKA,SAAS,UAAU,KAAK,QAAQ,MAAM,CAAC;CAC9E;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACYA,IAAa,aAAb,MAAmF;CAClF;CACA;CACA;CACA;CAEA,YAAY,SAAqC;EAChD,KAAK,SAAS,IAAI,OAA4B;GAC7C,WAAW,SAAS;GACpB,MAAM,UAAU,GAAG,MAAM,KAAK,OAAO,GAAG,iBAAiB,MAAM,IAAI;EACpE,CAAC;EACD,KAAKC,WAAW,IAAI,QAA4B;GAAE,IAAI,SAAS;GAAI,OAAO,SAAS;EAAM,CAAC;EAC1F,KAAKC,aACJ,SAAS,eAAe,aAAa,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;EAC/E,KAAKC,cACJ,SAAS,gBACP,UAAU,UACX,IAAI,SAAS,sBAAsB;GAClC,QAAQ;GACR,SAAS,EAAE,OAAO,MAAM,KAAK,IAAI,EAAE;EACpC,CAAC;EACH,IAAI,SAAS,WAAW,KAAA,GAAW,KAAK,IAAI,QAAQ,MAAM;CAC3D;CAEA,IAAI,UAAgD;EACnD,OAAO,KAAKF;CACb;CAIA,IAAI,OAAiF;EACpF,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EACpD,KAAK,MAAM,SAAS,QAAQ,KAAKG,UAAU,KAAK;CACjD;CAEA,MAAM,QAAgD;EACrD,OAAO,IAAI,cAAsB,MAAM,MAAM;CAC9C;CAEA,MAAM,QAAgB,UAA0C;EAC/D,MAAM,MAAM,KAAK,OAAO,MAAM,WAAW,SAAS,KAAK,WAAW,MAAM;EACxE,IAAI,QAAQ,KAAA,GAAW,OAAO;GAAE,QAAQ;GAAW,OAAO;EAAI;EAC9D,IAAI,WAAW,QAAQ;GACtB,MAAM,SAAS,KAAK,OAAO,MAAM,WAAW,SAAS,KAAK,WAAW,KAAK;GAC1E,IAAI,WAAW,KAAA,GAAW,OAAO;IAAE,QAAQ;IAAW,OAAO;GAAO;EACrE;EACA,MAAM,QAAQ,KAAKC,OAAO,QAAQ;EAClC,IAAI,MAAM,WAAW,GAAG,OAAO,EAAE,QAAQ,YAAY;EACrD,OAAO;GAAE,QAAQ;GAAc;EAAM;CACtC;CAEA,MAAM,OAAO,SAAkB,OAAkC;EAChE,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;EAC/B,MAAM,WAAW,IAAI;EACrB,MAAM,YAAY,QAAQ;EAC1B,MAAM,SAAS,YAAY,SAAS;EACpC,IAAI,WAAW,KAAA,GAAW;GACzB,MAAM,QAAQ,KAAKA,OAAO,QAAQ;GAClC,IAAI,MAAM,WAAW,GAAG;IACvB,KAAKJ,SAAS,KAAK,QAAQ,WAAW,UAAU,WAAW;IAC3D,OAAO,KAAKC,WAAW,OAAO;GAC/B;GACA,KAAKD,SAAS,KAAK,QAAQ,WAAW,UAAU,YAAY;GAC5D,OAAO,KAAKE,YAAY,SAAS,KAAK;EACvC;EACA,MAAM,SAAS,KAAK,MAAM,QAAQ,QAAQ;EAC1C,IAAI,OAAO,WAAW,WACrB,OAAO,KAAKG,gBAAgB,SAAS,OAAO,QAAQ,OAAO,OAAO,GAAG;EACtE,IAAI,OAAO,WAAW,cAAc;GACnC,IAAI,WAAW,WAAW,OAAO,KAAKC,oBAAoB,UAAU,OAAO,KAAK;GAChF,KAAKN,SAAS,KAAK,QAAQ,QAAQ,UAAU,YAAY;GACzD,OAAO,KAAKE,YAAY,SAAS,OAAO,KAAK;EAC9C;EACA,KAAKF,SAAS,KAAK,QAAQ,QAAQ,UAAU,WAAW;EACxD,OAAO,KAAKC,WAAW,OAAO;CAC/B;CAEA,UAAgB;EACf,KAAKD,SAAS,QAAQ;CACvB;CAIA,UAAU,OAAyC;EAClD,IAAI,CAAC,WAAW,MAAM,OAAO,GAC5B,MAAM,IAAI,UACT,2CAA2C,KAAK,UAAU,MAAM,OAAO,GACxE;EACD,IAAI,CAAC,SAAS,MAAM,MAAM,KAAK,CAAC,QAAQ,IAAI,MAAM,MAAM,GACvD,MAAM,IAAI,UACT,iCAAiC,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,QAAQ,KAAK,UAAU,MAAM,MAAM,GAC7F;EACD,KAAK,OAAO,IAAI;GACf,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,MAAM;IAAE,QAAQ,MAAM;IAAQ,SAAS,MAAM;IAAS,MAAM,MAAM;GAAK;EACxE,CAAC;CACF;CAIA,OAAO,UAAqC;EAC3C,MAAM,UAAsD,KAAK,OAAO,QAAQ,QAAQ;EACxF,MAAM,0BAAU,IAAI,IAAY;EAChC,KAAK,MAAM,SAAS,SAAS,QAAQ,IAAI,MAAM,KAAK,MAAM;EAC1D,IAAI,QAAQ,IAAI,KAAK,GAAG,QAAQ,IAAI,MAAM;EAC1C,OAAO,CAAC,GAAG,OAAO;CACnB;CAIA,MAAMK,gBACL,SACA,OACA,QACA,OACA,KACoB;EACpB,KAAKL,SAAS,KAAK,SAAS,QAAQ,MAAM,IAAI;EAC9C,MAAM,UAAwC;GAC7C,QAAQ,MAAM;GACd,SAAS,MAAM;GACf;GACA;EACD;EACA,MAAM,WAAW,MAAM,MAAM,KAAK,QAAQ,SAAS,OAAO;EAC1D,IAAI,WAAW,UAAU,MAAM,KAAK,WAAW,OAC9C,OAAO,IAAI,SAAS,MAAM;GACzB,QAAQ,SAAS;GACjB,YAAY,SAAS;GACrB,SAAS,SAAS;EACnB,CAAC;EACF,OAAO;CACR;CAIA,oBAAoB,UAAkB,OAAoC;EACzE,KAAKA,SAAS,KAAK,SAAS,WAAW,QAAQ;EAC/C,MAAM,UAAU,IAAI,QAAQ,EAAE,OAAO,CAAC,GAAG,OAAO,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;EACvE,OAAO,IAAI,SAAS,MAAM;GAAE,QAAQ;GAAK;EAAQ,CAAC;CACnD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;ACzKA,SAAgB,aAAmB,SAAsD;CACxF,OAAO,IAAI,OAAa,OAAO;AAChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,iBACf,SAC8B;CAC9B,OAAO,IAAI,WAAmB,OAAO;AACtC"}
|