@kubb/studio 5.3.12 → 5.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["detectTool","process","logLevel","logLevelMap","detectUncachedTool","process","process","logLevel","logLevelMap","kubbVersion","version","delay"],"sources":["../src/constants.ts","../../../internals/utils/src/casing.ts","../../../internals/utils/src/errors.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../../../internals/utils/src/promise.ts","../../../internals/utils/src/tools.ts","../../../internals/utils/src/time.ts","../src/machine.ts","../src/api.ts","../package.json","../src/hooks.ts","../src/resolveConfig.ts","../src/configFile.ts","../src/generate.ts","../src/snapshotPackage.ts","../src/ws.ts","../src/rpc.ts","../src/StudioSession.ts","../src/client.ts","../src/runConnection.ts","../src/pair.ts"],"sourcesContent":["/**\n * Hosted Kubb Studio URL. Exported so credential stores can bind tokens to the resolved instance,\n * not whatever default the client would pick on its own.\n */\nexport const defaultStudioUrl = 'https://kubb.studio'\n\n/**\n * Defaults the Studio client uses when a host passes nothing.\n * Config path is left out on purpose: each host discovers that itself.\n */\nexport const agentDefaults = {\n studioUrl: defaultStudioUrl,\n retryIntervalMs: 30_000,\n heartbeatIntervalMs: 30_000,\n /**\n * Slowest heartbeat a host may ask for. Studio drops an agent from the active list once its\n * stored ping is older than its liveness window, and it stores a ping at most once a minute, so\n * a slower cadence would make a healthy agent look dead after a single missed ping.\n */\n maxHeartbeatIntervalMs: 60_000,\n /** How long a heartbeat ping may take before the session is treated as dead. */\n heartbeatTimeoutMs: 10_000,\n poolSize: 1,\n} as const\n","type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new Error('Build failed', { cause: toError(err) })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","import { spawn } from 'node:child_process'\n\n/**\n * How one formatter or linter is invoked: the executable, the argv it takes for an output\n * directory, and what to report when it is not installed.\n */\nexport type ToolCommand = {\n command: string\n args: (outputPath: string) => Array<string>\n errorMessage: string\n}\n\n/**\n * CLI command descriptors for each supported code formatter.\n */\nexport const formatters = {\n prettier: {\n command: 'prettier',\n args: (outputPath: string) => ['--ignore-unknown', '--write', outputPath],\n errorMessage: 'Prettier not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['format', '--write', outputPath],\n errorMessage: 'Biome not found',\n },\n oxfmt: {\n command: 'oxfmt',\n args: (outputPath: string) => [outputPath],\n errorMessage: 'Oxfmt not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * CLI command descriptors for each supported linter.\n */\nexport const linters = {\n eslint: {\n command: 'eslint',\n args: (outputPath: string) => [outputPath, '--fix'],\n errorMessage: 'Eslint not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['lint', '--fix', outputPath],\n errorMessage: 'Biome not found',\n },\n oxlint: {\n command: 'oxlint',\n // --no-ignore so oxlint lints the folder even when it's gitignored (generated output dirs usually are).\n args: (outputPath: string) => ['--fix', '--no-ignore', outputPath],\n errorMessage: 'Oxlint not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * Preference order for `format: 'auto'`, most-preferred first. Spelled out rather than taken from\n * the table's key order, which is arbitrary and would silently change what `auto` picks.\n */\nexport const FORMATTER_PREFERENCE = ['oxfmt', 'biome', 'prettier'] as const\n\n/**\n * Preference order for `lint: 'auto'`, most-preferred first.\n */\nexport const LINTER_PREFERENCE = ['oxlint', 'biome', 'eslint'] as const\n\n/**\n * Whether `name` is on PATH and answers `--version` with a zero exit.\n */\nexport function isToolAvailable(name: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(name, ['--version'], { stdio: 'ignore' })\n child.on('close', (code) => resolve(code === 0))\n child.on('error', () => resolve(false))\n })\n}\n\n/**\n * Returns the first installed executable from `candidates`, or `null` when none are found.\n *\n * Not memoized: a long-running host that probes repeatedly should cache the result itself, and a\n * `--watch` build should keep noticing a tool installed mid-session.\n */\nexport async function detectTool<TName extends string>(candidates: ReadonlyArray<TName>): Promise<TName | null> {\n for (const candidate of candidates) {\n if (await isToolAvailable(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Tokenizes a shell command string, respecting single and double quotes.\n *\n * @example\n * ```ts\n * tokenize('git commit -m \"initial commit\"')\n * // → ['git', 'commit', '-m', 'initial commit']\n * ```\n */\nexport function tokenize(command: string): Array<string> {\n return (command.match(/[^\\s\"']+|\"([^\"]*)\"|'([^']*)'/g) ?? []).map((token) => token.replace(/^[\"']|[\"']$/g, ''))\n}\n","/**\n * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.\n * Rounds to 2 decimal places for sub-millisecond precision without noise.\n *\n * @example\n * ```ts\n * const start = process.hrtime()\n * doWork()\n * getElapsedMs(start) // 42.35\n * ```\n */\nexport function getElapsedMs(hrStart: [number, number]): number {\n const [seconds, nanoseconds] = process.hrtime(hrStart)\n const ms = seconds * 1000 + nanoseconds / 1e6\n return Math.round(ms * 100) / 100\n}\n\n/**\n * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).\n *\n * @example\n * ```ts\n * formatMs(250) // '250ms'\n * formatMs(1500) // '1.50s'\n * formatMs(90000) // '1m 30.0s'\n * ```\n */\nexport function formatMs(ms: number): string {\n if (ms >= 60000) {\n const mins = Math.floor(ms / 60000)\n const secs = ((ms % 60000) / 1000).toFixed(1)\n return `${mins}m ${secs}s`\n }\n\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(2)}s`\n }\n return `${Math.round(ms)}ms`\n}\n","import { hash, randomBytes } from 'node:crypto'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { createStorage, type Storage } from 'unstorage'\nimport fsDriver from 'unstorage/drivers/fs'\n\n/**\n * Key-value storage the runtime uses for its machine secret and the last Studio config.\n *\n * One storage per process, since one process serves one config file. Hosts install their own\n * driver on startup: Nitro passes its `kubb` mount, the CLI an fs driver under `~/.kubb/cache`.\n * The in-memory default keeps the runtime usable without a host, at the cost of a machine\n * identity that changes on every restart.\n */\nlet storage: Storage = createStorage()\nlet hasInstalledStorage = false\n\n/**\n * Installs the storage driver the runtime persists to. Call once, before connecting.\n */\nexport function setStorage(next: Storage): void {\n storage = next\n hasInstalledStorage = true\n}\n\n/**\n * A storage backed by files under `base`, so the machine secret and the last Studio config\n * survive a restart. Repeated pairings of one machine depend on that secret staying put.\n */\nexport function createFileStorage(base: string): Storage {\n return createStorage({ driver: fsDriver({ base }) })\n}\n\nlet fallbackSecretPromise: Promise<string> | null = null\n\n/**\n * Loads the fallback machine secret from the runtime storage.\n * On first use it generates a secret and persists it, so the machine identity stays\n * stable across restarts. An identity that changes on every boot breaks session\n * creation with Studio whenever the startup registration call fails.\n */\nasync function loadOrCreateFallbackSecret(): Promise<string> {\n // The secret is memoized for the life of the process, so a host that reads the machine token\n // before installing its storage is bound to the throwaway in-memory default. Nothing else\n // surfaces that: the write succeeds, and the identity silently changes on every restart, which\n // Studio rejects with a 403 on the next session create.\n if (!hasInstalledStorage) {\n console.warn(\n styleText('yellow', 'Deriving the machine token before a storage driver was installed'),\n 'call setStorage() first, or set KUBB_AGENT_SECRET, to keep a stable machine identity across restarts',\n )\n }\n\n const stored = await storage.getItem('machine-secret').catch(() => null)\n\n if (typeof stored === 'string' && stored) {\n return stored\n }\n\n const secret = randomBytes(32).toString('hex')\n\n await storage.setItem('machine-secret', secret).catch(() => {\n console.warn(\n styleText('yellow', 'Could not persist the generated machine secret'),\n 'set KUBB_AGENT_SECRET to keep a stable machine identity across restarts',\n )\n })\n\n return secret\n}\n\n/**\n * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its\n * own identity without duplicating `getMachineToken`'s SHA-256 step.\n */\nexport function machineTokenFrom(secret: string): string {\n return hash('sha256', secret)\n}\n\n/**\n * Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.\n * Falls back to a generated secret persisted in the runtime storage if the env var is not set.\n * The token is hashed with SHA-256.\n */\nexport async function getMachineToken(): Promise<string> {\n if (process.env.KUBB_AGENT_SECRET) {\n return machineTokenFrom(process.env.KUBB_AGENT_SECRET)\n }\n\n fallbackSecretPromise ??= loadOrCreateFallbackSecret()\n\n return machineTokenFrom(await fallbackSecretPromise)\n}\n","import { styleText } from 'node:util'\nimport { getErrorMessage } from '@internals/utils'\nimport { logLevel as logLevelMap } from '@kubb/core'\nimport { FetchError, ofetch } from 'ofetch'\nimport type { AgentConnectResponse } from './protocol/index.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * Reads a human-readable message from a Studio JSON error body, when it has one. `FetchError`'s own\n * message stops at the status line, so the detail Studio sends with a failure (an agent limit, a\n * revoked token) would otherwise never reach the user.\n */\nfunction responseMessage(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') {\n return undefined\n }\n\n const body = data as { error_description?: unknown; message?: unknown; error?: unknown }\n for (const value of [body.error_description, body.message, body.error]) {\n if (typeof value === 'string' && value) {\n return value\n }\n }\n\n return undefined\n}\n\n/**\n * Retries after the first registration attempt, each backing off twice as far as the last.\n */\nconst REGISTER_RETRIES = 3\n\n/**\n * Shared in-flight registration so concurrent pool sessions trigger one purge, not N.\n */\nlet registrationInFlight: Promise<boolean> | null = null\n\ntype ConnectProps = {\n studioUrl: string\n token: string\n}\n\n/**\n * Thrown when Studio rejects the agent token itself (401). Retrying cannot help: the token was\n * revoked, or the agent it belonged to was deleted in the Studio UI. Hosts catch this to forget\n * the stored credential and pair again.\n */\nexport class InvalidAgentTokenError extends Error {\n constructor(studioUrl: string, options?: ErrorOptions) {\n super(`Kubb Studio rejected this agent's token. It was revoked or the agent was deleted in ${studioUrl}.`, options)\n this.name = 'InvalidAgentTokenError'\n }\n}\n\n/**\n * Whether a thrown value carries `statusCode`. Not narrowed to `FetchError`: a host wrapper can\n * throw its own error shape with the same field.\n *\n * A 401 means the agent token itself was rejected. A 403 from the session create endpoint means\n * the machine token stored in Studio no longer matches this agent (missing or mismatched).\n */\nfunction rejectedWith(error: unknown, statusCode: number): boolean {\n return (error as { statusCode?: number } | undefined)?.statusCode === statusCode\n}\n\nfunction sessionError(cause: unknown): Error {\n const detail = (cause instanceof FetchError ? responseMessage(cause.data) : undefined) ?? getErrorMessage(cause)\n return new Error(detail ? `Failed to get agent session from Kubb Studio: ${detail}` : 'Failed to get agent session from Kubb Studio', { cause })\n}\n\n/**\n * Performs the raw session create request against Studio.\n */\nasync function requestAgentSession({ token, studioUrl }: ConnectProps): Promise<AgentConnectResponse> {\n const url = `${studioUrl}/api/agent/sessions`\n\n const data = await ofetch<AgentConnectResponse>(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: { machineToken: await getMachineToken() },\n })\n\n if (!data) {\n throw new Error('No data available for agent session')\n }\n\n return data\n}\n\n/**\n * Obtain an agent session token from Kubb Studio via HTTP.\n *\n * When Studio rejects the machine token (403), for example after the agent restarted\n * with a new identity while the startup registration call failed, the agent re-registers\n * and retries once, so a single failed registration can't permanently block session creation.\n */\nexport async function createAgentSession({ token, studioUrl }: ConnectProps): Promise<AgentConnectResponse> {\n try {\n return await requestAgentSession({ token, studioUrl })\n } catch (error: unknown) {\n if (rejectedWith(error, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: error })\n }\n\n if (!rejectedWith(error, 403) || !(await registerAgent({ token, studioUrl }))) {\n throw sessionError(error)\n }\n\n try {\n return await requestAgentSession({ token, studioUrl })\n } catch (retryError: unknown) {\n if (rejectedWith(retryError, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: retryError })\n }\n\n throw sessionError(retryError)\n }\n }\n}\n\ntype RegisterProps = {\n studioUrl: string\n token: string\n poolSize?: number\n}\n\n/**\n * Register this agent with Kubb Studio by sending the machine ID.\n * Called on agent startup before creating a WebSocket session, and again when\n * Studio rejects the machine token during session creation.\n *\n * Retries with backoff because a failed registration leaves Studio with a stale\n * machine token that blocks every subsequent session create call. Registration\n * purges all of the agent's sessions on the Studio side, so concurrent callers\n * (multiple pool sessions hitting a 403 at once) share one in-flight run instead\n * of purging each other's fresh sessions.\n */\nexport function registerAgent(props: RegisterProps): Promise<boolean> {\n registrationInFlight ??= runRegistration(props).finally(() => {\n registrationInFlight = null\n })\n\n return registrationInFlight\n}\n\nasync function runRegistration({ token, studioUrl, poolSize }: RegisterProps): Promise<boolean> {\n const machineToken = await getMachineToken()\n\n try {\n await ofetch(`${studioUrl}/api/agent/connect`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n body: { machineToken, poolSize },\n retry: REGISTER_RETRIES,\n // 2s, 4s, then 8s. `retry` counts down, so the first retry is the one with the most left.\n retryDelay: ({ options }) => 2_000 * 2 ** (REGISTER_RETRIES - Number(options.retry)),\n })\n\n return true\n } catch (error) {\n if (rejectedWith(error, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: error })\n }\n\n console.error(styleText('red', `Failed to register agent with Studio after ${REGISTER_RETRIES + 1} attempts`))\n\n return false\n }\n}\n\ntype DisconnectProps = {\n studioUrl: string\n token: string\n sessionId: string\n slug?: string | null\n /**\n * Threshold for this function's own console lines, using the numeric constants `@kubb/core`\n * exports as `logLevel`. Left out, nothing prints, the same silent default `StudioSessionOptions`\n * gives a host that never set one.\n */\n logLevel?: number\n}\n\n/**\n * Notify Kubb Studio that this agent is disconnecting.\n * Called on process termination or server close. A failed notify is logged and swallowed: the\n * local socket is already gone, and failing teardown must not block shutdown or reconnect.\n */\nexport async function disconnect({ sessionId, token, studioUrl, slug, logLevel }: DisconnectProps): Promise<void> {\n const url = `${studioUrl}/api/agent/sessions/${sessionId}/disconnect`\n const tag = slug ?? 'agent'\n const canLog = logLevel !== undefined && logLevel > logLevelMap.silent\n\n try {\n await ofetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n })\n // console.error, not console.log: a CI runner only forwards a child process's stderr live, so\n // a stdout write here would be silently buffered away instead of reaching its log.\n if (canLog) {\n console.error(styleText('green', `[${tag}] Disconnected from Studio`))\n }\n } catch (error) {\n const statusCode = (error as { statusCode?: number } | undefined)?.statusCode\n if (statusCode !== undefined && statusCode >= 400 && statusCode < 500) return\n\n if (canLog) {\n console.warn(styleText('yellow', `[${tag}] Failed to notify Studio of disconnection: ${getErrorMessage(error)}`))\n }\n }\n}\n\n/**\n * Status values returned by Studio's jobs API.\n */\nexport type StudioJobStatus = 'queued' | 'running' | 'success' | 'failed' | 'canceled'\n\n/**\n * Package view returned on a successful snapshot job from Studio.\n */\nexport type StudioSnapshot = {\n /**\n * Immutable snapshot id.\n */\n id: string\n /**\n * npm package name, or `null` when Studio stored none.\n */\n name: string | null\n /**\n * npm package version, or `null` when Studio stored none.\n */\n version: string | null\n /**\n * Subresource integrity hash for the tarball, or `null` when unavailable.\n */\n integrity: string | null\n /**\n * Preferable download path, often the readable `/packages/{agentSlug}/{name}.tgz` form.\n */\n url: string\n /**\n * Stable download path keyed by snapshot id.\n */\n snapshotIdUrl: string\n /**\n * ISO timestamp after which Studio may delete the tarball.\n */\n expiresAt: string\n}\n\n/**\n * Job record from `POST /api/jobs` and `GET /api/jobs/{id}`.\n */\nexport type StudioJob = {\n /**\n * Job id returned by Studio when the job was queued.\n */\n id: string\n /**\n * Current status. Poll until `success`, `failed`, or `canceled`.\n */\n status: StudioJobStatus\n /**\n * Failure message when `status` is `failed`.\n */\n error?: string\n /**\n * Package view when a snapshot job finished successfully.\n */\n snapshot?: StudioSnapshot\n}\n\n/**\n * Queues a generation or snapshot job on Studio (`POST /api/jobs`).\n *\n * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.\n * Authenticates with the organization CI API key via `x-api-key`.\n *\n * @example Snapshot job\n * ```ts\n * const job = await createJob({\n * studioUrl: 'https://kubb.studio',\n * token: process.env.KUBB_TOKEN!,\n * type: 'snapshot',\n * agentId: agent.id,\n * name: '@kubb/demo',\n * version: '1.0.0',\n * })\n * const finished = await waitForJob({ studioUrl, token, id: job.id })\n * ```\n */\nexport async function createJob({\n studioUrl,\n token,\n type,\n agentId,\n name,\n version,\n config,\n}: {\n studioUrl: string\n token: string\n type: 'generation' | 'snapshot'\n agentId: string\n name?: string\n version?: string\n config?: Record<string, unknown>\n}): Promise<StudioJob> {\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { type, agentId, name, version, config },\n })\n\n return job\n}\n\n/**\n * A job runs a generation and packs a tarball, so it is never done the instant it is queued.\n */\nconst INITIAL_POLL_DELAY_MS = 2_000\n\n/**\n * Slowest the poll backs off to. Requests per run are roughly `timeoutMs` divided by this, and\n * every concurrent run on the same organization key draws on one budget.\n */\nconst MAX_POLL_INTERVAL_MS = 30_000\n\n/**\n * Polls `GET /api/jobs/{id}` until the job reaches a terminal status, waiting\n * {@link INITIAL_POLL_DELAY_MS} first and doubling up to {@link MAX_POLL_INTERVAL_MS} so a long\n * job stays inside the API key's rate limit.\n *\n * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the\n * deadline passes before Studio finishes.\n */\nexport async function waitForJob({\n studioUrl,\n token,\n id,\n timeoutMs = 60_000,\n}: {\n studioUrl: string\n token: string\n id: string\n /**\n * How long to keep polling before throwing, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = INITIAL_POLL_DELAY_MS\n\n for (;;) {\n await new Promise((resolve) => setTimeout(resolve, Math.max(Math.min(interval, deadline - Date.now()), 0)))\n\n if (Date.now() >= deadline) throw new Error('Timed out waiting for the Studio job')\n\n interval = Math.min(interval * 2, MAX_POLL_INTERVAL_MS)\n\n try {\n // ofetch retries a 429 immediately, which spends the rate limit faster than not retrying.\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs/${id}`, {\n headers: { 'x-api-key': token },\n retry: false,\n })\n\n if (job.status === 'success' || job.status === 'failed' || job.status === 'canceled') return job\n } catch (error) {\n const response = (error as { response?: { status?: number; _data?: { data?: { tryAgainIn?: unknown } } } }).response\n\n if (response?.status !== 429) throw error\n\n const retryAfter = response._data?.data?.tryAgainIn\n const usable = typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter > 0\n\n // Studio's wait may exceed the ceiling, and a refusal must never shorten the next poll.\n interval = Math.max(interval, usable ? retryAfter : MAX_POLL_INTERVAL_MS)\n }\n }\n}\n\n/**\n * CI agent returned by {@link createAgent}. The token is issued only once, at creation or reuse.\n */\nexport type StudioAgent = {\n /**\n * Agent id, passed to {@link createJob} as `agentId`.\n */\n id: string\n /**\n * Human-readable slug, used to build the readable snapshot URL and the agent's Studio page.\n */\n slug: string\n /**\n * Agent display name.\n */\n name: string\n /**\n * Bearer token for the WebSocket agent session. Mask it before logging.\n */\n token: string\n}\n\n/**\n * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.\n * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of\n * consuming a new one from the organization's agent limit.\n */\nexport async function createAgent({\n studioUrl,\n token,\n name,\n machineToken,\n}: {\n studioUrl: string\n token: string\n name: string\n machineToken: string\n}): Promise<StudioAgent> {\n try {\n return await ofetch<StudioAgent>(`${studioUrl}/api/agents`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { name, machineToken },\n })\n } catch (error: unknown) {\n if (error instanceof FetchError) {\n const upgradeUrl = (error.data as { data?: { upgradeUrl?: string } } | undefined)?.data?.upgradeUrl\n const detail = responseMessage(error.data) ?? getErrorMessage(error)\n const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : ''\n throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error })\n }\n\n throw error\n }\n}\n","","import type { Hookable, KubbHooks } from '@kubb/core'\nimport { x } from 'tinyexec'\n\n/**\n * Events a host emits about its Kubb Studio session, as opposed to a generation. `kubb:` stays\n * reserved for generation lifecycle.\n */\nexport type StudioConnectingContext = {\n /**\n * The Studio instance this session is opening against.\n */\n url: string\n}\n\nexport type StudioConnectedContext = {\n /**\n * The Studio instance this session attached to.\n */\n url: string\n /**\n * Both sides of the connection, so a host can print them and make a mismatch visible.\n */\n versions: {\n /**\n * The Studio instance's own version, when it sent one.\n */\n studio?: string\n /**\n * The version of the runtime that connected.\n */\n kubb: string\n /**\n * The version of the host itself, such as the `kubb` CLI or the agent image.\n */\n agent: string\n }\n /**\n * This agent's slug, refreshed on every connect so a rename in Studio shows up without a\n * re-pair. Absent when Studio predates the field.\n */\n agentSlug?: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none, or when\n * Studio predates the field.\n */\n organizationSlug?: string\n}\n\n/**\n * Fired once Studio confirms the `agent:connect` handshake was received and the session is fully\n * registered. Distinct from `studio:connected`, which only means the socket is open.\n */\nexport type StudioReadyContext = Record<string, never>\n\nexport type StudioDisconnectedContext = {\n /**\n * Why Studio ended the session.\n */\n reason: string\n}\n\nexport type StudioCommandStartContext = {\n /**\n * The command Studio sent, without its `studio:` prefix: `generate`, `connect` or `save`.\n */\n command: string\n}\n\nexport type StudioCommandEndContext = {\n /**\n * The command that finished, without its `studio:` prefix.\n */\n command: string\n /**\n * What the command did, when there is something to report: `applied 2/3 edits to kubb.config.ts`.\n */\n info?: string\n}\n\nexport type StudioWarnContext = {\n /**\n * What was refused or ignored, and what would change it.\n */\n message: string\n}\n\nexport type StudioErrorContext = {\n /**\n * The failure, for the host's own output. One Studio needs to hear about goes over the socket\n * through the `kubb:error` generation hook instead.\n */\n error: Error\n}\n\ndeclare global {\n namespace Kubb {\n interface KubbHooksRegistry {\n 'studio:connecting': [ctx: StudioConnectingContext]\n 'studio:connected': [ctx: StudioConnectedContext]\n 'studio:ready': [ctx: StudioReadyContext]\n 'studio:disconnected': [ctx: StudioDisconnectedContext]\n 'studio:command:start': [ctx: StudioCommandStartContext]\n 'studio:command:end': [ctx: StudioCommandEndContext]\n 'studio:warn': [ctx: StudioWarnContext]\n 'studio:error': [ctx: StudioErrorContext]\n }\n }\n}\n\n/**\n * Register a `kubb:hook:start` listener that spawns the requested command via tinyexec,\n * streams each stdout line as a `kubb:hook:line` event, and calls `kubb:hook:end` with the result.\n * Streaming the output lets Kubb Studio render live hook progress over the WebSocket connection.\n *\n * Returns a remover, so a session that runs one generation after another on the same emitter does\n * not stack a listener per run.\n */\nexport function setupHookListener(hooks: Hookable<KubbHooks>, root: string, signal?: AbortSignal): () => void {\n return hooks.hook('kubb:hook:start', async (ctx) => {\n const { id, command, args } = ctx\n // No id means nothing is waiting on the result (benchmarks, tests).\n if (!id) {\n return\n }\n\n const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command\n\n try {\n const proc = x(command, [...(args ?? [])], {\n signal,\n nodeOptions: { cwd: root, detached: true },\n })\n\n for await (const line of proc) {\n await hooks.callHook('kubb:hook:line', { id, line })\n }\n\n const { exitCode } = await proc\n\n if (exitCode !== 0) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n await hooks.callHook('kubb:error', { error })\n\n return\n }\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: true, error: null })\n } catch (caughtError) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n error.cause = caughtError\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n await hooks.callHook('kubb:error', { error })\n }\n })\n}\n\n/**\n * Waits for the `kubb:hook:end` matching `hookId`. Register this before calling `kubb:hook:start`:\n * `callHook` awaits its listeners, and {@link setupHookListener} calls `kubb:hook:end` from inside\n * that same listener, so a handler added afterward would already have missed it.\n */\nexport function waitForHookEnd(hooks: Hookable<KubbHooks>, hookId: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const handleHookEnd = (ctx: { id?: string; success: boolean; error?: Error | null }) => {\n if (ctx.id !== hookId) return\n hooks.removeHook('kubb:hook:end', handleHookEnd)\n\n if (ctx.success) {\n resolve()\n return\n }\n reject(ctx.error)\n }\n\n hooks.hook('kubb:hook:end', handleHookEnd)\n })\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { pathToFileURL } from 'node:url'\nimport type { Adapter, Plugin } from '@kubb/core'\nimport { camelCase } from '@internals/utils'\nimport { mergeDeep } from 'remeda'\nimport type { JSONKubbConfig } from './protocol/index.ts'\n\n/**\n * Turns the JSON config Studio sends back into live Kubb objects.\n *\n * A plugin or adapter instance carries closures (`parse`, `getImports`, ...) that cannot survive\n * JSON, so both sides pass options over the wire and the factory is re-invoked here with the merged\n * result. Only `@kubb/plugin-*` packages are resolved this way, so the reinstantiated factory is\n * always one Kubb ships, never an arbitrary module the payload names.\n */\n\ntype PluginFactory = (options: unknown) => Plugin\n\n/**\n * Imports a package, falling back to how the user's project would resolve it.\n *\n * `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,\n * `npm i -g`) only sees its own `node_modules` and misses the plugins installed next to the user's\n * config. The retry resolves from `process.cwd()` instead.\n */\nasync function importFromProject(packageName: string): Promise<Record<string, unknown>> {\n try {\n return await import(packageName)\n } catch {\n const require = createRequire(pathToFileURL(`${process.cwd()}/`))\n // `require.resolve` picks the package's `require` condition, so prefer the ESM build sitting\n // next to it. Loading the CJS copy would pull in a second `@kubb/core` instance.\n const resolved = require.resolve(packageName)\n const esm = resolved.replace(/\\.cjs$/, '.js')\n\n return await import(pathToFileURL(esm !== resolved && existsSync(esm) ? esm : resolved).href)\n }\n}\n\n/**\n * Strips the `@kubb/` scope from a plugin package name, matching the `name` convention Kubb\n * plugin factories use internally.\n *\n * @example\n * ```ts\n * toPluginName('@kubb/plugin-ts') // 'plugin-ts'\n * ```\n */\nfunction toPluginName(packageName: string): string {\n return packageName.split('/').pop() ?? packageName\n}\n\n/**\n * Adds the `@kubb/` scope a plugin's package carries but its `name` does not, the inverse of\n * {@link toPluginName}. A name outside the `plugin-` convention is left alone, so a third-party\n * plugin is not reported as one of Kubb's.\n *\n * @example\n * ```ts\n * toPackageName('plugin-ts') // '@kubb/plugin-ts'\n * toPackageName('@acme/my-plugin') // '@acme/my-plugin'\n * ```\n */\nexport function toPackageName(name: string): string {\n return name.startsWith('plugin-') ? `@kubb/${name}` : name\n}\n\n/**\n * Derives the conventional named export for a `@kubb/*` plugin package from its package name.\n *\n * @example\n * ```ts\n * toExportName('@kubb/plugin-react-query') // 'pluginReactQuery'\n * toExportName('@kubb/plugin-ts') // 'pluginTs'\n * ```\n */\nexport function toExportName(packageName: string): string {\n return camelCase(toPluginName(packageName))\n}\n\n/**\n * A `@kubb/plugin-*` package specifier. Nothing else may reach `import()`: only Kubb's own\n * plugins are supported, so a payload naming anything else, a third-party package or a path, is\n * refused before it can execute.\n */\nconst KUBB_PLUGIN_SPECIFIER = /^@kubb\\/plugin-[\\w.-]+$/\n\n/**\n * Whether `name` is a `@kubb/plugin-*` specifier. Exported so `configFile.ts` can refuse the same\n * shape before printing a Studio-supplied plugin name into the config file's source text.\n */\nexport function isKubbPluginSpecifier(name: string): boolean {\n return KUBB_PLUGIN_SPECIFIER.test(name)\n}\n\n/**\n * Dynamically imports a `@kubb/plugin-*` package and returns its factory function.\n *\n * Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`\n * build ARG, no runtime installation is possible in the distroless container.\n *\n * Resolution order: the camelCase named export the package name implies (e.g. `pluginTs`), then\n * the default export.\n *\n * @throws if the package cannot be imported or exports no callable factory.\n */\nasync function loadPluginFactory(packageName: string): Promise<PluginFactory> {\n if (!isKubbPluginSpecifier(packageName)) {\n throw new Error(`Plugin \"${packageName}\" is not a @kubb/plugin-* package. Kubb Studio only supports Kubb's own plugins.`)\n }\n\n let mod: Record<string, unknown>\n try {\n mod = await importFromProject(packageName)\n } catch (cause) {\n throw new Error(`Plugin \"${packageName}\" could not be loaded. Make sure it is installed: \\`npm install ${packageName}\\``, { cause })\n }\n\n const exportName = toExportName(packageName)\n\n if (typeof mod[exportName] === 'function') return mod[exportName] as PluginFactory\n\n if (typeof mod['default'] === 'function') return mod['default'] as PluginFactory\n\n throw new Error(`Plugin \"${packageName}\" does not export a callable factory. Tried the named export \"${exportName}\" and \"default\".`)\n}\n\n/**\n * Resolves each plugin entry by dynamically importing the `@kubb/plugin-*` package and\n * calling its factory with the provided options.\n *\n * Packages must be pre-installed in the Docker image at build time, use the `KUBB_PACKAGES`\n * build ARG to control which ones are available at runtime.\n *\n * @example\n * ```ts\n * { name: '@kubb/plugin-react-query', options: { output: { path: './hooks' } } }\n * { name: '@kubb/plugin-ts', options: { output: { path: './types' } } }\n * ```\n */\nexport async function resolvePlugins(plugins: NonNullable<JSONKubbConfig['plugins']>): Promise<Array<Plugin>> {\n return Promise.all(\n plugins.map(async ({ name, options }) => {\n const factory = await loadPluginFactory(name)\n return factory(options ?? {}) as Plugin\n }),\n )\n}\n\n/**\n * Merges studio plugin options with disk config plugins.\n * Studio takes priority: options from studio win over disk, and a plugin Studio explicitly\n * disabled is dropped even when the disk config still lists it. Disk plugins without a studio\n * counterpart are kept as-is. Studio plugins not present on disk are appended.\n *\n * For plugins present in both configs, the plugin is re-instantiated with merged options\n * so that all internal closures correctly reference the merged values.\n */\nexport async function mergePlugins(\n diskPlugins: Array<Plugin> | undefined,\n studioPlugins: JSONKubbConfig['plugins'] | undefined,\n): Promise<Array<Plugin> | undefined> {\n // Matched on the package's base name rather than by instantiating first. Every Kubb plugin\n // factory returns exactly that (`@kubb/plugin-ts` → `plugin-ts`), enforced by the `satisfies` on\n // each factory's name.\n const disabledNames = new Set((studioPlugins ?? []).filter((entry) => entry.disabled).map((entry) => toPluginName(entry.name)))\n const activeDiskPlugins = disabledNames.size ? diskPlugins?.filter((plugin) => !disabledNames.has(plugin.name)) : diskPlugins\n const activeStudioPlugins = studioPlugins?.filter((entry) => !entry.disabled)\n\n if (!activeDiskPlugins && !activeStudioPlugins?.length) return undefined\n if (!activeStudioPlugins?.length) return activeDiskPlugins\n\n if (!activeDiskPlugins) return resolvePlugins(activeStudioPlugins)\n\n const studioEntryByName = new Map(activeStudioPlugins.map((entry) => [toPluginName(entry.name), entry] as const))\n const diskNames = new Set(activeDiskPlugins.map((plugin) => plugin.name))\n\n // Each plugin is instantiated once, with its final options. Resolving the whole payload first\n // just to read the names would build every overlapping plugin twice and discard the first.\n const merged = await Promise.all(\n activeDiskPlugins.map(async (diskPlugin) => {\n const studioEntry = studioEntryByName.get(diskPlugin.name)\n if (!studioEntry) return diskPlugin\n\n // Disk as base, studio overrides, then re-instantiate so the plugin's closures reference the\n // merged values. A plugin that never sets `options` (e.g. `@kubb/plugin-barrel`) leaves\n // `diskPlugin.options` undefined, which `mergeDeep` can't accept.\n const options = mergeDeep((diskPlugin.options as Record<string, unknown>) ?? {}, (studioEntry.options as Record<string, unknown>) ?? {})\n const [resolved] = await resolvePlugins([{ name: studioEntry.name, options }])\n\n return resolved ?? diskPlugin\n }),\n )\n\n const studioOnly = activeStudioPlugins.filter((entry) => !diskNames.has(toPluginName(entry.name)))\n\n return [...merged, ...(await resolvePlugins(studioOnly))]\n}\n\n/**\n * Merges Studio-provided adapter option overrides into the disk config's adapter.\n *\n * Adapter instances carry live functions (`parse`, `getImports`, ...) that can't survive\n * JSON serialization over the WebSocket, so `studioOptions` is treated as an options patch\n * rather than a replacement adapter. Re-invokes the same `@kubb/adapter-<name>` factory the\n * disk config used, with the merged options, so the resulting instance has fresh closures\n * over the merged values instead of a plain object missing `parse`.\n */\nexport async function mergeAdapter(diskAdapter: Adapter | undefined, studioOptions: object | undefined): Promise<Adapter | undefined> {\n if (!studioOptions || !diskAdapter) {\n return diskAdapter\n }\n\n const packageName = `@kubb/adapter-${diskAdapter.name}`\n const mod = await importFromProject(packageName)\n const factory = mod[toExportName(packageName)]\n\n if (typeof factory !== 'function') {\n return diskAdapter\n }\n\n const mergedOptions = mergeDeep((diskAdapter.options as Record<string, unknown>) ?? {}, studioOptions as Record<string, unknown>)\n\n return factory(mergedOptions) as Adapter\n}\n","import { builders, detectCodeFormat, generateCode, parseModule } from 'magicast'\nimport type { ASTNode, ProxifiedModule } from 'magicast'\nimport type { ConfigEdit, ConfigEditOutcome, ConfigFileView, ConfigRef, ConfigView, OptionValue, PluginView } from './protocol/index.ts'\nimport { isKubbPluginSpecifier, toExportName } from './resolveConfig.ts'\n\n/**\n * A valid JavaScript identifier, so an import name can only ever print as `import { name } from`,\n * never as source that breaks out of the import statement.\n */\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/\n\n/**\n * A config or plugin options object literal in the file.\n */\ntype ObjectNode = Extract<ASTNode, { type: 'ObjectExpression' }>\n\n/**\n * A `pluginX(...)` call in a config's `plugins` array.\n */\ntype CallNode = Extract<ASTNode, { type: 'CallExpression' }>\n\n/**\n * A `key: value` entry of an object literal.\n */\ntype ObjectPropertyNode = Extract<ASTNode, { type: 'ObjectProperty' }>\n\n/**\n * `key: value` as an object literal property, in the file's quote and key style.\n *\n * Uses magicast's literal builder for the key/value nodes, then wraps them as a Babel\n * `ObjectProperty`, the type the rest of this file reads.\n */\nfunction literalProperty({ key, value }: { key: string; value: OptionValue }): ObjectPropertyNode {\n const built = (builders.literal({ [key]: value }) as unknown as ObjectNode).properties[0] as unknown as ObjectPropertyNode\n return { type: 'ObjectProperty', key: built.key, value: built.value, computed: false, shorthand: false }\n}\n\n/**\n * What `applyConfigEdits` did to a config file.\n */\ntype ApplyResult = {\n /**\n * The file's text after every applicable edit, unchanged from the input when none applied.\n */\n source: string\n /**\n * One entry per edit, in the order they were given.\n */\n outcomes: Array<ConfigEditOutcome>\n /**\n * Whether `source` differs from the input.\n */\n changed: boolean\n}\n\n/**\n * Marks the comment block a `disable-plugin` leaves behind, so `enable-plugin` can find its way\n * back to the exact lines it commented out. Carries the block's line count, so `enable-plugin`\n * restores exactly those lines instead of scanning forward through whatever comments follow.\n */\nconst DISABLED_MARKER = 'kubb:disabled'\n\n/**\n * The one line `disable-plugin` writes above the comment block it produces for `plugin`.\n */\nfunction formatMarker(plugin: string, lineCount: number, indent = ''): string {\n return `${indent}// ${DISABLED_MARKER} ${plugin} ${lineCount}`\n}\n\n/**\n * The plugin and comment-block length a marker line names, when `line` is one.\n */\nfunction parseMarker(line: string): { plugin: string; lineCount: number } | undefined {\n const trimmed = line.trim()\n if (!trimmed.startsWith(`// ${DISABLED_MARKER} `)) {\n return undefined\n }\n\n const match = trimmed.slice(`// ${DISABLED_MARKER} `.length).match(/^(.+)\\s+(\\d+)$/)\n return match ? { plugin: match[1]!, lineCount: Number(match[2]) } : undefined\n}\n\n/**\n * Steps through a config's wrappers to the object literal underneath: a `satisfies`/`as`\n * assertion, a `() => ...` factory, or a factory whose block body returns the config.\n */\nfunction unwrap(node: ASTNode | null | undefined): ASTNode | undefined {\n if (!node) {\n return undefined\n }\n if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') {\n return unwrap(node.expression)\n }\n if (node.type !== 'ArrowFunctionExpression' && node.type !== 'FunctionExpression') {\n return node\n }\n if (node.body.type !== 'BlockStatement') {\n return unwrap(node.body)\n }\n\n const returned = node.body.body.find((statement): statement is Extract<ASTNode, { type: 'ReturnStatement' }> => statement.type === 'ReturnStatement')\n return unwrap(returned?.argument)\n}\n\n/**\n * Every config object in `export default defineConfig(...)`, or why the file is unmanaged.\n *\n * An array export gets one entry per element, matching {@link ConfigRef}'s numeric index.\n *\n * Walks the parsed AST rather than magicast's proxies, which throw on node types they cannot\n * cast, most of what an unmanaged config file is made of.\n */\nfunction findConfigs(mod: ProxifiedModule): { configs: Array<ObjectNode> } | { reason: string } {\n const body = mod.$ast.type === 'Program' ? mod.$ast.body : []\n const declaration = body.find((node): node is Extract<ASTNode, { type: 'ExportDefaultDeclaration' }> => node.type === 'ExportDefaultDeclaration')\n\n const exported = unwrap(declaration?.declaration)\n if (!exported) {\n return { reason: 'no default export found' }\n }\n if (exported.type !== 'CallExpression' || exported.callee.type !== 'Identifier' || exported.callee.name !== 'defineConfig') {\n return { reason: 'default export is not a defineConfig(...) call' }\n }\n\n const argument = unwrap(exported.arguments[0])\n if (!argument) {\n return { reason: 'defineConfig(...) was called without a config' }\n }\n if (argument.type === 'ArrayExpression') {\n const configs: Array<ObjectNode> = []\n\n for (const element of argument.elements) {\n const entry = unwrap(element)\n if (entry?.type !== 'ObjectExpression') {\n return { reason: 'config is not an object literal' }\n }\n configs.push(entry)\n }\n return { configs }\n }\n if (argument.type !== 'ObjectExpression') {\n return { reason: 'config is not an object literal' }\n }\n return { configs: [argument] }\n}\n\n/**\n * The config entry an edit names, defaulting to the first when it names none.\n */\nfunction selectConfig(configs: Array<ObjectNode>, ref: ConfigRef | undefined): ObjectNode | undefined {\n if (ref === undefined) {\n return configs[0]\n }\n if (typeof ref === 'number') {\n return configs[ref]\n }\n return configs.find((config) => configName(config) === ref)\n}\n\nfunction configName(config: ObjectNode): string | undefined {\n const name = property(config, 'name')\n return name?.type === 'StringLiteral' ? name.value : undefined\n}\n\n/**\n * The name of an object literal property, for the two key shapes a config uses: `key: value` and\n * `'key': value`. `undefined` for a computed key, which the patcher never touches.\n */\nfunction propertyKey(entry: Extract<ASTNode, { type: 'ObjectProperty' }>): string | undefined {\n if (entry.key.type === 'Identifier') {\n return entry.key.name\n }\n if (entry.key.type === 'StringLiteral') {\n return entry.key.value\n }\n return undefined\n}\n\n/**\n * The index of an object literal's own property named `key`, `-1` when it has none.\n */\nfunction entryIndex({ node, key }: { node: ObjectNode; key: string }): number {\n return node.properties.findIndex((entry) => entry.type === 'ObjectProperty' && propertyKey(entry) === key)\n}\n\n/**\n * The value node of an object literal's own property.\n */\nfunction property(node: ObjectNode, key: string): ASTNode | undefined {\n const index = entryIndex({ node, key })\n return index === -1 ? undefined : (node.properties[index] as ObjectPropertyNode).value\n}\n\n/**\n * Writes `key: value` on an object literal, replacing the value when the property is already there.\n *\n * An existing property has its value swapped in place rather than being replaced whole, so recast\n * reprints only that value and leaves the object's own layout alone.\n */\nfunction setProperty({ node, key, value }: { node: ObjectNode; key: string; value: OptionValue }): void {\n const entry = literalProperty({ key, value })\n const index = entryIndex({ node, key })\n\n if (index === -1) {\n node.properties.push(entry)\n return\n }\n ;(node.properties[index] as ObjectPropertyNode).value = entry.value\n}\n\n/**\n * Drops `key` from an object literal.\n */\nfunction removeProperty({ node, key }: { node: ObjectNode; key: string }): void {\n const index = entryIndex({ node, key })\n if (index !== -1) {\n node.properties.splice(index, 1)\n }\n}\n\n/**\n * Reads a literal node's value: a primitive, or an object/array built only from primitives.\n * `undefined` for anything else, so a caller can use this both to read a value and to check\n * whether a node is a literal at all.\n */\nfunction readLiteral(node: ASTNode | undefined): OptionValue | undefined {\n if (!node) {\n return undefined\n }\n if (node.type === 'StringLiteral' || node.type === 'NumericLiteral' || node.type === 'BooleanLiteral') {\n return node.value\n }\n if (node.type === 'NullLiteral') {\n return null\n }\n if (node.type === 'TemplateLiteral') {\n return node.expressions.length === 0 ? (node.quasis[0]?.value.cooked ?? '') : undefined\n }\n if (node.type === 'UnaryExpression') {\n const value = readLiteral(node.argument)\n if (typeof value !== 'number') {\n return undefined\n }\n if (node.operator === '-') {\n return -value\n }\n if (node.operator === '+') {\n return value\n }\n return undefined\n }\n if (node.type === 'ArrayExpression') {\n const values = node.elements.map((element) => (element ? readLiteral(element) : undefined))\n return values.every((value) => value !== undefined) ? values : undefined\n }\n if (node.type === 'ObjectExpression') {\n const entries: Record<string, OptionValue> = {}\n for (const entry of node.properties) {\n if (entry.type !== 'ObjectProperty') {\n return undefined\n }\n const key = propertyKey(entry)\n const value = readLiteral(entry.value)\n if (key === undefined || value === undefined) {\n return undefined\n }\n entries[key] = value\n }\n return entries\n }\n return undefined\n}\n\n/**\n * Maps a factory identifier in the file back to the module it was imported from.\n */\nfunction importedFrom(mod: ProxifiedModule): Map<string, string> {\n return new Map(mod.imports.$items.map((item) => [item.local, item.from]))\n}\n\n/**\n * Every `pluginX(...)` element of a config's plugins array that resolves to an import.\n */\nfunction pluginCalls(mod: ProxifiedModule, config: ObjectNode): Array<{ importName: string; packageName: string; call: CallNode }> {\n const plugins = property(config, 'plugins')\n if (plugins?.type !== 'ArrayExpression') {\n return []\n }\n\n const imports = importedFrom(mod)\n\n return plugins.elements.flatMap((element) => {\n if (element?.type !== 'CallExpression' || element.callee.type !== 'Identifier') {\n return []\n }\n const packageName = imports.get(element.callee.name)\n return packageName ? [{ importName: element.callee.name, packageName, call: element }] : []\n })\n}\n\n/**\n * Plugins a previous `disable-plugin` commented out of this config, keyed by package name.\n *\n * Read from the marker lines rather than the AST, since a commented-out call is no longer a node.\n */\nfunction disabledMarkers(source: string): Array<{ packageName: string; line: number }> {\n return source.split('\\n').flatMap((line, index) => {\n const marker = parseMarker(line)\n return marker ? [{ packageName: marker.plugin, line: index + 1 }] : []\n })\n}\n\n/**\n * Reads which plugins the file declares and which of their options Studio may write.\n *\n * @example\n * ```ts\n * const view = readConfig(await readFile('kubb.config.ts', 'utf8'))\n * if (view.managed) {\n * view.configs.forEach((config) => console.log(config.name, config.plugins.length))\n * }\n * ```\n */\nexport function readConfig(source: string): ConfigFileView {\n let mod: ProxifiedModule\n try {\n mod = parseModule(source)\n } catch {\n return { managed: false, reason: 'the config file could not be parsed' }\n }\n\n const found = findConfigs(mod)\n if ('reason' in found) {\n return { managed: false, reason: found.reason }\n }\n\n const importNames = new Map([...importedFrom(mod)].map(([local, from]) => [from, local]))\n const disabled = disabledMarkers(source)\n\n return {\n managed: true,\n configs: found.configs.map((config): ConfigView => {\n const plugins = pluginCalls(mod, config).map(({ importName, packageName, call }): PluginView => {\n const entries: PluginView['options'] = {}\n const options = call.arguments[0]\n\n if (options?.type === 'ObjectExpression') {\n for (const entry of options.properties) {\n if (entry.type !== 'ObjectProperty') {\n continue\n }\n const key = propertyKey(entry)\n if (key === undefined) {\n continue\n }\n const value = readLiteral(entry.value)\n entries[key] = value === undefined ? { literal: false } : { literal: true, value }\n }\n }\n return { importName, packageName, options: entries }\n })\n\n const start = config.loc?.start.line ?? 0\n const end = config.loc?.end.line ?? Number.POSITIVE_INFINITY\n\n for (const { packageName } of disabled.filter((entry) => entry.line >= start && entry.line <= end)) {\n plugins.push({\n importName: importNames.get(packageName) ?? toExportName(packageName),\n packageName,\n options: {},\n disabled: true,\n })\n }\n\n return { name: configName(config), plugins }\n }),\n }\n}\n\n/**\n * Whether a value can be written into a config file as a literal.\n *\n * This is the trust boundary for edits that arrive over the agent WebSocket: a function, `undefined`,\n * or a non-finite number is refused rather than printed into the user's source.\n */\nexport function isOptionValue(value: unknown): value is OptionValue {\n if (value === null) {\n return true\n }\n if (typeof value === 'string' || typeof value === 'boolean') {\n return true\n }\n if (typeof value === 'number') {\n return Number.isFinite(value)\n }\n if (Array.isArray(value)) {\n return value.every(isOptionValue)\n }\n if (typeof value === 'object') {\n return Object.values(value).every(isOptionValue)\n }\n return false\n}\n\n/**\n * The options object of a plugin call, when it was called with one.\n */\nfunction getOptions(call: CallNode): ObjectNode | undefined {\n const options = call.arguments[0]\n return options?.type === 'ObjectExpression' ? options : undefined\n}\n\n/**\n * The options object of a plugin call, creating an empty one when the plugin was called bare.\n */\nfunction ensureOptions(call: CallNode): ObjectNode | undefined {\n if (call.arguments.length === 0) {\n call.arguments.push({ type: 'ObjectExpression', properties: [] })\n }\n return getOptions(call)\n}\n\n/**\n * Walks `path` down to the object holding its last key, descending only through object literals.\n */\nfunction optionParent(options: ObjectNode, path: Array<string>): { object: ObjectNode; key: string } | { reason: string } {\n let object = options\n\n for (const [index, key] of path.entries()) {\n if (index === path.length - 1) {\n return { object, key }\n }\n\n if (property(object, key) === undefined) {\n setProperty({ node: object, key, value: {} })\n }\n\n const next = property(object, key)\n if (next?.type !== 'ObjectExpression') {\n return { reason: `${key} is not an object, so ${path.join('.')} cannot be reached` }\n }\n object = next\n }\n return { reason: 'no option path given' }\n}\n\n/**\n * Writes `value` at `path` inside a plugin call's options, creating the options object and any\n * intermediate object along the path as needed. Refuses when the current value at `path` is\n * something other than a literal, so an option customized in code is never overwritten.\n */\nfunction applySet(call: CallNode, path: Array<string>, value: unknown): string | undefined {\n if (!isOptionValue(value)) {\n return 'the value is not a literal that can be written to a config file'\n }\n\n const options = ensureOptions(call)\n if (!options) {\n return 'the plugin was not called with an object literal'\n }\n\n const target = optionParent(options, path)\n if ('reason' in target) {\n return target.reason\n }\n\n const current = property(target.object, target.key)\n if (current !== undefined && readLiteral(current) === undefined) {\n return `${path.join('.')} is customized in code`\n }\n\n setProperty({ node: target.object, key: target.key, value })\n return undefined\n}\n\n/**\n * Deletes the property at `path` inside a plugin call's options, falling the plugin back to its\n * default for that option. Refuses when the value at `path` is not a literal, for the same reason\n * `applySet` does.\n */\nfunction applyRemove(call: CallNode, path: Array<string>): string | undefined {\n const options = getOptions(call)\n if (!options) {\n return 'the plugin has no options to remove'\n }\n\n const target = optionParent(options, path)\n if ('reason' in target) {\n return target.reason\n }\n\n const current = property(target.object, target.key)\n if (current === undefined) {\n return `${path.join('.')} is not set`\n }\n if (readLiteral(current) === undefined) {\n return `${path.join('.')} is customized in code`\n }\n\n removeProperty({ node: target.object, key: target.key })\n return undefined\n}\n\n/**\n * Outcome of `applyAddPlugin`. `addImport` is set when the new plugin call needs an import line\n * the caller must still insert; absent when the import was already there.\n */\ntype AddPluginResult = { reason: string } | { noop: true } | { addImport?: { importName: string; moduleSpecifier: string } }\n\n/**\n * Adds a `pluginX(...)` call to a config's plugins array. Replaying the same add is a no-op, while\n * an import name collision with an unrelated package remains an error.\n */\nfunction applyAddPlugin(mod: ProxifiedModule, config: ObjectNode, edit: Extract<ConfigEdit, { operation: 'add-plugin' }>): AddPluginResult {\n if (!isKubbPluginSpecifier(edit.plugin)) {\n return { reason: `\"${edit.plugin}\" is not a @kubb/plugin-* package` }\n }\n\n const importName = edit.importName ?? toExportName(edit.plugin)\n if (!IDENTIFIER.test(importName)) {\n return { reason: `\"${importName}\" is not a valid import name` }\n }\n\n if (pluginCalls(mod, config).some((plugin) => plugin.packageName === edit.plugin)) {\n return { noop: true }\n }\n\n const taken = importedFrom(mod).get(importName)\n if (taken && taken !== edit.plugin) {\n return { reason: `${importName} is already imported from ${taken}` }\n }\n\n const options = edit.options ?? {}\n if (!isOptionValue(options)) {\n return { reason: 'the options are not literals that can be written to a config file' }\n }\n\n const plugins = property(config, 'plugins')\n if (plugins?.type !== 'ArrayExpression') {\n return { reason: 'plugins is not an array literal' }\n }\n\n const call = Object.keys(options).length ? builders.functionCall(importName, options) : builders.functionCall(importName)\n plugins.elements.push(call.$ast as CallNode)\n\n return taken ? {} : { addImport: { importName, moduleSpecifier: edit.plugin } }\n}\n\n/**\n * Comments out a plugin call in place, keeping its options on disk so `enable-plugin` can restore\n * them exactly. Operates on `source` text rather than the AST: a commented-out call is no longer a\n * node magicast can address, and the surrounding array must not reflow when its element count\n * never actually changes.\n */\nfunction disablePlugin(source: string, mod: ProxifiedModule, config: ObjectNode, plugin: string): { source: string } | { reason: string } {\n const target = pluginCalls(mod, config).find((entry) => entry.packageName === plugin)\n if (!target) {\n return { reason: `${plugin} is not in the plugins array` }\n }\n\n const loc = target.call.loc\n if (!loc?.start || !loc.end) {\n return { reason: `${plugin} has no source location to comment out` }\n }\n\n const lines = source.split('\\n')\n const from = loc.start.line - 1\n const to = loc.end.line - 1\n const firstLine = lines[from] ?? ''\n const lastLine = lines[to] ?? ''\n\n // Only safe to comment out when the call sits alone on its lines: anything else sharing the\n // first line before it, or the last line after it besides a trailing comma, would be swallowed\n // into the comment along with the call, corrupting the file.\n if (firstLine.slice(0, loc.start.column).trim() !== '' || !/^,?\\s*$/.test(lastLine.slice(loc.end.column))) {\n return { reason: `${plugin} shares a line with other code, so it cannot be commented out safely` }\n }\n\n const indent = firstLine.match(/^\\s*/)?.[0] ?? ''\n const commented = lines.slice(from, to + 1).map((line) => (line.trim() ? `${indent}// ${line.slice(indent.length)}` : indent ? `${indent}//` : '//'))\n lines.splice(from, to - from + 1, formatMarker(plugin, commented.length, indent), ...commented)\n\n return { source: lines.join('\\n') }\n}\n\n/**\n * Uncomments the block a previous `disable-plugin` left behind for `plugin`.\n */\nfunction enablePlugin(source: string, plugin: string): { source: string } | { reason: string } {\n const lines = source.split('\\n')\n\n for (const [index, line] of lines.entries()) {\n const marker = parseMarker(line)\n if (marker?.plugin !== plugin) {\n continue\n }\n\n // Bounded by the marker's own line count rather than scanning for trailing `//` lines, so a\n // comment or another disabled block right after this one is left untouched.\n const end = index + 1 + marker.lineCount\n const restored = lines.slice(index + 1, end).map((commented) => commented.replace(/^(\\s*)\\/\\/ ?/, '$1'))\n lines.splice(index, end - index, ...restored)\n\n return { source: lines.join('\\n') }\n }\n\n return { reason: `${plugin} is not disabled` }\n}\n\n/**\n * Re-parses `source` and resolves the config entry an edit targets. Every edit re-parses rather\n * than sharing one module across the batch, since the disable/enable edits rewrite `source` as\n * text and would otherwise leave the others working from a stale tree.\n */\nfunction parseTarget(source: string, ref: ConfigRef | undefined): { mod: ProxifiedModule; config: ObjectNode } | { reason: string } {\n let mod: ProxifiedModule\n try {\n mod = parseModule(source)\n } catch {\n return { reason: 'the config file could not be parsed' }\n }\n\n const found = findConfigs(mod)\n if ('reason' in found) {\n return { reason: found.reason }\n }\n\n const config = selectConfig(found.configs, ref)\n if (!config) {\n return { reason: `no config entry found for ${JSON.stringify(ref)}` }\n }\n\n return { mod, config }\n}\n\n/**\n * Applies edits to a `kubb.config.ts` in place. Every node the edits do not touch keeps its\n * original text, so comments, formatting, and hand-written code around the config survive.\n *\n * Edits are independent: one that cannot be applied is reported in `outcomes` and the rest still run.\n *\n * @example\n * ```ts\n * const { source, outcomes } = applyConfigEdits(current, [\n * { operation: 'set', plugin: '@kubb/plugin-ts', path: ['enum', 'type'], value: 'enum' },\n * ])\n * ```\n *\n * @note recast always reprints a semicolon on a reprinted statement, so editing a block-body\n * `defineConfig` in a semicolon-free file adds one to the `return` line. This is a known gap.\n * Strip it when `detectCodeFormat` reports `useSemi: false`, if it turns out to matter in practice.\n */\nexport function applyConfigEdits(source: string, edits: Array<ConfigEdit>): ApplyResult {\n let current = source\n const format = detectCodeFormat(source)\n const endsWithNewline = source.endsWith('\\n')\n\n const outcomes = edits.map((edit): ConfigEditOutcome => {\n const target = parseTarget(current, edit.config)\n if ('reason' in target) {\n return { edit, applied: false, reason: target.reason }\n }\n const { mod, config } = target\n\n if (edit.operation === 'disable-plugin' || edit.operation === 'enable-plugin') {\n const result = edit.operation === 'disable-plugin' ? disablePlugin(current, mod, config, edit.plugin) : enablePlugin(current, edit.plugin)\n if ('reason' in result) {\n return { edit, applied: false, reason: result.reason }\n }\n current = result.source\n return { edit, applied: true }\n }\n\n if (edit.operation === 'add-plugin') {\n const result = applyAddPlugin(mod, config, edit)\n if ('reason' in result) {\n return { edit, applied: false, reason: result.reason }\n }\n if ('noop' in result) {\n return { edit, applied: true }\n }\n const afterLine = lastImportEndLine(mod)\n let next = generateCode(mod, { format }).code\n if (result.addImport) {\n next = insertImportLine({ source: next, afterLine, ...result.addImport })\n }\n current = withTrailingNewline(next, endsWithNewline)\n return { edit, applied: true }\n }\n\n const pluginCall = pluginCalls(mod, config).find((plugin) => plugin.packageName === edit.plugin)\n if (!pluginCall) {\n return { edit, applied: false, reason: `${edit.plugin} is not in the plugins array` }\n }\n\n const reason = edit.operation === 'set' ? applySet(pluginCall.call, edit.path, edit.value) : applyRemove(pluginCall.call, edit.path)\n if (!reason) {\n current = withTrailingNewline(generateCode(mod, { format }).code, endsWithNewline)\n }\n return { edit, applied: !reason, reason }\n })\n\n return { source: current, outcomes, changed: current !== source }\n}\n\n/**\n * The 1-based line where the file's last import declaration ends, or `0` when it has none. Read\n * off the parsed module, so a multi-line `import {\\n x,\\n} from '...'` reports its closing line\n * rather than the `import` keyword.\n */\nfunction lastImportEndLine(mod: ProxifiedModule): number {\n const body = mod.$ast.type === 'Program' ? mod.$ast.body : []\n\n return body.filter((node) => node.type === 'ImportDeclaration').at(-1)?.loc?.end.line ?? 0\n}\n\n/**\n * Writes an import after the last one already in the file, matching its quote style and whether it\n * ends in a semicolon. `afterLine` is where that last import ends, `0` for a file with none.\n *\n * Written as plain text rather than through magicast's import builder, which prints a brand-new\n * import declaration with its own default spacing and a semicolon regardless of `format`, since\n * that formatting only governs nodes recast can diff against the original source.\n */\nfunction insertImportLine({\n source,\n importName,\n moduleSpecifier,\n afterLine,\n}: {\n source: string\n importName: string\n moduleSpecifier: string\n afterLine: number\n}): string {\n const lines = source.split('\\n')\n\n const lastImportLine = afterLine > 0 ? lines[afterLine - 1] : undefined\n const quote = lastImportLine?.includes(`\"`) ? `\"` : `'`\n const semicolon = lastImportLine?.trimEnd().endsWith(';') ? ';' : ''\n const line = `import { ${importName} } from ${quote}${moduleSpecifier}${quote}${semicolon}`\n\n lines.splice(afterLine, 0, ...(afterLine > 0 ? [line] : [line, '']))\n\n return lines.join('\\n')\n}\n\n/**\n * `generateCode` always drops the file's trailing newline. Restore it when the input had one.\n */\nfunction withTrailingNewline(code: string, hadTrailingNewline: boolean): string {\n if (!hadTrailingNewline || code.endsWith('\\n')) {\n return code\n }\n return `${code}\\n`\n}\n","import { hash } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { type Config, createKubb, type Diagnostic, Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport {\n FORMATTER_PREFERENCE,\n LINTER_PREFERENCE,\n formatters,\n linters,\n memoize,\n tokenize,\n type ToolCommand,\n detectTool as detectUncachedTool,\n} from '@internals/utils'\nimport { waitForHookEnd } from './hooks.ts'\n\n/**\n * `isToolAvailable` spawns a process, and a long-lived connection generates repeatedly, so each\n * executable is probed once per process. The CLI deliberately does not memoize: a `--watch` build\n * should keep noticing a tool installed mid-session.\n */\nconst detectTool = memoize(new Map<ReadonlyArray<string>, Promise<string | null>>(), detectUncachedTool)\n\n/**\n * The two post-build tool steps. Formatting and linting differ only in which tools they look for,\n * so they run through one loop rather than two near-identical blocks.\n *\n * `noun` and `verbing` are spelled out instead of built from `kind`. Concatenating `` `${kind}ter` ``\n * and `` `${kind}ting` `` works for `format`, but doubles the `t` in `lint`, giving \"lintter\" and\n * \"lintting\" instead of \"linter\" and \"linting\".\n */\nconst TOOL_STEPS: ReadonlyArray<{ kind: 'format' | 'lint'; noun: string; verbing: string; tools: Record<string, ToolCommand>; detect: ReadonlyArray<string> }> =\n [\n // `detect` is the preference order for `auto`, most-preferred first. Spelled out rather than\n // taken from the table's key order, which is arbitrary and silently changes what `auto` picks.\n { kind: 'format', noun: 'formatter', verbing: 'Formatting', tools: formatters, detect: FORMATTER_PREFERENCE },\n { kind: 'lint', noun: 'linter', verbing: 'Linting', tools: linters, detect: LINTER_PREFERENCE },\n ]\n\n/**\n * Absolute path of the directory the formatter and linter are pointed at.\n */\nfunction outputPath(config: Config): string {\n return path.isAbsolute(config.output.path) ? config.output.path : path.resolve(process.cwd(), config.root, config.output.path)\n}\n\ntype RunHookProps = {\n hooks: Hookable<KubbHooks>\n /**\n * Stable identity for the command, hashed so the `kubb:hook:*` events for concurrent commands\n * can be told apart.\n */\n id: string\n command: string\n args: ReadonlyArray<string>\n}\n\n/**\n * Emits `kubb:hook:start` and waits for the matching `kubb:hook:end`. The host spawns the process:\n * this only describes what to run and when it finished.\n *\n * @throws whatever the command failed with, so callers can report it their own way.\n */\nasync function runHook({ hooks, id, command, args }: RunHookProps): Promise<void> {\n const hookId = hash('sha256', id)\n // Registered before the start event, since `callHook` awaits its listeners and the host calls\n // `kubb:hook:end` from inside that same listener.\n const hookEnd = waitForHookEnd(hooks, hookId)\n\n await hooks.callHook('kubb:hook:start', { id: hookId, command, args: [...args] })\n await hookEnd\n}\n\ntype GenerateProps = {\n config: Config\n hooks: Hookable<KubbHooks>\n signal?: AbortSignal\n}\n\nfunction isProblemErrorDiagnostic(diagnostic: Diagnostic): diagnostic is Diagnostic & { plugin?: string; message: string } {\n return (diagnostic.kind ?? 'problem') === 'problem' && diagnostic.severity === 'error'\n}\n\n/**\n * Folds error-severity diagnostics into one thrown error so logs name the failing plugin.\n */\nfunction formatGenerationFailure(diagnostics: ReadonlyArray<Diagnostic>): Error {\n const reasons = diagnostics\n .filter(isProblemErrorDiagnostic)\n .map((diagnostic) => (diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message))\n\n if (!reasons.length) {\n return new Error('Generation failed')\n }\n\n return new Error(`Generation failed: ${reasons.length} error${reasons.length === 1 ? '' : 's'}: ${reasons.join('; ')}`)\n}\n\n/**\n * Runs a full Kubb code-generation cycle for the given config.\n *\n * Emits lifecycle events on the provided `hooks` emitter so callers (e.g. the WebSocket stream)\n * can forward progress to connected clients. After a successful build, auto-formatting and\n * linting are applied when configured, followed by any user-defined `hooks.done` commands.\n */\nexport async function generate({ config, hooks, signal }: GenerateProps): Promise<void> {\n signal?.throwIfAborted()\n const hrStart = process.hrtime()\n\n await hooks.callHook('kubb:generation:start', { config })\n\n await hooks.callHook('kubb:info', { message: config.name ? `Setup generation ${config.name}` : 'Setup generation' })\n\n const kubb = createKubb(config, { hooks, signal })\n await kubb.setup()\n\n await hooks.callHook('kubb:info', { message: config.name ? `Build generation ${config.name}` : 'Build generation' })\n\n const { files, diagnostics, storage } = await kubb.safeBuild()\n signal?.throwIfAborted()\n\n await hooks.callHook('kubb:info', { message: 'Load summary' })\n\n // Core captures build failures as `error`-severity diagnostics instead of throwing, so\n // surface each one as a `kubb:error` event for the client. Warnings and info reported\n // through `ctx.warn`/`ctx.info` reach the client directly via core's events on this shared emitter.\n for (const diagnostic of diagnostics.filter(isProblemErrorDiagnostic)) {\n await hooks.callHook('kubb:error', { error: new Error(diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message) })\n }\n\n const status = Diagnostics.hasError(diagnostics) ? 'failed' : 'success'\n\n await hooks.callHook('kubb:generation:end', {\n config,\n // Only the files Kubb generated. `fsStorage().readKeys()` lists the working directory, so\n // Studio's tree would show the host's own source and miss output landing outside it.\n storage: { ...storage, readKeys: async () => [...new Set(files.map((file) => file.path))] },\n diagnostics,\n status,\n hrStart,\n filesCreated: files.length,\n })\n\n if (status === 'failed') {\n throw formatGenerationFailure(diagnostics)\n }\n\n await hooks.callHook('kubb:success', { message: 'Generation successfully' })\n\n for (const step of TOOL_STEPS) {\n const setting = config.output[step.kind]\n if (!setting) {\n continue\n }\n\n await hooks.callHook(`kubb:${step.kind}:start`)\n\n // `auto` means \"whatever is installed\", so the tool is detected now rather than at config time.\n const tool = setting === 'auto' ? await detectTool(step.detect) : setting\n\n if (!tool) {\n await hooks.callHook('kubb:warn', { message: `No ${step.noun} found (${step.detect.join(', ')}). Skipping ${step.verbing.toLowerCase()}.` })\n }\n\n if (tool && setting === 'auto') {\n await hooks.callHook('kubb:info', { message: `Auto-detected ${step.noun}: ${styleText('dim', tool)}` })\n }\n\n const command = tool ? step.tools[tool] : undefined\n\n if (command) {\n try {\n await runHook({ hooks, id: [config.name, tool].filter(Boolean).join('-'), command: command.command, args: command.args(outputPath(config)) })\n\n await hooks.callHook('kubb:success', { message: `${step.verbing} with ${tool} successfully` })\n } catch (caughtError) {\n await hooks.callHook('kubb:error', { error: new Error(command.errorMessage, { cause: caughtError }) })\n // Format/lint failures are non-fatal, but an abort during those commands must still reject.\n signal?.throwIfAborted()\n }\n }\n\n await hooks.callHook(`kubb:${step.kind}:end`)\n }\n\n // `output.postGenerate` commands run in order, each one waiting for the previous to finish.\n if (config.output.postGenerate?.length) {\n await hooks.callHook('kubb:hooks:start')\n\n for (const entry of config.output.postGenerate) {\n const line = typeof entry === 'string' ? entry : entry.command\n const [cmd, ...args] = tokenize(line)\n\n if (!cmd) {\n continue\n }\n\n await runHook({ hooks, id: line, command: cmd, args })\n await hooks.callHook('kubb:success', { message: `${line} successfully executed` })\n }\n\n await hooks.callHook('kubb:hooks:end')\n }\n}\n","import { createHash } from 'node:crypto'\nimport { glob, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join, relative, sep } from 'node:path'\nimport { promisify } from 'node:util'\nimport { gzip } from 'node:zlib'\nimport { build } from 'tsdown'\n\nconst gzipAsync = promisify(gzip)\n\ntype SnapshotFiles = Record<string, string>\ntype SnapshotPackage = { name: string; version: string; peerDependencies: Record<string, string> }\n\n/**\n * Maps a generated file's path to its place inside the tarball, stripping everything before a\n * `src`/`dist` segment and any `..`/empty path segment so a crafted file name cannot escape the\n * `package/` root.\n */\nfunction packagePath(filePath: string): string {\n const normalized = filePath.replaceAll('\\\\', '/')\n const relativePath = normalized.match(/\\/(?:src|dist)\\/.*$/)?.[0].slice(1) ?? normalized.replace(/^\\/+/, '')\n const safe = relativePath\n .split('/')\n .filter((part) => part && part !== '.' && part !== '..')\n .join('/')\n return `package/${safe}`\n}\n\n/**\n * Splits a tarball entry path into the legacy 100-byte `name` field and, when the path does not\n * fit, the 155-byte USTAR `prefix` field that extends it. Throws rather than silently truncating\n * a path the format cannot address (max 256 bytes: 100 name + 1 separator + 155 prefix).\n */\nfunction splitEntryPath(path: string): { name: string; prefix: string } {\n if (Buffer.byteLength(path, 'utf8') <= 100) {\n return { name: path, prefix: '' }\n }\n\n for (let i = path.length - 1; i >= 0; i--) {\n if (path[i] !== '/') continue\n\n const prefix = path.slice(0, i)\n const name = path.slice(i + 1)\n if (Buffer.byteLength(prefix, 'utf8') <= 155 && Buffer.byteLength(name, 'utf8') <= 100) {\n return { name, prefix }\n }\n }\n\n throw new Error(`Snapshot path is too long for a tar entry: ${path}`)\n}\n\nfunction header(path: string, size: number): Buffer {\n const { name, prefix } = splitEntryPath(path)\n const value = Buffer.alloc(512)\n value.write(name, 0, 'utf8')\n value.write('0000644\\0', 100, 'ascii')\n value.write('0000000\\0', 108, 'ascii')\n value.write('0000000\\0', 116, 'ascii')\n value.write(`${size.toString(8).padStart(11, '0')}\\0`, 124, 'ascii')\n value.write(\n `${Math.floor(Date.now() / 1000)\n .toString(8)\n .padStart(11, '0')}\\0`,\n 136,\n 'ascii',\n )\n value.fill(32, 148, 156)\n value.write('0', 156, 'ascii')\n value.write('ustar\\0', 257, 'ascii')\n value.write('00', 263, 'ascii')\n value.write('0000000\\0', 265, 'ascii')\n value.write('0000000\\0', 297, 'ascii')\n value.write(prefix, 345, 'utf8')\n const checksum = [...value].reduce((sum, byte) => sum + byte, 0)\n value.write(`${checksum.toString(8).padStart(6, '0')}\\0 `, 148, 'ascii')\n return value\n}\n\n/**\n * Packs a generation's files into a gzipped, npm-installable tarball: a `package/` root with the\n * generated sources, a `tsdown`-built `dist/` (esm + cjs), and a `package.json` manifest.\n */\nexport async function createSnapshotPackage(files: SnapshotFiles, packageInfo: SnapshotPackage): Promise<{ bytes: Buffer; integrity: string }> {\n const root = await mkdtemp(join(tmpdir(), 'kubb-snapshot-'))\n const dist = join(root, 'dist')\n\n // Resolved once so the sanitized target for each file is computed exactly one way, and any two\n // generated files that collide after sanitizing (e.g. `a/../index.ts` and `a/index.ts`) are\n // caught here instead of silently overwriting one another later.\n const resolvedPaths = Object.entries(files).map(([name, content]) => ({ name, content, target: packagePath(name) }))\n const targetOwners = new Map<string, string>()\n for (const { name, target } of resolvedPaths) {\n const owner = targetOwners.get(target)\n if (owner) {\n throw new Error(`Snapshot has two generated files that sanitize to the same path \"${target}\": \"${owner}\" and \"${name}\"`)\n }\n targetOwners.set(target, name)\n }\n\n try {\n await mkdir(dist)\n await Promise.all(\n resolvedPaths.map(async ({ content, target }) => {\n const path = join(root, target.slice('package/'.length))\n await mkdir(join(path, '..'), { recursive: true })\n await writeFile(path, content)\n }),\n )\n const sourceEntries = resolvedPaths.filter(({ name }) => /\\.(?:[cm]?[jt]sx?)$/.test(name)).map(({ target }) => join(root, target.slice('package/'.length)))\n if (sourceEntries.length)\n await build({\n entry: sourceEntries,\n outDir: dist,\n format: ['esm', 'cjs'],\n dts: false,\n sourcemap: false,\n unbundle: true,\n report: false,\n logLevel: 'silent',\n // The manifest below always points at `.mjs`/`.cjs`, so the build must produce those\n // extensions regardless of the host process's own `package.json` \"type\" (tsdown otherwise\n // infers it from the nearest ancestor package.json, which differs by host).\n fixedExtension: true,\n })\n const builtEntries = await Promise.all(\n (await Array.fromAsync(glob('**/*', { cwd: dist, withFileTypes: true })))\n .filter((entry) => entry.isFile())\n .map(async (entry) => {\n // `unbundle: true` preserves dist's own subdirectory structure, so the archive path must\n // follow suit: `entry.name` alone is just the basename and would flatten (and collide)\n // nested output files.\n const filePath = join(entry.parentPath, entry.name)\n const distRelativePath = relative(dist, filePath).split(sep).join('/')\n return [`package/dist/${distRelativePath}`, await readFile(filePath, 'utf8')] as const\n }),\n )\n // Without `@kubb/plugin-barrel` the build produces no `dist/index.*`, so pointing\n // `main`/`module`/`exports['.']` at it would ship a manifest with missing files.\n const builtPaths = new Set(builtEntries.map(([path]) => path))\n const hasBarrel = builtPaths.has('package/dist/index.mjs') && builtPaths.has('package/dist/index.cjs')\n const barrelFields = hasBarrel ? { main: './dist/index.cjs', module: './dist/index.mjs' } : {}\n const barrelExport = hasBarrel ? { '.': { import: './dist/index.mjs', require: './dist/index.cjs' } } : {}\n\n const entries = {\n 'package/package.json': JSON.stringify(\n {\n ...packageInfo,\n type: 'module',\n ...barrelFields,\n // `exports` denies any subpath it doesn't list, so the wildcard keeps individual\n // generated files (e.g. `models/Pet`) importable without a barrel.\n exports: { ...barrelExport, './*': { import: './dist/*.mjs', require: './dist/*.cjs' } },\n },\n null,\n 2,\n ),\n ...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),\n ...Object.fromEntries(builtEntries),\n }\n const chunks: Array<Buffer> = []\n\n for (const [name, content] of Object.entries(entries)) {\n const bytes = Buffer.from(content)\n chunks.push(header(name, bytes.length), bytes, Buffer.alloc((512 - (bytes.length % 512)) % 512))\n }\n\n const bytes = await gzipAsync(Buffer.concat([...chunks, Buffer.alloc(1024)]))\n return { bytes, integrity: `sha512-${createHash('sha512').update(bytes).digest('base64')}` }\n } finally {\n await rm(root, { recursive: true, force: true })\n }\n}\n","import { readFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { isAbsolute, relative, resolve } from 'node:path'\nimport { getElapsedMs } from '@internals/utils'\nimport { Diagnostics, type Hookable, type KubbHooks, type Storage } from '@kubb/core'\nimport WebSocket from 'ws'\nimport type { GenerationEvent, GenerationEventPayloads, GenerationEventType } from './protocol/index.ts'\nimport { toPackageName } from './resolveConfig.ts'\n\ntype WebSocketOptions = WebSocket.ClientOptions\n\n/**\n * How long the initial handshake may take before the socket is closed and the reconnect loop\n * takes over.\n */\nconst CONNECT_TIMEOUT_MS = 5_000\n\nconst require = createRequire(import.meta.url)\n\nfunction relativeStoragePath(root: string, filePath: string): string {\n return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll('\\\\', '/')\n}\n\n/**\n * Inverse of {@link relativeStoragePath}: rebuilds the storage key a relative path came from.\n */\nexport function absoluteStoragePath(root: string, relativePath: string): string {\n return resolve(root, relativePath)\n}\n\ntype PackageJSON = {\n version?: string\n}\n\nasync function resolvePeerDependencies(names: Array<string>): Promise<{\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}> {\n const uniqueNames = [...new Set(names.map(toPackageName))]\n const peerDependencies: Record<string, string> = {}\n const missingDependencies: Array<string> = []\n\n const versions = await Promise.all(\n uniqueNames.map(async (name) => {\n try {\n const path = require.resolve(`${name}/package.json`)\n const packageJSON = JSON.parse(await readFile(path, 'utf8')) as PackageJSON\n return packageJSON.version\n } catch {\n return undefined\n }\n }),\n )\n\n for (const [index, name] of uniqueNames.entries()) {\n const version = versions[index]\n if (version) {\n peerDependencies[name] = version\n continue\n }\n missingDependencies.push(name)\n }\n\n return { peerDependencies, missingDependencies }\n}\n\n/**\n * Opens a Studio WebSocket connection and closes it when the initial handshake exceeds the configured timeout.\n */\nexport function createWebsocket(url: string, options: WebSocketOptions): WebSocket {\n const ws = new WebSocket(url, options)\n\n const timer = setTimeout(() => {\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close(3008, 'Connection timeout')\n }\n }, CONNECT_TIMEOUT_MS)\n\n // Once the handshake settles the timer has nothing left to check, and leaving it pending holds\n // the socket for the rest of the window.\n ws.once('open', () => clearTimeout(timer))\n ws.once('close', () => clearTimeout(timer))\n\n return ws\n}\n\nexport type GenerationState = {\n storage: Storage\n root: string\n paths: Set<string>\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nexport type GenerationStreamOptions = {\n onGenerationEnd?: (result: GenerationState) => void\n}\n\n/** Forwards selected Kubb lifecycle events to a native Cap'n Web stream. */\nexport function createGenerationStream(\n hooks: Hookable<KubbHooks>,\n jobId: string,\n options: GenerationStreamOptions = {},\n): { stream: ReadableStream<GenerationEvent>; close: () => Promise<void>; dispose: () => void; fail: (error: unknown) => void } {\n const unhooks: Array<() => void> = []\n let root = ''\n // Infinite HWM so unread events don't stall result()\n const transform = new TransformStream<GenerationEvent>(undefined, undefined, { highWaterMark: Infinity })\n const writer = transform.writable.getWriter()\n let writes = Promise.resolve()\n let closed = false\n let streamError: unknown\n\n /**\n * Registers a listener and keeps its remover, so one generation's listeners come off the session\n * emitter again when that generation ends.\n */\n function on<TName extends keyof KubbHooks & string>(name: TName, handler: (...args: KubbHooks[TName]) => unknown): void {\n unhooks.push(hooks.hook(name, handler))\n }\n\n function emitEvent<Type extends GenerationEventType>(type: Type, data: GenerationEventPayloads[Type]): void {\n const event = { jobId, type, data, version: 1 as const, timestamp: Date.now() } as unknown as GenerationEvent\n // A prior failure skips the write; either way the chain settles so the next event still runs.\n writes = writes\n .then(() => writer.write(event))\n .catch((error) => {\n streamError = error\n })\n }\n\n on('kubb:plugin:start', (ctx) => {\n emitEvent('kubb:plugin:start', [{ plugin: { name: ctx.plugin.name } }])\n })\n\n on('kubb:plugin:end', (ctx) => {\n emitEvent('kubb:plugin:end', [{ plugin: { name: ctx.plugin.name }, duration: ctx.duration, success: ctx.success }])\n })\n\n on('kubb:build:start', ({ config, adapter }) => {\n root = config.root\n emitEvent('kubb:build:start', [{ config: { name: config.name }, adapter: { name: adapter.name } }])\n })\n\n on('kubb:build:end', ({ files, config, outputDir }) => {\n emitEvent('kubb:build:end', [{ files: files.map((file) => ({ path: relativeStoragePath(config.root, file.path), name: file.name })), outputDir }])\n })\n\n on('kubb:files:processing:start', ({ files }) => {\n emitEvent('kubb:files:processing:start', [{ total: files.length }])\n })\n\n on('kubb:files:processing:update', ({ files }) => {\n emitEvent('kubb:files:processing:update', [\n {\n files: files.map(({ file, processed, total, percentage }) => ({\n file: relativeStoragePath(root, file.path),\n processed,\n total,\n percentage,\n })),\n },\n ])\n })\n\n on('kubb:files:processing:end', ({ files }) => {\n emitEvent('kubb:files:processing:end', [{ total: files.length }])\n })\n\n // The three log levels differ only in their event name.\n for (const type of ['kubb:info', 'kubb:success', 'kubb:warn'] as const) {\n on(type, ({ message, info }) => {\n emitEvent(type, [{ message, info }])\n })\n }\n\n on('kubb:generation:start', ({ config }) => {\n emitEvent('kubb:generation:start', [\n {\n name: config.name,\n plugins: config.plugins.length,\n },\n ])\n })\n\n on('kubb:generation:end', async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {\n const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name))\n const keys = await storage.readKeys()\n const paths = new Set(keys.map((key) => relativeStoragePath(config.root, key)))\n\n // This hook fires for a failed run too (`status: 'failed'`), so a failed run's output must not\n // become `#lastGeneration`: the next run would otherwise promote it to `#previousGeneration` and\n // diff or serve a failed run's files as if they were the session's last real output.\n if ((status ?? 'success') === 'success') {\n options.onGenerationEnd?.({ storage, root: config.root, paths, peerDependencies, missingDependencies })\n }\n\n emitEvent('kubb:generation:end', [])\n\n if (!hrStart) {\n return\n }\n\n const duration = Math.round(getElapsedMs(hrStart))\n\n emitEvent('kubb:generation:summary', [\n { duration, fileCount: filesCreated ?? 0, failedPlugins: Diagnostics.failedPlugins(diagnostics).length, status: status ?? 'success' },\n ])\n })\n\n on('kubb:error', ({ error }) => {\n emitEvent('kubb:error', [\n {\n message: error.message,\n stack: error.stack,\n },\n ])\n })\n\n on('kubb:diagnostic', ({ diagnostic }) => {\n const cause = 'cause' in diagnostic ? diagnostic.cause : undefined\n emitEvent('kubb:diagnostic', [\n {\n code: diagnostic.code,\n message: diagnostic.message,\n severity: diagnostic.severity,\n location: 'location' in diagnostic ? diagnostic.location : undefined,\n help: 'help' in diagnostic ? diagnostic.help : undefined,\n plugin: 'plugin' in diagnostic ? diagnostic.plugin : undefined,\n stack: cause?.stack,\n },\n ])\n })\n\n // Bracketing events carry no context, so they forward identically.\n for (const type of [\n 'kubb:lifecycle:start',\n 'kubb:lifecycle:end',\n 'kubb:format:start',\n 'kubb:format:end',\n 'kubb:lint:start',\n 'kubb:lint:end',\n 'kubb:hooks:start',\n 'kubb:hooks:end',\n ] as const) {\n on(type, () => {\n emitEvent(type, [])\n })\n }\n\n on('kubb:hook:start', ({ id, command, args }) => {\n emitEvent('kubb:hook:start', [{ id, command, args: args ? [...args] : undefined }])\n })\n\n on('kubb:hook:line', ({ id, line }) => {\n emitEvent('kubb:hook:line', [{ id, line }])\n })\n\n on('kubb:hook:end', ({ id, command, args, success, error }) => {\n emitEvent('kubb:hook:end', [\n {\n id,\n command,\n args: args ? [...args] : undefined,\n success,\n error: error ? { message: error.message, stack: error.stack } : undefined,\n },\n ])\n })\n\n /**\n * Takes this generation's listeners off the session emitter. Safe to call twice.\n */\n function detach(): void {\n for (const unhook of unhooks) unhook()\n unhooks.length = 0\n }\n\n async function close(): Promise<void> {\n if (closed) {\n return\n }\n closed = true\n detach()\n await writes\n // Consumer cancel sets streamError; don't fail a successful generation over that.\n if (streamError) {\n return\n }\n await writer.close().catch(() => undefined)\n }\n\n function fail(error?: unknown): void {\n detach()\n if (closed) {\n return\n }\n closed = true\n void writer.abort(error).catch(() => undefined)\n }\n\n return { stream: transform.readable, close, dispose: () => fail(), fail }\n}\n","import { newWebSocketRpcSession, RpcTarget } from 'capnweb'\nimport type {\n AgentApi,\n GenerateInput,\n PublishSnapshotInput,\n ReadFilesInput,\n RpcConnection,\n RpcConnector,\n SaveConfigInput,\n StudioApi,\n} from './protocol/index.ts'\nimport { createWebsocket } from './ws.ts'\n\n/**\n * The only methods Studio may call on an agent. A `StudioSession` carries far more than\n * {@link AgentApi}, so it is wrapped rather than exposed: what Cap'n Web can reach is exactly what\n * this class re-declares.\n */\nclass AgentRpcTarget extends RpcTarget implements AgentApi {\n constructor(private readonly api: AgentApi) {\n super()\n }\n\n connect() {\n return this.api.connect()\n }\n startGeneration(input: GenerateInput) {\n return this.api.startGeneration(input)\n }\n saveConfig(input: SaveConfigInput) {\n return this.api.saveConfig(input)\n }\n publishSnapshot(input: PublishSnapshotInput) {\n return this.api.publishSnapshot(input)\n }\n readFiles(input: ReadFilesInput) {\n return this.api.readFiles(input)\n }\n}\n\n/**\n * Opens an authenticated Cap'n Web session to Studio over a WebSocket. Rejects an unencrypted URL\n * before opening the socket, so a bearer token never reaches a plaintext host.\n *\n * @example\n * ```ts\n * const rpc = await connectWebSocketRpc({ url: 'wss://studio.kubb.dev/s/1', token, local: session })\n * await rpc.studio.ping()\n * ```\n */\nexport const connectWebSocketRpc: RpcConnector = async ({ url, token, local }): Promise<RpcConnection> => {\n const { protocol, hostname, host } = new URL(url)\n // `URL` keeps the brackets on an IPv6 hostname, so `::1` arrives as `[::1]`.\n const isLoopback = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'\n if (protocol !== 'wss:' && !(protocol === 'ws:' && isLoopback)) {\n throw new Error(`Refusing unencrypted WebSocket to ${host}`)\n }\n\n const socket = createWebsocket(url, { headers: { Authorization: `Bearer ${token}` } })\n const closed = new Promise<void>((resolve) => socket.once('close', resolve))\n // `ws` implements the browser WebSocket surface capnweb uses, but declares its own nominal type.\n const studio = newWebSocketRpcSession<StudioApi>(socket as unknown as globalThis.WebSocket, new AgentRpcTarget(local))\n studio.onRpcBroken(() => socket.close())\n\n return {\n studio,\n closed,\n close: () => studio[Symbol.dispose](),\n }\n}\n","import { writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { getErrorMessage, inParallel, read, toError } from '@internals/utils'\nimport { type Config, fsStorage, Hookable, type KubbHooks, logLevel as logLevelMap, memoryStorage } from '@kubb/core'\nimport { version as kubbVersion } from '../package.json'\nimport { setupHookListener } from './hooks.ts'\nimport {\n type AgentApi,\n type AgentConnectResponse,\n type AgentPermissions,\n type ClientInfo,\n type ConfigFileView,\n type ConnectMessagePayload,\n type FileChange,\n type GenerateInput,\n type GenerateResult,\n type GenerationEvent,\n type GenerationRun,\n MAX_FILES_PER_REQUEST,\n type ReadFilesInput,\n type SaveConfigInput,\n type SaveResult,\n type PublishSnapshotInput,\n type PublishSnapshotResult,\n type RpcConnector,\n type RpcConnection,\n} from './protocol/index.ts'\nimport { createAgentSession, disconnect, InvalidAgentTokenError } from './api.ts'\nimport { applyConfigEdits, readConfig } from './configFile.ts'\nimport { generate } from './generate.ts'\nimport { agentDefaults } from './constants.ts'\nimport { mergeAdapter, mergePlugins, toPackageName } from './resolveConfig.ts'\nimport { createSnapshotPackage } from './snapshotPackage.ts'\nimport { RpcTarget } from 'capnweb'\nimport { absoluteStoragePath, createGenerationStream, type GenerationState } from './ws.ts'\nimport { connectWebSocketRpc } from './rpc.ts'\n\n/**\n * How many files are read from storage at once when serving `readFiles` or packing a snapshot.\n */\nconst FILE_READ_CONCURRENCY = 50\n\n/**\n * A run's file contents keyed by output-relative path.\n */\ntype GenerationSnapshot = Map<string, string>\n\n/**\n * Reads every file a run produced back out of its storage.\n */\nasync function readSnapshot(generation: GenerationState): Promise<GenerationSnapshot> {\n const snapshot: GenerationSnapshot = new Map()\n await inParallel({\n items: [...generation.paths],\n limit: FILE_READ_CONCURRENCY,\n run: async (path) => {\n const content = await generation.storage.readItem(absoluteStoragePath(generation.root, path))\n if (content !== null) {\n snapshot.set(path, content)\n }\n },\n })\n return snapshot\n}\n\n/**\n * How each path differs between two runs. Paths with identical content are left out.\n */\nfunction diffSnapshots(previous: GenerationSnapshot, current: GenerationSnapshot): Record<string, FileChange> {\n const changes: Record<string, FileChange> = {}\n for (const [path, content] of current) {\n if (!previous.has(path)) {\n changes[path] = 'added'\n continue\n }\n if (previous.get(path) !== content) changes[path] = 'changed'\n }\n for (const path of previous.keys()) {\n if (!current.has(path)) changes[path] = 'removed'\n }\n return changes\n}\n\nclass GenerationRunTarget extends RpcTarget implements GenerationRun {\n constructor(\n private readonly generationStream: ReadableStream<GenerationEvent>,\n private readonly generationResult: Promise<GenerateResult>,\n private readonly cancelGeneration: () => Promise<void>,\n /** Stops the run. Cap'n Web calls this on explicit disposal and on a dropped session alike. */\n private readonly stopGeneration: () => void,\n ) {\n super()\n }\n\n async events() {\n return this.generationStream\n }\n result() {\n return this.generationResult\n }\n cancel() {\n return this.cancelGeneration()\n }\n [Symbol.dispose]() {\n this.stopGeneration()\n }\n}\n\nexport type StudioSessionOptions = {\n connector?: RpcConnector\n token: string\n studioUrl?: string\n configPath: string\n /**\n * Loads the on-disk Kubb config. Injected so each host resolves config its own way: the Docker\n * agent from an explicit `KUBB_AGENT_CONFIG` path, the CLI through the same discovery\n * `kubb generate` uses.\n */\n loadConfig: () => Promise<Config>\n /**\n * The runtime's own version, reported to Studio next to the `kubb` version.\n */\n version: string\n /**\n * Identifies the host to Studio, so the UI can badge a CLI connection and show the real project.\n */\n client?: ClientInfo\n /**\n * What Studio may do in this project, off unless the host grants it. A sandbox session narrows\n * them further: it never writes to disk and never edits a config file, and it always generates\n * from the spec Studio sends.\n */\n permissions?: Partial<AgentPermissions>\n root?: string\n retryInterval?: number\n /**\n * Milliseconds between keep-alive pings, clamped to `agentDefaults.maxHeartbeatIntervalMs`.\n * Raise it to halve the traffic and database writes a long-lived agent costs, at the price of\n * Studio taking that much longer to notice the agent has gone. Lower it in development to see\n * connection state move immediately.\n */\n heartbeatInterval?: number\n /**\n * Number of pool sessions this agent serves. Read by `createClient`, which opens one\n * session per slot, and reported to Studio at registration.\n */\n poolSize?: number\n /**\n * Aborting this disconnects the session and stops the reconnect loop. Hosts wire it to their own\n * shutdown: Nitro's `close` hook, or `SIGINT`/`SIGTERM` in the CLI.\n */\n signal?: AbortSignal\n /**\n * Installs listeners on the session's event emitter, which carries both the session events and\n * the generations it runs. Left out, the runtime prints nothing, which is what a library should\n * default to.\n */\n installLogger?: (hooks: Hookable<KubbHooks>) => void | Promise<void>\n /**\n * Threshold for the reconnect loop's own `console.error` lines, using the numeric constants\n * `@kubb/core` exports as `logLevel`. Left out, those lines never print, the same silent default\n * as an unset `installLogger` — a reconnect happens outside any one session's hooks, so it has no\n * other way to ask a host how loud to be.\n */\n logLevel?: number\n /**\n * Called when this session's background reconnect is rejected with an invalid token. Unlike\n * `ClientOptions.onAuthRequired`, this fires once per session rather than once per pool:\n * `createClient` wraps it into that deduped, pool-stopping callback. Not meant to be set\n * directly by a host.\n */\n onTokenRejected?: (error: InvalidAgentTokenError) => void\n}\n\n/**\n * A session's options with every default filled in, so nothing downstream repeats a fallback.\n */\ntype ResolvedOptions = StudioSessionOptions & {\n studioUrl: string\n root: string\n permissions: AgentPermissions\n retryInterval: number\n heartbeatInterval: number\n /**\n * Absolute path to the config file, for reading and patching it. `configPath` keeps the form the\n * host gave, which is what Studio shows.\n */\n configFile: string\n}\n\n/**\n * Fills in a host's options: the hosted Studio URL, the current working directory, and every\n * permission off unless granted. Idempotent, so a reconnect can pass an already-resolved bag\n * back in.\n */\nfunction applyStudioDefaults(options: StudioSessionOptions): ResolvedOptions {\n const root = options.root ?? process.cwd()\n\n return {\n ...options,\n studioUrl: options.studioUrl ?? agentDefaults.studioUrl,\n root,\n // `configPath` is relative to the agent's root unless it is already absolute, which is what\n // `resolve` does on its own.\n configFile: path.resolve(root, options.configPath),\n permissions: { allowWrite: false, allowConfigEdit: false, allowInput: false, allowExec: false, allowRead: false, ...options.permissions },\n retryInterval: options.retryInterval ?? agentDefaults.retryIntervalMs,\n // Studio counts an agent offline once its last ping is older than its liveness window, so a\n // slower cadence would make a healthy agent invisible. Clamped here rather than in a host's\n // env parsing, so every host is held to the contract.\n heartbeatInterval: Math.min(options.heartbeatInterval ?? agentDefaults.heartbeatIntervalMs, agentDefaults.maxHeartbeatIntervalMs),\n }\n}\n\n/**\n * Schedules another connection attempt.\n *\n * A free function rather than a method: a pending retry timer reaches whatever it closes over, so\n * closing only over `options` (not a `StudioSession`) keeps a queued retry from pinning a closed\n * socket, its hook emitter, or its session id alive for the length of the retry interval.\n */\nfunction reconnect(options: ResolvedOptions): void {\n const { signal, retryInterval, onTokenRejected, logLevel } = options\n\n if (signal?.aborted) {\n return\n }\n\n // console.error, not console.info: a CI runner only forwards a child process's stderr live, so\n // an info-level write here would be silently buffered away instead of reaching its log.\n if (logLevel !== undefined && logLevel > logLevelMap.silent) {\n console.error(styleText('dim', `Retrying connection in ${retryInterval}ms to Kubb Studio ...`))\n }\n\n const cancel = () => clearTimeout(timer)\n const timer = setTimeout(() => {\n // Removed here rather than left to `{ once: true }`: the signal only aborts at shutdown, so one\n // listener per retry would accumulate for the whole life of a down-Studio retry loop.\n signal?.removeEventListener('abort', cancel)\n\n if (signal?.aborted) {\n return\n }\n\n // The rejection is never awaited, so it has to be caught here or it surfaces as an\n // unhandledRejection that kills the retry loop instead of trying again.\n new StudioSession(options).start().catch((error: unknown) => {\n if (logLevel !== undefined && logLevel > logLevelMap.silent) {\n console.error(styleText('red', `Reconnect attempt to Kubb Studio failed: ${getErrorMessage(error)}`))\n }\n\n // A rejected token stays rejected, so retrying only spams 401s until the process is killed.\n // The host learns about it here instead: the startup path already reports its own rejection\n // by throwing, so only the background path needs the callback.\n if (error instanceof InvalidAgentTokenError) {\n onTokenRejected?.(error)\n\n return\n }\n\n reconnect(options)\n })\n }, retryInterval)\n\n signal?.addEventListener('abort', cancel, { once: true })\n}\n\n/**\n * One agent-to-Studio RPC transport: opening it, keeping it alive, and serving remote methods.\n * `createClient` opens one per pool slot and is the only caller.\n */\nexport class StudioSession implements AgentApi {\n readonly #options: ResolvedOptions\n // Each session gets its own isolated event emitter so generation events from one session do not\n // bleed into another session's WebSocket stream.\n readonly #hooks = new Hookable<KubbHooks>()\n /**\n * Removers for every listener this session added (socket, shutdown signal, hooks) so `dispose`\n * detaches them in one pass. Listeners a host attached itself through `installLogger` survive.\n */\n readonly #unhooks: Array<() => void> = []\n\n /**\n * What `createAgentSession` handed back, and the marker for whether a session exists at all.\n * Before it resolves there is nothing to disconnect and no sandbox flag to read.\n */\n #session: AgentConnectResponse | undefined\n #rpc: RpcConnection | undefined\n // Returned with the session, so both sides can be named from the first RPC connection.\n #studioVersion: string | undefined\n\n // Whether the session is over: guards the close event from tearing down twice, and a shutdown\n // from being turned into a reconnect.\n #disposed = false\n // Guards against a second `generate` command starting while one is already running. Without\n // this, two concurrent `generate()` calls share this socket via `setupEventsStream`, and their\n // events interleave with no way for Studio to tell the two runs apart.\n #isGenerating = false\n #heartbeatTimer: ReturnType<typeof setTimeout> | undefined\n // The most recent generation's live storage, kept so `readFiles` and `snapshot` can read file\n // content on demand instead of Studio round tripping it back over RPC, and\n // instead of this holding the whole run's output in memory. `paths` is the whitelist a request\n // is checked against, so a caller can only ever read what this run actually produced. Set as\n // soon as `kubb:generation:end` fires, undefined again if a run fails before that.\n #lastGeneration: GenerationState | undefined\n // The run before `#lastGeneration`, read into memory right before the next run starts, so a\n // written-to-disk run can still be diffed after the new one overwrites its files. It backs both\n // `GenerateResult.changes` and `readFiles({ revision: 'previous' })`.\n #previousGeneration: GenerationSnapshot | undefined\n /**\n * Resolves when Studio calls {@link StudioSession.connect}. `studio:ready` waits on this so the\n * host does not queue jobs before the agent session is registered.\n */\n readonly #connectAck = Promise.withResolvers<void>()\n\n constructor(options: StudioSessionOptions) {\n this.#options = applyStudioDefaults(options)\n // dispose() may reject this before start() awaits it\n void this.#connectAck.promise.catch(() => {})\n }\n\n /**\n * A sandbox agent runs on Studio's own infrastructure, so it has no user project to touch.\n */\n get #isSandbox(): boolean {\n return this.#session?.isSandbox === true\n }\n\n get #canWrite(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowWrite\n }\n\n get #canEditConfig(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowConfigEdit\n }\n\n /**\n * A sandbox agent always generates from the spec Studio supplies. A local agent only when the\n * host opted in.\n */\n get #canUseInput(): boolean {\n return this.#isSandbox || this.#options.permissions.allowInput\n }\n\n /**\n * A sandbox agent always allows reading its output back; a local agent only when opted in.\n */\n get #canRead(): boolean {\n return this.#isSandbox || this.#options.permissions.allowRead\n }\n\n async start(): Promise<void> {\n const { token, studioUrl, signal, heartbeatInterval, installLogger } = this.#options\n\n await installLogger?.(this.#hooks)\n\n try {\n // Before the session exists, so a host can cover the wait: `createAgentSession` is a round\n // trip and the socket after it opens without being awaited.\n await this.#hooks.callHook('studio:connecting', { url: studioUrl })\n\n const session = await createAgentSession({ token, studioUrl })\n\n this.#session = session\n this.#studioVersion = session.version\n\n const rpc = await (this.#options.connector ?? connectWebSocketRpc)({ url: session.url, token, local: this })\n this.#rpc = rpc\n void rpc.closed.then(this.#onClose)\n\n signal?.addEventListener('abort', this.#onAbort, { once: true })\n this.#unhooks.push(() => signal?.removeEventListener('abort', this.#onAbort))\n\n this.#scheduleHeartbeat(heartbeatInterval)\n await this.#hooks.callHook('studio:connected', {\n url: studioUrl,\n versions: { studio: this.#studioVersion, kubb: kubbVersion, agent: this.#options.version },\n agentSlug: session.agentSlug,\n organizationSlug: session.organizationSlug,\n })\n // Studio registers the agent by calling connect() over RPC. Ready means that handshake landed.\n await this.#connectAck.promise\n await this.#hooks.callHook('studio:ready', {})\n } catch (error) {\n // A connector can fail after opening RPC and installing the heartbeat. Tear down every\n // partial resource before retrying, otherwise each retry leaks a timer and a live session.\n this.#disposed = true\n this.dispose()\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n if (error instanceof InvalidAgentTokenError) {\n throw error\n }\n\n reconnect(this.#options)\n }\n }\n\n #warn(message: string): Promise<void> | void {\n return this.#hooks.callHook('studio:warn', { message })\n }\n\n /**\n * Declines a request: logs why locally, then tells Studio. The two wordings differ on purpose,\n * since the log names the request that was ignored and the error names what the caller can do.\n */\n async #refuse(reason: string, message: string): Promise<never> {\n await this.#warn(reason)\n throw new Error(message)\n }\n\n #scheduleHeartbeat(interval: number): void {\n const rpc = this.#rpc\n if (!rpc) {\n return\n }\n this.#heartbeatTimer = setTimeout(async () => {\n try {\n await this.#ping(rpc)\n } catch {\n if (this.#rpc === rpc) {\n rpc.close()\n }\n return\n }\n\n if (this.#rpc === rpc) {\n this.#scheduleHeartbeat(interval)\n }\n }, interval)\n }\n\n /**\n * Races `studio.ping()` against a deadline, so a half-open socket can't hang it forever.\n * */\n #ping(rpc: RpcConnection): Promise<void> {\n const { promise: timedOut, reject: onTimeout } = Promise.withResolvers<never>()\n const timer = setTimeout(() => onTimeout(new Error('Heartbeat ping timed out')), agentDefaults.heartbeatTimeoutMs)\n\n return Promise.race([rpc.studio.ping(), timedOut]).finally(() => clearTimeout(timer))\n }\n\n /**\n * Reads `kubb.config.ts` and reports which plugin options Studio may edit.\n *\n * Skipped when the host did not grant `allowConfigEdit`. Not cached: the user can edit the file\n * between two Studio actions.\n */\n async #readConfigFileView(source?: string): Promise<ConfigFileView | undefined> {\n if (!this.#canEditConfig) {\n return undefined\n }\n\n try {\n return readConfig(source ?? (await read(this.#options.configFile)))\n } catch (error) {\n await this.#warn(`Could not read ${this.#options.configFile}: ${getErrorMessage(error)}`)\n\n return undefined\n }\n }\n\n async connect(): Promise<ConnectMessagePayload> {\n const { configPath, root, version, loadConfig, permissions } = this.#options\n const [config, file] = await Promise.all([loadConfig(), this.#readConfigFileView()])\n\n const payload: ConnectMessagePayload = {\n versions: { kubb: kubbVersion, agent: version },\n root,\n config: {\n path: configPath,\n file,\n plugins: config.plugins.map((plugin) => ({\n name: toPackageName(plugin.name),\n options: plugin.options ?? {},\n })),\n },\n permissions: {\n ...permissions,\n allowWrite: this.#canWrite,\n allowInput: this.#canUseInput,\n allowConfigEdit: this.#canEditConfig,\n allowRead: this.#canRead,\n },\n }\n this.#connectAck.resolve()\n return payload\n }\n\n #onAbort = (): void => void this.#end({ retry: false })\n\n #onClose = (): void => void this.#end({ retry: true })\n\n /**\n * Drops the socket and detaches every listener and timer this session added. Idempotent, and\n * safe before `connect` opened anything.\n *\n * @internal\n */\n dispose(): void {\n clearTimeout(this.#heartbeatTimer)\n this.#heartbeatTimer = undefined\n this.#rpc?.close()\n this.#rpc = undefined\n this.#connectAck.reject(new Error('Session ended before Studio called connect()'))\n\n for (const unhook of this.#unhooks) unhook()\n this.#unhooks.length = 0\n }\n\n /**\n * Ends the session: tells Studio it is over, drops the socket, and optionally reconnects.\n * `#disposed` keeps the close event from running this twice, and a shutdown from reconnecting.\n */\n async #end({ retry }: { retry: boolean }): Promise<void> {\n const { studioUrl, token, logLevel } = this.#options\n\n if (this.#disposed) {\n return\n }\n this.#disposed = true\n\n this.dispose()\n\n await this.#hooks.callHook('studio:disconnected', { reason: retry ? 'connection closed' : 'shutdown' })\n\n // Nothing to tell Studio about when the session never opened.\n if (this.#session) {\n // Already tearing down, so a failed disconnect changes nothing.\n await disconnect({ sessionId: this.#session.sessionId, studioUrl, token, slug: this.#session.slug, logLevel }).catch(() => {})\n }\n\n if (retry) {\n reconnect(this.#options)\n }\n }\n\n startGeneration(data: GenerateInput): GenerationRun {\n const generationStream = createGenerationStream(this.#hooks, data.jobId, {\n onGenerationEnd: (result) => {\n this.#lastGeneration = result\n },\n })\n const controller = new AbortController()\n const result = this.#runGeneration(data, controller)\n .then(async (value) => {\n await generationStream.close()\n return value\n })\n .catch((error) => {\n generationStream.fail(error)\n throw error\n })\n // A dispose can reject this with nobody holding it, which would otherwise be unhandled.\n void result.catch(() => {})\n\n return new GenerationRunTarget(\n generationStream.stream,\n result,\n async () => {\n controller.abort(new Error('Generation canceled'))\n },\n () => {\n controller.abort(new Error('Generation canceled'))\n generationStream.dispose()\n },\n )\n }\n\n async #runGeneration(data: GenerateInput, controller: AbortController): Promise<GenerateResult> {\n // Checked before the first `await`, so two calls in the same tick can't both pass.\n if (this.#isGenerating) {\n return this.#refuse('Ignored generate: a generation is already in progress', 'A generation is already in progress, please wait for it to finish')\n }\n this.#isGenerating = true\n\n const command = 'generate'\n const { root, loadConfig, permissions, client } = this.#options\n\n try {\n await this.#hooks.callHook('studio:command:start', { command })\n const config = await loadConfig()\n const patch = data.config\n const plugins = await mergePlugins(config.plugins, patch?.plugins)\n const adapter = await mergeAdapter(config.adapter, patch?.adapter)\n\n // A sandbox agent always uses the inline spec (empty string included, since it has no disk\n // file); a local agent only when opted in, and an empty or absent spec falls back to disk.\n const inputOverride = this.#isSandbox ? (patch?.input ?? '') : (permissions.allowInput && patch?.input) || undefined\n\n if (permissions.allowWrite && this.#isSandbox) {\n await this.#warn('Running in a sandbox, so writing files is disabled')\n }\n\n if (patch?.input && !this.#canUseInput) {\n // The Docker agent reads `allowInput` from `KUBB_AGENT_ALLOW_INPUT`. The CLI grants it\n // through `--allowInput` or the per-project prompt instead, so each host gets its own remedy.\n const remedy = client?.kind === 'cli' ? '--allowInput, or answer yes when kubb studio asks,' : 'KUBB_AGENT_ALLOW_INPUT=true'\n await this.#warn(`Ignored the spec from Studio; set ${remedy} to generate from it`)\n }\n\n const resolvedPlugins = plugins ?? config.plugins\n\n // The session's own emitter carries the run: the host's logger is already on it from\n // `connect`, and these two come off again below, so one run's listeners never see the next.\n // Cleared up front, filled the moment `kubb:generation:end` fires.\n // Read before `generate` runs, since `fsStorage` overwrites these files. Kept as soon as it is\n // read, so a run that fails still leaves the last successful run as the one to compare with.\n if (this.#lastGeneration) {\n this.#previousGeneration = await readSnapshot(this.#lastGeneration)\n }\n this.#lastGeneration = undefined\n const detach = [setupHookListener(this.#hooks, root, controller.signal)]\n\n try {\n await generate({\n config: {\n ...config,\n root,\n input: inputOverride ?? config.input,\n storage: this.#canWrite ? fsStorage() : memoryStorage(),\n output: permissions.allowExec ? { ...config.output } : { ...config.output, format: false, lint: false, postGenerate: [] },\n plugins: resolvedPlugins,\n adapter,\n },\n hooks: this.#hooks,\n signal: controller.signal,\n })\n } finally {\n for (const remove of detach) remove()\n }\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `${resolvedPlugins.length} plugin${resolvedPlugins.length === 1 ? '' : 's'}, ${this.#canWrite ? 'written to disk' : 'in memory'}${inputOverride !== undefined ? ', from a Studio spec' : ''}`,\n })\n\n // The generate call above reassigns the field, but control flow analysis still sees the\n // `= undefined` from this method and narrows it to `never`.\n const generation = this.#lastGeneration as GenerationState | undefined\n const files = [...(generation?.paths ?? [])]\n const previous = this.#previousGeneration\n if (!generation || !previous) {\n return { status: 'success', files, fileCount: files.length }\n }\n\n return { status: 'success', files, fileCount: files.length, changes: diffSnapshots(previous, await readSnapshot(generation)) }\n } finally {\n this.#isGenerating = false\n }\n }\n\n async saveConfig(data: SaveConfigInput): Promise<SaveResult> {\n const command = 'saveConfig'\n await this.#hooks.callHook('studio:command:start', { command })\n const { configPath, configFile } = this.#options\n\n // Every RPC call gets one result. `edits` is checked before it is walked because values cross\n // the agent trust boundary.\n if (!Array.isArray(data.edits)) {\n await this.#warn('Ignored save: the message carried no edits')\n\n return { outcomes: [], changed: false }\n }\n\n const edits = data.edits\n const refuse = (reason: string): SaveResult => ({ outcomes: edits.map((edit) => ({ edit, applied: false, reason })), changed: false })\n\n if (!this.#canEditConfig) {\n await this.#warn('Ignored save: editing kubb.config.ts was not granted')\n\n return refuse('the agent was not granted permission to edit kubb.config.ts')\n }\n\n // A generation reloads the config while it runs, so rewriting the file underneath it would\n // leave that run working from half the change.\n if (this.#isGenerating) {\n return refuse('a generation is in progress')\n }\n\n try {\n // Read straight before the patch rather than reusing what went out on connect. The user may\n // have edited the file since, and since every untouched node keeps its own text, patching\n // what is on disk right now preserves that edit.\n const current = await read(configFile)\n const { source: patched, outcomes, changed } = applyConfigEdits(current, edits)\n\n if (changed) {\n // `writeFile` rather than the `write` helper: that one trims and re-terminates what it\n // writes, which is right for generated output and wrong for a file the user wrote by hand.\n await writeFile(configFile, patched, 'utf-8')\n }\n\n const applied = outcomes.filter((outcome) => outcome.applied).length\n await this.#hooks.callHook('studio:command:end', { command, info: `applied ${applied}/${outcomes.length} edits to ${configPath}` })\n return { outcomes, changed, file: changed ? await this.#readConfigFileView(patched) : undefined }\n } catch (error) {\n // An unreadable config, a read-only filesystem. Reported as a refusal of every edit so\n // Studio hears back rather than waiting on a reply that never comes.\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n return refuse(getErrorMessage(error))\n }\n }\n\n async publishSnapshot(data: PublishSnapshotInput): Promise<PublishSnapshotResult> {\n const command = 'snapshot'\n await this.#hooks.callHook('studio:command:start', { command })\n\n if (this.#isSandbox) {\n return this.#refuse('Ignored snapshot: a sandbox agent has no project to build a package from', 'A sandbox agent has no project to build a package from')\n }\n\n const { name, version, bundledDependencies, uploadPath } = data\n\n if (!name || !version || !uploadPath) {\n return this.#refuse('Ignored snapshot: the message was missing required fields', 'The request was missing required fields')\n }\n\n const generation = this.#lastGeneration\n\n if (!generation) {\n return this.#refuse('Ignored snapshot: no prior generation to pack', 'No prior generation exists to pack, run a generation first')\n }\n\n const bundled = new Set(bundledDependencies ?? [])\n const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency))\n\n if (missing.length) {\n return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(', ')}`, `Missing dependencies: ${missing.join(', ')}`)\n }\n\n try {\n const files: Record<string, string> = {}\n await inParallel({\n items: [...generation.paths],\n limit: FILE_READ_CONCURRENCY,\n run: async (relativePath) => {\n const content = await generation.storage.readItem(absoluteStoragePath(generation.root, relativePath))\n if (content !== null) {\n files[relativePath] = content\n }\n },\n })\n\n const { bytes, integrity } = await createSnapshotPackage(files, { name, version, peerDependencies: generation.peerDependencies })\n\n // The tarball can't go on this request: Studio's handler answers before reading the body,\n // so the connection drops mid-upload. Ask for the redirect with an empty body first, then\n // PUT the bytes to wherever it points. That also keeps the bearer token off the storage\n // request, since it's a fresh call rather than a followed redirect.\n const { token, studioUrl } = this.#options\n const uploadUrl = new URL(uploadPath, studioUrl)\n if (uploadUrl.origin !== new URL(studioUrl).origin) {\n throw new Error('Snapshot upload path must stay on the Studio origin')\n }\n const redirect = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${token}` },\n redirect: 'manual',\n })\n const storageUrl = redirect.headers.get('location')\n if (redirect.status !== 307 || !storageUrl) {\n throw new Error(`Studio did not provide a storage URL (status ${redirect.status})`)\n }\n const storage = new URL(storageUrl)\n if (storage.protocol !== 'https:' && storage.hostname !== 'localhost' && storage.hostname !== '127.0.0.1') {\n throw new Error(`Refusing snapshot upload to ${storage.origin}`)\n }\n const response = await fetch(storage, { method: 'PUT', body: new Uint8Array(bytes), redirect: 'error' })\n if (!response.ok) {\n throw new Error(`Snapshot upload failed with status ${response.status}`)\n }\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `packed ${Object.keys(files).length} file${Object.keys(files).length === 1 ? '' : 's'}`,\n })\n return { integrity, peerDependencies: generation.peerDependencies }\n } catch (error) {\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n async readFiles(data: ReadFilesInput): Promise<{ files: Record<string, string> }> {\n const command = 'readFiles'\n await this.#hooks.callHook('studio:command:start', { command })\n const { client } = this.#options\n\n if (!this.#canRead) {\n await this.#warn('Ignored files: reading generated files was not granted')\n\n // Each host grants it a different way.\n const remedy = client?.kind === 'cli' ? '--allow-read, or answer yes when kubb studio asks,' : 'KUBB_AGENT_ALLOW_READ=true'\n throw new Error(`The agent was not granted permission to read generated files; set ${remedy} to allow it`)\n }\n\n // `paths` came off the wire, so check its shape before walking it.\n if (!Array.isArray(data.paths)) {\n return this.#refuse('Ignored files: the message carried no paths', 'The request carried no paths')\n }\n\n const { paths } = data\n\n if (paths.length > MAX_FILES_PER_REQUEST) {\n return this.#refuse(\n `Ignored files: requested ${paths.length} paths, more than the ${MAX_FILES_PER_REQUEST} allowed per request`,\n `At most ${MAX_FILES_PER_REQUEST} paths may be requested at once`,\n )\n }\n\n if (data.revision === 'previous') {\n const previous = this.#previousGeneration\n\n if (!previous) {\n return this.#refuse('Ignored files: no previous generation to read from', 'No previous generation to compare against')\n }\n\n const files = Object.fromEntries(paths.filter((path) => previous.has(path)).map((path) => [path, previous.get(path) as string]))\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? '' : 's'} from the previous run`,\n })\n return { files }\n }\n\n const generation = this.#lastGeneration\n\n if (!generation) {\n return this.#refuse('Ignored files: no prior generation to read from', 'No prior generation to read from, run a generation first')\n }\n\n // Checked against the paths this run actually produced before touching storage, so a caller\n // can only ever read what that run produced, never an arbitrary path on disk.\n const requested = paths.filter((path) => generation.paths.has(path))\n const files: Record<string, string> = {}\n await inParallel({\n items: requested,\n limit: FILE_READ_CONCURRENCY,\n run: async (path) => {\n const content = await generation.storage.readItem(absoluteStoragePath(generation.root, path))\n if (content !== null) {\n files[path] = content\n }\n },\n })\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? '' : 's'}`,\n })\n return { files }\n }\n}\n","import type { Storage } from 'unstorage'\nimport { agentDefaults } from './constants.ts'\nimport type { InvalidAgentTokenError } from './api.ts'\nimport { registerAgent } from './api.ts'\nimport { StudioSession, type StudioSessionOptions } from './StudioSession.ts'\nimport { setStorage } from './machine.ts'\n\nexport type ClientOptions = Omit<StudioSessionOptions, 'signal' | 'onTokenRejected'> & {\n /**\n * Where the machine secret and the last Studio config are persisted. Defaults to in-memory,\n * which gives up a stable machine identity across restarts.\n */\n storage?: Storage\n /**\n * Called once when a live pool's token is rejected during background reconnect (401: revoked, or\n * the agent was deleted). The whole pool is already stopped by the time this fires, so a host\n * only needs to get a replacement token and start a new client.\n *\n * Never fires for a startup rejection, which `connect()` reports by throwing, nor for an ordinary\n * session expiry or revocation, both of which reconnect on their own.\n */\n onAuthRequired?: (error: InvalidAgentTokenError) => void\n}\n\nexport type Client = {\n /**\n * Registers with Studio and opens the session pool. Resolves once the pool is starting: the\n * sessions keep running, and reconnect on their own, until `disconnect` is called.\n */\n connect: () => Promise<void>\n /**\n * Closes every session and stops reconnecting.\n */\n disconnect: () => void\n}\n\n/**\n * Creates the Kubb Studio client: the connection, the command loop, and the generation event\n * stream shared by the `kubb studio` CLI command and the Docker agent.\n *\n * Every permission is off by default. A host that wants more grants it explicitly.\n *\n * @example\n * ```ts\n * const studio = createClient({ token, configPath, version, loadConfig: () => loadMyConfig() })\n * await studio.connect()\n * ```\n */\nexport function createClient({ storage, onAuthRequired, ...options }: ClientOptions): Client {\n if (storage) {\n setStorage(storage)\n }\n\n const controller = new AbortController()\n const poolSize = options.poolSize ?? agentDefaults.poolSize\n function notifyAuthRequired(error: InvalidAgentTokenError) {\n // Several pool sessions can reject the same token at once, and a host can stop the pool\n // itself, so an aborted controller is what says this callback is spent.\n if (controller.signal.aborted) {\n return\n }\n\n // Stop the whole pool first: every session's socket closes and every pending retry timer is\n // canceled through the `signal` each one already listens on, so the caller starts its next\n // client from a clean slate.\n controller.abort()\n onAuthRequired?.(error)\n }\n\n return {\n async connect() {\n await registerAgent({ token: options.token, studioUrl: options.studioUrl ?? agentDefaults.studioUrl, poolSize })\n\n // Each slot is its own session, so one Studio user never sees another's generation events.\n // Awaited: `connect()` only ever rejects with `InvalidAgentTokenError` (every other failure\n // is retried internally through the session's own reconnect loop and resolves normally), so\n // awaiting here surfaces a dead token to the caller without blocking on a down Studio.\n await Promise.all(\n Array.from({ length: poolSize }, () => new StudioSession({ ...options, signal: controller.signal, onTokenRejected: notifyAuthRequired }).start()),\n )\n },\n disconnect() {\n controller.abort()\n },\n }\n}\n","import { InvalidAgentTokenError } from './api.ts'\nimport { type ClientOptions, createClient } from './client.ts'\n\n/**\n * Why a connection ended: the host asked it to stop through its `signal`, or the host declined to\n * replace a rejected token.\n */\nexport type ConnectionOutcome = 'shutdown' | 'stopped'\n\n/**\n * A rejected token, and whether it was already serving a live session when Studio rejected it.\n */\nexport type TokenRejection<TCredentials> = {\n error: InvalidAgentTokenError\n /**\n * The credential Studio rejected, so a host can carry parts of it into the replacement.\n */\n credentials: TCredentials\n /**\n * `false` when the token was dead before a session ever opened, which is what `connect()` itself\n * reports. `true` when a live pool's background reconnect was rejected, well after the session\n * was up. Hosts treat the two differently: only the first has nothing to tear down.\n */\n live: boolean\n}\n\nexport type ConnectionOptions<TCredentials extends { token: string }> = {\n /**\n * The credential to open with. Only its token is read here, so a host keeps whatever else it\n * stores alongside.\n */\n credentials: TCredentials\n /**\n * Builds the client options for one attempt. Called again for every reconnect, so a host whose\n * options depend on which agent approved, such as the permissions it granted, re-derives them\n * rather than reusing the ones the rejected token was opened with.\n */\n clientOptions: (credentials: TCredentials) => Omit<ClientOptions, 'token' | 'onAuthRequired'>\n /**\n * Called when Studio rejects the token. Return the credential to reconnect with, or `null` to\n * end the run. Throwing fails it, which is what a host does when it cannot pair again.\n */\n onTokenRejected: (rejection: TokenRejection<TCredentials>) => Promise<TCredentials | null>\n /**\n * Aborting this disconnects and ends the run. Hosts wire it to their own shutdown: `SIGINT` in\n * the CLI, Nitro's `close` hook in the Docker agent.\n */\n signal?: AbortSignal\n}\n\n/**\n * Waits for whichever comes first: the shutdown signal, or Studio rejecting the token during a\n * background reconnect. Resolves with the rejection, or nothing when the run is being shut down.\n */\nfunction waitForRejection(authRequired: Promise<InvalidAgentTokenError>, signal?: AbortSignal): Promise<InvalidAgentTokenError | undefined> {\n // Nothing to race without a signal: a host without one ends the run some other way.\n if (!signal) {\n return authRequired\n }\n\n // `{ once: true }` drops the listener when the abort fires, not when the other side settles the\n // race, so `settled` covers that half. Without it a reconnected run leaves one behind on the\n // host's signal for every attempt it makes.\n const settled = new AbortController()\n const shutdown = new Promise<undefined>((resolve) => {\n if (signal.aborted) {\n resolve(undefined)\n return\n }\n signal.addEventListener('abort', () => resolve(undefined), { once: true, signal: settled.signal })\n })\n\n return Promise.race([shutdown, authRequired]).finally(() => settled.abort())\n}\n\n/**\n * Keeps a host connected to Studio across token changes: it opens a client, waits until the run\n * ends or Studio rejects the token, and reconnects with whatever credential the host hands back.\n *\n * The host owns everything around that. Where credentials live, whether a rejected token may be\n * replaced, and how any of it is reported are all decisions `onTokenRejected` makes.\n *\n * @example\n * ```ts\n * const outcome = await runConnection({\n * credentials,\n * clientOptions: () => ({ studioUrl, configPath, version, loadConfig }),\n * signal: shutdown.signal,\n * onTokenRejected: ({ error, live }) => pairAgain(error, live),\n * })\n * ```\n */\nexport async function runConnection<TCredentials extends { token: string }>({\n credentials,\n clientOptions,\n onTokenRejected,\n signal,\n}: ConnectionOptions<TCredentials>): Promise<ConnectionOutcome> {\n let current = credentials\n\n while (true) {\n // A shutdown can land outside the race below, while a host is pairing or prompting. Registering\n // one more agent with Studio only to drop it again is not what the operator asked for.\n if (signal?.aborted) {\n return 'shutdown'\n }\n\n const { promise: authRequired, resolve: notifyAuthRequired } = Promise.withResolvers<InvalidAgentTokenError>()\n const client = createClient({ ...clientOptions(current), token: current.token, onAuthRequired: notifyAuthRequired })\n\n let rejection: TokenRejection<TCredentials> | undefined\n\n try {\n await client.connect()\n\n const error = await waitForRejection(authRequired, signal)\n\n if (!error) {\n return 'shutdown'\n }\n\n rejection = { error, credentials: current, live: true }\n } catch (error) {\n // Every other failure is retried inside the session's own reconnect loop, so anything that\n // surfaces here is a dead token or a host's own bug.\n if (!(error instanceof InvalidAgentTokenError)) {\n throw error\n }\n\n rejection = { error, credentials: current, live: false }\n } finally {\n client.disconnect()\n }\n\n const next = await onTokenRejected(rejection)\n\n if (!next) {\n return 'stopped'\n }\n\n current = next\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { styleText } from 'node:util'\nimport { getErrorMessage } from '@internals/utils'\nimport { ofetch } from 'ofetch'\nimport { agentDefaults } from './constants.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * RFC 8628 device-authorization response from Studio's `/api/auth/device/code` endpoint.\n * Field names match the RFC; the CLI polls with `device_code` and shows `user_code` to the user.\n */\nexport type PairingSession = {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\nexport type PairingResult = {\n /**\n * Bearer token for this machine. Write credentials with mode 0600 and never log the value.\n */\n token: string\n agent: {\n /**\n * Stable agent id in Studio.\n */\n id: string\n /**\n * Short slug used in logs and the UI (for example `brave-otter`).\n */\n slug: string\n /**\n * Display name chosen at pairing time.\n */\n name: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none.\n */\n organizationSlug?: string\n }\n}\n\n/**\n * Identifies the CLI to Studio's device authorization endpoint. A label, not a secret: what\n * authorizes a pairing is a signed-in person approving the code in the browser.\n */\nconst CLIENT_ID = 'kubb-cli'\n\n/**\n * Thrown when a caller aborts `startPairing` or `pollForPairingToken` through their `signal`, such\n * as a `kubb studio` shutdown mid-pairing. Distinct from a denial or an expired code, so a host can\n * exit quietly instead of reporting a pairing failure.\n */\nexport class PairingCanceledError extends Error {\n constructor() {\n super('Pairing was canceled')\n this.name = 'PairingCanceledError'\n }\n}\n\ntype StartPairingOptions = {\n studioUrl?: string\n /**\n * Display name for the agent, usually the project or machine name.\n */\n name: string\n hostname: string\n /**\n * Which client is pairing. Defaults to the CLI, where any signed-in member may approve their own\n * machine. The Docker image passes `kubb-agent`, whose codes only an admin can approve.\n */\n clientId?: string\n /**\n * What a `kubb-agent` pairing asks to be registered as. Studio rejects the request without it,\n * and ignores it for the CLI.\n */\n agentKind?: 'user' | 'sandbox'\n /**\n * Aborting this cancels the request in flight and rejects with {@link PairingCanceledError}.\n */\n signal?: AbortSignal\n}\n\n/**\n * Asks Studio for a pairing code. The machine token travels with the request and is stored against\n * the code, so approval knows which machine it is pairing: the same machine pairing twice rotates\n * one agent's token instead of creating a second agent.\n */\nexport async function startPairing({\n studioUrl = agentDefaults.studioUrl,\n name,\n hostname,\n clientId = CLIENT_ID,\n agentKind,\n signal,\n}: StartPairingOptions): Promise<PairingSession> {\n try {\n return await ofetch<PairingSession>(`${studioUrl}/api/auth/device/code`, {\n method: 'POST',\n body: {\n client_id: clientId,\n name,\n hostname,\n machine_token: await getMachineToken(),\n agent_kind: agentKind,\n },\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n throw error\n }\n}\n\ntype PollOptions = {\n studioUrl?: string\n session: PairingSession\n /**\n * Aborting this stops polling and rejects with {@link PairingCanceledError}, whether the abort\n * lands between polls or during the wait for the next one.\n */\n signal?: AbortSignal\n}\n\ntype PollError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied' | 'invalid_grant'\n\ntype PollResponse =\n | PairingResult\n | {\n error: PollError | string\n /**\n * Why, when Studio has something more useful to say than the RFC code. An approval that hits\n * the organization's agent limit comes back as `access_denied` with the limit spelled out.\n */\n error_description?: string\n }\n\nfunction isPairingResult(response: PollResponse | undefined): response is PairingResult {\n return !!response && typeof response === 'object' && 'token' in response && typeof response.token === 'string'\n}\n\n/**\n * Polls until the user approves or denies, honoring the server's `slow_down` back-off. A poll that\n * cannot reach Studio is warned about and retried, since the code stays valid either way.\n *\n * Studio's own endpoint is used rather than the auth layer's `/device/token`, because an approved\n * Kubb pairing is worth an agent bearer token, not a user session.\n *\n * @throws when the code expires, the user denies it, or Studio returns an unexpected error.\n */\nexport async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, session, signal }: PollOptions): Promise<PairingResult> {\n // Both fields cross the network, so neither is trusted as-is: a missing or zero `interval` would\n // spin the poll loop, and a missing or zero `expires_in` would expire the code before the first\n // poll. `> 0` is also false for `NaN` and for a missing field, so it doubles as the type guard.\n const deadline = Date.now() + (session.expires_in > 0 ? session.expires_in : 600) * 1000\n let intervalMs = (session.interval > 0 ? session.interval : 5) * 1000\n\n while (Date.now() < deadline) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n try {\n await delay(intervalMs, undefined, { signal })\n } catch {\n throw new PairingCanceledError()\n }\n\n let response: PollResponse | undefined\n try {\n response = await ofetch<PollResponse | undefined>(`${studioUrl}/api/agent/token`, {\n method: 'POST',\n body: { device_code: session.device_code },\n // A denial, an expiry, and \"not yet\" all come back as 4xx with a body the caller needs to\n // read, so let every response through and switch on `error` instead of catching.\n ignoreResponseError: true,\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n // Studio can go briefly unreachable (a deploy, a dropped connection) during the minutes the\n // user has to approve in the browser. One failed poll should not end a pairing whose code is\n // still valid, so warn and try again on the next tick, the way `registerAgent` retries.\n console.warn(styleText('yellow', `Could not reach Kubb Studio while waiting for approval, retrying: ${getErrorMessage(error)}`))\n continue\n }\n\n if (isPairingResult(response)) {\n return response\n }\n\n if (!response || typeof response !== 'object' || !('error' in response) || typeof response.error !== 'string') {\n throw new Error('Kubb Studio returned an empty pairing response, pair again')\n }\n\n if (response.error === 'authorization_pending') {\n continue\n }\n\n if (response.error === 'slow_down') {\n intervalMs += 5_000\n continue\n }\n\n if (response.error === 'access_denied') {\n throw new Error(response.error_description ?? 'Pairing was denied in the browser')\n }\n\n if (response.error === 'expired_token' || response.error === 'invalid_grant') {\n throw new Error(response.error_description ?? 'The pairing code expired, pair again')\n }\n\n throw new Error(response.error_description ?? `Pairing failed (${response.error})`)\n }\n\n throw new Error('The pairing code expired, pair again')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["detectTool","process","logLevel","logLevelMap","detectUncachedTool","process","process","logLevel","logLevelMap","kubbVersion","version","delay"],"sources":["../src/constants.ts","../../../internals/utils/src/casing.ts","../../../internals/utils/src/errors.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../../../internals/utils/src/promise.ts","../../../internals/utils/src/tools.ts","../../../internals/utils/src/time.ts","../src/machine.ts","../src/api.ts","../package.json","../src/hooks.ts","../src/resolveConfig.ts","../src/configFile.ts","../src/generate.ts","../src/snapshotPackage.ts","../src/generations.ts","../src/ws.ts","../src/rpc.ts","../src/StudioSession.ts","../src/client.ts","../src/runConnection.ts","../src/pair.ts"],"sourcesContent":["/**\n * Hosted Kubb Studio URL. Exported so credential stores can bind tokens to the resolved instance,\n * not whatever default the client would pick on its own.\n */\nexport const defaultStudioUrl = 'https://kubb.studio'\n\n/**\n * Defaults the Studio client uses when a host passes nothing.\n * Config path is left out on purpose: each host discovers that itself.\n */\nexport const agentDefaults = {\n studioUrl: defaultStudioUrl,\n retryIntervalMs: 30_000,\n heartbeatIntervalMs: 30_000,\n /**\n * Slowest heartbeat a host may ask for. Studio drops an agent from the active list once its\n * stored ping is older than its liveness window, and it stores a ping at most once a minute, so\n * a slower cadence would make a healthy agent look dead after a single missed ping.\n */\n maxHeartbeatIntervalMs: 60_000,\n /** How long a heartbeat ping may take before the session is treated as dead. */\n heartbeatTimeoutMs: 10_000,\n poolSize: 1,\n maxGenerations: 8,\n maxGenerationsMb: 100,\n maxSnapshotMb: 50,\n} as const\n\nfunction positiveNumber(value: string | undefined): number | undefined {\n const parsed = Number(value)\n return value && Number.isFinite(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/**\n * How many generations an agent keeps and how large they may get, read from\n * `KUBB_AGENT_MAX_GENERATIONS`, `KUBB_AGENT_MAX_GENERATIONS_MB` and `KUBB_AGENT_MAX_SNAPSHOT_MB`.\n * An unset or invalid value keeps the default.\n */\nexport function resolveGenerationLimits(env: NodeJS.ProcessEnv = process.env): { maxCount: number; maxMb: number; maxSnapshotMb: number } {\n return {\n maxCount: Math.max(1, Math.floor(positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS) ?? agentDefaults.maxGenerations)),\n maxMb: positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS_MB) ?? agentDefaults.maxGenerationsMb,\n maxSnapshotMb: positiveNumber(env.KUBB_AGENT_MAX_SNAPSHOT_MB) ?? agentDefaults.maxSnapshotMb,\n }\n}\n","type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new Error('Build failed', { cause: toError(err) })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","import { spawn } from 'node:child_process'\n\n/**\n * How one formatter or linter is invoked: the executable, the argv it takes for an output\n * directory, and what to report when it is not installed.\n */\nexport type ToolCommand = {\n command: string\n args: (outputPath: string) => Array<string>\n errorMessage: string\n}\n\n/**\n * CLI command descriptors for each supported code formatter.\n */\nexport const formatters = {\n prettier: {\n command: 'prettier',\n args: (outputPath: string) => ['--ignore-unknown', '--write', outputPath],\n errorMessage: 'Prettier not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['format', '--write', outputPath],\n errorMessage: 'Biome not found',\n },\n oxfmt: {\n command: 'oxfmt',\n args: (outputPath: string) => [outputPath],\n errorMessage: 'Oxfmt not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * CLI command descriptors for each supported linter.\n */\nexport const linters = {\n eslint: {\n command: 'eslint',\n args: (outputPath: string) => [outputPath, '--fix'],\n errorMessage: 'Eslint not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['lint', '--fix', outputPath],\n errorMessage: 'Biome not found',\n },\n oxlint: {\n command: 'oxlint',\n // --no-ignore so oxlint lints the folder even when it's gitignored (generated output dirs usually are).\n args: (outputPath: string) => ['--fix', '--no-ignore', outputPath],\n errorMessage: 'Oxlint not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * Preference order for `format: 'auto'`, most-preferred first. Spelled out rather than taken from\n * the table's key order, which is arbitrary and would silently change what `auto` picks.\n */\nexport const FORMATTER_PREFERENCE = ['oxfmt', 'biome', 'prettier'] as const\n\n/**\n * Preference order for `lint: 'auto'`, most-preferred first.\n */\nexport const LINTER_PREFERENCE = ['oxlint', 'biome', 'eslint'] as const\n\n/**\n * Whether `name` is on PATH and answers `--version` with a zero exit.\n */\nexport function isToolAvailable(name: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(name, ['--version'], { stdio: 'ignore' })\n child.on('close', (code) => resolve(code === 0))\n child.on('error', () => resolve(false))\n })\n}\n\n/**\n * Returns the first installed executable from `candidates`, or `null` when none are found.\n *\n * Not memoized: a long-running host that probes repeatedly should cache the result itself, and a\n * `--watch` build should keep noticing a tool installed mid-session.\n */\nexport async function detectTool<TName extends string>(candidates: ReadonlyArray<TName>): Promise<TName | null> {\n for (const candidate of candidates) {\n if (await isToolAvailable(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Tokenizes a shell command string, respecting single and double quotes.\n *\n * @example\n * ```ts\n * tokenize('git commit -m \"initial commit\"')\n * // → ['git', 'commit', '-m', 'initial commit']\n * ```\n */\nexport function tokenize(command: string): Array<string> {\n return (command.match(/[^\\s\"']+|\"([^\"]*)\"|'([^']*)'/g) ?? []).map((token) => token.replace(/^[\"']|[\"']$/g, ''))\n}\n","/**\n * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.\n * Rounds to 2 decimal places for sub-millisecond precision without noise.\n *\n * @example\n * ```ts\n * const start = process.hrtime()\n * doWork()\n * getElapsedMs(start) // 42.35\n * ```\n */\nexport function getElapsedMs(hrStart: [number, number]): number {\n const [seconds, nanoseconds] = process.hrtime(hrStart)\n const ms = seconds * 1000 + nanoseconds / 1e6\n return Math.round(ms * 100) / 100\n}\n\n/**\n * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).\n *\n * @example\n * ```ts\n * formatMs(250) // '250ms'\n * formatMs(1500) // '1.50s'\n * formatMs(90000) // '1m 30.0s'\n * ```\n */\nexport function formatMs(ms: number): string {\n if (ms >= 60000) {\n const mins = Math.floor(ms / 60000)\n const secs = ((ms % 60000) / 1000).toFixed(1)\n return `${mins}m ${secs}s`\n }\n\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(2)}s`\n }\n return `${Math.round(ms)}ms`\n}\n","import { hash, randomBytes } from 'node:crypto'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { createStorage, type Storage } from 'unstorage'\nimport fsDriver from 'unstorage/drivers/fs'\n\n/**\n * Key-value storage the runtime uses for its machine secret and the last Studio config.\n *\n * One storage per process, since one process serves one config file. Hosts install their own\n * driver on startup: Nitro passes its `kubb` mount, the CLI an fs driver under `~/.kubb/cache`.\n * The in-memory default keeps the runtime usable without a host, at the cost of a machine\n * identity that changes on every restart.\n */\nlet storage: Storage = createStorage()\nlet hasInstalledStorage = false\n\n/**\n * Installs the storage driver the runtime persists to. Call once, before connecting.\n */\nexport function setStorage(next: Storage): void {\n storage = next\n hasInstalledStorage = true\n}\n\n/**\n * A storage backed by files under `base`, so the machine secret and the last Studio config\n * survive a restart. Repeated pairings of one machine depend on that secret staying put.\n */\nexport function createFileStorage(base: string): Storage {\n return createStorage({ driver: fsDriver({ base }) })\n}\n\nlet fallbackSecretPromise: Promise<string> | null = null\n\n/**\n * Loads the fallback machine secret from the runtime storage.\n * On first use it generates a secret and persists it, so the machine identity stays\n * stable across restarts. An identity that changes on every boot breaks session\n * creation with Studio whenever the startup registration call fails.\n */\nasync function loadOrCreateFallbackSecret(): Promise<string> {\n // The secret is memoized for the life of the process, so a host that reads the machine token\n // before installing its storage is bound to the throwaway in-memory default. Nothing else\n // surfaces that: the write succeeds, and the identity silently changes on every restart, which\n // Studio rejects with a 403 on the next session create.\n if (!hasInstalledStorage) {\n console.warn(\n styleText('yellow', 'Deriving the machine token before a storage driver was installed'),\n 'call setStorage() first, or set KUBB_AGENT_SECRET, to keep a stable machine identity across restarts',\n )\n }\n\n const stored = await storage.getItem('machine-secret').catch(() => null)\n\n if (typeof stored === 'string' && stored) {\n return stored\n }\n\n const secret = randomBytes(32).toString('hex')\n\n await storage.setItem('machine-secret', secret).catch(() => {\n console.warn(\n styleText('yellow', 'Could not persist the generated machine secret'),\n 'set KUBB_AGENT_SECRET to keep a stable machine identity across restarts',\n )\n })\n\n return secret\n}\n\n/**\n * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its\n * own identity without duplicating `getMachineToken`'s SHA-256 step.\n */\nexport function machineTokenFrom(secret: string): string {\n return hash('sha256', secret)\n}\n\n/**\n * Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.\n * Falls back to a generated secret persisted in the runtime storage if the env var is not set.\n * The token is hashed with SHA-256.\n */\nexport async function getMachineToken(): Promise<string> {\n if (process.env.KUBB_AGENT_SECRET) {\n return machineTokenFrom(process.env.KUBB_AGENT_SECRET)\n }\n\n fallbackSecretPromise ??= loadOrCreateFallbackSecret()\n\n return machineTokenFrom(await fallbackSecretPromise)\n}\n","import { styleText } from 'node:util'\nimport { getErrorMessage } from '@internals/utils'\nimport { logLevel as logLevelMap } from '@kubb/core'\nimport { FetchError, ofetch } from 'ofetch'\nimport type { AgentConnectResponse } from './protocol/index.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * Reads a human-readable message from a Studio JSON error body, when it has one. `FetchError`'s own\n * message stops at the status line, so the detail Studio sends with a failure (an agent limit, a\n * revoked token) would otherwise never reach the user.\n */\nfunction responseMessage(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') {\n return undefined\n }\n\n const body = data as { error_description?: unknown; message?: unknown; error?: unknown }\n for (const value of [body.error_description, body.message, body.error]) {\n if (typeof value === 'string' && value) {\n return value\n }\n }\n\n return undefined\n}\n\n/**\n * Retries after the first registration attempt, each backing off twice as far as the last.\n */\nconst REGISTER_RETRIES = 3\n\n/**\n * Shared in-flight registration so concurrent pool sessions trigger one purge, not N.\n */\nlet registrationInFlight: Promise<boolean> | null = null\n\ntype ConnectProps = {\n studioUrl: string\n token: string\n}\n\n/**\n * Thrown when Studio rejects the agent token itself (401). Retrying cannot help: the token was\n * revoked, or the agent it belonged to was deleted in the Studio UI. Hosts catch this to forget\n * the stored credential and pair again.\n */\nexport class InvalidAgentTokenError extends Error {\n constructor(studioUrl: string, options?: ErrorOptions) {\n super(`Kubb Studio rejected this agent's token. It was revoked or the agent was deleted in ${studioUrl}.`, options)\n this.name = 'InvalidAgentTokenError'\n }\n}\n\n/**\n * Whether a thrown value carries `statusCode`. Not narrowed to `FetchError`: a host wrapper can\n * throw its own error shape with the same field.\n *\n * A 401 means the agent token itself was rejected. A 403 from the session create endpoint means\n * the machine token stored in Studio no longer matches this agent (missing or mismatched).\n */\nfunction rejectedWith(error: unknown, statusCode: number): boolean {\n return (error as { statusCode?: number } | undefined)?.statusCode === statusCode\n}\n\nfunction sessionError(cause: unknown): Error {\n const detail = (cause instanceof FetchError ? responseMessage(cause.data) : undefined) ?? getErrorMessage(cause)\n return new Error(detail ? `Failed to get agent session from Kubb Studio: ${detail}` : 'Failed to get agent session from Kubb Studio', { cause })\n}\n\n/**\n * Performs the raw session create request against Studio.\n */\nasync function requestAgentSession({ token, studioUrl }: ConnectProps): Promise<AgentConnectResponse> {\n const url = `${studioUrl}/api/agent/sessions`\n\n const data = await ofetch<AgentConnectResponse>(url, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body: { machineToken: await getMachineToken() },\n })\n\n if (!data) {\n throw new Error('No data available for agent session')\n }\n\n return data\n}\n\n/**\n * Obtain an agent session token from Kubb Studio via HTTP.\n *\n * When Studio rejects the machine token (403), for example after the agent restarted\n * with a new identity while the startup registration call failed, the agent re-registers\n * and retries once, so a single failed registration can't permanently block session creation.\n */\nexport async function createAgentSession({ token, studioUrl }: ConnectProps): Promise<AgentConnectResponse> {\n try {\n return await requestAgentSession({ token, studioUrl })\n } catch (error: unknown) {\n if (rejectedWith(error, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: error })\n }\n\n if (!rejectedWith(error, 403) || !(await registerAgent({ token, studioUrl }))) {\n throw sessionError(error)\n }\n\n try {\n return await requestAgentSession({ token, studioUrl })\n } catch (retryError: unknown) {\n if (rejectedWith(retryError, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: retryError })\n }\n\n throw sessionError(retryError)\n }\n }\n}\n\ntype RegisterProps = {\n studioUrl: string\n token: string\n poolSize?: number\n}\n\n/**\n * Register this agent with Kubb Studio by sending the machine ID.\n * Called on agent startup before creating a WebSocket session, and again when\n * Studio rejects the machine token during session creation.\n *\n * Retries with backoff because a failed registration leaves Studio with a stale\n * machine token that blocks every subsequent session create call. Registration\n * purges all of the agent's sessions on the Studio side, so concurrent callers\n * (multiple pool sessions hitting a 403 at once) share one in-flight run instead\n * of purging each other's fresh sessions.\n */\nexport function registerAgent(props: RegisterProps): Promise<boolean> {\n registrationInFlight ??= runRegistration(props).finally(() => {\n registrationInFlight = null\n })\n\n return registrationInFlight\n}\n\nasync function runRegistration({ token, studioUrl, poolSize }: RegisterProps): Promise<boolean> {\n const machineToken = await getMachineToken()\n\n try {\n await ofetch(`${studioUrl}/api/agent/connect`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n body: { machineToken, poolSize },\n retry: REGISTER_RETRIES,\n // 2s, 4s, then 8s. `retry` counts down, so the first retry is the one with the most left.\n retryDelay: ({ options }) => 2_000 * 2 ** (REGISTER_RETRIES - Number(options.retry)),\n })\n\n return true\n } catch (error) {\n if (rejectedWith(error, 401)) {\n throw new InvalidAgentTokenError(studioUrl, { cause: error })\n }\n\n console.error(styleText('red', `Failed to register agent with Studio after ${REGISTER_RETRIES + 1} attempts`))\n\n return false\n }\n}\n\ntype DisconnectProps = {\n studioUrl: string\n token: string\n sessionId: string\n slug?: string | null\n /**\n * Threshold for this function's own console lines, using the numeric constants `@kubb/core`\n * exports as `logLevel`. Left out, nothing prints, the same silent default `StudioSessionOptions`\n * gives a host that never set one.\n */\n logLevel?: number\n}\n\n/**\n * Notify Kubb Studio that this agent is disconnecting.\n * Called on process termination or server close. A failed notify is logged and swallowed: the\n * local socket is already gone, and failing teardown must not block shutdown or reconnect.\n */\nexport async function disconnect({ sessionId, token, studioUrl, slug, logLevel }: DisconnectProps): Promise<void> {\n const url = `${studioUrl}/api/agent/sessions/${sessionId}/disconnect`\n const tag = slug ?? 'agent'\n const canLog = logLevel !== undefined && logLevel > logLevelMap.silent\n\n try {\n await ofetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n })\n // console.error, not console.log: a CI runner only forwards a child process's stderr live, so\n // a stdout write here would be silently buffered away instead of reaching its log.\n if (canLog) {\n console.error(styleText('green', `[${tag}] Disconnected from Studio`))\n }\n } catch (error) {\n const statusCode = (error as { statusCode?: number } | undefined)?.statusCode\n if (statusCode !== undefined && statusCode >= 400 && statusCode < 500) return\n\n if (canLog) {\n console.warn(styleText('yellow', `[${tag}] Failed to notify Studio of disconnection: ${getErrorMessage(error)}`))\n }\n }\n}\n\n/**\n * Status values returned by Studio's jobs API.\n */\nexport type StudioJobStatus = 'queued' | 'running' | 'success' | 'failed' | 'canceled'\n\n/**\n * Package view returned on a successful snapshot job from Studio.\n */\nexport type StudioSnapshot = {\n /**\n * Immutable snapshot id.\n */\n id: string\n /**\n * npm package name, or `null` when Studio stored none.\n */\n name: string | null\n /**\n * npm package version, or `null` when Studio stored none.\n */\n version: string | null\n /**\n * Subresource integrity hash for the tarball, or `null` when unavailable.\n */\n integrity: string | null\n /**\n * Preferable download path, often the readable `/packages/{agentSlug}/{name}.tgz` form.\n */\n url: string\n /**\n * Stable download path keyed by snapshot id.\n */\n snapshotIdUrl: string\n /**\n * ISO timestamp after which Studio may delete the tarball.\n */\n expiresAt: string\n}\n\n/**\n * Job record from `POST /api/jobs` and `GET /api/jobs/{id}`.\n */\nexport type StudioJob = {\n /**\n * Job id returned by Studio when the job was queued.\n */\n id: string\n /**\n * Current status. Poll until `success`, `failed`, or `canceled`.\n */\n status: StudioJobStatus\n /**\n * Failure message when `status` is `failed`.\n */\n error?: string\n /**\n * Package view when a snapshot job finished successfully.\n */\n snapshot?: StudioSnapshot\n}\n\n/**\n * Queues a generation or snapshot job on Studio (`POST /api/jobs`).\n *\n * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.\n * Authenticates with the organization CI API key via `x-api-key`.\n *\n * @example Snapshot job\n * ```ts\n * const job = await createJob({\n * studioUrl: 'https://kubb.studio',\n * token: process.env.KUBB_TOKEN!,\n * type: 'snapshot',\n * agentId: agent.id,\n * name: '@kubb/demo',\n * version: '1.0.0',\n * })\n * const finished = await waitForJob({ studioUrl, token, id: job.id })\n * ```\n */\nexport async function createJob({\n studioUrl,\n token,\n type,\n agentId,\n name,\n version,\n config,\n}: {\n studioUrl: string\n token: string\n type: 'generation' | 'snapshot'\n agentId: string\n name?: string\n version?: string\n config?: Record<string, unknown>\n}): Promise<StudioJob> {\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { type, agentId, name, version, config },\n })\n\n return job\n}\n\n/**\n * A job runs a generation and packs a tarball, so it is never done the instant it is queued.\n */\nconst INITIAL_POLL_DELAY_MS = 2_000\n\n/**\n * Slowest the poll backs off to. Requests per run are roughly `timeoutMs` divided by this, and\n * every concurrent run on the same organization key draws on one budget.\n */\nconst MAX_POLL_INTERVAL_MS = 30_000\n\n/**\n * Polls `GET /api/jobs/{id}` until the job reaches a terminal status, waiting\n * {@link INITIAL_POLL_DELAY_MS} first and doubling up to {@link MAX_POLL_INTERVAL_MS} so a long\n * job stays inside the API key's rate limit.\n *\n * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the\n * deadline passes before Studio finishes.\n */\nexport async function waitForJob({\n studioUrl,\n token,\n id,\n timeoutMs = 60_000,\n}: {\n studioUrl: string\n token: string\n id: string\n /**\n * How long to keep polling before throwing, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = INITIAL_POLL_DELAY_MS\n\n for (;;) {\n await new Promise((resolve) => setTimeout(resolve, Math.max(Math.min(interval, deadline - Date.now()), 0)))\n\n if (Date.now() >= deadline) throw new Error('Timed out waiting for the Studio job')\n\n interval = Math.min(interval * 2, MAX_POLL_INTERVAL_MS)\n\n try {\n // ofetch retries a 429 immediately, which spends the rate limit faster than not retrying.\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs/${id}`, {\n headers: { 'x-api-key': token },\n retry: false,\n })\n\n if (job.status === 'success' || job.status === 'failed' || job.status === 'canceled') return job\n } catch (error) {\n const response = (error as { response?: { status?: number; _data?: { data?: { tryAgainIn?: unknown } } } }).response\n\n if (response?.status !== 429) throw error\n\n const retryAfter = response._data?.data?.tryAgainIn\n const usable = typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter > 0\n\n // Studio's wait may exceed the ceiling, and a refusal must never shorten the next poll.\n interval = Math.max(interval, usable ? retryAfter : MAX_POLL_INTERVAL_MS)\n }\n }\n}\n\n/**\n * CI agent returned by {@link createAgent}. The token is issued only once, at creation or reuse.\n */\nexport type StudioAgent = {\n /**\n * Agent id, passed to {@link createJob} as `agentId`.\n */\n id: string\n /**\n * Human-readable slug, used to build the readable snapshot URL and the agent's Studio page.\n */\n slug: string\n /**\n * Agent display name.\n */\n name: string\n /**\n * Bearer token for the WebSocket agent session. Mask it before logging.\n */\n token: string\n}\n\n/**\n * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.\n * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of\n * consuming a new one from the organization's agent limit.\n */\nexport async function createAgent({\n studioUrl,\n token,\n name,\n machineToken,\n}: {\n studioUrl: string\n token: string\n name: string\n machineToken: string\n}): Promise<StudioAgent> {\n try {\n return await ofetch<StudioAgent>(`${studioUrl}/api/agents`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { name, machineToken },\n })\n } catch (error: unknown) {\n if (error instanceof FetchError) {\n const upgradeUrl = (error.data as { data?: { upgradeUrl?: string } } | undefined)?.data?.upgradeUrl\n const detail = responseMessage(error.data) ?? getErrorMessage(error)\n const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : ''\n throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error })\n }\n\n throw error\n }\n}\n","","import type { Hookable, KubbHooks } from '@kubb/core'\nimport { x } from 'tinyexec'\n\n/**\n * Events a host emits about its Kubb Studio session, as opposed to a generation. `kubb:` stays\n * reserved for generation lifecycle.\n */\nexport type StudioConnectingContext = {\n /**\n * The Studio instance this session is opening against.\n */\n url: string\n}\n\nexport type StudioConnectedContext = {\n /**\n * The Studio instance this session attached to.\n */\n url: string\n /**\n * Both sides of the connection, so a host can print them and make a mismatch visible.\n */\n versions: {\n /**\n * The Studio instance's own version, when it sent one.\n */\n studio?: string\n /**\n * The version of the runtime that connected.\n */\n kubb: string\n /**\n * The version of the host itself, such as the `kubb` CLI or the agent image.\n */\n agent: string\n }\n /**\n * This agent's slug, refreshed on every connect so a rename in Studio shows up without a\n * re-pair. Absent when Studio predates the field.\n */\n agentSlug?: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none, or when\n * Studio predates the field.\n */\n organizationSlug?: string\n}\n\n/**\n * Fired once Studio confirms the `agent:connect` handshake was received and the session is fully\n * registered. Distinct from `studio:connected`, which only means the socket is open.\n */\nexport type StudioReadyContext = Record<string, never>\n\nexport type StudioDisconnectedContext = {\n /**\n * Why Studio ended the session.\n */\n reason: string\n}\n\nexport type StudioCommandStartContext = {\n /**\n * The command Studio sent, without its `studio:` prefix: `generate`, `connect` or `save`.\n */\n command: string\n}\n\nexport type StudioCommandEndContext = {\n /**\n * The command that finished, without its `studio:` prefix.\n */\n command: string\n /**\n * What the command did, when there is something to report: `applied 2/3 edits to kubb.config.ts`.\n */\n info?: string\n}\n\nexport type StudioWarnContext = {\n /**\n * What was refused or ignored, and what would change it.\n */\n message: string\n}\n\nexport type StudioErrorContext = {\n /**\n * The failure, for the host's own output. One Studio needs to hear about goes over the socket\n * through the `kubb:error` generation hook instead.\n */\n error: Error\n}\n\ndeclare global {\n namespace Kubb {\n interface KubbHooksRegistry {\n 'studio:connecting': [ctx: StudioConnectingContext]\n 'studio:connected': [ctx: StudioConnectedContext]\n 'studio:ready': [ctx: StudioReadyContext]\n 'studio:disconnected': [ctx: StudioDisconnectedContext]\n 'studio:command:start': [ctx: StudioCommandStartContext]\n 'studio:command:end': [ctx: StudioCommandEndContext]\n 'studio:warn': [ctx: StudioWarnContext]\n 'studio:error': [ctx: StudioErrorContext]\n }\n }\n}\n\n/**\n * Register a `kubb:hook:start` listener that spawns the requested command via tinyexec,\n * streams each stdout line as a `kubb:hook:line` event, and calls `kubb:hook:end` with the result.\n * Streaming the output lets Kubb Studio render live hook progress over the WebSocket connection.\n *\n * Returns a remover, so a session that runs one generation after another on the same emitter does\n * not stack a listener per run.\n */\nexport function setupHookListener(hooks: Hookable<KubbHooks>, root: string, signal?: AbortSignal): () => void {\n return hooks.hook('kubb:hook:start', async (ctx) => {\n const { id, command, args } = ctx\n // No id means nothing is waiting on the result (benchmarks, tests).\n if (!id) {\n return\n }\n\n const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command\n\n try {\n const proc = x(command, [...(args ?? [])], {\n signal,\n nodeOptions: { cwd: root, detached: true },\n })\n\n for await (const line of proc) {\n await hooks.callHook('kubb:hook:line', { id, line })\n }\n\n const { exitCode } = await proc\n\n if (exitCode !== 0) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n await hooks.callHook('kubb:error', { error })\n\n return\n }\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: true, error: null })\n } catch (caughtError) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n error.cause = caughtError\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n await hooks.callHook('kubb:error', { error })\n }\n })\n}\n\n/**\n * Waits for the `kubb:hook:end` matching `hookId`. Register this before calling `kubb:hook:start`:\n * `callHook` awaits its listeners, and {@link setupHookListener} calls `kubb:hook:end` from inside\n * that same listener, so a handler added afterward would already have missed it.\n */\nexport function waitForHookEnd(hooks: Hookable<KubbHooks>, hookId: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const handleHookEnd = (ctx: { id?: string; success: boolean; error?: Error | null }) => {\n if (ctx.id !== hookId) return\n hooks.removeHook('kubb:hook:end', handleHookEnd)\n\n if (ctx.success) {\n resolve()\n return\n }\n reject(ctx.error)\n }\n\n hooks.hook('kubb:hook:end', handleHookEnd)\n })\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { pathToFileURL } from 'node:url'\nimport type { Adapter, Plugin } from '@kubb/core'\nimport { camelCase } from '@internals/utils'\nimport { mergeDeep } from 'remeda'\nimport type { JSONKubbConfig } from './protocol/index.ts'\n\n/**\n * Turns the JSON config Studio sends back into live Kubb objects.\n *\n * A plugin or adapter instance carries closures (`parse`, `getImports`, ...) that cannot survive\n * JSON, so both sides pass options over the wire and the factory is re-invoked here with the merged\n * result. Only `@kubb/plugin-*` packages are resolved this way, so the reinstantiated factory is\n * always one Kubb ships, never an arbitrary module the payload names.\n */\n\ntype PluginFactory = (options: unknown) => Plugin\n\n/**\n * Imports a package, falling back to how the user's project would resolve it.\n *\n * `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,\n * `npm i -g`) only sees its own `node_modules` and misses the plugins installed next to the user's\n * config. The retry resolves from `process.cwd()` instead.\n */\nasync function importFromProject(packageName: string): Promise<Record<string, unknown>> {\n try {\n return await import(packageName)\n } catch {\n const require = createRequire(pathToFileURL(`${process.cwd()}/`))\n // `require.resolve` picks the package's `require` condition, so prefer the ESM build sitting\n // next to it. Loading the CJS copy would pull in a second `@kubb/core` instance.\n const resolved = require.resolve(packageName)\n const esm = resolved.replace(/\\.cjs$/, '.js')\n\n return await import(pathToFileURL(esm !== resolved && existsSync(esm) ? esm : resolved).href)\n }\n}\n\n/**\n * Strips the `@kubb/` scope from a plugin package name, matching the `name` convention Kubb\n * plugin factories use internally.\n *\n * @example\n * ```ts\n * toPluginName('@kubb/plugin-ts') // 'plugin-ts'\n * ```\n */\nfunction toPluginName(packageName: string): string {\n return packageName.split('/').pop() ?? packageName\n}\n\n/**\n * Adds the `@kubb/` scope a plugin's package carries but its `name` does not, the inverse of\n * {@link toPluginName}. A name outside the `plugin-` convention is left alone, so a third-party\n * plugin is not reported as one of Kubb's.\n *\n * @example\n * ```ts\n * toPackageName('plugin-ts') // '@kubb/plugin-ts'\n * toPackageName('@acme/my-plugin') // '@acme/my-plugin'\n * ```\n */\nexport function toPackageName(name: string): string {\n return name.startsWith('plugin-') ? `@kubb/${name}` : name\n}\n\n/**\n * Derives the conventional named export for a `@kubb/*` plugin package from its package name.\n *\n * @example\n * ```ts\n * toExportName('@kubb/plugin-react-query') // 'pluginReactQuery'\n * toExportName('@kubb/plugin-ts') // 'pluginTs'\n * ```\n */\nexport function toExportName(packageName: string): string {\n return camelCase(toPluginName(packageName))\n}\n\n/**\n * A `@kubb/plugin-*` package specifier. Nothing else may reach `import()`: only Kubb's own\n * plugins are supported, so a payload naming anything else, a third-party package or a path, is\n * refused before it can execute.\n */\nconst KUBB_PLUGIN_SPECIFIER = /^@kubb\\/plugin-[\\w.-]+$/\n\n/**\n * Whether `name` is a `@kubb/plugin-*` specifier. Exported so `configFile.ts` can refuse the same\n * shape before printing a Studio-supplied plugin name into the config file's source text.\n */\nexport function isKubbPluginSpecifier(name: string): boolean {\n return KUBB_PLUGIN_SPECIFIER.test(name)\n}\n\n/**\n * Dynamically imports a `@kubb/plugin-*` package and returns its factory function.\n *\n * Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`\n * build ARG, no runtime installation is possible in the distroless container.\n *\n * Resolution order: the camelCase named export the package name implies (e.g. `pluginTs`), then\n * the default export.\n *\n * @throws if the package cannot be imported or exports no callable factory.\n */\nasync function loadPluginFactory(packageName: string): Promise<PluginFactory> {\n if (!isKubbPluginSpecifier(packageName)) {\n throw new Error(`Plugin \"${packageName}\" is not a @kubb/plugin-* package. Kubb Studio only supports Kubb's own plugins.`)\n }\n\n let mod: Record<string, unknown>\n try {\n mod = await importFromProject(packageName)\n } catch (cause) {\n throw new Error(`Plugin \"${packageName}\" could not be loaded. Make sure it is installed: \\`npm install ${packageName}\\``, { cause })\n }\n\n const exportName = toExportName(packageName)\n\n if (typeof mod[exportName] === 'function') return mod[exportName] as PluginFactory\n\n if (typeof mod['default'] === 'function') return mod['default'] as PluginFactory\n\n throw new Error(`Plugin \"${packageName}\" does not export a callable factory. Tried the named export \"${exportName}\" and \"default\".`)\n}\n\n/**\n * Resolves each plugin entry by dynamically importing the `@kubb/plugin-*` package and\n * calling its factory with the provided options.\n *\n * Packages must be pre-installed in the Docker image at build time, use the `KUBB_PACKAGES`\n * build ARG to control which ones are available at runtime.\n *\n * @example\n * ```ts\n * { name: '@kubb/plugin-react-query', options: { output: { path: './hooks' } } }\n * { name: '@kubb/plugin-ts', options: { output: { path: './types' } } }\n * ```\n */\nexport async function resolvePlugins(plugins: NonNullable<JSONKubbConfig['plugins']>): Promise<Array<Plugin>> {\n return Promise.all(\n plugins.map(async ({ name, options }) => {\n const factory = await loadPluginFactory(name)\n return factory(options ?? {}) as Plugin\n }),\n )\n}\n\n/**\n * Merges studio plugin options with disk config plugins.\n * Studio takes priority: options from studio win over disk, and a plugin Studio explicitly\n * disabled is dropped even when the disk config still lists it. Disk plugins without a studio\n * counterpart are kept as-is. Studio plugins not present on disk are appended.\n *\n * For plugins present in both configs, the plugin is re-instantiated with merged options\n * so that all internal closures correctly reference the merged values.\n */\nexport async function mergePlugins(\n diskPlugins: Array<Plugin> | undefined,\n studioPlugins: JSONKubbConfig['plugins'] | undefined,\n): Promise<Array<Plugin> | undefined> {\n // Matched on the package's base name rather than by instantiating first. Every Kubb plugin\n // factory returns exactly that (`@kubb/plugin-ts` → `plugin-ts`), enforced by the `satisfies` on\n // each factory's name.\n const disabledNames = new Set((studioPlugins ?? []).filter((entry) => entry.disabled).map((entry) => toPluginName(entry.name)))\n const activeDiskPlugins = disabledNames.size ? diskPlugins?.filter((plugin) => !disabledNames.has(plugin.name)) : diskPlugins\n const activeStudioPlugins = studioPlugins?.filter((entry) => !entry.disabled)\n\n if (!activeDiskPlugins && !activeStudioPlugins?.length) return undefined\n if (!activeStudioPlugins?.length) return activeDiskPlugins\n\n if (!activeDiskPlugins) return resolvePlugins(activeStudioPlugins)\n\n const studioEntryByName = new Map(activeStudioPlugins.map((entry) => [toPluginName(entry.name), entry] as const))\n const diskNames = new Set(activeDiskPlugins.map((plugin) => plugin.name))\n\n // Each plugin is instantiated once, with its final options. Resolving the whole payload first\n // just to read the names would build every overlapping plugin twice and discard the first.\n const merged = await Promise.all(\n activeDiskPlugins.map(async (diskPlugin) => {\n const studioEntry = studioEntryByName.get(diskPlugin.name)\n if (!studioEntry) return diskPlugin\n\n // Disk as base, studio overrides, then re-instantiate so the plugin's closures reference the\n // merged values. A plugin that never sets `options` (e.g. `@kubb/plugin-barrel`) leaves\n // `diskPlugin.options` undefined, which `mergeDeep` can't accept.\n const options = mergeDeep((diskPlugin.options as Record<string, unknown>) ?? {}, (studioEntry.options as Record<string, unknown>) ?? {})\n const [resolved] = await resolvePlugins([{ name: studioEntry.name, options }])\n\n return resolved ?? diskPlugin\n }),\n )\n\n const studioOnly = activeStudioPlugins.filter((entry) => !diskNames.has(toPluginName(entry.name)))\n\n return [...merged, ...(await resolvePlugins(studioOnly))]\n}\n\n/**\n * Merges Studio-provided adapter option overrides into the disk config's adapter.\n *\n * Adapter instances carry live functions (`parse`, `getImports`, ...) that can't survive\n * JSON serialization over the WebSocket, so `studioOptions` is treated as an options patch\n * rather than a replacement adapter. Re-invokes the same `@kubb/adapter-<name>` factory the\n * disk config used, with the merged options, so the resulting instance has fresh closures\n * over the merged values instead of a plain object missing `parse`.\n */\nexport async function mergeAdapter(diskAdapter: Adapter | undefined, studioOptions: object | undefined): Promise<Adapter | undefined> {\n if (!studioOptions || !diskAdapter) {\n return diskAdapter\n }\n\n const packageName = `@kubb/adapter-${diskAdapter.name}`\n const mod = await importFromProject(packageName)\n const factory = mod[toExportName(packageName)]\n\n if (typeof factory !== 'function') {\n return diskAdapter\n }\n\n const mergedOptions = mergeDeep((diskAdapter.options as Record<string, unknown>) ?? {}, studioOptions as Record<string, unknown>)\n\n return factory(mergedOptions) as Adapter\n}\n","import { builders, detectCodeFormat, generateCode, parseModule } from 'magicast'\nimport type { ASTNode, ProxifiedModule } from 'magicast'\nimport type { ConfigEdit, ConfigEditOutcome, ConfigFileView, ConfigRef, ConfigView, OptionValue, PluginView } from './protocol/index.ts'\nimport { isKubbPluginSpecifier, toExportName } from './resolveConfig.ts'\n\n/**\n * A valid JavaScript identifier, so an import name can only ever print as `import { name } from`,\n * never as source that breaks out of the import statement.\n */\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/\n\n/**\n * A config or plugin options object literal in the file.\n */\ntype ObjectNode = Extract<ASTNode, { type: 'ObjectExpression' }>\n\n/**\n * A `pluginX(...)` call in a config's `plugins` array.\n */\ntype CallNode = Extract<ASTNode, { type: 'CallExpression' }>\n\n/**\n * A `key: value` entry of an object literal.\n */\ntype ObjectPropertyNode = Extract<ASTNode, { type: 'ObjectProperty' }>\n\n/**\n * `key: value` as an object literal property, in the file's quote and key style.\n *\n * Uses magicast's literal builder for the key/value nodes, then wraps them as a Babel\n * `ObjectProperty`, the type the rest of this file reads.\n */\nfunction literalProperty({ key, value }: { key: string; value: OptionValue }): ObjectPropertyNode {\n const built = (builders.literal({ [key]: value }) as unknown as ObjectNode).properties[0] as unknown as ObjectPropertyNode\n return { type: 'ObjectProperty', key: built.key, value: built.value, computed: false, shorthand: false }\n}\n\n/**\n * What `applyConfigEdits` did to a config file.\n */\ntype ApplyResult = {\n /**\n * The file's text after every applicable edit, unchanged from the input when none applied.\n */\n source: string\n /**\n * One entry per edit, in the order they were given.\n */\n outcomes: Array<ConfigEditOutcome>\n /**\n * Whether `source` differs from the input.\n */\n changed: boolean\n}\n\n/**\n * Marks the comment block a `disable-plugin` leaves behind, so `enable-plugin` can find its way\n * back to the exact lines it commented out. Carries the block's line count, so `enable-plugin`\n * restores exactly those lines instead of scanning forward through whatever comments follow.\n */\nconst DISABLED_MARKER = 'kubb:disabled'\n\n/**\n * The one line `disable-plugin` writes above the comment block it produces for `plugin`.\n */\nfunction formatMarker(plugin: string, lineCount: number, indent = ''): string {\n return `${indent}// ${DISABLED_MARKER} ${plugin} ${lineCount}`\n}\n\n/**\n * The plugin and comment-block length a marker line names, when `line` is one.\n */\nfunction parseMarker(line: string): { plugin: string; lineCount: number } | undefined {\n const trimmed = line.trim()\n if (!trimmed.startsWith(`// ${DISABLED_MARKER} `)) {\n return undefined\n }\n\n const match = trimmed.slice(`// ${DISABLED_MARKER} `.length).match(/^(.+)\\s+(\\d+)$/)\n return match ? { plugin: match[1]!, lineCount: Number(match[2]) } : undefined\n}\n\n/**\n * Steps through a config's wrappers to the object literal underneath: a `satisfies`/`as`\n * assertion, a `() => ...` factory, or a factory whose block body returns the config.\n */\nfunction unwrap(node: ASTNode | null | undefined): ASTNode | undefined {\n if (!node) {\n return undefined\n }\n if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') {\n return unwrap(node.expression)\n }\n if (node.type !== 'ArrowFunctionExpression' && node.type !== 'FunctionExpression') {\n return node\n }\n if (node.body.type !== 'BlockStatement') {\n return unwrap(node.body)\n }\n\n const returned = node.body.body.find((statement): statement is Extract<ASTNode, { type: 'ReturnStatement' }> => statement.type === 'ReturnStatement')\n return unwrap(returned?.argument)\n}\n\n/**\n * Every config object in `export default defineConfig(...)`, or why the file is unmanaged.\n *\n * An array export gets one entry per element, matching {@link ConfigRef}'s numeric index.\n *\n * Walks the parsed AST rather than magicast's proxies, which throw on node types they cannot\n * cast, most of what an unmanaged config file is made of.\n */\nfunction findConfigs(mod: ProxifiedModule): { configs: Array<ObjectNode> } | { reason: string } {\n const body = mod.$ast.type === 'Program' ? mod.$ast.body : []\n const declaration = body.find((node): node is Extract<ASTNode, { type: 'ExportDefaultDeclaration' }> => node.type === 'ExportDefaultDeclaration')\n\n const exported = unwrap(declaration?.declaration)\n if (!exported) {\n return { reason: 'no default export found' }\n }\n if (exported.type !== 'CallExpression' || exported.callee.type !== 'Identifier' || exported.callee.name !== 'defineConfig') {\n return { reason: 'default export is not a defineConfig(...) call' }\n }\n\n const argument = unwrap(exported.arguments[0])\n if (!argument) {\n return { reason: 'defineConfig(...) was called without a config' }\n }\n if (argument.type === 'ArrayExpression') {\n const configs: Array<ObjectNode> = []\n\n for (const element of argument.elements) {\n const entry = unwrap(element)\n if (entry?.type !== 'ObjectExpression') {\n return { reason: 'config is not an object literal' }\n }\n configs.push(entry)\n }\n return { configs }\n }\n if (argument.type !== 'ObjectExpression') {\n return { reason: 'config is not an object literal' }\n }\n return { configs: [argument] }\n}\n\n/**\n * The config entry an edit names, defaulting to the first when it names none.\n */\nfunction selectConfig(configs: Array<ObjectNode>, ref: ConfigRef | undefined): ObjectNode | undefined {\n if (ref === undefined) {\n return configs[0]\n }\n if (typeof ref === 'number') {\n return configs[ref]\n }\n return configs.find((config) => configName(config) === ref)\n}\n\nfunction configName(config: ObjectNode): string | undefined {\n const name = property(config, 'name')\n return name?.type === 'StringLiteral' ? name.value : undefined\n}\n\n/**\n * The name of an object literal property, for the two key shapes a config uses: `key: value` and\n * `'key': value`. `undefined` for a computed key, which the patcher never touches.\n */\nfunction propertyKey(entry: Extract<ASTNode, { type: 'ObjectProperty' }>): string | undefined {\n if (entry.key.type === 'Identifier') {\n return entry.key.name\n }\n if (entry.key.type === 'StringLiteral') {\n return entry.key.value\n }\n return undefined\n}\n\n/**\n * The index of an object literal's own property named `key`, `-1` when it has none.\n */\nfunction entryIndex({ node, key }: { node: ObjectNode; key: string }): number {\n return node.properties.findIndex((entry) => entry.type === 'ObjectProperty' && propertyKey(entry) === key)\n}\n\n/**\n * The value node of an object literal's own property.\n */\nfunction property(node: ObjectNode, key: string): ASTNode | undefined {\n const index = entryIndex({ node, key })\n return index === -1 ? undefined : (node.properties[index] as ObjectPropertyNode).value\n}\n\n/**\n * Writes `key: value` on an object literal, replacing the value when the property is already there.\n *\n * An existing property has its value swapped in place rather than being replaced whole, so recast\n * reprints only that value and leaves the object's own layout alone.\n */\nfunction setProperty({ node, key, value }: { node: ObjectNode; key: string; value: OptionValue }): void {\n const entry = literalProperty({ key, value })\n const index = entryIndex({ node, key })\n\n if (index === -1) {\n node.properties.push(entry)\n return\n }\n ;(node.properties[index] as ObjectPropertyNode).value = entry.value\n}\n\n/**\n * Drops `key` from an object literal.\n */\nfunction removeProperty({ node, key }: { node: ObjectNode; key: string }): void {\n const index = entryIndex({ node, key })\n if (index !== -1) {\n node.properties.splice(index, 1)\n }\n}\n\n/**\n * Reads a literal node's value: a primitive, or an object/array built only from primitives.\n * `undefined` for anything else, so a caller can use this both to read a value and to check\n * whether a node is a literal at all.\n */\nfunction readLiteral(node: ASTNode | undefined): OptionValue | undefined {\n if (!node) {\n return undefined\n }\n if (node.type === 'StringLiteral' || node.type === 'NumericLiteral' || node.type === 'BooleanLiteral') {\n return node.value\n }\n if (node.type === 'NullLiteral') {\n return null\n }\n if (node.type === 'TemplateLiteral') {\n return node.expressions.length === 0 ? (node.quasis[0]?.value.cooked ?? '') : undefined\n }\n if (node.type === 'UnaryExpression') {\n const value = readLiteral(node.argument)\n if (typeof value !== 'number') {\n return undefined\n }\n if (node.operator === '-') {\n return -value\n }\n if (node.operator === '+') {\n return value\n }\n return undefined\n }\n if (node.type === 'ArrayExpression') {\n const values = node.elements.map((element) => (element ? readLiteral(element) : undefined))\n return values.every((value) => value !== undefined) ? values : undefined\n }\n if (node.type === 'ObjectExpression') {\n const entries: Record<string, OptionValue> = {}\n for (const entry of node.properties) {\n if (entry.type !== 'ObjectProperty') {\n return undefined\n }\n const key = propertyKey(entry)\n const value = readLiteral(entry.value)\n if (key === undefined || value === undefined) {\n return undefined\n }\n entries[key] = value\n }\n return entries\n }\n return undefined\n}\n\n/**\n * Maps a factory identifier in the file back to the module it was imported from.\n */\nfunction importedFrom(mod: ProxifiedModule): Map<string, string> {\n return new Map(mod.imports.$items.map((item) => [item.local, item.from]))\n}\n\n/**\n * Every `pluginX(...)` element of a config's plugins array that resolves to an import.\n */\nfunction pluginCalls(mod: ProxifiedModule, config: ObjectNode): Array<{ importName: string; packageName: string; call: CallNode }> {\n const plugins = property(config, 'plugins')\n if (plugins?.type !== 'ArrayExpression') {\n return []\n }\n\n const imports = importedFrom(mod)\n\n return plugins.elements.flatMap((element) => {\n if (element?.type !== 'CallExpression' || element.callee.type !== 'Identifier') {\n return []\n }\n const packageName = imports.get(element.callee.name)\n return packageName ? [{ importName: element.callee.name, packageName, call: element }] : []\n })\n}\n\n/**\n * Plugins a previous `disable-plugin` commented out of this config, keyed by package name.\n *\n * Read from the marker lines rather than the AST, since a commented-out call is no longer a node.\n */\nfunction disabledMarkers(source: string): Array<{ packageName: string; line: number }> {\n return source.split('\\n').flatMap((line, index) => {\n const marker = parseMarker(line)\n return marker ? [{ packageName: marker.plugin, line: index + 1 }] : []\n })\n}\n\n/**\n * Reads which plugins the file declares and which of their options Studio may write.\n *\n * @example\n * ```ts\n * const view = readConfig(await readFile('kubb.config.ts', 'utf8'))\n * if (view.managed) {\n * view.configs.forEach((config) => console.log(config.name, config.plugins.length))\n * }\n * ```\n */\nexport function readConfig(source: string): ConfigFileView {\n let mod: ProxifiedModule\n try {\n mod = parseModule(source)\n } catch {\n return { managed: false, reason: 'the config file could not be parsed' }\n }\n\n const found = findConfigs(mod)\n if ('reason' in found) {\n return { managed: false, reason: found.reason }\n }\n\n const importNames = new Map([...importedFrom(mod)].map(([local, from]) => [from, local]))\n const disabled = disabledMarkers(source)\n\n return {\n managed: true,\n configs: found.configs.map((config): ConfigView => {\n const plugins = pluginCalls(mod, config).map(({ importName, packageName, call }): PluginView => {\n const entries: PluginView['options'] = {}\n const options = call.arguments[0]\n\n if (options?.type === 'ObjectExpression') {\n for (const entry of options.properties) {\n if (entry.type !== 'ObjectProperty') {\n continue\n }\n const key = propertyKey(entry)\n if (key === undefined) {\n continue\n }\n const value = readLiteral(entry.value)\n entries[key] = value === undefined ? { literal: false } : { literal: true, value }\n }\n }\n return { importName, packageName, options: entries }\n })\n\n const start = config.loc?.start.line ?? 0\n const end = config.loc?.end.line ?? Number.POSITIVE_INFINITY\n\n for (const { packageName } of disabled.filter((entry) => entry.line >= start && entry.line <= end)) {\n plugins.push({\n importName: importNames.get(packageName) ?? toExportName(packageName),\n packageName,\n options: {},\n disabled: true,\n })\n }\n\n return { name: configName(config), plugins }\n }),\n }\n}\n\n/**\n * Whether a value can be written into a config file as a literal.\n *\n * This is the trust boundary for edits that arrive over the agent WebSocket: a function, `undefined`,\n * or a non-finite number is refused rather than printed into the user's source.\n */\nexport function isOptionValue(value: unknown): value is OptionValue {\n if (value === null) {\n return true\n }\n if (typeof value === 'string' || typeof value === 'boolean') {\n return true\n }\n if (typeof value === 'number') {\n return Number.isFinite(value)\n }\n if (Array.isArray(value)) {\n return value.every(isOptionValue)\n }\n if (typeof value === 'object') {\n return Object.values(value).every(isOptionValue)\n }\n return false\n}\n\n/**\n * The options object of a plugin call, when it was called with one.\n */\nfunction getOptions(call: CallNode): ObjectNode | undefined {\n const options = call.arguments[0]\n return options?.type === 'ObjectExpression' ? options : undefined\n}\n\n/**\n * The options object of a plugin call, creating an empty one when the plugin was called bare.\n */\nfunction ensureOptions(call: CallNode): ObjectNode | undefined {\n if (call.arguments.length === 0) {\n call.arguments.push({ type: 'ObjectExpression', properties: [] })\n }\n return getOptions(call)\n}\n\n/**\n * Walks `path` down to the object holding its last key, descending only through object literals.\n */\nfunction optionParent(options: ObjectNode, path: Array<string>): { object: ObjectNode; key: string } | { reason: string } {\n let object = options\n\n for (const [index, key] of path.entries()) {\n if (index === path.length - 1) {\n return { object, key }\n }\n\n if (property(object, key) === undefined) {\n setProperty({ node: object, key, value: {} })\n }\n\n const next = property(object, key)\n if (next?.type !== 'ObjectExpression') {\n return { reason: `${key} is not an object, so ${path.join('.')} cannot be reached` }\n }\n object = next\n }\n return { reason: 'no option path given' }\n}\n\n/**\n * Writes `value` at `path` inside a plugin call's options, creating the options object and any\n * intermediate object along the path as needed. Refuses when the current value at `path` is\n * something other than a literal, so an option customized in code is never overwritten.\n */\nfunction applySet(call: CallNode, path: Array<string>, value: unknown): string | undefined {\n if (!isOptionValue(value)) {\n return 'the value is not a literal that can be written to a config file'\n }\n\n const options = ensureOptions(call)\n if (!options) {\n return 'the plugin was not called with an object literal'\n }\n\n const target = optionParent(options, path)\n if ('reason' in target) {\n return target.reason\n }\n\n const current = property(target.object, target.key)\n if (current !== undefined && readLiteral(current) === undefined) {\n return `${path.join('.')} is customized in code`\n }\n\n setProperty({ node: target.object, key: target.key, value })\n return undefined\n}\n\n/**\n * Deletes the property at `path` inside a plugin call's options, falling the plugin back to its\n * default for that option. Refuses when the value at `path` is not a literal, for the same reason\n * `applySet` does.\n */\nfunction applyRemove(call: CallNode, path: Array<string>): string | undefined {\n const options = getOptions(call)\n if (!options) {\n return 'the plugin has no options to remove'\n }\n\n const target = optionParent(options, path)\n if ('reason' in target) {\n return target.reason\n }\n\n const current = property(target.object, target.key)\n if (current === undefined) {\n return `${path.join('.')} is not set`\n }\n if (readLiteral(current) === undefined) {\n return `${path.join('.')} is customized in code`\n }\n\n removeProperty({ node: target.object, key: target.key })\n return undefined\n}\n\n/**\n * Outcome of `applyAddPlugin`. `addImport` is set when the new plugin call needs an import line\n * the caller must still insert; absent when the import was already there.\n */\ntype AddPluginResult = { reason: string } | { noop: true } | { addImport?: { importName: string; moduleSpecifier: string } }\n\n/**\n * Adds a `pluginX(...)` call to a config's plugins array. Replaying the same add is a no-op, while\n * an import name collision with an unrelated package remains an error.\n */\nfunction applyAddPlugin(mod: ProxifiedModule, config: ObjectNode, edit: Extract<ConfigEdit, { operation: 'add-plugin' }>): AddPluginResult {\n if (!isKubbPluginSpecifier(edit.plugin)) {\n return { reason: `\"${edit.plugin}\" is not a @kubb/plugin-* package` }\n }\n\n const importName = edit.importName ?? toExportName(edit.plugin)\n if (!IDENTIFIER.test(importName)) {\n return { reason: `\"${importName}\" is not a valid import name` }\n }\n\n if (pluginCalls(mod, config).some((plugin) => plugin.packageName === edit.plugin)) {\n return { noop: true }\n }\n\n const taken = importedFrom(mod).get(importName)\n if (taken && taken !== edit.plugin) {\n return { reason: `${importName} is already imported from ${taken}` }\n }\n\n const options = edit.options ?? {}\n if (!isOptionValue(options)) {\n return { reason: 'the options are not literals that can be written to a config file' }\n }\n\n const plugins = property(config, 'plugins')\n if (plugins?.type !== 'ArrayExpression') {\n return { reason: 'plugins is not an array literal' }\n }\n\n const call = Object.keys(options).length ? builders.functionCall(importName, options) : builders.functionCall(importName)\n plugins.elements.push(call.$ast as CallNode)\n\n return taken ? {} : { addImport: { importName, moduleSpecifier: edit.plugin } }\n}\n\n/**\n * Comments out a plugin call in place, keeping its options on disk so `enable-plugin` can restore\n * them exactly. Operates on `source` text rather than the AST: a commented-out call is no longer a\n * node magicast can address, and the surrounding array must not reflow when its element count\n * never actually changes.\n */\nfunction disablePlugin(source: string, mod: ProxifiedModule, config: ObjectNode, plugin: string): { source: string } | { reason: string } {\n const target = pluginCalls(mod, config).find((entry) => entry.packageName === plugin)\n if (!target) {\n return { reason: `${plugin} is not in the plugins array` }\n }\n\n const loc = target.call.loc\n if (!loc?.start || !loc.end) {\n return { reason: `${plugin} has no source location to comment out` }\n }\n\n const lines = source.split('\\n')\n const from = loc.start.line - 1\n const to = loc.end.line - 1\n const firstLine = lines[from] ?? ''\n const lastLine = lines[to] ?? ''\n\n // Only safe to comment out when the call sits alone on its lines: anything else sharing the\n // first line before it, or the last line after it besides a trailing comma, would be swallowed\n // into the comment along with the call, corrupting the file.\n if (firstLine.slice(0, loc.start.column).trim() !== '' || !/^,?\\s*$/.test(lastLine.slice(loc.end.column))) {\n return { reason: `${plugin} shares a line with other code, so it cannot be commented out safely` }\n }\n\n const indent = firstLine.match(/^\\s*/)?.[0] ?? ''\n const commented = lines.slice(from, to + 1).map((line) => (line.trim() ? `${indent}// ${line.slice(indent.length)}` : indent ? `${indent}//` : '//'))\n lines.splice(from, to - from + 1, formatMarker(plugin, commented.length, indent), ...commented)\n\n return { source: lines.join('\\n') }\n}\n\n/**\n * Uncomments the block a previous `disable-plugin` left behind for `plugin`.\n */\nfunction enablePlugin(source: string, plugin: string): { source: string } | { reason: string } {\n const lines = source.split('\\n')\n\n for (const [index, line] of lines.entries()) {\n const marker = parseMarker(line)\n if (marker?.plugin !== plugin) {\n continue\n }\n\n // Bounded by the marker's own line count rather than scanning for trailing `//` lines, so a\n // comment or another disabled block right after this one is left untouched.\n const end = index + 1 + marker.lineCount\n const restored = lines.slice(index + 1, end).map((commented) => commented.replace(/^(\\s*)\\/\\/ ?/, '$1'))\n lines.splice(index, end - index, ...restored)\n\n return { source: lines.join('\\n') }\n }\n\n return { reason: `${plugin} is not disabled` }\n}\n\n/**\n * Re-parses `source` and resolves the config entry an edit targets. Every edit re-parses rather\n * than sharing one module across the batch, since the disable/enable edits rewrite `source` as\n * text and would otherwise leave the others working from a stale tree.\n */\nfunction parseTarget(source: string, ref: ConfigRef | undefined): { mod: ProxifiedModule; config: ObjectNode } | { reason: string } {\n let mod: ProxifiedModule\n try {\n mod = parseModule(source)\n } catch {\n return { reason: 'the config file could not be parsed' }\n }\n\n const found = findConfigs(mod)\n if ('reason' in found) {\n return { reason: found.reason }\n }\n\n const config = selectConfig(found.configs, ref)\n if (!config) {\n return { reason: `no config entry found for ${JSON.stringify(ref)}` }\n }\n\n return { mod, config }\n}\n\n/**\n * Applies edits to a `kubb.config.ts` in place. Every node the edits do not touch keeps its\n * original text, so comments, formatting, and hand-written code around the config survive.\n *\n * Edits are independent: one that cannot be applied is reported in `outcomes` and the rest still run.\n *\n * @example\n * ```ts\n * const { source, outcomes } = applyConfigEdits(current, [\n * { operation: 'set', plugin: '@kubb/plugin-ts', path: ['enum', 'type'], value: 'enum' },\n * ])\n * ```\n *\n * @note recast always reprints a semicolon on a reprinted statement, so editing a block-body\n * `defineConfig` in a semicolon-free file adds one to the `return` line. This is a known gap.\n * Strip it when `detectCodeFormat` reports `useSemi: false`, if it turns out to matter in practice.\n */\nexport function applyConfigEdits(source: string, edits: Array<ConfigEdit>): ApplyResult {\n let current = source\n const format = detectCodeFormat(source)\n const endsWithNewline = source.endsWith('\\n')\n\n const outcomes = edits.map((edit): ConfigEditOutcome => {\n const target = parseTarget(current, edit.config)\n if ('reason' in target) {\n return { edit, applied: false, reason: target.reason }\n }\n const { mod, config } = target\n\n if (edit.operation === 'disable-plugin' || edit.operation === 'enable-plugin') {\n const result = edit.operation === 'disable-plugin' ? disablePlugin(current, mod, config, edit.plugin) : enablePlugin(current, edit.plugin)\n if ('reason' in result) {\n return { edit, applied: false, reason: result.reason }\n }\n current = result.source\n return { edit, applied: true }\n }\n\n if (edit.operation === 'add-plugin') {\n const result = applyAddPlugin(mod, config, edit)\n if ('reason' in result) {\n return { edit, applied: false, reason: result.reason }\n }\n if ('noop' in result) {\n return { edit, applied: true }\n }\n const afterLine = lastImportEndLine(mod)\n let next = generateCode(mod, { format }).code\n if (result.addImport) {\n next = insertImportLine({ source: next, afterLine, ...result.addImport })\n }\n current = withTrailingNewline(next, endsWithNewline)\n return { edit, applied: true }\n }\n\n const pluginCall = pluginCalls(mod, config).find((plugin) => plugin.packageName === edit.plugin)\n if (!pluginCall) {\n return { edit, applied: false, reason: `${edit.plugin} is not in the plugins array` }\n }\n\n const reason = edit.operation === 'set' ? applySet(pluginCall.call, edit.path, edit.value) : applyRemove(pluginCall.call, edit.path)\n if (!reason) {\n current = withTrailingNewline(generateCode(mod, { format }).code, endsWithNewline)\n }\n return { edit, applied: !reason, reason }\n })\n\n return { source: current, outcomes, changed: current !== source }\n}\n\n/**\n * The 1-based line where the file's last import declaration ends, or `0` when it has none. Read\n * off the parsed module, so a multi-line `import {\\n x,\\n} from '...'` reports its closing line\n * rather than the `import` keyword.\n */\nfunction lastImportEndLine(mod: ProxifiedModule): number {\n const body = mod.$ast.type === 'Program' ? mod.$ast.body : []\n\n return body.filter((node) => node.type === 'ImportDeclaration').at(-1)?.loc?.end.line ?? 0\n}\n\n/**\n * Writes an import after the last one already in the file, matching its quote style and whether it\n * ends in a semicolon. `afterLine` is where that last import ends, `0` for a file with none.\n *\n * Written as plain text rather than through magicast's import builder, which prints a brand-new\n * import declaration with its own default spacing and a semicolon regardless of `format`, since\n * that formatting only governs nodes recast can diff against the original source.\n */\nfunction insertImportLine({\n source,\n importName,\n moduleSpecifier,\n afterLine,\n}: {\n source: string\n importName: string\n moduleSpecifier: string\n afterLine: number\n}): string {\n const lines = source.split('\\n')\n\n const lastImportLine = afterLine > 0 ? lines[afterLine - 1] : undefined\n const quote = lastImportLine?.includes(`\"`) ? `\"` : `'`\n const semicolon = lastImportLine?.trimEnd().endsWith(';') ? ';' : ''\n const line = `import { ${importName} } from ${quote}${moduleSpecifier}${quote}${semicolon}`\n\n lines.splice(afterLine, 0, ...(afterLine > 0 ? [line] : [line, '']))\n\n return lines.join('\\n')\n}\n\n/**\n * `generateCode` always drops the file's trailing newline. Restore it when the input had one.\n */\nfunction withTrailingNewline(code: string, hadTrailingNewline: boolean): string {\n if (!hadTrailingNewline || code.endsWith('\\n')) {\n return code\n }\n return `${code}\\n`\n}\n","import { hash } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { type Config, createKubb, type Diagnostic, Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport {\n FORMATTER_PREFERENCE,\n LINTER_PREFERENCE,\n formatters,\n linters,\n memoize,\n tokenize,\n type ToolCommand,\n detectTool as detectUncachedTool,\n} from '@internals/utils'\nimport { waitForHookEnd } from './hooks.ts'\n\n/**\n * `isToolAvailable` spawns a process, and a long-lived connection generates repeatedly, so each\n * executable is probed once per process. The CLI deliberately does not memoize: a `--watch` build\n * should keep noticing a tool installed mid-session.\n */\nconst detectTool = memoize(new Map<ReadonlyArray<string>, Promise<string | null>>(), detectUncachedTool)\n\n/**\n * The two post-build tool steps. Formatting and linting differ only in which tools they look for,\n * so they run through one loop rather than two near-identical blocks.\n *\n * `noun` and `verbing` are spelled out instead of built from `kind`. Concatenating `` `${kind}ter` ``\n * and `` `${kind}ting` `` works for `format`, but doubles the `t` in `lint`, giving \"lintter\" and\n * \"lintting\" instead of \"linter\" and \"linting\".\n */\nconst TOOL_STEPS: ReadonlyArray<{ kind: 'format' | 'lint'; noun: string; verbing: string; tools: Record<string, ToolCommand>; detect: ReadonlyArray<string> }> =\n [\n // `detect` is the preference order for `auto`, most-preferred first. Spelled out rather than\n // taken from the table's key order, which is arbitrary and silently changes what `auto` picks.\n { kind: 'format', noun: 'formatter', verbing: 'Formatting', tools: formatters, detect: FORMATTER_PREFERENCE },\n { kind: 'lint', noun: 'linter', verbing: 'Linting', tools: linters, detect: LINTER_PREFERENCE },\n ]\n\n/**\n * Absolute path of the directory the formatter and linter are pointed at.\n */\nfunction outputPath(config: Config): string {\n return path.isAbsolute(config.output.path) ? config.output.path : path.resolve(process.cwd(), config.root, config.output.path)\n}\n\ntype RunHookProps = {\n hooks: Hookable<KubbHooks>\n /**\n * Stable identity for the command, hashed so the `kubb:hook:*` events for concurrent commands\n * can be told apart.\n */\n id: string\n command: string\n args: ReadonlyArray<string>\n}\n\n/**\n * Emits `kubb:hook:start` and waits for the matching `kubb:hook:end`. The host spawns the process:\n * this only describes what to run and when it finished.\n *\n * @throws whatever the command failed with, so callers can report it their own way.\n */\nasync function runHook({ hooks, id, command, args }: RunHookProps): Promise<void> {\n const hookId = hash('sha256', id)\n // Registered before the start event, since `callHook` awaits its listeners and the host calls\n // `kubb:hook:end` from inside that same listener.\n const hookEnd = waitForHookEnd(hooks, hookId)\n\n await hooks.callHook('kubb:hook:start', { id: hookId, command, args: [...args] })\n await hookEnd\n}\n\ntype GenerateProps = {\n config: Config\n hooks: Hookable<KubbHooks>\n signal?: AbortSignal\n}\n\nfunction isProblemErrorDiagnostic(diagnostic: Diagnostic): diagnostic is Diagnostic & { plugin?: string; message: string } {\n return (diagnostic.kind ?? 'problem') === 'problem' && diagnostic.severity === 'error'\n}\n\n/**\n * Folds error-severity diagnostics into one thrown error so logs name the failing plugin.\n */\nfunction formatGenerationFailure(diagnostics: ReadonlyArray<Diagnostic>): Error {\n const reasons = diagnostics\n .filter(isProblemErrorDiagnostic)\n .map((diagnostic) => (diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message))\n\n if (!reasons.length) {\n return new Error('Generation failed')\n }\n\n return new Error(`Generation failed: ${reasons.length} error${reasons.length === 1 ? '' : 's'}: ${reasons.join('; ')}`)\n}\n\n/**\n * Runs a full Kubb code-generation cycle for the given config.\n *\n * Emits lifecycle events on the provided `hooks` emitter so callers (e.g. the WebSocket stream)\n * can forward progress to connected clients. After a successful build, auto-formatting and\n * linting are applied when configured, followed by any user-defined `hooks.done` commands.\n */\nexport async function generate({ config, hooks, signal }: GenerateProps): Promise<void> {\n signal?.throwIfAborted()\n const hrStart = process.hrtime()\n\n await hooks.callHook('kubb:generation:start', { config })\n\n await hooks.callHook('kubb:info', { message: config.name ? `Setup generation ${config.name}` : 'Setup generation' })\n\n const kubb = createKubb(config, { hooks, signal })\n await kubb.setup()\n\n await hooks.callHook('kubb:info', { message: config.name ? `Build generation ${config.name}` : 'Build generation' })\n\n const { files, diagnostics, storage } = await kubb.safeBuild()\n signal?.throwIfAborted()\n\n await hooks.callHook('kubb:info', { message: 'Load summary' })\n\n // Core captures build failures as `error`-severity diagnostics instead of throwing, so\n // surface each one as a `kubb:error` event for the client. Warnings and info reported\n // through `ctx.warn`/`ctx.info` reach the client directly via core's events on this shared emitter.\n for (const diagnostic of diagnostics.filter(isProblemErrorDiagnostic)) {\n await hooks.callHook('kubb:error', { error: new Error(diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message) })\n }\n\n const status = Diagnostics.hasError(diagnostics) ? 'failed' : 'success'\n\n await hooks.callHook('kubb:generation:end', {\n config,\n // Only the files Kubb generated. `fsStorage().readKeys()` lists the working directory, so\n // Studio's tree would show the host's own source and miss output landing outside it.\n storage: { ...storage, readKeys: async () => [...new Set(files.map((file) => file.path))] },\n diagnostics,\n status,\n hrStart,\n filesCreated: files.length,\n })\n\n if (status === 'failed') {\n throw formatGenerationFailure(diagnostics)\n }\n\n await hooks.callHook('kubb:success', { message: 'Generation successfully' })\n\n for (const step of TOOL_STEPS) {\n const setting = config.output[step.kind]\n if (!setting) {\n continue\n }\n\n await hooks.callHook(`kubb:${step.kind}:start`)\n\n // `auto` means \"whatever is installed\", so the tool is detected now rather than at config time.\n const tool = setting === 'auto' ? await detectTool(step.detect) : setting\n\n if (!tool) {\n await hooks.callHook('kubb:warn', { message: `No ${step.noun} found (${step.detect.join(', ')}). Skipping ${step.verbing.toLowerCase()}.` })\n }\n\n if (tool && setting === 'auto') {\n await hooks.callHook('kubb:info', { message: `Auto-detected ${step.noun}: ${styleText('dim', tool)}` })\n }\n\n const command = tool ? step.tools[tool] : undefined\n\n if (command) {\n try {\n await runHook({ hooks, id: [config.name, tool].filter(Boolean).join('-'), command: command.command, args: command.args(outputPath(config)) })\n\n await hooks.callHook('kubb:success', { message: `${step.verbing} with ${tool} successfully` })\n } catch (caughtError) {\n await hooks.callHook('kubb:error', { error: new Error(command.errorMessage, { cause: caughtError }) })\n // Format/lint failures are non-fatal, but an abort during those commands must still reject.\n signal?.throwIfAborted()\n }\n }\n\n await hooks.callHook(`kubb:${step.kind}:end`)\n }\n\n // `output.postGenerate` commands run in order, each one waiting for the previous to finish.\n if (config.output.postGenerate?.length) {\n await hooks.callHook('kubb:hooks:start')\n\n for (const entry of config.output.postGenerate) {\n const line = typeof entry === 'string' ? entry : entry.command\n const [cmd, ...args] = tokenize(line)\n\n if (!cmd) {\n continue\n }\n\n await runHook({ hooks, id: line, command: cmd, args })\n await hooks.callHook('kubb:success', { message: `${line} successfully executed` })\n }\n\n await hooks.callHook('kubb:hooks:end')\n }\n}\n","import { createHash } from 'node:crypto'\nimport { glob, mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join, relative, sep } from 'node:path'\nimport { promisify } from 'node:util'\nimport { gzip } from 'node:zlib'\nimport { build } from 'tsdown'\n\nconst gzipAsync = promisify(gzip)\n\ntype SnapshotFiles = Record<string, string>\ntype SnapshotPackage = { name: string; version: string; peerDependencies: Record<string, string> }\n\n/**\n * Maps a generated file's path to its place inside the tarball, stripping everything before a\n * `src`/`dist` segment and any `..`/empty path segment so a crafted file name cannot escape the\n * `package/` root.\n */\nfunction packagePath(filePath: string): string {\n const normalized = filePath.replaceAll('\\\\', '/')\n const relativePath = normalized.match(/\\/(?:src|dist)\\/.*$/)?.[0].slice(1) ?? normalized.replace(/^\\/+/, '')\n const safe = relativePath\n .split('/')\n .filter((part) => part && part !== '.' && part !== '..')\n .join('/')\n return `package/${safe}`\n}\n\n/**\n * Splits a tarball entry path into the legacy 100-byte `name` field and, when the path does not\n * fit, the 155-byte USTAR `prefix` field that extends it. Throws rather than silently truncating\n * a path the format cannot address (max 256 bytes: 100 name + 1 separator + 155 prefix).\n */\nfunction splitEntryPath(path: string): { name: string; prefix: string } {\n if (Buffer.byteLength(path, 'utf8') <= 100) {\n return { name: path, prefix: '' }\n }\n\n for (let i = path.length - 1; i >= 0; i--) {\n if (path[i] !== '/') continue\n\n const prefix = path.slice(0, i)\n const name = path.slice(i + 1)\n if (Buffer.byteLength(prefix, 'utf8') <= 155 && Buffer.byteLength(name, 'utf8') <= 100) {\n return { name, prefix }\n }\n }\n\n throw new Error(`Snapshot path is too long for a tar entry: ${path}`)\n}\n\nfunction header(path: string, size: number): Buffer {\n const { name, prefix } = splitEntryPath(path)\n const value = Buffer.alloc(512)\n value.write(name, 0, 'utf8')\n value.write('0000644\\0', 100, 'ascii')\n value.write('0000000\\0', 108, 'ascii')\n value.write('0000000\\0', 116, 'ascii')\n value.write(`${size.toString(8).padStart(11, '0')}\\0`, 124, 'ascii')\n value.write(\n `${Math.floor(Date.now() / 1000)\n .toString(8)\n .padStart(11, '0')}\\0`,\n 136,\n 'ascii',\n )\n value.fill(32, 148, 156)\n value.write('0', 156, 'ascii')\n value.write('ustar\\0', 257, 'ascii')\n value.write('00', 263, 'ascii')\n value.write('0000000\\0', 265, 'ascii')\n value.write('0000000\\0', 297, 'ascii')\n value.write(prefix, 345, 'utf8')\n const checksum = [...value].reduce((sum, byte) => sum + byte, 0)\n value.write(`${checksum.toString(8).padStart(6, '0')}\\0 `, 148, 'ascii')\n return value\n}\n\n/**\n * Packs a generation's files into a gzipped, npm-installable tarball: a `package/` root with the\n * generated sources, a `tsdown`-built `dist/` (esm + cjs), and a `package.json` manifest.\n */\nexport async function createSnapshotPackage(files: SnapshotFiles, packageInfo: SnapshotPackage): Promise<{ bytes: Buffer; integrity: string }> {\n const root = await mkdtemp(join(tmpdir(), 'kubb-snapshot-'))\n const dist = join(root, 'dist')\n\n // Resolved once so the sanitized target for each file is computed exactly one way, and any two\n // generated files that collide after sanitizing (e.g. `a/../index.ts` and `a/index.ts`) are\n // caught here instead of silently overwriting one another later.\n const resolvedPaths = Object.entries(files).map(([name, content]) => ({ name, content, target: packagePath(name) }))\n const targetOwners = new Map<string, string>()\n for (const { name, target } of resolvedPaths) {\n const owner = targetOwners.get(target)\n if (owner) {\n throw new Error(`Snapshot has two generated files that sanitize to the same path \"${target}\": \"${owner}\" and \"${name}\"`)\n }\n targetOwners.set(target, name)\n }\n\n try {\n await mkdir(dist)\n await Promise.all(\n resolvedPaths.map(async ({ content, target }) => {\n const path = join(root, target.slice('package/'.length))\n await mkdir(join(path, '..'), { recursive: true })\n await writeFile(path, content)\n }),\n )\n const sourceEntries = resolvedPaths.filter(({ name }) => /\\.(?:[cm]?[jt]sx?)$/.test(name)).map(({ target }) => join(root, target.slice('package/'.length)))\n if (sourceEntries.length)\n await build({\n entry: sourceEntries,\n outDir: dist,\n format: ['esm', 'cjs'],\n dts: false,\n sourcemap: false,\n unbundle: true,\n report: false,\n logLevel: 'silent',\n // The manifest below always points at `.mjs`/`.cjs`, so the build must produce those\n // extensions regardless of the host process's own `package.json` \"type\" (tsdown otherwise\n // infers it from the nearest ancestor package.json, which differs by host).\n fixedExtension: true,\n })\n const builtEntries = await Promise.all(\n (await Array.fromAsync(glob('**/*', { cwd: dist, withFileTypes: true })))\n .filter((entry) => entry.isFile())\n .map(async (entry) => {\n // `unbundle: true` preserves dist's own subdirectory structure, so the archive path must\n // follow suit: `entry.name` alone is just the basename and would flatten (and collide)\n // nested output files.\n const filePath = join(entry.parentPath, entry.name)\n const distRelativePath = relative(dist, filePath).split(sep).join('/')\n return [`package/dist/${distRelativePath}`, await readFile(filePath, 'utf8')] as const\n }),\n )\n // Without `@kubb/plugin-barrel` the build produces no `dist/index.*`, so pointing\n // `main`/`module`/`exports['.']` at it would ship a manifest with missing files.\n const builtPaths = new Set(builtEntries.map(([path]) => path))\n const hasBarrel = builtPaths.has('package/dist/index.mjs') && builtPaths.has('package/dist/index.cjs')\n const barrelFields = hasBarrel ? { main: './dist/index.cjs', module: './dist/index.mjs' } : {}\n const barrelExport = hasBarrel ? { '.': { import: './dist/index.mjs', require: './dist/index.cjs' } } : {}\n\n const entries = {\n 'package/package.json': JSON.stringify(\n {\n ...packageInfo,\n type: 'module',\n ...barrelFields,\n // `exports` denies any subpath it doesn't list, so the wildcard keeps individual\n // generated files (e.g. `models/Pet`) importable without a barrel.\n exports: { ...barrelExport, './*': { import: './dist/*.mjs', require: './dist/*.cjs' } },\n },\n null,\n 2,\n ),\n ...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),\n ...Object.fromEntries(builtEntries),\n }\n const chunks: Array<Buffer> = []\n\n for (const [name, content] of Object.entries(entries)) {\n const bytes = Buffer.from(content)\n chunks.push(header(name, bytes.length), bytes, Buffer.alloc((512 - (bytes.length % 512)) % 512))\n }\n\n const bytes = await gzipAsync(Buffer.concat([...chunks, Buffer.alloc(1024)]))\n return { bytes, integrity: `sha512-${createHash('sha512').update(bytes).digest('base64')}` }\n } finally {\n await rm(root, { recursive: true, force: true })\n }\n}\n","import { createHash } from 'node:crypto'\nimport { relative, resolve, sep } from 'node:path'\nimport { inParallel } from '@internals/utils'\nimport { fsStorage, type Storage } from '@kubb/core'\n\nconst READ_CONCURRENCY = 50\nconst MB = 1024 * 1024\nconst INDEX_KEY = 'studio/generations.json'\n\n/**\n * Files a run produced, or the output directory held, keyed by path relative to `root`.\n */\nexport type SourceFiles = { storage: Storage; root: string; paths: Set<string> }\n\n/**\n * Which set of a generation to read: what the run produced, or the output directory before it ran.\n */\nexport type GenerationSource = 'output' | 'disk'\n\n/**\n * A set as kept in the store.\n */\ntype KeptSet = {\n /**\n * The paths a read is checked against. Empty when the set was too large to keep its content.\n */\n paths: Array<string>\n hashes: Record<string, string>\n bytes: number\n}\n\nexport type KeptGeneration = {\n jobId: string\n output: KeptSet\n disk?: KeptSet\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nconst hashOf = (content: string) => createHash('sha1').update(content).digest('hex').slice(0, 16)\n\n/**\n * What the output directory holds on disk before a run. `undefined` when `outputPath` is not a real\n * subdirectory of `root` (listing the root would take in every source file) or holds more than `maxFiles`.\n */\nexport async function listDisk({ root, outputPath, maxFiles }: { root: string; outputPath: string; maxFiles: number }): Promise<SourceFiles | undefined> {\n const outputDir = resolve(root, outputPath)\n const fromRoot = relative(resolve(root), outputDir)\n if (!fromRoot || fromRoot.startsWith('..') || fromRoot.startsWith(sep)) return undefined\n\n const storage = fsStorage()\n const keys = await storage.readKeys(outputDir)\n if (keys.length > maxFiles) return undefined\n\n const paths = keys\n .filter((key) => !key.split('/').includes('node_modules'))\n .map((key) => relative(resolve(root), resolve(outputDir, key)).replaceAll('\\\\', '/'))\n return { storage, root, paths: new Set(paths) }\n}\n\n/**\n * Keeps recent generations by job id in `storage`, with an index next to them so a store on disk\n * survives a restart. A generation is only ever looked up by its job id, so on a pooled sandbox one\n * tenant never reaches another's output. The oldest go past `maxCount` or `maxMb`, but the newest\n * always stays.\n */\nexport function createGenerationStore({ storage, maxCount, maxMb }: { storage: Storage; maxCount: number; maxMb: number }) {\n let index: Array<KeptGeneration> | undefined\n\n // Hashed so a job id from the wire can never point outside the store.\n const dirOf = (jobId: string) => `studio/generations/${hashOf(jobId)}/`\n\n async function load(): Promise<Array<KeptGeneration>> {\n if (index) return index\n const stored = await storage.readItem(INDEX_KEY).catch(() => null)\n try {\n index = stored ? (JSON.parse(stored) as Array<KeptGeneration>) : []\n } catch {\n index = []\n }\n return index\n }\n\n /**\n * Copies `files` into the store as one set of `jobId`. Above `maxSetMb` only the hashes are kept.\n */\n async function keep({ jobId, source, files, maxSetMb }: { jobId: string; source: GenerationSource; files: SourceFiles; maxSetMb: number }): Promise<KeptSet> {\n const maxSetBytes = maxSetMb * MB\n const hashes: Record<string, string> = {}\n let bytes = 0\n await inParallel({\n items: [...files.paths],\n limit: READ_CONCURRENCY,\n run: async (path) => {\n // Output outside the root would land outside the store too.\n if (path.split('/').includes('..')) return\n const content = await files.storage.readItem(resolve(files.root, path))\n if (content === null) return\n hashes[path] = hashOf(content)\n bytes += Buffer.byteLength(content)\n if (bytes <= maxSetBytes) await storage.writeItem(`${dirOf(jobId)}${source}/${path}`, content)\n },\n })\n if (bytes > maxSetBytes) {\n await storage.empty(`${dirOf(jobId)}${source}/`)\n return { paths: [], hashes, bytes: 0 }\n }\n return { paths: Object.keys(hashes), hashes, bytes }\n }\n\n async function drop(jobId: string): Promise<void> {\n await storage.empty(dirOf(jobId))\n }\n\n return {\n keep,\n drop,\n get: async (jobId: string) => (await load()).find((generation) => generation.jobId === jobId),\n latest: async () => (await load()).at(-1),\n async add(generation: KeptGeneration): Promise<void> {\n const entries = (await load()).filter((entry) => entry.jobId !== generation.jobId)\n entries.push(generation)\n const weight = () => entries.reduce((sum, { output, disk }) => sum + output.bytes + (disk?.bytes ?? 0), 0)\n while (entries.length > 1 && (entries.length > maxCount || weight() > maxMb * MB)) {\n await drop(entries.shift()!.jobId)\n }\n index = entries\n await storage.writeItem(INDEX_KEY, JSON.stringify(entries))\n },\n /**\n * Reads the requested paths the set holds, skipping any it does not.\n */\n async read({ generation, source, paths }: { generation: KeptGeneration; source: GenerationSource; paths: Array<string> }): Promise<Record<string, string>> {\n const kept = new Set(generation[source]?.paths)\n const files: Record<string, string> = {}\n await inParallel({\n items: paths.filter((path) => kept.has(path)),\n limit: READ_CONCURRENCY,\n run: async (path) => {\n const content = await storage.readItem(`${dirOf(generation.jobId)}${source}/${path}`)\n if (content !== null) files[path] = content\n },\n })\n return files\n },\n }\n}\n\nexport type GenerationStore = ReturnType<typeof createGenerationStore>\n","import { readFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { isAbsolute, relative, resolve } from 'node:path'\nimport { getElapsedMs } from '@internals/utils'\nimport { Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport WebSocket from 'ws'\nimport type { GenerationEvent, GenerationEventPayloads, GenerationEventType } from './protocol/index.ts'\nimport type { SourceFiles } from './generations.ts'\nimport { toPackageName } from './resolveConfig.ts'\n\ntype WebSocketOptions = WebSocket.ClientOptions\n\n/**\n * How long the initial handshake may take before the socket is closed and the reconnect loop\n * takes over.\n */\nconst CONNECT_TIMEOUT_MS = 5_000\n\nconst require = createRequire(import.meta.url)\n\nfunction relativeStoragePath(root: string, filePath: string): string {\n return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll('\\\\', '/')\n}\n\ntype PackageJSON = {\n version?: string\n}\n\nasync function resolvePeerDependencies(names: Array<string>): Promise<{\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}> {\n const uniqueNames = [...new Set(names.map(toPackageName))]\n const peerDependencies: Record<string, string> = {}\n const missingDependencies: Array<string> = []\n\n const versions = await Promise.all(\n uniqueNames.map(async (name) => {\n try {\n const path = require.resolve(`${name}/package.json`)\n const packageJSON = JSON.parse(await readFile(path, 'utf8')) as PackageJSON\n return packageJSON.version\n } catch {\n return undefined\n }\n }),\n )\n\n for (const [index, name] of uniqueNames.entries()) {\n const version = versions[index]\n if (version) {\n peerDependencies[name] = version\n continue\n }\n missingDependencies.push(name)\n }\n\n return { peerDependencies, missingDependencies }\n}\n\n/**\n * Opens a Studio WebSocket connection and closes it when the initial handshake exceeds the configured timeout.\n */\nexport function createWebsocket(url: string, options: WebSocketOptions): WebSocket {\n const ws = new WebSocket(url, options)\n\n const timer = setTimeout(() => {\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close(3008, 'Connection timeout')\n }\n }, CONNECT_TIMEOUT_MS)\n\n // Once the handshake settles the timer has nothing left to check, and leaving it pending holds\n // the socket for the rest of the window.\n ws.once('open', () => clearTimeout(timer))\n ws.once('close', () => clearTimeout(timer))\n\n return ws\n}\n\n/**\n * What `kubb:generation:end` reports: the files the run produced, still in its own storage.\n */\nexport type GenerationEnd = {\n output: SourceFiles\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nexport type GenerationStreamOptions = {\n onGenerationEnd?: (result: GenerationEnd) => void\n}\n\n/** Forwards selected Kubb lifecycle events to a native Cap'n Web stream. */\nexport function createGenerationStream(\n hooks: Hookable<KubbHooks>,\n jobId: string,\n options: GenerationStreamOptions = {},\n): { stream: ReadableStream<GenerationEvent>; close: () => Promise<void>; dispose: () => void; fail: (error: unknown) => void } {\n const unhooks: Array<() => void> = []\n let root = ''\n // Infinite HWM so unread events don't stall result()\n const transform = new TransformStream<GenerationEvent>(undefined, undefined, { highWaterMark: Infinity })\n const writer = transform.writable.getWriter()\n let writes = Promise.resolve()\n let closed = false\n let streamError: unknown\n\n /**\n * Registers a listener and keeps its remover, so one generation's listeners come off the session\n * emitter again when that generation ends.\n */\n function on<TName extends keyof KubbHooks & string>(name: TName, handler: (...args: KubbHooks[TName]) => unknown): void {\n unhooks.push(hooks.hook(name, handler))\n }\n\n function emitEvent<Type extends GenerationEventType>(type: Type, data: GenerationEventPayloads[Type]): void {\n const event = { jobId, type, data, version: 1 as const, timestamp: Date.now() } as unknown as GenerationEvent\n // A prior failure skips the write; either way the chain settles so the next event still runs.\n writes = writes\n .then(() => writer.write(event))\n .catch((error) => {\n streamError = error\n })\n }\n\n on('kubb:plugin:start', (ctx) => {\n emitEvent('kubb:plugin:start', [{ plugin: { name: ctx.plugin.name } }])\n })\n\n on('kubb:plugin:end', (ctx) => {\n emitEvent('kubb:plugin:end', [{ plugin: { name: ctx.plugin.name }, duration: ctx.duration, success: ctx.success }])\n })\n\n on('kubb:build:start', ({ config, adapter }) => {\n root = config.root\n emitEvent('kubb:build:start', [{ config: { name: config.name }, adapter: { name: adapter.name } }])\n })\n\n on('kubb:build:end', ({ files, config, outputDir }) => {\n emitEvent('kubb:build:end', [{ files: files.map((file) => ({ path: relativeStoragePath(config.root, file.path), name: file.name })), outputDir }])\n })\n\n on('kubb:files:processing:start', ({ files }) => {\n emitEvent('kubb:files:processing:start', [{ total: files.length }])\n })\n\n on('kubb:files:processing:update', ({ files }) => {\n emitEvent('kubb:files:processing:update', [\n {\n files: files.map(({ file, processed, total, percentage }) => ({\n file: relativeStoragePath(root, file.path),\n processed,\n total,\n percentage,\n })),\n },\n ])\n })\n\n on('kubb:files:processing:end', ({ files }) => {\n emitEvent('kubb:files:processing:end', [{ total: files.length }])\n })\n\n // The three log levels differ only in their event name.\n for (const type of ['kubb:info', 'kubb:success', 'kubb:warn'] as const) {\n on(type, ({ message, info }) => {\n emitEvent(type, [{ message, info }])\n })\n }\n\n on('kubb:generation:start', ({ config }) => {\n emitEvent('kubb:generation:start', [\n {\n name: config.name,\n plugins: config.plugins.length,\n },\n ])\n })\n\n on('kubb:generation:end', async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {\n const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name))\n const keys = await storage.readKeys()\n const paths = new Set(keys.map((key) => relativeStoragePath(config.root, key)))\n options.onGenerationEnd?.({ output: { storage, root: config.root, paths }, peerDependencies, missingDependencies })\n\n emitEvent('kubb:generation:end', [])\n\n if (!hrStart) {\n return\n }\n\n const duration = Math.round(getElapsedMs(hrStart))\n\n emitEvent('kubb:generation:summary', [\n { duration, fileCount: filesCreated ?? 0, failedPlugins: Diagnostics.failedPlugins(diagnostics).length, status: status ?? 'success' },\n ])\n })\n\n on('kubb:error', ({ error }) => {\n emitEvent('kubb:error', [\n {\n message: error.message,\n stack: error.stack,\n },\n ])\n })\n\n on('kubb:diagnostic', ({ diagnostic }) => {\n const cause = 'cause' in diagnostic ? diagnostic.cause : undefined\n emitEvent('kubb:diagnostic', [\n {\n code: diagnostic.code,\n message: diagnostic.message,\n severity: diagnostic.severity,\n location: 'location' in diagnostic ? diagnostic.location : undefined,\n help: 'help' in diagnostic ? diagnostic.help : undefined,\n plugin: 'plugin' in diagnostic ? diagnostic.plugin : undefined,\n stack: cause?.stack,\n },\n ])\n })\n\n // Bracketing events carry no context, so they forward identically.\n for (const type of [\n 'kubb:lifecycle:start',\n 'kubb:lifecycle:end',\n 'kubb:format:start',\n 'kubb:format:end',\n 'kubb:lint:start',\n 'kubb:lint:end',\n 'kubb:hooks:start',\n 'kubb:hooks:end',\n ] as const) {\n on(type, () => {\n emitEvent(type, [])\n })\n }\n\n on('kubb:hook:start', ({ id, command, args }) => {\n emitEvent('kubb:hook:start', [{ id, command, args: args ? [...args] : undefined }])\n })\n\n on('kubb:hook:line', ({ id, line }) => {\n emitEvent('kubb:hook:line', [{ id, line }])\n })\n\n on('kubb:hook:end', ({ id, command, args, success, error }) => {\n emitEvent('kubb:hook:end', [\n {\n id,\n command,\n args: args ? [...args] : undefined,\n success,\n error: error ? { message: error.message, stack: error.stack } : undefined,\n },\n ])\n })\n\n /**\n * Takes this generation's listeners off the session emitter. Safe to call twice.\n */\n function detach(): void {\n for (const unhook of unhooks) unhook()\n unhooks.length = 0\n }\n\n async function close(): Promise<void> {\n if (closed) {\n return\n }\n closed = true\n detach()\n await writes\n // Consumer cancel sets streamError; don't fail a successful generation over that.\n if (streamError) {\n return\n }\n await writer.close().catch(() => undefined)\n }\n\n function fail(error?: unknown): void {\n detach()\n if (closed) {\n return\n }\n closed = true\n void writer.abort(error).catch(() => undefined)\n }\n\n return { stream: transform.readable, close, dispose: () => fail(), fail }\n}\n","import { newWebSocketRpcSession, RpcTarget } from 'capnweb'\nimport type {\n AgentApi,\n GenerateInput,\n PublishSnapshotInput,\n ReadFilesInput,\n RpcConnection,\n RpcConnector,\n SaveConfigInput,\n StudioApi,\n} from './protocol/index.ts'\nimport { createWebsocket } from './ws.ts'\n\n/**\n * The only methods Studio may call on an agent. A `StudioSession` carries far more than\n * {@link AgentApi}, so it is wrapped rather than exposed: what Cap'n Web can reach is exactly what\n * this class re-declares.\n */\nclass AgentRpcTarget extends RpcTarget implements AgentApi {\n constructor(private readonly api: AgentApi) {\n super()\n }\n\n connect() {\n return this.api.connect()\n }\n startGeneration(input: GenerateInput) {\n return this.api.startGeneration(input)\n }\n saveConfig(input: SaveConfigInput) {\n return this.api.saveConfig(input)\n }\n publishSnapshot(input: PublishSnapshotInput) {\n return this.api.publishSnapshot(input)\n }\n readFiles(input: ReadFilesInput) {\n return this.api.readFiles(input)\n }\n}\n\n/**\n * Opens an authenticated Cap'n Web session to Studio over a WebSocket. Rejects an unencrypted URL\n * before opening the socket, so a bearer token never reaches a plaintext host.\n *\n * @example\n * ```ts\n * const rpc = await connectWebSocketRpc({ url: 'wss://studio.kubb.dev/s/1', token, local: session })\n * await rpc.studio.ping()\n * ```\n */\nexport const connectWebSocketRpc: RpcConnector = async ({ url, token, local }): Promise<RpcConnection> => {\n const { protocol, hostname, host } = new URL(url)\n // `URL` keeps the brackets on an IPv6 hostname, so `::1` arrives as `[::1]`.\n const isLoopback = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'\n if (protocol !== 'wss:' && !(protocol === 'ws:' && isLoopback)) {\n throw new Error(`Refusing unencrypted WebSocket to ${host}`)\n }\n\n const socket = createWebsocket(url, { headers: { Authorization: `Bearer ${token}` } })\n const closed = new Promise<void>((resolve) => socket.once('close', resolve))\n // `ws` implements the browser WebSocket surface capnweb uses, but declares its own nominal type.\n const studio = newWebSocketRpcSession<StudioApi>(socket as unknown as globalThis.WebSocket, new AgentRpcTarget(local))\n studio.onRpcBroken(() => socket.close())\n\n return {\n studio,\n closed,\n close: () => studio[Symbol.dispose](),\n }\n}\n","import { writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { getErrorMessage, read, toError } from '@internals/utils'\nimport { cacheStorage, type Config, fsStorage, Hookable, type KubbHooks, logLevel as logLevelMap, memoryStorage } from '@kubb/core'\nimport { version as kubbVersion } from '../package.json'\nimport { setupHookListener } from './hooks.ts'\nimport {\n type AgentApi,\n type AgentConnectResponse,\n type AgentPermissions,\n type ClientInfo,\n type ConfigFileView,\n type ConnectMessagePayload,\n type GenerateInput,\n type GenerateResult,\n type GenerationEvent,\n type GenerationRun,\n GENERATION_GONE_MESSAGE,\n MAX_FILES_PER_REQUEST,\n type ReadFilesInput,\n type SaveConfigInput,\n type SaveResult,\n type PublishSnapshotInput,\n type PublishSnapshotResult,\n type RpcConnector,\n type RpcConnection,\n} from './protocol/index.ts'\nimport { createAgentSession, disconnect, InvalidAgentTokenError } from './api.ts'\nimport { applyConfigEdits, readConfig } from './configFile.ts'\nimport { generate } from './generate.ts'\nimport { agentDefaults, resolveGenerationLimits } from './constants.ts'\nimport { mergeAdapter, mergePlugins, toPackageName } from './resolveConfig.ts'\nimport { createSnapshotPackage } from './snapshotPackage.ts'\nimport { RpcTarget } from 'capnweb'\nimport { createGenerationStore, type GenerationStore, listDisk } from './generations.ts'\nimport { createGenerationStream, type GenerationEnd } from './ws.ts'\nimport { connectWebSocketRpc } from './rpc.ts'\n\n/**\n * Past this many files in the output directory, no snapshot of it is taken before a run.\n */\nconst DISK_SNAPSHOT_MAX_FILES = 10_000\n\nclass GenerationRunTarget extends RpcTarget implements GenerationRun {\n constructor(\n private readonly generationStream: ReadableStream<GenerationEvent>,\n private readonly generationResult: Promise<GenerateResult>,\n private readonly cancelGeneration: () => Promise<void>,\n /** Stops the run. Cap'n Web calls this on explicit disposal and on a dropped session alike. */\n private readonly stopGeneration: () => void,\n ) {\n super()\n }\n\n async events() {\n return this.generationStream\n }\n result() {\n return this.generationResult\n }\n cancel() {\n return this.cancelGeneration()\n }\n [Symbol.dispose]() {\n this.stopGeneration()\n }\n}\n\nexport type StudioSessionOptions = {\n connector?: RpcConnector\n token: string\n studioUrl?: string\n configPath: string\n /**\n * Loads the on-disk Kubb config. Injected so each host resolves config its own way: the Docker\n * agent from an explicit `KUBB_AGENT_CONFIG` path, the CLI through the same discovery\n * `kubb generate` uses.\n */\n loadConfig: () => Promise<Config>\n /**\n * The runtime's own version, reported to Studio next to the `kubb` version.\n */\n version: string\n /**\n * Identifies the host to Studio, so the UI can badge a CLI connection and show the real project.\n */\n client?: ClientInfo\n /**\n * What Studio may do in this project, off unless the host grants it. A sandbox session narrows\n * them further: it never writes to disk and never edits a config file, and it always generates\n * from the spec Studio sends.\n */\n permissions?: Partial<AgentPermissions>\n root?: string\n retryInterval?: number\n /**\n * Milliseconds between keep-alive pings, clamped to `agentDefaults.maxHeartbeatIntervalMs`.\n * Raise it to halve the traffic and database writes a long-lived agent costs, at the price of\n * Studio taking that much longer to notice the agent has gone. Lower it in development to see\n * connection state move immediately.\n */\n heartbeatInterval?: number\n /**\n * Number of pool sessions this agent serves. Read by `createClient`, which opens one\n * session per slot, and reported to Studio at registration.\n */\n poolSize?: number\n /**\n * Aborting this disconnects the session and stops the reconnect loop. Hosts wire it to their own\n * shutdown: Nitro's `close` hook, or `SIGINT`/`SIGTERM` in the CLI.\n */\n signal?: AbortSignal\n /**\n * Installs listeners on the session's event emitter, which carries both the session events and\n * the generations it runs. Left out, the runtime prints nothing, which is what a library should\n * default to.\n */\n installLogger?: (hooks: Hookable<KubbHooks>) => void | Promise<void>\n /**\n * Threshold for the reconnect loop's own `console.error` lines, using the numeric constants\n * `@kubb/core` exports as `logLevel`. Left out, those lines never print, the same silent default\n * as an unset `installLogger` — a reconnect happens outside any one session's hooks, so it has no\n * other way to ask a host how loud to be.\n */\n logLevel?: number\n /**\n * Called when this session's background reconnect is rejected with an invalid token. Unlike\n * `ClientOptions.onAuthRequired`, this fires once per session rather than once per pool:\n * `createClient` wraps it into that deduped, pool-stopping callback. Not meant to be set\n * directly by a host.\n */\n onTokenRejected?: (error: InvalidAgentTokenError) => void\n}\n\n/**\n * A session's options with every default filled in, so nothing downstream repeats a fallback.\n */\ntype ResolvedOptions = StudioSessionOptions & {\n studioUrl: string\n root: string\n permissions: AgentPermissions\n retryInterval: number\n heartbeatInterval: number\n /**\n * Absolute path to the config file, for reading and patching it. `configPath` keeps the form the\n * host gave, which is what Studio shows.\n */\n configFile: string\n}\n\n/**\n * Fills in a host's options: the hosted Studio URL, the current working directory, and every\n * permission off unless granted. Idempotent, so a reconnect can pass an already-resolved bag\n * back in.\n */\nfunction applyStudioDefaults(options: StudioSessionOptions): ResolvedOptions {\n const root = options.root ?? process.cwd()\n\n return {\n ...options,\n studioUrl: options.studioUrl ?? agentDefaults.studioUrl,\n root,\n // `configPath` is relative to the agent's root unless it is already absolute, which is what\n // `resolve` does on its own.\n configFile: path.resolve(root, options.configPath),\n permissions: { allowWrite: false, allowConfigEdit: false, allowInput: false, allowExec: false, allowRead: false, ...options.permissions },\n retryInterval: options.retryInterval ?? agentDefaults.retryIntervalMs,\n // Studio counts an agent offline once its last ping is older than its liveness window, so a\n // slower cadence would make a healthy agent invisible. Clamped here rather than in a host's\n // env parsing, so every host is held to the contract.\n heartbeatInterval: Math.min(options.heartbeatInterval ?? agentDefaults.heartbeatIntervalMs, agentDefaults.maxHeartbeatIntervalMs),\n }\n}\n\n/**\n * Schedules another connection attempt.\n *\n * A free function rather than a method: a pending retry timer reaches whatever it closes over, so\n * closing only over `options` (not a `StudioSession`) keeps a queued retry from pinning a closed\n * socket, its hook emitter, or its session id alive for the length of the retry interval.\n */\nfunction reconnect(options: ResolvedOptions): void {\n const { signal, retryInterval, onTokenRejected, logLevel } = options\n\n if (signal?.aborted) {\n return\n }\n\n // console.error, not console.info: a CI runner only forwards a child process's stderr live, so\n // an info-level write here would be silently buffered away instead of reaching its log.\n if (logLevel !== undefined && logLevel > logLevelMap.silent) {\n console.error(styleText('dim', `Retrying connection in ${retryInterval}ms to Kubb Studio ...`))\n }\n\n const cancel = () => clearTimeout(timer)\n const timer = setTimeout(() => {\n // Removed here rather than left to `{ once: true }`: the signal only aborts at shutdown, so one\n // listener per retry would accumulate for the whole life of a down-Studio retry loop.\n signal?.removeEventListener('abort', cancel)\n\n if (signal?.aborted) {\n return\n }\n\n // The rejection is never awaited, so it has to be caught here or it surfaces as an\n // unhandledRejection that kills the retry loop instead of trying again.\n new StudioSession(options).start().catch((error: unknown) => {\n if (logLevel !== undefined && logLevel > logLevelMap.silent) {\n console.error(styleText('red', `Reconnect attempt to Kubb Studio failed: ${getErrorMessage(error)}`))\n }\n\n // A rejected token stays rejected, so retrying only spams 401s until the process is killed.\n // The host learns about it here instead: the startup path already reports its own rejection\n // by throwing, so only the background path needs the callback.\n if (error instanceof InvalidAgentTokenError) {\n onTokenRejected?.(error)\n\n return\n }\n\n reconnect(options)\n })\n }, retryInterval)\n\n signal?.addEventListener('abort', cancel, { once: true })\n}\n\n/**\n * One agent-to-Studio RPC transport: opening it, keeping it alive, and serving remote methods.\n * `createClient` opens one per pool slot and is the only caller.\n */\nexport class StudioSession implements AgentApi {\n readonly #options: ResolvedOptions\n // Each session gets its own isolated event emitter so generation events from one session do not\n // bleed into another session's WebSocket stream.\n readonly #hooks = new Hookable<KubbHooks>()\n /**\n * Removers for every listener this session added (socket, shutdown signal, hooks) so `dispose`\n * detaches them in one pass. Listeners a host attached itself through `installLogger` survive.\n */\n readonly #unhooks: Array<() => void> = []\n\n /**\n * What `createAgentSession` handed back, and the marker for whether a session exists at all.\n * Before it resolves there is nothing to disconnect and no sandbox flag to read.\n */\n #session: AgentConnectResponse | undefined\n #rpc: RpcConnection | undefined\n // Returned with the session, so both sides can be named from the first RPC connection.\n #studioVersion: string | undefined\n\n // Whether the session is over: guards the close event from tearing down twice, and a shutdown\n // from being turned into a reconnect.\n #disposed = false\n // Guards against a second `generate` command starting while one is already running. Without\n // this, two concurrent `generate()` calls share this socket via `setupEventsStream`, and their\n // events interleave with no way for Studio to tell the two runs apart.\n #isGenerating = false\n #heartbeatTimer: ReturnType<typeof setTimeout> | undefined\n // Set by `kubb:generation:end`, filed into `#generations` once the job finishes.\n #lastGeneration: GenerationEnd | undefined\n #store: GenerationStore | undefined\n readonly #limits = resolveGenerationLimits()\n /**\n * Resolves when Studio calls {@link StudioSession.connect}. `studio:ready` waits on this so the\n * host does not queue jobs before the agent session is registered.\n */\n readonly #connectAck = Promise.withResolvers<void>()\n\n constructor(options: StudioSessionOptions) {\n this.#options = applyStudioDefaults(options)\n // dispose() may reject this before start() awaits it\n void this.#connectAck.promise.catch(() => {})\n }\n\n /**\n * A sandbox agent runs on Studio's own infrastructure, so it has no user project to touch.\n */\n get #isSandbox(): boolean {\n return this.#session?.isSandbox === true\n }\n\n /**\n * Kept in the project's cache directory, so it survives a restart, except on a sandbox: its pool\n * sessions run every tenant's jobs, so it keeps them in memory. Looked up by job id only.\n */\n get #generations(): GenerationStore {\n this.#store ??= createGenerationStore({\n storage: this.#isSandbox ? memoryStorage() : cacheStorage({ root: this.#options.root }),\n maxCount: this.#limits.maxCount,\n maxMb: this.#limits.maxMb,\n })\n return this.#store\n }\n\n get #canWrite(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowWrite\n }\n\n get #canEditConfig(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowConfigEdit\n }\n\n /**\n * A sandbox agent always generates from the spec Studio supplies. A local agent only when the\n * host opted in.\n */\n get #canUseInput(): boolean {\n return this.#isSandbox || this.#options.permissions.allowInput\n }\n\n /**\n * A sandbox agent always allows reading its output back; a local agent only when opted in.\n */\n get #canRead(): boolean {\n return this.#isSandbox || this.#options.permissions.allowRead\n }\n\n async start(): Promise<void> {\n const { token, studioUrl, signal, heartbeatInterval, installLogger } = this.#options\n\n await installLogger?.(this.#hooks)\n\n try {\n // Before the session exists, so a host can cover the wait: `createAgentSession` is a round\n // trip and the socket after it opens without being awaited.\n await this.#hooks.callHook('studio:connecting', { url: studioUrl })\n\n const session = await createAgentSession({ token, studioUrl })\n\n this.#session = session\n this.#studioVersion = session.version\n\n const rpc = await (this.#options.connector ?? connectWebSocketRpc)({ url: session.url, token, local: this })\n this.#rpc = rpc\n void rpc.closed.then(this.#onClose)\n\n signal?.addEventListener('abort', this.#onAbort, { once: true })\n this.#unhooks.push(() => signal?.removeEventListener('abort', this.#onAbort))\n\n this.#scheduleHeartbeat(heartbeatInterval)\n await this.#hooks.callHook('studio:connected', {\n url: studioUrl,\n versions: { studio: this.#studioVersion, kubb: kubbVersion, agent: this.#options.version },\n agentSlug: session.agentSlug,\n organizationSlug: session.organizationSlug,\n })\n // Studio registers the agent by calling connect() over RPC. Ready means that handshake landed.\n await this.#connectAck.promise\n await this.#hooks.callHook('studio:ready', {})\n } catch (error) {\n // A connector can fail after opening RPC and installing the heartbeat. Tear down every\n // partial resource before retrying, otherwise each retry leaks a timer and a live session.\n this.#disposed = true\n this.dispose()\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n if (error instanceof InvalidAgentTokenError) {\n throw error\n }\n\n reconnect(this.#options)\n }\n }\n\n #warn(message: string): Promise<void> | void {\n return this.#hooks.callHook('studio:warn', { message })\n }\n\n /**\n * Declines a request: logs why locally, then tells Studio. The two wordings differ on purpose,\n * since the log names the request that was ignored and the error names what the caller can do.\n */\n async #refuse(reason: string, message: string): Promise<never> {\n await this.#warn(reason)\n throw new Error(message)\n }\n\n #scheduleHeartbeat(interval: number): void {\n const rpc = this.#rpc\n if (!rpc) {\n return\n }\n this.#heartbeatTimer = setTimeout(async () => {\n try {\n await this.#ping(rpc)\n } catch {\n if (this.#rpc === rpc) {\n rpc.close()\n }\n return\n }\n\n if (this.#rpc === rpc) {\n this.#scheduleHeartbeat(interval)\n }\n }, interval)\n }\n\n /**\n * Races `studio.ping()` against a deadline, so a half-open socket can't hang it forever.\n * */\n #ping(rpc: RpcConnection): Promise<void> {\n const { promise: timedOut, reject: onTimeout } = Promise.withResolvers<never>()\n const timer = setTimeout(() => onTimeout(new Error('Heartbeat ping timed out')), agentDefaults.heartbeatTimeoutMs)\n\n return Promise.race([rpc.studio.ping(), timedOut]).finally(() => clearTimeout(timer))\n }\n\n /**\n * Reads `kubb.config.ts` and reports which plugin options Studio may edit.\n *\n * Skipped when the host did not grant `allowConfigEdit`. Not cached: the user can edit the file\n * between two Studio actions.\n */\n async #readConfigFileView(source?: string): Promise<ConfigFileView | undefined> {\n if (!this.#canEditConfig) {\n return undefined\n }\n\n try {\n return readConfig(source ?? (await read(this.#options.configFile)))\n } catch (error) {\n await this.#warn(`Could not read ${this.#options.configFile}: ${getErrorMessage(error)}`)\n\n return undefined\n }\n }\n\n async connect(): Promise<ConnectMessagePayload> {\n const { configPath, root, version, loadConfig, permissions } = this.#options\n const [config, file] = await Promise.all([loadConfig(), this.#readConfigFileView()])\n\n const payload: ConnectMessagePayload = {\n versions: { kubb: kubbVersion, agent: version },\n root,\n config: {\n path: configPath,\n file,\n plugins: config.plugins.map((plugin) => ({\n name: toPackageName(plugin.name),\n options: plugin.options ?? {},\n })),\n },\n permissions: {\n ...permissions,\n allowWrite: this.#canWrite,\n allowInput: this.#canUseInput,\n allowConfigEdit: this.#canEditConfig,\n allowRead: this.#canRead,\n },\n }\n this.#connectAck.resolve()\n return payload\n }\n\n #onAbort = (): void => void this.#end({ retry: false })\n\n #onClose = (): void => void this.#end({ retry: true })\n\n /**\n * Drops the socket and detaches every listener and timer this session added. Idempotent, and\n * safe before `connect` opened anything.\n *\n * @internal\n */\n dispose(): void {\n clearTimeout(this.#heartbeatTimer)\n this.#heartbeatTimer = undefined\n this.#rpc?.close()\n this.#rpc = undefined\n this.#connectAck.reject(new Error('Session ended before Studio called connect()'))\n\n for (const unhook of this.#unhooks) unhook()\n this.#unhooks.length = 0\n }\n\n /**\n * Ends the session: tells Studio it is over, drops the socket, and optionally reconnects.\n * `#disposed` keeps the close event from running this twice, and a shutdown from reconnecting.\n */\n async #end({ retry }: { retry: boolean }): Promise<void> {\n const { studioUrl, token, logLevel } = this.#options\n\n if (this.#disposed) {\n return\n }\n this.#disposed = true\n\n this.dispose()\n\n await this.#hooks.callHook('studio:disconnected', { reason: retry ? 'connection closed' : 'shutdown' })\n\n // Nothing to tell Studio about when the session never opened.\n if (this.#session) {\n // Already tearing down, so a failed disconnect changes nothing.\n await disconnect({ sessionId: this.#session.sessionId, studioUrl, token, slug: this.#session.slug, logLevel }).catch(() => {})\n }\n\n if (retry) {\n reconnect(this.#options)\n }\n }\n\n startGeneration(data: GenerateInput): GenerationRun {\n const generationStream = createGenerationStream(this.#hooks, data.jobId, {\n onGenerationEnd: (result) => {\n this.#lastGeneration = result\n },\n })\n const controller = new AbortController()\n const result = this.#runGeneration(data, controller)\n .then(async (value) => {\n await generationStream.close()\n return value\n })\n .catch((error) => {\n generationStream.fail(error)\n throw error\n })\n // A dispose can reject this with nobody holding it, which would otherwise be unhandled.\n void result.catch(() => {})\n\n return new GenerationRunTarget(\n generationStream.stream,\n result,\n async () => {\n controller.abort(new Error('Generation canceled'))\n },\n () => {\n controller.abort(new Error('Generation canceled'))\n generationStream.dispose()\n },\n )\n }\n\n async #runGeneration(data: GenerateInput, controller: AbortController): Promise<GenerateResult> {\n // Checked before the first `await`, so two calls in the same tick can't both pass.\n if (this.#isGenerating) {\n return this.#refuse('Ignored generate: a generation is already in progress', 'A generation is already in progress, please wait for it to finish')\n }\n this.#isGenerating = true\n\n const command = 'generate'\n const { root, loadConfig, permissions, client } = this.#options\n\n try {\n await this.#hooks.callHook('studio:command:start', { command })\n const config = await loadConfig()\n const patch = data.config\n const plugins = await mergePlugins(config.plugins, patch?.plugins)\n const adapter = await mergeAdapter(config.adapter, patch?.adapter)\n\n // A sandbox agent always uses the inline spec (empty string included, since it has no disk\n // file); a local agent only when opted in, and an empty or absent spec falls back to disk.\n const inputOverride = this.#isSandbox ? (patch?.input ?? '') : (permissions.allowInput && patch?.input) || undefined\n\n if (permissions.allowWrite && this.#isSandbox) {\n await this.#warn('Running in a sandbox, so writing files is disabled')\n }\n\n if (patch?.input && !this.#canUseInput) {\n // The Docker agent reads `allowInput` from `KUBB_AGENT_ALLOW_INPUT`. The CLI grants it\n // through `--allowInput` or the per-project prompt instead, so each host gets its own remedy.\n const remedy = client?.kind === 'cli' ? '--allowInput, or answer yes when kubb studio asks,' : 'KUBB_AGENT_ALLOW_INPUT=true'\n await this.#warn(`Ignored the spec from Studio; set ${remedy} to generate from it`)\n }\n\n const resolvedPlugins = plugins ?? config.plugins\n\n // The session's own emitter carries the run: the host's logger is already on it from\n // `connect`, and these two come off again below, so one run's listeners never see the next.\n // Cleared up front, filled the moment `kubb:generation:end` fires.\n this.#lastGeneration = undefined\n const diskFiles = this.#hasProjectOnDisk ? await listDisk({ root, outputPath: config.output.path, maxFiles: DISK_SNAPSHOT_MAX_FILES }) : undefined\n const disk = diskFiles\n ? await this.#generations.keep({ jobId: data.jobId, source: 'disk', files: diskFiles, maxSetMb: this.#limits.maxSnapshotMb })\n : undefined\n const detach = [setupHookListener(this.#hooks, root, controller.signal)]\n\n try {\n await generate({\n config: {\n ...config,\n root,\n input: inputOverride ?? config.input,\n storage: this.#canWrite ? fsStorage() : memoryStorage(),\n output: permissions.allowExec ? { ...config.output } : { ...config.output, format: false, lint: false, postGenerate: [] },\n plugins: resolvedPlugins,\n adapter,\n },\n hooks: this.#hooks,\n signal: controller.signal,\n })\n } catch (error) {\n await this.#generations.drop(data.jobId)\n throw error\n } finally {\n for (const remove of detach) remove()\n }\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `${resolvedPlugins.length} plugin${resolvedPlugins.length === 1 ? '' : 's'}, ${this.#canWrite ? 'written to disk' : 'in memory'}${inputOverride !== undefined ? ', from a Studio spec' : ''}`,\n })\n\n // The generate call above reassigns the field, but control flow analysis still sees the\n // `= undefined` from this method and narrows it to `never`.\n const generation = this.#lastGeneration as GenerationEnd | undefined\n const output = generation\n ? await this.#generations.keep({ jobId: data.jobId, source: 'output', files: generation.output, maxSetMb: this.#limits.maxMb })\n : undefined\n if (generation && output) {\n if (!output.paths.length && Object.keys(output.hashes).length) {\n await this.#warn('Kept only the hashes of this generation: its output is too large to keep')\n }\n const { peerDependencies, missingDependencies } = generation\n await this.#generations.add({ jobId: data.jobId, output, disk, peerDependencies, missingDependencies })\n }\n const files = [...(generation?.output.paths ?? [])]\n return {\n status: 'success',\n files,\n fileCount: files.length,\n hashes: output?.hashes ?? {},\n disk: disk ? { hashes: disk.hashes } : undefined,\n }\n } finally {\n this.#isGenerating = false\n }\n }\n\n async saveConfig(data: SaveConfigInput): Promise<SaveResult> {\n const command = 'saveConfig'\n await this.#hooks.callHook('studio:command:start', { command })\n const { configPath, configFile } = this.#options\n\n // Every RPC call gets one result. `edits` is checked before it is walked because values cross\n // the agent trust boundary.\n if (!Array.isArray(data.edits)) {\n await this.#warn('Ignored save: the message carried no edits')\n\n return { outcomes: [], changed: false }\n }\n\n const edits = data.edits\n const refuse = (reason: string): SaveResult => ({ outcomes: edits.map((edit) => ({ edit, applied: false, reason })), changed: false })\n\n if (!this.#canEditConfig) {\n await this.#warn('Ignored save: editing kubb.config.ts was not granted')\n\n return refuse('the agent was not granted permission to edit kubb.config.ts')\n }\n\n // A generation reloads the config while it runs, so rewriting the file underneath it would\n // leave that run working from half the change.\n if (this.#isGenerating) {\n return refuse('a generation is in progress')\n }\n\n try {\n // Read straight before the patch rather than reusing what went out on connect. The user may\n // have edited the file since, and since every untouched node keeps its own text, patching\n // what is on disk right now preserves that edit.\n const current = await read(configFile)\n const { source: patched, outcomes, changed } = applyConfigEdits(current, edits)\n\n if (changed) {\n // `writeFile` rather than the `write` helper: that one trims and re-terminates what it\n // writes, which is right for generated output and wrong for a file the user wrote by hand.\n await writeFile(configFile, patched, 'utf-8')\n }\n\n const applied = outcomes.filter((outcome) => outcome.applied).length\n await this.#hooks.callHook('studio:command:end', { command, info: `applied ${applied}/${outcomes.length} edits to ${configPath}` })\n return { outcomes, changed, file: changed ? await this.#readConfigFileView(patched) : undefined }\n } catch (error) {\n // An unreadable config, a read-only filesystem. Reported as a refusal of every edit so\n // Studio hears back rather than waiting on a reply that never comes.\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n return refuse(getErrorMessage(error))\n }\n }\n\n async publishSnapshot(data: PublishSnapshotInput): Promise<PublishSnapshotResult> {\n const command = 'snapshot'\n await this.#hooks.callHook('studio:command:start', { command })\n\n if (this.#isSandbox) {\n return this.#refuse('Ignored snapshot: a sandbox agent has no project to build a package from', 'A sandbox agent has no project to build a package from')\n }\n\n const { name, version, bundledDependencies, uploadPath } = data\n\n if (!name || !version || !uploadPath) {\n return this.#refuse('Ignored snapshot: the message was missing required fields', 'The request was missing required fields')\n }\n\n const generation = await this.#generations.latest()\n\n if (!generation) {\n return this.#refuse('Ignored snapshot: no prior generation to pack', 'No prior generation exists to pack, run a generation first')\n }\n\n const bundled = new Set(bundledDependencies ?? [])\n const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency))\n\n if (missing.length) {\n return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(', ')}`, `Missing dependencies: ${missing.join(', ')}`)\n }\n\n try {\n const files = await this.#generations.read({ generation, source: 'output', paths: generation.output.paths })\n\n const { bytes, integrity } = await createSnapshotPackage(files, { name, version, peerDependencies: generation.peerDependencies })\n\n // The tarball can't go on this request: Studio's handler answers before reading the body,\n // so the connection drops mid-upload. Ask for the redirect with an empty body first, then\n // PUT the bytes to wherever it points. That also keeps the bearer token off the storage\n // request, since it's a fresh call rather than a followed redirect.\n const { token, studioUrl } = this.#options\n const uploadUrl = new URL(uploadPath, studioUrl)\n if (uploadUrl.origin !== new URL(studioUrl).origin) {\n throw new Error('Snapshot upload path must stay on the Studio origin')\n }\n const redirect = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${token}` },\n redirect: 'manual',\n })\n const storageUrl = redirect.headers.get('location')\n if (redirect.status !== 307 || !storageUrl) {\n throw new Error(`Studio did not provide a storage URL (status ${redirect.status})`)\n }\n const storage = new URL(storageUrl)\n if (storage.protocol !== 'https:' && storage.hostname !== 'localhost' && storage.hostname !== '127.0.0.1') {\n throw new Error(`Refusing snapshot upload to ${storage.origin}`)\n }\n const response = await fetch(storage, { method: 'PUT', body: new Uint8Array(bytes), redirect: 'error' })\n if (!response.ok) {\n throw new Error(`Snapshot upload failed with status ${response.status}`)\n }\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `packed ${Object.keys(files).length} file${Object.keys(files).length === 1 ? '' : 's'}`,\n })\n return { integrity, peerDependencies: generation.peerDependencies }\n } catch (error) {\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n /**\n * An agent with a project on disk can show a run against what its output directory held before.\n * A sandbox agent has no project.\n */\n get #hasProjectOnDisk(): boolean {\n return !this.#isSandbox && this.#canRead\n }\n\n async readFiles(data: ReadFilesInput): Promise<{ files: Record<string, string> }> {\n const command = 'readFiles'\n await this.#hooks.callHook('studio:command:start', { command })\n const { client } = this.#options\n\n if (!this.#canRead) {\n await this.#warn('Ignored files: reading generated files was not granted')\n\n // Each host grants it a different way.\n const remedy = client?.kind === 'cli' ? '--allow-read, or answer yes when kubb studio asks,' : 'KUBB_AGENT_ALLOW_READ=true'\n throw new Error(`The agent was not granted permission to read generated files; set ${remedy} to allow it`)\n }\n\n // `paths` came off the wire, so check its shape before walking it.\n if (!Array.isArray(data.paths)) {\n return this.#refuse('Ignored files: the message carried no paths', 'The request carried no paths')\n }\n\n const { paths } = data\n\n if (paths.length > MAX_FILES_PER_REQUEST) {\n return this.#refuse(\n `Ignored files: requested ${paths.length} paths, more than the ${MAX_FILES_PER_REQUEST} allowed per request`,\n `At most ${MAX_FILES_PER_REQUEST} paths may be requested at once`,\n )\n }\n\n if (typeof data.jobId !== 'string' || !data.jobId) {\n return this.#refuse('Ignored files: the message named no job', 'The request named no generation job')\n }\n\n const generation = await this.#generations.get(data.jobId)\n\n if (!generation) {\n return this.#refuse(`Ignored files: job ${data.jobId} is not kept on this agent`, GENERATION_GONE_MESSAGE)\n }\n\n const source = data.source === 'disk' ? 'disk' : 'output'\n\n if (!generation[source]) {\n return this.#refuse('Ignored files: that job has no snapshot of the files on disk', 'This agent kept no snapshot of the files on disk for that job')\n }\n\n // Only paths the set holds are read, never an arbitrary path.\n const files = await this.#generations.read({ generation, source, paths })\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? '' : 's'}`,\n })\n return { files }\n }\n}\n","import type { Storage } from 'unstorage'\nimport { agentDefaults } from './constants.ts'\nimport type { InvalidAgentTokenError } from './api.ts'\nimport { registerAgent } from './api.ts'\nimport { StudioSession, type StudioSessionOptions } from './StudioSession.ts'\nimport { setStorage } from './machine.ts'\n\nexport type ClientOptions = Omit<StudioSessionOptions, 'signal' | 'onTokenRejected'> & {\n /**\n * Where the machine secret and the last Studio config are persisted. Defaults to in-memory,\n * which gives up a stable machine identity across restarts.\n */\n storage?: Storage\n /**\n * Called once when a live pool's token is rejected during background reconnect (401: revoked, or\n * the agent was deleted). The whole pool is already stopped by the time this fires, so a host\n * only needs to get a replacement token and start a new client.\n *\n * Never fires for a startup rejection, which `connect()` reports by throwing, nor for an ordinary\n * session expiry or revocation, both of which reconnect on their own.\n */\n onAuthRequired?: (error: InvalidAgentTokenError) => void\n}\n\nexport type Client = {\n /**\n * Registers with Studio and opens the session pool. Resolves once the pool is starting: the\n * sessions keep running, and reconnect on their own, until `disconnect` is called.\n */\n connect: () => Promise<void>\n /**\n * Closes every session and stops reconnecting.\n */\n disconnect: () => void\n}\n\n/**\n * Creates the Kubb Studio client: the connection, the command loop, and the generation event\n * stream shared by the `kubb studio` CLI command and the Docker agent.\n *\n * Every permission is off by default. A host that wants more grants it explicitly.\n *\n * @example\n * ```ts\n * const studio = createClient({ token, configPath, version, loadConfig: () => loadMyConfig() })\n * await studio.connect()\n * ```\n */\nexport function createClient({ storage, onAuthRequired, ...options }: ClientOptions): Client {\n if (storage) {\n setStorage(storage)\n }\n\n const controller = new AbortController()\n const poolSize = options.poolSize ?? agentDefaults.poolSize\n function notifyAuthRequired(error: InvalidAgentTokenError) {\n // Several pool sessions can reject the same token at once, and a host can stop the pool\n // itself, so an aborted controller is what says this callback is spent.\n if (controller.signal.aborted) {\n return\n }\n\n // Stop the whole pool first: every session's socket closes and every pending retry timer is\n // canceled through the `signal` each one already listens on, so the caller starts its next\n // client from a clean slate.\n controller.abort()\n onAuthRequired?.(error)\n }\n\n return {\n async connect() {\n await registerAgent({ token: options.token, studioUrl: options.studioUrl ?? agentDefaults.studioUrl, poolSize })\n\n // Each slot is its own session, so one Studio user never sees another's generation events.\n // Awaited: `connect()` only ever rejects with `InvalidAgentTokenError` (every other failure\n // is retried internally through the session's own reconnect loop and resolves normally), so\n // awaiting here surfaces a dead token to the caller without blocking on a down Studio.\n await Promise.all(\n Array.from({ length: poolSize }, () => new StudioSession({ ...options, signal: controller.signal, onTokenRejected: notifyAuthRequired }).start()),\n )\n },\n disconnect() {\n controller.abort()\n },\n }\n}\n","import { InvalidAgentTokenError } from './api.ts'\nimport { type ClientOptions, createClient } from './client.ts'\n\n/**\n * Why a connection ended: the host asked it to stop through its `signal`, or the host declined to\n * replace a rejected token.\n */\nexport type ConnectionOutcome = 'shutdown' | 'stopped'\n\n/**\n * A rejected token, and whether it was already serving a live session when Studio rejected it.\n */\nexport type TokenRejection<TCredentials> = {\n error: InvalidAgentTokenError\n /**\n * The credential Studio rejected, so a host can carry parts of it into the replacement.\n */\n credentials: TCredentials\n /**\n * `false` when the token was dead before a session ever opened, which is what `connect()` itself\n * reports. `true` when a live pool's background reconnect was rejected, well after the session\n * was up. Hosts treat the two differently: only the first has nothing to tear down.\n */\n live: boolean\n}\n\nexport type ConnectionOptions<TCredentials extends { token: string }> = {\n /**\n * The credential to open with. Only its token is read here, so a host keeps whatever else it\n * stores alongside.\n */\n credentials: TCredentials\n /**\n * Builds the client options for one attempt. Called again for every reconnect, so a host whose\n * options depend on which agent approved, such as the permissions it granted, re-derives them\n * rather than reusing the ones the rejected token was opened with.\n */\n clientOptions: (credentials: TCredentials) => Omit<ClientOptions, 'token' | 'onAuthRequired'>\n /**\n * Called when Studio rejects the token. Return the credential to reconnect with, or `null` to\n * end the run. Throwing fails it, which is what a host does when it cannot pair again.\n */\n onTokenRejected: (rejection: TokenRejection<TCredentials>) => Promise<TCredentials | null>\n /**\n * Aborting this disconnects and ends the run. Hosts wire it to their own shutdown: `SIGINT` in\n * the CLI, Nitro's `close` hook in the Docker agent.\n */\n signal?: AbortSignal\n}\n\n/**\n * Waits for whichever comes first: the shutdown signal, or Studio rejecting the token during a\n * background reconnect. Resolves with the rejection, or nothing when the run is being shut down.\n */\nfunction waitForRejection(authRequired: Promise<InvalidAgentTokenError>, signal?: AbortSignal): Promise<InvalidAgentTokenError | undefined> {\n // Nothing to race without a signal: a host without one ends the run some other way.\n if (!signal) {\n return authRequired\n }\n\n // `{ once: true }` drops the listener when the abort fires, not when the other side settles the\n // race, so `settled` covers that half. Without it a reconnected run leaves one behind on the\n // host's signal for every attempt it makes.\n const settled = new AbortController()\n const shutdown = new Promise<undefined>((resolve) => {\n if (signal.aborted) {\n resolve(undefined)\n return\n }\n signal.addEventListener('abort', () => resolve(undefined), { once: true, signal: settled.signal })\n })\n\n return Promise.race([shutdown, authRequired]).finally(() => settled.abort())\n}\n\n/**\n * Keeps a host connected to Studio across token changes: it opens a client, waits until the run\n * ends or Studio rejects the token, and reconnects with whatever credential the host hands back.\n *\n * The host owns everything around that. Where credentials live, whether a rejected token may be\n * replaced, and how any of it is reported are all decisions `onTokenRejected` makes.\n *\n * @example\n * ```ts\n * const outcome = await runConnection({\n * credentials,\n * clientOptions: () => ({ studioUrl, configPath, version, loadConfig }),\n * signal: shutdown.signal,\n * onTokenRejected: ({ error, live }) => pairAgain(error, live),\n * })\n * ```\n */\nexport async function runConnection<TCredentials extends { token: string }>({\n credentials,\n clientOptions,\n onTokenRejected,\n signal,\n}: ConnectionOptions<TCredentials>): Promise<ConnectionOutcome> {\n let current = credentials\n\n while (true) {\n // A shutdown can land outside the race below, while a host is pairing or prompting. Registering\n // one more agent with Studio only to drop it again is not what the operator asked for.\n if (signal?.aborted) {\n return 'shutdown'\n }\n\n const { promise: authRequired, resolve: notifyAuthRequired } = Promise.withResolvers<InvalidAgentTokenError>()\n const client = createClient({ ...clientOptions(current), token: current.token, onAuthRequired: notifyAuthRequired })\n\n let rejection: TokenRejection<TCredentials> | undefined\n\n try {\n await client.connect()\n\n const error = await waitForRejection(authRequired, signal)\n\n if (!error) {\n return 'shutdown'\n }\n\n rejection = { error, credentials: current, live: true }\n } catch (error) {\n // Every other failure is retried inside the session's own reconnect loop, so anything that\n // surfaces here is a dead token or a host's own bug.\n if (!(error instanceof InvalidAgentTokenError)) {\n throw error\n }\n\n rejection = { error, credentials: current, live: false }\n } finally {\n client.disconnect()\n }\n\n const next = await onTokenRejected(rejection)\n\n if (!next) {\n return 'stopped'\n }\n\n current = next\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { styleText } from 'node:util'\nimport { getErrorMessage } from '@internals/utils'\nimport { ofetch } from 'ofetch'\nimport { agentDefaults } from './constants.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * RFC 8628 device-authorization response from Studio's `/api/auth/device/code` endpoint.\n * Field names match the RFC; the CLI polls with `device_code` and shows `user_code` to the user.\n */\nexport type PairingSession = {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\nexport type PairingResult = {\n /**\n * Bearer token for this machine. Write credentials with mode 0600 and never log the value.\n */\n token: string\n agent: {\n /**\n * Stable agent id in Studio.\n */\n id: string\n /**\n * Short slug used in logs and the UI (for example `brave-otter`).\n */\n slug: string\n /**\n * Display name chosen at pairing time.\n */\n name: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none.\n */\n organizationSlug?: string\n }\n}\n\n/**\n * Identifies the CLI to Studio's device authorization endpoint. A label, not a secret: what\n * authorizes a pairing is a signed-in person approving the code in the browser.\n */\nconst CLIENT_ID = 'kubb-cli'\n\n/**\n * Thrown when a caller aborts `startPairing` or `pollForPairingToken` through their `signal`, such\n * as a `kubb studio` shutdown mid-pairing. Distinct from a denial or an expired code, so a host can\n * exit quietly instead of reporting a pairing failure.\n */\nexport class PairingCanceledError extends Error {\n constructor() {\n super('Pairing was canceled')\n this.name = 'PairingCanceledError'\n }\n}\n\ntype StartPairingOptions = {\n studioUrl?: string\n /**\n * Display name for the agent, usually the project or machine name.\n */\n name: string\n hostname: string\n /**\n * Which client is pairing. Defaults to the CLI, where any signed-in member may approve their own\n * machine. The Docker image passes `kubb-agent`, whose codes only an admin can approve.\n */\n clientId?: string\n /**\n * What a `kubb-agent` pairing asks to be registered as. Studio rejects the request without it,\n * and ignores it for the CLI.\n */\n agentKind?: 'user' | 'sandbox'\n /**\n * Aborting this cancels the request in flight and rejects with {@link PairingCanceledError}.\n */\n signal?: AbortSignal\n}\n\n/**\n * Asks Studio for a pairing code. The machine token travels with the request and is stored against\n * the code, so approval knows which machine it is pairing: the same machine pairing twice rotates\n * one agent's token instead of creating a second agent.\n */\nexport async function startPairing({\n studioUrl = agentDefaults.studioUrl,\n name,\n hostname,\n clientId = CLIENT_ID,\n agentKind,\n signal,\n}: StartPairingOptions): Promise<PairingSession> {\n try {\n return await ofetch<PairingSession>(`${studioUrl}/api/auth/device/code`, {\n method: 'POST',\n body: {\n client_id: clientId,\n name,\n hostname,\n machine_token: await getMachineToken(),\n agent_kind: agentKind,\n },\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n throw error\n }\n}\n\ntype PollOptions = {\n studioUrl?: string\n session: PairingSession\n /**\n * Aborting this stops polling and rejects with {@link PairingCanceledError}, whether the abort\n * lands between polls or during the wait for the next one.\n */\n signal?: AbortSignal\n}\n\ntype PollError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied' | 'invalid_grant'\n\ntype PollResponse =\n | PairingResult\n | {\n error: PollError | string\n /**\n * Why, when Studio has something more useful to say than the RFC code. An approval that hits\n * the organization's agent limit comes back as `access_denied` with the limit spelled out.\n */\n error_description?: string\n }\n\nfunction isPairingResult(response: PollResponse | undefined): response is PairingResult {\n return !!response && typeof response === 'object' && 'token' in response && typeof response.token === 'string'\n}\n\n/**\n * Polls until the user approves or denies, honoring the server's `slow_down` back-off. A poll that\n * cannot reach Studio is warned about and retried, since the code stays valid either way.\n *\n * Studio's own endpoint is used rather than the auth layer's `/device/token`, because an approved\n * Kubb pairing is worth an agent bearer token, not a user session.\n *\n * @throws when the code expires, the user denies it, or Studio returns an unexpected error.\n */\nexport async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, session, signal }: PollOptions): Promise<PairingResult> {\n // Both fields cross the network, so neither is trusted as-is: a missing or zero `interval` would\n // spin the poll loop, and a missing or zero `expires_in` would expire the code before the first\n // poll. `> 0` is also false for `NaN` and for a missing field, so it doubles as the type guard.\n const deadline = Date.now() + (session.expires_in > 0 ? session.expires_in : 600) * 1000\n let intervalMs = (session.interval > 0 ? session.interval : 5) * 1000\n\n while (Date.now() < deadline) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n try {\n await delay(intervalMs, undefined, { signal })\n } catch {\n throw new PairingCanceledError()\n }\n\n let response: PollResponse | undefined\n try {\n response = await ofetch<PollResponse | undefined>(`${studioUrl}/api/agent/token`, {\n method: 'POST',\n body: { device_code: session.device_code },\n // A denial, an expiry, and \"not yet\" all come back as 4xx with a body the caller needs to\n // read, so let every response through and switch on `error` instead of catching.\n ignoreResponseError: true,\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n // Studio can go briefly unreachable (a deploy, a dropped connection) during the minutes the\n // user has to approve in the browser. One failed poll should not end a pairing whose code is\n // still valid, so warn and try again on the next tick, the way `registerAgent` retries.\n console.warn(styleText('yellow', `Could not reach Kubb Studio while waiting for approval, retrying: ${getErrorMessage(error)}`))\n continue\n }\n\n if (isPairingResult(response)) {\n return response\n }\n\n if (!response || typeof response !== 'object' || !('error' in response) || typeof response.error !== 'string') {\n throw new Error('Kubb Studio returned an empty pairing response, pair again')\n }\n\n if (response.error === 'authorization_pending') {\n continue\n }\n\n if (response.error === 'slow_down') {\n intervalMs += 5_000\n continue\n }\n\n if (response.error === 'access_denied') {\n throw new Error(response.error_description ?? 'Pairing was denied in the browser')\n }\n\n if (response.error === 'expired_token' || response.error === 'invalid_grant') {\n throw new Error(response.error_description ?? 'The pairing code expired, pair again')\n }\n\n throw new Error(response.error_description ?? `Pairing failed (${response.error})`)\n }\n\n throw new Error('The pairing code expired, pair again')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAa,mBAAmB;;;;;AAMhC,MAAa,gBAAgB;CAC3B,WAAW;CACX,iBAAiB;CACjB,qBAAqB;;;;;;CAMrB,wBAAwB;;CAExB,oBAAoB;CACpB,UAAU;CACV,gBAAgB;CAChB,kBAAkB;CAClB,eAAe;AACjB;AAEA,SAAS,eAAe,OAA+C;CACrE,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,SAAS,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS,KAAA;AACnE;;;;;;AAOA,SAAgB,wBAAwB,MAAyB,QAAQ,KAAiE;CACxI,OAAO;EACL,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,eAAe,IAAI,0BAA0B,KAAK,cAAc,cAAc,CAAC;EAChH,OAAO,eAAe,IAAI,6BAA6B,KAAK,cAAc;EAC1E,eAAe,eAAe,IAAI,0BAA0B,KAAK,cAAc;CACjF;AACF;;;;;;;;;;AC1BA,SAAS,gBAAgB,MAAc,QAAyB;CAC9D,OAAO,KACJ,KAAK,CAAC,CACN,QAAQ,qBAAqB,OAAO,CAAC,CACrC,QAAQ,yBAAyB,OAAO,CAAC,CACzC,QAAQ,gBAAgB,OAAO,CAAC,CAChC,MAAM,eAAe,CAAC,CACtB,OAAO,OAAO,CAAC,CACf,KAAK,MAAM,MAAM;EAChB,IAAI,KAAK,SAAS,KAAK,SAAS,KAAK,YAAY,GAAG,OAAO;EAE3D,QADa,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,KAC9E,KAAK,MAAM,CAAC;CAC5B,CAAC,CAAC,CACD,KAAK,EAAE,CAAC,CACR,QAAQ,iBAAiB,EAAE;AAChC;;;;;;;;;;AAWA,SAAgB,UAAU,MAAc,EAAE,SAAS,IAAI,SAAS,OAAgB,CAAC,GAAW;CAC1F,OAAO,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,KAAK;AAC7D;;;;;;;;;;;;;;ACnCA,SAAgB,QAAQ,OAAuB;CAC7C,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACjE;;;;;;;;;;AAWA,SAAgB,gBAAgB,OAAwB;CACtD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;;;;;;;;AChBA,IAAM,UAAN,MAAc;;;;;;;;;;;;;;;CAeZ,IAAI,QAAiB;EACnB,OAAO,OAAO,QAAQ;CACxB;;;;CAKA,IAAI,SAAkB;EACpB,OAAO,OAAQ,WAAkC,SAAS;CAC5D;;;;;;CAOA,IAAI,SAAkB;EACpB,OAAO,CAAC,KAAK,SAAS,CAAC,KAAK,UAAU,OAAO,YAAY,eAAe,QAAQ,UAAU,QAAQ;CACpG;;;;;;;;;CAUA,IAAI,OAAoB;EACtB,IAAI,KAAK,OAAO,OAAO;EACvB,IAAI,KAAK,QAAQ,OAAO;EAExB,OAAO;CACT;;;;;;;;;CAUA,IAAI,UAAkB;EACpB,IAAI,KAAK,OAAO,OAAO,QAAQ,SAAS,OAAO;EAC/C,IAAI,KAAK,QAAQ,OAAQ,WAA0D,MAAM,SAAS,QAAQ;EAE1G,OAAO,QAAQ,UAAU,QAAQ;CACnC;AACF;;;;AAKA,MAAa,UAAU,IAAI,QAAQ;;;;;;;;;;;;AC7CnC,eAAsB,KAAK,MAA+B;CACxD,IAAI,QAAQ,OACV,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,KAAK;CAE7B,OAAO,SAAS,MAAM,EAAE,UAAU,OAAO,CAAC;AAC5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACoBA,SAAgB,QAAsB,OAA4B,SAAuD;CACvH,QAAQ,QAAsB;EAC5B,IAAI,MAAM,IAAI,GAAG,GAAG,OAAO,MAAM,IAAI,GAAG;EACxC,MAAM,QAAQ,QAAQ,GAAG;EACzB,MAAM,IAAI,KAAK,KAAK;EACpB,OAAO;CACT;AACF;;;;;;;;;;AA0BA,eAAsB,WAAkB,EAAE,OAAO,OAAO,OAA8C;CACpG,MAAM,QAAQ,MAAM,QAAQ;CAE5B,MAAM,SAAS,YAA2B;EACxC,KAAK,MAAM,CAAC,OAAO,SAAS,OAAO,MAAM,IAAI,MAAM,KAAK;CAC1D;CAEA,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM,EAAE,SAAS,OAAO,CAAC,CAAC;AACzF;;;;;;ACrFA,MAAa,aAAa;CACxB,UAAU;EACR,SAAS;EACT,OAAO,eAAuB;GAAC;GAAoB;GAAW;EAAU;EACxE,cAAc;CAChB;CACA,OAAO;EACL,SAAS;EACT,OAAO,eAAuB;GAAC;GAAU;GAAW;EAAU;EAC9D,cAAc;CAChB;CACA,OAAO;EACL,SAAS;EACT,OAAO,eAAuB,CAAC,UAAU;EACzC,cAAc;CAChB;AACF;;;;AAKA,MAAa,UAAU;CACrB,QAAQ;EACN,SAAS;EACT,OAAO,eAAuB,CAAC,YAAY,OAAO;EAClD,cAAc;CAChB;CACA,OAAO;EACL,SAAS;EACT,OAAO,eAAuB;GAAC;GAAQ;GAAS;EAAU;EAC1D,cAAc;CAChB;CACA,QAAQ;EACN,SAAS;EAET,OAAO,eAAuB;GAAC;GAAS;GAAe;EAAU;EACjE,cAAc;CAChB;AACF;;;;;AAMA,MAAa,uBAAuB;CAAC;CAAS;CAAS;AAAU;;;;AAKjE,MAAa,oBAAoB;CAAC;CAAU;CAAS;AAAQ;;;;AAK7D,SAAgB,gBAAgB,MAAgC;CAC9D,OAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,QAAQ,MAAM,MAAM,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;EAC5D,MAAM,GAAG,UAAU,SAAS,QAAQ,SAAS,CAAC,CAAC;EAC/C,MAAM,GAAG,eAAe,QAAQ,KAAK,CAAC;CACxC,CAAC;AACH;;;;;;;AAQA,eAAsBA,aAAiC,YAAyD;CAC9G,KAAK,MAAM,aAAa,YACtB,IAAI,MAAM,gBAAgB,SAAS,GACjC,OAAO;CAIX,OAAO;AACT;;;;;;;;;;;AAWA,SAAgB,SAAS,SAAgC;CACvD,QAAQ,QAAQ,MAAM,+BAA+B,KAAK,CAAC,EAAA,CAAG,KAAK,UAAU,MAAM,QAAQ,gBAAgB,EAAE,CAAC;AAChH;;;;;;;;;;;;;;AC7FA,SAAgB,aAAa,SAAmC;CAC9D,MAAM,CAAC,SAAS,eAAe,QAAQ,OAAO,OAAO;CACrD,MAAM,KAAK,UAAU,MAAO,cAAc;CAC1C,OAAO,KAAK,MAAM,KAAK,GAAG,IAAI;AAChC;;;;;;;;;;;ACDA,IAAI,UAAmB,cAAc;AACrC,IAAI,sBAAsB;;;;AAK1B,SAAgB,WAAW,MAAqB;CAC9C,UAAU;CACV,sBAAsB;AACxB;;;;;AAMA,SAAgB,kBAAkB,MAAuB;CACvD,OAAO,cAAc,EAAE,QAAQ,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;AACrD;AAEA,IAAI,wBAAgD;;;;;;;AAQpD,eAAe,6BAA8C;CAK3D,IAAI,CAAC,qBACH,QAAQ,KACN,UAAU,UAAU,kEAAkE,GACtF,sGACF;CAGF,MAAM,SAAS,MAAM,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,YAAY,IAAI;CAEvE,IAAI,OAAO,WAAW,YAAY,QAChC,OAAO;CAGT,MAAM,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAE7C,MAAM,QAAQ,QAAQ,kBAAkB,MAAM,CAAC,CAAC,YAAY;EAC1D,QAAQ,KACN,UAAU,UAAU,gDAAgD,GACpE,yEACF;CACF,CAAC;CAED,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,QAAwB;CACvD,OAAO,KAAK,UAAU,MAAM;AAC9B;;;;;;AAOA,eAAsB,kBAAmC;CACvD,IAAIC,UAAQ,IAAI,mBACd,OAAO,iBAAiBA,UAAQ,IAAI,iBAAiB;CAGvD,0BAA0B,2BAA2B;CAErD,OAAO,iBAAiB,MAAM,qBAAqB;AACrD;;;;;;;;AChFA,SAAS,gBAAgB,MAAmC;CAC1D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAC3B;CAGF,MAAM,OAAO;CACb,KAAK,MAAM,SAAS;EAAC,KAAK;EAAmB,KAAK;EAAS,KAAK;CAAK,GACnE,IAAI,OAAO,UAAU,YAAY,OAC/B,OAAO;AAKb;;;;AAKA,MAAM,mBAAmB;;;;AAKzB,IAAI,uBAAgD;;;;;;AAYpD,IAAa,yBAAb,cAA4C,MAAM;CAChD,YAAY,WAAmB,SAAwB;EACrD,MAAM,uFAAuF,UAAU,IAAI,OAAO;EAClH,KAAK,OAAO;CACd;AACF;;;;;;;;AASA,SAAS,aAAa,OAAgB,YAA6B;CACjE,OAAQ,OAA+C,eAAe;AACxE;AAEA,SAAS,aAAa,OAAuB;CAC3C,MAAM,UAAU,iBAAiB,aAAa,gBAAgB,MAAM,IAAI,IAAI,KAAA,MAAc,gBAAgB,KAAK;CAC/G,OAAO,IAAI,MAAM,SAAS,iDAAiD,WAAW,gDAAgD,EAAE,MAAM,CAAC;AACjJ;;;;AAKA,eAAe,oBAAoB,EAAE,OAAO,aAA0D;CACpG,MAAM,MAAM,GAAG,UAAU;CAEzB,MAAM,OAAO,MAAM,OAA6B,KAAK;EACnD,QAAQ;EACR,SAAS,EAAE,eAAe,UAAU,QAAQ;EAC5C,MAAM,EAAE,cAAc,MAAM,gBAAgB,EAAE;CAChD,CAAC;CAED,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,qCAAqC;CAGvD,OAAO;AACT;;;;;;;;AASA,eAAsB,mBAAmB,EAAE,OAAO,aAA0D;CAC1G,IAAI;EACF,OAAO,MAAM,oBAAoB;GAAE;GAAO;EAAU,CAAC;CACvD,SAAS,OAAgB;EACvB,IAAI,aAAa,OAAO,GAAG,GACzB,MAAM,IAAI,uBAAuB,WAAW,EAAE,OAAO,MAAM,CAAC;EAG9D,IAAI,CAAC,aAAa,OAAO,GAAG,KAAK,CAAE,MAAM,cAAc;GAAE;GAAO;EAAU,CAAC,GACzE,MAAM,aAAa,KAAK;EAG1B,IAAI;GACF,OAAO,MAAM,oBAAoB;IAAE;IAAO;GAAU,CAAC;EACvD,SAAS,YAAqB;GAC5B,IAAI,aAAa,YAAY,GAAG,GAC9B,MAAM,IAAI,uBAAuB,WAAW,EAAE,OAAO,WAAW,CAAC;GAGnE,MAAM,aAAa,UAAU;EAC/B;CACF;AACF;;;;;;;;;;;;AAmBA,SAAgB,cAAc,OAAwC;CACpE,yBAAyB,gBAAgB,KAAK,CAAC,CAAC,cAAc;EAC5D,uBAAuB;CACzB,CAAC;CAED,OAAO;AACT;AAEA,eAAe,gBAAgB,EAAE,OAAO,WAAW,YAA6C;CAC9F,MAAM,eAAe,MAAM,gBAAgB;CAE3C,IAAI;EACF,MAAM,OAAO,GAAG,UAAU,qBAAqB;GAC7C,QAAQ;GACR,SAAS,EACP,eAAe,UAAU,QAC3B;GACA,MAAM;IAAE;IAAc;GAAS;GAC/B,OAAO;GAEP,aAAa,EAAE,cAAc,MAAQ,MAAM,mBAAmB,OAAO,QAAQ,KAAK;EACpF,CAAC;EAED,OAAO;CACT,SAAS,OAAO;EACd,IAAI,aAAa,OAAO,GAAG,GACzB,MAAM,IAAI,uBAAuB,WAAW,EAAE,OAAO,MAAM,CAAC;EAG9D,QAAQ,MAAM,UAAU,OAAO,uDAA6E,CAAC;EAE7G,OAAO;CACT;AACF;;;;;;AAoBA,eAAsB,WAAW,EAAE,WAAW,OAAO,WAAW,MAAM,UAAA,cAA4C;CAChH,MAAM,MAAM,GAAG,UAAU,sBAAsB,UAAU;CACzD,MAAM,MAAM,QAAQ;CACpB,MAAM,SAASC,eAAa,KAAA,KAAaA,aAAWC,SAAY;CAEhE,IAAI;EACF,MAAM,OAAO,KAAK;GAChB,QAAQ;GACR,SAAS,EACP,eAAe,UAAU,QAC3B;EACF,CAAC;EAGD,IAAI,QACF,QAAQ,MAAM,UAAU,SAAS,IAAI,IAAI,2BAA2B,CAAC;CAEzE,SAAS,OAAO;EACd,MAAM,aAAc,OAA+C;EACnE,IAAI,eAAe,KAAA,KAAa,cAAc,OAAO,aAAa,KAAK;EAEvE,IAAI,QACF,QAAQ,KAAK,UAAU,UAAU,IAAI,IAAI,8CAA8C,gBAAgB,KAAK,GAAG,CAAC;CAEpH;AACF;;;;;;;;;;;;;;;;;;;;AAkFA,eAAsB,UAAU,EAC9B,WACA,OACA,MACA,SACA,MACA,SACA,UASqB;CACrB,MAAM,EAAE,QAAQ,MAAM,OAA2B,GAAG,UAAU,YAAY;EACxE,QAAQ;EACR,SAAS,EAAE,aAAa,MAAM;EAC9B,MAAM;GAAE;GAAM;GAAS;GAAM;GAAS;EAAO;CAC/C,CAAC;CAED,OAAO;AACT;;;;AAKA,MAAM,wBAAwB;;;;;AAM9B,MAAM,uBAAuB;;;;;;;;;AAU7B,eAAsB,WAAW,EAC/B,WACA,OACA,IACA,YAAY,OAWS;CACrB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,WAAW;CAEf,SAAS;EACP,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,IAAI,KAAK,IAAI,UAAU,WAAW,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;EAE1G,IAAI,KAAK,IAAI,KAAK,UAAU,MAAM,IAAI,MAAM,sCAAsC;EAElF,WAAW,KAAK,IAAI,WAAW,GAAG,oBAAoB;EAEtD,IAAI;GAEF,MAAM,EAAE,QAAQ,MAAM,OAA2B,GAAG,UAAU,YAAY,MAAM;IAC9E,SAAS,EAAE,aAAa,MAAM;IAC9B,OAAO;GACT,CAAC;GAED,IAAI,IAAI,WAAW,aAAa,IAAI,WAAW,YAAY,IAAI,WAAW,YAAY,OAAO;EAC/F,SAAS,OAAO;GACd,MAAM,WAAY,MAA0F;GAE5G,IAAI,UAAU,WAAW,KAAK,MAAM;GAEpC,MAAM,aAAa,SAAS,OAAO,MAAM;GAIzC,WAAW,KAAK,IAAI,UAHL,OAAO,eAAe,YAAY,OAAO,SAAS,UAAU,KAAK,aAAa,IAGtD,aAAa,oBAAoB;EAC1E;CACF;AACF;;;;;;AA6BA,eAAsB,YAAY,EAChC,WACA,OACA,MACA,gBAMuB;CACvB,IAAI;EACF,OAAO,MAAM,OAAoB,GAAG,UAAU,cAAc;GAC1D,QAAQ;GACR,SAAS,EAAE,aAAa,MAAM;GAC9B,MAAM;IAAE;IAAM;GAAa;EAC7B,CAAC;CACH,SAAS,OAAgB;EACvB,IAAI,iBAAiB,YAAY;GAC/B,MAAM,aAAc,MAAM,MAAyD,MAAM;GACzF,MAAM,SAAS,gBAAgB,MAAM,IAAI,KAAK,gBAAgB,KAAK;GACnE,MAAM,OAAO,aAAa,oCAAoC,WAAW,KAAK;GAC9E,MAAM,IAAI,MAAM,yCAAyC,SAAS,QAAQ,EAAE,OAAO,MAAM,CAAC;EAC5F;EAEA,MAAM;CACR;AACF;;;;;;;;;;;;;;AEvUA,SAAgB,kBAAkB,OAA4B,MAAc,QAAkC;CAC5G,OAAO,MAAM,KAAK,mBAAmB,OAAO,QAAQ;EAClD,MAAM,EAAE,IAAI,SAAS,SAAS;EAE9B,IAAI,CAAC,IACH;EAGF,MAAM,kBAAkB,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,KAAK,GAAG,MAAM;EAExE,IAAI;GACF,MAAM,OAAO,EAAE,SAAS,CAAC,GAAI,QAAQ,CAAC,CAAE,GAAG;IACzC;IACA,aAAa;KAAE,KAAK;KAAM,UAAU;IAAK;GAC3C,CAAC;GAED,WAAW,MAAM,QAAQ,MACvB,MAAM,MAAM,SAAS,kBAAkB;IAAE;IAAI;GAAK,CAAC;GAGrD,MAAM,EAAE,aAAa,MAAM;GAE3B,IAAI,aAAa,GAAG;IAClB,MAAM,wBAAQ,IAAI,MAAM,wBAAwB,iBAAiB;IAEjE,MAAM,MAAM,SAAS,iBAAiB;KAAE;KAAI;KAAS;KAAM,SAAS;KAAO;IAAM,CAAC;IAClF,MAAM,MAAM,SAAS,cAAc,EAAE,MAAM,CAAC;IAE5C;GACF;GAEA,MAAM,MAAM,SAAS,iBAAiB;IAAE;IAAI;IAAS;IAAM,SAAS;IAAM,OAAO;GAAK,CAAC;EACzF,SAAS,aAAa;GACpB,MAAM,wBAAQ,IAAI,MAAM,wBAAwB,iBAAiB;GACjE,MAAM,QAAQ;GAEd,MAAM,MAAM,SAAS,iBAAiB;IAAE;IAAI;IAAS;IAAM,SAAS;IAAO;GAAM,CAAC;GAClF,MAAM,MAAM,SAAS,cAAc,EAAE,MAAM,CAAC;EAC9C;CACF,CAAC;AACH;;;;;;AAOA,SAAgB,eAAe,OAA4B,QAA+B;CACxF,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,iBAAiB,QAAiE;GACtF,IAAI,IAAI,OAAO,QAAQ;GACvB,MAAM,WAAW,iBAAiB,aAAa;GAE/C,IAAI,IAAI,SAAS;IACf,QAAQ;IACR;GACF;GACA,OAAO,IAAI,KAAK;EAClB;EAEA,MAAM,KAAK,iBAAiB,aAAa;CAC3C,CAAC;AACH;;;;;;;;;;ACzJA,eAAe,kBAAkB,aAAuD;CACtF,IAAI;EACF,OAAO,MAAM,OAAO;CACtB,QAAQ;EAIN,MAAM,WAHU,cAAc,cAAc,GAAG,QAAQ,IAAI,EAAE,EAAE,CAGxC,CAAC,CAAC,QAAQ,WAAW;EAC5C,MAAM,MAAM,SAAS,QAAQ,UAAU,KAAK;EAE5C,OAAO,MAAM,OAAO,cAAc,QAAQ,YAAY,WAAW,GAAG,IAAI,MAAM,QAAQ,CAAC,CAAC;CAC1F;AACF;;;;;;;;;;AAWA,SAAS,aAAa,aAA6B;CACjD,OAAO,YAAY,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;AACzC;;;;;;;;;;;;AAaA,SAAgB,cAAc,MAAsB;CAClD,OAAO,KAAK,WAAW,SAAS,IAAI,SAAS,SAAS;AACxD;;;;;;;;;;AAWA,SAAgB,aAAa,aAA6B;CACxD,OAAO,UAAU,aAAa,WAAW,CAAC;AAC5C;;;;;;AAOA,MAAM,wBAAwB;;;;;AAM9B,SAAgB,sBAAsB,MAAuB;CAC3D,OAAO,sBAAsB,KAAK,IAAI;AACxC;;;;;;;;;;;;AAaA,eAAe,kBAAkB,aAA6C;CAC5E,IAAI,CAAC,sBAAsB,WAAW,GACpC,MAAM,IAAI,MAAM,WAAW,YAAY,iFAAiF;CAG1H,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,kBAAkB,WAAW;CAC3C,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,WAAW,YAAY,kEAAkE,YAAY,KAAK,EAAE,MAAM,CAAC;CACrI;CAEA,MAAM,aAAa,aAAa,WAAW;CAE3C,IAAI,OAAO,IAAI,gBAAgB,YAAY,OAAO,IAAI;CAEtD,IAAI,OAAO,IAAI,eAAe,YAAY,OAAO,IAAI;CAErD,MAAM,IAAI,MAAM,WAAW,YAAY,gEAAgE,WAAW,iBAAiB;AACrI;;;;;;;;;;;;;;AAeA,eAAsB,eAAe,SAAyE;CAC5G,OAAO,QAAQ,IACb,QAAQ,IAAI,OAAO,EAAE,MAAM,cAAc;EAEvC,QAAO,MADe,kBAAkB,IAAI,EAAA,CAC7B,WAAW,CAAC,CAAC;CAC9B,CAAC,CACH;AACF;;;;;;;;;;AAWA,eAAsB,aACpB,aACA,eACoC;CAIpC,MAAM,gBAAgB,IAAI,KAAK,iBAAiB,CAAC,EAAA,CAAG,QAAQ,UAAU,MAAM,QAAQ,CAAC,CAAC,KAAK,UAAU,aAAa,MAAM,IAAI,CAAC,CAAC;CAC9H,MAAM,oBAAoB,cAAc,OAAO,aAAa,QAAQ,WAAW,CAAC,cAAc,IAAI,OAAO,IAAI,CAAC,IAAI;CAClH,MAAM,sBAAsB,eAAe,QAAQ,UAAU,CAAC,MAAM,QAAQ;CAE5E,IAAI,CAAC,qBAAqB,CAAC,qBAAqB,QAAQ,OAAO,KAAA;CAC/D,IAAI,CAAC,qBAAqB,QAAQ,OAAO;CAEzC,IAAI,CAAC,mBAAmB,OAAO,eAAe,mBAAmB;CAEjE,MAAM,oBAAoB,IAAI,IAAI,oBAAoB,KAAK,UAAU,CAAC,aAAa,MAAM,IAAI,GAAG,KAAK,CAAU,CAAC;CAChH,MAAM,YAAY,IAAI,IAAI,kBAAkB,KAAK,WAAW,OAAO,IAAI,CAAC;CAIxE,MAAM,SAAS,MAAM,QAAQ,IAC3B,kBAAkB,IAAI,OAAO,eAAe;EAC1C,MAAM,cAAc,kBAAkB,IAAI,WAAW,IAAI;EACzD,IAAI,CAAC,aAAa,OAAO;EAKzB,MAAM,UAAU,UAAW,WAAW,WAAuC,CAAC,GAAI,YAAY,WAAuC,CAAC,CAAC;EACvI,MAAM,CAAC,YAAY,MAAM,eAAe,CAAC;GAAE,MAAM,YAAY;GAAM;EAAQ,CAAC,CAAC;EAE7E,OAAO,YAAY;CACrB,CAAC,CACH;CAEA,MAAM,aAAa,oBAAoB,QAAQ,UAAU,CAAC,UAAU,IAAI,aAAa,MAAM,IAAI,CAAC,CAAC;CAEjG,OAAO,CAAC,GAAG,QAAQ,GAAI,MAAM,eAAe,UAAU,CAAE;AAC1D;;;;;;;;;;AAWA,eAAsB,aAAa,aAAkC,eAAiE;CACpI,IAAI,CAAC,iBAAiB,CAAC,aACrB,OAAO;CAGT,MAAM,cAAc,iBAAiB,YAAY;CAEjD,MAAM,WAAU,MADE,kBAAkB,WAAW,EAAA,CAC3B,aAAa,WAAW;CAE5C,IAAI,OAAO,YAAY,YACrB,OAAO;CAKT,OAAO,QAFe,UAAW,YAAY,WAAuC,CAAC,GAAG,aAE7D,CAAC;AAC9B;;;;;;;ACxNA,MAAM,aAAa;;;;;;;AAuBnB,SAAS,gBAAgB,EAAE,KAAK,SAAkE;CAChG,MAAM,QAAS,SAAS,QAAQ,GAAG,MAAM,MAAM,CAAC,CAAC,CAA2B,WAAW;CACvF,OAAO;EAAE,MAAM;EAAkB,KAAK,MAAM;EAAK,OAAO,MAAM;EAAO,UAAU;EAAO,WAAW;CAAM;AACzG;;;;;;AAyBA,MAAM,kBAAkB;;;;AAKxB,SAAS,aAAa,QAAgB,WAAmB,SAAS,IAAY;CAC5E,OAAO,GAAG,OAAO,KAAK,gBAAgB,GAAG,OAAO,GAAG;AACrD;;;;AAKA,SAAS,YAAY,MAAiE;CACpF,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,CAAC,QAAQ,WAAW,MAAM,gBAAgB,EAAE,GAC9C;CAGF,MAAM,QAAQ,QAAQ,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,MAAM,gBAAgB;CACnF,OAAO,QAAQ;EAAE,QAAQ,MAAM;EAAK,WAAW,OAAO,MAAM,EAAE;CAAE,IAAI,KAAA;AACtE;;;;;AAMA,SAAS,OAAO,MAAuD;CACrE,IAAI,CAAC,MACH;CAEF,IAAI,KAAK,SAAS,oBAAoB,KAAK,SAAS,yBAClD,OAAO,OAAO,KAAK,UAAU;CAE/B,IAAI,KAAK,SAAS,6BAA6B,KAAK,SAAS,sBAC3D,OAAO;CAET,IAAI,KAAK,KAAK,SAAS,kBACrB,OAAO,OAAO,KAAK,IAAI;CAIzB,OAAO,OADU,KAAK,KAAK,KAAK,MAAM,cAA0E,UAAU,SAAS,iBAC9G,CAAC,EAAE,QAAQ;AAClC;;;;;;;;;AAUA,SAAS,YAAY,KAA2E;CAI9F,MAAM,WAAW,QAHJ,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,OAAO,CAAC,EAAA,CACnC,MAAM,SAAyE,KAAK,SAAS,0BAEpF,CAAC,EAAE,WAAW;CAChD,IAAI,CAAC,UACH,OAAO,EAAE,QAAQ,0BAA0B;CAE7C,IAAI,SAAS,SAAS,oBAAoB,SAAS,OAAO,SAAS,gBAAgB,SAAS,OAAO,SAAS,gBAC1G,OAAO,EAAE,QAAQ,iDAAiD;CAGpE,MAAM,WAAW,OAAO,SAAS,UAAU,EAAE;CAC7C,IAAI,CAAC,UACH,OAAO,EAAE,QAAQ,gDAAgD;CAEnE,IAAI,SAAS,SAAS,mBAAmB;EACvC,MAAM,UAA6B,CAAC;EAEpC,KAAK,MAAM,WAAW,SAAS,UAAU;GACvC,MAAM,QAAQ,OAAO,OAAO;GAC5B,IAAI,OAAO,SAAS,oBAClB,OAAO,EAAE,QAAQ,kCAAkC;GAErD,QAAQ,KAAK,KAAK;EACpB;EACA,OAAO,EAAE,QAAQ;CACnB;CACA,IAAI,SAAS,SAAS,oBACpB,OAAO,EAAE,QAAQ,kCAAkC;CAErD,OAAO,EAAE,SAAS,CAAC,QAAQ,EAAE;AAC/B;;;;AAKA,SAAS,aAAa,SAA4B,KAAoD;CACpG,IAAI,QAAQ,KAAA,GACV,OAAO,QAAQ;CAEjB,IAAI,OAAO,QAAQ,UACjB,OAAO,QAAQ;CAEjB,OAAO,QAAQ,MAAM,WAAW,WAAW,MAAM,MAAM,GAAG;AAC5D;AAEA,SAAS,WAAW,QAAwC;CAC1D,MAAM,OAAO,SAAS,QAAQ,MAAM;CACpC,OAAO,MAAM,SAAS,kBAAkB,KAAK,QAAQ,KAAA;AACvD;;;;;AAMA,SAAS,YAAY,OAAyE;CAC5F,IAAI,MAAM,IAAI,SAAS,cACrB,OAAO,MAAM,IAAI;CAEnB,IAAI,MAAM,IAAI,SAAS,iBACrB,OAAO,MAAM,IAAI;AAGrB;;;;AAKA,SAAS,WAAW,EAAE,MAAM,OAAkD;CAC5E,OAAO,KAAK,WAAW,WAAW,UAAU,MAAM,SAAS,oBAAoB,YAAY,KAAK,MAAM,GAAG;AAC3G;;;;AAKA,SAAS,SAAS,MAAkB,KAAkC;CACpE,MAAM,QAAQ,WAAW;EAAE;EAAM;CAAI,CAAC;CACtC,OAAO,UAAU,KAAK,KAAA,IAAa,KAAK,WAAW,MAAM,CAAwB;AACnF;;;;;;;AAQA,SAAS,YAAY,EAAE,MAAM,KAAK,SAAsE;CACtG,MAAM,QAAQ,gBAAgB;EAAE;EAAK;CAAM,CAAC;CAC5C,MAAM,QAAQ,WAAW;EAAE;EAAM;CAAI,CAAC;CAEtC,IAAI,UAAU,IAAI;EAChB,KAAK,WAAW,KAAK,KAAK;EAC1B;CACF;CACC,KAAM,WAAW,MAAM,CAAwB,QAAQ,MAAM;AAChE;;;;AAKA,SAAS,eAAe,EAAE,MAAM,OAAgD;CAC9E,MAAM,QAAQ,WAAW;EAAE;EAAM;CAAI,CAAC;CACtC,IAAI,UAAU,IACZ,KAAK,WAAW,OAAO,OAAO,CAAC;AAEnC;;;;;;AAOA,SAAS,YAAY,MAAoD;CACvE,IAAI,CAAC,MACH;CAEF,IAAI,KAAK,SAAS,mBAAmB,KAAK,SAAS,oBAAoB,KAAK,SAAS,kBACnF,OAAO,KAAK;CAEd,IAAI,KAAK,SAAS,eAChB,OAAO;CAET,IAAI,KAAK,SAAS,mBAChB,OAAO,KAAK,YAAY,WAAW,IAAK,KAAK,OAAO,EAAE,EAAE,MAAM,UAAU,KAAM,KAAA;CAEhF,IAAI,KAAK,SAAS,mBAAmB;EACnC,MAAM,QAAQ,YAAY,KAAK,QAAQ;EACvC,IAAI,OAAO,UAAU,UACnB;EAEF,IAAI,KAAK,aAAa,KACpB,OAAO,CAAC;EAEV,IAAI,KAAK,aAAa,KACpB,OAAO;EAET;CACF;CACA,IAAI,KAAK,SAAS,mBAAmB;EACnC,MAAM,SAAS,KAAK,SAAS,KAAK,YAAa,UAAU,YAAY,OAAO,IAAI,KAAA,CAAU;EAC1F,OAAO,OAAO,OAAO,UAAU,UAAU,KAAA,CAAS,IAAI,SAAS,KAAA;CACjE;CACA,IAAI,KAAK,SAAS,oBAAoB;EACpC,MAAM,UAAuC,CAAC;EAC9C,KAAK,MAAM,SAAS,KAAK,YAAY;GACnC,IAAI,MAAM,SAAS,kBACjB;GAEF,MAAM,MAAM,YAAY,KAAK;GAC7B,MAAM,QAAQ,YAAY,MAAM,KAAK;GACrC,IAAI,QAAQ,KAAA,KAAa,UAAU,KAAA,GACjC;GAEF,QAAQ,OAAO;EACjB;EACA,OAAO;CACT;AAEF;;;;AAKA,SAAS,aAAa,KAA2C;CAC/D,OAAO,IAAI,IAAI,IAAI,QAAQ,OAAO,KAAK,SAAS,CAAC,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC;AAC1E;;;;AAKA,SAAS,YAAY,KAAsB,QAAwF;CACjI,MAAM,UAAU,SAAS,QAAQ,SAAS;CAC1C,IAAI,SAAS,SAAS,mBACpB,OAAO,CAAC;CAGV,MAAM,UAAU,aAAa,GAAG;CAEhC,OAAO,QAAQ,SAAS,SAAS,YAAY;EAC3C,IAAI,SAAS,SAAS,oBAAoB,QAAQ,OAAO,SAAS,cAChE,OAAO,CAAC;EAEV,MAAM,cAAc,QAAQ,IAAI,QAAQ,OAAO,IAAI;EACnD,OAAO,cAAc,CAAC;GAAE,YAAY,QAAQ,OAAO;GAAM;GAAa,MAAM;EAAQ,CAAC,IAAI,CAAC;CAC5F,CAAC;AACH;;;;;;AAOA,SAAS,gBAAgB,QAA8D;CACrF,OAAO,OAAO,MAAM,IAAI,CAAC,CAAC,SAAS,MAAM,UAAU;EACjD,MAAM,SAAS,YAAY,IAAI;EAC/B,OAAO,SAAS,CAAC;GAAE,aAAa,OAAO;GAAQ,MAAM,QAAQ;EAAE,CAAC,IAAI,CAAC;CACvE,CAAC;AACH;;;;;;;;;;;;AAaA,SAAgB,WAAW,QAAgC;CACzD,IAAI;CACJ,IAAI;EACF,MAAM,YAAY,MAAM;CAC1B,QAAQ;EACN,OAAO;GAAE,SAAS;GAAO,QAAQ;EAAsC;CACzE;CAEA,MAAM,QAAQ,YAAY,GAAG;CAC7B,IAAI,YAAY,OACd,OAAO;EAAE,SAAS;EAAO,QAAQ,MAAM;CAAO;CAGhD,MAAM,cAAc,IAAI,IAAI,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC;CACxF,MAAM,WAAW,gBAAgB,MAAM;CAEvC,OAAO;EACL,SAAS;EACT,SAAS,MAAM,QAAQ,KAAK,WAAuB;GACjD,MAAM,UAAU,YAAY,KAAK,MAAM,CAAC,CAAC,KAAK,EAAE,YAAY,aAAa,WAAuB;IAC9F,MAAM,UAAiC,CAAC;IACxC,MAAM,UAAU,KAAK,UAAU;IAE/B,IAAI,SAAS,SAAS,oBACpB,KAAK,MAAM,SAAS,QAAQ,YAAY;KACtC,IAAI,MAAM,SAAS,kBACjB;KAEF,MAAM,MAAM,YAAY,KAAK;KAC7B,IAAI,QAAQ,KAAA,GACV;KAEF,MAAM,QAAQ,YAAY,MAAM,KAAK;KACrC,QAAQ,OAAO,UAAU,KAAA,IAAY,EAAE,SAAS,MAAM,IAAI;MAAE,SAAS;MAAM;KAAM;IACnF;IAEF,OAAO;KAAE;KAAY;KAAa,SAAS;IAAQ;GACrD,CAAC;GAED,MAAM,QAAQ,OAAO,KAAK,MAAM,QAAQ;GACxC,MAAM,MAAM,OAAO,KAAK,IAAI,QAAQ,OAAO;GAE3C,KAAK,MAAM,EAAE,iBAAiB,SAAS,QAAQ,UAAU,MAAM,QAAQ,SAAS,MAAM,QAAQ,GAAG,GAC/F,QAAQ,KAAK;IACX,YAAY,YAAY,IAAI,WAAW,KAAK,aAAa,WAAW;IACpE;IACA,SAAS,CAAC;IACV,UAAU;GACZ,CAAC;GAGH,OAAO;IAAE,MAAM,WAAW,MAAM;IAAG;GAAQ;EAC7C,CAAC;CACH;AACF;;;;;;;AAQA,SAAgB,cAAc,OAAsC;CAClE,IAAI,UAAU,MACZ,OAAO;CAET,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAChD,OAAO;CAET,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,SAAS,KAAK;CAE9B,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,MAAM,aAAa;CAElC,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,aAAa;CAEjD,OAAO;AACT;;;;AAKA,SAAS,WAAW,MAAwC;CAC1D,MAAM,UAAU,KAAK,UAAU;CAC/B,OAAO,SAAS,SAAS,qBAAqB,UAAU,KAAA;AAC1D;;;;AAKA,SAAS,cAAc,MAAwC;CAC7D,IAAI,KAAK,UAAU,WAAW,GAC5B,KAAK,UAAU,KAAK;EAAE,MAAM;EAAoB,YAAY,CAAC;CAAE,CAAC;CAElE,OAAO,WAAW,IAAI;AACxB;;;;AAKA,SAAS,aAAa,SAAqB,MAA+E;CACxH,IAAI,SAAS;CAEb,KAAK,MAAM,CAAC,OAAO,QAAQ,KAAK,QAAQ,GAAG;EACzC,IAAI,UAAU,KAAK,SAAS,GAC1B,OAAO;GAAE;GAAQ;EAAI;EAGvB,IAAI,SAAS,QAAQ,GAAG,MAAM,KAAA,GAC5B,YAAY;GAAE,MAAM;GAAQ;GAAK,OAAO,CAAC;EAAE,CAAC;EAG9C,MAAM,OAAO,SAAS,QAAQ,GAAG;EACjC,IAAI,MAAM,SAAS,oBACjB,OAAO,EAAE,QAAQ,GAAG,IAAI,wBAAwB,KAAK,KAAK,GAAG,EAAE,oBAAoB;EAErF,SAAS;CACX;CACA,OAAO,EAAE,QAAQ,uBAAuB;AAC1C;;;;;;AAOA,SAAS,SAAS,MAAgB,MAAqB,OAAoC;CACzF,IAAI,CAAC,cAAc,KAAK,GACtB,OAAO;CAGT,MAAM,UAAU,cAAc,IAAI;CAClC,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,SAAS,aAAa,SAAS,IAAI;CACzC,IAAI,YAAY,QACd,OAAO,OAAO;CAGhB,MAAM,UAAU,SAAS,OAAO,QAAQ,OAAO,GAAG;CAClD,IAAI,YAAY,KAAA,KAAa,YAAY,OAAO,MAAM,KAAA,GACpD,OAAO,GAAG,KAAK,KAAK,GAAG,EAAE;CAG3B,YAAY;EAAE,MAAM,OAAO;EAAQ,KAAK,OAAO;EAAK;CAAM,CAAC;AAE7D;;;;;;AAOA,SAAS,YAAY,MAAgB,MAAyC;CAC5E,MAAM,UAAU,WAAW,IAAI;CAC/B,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,SAAS,aAAa,SAAS,IAAI;CACzC,IAAI,YAAY,QACd,OAAO,OAAO;CAGhB,MAAM,UAAU,SAAS,OAAO,QAAQ,OAAO,GAAG;CAClD,IAAI,YAAY,KAAA,GACd,OAAO,GAAG,KAAK,KAAK,GAAG,EAAE;CAE3B,IAAI,YAAY,OAAO,MAAM,KAAA,GAC3B,OAAO,GAAG,KAAK,KAAK,GAAG,EAAE;CAG3B,eAAe;EAAE,MAAM,OAAO;EAAQ,KAAK,OAAO;CAAI,CAAC;AAEzD;;;;;AAYA,SAAS,eAAe,KAAsB,QAAoB,MAAyE;CACzI,IAAI,CAAC,sBAAsB,KAAK,MAAM,GACpC,OAAO,EAAE,QAAQ,IAAI,KAAK,OAAO,mCAAmC;CAGtE,MAAM,aAAa,KAAK,cAAc,aAAa,KAAK,MAAM;CAC9D,IAAI,CAAC,WAAW,KAAK,UAAU,GAC7B,OAAO,EAAE,QAAQ,IAAI,WAAW,8BAA8B;CAGhE,IAAI,YAAY,KAAK,MAAM,CAAC,CAAC,MAAM,WAAW,OAAO,gBAAgB,KAAK,MAAM,GAC9E,OAAO,EAAE,MAAM,KAAK;CAGtB,MAAM,QAAQ,aAAa,GAAG,CAAC,CAAC,IAAI,UAAU;CAC9C,IAAI,SAAS,UAAU,KAAK,QAC1B,OAAO,EAAE,QAAQ,GAAG,WAAW,4BAA4B,QAAQ;CAGrE,MAAM,UAAU,KAAK,WAAW,CAAC;CACjC,IAAI,CAAC,cAAc,OAAO,GACxB,OAAO,EAAE,QAAQ,oEAAoE;CAGvF,MAAM,UAAU,SAAS,QAAQ,SAAS;CAC1C,IAAI,SAAS,SAAS,mBACpB,OAAO,EAAE,QAAQ,kCAAkC;CAGrD,MAAM,OAAO,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,SAAS,aAAa,YAAY,OAAO,IAAI,SAAS,aAAa,UAAU;CACxH,QAAQ,SAAS,KAAK,KAAK,IAAgB;CAE3C,OAAO,QAAQ,CAAC,IAAI,EAAE,WAAW;EAAE;EAAY,iBAAiB,KAAK;CAAO,EAAE;AAChF;;;;;;;AAQA,SAAS,cAAc,QAAgB,KAAsB,QAAoB,QAAyD;CACxI,MAAM,SAAS,YAAY,KAAK,MAAM,CAAC,CAAC,MAAM,UAAU,MAAM,gBAAgB,MAAM;CACpF,IAAI,CAAC,QACH,OAAO,EAAE,QAAQ,GAAG,OAAO,8BAA8B;CAG3D,MAAM,MAAM,OAAO,KAAK;CACxB,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,KACtB,OAAO,EAAE,QAAQ,GAAG,OAAO,wCAAwC;CAGrE,MAAM,QAAQ,OAAO,MAAM,IAAI;CAC/B,MAAM,OAAO,IAAI,MAAM,OAAO;CAC9B,MAAM,KAAK,IAAI,IAAI,OAAO;CAC1B,MAAM,YAAY,MAAM,SAAS;CACjC,MAAM,WAAW,MAAM,OAAO;CAK9B,IAAI,UAAU,MAAM,GAAG,IAAI,MAAM,MAAM,CAAC,CAAC,KAAK,MAAM,MAAM,CAAC,UAAU,KAAK,SAAS,MAAM,IAAI,IAAI,MAAM,CAAC,GACtG,OAAO,EAAE,QAAQ,GAAG,OAAO,sEAAsE;CAGnG,MAAM,SAAS,UAAU,MAAM,MAAM,CAAC,GAAG,MAAM;CAC/C,MAAM,YAAY,MAAM,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK,SAAU,KAAK,KAAK,IAAI,GAAG,OAAO,KAAK,KAAK,MAAM,OAAO,MAAM,MAAM,SAAS,GAAG,OAAO,MAAM,IAAK;CACpJ,MAAM,OAAO,MAAM,KAAK,OAAO,GAAG,aAAa,QAAQ,UAAU,QAAQ,MAAM,GAAG,GAAG,SAAS;CAE9F,OAAO,EAAE,QAAQ,MAAM,KAAK,IAAI,EAAE;AACpC;;;;AAKA,SAAS,aAAa,QAAgB,QAAyD;CAC7F,MAAM,QAAQ,OAAO,MAAM,IAAI;CAE/B,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,QAAQ,GAAG;EAC3C,MAAM,SAAS,YAAY,IAAI;EAC/B,IAAI,QAAQ,WAAW,QACrB;EAKF,MAAM,MAAM,QAAQ,IAAI,OAAO;EAC/B,MAAM,WAAW,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,KAAK,cAAc,UAAU,QAAQ,gBAAgB,IAAI,CAAC;EACvG,MAAM,OAAO,OAAO,MAAM,OAAO,GAAG,QAAQ;EAE5C,OAAO,EAAE,QAAQ,MAAM,KAAK,IAAI,EAAE;CACpC;CAEA,OAAO,EAAE,QAAQ,GAAG,OAAO,kBAAkB;AAC/C;;;;;;AAOA,SAAS,YAAY,QAAgB,KAA+F;CAClI,IAAI;CACJ,IAAI;EACF,MAAM,YAAY,MAAM;CAC1B,QAAQ;EACN,OAAO,EAAE,QAAQ,sCAAsC;CACzD;CAEA,MAAM,QAAQ,YAAY,GAAG;CAC7B,IAAI,YAAY,OACd,OAAO,EAAE,QAAQ,MAAM,OAAO;CAGhC,MAAM,SAAS,aAAa,MAAM,SAAS,GAAG;CAC9C,IAAI,CAAC,QACH,OAAO,EAAE,QAAQ,6BAA6B,KAAK,UAAU,GAAG,IAAI;CAGtE,OAAO;EAAE;EAAK;CAAO;AACvB;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,iBAAiB,QAAgB,OAAuC;CACtF,IAAI,UAAU;CACd,MAAM,SAAS,iBAAiB,MAAM;CACtC,MAAM,kBAAkB,OAAO,SAAS,IAAI;CAE5C,MAAM,WAAW,MAAM,KAAK,SAA4B;EACtD,MAAM,SAAS,YAAY,SAAS,KAAK,MAAM;EAC/C,IAAI,YAAY,QACd,OAAO;GAAE;GAAM,SAAS;GAAO,QAAQ,OAAO;EAAO;EAEvD,MAAM,EAAE,KAAK,WAAW;EAExB,IAAI,KAAK,cAAc,oBAAoB,KAAK,cAAc,iBAAiB;GAC7E,MAAM,SAAS,KAAK,cAAc,mBAAmB,cAAc,SAAS,KAAK,QAAQ,KAAK,MAAM,IAAI,aAAa,SAAS,KAAK,MAAM;GACzI,IAAI,YAAY,QACd,OAAO;IAAE;IAAM,SAAS;IAAO,QAAQ,OAAO;GAAO;GAEvD,UAAU,OAAO;GACjB,OAAO;IAAE;IAAM,SAAS;GAAK;EAC/B;EAEA,IAAI,KAAK,cAAc,cAAc;GACnC,MAAM,SAAS,eAAe,KAAK,QAAQ,IAAI;GAC/C,IAAI,YAAY,QACd,OAAO;IAAE;IAAM,SAAS;IAAO,QAAQ,OAAO;GAAO;GAEvD,IAAI,UAAU,QACZ,OAAO;IAAE;IAAM,SAAS;GAAK;GAE/B,MAAM,YAAY,kBAAkB,GAAG;GACvC,IAAI,OAAO,aAAa,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;GACzC,IAAI,OAAO,WACT,OAAO,iBAAiB;IAAE,QAAQ;IAAM;IAAW,GAAG,OAAO;GAAU,CAAC;GAE1E,UAAU,oBAAoB,MAAM,eAAe;GACnD,OAAO;IAAE;IAAM,SAAS;GAAK;EAC/B;EAEA,MAAM,aAAa,YAAY,KAAK,MAAM,CAAC,CAAC,MAAM,WAAW,OAAO,gBAAgB,KAAK,MAAM;EAC/F,IAAI,CAAC,YACH,OAAO;GAAE;GAAM,SAAS;GAAO,QAAQ,GAAG,KAAK,OAAO;EAA8B;EAGtF,MAAM,SAAS,KAAK,cAAc,QAAQ,SAAS,WAAW,MAAM,KAAK,MAAM,KAAK,KAAK,IAAI,YAAY,WAAW,MAAM,KAAK,IAAI;EACnI,IAAI,CAAC,QACH,UAAU,oBAAoB,aAAa,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,eAAe;EAEnF,OAAO;GAAE;GAAM,SAAS,CAAC;GAAQ;EAAO;CAC1C,CAAC;CAED,OAAO;EAAE,QAAQ;EAAS;EAAU,SAAS,YAAY;CAAO;AAClE;;;;;;AAOA,SAAS,kBAAkB,KAA8B;CAGvD,QAFa,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,OAAO,CAAC,EAAA,CAEhD,QAAQ,SAAS,KAAK,SAAS,mBAAmB,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,IAAI,QAAQ;AAC3F;;;;;;;;;AAUA,SAAS,iBAAiB,EACxB,QACA,YACA,iBACA,aAMS;CACT,MAAM,QAAQ,OAAO,MAAM,IAAI;CAE/B,MAAM,iBAAiB,YAAY,IAAI,MAAM,YAAY,KAAK,KAAA;CAC9D,MAAM,QAAQ,gBAAgB,SAAS,GAAG,IAAI,MAAM;CAEpD,MAAM,OAAO,YAAY,WAAW,UAAU,QAAQ,kBAAkB,QADtD,gBAAgB,QAAQ,CAAC,CAAC,SAAS,GAAG,IAAI,MAAM;CAGlE,MAAM,OAAO,WAAW,GAAG,GAAI,YAAY,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAE;CAEnE,OAAO,MAAM,KAAK,IAAI;AACxB;;;;AAKA,SAAS,oBAAoB,MAAc,oBAAqC;CAC9E,IAAI,CAAC,sBAAsB,KAAK,SAAS,IAAI,GAC3C,OAAO;CAET,OAAO,GAAG,KAAK;AACjB;;;;;;;;AC7tBA,MAAM,aAAa,wBAAQ,IAAI,IAAmD,GAAGC,YAAkB;;;;;;;;;AAUvG,MAAM,aACJ,CAGE;CAAE,MAAM;CAAU,MAAM;CAAa,SAAS;CAAc,OAAO;CAAY,QAAQ;AAAqB,GAC5G;CAAE,MAAM;CAAQ,MAAM;CAAU,SAAS;CAAW,OAAO;CAAS,QAAQ;AAAkB,CAChG;;;;AAKF,SAAS,WAAW,QAAwB;CAC1C,OAAO,KAAK,WAAW,OAAO,OAAO,IAAI,IAAI,OAAO,OAAO,OAAO,KAAK,QAAQC,UAAQ,IAAI,GAAG,OAAO,MAAM,OAAO,OAAO,IAAI;AAC/H;;;;;;;AAmBA,eAAe,QAAQ,EAAE,OAAO,IAAI,SAAS,QAAqC;CAChF,MAAM,SAAS,KAAK,UAAU,EAAE;CAGhC,MAAM,UAAU,eAAe,OAAO,MAAM;CAE5C,MAAM,MAAM,SAAS,mBAAmB;EAAE,IAAI;EAAQ;EAAS,MAAM,CAAC,GAAG,IAAI;CAAE,CAAC;CAChF,MAAM;AACR;AAQA,SAAS,yBAAyB,YAAyF;CACzH,QAAQ,WAAW,QAAQ,eAAe,aAAa,WAAW,aAAa;AACjF;;;;AAKA,SAAS,wBAAwB,aAA+C;CAC9E,MAAM,UAAU,YACb,OAAO,wBAAwB,CAAC,CAChC,KAAK,eAAgB,WAAW,SAAS,GAAG,WAAW,OAAO,IAAI,WAAW,YAAY,WAAW,OAAQ;CAE/G,IAAI,CAAC,QAAQ,QACX,uBAAO,IAAI,MAAM,mBAAmB;CAGtC,uBAAO,IAAI,MAAM,sBAAsB,QAAQ,OAAO,QAAQ,QAAQ,WAAW,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,GAAG;AACxH;;;;;;;;AASA,eAAsB,SAAS,EAAE,QAAQ,OAAO,UAAwC;CACtF,QAAQ,eAAe;CACvB,MAAM,UAAUA,UAAQ,OAAO;CAE/B,MAAM,MAAM,SAAS,yBAAyB,EAAE,OAAO,CAAC;CAExD,MAAM,MAAM,SAAS,aAAa,EAAE,SAAS,OAAO,OAAO,oBAAoB,OAAO,SAAS,mBAAmB,CAAC;CAEnH,MAAM,OAAO,WAAW,QAAQ;EAAE;EAAO;CAAO,CAAC;CACjD,MAAM,KAAK,MAAM;CAEjB,MAAM,MAAM,SAAS,aAAa,EAAE,SAAS,OAAO,OAAO,oBAAoB,OAAO,SAAS,mBAAmB,CAAC;CAEnH,MAAM,EAAE,OAAO,aAAa,YAAY,MAAM,KAAK,UAAU;CAC7D,QAAQ,eAAe;CAEvB,MAAM,MAAM,SAAS,aAAa,EAAE,SAAS,eAAe,CAAC;CAK7D,KAAK,MAAM,cAAc,YAAY,OAAO,wBAAwB,GAClE,MAAM,MAAM,SAAS,cAAc,EAAE,OAAO,IAAI,MAAM,WAAW,SAAS,GAAG,WAAW,OAAO,IAAI,WAAW,YAAY,WAAW,OAAO,EAAE,CAAC;CAGjJ,MAAM,SAAS,YAAY,SAAS,WAAW,IAAI,WAAW;CAE9D,MAAM,MAAM,SAAS,uBAAuB;EAC1C;EAGA,SAAS;GAAE,GAAG;GAAS,UAAU,YAAY,CAAC,GAAG,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC;EAAE;EAC1F;EACA;EACA;EACA,cAAc,MAAM;CACtB,CAAC;CAED,IAAI,WAAW,UACb,MAAM,wBAAwB,WAAW;CAG3C,MAAM,MAAM,SAAS,gBAAgB,EAAE,SAAS,0BAA0B,CAAC;CAE3E,KAAK,MAAM,QAAQ,YAAY;EAC7B,MAAM,UAAU,OAAO,OAAO,KAAK;EACnC,IAAI,CAAC,SACH;EAGF,MAAM,MAAM,SAAS,QAAQ,KAAK,KAAK,OAAO;EAG9C,MAAM,OAAO,YAAY,SAAS,MAAM,WAAW,KAAK,MAAM,IAAI;EAElE,IAAI,CAAC,MACH,MAAM,MAAM,SAAS,aAAa,EAAE,SAAS,MAAM,KAAK,KAAK,UAAU,KAAK,OAAO,KAAK,IAAI,EAAE,cAAc,KAAK,QAAQ,YAAY,EAAE,GAAG,CAAC;EAG7I,IAAI,QAAQ,YAAY,QACtB,MAAM,MAAM,SAAS,aAAa,EAAE,SAAS,iBAAiB,KAAK,KAAK,IAAI,UAAU,OAAO,IAAI,IAAI,CAAC;EAGxG,MAAM,UAAU,OAAO,KAAK,MAAM,QAAQ,KAAA;EAE1C,IAAI,SACF,IAAI;GACF,MAAM,QAAQ;IAAE;IAAO,IAAI,CAAC,OAAO,MAAM,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;IAAG,SAAS,QAAQ;IAAS,MAAM,QAAQ,KAAK,WAAW,MAAM,CAAC;GAAE,CAAC;GAE5I,MAAM,MAAM,SAAS,gBAAgB,EAAE,SAAS,GAAG,KAAK,QAAQ,QAAQ,KAAK,eAAe,CAAC;EAC/F,SAAS,aAAa;GACpB,MAAM,MAAM,SAAS,cAAc,EAAE,OAAO,IAAI,MAAM,QAAQ,cAAc,EAAE,OAAO,YAAY,CAAC,EAAE,CAAC;GAErG,QAAQ,eAAe;EACzB;EAGF,MAAM,MAAM,SAAS,QAAQ,KAAK,KAAK,KAAK;CAC9C;CAGA,IAAI,OAAO,OAAO,cAAc,QAAQ;EACtC,MAAM,MAAM,SAAS,kBAAkB;EAEvC,KAAK,MAAM,SAAS,OAAO,OAAO,cAAc;GAC9C,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;GACvD,MAAM,CAAC,KAAK,GAAG,QAAQ,SAAS,IAAI;GAEpC,IAAI,CAAC,KACH;GAGF,MAAM,QAAQ;IAAE;IAAO,IAAI;IAAM,SAAS;IAAK;GAAK,CAAC;GACrD,MAAM,MAAM,SAAS,gBAAgB,EAAE,SAAS,GAAG,KAAK,wBAAwB,CAAC;EACnF;EAEA,MAAM,MAAM,SAAS,gBAAgB;CACvC;AACF;;;ACpMA,MAAM,YAAY,UAAU,IAAI;;;;;;AAUhC,SAAS,YAAY,UAA0B;CAC7C,MAAM,aAAa,SAAS,WAAW,MAAM,GAAG;CAMhD,OAAO,YALc,WAAW,MAAM,qBAAqB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,WAAW,QAAQ,QAAQ,EAAE,EAAA,CAExG,MAAM,GAAG,CAAC,CACV,QAAQ,SAAS,QAAQ,SAAS,OAAO,SAAS,IAAI,CAAC,CACvD,KAAK,GACa;AACvB;;;;;;AAOA,SAAS,eAAe,MAAgD;CACtE,IAAI,OAAO,WAAW,MAAM,MAAM,KAAK,KACrC,OAAO;EAAE,MAAM;EAAM,QAAQ;CAAG;CAGlC,KAAK,IAAI,IAAI,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;EACzC,IAAI,KAAK,OAAO,KAAK;EAErB,MAAM,SAAS,KAAK,MAAM,GAAG,CAAC;EAC9B,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;EAC7B,IAAI,OAAO,WAAW,QAAQ,MAAM,KAAK,OAAO,OAAO,WAAW,MAAM,MAAM,KAAK,KACjF,OAAO;GAAE;GAAM;EAAO;CAE1B;CAEA,MAAM,IAAI,MAAM,8CAA8C,MAAM;AACtE;AAEA,SAAS,OAAO,MAAc,MAAsB;CAClD,MAAM,EAAE,MAAM,WAAW,eAAe,IAAI;CAC5C,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,MAAM,MAAM,MAAM,GAAG,MAAM;CAC3B,MAAM,MAAM,aAAa,KAAK,OAAO;CACrC,MAAM,MAAM,aAAa,KAAK,OAAO;CACrC,MAAM,MAAM,aAAa,KAAK,OAAO;CACrC,MAAM,MAAM,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO;CACnE,MAAM,MACJ,GAAG,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,CAAC,CAC7B,SAAS,CAAC,CAAC,CACX,SAAS,IAAI,GAAG,EAAE,KACrB,KACA,OACF;CACA,MAAM,KAAK,IAAI,KAAK,GAAG;CACvB,MAAM,MAAM,KAAK,KAAK,OAAO;CAC7B,MAAM,MAAM,WAAW,KAAK,OAAO;CACnC,MAAM,MAAM,MAAM,KAAK,OAAO;CAC9B,MAAM,MAAM,aAAa,KAAK,OAAO;CACrC,MAAM,MAAM,aAAa,KAAK,OAAO;CACrC,MAAM,MAAM,QAAQ,KAAK,MAAM;CAC/B,MAAM,WAAW,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,KAAK,SAAS,MAAM,MAAM,CAAC;CAC/D,MAAM,MAAM,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,MAAM,KAAK,OAAO;CACvE,OAAO;AACT;;;;;AAMA,eAAsB,sBAAsB,OAAsB,aAA6E;CAC7I,MAAM,OAAO,MAAM,QAAQ,KAAK,OAAO,GAAG,gBAAgB,CAAC;CAC3D,MAAM,OAAO,KAAK,MAAM,MAAM;CAK9B,MAAM,gBAAgB,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,cAAc;EAAE;EAAM;EAAS,QAAQ,YAAY,IAAI;CAAE,EAAE;CACnH,MAAM,+BAAe,IAAI,IAAoB;CAC7C,KAAK,MAAM,EAAE,MAAM,YAAY,eAAe;EAC5C,MAAM,QAAQ,aAAa,IAAI,MAAM;EACrC,IAAI,OACF,MAAM,IAAI,MAAM,oEAAoE,OAAO,MAAM,MAAM,SAAS,KAAK,EAAE;EAEzH,aAAa,IAAI,QAAQ,IAAI;CAC/B;CAEA,IAAI;EACF,MAAM,MAAM,IAAI;EAChB,MAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,EAAE,SAAS,aAAa;GAC/C,MAAM,OAAO,KAAK,MAAM,OAAO,MAAM,CAAiB,CAAC;GACvD,MAAM,MAAM,KAAK,MAAM,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;GACjD,MAAM,UAAU,MAAM,OAAO;EAC/B,CAAC,CACH;EACA,MAAM,gBAAgB,cAAc,QAAQ,EAAE,WAAW,sBAAsB,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,MAAM,OAAO,MAAM,CAAiB,CAAC,CAAC;EAC1J,IAAI,cAAc,QAChB,MAAM,MAAM;GACV,OAAO;GACP,QAAQ;GACR,QAAQ,CAAC,OAAO,KAAK;GACrB,KAAK;GACL,WAAW;GACX,UAAU;GACV,QAAQ;GACR,UAAU;GAIV,gBAAgB;EAClB,CAAC;EACH,MAAM,eAAe,MAAM,QAAQ,KAChC,MAAM,MAAM,UAAU,KAAK,QAAQ;GAAE,KAAK;GAAM,eAAe;EAAK,CAAC,CAAC,EAAA,CACpE,QAAQ,UAAU,MAAM,OAAO,CAAC,CAAC,CACjC,IAAI,OAAO,UAAU;GAIpB,MAAM,WAAW,KAAK,MAAM,YAAY,MAAM,IAAI;GAElD,OAAO,CAAC,gBADiB,SAAS,MAAM,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAC3B,KAAK,MAAM,SAAS,UAAU,MAAM,CAAC;EAC9E,CAAC,CACL;EAGA,MAAM,aAAa,IAAI,IAAI,aAAa,KAAK,CAAC,UAAU,IAAI,CAAC;EAC7D,MAAM,YAAY,WAAW,IAAI,wBAAwB,KAAK,WAAW,IAAI,wBAAwB;EACrG,MAAM,eAAe,YAAY;GAAE,MAAM;GAAoB,QAAQ;EAAmB,IAAI,CAAC;EAC7F,MAAM,eAAe,YAAY,EAAE,KAAK;GAAE,QAAQ;GAAoB,SAAS;EAAmB,EAAE,IAAI,CAAC;EAEzG,MAAM,UAAU;GACd,wBAAwB,KAAK,UAC3B;IACE,GAAG;IACH,MAAM;IACN,GAAG;IAGH,SAAS;KAAE,GAAG;KAAc,OAAO;MAAE,QAAQ;MAAgB,SAAS;KAAe;IAAE;GACzF,GACA,MACA,CACF;GACA,GAAG,OAAO,YAAY,cAAc,KAAK,EAAE,SAAS,aAAa,CAAC,QAAQ,OAAO,CAAC,CAAC;GACnF,GAAG,OAAO,YAAY,YAAY;EACpC;EACA,MAAM,SAAwB,CAAC;EAE/B,KAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,OAAO,GAAG;GACrD,MAAM,QAAQ,OAAO,KAAK,OAAO;GACjC,OAAO,KAAK,OAAO,MAAM,MAAM,MAAM,GAAG,OAAO,OAAO,OAAO,MAAO,MAAM,SAAS,OAAQ,GAAG,CAAC;EACjG;EAEA,MAAM,QAAQ,MAAM,UAAU,OAAO,OAAO,CAAC,GAAG,QAAQ,OAAO,MAAM,IAAI,CAAC,CAAC,CAAC;EAC5E,OAAO;GAAE;GAAO,WAAW,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,QAAQ;EAAI;CAC7F,UAAU;EACR,MAAM,GAAG,MAAM;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACjD;AACF;;;ACtKA,MAAM,mBAAmB;AACzB,MAAM,KAAK;AACX,MAAM,YAAY;AAgClB,MAAM,UAAU,YAAoB,WAAW,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;;;;;AAMhG,eAAsB,SAAS,EAAE,MAAM,YAAY,YAAsG;CACvJ,MAAM,YAAY,QAAQ,MAAM,UAAU;CAC1C,MAAM,WAAW,SAAS,QAAQ,IAAI,GAAG,SAAS;CAClD,IAAI,CAAC,YAAY,SAAS,WAAW,IAAI,KAAK,SAAS,WAAW,GAAG,GAAG,OAAO,KAAA;CAE/E,MAAM,UAAU,UAAU;CAC1B,MAAM,OAAO,MAAM,QAAQ,SAAS,SAAS;CAC7C,IAAI,KAAK,SAAS,UAAU,OAAO,KAAA;CAEnC,MAAM,QAAQ,KACX,QAAQ,QAAQ,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,SAAS,cAAc,CAAC,CAAC,CACzD,KAAK,QAAQ,SAAS,QAAQ,IAAI,GAAG,QAAQ,WAAW,GAAG,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC;CACtF,OAAO;EAAE;EAAS;EAAM,OAAO,IAAI,IAAI,KAAK;CAAE;AAChD;;;;;;;AAQA,SAAgB,sBAAsB,EAAE,SAAS,UAAU,SAAgE;CACzH,IAAI;CAGJ,MAAM,SAAS,UAAkB,sBAAsB,OAAO,KAAK,EAAE;CAErE,eAAe,OAAuC;EACpD,IAAI,OAAO,OAAO;EAClB,MAAM,SAAS,MAAM,QAAQ,SAAS,SAAS,CAAC,CAAC,YAAY,IAAI;EACjE,IAAI;GACF,QAAQ,SAAU,KAAK,MAAM,MAAM,IAA8B,CAAC;EACpE,QAAQ;GACN,QAAQ,CAAC;EACX;EACA,OAAO;CACT;;;;CAKA,eAAe,KAAK,EAAE,OAAO,QAAQ,OAAO,YAAiH;EAC3J,MAAM,cAAc,WAAW;EAC/B,MAAM,SAAiC,CAAC;EACxC,IAAI,QAAQ;EACZ,MAAM,WAAW;GACf,OAAO,CAAC,GAAG,MAAM,KAAK;GACtB,OAAO;GACP,KAAK,OAAO,SAAS;IAEnB,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,SAAS,IAAI,GAAG;IACpC,MAAM,UAAU,MAAM,MAAM,QAAQ,SAAS,QAAQ,MAAM,MAAM,IAAI,CAAC;IACtE,IAAI,YAAY,MAAM;IACtB,OAAO,QAAQ,OAAO,OAAO;IAC7B,SAAS,OAAO,WAAW,OAAO;IAClC,IAAI,SAAS,aAAa,MAAM,QAAQ,UAAU,GAAG,MAAM,KAAK,IAAI,OAAO,GAAG,QAAQ,OAAO;GAC/F;EACF,CAAC;EACD,IAAI,QAAQ,aAAa;GACvB,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE;GAC/C,OAAO;IAAE,OAAO,CAAC;IAAG;IAAQ,OAAO;GAAE;EACvC;EACA,OAAO;GAAE,OAAO,OAAO,KAAK,MAAM;GAAG;GAAQ;EAAM;CACrD;CAEA,eAAe,KAAK,OAA8B;EAChD,MAAM,QAAQ,MAAM,MAAM,KAAK,CAAC;CAClC;CAEA,OAAO;EACL;EACA;EACA,KAAK,OAAO,WAAmB,MAAM,KAAK,EAAA,CAAG,MAAM,eAAe,WAAW,UAAU,KAAK;EAC5F,QAAQ,aAAa,MAAM,KAAK,EAAA,CAAG,GAAG,EAAE;EACxC,MAAM,IAAI,YAA2C;GACnD,MAAM,WAAW,MAAM,KAAK,EAAA,CAAG,QAAQ,UAAU,MAAM,UAAU,WAAW,KAAK;GACjF,QAAQ,KAAK,UAAU;GACvB,MAAM,eAAe,QAAQ,QAAQ,KAAK,EAAE,QAAQ,WAAW,MAAM,OAAO,SAAS,MAAM,SAAS,IAAI,CAAC;GACzG,OAAO,QAAQ,SAAS,MAAM,QAAQ,SAAS,YAAY,OAAO,IAAI,QAAQ,KAC5E,MAAM,KAAK,QAAQ,MAAM,CAAC,CAAE,KAAK;GAEnC,QAAQ;GACR,MAAM,QAAQ,UAAU,WAAW,KAAK,UAAU,OAAO,CAAC;EAC5D;;;;EAIA,MAAM,KAAK,EAAE,YAAY,QAAQ,SAA0H;GACzJ,MAAM,OAAO,IAAI,IAAI,WAAW,OAAO,EAAE,KAAK;GAC9C,MAAM,QAAgC,CAAC;GACvC,MAAM,WAAW;IACf,OAAO,MAAM,QAAQ,SAAS,KAAK,IAAI,IAAI,CAAC;IAC5C,OAAO;IACP,KAAK,OAAO,SAAS;KACnB,MAAM,UAAU,MAAM,QAAQ,SAAS,GAAG,MAAM,WAAW,KAAK,IAAI,OAAO,GAAG,MAAM;KACpF,IAAI,YAAY,MAAM,MAAM,QAAQ;IACtC;GACF,CAAC;GACD,OAAO;EACT;CACF;AACF;;;;;;;AClIA,MAAM,qBAAqB;AAE3B,MAAM,UAAU,cAAc,YAAY,GAAG;AAE7C,SAAS,oBAAoB,MAAc,UAA0B;CACnE,QAAQ,WAAW,QAAQ,IAAI,SAAS,QAAQ,IAAI,GAAG,QAAQ,IAAI,SAAA,CAAU,WAAW,MAAM,GAAG;AACnG;AAMA,eAAe,wBAAwB,OAGpC;CACD,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,aAAa,CAAC,CAAC;CACzD,MAAM,mBAA2C,CAAC;CAClD,MAAM,sBAAqC,CAAC;CAE5C,MAAM,WAAW,MAAM,QAAQ,IAC7B,YAAY,IAAI,OAAO,SAAS;EAC9B,IAAI;GACF,MAAM,OAAO,QAAQ,QAAQ,GAAG,KAAK,cAAc;GAEnD,OADoB,KAAK,MAAM,MAAM,SAAS,MAAM,MAAM,CACzC,CAAC,CAAC;EACrB,QAAQ;GACN;EACF;CACF,CAAC,CACH;CAEA,KAAK,MAAM,CAAC,OAAO,SAAS,YAAY,QAAQ,GAAG;EACjD,MAAM,UAAU,SAAS;EACzB,IAAI,SAAS;GACX,iBAAiB,QAAQ;GACzB;EACF;EACA,oBAAoB,KAAK,IAAI;CAC/B;CAEA,OAAO;EAAE;EAAkB;CAAoB;AACjD;;;;AAKA,SAAgB,gBAAgB,KAAa,SAAsC;CACjF,MAAM,KAAK,IAAI,UAAU,KAAK,OAAO;CAErC,MAAM,QAAQ,iBAAiB;EAC7B,IAAI,GAAG,eAAe,UAAU,YAC9B,GAAG,MAAM,MAAM,oBAAoB;CAEvC,GAAG,kBAAkB;CAIrB,GAAG,KAAK,cAAc,aAAa,KAAK,CAAC;CACzC,GAAG,KAAK,eAAe,aAAa,KAAK,CAAC;CAE1C,OAAO;AACT;;AAgBA,SAAgB,uBACd,OACA,OACA,UAAmC,CAAC,GAC0F;CAC9H,MAAM,UAA6B,CAAC;CACpC,IAAI,OAAO;CAEX,MAAM,YAAY,IAAI,gBAAiC,KAAA,GAAW,KAAA,GAAW,EAAE,eAAe,SAAS,CAAC;CACxG,MAAM,SAAS,UAAU,SAAS,UAAU;CAC5C,IAAI,SAAS,QAAQ,QAAQ;CAC7B,IAAI,SAAS;CACb,IAAI;;;;;CAMJ,SAAS,GAA2C,MAAa,SAAuD;EACtH,QAAQ,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC;CACxC;CAEA,SAAS,UAA4C,MAAY,MAA2C;EAC1G,MAAM,QAAQ;GAAE;GAAO;GAAM;GAAM,SAAS;GAAY,WAAW,KAAK,IAAI;EAAE;EAE9E,SAAS,OACN,WAAW,OAAO,MAAM,KAAK,CAAC,CAAC,CAC/B,OAAO,UAAU;GAChB,cAAc;EAChB,CAAC;CACL;CAEA,GAAG,sBAAsB,QAAQ;EAC/B,UAAU,qBAAqB,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC;CACxE,CAAC;CAED,GAAG,oBAAoB,QAAQ;EAC7B,UAAU,mBAAmB,CAAC;GAAE,QAAQ,EAAE,MAAM,IAAI,OAAO,KAAK;GAAG,UAAU,IAAI;GAAU,SAAS,IAAI;EAAQ,CAAC,CAAC;CACpH,CAAC;CAED,GAAG,qBAAqB,EAAE,QAAQ,cAAc;EAC9C,OAAO,OAAO;EACd,UAAU,oBAAoB,CAAC;GAAE,QAAQ,EAAE,MAAM,OAAO,KAAK;GAAG,SAAS,EAAE,MAAM,QAAQ,KAAK;EAAE,CAAC,CAAC;CACpG,CAAC;CAED,GAAG,mBAAmB,EAAE,OAAO,QAAQ,gBAAgB;EACrD,UAAU,kBAAkB,CAAC;GAAE,OAAO,MAAM,KAAK,UAAU;IAAE,MAAM,oBAAoB,OAAO,MAAM,KAAK,IAAI;IAAG,MAAM,KAAK;GAAK,EAAE;GAAG;EAAU,CAAC,CAAC;CACnJ,CAAC;CAED,GAAG,gCAAgC,EAAE,YAAY;EAC/C,UAAU,+BAA+B,CAAC,EAAE,OAAO,MAAM,OAAO,CAAC,CAAC;CACpE,CAAC;CAED,GAAG,iCAAiC,EAAE,YAAY;EAChD,UAAU,gCAAgC,CACxC,EACE,OAAO,MAAM,KAAK,EAAE,MAAM,WAAW,OAAO,kBAAkB;GAC5D,MAAM,oBAAoB,MAAM,KAAK,IAAI;GACzC;GACA;GACA;EACF,EAAE,EACJ,CACF,CAAC;CACH,CAAC;CAED,GAAG,8BAA8B,EAAE,YAAY;EAC7C,UAAU,6BAA6B,CAAC,EAAE,OAAO,MAAM,OAAO,CAAC,CAAC;CAClE,CAAC;CAGD,KAAK,MAAM,QAAQ;EAAC;EAAa;EAAgB;CAAW,GAC1D,GAAG,OAAO,EAAE,SAAS,WAAW;EAC9B,UAAU,MAAM,CAAC;GAAE;GAAS;EAAK,CAAC,CAAC;CACrC,CAAC;CAGH,GAAG,0BAA0B,EAAE,aAAa;EAC1C,UAAU,yBAAyB,CACjC;GACE,MAAM,OAAO;GACb,SAAS,OAAO,QAAQ;EAC1B,CACF,CAAC;CACH,CAAC;CAED,GAAG,uBAAuB,OAAO,EAAE,QAAQ,SAAS,cAAc,CAAC,GAAG,QAAQ,SAAS,mBAAmB;EACxG,MAAM,EAAE,kBAAkB,wBAAwB,MAAM,wBAAwB,OAAO,QAAQ,KAAK,EAAE,WAAW,IAAI,CAAC;EACtH,MAAM,OAAO,MAAM,QAAQ,SAAS;EACpC,MAAM,QAAQ,IAAI,IAAI,KAAK,KAAK,QAAQ,oBAAoB,OAAO,MAAM,GAAG,CAAC,CAAC;EAC9E,QAAQ,kBAAkB;GAAE,QAAQ;IAAE;IAAS,MAAM,OAAO;IAAM;GAAM;GAAG;GAAkB;EAAoB,CAAC;EAElH,UAAU,uBAAuB,CAAC,CAAC;EAEnC,IAAI,CAAC,SACH;EAKF,UAAU,2BAA2B,CACnC;GAAE,UAHa,KAAK,MAAM,aAAa,OAAO,CAGrC;GAAG,WAAW,gBAAgB;GAAG,eAAe,YAAY,cAAc,WAAW,CAAC,CAAC;GAAQ,QAAQ,UAAU;EAAU,CACtI,CAAC;CACH,CAAC;CAED,GAAG,eAAe,EAAE,YAAY;EAC9B,UAAU,cAAc,CACtB;GACE,SAAS,MAAM;GACf,OAAO,MAAM;EACf,CACF,CAAC;CACH,CAAC;CAED,GAAG,oBAAoB,EAAE,iBAAiB;EACxC,MAAM,QAAQ,WAAW,aAAa,WAAW,QAAQ,KAAA;EACzD,UAAU,mBAAmB,CAC3B;GACE,MAAM,WAAW;GACjB,SAAS,WAAW;GACpB,UAAU,WAAW;GACrB,UAAU,cAAc,aAAa,WAAW,WAAW,KAAA;GAC3D,MAAM,UAAU,aAAa,WAAW,OAAO,KAAA;GAC/C,QAAQ,YAAY,aAAa,WAAW,SAAS,KAAA;GACrD,OAAO,OAAO;EAChB,CACF,CAAC;CACH,CAAC;CAGD,KAAK,MAAM,QAAQ;EACjB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACE,GAAG,YAAY;EACb,UAAU,MAAM,CAAC,CAAC;CACpB,CAAC;CAGH,GAAG,oBAAoB,EAAE,IAAI,SAAS,WAAW;EAC/C,UAAU,mBAAmB,CAAC;GAAE;GAAI;GAAS,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,KAAA;EAAU,CAAC,CAAC;CACpF,CAAC;CAED,GAAG,mBAAmB,EAAE,IAAI,WAAW;EACrC,UAAU,kBAAkB,CAAC;GAAE;GAAI;EAAK,CAAC,CAAC;CAC5C,CAAC;CAED,GAAG,kBAAkB,EAAE,IAAI,SAAS,MAAM,SAAS,YAAY;EAC7D,UAAU,iBAAiB,CACzB;GACE;GACA;GACA,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,KAAA;GACzB;GACA,OAAO,QAAQ;IAAE,SAAS,MAAM;IAAS,OAAO,MAAM;GAAM,IAAI,KAAA;EAClE,CACF,CAAC;CACH,CAAC;;;;CAKD,SAAS,SAAe;EACtB,KAAK,MAAM,UAAU,SAAS,OAAO;EACrC,QAAQ,SAAS;CACnB;CAEA,eAAe,QAAuB;EACpC,IAAI,QACF;EAEF,SAAS;EACT,OAAO;EACP,MAAM;EAEN,IAAI,aACF;EAEF,MAAM,OAAO,MAAM,CAAC,CAAC,YAAY,KAAA,CAAS;CAC5C;CAEA,SAAS,KAAK,OAAuB;EACnC,OAAO;EACP,IAAI,QACF;EAEF,SAAS;EACT,OAAY,MAAM,KAAK,CAAC,CAAC,YAAY,KAAA,CAAS;CAChD;CAEA,OAAO;EAAE,QAAQ,UAAU;EAAU;EAAO,eAAe,KAAK;EAAG;CAAK;AAC1E;;;;;;;;ACjRA,IAAM,iBAAN,cAA6B,UAA8B;CAC5B;CAA7B,YAAY,KAAgC;EAC1C,MAAM;EADqB,KAAA,MAAA;CAE7B;CAEA,UAAU;EACR,OAAO,KAAK,IAAI,QAAQ;CAC1B;CACA,gBAAgB,OAAsB;EACpC,OAAO,KAAK,IAAI,gBAAgB,KAAK;CACvC;CACA,WAAW,OAAwB;EACjC,OAAO,KAAK,IAAI,WAAW,KAAK;CAClC;CACA,gBAAgB,OAA6B;EAC3C,OAAO,KAAK,IAAI,gBAAgB,KAAK;CACvC;CACA,UAAU,OAAuB;EAC/B,OAAO,KAAK,IAAI,UAAU,KAAK;CACjC;AACF;;;;;;;;;;;AAYA,MAAa,sBAAoC,OAAO,EAAE,KAAK,OAAO,YAAoC;CACxG,MAAM,EAAE,UAAU,UAAU,SAAS,IAAI,IAAI,GAAG;CAGhD,IAAI,aAAa,UAAU,EAAE,aAAa,UADvB,aAAa,eAAe,aAAa,eAAe,aAAa,WAEtF,MAAM,IAAI,MAAM,qCAAqC,MAAM;CAG7D,MAAM,SAAS,gBAAgB,KAAK,EAAE,SAAS,EAAE,eAAe,UAAU,QAAQ,EAAE,CAAC;CACrF,MAAM,SAAS,IAAI,SAAe,YAAY,OAAO,KAAK,SAAS,OAAO,CAAC;CAE3E,MAAM,SAAS,uBAAkC,QAA2C,IAAI,eAAe,KAAK,CAAC;CACrH,OAAO,kBAAkB,OAAO,MAAM,CAAC;CAEvC,OAAO;EACL;EACA;EACA,aAAa,OAAO,OAAO,QAAQ,CAAC;CACtC;AACF;;;;;;AC1BA,MAAM,0BAA0B;AAEhC,IAAM,sBAAN,cAAkC,UAAmC;CAEhD;CACA;CACA;CAEA;CALnB,YACE,kBACA,kBACA,kBAEA,gBACA;EACA,MAAM;EANW,KAAA,mBAAA;EACA,KAAA,mBAAA;EACA,KAAA,mBAAA;EAEA,KAAA,iBAAA;CAGnB;CAEA,MAAM,SAAS;EACb,OAAO,KAAK;CACd;CACA,SAAS;EACP,OAAO,KAAK;CACd;CACA,SAAS;EACP,OAAO,KAAK,iBAAiB;CAC/B;CACA,CAAC,OAAO,WAAW;EACjB,KAAK,eAAe;CACtB;AACF;;;;;;AAyFA,SAAS,oBAAoB,SAAgD;CAC3E,MAAM,OAAO,QAAQ,QAAQC,UAAQ,IAAI;CAEzC,OAAO;EACL,GAAG;EACH,WAAW,QAAQ,aAAa,cAAc;EAC9C;EAGA,YAAY,KAAK,QAAQ,MAAM,QAAQ,UAAU;EACjD,aAAa;GAAE,YAAY;GAAO,iBAAiB;GAAO,YAAY;GAAO,WAAW;GAAO,WAAW;GAAO,GAAG,QAAQ;EAAY;EACxI,eAAe,QAAQ,iBAAiB,cAAc;EAItD,mBAAmB,KAAK,IAAI,QAAQ,qBAAqB,cAAc,qBAAqB,cAAc,sBAAsB;CAClI;AACF;;;;;;;;AASA,SAAS,UAAU,SAAgC;CACjD,MAAM,EAAE,QAAQ,eAAe,iBAAiB,UAAA,eAAa;CAE7D,IAAI,QAAQ,SACV;CAKF,IAAIC,eAAa,KAAA,KAAaA,aAAWC,SAAY,QACnD,QAAQ,MAAM,UAAU,OAAO,0BAA0B,cAAc,sBAAsB,CAAC;CAGhG,MAAM,eAAe,aAAa,KAAK;CACvC,MAAM,QAAQ,iBAAiB;EAG7B,QAAQ,oBAAoB,SAAS,MAAM;EAE3C,IAAI,QAAQ,SACV;EAKF,IAAI,cAAc,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,UAAmB;GAC3D,IAAID,eAAa,KAAA,KAAaA,aAAWC,SAAY,QACnD,QAAQ,MAAM,UAAU,OAAO,4CAA4C,gBAAgB,KAAK,GAAG,CAAC;GAMtG,IAAI,iBAAiB,wBAAwB;IAC3C,kBAAkB,KAAK;IAEvB;GACF;GAEA,UAAU,OAAO;EACnB,CAAC;CACH,GAAG,aAAa;CAEhB,QAAQ,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1D;;;;;AAMA,IAAa,gBAAb,MAA+C;CAC7C;CAGA,SAAkB,IAAI,SAAoB;;;;;CAK1C,WAAuC,CAAC;;;;;CAMxC;CACA;CAEA;CAIA,YAAY;CAIZ,gBAAgB;CAChB;CAEA;CACA;CACA,UAAmB,wBAAwB;;;;;CAK3C,cAAuB,QAAQ,cAAoB;CAEnD,YAAY,SAA+B;EACzC,KAAK,WAAW,oBAAoB,OAAO;EAE3C,KAAU,YAAY,QAAQ,YAAY,CAAC,CAAC;CAC9C;;;;CAKA,IAAI,aAAsB;EACxB,OAAO,KAAK,UAAU,cAAc;CACtC;;;;;CAMA,IAAI,eAAgC;EAClC,KAAK,WAAW,sBAAsB;GACpC,SAAS,KAAK,aAAa,cAAc,IAAI,aAAa,EAAE,MAAM,KAAK,SAAS,KAAK,CAAC;GACtF,UAAU,KAAK,QAAQ;GACvB,OAAO,KAAK,QAAQ;EACtB,CAAC;EACD,OAAO,KAAK;CACd;CAEA,IAAI,YAAqB;EACvB,OAAO,CAAC,KAAK,cAAc,KAAK,SAAS,YAAY;CACvD;CAEA,IAAI,iBAA0B;EAC5B,OAAO,CAAC,KAAK,cAAc,KAAK,SAAS,YAAY;CACvD;;;;;CAMA,IAAI,eAAwB;EAC1B,OAAO,KAAK,cAAc,KAAK,SAAS,YAAY;CACtD;;;;CAKA,IAAI,WAAoB;EACtB,OAAO,KAAK,cAAc,KAAK,SAAS,YAAY;CACtD;CAEA,MAAM,QAAuB;EAC3B,MAAM,EAAE,OAAO,WAAW,QAAQ,mBAAmB,kBAAkB,KAAK;EAE5E,MAAM,gBAAgB,KAAK,MAAM;EAEjC,IAAI;GAGF,MAAM,KAAK,OAAO,SAAS,qBAAqB,EAAE,KAAK,UAAU,CAAC;GAElE,MAAM,UAAU,MAAM,mBAAmB;IAAE;IAAO;GAAU,CAAC;GAE7D,KAAK,WAAW;GAChB,KAAK,iBAAiB,QAAQ;GAE9B,MAAM,MAAM,OAAO,KAAK,SAAS,aAAa,oBAAA,CAAqB;IAAE,KAAK,QAAQ;IAAK;IAAO,OAAO;GAAK,CAAC;GAC3G,KAAK,OAAO;GACZ,IAAS,OAAO,KAAK,KAAK,QAAQ;GAElC,QAAQ,iBAAiB,SAAS,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;GAC/D,KAAK,SAAS,WAAW,QAAQ,oBAAoB,SAAS,KAAK,QAAQ,CAAC;GAE5E,KAAK,mBAAmB,iBAAiB;GACzC,MAAM,KAAK,OAAO,SAAS,oBAAoB;IAC7C,KAAK;IACL,UAAU;KAAE,QAAQ,KAAK;KAAgB,MAAMC;KAAa,OAAO,KAAK,SAAS;IAAQ;IACzF,WAAW,QAAQ;IACnB,kBAAkB,QAAQ;GAC5B,CAAC;GAED,MAAM,KAAK,YAAY;GACvB,MAAM,KAAK,OAAO,SAAS,gBAAgB,CAAC,CAAC;EAC/C,SAAS,OAAO;GAGd,KAAK,YAAY;GACjB,KAAK,QAAQ;GACb,MAAM,KAAK,OAAO,SAAS,gBAAgB,EAAE,OAAO,QAAQ,KAAK,EAAE,CAAC;GAEpE,IAAI,iBAAiB,wBACnB,MAAM;GAGR,UAAU,KAAK,QAAQ;EACzB;CACF;CAEA,MAAM,SAAuC;EAC3C,OAAO,KAAK,OAAO,SAAS,eAAe,EAAE,QAAQ,CAAC;CACxD;;;;;CAMA,MAAM,QAAQ,QAAgB,SAAiC;EAC7D,MAAM,KAAK,MAAM,MAAM;EACvB,MAAM,IAAI,MAAM,OAAO;CACzB;CAEA,mBAAmB,UAAwB;EACzC,MAAM,MAAM,KAAK;EACjB,IAAI,CAAC,KACH;EAEF,KAAK,kBAAkB,WAAW,YAAY;GAC5C,IAAI;IACF,MAAM,KAAK,MAAM,GAAG;GACtB,QAAQ;IACN,IAAI,KAAK,SAAS,KAChB,IAAI,MAAM;IAEZ;GACF;GAEA,IAAI,KAAK,SAAS,KAChB,KAAK,mBAAmB,QAAQ;EAEpC,GAAG,QAAQ;CACb;;;;CAKA,MAAM,KAAmC;EACvC,MAAM,EAAE,SAAS,UAAU,QAAQ,cAAc,QAAQ,cAAqB;EAC9E,MAAM,QAAQ,iBAAiB,0BAAU,IAAI,MAAM,0BAA0B,CAAC,GAAG,cAAc,kBAAkB;EAEjH,OAAO,QAAQ,KAAK,CAAC,IAAI,OAAO,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,cAAc,aAAa,KAAK,CAAC;CACtF;;;;;;;CAQA,MAAM,oBAAoB,QAAsD;EAC9E,IAAI,CAAC,KAAK,gBACR;EAGF,IAAI;GACF,OAAO,WAAW,UAAW,MAAM,KAAK,KAAK,SAAS,UAAU,CAAE;EACpE,SAAS,OAAO;GACd,MAAM,KAAK,MAAM,kBAAkB,KAAK,SAAS,WAAW,IAAI,gBAAgB,KAAK,GAAG;GAExF;EACF;CACF;CAEA,MAAM,UAA0C;EAC9C,MAAM,EAAE,YAAY,MAAM,SAAA,WAAS,YAAY,gBAAgB,KAAK;EACpE,MAAM,CAAC,QAAQ,QAAQ,MAAM,QAAQ,IAAI,CAAC,WAAW,GAAG,KAAK,oBAAoB,CAAC,CAAC;EAEnF,MAAM,UAAiC;GACrC,UAAU;IAAE,MAAMA;IAAa,OAAOC;GAAQ;GAC9C;GACA,QAAQ;IACN,MAAM;IACN;IACA,SAAS,OAAO,QAAQ,KAAK,YAAY;KACvC,MAAM,cAAc,OAAO,IAAI;KAC/B,SAAS,OAAO,WAAW,CAAC;IAC9B,EAAE;GACJ;GACA,aAAa;IACX,GAAG;IACH,YAAY,KAAK;IACjB,YAAY,KAAK;IACjB,iBAAiB,KAAK;IACtB,WAAW,KAAK;GAClB;EACF;EACA,KAAK,YAAY,QAAQ;EACzB,OAAO;CACT;CAEA,iBAAuB,KAAK,KAAK,KAAK,EAAE,OAAO,MAAM,CAAC;CAEtD,iBAAuB,KAAK,KAAK,KAAK,EAAE,OAAO,KAAK,CAAC;;;;;;;CAQrD,UAAgB;EACd,aAAa,KAAK,eAAe;EACjC,KAAK,kBAAkB,KAAA;EACvB,KAAK,MAAM,MAAM;EACjB,KAAK,OAAO,KAAA;EACZ,KAAK,YAAY,uBAAO,IAAI,MAAM,8CAA8C,CAAC;EAEjF,KAAK,MAAM,UAAU,KAAK,UAAU,OAAO;EAC3C,KAAK,SAAS,SAAS;CACzB;;;;;CAMA,MAAM,KAAK,EAAE,SAA4C;EACvD,MAAM,EAAE,WAAW,OAAO,aAAa,KAAK;EAE5C,IAAI,KAAK,WACP;EAEF,KAAK,YAAY;EAEjB,KAAK,QAAQ;EAEb,MAAM,KAAK,OAAO,SAAS,uBAAuB,EAAE,QAAQ,QAAQ,sBAAsB,WAAW,CAAC;EAGtG,IAAI,KAAK,UAEP,MAAM,WAAW;GAAE,WAAW,KAAK,SAAS;GAAW;GAAW;GAAO,MAAM,KAAK,SAAS;GAAM;EAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EAG/H,IAAI,OACF,UAAU,KAAK,QAAQ;CAE3B;CAEA,gBAAgB,MAAoC;EAClD,MAAM,mBAAmB,uBAAuB,KAAK,QAAQ,KAAK,OAAO,EACvE,kBAAkB,WAAW;GAC3B,KAAK,kBAAkB;EACzB,EACF,CAAC;EACD,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,SAAS,KAAK,eAAe,MAAM,UAAU,CAAC,CACjD,KAAK,OAAO,UAAU;GACrB,MAAM,iBAAiB,MAAM;GAC7B,OAAO;EACT,CAAC,CAAC,CACD,OAAO,UAAU;GAChB,iBAAiB,KAAK,KAAK;GAC3B,MAAM;EACR,CAAC;EAEH,OAAY,YAAY,CAAC,CAAC;EAE1B,OAAO,IAAI,oBACT,iBAAiB,QACjB,QACA,YAAY;GACV,WAAW,sBAAM,IAAI,MAAM,qBAAqB,CAAC;EACnD,SACM;GACJ,WAAW,sBAAM,IAAI,MAAM,qBAAqB,CAAC;GACjD,iBAAiB,QAAQ;EAC3B,CACF;CACF;CAEA,MAAM,eAAe,MAAqB,YAAsD;EAE9F,IAAI,KAAK,eACP,OAAO,KAAK,QAAQ,yDAAyD,mEAAmE;EAElJ,KAAK,gBAAgB;EAErB,MAAM,UAAU;EAChB,MAAM,EAAE,MAAM,YAAY,aAAa,WAAW,KAAK;EAEvD,IAAI;GACF,MAAM,KAAK,OAAO,SAAS,wBAAwB,EAAE,QAAQ,CAAC;GAC9D,MAAM,SAAS,MAAM,WAAW;GAChC,MAAM,QAAQ,KAAK;GACnB,MAAM,UAAU,MAAM,aAAa,OAAO,SAAS,OAAO,OAAO;GACjE,MAAM,UAAU,MAAM,aAAa,OAAO,SAAS,OAAO,OAAO;GAIjE,MAAM,gBAAgB,KAAK,aAAc,OAAO,SAAS,KAAO,YAAY,cAAc,OAAO,SAAU,KAAA;GAE3G,IAAI,YAAY,cAAc,KAAK,YACjC,MAAM,KAAK,MAAM,oDAAoD;GAGvE,IAAI,OAAO,SAAS,CAAC,KAAK,cAAc;IAGtC,MAAM,SAAS,QAAQ,SAAS,QAAQ,uDAAuD;IAC/F,MAAM,KAAK,MAAM,qCAAqC,OAAO,qBAAqB;GACpF;GAEA,MAAM,kBAAkB,WAAW,OAAO;GAK1C,KAAK,kBAAkB,KAAA;GACvB,MAAM,YAAY,KAAK,oBAAoB,MAAM,SAAS;IAAE;IAAM,YAAY,OAAO,OAAO;IAAM,UAAU;GAAwB,CAAC,IAAI,KAAA;GACzI,MAAM,OAAO,YACT,MAAM,KAAK,aAAa,KAAK;IAAE,OAAO,KAAK;IAAO,QAAQ;IAAQ,OAAO;IAAW,UAAU,KAAK,QAAQ;GAAc,CAAC,IAC1H,KAAA;GACJ,MAAM,SAAS,CAAC,kBAAkB,KAAK,QAAQ,MAAM,WAAW,MAAM,CAAC;GAEvE,IAAI;IACF,MAAM,SAAS;KACb,QAAQ;MACN,GAAG;MACH;MACA,OAAO,iBAAiB,OAAO;MAC/B,SAAS,KAAK,YAAY,UAAU,IAAI,cAAc;MACtD,QAAQ,YAAY,YAAY,EAAE,GAAG,OAAO,OAAO,IAAI;OAAE,GAAG,OAAO;OAAQ,QAAQ;OAAO,MAAM;OAAO,cAAc,CAAC;MAAE;MACxH,SAAS;MACT;KACF;KACA,OAAO,KAAK;KACZ,QAAQ,WAAW;IACrB,CAAC;GACH,SAAS,OAAO;IACd,MAAM,KAAK,aAAa,KAAK,KAAK,KAAK;IACvC,MAAM;GACR,UAAU;IACR,KAAK,MAAM,UAAU,QAAQ,OAAO;GACtC;GAEA,MAAM,KAAK,OAAO,SAAS,sBAAsB;IAC/C;IACA,MAAM,GAAG,gBAAgB,OAAO,SAAS,gBAAgB,WAAW,IAAI,KAAK,IAAI,IAAI,KAAK,YAAY,oBAAoB,cAAc,kBAAkB,KAAA,IAAY,yBAAyB;GACjM,CAAC;GAID,MAAM,aAAa,KAAK;GACxB,MAAM,SAAS,aACX,MAAM,KAAK,aAAa,KAAK;IAAE,OAAO,KAAK;IAAO,QAAQ;IAAU,OAAO,WAAW;IAAQ,UAAU,KAAK,QAAQ;GAAM,CAAC,IAC5H,KAAA;GACJ,IAAI,cAAc,QAAQ;IACxB,IAAI,CAAC,OAAO,MAAM,UAAU,OAAO,KAAK,OAAO,MAAM,CAAC,CAAC,QACrD,MAAM,KAAK,MAAM,0EAA0E;IAE7F,MAAM,EAAE,kBAAkB,wBAAwB;IAClD,MAAM,KAAK,aAAa,IAAI;KAAE,OAAO,KAAK;KAAO;KAAQ;KAAM;KAAkB;IAAoB,CAAC;GACxG;GACA,MAAM,QAAQ,CAAC,GAAI,YAAY,OAAO,SAAS,CAAC,CAAE;GAClD,OAAO;IACL,QAAQ;IACR;IACA,WAAW,MAAM;IACjB,QAAQ,QAAQ,UAAU,CAAC;IAC3B,MAAM,OAAO,EAAE,QAAQ,KAAK,OAAO,IAAI,KAAA;GACzC;EACF,UAAU;GACR,KAAK,gBAAgB;EACvB;CACF;CAEA,MAAM,WAAW,MAA4C;EAC3D,MAAM,UAAU;EAChB,MAAM,KAAK,OAAO,SAAS,wBAAwB,EAAE,QAAQ,CAAC;EAC9D,MAAM,EAAE,YAAY,eAAe,KAAK;EAIxC,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,GAAG;GAC9B,MAAM,KAAK,MAAM,4CAA4C;GAE7D,OAAO;IAAE,UAAU,CAAC;IAAG,SAAS;GAAM;EACxC;EAEA,MAAM,QAAQ,KAAK;EACnB,MAAM,UAAU,YAAgC;GAAE,UAAU,MAAM,KAAK,UAAU;IAAE;IAAM,SAAS;IAAO;GAAO,EAAE;GAAG,SAAS;EAAM;EAEpI,IAAI,CAAC,KAAK,gBAAgB;GACxB,MAAM,KAAK,MAAM,sDAAsD;GAEvE,OAAO,OAAO,6DAA6D;EAC7E;EAIA,IAAI,KAAK,eACP,OAAO,OAAO,6BAA6B;EAG7C,IAAI;GAKF,MAAM,EAAE,QAAQ,SAAS,UAAU,YAAY,iBAAiB,MAD1C,KAAK,UAAU,GACoC,KAAK;GAE9E,IAAI,SAGF,MAAM,UAAU,YAAY,SAAS,OAAO;GAG9C,MAAM,UAAU,SAAS,QAAQ,YAAY,QAAQ,OAAO,CAAC,CAAC;GAC9D,MAAM,KAAK,OAAO,SAAS,sBAAsB;IAAE;IAAS,MAAM,WAAW,QAAQ,GAAG,SAAS,OAAO,YAAY;GAAa,CAAC;GAClI,OAAO;IAAE;IAAU;IAAS,MAAM,UAAU,MAAM,KAAK,oBAAoB,OAAO,IAAI,KAAA;GAAU;EAClG,SAAS,OAAO;GAGd,MAAM,KAAK,OAAO,SAAS,gBAAgB,EAAE,OAAO,QAAQ,KAAK,EAAE,CAAC;GAEpE,OAAO,OAAO,gBAAgB,KAAK,CAAC;EACtC;CACF;CAEA,MAAM,gBAAgB,MAA4D;EAChF,MAAM,UAAU;EAChB,MAAM,KAAK,OAAO,SAAS,wBAAwB,EAAE,QAAQ,CAAC;EAE9D,IAAI,KAAK,YACP,OAAO,KAAK,QAAQ,4EAA4E,wDAAwD;EAG1J,MAAM,EAAE,MAAM,SAAS,qBAAqB,eAAe;EAE3D,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,YACxB,OAAO,KAAK,QAAQ,6DAA6D,yCAAyC;EAG5H,MAAM,aAAa,MAAM,KAAK,aAAa,OAAO;EAElD,IAAI,CAAC,YACH,OAAO,KAAK,QAAQ,iDAAiD,4DAA4D;EAGnI,MAAM,UAAU,IAAI,IAAI,uBAAuB,CAAC,CAAC;EACjD,MAAM,UAAU,WAAW,oBAAoB,QAAQ,eAAe,CAAC,QAAQ,IAAI,UAAU,CAAC;EAE9F,IAAI,QAAQ,QACV,OAAO,KAAK,QAAQ,2CAA2C,QAAQ,KAAK,IAAI,KAAK,yBAAyB,QAAQ,KAAK,IAAI,GAAG;EAGpI,IAAI;GACF,MAAM,QAAQ,MAAM,KAAK,aAAa,KAAK;IAAE;IAAY,QAAQ;IAAU,OAAO,WAAW,OAAO;GAAM,CAAC;GAE3G,MAAM,EAAE,OAAO,cAAc,MAAM,sBAAsB,OAAO;IAAE;IAAM;IAAS,kBAAkB,WAAW;GAAiB,CAAC;GAMhI,MAAM,EAAE,OAAO,cAAc,KAAK;GAClC,MAAM,YAAY,IAAI,IAAI,YAAY,SAAS;GAC/C,IAAI,UAAU,WAAW,IAAI,IAAI,SAAS,CAAC,CAAC,QAC1C,MAAM,IAAI,MAAM,qDAAqD;GAEvE,MAAM,WAAW,MAAM,MAAM,WAAW;IACtC,QAAQ;IACR,SAAS,EAAE,eAAe,UAAU,QAAQ;IAC5C,UAAU;GACZ,CAAC;GACD,MAAM,aAAa,SAAS,QAAQ,IAAI,UAAU;GAClD,IAAI,SAAS,WAAW,OAAO,CAAC,YAC9B,MAAM,IAAI,MAAM,gDAAgD,SAAS,OAAO,EAAE;GAEpF,MAAM,UAAU,IAAI,IAAI,UAAU;GAClC,IAAI,QAAQ,aAAa,YAAY,QAAQ,aAAa,eAAe,QAAQ,aAAa,aAC5F,MAAM,IAAI,MAAM,+BAA+B,QAAQ,QAAQ;GAEjE,MAAM,WAAW,MAAM,MAAM,SAAS;IAAE,QAAQ;IAAO,MAAM,IAAI,WAAW,KAAK;IAAG,UAAU;GAAQ,CAAC;GACvG,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,sCAAsC,SAAS,QAAQ;GAGzE,MAAM,KAAK,OAAO,SAAS,sBAAsB;IAC/C;IACA,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAC,OAAO,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,IAAI,KAAK;GAC1F,CAAC;GACD,OAAO;IAAE;IAAW,kBAAkB,WAAW;GAAiB;EACpE,SAAS,OAAO;GACd,MAAM,KAAK,OAAO,SAAS,gBAAgB,EAAE,OAAO,QAAQ,KAAK,EAAE,CAAC;GACpE,MAAM;EACR;CACF;;;;;CAMA,IAAI,oBAA6B;EAC/B,OAAO,CAAC,KAAK,cAAc,KAAK;CAClC;CAEA,MAAM,UAAU,MAAkE;EAChF,MAAM,UAAU;EAChB,MAAM,KAAK,OAAO,SAAS,wBAAwB,EAAE,QAAQ,CAAC;EAC9D,MAAM,EAAE,WAAW,KAAK;EAExB,IAAI,CAAC,KAAK,UAAU;GAClB,MAAM,KAAK,MAAM,wDAAwD;GAGzE,MAAM,SAAS,QAAQ,SAAS,QAAQ,uDAAuD;GAC/F,MAAM,IAAI,MAAM,qEAAqE,OAAO,aAAa;EAC3G;EAGA,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,GAC3B,OAAO,KAAK,QAAQ,+CAA+C,8BAA8B;EAGnG,MAAM,EAAE,UAAU;EAElB,IAAI,MAAM,SAAA,IACR,OAAO,KAAK,QACV,4BAA4B,MAAM,OAAO,+CACzC,2CACF;EAGF,IAAI,OAAO,KAAK,UAAU,YAAY,CAAC,KAAK,OAC1C,OAAO,KAAK,QAAQ,2CAA2C,qCAAqC;EAGtG,MAAM,aAAa,MAAM,KAAK,aAAa,IAAI,KAAK,KAAK;EAEzD,IAAI,CAAC,YACH,OAAO,KAAK,QAAQ,sBAAsB,KAAK,MAAM,6BAA6B,uBAAuB;EAG3G,MAAM,SAAS,KAAK,WAAW,SAAS,SAAS;EAEjD,IAAI,CAAC,WAAW,SACd,OAAO,KAAK,QAAQ,gEAAgE,+DAA+D;EAIrJ,MAAM,QAAQ,MAAM,KAAK,aAAa,KAAK;GAAE;GAAY;GAAQ;EAAM,CAAC;EAExE,MAAM,KAAK,OAAO,SAAS,sBAAsB;GAC/C;GACA,MAAM,QAAQ,OAAO,KAAK,KAAK,CAAC,CAAC,OAAO,GAAG,MAAM,OAAO,iBAAiB,MAAM,WAAW,IAAI,KAAK;EACrG,CAAC;EACD,OAAO,EAAE,MAAM;CACjB;AACF;;;;;;;;;;;;;;;ACjwBA,SAAgB,aAAa,EAAE,SAAS,gBAAgB,GAAG,WAAkC;CAC3F,IAAI,SACF,WAAW,OAAO;CAGpB,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,WAAW,QAAQ,YAAY,cAAc;CACnD,SAAS,mBAAmB,OAA+B;EAGzD,IAAI,WAAW,OAAO,SACpB;EAMF,WAAW,MAAM;EACjB,iBAAiB,KAAK;CACxB;CAEA,OAAO;EACL,MAAM,UAAU;GACd,MAAM,cAAc;IAAE,OAAO,QAAQ;IAAO,WAAW,QAAQ,aAAa,cAAc;IAAW;GAAS,CAAC;GAM/G,MAAM,QAAQ,IACZ,MAAM,KAAK,EAAE,QAAQ,SAAS,SAAS,IAAI,cAAc;IAAE,GAAG;IAAS,QAAQ,WAAW;IAAQ,iBAAiB;GAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,CAClJ;EACF;EACA,aAAa;GACX,WAAW,MAAM;EACnB;CACF;AACF;;;;;;;AC/BA,SAAS,iBAAiB,cAA+C,QAAmE;CAE1I,IAAI,CAAC,QACH,OAAO;CAMT,MAAM,UAAU,IAAI,gBAAgB;CACpC,MAAM,WAAW,IAAI,SAAoB,YAAY;EACnD,IAAI,OAAO,SAAS;GAClB,QAAQ,KAAA,CAAS;GACjB;EACF;EACA,OAAO,iBAAiB,eAAe,QAAQ,KAAA,CAAS,GAAG;GAAE,MAAM;GAAM,QAAQ,QAAQ;EAAO,CAAC;CACnG,CAAC;CAED,OAAO,QAAQ,KAAK,CAAC,UAAU,YAAY,CAAC,CAAC,CAAC,cAAc,QAAQ,MAAM,CAAC;AAC7E;;;;;;;;;;;;;;;;;;AAmBA,eAAsB,cAAsD,EAC1E,aACA,eACA,iBACA,UAC8D;CAC9D,IAAI,UAAU;CAEd,OAAO,MAAM;EAGX,IAAI,QAAQ,SACV,OAAO;EAGT,MAAM,EAAE,SAAS,cAAc,SAAS,uBAAuB,QAAQ,cAAsC;EAC7G,MAAM,SAAS,aAAa;GAAE,GAAG,cAAc,OAAO;GAAG,OAAO,QAAQ;GAAO,gBAAgB;EAAmB,CAAC;EAEnH,IAAI;EAEJ,IAAI;GACF,MAAM,OAAO,QAAQ;GAErB,MAAM,QAAQ,MAAM,iBAAiB,cAAc,MAAM;GAEzD,IAAI,CAAC,OACH,OAAO;GAGT,YAAY;IAAE;IAAO,aAAa;IAAS,MAAM;GAAK;EACxD,SAAS,OAAO;GAGd,IAAI,EAAE,iBAAiB,yBACrB,MAAM;GAGR,YAAY;IAAE;IAAO,aAAa;IAAS,MAAM;GAAM;EACzD,UAAU;GACR,OAAO,WAAW;EACpB;EAEA,MAAM,OAAO,MAAM,gBAAgB,SAAS;EAE5C,IAAI,CAAC,MACH,OAAO;EAGT,UAAU;CACZ;AACF;;;;;;;AC7FA,MAAM,YAAY;;;;;;AAOlB,IAAa,uBAAb,cAA0C,MAAM;CAC9C,cAAc;EACZ,MAAM,sBAAsB;EAC5B,KAAK,OAAO;CACd;AACF;;;;;;AA8BA,eAAsB,aAAa,EACjC,YAAY,cAAc,WAC1B,MACA,UACA,WAAW,WACX,WACA,UAC+C;CAC/C,IAAI;EACF,OAAO,MAAM,OAAuB,GAAG,UAAU,wBAAwB;GACvE,QAAQ;GACR,MAAM;IACJ,WAAW;IACX;IACA;IACA,eAAe,MAAM,gBAAgB;IACrC,YAAY;GACd;GACA;EACF,CAAC;CACH,SAAS,OAAO;EACd,IAAI,QAAQ,SACV,MAAM,IAAI,qBAAqB;EAGjC,MAAM;CACR;AACF;AAyBA,SAAS,gBAAgB,UAA+D;CACtF,OAAO,CAAC,CAAC,YAAY,OAAO,aAAa,YAAY,WAAW,YAAY,OAAO,SAAS,UAAU;AACxG;;;;;;;;;;AAWA,eAAsB,oBAAoB,EAAE,YAAY,cAAc,WAAW,SAAS,UAA+C;CAIvI,MAAM,WAAW,KAAK,IAAI,KAAK,QAAQ,aAAa,IAAI,QAAQ,aAAa,OAAO;CACpF,IAAI,cAAc,QAAQ,WAAW,IAAI,QAAQ,WAAW,KAAK;CAEjE,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,IAAI,QAAQ,SACV,MAAM,IAAI,qBAAqB;EAGjC,IAAI;GACF,MAAMC,aAAM,YAAY,KAAA,GAAW,EAAE,OAAO,CAAC;EAC/C,QAAQ;GACN,MAAM,IAAI,qBAAqB;EACjC;EAEA,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,OAAiC,GAAG,UAAU,mBAAmB;IAChF,QAAQ;IACR,MAAM,EAAE,aAAa,QAAQ,YAAY;IAGzC,qBAAqB;IACrB;GACF,CAAC;EACH,SAAS,OAAO;GACd,IAAI,QAAQ,SACV,MAAM,IAAI,qBAAqB;GAMjC,QAAQ,KAAK,UAAU,UAAU,qEAAqE,gBAAgB,KAAK,GAAG,CAAC;GAC/H;EACF;EAEA,IAAI,gBAAgB,QAAQ,GAC1B,OAAO;EAGT,IAAI,CAAC,YAAY,OAAO,aAAa,YAAY,EAAE,WAAW,aAAa,OAAO,SAAS,UAAU,UACnG,MAAM,IAAI,MAAM,4DAA4D;EAG9E,IAAI,SAAS,UAAU,yBACrB;EAGF,IAAI,SAAS,UAAU,aAAa;GAClC,cAAc;GACd;EACF;EAEA,IAAI,SAAS,UAAU,iBACrB,MAAM,IAAI,MAAM,SAAS,qBAAqB,mCAAmC;EAGnF,IAAI,SAAS,UAAU,mBAAmB,SAAS,UAAU,iBAC3D,MAAM,IAAI,MAAM,SAAS,qBAAqB,sCAAsC;EAGtF,MAAM,IAAI,MAAM,SAAS,qBAAqB,mBAAmB,SAAS,MAAM,EAAE;CACpF;CAEA,MAAM,IAAI,MAAM,sCAAsC;AACxD"}