@calcit/procs 0.13.43 → 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 (61) 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/20260824-2119-validate-number-call-metadata.md +8 -0
  6. package/editing-history/20260824-2155-stable-js-struct-layout.md +11 -0
  7. package/editing-history/20260824-2224-struct-index-review-followups.md +7 -0
  8. package/editing-history/20260824-2233-indexed-struct-js-regressions.md +5 -0
  9. package/editing-history/20260824-2256-dynamic-method-analysis.md +9 -0
  10. package/editing-history/20260824-2329-dynamic-method-review-followups.md +7 -0
  11. package/editing-history/202608241731-contiguous-executable-calls.md +34 -0
  12. package/editing-history/202608241920-borrowed-syntax-arguments.md +34 -0
  13. package/editing-history/202608241924-unify-editing-history-directory.md +7 -0
  14. package/editing-history/202608242013-typed-native-number-ops.md +34 -0
  15. package/editing-history/20260825-0020-indexed-enum-match.md +34 -0
  16. package/editing-history/20260825-0100-indexed-enum-match-validation.md +7 -0
  17. package/editing-history/20260825-0120-indexed-enum-match-review.md +7 -0
  18. package/editing-history/20260825-1215-typed-literal-paths.md +56 -0
  19. package/editing-history/20260825-1344-typed-literal-path-review.md +17 -0
  20. package/editing-history/20260825-1505-typed-method-native-op.md +23 -0
  21. package/editing-history/20260825-1540-nil-unit-runtime-separation.md +15 -0
  22. package/editing-history/20260825-1725-typed-method-error-stack-review.md +10 -0
  23. package/editing-history/20260825-1733-nil-unit-doc-review.md +5 -0
  24. package/editing-history/20260825-1758-nil-unit-review-followup.md +6 -0
  25. package/editing-history/20260825-1845-release-01344.md +6 -0
  26. package/examples/bench_enum_match.rs +41 -0
  27. package/lib/calcit-data.d.mts +4 -0
  28. package/lib/calcit-data.mjs +59 -9
  29. package/lib/calcit.procs.d.mts +16 -8
  30. package/lib/calcit.procs.mjs +68 -46
  31. package/lib/custom-formatter.mjs +12 -3
  32. package/lib/js-cirru.mjs +6 -5
  33. package/lib/js-impl.mjs +4 -3
  34. package/lib/js-primes.d.mts +1 -1
  35. package/lib/js-primes.mjs +22 -18
  36. package/lib/js-struct-def.mjs +4 -6
  37. package/lib/js-struct-value.d.mts +5 -1
  38. package/lib/js-struct-value.mjs +66 -38
  39. package/lib/package.json +8 -2
  40. package/package.json +8 -2
  41. package/ts-src/calcit-data.mts +54 -9
  42. package/ts-src/calcit.procs.mts +66 -49
  43. package/ts-src/custom-formatter.mts +12 -3
  44. package/ts-src/js-cirru.mts +6 -5
  45. package/ts-src/js-impl.mts +4 -3
  46. package/ts-src/js-primes.mts +5 -1
  47. package/ts-src/js-struct-def.mts +4 -6
  48. package/ts-src/js-struct-value.mts +78 -33
  49. /package/{history → editing-history}/202608191501-release-01326.md +0 -0
  50. /package/{history → editing-history}/202608191628-revert-transparent-union-types.md +0 -0
  51. /package/{history → editing-history}/202608191741-release-01327.md +0 -0
  52. /package/{history → editing-history}/202608212020-release-01328.md +0 -0
  53. /package/{2026082200-release-01330.md → editing-history/2026082200-release-01330.md} +0 -0
  54. /package/{2026082200-same-package-module-meta.md → editing-history/2026082200-same-package-module-meta.md} +0 -0
  55. /package/{2026082200-transitive-module-loading.md → editing-history/2026082200-transitive-module-loading.md} +0 -0
  56. /package/{2026082222-release-01331.md → editing-history/2026082222-release-01331.md} +0 -0
  57. /package/{history → editing-history}/2026082223-release-01332.md +0 -0
  58. /package/{history → editing-history}/2026082223-same-package-namespaces.md +0 -0
  59. /package/{history → editing-history}/2026082300-fix-cross-package-namespace-merge.md +0 -0
  60. /package/{history → editing-history}/2026082300-release-01333.md +0 -0
  61. /package/{history → editing-history}/2026082316-release-01334.md +0 -0
@@ -105,6 +105,51 @@ export let getStringName = (x) => {
105
105
  }
106
106
  throw new Error("Cannot get string as name");
107
107
  };
108
+ /** Compare tag names by Unicode scalar value, matching Rust `str` ordering. */
109
+ export function compareTagNames(x, y) {
110
+ let xIdx = 0;
111
+ let yIdx = 0;
112
+ while (xIdx < x.value.length && yIdx < y.value.length) {
113
+ const xCode = x.value.codePointAt(xIdx);
114
+ const yCode = y.value.codePointAt(yIdx);
115
+ if (xCode < yCode)
116
+ return -1;
117
+ if (xCode > yCode)
118
+ return 1;
119
+ xIdx += xCode > 0xffff ? 2 : 1;
120
+ yIdx += yCode > 0xffff ? 2 : 1;
121
+ }
122
+ if (xIdx < x.value.length)
123
+ return 1;
124
+ if (yIdx < y.value.length)
125
+ return -1;
126
+ return 0;
127
+ }
128
+ export function tagNamesAreCanonical(fields) {
129
+ for (let idx = 1; idx < fields.length; idx++) {
130
+ if (compareTagNames(fields[idx - 1], fields[idx]) >= 0)
131
+ return false;
132
+ }
133
+ return true;
134
+ }
135
+ export function canonicalizeTagPairs(fields, values, context) {
136
+ if (fields.length !== values.length) {
137
+ throw new Error(`${context}: fields and values length mismatch`);
138
+ }
139
+ if (tagNamesAreCanonical(fields))
140
+ return [fields, values];
141
+ const pairs = fields.map((field, idx) => [field, values[idx]]);
142
+ pairs.sort((a, b) => compareTagNames(a[0], b[0]));
143
+ for (let idx = 1; idx < pairs.length; idx++) {
144
+ if (pairs[idx - 1][0].value === pairs[idx][0].value) {
145
+ throw new Error(`${context}: duplicated field :${pairs[idx][0].value}`);
146
+ }
147
+ }
148
+ return [
149
+ pairs.map(([field]) => field),
150
+ pairs.map(([, value]) => value),
151
+ ];
152
+ }
108
153
  /** returns -1 when not found */
109
154
  export function findInFields(xs, y) {
110
155
  let low = 0;
@@ -112,10 +157,11 @@ export function findInFields(xs, y) {
112
157
  while (low <= high) {
113
158
  const mid = Math.floor((low + high) / 2);
114
159
  const midVal = xs[mid];
115
- if (midVal.idx < y.idx) {
160
+ const ordering = compareTagNames(midVal, y);
161
+ if (ordering < 0) {
116
162
  low = mid + 1;
117
163
  }
118
- else if (midVal.idx > y.idx) {
164
+ else if (ordering > 0) {
119
165
  high = mid - 1;
120
166
  }
121
167
  else {
@@ -156,6 +202,7 @@ export let castTag = (x) => {
156
202
  };
157
203
  export var refsRegistry = new Map();
158
204
  let defaultHash_nil = valueHash("nil:");
205
+ let defaultHash_unit = valueHash("unit:");
159
206
  let defaultHash_number = valueHash("number:");
160
207
  let defaultHash_string = valueHash("string:");
161
208
  let defaultHash_tag = valueHash("tag:");
@@ -177,9 +224,11 @@ let defaultHash_unknown = valueHash("unknown:");
177
224
  let fnHashCounter = 0;
178
225
  let jsObjectHashCounter = 0;
179
226
  export let hashFunction = (x) => {
180
- if (x == null) {
227
+ if (x === null) {
181
228
  return defaultHash_nil;
182
229
  }
230
+ if (x === undefined)
231
+ return defaultHash_unit;
183
232
  if (typeof x === "number") {
184
233
  return mergeValueHash(defaultHash_number, x);
185
234
  }
@@ -367,9 +416,11 @@ let hashCirru = (base, x) => {
367
416
  // Dirty code to change ternary-tree behavior
368
417
  overwriteHashGenerator(hashFunction);
369
418
  export let toString = (x, escaped, disableJsDataWarning = false) => {
370
- if (x == null) {
419
+ if (x === null) {
371
420
  return "nil";
372
421
  }
422
+ if (x === undefined)
423
+ return "&unit";
373
424
  if (typeof x === "string") {
374
425
  if (escaped) {
375
426
  // turn to visual string representation
@@ -473,9 +524,11 @@ export let to_js_data = (x, options) => {
473
524
  return to_js_data_inner(x, addColon);
474
525
  };
475
526
  let to_js_data_inner = (x, addColon) => {
476
- if (x == null) {
527
+ if (x === null) {
477
528
  return null;
478
529
  }
530
+ if (x === undefined)
531
+ return undefined;
479
532
  if (x === true || x === false) {
480
533
  return x;
481
534
  }
@@ -560,10 +613,7 @@ export let _$n__$e_ = (x, y) => {
560
613
  if (x === y) {
561
614
  return true;
562
615
  }
563
- if (x == null) {
564
- if (y == null) {
565
- return true;
566
- }
616
+ if (x === null) {
567
617
  return false;
568
618
  }
569
619
  let tx = typeof x;
@@ -18,7 +18,13 @@ export declare const calcit_package_json: {
18
18
  "test-fail": string;
19
19
  "test-snippets": string;
20
20
  "check-js-runtime": string;
21
+ "check-literal-paths": string;
22
+ "check-typed-method-rem": string;
21
23
  "bench-recur-smoke": string;
24
+ "bench-enum-match": string;
25
+ "bench-literal-paths": string;
26
+ "bench-typed-method-rem": string;
27
+ "bench-struct-index": string;
22
28
  "check-smooth": string;
23
29
  "check-all": string;
24
30
  "check-agent-interface": string;
@@ -87,7 +93,9 @@ export declare function _$n_str_$o_count(x: CalcitValue): number;
87
93
  export declare function _$n_map_$o_count(x: CalcitValue): number;
88
94
  export declare function _$n_struct_$o_count(x: CalcitValue): number;
89
95
  export declare function _$n_struct_$o_field_tag(x: CalcitValue, idx: CalcitValue): CalcitTag;
90
- export declare function _$n_struct_$o_nth(x: CalcitValue, idx: CalcitValue): CalcitValue;
96
+ export declare function _$n_struct_$o_nth(x: CalcitValue, idx: CalcitValue, field?: CalcitValue): CalcitValue;
97
+ export declare function _$n_struct_$o_assoc_at(x: CalcitValue, idx: CalcitValue, field: CalcitValue, value: CalcitValue): CalcitValue;
98
+ export declare function _$n_struct_$o_with_at(x: CalcitValue, ...triples: CalcitValue[]): CalcitValue;
91
99
  export declare function _$n_set_$o_count(x: CalcitValue): number;
92
100
  export declare let _$L_: (...xs: CalcitValue[]) => CalcitSliceList;
93
101
  export declare let _SQUO_: (...xs: CalcitValue[]) => CalcitSliceList;
@@ -119,7 +127,7 @@ export declare let _$n_enum_$o_definition: (x: CalcitEnumValue) => CalcitEnumDef
119
127
  export declare let _$n_enum_def_$o_has_variant_$q_: (enumPrototype: CalcitValue, variantTag: CalcitTag) => boolean;
120
128
  export declare let _$n_enum_def_$o_has_variant: (enumPrototype: CalcitValue, variantTag: CalcitTag) => boolean;
121
129
  export declare let _$n_enum_def_$o_variant_arity: (enumPrototype: CalcitValue, variantTag: CalcitTag) => number;
122
- export declare let _$n_enum_$o_validate: (enumValue: CalcitValue, tag: CalcitValue) => CalcitValue;
130
+ export declare let _$n_enum_$o_validate: (enumValue: CalcitValue, tag: CalcitValue) => void;
123
131
  export declare let _$n_struct_$o_get: (xs: CalcitValue, k: CalcitTag) => CalcitValue;
124
132
  export declare let _$n_list_$o_assoc: (xs: CalcitValue, k: CalcitValue, v: CalcitValue) => CalcitList;
125
133
  export declare let _$n_enum_$o_assoc: (xs: CalcitValue, k: CalcitValue, v: CalcitValue) => CalcitEnumValue;
@@ -136,9 +144,9 @@ export declare let _$n_list_$o_assoc_before: (xs: CalcitList | CalcitSliceList,
136
144
  export declare let _$n_list_$o_assoc_after: (xs: CalcitSliceList, k: number, v: CalcitValue) => CalcitList | CalcitSliceList;
137
145
  export declare let _$n_list_$o_dissoc: (xs: CalcitValue | CalcitSliceList, k: CalcitValue) => CalcitList | CalcitSliceList;
138
146
  export declare let _$n_map_$o_dissoc: (xs: CalcitValue, ...args: CalcitValue[]) => CalcitMap | CalcitSliceMap;
139
- export declare let reset_$x_: (a: CalcitRef, v: CalcitValue) => null;
140
- export declare let add_watch: (a: CalcitRef, k: CalcitTag, f: CalcitFn) => null;
141
- export declare let remove_watch: (a: CalcitRef, k: CalcitTag) => null;
147
+ export declare let reset_$x_: (a: CalcitRef, v: CalcitValue) => CalcitValue;
148
+ export declare let add_watch: (a: CalcitRef, k: CalcitTag, f: CalcitFn) => void;
149
+ export declare let remove_watch: (a: CalcitRef, k: CalcitTag) => void;
142
150
  export declare let range: (n: number, m?: number, step?: number) => CalcitSliceList;
143
151
  export declare function _$n_list_$o_empty_$q_(xs: CalcitValue): boolean;
144
152
  export declare function _$n_str_$o_empty_$q_(xs: CalcitValue): boolean;
@@ -149,7 +157,7 @@ export declare let _$n_list_$o_last: (xs: CalcitValue) => CalcitValue;
149
157
  export declare let _$n_str_$o_first: (xs: CalcitValue) => CalcitValue;
150
158
  export declare let _$n_map_$o_destruct: (xs: CalcitValue) => CalcitValue;
151
159
  export declare let _$n_set_$o_destruct: (xs: CalcitValue) => CalcitValue;
152
- export declare let timeout_call: (duration: number, f: CalcitFn) => null;
160
+ export declare let timeout_call: (duration: number, f: CalcitFn) => void;
153
161
  export declare let _$n_list_$o_rest: (xs: CalcitValue) => CalcitList | CalcitSliceList;
154
162
  export declare let _$n_str_$o_rest: (xs: CalcitValue) => CalcitValue;
155
163
  export declare let recur: (...xs: CalcitValue[]) => CalcitRecur;
@@ -162,7 +170,7 @@ export declare let butlast: (xs: CalcitValue) => CalcitList | CalcitSliceList |
162
170
  export declare let initCrTernary: (x: string) => CalcitValue;
163
171
  export declare let _SHA__$M_: (...xs: CalcitValue[]) => CalcitValue;
164
172
  export declare let generate_id_$x_: () => string;
165
- export declare let _$n_display_stack: () => null;
173
+ export declare let _$n_display_stack: () => void;
166
174
  export declare let _$n_list_$o_slice: (xs: CalcitList, from: number, to: number) => CalcitSliceList | CalcitList;
167
175
  export declare let _$n_list_$o_concat: (...lists: (CalcitList | CalcitSliceList)[]) => CalcitList | CalcitSliceList;
168
176
  export declare let _$n_list_$o_reverse: (xs: CalcitList) => CalcitList;
@@ -247,7 +255,7 @@ export declare let parse_cirru: (code: string) => CalcitCirruQuote;
247
255
  export declare let parse_cirru_list: (code: string) => CalcitList;
248
256
  export declare let parse_cirru_edn: (code: string, options: CalcitValue) => CalcitValue;
249
257
  type DataShapeNode = {
250
- kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic";
258
+ kind: "nil" | "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic";
251
259
  } | {
252
260
  kind: "optional" | "list" | "set" | "ref";
253
261
  inner: number;
@@ -3,7 +3,7 @@ export const calcit_version = pkg.version;
3
3
  export const calcit_package_json = pkg;
4
4
  import { parse } from "@cirru/parser.ts";
5
5
  import { writeCirruCode } from "@cirru/writer.ts";
6
- import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, getStringName, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
6
+ import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, getStringName, compareTagNames, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
7
7
  import { CalcitRef, atom } from "./js-ref.mjs";
8
8
  import { CalcitStructValue } from "./js-struct-value.mjs";
9
9
  import { CalcitImpl } from "./js-impl.mjs";
@@ -50,9 +50,11 @@ export let type_of = (x) => {
50
50
  if (x instanceof CalcitMap || x instanceof CalcitSliceMap) {
51
51
  return newTag("map");
52
52
  }
53
- if (x == null) {
53
+ if (x === null) {
54
54
  return newTag("nil");
55
55
  }
56
+ if (x === undefined)
57
+ return newTag("unit");
56
58
  if (x instanceof CalcitRef) {
57
59
  return newTag("ref");
58
60
  }
@@ -214,7 +216,7 @@ export let defstruct = (name, ...entries) => {
214
216
  const fieldType = items[1];
215
217
  fields.push({ tag: fieldTag, type: fieldType });
216
218
  }
217
- fields.sort((a, b) => a.tag.idx - b.tag.idx);
219
+ fields.sort((a, b) => compareTagNames(a.tag, b.tag));
218
220
  for (let i = 1; i < fields.length; i++) {
219
221
  if (fields[i - 1].tag.value === fields[i].tag.value) {
220
222
  throw new Error(`defstruct duplicated field: ${fields[i].tag.toString()}`);
@@ -237,7 +239,7 @@ export let defenum = (name, ...variants) => {
237
239
  const payload = new CalcitSliceList(items.slice(1));
238
240
  entries.push({ tag, payload });
239
241
  }
240
- entries.sort((a, b) => a.tag.idx - b.tag.idx);
242
+ entries.sort((a, b) => compareTagNames(a.tag, b.tag));
241
243
  for (let i = 1; i < entries.length; i++) {
242
244
  if (entries[i - 1].tag.value === entries[i].tag.value) {
243
245
  throw new Error(`defenum duplicated variant: ${entries[i].tag.toString()}`);
@@ -280,7 +282,7 @@ export let _$n_impl_$o__$o_new = (name, ...pairs) => {
280
282
  }
281
283
  entries.push({ tag: fieldTag, value });
282
284
  }
283
- entries.sort((a, b) => a.tag.idx - b.tag.idx);
285
+ entries.sort((a, b) => compareTagNames(a.tag, b.tag));
284
286
  for (let i = 1; i < entries.length; i++) {
285
287
  if (entries[i - 1].tag.value === entries[i].tag.value) {
286
288
  throw new Error(`&impl::new duplicated field: ${entries[i].tag.toString()}`);
@@ -345,14 +347,26 @@ export function _$n_struct_$o_field_tag(x, idx) {
345
347
  throw new Error(`&struct:field-tag index ${i} out of bounds (${x.fields.length})`);
346
348
  return x.fields[i];
347
349
  }
348
- export function _$n_struct_$o_nth(x, idx) {
350
+ export function _$n_struct_$o_nth(x, idx, field) {
349
351
  if (!(x instanceof CalcitStructValue))
350
352
  throw new Error(`&struct:nth expected a struct value, got ${x}`);
353
+ if (field !== undefined)
354
+ return x.nthAt(idx, field);
351
355
  const i = idx;
352
356
  if (i < 0 || i >= x.values.length)
353
357
  throw new Error(`&struct:nth index ${i} out of range for struct with ${x.values.length} fields`);
354
358
  return x.values[i];
355
359
  }
360
+ export function _$n_struct_$o_assoc_at(x, idx, field, value) {
361
+ if (!(x instanceof CalcitStructValue))
362
+ throw new Error(`&struct:assoc-at expected a struct value, got ${x}`);
363
+ return x.assocAt(idx, field, value);
364
+ }
365
+ export function _$n_struct_$o_with_at(x, ...triples) {
366
+ if (!(x instanceof CalcitStructValue))
367
+ throw new Error(`&struct:with-at expected a struct value, got ${x}`);
368
+ return x.withAt(...triples);
369
+ }
356
370
  export function _$n_set_$o_count(x) {
357
371
  if (x instanceof CalcitSet)
358
372
  return x.len();
@@ -395,7 +409,7 @@ export let _$n__$s_ = (x, y) => {
395
409
  return x * y;
396
410
  };
397
411
  export let _$n_str = (x) => {
398
- if (x == null) {
412
+ if (x === null) {
399
413
  return "";
400
414
  }
401
415
  if (typeof x === "string") {
@@ -607,7 +621,7 @@ export let _$n_enum_$o_validate = function (enumValue, tag) {
607
621
  if (!(enumValue instanceof CalcitEnumValue))
608
622
  throw new Error("&enum:validate expects an enum value as first argument");
609
623
  if (enumValue.enumPrototype == null) {
610
- return null;
624
+ return;
611
625
  }
612
626
  const proto = assert_enum_tag_args("&enum:validate", enumValue.enumPrototype, tag);
613
627
  const tagValue = tag;
@@ -621,7 +635,7 @@ export let _$n_enum_$o_validate = function (enumValue, tag) {
621
635
  if (expected !== actual) {
622
636
  throw new Error(`enum variant expects ${expected} payload(s), got ${actual} for ${enumValue}`);
623
637
  }
624
- return null;
638
+ return;
625
639
  }
626
640
  throw new Error("Expected variant to be a list");
627
641
  };
@@ -783,7 +797,7 @@ export let reset_$x_ = (a, v) => {
783
797
  a.listeners.forEach((f) => {
784
798
  f(v, prev);
785
799
  });
786
- return null;
800
+ return v;
787
801
  };
788
802
  export let add_watch = (a, k, f) => {
789
803
  if (!(a instanceof CalcitRef)) {
@@ -796,11 +810,9 @@ export let add_watch = (a, k, f) => {
796
810
  throw new Error("Expected watcher function");
797
811
  }
798
812
  a.listeners.set(k, f);
799
- return null;
800
813
  };
801
814
  export let remove_watch = (a, k) => {
802
815
  a.listeners.delete(k);
803
- return null;
804
816
  };
805
817
  const MAX_RANGE_LENGTH = 4294967295;
806
818
  export let range = (n, m, step = 1) => {
@@ -921,7 +933,6 @@ export let timeout_call = (duration, f) => {
921
933
  throw new Error("Expected callback in fn");
922
934
  }
923
935
  setTimeout(f, duration);
924
- return null;
925
936
  };
926
937
  export let _$n_list_$o_rest = (xs) => {
927
938
  if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
@@ -1006,10 +1017,9 @@ export let generate_id_$x_ = () => {
1006
1017
  };
1007
1018
  export let _$n_display_stack = () => {
1008
1019
  console.trace();
1009
- return null;
1010
1020
  };
1011
1021
  export let _$n_list_$o_slice = (xs, from, to) => {
1012
- if (xs == null) {
1022
+ if (xs === null) {
1013
1023
  return null;
1014
1024
  }
1015
1025
  let size = xs.len();
@@ -1028,7 +1038,7 @@ export let _$n_list_$o_concat = (...lists) => {
1028
1038
  let result = new CalcitSliceList([]);
1029
1039
  for (let idx = 0; idx < lists.length; idx++) {
1030
1040
  let item = lists[idx];
1031
- if (item == null) {
1041
+ if (item === null) {
1032
1042
  continue;
1033
1043
  }
1034
1044
  if (item instanceof CalcitList || item instanceof CalcitSliceList) {
@@ -1046,7 +1056,7 @@ export let _$n_list_$o_concat = (...lists) => {
1046
1056
  return result;
1047
1057
  };
1048
1058
  export let _$n_list_$o_reverse = (xs) => {
1049
- if (xs == null) {
1059
+ if (xs === null) {
1050
1060
  return null;
1051
1061
  }
1052
1062
  return xs.reverse();
@@ -1075,12 +1085,12 @@ export let round_$q_ = (a) => {
1075
1085
  };
1076
1086
  export let _$n_str_$o_concat = (a, b) => {
1077
1087
  // Optimize string concatenation by avoiding unnecessary toString calls
1078
- const aStr = a != null ? toString(a, false) : "";
1079
- const bStr = b != null ? toString(b, false) : "";
1088
+ const aStr = a !== null ? toString(a, false) : "";
1089
+ const bStr = b !== null ? toString(b, false) : "";
1080
1090
  return aStr + bStr;
1081
1091
  };
1082
1092
  export let sort = (xs, f) => {
1083
- if (xs == null) {
1093
+ if (xs === null) {
1084
1094
  return null;
1085
1095
  }
1086
1096
  if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
@@ -1093,10 +1103,10 @@ export let floor = (n) => {
1093
1103
  return Math.floor(n);
1094
1104
  };
1095
1105
  export let _$n_merge = (a, b) => {
1096
- if (a == null) {
1106
+ if (a === null) {
1097
1107
  return b;
1098
1108
  }
1099
- if (b == null) {
1109
+ if (b === null) {
1100
1110
  return a;
1101
1111
  }
1102
1112
  if (a instanceof CalcitMap || a instanceof CalcitSliceMap) {
@@ -1138,10 +1148,10 @@ export let _$n_merge = (a, b) => {
1138
1148
  throw new Error("Expected map or struct value");
1139
1149
  };
1140
1150
  export let _$n_merge_non_nil = (a, b) => {
1141
- if (a == null) {
1151
+ if (a === null) {
1142
1152
  return b;
1143
1153
  }
1144
- if (b == null) {
1154
+ if (b === null) {
1145
1155
  return a;
1146
1156
  }
1147
1157
  if (!(a instanceof CalcitMap || a instanceof CalcitSliceMap)) {
@@ -1199,7 +1209,7 @@ export let _$n_include = (xs, y) => {
1199
1209
  if (!(xs instanceof CalcitSet)) {
1200
1210
  throw new Error("Expected a set");
1201
1211
  }
1202
- if (y == null) {
1212
+ if (y === null) {
1203
1213
  return xs;
1204
1214
  }
1205
1215
  return xs.include(y);
@@ -1208,7 +1218,7 @@ export let _$n_exclude = (xs, y) => {
1208
1218
  if (!(xs instanceof CalcitSet)) {
1209
1219
  throw new Error("Expected a set");
1210
1220
  }
1211
- if (y == null) {
1221
+ if (y === null) {
1212
1222
  return xs;
1213
1223
  }
1214
1224
  return xs.exclude(y);
@@ -1327,7 +1337,7 @@ export let aget = (x, name) => {
1327
1337
  export let aset = (x, name, v) => {
1328
1338
  return (x[name] = v);
1329
1339
  };
1330
- export let js_get = aget;
1340
+ export let js_get = (x, name) => x[name] ?? null;
1331
1341
  export let js_set = aset;
1332
1342
  /** generates `delete a.b` */
1333
1343
  export let js_delete = (obj, name) => {
@@ -1405,7 +1415,7 @@ export let quit_$x_ = () => {
1405
1415
  }
1406
1416
  };
1407
1417
  export let turn_string = (x) => {
1408
- if (x == null) {
1418
+ if (x === null) {
1409
1419
  return "";
1410
1420
  }
1411
1421
  if (typeof x === "string") {
@@ -1445,7 +1455,7 @@ export let ends_with_$q_ = (xs, y) => {
1445
1455
  return xs.endsWith(y);
1446
1456
  };
1447
1457
  export let blank_$q_ = (x) => {
1448
- if (x == null) {
1458
+ if (x === null) {
1449
1459
  return true;
1450
1460
  }
1451
1461
  if (typeof x === "string") {
@@ -1468,7 +1478,7 @@ export let arrayToList = (xs) => {
1468
1478
  return new CalcitSliceList(xs ?? []);
1469
1479
  };
1470
1480
  export let listToArray = (xs) => {
1471
- if (xs == null) {
1481
+ if (xs === null) {
1472
1482
  return null;
1473
1483
  }
1474
1484
  if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
@@ -1488,7 +1498,7 @@ export let bool_$q_ = (x) => {
1488
1498
  return typeof x === "boolean";
1489
1499
  };
1490
1500
  export let nil_$q_ = (x) => {
1491
- return x == null;
1501
+ return x === null;
1492
1502
  };
1493
1503
  export let tag_$q_ = (x) => {
1494
1504
  return x instanceof CalcitTag;
@@ -1565,8 +1575,10 @@ export let parse_cirru_edn = (code, options) => {
1565
1575
  }
1566
1576
  };
1567
1577
  const typed_edn_kind = (value) => {
1568
- if (value == null)
1578
+ if (value === null)
1569
1579
  return "nil";
1580
+ if (value === undefined)
1581
+ return "unit";
1570
1582
  if (typeof value === "boolean")
1571
1583
  return "bool";
1572
1584
  if (typeof value === "number")
@@ -1608,10 +1620,14 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
1608
1620
  if (node == null)
1609
1621
  typed_edn_error(path, `invalid data shape node #${nodeId}`);
1610
1622
  switch (node.kind) {
1611
- case "unit":
1612
- if (input == null)
1623
+ case "nil":
1624
+ if (input === null)
1613
1625
  return null;
1614
- return typed_edn_error(path, `expected nil, got ${typed_edn_kind(input)}`);
1626
+ return typed_edn_error(path, `expected Nil, got ${typed_edn_kind(input)}`);
1627
+ case "unit":
1628
+ if (input === undefined)
1629
+ return input;
1630
+ return typed_edn_error(path, `expected Unit, got ${typed_edn_kind(input)}`);
1615
1631
  case "bool":
1616
1632
  if (typeof input === "boolean")
1617
1633
  return input;
@@ -1641,7 +1657,7 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
1641
1657
  return input;
1642
1658
  return typed_edn_error(path, `expected cirru-quote, got ${typed_edn_kind(input)}`);
1643
1659
  case "optional":
1644
- return input == null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
1660
+ return input === null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
1645
1661
  case "list": {
1646
1662
  if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
1647
1663
  return typed_edn_error(path, `expected list, got ${typed_edn_kind(input)}`);
@@ -1706,8 +1722,8 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
1706
1722
  const idx = actualNames.indexOf(name);
1707
1723
  decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1));
1708
1724
  });
1709
- // Native declarations use lexical field order while the JS runtime keeps
1710
- // its interned-tag order. Re-align values to the nominal JS declaration.
1725
+ // Re-align by name so values produced by older runtimes or external data
1726
+ // still adopt the current nominal declaration's canonical field layout.
1711
1727
  if (decodedFields.size !== node.nominal.fields.length) {
1712
1728
  return typed_edn_error(path, `struct :${node.nominal.name.value} decoder fields do not match its nominal declaration`);
1713
1729
  }
@@ -1748,8 +1764,8 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
1748
1764
  export let parse_cirru_edn_as = (code, graph) => {
1749
1765
  if (typeof code !== "string")
1750
1766
  throw new Error(`parse-cirru-edn-as expected a string, got ${typed_edn_kind(code)}`);
1751
- if (graph.version !== 1)
1752
- throw new Error(`parse-cirru-edn-as expected data shape ABI version 1, got ${graph.version}`);
1767
+ if (graph.version !== 2)
1768
+ throw new Error(`parse-cirru-edn-as expected data shape ABI version 2, got ${graph.version}`);
1753
1769
  if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
1754
1770
  throw new Error("parse-cirru-edn-as expected a non-empty data shape fingerprint");
1755
1771
  }
@@ -1867,7 +1883,7 @@ const decode_runtime_map_node = (graph, nodeId, input, path, depth) => {
1867
1883
  return new CalcitSliceMap(entries.flat());
1868
1884
  }
1869
1885
  case "optional":
1870
- return input == null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
1886
+ return input === null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
1871
1887
  case "ref":
1872
1888
  if (!(input instanceof CalcitRef))
1873
1889
  return map_decode_error(path, `expected atom, got ${typed_edn_kind(input)}`);
@@ -1897,8 +1913,8 @@ const decode_runtime_map_node = (graph, nodeId, input, path, depth) => {
1897
1913
  }
1898
1914
  };
1899
1915
  export let decode_map_as = (value, graph) => {
1900
- if (graph.version !== 1)
1901
- throw new Error(`decode-map-as expected data shape ABI version 1, got ${graph.version}`);
1916
+ if (graph.version !== 2)
1917
+ throw new Error(`decode-map-as expected data shape ABI version 2, got ${graph.version}`);
1902
1918
  if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
1903
1919
  throw new Error("decode-map-as expected a non-empty data shape fingerprint");
1904
1920
  }
@@ -1940,7 +1956,7 @@ const cirru_quote_to_json = (value) => {
1940
1956
  throw new Error(`Unsupported cirru quote node: ${value}`);
1941
1957
  };
1942
1958
  const calcit_to_json = (value) => {
1943
- if (value == null)
1959
+ if (value === null)
1944
1960
  return null;
1945
1961
  if (typeof value === "string")
1946
1962
  return value;
@@ -2002,9 +2018,12 @@ export let json_pretty = function (value) {
2002
2018
  return JSON.stringify(calcit_to_json(value), null, 2);
2003
2019
  };
2004
2020
  export let format_to_lisp = (x) => {
2005
- if (x == null) {
2021
+ if (x === null) {
2006
2022
  return "nil";
2007
2023
  }
2024
+ else if (x === undefined) {
2025
+ return "&unit";
2026
+ }
2008
2027
  else if (x instanceof CalcitSymbol) {
2009
2028
  return x.value;
2010
2029
  }
@@ -2032,9 +2051,12 @@ export let format_to_cirru = (x) => {
2032
2051
  return writeCirruCode([xs], { useInline: false });
2033
2052
  };
2034
2053
  export let transform_code_to_cirru = (x) => {
2035
- if (x == null) {
2054
+ if (x === null) {
2036
2055
  return "nil";
2037
2056
  }
2057
+ else if (x === undefined) {
2058
+ return "&unit";
2059
+ }
2038
2060
  else if (x instanceof CalcitSymbol) {
2039
2061
  return x.value;
2040
2062
  }
@@ -10,9 +10,12 @@ import { CalcitSet } from "./js-set.mjs";
10
10
  import { CalcitEnumValue } from "./js-enum-value.mjs";
11
11
  import { CalcitCirruQuote } from "./js-cirru.mjs";
12
12
  let embedObject = (x) => {
13
- if (x == null) {
13
+ if (x === null) {
14
14
  return null;
15
15
  }
16
+ if (x === undefined) {
17
+ return span({ whiteSpace: "pre", color: hsl(240, 70, 50) }, "&unit");
18
+ }
16
19
  if (typeof x === "string") {
17
20
  return span({ whiteSpace: "pre", color: hsl(120, 70, 50), maxWidth: "100vw" }, `|${x}`);
18
21
  }
@@ -61,6 +64,12 @@ let td = (style, ...children) => {
61
64
  };
62
65
  /** handle null value in nested data */
63
66
  let saveString = (v) => {
67
+ if (v === null) {
68
+ return "nil";
69
+ }
70
+ if (v === undefined) {
71
+ return "&unit";
72
+ }
64
73
  if (typeof v === "string") {
65
74
  if (v.match(/[\s\"\n\t\,]/)) {
66
75
  return `"|${v}"`;
@@ -69,11 +78,11 @@ let saveString = (v) => {
69
78
  return `|${v}`;
70
79
  }
71
80
  }
72
- else if (v != null && v.toString) {
81
+ else if (v.toString) {
73
82
  return v.toString();
74
83
  }
75
84
  else {
76
- return "nil";
85
+ return String(v);
77
86
  }
78
87
  };
79
88
  export let load_console_formatter_$x_ = () => {
package/lib/js-cirru.mjs CHANGED
@@ -4,7 +4,7 @@ import { CalcitList, CalcitSliceList } from "./js-list.mjs";
4
4
  import { CalcitStructValue } from "./js-struct-value.mjs";
5
5
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
6
6
  import { CalcitSet } from "./js-set.mjs";
7
- import { CalcitTag, CalcitSymbol, CalcitRecur, newTag } from "./calcit-data.mjs";
7
+ import { CalcitTag, CalcitSymbol, CalcitRecur, newTag, compareTagNames } from "./calcit-data.mjs";
8
8
  import { CalcitEnumValue } from "./js-enum-value.mjs";
9
9
  import { CalcitEnumDef } from "./js-enum-def.mjs";
10
10
  import { CalcitImpl } from "./js-impl.mjs";
@@ -79,9 +79,12 @@ export let format_cirru_one_liner = (data) => {
79
79
  };
80
80
  /** better use string version of Cirru EDN in future */
81
81
  export let to_cirru_edn = (x) => {
82
- if (x == null) {
82
+ if (x === null) {
83
83
  return "nil";
84
84
  }
85
+ if (x === undefined) {
86
+ throw new Error("Cirru EDN cannot encode &unit");
87
+ }
85
88
  if (typeof x === "string") {
86
89
  return `|${x}`;
87
90
  }
@@ -364,9 +367,7 @@ const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
364
367
  throw new Error(`Expected field pairs for struct, got: ${pair}`);
365
368
  }
366
369
  });
367
- entries.sort((a, b) => {
368
- return a[0].cmp(b[0]);
369
- });
370
+ entries.sort((a, b) => compareTagNames(a[0], b[0]));
370
371
  let fields = [];
371
372
  let values = [];
372
373
  for (let idx = 0; idx < entries.length; idx++) {
package/lib/js-impl.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { castTag, findInFields, toString } from "./calcit-data.mjs";
1
+ import { canonicalizeTagPairs, castTag, findInFields, toString } from "./calcit-data.mjs";
2
2
  const CALCIT_IMPL_BRAND = Symbol.for("@calcit/procs/CalcitImpl");
3
3
  export class CalcitImpl {
4
4
  static [Symbol.hasInstance](value) {
@@ -29,10 +29,11 @@ export class CalcitImpl {
29
29
  // optimized dependencies. A global, non-enumerable brand keeps impl values
30
30
  // recognizable across those otherwise distinct module instances.
31
31
  Object.defineProperty(this, CALCIT_IMPL_BRAND, { value: true });
32
+ const [canonicalFields, canonicalValues] = canonicalizeTagPairs(fields, values, "CalcitImpl");
32
33
  this.name = name;
33
34
  this.origin = origin;
34
- this.fields = fields;
35
- this.values = values;
35
+ this.fields = canonicalFields;
36
+ this.values = canonicalValues;
36
37
  this.cachedHash = null;
37
38
  }
38
39
  get(k) {
@@ -10,6 +10,6 @@ import { CalcitSet as CalcitSet } from "./js-set.mjs";
10
10
  import { CalcitEnumValue } from "./js-enum-value.mjs";
11
11
  import { CalcitCirruQuote } from "./js-cirru.mjs";
12
12
  import { CalcitTrait } from "./js-trait.mjs";
13
- export type CalcitValue = string | number | boolean | CalcitMap | CalcitSliceMap | CalcitList | CalcitSliceList | CalcitSet | CalcitTag | CalcitSymbol | CalcitRef | CalcitEnumValue | CalcitFn | CalcitRecur | CalcitStructValue | CalcitImpl | CalcitTrait | CalcitStructDef | CalcitEnumDef | CalcitCirruQuote | null;
13
+ export type CalcitValue = string | number | boolean | CalcitMap | CalcitSliceMap | CalcitList | CalcitSliceList | CalcitSet | CalcitTag | CalcitSymbol | CalcitRef | CalcitEnumValue | CalcitFn | CalcitRecur | CalcitStructValue | CalcitImpl | CalcitTrait | CalcitStructDef | CalcitEnumDef | CalcitCirruQuote | undefined | null;
14
14
  export declare let isLiteral: (x: CalcitValue) => boolean;
15
15
  export declare let _$n_compare: (a: CalcitValue, b: CalcitValue) => number;