@calcit/procs 0.12.14 → 0.12.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
@@ -0,0 +1,33 @@
1
+ # 202504121453 - Automatic map-to-record rewrite in preprocessing
2
+
3
+ ## Summary
4
+
5
+ When a function parameter is typed as a struct type in its schema, but the caller passes a hashmap literal `{}`, the preprocessor now automatically rewrites the hashmap to a record construction `%{}` so that runtime gets a proper record with full type checking.
6
+
7
+ ## Key implementation details
8
+
9
+ ### type_annotation.rs
10
+ - Added `CalcitTypeAnnotation::resolve_to_struct()` — resolves `Struct`, `Record`, `TypeRef("ns/def")`, and `Optional(inner)` variants to concrete `CalcitStruct` definitions
11
+ - Added `resolve_struct_from_program(ns, def)` helper — looks up struct definitions from the program registry via `lookup_runtime_ready_registered` with fallback to `lookup_def_code_registered`
12
+
13
+ ### preprocess.rs
14
+ - Added `try_rewrite_map_args_to_records()` — iterates processed args, checks each against `fn_info.arg_types[idx]`, calls `try_rewrite_single_map_to_record()` for potential rewrites
15
+ - Added `try_rewrite_single_map_to_record()` — validates arg is `List` with `Proc(NativeMap)` head, resolves expected type to struct, validates all keys are tags, builds `[Proc(NativeRecord), Struct(def), k1, v1, ...]`
16
+ - Integration point: `preprocess_list_call()` → `Calcit::Fn` match arm, between arg preprocessing and type checking
17
+
18
+ ### AST transformation
19
+ - Input (hashmap literal): `[Proc(NativeMap), Tag(:x), 10, Tag(:y), 20]`
20
+ - Output (record literal): `[Proc(NativeRecord), Struct(Point2D), Tag(:x), 10, Tag(:y), 20]`
21
+
22
+ ### Rewrite conditions
23
+ - Function has schema with `:args` referencing a struct type (TypeRef, Struct, or Record)
24
+ - Argument is a hashmap literal (`{}`) with tag keys
25
+ - Struct definition is resolvable at preprocess time
26
+ - If any condition fails, argument is left unchanged (safe fallback)
27
+
28
+ ## Test
29
+ - Added `Point2D`, `sum-point`, `check-point-type`, `test-map-to-record` to `calcit/test-record.cirru`
30
+ - `check-point-type` uses `record?` to verify the rewrite produces actual records, not just maps
31
+
32
+ ## Gotcha
33
+ - `cr tree insert-after` can create doubly-nested nodes — always verify with `tree show` after insertion
@@ -55,4 +55,4 @@
55
55
  ## 后续经验
56
56
 
57
57
  - docs 元数据一旦进入 CLI 行为,就应该配套规范页与验证页,否则后续加字段很容易出现“文档能写、检索却不稳定”的分叉。
58
- - 模块文档检索要尽量和 core docs 走同一套 resolver,这样使用者不需要记两套命令心智模型。
58
+ - 模块文档检索要尽量和 core docs 走同一套 resolver,这样使用者不需要记两套命令心智模型。
@@ -0,0 +1,23 @@
1
+ # 2026-0410-0011 — 修复 snapshot `configs.version` 对 `||` 空字符串的漏判
2
+
3
+ ## 背景
4
+
5
+ PR `snapshot-format` 新增了空 `configs.version` 的回归测试,但 GitHub Actions 上加载 `compact.cirru` 时,`(:version ||)` 会解析成字符串 `"|"`,没有命中原先仅检查空白字符串的校验。
6
+
7
+ ## 知识点
8
+
9
+ - Cirru 的空字符串字面量 `||` 在这条解析路径里会落成 `"|"`,不能只靠 `trim().is_empty()` 判断是否为空。
10
+ - `configs.version` 的校验要同时覆盖真正空串和这个 pipe marker,才能在加载阶段稳定报出 `configs.version cannot be empty`。
11
+
12
+ ## 修改
13
+
14
+ - `src/snapshot.rs`
15
+ - 将 `parse_snapshot_config_string_field` 中 `version` 的空值判断扩展为:`text.trim().is_empty() || text == "|"`。
16
+
17
+ ## 验证
18
+
19
+ ```bash
20
+ cargo fmt -- src/snapshot.rs
21
+ ```
22
+
23
+ 本地 `cargo test` 在当前机器仍受 macOS 链接器环境影响(`ld: library 'System' not found`),实际正确性改由 GitHub Actions 复核。
@@ -0,0 +1,26 @@
1
+ # JS Codegen Fix for Map-to-Record Rewrite
2
+
3
+ ## Problem
4
+ The map-to-record rewrite (from previous commit) inserted `Calcit::Struct` directly into the AST. The interpreter handles this, but JS codegen (`emit_js.rs` line 319) has no arm for `Calcit::Struct` → panics with `unreachable!`.
5
+
6
+ ## Key Insight
7
+ The `%{}` macro normally expands to `&%{}` with a **symbol/import reference** as the first arg (not a literal `Calcit::Struct`). JS codegen relies on this — it emits the symbol as a variable reference (e.g., `Element`), and the JS runtime `_$n__PCT__$M_` function accepts the struct object at runtime.
8
+
9
+ ## Solution
10
+ 1. Added `resolve_to_struct_with_ref()` to `CalcitTypeAnnotation` — returns `Option<(CalcitStruct, Option<(Arc<str>, Arc<str>)>)>` with ns/def path from TypeRef annotations.
11
+ 2. Modified `try_rewrite_single_map_to_record()` to emit `Calcit::Import(CalcitImport { ... })` instead of `Calcit::Struct`:
12
+ - `ImportInfo::SameFile` when struct is in same namespace (avoids self-import duplicate declaration)
13
+ - `ImportInfo::NsReferDef` when struct is in a different namespace (generates proper cross-ns import)
14
+ - Falls back to `Calcit::Struct` only when no ns/def path is available
15
+
16
+ ## Pitfall: Self-Import Duplicate Declaration
17
+ First attempt used `NsReferDef` for all imports — caused `SyntaxError: Identifier 'Point2D' has already been declared` because a self-import was generated when struct is in same namespace. Fix: detect `ns == file_ns` and use `SameFile`.
18
+
19
+ ## Verification in Respo
20
+ - Typed `element->string` as `'respo.schema/Element`, `make-string` as `'respo.schema/Component`
21
+ - Test passes `{}` map to `element->string` → auto-rewritten to `%{} Element ...` record construction
22
+ - Generated JS: `import { Element } from "./respo.schema.mjs"` + `$clt._$n__PCT__$M_(Element, ...)`
23
+
24
+ ## Files Changed
25
+ - `src/calcit/type_annotation.rs`: Added `resolve_to_struct_with_ref()`
26
+ - `src/runner/preprocess.rs`: Emit Import reference in `try_rewrite_single_map_to_record()`
@@ -0,0 +1,29 @@
1
+ # Map-to-Record Field Validation and Nil Fill
2
+
3
+ ## Problem
4
+ Map-to-record rewrite was passing through ALL map keys without validation, and emitting only
5
+ the keys present in the map. This caused:
6
+ 1. Maps with keys not in the struct (e.g., `:x`) being incorrectly rewritten to records
7
+ 2. "fields size does not match" runtime error when the map has fewer keys than struct fields
8
+
9
+ ## Fix (preprocess.rs)
10
+ 1. **Field validation**: Check each map key against `struct_def.fields`. If any key is not a
11
+ valid struct field, skip the rewrite entirely (stay as map).
12
+ 2. **Nil fill**: Emit ALL struct fields in definition order. For fields not present in the map,
13
+ emit `Calcit::Nil`. This ensures the record always has the correct field count.
14
+ 3. **HashMap tracking**: Build a `HashMap<EdnTag, &Calcit>` of provided fields for O(1) lookup
15
+ during emission.
16
+
17
+ ## Key Types
18
+ - `struct_def.fields` is `Vec<EdnTag>` — compare with `EdnTag` directly, not `Arc<str>`
19
+ - `provided_fields` is `HashMap<EdnTag, &Calcit>` — avoids the `Arc<str>: Borrow<EdnTag>` issue
20
+
21
+ ## Respo DomProps Design
22
+ - 29 fields with `(:: :optional <type>)` annotations
23
+ - Strings: class-name, id, type, href, src, placeholder, name, title, data-name, data-comp, target
24
+ - Dynamic: style (map), value, inner-text
25
+ - Bools: disabled, checked, spell-check, read-only, selected
26
+ - Number: tab-index
27
+ - Fns: on-click, on-input, on-focus, on-blur, on-keydown, on-keyup, on-change
28
+ - Maps: on, event
29
+ - `create-element`/`create-list-element` convert record props to map via `&record:to-map`
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.12.14",
3
+ "version": "0.12.15",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.0.9",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.12.14",
3
+ "version": "0.12.15",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.0.9",
@@ -22,7 +22,7 @@ import {
22
22
  } from "./calcit-data.mjs";
23
23
 
24
24
  import { CalcitRef } from "./js-ref.mjs";
25
- import { fieldsEqual, CalcitRecord } from "./js-record.mjs";
25
+ import { CalcitRecord } from "./js-record.mjs";
26
26
  import { CalcitImpl } from "./js-impl.mjs";
27
27
  import { CalcitStruct } from "./js-struct.mjs";
28
28
  import { CalcitEnum } from "./js-enum.mjs";