@meekojs/meeko 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -2
- package/dist/autoimports-ts.d.ts +26 -0
- package/dist/autoimports-ts.js +127 -0
- package/dist/autoimports-ts.js.map +1 -0
- package/dist/autoimports.d.ts +21 -0
- package/dist/autoimports.js +285 -0
- package/dist/autoimports.js.map +1 -0
- package/dist/check.js +35 -23
- package/dist/check.js.map +1 -0
- package/dist/cli.js +59 -19
- package/dist/cli.js.map +1 -0
- package/dist/compiler/import-graph.d.ts +8 -0
- package/dist/compiler/import-graph.js +33 -0
- package/dist/compiler/import-graph.js.map +1 -0
- package/dist/compiler/mapping.js +1 -0
- package/dist/compiler/mapping.js.map +1 -0
- package/dist/compiler/project-root.d.ts +2 -0
- package/dist/compiler/project-root.js +18 -0
- package/dist/compiler/project-root.js.map +1 -0
- package/dist/compiler/sfc.js +1 -0
- package/dist/compiler/sfc.js.map +1 -0
- package/dist/compiler/sourcemap.d.ts +3 -0
- package/dist/compiler/sourcemap.js +75 -0
- package/dist/compiler/sourcemap.js.map +1 -0
- package/dist/compiler/template.js +1 -0
- package/dist/compiler/template.js.map +1 -0
- package/dist/compiler.js +22 -11
- package/dist/compiler.js.map +1 -0
- package/dist/devtools-ui/index.js +56 -6
- package/dist/devtools-ui/index.js.map +1 -0
- package/dist/devtools.d.ts +9 -0
- package/dist/devtools.js +16 -6
- package/dist/devtools.js.map +1 -0
- package/dist/dom.d.ts +17 -6
- package/dist/dom.js +86 -19
- package/dist/dom.js.map +1 -0
- package/dist/framework/check-app.d.ts +5 -0
- package/dist/framework/check-app.js +59 -0
- package/dist/framework/check-app.js.map +1 -0
- package/dist/framework/client.d.ts +45 -0
- package/dist/framework/client.js +146 -0
- package/dist/framework/client.js.map +1 -0
- package/dist/framework/clients.d.ts +82 -0
- package/dist/framework/clients.js +182 -0
- package/dist/framework/clients.js.map +1 -0
- package/dist/framework/config-type.d.ts +10 -0
- package/dist/framework/config-type.js +4 -0
- package/dist/framework/config-type.js.map +1 -0
- package/dist/framework/config.d.ts +15 -0
- package/dist/framework/config.js +138 -0
- package/dist/framework/config.js.map +1 -0
- package/dist/framework/contracts-generate.d.ts +8 -0
- package/dist/framework/contracts-generate.js +227 -0
- package/dist/framework/contracts-generate.js.map +1 -0
- package/dist/framework/generate.d.ts +7 -0
- package/dist/framework/generate.js +67 -0
- package/dist/framework/generate.js.map +1 -0
- package/dist/framework/init.d.ts +2 -0
- package/dist/framework/init.js +362 -0
- package/dist/framework/init.js.map +1 -0
- package/dist/framework/modules.d.ts +22 -0
- package/dist/framework/modules.js +29 -0
- package/dist/framework/modules.js.map +1 -0
- package/dist/framework/page.d.ts +60 -0
- package/dist/framework/page.js +42 -0
- package/dist/framework/page.js.map +1 -0
- package/dist/framework/prerender.d.ts +14 -0
- package/dist/framework/prerender.js +104 -0
- package/dist/framework/prerender.js.map +1 -0
- package/dist/framework/router.d.ts +39 -0
- package/dist/framework/router.js +173 -0
- package/dist/framework/router.js.map +1 -0
- package/dist/framework/server.d.ts +51 -0
- package/dist/framework/server.js +192 -0
- package/dist/framework/server.js.map +1 -0
- package/dist/framework/socket-contracts-generate.d.ts +5 -0
- package/dist/framework/socket-contracts-generate.js +159 -0
- package/dist/framework/socket-contracts-generate.js.map +1 -0
- package/dist/framework/socket.d.ts +70 -0
- package/dist/framework/socket.js +150 -0
- package/dist/framework/socket.js.map +1 -0
- package/dist/framework/state.d.ts +15 -0
- package/dist/framework/state.js +72 -0
- package/dist/framework/state.js.map +1 -0
- package/dist/framework-server/auto-clients.d.ts +3 -0
- package/dist/framework-server/auto-clients.js +27 -0
- package/dist/framework-server/auto-clients.js.map +1 -0
- package/dist/framework-server/context.d.ts +21 -0
- package/dist/framework-server/context.js +22 -0
- package/dist/framework-server/context.js.map +1 -0
- package/dist/framework-server/contracts.d.ts +64 -0
- package/dist/framework-server/contracts.js +129 -0
- package/dist/framework-server/contracts.js.map +1 -0
- package/dist/framework-server/discovery.d.ts +40 -0
- package/dist/framework-server/discovery.js +252 -0
- package/dist/framework-server/discovery.js.map +1 -0
- package/dist/framework-server/local.d.ts +19 -0
- package/dist/framework-server/local.js +98 -0
- package/dist/framework-server/local.js.map +1 -0
- package/dist/framework-server/node-upgrade.d.ts +23 -0
- package/dist/framework-server/node-upgrade.js +78 -0
- package/dist/framework-server/node-upgrade.js.map +1 -0
- package/dist/framework-server/node-websocket.d.ts +16 -0
- package/dist/framework-server/node-websocket.js +125 -0
- package/dist/framework-server/node-websocket.js.map +1 -0
- package/dist/framework-server/openapi.d.ts +17 -0
- package/dist/framework-server/openapi.js +118 -0
- package/dist/framework-server/openapi.js.map +1 -0
- package/dist/framework-server/session-stores.d.ts +66 -0
- package/dist/framework-server/session-stores.js +96 -0
- package/dist/framework-server/session-stores.js.map +1 -0
- package/dist/framework-server/sessions.d.ts +64 -0
- package/dist/framework-server/sessions.js +136 -0
- package/dist/framework-server/sessions.js.map +1 -0
- package/dist/framework-server/socket.d.ts +83 -0
- package/dist/framework-server/socket.js +161 -0
- package/dist/framework-server/socket.js.map +1 -0
- package/dist/framework-server.d.ts +21 -0
- package/dist/framework-server.js +12 -0
- package/dist/framework-server.js.map +1 -0
- package/dist/framework.d.ts +29 -0
- package/dist/framework.js +13 -0
- package/dist/framework.js.map +1 -0
- package/dist/hmr.js +1 -0
- package/dist/hmr.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +209 -0
- package/dist/init.js.map +1 -0
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -0
- package/dist/node.d.ts +12 -0
- package/dist/node.js +52 -0
- package/dist/node.js.map +1 -0
- package/dist/reactivity.d.ts +25 -1
- package/dist/reactivity.js +69 -10
- package/dist/reactivity.js.map +1 -0
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -0
- package/dist/types.js +23 -8
- package/dist/types.js.map +1 -0
- package/dist/vite.js +91 -6
- package/dist/vite.js.map +1 -0
- package/package.json +19 -3
package/dist/dom.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Signal } from "./reactivity.js";
|
|
1
|
+
import type { EffectSource, Signal } from "./reactivity.js";
|
|
2
2
|
/** Fragmento de DOM que pertenece a una instancia o a una región dinámica. */
|
|
3
3
|
export interface DomScope {
|
|
4
4
|
root: Element;
|
|
@@ -14,6 +14,13 @@ export interface ComponentModule<Props extends object> {
|
|
|
14
14
|
};
|
|
15
15
|
hydrateScope(scope: DomScope, props: Props, state?: Record<string, unknown>): ComponentRelease;
|
|
16
16
|
}
|
|
17
|
+
/** Componente compilado que puede transferir su estado durante SSR. */
|
|
18
|
+
export type Component<Props extends object> = ComponentModule<Props> & {
|
|
19
|
+
renderWithState(props: Props): {
|
|
20
|
+
html: string;
|
|
21
|
+
state: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
17
24
|
/** Función de limpieza que puede exponer el estado editable de su instancia. */
|
|
18
25
|
export interface ComponentRelease {
|
|
19
26
|
(): void;
|
|
@@ -24,11 +31,11 @@ export declare function withStateSnapshot(release: () => void, snapshot: () => R
|
|
|
24
31
|
/** Renderiza un hijo entre comentarios de ámbito sin añadir un elemento visual. */
|
|
25
32
|
export declare function renderComponent<Props extends object>(id: number, component: ComponentModule<Props>, props: Props, transferState?: boolean): string;
|
|
26
33
|
/** Hidrata un hijo dentro de su rango y actualiza props reactivas con limpieza previa. */
|
|
27
|
-
export declare function bindComponent<Props extends object>(scope: DomScope, id: number,
|
|
34
|
+
export declare function bindComponent<Props extends object>(scope: DomScope, id: number, input: ComponentModule<Props> | (() => ComponentModule<Props>), read: () => Props, location: string): () => void;
|
|
28
35
|
/** Conecta un texto reactivo al marcador SSR y comprueba su valor inicial. */
|
|
29
|
-
export declare function bindText(scope: DomScope, id: number, read: () => unknown, location: string): () => void;
|
|
36
|
+
export declare function bindText(scope: DomScope, id: number, read: () => unknown, location: string, source?: EffectSource): () => void;
|
|
30
37
|
/** Conecta un atributo dinámico al elemento SSR y comprueba su valor inicial. */
|
|
31
|
-
export declare function bindAttribute(scope: DomScope, id: number, name: string, read: () => unknown, location: string): () => void;
|
|
38
|
+
export declare function bindAttribute(scope: DomScope, id: number, name: string, read: () => unknown, location: string, source?: EffectSource): () => void;
|
|
32
39
|
/** Conecta un evento al elemento SSR y entrega su función de limpieza. */
|
|
33
40
|
export declare function bindEvent(scope: DomScope, id: number, name: string, handler: (event: Event) => void): () => void;
|
|
34
41
|
/** Genera los límites estables de una condición en SSR. */
|
|
@@ -39,5 +46,9 @@ export declare function bindIf(scope: DomScope, id: number, read: () => unknown,
|
|
|
39
46
|
export declare function keyToken(key: unknown): string;
|
|
40
47
|
/** Renderiza una lista con límites identificados por claves únicas. */
|
|
41
48
|
export declare function renderFor<T>(id: number, items: Iterable<T>, keyOf: (item: T, index: number) => string | number, render: (item: T, index: number) => string): string;
|
|
42
|
-
/**
|
|
43
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Hidrata una lista y reordena regiones por clave sin reemplazar nodos conservados.
|
|
51
|
+
* El compilador desactiva `trackIndex` sólo cuando el cuerpo de hidratación no lee
|
|
52
|
+
* la señal de índice; las llamadas directas conservan la señal por defecto.
|
|
53
|
+
*/
|
|
54
|
+
export declare function bindFor<T>(scope: DomScope, id: number, read: () => Iterable<T>, keyOf: (item: T, index: number) => string | number, render: (item: T, index: number) => string, hydrate: (item: Signal<T>, index: Signal<number>, inner: DomScope) => () => void, trackIndex?: boolean): () => void;
|
package/dist/dom.js
CHANGED
|
@@ -107,7 +107,7 @@ function componentRegion(scope, id) {
|
|
|
107
107
|
return { inner: { root: scope.root, start, end }, state: parsed };
|
|
108
108
|
}
|
|
109
109
|
/** Hidrata un hijo dentro de su rango y actualiza props reactivas con limpieza previa. */
|
|
110
|
-
export function bindComponent(scope, id,
|
|
110
|
+
export function bindComponent(scope, id, input, read, location) {
|
|
111
111
|
let inner;
|
|
112
112
|
let state;
|
|
113
113
|
try {
|
|
@@ -116,44 +116,65 @@ export function bindComponent(scope, id, component, read, location) {
|
|
|
116
116
|
catch (error) {
|
|
117
117
|
throw new Error(`Hydration mismatch in ${location}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
118
118
|
}
|
|
119
|
+
const resolveComponent = () => typeof input === "function" ? input() : input;
|
|
120
|
+
let component = resolveComponent();
|
|
119
121
|
let props = read();
|
|
120
122
|
let release = component.hydrateScope(inner, props, state);
|
|
121
123
|
const stop = effect(() => {
|
|
124
|
+
const nextComponent = resolveComponent();
|
|
122
125
|
const next = read();
|
|
123
|
-
if (sameProps(props, next))
|
|
126
|
+
if (nextComponent === component && sameProps(props, next))
|
|
124
127
|
return;
|
|
125
|
-
const state = release.snapshot?.();
|
|
126
|
-
const html =
|
|
128
|
+
const state = nextComponent === component ? release.snapshot?.() : undefined;
|
|
129
|
+
const html = nextComponent.renderToString(next, state);
|
|
127
130
|
release();
|
|
128
131
|
clearRegion(inner);
|
|
129
132
|
insertHtml(inner.end, html);
|
|
130
|
-
release =
|
|
133
|
+
release = nextComponent.hydrateScope(inner, next, state);
|
|
134
|
+
component = nextComponent;
|
|
131
135
|
props = next;
|
|
132
136
|
});
|
|
133
137
|
return () => { stop(); release(); };
|
|
134
138
|
}
|
|
135
139
|
/** Conecta un texto reactivo al marcador SSR y comprueba su valor inicial. */
|
|
136
|
-
export function bindText(scope, id, read, location) {
|
|
140
|
+
export function bindText(scope, id, read, location, source) {
|
|
137
141
|
const marker = findComment(scope, `meeko:text:${id}`);
|
|
138
142
|
const text = marker.nextSibling;
|
|
139
143
|
if (!text || text.nodeType !== 3)
|
|
140
144
|
throw new Error(`Missing text node in ${location}`);
|
|
141
|
-
|
|
145
|
+
const initial = String(read());
|
|
146
|
+
if (text.textContent !== initial)
|
|
142
147
|
throw new Error(`Hydration mismatch in ${location}`);
|
|
143
|
-
|
|
148
|
+
let first = true;
|
|
149
|
+
return effect(() => {
|
|
150
|
+
const next = String(read());
|
|
151
|
+
if (first) {
|
|
152
|
+
first = false;
|
|
153
|
+
if (text.textContent === next)
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
text.textContent = next;
|
|
157
|
+
}, source);
|
|
144
158
|
}
|
|
145
159
|
/** Conecta un atributo dinámico al elemento SSR y comprueba su valor inicial. */
|
|
146
|
-
export function bindAttribute(scope, id, name, read, location) {
|
|
160
|
+
export function bindAttribute(scope, id, name, read, location, source) {
|
|
147
161
|
const target = findElement(scope, `data-meeko-a-${id}`);
|
|
148
|
-
|
|
162
|
+
const initial = attributeValue(name, read());
|
|
163
|
+
if (target.getAttribute(name) !== initial)
|
|
149
164
|
throw new Error(`Hydration mismatch in ${location}`);
|
|
165
|
+
let first = true;
|
|
150
166
|
return effect(() => {
|
|
151
167
|
const next = attributeValue(name, read());
|
|
168
|
+
if (first) {
|
|
169
|
+
first = false;
|
|
170
|
+
if (target.getAttribute(name) === next)
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
152
173
|
if (next === null)
|
|
153
174
|
target.removeAttribute(name);
|
|
154
175
|
else
|
|
155
176
|
target.setAttribute(name, next);
|
|
156
|
-
});
|
|
177
|
+
}, source);
|
|
157
178
|
}
|
|
158
179
|
/** Conecta un evento al elemento SSR y entrega su función de limpieza. */
|
|
159
180
|
export function bindEvent(scope, id, name, handler) {
|
|
@@ -227,11 +248,42 @@ function moveRange(start, end, before) {
|
|
|
227
248
|
}
|
|
228
249
|
parent.insertBefore(fragment, before);
|
|
229
250
|
}
|
|
230
|
-
/**
|
|
231
|
-
|
|
251
|
+
/** Encuentra las posiciones que ya mantienen el orden relativo de la lista anterior. */
|
|
252
|
+
function stableListPositions(indices) {
|
|
253
|
+
const tails = [];
|
|
254
|
+
const previous = new Int32Array(indices.length).fill(-1);
|
|
255
|
+
for (let index = 0; index < indices.length; index++) {
|
|
256
|
+
const oldIndex = indices[index];
|
|
257
|
+
if (oldIndex < 0)
|
|
258
|
+
continue;
|
|
259
|
+
let low = 0;
|
|
260
|
+
let high = tails.length;
|
|
261
|
+
while (low < high) {
|
|
262
|
+
const middle = (low + high) >>> 1;
|
|
263
|
+
if (indices[tails[middle]] < oldIndex)
|
|
264
|
+
low = middle + 1;
|
|
265
|
+
else
|
|
266
|
+
high = middle;
|
|
267
|
+
}
|
|
268
|
+
if (low > 0)
|
|
269
|
+
previous[index] = tails[low - 1];
|
|
270
|
+
tails[low] = index;
|
|
271
|
+
}
|
|
272
|
+
const stable = new Set();
|
|
273
|
+
for (let index = tails.at(-1) ?? -1; index >= 0; index = previous[index])
|
|
274
|
+
stable.add(index);
|
|
275
|
+
return stable;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Hidrata una lista y reordena regiones por clave sin reemplazar nodos conservados.
|
|
279
|
+
* El compilador desactiva `trackIndex` sólo cuando el cuerpo de hidratación no lee
|
|
280
|
+
* la señal de índice; las llamadas directas conservan la señal por defecto.
|
|
281
|
+
*/
|
|
282
|
+
export function bindFor(scope, id, read, keyOf, render, hydrate, trackIndex = true) {
|
|
232
283
|
const outer = region(scope, "for", id);
|
|
233
284
|
const entries = new Map();
|
|
234
285
|
const initial = [...read()];
|
|
286
|
+
let currentKeys = [];
|
|
235
287
|
let cursor = outer.start.nextSibling;
|
|
236
288
|
for (let index = 0; index < initial.length; index++) {
|
|
237
289
|
const item = initial[index];
|
|
@@ -249,23 +301,35 @@ export function bindFor(scope, id, read, keyOf, render, hydrate) {
|
|
|
249
301
|
if (!end)
|
|
250
302
|
throw new Error(`Missing end marker for list key ${key}`);
|
|
251
303
|
const value = signal(item);
|
|
252
|
-
const position = signal(index);
|
|
304
|
+
const position = trackIndex ? signal(index) : undefined;
|
|
253
305
|
const release = createRoot((dispose) => {
|
|
254
306
|
const cleanup = hydrate(value, position, { root: scope.root, start, end: end });
|
|
255
307
|
return () => { cleanup(); dispose(); };
|
|
256
308
|
});
|
|
257
309
|
entries.set(key, { key, start, end: end, value, index: position, release });
|
|
310
|
+
currentKeys.push(key);
|
|
258
311
|
cursor = end.nextSibling;
|
|
259
312
|
}
|
|
260
313
|
if (cursor !== outer.end)
|
|
261
314
|
throw new Error(`Hydration mismatch in list ${id}: extra nodes`);
|
|
315
|
+
let initialItems = initial;
|
|
262
316
|
const stop = effect(() => {
|
|
263
317
|
const items = [...read()];
|
|
318
|
+
if (initialItems) {
|
|
319
|
+
const previous = initialItems;
|
|
320
|
+
initialItems = undefined;
|
|
321
|
+
if (items.length === previous.length && items.every((item, index) => Object.is(item, previous[index]) && keyToken(keyOf(item, index)) === currentKeys[index]))
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
264
324
|
const nextKeys = items.map((item, index) => keyToken(keyOf(item, index)));
|
|
265
|
-
|
|
325
|
+
const nextKeySet = new Set(nextKeys);
|
|
326
|
+
if (nextKeySet.size !== nextKeys.length)
|
|
266
327
|
throw new Error(`Duplicate Meeko list key in region ${id}`);
|
|
328
|
+
const oldPositions = new Map(currentKeys.map((key, index) => [key, index]));
|
|
329
|
+
const oldIndices = nextKeys.map((key) => oldPositions.get(key) ?? -1);
|
|
330
|
+
const stable = stableListPositions(oldIndices);
|
|
267
331
|
for (const [key, entry] of entries) {
|
|
268
|
-
if (
|
|
332
|
+
if (nextKeySet.has(key))
|
|
269
333
|
continue;
|
|
270
334
|
entry.release();
|
|
271
335
|
let node = entry.start;
|
|
@@ -293,7 +357,7 @@ export function bindFor(scope, id, read, keyOf, render, hydrate) {
|
|
|
293
357
|
if (!start)
|
|
294
358
|
throw new Error(`Missing new list marker for ${key}`);
|
|
295
359
|
const value = signal(item);
|
|
296
|
-
const position = signal(index);
|
|
360
|
+
const position = trackIndex ? signal(index) : undefined;
|
|
297
361
|
const release = createRoot((dispose) => {
|
|
298
362
|
const cleanup = hydrate(value, position, { root: scope.root, start: start, end });
|
|
299
363
|
return () => { cleanup(); dispose(); };
|
|
@@ -303,13 +367,16 @@ export function bindFor(scope, id, read, keyOf, render, hydrate) {
|
|
|
303
367
|
}
|
|
304
368
|
else {
|
|
305
369
|
entry.value.value = item;
|
|
306
|
-
entry.index
|
|
370
|
+
if (entry.index)
|
|
371
|
+
entry.index.value = index;
|
|
307
372
|
}
|
|
308
|
-
if (entry.end.nextSibling !== before)
|
|
373
|
+
if (oldIndices[index] !== -1 && !stable.has(index) && entry.end.nextSibling !== before)
|
|
309
374
|
moveRange(entry.start, entry.end, before);
|
|
310
375
|
before = entry.start;
|
|
311
376
|
}
|
|
377
|
+
currentKeys = nextKeys;
|
|
312
378
|
});
|
|
313
379
|
return () => { stop(); for (const entry of entries.values())
|
|
314
380
|
entry.release(); entries.clear(); };
|
|
315
381
|
}
|
|
382
|
+
//# sourceMappingURL=dom.js.map
|
package/dist/dom.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRoot, effect, signal } from \"./reactivity.js\";\nimport type { EffectSource, Signal } from \"./reactivity.js\";\nimport { attributeValue, serializeJson } from \"./server.js\";\n\n/** Fragmento de DOM que pertenece a una instancia o a una región dinámica. */\nexport interface DomScope {\n root: Element;\n start?: Comment;\n end?: Comment;\n}\n\n/** Contrato de un módulo de componente compilado para composición sin wrapper. */\nexport interface ComponentModule<Props extends object> {\n renderToString(props: Props, state?: Record<string, unknown>): string;\n renderWithState?(props: Props): { html: string; state: Record<string, unknown> };\n hydrateScope(scope: DomScope, props: Props, state?: Record<string, unknown>): ComponentRelease;\n}\n\n/** Componente compilado que puede transferir su estado durante SSR. */\nexport type Component<Props extends object> = ComponentModule<Props> & {\n renderWithState(props: Props): { html: string; state: Record<string, unknown> };\n};\n\n/** Función de limpieza que puede exponer el estado editable de su instancia. */\nexport interface ComponentRelease {\n (): void;\n snapshot?: () => Record<string, unknown>;\n}\n\n/** Añade una lectura de estado a la limpieza de un componente compilado. */\nexport function withStateSnapshot(release: () => void, snapshot: () => Record<string, unknown>): ComponentRelease {\n return Object.assign(release, { snapshot });\n}\n\nconst componentStart = /^meeko:component:\\d+(?::.*)?$/;\n\nfunction componentEnd(start: Node): Node {\n let depth = 0;\n for (let node: Node | null = start; node; node = node.nextSibling) {\n if (node.nodeType !== 8) continue;\n const data = (node as Comment).data;\n if (componentStart.test(data)) depth++;\n else if (/^\\/meeko:component:\\d+$/.test(data) && --depth === 0) return node;\n }\n throw new Error(`Missing end marker for ${(start as Comment).data}`);\n}\n\nfunction visit(scope: DomScope, callback: (node: Node) => boolean): Node | undefined {\n function siblings(first: Node | null, stop: Node | null = null): Node | undefined {\n for (let node = first; node && node !== stop;) {\n if (callback(node)) return node;\n if (node.nodeType === 8 && componentStart.test((node as Comment).data)) {\n const end = componentEnd(node);\n if (callback(end)) return end;\n node = end.nextSibling;\n continue;\n }\n const found = siblings(node.firstChild);\n if (found) return found;\n node = node.nextSibling;\n }\n return undefined;\n }\n return siblings(scope.start?.nextSibling ?? scope.root.firstChild, scope.end ?? null);\n}\n\nfunction findComment(scope: DomScope, data: string): Comment {\n const node = visit(scope, (candidate) => candidate.nodeType === 8 && (candidate as Comment).data === data);\n if (!node) throw new Error(`Missing Meeko marker ${data}`);\n return node as Comment;\n}\n\nfunction findElement(scope: DomScope, name: string): Element {\n const node = visit(scope, (candidate) => candidate.nodeType === 1 && (candidate as Element).hasAttribute(name));\n if (!node) throw new Error(`Missing Meeko target ${name}`);\n return node as Element;\n}\n\nfunction region(scope: DomScope, kind: string, id: number): DomScope {\n const start = findComment(scope, `meeko:${kind}:${id}`);\n const end = findComment(scope, `/meeko:${kind}:${id}`);\n if (start.parentNode !== end.parentNode) throw new Error(`Invalid Meeko region ${kind}:${id}`);\n return { root: scope.root, start, end };\n}\n\nfunction clearRegion(scope: DomScope): void {\n if (!scope.start || !scope.end) throw new Error(\"Expected a bounded Meeko region\");\n for (let node = scope.start.nextSibling; node && node !== scope.end;) {\n const next = node.nextSibling;\n node.parentNode?.removeChild(node);\n node = next;\n }\n}\n\nfunction insertHtml(end: Comment, html: string): void {\n const parent = end.parentNode;\n if (!parent) throw new Error(\"Detached Meeko region\");\n const range = end.ownerDocument.createRange();\n range.selectNodeContents(parent);\n parent.insertBefore(range.createContextualFragment(html), end);\n}\n\nfunction sameProps<Props extends object>(first: Props, second: Props): boolean {\n const firstKeys = Object.keys(first);\n const secondKeys = Object.keys(second);\n return firstKeys.length === secondKeys.length && firstKeys.every((key) => Object.hasOwn(second, key) && Object.is(first[key as keyof Props], second[key as keyof Props]));\n}\n\n/** Renderiza un hijo entre comentarios de ámbito sin añadir un elemento visual. */\nexport function renderComponent<Props extends object>(id: number, component: ComponentModule<Props>, props: Props, transferState = false): string {\n if (transferState) {\n if (!component.renderWithState) throw new TypeError(`Component ${id} cannot transfer SSR state`);\n const rendered = component.renderWithState(props);\n return `<!--meeko:component:${id}:${encodeURIComponent(serializeJson(rendered.state))}-->${rendered.html}<!--/meeko:component:${id}-->`;\n }\n return `<!--meeko:component:${id}-->${component.renderToString(props)}<!--/meeko:component:${id}-->`;\n}\n\nfunction componentRegion(scope: DomScope, id: number): { inner: DomScope; state?: Record<string, unknown> } {\n const prefix = `meeko:component:${id}`;\n const start = visit(scope, (candidate) => candidate.nodeType === 8 && ((candidate as Comment).data === prefix || (candidate as Comment).data.startsWith(`${prefix}:`))) as Comment | undefined;\n if (!start) throw new Error(`Missing Meeko marker ${prefix}`);\n const end = findComment(scope, `/meeko:component:${id}`);\n if (start.parentNode !== end.parentNode) throw new Error(`Invalid Meeko region component:${id}`);\n const encoded = start.data.slice(prefix.length);\n if (!encoded) return { inner: { root: scope.root, start, end } };\n const parsed: unknown = JSON.parse(decodeURIComponent(encoded.slice(1)));\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) throw new TypeError(`Invalid Meeko state for component:${id}`);\n return { inner: { root: scope.root, start, end }, state: parsed as Record<string, unknown> };\n}\n\n/** Hidrata un hijo dentro de su rango y actualiza props reactivas con limpieza previa. */\nexport function bindComponent<Props extends object>(scope: DomScope, id: number, input: ComponentModule<Props> | (() => ComponentModule<Props>), read: () => Props, location: string): () => void {\n let inner: DomScope;\n let state: Record<string, unknown> | undefined;\n try {\n ({ inner, state } = componentRegion(scope, id));\n } catch (error) {\n throw new Error(`Hydration mismatch in ${location}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });\n }\n const resolveComponent = (): ComponentModule<Props> => typeof input === \"function\" ? input() : input;\n let component = resolveComponent();\n let props = read();\n let release = component.hydrateScope(inner, props, state);\n const stop = effect(() => {\n const nextComponent = resolveComponent();\n const next = read();\n if (nextComponent === component && sameProps(props, next)) return;\n const state = nextComponent === component ? release.snapshot?.() : undefined;\n const html = nextComponent.renderToString(next, state);\n release();\n clearRegion(inner);\n insertHtml(inner.end!, html);\n release = nextComponent.hydrateScope(inner, next, state);\n component = nextComponent;\n props = next;\n });\n return () => { stop(); release(); };\n}\n\n/** Conecta un texto reactivo al marcador SSR y comprueba su valor inicial. */\nexport function bindText(scope: DomScope, id: number, read: () => unknown, location: string, source?: EffectSource): () => void {\n const marker = findComment(scope, `meeko:text:${id}`);\n const text = marker.nextSibling;\n if (!text || text.nodeType !== 3) throw new Error(`Missing text node in ${location}`);\n const initial = String(read());\n if (text.textContent !== initial) throw new Error(`Hydration mismatch in ${location}`);\n let first = true;\n return effect(() => {\n const next = String(read());\n if (first) {\n first = false;\n if (text.textContent === next) return;\n }\n text.textContent = next;\n }, source);\n}\n\n/** Conecta un atributo dinámico al elemento SSR y comprueba su valor inicial. */\nexport function bindAttribute(scope: DomScope, id: number, name: string, read: () => unknown, location: string, source?: EffectSource): () => void {\n const target = findElement(scope, `data-meeko-a-${id}`);\n const initial = attributeValue(name, read());\n if (target.getAttribute(name) !== initial) throw new Error(`Hydration mismatch in ${location}`);\n let first = true;\n return effect(() => {\n const next = attributeValue(name, read());\n if (first) {\n first = false;\n if (target.getAttribute(name) === next) return;\n }\n if (next === null) target.removeAttribute(name);\n else target.setAttribute(name, next);\n }, source);\n}\n\n/** Conecta un evento al elemento SSR y entrega su función de limpieza. */\nexport function bindEvent(scope: DomScope, id: number, name: string, handler: (event: Event) => void): () => void {\n const target = findElement(scope, `data-meeko-e-${id}`);\n target.addEventListener(name, handler);\n return () => target.removeEventListener(name, handler);\n}\n\n/** Genera los límites estables de una condición en SSR. */\nexport function renderIf(id: number, condition: unknown, render: () => string): string {\n return `<!--meeko:if:${id}-->${condition ? render() : \"\"}<!--/meeko:if:${id}-->`;\n}\n\n/** Hidrata una condición y conserva los nodos mientras sigue siendo verdadera. */\nexport function bindIf(scope: DomScope, id: number, read: () => unknown, render: () => string, hydrate: (inner: DomScope) => () => void): () => void {\n const inner = region(scope, \"if\", id);\n let active = Boolean(read());\n if (!active && inner.start?.nextSibling !== inner.end) throw new Error(`Hydration mismatch in condition ${id}`);\n let release = active ? hydrate(inner) : undefined;\n const stop = effect(() => {\n const next = Boolean(read());\n if (next === active) return;\n release?.();\n release = undefined;\n clearRegion(inner);\n if (next) {\n insertHtml(inner.end!, render());\n release = hydrate(inner);\n }\n active = next;\n });\n return () => { stop(); release?.(); };\n}\n\n/** Convierte una clave de lista en un token seguro para comentarios HTML. */\nexport function keyToken(key: unknown): string {\n if (typeof key === \"number\" && Number.isFinite(key)) return `n${encodeURIComponent(String(key)).replaceAll(\"-\", \"%2D\")}`;\n if (typeof key === \"string\") return `s${encodeURIComponent(key).replaceAll(\"-\", \"%2D\")}`;\n throw new TypeError(\"Meeko list keys must be strings or finite numbers\");\n}\n\n/** Renderiza una lista con límites identificados por claves únicas. */\nexport function renderFor<T>(id: number, items: Iterable<T>, keyOf: (item: T, index: number) => string | number, render: (item: T, index: number) => string): string {\n const seen = new Set<string>();\n let html = `<!--meeko:for:${id}-->`;\n let index = 0;\n for (const item of items) {\n const key = keyToken(keyOf(item, index));\n if (seen.has(key)) throw new Error(`Duplicate Meeko list key ${key}`);\n seen.add(key);\n html += `<!--meeko:item:${id}:${key}-->${render(item, index)}<!--/meeko:item:${id}:${key}-->`;\n index++;\n }\n return `${html}<!--/meeko:for:${id}-->`;\n}\n\ninterface ListEntry<T> {\n key: string;\n start: Comment;\n end: Comment;\n value: Signal<T>;\n index: Signal<number> | undefined;\n release: () => void;\n}\n\nfunction moveRange(start: Comment, end: Comment, before: Node): void {\n const parent = before.parentNode;\n if (!parent) throw new Error(\"Detached Meeko list\");\n const document = before.ownerDocument;\n if (!document) throw new Error(\"Missing document for Meeko list\");\n const fragment = document.createDocumentFragment();\n for (let node: Node | null = start; node;) {\n const next: Node | null = node.nextSibling;\n fragment.appendChild(node);\n if (node === end) break;\n node = next;\n }\n parent.insertBefore(fragment, before);\n}\n\n/** Encuentra las posiciones que ya mantienen el orden relativo de la lista anterior. */\nfunction stableListPositions(indices: readonly number[]): Set<number> {\n const tails: number[] = [];\n const previous = new Int32Array(indices.length).fill(-1);\n for (let index = 0; index < indices.length; index++) {\n const oldIndex = indices[index]!;\n if (oldIndex < 0) continue;\n let low = 0;\n let high = tails.length;\n while (low < high) {\n const middle = (low + high) >>> 1;\n if (indices[tails[middle]!]! < oldIndex) low = middle + 1;\n else high = middle;\n }\n if (low > 0) previous[index] = tails[low - 1]!;\n tails[low] = index;\n }\n const stable = new Set<number>();\n for (let index = tails.at(-1) ?? -1; index >= 0; index = previous[index]!) stable.add(index);\n return stable;\n}\n\n/**\n * Hidrata una lista y reordena regiones por clave sin reemplazar nodos conservados.\n * El compilador desactiva `trackIndex` sólo cuando el cuerpo de hidratación no lee\n * la señal de índice; las llamadas directas conservan la señal por defecto.\n */\nexport function bindFor<T>(scope: DomScope, id: number, read: () => Iterable<T>, keyOf: (item: T, index: number) => string | number, render: (item: T, index: number) => string, hydrate: (item: Signal<T>, index: Signal<number>, inner: DomScope) => () => void, trackIndex = true): () => void {\n const outer = region(scope, \"for\", id);\n const entries = new Map<string, ListEntry<T>>();\n const initial = [...read()];\n let currentKeys: string[] = [];\n let cursor = outer.start!.nextSibling;\n for (let index = 0; index < initial.length; index++) {\n const item = initial[index]!;\n const key = keyToken(keyOf(item, index));\n if (entries.has(key)) throw new Error(`Duplicate Meeko list key ${key}`);\n const startData = `meeko:item:${id}:${key}`;\n const endData = `/meeko:item:${id}:${key}`;\n if (!cursor || cursor.nodeType !== 8 || (cursor as Comment).data !== startData) throw new Error(`Hydration mismatch in list ${id} at key ${key}`);\n const start = cursor as Comment;\n let end = start.nextSibling;\n while (end && (end.nodeType !== 8 || (end as Comment).data !== endData)) end = end.nextSibling;\n if (!end) throw new Error(`Missing end marker for list key ${key}`);\n const value = signal(item);\n const position = trackIndex ? signal(index) : undefined;\n const release = createRoot((dispose) => {\n const cleanup = hydrate(value, position as Signal<number>, { root: scope.root, start, end: end as Comment });\n return () => { cleanup(); dispose(); };\n });\n entries.set(key, { key, start, end: end as Comment, value, index: position, release });\n currentKeys.push(key);\n cursor = end.nextSibling;\n }\n if (cursor !== outer.end) throw new Error(`Hydration mismatch in list ${id}: extra nodes`);\n let initialItems: T[] | undefined = initial;\n const stop = effect(() => {\n const items = [...read()];\n if (initialItems) {\n const previous = initialItems;\n initialItems = undefined;\n if (items.length === previous.length && items.every((item, index) =>\n Object.is(item, previous[index]) && keyToken(keyOf(item, index)) === currentKeys[index])) return;\n }\n const nextKeys = items.map((item, index) => keyToken(keyOf(item, index)));\n const nextKeySet = new Set(nextKeys);\n if (nextKeySet.size !== nextKeys.length) throw new Error(`Duplicate Meeko list key in region ${id}`);\n const oldPositions = new Map(currentKeys.map((key, index) => [key, index]));\n const oldIndices = nextKeys.map((key) => oldPositions.get(key) ?? -1);\n const stable = stableListPositions(oldIndices);\n for (const [key, entry] of entries) {\n if (nextKeySet.has(key)) continue;\n entry.release();\n let node: Node | null = entry.start;\n while (node) {\n const next: Node | null = node.nextSibling;\n node.parentNode?.removeChild(node);\n if (node === entry.end) break;\n node = next;\n }\n entries.delete(key);\n }\n let before: Node = outer.end!;\n for (let index = items.length - 1; index >= 0; index--) {\n const item = items[index]!;\n const key = nextKeys[index]!;\n let entry = entries.get(key);\n if (!entry) {\n const html = `<!--meeko:item:${id}:${key}-->${render(item, index)}<!--/meeko:item:${id}:${key}-->`;\n insertHtml(before as Comment, html);\n const end = before.previousSibling as Comment;\n let start: Node | null = end.previousSibling;\n while (start && (start.nodeType !== 8 || (start as Comment).data !== `meeko:item:${id}:${key}`)) start = start.previousSibling;\n if (!start) throw new Error(`Missing new list marker for ${key}`);\n const value = signal(item);\n const position = trackIndex ? signal(index) : undefined;\n const release = createRoot((dispose) => {\n const cleanup = hydrate(value, position as Signal<number>, { root: scope.root, start: start as Comment, end });\n return () => { cleanup(); dispose(); };\n });\n entry = { key, start: start as Comment, end, value, index: position, release };\n entries.set(key, entry);\n } else {\n entry.value.value = item;\n if (entry.index) entry.index.value = index;\n }\n if (oldIndices[index] !== -1 && !stable.has(index) && entry.end.nextSibling !== before) moveRange(entry.start, entry.end, before);\n before = entry.start;\n }\n currentKeys = nextKeys;\n });\n return () => { stop(); for (const entry of entries.values()) entry.release(); entries.clear(); };\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CompileDiagnostic } from "../compiler/sfc.js";
|
|
2
|
+
/** Checks every discovered application component, including new pages and layouts. */
|
|
3
|
+
export declare function checkApp(projectRoot: string): Promise<CompileDiagnostic[]>;
|
|
4
|
+
/** Mirrors app component declarations into the project's virtual type tree. */
|
|
5
|
+
export declare function generateAppTypes(projectRoot: string): Promise<string[]>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { checkSource } from "../check.js";
|
|
4
|
+
import { generateTypes } from "../types.js";
|
|
5
|
+
import { resolveConfigSources } from "./config.js";
|
|
6
|
+
async function appMeekoFiles(projectRoot) {
|
|
7
|
+
const root = resolve(projectRoot);
|
|
8
|
+
const files = [];
|
|
9
|
+
async function walk(directory) {
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === "ENOENT")
|
|
16
|
+
return;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const path = join(directory, entry.name);
|
|
21
|
+
if (entry.isDirectory())
|
|
22
|
+
await walk(path);
|
|
23
|
+
else if (entry.isFile() && entry.name.endsWith(".meeko"))
|
|
24
|
+
files.push(path);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const source of await resolveConfigSources(root)) {
|
|
28
|
+
await walk(join(source.root, "app", "pages"));
|
|
29
|
+
await walk(join(source.root, "app", "components"));
|
|
30
|
+
for (const file of [join(source.root, "app", "app.meeko"), join(source.root, "app", "error.meeko")]) {
|
|
31
|
+
try {
|
|
32
|
+
await readFile(file);
|
|
33
|
+
files.push(file);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error.code !== "ENOENT")
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return files.sort();
|
|
42
|
+
}
|
|
43
|
+
/** Checks every discovered application component, including new pages and layouts. */
|
|
44
|
+
export async function checkApp(projectRoot) {
|
|
45
|
+
const diagnostics = [];
|
|
46
|
+
for (const file of await appMeekoFiles(projectRoot))
|
|
47
|
+
diagnostics.push(...await checkSource(await readFile(file, "utf8"), file));
|
|
48
|
+
return diagnostics;
|
|
49
|
+
}
|
|
50
|
+
/** Mirrors app component declarations into the project's virtual type tree. */
|
|
51
|
+
export async function generateAppTypes(projectRoot) {
|
|
52
|
+
const outputs = new Set();
|
|
53
|
+
for (const file of await appMeekoFiles(projectRoot)) {
|
|
54
|
+
for (const output of await generateTypes(await readFile(file, "utf8"), file))
|
|
55
|
+
outputs.add(output);
|
|
56
|
+
}
|
|
57
|
+
return [...outputs].sort();
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=check-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-app.js","sourceRoot":"","sources":["../../src/framework/check-app.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, readFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { checkSource } from \"../check.js\";\nimport type { CompileDiagnostic } from \"../compiler/sfc.js\";\nimport { generateTypes } from \"../types.js\";\nimport { resolveConfigSources } from \"./config.js\";\n\nasync function appMeekoFiles(projectRoot: string): Promise<string[]> {\n const root = resolve(projectRoot);\n const files: string[] = [];\n async function walk(directory: string): Promise<void> {\n let entries;\n try { entries = await readdir(directory, { withFileTypes: true }); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return; throw error; }\n for (const entry of entries) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) await walk(path);\n else if (entry.isFile() && entry.name.endsWith(\".meeko\")) files.push(path);\n }\n }\n for (const source of await resolveConfigSources(root)) {\n await walk(join(source.root, \"app\", \"pages\"));\n await walk(join(source.root, \"app\", \"components\"));\n for (const file of [join(source.root, \"app\", \"app.meeko\"), join(source.root, \"app\", \"error.meeko\")]) {\n try { await readFile(file); files.push(file); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error; }\n }\n }\n return files.sort();\n}\n\n/** Checks every discovered application component, including new pages and layouts. */\nexport async function checkApp(projectRoot: string): Promise<CompileDiagnostic[]> {\n const diagnostics: CompileDiagnostic[] = [];\n for (const file of await appMeekoFiles(projectRoot)) diagnostics.push(...await checkSource(await readFile(file, \"utf8\"), file));\n return diagnostics;\n}\n\n/** Mirrors app component declarations into the project's virtual type tree. */\nexport async function generateAppTypes(projectRoot: string): Promise<string[]> {\n const outputs = new Set<string>();\n for (const file of await appMeekoFiles(projectRoot)) {\n for (const output of await generateTypes(await readFile(file, \"utf8\"), file)) outputs.add(output);\n }\n return [...outputs].sort();\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DomScope } from "../dom.js";
|
|
2
|
+
import type { PageSchema } from "./page.js";
|
|
3
|
+
import type { NavigationContext, Navigator, RouteRecord } from "./router.js";
|
|
4
|
+
import { StateStore } from "./state.js";
|
|
5
|
+
/** The public shape emitted by the .meeko compiler. */
|
|
6
|
+
export interface ClientComponent {
|
|
7
|
+
renderWithState(props: Record<string, unknown>): {
|
|
8
|
+
html: string;
|
|
9
|
+
state: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
renderToString(props: Record<string, unknown>, state?: Record<string, unknown>): string;
|
|
12
|
+
hydrateScope(scope: DomScope, props: Record<string, unknown>, state?: Record<string, unknown>): () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface ClientRoute {
|
|
15
|
+
/** Client-safe loader. Server-only sidecars must not be passed to loadRoute. */
|
|
16
|
+
params?: PageSchema;
|
|
17
|
+
search?: PageSchema;
|
|
18
|
+
load?: (context: NavigationContext & {
|
|
19
|
+
params: Readonly<Record<string, unknown>>;
|
|
20
|
+
search: Readonly<Record<string, unknown>>;
|
|
21
|
+
state: StateStore;
|
|
22
|
+
}) => Promise<unknown> | unknown;
|
|
23
|
+
}
|
|
24
|
+
export interface ClientAppOptions {
|
|
25
|
+
readonly routes: readonly RouteRecord[];
|
|
26
|
+
readonly root: Element;
|
|
27
|
+
readonly payload: string | unknown;
|
|
28
|
+
readonly loadPage: (file: string) => Promise<ClientComponent>;
|
|
29
|
+
readonly loadRoute?: (file: string) => Promise<ClientRoute>;
|
|
30
|
+
/** Fetches public loader data from the SSR handler during browser navigation. */
|
|
31
|
+
readonly serverData?: boolean;
|
|
32
|
+
readonly fetch?: typeof fetch;
|
|
33
|
+
readonly onNavigationError?: (error: unknown) => void;
|
|
34
|
+
readonly browser?: Window;
|
|
35
|
+
}
|
|
36
|
+
export interface ClientApp {
|
|
37
|
+
navigate: Navigator["navigate"];
|
|
38
|
+
readonly state: StateStore;
|
|
39
|
+
dispose(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hydrates the SSR tree and updates only the first changed outlet on navigation.
|
|
43
|
+
* Layout templates render their outlet with <component is="props.outlet"/>.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mountClientApp(options: ClientAppOptions): Promise<ClientApp>;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { signal } from "../reactivity.js";
|
|
2
|
+
import { pageSearchObject, validatePageInputs } from "./page.js";
|
|
3
|
+
import { createNavigator, matchRoute } from "./router.js";
|
|
4
|
+
import { hydrateWithStateStore, StateStore, withStateStore } from "./state.js";
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function readPayload(value) {
|
|
9
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
10
|
+
if (!isRecord(parsed) || typeof parsed.route !== "string" || !isRecord(parsed.props) || !isRecord(parsed.state)) {
|
|
11
|
+
throw new TypeError("Invalid Meeko hydration payload");
|
|
12
|
+
}
|
|
13
|
+
if (!isRecord(parsed.props.params) || !isRecord(parsed.props.search))
|
|
14
|
+
throw new TypeError("Invalid Meeko route props");
|
|
15
|
+
if (parsed.sharedState !== undefined && !isRecord(parsed.sharedState))
|
|
16
|
+
throw new TypeError("Invalid Meeko shared state");
|
|
17
|
+
return { route: parsed.route, props: parsed.props, state: parsed.state, sharedState: parsed.sharedState ?? {} };
|
|
18
|
+
}
|
|
19
|
+
function bind(component, props) {
|
|
20
|
+
return {
|
|
21
|
+
renderWithState: () => component.renderWithState(props),
|
|
22
|
+
renderToString: () => component.renderToString(props),
|
|
23
|
+
hydrateScope: (scope, _unused, state) => component.hydrateScope(scope, props, state),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function createTree(route, props, loadPage, fromLayout) {
|
|
27
|
+
let component = bind(await loadPage(route.file), props);
|
|
28
|
+
const layouts = [];
|
|
29
|
+
for (let index = route.layouts.length - 1; index >= fromLayout; index--) {
|
|
30
|
+
const file = route.layouts[index];
|
|
31
|
+
const outlet = signal(component);
|
|
32
|
+
const layoutProps = Object.defineProperty({}, "outlet", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: () => outlet.value,
|
|
35
|
+
});
|
|
36
|
+
component = bind(await loadPage(file), layoutProps);
|
|
37
|
+
layouts.unshift({ file, outlet });
|
|
38
|
+
}
|
|
39
|
+
return { component, layouts };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hydrates the SSR tree and updates only the first changed outlet on navigation.
|
|
43
|
+
* Layout templates render their outlet with <component is="props.outlet"/>.
|
|
44
|
+
*/
|
|
45
|
+
export async function mountClientApp(options) {
|
|
46
|
+
const browser = options.browser ?? window;
|
|
47
|
+
const payload = readPayload(options.payload);
|
|
48
|
+
const initial = matchRoute(options.routes, browser.location.pathname);
|
|
49
|
+
if (!initial || initial.route.id !== payload.route)
|
|
50
|
+
throw new Error("Meeko route hydration mismatch");
|
|
51
|
+
if (JSON.stringify(initial.params) !== JSON.stringify(payload.props.params))
|
|
52
|
+
throw new Error("Meeko route parameters do not match SSR");
|
|
53
|
+
const stateStore = new StateStore(payload.sharedState);
|
|
54
|
+
const first = await createTree(initial.route, payload.props, options.loadPage, 0);
|
|
55
|
+
let layouts = first.layouts;
|
|
56
|
+
let release = hydrateWithStateStore(stateStore, () => first.component.hydrateScope({ root: options.root }, {}, payload.state));
|
|
57
|
+
let disposed = false;
|
|
58
|
+
const navigator = createNavigator(options.routes, async (context) => {
|
|
59
|
+
let props;
|
|
60
|
+
if (options.serverData && !options.loadRoute) {
|
|
61
|
+
const response = await (options.fetch ?? fetch)(context.url.href, {
|
|
62
|
+
headers: { accept: "application/meeko+json" }, credentials: "same-origin", signal: context.signal,
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok)
|
|
65
|
+
throw new Error(`Route data request failed: HTTP ${response.status}`);
|
|
66
|
+
const csrf = response.headers.get("x-meeko-csrf-token");
|
|
67
|
+
if (csrf !== null) {
|
|
68
|
+
const meta = browser.document.querySelector('meta[name="meeko-csrf-token"]');
|
|
69
|
+
if (csrf && meta)
|
|
70
|
+
meta.content = csrf;
|
|
71
|
+
else if (csrf) {
|
|
72
|
+
const next = browser.document.createElement("meta");
|
|
73
|
+
next.name = "meeko-csrf-token";
|
|
74
|
+
next.content = csrf;
|
|
75
|
+
browser.document.head.append(next);
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
meta?.remove();
|
|
79
|
+
}
|
|
80
|
+
const payload = readPayload(await response.text());
|
|
81
|
+
if (payload.route !== context.match.route.id)
|
|
82
|
+
throw new Error("Meeko route data mismatch");
|
|
83
|
+
if (context.signal.aborted || disposed)
|
|
84
|
+
return;
|
|
85
|
+
stateStore.seed(payload.sharedState);
|
|
86
|
+
props = payload.props;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const sidecar = context.match.route.sidecar && options.loadRoute
|
|
90
|
+
? await options.loadRoute(context.match.route.sidecar)
|
|
91
|
+
: undefined;
|
|
92
|
+
const validated = await validatePageInputs(sidecar, context.match.params, pageSearchObject(context.url.searchParams));
|
|
93
|
+
const data = sidecar?.load ? await sidecar.load({ ...context, ...validated, state: stateStore }) : undefined;
|
|
94
|
+
props = {
|
|
95
|
+
params: validated.params,
|
|
96
|
+
search: validated.search,
|
|
97
|
+
...(data === undefined ? {} : { data }),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (context.signal.aborted || disposed)
|
|
101
|
+
return;
|
|
102
|
+
const retained = Math.min(context.retainedLayouts, layouts.length);
|
|
103
|
+
const next = await createTree(context.match.route, props, options.loadPage, retained);
|
|
104
|
+
if (context.signal.aborted || disposed)
|
|
105
|
+
return;
|
|
106
|
+
if (retained) {
|
|
107
|
+
withStateStore(stateStore, () => { layouts[retained - 1].outlet.value = next.component; });
|
|
108
|
+
layouts = [...layouts.slice(0, retained), ...next.layouts];
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const rendered = withStateStore(stateStore, () => next.component.renderWithState({}));
|
|
112
|
+
release();
|
|
113
|
+
options.root.innerHTML = rendered.html;
|
|
114
|
+
release = hydrateWithStateStore(stateStore, () => next.component.hydrateScope({ root: options.root }, {}, rendered.state));
|
|
115
|
+
layouts = next.layouts;
|
|
116
|
+
}, browser);
|
|
117
|
+
const onClick = (event) => {
|
|
118
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
119
|
+
return;
|
|
120
|
+
const target = event.target;
|
|
121
|
+
if (!target || !("closest" in target) || typeof target.closest !== "function")
|
|
122
|
+
return;
|
|
123
|
+
const anchor = target.closest("a[href]");
|
|
124
|
+
if (!anchor || anchor.hasAttribute("download") || (anchor.target && anchor.target !== "_self"))
|
|
125
|
+
return;
|
|
126
|
+
const url = new URL(anchor.href, browser.location.href);
|
|
127
|
+
if (url.origin !== browser.location.origin || !matchRoute(options.routes, url.pathname))
|
|
128
|
+
return;
|
|
129
|
+
event.preventDefault();
|
|
130
|
+
void navigator.navigate(url.href).catch((error) => { options.onNavigationError?.(error); });
|
|
131
|
+
};
|
|
132
|
+
browser.document.addEventListener("click", onClick);
|
|
133
|
+
return {
|
|
134
|
+
navigate: navigator.navigate,
|
|
135
|
+
state: stateStore,
|
|
136
|
+
dispose() {
|
|
137
|
+
if (disposed)
|
|
138
|
+
return;
|
|
139
|
+
disposed = true;
|
|
140
|
+
navigator.dispose();
|
|
141
|
+
browser.document.removeEventListener("click", onClick);
|
|
142
|
+
release();
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/framework/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAmD/E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,MAAM,GAAY,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9E,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAChH,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACvH,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACzH,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;AAClH,CAAC;AAED,SAAS,IAAI,CAAC,SAA0B,EAAE,KAA8B;IACtE,OAAO;QACL,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,CAAC;QACvD,cAAc,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC;QACrD,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;KACrF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,KAAkB,EAClB,KAA8B,EAC9B,QAAsC,EACtC,UAAkB;IAElB,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAE,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE;YACtD,UAAU,EAAE,IAAI;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK;SACxB,CAA4B,CAAC;QAC9B,SAAS,GAAG,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;QACpD,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAyB;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC;IAC1C,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACtE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACtG,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACxI,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAClF,IAAI,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC5B,IAAI,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/H,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAClE,IAAI,KAA8B,CAAC;QACnC,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;gBAChE,OAAO,EAAE,EAAE,MAAM,EAAE,wBAAwB,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM;aAClG,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACxF,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YACxD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB,+BAA+B,CAAC,CAAC;gBAC9F,IAAI,IAAI,IAAI,IAAI;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;qBACjC,IAAI,IAAI,EAAE,CAAC;oBACd,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;oBACpD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;oBAC/B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;oBACpB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrC,CAAC;;oBAAM,IAAI,EAAE,MAAM,EAAE,CAAC;YACxB,CAAC;YACD,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,IAAI,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC3F,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,QAAQ;gBAAE,OAAO;YAC/C,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACrC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS;gBAC9D,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;gBACtD,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;YACtH,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC7G,KAAK,GAAG;gBACN,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;aACxC,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,QAAQ;YAAE,OAAO;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACtF,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,QAAQ;YAAE,OAAO;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAE,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;QACtF,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC;QACvC,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3H,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACzB,CAAC,EAAE,OAAO,CAAC,CAAC;IACZ,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAE,EAAE;QACpC,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO;QAC7H,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU;YAAE,OAAO;QACtF,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAA6B,CAAC;QACrE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC;YAAE,OAAO;QACvG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC;YAAE,OAAO;QAChG,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvG,CAAC,CAAC;IACF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACpD,OAAO;QACL,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,KAAK,EAAE,UAAU;QACjB,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { Component, DomScope } from \"../dom.js\";\nimport { signal } from \"../reactivity.js\";\nimport type { Signal } from \"../reactivity.js\";\nimport { pageSearchObject, validatePageInputs } from \"./page.js\";\nimport type { PageSchema } from \"./page.js\";\nimport { createNavigator, matchRoute } from \"./router.js\";\nimport type { NavigationContext, Navigator, RouteRecord } from \"./router.js\";\nimport { hydrateWithStateStore, StateStore, withStateStore } from \"./state.js\";\n\n/** The public shape emitted by the .meeko compiler. */\nexport interface ClientComponent {\n renderWithState(props: Record<string, unknown>): { html: string; state: Record<string, unknown> };\n renderToString(props: Record<string, unknown>, state?: Record<string, unknown>): string;\n hydrateScope(scope: DomScope, props: Record<string, unknown>, state?: Record<string, unknown>): () => void;\n}\n\nexport interface ClientRoute {\n /** Client-safe loader. Server-only sidecars must not be passed to loadRoute. */\n params?: PageSchema;\n search?: PageSchema;\n load?: (context: NavigationContext & {\n params: Readonly<Record<string, unknown>>;\n search: Readonly<Record<string, unknown>>;\n state: StateStore;\n }) => Promise<unknown> | unknown;\n}\n\nexport interface ClientAppOptions {\n readonly routes: readonly RouteRecord[];\n readonly root: Element;\n readonly payload: string | unknown;\n readonly loadPage: (file: string) => Promise<ClientComponent>;\n readonly loadRoute?: (file: string) => Promise<ClientRoute>;\n /** Fetches public loader data from the SSR handler during browser navigation. */\n readonly serverData?: boolean;\n readonly fetch?: typeof fetch;\n readonly onNavigationError?: (error: unknown) => void;\n readonly browser?: Window;\n}\n\nexport interface ClientApp {\n navigate: Navigator[\"navigate\"];\n readonly state: StateStore;\n dispose(): void;\n}\n\ninterface HydrationPayload {\n route: string;\n props: Record<string, unknown>;\n state: Record<string, unknown>;\n sharedState: Record<string, unknown>;\n}\n\ninterface LayoutInstance {\n file: string;\n outlet: Signal<Component<Record<string, unknown>>>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readPayload(value: unknown): HydrationPayload {\n const parsed: unknown = typeof value === \"string\" ? JSON.parse(value) : value;\n if (!isRecord(parsed) || typeof parsed.route !== \"string\" || !isRecord(parsed.props) || !isRecord(parsed.state)) {\n throw new TypeError(\"Invalid Meeko hydration payload\");\n }\n if (!isRecord(parsed.props.params) || !isRecord(parsed.props.search)) throw new TypeError(\"Invalid Meeko route props\");\n if (parsed.sharedState !== undefined && !isRecord(parsed.sharedState)) throw new TypeError(\"Invalid Meeko shared state\");\n return { route: parsed.route, props: parsed.props, state: parsed.state, sharedState: parsed.sharedState ?? {} };\n}\n\nfunction bind(component: ClientComponent, props: Record<string, unknown>): Component<Record<string, unknown>> {\n return {\n renderWithState: () => component.renderWithState(props),\n renderToString: () => component.renderToString(props),\n hydrateScope: (scope, _unused, state) => component.hydrateScope(scope, props, state),\n };\n}\n\nasync function createTree(\n route: RouteRecord,\n props: Record<string, unknown>,\n loadPage: ClientAppOptions[\"loadPage\"],\n fromLayout: number,\n): Promise<{ component: Component<Record<string, unknown>>; layouts: LayoutInstance[] }> {\n let component = bind(await loadPage(route.file), props);\n const layouts: LayoutInstance[] = [];\n for (let index = route.layouts.length - 1; index >= fromLayout; index--) {\n const file = route.layouts[index]!;\n const outlet = signal(component);\n const layoutProps = Object.defineProperty({}, \"outlet\", {\n enumerable: true,\n get: () => outlet.value,\n }) as Record<string, unknown>;\n component = bind(await loadPage(file), layoutProps);\n layouts.unshift({ file, outlet });\n }\n return { component, layouts };\n}\n\n/**\n * Hydrates the SSR tree and updates only the first changed outlet on navigation.\n * Layout templates render their outlet with <component is=\"props.outlet\"/>.\n */\nexport async function mountClientApp(options: ClientAppOptions): Promise<ClientApp> {\n const browser = options.browser ?? window;\n const payload = readPayload(options.payload);\n const initial = matchRoute(options.routes, browser.location.pathname);\n if (!initial || initial.route.id !== payload.route) throw new Error(\"Meeko route hydration mismatch\");\n if (JSON.stringify(initial.params) !== JSON.stringify(payload.props.params)) throw new Error(\"Meeko route parameters do not match SSR\");\n const stateStore = new StateStore(payload.sharedState);\n const first = await createTree(initial.route, payload.props, options.loadPage, 0);\n let layouts = first.layouts;\n let release = hydrateWithStateStore(stateStore, () => first.component.hydrateScope({ root: options.root }, {}, payload.state));\n let disposed = false;\n const navigator = createNavigator(options.routes, async (context) => {\n let props: Record<string, unknown>;\n if (options.serverData && !options.loadRoute) {\n const response = await (options.fetch ?? fetch)(context.url.href, {\n headers: { accept: \"application/meeko+json\" }, credentials: \"same-origin\", signal: context.signal,\n });\n if (!response.ok) throw new Error(`Route data request failed: HTTP ${response.status}`);\n const csrf = response.headers.get(\"x-meeko-csrf-token\");\n if (csrf !== null) {\n const meta = browser.document.querySelector<HTMLMetaElement>('meta[name=\"meeko-csrf-token\"]');\n if (csrf && meta) meta.content = csrf;\n else if (csrf) {\n const next = browser.document.createElement(\"meta\");\n next.name = \"meeko-csrf-token\";\n next.content = csrf;\n browser.document.head.append(next);\n } else meta?.remove();\n }\n const payload = readPayload(await response.text());\n if (payload.route !== context.match.route.id) throw new Error(\"Meeko route data mismatch\");\n if (context.signal.aborted || disposed) return;\n stateStore.seed(payload.sharedState);\n props = payload.props;\n } else {\n const sidecar = context.match.route.sidecar && options.loadRoute\n ? await options.loadRoute(context.match.route.sidecar)\n : undefined;\n const validated = await validatePageInputs(sidecar, context.match.params, pageSearchObject(context.url.searchParams));\n const data = sidecar?.load ? await sidecar.load({ ...context, ...validated, state: stateStore }) : undefined;\n props = {\n params: validated.params,\n search: validated.search,\n ...(data === undefined ? {} : { data }),\n };\n }\n if (context.signal.aborted || disposed) return;\n const retained = Math.min(context.retainedLayouts, layouts.length);\n const next = await createTree(context.match.route, props, options.loadPage, retained);\n if (context.signal.aborted || disposed) return;\n if (retained) {\n withStateStore(stateStore, () => { layouts[retained - 1]!.outlet.value = next.component; });\n layouts = [...layouts.slice(0, retained), ...next.layouts];\n return;\n }\n const rendered = withStateStore(stateStore, () => next.component.renderWithState({}));\n release();\n options.root.innerHTML = rendered.html;\n release = hydrateWithStateStore(stateStore, () => next.component.hydrateScope({ root: options.root }, {}, rendered.state));\n layouts = next.layouts;\n }, browser);\n const onClick = (event: MouseEvent) => {\n if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n const target = event.target;\n if (!target || !(\"closest\" in target) || typeof target.closest !== \"function\") return;\n const anchor = target.closest(\"a[href]\") as HTMLAnchorElement | null;\n if (!anchor || anchor.hasAttribute(\"download\") || (anchor.target && anchor.target !== \"_self\")) return;\n const url = new URL(anchor.href, browser.location.href);\n if (url.origin !== browser.location.origin || !matchRoute(options.routes, url.pathname)) return;\n event.preventDefault();\n void navigator.navigate(url.href).catch((error: unknown) => { options.onNavigationError?.(error); });\n };\n browser.document.addEventListener(\"click\", onClick);\n return {\n navigate: navigator.navigate,\n state: stateStore,\n dispose() {\n if (disposed) return;\n disposed = true;\n navigator.dispose();\n browser.document.removeEventListener(\"click\", onClick);\n release();\n },\n };\n}\n"]}
|