@meekojs/meeko 0.0.3 → 0.0.4

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 (144) hide show
  1. package/README.md +18 -1
  2. package/dist/autoimports-ts.d.ts +26 -0
  3. package/dist/autoimports-ts.js +127 -0
  4. package/dist/autoimports-ts.js.map +1 -0
  5. package/dist/autoimports.d.ts +21 -0
  6. package/dist/autoimports.js +285 -0
  7. package/dist/autoimports.js.map +1 -0
  8. package/dist/check.js +35 -23
  9. package/dist/check.js.map +1 -0
  10. package/dist/cli.js +33 -4
  11. package/dist/cli.js.map +1 -0
  12. package/dist/compiler/import-graph.d.ts +8 -0
  13. package/dist/compiler/import-graph.js +33 -0
  14. package/dist/compiler/import-graph.js.map +1 -0
  15. package/dist/compiler/mapping.js +1 -0
  16. package/dist/compiler/mapping.js.map +1 -0
  17. package/dist/compiler/project-root.d.ts +2 -0
  18. package/dist/compiler/project-root.js +18 -0
  19. package/dist/compiler/project-root.js.map +1 -0
  20. package/dist/compiler/sfc.js +1 -0
  21. package/dist/compiler/sfc.js.map +1 -0
  22. package/dist/compiler/sourcemap.d.ts +3 -0
  23. package/dist/compiler/sourcemap.js +75 -0
  24. package/dist/compiler/sourcemap.js.map +1 -0
  25. package/dist/compiler/template.js +1 -0
  26. package/dist/compiler/template.js.map +1 -0
  27. package/dist/compiler.js +11 -5
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/devtools-ui/index.js +1 -0
  30. package/dist/devtools-ui/index.js.map +1 -0
  31. package/dist/devtools.js +1 -0
  32. package/dist/devtools.js.map +1 -0
  33. package/dist/dom.d.ts +8 -1
  34. package/dist/dom.js +10 -5
  35. package/dist/dom.js.map +1 -0
  36. package/dist/framework/check-app.d.ts +5 -0
  37. package/dist/framework/check-app.js +59 -0
  38. package/dist/framework/check-app.js.map +1 -0
  39. package/dist/framework/client.d.ts +45 -0
  40. package/dist/framework/client.js +146 -0
  41. package/dist/framework/client.js.map +1 -0
  42. package/dist/framework/clients.d.ts +82 -0
  43. package/dist/framework/clients.js +182 -0
  44. package/dist/framework/clients.js.map +1 -0
  45. package/dist/framework/config-type.d.ts +10 -0
  46. package/dist/framework/config-type.js +4 -0
  47. package/dist/framework/config-type.js.map +1 -0
  48. package/dist/framework/config.d.ts +15 -0
  49. package/dist/framework/config.js +138 -0
  50. package/dist/framework/config.js.map +1 -0
  51. package/dist/framework/contracts-generate.d.ts +8 -0
  52. package/dist/framework/contracts-generate.js +227 -0
  53. package/dist/framework/contracts-generate.js.map +1 -0
  54. package/dist/framework/generate.d.ts +7 -0
  55. package/dist/framework/generate.js +67 -0
  56. package/dist/framework/generate.js.map +1 -0
  57. package/dist/framework/init.d.ts +2 -0
  58. package/dist/framework/init.js +362 -0
  59. package/dist/framework/init.js.map +1 -0
  60. package/dist/framework/modules.d.ts +22 -0
  61. package/dist/framework/modules.js +29 -0
  62. package/dist/framework/modules.js.map +1 -0
  63. package/dist/framework/page.d.ts +60 -0
  64. package/dist/framework/page.js +42 -0
  65. package/dist/framework/page.js.map +1 -0
  66. package/dist/framework/prerender.d.ts +14 -0
  67. package/dist/framework/prerender.js +104 -0
  68. package/dist/framework/prerender.js.map +1 -0
  69. package/dist/framework/router.d.ts +39 -0
  70. package/dist/framework/router.js +173 -0
  71. package/dist/framework/router.js.map +1 -0
  72. package/dist/framework/server.d.ts +51 -0
  73. package/dist/framework/server.js +192 -0
  74. package/dist/framework/server.js.map +1 -0
  75. package/dist/framework/socket-contracts-generate.d.ts +5 -0
  76. package/dist/framework/socket-contracts-generate.js +159 -0
  77. package/dist/framework/socket-contracts-generate.js.map +1 -0
  78. package/dist/framework/socket.d.ts +70 -0
  79. package/dist/framework/socket.js +150 -0
  80. package/dist/framework/socket.js.map +1 -0
  81. package/dist/framework/state.d.ts +15 -0
  82. package/dist/framework/state.js +72 -0
  83. package/dist/framework/state.js.map +1 -0
  84. package/dist/framework-server/auto-clients.d.ts +3 -0
  85. package/dist/framework-server/auto-clients.js +27 -0
  86. package/dist/framework-server/auto-clients.js.map +1 -0
  87. package/dist/framework-server/context.d.ts +21 -0
  88. package/dist/framework-server/context.js +22 -0
  89. package/dist/framework-server/context.js.map +1 -0
  90. package/dist/framework-server/contracts.d.ts +64 -0
  91. package/dist/framework-server/contracts.js +129 -0
  92. package/dist/framework-server/contracts.js.map +1 -0
  93. package/dist/framework-server/discovery.d.ts +40 -0
  94. package/dist/framework-server/discovery.js +252 -0
  95. package/dist/framework-server/discovery.js.map +1 -0
  96. package/dist/framework-server/local.d.ts +19 -0
  97. package/dist/framework-server/local.js +98 -0
  98. package/dist/framework-server/local.js.map +1 -0
  99. package/dist/framework-server/node-upgrade.d.ts +23 -0
  100. package/dist/framework-server/node-upgrade.js +78 -0
  101. package/dist/framework-server/node-upgrade.js.map +1 -0
  102. package/dist/framework-server/node-websocket.d.ts +16 -0
  103. package/dist/framework-server/node-websocket.js +125 -0
  104. package/dist/framework-server/node-websocket.js.map +1 -0
  105. package/dist/framework-server/openapi.d.ts +17 -0
  106. package/dist/framework-server/openapi.js +118 -0
  107. package/dist/framework-server/openapi.js.map +1 -0
  108. package/dist/framework-server/session-stores.d.ts +66 -0
  109. package/dist/framework-server/session-stores.js +96 -0
  110. package/dist/framework-server/session-stores.js.map +1 -0
  111. package/dist/framework-server/sessions.d.ts +64 -0
  112. package/dist/framework-server/sessions.js +136 -0
  113. package/dist/framework-server/sessions.js.map +1 -0
  114. package/dist/framework-server/socket.d.ts +83 -0
  115. package/dist/framework-server/socket.js +161 -0
  116. package/dist/framework-server/socket.js.map +1 -0
  117. package/dist/framework-server.d.ts +21 -0
  118. package/dist/framework-server.js +12 -0
  119. package/dist/framework-server.js.map +1 -0
  120. package/dist/framework.d.ts +29 -0
  121. package/dist/framework.js +13 -0
  122. package/dist/framework.js.map +1 -0
  123. package/dist/hmr.js +1 -0
  124. package/dist/hmr.js.map +1 -0
  125. package/dist/index.d.ts +3 -3
  126. package/dist/index.js +2 -1
  127. package/dist/index.js.map +1 -0
  128. package/dist/init.js +4 -2
  129. package/dist/init.js.map +1 -0
  130. package/dist/internal.js +1 -0
  131. package/dist/internal.js.map +1 -0
  132. package/dist/node.d.ts +12 -0
  133. package/dist/node.js +52 -0
  134. package/dist/node.js.map +1 -0
  135. package/dist/reactivity.d.ts +12 -0
  136. package/dist/reactivity.js +40 -3
  137. package/dist/reactivity.js.map +1 -0
  138. package/dist/server.js +1 -0
  139. package/dist/server.js.map +1 -0
  140. package/dist/types.js +23 -8
  141. package/dist/types.js.map +1 -0
  142. package/dist/vite.js +91 -6
  143. package/dist/vite.js.map +1 -0
  144. package/package.json +14 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/framework-server/discovery.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createRequestContext } from \"./context.js\";\nimport type { RequestContext, ServerFetch } from \"./context.js\";\nimport { ApiError, dispatchApiRoute, dispatchRpc, errorResponse } from \"./contracts.js\";\nimport type { ApiRouteDefinition, RpcDefinition, Schema } from \"./contracts.js\";\nimport type { SessionManager } from \"./sessions.js\";\n\nexport type ApiMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\nexport interface ApiEndpoint { readonly kind: \"api\"; readonly file: string; readonly pathname: string; readonly method: ApiMethod; readonly clientKey: string; }\nexport interface RpcEndpoint { readonly kind: \"rpc\"; readonly file: string; readonly pathname: string; readonly id: string; }\nexport interface SocketEndpoint { readonly kind: \"socket\"; readonly file: string; readonly name: string; readonly pathname: string; }\nexport interface ServerManifest { readonly api: readonly ApiEndpoint[]; readonly rpc: readonly RpcEndpoint[]; readonly socket?: readonly SocketEndpoint[]; }\ntype PatternPart = { kind: \"static\" | \"param\" | \"rest\"; value: string };\n\nfunction parts(pathname: string): PatternPart[] {\n return pathname.split(\"/\").filter(Boolean).map((part, index, all) => {\n if (/^\\[\\.\\.\\.[A-Za-z_$][\\w$]*\\]$/.test(part)) {\n if (index !== all.length - 1) throw new Error(`Catch-all must be last: ${pathname}`);\n return { kind: \"rest\", value: part.slice(4, -1) };\n }\n if (/^\\[[A-Za-z_$][\\w$]*\\]$/.test(part)) return { kind: \"param\", value: part.slice(1, -1) };\n if (!/^[A-Za-z0-9_-]+$/.test(part)) throw new Error(`Invalid server route segment: ${part}`);\n return { kind: \"static\", value: part };\n });\n}\n\nfunction collisionKey(pathname: string): string {\n return parts(pathname).map((part) => part.kind === \"static\" ? part.value : part.kind === \"param\" ? \"[]\" : \"[...]\").join(\"/\");\n}\n\nfunction clientKey(pathname: string): string {\n return parts(pathname).map((part) => part.kind === \"static\" ? part.value : part.kind === \"param\" ? `by${part.value[0]!.toUpperCase()}${part.value.slice(1)}` : `by${part.value[0]!.toUpperCase()}${part.value.slice(1)}`).join(\".\");\n}\n\nasync function files(directory: string): Promise<string[]> {\n let entries;\n try { entries = await readdir(directory, { withFileTypes: true }); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return []; throw error; }\n const found: string[] = [];\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n const file = join(directory, entry.name);\n if (entry.isDirectory()) found.push(...await files(file));\n else if (entry.isFile() && /\\.(?:ts|js|mts|mjs)$/.test(entry.name) && !/\\.(?:test|spec|d)\\.(?:ts|js|mts|mjs)$/.test(entry.name)) found.push(file);\n }\n return found.sort();\n}\n\nfunction rel(root: string, file: string): string {\n return relative(root, file).split(sep).join(\"/\").replace(/\\.(?:ts|js|mts|mjs)$/, \"\");\n}\n\nfunction apiEndpoint(directory: string, file: string): ApiEndpoint {\n const path = rel(directory, file);\n const match = /^(.*)\\.(get|post|put|patch|delete)$/.exec(path);\n if (!match) throw new Error(`API handler requires a method suffix: ${file}`);\n const route = match[1]!.split(\"/\");\n if (route.at(-1) === \"index\") route.pop();\n const pathname = `/api${route.length ? `/${route.join(\"/\")}` : \"\"}`;\n parts(pathname);\n return { kind: \"api\", file, pathname, method: match[2]!.toUpperCase() as ApiMethod, clientKey: clientKey(pathname.slice(4)) };\n}\n\nfunction rpcEndpoint(directory: string, file: string): RpcEndpoint {\n const path = rel(directory, file);\n const route = path.split(\"/\");\n if (route.some((part) => !/^[A-Za-z_$][\\w$]*$/.test(part))) throw new Error(`Invalid RPC procedure name: ${file}`);\n const id = route.join(\".\");\n return { kind: \"rpc\", file, id, pathname: `/_meeko/rpc/${route.join(\"/\")}` };\n}\n\n/** Static discovery is shared by development and production; no handler is executed here. */\nexport async function discoverServerManifest(projectRoot: string): Promise<ServerManifest> {\n const root = resolve(projectRoot);\n const apiRoot = join(root, \"server\", \"api\");\n const rpcRoot = join(root, \"server\", \"rpc\");\n const socketRoot = join(root, \"server\", \"socket\");\n const api = (await files(apiRoot)).map((file) => apiEndpoint(apiRoot, file));\n const rpc = (await files(rpcRoot)).map((file) => rpcEndpoint(rpcRoot, file));\n const socket = (await files(socketRoot)).map((file): SocketEndpoint => {\n const name = rel(socketRoot, file);\n if (!/^[A-Za-z_$][\\w$-]*$/.test(name)) throw new Error(`Invalid Socket channel name in ${file}; use a single named file`);\n return { kind: \"socket\", file, name, pathname: `/_meeko/socket/${name}` };\n });\n const socketNames = new Map<string, string>();\n for (const endpoint of socket) {\n const previous = socketNames.get(endpoint.name);\n if (previous) throw new Error(`Socket channel collision: ${previous} and ${endpoint.file} both resolve to ${endpoint.name}`);\n socketNames.set(endpoint.name, endpoint.file);\n }\n const paths = new Map<string, string>();\n const keys = new Map<string, string>();\n for (const endpoint of api) {\n const pathKey = `${endpoint.method} ${collisionKey(endpoint.pathname)}`;\n const previous = paths.get(pathKey);\n if (previous) throw new Error(`API route collision: ${previous} and ${endpoint.file} both resolve to ${pathKey}`);\n paths.set(pathKey, endpoint.file);\n const key = `${endpoint.method} ${endpoint.clientKey}`;\n const keyPrevious = keys.get(key);\n if (keyPrevious) throw new Error(`API client name collision: ${keyPrevious} and ${endpoint.file} both generate ${key}`);\n keys.set(key, endpoint.file);\n }\n const ids = new Map<string, string>();\n for (const endpoint of rpc) {\n const previous = ids.get(endpoint.id);\n if (previous) throw new Error(`RPC collision: ${previous} and ${endpoint.file} both resolve to ${endpoint.id}`);\n ids.set(endpoint.id, endpoint.file);\n }\n return Object.freeze({ api: Object.freeze(api), rpc: Object.freeze(rpc), socket: Object.freeze(socket) });\n}\n\n/** Loads server-only channel definitions; callers pass the result to createSocketHub. */\nexport async function loadSocketChannels(manifest: Pick<ServerManifest, \"socket\">, load: (file: string) => Promise<unknown> = async (file) => import(pathToFileURL(file).href) as Promise<unknown>): Promise<Readonly<Record<string, unknown>>> {\n const channels: Record<string, unknown> = Object.create(null);\n for (const endpoint of manifest.socket ?? []) {\n const module = await load(endpoint.file);\n if (!module || typeof module !== \"object\" || !(\"default\" in module)) throw new Error(`${endpoint.file}: Socket channel requires a default export`);\n channels[endpoint.name] = (module as { default: unknown }).default;\n }\n return channels;\n}\n\nfunction decode(part: string): string | undefined {\n try {\n const value = decodeURIComponent(part);\n return value && value !== \".\" && value !== \"..\" && !value.includes(\"/\") && !value.includes(\"\\\\\") ? value : undefined;\n } catch { return undefined; }\n}\n\nfunction match(pathname: string, pattern: string): Record<string, string> | undefined {\n const raw = pathname.split(\"/\").filter(Boolean);\n const path = raw.map(decode);\n if (path.some((part) => part === undefined)) return undefined;\n const patternParts = parts(pattern);\n const params: Record<string, string> = Object.create(null);\n let index = 0;\n for (const part of patternParts) {\n if (part.kind === \"rest\") {\n if (index >= path.length) return undefined;\n params[part.value] = path.slice(index).join(\"/\");\n index = path.length;\n break;\n }\n const value = path[index++];\n if (value === undefined || (part.kind === \"static\" && part.value !== value)) return undefined;\n if (part.kind === \"param\") params[part.value] = value;\n }\n return index === path.length ? params : undefined;\n}\n\nfunction rank(endpoint: ApiEndpoint): number[] {\n return parts(endpoint.pathname).map((part) => part.kind === \"static\" ? 3 : part.kind === \"param\" ? 2 : 1);\n}\n\nfunction compareApi(a: ApiEndpoint, b: ApiEndpoint): number {\n const left = rank(a);\n const right = rank(b);\n for (let index = 0; index < Math.max(left.length, right.length); index++) {\n const score = (right[index] ?? 0) - (left[index] ?? 0);\n if (score) return score;\n }\n return a.pathname.localeCompare(b.pathname);\n}\n\nexport interface ServerDispatcherOptions<S = unknown> {\n manifest: ServerManifest;\n sessions: SessionManager;\n services: S | ((request: Request) => S | Promise<S>);\n load?: (file: string) => Promise<unknown>;\n}\n\nfunction exportedDefinition(module: unknown): unknown {\n if (module && typeof module === \"object\" && \"default\" in module) return (module as { default: unknown }).default;\n throw new ApiError(\"INVALID_HANDLER\", 500);\n}\n\n/** Dispatches API and RPC through the same request context and security pipeline. */\nfunction requestDispatcher<S>(options: Pick<ServerDispatcherOptions<S>, \"manifest\" | \"sessions\" | \"load\">): (context: RequestContext<S>) => Promise<Response> {\n const api = [...options.manifest.api].sort(compareApi);\n const load = options.load ?? (async (file: string) => import(pathToFileURL(file).href) as Promise<unknown>);\n return async (context) => {\n const request = context.request;\n try {\n const pathname = new URL(request.url).pathname;\n if (pathname.startsWith(\"/_meeko/rpc/\")) {\n const endpoint = options.manifest.rpc.find((record) => record.pathname === pathname);\n if (!endpoint) throw new ApiError(\"NOT_FOUND\", 404);\n if (request.method !== \"POST\") throw new ApiError(\"METHOD_NOT_ALLOWED\", 405);\n const definition = exportedDefinition(await load(endpoint.file)) as RpcDefinition<Schema, Schema, S>;\n return dispatchRpc(definition, context, options.sessions);\n }\n let allowed: ApiMethod[] = [];\n for (const endpoint of api) {\n const params = match(pathname, endpoint.pathname);\n if (!params) continue;\n if (request.method !== endpoint.method) { allowed.push(endpoint.method); continue; }\n const definition = exportedDefinition(await load(endpoint.file)) as ApiRouteDefinition<Schema | undefined, Schema | undefined, Schema | undefined, Schema | undefined, S>;\n return dispatchApiRoute(definition, context, options.sessions, params);\n }\n if (allowed.length) {\n const response = errorResponse(new ApiError(\"METHOD_NOT_ALLOWED\", 405), context.requestId);\n response.headers.set(\"allow\", [...new Set(allowed)].join(\", \"));\n return response;\n }\n throw new ApiError(\"NOT_FOUND\", 404);\n } catch (error) { return errorResponse(error, context.requestId); }\n };\n}\n\nexport function createServerDispatcher<S>(options: ServerDispatcherOptions<S>): (request: Request) => Promise<Response> {\n const dispatch = requestDispatcher(options);\n return async (request) => {\n let requestId: string = randomUUID();\n try {\n const session = await options.sessions.load(request);\n const services = typeof options.services === \"function\" ? await (options.services as (request: Request) => S | Promise<S>)(request) : options.services;\n const context = createRequestContext(request, services, session);\n requestId = context.requestId;\n return dispatch(context);\n } catch (error) { return errorResponse(error, requestId); }\n };\n}\n\n/** SSR dispatch reuses the original session, services, state and request ID. */\nexport function createLocalServerFetch<S>(options: Pick<ServerDispatcherOptions<S>, \"manifest\" | \"sessions\" | \"load\">): ServerFetch {\n const dispatch = requestDispatcher(options);\n return async (request, active) => {\n const source = active.request;\n if (new URL(request.url).origin !== new URL(source.url).origin) {\n return errorResponse(new ApiError(\"INVALID_ORIGIN\", 403), active.requestId);\n }\n for (const name of [\"cookie\", \"authorization\"]) {\n if (request.headers.get(name) !== source.headers.get(name)) {\n return errorResponse(new ApiError(\"INVALID_CREDENTIALS\", 403), active.requestId);\n }\n }\n const context: RequestContext<S> = { ...active, request, signal: request.signal } as RequestContext<S>;\n return dispatch(context);\n };\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { RequestContext } from "./context.js";
2
+ import type { ApiMethod, ServerManifest } from "./discovery.js";
3
+ import type { SessionManager } from "./sessions.js";
4
+ export interface LocalCallOptions<S> {
5
+ manifest: ServerManifest;
6
+ context: RequestContext<S>;
7
+ sessions: SessionManager;
8
+ load?: (file: string) => Promise<unknown>;
9
+ }
10
+ export interface LocalApiInput {
11
+ params?: Readonly<Record<string, string>>;
12
+ query?: Readonly<Record<string, string | number | boolean | readonly string[] | null | undefined>>;
13
+ body?: unknown;
14
+ }
15
+ /** Local calls use in-memory Request objects, never a network fetch or global context. */
16
+ export declare function createLocalCalls<S>(options: LocalCallOptions<S>): {
17
+ api(clientKey: string, method: ApiMethod, input?: LocalApiInput): Promise<Response>;
18
+ rpc(id: string, input: unknown): Promise<Response>;
19
+ };
@@ -0,0 +1,98 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import { ApiError, dispatchApiRoute, dispatchRpc, errorResponse } from "./contracts.js";
3
+ import { serializeJson } from "../server.js";
4
+ function publicHeaders(request) {
5
+ const headers = new Headers();
6
+ for (const name of ["cookie", "origin", "x-meeko-csrf", "authorization"]) {
7
+ const value = request.headers.get(name);
8
+ if (value !== null)
9
+ headers.set(name, value);
10
+ }
11
+ return headers;
12
+ }
13
+ function resolvePath(pattern, params) {
14
+ const used = new Set();
15
+ const path = pattern.split("/").map((part) => {
16
+ const match = /^\[([A-Za-z_$][\w$]*)\]$/.exec(part);
17
+ const rest = /^\[\.\.\.([A-Za-z_$][\w$]*)\]$/.exec(part);
18
+ if (!match && !rest)
19
+ return part;
20
+ const key = (match ?? rest)[1];
21
+ const value = params[key];
22
+ if (!value)
23
+ throw new ApiError("INVALID_PARAMS", 400);
24
+ used.add(key);
25
+ if (rest) {
26
+ const segments = value.split("/");
27
+ if (segments.some((segment) => !segment || segment === "." || segment === ".."))
28
+ throw new ApiError("INVALID_PARAMS", 400);
29
+ return segments.map(encodeURIComponent).join("/");
30
+ }
31
+ if (value.includes("/") || value === "." || value === "..")
32
+ throw new ApiError("INVALID_PARAMS", 400);
33
+ return encodeURIComponent(value);
34
+ }).join("/");
35
+ for (const key of Object.keys(params))
36
+ if (!used.has(key))
37
+ throw new ApiError("INVALID_PARAMS", 400);
38
+ return path;
39
+ }
40
+ function routeModule(module) {
41
+ if (module && typeof module === "object" && "default" in module)
42
+ return module.default;
43
+ throw new ApiError("INVALID_HANDLER", 500);
44
+ }
45
+ /** Local calls use in-memory Request objects, never a network fetch or global context. */
46
+ export function createLocalCalls(options) {
47
+ const load = options.load ?? (async (file) => import(pathToFileURL(file).href));
48
+ const derived = (request) => ({ ...options.context, request });
49
+ return {
50
+ async api(clientKey, method, input = {}) {
51
+ try {
52
+ const endpoint = options.manifest.api.find((entry) => entry.clientKey === clientKey && entry.method === method);
53
+ if (!endpoint)
54
+ throw new ApiError("NOT_FOUND", 404);
55
+ const url = new URL(resolvePath(endpoint.pathname, input.params ?? {}), options.context.request.url);
56
+ for (const [key, value] of Object.entries(input.query ?? {})) {
57
+ if (value === undefined || value === null)
58
+ continue;
59
+ if (Array.isArray(value))
60
+ for (const item of value)
61
+ url.searchParams.append(key, item);
62
+ else
63
+ url.searchParams.set(key, String(value));
64
+ }
65
+ const headers = publicHeaders(options.context.request);
66
+ if ((method === "GET") && Object.hasOwn(input, "body"))
67
+ throw new ApiError("INVALID_BODY", 400);
68
+ const body = Object.hasOwn(input, "body") ? serializeJson(input.body) : undefined;
69
+ if (body !== undefined)
70
+ headers.set("content-type", "application/json");
71
+ const request = new Request(url, { method, headers, signal: options.context.signal, ...(body === undefined ? {} : { body }) });
72
+ const definition = routeModule(await load(endpoint.file));
73
+ return dispatchApiRoute(definition, derived(request), options.sessions, input.params ?? {});
74
+ }
75
+ catch (error) {
76
+ return errorResponse(error, options.context.requestId);
77
+ }
78
+ },
79
+ async rpc(id, input) {
80
+ try {
81
+ const endpoint = options.manifest.rpc.find((entry) => entry.id === id);
82
+ if (!endpoint)
83
+ throw new ApiError("NOT_FOUND", 404);
84
+ const headers = publicHeaders(options.context.request);
85
+ headers.set("content-type", "application/json");
86
+ const request = new Request(new URL(endpoint.pathname, options.context.request.url), {
87
+ method: "POST", headers, signal: options.context.signal, body: serializeJson(input),
88
+ });
89
+ const definition = routeModule(await load(endpoint.file));
90
+ return dispatchRpc(definition, derived(request), options.sessions);
91
+ }
92
+ catch (error) {
93
+ return errorResponse(error, options.context.requestId);
94
+ }
95
+ },
96
+ };
97
+ }
98
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/framework-server/local.ts"],"names":[],"mappings":"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","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { ApiError, dispatchApiRoute, dispatchRpc, errorResponse } from \"./contracts.js\";\nimport type { ApiRouteDefinition, RpcDefinition, Schema } from \"./contracts.js\";\nimport type { RequestContext } from \"./context.js\";\nimport type { ApiMethod, ServerManifest } from \"./discovery.js\";\nimport type { SessionManager } from \"./sessions.js\";\nimport { serializeJson } from \"../server.js\";\n\nexport interface LocalCallOptions<S> {\n manifest: ServerManifest;\n context: RequestContext<S>;\n sessions: SessionManager;\n load?: (file: string) => Promise<unknown>;\n}\n\nexport interface LocalApiInput {\n params?: Readonly<Record<string, string>>;\n query?: Readonly<Record<string, string | number | boolean | readonly string[] | null | undefined>>;\n body?: unknown;\n}\n\nfunction publicHeaders(request: Request): Headers {\n const headers = new Headers();\n for (const name of [\"cookie\", \"origin\", \"x-meeko-csrf\", \"authorization\"]) {\n const value = request.headers.get(name);\n if (value !== null) headers.set(name, value);\n }\n return headers;\n}\n\nfunction resolvePath(pattern: string, params: Readonly<Record<string, string>>): string {\n const used = new Set<string>();\n const path = pattern.split(\"/\").map((part) => {\n const match = /^\\[([A-Za-z_$][\\w$]*)\\]$/.exec(part);\n const rest = /^\\[\\.\\.\\.([A-Za-z_$][\\w$]*)\\]$/.exec(part);\n if (!match && !rest) return part;\n const key = (match ?? rest)![1]!;\n const value = params[key];\n if (!value) throw new ApiError(\"INVALID_PARAMS\", 400);\n used.add(key);\n if (rest) {\n const segments = value.split(\"/\");\n if (segments.some((segment) => !segment || segment === \".\" || segment === \"..\")) throw new ApiError(\"INVALID_PARAMS\", 400);\n return segments.map(encodeURIComponent).join(\"/\");\n }\n if (value.includes(\"/\") || value === \".\" || value === \"..\") throw new ApiError(\"INVALID_PARAMS\", 400);\n return encodeURIComponent(value);\n }).join(\"/\");\n for (const key of Object.keys(params)) if (!used.has(key)) throw new ApiError(\"INVALID_PARAMS\", 400);\n return path;\n}\n\nfunction routeModule(module: unknown): unknown {\n if (module && typeof module === \"object\" && \"default\" in module) return (module as { default: unknown }).default;\n throw new ApiError(\"INVALID_HANDLER\", 500);\n}\n\n/** Local calls use in-memory Request objects, never a network fetch or global context. */\nexport function createLocalCalls<S>(options: LocalCallOptions<S>) {\n const load = options.load ?? (async (file: string) => import(pathToFileURL(file).href) as Promise<unknown>);\n const derived = (request: Request): RequestContext<S> => ({ ...options.context, request });\n return {\n async api(clientKey: string, method: ApiMethod, input: LocalApiInput = {}): Promise<Response> {\n try {\n const endpoint = options.manifest.api.find((entry) => entry.clientKey === clientKey && entry.method === method);\n if (!endpoint) throw new ApiError(\"NOT_FOUND\", 404);\n const url = new URL(resolvePath(endpoint.pathname, input.params ?? {}), options.context.request.url);\n for (const [key, value] of Object.entries(input.query ?? {})) {\n if (value === undefined || value === null) continue;\n if (Array.isArray(value)) for (const item of value) url.searchParams.append(key, item);\n else url.searchParams.set(key, String(value));\n }\n const headers = publicHeaders(options.context.request);\n if ((method === \"GET\") && Object.hasOwn(input, \"body\")) throw new ApiError(\"INVALID_BODY\", 400);\n const body = Object.hasOwn(input, \"body\") ? serializeJson(input.body) : undefined;\n if (body !== undefined) headers.set(\"content-type\", \"application/json\");\n const request = new Request(url, { method, headers, signal: options.context.signal, ...(body === undefined ? {} : { body }) });\n const definition = routeModule(await load(endpoint.file)) as ApiRouteDefinition<Schema | undefined, Schema | undefined, Schema | undefined, Schema | undefined, S>;\n return dispatchApiRoute(definition, derived(request), options.sessions, input.params ?? {});\n } catch (error) { return errorResponse(error, options.context.requestId); }\n },\n async rpc(id: string, input: unknown): Promise<Response> {\n try {\n const endpoint = options.manifest.rpc.find((entry) => entry.id === id);\n if (!endpoint) throw new ApiError(\"NOT_FOUND\", 404);\n const headers = publicHeaders(options.context.request);\n headers.set(\"content-type\", \"application/json\");\n const request = new Request(new URL(endpoint.pathname, options.context.request.url), {\n method: \"POST\", headers, signal: options.context.signal, body: serializeJson(input),\n });\n const definition = routeModule(await load(endpoint.file)) as RpcDefinition<Schema, Schema, S>;\n return dispatchRpc(definition, derived(request), options.sessions);\n } catch (error) { return errorResponse(error, options.context.requestId); }\n },\n };\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ import type { Duplex } from "node:stream";
3
+ import type { SocketHub, SocketPeer } from "./socket.js";
4
+ /** A WebSocket server library owns framing and the HTTP 101 response. */
5
+ export interface NodeAcceptedSocket {
6
+ peer: SocketPeer;
7
+ onMessage(listener: (message: string | Uint8Array) => void): void;
8
+ onClose(listener: () => void): void;
9
+ }
10
+ export interface NodeSocketUpgradeOptions {
11
+ hub: SocketHub;
12
+ /** Fixed public origin; never infer security policy from an untrusted Host header. */
13
+ publicOrigin: string;
14
+ pathnamePrefix?: string;
15
+ upgrade(incoming: IncomingMessage, socket: Duplex, head: Buffer): Promise<NodeAcceptedSocket> | NodeAcceptedSocket;
16
+ reject?(incoming: IncomingMessage, socket: Duplex, status: number): void;
17
+ onError?(error: unknown): void;
18
+ }
19
+ /**
20
+ * Handles the Node `upgrade` event after policy preflight. The host supplies a
21
+ * WebSocket implementation; this adapter does not implement wire framing.
22
+ */
23
+ export declare function createNodeSocketUpgradeHandler(options: NodeSocketUpgradeOptions): (incoming: IncomingMessage, socket: Duplex, head: Buffer) => Promise<boolean>;
@@ -0,0 +1,78 @@
1
+ import { ApiError } from "./contracts.js";
2
+ /**
3
+ * Handles the Node `upgrade` event after policy preflight. The host supplies a
4
+ * WebSocket implementation; this adapter does not implement wire framing.
5
+ */
6
+ export function createNodeSocketUpgradeHandler(options) {
7
+ const origin = new URL(options.publicOrigin);
8
+ if (origin.protocol !== "http:" && origin.protocol !== "https:")
9
+ throw new TypeError("Socket publicOrigin must use HTTP or HTTPS");
10
+ const prefix = options.pathnamePrefix ?? "/_meeko/socket";
11
+ if (!prefix.startsWith("/") || prefix.endsWith("/"))
12
+ throw new TypeError("Invalid socket pathname prefix");
13
+ return async (incoming, socket, head) => {
14
+ let url;
15
+ try {
16
+ url = new URL(incoming.url ?? "", origin);
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ if (!url.pathname.startsWith(`${prefix}/`))
22
+ return false;
23
+ const reject = (status) => { if (options.reject)
24
+ options.reject(incoming, socket, status);
25
+ else
26
+ socket.destroy(); };
27
+ const name = url.pathname.slice(prefix.length + 1);
28
+ if (url.origin !== origin.origin || !/^[A-Za-z_$][\w$-]*$/.test(name)) {
29
+ reject(400);
30
+ return true;
31
+ }
32
+ if (incoming.method !== "GET" || incoming.headers.upgrade?.toLowerCase() !== "websocket") {
33
+ reject(400);
34
+ return true;
35
+ }
36
+ const headers = new Headers();
37
+ for (const [key, value] of Object.entries(incoming.headers)) {
38
+ if (value !== undefined)
39
+ headers.set(key, Array.isArray(value) ? value.join(", ") : value);
40
+ }
41
+ const request = new Request(url, { headers });
42
+ const roomId = url.searchParams.get("room") ?? undefined;
43
+ try {
44
+ await options.hub.preflight(name, request, roomId);
45
+ }
46
+ catch (error) {
47
+ reject(error instanceof ApiError ? error.status : 500);
48
+ options.onError?.(error);
49
+ return true;
50
+ }
51
+ let accepted;
52
+ try {
53
+ accepted = await options.upgrade(incoming, socket, head);
54
+ }
55
+ catch (error) {
56
+ socket.destroy();
57
+ options.onError?.(error);
58
+ return true;
59
+ }
60
+ try {
61
+ const connection = await options.hub.accept(name, request, accepted.peer, roomId);
62
+ let delivery = Promise.resolve();
63
+ accepted.onMessage((message) => {
64
+ delivery = delivery.then(() => connection.receive(message)).catch((error) => {
65
+ options.onError?.(error);
66
+ connection.close(error instanceof ApiError && error.status < 500 ? 1008 : 1011);
67
+ });
68
+ });
69
+ accepted.onClose(() => connection.close());
70
+ }
71
+ catch (error) {
72
+ accepted.peer.close(1008, "Unauthorized");
73
+ options.onError?.(error);
74
+ }
75
+ return true;
76
+ };
77
+ }
78
+ //# sourceMappingURL=node-upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-upgrade.js","sourceRoot":"","sources":["../../src/framework-server/node-upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport type { Duplex } from \"node:stream\";\nimport type { SocketHub, SocketPeer } from \"./socket.js\";\nimport { ApiError } from \"./contracts.js\";\n\n/** A WebSocket server library owns framing and the HTTP 101 response. */\nexport interface NodeAcceptedSocket {\n peer: SocketPeer;\n onMessage(listener: (message: string | Uint8Array) => void): void;\n onClose(listener: () => void): void;\n}\n\nexport interface NodeSocketUpgradeOptions {\n hub: SocketHub;\n /** Fixed public origin; never infer security policy from an untrusted Host header. */\n publicOrigin: string;\n pathnamePrefix?: string;\n upgrade(incoming: IncomingMessage, socket: Duplex, head: Buffer): Promise<NodeAcceptedSocket> | NodeAcceptedSocket;\n reject?(incoming: IncomingMessage, socket: Duplex, status: number): void;\n onError?(error: unknown): void;\n}\n\n/**\n * Handles the Node `upgrade` event after policy preflight. The host supplies a\n * WebSocket implementation; this adapter does not implement wire framing.\n */\nexport function createNodeSocketUpgradeHandler(options: NodeSocketUpgradeOptions): (incoming: IncomingMessage, socket: Duplex, head: Buffer) => Promise<boolean> {\n const origin = new URL(options.publicOrigin);\n if (origin.protocol !== \"http:\" && origin.protocol !== \"https:\") throw new TypeError(\"Socket publicOrigin must use HTTP or HTTPS\");\n const prefix = options.pathnamePrefix ?? \"/_meeko/socket\";\n if (!prefix.startsWith(\"/\") || prefix.endsWith(\"/\")) throw new TypeError(\"Invalid socket pathname prefix\");\n return async (incoming, socket, head) => {\n let url: URL;\n try { url = new URL(incoming.url ?? \"\", origin); }\n catch { return false; }\n if (!url.pathname.startsWith(`${prefix}/`)) return false;\n const reject = (status: number) => { if (options.reject) options.reject(incoming, socket, status); else socket.destroy(); };\n const name = url.pathname.slice(prefix.length + 1);\n if (url.origin !== origin.origin || !/^[A-Za-z_$][\\w$-]*$/.test(name)) { reject(400); return true; }\n if (incoming.method !== \"GET\" || incoming.headers.upgrade?.toLowerCase() !== \"websocket\") { reject(400); return true; }\n const headers = new Headers();\n for (const [key, value] of Object.entries(incoming.headers)) {\n if (value !== undefined) headers.set(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n const request = new Request(url, { headers });\n const roomId = url.searchParams.get(\"room\") ?? undefined;\n try { await options.hub.preflight(name, request, roomId); }\n catch (error) { reject(error instanceof ApiError ? error.status : 500); options.onError?.(error); return true; }\n let accepted: NodeAcceptedSocket;\n try { accepted = await options.upgrade(incoming, socket, head); }\n catch (error) { socket.destroy(); options.onError?.(error); return true; }\n try {\n const connection = await options.hub.accept(name, request, accepted.peer, roomId);\n let delivery = Promise.resolve();\n accepted.onMessage((message) => {\n delivery = delivery.then(() => connection.receive(message)).catch((error: unknown) => {\n options.onError?.(error);\n connection.close(error instanceof ApiError && error.status < 500 ? 1008 : 1011);\n });\n });\n accepted.onClose(() => connection.close());\n } catch (error) {\n accepted.peer.close(1008, \"Unauthorized\");\n options.onError?.(error);\n }\n return true;\n };\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import { type Server as HttpServer } from "node:http";
2
+ import type { NodeSocketUpgradeOptions } from "./node-upgrade.js";
3
+ export interface NodeWebSocketOptions extends Omit<NodeSocketUpgradeOptions, "upgrade" | "reject"> {
4
+ /** The same HTTP server used by the Fetch handler. */
5
+ server: HttpServer;
6
+ /** Maximum WebSocket frame payload, enforced before decoding. Defaults to 64 KiB. */
7
+ maxMessageBytes?: number;
8
+ /** Ping interval and idle timeout. Defaults to 30 seconds. */
9
+ heartbeatMs?: number;
10
+ }
11
+ export interface NodeWebSocketServer {
12
+ /** Detaches the upgrade listener and closes all peers. The HTTP server remains owned by its caller. */
13
+ close(): Promise<void>;
14
+ }
15
+ /** Attach Meeko channels to a Node HTTP server using ws for protocol framing. */
16
+ export declare function attachNodeWebSocketServer(options: NodeWebSocketOptions): NodeWebSocketServer;
@@ -0,0 +1,125 @@
1
+ import { STATUS_CODES } from "node:http";
2
+ import { createRequire } from "node:module";
3
+ import { createNodeSocketUpgradeHandler } from "./node-upgrade.js";
4
+ /** Attach Meeko channels to a Node HTTP server using ws for protocol framing. */
5
+ export function attachNodeWebSocketServer(options) {
6
+ const maxPayload = options.maxMessageBytes ?? 64 * 1024;
7
+ const heartbeatMs = options.heartbeatMs ?? 30_000;
8
+ if (!Number.isSafeInteger(maxPayload) || maxPayload <= 0)
9
+ throw new RangeError("Invalid socket message limit");
10
+ if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs <= 0)
11
+ throw new RangeError("Invalid socket heartbeat interval");
12
+ // Keep the optional server transport out of engine-only and HTTP-only imports.
13
+ const { WebSocket: WebSocketClass, WebSocketServer } = createRequire(import.meta.url)("ws");
14
+ const webSockets = new WebSocketServer({ noServer: true, maxPayload, perMessageDeflate: false });
15
+ const alive = new WeakMap();
16
+ let closing = false;
17
+ let closePromise;
18
+ const upgrade = (incoming, socket, head) => new Promise((resolve, reject) => {
19
+ try {
20
+ webSockets.handleUpgrade(incoming, socket, head, (peer) => {
21
+ alive.set(peer, true);
22
+ peer.on("pong", () => alive.set(peer, true));
23
+ peer.on("error", (error) => options.onError?.(error));
24
+ const pending = [];
25
+ let pendingBytes = 0;
26
+ let messageListener;
27
+ let closeListener;
28
+ let didClose = false;
29
+ peer.on("message", (data, isBinary) => {
30
+ if (isBinary) {
31
+ peer.close(1003, "Text messages required");
32
+ return;
33
+ }
34
+ const bytes = Array.isArray(data) ? Buffer.concat(data) : Buffer.isBuffer(data) ? data : Buffer.from(data);
35
+ if (messageListener) {
36
+ messageListener(bytes);
37
+ return;
38
+ }
39
+ pendingBytes += bytes.byteLength;
40
+ if (pendingBytes > maxPayload) {
41
+ peer.close(1009, "Message buffer exceeded");
42
+ return;
43
+ }
44
+ pending.push(bytes);
45
+ });
46
+ peer.once("close", () => { didClose = true; closeListener?.(); });
47
+ resolve({
48
+ peer: {
49
+ send(message) {
50
+ if (peer.readyState !== WebSocketClass.OPEN)
51
+ throw new Error("Socket is disconnected");
52
+ peer.send(message);
53
+ },
54
+ close(code, reason) { if (peer.readyState === WebSocketClass.OPEN)
55
+ peer.close(code, reason); },
56
+ get bufferedAmount() { return peer.bufferedAmount; },
57
+ },
58
+ onMessage(listener) {
59
+ messageListener = listener;
60
+ for (const message of pending)
61
+ listener(message);
62
+ pending.length = 0;
63
+ pendingBytes = 0;
64
+ },
65
+ onClose(listener) { closeListener = listener; if (didClose)
66
+ listener(); },
67
+ });
68
+ });
69
+ }
70
+ catch (error) {
71
+ reject(error);
72
+ }
73
+ });
74
+ const bridge = createNodeSocketUpgradeHandler({
75
+ hub: options.hub,
76
+ publicOrigin: options.publicOrigin,
77
+ ...(options.pathnamePrefix ? { pathnamePrefix: options.pathnamePrefix } : {}),
78
+ upgrade,
79
+ reject(_incoming, socket, status) {
80
+ if (!socket.destroyed)
81
+ socket.end(`HTTP/1.1 ${status} ${STATUS_CODES[status] ?? "Error"}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
82
+ },
83
+ ...(options.onError ? { onError: options.onError } : {}),
84
+ });
85
+ const onUpgrade = (incoming, socket, head) => {
86
+ if (closing) {
87
+ socket.destroy();
88
+ return;
89
+ }
90
+ void bridge(incoming, socket, head).then((handled) => {
91
+ if (!handled)
92
+ socket.destroy();
93
+ }).catch((error) => {
94
+ options.onError?.(error);
95
+ socket.destroy();
96
+ });
97
+ };
98
+ options.server.on("upgrade", onUpgrade);
99
+ const heartbeat = setInterval(() => {
100
+ for (const peer of webSockets.clients) {
101
+ if (alive.get(peer) === false) {
102
+ peer.terminate();
103
+ continue;
104
+ }
105
+ alive.set(peer, false);
106
+ if (peer.readyState === WebSocketClass.OPEN)
107
+ peer.ping();
108
+ }
109
+ }, heartbeatMs);
110
+ heartbeat.unref();
111
+ return {
112
+ close() {
113
+ if (closePromise)
114
+ return closePromise;
115
+ closing = true;
116
+ options.server.off("upgrade", onUpgrade);
117
+ clearInterval(heartbeat);
118
+ for (const peer of webSockets.clients)
119
+ peer.terminate();
120
+ closePromise = new Promise((resolve, reject) => webSockets.close((error) => error ? reject(error) : resolve()));
121
+ return closePromise;
122
+ },
123
+ };
124
+ }
125
+ //# sourceMappingURL=node-websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-websocket.js","sourceRoot":"","sources":["../../src/framework-server/node-websocket.ts"],"names":[],"mappings":"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","sourcesContent":["import { STATUS_CODES, type IncomingMessage, type Server as HttpServer } from \"node:http\";\nimport { createRequire } from \"node:module\";\nimport type { Duplex } from \"node:stream\";\nimport type WebSocket from \"ws\";\nimport { createNodeSocketUpgradeHandler } from \"./node-upgrade.js\";\nimport type { NodeAcceptedSocket, NodeSocketUpgradeOptions } from \"./node-upgrade.js\";\n\nexport interface NodeWebSocketOptions extends Omit<NodeSocketUpgradeOptions, \"upgrade\" | \"reject\"> {\n /** The same HTTP server used by the Fetch handler. */\n server: HttpServer;\n /** Maximum WebSocket frame payload, enforced before decoding. Defaults to 64 KiB. */\n maxMessageBytes?: number;\n /** Ping interval and idle timeout. Defaults to 30 seconds. */\n heartbeatMs?: number;\n}\n\nexport interface NodeWebSocketServer {\n /** Detaches the upgrade listener and closes all peers. The HTTP server remains owned by its caller. */\n close(): Promise<void>;\n}\n\n/** Attach Meeko channels to a Node HTTP server using ws for protocol framing. */\nexport function attachNodeWebSocketServer(options: NodeWebSocketOptions): NodeWebSocketServer {\n const maxPayload = options.maxMessageBytes ?? 64 * 1024;\n const heartbeatMs = options.heartbeatMs ?? 30_000;\n if (!Number.isSafeInteger(maxPayload) || maxPayload <= 0) throw new RangeError(\"Invalid socket message limit\");\n if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs <= 0) throw new RangeError(\"Invalid socket heartbeat interval\");\n // Keep the optional server transport out of engine-only and HTTP-only imports.\n const { WebSocket: WebSocketClass, WebSocketServer } = createRequire(import.meta.url)(\"ws\") as typeof import(\"ws\");\n const webSockets = new WebSocketServer({ noServer: true, maxPayload, perMessageDeflate: false });\n const alive = new WeakMap<WebSocket, boolean>();\n let closing = false;\n let closePromise: Promise<void> | undefined;\n\n const upgrade = (incoming: IncomingMessage, socket: Duplex, head: Buffer): Promise<NodeAcceptedSocket> =>\n new Promise((resolve, reject) => {\n try {\n webSockets.handleUpgrade(incoming, socket, head, (peer) => {\n alive.set(peer, true);\n peer.on(\"pong\", () => alive.set(peer, true));\n peer.on(\"error\", (error) => options.onError?.(error));\n const pending: Uint8Array[] = [];\n let pendingBytes = 0;\n let messageListener: ((message: string | Uint8Array) => void) | undefined;\n let closeListener: (() => void) | undefined;\n let didClose = false;\n peer.on(\"message\", (data, isBinary) => {\n if (isBinary) { peer.close(1003, \"Text messages required\"); return; }\n const bytes = Array.isArray(data) ? Buffer.concat(data) : Buffer.isBuffer(data) ? data : Buffer.from(data);\n if (messageListener) { messageListener(bytes); return; }\n pendingBytes += bytes.byteLength;\n if (pendingBytes > maxPayload) { peer.close(1009, \"Message buffer exceeded\"); return; }\n pending.push(bytes);\n });\n peer.once(\"close\", () => { didClose = true; closeListener?.(); });\n resolve({\n peer: {\n send(message) {\n if (peer.readyState !== WebSocketClass.OPEN) throw new Error(\"Socket is disconnected\");\n peer.send(message);\n },\n close(code, reason) { if (peer.readyState === WebSocketClass.OPEN) peer.close(code, reason); },\n get bufferedAmount() { return peer.bufferedAmount; },\n },\n onMessage(listener) {\n messageListener = listener;\n for (const message of pending) listener(message);\n pending.length = 0;\n pendingBytes = 0;\n },\n onClose(listener) { closeListener = listener; if (didClose) listener(); },\n });\n });\n } catch (error) { reject(error); }\n });\n\n const bridge = createNodeSocketUpgradeHandler({\n hub: options.hub,\n publicOrigin: options.publicOrigin,\n ...(options.pathnamePrefix ? { pathnamePrefix: options.pathnamePrefix } : {}),\n upgrade,\n reject(_incoming, socket, status) {\n if (!socket.destroyed) socket.end(`HTTP/1.1 ${status} ${STATUS_CODES[status] ?? \"Error\"}\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n`);\n },\n ...(options.onError ? { onError: options.onError } : {}),\n });\n const onUpgrade = (incoming: IncomingMessage, socket: Duplex, head: Buffer): void => {\n if (closing) { socket.destroy(); return; }\n void bridge(incoming, socket, head).then((handled) => {\n if (!handled) socket.destroy();\n }).catch((error: unknown) => {\n options.onError?.(error);\n socket.destroy();\n });\n };\n options.server.on(\"upgrade\", onUpgrade);\n const heartbeat = setInterval(() => {\n for (const peer of webSockets.clients) {\n if (alive.get(peer) === false) { peer.terminate(); continue; }\n alive.set(peer, false);\n if (peer.readyState === WebSocketClass.OPEN) peer.ping();\n }\n }, heartbeatMs);\n heartbeat.unref();\n\n return {\n close() {\n if (closePromise) return closePromise;\n closing = true;\n options.server.off(\"upgrade\", onUpgrade);\n clearInterval(heartbeat);\n for (const peer of webSockets.clients) peer.terminate();\n closePromise = new Promise<void>((resolve, reject) => webSockets.close((error) => error ? reject(error) : resolve()));\n return closePromise;\n },\n };\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { Schema } from "./contracts.js";
2
+ import type { ServerManifest } from "./discovery.js";
3
+ export interface OpenApiOptions {
4
+ manifest: ServerManifest;
5
+ info: {
6
+ title: string;
7
+ version: string;
8
+ };
9
+ /** Only schemas explicitly exported from shared/schemas belong here. */
10
+ publicSchemas: Readonly<Record<string, Schema>>;
11
+ /** For Valibot, supply a converter from @valibot/to-json-schema. */
12
+ toJsonSchema(schema: Schema): unknown;
13
+ load?: (file: string) => Promise<unknown>;
14
+ cookieName?: string;
15
+ }
16
+ /** Generates OpenAPI only from a caller-provided public schema registry. */
17
+ export declare function generateOpenApi(options: OpenApiOptions): Promise<Record<string, unknown>>;
@@ -0,0 +1,118 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import { serializeJson } from "../server.js";
3
+ function asSchema(value, name) {
4
+ if (!value || typeof value !== "object" || Array.isArray(value))
5
+ throw new Error(`OpenAPI schema ${name} is not representable as JSON Schema`);
6
+ serializeJson(value);
7
+ return value;
8
+ }
9
+ function properties(value, name) {
10
+ if (value.type !== "object" || !value.properties || typeof value.properties !== "object" || Array.isArray(value.properties))
11
+ throw new Error(`OpenAPI ${name} must convert to an object schema with properties`);
12
+ const entries = Object.entries(value.properties);
13
+ const result = {};
14
+ for (const [key, schema] of entries)
15
+ result[key] = asSchema(schema, `${name}.${key}`);
16
+ const required = new Set(Array.isArray(value.required) ? value.required.filter((key) => typeof key === "string") : []);
17
+ return { properties: result, required };
18
+ }
19
+ function publicPath(pathname) {
20
+ return pathname.replace(/\[\.\.\.[^\]]+\]/g, () => { throw new Error(`OpenAPI cannot represent catch-all route ${pathname}`); })
21
+ .replace(/\[([A-Za-z_$][\w$]*)\]/g, "{$1}");
22
+ }
23
+ /** Generates OpenAPI only from a caller-provided public schema registry. */
24
+ export async function generateOpenApi(options) {
25
+ const load = options.load ?? (async (file) => import(pathToFileURL(file).href));
26
+ const registry = new Map();
27
+ for (const [name, schema] of Object.entries(options.publicSchemas)) {
28
+ if (!/^[A-Za-z_$][\w$]*$/.test(name))
29
+ throw new Error(`Invalid public schema name: ${name}`);
30
+ if (registry.has(schema))
31
+ throw new Error(`Duplicate public schema instance: ${name}`);
32
+ registry.set(schema, name);
33
+ }
34
+ const schemas = {};
35
+ const ref = (schema, label) => {
36
+ if (!schema)
37
+ throw new Error(`OpenAPI ${label} requires a schema`);
38
+ const name = registry.get(schema);
39
+ if (!name)
40
+ throw new Error(`OpenAPI ${label} uses a schema outside publicSchemas`);
41
+ if (!schemas[name]) {
42
+ let converted;
43
+ try {
44
+ converted = options.toJsonSchema(schema);
45
+ }
46
+ catch (error) {
47
+ throw new Error(`OpenAPI schema ${name} cannot be converted: ${error instanceof Error ? error.message : String(error)}`);
48
+ }
49
+ schemas[name] = asSchema(converted, name);
50
+ }
51
+ return { $ref: `#/components/schemas/${name}` };
52
+ };
53
+ const paths = {};
54
+ const usePath = (path) => paths[path] ??= {};
55
+ const requireDefinition = async (file) => {
56
+ const module = await load(file);
57
+ if (!module || typeof module !== "object" || !("default" in module) || !module.default || typeof module.default !== "object")
58
+ throw new Error(`OpenAPI handler has no default definition: ${file}`);
59
+ return module.default;
60
+ };
61
+ for (const endpoint of options.manifest.api) {
62
+ const definition = await requireDefinition(endpoint.file);
63
+ if (definition.contentType === "response")
64
+ throw new Error(`OpenAPI non-JSON response requires an explicit representation: ${endpoint.file}`);
65
+ const operation = { operationId: `${endpoint.clientKey.replaceAll(".", "_")}_${endpoint.method.toLowerCase()}`, responses: { "200": { description: "OK", content: { "application/json": { schema: ref(definition.response, `${endpoint.file} response`) } } } } };
66
+ const parameters = [];
67
+ const routeParams = [...endpoint.pathname.matchAll(/\[([A-Za-z_$][\w$]*)\]/g)].map((match) => match[1]);
68
+ if (routeParams.length) {
69
+ const { $ref } = ref(definition.params, `${endpoint.file} params`);
70
+ const converted = schemas[$ref.slice("#/components/schemas/".length)];
71
+ const object = properties(converted, `${endpoint.file} params`);
72
+ for (const name of routeParams) {
73
+ const schema = object.properties[name];
74
+ if (!schema)
75
+ throw new Error(`OpenAPI path parameter ${name} is missing from ${endpoint.file} params schema`);
76
+ parameters.push({ in: "path", name, required: true, schema });
77
+ }
78
+ }
79
+ if (definition.query) {
80
+ const { $ref } = ref(definition.query, `${endpoint.file} query`);
81
+ const converted = schemas[$ref.slice("#/components/schemas/".length)];
82
+ const object = properties(converted, `${endpoint.file} query`);
83
+ for (const [name, schema] of Object.entries(object.properties))
84
+ parameters.push({ in: "query", name, required: object.required.has(name), schema });
85
+ }
86
+ if (parameters.length)
87
+ operation.parameters = parameters;
88
+ if (definition.body)
89
+ operation.requestBody = { required: true, content: { "application/json": { schema: ref(definition.body, `${endpoint.file} body`) } } };
90
+ if (definition.auth === "required")
91
+ operation.security = [{ sessionCookie: [] }];
92
+ const path = publicPath(endpoint.pathname);
93
+ const item = usePath(path);
94
+ const method = endpoint.method.toLowerCase();
95
+ if (item[method])
96
+ throw new Error(`OpenAPI duplicate operation: ${endpoint.method} ${path}`);
97
+ item[method] = operation;
98
+ }
99
+ for (const endpoint of options.manifest.rpc) {
100
+ const definition = await requireDefinition(endpoint.file);
101
+ const item = usePath(endpoint.pathname);
102
+ if (item.post)
103
+ throw new Error(`OpenAPI duplicate operation: POST ${endpoint.pathname}`);
104
+ item.post = {
105
+ operationId: `rpc_${endpoint.id.replaceAll(".", "_")}`,
106
+ requestBody: { required: true, content: { "application/json": { schema: ref(definition.input, `${endpoint.file} input`) } } },
107
+ responses: { "200": { description: "OK", content: { "application/json": { schema: ref(definition.output, `${endpoint.file} output`) } } } },
108
+ ...(definition.auth === "required" ? { security: [{ sessionCookie: [] }] } : {}),
109
+ };
110
+ }
111
+ return {
112
+ openapi: "3.1.0",
113
+ info: options.info,
114
+ paths,
115
+ components: { schemas, securitySchemes: { sessionCookie: { type: "apiKey", in: "cookie", name: options.cookieName ?? "__Host-meeko.sid" } } },
116
+ };
117
+ }
118
+ //# sourceMappingURL=openapi.js.map