@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
|
@@ -7,8 +7,19 @@ export function defineSocketChannel(definition) {
|
|
|
7
7
|
export class LocalSocketPubSub {
|
|
8
8
|
listeners = new Map();
|
|
9
9
|
async publish(channel, room, message) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const errors = [];
|
|
11
|
+
for (const listener of this.listeners.get(`${channel}\0${room}`) ?? []) {
|
|
12
|
+
try {
|
|
13
|
+
await listener(message);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
errors.push(error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (errors.length === 1)
|
|
20
|
+
throw errors[0];
|
|
21
|
+
if (errors.length > 1)
|
|
22
|
+
throw new AggregateError(errors, "Socket delivery failed for multiple peers");
|
|
12
23
|
}
|
|
13
24
|
subscribe(channel, room, listener) {
|
|
14
25
|
const key = `${channel}\0${room}`;
|
|
@@ -74,10 +85,8 @@ export function createSocketHub(options) {
|
|
|
74
85
|
throw new ApiError("UNKNOWN_SOCKET_EVENT", 404);
|
|
75
86
|
const checked = await validate(schema, payload);
|
|
76
87
|
const fresh = await options.sessions.load(request);
|
|
77
|
-
if (definition.auth === "required" && !fresh.current)
|
|
78
|
-
|
|
79
|
-
throw new ApiError("UNAUTHENTICATED", 401);
|
|
80
|
-
}
|
|
88
|
+
if (definition.auth === "required" && !fresh.current)
|
|
89
|
+
failAndClose(new ApiError("UNAUTHENTICATED", 401), 1008, "Session expired");
|
|
81
90
|
const sendContext = createRequestContext(request, services, fresh);
|
|
82
91
|
if (definition.authorize && !(await definition.authorize({ context: sendContext, ...(roomId === undefined ? {} : { roomId }) })))
|
|
83
92
|
throw new ApiError("FORBIDDEN", 403);
|
|
@@ -86,19 +95,53 @@ export function createSocketHub(options) {
|
|
|
86
95
|
const message = serializeJson({ event, payload: checked });
|
|
87
96
|
const messageBytes = new TextEncoder().encode(message).byteLength;
|
|
88
97
|
if (messageBytes > maxMessageBytes || (peer.bufferedAmount ?? 0) + messageBytes > maxBufferBytes) {
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
failAndClose(new ApiError("SOCKET_LIMIT_EXCEEDED", 413), 1009, "Message too large");
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
await peer.send(message);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
try {
|
|
105
|
+
close(1011, "Send failed");
|
|
106
|
+
}
|
|
107
|
+
catch (closeError) {
|
|
108
|
+
throw new AggregateError([error, closeError], "Socket send and close failed");
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
91
111
|
}
|
|
92
|
-
await peer.send(message);
|
|
93
112
|
};
|
|
94
113
|
const release = pubsub.subscribe(name, roomId ?? "", ({ event, payload }) => emit(event, payload));
|
|
95
114
|
const close = (code, reason) => {
|
|
96
115
|
if (closed)
|
|
97
116
|
return;
|
|
98
117
|
closed = true;
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
const failures = [];
|
|
119
|
+
try {
|
|
120
|
+
release();
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
failures.push(error);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
peer.close(code, reason);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
failures.push(error);
|
|
130
|
+
}
|
|
131
|
+
if (failures.length === 1)
|
|
132
|
+
throw failures[0];
|
|
133
|
+
if (failures.length > 1)
|
|
134
|
+
throw new AggregateError(failures, "Socket unsubscribe and peer close failed");
|
|
101
135
|
};
|
|
136
|
+
function failAndClose(error, code, reason) {
|
|
137
|
+
try {
|
|
138
|
+
close(code, reason);
|
|
139
|
+
}
|
|
140
|
+
catch (cleanupError) {
|
|
141
|
+
error.cause = cleanupError;
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
102
145
|
return {
|
|
103
146
|
emit,
|
|
104
147
|
close,
|
|
@@ -106,35 +149,27 @@ export function createSocketHub(options) {
|
|
|
106
149
|
if (closed)
|
|
107
150
|
throw new ApiError("SOCKET_DISCONNECTED", 409);
|
|
108
151
|
const bytes = typeof raw === "string" ? new TextEncoder().encode(raw) : raw;
|
|
109
|
-
if (bytes.byteLength > maxMessageBytes)
|
|
110
|
-
|
|
111
|
-
throw new ApiError("SOCKET_LIMIT_EXCEEDED", 413);
|
|
112
|
-
}
|
|
152
|
+
if (bytes.byteLength > maxMessageBytes)
|
|
153
|
+
failAndClose(new ApiError("SOCKET_LIMIT_EXCEEDED", 413), 1009, "Message too large");
|
|
113
154
|
let envelope;
|
|
114
155
|
try {
|
|
115
156
|
envelope = JSON.parse(typeof raw === "string" ? raw : new TextDecoder("utf-8", { fatal: true }).decode(raw));
|
|
116
157
|
}
|
|
117
158
|
catch {
|
|
118
|
-
|
|
119
|
-
throw new ApiError("INVALID_SOCKET_MESSAGE", 400);
|
|
159
|
+
failAndClose(new ApiError("INVALID_SOCKET_MESSAGE", 400), 1008, "Invalid message");
|
|
120
160
|
}
|
|
121
161
|
if (!envelope || typeof envelope !== "object" || typeof envelope.event !== "string" || !("payload" in envelope)) {
|
|
122
|
-
|
|
123
|
-
throw new ApiError("INVALID_SOCKET_MESSAGE", 400);
|
|
162
|
+
failAndClose(new ApiError("INVALID_SOCKET_MESSAGE", 400), 1008, "Invalid message");
|
|
124
163
|
}
|
|
125
164
|
const event = envelope.event;
|
|
126
165
|
const schema = definition.clientToServer[event];
|
|
127
|
-
if (!schema)
|
|
128
|
-
|
|
129
|
-
throw new ApiError("UNKNOWN_SOCKET_EVENT", 404);
|
|
130
|
-
}
|
|
166
|
+
if (!schema)
|
|
167
|
+
failAndClose(new ApiError("UNKNOWN_SOCKET_EVENT", 404), 1008, "Unknown event");
|
|
131
168
|
const payload = await validate(schema, envelope.payload);
|
|
132
169
|
// Re-read storage and policy for every event, including after a session revocation.
|
|
133
170
|
const fresh = await options.sessions.load(request);
|
|
134
|
-
if (definition.auth === "required" && !fresh.current)
|
|
135
|
-
|
|
136
|
-
throw new ApiError("UNAUTHENTICATED", 401);
|
|
137
|
-
}
|
|
171
|
+
if (definition.auth === "required" && !fresh.current)
|
|
172
|
+
failAndClose(new ApiError("UNAUTHENTICATED", 401), 1008, "Session expired");
|
|
138
173
|
const eventContext = createRequestContext(request, services, fresh);
|
|
139
174
|
if (definition.authorize && !(await definition.authorize({ context: eventContext, ...(roomId === undefined ? {} : { roomId }) })))
|
|
140
175
|
throw new ApiError("FORBIDDEN", 403);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/framework-server/socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequestContext, type RequestContext } from \"./context.js\";\nimport { ApiError, type Schema } from \"./contracts.js\";\nimport { serializeJson } from \"../server.js\";\nimport type { SessionManager } from \"./sessions.js\";\n\ntype Output<S> = S extends Schema<unknown, infer O> ? O : never;\ntype SchemaMap = Readonly<Record<string, Schema>>;\n\nexport interface SocketPeer {\n send(message: string): void | Promise<void>;\n close(code?: number, reason?: string): void;\n readonly bufferedAmount?: number;\n}\n\nexport interface SocketChannelDefinition<C extends SchemaMap, S extends SchemaMap, TServices = unknown> {\n clientToServer: C;\n serverToClient: S;\n auth?: \"required\" | \"optional\";\n authorize?: (args: { context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n authorizeEvent?: (args: { event: keyof C & string; payload: unknown; context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n authorizeSend?: (args: { event: keyof S & string; payload: unknown; context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n handlers?: { [K in keyof C]?: (args: { payload: Output<C[K]>; context: RequestContext<TServices>; roomId?: string; emit: <E extends keyof S & string>(event: E, payload: Output<S[E]>) => Promise<void> }) => void | Promise<void> };\n}\n\nexport function defineSocketChannel<C extends SchemaMap, S extends SchemaMap, TServices = unknown>(definition: SocketChannelDefinition<C, S, TServices>): SocketChannelDefinition<C, S, TServices> {\n return definition;\n}\n\nexport interface SocketPubSub {\n publish(channel: string, room: string, message: { event: string; payload: unknown }): Promise<void>;\n subscribe(channel: string, room: string, listener: (message: { event: string; payload: unknown }) => void | Promise<void>): () => void;\n}\n\nexport class LocalSocketPubSub implements SocketPubSub {\n private readonly listeners = new Map<string, Set<(message: { event: string; payload: unknown }) => void | Promise<void>>>();\n async publish(channel: string, room: string, message: { event: string; payload: unknown }): Promise<void> {\n for (const listener of this.listeners.get(`${channel}\\0${room}`) ?? []) await listener(message);\n }\n subscribe(channel: string, room: string, listener: (message: { event: string; payload: unknown }) => void | Promise<void>): () => void {\n const key = `${channel}\\0${room}`;\n let listeners = this.listeners.get(key);\n if (!listeners) { listeners = new Set(); this.listeners.set(key, listeners); }\n listeners.add(listener);\n return () => {\n listeners!.delete(listener);\n if (!listeners!.size) this.listeners.delete(key);\n };\n }\n}\n\nexport interface SocketConnection {\n receive(message: string | Uint8Array): Promise<void>;\n emit(event: string, payload: unknown): Promise<void>;\n close(code?: number, reason?: string): void;\n}\n\nexport interface SocketHubOptions<TServices = unknown> {\n channels: Readonly<Record<string, unknown>>;\n sessions: SessionManager;\n services: TServices | ((request: Request) => TServices | Promise<TServices>);\n allowedOrigins?: readonly string[];\n maxMessageBytes?: number;\n maxBufferBytes?: number;\n pubsub?: SocketPubSub;\n}\n\nexport interface SocketHub {\n preflight(name: string, request: Request, roomId?: string): Promise<void>;\n accept(name: string, request: Request, peer: SocketPeer, roomId?: string): Promise<SocketConnection>;\n broadcast(name: string, roomId: string, event: string, payload: unknown): Promise<void>;\n}\n\nasync function validate(schema: Schema, value: unknown): Promise<unknown> {\n const result = await schema[\"~standard\"].validate(value);\n if (\"issues\" in result && result.issues) throw new ApiError(\"INVALID_SOCKET_PAYLOAD\", 400);\n return (result as { value: unknown }).value;\n}\n\nfunction channelDefinition(value: unknown): SocketChannelDefinition<SchemaMap, SchemaMap> {\n if (!value || typeof value !== \"object\" || !(\"clientToServer\" in value) || !(\"serverToClient\" in value)) throw new ApiError(\"UNKNOWN_SOCKET_CHANNEL\", 404);\n return value as SocketChannelDefinition<SchemaMap, SchemaMap>;\n}\n\nfunction originAllowed(request: Request, allowedOrigins: readonly string[]): boolean {\n const origin = request.headers.get(\"origin\");\n return !!origin && (origin === new URL(request.url).origin || allowedOrigins.includes(origin));\n}\n\n/** The host performs the HTTP upgrade, then passes its connected peer here. */\nexport function createSocketHub<TServices>(options: SocketHubOptions<TServices>): SocketHub {\n const maxMessageBytes = options.maxMessageBytes ?? 64 * 1024;\n const maxBufferBytes = options.maxBufferBytes ?? 1024 * 1024;\n if (!Number.isSafeInteger(maxMessageBytes) || maxMessageBytes <= 0 || !Number.isSafeInteger(maxBufferBytes) || maxBufferBytes <= 0) throw new RangeError(\"Invalid socket limits\");\n const pubsub = options.pubsub ?? new LocalSocketPubSub();\n const getDefinition = (name: string) => channelDefinition(options.channels[name]);\n const authorizeHandshake = async (name: string, request: Request, roomId?: string) => {\n const definition = getDefinition(name);\n if (!originAllowed(request, options.allowedOrigins ?? [])) throw new ApiError(\"INVALID_ORIGIN\", 403);\n const session = await options.sessions.load(request);\n if (definition.auth === \"required\" && !session.current) throw new ApiError(\"UNAUTHENTICATED\", 401);\n const services = typeof options.services === \"function\" ? await (options.services as (request: Request) => TServices | Promise<TServices>)(request) : options.services;\n const context = createRequestContext(request, services, session);\n if (definition.authorize && !(await definition.authorize({ context, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n return { definition, services };\n };\n return {\n async preflight(name, request, roomId) { await authorizeHandshake(name, request, roomId); },\n async accept(name: string, request: Request, peer: SocketPeer, roomId?: string): Promise<SocketConnection> {\n const { definition, services } = await authorizeHandshake(name, request, roomId);\n let closed = false;\n const emit = async (event: string, payload: unknown): Promise<void> => {\n if (closed) throw new ApiError(\"SOCKET_DISCONNECTED\", 409);\n const schema = definition.serverToClient[event];\n if (!schema) throw new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404);\n const checked = await validate(schema, payload);\n const fresh = await options.sessions.load(request);\n if (definition.auth === \"required\" && !fresh.current) { close(1008, \"Session expired\"); throw new ApiError(\"UNAUTHENTICATED\", 401); }\n const sendContext = createRequestContext(request, services, fresh);\n if (definition.authorize && !(await definition.authorize({ context: sendContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n if (definition.authorizeSend && !(await definition.authorizeSend({ event, payload: checked, context: sendContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n const message = serializeJson({ event, payload: checked });\n const messageBytes = new TextEncoder().encode(message).byteLength;\n if (messageBytes > maxMessageBytes || (peer.bufferedAmount ?? 0) + messageBytes > maxBufferBytes) {\n close(1009, \"Message too large\");\n throw new ApiError(\"SOCKET_LIMIT_EXCEEDED\", 413);\n }\n await peer.send(message);\n };\n const release = pubsub.subscribe(name, roomId ?? \"\", ({ event, payload }) => emit(event, payload));\n const close = (code?: number, reason?: string): void => {\n if (closed) return;\n closed = true;\n release();\n peer.close(code, reason);\n };\n return {\n emit,\n close,\n async receive(raw) {\n if (closed) throw new ApiError(\"SOCKET_DISCONNECTED\", 409);\n const bytes = typeof raw === \"string\" ? new TextEncoder().encode(raw) : raw;\n if (bytes.byteLength > maxMessageBytes) { close(1009, \"Message too large\"); throw new ApiError(\"SOCKET_LIMIT_EXCEEDED\", 413); }\n let envelope: unknown;\n try { envelope = JSON.parse(typeof raw === \"string\" ? raw : new TextDecoder(\"utf-8\", { fatal: true }).decode(raw)) as unknown; }\n catch { close(1008, \"Invalid message\"); throw new ApiError(\"INVALID_SOCKET_MESSAGE\", 400); }\n if (!envelope || typeof envelope !== \"object\" || typeof (envelope as { event?: unknown }).event !== \"string\" || !(\"payload\" in envelope)) {\n close(1008, \"Invalid message\");\n throw new ApiError(\"INVALID_SOCKET_MESSAGE\", 400);\n }\n const event = (envelope as { event?: unknown }).event as string;\n const schema = definition.clientToServer[event];\n if (!schema) { close(1008, \"Unknown event\"); throw new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404); }\n const payload = await validate(schema, (envelope as { payload: unknown }).payload);\n // Re-read storage and policy for every event, including after a session revocation.\n const fresh = await options.sessions.load(request);\n if (definition.auth === \"required\" && !fresh.current) { close(1008, \"Session expired\"); throw new ApiError(\"UNAUTHENTICATED\", 401); }\n const eventContext = createRequestContext(request, services, fresh);\n if (definition.authorize && !(await definition.authorize({ context: eventContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n if (definition.authorizeEvent && !(await definition.authorizeEvent({ event, payload, context: eventContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n const handler = definition.handlers?.[event];\n if (!handler) throw new ApiError(\"UNHANDLED_SOCKET_EVENT\", 404);\n await handler({ payload, context: eventContext, ...(roomId === undefined ? {} : { roomId }), emit });\n },\n };\n },\n async broadcast(name: string, roomId: string, event: string, payload: unknown): Promise<void> {\n const definition = getDefinition(name);\n const schema = definition.serverToClient[event];\n if (!schema) throw new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404);\n const checked = await validate(schema, payload);\n serializeJson({ event, payload: checked });\n await pubsub.publish(name, roomId, { event, payload: checked });\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/framework-server/socket.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRequestContext, type RequestContext } from \"./context.js\";\nimport { ApiError, type Schema } from \"./contracts.js\";\nimport { serializeJson } from \"../server.js\";\nimport type { SessionManager } from \"./sessions.js\";\n\ntype Output<S> = S extends Schema<unknown, infer O> ? O : never;\ntype SchemaMap = Readonly<Record<string, Schema>>;\n\nexport interface SocketPeer {\n send(message: string): void | Promise<void>;\n close(code?: number, reason?: string): void;\n readonly bufferedAmount?: number;\n}\n\nexport interface SocketChannelDefinition<C extends SchemaMap, S extends SchemaMap, TServices = unknown> {\n clientToServer: C;\n serverToClient: S;\n auth?: \"required\" | \"optional\";\n authorize?: (args: { context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n authorizeEvent?: (args: { event: keyof C & string; payload: unknown; context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n authorizeSend?: (args: { event: keyof S & string; payload: unknown; context: RequestContext<TServices>; roomId?: string }) => boolean | Promise<boolean>;\n handlers?: { [K in keyof C]?: (args: { payload: Output<C[K]>; context: RequestContext<TServices>; roomId?: string; emit: <E extends keyof S & string>(event: E, payload: Output<S[E]>) => Promise<void> }) => void | Promise<void> };\n}\n\nexport function defineSocketChannel<C extends SchemaMap, S extends SchemaMap, TServices = unknown>(definition: SocketChannelDefinition<C, S, TServices>): SocketChannelDefinition<C, S, TServices> {\n return definition;\n}\n\nexport interface SocketPubSub {\n publish(channel: string, room: string, message: { event: string; payload: unknown }): Promise<void>;\n subscribe(channel: string, room: string, listener: (message: { event: string; payload: unknown }) => void | Promise<void>): () => void;\n}\n\nexport class LocalSocketPubSub implements SocketPubSub {\n private readonly listeners = new Map<string, Set<(message: { event: string; payload: unknown }) => void | Promise<void>>>();\n async publish(channel: string, room: string, message: { event: string; payload: unknown }): Promise<void> {\n const errors: unknown[] = [];\n for (const listener of this.listeners.get(`${channel}\\0${room}`) ?? []) {\n try { await listener(message); }\n catch (error) { errors.push(error); }\n }\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, \"Socket delivery failed for multiple peers\");\n }\n subscribe(channel: string, room: string, listener: (message: { event: string; payload: unknown }) => void | Promise<void>): () => void {\n const key = `${channel}\\0${room}`;\n let listeners = this.listeners.get(key);\n if (!listeners) { listeners = new Set(); this.listeners.set(key, listeners); }\n listeners.add(listener);\n return () => {\n listeners!.delete(listener);\n if (!listeners!.size) this.listeners.delete(key);\n };\n }\n}\n\nexport interface SocketConnection {\n receive(message: string | Uint8Array): Promise<void>;\n emit(event: string, payload: unknown): Promise<void>;\n close(code?: number, reason?: string): void;\n}\n\nexport interface SocketHubOptions<TServices = unknown> {\n channels: Readonly<Record<string, unknown>>;\n sessions: SessionManager;\n services: TServices | ((request: Request) => TServices | Promise<TServices>);\n allowedOrigins?: readonly string[];\n maxMessageBytes?: number;\n maxBufferBytes?: number;\n pubsub?: SocketPubSub;\n}\n\nexport interface SocketHub {\n preflight(name: string, request: Request, roomId?: string): Promise<void>;\n accept(name: string, request: Request, peer: SocketPeer, roomId?: string): Promise<SocketConnection>;\n broadcast(name: string, roomId: string, event: string, payload: unknown): Promise<void>;\n}\n\nasync function validate(schema: Schema, value: unknown): Promise<unknown> {\n const result = await schema[\"~standard\"].validate(value);\n if (\"issues\" in result && result.issues) throw new ApiError(\"INVALID_SOCKET_PAYLOAD\", 400);\n return (result as { value: unknown }).value;\n}\n\nfunction channelDefinition(value: unknown): SocketChannelDefinition<SchemaMap, SchemaMap> {\n if (!value || typeof value !== \"object\" || !(\"clientToServer\" in value) || !(\"serverToClient\" in value)) throw new ApiError(\"UNKNOWN_SOCKET_CHANNEL\", 404);\n return value as SocketChannelDefinition<SchemaMap, SchemaMap>;\n}\n\nfunction originAllowed(request: Request, allowedOrigins: readonly string[]): boolean {\n const origin = request.headers.get(\"origin\");\n return !!origin && (origin === new URL(request.url).origin || allowedOrigins.includes(origin));\n}\n\n/** The host performs the HTTP upgrade, then passes its connected peer here. */\nexport function createSocketHub<TServices>(options: SocketHubOptions<TServices>): SocketHub {\n const maxMessageBytes = options.maxMessageBytes ?? 64 * 1024;\n const maxBufferBytes = options.maxBufferBytes ?? 1024 * 1024;\n if (!Number.isSafeInteger(maxMessageBytes) || maxMessageBytes <= 0 || !Number.isSafeInteger(maxBufferBytes) || maxBufferBytes <= 0) throw new RangeError(\"Invalid socket limits\");\n const pubsub = options.pubsub ?? new LocalSocketPubSub();\n const getDefinition = (name: string) => channelDefinition(options.channels[name]);\n const authorizeHandshake = async (name: string, request: Request, roomId?: string) => {\n const definition = getDefinition(name);\n if (!originAllowed(request, options.allowedOrigins ?? [])) throw new ApiError(\"INVALID_ORIGIN\", 403);\n const session = await options.sessions.load(request);\n if (definition.auth === \"required\" && !session.current) throw new ApiError(\"UNAUTHENTICATED\", 401);\n const services = typeof options.services === \"function\" ? await (options.services as (request: Request) => TServices | Promise<TServices>)(request) : options.services;\n const context = createRequestContext(request, services, session);\n if (definition.authorize && !(await definition.authorize({ context, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n return { definition, services };\n };\n return {\n async preflight(name, request, roomId) { await authorizeHandshake(name, request, roomId); },\n async accept(name: string, request: Request, peer: SocketPeer, roomId?: string): Promise<SocketConnection> {\n const { definition, services } = await authorizeHandshake(name, request, roomId);\n let closed = false;\n const emit = async (event: string, payload: unknown): Promise<void> => {\n if (closed) throw new ApiError(\"SOCKET_DISCONNECTED\", 409);\n const schema = definition.serverToClient[event];\n if (!schema) throw new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404);\n const checked = await validate(schema, payload);\n const fresh = await options.sessions.load(request);\n if (definition.auth === \"required\" && !fresh.current) failAndClose(new ApiError(\"UNAUTHENTICATED\", 401), 1008, \"Session expired\");\n const sendContext = createRequestContext(request, services, fresh);\n if (definition.authorize && !(await definition.authorize({ context: sendContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n if (definition.authorizeSend && !(await definition.authorizeSend({ event, payload: checked, context: sendContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n const message = serializeJson({ event, payload: checked });\n const messageBytes = new TextEncoder().encode(message).byteLength;\n if (messageBytes > maxMessageBytes || (peer.bufferedAmount ?? 0) + messageBytes > maxBufferBytes) {\n failAndClose(new ApiError(\"SOCKET_LIMIT_EXCEEDED\", 413), 1009, \"Message too large\");\n }\n try { await peer.send(message); }\n catch (error) {\n try { close(1011, \"Send failed\"); }\n catch (closeError) { throw new AggregateError([error, closeError], \"Socket send and close failed\"); }\n throw error;\n }\n };\n const release = pubsub.subscribe(name, roomId ?? \"\", ({ event, payload }) => emit(event, payload));\n const close = (code?: number, reason?: string): void => {\n if (closed) return;\n closed = true;\n const failures: unknown[] = [];\n try { release(); }\n catch (error) { failures.push(error); }\n try { peer.close(code, reason); }\n catch (error) { failures.push(error); }\n if (failures.length === 1) throw failures[0];\n if (failures.length > 1) throw new AggregateError(failures, \"Socket unsubscribe and peer close failed\");\n };\n function failAndClose(error: ApiError, code: number, reason: string): never {\n try { close(code, reason); }\n catch (cleanupError) { error.cause = cleanupError; }\n throw error;\n }\n return {\n emit,\n close,\n async receive(raw) {\n if (closed) throw new ApiError(\"SOCKET_DISCONNECTED\", 409);\n const bytes = typeof raw === \"string\" ? new TextEncoder().encode(raw) : raw;\n if (bytes.byteLength > maxMessageBytes) failAndClose(new ApiError(\"SOCKET_LIMIT_EXCEEDED\", 413), 1009, \"Message too large\");\n let envelope: unknown;\n try { envelope = JSON.parse(typeof raw === \"string\" ? raw : new TextDecoder(\"utf-8\", { fatal: true }).decode(raw)) as unknown; }\n catch { failAndClose(new ApiError(\"INVALID_SOCKET_MESSAGE\", 400), 1008, \"Invalid message\"); }\n if (!envelope || typeof envelope !== \"object\" || typeof (envelope as { event?: unknown }).event !== \"string\" || !(\"payload\" in envelope)) {\n failAndClose(new ApiError(\"INVALID_SOCKET_MESSAGE\", 400), 1008, \"Invalid message\");\n }\n const event = (envelope as { event?: unknown }).event as string;\n const schema = definition.clientToServer[event];\n if (!schema) failAndClose(new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404), 1008, \"Unknown event\");\n const payload = await validate(schema, (envelope as { payload: unknown }).payload);\n // Re-read storage and policy for every event, including after a session revocation.\n const fresh = await options.sessions.load(request);\n if (definition.auth === \"required\" && !fresh.current) failAndClose(new ApiError(\"UNAUTHENTICATED\", 401), 1008, \"Session expired\");\n const eventContext = createRequestContext(request, services, fresh);\n if (definition.authorize && !(await definition.authorize({ context: eventContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n if (definition.authorizeEvent && !(await definition.authorizeEvent({ event, payload, context: eventContext, ...(roomId === undefined ? {} : { roomId }) }))) throw new ApiError(\"FORBIDDEN\", 403);\n const handler = definition.handlers?.[event];\n if (!handler) throw new ApiError(\"UNHANDLED_SOCKET_EVENT\", 404);\n await handler({ payload, context: eventContext, ...(roomId === undefined ? {} : { roomId }), emit });\n },\n };\n },\n async broadcast(name: string, roomId: string, event: string, payload: unknown): Promise<void> {\n const definition = getDefinition(name);\n const schema = definition.serverToClient[event];\n if (!schema) throw new ApiError(\"UNKNOWN_SOCKET_EVENT\", 404);\n const checked = await validate(schema, payload);\n serializeJson({ event, payload: checked });\n await pubsub.publish(name, roomId, { event, payload: checked });\n },\n };\n}\n"]}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createRequestContext, useRequestState } from "./framework-server/context.js";
|
|
2
|
+
export { defineServerMiddleware, composeServerMiddleware } from "./framework-server/middleware.js";
|
|
3
|
+
export type { ServerMiddleware } from "./framework-server/middleware.js";
|
|
2
4
|
export type { RequestContext, ServerFetch } from "./framework-server/context.js";
|
|
3
5
|
export { createServerClientOptions } from "./framework-server/auto-clients.js";
|
|
4
6
|
export { MemorySessionStore, SessionHandle, SessionManager, SessionSecurityError } from "./framework-server/sessions.js";
|
package/dist/framework-server.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createRequestContext, useRequestState } from "./framework-server/context.js";
|
|
2
|
+
export { defineServerMiddleware, composeServerMiddleware } from "./framework-server/middleware.js";
|
|
2
3
|
export { createServerClientOptions } from "./framework-server/auto-clients.js";
|
|
3
4
|
export { MemorySessionStore, SessionHandle, SessionManager, SessionSecurityError } from "./framework-server/sessions.js";
|
|
4
5
|
export { RedisSessionStore, SqlSessionStore } from "./framework-server/session-stores.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework-server.js","sourceRoot":"","sources":["../src/framework-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"framework-server.js","sourceRoot":"","sources":["../src/framework-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAGnG,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAEzH,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAE1F,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAEjK,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE7I,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEvG,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AAEpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { createRequestContext, useRequestState } from \"./framework-server/context.js\";\nexport { defineServerMiddleware, composeServerMiddleware } from \"./framework-server/middleware.js\";\nexport type { ServerMiddleware } from \"./framework-server/middleware.js\";\nexport type { RequestContext, ServerFetch } from \"./framework-server/context.js\";\nexport { createServerClientOptions } from \"./framework-server/auto-clients.js\";\nexport { MemorySessionStore, SessionHandle, SessionManager, SessionSecurityError } from \"./framework-server/sessions.js\";\nexport type { SessionOptions, SessionRecord, SessionStore } from \"./framework-server/sessions.js\";\nexport { RedisSessionStore, SqlSessionStore } from \"./framework-server/session-stores.js\";\nexport type { RedisSessionClient, RedisSessionStoreOptions, SqlSessionDriver, SqlSessionRow, SqlSessionStoreOptions } from \"./framework-server/session-stores.js\";\nexport { ApiError, defineApiRoute, defineRpc, dispatchApiRoute, dispatchRpc, errorResponse, executeApiRoute, executeRpc } from \"./framework-server/contracts.js\";\nexport type { ApiRouteDefinition, RpcDefinition, Schema } from \"./framework-server/contracts.js\";\nexport { createLocalServerFetch, createServerDispatcher, discoverServerManifest, loadSocketChannels } from \"./framework-server/discovery.js\";\nexport type { ApiEndpoint, ApiMethod, RpcEndpoint, SocketEndpoint, ServerDispatcherOptions, ServerManifest } from \"./framework-server/discovery.js\";\nexport { createSocketHub, defineSocketChannel, LocalSocketPubSub } from \"./framework-server/socket.js\";\nexport type { SocketChannelDefinition, SocketConnection, SocketHubOptions, SocketPeer, SocketPubSub } from \"./framework-server/socket.js\";\nexport { createNodeSocketUpgradeHandler } from \"./framework-server/node-upgrade.js\";\nexport type { NodeAcceptedSocket, NodeSocketUpgradeOptions } from \"./framework-server/node-upgrade.js\";\nexport { attachNodeWebSocketServer } from \"./framework-server/node-websocket.js\";\nexport type { NodeWebSocketOptions, NodeWebSocketServer } from \"./framework-server/node-websocket.js\";\nexport { generateOpenApi } from \"./framework-server/openapi.js\";\nexport type { OpenApiOptions } from \"./framework-server/openapi.js\";\nexport { createLocalCalls } from \"./framework-server/local.js\";\nexport type { LocalApiInput, LocalCallOptions } from \"./framework-server/local.js\";\n"]}
|
package/dist/framework.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type { ClientApp, ClientAppOptions, ClientComponent, ClientRoute } from "
|
|
|
8
8
|
export { definePage, PageInputError } from "./framework/page.js";
|
|
9
9
|
export type { PageDefinition, PageLoadContext, PageSchema } from "./framework/page.js";
|
|
10
10
|
export { StateStore, useState } from "./framework/state.js";
|
|
11
|
+
export { defineRouteMiddleware } from "./framework/middleware.js";
|
|
12
|
+
export type { RouteMiddleware, RouteMiddlewareContext } from "./framework/middleware.js";
|
|
11
13
|
export { ClientError, createApiClient, createBrowserClientOptions, createPublicServerManifest, createRpcClient } from "./framework/clients.js";
|
|
12
14
|
export type { ApiClient, ApiOperation, ClientOptions, PublicApiEndpoint, PublicRpcEndpoint, PublicServerManifest, RpcClient, RpcOperation } from "./framework/clients.js";
|
|
13
15
|
export { createSocketClient, createSocketRegistry, useSocket, useSocketChannel } from "./framework/socket.js";
|
|
@@ -18,7 +20,7 @@ export interface GeneratedRoutes {
|
|
|
18
20
|
type RouteKey = keyof GeneratedRoutes extends never ? string : keyof GeneratedRoutes & string;
|
|
19
21
|
type RouteParams<K extends string> = K extends keyof GeneratedRoutes ? GeneratedRoutes[K] : Record<string, string>;
|
|
20
22
|
type HrefOptions<K extends string> = (keyof RouteParams<K> extends never ? {
|
|
21
|
-
params?:
|
|
23
|
+
params?: never;
|
|
22
24
|
} : {
|
|
23
25
|
params: RouteParams<K>;
|
|
24
26
|
}) & {
|
package/dist/framework.js
CHANGED
|
@@ -4,6 +4,7 @@ export { defineMeekoConfig } from "./framework/config-type.js";
|
|
|
4
4
|
export { mountClientApp } from "./framework/client.js";
|
|
5
5
|
export { definePage, PageInputError } from "./framework/page.js";
|
|
6
6
|
export { StateStore, useState } from "./framework/state.js";
|
|
7
|
+
export { defineRouteMiddleware } from "./framework/middleware.js";
|
|
7
8
|
export { ClientError, createApiClient, createBrowserClientOptions, createPublicServerManifest, createRpcClient } from "./framework/clients.js";
|
|
8
9
|
export { createSocketClient, createSocketRegistry, useSocket, useSocketChannel } from "./framework/socket.js";
|
|
9
10
|
/** Builds a checked URL when the project's route declarations are installed. */
|
package/dist/framework.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework.js","sourceRoot":"","sources":["../src/framework.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE/I,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAiB9G,gFAAgF;AAChF,MAAM,UAAU,IAAI,CAAqB,MAA8B,EAAE,EAAK,EAAE,GAAG,CAAC,OAAO,CAAmB;IAC5G,OAAO,WAAW,CAAC,MAAM,EAAE,EAAE,EAAE,OAAwD,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import { href as runtimeHref } from \"./framework/router.js\";\nimport type { RouteRecord } from \"./framework/router.js\";\n\nexport { createNavigator, createRouteManifest, matchRoute } from \"./framework/router.js\";\nexport { defineMeekoConfig } from \"./framework/config-type.js\";\nexport type { MeekoConfig } from \"./framework/config-type.js\";\nexport type { NavigationContext, Navigator, RouteMatch, RouteRecord } from \"./framework/router.js\";\nexport { mountClientApp } from \"./framework/client.js\";\nexport type { ClientApp, ClientAppOptions, ClientComponent, ClientRoute } from \"./framework/client.js\";\nexport { definePage, PageInputError } from \"./framework/page.js\";\nexport type { PageDefinition, PageLoadContext, PageSchema } from \"./framework/page.js\";\nexport { StateStore, useState } from \"./framework/state.js\";\nexport { ClientError, createApiClient, createBrowserClientOptions, createPublicServerManifest, createRpcClient } from \"./framework/clients.js\";\nexport type { ApiClient, ApiOperation, ClientOptions, PublicApiEndpoint, PublicRpcEndpoint, PublicServerManifest, RpcClient, RpcOperation } from \"./framework/clients.js\";\nexport { createSocketClient, createSocketRegistry, useSocket, useSocketChannel } from \"./framework/socket.js\";\nexport type { SocketClient, SocketContract, SocketSchema, SocketClientOptions, SocketRegistry, SocketRegistryOptions } from \"./framework/socket.js\";\n\n/** Augmented by generated declarations in .meeko/types/routes.d.ts. */\nexport interface GeneratedRoutes {}\n\ntype RouteKey = keyof GeneratedRoutes extends never ? string : keyof GeneratedRoutes & string;\ntype RouteParams<K extends string> = K extends keyof GeneratedRoutes ? GeneratedRoutes[K] : Record<string, string>;\ntype HrefOptions<K extends string> = (keyof RouteParams<K> extends never\n ? { params?:
|
|
1
|
+
{"version":3,"file":"framework.js","sourceRoot":"","sources":["../src/framework.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE/I,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAiB9G,gFAAgF;AAChF,MAAM,UAAU,IAAI,CAAqB,MAA8B,EAAE,EAAK,EAAE,GAAG,CAAC,OAAO,CAAmB;IAC5G,OAAO,WAAW,CAAC,MAAM,EAAE,EAAE,EAAE,OAAwD,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import { href as runtimeHref } from \"./framework/router.js\";\nimport type { RouteRecord } from \"./framework/router.js\";\n\nexport { createNavigator, createRouteManifest, matchRoute } from \"./framework/router.js\";\nexport { defineMeekoConfig } from \"./framework/config-type.js\";\nexport type { MeekoConfig } from \"./framework/config-type.js\";\nexport type { NavigationContext, Navigator, RouteMatch, RouteRecord } from \"./framework/router.js\";\nexport { mountClientApp } from \"./framework/client.js\";\nexport type { ClientApp, ClientAppOptions, ClientComponent, ClientRoute } from \"./framework/client.js\";\nexport { definePage, PageInputError } from \"./framework/page.js\";\nexport type { PageDefinition, PageLoadContext, PageSchema } from \"./framework/page.js\";\nexport { StateStore, useState } from \"./framework/state.js\";\nexport { defineRouteMiddleware } from \"./framework/middleware.js\";\nexport type { RouteMiddleware, RouteMiddlewareContext } from \"./framework/middleware.js\";\nexport { ClientError, createApiClient, createBrowserClientOptions, createPublicServerManifest, createRpcClient } from \"./framework/clients.js\";\nexport type { ApiClient, ApiOperation, ClientOptions, PublicApiEndpoint, PublicRpcEndpoint, PublicServerManifest, RpcClient, RpcOperation } from \"./framework/clients.js\";\nexport { createSocketClient, createSocketRegistry, useSocket, useSocketChannel } from \"./framework/socket.js\";\nexport type { SocketClient, SocketContract, SocketSchema, SocketClientOptions, SocketRegistry, SocketRegistryOptions } from \"./framework/socket.js\";\n\n/** Augmented by generated declarations in .meeko/types/routes.d.ts. */\nexport interface GeneratedRoutes {}\n\ntype RouteKey = keyof GeneratedRoutes extends never ? string : keyof GeneratedRoutes & string;\ntype RouteParams<K extends string> = K extends keyof GeneratedRoutes ? GeneratedRoutes[K] : Record<string, string>;\ntype HrefOptions<K extends string> = (keyof RouteParams<K> extends never\n ? { params?: never }\n : { params: RouteParams<K> }) & {\n search?: Readonly<Record<string, string | number | boolean | null | undefined>>;\n};\ntype HrefArguments<K extends string> = K extends keyof GeneratedRoutes\n ? keyof RouteParams<K> extends never ? [options?: HrefOptions<K>] : [options: HrefOptions<K>]\n : [options?: HrefOptions<K>];\n\n/** Builds a checked URL when the project's route declarations are installed. */\nexport function href<K extends RouteKey>(routes: readonly RouteRecord[], id: K, ...[options]: HrefArguments<K>): string {\n return runtimeHref(routes, id, options as Parameters<typeof runtimeHref>[2] | undefined);\n}\n"]}
|
package/dist/hmr.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isWritableSignal } from "./reactivity.js";
|
|
1
|
+
import { captureParentOwner, isWritableSignal } from "./reactivity.js";
|
|
2
2
|
const enabled = new Set();
|
|
3
3
|
const instances = new Map();
|
|
4
4
|
/** Copia solo las señales editables de un setup, sin serializarlas. */
|
|
@@ -27,7 +27,7 @@ export function enableHotModule(id) {
|
|
|
27
27
|
function registerHotTarget(id, key, target, props, component, snapshot, dispose) {
|
|
28
28
|
if (!enabled.has(id))
|
|
29
29
|
return dispose;
|
|
30
|
-
const entry = { target, props, component: component, snapshot, dispose };
|
|
30
|
+
const entry = { target, props, component: component, snapshot, dispose, withParent: captureParentOwner() };
|
|
31
31
|
let group = instances.get(id);
|
|
32
32
|
if (!group) {
|
|
33
33
|
group = new Map();
|
|
@@ -40,12 +40,17 @@ function registerHotTarget(id, key, target, props, component, snapshot, dispose)
|
|
|
40
40
|
}
|
|
41
41
|
const slot = {
|
|
42
42
|
current: entry,
|
|
43
|
-
|
|
43
|
+
forget: () => {
|
|
44
44
|
if (group.get(key) !== slot)
|
|
45
45
|
return;
|
|
46
46
|
group.delete(key);
|
|
47
47
|
if (group.size === 0)
|
|
48
48
|
instances.delete(id);
|
|
49
|
+
},
|
|
50
|
+
stop: () => {
|
|
51
|
+
if (group.get(key) !== slot)
|
|
52
|
+
return;
|
|
53
|
+
slot.forget();
|
|
49
54
|
slot.current.dispose();
|
|
50
55
|
},
|
|
51
56
|
};
|
|
@@ -80,7 +85,9 @@ function replaceScope(scope, html) {
|
|
|
80
85
|
export function replaceHotStyle(id, css) {
|
|
81
86
|
if (typeof document === "undefined")
|
|
82
87
|
return;
|
|
83
|
-
|
|
88
|
+
// Module IDs are file paths and may contain CSS selector metacharacters.
|
|
89
|
+
let style = [...document.querySelectorAll("style[data-meeko-hot]")]
|
|
90
|
+
.find((candidate) => candidate.dataset.meekoHot === id);
|
|
84
91
|
if (!style) {
|
|
85
92
|
style = document.createElement("style");
|
|
86
93
|
style.dataset.meekoHot = id;
|
|
@@ -99,26 +106,20 @@ export function replaceHotModule(id, next) {
|
|
|
99
106
|
const entry = slot.current;
|
|
100
107
|
const state = entry.snapshot();
|
|
101
108
|
const previous = entry.target.kind === "root" ? entry.target.root.innerHTML : scopeRange(entry.target.scope).cloneContents();
|
|
102
|
-
return { slot, entry, state, html: component.renderToString(entry.props, state), previous };
|
|
109
|
+
return { slot, entry, state, html: entry.withParent(() => component.renderToString(entry.props, state)), previous };
|
|
103
110
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
else {
|
|
114
|
-
if (!component.hydrateScope)
|
|
115
|
-
throw new Error("Updated Meeko child component has no hydrateScope export");
|
|
116
|
-
component.hydrateScope(entry.target.scope, entry.props, state);
|
|
111
|
+
const committed = [];
|
|
112
|
+
const rollback = ({ slot, entry, state, previous }, failures) => {
|
|
113
|
+
const partial = slot.current !== entry ? slot.current : undefined;
|
|
114
|
+
if (partial) {
|
|
115
|
+
try {
|
|
116
|
+
partial.dispose();
|
|
117
|
+
}
|
|
118
|
+
catch (cleanupError) {
|
|
119
|
+
failures.push(cleanupError);
|
|
117
120
|
}
|
|
118
|
-
if (slot.current === entry)
|
|
119
|
-
throw new Error("Updated Meeko component did not register its hydrated instance");
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
try {
|
|
122
123
|
if (entry.target.kind === "root")
|
|
123
124
|
entry.target.root.innerHTML = previous;
|
|
124
125
|
else {
|
|
@@ -126,11 +127,60 @@ export function replaceHotModule(id, next) {
|
|
|
126
127
|
range.deleteContents();
|
|
127
128
|
range.insertNode(previous);
|
|
128
129
|
}
|
|
130
|
+
if (entry.target.kind === "root") {
|
|
131
|
+
const { root } = entry.target;
|
|
132
|
+
entry.withParent(() => entry.component.hydrate(root, entry.props, state));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const { scope } = entry.target;
|
|
136
|
+
entry.withParent(() => entry.component.hydrateScope?.(scope, entry.props, state));
|
|
137
|
+
}
|
|
138
|
+
if (slot.current === entry || slot.current === partial)
|
|
139
|
+
throw new Error("Restored Meeko component did not register its hydrated instance");
|
|
140
|
+
}
|
|
141
|
+
catch (rollbackError) {
|
|
142
|
+
failures.push(rollbackError);
|
|
143
|
+
if (slot.current !== entry && slot.current !== partial) {
|
|
144
|
+
try {
|
|
145
|
+
slot.current.dispose();
|
|
146
|
+
}
|
|
147
|
+
catch (cleanupError) {
|
|
148
|
+
failures.push(cleanupError);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
slot.forget();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
for (const item of prepared) {
|
|
155
|
+
const { slot, entry, state, html } = item;
|
|
156
|
+
try {
|
|
157
|
+
entry.dispose();
|
|
129
158
|
if (entry.target.kind === "root")
|
|
130
|
-
entry.
|
|
159
|
+
entry.target.root.innerHTML = html;
|
|
131
160
|
else
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
replaceScope(entry.target.scope, html);
|
|
162
|
+
if (entry.target.kind === "root") {
|
|
163
|
+
const { root } = entry.target;
|
|
164
|
+
entry.withParent(() => component.hydrate(root, entry.props, state));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (!component.hydrateScope)
|
|
168
|
+
throw new Error("Updated Meeko child component has no hydrateScope export");
|
|
169
|
+
const { scope } = entry.target;
|
|
170
|
+
entry.withParent(() => component.hydrateScope(scope, entry.props, state));
|
|
171
|
+
}
|
|
172
|
+
if (slot.current === entry)
|
|
173
|
+
throw new Error("Updated Meeko component did not register its hydrated instance");
|
|
174
|
+
committed.push(item);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const failures = [error];
|
|
178
|
+
rollback(item, failures);
|
|
179
|
+
for (const previous of committed.reverse())
|
|
180
|
+
rollback(previous, failures);
|
|
181
|
+
if (failures.length === 1)
|
|
182
|
+
throw error;
|
|
183
|
+
throw new AggregateError(failures, "Meeko hot replacement and recovery failed");
|
|
134
184
|
}
|
|
135
185
|
}
|
|
136
186
|
replaceHotStyle(id, component.css ?? "");
|
package/dist/hmr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import { isWritableSignal } from \"./reactivity.js\";\nimport type { DomScope } from \"./dom.js\";\n\n/** Valores de señales transferidos en memoria entre versiones de un componente. */\nexport type HotState = Record<string, unknown>;\n\n/** Contrato mínimo de un módulo Meeko reemplazable en desarrollo. */\nexport interface HotComponent<Props = unknown> {\n renderToString(props: Props, state?: HotState): string;\n hydrate(root: Element, props: Props, state?: HotState): () => void;\n hydrateScope?(scope: DomScope, props: Props, state?: HotState): () => void;\n css?: string;\n}\n\ninterface HotInstance {\n target: { kind: \"root\"; root: Element } | { kind: \"scope\"; scope: DomScope };\n props: unknown;\n component: HotComponent;\n snapshot: () => HotState;\n dispose: () => void;\n}\n\ninterface HotSlot {\n current: HotInstance;\n stop: () => void;\n}\n\nconst enabled = new Set<string>();\nconst instances = new Map<string, Map<Element | Comment, HotSlot>>();\n\n/** Copia solo las señales editables de un setup, sin serializarlas. */\nexport function snapshotSignals(bindings: Record<string, unknown>): HotState {\n const state: HotState = {};\n for (const [name, value] of Object.entries(bindings)) {\n if (isWritableSignal(value)) state[name] = value.value;\n }\n return state;\n}\n\n/** Restaura señales compatibles antes de renderizar o hidratar la nueva versión. */\nexport function restoreSignals(bindings: Record<string, unknown>, state?: HotState): void {\n if (!state) return;\n for (const [name, value] of Object.entries(state)) {\n const target = bindings[name];\n if (isWritableSignal(target)) target.value = value;\n }\n}\n\n/** Activa el seguimiento de instancias solo para módulos servidos por Vite. */\nexport function enableHotModule(id: string): void {\n enabled.add(id);\n}\n\nfunction registerHotTarget<Props>(id: string, key: Element | Comment, target: HotInstance[\"target\"], props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n if (!enabled.has(id)) return dispose;\n const entry: HotInstance = { target, props, component: component as HotComponent, snapshot, dispose };\n let group = instances.get(id);\n if (!group) {\n group = new Map();\n instances.set(id, group);\n }\n const existing = group.get(key);\n if (existing) {\n existing.current = entry;\n return existing.stop;\n }\n const slot: HotSlot = {\n current: entry,\n stop: () => {\n if (group.get(key) !== slot) return;\n group.delete(key);\n if (group.size === 0) instances.delete(id);\n slot.current.dispose();\n },\n };\n group.set(key, slot);\n return slot.stop;\n}\n\n/** Registra una raíz hidratada y elimina el registro al liberarla. */\nexport function registerHotInstance<Props>(id: string, root: Element, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n return registerHotTarget(id, root, { kind: \"root\", root }, props, component, snapshot, dispose);\n}\n\n/** Registra un componente hijo delimitado por comentarios para actualizarlo sin tocar al padre. */\nexport function registerHotScope<Props>(id: string, scope: DomScope, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n if (!scope.start || !scope.end) throw new Error(\"Hot child component requires a bounded scope\");\n return registerHotTarget(id, scope.start, { kind: \"scope\", scope }, props, component, snapshot, dispose);\n}\n\nfunction scopeRange(scope: DomScope): Range {\n if (!scope.start || !scope.end) throw new Error(\"Hot child component lost its markers\");\n const range = scope.start.ownerDocument.createRange();\n range.setStartAfter(scope.start);\n range.setEndBefore(scope.end);\n return range;\n}\n\nfunction replaceScope(scope: DomScope, html: string): void {\n const range = scopeRange(scope);\n const content = range.createContextualFragment(html);\n range.deleteContents();\n range.insertNode(content);\n}\n\n/** Aplica CSS nuevo después de la hoja virtual cargada por Vite. */\nexport function replaceHotStyle(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n let style = document.querySelector<HTMLStyleElement>(`style[data-meeko-hot=\"${id}\"]`);\n if (!style) {\n style = document.createElement(\"style\");\n style.dataset.meekoHot = id;\n document.head.appendChild(style);\n }\n style.textContent = css;\n}\n\n/** Sustituye las instancias activas sin recargar la página y conserva señales editables. */\nexport function replaceHotModule(id: string, next: unknown): void {\n if (typeof next !== \"object\" || next === null || !(\"renderToString\" in next) || !(\"hydrate\" in next)\n || typeof next.renderToString !== \"function\" || typeof next.hydrate !== \"function\") {\n throw new TypeError(\"Invalid Meeko hot module\");\n }\n const component = next as HotComponent;\n const prepared = [...(instances.get(id)?.values() ?? [])].map((slot) => {\n const entry = slot.current;\n const state = entry.snapshot();\n const previous = entry.target.kind === \"root\" ? entry.target.root.innerHTML : scopeRange(entry.target.scope).cloneContents();\n return { slot, entry, state, html: component.renderToString(entry.props, state), previous };\n });\n for (const { slot, entry, state, html, previous } of prepared) {\n entry.dispose();\n if (entry.target.kind === \"root\") entry.target.root.innerHTML = html;\n else replaceScope(entry.target.scope, html);\n try {\n if (entry.target.kind === \"root\") component.hydrate(entry.target.root, entry.props, state);\n else {\n if (!component.hydrateScope) throw new Error(\"Updated Meeko child component has no hydrateScope export\");\n component.hydrateScope(entry.target.scope, entry.props, state);\n }\n if (slot.current === entry) throw new Error(\"Updated Meeko component did not register its hydrated instance\");\n } catch (error) {\n if (entry.target.kind === \"root\") entry.target.root.innerHTML = previous as string;\n else {\n const range = scopeRange(entry.target.scope);\n range.deleteContents();\n range.insertNode(previous as DocumentFragment);\n }\n if (entry.target.kind === \"root\") entry.component.hydrate(entry.target.root, entry.props, state);\n else entry.component.hydrateScope?.(entry.target.scope, entry.props, state);\n throw error;\n }\n }\n replaceHotStyle(id, component.css ?? \"\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import { captureParentOwner, isWritableSignal } from \"./reactivity.js\";\nimport type { DomScope } from \"./dom.js\";\n\n/** Valores de señales transferidos en memoria entre versiones de un componente. */\nexport type HotState = Record<string, unknown>;\n\n/** Contrato mínimo de un módulo Meeko reemplazable en desarrollo. */\nexport interface HotComponent<Props = unknown> {\n renderToString(props: Props, state?: HotState): string;\n hydrate(root: Element, props: Props, state?: HotState): () => void;\n hydrateScope?(scope: DomScope, props: Props, state?: HotState): () => void;\n css?: string;\n}\n\ninterface HotInstance {\n target: { kind: \"root\"; root: Element } | { kind: \"scope\"; scope: DomScope };\n props: unknown;\n component: HotComponent;\n snapshot: () => HotState;\n dispose: () => void;\n withParent: <T>(work: () => T) => T;\n}\n\ninterface HotSlot {\n current: HotInstance;\n stop: () => void;\n forget: () => void;\n}\n\nconst enabled = new Set<string>();\nconst instances = new Map<string, Map<Element | Comment, HotSlot>>();\n\n/** Copia solo las señales editables de un setup, sin serializarlas. */\nexport function snapshotSignals(bindings: Record<string, unknown>): HotState {\n const state: HotState = {};\n for (const [name, value] of Object.entries(bindings)) {\n if (isWritableSignal(value)) state[name] = value.value;\n }\n return state;\n}\n\n/** Restaura señales compatibles antes de renderizar o hidratar la nueva versión. */\nexport function restoreSignals(bindings: Record<string, unknown>, state?: HotState): void {\n if (!state) return;\n for (const [name, value] of Object.entries(state)) {\n const target = bindings[name];\n if (isWritableSignal(target)) target.value = value;\n }\n}\n\n/** Activa el seguimiento de instancias solo para módulos servidos por Vite. */\nexport function enableHotModule(id: string): void {\n enabled.add(id);\n}\n\nfunction registerHotTarget<Props>(id: string, key: Element | Comment, target: HotInstance[\"target\"], props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n if (!enabled.has(id)) return dispose;\n const entry: HotInstance = { target, props, component: component as HotComponent, snapshot, dispose, withParent: captureParentOwner() };\n let group = instances.get(id);\n if (!group) {\n group = new Map();\n instances.set(id, group);\n }\n const existing = group.get(key);\n if (existing) {\n existing.current = entry;\n return existing.stop;\n }\n const slot: HotSlot = {\n current: entry,\n forget: () => {\n if (group.get(key) !== slot) return;\n group.delete(key);\n if (group.size === 0) instances.delete(id);\n },\n stop: () => {\n if (group.get(key) !== slot) return;\n slot.forget();\n slot.current.dispose();\n },\n };\n group.set(key, slot);\n return slot.stop;\n}\n\n/** Registra una raíz hidratada y elimina el registro al liberarla. */\nexport function registerHotInstance<Props>(id: string, root: Element, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n return registerHotTarget(id, root, { kind: \"root\", root }, props, component, snapshot, dispose);\n}\n\n/** Registra un componente hijo delimitado por comentarios para actualizarlo sin tocar al padre. */\nexport function registerHotScope<Props>(id: string, scope: DomScope, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void {\n if (!scope.start || !scope.end) throw new Error(\"Hot child component requires a bounded scope\");\n return registerHotTarget(id, scope.start, { kind: \"scope\", scope }, props, component, snapshot, dispose);\n}\n\nfunction scopeRange(scope: DomScope): Range {\n if (!scope.start || !scope.end) throw new Error(\"Hot child component lost its markers\");\n const range = scope.start.ownerDocument.createRange();\n range.setStartAfter(scope.start);\n range.setEndBefore(scope.end);\n return range;\n}\n\nfunction replaceScope(scope: DomScope, html: string): void {\n const range = scopeRange(scope);\n const content = range.createContextualFragment(html);\n range.deleteContents();\n range.insertNode(content);\n}\n\n/** Aplica CSS nuevo después de la hoja virtual cargada por Vite. */\nexport function replaceHotStyle(id: string, css: string): void {\n if (typeof document === \"undefined\") return;\n // Module IDs are file paths and may contain CSS selector metacharacters.\n let style = [...document.querySelectorAll<HTMLStyleElement>(\"style[data-meeko-hot]\")]\n .find((candidate) => candidate.dataset.meekoHot === id);\n if (!style) {\n style = document.createElement(\"style\");\n style.dataset.meekoHot = id;\n document.head.appendChild(style);\n }\n style.textContent = css;\n}\n\n/** Sustituye las instancias activas sin recargar la página y conserva señales editables. */\nexport function replaceHotModule(id: string, next: unknown): void {\n if (typeof next !== \"object\" || next === null || !(\"renderToString\" in next) || !(\"hydrate\" in next)\n || typeof next.renderToString !== \"function\" || typeof next.hydrate !== \"function\") {\n throw new TypeError(\"Invalid Meeko hot module\");\n }\n const component = next as HotComponent;\n const prepared = [...(instances.get(id)?.values() ?? [])].map((slot) => {\n const entry = slot.current;\n const state = entry.snapshot();\n const previous = entry.target.kind === \"root\" ? entry.target.root.innerHTML : scopeRange(entry.target.scope).cloneContents();\n return { slot, entry, state, html: entry.withParent(() => component.renderToString(entry.props, state)), previous };\n });\n type Prepared = (typeof prepared)[number];\n const committed: Prepared[] = [];\n const rollback = ({ slot, entry, state, previous }: Prepared, failures: unknown[]): void => {\n const partial = slot.current !== entry ? slot.current : undefined;\n if (partial) {\n try { partial.dispose(); }\n catch (cleanupError) { failures.push(cleanupError); }\n }\n try {\n if (entry.target.kind === \"root\") entry.target.root.innerHTML = previous as string;\n else {\n const range = scopeRange(entry.target.scope);\n range.deleteContents();\n range.insertNode(previous as DocumentFragment);\n }\n if (entry.target.kind === \"root\") {\n const { root } = entry.target;\n entry.withParent(() => entry.component.hydrate(root, entry.props, state));\n } else {\n const { scope } = entry.target;\n entry.withParent(() => entry.component.hydrateScope?.(scope, entry.props, state));\n }\n if (slot.current === entry || slot.current === partial) throw new Error(\"Restored Meeko component did not register its hydrated instance\");\n } catch (rollbackError) {\n failures.push(rollbackError);\n if (slot.current !== entry && slot.current !== partial) {\n try { slot.current.dispose(); }\n catch (cleanupError) { failures.push(cleanupError); }\n }\n slot.forget();\n }\n };\n for (const item of prepared) {\n const { slot, entry, state, html } = item;\n try {\n entry.dispose();\n if (entry.target.kind === \"root\") entry.target.root.innerHTML = html;\n else replaceScope(entry.target.scope, html);\n if (entry.target.kind === \"root\") {\n const { root } = entry.target;\n entry.withParent(() => component.hydrate(root, entry.props, state));\n }\n else {\n if (!component.hydrateScope) throw new Error(\"Updated Meeko child component has no hydrateScope export\");\n const { scope } = entry.target;\n entry.withParent(() => component.hydrateScope!(scope, entry.props, state));\n }\n if (slot.current === entry) throw new Error(\"Updated Meeko component did not register its hydrated instance\");\n committed.push(item);\n } catch (error) {\n const failures: unknown[] = [error];\n rollback(item, failures);\n for (const previous of committed.reverse()) rollback(previous, failures);\n if (failures.length === 1) throw error;\n throw new AggregateError(failures, \"Meeko hot replacement and recovery failed\");\n }\n }\n replaceHotStyle(id, component.css ?? \"\");\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { batch, computed, createContext, createRoot, effect, inject, onCleanup, provide, signal, untrack } from "./reactivity.js";
|
|
2
2
|
export type { ContextKey, ReadonlySignal, Signal } from "./reactivity.js";
|
|
3
|
+
export { provideErrorHandler } from "./component-errors.js";
|
|
4
|
+
export type { ComponentErrorHandler } from "./component-errors.js";
|
|
3
5
|
export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
|
|
4
6
|
export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
|
|
5
7
|
export type { Component, ComponentModule, ComponentRelease, DomScope } from "./dom.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { batch, computed, createContext, createRoot, effect, inject, onCleanup, provide, signal, untrack } from "./reactivity.js";
|
|
2
|
+
export { provideErrorHandler } from "./component-errors.js";
|
|
2
3
|
export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
|
|
3
4
|
export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
|
|
4
5
|
export { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from "./hmr.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAElI,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEjK,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEtJ,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC","sourcesContent":["export { batch, computed, createContext, createRoot, effect, inject, onCleanup, provide, signal, untrack } from \"./reactivity.js\";\nexport type { ContextKey, ReadonlySignal, Signal } from \"./reactivity.js\";\nexport { attributeValue, escapeHtml, renderAttribute, serializeJson } from \"./server.js\";\nexport { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from \"./dom.js\";\nexport type { Component, ComponentModule, ComponentRelease, DomScope } from \"./dom.js\";\nexport { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from \"./hmr.js\";\nexport type { HotComponent, HotState } from \"./hmr.js\";\nexport { registerDevtoolsInstance } from \"./devtools.js\";\nexport type { DevtoolsEvent, DevtoolsHook, DevtoolsInstance, DevtoolsRegistration, DevtoolsSnapshot, DevtoolsValue } from \"./devtools.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAElI,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAEjK,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEtJ,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC","sourcesContent":["export { batch, computed, createContext, createRoot, effect, inject, onCleanup, provide, signal, untrack } from \"./reactivity.js\";\nexport type { ContextKey, ReadonlySignal, Signal } from \"./reactivity.js\";\nexport { provideErrorHandler } from \"./component-errors.js\";\nexport type { ComponentErrorHandler } from \"./component-errors.js\";\nexport { attributeValue, escapeHtml, renderAttribute, serializeJson } from \"./server.js\";\nexport { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from \"./dom.js\";\nexport type { Component, ComponentModule, ComponentRelease, DomScope } from \"./dom.js\";\nexport { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from \"./hmr.js\";\nexport type { HotComponent, HotState } from \"./hmr.js\";\nexport { registerDevtoolsInstance } from \"./devtools.js\";\nexport type { DevtoolsEvent, DevtoolsHook, DevtoolsInstance, DevtoolsRegistration, DevtoolsSnapshot, DevtoolsValue } from \"./devtools.js\";\n"]}
|
package/dist/init.js
CHANGED
|
@@ -125,7 +125,33 @@ const server = createServer(async (request, response) => {
|
|
|
125
125
|
|
|
126
126
|
const port = Number(process.env.PORT ?? 4173);
|
|
127
127
|
server.listen(port, "127.0.0.1", () => console.log("http://127.0.0.1:" + port));
|
|
128
|
-
|
|
128
|
+
let shutdownPromise;
|
|
129
|
+
const closeServer = () => new Promise((resolveClose, rejectClose) => {
|
|
130
|
+
server.close((error) => error ? rejectClose(error) : resolveClose());
|
|
131
|
+
});
|
|
132
|
+
async function shutdown() {
|
|
133
|
+
if (shutdownPromise) return shutdownPromise;
|
|
134
|
+
shutdownPromise = (async () => {
|
|
135
|
+
const results = await Promise.allSettled([
|
|
136
|
+
closeServer(),
|
|
137
|
+
vite?.close() ?? Promise.resolve(),
|
|
138
|
+
]);
|
|
139
|
+
const failures = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
140
|
+
if (failures.length === 1) throw failures[0];
|
|
141
|
+
if (failures.length > 1) throw new AggregateError(failures, "Meeko server shutdown failed");
|
|
142
|
+
})();
|
|
143
|
+
return shutdownPromise;
|
|
144
|
+
}
|
|
145
|
+
const onSignal = () => {
|
|
146
|
+
process.off("SIGINT", onSignal);
|
|
147
|
+
process.off("SIGTERM", onSignal);
|
|
148
|
+
void shutdown().catch((error) => {
|
|
149
|
+
try { console.error("Meeko server shutdown failed", error); }
|
|
150
|
+
finally { process.exitCode = 1; }
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
process.once("SIGINT", onSignal);
|
|
154
|
+
process.once("SIGTERM", onSignal);
|
|
129
155
|
`,
|
|
130
156
|
"README.md": `# Meeko SSR starter
|
|
131
157
|
|