@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.
@@ -1,5 +1,8 @@
1
1
  # Zenith Runtime V0 Hydration Contract
2
2
 
3
+ Canonical public docs: `../zenith-docs/documentation/contracts/hydration-contract.md`
4
+
5
+
3
6
  Status: FROZEN (V0)
4
7
 
5
8
  This contract locks hydration and reactivity boundaries for Zenith V0.
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
 
@@ -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 { root, expressions, markers, events, stateValues, stateKeys, signals, components, route, params, ssrData, props } = normalized;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenithbuild/runtime",
3
- "version": "0.5.0-beta.2.12",
3
+ "version": "0.5.0-beta.2.16",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {