@calcit/procs 0.13.13 → 0.13.15
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/.yarn/install-state.gz +0 -0
- package/README.md +6 -0
- package/history/202608131228-scope-development-dependencies.md +36 -0
- package/history/202608131258-audit-legacy-upgrade-playbook.md +23 -0
- package/history/202608131607-runtime-map-decoder.md +13 -0
- package/history/202608131625-core-decode-map-as-metadata.md +12 -0
- package/history/202608131650-release-0.13.15.md +10 -0
- package/lib/calcit.procs.d.mts +6 -1
- package/lib/calcit.procs.mjs +133 -0
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/ts-src/calcit.procs.mts +132 -1
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -130,6 +130,8 @@ Related examples and workflows:
|
|
|
130
130
|
:dependencies $ {}
|
|
131
131
|
|calcit-lang/memof |0.0.11
|
|
132
132
|
|calcit-lang/lilac |main
|
|
133
|
+
:dev-dependencies $ {}
|
|
134
|
+
|calcit-lang/calcit-test |0.1.0
|
|
133
135
|
```
|
|
134
136
|
|
|
135
137
|
Run `caps` to resolve the recursive dependency graph and install it. Immutable revisions are stored under
|
|
@@ -141,6 +143,10 @@ Published SemVer tags are preferred. Branch refs remain supported for developmen
|
|
|
141
143
|
the resolved commit. When a graph requests several SemVer tags for one repository, the highest requested
|
|
142
144
|
version is selected and reported.
|
|
143
145
|
|
|
146
|
+
Root projects install both `:dependencies` and `:dev-dependencies`. Recursive resolution only follows
|
|
147
|
+
`:dependencies`, so test and maintenance modules declared by a dependency do not leak into consumers.
|
|
148
|
+
Use `caps add --dev <org/repo>@<ref>` and `caps remove --dev <org/repo>` to manage the development group.
|
|
149
|
+
|
|
144
150
|
`:calcit-version` helps with version checks and provides hints in [CI](https://github.com/calcit-lang/setup-cr).
|
|
145
151
|
|
|
146
152
|
To load modules, use `:modules` configuration and the runtime snapshot file `calcit.cirru` (legacy: `compact.cirru`):
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Scope development dependencies in recursive resolution
|
|
2
|
+
|
|
3
|
+
## Knowledge points
|
|
4
|
+
|
|
5
|
+
- `deps.cirru` now distinguishes consumer-facing `:dependencies` from root-only
|
|
6
|
+
`:dev-dependencies`.
|
|
7
|
+
- A root project resolves and installs both groups. A materialized dependency module exposes only
|
|
8
|
+
its `:dependencies` to the recursive graph, preventing its tests, examples, documentation tools,
|
|
9
|
+
and maintenance modules from leaking into consumers.
|
|
10
|
+
- The same repository may appear in both root groups only when both declarations use the same ref;
|
|
11
|
+
conflicting refs fail before installation or before `caps add` writes the file.
|
|
12
|
+
- `caps add --dev` and `caps remove --dev` manage development dependencies. `caps outdated` and
|
|
13
|
+
`caps upgrade --all` inspect both root groups and update the declaration in its original group.
|
|
14
|
+
- Upgrade guidance should ask projects to audit dependency intent, move project-only tooling into
|
|
15
|
+
`:dev-dependencies`, and confirm the resulting boundary with `caps tree`.
|
|
16
|
+
- A legacy-project upgrade must be staged: record the old behavior, update `cr`/`caps`, migrate the
|
|
17
|
+
dependency graph and Snapshot, then run every entry through strict preprocessing before tightening
|
|
18
|
+
dynamic dispatch and static debt baselines.
|
|
19
|
+
- `--check-only` is a blocking preprocessing gate, including warnings, but it covers the selected
|
|
20
|
+
entry's reachable path rather than every public definition. `check-types`, `weak-types`, and
|
|
21
|
+
`deprecated` are report commands whose JSON summaries require explicit CI comparison.
|
|
22
|
+
- Type-debt baselines should compare separate categories instead of one total. For coverage, track
|
|
23
|
+
`none` and `none + partial` so progress from none to partial is accepted; track dynamic, nil,
|
|
24
|
+
Optional compatibility, and deprecated calls independently.
|
|
25
|
+
|
|
26
|
+
## Validation
|
|
27
|
+
|
|
28
|
+
- `cargo fmt`
|
|
29
|
+
- `cargo clippy -- -D warnings`
|
|
30
|
+
- `yarn compile`
|
|
31
|
+
- `cargo test`
|
|
32
|
+
- `yarn check-agent-interface`
|
|
33
|
+
- `yarn check-all`
|
|
34
|
+
- `cr docs format-md docs/run/upgrade.md --check`
|
|
35
|
+
- `cr docs check-md docs/run/upgrade.md --entry calcit/test.cirru --failures-only`
|
|
36
|
+
- `cr docs graph check` (with a temporary writable HOME/cache)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Audit the legacy project upgrade playbook
|
|
2
|
+
|
|
3
|
+
## Findings
|
|
4
|
+
|
|
5
|
+
- An old project cannot safely treat dependency upgrade success as completion. The workflow must
|
|
6
|
+
preserve an old-toolchain behavior baseline and separate tool, dependency, Snapshot, type, and
|
|
7
|
+
behavior changes into reviewable stages.
|
|
8
|
+
- New CLI validation has distinct failure semantics. `--check-only`, examples, Markdown checks, and
|
|
9
|
+
tests are blocking gates; static type/deprecation analysis emits reports that require explicit
|
|
10
|
+
JSON summary comparison in CI.
|
|
11
|
+
- Strict preprocessing must run for every named entry, then run again with `--warn-dyn-method`.
|
|
12
|
+
Entry reachability does not replace tests/examples for public library definitions.
|
|
13
|
+
- Gradual typing needs category-aware baselines for none/not-full coverage, dynamic locations,
|
|
14
|
+
nil/Optional debt, and deprecated calls. A single aggregate can hide category regressions.
|
|
15
|
+
- Snapshot migration must verify which legacy file is authoritative and must not claim that Calcit
|
|
16
|
+
no longer depends on a Snapshot.
|
|
17
|
+
|
|
18
|
+
## Documentation validation
|
|
19
|
+
|
|
20
|
+
- `cr docs format-md docs/run/upgrade.md --check`
|
|
21
|
+
- `cr docs check-md docs/run/upgrade.md --entry calcit/test.cirru --failures-only`
|
|
22
|
+
- `cr docs graph check` with a temporary writable HOME/cache: 22 nodes, 51 edges
|
|
23
|
+
- `git diff --check`
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Runtime map decoder for typed boundary data
|
|
2
|
+
|
|
3
|
+
## Change summary
|
|
4
|
+
|
|
5
|
+
- Added `decode-map-as value TypeExpr`, a compile-time-derived decoder for an already-evaluated Calcit Map.
|
|
6
|
+
- The decoder constructs nominal Struct values recursively, rejects unknown keys and missing required fields, and reports nested failure paths.
|
|
7
|
+
- Struct fields declared as `Option<T>` accept raw `T`, preserve an already-wrapped `%some`/`%none`, and become `%none` when the source field is absent.
|
|
8
|
+
- `Dynamic` is allowed only as an explicit runtime-boundary leaf for this decoder; the existing text-based `parse-cirru-edn-as` remains a closed-data decoder and continues to reject it.
|
|
9
|
+
- Native and JavaScript backends share the graph ABI and tests. WASM continues to reject both typed EDN decoder syntaxes explicitly.
|
|
10
|
+
|
|
11
|
+
## Knowledge point
|
|
12
|
+
|
|
13
|
+
Runtime Maps are not Cirru EDN: they do not carry nominal Struct identity and commonly represent optional data by omitted keys. A typed boundary decoder must therefore perform map-to-Struct construction, enforce required fields, and lift omitted or raw optional values into nominal `Option` variants. Reusing the closed EDN decoder without these rules silently permits invalid Structs or forces callers back to ad-hoc dynamic map readers.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Document `decode-map-as` in calcit-core
|
|
2
|
+
|
|
3
|
+
## Change summary
|
|
4
|
+
|
|
5
|
+
- Added the `decode-map-as` runtime syntax to the core snapshot metadata with builtin/internal/meta/syntax tags and a typed boundary schema.
|
|
6
|
+
- Added reusable `RuntimeMapMeta` and `RuntimeMapResponse` Struct fixtures for core examples and tests.
|
|
7
|
+
- Added a passing example plus definition-attached unit tests covering recursive Struct decoding through an Option field, omitted `Option` fields becoming `%none`, pre-wrapped `%some`, and explicit Dynamic payload preservation.
|
|
8
|
+
- Documented the native/JavaScript support boundary and kept WASM behavior explicit.
|
|
9
|
+
|
|
10
|
+
## Knowledge point
|
|
11
|
+
|
|
12
|
+
Compiler syntax and core snapshot metadata are separate surfaces. Adding a Rust/JS builtin is incomplete until `calcit-core.cirru` exposes its documentation, examples, schema, and tests; otherwise `cr docs`, `cr test`, and downstream agents cannot discover or verify the feature.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Release Calcit 0.13.15
|
|
2
|
+
|
|
3
|
+
## Change summary
|
|
4
|
+
|
|
5
|
+
- Released the merged `decode-map-as` runtime decoder and its core snapshot metadata, examples, and tests.
|
|
6
|
+
- Kept Cargo and npm package versions synchronized at `0.13.15` for the direct-main release workflow.
|
|
7
|
+
|
|
8
|
+
## Knowledge point
|
|
9
|
+
|
|
10
|
+
Version-only release changes are allowed directly on `main`; the functional implementation was merged and validated before this release commit. The release tag is created from the synchronized `main` commit so Cargo and npm publish the same version.
|
package/lib/calcit.procs.d.mts
CHANGED
|
@@ -247,7 +247,7 @@ export declare let parse_cirru: (code: string) => CalcitCirruQuote;
|
|
|
247
247
|
export declare let parse_cirru_list: (code: string) => CalcitList;
|
|
248
248
|
export declare let parse_cirru_edn: (code: string, options: CalcitValue) => CalcitValue;
|
|
249
249
|
type DataShapeNode = {
|
|
250
|
-
kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote";
|
|
250
|
+
kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic";
|
|
251
251
|
} | {
|
|
252
252
|
kind: "optional" | "list" | "set" | "ref";
|
|
253
253
|
inner: number;
|
|
@@ -255,6 +255,10 @@ type DataShapeNode = {
|
|
|
255
255
|
kind: "map";
|
|
256
256
|
key: number;
|
|
257
257
|
value: number;
|
|
258
|
+
} | {
|
|
259
|
+
kind: "map-option";
|
|
260
|
+
nominal: CalcitEnumDef;
|
|
261
|
+
inner: number;
|
|
258
262
|
} | {
|
|
259
263
|
kind: "struct";
|
|
260
264
|
nominal: CalcitStructDef;
|
|
@@ -274,6 +278,7 @@ type DataShapeGraph = {
|
|
|
274
278
|
nodes: DataShapeNode[];
|
|
275
279
|
};
|
|
276
280
|
export declare let parse_cirru_edn_as: (code: string, graph: DataShapeGraph) => CalcitValue;
|
|
281
|
+
export declare let decode_map_as: (value: CalcitValue, graph: DataShapeGraph) => CalcitValue;
|
|
277
282
|
export declare let json_parse: (code: CalcitValue) => CalcitValue;
|
|
278
283
|
export declare let json_stringify: (value: CalcitValue) => string;
|
|
279
284
|
export declare let json_pretty: (value: CalcitValue) => string;
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -1746,6 +1746,139 @@ export let parse_cirru_edn_as = (code, graph) => {
|
|
|
1746
1746
|
}
|
|
1747
1747
|
return decode_typed_edn_node(graph, graph.root, input, "$", 0);
|
|
1748
1748
|
};
|
|
1749
|
+
const map_decode_error = (path, message) => {
|
|
1750
|
+
throw new Error(`decode-map-as failed at ${path}: ${message}`);
|
|
1751
|
+
};
|
|
1752
|
+
const decode_runtime_map_node = (graph, nodeId, input, path, depth) => {
|
|
1753
|
+
if (depth > 1024)
|
|
1754
|
+
map_decode_error(path, "decode nesting exceeds 1024");
|
|
1755
|
+
const node = graph.nodes[nodeId];
|
|
1756
|
+
if (node == null)
|
|
1757
|
+
map_decode_error(path, `invalid data shape node #${nodeId}`);
|
|
1758
|
+
switch (node.kind) {
|
|
1759
|
+
case "dynamic":
|
|
1760
|
+
return input;
|
|
1761
|
+
case "map-option": {
|
|
1762
|
+
if (input instanceof CalcitEnumValue &&
|
|
1763
|
+
(input.enumPrototype === node.nominal || input.enumPrototype?.name() === node.nominal.name())) {
|
|
1764
|
+
const tag = input.tag;
|
|
1765
|
+
if (!(tag instanceof CalcitTag))
|
|
1766
|
+
map_decode_error(path, "Option variant is not a tag");
|
|
1767
|
+
const tag_name = tag.value;
|
|
1768
|
+
if (tag_name === "none" && input.extra.length === 0)
|
|
1769
|
+
return input;
|
|
1770
|
+
if (tag_name === "some" && input.extra.length === 1) {
|
|
1771
|
+
return new CalcitEnumValue(tag, [decode_runtime_map_node(graph, node.inner, input.extra[0], path, depth + 1)], node.nominal);
|
|
1772
|
+
}
|
|
1773
|
+
return map_decode_error(path, "invalid Option value");
|
|
1774
|
+
}
|
|
1775
|
+
return new CalcitEnumValue(newTag("some"), [decode_runtime_map_node(graph, node.inner, input, path, depth + 1)], node.nominal);
|
|
1776
|
+
}
|
|
1777
|
+
case "struct": {
|
|
1778
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1779
|
+
return map_decode_error(path, `expected map for struct :${node.nominal.name.value}, got ${typed_edn_kind(input)}`);
|
|
1780
|
+
}
|
|
1781
|
+
const entries = new Map();
|
|
1782
|
+
input.pairs().forEach(([key, value]) => {
|
|
1783
|
+
const name = key instanceof CalcitTag ? key.value : typeof key === "string" ? key : null;
|
|
1784
|
+
if (name == null)
|
|
1785
|
+
map_decode_error(path, "struct map keys must be tags or strings");
|
|
1786
|
+
if (!node.fields.some(([field]) => field === name)) {
|
|
1787
|
+
map_decode_error(path, `struct :${node.nominal.name.value} has unknown field :${name}`);
|
|
1788
|
+
}
|
|
1789
|
+
if (entries.has(name))
|
|
1790
|
+
map_decode_error(path, `struct :${node.nominal.name.value} has duplicate field :${name}`);
|
|
1791
|
+
entries.set(name, value);
|
|
1792
|
+
});
|
|
1793
|
+
const decoded = new Map();
|
|
1794
|
+
node.fields.forEach(([field, child]) => {
|
|
1795
|
+
if (entries.has(field)) {
|
|
1796
|
+
decoded.set(field, decode_runtime_map_node(graph, child, entries.get(field), `${path}.${field}`, depth + 1));
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
const fieldNode = graph.nodes[child];
|
|
1800
|
+
if (fieldNode?.kind === "map-option") {
|
|
1801
|
+
decoded.set(field, new CalcitEnumValue(newTag("none"), [], fieldNode.nominal));
|
|
1802
|
+
}
|
|
1803
|
+
else {
|
|
1804
|
+
map_decode_error(path, `struct :${node.nominal.name.value} is missing required field :${field}`);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
const values = node.nominal.fields.map((field) => decoded.get(field.value));
|
|
1809
|
+
return new CalcitStructValue(node.nominal.name, node.nominal.fields, values, node.nominal);
|
|
1810
|
+
}
|
|
1811
|
+
case "list": {
|
|
1812
|
+
if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
|
|
1813
|
+
return map_decode_error(path, `expected list, got ${typed_edn_kind(input)}`);
|
|
1814
|
+
}
|
|
1815
|
+
return new CalcitSliceList(Array.from(input.items()).map((value, idx) => decode_runtime_map_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1)));
|
|
1816
|
+
}
|
|
1817
|
+
case "set": {
|
|
1818
|
+
if (!(input instanceof CalcitSet || input instanceof TypedEdnSetView)) {
|
|
1819
|
+
return map_decode_error(path, `expected set, got ${typed_edn_kind(input)}`);
|
|
1820
|
+
}
|
|
1821
|
+
const values = [];
|
|
1822
|
+
const source = input instanceof TypedEdnSetView ? input.items : input.values();
|
|
1823
|
+
source.forEach((value) => {
|
|
1824
|
+
const decoded = decode_runtime_map_node(graph, node.inner, value, `${path}.item`, depth + 1);
|
|
1825
|
+
if (values.some((item) => _$n__$e_(item, decoded)))
|
|
1826
|
+
map_decode_error(path, "duplicate decoded set value");
|
|
1827
|
+
values.push(decoded);
|
|
1828
|
+
});
|
|
1829
|
+
return new CalcitSet(values);
|
|
1830
|
+
}
|
|
1831
|
+
case "map": {
|
|
1832
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1833
|
+
return map_decode_error(path, `expected map, got ${typed_edn_kind(input)}`);
|
|
1834
|
+
}
|
|
1835
|
+
const entries = [];
|
|
1836
|
+
input.pairs().forEach(([key, value]) => {
|
|
1837
|
+
const decodedKey = decode_runtime_map_node(graph, node.key, key, `${path}.key`, depth + 1);
|
|
1838
|
+
if (entries.some(([existing]) => _$n__$e_(existing, decodedKey)))
|
|
1839
|
+
map_decode_error(path, "duplicate decoded map key");
|
|
1840
|
+
entries.push([decodedKey, decode_runtime_map_node(graph, node.value, value, `${path}.value`, depth + 1)]);
|
|
1841
|
+
});
|
|
1842
|
+
return new CalcitSliceMap(entries.flat());
|
|
1843
|
+
}
|
|
1844
|
+
case "optional":
|
|
1845
|
+
return input == null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
|
|
1846
|
+
case "ref":
|
|
1847
|
+
if (!(input instanceof CalcitRef))
|
|
1848
|
+
return map_decode_error(path, `expected atom, got ${typed_edn_kind(input)}`);
|
|
1849
|
+
return atom(decode_runtime_map_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
|
|
1850
|
+
case "enum": {
|
|
1851
|
+
if (!(input instanceof CalcitEnumValue) || input.enumPrototype == null) {
|
|
1852
|
+
return map_decode_error(path, `expected enum :${node.nominal.name()}, got ${typed_edn_kind(input)}`);
|
|
1853
|
+
}
|
|
1854
|
+
const actualEnumName = enum_prototype_name(input.enumPrototype);
|
|
1855
|
+
if (actualEnumName !== node.nominal.name()) {
|
|
1856
|
+
return map_decode_error(path, `expected enum :${node.nominal.name()}, got enum :${actualEnumName}`);
|
|
1857
|
+
}
|
|
1858
|
+
if (!(input.tag instanceof CalcitTag))
|
|
1859
|
+
return map_decode_error(path, `enum :${node.nominal.name()} variant must be a tag`);
|
|
1860
|
+
const inputTag = input.tag;
|
|
1861
|
+
const variant = node.variants.find((candidate) => candidate.tag === inputTag.value);
|
|
1862
|
+
if (variant == null)
|
|
1863
|
+
return map_decode_error(path, `enum :${node.nominal.name()} has no variant :${inputTag.value}`);
|
|
1864
|
+
if (variant.payload.length !== input.extra.length) {
|
|
1865
|
+
return map_decode_error(path, `enum :${node.nominal.name()} variant :${variant.tag} expects ${variant.payload.length} payload(s), got ${input.extra.length}`);
|
|
1866
|
+
}
|
|
1867
|
+
const values = variant.payload.map((payloadNode, idx) => decode_runtime_map_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1));
|
|
1868
|
+
return new CalcitEnumValue(newTag(variant.tag), values, node.nominal);
|
|
1869
|
+
}
|
|
1870
|
+
default:
|
|
1871
|
+
return decode_typed_edn_node(graph, nodeId, input, path, depth);
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
export let decode_map_as = (value, graph) => {
|
|
1875
|
+
if (graph.version !== 1)
|
|
1876
|
+
throw new Error(`decode-map-as expected data shape ABI version 1, got ${graph.version}`);
|
|
1877
|
+
if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
|
|
1878
|
+
throw new Error("decode-map-as expected a non-empty data shape fingerprint");
|
|
1879
|
+
}
|
|
1880
|
+
return decode_runtime_map_node(graph, graph.root, value, "$", 0);
|
|
1881
|
+
};
|
|
1749
1882
|
const json_to_calcit = (value) => {
|
|
1750
1883
|
if (value == null)
|
|
1751
1884
|
return null;
|
package/lib/package.json
CHANGED
package/package.json
CHANGED
package/ts-src/calcit.procs.mts
CHANGED
|
@@ -1676,9 +1676,10 @@ export let parse_cirru_edn = (code: string, options: CalcitValue) => {
|
|
|
1676
1676
|
};
|
|
1677
1677
|
|
|
1678
1678
|
type DataShapeNode =
|
|
1679
|
-
| { kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" }
|
|
1679
|
+
| { kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" | "dynamic" }
|
|
1680
1680
|
| { kind: "optional" | "list" | "set" | "ref"; inner: number }
|
|
1681
1681
|
| { kind: "map"; key: number; value: number }
|
|
1682
|
+
| { kind: "map-option"; nominal: CalcitEnumDef; inner: number }
|
|
1682
1683
|
| { kind: "struct"; nominal: CalcitStructDef; fields: Array<[string, number]> }
|
|
1683
1684
|
| { kind: "enum"; nominal: CalcitEnumDef; variants: Array<{ tag: string; payload: number[] }> };
|
|
1684
1685
|
|
|
@@ -1883,6 +1884,136 @@ export let parse_cirru_edn_as = (code: string, graph: DataShapeGraph): CalcitVal
|
|
|
1883
1884
|
return decode_typed_edn_node(graph, graph.root, input, "$", 0) as CalcitValue;
|
|
1884
1885
|
};
|
|
1885
1886
|
|
|
1887
|
+
const map_decode_error = (path: string, message: string): never => {
|
|
1888
|
+
throw new Error(`decode-map-as failed at ${path}: ${message}`);
|
|
1889
|
+
};
|
|
1890
|
+
|
|
1891
|
+
const decode_runtime_map_node = (graph: DataShapeGraph, nodeId: number, input: any, path: string, depth: number): any => {
|
|
1892
|
+
if (depth > 1024) map_decode_error(path, "decode nesting exceeds 1024");
|
|
1893
|
+
const node = graph.nodes[nodeId];
|
|
1894
|
+
if (node == null) map_decode_error(path, `invalid data shape node #${nodeId}`);
|
|
1895
|
+
|
|
1896
|
+
switch (node.kind) {
|
|
1897
|
+
case "dynamic":
|
|
1898
|
+
return input;
|
|
1899
|
+
case "map-option": {
|
|
1900
|
+
if (
|
|
1901
|
+
input instanceof CalcitEnumValue &&
|
|
1902
|
+
(input.enumPrototype === node.nominal || input.enumPrototype?.name() === node.nominal.name())
|
|
1903
|
+
) {
|
|
1904
|
+
const tag: CalcitValue = input.tag;
|
|
1905
|
+
if (!(tag instanceof CalcitTag)) map_decode_error(path, "Option variant is not a tag");
|
|
1906
|
+
const tag_name = (tag as CalcitTag).value;
|
|
1907
|
+
if (tag_name === "none" && input.extra.length === 0) return input;
|
|
1908
|
+
if (tag_name === "some" && input.extra.length === 1) {
|
|
1909
|
+
return new CalcitEnumValue(tag as CalcitTag, [decode_runtime_map_node(graph, node.inner, input.extra[0], path, depth + 1)], node.nominal);
|
|
1910
|
+
}
|
|
1911
|
+
return map_decode_error(path, "invalid Option value");
|
|
1912
|
+
}
|
|
1913
|
+
return new CalcitEnumValue(newTag("some"), [decode_runtime_map_node(graph, node.inner, input, path, depth + 1)], node.nominal);
|
|
1914
|
+
}
|
|
1915
|
+
case "struct": {
|
|
1916
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1917
|
+
return map_decode_error(path, `expected map for struct :${node.nominal.name.value}, got ${typed_edn_kind(input)}`);
|
|
1918
|
+
}
|
|
1919
|
+
const entries = new Map<string, any>();
|
|
1920
|
+
input.pairs().forEach(([key, value]) => {
|
|
1921
|
+
const name = key instanceof CalcitTag ? key.value : typeof key === "string" ? key : null;
|
|
1922
|
+
if (name == null) map_decode_error(path, "struct map keys must be tags or strings");
|
|
1923
|
+
if (!node.fields.some(([field]) => field === name)) {
|
|
1924
|
+
map_decode_error(path, `struct :${node.nominal.name.value} has unknown field :${name}`);
|
|
1925
|
+
}
|
|
1926
|
+
if (entries.has(name)) map_decode_error(path, `struct :${node.nominal.name.value} has duplicate field :${name}`);
|
|
1927
|
+
entries.set(name, value);
|
|
1928
|
+
});
|
|
1929
|
+
const decoded = new Map<string, any>();
|
|
1930
|
+
node.fields.forEach(([field, child]) => {
|
|
1931
|
+
if (entries.has(field)) {
|
|
1932
|
+
decoded.set(field, decode_runtime_map_node(graph, child, entries.get(field), `${path}.${field}`, depth + 1));
|
|
1933
|
+
} else {
|
|
1934
|
+
const fieldNode = graph.nodes[child];
|
|
1935
|
+
if (fieldNode?.kind === "map-option") {
|
|
1936
|
+
decoded.set(field, new CalcitEnumValue(newTag("none"), [], fieldNode.nominal));
|
|
1937
|
+
} else {
|
|
1938
|
+
map_decode_error(path, `struct :${node.nominal.name.value} is missing required field :${field}`);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
const values = node.nominal.fields.map((field) => decoded.get(field.value));
|
|
1943
|
+
return new CalcitStructValue(node.nominal.name, node.nominal.fields, values, node.nominal);
|
|
1944
|
+
}
|
|
1945
|
+
case "list": {
|
|
1946
|
+
if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
|
|
1947
|
+
return map_decode_error(path, `expected list, got ${typed_edn_kind(input)}`);
|
|
1948
|
+
}
|
|
1949
|
+
return new CalcitSliceList(Array.from(input.items()).map((value, idx) => decode_runtime_map_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1)));
|
|
1950
|
+
}
|
|
1951
|
+
case "set": {
|
|
1952
|
+
if (!(input instanceof CalcitSet || input instanceof TypedEdnSetView)) {
|
|
1953
|
+
return map_decode_error(path, `expected set, got ${typed_edn_kind(input)}`);
|
|
1954
|
+
}
|
|
1955
|
+
const values: any[] = [];
|
|
1956
|
+
const source = input instanceof TypedEdnSetView ? input.items : input.values();
|
|
1957
|
+
source.forEach((value) => {
|
|
1958
|
+
const decoded = decode_runtime_map_node(graph, node.inner, value, `${path}.item`, depth + 1);
|
|
1959
|
+
if (values.some((item) => _$n__$e_(item, decoded))) map_decode_error(path, "duplicate decoded set value");
|
|
1960
|
+
values.push(decoded);
|
|
1961
|
+
});
|
|
1962
|
+
return new CalcitSet(values);
|
|
1963
|
+
}
|
|
1964
|
+
case "map": {
|
|
1965
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1966
|
+
return map_decode_error(path, `expected map, got ${typed_edn_kind(input)}`);
|
|
1967
|
+
}
|
|
1968
|
+
const entries: any[] = [];
|
|
1969
|
+
input.pairs().forEach(([key, value]) => {
|
|
1970
|
+
const decodedKey = decode_runtime_map_node(graph, node.key, key, `${path}.key`, depth + 1);
|
|
1971
|
+
if (entries.some(([existing]) => _$n__$e_(existing, decodedKey))) map_decode_error(path, "duplicate decoded map key");
|
|
1972
|
+
entries.push([decodedKey, decode_runtime_map_node(graph, node.value, value, `${path}.value`, depth + 1)]);
|
|
1973
|
+
});
|
|
1974
|
+
return new CalcitSliceMap(entries.flat());
|
|
1975
|
+
}
|
|
1976
|
+
case "optional":
|
|
1977
|
+
return input == null ? null : decode_runtime_map_node(graph, node.inner, input, path, depth + 1);
|
|
1978
|
+
case "ref":
|
|
1979
|
+
if (!(input instanceof CalcitRef)) return map_decode_error(path, `expected atom, got ${typed_edn_kind(input)}`);
|
|
1980
|
+
return atom(decode_runtime_map_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
|
|
1981
|
+
case "enum": {
|
|
1982
|
+
if (!(input instanceof CalcitEnumValue) || input.enumPrototype == null) {
|
|
1983
|
+
return map_decode_error(path, `expected enum :${node.nominal.name()}, got ${typed_edn_kind(input)}`);
|
|
1984
|
+
}
|
|
1985
|
+
const actualEnumName = enum_prototype_name(input.enumPrototype);
|
|
1986
|
+
if (actualEnumName !== node.nominal.name()) {
|
|
1987
|
+
return map_decode_error(path, `expected enum :${node.nominal.name()}, got enum :${actualEnumName}`);
|
|
1988
|
+
}
|
|
1989
|
+
if (!(input.tag instanceof CalcitTag)) return map_decode_error(path, `enum :${node.nominal.name()} variant must be a tag`);
|
|
1990
|
+
const inputTag = input.tag as CalcitTag;
|
|
1991
|
+
const variant = node.variants.find((candidate) => candidate.tag === inputTag.value);
|
|
1992
|
+
if (variant == null) return map_decode_error(path, `enum :${node.nominal.name()} has no variant :${inputTag.value}`);
|
|
1993
|
+
if (variant.payload.length !== input.extra.length) {
|
|
1994
|
+
return map_decode_error(
|
|
1995
|
+
path,
|
|
1996
|
+
`enum :${node.nominal.name()} variant :${variant.tag} expects ${variant.payload.length} payload(s), got ${input.extra.length}`
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
const values = variant.payload.map((payloadNode, idx) =>
|
|
2000
|
+
decode_runtime_map_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1)
|
|
2001
|
+
);
|
|
2002
|
+
return new CalcitEnumValue(newTag(variant.tag), values, node.nominal);
|
|
2003
|
+
}
|
|
2004
|
+
default:
|
|
2005
|
+
return decode_typed_edn_node(graph, nodeId, input, path, depth);
|
|
2006
|
+
}
|
|
2007
|
+
};
|
|
2008
|
+
|
|
2009
|
+
export let decode_map_as = (value: CalcitValue, graph: DataShapeGraph): CalcitValue => {
|
|
2010
|
+
if (graph.version !== 1) throw new Error(`decode-map-as expected data shape ABI version 1, got ${graph.version}`);
|
|
2011
|
+
if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
|
|
2012
|
+
throw new Error("decode-map-as expected a non-empty data shape fingerprint");
|
|
2013
|
+
}
|
|
2014
|
+
return decode_runtime_map_node(graph, graph.root, value, "$", 0) as CalcitValue;
|
|
2015
|
+
};
|
|
2016
|
+
|
|
1886
2017
|
const json_to_calcit = (value: any): CalcitValue => {
|
|
1887
2018
|
if (value == null) return null;
|
|
1888
2019
|
if (typeof value === "string") return value;
|