@calcit/procs 0.29.0-alpha.20 → 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.
- package/lib/calcit.procs.d.mts +4 -2
- package/lib/calcit.procs.mjs +63 -5
- package/lib/js-struct-def.d.mts +4 -2
- package/lib/js-struct-def.mjs +6 -5
- package/lib/js-struct-value.mjs +47 -92
- package/lib/js-type-form.d.mts +1 -1
- package/lib/js-type-form.mjs +36 -15
- package/lib/package.json +1 -1
- package/package.json +1 -1
package/lib/calcit.procs.d.mts
CHANGED
|
@@ -155,7 +155,7 @@ export declare let _$n_list_$o_assoc_after: (xs: CalcitSliceList, k: number, v:
|
|
|
155
155
|
export declare let _$n_list_$o_dissoc: (xs: CalcitValue | CalcitSliceList, k: CalcitValue) => CalcitList | CalcitSliceList;
|
|
156
156
|
export declare let _$n_map_$o_dissoc: (xs: CalcitValue, ...args: CalcitValue[]) => CalcitMap | CalcitSliceMap;
|
|
157
157
|
export declare let reset_$x_: (a: CalcitRef, v: CalcitValue) => CalcitValue;
|
|
158
|
-
export declare let
|
|
158
|
+
export declare let add_watch_$x_: (a: CalcitRef, k: CalcitTag, f: CalcitFn) => void;
|
|
159
159
|
export declare let remove_watch: (a: CalcitRef, k: CalcitTag) => void;
|
|
160
160
|
export declare let range: (n: number, m?: number, step?: number) => CalcitSliceList;
|
|
161
161
|
export declare function _$n_list_$o_empty_$q_(xs: CalcitValue): boolean;
|
|
@@ -243,7 +243,7 @@ export declare let turn_symbol: (x: CalcitValue) => CalcitSymbol;
|
|
|
243
243
|
export declare let to_lispy_string: (...args: CalcitValue[]) => string;
|
|
244
244
|
/** helper function for println, js only */
|
|
245
245
|
export declare let printable: (...args: CalcitValue[]) => string;
|
|
246
|
-
export declare let
|
|
246
|
+
export declare let monotonic_time_ms: () => number;
|
|
247
247
|
export declare let quit_$x_: (code: number) => void;
|
|
248
248
|
export declare let turn_string: (x: CalcitValue) => string;
|
|
249
249
|
export declare let identical_$q_: (x: CalcitValue, y: CalcitValue) => boolean;
|
|
@@ -325,6 +325,8 @@ type DataShapeGraph = {
|
|
|
325
325
|
fingerprint: string;
|
|
326
326
|
nodes: DataShapeNode[];
|
|
327
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;
|
|
328
330
|
export declare let parse_cirru_edn_as: (code: string, graph: DataShapeGraph) => CalcitValue;
|
|
329
331
|
export declare let decode_map_as: (value: CalcitValue, graph: DataShapeGraph) => CalcitValue;
|
|
330
332
|
export declare let json_parse: (code: CalcitValue) => CalcitValue;
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -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
|
|
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
|
}
|
|
@@ -1661,8 +1661,8 @@ export let to_lispy_string = (...args) => {
|
|
|
1661
1661
|
export let printable = (...args) => {
|
|
1662
1662
|
return args.map((x) => toString(x, false)).join(" ");
|
|
1663
1663
|
};
|
|
1664
|
-
//
|
|
1665
|
-
export let
|
|
1664
|
+
// monotonic milliseconds since app start
|
|
1665
|
+
export let monotonic_time_ms = () => {
|
|
1666
1666
|
if (inNodeJs) {
|
|
1667
1667
|
// uptime returns in seconds
|
|
1668
1668
|
return process.uptime() * 1000;
|
|
@@ -1947,6 +1947,64 @@ export let parse_cirru_edn = (code, options) => {
|
|
|
1947
1947
|
throw new Error(`Expected EDN in a single node, got ${nodes.length}`);
|
|
1948
1948
|
}
|
|
1949
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
|
+
}
|
|
1950
2008
|
const typed_edn_kind = (value) => {
|
|
1951
2009
|
if (value === null)
|
|
1952
2010
|
return "nil";
|
package/lib/js-struct-def.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
17
|
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
16
|
-
export declare function bind_struct_definition(value: CalcitValue, definitionRef: string): CalcitValue;
|
|
18
|
+
export declare function bind_struct_definition(value: CalcitValue, definitionRef: string, fieldValidators?: FieldValidator[] | null): CalcitValue;
|
package/lib/js-struct-def.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { CalcitImpl } from "./js-impl.mjs";
|
|
|
3
3
|
import { CalcitEnumDef } from "./js-enum-def.mjs";
|
|
4
4
|
import { CalcitStructValue } from "./js-struct-value.mjs";
|
|
5
5
|
export class CalcitStructDef {
|
|
6
|
-
constructor(name, fields, fieldTypes, impls = [], definitionRef = null) {
|
|
6
|
+
constructor(name, fields, fieldTypes, impls = [], definitionRef = null, fieldValidators = null) {
|
|
7
7
|
const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
|
|
8
8
|
this.name = name;
|
|
9
9
|
this.fields = canonicalFields;
|
|
@@ -11,13 +11,14 @@ export class CalcitStructDef {
|
|
|
11
11
|
this.impls = impls ?? [];
|
|
12
12
|
this.cachedHash = null;
|
|
13
13
|
this.definitionRef = definitionRef;
|
|
14
|
+
this.fieldValidators = fieldValidators == null ? null : canonicalizeTagPairs(fields, fieldValidators, "CalcitStructDef validators")[1];
|
|
14
15
|
}
|
|
15
16
|
withImpls(impls) {
|
|
16
17
|
if (impls instanceof CalcitImpl) {
|
|
17
|
-
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);
|
|
18
19
|
}
|
|
19
20
|
else if (Array.isArray(impls)) {
|
|
20
|
-
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);
|
|
21
22
|
}
|
|
22
23
|
throw new Error("Expected an impl as implementation");
|
|
23
24
|
}
|
|
@@ -36,9 +37,9 @@ export class CalcitStructDef {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
39
|
-
export function bind_struct_definition(value, definitionRef) {
|
|
40
|
+
export function bind_struct_definition(value, definitionRef, fieldValidators = null) {
|
|
40
41
|
if (value instanceof CalcitStructDef && value.definitionRef == null) {
|
|
41
|
-
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);
|
|
42
43
|
}
|
|
43
44
|
if (value instanceof CalcitEnumDef && value.prototype.structRef.definitionRef == null) {
|
|
44
45
|
const prototype = value.prototype;
|
package/lib/js-struct-value.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
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
5
|
import { valueMatchesTypeForm } from "./js-type-form.mjs";
|
|
6
6
|
/// Field writes check the declared field type, like the native runtime.
|
|
7
|
-
let assertFieldValue = (structRef, idx, value, operation) => {
|
|
7
|
+
let assertFieldValue = (structRef, idx, value, operation, requireEvidence = false) => {
|
|
8
8
|
const fieldType = structRef?.fieldTypes?.[idx];
|
|
9
|
-
|
|
9
|
+
const validator = structRef?.fieldValidators?.[idx];
|
|
10
|
+
if (validator != null ? !validator(value, requireEvidence)
|
|
11
|
+
: fieldType != null && !valueMatchesTypeForm(value, fieldType, requireEvidence)) {
|
|
10
12
|
throw new Error(`${operation} field :${structRef.fields[idx].value} expects type ${toString(fieldType, true)}, but received ${toString(value, true)}`);
|
|
11
13
|
}
|
|
12
14
|
};
|
|
@@ -48,17 +50,14 @@ export class CalcitStructValue {
|
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
assoc(k, v) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
assertFieldValue(this.structRef, idx, v, "&struct:assoc");
|
|
56
|
-
values[idx] = v;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
values[idx] = this.values[idx];
|
|
60
|
-
}
|
|
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}'`);
|
|
61
57
|
}
|
|
58
|
+
assertFieldValue(this.structRef, idx, v, "&struct:assoc", true);
|
|
59
|
+
const values = this.values.slice();
|
|
60
|
+
values[idx] = v;
|
|
62
61
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
63
62
|
}
|
|
64
63
|
nthAt(index, field) {
|
|
@@ -127,7 +126,7 @@ export class CalcitStructValue {
|
|
|
127
126
|
else {
|
|
128
127
|
throw new Error("Expected an impl or array of impls");
|
|
129
128
|
}
|
|
130
|
-
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);
|
|
131
130
|
return new CalcitStructValue(this.name, this.fields, this.values, nextStruct);
|
|
132
131
|
}
|
|
133
132
|
}
|
|
@@ -192,45 +191,38 @@ export let fieldsEqual = (xs, ys) => {
|
|
|
192
191
|
}
|
|
193
192
|
return true;
|
|
194
193
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (proto instanceof
|
|
198
|
-
|
|
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");
|
|
199
198
|
}
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
if (xs.length % 2 !== 0) {
|
|
200
|
+
throw new Error("Expected even number of key/value");
|
|
202
201
|
}
|
|
203
|
-
|
|
204
|
-
throw new Error("
|
|
202
|
+
if (xs.length !== proto.fields.length * 2) {
|
|
203
|
+
throw new Error("fields size does not match");
|
|
205
204
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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");
|
|
209
211
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
const idx = findInFields(proto.fields, castTag(key));
|
|
213
|
+
if (idx < 0) {
|
|
214
|
+
throw new Error("invalid field name for this struct");
|
|
212
215
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
let idx = -1;
|
|
216
|
-
let k = recordProto.fields[i];
|
|
217
|
-
for (let j = 0; j < recordProto.fields.length; j++) {
|
|
218
|
-
if (k === castTag(xs[j * 2])) {
|
|
219
|
-
idx = j;
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (idx < 0) {
|
|
224
|
-
throw new Error("invalid field name for this struct");
|
|
225
|
-
}
|
|
226
|
-
if (values[i] != null) {
|
|
227
|
-
throw new Error("struct field already has value, probably duplicated key");
|
|
228
|
-
}
|
|
229
|
-
values[i] = xs[idx * 2 + 1];
|
|
216
|
+
if (seen.has(idx)) {
|
|
217
|
+
throw new Error("struct field already has value, probably duplicated key");
|
|
230
218
|
}
|
|
231
|
-
|
|
219
|
+
seen.add(idx);
|
|
220
|
+
assertFieldValue(proto, idx, xs[i + 1], "&%{}");
|
|
221
|
+
values[idx] = xs[i + 1];
|
|
232
222
|
}
|
|
223
|
+
return new CalcitStructValue(proto.name, proto.fields, values, proto);
|
|
233
224
|
};
|
|
225
|
+
export let _$n__PCT__$M_ = (proto, ...xs) => constructStruct(proto, xs);
|
|
234
226
|
/// update record with new values
|
|
235
227
|
export let _$n_struct_$o_with = (proto, ...xs) => {
|
|
236
228
|
if (proto instanceof CalcitStructValue) {
|
|
@@ -245,7 +237,7 @@ export let _$n_struct_$o_with = (proto, ...xs) => {
|
|
|
245
237
|
if (idx < 0) {
|
|
246
238
|
throw new Error(`Cannot find field ${k} among ${proto.fields}`);
|
|
247
239
|
}
|
|
248
|
-
assertFieldValue(proto.structRef, idx, v, "&struct:with");
|
|
240
|
+
assertFieldValue(proto.structRef, idx, v, "&struct:with", true);
|
|
249
241
|
values[idx] = v;
|
|
250
242
|
}
|
|
251
243
|
return new CalcitStructValue(proto.name, proto.fields, values, proto.structRef);
|
|
@@ -273,57 +265,20 @@ export let _$n_struct_$o_definition = (x) => {
|
|
|
273
265
|
}
|
|
274
266
|
};
|
|
275
267
|
export let _$n_struct_$o_from_map = (proto, data) => {
|
|
276
|
-
|
|
277
|
-
if (proto instanceof CalcitStructDef) {
|
|
278
|
-
recordProto = new CalcitStructValue(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
268
|
+
if (!(proto instanceof CalcitStructDef)) {
|
|
281
269
|
throw new Error("Expected prototype to be struct");
|
|
282
270
|
}
|
|
283
|
-
if (data instanceof
|
|
284
|
-
|
|
285
|
-
return new CalcitStructValue(recordProto.name, recordProto.fields, data.values, recordProto.structRef);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
let values = [];
|
|
289
|
-
for (let i = 0; i < recordProto.fields.length; i++) {
|
|
290
|
-
let field = recordProto.fields[i];
|
|
291
|
-
let idx = findInFields(data.fields, field);
|
|
292
|
-
if (idx < 0) {
|
|
293
|
-
throw new Error(`Cannot find field ${field} among ${data.fields}`);
|
|
294
|
-
}
|
|
295
|
-
values.push(data.values[idx]);
|
|
296
|
-
}
|
|
297
|
-
return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
|
|
298
|
-
}
|
|
271
|
+
if (!(data instanceof CalcitMap) && !(data instanceof CalcitSliceMap)) {
|
|
272
|
+
throw new Error("&struct:from-map requires a Map");
|
|
299
273
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
let k = pairs[i][0];
|
|
305
|
-
let v = pairs[i][1];
|
|
306
|
-
pairs_buffer.push([castTag(k), v]);
|
|
307
|
-
}
|
|
308
|
-
// mutable sort
|
|
309
|
-
pairs_buffer.sort((pair1, pair2) => pair1[0].cmp(pair2[0]));
|
|
310
|
-
let values = [];
|
|
311
|
-
outerLoop: for (let i = 0; i < recordProto.fields.length; i++) {
|
|
312
|
-
let field = recordProto.fields[i];
|
|
313
|
-
for (let idx = 0; idx < pairs_buffer.length; idx++) {
|
|
314
|
-
let pair = pairs_buffer[idx];
|
|
315
|
-
if (pair[0] === field) {
|
|
316
|
-
values.push(pair[1]);
|
|
317
|
-
continue outerLoop; // dirty code for performance
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
throw new Error(`Cannot find field ${field} among ${pairs_buffer}`);
|
|
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");
|
|
321
278
|
}
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
throw new Error("Expected a struct value or data for making a struct");
|
|
279
|
+
fields.push(key, value);
|
|
326
280
|
}
|
|
281
|
+
return constructStruct(proto, fields);
|
|
327
282
|
};
|
|
328
283
|
export let _$n_struct_$o_to_map = (x) => {
|
|
329
284
|
if (x instanceof CalcitStructValue) {
|
package/lib/js-type-form.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { CalcitValue } from "./js-primes.mjs";
|
|
2
|
-
export declare let valueMatchesTypeForm: (value: CalcitValue, form: CalcitValue) => boolean;
|
|
2
|
+
export declare let valueMatchesTypeForm: (value: CalcitValue, form: CalcitValue, requireEvidence?: boolean) => boolean;
|
package/lib/js-type-form.mjs
CHANGED
|
@@ -4,33 +4,42 @@ import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
|
4
4
|
import { CalcitSet } from "./js-set.mjs";
|
|
5
5
|
import { CalcitRef } from "./js-ref.mjs";
|
|
6
6
|
import { CalcitEnumValue } from "./js-enum-value.mjs";
|
|
7
|
+
import { CalcitCirruQuote } from "./js-cirru.mjs";
|
|
7
8
|
/// Shallow runtime check of a value against a struct field type form, matching
|
|
8
9
|
/// the native `value_matches_type_annotation`: container kinds are checked,
|
|
9
10
|
/// members are not. Names that cannot be resolved here (aliases, traits, type
|
|
10
|
-
/// variables) stay permissive
|
|
11
|
-
|
|
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) => {
|
|
12
19
|
if (form instanceof CalcitSliceList || form instanceof CalcitList) {
|
|
13
20
|
const items = form.toArray();
|
|
14
21
|
const head = items[0];
|
|
15
22
|
if (head instanceof CalcitSymbol && head.value === "quote") {
|
|
16
|
-
return
|
|
23
|
+
return matchTypeForm(value, items[1], requireEvidence);
|
|
17
24
|
}
|
|
18
25
|
if (head instanceof CalcitSymbol && head.value === "::") {
|
|
19
26
|
const name = typeFormName(items[1]);
|
|
20
27
|
// Nil is `null` here; `undefined` is Unit. Only the JS host boundary
|
|
21
28
|
// type also admits `undefined`.
|
|
22
29
|
if (name === "Optional") {
|
|
23
|
-
return value === null
|
|
30
|
+
return value === null ? true : matchTypeForm(value, items[2], requireEvidence);
|
|
24
31
|
}
|
|
25
32
|
if (name === "JsNullish") {
|
|
26
|
-
return value == null
|
|
33
|
+
return value == null ? true : matchTypeForm(value, items[2], requireEvidence);
|
|
27
34
|
}
|
|
28
|
-
|
|
35
|
+
if (name === "Fn" && requireEvidence)
|
|
36
|
+
return null;
|
|
37
|
+
return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
|
|
29
38
|
}
|
|
30
|
-
return true;
|
|
39
|
+
return requireEvidence ? null : true;
|
|
31
40
|
}
|
|
32
41
|
const name = typeFormName(form);
|
|
33
|
-
return name == null
|
|
42
|
+
return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
|
|
34
43
|
};
|
|
35
44
|
let typeFormName = (form) => {
|
|
36
45
|
if (form instanceof CalcitSymbol)
|
|
@@ -48,7 +57,7 @@ let typeFormName = (form) => {
|
|
|
48
57
|
let isIntegerIn = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
49
58
|
let isList = (value) => value instanceof CalcitList || value instanceof CalcitSliceList;
|
|
50
59
|
let isMap = (value) => value instanceof CalcitMap || value instanceof CalcitSliceMap;
|
|
51
|
-
let valueMatchesTypeName = (value, rawName) => {
|
|
60
|
+
let valueMatchesTypeName = (value, rawName, requireEvidence) => {
|
|
52
61
|
const name = rawName.startsWith("calcit.core/") ? rawName.slice("calcit.core/".length) : rawName;
|
|
53
62
|
switch (name) {
|
|
54
63
|
case "Dynamic":
|
|
@@ -106,30 +115,42 @@ let valueMatchesTypeName = (value, rawName) => {
|
|
|
106
115
|
case "Unit":
|
|
107
116
|
case "unit":
|
|
108
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;
|
|
109
126
|
case "List":
|
|
110
127
|
case "list":
|
|
111
|
-
return isList(value);
|
|
128
|
+
return requireEvidence ? null : isList(value);
|
|
112
129
|
case "Map":
|
|
113
130
|
case "map":
|
|
114
|
-
return isMap(value);
|
|
131
|
+
return requireEvidence ? null : isMap(value);
|
|
115
132
|
case "Set":
|
|
116
133
|
case "set":
|
|
117
|
-
return value instanceof CalcitSet;
|
|
134
|
+
return requireEvidence ? null : value instanceof CalcitSet;
|
|
118
135
|
case "Ref":
|
|
119
136
|
case "ref":
|
|
120
|
-
return value instanceof CalcitRef;
|
|
137
|
+
return requireEvidence ? null : value instanceof CalcitRef;
|
|
121
138
|
case "Fn":
|
|
122
139
|
case "fn":
|
|
123
140
|
return typeof value === "function";
|
|
124
141
|
case "Struct":
|
|
125
142
|
case "struct":
|
|
126
143
|
case "record":
|
|
127
|
-
return isStructValue(value);
|
|
144
|
+
return requireEvidence ? null : isStructValue(value);
|
|
128
145
|
case "Enum":
|
|
129
146
|
case "enum":
|
|
130
147
|
case "tuple":
|
|
131
|
-
return value instanceof CalcitEnumValue;
|
|
148
|
+
return requireEvidence ? null : value instanceof CalcitEnumValue;
|
|
132
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;
|
|
133
154
|
// Core Struct and Enum definitions (bare names resolve to them through the
|
|
134
155
|
// implicit core import) are known here, so a reference to one admits only
|
|
135
156
|
// values of that definition, as the native matcher does.
|
package/lib/package.json
CHANGED