@zenithbuild/runtime 0.7.5 → 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/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/markup.js +7 -7
- package/dist/payload.d.ts +9 -10
- package/dist/payload.js +128 -219
- 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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function isZenithRuntimeError(error: any): boolean;
|
|
2
|
+
export function createZenithRuntimeError(details: any, cause: any): Error;
|
|
3
|
+
export function throwZenithRuntimeError(details: any, cause: any): void;
|
|
4
|
+
export function rethrowZenithRuntimeError(error: any, fallback?: {}): void;
|
|
5
|
+
export const DOCS_LINKS: Readonly<{
|
|
6
|
+
eventBinding: "/docs/documentation/contracts/runtime-contract.md#event-bindings";
|
|
7
|
+
expressionScope: "/docs/documentation/reference/reactive-binding-model.md#expression-resolution";
|
|
8
|
+
markerTable: "/docs/documentation/reference/markers.md";
|
|
9
|
+
componentBootstrap: "/docs/documentation/contracts/runtime-contract.md#component-bootstrap";
|
|
10
|
+
refs: "/docs/documentation/reference/reactive-binding-model.md#refs-and-mount";
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const MAX_MESSAGE_LENGTH = 120;
|
|
2
|
+
const MAX_HINT_LENGTH = 140;
|
|
3
|
+
const MAX_PATH_LENGTH = 120;
|
|
4
|
+
const MAX_DOCS_LINK_LENGTH = 180;
|
|
5
|
+
const MAX_SNIPPET_LENGTH = 220;
|
|
6
|
+
const MAX_STACK_LENGTH = 420;
|
|
7
|
+
const VALID_PHASES = Object.freeze(Object.assign(Object.create(null), {
|
|
8
|
+
hydrate: 1,
|
|
9
|
+
bind: 1,
|
|
10
|
+
render: 1,
|
|
11
|
+
event: 1
|
|
12
|
+
}));
|
|
13
|
+
const VALID_CODES = Object.freeze(Object.assign(Object.create(null), {
|
|
14
|
+
UNRESOLVED_EXPRESSION: 1,
|
|
15
|
+
NON_RENDERABLE_VALUE: 1,
|
|
16
|
+
MARKER_MISSING: 1,
|
|
17
|
+
FRAGMENT_MOUNT_FAILED: 1,
|
|
18
|
+
BINDING_APPLY_FAILED: 1,
|
|
19
|
+
EVENT_HANDLER_FAILED: 1,
|
|
20
|
+
COMPONENT_BOOTSTRAP_FAILED: 1,
|
|
21
|
+
UNSAFE_MEMBER_ACCESS: 1
|
|
22
|
+
}));
|
|
23
|
+
const DOCS_EXPRESSION_SCOPE = '/docs/documentation/reference/reactive-binding-model.md#expression-resolution';
|
|
24
|
+
const DOCS_RENDERABLE_VALUES = '/docs/documentation/reference/reactive-binding-model.md#renderable-values';
|
|
25
|
+
const DOCS_MARKERS = '/docs/documentation/reference/markers.md';
|
|
26
|
+
const DOCS_FRAGMENT_CONTRACT = '/docs/documentation/contracts/runtime-contract.md#fragment-contract';
|
|
27
|
+
const DOCS_BINDING_APPLICATION = '/docs/documentation/contracts/runtime-contract.md#binding-application';
|
|
28
|
+
const DOCS_EVENT_BINDINGS = '/docs/documentation/contracts/runtime-contract.md#event-bindings';
|
|
29
|
+
const DOCS_COMPONENT_BOOTSTRAP = '/docs/documentation/contracts/runtime-contract.md#component-bootstrap';
|
|
30
|
+
const DOCS_LINK_BY_CODE = Object.freeze({
|
|
31
|
+
UNRESOLVED_EXPRESSION: DOCS_EXPRESSION_SCOPE,
|
|
32
|
+
NON_RENDERABLE_VALUE: DOCS_RENDERABLE_VALUES,
|
|
33
|
+
MARKER_MISSING: DOCS_MARKERS,
|
|
34
|
+
FRAGMENT_MOUNT_FAILED: DOCS_FRAGMENT_CONTRACT,
|
|
35
|
+
BINDING_APPLY_FAILED: DOCS_BINDING_APPLICATION,
|
|
36
|
+
EVENT_HANDLER_FAILED: DOCS_EVENT_BINDINGS,
|
|
37
|
+
COMPONENT_BOOTSTRAP_FAILED: DOCS_COMPONENT_BOOTSTRAP,
|
|
38
|
+
UNSAFE_MEMBER_ACCESS: DOCS_EXPRESSION_SCOPE
|
|
39
|
+
});
|
|
40
|
+
const ABSOLUTE_PATH_RE = /(?:[A-Za-z]:\\[^\s"'`]+|\/(?:Users|home|private|tmp|var\/folders)\/[^\s"'`]+)/g;
|
|
41
|
+
function _truncate(input, maxLength) {
|
|
42
|
+
const text = String(input ?? '');
|
|
43
|
+
if (text.length <= maxLength)
|
|
44
|
+
return text;
|
|
45
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
46
|
+
}
|
|
47
|
+
function _sanitizeAbsolutePaths(value) {
|
|
48
|
+
return String(value ?? '').replace(ABSOLUTE_PATH_RE, '<path>');
|
|
49
|
+
}
|
|
50
|
+
function _compact(value, sanitizePaths = true) {
|
|
51
|
+
const text = sanitizePaths ? _sanitizeAbsolutePaths(value) : String(value ?? '');
|
|
52
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
53
|
+
}
|
|
54
|
+
function _sanitizeOptionalText(value, maxLength, sanitizePaths = true) {
|
|
55
|
+
if (value === null || value === undefined || value === false) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const compact = _compact(value, sanitizePaths);
|
|
59
|
+
if (!compact)
|
|
60
|
+
return undefined;
|
|
61
|
+
return _truncate(compact, maxLength);
|
|
62
|
+
}
|
|
63
|
+
function _sanitizeMessage(value) {
|
|
64
|
+
return _sanitizeOptionalText(value, MAX_MESSAGE_LENGTH) || 'Runtime failure';
|
|
65
|
+
}
|
|
66
|
+
const _sanitizeHint = (value) => _sanitizeOptionalText(value, MAX_HINT_LENGTH);
|
|
67
|
+
const _sanitizePath = (value) => _sanitizeOptionalText(value, MAX_PATH_LENGTH);
|
|
68
|
+
const _sanitizeDocsLink = (value) => _sanitizeOptionalText(value, MAX_DOCS_LINK_LENGTH, false);
|
|
69
|
+
function _sanitizeSourceLocation(value) {
|
|
70
|
+
if (!value || typeof value !== 'object')
|
|
71
|
+
return undefined;
|
|
72
|
+
const line = Number(value.line);
|
|
73
|
+
const column = Number(value.column);
|
|
74
|
+
if (!Number.isInteger(line) || !Number.isInteger(column)) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (line < 1 || column < 1) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return { line, column };
|
|
81
|
+
}
|
|
82
|
+
function _sanitizeSource(value) {
|
|
83
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
const file = typeof value.file === 'string' ? _truncate(value.file.trim(), 240) : '';
|
|
87
|
+
if (!file) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const start = _sanitizeSourceLocation(value.start);
|
|
91
|
+
const end = _sanitizeSourceLocation(value.end);
|
|
92
|
+
const snippet = typeof value.snippet === 'string'
|
|
93
|
+
? _truncate(_compact(value.snippet, false), MAX_SNIPPET_LENGTH)
|
|
94
|
+
: undefined;
|
|
95
|
+
return {
|
|
96
|
+
file,
|
|
97
|
+
...(start ? { start } : null),
|
|
98
|
+
...(end ? { end } : null),
|
|
99
|
+
...(snippet ? { snippet } : null)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function _normalizeMarker(marker) {
|
|
103
|
+
if (!marker || typeof marker !== 'object')
|
|
104
|
+
return undefined;
|
|
105
|
+
const markerType = _truncate(_compact(marker.type || 'data-zx'), 48);
|
|
106
|
+
const markerId = marker.id;
|
|
107
|
+
if (markerId === null || markerId === undefined || markerId === '')
|
|
108
|
+
return undefined;
|
|
109
|
+
if (typeof markerId === 'number') {
|
|
110
|
+
return { type: markerType, id: markerId };
|
|
111
|
+
}
|
|
112
|
+
return { type: markerType, id: _truncate(_sanitizeAbsolutePaths(markerId), 48) };
|
|
113
|
+
}
|
|
114
|
+
function _extractErrorMessage(error) {
|
|
115
|
+
if (!error)
|
|
116
|
+
return '';
|
|
117
|
+
if (typeof error === 'string')
|
|
118
|
+
return error;
|
|
119
|
+
if (error instanceof Error && typeof error.message === 'string')
|
|
120
|
+
return error.message;
|
|
121
|
+
if (typeof error.message === 'string')
|
|
122
|
+
return error.message;
|
|
123
|
+
return String(error);
|
|
124
|
+
}
|
|
125
|
+
function _readProcessEnv(name) {
|
|
126
|
+
const runtimeProcess = typeof process !== 'undefined' ? process : globalThis?.process;
|
|
127
|
+
const value = runtimeProcess?.env?.[name];
|
|
128
|
+
return typeof value === 'string' ? value : undefined;
|
|
129
|
+
}
|
|
130
|
+
function _shouldLogRuntimeError() {
|
|
131
|
+
if (_readProcessEnv('ZENITH_LOG_RUNTIME_ERRORS') === '1') {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
const isTestMode = _readProcessEnv('NODE_ENV') === 'test'
|
|
135
|
+
|| _readProcessEnv('ZENITH_TEST_MODE') === '1';
|
|
136
|
+
return !isTestMode;
|
|
137
|
+
}
|
|
138
|
+
const _renderOverlay = () => { };
|
|
139
|
+
function _mapLegacyError(error, fallback) {
|
|
140
|
+
const rawMessage = _extractErrorMessage(error);
|
|
141
|
+
const safeMessage = _sanitizeMessage(rawMessage);
|
|
142
|
+
const details = {
|
|
143
|
+
phase: VALID_PHASES[fallback.phase] ? fallback.phase : 'hydrate',
|
|
144
|
+
code: VALID_CODES[fallback.code] ? fallback.code : 'BINDING_APPLY_FAILED',
|
|
145
|
+
message: _sanitizeMessage(fallback.message || safeMessage),
|
|
146
|
+
marker: _normalizeMarker(fallback.marker),
|
|
147
|
+
path: _sanitizePath(fallback.path),
|
|
148
|
+
hint: _sanitizeHint(fallback.hint),
|
|
149
|
+
source: _sanitizeSource(fallback.source),
|
|
150
|
+
docsLink: _sanitizeDocsLink(fallback.docsLink)
|
|
151
|
+
};
|
|
152
|
+
if (/failed to resolve expression literal/i.test(rawMessage)) {
|
|
153
|
+
details.phase = 'bind';
|
|
154
|
+
details.code = 'UNRESOLVED_EXPRESSION';
|
|
155
|
+
details.hint = details.hint || 'Verify expression scope keys and signal aliases.';
|
|
156
|
+
}
|
|
157
|
+
else if (/non-renderable (object|function)/i.test(rawMessage)) {
|
|
158
|
+
details.phase = 'render';
|
|
159
|
+
details.code = 'NON_RENDERABLE_VALUE';
|
|
160
|
+
const match = rawMessage.match(/at\s+([A-Za-z0-9_\[\].-]+)/);
|
|
161
|
+
if (match && !details.path) {
|
|
162
|
+
details.path = _sanitizePath(match[1]);
|
|
163
|
+
}
|
|
164
|
+
details.hint = details.hint || 'Use map() to render object fields into nodes.';
|
|
165
|
+
}
|
|
166
|
+
else if (/unresolved .* marker index/i.test(rawMessage)) {
|
|
167
|
+
details.phase = 'bind';
|
|
168
|
+
details.code = 'MARKER_MISSING';
|
|
169
|
+
const markerMatch = rawMessage.match(/unresolved\s+(\w+)\s+marker index\s+(\d+)/i);
|
|
170
|
+
if (markerMatch && !details.marker) {
|
|
171
|
+
details.marker = {
|
|
172
|
+
type: `data-zx-${markerMatch[1]}`,
|
|
173
|
+
id: Number(markerMatch[2])
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
details.hint = details.hint || 'Confirm SSR markers and client selector tables match.';
|
|
177
|
+
}
|
|
178
|
+
if (!details.docsLink) {
|
|
179
|
+
details.docsLink = DOCS_LINK_BY_CODE[details.code];
|
|
180
|
+
}
|
|
181
|
+
return details;
|
|
182
|
+
}
|
|
183
|
+
export function isZenithRuntimeError(error) {
|
|
184
|
+
return !!(error &&
|
|
185
|
+
typeof error === 'object' &&
|
|
186
|
+
error.zenithRuntimeError &&
|
|
187
|
+
error.zenithRuntimeError.kind === 'ZENITH_RUNTIME_ERROR');
|
|
188
|
+
}
|
|
189
|
+
export function createZenithRuntimeError(details, cause) {
|
|
190
|
+
const phase = VALID_PHASES[details?.phase] ? details.phase : 'hydrate';
|
|
191
|
+
const code = VALID_CODES[details?.code] ? details.code : 'BINDING_APPLY_FAILED';
|
|
192
|
+
const message = _sanitizeMessage(details?.message || 'Runtime failure');
|
|
193
|
+
const docsLink = _sanitizeDocsLink(details?.docsLink || DOCS_LINK_BY_CODE[code]);
|
|
194
|
+
const payload = {
|
|
195
|
+
kind: 'ZENITH_RUNTIME_ERROR',
|
|
196
|
+
phase,
|
|
197
|
+
code,
|
|
198
|
+
message,
|
|
199
|
+
...(docsLink ? { docsLink } : null)
|
|
200
|
+
};
|
|
201
|
+
const marker = _normalizeMarker(details?.marker);
|
|
202
|
+
if (marker)
|
|
203
|
+
payload.marker = marker;
|
|
204
|
+
const path = _sanitizePath(details?.path);
|
|
205
|
+
if (path)
|
|
206
|
+
payload.path = path;
|
|
207
|
+
const hint = _sanitizeHint(details?.hint);
|
|
208
|
+
if (hint)
|
|
209
|
+
payload.hint = hint;
|
|
210
|
+
const source = _sanitizeSource(details?.source);
|
|
211
|
+
if (source)
|
|
212
|
+
payload.source = source;
|
|
213
|
+
const stack = _sanitizeHint(details?.stack);
|
|
214
|
+
if (stack) {
|
|
215
|
+
payload.stack = _truncate(stack, MAX_STACK_LENGTH);
|
|
216
|
+
}
|
|
217
|
+
const error = new Error(`[Zenith Runtime] ${code}: ${message}`);
|
|
218
|
+
error.name = 'ZenithRuntimeError';
|
|
219
|
+
error.zenithRuntimeError = payload;
|
|
220
|
+
if (cause !== undefined) {
|
|
221
|
+
error.cause = cause;
|
|
222
|
+
}
|
|
223
|
+
error.toJSON = () => payload;
|
|
224
|
+
return error;
|
|
225
|
+
}
|
|
226
|
+
function _reportRuntimeError(error) {
|
|
227
|
+
if (!error || error.__zenithRuntimeErrorReported === true)
|
|
228
|
+
return;
|
|
229
|
+
error.__zenithRuntimeErrorReported = true;
|
|
230
|
+
const payload = error.zenithRuntimeError;
|
|
231
|
+
if (payload
|
|
232
|
+
&& _shouldLogRuntimeError()
|
|
233
|
+
&& typeof console !== 'undefined'
|
|
234
|
+
&& typeof console.error === 'function') {
|
|
235
|
+
console.error('[Zenith Runtime]', payload);
|
|
236
|
+
}
|
|
237
|
+
_renderOverlay(payload);
|
|
238
|
+
}
|
|
239
|
+
export function throwZenithRuntimeError(details, cause) {
|
|
240
|
+
const error = createZenithRuntimeError(details, cause);
|
|
241
|
+
_reportRuntimeError(error);
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
export function rethrowZenithRuntimeError(error, fallback = {}) {
|
|
245
|
+
if (isZenithRuntimeError(error)) {
|
|
246
|
+
const payload = error.zenithRuntimeError || {};
|
|
247
|
+
let updatedPayload = payload;
|
|
248
|
+
const marker = payload.marker || _normalizeMarker(fallback.marker);
|
|
249
|
+
const path = payload.path || _sanitizePath(fallback.path);
|
|
250
|
+
const hint = payload.hint || _sanitizeHint(fallback.hint);
|
|
251
|
+
const source = payload.source || _sanitizeSource(fallback.source);
|
|
252
|
+
const docsLink = payload.docsLink || _sanitizeDocsLink(fallback.docsLink || DOCS_LINK_BY_CODE[payload.code]);
|
|
253
|
+
if (marker || path || hint || source || docsLink) {
|
|
254
|
+
updatedPayload = {
|
|
255
|
+
...payload,
|
|
256
|
+
...(marker ? { marker } : null),
|
|
257
|
+
...(path ? { path } : null),
|
|
258
|
+
...(hint ? { hint } : null),
|
|
259
|
+
...(source ? { source } : null),
|
|
260
|
+
...(docsLink ? { docsLink } : null)
|
|
261
|
+
};
|
|
262
|
+
error.zenithRuntimeError = updatedPayload;
|
|
263
|
+
error.toJSON = () => updatedPayload;
|
|
264
|
+
}
|
|
265
|
+
_reportRuntimeError(error);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
const mapped = _mapLegacyError(error, fallback || {});
|
|
269
|
+
const wrapped = createZenithRuntimeError(mapped, error);
|
|
270
|
+
_reportRuntimeError(wrapped);
|
|
271
|
+
throw wrapped;
|
|
272
|
+
}
|
|
273
|
+
export const DOCS_LINKS = Object.freeze({
|
|
274
|
+
eventBinding: DOCS_EVENT_BINDINGS,
|
|
275
|
+
expressionScope: DOCS_EXPRESSION_SCOPE,
|
|
276
|
+
markerTable: DOCS_MARKERS,
|
|
277
|
+
componentBootstrap: DOCS_COMPONENT_BOOTSTRAP,
|
|
278
|
+
refs: '/docs/documentation/reference/reactive-binding-model.md#refs-and-mount'
|
|
279
|
+
});
|
package/dist/events.js
CHANGED
|
@@ -40,7 +40,7 @@ function _resolveEventHandler(context, expressionBinding, marker, eventBinding)
|
|
|
40
40
|
message: `Event binding at index ${eventBinding.index} expected a function reference. You passed: ${_describeBindingExpression(expressionBinding)}`,
|
|
41
41
|
marker: { type: `data-zx-on-${eventBinding.event}`, id: eventBinding.index },
|
|
42
42
|
path: `event[${eventBinding.index}].${eventBinding.event}`,
|
|
43
|
-
hint: 'Use on:*={handler}
|
|
43
|
+
hint: 'Use on:*={handler}; forwarded props must be functions.',
|
|
44
44
|
docsLink: DOCS_LINKS.eventBinding,
|
|
45
45
|
source: _resolveBindingSource(expressionBinding, marker, eventBinding)
|
|
46
46
|
});
|
|
@@ -57,7 +57,7 @@ function _createWrappedEventHandler(handler, expressionBinding, marker, eventBin
|
|
|
57
57
|
message: `Event handler failed for "${eventBinding.event}"`,
|
|
58
58
|
marker: { type: `data-zx-on-${eventBinding.event}`, id: eventBinding.index },
|
|
59
59
|
path: `event[${eventBinding.index}].${eventBinding.event}`,
|
|
60
|
-
hint: 'Inspect
|
|
60
|
+
hint: 'Inspect handler body and referenced state.',
|
|
61
61
|
docsLink: DOCS_LINKS.eventBinding,
|
|
62
62
|
source: _resolveBindingSource(expressionBinding, marker, eventBinding)
|
|
63
63
|
});
|
package/dist/expressions.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export function _evaluateExpression(binding: any, stateValues: any, stateKeys: a
|
|
|
3
3
|
export function _throwUnresolvedMemberChainError(literal: any, markerIndex: any, mode: any, pathSuffix: any, hint: any, source: any): void;
|
|
4
4
|
export function _resolveStrictMemberChainLiteral(literal: any, stateValues: any, stateKeys: any, params: any, ssrData: any, mode: any, props: any, markerIndex: any, source: any): any;
|
|
5
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
6
|
export function _resolveBindingSource(binding: any, markerBinding: any, eventBinding: any): any;
|
|
8
7
|
export function _describeBindingExpression(binding: any): string;
|
|
9
8
|
export function _markerTypeForError(kind: any): any;
|
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
|
+
}
|