{"version":3,"file":"typegen-Junx6g4g.mjs","names":["walk","toRelative","extractPathParams","extractApiQueryParams"],"sources":["../src/typegen/scanner-cache.ts","../src/typegen/extract-ast.ts","../src/typegen/scanner.ts","../src/typegen/glob-fix.ts","../src/typegen/render/manifest.ts","../src/typegen/token-conventions.ts","../src/typegen/asset-types.ts","../src/typegen/index.ts"],"sourcesContent":["/**\n * Persistent per-file extraction cache for the typegen scanner.\n *\n * The scanner's dominant cost on large projects is reading every\n * `src/**\\/*.ts` file and running the regex extractors over each one.\n * On a watch/rebuild loop almost nothing has changed, yet the old\n * `scanProject` re-read and re-scanned the entire tree every time.\n *\n * This cache stores the per-file extraction result (`FileExtract`)\n * keyed by a cheap filesystem signature (`mtimeMs:size`). On the next\n * scan we `stat()` each file — a near-free syscall, no content read —\n * and reuse the cached extract whenever the signature is unchanged.\n * Only genuinely-changed files pay the readFile + regex cost.\n *\n * We deliberately key on `mtimeMs:size` rather than a content hash:\n * hashing requires reading the file, which is exactly the cost we are\n * trying to avoid. The cross-file join phase in `scanProject` always\n * re-runs over the full (cached + fresh) extract set, so a stale entry\n * can never produce an inconsistent `ScanResult` — the worst case of a\n * signature collision (same size, identical mtime, different content)\n * is a missed re-scan, which `--no-cache` / a `clean` sidesteps.\n *\n * @module @forinda/kickjs-cli/typegen/scanner-cache\n */\n\nimport { mkdir, readFile, stat, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { FileExtract } from './scanner'\n\n/** Bump when the shape of `FileExtract` (or any extractor) changes. */\n// v2: per-file extraction switched to AST-first (extract-ast.ts) — v1\n// entries hold regex-era results that can differ (template-literal\n// paths, aliased schema-ref resolution), so they must not be served.\nconst CACHE_VERSION = 2\n\n/** The array-valued keys every `FileExtract` must carry. */\nconst EXTRACT_ARRAY_KEYS = [\n  'classes',\n  'tokens',\n  'injects',\n  'pluginsAndAdapters',\n  'augmentations',\n  'contextKeys',\n  'routes',\n  'moduleMounts',\n  'globPatterns',\n] as const\n\n/**\n * Structurally validate a cached extract before trusting it. The join\n * phase spreads these arrays (`...extract.classes`), so a truncated or\n * hand-edited `scan.json` whose entry is missing a field would crash\n * the scanner. Rejecting the entry here lets the cache self-heal — the\n * file is treated as uncached and re-scanned.\n */\nfunction isFileExtract(value: unknown): value is FileExtract {\n  if (!value || typeof value !== 'object') return false\n  const extract = value as Record<string, unknown>\n  return EXTRACT_ARRAY_KEYS.every((key) => Array.isArray(extract[key]))\n}\n\n/** One cached file: its signature plus the extraction it produced. */\ninterface CacheEntry {\n  /** `${mtimeMs}:${size}` — cheap change signature, no content read. */\n  sig: string\n  extract: FileExtract\n}\n\n/** On-disk cache document. */\ninterface CacheDoc {\n  version: number\n  /** Keyed by absolute file path. */\n  files: Record<string, CacheEntry>\n}\n\n/**\n * In-memory + on-disk cache handle. Construct via `loadScanCache`,\n * consult with `get`, populate with `set`, and persist via `save`.\n * Entries for files no longer present on disk are dropped on `save`\n * (the scanner reports every live path through `markSeen`).\n */\nexport class ScanCache {\n  private readonly path: string\n  private readonly prev: Map<string, CacheEntry>\n  private readonly next = new Map<string, FileExtract>()\n  private readonly nextSig = new Map<string, string>()\n\n  private constructor(path: string, prev: Map<string, CacheEntry>) {\n    this.path = path\n    this.prev = prev\n  }\n\n  /**\n   * Load the cache for a given cache directory. A missing, unreadable,\n   * malformed, or version-mismatched cache yields an empty cache — the\n   * scan then behaves exactly like a cold first run.\n   */\n  static async load(cacheDir: string): Promise<ScanCache> {\n    const file = join(cacheDir, 'scan.json')\n    const prev = new Map<string, CacheEntry>()\n    try {\n      const raw = await readFile(file, 'utf-8')\n      const doc = JSON.parse(raw) as CacheDoc\n      if (doc.version === CACHE_VERSION && doc.files) {\n        for (const [path, entry] of Object.entries(doc.files)) {\n          if (entry && typeof entry.sig === 'string' && isFileExtract(entry.extract)) {\n            prev.set(path, entry)\n          }\n        }\n      }\n    } catch {\n      // Cold start — empty cache.\n    }\n    return new ScanCache(file, prev)\n  }\n\n  /** Compute the `mtimeMs:size` signature for a file, or null if stat fails. */\n  static async signature(filePath: string): Promise<string | null> {\n    try {\n      const s = await stat(filePath)\n      return `${s.mtimeMs}:${s.size}`\n    } catch {\n      return null\n    }\n  }\n\n  /**\n   * Return the cached extract for `filePath` iff its stored signature\n   * matches `sig`. A hit means the file is byte-identical to last scan\n   * (modulo an mtime+size collision) and need not be re-read.\n   */\n  get(filePath: string, sig: string): FileExtract | null {\n    const entry = this.prev.get(filePath)\n    return entry && entry.sig === sig ? entry.extract : null\n  }\n\n  /** Record a fresh (or reused) extract for the next `save`. */\n  set(filePath: string, sig: string, extract: FileExtract): void {\n    this.next.set(filePath, extract)\n    this.nextSig.set(filePath, sig)\n  }\n\n  /** Every file path present in the loaded (previous) cache. */\n  cachedFiles(): string[] {\n    return [...this.prev.keys()]\n  }\n\n  /**\n   * Read a previously-cached extract WITHOUT a signature check. Used by\n   * the incremental scan, where Vite has already told us precisely which\n   * files changed — so unchanged files are trusted as-is, skipping even\n   * the `stat()` a full scan would do.\n   */\n  peek(filePath: string): FileExtract | null {\n    return this.prev.get(filePath)?.extract ?? null\n  }\n\n  /**\n   * Carry a previously-cached entry forward into the next `save`,\n   * unchanged. Returns false if the file was not in the prior cache.\n   */\n  carry(filePath: string): boolean {\n    const entry = this.prev.get(filePath)\n    if (!entry) return false\n    this.next.set(filePath, entry.extract)\n    this.nextSig.set(filePath, entry.sig)\n    return true\n  }\n\n  /**\n   * Persist the cache. Only files passed through `set` this run survive,\n   * so entries for deleted files are pruned automatically. Best-effort:\n   * a write failure is swallowed (the cache is an optimization, never a\n   * correctness dependency).\n   */\n  async save(): Promise<void> {\n    const files: Record<string, CacheEntry> = {}\n    for (const [path, extract] of this.next) {\n      const sig = this.nextSig.get(path)\n      if (sig) files[path] = { sig, extract }\n    }\n    const doc: CacheDoc = { version: CACHE_VERSION, files }\n    try {\n      await mkdir(dirname(this.path), { recursive: true })\n      await writeFile(this.path, JSON.stringify(doc), 'utf-8')\n    } catch {\n      // Best-effort.\n    }\n  }\n}\n","/**\n * AST-based per-file extraction — oxc-parser replacement for the regex\n * extractors in `scanner.ts`.\n *\n * Produces the exact same {@link FileExtract} shape so the scanner's\n * cache / incremental / cross-file-join machinery is untouched. The\n * scanner calls {@link extractFileAst} first and falls back to the\n * regex path when the file doesn't parse (mid-edit syntax errors in\n * watch mode — regex still salvages partial results there).\n *\n * What AST extraction fixes over the regex path (forinda/kick-js#108):\n *   - template-literal route paths (`@Get(\\`/v1/users/:id\\`)`) — regex\n *     silently fell back to `/`\n *   - nested parens/braces inside stacked decorator args\n *   - string literals containing `(`/`)`/`{`/`}` skewing the\n *     balanced-delimiter walkers\n *   - aliased named imports in schema-ref resolution\n *\n * Everything here is pure (depends only on the source text), so results\n * stay safe to cache by filesystem signature.\n */\nimport { relative, sep } from 'node:path'\n\nimport { parseSync } from 'oxc-parser'\n\nimport {\n  DECORATOR_NAMES,\n  type DecoratorName,\n  type DiscoveredAugmentation,\n  type DiscoveredClass,\n  type DiscoveredContextKey,\n  type DiscoveredInject,\n  type DiscoveredPluginOrAdapter,\n  type DiscoveredRoute,\n  type DiscoveredToken,\n  type DecoratorRef,\n  type ContributorRefSet,\n  type FileExtract,\n  type ModuleMount,\n  type SchemaRef,\n} from './scanner'\n\n// scanner.ts ⇄ extract-ast.ts is an import cycle (scanner calls\n// extractFileAst; we need its DECORATOR_NAMES). Type imports are erased,\n// but reading the DECORATOR_NAMES *value* during this module's init\n// would hit the TDZ while scanner is still evaluating — so the set is\n// built lazily on first extraction instead.\nlet decoratorNameSet: Set<string> | null = null\nfunction getDecoratorNameSet(): Set<string> {\n  decoratorNameSet ??= new Set<string>(DECORATOR_NAMES)\n  return decoratorNameSet\n}\n\n// oxc-parser returns ESTree-shaped plain objects. We walk them\n// structurally; a tiny structural node type keeps us honest without\n// pulling in the full @oxc-project/types surface.\ninterface Node {\n  type: string\n  [key: string]: unknown\n}\n\nconst HTTP_DECORATORS = new Set(['Get', 'Post', 'Put', 'Delete', 'Patch'])\n\nfunction isNode(value: unknown): value is Node {\n  return typeof value === 'object' && value !== null && typeof (value as Node).type === 'string'\n}\n\n/** Depth-first walk over every AST node (objects with a string `type`). */\nfunction walk(node: unknown, visit: (node: Node) => void): void {\n  if (Array.isArray(node)) {\n    for (const item of node) walk(item, visit)\n    return\n  }\n  if (!isNode(node)) return\n  visit(node)\n  for (const key of Object.keys(node)) {\n    if (key === 'type') continue\n    const value = node[key]\n    if (typeof value === 'object' && value !== null) walk(value, visit)\n  }\n}\n\n/** Static string value of a literal / no-substitution template, else null. */\nfunction stringValue(node: unknown): string | null {\n  if (!isNode(node)) return null\n  if (node.type === 'Literal' && typeof node.value === 'string') return node.value\n  if (node.type === 'TemplateLiteral') {\n    const quasis = node.quasis as Node[] | undefined\n    const expressions = node.expressions as unknown[] | undefined\n    if (quasis?.length === 1 && (expressions?.length ?? 0) === 0) {\n      const cooked = (quasis[0] as { value?: { cooked?: string } }).value?.cooked\n      return typeof cooked === 'string' ? cooked : null\n    }\n  }\n  return null\n}\n\nfunction identifierName(node: unknown): string | null {\n  return isNode(node) && node.type === 'Identifier' ? (node.name as string) : null\n}\n\n/** Callee name for `name(...)` calls (plain identifier callees only). */\nfunction calleeName(call: Node): string | null {\n  return identifierName(call.callee)\n}\n\n/** Object-literal property lookup by static key name. */\nfunction getProp(obj: Node | null, name: string): Node | null {\n  if (!obj || obj.type !== 'ObjectExpression') return null\n  for (const prop of (obj.properties as Node[] | undefined) ?? []) {\n    if (prop.type !== 'Property') continue\n    const key = prop.key as Node\n    const keyName =\n      identifierName(key) ?? (key.type === 'Literal' ? String(key.value) : stringValue(key))\n    if (keyName === name) return prop.value as Node\n  }\n  return null\n}\n\n/** First argument when it's an object literal, else null. */\nfunction firstObjectArg(call: Node): Node | null {\n  const arg = (call.arguments as Node[] | undefined)?.[0]\n  return isNode(arg) && arg.type === 'ObjectExpression' ? arg : null\n}\n\nfunction extractStringArrayProp(obj: Node | null, key: string): string[] {\n  const value = getProp(obj, key)\n  if (!isNode(value) || value.type !== 'ArrayExpression') return []\n  const out: string[] = []\n  for (const el of (value.elements as unknown[] | undefined) ?? []) {\n    const s = stringValue(el)\n    if (s !== null) out.push(s)\n  }\n  return out\n}\n\nfunction toRelative(filePath: string, cwd: string): string {\n  return relative(cwd, filePath).split(sep).join('/')\n}\n\nfunction extractPathParams(path: string): string[] {\n  const matches = path.match(/:([a-zA-Z_]\\w*)/g) ?? []\n  return matches.map((m) => m.slice(1))\n}\n\n/** Mirrors scanner.ts joinMountPath — slash-edge handling for #235 §3. */\nfunction joinMountPath(mountPath: string, routePath: string): string {\n  const prefix = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n  if (!routePath || routePath === '/') return prefix || '/'\n  const suffix = routePath.startsWith('/') ? routePath : '/' + routePath\n  return prefix + suffix || '/'\n}\n\n/** `implements` clause includes `name`? */\nfunction classImplements(cls: Node, name: string): boolean {\n  for (const impl of (cls.implements as Node[] | undefined) ?? []) {\n    const expr = (impl.expression ?? impl) as Node\n    if (identifierName(expr) === name) return true\n    // Qualified form (`kick.AppModule`) — match the rightmost segment.\n    if (expr.type === 'TSQualifiedName' || expr.type === 'MemberExpression') {\n      const right = (expr.right ?? expr.property) as Node | undefined\n      if (right && identifierName(right) === name) return true\n    }\n  }\n  return false\n}\n\n/** Decorators attached to a class / method / property / param node. */\nfunction decoratorsOf(node: Node): Node[] {\n  return (node.decorators as Node[] | undefined) ?? []\n}\n\n/** `@Name(...)` decorator → { name, call } (call-expression form only). */\nfunction decoratorCall(dec: Node): { name: string; call: Node } | null {\n  const expr = dec.expression as Node\n  if (!isNode(expr) || expr.type !== 'CallExpression') return null\n  const name = calleeName(expr)\n  return name ? { name, call: expr } : null\n}\n\ninterface ImportBinding {\n  source: string\n}\n\n/**\n * Per-file context shared by the extract passes: import map (local\n * binding name → module specifier) and the set of top-level const names\n * (same-file schema refs resolve to the `''` sentinel).\n */\ninterface FileContext {\n  imports: Map<string, ImportBinding>\n  topLevelConsts: Set<string>\n}\n\nfunction buildFileContext(program: Node): FileContext {\n  const imports = new Map<string, ImportBinding>()\n  const topLevelConsts = new Set<string>()\n  for (const stmt of (program.body as Node[] | undefined) ?? []) {\n    if (stmt.type === 'ImportDeclaration') {\n      const source = stringValue(stmt.source) ?? ''\n      for (const spec of (stmt.specifiers as Node[] | undefined) ?? []) {\n        const local = identifierName(spec.local)\n        if (local) imports.set(local, { source })\n      }\n      continue\n    }\n    // `const X = ...` / `export const X = ...` at top level\n    const decl =\n      stmt.type === 'VariableDeclaration'\n        ? stmt\n        : stmt.type === 'ExportNamedDeclaration' && isNode(stmt.declaration)\n          ? (stmt.declaration as Node)\n          : null\n    if (isNode(decl) && decl.type === 'VariableDeclaration') {\n      for (const d of (decl.declarations as Node[] | undefined) ?? []) {\n        const name = identifierName(d.id)\n        if (name) topLevelConsts.add(name)\n      }\n    }\n  }\n  return { imports, topLevelConsts }\n}\n\n/**\n * Mirror of scanner.ts `resolveImportSource` semantics on top of the\n * AST import map: imported → module specifier, same-file const → `''`\n * sentinel, otherwise null.\n */\nfunction resolveSchemaRef(identifier: string, ctx: FileContext): SchemaRef {\n  const imported = ctx.imports.get(identifier)\n  if (imported) return { identifier, source: imported.source }\n  if (ctx.topLevelConsts.has(identifier)) return { identifier, source: '' }\n  return { identifier, source: null }\n}\n\n/**\n * Every decorator applied at a site, as `{ identifier, source }`, with the\n * HTTP verb decorators (which *are* the route) filtered out.\n *\n * Deliberately unclassified here: the extractor sees one file at a time\n * and can't know whether `@LoadTenant` resolves to a context contributor,\n * a framework decorator, or an adopter's own wrapper. The scanner's join\n * phase makes that call with the whole project in view — see\n * `resolveRouteContextKeys`. Keeping the policy in one place is what\n * makes \"degrade when unprovable\" auditable.\n */\nfunction appliedDecoratorRefs(decorators: Node[], ctx: FileContext): DecoratorRef[] {\n  const out: DecoratorRef[] = []\n  for (const dec of decorators) {\n    // `@Foo(...)` → callee identifier; `@Foo` → the expression itself.\n    const call = decoratorCall(dec)\n    const name = call ? call.name : identifierName((dec as { expression?: Node }).expression)\n    if (!name || HTTP_DECORATORS.has(name)) continue\n    const imported = ctx.imports.get(name)\n    out.push({\n      identifier: name,\n      // '' = declared in this same file, null = couldn't be resolved.\n      source: imported ? imported.source : ctx.topLevelConsts.has(name) ? '' : null,\n    })\n  }\n  return out\n}\n\n/** Entry points taking `ApplicationOptions`, keyed by their framework module. */\nconst APP_ENTRY_BINDINGS = new Set(['bootstrap', 'createWebApp', 'Application'])\n\n/**\n * Is `name` one of the framework's app entry points, imported from the\n * framework?\n *\n * The import check is load-bearing, not decoration. Matching on the bare\n * name would classify an adopter's own local `bootstrap()` wrapper as an\n * app-entry site and union whatever its options object called\n * `contributors` into EVERY route's key set — asserting keys that may not\n * exist. That is a false narrow, and under `require()` gating a false\n * narrow means a call that should compile does, or worse one that\n * shouldn't. Decorator bindings are matched against their declaration\n * file for the same reason (see `declarationMatchesImport`); this is the\n * equivalent check for call sites.\n *\n * An unrecognised callee simply isn't classified as app-level, so its\n * `contributors` falls through to `hasNonDecoratorContributors` and\n * degrades the project — the safe direction.\n */\nfunction isFrameworkAppEntry(name: string, ctx: FileContext): boolean {\n  if (!APP_ENTRY_BINDINGS.has(name)) return false\n  const source = ctx.imports.get(name)?.source\n  // `@forinda/kickjs` and its subpaths (`/web` exports createWebApp).\n  return source === '@forinda/kickjs' || (source?.startsWith('@forinda/kickjs/') ?? false)\n}\n\n/**\n * Collect the contributor bindings referenced inside a module's\n * `contributors()` hook.\n *\n * The two supported forms are the two the guide documents:\n *\n * ```ts\n * contributors: () => [LoadTenant.registration]\n * contributors: () => [LoadTenant.with({ source: 'subdomain' }).registration]\n * ```\n *\n * `resolved` is false when the hook contains anything else — a spread, a\n * helper call, a variable holding the array. Narrowing then degrades for\n * that module's routes rather than reporting a partial set, since a\n * missing key produces a false compile error on `ctx.require()`.\n */\nfunction collectContributorRefs(\n  hook: Node,\n  ctx: FileContext,\n): { refs: DecoratorRef[]; resolved: boolean } {\n  const unresolved = { refs: [] as DecoratorRef[], resolved: false }\n\n  // Structural, not a free walk. A walk that merely collects every\n  // `.registration` it can see reports `resolved: true` for a hook like\n  // `contributors: () => buildContributors()` — nothing to collect, so\n  // nothing looks wrong — and the route is then narrowed to a set that\n  // omits whatever the helper returns.\n  const returned = returnedExpression(hook)\n  if (!returned || returned.type !== 'ArrayExpression') return unresolved\n\n  const refs: DecoratorRef[] = []\n  for (const element of (returned.elements as unknown[] | undefined) ?? []) {\n    // A hole, a spread, or anything that isn't a plain member expression\n    // means we can't enumerate the list.\n    if (!isNode(element) || element.type !== 'MemberExpression') return unresolved\n    if (identifierName(element.property) !== 'registration') return unresolved\n\n    const object = element.object as Node\n    // `LoadTenant.registration`\n    let name = identifierName(object)\n    // `LoadTenant.with({...}).registration`\n    if (!name && isNode(object) && object.type === 'CallExpression') {\n      const callee = object.callee as Node\n      if (\n        isNode(callee) &&\n        callee.type === 'MemberExpression' &&\n        identifierName(callee.property) === 'with'\n      ) {\n        name = identifierName(callee.object)\n      }\n    }\n    if (!name) return unresolved\n\n    const imported = ctx.imports.get(name)\n    refs.push({\n      identifier: name,\n      source: imported ? imported.source : ctx.topLevelConsts.has(name) ? '' : null,\n    })\n  }\n\n  return { refs, resolved: true }\n}\n\n/**\n * The expression a hook returns: the body of a concise arrow, or the\n * argument of the single top-level `return` in a block body. Returns null\n * for anything less obvious (multiple returns, conditional returns),\n * which callers treat as unresolvable.\n */\nfunction returnedExpression(hook: Node): Node | null {\n  const value = (hook.value ?? hook) as Node\n  if (!isNode(value)) return null\n\n  // App-level `contributors: [X.registration]` is a bare array option, not\n  // a hook — `ApplicationOptions.contributors` is `ContributorRegistrations`\n  // where `AppModule.contributors` is `(): ContributorRegistrations`.\n  if (value.type === 'ArrayExpression') return value\n\n  const body = value.body as Node | undefined\n  if (!isNode(body)) return null\n\n  if (body.type !== 'BlockStatement') return body // concise arrow body\n\n  const statements = ((body.body as Node[] | undefined) ?? []).filter(\n    (s) => s.type === 'ReturnStatement',\n  )\n  if (statements.length !== 1) return null\n  const argument = statements[0].argument as Node | undefined\n  return isNode(argument) ? argument : null\n}\n\n/** Schema field (`body:` / `query:` / `params:`) — bare identifiers only. */\nfunction schemaFieldRef(options: Node | null, field: string, ctx: FileContext): SchemaRef | null {\n  const value = getProp(options, field)\n  const name = identifierName(value)\n  return name ? resolveSchemaRef(name, ctx) : null\n}\n\ninterface QueryParamsConfig {\n  filterable: string[]\n  sortable: string[]\n  searchable: string[]\n}\n\n/**\n * `@ApiQueryParams(...)` on a method — inline object literal or a\n * same-file const reference. Present-but-opaque (imported config,\n * column-object map, function call) → empty arrays, mirroring the\n * regex path's \"decorator exists but nothing statically extractable\".\n */\nfunction extractApiQueryParams(\n  methodDecorators: Node[],\n  topLevelConstInits: Map<string, Node>,\n): QueryParamsConfig | null {\n  for (const dec of methodDecorators) {\n    const dc = decoratorCall(dec)\n    if (!dc || dc.name !== 'ApiQueryParams') continue\n    const arg = (dc.call.arguments as Node[] | undefined)?.[0]\n    let obj: Node | null = null\n    if (isNode(arg) && arg.type === 'ObjectExpression') {\n      obj = arg\n    } else {\n      const refName = identifierName(arg)\n      if (refName) {\n        const init = topLevelConstInits.get(refName)\n        if (init && init.type === 'ObjectExpression') obj = init\n      }\n    }\n    return {\n      filterable: extractStringArrayProp(obj, 'filterable'),\n      sortable: extractStringArrayProp(obj, 'sortable'),\n      searchable: extractStringArrayProp(obj, 'searchable'),\n    }\n  }\n  return null\n}\n\n/**\n * Parse + extract one source file. Returns `null` when oxc reports\n * parse errors — the caller falls back to the regex extractors, which\n * tolerate broken mid-edit sources by matching whatever still looks\n * right.\n */\nexport function extractFileAst(source: string, filePath: string, cwd: string): FileExtract | null {\n  let program: Node\n  try {\n    const result = parseSync(filePath, source)\n    if (result.errors.length > 0) return null\n    program = result.program as unknown as Node\n  } catch {\n    return null\n  }\n\n  const relPath = toRelative(filePath, cwd)\n  const ctx = buildFileContext(program)\n\n  const classes: DiscoveredClass[] = []\n  const tokens: DiscoveredToken[] = []\n  const injects: DiscoveredInject[] = []\n  const pluginsAndAdapters: DiscoveredPluginOrAdapter[] = []\n  const augmentations: DiscoveredAugmentation[] = []\n  const contextKeys: DiscoveredContextKey[] = []\n  const routes: DiscoveredRoute[] = []\n  const moduleMounts: ModuleMount[] = []\n  const globPatterns: string[] = []\n\n  const seenHelperNames = new Set<string>()\n  const seenContextKeys = new Set<string>()\n\n  // `const LoadTenant = defineHttpContextDecorator({...})` — map the init\n  // CallExpression back to the binding it's assigned to, so an applied\n  // `@LoadTenant` decorator can later be resolved to the key it writes.\n  // The walk below visits call expressions without their parent, hence\n  // the pre-pass.\n  const bindingByInit = new Map<Node, string>()\n  walk(program, (node) => {\n    if (node.type !== 'VariableDeclarator') return\n    const name = identifierName(node.id)\n    const init = node.init\n    if (name && isNode(init)) bindingByInit.set(init, name)\n  })\n  const seenTokenNodes = new Set<Node>()\n  /** Top-level `const X = <init>` map for @ApiQueryParams const refs. */\n  const topLevelConstInits = new Map<string, Node>()\n\n  for (const stmt of (program.body as Node[] | undefined) ?? []) {\n    const decl =\n      stmt.type === 'VariableDeclaration'\n        ? stmt\n        : stmt.type === 'ExportNamedDeclaration' && isNode(stmt.declaration)\n          ? (stmt.declaration as Node)\n          : null\n    if (isNode(decl) && decl.type === 'VariableDeclaration') {\n      for (const d of (decl.declarations as Node[] | undefined) ?? []) {\n        const name = identifierName(d.id)\n        if (name && isNode(d.init)) topLevelConstInits.set(name, d.init as Node)\n      }\n    }\n  }\n\n  // ── Pass 1: top-level statements — classes + defineModule consts ──────\n  const exportedClasses: Array<{ cls: Node; isDefault: boolean }> = []\n  for (const stmt of (program.body as Node[] | undefined) ?? []) {\n    if (stmt.type === 'ExportNamedDeclaration' && isNode(stmt.declaration)) {\n      const d = stmt.declaration as Node\n      if (d.type === 'ClassDeclaration') exportedClasses.push({ cls: d, isDefault: false })\n    } else if (stmt.type === 'ExportDefaultDeclaration' && isNode(stmt.declaration)) {\n      const d = stmt.declaration as Node\n      if (d.type === 'ClassDeclaration') exportedClasses.push({ cls: d, isDefault: true })\n    }\n  }\n\n  for (const { cls, isDefault } of exportedClasses) {\n    const className = identifierName(cls.id)\n    if (!className) continue\n\n    // First call-form decorator whose name is in DECORATOR_NAMES — same\n    // pick as the regex (first in the stack wins).\n    let tagged: DecoratorName | null = null\n    for (const dec of decoratorsOf(cls)) {\n      const dc = decoratorCall(dec)\n      if (dc && getDecoratorNameSet().has(dc.name)) {\n        tagged = dc.name as DecoratorName\n        break\n      }\n    }\n    if (tagged) {\n      classes.push({ className, decorator: tagged, filePath, relativePath: relPath, isDefault })\n    } else if (classImplements(cls, 'AppModule')) {\n      classes.push({\n        className,\n        decorator: 'Module',\n        filePath,\n        relativePath: relPath,\n        isDefault,\n      })\n    }\n  }\n\n  // v4 factory modules: `export const XModule = defineModule({...})`\n  for (const stmt of (program.body as Node[] | undefined) ?? []) {\n    if (stmt.type !== 'ExportNamedDeclaration' || !isNode(stmt.declaration)) continue\n    const d = stmt.declaration as Node\n    if (d.type !== 'VariableDeclaration') continue\n    for (const declarator of (d.declarations as Node[] | undefined) ?? []) {\n      const name = identifierName(declarator.id)\n      const init = declarator.init as Node | undefined\n      if (!name || !isNode(init) || init.type !== 'CallExpression') continue\n      if (calleeName(init) !== 'defineModule') continue\n      if (classes.some((c) => c.className === name)) continue\n      classes.push({\n        className: name,\n        decorator: 'Module',\n        filePath,\n        relativePath: relPath,\n        isDefault: false,\n      })\n    }\n  }\n\n  // ── Pass 2: whole-tree walk — tokens, injects, helpers, globs ─────────\n  walk(program, (node) => {\n    // createToken('name') — const-bound (variable) or bare (null)\n    if (node.type === 'VariableDeclarator') {\n      const init = node.init as Node | undefined\n      if (isNode(init) && init.type === 'CallExpression' && calleeName(init) === 'createToken') {\n        const name = stringValue((init.arguments as Node[] | undefined)?.[0])\n        if (name !== null) {\n          seenTokenNodes.add(init)\n          tokens.push({\n            name,\n            variable: identifierName(node.id),\n            filePath,\n            relativePath: relPath,\n          })\n        }\n      }\n      return\n    }\n\n    if (node.type !== 'CallExpression') {\n      // import.meta.glob handled below (CallExpression); decorators are\n      // reached through their owning class/method nodes, but @Inject can\n      // appear on constructor params too — catch every decorator node.\n      if (node.type === 'Decorator') {\n        const dc = decoratorCall(node)\n        if (dc?.name === 'Inject') {\n          const lit = stringValue((dc.call.arguments as Node[] | undefined)?.[0])\n          if (lit !== null) injects.push({ name: lit, filePath, relativePath: relPath })\n        }\n      }\n      return\n    }\n\n    const callee = node.callee as Node\n    const name = calleeName(node)\n\n    // Bare createToken('name') not consumed by the declarator pass\n    if (name === 'createToken' && !seenTokenNodes.has(node)) {\n      const tokenName = stringValue((node.arguments as Node[] | undefined)?.[0])\n      if (tokenName !== null) {\n        tokens.push({ name: tokenName, variable: null, filePath, relativePath: relPath })\n      }\n      return\n    }\n\n    // defineAdapter({ name }) / definePlugin({ name })\n    if (name === 'defineAdapter' || name === 'definePlugin') {\n      const literal = stringValue(getProp(firstObjectArg(node), 'name'))\n      if (literal !== null) {\n        const kind = name === 'definePlugin' ? 'plugin' : 'adapter'\n        const dedupeKey = `${name}::${literal}::${filePath}`\n        if (!seenHelperNames.has(dedupeKey)) {\n          seenHelperNames.add(dedupeKey)\n          pluginsAndAdapters.push({ kind, name: literal, filePath, relativePath: relPath })\n        }\n      }\n      return\n    }\n\n    // defineAugmentation('Name', { description, example })\n    if (name === 'defineAugmentation') {\n      const args = (node.arguments as Node[] | undefined) ?? []\n      const augName = stringValue(args[0])\n      if (augName !== null) {\n        const meta = isNode(args[1]) && args[1].type === 'ObjectExpression' ? args[1] : null\n        augmentations.push({\n          name: augName,\n          description: stringValue(getProp(meta, 'description')),\n          example: stringValue(getProp(meta, 'example')),\n          filePath,\n          relativePath: relPath,\n        })\n      }\n      return\n    }\n\n    // defineContextDecorator({ key }) / defineHttpContextDecorator({ key })\n    // — direct form\n    if (name === 'defineContextDecorator' || name === 'defineHttpContextDecorator') {\n      const key = stringValue(getProp(firstObjectArg(node), 'key'))\n      if (key !== null && !seenContextKeys.has(key)) {\n        seenContextKeys.add(key)\n        contextKeys.push({\n          key,\n          exportName: bindingByInit.get(node) ?? null,\n          filePath,\n          relativePath: relPath,\n        })\n      }\n      return\n    }\n\n    // Curried form: define(Http)ContextDecorator.withParams<P>()({ key })\n    // — outer call's callee is the inner `withParams()` call.\n    if (isNode(callee) && callee.type === 'CallExpression') {\n      const innerCallee = callee.callee as Node\n      if (\n        isNode(innerCallee) &&\n        innerCallee.type === 'MemberExpression' &&\n        identifierName(innerCallee.property) === 'withParams'\n      ) {\n        const base = identifierName(innerCallee.object)\n        if (base === 'defineContextDecorator' || base === 'defineHttpContextDecorator') {\n          const key = stringValue(getProp(firstObjectArg(node), 'key'))\n          if (key !== null && !seenContextKeys.has(key)) {\n            seenContextKeys.add(key)\n            contextKeys.push({\n              key,\n              exportName: bindingByInit.get(node) ?? null,\n              filePath,\n              relativePath: relPath,\n            })\n          }\n        }\n      }\n      return\n    }\n\n    // import.meta.glob('...' | [...]) — module files only (caller gates)\n    if (\n      isNode(callee) &&\n      callee.type === 'MemberExpression' &&\n      identifierName(callee.property) === 'glob'\n    ) {\n      const obj = callee.object as Node\n      if (isNode(obj) && obj.type === 'MetaProperty') {\n        // Flatten every string literal across the args — matches the\n        // regex behaviour (single string + array forms, options object\n        // strings included only if literal; in practice options carry\n        // booleans).\n        walk(node.arguments, (argNode) => {\n          const s = stringValue(argNode)\n          if (s !== null) globPatterns.push(s)\n        })\n      }\n    }\n  })\n\n  // ── Pass 3: class bodies — routes + class-style adapters ─────────────\n  const allClasses: Array<{ cls: Node; className: string }> = []\n  walk(program, (node) => {\n    if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {\n      const className = identifierName(node.id)\n      if (className) allClasses.push({ cls: node, className })\n    }\n  })\n\n  for (const { cls, className } of allClasses) {\n    const discovered = classes.find((c) => c.className === className)\n    const body = (cls.body as Node | undefined)?.body as Node[] | undefined\n\n    // Class-style adapters: `class X implements AppAdapter { name = '...' }`\n    if (classImplements(cls, 'AppAdapter')) {\n      for (const member of body ?? []) {\n        if (member.type !== 'PropertyDefinition') continue\n        if (identifierName(member.key) !== 'name') continue\n        const literal = stringValue(member.value)\n        if (literal === null) continue\n        const dedupeKey = `class::${literal}::${filePath}`\n        if (!seenHelperNames.has(dedupeKey)) {\n          seenHelperNames.add(dedupeKey)\n          pluginsAndAdapters.push({\n            kind: 'adapter',\n            name: literal,\n            filePath,\n            relativePath: relPath,\n          })\n        }\n        break\n      }\n    }\n\n    for (const member of body ?? []) {\n      if (member.type !== 'MethodDefinition') continue\n      const methodName = identifierName(member.key)\n      if (!methodName) continue\n\n      // Module mounts: `routes() { ... }` bodies — zip path/controller\n      // pairs in source order (works for the class form; the object-\n      // method form inside defineModule(build) is handled below).\n      if (methodName === 'routes') {\n        collectMounts(member.value as Node, moduleMounts)\n        continue\n      }\n\n      // Route handlers — every HTTP decorator on the method emits one\n      // route (stacked verbs = multiple routes, same as the regex).\n      if (!discovered) continue\n      const decs = decoratorsOf(member)\n      const apiQp = extractApiQueryParams(decs, topLevelConstInits)\n      // Method- AND class-level decorators both contribute to the route's\n      // context keys (class-level applies to every method on it).\n      const appliedDecorators = [\n        ...appliedDecoratorRefs(decoratorsOf(cls), ctx),\n        ...appliedDecoratorRefs(decs, ctx),\n      ]\n      for (const dec of decs) {\n        const dc = decoratorCall(dec)\n        if (!dc || !HTTP_DECORATORS.has(dc.name)) continue\n        const args = (dc.call.arguments as Node[] | undefined) ?? []\n        const rawPath = stringValue(args[0])\n        const path = rawPath && rawPath.length > 0 ? rawPath : '/'\n        const options = isNode(args[1]) && args[1].type === 'ObjectExpression' ? args[1] : null\n\n        routes.push({\n          controller: className,\n          method: methodName,\n          httpMethod: dc.name.toUpperCase() as DiscoveredRoute['httpMethod'],\n          path,\n          // extractFile contract: own-path params only — the cross-file\n          // join re-applies mount prefixes (scanner.ts joinExtracts).\n          pathParams: extractPathParams(path),\n          queryFilterable: apiQp?.filterable ?? null,\n          querySortable: apiQp?.sortable ?? null,\n          querySearchable: apiQp?.searchable ?? null,\n          bodySchema: schemaFieldRef(options, 'body', ctx),\n          querySchema: schemaFieldRef(options, 'query', ctx),\n          paramsSchema: schemaFieldRef(options, 'params', ctx),\n          responseSchema: schemaFieldRef(options, 'response', ctx),\n          filePath,\n          relativePath: relPath,\n          controllerIsDefaultExport: discovered.isDefault,\n          appliedDecorators,\n          // Per-file contract: own path only (parity with the regex\n          // extractor's empty-mount default); joinExtracts overwrites with\n          // the cross-file mount-joined path.\n          mountedPath: path,\n        })\n      }\n    }\n  }\n\n  // Object-method `routes()` (defineModule build objects and friends).\n  walk(program, (node) => {\n    if (node.type !== 'Property' || identifierName(node.key) !== 'routes') return\n    const value = node.value as Node\n    if (\n      isNode(value) &&\n      (value.type === 'FunctionExpression' || value.type === 'ArrowFunctionExpression')\n    ) {\n      collectMounts(value, moduleMounts)\n    }\n  })\n\n  // ── Module-level `contributors()` ──────────────────────────────────\n  //\n  // `AppModule` declares `contributors?()` and `routes()` as siblings, so\n  // a `contributors` member sitting next to a `routes` member IS the\n  // module hook. That distinction matters: `bootstrap({ contributors })`\n  // and `definePlugin({ contributors })` apply app-wide and can't be\n  // attributed to any particular route, so they still force degradation.\n  // Anything not positively classified as module-level is treated as one\n  // of those.\n  const moduleContributorNodes = new Set<Node>()\n  const allContributorNodes = new Set<Node>()\n\n  walk(program, (node) => {\n    const isProp = node.type === 'Property' && identifierName(node.key) === 'contributors'\n    const isMethod = node.type === 'MethodDefinition' && identifierName(node.key) === 'contributors'\n    if (isProp || isMethod) allContributorNodes.add(node)\n  })\n\n  /** Members of an object literal or class body, whichever this is. */\n  const membersOf = (container: Node): Node[] =>\n    ((container.properties ?? (container.body as Node | undefined)?.body) as Node[] | undefined) ??\n    []\n\n  walk(program, (node) => {\n    if (node.type !== 'ObjectExpression' && node.type !== 'ClassDeclaration') return\n    const members = membersOf(node)\n    const names = members.map((m) => identifierName(m.key))\n    if (!names.includes('routes')) return\n    for (const member of members) {\n      if (identifierName(member.key) === 'contributors') moduleContributorNodes.add(member)\n    }\n  })\n\n  // App-level `contributors: [...]` — `bootstrap()`, `createWebApp()`, and\n  // `new Application()` all take `ApplicationOptions`. These apply to\n  // EVERY route, so unlike a module hook they need no attribution: their\n  // keys union into all of them.\n  const appContributorNodes = new Set<Node>()\n  walk(program, (node) => {\n    const callee =\n      node.type === 'CallExpression'\n        ? calleeName(node)\n        : node.type === 'NewExpression'\n          ? identifierName(node.callee)\n          : null\n    if (callee === null) return\n    if (!isFrameworkAppEntry(callee, ctx)) return\n    const options = firstObjectArg(node)\n    if (!options) return\n    for (const prop of (options.properties as Node[] | undefined) ?? []) {\n      if (prop.type === 'Property' && identifierName(prop.key) === 'contributors') {\n        appContributorNodes.add(prop)\n      }\n    }\n  })\n\n  const collectFrom = (nodes: Set<Node>): ContributorRefSet | null => {\n    if (nodes.size === 0) return null\n    const refs: DecoratorRef[] = []\n    let resolved = true\n    for (const node of nodes) {\n      const collected = collectContributorRefs(node, ctx)\n      if (!collected.resolved) resolved = false\n      refs.push(...collected.refs)\n    }\n    return { refs, resolved, filePath }\n  }\n\n  const moduleContributors = collectFrom(moduleContributorNodes)\n  const appContributors = collectFrom(appContributorNodes)\n\n  return {\n    classes,\n    tokens,\n    injects,\n    pluginsAndAdapters,\n    augmentations,\n    contextKeys,\n    routes,\n    moduleMounts,\n    globPatterns: /\\.module\\.[mc]?[tj]sx?$/.test(filePath) ? globPatterns : [],\n    moduleContributors,\n    appContributors,\n    // Whatever is left after module hooks and app-entry options is an\n    // adapter or plugin `contributors` — those ship from packages whose\n    // bodies we can't read, so they still force degradation.\n    hasNonDecoratorContributors:\n      allContributorNodes.size > moduleContributorNodes.size + appContributorNodes.size,\n  }\n}\n\n/**\n * Collect `{ path: '...', controller: Ident }` pairs from a routes()\n * function body — zipped in traversal order, shorter list wins, same\n * as the regex `extractModuleMounts`.\n */\nfunction collectMounts(fn: Node, out: ModuleMount[]): void {\n  const paths: string[] = []\n  const controllers: string[] = []\n  walk(fn.body, (node) => {\n    if (node.type !== 'Property') return\n    const key = identifierName(node.key)\n    if (key === 'path') {\n      const s = stringValue(node.value)\n      if (s !== null) paths.push(s)\n    } else if (key === 'controller') {\n      const id = identifierName(node.value)\n      if (id && /^[A-Z]/.test(id)) controllers.push(id)\n    }\n  })\n  const n = Math.min(paths.length, controllers.length)\n  for (let i = 0; i < n; i++) {\n    out.push({ controller: controllers[i], mountPath: paths[i] })\n  }\n}\n\n/**\n * `joinExtracts` re-derives `pathParams` with the mount prefix — the\n * scanner needs the same joiner the regex path used; re-exported here\n * so both implementations share one definition is unnecessary (the\n * scanner keeps its own). This export exists for tests only.\n */\nexport const __testing = { joinMountPath, extractPathParams }\n","/**\n * Static scanner for KickJS decorated classes and DI tokens.\n *\n * Walks `src/**\\/*.ts` (excluding tests and node_modules) and extracts:\n *\n * - Decorated classes (`@Service`, `@Controller`, `@Repository`, etc.)\n * - `createToken<T>('name')` definitions\n * - `@Inject('literal')` calls\n *\n * The output feeds the type generator, which emits `.kickjs/types/*.d.ts`\n * files used by the user's tsc to make `container.resolve()` and module\n * discovery type-safe.\n *\n * This is intentionally regex-based (not AST-based) to avoid the\n * ts-morph / typescript compiler dependency. Pattern from\n * `packages/vite/src/module-discovery.ts` which already uses regex\n * to detect `*.module.ts` exports.\n *\n * ## Collision detection\n *\n * Two classes with the same name across different files is a collision.\n * The scanner records all collisions in `ScanResult.collisions` so the\n * caller (generator) can decide whether to hard-error or auto-namespace.\n *\n * @module @forinda/kickjs-cli/typegen/scanner\n */\n\nimport type { Dirent } from 'node:fs'\nimport { readdir, readFile } from 'node:fs/promises'\nimport { dirname, join, relative, resolve, sep } from 'node:path'\nimport { ScanCache } from './scanner-cache'\nimport { extractFileAst } from './extract-ast'\n\n/** Decorators that mark a class as DI-managed */\nexport const DECORATOR_NAMES = [\n  'Service',\n  'Controller',\n  'Repository',\n  'Injectable',\n  'Component',\n  'Module',\n] as const\n\nexport type DecoratorName = (typeof DECORATOR_NAMES)[number]\n\n/** A single discovered decorated class */\nexport interface DiscoveredClass {\n  /** Class name (e.g., 'UserService') */\n  className: string\n  /** Decorator that marked it (e.g., 'Service') */\n  decorator: DecoratorName\n  /** Absolute file path */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n  /** True if exported as `default` */\n  isDefault: boolean\n}\n\n/** A single route handler discovered on a controller class */\nexport interface DiscoveredRoute {\n  /** Owning controller class name (e.g. 'UserController') */\n  controller: string\n  /** Handler method name on the controller (e.g. 'getUser') */\n  method: string\n  /** HTTP verb (uppercase) */\n  httpMethod: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'\n  /** Route path including parameter placeholders (e.g. '/:id/posts/:postId') */\n  path: string\n  /** URL path parameter names extracted from `:placeholder` segments */\n  pathParams: string[]\n  /**\n   * Whitelisted query field names extracted from `@ApiQueryParams({...})`.\n   * `null` means no `@ApiQueryParams` was found on this method (so the\n   * generator emits an unconstrained `query` shape). An empty array means\n   * the decorator existed but no fields could be statically extracted\n   * (e.g. an opaque imported config).\n   */\n  queryFilterable: string[] | null\n  querySortable: string[] | null\n  querySearchable: string[] | null\n  /**\n   * Schema identifiers referenced from the route decorator's second arg\n   * (e.g. `@Post('/', { body: createTaskSchema })`). `null` means no\n   * such reference; the value carries the identifier and the resolved\n   * import source (relative module path) if known.\n   */\n  bodySchema: SchemaRef | null\n  querySchema: SchemaRef | null\n  paramsSchema: SchemaRef | null\n  /**\n   * Declared response contract from the route decorator\n   * (`@Get('/', { response: schema })`). When present it WINS over\n   * return-type inference in the emitted `response` field — and the same\n   * declaration drives the Swagger success-response schema.\n   */\n  responseSchema?: SchemaRef | null\n  /** Absolute file path of the controller */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n  /**\n   * True when the controller class is `export default` — the response\n   * typegen hoists `import type { default as _Cn }` instead of a named\n   * import. Optional so regex-path extraction and old fixtures stay valid.\n   */\n  controllerIsDefaultExport?: boolean\n  /**\n   * Module-mount-joined path (`/tasks/:id` for a controller mounted at\n   * `/tasks` with `@Get('/:id')`). Excludes the bootstrap `/api/v{n}`\n   * prefix (not statically scannable). The KickRoutes.Api flat map keys\n   * on this — the bare `path` would collide across controllers and 404\n   * against real URLs. Optional for old fixtures; falls back to `path`.\n   */\n  mountedPath?: string\n  /**\n   * Every decorator applied to this route's method or its controller\n   * class, minus the HTTP verb decorators. Unclassified on purpose — the\n   * join phase decides which are context contributors, which are known\n   * framework decorators, and which are unrecognised (and therefore make\n   * the route's key set unprovable).\n   */\n  appliedDecorators?: DecoratorRef[]\n  /**\n   * Union of context keys proven populated for this route, or `null` when\n   * completeness could not be established. `null` is emitted as `string`\n   * (no narrowing) — never as an empty union, which would wrongly reject\n   * every `ctx.require()` call on the route.\n   */\n  contextKeys?: string[] | null\n}\n\n/**\n * Contributor bindings collected from one registration site — a module's\n * `contributors()` hook, or an app-level `contributors: [...]` option.\n */\nexport interface ContributorRefSet {\n  refs: DecoratorRef[]\n  /** False when the hook contained an entry the extractor couldn't name. */\n  resolved: boolean\n  /**\n   * The file the registration lives in. Relative import specifiers inside\n   * it resolve against THIS file, not against the controller's — a module\n   * (or `src/index.ts`) and the controllers it affects routinely sit in\n   * different directories.\n   */\n  filePath: string\n}\n\n/** A decorator as written at a call site, with its import resolved. */\nexport interface DecoratorRef {\n  /** The decorator's local binding name, e.g. `LoadTenant`. */\n  identifier: string\n  /**\n   * Module specifier it was imported from; `''` when declared in the same\n   * file, `null` when the binding could not be resolved at all.\n   */\n  source: string | null\n}\n\n/** A statically-resolved schema identifier reference */\nexport interface SchemaRef {\n  /** The identifier as written (e.g. `createTaskSchema`) */\n  identifier: string\n  /**\n   * Resolved module specifier (relative path or bare module name) where\n   * the identifier is defined. `null` means the source could not be\n   * statically determined (the generator falls back to `unknown`).\n   */\n  source: string | null\n}\n\n/** A `createToken<T>('name')` call discovered in source */\nexport interface DiscoveredToken {\n  /** The literal string passed to `createToken()` */\n  name: string\n  /** The const variable name on the LHS, if any */\n  variable: string | null\n  /** Absolute file path */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n}\n\n/** An `@Inject('literal')` call discovered in source */\nexport interface DiscoveredInject {\n  /** The literal string passed to `@Inject()` */\n  name: string\n  /** Absolute file path */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n}\n\n/** A name collision — same class name in two or more files */\nexport interface ClassCollision {\n  /** The colliding class name */\n  className: string\n  /** All files declaring the class */\n  classes: DiscoveredClass[]\n}\n\n/**\n * Information about a discovered env schema file. The typegen\n * generator uses this to emit a `KickEnv` + `NodeJS.ProcessEnv`\n * augmentation that flows through to `@Value` and `process.env`.\n *\n * `null` means no env file was found at the configured location.\n */\nexport interface DiscoveredEnv {\n  /** Absolute path to the env schema file */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n}\n\n/**\n * A plugin or adapter discovered in source — either via `defineAdapter({ name })`\n * / `definePlugin({ name })` calls, or via a class that `implements AppAdapter`\n * and declares a string-literal `name` field.\n *\n * The `name` here is the literal string passed to the framework (the value\n * `dependsOn` references), NOT the symbol on the LHS. `defineAdapter` lets\n * authors choose any name they want; the symbol is irrelevant at runtime.\n */\nexport interface DiscoveredPluginOrAdapter {\n  /** Whether this is a plugin (`definePlugin`) or adapter (`defineAdapter` / class) */\n  kind: 'plugin' | 'adapter'\n  /** The string literal passed as `name` (the value `dependsOn` references) */\n  name: string\n  /** Absolute file path */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n}\n\n/**\n * A context key discovered from a `defineContextDecorator({ key })` or\n * `defineHttpContextDecorator({ key })` call (including the curried\n * `.withParams<P>()({ key })` form). Feeds the `kick/context` typegen\n * plugin, which emits the `ContextKeys` augmentation so `dependsOn`\n * typo-checking is automatic and complete.\n */\nexport interface DiscoveredContextKey {\n  /** The literal `key:` value the contributor writes. */\n  key: string\n  /**\n   * The binding the decorator was assigned to — `LoadTenant` for\n   * `export const LoadTenant = defineHttpContextDecorator({ key: 'tenant' })`.\n   *\n   * `null` when the call isn't a simple `const X = …` initialiser (inline\n   * in an array, returned from a factory, …). Needed to map an applied\n   * `@LoadTenant` decorator back to the key it populates, which is what\n   * lets per-route context-key narrowing work at all; a `null` binding\n   * simply can't be resolved from a decorator site, so routes using it\n   * degrade to unnarrowed rather than guessing.\n   */\n  exportName: string | null\n  /** Absolute file path. */\n  filePath: string\n  /** Path relative to scan root, with forward slashes. */\n  relativePath: string\n}\n\n/**\n * A `defineAugmentation('Name', meta)` call discovered in source. Plugins\n * call this to advertise an augmentable interface so the typegen can list\n * every augmentation surface in one generated file.\n */\nexport interface DiscoveredAugmentation {\n  /** The literal string passed as the first arg to `defineAugmentation` */\n  name: string\n  /** Optional `description` extracted from the second-arg object literal */\n  description: string | null\n  /** Optional `example` extracted from the second-arg object literal */\n  example: string | null\n  /** Absolute file path */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n}\n\n/**\n * A decorated class whose file sits inside a module directory but\n * isn't picked up by any of the module's `import.meta.glob(...)`\n * patterns. Surfaced as a typegen warning per forinda/kick-js#235 §4\n * so adopters notice silent registration drift before it bites them\n * at runtime with a `MissingContributorError` or wrong code path.\n */\nexport interface OrphanedClass {\n  /** The decorated class name */\n  className: string\n  /** Absolute path of the class file */\n  filePath: string\n  /** Path relative to scan root, with forward slashes */\n  relativePath: string\n  /** Absolute path of the module file whose globs didn't match */\n  moduleFilePath: string\n  /** The decorator name (`Service`, `Controller`, `Repository`, …) */\n  decorator: DecoratorName\n}\n\n/** Aggregated scanner output */\nexport interface ScanResult {\n  classes: DiscoveredClass[]\n  routes: DiscoveredRoute[]\n  tokens: DiscoveredToken[]\n  injects: DiscoveredInject[]\n  collisions: ClassCollision[]\n  /** Discovered env schema file (or null if none found at the configured path) */\n  env: DiscoveredEnv | null\n  /** Plugins/adapters discovered via `defineAdapter`/`definePlugin`/`implements AppAdapter` */\n  pluginsAndAdapters: DiscoveredPluginOrAdapter[]\n  /** Augmentation interfaces declared via `defineAugmentation('Name', meta)` */\n  augmentations: DiscoveredAugmentation[]\n  /** Context keys from `define(Http)ContextDecorator({ key })` calls */\n  contextKeys: DiscoveredContextKey[]\n  /**\n   * Decorated classes that sit inside a module directory but aren't\n   * picked up by any of the module's `import.meta.glob(...)` patterns.\n   * Empty when every decorator file is matched. forinda/kick-js#235 §4.\n   */\n  orphanedClasses: OrphanedClass[]\n}\n\n/** Options for the scanner */\nexport interface ScanOptions {\n  /** Root directory to scan (e.g., absolute path to `src`) */\n  root: string\n  /** Project root used to compute relative paths (e.g., process.cwd()) */\n  cwd: string\n  /** Glob-like extensions to scan */\n  extensions?: string[]\n  /** Substrings that exclude a path (matched against relative path) */\n  exclude?: string[]\n  /**\n   * Path to the env schema file, relative to `cwd`. Defaults to\n   * `'src/env.ts'`. The file must contain a `defineEnv(...)` call\n   * with a default export for the typegen to emit a typed `KickEnv`\n   * augmentation. If the file does not exist or doesn't match the\n   * expected shape, env typing is skipped silently.\n   */\n  envFile?: string\n  /**\n   * Directory for the persistent per-file extraction cache. When set,\n   * unchanged files (matched by `mtimeMs:size` signature) are served\n   * from `<cacheDir>/scan.json` instead of being re-read and re-scanned.\n   * Omit to disable caching (every scan is a cold read — the original\n   * behaviour). Typically `<cwd>/.kickjs/cache`.\n   */\n  cacheDir?: string\n}\n\nconst DEFAULT_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts']\nconst DEFAULT_EXCLUDES = ['node_modules', '.kickjs', 'dist', 'build', '.test.', '.spec.', '.d.ts']\n\n/**\n * Match a class-level decorator immediately followed by an exported\n * class declaration. Captures decorator name and class name.\n */\nconst DECORATED_CLASS_REGEX = new RegExp(\n  String.raw`@(${DECORATOR_NAMES.join('|')})\\s*\\([^)]*\\)` +\n    String.raw`(?:\\s*@[A-Z]\\w*(?:\\s*\\([^)]*\\))?)*` +\n    String.raw`\\s*export\\s+(default\\s+)?(?:abstract\\s+)?class\\s+(\\w+)`,\n  'g',\n)\n\n/**\n * Match an exported class declaration that implements `AppModule`.\n * KickJS modules are not decorated — they implement the `AppModule`\n * interface — so the decorated-class scanner never picks them up. This\n * regex captures them by name so `ModuleToken` can be populated.\n *\n * Tolerates an `extends BaseClass` clause before `implements`, multiple\n * implements clauses (`implements Foo, AppModule`), and `default` exports.\n */\nconst APP_MODULE_CLASS_REGEX = new RegExp(\n  String.raw`export\\s+(default\\s+)?(?:abstract\\s+)?class\\s+(\\w+)` +\n    String.raw`(?:\\s+extends\\s+\\w+(?:<[^>]*>)?)?` +\n    String.raw`\\s+implements\\s+[^{]*\\bAppModule\\b`,\n  'g',\n)\n\n/**\n * Match the v4 module factory form\n * `export const XModule = defineModule({ ... })` (the `class implements\n * AppModule` form above is the deprecated v3 style). The const name is\n * the module's identity and becomes its `ModuleToken` entry. An\n * optional generic / type annotation on the const is tolerated.\n */\nconst DEFINE_MODULE_REGEX =\n  /export\\s+const\\s+(\\w+)\\s*(?::\\s*[^=]+)?=\\s*defineModule\\s*(?:<[^>]*>)?\\s*\\(/g\n\n/**\n * Match a `createToken<T>('name')` call with optional `export const X =`\n * or `const X =` prefix. Tolerates whitespace and the type parameter\n * being absent (`createToken('name')`).\n */\nconst CREATE_TOKEN_REGEX =\n  /(?:export\\s+)?const\\s+(\\w+)\\s*(?::\\s*[^=]+)?=\\s*createToken\\s*(?:<[^>]*>)?\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]\\s*\\)/g\n\n/**\n * Match a bare `createToken<T>('name')` call (no const assignment) so\n * we still pick up dynamically-used tokens.\n */\nconst BARE_CREATE_TOKEN_REGEX = /createToken\\s*(?:<[^>]*>)?\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]\\s*\\)/g\n\n/** Match `@Inject('literal')` — only literals; computed args are skipped */\nconst INJECT_LITERAL_REGEX = /@Inject\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]\\s*\\)/g\n\n/**\n * Match the start of a `defineAdapter(...)` or `definePlugin(...)` call,\n * tolerating optional `<TConfig, TExtra>` generics. Captures the helper\n * name. The callsite's first-arg object is parsed forward via\n * `findBalancedClose` so nested objects/parens don't confuse us.\n */\nconst DEFINE_HELPER_START = /\\b(defineAdapter|definePlugin)\\s*(?:<[^>]*>)?\\s*\\(/g\n\n/**\n * Match the start of a `defineContextDecorator(...)` /\n * `defineHttpContextDecorator(...)` call up to the `(` that opens the\n * spec object — tolerating optional `<...>` generics AND the curried\n * `.withParams<P>()(...)` form (the empty `()` is consumed so the next\n * `(` is the spec). The spec's `key:` literal is then read forward via\n * `findBalancedClose`, mirroring `DEFINE_HELPER_START`.\n */\n// `(?:<(?:[^<>]|<[^<>]*>)*>)?` tolerates one level of nested generics\n// (e.g. `defineHttpContextDecorator<'tenant', Record<string, never>>`),\n// which a flat `<[^>]*>` would truncate at the inner `>`.\nconst CONTEXT_DECORATOR_START =\n  /(?:(?:export\\s+)?(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*(?::[^=]+)?=\\s*)?\\b(?:defineContextDecorator|defineHttpContextDecorator)\\s*(?:\\.withParams\\s*<(?:[^<>]|<[^<>]*>)*>\\s*\\(\\s*\\))?\\s*(?:<(?:[^<>]|<[^<>]*>)*>)?\\s*\\(/g\n\n/**\n * Match a class declaration whose `implements` clause includes `AppAdapter`.\n * Captures the class name. Used to pick up the (rare, post-defineAdapter)\n * legacy class-style adapters so their literal `name = '...'` field can\n * still feed `KickJsPluginRegistry`.\n */\nconst APP_ADAPTER_CLASS_REGEX = new RegExp(\n  String.raw`export\\s+(?:default\\s+)?(?:abstract\\s+)?class\\s+(\\w+)` +\n    String.raw`(?:\\s+extends\\s+\\w+(?:<[^>]*>)?)?` +\n    String.raw`\\s+implements\\s+[^{]*\\bAppAdapter\\b`,\n  'g',\n)\n\n/** Match a string-literal `name = '...'` field on a class body. */\nconst CLASS_NAME_FIELD_REGEX = /\\bname\\s*(?::\\s*[^=]+)?=\\s*['\"`]([^'\"`]+)['\"`]/\n\n/**\n * Match the start of a `defineAugmentation('Name', ...)` call. Captures\n * the literal name. The optional second-arg object is parsed forward so\n * `description` / `example` can be pulled out.\n */\nconst DEFINE_AUGMENTATION_START = /\\bdefineAugmentation\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]\\s*(,\\s*\\{)?/g\n\n/** HTTP route decorator names recognised by the scanner */\nconst HTTP_DECORATORS = ['Get', 'Post', 'Put', 'Delete', 'Patch'] as const\n\n/**\n * Locate the start of a route decorator: `@Get(`, `@Post(`, etc.\n * Used by `extractRoutesFromSource`; the rest of the route declaration\n * (balanced parens, stacked decorators, method name) is parsed by walking\n * the source forward from this match. The previous all-in-one regex\n * couldn't handle nested parens in stacked decorator args (e.g.\n * `@ApiResponse(201, { schema: z.object({ id: z.string() }) })`) — see\n * forinda/kick-js#108.\n */\nconst ROUTE_DECORATOR_START = new RegExp(String.raw`@(${HTTP_DECORATORS.join('|')})\\s*\\(`, 'g')\n\n/**\n * Find the index of the `)` that balances the `(` at `openPos`.\n * Returns -1 if no matching `)` exists. Counts balanced parens only;\n * does not understand string literals, so a `(` or `)` inside a string\n * inside the args will skew the depth counter (matches the limitation\n * of `extractRouteOptionsArg`).\n */\nfunction findBalancedClose(text: string, openPos: number): number {\n  let depth = 1\n  for (let i = openPos + 1; i < text.length; i++) {\n    const ch = text[i]\n    if (ch === '(') depth++\n    else if (ch === ')') {\n      depth--\n      if (depth === 0) return i\n    }\n  }\n  return -1\n}\n\n/**\n * Walk forward from the end of a route decorator past any stacked\n * decorators (`@ApiOperation(...)`, `@ApiResponse(...)`, `@Middleware(fn)`,\n * etc.), then past optional `public`/`private`/`protected` and `async`,\n * and capture the method name + opening `(`.\n *\n * Returns the method name and the position immediately after the method's\n * opening `(`, or `null` if the source between the route decorator and\n * the method body doesn't fit the expected shape.\n */\nfunction readMethodAfterDecorators(\n  block: string,\n  startPos: number,\n): { methodName: string; endPos: number } | null {\n  let pos = startPos\n  // Stacked decorators: @PascalCase optionally followed by balanced (...)\n  while (pos < block.length) {\n    while (pos < block.length && /\\s/.test(block[pos])) pos++\n    if (block[pos] !== '@') break\n    const decMatch = block.slice(pos).match(/^@([A-Z]\\w*)/)\n    if (!decMatch) break\n    pos += decMatch[0].length\n    while (pos < block.length && /\\s/.test(block[pos])) pos++\n    if (block[pos] === '(') {\n      const close = findBalancedClose(block, pos)\n      if (close < 0) return null\n      pos = close + 1\n    }\n  }\n  // Modifiers + async\n  while (pos < block.length && /\\s/.test(block[pos])) pos++\n  for (const mod of ['public', 'private', 'protected'] as const) {\n    if (block.slice(pos, pos + mod.length) === mod && /\\s/.test(block.charAt(pos + mod.length))) {\n      pos += mod.length\n      while (pos < block.length && /\\s/.test(block[pos])) pos++\n      break\n    }\n  }\n  if (block.slice(pos, pos + 5) === 'async' && /\\s/.test(block.charAt(pos + 5))) {\n    pos += 5\n    while (pos < block.length && /\\s/.test(block[pos])) pos++\n  }\n  // Method name + `(`\n  const methodMatch = block.slice(pos).match(/^([a-zA-Z_]\\w*)\\s*\\(/)\n  if (!methodMatch) return null\n  return { methodName: methodMatch[1], endPos: pos + methodMatch[0].length }\n}\n\n/** Extract `:placeholder` segments from an Express route path */\nfunction extractPathParams(path: string): string[] {\n  const matches = path.match(/:([a-zA-Z_]\\w*)/g) ?? []\n  return matches.map((m) => m.slice(1))\n}\n\n/**\n * Join a controller's mount-prefix path with a per-route path.\n * Handles the slash edge cases so `'/orgs/:id'` + `'/'` becomes\n * `'/orgs/:id'` (no trailing slash) and `'/orgs/:id'` + `'/:code'`\n * becomes `'/orgs/:id/:code'`. forinda/kick-js#235 §3.\n */\nfunction joinMountPath(mountPath: string, routePath: string): string {\n  const prefix = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath\n  if (!routePath || routePath === '/') return prefix || '/'\n  const suffix = routePath.startsWith('/') ? routePath : '/' + routePath\n  return prefix + suffix || '/'\n}\n\n/**\n * Match the `routes()` method body on a class implementing `AppModule`.\n * Captures the body region so we can scan it for `path:` + `controller:`\n * pairs. Tolerates `routes(): ModuleRoutes` and stripped return type.\n */\nconst ROUTES_METHOD_START =\n  /\\b(?:public\\s+|private\\s+|protected\\s+)?routes\\s*\\([^)]*\\)\\s*(?::\\s*[A-Za-z_][\\w<>[\\]\\s,|]*\\s*)?\\{/g\n\n/**\n * Match `path: '/...'` inside a routes-method body. Picks up both\n * single-quoted and double-quoted / template literal forms.\n */\nconst PATH_FIELD_REGEX = /\\bpath\\s*:\\s*['\"`]([^'\"`]*)['\"`]/g\n\n/** Match `controller: SomeController` (bare identifier only). */\nconst CONTROLLER_FIELD_REGEX = /\\bcontroller\\s*:\\s*([A-Z]\\w*)\\b/g\n\n/**\n * Match the start of an `import.meta.glob(...)` call. The first arg\n * (string or string array) gets parsed forward via balanced-paren\n * walking to handle whitespace + line breaks inside the array.\n * forinda/kick-js#235 §4.\n */\nconst IMPORT_META_GLOB_START = /\\bimport\\.meta\\.glob\\s*\\(/g\n\n/**\n * Extract every glob pattern from `import.meta.glob([...patterns], ...)` calls\n * in a module file. Single-string form (`import.meta.glob('./**\\\\/*.ts')`) and\n * array form both supported. Negation patterns (`!./**\\\\/*.test.ts`)\n * are returned with the leading `!` preserved so the caller can apply\n * exclusion logic.\n */\nexport function extractGlobPatterns(source: string): string[] {\n  const patterns: string[] = []\n  IMPORT_META_GLOB_START.lastIndex = 0\n  while (IMPORT_META_GLOB_START.exec(source) !== null) {\n    const openParen = IMPORT_META_GLOB_START.lastIndex - 1\n    const closeParen = findBalancedClose(source, openParen)\n    if (closeParen < 0) continue\n    const args = source.slice(openParen + 1, closeParen)\n    // Pull out every string literal inside the first-arg region —\n    // we don't bother distinguishing the array form from the bare\n    // string; both end up as flat patterns.\n    const literalRe = /['\"`]([^'\"`]+)['\"`]/g\n    let lit: RegExpExecArray | null\n    while ((lit = literalRe.exec(args)) !== null) {\n      patterns.push(lit[1] as string)\n    }\n  }\n  return patterns\n}\n\n/**\n * Convert a Vite-style glob (e.g. `./**\\\\/*.controller.ts`) to a\n * RegExp. Supports `**` (any path segments including `/`), `*` (any\n * chars within one segment), `?` (single char). Brace alternation is\n * intentionally not handled — none of the templated globs use it,\n * and a false-negative on an unusual pattern is safer than a false-\n * positive (better to skip the warning than to wrongly silence one).\n */\nfunction globToRegex(pattern: string): RegExp {\n  // Process `?` before any of the substitutions that insert `?` into\n  // the output (e.g. the `(?:.+/)?` non-capture group for `**/`).\n  // If we left it for last, the `?` → `.` pass would mangle those\n  // groups into `(.:.+\\/).` — broken regex.\n  const escaped = pattern\n    .replace(/[.+^$()|[\\]\\\\]/g, '\\\\$&')\n    .replace(/\\?/g, '.')\n    .replace(/\\*\\*\\//g, '___DOUBLESTAR_SLASH___')\n    .replace(/\\*\\*/g, '___DOUBLESTAR___')\n    .replace(/\\*/g, '[^/]*')\n    .replace(/___DOUBLESTAR_SLASH___/g, '(?:.+/)?')\n    .replace(/___DOUBLESTAR___/g, '.*')\n  return new RegExp('^' + escaped + '$')\n}\n\n/**\n * Decide whether a file (relative to the module file's directory)\n * matches any of the module's positive glob patterns. Negation\n * patterns (`!./**\\\\/*.test.ts`) subtract; a file matched by both a\n * positive and a negation is excluded.\n */\nexport function fileMatchesAnyGlob(\n  moduleRelativePath: string,\n  patterns: readonly string[],\n): boolean {\n  const normalised = moduleRelativePath.startsWith('./')\n    ? moduleRelativePath\n    : './' + moduleRelativePath\n  let matched = false\n  for (const pattern of patterns) {\n    const isNegation = pattern.startsWith('!')\n    const body = isNegation ? pattern.slice(1) : pattern\n    if (globToRegex(body).test(normalised)) {\n      matched = !isNegation\n    }\n  }\n  return matched\n}\n\n/**\n * A `{ controller, mountPath }` pair extracted from a module's\n * `routes()` body. Multiple entries appear when a module returns an\n * array (multi-mount). forinda/kick-js#235 §3.\n */\nexport interface ModuleMount {\n  controller: string\n  mountPath: string\n}\n\n/**\n * Scan a module file's `routes()` body for `{ path, controller }` pairs.\n * A single return value or an array of return values both work — we\n * regex out every `path: '...'` and every `controller: Ident` and\n * zip them in order. Adopter writing wildly creative bodies won't be\n * matched; that's fine — the scanner falls back to no-mount behaviour\n * (per-route path only) which is the pre-fix behaviour.\n *\n * Returns a list of `{ controller, mountPath }` entries. A controller\n * that appears multiple times in `routes()` (rare; multi-mount\n * version-bundled controllers) gets multiple entries; the route\n * scanner uses the first one for path-param extraction since the\n * pattern usually shares the prefix.\n */\nexport function extractModuleMounts(source: string): ModuleMount[] {\n  const out: ModuleMount[] = []\n  ROUTES_METHOD_START.lastIndex = 0\n  let m: RegExpExecArray | null\n  while ((m = ROUTES_METHOD_START.exec(source)) !== null) {\n    const openBrace = source.indexOf('{', m.index + m[0].length - 1)\n    if (openBrace < 0) continue\n    const closeBrace = findBalancedBrace(source, openBrace)\n    if (closeBrace < 0) continue\n    const body = source.slice(openBrace + 1, closeBrace)\n\n    const paths: string[] = []\n    PATH_FIELD_REGEX.lastIndex = 0\n    let p: RegExpExecArray | null\n    while ((p = PATH_FIELD_REGEX.exec(body)) !== null) {\n      paths.push(p[1] ?? '')\n    }\n\n    const controllers: string[] = []\n    CONTROLLER_FIELD_REGEX.lastIndex = 0\n    let c: RegExpExecArray | null\n    while ((c = CONTROLLER_FIELD_REGEX.exec(body)) !== null) {\n      controllers.push(c[1] as string)\n    }\n\n    // Zip — if counts mismatch, take the shorter of the two so we\n    // never assign a wrong controller to a path.\n    const n = Math.min(paths.length, controllers.length)\n    for (let i = 0; i < n; i++) {\n      out.push({ controller: controllers[i] as string, mountPath: paths[i] as string })\n    }\n  }\n  return out\n}\n\n/**\n * Extract a bare identifier value from a single field in an object literal\n * embedded in a string. Returns `null` if the field is missing or its value\n * isn't a bare identifier (e.g. an inline object, function call, etc.).\n *\n * Example: `extractObjectFieldIdentifier(\"'/' , { body: createTaskSchema }\", 'body')`\n * returns `'createTaskSchema'`.\n */\nfunction extractObjectFieldIdentifier(text: string, field: string): string | null {\n  // Look for `field: <identifier>` not followed by `(` (function call) or `{` (inline object)\n  const re = new RegExp(String.raw`\\b${field}\\s*:\\s*([A-Za-z_$][\\w$]*)`, 'g')\n  const m = re.exec(text)\n  if (!m) return null\n  return m[1]\n}\n\n/**\n * Resolve a bare identifier to its module source by inspecting the file's\n * top-level imports and same-file `const` declarations.\n *\n * - `import { X } from './path'` → returns `'./path'`\n * - `import X from './path'` (default import) → returns `'./path'`\n * - `import * as X from './path'` → returns `'./path'`\n * - `const X = z.object(...)` (same file) → returns `null` (caller emits a self-import)\n *\n * Returns `null` when the identifier cannot be resolved.\n */\nfunction resolveImportSource(source: string, identifier: string): string | null {\n  // Named import: `import { X, Y as Z } from './path'`\n  const namedRe = new RegExp(\n    String.raw`import\\s*(?:type\\s+)?\\{[^}]*\\b${identifier}\\b[^}]*\\}\\s*from\\s*['\"\\`]([^'\"\\`]+)['\"\\`]`,\n  )\n  const named = namedRe.exec(source)\n  if (named) return named[1]\n\n  // Default import: `import X from './path'`\n  const defaultRe = new RegExp(\n    String.raw`import\\s+(?:type\\s+)?${identifier}\\s+from\\s*['\"\\`]([^'\"\\`]+)['\"\\`]`,\n  )\n  const def = defaultRe.exec(source)\n  if (def) return def[1]\n\n  // Namespace import: `import * as X from './path'`\n  const nsRe = new RegExp(\n    String.raw`import\\s*\\*\\s*as\\s+${identifier}\\s+from\\s*['\"\\`]([^'\"\\`]+)['\"\\`]`,\n  )\n  const ns = nsRe.exec(source)\n  if (ns) return ns[1]\n\n  // Same-file const declaration — return empty string as a sentinel meaning\n  // \"current file\". The generator turns this into a self-relative reference.\n  const constRe = new RegExp(String.raw`(?:^|\\n)\\s*(?:export\\s+)?const\\s+${identifier}\\b`)\n  if (constRe.test(source)) return ''\n\n  return null\n}\n\n/**\n * Extract whitelist arrays from an `@ApiQueryParams(...)` decorator\n * within `decoratorBlock`. Handles two forms:\n *\n * - Inline literal: `@ApiQueryParams({ filterable: ['a', 'b'], ... })`\n * - Const reference: `@ApiQueryParams(SOME_CONFIG)` — looks up\n *   `const SOME_CONFIG = { ... }` in the same file (`fullSource`).\n *\n * Returns `null` if no `@ApiQueryParams` is present. Returns\n * `{ filterable: [], sortable: [], searchable: [] }` if the decorator\n * is present but no fields could be statically extracted (opaque\n * imports, column-object configs, function calls, etc.).\n */\nfunction extractApiQueryParams(\n  decoratorBlock: string,\n  fullSource: string,\n): { filterable: string[]; sortable: string[]; searchable: string[] } | null {\n  const apiMatch = /@ApiQueryParams\\s*\\(\\s*([\\s\\S]*?)\\s*\\)\\s*$/.exec(decoratorBlock)\n  if (!apiMatch) {\n    // Try without anchoring to the end (decorator may not be the last in the block)\n    const loose = /@ApiQueryParams\\s*\\(([\\s\\S]*?)\\)/.exec(decoratorBlock)\n    if (!loose) return null\n    return parseApiQueryParamsArg(loose[1].trim(), fullSource)\n  }\n  return parseApiQueryParamsArg(apiMatch[1].trim(), fullSource)\n}\n\nfunction parseApiQueryParamsArg(\n  arg: string,\n  fullSource: string,\n): { filterable: string[]; sortable: string[]; searchable: string[] } {\n  // Inline literal — starts with `{`\n  if (arg.startsWith('{')) {\n    return parseInlineConfigLiteral(arg)\n  }\n  // Const reference — bare identifier (possibly with type assertion)\n  const idMatch = /^([A-Za-z_]\\w*)/.exec(arg)\n  if (idMatch) {\n    const ident = idMatch[1]\n    // Look for `const IDENT = { ... }` in the same source file\n    const constRe = new RegExp(\n      String.raw`const\\s+${ident}\\s*(?::\\s*[^=]+)?=\\s*(\\{[\\s\\S]*?\\n\\})`,\n      'm',\n    )\n    const constMatch = constRe.exec(fullSource)\n    if (constMatch) {\n      return parseInlineConfigLiteral(constMatch[1])\n    }\n  }\n  // Fallback: decorator present but extraction failed\n  return { filterable: [], sortable: [], searchable: [] }\n}\n\n/** Extract a string array literal for one config key from an inline object literal */\nfunction extractStringArray(literal: string, key: string): string[] {\n  const re = new RegExp(String.raw`${key}\\s*:\\s*\\[([\\s\\S]*?)\\]`)\n  const m = re.exec(literal)\n  if (!m) return []\n  return Array.from(m[1].matchAll(/['\"`]([^'\"`]+)['\"`]/g)).map((x) => x[1])\n}\n\n/** Parse an inline `{ filterable: [...], sortable: [...], searchable: [...] }` literal */\nfunction parseInlineConfigLiteral(literal: string): {\n  filterable: string[]\n  sortable: string[]\n  searchable: string[]\n} {\n  return {\n    filterable: extractStringArray(literal, 'filterable'),\n    sortable: extractStringArray(literal, 'sortable'),\n    searchable: extractStringArray(literal, 'searchable'),\n  }\n}\n\n/** Recursively walk a directory and yield matching file paths */\nasync function walk(dir: string, opts: ScanOptions): Promise<string[]> {\n  const exts = opts.extensions ?? DEFAULT_EXTENSIONS\n  const excludes = opts.exclude ?? DEFAULT_EXCLUDES\n  const out: string[] = []\n\n  let entries: Dirent[]\n  try {\n    entries = (await readdir(dir, { withFileTypes: true, encoding: 'utf-8' })) as Dirent[]\n  } catch {\n    return out\n  }\n\n  for (const entry of entries) {\n    const full = join(dir, entry.name)\n    const rel = relative(opts.cwd, full)\n\n    if (excludes.some((ex) => rel.includes(ex))) continue\n\n    if (entry.isDirectory()) {\n      out.push(...(await walk(full, opts)))\n    } else if (entry.isFile()) {\n      if (exts.some((ext) => entry.name.endsWith(ext))) {\n        out.push(full)\n      }\n    }\n  }\n\n  return out\n}\n\n/** Compute the forward-slash relative path used in scanner output */\nfunction toRelative(filePath: string, cwd: string): string {\n  return relative(cwd, filePath).split(sep).join('/')\n}\n\n/** Extract decorated classes from a single source file */\nexport function extractClassesFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredClass[] {\n  const out: DiscoveredClass[] = []\n  const relPath = toRelative(filePath, cwd)\n\n  DECORATED_CLASS_REGEX.lastIndex = 0\n  let match: RegExpExecArray | null\n  while ((match = DECORATED_CLASS_REGEX.exec(source)) !== null) {\n    const [, decorator, defaultMarker, className] = match\n    out.push({\n      className,\n      decorator: decorator as DecoratorName,\n      filePath,\n      relativePath: relPath,\n      isDefault: Boolean(defaultMarker),\n    })\n  }\n\n  // KickJS modules are undecorated classes that `implements AppModule`.\n  // Tag them with the synthetic `Module` decorator so downstream code that\n  // already filters by `c.decorator === 'Module'` keeps working.\n  APP_MODULE_CLASS_REGEX.lastIndex = 0\n  let modMatch: RegExpExecArray | null\n  while ((modMatch = APP_MODULE_CLASS_REGEX.exec(source)) !== null) {\n    const [, defaultMarker, className] = modMatch\n    if (out.some((c) => c.className === className && c.filePath === filePath)) continue\n    out.push({\n      className,\n      decorator: 'Module',\n      filePath,\n      relativePath: relPath,\n      isDefault: Boolean(defaultMarker),\n    })\n  }\n\n  // v4 factory modules: `export const XModule = defineModule({ ... })`.\n  // Tag with the synthetic `Module` decorator (same as the v3 class form)\n  // so `buildModuleTokens` populates `ModuleToken` — without this, a\n  // project that uses only `defineModule()` emits `ModuleToken = never`.\n  DEFINE_MODULE_REGEX.lastIndex = 0\n  let defMatch: RegExpExecArray | null\n  while ((defMatch = DEFINE_MODULE_REGEX.exec(source)) !== null) {\n    const [, className] = defMatch\n    if (out.some((c) => c.className === className && c.filePath === filePath)) continue\n    out.push({\n      className,\n      decorator: 'Module',\n      filePath,\n      relativePath: relPath,\n      isDefault: false,\n    })\n  }\n\n  return out\n}\n\n/** Extract `createToken('name')` definitions from a single source file */\nexport function extractTokensFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredToken[] {\n  const out: DiscoveredToken[] = []\n  const relPath = toRelative(filePath, cwd)\n  const seen = new Set<string>()\n\n  // First pass: const-bound tokens (preferred — we get the variable name)\n  CREATE_TOKEN_REGEX.lastIndex = 0\n  let match: RegExpExecArray | null\n  while ((match = CREATE_TOKEN_REGEX.exec(source)) !== null) {\n    const [full, variable, name] = match\n    seen.add(full)\n    out.push({ name, variable, filePath, relativePath: relPath })\n  }\n\n  // Second pass: bare calls not captured above (rare but possible)\n  BARE_CREATE_TOKEN_REGEX.lastIndex = 0\n  while ((match = BARE_CREATE_TOKEN_REGEX.exec(source)) !== null) {\n    if (seen.has(match[0])) continue\n    out.push({\n      name: match[1],\n      variable: null,\n      filePath,\n      relativePath: relPath,\n    })\n  }\n\n  return out\n}\n\n/**\n * Extract route handlers from a source file.\n *\n * For each decorated class in `classesInFile`, slices the source from\n * the class declaration to the next class (or EOF) and runs the route\n * decorator regex within that slice. The result is a list of routes\n * tagged with their owning controller.\n *\n * Heuristic note: this assumes classes are not nested. KickJS controllers\n * are top-level by convention so this holds in practice.\n */\nexport function extractRoutesFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n  classesInFile: DiscoveredClass[],\n  mountPathByController: ReadonlyMap<string, string> = new Map(),\n): DiscoveredRoute[] {\n  const out: DiscoveredRoute[] = []\n  if (classesInFile.length === 0) return out\n  const relPath = toRelative(filePath, cwd)\n\n  // Locate each class declaration's offset in the source\n  const positions: Array<{ cls: DiscoveredClass; start: number }> = []\n  for (const cls of classesInFile) {\n    const re = new RegExp(String.raw`class\\s+${cls.className}\\b`)\n    const m = re.exec(source)\n    if (m?.index !== undefined) {\n      positions.push({ cls, start: m.index })\n    }\n  }\n  positions.sort((a, b) => a.start - b.start)\n\n  for (let i = 0; i < positions.length; i++) {\n    const { cls, start } = positions[i]\n    const end = i + 1 < positions.length ? positions[i + 1].start : source.length\n    const block = source.slice(start, end)\n\n    // Two-pass walk: locate each route decorator start, then balance-parse\n    // forward through args and any stacked decorators to find the method\n    // name. Replaces the previous single regex which mis-parsed nested\n    // parens (forinda/kick-js#108).\n    ROUTE_DECORATOR_START.lastIndex = 0\n    let startMatch: RegExpExecArray | null\n    while ((startMatch = ROUTE_DECORATOR_START.exec(block)) !== null) {\n      const verb = startMatch[1]\n      const decoratorStart = startMatch.index\n      const openParen = ROUTE_DECORATOR_START.lastIndex - 1\n      const closeParen = findBalancedClose(block, openParen)\n      if (closeParen < 0) continue\n\n      const routeArgs = block.slice(openParen + 1, closeParen)\n\n      const pathLiteralMatch = routeArgs.match(/^\\s*['\"`]([^'\"`]*)['\"`]/)\n      const path = pathLiteralMatch && pathLiteralMatch[1].length > 0 ? pathLiteralMatch[1] : '/'\n\n      const methodInfo = readMethodAfterDecorators(block, closeParen + 1)\n      if (!methodInfo) continue\n      const { methodName, endPos } = methodInfo\n\n      // Advance the regex iterator past this method so the next iteration\n      // starts looking after the consumed region.\n      ROUTE_DECORATOR_START.lastIndex = endPos\n\n      const matchedText = block.slice(decoratorStart, endPos)\n      const apiQp = extractApiQueryParams(matchedText, source)\n\n      const bodyId = extractObjectFieldIdentifier(routeArgs, 'body')\n      const queryId = extractObjectFieldIdentifier(routeArgs, 'query')\n      const paramsId = extractObjectFieldIdentifier(routeArgs, 'params')\n      const responseId = extractObjectFieldIdentifier(routeArgs, 'response')\n\n      // forinda/kick-js#235 §3 — when the controller is mounted under a path\n      // with `:params` (e.g. `/orgs/:id/extensions`), surface those\n      // params in `pathParams` so the typegen widens `ctx.params`\n      // without adopters repeating `params: schema` on every route.\n      const mountPath = mountPathByController.get(cls.className) ?? ''\n      const fullPath = mountPath ? joinMountPath(mountPath, path) : path\n\n      out.push({\n        controller: cls.className,\n        method: methodName,\n        httpMethod: verb.toUpperCase() as DiscoveredRoute['httpMethod'],\n        path,\n        pathParams: extractPathParams(fullPath),\n        queryFilterable: apiQp?.filterable ?? null,\n        querySortable: apiQp?.sortable ?? null,\n        querySearchable: apiQp?.searchable ?? null,\n        bodySchema: bodyId\n          ? { identifier: bodyId, source: resolveImportSource(source, bodyId) }\n          : null,\n        querySchema: queryId\n          ? { identifier: queryId, source: resolveImportSource(source, queryId) }\n          : null,\n        paramsSchema: paramsId\n          ? { identifier: paramsId, source: resolveImportSource(source, paramsId) }\n          : null,\n        responseSchema: responseId\n          ? { identifier: responseId, source: resolveImportSource(source, responseId) }\n          : null,\n        filePath,\n        relativePath: relPath,\n        controllerIsDefaultExport: cls.isDefault,\n        mountedPath: fullPath,\n      })\n    }\n  }\n\n  return out\n}\n\n/** Extract `@Inject('literal')` calls from a single source file */\nexport function extractInjectsFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredInject[] {\n  const out: DiscoveredInject[] = []\n  const relPath = toRelative(filePath, cwd)\n\n  INJECT_LITERAL_REGEX.lastIndex = 0\n  let match: RegExpExecArray | null\n  while ((match = INJECT_LITERAL_REGEX.exec(source)) !== null) {\n    out.push({ name: match[1], filePath, relativePath: relPath })\n  }\n\n  return out\n}\n\n/**\n * Extract the bounds of an object literal that begins at `openBracePos`\n * (the index of the `{` character). Returns the index of the matching `}`\n * or -1 if no match is found. Counts balanced braces only — does not\n * understand string literals so a `{` or `}` inside a string inside the\n * object will skew the depth counter (matches `findBalancedClose`).\n */\nfunction findBalancedBrace(text: string, openBracePos: number): number {\n  let depth = 1\n  for (let i = openBracePos + 1; i < text.length; i++) {\n    const ch = text[i]\n    if (ch === '{') depth++\n    else if (ch === '}') {\n      depth--\n      if (depth === 0) return i\n    }\n  }\n  return -1\n}\n\n/**\n * Extract plugins/adapters declared via `defineAdapter({ name: '...' })`\n * or `definePlugin({ name: '...' })` calls and via class-style adapters\n * (`class XxxAdapter implements AppAdapter` with a string-literal `name`\n * field).\n *\n * Only the literal `name:` field feeds the result — the symbol on the LHS\n * is irrelevant since `dependsOn` references the runtime name.\n */\nexport function extractPluginsAndAdaptersFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredPluginOrAdapter[] {\n  const out: DiscoveredPluginOrAdapter[] = []\n  const relPath = toRelative(filePath, cwd)\n  const seen = new Set<string>()\n\n  // Pass 1: defineAdapter / definePlugin calls\n  DEFINE_HELPER_START.lastIndex = 0\n  let helperMatch: RegExpExecArray | null\n  while ((helperMatch = DEFINE_HELPER_START.exec(source)) !== null) {\n    const helper = helperMatch[1] as 'defineAdapter' | 'definePlugin'\n    const openParen = DEFINE_HELPER_START.lastIndex - 1\n    const closeParen = findBalancedClose(source, openParen)\n    if (closeParen < 0) continue\n    const callArgs = source.slice(openParen + 1, closeParen)\n    // Look for the first `name: 'literal'` in the call args\n    const nameMatch = /\\bname\\s*:\\s*['\"`]([^'\"`]+)['\"`]/.exec(callArgs)\n    if (!nameMatch) continue\n    const name = nameMatch[1]\n    const dedupeKey = `${helper}::${name}::${filePath}`\n    if (seen.has(dedupeKey)) continue\n    seen.add(dedupeKey)\n    out.push({\n      kind: helper === 'definePlugin' ? 'plugin' : 'adapter',\n      name,\n      filePath,\n      relativePath: relPath,\n    })\n  }\n\n  // Pass 2: class-style adapters (`class X implements AppAdapter { name = 'X' }`)\n  APP_ADAPTER_CLASS_REGEX.lastIndex = 0\n  let classMatch: RegExpExecArray | null\n  while ((classMatch = APP_ADAPTER_CLASS_REGEX.exec(source)) !== null) {\n    const classStart = classMatch.index\n    // Find the class body opening brace\n    const bracePos = source.indexOf('{', classStart)\n    if (bracePos < 0) continue\n    const closeBrace = findBalancedBrace(source, bracePos)\n    if (closeBrace < 0) continue\n    const body = source.slice(bracePos + 1, closeBrace)\n    const nameMatch = CLASS_NAME_FIELD_REGEX.exec(body)\n    if (!nameMatch) continue\n    const name = nameMatch[1]\n    const dedupeKey = `class::${name}::${filePath}`\n    if (seen.has(dedupeKey)) continue\n    seen.add(dedupeKey)\n    out.push({ kind: 'adapter', name, filePath, relativePath: relPath })\n  }\n\n  return out\n}\n\n/**\n * Extract context keys from `defineContextDecorator({ key: '...' })` and\n * `defineHttpContextDecorator({ key: '...' })` calls (including the\n * curried `.withParams<P>()({ key: '...' })` form). Only the literal\n * `key:` field feeds the result — the symbol on the LHS is irrelevant\n * since `dependsOn` references the runtime key string.\n *\n * Mirrors {@link extractPluginsAndAdaptersFromSource}: regex to the spec\n * object's opening paren, `findBalancedClose` to its end, then the first\n * `key: 'literal'` inside.\n */\nexport function extractContextKeysFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredContextKey[] {\n  const out: DiscoveredContextKey[] = []\n  const relPath = toRelative(filePath, cwd)\n  const seen = new Set<string>()\n\n  CONTEXT_DECORATOR_START.lastIndex = 0\n  // Capture group 1 is the optional `const <Name> =` binding; `lastIndex`\n  // points just past the spec's opening paren either way.\n  let match: RegExpExecArray | null\n  while ((match = CONTEXT_DECORATOR_START.exec(source)) !== null) {\n    const openParen = CONTEXT_DECORATOR_START.lastIndex - 1\n    const closeParen = findBalancedClose(source, openParen)\n    if (closeParen < 0) continue\n    const callArgs = source.slice(openParen + 1, closeParen)\n    const keyMatch = /\\bkey\\s*:\\s*['\"`]([^'\"`]+)['\"`]/.exec(callArgs)\n    if (!keyMatch) continue\n    const key = keyMatch[1]\n    if (seen.has(key)) continue\n    seen.add(key)\n    out.push({ key, exportName: match[1] ?? null, filePath, relativePath: relPath })\n  }\n\n  return out\n}\n\n/**\n * Extract `defineAugmentation('Name', { description, example })` calls\n * from a source file. The metadata object is optional — when absent both\n * `description` and `example` resolve to `null`.\n */\nexport function extractAugmentationsFromSource(\n  source: string,\n  filePath: string,\n  cwd: string,\n): DiscoveredAugmentation[] {\n  const out: DiscoveredAugmentation[] = []\n  const relPath = toRelative(filePath, cwd)\n\n  DEFINE_AUGMENTATION_START.lastIndex = 0\n  let match: RegExpExecArray | null\n  while ((match = DEFINE_AUGMENTATION_START.exec(source)) !== null) {\n    const name = match[1]\n    let description: string | null = null\n    let example: string | null = null\n\n    // If the regex matched a metadata object opening (`, {`), parse it\n    if (match[2]) {\n      const bracePos = source.indexOf('{', match.index + match[0].length - 1)\n      if (bracePos >= 0) {\n        const closeBrace = findBalancedBrace(source, bracePos)\n        if (closeBrace >= 0) {\n          const body = source.slice(bracePos + 1, closeBrace)\n          description = readStringField(body, 'description')\n          example = readStringField(body, 'example')\n        }\n      }\n    }\n\n    out.push({ name, description, example, filePath, relativePath: relPath })\n  }\n\n  return out\n}\n\n/**\n * Pull a string-valued field out of a JS object-literal body, respecting\n * the opening quote so the value isn't truncated at the first foreign\n * quote character. Handles backslash escapes inside the literal.\n *\n * Why a custom parser instead of one regex per delimiter: real-world\n * `defineAugmentation` calls embed all three quote characters at once\n * — backtick template literals carrying TS shapes like\n * `'free' | 'pro'` (single quotes) AND `\\`ctx.get(...)\\`` (escaped\n * backticks). A character-class regex like `[^'\"`]+` truncates on the\n * first foreign quote it sees. This walker scans char-by-char from\n * the matched delimiter and only stops on the matching one.\n */\nfunction readStringField(body: string, field: string): string | null {\n  // Locate `field:` followed by an opening quote. Tolerate any whitespace.\n  const fieldRe = new RegExp(`\\\\b${field}\\\\s*:\\\\s*(['\"\\`])`, 'g')\n  const m = fieldRe.exec(body)\n  if (!m) return null\n  const quote = m[1]\n  const start = m.index + m[0].length\n  let i = start\n  let raw: string | null = null\n  while (i < body.length) {\n    const ch = body[i]\n    if (ch === '\\\\') {\n      // Skip the escaped char — supports \\`, \\', \\\", \\n, \\\\ etc.\n      i += 2\n      continue\n    }\n    if (ch === quote) {\n      raw = body.slice(start, i)\n      break\n    }\n    i++\n  }\n  if (raw === null) return null\n  // Unescape JS string-literal escapes so the JSDoc renderer sees the\n  // value the source author actually intended (`\\`` → `` ` ``, `\\'` →\n  // `'`, etc). Without this, escaped backticks in a backtick template\n  // literal would surface as literal backslashes in the catalogue.\n  return raw.replace(/\\\\(.)/g, (_m, c) => {\n    if (c === 'n') return '\\n'\n    if (c === 't') return '\\t'\n    if (c === 'r') return '\\r'\n    return c\n  })\n}\n\n/**\n * Default search order for the env schema file. Newer projects keep\n * the schema under `src/config/` so the framework's \"config\" concept\n * has a single home; older scaffolds dropped it at `src/env.ts` (kept\n * here for back-compat). The first match wins.\n */\nconst DEFAULT_ENV_FILE_CANDIDATES = [\n  'src/config/index.ts',\n  'src/config/env.ts',\n  'src/config.ts',\n  'src/env.ts',\n] as const\n\n/**\n * Look for an env schema file. When `envFile` is the string default\n * (`'src/env.ts'`) or omitted, every entry in `DEFAULT_ENV_FILE_CANDIDATES`\n * is tried in order. When the caller passes an explicit path, only that\n * path is tried (so projects can opt out of the search by setting\n * `kick.config.ts → typegen.envFile`).\n *\n * Returns a `DiscoveredEnv` if the file exists and contains both a\n * `defineEnv(...)` call and a default export — the two markers we\n * need before it's safe to emit `import type schema from '...'` in\n * the generator. Returns `null` for any other state (no candidate\n * found, no defineEnv, no default export) so the generator skips env\n * typing silently.\n */\nexport async function detectEnvFile(cwd: string, envFile: string): Promise<DiscoveredEnv | null> {\n  // The CLI passes the literal default `'src/env.ts'` when the user\n  // hasn't overridden it. Treat that as \"use the search list\" rather\n  // than pinning to one path, so newer scaffolds at src/config/ keep\n  // working without forcing every project to set typegen.envFile.\n  const candidates: readonly string[] =\n    envFile === 'src/env.ts' ? DEFAULT_ENV_FILE_CANDIDATES : [envFile]\n\n  for (const candidate of candidates) {\n    const abs = resolve(cwd, candidate)\n    let source: string\n    try {\n      source = await readFile(abs, 'utf-8')\n    } catch {\n      continue\n    }\n    // Cheap heuristic: a schema-construction call AND a default\n    // export. The default export must be the SCHEMA itself — the\n    // generator emits `import type schema from '...'` and runs it\n    // through schema-to-type inference. `loadEnvFromSchema(...)` is\n    // deliberately NOT in the accept list because its return value is\n    // the parsed env object, not the schema. Adopters routinely write\n    //   export default envSchema\n    //   export const env = loadEnvFromSchema(envSchema)\n    // where only `envSchema` is the schema; detecting on\n    // `loadEnvFromSchema` would also accept the anti-pattern\n    //   export default loadEnvFromSchema(schema)\n    // which would push the parsed *env value* into `InferSchemaOutput`\n    // and emit a broken `KickEnv`.\n    //\n    // Accept lists:\n    //   - `defineEnv(...)`                       — legacy Zod scaffold\n    //   - `fromZod / fromValibot / fromYup(...)` — kickjs-schema adapters\n    if (!/\\bdefineEnv\\s*\\(/.test(source) && !/\\bfrom(Zod|Valibot|Yup)\\s*\\(/.test(source)) {\n      continue\n    }\n    if (!/export\\s+default\\b/.test(source)) continue\n    // Reject the \"default-export is the parsed env\" pattern\n    // explicitly. Without this guard, a file that constructs the\n    // schema with `fromZod(...)` and then does\n    // `export default loadEnvFromSchema(envSchema)` would slip past\n    // the schema-construction check above and feed the parsed env's\n    // value type into `InferSchemaOutput`.\n    if (/export\\s+default\\s+loadEnvFromSchema\\s*\\(/.test(source)) continue\n    return {\n      filePath: abs,\n      relativePath: toRelative(abs, cwd),\n    }\n  }\n\n  return null\n}\n\n/** Detect duplicate class names across files */\nexport function findCollisions(classes: DiscoveredClass[]): ClassCollision[] {\n  const groups = new Map<string, DiscoveredClass[]>()\n  for (const cls of classes) {\n    const arr = groups.get(cls.className) ?? []\n    arr.push(cls)\n    groups.set(cls.className, arr)\n  }\n\n  const collisions: ClassCollision[] = []\n  for (const [className, group] of groups) {\n    // Two declarations of the same class name in different files = collision.\n    // Multiple decorators on the same file/class are NOT a collision.\n    const distinctFiles = new Set(group.map((c) => c.filePath))\n    if (distinctFiles.size > 1) {\n      collisions.push({ className, classes: group })\n    }\n  }\n\n  // Deterministic order\n  collisions.sort((a, b) => a.className.localeCompare(b.className))\n  return collisions\n}\n\n/**\n * Framework decorators that are known NOT to register context\n * contributors. Anything applied to a route that isn't one of these and\n * isn't a resolvable context decorator makes the route's key set\n * unprovable — an adopter's own decorator can wrap contributors of its\n * own (the composition recipe in the context-decorators guide does\n * exactly that), so \"unrecognised\" cannot be treated as \"harmless\".\n */\nconst CONTRIBUTOR_FREE_DECORATORS = new Set([\n  'Controller',\n  'Service',\n  'Repository',\n  'Injectable',\n  'Component',\n  'Module',\n  'Middleware',\n  'ApiQueryParams',\n  'Public',\n  'Roles',\n  'Cron',\n  'Cacheable',\n  'CacheEvict',\n  'FileUpload',\n  'Asset',\n  'Builder',\n  'PostConstruct',\n  'PreDestroy',\n  'Value',\n  'Inject',\n  'Autowired',\n])\n\n/**\n * Resolve each route's provable context-key union, mutating `routes` in\n * place.\n *\n * The rule is \"prove completeness or degrade\". A route gets a key union\n * only when EVERY decorator applied to it (method and class) is either a\n * known contributor-free framework decorator or a context decorator whose\n * key we resolved. Anything unaccounted for — an unresolvable import, an\n * unrecognised decorator, an ambiguous binding name, or the presence of\n * non-decorator contributor registration anywhere in the project — yields\n * `null`, which downstream emits as `string` (today's behaviour).\n *\n * Degrading is always safe; narrowing wrongly is not. Because `require()`\n * narrowing rejects unknown keys, an over-narrow union turns a legitimate\n * call into a compile error. That fails loudly rather than silently, but\n * it is still a false alarm, and false alarms are how a feature like this\n * gets switched off.\n */\n/** Strip a `.ts` / `.tsx` / `.js` / `.mjs` … extension and normalise slashes. */\nfunction normalizeModulePath(p: string): string {\n  return p\n    .split(sep)\n    .join('/')\n    .replace(/\\.[mc]?[tj]sx?$/, '')\n}\n\n/**\n * Does `specifier`, as written in `importerFile`, refer to `declFile`?\n *\n * Three forms, deliberately conservative — a `false` costs a route its\n * narrowing (safe), a wrong `true` credits a route with a key it doesn't\n * carry (not safe):\n *\n * - `''` — the decorator is declared in the importing file itself.\n * - relative (`./x`, `../contributors/x`) — resolved against the\n *   importer's directory, extension-insensitive, `/index` tolerated.\n * - anything else — a bare package specifier or a path alias. tsconfig\n *   `paths` aren't parsed here, but `@/*` → `./src/*` is what `kick new`\n *   scaffolds and is near-universal in adopter projects, so an alias-ish\n *   specifier is matched by path suffix. A real package specifier\n *   (`@acme/auth`) won't suffix-match any local file and so degrades,\n *   which is the outcome we want for a third-party decorator.\n */\nfunction declarationMatchesImport(\n  importerFile: string,\n  specifier: string,\n  declFile: string,\n): boolean {\n  const decl = normalizeModulePath(declFile)\n\n  if (specifier === '') return decl === normalizeModulePath(importerFile)\n\n  if (specifier.startsWith('.')) {\n    const resolved = normalizeModulePath(resolve(dirname(importerFile), specifier))\n    return decl === resolved || decl === `${resolved}/index`\n  }\n\n  // Alias or bare specifier. Drop a leading alias marker, then require the\n  // remainder to be a path suffix of the declaration file.\n  const tail = specifier.replace(/^[@~#]\\//, '')\n  // A scoped package (`@acme/auth`) keeps its `@`, so it can't suffix-match\n  // a real file path and correctly falls through to `false`.\n  if (tail === specifier && specifier.startsWith('@')) return false\n  const normalizedTail = normalizeModulePath(tail)\n  return decl === normalizedTail || decl.endsWith(`/${normalizedTail}`)\n}\n\n/**\n * Module `contributors()` resolved per controller. `'ambiguous'` marks a\n * controller mounted by more than one module where contributors are in\n * play — which set applies depends on which mount served the request, so\n * neither can be asserted.\n */\nexport type ModuleContributorsByController = Map<string, ContributorRefSet | 'ambiguous'>\n\n/**\n * Attribute each module file's `contributors()` to the controllers that\n * same file mounts. Mirrors how `moduleMounts` already ties a mount path\n * to a controller — the hook and the mounts live on the same module\n * object, so they share a file.\n */\nexport function buildModuleContributorMap(\n  extracts: (FileExtract | null)[],\n): ModuleContributorsByController {\n  const out: ModuleContributorsByController = new Map()\n  const mountedBy = new Map<string, number>()\n\n  for (const extract of extracts) {\n    if (!extract) continue\n    for (const { controller } of extract.moduleMounts) {\n      mountedBy.set(controller, (mountedBy.get(controller) ?? 0) + 1)\n    }\n  }\n\n  for (const extract of extracts) {\n    if (!extract?.moduleContributors) continue\n    for (const { controller } of extract.moduleMounts) {\n      // Mounted twice AND contributors are involved — can't attribute.\n      if ((mountedBy.get(controller) ?? 0) > 1) {\n        out.set(controller, 'ambiguous')\n        continue\n      }\n      const existing = out.get(controller)\n      out.set(controller, existing === undefined ? extract.moduleContributors : 'ambiguous')\n    }\n  }\n\n  return out\n}\n\nexport function resolveRouteContextKeys(\n  routes: DiscoveredRoute[],\n  contextKeys: DiscoveredContextKey[],\n  hasNonDecoratorContributors: boolean,\n  moduleContributors: ModuleContributorsByController = new Map(),\n  appContributors: ContributorRefSet[] = [],\n): void {\n  if (hasNonDecoratorContributors) {\n    // An ADAPTER or PLUGIN `contributors()` — its body ships from a\n    // package we can't read, so the keys it adds to every route are\n    // unknowable and nothing is provable while one exists. Module hooks\n    // and app-entry `contributors: [...]` no longer land here; both are\n    // resolved below.\n    for (const route of routes) route.contextKeys = null\n    return\n  }\n\n  // Binding name → the context decorators exporting it. More than one\n  // entry means the name is ambiguous project-wide and we refuse to guess.\n  const byExportName = new Map<string, DiscoveredContextKey[]>()\n  for (const ck of contextKeys) {\n    if (!ck.exportName) continue\n    const list = byExportName.get(ck.exportName)\n    if (list) list.push(ck)\n    else byExportName.set(ck.exportName, [ck])\n  }\n\n  for (const route of routes) {\n    const applied = route.appliedDecorators\n    if (!applied) {\n      // Only the AST extractor records applied decorators. A route that\n      // came from the regex fallback (unparseable file, mid-edit in watch\n      // mode) has no decorator list, so nothing about it is provable.\n      // Note the distinction from `[]`, which means \"parsed fine, carries\n      // no decorators\" and IS provable.\n      route.contextKeys = null\n      continue\n    }\n    const keys = new Set<string>()\n    let provable = true\n\n    /**\n     * Resolve one binding reference to the key it writes, or `null` when\n     * it can't be pinned down. Name AND declaration file must both match:\n     * matching on the name alone would credit a route that imports\n     * `@LoadTenant` from an unrelated package with the project's own\n     * `LoadTenant` key.\n     */\n    const resolveRef = (importerFile: string, ref: DecoratorRef): string | null => {\n      if (ref.source === null) return null\n      const matches = (byExportName.get(ref.identifier) ?? []).filter((ck) =>\n        declarationMatchesImport(importerFile, ref.source as string, ck.filePath),\n      )\n      return matches.length === 1 ? matches[0].key : null\n    }\n\n    for (const dec of applied) {\n      if (CONTRIBUTOR_FREE_DECORATORS.has(dec.identifier)) continue\n      const key = resolveRef(route.filePath, dec)\n      if (key === null) {\n        provable = false\n        break\n      }\n      keys.add(key)\n    }\n\n    // App-level `contributors: [...]` — applies to every route, so no\n    // attribution is needed. Resolved once outside the loop would be\n    // marginally faster; done here so a single unresolvable entry\n    // degrades routes uniformly with the other paths.\n    if (provable) {\n      for (const set of appContributors) {\n        if (!set.resolved) {\n          provable = false\n          break\n        }\n        for (const ref of set.refs) {\n          const key = resolveRef(set.filePath, ref)\n          if (key === null) {\n            provable = false\n            break\n          }\n          keys.add(key)\n        }\n        if (!provable) break\n      }\n    }\n\n    // Module-level `contributors()` — applies to every route the module\n    // mounts, so it unions into the route's key set the same way a\n    // class-level decorator does. Precedence (method > class > module)\n    // decides which registration wins for a duplicate key, not whether\n    // the key is present, so a union is the right operation here.\n    if (provable) {\n      const fromModule = moduleContributors.get(route.controller)\n      if (fromModule === 'ambiguous') {\n        provable = false\n      } else if (fromModule) {\n        if (!fromModule.resolved) {\n          provable = false\n        } else {\n          for (const ref of fromModule.refs) {\n            const key = resolveRef(fromModule.filePath, ref)\n            if (key === null) {\n              provable = false\n              break\n            }\n            keys.add(key)\n          }\n        }\n      }\n    }\n\n    // Sorted so the emitted union is stable across runs — an unstable\n    // order would show up as spurious drift under `kick typegen --check`.\n    route.contextKeys = provable ? [...keys].toSorted() : null\n  }\n}\n\n/**\n * The complete per-file extraction result. Every field here is a pure\n * function of a single file's source text — no cross-file context — so\n * the whole object is cacheable keyed by the file's signature.\n *\n * The one subtlety is `routes`: their `pathParams` are computed with an\n * EMPTY mount map (own-path params only). The cross-file mount prefix\n * is re-applied during the join phase of `scanProject`, which is a\n * cheap pure-JS recompute (no regex, no I/O). This keeps routes fully\n * cacheable even though their final `pathParams` depend on a sibling\n * module file's `routes()` mount path.\n */\nexport interface FileExtract {\n  classes: DiscoveredClass[]\n  tokens: DiscoveredToken[]\n  injects: DiscoveredInject[]\n  pluginsAndAdapters: DiscoveredPluginOrAdapter[]\n  augmentations: DiscoveredAugmentation[]\n  contextKeys: DiscoveredContextKey[]\n  /** Routes with own-path `pathParams` only — mount prefix applied at join. */\n  routes: DiscoveredRoute[]\n  /** `{ controller, mountPath }` pairs from this file's `routes()` body. */\n  moduleMounts: ModuleMount[]\n  /** `import.meta.glob([...])` patterns (only non-empty for `*.module.ts`). */\n  globPatterns: string[]\n  /**\n   * Contributor bindings referenced by this file's module `contributors()`\n   * hook, or `null` when the file has no module hook. These apply to every\n   * controller the same file mounts (see `moduleMounts`).\n   *\n   * `resolved: false` means the hook exists but contains something the\n   * extractor couldn't enumerate — the mounted controllers' routes then\n   * degrade rather than narrowing to a partial set.\n   */\n  moduleContributors?: ContributorRefSet | null\n  /**\n   * Contributor bindings from an app-level `contributors: [...]` option —\n   * `bootstrap()`, `createWebApp()`, or `new Application()`. These apply\n   * to every route in the application, so they need no attribution; their\n   * keys union into all of them.\n   */\n  appContributors?: ContributorRefSet | null\n  /**\n   * This file registers context contributors somewhere other than a\n   * method/class decorator — a `contributors()` module/adapter/plugin hook\n   * or `bootstrap({ contributors: [...] })`.\n   *\n   * Presence, not resolution: those sites add keys to routes that carry no\n   * corresponding decorator, so per-route narrowing can't be proven\n   * complete anywhere in the project once one exists. Detecting them is\n   * enough to degrade; resolving them is a later increment.\n   */\n  hasNonDecoratorContributors: boolean\n}\n\n/**\n * `contributors` used as an object property (`contributors: [...]`) or a\n * method (`contributors() {}` / `contributors: () =>`). Matches the\n * module, adapter, plugin, and bootstrap registration sites in one shot.\n */\nconst CONTRIBUTOR_REGISTRATION = /\\bcontributors\\s*(?::|\\()/\n\n/**\n * Run per-file extraction over one source string. Pure: depends only\n * on the file's own text, so the result is safe to cache by filesystem\n * signature (see `scanner-cache.ts`).\n *\n * AST-first (oxc-parser — exact decorator/argument parsing, template-\n * literal route paths, no balanced-delimiter heuristics), falling back\n * to the regex extractors when the file doesn't parse — mid-edit\n * syntax errors in watch mode still yield partial results that keep\n * the dev loop alive.\n */\nexport function extractFile(source: string, filePath: string, cwd: string): FileExtract {\n  const ast = extractFileAst(source, filePath, cwd)\n  // The AST path classifies each `contributors` occurrence — module hook\n  // (attributable to the controllers that module mounts) vs adapter /\n  // plugin / bootstrap (app-wide, forces degradation) — so its own value\n  // is authoritative and must not be overwritten here.\n  if (ast) return ast\n  // The regex path can't tell the two apart, so it assumes the worse one.\n  // Erring the other way would silently enable narrowing on a project\n  // that registers contributors app-wide.\n  return {\n    ...extractFileRegex(source, filePath, cwd),\n    hasNonDecoratorContributors: CONTRIBUTOR_REGISTRATION.test(source),\n  }\n}\n\n/**\n * The original regex extraction pipeline — fallback for unparseable\n * sources and the parity baseline for the AST extractor's tests.\n */\nexport function extractFileRegex(source: string, filePath: string, cwd: string): FileExtract {\n  const classes = extractClassesFromSource(source, filePath, cwd)\n  return {\n    classes,\n    tokens: extractTokensFromSource(source, filePath, cwd),\n    injects: extractInjectsFromSource(source, filePath, cwd),\n    pluginsAndAdapters: extractPluginsAndAdaptersFromSource(source, filePath, cwd),\n    augmentations: extractAugmentationsFromSource(source, filePath, cwd),\n    contextKeys: extractContextKeysFromSource(source, filePath, cwd),\n    // Empty mount map → own-path params only; prefix re-applied at join.\n    routes: extractRoutesFromSource(source, filePath, cwd, classes, new Map()),\n    moduleMounts: extractModuleMounts(source),\n    globPatterns: /\\.module\\.[mc]?[tj]sx?$/.test(filePath) ? extractGlobPatterns(source) : [],\n    // Overwritten by `extractFile`, which computes it once for both the\n    // AST and regex paths so they can't disagree.\n    hasNonDecoratorContributors: false,\n  }\n}\n\n/**\n * Read + extract a single file, consulting the optional persistent\n * cache first. On a signature hit the file is not read at all — the\n * cached extract is returned directly. Returns null when the file\n * cannot be read (deleted mid-scan / permission error).\n */\nasync function loadFileExtract(\n  file: string,\n  cwd: string,\n  cache: ScanCache | null,\n): Promise<FileExtract | null> {\n  const sig = cache ? await ScanCache.signature(file) : null\n  if (cache && sig) {\n    const hit = cache.get(file, sig)\n    if (hit) {\n      cache.set(file, sig, hit)\n      return hit\n    }\n  }\n  let source: string\n  try {\n    source = await readFile(file, 'utf-8')\n  } catch {\n    return null\n  }\n  const extract = extractFile(source, file, cwd)\n  if (cache && sig) cache.set(file, sig, extract)\n  return extract\n}\n\n/** Map a concurrency-bounded async fn over items, preserving order. */\nasync function mapConcurrent<T, R>(\n  items: T[],\n  limit: number,\n  fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n  const out: R[] = []\n  let next = 0\n  const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n    for (;;) {\n      const i = next++\n      if (i >= items.length) return\n      out[i] = await fn(items[i], i)\n    }\n  })\n  await Promise.all(workers)\n  return out\n}\n\n/**\n * Scan a project for decorated classes, createToken definitions, and\n * `@Inject` literal usages.\n *\n * Per-file extraction is read + parsed concurrently and, when\n * `opts.cacheDir` is set, served from a persistent signature cache so\n * unchanged files are never re-read on a watch/rebuild. The cross-file\n * join phase (mount-prefix resolution, orphan detection) always runs\n * over the full extract set, so cached entries can never desync output.\n */\nexport async function scanProject(opts: ScanOptions): Promise<ScanResult> {\n  const root = resolve(opts.root)\n  // Sort the walk output so the cross-file join sees files in the same\n  // order the incremental path does (which sorts its working set). The\n  // §3 mount-map is first-wins, so a stable order keeps a multi-mount\n  // controller's pathParams identical across cold and incremental runs\n  // (and across filesystems with different readdir ordering).\n  const files = (await walk(root, opts)).toSorted()\n\n  const cache = opts.cacheDir ? await ScanCache.load(opts.cacheDir) : null\n\n  // Concurrent read + per-file extraction (cache-aware). I/O-bound, so\n  // a modest fan-out hides per-file latency without thrashing the FD\n  // table. Order is preserved for deterministic downstream iteration.\n  const extracts = await mapConcurrent(files, 16, (file) => loadFileExtract(file, opts.cwd, cache))\n\n  const joined = joinExtracts(files, extracts)\n  const env = await detectEnvFile(opts.cwd, opts.envFile ?? 'src/env.ts')\n\n  // Persist the refreshed cache (prunes entries for deleted files).\n  if (cache) await cache.save()\n\n  return { ...joined, env }\n}\n\n/** A precise set of filesystem changes, as reported by a watcher. */\nexport interface ScanDelta {\n  /** Files added or modified since the last scan (absolute or cwd-relative). */\n  changed: string[]\n  /** Files deleted since the last scan. */\n  removed: string[]\n}\n\n/**\n * Does `file` belong in the scan? Mirrors `walk()`'s ext + exclude\n * filtering so a watcher event for a `.d.ts`, a test, or a node_modules\n * file is ignored just as the full walk would ignore it.\n */\nfunction isScannableFile(file: string, root: string, opts: ScanOptions): boolean {\n  const exts = opts.extensions ?? DEFAULT_EXTENSIONS\n  const excludes = opts.exclude ?? DEFAULT_EXCLUDES\n  if (!file.startsWith(root + sep) && file !== root) return false\n  if (!exts.some((ext) => file.endsWith(ext))) return false\n  const rel = relative(opts.cwd, file)\n  if (excludes.some((ex) => rel.includes(ex))) return false\n  return true\n}\n\n/**\n * Incremental scan driven by an exact watcher delta (e.g. Vite's\n * chokidar events). Unlike `scanProject` this performs NO directory\n * walk and NO `stat()` of unchanged files: it loads the persistent\n * cache, re-extracts only the `changed` files, drops `removed` ones,\n * and re-runs the cheap cross-file join over the resulting set.\n *\n * Requires a warm cache. With no `cacheDir`, an empty cache (cold\n * start), it transparently falls back to a full `scanProject` so the\n * caller never has to special-case the first run.\n */\nexport async function scanProjectIncremental(\n  opts: ScanOptions,\n  delta: ScanDelta,\n): Promise<ScanResult> {\n  if (!opts.cacheDir) return scanProject(opts)\n  const root = resolve(opts.root)\n  const cache = await ScanCache.load(opts.cacheDir)\n  const cachedFiles = cache.cachedFiles()\n  if (cachedFiles.length === 0) return scanProject(opts)\n\n  const removed = new Set(delta.removed.map((f) => resolve(opts.cwd, f)))\n  const changed = delta.changed\n    .map((f) => resolve(opts.cwd, f))\n    .filter((f) => !removed.has(f) && isScannableFile(f, root, opts))\n  const changedSet = new Set(changed)\n\n  // Working set = (previously cached ∪ newly added) − removed.\n  const working = new Set(cachedFiles)\n  for (const f of changedSet) working.add(f)\n  for (const f of removed) working.delete(f)\n\n  // Re-extract only the changed files (concurrent). Everything else is\n  // served from the cache without a read or a stat.\n  const fresh = new Map<string, FileExtract>()\n  await mapConcurrent(changed, 16, async (file) => {\n    if (!working.has(file)) return\n    const sig = await ScanCache.signature(file)\n    let source: string\n    try {\n      source = await readFile(file, 'utf-8')\n    } catch {\n      working.delete(file)\n      return\n    }\n    const extract = extractFile(source, file, opts.cwd)\n    fresh.set(file, extract)\n    if (sig) cache.set(file, sig, extract)\n  })\n\n  const orderedFiles = [...working].toSorted()\n  const extracts = orderedFiles.map((file) => {\n    const f = fresh.get(file)\n    if (f) return f\n    cache.carry(file) // keep the unchanged entry in the next saved cache\n    return cache.peek(file)\n  })\n\n  const joined = joinExtracts(orderedFiles, extracts)\n  const env = await detectEnvFile(opts.cwd, opts.envFile ?? 'src/env.ts')\n  await cache.save()\n\n  return { ...joined, env }\n}\n\n/**\n * Cross-file join over a set of per-file extracts: resolves mount-prefix\n * route params, detects glob-orphaned classes, concatenates and sorts\n * every discovered entity into a deterministic `ScanResult` (minus the\n * async `env` field, which the caller attaches). Pure and synchronous —\n * shared by both `scanProject` and `scanProjectIncremental`.\n */\nfunction joinExtracts(files: string[], extracts: (FileExtract | null)[]): Omit<ScanResult, 'env'> {\n  const classes: DiscoveredClass[] = []\n  const routes: DiscoveredRoute[] = []\n  const tokens: DiscoveredToken[] = []\n  const injects: DiscoveredInject[] = []\n  const pluginsAndAdapters: DiscoveredPluginOrAdapter[] = []\n  const augmentations: DiscoveredAugmentation[] = []\n  const contextKeys: DiscoveredContextKey[] = []\n\n  // forinda/kick-js#235 §3 — build a `Controller → mountPath` map from every\n  // module file's `routes()` body so per-route `pathParams` can include\n  // the prefix params (e.g. `/orgs/:id`) without adopters re-declaring\n  // `params:` on every method. First mount wins on duplicates (rare\n  // multi-mount controllers — typically share the prefix shape).\n  const mountPathByController = new Map<string, string>()\n  for (const extract of extracts) {\n    if (!extract) continue\n    for (const { controller, mountPath } of extract.moduleMounts) {\n      if (!mountPathByController.has(controller)) {\n        mountPathByController.set(controller, mountPath)\n      }\n    }\n  }\n\n  // A per-file glob-pattern map drives the §4 orphan pass below without\n  // re-reading module sources.\n  const globPatternsByFile = new Map<string, string[]>()\n  for (let i = 0; i < files.length; i++) {\n    const extract = extracts[i]\n    if (!extract) continue\n    classes.push(...extract.classes)\n    tokens.push(...extract.tokens)\n    injects.push(...extract.injects)\n    pluginsAndAdapters.push(...extract.pluginsAndAdapters)\n    augmentations.push(...extract.augmentations)\n    contextKeys.push(...extract.contextKeys)\n    if (extract.globPatterns.length > 0) globPatternsByFile.set(files[i], extract.globPatterns)\n\n    // Re-apply the cross-file mount prefix to each cached route's\n    // pathParams. Routes were extracted with an empty mount map, so\n    // their pathParams currently carry own-path params only.\n    for (const route of extract.routes) {\n      const mountPath = mountPathByController.get(route.controller)\n      if (mountPath) {\n        const fullPath = joinMountPath(mountPath, route.path)\n        routes.push({ ...route, pathParams: extractPathParams(fullPath), mountedPath: fullPath })\n      } else {\n        routes.push({ ...route, mountedPath: route.mountedPath ?? route.path })\n      }\n    }\n  }\n\n  // Per-route context-key resolution. Runs after the route list is\n  // complete because the decision is project-wide: one non-decorator\n  // registration site anywhere degrades every route.\n  resolveRouteContextKeys(\n    routes,\n    contextKeys,\n    extracts.some((e) => e?.hasNonDecoratorContributors === true),\n    buildModuleContributorMap(extracts),\n    extracts.flatMap((e) => (e?.appContributors ? [e.appContributors] : [])),\n  )\n\n  // forinda/kick-js#235 §4 — for every module file, extract its\n  // `import.meta.glob([...])` patterns and flag any decorated class\n  // whose file sits inside the module directory but isn't matched by\n  // a positive pattern. Catches the \"added a new file type, forgot to\n  // extend the glob\" silent-degradation case.\n  // Normalize Windows backslashes to forward slashes before any\n  // slicing / startsWith / glob-matching — the rest of the scanner\n  // already speaks forward-slash relative paths, but absolute\n  // `filePath` values may carry the platform separator on Windows.\n  const orphanedClasses: OrphanedClass[] = []\n  for (const [moduleFile, patterns] of globPatternsByFile) {\n    if (!/\\.module\\.[mc]?[tj]sx?$/.test(moduleFile)) continue\n    if (patterns.length === 0) continue\n    const moduleFilePosix = moduleFile.replaceAll(sep, '/')\n    const moduleDir = moduleFilePosix.slice(0, moduleFilePosix.lastIndexOf('/'))\n    for (const cls of classes) {\n      // Skip module files themselves — they're scanner-synthesized\n      // `decorator: 'Module'` entries that aren't glob contributors.\n      if (cls.decorator === 'Module') continue\n      const classFilePosix = cls.filePath.replaceAll(sep, '/')\n      if (!classFilePosix.startsWith(moduleDir + '/')) continue\n      if (classFilePosix === moduleFilePosix) continue\n      const moduleRelative = classFilePosix.slice(moduleDir.length + 1)\n      if (!fileMatchesAnyGlob(moduleRelative, patterns)) {\n        orphanedClasses.push({\n          className: cls.className,\n          filePath: cls.filePath,\n          relativePath: cls.relativePath,\n          moduleFilePath: moduleFile,\n          decorator: cls.decorator,\n        })\n      }\n    }\n  }\n\n  // Deterministic ordering for stable .d.ts output\n  classes.sort((a, b) => {\n    if (a.className !== b.className) return a.className.localeCompare(b.className)\n    return a.relativePath.localeCompare(b.relativePath)\n  })\n  tokens.sort(\n    (a, b) => a.name.localeCompare(b.name) || a.relativePath.localeCompare(b.relativePath),\n  )\n  injects.sort(\n    (a, b) => a.name.localeCompare(b.name) || a.relativePath.localeCompare(b.relativePath),\n  )\n  routes.sort(\n    (a, b) => a.controller.localeCompare(b.controller) || a.method.localeCompare(b.method),\n  )\n  pluginsAndAdapters.sort(\n    (a, b) => a.name.localeCompare(b.name) || a.relativePath.localeCompare(b.relativePath),\n  )\n  augmentations.sort(\n    (a, b) => a.name.localeCompare(b.name) || a.relativePath.localeCompare(b.relativePath),\n  )\n  contextKeys.sort(\n    (a, b) => a.key.localeCompare(b.key) || a.relativePath.localeCompare(b.relativePath),\n  )\n\n  const collisions = findCollisions(classes)\n\n  orphanedClasses.sort(\n    (a, b) =>\n      a.relativePath.localeCompare(b.relativePath) || a.className.localeCompare(b.className),\n  )\n\n  return {\n    classes,\n    routes,\n    tokens,\n    injects,\n    collisions,\n    pluginsAndAdapters,\n    augmentations,\n    contextKeys,\n    orphanedClasses,\n  }\n}\n","/**\n * Module-glob convenience for the orphaned-class case.\n *\n * A decorated class (`@Controller`, `@Service`, …) only registers at runtime\n * if some module's `import.meta.glob([...], { eager: true })` imports its file —\n * that eager import is what fires the decorator. When an adopter organises a\n * module into sub-directories (`controllers/`, `presentation/`, …) the\n * scaffolded glob often doesn't reach the new depth, so those classes never\n * load: routes silently vanish, DI tokens resolve `undefined`.\n *\n * The scanner already detects this (`orphanedClasses`). These helpers turn the\n * detection into a fix:\n *\n *   - {@link suggestGlobsForOrphans} derives the minimal set of RECURSIVE glob\n *     patterns (`./**\\/*.controller.ts`) that would cover the orphans — depth\n *     independent, so it keeps working however the adopter nests files.\n *   - {@link patchModuleGlobSource} splices those patterns into the module\n *     file's existing `import.meta.glob(...)` call (array or bare-string form),\n *     skipping any already present. Drives `kick typegen --fix`.\n */\n\nimport { extractGlobPatterns } from './scanner'\n\n/** One orphan, reduced to what the fixer needs. */\nexport interface OrphanLike {\n  /** Project-relative path of the orphaned class file, e.g. `src/modules/x/controllers/x.controller.ts`. */\n  relativePath: string\n}\n\n/** Strip the trailing extension, returning `{ stem, ext }` (ext WITHOUT the dot). */\nfunction splitExt(basename: string): { stem: string; ext: string } {\n  const dot = basename.lastIndexOf('.')\n  if (dot <= 0) return { stem: basename, ext: '' }\n  return { stem: basename.slice(0, dot), ext: basename.slice(dot + 1) }\n}\n\n/**\n * Derive the recursive glob that would cover a single orphan file.\n *\n * `expenses.controller.ts` → `./**\\/*.controller.ts` (precise: only sibling\n * controllers, at any depth). A file with no compound suffix (`thing.ts`) falls\n * back to `./**\\/*.ts` for that extension — broader, but still scoped to the\n * module's own tree.\n */\nexport function globForOrphanFile(relativePath: string): string {\n  const normalised = relativePath.replaceAll('\\\\', '/')\n  const basename = normalised.slice(normalised.lastIndexOf('/') + 1)\n  const { stem, ext } = splitExt(basename)\n  const extPart = ext || 'ts'\n  // `expenses.controller` → kind = `controller`; `expenses` → no kind.\n  const innerDot = stem.lastIndexOf('.')\n  if (innerDot > 0) {\n    return `./**/*.${stem.slice(innerDot + 1)}.${extPart}`\n  }\n  return `./**/*.${extPart}`\n}\n\n/**\n * The distinct, sorted set of recursive globs covering a module's orphans.\n * Feed the orphans of ONE module file (group by `moduleFilePath` first).\n */\nexport function suggestGlobsForOrphans(orphans: readonly OrphanLike[]): string[] {\n  const set = new Set<string>()\n  for (const o of orphans) set.add(globForOrphanFile(o.relativePath))\n  return [...set].toSorted()\n}\n\n/** A single `import.meta.glob(...)` call: byte offsets of its name start, `(`, and matching `)`. */\ninterface GlobCall {\n  start: number\n  open: number\n  close: number\n}\n\n/**\n * Index of the `)` that closes the `(` at `openIdx`, balanced and\n * quote-aware (parens inside string literals don't count). `-1` if\n * unbalanced.\n */\nfunction matchingParen(source: string, openIdx: number): number {\n  let depth = 0\n  let quote: string | null = null\n  for (let i = openIdx; i < source.length; i++) {\n    const ch = source[i]\n    if (quote) {\n      if (ch === '\\\\') {\n        i++\n        continue\n      }\n      if (ch === quote) quote = null\n      continue\n    }\n    if (ch === \"'\" || ch === '\"' || ch === '`') {\n      quote = ch\n    } else if (ch === '(') {\n      depth++\n    } else if (ch === ')') {\n      depth--\n      if (depth === 0) return i\n    }\n  }\n  return -1\n}\n\n/**\n * First index of `char` within `[from, to)` that sits OUTSIDE a string literal.\n * Used to find the array's opening `[` without being fooled by a `[` inside a\n * glob pattern (`'./[A-Z]*.ts'`). `-1` if none.\n */\nfunction indexOfUnquoted(source: string, char: string, from: number, to: number): number {\n  let quote: string | null = null\n  for (let i = from; i < to; i++) {\n    const ch = source[i]\n    if (quote) {\n      if (ch === '\\\\') {\n        i++\n        continue\n      }\n      if (ch === quote) quote = null\n      continue\n    }\n    if (ch === \"'\" || ch === '\"' || ch === '`') {\n      quote = ch\n    } else if (ch === char) {\n      return i\n    }\n  }\n  return -1\n}\n\n/**\n * Index of the `]` that closes the `[` at `openIdx`, balanced and quote-aware\n * (brackets inside string literals — e.g. the char class in `'./[A-Z]*.ts'` —\n * don't count). `-1` if unbalanced.\n */\nfunction matchingBracket(source: string, openIdx: number): number {\n  let depth = 0\n  let quote: string | null = null\n  for (let i = openIdx; i < source.length; i++) {\n    const ch = source[i]\n    if (quote) {\n      if (ch === '\\\\') {\n        i++\n        continue\n      }\n      if (ch === quote) quote = null\n      continue\n    }\n    if (ch === \"'\" || ch === '\"' || ch === '`') {\n      quote = ch\n    } else if (ch === '[') {\n      depth++\n    } else if (ch === ']') {\n      depth--\n      if (depth === 0) return i\n    }\n  }\n  return -1\n}\n\n/** Locate every `import.meta.glob(...)` call with its balanced boundaries. */\nfunction findGlobCalls(source: string): GlobCall[] {\n  const re = /\\bimport\\.meta\\.glob\\s*\\(/g\n  const calls: GlobCall[] = []\n  let m: RegExpExecArray | null\n  while ((m = re.exec(source)) !== null) {\n    const open = source.indexOf('(', m.index)\n    if (open < 0) continue\n    const close = matchingParen(source, open)\n    if (close < 0) continue\n    calls.push({ start: m.index, open, close })\n  }\n  return calls\n}\n\n/**\n * Splice `addPatterns` into the module's EAGER `import.meta.glob(...)` call,\n * preserving its existing patterns. The eager call (`{ eager: true }`) is the\n * one that actually fires decorators — a module may also hold lazy globs (e.g.\n * route-component loaders) that must NOT be touched, so we target the eager\n * call specifically and fall back to the first call only when none is marked\n * eager. Handles both the array form (`import.meta.glob(['./a'], …)`) and the\n * bare-string form (`import.meta.glob('./a', …)`, upgraded to an array).\n *\n * Patterns already present in the TARGET call (positive or negated) are\n * skipped, so it's idempotent. Returns the patched source, or `null` when\n * there's no glob call to patch or nothing new to add.\n */\nexport function patchModuleGlobSource(\n  source: string,\n  addPatterns: readonly string[],\n): string | null {\n  const calls = findGlobCalls(source)\n  if (calls.length === 0) return null\n\n  // Prefer the eager call (the decorator-firing loader); fall back to the first.\n  const target =\n    calls.find((c) => /\\beager\\s*:\\s*true\\b/.test(source.slice(c.open, c.close + 1))) ?? calls[0]\n\n  // Idempotency scoped to the TARGET call only — a pattern present in some\n  // other (lazy) call shouldn't suppress adding it to the eager loader.\n  const callSource = source.slice(target.start, target.close + 1)\n  const existing = new Set(\n    extractGlobPatterns(callSource).map((p) => (p.startsWith('!') ? p.slice(1) : p)),\n  )\n  const fresh = addPatterns.filter((p) => !existing.has(p))\n  if (fresh.length === 0) return null\n\n  const insertion = fresh.map((p) => `'${p}'`).join(', ')\n\n  // Array form: insert before the closing `]` of this call's array literal.\n  // Both the opening `[` lookup and its matching `]` are quote-aware so a\n  // char-class inside a pattern (`'./[A-Z]*.ts'`) can't be mistaken for the\n  // array delimiters.\n  const bracket = indexOfUnquoted(source, '[', target.open, target.close)\n  if (bracket >= 0) {\n    const closeBracket = matchingBracket(source, bracket)\n    if (closeBracket < 0 || closeBracket > target.close) return null\n    // Respect a trailing comma / whitespace already before `]`.\n    const before = source.slice(0, closeBracket)\n    const needsComma = !/[[,]\\s*$/.test(before)\n    const sep = needsComma ? ', ' : ''\n    return before + sep + insertion + source.slice(closeBracket)\n  }\n\n  // Bare-string form: wrap this call's first string literal into an array.\n  const strRe = /(['\"`])((?:\\\\.|(?!\\1).)*)\\1/\n  const tail = source.slice(target.open + 1, target.close)\n  const m = strRe.exec(tail)\n  if (!m) return null\n  const absStart = target.open + 1 + m.index\n  const absEnd = absStart + m[0].length\n  return source.slice(0, absStart) + `[${m[0]}, ${insertion}]` + source.slice(absEnd)\n}\n","/**\n * Pure renderers for the DI-manifest typegen surface — the\n * `KickJsRegistry` augmentation, the `ServiceToken` / `ModuleToken`\n * unions, the `KickJsPluginRegistry` augmentation, and the\n * `defineAugmentation` catalogue.\n *\n * These used to live in `generator.ts` (the monolithic legacy pass).\n * They are now consumed by the per-domain builtin typegen plugins\n * (`kick/registry`, `kick/services`, `kick/modules`, `kick/plugins`,\n * `kick/augmentations`) so the whole pipeline is plugin-based and each\n * file is emitted + cache-tracked independently by the runner.\n *\n * Every function here is pure (scan data in → string out); no fs, no\n * config, no globals.\n *\n * @module @forinda/kickjs-cli/typegen/render/manifest\n */\n\nimport { dirname, relative, sep } from 'node:path'\nimport type {\n  ClassCollision,\n  DiscoveredAugmentation,\n  DiscoveredClass,\n  DiscoveredInject,\n  DiscoveredPluginOrAdapter,\n  DiscoveredToken,\n} from '../scanner'\n\n/** Header written to every generated file */\nexport const HEADER = `/* eslint-disable */\n// AUTO-GENERATED by \\`kick typegen\\`. DO NOT EDIT.\n// Re-run with \\`kick typegen\\` or rely on \\`kick dev\\` to refresh.\n`\n\n/** Decorators whose classes participate in the DI registry augmentation */\nexport const REGISTRY_DECORATORS = new Set(['Service', 'Repository', 'Injectable', 'Component'])\n\n/** Thrown when collisions are found and not allowed */\nexport class TokenCollisionError extends Error {\n  readonly collisions: ClassCollision[]\n  constructor(collisions: ClassCollision[]) {\n    super(formatCollisionMessage(collisions))\n    this.name = 'TokenCollisionError'\n    this.collisions = collisions\n  }\n}\n\n/** Build a human-readable message describing every collision */\nfunction formatCollisionMessage(collisions: ClassCollision[]): string {\n  const lines: string[] = ['kick typegen: token collision detected']\n  for (const c of collisions) {\n    lines.push('')\n    lines.push(`  ${c.classes.length} classes named '${c.className}':`)\n    for (const cls of c.classes) {\n      lines.push(`    - ${cls.relativePath}`)\n    }\n  }\n  lines.push('')\n  lines.push('Resolutions:')\n  lines.push('  (a) Rename one of the classes')\n  lines.push(\n    \"  (b) Use createToken<T>('namespaced/Name') and import the token explicitly — see @forinda/kickjs\",\n  )\n  lines.push('  (c) Pass --allow-duplicates to namespace the registry keys automatically')\n  lines.push(\"      (e.g. 'modules/users/UserService' instead of 'UserService')\")\n  return lines.join('\\n')\n}\n\n/** Compute the module specifier (without extension) used inside `import('...')` */\nfunction importSpecifierFor(targetFile: string, fromFile: string): string {\n  const fromDir = dirname(fromFile)\n  let rel = relative(fromDir, targetFile).split(sep).join('/')\n  rel = rel.replace(/\\.(ts|tsx|mts|cts)$/i, '')\n  if (!rel.startsWith('.')) rel = './' + rel\n  return rel\n}\n\n/**\n * Build the namespaced registry key for a colliding class.\n * Strips the `src/` prefix and the file extension, then appends the\n * class name. Example: `src/modules/users/user.service.ts` + `UserService`\n * → `modules/users/UserService`.\n */\nexport function namespacedKeyFor(cls: DiscoveredClass): string {\n  const rel = cls.relativePath.replace(/^src\\//, '').replace(/\\.(ts|tsx|mts|cts)$/i, '')\n  const parts = rel.split('/')\n  parts.pop()\n  const ns = parts.join('/')\n  return ns ? `${ns}/${cls.className}` : cls.className\n}\n\n/**\n * Render the `KickJsRegistry` module augmentation. Each entry maps a\n * string token to the imported class type. Default-exported classes are\n * imported as `import('...').default`. `collidingNames` lists class\n * names that should be auto-namespaced; everything else gets a bare key.\n */\nexport function renderRegistry(\n  classes: DiscoveredClass[],\n  outFile: string,\n  collidingNames: Set<string>,\n): string {\n  const seen = new Set<string>()\n  const entries: string[] = []\n\n  for (const c of classes) {\n    if (!REGISTRY_DECORATORS.has(c.decorator)) continue\n\n    const key = collidingNames.has(c.className) ? namespacedKeyFor(c) : c.className\n    if (seen.has(key)) continue\n    seen.add(key)\n\n    const spec = importSpecifierFor(c.filePath, outFile)\n    const ref = c.isDefault ? `import('${spec}').default` : `import('${spec}').${c.className}`\n    entries.push(`    '${key}': ${ref}`)\n  }\n\n  const body = entries.length\n    ? entries.join('\\n')\n    : '    // (no services discovered yet — run `kick g service <name>` to add one)'\n\n  return `${HEADER}\ndeclare module '@forinda/kickjs' {\n  interface KickJsRegistry {\n${body}\n  }\n}\n\nexport {}\n`\n}\n\n/** True when `str` is a bare JS identifier (usable as an unquoted key). */\nfunction isIdentifierKey(str: string): boolean {\n  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(str)\n}\n\n/**\n * Render the `ContextKeys` augmentation from discovered context-decorator\n * keys. Each `key:` literal becomes a `'<key>': true` entry — a key-only\n * registry (the value type is irrelevant; `true` is conventional) so\n * `dependsOn: ['<key>']` is autocompleted + typo-checked without forcing\n * a value type into `ContextMeta`. Non-identifier keys are quoted.\n */\nexport function renderContextKeys(keys: readonly { key: string }[]): string {\n  const unique = [...new Set(keys.map((k) => k.key))].toSorted()\n  const body = unique\n    .map((k) => `    ${isIdentifierKey(k) ? k : JSON.stringify(k)}: true`)\n    .join('\\n')\n  return `${HEADER}\ndeclare module '@forinda/kickjs' {\n  /**\n   * Key-only registry of every context key produced by a\n   * \\`defineContextDecorator\\` / \\`defineHttpContextDecorator\\` in the\n   * project. Feeds \\`dependsOn\\` typo-checking. Value types live in\n   * \\`ContextMeta\\`; this only records that the key exists.\n   */\n  interface ContextKeys {\n${body}\n  }\n}\n\nexport {}\n`\n}\n\n/** Render a string-literal union type containing the given names */\nexport function renderUnion(typeName: string, names: string[], emptyComment: string): string {\n  if (names.length === 0) {\n    return `${HEADER}\n// ${emptyComment}\nexport type ${typeName} = never\n`\n  }\n  const sorted = [...new Set(names)].toSorted()\n  return `${HEADER}\nexport type ${typeName} =\n${sorted.map((n) => `  | '${n}'`).join('\\n')}\n`\n}\n\n/**\n * Build the `ServiceToken` union members — class names (namespaced on\n * collision), plus `createToken('name')` and `@Inject('literal')`\n * literals — so tooling autocomplete sees every known token.\n */\nexport function buildServiceTokens(\n  classes: DiscoveredClass[],\n  tokens: DiscoveredToken[],\n  injects: DiscoveredInject[],\n  collidingNames: Set<string>,\n): string[] {\n  const classTokens = classes\n    .filter((c) => REGISTRY_DECORATORS.has(c.decorator))\n    .map((c) => (collidingNames.has(c.className) ? namespacedKeyFor(c) : c.className))\n  return [...classTokens, ...tokens.map((t) => t.name), ...injects.map((i) => i.name)]\n}\n\n/** Build the `ModuleToken` union members — discovered `@Module` class names. */\nexport function buildModuleTokens(classes: DiscoveredClass[]): string[] {\n  return classes.filter((c) => c.decorator === 'Module').map((c) => c.className)\n}\n\n/**\n * Render the `KickJsPluginRegistry` augmentation. Each entry maps the\n * literal `name` field of a plugin/adapter to a marker type (the\n * registry value isn't load-bearing at runtime — `dependsOn` only cares\n * about `keyof`, so any non-`never` type works). We emit `'plugin'` /\n * `'adapter'` strings so DevTools can later read the registry to tell\n * the kinds apart without a second source of truth.\n *\n * When the project has no discoverable plugins/adapters, the\n * augmentation is intentionally empty rather than skipped so the\n * `keyof` constraint resolves to `never` (harmless — `dependsOn: []`\n * still works).\n */\nexport function renderPlugins(items: DiscoveredPluginOrAdapter[]): string {\n  // Dedupe by name — two declarations with the same name are a runtime\n  // boot-time error in `mount-sort.ts`; we surface the conflict via a\n  // single registry entry rather than a duplicate-key TS error.\n  const byName = new Map<string, DiscoveredPluginOrAdapter>()\n  for (const item of items) {\n    if (!byName.has(item.name)) byName.set(item.name, item)\n  }\n\n  const sorted = [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name))\n  const entries = sorted.map((item) => `    '${item.name}': '${item.kind}'`).join('\\n')\n\n  const body = entries\n    ? entries\n    : '    // (no plugins/adapters discovered yet — `defineAdapter`/`definePlugin` calls feed this)'\n\n  return `${HEADER}\ndeclare module '@forinda/kickjs' {\n  /**\n   * Map of every plugin/adapter \\`name\\` discovered in the project. The\n   * value type is the kind tag (\\`'plugin'\\` or \\`'adapter'\\`); the\n   * \\`keyof\\` of this interface narrows \\`dependsOn\\` so misspelled deps\n   * become compile errors instead of boot-time \\`MissingMountDepError\\`.\n   */\n  interface KickJsPluginRegistry {\n${body}\n  }\n}\n\nexport {}\n`\n}\n\n/**\n * Render the augmentation manifest — one block per `defineAugmentation`\n * call discovered in the project. The output is a `.d.ts` file that does\n * nothing at runtime but acts as in-IDE documentation.\n */\nexport function renderAugmentations(items: DiscoveredAugmentation[]): string {\n  if (items.length === 0) {\n    return `${HEADER}\n// No augmentations discovered.\n//\n// Plugins advertise augmentable interfaces via:\n//\n//   import { defineAugmentation } from '@forinda/kickjs'\n//   defineAugmentation('FeatureFlags', {\n//     description: 'Feature flag shape consumed by FlagsPlugin',\n//     example: '{ beta: boolean; rolloutPercentage: number }',\n//   })\n//\n// See \\`docs/guide/typegen.md#augmentations\\` for the full pattern.\nexport {}\n`\n  }\n\n  // Dedupe by name — multiple plugins shouldn't claim the same name,\n  // but if they do we keep the first.\n  const byName = new Map<string, DiscoveredAugmentation>()\n  for (const item of items) {\n    if (!byName.has(item.name)) byName.set(item.name, item)\n  }\n\n  const blocks: string[] = []\n  for (const item of [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name))) {\n    const docLines: string[] = []\n    if (item.description) {\n      for (const line of item.description.split('\\n')) docLines.push(` * ${line}`)\n    }\n    if (item.example) {\n      docLines.push(` * @example`, ` * \\`\\`\\`ts`)\n      for (const line of item.example.split('\\n')) docLines.push(` * ${line}`)\n      docLines.push(` * \\`\\`\\``)\n    }\n    docLines.push(` * @see ${item.relativePath}`)\n    blocks.push(\n      ['/**', ...docLines, ' */', `export interface ${item.name}Augmentation {}`].join('\\n'),\n    )\n  }\n\n  return `${HEADER}\n// Catalogue of augmentable interfaces in this project. The interfaces\n// below are documentation only — augment the source-of-truth interfaces\n// in your own \\`d.ts\\` files (the framework declares the actual types).\n\n${blocks.join('\\n\\n')}\n`\n}\n","/**\n * Token convention validator (architecture.md §22.4 #1).\n *\n * Warns (never errors) on `createToken('literal')` calls whose literal\n * doesn't match the convention from §22.2:\n *\n *   <scope>/<PascalKey>[/<suffix>][:<instance>[:<extra>]*]\n *\n * Where `<scope>` is lowercase + may start with the reserved `kick/`\n * prefix for first-party tokens. Legacy framework tokens that started\n * with `kickjs.` (pre-§22) are exempt — they get migrated alongside\n * the first-party adapter migrations.\n *\n * The matching reserved-prefix check (third-party tokens squatting\n * `kick/`) is the responsibility of `@forinda/kickjs-lint`'s\n * `token-reserved-prefix` rule, not the typegen layer — different\n * audience (adopter codebase) and different default severity.\n *\n * @module @forinda/kickjs-cli/typegen/token-conventions\n */\n\nimport type { DiscoveredToken } from './scanner'\n\n/**\n * Regex for the §22.2 token shape. Breakdown:\n *\n * - `^(kick\\/)?` — optional reserved framework prefix.\n * - `([a-z][\\w-]*\\/[A-Z]\\w*)` — `<scope>/<PascalKey>`. Scope is\n *   lowercase, key is PascalCase.\n * - `(\\/.+)?` — optional `/suffix` for sub-flavours\n *   (e.g. `mycorp/Cache/redis`).\n * - `(:[a-z][\\w-]+(:[a-z][\\w-]+)*)?` — optional `:instance` (and\n *   further `:extra` colon-sections) for `.scoped()` shards.\n */\nconst TOKEN_CONVENTION_REGEX =\n  /^(kick\\/)?([a-z][\\w-]*\\/[A-Z]\\w*)(\\/.+)?(:[a-z][\\w-]+(:[a-z][\\w-]+)*)?$/\n\nconst LEGACY_PREFIX = 'kickjs.'\n\nexport interface TokenConventionWarning {\n  token: string\n  variable: string | null\n  filePath: string\n  reason: string\n  suggestion?: string\n}\n\nexport function validateTokenConventions(\n  tokens: readonly DiscoveredToken[],\n): TokenConventionWarning[] {\n  const warnings: TokenConventionWarning[] = []\n  for (const token of tokens) {\n    const name = token.name\n    if (name.startsWith(LEGACY_PREFIX)) continue\n    if (TOKEN_CONVENTION_REGEX.test(name)) continue\n    warnings.push({\n      token: name,\n      variable: token.variable,\n      filePath: token.relativePath,\n      reason: 'does not match `<scope>/<PascalKey>[/<suffix>][:<instance>]`',\n      suggestion: suggestRename(name),\n    })\n  }\n  return warnings\n}\n\nfunction suggestRename(name: string): string | undefined {\n  if (/^[A-Z]\\w*$/.test(name)) {\n    return `'<scope>/${name}' (e.g. 'mycorp/${name}')`\n  }\n  if (name.includes('.')) {\n    return `consider '<scope>/PascalKey' instead of dotted form`\n  }\n  const slashLower = /^([a-z][\\w-]*)\\/([a-z]\\w*)$/.exec(name)\n  if (slashLower) {\n    const [, scope, key] = slashLower\n    return `'${scope}/${key.charAt(0).toUpperCase()}${key.slice(1)}'`\n  }\n  return undefined\n}\n","/**\n * Walks every `assetMap` entry's source directory + emits a typed\n * `KickAssets` ambient augmentation (assets-plan.md PR 4). Generates\n * `.kickjs/types/assets.d.ts` so adopters get autocomplete on\n * `assets.<namespace>.<key>` and `@Asset('<namespace>/<key>')`.\n *\n * Pure module — no side effects beyond what the caller does with the\n * returned content. Mirrors the shape of `renderPlugins` /\n * `renderRegistry` in the generator so the typegen output stays\n * consistent across surfaces.\n *\n * @module @forinda/kickjs-cli/typegen/asset-types\n */\n\nimport { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { globSync } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry } from '../config'\n\nexport interface DiscoveredAssetEntry {\n  namespace: string\n  /**\n   * Path under the namespace — what comes after the `<namespace>/`\n   * prefix in the logical key. Stripped of extension when the file's\n   * basename was unique within the namespace, or carries the full\n   * extension when a collision was auto-resolved.\n   */\n  key: string\n}\n\nexport interface DiscoveredAssets {\n  entries: DiscoveredAssetEntry[]\n  count: number\n}\n\nexport function discoverAssets(\n  assetMap: Record<string, AssetMapEntry> | undefined,\n  cwd: string,\n): DiscoveredAssets {\n  if (!assetMap) return { entries: [], count: 0 }\n\n  const seen = new Map<string, DiscoveredAssetEntry>()\n  for (const [namespace, entry] of Object.entries(assetMap)) {\n    if (!entry || typeof entry.src !== 'string') continue\n    const srcAbs = resolve(cwd, entry.src)\n    if (!isDir(srcAbs)) continue\n    // Mirror build.ts: same `glob` engine, same defaults — typegen +\n    // build agree on what counts as an asset, including full minimatch\n    // patterns (extglob, negation, etc.) that the old lite matcher\n    // silently treated as match-all.\n    const matches = globSync(entry.glob ?? '**/*', {\n      cwd: srcAbs,\n      nodir: true,\n      dot: false,\n      posix: true,\n    })\n    matches.sort()\n    // Run the same key-shaping logic the build pipeline + dev resolver\n    // use, so the emitted type matches the manifest exactly. Auto-mode\n    // keeps extensions on collision groups; singletons stay stripped.\n    const { pairs } = groupAssetKeys(namespace, matches, {\n      strategy: entry.keys ?? 'auto',\n    })\n    for (const { key: logical } of pairs) {\n      // The helper returns the full `<namespace>/<...>` form; strip\n      // the namespace prefix so the entry shape matches the legacy\n      // contract (key = path under namespace).\n      const subKey = logical.slice(namespace.length + 1)\n      seen.set(logical, { namespace, key: subKey })\n    }\n  }\n  return { entries: [...seen.values()], count: seen.size }\n}\n\nexport function renderAssetTypes(discovered: DiscoveredAssets): string {\n  const HEADER = `/* eslint-disable */\n// AUTO-GENERATED by \\`kick typegen\\`. DO NOT EDIT.\n// Re-run with \\`kick typegen\\` or rely on \\`kick dev\\` to refresh.\n`\n\n  if (discovered.entries.length === 0) {\n    return `${HEADER}\ndeclare module '@forinda/kickjs' {\n  /**\n   * Map of every typed asset discovered in the project's assetMap.\n   * (No assetMap entries discovered yet — declare with\n   *  \\`assetMap: { name: { src: 'src/...' } }\\` in kick.config.ts.)\n   */\n  interface KickAssets {}\n}\n\nexport {}\n`\n  }\n\n  const tree: TreeNode = {}\n  for (const entry of discovered.entries) {\n    const path = `${entry.namespace}/${entry.key}`.split('/')\n    let node = tree\n    for (let i = 0; i < path.length - 1; i++) {\n      const part = path[i]\n      const existing = node[part]\n      if (existing === LEAF) {\n        // The intermediate path was previously claimed by a leaf —\n        // collision between a file (`mails/welcome.ejs`) and a\n        // directory (`mails/welcome/x.ejs`). Promote to a sub-tree\n        // so the directory wins (consistent with the file-tree\n        // semantics; the build pipeline emits a runtime warning for\n        // the colliding leaf).\n        const promoted: TreeNode = {}\n        node[part] = promoted\n        node = promoted\n      } else {\n        if (!existing) node[part] = {}\n        node = node[part] as TreeNode\n      }\n    }\n    const leaf = path[path.length - 1]\n    if (typeof node[leaf] === 'object') {\n      // The leaf collides with an already-built subtree. Skip; the\n      // directory wins (matches the promote-to-subtree branch above).\n      continue\n    }\n    node[leaf] = LEAF\n  }\n\n  const body = renderTree(tree, '    ')\n  return `${HEADER}\ndeclare module '@forinda/kickjs' {\n  /**\n   * Map of every typed asset discovered in the project's assetMap.\n   * Each leaf is a \\`() => string\\` thunk that returns the resolved\n   * absolute path for the file in the current run mode (dev → src,\n   * prod → dist).\n   */\n  interface KickAssets {\n${body}\n  }\n}\n\nexport {}\n`\n}\n\nconst LEAF = Symbol('asset-leaf')\ntype TreeNode = { [key: string]: TreeNode | typeof LEAF }\n\nfunction renderTree(node: TreeNode, indent: string): string {\n  const keys = Object.keys(node).toSorted()\n  const lines: string[] = []\n  for (const key of keys) {\n    const child = node[key]\n    const safeKey = isIdentifier(key) ? key : JSON.stringify(key)\n    if (child === LEAF) {\n      lines.push(`${indent}${safeKey}: () => string`)\n    } else {\n      lines.push(`${indent}${safeKey}: {`)\n      lines.push(renderTree(child, `${indent}  `))\n      lines.push(`${indent}}`)\n    }\n  }\n  return lines.join('\\n')\n}\n\nfunction isIdentifier(str: string): boolean {\n  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(str)\n}\n\nfunction isDir(path: string): boolean {\n  try {\n    return statSync(path).isDirectory()\n  } catch {\n    return false\n  }\n}\n","/**\n * Public entry point for the KickJS typegen module.\n *\n * Used by:\n * - `kick typegen` (one-shot or watch mode)\n * - `kick dev` (auto-runs once before Vite starts; refreshes when files change)\n *\n * @module @forinda/kickjs-cli/typegen\n */\n\nimport { resolve, basename, dirname, join } from 'node:path'\nimport { mkdir, readdir, readFile, stat, unlink, writeFile } from 'node:fs/promises'\nimport { patchModuleGlobSource, suggestGlobsForOrphans } from './glob-fix'\nimport { scanProject, scanProjectIncremental, type ScanDelta, type ScanResult } from './scanner'\nimport {\n  buildModuleTokens,\n  buildServiceTokens,\n  REGISTRY_DECORATORS,\n  TokenCollisionError,\n} from './render/manifest'\nimport { validateTokenConventions, type TokenConventionWarning } from './token-conventions'\nimport { discoverAssets } from './asset-types'\nimport type { AssetMapEntry } from '../config'\nimport type { TypegenPluginResult } from './plugin'\n\nexport type {\n  DiscoveredClass,\n  DiscoveredToken,\n  DiscoveredInject,\n  DiscoveredEnv,\n  DiscoveredPluginOrAdapter,\n  DiscoveredAugmentation,\n  ClassCollision,\n  ScanResult,\n} from './scanner'\nexport { TokenCollisionError } from './render/manifest'\nexport { TypegenDriftError } from './plugin'\nexport { validateTokenConventions, type TokenConventionWarning } from './token-conventions'\n\n/**\n * Result of a typegen run — useful for logging and tests. Computed from\n * the scan result + asset discovery; the per-file emission itself is now\n * owned entirely by the typegen plugins (see `builtin/`).\n */\nexport interface GenerateResult {\n  /** Number of registry-decorated classes (KickJsRegistry entries) */\n  registryEntries: number\n  /** Number of unique service tokens (classes + createToken + @Inject literals) */\n  serviceTokens: number\n  /** Number of module tokens */\n  moduleTokens: number\n  /** Number of route entries */\n  routeEntries: number\n  /** Number of unique plugin/adapter names */\n  pluginEntries: number\n  /** Number of unique `defineAugmentation` calls */\n  augmentationEntries: number\n  /** Number of typed asset entries */\n  assetEntries: number\n  /** Whether a typed env augmentation will be emitted */\n  envWritten: boolean\n  /** Files written this pass (barrel + plugin outputs), for the sweep */\n  written: string[]\n  /** Number of collisions (only > 0 with allowDuplicates) */\n  resolvedCollisions: number\n}\n\n/** Options for `runTypegen` */\nexport interface RunTypegenOptions {\n  /** Project root (defaults to `process.cwd()`) */\n  cwd?: string\n  /** Source directory to scan (defaults to `src`) */\n  srcDir?: string\n  /** Output directory (defaults to `.kickjs/types`) */\n  outDir?: string\n  /** Suppress console output */\n  silent?: boolean\n  /**\n   * Patch module `import.meta.glob(...)` calls in place to cover decorated\n   * classes the scanner flags as orphaned (their file isn't imported by any\n   * module glob, so the decorator never fires at runtime). Wired to\n   * `kick typegen --fix`. Off by default — diagnostics only.\n   */\n  fix?: boolean\n  /**\n   * When `true`, duplicate class names are auto-namespaced by file path\n   * instead of throwing. `kick dev` enables this so the dev server is\n   * never blocked by an in-progress rename. CLI default is `false` so\n   * `kick typegen` (and CI) catches collisions early. */\n  allowDuplicates?: boolean\n  /**\n   * Disable the persistent per-file scan cache (`.kickjs/cache/scan.json`).\n   * Every file is re-read + re-extracted from cold. Escape hatch for the\n   * rare `mtimeMs:size` signature collision (a file edited so fast its\n   * mtime + size are unchanged) where the cache would serve a stale\n   * extract. Wired to `kick typegen --no-cache` / `kick dev --no-typegen-cache`.\n   */\n  noCache?: boolean\n  /**\n   * Schema validator used to derive `body`/`query`/`params` types from\n   * route metadata. Currently only `'zod'` is supported; `false` (the\n   * default) leaves these fields as `unknown`. Loaded from\n   * `kick.config.ts` `typegen.schemaValidator` when invoked via the CLI.\n   */\n  schemaValidator?: 'zod' | 'kickjs-schema' | false\n  /**\n   * Path to the env schema file (relative to `cwd`). The file must\n   * default-export a `defineEnv(...)` schema for the typed `KickEnv`\n   * augmentation to be emitted. Defaults to `'src/env.ts'`. Set to\n   * `false` to disable env typing entirely.\n   */\n  envFile?: string | false\n  /**\n   * Asset map from `kick.config.ts`. When set, `runTypegen` walks\n   * each entry's `src` directory + emits `.kickjs/types/assets.d.ts`\n   * augmenting `KickAssets` for autocomplete on `assets.x.y()` and\n   * `@Asset('x/y')`. Omit to skip the asset typegen pass entirely.\n   */\n  assetMap?: Record<string, AssetMapEntry>\n  /**\n   * Whether `runTypegen` should also run the TypegenPlugin pipeline\n   * (`runAllPluginTypegens`) after the legacy generator pass. Defaults\n   * to `true` so single-shot callers (kick g, commands/typegen, tests)\n   * keep getting a fully-refreshed `.kickjs/types/` from one entry\n   * point. `watchTypegen` flips this to `false` because it manages\n   * the plugin pass itself + would otherwise double-run it on every\n   * filesystem trigger.\n   */\n  runPlugins?: boolean\n  /**\n   * Exact watcher delta (Vite chokidar events). When present, the scan\n   * runs incrementally — re-extracting only the changed files and\n   * skipping the directory walk — and the same delta is forwarded to\n   * the plugin pass. `kick dev` supplies this on every file change.\n   */\n  changedFiles?: ScanDelta\n}\n\n/** Resolve options to absolute paths */\nfunction resolveOptions(opts: RunTypegenOptions): {\n  cwd: string\n  srcDir: string\n  outDir: string\n  silent: boolean\n  allowDuplicates: boolean\n  schemaValidator: 'zod' | 'kickjs-schema' | false\n  envFile: string | false\n} {\n  const cwd = opts.cwd ?? process.cwd()\n  return {\n    cwd,\n    srcDir: resolve(cwd, opts.srcDir ?? 'src'),\n    outDir: resolve(cwd, opts.outDir ?? '.kickjs/types'),\n    silent: opts.silent ?? false,\n    allowDuplicates: opts.allowDuplicates ?? false,\n    schemaValidator: opts.schemaValidator ?? false,\n    envFile: opts.envFile ?? 'src/env.ts',\n  }\n}\n\n/**\n * Run a single typegen pass: scan source files, generate `.d.ts` files.\n *\n * Returns the discovered scan result alongside the generation result so\n * callers (`kick dev`, devtools) can log them or feed them to other tools.\n *\n * Throws `TokenCollisionError` if duplicate class names are found and\n * `allowDuplicates` is false.\n */\nexport async function runTypegen(opts: RunTypegenOptions = {}): Promise<{\n  scan: ScanResult\n  result: GenerateResult\n  /** Token convention warnings — empty when every literal matches §22.2. */\n  tokenWarnings: TokenConventionWarning[]\n}> {\n  const { cwd, srcDir, outDir, silent, allowDuplicates, envFile } = resolveOptions(opts)\n\n  const start = Date.now()\n  const scanOpts = {\n    root: srcDir,\n    cwd,\n    // Omitting cacheDir disables the persistent scan cache entirely —\n    // every file is re-read cold (the --no-cache escape hatch).\n    cacheDir: opts.noCache ? undefined : resolve(cwd, '.kickjs', 'cache'),\n    // Pass through unless explicitly disabled\n    envFile: envFile === false ? undefined : envFile,\n  }\n  const scan = opts.changedFiles\n    ? await scanProjectIncremental(scanOpts, opts.changedFiles)\n    : await scanProject(scanOpts)\n\n  // Collision gate. This used to live inside the legacy generator\n  // (`generateTypes` threw before writing). Now that every file is\n  // emitted by an isolated plugin, the gate must run here — at the\n  // orchestration level, before any plugin executes — so a duplicate\n  // class name fails the whole command loudly instead of being silently\n  // namespaced by the registry plugin.\n  if (scan.collisions.length > 0 && !allowDuplicates) {\n    throw new TokenCollisionError(scan.collisions)\n  }\n\n  const assets = discoverAssets(opts.assetMap, cwd)\n\n  // Every `.kickjs/types/*` file is now owned by a typegen plugin\n  // (builtin/ + adopter plugins). Run the pipeline by default so\n  // single-shot callers (kick g <leaf> / kick new / tests) stay on one\n  // entry point and see a fully-refreshed `.kickjs/types/` after the\n  // call returns. The `kick typegen` / `kick dev` / `kick build` /\n  // watch paths opt out (`runPlugins: false`) because they drive the\n  // plugin pass externally (for --check + per-plugin status) and would\n  // otherwise double-run it — they call `writeTypegenArtifacts`\n  // themselves after their own plugin pass.\n  let pluginResults: TypegenPluginResult[] = []\n  const written: string[] = []\n  if (opts.runPlugins !== false) {\n    try {\n      const { runAllPluginTypegens } = await import('./run-plugins')\n      const { loadKickConfig } = await import('../config')\n      const pluginConfig = await loadKickConfig(cwd)\n      pluginResults = await runAllPluginTypegens({\n        cwd,\n        config: pluginConfig,\n        silent: true,\n        changedFiles: opts.changedFiles,\n      })\n    } catch (err) {\n      // Broken plugins shouldn't block dev tooling. The runner already\n      // isolates each plugin (per-plugin try/catch), so reaching here\n      // means a non-plugin failure (scanner, fs). Surfacing as a throw\n      // would abort the wider command (kick g, kick new); log + continue.\n      if (!silent) {\n        const msg = err instanceof Error ? err.message : String(err)\n        console.warn(`  kick typegen: plugin pipeline failed (${msg}) — continuing`)\n      }\n    }\n    written.push(...(await writeTypegenArtifacts(outDir, pluginResults, silent)))\n  }\n\n  const tokenWarnings = validateTokenConventions(scan.tokens)\n  const result = buildGenerateResult(scan, assets.count, written)\n  const elapsed = Date.now() - start\n\n  if (!silent) {\n    const where = outDir.replace(cwd + '/', '')\n    const collisionNote =\n      result.resolvedCollisions > 0 ? `, ${result.resolvedCollisions} collisions namespaced` : ''\n    const envNote = result.envWritten ? ', env typed' : ''\n    const pluginNote = result.pluginEntries > 0 ? `, ${result.pluginEntries} plugins/adapters` : ''\n    const augNote =\n      result.augmentationEntries > 0 ? `, ${result.augmentationEntries} augmentations` : ''\n    const assetNote = result.assetEntries > 0 ? `, ${result.assetEntries} assets` : ''\n    console.log(\n      `  kick typegen → ${result.serviceTokens} services, ${result.routeEntries} routes, ${result.moduleTokens} modules${pluginNote}${augNote}${assetNote}${envNote}${collisionNote} → ${where} (${elapsed}ms)`,\n    )\n    if (tokenWarnings.length > 0) {\n      console.warn(\n        `  kick typegen: ${tokenWarnings.length} token(s) don't match the §22.2 convention:`,\n      )\n      for (const warning of tokenWarnings) {\n        const variableNote = warning.variable ? ` [${warning.variable}]` : ''\n        console.warn(\n          `    '${warning.token}' (${warning.filePath})${variableNote} — ${warning.reason}`,\n        )\n        if (warning.suggestion) {\n          console.warn(`      → suggestion: ${warning.suggestion}`)\n        }\n      }\n    }\n  }\n\n  // forinda/kick-js#235 §4 — decorated classes sitting inside a module\n  // directory whose globs don't pick them up. At runtime the decorator never\n  // fires; downstream code paths get confusing `MissingContributorError` or\n  // silent misroutes. `--fix` patches the module globs; otherwise we print the\n  // exact recursive patterns to add. Runs outside the `!silent` guard so\n  // `--fix` still applies under `--silent`.\n  if (scan.orphanedClasses.length > 0) {\n    await handleOrphanedClasses(scan.orphanedClasses, { fix: opts.fix ?? false, silent })\n  }\n\n  return { scan, result, tokenWarnings }\n}\n\n/**\n * Report (and optionally fix) decorated classes whose files aren't imported by\n * any module's `import.meta.glob(...)`. Groups orphans by their owning module\n * file, derives the minimal recursive globs that would cover them, and either\n * patches the module source in place (`fix`) or prints a copy-pasteable\n * suggestion + the `--fix` hint.\n */\nasync function handleOrphanedClasses(\n  orphans: ScanResult['orphanedClasses'],\n  { fix, silent }: { fix: boolean; silent: boolean },\n): Promise<void> {\n  // Group by the module file whose globs missed them.\n  const byModule = new Map<string, typeof orphans>()\n  for (const o of orphans) {\n    const list = byModule.get(o.moduleFilePath) ?? []\n    list.push(o)\n    byModule.set(o.moduleFilePath, list)\n  }\n\n  if (fix) {\n    let patched = 0\n    const skipped: string[] = []\n    for (const [moduleFile, group] of byModule) {\n      const patterns = suggestGlobsForOrphans(group)\n      try {\n        const source = await readFile(moduleFile, 'utf-8')\n        const next = patchModuleGlobSource(source, patterns)\n        if (next && next !== source) {\n          await writeFile(moduleFile, next)\n          patched++\n          if (!silent) {\n            console.log(\n              `  kick typegen --fix: patched ${moduleFile}\\n      + ${patterns.join(', ')}`,\n            )\n          }\n        } else {\n          skipped.push(moduleFile)\n        }\n      } catch {\n        skipped.push(moduleFile)\n      }\n    }\n    if (!silent) {\n      if (patched > 0) {\n        console.log(\n          `  kick typegen --fix: updated ${patched} module glob(s) — re-run typegen to pick up the now-loaded classes.`,\n        )\n      }\n      if (skipped.length > 0) {\n        console.warn(\n          `  kick typegen --fix: could not auto-patch ${skipped.length} module(s) (no import.meta.glob() call found) — add the patterns by hand:`,\n        )\n        for (const m of skipped) {\n          const group = byModule.get(m) ?? []\n          console.warn(`    ${m}: ${suggestGlobsForOrphans(group).join(', ')}`)\n        }\n      }\n    }\n    return\n  }\n\n  if (silent) return\n\n  console.warn(\n    `  kick typegen: ${orphans.length} decorated class(es) not matched by any module's import.meta.glob():`,\n  )\n  for (const [moduleFile, group] of byModule) {\n    for (const orphan of group) {\n      console.warn(`    @${orphan.decorator} ${orphan.className} (${orphan.relativePath})`)\n    }\n    const patterns = suggestGlobsForOrphans(group)\n    console.warn(`      → add to import.meta.glob([...]) in ${moduleFile}:`)\n    console.warn(`          ${patterns.map((p) => `'${p}'`).join(', ')}`)\n  }\n  console.warn(`      → or run \\`kick typegen --fix\\` to apply these automatically.`)\n}\n\n/** Derive the logging/test `GenerateResult` from a scan — no files written here. */\nfunction buildGenerateResult(\n  scan: ScanResult,\n  assetCount: number,\n  written: string[],\n): GenerateResult {\n  const colliding = new Set(scan.collisions.map((c) => c.className))\n  const registryClasses = scan.classes.filter((c) => REGISTRY_DECORATORS.has(c.decorator))\n  const serviceNames = buildServiceTokens(scan.classes, scan.tokens, scan.injects, colliding)\n  return {\n    registryEntries: registryClasses.length,\n    serviceTokens: new Set(serviceNames).size,\n    moduleTokens: buildModuleTokens(scan.classes).length,\n    routeEntries: scan.routes.length,\n    pluginEntries: new Set(scan.pluginsAndAdapters.map((p) => p.name)).size,\n    augmentationEntries: new Set(scan.augmentations.map((a) => a.name)).size,\n    assetEntries: assetCount,\n    envWritten: scan.env !== null,\n    written,\n    resolvedCollisions: scan.collisions.length,\n  }\n}\n\n/**\n * Post-plugin-pass finalisation: write the `.kickjs/.gitignore` guard\n * and sweep stale legacy files. Shared by `runTypegen` (single-shot\n * mode) and the split-mode callers (`kick typegen` / `kick dev` /\n * watch) so the artifact-writing + sweep stay identical across both.\n *\n * No barrel `index.d.ts` is emitted: the scaffolded tsconfig pulls\n * `.kickjs/types/**` in via `include` globs, so every `declare module`\n * / `declare global` augmentation in the per-plugin files applies by\n * inclusion. The old barrel + its `ServiceToken`/`ModuleToken`\n * re-exports were redundant; they're swept as legacy orphans.\n *\n * Returns the list of files considered \"written\" this pass (the plugin\n * outputs) for the caller's bookkeeping.\n */\nexport async function writeTypegenArtifacts(\n  outDir: string,\n  pluginResults: readonly TypegenPluginResult[],\n  silent: boolean,\n): Promise<string[]> {\n  await mkdir(outDir, { recursive: true })\n  // `.kickjs/.gitignore` keeps the generated tree out of git even if the\n  // project's root .gitignore predates the `.kickjs/` convention.\n  await writeFile(\n    join(dirname(outDir), '.gitignore'),\n    '# Auto-generated by kick typegen\\n*\\n',\n    'utf-8',\n  )\n  const written = pluginResults.filter((r) => r.outFile).map((r) => r.outFile as string)\n  await sweepStaleTypegen(outDir, written, pluginResults, silent)\n  return written\n}\n\n/**\n * Watch mode for `kick typegen --watch`.\n *\n * Uses Node's built-in `fs.watch` (recursive, available on Linux 22+ and\n * macOS 19+). Falls back gracefully if recursive watch is not supported.\n *\n * Debounces re-runs by 100ms so a bulk file change (e.g. `kick g module`\n * creating 5 files at once) emits one regen, not five.\n *\n * In watch mode collisions are reported but never thrown — the watcher\n * keeps running so the user can fix the rename and the next scan\n * recovers automatically.\n *\n * Returns a `stop()` function that closes the watcher.\n */\nexport async function watchTypegen(opts: RunTypegenOptions = {}): Promise<() => void> {\n  const resolved = resolveOptions(opts)\n  const { srcDir, silent, cwd } = resolved\n  // Watch mode always tolerates collisions — otherwise an in-progress\n  // rename would crash the dev loop. The error is still printed.\n  // `runPlugins: false` keeps `runTypegen` from double-running the\n  // plugin pipeline; the watcher invokes `runPlugins()` explicitly\n  // after each `runLegacy()` so both phases land before the sweep.\n  // `resolved` comes from resolveOptions, which doesn't carry noCache —\n  // thread it through explicitly so `kick typegen --watch --no-cache`\n  // disables the scan cache on every rebuild too.\n  const runOpts: RunTypegenOptions = {\n    ...resolved,\n    allowDuplicates: true,\n    runPlugins: false,\n    noCache: opts.noCache,\n  }\n\n  // Polling is the right strategy for Docker bind mounts, WSL crosses,\n  // NFS, and some kernel/filesystem combos where `fs.watch` returns\n  // without errors but events silently drop. Adopters opt in via\n  // `KICKJS_WATCH_POLLING=1`; default stays event-based (lower CPU).\n  const forcePolling =\n    process.env.KICKJS_WATCH_POLLING === '1' || process.env.KICKJS_WATCH_POLLING === 'true'\n\n  // Lazy-import the plugin pipeline + config loader to avoid an eager\n  // module-eval cycle (this file is reachable from plugin/builtins via\n  // commands/typegen → ../typegen).\n  const [{ runAllPluginTypegens }, { loadKickConfig }] = await Promise.all([\n    import('./run-plugins'),\n    import('../config'),\n  ])\n  const pluginConfig = await loadKickConfig(cwd)\n  // `runLegacy` now just runs the scan + collision gate (collisions are\n  // tolerated in watch mode via allowDuplicates) and refreshes the\n  // logging counts; all file emission happens in `runPlugins`.\n  const runLegacy = async () => {\n    try {\n      await runTypegen({ ...runOpts })\n    } catch (err) {\n      if (silent) return\n      if (err instanceof TokenCollisionError) {\n        console.error('\\n' + err.message + '\\n')\n      } else {\n        const msg = err instanceof Error ? err.message : String(err)\n        console.error(`  kick typegen failed: ${msg}`)\n      }\n    }\n  }\n  const runPlugins = async () => {\n    try {\n      const pluginResults = await runAllPluginTypegens({\n        cwd,\n        config: pluginConfig,\n        silent: true,\n      })\n      await writeTypegenArtifacts(resolved.outDir, pluginResults, true)\n    } catch {\n      /* swallow — watcher must never die */\n    }\n  }\n\n  // Initial run — scan/gate pass first, then plugin typegens + artifacts.\n  await runLegacy()\n  await runPlugins()\n\n  const { watch } = await import('node:fs')\n\n  let timer: ReturnType<typeof setTimeout> | null = null\n  const trigger = (filename: string | null) => {\n    // Only react to TypeScript source changes; ignore everything else\n    if (!filename) return\n    if (!/\\.(ts|tsx|mts|cts)$/.test(filename)) return\n    if (filename.includes('.kickjs')) return\n    if (filename.endsWith('.d.ts')) return\n\n    if (timer) clearTimeout(timer)\n    timer = setTimeout(() => {\n      void runLegacy().then(runPlugins)\n    }, 100)\n  }\n\n  // Forced-polling path — skip fs.watch entirely and just re-scan\n  // periodically. The 2s interval matches the existing fallback so\n  // adopters who flip the env var don't see a dramatic CPU jump.\n  if (forcePolling) {\n    if (!silent) {\n      console.log('  kick typegen: polling mode (KICKJS_WATCH_POLLING)')\n    }\n    // Must drive BOTH phases — the plugin pass owns every\n    // `.kickjs/types/kick__*` file now, so scan/gate alone (runLegacy)\n    // would never refresh types on the polling path. Both closures\n    // swallow their own errors, so the interval loop never dies.\n    const interval = setInterval(() => {\n      void runLegacy().then(runPlugins)\n    }, 2000)\n    return () => clearInterval(interval)\n  }\n\n  let watcher: ReturnType<typeof watch>\n  try {\n    watcher = watch(srcDir, { recursive: true }, (_event, filename) => {\n      trigger(filename)\n    })\n  } catch (err: any) {\n    if (!silent) {\n      console.warn(\n        `  kick typegen: watch mode unavailable (${err?.message ?? err}). Falling back to polling.`,\n      )\n    }\n    // Polling fallback — re-run both phases every 2s (see forcePolling\n    // note above: the plugin pass is the sole emitter, so scan/gate\n    // alone would never refresh types).\n    const interval = setInterval(() => {\n      void runLegacy().then(runPlugins)\n    }, 2000)\n    return () => clearInterval(interval)\n  }\n\n  return () => {\n    if (timer) clearTimeout(timer)\n    watcher.close()\n  }\n}\n\n/**\n * Remove orphaned typegen output. The legacy generator emitted\n * `assets.d.ts`, `env.ts`, and `routes.ts` directly; once those carved\n * into the `kick/assets`, `kick/env`, and `kick/routes` plugins, the\n * legacy file names became stale on disk for any project that had run\n * an older CLI. Without a sweep, both copies coexist and adopters get\n * duplicated `KickAssets` / `KickEnv` / `KickRoutes` augmentations.\n *\n * Strategy: enumerate the top level of `outDir`, keep the union of\n * generator-written files + plugin-written files, unlink anything\n * else. Subdirectories are left alone — typegen never creates them.\n * Errors are swallowed (silent → no log) so a transient ENOENT or\n * permission glitch never aborts the wider command.\n */\nexport async function sweepStaleTypegen(\n  outDir: string,\n  generatorWritten: readonly string[],\n  pluginResults: readonly TypegenPluginResult[],\n  silent: boolean,\n): Promise<string[]> {\n  const expected = new Set<string>()\n  for (const file of generatorWritten) expected.add(basename(file))\n  for (const r of pluginResults) {\n    if (r.outFile) expected.add(basename(r.outFile))\n  }\n  let entries: string[]\n  try {\n    entries = await readdir(outDir)\n  } catch {\n    return []\n  }\n  const removed: string[] = []\n  for (const name of entries) {\n    // Allowlist, NOT denylist. The earlier \"delete anything not in the\n    // expected set\" form was a footgun: if the plugin pass aborted\n    // (e.g. one plugin threw and the runner bubbled it up, so\n    // `pluginResults` came back empty), the expected set lost\n    // `kick__routes.ts` / `kick__assets.d.ts` / `kick__env.ts` and the\n    // sweep deleted those good files — wiping controller route types\n    // project-wide. We now only remove the specific pre-carve filenames\n    // the legacy generator used to emit, and only when the current pass\n    // didn't (re)write them. Anything else is left untouched.\n    if (!LEGACY_ORPHAN_FILES.has(name)) continue\n    if (expected.has(name)) continue\n    const abs = resolve(outDir, name)\n    try {\n      const s = await stat(abs)\n      if (!s.isFile()) continue\n      await unlink(abs)\n      removed.push(name)\n    } catch {\n      // Best-effort; don't crash the typegen pass on a stat/unlink miss.\n    }\n  }\n  if (removed.length > 0 && !silent) {\n    console.log(`  kick typegen: swept ${removed.length} stale file(s): ${removed.join(', ')}`)\n  }\n  return removed\n}\n\n/**\n * Filenames the legacy monolithic generator (`generator.ts`, now\n * removed) emitted directly, before every augmentation became its own\n * typegen plugin. A project that upgrades across this change has these\n * as orphans on disk; the sweep removes them so the augmentations aren't\n * declared twice and the dropped `index.d.ts` barrel doesn't linger.\n *\n * Split into two waves only for documentation:\n * - `assets.d.ts` / `env.ts` / `routes.ts` — the M2.B-T8 carve.\n * - `registry.d.ts` / `services.d.ts` / `modules.d.ts` / `plugins.d.ts`\n *   / `augmentations.d.ts` / `index.d.ts` — this plugin-only refactor.\n *\n * None collide with any current plugin output (all `kick__*`), so the\n * sweep can never touch live output.\n */\nconst LEGACY_ORPHAN_FILES: ReadonlySet<string> = new Set([\n  'assets.d.ts',\n  'env.ts',\n  'routes.ts',\n  'registry.d.ts',\n  'services.d.ts',\n  'modules.d.ts',\n  'plugins.d.ts',\n  'augmentations.d.ts',\n  'index.d.ts',\n])\n"],"mappings":";;;;;;;;;;wZAiCA,MAGM,EAAqB,CACzB,UACA,SACA,UACA,qBACA,gBACA,cACA,SACA,eACA,cACF,EASA,SAAS,EAAc,EAAsC,CAC3D,GAAI,CAAC,GAAS,OAAO,GAAU,SAAU,MAAO,GAChD,IAAM,EAAU,EAChB,OAAO,EAAmB,MAAO,GAAQ,MAAM,QAAQ,EAAQ,EAAI,CAAC,CACtE,CAsBA,IAAa,EAAb,MAAa,CAAU,CACrB,KACA,KACA,KAAwB,IAAI,IAC5B,QAA2B,IAAI,IAE/B,YAAoB,EAAc,EAA+B,CAC/D,KAAK,KAAO,EACZ,KAAK,KAAO,CACd,CAOA,aAAa,KAAK,EAAsC,CACtD,IAAM,EAAO,EAAK,EAAU,WAAW,EACjC,EAAO,IAAI,IACjB,GAAI,CACF,IAAM,EAAM,MAAM,EAAS,EAAM,OAAO,EAClC,EAAM,KAAK,MAAM,CAAG,EAC1B,GAAI,EAAI,UAAY,GAAiB,EAAI,MAClC,IAAA,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,EAAI,KAAK,EAC9C,GAAS,OAAO,EAAM,KAAQ,UAAY,EAAc,EAAM,OAAO,GACvE,EAAK,IAAI,EAAM,CAAK,CAI5B,MAAQ,CAER,CACA,OAAO,IAAI,EAAU,EAAM,CAAI,CACjC,CAGA,aAAa,UAAU,EAA0C,CAC/D,GAAI,CACF,IAAM,EAAI,MAAM,EAAK,CAAQ,EAC7B,MAAO,GAAG,EAAE,QAAQ,GAAG,EAAE,MAC3B,MAAQ,CACN,OAAO,IACT,CACF,CAOA,IAAI,EAAkB,EAAiC,CACrD,IAAM,EAAQ,KAAK,KAAK,IAAI,CAAQ,EACpC,OAAO,GAAS,EAAM,MAAQ,EAAM,EAAM,QAAU,IACtD,CAGA,IAAI,EAAkB,EAAa,EAA4B,CAC7D,KAAK,KAAK,IAAI,EAAU,CAAO,EAC/B,KAAK,QAAQ,IAAI,EAAU,CAAG,CAChC,CAGA,aAAwB,CACtB,MAAO,CAAC,GAAG,KAAK,KAAK,KAAK,CAAC,CAC7B,CAQA,KAAK,EAAsC,CACzC,OAAO,KAAK,KAAK,IAAI,CAAQ,CAAC,EAAE,SAAW,IAC7C,CAMA,MAAM,EAA2B,CAC/B,IAAM,EAAQ,KAAK,KAAK,IAAI,CAAQ,EAIpC,OAHK,GACL,KAAK,KAAK,IAAI,EAAU,EAAM,OAAO,EACrC,KAAK,QAAQ,IAAI,EAAU,EAAM,GAAG,EAC7B,IAHY,EAIrB,CAQA,MAAM,MAAsB,CAC1B,IAAM,EAAoC,CAAC,EAC3C,IAAK,GAAM,CAAC,EAAM,KAAY,KAAK,KAAM,CACvC,IAAM,EAAM,KAAK,QAAQ,IAAI,CAAI,EAC7B,IAAK,EAAM,GAAQ,CAAE,MAAK,SAAQ,EACxC,CACA,IAAM,EAAgB,CAAE,QAAS,EAAe,OAAM,EACtD,GAAI,CACF,MAAM,EAAM,EAAQ,KAAK,IAAI,EAAG,CAAE,UAAW,EAAK,CAAC,EACnD,MAAM,EAAU,KAAK,KAAM,KAAK,UAAU,CAAG,EAAG,OAAO,CACzD,MAAQ,CAER,CACF,CACF,EC9IA,IAAI,EAAuC,KAC3C,SAAS,GAAmC,CAE1C,MADA,KAAqB,IAAI,IAAY,CAAe,EAC7C,CACT,CAUA,MAAM,EAAkB,IAAI,IAAI,CAAC,MAAO,OAAQ,MAAO,SAAU,OAAO,CAAC,EAEzE,SAAS,EAAO,EAA+B,CAC7C,OAAO,OAAO,GAAU,YAAY,GAAkB,OAAQ,EAAe,MAAS,QACxF,CAGA,SAASA,EAAK,EAAe,EAAmC,CAC9D,GAAI,MAAM,QAAQ,CAAI,EAAG,CACvB,IAAK,IAAM,KAAQ,EAAM,EAAK,EAAM,CAAK,EACzC,MACF,CACK,KAAO,CAAI,EAChB,GAAM,CAAI,EACV,IAAK,IAAM,KAAO,OAAO,KAAK,CAAI,EAAG,CACnC,GAAI,IAAQ,OAAQ,SACpB,IAAM,EAAQ,EAAK,GACf,OAAO,GAAU,UAAY,GAAgB,EAAK,EAAO,CAAK,CACpE,CALU,CAMZ,CAGA,SAAS,EAAY,EAA8B,CACjD,GAAI,CAAC,EAAO,CAAI,EAAG,OAAO,KAC1B,GAAI,EAAK,OAAS,WAAa,OAAO,EAAK,OAAU,SAAU,OAAO,EAAK,MAC3E,GAAI,EAAK,OAAS,kBAAmB,CACnC,IAAM,EAAS,EAAK,OACd,EAAc,EAAK,YACzB,GAAI,GAAQ,SAAW,IAAM,GAAa,QAAU,KAAO,EAAG,CAC5D,IAAM,EAAU,EAAO,EAAE,CAAqC,OAAO,OACrE,OAAO,OAAO,GAAW,SAAW,EAAS,IAC/C,CACF,CACA,OAAO,IACT,CAEA,SAAS,EAAe,EAA8B,CACpD,OAAO,EAAO,CAAI,GAAK,EAAK,OAAS,aAAgB,EAAK,KAAkB,IAC9E,CAGA,SAAS,EAAW,EAA2B,CAC7C,OAAO,EAAe,EAAK,MAAM,CACnC,CAGA,SAAS,EAAQ,EAAkB,EAA2B,CAC5D,GAAI,CAAC,GAAO,EAAI,OAAS,mBAAoB,OAAO,KACpD,IAAK,IAAM,KAAS,EAAI,YAAqC,CAAC,EAAG,CAC/D,GAAI,EAAK,OAAS,WAAY,SAC9B,IAAM,EAAM,EAAK,IAGjB,IADE,EAAe,CAAG,IAAM,EAAI,OAAS,UAAY,OAAO,EAAI,KAAK,EAAI,EAAY,CAAG,MACtE,EAAM,OAAO,EAAK,KACpC,CACA,OAAO,IACT,CAGA,SAAS,EAAe,EAAyB,CAC/C,IAAM,EAAO,EAAK,YAAmC,GACrD,OAAO,EAAO,CAAG,GAAK,EAAI,OAAS,mBAAqB,EAAM,IAChE,CAEA,SAAS,EAAuB,EAAkB,EAAuB,CACvE,IAAM,EAAQ,EAAQ,EAAK,CAAG,EAC9B,GAAI,CAAC,EAAO,CAAK,GAAK,EAAM,OAAS,kBAAmB,MAAO,CAAC,EAChE,IAAM,EAAgB,CAAC,EACvB,IAAK,IAAM,KAAO,EAAM,UAAsC,CAAC,EAAG,CAChE,IAAM,EAAI,EAAY,CAAE,EACpB,IAAM,MAAM,EAAI,KAAK,CAAC,CAC5B,CACA,OAAO,CACT,CAEA,SAASC,GAAW,EAAkB,EAAqB,CACzD,OAAO,EAAS,EAAK,CAAQ,CAAC,CAAC,MAAM,CAAG,CAAC,CAAC,KAAK,GAAG,CACpD,CAEA,SAASC,GAAkB,EAAwB,CAEjD,OADgB,EAAK,MAAM,kBAAkB,GAAK,CAAC,EAAA,CACpC,IAAK,GAAM,EAAE,MAAM,CAAC,CAAC,CACtC,CAWA,SAAS,GAAgB,EAAW,EAAuB,CACzD,IAAK,IAAM,KAAS,EAAI,YAAqC,CAAC,EAAG,CAC/D,IAAM,EAAQ,EAAK,YAAc,EACjC,GAAI,EAAe,CAAI,IAAM,EAAM,MAAO,GAE1C,GAAI,EAAK,OAAS,mBAAqB,EAAK,OAAS,mBAAoB,CACvE,IAAM,EAAS,EAAK,OAAS,EAAK,SAClC,GAAI,GAAS,EAAe,CAAK,IAAM,EAAM,MAAO,EACtD,CACF,CACA,MAAO,EACT,CAGA,SAAS,EAAa,EAAoB,CACxC,OAAQ,EAAK,YAAqC,CAAC,CACrD,CAGA,SAAS,EAAc,EAAgD,CACrE,IAAM,EAAO,EAAI,WACjB,GAAI,CAAC,EAAO,CAAI,GAAK,EAAK,OAAS,iBAAkB,OAAO,KAC5D,IAAM,EAAO,EAAW,CAAI,EAC5B,OAAO,EAAO,CAAE,OAAM,KAAM,CAAK,EAAI,IACvC,CAgBA,SAAS,GAAiB,EAA4B,CACpD,IAAM,EAAU,IAAI,IACd,EAAiB,IAAI,IAC3B,IAAK,IAAM,KAAS,EAAQ,MAA+B,CAAC,EAAG,CAC7D,GAAI,EAAK,OAAS,oBAAqB,CACrC,IAAM,EAAS,EAAY,EAAK,MAAM,GAAK,GAC3C,IAAK,IAAM,KAAS,EAAK,YAAqC,CAAC,EAAG,CAChE,IAAM,EAAQ,EAAe,EAAK,KAAK,EACnC,GAAO,EAAQ,IAAI,EAAO,CAAE,QAAO,CAAC,CAC1C,CACA,QACF,CAEA,IAAM,EACJ,EAAK,OAAS,sBACV,EACA,EAAK,OAAS,0BAA4B,EAAO,EAAK,WAAW,EAC9D,EAAK,YACN,KACR,GAAI,EAAO,CAAI,GAAK,EAAK,OAAS,sBAChC,IAAK,IAAM,KAAM,EAAK,cAAuC,CAAC,EAAG,CAC/D,IAAM,EAAO,EAAe,EAAE,EAAE,EAC5B,GAAM,EAAe,IAAI,CAAI,CACnC,CAEJ,CACA,MAAO,CAAE,UAAS,gBAAe,CACnC,CAOA,SAAS,GAAiB,EAAoB,EAA6B,CACzE,IAAM,EAAW,EAAI,QAAQ,IAAI,CAAU,EAG3C,OAFI,EAAiB,CAAE,aAAY,OAAQ,EAAS,MAAO,EACvD,EAAI,eAAe,IAAI,CAAU,EAAU,CAAE,aAAY,OAAQ,EAAG,EACjE,CAAE,aAAY,OAAQ,IAAK,CACpC,CAaA,SAAS,GAAqB,EAAoB,EAAkC,CAClF,IAAM,EAAsB,CAAC,EAC7B,IAAK,IAAM,KAAO,EAAY,CAE5B,IAAM,EAAO,EAAc,CAAG,EACxB,EAAO,EAAO,EAAK,KAAO,EAAgB,EAA8B,UAAU,EACxF,GAAI,CAAC,GAAQ,EAAgB,IAAI,CAAI,EAAG,SACxC,IAAM,EAAW,EAAI,QAAQ,IAAI,CAAI,EACrC,EAAI,KAAK,CACP,WAAY,EAEZ,OAAQ,EAAW,EAAS,OAAS,EAAI,eAAe,IAAI,CAAI,EAAI,GAAK,IAC3E,CAAC,CACH,CACA,OAAO,CACT,CAGA,MAAM,EAAqB,IAAI,IAAI,CAAC,YAAa,eAAgB,aAAa,CAAC,EAoB/E,SAAS,GAAoB,EAAc,EAA2B,CACpE,GAAI,CAAC,EAAmB,IAAI,CAAI,EAAG,MAAO,GAC1C,IAAM,EAAS,EAAI,QAAQ,IAAI,CAAI,CAAC,EAAE,OAEtC,OAAO,IAAW,oBAAsB,GAAQ,WAAW,kBAAkB,GAAK,GACpF,CAkBA,SAAS,GACP,EACA,EAC6C,CAC7C,IAAM,EAAa,CAAE,KAAM,CAAC,EAAqB,SAAU,EAAM,EAO3D,EAAW,EAAmB,CAAI,EACxC,GAAI,CAAC,GAAY,EAAS,OAAS,kBAAmB,OAAO,EAE7D,IAAM,EAAuB,CAAC,EAC9B,IAAK,IAAM,KAAY,EAAS,UAAsC,CAAC,EAAG,CAIxE,GADI,CAAC,EAAO,CAAO,GAAK,EAAQ,OAAS,oBACrC,EAAe,EAAQ,QAAQ,IAAM,eAAgB,OAAO,EAEhE,IAAM,EAAS,EAAQ,OAEnB,EAAO,EAAe,CAAM,EAEhC,GAAI,CAAC,GAAQ,EAAO,CAAM,GAAK,EAAO,OAAS,iBAAkB,CAC/D,IAAM,EAAS,EAAO,OAEpB,EAAO,CAAM,GACb,EAAO,OAAS,oBAChB,EAAe,EAAO,QAAQ,IAAM,SAEpC,EAAO,EAAe,EAAO,MAAM,EAEvC,CACA,GAAI,CAAC,EAAM,OAAO,EAElB,IAAM,EAAW,EAAI,QAAQ,IAAI,CAAI,EACrC,EAAK,KAAK,CACR,WAAY,EACZ,OAAQ,EAAW,EAAS,OAAS,EAAI,eAAe,IAAI,CAAI,EAAI,GAAK,IAC3E,CAAC,CACH,CAEA,MAAO,CAAE,OAAM,SAAU,EAAK,CAChC,CAQA,SAAS,EAAmB,EAAyB,CACnD,IAAM,EAAS,EAAK,OAAS,EAC7B,GAAI,CAAC,EAAO,CAAK,EAAG,OAAO,KAK3B,GAAI,EAAM,OAAS,kBAAmB,OAAO,EAE7C,IAAM,EAAO,EAAM,KACnB,GAAI,CAAC,EAAO,CAAI,EAAG,OAAO,KAE1B,GAAI,EAAK,OAAS,iBAAkB,OAAO,EAE3C,IAAM,GAAe,EAAK,MAA+B,CAAC,EAAA,CAAG,OAC1D,GAAM,EAAE,OAAS,iBACpB,EACA,GAAI,EAAW,SAAW,EAAG,OAAO,KACpC,IAAM,EAAW,EAAW,EAAE,CAAC,SAC/B,OAAO,EAAO,CAAQ,EAAI,EAAW,IACvC,CAGA,SAAS,EAAe,EAAsB,EAAe,EAAoC,CAE/F,IAAM,EAAO,EADC,EAAQ,EAAS,CACC,CAAC,EACjC,OAAO,EAAO,GAAiB,EAAM,CAAG,EAAI,IAC9C,CAcA,SAASC,GACP,EACA,EAC0B,CAC1B,IAAK,IAAM,KAAO,EAAkB,CAClC,IAAM,EAAK,EAAc,CAAG,EAC5B,GAAI,CAAC,GAAM,EAAG,OAAS,iBAAkB,SACzC,IAAM,EAAO,EAAG,KAAK,YAAmC,GACpD,EAAmB,KACvB,GAAI,EAAO,CAAG,GAAK,EAAI,OAAS,mBAC9B,EAAM,MACD,CACL,IAAM,EAAU,EAAe,CAAG,EAClC,GAAI,EAAS,CACX,IAAM,EAAO,EAAmB,IAAI,CAAO,EACvC,GAAQ,EAAK,OAAS,qBAAoB,EAAM,EACtD,CACF,CACA,MAAO,CACL,WAAY,EAAuB,EAAK,YAAY,EACpD,SAAU,EAAuB,EAAK,UAAU,EAChD,WAAY,EAAuB,EAAK,YAAY,CACtD,CACF,CACA,OAAO,IACT,CAQA,SAAgB,GAAe,EAAgB,EAAkB,EAAiC,CAChG,IAAI,EACJ,GAAI,CACF,IAAM,EAAS,EAAU,EAAU,CAAM,EACzC,GAAI,EAAO,OAAO,OAAS,EAAG,OAAO,KACrC,EAAU,EAAO,OACnB,MAAQ,CACN,OAAO,IACT,CAEA,IAAM,EAAUF,GAAW,EAAU,CAAG,EAClC,EAAM,GAAiB,CAAO,EAE9B,EAA6B,CAAC,EAC9B,EAA4B,CAAC,EAC7B,EAA8B,CAAC,EAC/B,EAAkD,CAAC,EACnD,EAA0C,CAAC,EAC3C,EAAsC,CAAC,EACvC,EAA4B,CAAC,EAC7B,EAA8B,CAAC,EAC/B,EAAyB,CAAC,EAE1B,EAAkB,IAAI,IACtB,EAAkB,IAAI,IAOtB,EAAgB,IAAI,IAC1B,EAAK,EAAU,GAAS,CACtB,GAAI,EAAK,OAAS,qBAAsB,OACxC,IAAM,EAAO,EAAe,EAAK,EAAE,EAC7B,EAAO,EAAK,KACd,GAAQ,EAAO,CAAI,GAAG,EAAc,IAAI,EAAM,CAAI,CACxD,CAAC,EACD,IAAM,EAAiB,IAAI,IAErB,EAAqB,IAAI,IAE/B,IAAK,IAAM,KAAS,EAAQ,MAA+B,CAAC,EAAG,CAC7D,IAAM,EACJ,EAAK,OAAS,sBACV,EACA,EAAK,OAAS,0BAA4B,EAAO,EAAK,WAAW,EAC9D,EAAK,YACN,KACR,GAAI,EAAO,CAAI,GAAK,EAAK,OAAS,sBAChC,IAAK,IAAM,KAAM,EAAK,cAAuC,CAAC,EAAG,CAC/D,IAAM,EAAO,EAAe,EAAE,EAAE,EAC5B,GAAQ,EAAO,EAAE,IAAI,GAAG,EAAmB,IAAI,EAAM,EAAE,IAAY,CACzE,CAEJ,CAGA,IAAM,EAA4D,CAAC,EACnE,IAAK,IAAM,KAAS,EAAQ,MAA+B,CAAC,EAC1D,GAAI,EAAK,OAAS,0BAA4B,EAAO,EAAK,WAAW,EAAG,CACtE,IAAM,EAAI,EAAK,YACX,EAAE,OAAS,oBAAoB,EAAgB,KAAK,CAAE,IAAK,EAAG,UAAW,EAAM,CAAC,CACtF,MAAO,GAAI,EAAK,OAAS,4BAA8B,EAAO,EAAK,WAAW,EAAG,CAC/E,IAAM,EAAI,EAAK,YACX,EAAE,OAAS,oBAAoB,EAAgB,KAAK,CAAE,IAAK,EAAG,UAAW,EAAK,CAAC,CACrF,CAGF,IAAK,GAAM,CAAE,MAAK,eAAe,EAAiB,CAChD,IAAM,EAAY,EAAe,EAAI,EAAE,EACvC,GAAI,CAAC,EAAW,SAIhB,IAAI,EAA+B,KACnC,IAAK,IAAM,KAAO,EAAa,CAAG,EAAG,CACnC,IAAM,EAAK,EAAc,CAAG,EAC5B,GAAI,GAAM,EAAoB,CAAC,CAAC,IAAI,EAAG,IAAI,EAAG,CAC5C,EAAS,EAAG,KACZ,KACF,CACF,CACI,EACF,EAAQ,KAAK,CAAE,YAAW,UAAW,EAAQ,WAAU,aAAc,EAAS,WAAU,CAAC,EAChF,GAAgB,EAAK,WAAW,GACzC,EAAQ,KAAK,CACX,YACA,UAAW,SACX,WACA,aAAc,EACd,WACF,CAAC,CAEL,CAGA,IAAK,IAAM,KAAS,EAAQ,MAA+B,CAAC,EAAG,CAC7D,GAAI,EAAK,OAAS,0BAA4B,CAAC,EAAO,EAAK,WAAW,EAAG,SACzE,IAAM,EAAI,EAAK,YACX,KAAE,OAAS,sBACf,IAAK,IAAM,KAAe,EAAE,cAAuC,CAAC,EAAG,CACrE,IAAM,EAAO,EAAe,EAAW,EAAE,EACnC,EAAO,EAAW,KACpB,CAAC,GAAQ,CAAC,EAAO,CAAI,GAAK,EAAK,OAAS,kBACxC,EAAW,CAAI,IAAM,iBACrB,EAAQ,KAAM,GAAM,EAAE,YAAc,CAAI,GAC5C,EAAQ,KAAK,CACX,UAAW,EACX,UAAW,SACX,WACA,aAAc,EACd,UAAW,EACb,CAAC,EACH,CACF,CAGA,EAAK,EAAU,GAAS,CAEtB,GAAI,EAAK,OAAS,qBAAsB,CACtC,IAAM,EAAO,EAAK,KAClB,GAAI,EAAO,CAAI,GAAK,EAAK,OAAS,kBAAoB,EAAW,CAAI,IAAM,cAAe,CACxF,IAAM,EAAO,EAAa,EAAK,YAAmC,EAAE,EAChE,IAAS,OACX,EAAe,IAAI,CAAI,EACvB,EAAO,KAAK,CACV,OACA,SAAU,EAAe,EAAK,EAAE,EAChC,WACA,aAAc,CAChB,CAAC,EAEL,CACA,MACF,CAEA,GAAI,EAAK,OAAS,iBAAkB,CAIlC,GAAI,EAAK,OAAS,YAAa,CAC7B,IAAM,EAAK,EAAc,CAAI,EAC7B,GAAI,GAAI,OAAS,SAAU,CACzB,IAAM,EAAM,EAAa,EAAG,KAAK,YAAmC,EAAE,EAClE,IAAQ,MAAM,EAAQ,KAAK,CAAE,KAAM,EAAK,WAAU,aAAc,CAAQ,CAAC,CAC/E,CACF,CACA,MACF,CAEA,IAAM,EAAS,EAAK,OACd,EAAO,EAAW,CAAI,EAG5B,GAAI,IAAS,eAAiB,CAAC,EAAe,IAAI,CAAI,EAAG,CACvD,IAAM,EAAY,EAAa,EAAK,YAAmC,EAAE,EACrE,IAAc,MAChB,EAAO,KAAK,CAAE,KAAM,EAAW,SAAU,KAAM,WAAU,aAAc,CAAQ,CAAC,EAElF,MACF,CAGA,GAAI,IAAS,iBAAmB,IAAS,eAAgB,CACvD,IAAM,EAAU,EAAY,EAAQ,EAAe,CAAI,EAAG,MAAM,CAAC,EACjE,GAAI,IAAY,KAAM,CACpB,IAAM,EAAO,IAAS,eAAiB,SAAW,UAC5C,EAAY,GAAG,EAAK,IAAI,EAAQ,IAAI,IACrC,EAAgB,IAAI,CAAS,IAChC,EAAgB,IAAI,CAAS,EAC7B,EAAmB,KAAK,CAAE,OAAM,KAAM,EAAS,WAAU,aAAc,CAAQ,CAAC,EAEpF,CACA,MACF,CAGA,GAAI,IAAS,qBAAsB,CACjC,IAAM,EAAQ,EAAK,WAAoC,CAAC,EAClD,EAAU,EAAY,EAAK,EAAE,EACnC,GAAI,IAAY,KAAM,CACpB,IAAM,EAAO,EAAO,EAAK,EAAE,GAAK,EAAK,EAAE,CAAC,OAAS,mBAAqB,EAAK,GAAK,KAChF,EAAc,KAAK,CACjB,KAAM,EACN,YAAa,EAAY,EAAQ,EAAM,aAAa,CAAC,EACrD,QAAS,EAAY,EAAQ,EAAM,SAAS,CAAC,EAC7C,WACA,aAAc,CAChB,CAAC,CACH,CACA,MACF,CAIA,GAAI,IAAS,0BAA4B,IAAS,6BAA8B,CAC9E,IAAM,EAAM,EAAY,EAAQ,EAAe,CAAI,EAAG,KAAK,CAAC,EACxD,IAAQ,MAAQ,CAAC,EAAgB,IAAI,CAAG,IAC1C,EAAgB,IAAI,CAAG,EACvB,EAAY,KAAK,CACf,MACA,WAAY,EAAc,IAAI,CAAI,GAAK,KACvC,WACA,aAAc,CAChB,CAAC,GAEH,MACF,CAIA,GAAI,EAAO,CAAM,GAAK,EAAO,OAAS,iBAAkB,CACtD,IAAM,EAAc,EAAO,OAC3B,GACE,EAAO,CAAW,GAClB,EAAY,OAAS,oBACrB,EAAe,EAAY,QAAQ,IAAM,aACzC,CACA,IAAM,EAAO,EAAe,EAAY,MAAM,EAC9C,GAAI,IAAS,0BAA4B,IAAS,6BAA8B,CAC9E,IAAM,EAAM,EAAY,EAAQ,EAAe,CAAI,EAAG,KAAK,CAAC,EACxD,IAAQ,MAAQ,CAAC,EAAgB,IAAI,CAAG,IAC1C,EAAgB,IAAI,CAAG,EACvB,EAAY,KAAK,CACf,MACA,WAAY,EAAc,IAAI,CAAI,GAAK,KACvC,WACA,aAAc,CAChB,CAAC,EAEL,CACF,CACA,MACF,CAGA,GACE,EAAO,CAAM,GACb,EAAO,OAAS,oBAChB,EAAe,EAAO,QAAQ,IAAM,OACpC,CACA,IAAM,EAAM,EAAO,OACf,EAAO,CAAG,GAAK,EAAI,OAAS,gBAK9B,EAAK,EAAK,UAAY,GAAY,CAChC,IAAM,EAAI,EAAY,CAAO,EACzB,IAAM,MAAM,EAAa,KAAK,CAAC,CACrC,CAAC,CAEL,CACF,CAAC,EAGD,IAAM,GAAsD,CAAC,EAC7D,EAAK,EAAU,GAAS,CACtB,GAAI,EAAK,OAAS,oBAAsB,EAAK,OAAS,kBAAmB,CACvE,IAAM,EAAY,EAAe,EAAK,EAAE,EACpC,GAAW,GAAW,KAAK,CAAE,IAAK,EAAM,WAAU,CAAC,CACzD,CACF,CAAC,EAED,IAAK,GAAM,CAAE,MAAK,eAAe,GAAY,CAC3C,IAAM,EAAa,EAAQ,KAAM,GAAM,EAAE,YAAc,CAAS,EAC1D,EAAQ,EAAI,MAA2B,KAG7C,GAAI,GAAgB,EAAK,YAAY,EACnC,IAAK,IAAM,KAAU,GAAQ,CAAC,EAAG,CAE/B,GADI,EAAO,OAAS,sBAChB,EAAe,EAAO,GAAG,IAAM,OAAQ,SAC3C,IAAM,EAAU,EAAY,EAAO,KAAK,EACxC,GAAI,IAAY,KAAM,SACtB,IAAM,EAAY,UAAU,EAAQ,IAAI,IACnC,EAAgB,IAAI,CAAS,IAChC,EAAgB,IAAI,CAAS,EAC7B,EAAmB,KAAK,CACtB,KAAM,UACN,KAAM,EACN,WACA,aAAc,CAChB,CAAC,GAEH,KACF,CAGF,IAAK,IAAM,KAAU,GAAQ,CAAC,EAAG,CAC/B,GAAI,EAAO,OAAS,mBAAoB,SACxC,IAAM,EAAa,EAAe,EAAO,GAAG,EAC5C,GAAI,CAAC,EAAY,SAKjB,GAAI,IAAe,SAAU,CAC3B,GAAc,EAAO,MAAe,CAAY,EAChD,QACF,CAIA,GAAI,CAAC,EAAY,SACjB,IAAM,EAAO,EAAa,CAAM,EAC1B,EAAQE,GAAsB,EAAM,CAAkB,EAGtD,EAAoB,CACxB,GAAG,GAAqB,EAAa,CAAG,EAAG,CAAG,EAC9C,GAAG,GAAqB,EAAM,CAAG,CACnC,EACA,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAK,EAAc,CAAG,EAC5B,GAAI,CAAC,GAAM,CAAC,EAAgB,IAAI,EAAG,IAAI,EAAG,SAC1C,IAAM,EAAQ,EAAG,KAAK,WAAoC,CAAC,EACrD,EAAU,EAAY,EAAK,EAAE,EAC7B,EAAO,GAAW,EAAQ,OAAS,EAAI,EAAU,IACjD,EAAU,EAAO,EAAK,EAAE,GAAK,EAAK,EAAE,CAAC,OAAS,mBAAqB,EAAK,GAAK,KAEnF,EAAO,KAAK,CACV,WAAY,EACZ,OAAQ,EACR,WAAY,EAAG,KAAK,YAAY,EAChC,OAGA,WAAYD,GAAkB,CAAI,EAClC,gBAAiB,GAAO,YAAc,KACtC,cAAe,GAAO,UAAY,KAClC,gBAAiB,GAAO,YAAc,KACtC,WAAY,EAAe,EAAS,OAAQ,CAAG,EAC/C,YAAa,EAAe,EAAS,QAAS,CAAG,EACjD,aAAc,EAAe,EAAS,SAAU,CAAG,EACnD,eAAgB,EAAe,EAAS,WAAY,CAAG,EACvD,WACA,aAAc,EACd,0BAA2B,EAAW,UACtC,oBAIA,YAAa,CACf,CAAC,CACH,CACF,CACF,CAGA,EAAK,EAAU,GAAS,CACtB,GAAI,EAAK,OAAS,YAAc,EAAe,EAAK,GAAG,IAAM,SAAU,OACvE,IAAM,EAAQ,EAAK,MAEjB,EAAO,CAAK,IACX,EAAM,OAAS,sBAAwB,EAAM,OAAS,4BAEvD,GAAc,EAAO,CAAY,CAErC,CAAC,EAWD,IAAM,EAAyB,IAAI,IAC7B,EAAsB,IAAI,IAEhC,EAAK,EAAU,GAAS,CACtB,IAAM,EAAS,EAAK,OAAS,YAAc,EAAe,EAAK,GAAG,IAAM,eAClE,EAAW,EAAK,OAAS,oBAAsB,EAAe,EAAK,GAAG,IAAM,gBAC9E,GAAU,IAAU,EAAoB,IAAI,CAAI,CACtD,CAAC,EAGD,IAAM,GAAa,GACf,EAAU,YAAe,EAAU,MAA2B,MAChE,CAAC,EAEH,EAAK,EAAU,GAAS,CACtB,GAAI,EAAK,OAAS,oBAAsB,EAAK,OAAS,mBAAoB,OAC1E,IAAM,EAAU,GAAU,CAAI,EAChB,KAAQ,IAAK,GAAM,EAAe,EAAE,GAAG,CAC5C,CAAC,CAAC,SAAS,QAAQ,EAC5B,IAAK,IAAM,KAAU,EACf,EAAe,EAAO,GAAG,IAAM,gBAAgB,EAAuB,IAAI,CAAM,CAExF,CAAC,EAMD,IAAM,EAAsB,IAAI,IAChC,EAAK,EAAU,GAAS,CACtB,IAAM,EACJ,EAAK,OAAS,iBACV,EAAW,CAAI,EACf,EAAK,OAAS,gBACZ,EAAe,EAAK,MAAM,EAC1B,KAER,GADI,IAAW,MACX,CAAC,GAAoB,EAAQ,CAAG,EAAG,OACvC,IAAM,EAAU,EAAe,CAAI,EAC9B,KACL,IAAK,IAAM,KAAS,EAAQ,YAAqC,CAAC,EAC5D,EAAK,OAAS,YAAc,EAAe,EAAK,GAAG,IAAM,gBAC3D,EAAoB,IAAI,CAAI,CAGlC,CAAC,EAED,IAAM,EAAe,GAA+C,CAClE,GAAI,EAAM,OAAS,EAAG,OAAO,KAC7B,IAAM,EAAuB,CAAC,EAC1B,EAAW,GACf,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAY,GAAuB,EAAM,CAAG,EAC7C,EAAU,WAAU,EAAW,IACpC,EAAK,KAAK,GAAG,EAAU,IAAI,CAC7B,CACA,MAAO,CAAE,OAAM,WAAU,UAAS,CACpC,EAEM,EAAqB,EAAY,CAAsB,EACvD,EAAkB,EAAY,CAAmB,EAEvD,MAAO,CACL,UACA,SACA,UACA,qBACA,gBACA,cACA,SACA,eACA,aAAc,0BAA0B,KAAK,CAAQ,EAAI,EAAe,CAAC,EACzE,qBACA,kBAIA,4BACE,EAAoB,KAAO,EAAuB,KAAO,EAAoB,IACjF,CACF,CAOA,SAAS,GAAc,EAAU,EAA0B,CACzD,IAAM,EAAkB,CAAC,EACnB,EAAwB,CAAC,EAC/B,EAAK,EAAG,KAAO,GAAS,CACtB,GAAI,EAAK,OAAS,WAAY,OAC9B,IAAM,EAAM,EAAe,EAAK,GAAG,EACnC,GAAI,IAAQ,OAAQ,CAClB,IAAM,EAAI,EAAY,EAAK,KAAK,EAC5B,IAAM,MAAM,EAAM,KAAK,CAAC,CAC9B,MAAO,GAAI,IAAQ,aAAc,CAC/B,IAAM,EAAK,EAAe,EAAK,KAAK,EAChC,GAAM,SAAS,KAAK,CAAE,GAAG,EAAY,KAAK,CAAE,CAClD,CACF,CAAC,EACD,IAAM,EAAI,KAAK,IAAI,EAAM,OAAQ,EAAY,MAAM,EACnD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IACrB,EAAI,KAAK,CAAE,WAAY,EAAY,GAAI,UAAW,EAAM,EAAG,CAAC,CAEhE,CC52BA,MAAa,EAAkB,CAC7B,UACA,aACA,aACA,aACA,YACA,QACF,EAwTM,EAAqB,CAAC,MAAO,OAAQ,OAAQ,MAAM,EACnD,EAAmB,CAAC,eAAgB,UAAW,OAAQ,QAAS,SAAU,SAAU,OAAO,EAM3F,EAAwB,IAAI,OAChC,OAAO,GAAG,KAAK,EAAgB,KAAK,GAAG,EAAE,eACvC,OAAO,GAAG,qCACV,OAAO,GAAG,yDACZ,GACF,EAWM,GAAyB,IAAI,OACjC,OAAO,GAAG,sDACR,OAAO,GAAG,oCACV,OAAO,GAAG,qCACZ,GACF,EASM,GACJ,+EAOI,GACJ,8GAMI,GAA0B,8DAG1B,GAAuB,2CAQvB,EAAsB,sDAatB,EACJ,8NAQI,GAA0B,IAAI,OAClC,OAAO,GAAG,wDACR,OAAO,GAAG,oCACV,OAAO,GAAG,sCACZ,GACF,EAGM,GAAyB,iDAOzB,GAA4B,+DAc5B,EAAwB,IAAI,OAAO,OAAO,GAAG,KAAK,CAX/B,MAAO,OAAQ,MAAO,SAAU,OAWa,CAAC,CAAC,KAAK,GAAG,EAAE,QAAS,GAAG,EAS9F,SAAS,EAAkB,EAAc,EAAyB,CAChE,IAAI,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAU,EAAG,EAAI,EAAK,OAAQ,IAAK,CAC9C,IAAM,EAAK,EAAK,GAChB,GAAI,IAAO,IAAK,SACX,GAAI,IAAO,MACd,IACI,IAAU,GAAG,OAAO,CAE5B,CACA,MAAO,EACT,CAYA,SAAS,GACP,EACA,EAC+C,CAC/C,IAAI,EAAM,EAEV,KAAO,EAAM,EAAM,QAAQ,CACzB,KAAO,EAAM,EAAM,QAAU,KAAK,KAAK,EAAM,EAAI,GAAG,IACpD,GAAI,EAAM,KAAS,IAAK,MACxB,IAAM,EAAW,EAAM,MAAM,CAAG,CAAC,CAAC,MAAM,cAAc,EACtD,GAAI,CAAC,EAAU,MAEf,IADA,GAAO,EAAS,EAAE,CAAC,OACZ,EAAM,EAAM,QAAU,KAAK,KAAK,EAAM,EAAI,GAAG,IACpD,GAAI,EAAM,KAAS,IAAK,CACtB,IAAM,EAAQ,EAAkB,EAAO,CAAG,EAC1C,GAAI,EAAQ,EAAG,OAAO,KACtB,EAAM,EAAQ,CAChB,CACF,CAEA,KAAO,EAAM,EAAM,QAAU,KAAK,KAAK,EAAM,EAAI,GAAG,IACpD,IAAK,IAAM,IAAO,CAAC,SAAU,UAAW,WAAW,EACjD,GAAI,EAAM,MAAM,EAAK,EAAM,EAAI,MAAM,IAAM,GAAO,KAAK,KAAK,EAAM,OAAO,EAAM,EAAI,MAAM,CAAC,EAAG,CAE3F,IADA,GAAO,EAAI,OACJ,EAAM,EAAM,QAAU,KAAK,KAAK,EAAM,EAAI,GAAG,IACpD,KACF,CAEF,GAAI,EAAM,MAAM,EAAK,EAAM,CAAC,IAAM,SAAW,KAAK,KAAK,EAAM,OAAO,EAAM,CAAC,CAAC,EAE1E,IADA,GAAO,EACA,EAAM,EAAM,QAAU,KAAK,KAAK,EAAM,EAAI,GAAG,IAGtD,IAAM,EAAc,EAAM,MAAM,CAAG,CAAC,CAAC,MAAM,sBAAsB,EAEjE,OADK,EACE,CAAE,WAAY,EAAY,GAAI,OAAQ,EAAM,EAAY,EAAE,CAAC,MAAO,EADhD,IAE3B,CAGA,SAAS,GAAkB,EAAwB,CAEjD,OADgB,EAAK,MAAM,kBAAkB,GAAK,CAAC,EAAA,CACpC,IAAK,GAAM,EAAE,MAAM,CAAC,CAAC,CACtC,CAQA,SAAS,GAAc,EAAmB,EAA2B,CACnE,IAAM,EAAS,EAAU,SAAS,GAAG,EAAI,EAAU,MAAM,EAAG,EAAE,EAAI,EAGlE,MAFI,CAAC,GAAa,IAAc,IAAY,GAAU,IAE/C,GADQ,EAAU,WAAW,GAAG,EAAI,EAAY,IAAM,IACnC,GAC5B,CAOA,MAAM,GACJ,sGAMI,GAAmB,oCAGnB,GAAyB,mCAQzB,EAAyB,6BAS/B,SAAgB,GAAoB,EAA0B,CAC5D,IAAM,EAAqB,CAAC,EAE5B,IADA,EAAuB,UAAY,EAC5B,EAAuB,KAAK,CAAM,IAAM,MAAM,CACnD,IAAM,EAAY,EAAuB,UAAY,EAC/C,EAAa,EAAkB,EAAQ,CAAS,EACtD,GAAI,EAAa,EAAG,SACpB,IAAM,EAAO,EAAO,MAAM,EAAY,EAAG,CAAU,EAI7C,EAAY,uBACd,EACJ,MAAQ,EAAM,EAAU,KAAK,CAAI,KAAO,MACtC,EAAS,KAAK,EAAI,EAAY,CAElC,CACA,OAAO,CACT,CAUA,SAAS,GAAY,EAAyB,CAK5C,IAAM,EAAU,EACb,QAAQ,kBAAmB,MAAM,CAAC,CAClC,QAAQ,MAAO,GAAG,CAAC,CACnB,QAAQ,UAAW,wBAAwB,CAAC,CAC5C,QAAQ,QAAS,kBAAkB,CAAC,CACpC,QAAQ,MAAO,OAAO,CAAC,CACvB,QAAQ,0BAA2B,UAAU,CAAC,CAC9C,QAAQ,oBAAqB,IAAI,EACpC,OAAW,OAAO,IAAM,EAAU,GAAG,CACvC,CAQA,SAAgB,GACd,EACA,EACS,CACT,IAAM,EAAa,EAAmB,WAAW,IAAI,EACjD,EACA,KAAO,EACP,EAAU,GACd,IAAK,IAAM,KAAW,EAAU,CAC9B,IAAM,EAAa,EAAQ,WAAW,GAAG,EAErC,GADS,EAAa,EAAQ,MAAM,CAAC,EAAI,CACzB,CAAC,CAAC,KAAK,CAAU,IACnC,EAAU,CAAC,EAEf,CACA,OAAO,CACT,CA0BA,SAAgB,GAAoB,EAA+B,CACjE,IAAM,EAAqB,CAAC,EAC5B,GAAoB,UAAY,EAChC,IAAI,EACJ,MAAQ,EAAI,GAAoB,KAAK,CAAM,KAAO,MAAM,CACtD,IAAM,EAAY,EAAO,QAAQ,IAAK,EAAE,MAAQ,EAAE,EAAE,CAAC,OAAS,CAAC,EAC/D,GAAI,EAAY,EAAG,SACnB,IAAM,EAAa,EAAkB,EAAQ,CAAS,EACtD,GAAI,EAAa,EAAG,SACpB,IAAM,EAAO,EAAO,MAAM,EAAY,EAAG,CAAU,EAE7C,EAAkB,CAAC,EACzB,GAAiB,UAAY,EAC7B,IAAI,EACJ,MAAQ,EAAI,GAAiB,KAAK,CAAI,KAAO,MAC3C,EAAM,KAAK,EAAE,IAAM,EAAE,EAGvB,IAAM,EAAwB,CAAC,EAC/B,GAAuB,UAAY,EACnC,IAAI,EACJ,MAAQ,EAAI,GAAuB,KAAK,CAAI,KAAO,MACjD,EAAY,KAAK,EAAE,EAAY,EAKjC,IAAM,EAAI,KAAK,IAAI,EAAM,OAAQ,EAAY,MAAM,EACnD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,IACrB,EAAI,KAAK,CAAE,WAAY,EAAY,GAAc,UAAW,EAAM,EAAa,CAAC,CAEpF,CACA,OAAO,CACT,CAUA,SAAS,EAA6B,EAAc,EAA8B,CAGhF,IAAM,EAAI,IADK,OAAO,OAAO,GAAG,KAAK,EAAM,2BAA4B,GAC5D,CAAC,CAAC,KAAK,CAAI,EAEtB,OADK,EACE,EAAE,GADM,IAEjB,CAaA,SAAS,EAAoB,EAAgB,EAAmC,CAK9E,IAAM,EAAQ,IAHM,OAClB,OAAO,GAAG,iCAAiC,EAAW,0CAEpC,CAAC,CAAC,KAAK,CAAM,EACjC,GAAI,EAAO,OAAO,EAAM,GAMxB,IAAM,EAAM,IAHU,OACpB,OAAO,GAAG,wBAAwB,EAAW,iCAE3B,CAAC,CAAC,KAAK,CAAM,EACjC,GAAI,EAAK,OAAO,EAAI,GAMpB,IAAM,EAAK,IAHM,OACf,OAAO,GAAG,sBAAsB,EAAW,iCAE/B,CAAC,CAAC,KAAK,CAAM,EAQ3B,OAPI,EAAW,EAAG,GAKd,IADgB,OAAO,OAAO,GAAG,oCAAoC,EAAW,GAC1E,CAAC,CAAC,KAAK,CAAM,EAAU,GAE1B,IACT,CAeA,SAAS,GACP,EACA,EAC2E,CAC3E,IAAM,EAAW,6CAA6C,KAAK,CAAc,EACjF,GAAI,CAAC,EAAU,CAEb,IAAM,EAAQ,mCAAmC,KAAK,CAAc,EAEpE,OADK,EACE,GAAuB,EAAM,EAAE,CAAC,KAAK,EAAG,CAAU,EADtC,IAErB,CACA,OAAO,GAAuB,EAAS,EAAE,CAAC,KAAK,EAAG,CAAU,CAC9D,CAEA,SAAS,GACP,EACA,EACoE,CAEpE,GAAI,EAAI,WAAW,GAAG,EACpB,OAAO,GAAyB,CAAG,EAGrC,IAAM,EAAU,kBAAkB,KAAK,CAAG,EAC1C,GAAI,EAAS,CACX,IAAM,EAAQ,EAAQ,GAMhB,EAAa,IAJC,OAClB,OAAO,GAAG,WAAW,EAAM,uCAC3B,GAEuB,CAAC,CAAC,KAAK,CAAU,EAC1C,GAAI,EACF,OAAO,GAAyB,EAAW,EAAE,CAEjD,CAEA,MAAO,CAAE,WAAY,CAAC,EAAG,SAAU,CAAC,EAAG,WAAY,CAAC,CAAE,CACxD,CAGA,SAAS,EAAmB,EAAiB,EAAuB,CAElE,IAAM,EAAI,IADK,OAAO,OAAO,GAAG,GAAG,EAAI,sBAC5B,CAAC,CAAC,KAAK,CAAO,EAEzB,OADK,EACE,MAAM,KAAK,EAAE,EAAE,CAAC,SAAS,sBAAsB,CAAC,CAAC,CAAC,IAAK,GAAM,EAAE,EAAE,EADzD,CAAC,CAElB,CAGA,SAAS,GAAyB,EAIhC,CACA,MAAO,CACL,WAAY,EAAmB,EAAS,YAAY,EACpD,SAAU,EAAmB,EAAS,UAAU,EAChD,WAAY,EAAmB,EAAS,YAAY,CACtD,CACF,CAGA,eAAe,GAAK,EAAa,EAAsC,CACrE,IAAM,EAAO,EAAK,YAAc,EAC1B,EAAW,EAAK,SAAW,EAC3B,EAAgB,CAAC,EAEnB,EACJ,GAAI,CACF,EAAW,MAAM,EAAQ,EAAK,CAAE,cAAe,GAAM,SAAU,OAAQ,CAAC,CAC1E,MAAQ,CACN,OAAO,CACT,CAEA,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAM,EAAO,EAAK,EAAK,EAAM,IAAI,EAC3B,EAAM,EAAS,EAAK,IAAK,CAAI,EAE/B,EAAS,KAAM,GAAO,EAAI,SAAS,CAAE,CAAC,IAEtC,EAAM,YAAY,EACpB,EAAI,KAAK,GAAI,MAAM,GAAK,EAAM,CAAI,CAAE,EAC3B,EAAM,OAAO,GAClB,EAAK,KAAM,GAAQ,EAAM,KAAK,SAAS,CAAG,CAAC,GAC7C,EAAI,KAAK,CAAI,EAGnB,CAEA,OAAO,CACT,CAGA,SAAS,EAAW,EAAkB,EAAqB,CACzD,OAAO,EAAS,EAAK,CAAQ,CAAC,CAAC,MAAM,CAAG,CAAC,CAAC,KAAK,GAAG,CACpD,CAGA,SAAgB,GACd,EACA,EACA,EACmB,CACnB,IAAM,EAAyB,CAAC,EAC1B,EAAU,EAAW,EAAU,CAAG,EAExC,EAAsB,UAAY,EAClC,IAAI,EACJ,MAAQ,EAAQ,EAAsB,KAAK,CAAM,KAAO,MAAM,CAC5D,GAAM,EAAG,EAAW,EAAe,GAAa,EAChD,EAAI,KAAK,CACP,YACW,YACX,WACA,aAAc,EACd,UAAW,EAAQ,CACrB,CAAC,CACH,CAKA,GAAuB,UAAY,EACnC,IAAI,EACJ,MAAQ,EAAW,GAAuB,KAAK,CAAM,KAAO,MAAM,CAChE,GAAM,EAAG,EAAe,GAAa,EACjC,EAAI,KAAM,GAAM,EAAE,YAAc,GAAa,EAAE,WAAa,CAAQ,GACxE,EAAI,KAAK,CACP,YACA,UAAW,SACX,WACA,aAAc,EACd,UAAW,EAAQ,CACrB,CAAC,CACH,CAMA,GAAoB,UAAY,EAChC,IAAI,EACJ,MAAQ,EAAW,GAAoB,KAAK,CAAM,KAAO,MAAM,CAC7D,GAAM,EAAG,GAAa,EAClB,EAAI,KAAM,GAAM,EAAE,YAAc,GAAa,EAAE,WAAa,CAAQ,GACxE,EAAI,KAAK,CACP,YACA,UAAW,SACX,WACA,aAAc,EACd,UAAW,EACb,CAAC,CACH,CAEA,OAAO,CACT,CAGA,SAAgB,GACd,EACA,EACA,EACmB,CACnB,IAAM,EAAyB,CAAC,EAC1B,EAAU,EAAW,EAAU,CAAG,EAClC,EAAO,IAAI,IAGjB,GAAmB,UAAY,EAC/B,IAAI,EACJ,MAAQ,EAAQ,GAAmB,KAAK,CAAM,KAAO,MAAM,CACzD,GAAM,CAAC,EAAM,EAAU,GAAQ,EAC/B,EAAK,IAAI,CAAI,EACb,EAAI,KAAK,CAAE,OAAM,WAAU,WAAU,aAAc,CAAQ,CAAC,CAC9D,CAIA,IADA,GAAwB,UAAY,GAC5B,EAAQ,GAAwB,KAAK,CAAM,KAAO,MACpD,EAAK,IAAI,EAAM,EAAE,GACrB,EAAI,KAAK,CACP,KAAM,EAAM,GACZ,SAAU,KACV,WACA,aAAc,CAChB,CAAC,EAGH,OAAO,CACT,CAaA,SAAgB,GACd,EACA,EACA,EACA,EACA,EAAqD,IAAI,IACtC,CACnB,IAAM,EAAyB,CAAC,EAChC,GAAI,EAAc,SAAW,EAAG,OAAO,EACvC,IAAM,EAAU,EAAW,EAAU,CAAG,EAGlC,EAA4D,CAAC,EACnE,IAAK,IAAM,KAAO,EAAe,CAE/B,IAAM,EAAI,IADK,OAAO,OAAO,GAAG,WAAW,EAAI,UAAU,GAC9C,CAAC,CAAC,KAAK,CAAM,EACpB,GAAG,QAAU,IAAA,IACf,EAAU,KAAK,CAAE,MAAK,MAAO,EAAE,KAAM,CAAC,CAE1C,CACA,EAAU,MAAM,EAAG,IAAM,EAAE,MAAQ,EAAE,KAAK,EAE1C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAU,OAAQ,IAAK,CACzC,GAAM,CAAE,MAAK,SAAU,EAAU,GAC3B,EAAM,EAAI,EAAI,EAAU,OAAS,EAAU,EAAI,EAAE,CAAC,MAAQ,EAAO,OACjE,EAAQ,EAAO,MAAM,EAAO,CAAG,EAMrC,EAAsB,UAAY,EAClC,IAAI,EACJ,MAAQ,EAAa,EAAsB,KAAK,CAAK,KAAO,MAAM,CAChE,IAAM,EAAO,EAAW,GAClB,EAAiB,EAAW,MAC5B,EAAY,EAAsB,UAAY,EAC9C,EAAa,EAAkB,EAAO,CAAS,EACrD,GAAI,EAAa,EAAG,SAEpB,IAAM,EAAY,EAAM,MAAM,EAAY,EAAG,CAAU,EAEjD,EAAmB,EAAU,MAAM,yBAAyB,EAC5D,EAAO,GAAoB,EAAiB,EAAE,CAAC,OAAS,EAAI,EAAiB,GAAK,IAElF,EAAa,GAA0B,EAAO,EAAa,CAAC,EAClE,GAAI,CAAC,EAAY,SACjB,GAAM,CAAE,aAAY,UAAW,EAI/B,EAAsB,UAAY,EAGlC,IAAM,EAAQ,GADM,EAAM,MAAM,EAAgB,CACF,EAAG,CAAM,EAEjD,EAAS,EAA6B,EAAW,MAAM,EACvD,EAAU,EAA6B,EAAW,OAAO,EACzD,EAAW,EAA6B,EAAW,QAAQ,EAC3D,EAAa,EAA6B,EAAW,UAAU,EAM/D,EAAY,EAAsB,IAAI,EAAI,SAAS,GAAK,GACxD,EAAW,EAAY,GAAc,EAAW,CAAI,EAAI,EAE9D,EAAI,KAAK,CACP,WAAY,EAAI,UAChB,OAAQ,EACR,WAAY,EAAK,YAAY,EAC7B,OACA,WAAY,GAAkB,CAAQ,EACtC,gBAAiB,GAAO,YAAc,KACtC,cAAe,GAAO,UAAY,KAClC,gBAAiB,GAAO,YAAc,KACtC,WAAY,EACR,CAAE,WAAY,EAAQ,OAAQ,EAAoB,EAAQ,CAAM,CAAE,EAClE,KACJ,YAAa,EACT,CAAE,WAAY,EAAS,OAAQ,EAAoB,EAAQ,CAAO,CAAE,EACpE,KACJ,aAAc,EACV,CAAE,WAAY,EAAU,OAAQ,EAAoB,EAAQ,CAAQ,CAAE,EACtE,KACJ,eAAgB,EACZ,CAAE,WAAY,EAAY,OAAQ,EAAoB,EAAQ,CAAU,CAAE,EAC1E,KACJ,WACA,aAAc,EACd,0BAA2B,EAAI,UAC/B,YAAa,CACf,CAAC,CACH,CACF,CAEA,OAAO,CACT,CAGA,SAAgB,GACd,EACA,EACA,EACoB,CACpB,IAAM,EAA0B,CAAC,EAC3B,EAAU,EAAW,EAAU,CAAG,EAExC,GAAqB,UAAY,EACjC,IAAI,EACJ,MAAQ,EAAQ,GAAqB,KAAK,CAAM,KAAO,MACrD,EAAI,KAAK,CAAE,KAAM,EAAM,GAAI,WAAU,aAAc,CAAQ,CAAC,EAG9D,OAAO,CACT,CASA,SAAS,EAAkB,EAAc,EAA8B,CACrE,IAAI,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAe,EAAG,EAAI,EAAK,OAAQ,IAAK,CACnD,IAAM,EAAK,EAAK,GAChB,GAAI,IAAO,IAAK,SACX,GAAI,IAAO,MACd,IACI,IAAU,GAAG,OAAO,CAE5B,CACA,MAAO,EACT,CAWA,SAAgB,GACd,EACA,EACA,EAC6B,CAC7B,IAAM,EAAmC,CAAC,EACpC,EAAU,EAAW,EAAU,CAAG,EAClC,EAAO,IAAI,IAGjB,EAAoB,UAAY,EAChC,IAAI,EACJ,MAAQ,EAAc,EAAoB,KAAK,CAAM,KAAO,MAAM,CAChE,IAAM,EAAS,EAAY,GACrB,EAAY,EAAoB,UAAY,EAC5C,EAAa,EAAkB,EAAQ,CAAS,EACtD,GAAI,EAAa,EAAG,SACpB,IAAM,EAAW,EAAO,MAAM,EAAY,EAAG,CAAU,EAEjD,EAAY,mCAAmC,KAAK,CAAQ,EAClE,GAAI,CAAC,EAAW,SAChB,IAAM,EAAO,EAAU,GACjB,EAAY,GAAG,EAAO,IAAI,EAAK,IAAI,IACrC,EAAK,IAAI,CAAS,IACtB,EAAK,IAAI,CAAS,EAClB,EAAI,KAAK,CACP,KAAM,IAAW,eAAiB,SAAW,UAC7C,OACA,WACA,aAAc,CAChB,CAAC,EACH,CAGA,GAAwB,UAAY,EACpC,IAAI,EACJ,MAAQ,EAAa,GAAwB,KAAK,CAAM,KAAO,MAAM,CACnE,IAAM,EAAa,EAAW,MAExB,EAAW,EAAO,QAAQ,IAAK,CAAU,EAC/C,GAAI,EAAW,EAAG,SAClB,IAAM,EAAa,EAAkB,EAAQ,CAAQ,EACrD,GAAI,EAAa,EAAG,SACpB,IAAM,EAAO,EAAO,MAAM,EAAW,EAAG,CAAU,EAC5C,EAAY,GAAuB,KAAK,CAAI,EAClD,GAAI,CAAC,EAAW,SAChB,IAAM,EAAO,EAAU,GACjB,EAAY,UAAU,EAAK,IAAI,IACjC,EAAK,IAAI,CAAS,IACtB,EAAK,IAAI,CAAS,EAClB,EAAI,KAAK,CAAE,KAAM,UAAW,OAAM,WAAU,aAAc,CAAQ,CAAC,EACrE,CAEA,OAAO,CACT,CAaA,SAAgB,GACd,EACA,EACA,EACwB,CACxB,IAAM,EAA8B,CAAC,EAC/B,EAAU,EAAW,EAAU,CAAG,EAClC,EAAO,IAAI,IAEjB,EAAwB,UAAY,EAGpC,IAAI,EACJ,MAAQ,EAAQ,EAAwB,KAAK,CAAM,KAAO,MAAM,CAC9D,IAAM,EAAY,EAAwB,UAAY,EAChD,EAAa,EAAkB,EAAQ,CAAS,EACtD,GAAI,EAAa,EAAG,SACpB,IAAM,EAAW,EAAO,MAAM,EAAY,EAAG,CAAU,EACjD,EAAW,kCAAkC,KAAK,CAAQ,EAChE,GAAI,CAAC,EAAU,SACf,IAAM,EAAM,EAAS,GACjB,EAAK,IAAI,CAAG,IAChB,EAAK,IAAI,CAAG,EACZ,EAAI,KAAK,CAAE,MAAK,WAAY,EAAM,IAAM,KAAM,WAAU,aAAc,CAAQ,CAAC,EACjF,CAEA,OAAO,CACT,CAOA,SAAgB,GACd,EACA,EACA,EAC0B,CAC1B,IAAM,EAAgC,CAAC,EACjC,EAAU,EAAW,EAAU,CAAG,EAExC,GAA0B,UAAY,EACtC,IAAI,EACJ,MAAQ,EAAQ,GAA0B,KAAK,CAAM,KAAO,MAAM,CAChE,IAAM,EAAO,EAAM,GACf,EAA6B,KAC7B,EAAyB,KAG7B,GAAI,EAAM,GAAI,CACZ,IAAM,EAAW,EAAO,QAAQ,IAAK,EAAM,MAAQ,EAAM,EAAE,CAAC,OAAS,CAAC,EACtE,GAAI,GAAY,EAAG,CACjB,IAAM,EAAa,EAAkB,EAAQ,CAAQ,EACrD,GAAI,GAAc,EAAG,CACnB,IAAM,EAAO,EAAO,MAAM,EAAW,EAAG,CAAU,EAClD,EAAc,GAAgB,EAAM,aAAa,EACjD,EAAU,GAAgB,EAAM,SAAS,CAC3C,CACF,CACF,CAEA,EAAI,KAAK,CAAE,OAAM,cAAa,UAAS,WAAU,aAAc,CAAQ,CAAC,CAC1E,CAEA,OAAO,CACT,CAeA,SAAS,GAAgB,EAAc,EAA8B,CAGnE,IAAM,EADc,OAAO,MAAM,EAAM,mBAAoB,GAC3C,CAAC,CAAC,KAAK,CAAI,EAC3B,GAAI,CAAC,EAAG,OAAO,KACf,IAAM,EAAQ,EAAE,GACV,EAAQ,EAAE,MAAQ,EAAE,EAAE,CAAC,OACzB,EAAI,EACJ,EAAqB,KACzB,KAAO,EAAI,EAAK,QAAQ,CACtB,IAAM,EAAK,EAAK,GAChB,GAAI,IAAO,KAAM,CAEf,GAAK,EACL,QACF,CACA,GAAI,IAAO,EAAO,CAChB,EAAM,EAAK,MAAM,EAAO,CAAC,EACzB,KACF,CACA,GACF,CAMA,OALI,IAAQ,KAAa,KAKlB,EAAI,QAAQ,UAAW,EAAI,IAC5B,IAAM,IAAY;EAClB,IAAM,IAAY,IAClB,IAAM,IAAY,KACf,CACR,CACH,CAQA,MAAM,GAA8B,CAClC,sBACA,oBACA,gBACA,YACF,EAgBA,eAAsB,GAAc,EAAa,EAAgD,CAK/F,IAAM,EACJ,IAAY,aAAe,GAA8B,CAAC,CAAO,EAEnE,IAAK,IAAM,KAAa,EAAY,CAClC,IAAM,EAAM,EAAQ,EAAK,CAAS,EAC9B,EACJ,GAAI,CACF,EAAS,MAAM,EAAS,EAAK,OAAO,CACtC,MAAQ,CACN,QACF,CAkBI,MAAC,mBAAmB,KAAK,CAAM,GAAK,CAAC,+BAA+B,KAAK,CAAM,IAG9E,qBAAqB,KAAK,CAAM,GAOjC,6CAA4C,KAAK,CAAM,EAC3D,MAAO,CACL,SAAU,EACV,aAAc,EAAW,EAAK,CAAG,CACnC,CACF,CAEA,OAAO,IACT,CAGA,SAAgB,GAAe,EAA8C,CAC3E,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAO,EAAS,CACzB,IAAM,EAAM,EAAO,IAAI,EAAI,SAAS,GAAK,CAAC,EAC1C,EAAI,KAAK,CAAG,EACZ,EAAO,IAAI,EAAI,UAAW,CAAG,CAC/B,CAEA,IAAM,EAA+B,CAAC,EACtC,IAAK,GAAM,CAAC,EAAW,KAAU,EAI3B,IADsB,IAAI,EAAM,IAAK,GAAM,EAAE,QAAQ,CACzC,CAAC,CAAC,KAAO,GACvB,EAAW,KAAK,CAAE,YAAW,QAAS,CAAM,CAAC,EAMjD,OADA,EAAW,MAAM,EAAG,IAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC,EACzD,CACT,CAUA,MAAM,GAA8B,IAAI,IAAI,CAC1C,aACA,UACA,aACA,aACA,YACA,SACA,aACA,iBACA,SACA,QACA,OACA,YACA,aACA,aACA,QACA,UACA,gBACA,aACA,QACA,SACA,WACF,CAAC,EAqBD,SAAS,EAAoB,EAAmB,CAC9C,OAAO,EACJ,MAAM,CAAG,CAAC,CACV,KAAK,GAAG,CAAC,CACT,QAAQ,kBAAmB,EAAE,CAClC,CAmBA,SAAS,GACP,EACA,EACA,EACS,CACT,IAAM,EAAO,EAAoB,CAAQ,EAEzC,GAAI,IAAc,GAAI,OAAO,IAAS,EAAoB,CAAY,EAEtE,GAAI,EAAU,WAAW,GAAG,EAAG,CAC7B,IAAM,EAAW,EAAoB,EAAQ,EAAQ,CAAY,EAAG,CAAS,CAAC,EAC9E,OAAO,IAAS,GAAY,IAAS,GAAG,EAAS,OACnD,CAIA,IAAM,EAAO,EAAU,QAAQ,WAAY,EAAE,EAG7C,GAAI,IAAS,GAAa,EAAU,WAAW,GAAG,EAAG,MAAO,GAC5D,IAAM,EAAiB,EAAoB,CAAI,EAC/C,OAAO,IAAS,GAAkB,EAAK,SAAS,IAAI,GAAgB,CACtE,CAgBA,SAAgB,GACd,EACgC,CAChC,IAAM,EAAsC,IAAI,IAC1C,EAAY,IAAI,IAEtB,IAAK,IAAM,KAAW,EACf,KACL,IAAK,GAAM,CAAE,gBAAgB,EAAQ,aACnC,EAAU,IAAI,GAAa,EAAU,IAAI,CAAU,GAAK,GAAK,CAAC,EAIlE,IAAK,IAAM,KAAW,EACf,MAAS,mBACd,IAAK,GAAM,CAAE,gBAAgB,EAAQ,aAAc,CAEjD,IAAK,EAAU,IAAI,CAAU,GAAK,GAAK,EAAG,CACxC,EAAI,IAAI,EAAY,WAAW,EAC/B,QACF,CACA,IAAM,EAAW,EAAI,IAAI,CAAU,EACnC,EAAI,IAAI,EAAY,IAAa,IAAA,GAAY,EAAQ,mBAAqB,WAAW,CACvF,CAGF,OAAO,CACT,CAEA,SAAgB,GACd,EACA,EACA,EACA,EAAqD,IAAI,IACzD,EAAuC,CAAC,EAClC,CACN,GAAI,EAA6B,CAM/B,IAAK,IAAM,KAAS,EAAQ,EAAM,YAAc,KAChD,MACF,CAIA,IAAM,EAAe,IAAI,IACzB,IAAK,IAAM,KAAM,EAAa,CAC5B,GAAI,CAAC,EAAG,WAAY,SACpB,IAAM,EAAO,EAAa,IAAI,EAAG,UAAU,EACvC,EAAM,EAAK,KAAK,CAAE,EACjB,EAAa,IAAI,EAAG,WAAY,CAAC,CAAE,CAAC,CAC3C,CAEA,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAU,EAAM,kBACtB,GAAI,CAAC,EAAS,CAMZ,EAAM,YAAc,KACpB,QACF,CACA,IAAM,EAAO,IAAI,IACb,EAAW,GAST,GAAc,EAAsB,IAAqC,CAC7E,GAAI,EAAI,SAAW,KAAM,OAAO,KAChC,IAAM,GAAW,EAAa,IAAI,EAAI,UAAU,GAAK,CAAC,EAAA,CAAG,OAAQ,GAC/D,GAAyB,EAAc,EAAI,OAAkB,EAAG,QAAQ,CAC1E,EACA,OAAO,EAAQ,SAAW,EAAI,EAAQ,EAAE,CAAC,IAAM,IACjD,EAEA,IAAK,IAAM,KAAO,EAAS,CACzB,GAAI,GAA4B,IAAI,EAAI,UAAU,EAAG,SACrD,IAAM,EAAM,EAAW,EAAM,SAAU,CAAG,EAC1C,GAAI,IAAQ,KAAM,CAChB,EAAW,GACX,KACF,CACA,EAAK,IAAI,CAAG,CACd,CAMA,GAAI,EACF,IAAK,IAAM,KAAO,EAAiB,CACjC,GAAI,CAAC,EAAI,SAAU,CACjB,EAAW,GACX,KACF,CACA,IAAK,IAAM,KAAO,EAAI,KAAM,CAC1B,IAAM,EAAM,EAAW,EAAI,SAAU,CAAG,EACxC,GAAI,IAAQ,KAAM,CAChB,EAAW,GACX,KACF,CACA,EAAK,IAAI,CAAG,CACd,CACA,GAAI,CAAC,EAAU,KACjB,CAQF,GAAI,EAAU,CACZ,IAAM,EAAa,EAAmB,IAAI,EAAM,UAAU,EAC1D,GAAI,IAAe,YACjB,EAAW,QACN,GAAI,EACT,GAAI,CAAC,EAAW,SACd,EAAW,QAEX,IAAK,IAAM,KAAO,EAAW,KAAM,CACjC,IAAM,EAAM,EAAW,EAAW,SAAU,CAAG,EAC/C,GAAI,IAAQ,KAAM,CAChB,EAAW,GACX,KACF,CACA,EAAK,IAAI,CAAG,CACd,CAGN,CAIA,EAAM,YAAc,EAAW,CAAC,GAAG,CAAI,CAAC,CAAC,SAAS,EAAI,IACxD,CACF,CA8DA,MAAM,GAA2B,4BAajC,SAAgB,GAAY,EAAgB,EAAkB,EAA0B,CAUtF,OATY,GAAe,EAAQ,EAAU,CAKzC,GAIG,CACL,GAAG,GAAiB,EAAQ,EAAU,CAAG,EACzC,4BAA6B,GAAyB,KAAK,CAAM,CACnE,CACF,CAMA,SAAgB,GAAiB,EAAgB,EAAkB,EAA0B,CAC3F,IAAM,EAAU,GAAyB,EAAQ,EAAU,CAAG,EAC9D,MAAO,CACL,UACA,OAAQ,GAAwB,EAAQ,EAAU,CAAG,EACrD,QAAS,GAAyB,EAAQ,EAAU,CAAG,EACvD,mBAAoB,GAAoC,EAAQ,EAAU,CAAG,EAC7E,cAAe,GAA+B,EAAQ,EAAU,CAAG,EACnE,YAAa,GAA6B,EAAQ,EAAU,CAAG,EAE/D,OAAQ,GAAwB,EAAQ,EAAU,EAAK,EAAS,IAAI,GAAK,EACzE,aAAc,GAAoB,CAAM,EACxC,aAAc,0BAA0B,KAAK,CAAQ,EAAI,GAAoB,CAAM,EAAI,CAAC,EAGxF,4BAA6B,EAC/B,CACF,CAQA,eAAe,GACb,EACA,EACA,EAC6B,CAC7B,IAAM,EAAM,EAAQ,MAAM,EAAU,UAAU,CAAI,EAAI,KACtD,GAAI,GAAS,EAAK,CAChB,IAAM,EAAM,EAAM,IAAI,EAAM,CAAG,EAC/B,GAAI,EAEF,OADA,EAAM,IAAI,EAAM,EAAK,CAAG,EACjB,CAEX,CACA,IAAI,EACJ,GAAI,CACF,EAAS,MAAM,EAAS,EAAM,OAAO,CACvC,MAAQ,CACN,OAAO,IACT,CACA,IAAM,EAAU,GAAY,EAAQ,EAAM,CAAG,EAE7C,OADI,GAAS,GAAK,EAAM,IAAI,EAAM,EAAK,CAAO,EACvC,CACT,CAGA,eAAe,GACb,EACA,EACA,EACc,CACd,IAAM,EAAW,CAAC,EACd,EAAO,EACL,EAAU,MAAM,KAAK,CAAE,OAAQ,KAAK,IAAI,EAAO,EAAM,MAAM,CAAE,EAAG,SAAY,CAChF,OAAS,CACP,IAAM,EAAI,IACV,GAAI,GAAK,EAAM,OAAQ,OACvB,EAAI,GAAK,MAAM,EAAG,EAAM,GAAI,CAAC,CAC/B,CACF,CAAC,EAED,OADA,MAAM,QAAQ,IAAI,CAAO,EAClB,CACT,CAYA,eAAsB,EAAY,EAAwC,CAOxE,IAAM,GAAS,MAAM,GANR,EAAQ,EAAK,IAMG,EAAG,CAAI,EAAA,CAAG,SAAS,EAE1C,EAAQ,EAAK,SAAW,MAAM,EAAU,KAAK,EAAK,QAAQ,EAAI,KAO9D,EAAS,GAAa,EAAO,MAFZ,GAAc,EAAO,GAAK,GAAS,GAAgB,EAAM,EAAK,IAAK,CAAK,CAAC,CAErD,EACrC,EAAM,MAAM,GAAc,EAAK,IAAK,EAAK,SAAW,YAAY,EAKtE,OAFI,GAAO,MAAM,EAAM,KAAK,EAErB,CAAE,GAAG,EAAQ,KAAI,CAC1B,CAeA,SAAS,GAAgB,EAAc,EAAc,EAA4B,CAC/E,IAAM,EAAO,EAAK,YAAc,EAC1B,EAAW,EAAK,SAAW,EAEjC,GADI,CAAC,EAAK,WAAW,EAAO,CAAG,GAAK,IAAS,GACzC,CAAC,EAAK,KAAM,GAAQ,EAAK,SAAS,CAAG,CAAC,EAAG,MAAO,GACpD,IAAM,EAAM,EAAS,EAAK,IAAK,CAAI,EAEnC,MADA,CAAI,EAAS,KAAM,GAAO,EAAI,SAAS,CAAE,CAAC,CAE5C,CAaA,eAAsB,GACpB,EACA,EACqB,CACrB,GAAI,CAAC,EAAK,SAAU,OAAO,EAAY,CAAI,EAC3C,IAAM,EAAO,EAAQ,EAAK,IAAI,EACxB,EAAQ,MAAM,EAAU,KAAK,EAAK,QAAQ,EAC1C,EAAc,EAAM,YAAY,EACtC,GAAI,EAAY,SAAW,EAAG,OAAO,EAAY,CAAI,EAErD,IAAM,EAAU,IAAI,IAAI,EAAM,QAAQ,IAAK,GAAM,EAAQ,EAAK,IAAK,CAAC,CAAC,CAAC,EAChE,EAAU,EAAM,QACnB,IAAK,GAAM,EAAQ,EAAK,IAAK,CAAC,CAAC,CAAC,CAChC,OAAQ,GAAM,CAAC,EAAQ,IAAI,CAAC,GAAK,GAAgB,EAAG,EAAM,CAAI,CAAC,EAC5D,EAAa,IAAI,IAAI,CAAO,EAG5B,EAAU,IAAI,IAAI,CAAW,EACnC,IAAK,IAAM,KAAK,EAAY,EAAQ,IAAI,CAAC,EACzC,IAAK,IAAM,KAAK,EAAS,EAAQ,OAAO,CAAC,EAIzC,IAAM,EAAQ,IAAI,IAClB,MAAM,GAAc,EAAS,GAAI,KAAO,IAAS,CAC/C,GAAI,CAAC,EAAQ,IAAI,CAAI,EAAG,OACxB,IAAM,EAAM,MAAM,EAAU,UAAU,CAAI,EACtC,EACJ,GAAI,CACF,EAAS,MAAM,EAAS,EAAM,OAAO,CACvC,MAAQ,CACN,EAAQ,OAAO,CAAI,EACnB,MACF,CACA,IAAM,EAAU,GAAY,EAAQ,EAAM,EAAK,GAAG,EAClD,EAAM,IAAI,EAAM,CAAO,EACnB,GAAK,EAAM,IAAI,EAAM,EAAK,CAAO,CACvC,CAAC,EAED,IAAM,EAAe,CAAC,GAAG,CAAO,CAAC,CAAC,SAAS,EAQrC,EAAS,GAAa,EAPX,EAAa,IAAK,GACvB,EAAM,IAAI,CAChB,IACJ,EAAM,MAAM,CAAI,EACT,EAAM,KAAK,CAAI,EAGyB,CAAC,EAC5C,EAAM,MAAM,GAAc,EAAK,IAAK,EAAK,SAAW,YAAY,EAGtE,OAFA,MAAM,EAAM,KAAK,EAEV,CAAE,GAAG,EAAQ,KAAI,CAC1B,CASA,SAAS,GAAa,EAAiB,EAA2D,CAChG,IAAM,EAA6B,CAAC,EAC9B,EAA4B,CAAC,EAC7B,EAA4B,CAAC,EAC7B,EAA8B,CAAC,EAC/B,EAAkD,CAAC,EACnD,EAA0C,CAAC,EAC3C,EAAsC,CAAC,EAOvC,EAAwB,IAAI,IAClC,IAAK,IAAM,KAAW,EACf,KACL,IAAK,GAAM,CAAE,aAAY,eAAe,EAAQ,aACzC,EAAsB,IAAI,CAAU,GACvC,EAAsB,IAAI,EAAY,CAAS,EAOrD,IAAM,EAAqB,IAAI,IAC/B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAU,EAAS,GACpB,KAOL,CANA,EAAQ,KAAK,GAAG,EAAQ,OAAO,EAC/B,EAAO,KAAK,GAAG,EAAQ,MAAM,EAC7B,EAAQ,KAAK,GAAG,EAAQ,OAAO,EAC/B,EAAmB,KAAK,GAAG,EAAQ,kBAAkB,EACrD,EAAc,KAAK,GAAG,EAAQ,aAAa,EAC3C,EAAY,KAAK,GAAG,EAAQ,WAAW,EACnC,EAAQ,aAAa,OAAS,GAAG,EAAmB,IAAI,EAAM,GAAI,EAAQ,YAAY,EAK1F,IAAK,IAAM,KAAS,EAAQ,OAAQ,CAClC,IAAM,EAAY,EAAsB,IAAI,EAAM,UAAU,EAC5D,GAAI,EAAW,CACb,IAAM,EAAW,GAAc,EAAW,EAAM,IAAI,EACpD,EAAO,KAAK,CAAE,GAAG,EAAO,WAAY,GAAkB,CAAQ,EAAG,YAAa,CAAS,CAAC,CAC1F,MACE,EAAO,KAAK,CAAE,GAAG,EAAO,YAAa,EAAM,aAAe,EAAM,IAAK,CAAC,CAE1E,CAb0F,CAc5F,CAKA,GACE,EACA,EACA,EAAS,KAAM,GAAM,GAAG,8BAAgC,EAAI,EAC5D,GAA0B,CAAQ,EAClC,EAAS,QAAS,GAAO,GAAG,gBAAkB,CAAC,EAAE,eAAe,EAAI,CAAC,CAAE,CACzE,EAWA,IAAM,EAAmC,CAAC,EAC1C,IAAK,GAAM,CAAC,EAAY,KAAa,EAAoB,CAEvD,GADI,CAAC,0BAA0B,KAAK,CAAU,GAC1C,EAAS,SAAW,EAAG,SAC3B,IAAM,EAAkB,EAAW,WAAW,EAAK,GAAG,EAChD,EAAY,EAAgB,MAAM,EAAG,EAAgB,YAAY,GAAG,CAAC,EAC3E,IAAK,IAAM,KAAO,EAAS,CAGzB,GAAI,EAAI,YAAc,SAAU,SAChC,IAAM,EAAiB,EAAI,SAAS,WAAW,EAAK,GAAG,EAClD,EAAe,WAAW,EAAY,GAAG,GAC1C,IAAmB,IAElB,GADkB,EAAe,MAAM,EAAU,OAAS,CAC1B,EAAG,CAAQ,GAC9C,EAAgB,KAAK,CACnB,UAAW,EAAI,UACf,SAAU,EAAI,SACd,aAAc,EAAI,aAClB,eAAgB,EAChB,UAAW,EAAI,SACjB,CAAC,EAEL,CACF,CAGA,EAAQ,MAAM,EAAG,IACX,EAAE,YAAc,EAAE,UACf,EAAE,aAAa,cAAc,EAAE,YAAY,EADV,EAAE,UAAU,cAAc,EAAE,SAAS,CAE9E,EACD,EAAO,MACJ,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,GAAK,EAAE,aAAa,cAAc,EAAE,YAAY,CACvF,EACA,EAAQ,MACL,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,GAAK,EAAE,aAAa,cAAc,EAAE,YAAY,CACvF,EACA,EAAO,MACJ,EAAG,IAAM,EAAE,WAAW,cAAc,EAAE,UAAU,GAAK,EAAE,OAAO,cAAc,EAAE,MAAM,CACvF,EACA,EAAmB,MAChB,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,GAAK,EAAE,aAAa,cAAc,EAAE,YAAY,CACvF,EACA,EAAc,MACX,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,GAAK,EAAE,aAAa,cAAc,EAAE,YAAY,CACvF,EACA,EAAY,MACT,EAAG,IAAM,EAAE,IAAI,cAAc,EAAE,GAAG,GAAK,EAAE,aAAa,cAAc,EAAE,YAAY,CACrF,EAEA,IAAM,EAAa,GAAe,CAAO,EAOzC,OALA,EAAgB,MACb,EAAG,IACF,EAAE,aAAa,cAAc,EAAE,YAAY,GAAK,EAAE,UAAU,cAAc,EAAE,SAAS,CACzF,EAEO,CACL,UACA,SACA,SACA,UACA,aACA,qBACA,gBACA,cACA,iBACF,CACF,CCniEA,SAAS,GAAS,EAAiD,CACjE,IAAM,EAAM,EAAS,YAAY,GAAG,EAEpC,OADI,GAAO,EAAU,CAAE,KAAM,EAAU,IAAK,EAAG,EACxC,CAAE,KAAM,EAAS,MAAM,EAAG,CAAG,EAAG,IAAK,EAAS,MAAM,EAAM,CAAC,CAAE,CACtE,CAUA,SAAgB,GAAkB,EAA8B,CAC9D,IAAM,EAAa,EAAa,WAAW,KAAM,GAAG,EAE9C,CAAE,OAAM,OAAQ,GADL,EAAW,MAAM,EAAW,YAAY,GAAG,EAAI,CAC1B,CAAC,EACjC,EAAU,GAAO,KAEjB,EAAW,EAAK,YAAY,GAAG,EAIrC,OAHI,EAAW,EACN,UAAU,EAAK,MAAM,EAAW,CAAC,EAAE,GAAG,IAExC,UAAU,GACnB,CAMA,SAAgB,GAAuB,EAA0C,CAC/E,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAK,EAAS,EAAI,IAAI,GAAkB,EAAE,YAAY,CAAC,EAClE,MAAO,CAAC,GAAG,CAAG,CAAC,CAAC,SAAS,CAC3B,CAcA,SAAS,GAAc,EAAgB,EAAyB,CAC9D,IAAI,EAAQ,EACR,EAAuB,KAC3B,IAAK,IAAI,EAAI,EAAS,EAAI,EAAO,OAAQ,IAAK,CAC5C,IAAM,EAAK,EAAO,GAClB,GAAI,EAAO,CACT,GAAI,IAAO,KAAM,CACf,IACA,QACF,CACI,IAAO,IAAO,EAAQ,MAC1B,QACF,CACA,GAAI,IAAO,KAAO,IAAO,KAAO,IAAO,IACrC,EAAQ,OACH,GAAI,IAAO,IAChB,SACK,GAAI,IAAO,MAChB,IACI,IAAU,GAAG,OAAO,CAE5B,CACA,MAAO,EACT,CAOA,SAAS,GAAgB,EAAgB,EAAc,EAAc,EAAoB,CACvF,IAAI,EAAuB,KAC3B,IAAK,IAAI,EAAI,EAAM,EAAI,EAAI,IAAK,CAC9B,IAAM,EAAK,EAAO,GAClB,GAAI,EAAO,CACT,GAAI,IAAO,KAAM,CACf,IACA,QACF,CACI,IAAO,IAAO,EAAQ,MAC1B,QACF,CACA,GAAI,IAAO,KAAO,IAAO,KAAO,IAAO,IACrC,EAAQ,OACH,GAAI,IAAO,EAChB,OAAO,CAEX,CACA,MAAO,EACT,CAOA,SAAS,GAAgB,EAAgB,EAAyB,CAChE,IAAI,EAAQ,EACR,EAAuB,KAC3B,IAAK,IAAI,EAAI,EAAS,EAAI,EAAO,OAAQ,IAAK,CAC5C,IAAM,EAAK,EAAO,GAClB,GAAI,EAAO,CACT,GAAI,IAAO,KAAM,CACf,IACA,QACF,CACI,IAAO,IAAO,EAAQ,MAC1B,QACF,CACA,GAAI,IAAO,KAAO,IAAO,KAAO,IAAO,IACrC,EAAQ,OACH,GAAI,IAAO,IAChB,SACK,GAAI,IAAO,MAChB,IACI,IAAU,GAAG,OAAO,CAE5B,CACA,MAAO,EACT,CAGA,SAAS,GAAc,EAA4B,CACjD,IAAM,EAAK,6BACL,EAAoB,CAAC,EACvB,EACJ,MAAQ,EAAI,EAAG,KAAK,CAAM,KAAO,MAAM,CACrC,IAAM,EAAO,EAAO,QAAQ,IAAK,EAAE,KAAK,EACxC,GAAI,EAAO,EAAG,SACd,IAAM,EAAQ,GAAc,EAAQ,CAAI,EACpC,EAAQ,GACZ,EAAM,KAAK,CAAE,MAAO,EAAE,MAAO,OAAM,OAAM,CAAC,CAC5C,CACA,OAAO,CACT,CAeA,SAAgB,GACd,EACA,EACe,CACf,IAAM,EAAQ,GAAc,CAAM,EAClC,GAAI,EAAM,SAAW,EAAG,OAAO,KAG/B,IAAM,EACJ,EAAM,KAAM,GAAM,uBAAuB,KAAK,EAAO,MAAM,EAAE,KAAM,EAAE,MAAQ,CAAC,CAAC,CAAC,GAAK,EAAM,GAIvF,EAAa,EAAO,MAAM,EAAO,MAAO,EAAO,MAAQ,CAAC,EACxD,EAAW,IAAI,IACnB,GAAoB,CAAU,CAAC,CAAC,IAAK,GAAO,EAAE,WAAW,GAAG,EAAI,EAAE,MAAM,CAAC,EAAI,CAAE,CACjF,EACM,EAAQ,EAAY,OAAQ,GAAM,CAAC,EAAS,IAAI,CAAC,CAAC,EACxD,GAAI,EAAM,SAAW,EAAG,OAAO,KAE/B,IAAM,EAAY,EAAM,IAAK,GAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAMhD,EAAU,GAAgB,EAAQ,IAAK,EAAO,KAAM,EAAO,KAAK,EACtE,GAAI,GAAW,EAAG,CAChB,IAAM,EAAe,GAAgB,EAAQ,CAAO,EACpD,GAAI,EAAe,GAAK,EAAe,EAAO,MAAO,OAAO,KAE5D,IAAM,EAAS,EAAO,MAAM,EAAG,CAAY,EAG3C,OAAO,GAFa,WAAW,KAAK,CAAM,EACV,GAAP,MACH,EAAY,EAAO,MAAM,CAAY,CAC7D,CAGA,IAAM,EAAQ,8BACR,EAAO,EAAO,MAAM,EAAO,KAAO,EAAG,EAAO,KAAK,EACjD,EAAI,EAAM,KAAK,CAAI,EACzB,GAAI,CAAC,EAAG,OAAO,KACf,IAAM,EAAW,EAAO,KAAO,EAAI,EAAE,MAC/B,EAAS,EAAW,EAAE,EAAE,CAAC,OAC/B,OAAO,EAAO,MAAM,EAAG,CAAQ,EAAI,IAAI,EAAE,GAAG,IAAI,EAAU,GAAK,EAAO,MAAM,CAAM,CACpF,CC5MA,MAAa,EAAS,6IAMT,GAAsB,IAAI,IAAI,CAAC,UAAW,aAAc,aAAc,WAAW,CAAC,EAG/F,IAAa,GAAb,cAAyC,KAAM,CAC7C,WACA,YAAY,EAA8B,CACxC,MAAM,GAAuB,CAAU,CAAC,EACxC,KAAK,KAAO,sBACZ,KAAK,WAAa,CACpB,CACF,EAGA,SAAS,GAAuB,EAAsC,CACpE,IAAM,EAAkB,CAAC,wCAAwC,EACjE,IAAK,IAAM,KAAK,EAAY,CAC1B,EAAM,KAAK,EAAE,EACb,EAAM,KAAK,KAAK,EAAE,QAAQ,OAAO,kBAAkB,EAAE,UAAU,GAAG,EAClE,IAAK,IAAM,KAAO,EAAE,QAClB,EAAM,KAAK,SAAS,EAAI,cAAc,CAE1C,CASA,OARA,EAAM,KAAK,EAAE,EACb,EAAM,KAAK,cAAc,EACzB,EAAM,KAAK,iCAAiC,EAC5C,EAAM,KACJ,mGACF,EACA,EAAM,KAAK,4EAA4E,EACvF,EAAM,KAAK,mEAAmE,EACvE,EAAM,KAAK;CAAI,CACxB,CAGA,SAAS,GAAmB,EAAoB,EAA0B,CAExE,IAAI,EAAM,EADM,EAAQ,CACC,EAAG,CAAU,CAAC,CAAC,MAAM,CAAG,CAAC,CAAC,KAAK,GAAG,EAG3D,MAFA,GAAM,EAAI,QAAQ,uBAAwB,EAAE,EACvC,EAAI,WAAW,GAAG,IAAG,EAAM,KAAO,GAChC,CACT,CAQA,SAAgB,GAAiB,EAA8B,CAE7D,IAAM,EADM,EAAI,aAAa,QAAQ,SAAU,EAAE,CAAC,CAAC,QAAQ,uBAAwB,EACnE,CAAC,CAAC,MAAM,GAAG,EAC3B,EAAM,IAAI,EACV,IAAM,EAAK,EAAM,KAAK,GAAG,EACzB,OAAO,EAAK,GAAG,EAAG,GAAG,EAAI,YAAc,EAAI,SAC7C,CAQA,SAAgB,GACd,EACA,EACA,EACQ,CACR,IAAM,EAAO,IAAI,IACX,EAAoB,CAAC,EAE3B,IAAK,IAAM,KAAK,EAAS,CACvB,GAAI,CAAC,GAAoB,IAAI,EAAE,SAAS,EAAG,SAE3C,IAAM,EAAM,EAAe,IAAI,EAAE,SAAS,EAAI,GAAiB,CAAC,EAAI,EAAE,UACtE,GAAI,EAAK,IAAI,CAAG,EAAG,SACnB,EAAK,IAAI,CAAG,EAEZ,IAAM,EAAO,GAAmB,EAAE,SAAU,CAAO,EAC7C,EAAM,EAAE,UAAY,WAAW,EAAK,YAAc,WAAW,EAAK,KAAK,EAAE,YAC/E,EAAQ,KAAK,QAAQ,EAAI,KAAK,GAAK,CACrC,CAEA,IAAM,EAAO,EAAQ,OACjB,EAAQ,KAAK;CAAI,EACjB,+EAEJ,MAAO,GAAG,EAAO;;;EAGjB,EAAK;;;;;CAMP,CAGA,SAAS,GAAgB,EAAsB,CAC7C,MAAO,6BAA6B,KAAK,CAAG,CAC9C,CASA,SAAgB,GAAkB,EAA0C,CAE1E,IAAM,EADS,CAAC,GAAG,IAAI,IAAI,EAAK,IAAK,GAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAClC,CAAC,CAChB,IAAK,GAAM,OAAO,GAAgB,CAAC,EAAI,EAAI,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,CACrE,KAAK;CAAI,EACZ,MAAO,GAAG,EAAO;;;;;;;;;EASjB,EAAK;;;;;CAMP,CAGA,SAAgB,GAAY,EAAkB,EAAiB,EAA8B,CAC3F,GAAI,EAAM,SAAW,EACnB,MAAO,GAAG,EAAO;KAChB,EAAa;cACJ,EAAS;EAGrB,IAAM,EAAS,CAAC,GAAG,IAAI,IAAI,CAAK,CAAC,CAAC,CAAC,SAAS,EAC5C,MAAO,GAAG,EAAO;cACL,EAAS;EACrB,EAAO,IAAK,GAAM,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK;CAAI,EAAE;CAE7C,CAOA,SAAgB,GACd,EACA,EACA,EACA,EACU,CAIV,MAAO,CAAC,GAHY,EACjB,OAAQ,GAAM,GAAoB,IAAI,EAAE,SAAS,CAAC,CAAC,CACnD,IAAK,GAAO,EAAe,IAAI,EAAE,SAAS,EAAI,GAAiB,CAAC,EAAI,EAAE,SACpD,EAAG,GAAG,EAAO,IAAK,GAAM,EAAE,IAAI,EAAG,GAAG,EAAQ,IAAK,GAAM,EAAE,IAAI,CAAC,CACrF,CAGA,SAAgB,GAAkB,EAAsC,CACtE,OAAO,EAAQ,OAAQ,GAAM,EAAE,YAAc,QAAQ,CAAC,CAAC,IAAK,GAAM,EAAE,SAAS,CAC/E,CAeA,SAAgB,GAAc,EAA4C,CAIxE,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EACZ,EAAO,IAAI,EAAK,IAAI,GAAG,EAAO,IAAI,EAAK,KAAM,CAAI,EAIxD,IAAM,EADS,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,UAAU,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAC7D,CAAC,CAAC,IAAK,GAAS,QAAQ,EAAK,KAAK,MAAM,EAAK,KAAK,EAAE,CAAC,CAAC,KAAK;CAAI,EAMpF,MAAO,GAAG,EAAO;;;;;;;;;EAJJ,GAET,+FAWC;;;;;CAMP,CAOA,SAAgB,GAAoB,EAAyC,CAC3E,GAAI,EAAM,SAAW,EACnB,MAAO,GAAG,EAAO;;;;;;;;;;;;;EAkBnB,IAAM,EAAS,IAAI,IACnB,IAAK,IAAM,KAAQ,EACZ,EAAO,IAAI,EAAK,IAAI,GAAG,EAAO,IAAI,EAAK,KAAM,CAAI,EAGxD,IAAM,EAAmB,CAAC,EAC1B,IAAK,IAAM,IAAQ,CAAC,GAAG,EAAO,OAAO,CAAC,CAAC,CAAC,UAAU,EAAG,IAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,EAAG,CACxF,IAAM,EAAqB,CAAC,EAC5B,GAAI,EAAK,YACP,IAAK,IAAM,KAAQ,EAAK,YAAY,MAAM;CAAI,EAAG,EAAS,KAAK,MAAM,GAAM,EAE7E,GAAI,EAAK,QAAS,CAChB,EAAS,KAAK,cAAe,UAAa,EAC1C,IAAK,IAAM,KAAQ,EAAK,QAAQ,MAAM;CAAI,EAAG,EAAS,KAAK,MAAM,GAAM,EACvE,EAAS,KAAK,QAAW,CAC3B,CACA,EAAS,KAAK,WAAW,EAAK,cAAc,EAC5C,EAAO,KACL,CAAC,MAAO,GAAG,EAAU,MAAO,oBAAoB,EAAK,KAAK,gBAAgB,CAAC,CAAC,KAAK;CAAI,CACvF,CACF,CAEA,MAAO,GAAG,EAAO;;;;;EAKjB,EAAO,KAAK;;CAAM,EAAE;CAEtB,CC7QA,MAAM,GACJ,0EAYF,SAAgB,GACd,EAC0B,CAC1B,IAAM,EAAqC,CAAC,EAC5C,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAM,KACf,EAAK,WAAW,SAAa,GAC7B,GAAuB,KAAK,CAAI,GACpC,EAAS,KAAK,CACZ,MAAO,EACP,SAAU,EAAM,SAChB,SAAU,EAAM,aAChB,OAAQ,+DACR,WAAY,GAAc,CAAI,CAChC,CAAC,CACH,CACA,OAAO,CACT,CAEA,SAAS,GAAc,EAAkC,CACvD,GAAI,aAAa,KAAK,CAAI,EACxB,MAAO,YAAY,EAAK,kBAAkB,EAAK,IAEjD,GAAI,EAAK,SAAS,GAAG,EACnB,MAAO,sDAET,IAAM,EAAa,8BAA8B,KAAK,CAAI,EAC1D,GAAI,EAAY,CACd,GAAM,EAAG,EAAO,GAAO,EACvB,MAAO,IAAI,EAAM,GAAG,EAAI,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAI,MAAM,CAAC,EAAE,EACjE,CAEF,CC3CA,SAAgB,GACd,EACA,EACkB,CAClB,GAAI,CAAC,EAAU,MAAO,CAAE,QAAS,CAAC,EAAG,MAAO,CAAE,EAE9C,IAAM,EAAO,IAAI,IACjB,IAAK,GAAM,CAAC,EAAW,KAAU,OAAO,QAAQ,CAAQ,EAAG,CACzD,GAAI,CAAC,GAAS,OAAO,EAAM,KAAQ,SAAU,SAC7C,IAAM,EAAS,EAAQ,EAAK,EAAM,GAAG,EACrC,GAAI,CAAC,GAAM,CAAM,EAAG,SAKpB,IAAM,EAAU,EAAS,EAAM,MAAQ,OAAQ,CAC7C,IAAK,EACL,MAAO,GACP,IAAK,GACL,MAAO,EACT,CAAC,EACD,EAAQ,KAAK,EAIb,GAAM,CAAE,SAAU,EAAe,EAAW,EAAS,CACnD,SAAU,EAAM,MAAQ,MAC1B,CAAC,EACD,IAAK,GAAM,CAAE,IAAK,KAAa,EAAO,CAIpC,IAAM,EAAS,EAAQ,MAAM,EAAU,OAAS,CAAC,EACjD,EAAK,IAAI,EAAS,CAAE,YAAW,IAAK,CAAO,CAAC,CAC9C,CACF,CACA,MAAO,CAAE,QAAS,CAAC,GAAG,EAAK,OAAO,CAAC,EAAG,MAAO,EAAK,IAAK,CACzD,CAEA,SAAgB,GAAiB,EAAsC,CACrE,IAAM,EAAS,6IAKf,GAAI,EAAW,QAAQ,SAAW,EAChC,MAAO,GAAG,EAAO;;;;;;;;;;;EAcnB,IAAM,EAAiB,CAAC,EACxB,IAAK,IAAM,KAAS,EAAW,QAAS,CACtC,IAAM,EAAO,GAAG,EAAM,UAAU,GAAG,EAAM,MAAM,MAAM,GAAG,EACpD,EAAO,EACX,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAS,EAAG,IAAK,CACxC,IAAM,EAAO,EAAK,GACZ,EAAW,EAAK,GACtB,GAAI,IAAa,GAAM,CAOrB,IAAM,EAAqB,CAAC,EAC5B,EAAK,GAAQ,EACb,EAAO,CACT,MACO,IAAU,EAAK,GAAQ,CAAC,GAC7B,EAAO,EAAK,EAEhB,CACA,IAAM,EAAO,EAAK,EAAK,OAAS,GAC5B,OAAO,EAAK,IAAU,WAK1B,EAAK,GAAQ,GACf,CAGA,MAAO,GAAG,EAAO;;;;;;;;;EADJ,GAAW,EAAM,MAU3B,EAAE;;;;;CAMP,CAEA,MAAM,GAAO,OAAO,YAAY,EAGhC,SAAS,GAAW,EAAgB,EAAwB,CAC1D,IAAM,EAAO,OAAO,KAAK,CAAI,CAAC,CAAC,SAAS,EAClC,EAAkB,CAAC,EACzB,IAAK,IAAM,KAAO,EAAM,CACtB,IAAM,EAAQ,EAAK,GACb,EAAU,GAAa,CAAG,EAAI,EAAM,KAAK,UAAU,CAAG,EACxD,IAAU,GACZ,EAAM,KAAK,GAAG,IAAS,EAAQ,eAAe,GAE9C,EAAM,KAAK,GAAG,IAAS,EAAQ,IAAI,EACnC,EAAM,KAAK,GAAW,EAAO,GAAG,EAAO,GAAG,CAAC,EAC3C,EAAM,KAAK,GAAG,EAAO,EAAE,EAE3B,CACA,OAAO,EAAM,KAAK;CAAI,CACxB,CAEA,SAAS,GAAa,EAAsB,CAC1C,MAAO,6BAA6B,KAAK,CAAG,CAC9C,CAEA,SAAS,GAAM,EAAuB,CACpC,GAAI,CACF,OAAO,EAAS,CAAI,CAAC,CAAC,YAAY,CACpC,MAAQ,CACN,MAAO,EACT,CACF,wGCpCA,SAAS,GAAe,EAQtB,CACA,IAAM,EAAM,EAAK,KAAO,QAAQ,IAAI,EACpC,MAAO,CACL,MACA,OAAQ,EAAQ,EAAK,EAAK,QAAU,KAAK,EACzC,OAAQ,EAAQ,EAAK,EAAK,QAAU,eAAe,EACnD,OAAQ,EAAK,QAAU,GACvB,gBAAiB,EAAK,iBAAmB,GACzC,gBAAiB,EAAK,iBAAmB,GACzC,QAAS,EAAK,SAAW,YAC3B,CACF,CAWA,eAAsB,GAAW,EAA0B,CAAC,EAKzD,CACD,GAAM,CAAE,MAAK,SAAQ,SAAQ,SAAQ,kBAAiB,WAAY,GAAe,CAAI,EAE/E,EAAQ,KAAK,IAAI,EACjB,EAAW,CACf,KAAM,EACN,MAGA,SAAU,EAAK,QAAU,IAAA,GAAY,EAAQ,EAAK,UAAW,OAAO,EAEpE,QAAS,IAAY,GAAQ,IAAA,GAAY,CAC3C,EACM,EAAO,EAAK,aACd,MAAM,GAAuB,EAAU,EAAK,YAAY,EACxD,MAAM,EAAY,CAAQ,EAQ9B,GAAI,EAAK,WAAW,OAAS,GAAK,CAAC,EACjC,MAAM,IAAI,GAAoB,EAAK,UAAU,EAG/C,IAAM,EAAS,GAAe,EAAK,SAAU,CAAG,EAW5C,EAAuC,CAAC,EACtC,EAAoB,CAAC,EAC3B,GAAI,EAAK,aAAe,GAAO,CAC7B,GAAI,CACF,GAAM,CAAE,wBAAyB,MAAM,OAAO,8BACxC,CAAE,kBAAmB,MAAM,OAAO,wBAAY,CAAA,KAAA,GAAA,EAAA,CAAA,EAEpD,EAAgB,MAAM,EAAqB,CACzC,MACA,OAAQ,MAHiB,EAAe,CAAG,EAI3C,OAAQ,GACR,aAAc,EAAK,YACrB,CAAC,CACH,OAAS,EAAK,CAKZ,GAAI,CAAC,EAAQ,CACX,IAAM,EAAM,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,EAC3D,QAAQ,KAAK,2CAA2C,EAAI,eAAe,CAC7E,CACF,CACA,EAAQ,KAAK,GAAI,MAAM,EAAsB,EAAQ,EAAe,CAAM,CAAE,CAC9E,CAEA,IAAM,EAAgB,GAAyB,EAAK,MAAM,EACpD,EAAS,GAAoB,EAAM,EAAO,MAAO,CAAO,EACxD,EAAU,KAAK,IAAI,EAAI,EAE7B,GAAI,CAAC,EAAQ,CACX,IAAM,EAAQ,EAAO,QAAQ,EAAM,IAAK,EAAE,EACpC,EACJ,EAAO,mBAAqB,EAAI,KAAK,EAAO,mBAAmB,wBAA0B,GACrF,EAAU,EAAO,WAAa,cAAgB,GAC9C,EAAa,EAAO,cAAgB,EAAI,KAAK,EAAO,cAAc,mBAAqB,GACvF,EACJ,EAAO,oBAAsB,EAAI,KAAK,EAAO,oBAAoB,gBAAkB,GAC/E,EAAY,EAAO,aAAe,EAAI,KAAK,EAAO,aAAa,SAAW,GAIhF,GAHA,QAAQ,IACN,oBAAoB,EAAO,cAAc,aAAa,EAAO,aAAa,WAAW,EAAO,aAAa,UAAU,IAAa,IAAU,IAAY,IAAU,EAAc,KAAK,EAAM,IAAI,EAAQ,IACvM,EACI,EAAc,OAAS,EAAG,CAC5B,QAAQ,KACN,mBAAmB,EAAc,OAAO,4CAC1C,EACA,IAAK,IAAM,KAAW,EAAe,CACnC,IAAM,EAAe,EAAQ,SAAW,KAAK,EAAQ,SAAS,GAAK,GACnE,QAAQ,KACN,QAAQ,EAAQ,MAAM,KAAK,EAAQ,SAAS,GAAG,EAAa,KAAK,EAAQ,QAC3E,EACI,EAAQ,YACV,QAAQ,KAAK,uBAAuB,EAAQ,YAAY,CAE5D,CACF,CACF,CAYA,OAJI,EAAK,gBAAgB,OAAS,GAChC,MAAM,GAAsB,EAAK,gBAAiB,CAAE,IAAK,EAAK,KAAO,GAAO,QAAO,CAAC,EAG/E,CAAE,OAAM,SAAQ,eAAc,CACvC,CASA,eAAe,GACb,EACA,CAAE,MAAK,UACQ,CAEf,IAAM,EAAW,IAAI,IACrB,IAAK,IAAM,KAAK,EAAS,CACvB,IAAM,EAAO,EAAS,IAAI,EAAE,cAAc,GAAK,CAAC,EAChD,EAAK,KAAK,CAAC,EACX,EAAS,IAAI,EAAE,eAAgB,CAAI,CACrC,CAEA,GAAI,EAAK,CACP,IAAI,EAAU,EACR,EAAoB,CAAC,EAC3B,IAAK,GAAM,CAAC,EAAY,KAAU,EAAU,CAC1C,IAAM,EAAW,GAAuB,CAAK,EAC7C,GAAI,CACF,IAAM,EAAS,MAAM,EAAS,EAAY,OAAO,EAC3C,EAAO,GAAsB,EAAQ,CAAQ,EAC/C,GAAQ,IAAS,GACnB,MAAM,EAAU,EAAY,CAAI,EAChC,IACK,GACH,QAAQ,IACN,iCAAiC,EAAW,YAAY,EAAS,KAAK,IAAI,GAC5E,GAGF,EAAQ,KAAK,CAAU,CAE3B,MAAQ,CACN,EAAQ,KAAK,CAAU,CACzB,CACF,CACA,GAAI,CAAC,IACC,EAAU,GACZ,QAAQ,IACN,iCAAiC,EAAQ,oEAC3C,EAEE,EAAQ,OAAS,GAAG,CACtB,QAAQ,KACN,8CAA8C,EAAQ,OAAO,0EAC/D,EACA,IAAK,IAAM,KAAK,EAAS,CACvB,IAAM,EAAQ,EAAS,IAAI,CAAC,GAAK,CAAC,EAClC,QAAQ,KAAK,OAAO,EAAE,IAAI,GAAuB,CAAK,CAAC,CAAC,KAAK,IAAI,GAAG,CACtE,CACF,CAEF,MACF,CAEI,MAEJ,SAAQ,KACN,mBAAmB,EAAQ,OAAO,qEACpC,EACA,IAAK,GAAM,CAAC,EAAY,KAAU,EAAU,CAC1C,IAAK,IAAM,KAAU,EACnB,QAAQ,KAAK,QAAQ,EAAO,UAAU,GAAG,EAAO,UAAU,IAAI,EAAO,aAAa,EAAE,EAEtF,IAAM,EAAW,GAAuB,CAAK,EAC7C,QAAQ,KAAK,6CAA6C,EAAW,EAAE,EACvE,QAAQ,KAAK,aAAa,EAAS,IAAK,GAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG,CACtE,CACA,QAAQ,KAAK,mEAAqE,CATlF,CAUF,CAGA,SAAS,GACP,EACA,EACA,EACgB,CAChB,IAAM,EAAY,IAAI,IAAI,EAAK,WAAW,IAAK,GAAM,EAAE,SAAS,CAAC,EAC3D,EAAkB,EAAK,QAAQ,OAAQ,GAAM,GAAoB,IAAI,EAAE,SAAS,CAAC,EACjF,EAAe,GAAmB,EAAK,QAAS,EAAK,OAAQ,EAAK,QAAS,CAAS,EAC1F,MAAO,CACL,gBAAiB,EAAgB,OACjC,cAAe,IAAI,IAAI,CAAY,CAAC,CAAC,KACrC,aAAc,GAAkB,EAAK,OAAO,CAAC,CAAC,OAC9C,aAAc,EAAK,OAAO,OAC1B,cAAe,IAAI,IAAI,EAAK,mBAAmB,IAAK,GAAM,EAAE,IAAI,CAAC,CAAC,CAAC,KACnE,oBAAqB,IAAI,IAAI,EAAK,cAAc,IAAK,GAAM,EAAE,IAAI,CAAC,CAAC,CAAC,KACpE,aAAc,EACd,WAAY,EAAK,MAAQ,KACzB,UACA,mBAAoB,EAAK,WAAW,MACtC,CACF,CAiBA,eAAsB,EACpB,EACA,EACA,EACmB,CACnB,MAAM,EAAM,EAAQ,CAAE,UAAW,EAAK,CAAC,EAGvC,MAAM,EACJ,EAAK,EAAQ,CAAM,EAAG,YAAY,EAClC;;EACA,OACF,EACA,IAAM,EAAU,EAAc,OAAQ,GAAM,EAAE,OAAO,CAAC,CAAC,IAAK,GAAM,EAAE,OAAiB,EAErF,OADA,MAAM,GAAkB,EAAQ,EAAS,EAAe,CAAM,EACvD,CACT,CAiBA,eAAsB,GAAa,EAA0B,CAAC,EAAwB,CACpF,IAAM,EAAW,GAAe,CAAI,EAC9B,CAAE,SAAQ,SAAQ,OAAQ,EAS1B,EAA6B,CACjC,GAAG,EACH,gBAAiB,GACjB,WAAY,GACZ,QAAS,EAAK,OAChB,EAMM,EACJ,QAAQ,IAAI,uBAAyB,KAAO,QAAQ,IAAI,uBAAyB,OAK7E,CAAC,CAAE,wBAAwB,CAAE,mBAAoB,MAAM,QAAQ,IAAI,CACvE,OAAO,8BACP,OAAO,wBAAY,CAAA,KAAA,GAAA,EAAA,CAAA,CACrB,CAAC,EACK,EAAe,MAAM,EAAe,CAAG,EAIvC,EAAY,SAAY,CAC5B,GAAI,CACF,MAAM,GAAW,CAAE,GAAG,CAAQ,CAAC,CACjC,OAAS,EAAK,CACZ,GAAI,EAAQ,OACZ,GAAI,aAAe,GACjB,QAAQ,MAAM;EAAO,EAAI,QAAU;CAAI,MAClC,CACL,IAAM,EAAM,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,EAC3D,QAAQ,MAAM,0BAA0B,GAAK,CAC/C,CACF,CACF,EACM,EAAa,SAAY,CAC7B,GAAI,CACF,IAAM,EAAgB,MAAM,EAAqB,CAC/C,MACA,OAAQ,EACR,OAAQ,EACV,CAAC,EACD,MAAM,EAAsB,EAAS,OAAQ,EAAe,EAAI,CAClE,MAAQ,CAER,CACF,EAGA,MAAM,EAAU,EAChB,MAAM,EAAW,EAEjB,GAAM,CAAE,SAAU,MAAM,OAAO,WAE3B,EAA8C,KAC5C,EAAW,GAA4B,CAEtC,GACA,sBAAsB,KAAK,CAAQ,IACpC,EAAS,SAAS,SAAS,GAC3B,EAAS,SAAS,OAAO,IAEzB,GAAO,aAAa,CAAK,EAC7B,EAAQ,eAAiB,CACvB,EAAe,CAAC,CAAC,KAAK,CAAU,CAClC,EAAG,GAAG,GACR,EAKA,GAAI,EAAc,CACX,GACH,QAAQ,IAAI,qDAAqD,EAMnE,IAAM,EAAW,gBAAkB,CACjC,EAAe,CAAC,CAAC,KAAK,CAAU,CAClC,EAAG,GAAI,EACP,UAAa,cAAc,CAAQ,CACrC,CAEA,IAAI,EACJ,GAAI,CACF,EAAU,EAAM,EAAQ,CAAE,UAAW,EAAK,GAAI,EAAQ,IAAa,CACjE,EAAQ,CAAQ,CAClB,CAAC,CACH,OAAS,EAAU,CACZ,GACH,QAAQ,KACN,2CAA2C,GAAK,SAAW,EAAI,4BACjE,EAKF,IAAM,EAAW,gBAAkB,CACjC,EAAe,CAAC,CAAC,KAAK,CAAU,CAClC,EAAG,GAAI,EACP,UAAa,cAAc,CAAQ,CACrC,CAEA,UAAa,CACP,GAAO,aAAa,CAAK,EAC7B,EAAQ,MAAM,CAChB,CACF,CAgBA,eAAsB,GACpB,EACA,EACA,EACA,EACmB,CACnB,IAAM,EAAW,IAAI,IACrB,IAAK,IAAM,KAAQ,EAAkB,EAAS,IAAI,EAAS,CAAI,CAAC,EAChE,IAAK,IAAM,KAAK,EACV,EAAE,SAAS,EAAS,IAAI,EAAS,EAAE,OAAO,CAAC,EAEjD,IAAI,EACJ,GAAI,CACF,EAAU,MAAM,EAAQ,CAAM,CAChC,MAAQ,CACN,MAAO,CAAC,CACV,CACA,IAAM,EAAoB,CAAC,EAC3B,IAAK,IAAM,KAAQ,EAAS,CAW1B,GADI,CAAC,GAAoB,IAAI,CAAI,GAC7B,EAAS,IAAI,CAAI,EAAG,SACxB,IAAM,EAAM,EAAQ,EAAQ,CAAI,EAChC,GAAI,CAEF,GAAI,EAAC,MADW,EAAK,CAAG,EAAA,CACjB,OAAO,EAAG,SACjB,MAAM,EAAO,CAAG,EAChB,EAAQ,KAAK,CAAI,CACnB,MAAQ,CAER,CACF,CAIA,OAHI,EAAQ,OAAS,GAAK,CAAC,GACzB,QAAQ,IAAI,yBAAyB,EAAQ,OAAO,kBAAkB,EAAQ,KAAK,IAAI,GAAG,EAErF,CACT,CAiBA,MAAM,GAA2C,IAAI,IAAI,CACvD,cACA,SACA,YACA,gBACA,gBACA,eACA,eACA,qBACA,YACF,CAAC"}