theokit 0.2.0 → 0.2.2
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-E6PU47GV.js +175 -0
- package/dist/actions-virtual-module-E6PU47GV.js.map +1 -0
- package/dist/actions-virtual-module-E7IELVHL.js +174 -0
- package/dist/actions-virtual-module-E7IELVHL.js.map +1 -0
- package/dist/app-typed-client-RZMVWHJ3.js +289 -0
- package/dist/app-typed-client-RZMVWHJ3.js.map +1 -0
- package/dist/app-typed-client-YIBP6JWH.js +289 -0
- package/dist/app-typed-client-YIBP6JWH.js.map +1 -0
- package/dist/{aws-lambda-PONVF3UX.js → aws-lambda-XYCGZH3I.js} +2 -2
- package/dist/{build-IQQYCA3B.js → build-NCGIV4LL.js} +41 -14
- package/dist/build-NCGIV4LL.js.map +1 -0
- package/dist/build-request-body-preview-EOP2Y4F3.js +52 -0
- package/dist/build-request-body-preview-EOP2Y4F3.js.map +1 -0
- package/dist/build-request-body-preview-II2235E6.js +54 -0
- package/dist/build-request-body-preview-II2235E6.js.map +1 -0
- package/dist/{bun-DITMZMKL.js → bun-K73TQEWC.js} +2 -2
- package/dist/{chunk-GQSTRXXB.js → chunk-2BQYEBCK.js} +77 -68
- package/dist/chunk-2BQYEBCK.js.map +1 -0
- package/dist/chunk-7DHGV77J.js +140 -0
- package/dist/chunk-7DHGV77J.js.map +1 -0
- package/dist/{chunk-WQCPKA2L.js → chunk-C27AZN46.js} +570 -165
- package/dist/chunk-C27AZN46.js.map +1 -0
- package/dist/chunk-ERAZNHHR.js +73 -0
- package/dist/chunk-ERAZNHHR.js.map +1 -0
- package/dist/{chunk-ZK5JCE5J.js → chunk-EXJWO2HB.js} +201 -43
- package/dist/chunk-EXJWO2HB.js.map +1 -0
- package/dist/chunk-F3TG5FJV.js +97 -0
- package/dist/chunk-F3TG5FJV.js.map +1 -0
- package/dist/{chunk-OK2PJOLK.js → chunk-KJVEJILQ.js} +7 -3
- package/dist/chunk-KJVEJILQ.js.map +1 -0
- package/dist/{chunk-JBC7PFV4.js → chunk-M23FP2AS.js} +2 -2
- package/dist/chunk-M23FP2AS.js.map +1 -0
- package/dist/{chunk-FRBYJANT.js → chunk-M6MYZSD2.js} +195 -41
- package/dist/chunk-M6MYZSD2.js.map +1 -0
- package/dist/{chunk-S3JBIFKS.js → chunk-MG7H3LAW.js} +2 -2
- package/dist/chunk-MOT42NAH.js +165 -0
- package/dist/chunk-MOT42NAH.js.map +1 -0
- package/dist/{chunk-OEB3OIYP.js → chunk-OJHO2X7C.js} +497 -168
- package/dist/chunk-OJHO2X7C.js.map +1 -0
- package/dist/chunk-QLGES2HC.js +189 -0
- package/dist/chunk-QLGES2HC.js.map +1 -0
- package/dist/{chunk-JZ5WEG3S.js → chunk-SEVYARIU.js} +9 -5
- package/dist/chunk-SEVYARIU.js.map +1 -0
- package/dist/chunk-WSJKACWB.js +17 -0
- package/dist/chunk-WSJKACWB.js.map +1 -0
- package/dist/{chunk-3Q36SEDK.js → chunk-ZBWROIIO.js} +43 -2
- package/dist/{chunk-3Q36SEDK.js.map → chunk-ZBWROIIO.js.map} +1 -1
- package/dist/{chunk-Y5KE6YFP.js → chunk-ZJW5FFYJ.js} +3 -3
- package/dist/chunk-ZJW5FFYJ.js.map +1 -0
- package/dist/chunk-ZOXNJV2O.js +358 -0
- package/dist/chunk-ZOXNJV2O.js.map +1 -0
- package/dist/cli/index.js +22 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +31 -1
- package/dist/client/index.js +100 -0
- package/dist/client/index.js.map +1 -1
- package/dist/{cloudflare-RBANU3SE.js → cloudflare-LCAOTRYZ.js} +2 -2
- package/dist/{deno-deploy-J7LI6HNL.js → deno-deploy-ZN4RE5HF.js} +2 -2
- package/dist/{dev-VN6WS3CM.js → dev-WDXC74CK.js} +15 -7
- package/dist/dev-WDXC74CK.js.map +1 -0
- package/dist/dev-emit-3CHM73TV.js +390 -0
- package/dist/dev-emit-3CHM73TV.js.map +1 -0
- package/dist/dev-emit-I3CKOHML.js +44 -0
- package/dist/dev-emit-I3CKOHML.js.map +1 -0
- package/dist/devtools/entry.js +545 -181
- package/dist/devtools/entry.js.map +1 -1
- package/dist/{dispatcher-IK3FVMX5.js → dispatcher-AQJGI3HU.js} +2 -2
- package/dist/dispatcher-E6QV32BO.js +83 -0
- package/dist/dispatcher-E6QV32BO.js.map +1 -0
- package/dist/{dist-Q5VBFWVL.js → dist-R2Q7GVDW.js} +10357 -12068
- package/dist/dist-R2Q7GVDW.js.map +1 -0
- package/dist/{generate-SIO4JSKJ.js → generate-DT4IIAHF.js} +88 -22
- package/dist/generate-DT4IIAHF.js.map +1 -0
- package/dist/index.d.ts +108 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/{info-B3UOXFUF.js → info-VM7EK4JC.js} +3 -3
- package/dist/{netlify-IFY6ZGFV.js → netlify-5VQ22Z2P.js} +2 -2
- package/dist/{node-KWZI3CKV.js → node-3APTLGWB.js} +2 -2
- package/dist/openapi-CBASKC7W.js +84 -0
- package/dist/openapi-CBASKC7W.js.map +1 -0
- package/dist/registry-5QFTLOL2.js +25 -0
- package/dist/{routes-CAGFZKUK.js → routes-7O4NUDYS.js} +9 -7
- package/dist/{routes-CAGFZKUK.js.map → routes-7O4NUDYS.js.map} +1 -1
- package/dist/{schema-Cxq2qqwh.d.ts → schema-CjVly9c_.d.ts} +43 -1
- package/dist/{schema-MVB2FVK6.js → schema-JAKF23GR.js} +2 -2
- package/dist/server/cost/index.js +1 -1
- package/dist/server/index.d.ts +219 -47
- package/dist/server/index.js +35 -116
- package/dist/server/index.js.map +1 -1
- package/dist/{start-ZGWW5MOL.js → start-5JTFWF4T.js} +20 -14
- package/dist/start-5JTFWF4T.js.map +1 -0
- package/dist/{static-BH6JQNM2.js → static-TZBVBDTB.js} +4 -4
- package/dist/{upgrade-readiness-OHL36FQL.js → upgrade-readiness-Q46KTG2J.js} +6 -1
- package/dist/upgrade-readiness-Q46KTG2J.js.map +1 -0
- package/dist/{vercel-RHADE5IP.js → vercel-MWW24ABC.js} +2 -2
- package/dist/vite-plugin/index.d.ts +25 -1
- package/dist/vite-plugin/index.js +5 -2
- package/dist/{vite-plugin-WA3G45YY.js → vite-plugin-4WZ3C2AC.js} +7 -6
- package/package.json +4 -2
- package/dist/build-IQQYCA3B.js.map +0 -1
- package/dist/chunk-FRBYJANT.js.map +0 -1
- package/dist/chunk-GQSTRXXB.js.map +0 -1
- package/dist/chunk-JBC7PFV4.js.map +0 -1
- package/dist/chunk-JZ5WEG3S.js.map +0 -1
- package/dist/chunk-OEB3OIYP.js.map +0 -1
- package/dist/chunk-OK2PJOLK.js.map +0 -1
- package/dist/chunk-WQCPKA2L.js.map +0 -1
- package/dist/chunk-Y5KE6YFP.js.map +0 -1
- package/dist/chunk-ZK5JCE5J.js.map +0 -1
- package/dist/dev-VN6WS3CM.js.map +0 -1
- package/dist/dist-Q5VBFWVL.js.map +0 -1
- package/dist/generate-SIO4JSKJ.js.map +0 -1
- package/dist/registry-BB3UYAOG.js +0 -25
- package/dist/start-ZGWW5MOL.js.map +0 -1
- package/dist/upgrade-readiness-OHL36FQL.js.map +0 -1
- /package/dist/{aws-lambda-PONVF3UX.js.map → aws-lambda-XYCGZH3I.js.map} +0 -0
- /package/dist/{bun-DITMZMKL.js.map → bun-K73TQEWC.js.map} +0 -0
- /package/dist/{chunk-S3JBIFKS.js.map → chunk-MG7H3LAW.js.map} +0 -0
- /package/dist/{cloudflare-RBANU3SE.js.map → cloudflare-LCAOTRYZ.js.map} +0 -0
- /package/dist/{deno-deploy-J7LI6HNL.js.map → deno-deploy-ZN4RE5HF.js.map} +0 -0
- /package/dist/{dispatcher-IK3FVMX5.js.map → dispatcher-AQJGI3HU.js.map} +0 -0
- /package/dist/{info-B3UOXFUF.js.map → info-VM7EK4JC.js.map} +0 -0
- /package/dist/{netlify-IFY6ZGFV.js.map → netlify-5VQ22Z2P.js.map} +0 -0
- /package/dist/{node-KWZI3CKV.js.map → node-3APTLGWB.js.map} +0 -0
- /package/dist/{registry-BB3UYAOG.js.map → registry-5QFTLOL2.js.map} +0 -0
- /package/dist/{schema-MVB2FVK6.js.map → schema-JAKF23GR.js.map} +0 -0
- /package/dist/{static-BH6JQNM2.js.map → static-TZBVBDTB.js.map} +0 -0
- /package/dist/{vercel-RHADE5IP.js.map → vercel-MWW24ABC.js.map} +0 -0
- /package/dist/{vite-plugin-WA3G45YY.js.map → vite-plugin-4WZ3C2AC.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/preflight-node-version.ts"],"sourcesContent":["/**\n * Node version + native-binding ABI preflight for `theokit dev` / `theokit build` / `theokit start`.\n *\n * Why this exists:\n * In multi-Node-version setups (developer workflow: Node 22 for one\n * project, Node 20 for another, nvm switching frequently), the\n * `nohup env ...` invocation pattern strips the nvm PATH prefix and\n * the dev server launches under whichever `node` binary is FIRST in\n * PATH at spawn time — NOT the version the calling shell reported.\n *\n * When `theokit dev` boots under the wrong Node, every native binding\n * in `node_modules` (`better-sqlite3`, optionally `@lancedb/lancedb`,\n * etc.) fails with NODE_MODULE_VERSION mismatch on first use. The\n * error message references a low-level binary path; the dev wastes\n * time investigating a process-management issue.\n *\n * This preflight runs UPFRONT (before any module that touches native\n * bindings loads) and emits an actionable error referencing nvm + the\n * pinned `.nvmrc` floor.\n *\n * What it does:\n * 1. Reads `process.versions.node` + `process.versions.modules` (ABI).\n * 2. If Node major < 22, refuse to boot with a nvm install/use hint.\n * 3. If at least one native binding's compiled ABI != current ABI,\n * refuse to boot with a `pnpm rebuild <pkg>` hint.\n *\n * What it does NOT do:\n * * Auto-rebuild — that's the SDK's preflight (vitest setup, sentinel\n * cached). Auto-rebuild in a dev CLI is risky (slow, can run during\n * `pnpm dev` watch loops).\n * * Probe every native dep — only checks the ones the framework KNOWS\n * are required (currently `better-sqlite3` because of the cost\n * ledger + cron persistence + memory index DB).\n *\n * @internal\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\n\nconst NODE_MIN_MAJOR = 22\nconst NODE_MIN_FULL = '22.12.0'\n\ninterface BindingProbe {\n pkg: string\n /** path under <cwd>/node_modules — used only for the diagnostic message */\n hintPath?: string\n required: boolean\n}\n\nconst BINDINGS_TO_CHECK: readonly BindingProbe[] = [\n // better-sqlite3 is the only HARD-required native dep at framework boot\n // (cost ledger + cron persistence + memory index). Other native deps\n // (@lancedb/lancedb) are opt-in peers — preflight skips them when absent.\n { pkg: 'better-sqlite3', required: true },\n { pkg: '@lancedb/lancedb', required: false },\n]\n\nfunction checkNodeFloor(): { ok: true } | { ok: false; reason: string } {\n const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10)\n if (major >= NODE_MIN_MAJOR) return { ok: true }\n return {\n ok: false,\n reason:\n `theokit requires Node >= ${NODE_MIN_FULL} (you are running v${process.versions.node}).\\n\\n` +\n 'Quick fix:\\n' +\n ' nvm use 22 # switch to a Node 22.x already installed\\n' +\n \" nvm install 22.12 # install + switch if you don't have it\\n\\n\" +\n 'Why this matters:\\n' +\n ' theokit ships AsyncLocalStorage + native fetch + structured WHATWG\\n' +\n ' Streams optimizations that depend on Node 22 internals. Older Node\\n' +\n ' versions silently miscompile SSR streaming and break native\\n' +\n ' bindings (NODE_MODULE_VERSION mismatch).\\n\\n' +\n 'Production tip: pin your CI runner to `actions/setup-node` v4 with\\n' +\n \" `node-version-file: .nvmrc` — that's what `create-theokit` writes.\",\n }\n}\n\nfunction checkBindingAbi(cwd: string): { ok: true } | { ok: false; reason: string } {\n const offenders: string[] = []\n // Build createRequire from the cwd so we resolve the consumer's deps,\n // NOT theokit's own node_modules.\n const cwdRequire = createRequire(join(cwd, 'package.json'))\n for (const probe of BINDINGS_TO_CHECK) {\n try {\n cwdRequire.resolve(probe.pkg)\n } catch {\n // not installed — required vs optional gates the behaviour\n if (probe.required) {\n offenders.push(`${probe.pkg} (required dep is NOT installed — run \\`pnpm install\\`)`)\n }\n continue\n }\n // Try a dry require — better-sqlite3 dlopens at load-time, so the ABI\n // mismatch surfaces here as a sync throw.\n try {\n cwdRequire(probe.pkg)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (/NODE_MODULE_VERSION|was compiled against|did not self-register/.test(msg)) {\n offenders.push(`${probe.pkg} (compiled against different Node ABI)`)\n } else if (probe.required) {\n // surface non-ABI errors verbatim for required deps\n offenders.push(`${probe.pkg} (require failed: ${msg.split('\\n')[0]})`)\n }\n }\n }\n if (offenders.length === 0) return { ok: true }\n return {\n ok: false,\n reason:\n `Native binding ABI mismatch detected (Node v${process.versions.node}, ABI ${process.versions.modules}).\\n\\n` +\n 'Offending packages:\\n' +\n offenders.map((o) => ` - ${o}`).join('\\n') +\n '\\n\\nQuick fix:\\n' +\n ' pnpm rebuild # rebuild ALL native bindings for current Node\\n' +\n ' pnpm rebuild better-sqlite3 # rebuild just one\\n\\n' +\n 'Why this happened:\\n' +\n ' Your `theokit dev` (or build/start) launched under a different\\n' +\n ' Node version than the one used to install/build node_modules.\\n' +\n ' Common cause: `nohup env ...` strips the nvm PATH prefix, so the\\n' +\n ' dev server picked the system `node` instead of `nvm use 22`.\\n\\n' +\n 'Permanent fix:\\n' +\n ' Prefix your dev/start command with the absolute Node 22 path:\\n' +\n ' PATH=\"$(nvm which 22 | xargs dirname):$PATH\" theokit dev\\n' +\n ' Or use direnv with `.envrc` containing `use node 22.12.0`.',\n }\n}\n\n/**\n * Read `.nvmrc` from cwd if present — used only to enrich error messages\n * with the project's pinned floor. Falls back to the framework default.\n *\n * @internal\n */\nfunction readNvmrcFloor(cwd: string): string | undefined {\n const path = join(cwd, '.nvmrc')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI reads consumer's own .nvmrc; consumer owns cwd\n if (!existsSync(path)) return undefined\n try {\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- same\n return readFileSync(path, 'utf8').trim()\n } catch {\n return undefined\n }\n}\n\n/**\n * Run the Node + ABI preflight. Throws a descriptive Error on mismatch\n * BEFORE any module that imports better-sqlite3 loads. The error message\n * is the only user-facing artefact — caller can rely on `error.message`\n * being a full actionable diagnostic.\n *\n * @throws Error with multi-line actionable message when Node version or\n * native binding ABI is wrong.\n * @internal\n */\nexport function preflightNodeAndBindings(cwd: string): void {\n const nodeCheck = checkNodeFloor()\n if (!nodeCheck.ok) {\n const pinned = readNvmrcFloor(cwd)\n const suffix = pinned !== undefined ? `\\n\\nYour project pins Node ${pinned} in .nvmrc.` : ''\n throw new Error(`[theokit preflight] ${nodeCheck.reason}${suffix}`)\n }\n const abiCheck = checkBindingAbi(cwd)\n if (!abiCheck.ok) {\n throw new Error(`[theokit preflight] ${abiCheck.reason}`)\n }\n}\n"],"mappings":";;;;AAqCA,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAStB,IAAM,oBAA6C;AAAA;AAAA;AAAA;AAAA,EAIjD,EAAE,KAAK,kBAAkB,UAAU,KAAK;AAAA,EACxC,EAAE,KAAK,oBAAoB,UAAU,MAAM;AAC7C;AAEA,SAAS,iBAA+D;AACtE,QAAM,QAAQ,OAAO,SAAS,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,EAAE;AAC5E,MAAI,SAAS,eAAgB,QAAO,EAAE,IAAI,KAAK;AAC/C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,4BAA4B,aAAa,sBAAsB,QAAQ,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxF;AACF;AAEA,SAAS,gBAAgB,KAA2D;AAClF,QAAM,YAAsB,CAAC;AAG7B,QAAM,aAAa,cAAc,KAAK,KAAK,cAAc,CAAC;AAC1D,aAAW,SAAS,mBAAmB;AACrC,QAAI;AACF,iBAAW,QAAQ,MAAM,GAAG;AAAA,IAC9B,QAAQ;AAEN,UAAI,MAAM,UAAU;AAClB,kBAAU,KAAK,GAAG,MAAM,GAAG,8DAAyD;AAAA,MACtF;AACA;AAAA,IACF;AAGA,QAAI;AACF,iBAAW,MAAM,GAAG;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAI,iEAAiE,KAAK,GAAG,GAAG;AAC9E,kBAAU,KAAK,GAAG,MAAM,GAAG,wCAAwC;AAAA,MACrE,WAAW,MAAM,UAAU;AAEzB,kBAAU,KAAK,GAAG,MAAM,GAAG,qBAAqB,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,IAAI,KAAK;AAC9C,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QACE,+CAA+C,QAAQ,SAAS,IAAI,SAAS,QAAQ,SAAS,OAAO;AAAA;AAAA;AAAA,IAErG,UAAU,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,IAC1C;AAAA,EAYJ;AACF;AAQA,SAAS,eAAe,KAAiC;AACvD,QAAM,OAAO,KAAK,KAAK,QAAQ;AAE/B,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AAEF,WAAO,aAAa,MAAM,MAAM,EAAE,KAAK;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,yBAAyB,KAAmB;AAC1D,QAAM,YAAY,eAAe;AACjC,MAAI,CAAC,UAAU,IAAI;AACjB,UAAM,SAAS,eAAe,GAAG;AACjC,UAAM,SAAS,WAAW,SAAY;AAAA;AAAA,yBAA8B,MAAM,gBAAgB;AAC1F,UAAM,IAAI,MAAM,uBAAuB,UAAU,MAAM,GAAG,MAAM,EAAE;AAAA,EACpE;AACA,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,EAC1D;AACF;","names":[]}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// src/devtools/shared.ts
|
|
2
2
|
var RING_BUFFER_CAP = 50;
|
|
3
3
|
var MAX_QUEUE_SIZE = 100;
|
|
4
|
-
var STORAGE_VERSION =
|
|
4
|
+
var STORAGE_VERSION = 2;
|
|
5
5
|
var initialState = {
|
|
6
6
|
open: false,
|
|
7
7
|
visible: true,
|
|
8
8
|
position: "bottom-right",
|
|
9
|
-
theme: "
|
|
9
|
+
theme: "dark",
|
|
10
10
|
activeTab: "requests",
|
|
11
11
|
requests: [],
|
|
12
12
|
errors: [],
|
|
13
13
|
agentRuns: [],
|
|
14
|
+
actionCalls: [],
|
|
14
15
|
routeManifest: null,
|
|
15
16
|
activeRoutePath: null,
|
|
16
17
|
activeChain: []
|
|
@@ -70,6 +71,9 @@ var dispatcher = {
|
|
|
70
71
|
onAgentRun: queuable((d, run) => {
|
|
71
72
|
d({ type: "AGENT_RUN_ADD", run });
|
|
72
73
|
}),
|
|
74
|
+
onActionCall: queuable((d, record) => {
|
|
75
|
+
d({ type: "ACTION_CALL_ADD", record });
|
|
76
|
+
}),
|
|
73
77
|
/**
|
|
74
78
|
* Wire React's dispatch function in. Idempotent (EC-24): only the
|
|
75
79
|
* NULL → non-null transition flushes the queue; subsequent non-null
|
|
@@ -102,4 +106,4 @@ export {
|
|
|
102
106
|
CHANNEL_ROUTE_MATCHED,
|
|
103
107
|
dispatcher
|
|
104
108
|
};
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-KJVEJILQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/devtools/shared.ts","../src/devtools/dispatcher.ts"],"sourcesContent":["/**\n * Devtools — shared types.\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\n\nexport type DevtoolsPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\nexport type DevtoolsTab =\n | 'requests'\n | 'routes'\n | 'agents'\n | 'actions'\n | 'errors'\n | 'csrf-readiness'\n | 'settings'\nexport type DevtoolsTheme = 'light' | 'dark' | 'system'\n\nexport interface RequestRecord {\n id: string\n traceId: string\n method: string\n path: string\n status: number\n durationMs: number\n startedAt: number\n csrfWarn?: { code: string; docsUrl: string }\n headers?: Record<string, string>\n bodyPreview?: string\n bodyLength?: number\n bodyTruncated?: boolean\n}\n\nexport interface ErrorRecord {\n id: string\n type: 'console' | 'unhandled' | 'csrf.warn'\n message: string\n stack?: string\n code?: string\n docsUrl?: string\n timestamp: number\n}\n\nexport interface CsrfWarnPayload {\n event: 'csrf.warn'\n code: string\n docsUrl: string\n method: string\n path: string\n reason: string\n}\n\nexport interface RouteInfo {\n path: string\n absoluteFilePath: string\n layoutChain: string[]\n hasLoading: boolean\n hasError: boolean\n hasNotFound: boolean\n}\n\nexport interface RouteManifest {\n routes: RouteInfo[]\n}\n\n/**\n * Per-agent-run telemetry surfaced to the Agents devtools tab.\n *\n * Emitted by `trackAgentRun` (server-side) via the dispatcher in dev mode;\n * prod tree-shakes the entire wire (v1.1 EC-4 `__IS_DEV` IIFE guard).\n * Type lives here (devtools/shared) since both producer (server/cost) and\n * consumer (AgentsTab) need the shape; following architecture v3 ADR-0001\n * rule \"shared types in core/contracts\" is the canonical home — for this\n * one we keep it in devtools/shared to minimize churn in core/contracts/.\n */\nexport interface AgentRunRecord {\n id: string\n timestamp: number\n userId: string\n model: string\n tokensInput: number\n tokensOutput: number\n costUsd: number\n status: 'finished' | 'error' | 'aborted'\n}\n\n/**\n * T5.1 — Per-action-call telemetry surfaced to the Actions devtools tab.\n *\n * Emitted by `action-execute.ts` (server-side) via the dispatcher in dev\n * mode; prod tree-shakes the wire (mirrors AgentRunRecord pattern).\n * Field-level error pins consume `error.fields` directly when status='error'\n * (per plan ADR D6).\n */\nexport interface ActionCallRecord {\n id: string\n timestamp: number\n name: string\n input: unknown\n output?: unknown\n error?: {\n code: string\n message: string\n fields?: Record<string, string[]>\n issues?: unknown[]\n }\n durationMs: number\n status: 'success' | 'error'\n}\n\nexport interface DevtoolsState {\n open: boolean\n visible: boolean\n position: DevtoolsPosition\n theme: DevtoolsTheme\n activeTab: DevtoolsTab\n requests: RequestRecord[]\n errors: ErrorRecord[]\n agentRuns: AgentRunRecord[]\n actionCalls: ActionCallRecord[]\n routeManifest: RouteManifest | null\n activeRoutePath: string | null\n activeChain: string[]\n}\n\nexport type DevtoolsAction =\n | { type: 'TOGGLE_PANEL' }\n | { type: 'TOGGLE_VISIBLE' }\n | { type: 'SET_TAB'; tab: DevtoolsTab }\n | { type: 'SET_POSITION'; position: DevtoolsPosition }\n | { type: 'SET_THEME'; theme: DevtoolsTheme }\n | { type: 'REQUEST_ADD'; request: RequestRecord }\n | { type: 'ERROR_ADD'; error: ErrorRecord }\n | { type: 'CSRF_WARN'; payload: CsrfWarnPayload }\n | { type: 'MANIFEST_UPDATED'; manifest: RouteManifest }\n | { type: 'ROUTE_MATCHED'; path: string; chain: string[] }\n | { type: 'AGENT_RUN_ADD'; run: AgentRunRecord }\n | { type: 'ACTION_CALL_ADD'; record: ActionCallRecord }\n | { type: 'RESET_REQUESTS' }\n | { type: 'RESET_ERRORS' }\n | { type: 'RESET_AGENT_RUNS' }\n | { type: 'RESET_ACTION_CALLS' }\n\nexport const RING_BUFFER_CAP = 50\nexport const MAX_QUEUE_SIZE = 100\nexport const STORAGE_VERSION = 2\n\nexport const initialState: DevtoolsState = {\n open: false,\n visible: true,\n position: 'bottom-right',\n theme: 'dark',\n activeTab: 'requests',\n requests: [],\n errors: [],\n agentRuns: [],\n actionCalls: [],\n routeManifest: null,\n activeRoutePath: null,\n activeChain: [],\n}\n\nexport const CHANNEL_REQUEST = 'theo:devtools:request' as const\nexport const CHANNEL_ERROR = 'theo:devtools:error' as const\nexport const CHANNEL_CSRF_WARN = 'theo:devtools:csrf.warn' as const\nexport const CHANNEL_MANIFEST = 'theo:devtools:manifest' as const\nexport const CHANNEL_ROUTE_MATCHED = 'theo:devtools:route-matched' as const\nexport const CHANNEL_AGENT_RUN = 'theo:devtools:agent.run' as const\nexport const CHANNEL_ACTION_CALL = 'theo:devtools:action.call' as const\n","/**\n * T2.1 — Devtools dispatcher with pre-mount event queue.\n *\n * Pattern: events emitted from anywhere (server WS, browser console.error,\n * dispatcher.onCsrfWarn) can fire BEFORE the React tree mounts. Each call\n * either dispatches immediately (when `setDispatch` registered a dispatch\n * fn) or queues until React is ready, then flushes on first mount.\n *\n * EC-23: queue capped at MAX_QUEUE_SIZE (FIFO eviction).\n * EC-24: setDispatch is idempotent — flush only on NULL → non-null\n * transition (StrictMode re-mount safe).\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\nimport {\n type ActionCallRecord,\n type AgentRunRecord,\n type CsrfWarnPayload,\n type DevtoolsAction,\n type ErrorRecord,\n MAX_QUEUE_SIZE,\n type RequestRecord,\n type RouteManifest,\n} from './shared.js'\n\ntype Dispatch = (action: DevtoolsAction) => void\ntype QueuedItem = (d: Dispatch) => void\n\nlet _dispatch: Dispatch | null = null\nconst _queue: QueuedItem[] = []\n\nfunction queuable<Args extends unknown[]>(\n fn: (d: Dispatch, ...args: Args) => void,\n): (...args: Args) => void {\n return (...args: Args) => {\n if (_dispatch) {\n try {\n fn(_dispatch, ...args)\n } catch (err) {\n // EC-25: an error inside reducer-bound dispatch path MUST NOT bubble\n // to the original event source (e.g. logger / HMR callback / global\n // error handler). Log and continue.\n\n console.error('[theo devtools] dispatch failed', err)\n }\n } else {\n // EC-23: cap queue, FIFO-evict if full\n if (_queue.length >= MAX_QUEUE_SIZE) _queue.shift()\n _queue.push((d) => {\n fn(d, ...args)\n })\n }\n }\n}\n\nfunction flushQueue(): void {\n while (_queue.length) {\n const fn = _queue.shift()\n if (!fn || !_dispatch) break\n try {\n fn(_dispatch)\n } catch (err) {\n console.error('[theo devtools] queued event failed', err)\n }\n }\n}\n\nexport const dispatcher = {\n onRequest: queuable((d: Dispatch, req: RequestRecord) => {\n d({ type: 'REQUEST_ADD', request: req })\n }),\n onError: queuable((d: Dispatch, err: ErrorRecord) => {\n d({ type: 'ERROR_ADD', error: err })\n }),\n onCsrfWarn: queuable((d: Dispatch, payload: CsrfWarnPayload) => {\n d({ type: 'CSRF_WARN', payload })\n }),\n onManifestUpdated: queuable((d: Dispatch, manifest: RouteManifest) => {\n d({ type: 'MANIFEST_UPDATED', manifest })\n }),\n onRouteMatched: queuable((d: Dispatch, path: string, chain: string[]) => {\n d({ type: 'ROUTE_MATCHED', path, chain })\n }),\n onAgentRun: queuable((d: Dispatch, run: AgentRunRecord) => {\n d({ type: 'AGENT_RUN_ADD', run })\n }),\n onActionCall: queuable((d: Dispatch, record: ActionCallRecord) => {\n d({ type: 'ACTION_CALL_ADD', record })\n }),\n\n /**\n * Wire React's dispatch function in. Idempotent (EC-24): only the\n * NULL → non-null transition flushes the queue; subsequent non-null\n * sets just replace the reference (used for StrictMode re-mount).\n */\n setDispatch(d: Dispatch | null): void {\n const prev = _dispatch\n _dispatch = d\n if (d && !prev) flushQueue()\n },\n\n /** Testing helper — clear queue + dispatch reference between tests. */\n _reset(): void {\n _dispatch = null\n _queue.length = 0\n },\n\n /** Testing helper — observe queue length. */\n _queueLength(): number {\n return _queue.length\n },\n}\n\nexport type Dispatcher = typeof dispatcher\n"],"mappings":";AA8IO,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAExB,IAAM,eAA8B;AAAA,EACzC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,QAAQ,CAAC;AAAA,EACT,WAAW,CAAC;AAAA,EACZ,aAAa,CAAC;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa,CAAC;AAChB;AAEO,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AACtB,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;;;ACzIrC,IAAI,YAA6B;AACjC,IAAM,SAAuB,CAAC;AAE9B,SAAS,SACP,IACyB;AACzB,SAAO,IAAI,SAAe;AACxB,QAAI,WAAW;AACb,UAAI;AACF,WAAG,WAAW,GAAG,IAAI;AAAA,MACvB,SAAS,KAAK;AAKZ,gBAAQ,MAAM,mCAAmC,GAAG;AAAA,MACtD;AAAA,IACF,OAAO;AAEL,UAAI,OAAO,UAAU,eAAgB,QAAO,MAAM;AAClD,aAAO,KAAK,CAAC,MAAM;AACjB,WAAG,GAAG,GAAG,IAAI;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,aAAmB;AAC1B,SAAO,OAAO,QAAQ;AACpB,UAAM,KAAK,OAAO,MAAM;AACxB,QAAI,CAAC,MAAM,CAAC,UAAW;AACvB,QAAI;AACF,SAAG,SAAS;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,uCAAuC,GAAG;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,IAAM,aAAa;AAAA,EACxB,WAAW,SAAS,CAAC,GAAa,QAAuB;AACvD,MAAE,EAAE,MAAM,eAAe,SAAS,IAAI,CAAC;AAAA,EACzC,CAAC;AAAA,EACD,SAAS,SAAS,CAAC,GAAa,QAAqB;AACnD,MAAE,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAAA,EACrC,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,YAA6B;AAC9D,MAAE,EAAE,MAAM,aAAa,QAAQ,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,mBAAmB,SAAS,CAAC,GAAa,aAA4B;AACpE,MAAE,EAAE,MAAM,oBAAoB,SAAS,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,gBAAgB,SAAS,CAAC,GAAa,MAAc,UAAoB;AACvE,MAAE,EAAE,MAAM,iBAAiB,MAAM,MAAM,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,QAAwB;AACzD,MAAE,EAAE,MAAM,iBAAiB,IAAI,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,cAAc,SAAS,CAAC,GAAa,WAA6B;AAChE,MAAE,EAAE,MAAM,mBAAmB,OAAO,CAAC;AAAA,EACvC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,YAAY,GAA0B;AACpC,UAAM,OAAO;AACb,gBAAY;AACZ,QAAI,KAAK,CAAC,KAAM,YAAW;AAAA,EAC7B;AAAA;AAAA,EAGA,SAAe;AACb,gBAAY;AACZ,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA,EAGA,eAAuB;AACrB,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -9,7 +9,7 @@ var __IS_DEV = (() => {
|
|
|
9
9
|
async function trackAgentRun(input, opts) {
|
|
10
10
|
if (__IS_DEV) {
|
|
11
11
|
try {
|
|
12
|
-
const mod = await import("./dispatcher-
|
|
12
|
+
const mod = await import("./dispatcher-AQJGI3HU.js");
|
|
13
13
|
mod.dispatcher.onAgentRun({
|
|
14
14
|
// eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id
|
|
15
15
|
id: `run-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -182,4 +182,4 @@ export {
|
|
|
182
182
|
InMemoryUsageStorage,
|
|
183
183
|
trackAgentTools
|
|
184
184
|
};
|
|
185
|
-
//# sourceMappingURL=chunk-
|
|
185
|
+
//# sourceMappingURL=chunk-M23FP2AS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/cost/track-agent-run.ts","../src/server/cost/usage-storage-memory.ts","../src/server/cost/track-agent-tools.ts"],"sourcesContent":["import type { AgentRunRecord } from '../../devtools/shared.js'\n\nimport type { UsageRecord, UsageStorageAdapter } from './cost-types.js'\n\nexport interface TrackAgentRunInput {\n userId: string\n model: string\n tokens: { input: number; output: number }\n costUsd: number\n /** Defaults to `new Date()`. */\n timestamp?: Date\n /** Optional execution status surfaced to devtools (default 'finished'). */\n status?: 'finished' | 'error' | 'aborted'\n}\n\nexport interface TrackAgentRunOptions {\n /** Adapter resolved from `theo.config.ts > cost.storage`. */\n storage: UsageStorageAdapter | undefined\n}\n\n// v1.1 EC-4 MUST FIX — universal dev gate (Vite OR tsup-bundled).\n// Both checks are statically replaceable by bundlers in prod build,\n// so the entire dispatcher import is tree-shaken from the prod bundle.\nconst __IS_DEV = (() => {\n try {\n return (import.meta as { env?: { DEV?: boolean } }).env?.DEV === true\n } catch {\n return process.env.NODE_ENV !== 'production'\n }\n})()\n\n/**\n * Record a single agent run's usage + cost. Companion to the client-side\n * `<CostMeter>` from `@usetheo/ui`.\n *\n * EC-14: this function NEVER bubbles errors back to the caller. Adapter\n * failures (network outage, DB down, etc.) are logged via `console.warn`\n * and swallowed. The agent response MUST NOT fail because cost tracking\n * is degraded.\n *\n * No-op when `storage` is undefined (cost tracking unconfigured).\n *\n * In dev mode (Vite OR tsup-built with NODE_ENV != 'production'), fires a\n * `theokit-evolution-ci-and-dx` Phase 3 dispatcher event so the devtools\n * Agents tab can render the run. Prod tree-shakes the entire dispatcher\n * import via the `__IS_DEV` IIFE guard.\n *\n * @see docs/concepts/cost-tracking.md (when implemented in T6.3)\n */\nexport async function trackAgentRun(\n input: TrackAgentRunInput,\n opts: TrackAgentRunOptions,\n): Promise<void> {\n // Dev-only: surface run to devtools Agents tab (T3.1)\n if (__IS_DEV) {\n try {\n const mod = (await import('../../devtools/dispatcher.js')) as {\n dispatcher: { onAgentRun: (r: AgentRunRecord) => void }\n }\n mod.dispatcher.onAgentRun({\n // eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id\n id: `run-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,\n timestamp: (input.timestamp ?? new Date()).getTime(),\n userId: input.userId,\n model: input.model,\n tokensInput: input.tokens.input,\n tokensOutput: input.tokens.output,\n costUsd: input.costUsd,\n status: input.status ?? 'finished',\n })\n } catch {\n // devtools dispatcher missing in some prod-like bundle — silently skip\n }\n }\n\n if (!opts.storage) return\n const record: UsageRecord = {\n userId: input.userId,\n model: input.model,\n tokens: input.tokens,\n costUsd: input.costUsd,\n timestamp: input.timestamp ?? new Date(),\n }\n try {\n await opts.storage.record(record)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n\n console.warn(\n `[theokit:cost] usage storage record failed: ${message}. ` +\n '(Response is unaffected — cost tracking degraded.)',\n )\n }\n}\n","import type { StorageAdapter } from '../storage/storage-types.js'\n\nimport type {\n ToolUsageRecord,\n UsageQuery,\n UsageRecord,\n UsageResult,\n UsageStorageAdapter,\n} from './cost-types.js'\n\n/**\n * In-memory usage storage for dev/tests. Unbounded — production\n * deployments MUST plug in a durable adapter (Postgres/Redis recipes\n * land with R0.6.7; documented in EC-114).\n *\n * Phase 5 — Production-Readiness #4: accepts both `UsageRecord` (LLM call,\n * kind='llm' or omitted) AND `ToolUsageRecord` (tool call, kind='tool').\n * `getUsage` only sums LLM records (tools have no token/cost dimension).\n *\n * EC-9 (backward compat): input without `kind` is normalized to `kind:'llm'`\n * — adapters from older versions stay working.\n *\n * Concurrency: Node's single-threaded event loop guarantees that\n * `Array.push` is atomic, so concurrent record() calls cannot lose data.\n */\nexport class InMemoryUsageStorage implements UsageStorageAdapter, StorageAdapter {\n readonly name = 'memory'\n readonly #records: (UsageRecord | ToolUsageRecord)[] = []\n\n /**\n * T2.3 — `StorageAdapter` lifecycle hook (ADR-0007 D6). In-memory storage\n * has no real cleanup to perform — this is intentionally a noop so the\n * adapter can be registered with `StorageManager.register()` and\n * participate in graceful shutdown without changing behavior.\n *\n * Does NOT clear stored records (dispose ≠ reset). For test reset use\n * a fresh instance.\n */\n dispose(): Promise<void> {\n // Intentional noop — see jsdoc above\n return Promise.resolve()\n }\n\n async record(input: UsageRecord | ToolUsageRecord): Promise<void> {\n // EC-9: normalize legacy input (no `kind` field) to kind:'llm'.\n const normalized: UsageRecord | ToolUsageRecord =\n 'kind' in input && input.kind === 'tool'\n ? { ...input }\n : ({ ...input, kind: 'llm' as const } satisfies UsageRecord)\n this.#records.push(normalized)\n return Promise.resolve()\n }\n\n async getUsage(query: UsageQuery): Promise<UsageResult> {\n const from = query.period.from.getTime()\n const to = query.period.to.getTime()\n let totalTokens = 0\n let totalCostUsd = 0\n let runs = 0\n for (const r of this.#records) {\n if (r.kind === 'tool') continue\n if (r.userId !== query.userId) continue\n const t = r.timestamp.getTime()\n if (t < from || t > to) continue\n totalTokens += r.tokens.input + r.tokens.output\n totalCostUsd += r.costUsd\n runs += 1\n }\n return Promise.resolve({ totalTokens, totalCostUsd, runs })\n }\n\n /** @internal — test helper to inspect raw record stream */\n __getRecords(): readonly (UsageRecord | ToolUsageRecord)[] {\n return this.#records\n }\n}\n","import type { ToolUsageRecord, UsageStorageAdapter } from './cost-types.js'\n\n/**\n * Phase 5 — Production-Readiness #4: tool lifecycle hooks for cost tracking.\n *\n * The SDK calls `onToolStart` → handler runs → `onToolEnd` (success) or\n * `onToolError` (throw). This factory returns the three callbacks ready to\n * attach to `Agent.create({ tools, onToolStart, onToolEnd, onToolError })`.\n *\n * Each successful tool call writes a `ToolUsageRecord{success:true}`; each\n * failure writes `ToolUsageRecord{success:false}` with `errorMessage` from\n * the thrown Error.\n *\n * Invariants:\n * - `onToolStart` records nothing yet (no duration available). Only stashes\n * a `startedAt` timestamp by `callId` in a per-factory Map.\n * - `onToolEnd` reads the stashed timestamp, computes `durationMs`, writes\n * a successful record. Removes the Map entry.\n * - `onToolError` analog with `success:false`.\n * - Hook throws are SWALLOWED to stderr — observation hooks must not crash\n * the run (per the v1.1.0 release contract).\n * - EC-8 (SHOULD TEST) — orphan starts (no matching End/Error) are pruned\n * on every onToolStart if older than 5 minutes. Bounded memory.\n * - EC-16 (DOCUMENT) — callId uniqueness is SDK contract; duplicate Start\n * for the same callId uses last-write-wins.\n */\n\nconst ORPHAN_TTL_MS = 5 * 60 * 1000\n\ninterface PendingStart {\n startedAt: number\n userId: string\n conversationId: string\n}\n\nexport interface TrackAgentToolsOptions {\n storage: UsageStorageAdapter\n /** Defaults to () => new Date() — overridable for deterministic testing. */\n now?: () => Date\n /** Identifier for the user — passed through to ToolUsageRecord.userId. */\n userId?: string\n /** Identifier for the conversation — passed through to ToolUsageRecord.conversationId. */\n conversationId?: string\n}\n\nexport interface ToolHookEvent {\n callId: string\n name: string\n // SDK shape — we only read these structurally\n [key: string]: unknown\n}\n\nexport interface TrackAgentToolsHooks {\n onToolStart: (event: ToolHookEvent) => void\n onToolEnd: (event: ToolHookEvent) => void\n onToolError: (event: ToolHookEvent) => void\n}\n\n/**\n * Create the three tool-lifecycle callbacks. Hand the returned object's\n * methods to `Agent.create({ tools, onToolStart, onToolEnd, onToolError })`.\n */\nexport function trackAgentTools(opts: TrackAgentToolsOptions): TrackAgentToolsHooks {\n const pending = new Map<string, PendingStart>()\n const now = opts.now ?? ((): Date => new Date())\n const userId = opts.userId ?? 'anonymous'\n const conversationId = opts.conversationId ?? 'unknown'\n\n function pruneOrphans(): void {\n const cutoff = now().getTime() - ORPHAN_TTL_MS\n for (const [id, p] of pending) {\n if (p.startedAt < cutoff) pending.delete(id)\n }\n }\n\n function safeRecord(record: ToolUsageRecord): void {\n Promise.resolve(opts.storage.record(record)).catch((err: unknown) => {\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(`[theokit:trackAgentTools] storage.record failed: ${msg}`)\n })\n }\n\n return {\n onToolStart(event: ToolHookEvent): void {\n try {\n pruneOrphans()\n pending.set(event.callId, {\n startedAt: now().getTime(),\n userId,\n conversationId,\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolStart hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n onToolEnd(event: ToolHookEvent): void {\n try {\n const start = pending.get(event.callId)\n const startedAt = start?.startedAt ?? now().getTime()\n const durationMs = start ? now().getTime() - startedAt : 0\n pending.delete(event.callId)\n if (!start) {\n console.warn(\n `[theokit:trackAgentTools] orphan onToolEnd for callId=${event.callId}; durationMs:0 recorded`,\n )\n }\n safeRecord({\n kind: 'tool',\n userId: start?.userId ?? userId,\n conversationId: start?.conversationId ?? conversationId,\n toolName: event.name,\n callId: event.callId,\n success: true,\n durationMs,\n timestamp: now(),\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolEnd hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n onToolError(event: ToolHookEvent): void {\n try {\n const start = pending.get(event.callId)\n const startedAt = start?.startedAt ?? now().getTime()\n const durationMs = start ? now().getTime() - startedAt : 0\n pending.delete(event.callId)\n const error = event.error\n let errorMessage = 'unknown error'\n if (error instanceof Error) errorMessage = error.message\n else if (typeof error === 'string') errorMessage = error\n safeRecord({\n kind: 'tool',\n userId: start?.userId ?? userId,\n conversationId: start?.conversationId ?? conversationId,\n toolName: event.name,\n callId: event.callId,\n success: false,\n durationMs,\n errorMessage,\n timestamp: now(),\n })\n } catch (err) {\n console.warn(\n `[theokit:trackAgentTools] onToolError hook crashed (swallowed): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n },\n }\n}\n\n/**\n * @internal — testing helper. Inspects the size of the pending-starts Map of\n * a factory instance. Not in the public API.\n */\nexport const __internalsForTesting = {\n ORPHAN_TTL_MS,\n}\n"],"mappings":"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","names":[]}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
BATCH_PATH,
|
|
10
10
|
CSP_REPORT_PATH,
|
|
11
|
+
CsrfReadinessStore,
|
|
11
12
|
TRACE_HEADER,
|
|
12
13
|
applySecurityHeaders,
|
|
13
14
|
createCorsHandler,
|
|
@@ -23,13 +24,17 @@ import {
|
|
|
23
24
|
handleCsrfReadiness,
|
|
24
25
|
loadEnv,
|
|
25
26
|
logRequest,
|
|
26
|
-
matchRoute,
|
|
27
27
|
resolveTransformer,
|
|
28
|
-
scanServerActions,
|
|
29
|
-
scanServerRoutes,
|
|
30
|
-
scanWebSocketRoutes,
|
|
31
28
|
sendError
|
|
32
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-OJHO2X7C.js";
|
|
30
|
+
import {
|
|
31
|
+
matchRoute,
|
|
32
|
+
scanServerRoutes,
|
|
33
|
+
scanWebSocketRoutes
|
|
34
|
+
} from "./chunk-2BQYEBCK.js";
|
|
35
|
+
import {
|
|
36
|
+
scanServerActions
|
|
37
|
+
} from "./chunk-7DHGV77J.js";
|
|
33
38
|
import {
|
|
34
39
|
generateNonce
|
|
35
40
|
} from "./chunk-PB4GR7EP.js";
|
|
@@ -612,13 +617,17 @@ function createActionMiddleware(vite, serverDir, options) {
|
|
|
612
617
|
csrfMode,
|
|
613
618
|
disallowed
|
|
614
619
|
);
|
|
615
|
-
logRequest(
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
620
|
+
logRequest(
|
|
621
|
+
{
|
|
622
|
+
method: req.method ?? "POST",
|
|
623
|
+
url,
|
|
624
|
+
status: res.statusCode,
|
|
625
|
+
duration: Date.now() - start,
|
|
626
|
+
requestId
|
|
627
|
+
},
|
|
628
|
+
void 0,
|
|
629
|
+
req
|
|
630
|
+
);
|
|
622
631
|
})();
|
|
623
632
|
};
|
|
624
633
|
}
|
|
@@ -698,6 +707,35 @@ async function handleBatchInline(req, res, batching, requestId) {
|
|
|
698
707
|
sendError(res, "BATCH_ERROR", message, 400, void 0, requestId);
|
|
699
708
|
}
|
|
700
709
|
}
|
|
710
|
+
async function runPluginsBeforeRouteMatch(pluginRunner, reqCtx) {
|
|
711
|
+
const { req, res, requestId, start, url } = reqCtx;
|
|
712
|
+
try {
|
|
713
|
+
const hookResult = await pluginRunner.runOnRequest({
|
|
714
|
+
request: req,
|
|
715
|
+
response: res,
|
|
716
|
+
ctx: {},
|
|
717
|
+
requestId
|
|
718
|
+
});
|
|
719
|
+
if (hookResult.shortCircuited) {
|
|
720
|
+
logRequest(
|
|
721
|
+
{
|
|
722
|
+
method: req.method ?? "GET",
|
|
723
|
+
url,
|
|
724
|
+
status: res.statusCode,
|
|
725
|
+
duration: Date.now() - start,
|
|
726
|
+
requestId
|
|
727
|
+
},
|
|
728
|
+
void 0,
|
|
729
|
+
req
|
|
730
|
+
);
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
} catch (err) {
|
|
734
|
+
sendError(res, "PLUGIN_ERROR", err.message, 500, void 0, requestId);
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
701
739
|
function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
702
740
|
const opts = rateLimitConfigOrOptions && "windowMs" in rateLimitConfigOrOptions ? { rateLimitConfig: rateLimitConfigOrOptions } : rateLimitConfigOrOptions ?? {};
|
|
703
741
|
const loadModule = createViteLoader(vite);
|
|
@@ -715,7 +753,7 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
715
753
|
const csrfReadinessStore = opts.csrfReadinessStore;
|
|
716
754
|
const servicesProxyPrefixes = opts.servicesProxyPrefixes ?? [];
|
|
717
755
|
return (req, res, next) => {
|
|
718
|
-
|
|
756
|
+
const handle = async () => {
|
|
719
757
|
const url = req.url ?? "";
|
|
720
758
|
if (await handleCspReportIfMatch(req, res, url, { auditLogger, onCspViolation })) {
|
|
721
759
|
return;
|
|
@@ -739,6 +777,9 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
739
777
|
if (applyRateLimitOr429(req, res, { rateLimiter, requestId, url, start })) {
|
|
740
778
|
return;
|
|
741
779
|
}
|
|
780
|
+
if (pluginRunner && await runPluginsBeforeRouteMatch(pluginRunner, { req, res, requestId, start, url })) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
742
783
|
if (await handleBatchIfMatch(req, res, { url, batching, requestId, start })) {
|
|
743
784
|
return;
|
|
744
785
|
}
|
|
@@ -750,13 +791,17 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
750
791
|
const suggestion = findSuggestion(urlPath, routePaths);
|
|
751
792
|
const msg = suggestion ? `API route not found: ${urlPath}. Did you mean: ${suggestion}?` : "API route not found";
|
|
752
793
|
sendError(res, "NOT_FOUND", msg, 404, void 0, requestId);
|
|
753
|
-
logRequest(
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
794
|
+
logRequest(
|
|
795
|
+
{
|
|
796
|
+
method: req.method ?? "GET",
|
|
797
|
+
url,
|
|
798
|
+
status: 404,
|
|
799
|
+
duration: Date.now() - start,
|
|
800
|
+
requestId
|
|
801
|
+
},
|
|
802
|
+
void 0,
|
|
803
|
+
req
|
|
804
|
+
);
|
|
760
805
|
return;
|
|
761
806
|
}
|
|
762
807
|
const method = (req.method ?? "GET").toUpperCase();
|
|
@@ -774,8 +819,13 @@ function createApiMiddleware(vite, serverDir, rateLimitConfigOrOptions) {
|
|
|
774
819
|
csrfMode,
|
|
775
820
|
disallowed
|
|
776
821
|
});
|
|
777
|
-
logRequest(
|
|
778
|
-
|
|
822
|
+
logRequest(
|
|
823
|
+
{ method, url, status: res.statusCode, duration: Date.now() - start, requestId },
|
|
824
|
+
void 0,
|
|
825
|
+
req
|
|
826
|
+
);
|
|
827
|
+
};
|
|
828
|
+
void handle();
|
|
779
829
|
};
|
|
780
830
|
}
|
|
781
831
|
|
|
@@ -1039,6 +1089,21 @@ var theoConfigSchema = z.object({
|
|
|
1039
1089
|
* matching `--target`, `theokit build` emits a cross-reference note.
|
|
1040
1090
|
*/
|
|
1041
1091
|
adapters: z.array(z.string()).optional(),
|
|
1092
|
+
/**
|
|
1093
|
+
* Extra package names to pre-bundle via Vite's `optimizeDeps.include`.
|
|
1094
|
+
*
|
|
1095
|
+
* Framework auto-includes `@usetheo/ui` and `lucide-react` when present.
|
|
1096
|
+
* Apps adding plugin peer-deps that rely on a runtime `import('<pkg>')`
|
|
1097
|
+
* (dynamic specifier) — e.g. `@theokit/plugin-canvas` consumers
|
|
1098
|
+
* installing `mermaid` for Mermaid SVG rendering — must declare those
|
|
1099
|
+
* here so Vite can resolve the literal in dev mode without
|
|
1100
|
+
* "Failed to resolve module specifier" errors. Production builds use
|
|
1101
|
+
* tsup/esbuild bundling and are not affected.
|
|
1102
|
+
*
|
|
1103
|
+
* Example:
|
|
1104
|
+
* viteOptimizeDeps: ['mermaid']
|
|
1105
|
+
*/
|
|
1106
|
+
viteOptimizeDeps: z.array(z.string()).optional(),
|
|
1042
1107
|
/**
|
|
1043
1108
|
* Jobs backend (ADR D3 + T2.1). When configured, every request gets
|
|
1044
1109
|
* `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a
|
|
@@ -1065,7 +1130,33 @@ var theoConfigSchema = z.object({
|
|
|
1065
1130
|
* that boot alongside the TheoKit TS app. Empty `services: {}` is the
|
|
1066
1131
|
* default and preserves Wave 1 behavior.
|
|
1067
1132
|
*/
|
|
1068
|
-
services: servicesConfigSchema
|
|
1133
|
+
services: servicesConfigSchema,
|
|
1134
|
+
/**
|
|
1135
|
+
* G2 — OpenAPI emit (build-time only).
|
|
1136
|
+
*
|
|
1137
|
+
* When present, `theokit build` writes a generated `openapi.json` from
|
|
1138
|
+
* every `defineRoute()` body/query/params Zod schema. `undefined` keeps
|
|
1139
|
+
* the framework backward-compatible (no emit). Pass `openapi: {}` to
|
|
1140
|
+
* opt in with defaults.
|
|
1141
|
+
*
|
|
1142
|
+
* Defaults: spec 3.1.0 · servers `http://localhost:3000` · title
|
|
1143
|
+
* "TheoKit App" · version "0.0.0" · outDir ".theo" (next to dist).
|
|
1144
|
+
*
|
|
1145
|
+
* The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides
|
|
1146
|
+
* `servers[].url` at emit time without rebuilding the config.
|
|
1147
|
+
*/
|
|
1148
|
+
openapi: z.object({
|
|
1149
|
+
servers: z.array(
|
|
1150
|
+
z.object({
|
|
1151
|
+
url: z.string().url(),
|
|
1152
|
+
description: z.string().optional()
|
|
1153
|
+
})
|
|
1154
|
+
).default([{ url: "http://localhost:3000", description: "Local development" }]),
|
|
1155
|
+
specVersion: z.enum(["3.1.0", "3.0.3"]).default("3.1.0"),
|
|
1156
|
+
title: z.string().default("TheoKit App"),
|
|
1157
|
+
version: z.string().default("0.0.0"),
|
|
1158
|
+
outDir: z.string().default(".theo")
|
|
1159
|
+
}).optional()
|
|
1069
1160
|
}).refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {
|
|
1070
1161
|
message: "service.port collides with TheoKit web port \u2014 change one of them",
|
|
1071
1162
|
path: ["services"]
|
|
@@ -1164,7 +1255,7 @@ function resolveExportSubpath(pkgRoot, subpath) {
|
|
|
1164
1255
|
}
|
|
1165
1256
|
let target;
|
|
1166
1257
|
if (typeof exp === "string") target = exp;
|
|
1167
|
-
else if (
|
|
1258
|
+
else if (typeof exp === "object") {
|
|
1168
1259
|
const e = exp;
|
|
1169
1260
|
target = e.import ?? e.default;
|
|
1170
1261
|
}
|
|
@@ -1234,7 +1325,9 @@ var DEFAULT_RESOLVED = {
|
|
|
1234
1325
|
disallowed: void 0,
|
|
1235
1326
|
cors: void 0,
|
|
1236
1327
|
auditLogger: void 0,
|
|
1237
|
-
devtoolsEnabled: true
|
|
1328
|
+
devtoolsEnabled: true,
|
|
1329
|
+
openapi: void 0,
|
|
1330
|
+
distDir: ".theo"
|
|
1238
1331
|
};
|
|
1239
1332
|
async function resolvePluginConfig(projectRoot) {
|
|
1240
1333
|
try {
|
|
@@ -1263,7 +1356,9 @@ async function resolvePluginConfig(projectRoot) {
|
|
|
1263
1356
|
disallowed: userConfig.security?.disallowed,
|
|
1264
1357
|
cors: userConfig.security?.cors,
|
|
1265
1358
|
auditLogger,
|
|
1266
|
-
devtoolsEnabled: userConfig.devtools !== false
|
|
1359
|
+
devtoolsEnabled: userConfig.devtools !== false,
|
|
1360
|
+
openapi: userConfig.openapi,
|
|
1361
|
+
distDir: userConfig.distDir
|
|
1267
1362
|
};
|
|
1268
1363
|
} catch {
|
|
1269
1364
|
return DEFAULT_RESOLVED;
|
|
@@ -1600,9 +1695,7 @@ async function integrateUseTheoUI(cwd, opts) {
|
|
|
1600
1695
|
}
|
|
1601
1696
|
const tailwindPlugins = normalizePluginReturn(tailwindPlugin);
|
|
1602
1697
|
if (tailwindPlugins === null) {
|
|
1603
|
-
console.warn(
|
|
1604
|
-
`[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`
|
|
1605
|
-
);
|
|
1698
|
+
console.warn(`[theokit] @tailwindcss/vite returned unexpected shape. Skipping auto-config.`);
|
|
1606
1699
|
return [];
|
|
1607
1700
|
}
|
|
1608
1701
|
const uiPlugins = normalizePluginReturn(uiPlugin);
|
|
@@ -1876,7 +1969,42 @@ async function theoPluginAsync(rootOrOptions) {
|
|
|
1876
1969
|
})
|
|
1877
1970
|
);
|
|
1878
1971
|
}
|
|
1879
|
-
|
|
1972
|
+
const { appTypedClientPlugin } = await import("./app-typed-client-RZMVWHJ3.js");
|
|
1973
|
+
const appClientPlugin = appTypedClientPlugin({
|
|
1974
|
+
cwd: projectRoot,
|
|
1975
|
+
serverDir: resolve6(projectRoot, "server"),
|
|
1976
|
+
distDir: resolve6(projectRoot, ".theo")
|
|
1977
|
+
});
|
|
1978
|
+
const { actionsVirtualModule } = await import("./actions-virtual-module-E7IELVHL.js");
|
|
1979
|
+
const actionsPlugin = actionsVirtualModule({
|
|
1980
|
+
serverDir: resolve6(projectRoot, "server"),
|
|
1981
|
+
distDir: resolve6(projectRoot, ".theo")
|
|
1982
|
+
});
|
|
1983
|
+
return [
|
|
1984
|
+
theoPlugin(rootOrOptions),
|
|
1985
|
+
...uiPlugins,
|
|
1986
|
+
...servicesPlugins,
|
|
1987
|
+
appClientPlugin,
|
|
1988
|
+
actionsPlugin
|
|
1989
|
+
];
|
|
1990
|
+
}
|
|
1991
|
+
function buildOptimizeDepsInclude(projectRoot, viteOptimizeDeps) {
|
|
1992
|
+
const include = [];
|
|
1993
|
+
if (existsSync7(resolve6(projectRoot, "node_modules", "@usetheo", "ui"))) {
|
|
1994
|
+
include.push("@usetheo/ui");
|
|
1995
|
+
}
|
|
1996
|
+
if (existsSync7(resolve6(projectRoot, "node_modules", "lucide-react"))) {
|
|
1997
|
+
include.push("lucide-react");
|
|
1998
|
+
}
|
|
1999
|
+
include.push("devalue");
|
|
2000
|
+
if (Array.isArray(viteOptimizeDeps)) {
|
|
2001
|
+
for (const pkg of viteOptimizeDeps) {
|
|
2002
|
+
if (typeof pkg === "string" && pkg.length > 0 && !include.includes(pkg)) {
|
|
2003
|
+
include.push(pkg);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return include;
|
|
1880
2008
|
}
|
|
1881
2009
|
function theoPlugin(rootOrOptions) {
|
|
1882
2010
|
const options = typeof rootOrOptions === "string" ? { root: rootOrOptions } : rootOrOptions ?? {};
|
|
@@ -1895,6 +2023,8 @@ function theoPlugin(rootOrOptions) {
|
|
|
1895
2023
|
let cors;
|
|
1896
2024
|
let auditLogger;
|
|
1897
2025
|
let devtoolsEnabled = true;
|
|
2026
|
+
let resolvedOpenApi;
|
|
2027
|
+
let resolvedDistDir = ".theo";
|
|
1898
2028
|
let isDevMode = false;
|
|
1899
2029
|
let configLoadedOnce = false;
|
|
1900
2030
|
return {
|
|
@@ -1913,6 +2043,8 @@ function theoPlugin(rootOrOptions) {
|
|
|
1913
2043
|
cors = resolved.cors;
|
|
1914
2044
|
auditLogger = resolved.auditLogger;
|
|
1915
2045
|
devtoolsEnabled = resolved.devtoolsEnabled;
|
|
2046
|
+
resolvedOpenApi = resolved.openapi;
|
|
2047
|
+
resolvedDistDir = resolved.distDir;
|
|
1916
2048
|
},
|
|
1917
2049
|
// Vite calls this hook (sync return is fine — no awaits left after
|
|
1918
2050
|
// the auto-chain moved to theoPluginAsync). The @usetheo/ui +
|
|
@@ -1920,13 +2052,7 @@ function theoPlugin(rootOrOptions) {
|
|
|
1920
2052
|
// Vite drops plugins returned from a child plugin's config() hook.
|
|
1921
2053
|
config() {
|
|
1922
2054
|
const ext = existsSync7(resolve6(theoSrcDir, "index.ts")) ? ".ts" : ".js";
|
|
1923
|
-
const optimizeDepsInclude =
|
|
1924
|
-
if (existsSync7(resolve6(projectRoot, "node_modules", "@usetheo", "ui"))) {
|
|
1925
|
-
optimizeDepsInclude.push("@usetheo/ui");
|
|
1926
|
-
}
|
|
1927
|
-
if (existsSync7(resolve6(projectRoot, "node_modules", "lucide-react"))) {
|
|
1928
|
-
optimizeDepsInclude.push("lucide-react");
|
|
1929
|
-
}
|
|
2055
|
+
const optimizeDepsInclude = buildOptimizeDepsInclude(projectRoot, options.viteOptimizeDeps);
|
|
1930
2056
|
const warmupClientFiles = [];
|
|
1931
2057
|
for (const candidate of ["app/layout.tsx", "app/page.tsx", "app/page.jsx"]) {
|
|
1932
2058
|
const full = resolve6(projectRoot, candidate);
|
|
@@ -1935,7 +2061,10 @@ function theoPlugin(rootOrOptions) {
|
|
|
1935
2061
|
const servicesProxy = options.services && Object.keys(options.services).length > 0 ? buildServicesProxyConfig(options.services) : void 0;
|
|
1936
2062
|
return {
|
|
1937
2063
|
envPrefix: "THEO_PUBLIC_",
|
|
1938
|
-
optimizeDeps:
|
|
2064
|
+
optimizeDeps: {
|
|
2065
|
+
...optimizeDepsInclude.length > 0 ? { include: optimizeDepsInclude } : {},
|
|
2066
|
+
force: true
|
|
2067
|
+
},
|
|
1939
2068
|
server: {
|
|
1940
2069
|
...warmupClientFiles.length > 0 ? { warmup: { clientFiles: warmupClientFiles } } : {},
|
|
1941
2070
|
...servicesProxy !== void 0 ? { proxy: servicesProxy } : {},
|
|
@@ -2050,7 +2179,7 @@ function theoPlugin(rootOrOptions) {
|
|
|
2050
2179
|
].join("\n");
|
|
2051
2180
|
}
|
|
2052
2181
|
},
|
|
2053
|
-
configureServer(server) {
|
|
2182
|
+
async configureServer(server) {
|
|
2054
2183
|
isDevMode = true;
|
|
2055
2184
|
globalThis.__theoViteHotServer = server;
|
|
2056
2185
|
server.ws.on("theo:devtools:request-manifest", () => {
|
|
@@ -2065,6 +2194,7 @@ function theoPlugin(rootOrOptions) {
|
|
|
2065
2194
|
createActionMiddleware(server, serverDir, { pluginRunner, csrfMode, disallowed })
|
|
2066
2195
|
);
|
|
2067
2196
|
const servicesProxyPrefixes = options.services && Object.keys(options.services).length > 0 ? Object.values(options.services).map((s) => s.proxy) : [];
|
|
2197
|
+
const csrfReadinessStore = options.csrfReadinessStore ?? new CsrfReadinessStore();
|
|
2068
2198
|
server.middlewares.use(
|
|
2069
2199
|
createApiMiddleware(server, serverDir, {
|
|
2070
2200
|
rateLimitConfig: options.rateLimit,
|
|
@@ -2076,7 +2206,8 @@ function theoPlugin(rootOrOptions) {
|
|
|
2076
2206
|
disallowed,
|
|
2077
2207
|
cors,
|
|
2078
2208
|
auditLogger,
|
|
2079
|
-
servicesProxyPrefixes
|
|
2209
|
+
servicesProxyPrefixes,
|
|
2210
|
+
csrfReadinessStore
|
|
2080
2211
|
})
|
|
2081
2212
|
);
|
|
2082
2213
|
const configPath = resolve6(projectRoot, "theo.config.ts");
|
|
@@ -2105,6 +2236,29 @@ function theoPlugin(rootOrOptions) {
|
|
|
2105
2236
|
}
|
|
2106
2237
|
server.watcher.on("add", handleRouteChange);
|
|
2107
2238
|
server.watcher.on("unlink", handleRouteChange);
|
|
2239
|
+
if (resolvedOpenApi !== void 0) {
|
|
2240
|
+
const openApiCfg = resolvedOpenApi;
|
|
2241
|
+
const { reEmitOpenApi } = await import("./dev-emit-3CHM73TV.js");
|
|
2242
|
+
const openApiServerDir = resolve6(projectRoot, "server");
|
|
2243
|
+
const openApiDistDir = resolve6(projectRoot, resolvedDistDir);
|
|
2244
|
+
const isRouteFileForOpenApi = (file) => file.startsWith(openApiServerDir) && /\.(ts|tsx|js|mjs)$/.test(file);
|
|
2245
|
+
void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
|
|
2246
|
+
server.watcher.on("change", (file) => {
|
|
2247
|
+
if (isRouteFileForOpenApi(file)) {
|
|
2248
|
+
void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
server.watcher.on("add", (file) => {
|
|
2252
|
+
if (isRouteFileForOpenApi(file)) {
|
|
2253
|
+
void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
|
|
2254
|
+
}
|
|
2255
|
+
});
|
|
2256
|
+
server.watcher.on("unlink", (file) => {
|
|
2257
|
+
if (isRouteFileForOpenApi(file)) {
|
|
2258
|
+
void reEmitOpenApi(openApiServerDir, openApiDistDir, openApiCfg);
|
|
2259
|
+
}
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2108
2262
|
setupWsUpgrade(server, projectRoot);
|
|
2109
2263
|
server.httpServer?.once("close", () => {
|
|
2110
2264
|
;
|
|
@@ -2130,4 +2284,4 @@ export {
|
|
|
2130
2284
|
theoPluginAsync,
|
|
2131
2285
|
theoPlugin
|
|
2132
2286
|
};
|
|
2133
|
-
//# sourceMappingURL=chunk-
|
|
2287
|
+
//# sourceMappingURL=chunk-M6MYZSD2.js.map
|