@calcit/procs 0.29.0-alpha.2 → 0.29.0-alpha.23

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.
@@ -208,6 +208,7 @@ export var refsRegistry = new Map();
208
208
  let defaultHash_nil = valueHash("nil:");
209
209
  let defaultHash_unit = valueHash("unit:");
210
210
  let defaultHash_number = valueHash("number:");
211
+ let defaultHash_nan = valueHash("number:NaN");
211
212
  let defaultHash_string = valueHash("string:");
212
213
  let defaultHash_tag = valueHash("tag:");
213
214
  let defaultHash_true = valueHash("bool:true");
@@ -237,7 +238,10 @@ export let hashFunction = (x) => {
237
238
  if (x === undefined)
238
239
  return defaultHash_unit;
239
240
  if (typeof x === "number") {
240
- return mergeValueHash(defaultHash_number, x);
241
+ // NaN equals NaN, so it needs a hash that equals itself; `0` and `-0` share one hash
242
+ if (Number.isNaN(x))
243
+ return defaultHash_nan;
244
+ return mergeValueHash(defaultHash_number, x === 0 ? 0 : x);
241
245
  }
242
246
  if (typeof x === "string") {
243
247
  return mergeValueHash(defaultHash_string, x);
@@ -369,7 +373,8 @@ export let hashFunction = (x) => {
369
373
  let base = defaultHash_impl;
370
374
  base = mergeValueHash(base, hashFunction(x.name));
371
375
  if (x.origin != null) {
372
- base = mergeValueHash(base, hashFunction(x.origin));
376
+ // Impl equality compares origin names, not trait object identities.
377
+ base = mergeValueHash(base, hashFunction(x.origin.name.value));
373
378
  }
374
379
  for (let idx = 0; idx < x.fields.length; idx++) {
375
380
  base = mergeValueHash(base, hashFunction(x.fields[idx]));
@@ -656,8 +661,8 @@ export let _$n__$e_ = (x, y) => {
656
661
  return false;
657
662
  }
658
663
  if (tx === "number") {
659
- // already checked above
660
- return false;
664
+ // value equality: NaN equals NaN (`0 === -0` already held above)
665
+ return Number.isNaN(x) && Number.isNaN(y);
661
666
  }
662
667
  if (tx === "function") {
663
668
  // method values are closures created on the fly (see invoke_method_closure),
@@ -837,7 +842,11 @@ export let _$n__$e_ = (x, y) => {
837
842
  }
838
843
  if (x instanceof CalcitEnumDef) {
839
844
  if (y instanceof CalcitEnumDef) {
840
- return x.name === y.name;
845
+ // The prototype carries the nominal origin, variant tags and payload
846
+ // schemas. Comparing name methods compares one shared function object.
847
+ return _$n__$e_(x.prototype, y.prototype)
848
+ && x.impls.length === y.impls.length
849
+ && x.impls.every((impl, index) => _$n__$e_(impl, y.impls[index]));
841
850
  }
842
851
  return false;
843
852
  }
@@ -31,6 +31,7 @@ export declare const calcit_package_json: {
31
31
  "check-smooth": string;
32
32
  "check-all": string;
33
33
  "check-strict-default": string;
34
+ "check-core-tests": string;
34
35
  "check-agent-interface": string;
35
36
  "try-all": string;
36
37
  "try-core-tests": string;
@@ -111,7 +112,7 @@ export declare let _$L_: (...xs: CalcitValue[]) => CalcitSliceList;
111
112
  export declare let _SQUO_: (...xs: CalcitValue[]) => CalcitSliceList;
112
113
  export declare let _$n__$M_: (...xs: CalcitValue[]) => CalcitSliceMap;
113
114
  export declare let defatom: (path: string, x: CalcitValue) => CalcitValue;
114
- export { atom } from "./js-ref.mjs";
115
+ export { atom, ref } from "./js-ref.mjs";
115
116
  export declare let peekDefatom: (path: string) => CalcitRef;
116
117
  export declare let _$n_atom_$o_deref: (x: CalcitRef) => CalcitValue;
117
118
  export declare let _$n__ADD_: (x: number, y: number) => number;
@@ -154,7 +155,7 @@ export declare let _$n_list_$o_assoc_after: (xs: CalcitSliceList, k: number, v:
154
155
  export declare let _$n_list_$o_dissoc: (xs: CalcitValue | CalcitSliceList, k: CalcitValue) => CalcitList | CalcitSliceList;
155
156
  export declare let _$n_map_$o_dissoc: (xs: CalcitValue, ...args: CalcitValue[]) => CalcitMap | CalcitSliceMap;
156
157
  export declare let reset_$x_: (a: CalcitRef, v: CalcitValue) => CalcitValue;
157
- export declare let add_watch: (a: CalcitRef, k: CalcitTag, f: CalcitFn) => void;
158
+ export declare let add_watch_$x_: (a: CalcitRef, k: CalcitTag, f: CalcitFn) => void;
158
159
  export declare let remove_watch: (a: CalcitRef, k: CalcitTag) => void;
159
160
  export declare let range: (n: number, m?: number, step?: number) => CalcitSliceList;
160
161
  export declare function _$n_list_$o_empty_$q_(xs: CalcitValue): boolean;
@@ -188,7 +189,8 @@ export declare let _$n__GT_: (a: number, b: number) => boolean;
188
189
  export declare let _$n__LT_: (a: number, b: number) => boolean;
189
190
  export declare let _$n__: (a: number, b: number) => number;
190
191
  export declare let _$n__SLSH_: (a: number, b: number) => number;
191
- /** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`. */
192
+ /** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
193
+ * Operands in the error use the native number text (`inf`, expanded exponents) so messages match native. */
192
194
  export declare let _$n_number_$o_rem: (a: number, b: number) => number;
193
195
  export declare let _$n_number_$o_fits_$q_: (value: number, target: CalcitTag) => boolean;
194
196
  export declare let round_$q_: (a: number) => boolean;
@@ -241,7 +243,7 @@ export declare let turn_symbol: (x: CalcitValue) => CalcitSymbol;
241
243
  export declare let to_lispy_string: (...args: CalcitValue[]) => string;
242
244
  /** helper function for println, js only */
243
245
  export declare let printable: (...args: CalcitValue[]) => string;
244
- export declare let cpu_time: () => number;
246
+ export declare let monotonic_time_ms: () => number;
245
247
  export declare let quit_$x_: (code: number) => void;
246
248
  export declare let turn_string: (x: CalcitValue) => string;
247
249
  export declare let identical_$q_: (x: CalcitValue, y: CalcitValue) => boolean;
@@ -323,6 +325,8 @@ type DataShapeGraph = {
323
325
  fingerprint: string;
324
326
  nodes: DataShapeNode[];
325
327
  };
328
+ /** Validate existing data without the decoder's map conversion or Option lifting. */
329
+ export declare function validate_data_shape(input: CalcitValue, graph: DataShapeGraph): boolean;
326
330
  export declare let parse_cirru_edn_as: (code: string, graph: DataShapeGraph) => CalcitValue;
327
331
  export declare let decode_map_as: (value: CalcitValue, graph: DataShapeGraph) => CalcitValue;
328
332
  export declare let json_parse: (code: CalcitValue) => CalcitValue;
@@ -361,6 +365,7 @@ export declare function _$n_methods_of(obj: CalcitValue): CalcitSliceList;
361
365
  export declare function _$n_inspect_methods(obj: CalcitValue, note: CalcitValue): CalcitValue;
362
366
  /** Dispatch through one nominal origin; duplicate implementations never select by order. */
363
367
  export declare function _$n_trait_call(traitDef: CalcitValue, method: CalcitValue, obj: CalcitValue, ...args: CalcitValue[]): CalcitValue;
368
+ export declare let _$n_map_$o_fold_kv: (m: CalcitValue, initial: CalcitValue, reducer: CalcitFn) => CalcitValue;
364
369
  export declare let _$n_map_$o_to_list: (m: CalcitValue) => CalcitSliceList;
365
370
  export declare let _$n_map_$o_keys: (m: CalcitValue) => CalcitSet;
366
371
  export declare let _$n_map_$o_vals: (m: CalcitValue) => CalcitSliceList;
@@ -224,7 +224,7 @@ export let _$n_trait_$o__$o_new = function (name, methods, parents) {
224
224
  const requires = parents == null ? [] : list_items(parents);
225
225
  for (const parent of requires) {
226
226
  if (!(parent instanceof CalcitTrait)) {
227
- throw new Error(`trait ${toString(name, false)} 'requires expects a trait, but received: ${toString(parent, true)}`);
227
+ throw new Error(`trait ${toString(name, false)}: each requires entry must be a trait, but received: ${toString(parent, true)}`);
228
228
  }
229
229
  }
230
230
  const traitDef = new CalcitTrait(name, methodNames, methodTypes, requires);
@@ -508,7 +508,7 @@ export let defatom = (path, x) => {
508
508
  refsRegistry.set(path, v);
509
509
  return v;
510
510
  };
511
- export { atom } from "./js-ref.mjs";
511
+ export { atom, ref } from "./js-ref.mjs";
512
512
  export let peekDefatom = (path) => {
513
513
  return refsRegistry.get(path);
514
514
  };
@@ -817,7 +817,7 @@ export let _$n_struct_$o_impl_traits = function (xs, ...traits) {
817
817
  throw new Error("&struct:impl-traits expected a struct value");
818
818
  const impls = traits.map((trait) => coerce_impl(trait, "&struct:impl-traits"));
819
819
  checkRequiredTraitImpls(xs.structRef.impls.concat(impls), "&struct:impl-traits");
820
- const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls), xs.structRef.definitionRef);
820
+ const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls), xs.structRef.definitionRef, xs.structRef.fieldValidators);
821
821
  return new CalcitStructValue(xs.name, xs.fields, xs.values, nextStruct);
822
822
  };
823
823
  export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
@@ -828,7 +828,7 @@ export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
828
828
  const addedImpls = traits.map((trait) => coerce_impl(trait, "&struct-def:impl-traits"));
829
829
  const baseImpls = xs.impls ?? [];
830
830
  checkRequiredTraitImpls(baseImpls.concat(addedImpls), "&struct-def:impl-traits");
831
- return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls), xs.definitionRef);
831
+ return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls), xs.definitionRef, xs.fieldValidators);
832
832
  };
833
833
  export let _$n_enum_def_$o_impl_traits = function (xs, ...traits) {
834
834
  if (traits.length < 1)
@@ -920,7 +920,7 @@ export let reset_$x_ = (a, v) => {
920
920
  });
921
921
  return v;
922
922
  };
923
- export let add_watch = (a, k, f) => {
923
+ export let add_watch_$x_ = (a, k, f) => {
924
924
  if (!(a instanceof CalcitRef)) {
925
925
  throw new Error("Expected ref for add-watch!");
926
926
  }
@@ -1200,10 +1200,11 @@ export let _$n__ = (a, b) => {
1200
1200
  export let _$n__SLSH_ = (a, b) => {
1201
1201
  return a / b;
1202
1202
  };
1203
- /** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`. */
1203
+ /** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
1204
+ * Operands in the error use the native number text (`inf`, expanded exponents) so messages match native. */
1204
1205
  export let _$n_number_$o_rem = (a, b) => {
1205
1206
  if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
1206
- throw new Error(`&number:rem requires safe integers, but received: ${toString(a, false)} ${toString(b, false)}`);
1207
+ throw new Error(`&number:rem requires safe integers, but received: ${format_calcit_number(a)} ${format_calcit_number(b)}`);
1207
1208
  }
1208
1209
  if (b === 0) {
1209
1210
  throw new Error("&number:rem divisor must not be zero");
@@ -1418,7 +1419,12 @@ export let _$n_str_$o_replace = (x, y, z) => {
1418
1419
  return x.replace(new RegExp(literalPattern, "gu"), () => z);
1419
1420
  };
1420
1421
  export let split = (xs, x) => {
1421
- return new CalcitSliceList(xs.split(x));
1422
+ // An empty delimiter splits by code point, matching native for astral characters.
1423
+ if (x === "") {
1424
+ return new CalcitSliceList(Array.from(xs));
1425
+ }
1426
+ // Drop empty pieces to match native and WASM: `split | |,` is an empty list.
1427
+ return new CalcitSliceList(xs.split(x).filter((piece) => piece !== ""));
1422
1428
  };
1423
1429
  export let split_lines = (xs) => {
1424
1430
  return new CalcitSliceList(xs.split("\n"));
@@ -1655,8 +1661,8 @@ export let to_lispy_string = (...args) => {
1655
1661
  export let printable = (...args) => {
1656
1662
  return args.map((x) => toString(x, false)).join(" ");
1657
1663
  };
1658
- // time from app start
1659
- export let cpu_time = () => {
1664
+ // monotonic milliseconds since app start
1665
+ export let monotonic_time_ms = () => {
1660
1666
  if (inNodeJs) {
1661
1667
  // uptime returns in seconds
1662
1668
  return process.uptime() * 1000;
@@ -1941,6 +1947,64 @@ export let parse_cirru_edn = (code, options) => {
1941
1947
  throw new Error(`Expected EDN in a single node, got ${nodes.length}`);
1942
1948
  }
1943
1949
  };
1950
+ /** Validate existing data without the decoder's map conversion or Option lifting. */
1951
+ export function validate_data_shape(input, graph) {
1952
+ if (graph.version !== 3)
1953
+ throw new Error(`validate-data-shape expected ABI version 3, got ${graph.version}`);
1954
+ const sameNominal = (actual, expected) => actual === expected || (actual != null && expected != null && expected.definitionRef != null &&
1955
+ actual.definitionRef === expected.definitionRef && actual.name.value === expected.name.value &&
1956
+ actual.fields.length === expected.fields.length && actual.fields.every((field, idx) => field.value === expected.fields[idx].value && _$n__$e_(actual.fieldTypes[idx], expected.fieldTypes[idx])));
1957
+ const check = (value, nodeId, depth) => {
1958
+ if (depth > 1024)
1959
+ return false;
1960
+ const node = graph.nodes[nodeId];
1961
+ if (node == null)
1962
+ return false;
1963
+ switch (node.kind) {
1964
+ case "dynamic": return true;
1965
+ case "nil": return value === null;
1966
+ case "unit": return value === undefined;
1967
+ case "bool": return typeof value === "boolean";
1968
+ case "number": return typeof value === "number";
1969
+ case "numeric": return typeof value === "number" && _$n_number_$o_fits_$q_(value, newTag(node.target));
1970
+ case "string": return typeof value === "string";
1971
+ case "symbol": return value instanceof CalcitSymbol;
1972
+ case "tag": return value instanceof CalcitTag;
1973
+ case "buffer": return value instanceof Uint8Array;
1974
+ case "cirru-quote": return value instanceof CalcitCirruQuote;
1975
+ case "optional": return value === null || check(value, node.inner, depth + 1);
1976
+ case "list": {
1977
+ if (!(value instanceof CalcitList || value instanceof CalcitSliceList))
1978
+ return false;
1979
+ for (const item of value.items())
1980
+ if (!check(item, node.inner, depth + 1))
1981
+ return false;
1982
+ return true;
1983
+ }
1984
+ case "set": return value instanceof CalcitSet && value.values().every(item => check(item, node.inner, depth + 1));
1985
+ case "map": return (value instanceof CalcitMap || value instanceof CalcitSliceMap) && value.pairs().every(([key, item]) => check(key, node.key, depth + 1) && check(item, node.value, depth + 1));
1986
+ case "ref": return value instanceof CalcitRef && check(value.value, node.inner, depth + 1);
1987
+ case "struct": return value instanceof CalcitStructValue && sameNominal(value.structRef, node.nominal) &&
1988
+ value.values.length === node.fields.length && node.fields.every(([name, child], idx) => value.fields[idx]?.value === name && check(value.values[idx], child, depth + 1));
1989
+ case "map-option":
1990
+ case "enum": {
1991
+ if (!(value instanceof CalcitEnumValue) || value.enumPrototype == null ||
1992
+ !sameNominal(value.enumPrototype.prototype.structRef, node.nominal.prototype.structRef) ||
1993
+ !_$n__$e_(value.enumPrototype.prototype, node.nominal.prototype) || !(value.tag instanceof CalcitTag))
1994
+ return false;
1995
+ if (node.kind === "map-option") {
1996
+ return value.tag.value === "none" ? value.extra.length === 0 : value.tag.value === "some" &&
1997
+ value.extra.length === 1 && check(value.extra[0], node.inner, depth + 1);
1998
+ }
1999
+ const tag = value.tag.value;
2000
+ const variant = node.variants.find(candidate => candidate.tag === tag);
2001
+ return variant != null && value.extra.length === variant.payload.length &&
2002
+ variant.payload.every((child, idx) => check(value.extra[idx], child, depth + 1));
2003
+ }
2004
+ }
2005
+ };
2006
+ return check(input, graph.root, 0);
2007
+ }
1944
2008
  const typed_edn_kind = (value) => {
1945
2009
  if (value === null)
1946
2010
  return "nil";
@@ -2808,6 +2872,19 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
2808
2872
  }
2809
2873
  throw new Error(`&trait-call: cannot find impl for trait ${traitDef.name.toString()} on ${toString(obj, true)}. Hint: use defimpl to create impls tagged by trait.`);
2810
2874
  }
2875
+ export let _$n_map_$o_fold_kv = (m, initial, reducer) => {
2876
+ if (!(m instanceof CalcitMap || m instanceof CalcitSliceMap)) {
2877
+ throw new Error("&map:fold-kv expected a Map");
2878
+ }
2879
+ if (typeof reducer !== "function") {
2880
+ throw new Error("&map:fold-kv expected a callback function");
2881
+ }
2882
+ let accumulator = initial;
2883
+ for (const [key, value] of m.pairs()) {
2884
+ accumulator = reducer(accumulator, key, value);
2885
+ }
2886
+ return accumulator;
2887
+ };
2811
2888
  export let _$n_map_$o_to_list = (m) => {
2812
2889
  if (m instanceof CalcitMap || m instanceof CalcitSliceMap) {
2813
2890
  let ys = [];
@@ -12,4 +12,6 @@ import { CalcitCirruQuote } from "./js-cirru.mjs";
12
12
  import { CalcitTrait } from "./js-trait.mjs";
13
13
  export type CalcitValue = string | number | boolean | CalcitMap | CalcitSliceMap | CalcitList | CalcitSliceList | CalcitSet | CalcitTag | CalcitSymbol | CalcitRef | CalcitEnumValue | CalcitFn | CalcitRecur | CalcitStructValue | CalcitImpl | CalcitTrait | CalcitStructDef | CalcitEnumDef | CalcitCirruQuote | Uint8Array | undefined | null;
14
14
  export declare let isLiteral: (x: CalcitValue) => boolean;
15
+ /** total order over numbers: `-0` equals `0`, NaN equals NaN and sorts after every number */
16
+ export declare let compareNumbers: (x: number, y: number) => number;
15
17
  export declare let _$n_compare: (a: CalcitValue, b: CalcitValue) => number;
package/lib/js-primes.mjs CHANGED
@@ -101,6 +101,14 @@ let rawCompare = (x, y) => {
101
101
  return 0;
102
102
  }
103
103
  };
104
+ /** total order over numbers: `-0` equals `0`, NaN equals NaN and sorts after every number */
105
+ export let compareNumbers = (x, y) => {
106
+ if (x < y)
107
+ return -1;
108
+ if (x > y)
109
+ return 1;
110
+ return (Number.isNaN(x) ? 1 : 0) - (Number.isNaN(y) ? 1 : 0);
111
+ };
104
112
  /** lexicographic order over two sequences of values, a shorter prefix sorts first (same as Rust `Vec::cmp`) */
105
113
  let compareSequences = (xs, ys) => {
106
114
  let n = Math.min(xs.length, ys.length);
@@ -112,6 +120,18 @@ let compareSequences = (xs, ys) => {
112
120
  return rawCompare(xs.length, ys.length);
113
121
  };
114
122
  let listToArray = (x) => Array.from(x.items());
123
+ // Plain functions compare by identity. IDs are process-local, never serialized,
124
+ // and kept weakly so ordering does not retain closures or mutate frozen hosts.
125
+ const functionOrderIds = new WeakMap();
126
+ let nextFunctionOrderId = 0;
127
+ const functionOrderId = (fn) => {
128
+ let id = functionOrderIds.get(fn);
129
+ if (id === undefined) {
130
+ id = ++nextFunctionOrderId;
131
+ functionOrderIds.set(fn, id);
132
+ }
133
+ return id;
134
+ };
115
135
  export let _$n_compare = (a, b) => {
116
136
  if (a === b)
117
137
  return 0;
@@ -125,7 +145,7 @@ export let _$n_compare = (a, b) => {
125
145
  case PseudoTypeIndex.bool:
126
146
  return rawCompare(a, b);
127
147
  case PseudoTypeIndex.number:
128
- return rawCompare(a, b);
148
+ return compareNumbers(a, b);
129
149
  case PseudoTypeIndex.tag:
130
150
  return rawCompare(a.value, b.value);
131
151
  case PseudoTypeIndex.symbol:
@@ -134,6 +154,36 @@ export let _$n_compare = (a, b) => {
134
154
  return rawCompare(a, b);
135
155
  case PseudoTypeIndex.ref:
136
156
  return rawCompare(a.path, b.path);
157
+ case PseudoTypeIndex.fn: {
158
+ const left = a;
159
+ const right = b;
160
+ // Method closures use name equality, unlike ordinary function values.
161
+ if (left.__calcitMethodName != null || right.__calcitMethodName != null) {
162
+ if (left.__calcitMethodName == null)
163
+ return -1;
164
+ if (right.__calcitMethodName == null)
165
+ return 1;
166
+ return compareUnicodeStrings(left.__calcitMethodName, right.__calcitMethodName);
167
+ }
168
+ return rawCompare(functionOrderId(left), functionOrderId(right));
169
+ }
170
+ case PseudoTypeIndex.impl: {
171
+ const left = a;
172
+ const right = b;
173
+ const nameOrder = compareUnicodeStrings(left.name.value, right.name.value);
174
+ if (nameOrder !== 0)
175
+ return nameOrder;
176
+ if (left.origin == null && right.origin != null)
177
+ return -1;
178
+ if (left.origin != null && right.origin == null)
179
+ return 1;
180
+ if (left.origin != null && right.origin != null) {
181
+ const originOrder = compareUnicodeStrings(left.origin.name.value, right.origin.name.value);
182
+ if (originOrder !== 0)
183
+ return originOrder;
184
+ }
185
+ return compareSequences(left.fields, right.fields) || compareSequences(left.values, right.values);
186
+ }
137
187
  case PseudoTypeIndex.struct_value: {
138
188
  const left = a;
139
189
  const right = b;
@@ -176,6 +226,43 @@ export let _$n_compare = (a, b) => {
176
226
  return tagOrder;
177
227
  return compareSequences(left.extra, right.extra);
178
228
  }
229
+ case PseudoTypeIndex.enum_def: {
230
+ const left = a;
231
+ const right = b;
232
+ const leftRef = left.prototype.structRef.definitionRef;
233
+ const rightRef = right.prototype.structRef.definitionRef;
234
+ if (leftRef !== rightRef) {
235
+ if (leftRef == null)
236
+ return -1;
237
+ if (rightRef == null)
238
+ return 1;
239
+ return compareUnicodeStrings(leftRef, rightRef);
240
+ }
241
+ const nameOrder = compareUnicodeStrings(left.name(), right.name());
242
+ if (nameOrder !== 0)
243
+ return nameOrder;
244
+ const variantCountOrder = rawCompare(left.prototype.fields.length, right.prototype.fields.length);
245
+ if (variantCountOrder !== 0)
246
+ return variantCountOrder;
247
+ for (let index = 0; index < left.prototype.fields.length; index++) {
248
+ const tagOrder = _$n_compare(left.prototype.fields[index], right.prototype.fields[index]);
249
+ if (tagOrder !== 0)
250
+ return tagOrder;
251
+ const leftPayload = left.prototype.values[index];
252
+ const rightPayload = right.prototype.values[index];
253
+ // Native orders variant payloads by arity before comparing types.
254
+ if ((leftPayload instanceof CalcitList || leftPayload instanceof CalcitSliceList)
255
+ && (rightPayload instanceof CalcitList || rightPayload instanceof CalcitSliceList)) {
256
+ const arityOrder = rawCompare(leftPayload.len(), rightPayload.len());
257
+ if (arityOrder !== 0)
258
+ return arityOrder;
259
+ }
260
+ const payloadOrder = _$n_compare(leftPayload, rightPayload);
261
+ if (payloadOrder !== 0)
262
+ return payloadOrder;
263
+ }
264
+ return rawCompare(left.impls.length, right.impls.length) || compareSequences(left.impls, right.impls);
265
+ }
179
266
  case PseudoTypeIndex.set: {
180
267
  // like native: smaller sets first, then the sorted elements
181
268
  const left = Array.from(a.values());
package/lib/js-ref.d.mts CHANGED
@@ -9,4 +9,7 @@ export declare class CalcitRef {
9
9
  constructor(x: CalcitValue, path: string);
10
10
  toString(): string;
11
11
  }
12
+ /** Creates a local `Ref<T>`; generated code calls this for both `ref` and the compatibility spelling `atom`. */
13
+ export declare let ref: (x: CalcitValue) => CalcitValue;
14
+ /** Compatibility export for JavaScript callers written before `ref`; it is the same function. */
12
15
  export declare let atom: (x: CalcitValue) => CalcitValue;
package/lib/js-ref.mjs CHANGED
@@ -10,8 +10,11 @@ export class CalcitRef {
10
10
  }
11
11
  }
12
12
  var atomCounter = 0;
13
- export let atom = (x) => {
13
+ /** Creates a local `Ref<T>`; generated code calls this for both `ref` and the compatibility spelling `atom`. */
14
+ export let ref = (x) => {
14
15
  atomCounter = atomCounter + 1;
15
16
  let v = new CalcitRef(x, `atom-${atomCounter}`);
16
17
  return v;
17
18
  };
19
+ /** Compatibility export for JavaScript callers written before `ref`; it is the same function. */
20
+ export let atom = ref;
@@ -1,6 +1,7 @@
1
1
  import { CalcitTag } from "./calcit-data.mjs";
2
2
  import { CalcitValue } from "./js-primes.mjs";
3
3
  import { CalcitImpl } from "./js-impl.mjs";
4
+ export type FieldValidator = ((value: CalcitValue, requireEvidence?: boolean) => boolean) | null;
4
5
  export declare class CalcitStructDef {
5
6
  name: CalcitTag;
6
7
  fields: CalcitTag[];
@@ -8,9 +9,10 @@ export declare class CalcitStructDef {
8
9
  impls: CalcitImpl[];
9
10
  cachedHash: number;
10
11
  readonly definitionRef: string | null;
11
- constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls?: CalcitImpl[], definitionRef?: string | null);
12
+ readonly fieldValidators: FieldValidator[] | null;
13
+ constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls?: CalcitImpl[], definitionRef?: string | null, fieldValidators?: FieldValidator[] | null);
12
14
  withImpls(impls: CalcitImpl | CalcitImpl[]): CalcitStructDef;
13
15
  toString(disableJsDataWarning?: boolean): string;
14
16
  }
15
- /** Match native definition binding without replacing an alias's existing identity. */
16
- export declare function bind_struct_definition(value: CalcitValue, definitionRef: string): CalcitValue;
17
+ /** Bind nominal definitions like native, preserving an alias's existing identity. */
18
+ export declare function bind_struct_definition(value: CalcitValue, definitionRef: string, fieldValidators?: FieldValidator[] | null): CalcitValue;
@@ -1,7 +1,9 @@
1
1
  import { canonicalizeTagPairs, toString } from "./calcit-data.mjs";
2
2
  import { CalcitImpl } from "./js-impl.mjs";
3
+ import { CalcitEnumDef } from "./js-enum-def.mjs";
4
+ import { CalcitStructValue } from "./js-struct-value.mjs";
3
5
  export class CalcitStructDef {
4
- constructor(name, fields, fieldTypes, impls = [], definitionRef = null) {
6
+ constructor(name, fields, fieldTypes, impls = [], definitionRef = null, fieldValidators = null) {
5
7
  const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
6
8
  this.name = name;
7
9
  this.fields = canonicalFields;
@@ -9,13 +11,14 @@ export class CalcitStructDef {
9
11
  this.impls = impls ?? [];
10
12
  this.cachedHash = null;
11
13
  this.definitionRef = definitionRef;
14
+ this.fieldValidators = fieldValidators == null ? null : canonicalizeTagPairs(fields, fieldValidators, "CalcitStructDef validators")[1];
12
15
  }
13
16
  withImpls(impls) {
14
17
  if (impls instanceof CalcitImpl) {
15
- return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls], this.definitionRef);
18
+ return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls], this.definitionRef, this.fieldValidators);
16
19
  }
17
20
  else if (Array.isArray(impls)) {
18
- return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls, this.definitionRef);
21
+ return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls, this.definitionRef, this.fieldValidators);
19
22
  }
20
23
  throw new Error("Expected an impl as implementation");
21
24
  }
@@ -33,10 +36,15 @@ export class CalcitStructDef {
33
36
  return parts.join("");
34
37
  }
35
38
  }
36
- /** Match native definition binding without replacing an alias's existing identity. */
37
- export function bind_struct_definition(value, definitionRef) {
39
+ /** Bind nominal definitions like native, preserving an alias's existing identity. */
40
+ export function bind_struct_definition(value, definitionRef, fieldValidators = null) {
38
41
  if (value instanceof CalcitStructDef && value.definitionRef == null) {
39
- return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef);
42
+ return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef, fieldValidators);
43
+ }
44
+ if (value instanceof CalcitEnumDef && value.prototype.structRef.definitionRef == null) {
45
+ const prototype = value.prototype;
46
+ const structRef = bind_struct_definition(prototype.structRef, definitionRef);
47
+ return new CalcitEnumDef(new CalcitStructValue(prototype.name, prototype.fields, prototype.values, structRef));
40
48
  }
41
49
  return value;
42
50
  }
@@ -1,7 +1,17 @@
1
1
  import { CalcitImpl } from "./js-impl.mjs";
2
- import { newTag, castTag, toString, getStringName, findInFields, compareTagNames, canonicalizeTagPairs, } from "./calcit-data.mjs";
2
+ import { newTag, castTag, toString, CalcitTag, CalcitSymbol, getStringName, findInFields, compareTagNames, canonicalizeTagPairs, } from "./calcit-data.mjs";
3
3
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
4
4
  import { CalcitStructDef } from "./js-struct-def.mjs";
5
+ import { valueMatchesTypeForm } from "./js-type-form.mjs";
6
+ /// Field writes check the declared field type, like the native runtime.
7
+ let assertFieldValue = (structRef, idx, value, operation, requireEvidence = false) => {
8
+ const fieldType = structRef?.fieldTypes?.[idx];
9
+ const validator = structRef?.fieldValidators?.[idx];
10
+ if (validator != null ? !validator(value, requireEvidence)
11
+ : fieldType != null && !valueMatchesTypeForm(value, fieldType, requireEvidence)) {
12
+ throw new Error(`${operation} field :${structRef.fields[idx].value} expects type ${toString(fieldType, true)}, but received ${toString(value, true)}`);
13
+ }
14
+ };
5
15
  export class CalcitStructValue {
6
16
  constructor(name, fields, values, structRef) {
7
17
  this.name = name;
@@ -40,16 +50,14 @@ export class CalcitStructValue {
40
50
  }
41
51
  }
42
52
  assoc(k, v) {
43
- let values = new Array(this.fields.length);
44
- let k_id = castTag(k);
45
- for (let idx = 0; idx < this.fields.length; idx++) {
46
- if (this.fields[idx] === k_id) {
47
- values[idx] = v;
48
- }
49
- else {
50
- values[idx] = this.values[idx];
51
- }
53
+ const field = castTag(k);
54
+ const idx = findInFields(this.fields, field);
55
+ if (idx < 0) {
56
+ throw new Error(`&struct:assoc invalid field ${field.toString()} for struct '${this.name.value}'`);
52
57
  }
58
+ assertFieldValue(this.structRef, idx, v, "&struct:assoc", true);
59
+ const values = this.values.slice();
60
+ values[idx] = v;
53
61
  return new CalcitStructValue(this.name, this.fields, values, this.structRef);
54
62
  }
55
63
  nthAt(index, field) {
@@ -60,6 +68,7 @@ export class CalcitStructValue {
60
68
  assocAt(index, field, value) {
61
69
  const idx = checkedStructIndex(index, "&struct:assoc-at");
62
70
  this.assertFieldAt(idx, field, "&struct:assoc-at");
71
+ assertFieldValue(this.structRef, idx, value, "&struct:assoc-at");
63
72
  const values = this.values.slice();
64
73
  values[idx] = value;
65
74
  return new CalcitStructValue(this.name, this.fields, values, this.structRef);
@@ -72,6 +81,7 @@ export class CalcitStructValue {
72
81
  for (let base = 0; base < triples.length; base += 3) {
73
82
  const idx = checkedStructIndex(triples[base], "&struct:with-at");
74
83
  this.assertFieldAt(idx, triples[base + 1], "&struct:with-at");
84
+ assertFieldValue(this.structRef, idx, triples[base + 2], "&struct:with-at");
75
85
  values[idx] = triples[base + 2];
76
86
  }
77
87
  return new CalcitStructValue(this.name, this.fields, values, this.structRef);
@@ -116,7 +126,7 @@ export class CalcitStructValue {
116
126
  else {
117
127
  throw new Error("Expected an impl or array of impls");
118
128
  }
119
- let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls), this.structRef.definitionRef);
129
+ let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls), this.structRef.definitionRef, this.structRef.fieldValidators);
120
130
  return new CalcitStructValue(this.name, this.fields, this.values, nextStruct);
121
131
  }
122
132
  }
@@ -181,45 +191,38 @@ export let fieldsEqual = (xs, ys) => {
181
191
  }
182
192
  return true;
183
193
  };
184
- export let _$n__PCT__$M_ = (proto, ...xs) => {
185
- let recordProto;
186
- if (proto instanceof CalcitStructValue) {
187
- recordProto = proto;
194
+ // Share complete construction without spreading map contents into call arguments.
195
+ const constructStruct = (proto, xs) => {
196
+ if (!(proto instanceof CalcitStructDef)) {
197
+ throw new Error("Expected prototype to be a StructDef");
188
198
  }
189
- else if (proto instanceof CalcitStructDef) {
190
- recordProto = new CalcitStructValue(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
199
+ if (xs.length % 2 !== 0) {
200
+ throw new Error("Expected even number of key/value");
191
201
  }
192
- else {
193
- throw new Error("Expected prototype to be a StructDef");
202
+ if (xs.length !== proto.fields.length * 2) {
203
+ throw new Error("fields size does not match");
194
204
  }
195
- {
196
- if (xs.length % 2 !== 0) {
197
- throw new Error("Expected even number of key/value");
205
+ const values = new Array(proto.fields.length);
206
+ const seen = new Set();
207
+ for (let i = 0; i < xs.length; i += 2) {
208
+ const key = xs[i];
209
+ if (!(key instanceof CalcitTag) && !(key instanceof CalcitSymbol) && typeof key !== "string") {
210
+ throw new Error("&%{} requires field in string/tag/symbol");
198
211
  }
199
- if (xs.length !== recordProto.fields.length * 2) {
200
- throw new Error("fields size does not match");
212
+ const idx = findInFields(proto.fields, castTag(key));
213
+ if (idx < 0) {
214
+ throw new Error("invalid field name for this struct");
201
215
  }
202
- let values = new Array(recordProto.fields.length);
203
- for (let i = 0; i < recordProto.fields.length; i++) {
204
- let idx = -1;
205
- let k = recordProto.fields[i];
206
- for (let j = 0; j < recordProto.fields.length; j++) {
207
- if (k === castTag(xs[j * 2])) {
208
- idx = j;
209
- break;
210
- }
211
- }
212
- if (idx < 0) {
213
- throw new Error("invalid field name for this struct");
214
- }
215
- if (values[i] != null) {
216
- throw new Error("struct field already has value, probably duplicated key");
217
- }
218
- values[i] = xs[idx * 2 + 1];
216
+ if (seen.has(idx)) {
217
+ throw new Error("struct field already has value, probably duplicated key");
219
218
  }
220
- return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
219
+ seen.add(idx);
220
+ assertFieldValue(proto, idx, xs[i + 1], "&%{}");
221
+ values[idx] = xs[i + 1];
221
222
  }
223
+ return new CalcitStructValue(proto.name, proto.fields, values, proto);
222
224
  };
225
+ export let _$n__PCT__$M_ = (proto, ...xs) => constructStruct(proto, xs);
223
226
  /// update record with new values
224
227
  export let _$n_struct_$o_with = (proto, ...xs) => {
225
228
  if (proto instanceof CalcitStructValue) {
@@ -234,6 +237,7 @@ export let _$n_struct_$o_with = (proto, ...xs) => {
234
237
  if (idx < 0) {
235
238
  throw new Error(`Cannot find field ${k} among ${proto.fields}`);
236
239
  }
240
+ assertFieldValue(proto.structRef, idx, v, "&struct:with", true);
237
241
  values[idx] = v;
238
242
  }
239
243
  return new CalcitStructValue(proto.name, proto.fields, values, proto.structRef);
@@ -261,57 +265,20 @@ export let _$n_struct_$o_definition = (x) => {
261
265
  }
262
266
  };
263
267
  export let _$n_struct_$o_from_map = (proto, data) => {
264
- let recordProto;
265
- if (proto instanceof CalcitStructDef) {
266
- recordProto = new CalcitStructValue(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
267
- }
268
- else {
268
+ if (!(proto instanceof CalcitStructDef)) {
269
269
  throw new Error("Expected prototype to be struct");
270
270
  }
271
- if (data instanceof CalcitStructValue) {
272
- if (fieldsEqual(recordProto.fields, data.fields)) {
273
- return new CalcitStructValue(recordProto.name, recordProto.fields, data.values, recordProto.structRef);
274
- }
275
- else {
276
- let values = [];
277
- for (let i = 0; i < recordProto.fields.length; i++) {
278
- let field = recordProto.fields[i];
279
- let idx = findInFields(data.fields, field);
280
- if (idx < 0) {
281
- throw new Error(`Cannot find field ${field} among ${data.fields}`);
282
- }
283
- values.push(data.values[idx]);
284
- }
285
- return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
286
- }
271
+ if (!(data instanceof CalcitMap) && !(data instanceof CalcitSliceMap)) {
272
+ throw new Error("&struct:from-map requires a Map");
287
273
  }
288
- else if (data instanceof CalcitMap || data instanceof CalcitSliceMap) {
289
- let pairs_buffer = [];
290
- let pairs = data.pairs();
291
- for (let i = 0; i < pairs.length; i++) {
292
- let k = pairs[i][0];
293
- let v = pairs[i][1];
294
- pairs_buffer.push([castTag(k), v]);
274
+ const fields = [];
275
+ for (const [key, value] of data.pairs()) {
276
+ if (!(key instanceof CalcitTag) && typeof key !== "string") {
277
+ throw new Error("&struct:from-map requires field in string/tag");
295
278
  }
296
- // mutable sort
297
- pairs_buffer.sort((pair1, pair2) => pair1[0].cmp(pair2[0]));
298
- let values = [];
299
- outerLoop: for (let i = 0; i < recordProto.fields.length; i++) {
300
- let field = recordProto.fields[i];
301
- for (let idx = 0; idx < pairs_buffer.length; idx++) {
302
- let pair = pairs_buffer[idx];
303
- if (pair[0] === field) {
304
- values.push(pair[1]);
305
- continue outerLoop; // dirty code for performance
306
- }
307
- }
308
- throw new Error(`Cannot find field ${field} among ${pairs_buffer}`);
309
- }
310
- return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
311
- }
312
- else {
313
- throw new Error("Expected a struct value or data for making a struct");
279
+ fields.push(key, value);
314
280
  }
281
+ return constructStruct(proto, fields);
315
282
  };
316
283
  export let _$n_struct_$o_to_map = (x) => {
317
284
  if (x instanceof CalcitStructValue) {
@@ -0,0 +1,2 @@
1
+ import { CalcitValue } from "./js-primes.mjs";
2
+ export declare let valueMatchesTypeForm: (value: CalcitValue, form: CalcitValue, requireEvidence?: boolean) => boolean;
@@ -0,0 +1,188 @@
1
+ import { CalcitSymbol, CalcitTag } from "./calcit-data.mjs";
2
+ import { CalcitList, CalcitSliceList } from "./js-list.mjs";
3
+ import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
4
+ import { CalcitSet } from "./js-set.mjs";
5
+ import { CalcitRef } from "./js-ref.mjs";
6
+ import { CalcitEnumValue } from "./js-enum-value.mjs";
7
+ import { CalcitCirruQuote } from "./js-cirru.mjs";
8
+ /// Shallow runtime check of a value against a struct field type form, matching
9
+ /// the native `value_matches_type_annotation`: container kinds are checked,
10
+ /// members are not. Names that cannot be resolved here (aliases, traits, type
11
+ /// variables) stay permissive only for the legacy, statically checked path.
12
+ /// Dynamic writes require evidence: deep contracts use generated validators,
13
+ /// while this fallback accepts only completely checkable scalar/open forms.
14
+ export let valueMatchesTypeForm = (value, form, requireEvidence = false) => matchTypeForm(value, form, requireEvidence) === true;
15
+ // null means that the contract cannot be checked here, not a mismatched value.
16
+ // Nullable absence proves its own branch; present payloads still require
17
+ // evidence for the inner contract.
18
+ let matchTypeForm = (value, form, requireEvidence) => {
19
+ if (form instanceof CalcitSliceList || form instanceof CalcitList) {
20
+ const items = form.toArray();
21
+ const head = items[0];
22
+ if (head instanceof CalcitSymbol && head.value === "quote") {
23
+ return matchTypeForm(value, items[1], requireEvidence);
24
+ }
25
+ if (head instanceof CalcitSymbol && head.value === "::") {
26
+ const name = typeFormName(items[1]);
27
+ // Nil is `null` here; `undefined` is Unit. Only the JS host boundary
28
+ // type also admits `undefined`.
29
+ if (name === "Optional") {
30
+ return value === null ? true : matchTypeForm(value, items[2], requireEvidence);
31
+ }
32
+ if (name === "JsNullish") {
33
+ return value == null ? true : matchTypeForm(value, items[2], requireEvidence);
34
+ }
35
+ if (name === "Fn" && requireEvidence)
36
+ return null;
37
+ return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
38
+ }
39
+ return requireEvidence ? null : true;
40
+ }
41
+ const name = typeFormName(form);
42
+ return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
43
+ };
44
+ let typeFormName = (form) => {
45
+ if (form instanceof CalcitSymbol)
46
+ return form.value;
47
+ if (form instanceof CalcitTag)
48
+ return form.value;
49
+ if (form instanceof CalcitSliceList || form instanceof CalcitList) {
50
+ const items = form.toArray();
51
+ if (items[0] instanceof CalcitSymbol && items[0].value === "quote")
52
+ return typeFormName(items[1]);
53
+ }
54
+ return null;
55
+ };
56
+ /// Numeric refinements check range and integrality like the native `CalcitNumericRefinement::accepts`.
57
+ let isIntegerIn = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
58
+ let isList = (value) => value instanceof CalcitList || value instanceof CalcitSliceList;
59
+ let isMap = (value) => value instanceof CalcitMap || value instanceof CalcitSliceMap;
60
+ let valueMatchesTypeName = (value, rawName, requireEvidence) => {
61
+ const name = rawName.startsWith("calcit.core/") ? rawName.slice("calcit.core/".length) : rawName;
62
+ switch (name) {
63
+ case "Dynamic":
64
+ case "dynamic":
65
+ case "any":
66
+ case "JsObject":
67
+ return true;
68
+ case "Number":
69
+ case "number":
70
+ case "Float64":
71
+ case "float64":
72
+ return typeof value === "number";
73
+ case "Float32":
74
+ case "float32":
75
+ return typeof value === "number" && Math.fround(value) === value;
76
+ case "Int8":
77
+ case "int8":
78
+ return isIntegerIn(value, -128, 127);
79
+ case "UInt8":
80
+ case "uint8":
81
+ return isIntegerIn(value, 0, 255);
82
+ case "Int16":
83
+ case "int16":
84
+ return isIntegerIn(value, -32768, 32767);
85
+ case "UInt16":
86
+ case "uint16":
87
+ return isIntegerIn(value, 0, 65535);
88
+ case "Int32":
89
+ case "int32":
90
+ return isIntegerIn(value, -2147483648, 2147483647);
91
+ case "UInt32":
92
+ case "uint32":
93
+ return isIntegerIn(value, 0, 4294967295);
94
+ case "Int64":
95
+ case "int64":
96
+ return isIntegerIn(value, -Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
97
+ case "UInt64":
98
+ case "uint64":
99
+ return isIntegerIn(value, 0, Number.MAX_SAFE_INTEGER);
100
+ case "String":
101
+ case "string":
102
+ return typeof value === "string";
103
+ case "Bool":
104
+ case "bool":
105
+ return typeof value === "boolean";
106
+ case "Tag":
107
+ case "tag":
108
+ return value instanceof CalcitTag;
109
+ case "Symbol":
110
+ case "symbol":
111
+ return value instanceof CalcitSymbol;
112
+ case "Nil":
113
+ case "nil":
114
+ return value === null;
115
+ case "Unit":
116
+ case "unit":
117
+ return value === undefined;
118
+ case "Never":
119
+ return false;
120
+ case "Buffer":
121
+ case "buffer":
122
+ return value instanceof Uint8Array;
123
+ case "CirruQuote":
124
+ case "cirru-quote":
125
+ return value instanceof CalcitCirruQuote;
126
+ case "List":
127
+ case "list":
128
+ return requireEvidence ? null : isList(value);
129
+ case "Map":
130
+ case "map":
131
+ return requireEvidence ? null : isMap(value);
132
+ case "Set":
133
+ case "set":
134
+ return requireEvidence ? null : value instanceof CalcitSet;
135
+ case "Ref":
136
+ case "ref":
137
+ return requireEvidence ? null : value instanceof CalcitRef;
138
+ case "Fn":
139
+ case "fn":
140
+ return typeof value === "function";
141
+ case "Struct":
142
+ case "struct":
143
+ case "record":
144
+ return requireEvidence ? null : isStructValue(value);
145
+ case "Enum":
146
+ case "enum":
147
+ case "tuple":
148
+ return requireEvidence ? null : value instanceof CalcitEnumValue;
149
+ }
150
+ // Deep/nominal contracts must use the compiler's resolved validator. A
151
+ // short-name match or an erased generic cannot establish runtime evidence.
152
+ if (requireEvidence)
153
+ return null;
154
+ // Core Struct and Enum definitions (bare names resolve to them through the
155
+ // implicit core import) are known here, so a reference to one admits only
156
+ // values of that definition, as the native matcher does.
157
+ if (CORE_NOMINAL_DEFS.has(name)) {
158
+ if (isStructValue(value))
159
+ return value.structRef.definitionRef === `calcit.core/${name}`;
160
+ return value instanceof CalcitEnumValue && value.enumPrototype?.prototype.structRef.definitionRef === `calcit.core/${name}`;
161
+ }
162
+ // Other names may be type variables, aliases or traits that cannot be
163
+ // resolved here. A qualified reference is checked against the value's own
164
+ // Struct or Enum name; other values are left to the static checker like
165
+ // unresolved forms.
166
+ const shortName = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
167
+ if (isStructValue(value))
168
+ return value.name.value === shortName || !name.includes("/");
169
+ if (value instanceof CalcitEnumValue && value.enumPrototype != null) {
170
+ return value.enumPrototype.name() === shortName || !name.includes("/");
171
+ }
172
+ return true;
173
+ };
174
+ /// Struct and Enum definitions of `calcit.core` usable as field types.
175
+ const CORE_NOMINAL_DEFS = new Set([
176
+ "Option",
177
+ "Result",
178
+ "Data",
179
+ "ListDestruct",
180
+ "MapDestruct",
181
+ "SetDestruct",
182
+ "StringDestruct",
183
+ "MapEntry",
184
+ "RuntimeMapMeta",
185
+ "RuntimeMapResponse",
186
+ ]);
187
+ // Duck-typed to avoid a module cycle with js-struct-value.
188
+ let isStructValue = (value) => value != null && typeof value === "object" && "structRef" in value && "fields" in value;
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.29.0-alpha.2",
3
+ "version": "0.29.0-alpha.23",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",
@@ -21,7 +21,7 @@
21
21
  "test-rs": "cargo test -q",
22
22
  "test-fail": "cargo test -q --bin calcit type_fail_",
23
23
  "test-snippets": "cargo test -q snippets::tests",
24
- "check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-numeric-predicate.mjs && node scripts/check-predicate-method-names.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
24
+ "check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
25
25
  "check-js-ffi-source": "cargo build --bin calcit && node scripts/check-js-ffi-source.mjs",
26
26
  "check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
27
27
  "check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
@@ -31,8 +31,9 @@
31
31
  "bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
32
32
  "bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
33
33
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
34
- "check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
34
+ "check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
35
35
  "check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs && node scripts/check-spread-boundary.mjs",
36
+ "check-core-tests": "node --test scripts/core-test-protocol.test.mjs && cargo build --bin calcit && node scripts/run-core-tests.mjs && node scripts/run-core-tests.mjs --snapshot calcit/test-traits.cirru --target test-traits.main/test-qualified-contains-boundary --backend native,js",
36
37
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
37
38
  "try-all": "yarn check-all",
38
39
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.29.0-alpha.2",
3
+ "version": "0.29.0-alpha.23",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",
@@ -21,7 +21,7 @@
21
21
  "test-rs": "cargo test -q",
22
22
  "test-fail": "cargo test -q --bin calcit type_fail_",
23
23
  "test-snippets": "cargo test -q snippets::tests",
24
- "check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-numeric-predicate.mjs && node scripts/check-predicate-method-names.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
24
+ "check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
25
25
  "check-js-ffi-source": "cargo build --bin calcit && node scripts/check-js-ffi-source.mjs",
26
26
  "check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
27
27
  "check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
@@ -31,8 +31,9 @@
31
31
  "bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
32
32
  "bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
33
33
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
34
- "check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
34
+ "check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
35
35
  "check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs && node scripts/check-spread-boundary.mjs",
36
+ "check-core-tests": "node --test scripts/core-test-protocol.test.mjs && cargo build --bin calcit && node scripts/run-core-tests.mjs && node scripts/run-core-tests.mjs --snapshot calcit/test-traits.cirru --target test-traits.main/test-qualified-contains-boundary --backend native,js",
36
37
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
37
38
  "try-all": "yarn check-all",
38
39
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",