From 724d6e23775e0a0fa1a8c58786985645866be045 Mon Sep 17 00:00:00 2001 From: tiye Date: Mon, 19 Oct 2026 22:59:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E7=A4=BE=E5=8C=BA=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E8=87=B3=200.30.1=20=E5=80=99=E9=80=89=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=AD=98=E6=A1=A3=E4=BF=9D=E5=AD=98=E6=97=B6?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/upload.yaml | 6 +- .gitignore | 1 + README.md | 20 +++++- calcit.cirru | 114 +++++++++++++++++----------------- deps.cirru | 8 +-- package.json | 2 +- yarn.lock | 28 ++++++--- 7 files changed, 104 insertions(+), 75 deletions(-) diff --git a/.github/workflows/upload.yaml b/.github/workflows/upload.yaml index 8854d90..a313639 100644 --- a/.github/workflows/upload.yaml +++ b/.github/workflows/upload.yaml @@ -52,8 +52,10 @@ jobs: test ! -e package.cirru calcit calcit.cirru edit format git diff --exit-code -- calcit.cirru - calcit calcit.cirru --check-only - calcit calcit.cirru analyze check-public --ns app.comp.container --ns app.config --ns app.main --ns app.schema --ns app.updater --summary-only + calcit calcit.cirru --check-only --all-defs + calcit calcit.cirru --check-only --ns app.comp.container --ns app.config --ns app.main --ns app.schema --ns app.updater --summary-only --format json + calcit calcit.cirru fix --workflow strict --verify --format edn + calcit calcit.cirru test --require-match --summary-only --format json - name: Select frontend CDN path id: asset-path diff --git a/.gitignore b/.gitignore index a8d1892..a569c13 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ js-out/ node_modules/ dist/ .yarn/*.gz +.calcit/ diff --git a/README.md b/README.md index da664b4..0cb7dc3 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,20 @@ https://github.com/calcit-lang/respo-calcit-workflow ### Development and deployment -Use Calcit 0.27.0, Node.js 24, and Yarn 4.18.0. The canonical source files are +本轮验收使用配对 Calcit/procs 0.30.1-alpha.5,以及正式 Reel 0.6.39、Respo 0.16.122、 +js-ffi 0.2.3,不等于正式 0.30.1 二进制验收。站点链接、存储键与部署路径不变。 +自动保存从误用的 60 毫秒修正为 60 秒;存档恢复成功后才安装定时器与卸载保存监听。 +损坏存档仍明确报告错误,但不会被后续自动保存覆盖;页面静态链接仍可用。 +Use Node.js 24 and Yarn 4.18.0. The canonical source files are `calcit.cirru` and `deps.cirru`; do not recreate `compact.cirru` or `package.cirru`. ```sh caps --strict --ci yarn install --immutable caps verify --toolchain -calcit calcit.cirru --check-only +calcit calcit.cirru --check-only --all-defs +calcit calcit.cirru fix --workflow strict --verify --format edn +calcit calcit.cirru test --require-match --summary-only --format json yarn build ``` @@ -36,6 +42,16 @@ in another terminal while editing Calcit, without a process-management dependenc The browser app uses typed Reel state, one Enum per dispatch, and validates legacy Map/new Store data when hydrating the original `map.clj.im` storage key. +空 Reel 使用公开 State 构造器与 Ref 的泛型上下文,不再对 `new-reel` 作强断言。 +实际 DOM dispatch 边界检查 Enum;采用 `render-with!`、当前 ref/watch API 和 Respo DOM 查询适配。 +旧 Struct 存档仅在读取边界转换为字段 Map,再验证构造成当前 Store; +保留一处实际 `&struct:to-map` 转换,不以类型断言或 Dynamic 擦除伪造身份。 +移除无引用的模板 `repeat!` / `snippets`,不引入新的定时器框架。 + +本次本地验收未运行 Caps;开发者仍须执行上述安装步骤以生成模块链接,CI 独立 strict 解析。 +两项定义附着业务测试覆盖旧 Map/Struct 存档往返、局部状态更新及 content 保留, +不新增检查脚本。浏览器用隔离合成存档验收恢复、保存重载、热更新回放及损坏存档卸载不覆盖; +临时观察页不提交。外部历史链接可达性及移动端未验收,本 PR 不授权合并或生产部署。 `VITE_BASE_URL` controls asset URLs (default `./`). CI builds against `https://cos-sh.tiye.me/clojure-china/map.clojure-china.org/`, with diff --git a/calcit.cirru b/calcit.cirru index d75b179..d9f0140 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -163,38 +163,36 @@ 'app.main $ %{} 'FileEntry :defs $ {} '*reel $ %{} 'CodeEntry (:doc |) - :code $ quote $ defatom *reel - assert-type (typed/new-reel schema/store) (:: 'reel.typed/State 'Enum 'app.schema/Store) + :code $ quote $ defref *reel + typed/State :base schema/store :store schema/store :records ([]) :pointer (Option :none) :stopped? false :display? false :merged? false :examples $ [] :schema $ :: 'Ref $ :: 'reel.typed/State 'Enum 'app.schema/Store 'dispatch! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn dispatch! (op) - when config/dev? $ println |Dispatch: op - reset! *reel $ match (typed/decode-control op) - (:some control) (typed/apply-control updater @*reel control) - (:none) - typed/record-op updater @*reel op (generate-id!) (now-ms) + :code $ quote $ defn dispatch! (raw-op) + if (enum? raw-op) + let + op raw-op + when config/dev? $ println |Dispatch: op + reset! *reel $ match (typed/decode-control op) + (:some control) (typed/apply-control updater @*reel control) + (:none) + typed/record-op updater @*reel op (generate-id!) (now-ms) + raise |Dispatch-expected-an-Enum + , &unit :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) - :args $ [] 'Enum + :args $ [] 'Dynamic :features $ #{} :js-ffi 'main! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn main! () println "|Running mode:" $ if config/dev? |dev |release render-app! - add-watch *reel :changes $ fn (r p) + add-watch! *reel :changes $ fn (r p) hint-fn $ {} :args $ [] (:: 'reel.typed/State 'Enum 'app.schema/Store) (:: 'reel.typed/State 'Enum 'app.schema/Store) :return 'Unit render-app! listen-devtools! |a dispatch! - js/window.addEventListener |beforeunload $ fn (event) - hint-fn $ {} - :args $ [] 'Dynamic - :return 'Unit - :features $ #{} :js-ffi - persist-storage! - browser/set-interval! persist-storage! 60 match browser/storage-get $ get config/site :storage-key @@ -202,6 +200,8 @@ (:some raw) dispatch! $ :: :hydrate-storage $ parse-cirru-edn raw (:none) &unit + browser/add-event-listener! |beforeunload $ fn (event) (persist-storage!) + browser/set-interval! persist-storage! 60000 println "|App started." :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) @@ -209,9 +209,9 @@ :features $ #{} :js-ffi 'mount-target $ %{} 'CodeEntry (:doc |) :code $ quote $ def mount-target - (browser/query-selector |.app) .unwrap + option:unwrap $ dom/query-selector |.app :examples $ [] - :schema $ :: 'js-ffi.browser/DomElementHost + :schema $ :: 'respo.dom/DomElement 'persist-storage! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn persist-storage! () browser/storage-set! @@ -225,8 +225,8 @@ 'reload! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn reload! () if (js-nullish? build-errors) - do (remove-watch *reel :changes) (clear-cache!) - add-watch *reel :changes $ fn (reel prev) + do (remove-watch! *reel :changes) (clear-cache!) + add-watch! *reel :changes $ fn (reel prev) hint-fn $ {} :args $ [] (:: 'reel.typed/State 'Enum 'app.schema/Store) (:: 'reel.typed/State 'Enum 'app.schema/Store) :return 'Unit @@ -241,31 +241,9 @@ :features $ #{} :js-ffi 'render-app! $ %{} 'CodeEntry (:doc |) :code $ quote $ defn render-app! () - render! mount-target (comp-container @*reel) dispatch! - :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Unit) - :args $ [] - :features $ #{} :js-ffi - 'repeat! $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn repeat! (duration cb) - js/setTimeout - fn () - hint-fn $ {} - :args $ [] - :return 'Unit - :features $ #{} :js-ffi - cb - repeat! (* 1000 duration) cb - * 1000 duration - , &unit - :examples $ [] - :schema $ :: 'Fn $ {} (:return 'Unit) - :args $ [] 'Number $ :: 'Fn - {} (:return 'Unit) - :args $ [] - :features $ #{} :js-ffi - 'snippets $ %{} 'CodeEntry (:doc |) - :code $ quote $ defn snippets () (println config/cdn?) + render-with! mount-target + fn () $ comp-container @*reel + , dispatch! :examples $ [] :schema $ :: 'Fn $ {} (:return 'Unit) :args $ [] @@ -273,7 +251,7 @@ :ns $ %{} 'NsEntry (:doc |) :code $ quote $ ns app.main :require - respo.core :refer $ render! clear-cache! + respo.core :refer $ render-with! clear-cache! app.comp.container :refer $ comp-container app.updater :refer $ updater app.schema :as schema @@ -284,6 +262,7 @@ app.config :as config |./calcit.build-errors.mjs :default build-errors |bottom-tip :default hud! + respo.ffi.browser :as dom 'app.schema $ %{} 'FileEntry :defs $ {} 'StatesInput $ %{} 'CodeEntry (:doc |) @@ -298,16 +277,29 @@ :content 'String :examples $ [] :schema $ :: 'StructDef - 'decode-store $ %{} 'CodeEntry (:doc |) + 'decode-store $ %{} 'CodeEntry + :doc "|Restore legacy Maps or parsed Struct archives by validating named fields into the current Store constructor. The single struct-to-map conversion is an actual archive boundary, not a type assertion; unrelated legacy keys remain ignored." :code $ quote $ defn decode-store (raw) - decode-map-as - {} - :states $ option:unwrap $ get raw :states - :content $ option:unwrap $ get raw :content - , Store + let + fields $ if (map? raw) raw $ if (struct? raw) (&struct:to-map raw) (raise |Saved-store-must-be-a-Map-or-Struct) + decode-map-as + {} + :states $ option:unwrap $ get fields :states + :content $ option:unwrap $ get fields :content + , Store :examples $ [] :schema $ :: 'Fn $ {} (:return 'app.schema/Store) :args $ [] 'Dynamic + :tests $ [] $ %{} 'TestEntry (:name |legacy-map-and-struct-archive) + :code $ quote $ let + decoded $ decode-store $ {} + :states $ {} + :content "|历史存档 🐈" + :unused |legacy-extra + restored $ decode-store $ parse-cirru-edn (format-cirru-edn decoded) + assert= "|历史存档 🐈" $ :content decoded + assert= "|历史存档 🐈" $ :content restored + assert= ({}) (:states restored) 'store $ %{} 'CodeEntry (:doc |) :code $ quote $ def store Store :states ({}) :content | @@ -325,9 +317,9 @@ input $ decode-map-as {} (:cursor cursor) (:data data) , app.schema/StatesInput - struct-with store $ :states $ assoc-in (:states store) - concat (:cursor input) ([] :data) - :data input + struct-with store $ :states $ decode-map-as + update-state-tree (:states store) (:cursor input) (:data input) + :: 'Map 'Tag 'Dynamic (:content content) struct-with store $ :content $ expect-string |content content (:hydrate-storage data) (decode-store data) @@ -336,8 +328,18 @@ :schema $ :: 'Fn $ {} (:return 'app.schema/Store) :args $ [] 'app.schema/Store 'Enum 'String 'Number :features $ #{} :js-ffi + :tests $ [] $ %{} 'TestEntry (:name |cursor-preserves-content) + :code $ quote $ let + base $ app.schema/Store :states ({}) :content |keep + next $ updater base + :: :states ([] :editor) |draft + , |test 0 + assert= |keep $ :content next + assert= |draft $ option:unwrap $ get-in (:states next) ([] :editor :data) + assert= ({}) (:states base) :ns $ %{} 'NsEntry (:doc |) :code $ quote $ ns app.updater :require app.schema :refer $ Store StatesInput decode-store js-ffi.contract :refer $ expect-string + respo.cursor :refer $ update-state-tree diff --git a/deps.cirru b/deps.cirru index ae33400..cdaf483 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,6 +1,6 @@ -{} (:calcit-version |0.27.0) +{} (:calcit-version |0.30.1-alpha.5) :version |0.0.1 :dependencies $ {} - |Respo/reel.calcit |0.6.33-alpha.2 - |Respo/respo.calcit |0.16.114-alpha.5 - |calcit-lang/js-ffi |0.2.1-alpha.4 + |Respo/reel.calcit |0.6.39 + |Respo/respo.calcit |0.16.122 + |calcit-lang/js-ffi |0.2.3 diff --git a/package.json b/package.json index 213d6bb..27a2ac7 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "vite": "^8.3.1" }, "dependencies": { - "@calcit/procs": "0.27.0", + "@calcit/procs": "0.30.1-alpha.5", "bottom-tip": "^0.1.5", "feather-icons": "^4.29.1", "shortid": "^2.2.16" diff --git a/yarn.lock b/yarn.lock index a7fd84b..5498640 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,14 +5,22 @@ __metadata: version: 10 cacheKey: 10c0 -"@calcit/procs@npm:0.27.0": - version: 0.27.0 - resolution: "@calcit/procs@npm:0.27.0" +"@calcit/finger-vec@npm:0.1.1": + version: 0.1.1 + resolution: "@calcit/finger-vec@npm:0.1.1" + checksum: 10c0/c75e7f58924a7bb226ca4a47f6b0702021796b096c04d7fb7e9a92cb49b11ca64d654ea10d78bde29a8ac2d2d05ef9e7865efd2b358af2ca989d950b04d529a8 + languageName: node + linkType: hard + +"@calcit/procs@npm:0.30.1-alpha.5": + version: 0.30.1-alpha.5 + resolution: "@calcit/procs@npm:0.30.1-alpha.5" dependencies: + "@calcit/finger-vec": "npm:0.1.1" "@calcit/ternary-tree": "npm:0.0.26" - "@cirru/parser.ts": "npm:^0.0.9" + "@cirru/parser.ts": "npm:0.0.10" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/85edf1724beaf5e3b895f456b9d81ae93100316802b19bc20ab7cbcc61e295892e787c2f9cca0c9c231ba1cc3c3d320ae158d3f8c9fd39eda24500c56ebe99b1 + checksum: 10c0/925226e14b05ab6f1ed5adf32611a3575324d9e99474e84dce6f01093dce4d869d301585bb76c06d56725a716a76536888f99b9da5c961e79c9b0c62a5b27b1c languageName: node linkType: hard @@ -23,10 +31,10 @@ __metadata: languageName: node linkType: hard -"@cirru/parser.ts@npm:^0.0.9": - version: 0.0.9 - resolution: "@cirru/parser.ts@npm:0.0.9" - checksum: 10c0/3b13623b8f627ac81adae0cb6a4e3664f0da09115a997ca9b78b8d5d0b0f9b9b18c1ad847e068831cb1052c7da2a00329d2af04421b1024a004eef0ddca5fd7a +"@cirru/parser.ts@npm:0.0.10": + version: 0.0.10 + resolution: "@cirru/parser.ts@npm:0.0.10" + checksum: 10c0/d66a16bcf49c60865fb8e733a5c219289a7c755c6400dea7e7a8d3166ee7841a623030f35f8ee96e232bc40f974f1b79a0ccdf089947fc5779699485102b5995 languageName: node linkType: hard @@ -964,7 +972,7 @@ __metadata: version: 0.0.0-use.local resolution: "sitemap@workspace:." dependencies: - "@calcit/procs": "npm:0.27.0" + "@calcit/procs": "npm:0.30.1-alpha.5" bottom-tip: "npm:^0.1.5" feather-icons: "npm:^4.29.1" shortid: "npm:^2.2.16"