@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.
- package/README.md +21 -19
- package/dist/autoimports-ts.d.ts +4 -4
- package/dist/autoimports-ts.js +86 -30
- package/dist/autoimports-ts.js.map +1 -1
- package/dist/autoimports.d.ts +2 -2
- package/dist/autoimports.js +23 -7
- package/dist/autoimports.js.map +1 -1
- package/dist/check.js +39 -10
- package/dist/check.js.map +1 -1
- package/dist/cli.js +37 -4
- package/dist/cli.js.map +1 -1
- package/dist/compiler/events.d.ts +2 -0
- package/dist/compiler/events.js +6 -0
- package/dist/compiler/events.js.map +1 -0
- package/dist/compiler/props.d.ts +12 -0
- package/dist/compiler/props.js +32 -0
- package/dist/compiler/props.js.map +1 -0
- package/dist/compiler.js +55 -12
- package/dist/compiler.js.map +1 -1
- package/dist/component-errors.d.ts +18 -0
- package/dist/component-errors.js +65 -0
- package/dist/component-errors.js.map +1 -0
- package/dist/devtools.js +2 -4
- package/dist/devtools.js.map +1 -1
- package/dist/dom.d.ts +11 -5
- package/dist/dom.js +288 -88
- package/dist/dom.js.map +1 -1
- package/dist/error-observer.d.ts +6 -0
- package/dist/error-observer.js +41 -0
- package/dist/error-observer.js.map +1 -0
- package/dist/framework/check-app.js +160 -5
- package/dist/framework/check-app.js.map +1 -1
- package/dist/framework/check-project.d.ts +4 -0
- package/dist/framework/check-project.js +42 -0
- package/dist/framework/check-project.js.map +1 -0
- package/dist/framework/client.d.ts +3 -0
- package/dist/framework/client.js +136 -33
- package/dist/framework/client.js.map +1 -1
- package/dist/framework/clients.js +12 -4
- package/dist/framework/clients.js.map +1 -1
- package/dist/framework/contracts-generate.js +66 -3
- package/dist/framework/contracts-generate.js.map +1 -1
- package/dist/framework/init.js +21 -200
- package/dist/framework/init.js.map +1 -1
- package/dist/framework/middleware.d.ts +14 -0
- package/dist/framework/middleware.js +16 -0
- package/dist/framework/middleware.js.map +1 -0
- package/dist/framework/page-contracts-check.d.ts +8 -0
- package/dist/framework/page-contracts-check.js +110 -0
- package/dist/framework/page-contracts-check.js.map +1 -0
- package/dist/framework/page-contracts-generate.d.ts +2 -0
- package/dist/framework/page-contracts-generate.js +94 -0
- package/dist/framework/page-contracts-generate.js.map +1 -0
- package/dist/framework/page.d.ts +7 -3
- package/dist/framework/page.js +3 -1
- package/dist/framework/page.js.map +1 -1
- package/dist/framework/prerender.js +11 -5
- package/dist/framework/prerender.js.map +1 -1
- package/dist/framework/project-templates.d.ts +3 -0
- package/dist/framework/project-templates.js +119 -0
- package/dist/framework/project-templates.js.map +1 -0
- package/dist/framework/project.d.ts +9 -0
- package/dist/framework/project.js +272 -0
- package/dist/framework/project.js.map +1 -0
- package/dist/framework/router.d.ts +3 -1
- package/dist/framework/router.js +94 -8
- package/dist/framework/router.js.map +1 -1
- package/dist/framework/server.d.ts +17 -3
- package/dist/framework/server.js +71 -24
- package/dist/framework/server.js.map +1 -1
- package/dist/framework/socket-contracts-generate.js +8 -5
- package/dist/framework/socket-contracts-generate.js.map +1 -1
- package/dist/framework/socket.d.ts +2 -0
- package/dist/framework/socket.js +119 -17
- package/dist/framework/socket.js.map +1 -1
- package/dist/framework/standard-schema-types.d.ts +7 -0
- package/dist/framework/standard-schema-types.js +69 -0
- package/dist/framework/standard-schema-types.js.map +1 -0
- package/dist/framework/state.d.ts +7 -0
- package/dist/framework/state.js +78 -14
- package/dist/framework/state.js.map +1 -1
- package/dist/framework/styles.d.ts +2 -0
- package/dist/framework/styles.js +6 -0
- package/dist/framework/styles.js.map +1 -0
- package/dist/framework/type-declarations.d.ts +5 -0
- package/dist/framework/type-declarations.js +84 -0
- package/dist/framework/type-declarations.js.map +1 -0
- package/dist/framework-server/contracts.d.ts +11 -2
- package/dist/framework-server/contracts.js +62 -7
- package/dist/framework-server/contracts.js.map +1 -1
- package/dist/framework-server/discovery.d.ts +4 -1
- package/dist/framework-server/discovery.js +21 -16
- package/dist/framework-server/discovery.js.map +1 -1
- package/dist/framework-server/local.d.ts +2 -0
- package/dist/framework-server/local.js +5 -3
- package/dist/framework-server/local.js.map +1 -1
- package/dist/framework-server/middleware.d.ts +4 -0
- package/dist/framework-server/middleware.js +56 -0
- package/dist/framework-server/middleware.js.map +1 -0
- package/dist/framework-server/node-upgrade.js +27 -9
- package/dist/framework-server/node-upgrade.js.map +1 -1
- package/dist/framework-server/node-websocket.js +4 -2
- package/dist/framework-server/node-websocket.js.map +1 -1
- package/dist/framework-server/session-stores.d.ts +9 -2
- package/dist/framework-server/session-stores.js +37 -11
- package/dist/framework-server/session-stores.js.map +1 -1
- package/dist/framework-server/sessions.d.ts +6 -0
- package/dist/framework-server/sessions.js +41 -10
- package/dist/framework-server/sessions.js.map +1 -1
- package/dist/framework-server/socket.js +62 -27
- package/dist/framework-server/socket.js.map +1 -1
- package/dist/framework-server.d.ts +2 -0
- package/dist/framework-server.js +1 -0
- package/dist/framework-server.js.map +1 -1
- package/dist/framework.d.ts +3 -1
- package/dist/framework.js +1 -0
- package/dist/framework.js.map +1 -1
- package/dist/hmr.js +74 -24
- package/dist/hmr.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init.js +27 -1
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +2 -2
- package/dist/internal.js.map +1 -1
- package/dist/node.d.ts +5 -1
- package/dist/node.js +80 -10
- package/dist/node.js.map +1 -1
- package/dist/reactivity.d.ts +12 -3
- package/dist/reactivity.js +94 -17
- package/dist/reactivity.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/vite.js +29 -9
- package/dist/vite.js.map +1 -1
- package/package.json +2 -2
package/dist/framework/server.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, readdir, stat } from "node:fs/promises";
|
|
2
3
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
3
4
|
import { escapeHtml, serializeJson } from "../server.js";
|
|
4
5
|
import { withRequestContext } from "../framework-server/context.js";
|
|
6
|
+
import { ApiError, errorResponse, normalizeServerError, observeServerError } from "../framework-server/contracts.js";
|
|
5
7
|
import { PageInputError, pageSearchObject, validatePageInputs } from "./page.js";
|
|
8
|
+
import { runRouteMiddleware } from "./middleware.js";
|
|
6
9
|
import { StateStore, withStateStore } from "./state.js";
|
|
7
10
|
import { createRouteManifest, matchRoute } from "./router.js";
|
|
8
11
|
function routeId(relativeFile) {
|
|
@@ -27,6 +30,7 @@ export async function discoverRoutes(projectRoot) {
|
|
|
27
30
|
}
|
|
28
31
|
await walk(pagesRoot);
|
|
29
32
|
const appShell = resolve(projectRoot, "app", "app.meeko");
|
|
33
|
+
const defaultLayout = resolve(projectRoot, "app", "layouts", "default.meeko");
|
|
30
34
|
let hasAppShell = false;
|
|
31
35
|
try {
|
|
32
36
|
hasAppShell = (await stat(appShell)).isFile();
|
|
@@ -35,12 +39,25 @@ export async function discoverRoutes(projectRoot) {
|
|
|
35
39
|
if (error.code !== "ENOENT")
|
|
36
40
|
throw error;
|
|
37
41
|
}
|
|
42
|
+
let hasDefaultLayout = false;
|
|
43
|
+
try {
|
|
44
|
+
hasDefaultLayout = (await lstat(defaultLayout)).isFile();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code !== "ENOENT")
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
if (hasDefaultLayout && files.has(join(pagesRoot, "+layout.meeko"))) {
|
|
51
|
+
throw new Error(`Both ${defaultLayout} and ${join(pagesRoot, "+layout.meeko")} define the root layout`);
|
|
52
|
+
}
|
|
38
53
|
const routes = [];
|
|
39
54
|
for (const file of files) {
|
|
40
55
|
if (!file.endsWith(".meeko") || basename(file).startsWith("+"))
|
|
41
56
|
continue;
|
|
42
57
|
const directory = dirname(file);
|
|
43
58
|
const layouts = hasAppShell ? [appShell] : [];
|
|
59
|
+
if (hasDefaultLayout)
|
|
60
|
+
layouts.push(defaultLayout);
|
|
44
61
|
let cursor = pagesRoot;
|
|
45
62
|
const relativeDirectory = relative(pagesRoot, directory);
|
|
46
63
|
if (files.has(join(cursor, "+layout.meeko")))
|
|
@@ -74,32 +91,43 @@ export async function discoverRoutes(projectRoot) {
|
|
|
74
91
|
}
|
|
75
92
|
return createRouteManifest(routes);
|
|
76
93
|
}
|
|
77
|
-
/** Portable Fetch handler. Node adapters can pass their Request directly. */
|
|
78
94
|
export function createFrameworkHandler(options) {
|
|
79
95
|
return async (request) => {
|
|
96
|
+
const fallbackRequestId = randomUUID();
|
|
80
97
|
if (request.method !== "GET" && request.method !== "HEAD")
|
|
81
|
-
return new Response(null, {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (!match)
|
|
85
|
-
return new Response("Not Found", { status: 404 });
|
|
98
|
+
return new Response(null, {
|
|
99
|
+
status: 405, headers: { Allow: "GET, HEAD", "x-request-id": fallbackRequestId, "cache-control": "no-store" },
|
|
100
|
+
});
|
|
86
101
|
const stateStore = new StateStore();
|
|
102
|
+
let errorRoute;
|
|
87
103
|
let requestContext;
|
|
88
104
|
const finish = (response) => {
|
|
89
|
-
if (!requestContext)
|
|
90
|
-
return response;
|
|
91
105
|
const headers = new Headers(response.headers);
|
|
92
|
-
headers.set("x-request-id", requestContext
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
headers.set("x-request-id", requestContext?.requestId ?? fallbackRequestId);
|
|
107
|
+
if (requestContext) {
|
|
108
|
+
headers.set("x-meeko-csrf-token", requestContext.session.current?.csrfToken ?? "");
|
|
109
|
+
if (requestContext.session.cookieHeader)
|
|
110
|
+
headers.append("set-cookie", requestContext.session.cookieHeader);
|
|
111
|
+
}
|
|
96
112
|
return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
|
|
97
113
|
};
|
|
98
114
|
try {
|
|
115
|
+
const url = new URL(request.url);
|
|
116
|
+
const match = matchRoute(options.routes, url.pathname);
|
|
117
|
+
if (!match)
|
|
118
|
+
return finish(new Response("Not Found", {
|
|
119
|
+
status: 404, headers: { "cache-control": "no-store" },
|
|
120
|
+
}));
|
|
121
|
+
errorRoute = match.route.error;
|
|
99
122
|
requestContext = await options.createContext?.(request);
|
|
100
123
|
if (requestContext && requestContext.request !== request)
|
|
101
124
|
throw new TypeError("Request context belongs to another request");
|
|
102
125
|
const render = async () => {
|
|
126
|
+
const middlewareResponse = options.loadMiddleware && await runRouteMiddleware(await options.loadMiddleware(), {
|
|
127
|
+
url, match, signal: request.signal, phase: "server", request,
|
|
128
|
+
});
|
|
129
|
+
if (middlewareResponse)
|
|
130
|
+
return finish(middlewareResponse);
|
|
103
131
|
const sidecar = match.route.sidecar && options.loadRoute ? await options.loadRoute(match.route.sidecar) : undefined;
|
|
104
132
|
const validated = await validatePageInputs(sidecar, match.params, pageSearchObject(url.searchParams));
|
|
105
133
|
const context = {
|
|
@@ -166,26 +194,45 @@ export function createFrameworkHandler(options) {
|
|
|
166
194
|
catch (error) {
|
|
167
195
|
if (error instanceof Response)
|
|
168
196
|
return finish(error);
|
|
197
|
+
const known = error instanceof PageInputError ? new ApiError(error.code, 400) : normalizeServerError(error);
|
|
198
|
+
const requestId = requestContext?.requestId ?? fallbackRequestId;
|
|
199
|
+
const report = (failure) => {
|
|
200
|
+
observeServerError(failure, requestId, options.onError);
|
|
201
|
+
};
|
|
202
|
+
if (known.status >= 500)
|
|
203
|
+
report(error);
|
|
204
|
+
if (request.headers.get("accept")?.split(",").some((value) => value.trim().startsWith("application/meeko+json"))) {
|
|
205
|
+
return finish(errorResponse(known, requestId));
|
|
206
|
+
}
|
|
169
207
|
if (error instanceof PageInputError)
|
|
170
208
|
return finish(new Response(error.code, {
|
|
171
209
|
status: 400, headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" },
|
|
172
210
|
}));
|
|
173
|
-
|
|
211
|
+
const errorFile = errorRoute;
|
|
212
|
+
if (errorFile) {
|
|
174
213
|
try {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
214
|
+
const renderError = async () => {
|
|
215
|
+
const errorPage = await options.loadPage(errorFile);
|
|
216
|
+
const errorRendered = withStateStore(stateStore, () => errorPage.renderWithState({
|
|
217
|
+
status: known.status, message: known.status >= 500 ? "Internal Server Error" : known.code,
|
|
218
|
+
}));
|
|
219
|
+
const document = `<!doctype html><html><head><meta charset="utf-8"><title>Error</title></head><body><div id="app">${errorRendered.html}</div></body></html>`;
|
|
220
|
+
return finish(new Response(request.method === "HEAD" ? null : document, {
|
|
221
|
+
status: known.status, headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
|
|
222
|
+
}));
|
|
223
|
+
};
|
|
224
|
+
return requestContext
|
|
225
|
+
? await withRequestContext(requestContext, options.serverFetch, renderError)
|
|
226
|
+
: await renderError();
|
|
183
227
|
}
|
|
184
|
-
catch {
|
|
228
|
+
catch (failure) {
|
|
229
|
+
report(failure);
|
|
185
230
|
// The error route failed; keep private errors out of the response.
|
|
186
231
|
}
|
|
187
232
|
}
|
|
188
|
-
return finish(new Response(request.method === "HEAD" ? null : "Internal Server Error"
|
|
233
|
+
return finish(new Response(request.method === "HEAD" ? null : known.status >= 500 ? "Internal Server Error" : known.code, {
|
|
234
|
+
status: known.status, headers: { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" },
|
|
235
|
+
}));
|
|
189
236
|
}
|
|
190
237
|
};
|
|
191
238
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/framework/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, stat } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { escapeHtml, serializeJson } from \"../server.js\";\nimport type { RequestContext } from \"../framework-server/context.js\";\nimport { withRequestContext } from \"../framework-server/context.js\";\nimport type { ServerFetch } from \"../framework-server/context.js\";\nimport { PageInputError, pageSearchObject, validatePageInputs } from \"./page.js\";\nimport type { PageSchema } from \"./page.js\";\nimport { StateStore, withStateStore } from \"./state.js\";\nimport { createRouteManifest, matchRoute } from \"./router.js\";\nimport type { RouteRecord } from \"./router.js\";\n\nfunction routeId(relativeFile: string): string {\n const parts = relativeFile.slice(0, -\".meeko\".length).split(sep);\n if (parts.at(-1) === \"index\") parts.pop();\n const visible = parts.filter((part) => !/^\\([^/()]+\\)$/.test(part));\n return visible.length ? `/${visible.join(\"/\")}` : \"/\";\n}\n\n/** Discovers pages once; the returned manifest is safe to serialize for the browser. */\nexport async function discoverRoutes(projectRoot: string): Promise<readonly RouteRecord[]> {\n const pagesRoot = resolve(projectRoot, \"app\", \"pages\");\n const files = new Set<string>();\n async function walk(directory: string): Promise<void> {\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) await walk(path);\n else if (entry.isFile()) files.add(path);\n }\n }\n await walk(pagesRoot);\n const appShell = resolve(projectRoot, \"app\", \"app.meeko\");\n let hasAppShell = false;\n try { hasAppShell = (await stat(appShell)).isFile(); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n const routes: RouteRecord[] = [];\n for (const file of files) {\n if (!file.endsWith(\".meeko\") || basename(file).startsWith(\"+\")) continue;\n const directory = dirname(file);\n const layouts: string[] = hasAppShell ? [appShell] : [];\n let cursor = pagesRoot;\n const relativeDirectory = relative(pagesRoot, directory);\n if (files.has(join(cursor, \"+layout.meeko\"))) layouts.push(join(cursor, \"+layout.meeko\"));\n if (relativeDirectory) for (const segment of relativeDirectory.split(sep)) {\n cursor = join(cursor, segment);\n if (files.has(join(cursor, \"+layout.meeko\"))) layouts.push(join(cursor, \"+layout.meeko\"));\n }\n let error: string | undefined;\n let errorCursor = directory;\n while (errorCursor.startsWith(pagesRoot)) {\n const candidate = join(errorCursor, \"+error.meeko\");\n if (files.has(candidate)) { error = candidate; break; }\n if (errorCursor === pagesRoot) break;\n errorCursor = dirname(errorCursor);\n }\n const sidecar = file.slice(0, -\".meeko\".length) + \".route.ts\";\n routes.push({\n id: routeId(relative(pagesRoot, file)),\n file,\n layouts,\n ...(error ? { error } : {}),\n ...(files.has(sidecar) ? { sidecar } : {}),\n });\n }\n return createRouteManifest(routes);\n}\n\nexport interface PageModule {\n css?: string;\n renderWithState(props: Record<string, unknown>): { html: string; state: Record<string, unknown> };\n renderToString?(props: Record<string, unknown>): string;\n}\n\nexport interface PageRoute {\n params?: PageSchema;\n search?: PageSchema;\n load?: (context: PageRequestContext) => Promise<unknown> | unknown;\n head?: (context: PageRequestContext & { data: unknown }) => { title?: string };\n render?: \"ssr\" | \"prerender\" | \"client\";\n prerenderEntries?: readonly Readonly<Record<string, string>>[] | (() => Promise<readonly Readonly<Record<string, string>>[]>);\n}\n\nexport interface PageRequestContext {\n readonly request: Request;\n readonly params: Readonly<Record<string, unknown>>;\n readonly search: Readonly<Record<string, unknown>>;\n readonly searchParams: URLSearchParams;\n readonly signal: AbortSignal;\n /** Public state transferred from this request to its browser app instance. */\n readonly state: StateStore;\n /** Present when the host supplies a per-request context. */\n readonly context?: RequestContext;\n}\n\nexport interface FrameworkHandlerOptions {\n readonly routes: readonly RouteRecord[];\n readonly loadPage: (file: string) => Promise<PageModule>;\n readonly loadRoute?: (file: string) => Promise<PageRoute>;\n readonly createContext?: (request: Request) => RequestContext | Promise<RequestContext>;\n /** Optional in-process dispatcher for automatic API/RPC calls during SSR. */\n readonly serverFetch?: ServerFetch;\n /** Optional host layout renderer; default composition uses <component is=\"props.outlet\"/>. */\n readonly renderLayout?: (file: string, children: string, context: PageRequestContext) => Promise<string> | string;\n readonly clientEntry?: string;\n}\n\n/** Portable Fetch handler. Node adapters can pass their Request directly. */\nexport function createFrameworkHandler(options: FrameworkHandlerOptions): (request: Request) => Promise<Response> {\n return async (request) => {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return new Response(null, { status: 405, headers: { Allow: \"GET, HEAD\" } });\n const url = new URL(request.url);\n const match = matchRoute(options.routes, url.pathname);\n if (!match) return new Response(\"Not Found\", { status: 404 });\n const stateStore = new StateStore();\n let requestContext: RequestContext | undefined;\n const finish = (response: Response): Response => {\n if (!requestContext) return response;\n const headers = new Headers(response.headers);\n headers.set(\"x-request-id\", requestContext.requestId);\n headers.set(\"x-meeko-csrf-token\", requestContext.session.current?.csrfToken ?? \"\");\n if (requestContext.session.cookieHeader) headers.append(\"set-cookie\", requestContext.session.cookieHeader);\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers });\n };\n try {\n requestContext = await options.createContext?.(request);\n if (requestContext && requestContext.request !== request) throw new TypeError(\"Request context belongs to another request\");\n const render = async (): Promise<Response> => {\n const sidecar = match.route.sidecar && options.loadRoute ? await options.loadRoute(match.route.sidecar) : undefined;\n const validated = await validatePageInputs(sidecar, match.params, pageSearchObject(url.searchParams));\n const context: PageRequestContext = {\n request, params: validated.params, search: validated.search, searchParams: url.searchParams, signal: request.signal, state: stateStore,\n ...(requestContext ? { context: requestContext } : {}),\n };\n const data = sidecar?.load ? await sidecar.load(context) : undefined;\n if (data instanceof Response) return finish(data);\n const props: Record<string, unknown> = {\n params: validated.params,\n search: validated.search,\n ...(data === undefined ? {} : { data }),\n };\n let module = await options.loadPage(match.route.file);\n const styles: string[] = [];\n if (module.css) styles.push(module.css);\n let input = props;\n let rendered: ReturnType<PageModule[\"renderWithState\"]>;\n if (options.renderLayout) {\n rendered = withStateStore(stateStore, () => module.renderWithState(input));\n for (const layout of [...match.route.layouts].reverse()) {\n const layoutModule = await options.loadPage(layout);\n if (layoutModule.css) styles.unshift(layoutModule.css);\n rendered = { html: await options.renderLayout(layout, rendered.html, context), state: rendered.state };\n }\n } else {\n for (const layout of [...match.route.layouts].reverse()) {\n const child = module;\n const childInput = input;\n const outlet = {\n renderWithState: () => child.renderWithState(childInput),\n renderToString: () => child.renderToString?.(childInput) ?? child.renderWithState(childInput).html,\n hydrateScope: () => { throw new Error(\"Server outlet cannot hydrate\"); },\n };\n module = await options.loadPage(layout);\n if (module.css) styles.unshift(module.css);\n input = { outlet };\n }\n rendered = withStateStore(stateStore, () => module.renderWithState(input));\n }\n const head = sidecar?.head?.({ ...context, data });\n const title = head?.title ? `<title>${escapeHtml(head.title)}</title>` : \"\";\n const styleTags = [...new Set(styles)].map((css) => `<style>${css.replaceAll(\"<\", \"\\\\3C \")}</style>`).join(\"\");\n const payload = serializeJson({ route: match.route.id, props, state: rendered.state, sharedState: stateStore.snapshot() });\n if (request.headers.get(\"accept\")?.split(\",\").some((value) => value.trim().startsWith(\"application/meeko+json\"))) {\n return finish(new Response(request.method === \"HEAD\" ? null : payload, {\n status: 200, headers: { \"content-type\": \"application/meeko+json; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n }\n const script = options.clientEntry ? `<script type=\"module\" src=\"${escapeHtml(options.clientEntry)}\"></script>` : \"\";\n const csrfMeta = requestContext?.session.current ? `<meta name=\"meeko-csrf-token\" content=\"${escapeHtml(requestContext.session.current.csrfToken)}\">` : \"\";\n const document = `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><link rel=\"icon\" href=\"data:,\">${csrfMeta}${styleTags}${title}</head><body><div id=\"app\">${rendered.html}</div><script id=\"meeko-payload\" type=\"application/json\">${payload}</script>${script}</body></html>`;\n return finish(new Response(request.method === \"HEAD\" ? null : document, {\n status: 200, headers: { \"content-type\": \"text/html; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n };\n return requestContext ? await withRequestContext(requestContext, options.serverFetch, render) : await render();\n } catch (error) {\n if (error instanceof Response) return finish(error);\n if (error instanceof PageInputError) return finish(new Response(error.code, {\n status: 400, headers: { \"content-type\": \"text/plain; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n if (match.route.error) {\n try {\n const errorPage = await options.loadPage(match.route.error);\n const errorRendered = withStateStore(stateStore, () => errorPage.renderWithState({\n status: 500, message: \"Internal Server Error\",\n }));\n const document = `<!doctype html><html><head><meta charset=\"utf-8\"><title>Error</title></head><body><div id=\"app\">${errorRendered.html}</div></body></html>`;\n return finish(new Response(request.method === \"HEAD\" ? null : document, {\n status: 500, headers: { \"content-type\": \"text/html; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n } catch {\n // The error route failed; keep private errors out of the response.\n }\n }\n return finish(new Response(request.method === \"HEAD\" ? null : \"Internal Server Error\", { status: 500 }));\n }\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/framework/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { lstat, readdir, stat } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { escapeHtml, serializeJson } from \"../server.js\";\nimport type { RequestContext } from \"../framework-server/context.js\";\nimport { withRequestContext } from \"../framework-server/context.js\";\nimport type { ServerFetch } from \"../framework-server/context.js\";\nimport { ApiError, errorResponse, normalizeServerError, observeServerError } from \"../framework-server/contracts.js\";\nimport { PageInputError, pageSearchObject, validatePageInputs } from \"./page.js\";\nimport { runRouteMiddleware } from \"./middleware.js\";\nimport type { RouteMiddleware } from \"./middleware.js\";\nimport type { PageSchema } from \"./page.js\";\nimport { StateStore, withStateStore } from \"./state.js\";\nimport { createRouteManifest, matchRoute } from \"./router.js\";\nimport type { RouteRecord } from \"./router.js\";\n\nfunction routeId(relativeFile: string): string {\n const parts = relativeFile.slice(0, -\".meeko\".length).split(sep);\n if (parts.at(-1) === \"index\") parts.pop();\n const visible = parts.filter((part) => !/^\\([^/()]+\\)$/.test(part));\n return visible.length ? `/${visible.join(\"/\")}` : \"/\";\n}\n\n/** Discovers pages once; the returned manifest is safe to serialize for the browser. */\nexport async function discoverRoutes(projectRoot: string): Promise<readonly RouteRecord[]> {\n const pagesRoot = resolve(projectRoot, \"app\", \"pages\");\n const files = new Set<string>();\n async function walk(directory: string): Promise<void> {\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) await walk(path);\n else if (entry.isFile()) files.add(path);\n }\n }\n await walk(pagesRoot);\n const appShell = resolve(projectRoot, \"app\", \"app.meeko\");\n const defaultLayout = resolve(projectRoot, \"app\", \"layouts\", \"default.meeko\");\n let hasAppShell = false;\n try { hasAppShell = (await stat(appShell)).isFile(); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n let hasDefaultLayout = false;\n try { hasDefaultLayout = (await lstat(defaultLayout)).isFile(); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n if (hasDefaultLayout && files.has(join(pagesRoot, \"+layout.meeko\"))) {\n throw new Error(`Both ${defaultLayout} and ${join(pagesRoot, \"+layout.meeko\")} define the root layout`);\n }\n const routes: RouteRecord[] = [];\n for (const file of files) {\n if (!file.endsWith(\".meeko\") || basename(file).startsWith(\"+\")) continue;\n const directory = dirname(file);\n const layouts: string[] = hasAppShell ? [appShell] : [];\n if (hasDefaultLayout) layouts.push(defaultLayout);\n let cursor = pagesRoot;\n const relativeDirectory = relative(pagesRoot, directory);\n if (files.has(join(cursor, \"+layout.meeko\"))) layouts.push(join(cursor, \"+layout.meeko\"));\n if (relativeDirectory) for (const segment of relativeDirectory.split(sep)) {\n cursor = join(cursor, segment);\n if (files.has(join(cursor, \"+layout.meeko\"))) layouts.push(join(cursor, \"+layout.meeko\"));\n }\n let error: string | undefined;\n let errorCursor = directory;\n while (errorCursor.startsWith(pagesRoot)) {\n const candidate = join(errorCursor, \"+error.meeko\");\n if (files.has(candidate)) { error = candidate; break; }\n if (errorCursor === pagesRoot) break;\n errorCursor = dirname(errorCursor);\n }\n const sidecar = file.slice(0, -\".meeko\".length) + \".route.ts\";\n routes.push({\n id: routeId(relative(pagesRoot, file)),\n file,\n layouts,\n ...(error ? { error } : {}),\n ...(files.has(sidecar) ? { sidecar } : {}),\n });\n }\n return createRouteManifest(routes);\n}\n\nexport interface PageModule {\n css?: string;\n renderWithState(props: Record<string, unknown>): { html: string; state: Record<string, unknown> };\n renderToString?(props: Record<string, unknown>): string;\n}\n\nexport interface PageRoute {\n params?: PageSchema;\n search?: PageSchema;\n load?: (context: PageRequestContext) => Promise<unknown> | unknown;\n head?: (context: PageRequestContext & { data: unknown }) => { title?: string };\n render?: \"ssr\" | \"prerender\";\n prerenderEntries?: readonly Readonly<Record<string, string>>[] | (() => Promise<readonly Readonly<Record<string, string>>[]>);\n}\n\nexport interface PageRequestContext {\n readonly request: Request;\n readonly params: Readonly<Record<string, unknown>>;\n readonly search: Readonly<Record<string, unknown>>;\n readonly searchParams: URLSearchParams;\n readonly signal: AbortSignal;\n /** Public state transferred from this request to its browser app instance. */\n readonly state: StateStore;\n /** Present when the host supplies a per-request context. */\n readonly context?: RequestContext;\n}\n\nexport interface FrameworkHandlerOptions<Route = PageRoute> {\n readonly routes: readonly RouteRecord[];\n readonly loadPage: (file: string) => Promise<PageModule>;\n readonly loadRoute?: (file: string) => Promise<Route>;\n /** Isomorphic route middleware, evaluated before the route sidecar loader. */\n readonly loadMiddleware?: () => Promise<readonly RouteMiddleware[]>;\n readonly createContext?: (request: Request) => RequestContext | Promise<RequestContext>;\n /** Optional in-process dispatcher for automatic API/RPC calls during SSR. */\n readonly serverFetch?: ServerFetch;\n /** Receives internal page failures with the request ID; never sent to the browser. */\n readonly onError?: (error: unknown, requestId: string) => void;\n /** Optional host layout renderer; default composition uses <component is=\"props.outlet\"/>. */\n readonly renderLayout?: (file: string, children: string, context: PageRequestContext) => Promise<string> | string;\n readonly clientEntry?: string;\n}\n\n/** A typed sidecar may narrow callback arguments after its schemas validate them. */\ntype TypedPageRoute = Omit<PageRoute, \"load\" | \"head\"> & {\n load?: (context: never) => Promise<unknown> | unknown;\n head?: (context: never) => { title?: string };\n};\n\n/** Portable Fetch handler. Node adapters can pass their Request directly. */\nexport function createFrameworkHandler(options: FrameworkHandlerOptions): (request: Request) => Promise<Response>;\nexport function createFrameworkHandler<Route extends TypedPageRoute>(options: FrameworkHandlerOptions<Route>): (request: Request) => Promise<Response>;\nexport function createFrameworkHandler(options: FrameworkHandlerOptions<unknown>): (request: Request) => Promise<Response> {\n return async (request) => {\n const fallbackRequestId = randomUUID();\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return new Response(null, {\n status: 405, headers: { Allow: \"GET, HEAD\", \"x-request-id\": fallbackRequestId, \"cache-control\": \"no-store\" },\n });\n const stateStore = new StateStore();\n let errorRoute: string | undefined;\n let requestContext: RequestContext | undefined;\n const finish = (response: Response): Response => {\n const headers = new Headers(response.headers);\n headers.set(\"x-request-id\", requestContext?.requestId ?? fallbackRequestId);\n if (requestContext) {\n headers.set(\"x-meeko-csrf-token\", requestContext.session.current?.csrfToken ?? \"\");\n if (requestContext.session.cookieHeader) headers.append(\"set-cookie\", requestContext.session.cookieHeader);\n }\n return new Response(response.body, { status: response.status, statusText: response.statusText, headers });\n };\n try {\n const url = new URL(request.url);\n const match = matchRoute(options.routes, url.pathname);\n if (!match) return finish(new Response(\"Not Found\", {\n status: 404, headers: { \"cache-control\": \"no-store\" },\n }));\n errorRoute = match.route.error;\n requestContext = await options.createContext?.(request);\n if (requestContext && requestContext.request !== request) throw new TypeError(\"Request context belongs to another request\");\n const render = async (): Promise<Response> => {\n const middlewareResponse = options.loadMiddleware && await runRouteMiddleware(await options.loadMiddleware(), {\n url, match, signal: request.signal, phase: \"server\", request,\n });\n if (middlewareResponse) return finish(middlewareResponse);\n const sidecar = match.route.sidecar && options.loadRoute ? await options.loadRoute(match.route.sidecar) as PageRoute : undefined;\n const validated = await validatePageInputs(sidecar, match.params, pageSearchObject(url.searchParams));\n const context: PageRequestContext = {\n request, params: validated.params, search: validated.search, searchParams: url.searchParams, signal: request.signal, state: stateStore,\n ...(requestContext ? { context: requestContext } : {}),\n };\n const data = sidecar?.load ? await sidecar.load(context) : undefined;\n if (data instanceof Response) return finish(data);\n const props: Record<string, unknown> = {\n params: validated.params,\n search: validated.search,\n ...(data === undefined ? {} : { data }),\n };\n let module = await options.loadPage(match.route.file);\n const styles: string[] = [];\n if (module.css) styles.push(module.css);\n let input = props;\n let rendered: ReturnType<PageModule[\"renderWithState\"]>;\n if (options.renderLayout) {\n rendered = withStateStore(stateStore, () => module.renderWithState(input));\n for (const layout of [...match.route.layouts].reverse()) {\n const layoutModule = await options.loadPage(layout);\n if (layoutModule.css) styles.unshift(layoutModule.css);\n rendered = { html: await options.renderLayout(layout, rendered.html, context), state: rendered.state };\n }\n } else {\n for (const layout of [...match.route.layouts].reverse()) {\n const child = module;\n const childInput = input;\n const outlet = {\n renderWithState: () => child.renderWithState(childInput),\n renderToString: () => child.renderToString?.(childInput) ?? child.renderWithState(childInput).html,\n hydrateScope: () => { throw new Error(\"Server outlet cannot hydrate\"); },\n };\n module = await options.loadPage(layout);\n if (module.css) styles.unshift(module.css);\n input = { outlet };\n }\n rendered = withStateStore(stateStore, () => module.renderWithState(input));\n }\n const head = sidecar?.head?.({ ...context, data });\n const title = head?.title ? `<title>${escapeHtml(head.title)}</title>` : \"\";\n const styleTags = [...new Set(styles)].map((css) => `<style>${css.replaceAll(\"<\", \"\\\\3C \")}</style>`).join(\"\");\n const payload = serializeJson({ route: match.route.id, props, state: rendered.state, sharedState: stateStore.snapshot() });\n if (request.headers.get(\"accept\")?.split(\",\").some((value) => value.trim().startsWith(\"application/meeko+json\"))) {\n return finish(new Response(request.method === \"HEAD\" ? null : payload, {\n status: 200, headers: { \"content-type\": \"application/meeko+json; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n }\n const script = options.clientEntry ? `<script type=\"module\" src=\"${escapeHtml(options.clientEntry)}\"></script>` : \"\";\n const csrfMeta = requestContext?.session.current ? `<meta name=\"meeko-csrf-token\" content=\"${escapeHtml(requestContext.session.current.csrfToken)}\">` : \"\";\n const document = `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><link rel=\"icon\" href=\"data:,\">${csrfMeta}${styleTags}${title}</head><body><div id=\"app\">${rendered.html}</div><script id=\"meeko-payload\" type=\"application/json\">${payload}</script>${script}</body></html>`;\n return finish(new Response(request.method === \"HEAD\" ? null : document, {\n status: 200, headers: { \"content-type\": \"text/html; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n };\n return requestContext ? await withRequestContext(requestContext, options.serverFetch, render) : await render();\n } catch (error) {\n if (error instanceof Response) return finish(error);\n const known = error instanceof PageInputError ? new ApiError(error.code, 400) : normalizeServerError(error);\n const requestId = requestContext?.requestId ?? fallbackRequestId;\n const report = (failure: unknown): void => {\n observeServerError(failure, requestId, options.onError);\n };\n if (known.status >= 500) report(error);\n if (request.headers.get(\"accept\")?.split(\",\").some((value) => value.trim().startsWith(\"application/meeko+json\"))) {\n return finish(errorResponse(known, requestId));\n }\n if (error instanceof PageInputError) return finish(new Response(error.code, {\n status: 400, headers: { \"content-type\": \"text/plain; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n const errorFile = errorRoute;\n if (errorFile) {\n try {\n const renderError = async (): Promise<Response> => {\n const errorPage = await options.loadPage(errorFile);\n const errorRendered = withStateStore(stateStore, () => errorPage.renderWithState({\n status: known.status, message: known.status >= 500 ? \"Internal Server Error\" : known.code,\n }));\n const document = `<!doctype html><html><head><meta charset=\"utf-8\"><title>Error</title></head><body><div id=\"app\">${errorRendered.html}</div></body></html>`;\n return finish(new Response(request.method === \"HEAD\" ? null : document, {\n status: known.status, headers: { \"content-type\": \"text/html; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n };\n return requestContext\n ? await withRequestContext(requestContext, options.serverFetch, renderError)\n : await renderError();\n } catch (failure) {\n report(failure);\n // The error route failed; keep private errors out of the response.\n }\n }\n return finish(new Response(request.method === \"HEAD\" ? null : known.status >= 500 ? \"Internal Server Error\" : known.code, {\n status: known.status, headers: { \"content-type\": \"text/plain; charset=utf-8\", \"cache-control\": \"no-store\" },\n }));\n }\n };\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import ts from "@typescript/typescript6";
|
|
4
4
|
import { discoverServerManifest } from "../framework-server/discovery.js";
|
|
5
|
+
import { standardSchemaTypes } from "./standard-schema-types.js";
|
|
5
6
|
function inside(parent, child) {
|
|
6
7
|
const path = relative(parent, child);
|
|
7
8
|
return path === "" || (path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path));
|
|
@@ -53,11 +54,10 @@ function property(object, name) {
|
|
|
53
54
|
return undefined;
|
|
54
55
|
return ts.isPropertyAssignment(entry) ? entry.initializer : ts.isShorthandPropertyAssignment(entry) ? entry.name : undefined;
|
|
55
56
|
}
|
|
56
|
-
function publicSchemas(file, root, options) {
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
57
|
+
function publicSchemas(file, root, options, program, checker) {
|
|
58
|
+
const ast = program.getSourceFile(file);
|
|
59
|
+
if (!ast)
|
|
59
60
|
throw new Error(`${file}: Socket channel file missing`);
|
|
60
|
-
const ast = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);
|
|
61
61
|
const imports = new Map();
|
|
62
62
|
for (const statement of ast.statements) {
|
|
63
63
|
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
|
|
@@ -110,6 +110,7 @@ function publicSchemas(file, root, options) {
|
|
|
110
110
|
const exported = imported.exported === "*" ? member : imported.exported;
|
|
111
111
|
if (!exported || (member && imported.exported !== "*"))
|
|
112
112
|
throw new Error(`${file}: ${name}.${event} must reference one shared export`);
|
|
113
|
+
standardSchemaTypes(value, checker, () => new Error(`${file}: ${name}.${event} must expose concrete Standard Schema input and output types`));
|
|
113
114
|
validateSharedGraph(imported.file, root, shared, options, seen);
|
|
114
115
|
if (result[event])
|
|
115
116
|
throw new Error(`${file}: duplicate Socket event ${name}.${event}`);
|
|
@@ -125,7 +126,9 @@ export async function generateSocketContracts(projectRoot) {
|
|
|
125
126
|
const manifest = await discoverServerManifest(root);
|
|
126
127
|
const output = join(root, ".meeko", "generated", "socket-contracts.ts");
|
|
127
128
|
const options = compilerOptions(root);
|
|
128
|
-
const
|
|
129
|
+
const program = ts.createProgram((manifest.socket ?? []).map((endpoint) => endpoint.file), { ...options, noEmit: true });
|
|
130
|
+
const checker = program.getTypeChecker();
|
|
131
|
+
const channels = (manifest.socket ?? []).map((endpoint) => ({ name: endpoint.name, schemas: publicSchemas(endpoint.file, root, options, program, checker) }));
|
|
129
132
|
const sources = [...new Set(channels.flatMap(({ schemas }) => [...Object.values(schemas.clientToServer), ...Object.values(schemas.serverToClient)].map((entry) => entry.file)))].sort();
|
|
130
133
|
const aliases = new Map(sources.map((file, index) => [file, `__schema${index}`]));
|
|
131
134
|
const lines = sources.map((file) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-contracts-generate.js","sourceRoot":"","sources":["../../src/framework/socket-contracts-generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport ts from \"@typescript/typescript6\";\nimport { discoverServerManifest } from \"../framework-server/discovery.js\";\n\ninterface ImportedSymbol { file: string; exported: string; }\ninterface SchemaReference { file: string; exported: string; }\n\nfunction inside(parent: string, child: string): boolean {\n const path = relative(parent, child);\n return path === \"\" || (path !== \"..\" && !path.startsWith(`..${sep}`) && !isAbsolute(path));\n}\n\nfunction compilerOptions(root: string): ts.CompilerOptions {\n const config = ts.findConfigFile(root, ts.sys.fileExists, \"tsconfig.json\");\n if (!config) return { module: ts.ModuleKind.NodeNext, moduleResolution: ts.ModuleResolutionKind.NodeNext };\n const loaded = ts.readConfigFile(config, ts.sys.readFile);\n if (loaded.error) throw new Error(ts.flattenDiagnosticMessageText(loaded.error.messageText, \"\\n\"));\n return ts.parseJsonConfigFileContent(loaded.config, ts.sys, dirname(config)).options;\n}\n\nfunction resolveImport(specifier: string, importer: string, options: ts.CompilerOptions): string {\n const target = ts.resolveModuleName(specifier, importer, options, ts.sys).resolvedModule?.resolvedFileName;\n if (!target) throw new Error(`${importer}: unresolved import ${specifier}`);\n return target;\n}\n\nfunction validateSharedGraph(file: string, root: string, shared: string, options: ts.CompilerOptions, seen: Set<string>): void {\n if (seen.has(file)) return;\n seen.add(file);\n if (!inside(shared, file)) throw new Error(`${file}: public Socket schema must stay inside shared/`);\n const source = ts.sys.readFile(file);\n if (source === undefined) throw new Error(`${file}: Socket schema file missing`);\n const ast = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);\n const check = (specifier: string): void => {\n const target = resolveImport(specifier, file, options);\n if (inside(root, target) && !relative(root, target).split(sep).includes(\"node_modules\")) validateSharedGraph(target, root, shared, options, seen);\n };\n for (const statement of ast.statements) {\n if ((ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) check(statement.moduleSpecifier.text);\n }\n const visit = (node: ts.Node): void => {\n if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)) check(node.argument.literal.text);\n ts.forEachChild(node, visit);\n };\n visit(ast);\n}\n\nfunction property(object: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined {\n const entry = object.properties.find((candidate) => (ts.isPropertyAssignment(candidate) || ts.isShorthandPropertyAssignment(candidate)) && candidate.name && (ts.isIdentifier(candidate.name) || ts.isStringLiteral(candidate.name)) && candidate.name.text === name);\n if (!entry) return undefined;\n return ts.isPropertyAssignment(entry) ? entry.initializer : ts.isShorthandPropertyAssignment(entry) ? entry.name : undefined;\n}\n\nfunction publicSchemas(file: string, root: string, options: ts.CompilerOptions): { clientToServer: Record<string, SchemaReference>; serverToClient: Record<string, SchemaReference> } {\n const source = ts.sys.readFile(file);\n if (source === undefined) throw new Error(`${file}: Socket channel file missing`);\n const ast = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);\n const imports = new Map<string, ImportedSymbol>();\n for (const statement of ast.statements) {\n if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;\n const clause = statement.importClause;\n if (!clause || clause.isTypeOnly) continue;\n const target = resolveImport(statement.moduleSpecifier.text, file, options);\n if (clause.name) imports.set(clause.name.text, { file: target, exported: \"default\" });\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n for (const element of clause.namedBindings.elements) {\n if (!element.isTypeOnly) imports.set(element.name.text, { file: target, exported: element.propertyName?.text ?? element.name.text });\n }\n }\n if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) imports.set(clause.namedBindings.name.text, { file: target, exported: \"*\" });\n }\n const assignment = ast.statements.find((statement): statement is ts.ExportAssignment => ts.isExportAssignment(statement) && !statement.isExportEquals);\n const call = assignment?.expression;\n if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression) || call.expression.text !== \"defineSocketChannel\" || !call.arguments[0] || !ts.isObjectLiteralExpression(call.arguments[0])) {\n throw new Error(`${file}: public Socket contract requires export default defineSocketChannel({...})`);\n }\n const shared = join(root, \"shared\");\n const seen = new Set<string>();\n const direction = (name: \"clientToServer\" | \"serverToClient\"): Record<string, SchemaReference> => {\n const object = property(call.arguments[0] as ts.ObjectLiteralExpression, name);\n if (!object || !ts.isObjectLiteralExpression(object)) throw new Error(`${file}: ${name} must be a static object`);\n const result: Record<string, SchemaReference> = Object.create(null);\n for (const entry of object.properties) {\n if (!ts.isPropertyAssignment(entry) || !(ts.isIdentifier(entry.name) || ts.isStringLiteral(entry.name))) throw new Error(`${file}: ${name} requires named schema properties`);\n const event = entry.name.text;\n const value = entry.initializer;\n let local: string;\n let member: string | undefined;\n if (ts.isIdentifier(value)) local = value.text;\n else if (ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression)) { local = value.expression.text; member = value.name.text; }\n else throw new Error(`${file}: ${name}.${event} must reference an imported shared schema`);\n const imported = imports.get(local);\n if (!imported || !inside(shared, imported.file)) throw new Error(`${file}: ${name}.${event} must come from shared/`);\n const exported = imported.exported === \"*\" ? member : imported.exported;\n if (!exported || (member && imported.exported !== \"*\")) throw new Error(`${file}: ${name}.${event} must reference one shared export`);\n validateSharedGraph(imported.file, root, shared, options, seen);\n if (result[event]) throw new Error(`${file}: duplicate Socket event ${name}.${event}`);\n result[event] = { file: imported.file, exported };\n }\n return result;\n };\n return { clientToServer: direction(\"clientToServer\"), serverToClient: direction(\"serverToClient\") };\n}\n\n/** Emits browser-safe, inferred Socket contracts from server channel definitions. */\nexport async function generateSocketContracts(projectRoot: string): Promise<{ file: string; count: number }> {\n const root = resolve(projectRoot);\n const manifest = await discoverServerManifest(root);\n const output = join(root, \".meeko\", \"generated\", \"socket-contracts.ts\");\n const options = compilerOptions(root);\n const channels = (manifest.socket ?? []).map((endpoint) => ({ name: endpoint.name, schemas: publicSchemas(endpoint.file, root, options) }));\n const sources = [...new Set(channels.flatMap(({ schemas }) => [...Object.values(schemas.clientToServer), ...Object.values(schemas.serverToClient)].map((entry) => entry.file)))].sort();\n const aliases = new Map(sources.map((file, index) => [file, `__schema${index}`]));\n const lines = sources.map((file) => {\n const target = relative(dirname(output), file).replaceAll(\"\\\\\", \"/\").replace(/\\.(?:ts|mts|cts)$/, (suffix) => suffix === \".mts\" ? \".mjs\" : suffix === \".cts\" ? \".cjs\" : \".js\");\n return `import * as ${aliases.get(file)} from ${JSON.stringify(target.startsWith(\".\") ? target : `./${target}`)};`;\n });\n lines.push(\"export const socketContracts = {\");\n for (const channel of channels) {\n lines.push(` ${JSON.stringify(channel.name)}: {`);\n for (const direction of [\"clientToServer\", \"serverToClient\"] as const) {\n lines.push(` ${direction}: {`);\n for (const [event, schema] of Object.entries(channel.schemas[direction])) lines.push(` ${JSON.stringify(event)}: ${aliases.get(schema.file)}[${JSON.stringify(schema.exported)}],`);\n lines.push(\" },\");\n }\n lines.push(\" },\");\n }\n lines.push(\"} as const;\", \"\");\n const code = lines.join(\"\\n\");\n let previous: string | undefined;\n try { previous = await readFile(output, \"utf8\"); } catch { /* First generation. */ }\n if (previous !== code) { await mkdir(dirname(output), { recursive: true }); await writeFile(output, code); }\n return { file: output, count: channels.length };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"socket-contracts-generate.js","sourceRoot":"","sources":["../../src/framework/socket-contracts-generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport ts from \"@typescript/typescript6\";\nimport { discoverServerManifest } from \"../framework-server/discovery.js\";\nimport { standardSchemaTypes } from \"./standard-schema-types.js\";\n\ninterface ImportedSymbol { file: string; exported: string; }\ninterface SchemaReference { file: string; exported: string; }\n\nfunction inside(parent: string, child: string): boolean {\n const path = relative(parent, child);\n return path === \"\" || (path !== \"..\" && !path.startsWith(`..${sep}`) && !isAbsolute(path));\n}\n\nfunction compilerOptions(root: string): ts.CompilerOptions {\n const config = ts.findConfigFile(root, ts.sys.fileExists, \"tsconfig.json\");\n if (!config) return { module: ts.ModuleKind.NodeNext, moduleResolution: ts.ModuleResolutionKind.NodeNext };\n const loaded = ts.readConfigFile(config, ts.sys.readFile);\n if (loaded.error) throw new Error(ts.flattenDiagnosticMessageText(loaded.error.messageText, \"\\n\"));\n return ts.parseJsonConfigFileContent(loaded.config, ts.sys, dirname(config)).options;\n}\n\nfunction resolveImport(specifier: string, importer: string, options: ts.CompilerOptions): string {\n const target = ts.resolveModuleName(specifier, importer, options, ts.sys).resolvedModule?.resolvedFileName;\n if (!target) throw new Error(`${importer}: unresolved import ${specifier}`);\n return target;\n}\n\nfunction validateSharedGraph(file: string, root: string, shared: string, options: ts.CompilerOptions, seen: Set<string>): void {\n if (seen.has(file)) return;\n seen.add(file);\n if (!inside(shared, file)) throw new Error(`${file}: public Socket schema must stay inside shared/`);\n const source = ts.sys.readFile(file);\n if (source === undefined) throw new Error(`${file}: Socket schema file missing`);\n const ast = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);\n const check = (specifier: string): void => {\n const target = resolveImport(specifier, file, options);\n if (inside(root, target) && !relative(root, target).split(sep).includes(\"node_modules\")) validateSharedGraph(target, root, shared, options, seen);\n };\n for (const statement of ast.statements) {\n if ((ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) check(statement.moduleSpecifier.text);\n }\n const visit = (node: ts.Node): void => {\n if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)) check(node.argument.literal.text);\n ts.forEachChild(node, visit);\n };\n visit(ast);\n}\n\nfunction property(object: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined {\n const entry = object.properties.find((candidate) => (ts.isPropertyAssignment(candidate) || ts.isShorthandPropertyAssignment(candidate)) && candidate.name && (ts.isIdentifier(candidate.name) || ts.isStringLiteral(candidate.name)) && candidate.name.text === name);\n if (!entry) return undefined;\n return ts.isPropertyAssignment(entry) ? entry.initializer : ts.isShorthandPropertyAssignment(entry) ? entry.name : undefined;\n}\n\nfunction publicSchemas(file: string, root: string, options: ts.CompilerOptions, program: ts.Program, checker: ts.TypeChecker): { clientToServer: Record<string, SchemaReference>; serverToClient: Record<string, SchemaReference> } {\n const ast = program.getSourceFile(file);\n if (!ast) throw new Error(`${file}: Socket channel file missing`);\n const imports = new Map<string, ImportedSymbol>();\n for (const statement of ast.statements) {\n if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;\n const clause = statement.importClause;\n if (!clause || clause.isTypeOnly) continue;\n const target = resolveImport(statement.moduleSpecifier.text, file, options);\n if (clause.name) imports.set(clause.name.text, { file: target, exported: \"default\" });\n if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {\n for (const element of clause.namedBindings.elements) {\n if (!element.isTypeOnly) imports.set(element.name.text, { file: target, exported: element.propertyName?.text ?? element.name.text });\n }\n }\n if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) imports.set(clause.namedBindings.name.text, { file: target, exported: \"*\" });\n }\n const assignment = ast.statements.find((statement): statement is ts.ExportAssignment => ts.isExportAssignment(statement) && !statement.isExportEquals);\n const call = assignment?.expression;\n if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression) || call.expression.text !== \"defineSocketChannel\" || !call.arguments[0] || !ts.isObjectLiteralExpression(call.arguments[0])) {\n throw new Error(`${file}: public Socket contract requires export default defineSocketChannel({...})`);\n }\n const shared = join(root, \"shared\");\n const seen = new Set<string>();\n const direction = (name: \"clientToServer\" | \"serverToClient\"): Record<string, SchemaReference> => {\n const object = property(call.arguments[0] as ts.ObjectLiteralExpression, name);\n if (!object || !ts.isObjectLiteralExpression(object)) throw new Error(`${file}: ${name} must be a static object`);\n const result: Record<string, SchemaReference> = Object.create(null);\n for (const entry of object.properties) {\n if (!ts.isPropertyAssignment(entry) || !(ts.isIdentifier(entry.name) || ts.isStringLiteral(entry.name))) throw new Error(`${file}: ${name} requires named schema properties`);\n const event = entry.name.text;\n const value = entry.initializer;\n let local: string;\n let member: string | undefined;\n if (ts.isIdentifier(value)) local = value.text;\n else if (ts.isPropertyAccessExpression(value) && ts.isIdentifier(value.expression)) { local = value.expression.text; member = value.name.text; }\n else throw new Error(`${file}: ${name}.${event} must reference an imported shared schema`);\n const imported = imports.get(local);\n if (!imported || !inside(shared, imported.file)) throw new Error(`${file}: ${name}.${event} must come from shared/`);\n const exported = imported.exported === \"*\" ? member : imported.exported;\n if (!exported || (member && imported.exported !== \"*\")) throw new Error(`${file}: ${name}.${event} must reference one shared export`);\n standardSchemaTypes(value, checker, () => new Error(`${file}: ${name}.${event} must expose concrete Standard Schema input and output types`));\n validateSharedGraph(imported.file, root, shared, options, seen);\n if (result[event]) throw new Error(`${file}: duplicate Socket event ${name}.${event}`);\n result[event] = { file: imported.file, exported };\n }\n return result;\n };\n return { clientToServer: direction(\"clientToServer\"), serverToClient: direction(\"serverToClient\") };\n}\n\n/** Emits browser-safe, inferred Socket contracts from server channel definitions. */\nexport async function generateSocketContracts(projectRoot: string): Promise<{ file: string; count: number }> {\n const root = resolve(projectRoot);\n const manifest = await discoverServerManifest(root);\n const output = join(root, \".meeko\", \"generated\", \"socket-contracts.ts\");\n const options = compilerOptions(root);\n const program = ts.createProgram((manifest.socket ?? []).map((endpoint) => endpoint.file), { ...options, noEmit: true });\n const checker = program.getTypeChecker();\n const channels = (manifest.socket ?? []).map((endpoint) => ({ name: endpoint.name, schemas: publicSchemas(endpoint.file, root, options, program, checker) }));\n const sources = [...new Set(channels.flatMap(({ schemas }) => [...Object.values(schemas.clientToServer), ...Object.values(schemas.serverToClient)].map((entry) => entry.file)))].sort();\n const aliases = new Map(sources.map((file, index) => [file, `__schema${index}`]));\n const lines = sources.map((file) => {\n const target = relative(dirname(output), file).replaceAll(\"\\\\\", \"/\").replace(/\\.(?:ts|mts|cts)$/, (suffix) => suffix === \".mts\" ? \".mjs\" : suffix === \".cts\" ? \".cjs\" : \".js\");\n return `import * as ${aliases.get(file)} from ${JSON.stringify(target.startsWith(\".\") ? target : `./${target}`)};`;\n });\n lines.push(\"export const socketContracts = {\");\n for (const channel of channels) {\n lines.push(` ${JSON.stringify(channel.name)}: {`);\n for (const direction of [\"clientToServer\", \"serverToClient\"] as const) {\n lines.push(` ${direction}: {`);\n for (const [event, schema] of Object.entries(channel.schemas[direction])) lines.push(` ${JSON.stringify(event)}: ${aliases.get(schema.file)}[${JSON.stringify(schema.exported)}],`);\n lines.push(\" },\");\n }\n lines.push(\" },\");\n }\n lines.push(\"} as const;\", \"\");\n const code = lines.join(\"\\n\");\n let previous: string | undefined;\n try { previous = await readFile(output, \"utf8\"); } catch { /* First generation. */ }\n if (previous !== code) { await mkdir(dirname(output), { recursive: true }); await writeFile(output, code); }\n return { file: output, count: channels.length };\n}\n"]}
|
|
@@ -49,6 +49,8 @@ export interface SocketClientOptions {
|
|
|
49
49
|
readonly maxRetryMs?: number;
|
|
50
50
|
readonly maxMessageBytes?: number;
|
|
51
51
|
readonly webSocketFactory?: (url: string) => WebSocket;
|
|
52
|
+
/** Receives invalid inbound messages and consumer listener failures. */
|
|
53
|
+
readonly onError?: (error: unknown) => void;
|
|
52
54
|
}
|
|
53
55
|
export interface SocketRegistryOptions extends SocketClientOptions {
|
|
54
56
|
/** HTTP(S) or WS(S) base URL. Defaults to the current browser origin. */
|
package/dist/framework/socket.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { onCleanup, signal } from "../reactivity.js";
|
|
1
|
+
import { currentEffectOwner, onCleanup, signal } from "../reactivity.js";
|
|
2
|
+
import { logClientError, observeCallback } from "../error-observer.js";
|
|
3
|
+
import { currentAppErrorChannel, currentComponentErrorHandler } from "../component-errors.js";
|
|
2
4
|
import { serializeJson } from "../server.js";
|
|
3
5
|
function socketUrl(name, options) {
|
|
4
6
|
if (typeof window === "undefined")
|
|
@@ -33,21 +35,71 @@ export function createSocketClient(url, contract, options = {}) {
|
|
|
33
35
|
let retry;
|
|
34
36
|
let attempts = 0;
|
|
35
37
|
let closed = false;
|
|
38
|
+
const reportError = (error) => {
|
|
39
|
+
if (!options.onError) {
|
|
40
|
+
logClientError("Meeko socket client error", error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
observeCallback(() => options.onError(error), (observerError) => logClientError("Meeko socket error observer failed", error, observerError));
|
|
44
|
+
};
|
|
45
|
+
const setState = (next) => {
|
|
46
|
+
try {
|
|
47
|
+
state.value = next;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
reportError(error);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const closePeer = (peer, code, reason) => {
|
|
54
|
+
try {
|
|
55
|
+
peer?.close(code, reason);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
reportError(error);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const scheduleRetry = () => {
|
|
62
|
+
if (closed)
|
|
63
|
+
return;
|
|
64
|
+
const delay = Math.min((options.retryMs ?? 500) * 2 ** attempts++, options.maxRetryMs ?? 30_000);
|
|
65
|
+
retry = setTimeout(() => {
|
|
66
|
+
retry = undefined;
|
|
67
|
+
try {
|
|
68
|
+
connect();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
reportError(error);
|
|
72
|
+
setState("closed");
|
|
73
|
+
scheduleRetry();
|
|
74
|
+
}
|
|
75
|
+
}, delay);
|
|
76
|
+
};
|
|
36
77
|
const connect = () => {
|
|
37
78
|
if (closed)
|
|
38
79
|
return;
|
|
39
|
-
|
|
80
|
+
setState("connecting");
|
|
40
81
|
const peer = factory(url);
|
|
41
82
|
socket = peer;
|
|
83
|
+
let transportErrorReported = false;
|
|
42
84
|
peer.addEventListener("open", () => { if (peer === socket) {
|
|
43
85
|
attempts = 0;
|
|
44
|
-
|
|
86
|
+
setState("open");
|
|
45
87
|
} });
|
|
88
|
+
peer.addEventListener("error", () => {
|
|
89
|
+
if (peer !== socket || closed || transportErrorReported)
|
|
90
|
+
return;
|
|
91
|
+
transportErrorReported = true;
|
|
92
|
+
reportError(new Error("Socket transport error"));
|
|
93
|
+
});
|
|
46
94
|
peer.addEventListener("message", (message) => {
|
|
47
|
-
if (peer !== socket ||
|
|
95
|
+
if (peer !== socket || closed)
|
|
96
|
+
return;
|
|
97
|
+
if (typeof message.data !== "string" || new TextEncoder().encode(message.data).byteLength > maxBytes) {
|
|
98
|
+
reportError(new RangeError("Socket message too large or not text"));
|
|
48
99
|
closed = true;
|
|
49
|
-
|
|
50
|
-
|
|
100
|
+
setState("closed");
|
|
101
|
+
listeners.clear();
|
|
102
|
+
closePeer(peer, 1009, "Message too large");
|
|
51
103
|
return;
|
|
52
104
|
}
|
|
53
105
|
void (async () => {
|
|
@@ -58,20 +110,33 @@ export function createSocketClient(url, contract, options = {}) {
|
|
|
58
110
|
if (!schema)
|
|
59
111
|
throw new TypeError("Unknown socket event");
|
|
60
112
|
const value = await validate(schema, decoded.payload);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
113
|
+
if (peer !== socket || closed)
|
|
114
|
+
return;
|
|
115
|
+
for (const listener of listeners.get(decoded.event) ?? []) {
|
|
116
|
+
if (peer !== socket || closed)
|
|
117
|
+
return;
|
|
118
|
+
observeCallback(() => listener(value), reportError);
|
|
119
|
+
}
|
|
120
|
+
})().catch((error) => {
|
|
121
|
+
if (peer !== socket || closed)
|
|
122
|
+
return;
|
|
123
|
+
reportError(error);
|
|
124
|
+
closed = true;
|
|
125
|
+
setState("closed");
|
|
126
|
+
listeners.clear();
|
|
127
|
+
closePeer(peer, 1008, "Invalid event");
|
|
128
|
+
});
|
|
64
129
|
});
|
|
65
130
|
peer.addEventListener("close", (event) => {
|
|
66
131
|
if (peer !== socket || closed)
|
|
67
132
|
return;
|
|
68
|
-
|
|
133
|
+
setState("closed");
|
|
69
134
|
if ([1008, 1009].includes(event.code)) {
|
|
70
135
|
closed = true;
|
|
136
|
+
listeners.clear();
|
|
71
137
|
return;
|
|
72
138
|
}
|
|
73
|
-
|
|
74
|
-
retry = setTimeout(connect, delay);
|
|
139
|
+
scheduleRetry();
|
|
75
140
|
});
|
|
76
141
|
};
|
|
77
142
|
connect();
|
|
@@ -95,18 +160,23 @@ export function createSocketClient(url, contract, options = {}) {
|
|
|
95
160
|
const set = listeners.get(event) ?? new Set();
|
|
96
161
|
set.add(listener);
|
|
97
162
|
listeners.set(event, set);
|
|
98
|
-
|
|
163
|
+
const unsubscribe = () => { set.delete(listener); if (set.size === 0)
|
|
99
164
|
listeners.delete(event); };
|
|
165
|
+
if (currentEffectOwner())
|
|
166
|
+
onCleanup(unsubscribe);
|
|
167
|
+
return unsubscribe;
|
|
100
168
|
},
|
|
101
169
|
close() {
|
|
102
|
-
if (closed)
|
|
170
|
+
if (closed) {
|
|
171
|
+
listeners.clear();
|
|
103
172
|
return;
|
|
173
|
+
}
|
|
104
174
|
closed = true;
|
|
105
175
|
if (retry)
|
|
106
176
|
clearTimeout(retry);
|
|
107
|
-
socket
|
|
177
|
+
closePeer(socket, 1000, "Client closed");
|
|
108
178
|
listeners.clear();
|
|
109
|
-
|
|
179
|
+
setState("closed");
|
|
110
180
|
},
|
|
111
181
|
};
|
|
112
182
|
}
|
|
@@ -114,7 +184,8 @@ export function createSocketClient(url, contract, options = {}) {
|
|
|
114
184
|
export function useSocket(url, contract, options = {}) {
|
|
115
185
|
if (typeof window === "undefined")
|
|
116
186
|
throw new Error("useSocket() is only available in the browser");
|
|
117
|
-
const
|
|
187
|
+
const onError = options.onError ?? currentComponentErrorHandler();
|
|
188
|
+
const client = createSocketClient(url, contract, onError ? { ...options, onError } : options);
|
|
118
189
|
try {
|
|
119
190
|
onCleanup(() => client.close());
|
|
120
191
|
}
|
|
@@ -129,9 +200,40 @@ export function createSocketRegistry(contracts, options = {}) {
|
|
|
129
200
|
const result = Object.create(null);
|
|
130
201
|
for (const name of Object.keys(contracts)) {
|
|
131
202
|
let client;
|
|
203
|
+
const appClients = new WeakMap();
|
|
204
|
+
const rootClients = new WeakMap();
|
|
132
205
|
Object.defineProperty(result, name, {
|
|
133
206
|
enumerable: true,
|
|
134
207
|
get() {
|
|
208
|
+
const channel = currentAppErrorChannel();
|
|
209
|
+
if (channel) {
|
|
210
|
+
let scoped = appClients.get(channel);
|
|
211
|
+
if (!scoped) {
|
|
212
|
+
const onError = options.onError ?? ((error) => {
|
|
213
|
+
const handler = channel.current();
|
|
214
|
+
if (handler)
|
|
215
|
+
return handler(error);
|
|
216
|
+
logClientError("Meeko socket client error", error);
|
|
217
|
+
});
|
|
218
|
+
scoped = createSocketClient(socketUrl(name, options), contracts[name], { ...options, onError });
|
|
219
|
+
appClients.set(channel, scoped);
|
|
220
|
+
const owned = scoped;
|
|
221
|
+
channel.track(() => { owned.close(); appClients.delete(channel); });
|
|
222
|
+
}
|
|
223
|
+
return scoped;
|
|
224
|
+
}
|
|
225
|
+
const owner = currentEffectOwner();
|
|
226
|
+
if (owner) {
|
|
227
|
+
let scoped = rootClients.get(owner);
|
|
228
|
+
if (!scoped) {
|
|
229
|
+
const onError = options.onError ?? currentComponentErrorHandler();
|
|
230
|
+
scoped = createSocketClient(socketUrl(name, options), contracts[name], onError ? { ...options, onError } : options);
|
|
231
|
+
rootClients.set(owner, scoped);
|
|
232
|
+
const owned = scoped;
|
|
233
|
+
onCleanup(() => { owned.close(); rootClients.delete(owner); });
|
|
234
|
+
}
|
|
235
|
+
return scoped;
|
|
236
|
+
}
|
|
135
237
|
if (!client)
|
|
136
238
|
client = createSocketClient(socketUrl(name, options), contracts[name], options);
|
|
137
239
|
return client;
|