@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.
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.
@@ -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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.13",
3
+ "version": "0.13.15",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.13",
3
+ "version": "0.13.15",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
@@ -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;