@altopelago/aeos-core 0.9.1 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,7 @@ export const KNOWN_CONSTRAINT_KEYS = new Set([
28
28
  'min_digits',
29
29
  'max_digits',
30
30
  'radix',
31
+ 'allow_unspecified_radix',
31
32
  'min_value',
32
33
  'max_value',
33
34
  'min_length',
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkIH;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC;IAC9D,UAAU;IACV,MAAM;IACN,QAAQ;IACR,UAAU;IACV,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,aAAa;IACb,WAAW;IACX,gBAAgB;IAChB,0BAA0B;IAC1B,wBAAwB;IACxB,SAAS;IACT,cAAc;IACd,cAAc;IACd,cAAc;IACd,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,OAAO;IACP,WAAW;IACX,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,UAAU;IACV,YAAY;IACZ,mBAAmB;CACtB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAoC;IACzE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoJH;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC;IAC9D,UAAU;IACV,MAAM;IACN,QAAQ;IACR,UAAU;IACV,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,aAAa;IACb,aAAa;IACb,WAAW;IACX,gBAAgB;IAChB,0BAA0B;IAC1B,wBAAwB;IACxB,SAAS;IACT,cAAc;IACd,cAAc;IACd,cAAc;IACd,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,OAAO;IACP,yBAAyB;IACzB,WAAW;IACX,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,UAAU;IACV,YAAY;IACZ,mBAAmB;CACtB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,WAAoC;IACzE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -6,6 +6,7 @@
6
6
  import type { AES } from './types/aes.js';
7
7
  import type { SchemaV1 } from './types/schema.js';
8
8
  import type { ResultEnvelope } from './types/envelope.js';
9
+ import type { ResourcePolicyV1 } from './types/schema.js';
9
10
  /**
10
11
  * Validation options
11
12
  */
@@ -21,6 +22,8 @@ export interface ValidateOptions {
21
22
  * - error: emit error
22
23
  */
23
24
  readonly trailingSeparatorDelimiterPolicy?: 'off' | 'warn' | 'error';
25
+ /** Optional consumer-controlled validation resource limits. */
26
+ readonly resourcePolicy?: ResourcePolicyV1;
24
27
  }
25
28
  /**
26
29
  * Validate an AES against a schema.
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA6D1D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,gCAAgC,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;CAExE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CACpB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,QAAQ,EAChB,OAAO,GAAE,eAAoB,GAC9B,cAAc,CAqUhB"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAW1D,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAkDzE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,gCAAgC,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAErE,+DAA+D;IAC/D,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC;CAE9C;AAgJD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CACpB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,QAAQ,EAChB,OAAO,GAAE,eAAoB,GAC9B,cAAc,CAuWhB"}
package/dist/validate.js CHANGED
@@ -12,7 +12,7 @@ import { checkPresence } from './rules/presence.js';
12
12
  import { checkTypes } from './rules/typeCheck.js';
13
13
  import { checkReferenceForms } from './rules/referenceForm.js';
14
14
  import { checkNumericForm } from './rules/numericForm.js';
15
- import { checkStringForm, checkPatterns } from './rules/stringForm.js';
15
+ import { checkStringForm, checkPatterns, matchesPortablePattern } from './rules/stringForm.js';
16
16
  const TYPE_ALIASES = {
17
17
  NumberLiteral: ['NumberLiteral'],
18
18
  StringLiteral: ['StringLiteral'],
@@ -40,6 +40,113 @@ const TYPE_ALIASES = {
40
40
  function formatQuotedMemberSegment(key) {
41
41
  return `.[${JSON.stringify(String(key))}]`;
42
42
  }
43
+ const DEFAULT_RESOURCE_POLICY = {
44
+ max_events: 100_000,
45
+ max_rules: 10_000,
46
+ max_any_of_cases: 64,
47
+ max_schema_depth: 64,
48
+ max_path_length: 4_096,
49
+ max_reference_resolution_steps: 64,
50
+ max_selector_expansions: 100_000,
51
+ max_string_length_default: 10_000_000,
52
+ max_container_children_default: 1_000_000,
53
+ };
54
+ function normalizeResourcePolicy(policy, source, ctx) {
55
+ if (policy === undefined)
56
+ return {};
57
+ if (policy === null || typeof policy !== 'object') {
58
+ emitResourceError(ctx, '$', `${source} resource policy must be an object`);
59
+ return {};
60
+ }
61
+ const normalized = {};
62
+ for (const key of Object.keys(policy)) {
63
+ if (!(key in DEFAULT_RESOURCE_POLICY)) {
64
+ emitResourceError(ctx, '$', `Unknown ${source} resource policy key: ${String(key)}`);
65
+ continue;
66
+ }
67
+ const value = policy[key];
68
+ if (value !== undefined && (typeof value !== 'number' || !Number.isInteger(value) || value < 0)) {
69
+ emitResourceError(ctx, '$', `${source} resource policy ${String(key)} must be a non-negative integer`);
70
+ continue;
71
+ }
72
+ if (value !== undefined) {
73
+ normalized[key] = value;
74
+ }
75
+ }
76
+ return normalized;
77
+ }
78
+ function resolveResourcePolicy(schemaPolicy, optionPolicy, ctx) {
79
+ return {
80
+ ...DEFAULT_RESOURCE_POLICY,
81
+ ...normalizeResourcePolicy(schemaPolicy, 'schema', ctx),
82
+ ...normalizeResourcePolicy(optionPolicy, 'option', ctx),
83
+ };
84
+ }
85
+ function emitResourceError(ctx, path, message, span = null) {
86
+ emitError(ctx, createDiag(path, span, message, ErrorCodes.INVALID_SCHEMA_POLICY));
87
+ }
88
+ const STRING_LIKE_VALUE_TYPES = new Set([
89
+ 'StringLiteral',
90
+ 'TrimtickLiteral',
91
+ 'SeparatorLiteral',
92
+ 'HexLiteral',
93
+ 'EncodingLiteral',
94
+ 'NullLiteral',
95
+ 'DateLiteral',
96
+ 'TimeLiteral',
97
+ 'DateTimeLiteral',
98
+ 'ZRUTDateTimeLiteral',
99
+ ]);
100
+ function stringLikePayloadLength(event) {
101
+ if (!STRING_LIKE_VALUE_TYPES.has(event.type))
102
+ return null;
103
+ const payload = event.value.length > 0 ? event.value : event.raw;
104
+ return payload.length;
105
+ }
106
+ function enforceStringLengthResourceBudget(info, path, policy, ctx) {
107
+ const payloadLength = stringLikePayloadLength(info);
108
+ if (payloadLength !== null && payloadLength > policy.max_string_length_default) {
109
+ emitResourceError(ctx, path, `String-like payload length ${payloadLength} exceeds max_string_length_default ${policy.max_string_length_default}`, info.span);
110
+ }
111
+ for (const [key, attribute] of info.attributes ?? []) {
112
+ enforceStringLengthResourceBudget(attribute, `${path}@${key}`, policy, ctx);
113
+ }
114
+ }
115
+ function inspectSchemaResourceShape(schema, policy, ctx) {
116
+ for (const rule of schema.rules) {
117
+ const rulePath = typeof rule.path === 'string' && rule.path.length > 0
118
+ ? rule.path
119
+ : typeof rule.selector === 'string' && rule.selector.length > 0
120
+ ? rule.selector
121
+ : '$';
122
+ if (rulePath.length > policy.max_path_length) {
123
+ emitResourceError(ctx, rulePath, `Rule path length ${rulePath.length} exceeds max_path_length ${policy.max_path_length}`);
124
+ }
125
+ inspectConstraintResourceShape(rule.constraints, rulePath, 1, policy, ctx);
126
+ }
127
+ for (const [datatype, constraints] of Object.entries(schema.datatype_rules ?? {})) {
128
+ inspectConstraintResourceShape(constraints, `datatype_rules.${datatype}`, 1, policy, ctx);
129
+ }
130
+ }
131
+ function inspectConstraintResourceShape(constraints, path, depth, policy, ctx) {
132
+ if (depth > policy.max_schema_depth) {
133
+ emitResourceError(ctx, path, `Schema constraint depth exceeds max_schema_depth ${policy.max_schema_depth}`);
134
+ return;
135
+ }
136
+ if (constraints.any_of !== undefined) {
137
+ if (constraints.any_of.length > policy.max_any_of_cases) {
138
+ emitResourceError(ctx, path, `any_of case count ${constraints.any_of.length} exceeds max_any_of_cases ${policy.max_any_of_cases}`);
139
+ }
140
+ constraints.any_of.forEach((branch, index) => {
141
+ inspectConstraintResourceShape(branch, `${path}.any_of[${index}]`, depth + 1, policy, ctx);
142
+ });
143
+ }
144
+ if (constraints.attributes !== undefined) {
145
+ for (const [key, child] of Object.entries(constraints.attributes)) {
146
+ inspectConstraintResourceShape(child, `${path}@${key}`, depth + 1, policy, ctx);
147
+ }
148
+ }
149
+ }
43
150
  /**
44
151
  * Validate an AES against a schema.
45
152
  *
@@ -70,6 +177,20 @@ export function validate(aes, schema, options = {}) {
70
177
  // TODO: Phase 9 - Guarantees
71
178
  // Phase 1: Envelope plumbing
72
179
  const ctx = createDiagContext();
180
+ const resourcePolicy = resolveResourcePolicy(schema.resource_policy, options.resourcePolicy, ctx);
181
+ if (ctx.errors.length > 0) {
182
+ return createFailingEnvelope(ctx.errors, ctx.warnings, {});
183
+ }
184
+ if (aes.length > resourcePolicy.max_events) {
185
+ emitResourceError(ctx, '$', `AES event count ${aes.length} exceeds max_events ${resourcePolicy.max_events}`);
186
+ }
187
+ if (schema.rules.length > resourcePolicy.max_rules) {
188
+ emitResourceError(ctx, '$', `Schema rule count ${schema.rules.length} exceeds max_rules ${resourcePolicy.max_rules}`);
189
+ }
190
+ inspectSchemaResourceShape(schema, resourcePolicy, ctx);
191
+ if (ctx.errors.length > 0) {
192
+ return createFailingEnvelope(ctx.errors, ctx.warnings, {});
193
+ }
73
194
  // Phase 3: (moved to run after Phase 2)
74
195
  // Helpers: format canonical path (local, no runtime AEON deps)
75
196
  function formatCanonicalPath(path) {
@@ -182,6 +303,9 @@ export function validate(aes, schema, options = {}) {
182
303
  for (let i = 0; i < aes.length; i++) {
183
304
  const event = aes[i];
184
305
  const pathStr = formatCanonicalPath(event.path);
306
+ if (pathStr.length > resourcePolicy.max_path_length) {
307
+ emitResourceError(ctx, pathStr, `Path length ${pathStr.length} exceeds max_path_length ${resourcePolicy.max_path_length}`, toTuple(event.span));
308
+ }
185
309
  if (Array.isArray(event.path?.segments)) {
186
310
  for (const seg of event.path.segments) {
187
311
  if (seg?.type === 'index') {
@@ -217,19 +341,31 @@ export function validate(aes, schema, options = {}) {
217
341
  if ((event.value.type === 'TupleLiteral' || event.value.type === 'ListLiteral' || event.value.type === 'ListNode')
218
342
  && Array.isArray(event.value.elements)) {
219
343
  containerArity.set(pathStr, event.value.elements.length);
344
+ if (event.value.elements.length > resourcePolicy.max_container_children_default) {
345
+ emitResourceError(ctx, pathStr, `Container child count ${event.value.elements.length} exceeds max_container_children_default ${resourcePolicy.max_container_children_default}`, toTuple(event.span));
346
+ }
220
347
  hydrateIndexedFallback(pathStr, event.value, toTuple(event.span));
221
348
  }
222
349
  else if (event.value.type === 'ObjectNode' && Array.isArray(event.value.bindings)) {
223
350
  containerArity.set(pathStr, event.value.bindings.length);
351
+ if (event.value.bindings.length > resourcePolicy.max_container_children_default) {
352
+ emitResourceError(ctx, pathStr, `Container child count ${event.value.bindings.length} exceeds max_container_children_default ${resourcePolicy.max_container_children_default}`, toTuple(event.span));
353
+ }
224
354
  }
225
355
  else if (event.value.type === 'NodeLiteral' && Array.isArray(event.value.children)) {
226
356
  containerArity.set(pathStr, event.value.children.length);
357
+ if (event.value.children.length > resourcePolicy.max_container_children_default) {
358
+ emitResourceError(ctx, pathStr, `Container child count ${event.value.children.length} exceeds max_container_children_default ${resourcePolicy.max_container_children_default}`, toTuple(event.span));
359
+ }
227
360
  hydrateIndexedFallback(pathStr, event.value, toTuple(event.span));
228
361
  }
229
362
  }
230
363
  }
231
364
  // Register index even for first occurrence
232
365
  }
366
+ for (const [path, info] of eventsByPath) {
367
+ enforceStringLengthResourceBudget(info, path, resourcePolicy, ctx);
368
+ }
233
369
  // Optional separator literal trailing-delimiter policy
234
370
  if (trailingSeparatorPolicy !== 'off') {
235
371
  for (const event of aes) {
@@ -254,14 +390,16 @@ export function validate(aes, schema, options = {}) {
254
390
  }
255
391
  // Phase 3: Build rule index from schema (run after baseline invariants)
256
392
  const ruleIndex = buildRuleIndex(schema, ctx);
257
- const effectiveRuleIndex = expandWildcardRules(expandSelectorRules(ruleIndex, schema, eventsByPath, ctx), eventsByPath);
393
+ const selectorExpansionBudget = { count: 0 };
394
+ const expandedRuleIndex = expandWildcardRules(expandSelectorRules(ruleIndex, schema, eventsByPath, ctx, resourcePolicy, selectorExpansionBudget), eventsByPath, ctx, resourcePolicy, selectorExpansionBudget);
395
+ const effectiveRuleIndex = mergeDatatypeRules(expandedRuleIndex, schema.datatype_rules, eventsByPath);
258
396
  // Phase 4: Presence checks (required fields)
259
397
  const boundPaths = new Set(seen.keys());
260
398
  checkPresence(effectiveRuleIndex, boundPaths, ctx);
261
399
  checkWorldPolicy(schema, aes, boundPaths, ctx);
262
400
  // Phase 5: Type checks (literal kind)
263
401
  checkReferenceForms(schema, effectiveRuleIndex, eventsByPath, ctx);
264
- const effectiveEventsByPath = resolveReferenceFormEvents(effectiveRuleIndex, eventsByPath);
402
+ const effectiveEventsByPath = resolveReferenceFormEvents(effectiveRuleIndex, eventsByPath, resourcePolicy, ctx);
265
403
  const selectedRuleIndex = selectAnyOfRules(effectiveRuleIndex, effectiveEventsByPath, ctx);
266
404
  checkTypes(selectedRuleIndex, effectiveEventsByPath, ctx);
267
405
  // Phase 5b: core v1 arity/cardinality checks for tuple/list/node containers
@@ -380,7 +518,7 @@ function checkWorldPolicy(schema, aes, boundPaths, ctx) {
380
518
  emitError(ctx, createDiag(path, toTupleLocal(event.span), `Binding '${path}' is not allowed by closed-world schema`, ErrorCodes.UNEXPECTED_BINDING));
381
519
  }
382
520
  }
383
- function resolveReferenceFormEvents(ruleIndex, eventsByPath) {
521
+ function resolveReferenceFormEvents(ruleIndex, eventsByPath, resourcePolicy, ctx) {
384
522
  const resolved = new Map(eventsByPath);
385
523
  for (const [path, rule] of ruleIndex.entries()) {
386
524
  if (rule.constraints.resolve_reference_form !== true)
@@ -388,8 +526,12 @@ function resolveReferenceFormEvents(ruleIndex, eventsByPath) {
388
526
  const event = eventsByPath.get(path);
389
527
  if (!event || !isReferenceType(event.type) || !event.referencePath)
390
528
  continue;
391
- const terminal = resolveTerminalReferenceEvent(event, eventsByPath, new Set());
529
+ const resolutionState = { exhausted: false };
530
+ const terminal = resolveTerminalReferenceEvent(event, eventsByPath, new Set(), resourcePolicy.max_reference_resolution_steps, resolutionState);
392
531
  if (!terminal) {
532
+ if (resolutionState.exhausted) {
533
+ emitResourceError(ctx, path, `Reference resolution exceeded max_reference_resolution_steps ${resourcePolicy.max_reference_resolution_steps}`, event.span);
534
+ }
393
535
  resolved.delete(path);
394
536
  continue;
395
537
  }
@@ -400,7 +542,7 @@ function resolveReferenceFormEvents(ruleIndex, eventsByPath) {
400
542
  }
401
543
  return resolved;
402
544
  }
403
- function expandSelectorRules(ruleIndex, schema, eventsByPath, ctx) {
545
+ function expandSelectorRules(ruleIndex, schema, eventsByPath, ctx, resourcePolicy, expansionBudget) {
404
546
  const expanded = new Map(ruleIndex);
405
547
  for (const rule of schema.rules) {
406
548
  if (typeof rule.selector !== 'string' || rule.selector.length === 0)
@@ -412,6 +554,11 @@ function expandSelectorRules(ruleIndex, schema, eventsByPath, ctx) {
412
554
  if (!matchesSelectorPath(actualPath, rule.selector))
413
555
  continue;
414
556
  matched = true;
557
+ expansionBudget.count += 1;
558
+ if (expansionBudget.count > resourcePolicy.max_selector_expansions) {
559
+ emitResourceError(ctx, rule.selector, `Selector expansion count exceeds max_selector_expansions ${resourcePolicy.max_selector_expansions}`);
560
+ return expanded;
561
+ }
415
562
  if (!expanded.has(actualPath)) {
416
563
  expanded.set(actualPath, { ...rule, path: actualPath });
417
564
  }
@@ -422,7 +569,7 @@ function expandSelectorRules(ruleIndex, schema, eventsByPath, ctx) {
422
569
  }
423
570
  return expanded;
424
571
  }
425
- function expandWildcardRules(ruleIndex, eventsByPath) {
572
+ function expandWildcardRules(ruleIndex, eventsByPath, ctx, resourcePolicy, expansionBudget) {
426
573
  const expanded = new Map(ruleIndex);
427
574
  for (const [path, rule] of ruleIndex.entries()) {
428
575
  if (!path.includes('[*]'))
@@ -430,6 +577,11 @@ function expandWildcardRules(ruleIndex, eventsByPath) {
430
577
  expanded.delete(path);
431
578
  for (const actualPath of eventsByPath.keys()) {
432
579
  if (matchesAllowedPath(actualPath, path)) {
580
+ expansionBudget.count += 1;
581
+ if (expansionBudget.count > resourcePolicy.max_selector_expansions) {
582
+ emitResourceError(ctx, path, `Wildcard expansion count exceeds max_selector_expansions ${resourcePolicy.max_selector_expansions}`);
583
+ return expanded;
584
+ }
433
585
  expanded.set(actualPath, { ...rule, path: actualPath });
434
586
  }
435
587
  }
@@ -459,6 +611,9 @@ function withoutAnyOf(constraints) {
459
611
  const { any_of: _anyOf, ...rest } = constraints;
460
612
  return rest;
461
613
  }
614
+ function patternMatches(pattern, value) {
615
+ return matchesPortablePattern(pattern, value);
616
+ }
462
617
  function constraintBranchMatchesEvent(constraints, event) {
463
618
  if (constraints.type_is !== undefined) {
464
619
  const containerOk = constraints.type_is === 'list'
@@ -492,7 +647,7 @@ function constraintBranchMatchesEvent(constraints, event) {
492
647
  return false;
493
648
  if (constraints.max_length !== undefined && valueLength > constraints.max_length)
494
649
  return false;
495
- if (constraints.pattern !== undefined && !(new RegExp(constraints.pattern).test(event.value)))
650
+ if (!patternMatches(constraints.pattern, event.value))
496
651
  return false;
497
652
  }
498
653
  if (hasDigitFormConstraints(constraints) && isDigitFormLiteral(event.type)) {
@@ -503,15 +658,19 @@ function constraintBranchMatchesEvent(constraints, event) {
503
658
  return false;
504
659
  if (constraints.max_digits !== undefined && digitCount > constraints.max_digits)
505
660
  return false;
506
- if (event.type === 'RadixLiteral' && constraints.radix !== undefined && firstInvalidRadixDigit(event.raw, constraints.radix) !== null)
661
+ if (event.type === 'RadixLiteral' && constraints.radix !== undefined && !radixConstraintMatches(event.datatype, event.raw, constraints))
507
662
  return false;
508
663
  }
509
664
  return true;
510
665
  }
511
- function resolveTerminalReferenceEvent(event, eventsByPath, activePaths) {
666
+ function resolveTerminalReferenceEvent(event, eventsByPath, activePaths, remainingSteps, state) {
512
667
  if (!isReferenceType(event.type) || !event.referencePath) {
513
668
  return event;
514
669
  }
670
+ if (remainingSteps <= 0) {
671
+ state.exhausted = true;
672
+ return null;
673
+ }
515
674
  const targetPath = formatReferenceLookupPath(event.referencePath);
516
675
  if (activePaths.has(targetPath)) {
517
676
  return null;
@@ -522,7 +681,7 @@ function resolveTerminalReferenceEvent(event, eventsByPath, activePaths) {
522
681
  }
523
682
  activePaths.add(targetPath);
524
683
  const resolved = isReferenceType(target.type)
525
- ? resolveTerminalReferenceEvent(target, eventsByPath, activePaths)
684
+ ? resolveTerminalReferenceEvent(target, eventsByPath, activePaths, remainingSteps - 1, state)
526
685
  : target;
527
686
  activePaths.delete(targetPath);
528
687
  return resolved;
@@ -780,6 +939,28 @@ function checkDatatypeRules(datatypeRules, eventsByPath, ctx) {
780
939
  }
781
940
  }
782
941
  }
942
+ function mergeDatatypeRules(ruleIndex, datatypeRules, eventsByPath) {
943
+ if (!datatypeRules)
944
+ return ruleIndex;
945
+ const merged = new Map();
946
+ for (const [path, rule] of ruleIndex.entries()) {
947
+ merged.set(path, { ...rule, constraints: { ...rule.constraints } });
948
+ }
949
+ for (const [path, event] of eventsByPath.entries()) {
950
+ if (!event.datatype)
951
+ continue;
952
+ const datatypeRule = datatypeRules[datatypeBase(event.datatype).toLowerCase()];
953
+ if (!datatypeRule)
954
+ continue;
955
+ const existing = merged.get(path);
956
+ const constraints = existing?.constraints ?? {};
957
+ merged.set(path, {
958
+ path,
959
+ constraints: { ...datatypeRule, ...constraints },
960
+ });
961
+ }
962
+ return merged;
963
+ }
783
964
  function checkAttributeConstraints(ruleIndex, eventsByPath, datatypeRules, ctx) {
784
965
  for (const [path, rule] of ruleIndex) {
785
966
  if (!rule.constraints.attributes && rule.constraints.closed_attributes !== true)
@@ -854,20 +1035,26 @@ function validateAttributeEntry(path, entry, constraints, datatypeRules, ctx) {
854
1035
  emitError(ctx, createDiag(path, entry.span, `Numeric form violation: expected max ${effectiveConstraints.max_digits} digits, got ${digitCount}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
855
1036
  }
856
1037
  if (entry.type === 'RadixLiteral' && effectiveConstraints.radix !== undefined) {
1038
+ const declaredRadix = declaredRadixFromDatatype(entry.datatype);
1039
+ if ((declaredRadix === null && effectiveConstraints.allow_unspecified_radix !== true)
1040
+ || (declaredRadix !== null && declaredRadix !== effectiveConstraints.radix)) {
1041
+ emitError(ctx, createDiag(path, entry.span, `Numeric form violation: expected declared radix ${effectiveConstraints.radix}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
1042
+ return;
1043
+ }
857
1044
  const invalidDigit = firstInvalidRadixDigit(entry.raw, effectiveConstraints.radix);
858
1045
  if (invalidDigit !== null) {
859
1046
  emitError(ctx, createDiag(path, entry.span, `Numeric form violation: radix literal digit '${invalidDigit}' is outside radix ${effectiveConstraints.radix}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
860
1047
  }
861
1048
  }
862
1049
  }
863
- if (entry.type === 'StringLiteral') {
1050
+ if (isStringType(entry.type)) {
864
1051
  if (effectiveConstraints.min_length !== undefined && entry.value.length < effectiveConstraints.min_length) {
865
1052
  emitError(ctx, createDiag(path, entry.span, `String length violation: expected min length ${effectiveConstraints.min_length}, got ${entry.value.length}`, ErrorCodes.STRING_LENGTH_VIOLATION));
866
1053
  }
867
1054
  if (effectiveConstraints.max_length !== undefined && entry.value.length > effectiveConstraints.max_length) {
868
1055
  emitError(ctx, createDiag(path, entry.span, `String length violation: expected max length ${effectiveConstraints.max_length}, got ${entry.value.length}`, ErrorCodes.STRING_LENGTH_VIOLATION));
869
1056
  }
870
- if (effectiveConstraints.pattern !== undefined && !(new RegExp(effectiveConstraints.pattern).test(entry.value))) {
1057
+ if (effectiveConstraints.pattern !== undefined && !patternMatches(effectiveConstraints.pattern, entry.value)) {
871
1058
  emitError(ctx, createDiag(path, entry.span, `Pattern mismatch: value does not match ${effectiveConstraints.pattern}`, ErrorCodes.PATTERN_MISMATCH));
872
1059
  }
873
1060
  }
@@ -1004,10 +1191,38 @@ function hasDigitFormConstraints(constraints) {
1004
1191
  return constraints.sign !== undefined || constraints.min_digits !== undefined || constraints.max_digits !== undefined || constraints.radix !== undefined;
1005
1192
  }
1006
1193
  function isDigitFormLiteral(type) {
1007
- return type === 'NumberLiteral' || type === 'HexLiteral' || type === 'RadixLiteral' || type === 'SeparatorLiteral';
1194
+ return type === 'NumberLiteral' || type === 'HexLiteral' || type === 'RadixLiteral';
1195
+ }
1196
+ function radixConstraintMatches(datatype, raw, constraints) {
1197
+ if (constraints.radix === undefined)
1198
+ return true;
1199
+ const declaredRadix = declaredRadixFromDatatype(datatype);
1200
+ if (declaredRadix === null && constraints.allow_unspecified_radix !== true)
1201
+ return false;
1202
+ if (declaredRadix !== null && declaredRadix !== constraints.radix)
1203
+ return false;
1204
+ return firstInvalidRadixDigit(raw, constraints.radix) === null;
1205
+ }
1206
+ function declaredRadixFromDatatype(datatype) {
1207
+ if (datatype === undefined)
1208
+ return null;
1209
+ const match = /^radix(?:\[(\d+)\]|(\d+))$/i.exec(datatype.trim());
1210
+ if (!match)
1211
+ return null;
1212
+ const value = Number(match[1] ?? match[2]);
1213
+ return Number.isInteger(value) && value >= 0 ? value : null;
1008
1214
  }
1009
1215
  function isStringType(type) {
1010
- return type === 'StringLiteral' || type === 'TrimtickLiteral';
1216
+ return type === 'StringLiteral'
1217
+ || type === 'TrimtickLiteral'
1218
+ || type === 'TrimtickStringLiteral'
1219
+ || type === 'SeparatorLiteral'
1220
+ || type === 'NullLiteral'
1221
+ || type === 'EncodingLiteral'
1222
+ || type === 'DateLiteral'
1223
+ || type === 'TimeLiteral'
1224
+ || type === 'DateTimeLiteral'
1225
+ || type === 'ZRUTDateTimeLiteral';
1011
1226
  }
1012
1227
  function countFormDigits(type, raw) {
1013
1228
  if (type === 'NumberLiteral')
@@ -1018,7 +1233,7 @@ function countFormDigits(type, raw) {
1018
1233
  .replace(/_/g, '');
1019
1234
  let count = 0;
1020
1235
  for (const char of body) {
1021
- if ((char >= '0' && char <= '9') || (type !== 'SeparatorLiteral' && ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || char === '&' || char === '!'))) {
1236
+ if ((char >= '0' && char <= '9') || ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || char === '&' || char === '!')) {
1022
1237
  count++;
1023
1238
  }
1024
1239
  }