theokit 0.5.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-DL74V6SR.js → actions-virtual-module-4WVKHQ2X.js} +5 -2
- package/dist/actions-virtual-module-4WVKHQ2X.js.map +1 -0
- package/dist/{actions-virtual-module-ZDD54ZZE.js → actions-virtual-module-LTOXBSCF.js} +16 -2
- package/dist/actions-virtual-module-LTOXBSCF.js.map +1 -0
- package/dist/{app-typed-client-4M4NVSK3.js → app-typed-client-4N5OGNOQ.js} +15 -4
- package/dist/app-typed-client-4N5OGNOQ.js.map +1 -0
- package/dist/{app-typed-client-4GIKUKRT.js → app-typed-client-SAETWZT5.js} +5 -4
- package/dist/app-typed-client-SAETWZT5.js.map +1 -0
- package/dist/{aws-lambda-SCRGTGF5.js → aws-lambda-WT4HRBNL.js} +3 -3
- package/dist/{build-R74EU6BP.js → build-KQD2EDU4.js} +10 -11
- package/dist/build-KQD2EDU4.js.map +1 -0
- package/dist/{bun-FURRCQMS.js → bun-UTDVN6R4.js} +3 -3
- package/dist/{check-NXYPLM6M.js → check-F3UDYSZ4.js} +2 -2
- package/dist/chunk-223EFY5X.js +469 -0
- package/dist/chunk-223EFY5X.js.map +1 -0
- package/dist/{chunk-KJLECZWQ.js → chunk-3FYJ7R7N.js} +57 -14
- package/dist/chunk-3FYJ7R7N.js.map +1 -0
- package/dist/{chunk-HDA6M7P4.js → chunk-3LVRAGAZ.js} +3 -5
- package/dist/{chunk-JD7AQNJS.js → chunk-4I47JW5O.js} +38 -425
- package/dist/chunk-4I47JW5O.js.map +1 -0
- package/dist/{chunk-H4J2LECY.js → chunk-6BUUZ2PK.js} +9 -9
- package/dist/chunk-6BUUZ2PK.js.map +1 -0
- package/dist/{chunk-P2RS3WWY.js → chunk-6FYD34NX.js} +194 -22
- package/dist/chunk-6FYD34NX.js.map +1 -0
- package/dist/{chunk-X3VNROZ7.js → chunk-6VSKLYT6.js} +78 -21
- package/dist/chunk-6VSKLYT6.js.map +1 -0
- package/dist/{chunk-FGOX37NB.js → chunk-BNGBG2SQ.js} +21 -2
- package/dist/chunk-BNGBG2SQ.js.map +1 -0
- package/dist/chunk-CBGRFVF5.js +421 -0
- package/dist/chunk-CBGRFVF5.js.map +1 -0
- package/dist/{chunk-X32KQH5C.js → chunk-CCVC6P6B.js} +3 -3
- package/dist/chunk-CCVC6P6B.js.map +1 -0
- package/dist/{chunk-DNMSV3R3.js → chunk-D7ZH7DTG.js} +3 -3
- package/dist/chunk-D7ZH7DTG.js.map +1 -0
- package/dist/{chunk-MWYSRZI4.js → chunk-EQV67HZL.js} +2 -2
- package/dist/{chunk-CG563V5M.js → chunk-IEES3CHD.js} +39 -5
- package/dist/chunk-IEES3CHD.js.map +1 -0
- package/dist/{chunk-DUKXQNM7.js → chunk-IHMJV2OK.js} +2 -2
- package/dist/{chunk-WGHJD6I7.js → chunk-JAIKGP3Q.js} +76 -21
- package/dist/chunk-JAIKGP3Q.js.map +1 -0
- package/dist/{chunk-B3L3TJVF.js → chunk-JBCHWRKF.js} +50 -50
- package/dist/chunk-JBCHWRKF.js.map +1 -0
- package/dist/{chunk-XMS5VRIK.js → chunk-JQSFBJR5.js} +3 -3
- package/dist/chunk-JQSFBJR5.js.map +1 -0
- package/dist/{chunk-S74FECKW.js → chunk-MV4LKTW6.js} +119 -153
- package/dist/chunk-MV4LKTW6.js.map +1 -0
- package/dist/chunk-PBEH6NXR.js +44 -0
- package/dist/chunk-PBEH6NXR.js.map +1 -0
- package/dist/chunk-PPPR5DGR.js +1 -0
- package/dist/{chunk-NZXH2LQQ.js → chunk-SJIRP73B.js} +18 -1
- package/dist/chunk-SJIRP73B.js.map +1 -0
- package/dist/cli/index.js +25 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +4 -4
- package/dist/{cloudflare-IZ6VJ2OU.js → cloudflare-U5GYYI47.js} +3 -3
- package/dist/db-ZPDW3UCU.js +78 -0
- package/dist/db-ZPDW3UCU.js.map +1 -0
- package/dist/{deno-deploy-O73NBXIW.js → deno-deploy-3QHJ3AJQ.js} +3 -3
- package/dist/{dev-ZU46C5AZ.js → dev-NDEZA2MP.js} +21 -11
- package/dist/dev-NDEZA2MP.js.map +1 -0
- package/dist/{dev-emit-JBVINGHU.js → dev-emit-O4EGOSNV.js} +77 -24
- package/dist/dev-emit-O4EGOSNV.js.map +1 -0
- package/dist/{dev-emit-66FCOS7V.js → dev-emit-OUPI7NAQ.js} +4 -5
- package/dist/{dev-emit-66FCOS7V.js.map → dev-emit-OUPI7NAQ.js.map} +1 -1
- package/dist/devtools/entry.js +1 -0
- package/dist/devtools/entry.js.map +1 -1
- package/dist/{dist-KRW6OVD4.js → dist-6GPD6WCU.js} +10231 -8164
- package/dist/dist-6GPD6WCU.js.map +1 -0
- package/dist/generate-ODDAYXNR.js +494 -0
- package/dist/generate-ODDAYXNR.js.map +1 -0
- package/dist/{index-CuHICqb9.d.ts → index-DWZF8uQD.d.ts} +132 -132
- package/dist/index.d.ts +32 -3
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/{info-CORLCPEJ.js → info-CSGWIAXA.js} +5 -5
- package/dist/{lib-NST3PNZX.js → lib-JBI3XXH3.js} +27 -27
- package/dist/{lib-NST3PNZX.js.map → lib-JBI3XXH3.js.map} +1 -1
- package/dist/{netlify-O7G3RLK4.js → netlify-DUB7BZ4E.js} +3 -3
- package/dist/{node-LXPQLMVB.js → node-ELE236WK.js} +3 -3
- package/dist/{openapi-TEOUOIJ2.js → openapi-NXGRXABL.js} +7 -8
- package/dist/{openapi-TEOUOIJ2.js.map → openapi-NXGRXABL.js.map} +1 -1
- package/dist/registry-B7FA6KMI.js +25 -0
- package/dist/{routes-GAXVWJUK.js → routes-6A5XFGF7.js} +7 -9
- package/dist/routes-6A5XFGF7.js.map +1 -0
- package/dist/{scan-NQFI5S7P.js → scan-I5PT6TUX.js} +2 -2
- package/dist/{schema-D3v8VB7o.d.ts → schema-BpH6ivDY.d.ts} +2 -4
- package/dist/{schema-Z5VJ2I76.js → schema-NPI4NJEF.js} +3 -3
- package/dist/server/auth/index.d.ts +20 -20
- package/dist/server/auth/index.js +3 -5
- package/dist/server/define/index.d.ts +7 -0
- package/dist/server/define/index.js +1 -1
- package/dist/server/http/index.d.ts +2 -2
- package/dist/server/http/index.js +1 -2
- package/dist/server/index.d.ts +36 -3
- package/dist/server/index.js +66 -61
- package/dist/server/index.js.map +1 -1
- package/dist/server/scan/index.d.ts +13 -13
- package/dist/server/scan/index.js +8 -12
- package/dist/server/security/index.d.ts +69 -69
- package/dist/server/security/index.js +1 -1
- package/dist/{services-typed-client-ASZL7FQV.js → services-typed-client-24VBMDOF.js} +2 -2
- package/dist/{services-typed-client-KK6RHRDG.js → services-typed-client-IETY2SAK.js} +2 -2
- package/dist/{start-HGUNNF5X.js → start-SPFWOGHL.js} +95 -80
- package/dist/start-SPFWOGHL.js.map +1 -0
- package/dist/{static-EO73I4C7.js → static-YWF2M6YT.js} +4 -4
- package/dist/{theo-cloud-HBCYEODQ.js → theo-cloud-EUK56I7O.js} +2 -2
- package/dist/{vercel-GSFDMF7K.js → vercel-LGDQWYZD.js} +3 -3
- package/dist/vite-plugin/index.d.ts +1 -1
- package/dist/vite-plugin/index.js +6 -7
- package/dist/{vite-plugin-TX5H4MB6.js → vite-plugin-43KQU5S7.js} +11 -9
- package/dist/vite-plugin-43KQU5S7.js.map +1 -0
- package/package.json +22 -19
- package/dist/actions-virtual-module-DL74V6SR.js.map +0 -1
- package/dist/actions-virtual-module-ZDD54ZZE.js.map +0 -1
- package/dist/app-typed-client-4GIKUKRT.js.map +0 -1
- package/dist/app-typed-client-4M4NVSK3.js.map +0 -1
- package/dist/build-R74EU6BP.js.map +0 -1
- package/dist/chunk-4HBI7DHP.js +0 -20
- package/dist/chunk-4HBI7DHP.js.map +0 -1
- package/dist/chunk-B3L3TJVF.js.map +0 -1
- package/dist/chunk-CG563V5M.js.map +0 -1
- package/dist/chunk-DNMSV3R3.js.map +0 -1
- package/dist/chunk-FGOX37NB.js.map +0 -1
- package/dist/chunk-GPF64ENM.js +0 -73
- package/dist/chunk-H4J2LECY.js.map +0 -1
- package/dist/chunk-HDA6M7P4.js.map +0 -1
- package/dist/chunk-JD7AQNJS.js.map +0 -1
- package/dist/chunk-KJLECZWQ.js.map +0 -1
- package/dist/chunk-NZXH2LQQ.js.map +0 -1
- package/dist/chunk-OLPB36O2.js +0 -259
- package/dist/chunk-OLPB36O2.js.map +0 -1
- package/dist/chunk-P2RS3WWY.js.map +0 -1
- package/dist/chunk-P3SGM4NR.js +0 -156
- package/dist/chunk-P3SGM4NR.js.map +0 -1
- package/dist/chunk-S74FECKW.js.map +0 -1
- package/dist/chunk-UVHRD33F.js +0 -181
- package/dist/chunk-UVHRD33F.js.map +0 -1
- package/dist/chunk-WGHJD6I7.js.map +0 -1
- package/dist/chunk-X32KQH5C.js.map +0 -1
- package/dist/chunk-X3VNROZ7.js.map +0 -1
- package/dist/chunk-XMS5VRIK.js.map +0 -1
- package/dist/dev-ZU46C5AZ.js.map +0 -1
- package/dist/dev-emit-JBVINGHU.js.map +0 -1
- package/dist/dist-KRW6OVD4.js.map +0 -1
- package/dist/generate-AZ2K6Z2Z.js +0 -281
- package/dist/generate-AZ2K6Z2Z.js.map +0 -1
- package/dist/registry-KW4F4D2L.js +0 -25
- package/dist/routes-GAXVWJUK.js.map +0 -1
- package/dist/start-HGUNNF5X.js.map +0 -1
- package/dist/{aws-lambda-SCRGTGF5.js.map → aws-lambda-WT4HRBNL.js.map} +0 -0
- package/dist/{bun-FURRCQMS.js.map → bun-UTDVN6R4.js.map} +0 -0
- package/dist/{check-NXYPLM6M.js.map → check-F3UDYSZ4.js.map} +0 -0
- package/dist/{chunk-GPF64ENM.js.map → chunk-3LVRAGAZ.js.map} +0 -0
- package/dist/{chunk-MWYSRZI4.js.map → chunk-EQV67HZL.js.map} +0 -0
- package/dist/{chunk-DUKXQNM7.js.map → chunk-IHMJV2OK.js.map} +0 -0
- package/dist/{node-LXPQLMVB.js.map → chunk-PPPR5DGR.js.map} +0 -0
- package/dist/{cloudflare-IZ6VJ2OU.js.map → cloudflare-U5GYYI47.js.map} +0 -0
- package/dist/{deno-deploy-O73NBXIW.js.map → deno-deploy-3QHJ3AJQ.js.map} +0 -0
- package/dist/{info-CORLCPEJ.js.map → info-CSGWIAXA.js.map} +0 -0
- package/dist/{module-loader-Cfz7dgCP.d.ts → match-CfbEFRG4.d.ts} +4 -4
- /package/dist/{netlify-O7G3RLK4.js.map → netlify-DUB7BZ4E.js.map} +0 -0
- /package/dist/{scan-NQFI5S7P.js.map → node-ELE236WK.js.map} +0 -0
- /package/dist/{registry-KW4F4D2L.js.map → registry-B7FA6KMI.js.map} +0 -0
- /package/dist/{schema-Z5VJ2I76.js.map → scan-I5PT6TUX.js.map} +0 -0
- /package/dist/{vite-plugin-TX5H4MB6.js.map → schema-NPI4NJEF.js.map} +0 -0
- /package/dist/{services-typed-client-ASZL7FQV.js.map → services-typed-client-24VBMDOF.js.map} +0 -0
- /package/dist/{services-typed-client-KK6RHRDG.js.map → services-typed-client-IETY2SAK.js.map} +0 -0
- /package/dist/{static-EO73I4C7.js.map → static-YWF2M6YT.js.map} +0 -0
- /package/dist/{theo-cloud-HBCYEODQ.js.map → theo-cloud-EUK56I7O.js.map} +0 -0
- /package/dist/{vercel-GSFDMF7K.js.map → vercel-LGDQWYZD.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/security/csrf-readiness-store.ts","../src/server/security/csp-report.ts","../src/server/security/csrf-readiness-endpoint.ts","../src/server/security/security-headers.ts"],"sourcesContent":["/**\n * T2.2 — In-memory bounded counter for CSRF warn events.\n *\n * Aggregates `csrf.warn` payloads by `(method, path, reason)` triple.\n * Used by the `/__theo/csrf-readiness` endpoint to expose a summary the\n * developer (or devtools tab) can inspect, so users do NOT need to grep\n * stdout to know which endpoints will break under 0.3.0 strict CSRF.\n *\n * Bounded at 1000 distinct keys (EC-22). Eviction is LRU-by-insertion —\n * when the cap is hit, the oldest key is dropped and a re-record of an\n * evicted key starts fresh (count: 1).\n *\n * Single-threaded by virtue of the JS event loop; Map operations are\n * atomic. NOT shared across processes — each worker has its own store.\n */\n\nexport interface CsrfWarnRecord {\n method: string\n path: string\n reason: string\n}\n\nexport interface CsrfReadinessRouteSummary {\n method: string\n path: string\n reason: string\n count: number\n firstSeen: string\n lastSeen: string\n}\n\nexport interface CsrfReadinessSummary {\n generatedAt: string\n totalEvents: number\n routes: CsrfReadinessRouteSummary[]\n}\n\ninterface StoredEntry {\n count: number\n firstSeen: string\n lastSeen: string\n}\n\nexport class CsrfReadinessStore {\n static readonly MAX_ENTRIES = 1000\n private readonly entries = new Map<string, StoredEntry>()\n\n private keyFor(event: CsrfWarnRecord): string {\n return `${event.method}\u0000${event.path}\u0000${event.reason}`\n }\n\n record(event: CsrfWarnRecord): void {\n const key = this.keyFor(event)\n const now = new Date().toISOString()\n const existing = this.entries.get(key)\n if (existing) {\n existing.count++\n existing.lastSeen = now\n return\n }\n if (this.entries.size >= CsrfReadinessStore.MAX_ENTRIES) {\n const firstKey = this.entries.keys().next().value\n if (firstKey !== undefined) this.entries.delete(firstKey)\n }\n this.entries.set(key, { count: 1, firstSeen: now, lastSeen: now })\n }\n\n summary(): CsrfReadinessSummary {\n let total = 0\n const routes: CsrfReadinessRouteSummary[] = []\n for (const [key, entry] of this.entries) {\n const [method, path, reason] = key.split('\u0000')\n routes.push({\n method,\n path,\n reason,\n count: entry.count,\n firstSeen: entry.firstSeen,\n lastSeen: entry.lastSeen,\n })\n total += entry.count\n }\n return {\n generatedAt: new Date().toISOString(),\n totalEvents: total,\n routes,\n }\n }\n\n reset(): void {\n this.entries.clear()\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport { safeAudit, type AuditLogger } from '../observability/audit-log.js'\n\n/**\n * T5.1 — Built-in CSP report endpoint.\n *\n * Browsers POST violation reports to `report-uri` (legacy `application/csp-report`)\n * or `Reporting API` (`application/reports+json`). Framework auto-registers this\n * endpoint so `cspMode: 'report-only'` is actually useful out of the box.\n *\n * Forwards normalized violations to:\n * - audit logger (`csp.violation`)\n * - devtools dispatcher (dev only, for Errors tab)\n * - optional user hook (Sentry, etc.)\n *\n * EC-2: browser MAY send `{\"csp-report\": null}`, empty `{}`, or\n * reports+json entries lacking `body`. Handler MUST short-circuit to\n * 204 (valid format, no violation), NEVER crash via null deref.\n */\n\nexport const CSP_REPORT_PATH = '/__theo/csp-report'\n\n/** Max body bytes accepted. Real CSP reports are < 2 KB; 16 KB is generous. */\nconst MAX_BODY = 16 * 1024\n\nexport interface CspViolation {\n blockedUrl: string\n documentUrl: string\n violatedDirective: string\n effectiveDirective?: string\n originalPolicy?: string\n disposition?: 'enforce' | 'report'\n statusCode?: number\n sourceFile?: string\n lineNumber?: number\n columnNumber?: number\n}\n\nexport interface CspReportHandlerOptions {\n auditLogger?: AuditLogger\n devtoolsDispatcher?: {\n onCspViolation?: (v: CspViolation) => void\n }\n /** Optional user-provided sink (Sentry, custom log router). Errors here are swallowed. */\n onViolation?: (v: CspViolation) => void\n}\n\n/**\n * Map a legacy `application/csp-report` payload to the internal shape.\n * Caller must ensure `raw` is a non-null object.\n */\nfunction pickHeader(value: string | string[] | undefined): string {\n if (typeof value === 'string') return value\n if (Array.isArray(value) && value.length > 0) return value[0]\n return ''\n}\n\nfunction toStringSafe(value: unknown, fallback = '(missing)'): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n return fallback\n}\n\nexport function normalizeLegacy(raw: Record<string, unknown>): CspViolation {\n return {\n blockedUrl: toStringSafe(raw['blocked-uri']),\n documentUrl: toStringSafe(raw['document-uri']),\n violatedDirective: toStringSafe(raw['violated-directive']),\n effectiveDirective:\n typeof raw['effective-directive'] === 'string' ? raw['effective-directive'] : undefined,\n originalPolicy: typeof raw['original-policy'] === 'string' ? raw['original-policy'] : undefined,\n disposition:\n raw.disposition === 'enforce' || raw.disposition === 'report' ? raw.disposition : undefined,\n statusCode: typeof raw['status-code'] === 'number' ? raw['status-code'] : undefined,\n sourceFile: typeof raw['source-file'] === 'string' ? raw['source-file'] : undefined,\n lineNumber: typeof raw['line-number'] === 'number' ? raw['line-number'] : undefined,\n columnNumber: typeof raw['column-number'] === 'number' ? raw['column-number'] : undefined,\n }\n}\n\n/**\n * Map a `reports+json` entry to the internal shape. Returns `null` if\n * the entry lacks a usable `body` object (EC-2).\n */\nexport function normalizeNew(entry: unknown): CspViolation | null {\n if (!entry || typeof entry !== 'object') return null\n const body = (entry as { body?: unknown }).body\n if (!body || typeof body !== 'object') return null\n const b = body as Record<string, unknown>\n return {\n blockedUrl: toStringSafe(b.blockedURL),\n documentUrl: toStringSafe(b.documentURL),\n violatedDirective: toStringSafe(b.violatedDirective),\n effectiveDirective: typeof b.effectiveDirective === 'string' ? b.effectiveDirective : undefined,\n originalPolicy: typeof b.originalPolicy === 'string' ? b.originalPolicy : undefined,\n disposition:\n b.disposition === 'enforce' || b.disposition === 'report' ? b.disposition : undefined,\n statusCode: typeof b.statusCode === 'number' ? b.statusCode : undefined,\n sourceFile: typeof b.sourceFile === 'string' ? b.sourceFile : undefined,\n lineNumber: typeof b.lineNumber === 'number' ? b.lineNumber : undefined,\n columnNumber: typeof b.columnNumber === 'number' ? b.columnNumber : undefined,\n }\n}\n\nasync function readBody(req: IncomingMessage, maxBytes: number): Promise<string> {\n return await new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = []\n let total = 0\n req.on('data', (chunk: Buffer) => {\n total += chunk.length\n if (total > maxBytes) {\n reject(new Error('body too large'))\n req.destroy()\n return\n }\n chunks.push(chunk)\n })\n req.on('end', () => {\n resolve(Buffer.concat(chunks).toString('utf8'))\n })\n req.on('error', reject)\n })\n}\n\nexport async function handleCspReport(\n req: IncomingMessage,\n res: ServerResponse,\n opts: CspReportHandlerOptions,\n): Promise<void> {\n const ctHeader = req.headers['content-type']\n const ct = pickHeader(ctHeader)\n\n let raw: string\n try {\n raw = await readBody(req, MAX_BODY)\n } catch {\n res.statusCode = 413\n res.end()\n return\n }\n\n let violations: CspViolation[]\n try {\n if (ct.startsWith('application/csp-report')) {\n // EC-2: browsers MAY POST {\"csp-report\": null} or {} on\n // disposition='report' policies. Guard against null/undefined/non-object.\n const parsed = JSON.parse(raw) as Record<string, unknown>\n const inner = parsed['csp-report']\n if (!inner || typeof inner !== 'object') {\n res.statusCode = 204\n res.end()\n return\n }\n violations = [normalizeLegacy(inner as Record<string, unknown>)]\n } else if (ct.startsWith('application/reports+json')) {\n // EC-2: filter out entries lacking `body` BEFORE normalizing.\n const parsed: unknown = JSON.parse(raw)\n const entries = Array.isArray(parsed) ? parsed : []\n violations = entries.map((e) => normalizeNew(e)).filter((v): v is CspViolation => v !== null)\n } else {\n res.statusCode = 415\n res.end()\n return\n }\n } catch {\n res.statusCode = 400\n res.end()\n return\n }\n\n for (const v of violations) {\n safeAudit(opts.auditLogger, {\n action: 'csp.violation',\n metadata: v as unknown as Record<string, unknown>,\n })\n try {\n opts.devtoolsDispatcher?.onCspViolation?.(v)\n } catch {\n // dispatcher errors are isolated from the request\n }\n try {\n opts.onViolation?.(v)\n } catch {\n // user hook throws are swallowed — never crash the request\n }\n }\n\n res.statusCode = 204\n res.end()\n}\n\n/**\n * T5a.2 Phase B slice 4/6 — Web-Standards CSP report handler.\n *\n * Mirror of `handleCspReport(req: IncomingMessage, res: ServerResponse,\n * opts): Promise<void>` for the Web `Request` shape. Returns\n * `Response` directly instead of mutating `res`.\n *\n * Same content-type dispatch (legacy `application/csp-report` vs new\n * `application/reports+json`), same normalizers (`normalizeLegacy`,\n * `normalizeNew`), same side-effect loop (safeAudit + devtools + user\n * hook). The body cap is enforced post-read because Web Request body\n * streaming doesn't have a portable mid-stream rejection primitive\n * across CF Workers / Bun / Deno; CSP reports are < 2 KB typical, well\n * under MAX_BODY (16 KB).\n *\n * Returns:\n * - 204 on accepted reports OR no-op empty payload\n * - 413 on body too large (post-read check)\n * - 415 on unsupported content-type\n * - 400 on malformed JSON\n */\nasync function readBodyFromRequest(request: Request, maxBytes: number): Promise<string | null> {\n const contentLengthHeader = request.headers.get('content-length')\n if (contentLengthHeader !== null) {\n const declared = Number.parseInt(contentLengthHeader, 10)\n if (Number.isFinite(declared) && declared > maxBytes) return null\n }\n const text = await request.text()\n if (text.length > maxBytes) return null\n return text\n}\n\nfunction dispatchViolations(violations: CspViolation[], opts: CspReportHandlerOptions): void {\n for (const v of violations) {\n safeAudit(opts.auditLogger, {\n action: 'csp.violation',\n metadata: v as unknown as Record<string, unknown>,\n })\n try {\n opts.devtoolsDispatcher?.onCspViolation?.(v)\n } catch {\n // dispatcher errors are isolated from the request\n }\n try {\n opts.onViolation?.(v)\n } catch {\n // user hook throws are swallowed — never crash the request\n }\n }\n}\n\nexport async function handleCspReportRequest(\n request: Request,\n opts: CspReportHandlerOptions,\n): Promise<Response> {\n const ct = request.headers.get('content-type') ?? ''\n\n const raw = await readBodyFromRequest(request, MAX_BODY)\n if (raw === null) {\n return new Response(null, { status: 413 })\n }\n\n let violations: CspViolation[]\n try {\n if (ct.startsWith('application/csp-report')) {\n const parsed = JSON.parse(raw) as Record<string, unknown>\n const inner = parsed['csp-report']\n if (!inner || typeof inner !== 'object') {\n return new Response(null, { status: 204 })\n }\n violations = [normalizeLegacy(inner as Record<string, unknown>)]\n } else if (ct.startsWith('application/reports+json')) {\n const parsed: unknown = JSON.parse(raw)\n const entries = Array.isArray(parsed) ? parsed : []\n violations = entries.map((e) => normalizeNew(e)).filter((v): v is CspViolation => v !== null)\n } else {\n return new Response(null, { status: 415 })\n }\n } catch {\n return new Response(null, { status: 400 })\n }\n\n dispatchViolations(violations, opts)\n return new Response(null, { status: 204 })\n}\n","/**\n * T2.2 — `/__theo/csrf-readiness` endpoint.\n *\n * GET /__theo/csrf-readiness → 200 + JSON summary\n * POST /__theo/csrf-readiness/reset → 204; clears the store\n *\n * The reset endpoint enforces CSRF (own dog food) — requires\n * `X-Theo-Action: 1` AND a matching Origin header (EC-15). This avoids\n * the endpoint being weaponizable from a cross-origin page when the\n * endpoint is opt-in exposed in production.\n *\n * Mount opt-in: in dev mode, the host wires this in unconditionally. In\n * production, only mount when `config.security.csrfTelemetry.exposeReadinessEndpoint === true`.\n * EC-10 parallel: returns 404 (via boolean false return) when the URL\n * does not match — the caller continues normal request handling.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\nimport type { CsrfReadinessStore } from './csrf-readiness-store.js'\n\nexport const CSRF_READINESS_PATH = '/__theo/csrf-readiness'\nexport const CSRF_READINESS_RESET_PATH = '/__theo/csrf-readiness/reset'\n\nfunction originMatchesHost(req: IncomingMessage): boolean {\n const origin = req.headers.origin\n if (typeof origin !== 'string') return false\n const host = req.headers.host\n if (typeof host !== 'string') return false\n try {\n const parsed = new URL(origin)\n return parsed.host === host\n } catch {\n return false\n }\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { 'content-type': 'application/json' })\n res.end(JSON.stringify(body))\n}\n\nfunction sendNoContent(res: ServerResponse): void {\n res.writeHead(204)\n res.end()\n}\n\nfunction sendError(res: ServerResponse, status: number, code: string, message: string): void {\n res.writeHead(status, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ error: { code, message } }))\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async return for future telemetry hooks\nexport async function handleCsrfReadiness(\n req: IncomingMessage,\n res: ServerResponse,\n store: CsrfReadinessStore,\n): Promise<boolean> {\n const url = (req.url ?? '').split('?')[0]\n const method = req.method ?? 'GET'\n\n if (url === CSRF_READINESS_PATH) {\n if (method === 'GET') {\n sendJson(res, 200, store.summary())\n return true\n }\n sendError(res, 405, 'METHOD_NOT_ALLOWED', `Use GET on ${CSRF_READINESS_PATH}`)\n return true\n }\n\n if (url === CSRF_READINESS_RESET_PATH) {\n if (method !== 'POST') {\n sendError(res, 405, 'METHOD_NOT_ALLOWED', `Use POST on ${CSRF_READINESS_RESET_PATH}`)\n return true\n }\n // Enforce CSRF on our own endpoint (dog-food).\n const hasHeader = req.headers['x-theo-action'] === '1'\n if (!hasHeader || !originMatchesHost(req)) {\n sendError(res, 403, 'CSRF_INVALID', 'Reset requires X-Theo-Action: 1 + same-origin')\n return true\n }\n store.reset()\n sendNoContent(res)\n return true\n }\n\n return false\n}\n\n/**\n * T5a.2 Phase B slice 3/6 — Web-Standards-shaped CSRF readiness handler.\n *\n * Mirror of `handleCsrfReadiness(req: IncomingMessage, res: ServerResponse,\n * store): Promise<boolean>` for the Web `Request` shape. Returns\n * `Response | null` instead of mutating `res` and returning a boolean:\n * - `Response` → this handler claimed the URL; caller short-circuits.\n * - `null` → URL doesn't match our endpoint; caller continues normal dispatch.\n *\n * Same routes (GET `CSRF_READINESS_PATH`, POST `CSRF_READINESS_RESET_PATH`).\n * Same CSRF dog-food on reset (X-Theo-Action + same-origin).\n *\n * Wire into `executeWebRequest` integration via a future Phase B sub-slice\n * (consumer can also call this directly today via the\n * `theokit/server/security` sub-path).\n */\nfunction buildJsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\nfunction buildErrorResponse(status: number, code: string, message: string): Response {\n return buildJsonResponse(status, { error: { code, message } })\n}\n\n/**\n * Returns true when `Origin` header and request URL host match (RFC 6454\n * same-origin check). Web-Standards counterpart of `originMatchesHost`.\n */\nfunction originMatchesHostFromRequest(request: Request): boolean {\n const origin = request.headers.get('origin')\n if (origin === null || origin.length === 0) return false\n const host = request.headers.get('host')\n if (host === null || host.length === 0) {\n // Fallback: derive host from request.url (Web Request guarantees absolute URL)\n try {\n const reqHost = new URL(request.url).host\n const originHost = new URL(origin).host\n return originHost === reqHost\n } catch {\n return false\n }\n }\n try {\n return new URL(origin).host === host\n } catch {\n return false\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await -- async return for future telemetry hooks\nexport async function handleCsrfReadinessRequest(\n request: Request,\n store: CsrfReadinessStore,\n): Promise<Response | null> {\n const url = new URL(request.url).pathname\n const method = request.method.toUpperCase()\n\n if (url === CSRF_READINESS_PATH) {\n if (method === 'GET') {\n return buildJsonResponse(200, store.summary())\n }\n return buildErrorResponse(405, 'METHOD_NOT_ALLOWED', `Use GET on ${CSRF_READINESS_PATH}`)\n }\n\n if (url === CSRF_READINESS_RESET_PATH) {\n if (method !== 'POST') {\n return buildErrorResponse(\n 405,\n 'METHOD_NOT_ALLOWED',\n `Use POST on ${CSRF_READINESS_RESET_PATH}`,\n )\n }\n // Enforce CSRF on our own endpoint (dog-food).\n const hasHeader = request.headers.get('x-theo-action') === '1'\n if (!hasHeader || !originMatchesHostFromRequest(request)) {\n return buildErrorResponse(\n 403,\n 'CSRF_INVALID',\n 'Reset requires X-Theo-Action: 1 + same-origin',\n )\n }\n store.reset()\n return new Response(null, { status: 204 })\n }\n\n return null\n}\n","import type { ServerResponse } from 'node:http'\n\n/**\n * Phase 6 — Default Security Headers (D4 / EC-2).\n *\n * Per-response security baseline. The framework applies these BEFORE the\n * route handler runs so a handler can still override via `res.setHeader`.\n *\n * EC-2 backward-compatibility: CSP ships in `report-only` mode by default\n * for 0.2.0. Existing apps with inline scripts or third-party CDN scripts\n * keep working but consumers see violation reports via their CSP report\n * collector (or browser DevTools). 0.3.0 will flip the default to\n * `enforce` after a release of visibility.\n */\n\n/**\n * Default Content-Security-Policy. Conservative-but-not-paralyzing:\n *\n * - default-src 'self' — every fetch falls back to same-origin\n * - script-src 'self' — T6.1 (0.3.0): `'unsafe-inline'`\n * dropped. The SSR pipeline issues a\n * per-request nonce that REPLACES\n * this directive at runtime\n * (`'nonce-<token>'`). For static /\n * non-SSR contexts where no nonce is\n * available, the policy is strict-\n * no-inline — user inline scripts\n * must be migrated to external\n * `<script src=\"...\">` or threaded\n * through `ctx.nonce`.\n * - style-src 'self' 'unsafe-inline' — Tailwind + TheoUI use style attrs\n * in animation directives\n * - img-src 'self' data: blob: — supports inline data URIs, blobs\n * from canvas exports\n * - font-src 'self' data: — Geist fonts inline-base64\n * - connect-src 'self' ws: wss: — WebSocket dev HMR + agent streams\n * - frame-ancestors 'none' — clickjacking defense\n */\n/**\n * T5.1 — default CSP includes the built-in report endpoint so violations\n * are visible without extra config. Apps that ship a custom `csp` string\n * override the report-uri.\n */\nexport const DEFAULT_CSP =\n \"default-src 'self'; \" +\n \"script-src 'self'; \" +\n \"style-src 'self' 'unsafe-inline'; \" +\n \"img-src 'self' data: blob:; \" +\n \"font-src 'self' data:; \" +\n \"connect-src 'self' ws: wss:; \" +\n \"frame-ancestors 'none'; \" +\n 'report-uri /__theo/csp-report'\n\n/**\n * T1.1 — Default Permissions-Policy header (default-deny stance).\n *\n * Disables sensitive Web APIs that the vast majority of apps don't use.\n * Apps that DO need any of these opt in by overriding `permissionsPolicy`\n * in `config.security.headers`.\n *\n * The seven features listed are the most-abused for: tracking\n * (geolocation, accelerometer, gyroscope), spyware (camera, microphone),\n * fraud (payment), and hardware exfiltration (usb).\n *\n * Aligns with Mozilla baseline + OWASP Secure Headers + Lighthouse PWA.\n */\nexport const DEFAULT_PERMISSIONS_POLICY =\n 'geolocation=(), camera=(), microphone=(), payment=(), usb=(), accelerometer=(), gyroscope=()'\n\nexport type CspMode = 'enforce' | 'report-only' | 'off'\n\nexport interface SecurityHeadersConfig {\n /**\n * Custom CSP policy string. When set, replaces the default verbatim.\n * Pass `false` to disable CSP entirely (alias for `cspMode: 'off'`).\n */\n csp?: string | false\n /**\n * Enforcement mode for CSP. Default `report-only` for 0.2.0 (EC-2).\n * 0.3.0 will default to `enforce`.\n */\n cspMode?: CspMode\n /**\n * Strict-Transport-Security value. Defaults to\n * `max-age=31536000; includeSubDomains` in production. Pass `false` to\n * suppress (e.g. internal LANs without TLS).\n */\n hsts?: string | false\n /** X-Frame-Options. Default DENY. */\n frameOptions?: 'DENY' | 'SAMEORIGIN'\n /** X-Content-Type-Options. Default `nosniff`. */\n contentTypeOptions?: 'nosniff'\n /** Referrer-Policy. Default `strict-origin-when-cross-origin`. */\n referrerPolicy?: string\n /**\n * T1.1 — Permissions-Policy directive string. When set, replaces the\n * default verbatim (no merge — see ADR-EC-12). Pass `false` to disable\n * the header entirely. Schema-level refinement rejects any string\n * containing CR/LF (EC-3 — CWE-113 HTTP Response Splitting mitigation).\n */\n permissionsPolicy?: string | false\n}\n\nexport interface SecurityEnv {\n production: boolean\n}\n\n/**\n * T4.1 — Per-request options passed by the SSR pipeline.\n *\n * - `nonce`: when set, the framework substitutes the `'unsafe-inline'`\n * token in `script-src` with `'nonce-<nonce>'`. EC-3 forces\n * `Cache-Control: private, no-store` so a CDN cannot cache the HTML\n * (carrying one nonce) and re-serve it with a freshly-generated CSP\n * header (carrying a different nonce).\n * - `prerender`: when true, the nonce is IGNORED. Prerendered HTML is\n * generated at build time with no nonce in the script tags; mixing a\n * runtime nonce in the header would block every script. EC-4.\n */\nexport interface SecurityHeadersOptions {\n nonce?: string\n prerender?: boolean\n}\n\nconst DEFAULT_HSTS = 'max-age=31536000; includeSubDomains'\n\n/**\n * Apply a nonce to the script-src directive of an existing CSP policy\n * string. Replaces `'unsafe-inline'` (if present) with `'nonce-<value>'`;\n * otherwise appends the nonce directive to the script-src line.\n */\nfunction applyNonceToCsp(policy: string, nonce: string): string {\n return policy\n .split(';')\n .map((directive) => {\n const trimmed = directive.trim()\n if (!trimmed.startsWith('script-src')) return directive\n if (trimmed.includes(\"'unsafe-inline'\")) {\n return directive.replace(\"'unsafe-inline'\", `'nonce-${nonce}'`)\n }\n return `${directive} 'nonce-${nonce}'`\n })\n .join(';')\n}\n\n/**\n * Apply Content-Security-Policy header(s) to `out`. Extracted from\n * `buildSecurityHeaders` to keep that function's complexity within ceiling.\n * Handles the four shapes:\n * csp: false → opt out\n * cspMode: 'off' → opt out\n * cspMode: 'enforce' → Content-Security-Policy (T6.1 default for 0.3.0)\n * cspMode: 'report-only' → Content-Security-Policy-Report-Only (legacy 0.2.x)\n */\nfunction applyCsp(\n out: Record<string, string>,\n config: SecurityHeadersConfig,\n effectiveNonce: string | undefined,\n): void {\n if (config.csp === false || config.cspMode === 'off') return\n let policy = typeof config.csp === 'string' ? config.csp : DEFAULT_CSP\n if (effectiveNonce) {\n policy = applyNonceToCsp(policy, effectiveNonce)\n }\n // T6.1 — default flipped from 'report-only' to 'enforce' for 0.3.0.\n const mode: CspMode = config.cspMode ?? 'enforce'\n if (mode === 'enforce') {\n out['Content-Security-Policy'] = policy\n } else {\n out['Content-Security-Policy-Report-Only'] = policy\n }\n}\n\n/**\n * Build the security headers map for a given config + env. Pure function —\n * returned object can be inspected, logged, or applied to a response.\n */\nexport function buildSecurityHeaders(\n config: SecurityHeadersConfig,\n env: SecurityEnv,\n options: SecurityHeadersOptions = {},\n): Record<string, string> {\n const out: Record<string, string> = {}\n\n // EC-4: prerendered routes must NOT receive a nonce — the build-time\n // HTML carries no nonce, so a runtime nonce would mismatch.\n const effectiveNonce = options.prerender ? undefined : options.nonce\n\n applyCsp(out, config, effectiveNonce)\n\n // X-Frame-Options\n out['X-Frame-Options'] = config.frameOptions ?? 'DENY'\n\n // X-Content-Type-Options\n out['X-Content-Type-Options'] = config.contentTypeOptions ?? 'nosniff'\n\n // Referrer-Policy\n out['Referrer-Policy'] = config.referrerPolicy ?? 'strict-origin-when-cross-origin'\n\n // T1.1 — Permissions-Policy. Default-deny on geo/camera/mic/payment/usb;\n // configurable via `permissionsPolicy`; suppressed via `permissionsPolicy: false`.\n if (config.permissionsPolicy !== false) {\n out['Permissions-Policy'] =\n typeof config.permissionsPolicy === 'string'\n ? config.permissionsPolicy\n : DEFAULT_PERMISSIONS_POLICY\n }\n\n // HSTS — production only, suppressible via hsts: false\n if (env.production && config.hsts !== false) {\n out['Strict-Transport-Security'] = typeof config.hsts === 'string' ? config.hsts : DEFAULT_HSTS\n }\n\n // EC-3: when a nonce is in play, the response carries a one-shot CSP\n // header AND inline scripts in the HTML body. A CDN that caches the\n // HTML and proxies a fresh CSP per request would block every script\n // (nonces wouldn't match). Force no-store to prevent that class of\n // silent prod-only failure. Prerendered routes (which carry no nonce\n // by design — EC-4) are exempt; their HTML is meant to be cacheable.\n if (effectiveNonce) {\n out['Cache-Control'] = 'private, no-store'\n }\n\n return out\n}\n\n/**\n * Apply security headers to a Node ServerResponse. Called by the\n * api-middleware before the route handler runs. The handler can override\n * any header via `res.setHeader()` — last write wins by Node convention.\n */\nexport function applySecurityHeaders(\n res: ServerResponse,\n config: SecurityHeadersConfig,\n env: SecurityEnv,\n options: SecurityHeadersOptions = {},\n): void {\n const headers = buildSecurityHeaders(config, env, options)\n for (const [key, value] of Object.entries(headers)) {\n res.setHeader(key, value)\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/router/scan.ts","../src/router/types.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time file-system scanner. All paths originate from `appDir`,\n * which is itself derived from `process.cwd()`. No HTTP input flows here.\n */\nimport { readdirSync, statSync, existsSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\nimport type { RouteNode, RouteFileName } from './types.js'\nimport { ROUTE_FILE_NAMES, ROUTE_FILE_EXTENSIONS } from './types.js'\n\nfunction toNodeKey(name: RouteFileName): 'page' | 'layout' | 'error' | 'loading' | 'notFound' {\n if (name === 'not-found') return 'notFound'\n return name\n}\n\nfunction setRouteFile(\n node: RouteNode,\n key: 'page' | 'layout' | 'error' | 'loading' | 'notFound',\n value: string,\n): void {\n switch (key) {\n case 'page':\n node.page = value\n break\n case 'layout':\n node.layout = value\n break\n case 'error':\n node.error = value\n break\n case 'loading':\n node.loading = value\n break\n case 'notFound':\n node.notFound = value\n break\n }\n}\n\nfunction attachRouteFiles(node: RouteNode, dir: string): void {\n // Check route files with extension priority (.tsx > .ts > .jsx > .js)\n for (const name of ROUTE_FILE_NAMES) {\n const key = toNodeKey(name)\n if (node[key] !== undefined) continue\n for (const ext of ROUTE_FILE_EXTENSIONS) {\n const filename = `${name}${ext}`\n if (existsSync(join(dir, filename))) {\n setRouteFile(node, key, resolve(dir, filename))\n break\n }\n }\n }\n}\n\nfunction nodeHasContent(node: RouteNode): boolean {\n return (\n node.page !== undefined ||\n node.layout !== undefined ||\n node.error !== undefined ||\n node.loading !== undefined ||\n node.notFound !== undefined ||\n node.children.length > 0\n )\n}\n\nfunction scanDir(dir: string, segment: string, routePath: string): RouteNode {\n const node: RouteNode = { segment, path: routePath, children: [] }\n attachRouteFiles(node, dir)\n\n // Recurse into subdirectories\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue\n if (entry.name.startsWith('_') || entry.name.startsWith('.')) continue\n\n // Route groups: (marketing), (shop) — organize without affecting URL\n const isRouteGroup = entry.name.startsWith('(') && entry.name.endsWith(')')\n const urlSegment = isRouteGroup ? '' : entry.name\n const childPath = urlSegment\n ? (routePath === '/' ? `/${urlSegment}` : `${routePath}/${urlSegment}`)\n : routePath\n const child = scanDir(join(dir, entry.name), isRouteGroup ? '' : entry.name, childPath)\n\n // Prune empty nodes — a directory with no route files AND no\n // children is irrelevant to the manifest.\n if (nodeHasContent(child)) {\n node.children.push(child)\n }\n }\n\n return node\n}\n\nexport function scanRoutes(appDir: string): RouteNode {\n if (!existsSync(appDir)) {\n throw new Error(`App directory does not exist: ${appDir}`)\n }\n if (!statSync(appDir).isDirectory()) {\n throw new Error(`App path is not a directory: ${appDir}`)\n }\n return scanDir(appDir, '', '/')\n}\n","// T2.2 (architecture-cleanup) — RouteNode moved to core/contracts/route-node.ts\n// (canonical home per ADR-0001 v3). Re-export keeps the local path stable.\nexport type { RouteNode } from '../core/contracts/route-node.js'\n\nexport const ROUTE_FILE_NAMES = ['page', 'layout', 'error', 'loading', 'not-found'] as const\n\nexport type RouteFileName = (typeof ROUTE_FILE_NAMES)[number]\n\nexport const ROUTE_FILE_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'] as const\n\nconst ROUTE_FILE_REGEX = /^(page|layout|error|loading|not-found)\\.(tsx|ts|jsx|js)$/\n\nexport function isRouteFile(filename: string): boolean {\n return ROUTE_FILE_REGEX.test(filename)\n}\n"],"mappings":";;;;AAIA,SAAS,aAAa,UAAU,kBAAkB;AAClD,SAAS,MAAM,eAAe;;;ACDvB,IAAM,mBAAmB,CAAC,QAAQ,UAAU,SAAS,WAAW,WAAW;AAI3E,IAAM,wBAAwB,CAAC,QAAQ,OAAO,QAAQ,KAAK;AAElE,IAAM,mBAAmB;AAElB,SAAS,YAAY,UAA2B;AACrD,SAAO,iBAAiB,KAAK,QAAQ;AACvC;;;ADJA,SAAS,UAAU,MAA2E;AAC5F,MAAI,SAAS,YAAa,QAAO;AACjC,SAAO;AACT;AAEA,SAAS,aACP,MACA,KACA,OACM;AACN,UAAQ,KAAK;AAAA,IACX,KAAK;AACH,WAAK,OAAO;AACZ;AAAA,IACF,KAAK;AACH,WAAK,SAAS;AACd;AAAA,IACF,KAAK;AACH,WAAK,QAAQ;AACb;AAAA,IACF,KAAK;AACH,WAAK,UAAU;AACf;AAAA,IACF,KAAK;AACH,WAAK,WAAW;AAChB;AAAA,EACJ;AACF;AAEA,SAAS,iBAAiB,MAAiB,KAAmB;AAE5D,aAAW,QAAQ,kBAAkB;AACnC,UAAM,MAAM,UAAU,IAAI;AAC1B,QAAI,KAAK,GAAG,MAAM,OAAW;AAC7B,eAAW,OAAO,uBAAuB;AACvC,YAAM,WAAW,GAAG,IAAI,GAAG,GAAG;AAC9B,UAAI,WAAW,KAAK,KAAK,QAAQ,CAAC,GAAG;AACnC,qBAAa,MAAM,KAAK,QAAQ,KAAK,QAAQ,CAAC;AAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,eAAe,MAA0B;AAChD,SACE,KAAK,SAAS,UACd,KAAK,WAAW,UAChB,KAAK,UAAU,UACf,KAAK,YAAY,UACjB,KAAK,aAAa,UAClB,KAAK,SAAS,SAAS;AAE3B;AAEA,SAAS,QAAQ,KAAa,SAAiB,WAA8B;AAC3E,QAAM,OAAkB,EAAE,SAAS,MAAM,WAAW,UAAU,CAAC,EAAE;AACjE,mBAAiB,MAAM,GAAG;AAG1B,aAAW,SAAS,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAC7D,QAAI,CAAC,MAAM,YAAY,EAAG;AAC1B,QAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,KAAK,WAAW,GAAG,EAAG;AAG9D,UAAM,eAAe,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,KAAK,SAAS,GAAG;AAC1E,UAAM,aAAa,eAAe,KAAK,MAAM;AAC7C,UAAM,YAAY,aACb,cAAc,MAAM,IAAI,UAAU,KAAK,GAAG,SAAS,IAAI,UAAU,KAClE;AACJ,UAAM,QAAQ,QAAQ,KAAK,KAAK,MAAM,IAAI,GAAG,eAAe,KAAK,MAAM,MAAM,SAAS;AAItF,QAAI,eAAe,KAAK,GAAG;AACzB,WAAK,SAAS,KAAK,KAAK;AAAA,IAC1B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,WAAW,QAA2B;AACpD,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,iCAAiC,MAAM,EAAE;AAAA,EAC3D;AACA,MAAI,CAAC,SAAS,MAAM,EAAE,YAAY,GAAG;AACnC,UAAM,IAAI,MAAM,gCAAgC,MAAM,EAAE;AAAA,EAC1D;AACA,SAAO,QAAQ,QAAQ,IAAI,GAAG;AAChC;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/generators/openapi-client-gen.ts","../src/services/schema.ts","../src/services/adapters-bridge/vite-proxy-builder.ts"],"sourcesContent":["/**\n * OpenAPI → TypeScript client generation (T5.1).\n *\n * Thin wrapper around `@hey-api/openapi-ts` (the de facto 2026 generator\n * used by Vercel/OpenCode/PayPal). Called by the Vite plugin\n * `services-typed-client.ts` at dev startup and when an OpenAPI URL\n * changes.\n *\n * Production behavior:\n * - For each service with a `openapi` URL, fetch the spec\n * - Run Hey API generator to write `<cwd>/clients/<service-name>.ts`\n * - On any failure: log warning, do NOT crash dev (best-effort)\n *\n * The actual `@hey-api/openapi-ts` invocation is dynamic-imported so\n * that consumers without the dep (TS-only Wave 1 apps) don't pay\n * the bundle cost.\n */\nimport type { ManifestServiceEntry } from '../adapters-bridge/manifest.js'\n\nexport interface GenerateClientOptions {\n service: ManifestServiceEntry\n /** Directory to write `<service-name>.ts` into. */\n outputDir: string\n /** Logger for warnings/info. */\n log?: (level: 'info' | 'warn' | 'error', msg: string) => void\n /** Test injection — replace fetch (so tests don't hit the network). */\n customFetch?: typeof fetch\n}\n\nexport interface GenerateClientResult {\n generated: boolean\n outputFile?: string\n skippedReason?: string\n}\n\n/**\n * Generate a typed client for one service. No-op (returns skipped=true)\n * if the service has no `openapi` URL.\n *\n * The generator runs the Hey API tool. If the tool import fails (not\n * installed), the function logs a warning and returns `{ generated: false }`.\n */\nexport async function generateTypedClient(\n options: GenerateClientOptions,\n): Promise<GenerateClientResult> {\n const log =\n options.log ??\n ((_level: 'info' | 'warn' | 'error', _msg: string) => {\n // default: silent in production; tests inject their own\n })\n const { service, outputDir } = options\n\n if (!service.openapi) {\n return { generated: false, skippedReason: 'no openapi URL declared' }\n }\n\n const f = options.customFetch ?? fetch\n let spec: unknown\n try {\n const res = await f(service.openapi)\n if (!res.ok) {\n log('warn', `[${service.name}] openapi fetch returned ${String(res.status)}; skipping`)\n return { generated: false, skippedReason: `fetch returned ${String(res.status)}` }\n }\n spec = await res.json()\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] openapi fetch failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'fetch failed' }\n }\n\n // Dynamic import — soft dep. We pass through a variable so the typechecker\n // doesn't resolve the literal at build time (the dep is optional;\n // consumers without it get a graceful skip).\n let createClient: unknown\n try {\n // Soft-dep resolution. We pass the module name through a String wrapper\n // call site that the typechecker cannot statically resolve — so missing\n // dep at typecheck time does not break the build. Runtime behavior is\n // identical to `import('@hey-api/openapi-ts')`.\n const segment1 = '@hey-api/'\n const segment2 = 'openapi-ts'\n const moduleName = `${segment1}${segment2}`\n const dyn: Promise<unknown> = import(moduleName).catch(() => null)\n const mod = (await dyn) as { createClient?: unknown } | null\n createClient = mod?.createClient\n } catch {\n createClient = undefined\n }\n\n if (typeof createClient !== 'function') {\n log(\n 'warn',\n `[${service.name}] @hey-api/openapi-ts not installed; typed client not generated. ` +\n `Run \\`pnpm add -D @hey-api/openapi-ts @hey-api/client-fetch\\` to enable.`,\n )\n return { generated: false, skippedReason: 'hey-api not installed' }\n }\n\n const outputFile = `${outputDir}/${service.name}.ts`\n try {\n await (createClient as (cfg: unknown) => Promise<unknown>)({\n input: spec,\n output: { path: outputDir, format: 'prettier' },\n plugins: [{ name: '@hey-api/client-fetch' }],\n })\n log('info', `[${service.name}] typed client generated at ${outputFile}`)\n return { generated: true, outputFile }\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] Hey API generation failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'generator threw' }\n }\n}\n","/**\n * Wave 2 — Polyglot Services Orchestration (T1.1).\n *\n * Declarative `services: {}` primitive for `theo.config.ts`. Each entry\n * declares an external sidecar process (Python FastAPI / Node Hono) that\n * boots alongside the TheoKit TS app. Empty `services: {}` is the default\n * and preserves Wave 1 behavior (no impact on TS-only apps).\n *\n * See ADRs 0012-0015 + plan: docs/plans/wave-2-polyglot-services-plan.md\n */\nimport { z } from 'zod'\n\n/** Wave 2 runtime kinds. Go/Rust/Java/Ruby/PHP archived; deferred per ADR-0012 invariant #2/#3. */\nconst ServiceRuntimeSchema = z.enum(['python', 'node'])\n\n/**\n * Plan v1.2 T2.2 — service shape enum, mirrored from `services.schema.json`\n * server | worker | frontend. Optional; emitters default to \"server\".\n */\nconst ServiceTypeSchema = z.enum(['server', 'worker', 'frontend'])\n\n/** EC-3 fix: names that conflict with generated docker-compose entries. */\nconst RESERVED_SERVICE_NAMES = ['web', 'caddy', 'postgres', 'redis'] as const\n\n/**\n * EC-12 fix: service names must be docker-compose-safe — lowercase,\n * start with a letter, contain only a-z 0-9 -. Reserved names rejected.\n */\nconst ServiceNameSchema = z\n .string()\n .min(1)\n .regex(\n /^[a-z][a-z0-9-]*$/,\n 'service name must be lowercase, start with letter, contain only a-z 0-9 -',\n )\n .refine((n) => !(RESERVED_SERVICE_NAMES as readonly string[]).includes(n), {\n message: `service name conflicts with reserved name (${RESERVED_SERVICE_NAMES.join('/')})`,\n })\n\n/**\n * Single service definition. All commands run from `services/<name>/` cwd.\n *\n * EC-4 fix: `proxy` regex requires NON-EMPTY path after `/` (the `+` quantifier)\n * to reject `/` which would catch-all and conflict with TheoKit's own routing.\n */\nconst ServiceDefinitionSchema = z.object({\n runtime: ServiceRuntimeSchema,\n type: ServiceTypeSchema.optional(),\n port: z.number().int().min(1).max(65535),\n proxy: z.string().regex(/^\\/[a-zA-Z0-9\\-_/]+$/, 'proxy must be a non-root path starting with /'),\n dev: z.string().min(1),\n build: z.string().optional(),\n start: z.string().min(1),\n openapi: z.string().url().optional(),\n healthcheck: z.string().regex(/^\\//, 'healthcheck must start with /').default('/health'),\n cors: z.boolean().default(false),\n env: z.record(z.string(), z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n /**\n * EC-25 + ref doc §8: by default the proxy strips upstream Set-Cookie\n * to prevent the polyglot service from issuing cookies that conflict\n * with TheoKit's encrypted session. Set to true to opt-in.\n */\n passSetCookie: z.boolean().default(false),\n})\n\nexport type ServiceDefinition = z.infer<typeof ServiceDefinitionSchema>\nexport type ServicesConfig = Record<string, ServiceDefinition>\n\n/**\n * Topological-order helper used by `dependsOn` cycle detection refine.\n * Returns true iff `graph` is a DAG. Each node's deps must all reference\n * existing nodes; cycles return false.\n */\nfunction isDag(graph: Record<string, readonly string[]>): boolean {\n const WHITE = 0\n const GRAY = 1\n const BLACK = 2\n const colors: Record<string, 0 | 1 | 2> = {}\n for (const node of Object.keys(graph)) colors[node] = WHITE\n\n function visit(node: string): boolean {\n if (colors[node] === GRAY) return false // cycle\n if (colors[node] === BLACK) return true\n colors[node] = GRAY\n for (const dep of graph[node] ?? []) {\n if (!(dep in graph)) return false // missing reference\n if (!visit(dep)) return false\n }\n colors[node] = BLACK\n return true\n }\n\n for (const node of Object.keys(graph)) {\n if (colors[node] === WHITE && !visit(node)) return false\n }\n return true\n}\n\n/**\n * Full services config schema with cross-service refines.\n *\n * EC-1 fix: detect duplicate ports across services.\n * EC-13: empty `dependsOn: []` accepted as no-deps.\n * Self-dep, missing-ref, and cycles rejected via topological check.\n */\nexport const servicesConfigSchema = z\n .record(ServiceNameSchema, ServiceDefinitionSchema)\n .default({})\n // EC-1: duplicate port detection across services\n .refine(\n (s) => {\n const ports = Object.values(s).map((v) => v.port)\n return new Set(ports).size === ports.length\n },\n {\n message: 'duplicate port across services — each service must bind a unique port',\n },\n )\n // duplicate proxy prefix detection\n .refine(\n (s) => {\n const prefixes = Object.values(s).map((v) => v.proxy)\n return new Set(prefixes).size === prefixes.length\n },\n { message: 'duplicate proxy prefix across services' },\n )\n // dependsOn correctness (self-dep, missing reference, cycle)\n .refine(\n (s) => {\n // Build graph\n const graph: Record<string, readonly string[]> = {}\n for (const [name, def] of Object.entries(s)) {\n graph[name] = def.dependsOn ?? []\n }\n // Self-dep: a service mentioning itself is rejected as part of cycle detection\n for (const [name, deps] of Object.entries(graph)) {\n if (deps.includes(name)) return false\n }\n return isDag(graph)\n },\n {\n message: 'invalid dependsOn — must reference existing services, no self-deps, no cycles',\n },\n )\n\nexport type ServicesConfigInput = z.input<typeof servicesConfigSchema>\nexport type ServicesConfigOutput = z.output<typeof servicesConfigSchema>\n","/**\n * Builds a Vite `server.proxy` configuration from TheoKit's\n * declarative `services: {}` config (T2.1).\n *\n * Pure function — no Vite imports, just data shaping. The Vite plugin\n * (services-dev.ts) calls this and merges the result into `vite.config.server.proxy`.\n *\n * Pattern follows Vite proxy reference (referencias/vite/packages/vite/src/node/server/middlewares/proxy.ts):\n * - Each path prefix becomes its own entry\n * - `changeOrigin: true` set by default (matches Vite's string-shortcut behavior)\n * - User-set entries take precedence on prefix collision (we never clobber)\n */\nimport type { ServicesConfig } from '../schema.js'\n\n/**\n * Shape compatible with Vite's `server.proxy` type. We intentionally don't\n * import Vite's `ProxyOptions` so this module stays platform-neutral and\n * unit-testable without Vite.\n */\nexport interface ViteProxyEntry {\n target: string\n changeOrigin: boolean\n rewrite?: (path: string) => string\n}\n\nexport type ViteProxyConfig = Record<string, string | ViteProxyEntry>\n\nexport function buildServicesProxyConfig(\n services: ServicesConfig,\n userProxy: ViteProxyConfig = {},\n): ViteProxyConfig {\n const fromServices: ViteProxyConfig = {}\n for (const [, def] of Object.entries(services)) {\n const prefix = def.proxy\n fromServices[prefix] = {\n target: `http://localhost:${String(def.port)}`,\n changeOrigin: true,\n // Strip the service's `proxy` prefix from the upstream URL so the\n // sidecar receives its own native paths (e.g. `/api/agent/echo` →\n // `/echo`). The sidecar declares routes against its own root path —\n // it doesn't know about the TheoKit proxy prefix.\n rewrite: (path: string) =>\n path.startsWith(prefix) ? path.slice(prefix.length) || '/' : path,\n }\n }\n // User proxy wins on collision (we add services first, then spread user on top)\n return { ...fromServices, ...userProxy }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/schema.ts","../src/config/schemas/header-safe.ts","../src/config/schemas/rate-limit.ts","../src/config/schemas/upload.ts","../src/config/schemas/logging.ts","../src/config/schemas/cache.ts","../src/config/schemas/security.ts","../src/config/schemas/storage.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport { servicesConfigSchema } from '../services/index.js'\n\nimport {\n cacheSchema,\n loggingSchema,\n rateLimitSchema,\n securitySchema,\n storageSchema,\n uploadSchema,\n type FormatErrorContext,\n type FormatErrorHook,\n} from './schemas/index.js'\n\n// Re-export per-concern schemas + types so downstream consumers\n// (`adapters/*`, vite-plugin, generators, tests) keep their existing\n// imports valid. Per plan T2.3 — pure structural split; zero behavior\n// change at the call site.\nexport {\n cacheSchema,\n corsSchema,\n disallowedConfigSchema,\n headerSafeString,\n loggingSchema,\n rateLimitSchema,\n securityHeadersSchema,\n securitySchema,\n storageSchema,\n uploadSchema,\n} from './schemas/index.js'\n\nexport type { FormatErrorContext, FormatErrorHook, StorageConfig } from './schemas/index.js'\n\n/**\n * Root `theo.config.ts` schema — composer assembled from the per-concern\n * primitives re-exported above.\n *\n * Embedded blocks that exist ONLY as part of the root config (`agents`,\n * `ui`, `devtools`, `jobs`, `openapi`) stay inline below. They have no\n * external consumers; splitting them would create lonely files (M5\n * smell) without comprehension benefit.\n */\nexport const theoConfigSchema = z\n .object({\n /**\n * Plan v1.2 T2.1 — project identifier propagated into\n * `.theokit/services.json` v2 `project` field. DNS-1123 compatible (drives\n * Gitea repo + ArgoCD App naming on TheoCloud). When omitted, the build\n * emits services.json v1 with the legacy `services-bundle` fallback\n * (ADR D10) and logs a deprecation warning.\n */\n name: z\n .string()\n .max(63)\n .refine(\n // DNS-1123 equivalent expressed as explicit single-char anchors so\n // security/detect-unsafe-regex stays clean (no backtracking).\n (value) =>\n value.length > 0 &&\n /^[a-z0-9-]+$/u.test(value) &&\n !value.startsWith('-') &&\n !value.endsWith('-'),\n 'name must match DNS-1123 (lowercase alphanumeric+hyphens, 1-63 chars, no leading/trailing hyphen)',\n )\n .optional(),\n appDir: z.string().default('app'),\n serverDir: z.string().default('server'),\n /**\n * T2.2 / EC-4 — Build output directory. Must be a relative path inside\n * the project root. Refused absolute or parent-relative paths to prevent\n * `cleanOutDir` from wiping arbitrary locations (defense-in-depth on\n * top of cleanOutDir's runtime EC-3 guard).\n */\n distDir: z\n .string()\n .default('.theokit')\n .refine(\n (d) => !/^([A-Za-z]:)?[/\\\\]/.test(d) && !d.startsWith('..'),\n 'distDir must be a relative path inside the project root (e.g., \".theokit\")',\n ),\n /**\n * Agent runtime configuration.\n *\n * - `maxRegistries` (T2.3, legacy): dev-mode cleanup of stale\n * `.theokit/agents/<id>/` dirs by mtime. Now superseded by SDK's\n * native registry GC but kept for backward-compat.\n * - `registry` (Phase 6, Production-Readiness #2): forwarded to\n * `Agent.registry.configure()` lazily on first request.\n * * `maxAgents` — LRU cap. MUST be ≥ max-concurrent-active-conversations\n * (EC-17 DOCUMENT): with maxAgents:1 and 2 concurrent chats, LRU evicts\n * mid-stream → SDK aborts with code:'aborted'. Default 100 covers\n * indie/small-team; tune up for high-traffic.\n * * `idleTimeoutMs` — auto-evict idle agents (default 30 min). 0 disables\n * idle eviction (LRU only).\n */\n agents: z\n .object({\n maxRegistries: z.number().int().positive().default(100),\n registry: z\n .object({\n /** LRU cap — MUST be ≥ max-concurrent-active-conversations. */\n maxAgents: z.number().int().positive().max(10_000).default(100),\n /** Idle eviction window in ms. 0 disables idle eviction (LRU only). */\n idleTimeoutMs: z\n .number()\n .int()\n .nonnegative()\n .default(30 * 60_000),\n })\n .optional(),\n })\n .optional(),\n port: z.number().int().min(1).max(65535).default(3000),\n ssr: z.boolean().default(false),\n /** When true (and ssr === true), use renderToPipeableStream with progressive\n * shell flush instead of single-shot renderToString. Opt-in for streaming\n * SSR; default false preserves the current behavior. */\n ssrStreaming: z.boolean().default(false),\n rateLimit: rateLimitSchema.optional(),\n upload: uploadSchema.optional(),\n logging: loggingSchema.optional(),\n security: securitySchema.optional(),\n serialization: z.enum(['json', 'superjson']).default('json'),\n // Plugins are validated structurally at runtime by createPluginRunnerFromConfig.\n // Zod only checks the shape minimally (must be array). Type-level safety is\n // provided through defineConfig at the user surface.\n plugins: z.array(z.unknown()).optional(),\n /** Enable client-side batching of theoFetch calls and the\n * /api/__theo_batch__ server endpoint. */\n batching: z\n .union([z.boolean(), z.object({ max: z.number().int().positive().optional() })])\n .optional(),\n /** T4.1 — Audit log. When `logger` is provided, framework events\n * (csrf.warn, rate-limit.exceeded, session.rotated, csp.violation) are\n * emitted to it. Default: noop. */\n audit: z\n .object({\n logger: z.unknown().optional(),\n })\n .optional(),\n /** TheoUI auto-wire (T2.1). `false` = opt-out; object = explicit theme/fonts;\n * undefined = enabled when @theokit/ui is detected in node_modules. */\n ui: z\n .union([\n z.literal(false),\n z.object({\n theme: z.enum(['violet-forge', 'noir', 'paper']).optional(),\n fonts: z.enum(['bundled', 'cdn']).optional(),\n }),\n ])\n .optional(),\n /**\n * Cache subsystem (caching-and-revalidation-plan).\n * Default `undefined` keeps caching disabled (backward compatible).\n * Pass `cache: {}` to opt in with defaults.\n */\n cache: cacheSchema.optional(),\n /**\n * Devtools overlay (Phase 0.4.0+ — see docs/plans/devtools-plan.md).\n *\n * - `undefined` (default): devtools auto-injects in `pnpm dev`, NEVER in `vite build`.\n * - `false`: devtools disabled entirely (Vite plugin skips injection even in dev).\n * - `{ ... }`: devtools enabled with explicit defaults (position, theme).\n *\n * Tree-shaken to noop in prod via the dual-export pattern in\n * `packages/theo/src/devtools/index.ts` (EC-17 positive prod check).\n */\n devtools: z\n .union([\n z.literal(false),\n z.object({\n position: z.enum(['top-left', 'top-right', 'bottom-left', 'bottom-right']).optional(),\n theme: z.enum(['light', 'dark', 'system']).optional(),\n }),\n ])\n .optional(),\n /**\n * Informative list of deploy adapters the app supports. Does NOT\n * trigger per-build translations — only the `--target` CLI flag does\n * (EC-201 / ADR D2). If `config.adapters` includes a target NOT\n * matching `--target`, `theokit build` emits a cross-reference note.\n */\n adapters: z.array(z.string()).optional(),\n /**\n * Extra package names to pre-bundle via Vite's `optimizeDeps.include`.\n *\n * Framework auto-includes `@theokit/ui` and `lucide-react` when present.\n * Apps adding plugin peer-deps that rely on a runtime `import('<pkg>')`\n * (dynamic specifier) — e.g. `@theokit/plugin-canvas` consumers\n * installing `mermaid` for Mermaid SVG rendering — must declare those\n * here so Vite can resolve the literal in dev mode without\n * \"Failed to resolve module specifier\" errors. Production builds use\n * tsup/esbuild bundling and are not affected.\n *\n * Example:\n * viteOptimizeDeps: ['mermaid']\n */\n viteOptimizeDeps: z.array(z.string()).optional(),\n /**\n * Jobs backend (ADR D3 + T2.1). When configured, every request gets\n * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a\n * `JobBackend` instance (InMemoryJobBackend, PostgresJobBackend, etc.).\n */\n jobs: z\n .object({\n backend: z.custom<unknown>((v) => typeof v === 'object' && v !== null),\n })\n .optional(),\n /**\n * StorageManager configuration (T1.1 / ADR-0007).\n *\n * Declares Postgres servers + databases + Redis servers. Consumed by\n * `getStorageManager().configure()` at boot via `start.ts`.\n *\n * Default `undefined` means manager exists but is not configured — adapters\n * relying on the manager (PostgresJobBackend.fromStorageManager, etc.) will\n * throw with an actionable error on first use.\n */\n storage: storageSchema.optional(),\n /**\n * Wave 2 — Polyglot services orchestration (T1.1 / ADR-0012/0013/0014/0015).\n *\n * Declarative external sidecar processes (Python FastAPI / Node Hono)\n * that boot alongside the TheoKit TS app. Empty `services: {}` is the\n * default and preserves Wave 1 behavior.\n */\n services: servicesConfigSchema,\n /**\n * G2 — OpenAPI emit (build-time only).\n *\n * When present, `theokit build` writes a generated `openapi.json` from\n * every `defineRoute()` body/query/params Zod schema. `undefined` keeps\n * the framework backward-compatible (no emit). Pass `openapi: {}` to\n * opt in with defaults.\n *\n * Defaults: spec 3.1.0 · servers `http://localhost:3000` · title\n * \"TheoKit App\" · version \"0.0.0\" · outDir \".theokit\" (next to dist).\n *\n * The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides\n * `servers[].url` at emit time without rebuilding the config.\n */\n openapi: z\n .object({\n servers: z\n .array(\n z.object({\n url: z.url(),\n description: z.string().optional(),\n }),\n )\n .default([{ url: 'http://localhost:3000', description: 'Local development' }]),\n specVersion: z.enum(['3.1.0', '3.0.3']).default('3.1.0'),\n title: z.string().default('TheoKit App'),\n version: z.string().default('0.0.0'),\n outDir: z.string().default('.theokit'),\n })\n .optional(),\n /**\n * G5 T1.3 — error envelope transformer hook (blueprint ADR D3).\n *\n * When present, runs at framework error-boundary time to enrich the\n * envelope with consumer-defined extensions (`hint`, custom telemetry\n * tags, etc.) BEFORE the envelope is serialized to the client. The\n * function signature is preserved via the explicit `FormatErrorHook`\n * type — TS inference flows from `theo.config.ts` into `@theo/client`\n * codegen and `@theokit/ui` AgentErrorCard consumers.\n *\n * Use `z.custom<FormatErrorHook>(...)` because Zod's built-in\n * `z.function()` discards its TS signature after parse; `z.custom`\n * with a function-typed runtime guard preserves the call-site type\n * without trading off Zod runtime validation.\n */\n formatError: z\n .custom<FormatErrorHook>((val) => typeof val === 'function', {\n message: 'formatError must be a function',\n })\n .optional(),\n })\n // EC-2 fix: cross-config refine — no service may share TheoKit's web port.\n .refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {\n message: 'service.port collides with TheoKit web port — change one of them',\n path: ['services'],\n })\n\nexport type TheoConfig = z.infer<typeof theoConfigSchema>\n\nexport type OpenApiConfig = NonNullable<TheoConfig['openapi']>\n\n// Silence unused-import warnings for type-only re-exports — TS strips at compile,\n// but exports above re-introduce them for downstream consumers.\nexport type _FormatErrorContext = FormatErrorContext\n","import { z } from 'zod'\n\n/**\n * EC-3 — CWE-113 HTTP Response Splitting mitigation.\n *\n * Every string that becomes a header value must reject CR/LF. Apps that\n * derive header values from untrusted input (feature flags, tenant config,\n * URL params) would otherwise let attackers inject Set-Cookie / Location\n * headers via `\\r\\n`. Apply this refinement to every header-bound string\n * field: permissionsPolicy, csp, hsts, referrerPolicy, CORS allow/expose.\n */\nexport const headerSafeString = z\n .string()\n .refine((s) => !/[\\r\\n]/.test(s), { message: 'Header value must not contain CR/LF' })\n","import { z } from 'zod'\n\n/**\n * Base bucket config — legacy shape preserved for backwards compatibility.\n */\nconst baseRateLimitSchema = z.object({\n windowMs: z.number().min(1),\n max: z.number().int().min(1),\n})\n\n/**\n * T2.2 — Per-route + per-user rate limit. The new shape adds `routes`\n * (path map), `keyBy`, and `cookieName`. The legacy flat shape is still\n * accepted via the union — see `createRouteRateLimiter` for normalization.\n */\nexport const rateLimitSchema = z.union([\n baseRateLimitSchema,\n z.object({\n default: baseRateLimitSchema.optional(),\n routes: z.record(z.string(), baseRateLimitSchema).optional(),\n keyBy: z\n .union([\n z.enum(['ip', 'session', 'user']),\n z.function({ input: z.tuple([z.unknown()]), output: z.string() }),\n ])\n .optional(),\n cookieName: z.string().min(1).optional(),\n }),\n])\n","import { z } from 'zod'\n\nexport const uploadSchema = z.object({\n maxFileSize: z\n .number()\n .min(1)\n .default(10 * 1024 * 1024), // 10MB\n maxFiles: z.number().int().min(1).default(10),\n maxFieldSize: z\n .number()\n .min(1)\n .default(1 * 1024 * 1024), // 1MB\n})\n","import { z } from 'zod'\n\nexport const loggingSchema = z.object({\n level: z.enum(['debug', 'info', 'warn', 'error', 'silent']).default('info'),\n})\n","import { z } from 'zod'\n\n/**\n * Cache subsystem config (caching-and-revalidation-plan).\n * Default `cache: undefined` keeps the framework backward-compatible\n * (no engine initialized → no cache primitives available).\n *\n * Setting `cache: {}` opts in with all defaults.\n */\nconst routeRuleSchema = z.object({\n maxAge: z.number().nonnegative().finite().optional(),\n swr: z.number().nonnegative().finite().optional(),\n tags: z.array(z.string()).optional(),\n})\n\nexport const cacheSchema = z.object({\n enabled: z.boolean().default(true),\n /** 'memory' uses InMemoryCacheAdapter; otherwise pass a custom adapter instance. */\n storage: z.union([z.literal('memory'), z.custom<unknown>()]).default('memory'),\n maxEntries: z.number().int().positive().default(1000),\n defaults: z\n .object({\n maxAge: z.number().nonnegative().finite().default(1),\n swr: z.number().nonnegative().finite().optional(),\n cacheErrors: z.boolean().default(false),\n })\n .default({ maxAge: 1, cacheErrors: false }),\n keyDerivation: z\n .object({\n excludeQuery: z.array(z.string()).optional(),\n sortQuery: z.boolean().default(true),\n lowercaseHost: z.boolean().default(true),\n })\n .default({ sortQuery: true, lowercaseHost: true }),\n routeRules: z.record(z.string(), routeRuleSchema).optional(),\n})\n","import { z } from 'zod'\n\nimport { headerSafeString } from './header-safe.js'\n\n/**\n * Phase 5 — CSRF warn-first (EC-1).\n *\n * 0.2.0 default: `warn`. Existing apps keep working but get structured\n * warnings about every state-mutating request that does not carry the\n * `X-Theo-Action: 1` header. 0.3.0 will flip the default to `strict`.\n *\n * Set explicitly to `strict` to opt into the future default early,\n * or `off` to disable CSRF entirely (only valid when you have another\n * defense — bearer auth, no session cookies, etc).\n */\n\n/**\n * Phase 6 — Default security headers (D4 / EC-2).\n *\n * 0.2.0 defaults:\n * - CSP in `report-only` mode (EC-2: don't break existing apps)\n * - X-Frame-Options: DENY · X-Content-Type-Options: nosniff\n * - Referrer-Policy: strict-origin-when-cross-origin\n * - HSTS in production only (no TLS on localhost)\n *\n * Users override individual headers, swap CSP to `enforce`, or disable\n * CSP entirely (`csp: false` / `cspMode: 'off'`).\n */\nexport const securityHeadersSchema = z.object({\n csp: z.union([headerSafeString, z.literal(false)]).optional(),\n // T6.1 — default flipped from 'report-only' to 'enforce' for 0.3.0.\n // Users who want the old behaviour set `cspMode: 'report-only'`\n // explicitly. See docs/migrating/0.2-to-0.3.md.\n cspMode: z.enum(['enforce', 'report-only', 'off']).default('enforce'),\n hsts: z.union([headerSafeString, z.literal(false)]).optional(),\n frameOptions: z.enum(['DENY', 'SAMEORIGIN']).default('DENY'),\n contentTypeOptions: z.literal('nosniff').default('nosniff'),\n referrerPolicy: headerSafeString.default('strict-origin-when-cross-origin'),\n /**\n * T1.1 — Permissions-Policy directive string. EC-3-refined: rejects CR/LF.\n * Pass `false` to suppress the header.\n */\n permissionsPolicy: z.union([headerSafeString, z.literal(false)]).optional(),\n})\n\n/**\n * T5.1 — Disallowed-routes escalation pattern (Rails-inspired).\n *\n * `routes` accepts string (exact match — trailing slash matters) or\n * RegExp entries. Matched routes that would otherwise emit `csrf.warn`\n * dispatch through `disallowedBehavior` instead:\n * - `'warn'` : no-op vs the default warn-mode behavior\n * - `'raise'` : escalate to 403, even when global `csrf` mode is 'warn'\n *\n * Use to roll out strict mode per-route (e.g., flip /api/auth/* first)\n * without committing the entire surface to strict at once.\n */\nexport const disallowedConfigSchema = z.object({\n routes: z.array(z.union([z.string(), z.instanceof(RegExp)])),\n behavior: z.enum(['warn', 'raise']).default('raise'),\n})\n\n/**\n * T1.2 — CORS configuration.\n *\n * `origins` accepts a single value (`'*'`, string, RegExp, callback) OR an\n * array of (string | RegExp). The spec-violating `origins: '*'` +\n * `credentials: true` combination is rejected at parse time (browsers\n * ignore wildcards when credentials are sent).\n *\n * EC-3 — `allowedHeaders` and `exposedHeaders` entries go through the\n * header-safe refinement (CR/LF rejected — CWE-113 mitigation).\n */\nexport const corsSchema = z\n .object({\n origins: z.union([\n z.literal('*'),\n headerSafeString,\n z.instanceof(RegExp),\n z.array(z.union([headerSafeString, z.instanceof(RegExp)])),\n z.function({ input: z.tuple([z.string()]), output: z.boolean() }),\n ]),\n methods: z\n .array(z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']))\n .optional(),\n allowedHeaders: z.array(headerSafeString).optional(),\n exposedHeaders: z.array(headerSafeString).optional(),\n credentials: z.boolean().default(false),\n maxAge: z.number().int().min(0).max(86400).default(600),\n })\n .refine((c) => !(c.origins === '*' && c.credentials), {\n message: 'CORS spec forbids origins:\"*\" with credentials:true — browsers ignore the wildcard',\n })\n\nexport const securitySchema = z.object({\n // T6.1 — default flipped from 'warn' to 'strict' for 0.3.0. Apps that\n // grep their warn-mode logs from 0.2.x already know which endpoints\n // break; opt back into 'warn' globally OR use disallowedRoutes for\n // surgical migration. See docs/migrating/0.2-to-0.3.md.\n csrf: z.enum(['off', 'warn', 'strict']).default('strict'),\n headers: securityHeadersSchema.optional(),\n /** T5.1 — per-route escalation (Rails disallowed_warnings pattern). */\n disallowed: disallowedConfigSchema.optional(),\n /** T1.2 — Cross-Origin Resource Sharing. Single global config; runs first in pipeline. */\n cors: corsSchema.optional(),\n})\n","import { z } from 'zod'\n\n/**\n * StorageManager schemas (T1.1 / ADR-0007 D4).\n *\n * `theo.config.ts > storage` declares Postgres servers (credentials), the\n * databases on each server (TheoCloud / managed PG / self-host), and Redis\n * servers. The runtime `StorageManager` consumes this block; adapters\n * (PostgresJobBackend, PostgresConversationStorage, etc.) request pools by\n * database name and never see raw credentials at the call site.\n *\n * EC-1 (DOCUMENT in concept doc T4.1): Zod default strip-unknown mode\n * silently drops typo keys (`databasees` instead of `databases`). The\n * concept doc warns users to use exact names; runtime errors are still\n * actionable (`Database \"X\" not configured`).\n *\n * EC-2 (DEFERRED to use-time): `databases.X.server` references a key in\n * `servers`. Cross-field validation is intentionally NOT enforced at parse\n * time — `StorageManager.usePostgres()` throws an actionable error on the\n * first call with the dangling reference, matching Rails / Nitro behavior.\n */\nconst tlsConfigSchema = z.object({\n rejectUnauthorized: z.boolean().optional(),\n caCert: z.string().optional(),\n clientCert: z.string().optional(),\n clientKey: z.string().optional(),\n})\n\nconst serverConfigSchema = z.object({\n host: z.string().min(1),\n port: z.number().int().positive().max(65535).optional(),\n user: z.string().min(1),\n /** Password may be the empty string (some providers permit it). */\n password: z.string(),\n tls: tlsConfigSchema.optional(),\n})\n\nconst postgresPoolConfigSchema = z.object({\n min: z.number().int().nonnegative().optional(),\n max: z.number().int().positive().optional(),\n connectionTimeoutMillis: z.number().int().positive().optional(),\n idleTimeoutMillis: z.number().int().nonnegative().optional(),\n})\n\nconst postgresDatabaseConfigSchema = z.object({\n /** References a key in `storage.servers`. Resolved at `usePostgres()` call time. */\n server: z.string().min(1),\n database: z.string().min(1),\n pool: postgresPoolConfigSchema.optional(),\n})\n\nconst redisServerConfigSchema = serverConfigSchema.extend({\n db: z.number().int().nonnegative().optional(),\n maxRetriesPerRequest: z.number().int().nonnegative().optional(),\n})\n\nexport const storageSchema = z.object({\n servers: z.record(z.string(), serverConfigSchema).optional(),\n databases: z.record(z.string(), postgresDatabaseConfigSchema).optional(),\n redis: z.record(z.string(), redisServerConfigSchema).optional(),\n})\n\nexport type StorageConfig = z.infer<typeof storageSchema>\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z"]}
|
package/dist/chunk-GPF64ENM.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
compilePattern,
|
|
3
|
-
scanServerRoutes,
|
|
4
|
-
scanWebSocketRoutes
|
|
5
|
-
} from "./chunk-OLPB36O2.js";
|
|
6
|
-
import {
|
|
7
|
-
scanServerActions
|
|
8
|
-
} from "./chunk-P3SGM4NR.js";
|
|
9
|
-
|
|
10
|
-
// src/server/scan/manifest.ts
|
|
11
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
12
|
-
import { join, resolve, relative } from "path";
|
|
13
|
-
function generateManifest(serverDir) {
|
|
14
|
-
const routes = scanServerRoutes(serverDir);
|
|
15
|
-
const actions = scanServerActions(serverDir);
|
|
16
|
-
const websockets = scanWebSocketRoutes(serverDir);
|
|
17
|
-
return {
|
|
18
|
-
version: 1,
|
|
19
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20
|
-
routes: routes.map((r) => ({
|
|
21
|
-
filePath: relative(serverDir, r.filePath),
|
|
22
|
-
routePath: r.routePath,
|
|
23
|
-
paramNames: r.paramNames,
|
|
24
|
-
...r.methods !== void 0 ? { methods: r.methods } : {}
|
|
25
|
-
})),
|
|
26
|
-
actions: actions.map((a) => ({
|
|
27
|
-
filePath: relative(serverDir, a.filePath),
|
|
28
|
-
actionPath: a.actionPath
|
|
29
|
-
})),
|
|
30
|
-
websockets: websockets.map((w) => ({
|
|
31
|
-
filePath: relative(serverDir, w.filePath),
|
|
32
|
-
wsPath: w.wsPath
|
|
33
|
-
}))
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function writeManifest(manifest, outputDir) {
|
|
37
|
-
mkdirSync(outputDir, { recursive: true });
|
|
38
|
-
const manifestPath = join(outputDir, "manifest.json");
|
|
39
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
40
|
-
}
|
|
41
|
-
function loadManifest(distDir, serverDir) {
|
|
42
|
-
const manifestPath = join(distDir, "manifest.json");
|
|
43
|
-
if (!existsSync(manifestPath)) {
|
|
44
|
-
throw new Error(`No manifest found at ${manifestPath}. Run "theo build" first.`);
|
|
45
|
-
}
|
|
46
|
-
const raw = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
47
|
-
const routes = raw.routes.map((r) => {
|
|
48
|
-
const { pattern, paramNames } = compilePattern(r.routePath);
|
|
49
|
-
return {
|
|
50
|
-
filePath: resolve(serverDir, r.filePath),
|
|
51
|
-
routePath: r.routePath,
|
|
52
|
-
paramNames,
|
|
53
|
-
pattern,
|
|
54
|
-
...r.methods !== void 0 ? { methods: r.methods } : {}
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
const actions = raw.actions.map((a) => ({
|
|
58
|
-
filePath: resolve(serverDir, a.filePath),
|
|
59
|
-
actionPath: a.actionPath
|
|
60
|
-
}));
|
|
61
|
-
const websockets = raw.websockets.map((w) => ({
|
|
62
|
-
filePath: resolve(serverDir, w.filePath),
|
|
63
|
-
wsPath: w.wsPath
|
|
64
|
-
}));
|
|
65
|
-
return { routes, actions, websockets };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export {
|
|
69
|
-
generateManifest,
|
|
70
|
-
writeManifest,
|
|
71
|
-
loadManifest
|
|
72
|
-
};
|
|
73
|
-
//# sourceMappingURL=chunk-GPF64ENM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/define/define-route.ts","../src/server/define/define-action.ts","../src/server/define/define-middleware.ts","../src/server/define/define-agent-endpoint.ts","../src/server/define/define-agent-tool.ts","../src/server/define/define-websocket.ts","../src/server/define/define-channel.ts","../src/server/define/define-plugin.ts"],"sourcesContent":["import type { z } from 'zod'\n\n// T2.2 (architecture-cleanup) — RouteConfig type moved to core/contracts/\n// (canonical home per ADR-0001 v3). Re-export preserves the public path\n// `import { type RouteConfig } from 'theokit/server'`.\nexport type { RouteConfig } from '../../core/contracts/route-config.js'\n\nimport type { RouteConfig } from '../../core/contracts/route-config.js'\n\n/**\n * Define a typed HTTP route.\n * Identity function — provides type inference for route handlers.\n */\nexport function defineRoute<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n TResponse = unknown,\n>(\n config: RouteConfig<TQuery, TBody, TParams, TCtx, TResponse>,\n): RouteConfig<TQuery, TBody, TParams, TCtx, TResponse> {\n return config\n}\n","import type { z } from 'zod'\n\n/**\n * Action wire-protocol accept mode per plan g3-server-actions-and-useaction\n * v1.2 ADR D1. Default behavior (when omitted) is `'json'`. `'form'` opts the\n * action into FormData multipart parsing for progressive-enhancement forms;\n * the runtime in `server/http/action-execute.ts` will coerce FormData entries\n * against the `input` schema via `formDataToObject` (Astro pattern).\n */\nexport type ActionAccept = 'form' | 'json'\n\nexport interface ActionConfig<TInput extends z.ZodType, TCtx = unknown> {\n /**\n * Zod input schema. Required: every action declares its input contract via\n * Zod (architecture rule: zod-is-SSOT). The shape becomes the handler's\n * typed `input` parameter via `z.infer<TInput>`.\n */\n input: TInput\n /**\n * Wire-protocol accept mode. Defaults to `'json'` when omitted. Setting\n * `'form'` switches the runtime to FormData multipart parsing — the input\n * schema MUST be `z.object(...)` so field-by-field coercion can drive\n * boolean string / number / array coercion (Astro pattern).\n */\n accept?: ActionAccept\n handler: (ctx: { input: z.infer<TInput>; ctx: TCtx }) => unknown\n}\n\n/**\n * Define a typed server action.\n *\n * Identity function — provides type inference for action handlers. The\n * runtime that consumes the config (validation + invocation + serialization)\n * lives in `server/http/action-execute.ts`.\n *\n * Per plan g3-server-actions-and-useaction v1.2 § Phase 1 / T1.2: the new\n * `accept?: 'form' | 'json'` field is the only contract change vs the\n * pre-G3 identity. Existing callsites (`defineAction({input, handler})`)\n * continue to compile — `accept` is opt-in.\n */\nexport function defineAction<TInput extends z.ZodType, TCtx = unknown>(\n config: ActionConfig<TInput, TCtx>,\n): ActionConfig<TInput, TCtx> {\n return config\n}\n","export type MiddlewareHandler = (\n request: Request,\n next: (request: Request) => Promise<Response>,\n) => Response | Promise<Response>\n\n/**\n * Define a middleware handler.\n * Identity function — provides type annotation for middleware.\n */\nexport function defineMiddleware(handler: MiddlewareHandler): MiddlewareHandler {\n return handler\n}\n","import type { z } from 'zod'\n\nimport type { AgentEvent } from '../agent/agent-types.js'\n\nimport type { RouteConfig } from './define-route.js'\n\n/**\n * T5.1 — defineAgentEndpoint\n *\n * Sugar over defineRoute (ADR D4). Accepts an async generator that yields\n * AgentEvents and produces a RouteConfig whose handler returns a Response\n * streaming Server-Sent Events (SSE).\n *\n * Wire format: `data: <JSON>\\n\\n` per event. Standards-compliant.\n *\n * The generator may throw — the wrapper catches and emits a final\n * `{ type: 'error', message }` event before closing the stream.\n *\n * The wrapper observes `request.signal` (EC-7) — when aborted, the\n * underlying generator is told to `return()` and the stream closes\n * promptly.\n *\n * Note (EC-12, Out of Scope): SSE backpressure (slow consumer) is not\n * handled here. For high-frequency token streaming consider a different\n * transport (WS) or a buffer policy. This MVP enqueues each event\n * immediately.\n */\n\nexport interface AgentEndpointHandlerArgs<TCtx = unknown, TBody = unknown> {\n query: undefined\n body: TBody\n params: undefined\n request: Request\n ctx: TCtx\n /**\n * Mutable headers bag that the wrapper merges into the SSE response BEFORE\n * the stream starts. Used by `createConversationHistory` to issue a\n * conversation-id cookie on first request. Append entries via\n * `cookieHeaders.append('set-cookie', '<cookie-string>')`.\n *\n * Cookies appended AFTER the first yield are NOT applied — the response\n * headers commit when the wrapper constructs the Response, before the\n * async generator runs. This is per HTTP semantics, not a wrapper choice.\n */\n cookieHeaders: Headers\n /**\n * Phase 3 (Production-Readiness #5) — request abort signal.\n *\n * Fires when the SSE client disconnects (browser closes tab, abort fetch,\n * etc.). Thread this to `agent.send(msg, { signal })` so the SDK cancels\n * the in-flight provider call — STOPS TOKENS FROM CHARGING for output\n * the user will never receive.\n *\n * EC-1 (MUST FIX): the signal is derived via duck-type detection\n * (`'aborted' in r.signal && typeof r.signal.addEventListener === 'function'`)\n * to survive cross-realm scenarios (Node 18 polyfills, undici, Edge\n * runtimes with their own AbortSignal globals). `instanceof AbortSignal`\n * would fail in those cases.\n */\n signal: AbortSignal\n}\n\nexport interface AgentEndpointConfig<TCtx = unknown, TBody = unknown> {\n handler: (\n args: AgentEndpointHandlerArgs<TCtx, TBody>,\n ) => AsyncGenerator<AgentEvent, void, unknown>\n}\n\nconst SSE_HEADERS = {\n 'content-type': 'text/event-stream',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n} as const\n\nfunction encodeSSE(event: AgentEvent): Uint8Array {\n return new TextEncoder().encode(`data: ${JSON.stringify(event)}\\n\\n`)\n}\n\n/**\n * Resolve an AbortSignal from either a Web `Request` (`.signal`) or a\n * Node `IncomingMessage` (`.aborted` flag + `'close'`/'aborted' events).\n *\n * The framework's `executeRoute` passes IncomingMessage to route handlers\n * today, but `defineAgentEndpoint` was designed for the Web Standards\n * `Request` shape. This helper bridges both — preventing a runtime crash\n * (`Cannot read properties of undefined (reading 'aborted')`) that would\n * otherwise abort the SSE stream silently before the first yield.\n */\nfunction resolveAbortSignal(request: unknown): AbortSignal {\n if (typeof request !== 'object' || request === null) {\n return new AbortController().signal\n }\n const r = request as {\n signal?: unknown\n aborted?: boolean\n on?: (event: string, cb: () => void) => void\n }\n if (\n typeof r.signal === 'object' &&\n r.signal !== null &&\n 'aborted' in r.signal &&\n typeof (r.signal as AbortSignal).addEventListener === 'function'\n ) {\n return r.signal as AbortSignal\n }\n\n const controller = new AbortController()\n if (r.aborted === true) controller.abort()\n if (typeof r.on === 'function') {\n r.on('close', () => {\n if (!controller.signal.aborted) controller.abort()\n })\n r.on('aborted', () => {\n if (!controller.signal.aborted) controller.abort()\n })\n }\n return controller.signal\n}\n\nexport function defineAgentEndpoint<TBody = unknown, TCtx = unknown>(\n config: AgentEndpointConfig<TCtx, TBody>,\n): RouteConfig<z.ZodUndefined, z.ZodUndefined, z.ZodUndefined, TCtx, Response> {\n return {\n handler: async ({ request, ctx, body, query, params }) => {\n const cookieHeaders = new Headers()\n const signal = resolveAbortSignal(request)\n const generator = config.handler({\n request,\n ctx: ctx,\n body: body as TBody,\n query: query,\n params: params,\n cookieHeaders,\n signal,\n })\n\n // Prime the generator to its first yield. This forces the handler's\n // synchronous + async setup (including `createConversationHistory` if\n // used) to run BEFORE the Response headers commit, so any Set-Cookie\n // lines appended to `cookieHeaders` land in the actual response.\n //\n // First-byte latency cost: bounded by the work up to the first yield\n // (typically 100-500ms for an LLM chat with `agent.send`). Acceptable\n // for the use case; alternative (heuristic detection of cookie writes)\n // is brittle.\n let firstResult: IteratorResult<AgentEvent, void>\n let primeError: unknown\n try {\n firstResult = await generator.next()\n } catch (err) {\n primeError = err\n firstResult = { value: undefined, done: true }\n }\n\n // Merge any cookies the handler issued into the response headers.\n const responseHeaders = new Headers(SSE_HEADERS)\n for (const value of cookieHeaders.getSetCookie()) {\n responseHeaders.append('set-cookie', value)\n }\n\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n const onAbort = () => {\n void generator.return(undefined)\n }\n\n if (signal.aborted) {\n controller.close()\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n\n try {\n // If priming threw, yield a final error event and close.\n if (primeError !== undefined) {\n let message: string\n if (primeError instanceof Error) {\n message = primeError.message\n } else if (typeof primeError === 'string') {\n message = primeError\n } else {\n message = 'agent handler failed during setup'\n }\n controller.enqueue(encodeSSE({ type: 'error', message }))\n return\n }\n // Enqueue the primed first value (if any).\n if (firstResult.done !== true) {\n controller.enqueue(encodeSSE(firstResult.value))\n } else {\n return\n }\n // Continue iterating remaining events.\n for await (const event of generator) {\n // `signal.aborted` can flip true asynchronously via the\n // listener registered above. ESLint's narrowing only sees\n // the false assertion at line 114; the dynamic abort is\n // legitimate and the guard prevents enqueue-after-close.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (signal.aborted) break\n controller.enqueue(encodeSSE(event))\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n controller.enqueue(encodeSSE({ type: 'error', message }))\n } finally {\n signal.removeEventListener('abort', onAbort)\n controller.close()\n }\n },\n cancel() {\n void generator.return(undefined)\n },\n })\n\n return new Response(stream, { headers: responseHeaders })\n },\n }\n}\n","import { z } from 'zod'\n\n/**\n * Item #4 — `defineAgentTool`\n *\n * Sugar over the `@theokit/sdk` `CustomTool` contract. Takes a Zod schema +\n * handler and produces a structurally-compatible `CustomTool` that\n * `Agent.create({ tools: [...] })` accepts.\n *\n * Uses Zod v4's native `z.toJSONSchema()` to convert the input schema to\n * JSON Schema for LLM providers.\n *\n * Handler error propagation:\n * `defineAgentTool` parses the input via the Zod schema BEFORE calling the\n * user handler. Invalid input throws a `ZodError`, which the SDK's tool-\n * dispatcher (or the `streamAgentRun` adapter) sees as a tool failure and\n * surfaces as an `error` AgentEvent on the SSE wire (ADR D3).\n */\n\n/**\n * Local mirror of the SDK's `CustomTool` interface. We don't `import type`\n * from `@theokit/sdk` because the SDK is an optional peer (consumers who\n * never call `defineAgentTool` shouldn't need it installed). The shape is\n * the wire contract; any structurally-matching object is accepted by\n * `Agent.create({ tools })`.\n *\n * @public\n */\nexport interface CustomTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n handler: (input: Record<string, unknown>) => string | Promise<string>\n}\n\n/**\n * Spec accepted by {@link defineAgentTool}. `inputSchema` is a Zod 3 schema\n * rooted in `z.object(...)`. The `handler` argument type is inferred via\n * `z.infer<T>`.\n *\n * @public\n */\nexport interface DefineAgentToolSpec<T extends z.ZodType> {\n /** Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`. */\n name: string\n /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */\n description: string\n /** Zod schema describing the input. Must be `z.object(...)` at the root. */\n inputSchema: T\n /** Handler invoked with the parsed input. */\n handler: (input: z.infer<T>) => string | Promise<string>\n}\n\nconst TOOL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/\n\nfunction isZodObject(schema: z.ZodType): boolean {\n // Zod 3 stores the typeName at `_def.typeName`. ZodObject has 'ZodObject'.\n // Refinements (`.refine`), transforms (`.transform`), and defaults wrap the\n // underlying schema in ZodEffects / ZodDefault / etc. — walk the chain via\n // `_def.schema` / `_def.innerType` until we hit ZodObject (or give up).\n // Avoids importing the ZodObject class to keep the runtime import surface\n // minimal.\n let current: unknown = schema\n for (let depth = 0; depth < 10; depth++) {\n const def = (current as { _def?: { typeName?: string; schema?: unknown; innerType?: unknown } })\n ._def\n if (def?.typeName === 'ZodObject') return true\n if (def?.schema !== undefined) {\n current = def.schema\n continue\n }\n if (def?.innerType !== undefined) {\n current = def.innerType\n continue\n }\n return false\n }\n return false\n}\n\n/**\n * Build a {@link CustomTool} from a Zod 3 schema + handler.\n *\n * Behavior:\n * - Validates `name` matches the LLM tool-name regex.\n * - Requires `inputSchema` to be a `ZodObject` (Anthropic + SDK contract).\n * - Warns (not throws) if `description` is empty — empty descriptions\n * degrade LLM tool selection.\n * - Converts the Zod schema to JSON Schema 7 inline (no `$ref`s — LLMs handle\n * inline schemas more reliably).\n * - Strips the top-level `$schema` field (Anthropic rejects schemas with\n * `$schema` at root in some provider modes).\n * - Wraps the handler to parse the input via the Zod schema BEFORE invoking\n * the user code — bad LLM-supplied input throws `ZodError`, which the SDK\n * converts to `tool_result(isError)`.\n *\n * @public\n */\nexport function defineAgentTool<T extends z.ZodType>(spec: DefineAgentToolSpec<T>): CustomTool {\n if (!TOOL_NAME_REGEX.test(spec.name)) {\n throw new Error(\n `defineAgentTool: name must match ${TOOL_NAME_REGEX.source}. Got: ${JSON.stringify(spec.name)}`,\n )\n }\n if (!isZodObject(spec.inputSchema)) {\n throw new Error('defineAgentTool: inputSchema must be a ZodObject (z.object({...}))')\n }\n if (spec.description.length === 0) {\n console.warn(\n `defineAgentTool(${JSON.stringify(spec.name)}): empty description degrades LLM tool selection — provide a one-sentence summary.`,\n )\n }\n\n // Zod v4 native JSON Schema conversion — replaces zod-to-json-schema dep.\n // Strip $schema (Anthropic + some providers reject it).\n const { $schema: _$schema, ...inputSchema } = z.toJSONSchema(spec.inputSchema) as Record<string, unknown> & {\n $schema?: unknown\n }\n\n return {\n name: spec.name,\n description: spec.description,\n inputSchema,\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const parsed = spec.inputSchema.parse(input) as z.infer<T>\n return await spec.handler(parsed)\n },\n }\n}\n","import type { IncomingMessage } from 'node:http'\n\nexport interface WebSocketLike {\n send(data: string | Buffer): void\n close(code?: number, reason?: string): void\n}\n\nexport interface WebSocketHandler {\n onOpen?: (ws: WebSocketLike, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, data: string | Buffer) => void\n onClose?: (ws: WebSocketLike, code: number, reason: Buffer) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Define a WebSocket endpoint handler.\n * Identity function — provides type inference for WebSocket handlers.\n */\nexport function defineWebSocket(handler: WebSocketHandler): WebSocketHandler {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 3/3 — Web-Standards WebSocket endpoint handler.\n *\n * Mirror of `WebSocketHandler` for the Web `Request` shape. `onOpen`\n * receives `request: Request` instead of `req: IncomingMessage`. The\n * rest of the lifecycle (onMessage, onClose, onError) is shape-agnostic\n * (`WebSocketLike` is already Web-standards-compatible per the existing\n * design — `send(string | Buffer)` works on both Node `ws` and Web\n * `WebSocket` instances; CF Workers / Bun / Deno coerce as needed at\n * the adapter boundary).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F (closes Phase F).\n *\n * **Architectural note — WebSocket upgrade semantics differ across runtimes:**\n * - Node + `ws`: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * `req` is `IncomingMessage`. Use `WebSocketHandler`.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request). Use `WebSocketHandlerWeb`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Cross-runtime WebSocket endpoints ship BOTH `WebSocketHandler` +\n * `WebSocketHandlerWeb` exports; the runtime adapter picks the matching\n * one. This is the canonical Hono / Nitric pattern.\n */\nexport interface WebSocketHandlerWeb {\n onOpen?: (ws: WebSocketLike, request: Request) => void\n onMessage?: (ws: WebSocketLike, data: string | Uint8Array) => void\n onClose?: (ws: WebSocketLike, code: number, reason: string) => void\n onError?: (ws: WebSocketLike, error: Error) => void\n}\n\n/**\n * Web-Standards `defineWebSocket` sibling. Identity function — provides\n * type inference for Web WebSocket handlers.\n *\n * **Type difference note vs Node path:**\n * - `onMessage` data is `string | Uint8Array` instead of `string | Buffer`\n * (Web standards have no `Buffer`; Node's Buffer is a Uint8Array\n * subclass so the Node path's Buffer values flow through unchanged\n * when adapters wrap them).\n * - `onClose` reason is `string` instead of `Buffer` (Web `CloseEvent`\n * exposes the reason as a UTF-8 string natively).\n */\nexport function defineWebSocketWeb(handler: WebSocketHandlerWeb): WebSocketHandlerWeb {\n return handler\n}\n","import type { IncomingMessage } from 'node:http'\n\nimport type { WebSocketLike } from './define-websocket.js'\n\nexport interface ChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, req: IncomingMessage) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Define a channel handler for WebSocket rooms.\n * Identity function — provides type inference for channel handlers.\n */\nexport function defineChannel<TMessage = unknown>(\n handler: ChannelHandler<TMessage>,\n): ChannelHandler<TMessage> {\n return handler\n}\n\n/**\n * T5a.2 Phase F slice 2/3 — Web-Standards channel handler.\n *\n * Mirror of `ChannelHandler<TMessage>` for the Web `Request` shape.\n * `onSubscribe` receives `request: Request` instead of `req: IncomingMessage`\n * — the rest of the surface (onMessage, onUnsubscribe) is shape-agnostic\n * (WebSocketLike is already Web-standards-compatible per `define-websocket.ts`).\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase F.\n *\n * **Architectural note:** WebSocket upgrade semantics differ across\n * runtimes:\n * - Node: `WebSocketServer.handleUpgrade(req, socket, head, cb)` —\n * hands you `req: IncomingMessage` at the upgrade handshake.\n * - CF Workers: `new WebSocketPair()` + `request.headers` (the upgrade\n * handshake IS a Web Request) — hands you `request: Request`.\n * - Bun: `server.upgrade(request, { data })` — same Web Request shape.\n * - Deno: `Deno.upgradeWebSocket(request)` — same Web Request shape.\n *\n * Channel handlers targeting CF/Bun/Deno use `WebChannelHandler`; legacy\n * Node consumers stay on `ChannelHandler`. Cross-runtime channels ship\n * both shapes.\n */\nexport interface WebChannelHandler<TMessage = unknown> {\n onSubscribe?: (ws: WebSocketLike, room: string, request: Request) => void\n onMessage?: (ws: WebSocketLike, room: string, data: TMessage) => void\n onUnsubscribe?: (ws: WebSocketLike, room: string) => void\n}\n\n/**\n * Web-Standards `defineChannel` sibling. Identity function — provides\n * type inference for Web channel handlers.\n */\nexport function defineWebChannel<TMessage = unknown>(\n handler: WebChannelHandler<TMessage>,\n): WebChannelHandler<TMessage> {\n return handler\n}\n","import type { TheoPlugin } from '../plugin-types.js'\n\n/**\n * Identity function for defining a Theo plugin.\n *\n * **Note:** Prefer `definePlugin` (shorter, canonical name per ADR-0008 D6).\n * Both functions are identical — `defineTheoPlugin` is kept as an alias for\n * existing in-tree consumers without forcing a migration sweep.\n */\nexport function defineTheoPlugin(plugin: TheoPlugin): TheoPlugin {\n return plugin\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/scan/manifest.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time manifest emitter / loader. All paths derived from `distDir`\n * + `serverDir`, themselves resolved from `process.cwd()`. No HTTP input.\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join, resolve, relative } from 'node:path'\n\nimport { scanServerActions } from './action-scan.js'\nimport type { ActionNode } from './action-scan.js'\nimport { compilePattern } from './match.js'\nimport type { ServerRouteNode } from './match.js'\nimport { scanServerRoutes } from './scan.js'\nimport { scanWebSocketRoutes } from './ws-scan.js'\nimport type { WebSocketRouteNode } from './ws-scan.js'\n\n// --- Manifest Types ---\n\nexport interface ManifestRoute {\n filePath: string\n routePath: string\n paramNames: string[]\n /** HTTP methods (uppercase) the route file exports. Optional — manifests\n * generated before G1 omit this; loaders treat absence as \"unknown\". */\n methods?: string[]\n}\n\nexport interface ManifestAction {\n filePath: string\n actionPath: string\n}\n\nexport interface ManifestWebSocket {\n filePath: string\n wsPath: string\n}\n\nexport interface TheoManifest {\n version: 1\n generatedAt: string\n routes: ManifestRoute[]\n actions: ManifestAction[]\n websockets: ManifestWebSocket[]\n}\n\nexport interface LoadedManifest {\n routes: ServerRouteNode[]\n actions: ActionNode[]\n websockets: WebSocketRouteNode[]\n}\n\n// --- Generate ---\n\nexport function generateManifest(serverDir: string): TheoManifest {\n const routes = scanServerRoutes(serverDir)\n const actions = scanServerActions(serverDir)\n const websockets = scanWebSocketRoutes(serverDir)\n\n return {\n version: 1,\n generatedAt: new Date().toISOString(),\n routes: routes.map((r) => ({\n filePath: relative(serverDir, r.filePath),\n routePath: r.routePath,\n paramNames: r.paramNames,\n ...(r.methods !== undefined ? { methods: r.methods } : {}),\n })),\n actions: actions.map((a) => ({\n filePath: relative(serverDir, a.filePath),\n actionPath: a.actionPath,\n })),\n websockets: websockets.map((w) => ({\n filePath: relative(serverDir, w.filePath),\n wsPath: w.wsPath,\n })),\n }\n}\n\n// --- Write ---\n\nexport function writeManifest(manifest: TheoManifest, outputDir: string): void {\n mkdirSync(outputDir, { recursive: true })\n const manifestPath = join(outputDir, 'manifest.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n}\n\n// --- Load ---\n\nexport function loadManifest(distDir: string, serverDir: string): LoadedManifest {\n const manifestPath = join(distDir, 'manifest.json')\n\n if (!existsSync(manifestPath)) {\n throw new Error(`No manifest found at ${manifestPath}. Run \"theo build\" first.`)\n }\n\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8')) as TheoManifest\n\n const routes: ServerRouteNode[] = raw.routes.map((r) => {\n const { pattern, paramNames } = compilePattern(r.routePath)\n return {\n filePath: resolve(serverDir, r.filePath),\n routePath: r.routePath,\n paramNames,\n pattern,\n ...(r.methods !== undefined ? { methods: r.methods } : {}),\n }\n })\n\n const actions: ActionNode[] = raw.actions.map((a) => ({\n filePath: resolve(serverDir, a.filePath),\n actionPath: a.actionPath,\n }))\n\n const websockets: WebSocketRouteNode[] = raw.websockets.map((w) => ({\n filePath: resolve(serverDir, w.filePath),\n wsPath: w.wsPath,\n }))\n\n return { routes, actions, websockets }\n}\n"],"mappings":";;;;;;;;;;;;AAIA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,MAAM,SAAS,gBAAgB;AA+CjC,SAAS,iBAAiB,WAAiC;AAChE,QAAM,SAAS,iBAAiB,SAAS;AACzC,QAAM,UAAU,kBAAkB,SAAS;AAC3C,QAAM,aAAa,oBAAoB,SAAS;AAEhD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,MACzB,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,WAAW,EAAE;AAAA,MACb,YAAY,EAAE;AAAA,MACd,GAAI,EAAE,YAAY,SAAY,EAAE,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1D,EAAE;AAAA,IACF,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,MAC3B,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,IACF,YAAY,WAAW,IAAI,CAAC,OAAO;AAAA,MACjC,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,QAAQ,EAAE;AAAA,IACZ,EAAE;AAAA,EACJ;AACF;AAIO,SAAS,cAAc,UAAwB,WAAyB;AAC7E,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,eAAe,KAAK,WAAW,eAAe;AACpD,gBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC/D;AAIO,SAAS,aAAa,SAAiB,WAAmC;AAC/E,QAAM,eAAe,KAAK,SAAS,eAAe;AAElD,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,UAAM,IAAI,MAAM,wBAAwB,YAAY,2BAA2B;AAAA,EACjF;AAEA,QAAM,MAAM,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAE1D,QAAM,SAA4B,IAAI,OAAO,IAAI,CAAC,MAAM;AACtD,UAAM,EAAE,SAAS,WAAW,IAAI,eAAe,EAAE,SAAS;AAC1D,WAAO;AAAA,MACL,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,MACvC,WAAW,EAAE;AAAA,MACb;AAAA,MACA;AAAA,MACA,GAAI,EAAE,YAAY,SAAY,EAAE,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,QAAM,UAAwB,IAAI,QAAQ,IAAI,CAAC,OAAO;AAAA,IACpD,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,YAAY,EAAE;AAAA,EAChB,EAAE;AAEF,QAAM,aAAmC,IAAI,WAAW,IAAI,CAAC,OAAO;AAAA,IAClE,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,QAAQ,EAAE;AAAA,EACZ,EAAE;AAEF,SAAO,EAAE,QAAQ,SAAS,WAAW;AACvC;","names":[]}
|