@zenithbuild/runtime 0.7.2 → 0.7.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.
Files changed (47) hide show
  1. package/HYDRATION_CONTRACT.md +1 -1
  2. package/LICENSE +21 -0
  3. package/README.md +16 -18
  4. package/RUNTIME_CONTRACT.md +24 -12
  5. package/dist/cleanup.js +6 -3
  6. package/dist/diagnostics.d.ts +7 -0
  7. package/dist/diagnostics.js +7 -0
  8. package/dist/effect-runtime.d.ts +2 -0
  9. package/dist/effect-runtime.js +139 -0
  10. package/dist/effect-scheduler.d.ts +4 -0
  11. package/dist/effect-scheduler.js +88 -0
  12. package/dist/effect-utils.d.ts +19 -0
  13. package/dist/effect-utils.js +160 -0
  14. package/dist/env.d.ts +12 -0
  15. package/dist/env.js +18 -2
  16. package/dist/events.d.ts +1 -8
  17. package/dist/events.js +108 -46
  18. package/dist/expressions.d.ts +14 -0
  19. package/dist/expressions.js +295 -0
  20. package/dist/fragment-patch.d.ts +12 -0
  21. package/dist/fragment-patch.js +118 -0
  22. package/dist/hydrate.d.ts +3 -117
  23. package/dist/hydrate.js +235 -1817
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +2 -2
  26. package/dist/markup.d.ts +8 -0
  27. package/dist/markup.js +127 -0
  28. package/dist/mount-runtime.d.ts +1 -0
  29. package/dist/mount-runtime.js +39 -0
  30. package/dist/payload.d.ts +21 -0
  31. package/dist/payload.js +386 -0
  32. package/dist/reactivity-core.d.ts +3 -0
  33. package/dist/reactivity-core.js +22 -0
  34. package/dist/render.d.ts +3 -0
  35. package/dist/render.js +340 -0
  36. package/dist/scanner.d.ts +1 -0
  37. package/dist/scanner.js +61 -0
  38. package/dist/side-effect-scope.d.ts +16 -0
  39. package/dist/side-effect-scope.js +99 -0
  40. package/dist/signal.js +1 -1
  41. package/dist/state.js +1 -1
  42. package/dist/template-parser.d.ts +1 -0
  43. package/dist/template-parser.js +268 -0
  44. package/dist/template.js +10 -11
  45. package/dist/zeneffect.d.ts +12 -14
  46. package/dist/zeneffect.js +25 -519
  47. package/package.json +5 -3
package/dist/events.js CHANGED
@@ -1,57 +1,119 @@
1
- // ---------------------------------------------------------------------------
2
- // events.js — Zenith Runtime V0
3
- // ---------------------------------------------------------------------------
4
- // Event binding system.
5
- //
6
- // For: <button data-zx-on-click="1">
7
- //
8
- // Algorithm:
9
- // 1. Extract event name from attribute suffix ("click")
10
- // 2. Resolve expression at index → must be a function
11
- // 3. addEventListener directly
12
- //
13
- // No synthetic events.
14
- // No delegation.
15
- // No wrapping.
16
- // ---------------------------------------------------------------------------
17
1
  import { _registerListener } from './cleanup.js';
18
- import { rethrowZenithRuntimeError, throwZenithRuntimeError } from './diagnostics.js';
19
- /**
20
- * Bind an event listener to a DOM element.
21
- *
22
- * @param {Element} element - The DOM element
23
- * @param {string} eventName - The event name (e.g. "click")
24
- * @param {() => any} exprFn - Pre-bound expression function that must resolve to a handler
25
- */
26
- export function bindEvent(element, eventName, exprFn) {
27
- const resolved = exprFn();
28
- if (typeof resolved !== 'function') {
29
- throwZenithRuntimeError({
30
- phase: 'bind',
31
- code: 'BINDING_APPLY_FAILED',
32
- message: `Event binding expected a function reference for "${eventName}"`,
33
- marker: { type: `data-zx-on-${eventName}`, id: '<unknown>' },
34
- path: `event:${eventName}`,
35
- hint: 'Use on:*={handler} and ensure forwarded props are function-valued.',
36
- docsLink: '/docs/documentation/contracts/runtime-contract.md#event-bindings'
37
- });
38
- }
39
- const wrapped = function zenithBoundEvent(event) {
2
+ import { rethrowZenithRuntimeError, throwZenithRuntimeError, DOCS_LINKS } from './diagnostics.js';
3
+ import { _evaluateExpression, _describeBindingExpression, _resolveBindingSource } from './expressions.js';
4
+ export function bindEventMarkers(context) {
5
+ const { root, events } = context;
6
+ const eventIndices = new Set();
7
+ const escDispatchEntries = [];
8
+ for (let i = 0; i < events.length; i++) {
9
+ const eventBinding = events[i];
10
+ if (eventIndices.has(eventBinding.index)) {
11
+ throw new Error(`[Zenith Runtime] duplicate event index ${eventBinding.index}`);
12
+ }
13
+ eventIndices.add(eventBinding.index);
14
+ const marker = context.markerByIndex.get(eventBinding.index) || null;
15
+ const nodes = context.resolveNodes(eventBinding.selector, eventBinding.index, 'event', eventBinding.source || marker?.source);
16
+ const expressionBinding = context.expressions[eventBinding.index];
17
+ const handler = _resolveEventHandler(context, expressionBinding, marker, eventBinding);
18
+ for (let j = 0; j < nodes.length; j++) {
19
+ const node = nodes[j];
20
+ const wrappedHandler = _createWrappedEventHandler(handler, expressionBinding, marker, eventBinding);
21
+ if (eventBinding.event === 'esc') {
22
+ escDispatchEntries.push({ node, handler: wrappedHandler });
23
+ continue;
24
+ }
25
+ _bindDomEvent(node, eventBinding.event, wrappedHandler);
26
+ }
27
+ }
28
+ if (escDispatchEntries.length > 0) {
29
+ _registerEscDispatch(root, escDispatchEntries);
30
+ }
31
+ }
32
+ function _resolveEventHandler(context, expressionBinding, marker, eventBinding) {
33
+ const handler = _evaluateExpression(expressionBinding, context.stateValues, context.stateKeys, context.signalMap, context.componentBindings, context.params, context.ssrData, 'event', context.props, context.exprFns, marker, eventBinding);
34
+ if (typeof handler === 'function') {
35
+ return handler;
36
+ }
37
+ throwZenithRuntimeError({
38
+ phase: 'bind',
39
+ code: 'BINDING_APPLY_FAILED',
40
+ message: `Event binding at index ${eventBinding.index} expected a function reference. You passed: ${_describeBindingExpression(expressionBinding)}`,
41
+ marker: { type: `data-zx-on-${eventBinding.event}`, id: eventBinding.index },
42
+ path: `event[${eventBinding.index}].${eventBinding.event}`,
43
+ hint: 'Use on:*={handler} or ensure the forwarded prop is a function.',
44
+ docsLink: DOCS_LINKS.eventBinding,
45
+ source: _resolveBindingSource(expressionBinding, marker, eventBinding)
46
+ });
47
+ }
48
+ function _createWrappedEventHandler(handler, expressionBinding, marker, eventBinding) {
49
+ return function zenithEventHandler(event) {
40
50
  try {
41
- return resolved.call(this, event);
51
+ return handler.call(this, event);
42
52
  }
43
53
  catch (error) {
44
54
  rethrowZenithRuntimeError(error, {
45
55
  phase: 'event',
46
56
  code: 'EVENT_HANDLER_FAILED',
47
- message: `Event handler failed for "${eventName}"`,
48
- marker: { type: `data-zx-on-${eventName}`, id: '<unknown>' },
49
- path: `event:${eventName}:${resolved.name || '<anonymous>'}`,
50
- hint: 'Inspect handler logic and referenced state.',
51
- docsLink: '/docs/documentation/contracts/runtime-contract.md#event-bindings'
57
+ message: `Event handler failed for "${eventBinding.event}"`,
58
+ marker: { type: `data-zx-on-${eventBinding.event}`, id: eventBinding.index },
59
+ path: `event[${eventBinding.index}].${eventBinding.event}`,
60
+ hint: 'Inspect the handler body and referenced state.',
61
+ docsLink: DOCS_LINKS.eventBinding,
62
+ source: _resolveBindingSource(expressionBinding, marker, eventBinding)
52
63
  });
53
64
  }
54
65
  };
55
- element.addEventListener(eventName, wrapped);
56
- _registerListener(element, eventName, wrapped);
66
+ }
67
+ function _registerEscDispatch(root, escDispatchEntries) {
68
+ const doc = _resolveOwnerDocument(root);
69
+ if (!doc || typeof doc.addEventListener !== 'function') {
70
+ return;
71
+ }
72
+ const escDispatchListener = function zenithEscDispatch(event) {
73
+ if (!event || event.key !== 'Escape') {
74
+ return;
75
+ }
76
+ const targetEntry = _resolveEscTarget(doc, escDispatchEntries);
77
+ if (!targetEntry) {
78
+ return;
79
+ }
80
+ return targetEntry.handler.call(targetEntry.node, event);
81
+ };
82
+ _bindDomEvent(doc, 'keydown', escDispatchListener);
83
+ }
84
+ function _resolveEscTarget(doc, escDispatchEntries) {
85
+ const activeElement = doc.activeElement || null;
86
+ if (activeElement && activeElement !== doc.body && activeElement !== doc.documentElement) {
87
+ for (let i = escDispatchEntries.length - 1; i >= 0; i--) {
88
+ const entry = escDispatchEntries[i];
89
+ if (!entry?.node || !entry.node.isConnected) {
90
+ continue;
91
+ }
92
+ if (typeof entry.node.contains === 'function' && entry.node.contains(activeElement)) {
93
+ return entry;
94
+ }
95
+ }
96
+ }
97
+ if (activeElement === null || activeElement === doc.body || activeElement === doc.documentElement) {
98
+ for (let i = escDispatchEntries.length - 1; i >= 0; i--) {
99
+ const entry = escDispatchEntries[i];
100
+ if (entry?.node?.isConnected) {
101
+ return entry;
102
+ }
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ function _bindDomEvent(target, eventName, handler) {
108
+ target.addEventListener(eventName, handler);
109
+ _registerListener(target, eventName, handler);
110
+ }
111
+ function _resolveOwnerDocument(root) {
112
+ if (root?.ownerDocument) {
113
+ return root.ownerDocument;
114
+ }
115
+ if (root?.nodeType === 9) {
116
+ return root;
117
+ }
118
+ return typeof document !== 'undefined' ? document : null;
57
119
  }
@@ -0,0 +1,14 @@
1
+ export function _resolveExpressionSignalIndices(binding: any): any[];
2
+ export function _evaluateExpression(binding: any, stateValues: any, stateKeys: any, signalMap: any, componentBindings: any, params: any, ssrData: any, mode: any, props: any, exprFns: any, markerBinding?: null, eventBinding?: null): any;
3
+ export function _throwUnresolvedMemberChainError(literal: any, markerIndex: any, mode: any, pathSuffix: any, hint: any, source: any): void;
4
+ export function _resolveStrictMemberChainLiteral(literal: any, stateValues: any, stateKeys: any, params: any, ssrData: any, mode: any, props: any, markerIndex: any, source: any): any;
5
+ export function _resolvePrimitiveLiteral(literal: any): any;
6
+ export function _buildLiteralScope(stateValues: any, stateKeys: any, params: any, ssrData: any, mode: any, props: any): any;
7
+ export function _resolveBindingSource(binding: any, markerBinding: any, eventBinding: any): any;
8
+ export function _describeBindingExpression(binding: any): string;
9
+ export function _markerTypeForError(kind: any): any;
10
+ export function _truncateLiteralForError(str: any): string;
11
+ export const UNRESOLVED_LITERAL: unique symbol;
12
+ export const STRICT_MEMBER_CHAIN_LITERAL_RE: RegExp;
13
+ export const CANONICAL_MEMBER_CHAIN_BASES: Set<string>;
14
+ export const UNSAFE_MEMBER_KEYS: Set<string>;
@@ -0,0 +1,295 @@
1
+ import { throwZenithRuntimeError, DOCS_LINKS } from './diagnostics.js';
2
+ import { _fragment } from './markup.js';
3
+ export const UNRESOLVED_LITERAL = Symbol('unresolved_literal');
4
+ export const STRICT_MEMBER_CHAIN_LITERAL_RE = /^(?:true|false|null|undefined|[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*)$/;
5
+ export const CANONICAL_MEMBER_CHAIN_BASES = new Set(['props', 'params', 'data', 'ssr']);
6
+ export const UNSAFE_MEMBER_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
7
+ export function _resolveExpressionSignalIndices(binding) {
8
+ if (!binding || typeof binding !== 'object') {
9
+ return [];
10
+ }
11
+ if (Array.isArray(binding.signal_indices) && binding.signal_indices.length > 0) {
12
+ return [...new Set(binding.signal_indices.filter((value) => Number.isInteger(value) && value >= 0))];
13
+ }
14
+ if (Number.isInteger(binding.signal_index) && binding.signal_index >= 0) {
15
+ return [binding.signal_index];
16
+ }
17
+ return [];
18
+ }
19
+ export function _evaluateExpression(binding, stateValues, stateKeys, signalMap, componentBindings, params, ssrData, mode, props, exprFns, markerBinding = null, eventBinding = null) {
20
+ if (binding.fn_index != null && binding.fn_index !== undefined) {
21
+ const fns = Array.isArray(exprFns) ? exprFns : [];
22
+ const fn = fns[binding.fn_index];
23
+ if (typeof fn === 'function') {
24
+ try {
25
+ return fn({
26
+ signalMap,
27
+ params,
28
+ ssrData,
29
+ props: props || {},
30
+ componentBindings,
31
+ fragment: _fragment
32
+ });
33
+ }
34
+ catch (fnErr) {
35
+ throw fnErr;
36
+ }
37
+ }
38
+ }
39
+ if (binding.signal_index !== null && binding.signal_index !== undefined) {
40
+ const signalValue = signalMap.get(binding.signal_index);
41
+ if (!signalValue || typeof signalValue.get !== 'function') {
42
+ throw new Error('[Zenith Runtime] expression.signal_index did not resolve to a signal');
43
+ }
44
+ return mode === 'event' ? signalValue : signalValue.get();
45
+ }
46
+ if (binding.state_index !== null && binding.state_index !== undefined) {
47
+ const resolved = stateValues[binding.state_index];
48
+ if (mode !== 'event' &&
49
+ resolved &&
50
+ typeof resolved === 'object' &&
51
+ typeof resolved.get === 'function') {
52
+ return resolved.get();
53
+ }
54
+ if (mode !== 'event' && typeof resolved === 'function') {
55
+ return resolved();
56
+ }
57
+ return resolved;
58
+ }
59
+ if (typeof binding.component_instance === 'string' && typeof binding.component_binding === 'string') {
60
+ const instanceBindings = componentBindings[binding.component_instance];
61
+ const resolved = instanceBindings && Object.prototype.hasOwnProperty.call(instanceBindings, binding.component_binding)
62
+ ? instanceBindings[binding.component_binding]
63
+ : undefined;
64
+ if (mode !== 'event' &&
65
+ resolved &&
66
+ typeof resolved === 'object' &&
67
+ typeof resolved.get === 'function') {
68
+ return resolved.get();
69
+ }
70
+ if (mode !== 'event' && typeof resolved === 'function') {
71
+ return resolved();
72
+ }
73
+ return resolved;
74
+ }
75
+ if (binding.literal !== null && binding.literal !== undefined) {
76
+ if (typeof binding.literal === 'string') {
77
+ const trimmedLiteral = binding.literal.trim();
78
+ // 1. Static primitives (true, false, null, undefined, numbers, quoted strings)
79
+ const primitiveValue = _resolvePrimitiveLiteral(trimmedLiteral);
80
+ if (primitiveValue !== UNRESOLVED_LITERAL) {
81
+ return primitiveValue;
82
+ }
83
+ // 2. Canonical payload roots
84
+ if (trimmedLiteral === 'data' || trimmedLiteral === 'ssr') {
85
+ return ssrData;
86
+ }
87
+ if (trimmedLiteral === 'params') {
88
+ return params;
89
+ }
90
+ if (trimmedLiteral === 'props') {
91
+ return props || {};
92
+ }
93
+ // 3. Bounded canonical member chains (props.*, params.*, data.*, ssr.*, exact stateKeys)
94
+ const strictMemberValue = _resolveStrictMemberChainLiteral(trimmedLiteral, stateValues, stateKeys, params, ssrData, mode, props, binding.marker_index, _resolveBindingSource(binding, markerBinding, eventBinding));
95
+ if (strictMemberValue !== UNRESOLVED_LITERAL) {
96
+ return strictMemberValue;
97
+ }
98
+ // 4. Anything else is a literal that was not lowered by the compiler.
99
+ // No heuristic guessing, no identifier extraction, no alias recovery.
100
+ throwZenithRuntimeError({
101
+ phase: 'bind',
102
+ code: 'EXPRESSION_NOT_LOWERED',
103
+ message: `Expression literal was not lowered by the compiler: ${_truncateLiteralForError(trimmedLiteral)}`,
104
+ marker: {
105
+ type: _markerTypeForError(mode),
106
+ id: binding.marker_index
107
+ },
108
+ path: `expression[${binding.marker_index}]`,
109
+ hint: 'This expression must be lowered to fn_index, signal_index, or state_index by the compiler. Literal string interpretation is restricted to static primitives and canonical member chains (props.*, params.*, data.*, ssr.*).',
110
+ docsLink: DOCS_LINKS.expressionScope,
111
+ source: _resolveBindingSource(binding, markerBinding, eventBinding)
112
+ });
113
+ }
114
+ return binding.literal;
115
+ }
116
+ return '';
117
+ }
118
+ export function _throwUnresolvedMemberChainError(literal, markerIndex, mode, pathSuffix, hint, source) {
119
+ throwZenithRuntimeError({
120
+ phase: 'bind',
121
+ code: 'UNRESOLVED_EXPRESSION',
122
+ message: `Failed to resolve expression literal: ${_truncateLiteralForError(literal)}`,
123
+ marker: {
124
+ type: _markerTypeForError(mode),
125
+ id: markerIndex
126
+ },
127
+ path: `marker[${markerIndex}].${pathSuffix}`,
128
+ hint,
129
+ docsLink: DOCS_LINKS.expressionScope,
130
+ source
131
+ });
132
+ }
133
+ export function _resolveStrictMemberChainLiteral(literal, stateValues, stateKeys, params, ssrData, mode, props, markerIndex, source) {
134
+ if (typeof literal !== 'string' || !STRICT_MEMBER_CHAIN_LITERAL_RE.test(literal)) {
135
+ return UNRESOLVED_LITERAL;
136
+ }
137
+ // Primitives are handled by _resolvePrimitiveLiteral before this function
138
+ if (literal === 'true' || literal === 'false' || literal === 'null' || literal === 'undefined') {
139
+ return UNRESOLVED_LITERAL;
140
+ }
141
+ const segments = literal.split('.');
142
+ const baseIdentifier = segments[0];
143
+ // Bounded resolution: only canonical payload prefixes and exact state keys
144
+ const isCanonicalBase = CANONICAL_MEMBER_CHAIN_BASES.has(baseIdentifier);
145
+ const isExactStateKey = !isCanonicalBase && Array.isArray(stateKeys) && stateKeys.includes(baseIdentifier);
146
+ if (!isCanonicalBase && !isExactStateKey) {
147
+ // Not a canonical base and not an exact state key — this literal was not lowered
148
+ return UNRESOLVED_LITERAL;
149
+ }
150
+ const scope = _buildLiteralScope(stateValues, stateKeys, params, ssrData, mode, props);
151
+ if (!Object.prototype.hasOwnProperty.call(scope, baseIdentifier)) {
152
+ _throwUnresolvedMemberChainError(literal, markerIndex, mode, `expression.${baseIdentifier}`, `Base identifier "${baseIdentifier}" is not bound. Check props/data/params and declared state keys.`, source);
153
+ }
154
+ let cursor = scope[baseIdentifier];
155
+ let traversedPath = baseIdentifier;
156
+ for (let i = 1; i < segments.length; i++) {
157
+ const segment = segments[i];
158
+ if (UNSAFE_MEMBER_KEYS.has(segment)) {
159
+ throwZenithRuntimeError({
160
+ phase: 'bind',
161
+ code: 'UNSAFE_MEMBER_ACCESS',
162
+ message: `Blocked unsafe member access: ${segment} in path "${literal}"`,
163
+ path: `marker[${markerIndex}].expression.${literal}`,
164
+ hint: 'Property access to __proto__, prototype, and constructor is forbidden.',
165
+ docsLink: DOCS_LINKS.expressionScope,
166
+ source
167
+ });
168
+ }
169
+ if (cursor === null || cursor === undefined) {
170
+ _throwUnresolvedMemberChainError(literal, markerIndex, mode, `expression.${traversedPath}.${segment}`, `Cannot read "${segment}" from ${traversedPath} because it is null or undefined.`, source);
171
+ }
172
+ const cursorType = typeof cursor;
173
+ if (cursorType !== 'object' && cursorType !== 'function') {
174
+ _throwUnresolvedMemberChainError(literal, markerIndex, mode, `expression.${traversedPath}.${segment}`, `Cannot read "${segment}" from ${traversedPath} because it resolved to a ${cursorType}.`, source);
175
+ }
176
+ if (!Object.prototype.hasOwnProperty.call(cursor, segment)) {
177
+ _throwUnresolvedMemberChainError(literal, markerIndex, mode, `expression.${traversedPath}.${segment}`, `Missing member "${segment}" on ${traversedPath}. Check your bindings.`, source);
178
+ }
179
+ cursor = cursor[segment];
180
+ traversedPath = `${traversedPath}.${segment}`;
181
+ }
182
+ return cursor;
183
+ }
184
+ export function _resolvePrimitiveLiteral(literal) {
185
+ if (typeof literal !== 'string') {
186
+ return UNRESOLVED_LITERAL;
187
+ }
188
+ if (literal === 'true')
189
+ return true;
190
+ if (literal === 'false')
191
+ return false;
192
+ if (literal === 'null')
193
+ return null;
194
+ if (literal === 'undefined')
195
+ return undefined;
196
+ if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(literal)) {
197
+ return Number(literal);
198
+ }
199
+ if (literal.length >= 2 && literal.startsWith('"') && literal.endsWith('"')) {
200
+ try {
201
+ return JSON.parse(literal);
202
+ }
203
+ catch {
204
+ return UNRESOLVED_LITERAL;
205
+ }
206
+ }
207
+ if (literal.length >= 2 && literal.startsWith('\'') && literal.endsWith('\'')) {
208
+ return literal
209
+ .slice(1, -1)
210
+ .replace(/\\\\/g, '\\')
211
+ .replace(/\\'/g, '\'');
212
+ }
213
+ if (literal.length >= 2 && literal.startsWith('`') && literal.endsWith('`')) {
214
+ return literal.slice(1, -1);
215
+ }
216
+ return UNRESOLVED_LITERAL;
217
+ }
218
+ export function _buildLiteralScope(stateValues, stateKeys, params, ssrData, mode, props) {
219
+ const scope = Object.create(null);
220
+ scope.params = params;
221
+ scope.data = ssrData;
222
+ scope.ssr = ssrData;
223
+ scope.props = props || {};
224
+ scope.__zenith_fragment = _fragment;
225
+ // Exact state keys only — no alias derivation, no mangled-name recovery
226
+ if (Array.isArray(stateKeys)) {
227
+ for (let i = 0; i < stateKeys.length; i++) {
228
+ const key = stateKeys[i];
229
+ if (typeof key !== 'string' || key.length === 0) {
230
+ continue;
231
+ }
232
+ if (Object.prototype.hasOwnProperty.call(scope, key)) {
233
+ continue;
234
+ }
235
+ scope[key] = stateValues[i];
236
+ }
237
+ }
238
+ return scope;
239
+ }
240
+ // _isLikelyExpressionLiteral and _extractMissingIdentifier removed:
241
+ // Runtime no longer performs heuristic identifier extraction or expression
242
+ // shape guessing. Unresolved literals throw EXPRESSION_NOT_LOWERED directly.
243
+ export function _resolveBindingSource(binding, markerBinding, eventBinding) {
244
+ const candidates = [
245
+ binding?.source,
246
+ eventBinding?.source,
247
+ markerBinding?.source
248
+ ];
249
+ for (let i = 0; i < candidates.length; i++) {
250
+ const candidate = candidates[i];
251
+ if (candidate && typeof candidate === 'object' && typeof candidate.file === 'string') {
252
+ return candidate;
253
+ }
254
+ }
255
+ return undefined;
256
+ }
257
+ export function _describeBindingExpression(binding) {
258
+ if (!binding || typeof binding !== 'object') {
259
+ return '<unknown>';
260
+ }
261
+ if (typeof binding.literal === 'string' && binding.literal.trim().length > 0) {
262
+ return _truncateLiteralForError(binding.literal.trim());
263
+ }
264
+ if (Number.isInteger(binding.state_index)) {
265
+ return `state[${binding.state_index}]`;
266
+ }
267
+ if (Number.isInteger(binding.signal_index)) {
268
+ return `signal[${binding.signal_index}]`;
269
+ }
270
+ if (typeof binding.component_instance === 'string' && typeof binding.component_binding === 'string') {
271
+ return `${binding.component_instance}.${binding.component_binding}`;
272
+ }
273
+ return '<unknown expression>';
274
+ }
275
+ export function _markerTypeForError(kind) {
276
+ if (kind === 'text')
277
+ return 'data-zx-e';
278
+ if (kind === 'attr')
279
+ return 'data-zx-attr';
280
+ if (kind === 'event')
281
+ return 'data-zx-on';
282
+ return kind;
283
+ }
284
+ export function _truncateLiteralForError(str) {
285
+ if (typeof str !== 'string')
286
+ return String(str);
287
+ const sanitized = str
288
+ .replace(/[A-Za-z]:\\[^\s"'`]+/g, '<path>')
289
+ .replace(/\/Users\/[^\s"'`]+/g, '<path>')
290
+ .replace(/\/home\/[^\s"'`]+/g, '<path>')
291
+ .replace(/\/private\/[^\s"'`]+/g, '<path>')
292
+ .replace(/\/tmp\/[^\s"'`]+/g, '<path>')
293
+ .replace(/\/var\/folders\/[^\s"'`]+/g, '<path>');
294
+ return sanitized.length > 100 ? `${sanitized.substring(0, 97)}...` : sanitized;
295
+ }
@@ -0,0 +1,12 @@
1
+ export function createFragmentRegion(): {
2
+ mount(items: any, ctx: any): void;
3
+ update(newItemsRaw: any, ctx: any): void;
4
+ destroy(): void;
5
+ };
6
+ export function unmountRegion(region: any): void;
7
+ export function mountItemIntoRegion(item: any, ctx: any): {
8
+ nodes: never[];
9
+ unmounts: never[];
10
+ };
11
+ export const coerceText: typeof _coerceText;
12
+ import { _coerceText } from './render.js';
@@ -0,0 +1,118 @@
1
+ import { _createContextualFragment, _coerceText } from './render.js';
2
+ import { _isHtmlFragment } from './markup.js';
3
+ export function createFragmentRegion() {
4
+ let oldItems = [];
5
+ let regions = [];
6
+ return {
7
+ mount(items, ctx) {
8
+ oldItems = _flattenFragment(items);
9
+ for (let i = 0; i < oldItems.length; i++) {
10
+ regions.push(mountItemIntoRegion(oldItems[i], ctx));
11
+ }
12
+ },
13
+ update(newItemsRaw, ctx) {
14
+ const newItems = _flattenFragment(newItemsRaw);
15
+ for (let i = 0; i < newItems.length; i++) {
16
+ if (i < oldItems.length) {
17
+ if (oldItems[i] === newItems[i]) {
18
+ continue;
19
+ }
20
+ unmountRegion(regions[i]);
21
+ let insertBefore = ctx.insertBefore;
22
+ for (let j = i + 1; j < regions.length; j++) {
23
+ if (regions[j] && regions[j].nodes.length > 0) {
24
+ insertBefore = regions[j].nodes[0];
25
+ break;
26
+ }
27
+ }
28
+ const patchCtx = { parent: ctx.parent, insertBefore };
29
+ regions[i] = mountItemIntoRegion(newItems[i], patchCtx);
30
+ oldItems[i] = newItems[i];
31
+ }
32
+ else {
33
+ regions.push(mountItemIntoRegion(newItems[i], ctx));
34
+ oldItems.push(newItems[i]);
35
+ }
36
+ }
37
+ while (oldItems.length > newItems.length) {
38
+ const idx = oldItems.length - 1;
39
+ unmountRegion(regions[idx]);
40
+ regions.pop();
41
+ oldItems.pop();
42
+ }
43
+ },
44
+ destroy() {
45
+ for (let i = 0; i < regions.length; i++) {
46
+ unmountRegion(regions[i]);
47
+ }
48
+ regions = [];
49
+ oldItems = [];
50
+ }
51
+ };
52
+ }
53
+ export function unmountRegion(region) {
54
+ if (!region)
55
+ return;
56
+ for (let i = 0; i < region.unmounts.length; i++) {
57
+ try {
58
+ region.unmounts[i]();
59
+ }
60
+ catch (e) { }
61
+ }
62
+ for (let i = 0; i < region.nodes.length; i++) {
63
+ const node = region.nodes[i];
64
+ if (node.parentNode) {
65
+ node.parentNode.removeChild(node);
66
+ }
67
+ }
68
+ region.nodes = [];
69
+ region.unmounts = [];
70
+ }
71
+ export function mountItemIntoRegion(item, ctx) {
72
+ const parent = ctx.parent;
73
+ const insertBefore = ctx.insertBefore || null;
74
+ const doc = parent.ownerDocument || document;
75
+ const region = { nodes: [], unmounts: [] };
76
+ if (item && item.__zenith_fragment === true && typeof item.mount === 'function') {
77
+ const fragment = doc.createDocumentFragment();
78
+ item.mount(fragment);
79
+ const childNodes = Array.from(fragment.childNodes);
80
+ parent.insertBefore(fragment, insertBefore);
81
+ region.nodes = childNodes;
82
+ if (typeof item.unmount === 'function') {
83
+ region.unmounts.push(item.unmount.bind(item));
84
+ }
85
+ return region;
86
+ }
87
+ if (_isHtmlFragment(item)) {
88
+ const fragment = _createContextualFragment(parent, item.html);
89
+ const childNodes = Array.from(fragment.childNodes);
90
+ parent.insertBefore(fragment, insertBefore);
91
+ region.nodes = childNodes;
92
+ return region;
93
+ }
94
+ const text = _coerceText(item, ctx.rootPath || 'renderable');
95
+ if (text || text === '') {
96
+ const textNode = doc.createTextNode(text);
97
+ parent.insertBefore(textNode, insertBefore);
98
+ region.nodes = [textNode];
99
+ }
100
+ return region;
101
+ }
102
+ function _flattenFragment(value) {
103
+ if (!Array.isArray(value))
104
+ return [value];
105
+ const res = [];
106
+ function walk(item) {
107
+ if (Array.isArray(item)) {
108
+ for (let i = 0; i < item.length; i++)
109
+ walk(item[i]);
110
+ }
111
+ else {
112
+ res.push(item);
113
+ }
114
+ }
115
+ walk(value);
116
+ return res;
117
+ }
118
+ export const coerceText = _coerceText;