@zenithbuild/runtime 0.5.0-beta.2.12 → 0.5.0-beta.2.16
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/HYDRATION_CONTRACT.md +3 -0
- package/README.md +6 -0
- package/RUNTIME_CONTRACT.md +3 -0
- package/dist/hydrate.js +123 -1
- package/package.json +1 -1
package/HYDRATION_CONTRACT.md
CHANGED
package/README.md
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
The core runtime library for the Zenith framework.
|
|
7
7
|
|
|
8
|
+
## Canonical Docs
|
|
9
|
+
|
|
10
|
+
- Runtime contract: `../zenith-docs/documentation/contracts/runtime-contract.md`
|
|
11
|
+
- Hydration contract: `../zenith-docs/documentation/contracts/hydration-contract.md`
|
|
12
|
+
- Reactive binding model: `../zenith-docs/documentation/reference/reactive-binding-model.md`
|
|
13
|
+
|
|
8
14
|
## Overview
|
|
9
15
|
This package provides the reactivity system, hydration logic, and Virtual DOM primitives used by Zenith applications. It is designed to be lightweight, fast, and tree-shakeable.
|
|
10
16
|
|
package/RUNTIME_CONTRACT.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# RUNTIME_CONTRACT.md — Sealed Runtime Interface
|
|
2
2
|
|
|
3
|
+
Canonical public docs: `../zenith-docs/documentation/contracts/runtime-contract.md`
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
> **This document is a legal boundary.**
|
|
4
7
|
> The runtime is a consumer of bundler output.
|
|
5
8
|
> It does not reinterpret, normalize, or extend.
|
package/dist/hydrate.js
CHANGED
|
@@ -43,6 +43,7 @@ const COMPILED_LITERAL_CACHE = new Map();
|
|
|
43
43
|
* expressions: Array<{ marker_index: number, signal_index?: number|null, state_index?: number|null, component_instance?: string|null, component_binding?: string|null, literal?: string|null }>,
|
|
44
44
|
* markers: Array<{ index: number, kind: 'text' | 'attr' | 'event', selector: string, attr?: string }>,
|
|
45
45
|
* events: Array<{ index: number, event: string, selector: string }>,
|
|
46
|
+
* refs?: Array<{ index: number, state_index: number, selector: string }>,
|
|
46
47
|
* state_values: Array<*>,
|
|
47
48
|
* state_keys?: Array<string>,
|
|
48
49
|
* signals: Array<{ id: number, kind: 'signal', state_index: number }>,
|
|
@@ -55,7 +56,21 @@ export function hydrate(payload) {
|
|
|
55
56
|
try {
|
|
56
57
|
const normalized = _validatePayload(payload);
|
|
57
58
|
_deepFreezePayload(payload);
|
|
58
|
-
const {
|
|
59
|
+
const {
|
|
60
|
+
root,
|
|
61
|
+
expressions,
|
|
62
|
+
markers,
|
|
63
|
+
events,
|
|
64
|
+
refs,
|
|
65
|
+
stateValues,
|
|
66
|
+
stateKeys,
|
|
67
|
+
signals,
|
|
68
|
+
components,
|
|
69
|
+
route,
|
|
70
|
+
params,
|
|
71
|
+
ssrData,
|
|
72
|
+
props
|
|
73
|
+
} = normalized;
|
|
59
74
|
|
|
60
75
|
const componentBindings = Object.create(null);
|
|
61
76
|
|
|
@@ -72,6 +87,23 @@ export function hydrate(payload) {
|
|
|
72
87
|
signalMap.set(i, candidate);
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
const hydratedRefs = [];
|
|
91
|
+
for (let i = 0; i < refs.length; i++) {
|
|
92
|
+
const refBinding = refs[i];
|
|
93
|
+
const targetRef = stateValues[refBinding.state_index];
|
|
94
|
+
const nodes = _resolveNodes(root, refBinding.selector, refBinding.index, 'ref');
|
|
95
|
+
targetRef.current = nodes[0] || null;
|
|
96
|
+
hydratedRefs.push(targetRef);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (hydratedRefs.length > 0) {
|
|
100
|
+
_registerDisposer(() => {
|
|
101
|
+
for (let i = 0; i < hydratedRefs.length; i++) {
|
|
102
|
+
hydratedRefs[i].current = null;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
75
107
|
for (let i = 0; i < components.length; i++) {
|
|
76
108
|
const component = components[i];
|
|
77
109
|
const resolvedProps = Object.freeze(_resolveComponentProps(component.props || [], signalMap, {
|
|
@@ -246,6 +278,7 @@ export function hydrate(payload) {
|
|
|
246
278
|
}
|
|
247
279
|
|
|
248
280
|
const eventIndices = new Set();
|
|
281
|
+
const escDispatchEntries = [];
|
|
249
282
|
for (let i = 0; i < events.length; i++) {
|
|
250
283
|
const eventBinding = events[i];
|
|
251
284
|
if (eventIndices.has(eventBinding.index)) {
|
|
@@ -291,11 +324,67 @@ export function hydrate(payload) {
|
|
|
291
324
|
});
|
|
292
325
|
}
|
|
293
326
|
};
|
|
327
|
+
if (eventBinding.event === 'esc') {
|
|
328
|
+
escDispatchEntries.push({
|
|
329
|
+
node,
|
|
330
|
+
handler: wrappedHandler
|
|
331
|
+
});
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
294
334
|
node.addEventListener(eventBinding.event, wrappedHandler);
|
|
295
335
|
_registerListener(node, eventBinding.event, wrappedHandler);
|
|
296
336
|
}
|
|
297
337
|
}
|
|
298
338
|
|
|
339
|
+
if (escDispatchEntries.length > 0) {
|
|
340
|
+
const doc = root && root.ownerDocument ? root.ownerDocument : (typeof document !== 'undefined' ? document : null);
|
|
341
|
+
if (doc && typeof doc.addEventListener === 'function') {
|
|
342
|
+
const escDispatchListener = function zenithEscDispatch(event) {
|
|
343
|
+
if (!event || event.key !== 'Escape') {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const activeElement = doc.activeElement || null;
|
|
348
|
+
let targetEntry = null;
|
|
349
|
+
|
|
350
|
+
if (activeElement && activeElement !== doc.body && activeElement !== doc.documentElement) {
|
|
351
|
+
for (let i = escDispatchEntries.length - 1; i >= 0; i--) {
|
|
352
|
+
const entry = escDispatchEntries[i];
|
|
353
|
+
if (!entry || !entry.node) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (!entry.node.isConnected) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (typeof entry.node.contains === 'function' && entry.node.contains(activeElement)) {
|
|
360
|
+
targetEntry = entry;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!targetEntry && (activeElement === null || activeElement === doc.body || activeElement === doc.documentElement)) {
|
|
367
|
+
for (let i = escDispatchEntries.length - 1; i >= 0; i--) {
|
|
368
|
+
const entry = escDispatchEntries[i];
|
|
369
|
+
if (!entry || !entry.node || !entry.node.isConnected) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
targetEntry = entry;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!targetEntry) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return targetEntry.handler.call(targetEntry.node, event);
|
|
382
|
+
};
|
|
383
|
+
doc.addEventListener('keydown', escDispatchListener);
|
|
384
|
+
_registerListener(doc, 'keydown', escDispatchListener);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
299
388
|
return cleanup;
|
|
300
389
|
} catch (error) {
|
|
301
390
|
rethrowZenithRuntimeError(error, {
|
|
@@ -336,6 +425,8 @@ function _validatePayload(payload) {
|
|
|
336
425
|
throw new Error('[Zenith Runtime] hydrate(payload) requires events[]');
|
|
337
426
|
}
|
|
338
427
|
|
|
428
|
+
const refs = Array.isArray(payload.refs) ? payload.refs : [];
|
|
429
|
+
|
|
339
430
|
const stateValues = payload.state_values;
|
|
340
431
|
if (!Array.isArray(stateValues)) {
|
|
341
432
|
throw new Error('[Zenith Runtime] hydrate(payload) requires state_values[]');
|
|
@@ -425,6 +516,34 @@ function _validatePayload(payload) {
|
|
|
425
516
|
}
|
|
426
517
|
}
|
|
427
518
|
|
|
519
|
+
for (let i = 0; i < refs.length; i++) {
|
|
520
|
+
const refBinding = refs[i];
|
|
521
|
+
if (!refBinding || typeof refBinding !== 'object' || Array.isArray(refBinding)) {
|
|
522
|
+
throw new Error(`[Zenith Runtime] ref binding at position ${i} must be an object`);
|
|
523
|
+
}
|
|
524
|
+
if (!Number.isInteger(refBinding.index) || refBinding.index < 0) {
|
|
525
|
+
throw new Error(`[Zenith Runtime] ref binding at position ${i} requires non-negative index`);
|
|
526
|
+
}
|
|
527
|
+
if (
|
|
528
|
+
!Number.isInteger(refBinding.state_index) ||
|
|
529
|
+
refBinding.state_index < 0 ||
|
|
530
|
+
refBinding.state_index >= stateValues.length
|
|
531
|
+
) {
|
|
532
|
+
throw new Error(
|
|
533
|
+
`[Zenith Runtime] ref binding at position ${i} has out-of-bounds state_index`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (typeof refBinding.selector !== 'string' || refBinding.selector.length === 0) {
|
|
537
|
+
throw new Error(`[Zenith Runtime] ref binding at position ${i} requires selector`);
|
|
538
|
+
}
|
|
539
|
+
const candidate = stateValues[refBinding.state_index];
|
|
540
|
+
if (!candidate || typeof candidate !== 'object' || !Object.prototype.hasOwnProperty.call(candidate, 'current')) {
|
|
541
|
+
throw new Error(
|
|
542
|
+
`[Zenith Runtime] ref binding at position ${i} must resolve to a ref-like object`
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
428
547
|
for (let i = 0; i < signals.length; i++) {
|
|
429
548
|
const signalDescriptor = signals[i];
|
|
430
549
|
if (!signalDescriptor || typeof signalDescriptor !== 'object' || Array.isArray(signalDescriptor)) {
|
|
@@ -478,6 +597,7 @@ function _validatePayload(payload) {
|
|
|
478
597
|
for (let i = 0; i < expressions.length; i++) Object.freeze(expressions[i]);
|
|
479
598
|
for (let i = 0; i < markers.length; i++) Object.freeze(markers[i]);
|
|
480
599
|
for (let i = 0; i < events.length; i++) Object.freeze(events[i]);
|
|
600
|
+
for (let i = 0; i < refs.length; i++) Object.freeze(refs[i]);
|
|
481
601
|
for (let i = 0; i < signals.length; i++) Object.freeze(signals[i]);
|
|
482
602
|
for (let i = 0; i < components.length; i++) {
|
|
483
603
|
const c = components[i];
|
|
@@ -501,6 +621,7 @@ function _validatePayload(payload) {
|
|
|
501
621
|
Object.freeze(expressions);
|
|
502
622
|
Object.freeze(markers);
|
|
503
623
|
Object.freeze(events);
|
|
624
|
+
Object.freeze(refs);
|
|
504
625
|
Object.freeze(signals);
|
|
505
626
|
Object.freeze(components);
|
|
506
627
|
|
|
@@ -509,6 +630,7 @@ function _validatePayload(payload) {
|
|
|
509
630
|
expressions,
|
|
510
631
|
markers,
|
|
511
632
|
events,
|
|
633
|
+
refs,
|
|
512
634
|
stateValues,
|
|
513
635
|
stateKeys,
|
|
514
636
|
signals,
|