@zenithbuild/runtime 0.7.4 → 0.7.7
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 +1 -0
- package/RUNTIME_CONTRACT.md +8 -0
- package/dist/diagnostics-production.d.ts +11 -0
- package/dist/diagnostics-production.js +279 -0
- package/dist/events.js +2 -2
- package/dist/expressions.d.ts +0 -1
- package/dist/expressions.js +128 -150
- package/dist/hydrate.js +32 -67
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/markup.js +7 -7
- package/dist/payload.d.ts +9 -10
- package/dist/payload.js +128 -219
- package/dist/presence.d.ts +67 -0
- package/dist/presence.js +297 -0
- package/dist/render.js +99 -117
- package/dist/runtime-template-profile.d.ts +20 -0
- package/dist/runtime-template-profile.js +141 -0
- package/dist/scanner.js +2 -2
- package/dist/template.d.ts +7 -1
- package/dist/template.js +25 -23
- package/package.json +1 -1
package/dist/expressions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { throwZenithRuntimeError, DOCS_LINKS } from './diagnostics.js';
|
|
2
2
|
import { _fragment } from './markup.js';
|
|
3
3
|
export const UNRESOLVED_LITERAL = Symbol('unresolved_literal');
|
|
4
|
+
const OWN = Object.prototype.hasOwnProperty;
|
|
4
5
|
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
6
|
export const CANONICAL_MEMBER_CHAIN_BASES = new Set(['props', 'params', 'data', 'ssr']);
|
|
6
7
|
export const UNSAFE_MEMBER_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
@@ -17,109 +18,60 @@ export function _resolveExpressionSignalIndices(binding) {
|
|
|
17
18
|
return [];
|
|
18
19
|
}
|
|
19
20
|
export function _evaluateExpression(binding, stateValues, stateKeys, signalMap, componentBindings, params, ssrData, mode, props, exprFns, markerBinding = null, eventBinding = null) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const fn =
|
|
21
|
+
const runtimeProps = props || {};
|
|
22
|
+
if (binding.fn_index != null) {
|
|
23
|
+
const fn = Array.isArray(exprFns) ? exprFns[binding.fn_index] : undefined;
|
|
23
24
|
if (typeof fn === 'function') {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
catch (fnErr) {
|
|
35
|
-
throw fnErr;
|
|
36
|
-
}
|
|
25
|
+
return fn({
|
|
26
|
+
signalMap,
|
|
27
|
+
params,
|
|
28
|
+
ssrData,
|
|
29
|
+
props: runtimeProps,
|
|
30
|
+
componentBindings,
|
|
31
|
+
fragment: _fragment
|
|
32
|
+
});
|
|
37
33
|
}
|
|
38
34
|
}
|
|
39
|
-
if (binding.signal_index
|
|
35
|
+
if (binding.signal_index != null) {
|
|
40
36
|
const signalValue = signalMap.get(binding.signal_index);
|
|
41
37
|
if (!signalValue || typeof signalValue.get !== 'function') {
|
|
42
38
|
throw new Error('[Zenith Runtime] expression.signal_index did not resolve to a signal');
|
|
43
39
|
}
|
|
44
|
-
return
|
|
40
|
+
return _rvm(signalValue, mode);
|
|
45
41
|
}
|
|
46
|
-
if (binding.state_index
|
|
47
|
-
|
|
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;
|
|
42
|
+
if (binding.state_index != null) {
|
|
43
|
+
return _rvm(stateValues[binding.state_index], mode);
|
|
58
44
|
}
|
|
59
45
|
if (typeof binding.component_instance === 'string' && typeof binding.component_binding === 'string') {
|
|
60
|
-
|
|
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;
|
|
46
|
+
return _rvm(_rcb(binding, componentBindings), mode);
|
|
74
47
|
}
|
|
75
|
-
if (binding.literal
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
}
|
|
48
|
+
if (binding.literal == null) {
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
if (typeof binding.literal !== 'string') {
|
|
114
52
|
return binding.literal;
|
|
115
53
|
}
|
|
116
|
-
|
|
54
|
+
const trimmedLiteral = binding.literal.trim();
|
|
55
|
+
const primitiveValue = _resolvePrimitiveLiteral(trimmedLiteral);
|
|
56
|
+
if (primitiveValue !== UNRESOLVED_LITERAL) {
|
|
57
|
+
return primitiveValue;
|
|
58
|
+
}
|
|
59
|
+
const canonicalRootValue = _rcr(trimmedLiteral, params, ssrData, runtimeProps);
|
|
60
|
+
if (canonicalRootValue !== UNRESOLVED_LITERAL) {
|
|
61
|
+
return canonicalRootValue;
|
|
62
|
+
}
|
|
63
|
+
const source = _resolveBindingSource(binding, markerBinding, eventBinding);
|
|
64
|
+
const strictMemberValue = _resolveStrictMemberChainLiteral(trimmedLiteral, stateValues, stateKeys, params, ssrData, mode, runtimeProps, binding.marker_index, source);
|
|
65
|
+
if (strictMemberValue !== UNRESOLVED_LITERAL) {
|
|
66
|
+
return strictMemberValue;
|
|
67
|
+
}
|
|
68
|
+
_tenl(trimmedLiteral, binding.marker_index, mode, source);
|
|
117
69
|
}
|
|
118
70
|
export function _throwUnresolvedMemberChainError(literal, markerIndex, mode, pathSuffix, hint, source) {
|
|
119
71
|
throwZenithRuntimeError({
|
|
120
72
|
phase: 'bind',
|
|
121
73
|
code: 'UNRESOLVED_EXPRESSION',
|
|
122
|
-
message: `Failed to resolve
|
|
74
|
+
message: `Failed to resolve literal: ${_truncateLiteralForError(literal)}`,
|
|
123
75
|
marker: {
|
|
124
76
|
type: _markerTypeForError(mode),
|
|
125
77
|
id: markerIndex
|
|
@@ -134,53 +86,53 @@ export function _resolveStrictMemberChainLiteral(literal, stateValues, stateKeys
|
|
|
134
86
|
if (typeof literal !== 'string' || !STRICT_MEMBER_CHAIN_LITERAL_RE.test(literal)) {
|
|
135
87
|
return UNRESOLVED_LITERAL;
|
|
136
88
|
}
|
|
137
|
-
// Primitives are handled by _resolvePrimitiveLiteral before this function
|
|
138
89
|
if (literal === 'true' || literal === 'false' || literal === 'null' || literal === 'undefined') {
|
|
139
90
|
return UNRESOLVED_LITERAL;
|
|
140
91
|
}
|
|
141
92
|
const segments = literal.split('.');
|
|
142
93
|
const baseIdentifier = segments[0];
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
94
|
+
const baseValue = _resolveStrictBase(baseIdentifier, stateValues, stateKeys, params, ssrData, props);
|
|
95
|
+
if (baseValue === UNRESOLVED_LITERAL) {
|
|
148
96
|
return UNRESOLVED_LITERAL;
|
|
149
97
|
}
|
|
150
|
-
const
|
|
151
|
-
|
|
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];
|
|
98
|
+
const failResolve = (suffix, hint) => _throwUnresolvedMemberChainError(literal, markerIndex, mode, `expression.${suffix}`, hint, source);
|
|
99
|
+
let cursor = baseValue;
|
|
155
100
|
let traversedPath = baseIdentifier;
|
|
156
101
|
for (let i = 1; i < segments.length; i++) {
|
|
157
102
|
const segment = segments[i];
|
|
158
103
|
if (UNSAFE_MEMBER_KEYS.has(segment)) {
|
|
159
|
-
|
|
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
|
-
});
|
|
104
|
+
_tuma(literal, markerIndex, segment, source);
|
|
168
105
|
}
|
|
169
106
|
if (cursor === null || cursor === undefined) {
|
|
170
|
-
|
|
107
|
+
failResolve(`${traversedPath}.${segment}`, `Cannot read "${segment}" from ${traversedPath}; value is null/undefined.`);
|
|
171
108
|
}
|
|
172
109
|
const cursorType = typeof cursor;
|
|
173
110
|
if (cursorType !== 'object' && cursorType !== 'function') {
|
|
174
|
-
|
|
111
|
+
failResolve(`${traversedPath}.${segment}`, `Cannot read "${segment}" from ${traversedPath}; value is ${cursorType}.`);
|
|
175
112
|
}
|
|
176
|
-
if (!
|
|
177
|
-
|
|
113
|
+
if (!OWN.call(cursor, segment)) {
|
|
114
|
+
failResolve(`${traversedPath}.${segment}`, `Missing member "${segment}" on ${traversedPath}.`);
|
|
178
115
|
}
|
|
179
116
|
cursor = cursor[segment];
|
|
180
117
|
traversedPath = `${traversedPath}.${segment}`;
|
|
181
118
|
}
|
|
182
119
|
return cursor;
|
|
183
120
|
}
|
|
121
|
+
function _resolveStrictBase(baseIdentifier, stateValues, stateKeys, params, ssrData, props) {
|
|
122
|
+
if (baseIdentifier === '__zenith_fragment')
|
|
123
|
+
return _fragment;
|
|
124
|
+
if (CANONICAL_MEMBER_CHAIN_BASES.has(baseIdentifier)) {
|
|
125
|
+
if (baseIdentifier === 'props')
|
|
126
|
+
return props || {};
|
|
127
|
+
if (baseIdentifier === 'params')
|
|
128
|
+
return params;
|
|
129
|
+
return ssrData;
|
|
130
|
+
}
|
|
131
|
+
if (!Array.isArray(stateKeys))
|
|
132
|
+
return UNRESOLVED_LITERAL;
|
|
133
|
+
const stateIndex = stateKeys.indexOf(baseIdentifier);
|
|
134
|
+
return stateIndex === -1 ? UNRESOLVED_LITERAL : stateValues[stateIndex];
|
|
135
|
+
}
|
|
184
136
|
export function _resolvePrimitiveLiteral(literal) {
|
|
185
137
|
if (typeof literal !== 'string') {
|
|
186
138
|
return UNRESOLVED_LITERAL;
|
|
@@ -215,45 +167,21 @@ export function _resolvePrimitiveLiteral(literal) {
|
|
|
215
167
|
}
|
|
216
168
|
return UNRESOLVED_LITERAL;
|
|
217
169
|
}
|
|
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
170
|
export function _resolveBindingSource(binding, markerBinding, eventBinding) {
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
}
|
|
171
|
+
const source = binding?.source;
|
|
172
|
+
if (_isSourceSpan(source))
|
|
173
|
+
return source;
|
|
174
|
+
const eventSource = eventBinding?.source;
|
|
175
|
+
if (_isSourceSpan(eventSource))
|
|
176
|
+
return eventSource;
|
|
177
|
+
const markerSource = markerBinding?.source;
|
|
178
|
+
if (_isSourceSpan(markerSource))
|
|
179
|
+
return markerSource;
|
|
255
180
|
return undefined;
|
|
256
181
|
}
|
|
182
|
+
function _isSourceSpan(value) {
|
|
183
|
+
return Boolean(value) && typeof value === 'object' && typeof value.file === 'string';
|
|
184
|
+
}
|
|
257
185
|
export function _describeBindingExpression(binding) {
|
|
258
186
|
if (!binding || typeof binding !== 'object') {
|
|
259
187
|
return '<unknown>';
|
|
@@ -286,10 +214,60 @@ export function _truncateLiteralForError(str) {
|
|
|
286
214
|
return String(str);
|
|
287
215
|
const sanitized = str
|
|
288
216
|
.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>');
|
|
217
|
+
.replace(/\/(?:Users|home|private|tmp|var\/folders)\/[^\s"'`]+/g, '<path>');
|
|
294
218
|
return sanitized.length > 100 ? `${sanitized.substring(0, 97)}...` : sanitized;
|
|
295
219
|
}
|
|
220
|
+
function _rvm(value, mode) {
|
|
221
|
+
if (mode === 'event') {
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
if (value && typeof value === 'object' && typeof value.get === 'function') {
|
|
225
|
+
return value.get();
|
|
226
|
+
}
|
|
227
|
+
if (typeof value === 'function') {
|
|
228
|
+
return value();
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
function _rcb(binding, componentBindings) {
|
|
233
|
+
const instanceBindings = componentBindings[binding.component_instance];
|
|
234
|
+
if (!instanceBindings || !OWN.call(instanceBindings, binding.component_binding)) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
return instanceBindings[binding.component_binding];
|
|
238
|
+
}
|
|
239
|
+
function _rcr(literal, params, ssrData, props) {
|
|
240
|
+
if (literal === 'data' || literal === 'ssr')
|
|
241
|
+
return ssrData;
|
|
242
|
+
if (literal === 'params')
|
|
243
|
+
return params;
|
|
244
|
+
if (literal === 'props')
|
|
245
|
+
return props;
|
|
246
|
+
return UNRESOLVED_LITERAL;
|
|
247
|
+
}
|
|
248
|
+
function _tenl(literal, markerIndex, mode, source) {
|
|
249
|
+
throwZenithRuntimeError({
|
|
250
|
+
phase: 'bind',
|
|
251
|
+
code: 'EXPRESSION_NOT_LOWERED',
|
|
252
|
+
message: `Expression literal was not lowered by the compiler: ${_truncateLiteralForError(literal)}`,
|
|
253
|
+
marker: {
|
|
254
|
+
type: _markerTypeForError(mode),
|
|
255
|
+
id: markerIndex
|
|
256
|
+
},
|
|
257
|
+
path: `expression[${markerIndex}]`,
|
|
258
|
+
hint: 'Lower expression to fn_index/signal_index/state_index.',
|
|
259
|
+
docsLink: DOCS_LINKS.expressionScope,
|
|
260
|
+
source
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function _tuma(literal, markerIndex, member, source) {
|
|
264
|
+
throwZenithRuntimeError({
|
|
265
|
+
phase: 'bind',
|
|
266
|
+
code: 'UNSAFE_MEMBER_ACCESS',
|
|
267
|
+
message: `Blocked unsafe member access: ${member} in path "${literal}"`,
|
|
268
|
+
path: `marker[${markerIndex}].expression.${literal}`,
|
|
269
|
+
hint: 'Property access to __proto__/prototype/constructor is forbidden.',
|
|
270
|
+
docsLink: DOCS_LINKS.expressionScope,
|
|
271
|
+
source
|
|
272
|
+
});
|
|
273
|
+
}
|
package/dist/hydrate.js
CHANGED
|
@@ -10,7 +10,7 @@ import { createNodeResolver } from './scanner.js';
|
|
|
10
10
|
import { bindEventMarkers } from './events.js';
|
|
11
11
|
import { _applyMarkerValue } from './render.js';
|
|
12
12
|
export { _createContextualFragment, _coerceText } from './render.js';
|
|
13
|
-
// Raw HTML boundary
|
|
13
|
+
// Raw HTML boundary lock lives in render.js:
|
|
14
14
|
// attrName.toLowerCase() === 'innerhtml'
|
|
15
15
|
// innerHTML bindings are forbidden in Zenith
|
|
16
16
|
// attrName.toLowerCase() === 'unsafehtml'
|
|
@@ -19,7 +19,7 @@ export function hydrate(payload) {
|
|
|
19
19
|
try {
|
|
20
20
|
const normalized = _validatePayload(payload);
|
|
21
21
|
_deepFreezePayload(payload);
|
|
22
|
-
const { root, expressions, markers, events, refs, stateValues, stateKeys, signals, components,
|
|
22
|
+
const { root, expressions, markers, events, refs, stateValues, stateKeys, signals, components, params, ssrData, props, exprFns } = normalized;
|
|
23
23
|
const componentBindings = Object.create(null);
|
|
24
24
|
const resolveNodes = createNodeResolver(root);
|
|
25
25
|
const signalMap = _createSignalMap(signals, stateValues);
|
|
@@ -28,12 +28,10 @@ export function hydrate(payload) {
|
|
|
28
28
|
components,
|
|
29
29
|
signalMap,
|
|
30
30
|
componentBindings,
|
|
31
|
-
route,
|
|
32
31
|
resolveNodes
|
|
33
32
|
});
|
|
34
|
-
const
|
|
33
|
+
const runtimeContext = {
|
|
35
34
|
expressions,
|
|
36
|
-
markers,
|
|
37
35
|
stateValues,
|
|
38
36
|
stateKeys,
|
|
39
37
|
signalMap,
|
|
@@ -43,22 +41,16 @@ export function hydrate(payload) {
|
|
|
43
41
|
props,
|
|
44
42
|
exprFns,
|
|
45
43
|
resolveNodes
|
|
44
|
+
};
|
|
45
|
+
const markerState = _hydrateMarkers({
|
|
46
|
+
...runtimeContext,
|
|
47
|
+
markers
|
|
46
48
|
});
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
root,
|
|
50
|
-
expressions,
|
|
51
|
-
stateValues,
|
|
52
|
-
stateKeys,
|
|
53
|
-
signalMap,
|
|
54
|
-
componentBindings,
|
|
55
|
-
params,
|
|
56
|
-
ssrData,
|
|
57
|
-
props,
|
|
58
|
-
exprFns,
|
|
59
|
-
resolveNodes,
|
|
49
|
+
const renderContext = {
|
|
50
|
+
...runtimeContext,
|
|
60
51
|
...markerState
|
|
61
|
-
}
|
|
52
|
+
};
|
|
53
|
+
const renderMarkerByIndex = (index) => _renderMarker(renderContext, index);
|
|
62
54
|
_bindSignalSubscriptions(expressions, signalMap, stateValues, renderMarkerByIndex);
|
|
63
55
|
_bindComponentSignalSubscriptions(expressions, componentBindings, renderMarkerByIndex);
|
|
64
56
|
bindEventMarkers({
|
|
@@ -82,7 +74,7 @@ export function hydrate(payload) {
|
|
|
82
74
|
rethrowZenithRuntimeError(error, {
|
|
83
75
|
phase: 'hydrate',
|
|
84
76
|
code: 'BINDING_APPLY_FAILED',
|
|
85
|
-
hint: 'Inspect marker tables, expression bindings, and
|
|
77
|
+
hint: 'Inspect marker tables, expression bindings, and runtime diagnostics.',
|
|
86
78
|
docsLink: DOCS_LINKS.markerTable
|
|
87
79
|
});
|
|
88
80
|
}
|
|
@@ -121,13 +113,10 @@ function _hydrateRefs(refs, stateValues, resolveNodes) {
|
|
|
121
113
|
});
|
|
122
114
|
}
|
|
123
115
|
function _mountComponents(context) {
|
|
124
|
-
const { components, signalMap, componentBindings,
|
|
116
|
+
const { components, signalMap, componentBindings, resolveNodes } = context;
|
|
125
117
|
for (let i = 0; i < components.length; i++) {
|
|
126
118
|
const component = components[i];
|
|
127
|
-
const resolvedProps = Object.freeze(_resolveComponentProps(component.props || [], signalMap
|
|
128
|
-
component: component.instance,
|
|
129
|
-
route
|
|
130
|
-
}));
|
|
119
|
+
const resolvedProps = Object.freeze(_resolveComponentProps(component.props || [], signalMap));
|
|
131
120
|
const hosts = resolveNodes(component.selector, i, 'component', component.source);
|
|
132
121
|
for (let j = 0; j < hosts.length; j++) {
|
|
133
122
|
try {
|
|
@@ -176,7 +165,7 @@ function _mountComponents(context) {
|
|
|
176
165
|
code: 'COMPONENT_BOOTSTRAP_FAILED',
|
|
177
166
|
message: `Component bootstrap failed for "${component.instance}"`,
|
|
178
167
|
path: `component[${component.instance}]`,
|
|
179
|
-
hint: 'Fix the failing component and refresh
|
|
168
|
+
hint: 'Fix the failing component and refresh.',
|
|
180
169
|
docsLink: DOCS_LINKS.componentBootstrap,
|
|
181
170
|
source: component.source
|
|
182
171
|
});
|
|
@@ -188,49 +177,31 @@ function _mountComponents(context) {
|
|
|
188
177
|
}
|
|
189
178
|
}
|
|
190
179
|
function _hydrateMarkers(context) {
|
|
191
|
-
const { expressions, markers,
|
|
180
|
+
const { expressions, markers, resolveNodes } = context;
|
|
192
181
|
const markerByIndex = new Map();
|
|
193
182
|
const markerNodesByIndex = new Map();
|
|
194
|
-
const markerIndices = new Set();
|
|
195
|
-
const expressionMarkerIndices = new Set();
|
|
196
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
197
|
-
const expression = expressions[i];
|
|
198
|
-
if (expressionMarkerIndices.has(expression.marker_index)) {
|
|
199
|
-
throw new Error(`[Zenith Runtime] duplicate expression marker_index ${expression.marker_index}`);
|
|
200
|
-
}
|
|
201
|
-
expressionMarkerIndices.add(expression.marker_index);
|
|
202
|
-
}
|
|
203
183
|
for (let i = 0; i < markers.length; i++) {
|
|
204
184
|
const marker = markers[i];
|
|
205
|
-
if (markerIndices.has(marker.index)) {
|
|
206
|
-
throw new Error(`[Zenith Runtime] duplicate marker index ${marker.index}`);
|
|
207
|
-
}
|
|
208
|
-
markerIndices.add(marker.index);
|
|
209
185
|
markerByIndex.set(marker.index, marker);
|
|
210
186
|
if (marker.kind === 'event') {
|
|
211
187
|
continue;
|
|
212
188
|
}
|
|
213
189
|
const nodes = resolveNodes(marker.selector, marker.index, marker.kind, marker.source);
|
|
214
190
|
markerNodesByIndex.set(marker.index, nodes);
|
|
215
|
-
const value =
|
|
191
|
+
const value = _evaluateMarkerBinding(context, expressions[marker.index], marker);
|
|
216
192
|
_applyMarkerValue(nodes, marker, value);
|
|
217
193
|
}
|
|
218
|
-
for (let i = 0; i < expressions.length; i++) {
|
|
219
|
-
if (!markerIndices.has(i)) {
|
|
220
|
-
throw new Error(`[Zenith Runtime] missing marker index ${i}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
194
|
return { markerByIndex, markerNodesByIndex };
|
|
224
195
|
}
|
|
225
|
-
function _renderMarker(context) {
|
|
226
|
-
const marker = context.markerByIndex.get(
|
|
196
|
+
function _renderMarker(context, index) {
|
|
197
|
+
const marker = context.markerByIndex.get(index);
|
|
227
198
|
if (!marker || marker.kind === 'event') {
|
|
228
199
|
return;
|
|
229
200
|
}
|
|
230
|
-
const nodes = context.markerNodesByIndex.get(
|
|
201
|
+
const nodes = context.markerNodesByIndex.get(index)
|
|
231
202
|
|| context.resolveNodes(marker.selector, marker.index, marker.kind, marker.source);
|
|
232
|
-
context.markerNodesByIndex.set(
|
|
233
|
-
const value =
|
|
203
|
+
context.markerNodesByIndex.set(index, nodes);
|
|
204
|
+
const value = _evaluateMarkerBinding(context, context.expressions[index], marker);
|
|
234
205
|
_applyMarkerValue(nodes, marker, value);
|
|
235
206
|
}
|
|
236
207
|
function _isSignalLike(candidate) {
|
|
@@ -265,16 +236,7 @@ function _bindSignalSubscriptions(expressions, signalMap, stateValues, renderMar
|
|
|
265
236
|
_recordSignalMarkerDependency(dependentMarkersBySignal, stateValues[expression.state_index], expression.marker_index);
|
|
266
237
|
}
|
|
267
238
|
}
|
|
268
|
-
|
|
269
|
-
const unsubscribe = targetSignal.subscribe(() => {
|
|
270
|
-
for (let i = 0; i < markerIndexes.length; i++) {
|
|
271
|
-
renderMarkerByIndex(markerIndexes[i]);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
if (typeof unsubscribe === 'function') {
|
|
275
|
-
_registerDisposer(unsubscribe);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
239
|
+
_subscribeMarkerDependencies(dependentMarkersBySignal, renderMarkerByIndex);
|
|
278
240
|
}
|
|
279
241
|
function _bindComponentSignalSubscriptions(expressions, componentBindings, renderMarkerByIndex) {
|
|
280
242
|
const dependentMarkersByComponentSignal = new Map();
|
|
@@ -285,10 +247,7 @@ function _bindComponentSignalSubscriptions(expressions, componentBindings, rende
|
|
|
285
247
|
}
|
|
286
248
|
const instanceBindings = componentBindings[expression.component_instance];
|
|
287
249
|
const candidate = instanceBindings?.[expression.component_binding];
|
|
288
|
-
if (!candidate
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
if (typeof candidate.get !== 'function' || typeof candidate.subscribe !== 'function') {
|
|
250
|
+
if (!_isSignalLike(candidate)) {
|
|
292
251
|
continue;
|
|
293
252
|
}
|
|
294
253
|
if (!dependentMarkersByComponentSignal.has(candidate)) {
|
|
@@ -296,8 +255,14 @@ function _bindComponentSignalSubscriptions(expressions, componentBindings, rende
|
|
|
296
255
|
}
|
|
297
256
|
dependentMarkersByComponentSignal.get(candidate).push(expression.marker_index);
|
|
298
257
|
}
|
|
299
|
-
|
|
300
|
-
|
|
258
|
+
_subscribeMarkerDependencies(dependentMarkersByComponentSignal, renderMarkerByIndex);
|
|
259
|
+
}
|
|
260
|
+
function _evaluateMarkerBinding(context, expression, marker) {
|
|
261
|
+
return _evaluateExpression(expression, context.stateValues, context.stateKeys, context.signalMap, context.componentBindings, context.params, context.ssrData, marker.kind, context.props, context.exprFns, marker, null);
|
|
262
|
+
}
|
|
263
|
+
function _subscribeMarkerDependencies(dependencies, renderMarkerByIndex) {
|
|
264
|
+
for (const [targetSignal, markerIndexes] of dependencies.entries()) {
|
|
265
|
+
const unsubscribe = targetSignal.subscribe(() => {
|
|
301
266
|
for (let i = 0; i < markerIndexes.length; i++) {
|
|
302
267
|
renderMarkerByIndex(markerIndexes[i]);
|
|
303
268
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ export { signal } from "./signal.js";
|
|
|
2
2
|
export { state } from "./state.js";
|
|
3
3
|
export { hydrate } from "./hydrate.js";
|
|
4
4
|
export { effect, mount, zeneffect, zenEffect, zenMount } from "./zeneffect.js";
|
|
5
|
+
export { zenPresence, presence } from "./presence.js";
|
|
5
6
|
export { window, document, zenWindow, zenDocument } from "./env.js";
|
|
6
7
|
export { zenOn, zenResize, collectRefs } from "./platform.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { signal } from './signal.js';
|
|
2
2
|
export { state } from './state.js';
|
|
3
3
|
export { effect, mount, zeneffect, zenEffect, zenMount } from './zeneffect.js';
|
|
4
|
+
export { zenPresence, presence } from './presence.js';
|
|
4
5
|
export { hydrate } from './hydrate.js';
|
|
5
6
|
export { window, document, zenWindow, zenDocument } from './env.js';
|
|
6
7
|
export { zenOn, zenResize, collectRefs } from './platform.js';
|
package/dist/markup.js
CHANGED
|
@@ -30,7 +30,7 @@ export function _fragment(strings, ...values) {
|
|
|
30
30
|
phase: 'render',
|
|
31
31
|
code: 'NON_RENDERABLE_VALUE',
|
|
32
32
|
message: '__zenith_fragment must be called as a tagged template literal',
|
|
33
|
-
hint: 'This helper only accepts tagged
|
|
33
|
+
hint: 'This helper only accepts tagged templates.'
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
let result = '';
|
|
@@ -46,7 +46,7 @@ export function _fragment(strings, ...values) {
|
|
|
46
46
|
phase: 'render',
|
|
47
47
|
code: 'NON_RENDERABLE_VALUE',
|
|
48
48
|
message: 'Embedded markup expression contains forbidden <script> tag',
|
|
49
|
-
hint: 'Script tags are not allowed in embedded markup
|
|
49
|
+
hint: 'Script tags are not allowed in embedded markup.'
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
if (_FRAGMENT_EVENT_ATTR_RE.test(result)) {
|
|
@@ -54,7 +54,7 @@ export function _fragment(strings, ...values) {
|
|
|
54
54
|
phase: 'render',
|
|
55
55
|
code: 'NON_RENDERABLE_VALUE',
|
|
56
56
|
message: 'Embedded markup expression contains inline event handler (on*=)',
|
|
57
|
-
hint: 'Use on:event={handler}
|
|
57
|
+
hint: 'Use on:event={handler} instead of inline event attributes.'
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
if (_FRAGMENT_JS_URL_RE.test(result)) {
|
|
@@ -62,7 +62,7 @@ export function _fragment(strings, ...values) {
|
|
|
62
62
|
phase: 'render',
|
|
63
63
|
code: 'NON_RENDERABLE_VALUE',
|
|
64
64
|
message: 'Embedded markup expression contains javascript: URL',
|
|
65
|
-
hint: 'javascript: URLs are forbidden
|
|
65
|
+
hint: 'javascript: URLs are forbidden.'
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
result = result.replace(_FRAGMENT_SCRIPT_CLOSE_RE, '<\\/script');
|
|
@@ -99,7 +99,7 @@ function _fragmentInterpolate(val, interpolationIndex) {
|
|
|
99
99
|
phase: 'render',
|
|
100
100
|
code: 'NON_RENDERABLE_VALUE',
|
|
101
101
|
message: `Embedded markup interpolation[${interpolationIndex}] contains forbidden key "${keys[k]}"`,
|
|
102
|
-
hint: 'Prototype pollution keys are forbidden in embedded markup
|
|
102
|
+
hint: 'Prototype pollution keys are forbidden in embedded markup.'
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -107,14 +107,14 @@ function _fragmentInterpolate(val, interpolationIndex) {
|
|
|
107
107
|
phase: 'render',
|
|
108
108
|
code: 'NON_RENDERABLE_VALUE',
|
|
109
109
|
message: `Embedded markup interpolation[${interpolationIndex}] resolved to a non-renderable object`,
|
|
110
|
-
hint: 'Only
|
|
110
|
+
hint: 'Only primitives, arrays, and compiler-owned fragments are allowed.'
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
throwZenithRuntimeError({
|
|
114
114
|
phase: 'render',
|
|
115
115
|
code: 'NON_RENDERABLE_VALUE',
|
|
116
116
|
message: `Embedded markup interpolation[${interpolationIndex}] resolved to type "${typeof val}"`,
|
|
117
|
-
hint: 'Only
|
|
117
|
+
hint: 'Only primitives, arrays, and compiler-owned fragments are allowed.'
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
function _escapeFragmentHtml(input) {
|
package/dist/payload.d.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
export function _validatePayload(payload: any):
|
|
1
|
+
export function _validatePayload(payload: any): {
|
|
2
2
|
root: any;
|
|
3
|
-
expressions: any
|
|
4
|
-
markers: any
|
|
5
|
-
events: any
|
|
3
|
+
expressions: any;
|
|
4
|
+
markers: any;
|
|
5
|
+
events: any;
|
|
6
6
|
refs: any;
|
|
7
|
-
stateValues: any
|
|
8
|
-
stateKeys: any
|
|
9
|
-
signals: any
|
|
7
|
+
stateValues: any;
|
|
8
|
+
stateKeys: any;
|
|
9
|
+
signals: any;
|
|
10
10
|
components: any;
|
|
11
|
-
route: any;
|
|
12
11
|
params: any;
|
|
13
12
|
ssrData: any;
|
|
14
13
|
props: any;
|
|
15
14
|
exprFns: any;
|
|
16
|
-
}
|
|
17
|
-
export function _resolveComponentProps(propTable: any, signalMap: any
|
|
15
|
+
};
|
|
16
|
+
export function _resolveComponentProps(propTable: any, signalMap: any): any;
|
|
18
17
|
export function _deepFreezePayload(obj: any): void;
|
|
19
18
|
export function _isHydrationRefObject(obj: any): boolean;
|
|
20
19
|
export function _isPlainObject(value: any): boolean;
|