@calcit/procs 0.13.7 → 0.13.8
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/editing-history/202608092000-inspect-type-path-format.md +4 -0
- package/history/202608092318-enum-edn-option-diagnostics.md +21 -0
- package/history/202608100022-required-struct-field-access.md +46 -0
- package/history/202608100102-docs-field-access-ci.md +22 -0
- package/history/202608100145-cirru-edn-doc-fences.md +17 -0
- package/history/202608100339-struct-path-boundaries.md +11 -0
- package/history/202608100348-caps-branch-checkout.md +8 -0
- package/history/202608100349-caps-remote-branch-reset.md +8 -0
- package/history/202608100959-struct-path-review-followups.md +11 -0
- package/history/202608101042-release-0.13.8.md +6 -0
- package/lib/js-cirru.mjs +7 -6
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/ts-src/js-cirru.mts +8 -6
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Cirru EDN enum identity and Option migration diagnostics
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
- GitHub #326 暴露 JS `format-cirru-edn` / `parse-cirru-edn` 往返后 enum variant 从 interned `CalcitTag` 变成 `CalcitSymbol`,导致生成的 `match` 按身份比较时无法命中。
|
|
6
|
+
- GitHub #325 暴露 Option migration 诊断仍使用预处理前的 source symbol,普通 `=`, `some?`, `assoc` 等调用不会触发告警,并且 `starts-with?` / `ends-with?` 的 proc metadata 比公开 schema 更宽。
|
|
7
|
+
|
|
8
|
+
## 修改与边界
|
|
9
|
+
|
|
10
|
+
- JS Cirru EDN parser 在匿名和具名 enum 分支统一把解析出的 symbol variant 通过 `newTag` intern;quoted nominal name 也按 tag key 恢复 options map 中的 `CalcitEnumDef`。
|
|
11
|
+
- Option migration diagnostics 改用预处理后的 `head_form` / `call_head`,避免漏掉普通 source syntax,同时继续区分应用自行定义的同名函数。
|
|
12
|
+
- 结构操作诊断覆盖 `assoc`, `dissoc`, `merge`, `update` 及其 nested/non-nil variants。
|
|
13
|
+
- `starts-with?` / `ends-with?` proc 参数 metadata 收紧为 `String × String`,与文档和 core schema 一致;旧 Tag runtime 兼容测试用显式 `unsafe-coerce` 标明边界。
|
|
14
|
+
- 合法的 nominal enum equality 继续允许,包括类型推断尚未完成时,Option/Result constructor 和已声明返回 nominal enum 的调用之间的比较。
|
|
15
|
+
|
|
16
|
+
## 验证
|
|
17
|
+
|
|
18
|
+
- JS runtime identity check 覆盖匿名 enum、具名 enum prototype 恢复和 identity-based match 条件。
|
|
19
|
+
- Rust end-to-end snippet test 覆盖 issue 中的 `get-env`, `some?`, `update-in/assoc`, `nth/starts-with?` 普通源码写法。
|
|
20
|
+
- `cargo fmt --all`, `cargo clippy -- -D warnings`, `yarn compile`, `cargo test`, `yarn check-all` 全部通过。
|
|
21
|
+
- 安装当前全局 `cr` 后在 Respo 执行 `cr --check-only`,成功在真实项目中报告 3 个 Option migration warning(另有 1 个既有 JS nullable warning),未进入相关运行时失败路径。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Required Struct field access
|
|
2
|
+
|
|
3
|
+
## Background
|
|
4
|
+
|
|
5
|
+
Field syntax previously changed its contract according to inferred receiver
|
|
6
|
+
type: `(:field value)` returned the declared payload for a known Struct, but
|
|
7
|
+
silently lowered to Option-producing `get` when the receiver looked like a Map
|
|
8
|
+
or lost type information. This made local edits unstable: adding or losing a
|
|
9
|
+
type annotation changed downstream control flow without changing source syntax.
|
|
10
|
+
|
|
11
|
+
Issue #325 clarified that dynamic container absence and required model fields
|
|
12
|
+
are different boundaries. AI-assisted edits need those boundaries to remain
|
|
13
|
+
visible so diagnostics guide code toward a declared model instead of allowing
|
|
14
|
+
`Dynamic`/`Option` patches to spread.
|
|
15
|
+
|
|
16
|
+
## Contract
|
|
17
|
+
|
|
18
|
+
- `(:field value)` is required access. The receiver must be a statically known
|
|
19
|
+
named Struct and the field must be declared. Its result is the declared field
|
|
20
|
+
type.
|
|
21
|
+
- `get` and `get-in` are explicit partial lookup APIs for Map and indexed/path
|
|
22
|
+
access. They return nominal `Option<T>`.
|
|
23
|
+
- `get` on a Struct is rejected with a diagnostic pointing to required field
|
|
24
|
+
syntax; the runtime also rejects dynamically hidden Struct receivers.
|
|
25
|
+
- Loose/anonymous Struct field access is rejected until an expected named
|
|
26
|
+
Struct type rewrites or narrows it.
|
|
27
|
+
- Low-level `&struct:get` remains available for internal dynamic boundaries
|
|
28
|
+
such as reusable trait implementation bodies.
|
|
29
|
+
|
|
30
|
+
## Type inference details
|
|
31
|
+
|
|
32
|
+
- `update` preserves the receiver's collection/Struct type.
|
|
33
|
+
- `&struct:from-map` preserves the Struct definition supplied as its first
|
|
34
|
+
argument.
|
|
35
|
+
- Struct pattern-match expansion is exempt from source field validation because
|
|
36
|
+
all nominal branches are preprocessed before runtime guards select a variant.
|
|
37
|
+
|
|
38
|
+
## Diagnostics
|
|
39
|
+
|
|
40
|
+
- `W_REQUIRED_STRUCT_FIELD_TYPE`: required field syntax lacks a named Struct
|
|
41
|
+
contract.
|
|
42
|
+
- `W_UNKNOWN_STRUCT_FIELD`: a named Struct does not declare the requested field.
|
|
43
|
+
- `W_STRUCT_FIELD_OPTIONAL_LOOKUP`: `get` is used on a Struct.
|
|
44
|
+
|
|
45
|
+
Tests cover typed access, Map fallback rejection, unknown fields, `get` misuse,
|
|
46
|
+
nominal type preservation, loose Struct access, and the existing Struct suite.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Documentation validation for required Struct fields
|
|
2
|
+
|
|
3
|
+
## Background
|
|
4
|
+
|
|
5
|
+
The PR's local `yarn check-all` suite passed, but GitHub's Test workflow also
|
|
6
|
+
runs `scripts/check-docs-md.sh`. Several executable documentation snippets
|
|
7
|
+
still relied on the former context-sensitive field syntax.
|
|
8
|
+
|
|
9
|
+
## Documentation migration
|
|
10
|
+
|
|
11
|
+
- Map reads that branch on absence retain their `Option` value for `tag-match`
|
|
12
|
+
instead of unwrapping it first.
|
|
13
|
+
- Generic enum payloads, local trait implementations, generic Struct bodies,
|
|
14
|
+
and anonymous Struct examples use explicit `&struct:get` when their receiver
|
|
15
|
+
has no statically resolvable named Struct declaration.
|
|
16
|
+
- Snapshot/schema fragments now use standard `cirru.edn` fences with valid
|
|
17
|
+
EDN roots and quoted symbols, so `check-md` validates them as data instead
|
|
18
|
+
of evaluating tags such as `:entries` or `:where` as required field access.
|
|
19
|
+
|
|
20
|
+
## Verification
|
|
21
|
+
|
|
22
|
+
- `bash scripts/check-docs-md.sh`: 54 files, 286 blocks, all passed.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Standard Cirru EDN documentation fences
|
|
2
|
+
|
|
3
|
+
## Background
|
|
4
|
+
|
|
5
|
+
Executable documentation fences use the standard `cirru.edn` marker. The
|
|
6
|
+
non-standard `cirru-edn` spelling bypassed EDN validation and hid malformed
|
|
7
|
+
configuration and schema fragments.
|
|
8
|
+
|
|
9
|
+
## Change
|
|
10
|
+
|
|
11
|
+
- Replaced all newly introduced `cirru-edn` fences with `cirru.edn`.
|
|
12
|
+
- Wrapped standalone configuration/schema fields in top-level EDN maps.
|
|
13
|
+
- Quoted symbolic trait references inside EDN schema values.
|
|
14
|
+
|
|
15
|
+
## Verification
|
|
16
|
+
|
|
17
|
+
- `bash scripts/check-docs-md.sh`: 54 files and 290 blocks passed.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Struct path API boundaries
|
|
2
|
+
|
|
3
|
+
- Public collection path APIs (`get-in`, `assoc-in`, `update-in`, `dissoc-in`)
|
|
4
|
+
now stop at nominal Struct boundaries. Required fields remain visible as direct
|
|
5
|
+
`(:field value)` accesses, preserving their declared types for the checker.
|
|
6
|
+
- The preprocessor diagnoses literal and dynamic paths that would enter a Struct;
|
|
7
|
+
`get-in` no longer infers a field type through such a path.
|
|
8
|
+
- Runtime core implementations reject the same traversal after a Dynamic boundary,
|
|
9
|
+
so unsafe coercion cannot silently bypass the typed field-access contract.
|
|
10
|
+
- Updated the type-inference fixture to use nested direct field access instead of
|
|
11
|
+
`get-in` across a Struct.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## caps remote branch checkout
|
|
2
|
+
|
|
3
|
+
- `caps` now fetches remote branch refs as well as tags when resolving an
|
|
4
|
+
existing module clone.
|
|
5
|
+
- If a requested branch exists only as `origin/<branch>`, checkout now creates
|
|
6
|
+
a local tracking branch instead of failing after the fetch succeeds.
|
|
7
|
+
- This keeps branch-based module integration reproducible through `caps
|
|
8
|
+
download`, without manually editing the module cache.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## caps remote branch checkout hardening
|
|
2
|
+
|
|
3
|
+
- Existing module clones can use narrow or non-standard remote refspecs, for
|
|
4
|
+
which `git checkout --track origin/<branch>` may reject an otherwise fetched
|
|
5
|
+
remote ref.
|
|
6
|
+
- `caps` now creates/resets the requested local branch with `git checkout -B
|
|
7
|
+
<branch> origin/<branch>`, keeping branch-based module downloads reliable and
|
|
8
|
+
compatible with later `--pull-branch` updates.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Struct path-operation review follow-ups
|
|
2
|
+
|
|
3
|
+
- `contains-in?` now follows the same Struct boundary as the other collection
|
|
4
|
+
path APIs: a path may not enter a declared Struct field. Direct typed field
|
|
5
|
+
access keeps required-field diagnostics and return types precise.
|
|
6
|
+
- The preprocessor reports `W_STRUCT_PATH_OPERATION` for `contains-in?`, with
|
|
7
|
+
a regression test covering all five path APIs.
|
|
8
|
+
- Runtime guidance for `dissoc-in` now explains that declared Struct fields
|
|
9
|
+
cannot be removed; model optionality explicitly or convert to a map first.
|
|
10
|
+
- Module ref fetching now uses `--prune`, so stale remote-tracking branches do
|
|
11
|
+
not remain selectable after an upstream branch deletion.
|
package/lib/js-cirru.mjs
CHANGED
|
@@ -225,13 +225,17 @@ let recordFieldOrder = (a, b) => {
|
|
|
225
225
|
};
|
|
226
226
|
/** makes sure we got string */
|
|
227
227
|
let extractFieldTag = (x) => {
|
|
228
|
-
if (x[0] === ":") {
|
|
228
|
+
if (x[0] === ":" || x[0] === "'") {
|
|
229
229
|
return newTag(x.slice(1));
|
|
230
230
|
}
|
|
231
231
|
else {
|
|
232
232
|
return newTag(x);
|
|
233
233
|
}
|
|
234
234
|
};
|
|
235
|
+
let extractEnumTag = (x, options, preserveSourceEntries) => {
|
|
236
|
+
const parsedTag = extract_cirru_edn_inner(x, options, preserveSourceEntries);
|
|
237
|
+
return parsedTag instanceof CalcitSymbol ? newTag(parsedTag.value) : parsedTag;
|
|
238
|
+
};
|
|
235
239
|
let resolveEnumPrototype = (enumName, options) => {
|
|
236
240
|
if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
|
|
237
241
|
let value = options.get(extractFieldTag(enumName));
|
|
@@ -412,7 +416,7 @@ const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
|
|
|
412
416
|
if (x.length < 2) {
|
|
413
417
|
throw new Error(`anonymous enum expects at least 1 value, got: ${x}`);
|
|
414
418
|
}
|
|
415
|
-
return new CalcitEnumValue(
|
|
419
|
+
return new CalcitEnumValue(extractEnumTag(x[1], options, preserveSourceEntries), x
|
|
416
420
|
.slice(2)
|
|
417
421
|
.filter(notComment)
|
|
418
422
|
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)));
|
|
@@ -426,10 +430,7 @@ const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
|
|
|
426
430
|
throw new Error(`Expected string for enum name, got: ${enumName}`);
|
|
427
431
|
}
|
|
428
432
|
let enumPrototype = resolveEnumPrototype(enumName, options);
|
|
429
|
-
|
|
430
|
-
const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
|
|
431
|
-
const enumTag = proto != null ? proto.name.toString() : enumName;
|
|
432
|
-
return new CalcitEnumValue(extract_cirru_edn_inner(x[2], options, preserveSourceEntries), x
|
|
433
|
+
return new CalcitEnumValue(extractEnumTag(x[2], options, preserveSourceEntries), x
|
|
433
434
|
.slice(3)
|
|
434
435
|
.filter(notComment)
|
|
435
436
|
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)), enumPrototype);
|
package/lib/package.json
CHANGED
package/package.json
CHANGED
package/ts-src/js-cirru.mts
CHANGED
|
@@ -220,13 +220,18 @@ let recordFieldOrder = (a: [string, CirruEdnFormat], b: [string, CirruEdnFormat]
|
|
|
220
220
|
|
|
221
221
|
/** makes sure we got string */
|
|
222
222
|
let extractFieldTag = (x: string) => {
|
|
223
|
-
if (x[0] === ":") {
|
|
223
|
+
if (x[0] === ":" || x[0] === "'") {
|
|
224
224
|
return newTag(x.slice(1));
|
|
225
225
|
} else {
|
|
226
226
|
return newTag(x);
|
|
227
227
|
}
|
|
228
228
|
};
|
|
229
229
|
|
|
230
|
+
let extractEnumTag = (x: CirruEdnFormat, options: CalcitValue, preserveSourceEntries: boolean): CalcitValue => {
|
|
231
|
+
const parsedTag = extract_cirru_edn_inner(x, options, preserveSourceEntries);
|
|
232
|
+
return parsedTag instanceof CalcitSymbol ? newTag(parsedTag.value) : parsedTag;
|
|
233
|
+
};
|
|
234
|
+
|
|
230
235
|
let resolveEnumPrototype = (enumName: string, options: CalcitValue) => {
|
|
231
236
|
if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
|
|
232
237
|
let value = options.get(extractFieldTag(enumName));
|
|
@@ -405,7 +410,7 @@ const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preser
|
|
|
405
410
|
throw new Error(`anonymous enum expects at least 1 value, got: ${x}`);
|
|
406
411
|
}
|
|
407
412
|
return new CalcitEnumValue(
|
|
408
|
-
|
|
413
|
+
extractEnumTag(x[1], options, preserveSourceEntries),
|
|
409
414
|
x
|
|
410
415
|
.slice(2)
|
|
411
416
|
.filter(notComment)
|
|
@@ -421,11 +426,8 @@ const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preser
|
|
|
421
426
|
throw new Error(`Expected string for enum name, got: ${enumName}`);
|
|
422
427
|
}
|
|
423
428
|
let enumPrototype = resolveEnumPrototype(enumName, options);
|
|
424
|
-
// unwrap prototype to record then extract name
|
|
425
|
-
const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
|
|
426
|
-
const enumTag = proto != null ? proto.name.toString() : enumName;
|
|
427
429
|
return new CalcitEnumValue(
|
|
428
|
-
|
|
430
|
+
extractEnumTag(x[2], options, preserveSourceEntries),
|
|
429
431
|
x
|
|
430
432
|
.slice(3)
|
|
431
433
|
.filter(notComment)
|