theokit 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-3CDQTWOC.js → actions-virtual-module-G4BANOLW.js} +5 -3
- package/dist/{actions-virtual-module-3CDQTWOC.js.map → actions-virtual-module-G4BANOLW.js.map} +1 -1
- package/dist/{actions-virtual-module-EIPXX4ZB.js → actions-virtual-module-RM7JRY5N.js} +7 -7
- package/dist/agent-MN7XGJR3.js +209 -0
- package/dist/agent-MN7XGJR3.js.map +1 -0
- package/dist/{app-typed-client-7PBFWZUE.js → app-typed-client-5VQE6DNF.js} +7 -7
- package/dist/{app-typed-client-CSOK7NPC.js → app-typed-client-Z6BHD4MF.js} +5 -3
- package/dist/{app-typed-client-CSOK7NPC.js.map → app-typed-client-Z6BHD4MF.js.map} +1 -1
- package/dist/boot/index.js +3 -3
- package/dist/{build-HXND27XG.js → build-QDAFSKKW.js} +11 -7
- package/dist/{build-HXND27XG.js.map → build-QDAFSKKW.js.map} +1 -1
- package/dist/chunk-34YQOXGM.js +37 -0
- package/dist/chunk-34YQOXGM.js.map +1 -0
- package/dist/{chunk-7YZHAQU7.js → chunk-567NA7Y6.js} +8 -99
- package/dist/chunk-567NA7Y6.js.map +1 -0
- package/dist/{chunk-3S3BNW5K.js → chunk-5UUOGAJH.js} +2 -2
- package/dist/{chunk-BQDGES7C.js → chunk-GBXLKYIA.js} +19 -43
- package/dist/chunk-GBXLKYIA.js.map +1 -0
- package/dist/chunk-GDN3PXFH.js +101 -0
- package/dist/chunk-GDN3PXFH.js.map +1 -0
- package/dist/{chunk-TGTNRUH3.js → chunk-H243MR3F.js} +2 -2
- package/dist/{chunk-NHJMZCAS.js → chunk-JZHT3LW7.js} +1 -1
- package/dist/chunk-JZHT3LW7.js.map +1 -0
- package/dist/chunk-KGFNWIMS.js +94 -0
- package/dist/chunk-KGFNWIMS.js.map +1 -0
- package/dist/{chunk-EXP56GFQ.js → chunk-NBWB4S46.js} +38 -9
- package/dist/chunk-NBWB4S46.js.map +1 -0
- package/dist/{chunk-F4YUPDJ2.js → chunk-NXTF5PPW.js} +15 -41
- package/dist/chunk-NXTF5PPW.js.map +1 -0
- package/dist/{chunk-UNLA45FY.js → chunk-OTFIRP6S.js} +76 -46
- package/dist/chunk-OTFIRP6S.js.map +1 -0
- package/dist/chunk-P37RZRFV.js +31 -0
- package/dist/chunk-P37RZRFV.js.map +1 -0
- package/dist/{chunk-RYTZYFSD.js → chunk-UOR6JTCI.js} +205 -33
- package/dist/chunk-UOR6JTCI.js.map +1 -0
- package/dist/{chunk-WFNLNIJX.js → chunk-Z5IGLBWE.js} +1 -1
- package/dist/{chunk-O62MW4MT.js → chunk-ZJDKAD3L.js} +55 -14
- package/dist/chunk-ZJDKAD3L.js.map +1 -0
- package/dist/cli/index.js +22 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +3 -152
- package/dist/client/index.js +13 -236
- package/dist/client/index.js.map +1 -1
- package/dist/{dev-OWW4XVIH.js → dev-TEE4T6ZB.js} +13 -8
- package/dist/{dev-OWW4XVIH.js.map → dev-TEE4T6ZB.js.map} +1 -1
- package/dist/{dev-emit-5MDSBP5D.js → dev-emit-VJ5CFMPY.js} +5 -3
- package/dist/{dev-emit-5MDSBP5D.js.map → dev-emit-VJ5CFMPY.js.map} +1 -1
- package/dist/index.js +12 -13
- package/dist/index.js.map +1 -1
- package/dist/{info-OUEUZOT7.js → info-7PE2PZJI.js} +6 -5
- package/dist/{info-OUEUZOT7.js.map → info-7PE2PZJI.js.map} +1 -1
- package/dist/{internal-api-4YTJDITC.js → internal-api-5BDI6N5U.js} +20 -20
- package/dist/{internal-api-EFKZWIYZ.js → internal-api-J27TYE2I.js} +7 -4
- package/dist/load-config-JKYO5RFK.js +14 -0
- package/dist/{openapi-FHY6HC6I.js → openapi-MXMLZCXC.js} +7 -4
- package/dist/{openapi-FHY6HC6I.js.map → openapi-MXMLZCXC.js.map} +1 -1
- package/dist/{registry-34LL7NF4.js → registry-XJUYD2OU.js} +2 -2
- package/dist/{routes-EW7TP7NJ.js → routes-NNBEZSGN.js} +5 -3
- package/dist/{routes-EW7TP7NJ.js.map → routes-NNBEZSGN.js.map} +1 -1
- package/dist/server/auth/index.js +1 -1
- package/dist/server/define/index.d.ts +3 -70
- package/dist/server/define/index.js +2 -4
- package/dist/server/http/index.js +2 -2
- package/dist/server/index.d.ts +1 -3
- package/dist/server/index.js +90 -105
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +4 -4
- package/dist/server/observability/index.js +1 -1
- package/dist/server/scan/index.js +1 -1
- package/dist/{start-KIQ5TTLR.js → start-7MQEEQH4.js} +68 -8
- package/dist/start-7MQEEQH4.js.map +1 -0
- package/dist/{static-55G3LX2I.js → static-7ARBVDJF.js} +4 -4
- package/dist/vite-plugin/index.js +12 -13
- package/dist/{vite-plugin-RK66K26Z.js → vite-plugin-GC6WCU4P.js} +11 -7
- package/package.json +2 -6
- package/dist/agent-events-DosDXkSV.d.ts +0 -94
- package/dist/chunk-2KZQPDYR.js +0 -226
- package/dist/chunk-2KZQPDYR.js.map +0 -1
- package/dist/chunk-7YZHAQU7.js.map +0 -1
- package/dist/chunk-BQDGES7C.js.map +0 -1
- package/dist/chunk-EXP56GFQ.js.map +0 -1
- package/dist/chunk-F4YUPDJ2.js.map +0 -1
- package/dist/chunk-NHJMZCAS.js.map +0 -1
- package/dist/chunk-O62MW4MT.js.map +0 -1
- package/dist/chunk-RYTZYFSD.js.map +0 -1
- package/dist/chunk-UNLA45FY.js.map +0 -1
- package/dist/chunk-ZSTZXR2D.js +0 -208
- package/dist/chunk-ZSTZXR2D.js.map +0 -1
- package/dist/server/agent/index.d.ts +0 -229
- package/dist/server/agent/index.js +0 -17
- package/dist/start-KIQ5TTLR.js.map +0 -1
- /package/dist/{actions-virtual-module-EIPXX4ZB.js.map → actions-virtual-module-RM7JRY5N.js.map} +0 -0
- /package/dist/{app-typed-client-7PBFWZUE.js.map → app-typed-client-5VQE6DNF.js.map} +0 -0
- /package/dist/{chunk-3S3BNW5K.js.map → chunk-5UUOGAJH.js.map} +0 -0
- /package/dist/{chunk-TGTNRUH3.js.map → chunk-H243MR3F.js.map} +0 -0
- /package/dist/{chunk-WFNLNIJX.js.map → chunk-Z5IGLBWE.js.map} +0 -0
- /package/dist/{internal-api-4YTJDITC.js.map → internal-api-5BDI6N5U.js.map} +0 -0
- /package/dist/{internal-api-EFKZWIYZ.js.map → internal-api-J27TYE2I.js.map} +0 -0
- /package/dist/{server/agent/index.js.map → load-config-JKYO5RFK.js.map} +0 -0
- /package/dist/{registry-34LL7NF4.js.map → registry-XJUYD2OU.js.map} +0 -0
- /package/dist/{static-55G3LX2I.js.map → static-7ARBVDJF.js.map} +0 -0
- /package/dist/{vite-plugin-RK66K26Z.js.map → vite-plugin-GC6WCU4P.js.map} +0 -0
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/theo-fetch.ts","../../src/client/batch.ts","../../src/client/batch-transport.ts","../../src/client/app-client.ts","../../src/client/use-agent-stream.ts","../../src/client/agent-stream-core.ts","../../src/client/agent-tool-cards.ts","../../src/client/use-agent-tool-cards.ts","../../src/client/consume-ui-message-stream.ts","../../src/client/use-agent.ts","../../src/client/link.tsx","../../src/client/metadata.tsx","../../src/client/image.tsx"],"sourcesContent":["import superjson from 'superjson'\nimport type { z } from 'zod'\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from '../core/contracts/error-envelope.js'\n\nimport { getGlobalBatcher } from './batch-transport.js'\n\n// --- Transformer (T1.3) ---\n\n/**\n * Module-scoped flag preventing the transformer-mismatch warning from\n * firing more than once per session (EC-6).\n */\nlet mismatchWarned = false\n\n/** Test-only helper to reset the warned flag between assertions. */\nexport function __resetMismatchWarningForTests(): void {\n mismatchWarned = false\n}\n\n/**\n * Deserialize a fetch response body according to the negotiated transformer.\n *\n * `serverTransformerName` comes from the `x-theo-transformer` response header\n * (null when absent — server is using the default JSON path).\n * `clientTransformerName` is the transformer the client was built with\n * (typically injected via a Vite virtual module; falls back to `'json'`).\n *\n * Mismatch fires a single console.warn and falls back to JSON.parse (EC-5/EC-6).\n */\nexport function deserializeFetchResponse(\n raw: string,\n serverTransformerName: string | null,\n clientTransformerName: string,\n): unknown {\n // `raw` is typed `string`, but stay defensive: callers pass `await response.text()`\n // which can be empty.\n if (raw === '') {\n return null\n }\n\n const serverEffective = serverTransformerName ?? 'json'\n if (serverEffective !== clientTransformerName && !mismatchWarned) {\n mismatchWarned = true\n console.warn(\n `[theokit] transformer mismatch: server=${serverEffective}, client=${clientTransformerName}. Falling back to JSON.parse.`,\n )\n }\n\n if (serverEffective === 'superjson' && clientTransformerName === 'superjson') {\n const wrapped = JSON.parse(raw) as Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize(wrapped)\n }\n\n // Default path (json) or mismatch fallback\n return JSON.parse(raw)\n}\n\n// --- Utility Types ---\n\n/** Infer the response type from a route's handler return */\nexport type InferResponse<T> = T extends { handler: (...args: never[]) => infer R }\n ? Awaited<R>\n : unknown\n\n/** Extract the query Zod schema type, handling optional properties */\ntype ExtractQuery<T> = T extends { query?: infer Q } ? (Q extends z.ZodType ? Q : never) : never\n\n/** Extract the body Zod schema type, handling optional properties */\ntype ExtractBody<T> = T extends { body?: infer B } ? (B extends z.ZodType ? B : never) : never\n\n/** Infer query type from a route's query Zod schema */\nexport type InferQuery<T> = [ExtractQuery<T>] extends [never]\n ? undefined\n : ExtractQuery<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractQuery<T>>\n\n/** Infer body type from a route's body Zod schema */\nexport type InferBody<T> = [ExtractBody<T>] extends [never]\n ? undefined\n : ExtractBody<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractBody<T>>\n\n/** Build the options type based on what schemas the route has */\nexport type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> &\n (InferQuery<T> extends undefined ? { query?: never } : { query: InferQuery<T> }) &\n (InferBody<T> extends undefined ? { body?: never } : { body: InferBody<T> })\n\n// --- Error Class ---\n\n/**\n * G5 T2.1 — extract a TheoErrorEnvelope from a response body. Supports both\n * the new envelope-at-root shape (`{ code, message, ext?, ... }`, blueprint\n * Recommendations § concrete shape) and the legacy nested shape\n * (`{ error: { code, message, issues? } }`, G3 SerializedActionResult).\n *\n * Falls back to `INTERNAL_SERVER_ERROR` with a synthetic message when the\n * body shape is unrecognized. Pure; no I/O.\n */\nfunction extractEnvelope(status: number, body: unknown): TheoErrorEnvelope {\n if (!body || typeof body !== 'object') {\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n // New shape — envelope at root\n if (typeof obj.code === 'string' && typeof obj.message === 'string') {\n return {\n code: obj.code as TheoErrorCode,\n message: obj.message,\n cause: obj.cause,\n meta: obj.meta as Record<string, unknown> | undefined,\n ext: obj.ext,\n }\n }\n // Legacy shape — { error: { code, message, ... } }\n if (obj.error && typeof obj.error === 'object') {\n const nested = obj.error as Record<string, unknown>\n return {\n code:\n typeof nested.code === 'string' ? (nested.code as TheoErrorCode) : 'INTERNAL_SERVER_ERROR',\n message: typeof nested.message === 'string' ? nested.message : `HTTP ${String(status)}`,\n }\n }\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n}\n\n/**\n * Pull a single string field from an object, returning undefined if missing\n * or non-string. Helper for `extractLegacyFields` to stay under the\n * complexity ceiling.\n */\nfunction strField(obj: Record<string, unknown> | null, key: string): string | undefined {\n const v = obj?.[key]\n return typeof v === 'string' ? v : undefined\n}\n\n/**\n * Pull the canonical {message, code, issues} from either an envelope-at-root\n * body or the legacy { error: {...} } body. Pure.\n */\nfunction extractLegacyFields(\n status: number,\n body: unknown,\n): { message: string; code?: string; issues?: unknown[] } {\n if (!body || typeof body !== 'object') {\n return { message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n const nested =\n obj.error && typeof obj.error === 'object' ? (obj.error as Record<string, unknown>) : null\n const issues = Array.isArray(nested?.issues) ? nested.issues : undefined\n return {\n message: strField(obj, 'message') ?? strField(nested, 'message') ?? `HTTP ${String(status)}`,\n code: strField(obj, 'code') ?? strField(nested, 'code'),\n issues,\n }\n}\n\nexport class TheoFetchError extends Error {\n status: number\n code?: string\n issues?: unknown[]\n /**\n * G5 T2.1 — canonical envelope view of the server-side error. Use this in\n * consumer code that wants to switch on a typed TheoErrorCode instead of\n * coupling to the legacy `.status` / `.code` flat fields.\n */\n readonly envelope: TheoErrorEnvelope\n\n constructor(status: number, body?: unknown) {\n const legacy = extractLegacyFields(status, body)\n super(legacy.message)\n this.name = 'TheoFetchError'\n this.status = status\n this.code = legacy.code\n this.issues = legacy.issues\n this.envelope = extractEnvelope(status, body)\n }\n}\n\n/**\n * Serialize a query-param value to a string. Handles primitives, dates,\n * and falls back to JSON for arrays/objects. Avoids the\n * `[object Object]` foot-gun that `no-base-to-string` warns against.\n */\nfunction stringifyQueryValue(value: unknown): string {\n if (value === null) return 'null'\n if (value instanceof Date) return value.toISOString()\n switch (typeof value) {\n case 'string':\n return value\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// --- Main Function ---\n\ninterface TheoFetchInternalOptions {\n method?: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\n/**\n * Resolve the request origin per the documented fallback hierarchy. Pure\n * helper extracted to keep `theoFetch` under the complexity ceiling.\n * 1. `globalThis.location.origin` (browser)\n * 2. `globalThis.__THEO_ORIGIN__` (build-time literal)\n * 3. `process.env.THEO_ORIGIN` (escape hatch)\n * 4. `http://localhost` (placeholder for URL parsing — the URL is built\n * relative so only pathname+search ever flows to the wire)\n */\nfunction resolveRequestOrigin(): string {\n const g = globalThis as { location?: { origin?: string }; __THEO_ORIGIN__?: string }\n const fromEnv = typeof process !== 'undefined' ? process.env.THEO_ORIGIN : undefined\n return g.location?.origin ?? g.__THEO_ORIGIN__ ?? fromEnv ?? 'http://localhost'\n}\n\nfunction buildFetchUrl(path: string, query: Record<string, unknown> | undefined): URL {\n const fetchUrl = new URL(path, resolveRequestOrigin())\n if (!query) return fetchUrl\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) {\n fetchUrl.searchParams.set(k, stringifyQueryValue(v))\n }\n }\n return fetchUrl\n}\n\n/**\n * Normalize `HeadersInit` (Headers / `[string, string][]` / Record) into a\n * single plain `Record<string, string>`. Plain-object output is required\n * by callers that introspect headers via index access (test fixtures use\n * `(init.headers as Record<string, string>)['Content-Type']`).\n */\nfunction normalizeHeaders(input: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {}\n if (!input) return out\n if (input instanceof Headers) {\n input.forEach((value, key) => {\n out[key] = value\n })\n return out\n }\n if (Array.isArray(input)) {\n for (const [key, value] of input) out[key] = value\n return out\n }\n for (const [key, value] of Object.entries(input)) {\n out[key] = value\n }\n return out\n}\n\nfunction buildRequestInit(opts: TheoFetchInternalOptions): RequestInit {\n const init: RequestInit = {}\n if (opts.method !== undefined) init.method = opts.method\n if (opts.signal !== undefined) init.signal = opts.signal\n\n const headers = normalizeHeaders(opts.headers)\n init.headers = headers\n\n if (opts.body !== undefined) {\n init.body = JSON.stringify(opts.body)\n headers['Content-Type'] = 'application/json'\n }\n\n // Phase 5 — Auto-attach `X-Theo-Action: 1` for state-mutating methods so\n // the framework's CSRF check passes when servers run in `strict` mode.\n // Safe methods (GET/HEAD/OPTIONS) skip the header to keep them cacheable.\n const method = (opts.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {\n headers['X-Theo-Action'] = '1'\n }\n return init\n}\n\nasync function tryBatcher(\n url: string,\n options: TheoFetchInternalOptions,\n): Promise<{ matched: true; result: unknown } | { matched: false }> {\n const batcher = getGlobalBatcher()\n if (!batcher) return { matched: false }\n try {\n const result = await batcher.dispatch({\n path: url,\n method: options.method ?? 'GET',\n query: options.query,\n body: options.body,\n })\n return { matched: true, result }\n } catch {\n // Batcher failure: fall through to direct fetch (graceful degrade).\n return { matched: false }\n }\n}\n\nexport async function theoFetch<T>(\n url: string,\n options?: TheoFetchOptions<T>,\n): Promise<InferResponse<T>> {\n const internal = (options ?? {}) as unknown as TheoFetchInternalOptions\n\n // T1.5 — Transparent batching when globalThis.__THEO_BATCHING__ is truthy.\n const batchAttempt = await tryBatcher(url, internal)\n if (batchAttempt.matched) {\n return batchAttempt.result as InferResponse<T>\n }\n\n const fetchUrl = buildFetchUrl(url, internal.query)\n const init = buildRequestInit(internal)\n const response = await fetch(fetchUrl.toString(), init)\n\n if (!response.ok) {\n let errorBody: unknown\n try {\n errorBody = await response.json()\n } catch {\n // Non-JSON error response\n }\n throw new TheoFetchError(response.status, errorBody)\n }\n\n // Handle 204 No Content (EC-1)\n if (response.status === 204) {\n return null as InferResponse<T>\n }\n\n // Check for empty body\n const contentLength = response.headers.get('content-length')\n if (contentLength === '0') {\n return null as InferResponse<T>\n }\n\n // T1.3 — transformer-aware deserialization\n const serverTransformerName = response.headers.get('x-theo-transformer')\n const clientTransformerName = resolveClientTransformerName()\n const text = await response.text()\n return deserializeFetchResponse(\n text,\n serverTransformerName,\n clientTransformerName,\n ) as InferResponse<T>\n}\n\n/**\n * Read the client-configured transformer name. Default `'json'`.\n *\n * In a Vite build, this is overridden by virtual module\n * `/@theo/runtime-config` which sets `globalThis.__THEO_TRANSFORMER__`.\n * Outside Vite (Node SSR, tests) the default applies.\n */\nfunction resolveClientTransformerName(): string {\n const g = globalThis as { __THEO_TRANSFORMER__?: string }\n return g.__THEO_TRANSFORMER__ ?? 'json'\n}\n","/**\n * T5.1 — client-side microtask batching.\n *\n * Collects all `dispatch` calls within the same microtask and sends them as a\n * single HTTP POST to the configured transport. Each caller's promise resolves\n * with its own result; one failed item does not break the others (per-item\n * error isolation).\n *\n * Designed as a transport-agnostic primitive so unit tests do not require\n * network access. The default transport (fetch to `/api/__theo_batch__`)\n * lives alongside this module but is created by the consumer (e.g., theoFetch).\n */\n\nexport interface BatchRequest {\n path: string\n method: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: Record<string, string>\n}\n\nexport type BatchResponse =\n | { index: number; data: unknown }\n | { index: number; error: { message: string; code?: string } }\n\nexport type BatchTransport = (requests: BatchRequest[]) => Promise<BatchResponse[]>\n\nexport interface BatcherOptions {\n transport: BatchTransport\n /** Maximum batch size before flushing into multiple parallel batches. */\n max?: number\n}\n\nexport interface Batcher {\n dispatch(req: BatchRequest): Promise<unknown>\n}\n\ninterface PendingCall {\n req: BatchRequest\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\nexport function createBatcher(options: BatcherOptions): Batcher {\n const max = options.max ?? 32\n let queue: PendingCall[] = []\n let flushScheduled = false\n\n function flush(): void {\n flushScheduled = false\n const current = queue\n queue = []\n if (current.length === 0) return\n\n // Split into chunks respecting max\n const chunks: PendingCall[][] = []\n for (let i = 0; i < current.length; i += max) {\n chunks.push(current.slice(i, i + max))\n }\n\n for (const chunk of chunks) {\n const payload: BatchRequest[] = chunk.map((p) => p.req)\n options\n .transport(payload)\n .then((results) => {\n for (let i = 0; i < chunk.length; i++) {\n // `results[i]` could be undefined at runtime when the transport\n // returns fewer entries than the chunk; TypeScript's strict\n // typing without `noUncheckedIndexedAccess` does not surface\n // this, so we hand-narrow.\n const result = results.length > i ? results[i] : undefined\n if (result === undefined) {\n chunk[i].reject(new Error(`Batch transport returned no result for index ${i}`))\n continue\n }\n if ('error' in result) {\n const err = new Error(result.error.message)\n ;(err as { code?: string }).code = result.error.code\n chunk[i].reject(err)\n } else {\n chunk[i].resolve(result.data)\n }\n }\n })\n .catch((err: unknown) => {\n for (const item of chunk) item.reject(err)\n })\n }\n }\n\n return {\n dispatch(req: BatchRequest): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n queue.push({ req, resolve, reject })\n if (!flushScheduled) {\n flushScheduled = true\n queueMicrotask(flush)\n }\n })\n },\n }\n}\n","/**\n * T1.5 — Default HTTP transport for the client batcher.\n *\n * Wraps `fetch` to POST `/api/__theo_batch__` with the collected requests\n * and return the array of per-item results.\n */\n\nimport {\n createBatcher,\n type BatchTransport,\n type Batcher,\n type BatchRequest,\n type BatchResponse,\n} from './batch.js'\n\nconst BATCH_ENDPOINT = '/api/__theo_batch__'\n\nexport interface CreateBatchTransportOptions {\n /** Override fetch (default: globalThis.fetch). Used by tests. */\n fetchImpl?: typeof fetch\n /** Override endpoint (default '/api/__theo_batch__'). */\n endpoint?: string\n}\n\nexport function createBatchTransport(options: CreateBatchTransportOptions = {}): BatchTransport {\n const fetchImpl = options.fetchImpl ?? fetch\n const endpoint = options.endpoint ?? BATCH_ENDPOINT\n return async (requests: BatchRequest[]): Promise<BatchResponse[]> => {\n const response = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ requests }),\n })\n if (!response.ok) {\n throw new Error(`Batch transport returned ${response.status}`)\n }\n const payload = (await response.json()) as { results?: BatchResponse[] }\n if (!Array.isArray(payload.results)) {\n throw new Error('Batch response missing results array')\n }\n // Map results to indexed BatchResponse shape (server already returns the right form)\n return payload.results.map((item, index) => {\n if ('error' in item) {\n return { index, error: (item as { error: { message: string; code?: string } }).error }\n }\n return { index, data: (item as { data: unknown }).data }\n })\n }\n}\n\n// --- EC-7: singleton batcher per page, lazy-instantiated ---\n\nlet globalBatcher: Batcher | undefined\n\n/** Test-only — reset the module-scope singleton between assertions. */\nexport function __resetGlobalBatcherForTests(): void {\n globalBatcher = undefined\n}\n\n/**\n * Returns the global batcher singleton when `globalThis.__THEO_BATCHING__` is\n * truthy; undefined otherwise. Lazy-instantiated on first call.\n */\nexport function getGlobalBatcher(): Batcher | undefined {\n const g = globalThis as { __THEO_BATCHING__?: boolean }\n if (!g.__THEO_BATCHING__) return undefined\n globalBatcher ??= createBatcher({ transport: createBatchTransport() })\n return globalBatcher\n}\n","/**\n * Phase 3 of G1 (g1-client-codegen-plan.md):\n *\n * `createAppClient(baseUrl?, fetchImpl?)` returns a Proxy that walks property\n * access (`client.posts.id.get(...)`) into a `theoFetch` invocation against\n * `{baseUrl}/posts/{params.id}` with method `GET`.\n *\n * Type safety is delivered by the `.d.ts` file emitted in Phase 2 — this\n * runtime is structurally untyped. Power users may use `theoFetch` directly\n * when they need to escape the Proxy facade.\n *\n * Edge cases absorbed:\n * - EC-1 (thenable trap): `then` / `catch` / `finally` / `toJSON` / `Symbol.*`\n * return `undefined` so that accidental `await client.posts` resolves to a\n * plain Proxy value (it is NOT thenable) instead of looping.\n * - EC-7 (abort signal): `opts.signal` is spread through to the underlying\n * fetchImpl unchanged — verified in unit tests.\n *\n * Bundle target: ≤ 2KB gzipped.\n */\n\nimport { theoFetch, TheoFetchError } from './theo-fetch.js'\nimport type { TheoFetchOptions } from './theo-fetch.js'\nimport { HTTP_METHOD_LOWERCASE } from '../core/contracts/http-methods.js'\n\nconst HTTP_METHODS_SET = new Set(HTTP_METHOD_LOWERCASE)\n\n/**\n * Keys that JavaScript runtime / inspectors / Promise resolution probe\n * unconditionally. Returning `undefined` for these keeps the Proxy from\n * pretending to be a thenable / iterable / serializable thing.\n */\nconst NON_INTERCEPT_KEYS = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'toString',\n 'valueOf',\n 'constructor',\n 'prototype',\n])\n\ntype FetchImpl = typeof theoFetch\n\ninterface CallOptions {\n params?: Record<string, string | number>\n query?: Record<string, unknown>\n body?: unknown\n signal?: AbortSignal\n headers?: HeadersInit\n // arbitrary RequestInit fields (mode, credentials, cache, etc.)\n [k: string]: unknown\n}\n\nfunction injectParams(path: string, params?: Record<string, string | number>): string {\n if (!params) {\n if (/:(?:\\.\\.\\.)?[A-Za-z_]/.test(path)) {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Route ${path} requires params but none were provided. Pass them via opts.params, e.g. client.posts.get({ params: { id: '123' } }).`,\n },\n })\n }\n return path\n }\n return path.replace(/:(?:\\.\\.\\.)?([A-Za-z_][A-Za-z0-9_]*)/g, (_match, key: string) => {\n const value = params[key]\n if (value === undefined || value === null || value === '') {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Param '${key}' is required for path '${path}' but was missing/empty.`,\n },\n })\n }\n return encodeURIComponent(String(value))\n })\n}\n\ninterface ProxyContext {\n segments: string[]\n baseUrl: string\n fetchImpl: FetchImpl\n}\n\nfunction makeProxy(ctx: ProxyContext): unknown {\n const handler: ProxyHandler<() => void> = {\n get(_target, key) {\n if (typeof key !== 'string') return undefined\n if (NON_INTERCEPT_KEYS.has(key)) return undefined\n if (HTTP_METHODS_SET.has(key)) {\n return async (opts?: CallOptions) => {\n if (ctx.segments.length === 0) {\n throw new TheoFetchError(0, {\n error: {\n code: 'INVALID_PATH',\n message: `client.${key}() called without a route segment. Use client.<resource>.${key}(...) instead.`,\n },\n })\n }\n let finalPath = `${ctx.baseUrl}/${ctx.segments.join('/')}`\n if (opts?.params) {\n // Build a path with `:name` placeholders re-inserted from segments\n // whose name matches a key in opts.params (treat as dynamic).\n const segmentsWithDynamic = ctx.segments.map((seg) =>\n Object.prototype.hasOwnProperty.call(opts.params, seg) ? `:${seg}` : seg,\n )\n finalPath = injectParams(\n `${ctx.baseUrl}/${segmentsWithDynamic.join('/')}`,\n opts.params,\n )\n }\n const { params: _ignored, ...rest } = opts ?? {}\n const init = { method: key.toUpperCase(), ...rest } as unknown as TheoFetchOptions<unknown>\n return ctx.fetchImpl(finalPath, init)\n }\n }\n // Continue traversal — append segment to the path.\n return makeProxy({ ...ctx, segments: [...ctx.segments, key] })\n },\n apply() {\n return Promise.reject(\n new TheoFetchError(0, {\n error: {\n code: 'INVALID_CALL',\n message:\n 'client(...) is not callable. Use client.<resource>.<method>(opts?) — e.g. client.posts.get().',\n },\n }),\n )\n },\n }\n return new Proxy(() => undefined, handler)\n}\n\nexport interface CreateAppClientOptions {\n /** Base URL for the API. Defaults to `/api`. */\n baseUrl?: string\n /** Test-only fetch override. Production callers should not pass this. */\n fetchImpl?: FetchImpl\n}\n\nexport function createAppClient<TAppClient = unknown>(\n baseUrlOrOptions?: string | CreateAppClientOptions,\n legacyFetchImpl?: FetchImpl,\n): TAppClient {\n let baseUrl = '/api'\n let fetchImpl: FetchImpl = theoFetch\n if (typeof baseUrlOrOptions === 'string') {\n baseUrl = baseUrlOrOptions || '/api'\n } else if (baseUrlOrOptions && typeof baseUrlOrOptions === 'object') {\n if (baseUrlOrOptions.baseUrl) baseUrl = baseUrlOrOptions.baseUrl\n if (baseUrlOrOptions.fetchImpl) fetchImpl = baseUrlOrOptions.fetchImpl\n }\n // Legacy 2nd-arg fetchImpl overrides regardless of 1st-arg shape (test seam).\n if (legacyFetchImpl) fetchImpl = legacyFetchImpl\n // Strip trailing slash for predictable joining.\n if (baseUrl.length > 1 && baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1)\n return makeProxy({ segments: [], baseUrl, fetchImpl }) as TAppClient\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { AgentErrorEvent, AgentEvent } from '../core/contracts/agent-events.js'\n\nimport { consumeAgentStream } from './agent-stream-core.js'\n\n/**\n * Accumulated assistant text (M5-1): the concatenation of every `message`\n * event's `content`. Pure. Assumes append/delta semantics — a server that\n * emits full-snapshot messages should read `events` directly.\n */\nexport function deriveLiveText(events: readonly AgentEvent[]): string {\n let text = ''\n for (const event of events) {\n if (event.type === 'message') text += event.content\n }\n return text\n}\n\n/**\n * The current error (M5-1): the last `error` event, or `undefined`. Surfaces\n * the full `AgentErrorEvent` (with `code`/`retriable`) so the UI can branch.\n * Pure.\n */\nexport function deriveError(events: readonly AgentEvent[]): AgentErrorEvent | undefined {\n let last: AgentErrorEvent | undefined\n for (const event of events) {\n if (event.type === 'error') last = event\n }\n return last\n}\n\n/**\n * T5.2 — useAgentStream\n *\n * React hook to consume an agent endpoint defined with `defineAgentEndpoint`.\n *\n * Transport (ADR D7, EC-3): fetch + ReadableStream — NOT EventSource.\n * EventSource is GET-only; agent endpoints need POST + body.\n *\n * Returns:\n * events — array of AgentEvents accumulated so far\n * send — call with a payload to (re)open a stream\n * status — 'idle' | 'streaming' | 'done' | 'error'\n * abort — manually cancel an in-flight stream\n *\n * Cleanup (EC-8): on unmount, the current AbortController fires .abort(),\n * which propagates to the underlying fetch and the ReadableStream reader.\n * Safe under React.StrictMode double-mount.\n */\n\nexport type AgentStreamStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface UseAgentStreamReturn<TBody = unknown> {\n events: AgentEvent[]\n status: AgentStreamStatus\n /** Accumulated assistant text — concatenation of all `message` contents (M5-1). */\n liveText: string\n /** The last `error` event, or `undefined` (M5-1). */\n error: AgentErrorEvent | undefined\n send: (body: TBody) => void\n abort: () => void\n reset: () => void\n}\n\nexport interface UseAgentStreamOptions {\n /** Extra headers (e.g., auth). */\n headers?: Record<string, string>\n /** Override fetch (rare — primarily for tests). */\n fetch?: typeof fetch\n}\n\nexport function useAgentStream<TBody = unknown>(\n path: string,\n options: UseAgentStreamOptions = {},\n): UseAgentStreamReturn<TBody> {\n const [events, setEvents] = useState<AgentEvent[]>([])\n const [status, setStatus] = useState<AgentStreamStatus>('idle')\n const controllerRef = useRef<AbortController | null>(null)\n\n const abort = useCallback(() => {\n if (controllerRef.current) {\n controllerRef.current.abort()\n controllerRef.current = null\n }\n }, [])\n\n const reset = useCallback(() => {\n abort()\n setEvents([])\n setStatus('idle')\n }, [abort])\n\n const send = useCallback(\n (body: TBody) => {\n // Cancel any in-flight stream first.\n abort()\n const controller = new AbortController()\n controllerRef.current = controller\n setStatus('streaming')\n\n let sawError = false\n consumeAgentStream<TBody>(path, {\n body,\n fetch: options.fetch,\n headers: options.headers,\n signal: controller.signal,\n onEvent: (event) => {\n if (event.type === 'error') sawError = true\n setEvents((prev) => [...prev, event])\n },\n })\n .then(() => {\n if (controllerRef.current !== controller) return // superseded\n setStatus(sawError ? 'error' : 'done')\n })\n .catch(() => {\n if (controllerRef.current !== controller) return // superseded / aborted\n if (controller.signal.aborted) return\n setStatus('error')\n })\n },\n [abort, path, options.fetch, options.headers],\n )\n\n // EC-8: cleanup on unmount aborts in-flight stream.\n useEffect(() => {\n return () => {\n if (controllerRef.current) {\n controllerRef.current.abort()\n controllerRef.current = null\n }\n }\n }, [])\n\n // M5-1: derived views over the accumulated events.\n const liveText = useMemo(() => deriveLiveText(events), [events])\n const error = useMemo(() => deriveError(events), [events])\n\n return { events, status, liveText, error, send, abort, reset }\n}\n","import type { AgentEvent } from '../core/contracts/agent-events.js'\n\n/**\n * T5.2 — Pure SSE consumer used by useAgentStream.\n *\n * Split out from the React hook so the wire behavior can be tested\n * without a DOM. The hook is glue: useState + useEffect + this primitive.\n *\n * Transport (ADR D7, EC-3): fetch + ReadableStream — NOT EventSource.\n * EventSource is GET-only and cannot carry a body. Agent endpoints need\n * POST + JSON body, so we use fetch + manual SSE chunk parsing.\n */\n\nexport interface ConsumeOptions<TBody = unknown> {\n /** Request body — JSON-serialized into the POST. */\n body: TBody\n /** Called once per SSE event parsed off the stream. */\n onEvent: (event: AgentEvent) => void\n /** Optional fetch override (tests). */\n fetch?: typeof fetch\n /** Optional abort signal — passed through to fetch. */\n signal?: AbortSignal\n /** Extra headers (e.g., auth). */\n headers?: Record<string, string>\n}\n\n/**\n * Dispatch SSE events from a list of `\\n\\n`-separated chunk parts.\n * Extracted to keep the streaming loop under the max-depth ceiling.\n */\nfunction dispatchSseParts(parts: string[], onEvent: (event: AgentEvent) => void): void {\n for (const part of parts) {\n // A chunk may contain multiple lines; keep only the data: line.\n for (const line of part.split('\\n')) {\n const evt = parseSSEChunk(line)\n if (evt) onEvent(evt)\n }\n }\n}\n\n/**\n * Parse a single SSE line of the form `data: <json>`.\n * Returns null for non-data lines, comments, or malformed JSON.\n */\nexport function parseSSEChunk(line: string): AgentEvent | null {\n if (!line.startsWith('data:')) return null\n const raw = line.slice(5).trim()\n if (!raw) return null\n try {\n return JSON.parse(raw) as AgentEvent\n } catch {\n return null\n }\n}\n\n/**\n * POSTs to `path` with `body`, reads the SSE response, and invokes\n * `onEvent` for each parsed AgentEvent. Resolves when the server\n * closes the stream or the signal aborts.\n *\n * T1.1 — Attaches `X-Theo-Action: '1'` so 0.3.0 strict CSRF mode accepts\n * the request. The user can override (or suppress) by passing the header\n * in `options.headers` — spread order ensures their value wins.\n */\nexport async function consumeAgentStream<TBody = unknown>(\n path: string,\n options: ConsumeOptions<TBody>,\n): Promise<void> {\n const fetchImpl = options.fetch ?? globalThis.fetch\n const response = await fetchImpl(path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...options.headers,\n },\n body: JSON.stringify(options.body),\n signal: options.signal,\n })\n\n if (!response.body) return\n\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let buf = ''\n\n try {\n let done = false\n while (!done) {\n const chunk = await reader.read()\n done = chunk.done\n if (done) continue\n buf += decoder.decode(chunk.value, { stream: true })\n\n // SSE separates events with a blank line: `\\n\\n`.\n // Split, keep the trailing partial in buf.\n const parts = buf.split('\\n\\n')\n buf = parts.pop() ?? ''\n dispatchSseParts(parts, options.onEvent)\n }\n } finally {\n try {\n reader.releaseLock()\n } catch {\n // ignore — reader may already be released if the stream errored\n }\n }\n}\n","import type { AgentEvent } from '../core/contracts/agent-events.js'\n\n/**\n * A correlated, UI-facing view of one tool invocation (M5-2).\n *\n * `foldAgentToolCards` correlates a `tool_call` with its `tool_result` (by\n * `id`, falling back to FIFO-by-name) into a single card so consumers stop\n * hand-rolling `switch(event.type)`.\n */\nexport interface AgentToolCard {\n /** Correlation id (the event `id`, or a synthesized `tool-<index>`). */\n id: string\n name: string\n args: Record<string, unknown>\n status: 'running' | 'success' | 'error'\n /** The `tool_result.data` payload, once the result arrives. */\n result?: unknown\n}\n\n/**\n * Classifies a `tool_result.data` payload as success/error. Injectable so the\n * correlator is decoupled from any specific tool-result envelope shape.\n */\nexport type ToolEnvelopeResolver = (data: unknown) => { error: boolean }\n\nexport interface FoldAgentToolCardsOptions {\n /** Override the default envelope classifier. */\n resolveEnvelope?: ToolEnvelopeResolver\n}\n\n/**\n * Default envelope resolver: an object (or a JSON STRING parsing to an object)\n * with `ok === false` is an error; everything else is success (conservative).\n * sdk-tools handlers return a JSON STRING, so string parsing is intentional.\n */\nexport function defaultResolveEnvelope(data: unknown): { error: boolean } {\n return { error: coerceObject(data)?.ok === false }\n}\n\nfunction coerceObject(data: unknown): { ok?: unknown } | undefined {\n if (typeof data === 'object' && data !== null) return data\n if (typeof data === 'string') {\n try {\n const parsed: unknown = JSON.parse(data)\n if (typeof parsed === 'object' && parsed !== null) return parsed\n } catch {\n return undefined\n }\n }\n return undefined\n}\n\n/**\n * Fold an `AgentEvent[]` into correlated {@link AgentToolCard}s (M5-2). Pure.\n *\n * Correlation: a `tool_result` matches its `tool_call` by `id` when both carry\n * one; otherwise it matches the oldest still-`running` card with the same\n * `name` (FIFO). An unmatched `tool_result` becomes its own finished card.\n * A `tool_call` with no result stays `running`. The error status comes from\n * `options.resolveEnvelope` (default {@link defaultResolveEnvelope}).\n */\nexport function foldAgentToolCards(\n events: readonly AgentEvent[],\n options: FoldAgentToolCardsOptions = {},\n): AgentToolCard[] {\n const resolve = options.resolveEnvelope ?? defaultResolveEnvelope\n const cards: AgentToolCard[] = []\n const byId = new Map<string, AgentToolCard>()\n const runningByName = new Map<string, AgentToolCard[]>()\n\n events.forEach((event, index) => {\n if (event.type === 'tool_call') {\n const card: AgentToolCard = {\n id: event.id ?? `tool-${index}`,\n name: event.name,\n args: event.args,\n status: 'running',\n }\n cards.push(card)\n // Index by id (when present) AND by name FIFO — the two indexes overlap so\n // a result can correlate even when call/result disagree on carrying an id.\n if (event.id !== undefined) byId.set(event.id, card)\n pushRunning(runningByName, event.name, card)\n } else if (event.type === 'tool_result') {\n const card = matchResultCard(event, index, byId, runningByName, cards)\n card.result = event.data\n card.status = resolve(event.data).error ? 'error' : 'success'\n }\n })\n\n return cards\n}\n\nfunction pushRunning(\n runningByName: Map<string, AgentToolCard[]>,\n name: string,\n card: AgentToolCard,\n): void {\n const queue = runningByName.get(name)\n if (queue) queue.push(card)\n else runningByName.set(name, [card])\n}\n\nfunction removeRunning(\n runningByName: Map<string, AgentToolCard[]>,\n name: string,\n card: AgentToolCard,\n): void {\n const queue = runningByName.get(name)\n if (!queue) return\n const at = queue.indexOf(card)\n if (at >= 0) queue.splice(at, 1)\n}\n\nfunction matchResultCard(\n event: Extract<AgentEvent, { type: 'tool_result' }>,\n index: number,\n byId: Map<string, AgentToolCard>,\n runningByName: Map<string, AgentToolCard[]>,\n cards: AgentToolCard[],\n): AgentToolCard {\n // 1. Exact id match — consume from both indexes so it is matched at most once.\n if (event.id !== undefined) {\n const byIdCard = byId.get(event.id)\n if (byIdCard) {\n byId.delete(event.id)\n removeRunning(runningByName, byIdCard.name, byIdCard)\n return byIdCard\n }\n }\n // 2. FIFO-by-name fallback — works even if the call carried an id the result\n // omits (or vice-versa). Drop the matched card's id entry too.\n const fifo = runningByName.get(event.name)?.shift()\n if (fifo) {\n byId.delete(fifo.id)\n return fifo\n }\n // 3. Orphan result — surface it as its own finished card (never drop a result).\n const orphan: AgentToolCard = {\n id: event.id ?? `tool-${index}`,\n name: event.name,\n args: {},\n status: 'running',\n }\n cards.push(orphan)\n return orphan\n}\n","import { useMemo } from 'react'\n\nimport {\n type AgentToolCard,\n type FoldAgentToolCardsOptions,\n foldAgentToolCards,\n} from './agent-tool-cards.js'\nimport {\n type UseAgentStreamOptions,\n type UseAgentStreamReturn,\n useAgentStream,\n} from './use-agent-stream.js'\n\n/** {@link useAgentToolCards} return — the stream result plus correlated tool cards. */\nexport interface UseAgentToolCardsReturn<TBody = unknown> extends UseAgentStreamReturn<TBody> {\n /** Correlated tool cards folded from the stream events (M5-2). */\n toolCards: AgentToolCard[]\n}\n\n/**\n * `useAgentStream` + correlated tool cards (M5-2). Composes the stream hook with\n * the pure {@link foldAgentToolCards} reducer; pass `resolveEnvelope` to classify\n * tool-result payloads (default flags `{ ok: false }` envelopes as errors).\n */\nexport function useAgentToolCards<TBody = unknown>(\n path: string,\n options: UseAgentStreamOptions & FoldAgentToolCardsOptions = {},\n): UseAgentToolCardsReturn<TBody> {\n const { resolveEnvelope, ...streamOptions } = options\n const stream = useAgentStream<TBody>(path, streamOptions)\n const toolCards = useMemo(\n () => foldAgentToolCards(stream.events, { resolveEnvelope }),\n [stream.events, resolveEnvelope],\n )\n return { ...stream, toolCards }\n}\n","import type { UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`\n * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer\n * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path\n * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).\n *\n * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never\n * calls an agent never pays for it, and importing `theokit/client` does not hard-require\n * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app\n * always has `ai` installed (it is the UIMessageStream consumer).\n *\n * `onMessage` is invoked on every reconstruction step with the latest snapshot of the\n * assistant message, so a caller (the `useAgent` hook) can render streaming updates.\n */\nexport async function consumeUIMessageStream(\n response: Response,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n if (response.body === null) return\n\n const { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact\n // gate `useChat` runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n const chunkStream = new ReadableStream<UIMessageChunk>({\n async start(controller) {\n for await (const result of parsed) {\n if (result.success) controller.enqueue(result.value)\n }\n controller.close()\n },\n })\n\n for await (const message of readUIMessageStream({ stream: chunkStream })) {\n onMessage(message)\n }\n}\n","import type { UIMessage } from 'ai'\nimport { useCallback, useRef, useState } from 'react'\n\nimport { consumeUIMessageStream } from './consume-ui-message-stream.js'\n\n/**\n * M2 (theokit-ai-first) — `useAgent`, the typed client hook for the `agents/*.ts` convention.\n *\n * A thin React hook over `consumeUIMessageStream` (which reuses `ai`'s own UIMessageStream\n * reader). `useAgent('support')` binds to `POST /api/agents/support`; the generated\n * `@theo/agents` module (`.theokit/agents.d.ts`) types `send` to the agent's `input` schema,\n * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with\n * ZERO manual wiring (DoD line 2).\n *\n * Transport mirrors `useAgentStream`: fetch + ReadableStream (POST needs a body; EventSource\n * is GET-only). `ai` is loaded lazily by `consumeUIMessageStream` so non-agent apps never\n * pay for it.\n */\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface UseAgentReturn<TInput = unknown> {\n /** Reconstructed assistant messages so far (ai `UIMessage[]`). */\n messages: UIMessage[]\n status: UseAgentStatus\n /** The last error, or `undefined`. */\n error: Error | undefined\n /** Send a request; opens a new stream. Typed to the agent's `input` schema. */\n send: (input: TInput) => void\n /** Abort an in-flight stream. */\n abort: () => void\n /** Clear messages + error, back to idle. */\n reset: () => void\n}\n\nexport interface UseAgentOptions {\n /** Extra request headers (e.g., auth). */\n headers?: Record<string, string>\n /** Override fetch (primarily for tests). */\n fetch?: typeof fetch\n}\n\n/**\n * Bind to the agent endpoint at `path` (`/api/agents/<name>`). Prefer the generated\n * `useAgent` from `@theo/agents` (typed by agent name); this base accepts an explicit path.\n */\nexport function useAgent<TInput = unknown>(\n path: string,\n options: UseAgentOptions = {},\n): UseAgentReturn<TInput> {\n const [messages, setMessages] = useState<UIMessage[]>([])\n const [status, setStatus] = useState<UseAgentStatus>('idle')\n const [error, setError] = useState<Error | undefined>(undefined)\n const controllerRef = useRef<AbortController | null>(null)\n\n const abort = useCallback(() => {\n controllerRef.current?.abort()\n controllerRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n abort()\n setMessages([])\n setError(undefined)\n setStatus('idle')\n }, [abort])\n\n const send = useCallback(\n (input: TInput) => {\n abort()\n const controller = new AbortController()\n controllerRef.current = controller\n setMessages([])\n setError(undefined)\n setStatus('streaming')\n\n const fetchImpl = options.fetch ?? globalThis.fetch\n void (async () => {\n try {\n const response = await fetchImpl(path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...options.headers,\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n })\n await consumeUIMessageStream(response, (message) => {\n setMessages((prev) => {\n const next = [...prev]\n const idx = next.findIndex((m) => m.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n return next\n })\n })\n setStatus('done')\n } catch (err) {\n if (controller.signal.aborted) return\n setError(err instanceof Error ? err : new Error(String(err)))\n setStatus('error')\n }\n })()\n },\n [abort, options.fetch, options.headers, path],\n )\n\n return { messages, status, error, send, abort, reset }\n}\n","/**\n * <Link> — React Router Link with route prefetching.\n *\n * Wraps react-router's <Link> with prefetch behavior:\n * - `intent` (default): prefetch on hover + focus (~200ms before click)\n * - `viewport`: prefetch when visible (IntersectionObserver)\n * - `none`: no prefetch (same as plain react-router Link)\n *\n * Uses `<link rel=\"prefetch\">` (not modulepreload) because route paths\n * can be prefetched directly — no Vite manifest resolution needed (EC-1).\n */\nimport {\n Link as RouterLink,\n type LinkProps as RouterLinkProps,\n} from 'react-router'\nimport { useRef, useCallback, useEffect } from 'react'\n\nexport type PrefetchBehavior = 'none' | 'intent' | 'viewport'\n\nexport interface LinkProps extends RouterLinkProps {\n /** Prefetch strategy. Default: 'intent' (on hover + focus). */\n prefetch?: PrefetchBehavior\n}\n\n/** Deduplication — each URL prefetched at most once per session. */\nconst prefetched = new Set<string>()\n\nfunction injectPrefetch(href: string): void {\n // EC-2: SSR guard — document unavailable on server\n if (typeof document === 'undefined') return\n if (prefetched.has(href)) return\n prefetched.add(href)\n\n const link = document.createElement('link')\n link.rel = 'prefetch'\n link.href = href\n document.head.appendChild(link)\n}\n\nfunction resolveTo(to: LinkProps['to']): string {\n if (typeof to === 'string') return to\n return to?.pathname ?? ''\n}\n\n/**\n * TheoKit Link — drop-in replacement for react-router Link with prefetch.\n *\n * @example\n * ```tsx\n * import { Link } from 'theokit/client'\n *\n * <Link to=\"/contacts\">Contacts</Link>\n * <Link to=\"/dashboard\" prefetch=\"viewport\">Dashboard</Link>\n * <Link to=\"/settings\" prefetch=\"none\">Settings</Link>\n * ```\n */\nexport function Link({ prefetch = 'intent', to, onMouseEnter, onFocus, ...rest }: LinkProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n\n const handleIntent = useCallback(\n (event: React.MouseEvent<HTMLAnchorElement> | React.FocusEvent<HTMLAnchorElement>) => {\n if (prefetch === 'intent') {\n injectPrefetch(resolveTo(to))\n }\n // Forward original handlers\n if (event.type === 'mouseenter' && onMouseEnter) {\n ;(onMouseEnter as React.MouseEventHandler<HTMLAnchorElement>)(event as React.MouseEvent<HTMLAnchorElement>)\n }\n if (event.type === 'focus' && onFocus) {\n ;(onFocus as React.FocusEventHandler<HTMLAnchorElement>)(event as React.FocusEvent<HTMLAnchorElement>)\n }\n },\n [prefetch, to, onMouseEnter, onFocus],\n )\n\n // Viewport mode: IntersectionObserver\n useEffect(() => {\n if (prefetch !== 'viewport' || !ref.current) return\n if (typeof IntersectionObserver === 'undefined') return // SSR guard\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n injectPrefetch(resolveTo(to))\n observer.disconnect()\n }\n },\n { rootMargin: '200px' }, // prefetch slightly before visible\n )\n observer.observe(ref.current)\n return () => observer.disconnect()\n }, [prefetch, to])\n\n return (\n <RouterLink\n ref={ref}\n to={to}\n onMouseEnter={handleIntent}\n onFocus={handleIntent}\n {...rest}\n />\n )\n}\n","/**\n * <Metadata> — SEO-ready head tags from a single component.\n *\n * Uses React 19's native <title>/<meta>/<link> hoisting to <head>.\n * No build-time extraction needed — works in SSR and client.\n *\n * @example\n * ```tsx\n * import { Metadata } from 'theokit/client'\n *\n * export default function ContactsPage() {\n * return (\n * <>\n * <Metadata\n * title=\"Contacts | My CRM\"\n * description=\"Manage your contacts\"\n * ogImage=\"/og/contacts.png\"\n * canonical=\"https://mycrm.com/contacts\"\n * />\n * <h1>Contacts</h1>\n * </>\n * )\n * }\n * ```\n */\n\nexport interface MetadataProps {\n title?: string\n description?: string\n canonical?: string\n ogTitle?: string\n ogDescription?: string\n ogImage?: string\n ogType?: string\n ogUrl?: string\n twitterCard?: 'summary' | 'summary_large_image'\n /** Custom meta tags rendered as children */\n children?: React.ReactNode\n}\n\nexport function Metadata(props: MetadataProps) {\n const ogTitle = props.ogTitle ?? props.title\n const ogDesc = props.ogDescription ?? props.description\n\n return (\n <>\n {props.title && <title>{props.title}</title>}\n {props.description && <meta name=\"description\" content={props.description} />}\n {props.canonical && <link rel=\"canonical\" href={props.canonical} />}\n {ogTitle && <meta property=\"og:title\" content={ogTitle} />}\n {ogDesc && <meta property=\"og:description\" content={ogDesc} />}\n {props.ogImage && <meta property=\"og:image\" content={props.ogImage} />}\n {props.ogType && <meta property=\"og:type\" content={props.ogType} />}\n {props.ogUrl && <meta property=\"og:url\" content={props.ogUrl} />}\n {props.twitterCard && <meta name=\"twitter:card\" content={props.twitterCard} />}\n {props.children}\n </>\n )\n}\n","/**\n * <Image> — optimized image component with lazy loading.\n *\n * Renders a standard <img> with:\n * - loading=\"lazy\" by default (eager with priority={true})\n * - decoding=\"async\" for non-blocking decode\n * - width/height for CLS prevention\n * - srcSet/sizes forwarded for responsive images\n *\n * No CDN, no Sharp, no build-time optimization — pure HTML attributes\n * that deliver 80% of the performance value at zero complexity.\n *\n * @example\n * ```tsx\n * import { Image } from 'theokit/client'\n *\n * <Image src=\"/team.jpg\" alt=\"Team photo\" width={800} height={600} />\n * <Image src=\"/hero.jpg\" alt=\"Hero\" priority />\n * <Image\n * src=\"/product.jpg\"\n * alt=\"Product\"\n * width={400}\n * height={300}\n * srcSet=\"/product-400.jpg 400w, /product-800.jpg 800w\"\n * sizes=\"(max-width: 768px) 100vw, 400px\"\n * />\n * ```\n */\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL (required). */\n src: string\n /** Alt text for accessibility (required). */\n alt: string\n /** If true, loading=\"eager\" — use for above-the-fold images. */\n priority?: boolean\n}\n\nexport function Image({ priority, loading, decoding, ...props }: ImageProps) {\n return (\n <img\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? 'async'}\n {...props}\n />\n )\n}\n"],"mappings":"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","names":["useMemo","useMemo","useCallback","useRef","useState","useState","useRef","useCallback","useRef","useCallback","useEffect","jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/theo-fetch.ts","../../src/client/batch.ts","../../src/client/batch-transport.ts","../../src/client/app-client.ts","../../src/client/consume-ui-message-stream.ts","../../src/client/use-agent.ts","../../src/client/link.tsx","../../src/client/metadata.tsx","../../src/client/image.tsx"],"sourcesContent":["import superjson from 'superjson'\nimport type { z } from 'zod'\n\nimport type { TheoErrorCode, TheoErrorEnvelope } from '../core/contracts/error-envelope.js'\n\nimport { getGlobalBatcher } from './batch-transport.js'\n\n// --- Transformer (T1.3) ---\n\n/**\n * Module-scoped flag preventing the transformer-mismatch warning from\n * firing more than once per session (EC-6).\n */\nlet mismatchWarned = false\n\n/** Test-only helper to reset the warned flag between assertions. */\nexport function __resetMismatchWarningForTests(): void {\n mismatchWarned = false\n}\n\n/**\n * Deserialize a fetch response body according to the negotiated transformer.\n *\n * `serverTransformerName` comes from the `x-theo-transformer` response header\n * (null when absent — server is using the default JSON path).\n * `clientTransformerName` is the transformer the client was built with\n * (typically injected via a Vite virtual module; falls back to `'json'`).\n *\n * Mismatch fires a single console.warn and falls back to JSON.parse (EC-5/EC-6).\n */\nexport function deserializeFetchResponse(\n raw: string,\n serverTransformerName: string | null,\n clientTransformerName: string,\n): unknown {\n // `raw` is typed `string`, but stay defensive: callers pass `await response.text()`\n // which can be empty.\n if (raw === '') {\n return null\n }\n\n const serverEffective = serverTransformerName ?? 'json'\n if (serverEffective !== clientTransformerName && !mismatchWarned) {\n mismatchWarned = true\n console.warn(\n `[theokit] transformer mismatch: server=${serverEffective}, client=${clientTransformerName}. Falling back to JSON.parse.`,\n )\n }\n\n if (serverEffective === 'superjson' && clientTransformerName === 'superjson') {\n const wrapped = JSON.parse(raw) as Parameters<typeof superjson.deserialize>[0]\n return superjson.deserialize(wrapped)\n }\n\n // Default path (json) or mismatch fallback\n return JSON.parse(raw)\n}\n\n// --- Utility Types ---\n\n/** Infer the response type from a route's handler return */\nexport type InferResponse<T> = T extends { handler: (...args: never[]) => infer R }\n ? Awaited<R>\n : unknown\n\n/** Extract the query Zod schema type, handling optional properties */\ntype ExtractQuery<T> = T extends { query?: infer Q } ? (Q extends z.ZodType ? Q : never) : never\n\n/** Extract the body Zod schema type, handling optional properties */\ntype ExtractBody<T> = T extends { body?: infer B } ? (B extends z.ZodType ? B : never) : never\n\n/** Infer query type from a route's query Zod schema */\nexport type InferQuery<T> = [ExtractQuery<T>] extends [never]\n ? undefined\n : ExtractQuery<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractQuery<T>>\n\n/** Infer body type from a route's body Zod schema */\nexport type InferBody<T> = [ExtractBody<T>] extends [never]\n ? undefined\n : ExtractBody<T> extends z.ZodUndefined\n ? undefined\n : z.infer<ExtractBody<T>>\n\n/** Build the options type based on what schemas the route has */\nexport type TheoFetchOptions<T> = Omit<RequestInit, 'body' | 'method'> &\n (InferQuery<T> extends undefined ? { query?: never } : { query: InferQuery<T> }) &\n (InferBody<T> extends undefined ? { body?: never } : { body: InferBody<T> })\n\n// --- Error Class ---\n\n/**\n * G5 T2.1 — extract a TheoErrorEnvelope from a response body. Supports both\n * the new envelope-at-root shape (`{ code, message, ext?, ... }`, blueprint\n * Recommendations § concrete shape) and the legacy nested shape\n * (`{ error: { code, message, issues? } }`, G3 SerializedActionResult).\n *\n * Falls back to `INTERNAL_SERVER_ERROR` with a synthetic message when the\n * body shape is unrecognized. Pure; no I/O.\n */\nfunction extractEnvelope(status: number, body: unknown): TheoErrorEnvelope {\n if (!body || typeof body !== 'object') {\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n // New shape — envelope at root\n if (typeof obj.code === 'string' && typeof obj.message === 'string') {\n return {\n code: obj.code as TheoErrorCode,\n message: obj.message,\n cause: obj.cause,\n meta: obj.meta as Record<string, unknown> | undefined,\n ext: obj.ext,\n }\n }\n // Legacy shape — { error: { code, message, ... } }\n if (obj.error && typeof obj.error === 'object') {\n const nested = obj.error as Record<string, unknown>\n return {\n code:\n typeof nested.code === 'string' ? (nested.code as TheoErrorCode) : 'INTERNAL_SERVER_ERROR',\n message: typeof nested.message === 'string' ? nested.message : `HTTP ${String(status)}`,\n }\n }\n return { code: 'INTERNAL_SERVER_ERROR', message: `HTTP ${String(status)}` }\n}\n\n/**\n * Pull a single string field from an object, returning undefined if missing\n * or non-string. Helper for `extractLegacyFields` to stay under the\n * complexity ceiling.\n */\nfunction strField(obj: Record<string, unknown> | null, key: string): string | undefined {\n const v = obj?.[key]\n return typeof v === 'string' ? v : undefined\n}\n\n/**\n * Pull the canonical {message, code, issues} from either an envelope-at-root\n * body or the legacy { error: {...} } body. Pure.\n */\nfunction extractLegacyFields(\n status: number,\n body: unknown,\n): { message: string; code?: string; issues?: unknown[] } {\n if (!body || typeof body !== 'object') {\n return { message: `HTTP ${String(status)}` }\n }\n const obj = body as Record<string, unknown>\n const nested =\n obj.error && typeof obj.error === 'object' ? (obj.error as Record<string, unknown>) : null\n const issues = Array.isArray(nested?.issues) ? nested.issues : undefined\n return {\n message: strField(obj, 'message') ?? strField(nested, 'message') ?? `HTTP ${String(status)}`,\n code: strField(obj, 'code') ?? strField(nested, 'code'),\n issues,\n }\n}\n\nexport class TheoFetchError extends Error {\n status: number\n code?: string\n issues?: unknown[]\n /**\n * G5 T2.1 — canonical envelope view of the server-side error. Use this in\n * consumer code that wants to switch on a typed TheoErrorCode instead of\n * coupling to the legacy `.status` / `.code` flat fields.\n */\n readonly envelope: TheoErrorEnvelope\n\n constructor(status: number, body?: unknown) {\n const legacy = extractLegacyFields(status, body)\n super(legacy.message)\n this.name = 'TheoFetchError'\n this.status = status\n this.code = legacy.code\n this.issues = legacy.issues\n this.envelope = extractEnvelope(status, body)\n }\n}\n\n/**\n * Serialize a query-param value to a string. Handles primitives, dates,\n * and falls back to JSON for arrays/objects. Avoids the\n * `[object Object]` foot-gun that `no-base-to-string` warns against.\n */\nfunction stringifyQueryValue(value: unknown): string {\n if (value === null) return 'null'\n if (value instanceof Date) return value.toISOString()\n switch (typeof value) {\n case 'string':\n return value\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value)\n default:\n return JSON.stringify(value)\n }\n}\n\n// --- Main Function ---\n\ninterface TheoFetchInternalOptions {\n method?: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: HeadersInit\n signal?: AbortSignal\n}\n\n/**\n * Resolve the request origin per the documented fallback hierarchy. Pure\n * helper extracted to keep `theoFetch` under the complexity ceiling.\n * 1. `globalThis.location.origin` (browser)\n * 2. `globalThis.__THEO_ORIGIN__` (build-time literal)\n * 3. `process.env.THEO_ORIGIN` (escape hatch)\n * 4. `http://localhost` (placeholder for URL parsing — the URL is built\n * relative so only pathname+search ever flows to the wire)\n */\nfunction resolveRequestOrigin(): string {\n const g = globalThis as { location?: { origin?: string }; __THEO_ORIGIN__?: string }\n const fromEnv = typeof process !== 'undefined' ? process.env.THEO_ORIGIN : undefined\n return g.location?.origin ?? g.__THEO_ORIGIN__ ?? fromEnv ?? 'http://localhost'\n}\n\nfunction buildFetchUrl(path: string, query: Record<string, unknown> | undefined): URL {\n const fetchUrl = new URL(path, resolveRequestOrigin())\n if (!query) return fetchUrl\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) {\n fetchUrl.searchParams.set(k, stringifyQueryValue(v))\n }\n }\n return fetchUrl\n}\n\n/**\n * Normalize `HeadersInit` (Headers / `[string, string][]` / Record) into a\n * single plain `Record<string, string>`. Plain-object output is required\n * by callers that introspect headers via index access (test fixtures use\n * `(init.headers as Record<string, string>)['Content-Type']`).\n */\nfunction normalizeHeaders(input: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {}\n if (!input) return out\n if (input instanceof Headers) {\n input.forEach((value, key) => {\n out[key] = value\n })\n return out\n }\n if (Array.isArray(input)) {\n for (const [key, value] of input) out[key] = value\n return out\n }\n for (const [key, value] of Object.entries(input)) {\n out[key] = value\n }\n return out\n}\n\nfunction buildRequestInit(opts: TheoFetchInternalOptions): RequestInit {\n const init: RequestInit = {}\n if (opts.method !== undefined) init.method = opts.method\n if (opts.signal !== undefined) init.signal = opts.signal\n\n const headers = normalizeHeaders(opts.headers)\n init.headers = headers\n\n if (opts.body !== undefined) {\n init.body = JSON.stringify(opts.body)\n headers['Content-Type'] = 'application/json'\n }\n\n // Phase 5 — Auto-attach `X-Theo-Action: 1` for state-mutating methods so\n // the framework's CSRF check passes when servers run in `strict` mode.\n // Safe methods (GET/HEAD/OPTIONS) skip the header to keep them cacheable.\n const method = (opts.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {\n headers['X-Theo-Action'] = '1'\n }\n return init\n}\n\nasync function tryBatcher(\n url: string,\n options: TheoFetchInternalOptions,\n): Promise<{ matched: true; result: unknown } | { matched: false }> {\n const batcher = getGlobalBatcher()\n if (!batcher) return { matched: false }\n try {\n const result = await batcher.dispatch({\n path: url,\n method: options.method ?? 'GET',\n query: options.query,\n body: options.body,\n })\n return { matched: true, result }\n } catch {\n // Batcher failure: fall through to direct fetch (graceful degrade).\n return { matched: false }\n }\n}\n\nexport async function theoFetch<T>(\n url: string,\n options?: TheoFetchOptions<T>,\n): Promise<InferResponse<T>> {\n const internal = (options ?? {}) as unknown as TheoFetchInternalOptions\n\n // T1.5 — Transparent batching when globalThis.__THEO_BATCHING__ is truthy.\n const batchAttempt = await tryBatcher(url, internal)\n if (batchAttempt.matched) {\n return batchAttempt.result as InferResponse<T>\n }\n\n const fetchUrl = buildFetchUrl(url, internal.query)\n const init = buildRequestInit(internal)\n const response = await fetch(fetchUrl.toString(), init)\n\n if (!response.ok) {\n let errorBody: unknown\n try {\n errorBody = await response.json()\n } catch {\n // Non-JSON error response\n }\n throw new TheoFetchError(response.status, errorBody)\n }\n\n // Handle 204 No Content (EC-1)\n if (response.status === 204) {\n return null as InferResponse<T>\n }\n\n // Check for empty body\n const contentLength = response.headers.get('content-length')\n if (contentLength === '0') {\n return null as InferResponse<T>\n }\n\n // T1.3 — transformer-aware deserialization\n const serverTransformerName = response.headers.get('x-theo-transformer')\n const clientTransformerName = resolveClientTransformerName()\n const text = await response.text()\n return deserializeFetchResponse(\n text,\n serverTransformerName,\n clientTransformerName,\n ) as InferResponse<T>\n}\n\n/**\n * Read the client-configured transformer name. Default `'json'`.\n *\n * In a Vite build, this is overridden by virtual module\n * `/@theo/runtime-config` which sets `globalThis.__THEO_TRANSFORMER__`.\n * Outside Vite (Node SSR, tests) the default applies.\n */\nfunction resolveClientTransformerName(): string {\n const g = globalThis as { __THEO_TRANSFORMER__?: string }\n return g.__THEO_TRANSFORMER__ ?? 'json'\n}\n","/**\n * T5.1 — client-side microtask batching.\n *\n * Collects all `dispatch` calls within the same microtask and sends them as a\n * single HTTP POST to the configured transport. Each caller's promise resolves\n * with its own result; one failed item does not break the others (per-item\n * error isolation).\n *\n * Designed as a transport-agnostic primitive so unit tests do not require\n * network access. The default transport (fetch to `/api/__theo_batch__`)\n * lives alongside this module but is created by the consumer (e.g., theoFetch).\n */\n\nexport interface BatchRequest {\n path: string\n method: string\n query?: Record<string, unknown>\n body?: unknown\n headers?: Record<string, string>\n}\n\nexport type BatchResponse =\n | { index: number; data: unknown }\n | { index: number; error: { message: string; code?: string } }\n\nexport type BatchTransport = (requests: BatchRequest[]) => Promise<BatchResponse[]>\n\nexport interface BatcherOptions {\n transport: BatchTransport\n /** Maximum batch size before flushing into multiple parallel batches. */\n max?: number\n}\n\nexport interface Batcher {\n dispatch(req: BatchRequest): Promise<unknown>\n}\n\ninterface PendingCall {\n req: BatchRequest\n resolve: (value: unknown) => void\n reject: (reason: unknown) => void\n}\n\nexport function createBatcher(options: BatcherOptions): Batcher {\n const max = options.max ?? 32\n let queue: PendingCall[] = []\n let flushScheduled = false\n\n function flush(): void {\n flushScheduled = false\n const current = queue\n queue = []\n if (current.length === 0) return\n\n // Split into chunks respecting max\n const chunks: PendingCall[][] = []\n for (let i = 0; i < current.length; i += max) {\n chunks.push(current.slice(i, i + max))\n }\n\n for (const chunk of chunks) {\n const payload: BatchRequest[] = chunk.map((p) => p.req)\n options\n .transport(payload)\n .then((results) => {\n for (let i = 0; i < chunk.length; i++) {\n // `results[i]` could be undefined at runtime when the transport\n // returns fewer entries than the chunk; TypeScript's strict\n // typing without `noUncheckedIndexedAccess` does not surface\n // this, so we hand-narrow.\n const result = results.length > i ? results[i] : undefined\n if (result === undefined) {\n chunk[i].reject(new Error(`Batch transport returned no result for index ${i}`))\n continue\n }\n if ('error' in result) {\n const err = new Error(result.error.message)\n ;(err as { code?: string }).code = result.error.code\n chunk[i].reject(err)\n } else {\n chunk[i].resolve(result.data)\n }\n }\n })\n .catch((err: unknown) => {\n for (const item of chunk) item.reject(err)\n })\n }\n }\n\n return {\n dispatch(req: BatchRequest): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n queue.push({ req, resolve, reject })\n if (!flushScheduled) {\n flushScheduled = true\n queueMicrotask(flush)\n }\n })\n },\n }\n}\n","/**\n * T1.5 — Default HTTP transport for the client batcher.\n *\n * Wraps `fetch` to POST `/api/__theo_batch__` with the collected requests\n * and return the array of per-item results.\n */\n\nimport {\n createBatcher,\n type BatchTransport,\n type Batcher,\n type BatchRequest,\n type BatchResponse,\n} from './batch.js'\n\nconst BATCH_ENDPOINT = '/api/__theo_batch__'\n\nexport interface CreateBatchTransportOptions {\n /** Override fetch (default: globalThis.fetch). Used by tests. */\n fetchImpl?: typeof fetch\n /** Override endpoint (default '/api/__theo_batch__'). */\n endpoint?: string\n}\n\nexport function createBatchTransport(options: CreateBatchTransportOptions = {}): BatchTransport {\n const fetchImpl = options.fetchImpl ?? fetch\n const endpoint = options.endpoint ?? BATCH_ENDPOINT\n return async (requests: BatchRequest[]): Promise<BatchResponse[]> => {\n const response = await fetchImpl(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ requests }),\n })\n if (!response.ok) {\n throw new Error(`Batch transport returned ${response.status}`)\n }\n const payload = (await response.json()) as { results?: BatchResponse[] }\n if (!Array.isArray(payload.results)) {\n throw new Error('Batch response missing results array')\n }\n // Map results to indexed BatchResponse shape (server already returns the right form)\n return payload.results.map((item, index) => {\n if ('error' in item) {\n return { index, error: (item as { error: { message: string; code?: string } }).error }\n }\n return { index, data: (item as { data: unknown }).data }\n })\n }\n}\n\n// --- EC-7: singleton batcher per page, lazy-instantiated ---\n\nlet globalBatcher: Batcher | undefined\n\n/** Test-only — reset the module-scope singleton between assertions. */\nexport function __resetGlobalBatcherForTests(): void {\n globalBatcher = undefined\n}\n\n/**\n * Returns the global batcher singleton when `globalThis.__THEO_BATCHING__` is\n * truthy; undefined otherwise. Lazy-instantiated on first call.\n */\nexport function getGlobalBatcher(): Batcher | undefined {\n const g = globalThis as { __THEO_BATCHING__?: boolean }\n if (!g.__THEO_BATCHING__) return undefined\n globalBatcher ??= createBatcher({ transport: createBatchTransport() })\n return globalBatcher\n}\n","/**\n * Phase 3 of G1 (g1-client-codegen-plan.md):\n *\n * `createAppClient(baseUrl?, fetchImpl?)` returns a Proxy that walks property\n * access (`client.posts.id.get(...)`) into a `theoFetch` invocation against\n * `{baseUrl}/posts/{params.id}` with method `GET`.\n *\n * Type safety is delivered by the `.d.ts` file emitted in Phase 2 — this\n * runtime is structurally untyped. Power users may use `theoFetch` directly\n * when they need to escape the Proxy facade.\n *\n * Edge cases absorbed:\n * - EC-1 (thenable trap): `then` / `catch` / `finally` / `toJSON` / `Symbol.*`\n * return `undefined` so that accidental `await client.posts` resolves to a\n * plain Proxy value (it is NOT thenable) instead of looping.\n * - EC-7 (abort signal): `opts.signal` is spread through to the underlying\n * fetchImpl unchanged — verified in unit tests.\n *\n * Bundle target: ≤ 2KB gzipped.\n */\n\nimport { theoFetch, TheoFetchError } from './theo-fetch.js'\nimport type { TheoFetchOptions } from './theo-fetch.js'\nimport { HTTP_METHOD_LOWERCASE } from '../core/contracts/http-methods.js'\n\nconst HTTP_METHODS_SET = new Set(HTTP_METHOD_LOWERCASE)\n\n/**\n * Keys that JavaScript runtime / inspectors / Promise resolution probe\n * unconditionally. Returning `undefined` for these keeps the Proxy from\n * pretending to be a thenable / iterable / serializable thing.\n */\nconst NON_INTERCEPT_KEYS = new Set([\n 'then',\n 'catch',\n 'finally',\n 'toJSON',\n 'toString',\n 'valueOf',\n 'constructor',\n 'prototype',\n])\n\ntype FetchImpl = typeof theoFetch\n\ninterface CallOptions {\n params?: Record<string, string | number>\n query?: Record<string, unknown>\n body?: unknown\n signal?: AbortSignal\n headers?: HeadersInit\n // arbitrary RequestInit fields (mode, credentials, cache, etc.)\n [k: string]: unknown\n}\n\nfunction injectParams(path: string, params?: Record<string, string | number>): string {\n if (!params) {\n if (/:(?:\\.\\.\\.)?[A-Za-z_]/.test(path)) {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Route ${path} requires params but none were provided. Pass them via opts.params, e.g. client.posts.get({ params: { id: '123' } }).`,\n },\n })\n }\n return path\n }\n return path.replace(/:(?:\\.\\.\\.)?([A-Za-z_][A-Za-z0-9_]*)/g, (_match, key: string) => {\n const value = params[key]\n if (value === undefined || value === null || value === '') {\n throw new TheoFetchError(0, {\n error: {\n code: 'MISSING_PARAM',\n message: `Param '${key}' is required for path '${path}' but was missing/empty.`,\n },\n })\n }\n return encodeURIComponent(String(value))\n })\n}\n\ninterface ProxyContext {\n segments: string[]\n baseUrl: string\n fetchImpl: FetchImpl\n}\n\nfunction makeProxy(ctx: ProxyContext): unknown {\n const handler: ProxyHandler<() => void> = {\n get(_target, key) {\n if (typeof key !== 'string') return undefined\n if (NON_INTERCEPT_KEYS.has(key)) return undefined\n if (HTTP_METHODS_SET.has(key)) {\n return async (opts?: CallOptions) => {\n if (ctx.segments.length === 0) {\n throw new TheoFetchError(0, {\n error: {\n code: 'INVALID_PATH',\n message: `client.${key}() called without a route segment. Use client.<resource>.${key}(...) instead.`,\n },\n })\n }\n let finalPath = `${ctx.baseUrl}/${ctx.segments.join('/')}`\n if (opts?.params) {\n // Build a path with `:name` placeholders re-inserted from segments\n // whose name matches a key in opts.params (treat as dynamic).\n const segmentsWithDynamic = ctx.segments.map((seg) =>\n Object.prototype.hasOwnProperty.call(opts.params, seg) ? `:${seg}` : seg,\n )\n finalPath = injectParams(\n `${ctx.baseUrl}/${segmentsWithDynamic.join('/')}`,\n opts.params,\n )\n }\n const { params: _ignored, ...rest } = opts ?? {}\n const init = { method: key.toUpperCase(), ...rest } as unknown as TheoFetchOptions<unknown>\n return ctx.fetchImpl(finalPath, init)\n }\n }\n // Continue traversal — append segment to the path.\n return makeProxy({ ...ctx, segments: [...ctx.segments, key] })\n },\n apply() {\n return Promise.reject(\n new TheoFetchError(0, {\n error: {\n code: 'INVALID_CALL',\n message:\n 'client(...) is not callable. Use client.<resource>.<method>(opts?) — e.g. client.posts.get().',\n },\n }),\n )\n },\n }\n return new Proxy(() => undefined, handler)\n}\n\nexport interface CreateAppClientOptions {\n /** Base URL for the API. Defaults to `/api`. */\n baseUrl?: string\n /** Test-only fetch override. Production callers should not pass this. */\n fetchImpl?: FetchImpl\n}\n\nexport function createAppClient<TAppClient = unknown>(\n baseUrlOrOptions?: string | CreateAppClientOptions,\n legacyFetchImpl?: FetchImpl,\n): TAppClient {\n let baseUrl = '/api'\n let fetchImpl: FetchImpl = theoFetch\n if (typeof baseUrlOrOptions === 'string') {\n baseUrl = baseUrlOrOptions || '/api'\n } else if (baseUrlOrOptions && typeof baseUrlOrOptions === 'object') {\n if (baseUrlOrOptions.baseUrl) baseUrl = baseUrlOrOptions.baseUrl\n if (baseUrlOrOptions.fetchImpl) fetchImpl = baseUrlOrOptions.fetchImpl\n }\n // Legacy 2nd-arg fetchImpl overrides regardless of 1st-arg shape (test seam).\n if (legacyFetchImpl) fetchImpl = legacyFetchImpl\n // Strip trailing slash for predictable joining.\n if (baseUrl.length > 1 && baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1)\n return makeProxy({ segments: [], baseUrl, fetchImpl }) as TAppClient\n}\n","import type { UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * M2 (theokit-ai-first) — read a TheoKit agent endpoint's `UIMessageStream` SSE `Response`\n * into reconstructed assistant `UIMessage`s, reusing the `ai` package's own consumer\n * primitives (`parseJsonEventStream` + `readUIMessageStream`) — the exact path\n * `@ai-sdk/react`'s `useChat` runs internally. No reinvented wire parser (Rule 9).\n *\n * `ai` is an OPTIONAL peer dependency, so it is imported dynamically: an app that never\n * calls an agent never pays for it, and importing `theokit/client` does not hard-require\n * `ai` (mirrors how the agent runtime dynamically imports `@theokit/sdk`). An agent app\n * always has `ai` installed (it is the UIMessageStream consumer).\n *\n * `onMessage` is invoked on every reconstruction step with the latest snapshot of the\n * assistant message, so a caller (the `useAgent` hook) can render streaming updates.\n */\nexport async function consumeUIMessageStream(\n response: Response,\n onMessage: (message: UIMessage) => void,\n): Promise<void> {\n if (response.body === null) return\n\n const { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema } = await import('ai')\n\n // ai validates each SSE JSON frame against its own strict chunk schema (the exact\n // gate `useChat` runs), then yields `{ success, value }`; forward the valid chunks.\n const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema })\n const chunkStream = new ReadableStream<UIMessageChunk>({\n async start(controller) {\n for await (const result of parsed) {\n if (result.success) controller.enqueue(result.value)\n }\n controller.close()\n },\n })\n\n for await (const message of readUIMessageStream({ stream: chunkStream })) {\n onMessage(message)\n }\n}\n","import type { UIMessage } from 'ai'\nimport { useCallback, useRef, useState } from 'react'\n\nimport { consumeUIMessageStream } from './consume-ui-message-stream.js'\n\n/**\n * M2 (theokit-ai-first) — `useAgent`, the typed client hook for the `agents/*.ts` convention.\n *\n * A thin React hook over `consumeUIMessageStream` (which reuses `ai`'s own UIMessageStream\n * reader). `useAgent('support')` binds to `POST /api/agents/support`; the generated\n * `@theo/agents` module (`.theokit/agents.d.ts`) types `send` to the agent's `input` schema,\n * so the request shape is inferred end-to-end from the server `defineAgent({ input })` with\n * ZERO manual wiring (DoD line 2).\n *\n * Transport: fetch + ReadableStream (POST needs a body; EventSource is GET-only). `ai` is\n * loaded lazily by `consumeUIMessageStream` so non-agent apps never pay for it.\n */\nexport type UseAgentStatus = 'idle' | 'streaming' | 'done' | 'error'\n\nexport interface UseAgentReturn<TInput = unknown> {\n /** Reconstructed assistant messages so far (ai `UIMessage[]`). */\n messages: UIMessage[]\n status: UseAgentStatus\n /** The last error, or `undefined`. */\n error: Error | undefined\n /** Send a request; opens a new stream. Typed to the agent's `input` schema. */\n send: (input: TInput) => void\n /** Abort an in-flight stream. */\n abort: () => void\n /** Clear messages + error, back to idle. */\n reset: () => void\n}\n\nexport interface UseAgentOptions {\n /** Extra request headers (e.g., auth). */\n headers?: Record<string, string>\n /** Override fetch (primarily for tests). */\n fetch?: typeof fetch\n}\n\n/**\n * Bind to the agent endpoint at `path` (`/api/agents/<name>`). Prefer the generated\n * `useAgent` from `@theo/agents` (typed by agent name); this base accepts an explicit path.\n */\nexport function useAgent<TInput = unknown>(\n path: string,\n options: UseAgentOptions = {},\n): UseAgentReturn<TInput> {\n const [messages, setMessages] = useState<UIMessage[]>([])\n const [status, setStatus] = useState<UseAgentStatus>('idle')\n const [error, setError] = useState<Error | undefined>(undefined)\n const controllerRef = useRef<AbortController | null>(null)\n\n const abort = useCallback(() => {\n controllerRef.current?.abort()\n controllerRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n abort()\n setMessages([])\n setError(undefined)\n setStatus('idle')\n }, [abort])\n\n const send = useCallback(\n (input: TInput) => {\n abort()\n const controller = new AbortController()\n controllerRef.current = controller\n setMessages([])\n setError(undefined)\n setStatus('streaming')\n\n const fetchImpl = options.fetch ?? globalThis.fetch\n void (async () => {\n try {\n const response = await fetchImpl(path, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n 'X-Theo-Action': '1',\n ...options.headers,\n },\n body: JSON.stringify(input),\n signal: controller.signal,\n })\n await consumeUIMessageStream(response, (message) => {\n setMessages((prev) => {\n const next = [...prev]\n const idx = next.findIndex((m) => m.id === message.id)\n if (idx >= 0) next[idx] = message\n else next.push(message)\n return next\n })\n })\n setStatus('done')\n } catch (err) {\n if (controller.signal.aborted) return\n setError(err instanceof Error ? err : new Error(String(err)))\n setStatus('error')\n }\n })()\n },\n [abort, options.fetch, options.headers, path],\n )\n\n return { messages, status, error, send, abort, reset }\n}\n","/**\n * <Link> — React Router Link with route prefetching.\n *\n * Wraps react-router's <Link> with prefetch behavior:\n * - `intent` (default): prefetch on hover + focus (~200ms before click)\n * - `viewport`: prefetch when visible (IntersectionObserver)\n * - `none`: no prefetch (same as plain react-router Link)\n *\n * Uses `<link rel=\"prefetch\">` (not modulepreload) because route paths\n * can be prefetched directly — no Vite manifest resolution needed (EC-1).\n */\nimport {\n Link as RouterLink,\n type LinkProps as RouterLinkProps,\n} from 'react-router'\nimport { useRef, useCallback, useEffect } from 'react'\n\nexport type PrefetchBehavior = 'none' | 'intent' | 'viewport'\n\nexport interface LinkProps extends RouterLinkProps {\n /** Prefetch strategy. Default: 'intent' (on hover + focus). */\n prefetch?: PrefetchBehavior\n}\n\n/** Deduplication — each URL prefetched at most once per session. */\nconst prefetched = new Set<string>()\n\nfunction injectPrefetch(href: string): void {\n // EC-2: SSR guard — document unavailable on server\n if (typeof document === 'undefined') return\n if (prefetched.has(href)) return\n prefetched.add(href)\n\n const link = document.createElement('link')\n link.rel = 'prefetch'\n link.href = href\n document.head.appendChild(link)\n}\n\nfunction resolveTo(to: LinkProps['to']): string {\n if (typeof to === 'string') return to\n return to?.pathname ?? ''\n}\n\n/**\n * TheoKit Link — drop-in replacement for react-router Link with prefetch.\n *\n * @example\n * ```tsx\n * import { Link } from 'theokit/client'\n *\n * <Link to=\"/contacts\">Contacts</Link>\n * <Link to=\"/dashboard\" prefetch=\"viewport\">Dashboard</Link>\n * <Link to=\"/settings\" prefetch=\"none\">Settings</Link>\n * ```\n */\nexport function Link({ prefetch = 'intent', to, onMouseEnter, onFocus, ...rest }: LinkProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n\n const handleIntent = useCallback(\n (event: React.MouseEvent<HTMLAnchorElement> | React.FocusEvent<HTMLAnchorElement>) => {\n if (prefetch === 'intent') {\n injectPrefetch(resolveTo(to))\n }\n // Forward original handlers\n if (event.type === 'mouseenter' && onMouseEnter) {\n ;(onMouseEnter as React.MouseEventHandler<HTMLAnchorElement>)(event as React.MouseEvent<HTMLAnchorElement>)\n }\n if (event.type === 'focus' && onFocus) {\n ;(onFocus as React.FocusEventHandler<HTMLAnchorElement>)(event as React.FocusEvent<HTMLAnchorElement>)\n }\n },\n [prefetch, to, onMouseEnter, onFocus],\n )\n\n // Viewport mode: IntersectionObserver\n useEffect(() => {\n if (prefetch !== 'viewport' || !ref.current) return\n if (typeof IntersectionObserver === 'undefined') return // SSR guard\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n injectPrefetch(resolveTo(to))\n observer.disconnect()\n }\n },\n { rootMargin: '200px' }, // prefetch slightly before visible\n )\n observer.observe(ref.current)\n return () => observer.disconnect()\n }, [prefetch, to])\n\n return (\n <RouterLink\n ref={ref}\n to={to}\n onMouseEnter={handleIntent}\n onFocus={handleIntent}\n {...rest}\n />\n )\n}\n","/**\n * <Metadata> — SEO-ready head tags from a single component.\n *\n * Uses React 19's native <title>/<meta>/<link> hoisting to <head>.\n * No build-time extraction needed — works in SSR and client.\n *\n * @example\n * ```tsx\n * import { Metadata } from 'theokit/client'\n *\n * export default function ContactsPage() {\n * return (\n * <>\n * <Metadata\n * title=\"Contacts | My CRM\"\n * description=\"Manage your contacts\"\n * ogImage=\"/og/contacts.png\"\n * canonical=\"https://mycrm.com/contacts\"\n * />\n * <h1>Contacts</h1>\n * </>\n * )\n * }\n * ```\n */\n\nexport interface MetadataProps {\n title?: string\n description?: string\n canonical?: string\n ogTitle?: string\n ogDescription?: string\n ogImage?: string\n ogType?: string\n ogUrl?: string\n twitterCard?: 'summary' | 'summary_large_image'\n /** Custom meta tags rendered as children */\n children?: React.ReactNode\n}\n\nexport function Metadata(props: MetadataProps) {\n const ogTitle = props.ogTitle ?? props.title\n const ogDesc = props.ogDescription ?? props.description\n\n return (\n <>\n {props.title && <title>{props.title}</title>}\n {props.description && <meta name=\"description\" content={props.description} />}\n {props.canonical && <link rel=\"canonical\" href={props.canonical} />}\n {ogTitle && <meta property=\"og:title\" content={ogTitle} />}\n {ogDesc && <meta property=\"og:description\" content={ogDesc} />}\n {props.ogImage && <meta property=\"og:image\" content={props.ogImage} />}\n {props.ogType && <meta property=\"og:type\" content={props.ogType} />}\n {props.ogUrl && <meta property=\"og:url\" content={props.ogUrl} />}\n {props.twitterCard && <meta name=\"twitter:card\" content={props.twitterCard} />}\n {props.children}\n </>\n )\n}\n","/**\n * <Image> — optimized image component with lazy loading.\n *\n * Renders a standard <img> with:\n * - loading=\"lazy\" by default (eager with priority={true})\n * - decoding=\"async\" for non-blocking decode\n * - width/height for CLS prevention\n * - srcSet/sizes forwarded for responsive images\n *\n * No CDN, no Sharp, no build-time optimization — pure HTML attributes\n * that deliver 80% of the performance value at zero complexity.\n *\n * @example\n * ```tsx\n * import { Image } from 'theokit/client'\n *\n * <Image src=\"/team.jpg\" alt=\"Team photo\" width={800} height={600} />\n * <Image src=\"/hero.jpg\" alt=\"Hero\" priority />\n * <Image\n * src=\"/product.jpg\"\n * alt=\"Product\"\n * width={400}\n * height={300}\n * srcSet=\"/product-400.jpg 400w, /product-800.jpg 800w\"\n * sizes=\"(max-width: 768px) 100vw, 400px\"\n * />\n * ```\n */\n\nexport interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {\n /** Image source URL (required). */\n src: string\n /** Alt text for accessibility (required). */\n alt: string\n /** If true, loading=\"eager\" — use for above-the-fold images. */\n priority?: boolean\n}\n\nexport function Image({ priority, loading, decoding, ...props }: ImageProps) {\n return (\n <img\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? 'async'}\n {...props}\n />\n )\n}\n"],"mappings":"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","names":["useRef","useCallback","jsx","jsx"]}
|
|
@@ -8,12 +8,16 @@ import {
|
|
|
8
8
|
} from "./chunk-HNBWZKIQ.js";
|
|
9
9
|
import {
|
|
10
10
|
theoPluginAsync
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-ZJDKAD3L.js";
|
|
12
|
+
import "./chunk-OTFIRP6S.js";
|
|
13
|
+
import "./chunk-NBWB4S46.js";
|
|
14
|
+
import "./chunk-IEES3CHD.js";
|
|
15
|
+
import {
|
|
16
|
+
loadConfig
|
|
17
|
+
} from "./chunk-567NA7Y6.js";
|
|
13
18
|
import {
|
|
14
|
-
loadConfig,
|
|
15
19
|
loadEnv
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-GDN3PXFH.js";
|
|
17
21
|
import "./chunk-YJAUJXZS.js";
|
|
18
22
|
import {
|
|
19
23
|
orchestrateDev
|
|
@@ -21,10 +25,11 @@ import {
|
|
|
21
25
|
import "./chunk-5ODOE6EF.js";
|
|
22
26
|
import "./chunk-KXA37ONC.js";
|
|
23
27
|
import "./chunk-WR4F4EEZ.js";
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-NXTF5PPW.js";
|
|
29
|
+
import "./chunk-34YQOXGM.js";
|
|
30
|
+
import "./chunk-GBXLKYIA.js";
|
|
31
|
+
import "./chunk-P37RZRFV.js";
|
|
26
32
|
import "./chunk-HGZL5EOI.js";
|
|
27
|
-
import "./chunk-IEES3CHD.js";
|
|
28
33
|
|
|
29
34
|
// src/cli/commands/dev.ts
|
|
30
35
|
import react from "@vitejs/plugin-react";
|
|
@@ -99,4 +104,4 @@ export {
|
|
|
99
104
|
devCommand,
|
|
100
105
|
startDevServer
|
|
101
106
|
};
|
|
102
|
-
//# sourceMappingURL=dev-
|
|
107
|
+
//# sourceMappingURL=dev-TEE4T6ZB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/dev.ts"],"sourcesContent":["import react from '@vitejs/plugin-react'\nimport { createServer, type Plugin, type ViteDevServer } from 'vite'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { loadEnv } from '../../config/load-env.js'\nimport { validateProjectStructure } from '../../config/validate-structure.js'\nimport { orchestrateDev } from '../../services/index.js'\nimport { theoPluginAsync } from '../../vite-plugin/index.js'\nimport { preflightNodeAndBindings } from '../preflight-node-version.js'\n\ninterface DevOptions {\n port?: number\n}\n\nexport async function startDevServer(cwd: string, options?: DevOptions): Promise<ViteDevServer> {\n // Preflight (FIRST — BEFORE anything that touches native bindings):\n // verifies Node version + native binding ABI match. Emits actionable\n // error referencing `nvm use` + `pnpm rebuild` when the dev server\n // was spawned under the wrong Node (common gotcha with `nohup env ...`\n // stripping the nvm PATH prefix).\n preflightNodeAndBindings(cwd)\n\n // Phase 1 (T1.2) — Load .env files into process.env BEFORE any module\n // reads them. Must run before loadConfig() so theo.config.ts functions\n // referencing process.env.* resolve correctly.\n loadEnv({ cwd, mode: 'development' })\n\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n // Wave 2 (T1.1) — spawn polyglot services BEFORE Vite. Healthcheck gates\n // readiness. Empty `services: {}` → early return (Wave 1 BC preserved).\n const orchestration = await orchestrateDev({\n cwd,\n services: config.services,\n })\n if (!orchestration.allHealthy) {\n await orchestration.stop()\n throw new Error(\n `[services] services failed healthcheck: ${orchestration.unhealthy.join(', ')}. ` +\n `Check that each declared service binds its port and responds 200 on its ` +\n `healthcheck path within 30s.`,\n )\n }\n\n // Phase 7 — legacy fs-mtime sweep removed: SDK's Agent.registry handles\n // LRU + idle eviction natively (configurable via theo.config.ts >\n // agents.registry; lazy-configured on first request).\n\n const port = options?.port ?? config.port\n // Narrow the rateLimit union: only the legacy flat shape (windowMs+max)\n // flows into the theoPlugin's RateLimitConfig parameter. The per-route\n // variant is consumed inside the api-middleware via the loaded user\n // config separately.\n const flatRateLimit =\n config.rateLimit && 'windowMs' in config.rateLimit && 'max' in config.rateLimit\n ? config.rateLimit\n : undefined\n // theoPluginAsync auto-chains @theokit/ui + @tailwindcss/vite via\n // top-level Plugin[] return (sync `theoPlugin()` factory's config()\n // hook returning {plugins:[...]} is silently dropped by Vite — see\n // commit history). Spread the result so Vite flattens them.\n let server: ViteDevServer\n try {\n const theoPlugins = await theoPluginAsync({\n root: cwd,\n rateLimit: flatRateLimit,\n ssr: config.ssr,\n // Wave 2 (T3.1) — wire typed-client plugin when services declared.\n services: config.services,\n // T4.1 (canvas-ecosystem-refactor / ADR D6) — passthrough peer-deps\n // that plugins import dynamically (e.g., mermaid via plugin-canvas).\n viteOptimizeDeps: config.viteOptimizeDeps,\n })\n server = await createServer({\n root: cwd,\n plugins: [react(), ...theoPlugins] as Plugin[],\n server: {\n port,\n host: config.host,\n open: config.open,\n strictPort: config.strictPort,\n warmup: config.warmup ?? {\n clientFiles: ['./app/**/*.tsx', './app/**/*.ts'],\n },\n },\n logLevel: options?.port === 0 ? 'silent' : undefined,\n })\n\n await server.listen()\n } catch (err) {\n // EC-1 / try-finally pattern: if Vite create/listen fails AFTER services\n // started, stop them so we don't leak child processes.\n await orchestration.stop()\n throw err\n }\n\n // EC-1 fix: attach orchestration.stop() to the underlying HTTP server's\n // close event via Node-native API. We do NOT mutate `server.close`\n // (fragile across Vite upgrades). httpServer is `http.Server | null`.\n server.httpServer?.on('close', () => {\n void orchestration.stop()\n })\n\n return server\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n try {\n const cwd = process.cwd()\n const server = await startDevServer(cwd, options)\n server.printUrls()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`\\n ✗ ${msg}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/dev.ts"],"sourcesContent":["import react from '@vitejs/plugin-react'\nimport { createServer, type Plugin, type ViteDevServer } from 'vite'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { loadEnv } from '../../config/load-env.js'\nimport { validateProjectStructure } from '../../config/validate-structure.js'\nimport { orchestrateDev } from '../../services/index.js'\nimport { theoPluginAsync } from '../../vite-plugin/index.js'\nimport { preflightNodeAndBindings } from '../preflight-node-version.js'\n\ninterface DevOptions {\n port?: number\n}\n\nexport async function startDevServer(cwd: string, options?: DevOptions): Promise<ViteDevServer> {\n // Preflight (FIRST — BEFORE anything that touches native bindings):\n // verifies Node version + native binding ABI match. Emits actionable\n // error referencing `nvm use` + `pnpm rebuild` when the dev server\n // was spawned under the wrong Node (common gotcha with `nohup env ...`\n // stripping the nvm PATH prefix).\n preflightNodeAndBindings(cwd)\n\n // Phase 1 (T1.2) — Load .env files into process.env BEFORE any module\n // reads them. Must run before loadConfig() so theo.config.ts functions\n // referencing process.env.* resolve correctly.\n loadEnv({ cwd, mode: 'development' })\n\n const config = await loadConfig(cwd)\n validateProjectStructure(cwd)\n\n // Wave 2 (T1.1) — spawn polyglot services BEFORE Vite. Healthcheck gates\n // readiness. Empty `services: {}` → early return (Wave 1 BC preserved).\n const orchestration = await orchestrateDev({\n cwd,\n services: config.services,\n })\n if (!orchestration.allHealthy) {\n await orchestration.stop()\n throw new Error(\n `[services] services failed healthcheck: ${orchestration.unhealthy.join(', ')}. ` +\n `Check that each declared service binds its port and responds 200 on its ` +\n `healthcheck path within 30s.`,\n )\n }\n\n // Phase 7 — legacy fs-mtime sweep removed: SDK's Agent.registry handles\n // LRU + idle eviction natively (configurable via theo.config.ts >\n // agents.registry; lazy-configured on first request).\n\n const port = options?.port ?? config.port\n // Narrow the rateLimit union: only the legacy flat shape (windowMs+max)\n // flows into the theoPlugin's RateLimitConfig parameter. The per-route\n // variant is consumed inside the api-middleware via the loaded user\n // config separately.\n const flatRateLimit =\n config.rateLimit && 'windowMs' in config.rateLimit && 'max' in config.rateLimit\n ? config.rateLimit\n : undefined\n // theoPluginAsync auto-chains @theokit/ui + @tailwindcss/vite via\n // top-level Plugin[] return (sync `theoPlugin()` factory's config()\n // hook returning {plugins:[...]} is silently dropped by Vite — see\n // commit history). Spread the result so Vite flattens them.\n let server: ViteDevServer\n try {\n const theoPlugins = await theoPluginAsync({\n root: cwd,\n rateLimit: flatRateLimit,\n ssr: config.ssr,\n // Wave 2 (T3.1) — wire typed-client plugin when services declared.\n services: config.services,\n // T4.1 (canvas-ecosystem-refactor / ADR D6) — passthrough peer-deps\n // that plugins import dynamically (e.g., mermaid via plugin-canvas).\n viteOptimizeDeps: config.viteOptimizeDeps,\n })\n server = await createServer({\n root: cwd,\n plugins: [react(), ...theoPlugins] as Plugin[],\n server: {\n port,\n host: config.host,\n open: config.open,\n strictPort: config.strictPort,\n warmup: config.warmup ?? {\n clientFiles: ['./app/**/*.tsx', './app/**/*.ts'],\n },\n },\n logLevel: options?.port === 0 ? 'silent' : undefined,\n })\n\n await server.listen()\n } catch (err) {\n // EC-1 / try-finally pattern: if Vite create/listen fails AFTER services\n // started, stop them so we don't leak child processes.\n await orchestration.stop()\n throw err\n }\n\n // EC-1 fix: attach orchestration.stop() to the underlying HTTP server's\n // close event via Node-native API. We do NOT mutate `server.close`\n // (fragile across Vite upgrades). httpServer is `http.Server | null`.\n server.httpServer?.on('close', () => {\n void orchestration.stop()\n })\n\n return server\n}\n\nexport async function devCommand(options: DevOptions): Promise<void> {\n try {\n const cwd = process.cwd()\n const server = await startDevServer(cwd, options)\n server.printUrls()\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`\\n ✗ ${msg}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,WAAW;AAClB,SAAS,oBAAqD;AAa9D,eAAsB,eAAe,KAAa,SAA8C;AAM9F,2BAAyB,GAAG;AAK5B,UAAQ,EAAE,KAAK,MAAM,cAAc,CAAC;AAEpC,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,2BAAyB,GAAG;AAI5B,QAAM,gBAAgB,MAAM,eAAe;AAAA,IACzC;AAAA,IACA,UAAU,OAAO;AAAA,EACnB,CAAC;AACD,MAAI,CAAC,cAAc,YAAY;AAC7B,UAAM,cAAc,KAAK;AACzB,UAAM,IAAI;AAAA,MACR,2CAA2C,cAAc,UAAU,KAAK,IAAI,CAAC;AAAA,IAG/E;AAAA,EACF;AAMA,QAAM,OAAO,SAAS,QAAQ,OAAO;AAKrC,QAAM,gBACJ,OAAO,aAAa,cAAc,OAAO,aAAa,SAAS,OAAO,YAClE,OAAO,YACP;AAKN,MAAI;AACJ,MAAI;AACF,UAAM,cAAc,MAAM,gBAAgB;AAAA,MACxC,MAAM;AAAA,MACN,WAAW;AAAA,MACX,KAAK,OAAO;AAAA;AAAA,MAEZ,UAAU,OAAO;AAAA;AAAA;AAAA,MAGjB,kBAAkB,OAAO;AAAA,IAC3B,CAAC;AACD,aAAS,MAAM,aAAa;AAAA,MAC1B,MAAM;AAAA,MACN,SAAS,CAAC,MAAM,GAAG,GAAG,WAAW;AAAA,MACjC,QAAQ;AAAA,QACN;AAAA,QACA,MAAM,OAAO;AAAA,QACb,MAAM,OAAO;AAAA,QACb,YAAY,OAAO;AAAA,QACnB,QAAQ,OAAO,UAAU;AAAA,UACvB,aAAa,CAAC,kBAAkB,eAAe;AAAA,QACjD;AAAA,MACF;AAAA,MACA,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,IAC7C,CAAC;AAED,UAAM,OAAO,OAAO;AAAA,EACtB,SAAS,KAAK;AAGZ,UAAM,cAAc,KAAK;AACzB,UAAM;AAAA,EACR;AAKA,SAAO,YAAY,GAAG,SAAS,MAAM;AACnC,SAAK,cAAc,KAAK;AAAA,EAC1B,CAAC;AAED,SAAO;AACT;AAEA,eAAsB,WAAW,SAAoC;AACnE,MAAI;AACF,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,SAAS,MAAM,eAAe,KAAK,OAAO;AAChD,WAAO,UAAU;AAAA,EACnB,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAQ,MAAM;AAAA,WAAS,GAAG;AAAA,CAAI;AAC9B,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
} from "./chunk-JAIKGP3Q.js";
|
|
7
7
|
import {
|
|
8
8
|
generateManifest
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-NXTF5PPW.js";
|
|
10
|
+
import "./chunk-34YQOXGM.js";
|
|
11
|
+
import "./chunk-GBXLKYIA.js";
|
|
12
|
+
import "./chunk-P37RZRFV.js";
|
|
11
13
|
|
|
12
14
|
// src/vite-plugin/openapi-emit/dev-emit.ts
|
|
13
15
|
var inFlight = false;
|
|
@@ -39,4 +41,4 @@ export {
|
|
|
39
41
|
_resetInFlightForTests,
|
|
40
42
|
reEmitOpenApi
|
|
41
43
|
};
|
|
42
|
-
//# sourceMappingURL=dev-emit-
|
|
44
|
+
//# sourceMappingURL=dev-emit-VJ5CFMPY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin/openapi-emit/dev-emit.ts"],"sourcesContent":["/**\n * Dev-mode OpenAPI emit helper for `theokit dev`.\n *\n * Per P#3 plan v1.3 T1.1 (ADRs D3 + D4 + EC-8). Called once on `theokit dev`\n * boot AND on every chokidar 'change'/'add'/'unlink' watcher event for\n * route files when `config.openapi !== undefined`. Re-runs G2's\n * `emitOpenApi()` orchestrator to keep `<distDir>/openapi.json` always\n * fresh in dev.\n *\n * EC-8 absorbed (single-flight guard): if a previous invocation is still\n * awaiting `loadRoutesForOpenApi` (Vite SSR loader can hang on circular\n * imports or syntax errors), skip the new call and emit a `console.warn`\n * so watcher events don't pile up + dev server doesn't deadlock.\n *\n * Best-effort: ALL errors caught + warned. Never throws out of the\n * exported function (would crash Vite dev's chokidar handler).\n *\n * NEVER ship to runtime — dev-only artifact gated on `config.openapi`.\n */\nimport { generateManifest } from '../../server/scan/manifest.js'\n\nimport { emitOpenApi } from './emit.js'\nimport { loadRoutesForOpenApi } from './load-routes.js'\n\ninterface DevEmitConfig {\n servers: { url: string; description?: string }[]\n specVersion: '3.1.0' | '3.0.3'\n title: string\n version: string\n}\n\nlet inFlight = false\n\nexport async function reEmitOpenApi(\n serverDir: string,\n distDir: string,\n openApiConfig: DevEmitConfig,\n): Promise<void> {\n if (inFlight) {\n console.warn(\n '[openapi-emit] previous emit still running; skipping watcher event (EC-8 single-flight guard)',\n )\n return\n }\n inFlight = true\n try {\n const manifest = generateManifest(serverDir)\n const hydrated = await loadRoutesForOpenApi({ serverDir, routes: manifest.routes })\n emitOpenApi({\n manifest: hydrated,\n config: { ...openApiConfig, outDir: distDir },\n })\n } catch (err) {\n console.warn(`[openapi-emit] re-emit failed: ${(err as Error).message}`)\n } finally {\n inFlight = false\n }\n}\n\n/** Test seam — resets the inFlight flag between tests. NOT for production use. */\nexport function _resetInFlightForTests(): void {\n inFlight = false\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/openapi-emit/dev-emit.ts"],"sourcesContent":["/**\n * Dev-mode OpenAPI emit helper for `theokit dev`.\n *\n * Per P#3 plan v1.3 T1.1 (ADRs D3 + D4 + EC-8). Called once on `theokit dev`\n * boot AND on every chokidar 'change'/'add'/'unlink' watcher event for\n * route files when `config.openapi !== undefined`. Re-runs G2's\n * `emitOpenApi()` orchestrator to keep `<distDir>/openapi.json` always\n * fresh in dev.\n *\n * EC-8 absorbed (single-flight guard): if a previous invocation is still\n * awaiting `loadRoutesForOpenApi` (Vite SSR loader can hang on circular\n * imports or syntax errors), skip the new call and emit a `console.warn`\n * so watcher events don't pile up + dev server doesn't deadlock.\n *\n * Best-effort: ALL errors caught + warned. Never throws out of the\n * exported function (would crash Vite dev's chokidar handler).\n *\n * NEVER ship to runtime — dev-only artifact gated on `config.openapi`.\n */\nimport { generateManifest } from '../../server/scan/manifest.js'\n\nimport { emitOpenApi } from './emit.js'\nimport { loadRoutesForOpenApi } from './load-routes.js'\n\ninterface DevEmitConfig {\n servers: { url: string; description?: string }[]\n specVersion: '3.1.0' | '3.0.3'\n title: string\n version: string\n}\n\nlet inFlight = false\n\nexport async function reEmitOpenApi(\n serverDir: string,\n distDir: string,\n openApiConfig: DevEmitConfig,\n): Promise<void> {\n if (inFlight) {\n console.warn(\n '[openapi-emit] previous emit still running; skipping watcher event (EC-8 single-flight guard)',\n )\n return\n }\n inFlight = true\n try {\n const manifest = generateManifest(serverDir)\n const hydrated = await loadRoutesForOpenApi({ serverDir, routes: manifest.routes })\n emitOpenApi({\n manifest: hydrated,\n config: { ...openApiConfig, outDir: distDir },\n })\n } catch (err) {\n console.warn(`[openapi-emit] re-emit failed: ${(err as Error).message}`)\n } finally {\n inFlight = false\n }\n}\n\n/** Test seam — resets the inFlight flag between tests. NOT for production use. */\nexport function _resetInFlightForTests(): void {\n inFlight = false\n}\n"],"mappings":";;;;;;;;;;;;;;AA+BA,IAAI,WAAW;AAEf,eAAsB,cACpB,WACA,SACA,eACe;AACf,MAAI,UAAU;AACZ,YAAQ;AAAA,MACN;AAAA,IACF;AACA;AAAA,EACF;AACA,aAAW;AACX,MAAI;AACF,UAAM,WAAW,iBAAiB,SAAS;AAC3C,UAAM,WAAW,MAAM,qBAAqB,EAAE,WAAW,QAAQ,SAAS,OAAO,CAAC;AAClF,gBAAY;AAAA,MACV,UAAU;AAAA,MACV,QAAQ,EAAE,GAAG,eAAe,QAAQ,QAAQ;AAAA,IAC9C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,KAAK,kCAAmC,IAAc,OAAO,EAAE;AAAA,EACzE,UAAE;AACA,eAAW;AAAA,EACb;AACF;AAGO,SAAS,yBAA+B;AAC7C,aAAW;AACb;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -8,31 +8,30 @@ import {
|
|
|
8
8
|
scanRoutes,
|
|
9
9
|
theoConfigSchema,
|
|
10
10
|
theoPlugin
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-7BIM27LV.js";
|
|
13
|
-
import "./chunk-RSVN727G.js";
|
|
11
|
+
} from "./chunk-UOR6JTCI.js";
|
|
14
12
|
import "./chunk-D7ZH7DTG.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-RSVN727G.js";
|
|
14
|
+
import "./chunk-7BIM27LV.js";
|
|
15
|
+
import "./chunk-5UUOGAJH.js";
|
|
18
16
|
import "./chunk-5QW7IQQU.js";
|
|
19
|
-
import "./chunk-WFNLNIJX.js";
|
|
20
17
|
import "./chunk-IAJ2JVEH.js";
|
|
21
18
|
import "./chunk-VMEWD57H.js";
|
|
22
19
|
import "./chunk-JQSKBMXP.js";
|
|
23
20
|
import "./chunk-2J7XU3PW.js";
|
|
24
21
|
import "./chunk-WSJKACWB.js";
|
|
25
|
-
import "./chunk-ZEGYW52B.js";
|
|
26
22
|
import "./chunk-JBCHWRKF.js";
|
|
23
|
+
import "./chunk-C3ZZ56YZ.js";
|
|
24
|
+
import "./chunk-7MQOHNHE.js";
|
|
25
|
+
import "./chunk-X2VVCJ4V.js";
|
|
26
|
+
import "./chunk-JZHT3LW7.js";
|
|
27
|
+
import "./chunk-H243MR3F.js";
|
|
28
|
+
import "./chunk-GY5Q27BJ.js";
|
|
29
|
+
import "./chunk-Z5IGLBWE.js";
|
|
30
|
+
import "./chunk-ZEGYW52B.js";
|
|
27
31
|
import "./chunk-TSLSIRBR.js";
|
|
28
32
|
import "./chunk-UD3LFGDL.js";
|
|
29
33
|
import "./chunk-X32XEJXR.js";
|
|
30
34
|
import "./chunk-UVXB2ER7.js";
|
|
31
|
-
import "./chunk-EXP56GFQ.js";
|
|
32
|
-
import "./chunk-C3ZZ56YZ.js";
|
|
33
|
-
import "./chunk-7MQOHNHE.js";
|
|
34
|
-
import "./chunk-X2VVCJ4V.js";
|
|
35
|
-
import "./chunk-NHJMZCAS.js";
|
|
36
35
|
import "./chunk-DGUM43GV.js";
|
|
37
36
|
|
|
38
37
|
// src/config/define-config.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/define-config.ts","../src/config/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["import type { TheoConfig } from './schema.js'\n\n/**\n * Define Theo framework configuration.\n * Identity function — provides type inference for theo.config.ts.\n * Runtime validation happens in loadConfig(), not here.\n */\nexport function defineConfig(config: Partial<TheoConfig>): Partial<TheoConfig> {\n return config\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Project-structure validator. Reads paths joined onto `projectRoot`,\n * with names from a fixed `ValidationRule[]` table. No HTTP input.\n */\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { TheoProjectError } from '../core/errors.js'\n\ninterface ValidationRule {\n path: string\n errorMessage: string\n}\n\nconst REQUIRED_DIRS: ValidationRule[] = [\n {\n path: 'app',\n errorMessage: 'Missing required directory: app/',\n },\n]\n\nconst REQUIRED_FILES: ValidationRule[] = [\n {\n path: 'theo.config.ts',\n errorMessage: 'Missing required file: theo.config.ts',\n },\n {\n path: 'package.json',\n errorMessage: 'Missing required file: package.json',\n },\n]\n\nexport function validateProjectStructure(rootDir: string): void {\n if (!existsSync(rootDir)) {\n throw new TheoProjectError([`Project directory does not exist: ${rootDir}`], rootDir)\n }\n\n const errors: string[] = []\n\n for (const rule of REQUIRED_DIRS) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n for (const rule of REQUIRED_FILES) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n if (errors.length > 0) {\n throw new TheoProjectError(errors, rootDir)\n }\n}\n","export class TheoProjectError extends Error {\n public readonly errors: string[]\n public readonly rootDir: string\n\n constructor(errors: string[], rootDir: string) {\n const errorLines = errors.map((e) => ` - ${e}`).join('\\n')\n\n super(\n `Invalid Theo project structure\\n\\n` +\n ` Root: ${rootDir}\\n\\n` +\n (errorLines ? ` Errors:\\n${errorLines}\\n` : ''),\n )\n\n this.name = 'TheoProjectError'\n this.errors = errors\n this.rootDir = rootDir\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/config/define-config.ts","../src/config/validate-structure.ts","../src/core/errors.ts"],"sourcesContent":["import type { TheoConfig } from './schema.js'\n\n/**\n * Define Theo framework configuration.\n * Identity function — provides type inference for theo.config.ts.\n * Runtime validation happens in loadConfig(), not here.\n */\nexport function defineConfig(config: Partial<TheoConfig>): Partial<TheoConfig> {\n return config\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Project-structure validator. Reads paths joined onto `projectRoot`,\n * with names from a fixed `ValidationRule[]` table. No HTTP input.\n */\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { TheoProjectError } from '../core/errors.js'\n\ninterface ValidationRule {\n path: string\n errorMessage: string\n}\n\nconst REQUIRED_DIRS: ValidationRule[] = [\n {\n path: 'app',\n errorMessage: 'Missing required directory: app/',\n },\n]\n\nconst REQUIRED_FILES: ValidationRule[] = [\n {\n path: 'theo.config.ts',\n errorMessage: 'Missing required file: theo.config.ts',\n },\n {\n path: 'package.json',\n errorMessage: 'Missing required file: package.json',\n },\n]\n\nexport function validateProjectStructure(rootDir: string): void {\n if (!existsSync(rootDir)) {\n throw new TheoProjectError([`Project directory does not exist: ${rootDir}`], rootDir)\n }\n\n const errors: string[] = []\n\n for (const rule of REQUIRED_DIRS) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n for (const rule of REQUIRED_FILES) {\n if (!existsSync(join(rootDir, rule.path))) {\n errors.push(rule.errorMessage)\n }\n }\n\n if (errors.length > 0) {\n throw new TheoProjectError(errors, rootDir)\n }\n}\n","export class TheoProjectError extends Error {\n public readonly errors: string[]\n public readonly rootDir: string\n\n constructor(errors: string[], rootDir: string) {\n const errorLines = errors.map((e) => ` - ${e}`).join('\\n')\n\n super(\n `Invalid Theo project structure\\n\\n` +\n ` Root: ${rootDir}\\n\\n` +\n (errorLines ? ` Errors:\\n${errorLines}\\n` : ''),\n )\n\n this.name = 'TheoProjectError'\n this.errors = errors\n this.rootDir = rootDir\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,SAAS,aAAa,QAAkD;AAC7E,SAAO;AACT;;;ACLA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;;;ACLd,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAEhB,YAAY,QAAkB,SAAiB;AAC7C,UAAM,aAAa,OAAO,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI;AAE1D;AAAA,MACE;AAAA;AAAA,UACa,OAAO;AAAA;AAAA,KACjB,aAAa;AAAA,EAAc,UAAU;AAAA,IAAO;AAAA,IACjD;AAEA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AACF;;;ADHA,IAAM,gBAAkC;AAAA,EACtC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEA,IAAM,iBAAmC;AAAA,EACvC;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,cAAc;AAAA,EAChB;AACF;AAEO,SAAS,yBAAyB,SAAuB;AAC9D,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,UAAM,IAAI,iBAAiB,CAAC,qCAAqC,OAAO,EAAE,GAAG,OAAO;AAAA,EACtF;AAEA,QAAM,SAAmB,CAAC;AAE1B,aAAW,QAAQ,eAAe;AAChC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,aAAW,QAAQ,gBAAgB;AACjC,QAAI,CAAC,WAAW,KAAK,SAAS,KAAK,IAAI,CAAC,GAAG;AACzC,aAAO,KAAK,KAAK,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI,iBAAiB,QAAQ,OAAO;AAAA,EAC5C;AACF;","names":[]}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "tsx/esm";
|
|
3
|
+
import {
|
|
4
|
+
scanRoutes
|
|
5
|
+
} from "./chunk-IEES3CHD.js";
|
|
3
6
|
import {
|
|
4
7
|
loadConfig
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-567NA7Y6.js";
|
|
9
|
+
import "./chunk-GDN3PXFH.js";
|
|
6
10
|
import "./chunk-YJAUJXZS.js";
|
|
7
11
|
import "./chunk-45C3WUQ7.js";
|
|
8
|
-
import {
|
|
9
|
-
scanRoutes
|
|
10
|
-
} from "./chunk-IEES3CHD.js";
|
|
11
12
|
|
|
12
13
|
// src/cli/commands/info.ts
|
|
13
14
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -100,4 +101,4 @@ export {
|
|
|
100
101
|
buildInfo,
|
|
101
102
|
infoCommand
|
|
102
103
|
};
|
|
103
|
-
//# sourceMappingURL=info-
|
|
104
|
+
//# sourceMappingURL=info-7PE2PZJI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/info.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI `theo info` reader. Reads `package.json`, `theo.config.*`, lock\n * files under the user's `cwd`. Read-only build-time tool. No HTTP input.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { scanRoutes } from '../../router/scan.js'\n\nexport interface RuntimeInfo {\n name: 'node' | 'bun' | 'deno' | 'unknown'\n version: string\n}\n\nexport interface PackageJsonMinimal {\n name?: string\n version?: string\n}\n\nexport interface ConfigSummary {\n ok: boolean\n summary: string\n}\n\nexport interface BuildInfoDeps {\n cwd: string\n readPackageJson?: () => PackageJsonMinimal | null\n detectRuntime?: () => RuntimeInfo\n loadConfig?: () => Promise<ConfigSummary>\n countRoutes?: () => number\n}\n\nfunction defaultDetectRuntime(): RuntimeInfo {\n const g = globalThis as { Bun?: { version: string }; Deno?: { version: { deno: string } } }\n if (g.Bun?.version) {\n return { name: 'bun', version: g.Bun.version }\n }\n if (g.Deno?.version) {\n return { name: 'deno', version: g.Deno.version.deno }\n }\n if (typeof process !== 'undefined' && process.versions.node) {\n return { name: 'node', version: process.versions.node }\n }\n return { name: 'unknown', version: '?' }\n}\n\nfunction defaultReadPackageJson(cwd: string): PackageJsonMinimal | null {\n const path = resolve(cwd, 'package.json')\n if (!existsSync(path)) return null\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as PackageJsonMinimal\n return parsed\n } catch {\n return null\n }\n}\n\nasync function defaultLoadConfig(cwd: string): Promise<ConfigSummary> {\n try {\n await loadConfig(cwd)\n return { ok: true, summary: 'config OK' }\n } catch (err) {\n return { ok: false, summary: (err as Error).message }\n }\n}\n\nfunction defaultCountRoutes(cwd: string): number {\n const appDir = resolve(cwd, 'app')\n if (!existsSync(appDir)) return 0\n const tree = scanRoutes(appDir)\n let count = 0\n function walk(node: { page?: string; children: { page?: string; children: unknown[] }[] }): void {\n if (node.page) count++\n for (const child of node.children) walk(child as never)\n }\n walk(tree)\n return count\n}\n\nexport async function buildInfo(deps: BuildInfoDeps): Promise<string> {\n const cwd = deps.cwd\n const readPkg = deps.readPackageJson ?? (() => defaultReadPackageJson(cwd))\n const runtime = (deps.detectRuntime ?? defaultDetectRuntime)()\n const loadCfg = deps.loadConfig ?? (() => defaultLoadConfig(cwd))\n const countRts = deps.countRoutes ?? (() => defaultCountRoutes(cwd))\n\n const pkg = readPkg()\n const config = await loadCfg()\n\n let routesCount = 0\n let scanError: string | null = null\n try {\n routesCount = countRts()\n } catch (err) {\n scanError = (err as Error).message\n }\n\n const projectLine = pkg?.name ? `${pkg.name}@${pkg.version ?? '?'}` : '(missing)'\n\n const configLine = config.ok ? 'OK' : `INVALID — ${config.summary}`\n\n const routesLine = scanError ? `Scan failed: ${scanError}` : `Routes: ${routesCount}`\n\n return [\n `# Theo info`,\n ``,\n `## Project`,\n `Project: ${projectLine}`,\n ``,\n `## Runtime`,\n `${runtime.name} ${runtime.version}`,\n ``,\n `## Config`,\n `Config: ${configLine}`,\n ``,\n `## App`,\n routesLine,\n ``,\n ].join('\\n')\n}\n\nexport async function infoCommand(): Promise<void> {\n const cwd = process.cwd()\n const output = await buildInfo({ cwd })\n console.log(output)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/info.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI `theo info` reader. Reads `package.json`, `theo.config.*`, lock\n * files under the user's `cwd`. Read-only build-time tool. No HTTP input.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { loadConfig } from '../../config/load-config.js'\nimport { scanRoutes } from '../../router/scan.js'\n\nexport interface RuntimeInfo {\n name: 'node' | 'bun' | 'deno' | 'unknown'\n version: string\n}\n\nexport interface PackageJsonMinimal {\n name?: string\n version?: string\n}\n\nexport interface ConfigSummary {\n ok: boolean\n summary: string\n}\n\nexport interface BuildInfoDeps {\n cwd: string\n readPackageJson?: () => PackageJsonMinimal | null\n detectRuntime?: () => RuntimeInfo\n loadConfig?: () => Promise<ConfigSummary>\n countRoutes?: () => number\n}\n\nfunction defaultDetectRuntime(): RuntimeInfo {\n const g = globalThis as { Bun?: { version: string }; Deno?: { version: { deno: string } } }\n if (g.Bun?.version) {\n return { name: 'bun', version: g.Bun.version }\n }\n if (g.Deno?.version) {\n return { name: 'deno', version: g.Deno.version.deno }\n }\n if (typeof process !== 'undefined' && process.versions.node) {\n return { name: 'node', version: process.versions.node }\n }\n return { name: 'unknown', version: '?' }\n}\n\nfunction defaultReadPackageJson(cwd: string): PackageJsonMinimal | null {\n const path = resolve(cwd, 'package.json')\n if (!existsSync(path)) return null\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as PackageJsonMinimal\n return parsed\n } catch {\n return null\n }\n}\n\nasync function defaultLoadConfig(cwd: string): Promise<ConfigSummary> {\n try {\n await loadConfig(cwd)\n return { ok: true, summary: 'config OK' }\n } catch (err) {\n return { ok: false, summary: (err as Error).message }\n }\n}\n\nfunction defaultCountRoutes(cwd: string): number {\n const appDir = resolve(cwd, 'app')\n if (!existsSync(appDir)) return 0\n const tree = scanRoutes(appDir)\n let count = 0\n function walk(node: { page?: string; children: { page?: string; children: unknown[] }[] }): void {\n if (node.page) count++\n for (const child of node.children) walk(child as never)\n }\n walk(tree)\n return count\n}\n\nexport async function buildInfo(deps: BuildInfoDeps): Promise<string> {\n const cwd = deps.cwd\n const readPkg = deps.readPackageJson ?? (() => defaultReadPackageJson(cwd))\n const runtime = (deps.detectRuntime ?? defaultDetectRuntime)()\n const loadCfg = deps.loadConfig ?? (() => defaultLoadConfig(cwd))\n const countRts = deps.countRoutes ?? (() => defaultCountRoutes(cwd))\n\n const pkg = readPkg()\n const config = await loadCfg()\n\n let routesCount = 0\n let scanError: string | null = null\n try {\n routesCount = countRts()\n } catch (err) {\n scanError = (err as Error).message\n }\n\n const projectLine = pkg?.name ? `${pkg.name}@${pkg.version ?? '?'}` : '(missing)'\n\n const configLine = config.ok ? 'OK' : `INVALID — ${config.summary}`\n\n const routesLine = scanError ? `Scan failed: ${scanError}` : `Routes: ${routesCount}`\n\n return [\n `# Theo info`,\n ``,\n `## Project`,\n `Project: ${projectLine}`,\n ``,\n `## Runtime`,\n `${runtime.name} ${runtime.version}`,\n ``,\n `## Config`,\n `Config: ${configLine}`,\n ``,\n `## App`,\n routesLine,\n ``,\n ].join('\\n')\n}\n\nexport async function infoCommand(): Promise<void> {\n const cwd = process.cwd()\n const output = await buildInfo({ cwd })\n console.log(output)\n}\n"],"mappings":";;;;;;;;;;;;;AAIA,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AA4BxB,SAAS,uBAAoC;AAC3C,QAAM,IAAI;AACV,MAAI,EAAE,KAAK,SAAS;AAClB,WAAO,EAAE,MAAM,OAAO,SAAS,EAAE,IAAI,QAAQ;AAAA,EAC/C;AACA,MAAI,EAAE,MAAM,SAAS;AACnB,WAAO,EAAE,MAAM,QAAQ,SAAS,EAAE,KAAK,QAAQ,KAAK;AAAA,EACtD;AACA,MAAI,OAAO,YAAY,eAAe,QAAQ,SAAS,MAAM;AAC3D,WAAO,EAAE,MAAM,QAAQ,SAAS,QAAQ,SAAS,KAAK;AAAA,EACxD;AACA,SAAO,EAAE,MAAM,WAAW,SAAS,IAAI;AACzC;AAEA,SAAS,uBAAuB,KAAwC;AACtE,QAAM,OAAO,QAAQ,KAAK,cAAc;AACxC,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAM,aAAa,MAAM,OAAO;AACtC,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,kBAAkB,KAAqC;AACpE,MAAI;AACF,UAAM,WAAW,GAAG;AACpB,WAAO,EAAE,IAAI,MAAM,SAAS,YAAY;AAAA,EAC1C,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,SAAU,IAAc,QAAQ;AAAA,EACtD;AACF;AAEA,SAAS,mBAAmB,KAAqB;AAC/C,QAAM,SAAS,QAAQ,KAAK,KAAK;AACjC,MAAI,CAAC,WAAW,MAAM,EAAG,QAAO;AAChC,QAAM,OAAO,WAAW,MAAM;AAC9B,MAAI,QAAQ;AACZ,WAAS,KAAK,MAAmF;AAC/F,QAAI,KAAK,KAAM;AACf,eAAW,SAAS,KAAK,SAAU,MAAK,KAAc;AAAA,EACxD;AACA,OAAK,IAAI;AACT,SAAO;AACT;AAEA,eAAsB,UAAU,MAAsC;AACpE,QAAM,MAAM,KAAK;AACjB,QAAM,UAAU,KAAK,oBAAoB,MAAM,uBAAuB,GAAG;AACzE,QAAM,WAAW,KAAK,iBAAiB,sBAAsB;AAC7D,QAAM,UAAU,KAAK,eAAe,MAAM,kBAAkB,GAAG;AAC/D,QAAM,WAAW,KAAK,gBAAgB,MAAM,mBAAmB,GAAG;AAElE,QAAM,MAAM,QAAQ;AACpB,QAAM,SAAS,MAAM,QAAQ;AAE7B,MAAI,cAAc;AAClB,MAAI,YAA2B;AAC/B,MAAI;AACF,kBAAc,SAAS;AAAA,EACzB,SAAS,KAAK;AACZ,gBAAa,IAAc;AAAA,EAC7B;AAEA,QAAM,cAAc,KAAK,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,WAAW,GAAG,KAAK;AAEtE,QAAM,aAAa,OAAO,KAAK,OAAO,kBAAa,OAAO,OAAO;AAEjE,QAAM,aAAa,YAAY,gBAAgB,SAAS,KAAK,WAAW,WAAW;AAEnF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,WAAW;AAAA,IACvB;AAAA,IACA;AAAA,IACA,GAAG,QAAQ,IAAI,IAAI,QAAQ,OAAO;AAAA,IAClC;AAAA,IACA;AAAA,IACA,WAAW,UAAU;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,cAA6B;AACjD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,SAAS,MAAM,UAAU,EAAE,IAAI,CAAC;AACtC,UAAQ,IAAI,MAAM;AACpB;","names":[]}
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
import "./chunk-RSVN727G.js";
|
|
2
|
-
import {
|
|
3
|
-
BATCH_PATH,
|
|
4
|
-
TRACE_HEADER,
|
|
5
|
-
createCorsHandler,
|
|
6
|
-
executeAction,
|
|
7
|
-
executeRoute,
|
|
8
|
-
extractTraceId,
|
|
9
|
-
handleBatchRequest,
|
|
10
|
-
sendError
|
|
11
|
-
} from "./chunk-TGTNRUH3.js";
|
|
12
|
-
import "./chunk-GY5Q27BJ.js";
|
|
13
2
|
import {
|
|
14
3
|
findSuggestion
|
|
15
4
|
} from "./chunk-5QW7IQQU.js";
|
|
16
|
-
import {
|
|
17
|
-
logRequest
|
|
18
|
-
} from "./chunk-WFNLNIJX.js";
|
|
19
5
|
import {
|
|
20
6
|
createPluginRunnerFromConfig
|
|
21
7
|
} from "./chunk-IAJ2JVEH.js";
|
|
@@ -35,7 +21,6 @@ import {
|
|
|
35
21
|
scanWebSocketRoutes
|
|
36
22
|
} from "./chunk-2J7XU3PW.js";
|
|
37
23
|
import "./chunk-WSJKACWB.js";
|
|
38
|
-
import "./chunk-ZEGYW52B.js";
|
|
39
24
|
import {
|
|
40
25
|
CSP_REPORT_PATH,
|
|
41
26
|
CsrfReadinessStore,
|
|
@@ -43,14 +28,29 @@ import {
|
|
|
43
28
|
handleCspReport,
|
|
44
29
|
handleCsrfReadiness
|
|
45
30
|
} from "./chunk-JBCHWRKF.js";
|
|
46
|
-
import "./chunk-TSLSIRBR.js";
|
|
47
|
-
import "./chunk-UD3LFGDL.js";
|
|
48
|
-
import "./chunk-X32XEJXR.js";
|
|
49
|
-
import "./chunk-UVXB2ER7.js";
|
|
50
31
|
import {
|
|
51
32
|
generateNonce
|
|
52
33
|
} from "./chunk-C3ZZ56YZ.js";
|
|
53
34
|
import "./chunk-X2VVCJ4V.js";
|
|
35
|
+
import {
|
|
36
|
+
BATCH_PATH,
|
|
37
|
+
TRACE_HEADER,
|
|
38
|
+
createCorsHandler,
|
|
39
|
+
executeAction,
|
|
40
|
+
executeRoute,
|
|
41
|
+
extractTraceId,
|
|
42
|
+
handleBatchRequest,
|
|
43
|
+
sendError
|
|
44
|
+
} from "./chunk-H243MR3F.js";
|
|
45
|
+
import "./chunk-GY5Q27BJ.js";
|
|
46
|
+
import {
|
|
47
|
+
logRequest
|
|
48
|
+
} from "./chunk-Z5IGLBWE.js";
|
|
49
|
+
import "./chunk-ZEGYW52B.js";
|
|
50
|
+
import "./chunk-TSLSIRBR.js";
|
|
51
|
+
import "./chunk-UD3LFGDL.js";
|
|
52
|
+
import "./chunk-X32XEJXR.js";
|
|
53
|
+
import "./chunk-UVXB2ER7.js";
|
|
54
54
|
import "./chunk-DGUM43GV.js";
|
|
55
55
|
export {
|
|
56
56
|
BATCH_PATH,
|
|
@@ -80,4 +80,4 @@ export {
|
|
|
80
80
|
scanWebSocketRoutes,
|
|
81
81
|
sendError
|
|
82
82
|
};
|
|
83
|
-
//# sourceMappingURL=internal-api-
|
|
83
|
+
//# sourceMappingURL=internal-api-5BDI6N5U.js.map
|