@calcit/procs 0.13.42 → 0.13.44

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.
Files changed (68) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/RFCs/04-15-match-syntax-rfc.md +14 -8
  3. package/RFCs/04-15-type-directed-optimization-catalog.md +20 -14
  4. package/RFCs/08-05-systematic-nil-reduction-rfc.md +16 -5
  5. package/editing-history/20260823-0401-guard-range-progress.md +9 -0
  6. package/editing-history/20260824-0318-range-backend-consistency.md +13 -0
  7. package/editing-history/20260824-0645-unit-truthiness-review.md +6 -0
  8. package/editing-history/20260824-0655-project-module-merge-review.md +4 -0
  9. package/editing-history/20260824-0705-unit-effect-return-review.md +5 -0
  10. package/editing-history/20260824-1540-scaffold-data-kind-schema.md +6 -0
  11. package/editing-history/20260824-2119-validate-number-call-metadata.md +8 -0
  12. package/editing-history/20260824-2155-stable-js-struct-layout.md +11 -0
  13. package/editing-history/20260824-2224-struct-index-review-followups.md +7 -0
  14. package/editing-history/20260824-2233-indexed-struct-js-regressions.md +5 -0
  15. package/editing-history/20260824-2256-dynamic-method-analysis.md +9 -0
  16. package/editing-history/20260824-2329-dynamic-method-review-followups.md +7 -0
  17. package/editing-history/202608241525-release-01343.md +5 -0
  18. package/editing-history/202608241731-contiguous-executable-calls.md +34 -0
  19. package/editing-history/202608241920-borrowed-syntax-arguments.md +34 -0
  20. package/editing-history/202608241924-unify-editing-history-directory.md +7 -0
  21. package/editing-history/202608242013-typed-native-number-ops.md +34 -0
  22. package/editing-history/20260825-0020-indexed-enum-match.md +34 -0
  23. package/editing-history/20260825-0100-indexed-enum-match-validation.md +7 -0
  24. package/editing-history/20260825-0120-indexed-enum-match-review.md +7 -0
  25. package/editing-history/20260825-1215-typed-literal-paths.md +56 -0
  26. package/editing-history/20260825-1344-typed-literal-path-review.md +17 -0
  27. package/editing-history/20260825-1505-typed-method-native-op.md +23 -0
  28. package/editing-history/20260825-1540-nil-unit-runtime-separation.md +15 -0
  29. package/editing-history/20260825-1725-typed-method-error-stack-review.md +10 -0
  30. package/editing-history/20260825-1733-nil-unit-doc-review.md +5 -0
  31. package/editing-history/20260825-1758-nil-unit-review-followup.md +6 -0
  32. package/editing-history/20260825-1845-release-01344.md +6 -0
  33. package/examples/bench_enum_match.rs +41 -0
  34. package/lib/calcit-data.d.mts +4 -0
  35. package/lib/calcit-data.mjs +59 -9
  36. package/lib/calcit.procs.d.mts +17 -9
  37. package/lib/calcit.procs.mjs +104 -57
  38. package/lib/custom-formatter.mjs +12 -3
  39. package/lib/js-cirru.mjs +6 -5
  40. package/lib/js-impl.mjs +4 -3
  41. package/lib/js-primes.d.mts +1 -1
  42. package/lib/js-primes.mjs +22 -18
  43. package/lib/js-struct-def.mjs +4 -6
  44. package/lib/js-struct-value.d.mts +5 -1
  45. package/lib/js-struct-value.mjs +66 -38
  46. package/lib/package.json +8 -2
  47. package/package.json +8 -2
  48. package/ts-src/calcit-data.mts +54 -9
  49. package/ts-src/calcit.procs.mts +107 -61
  50. package/ts-src/custom-formatter.mts +12 -3
  51. package/ts-src/js-cirru.mts +6 -5
  52. package/ts-src/js-impl.mts +4 -3
  53. package/ts-src/js-primes.mts +5 -1
  54. package/ts-src/js-struct-def.mts +4 -6
  55. package/ts-src/js-struct-value.mts +78 -33
  56. /package/{history → editing-history}/202608191501-release-01326.md +0 -0
  57. /package/{history → editing-history}/202608191628-revert-transparent-union-types.md +0 -0
  58. /package/{history → editing-history}/202608191741-release-01327.md +0 -0
  59. /package/{history → editing-history}/202608212020-release-01328.md +0 -0
  60. /package/{2026082200-release-01330.md → editing-history/2026082200-release-01330.md} +0 -0
  61. /package/{2026082200-same-package-module-meta.md → editing-history/2026082200-same-package-module-meta.md} +0 -0
  62. /package/{2026082200-transitive-module-loading.md → editing-history/2026082200-transitive-module-loading.md} +0 -0
  63. /package/{2026082222-release-01331.md → editing-history/2026082222-release-01331.md} +0 -0
  64. /package/{history → editing-history}/2026082223-release-01332.md +0 -0
  65. /package/{history → editing-history}/2026082223-same-package-namespaces.md +0 -0
  66. /package/{history → editing-history}/2026082300-fix-cross-package-namespace-merge.md +0 -0
  67. /package/{history → editing-history}/2026082300-release-01333.md +0 -0
  68. /package/{history → editing-history}/2026082316-release-01334.md +0 -0
@@ -17,6 +17,7 @@ import {
17
17
  refsRegistry,
18
18
  toString,
19
19
  getStringName,
20
+ compareTagNames,
20
21
  _$n__$e_,
21
22
  hashFunction,
22
23
  } from "./calcit-data.mjs";
@@ -71,9 +72,10 @@ export let type_of = (x: any): CalcitTag => {
71
72
  if (x instanceof CalcitMap || x instanceof CalcitSliceMap) {
72
73
  return newTag("map");
73
74
  }
74
- if (x == null) {
75
+ if (x === null) {
75
76
  return newTag("nil");
76
77
  }
78
+ if (x === undefined) return newTag("unit");
77
79
  if (x instanceof CalcitRef) {
78
80
  return newTag("ref");
79
81
  }
@@ -252,7 +254,7 @@ export let defstruct = (name: CalcitValue, ...entries: CalcitValue[]): CalcitStr
252
254
  fields.push({ tag: fieldTag, type: fieldType });
253
255
  }
254
256
 
255
- fields.sort((a, b) => a.tag.idx - b.tag.idx);
257
+ fields.sort((a, b) => compareTagNames(a.tag, b.tag));
256
258
  for (let i = 1; i < fields.length; i++) {
257
259
  if (fields[i - 1].tag.value === fields[i].tag.value) {
258
260
  throw new Error(`defstruct duplicated field: ${fields[i].tag.toString()}`);
@@ -279,7 +281,7 @@ export let defenum = (name: CalcitValue, ...variants: CalcitValue[]): CalcitEnum
279
281
  entries.push({ tag, payload });
280
282
  }
281
283
 
282
- entries.sort((a, b) => a.tag.idx - b.tag.idx);
284
+ entries.sort((a, b) => compareTagNames(a.tag, b.tag));
283
285
  for (let i = 1; i < entries.length; i++) {
284
286
  if (entries[i - 1].tag.value === entries[i].tag.value) {
285
287
  throw new Error(`defenum duplicated variant: ${entries[i].tag.toString()}`);
@@ -324,7 +326,7 @@ export let _$n_impl_$o__$o_new = (name: CalcitValue, ...pairs: CalcitValue[]): C
324
326
  }
325
327
  entries.push({ tag: fieldTag, value });
326
328
  }
327
- entries.sort((a, b) => a.tag.idx - b.tag.idx);
329
+ entries.sort((a, b) => compareTagNames(a.tag, b.tag));
328
330
  for (let i = 1; i < entries.length; i++) {
329
331
  if (entries[i - 1].tag.value === entries[i].tag.value) {
330
332
  throw new Error(`&impl::new duplicated field: ${entries[i].tag.toString()}`);
@@ -391,13 +393,24 @@ export function _$n_struct_$o_field_tag(x: CalcitValue, idx: CalcitValue): Calci
391
393
  return x.fields[i];
392
394
  }
393
395
 
394
- export function _$n_struct_$o_nth(x: CalcitValue, idx: CalcitValue): CalcitValue {
396
+ export function _$n_struct_$o_nth(x: CalcitValue, idx: CalcitValue, field?: CalcitValue): CalcitValue {
395
397
  if (!(x instanceof CalcitStructValue)) throw new Error(`&struct:nth expected a struct value, got ${x}`);
398
+ if (field !== undefined) return x.nthAt(idx, field);
396
399
  const i = idx as number;
397
400
  if (i < 0 || i >= x.values.length) throw new Error(`&struct:nth index ${i} out of range for struct with ${x.values.length} fields`);
398
401
  return x.values[i];
399
402
  }
400
403
 
404
+ export function _$n_struct_$o_assoc_at(x: CalcitValue, idx: CalcitValue, field: CalcitValue, value: CalcitValue): CalcitValue {
405
+ if (!(x instanceof CalcitStructValue)) throw new Error(`&struct:assoc-at expected a struct value, got ${x}`);
406
+ return x.assocAt(idx, field, value);
407
+ }
408
+
409
+ export function _$n_struct_$o_with_at(x: CalcitValue, ...triples: CalcitValue[]): CalcitValue {
410
+ if (!(x instanceof CalcitStructValue)) throw new Error(`&struct:with-at expected a struct value, got ${x}`);
411
+ return x.withAt(...triples);
412
+ }
413
+
401
414
 
402
415
  export function _$n_set_$o_count(x: CalcitValue): number {
403
416
  if (x instanceof CalcitSet) return x.len();
@@ -449,7 +462,7 @@ export let _$n__$s_ = (x: number, y: number): number => {
449
462
  };
450
463
 
451
464
  export let _$n_str = (x: CalcitValue): string => {
452
- if (x == null) {
465
+ if (x === null) {
453
466
  return "";
454
467
  }
455
468
  if (typeof x === "string") {
@@ -675,11 +688,11 @@ export let _$n_enum_def_$o_variant_arity = function (enumPrototype: CalcitValue,
675
688
  throw new Error("Expected variant to be a list");
676
689
  };
677
690
 
678
- export let _$n_enum_$o_validate = function (enumValue: CalcitValue, tag: CalcitValue): CalcitValue {
691
+ export let _$n_enum_$o_validate = function (enumValue: CalcitValue, tag: CalcitValue): void {
679
692
  if (arguments.length !== 2) throw new Error("&enum:validate takes 2 arguments");
680
693
  if (!(enumValue instanceof CalcitEnumValue)) throw new Error("&enum:validate expects an enum value as first argument");
681
694
  if (enumValue.enumPrototype == null) {
682
- return null;
695
+ return;
683
696
  }
684
697
 
685
698
  const proto = assert_enum_tag_args("&enum:validate", enumValue.enumPrototype as CalcitValue, tag as CalcitTag);
@@ -696,7 +709,7 @@ export let _$n_enum_$o_validate = function (enumValue: CalcitValue, tag: CalcitV
696
709
  if (expected !== actual) {
697
710
  throw new Error(`enum variant expects ${expected} payload(s), got ${actual} for ${enumValue}`);
698
711
  }
699
- return null;
712
+ return;
700
713
  }
701
714
 
702
715
  throw new Error("Expected variant to be a list");
@@ -858,7 +871,7 @@ export let _$n_map_$o_dissoc = function (xs: CalcitValue, ...args: CalcitValue[]
858
871
  throw new Error("`dissoc` expected a map");
859
872
  };
860
873
 
861
- export let reset_$x_ = (a: CalcitRef, v: CalcitValue): null => {
874
+ export let reset_$x_ = (a: CalcitRef, v: CalcitValue): CalcitValue => {
862
875
  if (!(a instanceof CalcitRef)) {
863
876
  throw new Error("Expected ref for reset!");
864
877
  }
@@ -867,10 +880,10 @@ export let reset_$x_ = (a: CalcitRef, v: CalcitValue): null => {
867
880
  a.listeners.forEach((f) => {
868
881
  f(v, prev);
869
882
  });
870
- return null;
883
+ return v;
871
884
  };
872
885
 
873
- export let add_watch = (a: CalcitRef, k: CalcitTag, f: CalcitFn): null => {
886
+ export let add_watch = (a: CalcitRef, k: CalcitTag, f: CalcitFn): void => {
874
887
  if (!(a instanceof CalcitRef)) {
875
888
  throw new Error("Expected ref for add-watch!");
876
889
  }
@@ -881,30 +894,57 @@ export let add_watch = (a: CalcitRef, k: CalcitTag, f: CalcitFn): null => {
881
894
  throw new Error("Expected watcher function");
882
895
  }
883
896
  a.listeners.set(k, f);
884
- return null;
885
897
  };
886
898
 
887
- export let remove_watch = (a: CalcitRef, k: CalcitTag): null => {
899
+ export let remove_watch = (a: CalcitRef, k: CalcitTag): void => {
888
900
  a.listeners.delete(k);
889
- return null;
890
901
  };
891
902
 
892
- export let range = (n: number, m: number, step: number = 1): CalcitSliceList | CalcitList => {
893
- var result: CalcitList | CalcitSliceList = new CalcitSliceList([]);
894
- if (m != null) {
895
- var idx = n;
896
- while (idx < m) {
897
- result = result.append(idx);
898
- idx = idx + step;
903
+ const MAX_RANGE_LENGTH = 0xffff_ffff;
904
+
905
+ export let range = (n: number, m?: number, step: number = 1): CalcitSliceList => {
906
+ const base = m == null ? 0 : n;
907
+ const bound = m == null ? n : m;
908
+
909
+ if (!Number.isFinite(base) || !Number.isFinite(bound) || !Number.isFinite(step)) {
910
+ throw new TypeError("&list:range expected finite numbers for base, bound, and step");
911
+ }
912
+ if (base === bound) {
913
+ return new CalcitSliceList([]);
914
+ }
915
+ if (step === 0 || (bound > base && step < 0) || (bound < base && step > 0)) {
916
+ throw new Error("&list:range cannot construct list with a step of 0 or invalid step direction");
917
+ }
918
+
919
+ // Avoid overflowing `bound - base` when finite endpoints have opposite signs.
920
+ const estimatedLength = (base < 0) !== (bound < 0)
921
+ ? Math.ceil(Math.abs(base) / Math.abs(step) + Math.abs(bound) / Math.abs(step))
922
+ : Math.ceil((bound - base) / step);
923
+ if (!Number.isFinite(estimatedLength) || estimatedLength > MAX_RANGE_LENGTH) {
924
+ throw new Error("&list:range result is too large");
925
+ }
926
+
927
+ const result: Array<CalcitValue> = [];
928
+ if (step > 0) {
929
+ for (let value = base; value < bound; ) {
930
+ result.push(value);
931
+ const next = value + step;
932
+ if (next === value) {
933
+ throw new Error("&list:range step does not advance the current value");
934
+ }
935
+ value = next;
899
936
  }
900
937
  } else {
901
- var idx = 0;
902
- while (idx < n) {
903
- result = result.append(idx);
904
- idx = idx + step;
938
+ for (let value = base; value > bound; ) {
939
+ result.push(value);
940
+ const next = value + step;
941
+ if (next === value) {
942
+ throw new Error("&list:range step does not advance the current value");
943
+ }
944
+ value = next;
905
945
  }
906
946
  }
907
- return result;
947
+ return new CalcitSliceList(result);
908
948
  };
909
949
 
910
950
  export function _$n_list_$o_empty_$q_(xs: CalcitValue): boolean {
@@ -977,7 +1017,7 @@ export let _$n_set_$o_destruct = (xs: CalcitValue): CalcitValue => {
977
1017
  throw new Error("Expect a set");
978
1018
  };
979
1019
 
980
- export let timeout_call = (duration: number, f: CalcitFn): null => {
1020
+ export let timeout_call = (duration: number, f: CalcitFn): void => {
981
1021
  if (typeof duration !== "number") {
982
1022
  throw new Error("Expected duration in number");
983
1023
  }
@@ -985,7 +1025,6 @@ export let timeout_call = (duration: number, f: CalcitFn): null => {
985
1025
  throw new Error("Expected callback in fn");
986
1026
  }
987
1027
  setTimeout(f, duration);
988
- return null;
989
1028
  };
990
1029
 
991
1030
  export let _$n_list_$o_rest = (xs: CalcitValue): CalcitList | CalcitSliceList => {
@@ -1082,13 +1121,12 @@ export let generate_id_$x_ = (): string => {
1082
1121
  return `gen_id_${idCounter}_${time}`;
1083
1122
  };
1084
1123
 
1085
- export let _$n_display_stack = (): null => {
1124
+ export let _$n_display_stack = (): void => {
1086
1125
  console.trace();
1087
- return null;
1088
1126
  };
1089
1127
 
1090
1128
  export let _$n_list_$o_slice = (xs: CalcitList, from: number, to: number): CalcitSliceList | CalcitList => {
1091
- if (xs == null) {
1129
+ if (xs === null) {
1092
1130
  return null;
1093
1131
  }
1094
1132
  let size = xs.len();
@@ -1106,7 +1144,7 @@ export let _$n_list_$o_concat = (...lists: (CalcitList | CalcitSliceList)[]): Ca
1106
1144
  let result: CalcitSliceList | CalcitList = new CalcitSliceList([]);
1107
1145
  for (let idx = 0; idx < lists.length; idx++) {
1108
1146
  let item = lists[idx];
1109
- if (item == null) {
1147
+ if (item === null) {
1110
1148
  continue;
1111
1149
  }
1112
1150
  if (item instanceof CalcitList || item instanceof CalcitSliceList) {
@@ -1123,7 +1161,7 @@ export let _$n_list_$o_concat = (...lists: (CalcitList | CalcitSliceList)[]): Ca
1123
1161
  };
1124
1162
 
1125
1163
  export let _$n_list_$o_reverse = (xs: CalcitList): CalcitList => {
1126
- if (xs == null) {
1164
+ if (xs === null) {
1127
1165
  return null;
1128
1166
  }
1129
1167
  return xs.reverse();
@@ -1154,12 +1192,12 @@ export let round_$q_ = (a: number) => {
1154
1192
  };
1155
1193
  export let _$n_str_$o_concat = (a: string, b: string) => {
1156
1194
  // Optimize string concatenation by avoiding unnecessary toString calls
1157
- const aStr = a != null ? toString(a, false) : "";
1158
- const bStr = b != null ? toString(b, false) : "";
1195
+ const aStr = a !== null ? toString(a, false) : "";
1196
+ const bStr = b !== null ? toString(b, false) : "";
1159
1197
  return aStr + bStr;
1160
1198
  };
1161
1199
  export let sort = (xs: CalcitList | CalcitSliceList, f: CalcitFn): CalcitSliceList => {
1162
- if (xs == null) {
1200
+ if (xs === null) {
1163
1201
  return null;
1164
1202
  }
1165
1203
  if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
@@ -1174,10 +1212,10 @@ export let floor = (n: number): number => {
1174
1212
  };
1175
1213
 
1176
1214
  export let _$n_merge = (a: CalcitValue, b: CalcitMap | CalcitSliceMap): CalcitValue => {
1177
- if (a == null) {
1215
+ if (a === null) {
1178
1216
  return b;
1179
1217
  }
1180
- if (b == null) {
1218
+ if (b === null) {
1181
1219
  return a;
1182
1220
  }
1183
1221
  if (a instanceof CalcitMap || a instanceof CalcitSliceMap) {
@@ -1217,10 +1255,10 @@ export let _$n_merge = (a: CalcitValue, b: CalcitMap | CalcitSliceMap): CalcitVa
1217
1255
  };
1218
1256
 
1219
1257
  export let _$n_merge_non_nil = (a: CalcitMap | CalcitSliceMap, b: CalcitMap | CalcitSliceMap): CalcitMap | CalcitSliceMap => {
1220
- if (a == null) {
1258
+ if (a === null) {
1221
1259
  return b;
1222
1260
  }
1223
- if (b == null) {
1261
+ if (b === null) {
1224
1262
  return a;
1225
1263
  }
1226
1264
  if (!(a instanceof CalcitMap || a instanceof CalcitSliceMap)) {
@@ -1282,7 +1320,7 @@ export let _$n_include = (xs: CalcitSet, y: CalcitValue): CalcitSet => {
1282
1320
  if (!(xs instanceof CalcitSet)) {
1283
1321
  throw new Error("Expected a set");
1284
1322
  }
1285
- if (y == null) {
1323
+ if (y === null) {
1286
1324
  return xs;
1287
1325
  }
1288
1326
  return xs.include(y);
@@ -1292,7 +1330,7 @@ export let _$n_exclude = (xs: CalcitSet, y: CalcitValue): CalcitSet => {
1292
1330
  if (!(xs instanceof CalcitSet)) {
1293
1331
  throw new Error("Expected a set");
1294
1332
  }
1295
- if (y == null) {
1333
+ if (y === null) {
1296
1334
  return xs;
1297
1335
  }
1298
1336
  return xs.exclude(y);
@@ -1423,7 +1461,7 @@ export let aget = (x: any, name: string): any => {
1423
1461
  export let aset = (x: any, name: string, v: any): any => {
1424
1462
  return (x[name] = v);
1425
1463
  };
1426
- export let js_get = aget;
1464
+ export let js_get = (x: any, name: string): any => x[name] ?? null;
1427
1465
  export let js_set = aset;
1428
1466
  /** generates `delete a.b` */
1429
1467
  export let js_delete = (obj: any, name: string): any => {
@@ -1508,7 +1546,7 @@ export let quit_$x_ = (): void => {
1508
1546
  };
1509
1547
 
1510
1548
  export let turn_string = (x: CalcitValue): string => {
1511
- if (x == null) {
1549
+ if (x === null) {
1512
1550
  return "";
1513
1551
  }
1514
1552
  if (typeof x === "string") {
@@ -1551,7 +1589,7 @@ export let ends_with_$q_ = (xs: string, y: string): boolean => {
1551
1589
  };
1552
1590
 
1553
1591
  export let blank_$q_ = (x: string): boolean => {
1554
- if (x == null) {
1592
+ if (x === null) {
1555
1593
  return true;
1556
1594
  }
1557
1595
  if (typeof x === "string") {
@@ -1576,7 +1614,7 @@ export let arrayToList = (xs: Array<CalcitValue>): CalcitSliceList => {
1576
1614
  };
1577
1615
 
1578
1616
  export let listToArray = (xs: CalcitList | CalcitSliceList): Array<CalcitValue> => {
1579
- if (xs == null) {
1617
+ if (xs === null) {
1580
1618
  return null;
1581
1619
  }
1582
1620
  if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
@@ -1596,7 +1634,7 @@ export let bool_$q_ = (x: CalcitValue): boolean => {
1596
1634
  return typeof x === "boolean";
1597
1635
  };
1598
1636
  export let nil_$q_ = (x: CalcitValue): boolean => {
1599
- return x == null;
1637
+ return x === null;
1600
1638
  };
1601
1639
  export let tag_$q_ = (x: CalcitValue): boolean => {
1602
1640
  return x instanceof CalcitTag;
@@ -1676,7 +1714,7 @@ export let parse_cirru_edn = (code: string, options: CalcitValue) => {
1676
1714
  };
1677
1715
 
1678
1716
  type DataShapeNode =
1679
- | { kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic" }
1717
+ | { kind: "nil" | "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic" }
1680
1718
  | { kind: "optional" | "list" | "set" | "ref"; inner: number }
1681
1719
  | { kind: "map"; key: number; value: number }
1682
1720
  | { kind: "map-option"; nominal: CalcitEnumDef; inner: number }
@@ -1691,7 +1729,8 @@ type DataShapeGraph = {
1691
1729
  };
1692
1730
 
1693
1731
  const typed_edn_kind = (value: any): string => {
1694
- if (value == null) return "nil";
1732
+ if (value === null) return "nil";
1733
+ if (value === undefined) return "unit";
1695
1734
  if (typeof value === "boolean") return "bool";
1696
1735
  if (typeof value === "number") return "number";
1697
1736
  if (typeof value === "string") return "string";
@@ -1722,9 +1761,12 @@ const decode_typed_edn_node = (graph: DataShapeGraph, nodeId: number, input: any
1722
1761
  if (node == null) typed_edn_error(path, `invalid data shape node #${nodeId}`);
1723
1762
 
1724
1763
  switch (node.kind) {
1764
+ case "nil":
1765
+ if (input === null) return null;
1766
+ return typed_edn_error(path, `expected Nil, got ${typed_edn_kind(input)}`);
1725
1767
  case "unit":
1726
- if (input == null) return null;
1727
- return typed_edn_error(path, `expected nil, got ${typed_edn_kind(input)}`);
1768
+ if (input === undefined) return input;
1769
+ return typed_edn_error(path, `expected Unit, got ${typed_edn_kind(input)}`);
1728
1770
  case "bool":
1729
1771
  if (typeof input === "boolean") return input;
1730
1772
  return typed_edn_error(path, `expected bool, got ${typed_edn_kind(input)}`);
@@ -1747,7 +1789,7 @@ const decode_typed_edn_node = (graph: DataShapeGraph, nodeId: number, input: any
1747
1789
  if (input instanceof CalcitCirruQuote) return input;
1748
1790
  return typed_edn_error(path, `expected cirru-quote, got ${typed_edn_kind(input)}`);
1749
1791
  case "optional":
1750
- return input == null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
1792
+ return input === null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
1751
1793
  case "list": {
1752
1794
  if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
1753
1795
  return typed_edn_error(path, `expected list, got ${typed_edn_kind(input)}`);
@@ -1816,8 +1858,8 @@ const decode_typed_edn_node = (graph: DataShapeGraph, nodeId: number, input: any
1816
1858
  const idx = actualNames.indexOf(name);
1817
1859
  decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1));
1818
1860
  });
1819
- // Native declarations use lexical field order while the JS runtime keeps
1820
- // its interned-tag order. Re-align values to the nominal JS declaration.
1861
+ // Re-align by name so values produced by older runtimes or external data
1862
+ // still adopt the current nominal declaration's canonical field layout.
1821
1863
  if (decodedFields.size !== node.nominal.fields.length) {
1822
1864
  return typed_edn_error(path, `struct :${node.nominal.name.value} decoder fields do not match its nominal declaration`);
1823
1865
  }
@@ -1863,7 +1905,7 @@ const decode_typed_edn_node = (graph: DataShapeGraph, nodeId: number, input: any
1863
1905
 
1864
1906
  export let parse_cirru_edn_as = (code: string, graph: DataShapeGraph): CalcitValue => {
1865
1907
  if (typeof code !== "string") throw new Error(`parse-cirru-edn-as expected a string, got ${typed_edn_kind(code)}`);
1866
- if (graph.version !== 1) throw new Error(`parse-cirru-edn-as expected data shape ABI version 1, got ${graph.version}`);
1908
+ if (graph.version !== 2) throw new Error(`parse-cirru-edn-as expected data shape ABI version 2, got ${graph.version}`);
1867
1909
  if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
1868
1910
  throw new Error("parse-cirru-edn-as expected a non-empty data shape fingerprint");
1869
1911
  }
@@ -1974,7 +2016,7 @@ const decode_runtime_map_node = (graph: DataShapeGraph, nodeId: number, input: a
1974
2016
  return new CalcitSliceMap(entries.flat());
1975
2017
  }
1976
2018
  case "optional":
1977
- return input == null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
2019
+ return input === null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
1978
2020
  case "ref":
1979
2021
  if (!(input instanceof CalcitRef)) return map_decode_error(path, `expected atom, got ${typed_edn_kind(input)}`);
1980
2022
  return atom(decode_runtime_map_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
@@ -2007,7 +2049,7 @@ const decode_runtime_map_node = (graph: DataShapeGraph, nodeId: number, input: a
2007
2049
  };
2008
2050
 
2009
2051
  export let decode_map_as = (value: CalcitValue, graph: DataShapeGraph): CalcitValue => {
2010
- if (graph.version !== 1) throw new Error(`decode-map-as expected data shape ABI version 1, got ${graph.version}`);
2052
+ if (graph.version !== 2) throw new Error(`decode-map-as expected data shape ABI version 2, got ${graph.version}`);
2011
2053
  if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
2012
2054
  throw new Error("decode-map-as expected a non-empty data shape fingerprint");
2013
2055
  }
@@ -2045,7 +2087,7 @@ const cirru_quote_to_json = (value: ICirruNode): any => {
2045
2087
  };
2046
2088
 
2047
2089
  const calcit_to_json = (value: CalcitValue): any => {
2048
- if (value == null) return null;
2090
+ if (value === null) return null;
2049
2091
  if (typeof value === "string") return value;
2050
2092
  if (typeof value === "number") {
2051
2093
  if (Number.isFinite(value)) return value;
@@ -2102,8 +2144,10 @@ export let json_pretty = function (value: CalcitValue): string {
2102
2144
  };
2103
2145
 
2104
2146
  export let format_to_lisp = (x: CalcitValue): string => {
2105
- if (x == null) {
2147
+ if (x === null) {
2106
2148
  return "nil";
2149
+ } else if (x === undefined) {
2150
+ return "&unit";
2107
2151
  } else if (x instanceof CalcitSymbol) {
2108
2152
  return x.value;
2109
2153
  } else if (x instanceof CalcitList || x instanceof CalcitSliceList) {
@@ -2130,8 +2174,10 @@ export let format_to_cirru = (x: CalcitValue): string => {
2130
2174
  };
2131
2175
 
2132
2176
  export let transform_code_to_cirru = (x: CalcitValue): ICirruNode => {
2133
- if (x == null) {
2177
+ if (x === null) {
2134
2178
  return "nil";
2179
+ } else if (x === undefined) {
2180
+ return "&unit";
2135
2181
  } else if (x instanceof CalcitSymbol) {
2136
2182
  return x.value;
2137
2183
  } else if (x instanceof CalcitList || x instanceof CalcitSliceList) {
@@ -21,9 +21,12 @@ declare global {
21
21
  }
22
22
 
23
23
  let embedObject = (x: CalcitValue) => {
24
- if (x == null) {
24
+ if (x === null) {
25
25
  return null;
26
26
  }
27
+ if (x === undefined) {
28
+ return span({ whiteSpace: "pre", color: hsl(240, 70, 50) }, "&unit");
29
+ }
27
30
  if (typeof x === "string") {
28
31
  return span({ whiteSpace: "pre", color: hsl(120, 70, 50), maxWidth: "100vw" }, `|${x}`);
29
32
  }
@@ -77,16 +80,22 @@ let td = (style: any, ...children: any[]) => {
77
80
 
78
81
  /** handle null value in nested data */
79
82
  let saveString = (v: CalcitValue) => {
83
+ if (v === null) {
84
+ return "nil";
85
+ }
86
+ if (v === undefined) {
87
+ return "&unit";
88
+ }
80
89
  if (typeof v === "string") {
81
90
  if (v.match(/[\s\"\n\t\,]/)) {
82
91
  return `"|${v}"`;
83
92
  } else {
84
93
  return `|${v}`;
85
94
  }
86
- } else if (v != null && v.toString) {
95
+ } else if (v.toString) {
87
96
  return v.toString();
88
97
  } else {
89
- return "nil";
98
+ return String(v);
90
99
  }
91
100
  };
92
101
 
@@ -6,7 +6,7 @@ import { CalcitList, CalcitSliceList } from "./js-list.mjs";
6
6
  import { CalcitStructValue } from "./js-struct-value.mjs";
7
7
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
8
8
  import { CalcitSet } from "./js-set.mjs";
9
- import { CalcitTag, CalcitSymbol, CalcitRecur, newTag } from "./calcit-data.mjs";
9
+ import { CalcitTag, CalcitSymbol, CalcitRecur, newTag, compareTagNames } from "./calcit-data.mjs";
10
10
  import { CalcitEnumValue } from "./js-enum-value.mjs";
11
11
  import { CalcitEnumDef } from "./js-enum-def.mjs";
12
12
  import { CalcitImpl } from "./js-impl.mjs";
@@ -87,9 +87,12 @@ export let format_cirru_one_liner = (data: CalcitCirruQuote | CalcitList): strin
87
87
 
88
88
  /** better use string version of Cirru EDN in future */
89
89
  export let to_cirru_edn = (x: CalcitValue): CirruEdnFormat => {
90
- if (x == null) {
90
+ if (x === null) {
91
91
  return "nil";
92
92
  }
93
+ if (x === undefined) {
94
+ throw new Error("Cirru EDN cannot encode &unit");
95
+ }
93
96
  if (typeof x === "string") {
94
97
  return `|${x}`;
95
98
  }
@@ -352,9 +355,7 @@ const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preser
352
355
  throw new Error(`Expected field pairs for struct, got: ${pair}`);
353
356
  }
354
357
  });
355
- entries.sort((a, b) => {
356
- return a[0].cmp(b[0]);
357
- });
358
+ entries.sort((a, b) => compareTagNames(a[0], b[0]));
358
359
  let fields: Array<CalcitTag> = [];
359
360
  let values: Array<CalcitValue> = [];
360
361
 
@@ -1,6 +1,6 @@
1
1
  import { Hash } from "@calcit/ternary-tree";
2
2
  import { CalcitValue } from "./js-primes.mjs";
3
- import { CalcitTag, castTag, findInFields, toString } from "./calcit-data.mjs";
3
+ import { CalcitTag, canonicalizeTagPairs, castTag, findInFields, toString } from "./calcit-data.mjs";
4
4
  import type { CalcitTrait } from "./js-trait.mjs";
5
5
 
6
6
  const CALCIT_IMPL_BRAND = Symbol.for("@calcit/procs/CalcitImpl");
@@ -44,10 +44,11 @@ export class CalcitImpl {
44
44
  // optimized dependencies. A global, non-enumerable brand keeps impl values
45
45
  // recognizable across those otherwise distinct module instances.
46
46
  Object.defineProperty(this, CALCIT_IMPL_BRAND, { value: true });
47
+ const [canonicalFields, canonicalValues] = canonicalizeTagPairs(fields, values, "CalcitImpl");
47
48
  this.name = name;
48
49
  this.origin = origin;
49
- this.fields = fields;
50
- this.values = values;
50
+ this.fields = canonicalFields;
51
+ this.values = canonicalValues;
51
52
  this.cachedHash = null;
52
53
  }
53
54
 
@@ -32,6 +32,7 @@ export type CalcitValue =
32
32
  | CalcitStructDef
33
33
  | CalcitEnumDef
34
34
  | CalcitCirruQuote
35
+ | undefined
35
36
  | null;
36
37
 
37
38
  export let isLiteral = (x: CalcitValue): boolean => {
@@ -46,6 +47,7 @@ export let isLiteral = (x: CalcitValue): boolean => {
46
47
 
47
48
  enum PseudoTypeIndex {
48
49
  nil,
50
+ unit,
49
51
  bool,
50
52
  number,
51
53
  symbol,
@@ -67,7 +69,8 @@ enum PseudoTypeIndex {
67
69
 
68
70
  let typeAsInt = (x: CalcitValue): number => {
69
71
  // based on order used in Ord trait
70
- if (x == null) return PseudoTypeIndex.nil;
72
+ if (x === null) return PseudoTypeIndex.nil;
73
+ if (x === undefined) return PseudoTypeIndex.unit;
71
74
  let t = typeof x;
72
75
  if (t === "boolean") return PseudoTypeIndex.bool;
73
76
  if (t === "number") return PseudoTypeIndex.number;
@@ -107,6 +110,7 @@ export let _$n_compare = (a: CalcitValue, b: CalcitValue): number => {
107
110
  if (ta === tb) {
108
111
  switch (ta) {
109
112
  case PseudoTypeIndex.nil:
113
+ case PseudoTypeIndex.unit:
110
114
  return 0;
111
115
  case PseudoTypeIndex.bool:
112
116
  return rawCompare(a, b);
@@ -1,4 +1,4 @@
1
- import { CalcitTag, toString } from "./calcit-data.mjs";
1
+ import { CalcitTag, canonicalizeTagPairs, toString } from "./calcit-data.mjs";
2
2
  import { CalcitValue } from "./js-primes.mjs";
3
3
  import { CalcitImpl } from "./js-impl.mjs";
4
4
 
@@ -10,12 +10,10 @@ export class CalcitStructDef {
10
10
  cachedHash: number;
11
11
 
12
12
  constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls: CalcitImpl[] = []) {
13
- if (fields.length !== fieldTypes.length) {
14
- throw new Error("CalcitStructDef: fields and fieldTypes length mismatch");
15
- }
13
+ const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
16
14
  this.name = name;
17
- this.fields = fields;
18
- this.fieldTypes = fieldTypes;
15
+ this.fields = canonicalFields;
16
+ this.fieldTypes = canonicalTypes;
19
17
  this.impls = impls;
20
18
  this.cachedHash = null;
21
19
  }