@meekojs/meeko 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +21 -19
  2. package/dist/autoimports-ts.d.ts +4 -4
  3. package/dist/autoimports-ts.js +86 -30
  4. package/dist/autoimports-ts.js.map +1 -1
  5. package/dist/autoimports.d.ts +2 -2
  6. package/dist/autoimports.js +23 -7
  7. package/dist/autoimports.js.map +1 -1
  8. package/dist/check.js +39 -10
  9. package/dist/check.js.map +1 -1
  10. package/dist/cli.js +37 -4
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compiler/events.d.ts +2 -0
  13. package/dist/compiler/events.js +6 -0
  14. package/dist/compiler/events.js.map +1 -0
  15. package/dist/compiler/props.d.ts +12 -0
  16. package/dist/compiler/props.js +32 -0
  17. package/dist/compiler/props.js.map +1 -0
  18. package/dist/compiler.js +55 -12
  19. package/dist/compiler.js.map +1 -1
  20. package/dist/component-errors.d.ts +18 -0
  21. package/dist/component-errors.js +65 -0
  22. package/dist/component-errors.js.map +1 -0
  23. package/dist/devtools.js +2 -4
  24. package/dist/devtools.js.map +1 -1
  25. package/dist/dom.d.ts +11 -5
  26. package/dist/dom.js +288 -88
  27. package/dist/dom.js.map +1 -1
  28. package/dist/error-observer.d.ts +6 -0
  29. package/dist/error-observer.js +41 -0
  30. package/dist/error-observer.js.map +1 -0
  31. package/dist/framework/check-app.js +160 -5
  32. package/dist/framework/check-app.js.map +1 -1
  33. package/dist/framework/check-project.d.ts +4 -0
  34. package/dist/framework/check-project.js +42 -0
  35. package/dist/framework/check-project.js.map +1 -0
  36. package/dist/framework/client.d.ts +3 -0
  37. package/dist/framework/client.js +136 -33
  38. package/dist/framework/client.js.map +1 -1
  39. package/dist/framework/clients.js +12 -4
  40. package/dist/framework/clients.js.map +1 -1
  41. package/dist/framework/contracts-generate.js +66 -3
  42. package/dist/framework/contracts-generate.js.map +1 -1
  43. package/dist/framework/init.js +21 -200
  44. package/dist/framework/init.js.map +1 -1
  45. package/dist/framework/middleware.d.ts +14 -0
  46. package/dist/framework/middleware.js +16 -0
  47. package/dist/framework/middleware.js.map +1 -0
  48. package/dist/framework/page-contracts-check.d.ts +8 -0
  49. package/dist/framework/page-contracts-check.js +110 -0
  50. package/dist/framework/page-contracts-check.js.map +1 -0
  51. package/dist/framework/page-contracts-generate.d.ts +2 -0
  52. package/dist/framework/page-contracts-generate.js +94 -0
  53. package/dist/framework/page-contracts-generate.js.map +1 -0
  54. package/dist/framework/page.d.ts +7 -3
  55. package/dist/framework/page.js +3 -1
  56. package/dist/framework/page.js.map +1 -1
  57. package/dist/framework/prerender.js +11 -5
  58. package/dist/framework/prerender.js.map +1 -1
  59. package/dist/framework/project-templates.d.ts +3 -0
  60. package/dist/framework/project-templates.js +119 -0
  61. package/dist/framework/project-templates.js.map +1 -0
  62. package/dist/framework/project.d.ts +9 -0
  63. package/dist/framework/project.js +272 -0
  64. package/dist/framework/project.js.map +1 -0
  65. package/dist/framework/router.d.ts +3 -1
  66. package/dist/framework/router.js +94 -8
  67. package/dist/framework/router.js.map +1 -1
  68. package/dist/framework/server.d.ts +17 -3
  69. package/dist/framework/server.js +71 -24
  70. package/dist/framework/server.js.map +1 -1
  71. package/dist/framework/socket-contracts-generate.js +8 -5
  72. package/dist/framework/socket-contracts-generate.js.map +1 -1
  73. package/dist/framework/socket.d.ts +2 -0
  74. package/dist/framework/socket.js +119 -17
  75. package/dist/framework/socket.js.map +1 -1
  76. package/dist/framework/standard-schema-types.d.ts +7 -0
  77. package/dist/framework/standard-schema-types.js +69 -0
  78. package/dist/framework/standard-schema-types.js.map +1 -0
  79. package/dist/framework/state.d.ts +7 -0
  80. package/dist/framework/state.js +78 -14
  81. package/dist/framework/state.js.map +1 -1
  82. package/dist/framework/styles.d.ts +2 -0
  83. package/dist/framework/styles.js +6 -0
  84. package/dist/framework/styles.js.map +1 -0
  85. package/dist/framework/type-declarations.d.ts +5 -0
  86. package/dist/framework/type-declarations.js +84 -0
  87. package/dist/framework/type-declarations.js.map +1 -0
  88. package/dist/framework-server/contracts.d.ts +11 -2
  89. package/dist/framework-server/contracts.js +62 -7
  90. package/dist/framework-server/contracts.js.map +1 -1
  91. package/dist/framework-server/discovery.d.ts +4 -1
  92. package/dist/framework-server/discovery.js +21 -16
  93. package/dist/framework-server/discovery.js.map +1 -1
  94. package/dist/framework-server/local.d.ts +2 -0
  95. package/dist/framework-server/local.js +5 -3
  96. package/dist/framework-server/local.js.map +1 -1
  97. package/dist/framework-server/middleware.d.ts +4 -0
  98. package/dist/framework-server/middleware.js +56 -0
  99. package/dist/framework-server/middleware.js.map +1 -0
  100. package/dist/framework-server/node-upgrade.js +27 -9
  101. package/dist/framework-server/node-upgrade.js.map +1 -1
  102. package/dist/framework-server/node-websocket.js +4 -2
  103. package/dist/framework-server/node-websocket.js.map +1 -1
  104. package/dist/framework-server/session-stores.d.ts +9 -2
  105. package/dist/framework-server/session-stores.js +37 -11
  106. package/dist/framework-server/session-stores.js.map +1 -1
  107. package/dist/framework-server/sessions.d.ts +6 -0
  108. package/dist/framework-server/sessions.js +41 -10
  109. package/dist/framework-server/sessions.js.map +1 -1
  110. package/dist/framework-server/socket.js +62 -27
  111. package/dist/framework-server/socket.js.map +1 -1
  112. package/dist/framework-server.d.ts +2 -0
  113. package/dist/framework-server.js +1 -0
  114. package/dist/framework-server.js.map +1 -1
  115. package/dist/framework.d.ts +3 -1
  116. package/dist/framework.js +1 -0
  117. package/dist/framework.js.map +1 -1
  118. package/dist/hmr.js +74 -24
  119. package/dist/hmr.js.map +1 -1
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +1 -0
  122. package/dist/index.js.map +1 -1
  123. package/dist/init.js +27 -1
  124. package/dist/init.js.map +1 -1
  125. package/dist/internal.d.ts +2 -2
  126. package/dist/internal.js +2 -2
  127. package/dist/internal.js.map +1 -1
  128. package/dist/node.d.ts +5 -1
  129. package/dist/node.js +80 -10
  130. package/dist/node.js.map +1 -1
  131. package/dist/reactivity.d.ts +12 -3
  132. package/dist/reactivity.js +94 -17
  133. package/dist/reactivity.js.map +1 -1
  134. package/dist/types.d.ts +2 -0
  135. package/dist/types.js +3 -1
  136. package/dist/types.js.map +1 -1
  137. package/dist/vite.js +29 -9
  138. package/dist/vite.js.map +1 -1
  139. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/framework/socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { onCleanup, signal } from \"../reactivity.js\";\nimport type { ReadonlySignal } from \"../reactivity.js\";\nimport { serializeJson } from \"../server.js\";\n\nexport interface SocketSchema<I = unknown, O = I> {\n readonly \"~standard\": {\n readonly version: 1;\n readonly validate: (value: unknown) => { value: O; issues?: undefined } | { issues: readonly unknown[] } | Promise<{ value: O; issues?: undefined } | { issues: readonly unknown[] }>;\n readonly types?: { readonly input: I; readonly output: O } | undefined;\n };\n}\n\ntype SchemaInput<T> = T extends { readonly \"~standard\": { readonly types?: infer U } }\n ? NonNullable<U> extends { readonly input: infer I } ? I : never\n : T extends SocketSchema<infer I, unknown> ? I : never;\ntype SchemaOutput<T> = T extends { readonly \"~standard\": { readonly types?: infer U } }\n ? NonNullable<U> extends { readonly output: infer O } ? O : never\n : T extends SocketSchema<unknown, infer O> ? O : never;\ntype Events = Record<string, SocketSchema>;\n\nexport interface SocketContract<ClientToServer extends Events = Events, ServerToClient extends Events = Events> {\n readonly clientToServer: ClientToServer;\n readonly serverToClient: ServerToClient;\n}\n\nexport interface SocketClient<C extends SocketContract> {\n readonly state: ReadonlySignal<\"connecting\" | \"open\" | \"closed\">;\n emit<K extends keyof C[\"clientToServer\"] & string>(event: K, data: SchemaInput<C[\"clientToServer\"][K]>): Promise<void>;\n on<K extends keyof C[\"serverToClient\"] & string>(event: K, listener: (data: SchemaOutput<C[\"serverToClient\"][K]>) => void): () => void;\n close(): void;\n}\n\nexport interface SocketClientOptions {\n readonly retryMs?: number;\n readonly maxRetryMs?: number;\n readonly maxMessageBytes?: number;\n readonly webSocketFactory?: (url: string) => WebSocket;\n}\n\nexport interface SocketRegistryOptions extends SocketClientOptions {\n /** HTTP(S) or WS(S) base URL. Defaults to the current browser origin. */\n readonly baseUrl?: string;\n readonly pathnamePrefix?: string;\n readonly roomId?: string;\n}\n\nexport type SocketRegistry<C extends Record<string, SocketContract>> = {\n readonly [K in keyof C]: SocketClient<C[K]>;\n};\n\nfunction socketUrl(name: string, options: SocketRegistryOptions): string {\n if (typeof window === \"undefined\") throw new Error(\"Socket connections are unavailable during SSR\");\n const prefix = options.pathnamePrefix ?? \"/_meeko/socket\";\n if (!prefix.startsWith(\"/\") || prefix.endsWith(\"/\")) throw new TypeError(\"Invalid socket pathname prefix\");\n const url = new URL(`${prefix}/${encodeURIComponent(name)}`, options.baseUrl ?? window.location.href);\n if (url.protocol === \"http:\") url.protocol = \"ws:\";\n else if (url.protocol === \"https:\") url.protocol = \"wss:\";\n else if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\") throw new TypeError(\"Invalid socket URL protocol\");\n if (options.roomId !== undefined) url.searchParams.set(\"room\", options.roomId);\n return url.href;\n}\n\nasync function validate<T extends SocketSchema>(schema: T, value: unknown): Promise<SchemaOutput<T>> {\n const result = await schema[\"~standard\"].validate(value);\n if (\"issues\" in result && result.issues) throw new TypeError(\"Invalid socket event payload\");\n return (result as { value: SchemaOutput<T> }).value;\n}\n\n/** Native WebSocket client: no offline queue and no automatic replay. */\nexport function createSocketClient<C extends SocketContract>(url: string, contract: C, options: SocketClientOptions = {}): SocketClient<C> {\n const state = signal<\"connecting\" | \"open\" | \"closed\">(\"connecting\");\n const listeners = new Map<string, Set<(value: unknown) => void>>();\n const factory = options.webSocketFactory ?? ((address: string) => new WebSocket(address));\n const maxBytes = options.maxMessageBytes ?? 64 * 1024;\n let socket: WebSocket | undefined;\n let retry: ReturnType<typeof setTimeout> | undefined;\n let attempts = 0;\n let closed = false;\n\n const connect = () => {\n if (closed) return;\n state.value = \"connecting\";\n const peer = factory(url);\n socket = peer;\n peer.addEventListener(\"open\", () => { if (peer === socket) { attempts = 0; state.value = \"open\"; } });\n peer.addEventListener(\"message\", (message) => {\n if (peer !== socket || typeof message.data !== \"string\" || new TextEncoder().encode(message.data).byteLength > maxBytes) {\n closed = true;\n state.value = \"closed\";\n peer.close(1009, \"Message too large\");\n return;\n }\n void (async () => {\n const decoded: unknown = JSON.parse(message.data as string);\n if (typeof decoded !== \"object\" || decoded === null || !(\"event\" in decoded) || !(\"payload\" in decoded) || typeof decoded.event !== \"string\") throw new TypeError(\"Invalid socket message\");\n const schema = contract.serverToClient[decoded.event];\n if (!schema) throw new TypeError(\"Unknown socket event\");\n const value = await validate(schema, decoded.payload);\n for (const listener of listeners.get(decoded.event) ?? []) listener(value);\n })().catch(() => { closed = true; state.value = \"closed\"; peer.close(1008, \"Invalid event\"); });\n });\n peer.addEventListener(\"close\", (event) => {\n if (peer !== socket || closed) return;\n state.value = \"closed\";\n if ([1008, 1009].includes((event as CloseEvent).code)) { closed = true; return; }\n const delay = Math.min((options.retryMs ?? 500) * 2 ** attempts++, options.maxRetryMs ?? 30_000);\n retry = setTimeout(connect, delay);\n });\n };\n connect();\n return {\n state,\n async emit(event, data) {\n if (closed || socket?.readyState !== WebSocket.OPEN) throw new Error(\"Socket is disconnected\");\n const schema = contract.clientToServer[event];\n if (!schema) throw new TypeError(`Unknown socket event: ${event}`);\n const value = await validate(schema, data);\n if (closed || socket.readyState !== WebSocket.OPEN) throw new Error(\"Socket is disconnected\");\n const message = serializeJson({ event, payload: value });\n if (new TextEncoder().encode(message).byteLength > maxBytes) throw new RangeError(\"Socket message too large\");\n socket.send(message);\n },\n on(event, listener) {\n const set = listeners.get(event) ?? new Set();\n set.add(listener as (value: unknown) => void);\n listeners.set(event, set);\n return () => { set.delete(listener as (value: unknown) => void); if (set.size === 0) listeners.delete(event); };\n },\n close() {\n if (closed) return;\n closed = true;\n if (retry) clearTimeout(retry);\n socket?.close(1000, \"Client closed\");\n listeners.clear();\n state.value = \"closed\";\n },\n };\n}\n\n/** Binds socket lifetime to a Meeko root; SSR never opens a connection. */\nexport function useSocket<C extends SocketContract>(url: string, contract: C, options: SocketClientOptions = {}): SocketClient<C> {\n if (typeof window === \"undefined\") throw new Error(\"useSocket() is only available in the browser\");\n const client = createSocketClient(url, contract, options);\n try { onCleanup(() => client.close()); }\n catch (error) { client.close(); throw error; }\n return client;\n}\n\n/** Creates lazily connected, typed channel clients from public shared schemas. */\nexport function createSocketRegistry<C extends Record<string, SocketContract>>(contracts: C, options: SocketRegistryOptions = {}): SocketRegistry<C> {\n const result: Record<string, unknown> = Object.create(null);\n for (const name of Object.keys(contracts)) {\n let client: SocketClient<SocketContract> | undefined;\n Object.defineProperty(result, name, {\n enumerable: true,\n get() {\n if (!client) client = createSocketClient(socketUrl(name, options), contracts[name]!, options);\n return client;\n },\n });\n }\n return result as SocketRegistry<C>;\n}\n\n/** Creates a channel scoped to the current component root. */\nexport function useSocketChannel<C extends Record<string, SocketContract>, K extends keyof C & string>(contracts: C, name: K, options: SocketRegistryOptions = {}): SocketClient<C[K]> {\n const contract = contracts[name];\n if (!contract) throw new TypeError(`Unknown socket channel: ${name}`);\n return useSocket(socketUrl(name, options), contract, options);\n}\n"]}
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/framework/socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { currentEffectOwner, onCleanup, signal } from \"../reactivity.js\";\nimport type { ReadonlySignal } from \"../reactivity.js\";\nimport { logClientError, observeCallback } from \"../error-observer.js\";\nimport { currentAppErrorChannel, currentComponentErrorHandler } from \"../component-errors.js\";\nimport { serializeJson } from \"../server.js\";\n\nexport interface SocketSchema<I = unknown, O = I> {\n readonly \"~standard\": {\n readonly version: 1;\n readonly validate: (value: unknown) => { value: O; issues?: undefined } | { issues: readonly unknown[] } | Promise<{ value: O; issues?: undefined } | { issues: readonly unknown[] }>;\n readonly types?: { readonly input: I; readonly output: O } | undefined;\n };\n}\n\ntype SchemaInput<T> = T extends { readonly \"~standard\": { readonly types?: infer U } }\n ? NonNullable<U> extends { readonly input: infer I } ? I : never\n : T extends SocketSchema<infer I, unknown> ? I : never;\ntype SchemaOutput<T> = T extends { readonly \"~standard\": { readonly types?: infer U } }\n ? NonNullable<U> extends { readonly output: infer O } ? O : never\n : T extends SocketSchema<unknown, infer O> ? O : never;\ntype Events = Record<string, SocketSchema>;\n\nexport interface SocketContract<ClientToServer extends Events = Events, ServerToClient extends Events = Events> {\n readonly clientToServer: ClientToServer;\n readonly serverToClient: ServerToClient;\n}\n\nexport interface SocketClient<C extends SocketContract> {\n readonly state: ReadonlySignal<\"connecting\" | \"open\" | \"closed\">;\n emit<K extends keyof C[\"clientToServer\"] & string>(event: K, data: SchemaInput<C[\"clientToServer\"][K]>): Promise<void>;\n on<K extends keyof C[\"serverToClient\"] & string>(event: K, listener: (data: SchemaOutput<C[\"serverToClient\"][K]>) => void): () => void;\n close(): void;\n}\n\nexport interface SocketClientOptions {\n readonly retryMs?: number;\n readonly maxRetryMs?: number;\n readonly maxMessageBytes?: number;\n readonly webSocketFactory?: (url: string) => WebSocket;\n /** Receives invalid inbound messages and consumer listener failures. */\n readonly onError?: (error: unknown) => void;\n}\n\nexport interface SocketRegistryOptions extends SocketClientOptions {\n /** HTTP(S) or WS(S) base URL. Defaults to the current browser origin. */\n readonly baseUrl?: string;\n readonly pathnamePrefix?: string;\n readonly roomId?: string;\n}\n\nexport type SocketRegistry<C extends Record<string, SocketContract>> = {\n readonly [K in keyof C]: SocketClient<C[K]>;\n};\n\nfunction socketUrl(name: string, options: SocketRegistryOptions): string {\n if (typeof window === \"undefined\") throw new Error(\"Socket connections are unavailable during SSR\");\n const prefix = options.pathnamePrefix ?? \"/_meeko/socket\";\n if (!prefix.startsWith(\"/\") || prefix.endsWith(\"/\")) throw new TypeError(\"Invalid socket pathname prefix\");\n const url = new URL(`${prefix}/${encodeURIComponent(name)}`, options.baseUrl ?? window.location.href);\n if (url.protocol === \"http:\") url.protocol = \"ws:\";\n else if (url.protocol === \"https:\") url.protocol = \"wss:\";\n else if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\") throw new TypeError(\"Invalid socket URL protocol\");\n if (options.roomId !== undefined) url.searchParams.set(\"room\", options.roomId);\n return url.href;\n}\n\nasync function validate<T extends SocketSchema>(schema: T, value: unknown): Promise<SchemaOutput<T>> {\n const result = await schema[\"~standard\"].validate(value);\n if (\"issues\" in result && result.issues) throw new TypeError(\"Invalid socket event payload\");\n return (result as { value: SchemaOutput<T> }).value;\n}\n\n/** Native WebSocket client: no offline queue and no automatic replay. */\nexport function createSocketClient<C extends SocketContract>(url: string, contract: C, options: SocketClientOptions = {}): SocketClient<C> {\n const state = signal<\"connecting\" | \"open\" | \"closed\">(\"connecting\");\n const listeners = new Map<string, Set<(value: unknown) => void>>();\n const factory = options.webSocketFactory ?? ((address: string) => new WebSocket(address));\n const maxBytes = options.maxMessageBytes ?? 64 * 1024;\n let socket: WebSocket | undefined;\n let retry: ReturnType<typeof setTimeout> | undefined;\n let attempts = 0;\n let closed = false;\n const reportError = (error: unknown) => {\n if (!options.onError) { logClientError(\"Meeko socket client error\", error); return; }\n observeCallback(() => options.onError!(error),\n (observerError) => logClientError(\"Meeko socket error observer failed\", error, observerError));\n };\n const setState = (next: \"connecting\" | \"open\" | \"closed\") => {\n try { state.value = next; }\n catch (error) { reportError(error); }\n };\n const closePeer = (peer: WebSocket | undefined, code: number, reason: string) => {\n try { peer?.close(code, reason); }\n catch (error) { reportError(error); }\n };\n const scheduleRetry = () => {\n if (closed) return;\n const delay = Math.min((options.retryMs ?? 500) * 2 ** attempts++, options.maxRetryMs ?? 30_000);\n retry = setTimeout(() => {\n retry = undefined;\n try { connect(); }\n catch (error) {\n reportError(error);\n setState(\"closed\");\n scheduleRetry();\n }\n }, delay);\n };\n\n const connect = () => {\n if (closed) return;\n setState(\"connecting\");\n const peer = factory(url);\n socket = peer;\n let transportErrorReported = false;\n peer.addEventListener(\"open\", () => { if (peer === socket) { attempts = 0; setState(\"open\"); } });\n peer.addEventListener(\"error\", () => {\n if (peer !== socket || closed || transportErrorReported) return;\n transportErrorReported = true;\n reportError(new Error(\"Socket transport error\"));\n });\n peer.addEventListener(\"message\", (message) => {\n if (peer !== socket || closed) return;\n if (typeof message.data !== \"string\" || new TextEncoder().encode(message.data).byteLength > maxBytes) {\n reportError(new RangeError(\"Socket message too large or not text\"));\n closed = true;\n setState(\"closed\");\n listeners.clear();\n closePeer(peer, 1009, \"Message too large\");\n return;\n }\n void (async () => {\n const decoded: unknown = JSON.parse(message.data as string);\n if (typeof decoded !== \"object\" || decoded === null || !(\"event\" in decoded) || !(\"payload\" in decoded) || typeof decoded.event !== \"string\") throw new TypeError(\"Invalid socket message\");\n const schema = contract.serverToClient[decoded.event];\n if (!schema) throw new TypeError(\"Unknown socket event\");\n const value = await validate(schema, decoded.payload);\n if (peer !== socket || closed) return;\n for (const listener of listeners.get(decoded.event) ?? []) {\n if (peer !== socket || closed) return;\n observeCallback(() => listener(value), reportError);\n }\n })().catch((error: unknown) => {\n if (peer !== socket || closed) return;\n reportError(error);\n closed = true;\n setState(\"closed\");\n listeners.clear();\n closePeer(peer, 1008, \"Invalid event\");\n });\n });\n peer.addEventListener(\"close\", (event) => {\n if (peer !== socket || closed) return;\n setState(\"closed\");\n if ([1008, 1009].includes((event as CloseEvent).code)) { closed = true; listeners.clear(); return; }\n scheduleRetry();\n });\n };\n connect();\n return {\n state,\n async emit(event, data) {\n if (closed || socket?.readyState !== WebSocket.OPEN) throw new Error(\"Socket is disconnected\");\n const schema = contract.clientToServer[event];\n if (!schema) throw new TypeError(`Unknown socket event: ${event}`);\n const value = await validate(schema, data);\n if (closed || socket.readyState !== WebSocket.OPEN) throw new Error(\"Socket is disconnected\");\n const message = serializeJson({ event, payload: value });\n if (new TextEncoder().encode(message).byteLength > maxBytes) throw new RangeError(\"Socket message too large\");\n socket.send(message);\n },\n on(event, listener) {\n const set = listeners.get(event) ?? new Set();\n set.add(listener as (value: unknown) => void);\n listeners.set(event, set);\n const unsubscribe = () => { set.delete(listener as (value: unknown) => void); if (set.size === 0) listeners.delete(event); };\n if (currentEffectOwner()) onCleanup(unsubscribe);\n return unsubscribe;\n },\n close() {\n if (closed) { listeners.clear(); return; }\n closed = true;\n if (retry) clearTimeout(retry);\n closePeer(socket, 1000, \"Client closed\");\n listeners.clear();\n setState(\"closed\");\n },\n };\n}\n\n/** Binds socket lifetime to a Meeko root; SSR never opens a connection. */\nexport function useSocket<C extends SocketContract>(url: string, contract: C, options: SocketClientOptions = {}): SocketClient<C> {\n if (typeof window === \"undefined\") throw new Error(\"useSocket() is only available in the browser\");\n const onError = options.onError ?? currentComponentErrorHandler();\n const client = createSocketClient(url, contract, onError ? { ...options, onError } : options);\n try { onCleanup(() => client.close()); }\n catch (error) { client.close(); throw error; }\n return client;\n}\n\n/** Creates lazily connected, typed channel clients from public shared schemas. */\nexport function createSocketRegistry<C extends Record<string, SocketContract>>(contracts: C, options: SocketRegistryOptions = {}): SocketRegistry<C> {\n const result: Record<string, unknown> = Object.create(null);\n for (const name of Object.keys(contracts)) {\n let client: SocketClient<SocketContract> | undefined;\n const appClients = new WeakMap<NonNullable<ReturnType<typeof currentAppErrorChannel>>, SocketClient<SocketContract>>();\n const rootClients = new WeakMap<object, SocketClient<SocketContract>>();\n Object.defineProperty(result, name, {\n enumerable: true,\n get() {\n const channel = currentAppErrorChannel();\n if (channel) {\n let scoped = appClients.get(channel);\n if (!scoped) {\n const onError = options.onError ?? ((error: unknown) => {\n const handler = channel.current();\n if (handler) return handler(error);\n logClientError(\"Meeko socket client error\", error);\n });\n scoped = createSocketClient(socketUrl(name, options), contracts[name]!, { ...options, onError });\n appClients.set(channel, scoped);\n const owned = scoped;\n channel.track(() => { owned.close(); appClients.delete(channel); });\n }\n return scoped;\n }\n const owner = currentEffectOwner();\n if (owner) {\n let scoped = rootClients.get(owner);\n if (!scoped) {\n const onError = options.onError ?? currentComponentErrorHandler();\n scoped = createSocketClient(socketUrl(name, options), contracts[name]!, onError ? { ...options, onError } : options);\n rootClients.set(owner, scoped);\n const owned = scoped;\n onCleanup(() => { owned.close(); rootClients.delete(owner); });\n }\n return scoped;\n }\n if (!client) client = createSocketClient(socketUrl(name, options), contracts[name]!, options);\n return client;\n },\n });\n }\n return result as SocketRegistry<C>;\n}\n\n/** Creates a channel scoped to the current component root. */\nexport function useSocketChannel<C extends Record<string, SocketContract>, K extends keyof C & string>(contracts: C, name: K, options: SocketRegistryOptions = {}): SocketClient<C[K]> {\n const contract = contracts[name];\n if (!contract) throw new TypeError(`Unknown socket channel: ${name}`);\n return useSocket(socketUrl(name, options), contract, options);\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import ts from "@typescript/typescript6";
2
+ export interface StandardSchemaTypes {
3
+ readonly input: ts.Type;
4
+ readonly output: ts.Type;
5
+ }
6
+ /** Resolve the public Standard Schema contract without allowing erased client types. */
7
+ export declare function standardSchemaTypes(expression: ts.Expression, checker: ts.TypeChecker, invalid: () => Error): StandardSchemaTypes;
@@ -0,0 +1,69 @@
1
+ import ts from "@typescript/typescript6";
2
+ const unsafeFlags = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never | ts.TypeFlags.TypeParameter;
3
+ function isErased(type) {
4
+ return (type.flags & unsafeFlags) !== 0;
5
+ }
6
+ function isUnsafe(type, checker, location, seen) {
7
+ if (isErased(type))
8
+ return true;
9
+ if (seen.has(type))
10
+ return false;
11
+ seen.add(type);
12
+ if ((type.isUnionOrIntersection() && type.types.some((part) => isUnsafe(part, checker, location, seen)))
13
+ || type.aliasTypeArguments?.some((argument) => isUnsafe(argument, checker, location, seen)))
14
+ return true;
15
+ if (type.flags & ts.TypeFlags.Object) {
16
+ const object = type;
17
+ if ((object.objectFlags & ts.ObjectFlags.Reference) !== 0
18
+ && checker.getTypeArguments(object).some((argument) => isUnsafe(argument, checker, location, seen)))
19
+ return true;
20
+ const numberIndex = checker.getIndexTypeOfType(type, ts.IndexKind.Number);
21
+ if (numberIndex && isUnsafe(numberIndex, checker, location, seen))
22
+ return true;
23
+ const stringIndex = checker.getIndexTypeOfType(type, ts.IndexKind.String);
24
+ if (stringIndex && isUnsafe(stringIndex, checker, location, seen))
25
+ return true;
26
+ // Array and tuple methods contain library-level generics that are not part
27
+ // of the serialized payload. Their element/type arguments above are.
28
+ if (!checker.isArrayLikeType(type)) {
29
+ for (const property of checker.getPropertiesOfType(type)) {
30
+ if (isUnsafe(checker.getTypeOfSymbolAtLocation(property, location), checker, location, seen))
31
+ return true;
32
+ }
33
+ for (const signature of [...checker.getSignaturesOfType(type, ts.SignatureKind.Call), ...checker.getSignaturesOfType(type, ts.SignatureKind.Construct)]) {
34
+ if (signature.getParameters().some((parameter) => isUnsafe(checker.getTypeOfSymbolAtLocation(parameter, location), checker, location, seen))
35
+ || isUnsafe(signature.getReturnType(), checker, location, seen))
36
+ return true;
37
+ }
38
+ }
39
+ }
40
+ return false;
41
+ }
42
+ /** Resolve the public Standard Schema contract without allowing erased client types. */
43
+ export function standardSchemaTypes(expression, checker, invalid) {
44
+ const schemaType = checker.getTypeAtLocation(expression);
45
+ if (isErased(schemaType))
46
+ throw invalid();
47
+ const standard = checker.getPropertyOfType(schemaType, "~standard");
48
+ if (!standard)
49
+ throw invalid();
50
+ const standardType = checker.getTypeOfSymbolAtLocation(standard, expression);
51
+ if (isErased(standardType))
52
+ throw invalid();
53
+ const types = checker.getPropertyOfType(standardType, "types");
54
+ if (!types)
55
+ throw invalid();
56
+ const typesType = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(types, expression));
57
+ if (isErased(typesType))
58
+ throw invalid();
59
+ const input = checker.getPropertyOfType(typesType, "input");
60
+ const output = checker.getPropertyOfType(typesType, "output");
61
+ if (!input || !output)
62
+ throw invalid();
63
+ const inputType = checker.getTypeOfSymbolAtLocation(input, expression);
64
+ const outputType = checker.getTypeOfSymbolAtLocation(output, expression);
65
+ if (isUnsafe(inputType, checker, expression, new Set()) || isUnsafe(outputType, checker, expression, new Set()))
66
+ throw invalid();
67
+ return { input: inputType, output: outputType };
68
+ }
69
+ //# sourceMappingURL=standard-schema-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-schema-types.js","sourceRoot":"","sources":["../../src/framework/standard-schema-types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAOzC,MAAM,WAAW,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC;AAE9G,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,QAAQ,CAAC,IAAa,EAAE,OAAuB,EAAE,QAAiB,EAAE,IAAkB;IAC7F,IAAI,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEf,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;WACnG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3G,IAAI,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,IAAqB,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC;eACpD,OAAO,CAAC,gBAAgB,CAAC,MAA0B,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAEvI,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1E,IAAI,WAAW,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/E,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1E,IAAI,WAAW,IAAI,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAE/E,2EAA2E;QAC3E,qEAAqE;QACrE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzD,IAAI,QAAQ,CAAC,OAAO,CAAC,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YAC5G,CAAC;YACD,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;gBACxJ,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAC/C,QAAQ,CAAC,OAAO,CAAC,yBAAyB,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;uBACvF,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YACjF,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CACjC,UAAyB,EACzB,OAAuB,EACvB,OAAoB;IAEpB,MAAM,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzD,IAAI,QAAQ,CAAC,UAAU,CAAC;QAAE,MAAM,OAAO,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ;QAAE,MAAM,OAAO,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7E,IAAI,QAAQ,CAAC,YAAY,CAAC;QAAE,MAAM,OAAO,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK;QAAE,MAAM,OAAO,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,yBAAyB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IACnG,IAAI,QAAQ,CAAC,SAAS,CAAC;QAAE,MAAM,OAAO,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9D,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,MAAM,OAAO,EAAE,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,yBAAyB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,yBAAyB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACzE,IAAI,QAAQ,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,GAAG,EAAE,CAAC;QAAE,MAAM,OAAO,EAAE,CAAC;IACjI,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAClD,CAAC","sourcesContent":["import ts from \"@typescript/typescript6\";\n\nexport interface StandardSchemaTypes {\n readonly input: ts.Type;\n readonly output: ts.Type;\n}\n\nconst unsafeFlags = ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never | ts.TypeFlags.TypeParameter;\n\nfunction isErased(type: ts.Type): boolean {\n return (type.flags & unsafeFlags) !== 0;\n}\n\nfunction isUnsafe(type: ts.Type, checker: ts.TypeChecker, location: ts.Node, seen: Set<ts.Type>): boolean {\n if (isErased(type)) return true;\n if (seen.has(type)) return false;\n seen.add(type);\n\n if ((type.isUnionOrIntersection() && type.types.some((part) => isUnsafe(part, checker, location, seen)))\n || type.aliasTypeArguments?.some((argument) => isUnsafe(argument, checker, location, seen))) return true;\n\n if (type.flags & ts.TypeFlags.Object) {\n const object = type as ts.ObjectType;\n if ((object.objectFlags & ts.ObjectFlags.Reference) !== 0\n && checker.getTypeArguments(object as ts.TypeReference).some((argument) => isUnsafe(argument, checker, location, seen))) return true;\n\n const numberIndex = checker.getIndexTypeOfType(type, ts.IndexKind.Number);\n if (numberIndex && isUnsafe(numberIndex, checker, location, seen)) return true;\n const stringIndex = checker.getIndexTypeOfType(type, ts.IndexKind.String);\n if (stringIndex && isUnsafe(stringIndex, checker, location, seen)) return true;\n\n // Array and tuple methods contain library-level generics that are not part\n // of the serialized payload. Their element/type arguments above are.\n if (!checker.isArrayLikeType(type)) {\n for (const property of checker.getPropertiesOfType(type)) {\n if (isUnsafe(checker.getTypeOfSymbolAtLocation(property, location), checker, location, seen)) return true;\n }\n for (const signature of [...checker.getSignaturesOfType(type, ts.SignatureKind.Call), ...checker.getSignaturesOfType(type, ts.SignatureKind.Construct)]) {\n if (signature.getParameters().some((parameter) =>\n isUnsafe(checker.getTypeOfSymbolAtLocation(parameter, location), checker, location, seen))\n || isUnsafe(signature.getReturnType(), checker, location, seen)) return true;\n }\n }\n }\n return false;\n}\n\n/** Resolve the public Standard Schema contract without allowing erased client types. */\nexport function standardSchemaTypes(\n expression: ts.Expression,\n checker: ts.TypeChecker,\n invalid: () => Error,\n): StandardSchemaTypes {\n const schemaType = checker.getTypeAtLocation(expression);\n if (isErased(schemaType)) throw invalid();\n const standard = checker.getPropertyOfType(schemaType, \"~standard\");\n if (!standard) throw invalid();\n const standardType = checker.getTypeOfSymbolAtLocation(standard, expression);\n if (isErased(standardType)) throw invalid();\n const types = checker.getPropertyOfType(standardType, \"types\");\n if (!types) throw invalid();\n const typesType = checker.getNonNullableType(checker.getTypeOfSymbolAtLocation(types, expression));\n if (isErased(typesType)) throw invalid();\n const input = checker.getPropertyOfType(typesType, \"input\");\n const output = checker.getPropertyOfType(typesType, \"output\");\n if (!input || !output) throw invalid();\n const inputType = checker.getTypeOfSymbolAtLocation(input, expression);\n const outputType = checker.getTypeOfSymbolAtLocation(output, expression);\n if (isUnsafe(inputType, checker, expression, new Set()) || isUnsafe(outputType, checker, expression, new Set())) throw invalid();\n return { input: inputType, output: outputType };\n}\n"]}
@@ -5,6 +5,13 @@ export declare class StateStore {
5
5
  get<T>(key: string, factory: () => T): Signal<T>;
6
6
  /** Seeds values returned by a later server navigation without replacing local state. */
7
7
  seed(values: Readonly<Record<string, unknown>>): void;
8
+ /** Applies navigation state only if the synchronous DOM commit succeeds. */
9
+ seedForNavigation<T>(values: Readonly<Record<string, unknown>>, commit: () => T): T;
10
+ /** Gives an async client loader isolated signals until its route commits. */
11
+ navigationDraft(): {
12
+ state: StateStore;
13
+ commit<T>(work: () => T): T;
14
+ };
8
15
  snapshot(): Record<string, unknown>;
9
16
  }
10
17
  /** One keyed signal per application request or browser app instance. */
@@ -1,4 +1,4 @@
1
- import { createContext, createRoot, inject, provide, signal } from "../reactivity.js";
1
+ import { createContext, createRoot, finishWithCleanup, inject, provide, signal } from "../reactivity.js";
2
2
  import { serializeJson } from "../server.js";
3
3
  const stateKey = createContext("Meeko framework state");
4
4
  function assertKey(key) {
@@ -26,12 +26,86 @@ export class StateStore {
26
26
  /** Seeds values returned by a later server navigation without replacing local state. */
27
27
  seed(values) {
28
28
  serializeJson(values);
29
- for (const [key, value] of Object.entries(values)) {
29
+ const entries = Object.entries(values);
30
+ for (const [key] of entries)
30
31
  assertKey(key);
32
+ for (const [key, value] of entries) {
31
33
  if (!this.values.has(key))
32
34
  this.values.set(key, signal(value));
33
35
  }
34
36
  }
37
+ /** Applies navigation state only if the synchronous DOM commit succeeds. */
38
+ seedForNavigation(values, commit) {
39
+ const previousKeys = new Set(this.values.keys());
40
+ this.seed(values);
41
+ try {
42
+ return commit();
43
+ }
44
+ catch (error) {
45
+ for (const key of this.values.keys())
46
+ if (!previousKeys.has(key))
47
+ this.values.delete(key);
48
+ throw error;
49
+ }
50
+ }
51
+ /** Gives an async client loader isolated signals until its route commits. */
52
+ navigationDraft() {
53
+ const initial = JSON.parse(serializeJson(this.snapshot()));
54
+ const draft = new StateStore(initial);
55
+ return {
56
+ state: draft,
57
+ commit: (work) => {
58
+ const next = draft.snapshot();
59
+ const changed = Object.entries(next).filter(([key, value]) => !Object.hasOwn(initial, key) || serializeJson(value) !== serializeJson(initial[key]));
60
+ const current = this.snapshot();
61
+ for (const [key] of changed) {
62
+ if (Object.hasOwn(current, key) !== Object.hasOwn(initial, key)
63
+ || (Object.hasOwn(initial, key) && serializeJson(current[key]) !== serializeJson(initial[key]))) {
64
+ throw new Error(`Meeko state changed while navigating: ${key}`);
65
+ }
66
+ }
67
+ const previous = new Map([...this.values].map(([key, value]) => {
68
+ const serialized = serializeJson(value.value);
69
+ return [key, { signal: value, serialized, value: JSON.parse(serialized) }];
70
+ }));
71
+ try {
72
+ for (const [key, value] of changed) {
73
+ const existing = this.values.get(key);
74
+ if (existing)
75
+ existing.value = value;
76
+ else
77
+ this.values.set(key, signal(value));
78
+ }
79
+ return work();
80
+ }
81
+ catch (error) {
82
+ const failures = [error];
83
+ for (const key of this.values.keys())
84
+ if (!previous.has(key))
85
+ this.values.delete(key);
86
+ for (const [key, prior] of previous) {
87
+ this.values.set(key, prior.signal);
88
+ let unchanged = false;
89
+ try {
90
+ unchanged = serializeJson(prior.signal.value) === prior.serialized;
91
+ }
92
+ catch { /* A now-invalid value still needs restoration. */ }
93
+ if (!unchanged) {
94
+ try {
95
+ prior.signal.value = prior.value;
96
+ }
97
+ catch (restoreError) {
98
+ failures.push(restoreError);
99
+ }
100
+ }
101
+ }
102
+ if (failures.length > 1)
103
+ throw new AggregateError(failures, "Meeko navigation state recovery failed");
104
+ throw error;
105
+ }
106
+ },
107
+ };
108
+ }
35
109
  snapshot() {
36
110
  const result = Object.create(null);
37
111
  for (const [key, value] of this.values)
@@ -48,12 +122,7 @@ export function useState(key, factory) {
48
122
  export function withStateStore(store, render) {
49
123
  return createRoot((dispose) => {
50
124
  provide(stateKey, store);
51
- try {
52
- return render();
53
- }
54
- finally {
55
- dispose();
56
- }
125
+ return finishWithCleanup(render, dispose);
57
126
  });
58
127
  }
59
128
  /** Keeps the provider alive until the hydrated component is released. */
@@ -61,12 +130,7 @@ export function hydrateWithStateStore(store, hydrate) {
61
130
  return createRoot((dispose) => {
62
131
  provide(stateKey, store);
63
132
  const release = hydrate();
64
- return () => { try {
65
- release();
66
- }
67
- finally {
68
- dispose();
69
- } };
133
+ return () => finishWithCleanup(release, dispose);
70
134
  });
71
135
  }
72
136
  //# sourceMappingURL=state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/framework/state.ts"],"names":[],"mappings":"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","sourcesContent":["import { createContext, createRoot, inject, provide, signal } from \"../reactivity.js\";\nimport type { Signal } from \"../reactivity.js\";\nimport { serializeJson } from \"../server.js\";\n\nconst stateKey = createContext<StateStore>(\"Meeko framework state\");\n\nfunction assertKey(key: string): void {\n if (!key || key === \"__proto__\" || key === \"constructor\" || key === \"prototype\") throw new TypeError(\"Invalid state key\");\n}\n\nexport class StateStore {\n private readonly values = new Map<string, Signal<unknown>>();\n\n constructor(initial: Readonly<Record<string, unknown>> = {}) {\n serializeJson(initial);\n for (const [key, value] of Object.entries(initial)) { assertKey(key); this.values.set(key, signal(value)); }\n }\n\n get<T>(key: string, factory: () => T): Signal<T> {\n assertKey(key);\n let value = this.values.get(key);\n if (!value) {\n value = signal(factory());\n this.values.set(key, value);\n }\n return value as Signal<T>;\n }\n\n /** Seeds values returned by a later server navigation without replacing local state. */\n seed(values: Readonly<Record<string, unknown>>): void {\n serializeJson(values);\n for (const [key, value] of Object.entries(values)) {\n assertKey(key);\n if (!this.values.has(key)) this.values.set(key, signal(value));\n }\n }\n\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = Object.create(null);\n for (const [key, value] of this.values) result[key] = value.value;\n serializeJson(result);\n return result;\n }\n}\n\n/** One keyed signal per application request or browser app instance. */\nexport function useState<T>(key: string, factory: () => T): Signal<T> {\n return inject(stateKey).get(key, factory);\n}\n\n/** Runs synchronous SSR rendering with a state provider and releases its root. */\nexport function withStateStore<T>(store: StateStore, render: () => T): T {\n return createRoot((dispose) => {\n provide(stateKey, store);\n try { return render(); }\n finally { dispose(); }\n });\n}\n\n/** Keeps the provider alive until the hydrated component is released. */\nexport function hydrateWithStateStore(store: StateStore, hydrate: () => () => void): () => void {\n return createRoot((dispose) => {\n provide(stateKey, store);\n const release = hydrate();\n return () => { try { release(); } finally { dispose(); } };\n });\n}\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/framework/state.ts"],"names":[],"mappings":"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","sourcesContent":["import { createContext, createRoot, finishWithCleanup, inject, provide, signal } from \"../reactivity.js\";\nimport type { Signal } from \"../reactivity.js\";\nimport { serializeJson } from \"../server.js\";\n\nconst stateKey = createContext<StateStore>(\"Meeko framework state\");\n\nfunction assertKey(key: string): void {\n if (!key || key === \"__proto__\" || key === \"constructor\" || key === \"prototype\") throw new TypeError(\"Invalid state key\");\n}\n\nexport class StateStore {\n private readonly values = new Map<string, Signal<unknown>>();\n\n constructor(initial: Readonly<Record<string, unknown>> = {}) {\n serializeJson(initial);\n for (const [key, value] of Object.entries(initial)) { assertKey(key); this.values.set(key, signal(value)); }\n }\n\n get<T>(key: string, factory: () => T): Signal<T> {\n assertKey(key);\n let value = this.values.get(key);\n if (!value) {\n value = signal(factory());\n this.values.set(key, value);\n }\n return value as Signal<T>;\n }\n\n /** Seeds values returned by a later server navigation without replacing local state. */\n seed(values: Readonly<Record<string, unknown>>): void {\n serializeJson(values);\n const entries = Object.entries(values);\n for (const [key] of entries) assertKey(key);\n for (const [key, value] of entries) {\n if (!this.values.has(key)) this.values.set(key, signal(value));\n }\n }\n\n /** Applies navigation state only if the synchronous DOM commit succeeds. */\n seedForNavigation<T>(values: Readonly<Record<string, unknown>>, commit: () => T): T {\n const previousKeys = new Set(this.values.keys());\n this.seed(values);\n try { return commit(); }\n catch (error) {\n for (const key of this.values.keys()) if (!previousKeys.has(key)) this.values.delete(key);\n throw error;\n }\n }\n\n /** Gives an async client loader isolated signals until its route commits. */\n navigationDraft(): { state: StateStore; commit<T>(work: () => T): T } {\n const initial = JSON.parse(serializeJson(this.snapshot())) as Record<string, unknown>;\n const draft = new StateStore(initial);\n return {\n state: draft,\n commit: <T>(work: () => T): T => {\n const next = draft.snapshot();\n const changed = Object.entries(next).filter(([key, value]) =>\n !Object.hasOwn(initial, key) || serializeJson(value) !== serializeJson(initial[key]));\n const current = this.snapshot();\n for (const [key] of changed) {\n if (Object.hasOwn(current, key) !== Object.hasOwn(initial, key)\n || (Object.hasOwn(initial, key) && serializeJson(current[key]) !== serializeJson(initial[key]))) {\n throw new Error(`Meeko state changed while navigating: ${key}`);\n }\n }\n const previous = new Map([...this.values].map(([key, value]) => {\n const serialized = serializeJson(value.value);\n return [key, { signal: value, serialized, value: JSON.parse(serialized) as unknown }] as const;\n }));\n try {\n for (const [key, value] of changed) {\n const existing = this.values.get(key);\n if (existing) existing.value = value;\n else this.values.set(key, signal(value));\n }\n return work();\n } catch (error) {\n const failures: unknown[] = [error];\n for (const key of this.values.keys()) if (!previous.has(key)) this.values.delete(key);\n for (const [key, prior] of previous) {\n this.values.set(key, prior.signal);\n let unchanged = false;\n try { unchanged = serializeJson(prior.signal.value) === prior.serialized; }\n catch { /* A now-invalid value still needs restoration. */ }\n if (!unchanged) {\n try { prior.signal.value = prior.value; }\n catch (restoreError) { failures.push(restoreError); }\n }\n }\n if (failures.length > 1) throw new AggregateError(failures, \"Meeko navigation state recovery failed\");\n throw error;\n }\n },\n };\n }\n\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = Object.create(null);\n for (const [key, value] of this.values) result[key] = value.value;\n serializeJson(result);\n return result;\n }\n}\n\n/** One keyed signal per application request or browser app instance. */\nexport function useState<T>(key: string, factory: () => T): Signal<T> {\n return inject(stateKey).get(key, factory);\n}\n\n/** Runs synchronous SSR rendering with a state provider and releases its root. */\nexport function withStateStore<T>(store: StateStore, render: () => T): T {\n return createRoot((dispose) => {\n provide(stateKey, store);\n return finishWithCleanup(render, dispose);\n });\n}\n\n/** Keeps the provider alive until the hydrated component is released. */\nexport function hydrateWithStateStore(store: StateStore, hydrate: () => () => void): () => void {\n return createRoot((dispose) => {\n provide(stateKey, store);\n const release = hydrate();\n return () => finishWithCleanup(release, dispose);\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** Renders stylesheet URLs from a Vite manifest or the development convention. */
2
+ export declare function stylesheetLinks(paths: readonly string[]): string;
@@ -0,0 +1,6 @@
1
+ import { escapeHtml } from "../server.js";
2
+ /** Renders stylesheet URLs from a Vite manifest or the development convention. */
3
+ export function stylesheetLinks(paths) {
4
+ return paths.map((path) => `<link rel="stylesheet" href="${escapeHtml(path)}">`).join("");
5
+ }
6
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../src/framework/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAC,KAAwB;IACtD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gCAAgC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5F,CAAC","sourcesContent":["import { escapeHtml } from \"../server.js\";\n\n/** Renders stylesheet URLs from a Vite manifest or the development convention. */\nexport function stylesheetLinks(paths: readonly string[]): string {\n return paths.map((path) => `<link rel=\"stylesheet\" href=\"${escapeHtml(path)}\">`).join(\"\");\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const projectNames: readonly ["app", "server", "shared"];
2
+ /** Keeps TypeScript's three execution scopes under Meeko's generated directory. */
3
+ export declare function generateFrameworkTsconfigs(projectRoot: string): Promise<string[]>;
4
+ /** Generates declarations without loading a Vite server into the editor host. */
5
+ export declare function prepareFrameworkTypeDeclarations(projectRoot: string): Promise<void>;
@@ -0,0 +1,84 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join, relative, resolve } from "node:path";
3
+ import { generateTypeScriptAutoImports } from "../autoimports-ts.js";
4
+ import { generateAppTypes } from "./check-app.js";
5
+ import { resolveConfigSources } from "./config.js";
6
+ import { generateServerContracts } from "./contracts-generate.js";
7
+ import { generateRoutes } from "./generate.js";
8
+ import { generatePageContracts } from "./page-contracts-generate.js";
9
+ export const projectNames = ["app", "server", "shared"];
10
+ const compilerOptions = {
11
+ target: "ES2022", module: "NodeNext", moduleResolution: "NodeNext", lib: ["ES2022", "DOM"],
12
+ types: ["node", "vite/client"], strict: true, noEmit: true, allowArbitraryExtensions: true,
13
+ exactOptionalPropertyTypes: true, noUncheckedIndexedAccess: true, verbatimModuleSyntax: true,
14
+ skipLibCheck: true,
15
+ };
16
+ const generatedIncludes = {
17
+ app: [
18
+ "generated/entry-client.ts", "generated/clients.client.ts", "generated/socket-contracts.ts",
19
+ "types/**/*.d.meeko.ts", "types/routes.d.ts", "types/virtual-pages.d.ts",
20
+ "types/server-contracts.d.ts", "types/autoimports/client.d.ts",
21
+ ],
22
+ server: [
23
+ "generated/entry-server.ts", "generated/clients.server.ts", "generated/socket-contracts.ts",
24
+ "generated/page-contracts.server.ts", "types/virtual-pages.d.ts", "types/routes.d.ts",
25
+ "types/**/*.d.meeko.ts", "types/autoimports/server.d.ts",
26
+ ],
27
+ shared: ["types/autoimports/shared.d.ts"],
28
+ };
29
+ async function writeIfChanged(file, source) {
30
+ try {
31
+ if (await readFile(file, "utf8") === source)
32
+ return;
33
+ }
34
+ catch (error) {
35
+ if (error.code !== "ENOENT")
36
+ throw error;
37
+ }
38
+ await writeFile(file, source, "utf8");
39
+ }
40
+ /** Keeps TypeScript's three execution scopes under Meeko's generated directory. */
41
+ export async function generateFrameworkTsconfigs(projectRoot) {
42
+ const generatedRoot = join(resolve(projectRoot), ".meeko");
43
+ const sourceRoots = (await resolveConfigSources(projectRoot)).map(({ root }) => relative(generatedRoot, root).replaceAll("\\", "/"));
44
+ await mkdir(generatedRoot, { recursive: true });
45
+ const configs = [];
46
+ for (const name of projectNames) {
47
+ const file = join(generatedRoot, `tsconfig.${name}.json`);
48
+ const includes = [...generatedIncludes[name]];
49
+ const excludes = [];
50
+ for (const root of sourceRoots) {
51
+ if (name === "app") {
52
+ includes.push(`${root}/app/**/*.ts`, `${root}/server/**/*.client.ts`, `${root}/shared/**/*.client.ts`);
53
+ excludes.push(`${root}/app/pages/**/*.route.ts`, `${root}/app/**/*.server.ts`, `${root}/app/**/*.shared.ts`);
54
+ }
55
+ else if (name === "server") {
56
+ includes.push(`${root}/server/**/*.ts`, `${root}/app/**/*.server.ts`, `${root}/app/pages/**/*.route.ts`, `${root}/shared/**/*.server.ts`);
57
+ excludes.push(`${root}/server/**/*.client.ts`, `${root}/server/**/*.shared.ts`);
58
+ }
59
+ else {
60
+ includes.push(`${root}/shared/**/*.ts`, `${root}/app/**/*.shared.ts`, `${root}/server/**/*.shared.ts`);
61
+ excludes.push(`${root}/shared/**/*.client.ts`, `${root}/shared/**/*.server.ts`);
62
+ }
63
+ }
64
+ const source = JSON.stringify({
65
+ compilerOptions: { ...compilerOptions, rootDirs: name === "shared" ? undefined : ["..", "./types"] },
66
+ include: includes,
67
+ ...(excludes.length ? { exclude: excludes } : {}),
68
+ }, null, 2) + "\n";
69
+ await writeIfChanged(file, source);
70
+ configs.push(file);
71
+ }
72
+ return configs;
73
+ }
74
+ /** Generates declarations without loading a Vite server into the editor host. */
75
+ export async function prepareFrameworkTypeDeclarations(projectRoot) {
76
+ const root = resolve(projectRoot);
77
+ await generateRoutes(root);
78
+ await generateServerContracts(root);
79
+ await generateAppTypes(root);
80
+ await generatePageContracts(root);
81
+ await generateTypeScriptAutoImports(root);
82
+ await generateFrameworkTsconfigs(root);
83
+ }
84
+ //# sourceMappingURL=type-declarations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-declarations.js","sourceRoot":"","sources":["../../src/framework/type-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport { generateTypeScriptAutoImports } from \"../autoimports-ts.js\";\nimport { generateAppTypes } from \"./check-app.js\";\nimport { resolveConfigSources } from \"./config.js\";\nimport { generateServerContracts } from \"./contracts-generate.js\";\nimport { generateRoutes } from \"./generate.js\";\nimport { generatePageContracts } from \"./page-contracts-generate.js\";\n\nexport const projectNames = [\"app\", \"server\", \"shared\"] as const;\n\nconst compilerOptions = {\n target: \"ES2022\", module: \"NodeNext\", moduleResolution: \"NodeNext\", lib: [\"ES2022\", \"DOM\"],\n types: [\"node\", \"vite/client\"], strict: true, noEmit: true, allowArbitraryExtensions: true,\n exactOptionalPropertyTypes: true, noUncheckedIndexedAccess: true, verbatimModuleSyntax: true,\n skipLibCheck: true,\n} as const;\n\nconst generatedIncludes: Record<(typeof projectNames)[number], string[]> = {\n app: [\n \"generated/entry-client.ts\", \"generated/clients.client.ts\", \"generated/socket-contracts.ts\",\n \"types/**/*.d.meeko.ts\", \"types/routes.d.ts\", \"types/virtual-pages.d.ts\",\n \"types/server-contracts.d.ts\", \"types/autoimports/client.d.ts\",\n ],\n server: [\n \"generated/entry-server.ts\", \"generated/clients.server.ts\", \"generated/socket-contracts.ts\",\n \"generated/page-contracts.server.ts\", \"types/virtual-pages.d.ts\", \"types/routes.d.ts\",\n \"types/**/*.d.meeko.ts\", \"types/autoimports/server.d.ts\",\n ],\n shared: [\"types/autoimports/shared.d.ts\"],\n};\n\nasync function writeIfChanged(file: string, source: string): Promise<void> {\n try { if (await readFile(file, \"utf8\") === source) return; }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n await writeFile(file, source, \"utf8\");\n}\n\n/** Keeps TypeScript's three execution scopes under Meeko's generated directory. */\nexport async function generateFrameworkTsconfigs(projectRoot: string): Promise<string[]> {\n const generatedRoot = join(resolve(projectRoot), \".meeko\");\n const sourceRoots = (await resolveConfigSources(projectRoot)).map(({ root }) => relative(generatedRoot, root).replaceAll(\"\\\\\", \"/\"));\n await mkdir(generatedRoot, { recursive: true });\n const configs: string[] = [];\n for (const name of projectNames) {\n const file = join(generatedRoot, `tsconfig.${name}.json`);\n const includes = [...generatedIncludes[name]];\n const excludes: string[] = [];\n for (const root of sourceRoots) {\n if (name === \"app\") {\n includes.push(`${root}/app/**/*.ts`, `${root}/server/**/*.client.ts`, `${root}/shared/**/*.client.ts`);\n excludes.push(`${root}/app/pages/**/*.route.ts`, `${root}/app/**/*.server.ts`, `${root}/app/**/*.shared.ts`);\n } else if (name === \"server\") {\n includes.push(`${root}/server/**/*.ts`, `${root}/app/**/*.server.ts`, `${root}/app/pages/**/*.route.ts`, `${root}/shared/**/*.server.ts`);\n excludes.push(`${root}/server/**/*.client.ts`, `${root}/server/**/*.shared.ts`);\n } else {\n includes.push(`${root}/shared/**/*.ts`, `${root}/app/**/*.shared.ts`, `${root}/server/**/*.shared.ts`);\n excludes.push(`${root}/shared/**/*.client.ts`, `${root}/shared/**/*.server.ts`);\n }\n }\n const source = JSON.stringify({\n compilerOptions: { ...compilerOptions, rootDirs: name === \"shared\" ? undefined : [\"..\", \"./types\"] },\n include: includes,\n ...(excludes.length ? { exclude: excludes } : {}),\n }, null, 2) + \"\\n\";\n await writeIfChanged(file, source);\n configs.push(file);\n }\n return configs;\n}\n\n/** Generates declarations without loading a Vite server into the editor host. */\nexport async function prepareFrameworkTypeDeclarations(projectRoot: string): Promise<void> {\n const root = resolve(projectRoot);\n await generateRoutes(root);\n await generateServerContracts(root);\n await generateAppTypes(root);\n await generatePageContracts(root);\n await generateTypeScriptAutoImports(root);\n await generateFrameworkTsconfigs(root);\n}\n"]}
@@ -29,6 +29,15 @@ export declare class ApiError extends Error {
29
29
  readonly status: number;
30
30
  constructor(code: string, status: number, message?: string);
31
31
  }
32
+ /** Preserve public errors loaded through separate Vite module graphs; hide all others. */
33
+ export declare function normalizeServerError(error: unknown): ApiError;
34
+ export type ServerErrorObserver = (error: unknown, requestId: string) => void;
35
+ /** Observer failures cannot replace a response or escape as unhandled rejections. */
36
+ export declare function observeErrorCallback(error: unknown, observer?: (error: unknown) => void, requestId?: string): void;
37
+ /** Apply the same isolation to observers that also receive a request ID. */
38
+ export declare function observeServerError(error: unknown, requestId: string, observer?: ServerErrorObserver): void;
39
+ /** Surface internal failures to the host while keeping observers out of the response path. */
40
+ export declare function reportServerError(error: unknown, requestId: string, observer?: ServerErrorObserver): void;
32
41
  export interface ApiRouteDefinition<P extends Schema | undefined, Q extends Schema | undefined, B extends Schema | undefined, R extends Schema | undefined, S = unknown> {
33
42
  params?: P;
34
43
  query?: Q;
@@ -59,6 +68,6 @@ export declare function defineRpc<I extends Schema, O extends Schema, S = unknow
59
68
  export declare function executeApiRoute<P extends Schema | undefined, Q extends Schema | undefined, B extends Schema | undefined, R extends Schema | undefined, S>(definition: ApiRouteDefinition<P, Q, B, R, S>, context: RequestContext<S>, sessions: SessionManager, rawParams?: unknown): Promise<Response>;
60
69
  export declare function executeRpc<I extends Schema, O extends Schema, S>(definition: RpcDefinition<I, O, S>, rawInput: unknown, context: RequestContext<S>, sessions: SessionManager): Promise<Output<O>>;
61
70
  export declare function errorResponse(error: unknown, requestId: string): Response;
62
- export declare function dispatchApiRoute<P extends Schema | undefined, Q extends Schema | undefined, B extends Schema | undefined, R extends Schema | undefined, S>(definition: ApiRouteDefinition<P, Q, B, R, S>, context: RequestContext<S>, sessions: SessionManager, params?: unknown): Promise<Response>;
63
- export declare function dispatchRpc<I extends Schema, O extends Schema, S>(definition: RpcDefinition<I, O, S>, context: RequestContext<S>, sessions: SessionManager): Promise<Response>;
71
+ export declare function dispatchApiRoute<P extends Schema | undefined, Q extends Schema | undefined, B extends Schema | undefined, R extends Schema | undefined, S>(definition: ApiRouteDefinition<P, Q, B, R, S>, context: RequestContext<S>, sessions: SessionManager, params?: unknown, onError?: ServerErrorObserver): Promise<Response>;
72
+ export declare function dispatchRpc<I extends Schema, O extends Schema, S>(definition: RpcDefinition<I, O, S>, context: RequestContext<S>, sessions: SessionManager, onError?: ServerErrorObserver): Promise<Response>;
64
73
  export {};
@@ -7,8 +7,55 @@ export class ApiError extends Error {
7
7
  super(message);
8
8
  this.code = code;
9
9
  this.status = status;
10
+ this.name = "ApiError";
10
11
  }
11
12
  }
13
+ /** Preserve public errors loaded through separate Vite module graphs; hide all others. */
14
+ export function normalizeServerError(error) {
15
+ if (error instanceof SessionSecurityError)
16
+ return new ApiError(error.code, 403);
17
+ if (error instanceof Error && (error.name === "SessionSecurityError" || error.constructor.name === "SessionSecurityError")) {
18
+ const code = error.code;
19
+ if (code === "INVALID_ORIGIN" || code === "INVALID_CSRF")
20
+ return new ApiError(code, 403);
21
+ }
22
+ if (error instanceof Error && (error instanceof ApiError || error.name === "ApiError" || error.constructor.name === "ApiError")) {
23
+ const candidate = error;
24
+ if (typeof candidate.code === "string" && candidate.code.length > 0
25
+ && typeof candidate.status === "number" && Number.isInteger(candidate.status)
26
+ && candidate.status >= 400 && candidate.status <= 599) {
27
+ return error instanceof ApiError ? error : new ApiError(candidate.code, candidate.status);
28
+ }
29
+ }
30
+ return new ApiError("INTERNAL_ERROR", 500);
31
+ }
32
+ function reportObserverFailure(error, observerError, requestId) {
33
+ try {
34
+ const label = requestId ? `Meeko server error observer failed (${requestId})` : "Meeko server error observer failed";
35
+ // Error messages and custom properties may contain private server data.
36
+ console.error(label, { original: error instanceof Error ? "Error" : typeof error, observer: observerError instanceof Error ? "Error" : typeof observerError });
37
+ }
38
+ catch { /* The fallback logger cannot change the original response. */ }
39
+ }
40
+ /** Observer failures cannot replace a response or escape as unhandled rejections. */
41
+ export function observeErrorCallback(error, observer, requestId) {
42
+ try {
43
+ void Promise.resolve(observer?.(error)).catch((observerError) => reportObserverFailure(error, observerError, requestId));
44
+ }
45
+ catch (observerError) {
46
+ reportObserverFailure(error, observerError, requestId);
47
+ }
48
+ }
49
+ /** Apply the same isolation to observers that also receive a request ID. */
50
+ export function observeServerError(error, requestId, observer) {
51
+ observeErrorCallback(error, observer ? (failure) => observer(failure, requestId) : undefined, requestId);
52
+ }
53
+ /** Surface internal failures to the host while keeping observers out of the response path. */
54
+ export function reportServerError(error, requestId, observer) {
55
+ if (normalizeServerError(error).status < 500)
56
+ return;
57
+ observeServerError(error, requestId, observer);
58
+ }
12
59
  export function defineApiRoute(definition) {
13
60
  return definition;
14
61
  }
@@ -96,26 +143,33 @@ export async function executeRpc(definition, rawInput, context, sessions) {
96
143
  return output;
97
144
  }
98
145
  export function errorResponse(error, requestId) {
99
- const known = error instanceof ApiError ? error : error instanceof SessionSecurityError ? new ApiError(error.code, 403) : new ApiError("INTERNAL_ERROR", 500);
146
+ const known = normalizeServerError(error);
100
147
  return new Response(serializeJson({ code: known.code, status: known.status, requestId }), { status: known.status, headers: { "content-type": "application/json; charset=utf-8", "x-request-id": requestId } });
101
148
  }
102
149
  function finish(response, context) {
103
- response.headers.set("x-request-id", context.requestId);
150
+ // Fetch-created responses such as Response.redirect have immutable headers.
151
+ // Wrap the response to preserve its status, body, and headers before adding
152
+ // request metadata and session changes.
153
+ const finished = new Response(response.body, response);
154
+ finished.headers.set("x-request-id", context.requestId);
104
155
  if (context.session.cookieHeader)
105
- response.headers.append("set-cookie", context.session.cookieHeader);
156
+ finished.headers.append("set-cookie", context.session.cookieHeader);
106
157
  if (context.session.current)
107
- response.headers.set("x-meeko-csrf-token", context.session.current.csrfToken);
108
- return response;
158
+ finished.headers.set("x-meeko-csrf-token", context.session.current.csrfToken);
159
+ else if (context.session.cookieHeader)
160
+ finished.headers.set("x-meeko-csrf-token", "");
161
+ return finished;
109
162
  }
110
- export async function dispatchApiRoute(definition, context, sessions, params = {}) {
163
+ export async function dispatchApiRoute(definition, context, sessions, params = {}, onError) {
111
164
  try {
112
165
  return finish(await executeApiRoute(definition, context, sessions, params), context);
113
166
  }
114
167
  catch (error) {
168
+ reportServerError(error, context.requestId, onError);
115
169
  return finish(errorResponse(error, context.requestId), context);
116
170
  }
117
171
  }
118
- export async function dispatchRpc(definition, context, sessions) {
172
+ export async function dispatchRpc(definition, context, sessions, onError) {
119
173
  try {
120
174
  if (context.request.method !== "POST")
121
175
  throw new ApiError("METHOD_NOT_ALLOWED", 405);
@@ -123,6 +177,7 @@ export async function dispatchRpc(definition, context, sessions) {
123
177
  return finish(new Response(serializeJson(output), { headers: { "content-type": "application/json; charset=utf-8" } }), context);
124
178
  }
125
179
  catch (error) {
180
+ reportServerError(error, context.requestId, onError);
126
181
  return finish(errorResponse(error, context.requestId), context);
127
182
  }
128
183
  }