@altopelago/aeon-aes 0.9.0

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/modes.js ADDED
@@ -0,0 +1,801 @@
1
+ /**
2
+ * Phase 7 — Mode Enforcement
3
+ *
4
+ * Enforces:
5
+ * - Transport vs Strict mode typing rules
6
+ * - Toggle typing rules (no semantic toggle unless typed)
7
+ * - Header correctness
8
+ *
9
+ * Non-negotiable constraints:
10
+ * - No coercion, ever
11
+ * - No schema execution
12
+ * - No reference resolution
13
+ * - Fail-closed by default
14
+ */
15
+ import { formatPath } from './paths.js';
16
+ import { formatReferenceTargetPath } from './reference-target.js';
17
+ /**
18
+ * Mode enforcement error
19
+ */
20
+ export class ModeEnforcementError extends Error {
21
+ span;
22
+ code;
23
+ path;
24
+ constructor(message, span, code, path) {
25
+ super(message);
26
+ this.name = 'ModeEnforcementError';
27
+ this.span = span;
28
+ this.code = code;
29
+ this.path = path;
30
+ }
31
+ }
32
+ /**
33
+ * Error: Untyped value in typed mode
34
+ */
35
+ export class UntypedValueInStrictModeError extends ModeEnforcementError {
36
+ constructor(span, path) {
37
+ super(`Untyped value in typed mode: '${path}' requires explicit type annotation`, span, 'UNTYPED_VALUE_IN_STRICT_MODE', path);
38
+ this.name = 'UntypedValueInStrictModeError';
39
+ }
40
+ }
41
+ /**
42
+ * Error: toggle literal requires :toggle in typed mode
43
+ */
44
+ export class UntypedToggleLiteralError extends ModeEnforcementError {
45
+ constructor(span, path) {
46
+ super(`Untyped toggle literal in typed mode: '${path}' requires ':toggle' type annotation`, span, 'UNTYPED_TOGGLE_LITERAL', path);
47
+ this.name = 'UntypedToggleLiteralError';
48
+ }
49
+ }
50
+ export class CustomToggleAliasNotAllowedError extends ModeEnforcementError {
51
+ constructor(span, path, datatype) {
52
+ super(`Custom toggle alias not allowed at '${path}': use ':toggle' instead of ':${datatype}'`, span, 'CUSTOM_TOGGLE_ALIAS_NOT_ALLOWED', path);
53
+ this.name = 'CustomToggleAliasNotAllowedError';
54
+ }
55
+ }
56
+ /**
57
+ * Error: Structured header and shorthand header used together
58
+ */
59
+ export class HeaderConflictError extends ModeEnforcementError {
60
+ constructor(span) {
61
+ super('Header conflict: cannot use both structured header (aeon:header) and shorthand header fields', span, 'HEADER_CONFLICT', '$');
62
+ this.name = 'HeaderConflictError';
63
+ }
64
+ }
65
+ /**
66
+ * Error: Reserved datatype annotation does not match literal/container kind
67
+ */
68
+ export class DatatypeLiteralMismatchError extends ModeEnforcementError {
69
+ constructor(span, path, datatype, actualKind, expectedKinds) {
70
+ super(`Datatype/literal mismatch at '${path}': datatype ':${datatype}' expects ${expectedKinds.join(' or ')}, got ${actualKind}`, span, 'DATATYPE_LITERAL_MISMATCH', path);
71
+ this.name = 'DatatypeLiteralMismatchError';
72
+ }
73
+ }
74
+ export class InvalidCustomDatatypeBracketShapeError extends ModeEnforcementError {
75
+ constructor(span, path, datatype, actualKind) {
76
+ super(`Datatype/literal mismatch at '${path}': datatype ':${datatype}' has bracket specs incompatible with both SeparatorLiteral and RadixLiteral, got ${actualKind}`, span, 'DATATYPE_LITERAL_MISMATCH', path);
77
+ this.name = 'InvalidCustomDatatypeBracketShapeError';
78
+ }
79
+ }
80
+ export class IncompatibleCustomDatatypeAdornmentsError extends ModeEnforcementError {
81
+ constructor(span, path, datatype, actualKind) {
82
+ super(`Datatype/literal mismatch at '${path}': datatype ':${datatype}' combines incompatible generic and bracket constraints, got ${actualKind}`, span, 'DATATYPE_LITERAL_MISMATCH', path);
83
+ this.name = 'IncompatibleCustomDatatypeAdornmentsError';
84
+ }
85
+ }
86
+ /**
87
+ * Error: Custom datatype is not permitted by typed-mode datatype policy
88
+ */
89
+ export class CustomDatatypeNotAllowedError extends ModeEnforcementError {
90
+ constructor(span, path, datatype) {
91
+ super(`Custom datatype not allowed in typed mode at '${path}': ':${datatype}' requires --datatype-policy allow_custom`, span, 'CUSTOM_DATATYPE_NOT_ALLOWED', path);
92
+ this.name = 'CustomDatatypeNotAllowedError';
93
+ }
94
+ }
95
+ /**
96
+ * Error: Node head datatype must remain :node in strict mode
97
+ */
98
+ export class InvalidNodeHeadDatatypeError extends ModeEnforcementError {
99
+ constructor(span, path, datatype) {
100
+ super(`Invalid node head datatype in strict mode at '${path}': node heads must use ':node', got ':${datatype}'`, span, 'INVALID_NODE_HEAD_DATATYPE', path);
101
+ this.name = 'InvalidNodeHeadDatatypeError';
102
+ }
103
+ }
104
+ /**
105
+ * Extract mode from header
106
+ */
107
+ export function extractMode(header) {
108
+ if (!header) {
109
+ return 'transport'; // Default
110
+ }
111
+ // Check for mode field
112
+ const modeValue = header.fields.get('mode');
113
+ if (modeValue && modeValue.type === 'StringLiteral') {
114
+ const mode = modeValue.value.toLowerCase();
115
+ if (mode === 'strict') {
116
+ return 'strict';
117
+ }
118
+ if (mode === 'custom') {
119
+ return 'custom';
120
+ }
121
+ }
122
+ return 'transport'; // Default
123
+ }
124
+ /**
125
+ * Enforce mode constraints on events
126
+ *
127
+ * In typed modes (`strict` and `custom`):
128
+ * - Every binding must have an explicit type annotation
129
+ *
130
+ * In strict mode only:
131
+ * - Untyped toggle literals (yes/no/on/off) require :toggle type
132
+ *
133
+ * In transport mode:
134
+ * - Untyped values are allowed (stay raw, no semantic interpretation)
135
+ * - Explicit datatype annotations are still validated for compatibility
136
+ * - Custom datatype labels are accepted by default
137
+ */
138
+ export function enforceMode(events, header, options = {}) {
139
+ const mode = extractMode(header);
140
+ const datatypePolicy = options.datatypePolicy ?? defaultDatatypePolicyForMode(mode);
141
+ const errors = [];
142
+ const pathToIndex = new Map();
143
+ for (let i = 0; i < events.length; i++) {
144
+ pathToIndex.set(formatPath(events[i].path), i);
145
+ }
146
+ // Header correctness: structured vs shorthand mutual exclusion
147
+ if (header && header.hasStructured && header.hasShorthand) {
148
+ errors.push(new HeaderConflictError(header.span));
149
+ }
150
+ for (const event of events) {
151
+ // Shorthand header metadata is control-plane information, not payload.
152
+ // Structured header payload bindings still follow normal typing rules,
153
+ // except for the mode selector itself so strict mode can be declared.
154
+ if (shouldSkipHeaderEvent(event, header)) {
155
+ continue;
156
+ }
157
+ // Indexed list/tuple element events are synthetic structural nodes.
158
+ // Strict typing is enforced on declared bindings and nested members.
159
+ if (isIndexedElementEvent(event)) {
160
+ continue;
161
+ }
162
+ if (!event.datatype) {
163
+ if (mode === 'strict' || mode === 'custom') {
164
+ if (mode === 'strict' && event.value.type === 'ToggleLiteral') {
165
+ errors.push(new UntypedToggleLiteralError(event.span, formatPath(event.path)));
166
+ }
167
+ else {
168
+ errors.push(new UntypedValueInStrictModeError(event.span, formatPath(event.path)));
169
+ }
170
+ }
171
+ continue;
172
+ }
173
+ const expectedKinds = expectedKindsForReservedDatatype(event.datatype);
174
+ const actualKind = resolveDatatypeCheckKind(event, events, pathToIndex) ?? event.value.type;
175
+ if (datatypeBase(event.datatype) === 'switch' && actualKind === 'ToggleLiteral') {
176
+ errors.push(new CustomToggleAliasNotAllowedError(event.span, formatPath(event.path), event.datatype));
177
+ continue;
178
+ }
179
+ if ((mode === 'strict' || mode === 'custom') && !expectedKinds && datatypePolicy === 'reserved_only') {
180
+ errors.push(new CustomDatatypeNotAllowedError(event.span, formatPath(event.path), event.datatype));
181
+ continue;
182
+ }
183
+ if (mode === 'strict' && !expectedKinds && actualKind === 'ToggleLiteral') {
184
+ errors.push(new CustomToggleAliasNotAllowedError(event.span, formatPath(event.path), event.datatype));
185
+ continue;
186
+ }
187
+ if (expectedKinds && !expectedKinds.includes(actualKind)) {
188
+ errors.push(new DatatypeLiteralMismatchError(event.span, formatPath(event.path), event.datatype, actualKind, expectedKinds));
189
+ }
190
+ if (!expectedKinds) {
191
+ const customShape = classifyCustomDatatypeShape(event.datatype);
192
+ if (customShape === 'invalid_both' && (actualKind === 'SeparatorLiteral' || actualKind === 'RadixLiteral')) {
193
+ errors.push(new InvalidCustomDatatypeBracketShapeError(event.span, formatPath(event.path), event.datatype, actualKind));
194
+ }
195
+ else {
196
+ const customExpectedKinds = expectedKindsForCustomDatatype(event.datatype, customShape);
197
+ if (customExpectedKinds && customExpectedKinds.length === 0) {
198
+ errors.push(new IncompatibleCustomDatatypeAdornmentsError(event.span, formatPath(event.path), event.datatype, actualKind));
199
+ }
200
+ else if (customExpectedKinds && !customExpectedKinds.includes(actualKind)) {
201
+ errors.push(new DatatypeLiteralMismatchError(event.span, formatPath(event.path), event.datatype, actualKind, customExpectedKinds));
202
+ }
203
+ }
204
+ }
205
+ errors.push(...validateAnnotationEntries(event.annotations, formatPath(event.path), event.span, events, pathToIndex, mode, datatypePolicy));
206
+ errors.push(...validateAnonymousTypedValues(event.value, formatPath(event.path), event.span, events, pathToIndex, mode, datatypePolicy));
207
+ errors.push(...validateNodeHeadDatatypes(event.value, formatPath(event.path), event.span, mode));
208
+ }
209
+ // Fail-closed: return empty events if errors exist
210
+ if (errors.length > 0 && !options.recovery) {
211
+ return { events: [], errors };
212
+ }
213
+ return { events, errors };
214
+ }
215
+ function defaultDatatypePolicyForMode(mode) {
216
+ return mode === 'strict' ? 'reserved_only' : 'allow_custom';
217
+ }
218
+ function isModeSelectorHeaderEvent(event) {
219
+ return event.path.segments.length === 2
220
+ && event.path.segments[0]?.type === 'root'
221
+ && event.path.segments[1]?.type === 'member'
222
+ && event.path.segments[1].key === 'aeon:mode';
223
+ }
224
+ function shouldSkipHeaderEvent(event, header) {
225
+ if (!header) {
226
+ return false;
227
+ }
228
+ if (header.hasShorthand && isTopLevelHeaderEvent(event)) {
229
+ return true;
230
+ }
231
+ return isModeSelectorHeaderEvent(event);
232
+ }
233
+ function isTopLevelHeaderEvent(event) {
234
+ return event.path.segments.length === 2
235
+ && event.path.segments[0]?.type === 'root'
236
+ && event.path.segments[1]?.type === 'member'
237
+ && event.path.segments[1].key.startsWith('aeon:');
238
+ }
239
+ // Legacy export for backward compatibility
240
+ export function validateMode(_mode) {
241
+ return [];
242
+ }
243
+ function isIndexedElementEvent(event) {
244
+ const lastSegment = event.path.segments[event.path.segments.length - 1];
245
+ return lastSegment?.type === 'index';
246
+ }
247
+ function resolveDatatypeCheckKind(event, events, pathToIndex, stack = []) {
248
+ const resolved = resolveReferenceValue(event.value, events, pathToIndex);
249
+ if (!resolved) {
250
+ return null;
251
+ }
252
+ if ((resolved.type === 'CloneReference' || resolved.type === 'PointerReference')) {
253
+ const resolution = resolveReferenceTarget(resolved.path, events, pathToIndex);
254
+ if (!resolution || stack.includes(resolution.targetPath)) {
255
+ return resolved.type;
256
+ }
257
+ if (!resolution.event) {
258
+ return resolved.type;
259
+ }
260
+ return resolveDatatypeCheckKind(resolution.event, events, pathToIndex, [...stack, resolution.targetPath]) ?? resolved.type;
261
+ }
262
+ return resolvedValueKind(resolved);
263
+ }
264
+ function resolvedValueKind(value) {
265
+ if (value.type === 'TypedValue') {
266
+ return resolvedValueKind(value.value);
267
+ }
268
+ if (value.type === 'StringLiteral') {
269
+ return value.trimticks ? 'TrimtickStringLiteral' : 'StringLiteral';
270
+ }
271
+ if (value.type === 'DateTimeLiteral') {
272
+ return value.raw.includes('&') ? 'ZRUTDateTimeLiteral' : 'DateTimeLiteral';
273
+ }
274
+ if (value.type === 'SeparatorLiteral') {
275
+ return 'SeparatorLiteral';
276
+ }
277
+ if (value.type === 'HexLiteral') {
278
+ return hasValidLiteralUnderscores(value.raw) ? 'HexLiteral' : 'InvalidHexLiteral';
279
+ }
280
+ if (value.type === 'RadixLiteral') {
281
+ return hasValidRadixLiteral(value.raw) ? 'RadixLiteral' : 'InvalidRadixLiteral';
282
+ }
283
+ if (value.type === 'EncodingLiteral') {
284
+ return hasValidEncodingLiteral(value.raw) ? 'EncodingLiteral' : 'InvalidEncodingLiteral';
285
+ }
286
+ return value.type;
287
+ }
288
+ function validateAnonymousTypedValues(value, ownerPath, span, events, pathToIndex, mode, datatypePolicy) {
289
+ const errors = [];
290
+ if (value.type === 'TypedValue') {
291
+ if (value.datatype) {
292
+ const datatype = formatTypeAnnotation(value.datatype);
293
+ const expectedKinds = expectedKindsForReservedDatatype(datatype);
294
+ const resolved = resolveReferenceValue(value.value, events, pathToIndex) ?? value.value;
295
+ const actualKind = resolvedValueKind(resolved);
296
+ if (datatypeBase(datatype) === 'switch' && actualKind === 'ToggleLiteral') {
297
+ errors.push(new CustomToggleAliasNotAllowedError(value.span, ownerPath, datatype));
298
+ }
299
+ else if ((mode === 'strict' || mode === 'custom') && !expectedKinds && datatypePolicy === 'reserved_only') {
300
+ errors.push(new CustomDatatypeNotAllowedError(value.span, ownerPath, datatype));
301
+ }
302
+ else {
303
+ if (mode === 'strict' && !expectedKinds && actualKind === 'ToggleLiteral') {
304
+ errors.push(new CustomToggleAliasNotAllowedError(value.span, ownerPath, datatype));
305
+ }
306
+ else if (expectedKinds && !expectedKinds.includes(actualKind)) {
307
+ errors.push(new DatatypeLiteralMismatchError(value.span, ownerPath, datatype, actualKind, expectedKinds));
308
+ }
309
+ else if (!expectedKinds) {
310
+ const customShape = classifyCustomDatatypeShape(datatype);
311
+ if (customShape === 'invalid_both' && (actualKind === 'SeparatorLiteral' || actualKind === 'RadixLiteral')) {
312
+ errors.push(new InvalidCustomDatatypeBracketShapeError(value.span, ownerPath, datatype, actualKind));
313
+ }
314
+ else {
315
+ const customExpectedKinds = expectedKindsForCustomDatatype(datatype, customShape);
316
+ if (customExpectedKinds && customExpectedKinds.length === 0) {
317
+ errors.push(new IncompatibleCustomDatatypeAdornmentsError(value.span, ownerPath, datatype, actualKind));
318
+ }
319
+ else if (customExpectedKinds && !customExpectedKinds.includes(actualKind)) {
320
+ errors.push(new DatatypeLiteralMismatchError(value.span, ownerPath, datatype, actualKind, customExpectedKinds));
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+ errors.push(...validateAnonymousTypedValues(value.value, ownerPath, span, events, pathToIndex, mode, datatypePolicy));
327
+ return errors;
328
+ }
329
+ if (value.type === 'ObjectNode') {
330
+ for (const binding of value.bindings) {
331
+ errors.push(...validateAnonymousTypedValues(binding.value, `${ownerPath}.${binding.key}`, span, events, pathToIndex, mode, datatypePolicy));
332
+ }
333
+ return errors;
334
+ }
335
+ if (value.type === 'ListNode' || value.type === 'TupleLiteral') {
336
+ for (let i = 0; i < value.elements.length; i++) {
337
+ errors.push(...validateAnonymousTypedValues(value.elements[i], `${ownerPath}[${i}]`, span, events, pathToIndex, mode, datatypePolicy));
338
+ }
339
+ return errors;
340
+ }
341
+ if (value.type === 'NodeLiteral') {
342
+ for (let i = 0; i < value.children.length; i++) {
343
+ errors.push(...validateAnonymousTypedValues(value.children[i], `${ownerPath}[${i}]`, span, events, pathToIndex, mode, datatypePolicy));
344
+ }
345
+ }
346
+ return errors;
347
+ }
348
+ function hasValidLiteralUnderscores(raw) {
349
+ const body = raw.slice(1);
350
+ return body.length > 0 && !body.startsWith('_') && !body.endsWith('_') && !body.includes('__');
351
+ }
352
+ function isValidRadixDigit(c) {
353
+ return /[0-9A-Za-z&!]/.test(c);
354
+ }
355
+ function hasValidRadixLiteral(raw) {
356
+ const body = raw.slice(1);
357
+ if (body.length === 0)
358
+ return false;
359
+ let index = 0;
360
+ if (body[index] === '+' || body[index] === '-')
361
+ index += 1;
362
+ if (index >= body.length)
363
+ return false;
364
+ let sawDigit = false;
365
+ let sawDecimal = false;
366
+ let prevWasDigit = false;
367
+ let sawDigitBeforeDecimal = false;
368
+ for (; index < body.length; index += 1) {
369
+ const c = body[index];
370
+ if (isValidRadixDigit(c)) {
371
+ sawDigit = true;
372
+ prevWasDigit = true;
373
+ if (!sawDecimal)
374
+ sawDigitBeforeDecimal = true;
375
+ continue;
376
+ }
377
+ if (c === '_') {
378
+ if (!prevWasDigit || index + 1 >= body.length || !isValidRadixDigit(body[index + 1]))
379
+ return false;
380
+ prevWasDigit = false;
381
+ continue;
382
+ }
383
+ if (c === '.') {
384
+ if (sawDecimal || index + 1 >= body.length || !isValidRadixDigit(body[index + 1]))
385
+ return false;
386
+ if (!prevWasDigit && sawDigitBeforeDecimal)
387
+ return false;
388
+ sawDecimal = true;
389
+ prevWasDigit = false;
390
+ continue;
391
+ }
392
+ return false;
393
+ }
394
+ return sawDigit && prevWasDigit;
395
+ }
396
+ function hasValidEncodingLiteral(raw) {
397
+ const body = raw.slice(1);
398
+ if (body.length === 0)
399
+ return false;
400
+ if (!/^[A-Za-z0-9+/_-]+={0,2}$/.test(body))
401
+ return false;
402
+ const firstPadding = body.indexOf('=');
403
+ if (firstPadding === -1)
404
+ return true;
405
+ return /^=+$/.test(body.slice(firstPadding));
406
+ }
407
+ function validateAnnotationEntries(annotations, ownerPath, span, events, pathToIndex, mode, datatypePolicy) {
408
+ if (!annotations || annotations.size === 0) {
409
+ return [];
410
+ }
411
+ const errors = [];
412
+ for (const [key, entry] of annotations) {
413
+ const attrPath = `${ownerPath}@${key}`;
414
+ if (entry.datatype) {
415
+ const expectedKinds = expectedKindsForReservedDatatype(entry.datatype);
416
+ if ((mode === 'strict' || mode === 'custom') && !expectedKinds && datatypePolicy === 'reserved_only') {
417
+ errors.push(new CustomDatatypeNotAllowedError(span, attrPath, entry.datatype));
418
+ }
419
+ else {
420
+ const resolved = resolveReferenceValue(entry.value, events, pathToIndex) ?? entry.value;
421
+ const actualKind = resolvedValueKind(resolved);
422
+ if (expectedKinds && !expectedKinds.includes(actualKind)) {
423
+ errors.push(new DatatypeLiteralMismatchError(span, attrPath, entry.datatype, actualKind, expectedKinds));
424
+ }
425
+ if (!expectedKinds) {
426
+ const customShape = classifyCustomDatatypeShape(entry.datatype);
427
+ if (customShape === 'invalid_both' && (actualKind === 'SeparatorLiteral' || actualKind === 'RadixLiteral')) {
428
+ errors.push(new InvalidCustomDatatypeBracketShapeError(span, attrPath, entry.datatype, actualKind));
429
+ }
430
+ else {
431
+ const customExpectedKinds = expectedKindsForCustomDatatype(entry.datatype, customShape);
432
+ if (customExpectedKinds && customExpectedKinds.length === 0) {
433
+ errors.push(new IncompatibleCustomDatatypeAdornmentsError(span, attrPath, entry.datatype, actualKind));
434
+ }
435
+ else if (customExpectedKinds && !customExpectedKinds.includes(actualKind)) {
436
+ errors.push(new DatatypeLiteralMismatchError(span, attrPath, entry.datatype, actualKind, customExpectedKinds));
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ errors.push(...validateNodeHeadDatatypes(entry.value, attrPath, span, mode));
443
+ errors.push(...validateAnnotationEntries(entry.annotations, attrPath, span, events, pathToIndex, mode, datatypePolicy));
444
+ }
445
+ return errors;
446
+ }
447
+ function validateNodeHeadDatatypes(value, ownerPath, span, mode) {
448
+ const errors = [];
449
+ if (value.type === 'TypedValue') {
450
+ errors.push(...validateNodeHeadDatatypes(value.value, ownerPath, span, mode));
451
+ return errors;
452
+ }
453
+ if (value.type === 'NodeLiteral') {
454
+ const headDatatype = value.datatype ? formatTypeAnnotation(value.datatype) : null;
455
+ if (mode === 'strict' && headDatatype && value.datatype.name.toLowerCase() !== 'node') {
456
+ errors.push(new InvalidNodeHeadDatatypeError(span, ownerPath, headDatatype));
457
+ }
458
+ for (let i = 0; i < value.children.length; i++) {
459
+ errors.push(...validateNodeHeadDatatypes(value.children[i], `${ownerPath}[${i}]`, span, mode));
460
+ }
461
+ return errors;
462
+ }
463
+ if (value.type === 'ObjectNode') {
464
+ for (const binding of value.bindings) {
465
+ errors.push(...validateNodeHeadDatatypes(binding.value, `${ownerPath}.${binding.key}`, span, mode));
466
+ }
467
+ return errors;
468
+ }
469
+ if (value.type === 'ListNode' || value.type === 'TupleLiteral') {
470
+ for (let i = 0; i < value.elements.length; i++) {
471
+ errors.push(...validateNodeHeadDatatypes(value.elements[i], `${ownerPath}[${i}]`, span, mode));
472
+ }
473
+ }
474
+ return errors;
475
+ }
476
+ function formatTypeAnnotation(datatype) {
477
+ const name = datatype.name;
478
+ const generics = datatype.genericArgs.length > 0 ? `<${datatype.genericArgs.join(', ')}>` : '';
479
+ const radixBase = datatype.radixBase != null ? `[${datatype.radixBase}]` : '';
480
+ const separators = datatype.separators.map((separator) => `[${separator}]`).join('');
481
+ return `${name}${generics}${radixBase}${separators}`;
482
+ }
483
+ function isAttrSegment(segment) {
484
+ return typeof segment === 'object' && segment !== null && segment.type === 'attr';
485
+ }
486
+ function resolveReferenceValue(value, events, pathToIndex) {
487
+ if (value.type !== 'CloneReference' && value.type !== 'PointerReference') {
488
+ return unwrapTypedValue(value);
489
+ }
490
+ const resolution = resolveReferenceTarget(value.path, events, pathToIndex);
491
+ if (!resolution) {
492
+ return null;
493
+ }
494
+ return resolveReferenceSubpath(resolution.event, resolution.remainder);
495
+ }
496
+ function resolveReferenceTarget(path, events, pathToIndex) {
497
+ for (let split = path.length; split >= 1; split--) {
498
+ const prefix = path.slice(0, split);
499
+ if (prefix.some((segment) => typeof segment === 'object' && segment.type === 'attr')) {
500
+ continue;
501
+ }
502
+ const prefixPath = formatReferenceTargetPath(prefix);
503
+ const targetIndex = pathToIndex.get(prefixPath);
504
+ if (targetIndex === undefined) {
505
+ continue;
506
+ }
507
+ const event = events[targetIndex];
508
+ if (!event) {
509
+ return null;
510
+ }
511
+ const remainder = path.slice(split);
512
+ if (remainder.length === 0) {
513
+ return { targetPath: prefixPath, event, remainder };
514
+ }
515
+ if (resolveReferenceSubpath(event, remainder)) {
516
+ return { targetPath: prefixPath, event, remainder };
517
+ }
518
+ }
519
+ return null;
520
+ }
521
+ function resolveReferenceSubpath(event, remainder) {
522
+ let context = {
523
+ value: event.value,
524
+ annotations: selectAnnotations(event.annotations, event.value),
525
+ };
526
+ for (const segment of remainder) {
527
+ if (isAttrSegment(segment)) {
528
+ const attrEntry = context.annotations?.get(segment.key);
529
+ if (!attrEntry)
530
+ return null;
531
+ context = {
532
+ value: attrEntry.value,
533
+ annotations: selectAnnotations(attrEntry.annotations, attrEntry.value),
534
+ };
535
+ continue;
536
+ }
537
+ if (typeof segment === 'string') {
538
+ const value = unwrapTypedValue(context.value);
539
+ if (value.type !== 'ObjectNode')
540
+ return null;
541
+ const binding = value.bindings.find((candidate) => candidate.key === segment);
542
+ if (!binding)
543
+ return null;
544
+ context = {
545
+ value: binding.value,
546
+ annotations: selectAnnotations(buildAnnotationMap(binding.attributes), binding.value),
547
+ };
548
+ continue;
549
+ }
550
+ if (typeof segment === 'number') {
551
+ const value = unwrapTypedValue(context.value);
552
+ if (value.type !== 'ListNode' && value.type !== 'TupleLiteral')
553
+ return null;
554
+ const element = value.elements[segment];
555
+ if (!element)
556
+ return null;
557
+ context = {
558
+ value: element,
559
+ annotations: selectAnnotations(undefined, element),
560
+ };
561
+ continue;
562
+ }
563
+ return null;
564
+ }
565
+ return context.value;
566
+ }
567
+ function selectAnnotations(preferred, value) {
568
+ if (preferred && preferred.size > 0)
569
+ return preferred;
570
+ if (value.type === 'TypedValue' && value.attributes.length > 0) {
571
+ return buildAnnotationMap(value.attributes);
572
+ }
573
+ return buildValueAnnotationMap(unwrapTypedValue(value));
574
+ }
575
+ function unwrapTypedValue(value) {
576
+ return value.type === 'TypedValue' ? value.value : value;
577
+ }
578
+ function buildValueAnnotationMap(value) {
579
+ if (value.type !== 'ObjectNode'
580
+ && value.type !== 'ListNode'
581
+ && value.type !== 'TupleLiteral'
582
+ && value.type !== 'NodeLiteral') {
583
+ return undefined;
584
+ }
585
+ return buildAnnotationMap(value.attributes);
586
+ }
587
+ function buildAnnotationMap(attributes) {
588
+ if (!attributes || attributes.length === 0)
589
+ return undefined;
590
+ const result = new Map();
591
+ for (const attribute of attributes) {
592
+ for (const [key, entry] of attribute.entries) {
593
+ const mapped = { value: entry.value };
594
+ const nested = buildAnnotationMap(entry.attributes);
595
+ if (nested) {
596
+ mapped.annotations = nested;
597
+ }
598
+ result.set(key, mapped);
599
+ }
600
+ }
601
+ return result;
602
+ }
603
+ function expectedKindsForReservedDatatype(datatype) {
604
+ const base = datatypeBase(datatype);
605
+ // Reserved scalar and container names validated by strict mode.
606
+ // Custom datatypes intentionally remain unconstrained.
607
+ if (NUMERIC_TYPES.has(base))
608
+ return ['NumberLiteral'];
609
+ if (base === 'infinity')
610
+ return ['InfinityLiteral'];
611
+ if (base === 'nan')
612
+ return ['NaNLiteral'];
613
+ if (base === 'string')
614
+ return ['StringLiteral'];
615
+ if (base === 'trimtick' || base === 'prose')
616
+ return ['TrimtickStringLiteral'];
617
+ if (base === 'boolean' || base === 'bool')
618
+ return ['BooleanLiteral'];
619
+ if (base === 'toggle')
620
+ return ['ToggleLiteral'];
621
+ if (base === 'hex')
622
+ return ['HexLiteral'];
623
+ if (RADIX_TYPES.has(base))
624
+ return ['RadixLiteral'];
625
+ if (ENCODING_TYPES.has(base))
626
+ return ['EncodingLiteral'];
627
+ if (base === 'date')
628
+ return ['DateLiteral'];
629
+ if (base === 'time')
630
+ return ['TimeLiteral'];
631
+ if (base === 'datetime')
632
+ return ['DateTimeLiteral'];
633
+ if (base === 'zrut')
634
+ return ['ZRUTDateTimeLiteral'];
635
+ if (SEPARATOR_TYPES.has(base))
636
+ return ['SeparatorLiteral'];
637
+ if (base === 'tuple')
638
+ return ['TupleLiteral'];
639
+ if (base === 'list')
640
+ return ['ListNode'];
641
+ if (OBJECT_TYPES.has(base))
642
+ return ['ObjectNode'];
643
+ if (base === 'node')
644
+ return ['NodeLiteral'];
645
+ if (base === 'null')
646
+ return ['NullLiteral'];
647
+ return null;
648
+ }
649
+ function datatypeBase(datatype) {
650
+ const genericIdx = datatype.indexOf('<');
651
+ const separatorIdx = datatype.indexOf('[');
652
+ const endIdx = [genericIdx, separatorIdx]
653
+ .filter((idx) => idx >= 0)
654
+ .reduce((min, idx) => Math.min(min, idx), datatype.length);
655
+ return datatype.slice(0, endIdx);
656
+ }
657
+ export function datatypeHasGenericArgs(datatype) {
658
+ let bracketDepth = 0;
659
+ let genericStart = -1;
660
+ for (let index = 0; index < datatype.length; index += 1) {
661
+ const char = datatype[index];
662
+ if (char === '[') {
663
+ bracketDepth += 1;
664
+ continue;
665
+ }
666
+ if (char === ']') {
667
+ bracketDepth = Math.max(0, bracketDepth - 1);
668
+ continue;
669
+ }
670
+ if (bracketDepth > 0) {
671
+ continue;
672
+ }
673
+ if (char === '<') {
674
+ genericStart = index;
675
+ continue;
676
+ }
677
+ if (char === '>' && genericStart >= 0) {
678
+ return true;
679
+ }
680
+ }
681
+ return false;
682
+ }
683
+ function expectedKindsForCustomDatatype(datatype, customShape) {
684
+ let expectedKinds = null;
685
+ if (datatypeHasGenericArgs(datatype)) {
686
+ expectedKinds = ['ListNode', 'TupleLiteral'];
687
+ }
688
+ const bracketKinds = expectedKindsForCustomDatatypeShape(customShape);
689
+ if (bracketKinds === null) {
690
+ return expectedKinds;
691
+ }
692
+ if (expectedKinds === null) {
693
+ return bracketKinds;
694
+ }
695
+ const combined = expectedKinds.filter((kind) => bracketKinds.includes(kind));
696
+ return combined;
697
+ }
698
+ function expectedKindsForCustomDatatypeShape(customShape) {
699
+ if (customShape === 'none' || customShape === 'invalid_both')
700
+ return null;
701
+ if (customShape === 'both')
702
+ return ['SeparatorLiteral', 'RadixLiteral'];
703
+ if (customShape === 'separator')
704
+ return ['SeparatorLiteral'];
705
+ return ['RadixLiteral'];
706
+ }
707
+ function classifyCustomDatatypeShape(datatype) {
708
+ const specs = bracketSpecs(datatype);
709
+ if (specs.length === 0)
710
+ return 'none';
711
+ const separatorOk = specs.every(isValidSeparatorSpec);
712
+ const radixOk = specs.length === 1 && isValidRadixBaseSpec(specs[0]);
713
+ if (separatorOk && radixOk)
714
+ return 'both';
715
+ if (separatorOk)
716
+ return 'separator';
717
+ if (radixOk)
718
+ return 'radix';
719
+ return 'invalid_both';
720
+ }
721
+ function bracketSpecs(datatype) {
722
+ const specs = [];
723
+ let genericDepth = 0;
724
+ let bracketStart = -1;
725
+ for (let index = 0; index < datatype.length; index += 1) {
726
+ const char = datatype[index];
727
+ if (char === '<') {
728
+ genericDepth += 1;
729
+ continue;
730
+ }
731
+ if (char === '>') {
732
+ genericDepth = Math.max(0, genericDepth - 1);
733
+ continue;
734
+ }
735
+ if (genericDepth > 0) {
736
+ continue;
737
+ }
738
+ if (char === '[') {
739
+ bracketStart = index + 1;
740
+ continue;
741
+ }
742
+ if (char === ']' && bracketStart >= 0) {
743
+ specs.push(datatype.slice(bracketStart, index));
744
+ bracketStart = -1;
745
+ }
746
+ }
747
+ if (datatypeBase(datatype) === 'radix' && specs.length > 0) {
748
+ return specs.slice(1);
749
+ }
750
+ return specs;
751
+ }
752
+ function isValidSeparatorSpec(spec) {
753
+ return /^[A-Za-z0-9!#$%&*+\-.:;=?@^_|~<>]$/.test(spec);
754
+ }
755
+ function isValidRadixBaseSpec(spec) {
756
+ if (!/^[1-9]\d*$/.test(spec))
757
+ return false;
758
+ const value = Number(spec);
759
+ return Number.isInteger(value) && value >= 2 && value <= 64;
760
+ }
761
+ const NUMERIC_TYPES = new Set([
762
+ 'number',
763
+ 'n',
764
+ 'int',
765
+ 'int8',
766
+ 'int16',
767
+ 'int32',
768
+ 'int64',
769
+ 'uint',
770
+ 'uint8',
771
+ 'uint16',
772
+ 'uint32',
773
+ 'uint64',
774
+ 'float',
775
+ 'float32',
776
+ 'float64',
777
+ ]);
778
+ const RADIX_TYPES = new Set([
779
+ 'radix',
780
+ 'radix2',
781
+ 'radix6',
782
+ 'radix8',
783
+ 'radix12',
784
+ ]);
785
+ const ENCODING_TYPES = new Set([
786
+ 'encoding',
787
+ 'base64',
788
+ 'embed',
789
+ 'inline',
790
+ ]);
791
+ const SEPARATOR_TYPES = new Set([
792
+ 'sep',
793
+ 'set',
794
+ ]);
795
+ const OBJECT_TYPES = new Set([
796
+ 'object',
797
+ 'obj',
798
+ 'envelope',
799
+ 'o',
800
+ ]);
801
+ //# sourceMappingURL=modes.js.map