@meekojs/meeko 0.0.0-stage → 0.0.2

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/dist/dom.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import type { Signal } from "./reactivity.js";
2
+ /** Fragmento de DOM que pertenece a una instancia o a una región dinámica. */
3
+ export interface DomScope {
4
+ root: Element;
5
+ start?: Comment;
6
+ end?: Comment;
7
+ }
8
+ /** Contrato de un módulo de componente compilado para composición sin wrapper. */
9
+ export interface ComponentModule<Props extends object> {
10
+ renderToString(props: Props, state?: Record<string, unknown>): string;
11
+ renderWithState?(props: Props): {
12
+ html: string;
13
+ state: Record<string, unknown>;
14
+ };
15
+ hydrateScope(scope: DomScope, props: Props, state?: Record<string, unknown>): ComponentRelease;
16
+ }
17
+ /** Función de limpieza que puede exponer el estado editable de su instancia. */
18
+ export interface ComponentRelease {
19
+ (): void;
20
+ snapshot?: () => Record<string, unknown>;
21
+ }
22
+ /** Añade una lectura de estado a la limpieza de un componente compilado. */
23
+ export declare function withStateSnapshot(release: () => void, snapshot: () => Record<string, unknown>): ComponentRelease;
24
+ /** Renderiza un hijo entre comentarios de ámbito sin añadir un elemento visual. */
25
+ export declare function renderComponent<Props extends object>(id: number, component: ComponentModule<Props>, props: Props, transferState?: boolean): string;
26
+ /** 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, component: ComponentModule<Props>, read: () => Props, location: string): () => void;
28
+ /** 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;
30
+ /** 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;
32
+ /** Conecta un evento al elemento SSR y entrega su función de limpieza. */
33
+ export declare function bindEvent(scope: DomScope, id: number, name: string, handler: (event: Event) => void): () => void;
34
+ /** Genera los límites estables de una condición en SSR. */
35
+ export declare function renderIf(id: number, condition: unknown, render: () => string): string;
36
+ /** Hidrata una condición y conserva los nodos mientras sigue siendo verdadera. */
37
+ export declare function bindIf(scope: DomScope, id: number, read: () => unknown, render: () => string, hydrate: (inner: DomScope) => () => void): () => void;
38
+ /** Convierte una clave de lista en un token seguro para comentarios HTML. */
39
+ export declare function keyToken(key: unknown): string;
40
+ /** Renderiza una lista con límites identificados por claves únicas. */
41
+ 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
+ /** Hidrata una lista y reordena regiones por clave sin reemplazar nodos conservados. */
43
+ 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): () => void;
package/dist/dom.js ADDED
@@ -0,0 +1,315 @@
1
+ import { createRoot, effect, signal } from "./reactivity.js";
2
+ import { attributeValue, serializeJson } from "./server.js";
3
+ /** Añade una lectura de estado a la limpieza de un componente compilado. */
4
+ export function withStateSnapshot(release, snapshot) {
5
+ return Object.assign(release, { snapshot });
6
+ }
7
+ const componentStart = /^meeko:component:\d+(?::.*)?$/;
8
+ function componentEnd(start) {
9
+ let depth = 0;
10
+ for (let node = start; node; node = node.nextSibling) {
11
+ if (node.nodeType !== 8)
12
+ continue;
13
+ const data = node.data;
14
+ if (componentStart.test(data))
15
+ depth++;
16
+ else if (/^\/meeko:component:\d+$/.test(data) && --depth === 0)
17
+ return node;
18
+ }
19
+ throw new Error(`Missing end marker for ${start.data}`);
20
+ }
21
+ function visit(scope, callback) {
22
+ function siblings(first, stop = null) {
23
+ for (let node = first; node && node !== stop;) {
24
+ if (callback(node))
25
+ return node;
26
+ if (node.nodeType === 8 && componentStart.test(node.data)) {
27
+ const end = componentEnd(node);
28
+ if (callback(end))
29
+ return end;
30
+ node = end.nextSibling;
31
+ continue;
32
+ }
33
+ const found = siblings(node.firstChild);
34
+ if (found)
35
+ return found;
36
+ node = node.nextSibling;
37
+ }
38
+ return undefined;
39
+ }
40
+ return siblings(scope.start?.nextSibling ?? scope.root.firstChild, scope.end ?? null);
41
+ }
42
+ function findComment(scope, data) {
43
+ const node = visit(scope, (candidate) => candidate.nodeType === 8 && candidate.data === data);
44
+ if (!node)
45
+ throw new Error(`Missing Meeko marker ${data}`);
46
+ return node;
47
+ }
48
+ function findElement(scope, name) {
49
+ const node = visit(scope, (candidate) => candidate.nodeType === 1 && candidate.hasAttribute(name));
50
+ if (!node)
51
+ throw new Error(`Missing Meeko target ${name}`);
52
+ return node;
53
+ }
54
+ function region(scope, kind, id) {
55
+ const start = findComment(scope, `meeko:${kind}:${id}`);
56
+ const end = findComment(scope, `/meeko:${kind}:${id}`);
57
+ if (start.parentNode !== end.parentNode)
58
+ throw new Error(`Invalid Meeko region ${kind}:${id}`);
59
+ return { root: scope.root, start, end };
60
+ }
61
+ function clearRegion(scope) {
62
+ if (!scope.start || !scope.end)
63
+ throw new Error("Expected a bounded Meeko region");
64
+ for (let node = scope.start.nextSibling; node && node !== scope.end;) {
65
+ const next = node.nextSibling;
66
+ node.parentNode?.removeChild(node);
67
+ node = next;
68
+ }
69
+ }
70
+ function insertHtml(end, html) {
71
+ const parent = end.parentNode;
72
+ if (!parent)
73
+ throw new Error("Detached Meeko region");
74
+ const range = end.ownerDocument.createRange();
75
+ range.selectNodeContents(parent);
76
+ parent.insertBefore(range.createContextualFragment(html), end);
77
+ }
78
+ function sameProps(first, second) {
79
+ const firstKeys = Object.keys(first);
80
+ const secondKeys = Object.keys(second);
81
+ return firstKeys.length === secondKeys.length && firstKeys.every((key) => Object.hasOwn(second, key) && Object.is(first[key], second[key]));
82
+ }
83
+ /** Renderiza un hijo entre comentarios de ámbito sin añadir un elemento visual. */
84
+ export function renderComponent(id, component, props, transferState = false) {
85
+ if (transferState) {
86
+ if (!component.renderWithState)
87
+ throw new TypeError(`Component ${id} cannot transfer SSR state`);
88
+ const rendered = component.renderWithState(props);
89
+ return `<!--meeko:component:${id}:${encodeURIComponent(serializeJson(rendered.state))}-->${rendered.html}<!--/meeko:component:${id}-->`;
90
+ }
91
+ return `<!--meeko:component:${id}-->${component.renderToString(props)}<!--/meeko:component:${id}-->`;
92
+ }
93
+ function componentRegion(scope, id) {
94
+ const prefix = `meeko:component:${id}`;
95
+ const start = visit(scope, (candidate) => candidate.nodeType === 8 && (candidate.data === prefix || candidate.data.startsWith(`${prefix}:`)));
96
+ if (!start)
97
+ throw new Error(`Missing Meeko marker ${prefix}`);
98
+ const end = findComment(scope, `/meeko:component:${id}`);
99
+ if (start.parentNode !== end.parentNode)
100
+ throw new Error(`Invalid Meeko region component:${id}`);
101
+ const encoded = start.data.slice(prefix.length);
102
+ if (!encoded)
103
+ return { inner: { root: scope.root, start, end } };
104
+ const parsed = JSON.parse(decodeURIComponent(encoded.slice(1)));
105
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
106
+ throw new TypeError(`Invalid Meeko state for component:${id}`);
107
+ return { inner: { root: scope.root, start, end }, state: parsed };
108
+ }
109
+ /** Hidrata un hijo dentro de su rango y actualiza props reactivas con limpieza previa. */
110
+ export function bindComponent(scope, id, component, read, location) {
111
+ let inner;
112
+ let state;
113
+ try {
114
+ ({ inner, state } = componentRegion(scope, id));
115
+ }
116
+ catch (error) {
117
+ throw new Error(`Hydration mismatch in ${location}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
118
+ }
119
+ let props = read();
120
+ let release = component.hydrateScope(inner, props, state);
121
+ const stop = effect(() => {
122
+ const next = read();
123
+ if (sameProps(props, next))
124
+ return;
125
+ const state = release.snapshot?.();
126
+ const html = component.renderToString(next, state);
127
+ release();
128
+ clearRegion(inner);
129
+ insertHtml(inner.end, html);
130
+ release = component.hydrateScope(inner, next, state);
131
+ props = next;
132
+ });
133
+ return () => { stop(); release(); };
134
+ }
135
+ /** Conecta un texto reactivo al marcador SSR y comprueba su valor inicial. */
136
+ export function bindText(scope, id, read, location) {
137
+ const marker = findComment(scope, `meeko:text:${id}`);
138
+ const text = marker.nextSibling;
139
+ if (!text || text.nodeType !== 3)
140
+ throw new Error(`Missing text node in ${location}`);
141
+ if (text.textContent !== String(read()))
142
+ throw new Error(`Hydration mismatch in ${location}`);
143
+ return effect(() => { text.textContent = String(read()); });
144
+ }
145
+ /** Conecta un atributo dinámico al elemento SSR y comprueba su valor inicial. */
146
+ export function bindAttribute(scope, id, name, read, location) {
147
+ const target = findElement(scope, `data-meeko-a-${id}`);
148
+ if (target.getAttribute(name) !== attributeValue(name, read()))
149
+ throw new Error(`Hydration mismatch in ${location}`);
150
+ return effect(() => {
151
+ const next = attributeValue(name, read());
152
+ if (next === null)
153
+ target.removeAttribute(name);
154
+ else
155
+ target.setAttribute(name, next);
156
+ });
157
+ }
158
+ /** Conecta un evento al elemento SSR y entrega su función de limpieza. */
159
+ export function bindEvent(scope, id, name, handler) {
160
+ const target = findElement(scope, `data-meeko-e-${id}`);
161
+ target.addEventListener(name, handler);
162
+ return () => target.removeEventListener(name, handler);
163
+ }
164
+ /** Genera los límites estables de una condición en SSR. */
165
+ export function renderIf(id, condition, render) {
166
+ return `<!--meeko:if:${id}-->${condition ? render() : ""}<!--/meeko:if:${id}-->`;
167
+ }
168
+ /** Hidrata una condición y conserva los nodos mientras sigue siendo verdadera. */
169
+ export function bindIf(scope, id, read, render, hydrate) {
170
+ const inner = region(scope, "if", id);
171
+ let active = Boolean(read());
172
+ if (!active && inner.start?.nextSibling !== inner.end)
173
+ throw new Error(`Hydration mismatch in condition ${id}`);
174
+ let release = active ? hydrate(inner) : undefined;
175
+ const stop = effect(() => {
176
+ const next = Boolean(read());
177
+ if (next === active)
178
+ return;
179
+ release?.();
180
+ release = undefined;
181
+ clearRegion(inner);
182
+ if (next) {
183
+ insertHtml(inner.end, render());
184
+ release = hydrate(inner);
185
+ }
186
+ active = next;
187
+ });
188
+ return () => { stop(); release?.(); };
189
+ }
190
+ /** Convierte una clave de lista en un token seguro para comentarios HTML. */
191
+ export function keyToken(key) {
192
+ if (typeof key === "number" && Number.isFinite(key))
193
+ return `n${encodeURIComponent(String(key)).replaceAll("-", "%2D")}`;
194
+ if (typeof key === "string")
195
+ return `s${encodeURIComponent(key).replaceAll("-", "%2D")}`;
196
+ throw new TypeError("Meeko list keys must be strings or finite numbers");
197
+ }
198
+ /** Renderiza una lista con límites identificados por claves únicas. */
199
+ export function renderFor(id, items, keyOf, render) {
200
+ const seen = new Set();
201
+ let html = `<!--meeko:for:${id}-->`;
202
+ let index = 0;
203
+ for (const item of items) {
204
+ const key = keyToken(keyOf(item, index));
205
+ if (seen.has(key))
206
+ throw new Error(`Duplicate Meeko list key ${key}`);
207
+ seen.add(key);
208
+ html += `<!--meeko:item:${id}:${key}-->${render(item, index)}<!--/meeko:item:${id}:${key}-->`;
209
+ index++;
210
+ }
211
+ return `${html}<!--/meeko:for:${id}-->`;
212
+ }
213
+ function moveRange(start, end, before) {
214
+ const parent = before.parentNode;
215
+ if (!parent)
216
+ throw new Error("Detached Meeko list");
217
+ const document = before.ownerDocument;
218
+ if (!document)
219
+ throw new Error("Missing document for Meeko list");
220
+ const fragment = document.createDocumentFragment();
221
+ for (let node = start; node;) {
222
+ const next = node.nextSibling;
223
+ fragment.appendChild(node);
224
+ if (node === end)
225
+ break;
226
+ node = next;
227
+ }
228
+ parent.insertBefore(fragment, before);
229
+ }
230
+ /** Hidrata una lista y reordena regiones por clave sin reemplazar nodos conservados. */
231
+ export function bindFor(scope, id, read, keyOf, render, hydrate) {
232
+ const outer = region(scope, "for", id);
233
+ const entries = new Map();
234
+ const initial = [...read()];
235
+ let cursor = outer.start.nextSibling;
236
+ for (let index = 0; index < initial.length; index++) {
237
+ const item = initial[index];
238
+ const key = keyToken(keyOf(item, index));
239
+ if (entries.has(key))
240
+ throw new Error(`Duplicate Meeko list key ${key}`);
241
+ const startData = `meeko:item:${id}:${key}`;
242
+ const endData = `/meeko:item:${id}:${key}`;
243
+ if (!cursor || cursor.nodeType !== 8 || cursor.data !== startData)
244
+ throw new Error(`Hydration mismatch in list ${id} at key ${key}`);
245
+ const start = cursor;
246
+ let end = start.nextSibling;
247
+ while (end && (end.nodeType !== 8 || end.data !== endData))
248
+ end = end.nextSibling;
249
+ if (!end)
250
+ throw new Error(`Missing end marker for list key ${key}`);
251
+ const value = signal(item);
252
+ const position = signal(index);
253
+ const release = createRoot((dispose) => {
254
+ const cleanup = hydrate(value, position, { root: scope.root, start, end: end });
255
+ return () => { cleanup(); dispose(); };
256
+ });
257
+ entries.set(key, { key, start, end: end, value, index: position, release });
258
+ cursor = end.nextSibling;
259
+ }
260
+ if (cursor !== outer.end)
261
+ throw new Error(`Hydration mismatch in list ${id}: extra nodes`);
262
+ const stop = effect(() => {
263
+ const items = [...read()];
264
+ const nextKeys = items.map((item, index) => keyToken(keyOf(item, index)));
265
+ if (new Set(nextKeys).size !== nextKeys.length)
266
+ throw new Error(`Duplicate Meeko list key in region ${id}`);
267
+ for (const [key, entry] of entries) {
268
+ if (nextKeys.includes(key))
269
+ continue;
270
+ entry.release();
271
+ let node = entry.start;
272
+ while (node) {
273
+ const next = node.nextSibling;
274
+ node.parentNode?.removeChild(node);
275
+ if (node === entry.end)
276
+ break;
277
+ node = next;
278
+ }
279
+ entries.delete(key);
280
+ }
281
+ let before = outer.end;
282
+ for (let index = items.length - 1; index >= 0; index--) {
283
+ const item = items[index];
284
+ const key = nextKeys[index];
285
+ let entry = entries.get(key);
286
+ if (!entry) {
287
+ const html = `<!--meeko:item:${id}:${key}-->${render(item, index)}<!--/meeko:item:${id}:${key}-->`;
288
+ insertHtml(before, html);
289
+ const end = before.previousSibling;
290
+ let start = end.previousSibling;
291
+ while (start && (start.nodeType !== 8 || start.data !== `meeko:item:${id}:${key}`))
292
+ start = start.previousSibling;
293
+ if (!start)
294
+ throw new Error(`Missing new list marker for ${key}`);
295
+ const value = signal(item);
296
+ const position = signal(index);
297
+ const release = createRoot((dispose) => {
298
+ const cleanup = hydrate(value, position, { root: scope.root, start: start, end });
299
+ return () => { cleanup(); dispose(); };
300
+ });
301
+ entry = { key, start: start, end, value, index: position, release };
302
+ entries.set(key, entry);
303
+ }
304
+ else {
305
+ entry.value.value = item;
306
+ entry.index.value = index;
307
+ }
308
+ if (entry.end.nextSibling !== before)
309
+ moveRange(entry.start, entry.end, before);
310
+ before = entry.start;
311
+ }
312
+ });
313
+ return () => { stop(); for (const entry of entries.values())
314
+ entry.release(); entries.clear(); };
315
+ }
package/dist/hmr.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { DomScope } from "./dom.js";
2
+ /** Valores de señales transferidos en memoria entre versiones de un componente. */
3
+ export type HotState = Record<string, unknown>;
4
+ /** Contrato mínimo de un módulo Meeko reemplazable en desarrollo. */
5
+ export interface HotComponent<Props = unknown> {
6
+ renderToString(props: Props, state?: HotState): string;
7
+ hydrate(root: Element, props: Props, state?: HotState): () => void;
8
+ hydrateScope?(scope: DomScope, props: Props, state?: HotState): () => void;
9
+ css?: string;
10
+ }
11
+ /** Copia solo las señales editables de un setup, sin serializarlas. */
12
+ export declare function snapshotSignals(bindings: Record<string, unknown>): HotState;
13
+ /** Restaura señales compatibles antes de renderizar o hidratar la nueva versión. */
14
+ export declare function restoreSignals(bindings: Record<string, unknown>, state?: HotState): void;
15
+ /** Activa el seguimiento de instancias solo para módulos servidos por Vite. */
16
+ export declare function enableHotModule(id: string): void;
17
+ /** Registra una raíz hidratada y elimina el registro al liberarla. */
18
+ export declare function registerHotInstance<Props>(id: string, root: Element, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void;
19
+ /** Registra un componente hijo delimitado por comentarios para actualizarlo sin tocar al padre. */
20
+ export declare function registerHotScope<Props>(id: string, scope: DomScope, props: Props, component: HotComponent<Props>, snapshot: () => HotState, dispose: () => void): () => void;
21
+ /** Aplica CSS nuevo después de la hoja virtual cargada por Vite. */
22
+ export declare function replaceHotStyle(id: string, css: string): void;
23
+ /** Sustituye las instancias activas sin recargar la página y conserva señales editables. */
24
+ export declare function replaceHotModule(id: string, next: unknown): void;
package/dist/hmr.js ADDED
@@ -0,0 +1,137 @@
1
+ import { isWritableSignal } from "./reactivity.js";
2
+ const enabled = new Set();
3
+ const instances = new Map();
4
+ /** Copia solo las señales editables de un setup, sin serializarlas. */
5
+ export function snapshotSignals(bindings) {
6
+ const state = {};
7
+ for (const [name, value] of Object.entries(bindings)) {
8
+ if (isWritableSignal(value))
9
+ state[name] = value.value;
10
+ }
11
+ return state;
12
+ }
13
+ /** Restaura señales compatibles antes de renderizar o hidratar la nueva versión. */
14
+ export function restoreSignals(bindings, state) {
15
+ if (!state)
16
+ return;
17
+ for (const [name, value] of Object.entries(state)) {
18
+ const target = bindings[name];
19
+ if (isWritableSignal(target))
20
+ target.value = value;
21
+ }
22
+ }
23
+ /** Activa el seguimiento de instancias solo para módulos servidos por Vite. */
24
+ export function enableHotModule(id) {
25
+ enabled.add(id);
26
+ }
27
+ function registerHotTarget(id, key, target, props, component, snapshot, dispose) {
28
+ if (!enabled.has(id))
29
+ return dispose;
30
+ const entry = { target, props, component: component, snapshot, dispose };
31
+ let group = instances.get(id);
32
+ if (!group) {
33
+ group = new Map();
34
+ instances.set(id, group);
35
+ }
36
+ const existing = group.get(key);
37
+ if (existing) {
38
+ existing.current = entry;
39
+ return existing.stop;
40
+ }
41
+ const slot = {
42
+ current: entry,
43
+ stop: () => {
44
+ if (group.get(key) !== slot)
45
+ return;
46
+ group.delete(key);
47
+ if (group.size === 0)
48
+ instances.delete(id);
49
+ slot.current.dispose();
50
+ },
51
+ };
52
+ group.set(key, slot);
53
+ return slot.stop;
54
+ }
55
+ /** Registra una raíz hidratada y elimina el registro al liberarla. */
56
+ export function registerHotInstance(id, root, props, component, snapshot, dispose) {
57
+ return registerHotTarget(id, root, { kind: "root", root }, props, component, snapshot, dispose);
58
+ }
59
+ /** Registra un componente hijo delimitado por comentarios para actualizarlo sin tocar al padre. */
60
+ export function registerHotScope(id, scope, props, component, snapshot, dispose) {
61
+ if (!scope.start || !scope.end)
62
+ throw new Error("Hot child component requires a bounded scope");
63
+ return registerHotTarget(id, scope.start, { kind: "scope", scope }, props, component, snapshot, dispose);
64
+ }
65
+ function scopeRange(scope) {
66
+ if (!scope.start || !scope.end)
67
+ throw new Error("Hot child component lost its markers");
68
+ const range = scope.start.ownerDocument.createRange();
69
+ range.setStartAfter(scope.start);
70
+ range.setEndBefore(scope.end);
71
+ return range;
72
+ }
73
+ function replaceScope(scope, html) {
74
+ const range = scopeRange(scope);
75
+ const content = range.createContextualFragment(html);
76
+ range.deleteContents();
77
+ range.insertNode(content);
78
+ }
79
+ /** Aplica CSS nuevo después de la hoja virtual cargada por Vite. */
80
+ export function replaceHotStyle(id, css) {
81
+ if (typeof document === "undefined")
82
+ return;
83
+ let style = document.querySelector(`style[data-meeko-hot="${id}"]`);
84
+ if (!style) {
85
+ style = document.createElement("style");
86
+ style.dataset.meekoHot = id;
87
+ document.head.appendChild(style);
88
+ }
89
+ style.textContent = css;
90
+ }
91
+ /** Sustituye las instancias activas sin recargar la página y conserva señales editables. */
92
+ export function replaceHotModule(id, next) {
93
+ if (typeof next !== "object" || next === null || !("renderToString" in next) || !("hydrate" in next)
94
+ || typeof next.renderToString !== "function" || typeof next.hydrate !== "function") {
95
+ throw new TypeError("Invalid Meeko hot module");
96
+ }
97
+ const component = next;
98
+ const prepared = [...(instances.get(id)?.values() ?? [])].map((slot) => {
99
+ const entry = slot.current;
100
+ const state = entry.snapshot();
101
+ 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 };
103
+ });
104
+ for (const { slot, entry, state, html, previous } of prepared) {
105
+ entry.dispose();
106
+ if (entry.target.kind === "root")
107
+ entry.target.root.innerHTML = html;
108
+ else
109
+ replaceScope(entry.target.scope, html);
110
+ try {
111
+ if (entry.target.kind === "root")
112
+ component.hydrate(entry.target.root, entry.props, state);
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);
117
+ }
118
+ if (slot.current === entry)
119
+ throw new Error("Updated Meeko component did not register its hydrated instance");
120
+ }
121
+ catch (error) {
122
+ if (entry.target.kind === "root")
123
+ entry.target.root.innerHTML = previous;
124
+ else {
125
+ const range = scopeRange(entry.target.scope);
126
+ range.deleteContents();
127
+ range.insertNode(previous);
128
+ }
129
+ if (entry.target.kind === "root")
130
+ entry.component.hydrate(entry.target.root, entry.props, state);
131
+ else
132
+ entry.component.hydrateScope?.(entry.target.scope, entry.props, state);
133
+ throw error;
134
+ }
135
+ }
136
+ replaceHotStyle(id, component.css ?? "");
137
+ }
@@ -0,0 +1,9 @@
1
+ export { batch, computed, createRoot, effect, signal, untrack } from "./reactivity.js";
2
+ export type { ReadonlySignal, Signal } from "./reactivity.js";
3
+ export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
4
+ export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
5
+ export type { ComponentModule, ComponentRelease, DomScope } from "./dom.js";
6
+ export { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from "./hmr.js";
7
+ export type { HotComponent, HotState } from "./hmr.js";
8
+ export { registerDevtoolsInstance } from "./devtools.js";
9
+ export type { DevtoolsEvent, DevtoolsHook, DevtoolsInstance, DevtoolsRegistration, DevtoolsSnapshot, DevtoolsValue } from "./devtools.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { batch, computed, createRoot, effect, signal, untrack } from "./reactivity.js";
2
+ export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
3
+ export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
4
+ export { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from "./hmr.js";
5
+ export { registerDevtoolsInstance } from "./devtools.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Contrato del runtime generado por el compilador Meeko.
3
+ *
4
+ * Esta ruta es interna y puede cambiar junto con el compilador. Las aplicaciones
5
+ * deben importar las API documentadas desde la raíz del paquete.
6
+ */
7
+ export { createRoot } from "./reactivity.js";
8
+ export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
9
+ export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
10
+ export type { ComponentModule, ComponentRelease, DomScope } from "./dom.js";
11
+ export { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from "./hmr.js";
12
+ export type { HotComponent, HotState } from "./hmr.js";
13
+ export { registerDevtoolsInstance } from "./devtools.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Contrato del runtime generado por el compilador Meeko.
3
+ *
4
+ * Esta ruta es interna y puede cambiar junto con el compilador. Las aplicaciones
5
+ * deben importar las API documentadas desde la raíz del paquete.
6
+ */
7
+ export { createRoot } from "./reactivity.js";
8
+ export { attributeValue, escapeHtml, renderAttribute, serializeJson } from "./server.js";
9
+ export { bindAttribute, bindComponent, bindEvent, bindFor, bindIf, bindText, keyToken, renderComponent, renderFor, renderIf, withStateSnapshot } from "./dom.js";
10
+ export { enableHotModule, registerHotInstance, registerHotScope, replaceHotModule, replaceHotStyle, restoreSignals, snapshotSignals } from "./hmr.js";
11
+ export { registerDevtoolsInstance } from "./devtools.js";
@@ -0,0 +1,22 @@
1
+ /** Señal de solo lectura que registra dependencias al consultar su valor. */
2
+ export interface ReadonlySignal<T> {
3
+ readonly value: T;
4
+ }
5
+ /** Señal cuyo valor puede actualizarse de forma reactiva. */
6
+ export interface Signal<T> extends ReadonlySignal<T> {
7
+ value: T;
8
+ }
9
+ /** Identifica señales editables creadas por Meeko para conservarlas durante HMR. */
10
+ export declare function isWritableSignal(value: unknown): value is Signal<unknown>;
11
+ /** Crea un valor observable. Las escrituras iguales no notifican cambios. */
12
+ export declare function signal<T>(initial: T): Signal<T>;
13
+ /** Ejecuta una función cuando cambian las señales leídas por ella. */
14
+ export declare function effect(fn: () => void): () => void;
15
+ /** Deriva un valor de otras señales y lo actualiza cuando cambian. */
16
+ export declare function computed<T>(fn: () => T): ReadonlySignal<T>;
17
+ /** Agrupa varias escrituras en una sola ronda de notificaciones. */
18
+ export declare function batch<T>(fn: () => T): T;
19
+ /** Lee señales sin registrar dependencias en el observador activo. */
20
+ export declare function untrack<T>(fn: () => T): T;
21
+ /** Asocia efectos a una instancia y entrega una función para liberarlos. */
22
+ export declare function createRoot<T>(fn: (dispose: () => void) => T): T;