theokit 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-events-lSIS9uYs.d.ts → agent-events-B3KNV9ER.d.ts} +1 -1
- package/dist/{app-typed-client-YIBP6JWH.js → app-typed-client-FOWJP534.js} +8 -16
- package/dist/app-typed-client-FOWJP534.js.map +1 -0
- package/dist/{app-typed-client-RZMVWHJ3.js → app-typed-client-IMILAGER.js} +8 -16
- package/dist/app-typed-client-IMILAGER.js.map +1 -0
- package/dist/{build-NCGIV4LL.js → build-KRSHWXMJ.js} +4 -4
- package/dist/{build-NCGIV4LL.js.map → build-KRSHWXMJ.js.map} +1 -1
- package/dist/{chunk-M23FP2AS.js → chunk-64JI5LTO.js} +1 -1
- package/dist/{chunk-M23FP2AS.js.map → chunk-64JI5LTO.js.map} +1 -1
- package/dist/{chunk-OJHO2X7C.js → chunk-CZM6WWWS.js} +1 -1
- package/dist/{chunk-OJHO2X7C.js.map → chunk-CZM6WWWS.js.map} +1 -1
- package/dist/{chunk-EXJWO2HB.js → chunk-F7RLPDXW.js} +25 -25
- package/dist/{chunk-EXJWO2HB.js.map → chunk-F7RLPDXW.js.map} +1 -1
- package/dist/{chunk-M6MYZSD2.js → chunk-RIBIAL7A.js} +26 -26
- package/dist/{chunk-M6MYZSD2.js.map → chunk-RIBIAL7A.js.map} +1 -1
- package/dist/{chunk-C27AZN46.js → chunk-TPCT3MXV.js} +1 -1
- package/dist/{chunk-C27AZN46.js.map → chunk-TPCT3MXV.js.map} +1 -1
- package/dist/{chunk-ZBWROIIO.js → chunk-X4JAX2U3.js} +3 -3
- package/dist/{chunk-ZBWROIIO.js.map → chunk-X4JAX2U3.js.map} +1 -1
- package/dist/{chunk-MG7H3LAW.js → chunk-XP7YXRHO.js} +2 -2
- package/dist/cli/index.js +7 -7
- package/dist/client/index.d.ts +2 -2
- package/dist/{dev-WDXC74CK.js → dev-UZJVBUF3.js} +5 -5
- package/dist/{dev-WDXC74CK.js.map → dev-UZJVBUF3.js.map} +1 -1
- package/dist/{dist-R2Q7GVDW.js → dist-E6BL4ZVY.js} +9 -9
- package/dist/dist-E6BL4ZVY.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{info-VM7EK4JC.js → info-FJ4NXKTB.js} +3 -3
- package/dist/{lib-NST3PNZX.js → lib-NL5JXGEB.js} +31 -31
- package/dist/lib-NL5JXGEB.js.map +1 -0
- package/dist/{openapi-CBASKC7W.js → openapi-ILYEJ6OR.js} +3 -3
- package/dist/{proper-lockfile-MVKMQGFK.js → proper-lockfile-4Y3DP3RP.js} +27 -27
- package/dist/proper-lockfile-4Y3DP3RP.js.map +1 -0
- package/dist/{routes-7O4NUDYS.js → routes-IGT42JJO.js} +3 -3
- package/dist/{schema-JAKF23GR.js → schema-VR6YNVLQ.js} +2 -2
- package/dist/server/cost/index.d.ts +1 -1
- package/dist/server/cost/index.js +1 -1
- package/dist/server/index.d.ts +12 -12
- package/dist/server/index.js +5 -5
- package/dist/server/index.js.map +1 -1
- package/dist/{sqlite-vec-54KB4RD3.js → sqlite-vec-ARPNUBWT.js} +2 -2
- package/dist/sqlite-vec-ARPNUBWT.js.map +1 -0
- package/dist/{start-5JTFWF4T.js → start-UX2IPPSH.js} +7 -7
- package/dist/{start-5JTFWF4T.js.map → start-UX2IPPSH.js.map} +1 -1
- package/dist/{upgrade-readiness-Q46KTG2J.js → upgrade-readiness-GUJBCJIS.js} +5 -5
- package/dist/{upgrade-readiness-Q46KTG2J.js.map → upgrade-readiness-GUJBCJIS.js.map} +1 -1
- package/dist/vite-plugin/index.d.ts +2 -2
- package/dist/vite-plugin/index.js +2 -2
- package/dist/{vite-plugin-4WZ3C2AC.js → vite-plugin-FTYXDKKG.js} +5 -5
- package/package.json +4 -4
- package/dist/app-typed-client-RZMVWHJ3.js.map +0 -1
- package/dist/app-typed-client-YIBP6JWH.js.map +0 -1
- package/dist/dist-R2Q7GVDW.js.map +0 -1
- package/dist/lib-NST3PNZX.js.map +0 -1
- package/dist/proper-lockfile-MVKMQGFK.js.map +0 -1
- package/dist/sqlite-vec-54KB4RD3.js.map +0 -1
- /package/dist/{chunk-MG7H3LAW.js.map → chunk-XP7YXRHO.js.map} +0 -0
- /package/dist/{info-VM7EK4JC.js.map → info-FJ4NXKTB.js.map} +0 -0
- /package/dist/{openapi-CBASKC7W.js.map → openapi-ILYEJ6OR.js.map} +0 -0
- /package/dist/{routes-7O4NUDYS.js.map → routes-IGT42JJO.js.map} +0 -0
- /package/dist/{schema-JAKF23GR.js.map → schema-VR6YNVLQ.js.map} +0 -0
- /package/dist/{vite-plugin-4WZ3C2AC.js.map → vite-plugin-FTYXDKKG.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import "tsx/esm";
|
|
3
3
|
import "./chunk-KT3MWNZG.js";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// ../../../theokit-plugins/node_modules/.pnpm/sqlite-vec@0.1.9/node_modules/sqlite-vec/index.mjs
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { arch, platform } from "process";
|
|
8
8
|
var BASE_PACKAGE_NAME = "sqlite-vec";
|
|
@@ -38,4 +38,4 @@ export {
|
|
|
38
38
|
getLoadablePath,
|
|
39
39
|
load
|
|
40
40
|
};
|
|
41
|
-
//# sourceMappingURL=sqlite-vec-
|
|
41
|
+
//# sourceMappingURL=sqlite-vec-ARPNUBWT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../theokit-plugins/node_modules/.pnpm/sqlite-vec@0.1.9/node_modules/sqlite-vec/index.mjs"],"sourcesContent":["\n\nimport { fileURLToPath } from \"node:url\";\nimport { arch, platform } from \"node:process\";\n\n\nconst BASE_PACKAGE_NAME = \"sqlite-vec\";\nconst ENTRYPOINT_BASE_NAME = \"vec0\";\nconst supportedPlatforms = [[\"darwin\",\"x64\"],[\"linux\",\"x64\"],[\"darwin\",\"arm64\"],[\"win32\",\"x64\"],[\"linux\",\"arm64\"]];\n\nconst invalidPlatformErrorMessage = `Unsupported platform for ${BASE_PACKAGE_NAME}, on a ${platform}-${arch} machine. Supported platforms are (${supportedPlatforms\n .map(([p, a]) => `${p}-${a}`)\n .join(\",\")}). Consult the ${BASE_PACKAGE_NAME} NPM package README for details.`;\n\nconst extensionNotFoundErrorMessage = packageName => `Loadble extension for ${BASE_PACKAGE_NAME} not found. Was the ${packageName} package installed?`;\n\nfunction validPlatform(platform, arch) {\n return (\n supportedPlatforms.find(([p, a]) => platform === p && arch === a) !== undefined\n );\n}\nfunction extensionSuffix(platform) {\n if (platform === \"win32\") return \"dll\";\n if (platform === \"darwin\") return \"dylib\";\n return \"so\";\n}\nfunction platformPackageName(platform, arch) {\n const os = platform === \"win32\" ? \"windows\" : platform;\n return `${BASE_PACKAGE_NAME}-${os}-${arch}`;\n}\n\nfunction getLoadablePath() {\n if (!validPlatform(platform, arch)) {\n throw new Error(\n invalidPlatformErrorMessage\n );\n }\n const packageName = platformPackageName(platform, arch);\n const loadablePath = fileURLToPath(import.meta.resolve(packageName + \"/\" + ENTRYPOINT_BASE_NAME + \".\" + extensionSuffix(platform)));\n return loadablePath;\n}\n\nfunction load(db) {\n db.loadExtension(getLoadablePath());\n}\n\nexport {getLoadablePath, load};\n"],"mappings":";;;;;AAEA,SAAS,qBAAqB;AAC9B,SAAS,MAAM,gBAAgB;AAG/B,IAAM,oBAAoB;AAC1B,IAAM,uBAAuB;AAC7B,IAAM,qBAAqB,CAAC,CAAC,UAAS,KAAK,GAAE,CAAC,SAAQ,KAAK,GAAE,CAAC,UAAS,OAAO,GAAE,CAAC,SAAQ,KAAK,GAAE,CAAC,SAAQ,OAAO,CAAC;AAEjH,IAAM,8BAA8B,4BAA4B,iBAAiB,UAAU,QAAQ,IAAI,IAAI,sCAAsC,mBAC9I,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,GAAG,CAAC,kBAAkB,iBAAiB;AAI/C,SAAS,cAAcA,WAAUC,OAAM;AACrC,SACE,mBAAmB,KAAK,CAAC,CAAC,GAAG,CAAC,MAAMD,cAAa,KAAKC,UAAS,CAAC,MAAM;AAE1E;AACA,SAAS,gBAAgBD,WAAU;AACjC,MAAIA,cAAa,QAAS,QAAO;AACjC,MAAIA,cAAa,SAAU,QAAO;AAClC,SAAO;AACT;AACA,SAAS,oBAAoBA,WAAUC,OAAM;AAC3C,QAAM,KAAKD,cAAa,UAAU,YAAYA;AAC9C,SAAO,GAAG,iBAAiB,IAAI,EAAE,IAAIC,KAAI;AAC3C;AAEA,SAAS,kBAAkB;AACzB,MAAI,CAAC,cAAc,UAAU,IAAI,GAAG;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,cAAc,oBAAoB,UAAU,IAAI;AACtD,QAAM,eAAe,cAAc,YAAY,QAAQ,cAAc,MAAM,uBAAuB,MAAM,gBAAgB,QAAQ,CAAC,CAAC;AAClI,SAAO;AACT;AAEA,SAAS,KAAK,IAAI;AAChB,KAAG,cAAc,gBAAgB,CAAC;AACpC;","names":["platform","arch"]}
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
resolveTransformer,
|
|
17
17
|
sendError,
|
|
18
18
|
warnOnce
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-TPCT3MXV.js";
|
|
20
20
|
import {
|
|
21
21
|
loadConfig,
|
|
22
22
|
loadEnv
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-XP7YXRHO.js";
|
|
24
|
+
import "./chunk-X4JAX2U3.js";
|
|
25
25
|
import "./chunk-WZ7CPVQB.js";
|
|
26
26
|
import {
|
|
27
27
|
loadManifest
|
|
@@ -47,7 +47,7 @@ import { resolve } from "path";
|
|
|
47
47
|
async function configureAgentRegistryFromConfig(registryConfig) {
|
|
48
48
|
if (registryConfig === void 0) return;
|
|
49
49
|
try {
|
|
50
|
-
const sdk = await import("./dist-
|
|
50
|
+
const sdk = await import("./dist-E6BL4ZVY.js").catch(() => null);
|
|
51
51
|
const sdkConfigure = sdk?.Agent?.registry?.configure;
|
|
52
52
|
if (sdkConfigure === void 0) return;
|
|
53
53
|
const { configureAgentRegistryOnce } = await import("./configure-agent-registry-6YGTJYBC.js");
|
|
@@ -71,7 +71,7 @@ async function configureStorageManagerFromConfig(storageConfig) {
|
|
|
71
71
|
if (storageConfig === void 0 || storageConfig === null) return;
|
|
72
72
|
try {
|
|
73
73
|
const { getStorageManager } = await import("./storage-manager-D5KBXXKB.js");
|
|
74
|
-
const { storageSchema } = await import("./schema-
|
|
74
|
+
const { storageSchema } = await import("./schema-VR6YNVLQ.js");
|
|
75
75
|
const parsed = storageSchema.parse(storageConfig);
|
|
76
76
|
getStorageManager().configure(parsed);
|
|
77
77
|
} catch (err) {
|
|
@@ -101,7 +101,7 @@ function installGracefulShutdown(server) {
|
|
|
101
101
|
[theokit] ${signal} received \u2014 evicting agents`);
|
|
102
102
|
void (async () => {
|
|
103
103
|
try {
|
|
104
|
-
const sdk = await import("./dist-
|
|
104
|
+
const sdk = await import("./dist-E6BL4ZVY.js").catch(() => null);
|
|
105
105
|
if (sdk?.Agent?.registry?.evictAll !== void 0) {
|
|
106
106
|
await sdk.Agent.registry.evictAll();
|
|
107
107
|
}
|
|
@@ -612,4 +612,4 @@ export {
|
|
|
612
612
|
resolveSsrEntry,
|
|
613
613
|
startCommand
|
|
614
614
|
};
|
|
615
|
-
//# sourceMappingURL=start-
|
|
615
|
+
//# sourceMappingURL=start-UX2IPPSH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/start.ts","../src/cli/commands/start-bootstrap-stages.ts","../src/cli/commands/start-graceful-shutdown.ts","../src/cli/commands/start-manifest-loader.ts","../src/cli/commands/start-request-handler.ts","../src/cli/commands/start-handlers.ts","../src/server/http/static.ts","../src/cli/commands/start-ssr-setup.ts","../src/cli/commands/start-websocket-handler.ts"],"sourcesContent":["/**\n * theokit start — production server orchestration spine.\n *\n * T4.2 (architecture-cleanup, ADR-0017): stages extracted to sibling modules.\n * - start-bootstrap-stages.ts — config/registry/storage bootstrap + resolveSsrEntry\n * - start-manifest-loader.ts — manifest.json or scan fallback\n * - start-ssr-setup.ts — SSR entry-server + HTML template split\n * - start-handlers.ts — branch handlers (action/route/static/404)\n * - start-request-handler.ts — request lifecycle wiring\n * - start-websocket-handler.ts — WS upgrade (opt-in)\n * - start-graceful-shutdown.ts — SIGTERM/SIGINT drain\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createServer } from 'node:http'\nimport { join, resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { loadEnv } from '../../config/load-env.js'\nimport { createPluginRunnerFromConfig } from '../../server/plugins/load-plugins.js'\nimport { createRateLimiter } from '../../server/rate-limit/rate-limit.js'\nimport { createProductionLoader } from '../../server/scan/module-loader.js'\nimport { resolveTransformer } from '../../server/transformer.js'\nimport { preflightNodeAndBindings } from '../preflight-node-version.js'\n\nimport {\n configureAgentRegistryFromConfig,\n configureStorageManagerFromConfig,\n} from './start-bootstrap-stages.js'\nimport { installGracefulShutdown } from './start-graceful-shutdown.js'\nimport type { RequestHandlerCtx } from './start-handlers.js'\nimport { loadRoutesAndActions } from './start-manifest-loader.js'\nimport { createRequestHandler } from './start-request-handler.js'\nimport { setupSsr } from './start-ssr-setup.js'\nimport { attachWebSocketHandler } from './start-websocket-handler.js'\n\n// Backwards-compat: external test fixtures may import resolveSsrEntry from here.\nexport { resolveSsrEntry } from './start-bootstrap-stages.js'\n\ninterface StartOptions {\n port?: number\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const cwd = process.cwd()\n // Preflight (FIRST — BEFORE anything that touches native bindings).\n preflightNodeAndBindings(cwd)\n loadEnv({ cwd, mode: 'production' })\n const config = await loadConfig(cwd)\n\n await configureAgentRegistryFromConfig(config.agents?.registry)\n await configureStorageManagerFromConfig(config.storage)\n\n const distDir = resolve(cwd, '.theo')\n const clientDir = resolve(distDir, 'client')\n const serverDir = resolve(cwd, 'server')\n\n if (!existsSync(clientDir)) {\n throw new Error('No build found. Run `theo build` first.')\n }\n\n const indexHtml = readFileSync(join(clientDir, 'index.html'), 'utf-8')\n const loadModule = createProductionLoader()\n const port = options.port ?? config.port\n const pluginRunner = await createPluginRunnerFromConfig(config.plugins)\n const transformer = resolveTransformer(config.serialization)\n\n const custom404Path = join(clientDir, '404.html')\n const custom500Path = join(clientDir, '500.html')\n const custom404Html = existsSync(custom404Path) ? readFileSync(custom404Path, 'utf-8') : null\n const custom500Html = existsSync(custom500Path) ? readFileSync(custom500Path, 'utf-8') : null\n\n const {\n routes: cachedRoutes,\n actions: cachedActions,\n wsRoutes: cachedWsRoutes,\n } = loadRoutesAndActions(distDir, serverDir)\n\n // Rate limiter (legacy flat form only — per-route variant is handled in\n // api-middleware integration path, not this fallback).\n const flatRateLimit =\n config.rateLimit && 'windowMs' in config.rateLimit && 'max' in config.rateLimit\n ? config.rateLimit\n : undefined\n const rateLimiter = flatRateLimit ? createRateLimiter(flatRateLimit) : null\n\n const ssr = await setupSsr({\n distDir,\n indexHtml,\n ssrConfigEnabled: config.ssr,\n ssrStreamingConfig: config.ssrStreaming,\n })\n\n const server = createServer(\n createRequestHandler({\n buildCtx: (req, res, requestId, startTime): RequestHandlerCtx => ({\n req,\n res,\n url: req.url ?? '/',\n requestId,\n startTime,\n clientDir,\n custom404Html,\n cachedRoutes,\n cachedActions,\n loadModule,\n serverDir,\n pluginRunner,\n transformer,\n csrfMode: config.security?.csrf ?? 'strict',\n disallowed: config.security?.disallowed,\n rateLimiter,\n }),\n securityHeadersConfig: config.security?.headers ?? {},\n ssrRender: ssr.render,\n ssrRenderStreaming: ssr.renderStreaming,\n ssrStreamingEnabled: ssr.streamingEnabled,\n htmlHead: ssr.htmlHead,\n htmlTail: ssr.htmlTail,\n indexHtml,\n custom500Html,\n }),\n )\n\n await attachWebSocketHandler(server, cachedWsRoutes, loadModule)\n\n server.listen(port, () => {\n console.log(`\\n Theo production server`)\n console.log(` → http://localhost:${String(port)}\\n`)\n })\n\n installGracefulShutdown(server)\n}\n","/**\n * Bootstrap stages extracted from `start.ts` per T4.2 (architecture-cleanup, ADR-0017).\n *\n * The full goal of T4.2 is a ≤30-LOC `startCommand` spine + 6-8 stage files. This\n * file ships the first batch: the configure-from-config bootstrap helpers + the\n * SSR entry resolver. They are stand-alone, side-effect-free at module load, and\n * already individually testable.\n *\n * Remaining stages (request-handler extraction, graceful-shutdown extraction,\n * signal-handlers extraction) are deferred to a follow-up sprint — see plan\n * `docs/plans/architecture-cleanup-plan.md` T4.2 Deferred sub-tasks.\n */\n\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { warnOnce } from '../../server/observability/logger.js'\n\ninterface SdkAgentRegistry {\n configure?: (opts: { maxAgents?: number; idleTimeoutMs?: number }) => void\n}\ninterface SdkModule {\n Agent?: { registry?: SdkAgentRegistry }\n}\n\n/**\n * Configure SDK's Agent.registry from `theo.config.ts > agents.registry`.\n * Lazy at boot; EC-3 sync flag flip prevents race under concurrent boot.\n * Silent no-op when registry config is absent or SDK is uninstalled.\n */\nexport async function configureAgentRegistryFromConfig(\n registryConfig: { maxAgents: number; idleTimeoutMs: number } | undefined,\n): Promise<void> {\n if (registryConfig === undefined) return\n try {\n const sdk = (await import('@usetheo/sdk').catch(() => null)) as SdkModule | null\n const sdkConfigure = sdk?.Agent?.registry?.configure\n if (sdkConfigure === undefined) return\n const { configureAgentRegistryOnce } =\n await import('../../server/agent/configure-agent-registry.js')\n configureAgentRegistryOnce(\n {\n configure: (opts) => {\n sdkConfigure(opts)\n },\n },\n registryConfig,\n )\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('bootstrap.agent_registry_skip', {\n event: 'bootstrap.agent_registry_skip',\n message: msg,\n })\n }\n}\n\n/**\n * Configure the StorageManager from `theo.config.ts > storage` (ADR-0007).\n * Manager enforces configure-once internally (D3); this helper bridges the\n * config to the singleton with actionable error handling.\n */\nexport async function configureStorageManagerFromConfig(storageConfig: unknown): Promise<void> {\n if (storageConfig === undefined || storageConfig === null) return\n try {\n const { getStorageManager } = await import('../../server/storage/storage-manager.js')\n const { storageSchema } = await import('../../config/schema.js')\n // Re-validate at boot so a malformed config from a non-Zod source\n // (test fixtures, dynamic configs) surfaces a clear error early.\n const parsed = storageSchema.parse(storageConfig)\n getStorageManager().configure(parsed)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('bootstrap.storage_skip', {\n event: 'bootstrap.storage_skip',\n message: msg,\n })\n }\n}\n\nconst SSR_EXTENSIONS = ['.mjs', '.js'] as const\n\n/**\n * Resolve the SSR entry-server module path. tsup may emit `.mjs` or `.js`\n * depending on output format. Try `.mjs` first (modern default) then fall\n * back to `.js`. Returns null when neither exists — SSR stays disabled.\n *\n * Exported so unit tests can pin the resolution order without booting the\n * full CLI.\n */\nexport function resolveSsrEntry(distDir: string): string | null {\n for (const ext of SSR_EXTENSIONS) {\n const path = resolve(distDir, `server/entry-server${ext}`)\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- distDir is from `theokit start`'s own caller-controlled config; the suffix is from a const literal array\n if (existsSync(path)) return path\n }\n return null\n}\n","/**\n * Graceful shutdown stage for `theokit start` (T4.2 architecture-cleanup,\n * ADR-0007 D6 — SIGTERM evicts agents + drains StorageManager).\n *\n * EC-13: SIGTERM evicts agents IMMEDIATELY (no per-request drain). In-flight\n * requests get aborted mid-stream — acceptable because the platform LB\n * removed this pod from rotation BEFORE sending SIGTERM (K8s preStop hook +\n * terminationGracePeriodSeconds; same on Vercel/CF/Render).\n *\n * Re-entry guard: multiple SIGTERMs in quick succession run shutdown ONCE.\n */\n\nimport type { Server as HttpServer } from 'node:http'\n\nimport { warnOnce } from '../../server/observability/logger.js'\n\nexport function installGracefulShutdown(server: HttpServer): void {\n let shuttingDown = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (shuttingDown) return\n shuttingDown = true\n console.log(`\\n [theokit] ${signal} received — evicting agents`)\n void (async () => {\n // Lazy-import SDK only at shutdown time to avoid forcing the dep on\n // apps that don't use agents at all.\n try {\n const sdk = (await import('@usetheo/sdk').catch(() => null)) as {\n Agent?: { registry?: { evictAll?: () => Promise<void> } }\n } | null\n if (sdk?.Agent?.registry?.evictAll !== undefined) {\n await sdk.Agent.registry.evictAll()\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('shutdown.evict_error', {\n event: 'shutdown.evict_error',\n message: msg,\n })\n }\n // T3.1 — drain the StorageManager AFTER agent eviction. Order matters:\n // agents may still hold open pool refs while evicting; closing pools\n // first would break in-flight queries.\n try {\n const { getStorageManager } = await import('../../server/storage/storage-manager.js')\n const manager = getStorageManager()\n await manager.dispose()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('shutdown.dispose_error', {\n event: 'shutdown.dispose_error',\n message: msg,\n })\n }\n console.log(` [theokit] shutdown complete`)\n server.close(() => {\n process.exit(0)\n })\n setTimeout(() => {\n warnOnce('shutdown.forced_exit', {\n event: 'shutdown.forced_exit',\n message: 'forced exit after 25s timeout',\n })\n process.exit(0)\n }, 25_000).unref()\n })()\n }\n process.on('SIGTERM', () => {\n shutdown('SIGTERM')\n })\n process.on('SIGINT', () => {\n shutdown('SIGINT')\n })\n}\n","/**\n * Manifest loading stage for `theokit start` (T4.2 architecture-cleanup).\n *\n * Loads pre-built manifest from `.theo/manifest.json` if present; otherwise\n * scans server/ for routes/actions/ws at startup with a structured warn.\n */\n\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { warnOnce } from '../../server/observability/logger.js'\nimport type { ActionNode } from '../../server/scan/action-scan.js'\nimport { scanServerActions } from '../../server/scan/action-scan.js'\nimport { loadManifest } from '../../server/scan/manifest.js'\nimport type { ServerRouteNode } from '../../server/scan/match.js'\nimport { scanServerRoutes } from '../../server/scan/scan.js'\nimport type { WebSocketRouteNode } from '../../server/scan/ws-scan.js'\nimport { scanWebSocketRoutes } from '../../server/scan/ws-scan.js'\n\nexport interface LoadedRoutes {\n routes: ServerRouteNode[]\n actions: ActionNode[]\n wsRoutes: WebSocketRouteNode[]\n}\n\nexport function loadRoutesAndActions(distDir: string, serverDir: string): LoadedRoutes {\n const manifestPath = join(distDir, 'manifest.json')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- distDir is from `theokit start`'s own caller-controlled cwd\n if (existsSync(manifestPath)) {\n const manifest = loadManifest(distDir, serverDir)\n return {\n routes: manifest.routes,\n actions: manifest.actions,\n wsRoutes: manifest.websockets,\n }\n }\n warnOnce('bootstrap.manifest_not_found', {\n event: 'bootstrap.manifest_not_found',\n message:\n 'No manifest found, scanning routes at startup. Run \"theo build\" to generate manifest.',\n serverDir,\n })\n return {\n routes: scanServerRoutes(serverDir),\n actions: scanServerActions(serverDir),\n wsRoutes: scanWebSocketRoutes(serverDir),\n }\n}\n","/**\n * Inline request handler for `theokit start` (T4.2 architecture-cleanup).\n *\n * Wires the per-request flow: security headers → action/route/static branches\n * → SSR fallback → CSR fallback → 500 page.\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport { generateNonce } from '../../server/auth/nonce.js'\nimport { sendError } from '../../server/http/send-response.js'\nimport { buildSecurityHeaders } from '../../server/security/security-headers.js'\n\nimport {\n tryServeAction,\n tryServeApiRoute,\n tryServeCustom404,\n tryServeStatic,\n type RequestHandlerCtx,\n} from './start-handlers.js'\nimport {\n isSsrRenderResult,\n type SsrRender,\n type SsrRenderResult,\n type SsrRenderStreaming,\n} from './start-ssr-setup.js'\n\nexport interface RequestHandlerContext {\n buildCtx: (\n req: IncomingMessage,\n res: ServerResponse,\n requestId: string,\n startTime: number,\n ) => RequestHandlerCtx\n securityHeadersConfig: Parameters<typeof buildSecurityHeaders>[0]\n ssrRender: SsrRender | null\n ssrRenderStreaming: SsrRenderStreaming | null\n ssrStreamingEnabled: boolean\n htmlHead: string\n htmlTail: string\n indexHtml: string\n custom500Html: string | null\n}\n\nfunction asSsrRenderResult(value: SsrRenderResult): SsrRenderResult {\n return value\n}\n\n/* eslint-disable complexity, sonarjs/cognitive-complexity --\n * inline request orchestrator co-located so the lifecycle is readable end-to-end.\n */\nexport function createRequestHandler(\n ctx: RequestHandlerContext,\n): (req: IncomingMessage, res: ServerResponse) => void {\n return (req: IncomingMessage, res: ServerResponse) => {\n void (async () => {\n const url = req.url ?? '/'\n const requestId = randomUUID()\n const start = Date.now()\n\n // Per-request nonce (EC-6 dev/prod parity).\n const nonce = generateNonce()\n const securityHeaders = buildSecurityHeaders(\n ctx.securityHeadersConfig,\n { production: true },\n { nonce },\n )\n for (const [k, v] of Object.entries(securityHeaders)) {\n res.setHeader(k, v)\n }\n\n const handlerCtx = ctx.buildCtx(req, res, requestId, start)\n\n try {\n if (await tryServeAction(handlerCtx)) return\n if (await tryServeApiRoute(handlerCtx)) return\n if (tryServeStatic(handlerCtx)) return\n if (tryServeCustom404(handlerCtx)) return\n\n // SSR streaming branch\n if (ctx.ssrStreamingEnabled && ctx.ssrRenderStreaming) {\n const controller = new AbortController()\n const onClose = (): void => {\n controller.abort()\n }\n req.on('close', onClose)\n try {\n const result = await ctx.ssrRenderStreaming(url, res, {\n signal: controller.signal,\n nonce,\n })\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n return\n } catch (streamErr) {\n console.error('[SSR Stream Error]', (streamErr as Error).message)\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n }\n if (!res.writableEnded) {\n res.end(ctx.custom500Html ?? '<h1>500 — Server Error</h1>')\n }\n return\n } finally {\n req.removeListener('close', onClose)\n }\n }\n\n // SSR (non-streaming) branch\n if (ctx.ssrRender) {\n try {\n const result = await ctx.ssrRender(url, { nonce })\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n let ssrHtml = ''\n let hydrationScript = ''\n if (typeof result === 'string') {\n ssrHtml = result\n } else if (isSsrRenderResult(result)) {\n const rendered = asSsrRenderResult(result)\n ssrHtml = rendered.html\n const dataJson = JSON.stringify(rendered.hydrationData).replace(/</g, '\\\\u003c')\n hydrationScript = `<script${\n nonce ? ` nonce=\"${nonce}\"` : ''\n }>window.__staticRouterHydrationData=${dataJson}</script>`\n }\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(ctx.htmlHead + ssrHtml + hydrationScript + ctx.htmlTail)\n return\n } catch (ssrErr) {\n console.error('[SSR Error] Falling back to CSR:', (ssrErr as Error).message)\n // Fall through to CSR fallback\n }\n }\n\n // CSR fallback\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(ctx.indexHtml)\n } catch (err) {\n if (ctx.custom500Html && !res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n res.end(ctx.custom500Html)\n } else if (!res.headersSent) {\n sendError(res, 'INTERNAL_ERROR', (err as Error).message, 500)\n } else {\n res.end()\n }\n }\n })()\n }\n}\n/* eslint-enable complexity, sonarjs/cognitive-complexity */\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { extname } from 'node:path'\n\nimport { executeAction } from '../../server/http/action-execute.js'\nimport { executeRoute } from '../../server/http/execute.js'\nimport { sendError } from '../../server/http/send-response.js'\nimport { serveStaticFile } from '../../server/http/static.js'\nimport { logRequest } from '../../server/observability/logger.js'\nimport { findSuggestion } from '../../server/observability/suggest.js'\nimport type { PluginRunner } from '../../server/plugins/plugin-runner.js'\nimport type { ActionNode } from '../../server/scan/action-scan.js'\nimport { matchRoute } from '../../server/scan/match.js'\nimport type { ServerRouteNode } from '../../server/scan/match.js'\nimport type { LoadModule } from '../../server/scan/module-loader.js'\nimport type { CsrfMode, DisallowedConfig } from '../../server/security/csrf.js'\nimport type { TheoTransformer } from '../../server/transformer.js'\n\n/**\n * T6.1 (PV-7 SRP): start.ts request orchestrator decomposed into 5 focused\n * per-branch handlers. Each handler returns `true` if it handled the\n * request (response sent) so the orchestrator can stop iterating.\n *\n * The original 455-LOC monolith closed over 14+ locals; the shared shape\n * `RequestHandlerCtx` makes the dependencies explicit + reviewable.\n */\nexport interface RequestHandlerCtx {\n req: IncomingMessage\n res: ServerResponse\n url: string\n requestId: string\n startTime: number\n // Pre-loaded build artifacts\n clientDir: string\n custom404Html: string | null\n // Manifest-resolved tables\n cachedRoutes: ServerRouteNode[]\n cachedActions: ActionNode[]\n // Runtime infra\n loadModule: LoadModule\n serverDir: string\n pluginRunner: PluginRunner | undefined\n transformer: TheoTransformer | undefined\n csrfMode: CsrfMode\n disallowed: DisallowedConfig | undefined\n rateLimiter:\n | ((req: IncomingMessage) => { limited: boolean; headers: Record<string, string> })\n | null\n}\n\n/** Apply rate limit; return true if request was limited (response sent). */\nfunction applyRateLimit(c: RequestHandlerCtx, method: string): boolean {\n if (!c.rateLimiter) return false\n const check = c.rateLimiter(c.req)\n for (const [k, v] of Object.entries(check.headers)) c.res.setHeader(k, v)\n if (check.limited) {\n sendError(c.res, 'RATE_LIMITED', 'Too many requests', 429, undefined, c.requestId)\n logRequest({\n method,\n url: c.url,\n status: 429,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n return false\n}\n\n/** Branch 1: action routes (`/api/__actions/{file}/{exportName}`). */\nexport async function tryServeAction(c: RequestHandlerCtx): Promise<boolean> {\n if (!c.url.startsWith('/api/__actions/')) return false\n c.res.setHeader('x-request-id', c.requestId)\n\n if (applyRateLimit(c, c.req.method ?? 'POST')) return true\n\n const pathAfterPrefix = c.url.slice('/api/__actions/'.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n if (segments.length < 2) {\n sendError(\n c.res,\n 'BAD_REQUEST',\n 'Action URL must be /api/__actions/{file}/{exportName}',\n 400,\n undefined,\n c.requestId,\n )\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: 400,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n const action = c.cachedActions.find((a) => a.actionPath === actionPath)\n if (!action) {\n const actionPaths = c.cachedActions.map((a) => a.actionPath)\n const suggestion = findSuggestion(actionPath, actionPaths)\n const msg = suggestion\n ? `Action \"${actionPath}\" not found. Did you mean: ${suggestion}?`\n : `Action \"${actionPath}\" not found`\n sendError(c.res, 'NOT_FOUND', msg, 404, undefined, c.requestId)\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: 404,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n await executeAction(\n action.filePath,\n exportName,\n c.req,\n c.res,\n c.loadModule,\n c.serverDir,\n c.requestId,\n c.pluginRunner,\n c.csrfMode,\n c.disallowed,\n )\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: c.res.statusCode,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n}\n\n/** Branch 2: API routes (`/api/*` excluding actions). */\nexport async function tryServeApiRoute(c: RequestHandlerCtx): Promise<boolean> {\n if (!c.url.startsWith('/api/')) return false\n c.res.setHeader('x-request-id', c.requestId)\n\n if (applyRateLimit(c, c.req.method ?? 'GET')) return true\n\n const match = matchRoute(c.url, c.cachedRoutes)\n if (!match) {\n const urlPath = c.url.split('?')[0]\n const routePaths = c.cachedRoutes.map((r) => r.routePath)\n const suggestion = findSuggestion(urlPath, routePaths)\n const msg = suggestion\n ? `API route not found: ${urlPath}. Did you mean: ${suggestion}?`\n : 'API route not found'\n sendError(c.res, 'NOT_FOUND', msg, 404, undefined, c.requestId)\n logRequest({\n method: c.req.method ?? 'GET',\n url: c.url,\n status: 404,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n const method = (c.req.method ?? 'GET').toUpperCase()\n // T3.1 (ADR-0016) — context object replaces 12 positional args\n await executeRoute({\n route: match.route,\n method,\n params: match.params,\n req: c.req,\n res: c.res,\n loadModule: c.loadModule,\n serverDir: c.serverDir,\n requestId: c.requestId,\n pluginRunner: c.pluginRunner,\n transformer: c.transformer,\n csrfMode: c.csrfMode,\n disallowed: c.disallowed,\n })\n logRequest({\n method,\n url: c.url,\n status: c.res.statusCode,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n}\n\n/** Branch 3: static files (returns true if a static asset was served). */\nexport function tryServeStatic(c: RequestHandlerCtx): boolean {\n return serveStaticFile(c.req, c.res, c.clientDir)\n}\n\n/** Branch 4: custom 404 for URLs that look like missing assets. */\nexport function tryServeCustom404(c: RequestHandlerCtx): boolean {\n const urlPath = c.url.split('?')[0]\n if (c.custom404Html && extname(urlPath)) {\n c.res.writeHead(404, { 'Content-Type': 'text/html' })\n c.res.end(c.custom404Html)\n return true\n }\n return false\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Static-file server. The URL path IS user-controlled, BUT before any fs\n * call we resolve to absolute and reject with 403 if the resolved path\n * escapes `clientDir` (see EC-1 guard at line below). The guard is\n * authoritative; the rule cannot see it.\n */\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { resolve, extname } from 'node:path'\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain',\n '.map': 'application/json',\n}\n\nexport function serveStaticFile(\n req: IncomingMessage,\n res: ServerResponse,\n clientDir: string,\n): boolean {\n const urlPath = (req.url ?? '/').split('?')[0]\n\n // Path traversal prevention (EC-1)\n const filePath = resolve(clientDir, '.' + urlPath)\n if (!filePath.startsWith(clientDir)) {\n res.writeHead(403)\n res.end('Forbidden')\n return true\n }\n\n if (!existsSync(filePath)) return false\n\n const stat = statSync(filePath)\n if (!stat.isFile()) return false\n\n const ext = extname(filePath)\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream'\n const content = readFileSync(filePath)\n\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': content.length,\n })\n res.end(content)\n return true\n}\n","/**\n * SSR setup stage for `theokit start` (T4.2 architecture-cleanup).\n *\n * Loads the SSR entry-server module if configured + builds template split\n * around the React root div. Returns null renderers when SSR is disabled.\n */\n\nimport type { ServerResponse } from 'node:http'\n\nimport { resolveSsrEntry } from './start-bootstrap-stages.js'\n\nexport interface SsrRenderResult {\n html: string\n hydrationData: {\n loaderData?: unknown\n actionData?: unknown\n errors?: unknown\n }\n}\n\nexport type RenderStreamingResult = { redirect: Response } | { streaming: true } | undefined\n\nexport type SsrRender = (\n url: string,\n options?: { nonce?: string },\n) => Promise<SsrRenderResult | { redirect: Response } | string>\n\nexport type SsrRenderStreaming = (\n url: string,\n response: ServerResponse,\n options?: { signal?: AbortSignal; nonce?: string },\n) => Promise<RenderStreamingResult>\n\nexport interface SsrSetupResult {\n enabled: boolean\n streamingEnabled: boolean\n render: SsrRender | null\n renderStreaming: SsrRenderStreaming | null\n htmlHead: string\n htmlTail: string\n}\n\nexport function isSsrRenderResult(value: unknown): value is SsrRenderResult {\n if (typeof value !== 'object' || value === null) return false\n if (!('html' in value)) return false\n const html = (value as Record<string, unknown>).html\n if (typeof html !== 'string') return false\n return true\n}\n\ninterface SsrEntryServer {\n render: SsrRender\n renderStreaming?: SsrRenderStreaming\n}\n\nexport async function setupSsr(opts: {\n distDir: string\n indexHtml: string\n ssrConfigEnabled: boolean\n ssrStreamingConfig: boolean | undefined\n}): Promise<SsrSetupResult> {\n const ssrServerPath: string | null = opts.ssrConfigEnabled ? resolveSsrEntry(opts.distDir) : null\n const enabled = ssrServerPath !== null\n const streamingEnabled = enabled && Boolean(opts.ssrStreamingConfig)\n\n if (ssrServerPath === null) {\n return {\n enabled: false,\n streamingEnabled: false,\n render: null,\n renderStreaming: null,\n htmlHead: '',\n htmlTail: '',\n }\n }\n\n const mod = (await import(ssrServerPath)) as SsrEntryServer\n const render = mod.render\n const renderStreaming = typeof mod.renderStreaming === 'function' ? mod.renderStreaming : null\n\n // Split HTML template on root div\n let htmlHead = ''\n let htmlTail = ''\n const rootDivMatch = /<div id=[\"']root[\"'][^>]*>/.exec(opts.indexHtml)\n if (rootDivMatch) {\n const splitIdx = opts.indexHtml.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n htmlHead = opts.indexHtml.slice(0, splitIdx)\n htmlTail = opts.indexHtml.slice(splitIdx)\n }\n\n return { enabled, streamingEnabled, render, renderStreaming, htmlHead, htmlTail }\n}\n","/**\n * WebSocket upgrade handler for `theokit start` (T4.2 architecture-cleanup).\n *\n * Wires `server.on('upgrade')` for declared WS routes. Opt-in: only attached\n * when `wsRoutes.length > 0`. Lazy-imports `ws` package — throws an actionable\n * error when wsRoutes declared but `ws` not installed.\n */\n\nimport type { Server as HttpServer } from 'node:http'\n\nimport type * as WsLib from 'ws'\n\nimport type { WebSocketHandler } from '../../server/define/define-websocket.js'\nimport type { LoadModule } from '../../server/scan/module-loader.js'\nimport type { WebSocketRouteNode } from '../../server/scan/ws-scan.js'\n\nexport async function attachWebSocketHandler(\n server: HttpServer,\n wsRoutes: WebSocketRouteNode[],\n loadModule: LoadModule,\n): Promise<void> {\n if (wsRoutes.length === 0) return\n\n let WebSocketServerCtor: typeof WsLib.WebSocketServer\n try {\n const wsModule = await import('ws')\n WebSocketServerCtor = wsModule.WebSocketServer\n } catch {\n throw new Error('WebSocket routes found but \"ws\" package is not installed. Run: npm install ws')\n }\n\n const wss = new WebSocketServerCtor({ noServer: true })\n\n server.on('upgrade', (request, socket, head) => {\n void (async () => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) {\n socket.destroy()\n return\n }\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find((r) => r.wsPath === wsPath)\n if (!match) {\n socket.destroy()\n return\n }\n\n try {\n const mod = await loadModule(match.filePath)\n const handler = ((mod as { default?: unknown }).default ?? mod) as WebSocketHandler\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => {\n handler.onMessage?.(ws, data.toString())\n })\n ws.on('close', (code: number, reason: Buffer) => {\n handler.onClose?.(ws, code, reason)\n })\n ws.on('error', (err: Error) => {\n handler.onError?.(ws, err)\n })\n })\n } catch {\n socket.destroy()\n }\n })()\n })\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","resolve","existsSync","existsSync","extname","existsSync","resolve","extname","resolve","existsSync","readFileSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/start.ts","../src/cli/commands/start-bootstrap-stages.ts","../src/cli/commands/start-graceful-shutdown.ts","../src/cli/commands/start-manifest-loader.ts","../src/cli/commands/start-request-handler.ts","../src/cli/commands/start-handlers.ts","../src/server/http/static.ts","../src/cli/commands/start-ssr-setup.ts","../src/cli/commands/start-websocket-handler.ts"],"sourcesContent":["/**\n * theokit start — production server orchestration spine.\n *\n * T4.2 (architecture-cleanup, ADR-0017): stages extracted to sibling modules.\n * - start-bootstrap-stages.ts — config/registry/storage bootstrap + resolveSsrEntry\n * - start-manifest-loader.ts — manifest.json or scan fallback\n * - start-ssr-setup.ts — SSR entry-server + HTML template split\n * - start-handlers.ts — branch handlers (action/route/static/404)\n * - start-request-handler.ts — request lifecycle wiring\n * - start-websocket-handler.ts — WS upgrade (opt-in)\n * - start-graceful-shutdown.ts — SIGTERM/SIGINT drain\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createServer } from 'node:http'\nimport { join, resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { loadEnv } from '../../config/load-env.js'\nimport { createPluginRunnerFromConfig } from '../../server/plugins/load-plugins.js'\nimport { createRateLimiter } from '../../server/rate-limit/rate-limit.js'\nimport { createProductionLoader } from '../../server/scan/module-loader.js'\nimport { resolveTransformer } from '../../server/transformer.js'\nimport { preflightNodeAndBindings } from '../preflight-node-version.js'\n\nimport {\n configureAgentRegistryFromConfig,\n configureStorageManagerFromConfig,\n} from './start-bootstrap-stages.js'\nimport { installGracefulShutdown } from './start-graceful-shutdown.js'\nimport type { RequestHandlerCtx } from './start-handlers.js'\nimport { loadRoutesAndActions } from './start-manifest-loader.js'\nimport { createRequestHandler } from './start-request-handler.js'\nimport { setupSsr } from './start-ssr-setup.js'\nimport { attachWebSocketHandler } from './start-websocket-handler.js'\n\n// Backwards-compat: external test fixtures may import resolveSsrEntry from here.\nexport { resolveSsrEntry } from './start-bootstrap-stages.js'\n\ninterface StartOptions {\n port?: number\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const cwd = process.cwd()\n // Preflight (FIRST — BEFORE anything that touches native bindings).\n preflightNodeAndBindings(cwd)\n loadEnv({ cwd, mode: 'production' })\n const config = await loadConfig(cwd)\n\n await configureAgentRegistryFromConfig(config.agents?.registry)\n await configureStorageManagerFromConfig(config.storage)\n\n const distDir = resolve(cwd, '.theo')\n const clientDir = resolve(distDir, 'client')\n const serverDir = resolve(cwd, 'server')\n\n if (!existsSync(clientDir)) {\n throw new Error('No build found. Run `theo build` first.')\n }\n\n const indexHtml = readFileSync(join(clientDir, 'index.html'), 'utf-8')\n const loadModule = createProductionLoader()\n const port = options.port ?? config.port\n const pluginRunner = await createPluginRunnerFromConfig(config.plugins)\n const transformer = resolveTransformer(config.serialization)\n\n const custom404Path = join(clientDir, '404.html')\n const custom500Path = join(clientDir, '500.html')\n const custom404Html = existsSync(custom404Path) ? readFileSync(custom404Path, 'utf-8') : null\n const custom500Html = existsSync(custom500Path) ? readFileSync(custom500Path, 'utf-8') : null\n\n const {\n routes: cachedRoutes,\n actions: cachedActions,\n wsRoutes: cachedWsRoutes,\n } = loadRoutesAndActions(distDir, serverDir)\n\n // Rate limiter (legacy flat form only — per-route variant is handled in\n // api-middleware integration path, not this fallback).\n const flatRateLimit =\n config.rateLimit && 'windowMs' in config.rateLimit && 'max' in config.rateLimit\n ? config.rateLimit\n : undefined\n const rateLimiter = flatRateLimit ? createRateLimiter(flatRateLimit) : null\n\n const ssr = await setupSsr({\n distDir,\n indexHtml,\n ssrConfigEnabled: config.ssr,\n ssrStreamingConfig: config.ssrStreaming,\n })\n\n const server = createServer(\n createRequestHandler({\n buildCtx: (req, res, requestId, startTime): RequestHandlerCtx => ({\n req,\n res,\n url: req.url ?? '/',\n requestId,\n startTime,\n clientDir,\n custom404Html,\n cachedRoutes,\n cachedActions,\n loadModule,\n serverDir,\n pluginRunner,\n transformer,\n csrfMode: config.security?.csrf ?? 'strict',\n disallowed: config.security?.disallowed,\n rateLimiter,\n }),\n securityHeadersConfig: config.security?.headers ?? {},\n ssrRender: ssr.render,\n ssrRenderStreaming: ssr.renderStreaming,\n ssrStreamingEnabled: ssr.streamingEnabled,\n htmlHead: ssr.htmlHead,\n htmlTail: ssr.htmlTail,\n indexHtml,\n custom500Html,\n }),\n )\n\n await attachWebSocketHandler(server, cachedWsRoutes, loadModule)\n\n server.listen(port, () => {\n console.log(`\\n Theo production server`)\n console.log(` → http://localhost:${String(port)}\\n`)\n })\n\n installGracefulShutdown(server)\n}\n","/**\n * Bootstrap stages extracted from `start.ts` per T4.2 (architecture-cleanup, ADR-0017).\n *\n * The full goal of T4.2 is a ≤30-LOC `startCommand` spine + 6-8 stage files. This\n * file ships the first batch: the configure-from-config bootstrap helpers + the\n * SSR entry resolver. They are stand-alone, side-effect-free at module load, and\n * already individually testable.\n *\n * Remaining stages (request-handler extraction, graceful-shutdown extraction,\n * signal-handlers extraction) are deferred to a follow-up sprint — see plan\n * `docs/plans/architecture-cleanup-plan.md` T4.2 Deferred sub-tasks.\n */\n\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { warnOnce } from '../../server/observability/logger.js'\n\ninterface SdkAgentRegistry {\n configure?: (opts: { maxAgents?: number; idleTimeoutMs?: number }) => void\n}\ninterface SdkModule {\n Agent?: { registry?: SdkAgentRegistry }\n}\n\n/**\n * Configure SDK's Agent.registry from `theo.config.ts > agents.registry`.\n * Lazy at boot; EC-3 sync flag flip prevents race under concurrent boot.\n * Silent no-op when registry config is absent or SDK is uninstalled.\n */\nexport async function configureAgentRegistryFromConfig(\n registryConfig: { maxAgents: number; idleTimeoutMs: number } | undefined,\n): Promise<void> {\n if (registryConfig === undefined) return\n try {\n const sdk = (await import('@theokit/sdk').catch(() => null)) as SdkModule | null\n const sdkConfigure = sdk?.Agent?.registry?.configure\n if (sdkConfigure === undefined) return\n const { configureAgentRegistryOnce } =\n await import('../../server/agent/configure-agent-registry.js')\n configureAgentRegistryOnce(\n {\n configure: (opts) => {\n sdkConfigure(opts)\n },\n },\n registryConfig,\n )\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('bootstrap.agent_registry_skip', {\n event: 'bootstrap.agent_registry_skip',\n message: msg,\n })\n }\n}\n\n/**\n * Configure the StorageManager from `theo.config.ts > storage` (ADR-0007).\n * Manager enforces configure-once internally (D3); this helper bridges the\n * config to the singleton with actionable error handling.\n */\nexport async function configureStorageManagerFromConfig(storageConfig: unknown): Promise<void> {\n if (storageConfig === undefined || storageConfig === null) return\n try {\n const { getStorageManager } = await import('../../server/storage/storage-manager.js')\n const { storageSchema } = await import('../../config/schema.js')\n // Re-validate at boot so a malformed config from a non-Zod source\n // (test fixtures, dynamic configs) surfaces a clear error early.\n const parsed = storageSchema.parse(storageConfig)\n getStorageManager().configure(parsed)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('bootstrap.storage_skip', {\n event: 'bootstrap.storage_skip',\n message: msg,\n })\n }\n}\n\nconst SSR_EXTENSIONS = ['.mjs', '.js'] as const\n\n/**\n * Resolve the SSR entry-server module path. tsup may emit `.mjs` or `.js`\n * depending on output format. Try `.mjs` first (modern default) then fall\n * back to `.js`. Returns null when neither exists — SSR stays disabled.\n *\n * Exported so unit tests can pin the resolution order without booting the\n * full CLI.\n */\nexport function resolveSsrEntry(distDir: string): string | null {\n for (const ext of SSR_EXTENSIONS) {\n const path = resolve(distDir, `server/entry-server${ext}`)\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- distDir is from `theokit start`'s own caller-controlled config; the suffix is from a const literal array\n if (existsSync(path)) return path\n }\n return null\n}\n","/**\n * Graceful shutdown stage for `theokit start` (T4.2 architecture-cleanup,\n * ADR-0007 D6 — SIGTERM evicts agents + drains StorageManager).\n *\n * EC-13: SIGTERM evicts agents IMMEDIATELY (no per-request drain). In-flight\n * requests get aborted mid-stream — acceptable because the platform LB\n * removed this pod from rotation BEFORE sending SIGTERM (K8s preStop hook +\n * terminationGracePeriodSeconds; same on Vercel/CF/Render).\n *\n * Re-entry guard: multiple SIGTERMs in quick succession run shutdown ONCE.\n */\n\nimport type { Server as HttpServer } from 'node:http'\n\nimport { warnOnce } from '../../server/observability/logger.js'\n\nexport function installGracefulShutdown(server: HttpServer): void {\n let shuttingDown = false\n const shutdown = (signal: NodeJS.Signals): void => {\n if (shuttingDown) return\n shuttingDown = true\n console.log(`\\n [theokit] ${signal} received — evicting agents`)\n void (async () => {\n // Lazy-import SDK only at shutdown time to avoid forcing the dep on\n // apps that don't use agents at all.\n try {\n const sdk = (await import('@theokit/sdk').catch(() => null)) as {\n Agent?: { registry?: { evictAll?: () => Promise<void> } }\n } | null\n if (sdk?.Agent?.registry?.evictAll !== undefined) {\n await sdk.Agent.registry.evictAll()\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('shutdown.evict_error', {\n event: 'shutdown.evict_error',\n message: msg,\n })\n }\n // T3.1 — drain the StorageManager AFTER agent eviction. Order matters:\n // agents may still hold open pool refs while evicting; closing pools\n // first would break in-flight queries.\n try {\n const { getStorageManager } = await import('../../server/storage/storage-manager.js')\n const manager = getStorageManager()\n await manager.dispose()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n warnOnce('shutdown.dispose_error', {\n event: 'shutdown.dispose_error',\n message: msg,\n })\n }\n console.log(` [theokit] shutdown complete`)\n server.close(() => {\n process.exit(0)\n })\n setTimeout(() => {\n warnOnce('shutdown.forced_exit', {\n event: 'shutdown.forced_exit',\n message: 'forced exit after 25s timeout',\n })\n process.exit(0)\n }, 25_000).unref()\n })()\n }\n process.on('SIGTERM', () => {\n shutdown('SIGTERM')\n })\n process.on('SIGINT', () => {\n shutdown('SIGINT')\n })\n}\n","/**\n * Manifest loading stage for `theokit start` (T4.2 architecture-cleanup).\n *\n * Loads pre-built manifest from `.theo/manifest.json` if present; otherwise\n * scans server/ for routes/actions/ws at startup with a structured warn.\n */\n\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { warnOnce } from '../../server/observability/logger.js'\nimport type { ActionNode } from '../../server/scan/action-scan.js'\nimport { scanServerActions } from '../../server/scan/action-scan.js'\nimport { loadManifest } from '../../server/scan/manifest.js'\nimport type { ServerRouteNode } from '../../server/scan/match.js'\nimport { scanServerRoutes } from '../../server/scan/scan.js'\nimport type { WebSocketRouteNode } from '../../server/scan/ws-scan.js'\nimport { scanWebSocketRoutes } from '../../server/scan/ws-scan.js'\n\nexport interface LoadedRoutes {\n routes: ServerRouteNode[]\n actions: ActionNode[]\n wsRoutes: WebSocketRouteNode[]\n}\n\nexport function loadRoutesAndActions(distDir: string, serverDir: string): LoadedRoutes {\n const manifestPath = join(distDir, 'manifest.json')\n // eslint-disable-next-line security/detect-non-literal-fs-filename -- distDir is from `theokit start`'s own caller-controlled cwd\n if (existsSync(manifestPath)) {\n const manifest = loadManifest(distDir, serverDir)\n return {\n routes: manifest.routes,\n actions: manifest.actions,\n wsRoutes: manifest.websockets,\n }\n }\n warnOnce('bootstrap.manifest_not_found', {\n event: 'bootstrap.manifest_not_found',\n message:\n 'No manifest found, scanning routes at startup. Run \"theo build\" to generate manifest.',\n serverDir,\n })\n return {\n routes: scanServerRoutes(serverDir),\n actions: scanServerActions(serverDir),\n wsRoutes: scanWebSocketRoutes(serverDir),\n }\n}\n","/**\n * Inline request handler for `theokit start` (T4.2 architecture-cleanup).\n *\n * Wires the per-request flow: security headers → action/route/static branches\n * → SSR fallback → CSR fallback → 500 page.\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport { generateNonce } from '../../server/auth/nonce.js'\nimport { sendError } from '../../server/http/send-response.js'\nimport { buildSecurityHeaders } from '../../server/security/security-headers.js'\n\nimport {\n tryServeAction,\n tryServeApiRoute,\n tryServeCustom404,\n tryServeStatic,\n type RequestHandlerCtx,\n} from './start-handlers.js'\nimport {\n isSsrRenderResult,\n type SsrRender,\n type SsrRenderResult,\n type SsrRenderStreaming,\n} from './start-ssr-setup.js'\n\nexport interface RequestHandlerContext {\n buildCtx: (\n req: IncomingMessage,\n res: ServerResponse,\n requestId: string,\n startTime: number,\n ) => RequestHandlerCtx\n securityHeadersConfig: Parameters<typeof buildSecurityHeaders>[0]\n ssrRender: SsrRender | null\n ssrRenderStreaming: SsrRenderStreaming | null\n ssrStreamingEnabled: boolean\n htmlHead: string\n htmlTail: string\n indexHtml: string\n custom500Html: string | null\n}\n\nfunction asSsrRenderResult(value: SsrRenderResult): SsrRenderResult {\n return value\n}\n\n/* eslint-disable complexity, sonarjs/cognitive-complexity --\n * inline request orchestrator co-located so the lifecycle is readable end-to-end.\n */\nexport function createRequestHandler(\n ctx: RequestHandlerContext,\n): (req: IncomingMessage, res: ServerResponse) => void {\n return (req: IncomingMessage, res: ServerResponse) => {\n void (async () => {\n const url = req.url ?? '/'\n const requestId = randomUUID()\n const start = Date.now()\n\n // Per-request nonce (EC-6 dev/prod parity).\n const nonce = generateNonce()\n const securityHeaders = buildSecurityHeaders(\n ctx.securityHeadersConfig,\n { production: true },\n { nonce },\n )\n for (const [k, v] of Object.entries(securityHeaders)) {\n res.setHeader(k, v)\n }\n\n const handlerCtx = ctx.buildCtx(req, res, requestId, start)\n\n try {\n if (await tryServeAction(handlerCtx)) return\n if (await tryServeApiRoute(handlerCtx)) return\n if (tryServeStatic(handlerCtx)) return\n if (tryServeCustom404(handlerCtx)) return\n\n // SSR streaming branch\n if (ctx.ssrStreamingEnabled && ctx.ssrRenderStreaming) {\n const controller = new AbortController()\n const onClose = (): void => {\n controller.abort()\n }\n req.on('close', onClose)\n try {\n const result = await ctx.ssrRenderStreaming(url, res, {\n signal: controller.signal,\n nonce,\n })\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n return\n } catch (streamErr) {\n console.error('[SSR Stream Error]', (streamErr as Error).message)\n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n }\n if (!res.writableEnded) {\n res.end(ctx.custom500Html ?? '<h1>500 — Server Error</h1>')\n }\n return\n } finally {\n req.removeListener('close', onClose)\n }\n }\n\n // SSR (non-streaming) branch\n if (ctx.ssrRender) {\n try {\n const result = await ctx.ssrRender(url, { nonce })\n if (result && typeof result === 'object' && 'redirect' in result) {\n res.writeHead(302, { Location: result.redirect.headers.get('location') ?? '/' })\n res.end()\n return\n }\n let ssrHtml = ''\n let hydrationScript = ''\n if (typeof result === 'string') {\n ssrHtml = result\n } else if (isSsrRenderResult(result)) {\n const rendered = asSsrRenderResult(result)\n ssrHtml = rendered.html\n const dataJson = JSON.stringify(rendered.hydrationData).replace(/</g, '\\\\u003c')\n hydrationScript = `<script${\n nonce ? ` nonce=\"${nonce}\"` : ''\n }>window.__staticRouterHydrationData=${dataJson}</script>`\n }\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(ctx.htmlHead + ssrHtml + hydrationScript + ctx.htmlTail)\n return\n } catch (ssrErr) {\n console.error('[SSR Error] Falling back to CSR:', (ssrErr as Error).message)\n // Fall through to CSR fallback\n }\n }\n\n // CSR fallback\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(ctx.indexHtml)\n } catch (err) {\n if (ctx.custom500Html && !res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'text/html' })\n res.end(ctx.custom500Html)\n } else if (!res.headersSent) {\n sendError(res, 'INTERNAL_ERROR', (err as Error).message, 500)\n } else {\n res.end()\n }\n }\n })()\n }\n}\n/* eslint-enable complexity, sonarjs/cognitive-complexity */\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport { extname } from 'node:path'\n\nimport { executeAction } from '../../server/http/action-execute.js'\nimport { executeRoute } from '../../server/http/execute.js'\nimport { sendError } from '../../server/http/send-response.js'\nimport { serveStaticFile } from '../../server/http/static.js'\nimport { logRequest } from '../../server/observability/logger.js'\nimport { findSuggestion } from '../../server/observability/suggest.js'\nimport type { PluginRunner } from '../../server/plugins/plugin-runner.js'\nimport type { ActionNode } from '../../server/scan/action-scan.js'\nimport { matchRoute } from '../../server/scan/match.js'\nimport type { ServerRouteNode } from '../../server/scan/match.js'\nimport type { LoadModule } from '../../server/scan/module-loader.js'\nimport type { CsrfMode, DisallowedConfig } from '../../server/security/csrf.js'\nimport type { TheoTransformer } from '../../server/transformer.js'\n\n/**\n * T6.1 (PV-7 SRP): start.ts request orchestrator decomposed into 5 focused\n * per-branch handlers. Each handler returns `true` if it handled the\n * request (response sent) so the orchestrator can stop iterating.\n *\n * The original 455-LOC monolith closed over 14+ locals; the shared shape\n * `RequestHandlerCtx` makes the dependencies explicit + reviewable.\n */\nexport interface RequestHandlerCtx {\n req: IncomingMessage\n res: ServerResponse\n url: string\n requestId: string\n startTime: number\n // Pre-loaded build artifacts\n clientDir: string\n custom404Html: string | null\n // Manifest-resolved tables\n cachedRoutes: ServerRouteNode[]\n cachedActions: ActionNode[]\n // Runtime infra\n loadModule: LoadModule\n serverDir: string\n pluginRunner: PluginRunner | undefined\n transformer: TheoTransformer | undefined\n csrfMode: CsrfMode\n disallowed: DisallowedConfig | undefined\n rateLimiter:\n | ((req: IncomingMessage) => { limited: boolean; headers: Record<string, string> })\n | null\n}\n\n/** Apply rate limit; return true if request was limited (response sent). */\nfunction applyRateLimit(c: RequestHandlerCtx, method: string): boolean {\n if (!c.rateLimiter) return false\n const check = c.rateLimiter(c.req)\n for (const [k, v] of Object.entries(check.headers)) c.res.setHeader(k, v)\n if (check.limited) {\n sendError(c.res, 'RATE_LIMITED', 'Too many requests', 429, undefined, c.requestId)\n logRequest({\n method,\n url: c.url,\n status: 429,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n return false\n}\n\n/** Branch 1: action routes (`/api/__actions/{file}/{exportName}`). */\nexport async function tryServeAction(c: RequestHandlerCtx): Promise<boolean> {\n if (!c.url.startsWith('/api/__actions/')) return false\n c.res.setHeader('x-request-id', c.requestId)\n\n if (applyRateLimit(c, c.req.method ?? 'POST')) return true\n\n const pathAfterPrefix = c.url.slice('/api/__actions/'.length).split('?')[0]\n const segments = pathAfterPrefix.split('/').filter(Boolean)\n if (segments.length < 2) {\n sendError(\n c.res,\n 'BAD_REQUEST',\n 'Action URL must be /api/__actions/{file}/{exportName}',\n 400,\n undefined,\n c.requestId,\n )\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: 400,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n const exportName = segments[segments.length - 1]\n const actionPath = segments.slice(0, -1).join('/')\n const action = c.cachedActions.find((a) => a.actionPath === actionPath)\n if (!action) {\n const actionPaths = c.cachedActions.map((a) => a.actionPath)\n const suggestion = findSuggestion(actionPath, actionPaths)\n const msg = suggestion\n ? `Action \"${actionPath}\" not found. Did you mean: ${suggestion}?`\n : `Action \"${actionPath}\" not found`\n sendError(c.res, 'NOT_FOUND', msg, 404, undefined, c.requestId)\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: 404,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n await executeAction(\n action.filePath,\n exportName,\n c.req,\n c.res,\n c.loadModule,\n c.serverDir,\n c.requestId,\n c.pluginRunner,\n c.csrfMode,\n c.disallowed,\n )\n logRequest({\n method: c.req.method ?? 'POST',\n url: c.url,\n status: c.res.statusCode,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n}\n\n/** Branch 2: API routes (`/api/*` excluding actions). */\nexport async function tryServeApiRoute(c: RequestHandlerCtx): Promise<boolean> {\n if (!c.url.startsWith('/api/')) return false\n c.res.setHeader('x-request-id', c.requestId)\n\n if (applyRateLimit(c, c.req.method ?? 'GET')) return true\n\n const match = matchRoute(c.url, c.cachedRoutes)\n if (!match) {\n const urlPath = c.url.split('?')[0]\n const routePaths = c.cachedRoutes.map((r) => r.routePath)\n const suggestion = findSuggestion(urlPath, routePaths)\n const msg = suggestion\n ? `API route not found: ${urlPath}. Did you mean: ${suggestion}?`\n : 'API route not found'\n sendError(c.res, 'NOT_FOUND', msg, 404, undefined, c.requestId)\n logRequest({\n method: c.req.method ?? 'GET',\n url: c.url,\n status: 404,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n }\n const method = (c.req.method ?? 'GET').toUpperCase()\n // T3.1 (ADR-0016) — context object replaces 12 positional args\n await executeRoute({\n route: match.route,\n method,\n params: match.params,\n req: c.req,\n res: c.res,\n loadModule: c.loadModule,\n serverDir: c.serverDir,\n requestId: c.requestId,\n pluginRunner: c.pluginRunner,\n transformer: c.transformer,\n csrfMode: c.csrfMode,\n disallowed: c.disallowed,\n })\n logRequest({\n method,\n url: c.url,\n status: c.res.statusCode,\n duration: Date.now() - c.startTime,\n requestId: c.requestId,\n })\n return true\n}\n\n/** Branch 3: static files (returns true if a static asset was served). */\nexport function tryServeStatic(c: RequestHandlerCtx): boolean {\n return serveStaticFile(c.req, c.res, c.clientDir)\n}\n\n/** Branch 4: custom 404 for URLs that look like missing assets. */\nexport function tryServeCustom404(c: RequestHandlerCtx): boolean {\n const urlPath = c.url.split('?')[0]\n if (c.custom404Html && extname(urlPath)) {\n c.res.writeHead(404, { 'Content-Type': 'text/html' })\n c.res.end(c.custom404Html)\n return true\n }\n return false\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Static-file server. The URL path IS user-controlled, BUT before any fs\n * call we resolve to absolute and reject with 403 if the resolved path\n * escapes `clientDir` (see EC-1 guard at line below). The guard is\n * authoritative; the rule cannot see it.\n */\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { resolve, extname } from 'node:path'\n\nconst MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain',\n '.map': 'application/json',\n}\n\nexport function serveStaticFile(\n req: IncomingMessage,\n res: ServerResponse,\n clientDir: string,\n): boolean {\n const urlPath = (req.url ?? '/').split('?')[0]\n\n // Path traversal prevention (EC-1)\n const filePath = resolve(clientDir, '.' + urlPath)\n if (!filePath.startsWith(clientDir)) {\n res.writeHead(403)\n res.end('Forbidden')\n return true\n }\n\n if (!existsSync(filePath)) return false\n\n const stat = statSync(filePath)\n if (!stat.isFile()) return false\n\n const ext = extname(filePath)\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream'\n const content = readFileSync(filePath)\n\n res.writeHead(200, {\n 'Content-Type': contentType,\n 'Content-Length': content.length,\n })\n res.end(content)\n return true\n}\n","/**\n * SSR setup stage for `theokit start` (T4.2 architecture-cleanup).\n *\n * Loads the SSR entry-server module if configured + builds template split\n * around the React root div. Returns null renderers when SSR is disabled.\n */\n\nimport type { ServerResponse } from 'node:http'\n\nimport { resolveSsrEntry } from './start-bootstrap-stages.js'\n\nexport interface SsrRenderResult {\n html: string\n hydrationData: {\n loaderData?: unknown\n actionData?: unknown\n errors?: unknown\n }\n}\n\nexport type RenderStreamingResult = { redirect: Response } | { streaming: true } | undefined\n\nexport type SsrRender = (\n url: string,\n options?: { nonce?: string },\n) => Promise<SsrRenderResult | { redirect: Response } | string>\n\nexport type SsrRenderStreaming = (\n url: string,\n response: ServerResponse,\n options?: { signal?: AbortSignal; nonce?: string },\n) => Promise<RenderStreamingResult>\n\nexport interface SsrSetupResult {\n enabled: boolean\n streamingEnabled: boolean\n render: SsrRender | null\n renderStreaming: SsrRenderStreaming | null\n htmlHead: string\n htmlTail: string\n}\n\nexport function isSsrRenderResult(value: unknown): value is SsrRenderResult {\n if (typeof value !== 'object' || value === null) return false\n if (!('html' in value)) return false\n const html = (value as Record<string, unknown>).html\n if (typeof html !== 'string') return false\n return true\n}\n\ninterface SsrEntryServer {\n render: SsrRender\n renderStreaming?: SsrRenderStreaming\n}\n\nexport async function setupSsr(opts: {\n distDir: string\n indexHtml: string\n ssrConfigEnabled: boolean\n ssrStreamingConfig: boolean | undefined\n}): Promise<SsrSetupResult> {\n const ssrServerPath: string | null = opts.ssrConfigEnabled ? resolveSsrEntry(opts.distDir) : null\n const enabled = ssrServerPath !== null\n const streamingEnabled = enabled && Boolean(opts.ssrStreamingConfig)\n\n if (ssrServerPath === null) {\n return {\n enabled: false,\n streamingEnabled: false,\n render: null,\n renderStreaming: null,\n htmlHead: '',\n htmlTail: '',\n }\n }\n\n const mod = (await import(ssrServerPath)) as SsrEntryServer\n const render = mod.render\n const renderStreaming = typeof mod.renderStreaming === 'function' ? mod.renderStreaming : null\n\n // Split HTML template on root div\n let htmlHead = ''\n let htmlTail = ''\n const rootDivMatch = /<div id=[\"']root[\"'][^>]*>/.exec(opts.indexHtml)\n if (rootDivMatch) {\n const splitIdx = opts.indexHtml.indexOf(rootDivMatch[0]) + rootDivMatch[0].length\n htmlHead = opts.indexHtml.slice(0, splitIdx)\n htmlTail = opts.indexHtml.slice(splitIdx)\n }\n\n return { enabled, streamingEnabled, render, renderStreaming, htmlHead, htmlTail }\n}\n","/**\n * WebSocket upgrade handler for `theokit start` (T4.2 architecture-cleanup).\n *\n * Wires `server.on('upgrade')` for declared WS routes. Opt-in: only attached\n * when `wsRoutes.length > 0`. Lazy-imports `ws` package — throws an actionable\n * error when wsRoutes declared but `ws` not installed.\n */\n\nimport type { Server as HttpServer } from 'node:http'\n\nimport type * as WsLib from 'ws'\n\nimport type { WebSocketHandler } from '../../server/define/define-websocket.js'\nimport type { LoadModule } from '../../server/scan/module-loader.js'\nimport type { WebSocketRouteNode } from '../../server/scan/ws-scan.js'\n\nexport async function attachWebSocketHandler(\n server: HttpServer,\n wsRoutes: WebSocketRouteNode[],\n loadModule: LoadModule,\n): Promise<void> {\n if (wsRoutes.length === 0) return\n\n let WebSocketServerCtor: typeof WsLib.WebSocketServer\n try {\n const wsModule = await import('ws')\n WebSocketServerCtor = wsModule.WebSocketServer\n } catch {\n throw new Error('WebSocket routes found but \"ws\" package is not installed. Run: npm install ws')\n }\n\n const wss = new WebSocketServerCtor({ noServer: true })\n\n server.on('upgrade', (request, socket, head) => {\n void (async () => {\n const url = request.url ?? '/'\n if (!url.startsWith('/ws/')) {\n socket.destroy()\n return\n }\n\n const wsPath = url.split('?')[0]\n const match = wsRoutes.find((r) => r.wsPath === wsPath)\n if (!match) {\n socket.destroy()\n return\n }\n\n try {\n const mod = await loadModule(match.filePath)\n const handler = ((mod as { default?: unknown }).default ?? mod) as WebSocketHandler\n\n wss.handleUpgrade(request, socket, head, (ws) => {\n handler.onOpen?.(ws, request)\n ws.on('message', (data: Buffer) => {\n handler.onMessage?.(ws, data.toString())\n })\n ws.on('close', (code: number, reason: Buffer) => {\n handler.onClose?.(ws, code, reason)\n })\n ws.on('error', (err: Error) => {\n handler.onError?.(ws, err)\n })\n })\n } catch {\n socket.destroy()\n }\n })()\n })\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","resolve","existsSync","existsSync","extname","existsSync","resolve","extname","resolve","existsSync","readFileSync","join"]}
|
|
@@ -108,7 +108,7 @@ function scanZeroConfigTailwindHint(cwd, out) {
|
|
|
108
108
|
} catch {
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
-
const hasUi = Boolean(pkg.dependencies?.["@
|
|
111
|
+
const hasUi = Boolean(pkg.dependencies?.["@theokit/ui"]) || Boolean(pkg.devDependencies?.["@theokit/ui"]);
|
|
112
112
|
if (!hasUi) return;
|
|
113
113
|
for (const ext of [".ts", ".js", ".mjs", ".cjs"]) {
|
|
114
114
|
const cfgPath = resolve(cwd, `tailwind.config${ext}`);
|
|
@@ -119,13 +119,13 @@ function scanZeroConfigTailwindHint(cwd, out) {
|
|
|
119
119
|
} catch {
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
|
-
if (content.includes("@
|
|
122
|
+
if (content.includes("@theokit/ui/preset")) return;
|
|
123
123
|
out.push({
|
|
124
124
|
file: `tailwind.config${ext}`,
|
|
125
125
|
line: 1,
|
|
126
126
|
rule: "zero-config-tailwind-suggest",
|
|
127
|
-
message: "@
|
|
128
|
-
fix: "import preset from '@
|
|
127
|
+
message: "@theokit/ui detected with a manual tailwind.config \u2014 extend with the UI preset to keep theme tokens in sync.",
|
|
128
|
+
fix: "import preset from '@theokit/ui/preset'; export default { presets: [preset], content: [...] }"
|
|
129
129
|
});
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
@@ -251,4 +251,4 @@ export {
|
|
|
251
251
|
scanUpgradeReadiness,
|
|
252
252
|
upgradeReadinessCommand
|
|
253
253
|
};
|
|
254
|
-
//# sourceMappingURL=upgrade-readiness-
|
|
254
|
+
//# sourceMappingURL=upgrade-readiness-GUJBCJIS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/upgrade-readiness.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI lint scanner. Reads user source files under `projectRoot` (CLI\n * argument resolved to absolute). Read-only; never writes. No HTTP input.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { resolve, relative, sep } from 'node:path'\n\n/**\n * T2.3 — Upgrade-readiness scanner for the 0.2 → 0.3 cutover.\n *\n * LINT-only. Reads user source, reports anticipated 0.3.0 violations with\n * file:line + suggested fix. NEVER writes user files (ADR D1).\n *\n * Three rules:\n * 1. csrf-missing-header — raw fetch POST/PUT/PATCH/DELETE missing the\n * X-Theo-Action: '1' header → 403 under strict CSRF.\n * 2. inline-script — <script>...</script> in .html files without\n * `src=` attribute → blocked under enforce CSP without unsafe-inline.\n * 3. dangerously-set-inline-script — React `dangerouslySetInnerHTML`\n * payload that contains <script>.\n *\n * EC-7: occurrences inside line comments (// ...) or wrapped in quoted\n * strings are best-effort skipped. The scanner is not a parser — it\n * filters obvious false-positive lines before running the violation\n * regex.\n * EC-8: scanning a directory with no `app/` and no `server/` returns a\n * 'no-project-detected' report with exitCode 0 (no crash).\n */\n\nexport type ViolationRule =\n | 'csrf-missing-header'\n | 'inline-script'\n | 'dangerously-set-inline-script'\n | 'zero-config-tailwind-suggest'\n | 'handrolled-dotenv-suggest'\n\nexport interface Violation {\n file: string\n line: number\n rule: ViolationRule\n message: string\n fix: string\n}\n\nexport type ReadinessStatus =\n | 'ready'\n | 'has-violations'\n | 'no-project-detected'\n | 'not-a-theokit-project'\n\nexport interface UpgradeReadinessReport {\n status: ReadinessStatus\n exitCode: 0 | 1\n violations: Violation[]\n}\n\nexport interface ScanOptions {\n cwd: string\n /** When true, exitCode is forced to 0 even when violations exist. */\n allowWarnings?: boolean\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.theokit', '.cache'])\nconst SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])\nconst HTML_EXTS = new Set(['.html', '.htm'])\n\nfunction walkFiles(dir: string, root: string, out: string[]): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const name of entries) {\n if (SKIP_DIRS.has(name)) continue\n const full = resolve(dir, name)\n let s\n try {\n s = statSync(full)\n } catch {\n continue\n }\n if (s.isDirectory()) {\n walkFiles(full, root, out)\n } else if (s.isFile()) {\n const dot = name.lastIndexOf('.')\n if (dot < 0) continue\n const ext = name.slice(dot)\n if (SOURCE_EXTS.has(ext) || HTML_EXTS.has(ext)) out.push(full)\n }\n }\n}\n\nfunction isLikelyCommentOrString(rawLine: string, matchIndex: number): boolean {\n // Comment: line-comment marker (//) appears before the match position.\n const lineCommentIdx = rawLine.indexOf('//')\n if (lineCommentIdx !== -1 && lineCommentIdx < matchIndex) {\n // Make sure // is not inside a string (best-effort: count unescaped\n // quote chars before the //)\n const before = rawLine.slice(0, lineCommentIdx)\n const quoteCount = (before.match(/(?<!\\\\)[\"']/g) ?? []).length\n if (quoteCount % 2 === 0) return true\n }\n // String literal: scan quote characters around match. If the match is\n // surrounded by an odd number of unescaped quotes on either side, it\n // sits inside a string. Best-effort — does not parse template literals\n // or multiline strings.\n const before = rawLine.slice(0, matchIndex)\n const doubleQuotes = (before.match(/(?<!\\\\)\"/g) ?? []).length\n const singleQuotes = (before.match(/(?<!\\\\)'/g) ?? []).length\n return doubleQuotes % 2 === 1 || singleQuotes % 2 === 1\n}\n\n// `fetch(` followed (within ~200 chars, same call site) by a method:\n// 'POST' | 'PUT' | 'PATCH' | 'DELETE'. We scan per-line to keep regex\n// state simple; multi-line fetch calls match on the line that carries\n// the `method:` literal.\nconst METHOD_PATTERN = /method\\s*:\\s*['\"`](POST|PUT|PATCH|DELETE)['\"`]/gi\n\nfunction scanSourceFile(file: string, rel: string, content: string, out: Violation[]): void {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n METHOD_PATTERN.lastIndex = 0\n const match = METHOD_PATTERN.exec(line)\n if (!match) continue\n if (isLikelyCommentOrString(line, match.index)) continue\n\n // Look 8 lines back AND forward for the enclosing `fetch(` and for an\n // X-Theo-Action header in the same call-site block.\n const blockStart = Math.max(0, i - 8)\n const blockEnd = Math.min(lines.length - 1, i + 8)\n const block = lines.slice(blockStart, blockEnd + 1).join('\\n')\n if (!/\\bfetch\\s*\\(/.test(block)) continue\n if (/\\btheoFetch\\s*\\(/.test(block)) continue\n if (/['\"`]X-Theo-Action['\"`]\\s*:/i.test(block)) continue\n\n out.push({\n file: rel,\n line: i + 1,\n rule: 'csrf-missing-header',\n message:\n 'fetch(' +\n match[1] +\n ') without X-Theo-Action header — will return 403 under 0.3.0 strict CSRF',\n fix: \"Use theoFetch(path, { method, body }) OR add headers: { 'X-Theo-Action': '1' }\",\n })\n }\n\n // dangerouslySetInnerHTML with <script> in the payload.\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!/dangerouslySetInnerHTML/i.test(line)) continue\n // The script may live on the same line or in __html on a following\n // line; check a small window.\n const window = lines.slice(i, Math.min(lines.length, i + 6)).join('\\n')\n if (!/__html\\s*:\\s*['\"`][^'\"`]*<script\\b/i.test(window)) continue\n out.push({\n file: rel,\n line: i + 1,\n rule: 'dangerously-set-inline-script',\n message:\n 'dangerouslySetInnerHTML payload contains <script> — blocked under 0.3.0 enforce CSP',\n fix: 'Move the script into a real module loaded via <script src=\"...\">, or thread ctx.nonce',\n })\n }\n}\n\nconst INLINE_SCRIPT_PATTERN = /<script\\b(?![^>]*\\bsrc\\s*=)[^>]*>[\\s\\S]*?<\\/script>/gi\n\nfunction scanHtmlFile(file: string, rel: string, content: string, out: Violation[]): void {\n INLINE_SCRIPT_PATTERN.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = INLINE_SCRIPT_PATTERN.exec(content)) !== null) {\n // Compute 1-based line number from match index.\n const before = content.slice(0, match.index)\n const line = (before.match(/\\n/g) ?? []).length + 1\n out.push({\n file: rel,\n line,\n rule: 'inline-script',\n message:\n 'Inline <script> without src — blocked under 0.3.0 enforce CSP without unsafe-inline',\n fix: \"Move into a real script file (e.g. /main.js) and load with <script src='/main.js'>, or thread ctx.nonce\",\n })\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has @usetheo/ui in deps AND a manual\n * tailwind.config.* that does NOT import the @usetheo/ui preset. Suggest\n * extending via `import preset from '@usetheo/ui/preset'`.\n */\nfunction scanZeroConfigTailwindHint(cwd: string, out: Violation[]): void {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) return\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n return\n }\n const hasUi =\n Boolean(pkg.dependencies?.['@usetheo/ui']) || Boolean(pkg.devDependencies?.['@usetheo/ui'])\n if (!hasUi) return\n\n for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {\n const cfgPath = resolve(cwd, `tailwind.config${ext}`)\n if (!existsSync(cfgPath)) continue\n let content: string\n try {\n content = readFileSync(cfgPath, 'utf8')\n } catch {\n continue\n }\n if (content.includes('@usetheo/ui/preset')) return // already using preset\n out.push({\n file: `tailwind.config${ext}`,\n line: 1,\n rule: 'zero-config-tailwind-suggest',\n message:\n '@usetheo/ui detected with a manual tailwind.config — extend with the UI preset to keep theme tokens in sync.',\n fix: \"import preset from '@usetheo/ui/preset'; export default { presets: [preset], content: [...] }\",\n })\n return\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has hand-rolled dotenv loading in\n * server/ (import 'dotenv/config', dotenv.config(), readFile('.env'), etc.).\n * Suggest the framework's loadEnv (auto-invoked by CLI commands; importable\n * from theokit/server for standalone scripts).\n */\nfunction scanHandRolledDotenvHint(\n file: string,\n rel: string,\n content: string,\n out: Violation[],\n): void {\n // Only flag server/ files\n if (!rel.startsWith('server/')) return\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n // import 'dotenv/config' or import * as dotenv from 'dotenv'\n if (/from\\s+['\"]dotenv['\"]/.test(line) || /import\\s+['\"]dotenv\\/config['\"]/.test(line)) {\n out.push({\n file: rel,\n line: i + 1,\n rule: 'handrolled-dotenv-suggest',\n message:\n 'Manual dotenv import detected — TheoKit auto-loads .env in CLI commands. For standalone scripts, import { loadEnv } from \"theokit/server\".',\n fix: \"Remove this import. CLI commands (dev/build/start) auto-load .env. For standalone scripts: import { loadEnv } from 'theokit/server'; loadEnv()\",\n })\n return // one hint per file\n }\n }\n}\n\n/**\n * EC-3: a directory may have an `app/` directory (Next.js, Remix, etc.) but\n * not actually be a TheoKit project. Run our 3 rules against such a project\n * would generate many false positives. The strict guard checks for `theokit`\n * in dependencies OR devDependencies of the cwd's package.json. Returns\n * `null` when the project IS a TheoKit project, or a non-TheoKit report\n * otherwise.\n */\nfunction detectNonTheokitProject(cwd: string): UpgradeReadinessReport | null {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n // No package.json — let the existing app/server detection handle it.\n return null\n }\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n // Malformed package.json — treat as \"not a TheoKit project we can verify\".\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n const deps = pkg.dependencies ?? {}\n const devDeps = pkg.devDependencies ?? {}\n if (!Object.hasOwn(deps, 'theokit') && !Object.hasOwn(devDeps, 'theokit')) {\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n return null\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async surface for future fs.promises migration\nexport async function scanUpgradeReadiness(options: ScanOptions): Promise<UpgradeReadinessReport> {\n const cwd = options.cwd\n const allowWarnings = options.allowWarnings === true\n\n const appDir = resolve(cwd, 'app')\n const serverDir = resolve(cwd, 'server')\n const publicDir = resolve(cwd, 'public')\n\n const hasApp = existsSync(appDir)\n const hasServer = existsSync(serverDir)\n\n if (!hasApp && !hasServer) {\n return { status: 'no-project-detected', exitCode: 0, violations: [] }\n }\n\n // EC-3: refuse to scan non-TheoKit projects (avoid FP avalanche).\n const nonTheokit = detectNonTheokitProject(cwd)\n if (nonTheokit) return nonTheokit\n\n const files: string[] = []\n if (hasApp) walkFiles(appDir, cwd, files)\n if (hasServer) walkFiles(serverDir, cwd, files)\n if (existsSync(publicDir)) walkFiles(publicDir, cwd, files)\n\n const violations: Violation[] = []\n for (const file of files) {\n let content: string\n try {\n content = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const rel = relative(cwd, file).split(sep).join('/')\n const dot = file.lastIndexOf('.')\n const ext = dot >= 0 ? file.slice(dot) : ''\n if (HTML_EXTS.has(ext)) {\n scanHtmlFile(file, rel, content, violations)\n } else if (SOURCE_EXTS.has(ext)) {\n scanSourceFile(file, rel, content, violations)\n scanHandRolledDotenvHint(file, rel, content, violations)\n }\n }\n\n // T4.1 — Project-level hint (not per-file): does the consumer have a\n // manual tailwind.config without the UI preset?\n scanZeroConfigTailwindHint(cwd, violations)\n\n const hasHigh = violations.length > 0\n const status: ReadinessStatus = hasHigh ? 'has-violations' : 'ready'\n const exitCode: 0 | 1 = hasHigh && !allowWarnings ? 1 : 0\n return { status, exitCode, violations }\n}\n\n/**\n * CLI wrapper. Prints a human-readable or JSON report and exits with the\n * computed exit code. Imported lazily from `cli/index.ts`.\n */\nexport async function upgradeReadinessCommand(opts: {\n json?: boolean\n allowWarnings?: boolean\n}): Promise<void> {\n const report = await scanUpgradeReadiness({\n cwd: process.cwd(),\n allowWarnings: opts.allowWarnings,\n })\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2))\n process.exit(report.exitCode)\n }\n\n console.log('')\n if (report.status === 'no-project-detected') {\n console.log(\" · No theokit project detected here (no 'app/' or 'server/' dir).\")\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.status === 'not-a-theokit-project') {\n console.log(' ✗ Not a TheoKit project (theokit not in package.json).')\n console.log(' Run this command from a TheoKit project root.')\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.violations.length === 0) {\n console.log(' ✓ Upgrade-readiness 0.3: no violations detected.')\n console.log('')\n // T1.3 — friendly post-success URL (blueprint R4; Next.js next-codemod precedent).\n // Anchor canon: #rollback (existing in docs/migration/0.2-to-0.3.md per T1.1).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3')\n console.log(' Need to roll back temporarily? See #rollback')\n console.log('')\n process.exit(report.exitCode)\n }\n console.log(` ✗ Upgrade-readiness 0.3: ${report.violations.length} violation(s)`)\n console.log('')\n for (const v of report.violations) {\n console.log(` ${v.file}:${v.line} [${v.rule}]`)\n console.log(` ${v.message}`)\n console.log(` fix: ${v.fix}`)\n console.log('')\n }\n // T1.3 — print migration-guide URL on violations branch too (one canonical link\n // regardless of verdict, per blueprint Q5 Astro CHANGELOG URL pattern).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3#rollback')\n console.log('')\n process.exit(report.exitCode)\n}\n"],"mappings":"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","names":["before"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/upgrade-readiness.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI lint scanner. Reads user source files under `projectRoot` (CLI\n * argument resolved to absolute). Read-only; never writes. No HTTP input.\n */\nimport { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'\nimport { resolve, relative, sep } from 'node:path'\n\n/**\n * T2.3 — Upgrade-readiness scanner for the 0.2 → 0.3 cutover.\n *\n * LINT-only. Reads user source, reports anticipated 0.3.0 violations with\n * file:line + suggested fix. NEVER writes user files (ADR D1).\n *\n * Three rules:\n * 1. csrf-missing-header — raw fetch POST/PUT/PATCH/DELETE missing the\n * X-Theo-Action: '1' header → 403 under strict CSRF.\n * 2. inline-script — <script>...</script> in .html files without\n * `src=` attribute → blocked under enforce CSP without unsafe-inline.\n * 3. dangerously-set-inline-script — React `dangerouslySetInnerHTML`\n * payload that contains <script>.\n *\n * EC-7: occurrences inside line comments (// ...) or wrapped in quoted\n * strings are best-effort skipped. The scanner is not a parser — it\n * filters obvious false-positive lines before running the violation\n * regex.\n * EC-8: scanning a directory with no `app/` and no `server/` returns a\n * 'no-project-detected' report with exitCode 0 (no crash).\n */\n\nexport type ViolationRule =\n | 'csrf-missing-header'\n | 'inline-script'\n | 'dangerously-set-inline-script'\n | 'zero-config-tailwind-suggest'\n | 'handrolled-dotenv-suggest'\n\nexport interface Violation {\n file: string\n line: number\n rule: ViolationRule\n message: string\n fix: string\n}\n\nexport type ReadinessStatus =\n | 'ready'\n | 'has-violations'\n | 'no-project-detected'\n | 'not-a-theokit-project'\n\nexport interface UpgradeReadinessReport {\n status: ReadinessStatus\n exitCode: 0 | 1\n violations: Violation[]\n}\n\nexport interface ScanOptions {\n cwd: string\n /** When true, exitCode is forced to 0 even when violations exist. */\n allowWarnings?: boolean\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.theokit', '.cache'])\nconst SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])\nconst HTML_EXTS = new Set(['.html', '.htm'])\n\nfunction walkFiles(dir: string, root: string, out: string[]): void {\n let entries: string[]\n try {\n entries = readdirSync(dir)\n } catch {\n return\n }\n for (const name of entries) {\n if (SKIP_DIRS.has(name)) continue\n const full = resolve(dir, name)\n let s\n try {\n s = statSync(full)\n } catch {\n continue\n }\n if (s.isDirectory()) {\n walkFiles(full, root, out)\n } else if (s.isFile()) {\n const dot = name.lastIndexOf('.')\n if (dot < 0) continue\n const ext = name.slice(dot)\n if (SOURCE_EXTS.has(ext) || HTML_EXTS.has(ext)) out.push(full)\n }\n }\n}\n\nfunction isLikelyCommentOrString(rawLine: string, matchIndex: number): boolean {\n // Comment: line-comment marker (//) appears before the match position.\n const lineCommentIdx = rawLine.indexOf('//')\n if (lineCommentIdx !== -1 && lineCommentIdx < matchIndex) {\n // Make sure // is not inside a string (best-effort: count unescaped\n // quote chars before the //)\n const before = rawLine.slice(0, lineCommentIdx)\n const quoteCount = (before.match(/(?<!\\\\)[\"']/g) ?? []).length\n if (quoteCount % 2 === 0) return true\n }\n // String literal: scan quote characters around match. If the match is\n // surrounded by an odd number of unescaped quotes on either side, it\n // sits inside a string. Best-effort — does not parse template literals\n // or multiline strings.\n const before = rawLine.slice(0, matchIndex)\n const doubleQuotes = (before.match(/(?<!\\\\)\"/g) ?? []).length\n const singleQuotes = (before.match(/(?<!\\\\)'/g) ?? []).length\n return doubleQuotes % 2 === 1 || singleQuotes % 2 === 1\n}\n\n// `fetch(` followed (within ~200 chars, same call site) by a method:\n// 'POST' | 'PUT' | 'PATCH' | 'DELETE'. We scan per-line to keep regex\n// state simple; multi-line fetch calls match on the line that carries\n// the `method:` literal.\nconst METHOD_PATTERN = /method\\s*:\\s*['\"`](POST|PUT|PATCH|DELETE)['\"`]/gi\n\nfunction scanSourceFile(file: string, rel: string, content: string, out: Violation[]): void {\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n METHOD_PATTERN.lastIndex = 0\n const match = METHOD_PATTERN.exec(line)\n if (!match) continue\n if (isLikelyCommentOrString(line, match.index)) continue\n\n // Look 8 lines back AND forward for the enclosing `fetch(` and for an\n // X-Theo-Action header in the same call-site block.\n const blockStart = Math.max(0, i - 8)\n const blockEnd = Math.min(lines.length - 1, i + 8)\n const block = lines.slice(blockStart, blockEnd + 1).join('\\n')\n if (!/\\bfetch\\s*\\(/.test(block)) continue\n if (/\\btheoFetch\\s*\\(/.test(block)) continue\n if (/['\"`]X-Theo-Action['\"`]\\s*:/i.test(block)) continue\n\n out.push({\n file: rel,\n line: i + 1,\n rule: 'csrf-missing-header',\n message:\n 'fetch(' +\n match[1] +\n ') without X-Theo-Action header — will return 403 under 0.3.0 strict CSRF',\n fix: \"Use theoFetch(path, { method, body }) OR add headers: { 'X-Theo-Action': '1' }\",\n })\n }\n\n // dangerouslySetInnerHTML with <script> in the payload.\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n if (!/dangerouslySetInnerHTML/i.test(line)) continue\n // The script may live on the same line or in __html on a following\n // line; check a small window.\n const window = lines.slice(i, Math.min(lines.length, i + 6)).join('\\n')\n if (!/__html\\s*:\\s*['\"`][^'\"`]*<script\\b/i.test(window)) continue\n out.push({\n file: rel,\n line: i + 1,\n rule: 'dangerously-set-inline-script',\n message:\n 'dangerouslySetInnerHTML payload contains <script> — blocked under 0.3.0 enforce CSP',\n fix: 'Move the script into a real module loaded via <script src=\"...\">, or thread ctx.nonce',\n })\n }\n}\n\nconst INLINE_SCRIPT_PATTERN = /<script\\b(?![^>]*\\bsrc\\s*=)[^>]*>[\\s\\S]*?<\\/script>/gi\n\nfunction scanHtmlFile(file: string, rel: string, content: string, out: Violation[]): void {\n INLINE_SCRIPT_PATTERN.lastIndex = 0\n let match: RegExpExecArray | null\n while ((match = INLINE_SCRIPT_PATTERN.exec(content)) !== null) {\n // Compute 1-based line number from match index.\n const before = content.slice(0, match.index)\n const line = (before.match(/\\n/g) ?? []).length + 1\n out.push({\n file: rel,\n line,\n rule: 'inline-script',\n message:\n 'Inline <script> without src — blocked under 0.3.0 enforce CSP without unsafe-inline',\n fix: \"Move into a real script file (e.g. /main.js) and load with <script src='/main.js'>, or thread ctx.nonce\",\n })\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has @theokit/ui in deps AND a manual\n * tailwind.config.* that does NOT import the @theokit/ui preset. Suggest\n * extending via `import preset from '@theokit/ui/preset'`.\n */\nfunction scanZeroConfigTailwindHint(cwd: string, out: Violation[]): void {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) return\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n return\n }\n const hasUi =\n Boolean(pkg.dependencies?.['@theokit/ui']) || Boolean(pkg.devDependencies?.['@theokit/ui'])\n if (!hasUi) return\n\n for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {\n const cfgPath = resolve(cwd, `tailwind.config${ext}`)\n if (!existsSync(cfgPath)) continue\n let content: string\n try {\n content = readFileSync(cfgPath, 'utf8')\n } catch {\n continue\n }\n if (content.includes('@theokit/ui/preset')) return // already using preset\n out.push({\n file: `tailwind.config${ext}`,\n line: 1,\n rule: 'zero-config-tailwind-suggest',\n message:\n '@theokit/ui detected with a manual tailwind.config — extend with the UI preset to keep theme tokens in sync.',\n fix: \"import preset from '@theokit/ui/preset'; export default { presets: [preset], content: [...] }\",\n })\n return\n }\n}\n\n/**\n * T4.1 — Zero-config hint: consumer has hand-rolled dotenv loading in\n * server/ (import 'dotenv/config', dotenv.config(), readFile('.env'), etc.).\n * Suggest the framework's loadEnv (auto-invoked by CLI commands; importable\n * from theokit/server for standalone scripts).\n */\nfunction scanHandRolledDotenvHint(\n file: string,\n rel: string,\n content: string,\n out: Violation[],\n): void {\n // Only flag server/ files\n if (!rel.startsWith('server/')) return\n const lines = content.split('\\n')\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? ''\n // import 'dotenv/config' or import * as dotenv from 'dotenv'\n if (/from\\s+['\"]dotenv['\"]/.test(line) || /import\\s+['\"]dotenv\\/config['\"]/.test(line)) {\n out.push({\n file: rel,\n line: i + 1,\n rule: 'handrolled-dotenv-suggest',\n message:\n 'Manual dotenv import detected — TheoKit auto-loads .env in CLI commands. For standalone scripts, import { loadEnv } from \"theokit/server\".',\n fix: \"Remove this import. CLI commands (dev/build/start) auto-load .env. For standalone scripts: import { loadEnv } from 'theokit/server'; loadEnv()\",\n })\n return // one hint per file\n }\n }\n}\n\n/**\n * EC-3: a directory may have an `app/` directory (Next.js, Remix, etc.) but\n * not actually be a TheoKit project. Run our 3 rules against such a project\n * would generate many false positives. The strict guard checks for `theokit`\n * in dependencies OR devDependencies of the cwd's package.json. Returns\n * `null` when the project IS a TheoKit project, or a non-TheoKit report\n * otherwise.\n */\nfunction detectNonTheokitProject(cwd: string): UpgradeReadinessReport | null {\n const pkgPath = resolve(cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n // No package.json — let the existing app/server detection handle it.\n return null\n }\n let pkg: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as typeof pkg\n } catch {\n // Malformed package.json — treat as \"not a TheoKit project we can verify\".\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n const deps = pkg.dependencies ?? {}\n const devDeps = pkg.devDependencies ?? {}\n if (!Object.hasOwn(deps, 'theokit') && !Object.hasOwn(devDeps, 'theokit')) {\n return { status: 'not-a-theokit-project', exitCode: 1, violations: [] }\n }\n return null\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async surface for future fs.promises migration\nexport async function scanUpgradeReadiness(options: ScanOptions): Promise<UpgradeReadinessReport> {\n const cwd = options.cwd\n const allowWarnings = options.allowWarnings === true\n\n const appDir = resolve(cwd, 'app')\n const serverDir = resolve(cwd, 'server')\n const publicDir = resolve(cwd, 'public')\n\n const hasApp = existsSync(appDir)\n const hasServer = existsSync(serverDir)\n\n if (!hasApp && !hasServer) {\n return { status: 'no-project-detected', exitCode: 0, violations: [] }\n }\n\n // EC-3: refuse to scan non-TheoKit projects (avoid FP avalanche).\n const nonTheokit = detectNonTheokitProject(cwd)\n if (nonTheokit) return nonTheokit\n\n const files: string[] = []\n if (hasApp) walkFiles(appDir, cwd, files)\n if (hasServer) walkFiles(serverDir, cwd, files)\n if (existsSync(publicDir)) walkFiles(publicDir, cwd, files)\n\n const violations: Violation[] = []\n for (const file of files) {\n let content: string\n try {\n content = readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const rel = relative(cwd, file).split(sep).join('/')\n const dot = file.lastIndexOf('.')\n const ext = dot >= 0 ? file.slice(dot) : ''\n if (HTML_EXTS.has(ext)) {\n scanHtmlFile(file, rel, content, violations)\n } else if (SOURCE_EXTS.has(ext)) {\n scanSourceFile(file, rel, content, violations)\n scanHandRolledDotenvHint(file, rel, content, violations)\n }\n }\n\n // T4.1 — Project-level hint (not per-file): does the consumer have a\n // manual tailwind.config without the UI preset?\n scanZeroConfigTailwindHint(cwd, violations)\n\n const hasHigh = violations.length > 0\n const status: ReadinessStatus = hasHigh ? 'has-violations' : 'ready'\n const exitCode: 0 | 1 = hasHigh && !allowWarnings ? 1 : 0\n return { status, exitCode, violations }\n}\n\n/**\n * CLI wrapper. Prints a human-readable or JSON report and exits with the\n * computed exit code. Imported lazily from `cli/index.ts`.\n */\nexport async function upgradeReadinessCommand(opts: {\n json?: boolean\n allowWarnings?: boolean\n}): Promise<void> {\n const report = await scanUpgradeReadiness({\n cwd: process.cwd(),\n allowWarnings: opts.allowWarnings,\n })\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2))\n process.exit(report.exitCode)\n }\n\n console.log('')\n if (report.status === 'no-project-detected') {\n console.log(\" · No theokit project detected here (no 'app/' or 'server/' dir).\")\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.status === 'not-a-theokit-project') {\n console.log(' ✗ Not a TheoKit project (theokit not in package.json).')\n console.log(' Run this command from a TheoKit project root.')\n console.log('')\n process.exit(report.exitCode)\n }\n if (report.violations.length === 0) {\n console.log(' ✓ Upgrade-readiness 0.3: no violations detected.')\n console.log('')\n // T1.3 — friendly post-success URL (blueprint R4; Next.js next-codemod precedent).\n // Anchor canon: #rollback (existing in docs/migration/0.2-to-0.3.md per T1.1).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3')\n console.log(' Need to roll back temporarily? See #rollback')\n console.log('')\n process.exit(report.exitCode)\n }\n console.log(` ✗ Upgrade-readiness 0.3: ${report.violations.length} violation(s)`)\n console.log('')\n for (const v of report.violations) {\n console.log(` ${v.file}:${v.line} [${v.rule}]`)\n console.log(` ${v.message}`)\n console.log(` fix: ${v.fix}`)\n console.log('')\n }\n // T1.3 — print migration-guide URL on violations branch too (one canonical link\n // regardless of verdict, per blueprint Q5 Astro CHANGELOG URL pattern).\n console.log(' Migration guide: https://theokit.dev/migration/0.2-to-0.3#rollback')\n console.log('')\n process.exit(report.exitCode)\n}\n"],"mappings":"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","names":["before"]}
|
|
@@ -74,7 +74,7 @@ interface TheoPluginOptions {
|
|
|
74
74
|
* — Vite cannot trace those without a hint, and the browser receives a
|
|
75
75
|
* `Failed to resolve module specifier '<pkg>'` error.
|
|
76
76
|
*
|
|
77
|
-
* Default targets pre-bundled regardless: `@
|
|
77
|
+
* Default targets pre-bundled regardless: `@theokit/ui`, `lucide-react`.
|
|
78
78
|
*
|
|
79
79
|
* Example:
|
|
80
80
|
* viteOptimizeDeps: ['mermaid']
|
|
@@ -104,7 +104,7 @@ interface TheoPluginOptions {
|
|
|
104
104
|
* `plugins` array.
|
|
105
105
|
*
|
|
106
106
|
* Consumers should prefer `await theoPluginAsync(...)` over `theoPlugin(...)`
|
|
107
|
-
* when they want @
|
|
107
|
+
* when they want @theokit/ui + @tailwindcss/vite auto-chaining. The sync
|
|
108
108
|
* factory remains for backward compatibility and tests.
|
|
109
109
|
*/
|
|
110
110
|
declare function theoPluginAsync(rootOrOptions?: string | TheoPluginOptions): Promise<Plugin[]>;
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
defineTheoIntegration,
|
|
6
6
|
theoPlugin,
|
|
7
7
|
theoPluginAsync
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-RIBIAL7A.js";
|
|
9
9
|
import "../chunk-7BIM27LV.js";
|
|
10
10
|
import "../chunk-O4BLVPML.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-CZM6WWWS.js";
|
|
12
12
|
import "../chunk-2BQYEBCK.js";
|
|
13
13
|
import "../chunk-7DHGV77J.js";
|
|
14
14
|
import "../chunk-PB4GR7EP.js";
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
defineTheoIntegration,
|
|
8
8
|
theoPlugin,
|
|
9
9
|
theoPluginAsync
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-F7RLPDXW.js";
|
|
11
|
+
import "./chunk-TPCT3MXV.js";
|
|
12
|
+
import "./chunk-XP7YXRHO.js";
|
|
13
|
+
import "./chunk-X4JAX2U3.js";
|
|
14
14
|
import "./chunk-5ODOE6EF.js";
|
|
15
15
|
import "./chunk-O7335ZGH.js";
|
|
16
16
|
import "./chunk-WZ7CPVQB.js";
|
|
@@ -25,4 +25,4 @@ export {
|
|
|
25
25
|
theoPlugin,
|
|
26
26
|
theoPluginAsync
|
|
27
27
|
};
|
|
28
|
-
//# sourceMappingURL=vite-plugin-
|
|
28
|
+
//# sourceMappingURL=vite-plugin-FTYXDKKG.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "theokit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"scripts": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"zod-to-json-schema": "^3.24.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@
|
|
78
|
+
"@theokit/ui": "^0.13.0",
|
|
79
79
|
"db0": "^0.3.0",
|
|
80
80
|
"react": "^19.0.0",
|
|
81
81
|
"react-dom": "^19.0.0",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"zod": "^3.24.0"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
|
-
"@
|
|
88
|
+
"@theokit/ui": {
|
|
89
89
|
"optional": true
|
|
90
90
|
},
|
|
91
91
|
"db0": {
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@types/busboy": "^1.5.4",
|
|
103
103
|
"@types/pg": "^8.20.0",
|
|
104
104
|
"@types/picomatch": "^4.0.0",
|
|
105
|
-
"@
|
|
105
|
+
"@theokit/sdk": "workspace:*",
|
|
106
106
|
"better-sqlite3": "^12.10.0",
|
|
107
107
|
"db0": "^0.3.0",
|
|
108
108
|
"pg": "^8.21.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin/app-typed-client.ts"],"sourcesContent":["/**\n * Phase 2 + Phase 4 of G1 (g1-client-codegen-plan.md):\n *\n * 1. `generateClientDts(manifest, dtsOutPath)` — pure function emitting the\n * `.theo/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 {\n generateManifest,\n type ManifestRoute,\n type TheoManifest,\n} from '../server/scan/manifest.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 const tokens: Array<{ token: string; bracket: boolean }> = []\n for (const seg of segments) {\n const norm = normalizeSegment(seg)\n tokens.push({ token: norm.identifier, bracket: norm.bracket })\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: Array<{ 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 const lines: string[] = []\n if (isRoot) {\n lines.push('{')\n } else {\n lines.push('{')\n }\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 paramsField = node.paramNamesAccumulated.length > 0\n ? `{ params: { ${node.paramNamesAccumulated.map((p) => `${p}: string`).join('; ')} } } & `\n : ''\n lines.push(\n `${indent} ${lower}: (opts${node.paramNamesAccumulated.length === 0 ? '?' : ''}: ${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) ? name : `'${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 =\n ctx.importLines.length > 0 ? ctx.importLines.join('\\n') + '\\n\\n' : '\\n'\n const warningsBlock =\n ctx.warnings.length > 0\n ? ' // ' + ctx.warnings.join('\\n // ') + '\\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 `.theo/` output dir (where `client.d.ts` lands). */\n distDir: string\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(\n opts.distDir.replace(/\\\\/g, '/'),\n 'client.d.ts',\n )\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 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 // eslint-disable-next-line no-console\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 // eslint-disable-next-line no-console\n console.error('[theokit:app-typed-client] initial codegen error:', err)\n }\n },\n buildEnd() {\n try {\n emitClientDts(opts)\n } catch (err) {\n // eslint-disable-next-line no-console\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 onFile = (file: string): void => {\n const normalized = file.replace(/\\\\/g, '/')\n if (normalized.startsWith(routesGlob)) 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":[]}
|