theokit 0.13.0 → 0.15.0
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/{actions-virtual-module-3CDQTWOC.js → actions-virtual-module-G4BANOLW.js} +5 -3
- package/dist/{actions-virtual-module-3CDQTWOC.js.map → actions-virtual-module-G4BANOLW.js.map} +1 -1
- package/dist/{actions-virtual-module-EIPXX4ZB.js → actions-virtual-module-RM7JRY5N.js} +7 -7
- package/dist/agent-MN7XGJR3.js +209 -0
- package/dist/agent-MN7XGJR3.js.map +1 -0
- package/dist/{app-typed-client-7PBFWZUE.js → app-typed-client-5VQE6DNF.js} +7 -7
- package/dist/{app-typed-client-CSOK7NPC.js → app-typed-client-Z6BHD4MF.js} +5 -3
- package/dist/{app-typed-client-CSOK7NPC.js.map → app-typed-client-Z6BHD4MF.js.map} +1 -1
- package/dist/boot/index.js +3 -3
- package/dist/{build-HXND27XG.js → build-QDAFSKKW.js} +11 -7
- package/dist/{build-HXND27XG.js.map → build-QDAFSKKW.js.map} +1 -1
- package/dist/chunk-34YQOXGM.js +37 -0
- package/dist/chunk-34YQOXGM.js.map +1 -0
- package/dist/{chunk-7YZHAQU7.js → chunk-567NA7Y6.js} +8 -99
- package/dist/chunk-567NA7Y6.js.map +1 -0
- package/dist/{chunk-3S3BNW5K.js → chunk-5UUOGAJH.js} +2 -2
- package/dist/{chunk-BQDGES7C.js → chunk-GBXLKYIA.js} +19 -43
- package/dist/chunk-GBXLKYIA.js.map +1 -0
- package/dist/chunk-GDN3PXFH.js +101 -0
- package/dist/chunk-GDN3PXFH.js.map +1 -0
- package/dist/{chunk-TGTNRUH3.js → chunk-H243MR3F.js} +2 -2
- package/dist/{chunk-NHJMZCAS.js → chunk-JZHT3LW7.js} +1 -1
- package/dist/chunk-JZHT3LW7.js.map +1 -0
- package/dist/chunk-KGFNWIMS.js +94 -0
- package/dist/chunk-KGFNWIMS.js.map +1 -0
- package/dist/{chunk-EXP56GFQ.js → chunk-NBWB4S46.js} +38 -9
- package/dist/chunk-NBWB4S46.js.map +1 -0
- package/dist/{chunk-F4YUPDJ2.js → chunk-NXTF5PPW.js} +15 -41
- package/dist/chunk-NXTF5PPW.js.map +1 -0
- package/dist/{chunk-UNLA45FY.js → chunk-OTFIRP6S.js} +76 -46
- package/dist/chunk-OTFIRP6S.js.map +1 -0
- package/dist/chunk-P37RZRFV.js +31 -0
- package/dist/chunk-P37RZRFV.js.map +1 -0
- package/dist/{chunk-RYTZYFSD.js → chunk-UOR6JTCI.js} +205 -33
- package/dist/chunk-UOR6JTCI.js.map +1 -0
- package/dist/{chunk-WFNLNIJX.js → chunk-Z5IGLBWE.js} +1 -1
- package/dist/{chunk-O62MW4MT.js → chunk-ZJDKAD3L.js} +55 -14
- package/dist/chunk-ZJDKAD3L.js.map +1 -0
- package/dist/cli/index.js +22 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +3 -152
- package/dist/client/index.js +13 -236
- package/dist/client/index.js.map +1 -1
- package/dist/{dev-OWW4XVIH.js → dev-TEE4T6ZB.js} +13 -8
- package/dist/{dev-OWW4XVIH.js.map → dev-TEE4T6ZB.js.map} +1 -1
- package/dist/{dev-emit-5MDSBP5D.js → dev-emit-VJ5CFMPY.js} +5 -3
- package/dist/{dev-emit-5MDSBP5D.js.map → dev-emit-VJ5CFMPY.js.map} +1 -1
- package/dist/index.js +12 -13
- package/dist/index.js.map +1 -1
- package/dist/{info-OUEUZOT7.js → info-7PE2PZJI.js} +6 -5
- package/dist/{info-OUEUZOT7.js.map → info-7PE2PZJI.js.map} +1 -1
- package/dist/{internal-api-4YTJDITC.js → internal-api-5BDI6N5U.js} +20 -20
- package/dist/{internal-api-EFKZWIYZ.js → internal-api-J27TYE2I.js} +7 -4
- package/dist/load-config-JKYO5RFK.js +14 -0
- package/dist/{openapi-FHY6HC6I.js → openapi-MXMLZCXC.js} +7 -4
- package/dist/{openapi-FHY6HC6I.js.map → openapi-MXMLZCXC.js.map} +1 -1
- package/dist/{registry-34LL7NF4.js → registry-XJUYD2OU.js} +2 -2
- package/dist/{routes-EW7TP7NJ.js → routes-NNBEZSGN.js} +5 -3
- package/dist/{routes-EW7TP7NJ.js.map → routes-NNBEZSGN.js.map} +1 -1
- package/dist/server/auth/index.js +1 -1
- package/dist/server/define/index.d.ts +3 -70
- package/dist/server/define/index.js +2 -4
- package/dist/server/http/index.js +2 -2
- package/dist/server/index.d.ts +1 -3
- package/dist/server/index.js +90 -105
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +4 -4
- package/dist/server/observability/index.js +1 -1
- package/dist/server/scan/index.js +1 -1
- package/dist/{start-KIQ5TTLR.js → start-7MQEEQH4.js} +68 -8
- package/dist/start-7MQEEQH4.js.map +1 -0
- package/dist/{static-55G3LX2I.js → static-7ARBVDJF.js} +4 -4
- package/dist/vite-plugin/index.js +12 -13
- package/dist/{vite-plugin-RK66K26Z.js → vite-plugin-GC6WCU4P.js} +11 -7
- package/package.json +2 -6
- package/dist/agent-events-DosDXkSV.d.ts +0 -94
- package/dist/chunk-2KZQPDYR.js +0 -226
- package/dist/chunk-2KZQPDYR.js.map +0 -1
- package/dist/chunk-7YZHAQU7.js.map +0 -1
- package/dist/chunk-BQDGES7C.js.map +0 -1
- package/dist/chunk-EXP56GFQ.js.map +0 -1
- package/dist/chunk-F4YUPDJ2.js.map +0 -1
- package/dist/chunk-NHJMZCAS.js.map +0 -1
- package/dist/chunk-O62MW4MT.js.map +0 -1
- package/dist/chunk-RYTZYFSD.js.map +0 -1
- package/dist/chunk-UNLA45FY.js.map +0 -1
- package/dist/chunk-ZSTZXR2D.js +0 -208
- package/dist/chunk-ZSTZXR2D.js.map +0 -1
- package/dist/server/agent/index.d.ts +0 -229
- package/dist/server/agent/index.js +0 -17
- package/dist/start-KIQ5TTLR.js.map +0 -1
- /package/dist/{actions-virtual-module-EIPXX4ZB.js.map → actions-virtual-module-RM7JRY5N.js.map} +0 -0
- /package/dist/{app-typed-client-7PBFWZUE.js.map → app-typed-client-5VQE6DNF.js.map} +0 -0
- /package/dist/{chunk-3S3BNW5K.js.map → chunk-5UUOGAJH.js.map} +0 -0
- /package/dist/{chunk-TGTNRUH3.js.map → chunk-H243MR3F.js.map} +0 -0
- /package/dist/{chunk-WFNLNIJX.js.map → chunk-Z5IGLBWE.js.map} +0 -0
- /package/dist/{internal-api-4YTJDITC.js.map → internal-api-5BDI6N5U.js.map} +0 -0
- /package/dist/{internal-api-EFKZWIYZ.js.map → internal-api-J27TYE2I.js.map} +0 -0
- /package/dist/{server/agent/index.js.map → load-config-JKYO5RFK.js.map} +0 -0
- /package/dist/{registry-34LL7NF4.js.map → registry-XJUYD2OU.js.map} +0 -0
- /package/dist/{static-55G3LX2I.js.map → static-7ARBVDJF.js.map} +0 -0
- /package/dist/{vite-plugin-RK66K26Z.js.map → vite-plugin-GC6WCU4P.js.map} +0 -0
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
import "tsx/esm";
|
|
3
3
|
import "./chunk-KXA37ONC.js";
|
|
4
4
|
import "./chunk-WR4F4EEZ.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-NXTF5PPW.js";
|
|
6
|
+
import "./chunk-34YQOXGM.js";
|
|
6
7
|
import {
|
|
7
8
|
scanServerActionsEnriched
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GBXLKYIA.js";
|
|
10
|
+
import "./chunk-P37RZRFV.js";
|
|
9
11
|
import "./chunk-HGZL5EOI.js";
|
|
10
12
|
|
|
11
13
|
// src/vite-plugin/actions-virtual-module.ts
|
|
@@ -209,4 +211,4 @@ export const actions = manifest
|
|
|
209
211
|
export {
|
|
210
212
|
actionsVirtualModule
|
|
211
213
|
};
|
|
212
|
-
//# sourceMappingURL=actions-virtual-module-
|
|
214
|
+
//# sourceMappingURL=actions-virtual-module-G4BANOLW.js.map
|
package/dist/{actions-virtual-module-3CDQTWOC.js.map → actions-virtual-module-G4BANOLW.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin/actions-virtual-module.ts"],"sourcesContent":["/**\n * Vite plugin: resolves `@theo/actions` virtual module and emits a typed\n * Proxy facade that calls `theoFetch('/_actions/<name>', body)` against the\n * server-side action handlers.\n *\n * Per plan g3-server-actions-and-useaction v1.2 § Phase 3 / T3.1 + ADR D4.\n * Astro `vite-plugin-actions.ts` modular pattern. Single focused plugin\n * (not monolithic), virtual module + `configureServer` watcher.\n *\n * Module shape emitted on client environment: thin proxy that forwards\n * `actions.<name>(input)` to `POST /_actions/<name>` with JSON or\n * FormData body. On server environment: re-export of the manifest for\n * SSR / module-loader access (T1.3 sub-C consumer).\n *\n * EC-15 (DOCUMENT): HMR uses Vite default invalidation — coarse but\n * correct. Optimization (fine-grained invalidation of only consumers of\n * `@theo/actions`) deferred to G3.1 if dev-server p95 reload > 500ms.\n */\nimport { existsSync, mkdirSync, renameSync, watch as fsWatch, writeFileSync } from 'node:fs'\nimport { dirname, join, posix, resolve } from 'node:path'\n\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport { scanServerActionsEnriched, type ActionManifestEntry } from '../server/internal-api.js'\n\nconst VIRTUAL_ID = '@theo/actions'\nconst RESOLVED_ID = `\\0${VIRTUAL_ID}`\n\nexport interface ActionsVirtualModuleOptions {\n /** Path to server directory (where `actions/` lives). */\n serverDir: string\n /**\n * Optional `.theokit/` output dir for emitting `actions.d.ts`. When provided,\n * the plugin writes an ambient `declare module '@theo/actions'` so TS in\n * consumer apps can resolve the virtual module import. Mirrors the G1\n * `app-typed-client` emit pattern.\n */\n distDir?: string\n}\n\nexport function actionsVirtualModule(opts: ActionsVirtualModuleOptions): Plugin {\n const { serverDir, distDir } = opts\n\n function emitActionsDts(): void {\n if (distDir === undefined) return\n const entries = safeScan(serverDir)\n const dtsPath = posix.join(resolve(distDir).replace(/\\\\/g, '/'), 'actions.d.ts')\n const content = generateActionsDts(entries)\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n mkdirSync(dirname(dtsPath), { recursive: true })\n const tmp = `${dtsPath}.${process.pid}.tmp`\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n writeFileSync(tmp, content)\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n renameSync(tmp, dtsPath)\n } catch (err) {\n console.error('[theokit:actions-virtual-module] dts emit error:', err)\n }\n }\n\n return {\n name: 'theo:actions-virtual-module',\n enforce: 'pre',\n configResolved() {\n emitActionsDts()\n },\n buildEnd() {\n emitActionsDts()\n },\n resolveId(id: string) {\n if (id === VIRTUAL_ID) return RESOLVED_ID\n return undefined\n },\n load(id: string) {\n if (id !== RESOLVED_ID) return undefined\n const entries = safeScan(serverDir)\n const ctx = this as unknown as { environment?: { name?: string } } | undefined\n const envName = ctx?.environment?.name\n const isClient = envName === 'client' || envName === undefined\n return isClient ? generateClientFacade(entries) : generateServerManifest(entries)\n },\n configureServer(server: ViteDevServer) {\n const actionsDir = join(serverDir, 'actions')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n if (!existsSync(actionsDir)) return\n // EC-15: coarse invalidation — invalidate ALL modules on actions file change.\n // Optimization deferred per plan ADR D4 note.\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time path\n const watcher = fsWatch(actionsDir, { recursive: true }, () => {\n emitActionsDts()\n const mod = server.moduleGraph.getModuleById(RESOLVED_ID)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n server.ws.send({ type: 'full-reload' })\n })\n server.httpServer?.once('close', () => {\n watcher.close()\n })\n },\n }\n}\n\n/**\n * Emit `.theokit/actions.d.ts` — ambient declaration of `@theo/actions` so\n * consumer TS apps can `import { actions } from '@theo/actions'` and get\n * typed completions for each scanned action name. Each entry maps name to\n * a generic `(input: unknown) => Promise<{data, error}>` invoker — the full\n * input/output inference from defineAction would require either monomorphic\n * dts-bundle or virtual type emit (deferred to a future plan).\n */\nfunction generateActionsDts(entries: readonly ActionManifestEntry[]): string {\n const lines = entries\n .map((entry) => {\n const invokerType = `(input: unknown) => Promise<{ data: unknown; error: undefined } | { data: undefined; error: { code: string; message: string; fields?: Record<string, string[]>; type?: string } }>`\n // P#4 plugin-forms typed __zodSchema (per plan p4-plugin-forms v1.1 EC-3).\n // When schemaFilePath present, emit intersection type so consumers see both\n // the callable invoker AND the __zodSchema field with the exact schema type.\n if (entry.schemaFilePath !== undefined) {\n const schemaImportPath = entry.schemaFilePath\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|tsx|js|jsx)$/, '.js')\n return ` ${JSON.stringify(entry.name)}: (${invokerType}) & { readonly __zodSchema: typeof import(${JSON.stringify(schemaImportPath)}).schema }`\n }\n return ` ${JSON.stringify(entry.name)}: ${invokerType}`\n })\n .join('\\n')\n return `// AUTO-GENERATED by @theo/actions virtual module. DO NOT EDIT MANUALLY.\ndeclare module '@theo/actions' {\n export const actions: {\n${lines}\n }\n}\n`\n}\n\nfunction safeScan(serverDir: string): ActionManifestEntry[] {\n try {\n return scanServerActionsEnriched(serverDir)\n } catch {\n // ActionScanError surfaces at build time; for HMR keep an empty manifest\n // so the dev server doesn't crash mid-edit.\n return []\n }\n}\n\n/**\n * Client-side module: emits a Proxy facade. For each action `name` in\n * manifest, `actions.<name>(input)` POSTs to `/_actions/<name>` with JSON\n * body (or FormData when input is a FormData instance) and parses the\n * `application/json+devalue` response via the runtime helper.\n */\nfunction generateClientFacade(entries: ActionManifestEntry[]): string {\n const entryLines = entries\n .map((entry) => ` ${JSON.stringify(entry.name)}: ${JSON.stringify(entry.urlPath)},`)\n .join('\\n')\n\n // P#4 plugin-forms shared-schema convention (per plan p4-plugin-forms v1.1 T1.1).\n // Emit ESM imports + ACTION_SCHEMA_MAP so the Proxy can attach __zodSchema.\n // Vite resolves absolute filesystem paths via its module graph; schema files\n // are isomorphic (zod-only, no server APIs) so they bundle into the client.\n const schemaEntries = entries.filter((e) => e.schemaFilePath !== undefined)\n const schemaImports = schemaEntries\n .map((entry, idx) => {\n // schemaEntries was filtered by `e.schemaFilePath !== undefined` above,\n // so the assertion is provably safe in this scope.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const importPath = entry.schemaFilePath!.replace(/\\\\/g, '/')\n return `import { schema as __theoSchema${idx} } from ${JSON.stringify(importPath)}`\n })\n .join('\\n')\n const schemaMapLines = schemaEntries\n .map((entry, idx) => ` ${JSON.stringify(entry.name)}: __theoSchema${idx},`)\n .join('\\n')\n\n return `// AUTO-GENERATED by @theo/actions virtual module\n/* eslint-disable */\n${schemaImports}\n\nconst ACTION_URL_MAP = {\n${entryLines}\n}\n\nconst ACTION_SCHEMA_MAP = {\n${schemaMapLines}\n}\n\n// Devtools telemetry — fires in the SAME realm as the Overlay React tree\n// (browser), so the Actions tab updates immediately on each call.\n// The Overlay component installs \\`window.__theoDevtoolsDispatcher\\` on\n// mount (dev only; prod tree-shakes the Overlay entirely). Reading the\n// global avoids a cross-package dynamic import that Vite's virtual-module\n// resolver cannot trace. Same pattern as React DevTools'\n// \\`__REACT_DEVTOOLS_GLOBAL_HOOK__\\`.\nfunction emitTelemetry(name, startedAt, input, outcome) {\n if (typeof window === 'undefined') return\n const dispatcher = window.__theoDevtoolsDispatcher\n if (!dispatcher || typeof dispatcher.onActionCall !== 'function') return\n try {\n dispatcher.onActionCall({\n id: 'act-' + String(Date.now()) + '-' + Math.random().toString(36).slice(2, 8),\n timestamp: startedAt,\n name,\n input,\n output: outcome.status === 'success' ? outcome.data : undefined,\n error: outcome.status === 'error' ? outcome.error : undefined,\n durationMs: Date.now() - startedAt,\n status: outcome.status,\n })\n } catch (_) {\n // dispatcher threw — devtools UI bug; never bubble to consumer code\n }\n}\n\nasync function callAction(name, url, input) {\n const startedAt = Date.now()\n const isForm = typeof FormData !== 'undefined' && input instanceof FormData\n const headers = { 'x-theo-action': '1' }\n const body = isForm ? input : JSON.stringify(input ?? {})\n if (!isForm) headers['content-type'] = 'application/json'\n let response\n try {\n response = await fetch(url, { method: 'POST', headers, body, credentials: 'same-origin' })\n } catch (e) {\n const err = { code: 'NETWORK_ERROR', message: e && e.message ? e.message : 'fetch failed' }\n emitTelemetry(name, startedAt, input, { status: 'error', error: err })\n return { data: undefined, error: err }\n }\n const contentType = response.headers.get('content-type') ?? ''\n let result\n if (response.status === 204) {\n result = { data: undefined, error: undefined }\n } else if (contentType.startsWith('application/json+devalue')) {\n const text = await response.text()\n const { parse } = await import('devalue')\n result = { data: parse(text, { URL: (h) => new URL(h) }), error: undefined }\n } else if (contentType.startsWith('application/json')) {\n const json = await response.json()\n result = response.ok ? { data: json, error: undefined } : { data: undefined, error: json }\n } else {\n result = { data: undefined, error: { code: 'INTERNAL_SERVER_ERROR', message: await response.text() } }\n }\n emitTelemetry(name, startedAt, input, result.error\n ? { status: 'error', error: result.error }\n : { status: 'success', data: result.data })\n return result\n}\n\n// P#4 plugin-forms cache: callable proxies must be stable per action so that\n// repeated property access does not allocate + reset the __zodSchema attach.\nconst ACTION_CALLABLE_CACHE = {}\n\nexport const actions = new Proxy({}, {\n get(target, prop) {\n if (typeof prop !== 'string') return undefined\n const url = ACTION_URL_MAP[prop]\n if (!url) return undefined\n if (!ACTION_CALLABLE_CACHE[prop]) {\n const callable = (input) => callAction(prop, url, input)\n const schema = ACTION_SCHEMA_MAP[prop]\n if (schema !== undefined) {\n Object.defineProperty(callable, '__zodSchema', {\n value: schema,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n }\n ACTION_CALLABLE_CACHE[prop] = callable\n }\n return ACTION_CALLABLE_CACHE[prop]\n },\n})\n`\n}\n\n/**\n * Server-side module: re-exports the manifest entries for SSR / module-\n * loader consumption (T1.3 sub-C in action-execute.ts will route requests\n * via this manifest).\n */\nfunction generateServerManifest(entries: ActionManifestEntry[]): string {\n return `// AUTO-GENERATED by @theo/actions virtual module (server env)\n/* eslint-disable */\nexport const manifest = ${JSON.stringify(entries, null, 2)}\nexport const actions = manifest\n`\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/actions-virtual-module.ts"],"sourcesContent":["/**\n * Vite plugin: resolves `@theo/actions` virtual module and emits a typed\n * Proxy facade that calls `theoFetch('/_actions/<name>', body)` against the\n * server-side action handlers.\n *\n * Per plan g3-server-actions-and-useaction v1.2 § Phase 3 / T3.1 + ADR D4.\n * Astro `vite-plugin-actions.ts` modular pattern. Single focused plugin\n * (not monolithic), virtual module + `configureServer` watcher.\n *\n * Module shape emitted on client environment: thin proxy that forwards\n * `actions.<name>(input)` to `POST /_actions/<name>` with JSON or\n * FormData body. On server environment: re-export of the manifest for\n * SSR / module-loader access (T1.3 sub-C consumer).\n *\n * EC-15 (DOCUMENT): HMR uses Vite default invalidation — coarse but\n * correct. Optimization (fine-grained invalidation of only consumers of\n * `@theo/actions`) deferred to G3.1 if dev-server p95 reload > 500ms.\n */\nimport { existsSync, mkdirSync, renameSync, watch as fsWatch, writeFileSync } from 'node:fs'\nimport { dirname, join, posix, resolve } from 'node:path'\n\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport { scanServerActionsEnriched, type ActionManifestEntry } from '../server/internal-api.js'\n\nconst VIRTUAL_ID = '@theo/actions'\nconst RESOLVED_ID = `\\0${VIRTUAL_ID}`\n\nexport interface ActionsVirtualModuleOptions {\n /** Path to server directory (where `actions/` lives). */\n serverDir: string\n /**\n * Optional `.theokit/` output dir for emitting `actions.d.ts`. When provided,\n * the plugin writes an ambient `declare module '@theo/actions'` so TS in\n * consumer apps can resolve the virtual module import. Mirrors the G1\n * `app-typed-client` emit pattern.\n */\n distDir?: string\n}\n\nexport function actionsVirtualModule(opts: ActionsVirtualModuleOptions): Plugin {\n const { serverDir, distDir } = opts\n\n function emitActionsDts(): void {\n if (distDir === undefined) return\n const entries = safeScan(serverDir)\n const dtsPath = posix.join(resolve(distDir).replace(/\\\\/g, '/'), 'actions.d.ts')\n const content = generateActionsDts(entries)\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n mkdirSync(dirname(dtsPath), { recursive: true })\n const tmp = `${dtsPath}.${process.pid}.tmp`\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n writeFileSync(tmp, content)\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n renameSync(tmp, dtsPath)\n } catch (err) {\n console.error('[theokit:actions-virtual-module] dts emit error:', err)\n }\n }\n\n return {\n name: 'theo:actions-virtual-module',\n enforce: 'pre',\n configResolved() {\n emitActionsDts()\n },\n buildEnd() {\n emitActionsDts()\n },\n resolveId(id: string) {\n if (id === VIRTUAL_ID) return RESOLVED_ID\n return undefined\n },\n load(id: string) {\n if (id !== RESOLVED_ID) return undefined\n const entries = safeScan(serverDir)\n const ctx = this as unknown as { environment?: { name?: string } } | undefined\n const envName = ctx?.environment?.name\n const isClient = envName === 'client' || envName === undefined\n return isClient ? generateClientFacade(entries) : generateServerManifest(entries)\n },\n configureServer(server: ViteDevServer) {\n const actionsDir = join(serverDir, 'actions')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time derived path\n if (!existsSync(actionsDir)) return\n // EC-15: coarse invalidation — invalidate ALL modules on actions file change.\n // Optimization deferred per plan ADR D4 note.\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- build-time path\n const watcher = fsWatch(actionsDir, { recursive: true }, () => {\n emitActionsDts()\n const mod = server.moduleGraph.getModuleById(RESOLVED_ID)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n server.ws.send({ type: 'full-reload' })\n })\n server.httpServer?.once('close', () => {\n watcher.close()\n })\n },\n }\n}\n\n/**\n * Emit `.theokit/actions.d.ts` — ambient declaration of `@theo/actions` so\n * consumer TS apps can `import { actions } from '@theo/actions'` and get\n * typed completions for each scanned action name. Each entry maps name to\n * a generic `(input: unknown) => Promise<{data, error}>` invoker — the full\n * input/output inference from defineAction would require either monomorphic\n * dts-bundle or virtual type emit (deferred to a future plan).\n */\nfunction generateActionsDts(entries: readonly ActionManifestEntry[]): string {\n const lines = entries\n .map((entry) => {\n const invokerType = `(input: unknown) => Promise<{ data: unknown; error: undefined } | { data: undefined; error: { code: string; message: string; fields?: Record<string, string[]>; type?: string } }>`\n // P#4 plugin-forms typed __zodSchema (per plan p4-plugin-forms v1.1 EC-3).\n // When schemaFilePath present, emit intersection type so consumers see both\n // the callable invoker AND the __zodSchema field with the exact schema type.\n if (entry.schemaFilePath !== undefined) {\n const schemaImportPath = entry.schemaFilePath\n .replace(/\\\\/g, '/')\n .replace(/\\.(ts|tsx|js|jsx)$/, '.js')\n return ` ${JSON.stringify(entry.name)}: (${invokerType}) & { readonly __zodSchema: typeof import(${JSON.stringify(schemaImportPath)}).schema }`\n }\n return ` ${JSON.stringify(entry.name)}: ${invokerType}`\n })\n .join('\\n')\n return `// AUTO-GENERATED by @theo/actions virtual module. DO NOT EDIT MANUALLY.\ndeclare module '@theo/actions' {\n export const actions: {\n${lines}\n }\n}\n`\n}\n\nfunction safeScan(serverDir: string): ActionManifestEntry[] {\n try {\n return scanServerActionsEnriched(serverDir)\n } catch {\n // ActionScanError surfaces at build time; for HMR keep an empty manifest\n // so the dev server doesn't crash mid-edit.\n return []\n }\n}\n\n/**\n * Client-side module: emits a Proxy facade. For each action `name` in\n * manifest, `actions.<name>(input)` POSTs to `/_actions/<name>` with JSON\n * body (or FormData when input is a FormData instance) and parses the\n * `application/json+devalue` response via the runtime helper.\n */\nfunction generateClientFacade(entries: ActionManifestEntry[]): string {\n const entryLines = entries\n .map((entry) => ` ${JSON.stringify(entry.name)}: ${JSON.stringify(entry.urlPath)},`)\n .join('\\n')\n\n // P#4 plugin-forms shared-schema convention (per plan p4-plugin-forms v1.1 T1.1).\n // Emit ESM imports + ACTION_SCHEMA_MAP so the Proxy can attach __zodSchema.\n // Vite resolves absolute filesystem paths via its module graph; schema files\n // are isomorphic (zod-only, no server APIs) so they bundle into the client.\n const schemaEntries = entries.filter((e) => e.schemaFilePath !== undefined)\n const schemaImports = schemaEntries\n .map((entry, idx) => {\n // schemaEntries was filtered by `e.schemaFilePath !== undefined` above,\n // so the assertion is provably safe in this scope.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const importPath = entry.schemaFilePath!.replace(/\\\\/g, '/')\n return `import { schema as __theoSchema${idx} } from ${JSON.stringify(importPath)}`\n })\n .join('\\n')\n const schemaMapLines = schemaEntries\n .map((entry, idx) => ` ${JSON.stringify(entry.name)}: __theoSchema${idx},`)\n .join('\\n')\n\n return `// AUTO-GENERATED by @theo/actions virtual module\n/* eslint-disable */\n${schemaImports}\n\nconst ACTION_URL_MAP = {\n${entryLines}\n}\n\nconst ACTION_SCHEMA_MAP = {\n${schemaMapLines}\n}\n\n// Devtools telemetry — fires in the SAME realm as the Overlay React tree\n// (browser), so the Actions tab updates immediately on each call.\n// The Overlay component installs \\`window.__theoDevtoolsDispatcher\\` on\n// mount (dev only; prod tree-shakes the Overlay entirely). Reading the\n// global avoids a cross-package dynamic import that Vite's virtual-module\n// resolver cannot trace. Same pattern as React DevTools'\n// \\`__REACT_DEVTOOLS_GLOBAL_HOOK__\\`.\nfunction emitTelemetry(name, startedAt, input, outcome) {\n if (typeof window === 'undefined') return\n const dispatcher = window.__theoDevtoolsDispatcher\n if (!dispatcher || typeof dispatcher.onActionCall !== 'function') return\n try {\n dispatcher.onActionCall({\n id: 'act-' + String(Date.now()) + '-' + Math.random().toString(36).slice(2, 8),\n timestamp: startedAt,\n name,\n input,\n output: outcome.status === 'success' ? outcome.data : undefined,\n error: outcome.status === 'error' ? outcome.error : undefined,\n durationMs: Date.now() - startedAt,\n status: outcome.status,\n })\n } catch (_) {\n // dispatcher threw — devtools UI bug; never bubble to consumer code\n }\n}\n\nasync function callAction(name, url, input) {\n const startedAt = Date.now()\n const isForm = typeof FormData !== 'undefined' && input instanceof FormData\n const headers = { 'x-theo-action': '1' }\n const body = isForm ? input : JSON.stringify(input ?? {})\n if (!isForm) headers['content-type'] = 'application/json'\n let response\n try {\n response = await fetch(url, { method: 'POST', headers, body, credentials: 'same-origin' })\n } catch (e) {\n const err = { code: 'NETWORK_ERROR', message: e && e.message ? e.message : 'fetch failed' }\n emitTelemetry(name, startedAt, input, { status: 'error', error: err })\n return { data: undefined, error: err }\n }\n const contentType = response.headers.get('content-type') ?? ''\n let result\n if (response.status === 204) {\n result = { data: undefined, error: undefined }\n } else if (contentType.startsWith('application/json+devalue')) {\n const text = await response.text()\n const { parse } = await import('devalue')\n result = { data: parse(text, { URL: (h) => new URL(h) }), error: undefined }\n } else if (contentType.startsWith('application/json')) {\n const json = await response.json()\n result = response.ok ? { data: json, error: undefined } : { data: undefined, error: json }\n } else {\n result = { data: undefined, error: { code: 'INTERNAL_SERVER_ERROR', message: await response.text() } }\n }\n emitTelemetry(name, startedAt, input, result.error\n ? { status: 'error', error: result.error }\n : { status: 'success', data: result.data })\n return result\n}\n\n// P#4 plugin-forms cache: callable proxies must be stable per action so that\n// repeated property access does not allocate + reset the __zodSchema attach.\nconst ACTION_CALLABLE_CACHE = {}\n\nexport const actions = new Proxy({}, {\n get(target, prop) {\n if (typeof prop !== 'string') return undefined\n const url = ACTION_URL_MAP[prop]\n if (!url) return undefined\n if (!ACTION_CALLABLE_CACHE[prop]) {\n const callable = (input) => callAction(prop, url, input)\n const schema = ACTION_SCHEMA_MAP[prop]\n if (schema !== undefined) {\n Object.defineProperty(callable, '__zodSchema', {\n value: schema,\n enumerable: false,\n writable: false,\n configurable: false,\n })\n }\n ACTION_CALLABLE_CACHE[prop] = callable\n }\n return ACTION_CALLABLE_CACHE[prop]\n },\n})\n`\n}\n\n/**\n * Server-side module: re-exports the manifest entries for SSR / module-\n * loader consumption (T1.3 sub-C in action-execute.ts will route requests\n * via this manifest).\n */\nfunction generateServerManifest(entries: ActionManifestEntry[]): string {\n return `// AUTO-GENERATED by @theo/actions virtual module (server env)\n/* eslint-disable */\nexport const manifest = ${JSON.stringify(entries, null, 2)}\nexport const actions = manifest\n`\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import "./chunk-RSVN727G.js";
|
|
2
|
-
import "./chunk-TGTNRUH3.js";
|
|
3
|
-
import "./chunk-GY5Q27BJ.js";
|
|
4
2
|
import "./chunk-5QW7IQQU.js";
|
|
5
|
-
import "./chunk-WFNLNIJX.js";
|
|
6
3
|
import "./chunk-IAJ2JVEH.js";
|
|
7
4
|
import "./chunk-VMEWD57H.js";
|
|
8
5
|
import "./chunk-JQSKBMXP.js";
|
|
@@ -10,14 +7,17 @@ import {
|
|
|
10
7
|
scanServerActionsEnriched
|
|
11
8
|
} from "./chunk-2J7XU3PW.js";
|
|
12
9
|
import "./chunk-WSJKACWB.js";
|
|
13
|
-
import "./chunk-ZEGYW52B.js";
|
|
14
10
|
import "./chunk-JBCHWRKF.js";
|
|
11
|
+
import "./chunk-C3ZZ56YZ.js";
|
|
12
|
+
import "./chunk-X2VVCJ4V.js";
|
|
13
|
+
import "./chunk-H243MR3F.js";
|
|
14
|
+
import "./chunk-GY5Q27BJ.js";
|
|
15
|
+
import "./chunk-Z5IGLBWE.js";
|
|
16
|
+
import "./chunk-ZEGYW52B.js";
|
|
15
17
|
import "./chunk-TSLSIRBR.js";
|
|
16
18
|
import "./chunk-UD3LFGDL.js";
|
|
17
19
|
import "./chunk-X32XEJXR.js";
|
|
18
20
|
import "./chunk-UVXB2ER7.js";
|
|
19
|
-
import "./chunk-C3ZZ56YZ.js";
|
|
20
|
-
import "./chunk-X2VVCJ4V.js";
|
|
21
21
|
import "./chunk-DGUM43GV.js";
|
|
22
22
|
|
|
23
23
|
// src/vite-plugin/actions-virtual-module.ts
|
|
@@ -221,4 +221,4 @@ export const actions = manifest
|
|
|
221
221
|
export {
|
|
222
222
|
actionsVirtualModule
|
|
223
223
|
};
|
|
224
|
-
//# sourceMappingURL=actions-virtual-module-
|
|
224
|
+
//# sourceMappingURL=actions-virtual-module-RM7JRY5N.js.map
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "tsx/esm";
|
|
3
|
+
import {
|
|
4
|
+
getApprovalRegistry,
|
|
5
|
+
resolveProvider
|
|
6
|
+
} from "./chunk-NBWB4S46.js";
|
|
7
|
+
import {
|
|
8
|
+
loadEnv
|
|
9
|
+
} from "./chunk-GDN3PXFH.js";
|
|
10
|
+
import {
|
|
11
|
+
scanAgents
|
|
12
|
+
} from "./chunk-34YQOXGM.js";
|
|
13
|
+
import "./chunk-P37RZRFV.js";
|
|
14
|
+
|
|
15
|
+
// src/server/agent/run-terminal-agent.ts
|
|
16
|
+
import {
|
|
17
|
+
compileAgentModule,
|
|
18
|
+
streamAgentUIMessages
|
|
19
|
+
} from "@theokit/agents";
|
|
20
|
+
|
|
21
|
+
// src/server/agent/render-terminal.ts
|
|
22
|
+
import { createInterface } from "readline";
|
|
23
|
+
var ANSI = {
|
|
24
|
+
dim: "\x1B[2m",
|
|
25
|
+
red: "\x1B[31m",
|
|
26
|
+
cyan: "\x1B[36m",
|
|
27
|
+
green: "\x1B[32m",
|
|
28
|
+
reset: "\x1B[0m"
|
|
29
|
+
};
|
|
30
|
+
function paint(text, color, tty) {
|
|
31
|
+
return tty ? `${ANSI[color]}${text}${ANSI.reset}` : text;
|
|
32
|
+
}
|
|
33
|
+
function display(value) {
|
|
34
|
+
return typeof value === "string" ? value : JSON.stringify(value ?? {});
|
|
35
|
+
}
|
|
36
|
+
function renderChunkLine(chunk, tty) {
|
|
37
|
+
switch (chunk.type) {
|
|
38
|
+
case "text-delta":
|
|
39
|
+
return chunk.delta;
|
|
40
|
+
case "text-end":
|
|
41
|
+
case "finish":
|
|
42
|
+
return "\n";
|
|
43
|
+
case "reasoning-delta":
|
|
44
|
+
return paint(chunk.delta, "dim", tty);
|
|
45
|
+
case "reasoning-end":
|
|
46
|
+
return "\n";
|
|
47
|
+
case "tool-input-available":
|
|
48
|
+
return paint(`
|
|
49
|
+
\u25B8 ${chunk.toolName}(${display(chunk.input)})
|
|
50
|
+
`, "cyan", tty);
|
|
51
|
+
case "tool-output-available":
|
|
52
|
+
return paint(` \u2713 ${display(chunk.output)}
|
|
53
|
+
`, "green", tty);
|
|
54
|
+
case "tool-output-error":
|
|
55
|
+
return paint(` \u2717 ${chunk.errorText}
|
|
56
|
+
`, "red", tty);
|
|
57
|
+
case "error":
|
|
58
|
+
return paint(`
|
|
59
|
+
\u2717 ${chunk.errorText}
|
|
60
|
+
`, "red", tty);
|
|
61
|
+
default:
|
|
62
|
+
return chunk.type === "data-checkpoint" ? paint("\n\u2387 checkpoint saved \u2014 resume with the same session id\n", "dim", tty) : "";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function renderAgentStreamToTerminal(chunks, opts) {
|
|
66
|
+
const tty = opts.stdout.isTTY;
|
|
67
|
+
const write = (s) => {
|
|
68
|
+
if (s) opts.stdout.write(s);
|
|
69
|
+
};
|
|
70
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
71
|
+
let sawError = false;
|
|
72
|
+
for await (const chunk of chunks) {
|
|
73
|
+
if (chunk.type === "tool-approval-request") {
|
|
74
|
+
const toolName = toolNames.get(chunk.toolCallId);
|
|
75
|
+
await opts.onApproval({ approvalId: chunk.approvalId, toolName: toolName ?? "tool" });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (chunk.type === "tool-input-available" && chunk.toolCallId) {
|
|
79
|
+
toolNames.set(chunk.toolCallId, chunk.toolName);
|
|
80
|
+
}
|
|
81
|
+
if (chunk.type === "error") sawError = true;
|
|
82
|
+
write(renderChunkLine(chunk, tty));
|
|
83
|
+
}
|
|
84
|
+
return { sawError };
|
|
85
|
+
}
|
|
86
|
+
function promptTerminalApproval(req, io, timeoutMs) {
|
|
87
|
+
if (!io.input.isTTY || !io.output.isTTY) {
|
|
88
|
+
io.output.write(`
|
|
89
|
+
\u26A0 Non-interactive terminal \u2014 auto-denying approval for '${req.toolName}'.
|
|
90
|
+
`);
|
|
91
|
+
return Promise.resolve(false);
|
|
92
|
+
}
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const rl = createInterface({ input: io.input, output: io.output });
|
|
95
|
+
let timer;
|
|
96
|
+
const settle = (approved) => {
|
|
97
|
+
if (timer) clearTimeout(timer);
|
|
98
|
+
rl.close();
|
|
99
|
+
resolve(approved);
|
|
100
|
+
};
|
|
101
|
+
if (timeoutMs !== void 0 && timeoutMs > 0) {
|
|
102
|
+
timer = setTimeout(() => {
|
|
103
|
+
io.output.write(`
|
|
104
|
+
\u26A0 Approval timed out \u2014 denying '${req.toolName}'.
|
|
105
|
+
`);
|
|
106
|
+
settle(false);
|
|
107
|
+
}, timeoutMs);
|
|
108
|
+
}
|
|
109
|
+
rl.question(`
|
|
110
|
+
\u2754 Approve ${req.toolName}? (y/N) `, (answer) => {
|
|
111
|
+
const a = answer.trim().toLowerCase();
|
|
112
|
+
settle(a === "y" || a === "yes");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/server/agent/run-terminal-agent.ts
|
|
118
|
+
async function runAgentInTerminal(mod, apiKey, input) {
|
|
119
|
+
const compiled = compileAgentModule(mod, input.source);
|
|
120
|
+
const stdout = input.stdout ?? process.stdout;
|
|
121
|
+
const registry = input.registry ?? getApprovalRegistry();
|
|
122
|
+
const sessionId = input.sessionId ?? crypto.randomUUID();
|
|
123
|
+
const promptApproval = input.promptApproval ?? ((req) => promptTerminalApproval(req, { input: process.stdin, output: process.stdout }, req.timeoutMs));
|
|
124
|
+
const gated = compiled.hitl;
|
|
125
|
+
const hitl = gated && gated.size > 0 ? {
|
|
126
|
+
gated,
|
|
127
|
+
awaitApproval: (approvalId, opts) => registry.register(approvalId, {
|
|
128
|
+
timeoutMs: opts.timeout ?? 3e5,
|
|
129
|
+
onTimeout: opts.onTimeout ?? "abort"
|
|
130
|
+
})
|
|
131
|
+
} : void 0;
|
|
132
|
+
const chunks = streamAgentUIMessages(compiled, apiKey, {
|
|
133
|
+
message: input.message,
|
|
134
|
+
sessionId,
|
|
135
|
+
hitl
|
|
136
|
+
});
|
|
137
|
+
return renderAgentStreamToTerminal(chunks, {
|
|
138
|
+
stdout,
|
|
139
|
+
onApproval: async ({ approvalId, toolName }) => {
|
|
140
|
+
const timeoutMs = gated?.get(toolName)?.timeout ?? 3e5;
|
|
141
|
+
const approved = await promptApproval({ toolName, timeoutMs });
|
|
142
|
+
registry.resolve(approvalId, approved);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/cli/commands/agent.ts
|
|
148
|
+
async function agentCommand(name, message, deps = {}) {
|
|
149
|
+
if (!message || message.trim().length === 0) {
|
|
150
|
+
throw new Error('theokit agent: a message is required. Usage: theokit agent <name> "<message>"');
|
|
151
|
+
}
|
|
152
|
+
const projectRoot = deps.projectRoot ?? process.cwd();
|
|
153
|
+
const agents = scanAgents(projectRoot);
|
|
154
|
+
const agent = agents.find((a) => a.name === name);
|
|
155
|
+
if (!agent) {
|
|
156
|
+
const names = agents.map((a) => a.name).join(", ") || "(none found in agents/)";
|
|
157
|
+
throw new Error(`theokit agent: '${name}' not found. Available agents: ${names}`);
|
|
158
|
+
}
|
|
159
|
+
const runAgent = deps.runAgent ?? runAgentInTerminal;
|
|
160
|
+
const apiKey = deps.resolveApiKey?.() ?? (() => {
|
|
161
|
+
loadEnv({ cwd: projectRoot, mode: "development" });
|
|
162
|
+
return resolveProvider().apiKey;
|
|
163
|
+
})();
|
|
164
|
+
let loadModule = deps.loadModule;
|
|
165
|
+
let dispose;
|
|
166
|
+
if (!loadModule) {
|
|
167
|
+
const loader = await createAgentSsrLoader(projectRoot);
|
|
168
|
+
loadModule = loader.load;
|
|
169
|
+
dispose = loader.dispose;
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const mod = await loadModule(agent.filePath);
|
|
173
|
+
return await runAgent(mod, apiKey, { message, source: agent.filePath });
|
|
174
|
+
} finally {
|
|
175
|
+
if (dispose) await dispose();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function createAgentSsrLoader(projectRoot) {
|
|
179
|
+
const { createServer } = await import("vite");
|
|
180
|
+
const react = (await import("@vitejs/plugin-react")).default;
|
|
181
|
+
const { theoPluginAsync } = await import("./vite-plugin-GC6WCU4P.js");
|
|
182
|
+
const { loadConfig } = await import("./load-config-JKYO5RFK.js");
|
|
183
|
+
const config = await loadConfig(projectRoot);
|
|
184
|
+
const theoPlugins = await theoPluginAsync({
|
|
185
|
+
root: projectRoot,
|
|
186
|
+
ssr: config.ssr,
|
|
187
|
+
services: config.services,
|
|
188
|
+
viteOptimizeDeps: config.viteOptimizeDeps
|
|
189
|
+
});
|
|
190
|
+
const server = await createServer({
|
|
191
|
+
root: projectRoot,
|
|
192
|
+
plugins: [react(), ...theoPlugins],
|
|
193
|
+
server: { middlewareMode: true },
|
|
194
|
+
appType: "custom",
|
|
195
|
+
logLevel: "silent"
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
load: (p) => server.ssrLoadModule(p),
|
|
199
|
+
dispose: async () => {
|
|
200
|
+
await server.close().catch((err) => {
|
|
201
|
+
console.warn("[theokit agent] Vite server cleanup error:", err);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
agentCommand
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=agent-MN7XGJR3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/agent/run-terminal-agent.ts","../src/server/agent/render-terminal.ts","../src/cli/commands/agent.ts"],"sourcesContent":["/**\n * M5 (theokit-ai-first) — run a compiled agent in the terminal, reusing the M4 harness.\n *\n * The web `mountAgent` renders `streamAgentUIMessages` to an HTTP `Response` and resolves HITL\n * approvals via the approve route. This is its terminal sibling: SAME `compileAgentModule` + SAME\n * `streamAgentUIMessages` + SAME in-process approval registry — only the render surface (stdout) and\n * the approval input (a readline prompt) differ (ADR 0039 D2). In a single-process CLI the stream's\n * `awaitApproval` (→ `registry.register`) and the terminal prompt (→ `registry.resolve`) share the one\n * in-process registry — the M4 singleton's exact fit. No new runtime, no LLM call, no tool dispatch.\n */\nimport {\n compileAgentModule,\n streamAgentUIMessages,\n type HumanInTheLoopOptions,\n} from '@theokit/agents'\n\nimport { getApprovalRegistry, type ApprovalRegistry } from './approval-registry.js'\nimport {\n promptTerminalApproval,\n renderAgentStreamToTerminal,\n type TerminalOut,\n} from './render-terminal.js'\n\nexport interface RunTerminalAgentInput {\n message: string\n /** Resume key; a fresh id per run when omitted. */\n sessionId?: string\n /** Injectable sink (defaults to `process.stdout` at the CLI entry). */\n stdout?: TerminalOut\n /** Injectable shared registry (defaults to the process singleton). */\n registry?: ApprovalRegistry\n /** Injectable approval prompt (defaults to the readline prompt over the process streams). */\n promptApproval?: (req: { toolName: string; timeoutMs?: number }) => Promise<boolean>\n /** Labels a fail-fast `AgentDefinitionError` (the file path). */\n source?: string\n}\n\n/**\n * Compile the module (M4, gathers `@Mixin` toolboxes), stream it, and render to the terminal. When the\n * agent has `@HumanInTheLoop`-gated tools, the terminal prompts and resolves the shared registry —\n * approve runs the tool, deny/timeout/non-TTY denies and the run continues.\n */\nexport async function runAgentInTerminal(\n mod: unknown,\n apiKey: string,\n input: RunTerminalAgentInput,\n): Promise<{ sawError: boolean }> {\n const compiled = compileAgentModule(mod, input.source)\n const stdout = input.stdout ?? process.stdout\n const registry = input.registry ?? getApprovalRegistry()\n const sessionId = input.sessionId ?? crypto.randomUUID()\n const promptApproval =\n input.promptApproval ??\n ((req: { toolName: string; timeoutMs?: number }) =>\n promptTerminalApproval(req, { input: process.stdin, output: process.stdout }, req.timeoutMs))\n\n // Mirror mount-agent's HITL wiring; absent ⇒ the non-HITL stream path (M2), unchanged.\n const gated = compiled.hitl\n const hitl =\n gated && gated.size > 0\n ? {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions) =>\n registry.register(approvalId, {\n timeoutMs: opts.timeout ?? 300_000,\n onTimeout: opts.onTimeout ?? 'abort',\n }),\n }\n : undefined\n\n const chunks = streamAgentUIMessages(compiled, apiKey, {\n message: input.message,\n sessionId,\n hitl,\n })\n return renderAgentStreamToTerminal(chunks, {\n stdout,\n onApproval: async ({ approvalId, toolName }) => {\n // The renderer paused here (the SDK run is paused in its awaited pre_tool_call hook); prompt,\n // then resolve the SAME registry the stream registered into — the run resumes with the decision.\n // The prompt shares the gated tool's timeout so it can never outlive the registry-settled run.\n const timeoutMs = gated?.get(toolName)?.timeout ?? 300_000\n const approved = await promptApproval({ toolName, timeoutMs })\n registry.resolve(approvalId, approved)\n },\n })\n}\n","/**\n * M5 (theokit-ai-first) — the terminal render surface for the M4 agent harness.\n *\n * Consumes the M4 `UIMessageChunk` stream (`streamAgentUIMessages`) and writes it to the terminal:\n * streaming text, a dim reasoning line, `▸ tool(input)` / result cards, a checkpoint notice, errors.\n * On a HITL `tool-approval-request` it delegates to the injected `onApproval` (the entry wires that to\n * a readline prompt + the approval registry). This is the ONLY new code M5 adds — no runtime, no LLM\n * call, no tool dispatch (ADR 0039 D2). I/O is INJECTED (`stdout`, prompt streams) so the whole\n * surface is deterministically testable without a TTY (ADR 0039 D4 / ADR-M5a).\n */\nimport { createInterface } from 'node:readline'\nimport type { Readable, Writable } from 'node:stream'\n\nimport type { UIMessageChunk } from 'ai'\n\n/** ANSI helpers — applied only on a TTY so a captured (non-TTY) sink stays plain-text for tests. */\nconst ANSI = {\n dim: '\\x1b[2m',\n red: '\\x1b[31m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n reset: '\\x1b[0m',\n}\nfunction paint(text: string, color: keyof typeof ANSI, tty: boolean | undefined): string {\n return tty ? `${ANSI[color]}${text}${ANSI.reset}` : text\n}\n\n/** A stdout-like sink; `isTTY` gates color (Node's `process.stdout` carries it, a test sink does not). */\nexport type TerminalOut = Writable & { isTTY?: boolean }\n\nexport interface TerminalRenderOptions {\n stdout: TerminalOut\n /** Resolve a HITL approval (the entry prompts + resolves the registry). */\n onApproval: (req: { approvalId: string; toolName: string }) => Promise<void>\n}\n\n/** Render an `unknown` tool output/input for display (string as-is; other JSON-ish values stringified). */\nfunction display(value: unknown): string {\n return typeof value === 'string' ? value : JSON.stringify(value ?? {})\n}\n\n/**\n * Map a single (non-approval) `UIMessageChunk` to its terminal line(s), or `''` for chunks that render\n * nothing (`start`/`text-start`/`reasoning-start`/…). Pure — the async loop owns approval + I/O.\n * Narrows the `ai` discriminated union directly on `chunk.type` (no cast; G3).\n */\nfunction renderChunkLine(chunk: UIMessageChunk, tty: boolean | undefined): string {\n switch (chunk.type) {\n case 'text-delta':\n return chunk.delta\n case 'text-end':\n case 'finish':\n return '\\n'\n case 'reasoning-delta':\n return paint(chunk.delta, 'dim', tty)\n case 'reasoning-end':\n return '\\n'\n case 'tool-input-available':\n return paint(`\\n▸ ${chunk.toolName}(${display(chunk.input)})\\n`, 'cyan', tty)\n case 'tool-output-available':\n return paint(` ✓ ${display(chunk.output)}\\n`, 'green', tty)\n case 'tool-output-error':\n return paint(` ✗ ${chunk.errorText}\\n`, 'red', tty)\n case 'error':\n return paint(`\\n✗ ${chunk.errorText}\\n`, 'red', tty)\n default:\n // ai-sdk `data-*` transient parts (e.g. the M4 `data-checkpoint` resume handle).\n return chunk.type === 'data-checkpoint'\n ? paint('\\n⎇ checkpoint saved — resume with the same session id\\n', 'dim', tty)\n : ''\n }\n}\n\n/**\n * Render the `UIMessageChunk` stream to `stdout`. Sequential: one chunk → its line(s). The SDK run is\n * already paused inside the awaited `pre_tool_call` hook when a `tool-approval-request` arrives, so a\n * blocking `await onApproval(...)` here is correct — no frame buffer, no concurrency. Returns whether\n * an `error` chunk was seen so the caller can signal a non-zero exit (fail-loud, not silent exit 0).\n */\nexport async function renderAgentStreamToTerminal(\n chunks: AsyncIterable<UIMessageChunk>,\n opts: TerminalRenderOptions,\n): Promise<{ sawError: boolean }> {\n const tty = opts.stdout.isTTY\n const write = (s: string): void => {\n if (s) opts.stdout.write(s)\n }\n // toolCallId → toolName, so the approval prompt (whose chunk carries only ids) can name the tool.\n const toolNames = new Map<string, string>()\n let sawError = false\n\n for await (const chunk of chunks) {\n if (chunk.type === 'tool-approval-request') {\n const toolName = toolNames.get(chunk.toolCallId)\n await opts.onApproval({ approvalId: chunk.approvalId, toolName: toolName ?? 'tool' })\n continue\n }\n if (chunk.type === 'tool-input-available' && chunk.toolCallId) {\n toolNames.set(chunk.toolCallId, chunk.toolName)\n }\n if (chunk.type === 'error') sawError = true\n write(renderChunkLine(chunk, tty))\n }\n return { sawError }\n}\n\n/** Injectable I/O for the approval prompt (defaults to the process streams at the CLI entry). */\nexport interface PromptIO {\n input: Readable & { isTTY?: boolean }\n output: Writable & { isTTY?: boolean }\n}\n\n/**\n * Prompt `Approve <tool>? (y/N)` and resolve to the decision. A non-interactive environment (piped /\n * CI — either stream is not a TTY) AUTO-DENIES without prompting (ADR-M5b fail-safe: a decision that\n * cannot be obtained is a deny, never an auto-approve; mirrors HITL `onTimeout: 'abort'`).\n *\n * `timeoutMs` (the gated tool's `@HumanInTheLoop` timeout) auto-denies + closes the prompt when the\n * human walks away — WITHOUT it the readline question would outlive the SDK run (the registry settles\n * the run at its own timeout but readline has no timer), hanging the CLI on a now-irrelevant prompt.\n */\nexport function promptTerminalApproval(\n req: { toolName: string },\n io: PromptIO,\n timeoutMs?: number,\n): Promise<boolean> {\n if (!io.input.isTTY || !io.output.isTTY) {\n io.output.write(`\\n⚠ Non-interactive terminal — auto-denying approval for '${req.toolName}'.\\n`)\n return Promise.resolve(false)\n }\n return new Promise<boolean>((resolve) => {\n const rl = createInterface({ input: io.input, output: io.output })\n let timer: ReturnType<typeof setTimeout> | undefined\n const settle = (approved: boolean): void => {\n if (timer) clearTimeout(timer)\n rl.close()\n resolve(approved)\n }\n if (timeoutMs !== undefined && timeoutMs > 0) {\n timer = setTimeout(() => {\n io.output.write(`\\n⚠ Approval timed out — denying '${req.toolName}'.\\n`)\n settle(false)\n }, timeoutMs)\n }\n rl.question(`\\n❔ Approve ${req.toolName}? (y/N) `, (answer) => {\n const a = answer.trim().toLowerCase()\n settle(a === 'y' || a === 'yes')\n })\n })\n}\n","/**\n * M5 (theokit-ai-first) — `theokit agent <name> [message]`: run a scanned agent in the terminal.\n *\n * Scans the top-level `agents/` directory (M2), loads the matched `agents/<name>.ts` through the\n * framework's OWN Vite pipeline (the SAME transpile `theokit dev` uses — legacy `@Agent`/`@Tool`\n * decorators need it; Node's native loader can't transpile TS), resolves the provider apiKey, and\n * runs it in the terminal via the M4 harness (`runAgentInTerminal`). A dev-time surface (ADR 0039 D1),\n * not a CLI product. `deps` is injectable so the fail-fast branches are tested without Vite/an LLM.\n */\nimport { loadEnv } from '../../config/load-env.js'\nimport { resolveProvider } from '../../server/agent/provider-resolver.js'\nimport { runAgentInTerminal } from '../../server/agent/run-terminal-agent.js'\nimport { scanAgents } from '../../server/scan/agent-scan.js'\n\nexport interface AgentCommandDeps {\n projectRoot?: string\n /** Load a TS agent module; defaults to a fresh minimal Vite SSR server (framework transpile). */\n loadModule?: (filePath: string) => Promise<Record<string, unknown>>\n runAgent?: typeof runAgentInTerminal\n resolveApiKey?: () => string\n}\n\nexport async function agentCommand(\n name: string,\n message: string | undefined,\n deps: AgentCommandDeps = {},\n): Promise<{ sawError: boolean }> {\n if (!message || message.trim().length === 0) {\n throw new Error('theokit agent: a message is required. Usage: theokit agent <name> \"<message>\"')\n }\n const projectRoot = deps.projectRoot ?? process.cwd()\n const agents = scanAgents(projectRoot)\n const agent = agents.find((a) => a.name === name)\n if (!agent) {\n const names = agents.map((a) => a.name).join(', ') || '(none found in agents/)'\n throw new Error(`theokit agent: '${name}' not found. Available agents: ${names}`)\n }\n\n const runAgent = deps.runAgent ?? runAgentInTerminal\n // Load .env into process.env BEFORE resolving the provider key — parity with `theokit dev`\n // (dev.ts loadEnv), so an app with OPENROUTER_API_KEY in .env works here too (review H1). Skipped\n // when the key is injected (tests) — a temp project has no .env anyway.\n const apiKey =\n deps.resolveApiKey?.() ??\n (() => {\n loadEnv({ cwd: projectRoot, mode: 'development' })\n return resolveProvider().apiKey\n })()\n\n let loadModule = deps.loadModule\n let dispose: (() => Promise<void>) | undefined\n if (!loadModule) {\n const loader = await createAgentSsrLoader(projectRoot)\n loadModule = loader.load\n dispose = loader.dispose\n }\n\n try {\n const mod = await loadModule(agent.filePath)\n return await runAgent(mod, apiKey, { message, source: agent.filePath })\n } finally {\n if (dispose) await dispose()\n }\n}\n\n/**\n * A minimal middleware-mode Vite server built from the SAME `theoPluginAsync` plugin set `theokit dev`\n * uses (`cli/commands/dev.ts`) — so a class-decorated `agents/<name>.ts` transpiles identically. No\n * HTTP listen, no service orchestration, silent — just `ssrLoadModule`. Caller MUST `dispose()`.\n */\nasync function createAgentSsrLoader(projectRoot: string): Promise<{\n load: (p: string) => Promise<Record<string, unknown>>\n dispose: () => Promise<void>\n}> {\n const { createServer } = await import('vite')\n const react = (await import('@vitejs/plugin-react')).default\n const { theoPluginAsync } = await import('../../vite-plugin/index.js')\n const { loadConfig } = await import('../../config/load-config.js')\n // Pass the project's ssr/services/optimizeDeps config through, exactly as dev.ts does — so an app\n // that customizes ssr.noExternal transpiles here identically to `theokit dev` (review L1).\n const config = await loadConfig(projectRoot)\n const theoPlugins = await theoPluginAsync({\n root: projectRoot,\n ssr: config.ssr,\n services: config.services,\n viteOptimizeDeps: config.viteOptimizeDeps,\n })\n const server = await createServer({\n root: projectRoot,\n plugins: [react(), ...theoPlugins],\n server: { middlewareMode: true },\n appType: 'custom',\n logLevel: 'silent',\n })\n return {\n load: (p) => server.ssrLoadModule(p) as Promise<Record<string, unknown>>,\n dispose: async () => {\n // Surface a cleanup failure instead of swallowing it (error-handling.md) — non-fatal, the run\n // already finished, so warn rather than throw past the CLI boundary.\n await server.close().catch((err: unknown) => {\n console.warn('[theokit agent] Vite server cleanup error:', err)\n })\n },\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import "./chunk-RSVN727G.js";
|
|
2
|
-
import "./chunk-TGTNRUH3.js";
|
|
3
|
-
import "./chunk-GY5Q27BJ.js";
|
|
4
2
|
import "./chunk-5QW7IQQU.js";
|
|
5
|
-
import "./chunk-WFNLNIJX.js";
|
|
6
3
|
import "./chunk-IAJ2JVEH.js";
|
|
7
4
|
import "./chunk-VMEWD57H.js";
|
|
8
5
|
import "./chunk-JQSKBMXP.js";
|
|
@@ -12,14 +9,17 @@ import {
|
|
|
12
9
|
import {
|
|
13
10
|
HTTP_METHODS
|
|
14
11
|
} from "./chunk-WSJKACWB.js";
|
|
15
|
-
import "./chunk-ZEGYW52B.js";
|
|
16
12
|
import "./chunk-JBCHWRKF.js";
|
|
13
|
+
import "./chunk-C3ZZ56YZ.js";
|
|
14
|
+
import "./chunk-X2VVCJ4V.js";
|
|
15
|
+
import "./chunk-H243MR3F.js";
|
|
16
|
+
import "./chunk-GY5Q27BJ.js";
|
|
17
|
+
import "./chunk-Z5IGLBWE.js";
|
|
18
|
+
import "./chunk-ZEGYW52B.js";
|
|
17
19
|
import "./chunk-TSLSIRBR.js";
|
|
18
20
|
import "./chunk-UD3LFGDL.js";
|
|
19
21
|
import "./chunk-X32XEJXR.js";
|
|
20
22
|
import "./chunk-UVXB2ER7.js";
|
|
21
|
-
import "./chunk-C3ZZ56YZ.js";
|
|
22
|
-
import "./chunk-X2VVCJ4V.js";
|
|
23
23
|
import "./chunk-DGUM43GV.js";
|
|
24
24
|
|
|
25
25
|
// src/vite-plugin/app-typed-client.ts
|
|
@@ -296,4 +296,4 @@ export {
|
|
|
296
296
|
emitClientDts,
|
|
297
297
|
generateClientDts
|
|
298
298
|
};
|
|
299
|
-
//# sourceMappingURL=app-typed-client-
|
|
299
|
+
//# sourceMappingURL=app-typed-client-5VQE6DNF.js.map
|
|
@@ -4,10 +4,12 @@ import "./chunk-KXA37ONC.js";
|
|
|
4
4
|
import "./chunk-WR4F4EEZ.js";
|
|
5
5
|
import {
|
|
6
6
|
generateManifest
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NXTF5PPW.js";
|
|
8
|
+
import "./chunk-34YQOXGM.js";
|
|
8
9
|
import {
|
|
9
10
|
HTTP_METHODS
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-GBXLKYIA.js";
|
|
12
|
+
import "./chunk-P37RZRFV.js";
|
|
11
13
|
import "./chunk-HGZL5EOI.js";
|
|
12
14
|
|
|
13
15
|
// src/vite-plugin/app-typed-client.ts
|
|
@@ -284,4 +286,4 @@ export {
|
|
|
284
286
|
emitClientDts,
|
|
285
287
|
generateClientDts
|
|
286
288
|
};
|
|
287
|
-
//# sourceMappingURL=app-typed-client-
|
|
289
|
+
//# sourceMappingURL=app-typed-client-Z6BHD4MF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin/app-typed-client.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Codegen-time fs ops: all paths derive from `opts.serverDir` / `opts.distDir`\n * (vite plugin opts — trusted, set by the framework's CLI). The plugin only\n * runs at dev/build time, never serves user HTTP input. No path traversal\n * surface here.\n */\n/**\n * Phase 2 + Phase 4 of G1 (g1-client-codegen-plan.md):\n *\n * 1. `generateClientDts(manifest, dtsOutPath)` — pure function emitting the\n * `.theokit/client.d.ts` shape (declared module `@theo/client`).\n * 2. `appTypedClientPlugin(opts)` — Vite plugin wrapper that calls (1),\n * writes the file atomically, and rewrites on route file changes.\n *\n * Implementation notes:\n * - Pure (1) is unit-testable without Vite or a filesystem (see G1-P2 tests).\n * - EC-2: kebab-case segments → camelCase; unsafe chars → bracket-access.\n * - EC-3: collision between HTTP method name and sub-segment name → method\n * wins; sub-segment ganha suffix `_` + warning comment.\n * - EC-6: write é `writeFileSync(tmp) + renameSync(tmp, final)` (POSIX atomic).\n * - EC-8: imports normalizam `\\\\` → `/`.\n */\n\nimport { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from 'node:fs'\nimport { dirname, isAbsolute, posix, relative, resolve } from 'node:path'\n\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport { HTTP_METHODS, type HttpMethod } from '../core/contracts/http-methods.js'\nimport { generateManifest, type ManifestRoute, type TheoManifest } from '../server/internal-api.js'\n\nconst FILE_HEADER = `// AUTO-GENERATED by theokit — do not edit.\n// Source: theokit/packages/theo/src/vite-plugin/app-typed-client.ts (G1 plan).\n// To refresh: \\`pnpm dev\\` or \\`pnpm build\\` regenerates this file on every route change.\n\n/* eslint-disable */\n`\n\ninterface TreeNode {\n /** sub-namespaces — keyed by segment name (already normalized). */\n children: Map<string, TreeNode>\n /** HTTP methods declared at this path → import alias. */\n methods: Map<HttpMethod, string>\n /** Whether any descendant declares dynamic params (for params type construction). */\n paramNamesAccumulated: string[]\n}\n\nfunction makeNode(): TreeNode {\n return { children: new Map(), methods: new Map(), paramNamesAccumulated: [] }\n}\n\n/**\n * Normalize a path segment to a valid TS identifier when possible.\n * EC-2: kebab-case → camelCase; pure dynamic (`:name`) → identifier `name`;\n * starts with digit or unsafe char → emit as bracket-access key.\n */\nfunction normalizeSegment(raw: string): { identifier: string; bracket: boolean } {\n // Dynamic segment: leading `:` (e.g. `:id` or `:...slug`).\n let s = raw\n if (s.startsWith(':...')) s = s.slice(4)\n else if (s.startsWith(':')) s = s.slice(1)\n\n // kebab → camel\n const camel = s.replace(/-([a-z0-9])/gi, (_, c: string) => c.toUpperCase())\n\n // Valid TS identifier (cannot start with digit, must be [a-zA-Z_$][a-zA-Z0-9_$]*)\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {\n return { identifier: camel, bracket: false }\n }\n // Fallback: bracket-access (keep raw form intact for JSON-string key)\n return { identifier: s, bracket: true }\n}\n\nfunction importPathFor(dtsOutPath: string, routeFileAbs: string): string {\n let rel = relative(dirname(dtsOutPath), routeFileAbs)\n // EC-8: normalize Windows path separators.\n rel = rel.replace(/\\\\/g, '/')\n // Strip extension (`.ts` / `.tsx`); TS module resolution likes extensionless.\n rel = rel.replace(/\\.[jt]sx?$/, '')\n if (!rel.startsWith('.') && !rel.startsWith('/')) {\n rel = './' + rel\n }\n return rel\n}\n\n/**\n * Convert a route path (e.g. `/api/users/:id/posts`) into the tree segments\n * we want under the `client` root. Strips the `/api/` prefix and splits.\n */\nfunction routePathSegments(routePath: string): string[] {\n let p = routePath\n if (p.startsWith('/api/')) p = p.slice(5)\n else if (p.startsWith('/api')) p = p.slice(4)\n if (p.startsWith('/')) p = p.slice(1)\n if (p === '') return []\n return p.split('/').filter(Boolean)\n}\n\nfunction paramNamesFromRoute(routePath: string): string[] {\n const out: string[] = []\n const re = /:(?:\\.\\.\\.)?([^/]+)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(routePath)) !== null) {\n out.push(m[1])\n }\n return out\n}\n\ninterface BuildContext {\n root: TreeNode\n /** Sequential index per route file for aliasing imports. */\n aliasCounter: { value: number }\n /** Lines emitted at the top of the declared module (one per route file). */\n importLines: string[]\n /** Diagnostic warnings rendered as block comments inside the declared module. */\n warnings: string[]\n dtsOutPath: string\n serverDir: string\n}\n\nfunction buildOneRoute(route: ManifestRoute, ctx: BuildContext): void {\n if (!route.methods || route.methods.length === 0) return\n // Build alias prefix once per route file (`_r0`, `_r1`, ...).\n const idx = ctx.aliasCounter.value++\n // Resolve absolute path for the route file (manifest stores it relative to serverDir).\n const absRouteFile = isAbsolute(route.filePath)\n ? route.filePath\n : resolve(ctx.serverDir, route.filePath)\n const importPath = importPathFor(ctx.dtsOutPath, absRouteFile)\n const aliases = route.methods\n .filter((m): m is HttpMethod => (HTTP_METHODS as readonly string[]).includes(m))\n .map((method) => ({ method, alias: `_r${idx}_${method}` }))\n if (aliases.length === 0) return\n const importClause = aliases.map((a) => `${a.method} as ${a.alias}`).join(', ')\n ctx.importLines.push(` import type { ${importClause} } from '${importPath}'`)\n\n // Walk segments + register methods at the leaf node.\n const segments = routePathSegments(route.routePath)\n let node = ctx.root\n for (const seg of segments) {\n const norm = normalizeSegment(seg)\n let child = node.children.get(norm.identifier)\n if (child === undefined) {\n child = makeNode()\n node.children.set(norm.identifier, child)\n }\n node = child\n }\n // Accumulated param names for typed params (e.g. `{ id: string }`).\n node.paramNamesAccumulated = paramNamesFromRoute(route.routePath)\n\n // Register each HTTP method (collision detection runs in a post-pass).\n for (const { method, alias } of aliases) {\n if (node.methods.has(method)) {\n ctx.warnings.push(\n `// WARNING: duplicate ${method} export at /${segments.join('/')} (route file declared twice). Keeping last definition.`,\n )\n }\n node.methods.set(method, alias)\n }\n}\n\n/**\n * EC-3: walk the tree post-build and resolve any node where a method's\n * lowercase name clashes with a child segment. HTTP method wins (more common);\n * the offending child gets a `_` suffix + a warning entry.\n */\nfunction resolveCollisions(node: TreeNode, pathPrefix: string, warnings: string[]): void {\n const methodLowerNames = new Set<string>()\n for (const method of node.methods.keys()) {\n methodLowerNames.add(method.toLowerCase())\n }\n const renames: { from: string; to: string }[] = []\n for (const childName of node.children.keys()) {\n if (methodLowerNames.has(childName)) {\n renames.push({ from: childName, to: `${childName}_` })\n }\n }\n for (const { from, to } of renames) {\n const old = node.children.get(from)\n if (old !== undefined) {\n node.children.delete(from)\n node.children.set(to, old)\n warnings.push(\n `WARNING (EC-3): path collision — method '${from}' clashed with sub-namespace at ${pathPrefix}/${from}; sub-namespace renamed to '${to}'.`,\n )\n }\n }\n // Recurse into all children (including the renamed ones).\n for (const [name, child] of node.children) {\n resolveCollisions(child, `${pathPrefix}/${name}`, warnings)\n }\n}\n\nfunction renderTreeNode(node: TreeNode, indent: string, _isRoot: boolean): string {\n // _isRoot is reserved for callers; current rendering is uniform across roots\n // and children (both open with `{`). Kept in the signature for future overrides.\n const lines: string[] = ['{']\n // Methods first\n for (const [method, alias] of node.methods) {\n const lower = method.toLowerCase()\n // Routes with path params (`:id`, `:name`) emit a `{ params: {...} }` object\n // intersected with TheoFetchOptions. Without the outer `{...}` wrap the\n // generated signature `(opts: params: {...} & ...)` is invalid TS syntax\n // (parser reads `params:` as a parameter label, then `{...}` as type — invalid).\n const paramsList = node.paramNamesAccumulated.map((p) => `${p}: string`).join('; ')\n const paramsField =\n node.paramNamesAccumulated.length > 0 ? `{ params: { ${paramsList} } } & ` : ''\n const optionalMark = node.paramNamesAccumulated.length === 0 ? '?' : ''\n lines.push(\n `${indent} ${lower}: (opts${optionalMark}: ${paramsField}import('theokit/client').TheoFetchOptions<typeof ${alias}>) => Promise<import('theokit/client').InferResponse<typeof ${alias}>>`,\n )\n }\n // Children\n for (const [name, child] of node.children) {\n const propKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n ? name\n : `'${name.replace(/'/g, \"\\\\'\")}'`\n lines.push(`${indent} ${propKey}: ${renderTreeNode(child, indent + ' ', false)}`)\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nexport interface GenerateClientDtsOptions {\n manifest: TheoManifest\n /** Absolute path to the .d.ts that will be emitted. Used to compute import paths. */\n dtsOutPath: string\n /** Absolute serverDir; manifest's filePath entries are relative to this. */\n serverDir: string\n}\n\nexport function generateClientDts({\n manifest,\n dtsOutPath,\n serverDir,\n}: GenerateClientDtsOptions): string {\n const ctx: BuildContext = {\n root: makeNode(),\n aliasCounter: { value: 0 },\n importLines: [],\n warnings: [],\n dtsOutPath,\n serverDir,\n }\n for (const route of manifest.routes) {\n buildOneRoute(route, ctx)\n }\n // EC-3: resolve collisions in a post-build pass (collisions materialize\n // when two routes are both processed, not during a single route's build).\n resolveCollisions(ctx.root, '', ctx.warnings)\n\n const importsBlock = ctx.importLines.length > 0 ? ctx.importLines.join('\\n') + '\\n\\n' : '\\n'\n const warningsBlock =\n ctx.warnings.length > 0 ? ' // ' + ctx.warnings.join('\\n // ') + '\\n\\n' : ''\n\n // Empty manifest (no routes with methods) → emit a stub interface so consumers\n // still get `client.X` autocomplete-friendly errors instead of \"Module not found\".\n const treeBody =\n ctx.root.children.size === 0 && ctx.root.methods.size === 0\n ? '{\\n // No routes detected — add `server/routes/*.ts` to populate this client.\\n }'\n : renderTreeNode(ctx.root, ' ', /* isRoot */ true)\n\n return `${FILE_HEADER}\ndeclare module '@theo/client' {\n${importsBlock}${warningsBlock} export interface AppClient ${treeBody}\n export const client: AppClient\n}\n`\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Phase 4 — Vite plugin\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AppTypedClientPluginOptions {\n /** Project root (cwd). */\n cwd: string\n /** Absolute path to the user's `server/` dir. */\n serverDir: string\n /** Absolute path to the `.theokit/` output dir (where `client.d.ts` lands). */\n distDir: string\n /**\n * Additional routes to merge into the typed client alongside file-system routes.\n * Each entry follows ManifestRoute shape (filePath, routePath, methods, paramNames).\n *\n * Used by `@theokit/http` `httpDecoratorsPlugin` to inject\n * decorator-defined routes — zero coupling between theokit core and the\n * decorator package (the walk happens in http-decorators; only data arrives here).\n */\n extraRoutes?: ManifestRoute[]\n}\n\nconst VIRTUAL_APP_CLIENT_ID = '@theo/client'\nconst RESOLVED_APP_CLIENT_ID = '\\0@theo/client'\n\n/** Atomic write via tmp + rename (EC-6). Skips when content is unchanged. */\nfunction writeFileIfChanged(filePath: string, content: string): { changed: boolean } {\n if (existsSync(filePath)) {\n try {\n const current = readFileSync(filePath, 'utf-8')\n if (current === content) return { changed: false }\n } catch {\n // fall through to write\n }\n }\n mkdirSync(dirname(filePath), { recursive: true })\n const tmp = `${filePath}.${process.pid}.tmp`\n writeFileSync(tmp, content)\n renameSync(tmp, filePath)\n return { changed: true }\n}\n\nfunction emitClientDts(opts: AppTypedClientPluginOptions): { changed: boolean; path: string } {\n if (!existsSync(opts.serverDir)) {\n // No server dir — emit empty stub to avoid \"module not found\".\n const stubPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n return {\n changed: writeFileIfChanged(\n stubPath,\n generateClientDts({\n manifest: {\n version: 1,\n generatedAt: new Date().toISOString(),\n routes: [],\n actions: [],\n websockets: [],\n },\n dtsOutPath: stubPath,\n serverDir: opts.serverDir,\n }),\n ).changed,\n path: stubPath,\n }\n }\n const manifest = generateManifest(opts.serverDir)\n\n // Decorator-client-bridge: merge extra routes (from @theokit/http\n // or any other source) into the manifest so generateClientDts() produces a\n // SINGLE typed client covering both file-system routes AND injected routes.\n if (opts.extraRoutes?.length) {\n manifest.routes.push(...opts.extraRoutes)\n }\n\n const dtsOutPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n const content = generateClientDts({\n manifest,\n dtsOutPath,\n serverDir: opts.serverDir,\n })\n const { changed } = writeFileIfChanged(dtsOutPath, content)\n return { changed, path: dtsOutPath }\n}\n\nexport function appTypedClientPlugin(opts: AppTypedClientPluginOptions): Plugin {\n let debounceTimer: NodeJS.Timeout | undefined\n let viteServer: ViteDevServer | undefined\n\n function scheduleEmit(): void {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n try {\n emitClientDts(opts)\n // Invalidate the virtual module so re-imports pick up potential new shape.\n if (viteServer) {\n const mod = viteServer.moduleGraph.getModuleById(RESOLVED_APP_CLIENT_ID)\n if (mod) viteServer.moduleGraph.invalidateModule(mod)\n }\n } catch (err) {\n // Surface but don't crash dev server.\n console.error('[theokit:app-typed-client] codegen error:', err)\n }\n }, 100)\n }\n\n return {\n name: 'theokit:app-typed-client',\n enforce: 'post',\n configResolved() {\n try {\n emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] initial codegen error:', err)\n }\n },\n buildEnd() {\n try {\n emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] buildEnd codegen error:', err)\n }\n },\n configureServer(server) {\n viteServer = server\n const routesGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'routes')\n const controllersGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'controllers')\n const onFile = (file: string): void => {\n const normalized = file.replace(/\\\\/g, '/')\n if (normalized.startsWith(routesGlob) || normalized.startsWith(controllersGlob))\n scheduleEmit()\n }\n server.watcher.on('add', onFile)\n server.watcher.on('change', onFile)\n server.watcher.on('unlink', onFile)\n },\n resolveId(id) {\n if (id === VIRTUAL_APP_CLIENT_ID) return RESOLVED_APP_CLIENT_ID\n return null\n },\n load(id) {\n if (id === RESOLVED_APP_CLIENT_ID) {\n return [\n `import { createAppClient } from 'theokit/client'`,\n `export const client = createAppClient()`,\n `export default client`,\n ``,\n ].join('\\n')\n }\n return null\n },\n }\n}\n\nexport { VIRTUAL_APP_CLIENT_ID, RESOLVED_APP_CLIENT_ID, emitClientDts }\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/app-typed-client.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Codegen-time fs ops: all paths derive from `opts.serverDir` / `opts.distDir`\n * (vite plugin opts — trusted, set by the framework's CLI). The plugin only\n * runs at dev/build time, never serves user HTTP input. No path traversal\n * surface here.\n */\n/**\n * Phase 2 + Phase 4 of G1 (g1-client-codegen-plan.md):\n *\n * 1. `generateClientDts(manifest, dtsOutPath)` — pure function emitting the\n * `.theokit/client.d.ts` shape (declared module `@theo/client`).\n * 2. `appTypedClientPlugin(opts)` — Vite plugin wrapper that calls (1),\n * writes the file atomically, and rewrites on route file changes.\n *\n * Implementation notes:\n * - Pure (1) is unit-testable without Vite or a filesystem (see G1-P2 tests).\n * - EC-2: kebab-case segments → camelCase; unsafe chars → bracket-access.\n * - EC-3: collision between HTTP method name and sub-segment name → method\n * wins; sub-segment ganha suffix `_` + warning comment.\n * - EC-6: write é `writeFileSync(tmp) + renameSync(tmp, final)` (POSIX atomic).\n * - EC-8: imports normalizam `\\\\` → `/`.\n */\n\nimport { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from 'node:fs'\nimport { dirname, isAbsolute, posix, relative, resolve } from 'node:path'\n\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport { HTTP_METHODS, type HttpMethod } from '../core/contracts/http-methods.js'\nimport { generateManifest, type ManifestRoute, type TheoManifest } from '../server/internal-api.js'\n\nconst FILE_HEADER = `// AUTO-GENERATED by theokit — do not edit.\n// Source: theokit/packages/theo/src/vite-plugin/app-typed-client.ts (G1 plan).\n// To refresh: \\`pnpm dev\\` or \\`pnpm build\\` regenerates this file on every route change.\n\n/* eslint-disable */\n`\n\ninterface TreeNode {\n /** sub-namespaces — keyed by segment name (already normalized). */\n children: Map<string, TreeNode>\n /** HTTP methods declared at this path → import alias. */\n methods: Map<HttpMethod, string>\n /** Whether any descendant declares dynamic params (for params type construction). */\n paramNamesAccumulated: string[]\n}\n\nfunction makeNode(): TreeNode {\n return { children: new Map(), methods: new Map(), paramNamesAccumulated: [] }\n}\n\n/**\n * Normalize a path segment to a valid TS identifier when possible.\n * EC-2: kebab-case → camelCase; pure dynamic (`:name`) → identifier `name`;\n * starts with digit or unsafe char → emit as bracket-access key.\n */\nfunction normalizeSegment(raw: string): { identifier: string; bracket: boolean } {\n // Dynamic segment: leading `:` (e.g. `:id` or `:...slug`).\n let s = raw\n if (s.startsWith(':...')) s = s.slice(4)\n else if (s.startsWith(':')) s = s.slice(1)\n\n // kebab → camel\n const camel = s.replace(/-([a-z0-9])/gi, (_, c: string) => c.toUpperCase())\n\n // Valid TS identifier (cannot start with digit, must be [a-zA-Z_$][a-zA-Z0-9_$]*)\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {\n return { identifier: camel, bracket: false }\n }\n // Fallback: bracket-access (keep raw form intact for JSON-string key)\n return { identifier: s, bracket: true }\n}\n\nfunction importPathFor(dtsOutPath: string, routeFileAbs: string): string {\n let rel = relative(dirname(dtsOutPath), routeFileAbs)\n // EC-8: normalize Windows path separators.\n rel = rel.replace(/\\\\/g, '/')\n // Strip extension (`.ts` / `.tsx`); TS module resolution likes extensionless.\n rel = rel.replace(/\\.[jt]sx?$/, '')\n if (!rel.startsWith('.') && !rel.startsWith('/')) {\n rel = './' + rel\n }\n return rel\n}\n\n/**\n * Convert a route path (e.g. `/api/users/:id/posts`) into the tree segments\n * we want under the `client` root. Strips the `/api/` prefix and splits.\n */\nfunction routePathSegments(routePath: string): string[] {\n let p = routePath\n if (p.startsWith('/api/')) p = p.slice(5)\n else if (p.startsWith('/api')) p = p.slice(4)\n if (p.startsWith('/')) p = p.slice(1)\n if (p === '') return []\n return p.split('/').filter(Boolean)\n}\n\nfunction paramNamesFromRoute(routePath: string): string[] {\n const out: string[] = []\n const re = /:(?:\\.\\.\\.)?([^/]+)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(routePath)) !== null) {\n out.push(m[1])\n }\n return out\n}\n\ninterface BuildContext {\n root: TreeNode\n /** Sequential index per route file for aliasing imports. */\n aliasCounter: { value: number }\n /** Lines emitted at the top of the declared module (one per route file). */\n importLines: string[]\n /** Diagnostic warnings rendered as block comments inside the declared module. */\n warnings: string[]\n dtsOutPath: string\n serverDir: string\n}\n\nfunction buildOneRoute(route: ManifestRoute, ctx: BuildContext): void {\n if (!route.methods || route.methods.length === 0) return\n // Build alias prefix once per route file (`_r0`, `_r1`, ...).\n const idx = ctx.aliasCounter.value++\n // Resolve absolute path for the route file (manifest stores it relative to serverDir).\n const absRouteFile = isAbsolute(route.filePath)\n ? route.filePath\n : resolve(ctx.serverDir, route.filePath)\n const importPath = importPathFor(ctx.dtsOutPath, absRouteFile)\n const aliases = route.methods\n .filter((m): m is HttpMethod => (HTTP_METHODS as readonly string[]).includes(m))\n .map((method) => ({ method, alias: `_r${idx}_${method}` }))\n if (aliases.length === 0) return\n const importClause = aliases.map((a) => `${a.method} as ${a.alias}`).join(', ')\n ctx.importLines.push(` import type { ${importClause} } from '${importPath}'`)\n\n // Walk segments + register methods at the leaf node.\n const segments = routePathSegments(route.routePath)\n let node = ctx.root\n for (const seg of segments) {\n const norm = normalizeSegment(seg)\n let child = node.children.get(norm.identifier)\n if (child === undefined) {\n child = makeNode()\n node.children.set(norm.identifier, child)\n }\n node = child\n }\n // Accumulated param names for typed params (e.g. `{ id: string }`).\n node.paramNamesAccumulated = paramNamesFromRoute(route.routePath)\n\n // Register each HTTP method (collision detection runs in a post-pass).\n for (const { method, alias } of aliases) {\n if (node.methods.has(method)) {\n ctx.warnings.push(\n `// WARNING: duplicate ${method} export at /${segments.join('/')} (route file declared twice). Keeping last definition.`,\n )\n }\n node.methods.set(method, alias)\n }\n}\n\n/**\n * EC-3: walk the tree post-build and resolve any node where a method's\n * lowercase name clashes with a child segment. HTTP method wins (more common);\n * the offending child gets a `_` suffix + a warning entry.\n */\nfunction resolveCollisions(node: TreeNode, pathPrefix: string, warnings: string[]): void {\n const methodLowerNames = new Set<string>()\n for (const method of node.methods.keys()) {\n methodLowerNames.add(method.toLowerCase())\n }\n const renames: { from: string; to: string }[] = []\n for (const childName of node.children.keys()) {\n if (methodLowerNames.has(childName)) {\n renames.push({ from: childName, to: `${childName}_` })\n }\n }\n for (const { from, to } of renames) {\n const old = node.children.get(from)\n if (old !== undefined) {\n node.children.delete(from)\n node.children.set(to, old)\n warnings.push(\n `WARNING (EC-3): path collision — method '${from}' clashed with sub-namespace at ${pathPrefix}/${from}; sub-namespace renamed to '${to}'.`,\n )\n }\n }\n // Recurse into all children (including the renamed ones).\n for (const [name, child] of node.children) {\n resolveCollisions(child, `${pathPrefix}/${name}`, warnings)\n }\n}\n\nfunction renderTreeNode(node: TreeNode, indent: string, _isRoot: boolean): string {\n // _isRoot is reserved for callers; current rendering is uniform across roots\n // and children (both open with `{`). Kept in the signature for future overrides.\n const lines: string[] = ['{']\n // Methods first\n for (const [method, alias] of node.methods) {\n const lower = method.toLowerCase()\n // Routes with path params (`:id`, `:name`) emit a `{ params: {...} }` object\n // intersected with TheoFetchOptions. Without the outer `{...}` wrap the\n // generated signature `(opts: params: {...} & ...)` is invalid TS syntax\n // (parser reads `params:` as a parameter label, then `{...}` as type — invalid).\n const paramsList = node.paramNamesAccumulated.map((p) => `${p}: string`).join('; ')\n const paramsField =\n node.paramNamesAccumulated.length > 0 ? `{ params: { ${paramsList} } } & ` : ''\n const optionalMark = node.paramNamesAccumulated.length === 0 ? '?' : ''\n lines.push(\n `${indent} ${lower}: (opts${optionalMark}: ${paramsField}import('theokit/client').TheoFetchOptions<typeof ${alias}>) => Promise<import('theokit/client').InferResponse<typeof ${alias}>>`,\n )\n }\n // Children\n for (const [name, child] of node.children) {\n const propKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n ? name\n : `'${name.replace(/'/g, \"\\\\'\")}'`\n lines.push(`${indent} ${propKey}: ${renderTreeNode(child, indent + ' ', false)}`)\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nexport interface GenerateClientDtsOptions {\n manifest: TheoManifest\n /** Absolute path to the .d.ts that will be emitted. Used to compute import paths. */\n dtsOutPath: string\n /** Absolute serverDir; manifest's filePath entries are relative to this. */\n serverDir: string\n}\n\nexport function generateClientDts({\n manifest,\n dtsOutPath,\n serverDir,\n}: GenerateClientDtsOptions): string {\n const ctx: BuildContext = {\n root: makeNode(),\n aliasCounter: { value: 0 },\n importLines: [],\n warnings: [],\n dtsOutPath,\n serverDir,\n }\n for (const route of manifest.routes) {\n buildOneRoute(route, ctx)\n }\n // EC-3: resolve collisions in a post-build pass (collisions materialize\n // when two routes are both processed, not during a single route's build).\n resolveCollisions(ctx.root, '', ctx.warnings)\n\n const importsBlock = ctx.importLines.length > 0 ? ctx.importLines.join('\\n') + '\\n\\n' : '\\n'\n const warningsBlock =\n ctx.warnings.length > 0 ? ' // ' + ctx.warnings.join('\\n // ') + '\\n\\n' : ''\n\n // Empty manifest (no routes with methods) → emit a stub interface so consumers\n // still get `client.X` autocomplete-friendly errors instead of \"Module not found\".\n const treeBody =\n ctx.root.children.size === 0 && ctx.root.methods.size === 0\n ? '{\\n // No routes detected — add `server/routes/*.ts` to populate this client.\\n }'\n : renderTreeNode(ctx.root, ' ', /* isRoot */ true)\n\n return `${FILE_HEADER}\ndeclare module '@theo/client' {\n${importsBlock}${warningsBlock} export interface AppClient ${treeBody}\n export const client: AppClient\n}\n`\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Phase 4 — Vite plugin\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AppTypedClientPluginOptions {\n /** Project root (cwd). */\n cwd: string\n /** Absolute path to the user's `server/` dir. */\n serverDir: string\n /** Absolute path to the `.theokit/` output dir (where `client.d.ts` lands). */\n distDir: string\n /**\n * Additional routes to merge into the typed client alongside file-system routes.\n * Each entry follows ManifestRoute shape (filePath, routePath, methods, paramNames).\n *\n * Used by `@theokit/http` `httpDecoratorsPlugin` to inject\n * decorator-defined routes — zero coupling between theokit core and the\n * decorator package (the walk happens in http-decorators; only data arrives here).\n */\n extraRoutes?: ManifestRoute[]\n}\n\nconst VIRTUAL_APP_CLIENT_ID = '@theo/client'\nconst RESOLVED_APP_CLIENT_ID = '\\0@theo/client'\n\n/** Atomic write via tmp + rename (EC-6). Skips when content is unchanged. */\nfunction writeFileIfChanged(filePath: string, content: string): { changed: boolean } {\n if (existsSync(filePath)) {\n try {\n const current = readFileSync(filePath, 'utf-8')\n if (current === content) return { changed: false }\n } catch {\n // fall through to write\n }\n }\n mkdirSync(dirname(filePath), { recursive: true })\n const tmp = `${filePath}.${process.pid}.tmp`\n writeFileSync(tmp, content)\n renameSync(tmp, filePath)\n return { changed: true }\n}\n\nfunction emitClientDts(opts: AppTypedClientPluginOptions): { changed: boolean; path: string } {\n if (!existsSync(opts.serverDir)) {\n // No server dir — emit empty stub to avoid \"module not found\".\n const stubPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n return {\n changed: writeFileIfChanged(\n stubPath,\n generateClientDts({\n manifest: {\n version: 1,\n generatedAt: new Date().toISOString(),\n routes: [],\n actions: [],\n websockets: [],\n },\n dtsOutPath: stubPath,\n serverDir: opts.serverDir,\n }),\n ).changed,\n path: stubPath,\n }\n }\n const manifest = generateManifest(opts.serverDir)\n\n // Decorator-client-bridge: merge extra routes (from @theokit/http\n // or any other source) into the manifest so generateClientDts() produces a\n // SINGLE typed client covering both file-system routes AND injected routes.\n if (opts.extraRoutes?.length) {\n manifest.routes.push(...opts.extraRoutes)\n }\n\n const dtsOutPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n const content = generateClientDts({\n manifest,\n dtsOutPath,\n serverDir: opts.serverDir,\n })\n const { changed } = writeFileIfChanged(dtsOutPath, content)\n return { changed, path: dtsOutPath }\n}\n\nexport function appTypedClientPlugin(opts: AppTypedClientPluginOptions): Plugin {\n let debounceTimer: NodeJS.Timeout | undefined\n let viteServer: ViteDevServer | undefined\n\n function scheduleEmit(): void {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n try {\n emitClientDts(opts)\n // Invalidate the virtual module so re-imports pick up potential new shape.\n if (viteServer) {\n const mod = viteServer.moduleGraph.getModuleById(RESOLVED_APP_CLIENT_ID)\n if (mod) viteServer.moduleGraph.invalidateModule(mod)\n }\n } catch (err) {\n // Surface but don't crash dev server.\n console.error('[theokit:app-typed-client] codegen error:', err)\n }\n }, 100)\n }\n\n return {\n name: 'theokit:app-typed-client',\n enforce: 'post',\n configResolved() {\n try {\n emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] initial codegen error:', err)\n }\n },\n buildEnd() {\n try {\n emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] buildEnd codegen error:', err)\n }\n },\n configureServer(server) {\n viteServer = server\n const routesGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'routes')\n const controllersGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'controllers')\n const onFile = (file: string): void => {\n const normalized = file.replace(/\\\\/g, '/')\n if (normalized.startsWith(routesGlob) || normalized.startsWith(controllersGlob))\n scheduleEmit()\n }\n server.watcher.on('add', onFile)\n server.watcher.on('change', onFile)\n server.watcher.on('unlink', onFile)\n },\n resolveId(id) {\n if (id === VIRTUAL_APP_CLIENT_ID) return RESOLVED_APP_CLIENT_ID\n return null\n },\n load(id) {\n if (id === RESOLVED_APP_CLIENT_ID) {\n return [\n `import { createAppClient } from 'theokit/client'`,\n `export const client = createAppClient()`,\n `export default client`,\n ``,\n ].join('\\n')\n }\n return null\n },\n }\n}\n\nexport { VIRTUAL_APP_CLIENT_ID, RESOLVED_APP_CLIENT_ID, emitClientDts }\n"],"mappings":"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","names":[]}
|
package/dist/boot/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
serveReservedRoute
|
|
3
|
+
} from "../chunk-V3LJN2GS.js";
|
|
1
4
|
import {
|
|
2
5
|
envelopeCodeToStatus
|
|
3
6
|
} from "../chunk-X32XEJXR.js";
|
|
@@ -5,9 +8,6 @@ import {
|
|
|
5
8
|
NotFoundError,
|
|
6
9
|
serverErrorToEnvelope
|
|
7
10
|
} from "../chunk-UVXB2ER7.js";
|
|
8
|
-
import {
|
|
9
|
-
serveReservedRoute
|
|
10
|
-
} from "../chunk-V3LJN2GS.js";
|
|
11
11
|
import "../chunk-DGUM43GV.js";
|
|
12
12
|
|
|
13
13
|
// src/server/boot.ts
|
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
} from "./chunk-JAIKGP3Q.js";
|
|
13
13
|
import {
|
|
14
14
|
importUserModule,
|
|
15
|
-
loadConfig
|
|
15
|
+
loadConfig
|
|
16
|
+
} from "./chunk-567NA7Y6.js";
|
|
17
|
+
import {
|
|
16
18
|
loadEnv
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-GDN3PXFH.js";
|
|
18
20
|
import "./chunk-YJAUJXZS.js";
|
|
19
21
|
import {
|
|
20
22
|
buildManifest,
|
|
@@ -23,10 +25,12 @@ import {
|
|
|
23
25
|
import {
|
|
24
26
|
generateManifest,
|
|
25
27
|
writeManifest as writeManifest2
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-NXTF5PPW.js";
|
|
29
|
+
import "./chunk-34YQOXGM.js";
|
|
30
|
+
import "./chunk-GBXLKYIA.js";
|
|
27
31
|
import {
|
|
28
32
|
walkSourceFiles
|
|
29
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-P37RZRFV.js";
|
|
30
34
|
|
|
31
35
|
// src/cli/commands/build.ts
|
|
32
36
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -527,7 +531,7 @@ async function buildCommand(options) {
|
|
|
527
531
|
`);
|
|
528
532
|
}
|
|
529
533
|
async function runAdapterBuild(target, config, cwd) {
|
|
530
|
-
const { theoPluginAsync } = await import("./vite-plugin-
|
|
534
|
+
const { theoPluginAsync } = await import("./vite-plugin-GC6WCU4P.js");
|
|
531
535
|
const { default: react } = await import("@vitejs/plugin-react");
|
|
532
536
|
const ctx = {
|
|
533
537
|
// `react()` may return Plugin or Plugin[] depending on version; spread the
|
|
@@ -535,7 +539,7 @@ async function runAdapterBuild(target, config, cwd) {
|
|
|
535
539
|
// type updated to `Plugin[] | Promise<Plugin[]>`).
|
|
536
540
|
makeVitePlugins: async (opts) => [react(), ...await theoPluginAsync(opts)].flat()
|
|
537
541
|
};
|
|
538
|
-
const { resolveAdapter } = await import("./registry-
|
|
542
|
+
const { resolveAdapter } = await import("./registry-XJUYD2OU.js");
|
|
539
543
|
const adapter = await resolveAdapter(target);
|
|
540
544
|
await adapter.build(config, cwd, ctx);
|
|
541
545
|
}
|
|
@@ -602,4 +606,4 @@ function relativize3(absPath, root) {
|
|
|
602
606
|
export {
|
|
603
607
|
buildCommand
|
|
604
608
|
};
|
|
605
|
-
//# sourceMappingURL=build-
|
|
609
|
+
//# sourceMappingURL=build-QDAFSKKW.js.map
|