@manifesto-ai/compiler 1.7.0 → 1.8.1

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/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import {
2
+ DEFAULT_ACTION_CONTEXT,
3
+ DEFAULT_PATCH_CONTEXT,
4
+ DEFAULT_SCHEMA_CONTEXT,
5
+ EFFECT_ARGS_CONTEXT,
2
6
  KEYWORDS,
3
7
  Lexer,
4
8
  LoweringError,
@@ -9,6 +13,7 @@ import {
9
13
  ScopeAnalyzer,
10
14
  SemanticValidator,
11
15
  analyzeScope,
16
+ compareUnicodeCodePoints,
12
17
  compileMelDomain,
13
18
  compileMelPatch,
14
19
  createError,
@@ -20,6 +25,7 @@ import {
20
25
  createWarning,
21
26
  filterBySeverity,
22
27
  generate,
28
+ generateCanonical,
23
29
  getBinaryPrecedence,
24
30
  getKeywordKind,
25
31
  hasErrors,
@@ -35,19 +41,19 @@ import {
35
41
  isStmtNode,
36
42
  isUnaryOp,
37
43
  lowerExprNode,
44
+ lowerPatchFragments,
38
45
  lowerRuntimePatch,
39
46
  lowerRuntimePatches,
40
47
  mergeLocations,
41
- normalizeExpr,
42
- normalizeFunctionCall,
43
48
  parse,
44
49
  tokenToBinaryOp,
45
50
  tokenize,
46
51
  unknownCallFn,
47
52
  unknownNodeKind,
48
53
  unsupportedBase,
54
+ validateAndExpandFlows,
49
55
  validateSemantics
50
- } from "./chunk-QP2LGMBA.js";
56
+ } from "./chunk-4JJQCFJH.js";
51
57
 
52
58
  // src/diagnostics/codes.ts
53
59
  var DIAGNOSTIC_CODES = {
@@ -80,12 +86,12 @@ var DIAGNOSTIC_CODES = {
80
86
  },
81
87
  E006: {
82
88
  code: "E006",
83
- message: "fail must be inside a guard (when or once)",
89
+ message: "fail must be inside a guard (when, once, or onceIntent)",
84
90
  category: "semantic"
85
91
  },
86
92
  E007: {
87
93
  code: "E007",
88
- message: "stop must be inside a guard (when or once)",
94
+ message: "stop must be inside a guard (when, once, or onceIntent)",
89
95
  category: "semantic"
90
96
  },
91
97
  E008: {
@@ -108,6 +114,141 @@ var DIAGNOSTIC_CODES = {
108
114
  message: "reduce/fold/scan is forbidden - use sum, min, max for aggregation",
109
115
  category: "semantic"
110
116
  },
117
+ E013: {
118
+ code: "E013",
119
+ message: "Circular include detected",
120
+ category: "semantic"
121
+ },
122
+ E014: {
123
+ code: "E014",
124
+ message: "Include expansion depth exceeds limit",
125
+ category: "semantic"
126
+ },
127
+ E015: {
128
+ code: "E015",
129
+ message: "Include target is not a declared flow",
130
+ category: "semantic"
131
+ },
132
+ E016: {
133
+ code: "E016",
134
+ message: "Include not allowed in InnerStmt position",
135
+ category: "semantic"
136
+ },
137
+ E017: {
138
+ code: "E017",
139
+ message: "once() not allowed in flow",
140
+ category: "semantic"
141
+ },
142
+ E018: {
143
+ code: "E018",
144
+ message: "onceIntent not allowed in flow",
145
+ category: "semantic"
146
+ },
147
+ E019: {
148
+ code: "E019",
149
+ message: "patch not allowed in flow",
150
+ category: "semantic"
151
+ },
152
+ E020: {
153
+ code: "E020",
154
+ message: "effect not allowed in flow",
155
+ category: "semantic"
156
+ },
157
+ E021: {
158
+ code: "E021",
159
+ message: "Flow parameter name conflicts with top-level identifier",
160
+ category: "semantic"
161
+ },
162
+ E022: {
163
+ code: "E022",
164
+ message: "Flow and action share the same name",
165
+ category: "semantic"
166
+ },
167
+ E023: {
168
+ code: "E023",
169
+ message: "Wrong number of arguments for included flow",
170
+ category: "semantic"
171
+ },
172
+ E024: {
173
+ code: "E024",
174
+ message: "Include argument type mismatch",
175
+ category: "type"
176
+ },
177
+ E030: {
178
+ code: "E030",
179
+ message: "Collection element type does not have an 'id' field",
180
+ category: "type"
181
+ },
182
+ E030a: {
183
+ code: "E030a",
184
+ message: "Collection element 'id' field is not a primitive type",
185
+ category: "type"
186
+ },
187
+ E030b: {
188
+ code: "E030b",
189
+ message: "Duplicate '.id' values detected in state initializer",
190
+ category: "type"
191
+ },
192
+ E031: {
193
+ code: "E031",
194
+ message: "updateById/removeById not allowed in this context",
195
+ category: "semantic"
196
+ },
197
+ E032: {
198
+ code: "E032",
199
+ message: "Nested transform primitive",
200
+ category: "semantic"
201
+ },
202
+ E033: {
203
+ code: "E033",
204
+ message: "Transform primitive collection argument is not a state path",
205
+ category: "semantic"
206
+ },
207
+ E034: {
208
+ code: "E034",
209
+ message: "Transform primitive in guard condition",
210
+ category: "semantic"
211
+ },
212
+ E035: {
213
+ code: "E035",
214
+ message: "Transform primitive in available condition",
215
+ category: "semantic"
216
+ },
217
+ E040: {
218
+ code: "E040",
219
+ message: "Circular computed dependency",
220
+ category: "semantic"
221
+ },
222
+ E041: {
223
+ code: "E041",
224
+ message: "Computed references undeclared identifier",
225
+ category: "semantic"
226
+ },
227
+ E042: {
228
+ code: "E042",
229
+ message: "State initializer references non-constant value",
230
+ category: "semantic"
231
+ },
232
+ E043: {
233
+ code: "E043",
234
+ message: "Non-trivial union type cannot be lowered to FieldSpec",
235
+ category: "type"
236
+ },
237
+ E044: {
238
+ code: "E044",
239
+ message: "Recursive named type cannot be lowered to FieldSpec",
240
+ category: "type"
241
+ },
242
+ E045: {
243
+ code: "E045",
244
+ message: "Nullable type cannot be lowered to FieldSpec",
245
+ category: "type"
246
+ },
247
+ E046: {
248
+ code: "E046",
249
+ message: "Record type cannot be lowered to FieldSpec",
250
+ category: "type"
251
+ },
111
252
  // ============ Scope Errors (E1xx) ============
112
253
  E_UNDEFINED: {
113
254
  code: "E_UNDEFINED",
@@ -223,6 +364,182 @@ function formatDiagnostics(diagnostics, source) {
223
364
  return diagnostics.map((d) => formatDiagnostic(d, source)).join("\n\n");
224
365
  }
225
366
 
367
+ // src/generator/normalizer.ts
368
+ function normalizeExpr(op, left, right) {
369
+ switch (op) {
370
+ // Arithmetic
371
+ case "+":
372
+ return { kind: "add", left, right };
373
+ case "-":
374
+ return { kind: "sub", left, right };
375
+ case "*":
376
+ return { kind: "mul", left, right };
377
+ case "/":
378
+ return { kind: "div", left, right };
379
+ case "%":
380
+ return { kind: "mod", left, right };
381
+ // Comparison
382
+ case "==":
383
+ return { kind: "eq", left, right };
384
+ case "!=":
385
+ return { kind: "neq", left, right };
386
+ case "<":
387
+ return { kind: "lt", left, right };
388
+ case "<=":
389
+ return { kind: "lte", left, right };
390
+ case ">":
391
+ return { kind: "gt", left, right };
392
+ case ">=":
393
+ return { kind: "gte", left, right };
394
+ // Logical
395
+ case "&&":
396
+ return { kind: "and", args: [left, right] };
397
+ case "||":
398
+ return { kind: "or", args: [left, right] };
399
+ // Nullish coalescing
400
+ case "??":
401
+ return { kind: "coalesce", args: [left, right] };
402
+ }
403
+ }
404
+ function normalizeFunctionCall(name, args) {
405
+ switch (name) {
406
+ // ============ Arithmetic ============
407
+ case "add":
408
+ return { kind: "add", left: args[0], right: args[1] };
409
+ case "sub":
410
+ return { kind: "sub", left: args[0], right: args[1] };
411
+ case "mul":
412
+ return { kind: "mul", left: args[0], right: args[1] };
413
+ case "div":
414
+ return { kind: "div", left: args[0], right: args[1] };
415
+ case "mod":
416
+ return { kind: "mod", left: args[0], right: args[1] };
417
+ case "neg":
418
+ return { kind: "neg", arg: args[0] };
419
+ case "abs":
420
+ return { kind: "abs", arg: args[0] };
421
+ case "min":
422
+ if (args.length === 1) {
423
+ return { kind: "minArray", array: args[0] };
424
+ }
425
+ return { kind: "min", args };
426
+ case "max":
427
+ if (args.length === 1) {
428
+ return { kind: "maxArray", array: args[0] };
429
+ }
430
+ return { kind: "max", args };
431
+ // v0.3.2: sum array aggregation
432
+ case "sum":
433
+ return { kind: "sumArray", array: args[0] };
434
+ case "floor":
435
+ return { kind: "floor", arg: args[0] };
436
+ case "ceil":
437
+ return { kind: "ceil", arg: args[0] };
438
+ case "round":
439
+ return { kind: "round", arg: args[0] };
440
+ case "sqrt":
441
+ return { kind: "sqrt", arg: args[0] };
442
+ case "pow":
443
+ return { kind: "pow", base: args[0], exponent: args[1] };
444
+ // ============ Comparison ============
445
+ case "eq":
446
+ return { kind: "eq", left: args[0], right: args[1] };
447
+ case "neq":
448
+ return { kind: "neq", left: args[0], right: args[1] };
449
+ case "gt":
450
+ return { kind: "gt", left: args[0], right: args[1] };
451
+ case "gte":
452
+ return { kind: "gte", left: args[0], right: args[1] };
453
+ case "lt":
454
+ return { kind: "lt", left: args[0], right: args[1] };
455
+ case "lte":
456
+ return { kind: "lte", left: args[0], right: args[1] };
457
+ // ============ Logical ============
458
+ case "and":
459
+ return { kind: "and", args };
460
+ case "or":
461
+ return { kind: "or", args };
462
+ case "not":
463
+ return { kind: "not", arg: args[0] };
464
+ // ============ Type Checking ============
465
+ case "isNull":
466
+ return { kind: "isNull", arg: args[0] };
467
+ case "isNotNull":
468
+ return { kind: "not", arg: { kind: "isNull", arg: args[0] } };
469
+ case "typeof":
470
+ return { kind: "typeof", arg: args[0] };
471
+ case "coalesce":
472
+ return { kind: "coalesce", args };
473
+ // ============ String ============
474
+ case "concat":
475
+ return { kind: "concat", args };
476
+ case "trim":
477
+ return { kind: "trim", str: args[0] };
478
+ case "lower":
479
+ case "toLowerCase":
480
+ return { kind: "toLowerCase", str: args[0] };
481
+ case "upper":
482
+ case "toUpperCase":
483
+ return { kind: "toUpperCase", str: args[0] };
484
+ case "strlen":
485
+ case "strLen":
486
+ return { kind: "strLen", str: args[0] };
487
+ case "substr":
488
+ case "substring":
489
+ return args[2] ? { kind: "substring", str: args[0], start: args[1], end: args[2] } : { kind: "substring", str: args[0], start: args[1] };
490
+ case "toString":
491
+ return { kind: "toString", arg: args[0] };
492
+ // ============ Collection ============
493
+ case "len":
494
+ case "length":
495
+ return { kind: "len", arg: args[0] };
496
+ case "at":
497
+ return { kind: "at", array: args[0], index: args[1] };
498
+ case "first":
499
+ return { kind: "first", array: args[0] };
500
+ case "last":
501
+ return { kind: "last", array: args[0] };
502
+ case "slice":
503
+ return args[2] ? { kind: "slice", array: args[0], start: args[1], end: args[2] } : { kind: "slice", array: args[0], start: args[1] };
504
+ case "includes":
505
+ return { kind: "includes", array: args[0], item: args[1] };
506
+ case "filter":
507
+ return { kind: "filter", array: args[0], predicate: args[1] };
508
+ case "map":
509
+ return { kind: "map", array: args[0], mapper: args[1] };
510
+ case "find":
511
+ return { kind: "find", array: args[0], predicate: args[1] };
512
+ case "every":
513
+ return { kind: "every", array: args[0], predicate: args[1] };
514
+ case "some":
515
+ return { kind: "some", array: args[0], predicate: args[1] };
516
+ case "append":
517
+ return { kind: "append", array: args[0], items: args.slice(1) };
518
+ // ============ Object ============
519
+ case "keys":
520
+ return { kind: "keys", obj: args[0] };
521
+ case "values":
522
+ return { kind: "values", obj: args[0] };
523
+ case "entries":
524
+ return { kind: "entries", obj: args[0] };
525
+ case "merge":
526
+ return { kind: "merge", objects: args };
527
+ // ============ Conditional ============
528
+ case "if":
529
+ case "cond":
530
+ return { kind: "if", cond: args[0], then: args[1], else: args[2] };
531
+ // ============ Unknown Function ============
532
+ default:
533
+ return {
534
+ kind: "object",
535
+ fields: {
536
+ __call: { kind: "lit", value: name },
537
+ __args: { kind: "lit", value: args }
538
+ }
539
+ };
540
+ }
541
+ }
542
+
226
543
  // src/generator/lowering.ts
227
544
  import {
228
545
  hashSchemaSync,
@@ -239,16 +556,7 @@ function lowerSystemValues(schema) {
239
556
  }
240
557
  lowered = true;
241
558
  for (const slot of ctx.slots.values()) {
242
- result.state.fields[slot.valueSlot] = {
243
- type: systemValueType(slot.key),
244
- required: true,
245
- default: null
246
- };
247
- result.state.fields[slot.intentSlot] = {
248
- type: "string",
249
- required: true,
250
- default: null
251
- };
559
+ ensureSystemSlotFields(result.state.fields, actionName, slot);
252
560
  }
253
561
  result.actions[actionName] = {
254
562
  ...action,
@@ -283,6 +591,40 @@ function systemValueType(key) {
283
591
  function normalizeSystemKeyForSlot(key) {
284
592
  return key.replaceAll(".", "_");
285
593
  }
594
+ function ensureObjectField(fields, name) {
595
+ const existing = fields[name];
596
+ if (existing && existing.type === "object") {
597
+ existing.required = false;
598
+ existing.default ??= {};
599
+ existing.fields ??= {};
600
+ return existing;
601
+ }
602
+ const created = {
603
+ type: "object",
604
+ required: false,
605
+ default: {},
606
+ fields: {}
607
+ };
608
+ fields[name] = created;
609
+ return created;
610
+ }
611
+ function ensureSystemSlotFields(rootFields, actionName, slot) {
612
+ const melField = ensureObjectField(rootFields, "$mel");
613
+ const sysField = ensureObjectField(melField.fields ?? (melField.fields = {}), "sys");
614
+ const actionField = ensureObjectField(sysField.fields ?? (sysField.fields = {}), actionName);
615
+ const keyField = ensureObjectField(actionField.fields ?? (actionField.fields = {}), slot.normalizedKey);
616
+ const keyFields = keyField.fields ?? (keyField.fields = {});
617
+ keyFields.intent = {
618
+ type: "string",
619
+ required: false,
620
+ default: null
621
+ };
622
+ keyFields.value = {
623
+ type: systemValueType(slot.key),
624
+ required: false,
625
+ default: null
626
+ };
627
+ }
286
628
  function toPatchPath(path) {
287
629
  return semanticPathToPatchPath(path);
288
630
  }
@@ -324,8 +666,9 @@ function collectSystemRefsFromExpr(expr, ctx) {
324
666
  const normalizedKey = normalizeSystemKeyForSlot(key);
325
667
  ctx.slots.set(key, {
326
668
  key,
327
- valueSlot: `__sys__${ctx.actionName}_${normalizedKey}_value`,
328
- intentSlot: `__sys__${ctx.actionName}_${normalizedKey}_intent`
669
+ normalizedKey,
670
+ valuePath: `$mel.sys.${ctx.actionName}.${normalizedKey}.value`,
671
+ intentPath: `$mel.sys.${ctx.actionName}.${normalizedKey}.intent`
329
672
  });
330
673
  }
331
674
  return;
@@ -362,7 +705,7 @@ function lowerFlow(flow, ctx) {
362
705
  kind: "if",
363
706
  cond: {
364
707
  kind: "neq",
365
- left: { kind: "get", path: slot.intentSlot },
708
+ left: { kind: "get", path: slot.intentPath },
366
709
  right: { kind: "get", path: "meta.intentId" }
367
710
  },
368
711
  then: {
@@ -371,7 +714,7 @@ function lowerFlow(flow, ctx) {
371
714
  {
372
715
  kind: "patch",
373
716
  op: "set",
374
- path: toPatchPath(slot.intentSlot),
717
+ path: toPatchPath(slot.intentPath),
375
718
  value: { kind: "get", path: "meta.intentId" }
376
719
  },
377
720
  {
@@ -379,7 +722,7 @@ function lowerFlow(flow, ctx) {
379
722
  type: "system.get",
380
723
  params: {
381
724
  key: { kind: "lit", value: slot.key },
382
- into: { kind: "lit", value: slot.valueSlot }
725
+ into: { kind: "lit", value: slot.valuePath }
383
726
  }
384
727
  }
385
728
  ]
@@ -391,7 +734,7 @@ function lowerFlow(flow, ctx) {
391
734
  for (const slot of ctx.slots.values()) {
392
735
  readinessConditions.push({
393
736
  kind: "eq",
394
- left: { kind: "get", path: slot.intentSlot },
737
+ left: { kind: "get", path: slot.intentPath },
395
738
  right: { kind: "get", path: "meta.intentId" }
396
739
  });
397
740
  }
@@ -453,7 +796,7 @@ function transformExpr(expr, ctx) {
453
796
  const key = expr.path.slice("$system.".length);
454
797
  const slot = ctx.slots.get(key);
455
798
  if (slot) {
456
- return { kind: "get", path: slot.valueSlot };
799
+ return { kind: "get", path: slot.valuePath };
457
800
  }
458
801
  }
459
802
  switch (expr.kind) {
@@ -1049,134 +1392,6 @@ function renderAsDomain(domainName, fragments, options) {
1049
1392
  return lines.join("\n");
1050
1393
  }
1051
1394
 
1052
- // src/lowering/context.ts
1053
- var DEFAULT_SCHEMA_CONTEXT = {
1054
- mode: "schema",
1055
- allowSysPaths: { prefixes: ["meta", "input"] },
1056
- fnTableVersion: "1.0",
1057
- allowItem: false
1058
- };
1059
- var DEFAULT_ACTION_CONTEXT = {
1060
- mode: "action",
1061
- allowSysPaths: { prefixes: ["meta", "input"] },
1062
- fnTableVersion: "1.0",
1063
- allowItem: false
1064
- };
1065
- var EFFECT_ARGS_CONTEXT = {
1066
- mode: "action",
1067
- allowSysPaths: { prefixes: ["meta", "input"] },
1068
- fnTableVersion: "1.0",
1069
- allowItem: true
1070
- };
1071
- var DEFAULT_PATCH_CONTEXT = {
1072
- allowSysPaths: { prefixes: ["meta", "input"] },
1073
- fnTableVersion: "1.0"
1074
- };
1075
-
1076
- // src/lowering/lower-patch.ts
1077
- function lowerPatchFragments(fragments, ctx) {
1078
- return fragments.map((fragment) => lowerPatchFragment(fragment, ctx));
1079
- }
1080
- function lowerPatchFragment(fragment, ctx) {
1081
- const condition = fragment.condition ? lowerExprNode(fragment.condition, createExprContext(ctx, "action")) : void 0;
1082
- const op = lowerPatchOp(fragment.op, ctx);
1083
- return {
1084
- fragmentId: fragment.fragmentId,
1085
- condition,
1086
- op,
1087
- confidence: fragment.confidence
1088
- };
1089
- }
1090
- function lowerPatchOp(op, ctx) {
1091
- switch (op.kind) {
1092
- case "addType":
1093
- return {
1094
- kind: "addType",
1095
- typeName: op.typeName,
1096
- typeExpr: lowerTypeExpr(op.typeExpr)
1097
- };
1098
- case "addField":
1099
- return {
1100
- kind: "addField",
1101
- typeName: op.typeName,
1102
- field: {
1103
- name: op.field.name,
1104
- type: lowerTypeExpr(op.field.type),
1105
- optional: op.field.optional,
1106
- defaultValue: op.field.defaultValue
1107
- }
1108
- };
1109
- case "setFieldType":
1110
- return {
1111
- kind: "setFieldType",
1112
- path: op.path,
1113
- typeExpr: lowerTypeExpr(op.typeExpr)
1114
- };
1115
- case "setDefaultValue":
1116
- return {
1117
- kind: "setDefaultValue",
1118
- path: op.path,
1119
- value: op.value
1120
- };
1121
- case "addConstraint":
1122
- return {
1123
- kind: "addConstraint",
1124
- targetPath: op.targetPath,
1125
- rule: lowerExprNode(op.rule, createExprContext(ctx, "schema")),
1126
- message: op.message
1127
- };
1128
- case "addComputed":
1129
- return {
1130
- kind: "addComputed",
1131
- name: op.name,
1132
- expr: lowerExprNode(op.expr, createExprContext(ctx, "schema")),
1133
- deps: op.deps
1134
- };
1135
- case "addActionAvailable":
1136
- return {
1137
- kind: "addActionAvailable",
1138
- actionName: op.actionName,
1139
- expr: lowerExprNode(op.expr, createExprContext(ctx, "schema"))
1140
- };
1141
- }
1142
- }
1143
- function lowerTypeExpr(typeExpr) {
1144
- switch (typeExpr.kind) {
1145
- case "primitive":
1146
- return { kind: "primitive", name: typeExpr.name };
1147
- case "array":
1148
- return { kind: "array", element: lowerTypeExpr(typeExpr.element) };
1149
- case "object":
1150
- return {
1151
- kind: "object",
1152
- fields: typeExpr.fields.map((f) => ({
1153
- name: f.name,
1154
- type: lowerTypeExpr(f.type),
1155
- optional: f.optional
1156
- }))
1157
- };
1158
- case "union":
1159
- return {
1160
- kind: "union",
1161
- members: typeExpr.members.map(lowerTypeExpr)
1162
- };
1163
- case "literal":
1164
- return { kind: "literal", value: typeExpr.value };
1165
- case "ref":
1166
- return { kind: "ref", name: typeExpr.name };
1167
- }
1168
- }
1169
- function createExprContext(patchCtx, mode) {
1170
- return {
1171
- mode,
1172
- allowSysPaths: patchCtx.allowSysPaths,
1173
- fnTableVersion: patchCtx.fnTableVersion,
1174
- actionName: patchCtx.actionName,
1175
- allowItem: false
1176
- // Only true for effect.args (not in schema ops)
1177
- };
1178
- }
1179
-
1180
1395
  // src/evaluation/context.ts
1181
1396
  import { parsePath as parsePath2 } from "@manifesto-ai/core";
1182
1397
  function createEvaluationContext(options) {
@@ -1259,6 +1474,8 @@ function evaluateNode(expr, ctx) {
1259
1474
  return evaluateDiv(expr.left, expr.right, ctx);
1260
1475
  case "mod":
1261
1476
  return evaluateMod(expr.left, expr.right, ctx);
1477
+ case "neg":
1478
+ return evaluateNeg(expr.arg, ctx);
1262
1479
  // String
1263
1480
  case "concat":
1264
1481
  return evaluateConcat(expr.args, ctx);
@@ -1465,6 +1682,10 @@ function evaluateMod(left, right, ctx) {
1465
1682
  }
1466
1683
  return l % r;
1467
1684
  }
1685
+ function evaluateNeg(arg, ctx) {
1686
+ const value = evaluateNode(arg, ctx);
1687
+ return typeof value === "number" ? -value : null;
1688
+ }
1468
1689
  function evaluateConcat(args, ctx) {
1469
1690
  const values = [];
1470
1691
  for (const arg of args) {
@@ -1653,7 +1874,8 @@ function evaluateAppend(array, items, ctx) {
1653
1874
  }
1654
1875
  function evaluateObject(fields, ctx) {
1655
1876
  const result = {};
1656
- for (const [key, value] of Object.entries(fields)) {
1877
+ for (const key of Object.keys(fields).sort(compareUnicodeCodePoints)) {
1878
+ const value = fields[key];
1657
1879
  result[key] = evaluateNode(value, ctx);
1658
1880
  }
1659
1881
  return result;
@@ -1670,21 +1892,21 @@ function evaluateKeys(obj, ctx) {
1670
1892
  if (o === null || typeof o !== "object" || Array.isArray(o)) {
1671
1893
  return null;
1672
1894
  }
1673
- return Object.keys(o);
1895
+ return Object.keys(o).sort(compareUnicodeCodePoints);
1674
1896
  }
1675
1897
  function evaluateValues(obj, ctx) {
1676
1898
  const o = evaluateNode(obj, ctx);
1677
1899
  if (o === null || typeof o !== "object" || Array.isArray(o)) {
1678
1900
  return null;
1679
1901
  }
1680
- return Object.values(o);
1902
+ return Object.keys(o).sort(compareUnicodeCodePoints).map((key) => o[key]);
1681
1903
  }
1682
1904
  function evaluateEntries(obj, ctx) {
1683
1905
  const o = evaluateNode(obj, ctx);
1684
1906
  if (o === null || typeof o !== "object" || Array.isArray(o)) {
1685
1907
  return null;
1686
1908
  }
1687
- return Object.entries(o);
1909
+ return Object.keys(o).sort(compareUnicodeCodePoints).map((key) => [key, o[key]]);
1688
1910
  }
1689
1911
  function evaluateMerge(objects, ctx) {
1690
1912
  const result = {};
@@ -2055,13 +2277,26 @@ function compile(source, options = {}) {
2055
2277
  };
2056
2278
  }
2057
2279
  const diagnostics = [];
2058
- if (!options.skipSemanticAnalysis && parseResult.program) {
2059
- const scopeResult = analyzeScope(parseResult.program);
2060
- const validateResult = validateSemantics(parseResult.program);
2280
+ const analyzeStart = performance.now();
2281
+ const flowResult = validateAndExpandFlows(parseResult.program);
2282
+ diagnostics.push(...flowResult.diagnostics);
2283
+ if (!options.skipSemanticAnalysis) {
2284
+ const scopeResult = analyzeScope(flowResult.program);
2285
+ const validateResult = validateSemantics(flowResult.program);
2061
2286
  diagnostics.push(...scopeResult.diagnostics, ...validateResult.diagnostics);
2062
2287
  }
2288
+ trace.push({ phase: "analyze", durationMs: performance.now() - analyzeStart });
2289
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
2290
+ return {
2291
+ schema: null,
2292
+ trace,
2293
+ warnings: diagnostics.filter((diagnostic) => diagnostic.severity === "warning"),
2294
+ errors: diagnostics.filter((diagnostic) => diagnostic.severity === "error"),
2295
+ success: false
2296
+ };
2297
+ }
2063
2298
  const generateStart = performance.now();
2064
- const genResult = generate(parseResult.program);
2299
+ const genResult = generate(flowResult.program);
2065
2300
  diagnostics.push(...genResult.diagnostics);
2066
2301
  trace.push({ phase: "generate", durationMs: performance.now() - generateStart });
2067
2302
  if (options.skipSemanticAnalysis) {
@@ -2146,6 +2381,7 @@ export {
2146
2381
  formatDiagnosticCode,
2147
2382
  formatDiagnostics,
2148
2383
  generate,
2384
+ generateCanonical,
2149
2385
  getBinaryPrecedence,
2150
2386
  getDiagnosticInfo,
2151
2387
  getKeywordKind,