theokit 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/{actions-virtual-module-4WVKHQ2X.js → actions-virtual-module-52C6RKDK.js} +4 -3
  2. package/dist/{actions-virtual-module-LTOXBSCF.js.map → actions-virtual-module-52C6RKDK.js.map} +1 -1
  3. package/dist/{actions-virtual-module-LTOXBSCF.js → actions-virtual-module-TESQ2T4F.js} +9 -7
  4. package/dist/{actions-virtual-module-4WVKHQ2X.js.map → actions-virtual-module-TESQ2T4F.js.map} +1 -1
  5. package/dist/{app-typed-client-SAETWZT5.js → app-typed-client-A4CP55NM.js} +4 -3
  6. package/dist/{app-typed-client-SAETWZT5.js.map → app-typed-client-A4CP55NM.js.map} +1 -1
  7. package/dist/{app-typed-client-4N5OGNOQ.js → app-typed-client-AJ65YXJM.js} +9 -7
  8. package/dist/{app-typed-client-4N5OGNOQ.js.map → app-typed-client-AJ65YXJM.js.map} +1 -1
  9. package/dist/boot/index.d.ts +39 -0
  10. package/dist/boot/index.js +35 -0
  11. package/dist/boot/index.js.map +1 -0
  12. package/dist/{build-KQD2EDU4.js → build-S73OUGYP.js} +2 -2
  13. package/dist/{chunk-CBGRFVF5.js → chunk-256WFVQQ.js} +2 -2
  14. package/dist/{chunk-4I47JW5O.js → chunk-2DXLN7FF.js} +5 -5
  15. package/dist/{chunk-6BUUZ2PK.js → chunk-4EL6OX3P.js} +1 -1
  16. package/dist/{chunk-6VSKLYT6.js → chunk-7BJ6YLKV.js} +19 -19
  17. package/dist/{chunk-3FYJ7R7N.js → chunk-BB4ISKOB.js} +93 -78
  18. package/dist/chunk-BB4ISKOB.js.map +1 -0
  19. package/dist/{chunk-3IOFTG3L.js → chunk-OPLICDUB.js} +1 -1
  20. package/dist/{server-error-to-envelope-UJK6OWDJ.js → chunk-UVXB2ER7.js} +42 -5
  21. package/dist/chunk-UVXB2ER7.js.map +1 -0
  22. package/dist/chunk-V3LJN2GS.js +38 -0
  23. package/dist/chunk-V3LJN2GS.js.map +1 -0
  24. package/dist/{chunk-Z5IGLBWE.js → chunk-WFNLNIJX.js} +1 -1
  25. package/dist/{chunk-TQYSPYKU.js → chunk-WS67ONGI.js} +4 -1
  26. package/dist/chunk-WS67ONGI.js.map +1 -0
  27. package/dist/chunk-X32XEJXR.js +35 -0
  28. package/dist/chunk-X32XEJXR.js.map +1 -0
  29. package/dist/{chunk-MV4LKTW6.js → chunk-Z374WCMZ.js} +55 -9
  30. package/dist/chunk-Z374WCMZ.js.map +1 -0
  31. package/dist/cli/index.js +3 -3
  32. package/dist/client/index.d.ts +77 -3
  33. package/dist/client/index.js +111 -3
  34. package/dist/client/index.js.map +1 -1
  35. package/dist/{dev-NDEZA2MP.js → dev-WLUBGVDM.js} +5 -4
  36. package/dist/{dev-NDEZA2MP.js.map → dev-WLUBGVDM.js.map} +1 -1
  37. package/dist/health-route-C0hk64_U.d.ts +57 -0
  38. package/dist/{index-DWZF8uQD.d.ts → index-B40qUSrQ.d.ts} +178 -2
  39. package/dist/index.js +10 -8
  40. package/dist/index.js.map +1 -1
  41. package/dist/server/auth/index.js +1 -1
  42. package/dist/server/define/index.d.ts +1 -0
  43. package/dist/server/define/index.js +14 -2
  44. package/dist/server/http/index.d.ts +4 -3
  45. package/dist/server/http/index.js +21 -3
  46. package/dist/server/index.d.ts +2 -1
  47. package/dist/server/index.js +105 -79
  48. package/dist/server/index.js.map +1 -1
  49. package/dist/server/jobs/index.js +4 -4
  50. package/dist/server/observability/index.js +1 -1
  51. package/dist/server/scan/index.js +1 -1
  52. package/dist/{server-error-to-envelope-NO4CCAFQ.js → server-error-to-envelope-EOFUIUNB.js} +2 -2
  53. package/dist/server-error-to-envelope-W7KJ6AXW.js +10 -0
  54. package/dist/{start-SPFWOGHL.js → start-WUFA2SRC.js} +48 -3
  55. package/dist/start-WUFA2SRC.js.map +1 -0
  56. package/dist/vite-plugin/index.js +10 -8
  57. package/dist/{vite-plugin-43KQU5S7.js → vite-plugin-TEHQFA6D.js} +5 -4
  58. package/dist/vite-plugin-TEHQFA6D.js.map +1 -0
  59. package/package.json +5 -1
  60. package/dist/chunk-3FYJ7R7N.js.map +0 -1
  61. package/dist/chunk-MV4LKTW6.js.map +0 -1
  62. package/dist/chunk-TQYSPYKU.js.map +0 -1
  63. package/dist/server-error-to-envelope-UJK6OWDJ.js.map +0 -1
  64. package/dist/start-SPFWOGHL.js.map +0 -1
  65. /package/dist/{build-KQD2EDU4.js.map → build-S73OUGYP.js.map} +0 -0
  66. /package/dist/{chunk-CBGRFVF5.js.map → chunk-256WFVQQ.js.map} +0 -0
  67. /package/dist/{chunk-4I47JW5O.js.map → chunk-2DXLN7FF.js.map} +0 -0
  68. /package/dist/{chunk-6BUUZ2PK.js.map → chunk-4EL6OX3P.js.map} +0 -0
  69. /package/dist/{chunk-6VSKLYT6.js.map → chunk-7BJ6YLKV.js.map} +0 -0
  70. /package/dist/{chunk-3IOFTG3L.js.map → chunk-OPLICDUB.js.map} +0 -0
  71. /package/dist/{chunk-Z5IGLBWE.js.map → chunk-WFNLNIJX.js.map} +0 -0
  72. /package/dist/{server-error-to-envelope-NO4CCAFQ.js.map → server-error-to-envelope-EOFUIUNB.js.map} +0 -0
  73. /package/dist/{vite-plugin-43KQU5S7.js.map → server-error-to-envelope-W7KJ6AXW.js.map} +0 -0
@@ -1,7 +1,3 @@
1
- #!/usr/bin/env node
2
- import "tsx/esm";
3
- import "./chunk-KT3MWNZG.js";
4
-
5
1
  // src/core/contracts/theo-error.ts
6
2
  var TheoError = class extends Error {
7
3
  name = "TheoError";
@@ -55,6 +51,43 @@ function sanitizeMeta(meta) {
55
51
  }
56
52
  return out;
57
53
  }
54
+ var NotFoundError = class extends TheoError {
55
+ constructor(message = "Not Found", meta) {
56
+ super({ code: "NOT_FOUND", message, ...meta !== void 0 ? { meta } : {} });
57
+ }
58
+ };
59
+ function fromUnknown(value) {
60
+ if (value instanceof TheoError) return value;
61
+ if (value instanceof Error) {
62
+ return new TheoError({
63
+ code: "INTERNAL_SERVER_ERROR",
64
+ message: value.message,
65
+ cause: value
66
+ });
67
+ }
68
+ if (typeof value === "string") {
69
+ return new TheoError({ code: "INTERNAL_SERVER_ERROR", message: value });
70
+ }
71
+ if (value === void 0 || value === null) {
72
+ return new TheoError({ code: "INTERNAL_SERVER_ERROR", message: "Unknown error" });
73
+ }
74
+ return new TheoError({
75
+ code: "INTERNAL_SERVER_ERROR",
76
+ message: `Non-Error value thrown: ${safeStringify(value)}`,
77
+ cause: value
78
+ });
79
+ }
80
+ function safeStringify(value) {
81
+ if (typeof value === "string") return value;
82
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
83
+ if (typeof value === "bigint") return `${value.toString()}n`;
84
+ if (typeof value === "symbol") return value.toString();
85
+ try {
86
+ return JSON.stringify(value);
87
+ } catch {
88
+ return "[unstringifiable value]";
89
+ }
90
+ }
58
91
 
59
92
  // src/core/contracts/server-error-to-envelope.ts
60
93
  var ERROR_NAME_TO_CODE = /* @__PURE__ */ new Map([
@@ -128,7 +161,11 @@ function serverErrorToEnvelope(value) {
128
161
  ext
129
162
  };
130
163
  }
164
+
131
165
  export {
166
+ TheoError,
167
+ NotFoundError,
168
+ fromUnknown,
132
169
  serverErrorToEnvelope
133
170
  };
134
- //# sourceMappingURL=server-error-to-envelope-UJK6OWDJ.js.map
171
+ //# sourceMappingURL=chunk-UVXB2ER7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts/theo-error.ts","../src/core/contracts/server-error-to-envelope.ts"],"sourcesContent":["/**\n * `TheoError` — envelope-emitting `Error` subclass per plan\n * g5-error-envelope-cross-layer v1.0 § Phase 1 / T1.2.\n *\n * Mirrors trpc's TRPCError ergonomic pattern (referencia:\n * trpc/packages/server/src/unstable-core-do-not-import/error/TRPCError.ts:65-87)\n * adapted for theokit's hybrid envelope model (blueprint Form 4).\n *\n * Per ADR D5: `meta.stack` is auto-stripped in non-dev builds when serialized\n * via `toJSON()` — defense against accidental stack leak to clients (encore\n * `json:\"-\"` analog).\n *\n * Lives in `core/contracts/` per architecture.md v3 invariant #3.\n */\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from './error-envelope.js'\n\n/**\n * Constructor options. `code` and `message` are required; `cause` follows TC39\n * proposal-error-cause; `meta` and `ext` are opt-in per blueprint ADR D2.\n */\nexport interface TheoErrorOptions<TExt = unknown> {\n readonly code: TheoErrorCode\n readonly message: string\n readonly cause?: unknown\n readonly meta?: Record<string, unknown>\n readonly ext?: TExt\n}\n\n/**\n * `TheoError` — envelope-emitting Error subclass. Use this anywhere in\n * `theokit/server`, route handlers, or middleware to emit a typed,\n * cross-layer-compatible error.\n *\n * Mirrors TRPCError pattern: `code` is required, `message` defaults to\n * code if omitted, `cause` chain is preserved via TC39 proposal-error-cause.\n */\nexport class TheoError<TExt = unknown> extends Error {\n override readonly name = 'TheoError'\n readonly code: TheoErrorCode\n readonly meta?: Record<string, unknown>\n readonly ext?: TExt\n\n constructor(opts: TheoErrorOptions<TExt>) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined)\n this.code = opts.code\n this.meta = opts.meta\n this.ext = opts.ext\n }\n\n /**\n * The canonical envelope view of this error. Use when crossing the wire\n * boundary or feeding telemetry — exposes the shape consumers can rely on\n * without depending on the class identity.\n */\n get envelope(): TheoErrorEnvelope<TExt> {\n return {\n code: this.code,\n message: this.message,\n cause: (this as Error & { cause?: unknown }).cause,\n meta: this.meta,\n ext: this.ext,\n }\n }\n\n /**\n * JSON serialization — emits envelope shape only (no name/stack class\n * internals). Per ADR D5, `meta.stack` is auto-stripped in non-dev to\n * prevent accidental leak; other meta keys are preserved.\n */\n toJSON(): TheoErrorEnvelope<TExt> {\n const meta = this.meta ? sanitizeMeta(this.meta) : undefined\n return {\n code: this.code,\n message: this.message,\n cause: (this as Error & { cause?: unknown }).cause,\n meta,\n ext: this.ext,\n }\n }\n}\n\n/**\n * Strip server-only sensitive meta keys in non-dev builds. Per ADR D5,\n * `stack` is the canonical leak vector (trpc accidentally exposes it via\n * `config.isDev` even today — references/trpc getErrorShape.ts:29-31).\n * Default to safe-by-default; preserve in dev only.\n */\nfunction sanitizeMeta(meta: Record<string, unknown>): Record<string, unknown> | undefined {\n // Treat anything other than NODE_ENV === 'development' as production-safe\n // (CI, staging, production all strip). Matches the security posture in\n // blueprint EC-5 (default STRICT).\n if (process.env.NODE_ENV === 'development') {\n return meta\n }\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(meta)) {\n if (k === 'stack') continue\n out[k] = v\n }\n return out\n}\n\n/**\n * Coerce any unknown value into a TheoError envelope.\n *\n * - If value is already a TheoError, returns it unchanged (no double-wrap).\n * - If value is a plain Error, wraps it as `INTERNAL_SERVER_ERROR` with\n * `cause` chain preserving the original.\n * - Other values (string, undefined, object) are coerced safely with a\n * meaningful message.\n *\n * Use at framework boundaries (route handler catch-all, middleware catch,\n * SDK egress) to ensure every error crossing the wire carries an envelope.\n */\n/**\n * `NotFoundError` — typed 404. A `TheoError` with code `NOT_FOUND`, so it\n * carries a stable `code` (not a bare status literal) and maps to HTTP 404 via\n * `envelopeCodeToStatus`. Throw it from a `defineRoute` handler for ergonomic\n * 404s (M7-1).\n */\nexport class NotFoundError extends TheoError {\n constructor(message = 'Not Found', meta?: Record<string, unknown>) {\n super({ code: 'NOT_FOUND', message, ...(meta !== undefined ? { meta } : {}) })\n }\n}\n\nexport function fromUnknown(value: unknown): TheoError {\n if (value instanceof TheoError) return value\n if (value instanceof Error) {\n return new TheoError({\n code: 'INTERNAL_SERVER_ERROR',\n message: value.message,\n cause: value,\n })\n }\n if (typeof value === 'string') {\n return new TheoError({ code: 'INTERNAL_SERVER_ERROR', message: value })\n }\n if (value === undefined || value === null) {\n return new TheoError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unknown error' })\n }\n // Object / number / boolean / etc. — best-effort stringify\n return new TheoError({\n code: 'INTERNAL_SERVER_ERROR',\n message: `Non-Error value thrown: ${safeStringify(value)}`,\n cause: value,\n })\n}\n\n/**\n * Best-effort stringify of an unknown value for error message context.\n * JSON.stringify first (preserves shape for diagnostics); on cycle/BigInt/symbol\n * failure, signal explicitly instead of falling back to `[object Object]`.\n */\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (typeof value === 'bigint') return `${value.toString()}n`\n if (typeof value === 'symbol') return value.toString()\n try {\n return JSON.stringify(value)\n } catch {\n return '[unstringifiable value]'\n }\n}\n","/**\n * G5 T2.5 — server-side boundary translation per blueprint ADR D3.\n *\n * Translates ad-hoc Error class instances (FileTooLargeError,\n * AuthRequiredError, RouterConventionError, etc.) into the canonical\n * `TheoErrorEnvelope` shape at the wire boundary. This is the SINGLE\n * boundary translation point — class identities stay in place inside the\n * codebase (no invasive call-site rewrites), but the envelope is what\n * crosses to clients.\n *\n * Pure; no I/O. Lives in `core/contracts/` per architecture.md v3\n * invariant #3 (canonical home for shared client↔server types). This file\n * is the EXCEPTION-of-an-exception: it intentionally inspects Error names\n * by string instead of `instanceof` to avoid `core/contracts/` taking a\n * dependency on `server/`. The mapping table is small and grep-discoverable.\n */\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from './error-envelope.js'\nimport { TheoError } from './theo-error.js'\n\n/**\n * Mapping from ad-hoc Error class name to envelope code. Add entries here\n * when a new ad-hoc Error is introduced anywhere in `theokit/server/`.\n * Build-time errors (Duplicate*, ActionScanError, InvalidPluginShapeError)\n * are NOT in this table by design — they never cross the wire boundary,\n * so their canonical shape is `INTERNAL_SERVER_ERROR` via the default branch.\n */\nconst ERROR_NAME_TO_CODE: ReadonlyMap<string, TheoErrorCode> = new Map<string, TheoErrorCode>([\n ['AuthRequiredError', 'UNAUTHORIZED'],\n ['FileTooLargeError', 'PAYLOAD_TOO_LARGE'],\n ['RequestBodyTooLargeError', 'PAYLOAD_TOO_LARGE'],\n ['BodyTooLargeError', 'PAYLOAD_TOO_LARGE'],\n // RouterConventionError reaches the wire only during dev-error overlay;\n // INTERNAL_SERVER_ERROR is appropriate, but we add a HintExt-shaped ext\n // with the migration guidance for the devtools display.\n ['RouterConventionError', 'INTERNAL_SERVER_ERROR'],\n])\n\ntype MetaExtractor = (err: Error) => Record<string, unknown> | null\n\n/**\n * Class-specific metadata extractors. Each function receives the raw Error\n * and returns an object merged into envelope.meta (excluding name/message\n * which are reserved).\n */\nconst META_EXTRACTORS: ReadonlyMap<string, MetaExtractor> = new Map<string, MetaExtractor>([\n [\n 'FileTooLargeError',\n (err) => {\n const e = err as Error & { truncatedFilenames?: string[]; maxFileSize?: number }\n return {\n truncatedFilenames: e.truncatedFilenames ?? [],\n maxFileSize: e.maxFileSize,\n }\n },\n ],\n [\n 'RouterConventionError',\n (err) => {\n const e = err as Error & { file?: string; suggestion?: string; migrationUrl?: string }\n return {\n file: e.file,\n suggestion: e.suggestion,\n migrationUrl: e.migrationUrl,\n }\n },\n ],\n])\n\ntype ExtBuilder = (err: Error) => unknown\n\n/**\n * Class-specific ext (extension slot) builders. RouterConventionError ships a\n * HintExt-shaped `{ hint }` so devtools / dev overlay can render the actionable\n * migration tip directly.\n */\nconst EXT_BUILDERS: ReadonlyMap<string, ExtBuilder> = new Map<string, ExtBuilder>([\n [\n 'RouterConventionError',\n () => ({\n hint: 'Run `theokit migrate router` to convert dotted-basename routes to directory-nested form.',\n }),\n ],\n])\n\n/**\n * Translate any server-side Error instance into a canonical envelope.\n *\n * Pass-through behavior:\n * - `TheoError` instances return their `.envelope` unchanged.\n * - Plain `Error` with unknown name → `INTERNAL_SERVER_ERROR`.\n * - Non-Error values → wrapped via `TheoError.fromUnknown` semantics.\n *\n * Meta carries the source class `name` plus class-specific fields when an\n * extractor is registered. This keeps wire-side telemetry useful without\n * leaking implementation details.\n */\nexport function serverErrorToEnvelope(value: unknown): TheoErrorEnvelope {\n if (value instanceof TheoError) {\n return value.envelope\n }\n if (!(value instanceof Error)) {\n return {\n code: 'INTERNAL_SERVER_ERROR',\n message: typeof value === 'string' ? value : 'Unknown error',\n }\n }\n const name = value.name\n const code = ERROR_NAME_TO_CODE.get(name) ?? 'INTERNAL_SERVER_ERROR'\n const extractor = META_EXTRACTORS.get(name)\n const extra = extractor ? extractor(value) : null\n const meta: Record<string, unknown> = { name }\n if (extra) {\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined) meta[k] = v\n }\n }\n const extBuilder = EXT_BUILDERS.get(name)\n const ext = extBuilder ? extBuilder(value) : undefined\n return {\n code,\n message: value.message,\n cause: (value as Error & { cause?: unknown }).cause,\n meta,\n ext,\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,38 @@
1
+ // src/server/define/health-route.ts
2
+ var HEALTH_PATH = "/__theo/health";
3
+ var READY_PATH = "/__theo/ready";
4
+ function defineHealthRoute(handler = () => ({ status: "ok" })) {
5
+ return { kind: "health", handler };
6
+ }
7
+ function defineReadyRoute(probe) {
8
+ return { kind: "ready", probe };
9
+ }
10
+ async function serveReservedRoute(pathname, routes = {}) {
11
+ if (pathname === HEALTH_PATH) {
12
+ const handler = routes.health?.handler ?? (() => ({ status: "ok" }));
13
+ return { status: 200, body: handler() };
14
+ }
15
+ if (pathname === READY_PATH) {
16
+ const probe = routes.ready?.probe;
17
+ if (probe === void 0) {
18
+ return { status: 200, body: { status: "ready" } };
19
+ }
20
+ try {
21
+ const ready = await probe();
22
+ return ready ? { status: 200, body: { status: "ready" } } : { status: 503, body: { status: "not-ready" } };
23
+ } catch (err) {
24
+ console.error("[theo] readiness probe threw \u2014 treating as not-ready", err);
25
+ return { status: 503, body: { status: "not-ready" } };
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+
31
+ export {
32
+ HEALTH_PATH,
33
+ READY_PATH,
34
+ defineHealthRoute,
35
+ defineReadyRoute,
36
+ serveReservedRoute
37
+ };
38
+ //# sourceMappingURL=chunk-V3LJN2GS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/define/health-route.ts"],"sourcesContent":["/**\n * M7-2 — health/ready reserved routes for the convention/filesystem-route\n * server. Liveness (`/__theo/health`, always 200) and readiness\n * (`/__theo/ready`, 200/503 from a probe) are registered on a reserved\n * namespace BEFORE the user-route catch-all + 404 branch — mirroring nitro's\n * `/_nitro/*` reserved-namespace pattern (knowledge-base/references/nitro/src/\n * runtime/internal/routes/dev-tasks.ts). Liveness and readiness are kept\n * separate by design: liveness says \"the process is up\", readiness says\n * \"dependencies are up\".\n *\n * @public\n */\n\n/** Reserved path for the liveness endpoint. */\nexport const HEALTH_PATH = '/__theo/health'\n/** Reserved path for the readiness endpoint. */\nexport const READY_PATH = '/__theo/ready'\n\n/** Config produced by {@link defineHealthRoute}. */\nexport interface HealthRouteConfig {\n readonly kind: 'health'\n /** Returns the liveness body (auto-serialized to JSON). Default: `{ status: 'ok' }`. */\n readonly handler: () => unknown\n}\n\n/** Config produced by {@link defineReadyRoute}. */\nexport interface ReadyRouteConfig {\n readonly kind: 'ready'\n /** Resolves `true` when dependencies are ready. A throw/reject is treated as not-ready (503). */\n readonly probe: () => boolean | Promise<boolean>\n}\n\n/** The reserved-route registry consulted by {@link serveReservedRoute}. */\nexport interface ReservedRoutes {\n readonly health?: HealthRouteConfig\n readonly ready?: ReadyRouteConfig\n}\n\n/** A reserved-route response: an HTTP status + a JSON-serializable body. */\nexport interface ReservedResponse {\n readonly status: number\n readonly body: unknown\n}\n\n/**\n * Define the liveness route. The default handler returns `{ status: 'ok' }`.\n * Liveness is always 200 — it asserts the process is up, not that dependencies are.\n */\nexport function defineHealthRoute(\n handler: () => unknown = () => ({ status: 'ok' }),\n): HealthRouteConfig {\n return { kind: 'health', handler }\n}\n\n/**\n * Define the readiness route. The `probe` decides 200 (ready) vs 503 (not-ready).\n * A probe that throws/rejects is treated as not-ready (503) — never a 500 crash.\n */\nexport function defineReadyRoute(probe: () => boolean | Promise<boolean>): ReadyRouteConfig {\n return { kind: 'ready', probe }\n}\n\n/**\n * Pure dispatcher: map a request pathname to a reserved-route response, or\n * `null` when the path is not reserved (so the user catch-all + 404 take over).\n * Liveness defaults to 200 `{status:'ok'}`; readiness without a probe is\n * trivially ready (200). Registered BEFORE the user catch-all by the caller.\n */\nexport async function serveReservedRoute(\n pathname: string,\n routes: ReservedRoutes = {},\n): Promise<ReservedResponse | null> {\n if (pathname === HEALTH_PATH) {\n const handler = routes.health?.handler ?? (() => ({ status: 'ok' }))\n return { status: 200, body: handler() }\n }\n if (pathname === READY_PATH) {\n const probe = routes.ready?.probe\n if (probe === undefined) {\n return { status: 200, body: { status: 'ready' } }\n }\n try {\n const ready = await probe()\n return ready\n ? { status: 200, body: { status: 'ready' } }\n : { status: 503, body: { status: 'not-ready' } }\n } catch (err) {\n // 503 (the dependency is not ready) but NEVER swallow the cause — a\n // readiness probe failing silently is invisible to ops (project rule:\n // \"NUNCA engula exceções\"). Log the cause, keep the 503 contract.\n console.error('[theo] readiness probe threw — treating as not-ready', err)\n return { status: 503, body: { status: 'not-ready' } }\n }\n }\n return null\n}\n"],"mappings":";AAcO,IAAM,cAAc;AAEpB,IAAM,aAAa;AAgCnB,SAAS,kBACd,UAAyB,OAAO,EAAE,QAAQ,KAAK,IAC5B;AACnB,SAAO,EAAE,MAAM,UAAU,QAAQ;AACnC;AAMO,SAAS,iBAAiB,OAA2D;AAC1F,SAAO,EAAE,MAAM,SAAS,MAAM;AAChC;AAQA,eAAsB,mBACpB,UACA,SAAyB,CAAC,GACQ;AAClC,MAAI,aAAa,aAAa;AAC5B,UAAM,UAAU,OAAO,QAAQ,YAAY,OAAO,EAAE,QAAQ,KAAK;AACjE,WAAO,EAAE,QAAQ,KAAK,MAAM,QAAQ,EAAE;AAAA,EACxC;AACA,MAAI,aAAa,YAAY;AAC3B,UAAM,QAAQ,OAAO,OAAO;AAC5B,QAAI,UAAU,QAAW;AACvB,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,QAAQ,QAAQ,EAAE;AAAA,IAClD;AACA,QAAI;AACF,YAAM,QAAQ,MAAM,MAAM;AAC1B,aAAO,QACH,EAAE,QAAQ,KAAK,MAAM,EAAE,QAAQ,QAAQ,EAAE,IACzC,EAAE,QAAQ,KAAK,MAAM,EAAE,QAAQ,YAAY,EAAE;AAAA,IACnD,SAAS,KAAK;AAIZ,cAAQ,MAAM,6DAAwD,GAAG;AACzE,aAAO,EAAE,QAAQ,KAAK,MAAM,EAAE,QAAQ,YAAY,EAAE;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -326,4 +326,4 @@ export {
326
326
  _resetWarnOnceForTests,
327
327
  warnOnce
328
328
  };
329
- //# sourceMappingURL=chunk-Z5IGLBWE.js.map
329
+ //# sourceMappingURL=chunk-WFNLNIJX.js.map
@@ -1,3 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+
1
4
  // src/core/contracts/theo-error.ts
2
5
  var TheoError = class extends Error {
3
6
  name = "TheoError";
@@ -128,4 +131,4 @@ function serverErrorToEnvelope(value) {
128
131
  export {
129
132
  serverErrorToEnvelope
130
133
  };
131
- //# sourceMappingURL=chunk-TQYSPYKU.js.map
134
+ //# sourceMappingURL=chunk-WS67ONGI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts/theo-error.ts","../src/core/contracts/server-error-to-envelope.ts"],"sourcesContent":["/**\n * `TheoError` — envelope-emitting `Error` subclass per plan\n * g5-error-envelope-cross-layer v1.0 § Phase 1 / T1.2.\n *\n * Mirrors trpc's TRPCError ergonomic pattern (referencia:\n * trpc/packages/server/src/unstable-core-do-not-import/error/TRPCError.ts:65-87)\n * adapted for theokit's hybrid envelope model (blueprint Form 4).\n *\n * Per ADR D5: `meta.stack` is auto-stripped in non-dev builds when serialized\n * via `toJSON()` — defense against accidental stack leak to clients (encore\n * `json:\"-\"` analog).\n *\n * Lives in `core/contracts/` per architecture.md v3 invariant #3.\n */\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from './error-envelope.js'\n\n/**\n * Constructor options. `code` and `message` are required; `cause` follows TC39\n * proposal-error-cause; `meta` and `ext` are opt-in per blueprint ADR D2.\n */\nexport interface TheoErrorOptions<TExt = unknown> {\n readonly code: TheoErrorCode\n readonly message: string\n readonly cause?: unknown\n readonly meta?: Record<string, unknown>\n readonly ext?: TExt\n}\n\n/**\n * `TheoError` — envelope-emitting Error subclass. Use this anywhere in\n * `theokit/server`, route handlers, or middleware to emit a typed,\n * cross-layer-compatible error.\n *\n * Mirrors TRPCError pattern: `code` is required, `message` defaults to\n * code if omitted, `cause` chain is preserved via TC39 proposal-error-cause.\n */\nexport class TheoError<TExt = unknown> extends Error {\n override readonly name = 'TheoError'\n readonly code: TheoErrorCode\n readonly meta?: Record<string, unknown>\n readonly ext?: TExt\n\n constructor(opts: TheoErrorOptions<TExt>) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined)\n this.code = opts.code\n this.meta = opts.meta\n this.ext = opts.ext\n }\n\n /**\n * The canonical envelope view of this error. Use when crossing the wire\n * boundary or feeding telemetry — exposes the shape consumers can rely on\n * without depending on the class identity.\n */\n get envelope(): TheoErrorEnvelope<TExt> {\n return {\n code: this.code,\n message: this.message,\n cause: (this as Error & { cause?: unknown }).cause,\n meta: this.meta,\n ext: this.ext,\n }\n }\n\n /**\n * JSON serialization — emits envelope shape only (no name/stack class\n * internals). Per ADR D5, `meta.stack` is auto-stripped in non-dev to\n * prevent accidental leak; other meta keys are preserved.\n */\n toJSON(): TheoErrorEnvelope<TExt> {\n const meta = this.meta ? sanitizeMeta(this.meta) : undefined\n return {\n code: this.code,\n message: this.message,\n cause: (this as Error & { cause?: unknown }).cause,\n meta,\n ext: this.ext,\n }\n }\n}\n\n/**\n * Strip server-only sensitive meta keys in non-dev builds. Per ADR D5,\n * `stack` is the canonical leak vector (trpc accidentally exposes it via\n * `config.isDev` even today — references/trpc getErrorShape.ts:29-31).\n * Default to safe-by-default; preserve in dev only.\n */\nfunction sanitizeMeta(meta: Record<string, unknown>): Record<string, unknown> | undefined {\n // Treat anything other than NODE_ENV === 'development' as production-safe\n // (CI, staging, production all strip). Matches the security posture in\n // blueprint EC-5 (default STRICT).\n if (process.env.NODE_ENV === 'development') {\n return meta\n }\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(meta)) {\n if (k === 'stack') continue\n out[k] = v\n }\n return out\n}\n\n/**\n * Coerce any unknown value into a TheoError envelope.\n *\n * - If value is already a TheoError, returns it unchanged (no double-wrap).\n * - If value is a plain Error, wraps it as `INTERNAL_SERVER_ERROR` with\n * `cause` chain preserving the original.\n * - Other values (string, undefined, object) are coerced safely with a\n * meaningful message.\n *\n * Use at framework boundaries (route handler catch-all, middleware catch,\n * SDK egress) to ensure every error crossing the wire carries an envelope.\n */\n/**\n * `NotFoundError` — typed 404. A `TheoError` with code `NOT_FOUND`, so it\n * carries a stable `code` (not a bare status literal) and maps to HTTP 404 via\n * `envelopeCodeToStatus`. Throw it from a `defineRoute` handler for ergonomic\n * 404s (M7-1).\n */\nexport class NotFoundError extends TheoError {\n constructor(message = 'Not Found', meta?: Record<string, unknown>) {\n super({ code: 'NOT_FOUND', message, ...(meta !== undefined ? { meta } : {}) })\n }\n}\n\nexport function fromUnknown(value: unknown): TheoError {\n if (value instanceof TheoError) return value\n if (value instanceof Error) {\n return new TheoError({\n code: 'INTERNAL_SERVER_ERROR',\n message: value.message,\n cause: value,\n })\n }\n if (typeof value === 'string') {\n return new TheoError({ code: 'INTERNAL_SERVER_ERROR', message: value })\n }\n if (value === undefined || value === null) {\n return new TheoError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unknown error' })\n }\n // Object / number / boolean / etc. — best-effort stringify\n return new TheoError({\n code: 'INTERNAL_SERVER_ERROR',\n message: `Non-Error value thrown: ${safeStringify(value)}`,\n cause: value,\n })\n}\n\n/**\n * Best-effort stringify of an unknown value for error message context.\n * JSON.stringify first (preserves shape for diagnostics); on cycle/BigInt/symbol\n * failure, signal explicitly instead of falling back to `[object Object]`.\n */\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (typeof value === 'bigint') return `${value.toString()}n`\n if (typeof value === 'symbol') return value.toString()\n try {\n return JSON.stringify(value)\n } catch {\n return '[unstringifiable value]'\n }\n}\n","/**\n * G5 T2.5 — server-side boundary translation per blueprint ADR D3.\n *\n * Translates ad-hoc Error class instances (FileTooLargeError,\n * AuthRequiredError, RouterConventionError, etc.) into the canonical\n * `TheoErrorEnvelope` shape at the wire boundary. This is the SINGLE\n * boundary translation point — class identities stay in place inside the\n * codebase (no invasive call-site rewrites), but the envelope is what\n * crosses to clients.\n *\n * Pure; no I/O. Lives in `core/contracts/` per architecture.md v3\n * invariant #3 (canonical home for shared client↔server types). This file\n * is the EXCEPTION-of-an-exception: it intentionally inspects Error names\n * by string instead of `instanceof` to avoid `core/contracts/` taking a\n * dependency on `server/`. The mapping table is small and grep-discoverable.\n */\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from './error-envelope.js'\nimport { TheoError } from './theo-error.js'\n\n/**\n * Mapping from ad-hoc Error class name to envelope code. Add entries here\n * when a new ad-hoc Error is introduced anywhere in `theokit/server/`.\n * Build-time errors (Duplicate*, ActionScanError, InvalidPluginShapeError)\n * are NOT in this table by design — they never cross the wire boundary,\n * so their canonical shape is `INTERNAL_SERVER_ERROR` via the default branch.\n */\nconst ERROR_NAME_TO_CODE: ReadonlyMap<string, TheoErrorCode> = new Map<string, TheoErrorCode>([\n ['AuthRequiredError', 'UNAUTHORIZED'],\n ['FileTooLargeError', 'PAYLOAD_TOO_LARGE'],\n ['RequestBodyTooLargeError', 'PAYLOAD_TOO_LARGE'],\n ['BodyTooLargeError', 'PAYLOAD_TOO_LARGE'],\n // RouterConventionError reaches the wire only during dev-error overlay;\n // INTERNAL_SERVER_ERROR is appropriate, but we add a HintExt-shaped ext\n // with the migration guidance for the devtools display.\n ['RouterConventionError', 'INTERNAL_SERVER_ERROR'],\n])\n\ntype MetaExtractor = (err: Error) => Record<string, unknown> | null\n\n/**\n * Class-specific metadata extractors. Each function receives the raw Error\n * and returns an object merged into envelope.meta (excluding name/message\n * which are reserved).\n */\nconst META_EXTRACTORS: ReadonlyMap<string, MetaExtractor> = new Map<string, MetaExtractor>([\n [\n 'FileTooLargeError',\n (err) => {\n const e = err as Error & { truncatedFilenames?: string[]; maxFileSize?: number }\n return {\n truncatedFilenames: e.truncatedFilenames ?? [],\n maxFileSize: e.maxFileSize,\n }\n },\n ],\n [\n 'RouterConventionError',\n (err) => {\n const e = err as Error & { file?: string; suggestion?: string; migrationUrl?: string }\n return {\n file: e.file,\n suggestion: e.suggestion,\n migrationUrl: e.migrationUrl,\n }\n },\n ],\n])\n\ntype ExtBuilder = (err: Error) => unknown\n\n/**\n * Class-specific ext (extension slot) builders. RouterConventionError ships a\n * HintExt-shaped `{ hint }` so devtools / dev overlay can render the actionable\n * migration tip directly.\n */\nconst EXT_BUILDERS: ReadonlyMap<string, ExtBuilder> = new Map<string, ExtBuilder>([\n [\n 'RouterConventionError',\n () => ({\n hint: 'Run `theokit migrate router` to convert dotted-basename routes to directory-nested form.',\n }),\n ],\n])\n\n/**\n * Translate any server-side Error instance into a canonical envelope.\n *\n * Pass-through behavior:\n * - `TheoError` instances return their `.envelope` unchanged.\n * - Plain `Error` with unknown name → `INTERNAL_SERVER_ERROR`.\n * - Non-Error values → wrapped via `TheoError.fromUnknown` semantics.\n *\n * Meta carries the source class `name` plus class-specific fields when an\n * extractor is registered. This keeps wire-side telemetry useful without\n * leaking implementation details.\n */\nexport function serverErrorToEnvelope(value: unknown): TheoErrorEnvelope {\n if (value instanceof TheoError) {\n return value.envelope\n }\n if (!(value instanceof Error)) {\n return {\n code: 'INTERNAL_SERVER_ERROR',\n message: typeof value === 'string' ? value : 'Unknown error',\n }\n }\n const name = value.name\n const code = ERROR_NAME_TO_CODE.get(name) ?? 'INTERNAL_SERVER_ERROR'\n const extractor = META_EXTRACTORS.get(name)\n const extra = extractor ? extractor(value) : null\n const meta: Record<string, unknown> = { name }\n if (extra) {\n for (const [k, v] of Object.entries(extra)) {\n if (v !== undefined) meta[k] = v\n }\n }\n const extBuilder = EXT_BUILDERS.get(name)\n const ext = extBuilder ? extBuilder(value) : undefined\n return {\n code,\n message: value.message,\n cause: (value as Error & { cause?: unknown }).cause,\n meta,\n ext,\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,35 @@
1
+ // src/core/contracts/envelope-code-to-status.ts
2
+ var CODE_TO_STATUS = {
3
+ // 4xx — client errors
4
+ BAD_REQUEST: 400,
5
+ UNAUTHORIZED: 401,
6
+ FORBIDDEN: 403,
7
+ NOT_FOUND: 404,
8
+ METHOD_NOT_ALLOWED: 405,
9
+ CONFLICT: 409,
10
+ PRECONDITION_FAILED: 412,
11
+ PAYLOAD_TOO_LARGE: 413,
12
+ UNSUPPORTED_MEDIA_TYPE: 415,
13
+ UNPROCESSABLE_ENTITY: 422,
14
+ TOO_MANY_REQUESTS: 429,
15
+ RATE_LIMITED: 429,
16
+ // 5xx — server errors
17
+ INTERNAL_SERVER_ERROR: 500,
18
+ NOT_IMPLEMENTED: 501,
19
+ BAD_GATEWAY: 502,
20
+ SERVICE_UNAVAILABLE: 503,
21
+ GATEWAY_TIMEOUT: 504,
22
+ // SDK-domain codes — intentionally 500
23
+ AGENT_RUN_ERROR: 500,
24
+ PROVIDER_KEY_MISSING: 500,
25
+ BUDGET_EXCEEDED: 500,
26
+ CREDENTIAL_POOL_EXHAUSTED: 500
27
+ };
28
+ function envelopeCodeToStatus(code) {
29
+ return CODE_TO_STATUS[code] ?? 500;
30
+ }
31
+
32
+ export {
33
+ envelopeCodeToStatus
34
+ };
35
+ //# sourceMappingURL=chunk-X32XEJXR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts/envelope-code-to-status.ts"],"sourcesContent":["/**\n * Maps a TheoErrorEnvelope `code` to its canonical HTTP status.\n *\n * Single source of truth — previously duplicated in `web-handler.ts` and\n * `handle-request-error.ts` (architecture-remediation plan T1.2, 2026-06-12).\n *\n * Every TheoErrorCode value MUST have an explicit entry here. SDK-domain codes\n * (AGENT_RUN_ERROR, PROVIDER_KEY_MISSING, BUDGET_EXCEEDED, CREDENTIAL_POOL_EXHAUSTED)\n * intentionally map to 500 — they represent internal failures, not client errors.\n */\n\nconst CODE_TO_STATUS: Record<string, number> = {\n // 4xx — client errors\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n CONFLICT: 409,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n UNSUPPORTED_MEDIA_TYPE: 415,\n UNPROCESSABLE_ENTITY: 422,\n TOO_MANY_REQUESTS: 429,\n RATE_LIMITED: 429,\n\n // 5xx — server errors\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n\n // SDK-domain codes — intentionally 500\n AGENT_RUN_ERROR: 500,\n PROVIDER_KEY_MISSING: 500,\n BUDGET_EXCEEDED: 500,\n CREDENTIAL_POOL_EXHAUSTED: 500,\n}\n\nexport function envelopeCodeToStatus(code: string): number {\n return CODE_TO_STATUS[code] ?? 500\n}\n"],"mappings":";AAWA,IAAM,iBAAyC;AAAA;AAAA,EAE7C,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,cAAc;AAAA;AAAA,EAGd,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,iBAAiB;AAAA;AAAA,EAGjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,2BAA2B;AAC7B;AAEO,SAAS,qBAAqB,MAAsB;AACzD,SAAO,eAAe,IAAI,KAAK;AACjC;","names":[]}
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import "tsx/esm";
3
+ import {
4
+ serverErrorToEnvelope
5
+ } from "./chunk-WS67ONGI.js";
3
6
 
4
7
  // src/server/plugins/plugin-runner.ts
5
8
  var DuplicatePluginError = class extends Error {
@@ -1535,6 +1538,37 @@ function coerceBoolean(formData, key) {
1535
1538
  return Boolean(val);
1536
1539
  }
1537
1540
 
1541
+ // src/core/contracts/envelope-code-to-status.ts
1542
+ var CODE_TO_STATUS2 = {
1543
+ // 4xx — client errors
1544
+ BAD_REQUEST: 400,
1545
+ UNAUTHORIZED: 401,
1546
+ FORBIDDEN: 403,
1547
+ NOT_FOUND: 404,
1548
+ METHOD_NOT_ALLOWED: 405,
1549
+ CONFLICT: 409,
1550
+ PRECONDITION_FAILED: 412,
1551
+ PAYLOAD_TOO_LARGE: 413,
1552
+ UNSUPPORTED_MEDIA_TYPE: 415,
1553
+ UNPROCESSABLE_ENTITY: 422,
1554
+ TOO_MANY_REQUESTS: 429,
1555
+ RATE_LIMITED: 429,
1556
+ // 5xx — server errors
1557
+ INTERNAL_SERVER_ERROR: 500,
1558
+ NOT_IMPLEMENTED: 501,
1559
+ BAD_GATEWAY: 502,
1560
+ SERVICE_UNAVAILABLE: 503,
1561
+ GATEWAY_TIMEOUT: 504,
1562
+ // SDK-domain codes — intentionally 500
1563
+ AGENT_RUN_ERROR: 500,
1564
+ PROVIDER_KEY_MISSING: 500,
1565
+ BUDGET_EXCEEDED: 500,
1566
+ CREDENTIAL_POOL_EXHAUSTED: 500
1567
+ };
1568
+ function envelopeCodeToStatus(code) {
1569
+ return CODE_TO_STATUS2[code] ?? 500;
1570
+ }
1571
+
1538
1572
  // src/server/http/handle-request-error.ts
1539
1573
  async function handleRequestError(err, c) {
1540
1574
  if (c.pluginRunner) {
@@ -1558,14 +1592,26 @@ async function handleRequestError(err, c) {
1558
1592
  const authErr = err;
1559
1593
  sendError(c.res, authErr.code, authErr.message, authErr.status, void 0, c.requestId);
1560
1594
  } else {
1561
- sendError(
1562
- c.res,
1563
- "INTERNAL_ERROR",
1564
- err instanceof Error ? err.message : "Internal server error",
1565
- 500,
1566
- void 0,
1567
- c.requestId
1568
- );
1595
+ const envelope = serverErrorToEnvelope(err);
1596
+ if (envelope.code === "INTERNAL_SERVER_ERROR") {
1597
+ sendError(
1598
+ c.res,
1599
+ "INTERNAL_ERROR",
1600
+ err instanceof Error ? err.message : "Internal server error",
1601
+ 500,
1602
+ void 0,
1603
+ c.requestId
1604
+ );
1605
+ } else {
1606
+ sendError(
1607
+ c.res,
1608
+ envelope.code,
1609
+ envelope.message,
1610
+ envelopeCodeToStatus(envelope.code),
1611
+ void 0,
1612
+ c.requestId
1613
+ );
1614
+ }
1569
1615
  }
1570
1616
  if (c.pluginRunner) {
1571
1617
  const errCtxObj = {};
@@ -2036,4 +2082,4 @@ export {
2036
2082
  applySecurityHeaders,
2037
2083
  generateNonce
2038
2084
  };
2039
- //# sourceMappingURL=chunk-MV4LKTW6.js.map
2085
+ //# sourceMappingURL=chunk-Z374WCMZ.js.map