From 6c1c19d33a80837d0a1ddb8009fa48ccf6e6321b Mon Sep 17 00:00:00 2001 From: tiye Date: Tue, 26 May 2026 13:27:44 +0800 Subject: [PATCH 01/26] extending echarts renderers --- .pnp.cjs | 30 ++++++ Agents.md | 22 +++- COMPONENTS.md | 275 +++++++++++++++++++++++++++++++++++++++++++++++- calcit.cirru | 286 ++++++++++++++++++++++---------------------------- main.mjs | 91 ++++++++++++++++ package.json | 1 + yarn.lock | 27 +++++ 7 files changed, 565 insertions(+), 167 deletions(-) diff --git a/.pnp.cjs b/.pnp.cjs index 553d5e5..4dbea80 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -30,6 +30,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@calcit/procs", "npm:0.12.35"],\ ["bottom-tip", "npm:0.1.5"],\ + ["echarts", "npm:6.1.0"],\ ["mermaid", "npm:11.15.0"],\ ["root-workspace-0b6124", "workspace:."],\ ["vite", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12"]\ @@ -1406,6 +1407,17 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["echarts", [\ + ["npm:6.1.0", {\ + "packageLocation": "../../../.yarn/berry/cache/echarts-npm-6.1.0-517b8ad5ee-10c0.zip/node_modules/echarts/",\ + "packageDependencies": [\ + ["echarts", "npm:6.1.0"],\ + ["tslib", "npm:2.3.0"],\ + ["zrender", "npm:6.1.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["encoding", [\ ["npm:0.1.13", {\ "packageLocation": "../../../.yarn/berry/cache/encoding-npm-0.1.13-82a1837d30-10c0.zip/node_modules/encoding/",\ @@ -2259,6 +2271,7 @@ const RAW_RUNTIME_STATE = "packageDependencies": [\ ["@calcit/procs", "npm:0.12.35"],\ ["bottom-tip", "npm:0.1.5"],\ + ["echarts", "npm:6.1.0"],\ ["mermaid", "npm:11.15.0"],\ ["root-workspace-0b6124", "workspace:."],\ ["vite", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12"]\ @@ -2428,6 +2441,13 @@ const RAW_RUNTIME_STATE = }]\ ]],\ ["tslib", [\ + ["npm:2.3.0", {\ + "packageLocation": "../../../.yarn/berry/cache/tslib-npm-2.3.0-277e75e108-10c0.zip/node_modules/tslib/",\ + "packageDependencies": [\ + ["tslib", "npm:2.3.0"]\ + ],\ + "linkType": "HARD"\ + }],\ ["npm:2.8.1", {\ "packageLocation": "../../../.yarn/berry/cache/tslib-npm-2.8.1-66590b21b8-10c0.zip/node_modules/tslib/",\ "packageDependencies": [\ @@ -2604,6 +2624,16 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }]\ + ]],\ + ["zrender", [\ + ["npm:6.1.0", {\ + "packageLocation": "../../../.yarn/berry/cache/zrender-npm-6.1.0-025f06fccb-10c0.zip/node_modules/zrender/",\ + "packageDependencies": [\ + ["tslib", "npm:2.3.0"],\ + ["zrender", "npm:6.1.0"]\ + ],\ + "linkType": "HARD"\ + }]\ ]]\ ]\ }'; diff --git a/Agents.md b/Agents.md index 1ec13fd..dae95bc 100644 --- a/Agents.md +++ b/Agents.md @@ -29,7 +29,8 @@ cr query config ```bash chrome-devtools status -chrome-devtools start +chrome-devtools start # headless 无界面模式(默认) +chrome-devtools start --headless false # 界面模式,方便用户直接确认页面状态 chrome-devtools list_pages chrome-devtools new_page 'http://127.0.0.1:3010' chrome-devtools select_page --bringToFront @@ -42,7 +43,7 @@ chrome-devtools fill 'value' 推荐工作流: -- 先 `chrome-devtools status`,确认 daemon 已启动;需要时用 `chrome-devtools start` 重启隔离浏览器。 +- 先 `chrome-devtools status`,确认 daemon 已启动;需要时用 `chrome-devtools start --headless false` 以界面模式重启,方便用户直接看到浏览器窗口确认。 - 用 `new_page` 打开本地页面,再用 `list_pages` / `select_page` 切换目标页。 - 优先 `take_snapshot` 查看 a11y 树文本和元素 uid;只有需要看视觉细节时再截图。 - 先看 `list_console_messages`,再决定是否需要 `evaluate_script`、`click`、`fill`。 @@ -71,6 +72,23 @@ cr js yarn vite ``` +relay 联调命令(端口 9100,系统 9001/9002 被 macOS 占用): + +```bash +edn-relay serve --bind 127.0.0.1:9100 +edn-relay genui --server ws://127.0.0.1:9100 "$(cat /tmp/layout.cirru)" --timeout-secs 10 +``` + +genui 布局文件用 2 空格缩进(不要用 tab),示例: + +``` +{} + :type |card + :text "|Hello" + :children $ [] + {} (:type |text) (:text "|world") +``` + ## 高频工作流 - 先定位再修改。先 `query def/search`,再 `tree show`,最后做 `tree replace` 或 `edit def`。 diff --git a/COMPONENTS.md b/COMPONENTS.md index 49ee45a..7737ceb 100644 --- a/COMPONENTS.md +++ b/COMPONENTS.md @@ -1,9 +1,276 @@ -# Components DSL +# edn-renderer 组件文档 -This document defines the current Cirru EDN layout DSL consumed by `edn-renderer` -on the `genui` channel. +## 整体架构 -## Envelope Shape +本项目由两个部分组成: + +- **edn-relay**:通用 WebSocket 中继服务器,负责消息路由和协议帧处理。relay 本身无业务逻辑,通过**频道名称**(如 `genui`)标识不同场景。 +- **edn-renderer**:前端渲染器,保存 UI 上下文和组件能力。命令行通过 relay 向 renderer 发送布局描述(Layout DSL),renderer 解析后渲染对应的 UI 组件。 + +``` +CLI / 脚本 + │ edn-relay genui --server ws://... '' + ▼ +edn-relay (relay) + │ WebSocket 消息路由 (genui 频道) + ▼ +edn-renderer (browser) + │ parse-layout-node → 渲染组件树 + ▼ +页面 UI +``` + +**设计原则**: + +- relay 保持通用,不感知业务 +- renderer 维护所有 UI 能力(组件列表、状态管理) +- Layout 组件负责将原子组件组合成复杂界面 +- 命令行可通过频道名称选择对应的 renderer 能力 + +--- + +## Layout DSL 格式 + +Layout 使用 Cirru EDN 格式描述,顶层为一个 map,包含 `:type` 字段标识节点类型。子节点通过 `:children` 列表组合。 + +```cirru +{} + :type |column + :children $ [] + {} + :type |text + :text "|Hello, World!" + {} + :type |chart + :kind |bar + :title "|Sales Data" + :series $ [] + {} (:label |Q1) (:value 120) + {} (:label |Q2) (:value 145) +``` + +--- + +## 布局容器 + +### column(垂直列) + +子节点纵向排列。 + +```cirru +{} + :type |column + :children $ [] + {} (:type |text) (:text "|第一行") + {} (:type |text) (:text "|第二行") +``` + +### row(水平行) + +子节点横向排列。 + +```cirru +{} + :type |row + :children $ [] + {} (:type |badge) (:text "|标签A") + {} (:type |badge) (:text "|标签B") +``` + +### card(卡片) + +带标题的容器,`:text` 为卡片标题,`:children` 为内容。 + +```cirru +{} + :type |card + :text "|数据概览" + :children $ [] + {} (:type |text) (:text "|内容在这里") +``` + +--- + +## 文本组件 + +### text(普通文本) + +渲染一段文字,`:text` 为内容。 + +```cirru +{} (:type |text) (:text "|这是一段描述") +``` + +### badge(徽标) + +带样式的标签,适合状态或分类标注。 + +```cirru +{} (:type |badge) (:text "|进行中") +``` + +### markdown(Markdown) + +将 `:text` 内容渲染为 Markdown 富文本。 + +```cirru +{} (:type |markdown) (:text "|## 标题\n\n这是**粗体**文字。") +``` + +--- + +## 交互组件 + +### button(按钮) + +显示一个按钮,当前为只读展示状态(`:disabled true`)。 + +```cirru +{} (:type |button) (:text "|点击操作") +``` + +### input(输入框) + +显示文本输入框,`:name` 为标识,`:placeholder` 为提示文字,`:text` 为当前值。 + +```cirru +{} + :type |input + :name "|search" + :placeholder "|搜索关键词" + :text | +``` + +--- + +## 分隔线 + +### divider(分割线) + +水平分隔线,无额外参数。 + +```cirru +{} (:type |divider) +``` + +--- + +## 图表组件 + +### chart(ECharts 图表) + +使用 ECharts 渲染数据可视化图表。支持多种图表类型。 + +| 字段 | 类型 | 说明 | +| --------- | ------ | --------------------------------------------------------------------------------------------- | +| `:kind` | 字符串 | 图表类型:`bar`(柱状图)、`line`(折线图)、`pie`(饼图)、`scatter`(散点图)。默认 `bar`。 | +| `:title` | 字符串 | 图表标题(可选) | +| `:series` | 列表 | 数据项,每项包含 `:label`(字符串)和 `:value`(数字) | + +**柱状图示例**: + +```cirru +{} + :type |chart + :kind |bar + :title "|各区域销售额" + :series $ [] + {} (:label |北区) (:value 42) + {} (:label |南区) (:value 27) + {} (:label |西区) (:value 35) +``` + +**折线图示例**: + +```cirru +{} + :type |chart + :kind |line + :title "|月度趋势" + :series $ [] + {} (:label |1月) (:value 10) + {} (:label |2月) (:value 18) + {} (:label |3月) (:value 14) + {} (:label |4月) (:value 22) +``` + +**饼图示例**: + +```cirru +{} + :type |chart + :kind |pie + :title "|市场份额" + :series $ [] + {} (:label |产品A) (:value 55) + {} (:label |产品B) (:value 25) + {} (:label |产品C) (:value 20) +``` + +--- + +## 图表渲染机制 + +`comp-chart-block` 将图表配置(kind、title、series)序列化为 Cirru EDN 格式,存储在 DOM 节点的 `title` 属性中。`main.mjs` 通过 `MutationObserver` 监听 `.echarts-host` 节点的插入,解析数据后调用 `echarts.setOption()` 渲染。 + +这与 Mermaid 图表的渲染方式相同,保持了一致的「Calcit 写入属性 → JS 读取渲染」的分工模式。 + +--- + +## Mermaid 图表 + +### mermaid(流程图 / 时序图等) + +使用 [Mermaid](https://mermaid.js.org/) 渲染图形,`:text` 为 Mermaid DSL 语法。 + +```cirru +{} + :type |mermaid + :text "|graph TD\n A[开始] --> B{条件}\n B -->|是| C[执行]\n B -->|否| D[跳过]" +``` + +--- + +## 完整示例 + +```cirru +{} + :type |column + :children $ [] + {} + :type |card + :text "|销售看板" + :children $ [] + {} + :type |row + :children $ [] + {} (:type |badge) (:text "|本季度") + {} (:type |badge) (:text "|对比去年 +12%") + {} (:type |divider) + {} + :type |chart + :kind |bar + :title "|各区域销售额(万元)" + :series $ [] + {} (:label |华北) (:value 88) + {} (:label |华东) (:value 120) + {} (:label |华南) (:value 95) + {} (:label |西部) (:value 62) + {} + :type |chart + :kind |pie + :title "|产品线占比" + :series $ [] + {} (:label |硬件) (:value 45) + {} (:label |软件) (:value 35) + {} (:label |服务) (:value 20) +``` + +--- + + + +## Envelope Shape (legacy) A layout payload is a single Cirru EDN map. diff --git a/calcit.cirru b/calcit.cirru index efe854a..39425e1 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -6,42 +6,16 @@ :files $ {} |app.comp.container $ %{} :FileEntry :defs $ {} + |LayoutNode $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def LayoutNode $ defenum LayoutNode (:column :list) (:row :list) (:card :dynamic :list) (:text :string) (:badge :string) (:divider) (:button :string) (:input :dynamic :dynamic :dynamic) (:markdown :string) (:mermaid :string) (:chart :dynamic :dynamic :list) + :examples $ [] |comp-chart-block $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defcomp comp-chart-block (series) - let - safe-series series - raw-max $ reduce safe-series 0 - fn (acc item) - if - > (:value item) acc - :value item - , acc - max-value $ if (> raw-max 0) raw-max 1 - list-> - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 12) - -> safe-series .to-list $ map-indexed - fn (idx item) - let - label $ or (:label item) |Item - value $ :value item - bar-width $ str - * 100 $ / value max-value - , |% - [] idx $ div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 6) - div - {} $ :style - {} (:display |flex) (:justify-content |space-between) (:gap 16) (:font-size 13) (:font-weight |600) (:color |#6d4a31) - <> label - <> $ str value - div - {} $ :style - {} (:height 12) (:border-radius 999) (:background-color |#efdfd1) (:overflow |hidden) - div $ {} - :style $ {} (:width bar-width) (:height |100%) (:border-radius 999) (:background "|linear-gradient(90deg, #c96f2d, #e5aa6f)") + defcomp comp-chart-block (series kind title) + [] (effect-echarts series kind title) + div $ {} (:class-name |echarts-host) + :style $ {} (:width |100%) (:height |300px) :examples $ [] |comp-container $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -163,111 +137,6 @@ {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) <> "|Waiting for a validated genui layout from the relay." :examples $ [] - |LayoutNode $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - def LayoutNode $ defenum LayoutNode - :column :list - :row :list - :card :dynamic :list - :text :string - :badge :string - :divider - :button :string - :input :dynamic :dynamic :dynamic - :markdown :string - :mermaid :string - :chart :list - :examples $ [] - |parse-layout-children $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn parse-layout-children (children path) - if - not $ list? children - raise $ str path "| field :children should be a list" - foldl children ([]) - fn (acc child) - append acc $ parse-layout-node child (str path |.children) - :examples $ [] - |parse-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn parse-layout-node (node path) - if - not $ map? node - raise $ str path "| expected a map node" - let - node-type $ :type node - children $ or (:children node) ([]) - series $ or (:series node) ([]) - if - not $ string? node-type - raise $ str path "| is missing string field :type" - case-default node-type - raise $ str path "| does not support node type " node-type - |column $ %:: LayoutNode :column $ parse-layout-children children path - |row $ %:: LayoutNode :row $ parse-layout-children children path - |card $ %:: LayoutNode :card (:text node) $ parse-layout-children children path - |text $ if - and - string? $ :text node - > - count $ :text node - , 0 - %:: LayoutNode :text $ :text node - raise $ str path "| text node requires non-empty :text" - |badge $ if - and - string? $ :text node - > - count $ :text node - , 0 - %:: LayoutNode :badge $ :text node - raise $ str path "| badge node requires non-empty :text" - |divider $ %:: LayoutNode :divider - |button $ if - and - string? $ :text node - > - count $ :text node - , 0 - %:: LayoutNode :button $ :text node - raise $ str path "| button node requires non-empty :text" - |input $ if - or - some? $ :name node - some? $ :placeholder node - %:: LayoutNode :input (:name node) (:placeholder node) (:text node) - raise $ str path "| input node requires :name or :placeholder" - |markdown $ if - and - string? $ :text node - > - count $ :text node - , 0 - %:: LayoutNode :markdown $ :text node - raise $ str path "| markdown node requires non-empty :text" - |mermaid $ if - and - string? $ :text node - > - count $ :text node - , 0 - %:: LayoutNode :mermaid $ :text node - raise $ str path "| mermaid node requires non-empty :text" - |chart $ do - if - not $ list? series - raise $ str path "| chart node requires list field :series" - every? series $ fn (item) - if - not $ map? item - raise $ str path "| chart series item should be a map" - if - and - string? $ :label item - number? $ :value item - , item $ raise (str path "| chart series item requires string :label and number :value") - %:: LayoutNode :chart series - :examples $ [] |comp-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defcomp comp-layout-node (node) @@ -290,8 +159,7 @@ div {} $ :style {} (:display |flex) (:flex-direction |column) (:gap 12) (:padding 16) (:border "|1px solid #e4d4c6") (:border-radius 16) (:background-color |#fffdf9) - if-let - title-text title + if-let (title-text title) div {} $ :style {} (:font-size 18) (:font-weight |600) (:color |#7e4f2c) @@ -313,32 +181,27 @@ {} (:display |inline-flex) (:align-items |center) (:padding 8) (:border-radius 999) (:background-color |#f3d7ba) (:color |#7d4d27) (:font-size 12) (:font-weight |600) (:width |fit-content) <> text (:divider) - div - {} $ :style - {} (:height 1) (:width |100%) (:background-color |#e6d4c4) + div $ {} + :style $ {} (:height 1) (:width |100%) (:background-color |#e6d4c4) (:button text) - button - {} (:disabled true) - :inner-text text - :style $ {} (:padding 10) (:border "|1px solid #cf8b5d") (:background-color |#e8b488) (:color |#3e2515) (:border-radius 999) (:font-size 14) (:font-weight |600) (:cursor |not-allowed) + button $ {} (:disabled true) (:inner-text text) + :style $ {} (:padding 10) (:border "|1px solid #cf8b5d") (:background-color |#e8b488) (:color |#3e2515) (:border-radius 999) (:font-size 14) (:font-weight |600) (:cursor |not-allowed) (:input name placeholder value) - input - {} (:disabled true) - :value $ or value | - :placeholder $ or placeholder - or name |Input - :style $ {} (:padding 10) (:border "|1px solid #d8c8ba") (:border-radius 12) (:font-size 14) (:background-color |#fff) (:min-width |160px) + input $ {} (:disabled true) + :value $ or value | + :placeholder $ or placeholder (or name |Input) + :style $ {} (:padding 10) (:border "|1px solid #d8c8ba") (:border-radius 12) (:font-size 14) (:background-color |#fff) (:min-width |160px) (:markdown text) div {} $ :style {} (:padding 18) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #e8d7ca") comp-markdown-block text - (:mermaid text) $ comp-mermaid-block text - (:chart series) + (:mermaid text) (comp-mermaid-block text) + (:chart kind title series) div {} $ :style {} (:padding 18) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #e8d7ca") - comp-chart-block series + comp-chart-block series kind title _ $ div {} $ :style {} (:padding 12) (:border "|1px solid #f08c6c") (:border-radius 12) (:background-color |#fff4ef) (:color |#9b3d15) @@ -407,19 +270,120 @@ :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) <> "|Rendering Mermaid diagram..." :examples $ [] + |effect-echarts $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defeffect effect-echarts (series kind title) (action el at?) + case-default action nil + :mount $ .!renderEcharts js/window el series kind title + :update $ .!renderEcharts js/window el series kind title + :unmount $ .!disposeEcharts js/window el + :examples $ [] + |parse-layout-children $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn parse-layout-children (children path) + if + not $ list? children + raise $ str path "| field :children should be a list" + foldl children ([]) + fn (acc child) + append acc $ parse-layout-node child (str path |.children) + :examples $ [] + |parse-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn parse-layout-node (node path) + if + not $ map? node + raise $ str path "| expected a map node" + let + node-type $ :type node + children $ or (:children node) ([]) + series $ or (:series node) ([]) + if + not $ string? node-type + raise $ str path "| is missing string field :type" + case-default node-type + raise $ str path "| does not support node type " node-type + |column $ %:: LayoutNode :column (parse-layout-children children path) + |row $ %:: LayoutNode :row (parse-layout-children children path) + |card $ %:: LayoutNode :card (:text node) (parse-layout-children children path) + |text $ if + and + string? $ :text node + > + count $ :text node + , 0 + %:: LayoutNode :text $ :text node + raise $ str path "| text node requires non-empty :text" + |badge $ if + and + string? $ :text node + > + count $ :text node + , 0 + %:: LayoutNode :badge $ :text node + raise $ str path "| badge node requires non-empty :text" + |divider $ %:: LayoutNode :divider + |button $ if + and + string? $ :text node + > + count $ :text node + , 0 + %:: LayoutNode :button $ :text node + raise $ str path "| button node requires non-empty :text" + |input $ if + or + some? $ :name node + some? $ :placeholder node + %:: LayoutNode :input (:name node) (:placeholder node) (:text node) + raise $ str path "| input node requires :name or :placeholder" + |markdown $ if + and + string? $ :text node + > + count $ :text node + , 0 + %:: LayoutNode :markdown $ :text node + raise $ str path "| markdown node requires non-empty :text" + |mermaid $ if + and + string? $ :text node + > + count $ :text node + , 0 + %:: LayoutNode :mermaid $ :text node + raise $ str path "| mermaid node requires non-empty :text" + |chart $ do + if + not $ list? series + raise $ str path "| chart node requires list field :series" + every? series $ fn (item) + if + not $ map? item + raise $ str path "| chart series item should be a map" + if + and + string? $ :label item + number? $ :value item + , item $ raise (str path "| chart series item requires string :label and number :value") + %:: LayoutNode :chart + or (:kind node) |bar + or (:title node) | + , series + :examples $ [] |validate-layout $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn validate-layout (layout) $ parse-layout-node layout |root + defn validate-layout (layout) (parse-layout-node layout |root) :examples $ [] |validate-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn validate-layout-node (node path) $ parse-layout-node node path + defn validate-layout-node (node path) (parse-layout-node node path) :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote ns app.comp.container $ :require (respo-ui.css :as css) respo.css :refer $ defstyle - respo.core :refer $ defcomp <> >> div button textarea span input list-> + respo.core :refer $ defcomp defeffect <> >> div button textarea span input list-> respo.comp.space :refer $ =< reel.comp.reel :refer $ comp-reel app.config :refer $ dev? @@ -431,7 +395,7 @@ :examples $ [] |site $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9001) (:relay-channel |genui) + def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) (:relay-channel |genui) :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.config) diff --git a/main.mjs b/main.mjs index 2d8170e..a8b1ad2 100644 --- a/main.mjs +++ b/main.mjs @@ -1,6 +1,97 @@ import mermaid from "mermaid"; +import * as echarts from "echarts"; +import { CalcitTag, CalcitSliceList, listToArray } from "@calcit/procs"; import { main_$x_ } from "./js-out/app.main.mjs"; +function ednToJs(v) { + if (v === null || v === undefined) return null; + if (typeof v === "number" || typeof v === "boolean") return v; + if (typeof v === "string") return v; + if (v instanceof CalcitTag) return v.value; + if (v instanceof CalcitSliceList) return listToArray(v).map(ednToJs); + if (v && v.constructor && v.constructor.name === "CalcitSliceMap") { + const obj = {}; + for (const [k, val] of v.pairs()) { + const key = k instanceof CalcitTag ? k.value : String(k); + obj[key] = ednToJs(val); + } + return obj; + } + return String(v); +} + +function buildEchartsOption({ kind, title, series }) { + const base = { + animation: false, + tooltip: {}, + ...(title ? { title: { text: title } } : {}), + }; + const names = (series || []).map((s) => s.label); + const values = (series || []).map((s) => s.value); + switch (kind) { + case "line": + return { + ...base, + xAxis: { type: "category", data: names }, + yAxis: { type: "value" }, + series: [{ type: "line", data: values }], + }; + case "pie": + return { + ...base, + series: [ + { + type: "pie", + data: (series || []).map((s) => ({ + name: s.label, + value: s.value, + })), + }, + ], + }; + case "scatter": + return { + ...base, + xAxis: { type: "category", data: names }, + yAxis: { type: "value" }, + series: [{ type: "scatter", data: values }], + }; + default: + return { + ...base, + xAxis: { type: "category", data: names }, + yAxis: { type: "value" }, + series: [{ type: "bar", data: values }], + }; + } +} + +window.renderEcharts = (el, seriesCalcit, kindCalcit, titleCalcit) => { + try { + const series = ednToJs(seriesCalcit); + const kind = ednToJs(kindCalcit); + const title = ednToJs(titleCalcit); + const option = buildEchartsOption({ kind, title, series }); + let chart = echarts.getInstanceByDom(el); + if (!chart) chart = echarts.init(el); + chart.setOption(option, true); + } catch (e) { + console.error("ECharts render error:", e); + } +}; + +window.disposeEcharts = (el) => { + const chart = echarts.getInstanceByDom(el); + if (chart) chart.dispose(); +}; + +window.addEventListener("resize", () => { + for (const host of document.querySelectorAll(".echarts-host")) { + const chart = echarts.getInstanceByDom(host); + if (chart) chart.resize(); + } +}); + mermaid.initialize({ startOnLoad: false, securityLevel: "loose", diff --git a/package.json b/package.json index ffd9139..f672acf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@calcit/procs": "^0.12.35", + "echarts": "^6.1.0", "mermaid": "^11.15.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index ddb7a86..bfe6fc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1150,6 +1150,16 @@ __metadata: languageName: node linkType: hard +"echarts@npm:^6.1.0": + version: 6.1.0 + resolution: "echarts@npm:6.1.0" + dependencies: + tslib: "npm:2.3.0" + zrender: "npm:6.1.0" + checksum: 10c0/a0911a9d75969ff946d0bce998e95e1924cdec7f5ca6be13ded376e2a47067c70b264ee8d04ba84345aa7895363e202f54c87d2bae114859bd154d565babc09c + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -1950,6 +1960,7 @@ __metadata: dependencies: "@calcit/procs": "npm:^0.12.35" bottom-tip: "npm:^0.1.5" + echarts: "npm:^6.1.0" mermaid: "npm:^11.15.0" vite: "npm:^8.0.12" languageName: unknown @@ -2095,6 +2106,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.3.0": + version: 2.3.0 + resolution: "tslib@npm:2.3.0" + checksum: 10c0/a845aed84e7e7dbb4c774582da60d7030ea39d67307250442d35c4c5dd77e4b44007098c37dd079e100029c76055f2a362734b8442ba828f8cc934f15ed9be61 + languageName: node + linkType: hard + "tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" @@ -2247,3 +2265,12 @@ __metadata: checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 languageName: node linkType: hard + +"zrender@npm:6.1.0": + version: 6.1.0 + resolution: "zrender@npm:6.1.0" + dependencies: + tslib: "npm:2.3.0" + checksum: 10c0/bfdb5e2ccd70893a9e2c7f33959749a6a1abc8912c2b1af301a05593d677a2f6f6087cdc4f0bf73883b99eea2f2d11d1922c71c3bf0bd4e9e5b362fd32129bce + languageName: node + linkType: hard From 43d76f3ac4666a4246037db95a838279ba5a61d1 Mon Sep 17 00:00:00 2001 From: tiye Date: Tue, 26 May 2026 15:57:32 +0800 Subject: [PATCH 02/26] prefer glue code in calcit over in js --- calcit.cirru | 178 +++++++++++++++++++++++++++++++++++++++++++++------ main.mjs | 162 ---------------------------------------------- 2 files changed, 159 insertions(+), 181 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 39425e1..5a149a5 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -6,16 +6,84 @@ :files $ {} |app.comp.container $ %{} :FileEntry :defs $ {} + |*mermaid-ready $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (defatom *mermaid-ready false) + :examples $ [] + |*rendered-svgs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defatom *rendered-svgs $ {} (:_init_ true) + :examples $ [] |LayoutNode $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def LayoutNode $ defenum LayoutNode (:column :list) (:row :list) (:card :dynamic :list) (:text :string) (:badge :string) (:divider) (:button :string) (:input :dynamic :dynamic :dynamic) (:markdown :string) (:mermaid :string) (:chart :dynamic :dynamic :list) :examples $ [] + |build-echarts-option $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-echarts-option (series kind title) + let + normalized-kind $ if (string? kind) kind |bar + normalized-series $ if (list? series) series ([]) + names $ -> normalized-series .to-list + map $ fn (item) (:label item) + values $ -> normalized-series .to-list + map $ fn (item) (:value item) + title-part $ if + or (nil? title) (= title |) + {} + {} $ :title + {} $ :text title + base $ merge + {} (:animation false) + :tooltip $ {} + , title-part + case-default normalized-kind + merge base $ {} + :xAxis $ {} (:type |category) (:data names) + :yAxis $ {} (:type |value) + :series $ [] + {} (:type |bar) (:data values) + |line $ merge base + {} + :xAxis $ {} (:type |category) (:data names) + :yAxis $ {} (:type |value) + :series $ [] + {} (:type |line) (:data values) + |pie $ merge base + {} $ :series + [] $ {} + :type |pie + :data $ -> normalized-series .to-list + map $ fn (item) + {} + :name $ :label item + :value $ :value item + |scatter $ merge base + {} + :xAxis $ {} (:type |category) (:data names) + :yAxis $ {} (:type |value) + :series $ [] + {} (:type |scatter) (:data values) + :examples $ [] + |build-mermaid-render-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-mermaid-render-payload (text) + let + source $ if (string? text) text | + trimmed $ trim source + if (= trimmed |) + {} (:empty? true) (:source |) (:graph-id |) + {} (:empty? false) (:source source) + :graph-id $ str |mermaid- (count source) |- + count $ split-lines source + :examples $ [] |comp-chart-block $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defcomp comp-chart-block (series kind title) - [] (effect-echarts series kind title) - div $ {} (:class-name |echarts-host) - :style $ {} (:width |100%) (:height |300px) + let + option $ build-echarts-option series kind title + [] (effect-echarts option) + div $ {} (:class-name |echarts-host) + :style $ {} (:width |100%) (:height |300px) :examples $ [] |comp-container $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -258,25 +326,62 @@ |comp-mermaid-block $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defcomp comp-mermaid-block (text) - div - {} (:class-name |mermaid-host) (:title text) - :style $ {} (:display |flex) (:flex-direction |column) (:gap 10) (:padding 16) (:border-radius 16) (:border "|1px solid #d9cabf") (:background-color |#fffdf9) - div - {} $ :style - {} (:font-size 13) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) - <> |Mermaid - div - {} (:class-name |mermaid-output) - :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) - <> "|Rendering Mermaid diagram..." + let + payload $ build-mermaid-render-payload text + svg-str $ if (:empty? payload) nil + let + result $ get @*rendered-svgs (:source payload) + if (string? result) result nil + [] (effect-mermaid text) + div + {} (:class-name |mermaid-host) + :style $ {} (:display |flex) (:flex-direction |column) (:gap 10) (:padding 16) (:border-radius 16) (:border "|1px solid #d9cabf") (:background-color |#fffdf9) + div + {} $ :style + {} (:font-size 13) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) + <> |Mermaid + div + {} + :class-name |mermaid-output + :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) + :innerHTML $ if (nil? svg-str) "|Rendering Mermaid diagram..." svg-str :examples $ [] |effect-echarts $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defeffect effect-echarts (series kind title) (action el at?) - case-default action nil - :mount $ .!renderEcharts js/window el series kind title - :update $ .!renderEcharts js/window el series kind title - :unmount $ .!disposeEcharts js/window el + defeffect effect-echarts (option) (action el at?) + let + render-chart $ fn () + let + existing $ echarts-lib/getInstanceByDom el + chart $ if (nil? existing) (echarts-lib/init el) existing + do (.!debug js/console "|[echarts] render" option) (.!setOption chart option true) + dispose-chart $ fn () + let + chart $ echarts-lib/getInstanceByDom el + when (some? chart) (.!dispose chart) + case-default action nil + :mount $ do (.!debug js/console "|[echarts] mount") (render-chart) + :update $ do (.!debug js/console "|[echarts] update") (render-chart) + :unmount $ do (.!debug js/console "|[echarts] unmount") (dispose-chart) + :examples $ [] + |effect-mermaid $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defeffect effect-mermaid (text) (action el at?) + let + payload $ build-mermaid-render-payload text + case-default action nil + :mount $ do (.!debug js/console "|[mermaid] mount" payload) + when (not (:empty? payload)) (render-mermaid-on el payload) + :update $ do (.!debug js/console "|[mermaid] update" payload) + when (not (:empty? payload)) (render-mermaid-on el payload) + :unmount $ .!debug js/console "|[mermaid] unmount" + :examples $ [] + |ensure-mermaid! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn ensure-mermaid! () $ when + not $ deref *mermaid-ready + .!initialize mermaid-lib $ js-object (:startOnLoad false) (:securityLevel |loose) (:theme |neutral) + reset! *mermaid-ready true :examples $ [] |parse-layout-children $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -371,6 +476,39 @@ or (:title node) | , series :examples $ [] + |render-mermaid-on $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn render-mermaid-on (el payload) + hint-fn $ {} (:async true) + let + source $ :source payload + graph-id $ :graph-id payload + output $ .!querySelector el |.mermaid-output + if (nil? output) (.!warn js/console "|[mermaid] missing .mermaid-output" el) + if (some? (get @*rendered-svgs source)) + .!debug js/console "|[mermaid] skip rendered" + do + reset! *rendered-svgs (assoc @*rendered-svgs source :rendering) + ensure-mermaid! + let + render-fn $ .-render mermaid-lib + try + let + result $ js-await (.!call render-fn mermaid-lib graph-id source) + svg $ .-svg result + bind-fns $ .-bindFunctions result + do + set! (.-innerHTML output) svg + when (some? bind-fns) (bind-fns output) + reset! *rendered-svgs (assoc @*rendered-svgs source svg) + .!debug js/console "|[mermaid] render" $ js-object + :length $ count source + :graphId graph-id + fn (error) + do + reset! *rendered-svgs (assoc @*rendered-svgs source false) + .!error js/console "|[mermaid] render failed" error + :examples $ [] |validate-layout $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn validate-layout (layout) (parse-layout-node layout |root) @@ -387,6 +525,8 @@ respo.comp.space :refer $ =< reel.comp.reel :refer $ comp-reel app.config :refer $ dev? + |echarts :as echarts-lib + |mermaid :default mermaid-lib |app.config $ %{} :FileEntry :defs $ {} |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) diff --git a/main.mjs b/main.mjs index a8b1ad2..7ead9f4 100644 --- a/main.mjs +++ b/main.mjs @@ -1,90 +1,6 @@ -import mermaid from "mermaid"; import * as echarts from "echarts"; -import { CalcitTag, CalcitSliceList, listToArray } from "@calcit/procs"; import { main_$x_ } from "./js-out/app.main.mjs"; -function ednToJs(v) { - if (v === null || v === undefined) return null; - if (typeof v === "number" || typeof v === "boolean") return v; - if (typeof v === "string") return v; - if (v instanceof CalcitTag) return v.value; - if (v instanceof CalcitSliceList) return listToArray(v).map(ednToJs); - if (v && v.constructor && v.constructor.name === "CalcitSliceMap") { - const obj = {}; - for (const [k, val] of v.pairs()) { - const key = k instanceof CalcitTag ? k.value : String(k); - obj[key] = ednToJs(val); - } - return obj; - } - return String(v); -} - -function buildEchartsOption({ kind, title, series }) { - const base = { - animation: false, - tooltip: {}, - ...(title ? { title: { text: title } } : {}), - }; - const names = (series || []).map((s) => s.label); - const values = (series || []).map((s) => s.value); - switch (kind) { - case "line": - return { - ...base, - xAxis: { type: "category", data: names }, - yAxis: { type: "value" }, - series: [{ type: "line", data: values }], - }; - case "pie": - return { - ...base, - series: [ - { - type: "pie", - data: (series || []).map((s) => ({ - name: s.label, - value: s.value, - })), - }, - ], - }; - case "scatter": - return { - ...base, - xAxis: { type: "category", data: names }, - yAxis: { type: "value" }, - series: [{ type: "scatter", data: values }], - }; - default: - return { - ...base, - xAxis: { type: "category", data: names }, - yAxis: { type: "value" }, - series: [{ type: "bar", data: values }], - }; - } -} - -window.renderEcharts = (el, seriesCalcit, kindCalcit, titleCalcit) => { - try { - const series = ednToJs(seriesCalcit); - const kind = ednToJs(kindCalcit); - const title = ednToJs(titleCalcit); - const option = buildEchartsOption({ kind, title, series }); - let chart = echarts.getInstanceByDom(el); - if (!chart) chart = echarts.init(el); - chart.setOption(option, true); - } catch (e) { - console.error("ECharts render error:", e); - } -}; - -window.disposeEcharts = (el) => { - const chart = echarts.getInstanceByDom(el); - if (chart) chart.dispose(); -}; - window.addEventListener("resize", () => { for (const host of document.querySelectorAll(".echarts-host")) { const chart = echarts.getInstanceByDom(host); @@ -92,88 +8,10 @@ window.addEventListener("resize", () => { } }); -mermaid.initialize({ - startOnLoad: false, - securityLevel: "loose", - theme: "neutral", -}); - -let mermaidRenderQueued = false; - -const renderMermaidBlocks = async () => { - const hosts = document.querySelectorAll(".mermaid-host"); - - for (const [index, host] of hosts.entries()) { - const source = host.getAttribute("title") || ""; - const output = host.querySelector(".mermaid-output"); - if (!output) continue; - - if (!source.trim()) { - output.textContent = ""; - continue; - } - - if (host.dataset.renderedSource === source) continue; - - try { - const graphId = `mermaid-${index}-${source.length}`; - const { svg, bindFunctions } = await mermaid.render( - graphId, - source, - output, - ); - output.innerHTML = svg; - bindFunctions?.(output); - host.dataset.renderedSource = source; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - output.textContent = `Mermaid render failed:\n${message}\n\n${source}`; - host.dataset.renderedSource = ""; - } - } -}; - -const scheduleRenderMermaidBlocks = () => { - if (mermaidRenderQueued) return; - mermaidRenderQueued = true; - queueMicrotask(async () => { - mermaidRenderQueued = false; - await renderMermaidBlocks(); - }); -}; - -const containsMermaidHost = (node) => { - if (!(node instanceof Element)) return false; - return ( - node.matches(".mermaid-host") || - node.querySelector(".mermaid-host") !== null - ); -}; - -const mermaidObserver = new MutationObserver((mutations) => { - for (const mutation of mutations) { - if (mutation.type !== "childList" || mutation.addedNodes.length === 0) { - continue; - } - - for (const node of mutation.addedNodes) { - if (containsMermaidHost(node)) { - scheduleRenderMermaidBlocks(); - return; - } - } - } -}); - -window.renderMermaidBlocks = renderMermaidBlocks; - main_$x_(); -mermaidObserver.observe(document.body, { childList: true, subtree: true }); -scheduleRenderMermaidBlocks(); if (import.meta.hot) { import.meta.hot.accept("./js-out/app.main.mjs", (main) => { main.reload_$x_(); - scheduleRenderMermaidBlocks(); }); } From cd3523e9643ddad6da28fd3e47360364710816e9 Mon Sep 17 00:00:00 2001 From: tiye Date: Tue, 26 May 2026 17:11:16 +0800 Subject: [PATCH 03/26] refine page styles, test complex charts --- Agents.md | 10 + RFCs/2026-05-mermaid-rendering-stability.md | 125 +++++++++ calcit.cirru | 287 ++++++++++++-------- deps.cirru | 2 +- 4 files changed, 304 insertions(+), 120 deletions(-) create mode 100644 RFCs/2026-05-mermaid-rendering-stability.md diff --git a/Agents.md b/Agents.md index dae95bc..c9cbede 100644 --- a/Agents.md +++ b/Agents.md @@ -107,9 +107,19 @@ genui 布局文件用 2 空格缩进(不要用 tab),示例: - 避免生成“可调用字符串”。错误写法如 `(<> ((str ...)))`,正确写法是 `<> $ str ...`。 - 变量必须保持 leaf。像 `week-start` 这种变量不要变成单元素 list,否则会被当成调用。 - Respo 样式里的纯数字会自动补 `px`。`flex`、`font-weight`、`line-height`、`z-index` 这类属性要显式写字符串,例如 `:flex "\"1"`。 +- 当前编译链里,空 map 字面量 `{}` 在生成的 JS 中可能落到 `$clt._$M_`,运行时是 `undefined`,不要拿它做 `defatom` 初始值后再 `assoc`。需要可写 map 时,先放一个占位键,例如 `{} (:_init_ true)`。 +- Respo 和命令式 DOM 更新会互相打架。像 Mermaid 这种库如果直接写 `innerHTML`/SVG,不要再给同一个容器挂虚拟 DOM children;优先只保留 `:innerHTML` prop。 +- Mermaid 的 `render` 需要并发保护。同一份 source 在一次渲染未完成前,要先写入 `:rendering` 之类的锁状态,避免重复调用同一个 graph id/source。 ## 修改约束 - 严禁直接手改 `calcit.cirru`,必须使用 `cr tree` 或 `cr edit`。 - 路径不要猜。先用 `cr query search` 拿路径,再用 `cr tree show` 确认。 - 静态样式优先抽到 `defstyle`,动态列表中尽量少写内联 `:style`。 + +## Mermaid/异步渲染补充约定 + +- 维护 Mermaid 节点时,优先检查三个面:Respo diff 是否会清空 DOM、缓存 atom 是否真的是 map、`render` 是否被重复触发。 +- `comp-mermaid-block` 当前约定是:`.mermaid-output` 只承载 `:innerHTML`,不再放 placeholder 子节点;占位文案也通过 `:innerHTML` 切换。 +- `render-mermaid-on` 当前约定是:先写缓存状态,再 `ensure-mermaid!`,再 `await mermaid.render(...)`,失败时把缓存写成 `false`,成功时写回 svg 字符串。 +- 浏览器联调时,先看 `chrome-devtools list_console_messages`,再查 `.mermaid-output.innerHTML` 是否真的变成 ` :rendering`。 + +## Root Causes + +### 1. Respo virtual DOM clears imperative SVG updates + +Mermaid 通过命令式方式写入 `output.innerHTML = svg`。如果同一个容器在 Respo 里还有普通 children,后续 reactive re-render 会继续做 child reconciliation,把已经写进去的 SVG 清掉。 + +结论:`.mermaid-output` 不能同时承载命令式 SVG 和普通虚拟 DOM children。 + +### 2. Empty Calcit map literal is not safe as a mutable atom base + +在当前编译链中,空 map 字面量 `{}` 会编译成 JS 侧的 `$clt._$M_`,而这个值在运行时是 `undefined`。 + +因此: + +- `defatom *rendered-svgs {}` 看起来像初始化为空 map。 +- 但后续执行 `assoc @*rendered-svgs source :rendering` 时,实际是在对 `nil` 做 `assoc`。 +- 这个异常发生在 async 函数里且早于 `await` 之后的错误处理,最终表现为 `Uncaught (in promise)`。 + +结论:不要把裸 `{}` 当作后续要 `assoc` 的 atom 初始值。 + +## Decision + +采用以下实现约定: + +1. 用缓存 atom 保存 Mermaid source 到渲染状态的映射。 +2. `.mermaid-output` 只用 `:innerHTML` 承载占位文案或最终 SVG,不再放任何虚拟 DOM children。 +3. 在真正调用 `mermaid.render` 前,先把对应 source 写成 `:rendering`,作为并发锁。 +4. atom 初始值使用带占位键的 map,例如 `{} (:_init_ true)`,确保运行时拿到的是可 `assoc` 的 `CalcitMap`。 + +## Implemented Shape + +关键实现形态如下: + +```cirru +defatom *rendered-svgs $ {} (:_init_ true) + +defcomp comp-mermaid-block (text) + let + payload $ build-mermaid-render-payload text + svg-str $ if (:empty? payload) nil + let + result $ get @*rendered-svgs (:source payload) + if (string? result) result nil + [] (effect-mermaid text) + div + {} (:class-name |mermaid-host) + div + {} + :class-name |mermaid-output + :innerHTML $ if (nil? svg-str) "|Rendering Mermaid diagram..." svg-str +``` + +```cirru +defn render-mermaid-on (el payload) + hint-fn $ {} (:async true) + let + source $ :source payload + graph-id $ :graph-id payload + output $ .!querySelector el |.mermaid-output + if (nil? output) (.!warn js/console "|[mermaid] missing .mermaid-output" el) + if (some? (get @*rendered-svgs source)) + .!debug js/console "|[mermaid] skip rendered" + do + reset! *rendered-svgs (assoc @*rendered-svgs source :rendering) + ensure-mermaid! + let + render-fn $ .-render mermaid-lib + try + let + result $ js-await (.!call render-fn mermaid-lib graph-id source) + svg $ .-svg result + do + set! (.-innerHTML output) svg + reset! *rendered-svgs (assoc @*rendered-svgs source svg) + fn (error) + do + reset! *rendered-svgs (assoc @*rendered-svgs source false) + .!error js/console "|[mermaid] render failed" error +``` + +## Verification + +本次修复后的验证信号: + +- `cr js` 编译通过,生成的 `js-out/app.comp.container.mjs` 中,`*rendered-svgs` 初始化为 `$clt._$n__$M_(_t_["_init_"], true)`,不再是 `$clt._$M_`。 +- 浏览器控制台没有新的 `Uncaught (in promise)`。 +- 控制台出现 `[mermaid] render` 调试日志。 +- 执行 `document.querySelector(".mermaid-output").innerHTML` 时,返回内容以 ` normalized-series .to-list - map $ fn (item) - {} - :name $ :label item - :value $ :value item + [] $ {} (:type |pie) + :data $ -> normalized-series .to-list + map $ fn (item) + {} + :name $ :label item + :value $ :value item |scatter $ merge base {} :xAxis $ {} (:type |category) (:data names) @@ -91,119 +90,165 @@ let store $ :store reel states $ :states store - ui $ or (:ui store) ({}) relay $ or (:relay store) ({}) renderer $ or (:renderer store) ({}) - sidebar-collapsed? $ not - = false $ :sidebar-collapsed? ui + drawer-open? $ get-in states ([] :drawer :data :show?) + drawer-plugin $ use-drawer (>> states :drawer) + {} + :style $ {} (:width 420) (:min-width 0) (:max-width "|calc(100vw - 20px)") (:padding "|14px 14px 18px") (:gap 14) (:background-color |#fffaf4) (:border-left "|1px solid #ead8c7") (:box-shadow "|-10px 0 30px hsla(24, 35%, 18%, 0.12)") (:overflow |auto) + :container-style $ if drawer-open? + {} (:position :fixed) (:top 0) (:right 0) (:bottom 0) (:left 0) (:z-index |40) + {} + :backdrop-style $ {} (:background-color "|hsla(28, 40%, 16%, 0.16)") (:backdrop-filter "|blur(10px)") + :render $ fn (on-close) + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 14) + div + {} $ :style + {} (:display |flex) (:justify-content |space-between) (:align-items |flex-start) (:gap 12) + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) + div + {} $ :style + {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) + <> "|Live session" + div + {} $ :style + {} (:font-size 22) (:font-weight |700) (:line-height |1.1) (:color |#2b2018) + <> "|Renderer diagnostics" + div + {} $ :style + {} (:font-size 13) (:line-height |1.6) (:color |#7b6451) + <> "|Inspect relay status, latest request ids, and the exact Cirru EDN payload being rendered." + button $ {} (:class-name css/button) (:inner-text |Close) + :style $ {} (:padding "|8px 12px") (:align-self |flex-start) + :on-click $ fn (e d!) (on-close d!) + div + {} $ :style + {} (:display |flex) (:gap 10) (:flex-wrap |wrap) + div + {} $ :style + {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) + <> $ str "|Relay: " + or (:status relay) |idle + div + {} $ :style + {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) + <> $ str "|Layout: " + or (:layout-id renderer) |waiting + if-let + request-id $ :last-request renderer + div + {} $ :style + {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) + <> $ str "|Request: " request-id + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 18) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") + div + {} $ :style + {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) + <> |Relay + div ({}) (<> "|Listening on relay channel genui.") + if-let + client-id $ :client-id relay + div ({}) + <> $ str "|Client: " client-id + if-let + relay-error $ :last-error relay + div + {} $ :style + {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) + <> $ str "|Relay error: " relay-error + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #ead8c7") + div + {} $ :style + {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) + <> "|Latest payload" + if-let + render-error $ :last-error renderer + div + {} $ :style + {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) + <> $ str "|Validation error: " render-error + textarea $ {} + :value $ or (:layout-source renderer) | + :read-only true + :spell-check false + :placeholder "|Incoming Cirru EDN layout payload will appear here." + :style $ {} (:width |100%) (:min-height |280px) (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #dcc8b6") (:background-color |#fff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) + when dev? $ comp-reel (>> states :reel) reel ({}) + help-alert $ use-alert (>> states :help) + {} $ :text "|Use the drawer to inspect relay status, request ids, and incoming Cirru EDN without reserving a permanent sidebar." div {} $ :style - {} (:min-height |100vh) (:padding 24) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) + {} (:min-height |100vh) (:padding 20) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) div {} $ :style - {} (:display |flex) (:gap 20) (:align-items |flex-start) + {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 12) (:padding "|12px 14px") (:border-radius 20) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:flex-wrap |wrap) div {} $ :style - {} - :width $ if sidebar-collapsed? |96px |320px - :display |flex - :flex-direction |column - :gap 12 - :padding 18 - :border-radius 20 - :background-color |#fffaf4 - :border "|1px solid #ead8c7" - :box-sizing |border-box - :flex-shrink |0 + {} (:display |flex) (:align-items |center) (:gap 10) (:flex-wrap |wrap) div {} $ :style - {} (:display |flex) (:align-items |center) (:justify-content |space-between) (:gap 8) - div - {} $ :style - {} - :font-size $ if sidebar-collapsed? 18 28 - :font-weight |700 - :line-height |1.1 - <> $ if sidebar-collapsed? |EDN "|EDN Renderer" - button $ {} - :inner-text $ if sidebar-collapsed? |>> |Collapse - :on-click $ fn (e d!) - d! $ :: :toggle-sidebar - :style $ {} - :padding $ if sidebar-collapsed? 8 10 - :border "|1px solid #d7bca4" - :background-color |#f7e4d0 - :color |#5d4028 - :border-radius 999 - :font-size 12 - :font-weight |600 - :cursor |pointer - if (not sidebar-collapsed?) - div - {} $ :style - {} (:font-size 13) (:line-height |1.6) (:color |#7e6650) - <> "|Listening on relay channel genui, validating incoming Cirru EDN layouts and rendering the accepted result." + {} (:font-size 22) (:font-weight |700) (:line-height |1.1) + <> "|EDN Renderer" div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 12) (:border-radius 14) (:background-color |#f4e7d8) - div ({}) - <> $ if sidebar-collapsed? - or (:status relay) |idle - str "|Relay status: " $ or (:status relay) |idle - if (not sidebar-collapsed?) - if-let - client-id $ :client-id relay - div ({}) - <> $ str "|Client: " client-id - if-let - relay-error $ :last-error relay - div - {} $ :style - {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) - <> $ if sidebar-collapsed? |ERR (str "|Relay error: " relay-error) + {} (:padding "|4px 10px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#8b6244) + <> "|Preview-first workspace" + div + {} $ :style + {} (:display |flex) (:gap 8) (:flex-wrap |wrap) (:align-items |center) (:justify-content |flex-end) div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) - div ({}) - <> $ if sidebar-collapsed? - or (:layout-id renderer) |waiting - str "|Layout id: " $ or (:layout-id renderer) |waiting - if (not sidebar-collapsed?) - if-let - request-id $ :last-request renderer - div ({}) - <> $ str "|Request: " request-id - if (not sidebar-collapsed?) - if-let - render-error $ :last-error renderer - div - {} $ :style - {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) - <> $ str "|Validation error: " render-error - if (not sidebar-collapsed?) - textarea $ {} - :value $ or (:layout-source renderer) | - :read-only true - :spell-check false - :placeholder "|Incoming Cirru EDN layout payload will appear here." - :style $ {} (:width |100%) (:min-height |260px) (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #dcc8b6") (:background-color |#fff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) - when - and dev? $ not sidebar-collapsed? - comp-reel (>> states :reel) reel $ {} + {} (:padding "|6px 10px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#6b4a32) + <> $ str "|Relay: " + or (:status relay) |idle + div + {} $ :style + {} (:padding "|6px 10px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#6b4a32) + <> $ if + some? $ :layout-id renderer + , "|Layout ready" "|Layout waiting" + button $ {} (:class-name css/button) (:inner-text "|Open drawer") + :style $ {} (:padding "|8px 12px") + :on-click $ fn (e d!) (.show drawer-plugin d!) + button $ {} (:class-name css/button) (:inner-text |Tips) + :style $ {} (:padding "|8px 12px") + :on-click $ fn (e d!) (.show help-alert d!) + if-let + relay-error $ :last-error relay + div + {} $ :style + {} (:padding "|12px 14px") (:border-radius 16) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 12) + <> $ str "|Relay error: " relay-error + if-let + render-error $ :last-error renderer div {} $ :style - {} (:flex |1) (:min-width |0) (:display |flex) (:flex-direction |column) (:gap 16) (:padding 22) (:border-radius 24) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") + {} (:padding "|12px 14px") (:border-radius 16) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 12) + <> $ str "|Validation error: " render-error + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 16) (:padding 22) (:border-radius 24) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") (:min-height |420px) (:margin-top 12) + div + {} $ :style + {} (:font-size 20) (:font-weight |600) + <> "|Rendered Preview" + if-let + layout $ :layout renderer + comp-layout-node layout div {} $ :style - {} (:font-size 20) (:font-weight |600) - <> "|Rendered Preview" - if-let - layout $ :layout renderer - comp-layout-node layout - div - {} $ :style - {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) - <> "|Waiting for a validated genui layout from the relay." + {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) + <> "|Waiting for a validated genui layout from the relay." + .render drawer-plugin + .render help-alert :examples $ [] |comp-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -340,11 +385,9 @@ {} $ :style {} (:font-size 13) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) <> |Mermaid - div - {} - :class-name |mermaid-output - :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) - :innerHTML $ if (nil? svg-str) "|Rendering Mermaid diagram..." svg-str + div $ {} (:class-name |mermaid-output) + :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) + :innerHTML $ if (nil? svg-str) "|Rendering Mermaid diagram..." svg-str :examples $ [] |effect-echarts $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -354,7 +397,11 @@ let existing $ echarts-lib/getInstanceByDom el chart $ if (nil? existing) (echarts-lib/init el) existing - do (.!debug js/console "|[echarts] render" option) (.!setOption chart option true) + plain-option $ to-js-data option + do (.!debug js/console "|[echarts] render/raw" option) (.!log js/console "|[echarts] render/plain" plain-option) + .!log js/console "|[echarts] render/plain-json" $ js/JSON.stringify plain-option nil 2 + .!log js/console "|[echarts] host-size" (.-clientWidth el) (.-clientHeight el) + .!setOption chart plain-option true dispose-chart $ fn () let chart $ echarts-lib/getInstanceByDom el @@ -371,9 +418,13 @@ payload $ build-mermaid-render-payload text case-default action nil :mount $ do (.!debug js/console "|[mermaid] mount" payload) - when (not (:empty? payload)) (render-mermaid-on el payload) + when + not $ :empty? payload + render-mermaid-on el payload :update $ do (.!debug js/console "|[mermaid] update" payload) - when (not (:empty? payload)) (render-mermaid-on el payload) + when + not $ :empty? payload + render-mermaid-on el payload :unmount $ .!debug js/console "|[mermaid] unmount" :examples $ [] |ensure-mermaid! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -485,10 +536,11 @@ graph-id $ :graph-id payload output $ .!querySelector el |.mermaid-output if (nil? output) (.!warn js/console "|[mermaid] missing .mermaid-output" el) - if (some? (get @*rendered-svgs source)) + if + some? $ get @*rendered-svgs source .!debug js/console "|[mermaid] skip rendered" do - reset! *rendered-svgs (assoc @*rendered-svgs source :rendering) + reset! *rendered-svgs $ assoc @*rendered-svgs source :rendering ensure-mermaid! let render-fn $ .-render mermaid-lib @@ -500,13 +552,13 @@ do set! (.-innerHTML output) svg when (some? bind-fns) (bind-fns output) - reset! *rendered-svgs (assoc @*rendered-svgs source svg) + reset! *rendered-svgs $ assoc @*rendered-svgs source svg .!debug js/console "|[mermaid] render" $ js-object :length $ count source :graphId graph-id fn (error) do - reset! *rendered-svgs (assoc @*rendered-svgs source false) + reset! *rendered-svgs $ assoc @*rendered-svgs source false .!error js/console "|[mermaid] render failed" error :examples $ [] |validate-layout $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -527,6 +579,7 @@ app.config :refer $ dev? |echarts :as echarts-lib |mermaid :default mermaid-lib + respo-alerts.core :refer $ use-alert use-drawer |app.config $ %{} :FileEntry :defs $ {} |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -682,7 +735,6 @@ def store $ {} :states $ {} :cursor $ [] - :ui $ {} (:sidebar-collapsed? true) :relay $ {} (:status |idle) (:client-id nil) (:last-error nil) :renderer $ {} (:layout nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) :examples $ [] @@ -696,9 +748,6 @@ tag-match op (:states cursor s) (update-states store cursor s) (:hydrate-storage data) data - (:toggle-sidebar) - assoc-in store ([] :ui :sidebar-collapsed?) - not $ get-in store ([] :ui :sidebar-collapsed?) (:relay-connected client-id) -> store assoc-in ([] :relay :status) |ready diff --git a/deps.cirru b/deps.cirru index 46dde8d..d1d3aa7 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,6 +1,6 @@ {} (:calcit-version |0.12.35) - :dependencies $ {} (|Respo/reel.calcit |0.6.3) + :dependencies $ {} (|Respo/alerts.calcit |0.10.10) (|Respo/reel.calcit |0.6.3) |Respo/respo-ui.calcit |0.6.4 |Respo/respo.calcit |0.16.43 |calcit-lang/memof |0.0.23 From 7300c5e91b7be27ba2400eef6e7d2e4369817de3 Mon Sep 17 00:00:00 2001 From: tiye Date: Tue, 26 May 2026 20:04:44 +0800 Subject: [PATCH 04/26] provide skill information for cli to explore --- Agents.md | 3 + SKILL.md | 410 +++++++++++++++++++++++++++++++++++++++++++++++++++ calcit.cirru | 161 +++++++++++++++++++- 3 files changed, 571 insertions(+), 3 deletions(-) create mode 100644 SKILL.md diff --git a/Agents.md b/Agents.md index c9cbede..bd0c699 100644 --- a/Agents.md +++ b/Agents.md @@ -116,6 +116,9 @@ genui 布局文件用 2 空格缩进(不要用 tab),示例: - 严禁直接手改 `calcit.cirru`,必须使用 `cr tree` 或 `cr edit`。 - 路径不要猜。先用 `cr query search` 拿路径,再用 `cr tree show` 确认。 - 静态样式优先抽到 `defstyle`,动态列表中尽量少写内联 `:style`。 +- `SKILL.md` 属于对外暴露文档;如果需要在页面运行时提供 `skill`,应通过 macro 把文本加载进 renderer,而不是让外部直接读仓库文件。 +- 组件说明要用可检索的数据结构维护,方便 renderer 按名称或批量返回组件帮助。 +- 面向 CLI 的帮助信息优先通过 renderer 协议暴露;实现细节不要堆回 `SKILL.md`。 ## Mermaid/异步渲染补充约定 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..56ee5d5 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,410 @@ +# `edn-renderer` Skill + +这是一份给其他项目复用的操作手册,目标是统一 `edn-relay` + `edn-renderer` 这一套 CLI 到浏览器的工作流。 + +这份文档不只是在描述当前项目怎么跑,也在明确一组应该长期保持稳定的使用约定,方便别的项目直接引用。 + +--- + +## 1. 目标 + +`edn-renderer` 的角色,是把 Cirru EDN 布局 payload 渲染到浏览器里,并把运行时结果、文档能力、调试信息暴露给 CLI。 + +典型循环是: + +1. 启动 relay +2. 启动 renderer 页面 +3. 用 CLI 向当前 renderer 发送 payload +4. 在浏览器里看渲染结果 +5. 在 CLI 里查询帮助、技能文档、组件说明 + +--- + +## 2. 统一约定 + +这份 skill 采用下面这些约定,后续其他项目也建议遵守: + +### 2.1 relay 一次只服务一个当前目标 + +不再要求命令行里反复指定: + +- 是哪个服务 +- 连接哪个端口 +- websocket 地址是什么 + +使用层面的约定应该是: + +- 一次只启动一个当前 relay 服务 +- CLI 默认只连接这个当前 relay +- 业务命令默认都发给这个当前 relay + +也就是说,使用时优先是这种形式: + +- `edn-relay serve` +- `edn-relay genui ''` +- `edn-relay help` +- `edn-relay skill` +- `edn-relay status` +- `edn-relay current` +- `edn-relay open` + +而不是每次都写: + +- `--server ...` +- `--bind ...` +- `ws://127.0.0.1:xxxx` + +### 2.2 统一持久化状态 + +relay 需要维护一份持久化状态,用来记住当前连接上下文。 + +这类状态包括但不限于: + +- 当前 relay 连接信息 +- 当前目标 renderer 信息 +- 默认 channel +- 最近一次使用的上下文 +- 文档缓存 / skill 缓存 + +具体存储位置和实现方式属于 relay 内部实现细节,不应该要求 CLI 使用者记住。 + +### 2.3 文档通过协议查询,不靠手工记忆 + +具体业务能力,不应该靠用户记命令细节,而应该通过: + +- `edn-relay help` + +让 relay 按协议向当前 renderer 查询文档,再决定怎么使用。 + +也就是说: + +1. 先运行 `edn-relay help` +2. 由 relay 从 renderer 侧拉取可用文档 +3. 再根据返回的文档去发 payload 或查询具体组件 + +这样做的好处是: + +- CLI 和 renderer 的能力不会脱节 +- renderer 新增组件后,CLI 能自动发现 +- 其他项目接入时,不需要额外抄一份命令说明 + +### 2.4 skill 也要通过 renderer 暴露 + +`SKILL.md` 不应只是仓库里的静态说明文件。 + +约定上,renderer 应该在运行时把这份 skill 暴露给: + +- `edn-relay skill` + +也就是说,后续的使用习惯应该是: + +1. `edn-relay skill` +2. relay 从当前 renderer 拿到 skill 内容 +3. CLI 侧展示或检索 skill + +这样其他项目不必直接读取仓库文件,也能通过运行中的 renderer 获取最新 skill。 + +### 2.5 组件说明要支持检索 + +组件文档不要只写成一长段不可检索的文本。 + +更推荐的约定是: + +- 支持按名称查询 +- 支持批量列出组件 +- 支持同时返回参数、说明、示例 + +例如概念上,应该能支持这类查询: + +- 查询全部组件 +- 查询单个组件,比如 `chart` +- 批量查询多个组件,比如 `chart`、`mermaid`、`markdown` + +这样 `edn-relay help` 才能真正成为一个可检索接口,而不只是打印一段静态帮助。 + +--- + +## 3. 推荐使用方式 + +### 3.1 启动 relay + +约定上,CLI 层应该尽量简化成: + +```bash +edn-relay serve +``` + +是否监听本地某个端口、如何绑定、如何记住状态,都应该由 relay 内部处理,而不是由用户每次手工指定。 + +### 3.2 启动 renderer + +在 renderer 项目里,开发时仍然需要把页面跑起来。 + +典型本地命令仍然是: + +```bash +cr js +yarn vite +``` + +这里不强调具体端口号,重点是: + +- renderer 页面能打开 +- renderer 能连接当前 relay +- renderer 能通过协议提供文档、skill、组件说明和渲染能力 + +### 3.3 打开浏览器页面 + +推荐继续使用隔离浏览器和 `chrome-devtools`: + +```bash +chrome-devtools status +chrome-devtools start --headless false +chrome-devtools new_page '' +chrome-devtools take_snapshot +chrome-devtools list_console_messages +``` + +这里的页面地址属于开发环境细节,不应该成为 CLI 使用者每次都要记住的协议参数。 + +--- + +## 4. 用 CLI 试用 renderer 的方式 + +### 4.1 最小发送方式 + +面向使用者时,更推荐展示这种形式: + +```bash +edn-relay genui ' +{} + :type |card + :text "|CLI Demo" + :children $ [] + {} (:type |badge) (:text |preview) + {} (:type |divider) + {} (:type |text) (:text "|Hello from CLI") +' +``` + +重点是: + +- 不再反复出现 `--server` +- 不再要求用户知道 websocket 地址 +- 当前 relay 目标由 relay 自己维护的持久化上下文决定 + +### 4.2 先问 help,再发业务 payload + +推荐流程不是“先猜功能”,而是: + +1. 运行 `edn-relay help` +2. 从 renderer 返回的文档里看当前支持什么 +3. 再运行 `edn-relay genui ...` + +如果要查询 skill,则走: + +1. 运行 `edn-relay skill` +2. 从 renderer 返回 skill 内容 +3. 再根据 skill 决定怎么调试、怎么探索能力 + +如果要查看当前 renderer 页面状态,则可以: + +1. 运行 `edn-relay status` +2. 查看当前 renderer 标题、页面地址和可用命令 +3. 需要直接打开页面时再运行 `edn-relay open` + +--- + +## 5. 如何发现 renderer 当前支持哪些功能 + +### 5.1 首选 `edn-relay help` + +后续面向使用者的统一入口,应该是: + +```bash +edn-relay help +``` + +这条命令的职责应该是: + +- 从当前 renderer 查询可用文档 +- 列出可用能力 +- 提示支持的组件和业务入口 +- 给出下一步可执行的命令建议 + +已经适合直接使用的形式包括: + +- `edn-relay help` +- `edn-relay help protocol` +- `edn-relay help examples` +- `edn-relay help chart mermaid` +- `edn-relay help card-demo` + +### 5.2 组件级帮助应该可检索 + +对于组件说明,renderer 侧应该提供可检索的组件帮助接口。 + +目前推荐按下面几类来查: + +- 组件:例如 `chart`、`mermaid` +- 协议:例如 `protocol` +- 示例:例如 `examples`、`card-demo` + +比如概念上应该支持: + +- 列出全部组件 +- 查询指定组件的说明 +- 批量查询多个组件 +- 返回示例 payload + +这类能力应该从 renderer 侧通过协议暴露出来,而不是让调用方直接读仓库源文件。 + +### 5.3 skill 作为更高层的操作指南 + +`edn-relay help` 偏向能力目录。 + +`edn-relay skill` 偏向操作经验和工作流。 + +两者的分工建议是: + +- `help`:查“有哪些能力” +- `skill`:查“怎么用这套能力干活” + +--- + +## 6. 在开发 renderer 本身时,仍然可以这样查实现 + +虽然面向使用者的推荐入口是 `edn-relay help` / `edn-relay skill`,但开发 renderer 自己时,仍然可以直接查代码。 + +推荐顺序: + +1. `README.md` +2. `COMPONENTS.md` +3. `Agents.md` +4. `cr query` 直接查定义 + +例如: + +```bash +cr query def app.comp.container/LayoutNode +cr query def app.comp.container/validate-layout-node +cr query def app.comp.container/comp-layout-node +cr query search 'mermaid' +cr query search 'chart' +cr query search 'genui' +``` + +这些命令适合开发者,不适合最终 CLI 使用者。最终 CLI 使用者更应该通过协议化 help 来发现功能。 + +--- + +## 7. 浏览器侧如何验证 + +虽然帮助和 skill 未来会从 renderer 协议暴露出来,但浏览器验证仍然很重要。 + +推荐命令: + +```bash +chrome-devtools take_snapshot +chrome-devtools list_console_messages +chrome-devtools take_screenshot --fullPage --filePath artifacts/debug.png +``` + +经验上: + +- `snapshot` 适合看结构和可访问树 +- `screenshot` 适合看视觉结果 +- `list_console_messages` 适合看图表、mermaid、overlay、事件问题 + +--- + +## 8. 常见问题排查思路 + +### 8.1 relay 侧问题 + +现象: + +- renderer 显示未连接 +- CLI 超时等不到 ack +- help / skill 查不出来内容 + +优先检查: + +- relay 是否已启动 +- relay 的持久化上下文是否已经正确写入 +- renderer 是否成功连接当前 relay + +### 8.2 文档 / skill 发现问题 + +如果 `edn-relay help` 或 `edn-relay skill` 没有返回预期内容,优先怀疑: + +- renderer 侧没有把文档通过协议暴露出去 +- skill 没有被 renderer 在运行时正确暴露 +- 组件说明没有以可检索方式对外提供 + +### 8.3 chart 空白 + +这个项目已经验证过一个关键经验: + +**图表库最终拿到的必须是原生 JS object,而不是原始 Calcit map。** + +所以如果图表区域是空的,先查: + +- 传给图表库的最终对象结构 +- host 尺寸是不是正常 +- 是否实际生成 canvas + +### 8.4 drawer / modal 点不动 + +如果按钮表面上能点,但没反应,优先查是不是有透明 overlay 盖住了页面。 + +一个简单探针是检查按钮中心点真正命中的元素是不是按钮本身。 + +--- + +## 9. 在其他项目里怎么复用这份 skill + +如果别的项目想复用 `edn-renderer` 这套能力,建议直接沿用下面的接口习惯: + +1. `edn-relay serve` 启动当前 relay +2. `edn-relay current` 查看当前 relay 上下文 +3. `edn-relay status` 查询当前 renderer 状态 +4. `edn-relay help` 查询当前 renderer 暴露的文档 +5. `edn-relay skill` 查询当前 renderer 暴露的操作技能 +6. `edn-relay genui ...` 发送实际 payload +7. 浏览器里用 drawer 和 console 做细节验证 + +也就是说,别的项目最好复用的是: + +- 单实例 relay 的交互模型 +- relay 的持久化上下文模型 +- renderer 侧帮助文档 / skill 的协议暴露方式 +- 组件文档的可检索暴露方式 + +--- + +## 10. 建议沉淀成长期接口的内容 + +为了方便长期复用,建议把下面这些能力稳定下来: + +- `edn-relay serve` +- `edn-relay current` +- `edn-relay status` +- `edn-relay open` +- `edn-relay help` +- `edn-relay skill` +- `edn-relay genui` +- relay 的持久化上下文机制 +- renderer 侧的文档查询协议 +- renderer 侧的 skill 查询协议 +- 组件说明的可检索暴露结构 + +这样后续无论换项目还是换 renderer,只要遵守同一套约定,CLI 使用方式就能保持一致。 + +--- + +## 11. 在其他项目中的引用建议 + +你可以在其他项目里这样引用这份文档: + +> 关于 `edn-relay` 与 `edn-renderer` 的统一 CLI / help / skill 工作流,请参考 `edn-renderer/SKILL.md`。 diff --git a/calcit.cirru b/calcit.cirru index 034e333..45f9861 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -582,13 +582,142 @@ respo-alerts.core :refer $ use-alert use-drawer |app.config $ %{} :FileEntry :defs $ {} + |build-help-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-help-payload (topics) + let + normalized-topics $ if (list? topics) topics ([]) + {} (:status |ok) (:kind |help) (:renderer |edn-renderer) (:summary renderer-help-overview) (:commands relay-commands) (:topics normalized-topics) + :components $ select-component-docs normalized-topics + :protocol_docs $ select-protocol-docs normalized-topics + :examples $ select-example-docs normalized-topics + :examples $ [] + |build-skill-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-skill-payload () $ {} (:status |ok) (:kind |skill) (:renderer |edn-renderer) (:title "|edn-renderer Skill") (:text skill-text) + :examples $ [] + |build-status-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-status-payload () $ {} (:status |ok) (:kind |status) (:renderer |edn-renderer) (:title current-page-title) (:page_url current-page-url) (:commands relay-commands) + :examples $ [] + |component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def component-docs $ [] + {} (:name |column) (:summary "|纵向容器,使用 `:children` 顺序渲染子节点。") + :fields $ [] |children + :example "|{}\n :type |column\n :children $ []\n {} (:type |text) (:text \"|Hello\")" + {} (:name |row) (:summary "|横向容器,使用 `:children` 横向排列子节点。") + :fields $ [] |children + :example "|{}\n :type |row\n :children $ []\n {} (:type |badge) (:text |A)\n {} (:type |badge) (:text |B)" + {} (:name |card) (:summary "|带标题的内容容器,常用于组合多个子节点。") + :fields $ [] |text |children + :example "|{}\n :type |card\n :text \"|Title\"\n :children $ []\n {} (:type |text) (:text \"|Body\")" + {} (:name |text) (:summary "|普通文本节点。") + :fields $ [] |text + :example "|{} (:type |text) (:text \"|Hello\")" + {} (:name |badge) (:summary "|紧凑状态标签。") + :fields $ [] |text + :example "|{} (:type |badge) (:text |preview)" + {} (:name |divider) (:summary "|水平分隔线。") + :fields $ [] + :example "|{} (:type |divider)" + {} (:name |markdown) (:summary "|Markdown 富文本块。") + :fields $ [] |text + :example "|{} (:type |markdown) (:text \"|## Title\")" + {} (:name |mermaid) (:summary "|Mermaid 图节点,` :text ` 为 Mermaid DSL。") + :fields $ [] |text + :example "|{} (:type |mermaid) (:text \"|flowchart LR\\n A --> B\")" + {} (:name |chart) (:summary "|ECharts 图表节点,支持 `bar`/`line`/`pie`/`scatter`。") + :fields $ [] |kind |title |series + :example "|{}\n :type |chart\n :kind |line\n :title \"|Traffic\"\n :series $ []\n {} (:label |Mon) (:value 120)" + {} (:name |button) (:summary "|只读展示按钮。") + :fields $ [] |text + :example "|{} (:type |button) (:text |Confirm)" + {} (:name |input) (:summary "|只读展示输入框。") + :fields $ [] |name |placeholder |text + :example "|{} (:type |input) (:name |email) (:placeholder |Email)" + :examples $ [] + |current-page-title $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def current-page-title $ .-title js/document + :examples $ [] + |current-page-url $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def current-page-url $ .-href js/location + :examples $ [] |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def dev? $ = |dev (get-env |mode |release) :examples $ [] + |example-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def example-docs $ [] + {} (:name |card-demo) (:summary "|最小 card 示例。") (:payload "|{}\n :type |card\n :text \"|CLI Demo\"\n :children $ []\n {} (:type |badge) (:text |preview)\n {} (:type |text) (:text \"|Hello from CLI\")") + {} (:name |chart-demo) (:summary "|折线图示例。") (:payload "|{}\n :type |chart\n :kind |line\n :title \"|Traffic trend\"\n :series $ []\n {} (:label |Mon) (:value 120)\n {} (:label |Tue) (:value 132)\n {} (:label |Wed) (:value 148)") + {} (:name |mermaid-demo) (:summary "|Mermaid 流程图示例。") (:payload "|{}\n :type |mermaid\n :text \"|flowchart LR\\n A --> B\\n B --> C\"") + :examples $ [] + |protocol-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def protocol-docs $ [] + {} (:name |genui) (:summary "|`genui` 频道用于发送布局树,renderer 校验并渲染后回 ack。") + {} (:name |renderer) (:summary "|`renderer` 频道用于查询帮助文档、skill 和状态信息。") + {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,并订阅 `genui`/`renderer` 频道。") + {} (:name |ack) (:summary "|CLI 请求最终通过 `ack` 拿到 renderer 的处理结果。") + :examples $ [] + |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def relay-commands $ [] |genui |help |skill |status |current |open + :examples $ [] + |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def renderer-help-overview "|使用 `edn-relay help` 查询当前 renderer 支持的能力;可以用 `edn-relay help protocol` 看协议摘要,用 `edn-relay help examples` 看示例,用 `edn-relay help chart mermaid` 过滤组件帮助。") + :examples $ [] + |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn select-component-docs (topics) + let + normalized-topics $ if (list? topics) topics ([]) + if (empty? normalized-topics) component-docs $ foldl component-docs ([]) + fn (acc item) + if + includes? normalized-topics $ :name item + append acc item + , acc + :examples $ [] + |select-example-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn select-example-docs (topics) + let + normalized-topics $ if (list? topics) topics ([]) + if (includes? normalized-topics |examples) example-docs $ foldl example-docs ([]) + fn (acc item) + if + includes? normalized-topics $ :name item + append acc item + , acc + :examples $ [] + |select-protocol-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn select-protocol-docs (topics) + let + normalized-topics $ if (list? topics) topics ([]) + if (includes? normalized-topics |protocol) protocol-docs $ foldl protocol-docs ([]) + fn (acc item) + if + includes? normalized-topics $ :name item + append acc item + , acc + :examples $ [] |site $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) (:relay-channel |genui) + def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) (:relay-channel |genui) (:relay-docs-channel |renderer) + :examples $ [] + |skill-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def skill-text $ slurp-file |SKILL.md + :examples $ [] + |slurp-file $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defmacro slurp-file (file-path) (read-file file-path) :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.config) @@ -620,7 +749,7 @@ reset! *ws ws .!addEventListener ws |open $ fn (event) send-relay-frame! ws $ {} (:kind |hello) (:role |browser) (:client_id client-id) - :channels $ [] (:relay-channel config/site) + :channels $ [] (:relay-channel config/site) (:relay-docs-channel config/site) .!addEventListener ws |message $ fn (event) handle-relay-message! ws $ .-data event .!addEventListener ws |error $ fn (event) @@ -655,12 +784,38 @@ if = (:channel frame) (:relay-channel config/site) handle-genui-event! ws frame - do |ignored + if + = (:channel frame) (:relay-docs-channel config/site) + handle-renderer-event! ws frame + do |ignored if (= kind |error) dispatch! $ :: :relay-status |error or (:error frame) "|Relay error" do |ignored :examples $ [] + |handle-renderer-event! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn handle-renderer-event! (ws frame) + let + request-id $ :id frame + payload $ :payload frame + op $ if (map? payload) (:op payload) nil + topics $ if + and (map? payload) + list? $ :topics payload + :topics payload + [] + do (.!debug js/console "|[renderer] request" payload) + if (= op |help) + let + response-payload $ config/build-help-payload topics + send-genui-ack! ws request-id true response-payload nil + if (= op |skill) + send-genui-ack! ws request-id true (config/build-skill-payload) nil + if (= op |status) + send-genui-ack! ws request-id true (config/build-status-payload) nil + send-genui-ack! ws request-id false nil $ str "|Unsupported renderer op: " op + :examples $ [] |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn main! () From f8df43a0f3c46b011963cf2c304e03910aabc22f Mon Sep 17 00:00:00 2001 From: tiye Date: Wed, 27 May 2026 01:26:12 +0800 Subject: [PATCH 05/26] multi channel refactoring --- .gitignore | 2 + .pnp.cjs | 10415 ----------------------- .pnp.loader.mjs | 2126 ----- .yarnrc.yml | 2 +- artifacts/edn-renderer-genui-final.png | Bin 283243 -> 0 bytes calcit.cirru | 208 +- 6 files changed, 170 insertions(+), 12583 deletions(-) delete mode 100755 .pnp.cjs delete mode 100644 .pnp.loader.mjs delete mode 100644 artifacts/edn-renderer-genui-final.png diff --git a/.gitignore b/.gitignore index 2c9c1f2..63864c7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ calcit.cirru .yarn/ .copilot-tmp/ + +artifacts/* diff --git a/.pnp.cjs b/.pnp.cjs deleted file mode 100755 index 4dbea80..0000000 --- a/.pnp.cjs +++ /dev/null @@ -1,10415 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable */ -// @ts-nocheck -"use strict"; - -const RAW_RUNTIME_STATE = -'{\ - "__info": [\ - "This file is automatically generated. Do not touch it, or risk",\ - "your modifications being lost."\ - ],\ - "dependencyTreeRoots": [\ - {\ - "name": "root-workspace-0b6124",\ - "reference": "workspace:."\ - }\ - ],\ - "enableTopLevelFallback": true,\ - "ignorePatternData": "(^(?:\\\\.yarn\\\\/sdks(?:\\\\/(?!\\\\.{1,2}(?:\\\\/|$))(?:(?:(?!(?:^|\\\\/)\\\\.{1,2}(?:\\\\/|$)).)*?)|$))$)",\ - "pnpZipBackend": "libzip",\ - "fallbackExclusionList": [\ - ["root-workspace-0b6124", ["workspace:."]]\ - ],\ - "fallbackPool": [\ - ],\ - "packageRegistryData": [\ - [null, [\ - [null, {\ - "packageLocation": "./",\ - "packageDependencies": [\ - ["@calcit/procs", "npm:0.12.35"],\ - ["bottom-tip", "npm:0.1.5"],\ - ["echarts", "npm:6.1.0"],\ - ["mermaid", "npm:11.15.0"],\ - ["root-workspace-0b6124", "workspace:."],\ - ["vite", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12"]\ - ],\ - "linkType": "SOFT"\ - }]\ - ]],\ - ["@antfu/install-pkg", [\ - ["npm:1.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@antfu-install-pkg-npm-1.1.0-9790551649-10c0.zip/node_modules/@antfu/install-pkg/",\ - "packageDependencies": [\ - ["@antfu/install-pkg", "npm:1.1.0"],\ - ["package-manager-detector", "npm:1.6.0"],\ - ["tinyexec", "npm:1.2.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@braintree/sanitize-url", [\ - ["npm:7.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@braintree-sanitize-url-npm-7.1.2-18120404e9-10c0.zip/node_modules/@braintree/sanitize-url/",\ - "packageDependencies": [\ - ["@braintree/sanitize-url", "npm:7.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@calcit/procs", [\ - ["npm:0.12.35", {\ - "packageLocation": "../../../.yarn/berry/cache/@calcit-procs-npm-0.12.35-7e735d1921-10c0.zip/node_modules/@calcit/procs/",\ - "packageDependencies": [\ - ["@calcit/procs", "npm:0.12.35"],\ - ["@calcit/ternary-tree", "npm:0.0.26"],\ - ["@cirru/parser.ts", "npm:0.0.9"],\ - ["@cirru/writer.ts", "npm:0.1.9"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@calcit/ternary-tree", [\ - ["npm:0.0.26", {\ - "packageLocation": "../../../.yarn/berry/cache/@calcit-ternary-tree-npm-0.0.26-842160961c-10c0.zip/node_modules/@calcit/ternary-tree/",\ - "packageDependencies": [\ - ["@calcit/ternary-tree", "npm:0.0.26"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@chevrotain/types", [\ - ["npm:11.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@chevrotain-types-npm-11.1.2-ac6f62fb22-10c0.zip/node_modules/@chevrotain/types/",\ - "packageDependencies": [\ - ["@chevrotain/types", "npm:11.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@cirru/parser.ts", [\ - ["npm:0.0.9", {\ - "packageLocation": "../../../.yarn/berry/cache/@cirru-parser.ts-npm-0.0.9-d7e6fe396b-10c0.zip/node_modules/@cirru/parser.ts/",\ - "packageDependencies": [\ - ["@cirru/parser.ts", "npm:0.0.9"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@cirru/writer.ts", [\ - ["npm:0.1.9", {\ - "packageLocation": "../../../.yarn/berry/cache/@cirru-writer.ts-npm-0.1.9-6e0d3d3bb3-10c0.zip/node_modules/@cirru/writer.ts/",\ - "packageDependencies": [\ - ["@cirru/writer.ts", "npm:0.1.9"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@emnapi/core", [\ - ["npm:1.10.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@emnapi-core-npm-1.10.0-e5e48f37ba-10c0.zip/node_modules/@emnapi/core/",\ - "packageDependencies": [\ - ["@emnapi/core", "npm:1.10.0"],\ - ["@emnapi/wasi-threads", "npm:1.2.1"],\ - ["tslib", "npm:2.8.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@emnapi/runtime", [\ - ["npm:1.10.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@emnapi-runtime-npm-1.10.0-4648518988-10c0.zip/node_modules/@emnapi/runtime/",\ - "packageDependencies": [\ - ["@emnapi/runtime", "npm:1.10.0"],\ - ["tslib", "npm:2.8.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@emnapi/wasi-threads", [\ - ["npm:1.2.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@emnapi-wasi-threads-npm-1.2.1-8626cfd5d3-10c0.zip/node_modules/@emnapi/wasi-threads/",\ - "packageDependencies": [\ - ["@emnapi/wasi-threads", "npm:1.2.1"],\ - ["tslib", "npm:2.8.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@iconify/types", [\ - ["npm:2.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@iconify-types-npm-2.0.0-faa2398199-10c0.zip/node_modules/@iconify/types/",\ - "packageDependencies": [\ - ["@iconify/types", "npm:2.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@iconify/utils", [\ - ["npm:3.1.3", {\ - "packageLocation": "../../../.yarn/berry/cache/@iconify-utils-npm-3.1.3-3c97794010-10c0.zip/node_modules/@iconify/utils/",\ - "packageDependencies": [\ - ["@antfu/install-pkg", "npm:1.1.0"],\ - ["@iconify/types", "npm:2.0.0"],\ - ["@iconify/utils", "npm:3.1.3"],\ - ["import-meta-resolve", "npm:4.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@isaacs/balanced-match", [\ - ["npm:4.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@isaacs-balanced-match-npm-4.0.1-8965afafe6-10c0.zip/node_modules/@isaacs/balanced-match/",\ - "packageDependencies": [\ - ["@isaacs/balanced-match", "npm:4.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@isaacs/brace-expansion", [\ - ["npm:5.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@isaacs-brace-expansion-npm-5.0.1-79f48d6db7-10c0.zip/node_modules/@isaacs/brace-expansion/",\ - "packageDependencies": [\ - ["@isaacs/balanced-match", "npm:4.0.1"],\ - ["@isaacs/brace-expansion", "npm:5.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@isaacs/fs-minipass", [\ - ["npm:4.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@isaacs-fs-minipass-npm-4.0.1-677026e841-10c0.zip/node_modules/@isaacs/fs-minipass/",\ - "packageDependencies": [\ - ["@isaacs/fs-minipass", "npm:4.0.1"],\ - ["minipass", "npm:7.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@mermaid-js/parser", [\ - ["npm:1.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@mermaid-js-parser-npm-1.1.1-eec28c7bd5-10c0.zip/node_modules/@mermaid-js/parser/",\ - "packageDependencies": [\ - ["@chevrotain/types", "npm:11.1.2"],\ - ["@mermaid-js/parser", "npm:1.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@napi-rs/wasm-runtime", [\ - ["npm:1.1.4", {\ - "packageLocation": "../../../.yarn/berry/cache/@napi-rs-wasm-runtime-npm-1.1.4-53a1e4ec11-10c0.zip/node_modules/@napi-rs/wasm-runtime/",\ - "packageDependencies": [\ - ["@napi-rs/wasm-runtime", "npm:1.1.4"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:781c306be22d98f8cc4b851223a48ae26b9ef0afd427bbc3c6120e567a6ec220663940cfffd3a08c9aae40a84cefcdf11d1f07062c8c4cd6be6e1c75706cb2e4#npm:1.1.4", {\ - "packageLocation": "./.yarn/__virtual__/@napi-rs-wasm-runtime-virtual-c08b2480ff/4/.yarn/berry/cache/@napi-rs-wasm-runtime-npm-1.1.4-53a1e4ec11-10c0.zip/node_modules/@napi-rs/wasm-runtime/",\ - "packageDependencies": [\ - ["@emnapi/core", "npm:1.10.0"],\ - ["@emnapi/runtime", "npm:1.10.0"],\ - ["@napi-rs/wasm-runtime", "virtual:781c306be22d98f8cc4b851223a48ae26b9ef0afd427bbc3c6120e567a6ec220663940cfffd3a08c9aae40a84cefcdf11d1f07062c8c4cd6be6e1c75706cb2e4#npm:1.1.4"],\ - ["@tybys/wasm-util", "npm:0.10.2"],\ - ["@types/emnapi__core", null],\ - ["@types/emnapi__runtime", null]\ - ],\ - "packagePeers": [\ - "@emnapi/core",\ - "@emnapi/runtime",\ - "@types/emnapi__core",\ - "@types/emnapi__runtime"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@npmcli/agent", [\ - ["npm:4.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@npmcli-agent-npm-4.0.0-502e5ae4f0-10c0.zip/node_modules/@npmcli/agent/",\ - "packageDependencies": [\ - ["@npmcli/agent", "npm:4.0.0"],\ - ["agent-base", "npm:7.1.4"],\ - ["http-proxy-agent", "npm:7.0.2"],\ - ["https-proxy-agent", "npm:7.0.6"],\ - ["lru-cache", "npm:11.2.5"],\ - ["socks-proxy-agent", "npm:8.0.5"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@npmcli/fs", [\ - ["npm:5.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@npmcli-fs-npm-5.0.0-9d737ae2f3-10c0.zip/node_modules/@npmcli/fs/",\ - "packageDependencies": [\ - ["@npmcli/fs", "npm:5.0.0"],\ - ["semver", "npm:7.7.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@oxc-project/types", [\ - ["npm:0.129.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@oxc-project-types-npm-0.129.0-b4f236a01a-10c0.zip/node_modules/@oxc-project/types/",\ - "packageDependencies": [\ - ["@oxc-project/types", "npm:0.129.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-android-arm64", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-android-arm64-npm-1.0.0-def8c9d3d7/node_modules/@rolldown/binding-android-arm64/",\ - "packageDependencies": [\ - ["@rolldown/binding-android-arm64", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-darwin-arm64", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-darwin-arm64-npm-1.0.0-d638c85202/node_modules/@rolldown/binding-darwin-arm64/",\ - "packageDependencies": [\ - ["@rolldown/binding-darwin-arm64", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-darwin-x64", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-darwin-x64-npm-1.0.0-030b87b50a/node_modules/@rolldown/binding-darwin-x64/",\ - "packageDependencies": [\ - ["@rolldown/binding-darwin-x64", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-freebsd-x64", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-freebsd-x64-npm-1.0.0-59274640a2/node_modules/@rolldown/binding-freebsd-x64/",\ - "packageDependencies": [\ - ["@rolldown/binding-freebsd-x64", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-arm-gnueabihf", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-arm-gnueabihf-npm-1.0.0-10272dff7a/node_modules/@rolldown/binding-linux-arm-gnueabihf/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-arm-gnueabihf", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-arm64-gnu", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-arm64-gnu-npm-1.0.0-0a6bd960f9/node_modules/@rolldown/binding-linux-arm64-gnu/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-arm64-gnu", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-arm64-musl", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-arm64-musl-npm-1.0.0-751ff366e5/node_modules/@rolldown/binding-linux-arm64-musl/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-arm64-musl", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-ppc64-gnu", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-ppc64-gnu-npm-1.0.0-b55399904e/node_modules/@rolldown/binding-linux-ppc64-gnu/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-ppc64-gnu", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-s390x-gnu", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-s390x-gnu-npm-1.0.0-19970ffa4e/node_modules/@rolldown/binding-linux-s390x-gnu/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-s390x-gnu", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-x64-gnu", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-x64-gnu-npm-1.0.0-1925068cf0/node_modules/@rolldown/binding-linux-x64-gnu/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-x64-gnu", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-linux-x64-musl", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-linux-x64-musl-npm-1.0.0-64c6c76a96/node_modules/@rolldown/binding-linux-x64-musl/",\ - "packageDependencies": [\ - ["@rolldown/binding-linux-x64-musl", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-openharmony-arm64", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-openharmony-arm64-npm-1.0.0-83f9698da7/node_modules/@rolldown/binding-openharmony-arm64/",\ - "packageDependencies": [\ - ["@rolldown/binding-openharmony-arm64", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-wasm32-wasi", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-wasm32-wasi-npm-1.0.0-781c306be2/node_modules/@rolldown/binding-wasm32-wasi/",\ - "packageDependencies": [\ - ["@emnapi/core", "npm:1.10.0"],\ - ["@emnapi/runtime", "npm:1.10.0"],\ - ["@napi-rs/wasm-runtime", "virtual:781c306be22d98f8cc4b851223a48ae26b9ef0afd427bbc3c6120e567a6ec220663940cfffd3a08c9aae40a84cefcdf11d1f07062c8c4cd6be6e1c75706cb2e4#npm:1.1.4"],\ - ["@rolldown/binding-wasm32-wasi", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-win32-arm64-msvc", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-win32-arm64-msvc-npm-1.0.0-cc4fbb1a4d/node_modules/@rolldown/binding-win32-arm64-msvc/",\ - "packageDependencies": [\ - ["@rolldown/binding-win32-arm64-msvc", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/binding-win32-x64-msvc", [\ - ["npm:1.0.0", {\ - "packageLocation": "./.yarn/unplugged/@rolldown-binding-win32-x64-msvc-npm-1.0.0-3b59de42d8/node_modules/@rolldown/binding-win32-x64-msvc/",\ - "packageDependencies": [\ - ["@rolldown/binding-win32-x64-msvc", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@rolldown/pluginutils", [\ - ["npm:1.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@rolldown-pluginutils-npm-1.0.0-9fa8d44a94-10c0.zip/node_modules/@rolldown/pluginutils/",\ - "packageDependencies": [\ - ["@rolldown/pluginutils", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@tybys/wasm-util", [\ - ["npm:0.10.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@tybys-wasm-util-npm-0.10.2-18d4347432-10c0.zip/node_modules/@tybys/wasm-util/",\ - "packageDependencies": [\ - ["@tybys/wasm-util", "npm:0.10.2"],\ - ["tslib", "npm:2.8.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3", [\ - ["npm:7.4.3", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-npm-7.4.3-e923ea3006-10c0.zip/node_modules/@types/d3/",\ - "packageDependencies": [\ - ["@types/d3", "npm:7.4.3"],\ - ["@types/d3-array", "npm:3.2.2"],\ - ["@types/d3-axis", "npm:3.0.6"],\ - ["@types/d3-brush", "npm:3.0.6"],\ - ["@types/d3-chord", "npm:3.0.6"],\ - ["@types/d3-color", "npm:3.1.3"],\ - ["@types/d3-contour", "npm:3.0.6"],\ - ["@types/d3-delaunay", "npm:6.0.4"],\ - ["@types/d3-dispatch", "npm:3.0.7"],\ - ["@types/d3-drag", "npm:3.0.7"],\ - ["@types/d3-dsv", "npm:3.0.7"],\ - ["@types/d3-ease", "npm:3.0.2"],\ - ["@types/d3-fetch", "npm:3.0.7"],\ - ["@types/d3-force", "npm:3.0.10"],\ - ["@types/d3-format", "npm:3.0.4"],\ - ["@types/d3-geo", "npm:3.1.0"],\ - ["@types/d3-hierarchy", "npm:3.1.7"],\ - ["@types/d3-interpolate", "npm:3.0.4"],\ - ["@types/d3-path", "npm:3.1.1"],\ - ["@types/d3-polygon", "npm:3.0.2"],\ - ["@types/d3-quadtree", "npm:3.0.6"],\ - ["@types/d3-random", "npm:3.0.3"],\ - ["@types/d3-scale", "npm:4.0.9"],\ - ["@types/d3-scale-chromatic", "npm:3.1.0"],\ - ["@types/d3-selection", "npm:3.0.11"],\ - ["@types/d3-shape", "npm:3.1.8"],\ - ["@types/d3-time", "npm:3.0.4"],\ - ["@types/d3-time-format", "npm:4.0.3"],\ - ["@types/d3-timer", "npm:3.0.2"],\ - ["@types/d3-transition", "npm:3.0.9"],\ - ["@types/d3-zoom", "npm:3.0.8"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-array", [\ - ["npm:3.2.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-array-npm-3.2.2-67f9457e88-10c0.zip/node_modules/@types/d3-array/",\ - "packageDependencies": [\ - ["@types/d3-array", "npm:3.2.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-axis", [\ - ["npm:3.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-axis-npm-3.0.6-0ebc147e40-10c0.zip/node_modules/@types/d3-axis/",\ - "packageDependencies": [\ - ["@types/d3-axis", "npm:3.0.6"],\ - ["@types/d3-selection", "npm:3.0.11"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-brush", [\ - ["npm:3.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-brush-npm-3.0.6-30321930bf-10c0.zip/node_modules/@types/d3-brush/",\ - "packageDependencies": [\ - ["@types/d3-brush", "npm:3.0.6"],\ - ["@types/d3-selection", "npm:3.0.11"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-chord", [\ - ["npm:3.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-chord-npm-3.0.6-0f87b85cc9-10c0.zip/node_modules/@types/d3-chord/",\ - "packageDependencies": [\ - ["@types/d3-chord", "npm:3.0.6"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-color", [\ - ["npm:3.1.3", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-color-npm-3.1.3-220f383461-10c0.zip/node_modules/@types/d3-color/",\ - "packageDependencies": [\ - ["@types/d3-color", "npm:3.1.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-contour", [\ - ["npm:3.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-contour-npm-3.0.6-5ea5c02cf4-10c0.zip/node_modules/@types/d3-contour/",\ - "packageDependencies": [\ - ["@types/d3-array", "npm:3.2.2"],\ - ["@types/d3-contour", "npm:3.0.6"],\ - ["@types/geojson", "npm:7946.0.16"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-delaunay", [\ - ["npm:6.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-delaunay-npm-6.0.4-1c26e73c8c-10c0.zip/node_modules/@types/d3-delaunay/",\ - "packageDependencies": [\ - ["@types/d3-delaunay", "npm:6.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-dispatch", [\ - ["npm:3.0.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-dispatch-npm-3.0.7-b447e8e821-10c0.zip/node_modules/@types/d3-dispatch/",\ - "packageDependencies": [\ - ["@types/d3-dispatch", "npm:3.0.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-drag", [\ - ["npm:3.0.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-drag-npm-3.0.7-5fe22891af-10c0.zip/node_modules/@types/d3-drag/",\ - "packageDependencies": [\ - ["@types/d3-drag", "npm:3.0.7"],\ - ["@types/d3-selection", "npm:3.0.11"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-dsv", [\ - ["npm:3.0.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-dsv-npm-3.0.7-91d9e89dde-10c0.zip/node_modules/@types/d3-dsv/",\ - "packageDependencies": [\ - ["@types/d3-dsv", "npm:3.0.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-ease", [\ - ["npm:3.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-ease-npm-3.0.2-04834f8d6b-10c0.zip/node_modules/@types/d3-ease/",\ - "packageDependencies": [\ - ["@types/d3-ease", "npm:3.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-fetch", [\ - ["npm:3.0.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-fetch-npm-3.0.7-629b9cab26-10c0.zip/node_modules/@types/d3-fetch/",\ - "packageDependencies": [\ - ["@types/d3-dsv", "npm:3.0.7"],\ - ["@types/d3-fetch", "npm:3.0.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-force", [\ - ["npm:3.0.10", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-force-npm-3.0.10-c4d85d7bb8-10c0.zip/node_modules/@types/d3-force/",\ - "packageDependencies": [\ - ["@types/d3-force", "npm:3.0.10"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-format", [\ - ["npm:3.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-format-npm-3.0.4-51c02ff119-10c0.zip/node_modules/@types/d3-format/",\ - "packageDependencies": [\ - ["@types/d3-format", "npm:3.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-geo", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-geo-npm-3.1.0-ecbe022e5b-10c0.zip/node_modules/@types/d3-geo/",\ - "packageDependencies": [\ - ["@types/d3-geo", "npm:3.1.0"],\ - ["@types/geojson", "npm:7946.0.16"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-hierarchy", [\ - ["npm:3.1.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-hierarchy-npm-3.1.7-fa79c02112-10c0.zip/node_modules/@types/d3-hierarchy/",\ - "packageDependencies": [\ - ["@types/d3-hierarchy", "npm:3.1.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-interpolate", [\ - ["npm:3.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-interpolate-npm-3.0.4-e863f31b1c-10c0.zip/node_modules/@types/d3-interpolate/",\ - "packageDependencies": [\ - ["@types/d3-color", "npm:3.1.3"],\ - ["@types/d3-interpolate", "npm:3.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-path", [\ - ["npm:3.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-path-npm-3.1.1-4554261297-10c0.zip/node_modules/@types/d3-path/",\ - "packageDependencies": [\ - ["@types/d3-path", "npm:3.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-polygon", [\ - ["npm:3.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-polygon-npm-3.0.2-d8c474e223-10c0.zip/node_modules/@types/d3-polygon/",\ - "packageDependencies": [\ - ["@types/d3-polygon", "npm:3.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-quadtree", [\ - ["npm:3.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-quadtree-npm-3.0.6-cb366e1702-10c0.zip/node_modules/@types/d3-quadtree/",\ - "packageDependencies": [\ - ["@types/d3-quadtree", "npm:3.0.6"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-random", [\ - ["npm:3.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-random-npm-3.0.3-087fba9d5a-10c0.zip/node_modules/@types/d3-random/",\ - "packageDependencies": [\ - ["@types/d3-random", "npm:3.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-scale", [\ - ["npm:4.0.9", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-scale-npm-4.0.9-3ab3a2da60-10c0.zip/node_modules/@types/d3-scale/",\ - "packageDependencies": [\ - ["@types/d3-scale", "npm:4.0.9"],\ - ["@types/d3-time", "npm:3.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-scale-chromatic", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-scale-chromatic-npm-3.1.0-9022e50bfe-10c0.zip/node_modules/@types/d3-scale-chromatic/",\ - "packageDependencies": [\ - ["@types/d3-scale-chromatic", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-selection", [\ - ["npm:3.0.11", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-selection-npm-3.0.11-a3fdcb0acb-10c0.zip/node_modules/@types/d3-selection/",\ - "packageDependencies": [\ - ["@types/d3-selection", "npm:3.0.11"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-shape", [\ - ["npm:3.1.8", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-shape-npm-3.1.8-55eab2bb5c-10c0.zip/node_modules/@types/d3-shape/",\ - "packageDependencies": [\ - ["@types/d3-path", "npm:3.1.1"],\ - ["@types/d3-shape", "npm:3.1.8"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-time", [\ - ["npm:3.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-time-npm-3.0.4-2ad4c1bfad-10c0.zip/node_modules/@types/d3-time/",\ - "packageDependencies": [\ - ["@types/d3-time", "npm:3.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-time-format", [\ - ["npm:4.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-time-format-npm-4.0.3-0a3c0bca90-10c0.zip/node_modules/@types/d3-time-format/",\ - "packageDependencies": [\ - ["@types/d3-time-format", "npm:4.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-timer", [\ - ["npm:3.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-timer-npm-3.0.2-94e22db08a-10c0.zip/node_modules/@types/d3-timer/",\ - "packageDependencies": [\ - ["@types/d3-timer", "npm:3.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-transition", [\ - ["npm:3.0.9", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-transition-npm-3.0.9-cecb963074-10c0.zip/node_modules/@types/d3-transition/",\ - "packageDependencies": [\ - ["@types/d3-selection", "npm:3.0.11"],\ - ["@types/d3-transition", "npm:3.0.9"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/d3-zoom", [\ - ["npm:3.0.8", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-d3-zoom-npm-3.0.8-c4e9d7f37e-10c0.zip/node_modules/@types/d3-zoom/",\ - "packageDependencies": [\ - ["@types/d3-interpolate", "npm:3.0.4"],\ - ["@types/d3-selection", "npm:3.0.11"],\ - ["@types/d3-zoom", "npm:3.0.8"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/geojson", [\ - ["npm:7946.0.16", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-geojson-npm-7946.0.16-7a73d95991-10c0.zip/node_modules/@types/geojson/",\ - "packageDependencies": [\ - ["@types/geojson", "npm:7946.0.16"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@types/trusted-types", [\ - ["npm:2.0.7", {\ - "packageLocation": "../../../.yarn/berry/cache/@types-trusted-types-npm-2.0.7-a07fc44f59-10c0.zip/node_modules/@types/trusted-types/",\ - "packageDependencies": [\ - ["@types/trusted-types", "npm:2.0.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["@upsetjs/venn.js", [\ - ["npm:2.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/@upsetjs-venn.js-npm-2.0.0-535990e54e-10c0.zip/node_modules/@upsetjs/venn.js/",\ - "packageDependencies": [\ - ["@upsetjs/venn.js", "npm:2.0.0"],\ - ["d3-selection", "npm:3.0.0"],\ - ["d3-transition", "virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["abbrev", [\ - ["npm:4.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/abbrev-npm-4.0.0-7d848a1ef0-10c0.zip/node_modules/abbrev/",\ - "packageDependencies": [\ - ["abbrev", "npm:4.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["agent-base", [\ - ["npm:7.1.4", {\ - "packageLocation": "../../../.yarn/berry/cache/agent-base-npm-7.1.4-cb8b4604d5-10c0.zip/node_modules/agent-base/",\ - "packageDependencies": [\ - ["agent-base", "npm:7.1.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["bottom-tip", [\ - ["npm:0.1.5", {\ - "packageLocation": "../../../.yarn/berry/cache/bottom-tip-npm-0.1.5-a08d7463a4-10c0.zip/node_modules/bottom-tip/",\ - "packageDependencies": [\ - ["bottom-tip", "npm:0.1.5"],\ - ["nanoid", "npm:4.0.2"],\ - ["virtual-dom", "npm:2.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["browser-split", [\ - ["npm:0.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/browser-split-npm-0.0.1-3bb65062e4-10c0.zip/node_modules/browser-split/",\ - "packageDependencies": [\ - ["browser-split", "npm:0.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["cacache", [\ - ["npm:20.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/cacache-npm-20.0.3-5f244d5bdd-10c0.zip/node_modules/cacache/",\ - "packageDependencies": [\ - ["@npmcli/fs", "npm:5.0.0"],\ - ["cacache", "npm:20.0.3"],\ - ["fs-minipass", "npm:3.0.3"],\ - ["glob", "npm:13.0.1"],\ - ["lru-cache", "npm:11.2.5"],\ - ["minipass", "npm:7.1.2"],\ - ["minipass-collect", "npm:2.0.1"],\ - ["minipass-flush", "npm:1.0.5"],\ - ["minipass-pipeline", "npm:1.2.4"],\ - ["p-map", "npm:7.0.4"],\ - ["ssri", "npm:13.0.0"],\ - ["unique-filename", "npm:5.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["camelize", [\ - ["npm:1.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/camelize-npm-1.0.1-d86ebe085a-10c0.zip/node_modules/camelize/",\ - "packageDependencies": [\ - ["camelize", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["chownr", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/chownr-npm-3.0.0-5275e85d25-10c0.zip/node_modules/chownr/",\ - "packageDependencies": [\ - ["chownr", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["commander", [\ - ["npm:7.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/commander-npm-7.2.0-19178180f8-10c0.zip/node_modules/commander/",\ - "packageDependencies": [\ - ["commander", "npm:7.2.0"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:8.3.0", {\ - "packageLocation": "../../../.yarn/berry/cache/commander-npm-8.3.0-c0d18c66d5-10c0.zip/node_modules/commander/",\ - "packageDependencies": [\ - ["commander", "npm:8.3.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["cose-base", [\ - ["npm:1.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/cose-base-npm-1.0.3-6724b8f494-10c0.zip/node_modules/cose-base/",\ - "packageDependencies": [\ - ["cose-base", "npm:1.0.3"],\ - ["layout-base", "npm:1.0.2"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:2.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/cose-base-npm-2.2.0-aba812e070-10c0.zip/node_modules/cose-base/",\ - "packageDependencies": [\ - ["cose-base", "npm:2.2.0"],\ - ["layout-base", "npm:2.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["cytoscape", [\ - ["npm:3.33.4", {\ - "packageLocation": "../../../.yarn/berry/cache/cytoscape-npm-3.33.4-714e5b0f0a-10c0.zip/node_modules/cytoscape/",\ - "packageDependencies": [\ - ["cytoscape", "npm:3.33.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["cytoscape-cose-bilkent", [\ - ["npm:4.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/cytoscape-cose-bilkent-npm-4.1.0-30566f1cf4-10c0.zip/node_modules/cytoscape-cose-bilkent/",\ - "packageDependencies": [\ - ["cytoscape-cose-bilkent", "npm:4.1.0"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:4.1.0", {\ - "packageLocation": "./.yarn/__virtual__/cytoscape-cose-bilkent-virtual-6a53aee6b7/4/.yarn/berry/cache/cytoscape-cose-bilkent-npm-4.1.0-30566f1cf4-10c0.zip/node_modules/cytoscape-cose-bilkent/",\ - "packageDependencies": [\ - ["@types/cytoscape", null],\ - ["cose-base", "npm:1.0.3"],\ - ["cytoscape", "npm:3.33.4"],\ - ["cytoscape-cose-bilkent", "virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:4.1.0"]\ - ],\ - "packagePeers": [\ - "@types/cytoscape",\ - "cytoscape"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["cytoscape-fcose", [\ - ["npm:2.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/cytoscape-fcose-npm-2.2.0-06f382e763-10c0.zip/node_modules/cytoscape-fcose/",\ - "packageDependencies": [\ - ["cytoscape-fcose", "npm:2.2.0"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:2.2.0", {\ - "packageLocation": "./.yarn/__virtual__/cytoscape-fcose-virtual-fabcb23de4/4/.yarn/berry/cache/cytoscape-fcose-npm-2.2.0-06f382e763-10c0.zip/node_modules/cytoscape-fcose/",\ - "packageDependencies": [\ - ["@types/cytoscape", null],\ - ["cose-base", "npm:2.2.0"],\ - ["cytoscape", "npm:3.33.4"],\ - ["cytoscape-fcose", "virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:2.2.0"]\ - ],\ - "packagePeers": [\ - "@types/cytoscape",\ - "cytoscape"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3", [\ - ["npm:7.9.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-npm-7.9.0-d293821ce6-10c0.zip/node_modules/d3/",\ - "packageDependencies": [\ - ["d3", "npm:7.9.0"],\ - ["d3-array", "npm:3.2.4"],\ - ["d3-axis", "npm:3.0.0"],\ - ["d3-brush", "npm:3.0.0"],\ - ["d3-chord", "npm:3.0.1"],\ - ["d3-color", "npm:3.1.0"],\ - ["d3-contour", "npm:4.0.2"],\ - ["d3-delaunay", "npm:6.0.4"],\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-drag", "npm:3.0.0"],\ - ["d3-dsv", "npm:3.0.1"],\ - ["d3-ease", "npm:3.0.1"],\ - ["d3-fetch", "npm:3.0.1"],\ - ["d3-force", "npm:3.0.0"],\ - ["d3-format", "npm:3.1.2"],\ - ["d3-geo", "npm:3.1.1"],\ - ["d3-hierarchy", "npm:3.1.2"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-path", "npm:3.1.0"],\ - ["d3-polygon", "npm:3.0.1"],\ - ["d3-quadtree", "npm:3.0.1"],\ - ["d3-random", "npm:3.0.1"],\ - ["d3-scale", "npm:4.0.2"],\ - ["d3-scale-chromatic", "npm:3.1.0"],\ - ["d3-selection", "npm:3.0.0"],\ - ["d3-shape", "npm:3.2.0"],\ - ["d3-time", "npm:3.1.0"],\ - ["d3-time-format", "npm:4.1.0"],\ - ["d3-timer", "npm:3.0.1"],\ - ["d3-transition", "virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1"],\ - ["d3-zoom", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-array", [\ - ["npm:2.12.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-array-npm-2.12.1-104e51ecda-10c0.zip/node_modules/d3-array/",\ - "packageDependencies": [\ - ["d3-array", "npm:2.12.1"],\ - ["internmap", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:3.2.4", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-array-npm-3.2.4-b427632bcc-10c0.zip/node_modules/d3-array/",\ - "packageDependencies": [\ - ["d3-array", "npm:3.2.4"],\ - ["internmap", "npm:2.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-axis", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-axis-npm-3.0.0-81ef16a9a5-10c0.zip/node_modules/d3-axis/",\ - "packageDependencies": [\ - ["d3-axis", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-brush", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-brush-npm-3.0.0-0f86c8ad35-10c0.zip/node_modules/d3-brush/",\ - "packageDependencies": [\ - ["d3-brush", "npm:3.0.0"],\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-drag", "npm:3.0.0"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-selection", "npm:3.0.0"],\ - ["d3-transition", "virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-chord", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-chord-npm-3.0.1-3fcb345658-10c0.zip/node_modules/d3-chord/",\ - "packageDependencies": [\ - ["d3-chord", "npm:3.0.1"],\ - ["d3-path", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-color", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-color-npm-3.1.0-fc73fe3b15-10c0.zip/node_modules/d3-color/",\ - "packageDependencies": [\ - ["d3-color", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-contour", [\ - ["npm:4.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-contour-npm-4.0.2-02b9880e75-10c0.zip/node_modules/d3-contour/",\ - "packageDependencies": [\ - ["d3-array", "npm:3.2.4"],\ - ["d3-contour", "npm:4.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-delaunay", [\ - ["npm:6.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-delaunay-npm-6.0.4-606be6b5a9-10c0.zip/node_modules/d3-delaunay/",\ - "packageDependencies": [\ - ["d3-delaunay", "npm:6.0.4"],\ - ["delaunator", "npm:5.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-dispatch", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-dispatch-npm-3.0.1-5f44c3166f-10c0.zip/node_modules/d3-dispatch/",\ - "packageDependencies": [\ - ["d3-dispatch", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-drag", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-drag-npm-3.0.0-cf7b48417f-10c0.zip/node_modules/d3-drag/",\ - "packageDependencies": [\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-drag", "npm:3.0.0"],\ - ["d3-selection", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-dsv", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-dsv-npm-3.0.1-5d88fb8a85-10c0.zip/node_modules/d3-dsv/",\ - "packageDependencies": [\ - ["commander", "npm:7.2.0"],\ - ["d3-dsv", "npm:3.0.1"],\ - ["iconv-lite", "npm:0.6.3"],\ - ["rw", "npm:1.3.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-ease", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-ease-npm-3.0.1-f8f3709dc7-10c0.zip/node_modules/d3-ease/",\ - "packageDependencies": [\ - ["d3-ease", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-fetch", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-fetch-npm-3.0.1-ad9ce3dc3e-10c0.zip/node_modules/d3-fetch/",\ - "packageDependencies": [\ - ["d3-dsv", "npm:3.0.1"],\ - ["d3-fetch", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-force", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-force-npm-3.0.0-462e87e63b-10c0.zip/node_modules/d3-force/",\ - "packageDependencies": [\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-force", "npm:3.0.0"],\ - ["d3-quadtree", "npm:3.0.1"],\ - ["d3-timer", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-format", [\ - ["npm:3.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-format-npm-3.1.2-78b0d9257a-10c0.zip/node_modules/d3-format/",\ - "packageDependencies": [\ - ["d3-format", "npm:3.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-geo", [\ - ["npm:3.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-geo-npm-3.1.1-6af0bd847d-10c0.zip/node_modules/d3-geo/",\ - "packageDependencies": [\ - ["d3-array", "npm:3.2.4"],\ - ["d3-geo", "npm:3.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-hierarchy", [\ - ["npm:3.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-hierarchy-npm-3.1.2-1ac1bae7e3-10c0.zip/node_modules/d3-hierarchy/",\ - "packageDependencies": [\ - ["d3-hierarchy", "npm:3.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-interpolate", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-interpolate-npm-3.0.1-77ddca7977-10c0.zip/node_modules/d3-interpolate/",\ - "packageDependencies": [\ - ["d3-color", "npm:3.1.0"],\ - ["d3-interpolate", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-path", [\ - ["npm:1.0.9", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-path-npm-1.0.9-84bf428111-10c0.zip/node_modules/d3-path/",\ - "packageDependencies": [\ - ["d3-path", "npm:1.0.9"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-path-npm-3.1.0-8d69e9e4e5-10c0.zip/node_modules/d3-path/",\ - "packageDependencies": [\ - ["d3-path", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-polygon", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-polygon-npm-3.0.1-ccec77a8d4-10c0.zip/node_modules/d3-polygon/",\ - "packageDependencies": [\ - ["d3-polygon", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-quadtree", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-quadtree-npm-3.0.1-6f0eae8c83-10c0.zip/node_modules/d3-quadtree/",\ - "packageDependencies": [\ - ["d3-quadtree", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-random", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-random-npm-3.0.1-4fabe65eda-10c0.zip/node_modules/d3-random/",\ - "packageDependencies": [\ - ["d3-random", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-sankey", [\ - ["npm:0.12.3", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-sankey-npm-0.12.3-d590847bc5-10c0.zip/node_modules/d3-sankey/",\ - "packageDependencies": [\ - ["d3-array", "npm:2.12.1"],\ - ["d3-sankey", "npm:0.12.3"],\ - ["d3-shape", "npm:1.3.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-scale", [\ - ["npm:4.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-scale-npm-4.0.2-d17a53447b-10c0.zip/node_modules/d3-scale/",\ - "packageDependencies": [\ - ["d3-array", "npm:3.2.4"],\ - ["d3-format", "npm:3.1.2"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-scale", "npm:4.0.2"],\ - ["d3-time", "npm:3.1.0"],\ - ["d3-time-format", "npm:4.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-scale-chromatic", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-scale-chromatic-npm-3.1.0-4c3af415f5-10c0.zip/node_modules/d3-scale-chromatic/",\ - "packageDependencies": [\ - ["d3-color", "npm:3.1.0"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-scale-chromatic", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-selection", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-selection-npm-3.0.0-39a42b4ca9-10c0.zip/node_modules/d3-selection/",\ - "packageDependencies": [\ - ["d3-selection", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-shape", [\ - ["npm:1.3.7", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-shape-npm-1.3.7-8220c839bc-10c0.zip/node_modules/d3-shape/",\ - "packageDependencies": [\ - ["d3-path", "npm:1.0.9"],\ - ["d3-shape", "npm:1.3.7"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:3.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-shape-npm-3.2.0-0beb7d8b02-10c0.zip/node_modules/d3-shape/",\ - "packageDependencies": [\ - ["d3-path", "npm:3.1.0"],\ - ["d3-shape", "npm:3.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-time", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-time-npm-3.1.0-fb068fd1c9-10c0.zip/node_modules/d3-time/",\ - "packageDependencies": [\ - ["d3-array", "npm:3.2.4"],\ - ["d3-time", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-time-format", [\ - ["npm:4.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-time-format-npm-4.1.0-7f352c4634-10c0.zip/node_modules/d3-time-format/",\ - "packageDependencies": [\ - ["d3-time", "npm:3.1.0"],\ - ["d3-time-format", "npm:4.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-timer", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-timer-npm-3.0.1-45083f465d-10c0.zip/node_modules/d3-timer/",\ - "packageDependencies": [\ - ["d3-timer", "npm:3.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-transition", [\ - ["npm:3.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-transition-npm-3.0.1-9191e0faaa-10c0.zip/node_modules/d3-transition/",\ - "packageDependencies": [\ - ["d3-transition", "npm:3.0.1"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1", {\ - "packageLocation": "./.yarn/__virtual__/d3-transition-virtual-0dd6d051f6/4/.yarn/berry/cache/d3-transition-npm-3.0.1-9191e0faaa-10c0.zip/node_modules/d3-transition/",\ - "packageDependencies": [\ - ["@types/d3-selection", null],\ - ["d3-color", "npm:3.1.0"],\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-ease", "npm:3.0.1"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-selection", "npm:3.0.0"],\ - ["d3-timer", "npm:3.0.1"],\ - ["d3-transition", "virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1"]\ - ],\ - "packagePeers": [\ - "@types/d3-selection",\ - "d3-selection"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["d3-zoom", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/d3-zoom-npm-3.0.0-18f706a421-10c0.zip/node_modules/d3-zoom/",\ - "packageDependencies": [\ - ["d3-dispatch", "npm:3.0.1"],\ - ["d3-drag", "npm:3.0.0"],\ - ["d3-interpolate", "npm:3.0.1"],\ - ["d3-selection", "npm:3.0.0"],\ - ["d3-transition", "virtual:535990e54ef8b15323814be03062a56ea8982df062a0bedee1553558793a24d8a8e549604cefccb8febd1cb695eded7c1a0aea886a841337d1d41f40c1416d26#npm:3.0.1"],\ - ["d3-zoom", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["dagre-d3-es", [\ - ["npm:7.0.14", {\ - "packageLocation": "../../../.yarn/berry/cache/dagre-d3-es-npm-7.0.14-eebc854bc5-10c0.zip/node_modules/dagre-d3-es/",\ - "packageDependencies": [\ - ["d3", "npm:7.9.0"],\ - ["dagre-d3-es", "npm:7.0.14"],\ - ["lodash-es", "npm:4.18.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["dayjs", [\ - ["npm:1.11.20", {\ - "packageLocation": "../../../.yarn/berry/cache/dayjs-npm-1.11.20-98d80a1c92-10c0.zip/node_modules/dayjs/",\ - "packageDependencies": [\ - ["dayjs", "npm:1.11.20"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["debug", [\ - ["npm:4.4.3", {\ - "packageLocation": "../../../.yarn/berry/cache/debug-npm-4.4.3-0105c6123a-10c0.zip/node_modules/debug/",\ - "packageDependencies": [\ - ["debug", "npm:4.4.3"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:643ed7cc338bcf145a82d8b05b3bef6bcf150ca545df386225596f10ce53cc90b88b3ca83e348ade1ccea5f3f8e76c92d2f0e2ba544da60d40aff9921c56872d#npm:4.4.3", {\ - "packageLocation": "./.yarn/__virtual__/debug-virtual-5da9fc757d/4/.yarn/berry/cache/debug-npm-4.4.3-0105c6123a-10c0.zip/node_modules/debug/",\ - "packageDependencies": [\ - ["@types/supports-color", null],\ - ["debug", "virtual:643ed7cc338bcf145a82d8b05b3bef6bcf150ca545df386225596f10ce53cc90b88b3ca83e348ade1ccea5f3f8e76c92d2f0e2ba544da60d40aff9921c56872d#npm:4.4.3"],\ - ["ms", "npm:2.1.3"],\ - ["supports-color", null]\ - ],\ - "packagePeers": [\ - "@types/supports-color",\ - "supports-color"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["delaunator", [\ - ["npm:5.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/delaunator-npm-5.1.0-206e2b64e9-10c0.zip/node_modules/delaunator/",\ - "packageDependencies": [\ - ["delaunator", "npm:5.1.0"],\ - ["robust-predicates", "npm:3.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["detect-libc", [\ - ["npm:2.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/detect-libc-npm-2.1.2-d0c382b1e2-10c0.zip/node_modules/detect-libc/",\ - "packageDependencies": [\ - ["detect-libc", "npm:2.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["dom-walk", [\ - ["npm:0.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/dom-walk-npm-0.1.2-7d20a1a8d8-10c0.zip/node_modules/dom-walk/",\ - "packageDependencies": [\ - ["dom-walk", "npm:0.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["dompurify", [\ - ["npm:3.4.5", {\ - "packageLocation": "../../../.yarn/berry/cache/dompurify-npm-3.4.5-04ed693109-10c0.zip/node_modules/dompurify/",\ - "packageDependencies": [\ - ["@types/trusted-types", "npm:2.0.7"],\ - ["dompurify", "npm:3.4.5"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["echarts", [\ - ["npm:6.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/echarts-npm-6.1.0-517b8ad5ee-10c0.zip/node_modules/echarts/",\ - "packageDependencies": [\ - ["echarts", "npm:6.1.0"],\ - ["tslib", "npm:2.3.0"],\ - ["zrender", "npm:6.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["encoding", [\ - ["npm:0.1.13", {\ - "packageLocation": "../../../.yarn/berry/cache/encoding-npm-0.1.13-82a1837d30-10c0.zip/node_modules/encoding/",\ - "packageDependencies": [\ - ["encoding", "npm:0.1.13"],\ - ["iconv-lite", "npm:0.6.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["env-paths", [\ - ["npm:2.2.1", {\ - "packageLocation": "../../../.yarn/berry/cache/env-paths-npm-2.2.1-7c7577428c-10c0.zip/node_modules/env-paths/",\ - "packageDependencies": [\ - ["env-paths", "npm:2.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["err-code", [\ - ["npm:2.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/err-code-npm-2.0.3-082e0ff9a7-10c0.zip/node_modules/err-code/",\ - "packageDependencies": [\ - ["err-code", "npm:2.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["error", [\ - ["npm:4.4.0", {\ - "packageLocation": "../../../.yarn/berry/cache/error-npm-4.4.0-dc2d401d15-10c0.zip/node_modules/error/",\ - "packageDependencies": [\ - ["camelize", "npm:1.0.1"],\ - ["error", "npm:4.4.0"],\ - ["string-template", "npm:0.2.1"],\ - ["xtend", "npm:4.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["es-toolkit", [\ - ["npm:1.47.0", {\ - "packageLocation": "../../../.yarn/berry/cache/es-toolkit-npm-1.47.0-d268ad97ad-10c0.zip/node_modules/es-toolkit/",\ - "packageDependencies": [\ - ["es-toolkit", "npm:1.47.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["ev-store", [\ - ["npm:7.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/ev-store-npm-7.0.0-1ae84fd275-10c0.zip/node_modules/ev-store/",\ - "packageDependencies": [\ - ["ev-store", "npm:7.0.0"],\ - ["individual", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["exponential-backoff", [\ - ["npm:3.1.3", {\ - "packageLocation": "../../../.yarn/berry/cache/exponential-backoff-npm-3.1.3-28be78d98e-10c0.zip/node_modules/exponential-backoff/",\ - "packageDependencies": [\ - ["exponential-backoff", "npm:3.1.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["fdir", [\ - ["npm:6.5.0", {\ - "packageLocation": "../../../.yarn/berry/cache/fdir-npm-6.5.0-8814a0dec7-10c0.zip/node_modules/fdir/",\ - "packageDependencies": [\ - ["fdir", "npm:6.5.0"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:0e783aadbd2b4b8e6f6056033c0b290501892d23bc7c5dad5477e00e48ad8bd3e4434c3962a52dd75a58e06dbb7218094a494bac954ef2f7f6fdb65d9717e5f4#npm:6.5.0", {\ - "packageLocation": "./.yarn/__virtual__/fdir-virtual-abd4ab2082/4/.yarn/berry/cache/fdir-npm-6.5.0-8814a0dec7-10c0.zip/node_modules/fdir/",\ - "packageDependencies": [\ - ["@types/picomatch", null],\ - ["fdir", "virtual:0e783aadbd2b4b8e6f6056033c0b290501892d23bc7c5dad5477e00e48ad8bd3e4434c3962a52dd75a58e06dbb7218094a494bac954ef2f7f6fdb65d9717e5f4#npm:6.5.0"],\ - ["picomatch", "npm:4.0.3"]\ - ],\ - "packagePeers": [\ - "@types/picomatch",\ - "picomatch"\ - ],\ - "linkType": "HARD"\ - }],\ - ["virtual:102914a73b14bffc325c2cdf701d5ae063b57309ea75829f709b4273a7ea0d0e11784f2d6f2635e156595ab235d9a24869844d54ab73f4ad81d3a7b01b185214#npm:6.5.0", {\ - "packageLocation": "./.yarn/__virtual__/fdir-virtual-c9b12f80ea/4/.yarn/berry/cache/fdir-npm-6.5.0-8814a0dec7-10c0.zip/node_modules/fdir/",\ - "packageDependencies": [\ - ["@types/picomatch", null],\ - ["fdir", "virtual:102914a73b14bffc325c2cdf701d5ae063b57309ea75829f709b4273a7ea0d0e11784f2d6f2635e156595ab235d9a24869844d54ab73f4ad81d3a7b01b185214#npm:6.5.0"],\ - ["picomatch", "npm:4.0.4"]\ - ],\ - "packagePeers": [\ - "@types/picomatch",\ - "picomatch"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["fs-minipass", [\ - ["npm:3.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/fs-minipass-npm-3.0.3-d148d6ac19-10c0.zip/node_modules/fs-minipass/",\ - "packageDependencies": [\ - ["fs-minipass", "npm:3.0.3"],\ - ["minipass", "npm:7.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["fsevents", [\ - ["patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1", {\ - "packageLocation": "./.yarn/unplugged/fsevents-patch-6b67494872/node_modules/fsevents/",\ - "packageDependencies": [\ - ["fsevents", "patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"],\ - ["node-gyp", "npm:12.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["glob", [\ - ["npm:13.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/glob-npm-13.0.1-322004d656-10c0.zip/node_modules/glob/",\ - "packageDependencies": [\ - ["glob", "npm:13.0.1"],\ - ["minimatch", "npm:10.1.2"],\ - ["minipass", "npm:7.1.2"],\ - ["path-scurry", "npm:2.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["global", [\ - ["npm:4.4.0", {\ - "packageLocation": "../../../.yarn/berry/cache/global-npm-4.4.0-888ee8033d-10c0.zip/node_modules/global/",\ - "packageDependencies": [\ - ["global", "npm:4.4.0"],\ - ["min-document", "npm:2.19.2"],\ - ["process", "npm:0.11.10"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["graceful-fs", [\ - ["npm:4.2.11", {\ - "packageLocation": "../../../.yarn/berry/cache/graceful-fs-npm-4.2.11-24bb648a68-10c0.zip/node_modules/graceful-fs/",\ - "packageDependencies": [\ - ["graceful-fs", "npm:4.2.11"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["hachure-fill", [\ - ["npm:0.5.2", {\ - "packageLocation": "../../../.yarn/berry/cache/hachure-fill-npm-0.5.2-188246a623-10c0.zip/node_modules/hachure-fill/",\ - "packageDependencies": [\ - ["hachure-fill", "npm:0.5.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["http-cache-semantics", [\ - ["npm:4.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/http-cache-semantics-npm-4.2.0-fadacfb3ad-10c0.zip/node_modules/http-cache-semantics/",\ - "packageDependencies": [\ - ["http-cache-semantics", "npm:4.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["http-proxy-agent", [\ - ["npm:7.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/http-proxy-agent-npm-7.0.2-643ed7cc33-10c0.zip/node_modules/http-proxy-agent/",\ - "packageDependencies": [\ - ["agent-base", "npm:7.1.4"],\ - ["debug", "virtual:643ed7cc338bcf145a82d8b05b3bef6bcf150ca545df386225596f10ce53cc90b88b3ca83e348ade1ccea5f3f8e76c92d2f0e2ba544da60d40aff9921c56872d#npm:4.4.3"],\ - ["http-proxy-agent", "npm:7.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["https-proxy-agent", [\ - ["npm:7.0.6", {\ - "packageLocation": "../../../.yarn/berry/cache/https-proxy-agent-npm-7.0.6-27a95c2690-10c0.zip/node_modules/https-proxy-agent/",\ - "packageDependencies": [\ - ["agent-base", "npm:7.1.4"],\ - ["debug", "virtual:643ed7cc338bcf145a82d8b05b3bef6bcf150ca545df386225596f10ce53cc90b88b3ca83e348ade1ccea5f3f8e76c92d2f0e2ba544da60d40aff9921c56872d#npm:4.4.3"],\ - ["https-proxy-agent", "npm:7.0.6"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["iconv-lite", [\ - ["npm:0.6.3", {\ - "packageLocation": "../../../.yarn/berry/cache/iconv-lite-npm-0.6.3-24b8aae27e-10c0.zip/node_modules/iconv-lite/",\ - "packageDependencies": [\ - ["iconv-lite", "npm:0.6.3"],\ - ["safer-buffer", "npm:2.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["import-meta-resolve", [\ - ["npm:4.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/import-meta-resolve-npm-4.2.0-d0ecf96035-10c0.zip/node_modules/import-meta-resolve/",\ - "packageDependencies": [\ - ["import-meta-resolve", "npm:4.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["imurmurhash", [\ - ["npm:0.1.4", {\ - "packageLocation": "../../../.yarn/berry/cache/imurmurhash-npm-0.1.4-610c5068a0-10c0.zip/node_modules/imurmurhash/",\ - "packageDependencies": [\ - ["imurmurhash", "npm:0.1.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["individual", [\ - ["npm:3.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/individual-npm-3.0.0-783c0f5e37-10c0.zip/node_modules/individual/",\ - "packageDependencies": [\ - ["individual", "npm:3.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["internmap", [\ - ["npm:1.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/internmap-npm-1.0.1-658c30de3f-10c0.zip/node_modules/internmap/",\ - "packageDependencies": [\ - ["internmap", "npm:1.0.1"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:2.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/internmap-npm-2.0.3-d74f5c9998-10c0.zip/node_modules/internmap/",\ - "packageDependencies": [\ - ["internmap", "npm:2.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["ip-address", [\ - ["npm:10.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/ip-address-npm-10.1.0-d5d5693401-10c0.zip/node_modules/ip-address/",\ - "packageDependencies": [\ - ["ip-address", "npm:10.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["is-object", [\ - ["npm:1.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/is-object-npm-1.0.2-f72f6facf4-10c0.zip/node_modules/is-object/",\ - "packageDependencies": [\ - ["is-object", "npm:1.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["isexe", [\ - ["npm:3.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/isexe-npm-3.1.1-9c0061eead-10c0.zip/node_modules/isexe/",\ - "packageDependencies": [\ - ["isexe", "npm:3.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["katex", [\ - ["npm:0.16.47", {\ - "packageLocation": "../../../.yarn/berry/cache/katex-npm-0.16.47-76e0e97e60-10c0.zip/node_modules/katex/",\ - "packageDependencies": [\ - ["commander", "npm:8.3.0"],\ - ["katex", "npm:0.16.47"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["khroma", [\ - ["npm:2.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/khroma-npm-2.1.0-ff0e57ac49-10c0.zip/node_modules/khroma/",\ - "packageDependencies": [\ - ["khroma", "npm:2.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["layout-base", [\ - ["npm:1.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/layout-base-npm-1.0.2-1977e5df51-10c0.zip/node_modules/layout-base/",\ - "packageDependencies": [\ - ["layout-base", "npm:1.0.2"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:2.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/layout-base-npm-2.0.1-f7e7c9f6f6-10c0.zip/node_modules/layout-base/",\ - "packageDependencies": [\ - ["layout-base", "npm:2.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss", [\ - ["npm:1.32.0", {\ - "packageLocation": "../../../.yarn/berry/cache/lightningcss-npm-1.32.0-9120a09118-10c0.zip/node_modules/lightningcss/",\ - "packageDependencies": [\ - ["detect-libc", "npm:2.1.2"],\ - ["lightningcss", "npm:1.32.0"],\ - ["lightningcss-android-arm64", "npm:1.32.0"],\ - ["lightningcss-darwin-arm64", "npm:1.32.0"],\ - ["lightningcss-darwin-x64", "npm:1.32.0"],\ - ["lightningcss-freebsd-x64", "npm:1.32.0"],\ - ["lightningcss-linux-arm-gnueabihf", "npm:1.32.0"],\ - ["lightningcss-linux-arm64-gnu", "npm:1.32.0"],\ - ["lightningcss-linux-arm64-musl", "npm:1.32.0"],\ - ["lightningcss-linux-x64-gnu", "npm:1.32.0"],\ - ["lightningcss-linux-x64-musl", "npm:1.32.0"],\ - ["lightningcss-win32-arm64-msvc", "npm:1.32.0"],\ - ["lightningcss-win32-x64-msvc", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-android-arm64", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-android-arm64-npm-1.32.0-a7e252e7b6/node_modules/lightningcss-android-arm64/",\ - "packageDependencies": [\ - ["lightningcss-android-arm64", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-darwin-arm64", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-darwin-arm64-npm-1.32.0-80ea961907/node_modules/lightningcss-darwin-arm64/",\ - "packageDependencies": [\ - ["lightningcss-darwin-arm64", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-darwin-x64", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-darwin-x64-npm-1.32.0-0490e306f3/node_modules/lightningcss-darwin-x64/",\ - "packageDependencies": [\ - ["lightningcss-darwin-x64", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-freebsd-x64", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-freebsd-x64-npm-1.32.0-bde44894e9/node_modules/lightningcss-freebsd-x64/",\ - "packageDependencies": [\ - ["lightningcss-freebsd-x64", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-linux-arm-gnueabihf", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-linux-arm-gnueabihf-npm-1.32.0-648c45fd6c/node_modules/lightningcss-linux-arm-gnueabihf/",\ - "packageDependencies": [\ - ["lightningcss-linux-arm-gnueabihf", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-linux-arm64-gnu", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-linux-arm64-gnu-npm-1.32.0-5b83cb14d4/node_modules/lightningcss-linux-arm64-gnu/",\ - "packageDependencies": [\ - ["lightningcss-linux-arm64-gnu", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-linux-arm64-musl", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-linux-arm64-musl-npm-1.32.0-ba6c30c731/node_modules/lightningcss-linux-arm64-musl/",\ - "packageDependencies": [\ - ["lightningcss-linux-arm64-musl", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-linux-x64-gnu", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-linux-x64-gnu-npm-1.32.0-b1c0f4de09/node_modules/lightningcss-linux-x64-gnu/",\ - "packageDependencies": [\ - ["lightningcss-linux-x64-gnu", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-linux-x64-musl", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-linux-x64-musl-npm-1.32.0-7053da09cc/node_modules/lightningcss-linux-x64-musl/",\ - "packageDependencies": [\ - ["lightningcss-linux-x64-musl", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-win32-arm64-msvc", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-win32-arm64-msvc-npm-1.32.0-677877e121/node_modules/lightningcss-win32-arm64-msvc/",\ - "packageDependencies": [\ - ["lightningcss-win32-arm64-msvc", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lightningcss-win32-x64-msvc", [\ - ["npm:1.32.0", {\ - "packageLocation": "./.yarn/unplugged/lightningcss-win32-x64-msvc-npm-1.32.0-3c26fdaa38/node_modules/lightningcss-win32-x64-msvc/",\ - "packageDependencies": [\ - ["lightningcss-win32-x64-msvc", "npm:1.32.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lodash-es", [\ - ["npm:4.18.1", {\ - "packageLocation": "../../../.yarn/berry/cache/lodash-es-npm-4.18.1-02cf41b912-10c0.zip/node_modules/lodash-es/",\ - "packageDependencies": [\ - ["lodash-es", "npm:4.18.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["lru-cache", [\ - ["npm:11.2.5", {\ - "packageLocation": "../../../.yarn/berry/cache/lru-cache-npm-11.2.5-a56eb40aef-10c0.zip/node_modules/lru-cache/",\ - "packageDependencies": [\ - ["lru-cache", "npm:11.2.5"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["make-fetch-happen", [\ - ["npm:15.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/make-fetch-happen-npm-15.0.3-10a832fcad-10c0.zip/node_modules/make-fetch-happen/",\ - "packageDependencies": [\ - ["@npmcli/agent", "npm:4.0.0"],\ - ["cacache", "npm:20.0.3"],\ - ["http-cache-semantics", "npm:4.2.0"],\ - ["make-fetch-happen", "npm:15.0.3"],\ - ["minipass", "npm:7.1.2"],\ - ["minipass-fetch", "npm:5.0.1"],\ - ["minipass-flush", "npm:1.0.5"],\ - ["minipass-pipeline", "npm:1.2.4"],\ - ["negotiator", "npm:1.0.0"],\ - ["proc-log", "npm:6.1.0"],\ - ["promise-retry", "npm:2.0.1"],\ - ["ssri", "npm:13.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["marked", [\ - ["npm:16.4.2", {\ - "packageLocation": "../../../.yarn/berry/cache/marked-npm-16.4.2-80bdca5d9b-10c0.zip/node_modules/marked/",\ - "packageDependencies": [\ - ["marked", "npm:16.4.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["mermaid", [\ - ["npm:11.15.0", {\ - "packageLocation": "../../../.yarn/berry/cache/mermaid-npm-11.15.0-c096dbb547-10c0.zip/node_modules/mermaid/",\ - "packageDependencies": [\ - ["@braintree/sanitize-url", "npm:7.1.2"],\ - ["@iconify/utils", "npm:3.1.3"],\ - ["@mermaid-js/parser", "npm:1.1.1"],\ - ["@types/d3", "npm:7.4.3"],\ - ["@upsetjs/venn.js", "npm:2.0.0"],\ - ["cytoscape", "npm:3.33.4"],\ - ["cytoscape-cose-bilkent", "virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:4.1.0"],\ - ["cytoscape-fcose", "virtual:c096dbb5475061e3cc215842f46629df52a0c08884099ef8f50086f3c0198470ff1da368a46951bcb2c229f4d1882193e72930ea1e3f2e10afff9077528864a9#npm:2.2.0"],\ - ["d3", "npm:7.9.0"],\ - ["d3-sankey", "npm:0.12.3"],\ - ["dagre-d3-es", "npm:7.0.14"],\ - ["dayjs", "npm:1.11.20"],\ - ["dompurify", "npm:3.4.5"],\ - ["es-toolkit", "npm:1.47.0"],\ - ["katex", "npm:0.16.47"],\ - ["khroma", "npm:2.1.0"],\ - ["marked", "npm:16.4.2"],\ - ["mermaid", "npm:11.15.0"],\ - ["roughjs", "npm:4.6.6"],\ - ["stylis", "npm:4.4.0"],\ - ["ts-dedent", "npm:2.2.0"],\ - ["uuid", "npm:14.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["min-document", [\ - ["npm:2.19.2", {\ - "packageLocation": "../../../.yarn/berry/cache/min-document-npm-2.19.2-3ed02fe99d-10c0.zip/node_modules/min-document/",\ - "packageDependencies": [\ - ["dom-walk", "npm:0.1.2"],\ - ["min-document", "npm:2.19.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minimatch", [\ - ["npm:10.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/minimatch-npm-10.1.2-9295d0648a-10c0.zip/node_modules/minimatch/",\ - "packageDependencies": [\ - ["@isaacs/brace-expansion", "npm:5.0.1"],\ - ["minimatch", "npm:10.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass", [\ - ["npm:3.3.6", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-npm-3.3.6-b8d93a945b-10c0.zip/node_modules/minipass/",\ - "packageDependencies": [\ - ["minipass", "npm:3.3.6"],\ - ["yallist", "npm:4.0.0"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:7.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-npm-7.1.2-3a5327d36d-10c0.zip/node_modules/minipass/",\ - "packageDependencies": [\ - ["minipass", "npm:7.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass-collect", [\ - ["npm:2.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-collect-npm-2.0.1-73d3907e40-10c0.zip/node_modules/minipass-collect/",\ - "packageDependencies": [\ - ["minipass", "npm:7.1.2"],\ - ["minipass-collect", "npm:2.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass-fetch", [\ - ["npm:5.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-fetch-npm-5.0.1-34f846b2fb-10c0.zip/node_modules/minipass-fetch/",\ - "packageDependencies": [\ - ["encoding", "npm:0.1.13"],\ - ["minipass", "npm:7.1.2"],\ - ["minipass-fetch", "npm:5.0.1"],\ - ["minipass-sized", "npm:2.0.0"],\ - ["minizlib", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass-flush", [\ - ["npm:1.0.5", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-flush-npm-1.0.5-efe79d9826-10c0.zip/node_modules/minipass-flush/",\ - "packageDependencies": [\ - ["minipass", "npm:3.3.6"],\ - ["minipass-flush", "npm:1.0.5"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass-pipeline", [\ - ["npm:1.2.4", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-pipeline-npm-1.2.4-5924cb077f-10c0.zip/node_modules/minipass-pipeline/",\ - "packageDependencies": [\ - ["minipass", "npm:3.3.6"],\ - ["minipass-pipeline", "npm:1.2.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minipass-sized", [\ - ["npm:2.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/minipass-sized-npm-2.0.0-a911fc7d3f-10c0.zip/node_modules/minipass-sized/",\ - "packageDependencies": [\ - ["minipass", "npm:7.1.2"],\ - ["minipass-sized", "npm:2.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["minizlib", [\ - ["npm:3.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/minizlib-npm-3.1.0-6680befdba-10c0.zip/node_modules/minizlib/",\ - "packageDependencies": [\ - ["minipass", "npm:7.1.2"],\ - ["minizlib", "npm:3.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["ms", [\ - ["npm:2.1.3", {\ - "packageLocation": "../../../.yarn/berry/cache/ms-npm-2.1.3-81ff3cfac1-10c0.zip/node_modules/ms/",\ - "packageDependencies": [\ - ["ms", "npm:2.1.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["nanoid", [\ - ["npm:3.3.11", {\ - "packageLocation": "../../../.yarn/berry/cache/nanoid-npm-3.3.11-f98c1f9ef6-10c0.zip/node_modules/nanoid/",\ - "packageDependencies": [\ - ["nanoid", "npm:3.3.11"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:4.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/nanoid-npm-4.0.2-ae010cad86-10c0.zip/node_modules/nanoid/",\ - "packageDependencies": [\ - ["nanoid", "npm:4.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["negotiator", [\ - ["npm:1.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/negotiator-npm-1.0.0-47d727e27e-10c0.zip/node_modules/negotiator/",\ - "packageDependencies": [\ - ["negotiator", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["next-tick", [\ - ["npm:0.2.2", {\ - "packageLocation": "../../../.yarn/berry/cache/next-tick-npm-0.2.2-2d1ea03b94-10c0.zip/node_modules/next-tick/",\ - "packageDependencies": [\ - ["next-tick", "npm:0.2.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["node-gyp", [\ - ["npm:12.2.0", {\ - "packageLocation": "./.yarn/unplugged/node-gyp-npm-12.2.0-11f8fe84f1/node_modules/node-gyp/",\ - "packageDependencies": [\ - ["env-paths", "npm:2.2.1"],\ - ["exponential-backoff", "npm:3.1.3"],\ - ["graceful-fs", "npm:4.2.11"],\ - ["make-fetch-happen", "npm:15.0.3"],\ - ["node-gyp", "npm:12.2.0"],\ - ["nopt", "npm:9.0.0"],\ - ["proc-log", "npm:6.1.0"],\ - ["semver", "npm:7.7.4"],\ - ["tar", "npm:7.5.7"],\ - ["tinyglobby", "npm:0.2.15"],\ - ["which", "npm:6.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["nopt", [\ - ["npm:9.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/nopt-npm-9.0.0-81316ec15c-10c0.zip/node_modules/nopt/",\ - "packageDependencies": [\ - ["abbrev", "npm:4.0.0"],\ - ["nopt", "npm:9.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["p-map", [\ - ["npm:7.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/p-map-npm-7.0.4-39386109d0-10c0.zip/node_modules/p-map/",\ - "packageDependencies": [\ - ["p-map", "npm:7.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["package-manager-detector", [\ - ["npm:1.6.0", {\ - "packageLocation": "../../../.yarn/berry/cache/package-manager-detector-npm-1.6.0-7526931ba9-10c0.zip/node_modules/package-manager-detector/",\ - "packageDependencies": [\ - ["package-manager-detector", "npm:1.6.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["path-data-parser", [\ - ["npm:0.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/path-data-parser-npm-0.1.0-08764d5ca0-10c0.zip/node_modules/path-data-parser/",\ - "packageDependencies": [\ - ["path-data-parser", "npm:0.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["path-scurry", [\ - ["npm:2.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/path-scurry-npm-2.0.1-7744619e5d-10c0.zip/node_modules/path-scurry/",\ - "packageDependencies": [\ - ["lru-cache", "npm:11.2.5"],\ - ["minipass", "npm:7.1.2"],\ - ["path-scurry", "npm:2.0.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["picocolors", [\ - ["npm:1.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/picocolors-npm-1.1.1-4fede47cf1-10c0.zip/node_modules/picocolors/",\ - "packageDependencies": [\ - ["picocolors", "npm:1.1.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["picomatch", [\ - ["npm:4.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/picomatch-npm-4.0.3-0a647b87cc-10c0.zip/node_modules/picomatch/",\ - "packageDependencies": [\ - ["picomatch", "npm:4.0.3"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:4.0.4", {\ - "packageLocation": "../../../.yarn/berry/cache/picomatch-npm-4.0.4-e82d450244-10c0.zip/node_modules/picomatch/",\ - "packageDependencies": [\ - ["picomatch", "npm:4.0.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["points-on-curve", [\ - ["npm:0.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/points-on-curve-npm-0.2.0-bf188db820-10c0.zip/node_modules/points-on-curve/",\ - "packageDependencies": [\ - ["points-on-curve", "npm:0.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["points-on-path", [\ - ["npm:0.2.1", {\ - "packageLocation": "../../../.yarn/berry/cache/points-on-path-npm-0.2.1-3897d05976-10c0.zip/node_modules/points-on-path/",\ - "packageDependencies": [\ - ["path-data-parser", "npm:0.1.0"],\ - ["points-on-curve", "npm:0.2.0"],\ - ["points-on-path", "npm:0.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["postcss", [\ - ["npm:8.5.14", {\ - "packageLocation": "../../../.yarn/berry/cache/postcss-npm-8.5.14-1cf8d01c78-10c0.zip/node_modules/postcss/",\ - "packageDependencies": [\ - ["nanoid", "npm:3.3.11"],\ - ["picocolors", "npm:1.1.1"],\ - ["postcss", "npm:8.5.14"],\ - ["source-map-js", "npm:1.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["proc-log", [\ - ["npm:6.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/proc-log-npm-6.1.0-84e609b3f4-10c0.zip/node_modules/proc-log/",\ - "packageDependencies": [\ - ["proc-log", "npm:6.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["process", [\ - ["npm:0.11.10", {\ - "packageLocation": "../../../.yarn/berry/cache/process-npm-0.11.10-aeb3b641ae-10c0.zip/node_modules/process/",\ - "packageDependencies": [\ - ["process", "npm:0.11.10"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["promise-retry", [\ - ["npm:2.0.1", {\ - "packageLocation": "../../../.yarn/berry/cache/promise-retry-npm-2.0.1-871f0b01b7-10c0.zip/node_modules/promise-retry/",\ - "packageDependencies": [\ - ["err-code", "npm:2.0.3"],\ - ["promise-retry", "npm:2.0.1"],\ - ["retry", "npm:0.12.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["retry", [\ - ["npm:0.12.0", {\ - "packageLocation": "../../../.yarn/berry/cache/retry-npm-0.12.0-72ac7fb4cc-10c0.zip/node_modules/retry/",\ - "packageDependencies": [\ - ["retry", "npm:0.12.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["robust-predicates", [\ - ["npm:3.0.3", {\ - "packageLocation": "../../../.yarn/berry/cache/robust-predicates-npm-3.0.3-727493a43d-10c0.zip/node_modules/robust-predicates/",\ - "packageDependencies": [\ - ["robust-predicates", "npm:3.0.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["rolldown", [\ - ["npm:1.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/rolldown-npm-1.0.0-48dff562be-10c0.zip/node_modules/rolldown/",\ - "packageDependencies": [\ - ["@oxc-project/types", "npm:0.129.0"],\ - ["@rolldown/binding-android-arm64", "npm:1.0.0"],\ - ["@rolldown/binding-darwin-arm64", "npm:1.0.0"],\ - ["@rolldown/binding-darwin-x64", "npm:1.0.0"],\ - ["@rolldown/binding-freebsd-x64", "npm:1.0.0"],\ - ["@rolldown/binding-linux-arm-gnueabihf", "npm:1.0.0"],\ - ["@rolldown/binding-linux-arm64-gnu", "npm:1.0.0"],\ - ["@rolldown/binding-linux-arm64-musl", "npm:1.0.0"],\ - ["@rolldown/binding-linux-ppc64-gnu", "npm:1.0.0"],\ - ["@rolldown/binding-linux-s390x-gnu", "npm:1.0.0"],\ - ["@rolldown/binding-linux-x64-gnu", "npm:1.0.0"],\ - ["@rolldown/binding-linux-x64-musl", "npm:1.0.0"],\ - ["@rolldown/binding-openharmony-arm64", "npm:1.0.0"],\ - ["@rolldown/binding-wasm32-wasi", "npm:1.0.0"],\ - ["@rolldown/binding-win32-arm64-msvc", "npm:1.0.0"],\ - ["@rolldown/binding-win32-x64-msvc", "npm:1.0.0"],\ - ["@rolldown/pluginutils", "npm:1.0.0"],\ - ["rolldown", "npm:1.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["root-workspace-0b6124", [\ - ["workspace:.", {\ - "packageLocation": "./",\ - "packageDependencies": [\ - ["@calcit/procs", "npm:0.12.35"],\ - ["bottom-tip", "npm:0.1.5"],\ - ["echarts", "npm:6.1.0"],\ - ["mermaid", "npm:11.15.0"],\ - ["root-workspace-0b6124", "workspace:."],\ - ["vite", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12"]\ - ],\ - "linkType": "SOFT"\ - }]\ - ]],\ - ["roughjs", [\ - ["npm:4.6.6", {\ - "packageLocation": "../../../.yarn/berry/cache/roughjs-npm-4.6.6-181dd8deb7-10c0.zip/node_modules/roughjs/",\ - "packageDependencies": [\ - ["hachure-fill", "npm:0.5.2"],\ - ["path-data-parser", "npm:0.1.0"],\ - ["points-on-curve", "npm:0.2.0"],\ - ["points-on-path", "npm:0.2.1"],\ - ["roughjs", "npm:4.6.6"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["rw", [\ - ["npm:1.3.3", {\ - "packageLocation": "../../../.yarn/berry/cache/rw-npm-1.3.3-2197930a8d-10c0.zip/node_modules/rw/",\ - "packageDependencies": [\ - ["rw", "npm:1.3.3"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["safer-buffer", [\ - ["npm:2.1.2", {\ - "packageLocation": "../../../.yarn/berry/cache/safer-buffer-npm-2.1.2-8d5c0b705e-10c0.zip/node_modules/safer-buffer/",\ - "packageDependencies": [\ - ["safer-buffer", "npm:2.1.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["semver", [\ - ["npm:7.7.4", {\ - "packageLocation": "../../../.yarn/berry/cache/semver-npm-7.7.4-746e538da5-10c0.zip/node_modules/semver/",\ - "packageDependencies": [\ - ["semver", "npm:7.7.4"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["smart-buffer", [\ - ["npm:4.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/smart-buffer-npm-4.2.0-5ac3f668bb-10c0.zip/node_modules/smart-buffer/",\ - "packageDependencies": [\ - ["smart-buffer", "npm:4.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["socks", [\ - ["npm:2.8.7", {\ - "packageLocation": "../../../.yarn/berry/cache/socks-npm-2.8.7-d1d20aae19-10c0.zip/node_modules/socks/",\ - "packageDependencies": [\ - ["ip-address", "npm:10.1.0"],\ - ["smart-buffer", "npm:4.2.0"],\ - ["socks", "npm:2.8.7"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["socks-proxy-agent", [\ - ["npm:8.0.5", {\ - "packageLocation": "../../../.yarn/berry/cache/socks-proxy-agent-npm-8.0.5-24d77a90dc-10c0.zip/node_modules/socks-proxy-agent/",\ - "packageDependencies": [\ - ["agent-base", "npm:7.1.4"],\ - ["debug", "virtual:643ed7cc338bcf145a82d8b05b3bef6bcf150ca545df386225596f10ce53cc90b88b3ca83e348ade1ccea5f3f8e76c92d2f0e2ba544da60d40aff9921c56872d#npm:4.4.3"],\ - ["socks", "npm:2.8.7"],\ - ["socks-proxy-agent", "npm:8.0.5"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["source-map-js", [\ - ["npm:1.2.1", {\ - "packageLocation": "../../../.yarn/berry/cache/source-map-js-npm-1.2.1-b9a47d7e1a-10c0.zip/node_modules/source-map-js/",\ - "packageDependencies": [\ - ["source-map-js", "npm:1.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["ssri", [\ - ["npm:13.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/ssri-npm-13.0.0-f5fa93375d-10c0.zip/node_modules/ssri/",\ - "packageDependencies": [\ - ["minipass", "npm:7.1.2"],\ - ["ssri", "npm:13.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["string-template", [\ - ["npm:0.2.1", {\ - "packageLocation": "../../../.yarn/berry/cache/string-template-npm-0.2.1-e8fa722df5-10c0.zip/node_modules/string-template/",\ - "packageDependencies": [\ - ["string-template", "npm:0.2.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["stylis", [\ - ["npm:4.4.0", {\ - "packageLocation": "../../../.yarn/berry/cache/stylis-npm-4.4.0-56096b8e1f-10c0.zip/node_modules/stylis/",\ - "packageDependencies": [\ - ["stylis", "npm:4.4.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["tar", [\ - ["npm:7.5.7", {\ - "packageLocation": "../../../.yarn/berry/cache/tar-npm-7.5.7-053aec5a88-10c0.zip/node_modules/tar/",\ - "packageDependencies": [\ - ["@isaacs/fs-minipass", "npm:4.0.1"],\ - ["chownr", "npm:3.0.0"],\ - ["minipass", "npm:7.1.2"],\ - ["minizlib", "npm:3.1.0"],\ - ["tar", "npm:7.5.7"],\ - ["yallist", "npm:5.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["tinyexec", [\ - ["npm:1.2.2", {\ - "packageLocation": "../../../.yarn/berry/cache/tinyexec-npm-1.2.2-34442e8104-10c0.zip/node_modules/tinyexec/",\ - "packageDependencies": [\ - ["tinyexec", "npm:1.2.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["tinyglobby", [\ - ["npm:0.2.15", {\ - "packageLocation": "../../../.yarn/berry/cache/tinyglobby-npm-0.2.15-0e783aadbd-10c0.zip/node_modules/tinyglobby/",\ - "packageDependencies": [\ - ["fdir", "virtual:0e783aadbd2b4b8e6f6056033c0b290501892d23bc7c5dad5477e00e48ad8bd3e4434c3962a52dd75a58e06dbb7218094a494bac954ef2f7f6fdb65d9717e5f4#npm:6.5.0"],\ - ["picomatch", "npm:4.0.3"],\ - ["tinyglobby", "npm:0.2.15"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:0.2.16", {\ - "packageLocation": "../../../.yarn/berry/cache/tinyglobby-npm-0.2.16-102914a73b-10c0.zip/node_modules/tinyglobby/",\ - "packageDependencies": [\ - ["fdir", "virtual:102914a73b14bffc325c2cdf701d5ae063b57309ea75829f709b4273a7ea0d0e11784f2d6f2635e156595ab235d9a24869844d54ab73f4ad81d3a7b01b185214#npm:6.5.0"],\ - ["picomatch", "npm:4.0.4"],\ - ["tinyglobby", "npm:0.2.16"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["ts-dedent", [\ - ["npm:2.2.0", {\ - "packageLocation": "../../../.yarn/berry/cache/ts-dedent-npm-2.2.0-00389a0e6b-10c0.zip/node_modules/ts-dedent/",\ - "packageDependencies": [\ - ["ts-dedent", "npm:2.2.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["tslib", [\ - ["npm:2.3.0", {\ - "packageLocation": "../../../.yarn/berry/cache/tslib-npm-2.3.0-277e75e108-10c0.zip/node_modules/tslib/",\ - "packageDependencies": [\ - ["tslib", "npm:2.3.0"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:2.8.1", {\ - "packageLocation": "../../../.yarn/berry/cache/tslib-npm-2.8.1-66590b21b8-10c0.zip/node_modules/tslib/",\ - "packageDependencies": [\ - ["tslib", "npm:2.8.1"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["unique-filename", [\ - ["npm:5.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/unique-filename-npm-5.0.0-605f54f18e-10c0.zip/node_modules/unique-filename/",\ - "packageDependencies": [\ - ["unique-filename", "npm:5.0.0"],\ - ["unique-slug", "npm:6.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["unique-slug", [\ - ["npm:6.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/unique-slug-npm-6.0.0-f26b186e99-10c0.zip/node_modules/unique-slug/",\ - "packageDependencies": [\ - ["imurmurhash", "npm:0.1.4"],\ - ["unique-slug", "npm:6.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["uuid", [\ - ["npm:14.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/uuid-npm-14.0.0-5e662e945a-10c0.zip/node_modules/uuid/",\ - "packageDependencies": [\ - ["uuid", "npm:14.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["virtual-dom", [\ - ["npm:2.1.1", {\ - "packageLocation": "../../../.yarn/berry/cache/virtual-dom-npm-2.1.1-8a39158cf1-10c0.zip/node_modules/virtual-dom/",\ - "packageDependencies": [\ - ["browser-split", "npm:0.0.1"],\ - ["error", "npm:4.4.0"],\ - ["ev-store", "npm:7.0.0"],\ - ["global", "npm:4.4.0"],\ - ["is-object", "npm:1.0.2"],\ - ["next-tick", "npm:0.2.2"],\ - ["virtual-dom", "npm:2.1.1"],\ - ["x-is-array", "npm:0.1.0"],\ - ["x-is-string", "npm:0.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["vite", [\ - ["npm:8.0.12", {\ - "packageLocation": "../../../.yarn/berry/cache/vite-npm-8.0.12-550f27727c-10c0.zip/node_modules/vite/",\ - "packageDependencies": [\ - ["vite", "npm:8.0.12"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12", {\ - "packageLocation": "./.yarn/__virtual__/vite-virtual-da5fb0342c/4/.yarn/berry/cache/vite-npm-8.0.12-550f27727c-10c0.zip/node_modules/vite/",\ - "packageDependencies": [\ - ["@types/esbuild", null],\ - ["@types/jiti", null],\ - ["@types/less", null],\ - ["@types/node", null],\ - ["@types/sass", null],\ - ["@types/sass-embedded", null],\ - ["@types/stylus", null],\ - ["@types/sugarss", null],\ - ["@types/terser", null],\ - ["@types/tsx", null],\ - ["@types/vitejs__devtools", null],\ - ["@types/yaml", null],\ - ["@vitejs/devtools", null],\ - ["esbuild", null],\ - ["fsevents", "patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"],\ - ["jiti", null],\ - ["less", null],\ - ["lightningcss", "npm:1.32.0"],\ - ["picomatch", "npm:4.0.4"],\ - ["postcss", "npm:8.5.14"],\ - ["rolldown", "npm:1.0.0"],\ - ["sass", null],\ - ["sass-embedded", null],\ - ["stylus", null],\ - ["sugarss", null],\ - ["terser", null],\ - ["tinyglobby", "npm:0.2.16"],\ - ["tsx", null],\ - ["vite", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:8.0.12"],\ - ["yaml", null]\ - ],\ - "packagePeers": [\ - "@types/esbuild",\ - "@types/jiti",\ - "@types/less",\ - "@types/node",\ - "@types/sass-embedded",\ - "@types/sass",\ - "@types/stylus",\ - "@types/sugarss",\ - "@types/terser",\ - "@types/tsx",\ - "@types/vitejs__devtools",\ - "@types/yaml",\ - "@vitejs/devtools",\ - "esbuild",\ - "jiti",\ - "less",\ - "sass-embedded",\ - "sass",\ - "stylus",\ - "sugarss",\ - "terser",\ - "tsx",\ - "yaml"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["which", [\ - ["npm:6.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/which-npm-6.0.0-48f25f0ec8-10c0.zip/node_modules/which/",\ - "packageDependencies": [\ - ["isexe", "npm:3.1.1"],\ - ["which", "npm:6.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["x-is-array", [\ - ["npm:0.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/x-is-array-npm-0.1.0-c49b0e68b6-10c0.zip/node_modules/x-is-array/",\ - "packageDependencies": [\ - ["x-is-array", "npm:0.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["x-is-string", [\ - ["npm:0.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/x-is-string-npm-0.1.0-a254bc0c3e-10c0.zip/node_modules/x-is-string/",\ - "packageDependencies": [\ - ["x-is-string", "npm:0.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["xtend", [\ - ["npm:4.0.2", {\ - "packageLocation": "../../../.yarn/berry/cache/xtend-npm-4.0.2-7f2375736e-10c0.zip/node_modules/xtend/",\ - "packageDependencies": [\ - ["xtend", "npm:4.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["yallist", [\ - ["npm:4.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/yallist-npm-4.0.0-b493d9e907-10c0.zip/node_modules/yallist/",\ - "packageDependencies": [\ - ["yallist", "npm:4.0.0"]\ - ],\ - "linkType": "HARD"\ - }],\ - ["npm:5.0.0", {\ - "packageLocation": "../../../.yarn/berry/cache/yallist-npm-5.0.0-8732dd9f1c-10c0.zip/node_modules/yallist/",\ - "packageDependencies": [\ - ["yallist", "npm:5.0.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["zrender", [\ - ["npm:6.1.0", {\ - "packageLocation": "../../../.yarn/berry/cache/zrender-npm-6.1.0-025f06fccb-10c0.zip/node_modules/zrender/",\ - "packageDependencies": [\ - ["tslib", "npm:2.3.0"],\ - ["zrender", "npm:6.1.0"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]]\ - ]\ -}'; - -function $$SETUP_STATE(hydrateRuntimeState, basePath) { - return hydrateRuntimeState(JSON.parse(RAW_RUNTIME_STATE), {basePath: basePath || __dirname}); -} - -const fs = require('fs'); -const path = require('path'); -const crypto = require('crypto'); -const os = require('os'); -const events = require('events'); -const nodeUtils = require('util'); -const stream = require('stream'); -const zlib = require('zlib'); -const require$$0 = require('module'); -const StringDecoder = require('string_decoder'); -const url = require('url'); -const buffer = require('buffer'); -const readline = require('readline'); -const assert = require('assert'); - -const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e }; - -function _interopNamespace(e) { - if (e && e.__esModule) return e; - const n = Object.create(null); - if (e) { - for (const k in e) { - if (k !== 'default') { - const d = Object.getOwnPropertyDescriptor(e, k); - Object.defineProperty(n, k, d.get ? d : { - enumerable: true, - get: () => e[k] - }); - } - } - } - n.default = e; - return Object.freeze(n); -} - -const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); -const path__default = /*#__PURE__*/_interopDefaultLegacy(path); -const nodeUtils__namespace = /*#__PURE__*/_interopNamespace(nodeUtils); -const zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); -const require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); -const StringDecoder__default = /*#__PURE__*/_interopDefaultLegacy(StringDecoder); -const buffer__default = /*#__PURE__*/_interopDefaultLegacy(buffer); -const assert__default = /*#__PURE__*/_interopDefaultLegacy(assert); - -const S_IFMT = 61440; -const S_IFDIR = 16384; -const S_IFREG = 32768; -const S_IFLNK = 40960; -const SAFE_TIME = 456789e3; - -function makeError$1(code, message) { - return Object.assign(new Error(`${code}: ${message}`), { code }); -} -function EBUSY(message) { - return makeError$1(`EBUSY`, message); -} -function ENOSYS(message, reason) { - return makeError$1(`ENOSYS`, `${message}, ${reason}`); -} -function EINVAL(reason) { - return makeError$1(`EINVAL`, `invalid argument, ${reason}`); -} -function EBADF(reason) { - return makeError$1(`EBADF`, `bad file descriptor, ${reason}`); -} -function ENOENT(reason) { - return makeError$1(`ENOENT`, `no such file or directory, ${reason}`); -} -function ENOTDIR(reason) { - return makeError$1(`ENOTDIR`, `not a directory, ${reason}`); -} -function EISDIR(reason) { - return makeError$1(`EISDIR`, `illegal operation on a directory, ${reason}`); -} -function EEXIST(reason) { - return makeError$1(`EEXIST`, `file already exists, ${reason}`); -} -function EROFS(reason) { - return makeError$1(`EROFS`, `read-only filesystem, ${reason}`); -} -function ENOTEMPTY(reason) { - return makeError$1(`ENOTEMPTY`, `directory not empty, ${reason}`); -} -function EOPNOTSUPP(reason) { - return makeError$1(`EOPNOTSUPP`, `operation not supported, ${reason}`); -} -function ERR_DIR_CLOSED() { - return makeError$1(`ERR_DIR_CLOSED`, `Directory handle was closed`); -} - -const DEFAULT_MODE = S_IFREG | 420; -class StatEntry { - uid = 0; - gid = 0; - size = 0; - blksize = 0; - atimeMs = 0; - mtimeMs = 0; - ctimeMs = 0; - birthtimeMs = 0; - atime = /* @__PURE__ */ new Date(0); - mtime = /* @__PURE__ */ new Date(0); - ctime = /* @__PURE__ */ new Date(0); - birthtime = /* @__PURE__ */ new Date(0); - dev = 0; - ino = 0; - mode = DEFAULT_MODE; - nlink = 1; - rdev = 0; - blocks = 1; - isBlockDevice() { - return false; - } - isCharacterDevice() { - return false; - } - isDirectory() { - return (this.mode & S_IFMT) === S_IFDIR; - } - isFIFO() { - return false; - } - isFile() { - return (this.mode & S_IFMT) === S_IFREG; - } - isSocket() { - return false; - } - isSymbolicLink() { - return (this.mode & S_IFMT) === S_IFLNK; - } -} -class BigIntStatsEntry { - uid = BigInt(0); - gid = BigInt(0); - size = BigInt(0); - blksize = BigInt(0); - atimeMs = BigInt(0); - mtimeMs = BigInt(0); - ctimeMs = BigInt(0); - birthtimeMs = BigInt(0); - atimeNs = BigInt(0); - mtimeNs = BigInt(0); - ctimeNs = BigInt(0); - birthtimeNs = BigInt(0); - atime = /* @__PURE__ */ new Date(0); - mtime = /* @__PURE__ */ new Date(0); - ctime = /* @__PURE__ */ new Date(0); - birthtime = /* @__PURE__ */ new Date(0); - dev = BigInt(0); - ino = BigInt(0); - mode = BigInt(DEFAULT_MODE); - nlink = BigInt(1); - rdev = BigInt(0); - blocks = BigInt(1); - isBlockDevice() { - return false; - } - isCharacterDevice() { - return false; - } - isDirectory() { - return (this.mode & BigInt(S_IFMT)) === BigInt(S_IFDIR); - } - isFIFO() { - return false; - } - isFile() { - return (this.mode & BigInt(S_IFMT)) === BigInt(S_IFREG); - } - isSocket() { - return false; - } - isSymbolicLink() { - return (this.mode & BigInt(S_IFMT)) === BigInt(S_IFLNK); - } -} -function makeDefaultStats() { - return new StatEntry(); -} -function clearStats(stats) { - for (const key in stats) { - if (Object.hasOwn(stats, key)) { - const element = stats[key]; - if (typeof element === `number`) { - stats[key] = 0; - } else if (typeof element === `bigint`) { - stats[key] = BigInt(0); - } else if (nodeUtils__namespace.types.isDate(element)) { - stats[key] = /* @__PURE__ */ new Date(0); - } - } - } - return stats; -} -function convertToBigIntStats(stats) { - const bigintStats = new BigIntStatsEntry(); - for (const key in stats) { - if (Object.hasOwn(stats, key)) { - const element = stats[key]; - if (typeof element === `number`) { - bigintStats[key] = BigInt(element); - } else if (nodeUtils__namespace.types.isDate(element)) { - bigintStats[key] = new Date(element); - } - } - } - bigintStats.atimeNs = bigintStats.atimeMs * BigInt(1e6); - bigintStats.mtimeNs = bigintStats.mtimeMs * BigInt(1e6); - bigintStats.ctimeNs = bigintStats.ctimeMs * BigInt(1e6); - bigintStats.birthtimeNs = bigintStats.birthtimeMs * BigInt(1e6); - return bigintStats; -} -function areStatsEqual(a, b) { - if (a.atimeMs !== b.atimeMs) - return false; - if (a.birthtimeMs !== b.birthtimeMs) - return false; - if (a.blksize !== b.blksize) - return false; - if (a.blocks !== b.blocks) - return false; - if (a.ctimeMs !== b.ctimeMs) - return false; - if (a.dev !== b.dev) - return false; - if (a.gid !== b.gid) - return false; - if (a.ino !== b.ino) - return false; - if (a.isBlockDevice() !== b.isBlockDevice()) - return false; - if (a.isCharacterDevice() !== b.isCharacterDevice()) - return false; - if (a.isDirectory() !== b.isDirectory()) - return false; - if (a.isFIFO() !== b.isFIFO()) - return false; - if (a.isFile() !== b.isFile()) - return false; - if (a.isSocket() !== b.isSocket()) - return false; - if (a.isSymbolicLink() !== b.isSymbolicLink()) - return false; - if (a.mode !== b.mode) - return false; - if (a.mtimeMs !== b.mtimeMs) - return false; - if (a.nlink !== b.nlink) - return false; - if (a.rdev !== b.rdev) - return false; - if (a.size !== b.size) - return false; - if (a.uid !== b.uid) - return false; - const aN = a; - const bN = b; - if (aN.atimeNs !== bN.atimeNs) - return false; - if (aN.mtimeNs !== bN.mtimeNs) - return false; - if (aN.ctimeNs !== bN.ctimeNs) - return false; - if (aN.birthtimeNs !== bN.birthtimeNs) - return false; - return true; -} - -const PortablePath = { - root: `/`, - dot: `.`, - parent: `..` -}; -const Filename = { - home: `~`, - nodeModules: `node_modules`, - manifest: `package.json`, - lockfile: `yarn.lock`, - virtual: `__virtual__`, - /** - * @deprecated - */ - pnpJs: `.pnp.js`, - pnpCjs: `.pnp.cjs`, - pnpData: `.pnp.data.json`, - pnpEsmLoader: `.pnp.loader.mjs`, - rc: `.yarnrc.yml`, - env: `.env` -}; -const npath = Object.create(path__default.default); -const ppath = Object.create(path__default.default.posix); -npath.cwd = () => process.cwd(); -ppath.cwd = process.platform === `win32` ? () => toPortablePath(process.cwd()) : process.cwd; -if (process.platform === `win32`) { - ppath.resolve = (...segments) => { - if (segments.length > 0 && ppath.isAbsolute(segments[0])) { - return path__default.default.posix.resolve(...segments); - } else { - return path__default.default.posix.resolve(ppath.cwd(), ...segments); - } - }; -} -const contains = function(pathUtils, from, to) { - from = pathUtils.normalize(from); - to = pathUtils.normalize(to); - if (from === to) - return `.`; - if (!from.endsWith(pathUtils.sep)) - from = from + pathUtils.sep; - if (to.startsWith(from)) { - return to.slice(from.length); - } else { - return null; - } -}; -npath.contains = (from, to) => contains(npath, from, to); -ppath.contains = (from, to) => contains(ppath, from, to); -const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/; -const UNC_WINDOWS_PATH_REGEXP = /^\/\/(\.\/)?(.*)$/; -const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/; -const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/; -function fromPortablePathWin32(p) { - let portablePathMatch, uncPortablePathMatch; - if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP)) - p = portablePathMatch[1]; - else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP)) - p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`; - else - return p; - return p.replace(/\//g, `\\`); -} -function toPortablePathWin32(p) { - p = p.replace(/\\/g, `/`); - let windowsPathMatch, uncWindowsPathMatch; - if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP)) - p = `/${windowsPathMatch[1]}`; - else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP)) - p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`; - return p; -} -const toPortablePath = process.platform === `win32` ? toPortablePathWin32 : (p) => p; -const fromPortablePath = process.platform === `win32` ? fromPortablePathWin32 : (p) => p; -npath.fromPortablePath = fromPortablePath; -npath.toPortablePath = toPortablePath; -function convertPath(targetPathUtils, sourcePath) { - return targetPathUtils === npath ? fromPortablePath(sourcePath) : toPortablePath(sourcePath); -} - -const defaultTime = new Date(SAFE_TIME * 1e3); -const defaultTimeMs = defaultTime.getTime(); -async function copyPromise(destinationFs, destination, sourceFs, source, opts) { - const normalizedDestination = destinationFs.pathUtils.normalize(destination); - const normalizedSource = sourceFs.pathUtils.normalize(source); - const prelayout = []; - const postlayout = []; - const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : await sourceFs.lstatPromise(normalizedSource); - await destinationFs.mkdirpPromise(destinationFs.pathUtils.dirname(destination), { utimes: [atime, mtime] }); - await copyImpl(prelayout, postlayout, destinationFs, normalizedDestination, sourceFs, normalizedSource, { ...opts, didParentExist: true }); - for (const operation of prelayout) - await operation(); - await Promise.all(postlayout.map((operation) => { - return operation(); - })); -} -async function copyImpl(prelayout, postlayout, destinationFs, destination, sourceFs, source, opts) { - const destinationStat = opts.didParentExist ? await maybeLStat(destinationFs, destination) : null; - const sourceStat = await sourceFs.lstatPromise(source); - const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : sourceStat; - let updated; - switch (true) { - case sourceStat.isDirectory(): - { - updated = await copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - case sourceStat.isFile(): - { - updated = await copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - case sourceStat.isSymbolicLink(): - { - updated = await copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - default: { - throw new Error(`Unsupported file type (${sourceStat.mode})`); - } - } - if (opts.linkStrategy?.type !== `HardlinkFromIndex` || !sourceStat.isFile()) { - if (updated || destinationStat?.mtime?.getTime() !== mtime.getTime() || destinationStat?.atime?.getTime() !== atime.getTime()) { - postlayout.push(() => destinationFs.lutimesPromise(destination, atime, mtime)); - updated = true; - } - if (destinationStat === null || (destinationStat.mode & 511) !== (sourceStat.mode & 511)) { - postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 511)); - updated = true; - } - } - return updated; -} -async function maybeLStat(baseFs, p) { - try { - return await baseFs.lstatPromise(p); - } catch { - return null; - } -} -async function copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null && !destinationStat.isDirectory()) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - let updated = false; - if (destinationStat === null) { - prelayout.push(async () => { - try { - await destinationFs.mkdirPromise(destination, { mode: sourceStat.mode }); - } catch (err) { - if (err.code !== `EEXIST`) { - throw err; - } - } - }); - updated = true; - } - const entries = await sourceFs.readdirPromise(source); - const nextOpts = opts.didParentExist && !destinationStat ? { ...opts, didParentExist: false } : opts; - if (opts.stableSort) { - for (const entry of entries.sort()) { - if (await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts)) { - updated = true; - } - } - } else { - const entriesUpdateStatus = await Promise.all(entries.map(async (entry) => { - await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts); - })); - if (entriesUpdateStatus.some((status) => status)) { - updated = true; - } - } - return updated; -} -async function copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, linkStrategy) { - const sourceHash = await sourceFs.checksumFilePromise(source, { algorithm: `sha1` }); - const defaultMode = 420; - const sourceMode = sourceStat.mode & 511; - const indexFileName = `${sourceHash}${sourceMode !== defaultMode ? sourceMode.toString(8) : ``}`; - const indexPath = destinationFs.pathUtils.join(linkStrategy.indexPath, sourceHash.slice(0, 2), `${indexFileName}.dat`); - let AtomicBehavior; - ((AtomicBehavior2) => { - AtomicBehavior2[AtomicBehavior2["Lock"] = 0] = "Lock"; - AtomicBehavior2[AtomicBehavior2["Rename"] = 1] = "Rename"; - })(AtomicBehavior || (AtomicBehavior = {})); - let atomicBehavior = 1 /* Rename */; - let indexStat = await maybeLStat(destinationFs, indexPath); - if (destinationStat) { - const isDestinationHardlinkedFromIndex = indexStat && destinationStat.dev === indexStat.dev && destinationStat.ino === indexStat.ino; - const isIndexModified = indexStat?.mtimeMs !== defaultTimeMs; - if (isDestinationHardlinkedFromIndex) { - if (isIndexModified && linkStrategy.autoRepair) { - atomicBehavior = 0 /* Lock */; - indexStat = null; - } - } - if (!isDestinationHardlinkedFromIndex) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - } - const tempPath = !indexStat && atomicBehavior === 1 /* Rename */ ? `${indexPath}.${Math.floor(Math.random() * 4294967296).toString(16).padStart(8, `0`)}` : null; - let tempPathCleaned = false; - prelayout.push(async () => { - if (!indexStat) { - if (atomicBehavior === 0 /* Lock */) { - await destinationFs.lockPromise(indexPath, async () => { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(indexPath, content); - }); - } - if (atomicBehavior === 1 /* Rename */ && tempPath) { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(tempPath, content); - try { - await destinationFs.linkPromise(tempPath, indexPath); - } catch (err) { - if (err.code === `EEXIST`) { - tempPathCleaned = true; - await destinationFs.unlinkPromise(tempPath); - } else { - throw err; - } - } - } - } - if (!destinationStat) { - await destinationFs.linkPromise(indexPath, destination); - } - }); - postlayout.push(async () => { - if (!indexStat) { - await destinationFs.lutimesPromise(indexPath, defaultTime, defaultTime); - if (sourceMode !== defaultMode) { - await destinationFs.chmodPromise(indexPath, sourceMode); - } - } - if (tempPath && !tempPathCleaned) { - await destinationFs.unlinkPromise(tempPath); - } - }); - return false; -} -async function copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - prelayout.push(async () => { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(destination, content); - }); - return true; -} -async function copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (opts.linkStrategy?.type === `HardlinkFromIndex`) { - return copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, opts.linkStrategy); - } else { - return copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } -} -async function copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - prelayout.push(async () => { - await destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination); - }); - return true; -} - -class CustomDir { - constructor(path, nextDirent, opts = {}) { - this.path = path; - this.nextDirent = nextDirent; - this.opts = opts; - } - closed = false; - throwIfClosed() { - if (this.closed) { - throw ERR_DIR_CLOSED(); - } - } - async *[Symbol.asyncIterator]() { - try { - let dirent; - while ((dirent = await this.read()) !== null) { - yield dirent; - } - } finally { - await this.close(); - } - } - read(cb) { - const dirent = this.readSync(); - if (typeof cb !== `undefined`) - return cb(null, dirent); - return Promise.resolve(dirent); - } - readSync() { - this.throwIfClosed(); - return this.nextDirent(); - } - close(cb) { - this.closeSync(); - if (typeof cb !== `undefined`) - return cb(null); - return Promise.resolve(); - } - closeSync() { - this.throwIfClosed(); - this.opts.onClose?.(); - this.closed = true; - } -} -function opendir(fakeFs, path, entries, opts) { - const nextDirent = () => { - const filename = entries.shift(); - if (typeof filename === `undefined`) - return null; - const entryPath = fakeFs.pathUtils.join(path, filename); - return Object.assign(fakeFs.statSync(entryPath), { - name: filename, - path: void 0 - }); - }; - return new CustomDir(path, nextDirent, opts); -} - -function assertStatus(current, expected) { - if (current !== expected) { - throw new Error(`Invalid StatWatcher status: expected '${expected}', got '${current}'`); - } -} -class CustomStatWatcher extends events.EventEmitter { - fakeFs; - path; - bigint; - status = "ready" /* Ready */; - changeListeners = /* @__PURE__ */ new Map(); - lastStats; - startTimeout = null; - static create(fakeFs, path, opts) { - const statWatcher = new CustomStatWatcher(fakeFs, path, opts); - statWatcher.start(); - return statWatcher; - } - constructor(fakeFs, path, { bigint = false } = {}) { - super(); - this.fakeFs = fakeFs; - this.path = path; - this.bigint = bigint; - this.lastStats = this.stat(); - } - start() { - assertStatus(this.status, "ready" /* Ready */); - this.status = "running" /* Running */; - this.startTimeout = setTimeout(() => { - this.startTimeout = null; - if (!this.fakeFs.existsSync(this.path)) { - this.emit("change" /* Change */, this.lastStats, this.lastStats); - } - }, 3); - } - stop() { - assertStatus(this.status, "running" /* Running */); - this.status = "stopped" /* Stopped */; - if (this.startTimeout !== null) { - clearTimeout(this.startTimeout); - this.startTimeout = null; - } - this.emit("stop" /* Stop */); - } - stat() { - try { - return this.fakeFs.statSync(this.path, { bigint: this.bigint }); - } catch { - const statInstance = this.bigint ? new BigIntStatsEntry() : new StatEntry(); - return clearStats(statInstance); - } - } - /** - * Creates an interval whose callback compares the current stats with the previous stats and notifies all listeners in case of changes. - * - * @param opts.persistent Decides whether the interval should be immediately unref-ed. - */ - makeInterval(opts) { - const interval = setInterval(() => { - const currentStats = this.stat(); - const previousStats = this.lastStats; - if (areStatsEqual(currentStats, previousStats)) - return; - this.lastStats = currentStats; - this.emit("change" /* Change */, currentStats, previousStats); - }, opts.interval); - return opts.persistent ? interval : interval.unref(); - } - /** - * Registers a listener and assigns it an interval. - */ - registerChangeListener(listener, opts) { - this.addListener("change" /* Change */, listener); - this.changeListeners.set(listener, this.makeInterval(opts)); - } - /** - * Unregisters the listener and clears the assigned interval. - */ - unregisterChangeListener(listener) { - this.removeListener("change" /* Change */, listener); - const interval = this.changeListeners.get(listener); - if (typeof interval !== `undefined`) - clearInterval(interval); - this.changeListeners.delete(listener); - } - /** - * Unregisters all listeners and clears all assigned intervals. - */ - unregisterAllChangeListeners() { - for (const listener of this.changeListeners.keys()) { - this.unregisterChangeListener(listener); - } - } - hasChangeListeners() { - return this.changeListeners.size > 0; - } - /** - * Refs all stored intervals. - */ - ref() { - for (const interval of this.changeListeners.values()) - interval.ref(); - return this; - } - /** - * Unrefs all stored intervals. - */ - unref() { - for (const interval of this.changeListeners.values()) - interval.unref(); - return this; - } -} - -const statWatchersByFakeFS = /* @__PURE__ */ new WeakMap(); -function watchFile(fakeFs, path, a, b) { - let bigint; - let persistent; - let interval; - let listener; - switch (typeof a) { - case `function`: - { - bigint = false; - persistent = true; - interval = 5007; - listener = a; - } - break; - default: - { - ({ - bigint = false, - persistent = true, - interval = 5007 - } = a); - listener = b; - } - break; - } - let statWatchers = statWatchersByFakeFS.get(fakeFs); - if (typeof statWatchers === `undefined`) - statWatchersByFakeFS.set(fakeFs, statWatchers = /* @__PURE__ */ new Map()); - let statWatcher = statWatchers.get(path); - if (typeof statWatcher === `undefined`) { - statWatcher = CustomStatWatcher.create(fakeFs, path, { bigint }); - statWatchers.set(path, statWatcher); - } - statWatcher.registerChangeListener(listener, { persistent, interval }); - return statWatcher; -} -function unwatchFile(fakeFs, path, cb) { - const statWatchers = statWatchersByFakeFS.get(fakeFs); - if (typeof statWatchers === `undefined`) - return; - const statWatcher = statWatchers.get(path); - if (typeof statWatcher === `undefined`) - return; - if (typeof cb === `undefined`) - statWatcher.unregisterAllChangeListeners(); - else - statWatcher.unregisterChangeListener(cb); - if (!statWatcher.hasChangeListeners()) { - statWatcher.stop(); - statWatchers.delete(path); - } -} -function unwatchAllFiles(fakeFs) { - const statWatchers = statWatchersByFakeFS.get(fakeFs); - if (typeof statWatchers === `undefined`) - return; - for (const path of statWatchers.keys()) { - unwatchFile(fakeFs, path); - } -} - -class FakeFS { - pathUtils; - constructor(pathUtils) { - this.pathUtils = pathUtils; - } - async *genTraversePromise(init, { stableSort = false } = {}) { - const stack = [init]; - while (stack.length > 0) { - const p = stack.shift(); - const entry = await this.lstatPromise(p); - if (entry.isDirectory()) { - const entries = await this.readdirPromise(p); - if (stableSort) { - for (const entry2 of entries.sort()) { - stack.push(this.pathUtils.join(p, entry2)); - } - } else { - throw new Error(`Not supported`); - } - } else { - yield p; - } - } - } - async checksumFilePromise(path, { algorithm = `sha512` } = {}) { - const fd = await this.openPromise(path, `r`); - try { - const CHUNK_SIZE = 65536; - const chunk = Buffer.allocUnsafeSlow(CHUNK_SIZE); - const hash = crypto.createHash(algorithm); - let bytesRead = 0; - while ((bytesRead = await this.readPromise(fd, chunk, 0, CHUNK_SIZE)) !== 0) - hash.update(bytesRead === CHUNK_SIZE ? chunk : chunk.slice(0, bytesRead)); - return hash.digest(`hex`); - } finally { - await this.closePromise(fd); - } - } - async removePromise(p, { recursive = true, maxRetries = 5 } = {}) { - let stat; - try { - stat = await this.lstatPromise(p); - } catch (error) { - if (error.code === `ENOENT`) { - return; - } else { - throw error; - } - } - if (stat.isDirectory()) { - if (recursive) { - const entries = await this.readdirPromise(p); - await Promise.all(entries.map((entry) => { - return this.removePromise(this.pathUtils.resolve(p, entry)); - })); - } - for (let t = 0; t <= maxRetries; t++) { - try { - await this.rmdirPromise(p); - break; - } catch (error) { - if (error.code !== `EBUSY` && error.code !== `ENOTEMPTY`) { - throw error; - } else if (t < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, t * 100)); - } - } - } - } else { - await this.unlinkPromise(p); - } - } - removeSync(p, { recursive = true } = {}) { - let stat; - try { - stat = this.lstatSync(p); - } catch (error) { - if (error.code === `ENOENT`) { - return; - } else { - throw error; - } - } - if (stat.isDirectory()) { - if (recursive) - for (const entry of this.readdirSync(p)) - this.removeSync(this.pathUtils.resolve(p, entry)); - this.rmdirSync(p); - } else { - this.unlinkSync(p); - } - } - async mkdirpPromise(p, { chmod, utimes } = {}) { - p = this.resolve(p); - if (p === this.pathUtils.dirname(p)) - return void 0; - const parts = p.split(this.pathUtils.sep); - let createdDirectory; - for (let u = 2; u <= parts.length; ++u) { - const subPath = parts.slice(0, u).join(this.pathUtils.sep); - if (!this.existsSync(subPath)) { - try { - await this.mkdirPromise(subPath); - } catch (error) { - if (error.code === `EEXIST`) { - continue; - } else { - throw error; - } - } - createdDirectory ??= subPath; - if (chmod != null) - await this.chmodPromise(subPath, chmod); - if (utimes != null) { - await this.utimesPromise(subPath, utimes[0], utimes[1]); - } else { - const parentStat = await this.statPromise(this.pathUtils.dirname(subPath)); - await this.utimesPromise(subPath, parentStat.atime, parentStat.mtime); - } - } - } - return createdDirectory; - } - mkdirpSync(p, { chmod, utimes } = {}) { - p = this.resolve(p); - if (p === this.pathUtils.dirname(p)) - return void 0; - const parts = p.split(this.pathUtils.sep); - let createdDirectory; - for (let u = 2; u <= parts.length; ++u) { - const subPath = parts.slice(0, u).join(this.pathUtils.sep); - if (!this.existsSync(subPath)) { - try { - this.mkdirSync(subPath); - } catch (error) { - if (error.code === `EEXIST`) { - continue; - } else { - throw error; - } - } - createdDirectory ??= subPath; - if (chmod != null) - this.chmodSync(subPath, chmod); - if (utimes != null) { - this.utimesSync(subPath, utimes[0], utimes[1]); - } else { - const parentStat = this.statSync(this.pathUtils.dirname(subPath)); - this.utimesSync(subPath, parentStat.atime, parentStat.mtime); - } - } - } - return createdDirectory; - } - async copyPromise(destination, source, { baseFs = this, overwrite = true, stableSort = false, stableTime = false, linkStrategy = null } = {}) { - return await copyPromise(this, destination, baseFs, source, { overwrite, stableSort, stableTime, linkStrategy }); - } - copySync(destination, source, { baseFs = this, overwrite = true } = {}) { - const stat = baseFs.lstatSync(source); - const exists = this.existsSync(destination); - if (stat.isDirectory()) { - this.mkdirpSync(destination); - const directoryListing = baseFs.readdirSync(source); - for (const entry of directoryListing) { - this.copySync(this.pathUtils.join(destination, entry), baseFs.pathUtils.join(source, entry), { baseFs, overwrite }); - } - } else if (stat.isFile()) { - if (!exists || overwrite) { - if (exists) - this.removeSync(destination); - const content = baseFs.readFileSync(source); - this.writeFileSync(destination, content); - } - } else if (stat.isSymbolicLink()) { - if (!exists || overwrite) { - if (exists) - this.removeSync(destination); - const target = baseFs.readlinkSync(source); - this.symlinkSync(convertPath(this.pathUtils, target), destination); - } - } else { - throw new Error(`Unsupported file type (file: ${source}, mode: 0o${stat.mode.toString(8).padStart(6, `0`)})`); - } - const mode = stat.mode & 511; - this.chmodSync(destination, mode); - } - async changeFilePromise(p, content, opts = {}) { - if (Buffer.isBuffer(content)) { - return this.changeFileBufferPromise(p, content, opts); - } else { - return this.changeFileTextPromise(p, content, opts); - } - } - async changeFileBufferPromise(p, content, { mode } = {}) { - let current = Buffer.alloc(0); - try { - current = await this.readFilePromise(p); - } catch { - } - if (Buffer.compare(current, content) === 0) - return; - await this.writeFilePromise(p, content, { mode }); - } - async changeFileTextPromise(p, content, { automaticNewlines, mode } = {}) { - let current = ``; - try { - current = await this.readFilePromise(p, `utf8`); - } catch { - } - const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; - if (current === normalizedContent) - return; - await this.writeFilePromise(p, normalizedContent, { mode }); - } - changeFileSync(p, content, opts = {}) { - if (Buffer.isBuffer(content)) { - return this.changeFileBufferSync(p, content, opts); - } else { - return this.changeFileTextSync(p, content, opts); - } - } - changeFileBufferSync(p, content, { mode } = {}) { - let current = Buffer.alloc(0); - try { - current = this.readFileSync(p); - } catch { - } - if (Buffer.compare(current, content) === 0) - return; - this.writeFileSync(p, content, { mode }); - } - changeFileTextSync(p, content, { automaticNewlines = false, mode } = {}) { - let current = ``; - try { - current = this.readFileSync(p, `utf8`); - } catch { - } - const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; - if (current === normalizedContent) - return; - this.writeFileSync(p, normalizedContent, { mode }); - } - async movePromise(fromP, toP) { - try { - await this.renamePromise(fromP, toP); - } catch (error) { - if (error.code === `EXDEV`) { - await this.copyPromise(toP, fromP); - await this.removePromise(fromP); - } else { - throw error; - } - } - } - moveSync(fromP, toP) { - try { - this.renameSync(fromP, toP); - } catch (error) { - if (error.code === `EXDEV`) { - this.copySync(toP, fromP); - this.removeSync(fromP); - } else { - throw error; - } - } - } - async lockPromise(affectedPath, callback) { - const lockPath = `${affectedPath}.flock`; - const interval = 1e3 / 60; - const startTime = Date.now(); - let fd = null; - const isAlive = async () => { - let pid; - try { - [pid] = await this.readJsonPromise(lockPath); - } catch { - return Date.now() - startTime < 500; - } - try { - process.kill(pid, 0); - return true; - } catch { - return false; - } - }; - while (fd === null) { - try { - fd = await this.openPromise(lockPath, `wx`); - } catch (error) { - if (error.code === `EEXIST`) { - if (!await isAlive()) { - try { - await this.unlinkPromise(lockPath); - continue; - } catch { - } - } - if (Date.now() - startTime < 60 * 1e3) { - await new Promise((resolve) => setTimeout(resolve, interval)); - } else { - throw new Error(`Couldn't acquire a lock in a reasonable time (via ${lockPath})`); - } - } else { - throw error; - } - } - } - await this.writePromise(fd, JSON.stringify([process.pid])); - try { - return await callback(); - } finally { - try { - await this.closePromise(fd); - await this.unlinkPromise(lockPath); - } catch { - } - } - } - async readJsonPromise(p) { - const content = await this.readFilePromise(p, `utf8`); - try { - return JSON.parse(content); - } catch (error) { - error.message += ` (in ${p})`; - throw error; - } - } - readJsonSync(p) { - const content = this.readFileSync(p, `utf8`); - try { - return JSON.parse(content); - } catch (error) { - error.message += ` (in ${p})`; - throw error; - } - } - async writeJsonPromise(p, data, { compact = false } = {}) { - const space = compact ? 0 : 2; - return await this.writeFilePromise(p, `${JSON.stringify(data, null, space)} -`); - } - writeJsonSync(p, data, { compact = false } = {}) { - const space = compact ? 0 : 2; - return this.writeFileSync(p, `${JSON.stringify(data, null, space)} -`); - } - async preserveTimePromise(p, cb) { - const stat = await this.lstatPromise(p); - const result = await cb(); - if (typeof result !== `undefined`) - p = result; - await this.lutimesPromise(p, stat.atime, stat.mtime); - } - async preserveTimeSync(p, cb) { - const stat = this.lstatSync(p); - const result = cb(); - if (typeof result !== `undefined`) - p = result; - this.lutimesSync(p, stat.atime, stat.mtime); - } -} -class BasePortableFakeFS extends FakeFS { - constructor() { - super(ppath); - } -} -function getEndOfLine(content) { - const matches = content.match(/\r?\n/g); - if (matches === null) - return os.EOL; - const crlf = matches.filter((nl) => nl === `\r -`).length; - const lf = matches.length - crlf; - return crlf > lf ? `\r -` : ` -`; -} -function normalizeLineEndings(originalContent, newContent) { - return newContent.replace(/\r?\n/g, getEndOfLine(originalContent)); -} - -class ProxiedFS extends FakeFS { - getExtractHint(hints) { - return this.baseFs.getExtractHint(hints); - } - resolve(path) { - return this.mapFromBase(this.baseFs.resolve(this.mapToBase(path))); - } - getRealPath() { - return this.mapFromBase(this.baseFs.getRealPath()); - } - async openPromise(p, flags, mode) { - return this.baseFs.openPromise(this.mapToBase(p), flags, mode); - } - openSync(p, flags, mode) { - return this.baseFs.openSync(this.mapToBase(p), flags, mode); - } - async opendirPromise(p, opts) { - return Object.assign(await this.baseFs.opendirPromise(this.mapToBase(p), opts), { path: p }); - } - opendirSync(p, opts) { - return Object.assign(this.baseFs.opendirSync(this.mapToBase(p), opts), { path: p }); - } - async readPromise(fd, buffer, offset, length, position) { - return await this.baseFs.readPromise(fd, buffer, offset, length, position); - } - readSync(fd, buffer, offset, length, position) { - return this.baseFs.readSync(fd, buffer, offset, length, position); - } - async writePromise(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return await this.baseFs.writePromise(fd, buffer, offset); - } else { - return await this.baseFs.writePromise(fd, buffer, offset, length, position); - } - } - writeSync(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return this.baseFs.writeSync(fd, buffer, offset); - } else { - return this.baseFs.writeSync(fd, buffer, offset, length, position); - } - } - async closePromise(fd) { - return this.baseFs.closePromise(fd); - } - closeSync(fd) { - this.baseFs.closeSync(fd); - } - createReadStream(p, opts) { - return this.baseFs.createReadStream(p !== null ? this.mapToBase(p) : p, opts); - } - createWriteStream(p, opts) { - return this.baseFs.createWriteStream(p !== null ? this.mapToBase(p) : p, opts); - } - async realpathPromise(p) { - return this.mapFromBase(await this.baseFs.realpathPromise(this.mapToBase(p))); - } - realpathSync(p) { - return this.mapFromBase(this.baseFs.realpathSync(this.mapToBase(p))); - } - async existsPromise(p) { - return this.baseFs.existsPromise(this.mapToBase(p)); - } - existsSync(p) { - return this.baseFs.existsSync(this.mapToBase(p)); - } - accessSync(p, mode) { - return this.baseFs.accessSync(this.mapToBase(p), mode); - } - async accessPromise(p, mode) { - return this.baseFs.accessPromise(this.mapToBase(p), mode); - } - async statPromise(p, opts) { - return this.baseFs.statPromise(this.mapToBase(p), opts); - } - statSync(p, opts) { - return this.baseFs.statSync(this.mapToBase(p), opts); - } - async fstatPromise(fd, opts) { - return this.baseFs.fstatPromise(fd, opts); - } - fstatSync(fd, opts) { - return this.baseFs.fstatSync(fd, opts); - } - lstatPromise(p, opts) { - return this.baseFs.lstatPromise(this.mapToBase(p), opts); - } - lstatSync(p, opts) { - return this.baseFs.lstatSync(this.mapToBase(p), opts); - } - async fchmodPromise(fd, mask) { - return this.baseFs.fchmodPromise(fd, mask); - } - fchmodSync(fd, mask) { - return this.baseFs.fchmodSync(fd, mask); - } - async chmodPromise(p, mask) { - return this.baseFs.chmodPromise(this.mapToBase(p), mask); - } - chmodSync(p, mask) { - return this.baseFs.chmodSync(this.mapToBase(p), mask); - } - async fchownPromise(fd, uid, gid) { - return this.baseFs.fchownPromise(fd, uid, gid); - } - fchownSync(fd, uid, gid) { - return this.baseFs.fchownSync(fd, uid, gid); - } - async chownPromise(p, uid, gid) { - return this.baseFs.chownPromise(this.mapToBase(p), uid, gid); - } - chownSync(p, uid, gid) { - return this.baseFs.chownSync(this.mapToBase(p), uid, gid); - } - async renamePromise(oldP, newP) { - return this.baseFs.renamePromise(this.mapToBase(oldP), this.mapToBase(newP)); - } - renameSync(oldP, newP) { - return this.baseFs.renameSync(this.mapToBase(oldP), this.mapToBase(newP)); - } - async copyFilePromise(sourceP, destP, flags = 0) { - return this.baseFs.copyFilePromise(this.mapToBase(sourceP), this.mapToBase(destP), flags); - } - copyFileSync(sourceP, destP, flags = 0) { - return this.baseFs.copyFileSync(this.mapToBase(sourceP), this.mapToBase(destP), flags); - } - async appendFilePromise(p, content, opts) { - return this.baseFs.appendFilePromise(this.fsMapToBase(p), content, opts); - } - appendFileSync(p, content, opts) { - return this.baseFs.appendFileSync(this.fsMapToBase(p), content, opts); - } - async writeFilePromise(p, content, opts) { - return this.baseFs.writeFilePromise(this.fsMapToBase(p), content, opts); - } - writeFileSync(p, content, opts) { - return this.baseFs.writeFileSync(this.fsMapToBase(p), content, opts); - } - async unlinkPromise(p) { - return this.baseFs.unlinkPromise(this.mapToBase(p)); - } - unlinkSync(p) { - return this.baseFs.unlinkSync(this.mapToBase(p)); - } - async utimesPromise(p, atime, mtime) { - return this.baseFs.utimesPromise(this.mapToBase(p), atime, mtime); - } - utimesSync(p, atime, mtime) { - return this.baseFs.utimesSync(this.mapToBase(p), atime, mtime); - } - async lutimesPromise(p, atime, mtime) { - return this.baseFs.lutimesPromise(this.mapToBase(p), atime, mtime); - } - lutimesSync(p, atime, mtime) { - return this.baseFs.lutimesSync(this.mapToBase(p), atime, mtime); - } - async mkdirPromise(p, opts) { - return this.baseFs.mkdirPromise(this.mapToBase(p), opts); - } - mkdirSync(p, opts) { - return this.baseFs.mkdirSync(this.mapToBase(p), opts); - } - async rmdirPromise(p, opts) { - return this.baseFs.rmdirPromise(this.mapToBase(p), opts); - } - rmdirSync(p, opts) { - return this.baseFs.rmdirSync(this.mapToBase(p), opts); - } - async rmPromise(p, opts) { - return this.baseFs.rmPromise(this.mapToBase(p), opts); - } - rmSync(p, opts) { - return this.baseFs.rmSync(this.mapToBase(p), opts); - } - async linkPromise(existingP, newP) { - return this.baseFs.linkPromise(this.mapToBase(existingP), this.mapToBase(newP)); - } - linkSync(existingP, newP) { - return this.baseFs.linkSync(this.mapToBase(existingP), this.mapToBase(newP)); - } - async symlinkPromise(target, p, type) { - const mappedP = this.mapToBase(p); - if (this.pathUtils.isAbsolute(target)) - return this.baseFs.symlinkPromise(this.mapToBase(target), mappedP, type); - const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); - const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); - return this.baseFs.symlinkPromise(mappedTarget, mappedP, type); - } - symlinkSync(target, p, type) { - const mappedP = this.mapToBase(p); - if (this.pathUtils.isAbsolute(target)) - return this.baseFs.symlinkSync(this.mapToBase(target), mappedP, type); - const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); - const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); - return this.baseFs.symlinkSync(mappedTarget, mappedP, type); - } - async readFilePromise(p, encoding) { - return this.baseFs.readFilePromise(this.fsMapToBase(p), encoding); - } - readFileSync(p, encoding) { - return this.baseFs.readFileSync(this.fsMapToBase(p), encoding); - } - readdirPromise(p, opts) { - return this.baseFs.readdirPromise(this.mapToBase(p), opts); - } - readdirSync(p, opts) { - return this.baseFs.readdirSync(this.mapToBase(p), opts); - } - async readlinkPromise(p) { - return this.mapFromBase(await this.baseFs.readlinkPromise(this.mapToBase(p))); - } - readlinkSync(p) { - return this.mapFromBase(this.baseFs.readlinkSync(this.mapToBase(p))); - } - async truncatePromise(p, len) { - return this.baseFs.truncatePromise(this.mapToBase(p), len); - } - truncateSync(p, len) { - return this.baseFs.truncateSync(this.mapToBase(p), len); - } - async ftruncatePromise(fd, len) { - return this.baseFs.ftruncatePromise(fd, len); - } - ftruncateSync(fd, len) { - return this.baseFs.ftruncateSync(fd, len); - } - watch(p, a, b) { - return this.baseFs.watch( - this.mapToBase(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - watchFile(p, a, b) { - return this.baseFs.watchFile( - this.mapToBase(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - unwatchFile(p, cb) { - return this.baseFs.unwatchFile(this.mapToBase(p), cb); - } - fsMapToBase(p) { - if (typeof p === `number`) { - return p; - } else { - return this.mapToBase(p); - } - } -} - -function direntToPortable(dirent) { - const portableDirent = dirent; - if (typeof dirent.path === `string`) - portableDirent.path = npath.toPortablePath(dirent.path); - return portableDirent; -} -class NodeFS extends BasePortableFakeFS { - realFs; - constructor(realFs = fs__default.default) { - super(); - this.realFs = realFs; - } - getExtractHint() { - return false; - } - getRealPath() { - return PortablePath.root; - } - resolve(p) { - return ppath.resolve(p); - } - async openPromise(p, flags, mode) { - return await new Promise((resolve, reject) => { - this.realFs.open(npath.fromPortablePath(p), flags, mode, this.makeCallback(resolve, reject)); - }); - } - openSync(p, flags, mode) { - return this.realFs.openSync(npath.fromPortablePath(p), flags, mode); - } - async opendirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (typeof opts !== `undefined`) { - this.realFs.opendir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.opendir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }).then((dir) => { - const dirWithFixedPath = dir; - Object.defineProperty(dirWithFixedPath, `path`, { - value: p, - configurable: true, - writable: true - }); - return dirWithFixedPath; - }); - } - opendirSync(p, opts) { - const dir = typeof opts !== `undefined` ? this.realFs.opendirSync(npath.fromPortablePath(p), opts) : this.realFs.opendirSync(npath.fromPortablePath(p)); - const dirWithFixedPath = dir; - Object.defineProperty(dirWithFixedPath, `path`, { - value: p, - configurable: true, - writable: true - }); - return dirWithFixedPath; - } - async readPromise(fd, buffer, offset = 0, length = 0, position = -1) { - return await new Promise((resolve, reject) => { - this.realFs.read(fd, buffer, offset, length, position, (error, bytesRead) => { - if (error) { - reject(error); - } else { - resolve(bytesRead); - } - }); - }); - } - readSync(fd, buffer, offset, length, position) { - return this.realFs.readSync(fd, buffer, offset, length, position); - } - async writePromise(fd, buffer, offset, length, position) { - return await new Promise((resolve, reject) => { - if (typeof buffer === `string`) { - return this.realFs.write(fd, buffer, offset, this.makeCallback(resolve, reject)); - } else { - return this.realFs.write(fd, buffer, offset, length, position, this.makeCallback(resolve, reject)); - } - }); - } - writeSync(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return this.realFs.writeSync(fd, buffer, offset); - } else { - return this.realFs.writeSync(fd, buffer, offset, length, position); - } - } - async closePromise(fd) { - await new Promise((resolve, reject) => { - this.realFs.close(fd, this.makeCallback(resolve, reject)); - }); - } - closeSync(fd) { - this.realFs.closeSync(fd); - } - createReadStream(p, opts) { - const realPath = p !== null ? npath.fromPortablePath(p) : p; - return this.realFs.createReadStream(realPath, opts); - } - createWriteStream(p, opts) { - const realPath = p !== null ? npath.fromPortablePath(p) : p; - return this.realFs.createWriteStream(realPath, opts); - } - async realpathPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.realpath(npath.fromPortablePath(p), {}, this.makeCallback(resolve, reject)); - }).then((path) => { - return npath.toPortablePath(path); - }); - } - realpathSync(p) { - return npath.toPortablePath(this.realFs.realpathSync(npath.fromPortablePath(p), {})); - } - async existsPromise(p) { - return await new Promise((resolve) => { - this.realFs.exists(npath.fromPortablePath(p), resolve); - }); - } - accessSync(p, mode) { - return this.realFs.accessSync(npath.fromPortablePath(p), mode); - } - async accessPromise(p, mode) { - return await new Promise((resolve, reject) => { - this.realFs.access(npath.fromPortablePath(p), mode, this.makeCallback(resolve, reject)); - }); - } - existsSync(p) { - return this.realFs.existsSync(npath.fromPortablePath(p)); - } - async statPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.stat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.stat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - statSync(p, opts) { - if (opts) { - return this.realFs.statSync(npath.fromPortablePath(p), opts); - } else { - return this.realFs.statSync(npath.fromPortablePath(p)); - } - } - async fstatPromise(fd, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.fstat(fd, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.fstat(fd, this.makeCallback(resolve, reject)); - } - }); - } - fstatSync(fd, opts) { - if (opts) { - return this.realFs.fstatSync(fd, opts); - } else { - return this.realFs.fstatSync(fd); - } - } - async lstatPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.lstat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.lstat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - lstatSync(p, opts) { - if (opts) { - return this.realFs.lstatSync(npath.fromPortablePath(p), opts); - } else { - return this.realFs.lstatSync(npath.fromPortablePath(p)); - } - } - async fchmodPromise(fd, mask) { - return await new Promise((resolve, reject) => { - this.realFs.fchmod(fd, mask, this.makeCallback(resolve, reject)); - }); - } - fchmodSync(fd, mask) { - return this.realFs.fchmodSync(fd, mask); - } - async chmodPromise(p, mask) { - return await new Promise((resolve, reject) => { - this.realFs.chmod(npath.fromPortablePath(p), mask, this.makeCallback(resolve, reject)); - }); - } - chmodSync(p, mask) { - return this.realFs.chmodSync(npath.fromPortablePath(p), mask); - } - async fchownPromise(fd, uid, gid) { - return await new Promise((resolve, reject) => { - this.realFs.fchown(fd, uid, gid, this.makeCallback(resolve, reject)); - }); - } - fchownSync(fd, uid, gid) { - return this.realFs.fchownSync(fd, uid, gid); - } - async chownPromise(p, uid, gid) { - return await new Promise((resolve, reject) => { - this.realFs.chown(npath.fromPortablePath(p), uid, gid, this.makeCallback(resolve, reject)); - }); - } - chownSync(p, uid, gid) { - return this.realFs.chownSync(npath.fromPortablePath(p), uid, gid); - } - async renamePromise(oldP, newP) { - return await new Promise((resolve, reject) => { - this.realFs.rename(npath.fromPortablePath(oldP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); - }); - } - renameSync(oldP, newP) { - return this.realFs.renameSync(npath.fromPortablePath(oldP), npath.fromPortablePath(newP)); - } - async copyFilePromise(sourceP, destP, flags = 0) { - return await new Promise((resolve, reject) => { - this.realFs.copyFile(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags, this.makeCallback(resolve, reject)); - }); - } - copyFileSync(sourceP, destP, flags = 0) { - return this.realFs.copyFileSync(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags); - } - async appendFilePromise(p, content, opts) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.appendFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.appendFile(fsNativePath, content, this.makeCallback(resolve, reject)); - } - }); - } - appendFileSync(p, content, opts) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.appendFileSync(fsNativePath, content, opts); - } else { - this.realFs.appendFileSync(fsNativePath, content); - } - } - async writeFilePromise(p, content, opts) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.writeFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.writeFile(fsNativePath, content, this.makeCallback(resolve, reject)); - } - }); - } - writeFileSync(p, content, opts) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.writeFileSync(fsNativePath, content, opts); - } else { - this.realFs.writeFileSync(fsNativePath, content); - } - } - async unlinkPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.unlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - }); - } - unlinkSync(p) { - return this.realFs.unlinkSync(npath.fromPortablePath(p)); - } - async utimesPromise(p, atime, mtime) { - return await new Promise((resolve, reject) => { - this.realFs.utimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); - }); - } - utimesSync(p, atime, mtime) { - this.realFs.utimesSync(npath.fromPortablePath(p), atime, mtime); - } - async lutimesPromise(p, atime, mtime) { - return await new Promise((resolve, reject) => { - this.realFs.lutimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); - }); - } - lutimesSync(p, atime, mtime) { - this.realFs.lutimesSync(npath.fromPortablePath(p), atime, mtime); - } - async mkdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - this.realFs.mkdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - }); - } - mkdirSync(p, opts) { - return this.realFs.mkdirSync(npath.fromPortablePath(p), opts); - } - async rmdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.rmdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.rmdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - rmdirSync(p, opts) { - return this.realFs.rmdirSync(npath.fromPortablePath(p), opts); - } - async rmPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.rm(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.rm(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - rmSync(p, opts) { - return this.realFs.rmSync(npath.fromPortablePath(p), opts); - } - async linkPromise(existingP, newP) { - return await new Promise((resolve, reject) => { - this.realFs.link(npath.fromPortablePath(existingP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); - }); - } - linkSync(existingP, newP) { - return this.realFs.linkSync(npath.fromPortablePath(existingP), npath.fromPortablePath(newP)); - } - async symlinkPromise(target, p, type) { - return await new Promise((resolve, reject) => { - this.realFs.symlink(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type, this.makeCallback(resolve, reject)); - }); - } - symlinkSync(target, p, type) { - return this.realFs.symlinkSync(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type); - } - async readFilePromise(p, encoding) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - this.realFs.readFile(fsNativePath, encoding, this.makeCallback(resolve, reject)); - }); - } - readFileSync(p, encoding) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - return this.realFs.readFileSync(fsNativePath, encoding); - } - async readdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - if (opts.recursive && process.platform === `win32`) { - if (opts.withFileTypes) { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback((results) => resolve(results.map(direntToPortable)), reject)); - } else { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback((results) => resolve(results.map(npath.toPortablePath)), reject)); - } - } else { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } - } else { - this.realFs.readdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - readdirSync(p, opts) { - if (opts) { - if (opts.recursive && process.platform === `win32`) { - if (opts.withFileTypes) { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts).map(direntToPortable); - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts).map(npath.toPortablePath); - } - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts); - } - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p)); - } - } - async readlinkPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.readlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - }).then((path) => { - return npath.toPortablePath(path); - }); - } - readlinkSync(p) { - return npath.toPortablePath(this.realFs.readlinkSync(npath.fromPortablePath(p))); - } - async truncatePromise(p, len) { - return await new Promise((resolve, reject) => { - this.realFs.truncate(npath.fromPortablePath(p), len, this.makeCallback(resolve, reject)); - }); - } - truncateSync(p, len) { - return this.realFs.truncateSync(npath.fromPortablePath(p), len); - } - async ftruncatePromise(fd, len) { - return await new Promise((resolve, reject) => { - this.realFs.ftruncate(fd, len, this.makeCallback(resolve, reject)); - }); - } - ftruncateSync(fd, len) { - return this.realFs.ftruncateSync(fd, len); - } - watch(p, a, b) { - return this.realFs.watch( - npath.fromPortablePath(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - watchFile(p, a, b) { - return this.realFs.watchFile( - npath.fromPortablePath(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - unwatchFile(p, cb) { - return this.realFs.unwatchFile(npath.fromPortablePath(p), cb); - } - makeCallback(resolve, reject) { - return (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }; - } -} - -const MOUNT_MASK = 4278190080; -class MountFS extends BasePortableFakeFS { - baseFs; - mountInstances; - fdMap = /* @__PURE__ */ new Map(); - nextFd = 3; - factoryPromise; - factorySync; - filter; - getMountPoint; - magic; - maxAge; - maxOpenFiles; - typeCheck; - isMount = /* @__PURE__ */ new Set(); - notMount = /* @__PURE__ */ new Set(); - realPaths = /* @__PURE__ */ new Map(); - constructor({ baseFs = new NodeFS(), filter = null, magicByte = 42, maxOpenFiles = Infinity, useCache = true, maxAge = 5e3, typeCheck = fs.constants.S_IFREG, getMountPoint, factoryPromise, factorySync }) { - if (Math.floor(magicByte) !== magicByte || !(magicByte > 1 && magicByte <= 127)) - throw new Error(`The magic byte must be set to a round value between 1 and 127 included`); - super(); - this.baseFs = baseFs; - this.mountInstances = useCache ? /* @__PURE__ */ new Map() : null; - this.factoryPromise = factoryPromise; - this.factorySync = factorySync; - this.filter = filter; - this.getMountPoint = getMountPoint; - this.magic = magicByte << 24; - this.maxAge = maxAge; - this.maxOpenFiles = maxOpenFiles; - this.typeCheck = typeCheck; - } - getExtractHint(hints) { - return this.baseFs.getExtractHint(hints); - } - getRealPath() { - return this.baseFs.getRealPath(); - } - saveAndClose() { - unwatchAllFiles(this); - if (this.mountInstances) { - for (const [path, { childFs }] of this.mountInstances.entries()) { - childFs.saveAndClose?.(); - this.mountInstances.delete(path); - } - } - } - discardAndClose() { - unwatchAllFiles(this); - if (this.mountInstances) { - for (const [path, { childFs }] of this.mountInstances.entries()) { - childFs.discardAndClose?.(); - this.mountInstances.delete(path); - } - } - } - resolve(p) { - return this.baseFs.resolve(p); - } - remapFd(mountFs, fd) { - const remappedFd = this.nextFd++ | this.magic; - this.fdMap.set(remappedFd, [mountFs, fd]); - return remappedFd; - } - async openPromise(p, flags, mode) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.openPromise(p, flags, mode); - }, async (mountFs, { subPath }) => { - return this.remapFd(mountFs, await mountFs.openPromise(subPath, flags, mode)); - }); - } - openSync(p, flags, mode) { - return this.makeCallSync(p, () => { - return this.baseFs.openSync(p, flags, mode); - }, (mountFs, { subPath }) => { - return this.remapFd(mountFs, mountFs.openSync(subPath, flags, mode)); - }); - } - async opendirPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.opendirPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.opendirPromise(subPath, opts); - }, { - requireSubpath: false - }); - } - opendirSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.opendirSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.opendirSync(subPath, opts); - }, { - requireSubpath: false - }); - } - async readPromise(fd, buffer, offset, length, position) { - if ((fd & MOUNT_MASK) !== this.magic) - return await this.baseFs.readPromise(fd, buffer, offset, length, position); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`read`); - const [mountFs, realFd] = entry; - return await mountFs.readPromise(realFd, buffer, offset, length, position); - } - readSync(fd, buffer, offset, length, position) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.readSync(fd, buffer, offset, length, position); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`readSync`); - const [mountFs, realFd] = entry; - return mountFs.readSync(realFd, buffer, offset, length, position); - } - async writePromise(fd, buffer, offset, length, position) { - if ((fd & MOUNT_MASK) !== this.magic) { - if (typeof buffer === `string`) { - return await this.baseFs.writePromise(fd, buffer, offset); - } else { - return await this.baseFs.writePromise(fd, buffer, offset, length, position); - } - } - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`write`); - const [mountFs, realFd] = entry; - if (typeof buffer === `string`) { - return await mountFs.writePromise(realFd, buffer, offset); - } else { - return await mountFs.writePromise(realFd, buffer, offset, length, position); - } - } - writeSync(fd, buffer, offset, length, position) { - if ((fd & MOUNT_MASK) !== this.magic) { - if (typeof buffer === `string`) { - return this.baseFs.writeSync(fd, buffer, offset); - } else { - return this.baseFs.writeSync(fd, buffer, offset, length, position); - } - } - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`writeSync`); - const [mountFs, realFd] = entry; - if (typeof buffer === `string`) { - return mountFs.writeSync(realFd, buffer, offset); - } else { - return mountFs.writeSync(realFd, buffer, offset, length, position); - } - } - async closePromise(fd) { - if ((fd & MOUNT_MASK) !== this.magic) - return await this.baseFs.closePromise(fd); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`close`); - this.fdMap.delete(fd); - const [mountFs, realFd] = entry; - return await mountFs.closePromise(realFd); - } - closeSync(fd) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.closeSync(fd); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`closeSync`); - this.fdMap.delete(fd); - const [mountFs, realFd] = entry; - return mountFs.closeSync(realFd); - } - createReadStream(p, opts) { - if (p === null) - return this.baseFs.createReadStream(p, opts); - return this.makeCallSync(p, () => { - return this.baseFs.createReadStream(p, opts); - }, (mountFs, { archivePath, subPath }) => { - const stream = mountFs.createReadStream(subPath, opts); - stream.path = npath.fromPortablePath(this.pathUtils.join(archivePath, subPath)); - return stream; - }); - } - createWriteStream(p, opts) { - if (p === null) - return this.baseFs.createWriteStream(p, opts); - return this.makeCallSync(p, () => { - return this.baseFs.createWriteStream(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.createWriteStream(subPath, opts); - }); - } - async realpathPromise(p) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.realpathPromise(p); - }, async (mountFs, { archivePath, subPath }) => { - let realArchivePath = this.realPaths.get(archivePath); - if (typeof realArchivePath === `undefined`) { - realArchivePath = await this.baseFs.realpathPromise(archivePath); - this.realPaths.set(archivePath, realArchivePath); - } - return this.pathUtils.join(realArchivePath, this.pathUtils.relative(PortablePath.root, await mountFs.realpathPromise(subPath))); - }); - } - realpathSync(p) { - return this.makeCallSync(p, () => { - return this.baseFs.realpathSync(p); - }, (mountFs, { archivePath, subPath }) => { - let realArchivePath = this.realPaths.get(archivePath); - if (typeof realArchivePath === `undefined`) { - realArchivePath = this.baseFs.realpathSync(archivePath); - this.realPaths.set(archivePath, realArchivePath); - } - return this.pathUtils.join(realArchivePath, this.pathUtils.relative(PortablePath.root, mountFs.realpathSync(subPath))); - }); - } - async existsPromise(p) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.existsPromise(p); - }, async (mountFs, { subPath }) => { - return await mountFs.existsPromise(subPath); - }); - } - existsSync(p) { - return this.makeCallSync(p, () => { - return this.baseFs.existsSync(p); - }, (mountFs, { subPath }) => { - return mountFs.existsSync(subPath); - }); - } - async accessPromise(p, mode) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.accessPromise(p, mode); - }, async (mountFs, { subPath }) => { - return await mountFs.accessPromise(subPath, mode); - }); - } - accessSync(p, mode) { - return this.makeCallSync(p, () => { - return this.baseFs.accessSync(p, mode); - }, (mountFs, { subPath }) => { - return mountFs.accessSync(subPath, mode); - }); - } - async statPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.statPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.statPromise(subPath, opts); - }); - } - statSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.statSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.statSync(subPath, opts); - }); - } - async fstatPromise(fd, opts) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fstatPromise(fd, opts); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fstat`); - const [mountFs, realFd] = entry; - return mountFs.fstatPromise(realFd, opts); - } - fstatSync(fd, opts) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fstatSync(fd, opts); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fstatSync`); - const [mountFs, realFd] = entry; - return mountFs.fstatSync(realFd, opts); - } - async lstatPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.lstatPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.lstatPromise(subPath, opts); - }); - } - lstatSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.lstatSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.lstatSync(subPath, opts); - }); - } - async fchmodPromise(fd, mask) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fchmodPromise(fd, mask); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fchmod`); - const [mountFs, realFd] = entry; - return mountFs.fchmodPromise(realFd, mask); - } - fchmodSync(fd, mask) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fchmodSync(fd, mask); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fchmodSync`); - const [mountFs, realFd] = entry; - return mountFs.fchmodSync(realFd, mask); - } - async chmodPromise(p, mask) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.chmodPromise(p, mask); - }, async (mountFs, { subPath }) => { - return await mountFs.chmodPromise(subPath, mask); - }); - } - chmodSync(p, mask) { - return this.makeCallSync(p, () => { - return this.baseFs.chmodSync(p, mask); - }, (mountFs, { subPath }) => { - return mountFs.chmodSync(subPath, mask); - }); - } - async fchownPromise(fd, uid, gid) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fchownPromise(fd, uid, gid); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fchown`); - const [zipFs, realFd] = entry; - return zipFs.fchownPromise(realFd, uid, gid); - } - fchownSync(fd, uid, gid) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.fchownSync(fd, uid, gid); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fchownSync`); - const [zipFs, realFd] = entry; - return zipFs.fchownSync(realFd, uid, gid); - } - async chownPromise(p, uid, gid) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.chownPromise(p, uid, gid); - }, async (mountFs, { subPath }) => { - return await mountFs.chownPromise(subPath, uid, gid); - }); - } - chownSync(p, uid, gid) { - return this.makeCallSync(p, () => { - return this.baseFs.chownSync(p, uid, gid); - }, (mountFs, { subPath }) => { - return mountFs.chownSync(subPath, uid, gid); - }); - } - async renamePromise(oldP, newP) { - return await this.makeCallPromise(oldP, async () => { - return await this.makeCallPromise(newP, async () => { - return await this.baseFs.renamePromise(oldP, newP); - }, async () => { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - }); - }, async (mountFsO, { subPath: subPathO }) => { - return await this.makeCallPromise(newP, async () => { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - }, async (mountFsN, { subPath: subPathN }) => { - if (mountFsO !== mountFsN) { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - } else { - return await mountFsO.renamePromise(subPathO, subPathN); - } - }); - }); - } - renameSync(oldP, newP) { - return this.makeCallSync(oldP, () => { - return this.makeCallSync(newP, () => { - return this.baseFs.renameSync(oldP, newP); - }, () => { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - }); - }, (mountFsO, { subPath: subPathO }) => { - return this.makeCallSync(newP, () => { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - }, (mountFsN, { subPath: subPathN }) => { - if (mountFsO !== mountFsN) { - throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` }); - } else { - return mountFsO.renameSync(subPathO, subPathN); - } - }); - }); - } - async copyFilePromise(sourceP, destP, flags = 0) { - const fallback = async (sourceFs, sourceP2, destFs, destP2) => { - if ((flags & fs.constants.COPYFILE_FICLONE_FORCE) !== 0) - throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP2}' -> ${destP2}'`), { code: `EXDEV` }); - if (flags & fs.constants.COPYFILE_EXCL && await this.existsPromise(sourceP2)) - throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP2}' -> '${destP2}'`), { code: `EEXIST` }); - let content; - try { - content = await sourceFs.readFilePromise(sourceP2); - } catch { - throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP2}' -> '${destP2}'`), { code: `EINVAL` }); - } - await destFs.writeFilePromise(destP2, content); - }; - return await this.makeCallPromise(sourceP, async () => { - return await this.makeCallPromise(destP, async () => { - return await this.baseFs.copyFilePromise(sourceP, destP, flags); - }, async (mountFsD, { subPath: subPathD }) => { - return await fallback(this.baseFs, sourceP, mountFsD, subPathD); - }); - }, async (mountFsS, { subPath: subPathS }) => { - return await this.makeCallPromise(destP, async () => { - return await fallback(mountFsS, subPathS, this.baseFs, destP); - }, async (mountFsD, { subPath: subPathD }) => { - if (mountFsS !== mountFsD) { - return await fallback(mountFsS, subPathS, mountFsD, subPathD); - } else { - return await mountFsS.copyFilePromise(subPathS, subPathD, flags); - } - }); - }); - } - copyFileSync(sourceP, destP, flags = 0) { - const fallback = (sourceFs, sourceP2, destFs, destP2) => { - if ((flags & fs.constants.COPYFILE_FICLONE_FORCE) !== 0) - throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP2}' -> ${destP2}'`), { code: `EXDEV` }); - if (flags & fs.constants.COPYFILE_EXCL && this.existsSync(sourceP2)) - throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP2}' -> '${destP2}'`), { code: `EEXIST` }); - let content; - try { - content = sourceFs.readFileSync(sourceP2); - } catch { - throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP2}' -> '${destP2}'`), { code: `EINVAL` }); - } - destFs.writeFileSync(destP2, content); - }; - return this.makeCallSync(sourceP, () => { - return this.makeCallSync(destP, () => { - return this.baseFs.copyFileSync(sourceP, destP, flags); - }, (mountFsD, { subPath: subPathD }) => { - return fallback(this.baseFs, sourceP, mountFsD, subPathD); - }); - }, (mountFsS, { subPath: subPathS }) => { - return this.makeCallSync(destP, () => { - return fallback(mountFsS, subPathS, this.baseFs, destP); - }, (mountFsD, { subPath: subPathD }) => { - if (mountFsS !== mountFsD) { - return fallback(mountFsS, subPathS, mountFsD, subPathD); - } else { - return mountFsS.copyFileSync(subPathS, subPathD, flags); - } - }); - }); - } - async appendFilePromise(p, content, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.appendFilePromise(p, content, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.appendFilePromise(subPath, content, opts); - }); - } - appendFileSync(p, content, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.appendFileSync(p, content, opts); - }, (mountFs, { subPath }) => { - return mountFs.appendFileSync(subPath, content, opts); - }); - } - async writeFilePromise(p, content, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.writeFilePromise(p, content, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.writeFilePromise(subPath, content, opts); - }); - } - writeFileSync(p, content, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.writeFileSync(p, content, opts); - }, (mountFs, { subPath }) => { - return mountFs.writeFileSync(subPath, content, opts); - }); - } - async unlinkPromise(p) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.unlinkPromise(p); - }, async (mountFs, { subPath }) => { - return await mountFs.unlinkPromise(subPath); - }); - } - unlinkSync(p) { - return this.makeCallSync(p, () => { - return this.baseFs.unlinkSync(p); - }, (mountFs, { subPath }) => { - return mountFs.unlinkSync(subPath); - }); - } - async utimesPromise(p, atime, mtime) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.utimesPromise(p, atime, mtime); - }, async (mountFs, { subPath }) => { - return await mountFs.utimesPromise(subPath, atime, mtime); - }); - } - utimesSync(p, atime, mtime) { - return this.makeCallSync(p, () => { - return this.baseFs.utimesSync(p, atime, mtime); - }, (mountFs, { subPath }) => { - return mountFs.utimesSync(subPath, atime, mtime); - }); - } - async lutimesPromise(p, atime, mtime) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.lutimesPromise(p, atime, mtime); - }, async (mountFs, { subPath }) => { - return await mountFs.lutimesPromise(subPath, atime, mtime); - }); - } - lutimesSync(p, atime, mtime) { - return this.makeCallSync(p, () => { - return this.baseFs.lutimesSync(p, atime, mtime); - }, (mountFs, { subPath }) => { - return mountFs.lutimesSync(subPath, atime, mtime); - }); - } - async mkdirPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.mkdirPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.mkdirPromise(subPath, opts); - }); - } - mkdirSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.mkdirSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.mkdirSync(subPath, opts); - }); - } - async rmdirPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.rmdirPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.rmdirPromise(subPath, opts); - }); - } - rmdirSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.rmdirSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.rmdirSync(subPath, opts); - }); - } - async rmPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.rmPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.rmPromise(subPath, opts); - }); - } - rmSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.rmSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.rmSync(subPath, opts); - }); - } - async linkPromise(existingP, newP) { - return await this.makeCallPromise(newP, async () => { - return await this.baseFs.linkPromise(existingP, newP); - }, async (mountFs, { subPath }) => { - return await mountFs.linkPromise(existingP, subPath); - }); - } - linkSync(existingP, newP) { - return this.makeCallSync(newP, () => { - return this.baseFs.linkSync(existingP, newP); - }, (mountFs, { subPath }) => { - return mountFs.linkSync(existingP, subPath); - }); - } - async symlinkPromise(target, p, type) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.symlinkPromise(target, p, type); - }, async (mountFs, { subPath }) => { - return await mountFs.symlinkPromise(target, subPath); - }); - } - symlinkSync(target, p, type) { - return this.makeCallSync(p, () => { - return this.baseFs.symlinkSync(target, p, type); - }, (mountFs, { subPath }) => { - return mountFs.symlinkSync(target, subPath); - }); - } - async readFilePromise(p, encoding) { - return this.makeCallPromise(p, async () => { - return await this.baseFs.readFilePromise(p, encoding); - }, async (mountFs, { subPath }) => { - return await mountFs.readFilePromise(subPath, encoding); - }); - } - readFileSync(p, encoding) { - return this.makeCallSync(p, () => { - return this.baseFs.readFileSync(p, encoding); - }, (mountFs, { subPath }) => { - return mountFs.readFileSync(subPath, encoding); - }); - } - async readdirPromise(p, opts) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.readdirPromise(p, opts); - }, async (mountFs, { subPath }) => { - return await mountFs.readdirPromise(subPath, opts); - }, { - requireSubpath: false - }); - } - readdirSync(p, opts) { - return this.makeCallSync(p, () => { - return this.baseFs.readdirSync(p, opts); - }, (mountFs, { subPath }) => { - return mountFs.readdirSync(subPath, opts); - }, { - requireSubpath: false - }); - } - async readlinkPromise(p) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.readlinkPromise(p); - }, async (mountFs, { subPath }) => { - return await mountFs.readlinkPromise(subPath); - }); - } - readlinkSync(p) { - return this.makeCallSync(p, () => { - return this.baseFs.readlinkSync(p); - }, (mountFs, { subPath }) => { - return mountFs.readlinkSync(subPath); - }); - } - async truncatePromise(p, len) { - return await this.makeCallPromise(p, async () => { - return await this.baseFs.truncatePromise(p, len); - }, async (mountFs, { subPath }) => { - return await mountFs.truncatePromise(subPath, len); - }); - } - truncateSync(p, len) { - return this.makeCallSync(p, () => { - return this.baseFs.truncateSync(p, len); - }, (mountFs, { subPath }) => { - return mountFs.truncateSync(subPath, len); - }); - } - async ftruncatePromise(fd, len) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.ftruncatePromise(fd, len); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`ftruncate`); - const [mountFs, realFd] = entry; - return mountFs.ftruncatePromise(realFd, len); - } - ftruncateSync(fd, len) { - if ((fd & MOUNT_MASK) !== this.magic) - return this.baseFs.ftruncateSync(fd, len); - const entry = this.fdMap.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`ftruncateSync`); - const [mountFs, realFd] = entry; - return mountFs.ftruncateSync(realFd, len); - } - watch(p, a, b) { - return this.makeCallSync(p, () => { - return this.baseFs.watch( - p, - // @ts-expect-error - reason TBS - a, - b - ); - }, (mountFs, { subPath }) => { - return mountFs.watch( - subPath, - // @ts-expect-error - reason TBS - a, - b - ); - }); - } - watchFile(p, a, b) { - return this.makeCallSync(p, () => { - return this.baseFs.watchFile( - p, - // @ts-expect-error - reason TBS - a, - b - ); - }, () => { - return watchFile(this, p, a, b); - }); - } - unwatchFile(p, cb) { - return this.makeCallSync(p, () => { - return this.baseFs.unwatchFile(p, cb); - }, () => { - return unwatchFile(this, p, cb); - }); - } - async makeCallPromise(p, discard, accept, { requireSubpath = true } = {}) { - if (typeof p !== `string`) - return await discard(); - const normalizedP = this.resolve(p); - const mountInfo = this.findMount(normalizedP); - if (!mountInfo) - return await discard(); - if (requireSubpath && mountInfo.subPath === `/`) - return await discard(); - return await this.getMountPromise(mountInfo.archivePath, async (mountFs) => await accept(mountFs, mountInfo)); - } - makeCallSync(p, discard, accept, { requireSubpath = true } = {}) { - if (typeof p !== `string`) - return discard(); - const normalizedP = this.resolve(p); - const mountInfo = this.findMount(normalizedP); - if (!mountInfo) - return discard(); - if (requireSubpath && mountInfo.subPath === `/`) - return discard(); - return this.getMountSync(mountInfo.archivePath, (mountFs) => accept(mountFs, mountInfo)); - } - findMount(p) { - if (this.filter && !this.filter.test(p)) - return null; - let filePath = ``; - while (true) { - const pathPartWithArchive = p.substring(filePath.length); - const mountPoint = this.getMountPoint(pathPartWithArchive, filePath); - if (!mountPoint) - return null; - filePath = this.pathUtils.join(filePath, mountPoint); - if (!this.isMount.has(filePath)) { - if (this.notMount.has(filePath)) - continue; - try { - if (this.typeCheck !== null && (this.baseFs.statSync(filePath).mode & fs.constants.S_IFMT) !== this.typeCheck) { - this.notMount.add(filePath); - continue; - } - } catch { - return null; - } - this.isMount.add(filePath); - } - return { - archivePath: filePath, - subPath: this.pathUtils.join(PortablePath.root, p.substring(filePath.length)) - }; - } - } - limitOpenFilesTimeout = null; - limitOpenFiles(max) { - if (this.mountInstances === null) - return; - const now = Date.now(); - let nextExpiresAt = now + this.maxAge; - let closeCount = max === null ? 0 : this.mountInstances.size - max; - for (const [path, { childFs, expiresAt, refCount }] of this.mountInstances.entries()) { - if (refCount !== 0 || childFs.hasOpenFileHandles?.()) { - continue; - } else if (now >= expiresAt) { - childFs.saveAndClose?.(); - this.mountInstances.delete(path); - closeCount -= 1; - continue; - } else if (max === null || closeCount <= 0) { - nextExpiresAt = expiresAt; - break; - } - childFs.saveAndClose?.(); - this.mountInstances.delete(path); - closeCount -= 1; - } - if (this.limitOpenFilesTimeout === null && (max === null && this.mountInstances.size > 0 || max !== null) && isFinite(nextExpiresAt)) { - this.limitOpenFilesTimeout = setTimeout(() => { - this.limitOpenFilesTimeout = null; - this.limitOpenFiles(null); - }, nextExpiresAt - now).unref(); - } - } - async getMountPromise(p, accept) { - if (this.mountInstances) { - let cachedMountFs = this.mountInstances.get(p); - if (!cachedMountFs) { - const createFsInstance = await this.factoryPromise(this.baseFs, p); - cachedMountFs = this.mountInstances.get(p); - if (!cachedMountFs) { - cachedMountFs = { - childFs: createFsInstance(), - expiresAt: 0, - refCount: 0 - }; - } - } - this.mountInstances.delete(p); - this.limitOpenFiles(this.maxOpenFiles - 1); - this.mountInstances.set(p, cachedMountFs); - cachedMountFs.expiresAt = Date.now() + this.maxAge; - cachedMountFs.refCount += 1; - try { - return await accept(cachedMountFs.childFs); - } finally { - cachedMountFs.refCount -= 1; - } - } else { - const mountFs = (await this.factoryPromise(this.baseFs, p))(); - try { - return await accept(mountFs); - } finally { - mountFs.saveAndClose?.(); - } - } - } - getMountSync(p, accept) { - if (this.mountInstances) { - let cachedMountFs = this.mountInstances.get(p); - if (!cachedMountFs) { - cachedMountFs = { - childFs: this.factorySync(this.baseFs, p), - expiresAt: 0, - refCount: 0 - }; - } - this.mountInstances.delete(p); - this.limitOpenFiles(this.maxOpenFiles - 1); - this.mountInstances.set(p, cachedMountFs); - cachedMountFs.expiresAt = Date.now() + this.maxAge; - return accept(cachedMountFs.childFs); - } else { - const childFs = this.factorySync(this.baseFs, p); - try { - return accept(childFs); - } finally { - childFs.saveAndClose?.(); - } - } - } -} - -class PosixFS extends ProxiedFS { - baseFs; - constructor(baseFs) { - super(npath); - this.baseFs = baseFs; - } - mapFromBase(path) { - return npath.fromPortablePath(path); - } - mapToBase(path) { - return npath.toPortablePath(path); - } -} - -const NUMBER_REGEXP = /^[0-9]+$/; -const VIRTUAL_REGEXP = /^(\/(?:[^/]+\/)*?(?:\$\$virtual|__virtual__))((?:\/((?:[^/]+-)?[a-f0-9]+)(?:\/([^/]+))?)?((?:\/.*)?))$/; -const VALID_COMPONENT = /^([^/]+-)?[a-f0-9]+$/; -class VirtualFS extends ProxiedFS { - baseFs; - static makeVirtualPath(base, component, to) { - if (ppath.basename(base) !== `__virtual__`) - throw new Error(`Assertion failed: Virtual folders must be named "__virtual__"`); - if (!ppath.basename(component).match(VALID_COMPONENT)) - throw new Error(`Assertion failed: Virtual components must be ended by an hexadecimal hash`); - const target = ppath.relative(ppath.dirname(base), to); - const segments = target.split(`/`); - let depth = 0; - while (depth < segments.length && segments[depth] === `..`) - depth += 1; - const finalSegments = segments.slice(depth); - const fullVirtualPath = ppath.join(base, component, String(depth), ...finalSegments); - return fullVirtualPath; - } - static resolveVirtual(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match || !match[3] && match[5]) - return p; - const target = ppath.dirname(match[1]); - if (!match[3] || !match[4]) - return target; - const isnum = NUMBER_REGEXP.test(match[4]); - if (!isnum) - return p; - const depth = Number(match[4]); - const backstep = `../`.repeat(depth); - const subpath = match[5] || `.`; - return VirtualFS.resolveVirtual(ppath.join(target, backstep, subpath)); - } - constructor({ baseFs = new NodeFS() } = {}) { - super(ppath); - this.baseFs = baseFs; - } - getExtractHint(hints) { - return this.baseFs.getExtractHint(hints); - } - getRealPath() { - return this.baseFs.getRealPath(); - } - realpathSync(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match) - return this.baseFs.realpathSync(p); - if (!match[5]) - return p; - const realpath = this.baseFs.realpathSync(this.mapToBase(p)); - return VirtualFS.makeVirtualPath(match[1], match[3], realpath); - } - async realpathPromise(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match) - return await this.baseFs.realpathPromise(p); - if (!match[5]) - return p; - const realpath = await this.baseFs.realpathPromise(this.mapToBase(p)); - return VirtualFS.makeVirtualPath(match[1], match[3], realpath); - } - mapToBase(p) { - if (p === ``) - return p; - if (this.pathUtils.isAbsolute(p)) - return VirtualFS.resolveVirtual(p); - const resolvedRoot = VirtualFS.resolveVirtual(this.baseFs.resolve(PortablePath.dot)); - const resolvedP = VirtualFS.resolveVirtual(this.baseFs.resolve(p)); - return ppath.relative(resolvedRoot, resolvedP) || PortablePath.dot; - } - mapFromBase(p) { - return p; - } -} - -const URL = Number(process.versions.node.split('.', 1)[0]) < 20 ? url.URL : globalThis.URL; - -class NodePathFS extends ProxiedFS { - baseFs; - constructor(baseFs) { - super(npath); - this.baseFs = baseFs; - } - mapFromBase(path) { - return path; - } - mapToBase(path) { - if (typeof path === `string`) - return path; - if (path instanceof URL) - return url.fileURLToPath(path); - if (Buffer.isBuffer(path)) { - const str = path.toString(); - if (!isUtf8(path, str)) - throw new Error(`Non-utf8 buffers are not supported at the moment. Please upvote the following issue if you encounter this error: https://github.com/yarnpkg/berry/issues/4942`); - return str; - } - throw new Error(`Unsupported path type: ${nodeUtils.inspect(path)}`); - } -} -function isUtf8(buf, str) { - if (typeof buffer__default.default.isUtf8 !== `undefined`) - return buffer__default.default.isUtf8(buf); - return Buffer.byteLength(str) === buf.byteLength; -} - -const kBaseFs = Symbol(`kBaseFs`); -const kFd = Symbol(`kFd`); -const kClosePromise = Symbol(`kClosePromise`); -const kCloseResolve = Symbol(`kCloseResolve`); -const kCloseReject = Symbol(`kCloseReject`); -const kRefs = Symbol(`kRefs`); -const kRef = Symbol(`kRef`); -const kUnref = Symbol(`kUnref`); -class FileHandle { - [kBaseFs]; - [kFd]; - [kRefs] = 1; - [kClosePromise] = void 0; - [kCloseResolve] = void 0; - [kCloseReject] = void 0; - constructor(fd, baseFs) { - this[kBaseFs] = baseFs; - this[kFd] = fd; - } - get fd() { - return this[kFd]; - } - async appendFile(data, options) { - try { - this[kRef](this.appendFile); - const encoding = (typeof options === `string` ? options : options?.encoding) ?? void 0; - return await this[kBaseFs].appendFilePromise(this.fd, data, encoding ? { encoding } : void 0); - } finally { - this[kUnref](); - } - } - async chown(uid, gid) { - try { - this[kRef](this.chown); - return await this[kBaseFs].fchownPromise(this.fd, uid, gid); - } finally { - this[kUnref](); - } - } - async chmod(mode) { - try { - this[kRef](this.chmod); - return await this[kBaseFs].fchmodPromise(this.fd, mode); - } finally { - this[kUnref](); - } - } - createReadStream(options) { - return this[kBaseFs].createReadStream(null, { ...options, fd: this.fd }); - } - createWriteStream(options) { - return this[kBaseFs].createWriteStream(null, { ...options, fd: this.fd }); - } - // FIXME: Missing FakeFS version - datasync() { - throw new Error(`Method not implemented.`); - } - // FIXME: Missing FakeFS version - sync() { - throw new Error(`Method not implemented.`); - } - async read(bufferOrOptions, offsetOrOptions, length, position) { - try { - this[kRef](this.read); - let buffer; - let offset; - if (!ArrayBuffer.isView(bufferOrOptions)) { - buffer = bufferOrOptions?.buffer ?? Buffer.alloc(16384); - offset = bufferOrOptions?.offset ?? 0; - length = bufferOrOptions?.length ?? buffer.byteLength - offset; - position = bufferOrOptions?.position ?? null; - } else if (typeof offsetOrOptions === `object` && offsetOrOptions !== null) { - buffer = bufferOrOptions; - offset = offsetOrOptions?.offset ?? 0; - length = offsetOrOptions?.length ?? buffer.byteLength - offset; - position = offsetOrOptions?.position ?? null; - } else { - buffer = bufferOrOptions; - offset = offsetOrOptions ?? 0; - length ??= 0; - } - if (length === 0) { - return { - bytesRead: length, - buffer - }; - } - const bytesRead = await this[kBaseFs].readPromise( - this.fd, - // FIXME: FakeFS should support ArrayBufferViews directly - Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength), - offset, - length, - position - ); - return { - bytesRead, - buffer - }; - } finally { - this[kUnref](); - } - } - async readFile(options) { - try { - this[kRef](this.readFile); - const encoding = (typeof options === `string` ? options : options?.encoding) ?? void 0; - return await this[kBaseFs].readFilePromise(this.fd, encoding); - } finally { - this[kUnref](); - } - } - readLines(options) { - return readline.createInterface({ - input: this.createReadStream(options), - crlfDelay: Infinity - }); - } - async stat(opts) { - try { - this[kRef](this.stat); - return await this[kBaseFs].fstatPromise(this.fd, opts); - } finally { - this[kUnref](); - } - } - async truncate(len) { - try { - this[kRef](this.truncate); - return await this[kBaseFs].ftruncatePromise(this.fd, len); - } finally { - this[kUnref](); - } - } - // FIXME: Missing FakeFS version - utimes(atime, mtime) { - throw new Error(`Method not implemented.`); - } - async writeFile(data, options) { - try { - this[kRef](this.writeFile); - const encoding = (typeof options === `string` ? options : options?.encoding) ?? void 0; - await this[kBaseFs].writeFilePromise(this.fd, data, encoding); - } finally { - this[kUnref](); - } - } - async write(...args) { - try { - this[kRef](this.write); - if (ArrayBuffer.isView(args[0])) { - const [buffer, offset, length, position] = args; - const bytesWritten = await this[kBaseFs].writePromise(this.fd, buffer, offset ?? void 0, length ?? void 0, position ?? void 0); - return { bytesWritten, buffer }; - } else { - const [data, position, encoding] = args; - const bytesWritten = await this[kBaseFs].writePromise(this.fd, data, position, encoding); - return { bytesWritten, buffer: data }; - } - } finally { - this[kUnref](); - } - } - // TODO: Use writev from FakeFS when that is implemented - async writev(buffers, position) { - try { - this[kRef](this.writev); - let bytesWritten = 0; - if (typeof position !== `undefined`) { - for (const buffer of buffers) { - const writeResult = await this.write(buffer, void 0, void 0, position); - bytesWritten += writeResult.bytesWritten; - position += writeResult.bytesWritten; - } - } else { - for (const buffer of buffers) { - const writeResult = await this.write(buffer); - bytesWritten += writeResult.bytesWritten; - } - } - return { - buffers, - bytesWritten - }; - } finally { - this[kUnref](); - } - } - // FIXME: Missing FakeFS version - readv(buffers, position) { - throw new Error(`Method not implemented.`); - } - close() { - if (this[kFd] === -1) return Promise.resolve(); - if (this[kClosePromise]) return this[kClosePromise]; - this[kRefs]--; - if (this[kRefs] === 0) { - const fd = this[kFd]; - this[kFd] = -1; - this[kClosePromise] = this[kBaseFs].closePromise(fd).finally(() => { - this[kClosePromise] = void 0; - }); - } else { - this[kClosePromise] = new Promise((resolve, reject) => { - this[kCloseResolve] = resolve; - this[kCloseReject] = reject; - }).finally(() => { - this[kClosePromise] = void 0; - this[kCloseReject] = void 0; - this[kCloseResolve] = void 0; - }); - } - return this[kClosePromise]; - } - [kRef](caller) { - if (this[kFd] === -1) { - const err = new Error(`file closed`); - err.code = `EBADF`; - err.syscall = caller.name; - throw err; - } - this[kRefs]++; - } - [kUnref]() { - this[kRefs]--; - if (this[kRefs] === 0) { - const fd = this[kFd]; - this[kFd] = -1; - this[kBaseFs].closePromise(fd).then(this[kCloseResolve], this[kCloseReject]); - } - } -} - -const SYNC_IMPLEMENTATIONS = /* @__PURE__ */ new Set([ - `accessSync`, - `appendFileSync`, - `createReadStream`, - `createWriteStream`, - `chmodSync`, - `fchmodSync`, - `chownSync`, - `fchownSync`, - `closeSync`, - `copyFileSync`, - `linkSync`, - `lstatSync`, - `fstatSync`, - `lutimesSync`, - `mkdirSync`, - `openSync`, - `opendirSync`, - `readlinkSync`, - `readFileSync`, - `readdirSync`, - `readlinkSync`, - `realpathSync`, - `renameSync`, - `rmdirSync`, - `rmSync`, - `statSync`, - `symlinkSync`, - `truncateSync`, - `ftruncateSync`, - `unlinkSync`, - `unwatchFile`, - `utimesSync`, - `watch`, - `watchFile`, - `writeFileSync`, - `writeSync` -]); -const ASYNC_IMPLEMENTATIONS = /* @__PURE__ */ new Set([ - `accessPromise`, - `appendFilePromise`, - `fchmodPromise`, - `chmodPromise`, - `fchownPromise`, - `chownPromise`, - `closePromise`, - `copyFilePromise`, - `linkPromise`, - `fstatPromise`, - `lstatPromise`, - `lutimesPromise`, - `mkdirPromise`, - `openPromise`, - `opendirPromise`, - `readdirPromise`, - `realpathPromise`, - `readFilePromise`, - `readdirPromise`, - `readlinkPromise`, - `renamePromise`, - `rmdirPromise`, - `rmPromise`, - `statPromise`, - `symlinkPromise`, - `truncatePromise`, - `ftruncatePromise`, - `unlinkPromise`, - `utimesPromise`, - `writeFilePromise`, - `writeSync` -]); -function patchFs(patchedFs, fakeFs) { - fakeFs = new NodePathFS(fakeFs); - const setupFn = (target, name, replacement) => { - const orig = target[name]; - target[name] = replacement; - if (typeof orig?.[nodeUtils.promisify.custom] !== `undefined`) { - replacement[nodeUtils.promisify.custom] = orig[nodeUtils.promisify.custom]; - } - }; - { - setupFn(patchedFs, `exists`, (p, ...args) => { - const hasCallback = typeof args[args.length - 1] === `function`; - const callback = hasCallback ? args.pop() : () => { - }; - process.nextTick(() => { - fakeFs.existsPromise(p).then((exists) => { - callback(exists); - }, () => { - callback(false); - }); - }); - }); - setupFn(patchedFs, `read`, (...args) => { - let [fd, buffer, offset, length, position, callback] = args; - if (args.length <= 3) { - let options = {}; - if (args.length < 3) { - callback = args[1]; - } else { - options = args[1]; - callback = args[2]; - } - ({ - buffer = Buffer.alloc(16384), - offset = 0, - length = buffer.byteLength, - position - } = options); - } - if (offset == null) - offset = 0; - length |= 0; - if (length === 0) { - process.nextTick(() => { - callback(null, 0, buffer); - }); - return; - } - if (position == null) - position = -1; - process.nextTick(() => { - fakeFs.readPromise(fd, buffer, offset, length, position).then((bytesRead) => { - callback(null, bytesRead, buffer); - }, (error) => { - callback(error, 0, buffer); - }); - }); - }); - for (const fnName of ASYNC_IMPLEMENTATIONS) { - const origName = fnName.replace(/Promise$/, ``); - if (typeof patchedFs[origName] === `undefined`) - continue; - const fakeImpl = fakeFs[fnName]; - if (typeof fakeImpl === `undefined`) - continue; - const wrapper = (...args) => { - const hasCallback = typeof args[args.length - 1] === `function`; - const callback = hasCallback ? args.pop() : () => { - }; - process.nextTick(() => { - fakeImpl.apply(fakeFs, args).then((result) => { - callback(null, result); - }, (error) => { - callback(error); - }); - }); - }; - setupFn(patchedFs, origName, wrapper); - } - patchedFs.realpath.native = patchedFs.realpath; - } - { - setupFn(patchedFs, `existsSync`, (p) => { - try { - return fakeFs.existsSync(p); - } catch { - return false; - } - }); - setupFn(patchedFs, `readSync`, (...args) => { - let [fd, buffer, offset, length, position] = args; - if (args.length <= 3) { - const options = args[2] || {}; - ({ offset = 0, length = buffer.byteLength, position } = options); - } - if (offset == null) - offset = 0; - length |= 0; - if (length === 0) - return 0; - if (position == null) - position = -1; - return fakeFs.readSync(fd, buffer, offset, length, position); - }); - for (const fnName of SYNC_IMPLEMENTATIONS) { - const origName = fnName; - if (typeof patchedFs[origName] === `undefined`) - continue; - const fakeImpl = fakeFs[fnName]; - if (typeof fakeImpl === `undefined`) - continue; - setupFn(patchedFs, origName, fakeImpl.bind(fakeFs)); - } - patchedFs.realpathSync.native = patchedFs.realpathSync; - } - { - const patchedFsPromises = patchedFs.promises; - for (const fnName of ASYNC_IMPLEMENTATIONS) { - const origName = fnName.replace(/Promise$/, ``); - if (typeof patchedFsPromises[origName] === `undefined`) - continue; - const fakeImpl = fakeFs[fnName]; - if (typeof fakeImpl === `undefined`) - continue; - if (fnName === `open`) - continue; - setupFn(patchedFsPromises, origName, (pathLike, ...args) => { - if (pathLike instanceof FileHandle) { - return pathLike[origName].apply(pathLike, args); - } else { - return fakeImpl.call(fakeFs, pathLike, ...args); - } - }); - } - setupFn(patchedFsPromises, `open`, async (...args) => { - const fd = await fakeFs.openPromise(...args); - return new FileHandle(fd, fakeFs); - }); - } - { - patchedFs.read[nodeUtils.promisify.custom] = async (fd, buffer, ...args) => { - const res = fakeFs.readPromise(fd, buffer, ...args); - return { bytesRead: await res, buffer }; - }; - patchedFs.write[nodeUtils.promisify.custom] = async (fd, buffer, ...args) => { - const res = fakeFs.writePromise(fd, buffer, ...args); - return { bytesWritten: await res, buffer }; - }; - } -} - -let cachedInstance; -let registeredFactory = () => { - throw new Error(`Assertion failed: No libzip instance is available, and no factory was configured`); -}; -function setFactory(factory) { - registeredFactory = factory; -} -function getInstance() { - if (typeof cachedInstance === `undefined`) - cachedInstance = registeredFactory(); - return cachedInstance; -} - -var libzipSync = {exports: {}}; - -(function (module, exports) { -var frozenFs = Object.assign({}, fs__default.default); -var createModule = function() { - var _scriptDir = void 0; - if (typeof __filename !== "undefined") _scriptDir = _scriptDir || __filename; - return function(createModule2) { - createModule2 = createModule2 || {}; - var Module = typeof createModule2 !== "undefined" ? createModule2 : {}; - var readyPromiseResolve, readyPromiseReject; - Module["ready"] = new Promise(function(resolve, reject) { - readyPromiseResolve = resolve; - readyPromiseReject = reject; - }); - var moduleOverrides = {}; - var key; - for (key in Module) { - if (Module.hasOwnProperty(key)) { - moduleOverrides[key] = Module[key]; - } - } - var scriptDirectory = ""; - function locateFile(path) { - if (Module["locateFile"]) { - return Module["locateFile"](path, scriptDirectory); - } - return scriptDirectory + path; - } - var read_, readBinary; - var nodeFS; - var nodePath; - { - { - scriptDirectory = __dirname + "/"; - } - read_ = function shell_read(filename, binary) { - var ret = tryParseAsDataURI(filename); - if (ret) { - return binary ? ret : ret.toString(); - } - if (!nodeFS) nodeFS = frozenFs; - if (!nodePath) nodePath = path__default.default; - filename = nodePath["normalize"](filename); - return nodeFS["readFileSync"](filename, binary ? null : "utf8"); - }; - readBinary = function readBinary2(filename) { - var ret = read_(filename, true); - if (!ret.buffer) { - ret = new Uint8Array(ret); - } - assert(ret.buffer); - return ret; - }; - if (process["argv"].length > 1) { - process["argv"][1].replace(/\\/g, "/"); - } - process["argv"].slice(2); - Module["inspect"] = function() { - return "[Emscripten Module object]"; - }; - } - Module["print"] || console.log.bind(console); - var err = Module["printErr"] || console.warn.bind(console); - for (key in moduleOverrides) { - if (moduleOverrides.hasOwnProperty(key)) { - Module[key] = moduleOverrides[key]; - } - } - moduleOverrides = null; - if (Module["arguments"]) ; - if (Module["thisProgram"]) ; - if (Module["quit"]) ; - var wasmBinary; - if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; - Module["noExitRuntime"] || true; - if (typeof WebAssembly !== "object") { - abort("no native wasm support detected"); - } - function getValue(ptr, type, noSafe) { - type = type || "i8"; - if (type.charAt(type.length - 1) === "*") type = "i32"; - switch (type) { - case "i1": - return HEAP8[ptr >> 0]; - case "i8": - return HEAP8[ptr >> 0]; - case "i16": - return LE_HEAP_LOAD_I16((ptr >> 1) * 2); - case "i32": - return LE_HEAP_LOAD_I32((ptr >> 2) * 4); - case "i64": - return LE_HEAP_LOAD_I32((ptr >> 2) * 4); - case "float": - return LE_HEAP_LOAD_F32((ptr >> 2) * 4); - case "double": - return LE_HEAP_LOAD_F64((ptr >> 3) * 8); - default: - abort("invalid type for getValue: " + type); - } - return null; - } - var wasmMemory; - var ABORT = false; - function assert(condition, text) { - if (!condition) { - abort("Assertion failed: " + text); - } - } - function getCFunc(ident) { - var func = Module["_" + ident]; - assert( - func, - "Cannot call unknown function " + ident + ", make sure it is exported" - ); - return func; - } - function ccall(ident, returnType, argTypes, args, opts) { - var toC = { - string: function(str) { - var ret2 = 0; - if (str !== null && str !== void 0 && str !== 0) { - var len = (str.length << 2) + 1; - ret2 = stackAlloc(len); - stringToUTF8(str, ret2, len); - } - return ret2; - }, - array: function(arr) { - var ret2 = stackAlloc(arr.length); - writeArrayToMemory(arr, ret2); - return ret2; - } - }; - function convertReturnValue(ret2) { - if (returnType === "string") return UTF8ToString(ret2); - if (returnType === "boolean") return Boolean(ret2); - return ret2; - } - var func = getCFunc(ident); - var cArgs = []; - var stack = 0; - if (args) { - for (var i = 0; i < args.length; i++) { - var converter = toC[argTypes[i]]; - if (converter) { - if (stack === 0) stack = stackSave(); - cArgs[i] = converter(args[i]); - } else { - cArgs[i] = args[i]; - } - } - } - var ret = func.apply(null, cArgs); - ret = convertReturnValue(ret); - if (stack !== 0) stackRestore(stack); - return ret; - } - function cwrap(ident, returnType, argTypes, opts) { - argTypes = argTypes || []; - var numericArgs = argTypes.every(function(type) { - return type === "number"; - }); - var numericRet = returnType !== "string"; - if (numericRet && numericArgs && !opts) { - return getCFunc(ident); - } - return function() { - return ccall(ident, returnType, argTypes, arguments); - }; - } - var UTF8Decoder = new TextDecoder("utf8"); - function UTF8ToString(ptr, maxBytesToRead) { - if (!ptr) return ""; - var maxPtr = ptr + maxBytesToRead; - for (var end = ptr; !(end >= maxPtr) && HEAPU8[end]; ) ++end; - return UTF8Decoder.decode(HEAPU8.subarray(ptr, end)); - } - function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { - if (!(maxBytesToWrite > 0)) return 0; - var startIdx = outIdx; - var endIdx = outIdx + maxBytesToWrite - 1; - for (var i = 0; i < str.length; ++i) { - var u = str.charCodeAt(i); - if (u >= 55296 && u <= 57343) { - var u1 = str.charCodeAt(++i); - u = 65536 + ((u & 1023) << 10) | u1 & 1023; - } - if (u <= 127) { - if (outIdx >= endIdx) break; - heap[outIdx++] = u; - } else if (u <= 2047) { - if (outIdx + 1 >= endIdx) break; - heap[outIdx++] = 192 | u >> 6; - heap[outIdx++] = 128 | u & 63; - } else if (u <= 65535) { - if (outIdx + 2 >= endIdx) break; - heap[outIdx++] = 224 | u >> 12; - heap[outIdx++] = 128 | u >> 6 & 63; - heap[outIdx++] = 128 | u & 63; - } else { - if (outIdx + 3 >= endIdx) break; - heap[outIdx++] = 240 | u >> 18; - heap[outIdx++] = 128 | u >> 12 & 63; - heap[outIdx++] = 128 | u >> 6 & 63; - heap[outIdx++] = 128 | u & 63; - } - } - heap[outIdx] = 0; - return outIdx - startIdx; - } - function stringToUTF8(str, outPtr, maxBytesToWrite) { - return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); - } - function lengthBytesUTF8(str) { - var len = 0; - for (var i = 0; i < str.length; ++i) { - var u = str.charCodeAt(i); - if (u >= 55296 && u <= 57343) - u = 65536 + ((u & 1023) << 10) | str.charCodeAt(++i) & 1023; - if (u <= 127) ++len; - else if (u <= 2047) len += 2; - else if (u <= 65535) len += 3; - else len += 4; - } - return len; - } - function allocateUTF8(str) { - var size = lengthBytesUTF8(str) + 1; - var ret = _malloc(size); - if (ret) stringToUTF8Array(str, HEAP8, ret, size); - return ret; - } - function writeArrayToMemory(array, buffer2) { - HEAP8.set(array, buffer2); - } - function alignUp(x, multiple) { - if (x % multiple > 0) { - x += multiple - x % multiple; - } - return x; - } - var buffer, HEAP8, HEAPU8; - var HEAP_DATA_VIEW; - function updateGlobalBufferAndViews(buf) { - buffer = buf; - Module["HEAP_DATA_VIEW"] = HEAP_DATA_VIEW = new DataView(buf); - Module["HEAP8"] = HEAP8 = new Int8Array(buf); - Module["HEAP16"] = new Int16Array(buf); - Module["HEAP32"] = new Int32Array(buf); - Module["HEAPU8"] = HEAPU8 = new Uint8Array(buf); - Module["HEAPU16"] = new Uint16Array(buf); - Module["HEAPU32"] = new Uint32Array(buf); - Module["HEAPF32"] = new Float32Array(buf); - Module["HEAPF64"] = new Float64Array(buf); - } - Module["INITIAL_MEMORY"] || 16777216; - var wasmTable; - var __ATPRERUN__ = []; - var __ATINIT__ = []; - var __ATPOSTRUN__ = []; - function preRun() { - if (Module["preRun"]) { - if (typeof Module["preRun"] == "function") - Module["preRun"] = [Module["preRun"]]; - while (Module["preRun"].length) { - addOnPreRun(Module["preRun"].shift()); - } - } - callRuntimeCallbacks(__ATPRERUN__); - } - function initRuntime() { - callRuntimeCallbacks(__ATINIT__); - } - function postRun() { - if (Module["postRun"]) { - if (typeof Module["postRun"] == "function") - Module["postRun"] = [Module["postRun"]]; - while (Module["postRun"].length) { - addOnPostRun(Module["postRun"].shift()); - } - } - callRuntimeCallbacks(__ATPOSTRUN__); - } - function addOnPreRun(cb) { - __ATPRERUN__.unshift(cb); - } - function addOnInit(cb) { - __ATINIT__.unshift(cb); - } - function addOnPostRun(cb) { - __ATPOSTRUN__.unshift(cb); - } - var runDependencies = 0; - var dependenciesFulfilled = null; - function addRunDependency(id) { - runDependencies++; - if (Module["monitorRunDependencies"]) { - Module["monitorRunDependencies"](runDependencies); - } - } - function removeRunDependency(id) { - runDependencies--; - if (Module["monitorRunDependencies"]) { - Module["monitorRunDependencies"](runDependencies); - } - if (runDependencies == 0) { - if (dependenciesFulfilled) { - var callback = dependenciesFulfilled; - dependenciesFulfilled = null; - callback(); - } - } - } - Module["preloadedImages"] = {}; - Module["preloadedAudios"] = {}; - function abort(what) { - if (Module["onAbort"]) { - Module["onAbort"](what); - } - what += ""; - err(what); - ABORT = true; - what = "abort(" + what + "). Build with -s ASSERTIONS=1 for more info."; - var e = new WebAssembly.RuntimeError(what); - readyPromiseReject(e); - throw e; - } - var dataURIPrefix = "data:application/octet-stream;base64,"; - function isDataURI(filename) { - return filename.startsWith(dataURIPrefix); - } - var wasmBinaryFile = "data:application/octet-stream;base64,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QCAAKAJIIgEgASADaiADEAcaAkAgAyAAKAJsIgFNBEAgACABIANrNgJsDAELIABCADcCbAsgACAAKAJoIANrIgE2AmggACAAKAJYIANrNgJYIAEgACgChC5JBEAgACABNgKELgsgAEH8gAEoAgARAwAgAiADaiECCwJAIAAoAgAiASgCBCIERQ0AIAAoAjwhBSAAIAIgBCACIARJGyICBH8gACgCSCAAKAJoaiAFaiEFIAEgBCACazYCBAJAAkACQAJAIAEoAhwiBCgCFEEBaw4CAQACCyAEQaABaiAFIAEoAgAgAkHcgAEoAgARCAAMAgsgASABKAIwIAUgASgCACACQcSAASgCABEEADYCMAwBCyAFIAEoAgAgAhAHGgsgASABKAIAIAJqNgIAIAEgASgCCCACajYCCCAAKAI8BSAFCyACaiICNgI8AkAgACgChC4iASACakEDSQ0AIAAoAmggAWshAQJAIAAoAnRBgQhPBEAgACAAIAAoAkggAWoiAi0AACACLQABIAAoAnwRAAA2AlQMAQsgAUUNACAAIAFBAWsgACgChAERAgAaCyAAKAKELiAAKAI8IgJBAUZrIgRFDQAgACABIAQgACgCgAERBQAgACAAKAKELiAEazYChC4gACgCPCECCyACQYUCSw0AIAAoAgAoAgRFDQAgACgCMCEBDAELCwJAIAAoAkQiAiAAKAJAIgNNDQAgAAJ/IAAoAjwgACgCaGoiASADSwRAIAAoAkggAWpBACACIAFrIgNBggIgA0GCAkkbIgMQGSABIANqDAELIAFBggJqIgEgA00NASAAKAJIIANqQQAgAiADayICIAEgA2siAyACIANJGyIDEBkgACgCQCADags2AkALC50CAQF/AkAgAAJ/IAAoAqAuIgFBwABGBEAgACgCBCAAKAIQaiAAKQOYLjcAACAAQgA3A5guIAAgACgCEEEIajYCEEEADAELIAFBIE4EQCAAKAIEIAAoAhBqIAApA5guPgAAIAAgAEGcLmo1AgA3A5guIAAgACgCEEEEajYCECAAIAAoAqAuQSBrIgE2AqAuCyABQRBOBEAgACgCBCAAKAIQaiAAKQOYLj0AACAAIAAoAhBBAmo2AhAgACAAKQOYLkIQiDcDmC4gACAAKAKgLkEQayIBNgKgLgsgAUEISA0BIAAgACgCECIBQQFqNgIQ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KAJUIgEEfyABQQAgARAiEBkgACgCVAVBAAsQBiAAQQA2AlQLC9wMAgl/AX4jAEFAaiIGJAACQAJAAkACQAJAIAEoAjBBABAjIgVBAkZBACABKAI4QQAQIyIEQQFGGw0AIAVBAUZBACAEQQJGGw0AIAVBAkciAw0BIARBAkcNAQsgASABLwEMQYAQcjsBDEEAIQMMAQsgASABLwEMQf/vA3E7AQxBACEFIANFBEBB9eABIAEoAjAgAEEIahBpIgVFDQILIAJBgAJxBEAgBSEDDAELIARBAkcEQCAFIQMMAQtB9cYBIAEoAjggAEEIahBpIgNFBEAgBRAcDAILIAMgBTYCAAsgASABLwEMQf7/A3EgAS8BUiIFQQBHcjsBDAJAAkACQAJAAn8CQAJAIAEpAyhC/v///w9WDQAgASkDIEL+////D1YNACACQYAEcUUNASABKQNIQv////8PVA0BCyAFQYECa0H//wNxQQNJIQdBAQwBCyAFQYECa0H//wNxIQQgAkGACnFBgApHDQEgBEEDSSEHQQALIQkgBkIcEBciBEUEQCAAQQhqIgAEQCAAQQA2AgQgAEEONgIACyADEBwMBQsgAkGACHEhBQJAAkAgAkGAAnEEQAJAIAUNACABKQMgQv////8PVg0AIAEpAyhCgICAgBBUDQMLIAQgASkDKBAYIAEpAyAhDAwBCwJAAkACQCAFDQAgASkDIEL/////D1YNACABKQMoIgxC/////w9WDQEgASkDSEKAgICAEFQNBAsgASkDKCIMQv////8PVA0BCyAEIAwQGAsgASkDICIMQv////8PWgRAIAQgDBAYCyABKQNIIgxC/////w9UDQELIAQgDBAYCyAELQAARQRAIABBCGoiAARAIABBADYCBCAAQRQ2AgALIAQQCCADEBwMBQtBASEKQQEgBC0AAAR+IAQpAxAFQgALp0H//wNxIAYQRyEFIAQQCCAFIAM2AgAgBw0BDAILIAMhBSAEQQJLDQELIAZCBxAXIgRFBEAgAEEIaiIABEAgAEEANgIEIABBDjYCAAsgBRAcDAMLIARBAhANIARBhxJBAhAsIAQgAS0AUhBwIAQgAS8BEBANIAQtAABFBEAgAEEIaiIABEAgAEEANgIEIABBFDYCAAsgBBAIDAILQYGyAkEHIAYQRyEDIAQQCCADIAU2AgBBASEL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NgIEIABB7A42AhggGiEdDAMLIARB/wFxIgJBwABxRQRAIAggDSADLwECQQJ0aiAdp0F/IAJ0QX9zcUECdGoiAy0AASICayEIIB0gAq2IIR0gAy0AACIERQ0HDAELCyAEQSBxBEAgB0G//gA2AgQgASECDAgLIAdB0f4ANgIEIABB0A42AhggASECDAcLIB1BfyAGdEF/c62DIBt8IhunIQUgCCAEQQ9xIgNrIQggGiAErUIPg4ghHSABIBdrIgYgAjMBAiAaQX8gA3RBf3Otg3ynIgRPDQIgBCAGayIGIBlNDQEgBygCjEdFDQEgB0HR/gA2AgQgAEG5DDYCGAsgASECIAshBQwFCwJAIA5FBEAgDCARIAZraiEDDAELIAYgDk0EQCAMIA4gBmtqIQMMAQsgDCARIAYgDmsiBmtqIQMgBSAGTQ0AIAUgBmshBQJAAkAgASADTSABIA8gAWusIhogBq0iGyAaIBtUGyIapyIGaiICIANLcQ0AIAMgBmogAUsgASADT3ENACABIAMgBhAHGiACIQEMAQsgASADIAMgAWsiASABQR91IgFqIAFzIgIQByACaiEBIBogAq0iHn0iHFANACACIANqIQIDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgASACKQAANwAAIAEgAikAGDcAGCABIAIpABA3ABAgASACKQAINwAIIBpCIH0hGiACQSBqIQIgAUEgaiEBIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAEgAikAADcAACABIAIpABg3ABggASACKQAQNwAQIAEgAikACDcACCABIAIpADg3ADggASACKQAwNwAwIAEgAikAKDcAKCABIAIpACA3ACAgASACKQBYNwBYIAEgAikAUDcAUCABIAIpAEg3AEggASACKQBANwBAIAEgAikAYDcAYCABIAIpAGg3AGggASACKQBwNwBwIAEgAikAeDcAeCACQYABaiECIAFBgAFqIQEgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAEgAikAADcAACABIAIpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCABIAIpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCABIAIoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCABIAIvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCABIAItAAA6AAAgAkEBaiECIAFBAWohAQsgHEIAUg0ACwsgDiEGIAwhAwsgBSAGSwRAAkACQCABIANNIAEgDyABa6wiGiAGrSIbIBogG1QbIhqnIglqIgIgA0txDQAgAyAJaiABSyABIANPcQ0AIAEgAyAJEAcaDAELIAEgAyADIAFrIgEgAUEfdSIBaiABcyIBEAcgAWohAiAaIAGtIh59IhxQDQAgASADaiEBA0ACQCAcIB4gHCAeVBsiG0IgVARAIBshGgwBCyAbIhpCIH0iIEIFiEIBfEIDgyIfUEUEQANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCAaQiB9IRogAUEgaiEBIAJBIGohAiAfQgF9Ih9CAFINAAsLICBC4ABUDQADQCACIAEpAAA3AAAgAiABKQAYNwAYIAIgASkAEDcAECACIAEpAAg3AAggAiABKQA4NwA4IAIgASkAMDcAMCACIAEpACg3ACggAiABKQAgNwAgIAIgASkAWDcAWCACIAEpAFA3AFAgAiABKQBINwBIIAIgASkAQDcAQCACIAEpAGA3AGAgAiABKQBoNwBoIAIgASkAcDcAcCACIAEpAHg3AHggAUGAAWohASACQYABaiECIBpCgAF9IhpCH1YNAAsLIBpCEFoEQCACIAEpAAA3AAAgAiABKQAINwAIIBpCEH0hGiACQRBqIQIgAUEQaiEBCyAaQghaBEAgAiABKQAANwAAIBpCCH0hGiACQQhqIQIgAUEIaiEBCyAaQgRaBEAgAiABKAAANgAAIBpCBH0hGiACQQRqIQIgAUEEaiEBCyAaQgJaBEAgAiABLwAAOwAAIBpCAn0hGiACQQJqIQIgAUECaiEBCyAcIBt9IRwgGlBFBEAgAiABLQAAOgAAIAJBAWohAiABQQFqIQELIBxCAFINAAsLIAUgBmshAUEAIARrIQUCQCAEQQdLBEAgBCEDDAELIAEgBE0EQCAEIQMMAQsgAiAEayEFA0ACQCACIAUpAAA3AAAgBEEBdCEDIAEgBGshASACIARqIQIgBEEDSw0AIAMhBCABIANLDQELC0EAIANrIQULIAIgBWohBAJAIAUgDyACa6wiGiABrSIbIBogG1QbIhqnIgFIIAVBf0pxDQAgBUEBSCABIARqIAJLcQ0AIAIgBCABEAcgAWohAgwDCyACIAQgAyADQR91IgFqIAFzIgEQByABaiECIBogAa0iHn0iHFANAiABIARqIQEDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIBpCIH0hGiABQSBqIQEgAkEgaiECIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCACIAEpADg3ADggAiABKQAwNwAwIAIgASkAKDcAKCACIAEpACA3ACAgAiABKQBYNwBYIAIgASkAUDcAUCACIAEpAEg3AEggAiABKQBANwBAIAIgASkAYDcAYCACIAEpAGg3AGggAiABKQBwNwBwIAIgASkAeDcAeCABQYABaiEBIAJBgAFqIQIgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAIgASkAADcAACACIAEpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCACIAEpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCACIAEoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCACIAEvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCACIAEtAAA6AAAgAkEBaiECIAFBAWohAQsgHFBFDQALDAILAkAgASADTSABIA8gAWusIhogBa0iGyAaIBtUGyIapyIEaiICIANLcQ0AIAMgBGogAUsgASADT3ENACABIAMgBBAHGgwCCyABIAMgAyABayIBIAFBH3UiAWogAXMiARAHIAFqIQIgGiABrSIefSIcUA0BIAEgA2ohAQNAAkAgHCAeIBwgHlQbIhtCIFQEQCAbIRoMAQsgGyIaQiB9IiBCBYhCAXxCA4MiH1BFBEADQCACIAEpAAA3AAAgAiABKQAYNwAYIAIgASkAEDcAECACIAEpAAg3AAggGkIgfSEaIAFBIGohASACQSBqIQIgH0IBfSIfQgBSDQALCyAgQuAAVA0AA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIAIgASkAODcAOCACIAEpADA3ADAgAiABKQAoNwAoIAIgASkAIDcAICACIAEpAFg3AFggAiABKQBQNwBQIAIgASkASDcASCACIAEpAEA3AEAgAiABKQBgNwBgIAIgASkAaDcAaCACIAEpAHA3AHAgAiABKQB4NwB4IAFBgAFqIQEgAkGAAWohAiAaQoABfSIaQh9WDQALCyAaQhBaBEAgAiABKQAANwAAIAIgASkACDcACCAaQhB9IRogAkEQaiECIAFBEGohAQsgGkIIWgRAIAIgASkAADcAACAaQgh9IRogAkEIaiECIAFBCGohAQsgGkIEWgRAIAIgASgAADYAACAaQgR9IRogAkEEaiECIAFBBGohAQsgGkICWgRAIAIgAS8AADsAACAaQgJ9IRogAkECaiECIAFBAmohAQsgHCAbfSEcIBpQRQRAIAIgAS0AADoAACACQQFqIQIgAUEBaiEBCyAcUEUNAAsMAQsCQAJAIBYEQAJAIAQgBUkEQCAHKAKYRyAESw0BCyABIARrIQMCQEEAIARrIgVBf0ogDyABa6wiGiAbIBogG1QbIhqnIgIgBUpxDQAgBUEBSCACIANqIAFLcQ0AIAEgAyACEAcgAmohAgwFCyABIAMgBCAEQR91IgFqIAFzIgEQByABaiECIBogAa0iHn0iHFANBCABIANqIQEDQAJAIBwgHiAcIB5UGyIbQiBUBEAgGyEaDAELIBsiGkIgfSIgQgWIQgF8QgODIh9QRQRAA0AgAiABKQAANwAAIAIgASkAGDcAGCACIAEpABA3ABAgAiABKQAINwAIIBpCIH0hGiABQSBqIQEgAkEgaiECIB9CAX0iH0IAUg0ACwsgIELgAFQNAANAIAIgASkAADcAACACIAEpABg3ABggAiABKQAQNwAQIAIgASkACDcACCACIAEpADg3ADggAiABKQAwNwAwIAIgASkAKDcAKCACIAEpACA3ACAgAiABKQBYNwBYIAIgASkAUDcAUCACIAEpAEg3AEggAiABKQBANwBAIAIgASkAYDcAYCACIAEpAGg3AGggAiABKQBwNwBwIAIgASkAeDcAeCABQYABaiEBIAJBgAFqIQIgGkKAAX0iGkIfVg0ACwsgGkIQWgRAIAIgASkAADcAACACIAEpAAg3AAggGkIQfSEaIAJBEGohAiABQRBqIQELIBpCCFoEQCACIAEpAAA3AAAgGkIIfSEaIAJBCGohAiABQQhqIQELIBpCBFoEQCACIAEoAAA2AAAgGkIEfSEaIAJBBGohAiABQQRqIQELIBpCAloEQCACIAEvAAA7AAAgGkICfSEaIAJBAmohAiABQQJqIQELIBwgG30hHCAaUEUEQCACIAEtAAA6AAAgAkEBaiECIAFBAWohAQsgHFBFDQALDAQLIBAgAWsiCUEBaiIGIAUgBSAGSxshAyABIARrIQIgAUEHcUUNAiADRQ0CIAEgAi0AADoAACACQQFqIQIgAUEBaiIGQQdxQQAgA0EBayIFGw0BIAYhASAFIQMgCSEGDAILAkAgBCAFSQRAIAcoAphHIARLDQELIAEgASAEayIGKQAANwAAIAEgBUEBa0EHcUEBaiIDaiECIAUgA2siBEUNAyADIAZqIQEDQCACIAEpAAA3AAAgAUEIaiEBIAJBCGohAiAEQQhrIgQNAAsMAwsgASAEIAUQPyECDAILIAEgAi0AADoAASAJQQFrIQYgA0ECayEFIAJBAWohAgJAIAFBAmoiCkEHcUUNACAFRQ0AIAEgAi0AADoAAiAJQQJrIQYgA0EDayEFIAJBAWohAgJAIAFBA2oiCkEHcUUNACAFRQ0AIAEgAi0AADoAAyAJQQNrIQYgA0EEayEFIAJBAWohAgJAIAFBBGoiCkEHcUUNACAFRQ0AIAEgAi0AADoABCAJQQRrIQYgA0EFayEFIAJBAWohAgJAIAFBBWoiCkEHcUUNACAFRQ0AIAEgAi0AADoABSAJQQVrIQYgA0EGayEFIAJBAWohAgJAIAFBBmoiCkEHcUUNACAFRQ0AIAEgAi0AADoABiAJQQZrIQYgA0EHayEFIAJBAWohAgJAIAFBB2oiCkEHcUUNACAFRQ0AIAEgAi0AADoAByAJQQdrIQYgA0EIayEDIAFBCGohASACQQFqIQIMBgsgCiEBIAUhAwwFCyAKIQEgBSEDDAQLIAohASAFIQMMAwsgCiEBIAUhAwwCCyAKIQEgBSEDDAELIAohASAFIQMLAkACQCAGQRdNBEAgA0UNASADQQFrIQUgA0EHcSIEBEADQCABIAItAAA6AAAgA0EBayEDIAFBAWohASACQQFqIQIgBEEBayIEDQALCyAFQQdJDQEDQCABIAItAAA6AAAgASACLQABOgABIAEgAi0AAjoAAiABIAItAAM6AAMgASACLQAEOgAEIAEgAi0ABToABSABIAItAAY6AAYgASACLQAHOgAHIAFBCGohASACQQhqIQIgA0EIayIDDQALDAELIAMNAQsgASECDAELIAEgBCADED8hAgsgCyEFDAELIAEgAy0AAjoAACABQQFqIQILIAUgFE8NACACIBNJDQELCyAAIAI2AgwgACAFIAhBA3ZrIgE2AgAgACATIAJrQYMCajYCECAAIBQgAWtBDmo2AgQgByAIQQdxIgA2AogBIAcgHUJ/IACthkJ/hYM+AoQBC+cFAQR/IAMgAiACIANLGyEEIAAgAWshAgJAIABBB3FFDQAgBEUNACAAIAItAAA6AAAgA0EBayEGIAJBAWohAiAAQQFqIgdBB3FBACAEQQFrIgUbRQRAIAchACAFIQQgBiEDDAELIAAgAi0AADoAASADQQJrIQYgBEECayEFIAJBAWohAgJAIABBAmoiB0EHcUUNACAFRQ0AIAAgAi0AADoAAiADQQNrIQYgBEEDayEFIAJBAWohAgJAIABBA2oiB0EHcUUNACAFRQ0AIAAgAi0AADoAAyADQQRrIQYgBEEEayEFIAJBAWohAgJAIABBBGoiB0EHcUUNACAFRQ0AIAAgAi0AADoABCADQQVrIQYgBEEFayEFIAJBAWohAgJAIABBBWoiB0EHcUUNACAFRQ0AIAAgAi0AADoABSADQQZrIQYgBEEGayEFIAJBAWohAgJAIABBBmoiB0EHcUUNACAFRQ0AIAAgAi0AADoABiADQQdrIQYgBEEHayEFIAJBAWohAgJAIABBB2oiB0EHcUUNACAFRQ0AIAAgAi0AADoAByADQQhrIQMgBEEIayEEIABBCGohACACQQFqIQIMBgsgByEAIAUhBCAGIQMMBQsgByEAIAUhBCAGIQMMBAsgByEAIAUhBCAGIQMMAwsgByEAIAUhBCAGIQMMAgsgByEAIAUhBCAGIQMMAQsgByEAIAUhBCAGIQMLAkAgA0EXTQRAIARFDQEgBEEBayEBIARBB3EiAwRAA0AgACACLQAAOgAAIARBAWshBCAAQQFqIQAgAkEBaiECIANBAWsiAw0ACwsgAUEHSQ0BA0AgACACLQAAOgAAIAAgAi0AAToAASAAIAItAAI6AAIgACACLQADOgADIAAgAi0ABDoABCAAIAItAAU6AAUgACACLQAGOgAGIAAgAi0ABzoAByAAQQhqIQAgAkEIaiECIARBCGsiBA0ACwwBCyAERQ0AIAAgASAEED8hAAsgAAvyCAEXfyAAKAJoIgwgACgCMEGGAmsiBWtBACAFIAxJGyENIAAoAnQhAiAAKAKQASEPIAAoAkgiDiAMaiIJIAAoAnAiBUECIAUbIgVBAWsiBmoiAy0AASESIAMtAAAhEyAGIA5qIQZBAyEDIAAoApQBIRYgACgCPCEUIAAoAkwhECAAKAI4IRECQAJ/IAVBA0kEQCANIQggDgwBCyAAIABBACAJLQABIAAoAnwRAAAgCS0AAiAAKAJ8EQAAIQoDQCAAIAogAyAJai0AACAAKAJ8EQAAIQogACgCUCAKQQF0ai8BACIIIAEgCCABQf//A3FJIggbIQEgA0ECayAHIAgbIQcgA0EBaiIDIAVNDQALIAFB//8DcSAHIA1qIghB//8DcU0NASAGIAdB//8DcSIDayEGIA4gA2sLIQMCQAJAIAwgAUH//wNxTQ0AIAIgAkECdiAFIA9JGyEKIA1B//8DcSEVIAlBAmohDyAJQQRrIRcDQAJAAkAgBiABQf//A3EiC2otAAAgE0cNACAGIAtBAWoiAWotAAAgEkcNACADIAtqIgItAAAgCS0AAEcNACABIANqLQAAIAktAAFGDQELIApBAWsiCkUNAiAQIAsgEXFBAXRqLwEAIgEgCEH//wNxSw0BDAILIAJBAmohAUEAIQQgDyECAkADQCACLQAAIAEtAABHDQEgAi0AASABLQABRwRAIARBAXIhBAwCCyACLQACIAEtAAJHBEAgBEECciEEDAILIAItAAMgAS0AA0cEQCAEQQNyIQQMAgsgAi0ABCABLQAERwRAIARBBHIhBAwCCyACLQAFIAEtAAVHBEAgBEEFciEEDAILIAItAAYgAS0ABkcEQCAEQQZyIQQMAgsgAi0AByABLQAHRwRAIARBB3IhBAwCCyABQQhqIQEgAkEIaiECIARB+AFJIRggBEEIaiEEIBgNAAtBgAIhBAsCQAJAIAUgBEECaiICSQRAIAAgCyAHQf//A3FrIgY2AmwgAiAUSwRAIBQPCyACIBZPBEAgAg8LIAkgBEEBaiIFaiIBLQABIRIgAS0AACETAkAgAkEESQ0AIAIgBmogDE8NACAGQf//A3EhCCAEQQFrIQtBACEDQQAhBwNAIBAgAyAIaiARcUEBdGovAQAiASAGQf//A3FJBEAgAyAVaiABTw0IIAMhByABIQYLIANBAWoiAyALTQ0ACyAAIAAgAEEAIAIgF2oiAS0AACAAKAJ8EQAAIAEtAAEgACgCfBEAACABLQACIAAoAnwRAAAhASAAKAJQIAFBAXRqLwEAIgEgBkH//wNxTwRAIAdB//8DcSEDIAYhAQwDCyAEQQJrIgdB//8DcSIDIBVqIAFPDQYMAgsgAyAFaiEGIAIhBQsgCkEBayIKRQ0DIBAgCyARcUEBdGovAQAiASAIQf//A3FNDQMMAQsgByANaiEIIA4gA2siAyAFaiEGIAIhBQsgDCABQf//A3FLDQALCyAFDwsgAiEFCyAFIAAoAjwiACAAIAVLGwuGBQETfyAAKAJ0IgMgA0ECdiAAKAJwIgNBAiADGyIDIAAoApABSRshByAAKAJoIgogACgCMEGGAmsiBWtB//8DcUEAIAUgCkkbIQwgACgCSCIIIApqIgkgA0EBayICaiIFLQABIQ0gBS0AACEOIAlBAmohBSACIAhqIQsgACgClAEhEiAAKAI8IQ8gACgCTCEQIAAoAjghESAAKAKIAUEFSCETA0ACQCAKIAFB//8DcU0NAANAAkACQCALIAFB//8DcSIGai0AACAORw0AIAsgBkEBaiIBai0AACANRw0AIAYgCGoiAi0AACAJLQAARw0AIAEgCGotAAAgCS0AAUYNAQsgB0EBayIHRQ0CIAwgECAGIBFxQQF0ai8BACIBSQ0BDAILCyACQQJqIQRBACECIAUhAQJAA0AgAS0AACAELQAARw0BIAEtAAEgBC0AAUcEQCACQQFyIQIMAgsgAS0AAiAELQACRwRAIAJBAnIhAgwCCyABLQADIAQtAANHBEAgAkEDciECDAILIAEtAAQgBC0ABEcEQCACQQRyIQIMAgsgAS0ABSAELQAFRwRAIAJBBXIhAgwCCyABLQAGIAQtAAZHBEAgAkEGciECDAILIAEtAAcgBC0AB0cEQCACQQdyIQIMAgsgBEEIaiEEIAFBCGohASACQfgBSSEUIAJBCGohAiAUDQALQYACIQILAkAgAyACQQJqIgFJBEAgACAGNgJsIAEgD0sEQCAPDwsgASASTwRAIAEPCyAIIAJBAWoiA2ohCyADIAlqIgMtAAEhDSADLQAAIQ4gASEDDAELIBMNAQsgB0EBayIHRQ0AIAwgECAGIBFxQQF0ai8BACIBSQ0BCwsgAwvLAQECfwJAA0AgAC0AACABLQAARw0BIAAtAAEgAS0AAUcEQCACQQFyDwsgAC0AAiABLQACRwRAIAJBAnIPCyAALQADIAEtAANHBEAgAkEDcg8LIAAtAAQgAS0ABEcEQCACQQRyDwsgAC0ABSABLQAFRwRAIAJBBXIPCyAALQAGIAEtAAZHBEAgAkEGcg8LIAAtAAcgAS0AB0cEQCACQQdyDwsgAUEIaiEBIABBCGohACACQfgBSSEDIAJBCGohAiADDQALQYACIQILIAIL5wwBB38gAEF/cyEAIAJBF08EQAJAIAFBA3FFDQAgAS0AACAAQf8BcXNBAnRB0BhqKAIAIABBCHZzIQAgAkEBayIEQQAgAUEBaiIDQQNxG0UEQCAEIQIgAyEBDAELIAEtAAEgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBAmohAwJAIAJBAmsiBEUNACADQQNxRQ0AIAEtAAIgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBA2ohAwJAIAJBA2siBEUNACADQQNxRQ0AIAEtAAMgAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBBGohASACQQRrIQIMAgsgBCECIAMhAQwBCyAEIQIgAyEBCyACQRRuIgNBbGwhCQJAIANBAWsiCEUEQEEAIQQMAQsgA0EUbCABakEUayEDQQAhBANAIAEoAhAgB3MiB0EWdkH8B3FB0DhqKAIAIAdBDnZB/AdxQdAwaigCACAHQQZ2QfwHcUHQKGooAgAgB0H/AXFBAnRB0CBqKAIAc3NzIQcgASgCDCAGcyIGQRZ2QfwHcUHQOGooAgAgBkEOdkH8B3FB0DBqKAIAIAZBBnZB/AdxQdAoaigCACAGQf8BcUECdEHQIGooAgBzc3MhBiABKAIIIAVzIgVBFnZB/AdxQdA4aigCACAFQQ52QfwHcUHQMGooAgAgBUEGdkH8B3FB0ChqKAIAIAVB/wFxQQJ0QdAgaigCAHNzcyEFIAEoAgQgBHMiBEEWdkH8B3FB0DhqKAIAIARBDnZB/AdxQdAwaigCACAEQQZ2QfwHcUHQKGooAgAgBEH/AXFBAnRB0CBqKAIAc3NzIQQgASgCACAAcyIAQRZ2QfwHcUHQOGooAgAgAEEOdkH8B3FB0DBqKAIAIABBBnZB/AdxQdAoaigCACAAQf8BcUECdEHQIGooAgBzc3MhACABQRRqIQEgCEEBayIIDQALIAMhAQsgAiAJaiECIAEoAhAgASgCDCABKAIIIAEoAgQgASgCACAAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQf8BcUECdEHQGGooAgAgBHNzIABBCHZzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBB/wFxQQJ0QdAYaigCACAFc3MgAEEIdnMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEH/AXFBAnRB0BhqKAIAIAZzcyAAQQh2cyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQf8BcUECdEHQGGooAgAgB3NzIABBCHZzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyIAQQh2IABB/wFxQQJ0QdAYaigCAHMiAEEIdiAAQf8BcUECdEHQGGooAgBzIgBBCHYgAEH/AXFBAnRB0BhqKAIAcyEAIAFBFGohAQsgAkEHSwRAA0AgAS0AByABLQAGIAEtAAUgAS0ABCABLQADIAEtAAIgAS0AASABLQAAIABB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyIAQf8BcXNBAnRB0BhqKAIAIABBCHZzIgBB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyIAQf8BcXNBAnRB0BhqKAIAIABBCHZzIgBB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBCGohASACQQhrIgJBB0sNAAsLAkAgAkUNACACQQFxBH8gAS0AACAAQf8BcXNBAnRB0BhqKAIAIABBCHZzIQAgAUEBaiEBIAJBAWsFIAILIQMgAkEBRg0AA0AgAS0AASABLQAAIABB/wFxc0ECdEHQGGooAgAgAEEIdnMiAEH/AXFzQQJ0QdAYaigCACAAQQh2cyEAIAFBAmohASADQQJrIgMNAAsLIABBf3MLwgIBA38jAEEQayIIJAACfwJAIAAEQCAEDQEgBVANAQsgBgRAIAZBADYCBCAGQRI2AgALQQAMAQtBgAEQCSIHRQRAIAYEQCAGQQA2AgQgBkEONgIAC0EADAELIAcgATcDCCAHQgA3AwAgB0EoaiIJECogByAFNwMYIAcgBDYCECAHIAM6AGAgB0EANgJsIAdCADcCZCAAKQMYIQEgCEF/NgIIIAhCjoCAgPAANwMAIAdBECAIECQgAUL/gQGDhCIBNwNwIAcgAadBBnZBAXE6AHgCQCACRQ0AIAkgAhBgQX9KDQAgBxAGQQAMAQsgBhBfIgIEQCAAIAAoAjBBAWo2AjAgAiAHNgIIIAJBATYCBCACIAA2AgAgAkI/IAAgB0EAQgBBDkEBEQoAIgEgAUIAUxs3AxgLIAILIQAgCEEQaiQAIAALYgEBf0E4EAkiAUUEQCAABEAgAEEANgIEIABBDjYCAAtBAA8LIAFBADYCCCABQgA3AwAgAUIANwMgIAFCgICAgBA3AiwgAUEAOgAoIAFBADYCFCABQgA3AgwgAUEAOwE0IAELuwEBAX4gASkDACICQgKDUEUEQCAAIAEpAxA3AxALIAJCBINQRQRAIAAgASkDGDcDGAsgAkIIg1BFBEAgACABKQMgNwMgCyACQhCDUEUEQCAAIAEoAig2AigLIAJCIINQRQRAIAAgASgCLDYCLAsgAkLAAINQRQRAIAAgAS8BMDsBMAsgAkKAAYNQRQRAIAAgAS8BMjsBMgsgAkKAAoNQRQRAIAAgASgCNDYCNAsgACAAKQMAIAKENwMAQQALGQAgAUUEQEEADwsgACABKAIAIAEzAQQQGws3AQJ/IABBACABG0UEQCAAIAFGDwsgAC8BBCIDIAEvAQRGBH8gACgCACABKAIAIAMQPQVBAQtFCyIBAX8gAUUEQEEADwsgARAJIgJFBEBBAA8LIAIgACABEAcLKQAgACABIAIgAyAEEEUiAEUEQEEADwsgACACQQAgBBA1IQEgABAGIAELcQEBfgJ/AkAgAkJ/VwRAIAMEQCADQQA2AgQgA0EUNgIACwwBCyAAIAEgAhARIgRCf1cEQCADBEAgAyAAKAIMNgIAIAMgACgCEDYCBAsMAQtBACACIARXDQEaIAMEQCADQQA2AgQgA0ERNgIACwtBfwsLNQAgACABIAJBABAmIgBFBEBBfw8LIAMEQCADIAAtAAk6AAALIAQEQCAEIAAoAkQ2AgALQQAL/AECAn8BfiMAQRBrIgMkAAJAIAAgA0EOaiABQYAGQQAQRiIARQRAIAIhAAwBCyADLwEOIgFBBUkEQCACIQAMAQsgAC0AAEEBRwRAIAIhAAwBCyAAIAGtQv//A4MQFyIBRQRAIAIhAAwBCyABEH0aAkAgARAVIAIEfwJ/IAIvAQQhAEEAIAIoAgAiBEUNABpBACAEIABB1IABKAIAEQAACwVBAAtHBEAgAiEADAELIAEgAS0AAAR+IAEpAwggASkDEH0FQgALIgVC//8DgxATIAWnQf//A3FBgBBBABA1IgBFBEAgAiEADAELIAIQEAsgARAICyADQRBqJAAgAAvmDwIIfwJ+IwBB4ABrIgckAEEeQS4gAxshCwJAAkAgAgRAIAIiBSIGLQAABH4gBikDCCAGKQMQfQVCAAsgC61aDQEgBARAIARBADYCBCAEQRM2AgALQn8hDQwCCyABIAutIAcgBBAtIgUNAEJ/IQ0MAQsgBUIEEBMoAABBoxJBqBIgAxsoAABHBEAgBARAIARBADYCBCAEQRM2AgALQn8hDSACDQEgBRAIDAELIABCADcDICAAQQA2AhggAEL/////DzcDECAAQQA7AQwgAEG/hig2AgggAEEBOgAGIABBADsBBCAAQQA2AgAgAEIANwNIIABBgIDYjXg2AkQgAEIANwMoIABCADcDMCAAQgA3AzggAEFAa0EAOwEAIABCADcDUCAAIAMEf0EABSAFEAwLOwEIIAAgBRAMOwEKIAAgBRAMOwEMIAAgBRAMNgIQIAUQDCEGIAUQDCEJIAdBADYCWCAHQgA3A1AgB0IANwNIIAcgCUEfcTYCPCAHIAZBC3Y2AjggByAGQQV2QT9xNgI0IAcgBkEBdEE+cTYCMCAHIAlBCXZB0ABqNgJEIAcgCUEFdkEPcUEBazYCQCAAIAdBMGoQBTYCFCAAIAUQFTYCGCAAIAUQFa03AyAgACAFEBWtNwMoIAUQDCEIIAUQDCEGIAACfiADBEBBACEJIABBADYCRCAAQQA7AUAgAEEANgI8QgAMAQsgBRAMIQkgACAFEAw2AjwgACAFEAw7AUAgACAFEBU2AkQgBRAVrQs3A0ggBS0AAEUEQCAEBEAgBEEANgIEIARBFDYCAAtCfyENIAINASAFEAgMAQsCQCAALwEMIgpBAXEEQCAKQcAAcQRAIABB//8DOwFSDAILIABBATsBUgwBCyAAQQA7AVILIABBADYCOCAAQgA3AzAgBiAIaiAJaiEKAkAgAgRAIAUtAAAEfiAFKQMIIAUpAxB9BUIACyAKrVoNASAEBEAgBEEANgIEIARBFTYCAAtCfyENDAILIAUQCCABIAqtQQAgBBAtIgUNAEJ/IQ0MAQsCQCAIRQ0AIAAgBSABIAhBASAEEGQiCDYCMCAIRQRAIAQoAgBBEUYEQCAEBEAgBEEANgIEIARBFTYCAAsLQn8hDSACDQIgBRAIDAILIAAtAA1BCHFFDQAgCEECECNBBUcNACAEBEAgBEEANgIEIARBFTYCAAtCfyENIAINASAFEAgMAQsgAEE0aiEIAkAgBkUNACAFIAEgBkEAIAQQRSIMRQRAQn8hDSACDQIgBRAIDAILIAwgBkGAAkGABCADGyAIIAQQbiEGIAwQBiAGRQRAQn8hDSACDQIgBRAIDAILIANFDQAgAEEBOgAECwJAIAlFDQAgACAFIAEgCUEAIAQQZCIBNgI4IAFFBEBCfyENIAINAiAFEAgMAgsgAC0ADUEIcUUNACABQQIQI0EFRw0AIAQEQCAEQQA2AgQgBEEVNgIAC0J/IQ0gAg0BIAUQCAwBCyAAIAAoAjRB9eABIAAoAjAQZzYCMCAAIAAoAjRB9cYBIAAoAjgQZzYCOAJAAkAgACkDKEL/////D1ENACAAKQMgQv////8PUQ0AIAApA0hC/////w9SDQELAkACQAJAIAgoAgAgB0EwakEBQYACQYAEIAMbIAQQRiIBRQRAIAJFDQEMAgsgASAHMwEwEBciAUUEQCAEBEAgBEEANgIEIARBDjYCAAsgAkUNAQwCCwJAIAApAyhC/////w9RBEAgACABEB03AygMAQsgA0UNAEEAIQYCQCABKQMQIg5CCHwiDSAOVA0AIAEpAwggDVQNACABIA03AxBBASEGCyABIAY6AAALIAApAyBC/////w9RBEAgACABEB03AyALAkAgAw0AIAApA0hC/////w9RBEAgACABEB03A0gLIAAoAjxB//8DRw0AIAAgARAVNgI8CyABLQAABH8gASkDECABKQMIUQVBAAsNAiAEBEAgBEEANgIEIARBFTYCAAsgARAIIAINAQsgBRAIC0J/IQ0MAgsgARAICyAFLQAARQRAIAQEQCAEQQA2AgQgBEEUNgIAC0J/IQ0gAg0BIAUQCAwBCyACRQRAIAUQCAtCfyENIAApA0hCf1cEQCAEBEAgBEEWNgIEIARBBDYCAAsMAQsjAEEQayIDJABBASEBAkAgACgCEEHjAEcNAEEAIQECQCAAKAI0IANBDmpBgbICQYAGQQAQRiICBEAgAy8BDiIFQQZLDQELIAQEQCAEQQA2AgQgBEEVNgIACwwBCyACIAWtQv//A4MQFyICRQRAIAQEQCAEQQA2AgQgBEEUNgIACwwBC0EBIQECQAJAAkAgAhAMQQFrDgICAQALQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAILIAApAyhCE1YhAQsgAkICEBMvAABBwYoBRwRAQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAELIAIQfUEBayIFQf8BcUEDTwRAQQAhASAEBEAgBEEANgIEIARBGDYCAAsgAhAIDAELIAMvAQ5BB0cEQEEAIQEgBARAIARBADYCBCAEQRU2AgALIAIQCAwBCyAAIAE6AAYgACAFQf8BcUGBAmo7AVIgACACEAw2AhAgAhAIQQEhAQsgA0EQaiQAIAFFDQAgCCAIKAIAEG02AgAgCiALaq0hDQsgB0HgAGokACANC4ECAQR/IwBBEGsiBCQAAkAgASAEQQxqQcAAQQAQJSIGRQ0AIAQoAgxBBWoiA0GAgARPBEAgAgRAIAJBADYCBCACQRI2AgALDAELQQAgA60QFyIDRQRAIAIEQCACQQA2AgQgAkEONgIACwwBCyADQQEQcCADIAEEfwJ/IAEvAQQhBUEAIAEoAgAiAUUNABpBACABIAVB1IABKAIAEQAACwVBAAsQEiADIAYgBCgCDBAsAn8gAy0AAEUEQCACBEAgAkEANgIEIAJBFDYCAAtBAAwBCyAAIAMtAAAEfiADKQMQBUIAC6dB//8DcSADKAIEEEcLIQUgAxAICyAEQRBqJAAgBQvgAQICfwF+QTAQCSICRQRAIAEEQCABQQA2AgQgAUEONgIAC0EADwsgAkIANwMIIAJBADYCACACQgA3AxAgAkIANwMYIAJCADcDICACQgA3ACUgAFAEQCACDwsCQCAAQv////8AVg0AIACnQQR0EAkiA0UNACACIAM2AgBBACEBQgEhBANAIAMgAUEEdGoiAUIANwIAIAFCADcABSAAIARSBEAgBKchASAEQgF8IQQMAQsLIAIgADcDCCACIAA3AxAgAg8LIAEEQCABQQA2AgQgAUEONgIAC0EAEBAgAhAGQQAL7gECA38BfiMAQRBrIgQkAAJAIARBDGpCBBAXIgNFBEBBfyECDAELAkAgAQRAIAJBgAZxIQUDQAJAIAUgASgCBHFFDQACQCADKQMIQgBUBEAgA0EAOgAADAELIANCADcDECADQQE6AAALIAMgAS8BCBANIAMgAS8BChANIAMtAABFBEAgAEEIaiIABEAgAEEANgIEIABBFDYCAAtBfyECDAQLQX8hAiAAIARBDGpCBBAbQQBIDQMgATMBCiIGUA0AIAAgASgCDCAGEBtBAEgNAwsgASgCACIBDQALC0EAIQILIAMQCAsgBEEQaiQAIAILPAEBfyAABEAgAUGABnEhAQNAIAEgACgCBHEEQCACIAAvAQpqQQRqIQILIAAoAgAiAA0ACwsgAkH//wNxC5wBAQN/IABFBEBBAA8LIAAhAwNAAn8CQAJAIAAvAQgiAUH04AFNBEAgAUEBRg0BIAFB9cYBRg0BDAILIAFBgbICRg0AIAFB9eABRw0BCyAAKAIAIQEgAEEANgIAIAAoAgwQBiAAEAYgASADIAAgA0YbIQMCQCACRQRAQQAhAgwBCyACIAE2AgALIAEMAQsgACICKAIACyIADQALIAMLsgQCBX8BfgJAAkACQCAAIAGtEBciAQRAIAEtAAANAUEAIQAMAgsgBARAIARBADYCBCAEQQ42AgALQQAPC0EAIQADQCABLQAABH4gASkDCCABKQMQfQVCAAtCBFQNASABEAwhByABIAEQDCIGrRATIghFBEBBACECIAQEQCAEQQA2AgQgBEEVNgIACyABEAggAEUNAwNAIAAoAgAhASAAKAIMEAYg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DCAAQRg2AggLDAMLIAAgBiAFLwFqQQAgBCABEQYAIQEgBhALIAFFDQILAkAgDUUEQCABIQMMAQsgACABIAUvAWgQgQEhAyABEAsgA0UNAQsCQCAJRSAMckUEQCADIQEMAQsgACADQQEQgAEhASADEAsgAUUNAQsgASEDDAELQQAhAwsgBUHwAGokACADC4UBAQF/IAFFBEAgAEEIaiIABEAgAEEANgIEIABBEjYCAAtBAA8LQTgQCSIDRQRAIABBCGoiAARAIABBADYCBCAAQQ42AgALQQAPCyADQQA2AhAgA0IANwIIIANCADcDKCADQQA2AgQgAyACNgIAIANCADcDGCADQQA2AjAgACABQTsgAxBCCw8AIAAgASACQQBBABCCAQusAgECfyABRQRAIABBCGoiAARAIABBADYCBCAAQRI2AgALQQAPCwJAIAJBfUsNACACQf//A3FBCEYNACAAQQhqIgAEQCAAQQA2AgQgAEEQNgIAC0EADwsCQEGwwAAQCSIFBEAgBUEANgIIIAVCADcCACAFQYiBAUGogQEgAxs2AqhAIAUgAjYCFCAFIAM6ABAgBUEAOgAPIAVBADsBDCAFIAMgAkF9SyIGcToADiAFQQggAiAGG0H//wNxIAQgBUGIgQFBqIEBIAMbKAIAEQAAIgI2AqxAIAINASAFEDEgBRAGCyAAQQhqIgAEQCAAQQA2AgQgAEEONgIAC0EADwsgACABQTogBRBCIgAEfyAABSAFKAKsQCAFKAKoQCgCBBEDACAFEDEgBRAGQQALC6ABAQF/IAIgACgCBCIDIAIgA0kbIgIEQCAAIAMgAms2AgQCQAJAAkACQCAAKAIcIgMoAhRBAWsOAgEAAgsgA0GgAWogASAAKAIAIAJB3IABKAIAEQgADAILIAAgACgCMCABIAAoAgAgAkHEgAEoAgARBAA2AjAMAQsgASAAKAIAIAIQBxoLIAAgACgCACACajYCACAAIAAoAgggAmo2AggLC7cCAQR/QX4hAgJAIABFDQAgACgCIEUNACAAKAIkIgRFDQAgACgCHCIBRQ0AIAEoAgAgAEcNAAJAAkAgASgCICIDQTlrDjkBAgICAgICAgICAgIBAgICAQICAgICAgICAgICAgICAgICAQICAgICAgICAgICAQICAgICAgICAgEACyADQZoFRg0AIANBKkcN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DCABajYCDCAGIAYoAgggAWo2AgggBSAFKAIUIAFqNgIUIAUgBSgCECABazYCECAGIAYoAhAgAWsiATYCECABDQAgBiAGKAIENgIIC0EAIQRBACEJIAMoAhBFDQAMCgsgAygCHCgCJCEGIAMgAygCGCIBQQFqNgIYIAEgBmotAAAhASADIARBAWo2AhAgAygCBCAEaiABOgAAIAEEQCADKAIQIQQMAQsLIAMoAhwoAixFDQAgAygCECIGIAlNDQAgBQJ/IAUoAjAhBEEAIAMoAgQgCWoiAUUNABogBCABIAYgCWtB1IABKAIAEQAACzYCMAsgA0HnADYCIAsCQCADKAIcKAIsBEAgAygCDCADKAIQIgFBAmpJBH8gBRAKIAMoAhANAkEABSABCyADKAIEaiAFKAIwOwAAIAMgAygCEEECajYCECADQaABakHkgAEoAgARAQAaCyADQfEANgIgIAUQCiADKAIQRQ0BDAcLDAYLIAUoAgQNAQsgAygCPA0AIApFDQEgAygCIEGaBUYNAQsCfyADKAKIASIBRQRAIAMgChCFAQwBCwJAAkACQCADKAKMAUECaw4CAAECCwJ/AkADQAJAAkAgAygCPA0AIAMQLyADKAI8DQAgCg0BQQAMBAsgAygCSCADKAJoai0AACEEIAMgAygC8C0iAUEBajYC8C0gASADKALsLWpBADoAACADIAMoAvAtIgFBAWo2AvAtIAEgAygC7C1qQQA6AAAgAyADKALwLSIBQQFqNgLwLSABIAMoAuwtaiAEOgAAIAMgBEECdGoiASABLwHkAUEBajsB5AEgAyADKAI8QQFrNgI8IAMgAygCaEEBaiIBNgJoIAMoAvAtIAMoAvQtRw0BQQAhBCADIAMoAlgiBkEATgR/IAMoAkggBmoFQQALIAEgBmtBABAPIAMgAygCaDYCWCADKAIAEAogAygCACgCEA0BDAILCyADQQA2AoQuIApBBEYEQCADIAMoAlgiAUEATgR/IAMoAkggAWoFQQALIAMoAmggAWtBARAPIAMgAygCaDYCWCADKAIAEApBA0ECIAMoAgAoAhAbDAILIAMoAvAtBEBBACEEIAMgAygCWCIBQQBOBH8gAygCSCABagVBAAsgAygCaCABa0EAEA8gAyADKAJoNgJYIAMoAgAQCiADKAIAKAIQRQ0BC0EBIQQLIAQL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bGl0ZXJhbC9sZW5ndGhzIHNldABpbnZhbGlkIGNvZGUgbGVuZ3RocyBzZXQAdW5rbm93biBoZWFkZXIgZmxhZ3Mgc2V0AGludmFsaWQgZGlzdGFuY2VzIHNldABpbnZhbGlkIGJpdCBsZW5ndGggcmVwZWF0AEZpbGUgYWxyZWFkeSBleGlzdHMAdG9vIG1hbnkgbGVuZ3RoIG9yIGRpc3RhbmNlIHN5bWJvbHMAaW52YWxpZCBzdG9yZWQgYmxvY2sgbGVuZ3RocwAlcyVzJXMAYnVmZmVyIGVycm9yAE5vIGVycm9yAHN0cmVhbSBlcnJvcgBUZWxsIGVycm9yAEludGVybmFsIGVycm9yAFNlZWsgZXJyb3IAV3JpdGUgZXJyb3IAZmlsZSBlcnJvcgBSZWFkIGVycm9yAFpsaWIgZXJyb3IAZGF0YSBlcnJvcgBDUkMgZXJyb3IAaW5jb21wYXRpYmxlIHZlcnNpb24AaW52YWxpZCBjb2RlIC0tIG1pc3NpbmcgZW5kLW9mLWJsb2NrAGluY29ycmVjdCBoZWFkZXIgY2hlY2sAaW5jb3JyZWN0IGxlbmd0aCBjaGVjawBpbmNvcnJlY3QgZGF0YSBjaGVjawBpbnZhbGlkIGRpc3RhbmNlIHRvbyBmYXIgYmFjawBoZWFkZXIgY3JjIG1pc21hdGNoADEuMi4xMy56bGliLW5nAGludmFsaWQgd2luZG93IHNpemUAUmVhZC1vbmx5IGFyY2hpdmUATm90IGEgemlwIGFyY2hpdmUAUmVzb3VyY2Ugc3RpbGwgaW4gdXNlAE1hbGxvYyBmYWlsdXJlAGludmFsaWQgYmxvY2sgdHlwZQBGYWlsdXJlIHRvIGNyZWF0ZSB0ZW1wb3JhcnkgZmlsZQBDYW4ndCBvcGVuIGZpbGUATm8gc3VjaCBmaWxlAFByZW1hdHVyZSBlbmQgb2YgZmlsZQBDYW4ndCByZW1vdmUgZmlsZQBpbnZhbGlkIGxpdGVyYWwvbGVuZ3RoIGNvZGUAaW52YWxpZCBkaXN0YW5jZSBjb2RlAHVua25vd24gY29tcHJlc3Npb24gbWV0aG9kAHN0cmVhbSBlbmQAQ29tcHJlc3NlZCBkYXRhIGludmFsaWQATXVsdGktZGlzayB6aXAgYXJjaGl2ZXMgbm90IHN1cHBvcnRl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UgAIANIACAAyAAgAsgAIAHIACADyAAgACgAIAIoACABKAAgAygAIACoACACqAAgAagAIAOoACAAaAAgAmgAIAFoACADaAAgAOgAIALoACAB6AAgA+gAIAAYACACGAAgARgAIAMYACAAmAAgApgAIAGYACADmAAgAFgAIAJYACABWAAgA1gAIADYACAC2AAgAdgAIAPYACAAOAAgAjgAIAE4ACADOAAgALgAIAK4ACABuAAgA7gAIAB4ACACeAAgAXgAIAN4ACAA+AAgAvgAIAH4ACAD+AAgAAQAIAIEACABBAAgAwQAIACEACAChAAgAYQAIAOEACAARAAgAkQAIAFEACADRAAgAMQAIALEACABxAAgA8QAIAAkACACJAAgASQAIAMkACAApAAgAqQAIAGkACADpAAgAGQAIAJkACABZAAgA2QAIADkACAC5AAgAeQAIAPkACAAFAAgAhQAIAEUACADFAAgAJQAIAKUACABlAAgA5QAIABUACACVAAgAVQAIANUACAA1AAgAtQAIAHUACAD1AAgADQAIAI0ACABNAAgAzQAIAC0ACACtAAgAbQAIAO0ACAAdAAgAnQAIAF0ACADdAAgAPQAIAL0ACAB9AAgA/QAIABMACQATAQkAkwAJAJMBCQBTAAkAUwEJANMACQDTAQkAMwAJADMBCQCzAAkAswEJAHMACQBzAQkA8wAJAPMBCQALAAkACwEJAIsACQCLAQkASwAJAEsBCQDLAAkAywEJACsACQArAQkAqwAJAKsBCQBrAAkAawEJAOsACQDrAQkAGwAJABsBCQCbAAkAmwEJAFsACQBbAQkA2wAJANsBCQA7AAkAOwEJALsACQC7AQkAewAJAHsBCQD7AAkA+wEJAAcACQAHAQkAhwAJAIcBCQBHAAkARwEJAMcACQDHAQkAJwAJACcBCQCnAAkApwEJAGcACQBnAQkA5wAJAOcBCQAXAAkAFwEJAJcACQCXAQkAVwAJAFcBCQDXAAkA1wEJADcACQA3AQkAtwAJALcBCQB3AAkAdwEJAPcACQD3AQkADwAJAA8BCQCPAAkAjwEJAE8ACQBPAQkAzwAJAM8BCQAvAAkALwEJAK8ACQCvAQkAbwAJAG8BCQDvAAkA7wEJAB8ACQAfAQkAnwAJAJ8BCQBfAAkAXwEJAN8ACQDfAQkAPwAJAD8BCQC/AAkA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SXMgYSBkaXJlY3RvcnkAVGV4dCBmaWxlIGJ1c3kARXhlYyBmb3JtYXQgZXJyb3IASW52YWxpZCBhcmd1bWVudABBcmd1bWVudCBsaXN0IHRvbyBsb25nAFN5bWJvbGljIGxpbmsgbG9vcABGaWxlbmFtZSB0b28gbG9uZwBUb28gbWFueSBvcGVuIGZpbGVzIGluIHN5c3RlbQBObyBmaWxlIGRlc2NyaXB0b3JzIGF2YWlsYWJsZQBCYWQgZmlsZSBkZXNjcmlwdG9yAE5vIGNoaWxkIHByb2Nlc3MAQmFkIGFkZHJlc3MARmlsZSB0b28gbGFyZ2UAVG9vIG1hbnkgbGlua3MATm8gbG9ja3MgYXZhaWxhYmxlAFJlc291cmNlIGRlYWRsb2NrIHdvdWxkIG9jY3VyAFN0YXRlIG5vdCByZWNvdmVyYWJsZQBQcmV2aW91cyBvd25lciBkaWVkAE9wZXJhdGlvbiBjYW5jZWxlZABGdW5jdGlvbiBub3QgaW1wbGVtZW50ZWQATm8gbWVzc2FnZSBvZiBkZXNpcmVkIHR5cGUASWRlbnRpZmllciByZW1vdmVkAERldmljZSBub3QgYSBzdHJlYW0ATm8gZGF0YSBhdmFpbGFibGUARGV2aWNlIHRpbWVvdXQAT3V0IG9mIHN0cmVhbXMgcmVzb3VyY2VzAExpbmsgaGFzIGJlZW4gc2V2ZXJlZABQcm90b2NvbCBlcnJvcgBCYWQgbWVzc2FnZQBGaWxlIGRlc2NyaXB0b3IgaW4gYmFkIHN0YXRlAE5vdCBhIHNvY2tldABEZXN0aW5hdGlvbiBhZGRyZXNzIHJlcXVpcmVkAE1lc3NhZ2UgdG9vIGxhcmdlAFByb3RvY29sIHdyb25nIHR5cGUgZm9yIHNvY2tldABQcm90b2NvbCBub3QgYXZhaWxhYmxlAFByb3RvY29sIG5vdCBzdXBwb3J0ZWQAU29ja2V0IHR5cGUgbm90IHN1cHBvcnRlZABOb3Qgc3VwcG9ydGVkAFByb3RvY29sIGZhbWlseSBub3Qgc3VwcG9ydGVkAEFkZHJlc3MgZmFtaWx5IG5vdCBzdXBwb3J0ZWQgYnkgcHJvdG9jb2wAQWRkcmVzcyBub3QgYXZhaWxhYmxlAE5ldHdvcmsgaXMgZG93bgBOZXR3b3JrIHVucmVhY2hhYmxlAENvbm5lY3Rpb24gcmVzZXQgYnkgbmV0d29yawBDb25uZWN0aW9uIGFib3J0ZWQATm8gYnVmZmVyIHNwYWNlIGF2YWlsYWJsZQBTb2NrZXQgaXMgY29ubmVjdGVkAFNvY2tldCBub3QgY29ubmVjdGVkAENhbm5vdCBzZW5kIGFmdGVyIHNvY2tldCBzaHV0ZG93bgBPcGVyYXRpb24gYWxyZWFkeSBpbiBwcm9ncmVzcwBPcGVyYXRpb24gaW4gcHJvZ3Jlc3MAU3RhbGUgZmlsZSBoYW5kbGUAUmVtb3RlIEkvTyBlcnJvcgBRdW90YSBleGNlZWRlZABObyBtZWRpdW0gZm91bmQAV3JvbmcgbWVkaXVtIHR5cGUATm8gZXJyb3IgaW5mb3JtYXRpb24AQcCAAQuFARMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABkAAAAaAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAAjAAAAgERQADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADcAAAA4AAAAOQAAADIAAAAzAAAANAAAADUAAAA2AAAANwAAADgAQfSCAQsCXEQAQbCDAQsQ/////////////////////w=="; - if (!isDataURI(wasmBinaryFile)) { - wasmBinaryFile = locateFile(wasmBinaryFile); - } - function getBinary(file) { - try { - if (file == wasmBinaryFile && wasmBinary) { - return new Uint8Array(wasmBinary); - } - var binary = tryParseAsDataURI(file); - if (binary) { - return binary; - } - if (readBinary) { - return readBinary(file); - } else { - throw "sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)"; - } - } catch (err2) { - abort(err2); - } - } - function instantiateSync(file, info) { - var instance; - var module2; - var binary; - try { - binary = getBinary(file); - module2 = new WebAssembly.Module(binary); - instance = new WebAssembly.Instance(module2, info); - } catch (e) { - var str = e.toString(); - err("failed to compile wasm module: " + str); - if (str.includes("imported Memory") || str.includes("memory import")) { - err( - "Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time)." - ); - } - throw e; - } - return [instance, module2]; - } - function createWasm() { - var info = { a: asmLibraryArg }; - function receiveInstance(instance, module2) { - var exports3 = instance.exports; - Module["asm"] = exports3; - wasmMemory = Module["asm"]["g"]; - updateGlobalBufferAndViews(wasmMemory.buffer); - wasmTable = Module["asm"]["W"]; - addOnInit(Module["asm"]["h"]); - removeRunDependency(); - } - addRunDependency(); - if (Module["instantiateWasm"]) { - try { - var exports2 = Module["instantiateWasm"](info, receiveInstance); - return exports2; - } catch (e) { - err("Module.instantiateWasm callback failed with error: " + e); - return false; - } - } - var result = instantiateSync(wasmBinaryFile, info); - receiveInstance(result[0]); - return Module["asm"]; - } - function LE_HEAP_LOAD_F32(byteOffset) { - return HEAP_DATA_VIEW.getFloat32(byteOffset, true); - } - function LE_HEAP_LOAD_F64(byteOffset) { - return HEAP_DATA_VIEW.getFloat64(byteOffset, true); - } - function LE_HEAP_LOAD_I16(byteOffset) { - return HEAP_DATA_VIEW.getInt16(byteOffset, true); - } - function LE_HEAP_LOAD_I32(byteOffset) { - return HEAP_DATA_VIEW.getInt32(byteOffset, true); - } - function LE_HEAP_STORE_I32(byteOffset, value) { - HEAP_DATA_VIEW.setInt32(byteOffset, value, true); - } - function callRuntimeCallbacks(callbacks) { - while (callbacks.length > 0) { - var callback = callbacks.shift(); - if (typeof callback == "function") { - callback(Module); - continue; - } - var func = callback.func; - if (typeof func === "number") { - if (callback.arg === void 0) { - wasmTable.get(func)(); - } else { - wasmTable.get(func)(callback.arg); - } - } else { - func(callback.arg === void 0 ? null : callback.arg); - } - } - } - function _gmtime_r(time, tmPtr) { - var date = new Date(LE_HEAP_LOAD_I32((time >> 2) * 4) * 1e3); - LE_HEAP_STORE_I32((tmPtr >> 2) * 4, date.getUTCSeconds()); - LE_HEAP_STORE_I32((tmPtr + 4 >> 2) * 4, date.getUTCMinutes()); - LE_HEAP_STORE_I32((tmPtr + 8 >> 2) * 4, date.getUTCHours()); - LE_HEAP_STORE_I32((tmPtr + 12 >> 2) * 4, date.getUTCDate()); - LE_HEAP_STORE_I32((tmPtr + 16 >> 2) * 4, date.getUTCMonth()); - LE_HEAP_STORE_I32((tmPtr + 20 >> 2) * 4, date.getUTCFullYear() - 1900); - LE_HEAP_STORE_I32((tmPtr + 24 >> 2) * 4, date.getUTCDay()); - LE_HEAP_STORE_I32((tmPtr + 36 >> 2) * 4, 0); - LE_HEAP_STORE_I32((tmPtr + 32 >> 2) * 4, 0); - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = (date.getTime() - start) / (1e3 * 60 * 60 * 24) | 0; - LE_HEAP_STORE_I32((tmPtr + 28 >> 2) * 4, yday); - if (!_gmtime_r.GMTString) _gmtime_r.GMTString = allocateUTF8("GMT"); - LE_HEAP_STORE_I32((tmPtr + 40 >> 2) * 4, _gmtime_r.GMTString); - return tmPtr; - } - function ___gmtime_r(a0, a1) { - return _gmtime_r(a0, a1); - } - function _emscripten_memcpy_big(dest, src, num) { - HEAPU8.copyWithin(dest, src, src + num); - } - function emscripten_realloc_buffer(size) { - try { - wasmMemory.grow(size - buffer.byteLength + 65535 >>> 16); - updateGlobalBufferAndViews(wasmMemory.buffer); - return 1; - } catch (e) { - } - } - function _emscripten_resize_heap(requestedSize) { - var oldSize = HEAPU8.length; - requestedSize = requestedSize >>> 0; - var maxHeapSize = 2147483648; - if (requestedSize > maxHeapSize) { - return false; - } - for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { - var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); - overGrownHeapSize = Math.min( - overGrownHeapSize, - requestedSize + 100663296 - ); - var newSize = Math.min( - maxHeapSize, - alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) - ); - var replacement = emscripten_realloc_buffer(newSize); - if (replacement) { - return true; - } - } - return false; - } - function _setTempRet0(val) { - } - function _time(ptr) { - var ret = Date.now() / 1e3 | 0; - if (ptr) { - LE_HEAP_STORE_I32((ptr >> 2) * 4, ret); - } - return ret; - } - function _tzset() { - if (_tzset.called) return; - _tzset.called = true; - var currentYear = (/* @__PURE__ */ new Date()).getFullYear(); - var winter = new Date(currentYear, 0, 1); - var summer = new Date(currentYear, 6, 1); - var winterOffset = winter.getTimezoneOffset(); - var summerOffset = summer.getTimezoneOffset(); - var stdTimezoneOffset = Math.max(winterOffset, summerOffset); - LE_HEAP_STORE_I32((__get_timezone() >> 2) * 4, stdTimezoneOffset * 60); - LE_HEAP_STORE_I32( - (__get_daylight() >> 2) * 4, - Number(winterOffset != summerOffset) - ); - function extractZone(date) { - var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); - return match ? match[1] : "GMT"; - } - var winterName = extractZone(winter); - var summerName = extractZone(summer); - var winterNamePtr = allocateUTF8(winterName); - var summerNamePtr = allocateUTF8(summerName); - if (summerOffset < winterOffset) { - LE_HEAP_STORE_I32((__get_tzname() >> 2) * 4, winterNamePtr); - LE_HEAP_STORE_I32((__get_tzname() + 4 >> 2) * 4, summerNamePtr); - } else { - LE_HEAP_STORE_I32((__get_tzname() >> 2) * 4, summerNamePtr); - LE_HEAP_STORE_I32((__get_tzname() + 4 >> 2) * 4, winterNamePtr); - } - } - function _timegm(tmPtr) { - _tzset(); - var time = Date.UTC( - LE_HEAP_LOAD_I32((tmPtr + 20 >> 2) * 4) + 1900, - LE_HEAP_LOAD_I32((tmPtr + 16 >> 2) * 4), - LE_HEAP_LOAD_I32((tmPtr + 12 >> 2) * 4), - LE_HEAP_LOAD_I32((tmPtr + 8 >> 2) * 4), - LE_HEAP_LOAD_I32((tmPtr + 4 >> 2) * 4), - LE_HEAP_LOAD_I32((tmPtr >> 2) * 4), - 0 - ); - var date = new Date(time); - LE_HEAP_STORE_I32((tmPtr + 24 >> 2) * 4, date.getUTCDay()); - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = (date.getTime() - start) / (1e3 * 60 * 60 * 24) | 0; - LE_HEAP_STORE_I32((tmPtr + 28 >> 2) * 4, yday); - return date.getTime() / 1e3 | 0; - } - function intArrayFromBase64(s) { - { - var buf; - try { - buf = Buffer.from(s, "base64"); - } catch (_) { - buf = new Buffer(s, "base64"); - } - return new Uint8Array( - buf["buffer"], - buf["byteOffset"], - buf["byteLength"] - ); - } - } - function tryParseAsDataURI(filename) { - if (!isDataURI(filename)) { - return; - } - return intArrayFromBase64(filename.slice(dataURIPrefix.length)); - } - var asmLibraryArg = { - e: ___gmtime_r, - c: _emscripten_memcpy_big, - d: _emscripten_resize_heap, - a: _setTempRet0, - b: _time, - f: _timegm - }; - var asm = createWasm(); - Module["___wasm_call_ctors"] = asm["h"]; - Module["_zip_ext_count_symlinks"] = asm["i"]; - Module["_zip_file_get_external_attributes"] = asm["j"]; - Module["_zipstruct_statS"] = asm["k"]; - Module["_zipstruct_stat_size"] = asm["l"]; - Module["_zipstruct_stat_mtime"] = asm["m"]; - Module["_zipstruct_stat_crc"] = asm["n"]; - Module["_zipstruct_errorS"] = asm["o"]; - Module["_zipstruct_error_code_zip"] = asm["p"]; - Module["_zipstruct_stat_comp_size"] = asm["q"]; - Module["_zipstruct_stat_comp_method"] = asm["r"]; - Module["_zip_close"] = asm["s"]; - Module["_zip_delete"] = asm["t"]; - Module["_zip_dir_add"] = asm["u"]; - Module["_zip_discard"] = asm["v"]; - Module["_zip_error_init_with_code"] = asm["w"]; - Module["_zip_get_error"] = asm["x"]; - Module["_zip_file_get_error"] = asm["y"]; - Module["_zip_error_strerror"] = asm["z"]; - Module["_zip_fclose"] = asm["A"]; - Module["_zip_file_add"] = asm["B"]; - Module["_free"] = asm["C"]; - var _malloc = Module["_malloc"] = asm["D"]; - Module["_zip_source_error"] = asm["E"]; - Module["_zip_source_seek"] = asm["F"]; - Module["_zip_file_set_external_attributes"] = asm["G"]; - Module["_zip_file_set_mtime"] = asm["H"]; - Module["_zip_fopen_index"] = asm["I"]; - Module["_zip_fread"] = asm["J"]; - Module["_zip_get_name"] = asm["K"]; - Module["_zip_get_num_entries"] = asm["L"]; - Module["_zip_source_read"] = asm["M"]; - Module["_zip_name_locate"] = asm["N"]; - Module["_zip_open_from_source"] = asm["O"]; - Module["_zip_set_file_compression"] = asm["P"]; - Module["_zip_source_buffer"] = asm["Q"]; - Module["_zip_source_buffer_create"] = asm["R"]; - Module["_zip_source_close"] = asm["S"]; - Module["_zip_source_free"] = asm["T"]; - Module["_zip_source_keep"] = asm["U"]; - Module["_zip_source_open"] = asm["V"]; - Module["_zip_source_tell"] = asm["X"]; - Module["_zip_stat_index"] = asm["Y"]; - var __get_tzname = Module["__get_tzname"] = asm["Z"]; - var __get_daylight = Module["__get_daylight"] = asm["_"]; - var __get_timezone = Module["__get_timezone"] = asm["$"]; - var stackSave = Module["stackSave"] = asm["aa"]; - var stackRestore = Module["stackRestore"] = asm["ba"]; - var stackAlloc = Module["stackAlloc"] = asm["ca"]; - Module["cwrap"] = cwrap; - Module["getValue"] = getValue; - var calledRun; - dependenciesFulfilled = function runCaller() { - if (!calledRun) run(); - if (!calledRun) dependenciesFulfilled = runCaller; - }; - function run(args) { - if (runDependencies > 0) { - return; - } - preRun(); - if (runDependencies > 0) { - return; - } - function doRun() { - if (calledRun) return; - calledRun = true; - Module["calledRun"] = true; - if (ABORT) return; - initRuntime(); - readyPromiseResolve(Module); - if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); - postRun(); - } - if (Module["setStatus"]) { - Module["setStatus"]("Running..."); - setTimeout(function() { - setTimeout(function() { - Module["setStatus"](""); - }, 1); - doRun(); - }, 1); - } else { - doRun(); - } - } - Module["run"] = run; - if (Module["preInit"]) { - if (typeof Module["preInit"] == "function") - Module["preInit"] = [Module["preInit"]]; - while (Module["preInit"].length > 0) { - Module["preInit"].pop()(); - } - } - run(); - return createModule2; - }; -}(); -module.exports = createModule; -}(libzipSync)); - -const createModule = libzipSync.exports; - -const number64 = [ - `number`, - // low - `number` - // high -]; -var Errors = /* @__PURE__ */ ((Errors2) => { - Errors2[Errors2["ZIP_ER_OK"] = 0] = "ZIP_ER_OK"; - Errors2[Errors2["ZIP_ER_MULTIDISK"] = 1] = "ZIP_ER_MULTIDISK"; - Errors2[Errors2["ZIP_ER_RENAME"] = 2] = "ZIP_ER_RENAME"; - Errors2[Errors2["ZIP_ER_CLOSE"] = 3] = "ZIP_ER_CLOSE"; - Errors2[Errors2["ZIP_ER_SEEK"] = 4] = "ZIP_ER_SEEK"; - Errors2[Errors2["ZIP_ER_READ"] = 5] = "ZIP_ER_READ"; - Errors2[Errors2["ZIP_ER_WRITE"] = 6] = "ZIP_ER_WRITE"; - Errors2[Errors2["ZIP_ER_CRC"] = 7] = "ZIP_ER_CRC"; - Errors2[Errors2["ZIP_ER_ZIPCLOSED"] = 8] = "ZIP_ER_ZIPCLOSED"; - Errors2[Errors2["ZIP_ER_NOENT"] = 9] = "ZIP_ER_NOENT"; - Errors2[Errors2["ZIP_ER_EXISTS"] = 10] = "ZIP_ER_EXISTS"; - Errors2[Errors2["ZIP_ER_OPEN"] = 11] = "ZIP_ER_OPEN"; - Errors2[Errors2["ZIP_ER_TMPOPEN"] = 12] = "ZIP_ER_TMPOPEN"; - Errors2[Errors2["ZIP_ER_ZLIB"] = 13] = "ZIP_ER_ZLIB"; - Errors2[Errors2["ZIP_ER_MEMORY"] = 14] = "ZIP_ER_MEMORY"; - Errors2[Errors2["ZIP_ER_CHANGED"] = 15] = "ZIP_ER_CHANGED"; - Errors2[Errors2["ZIP_ER_COMPNOTSUPP"] = 16] = "ZIP_ER_COMPNOTSUPP"; - Errors2[Errors2["ZIP_ER_EOF"] = 17] = "ZIP_ER_EOF"; - Errors2[Errors2["ZIP_ER_INVAL"] = 18] = "ZIP_ER_INVAL"; - Errors2[Errors2["ZIP_ER_NOZIP"] = 19] = "ZIP_ER_NOZIP"; - Errors2[Errors2["ZIP_ER_INTERNAL"] = 20] = "ZIP_ER_INTERNAL"; - Errors2[Errors2["ZIP_ER_INCONS"] = 21] = "ZIP_ER_INCONS"; - Errors2[Errors2["ZIP_ER_REMOVE"] = 22] = "ZIP_ER_REMOVE"; - Errors2[Errors2["ZIP_ER_DELETED"] = 23] = "ZIP_ER_DELETED"; - Errors2[Errors2["ZIP_ER_ENCRNOTSUPP"] = 24] = "ZIP_ER_ENCRNOTSUPP"; - Errors2[Errors2["ZIP_ER_RDONLY"] = 25] = "ZIP_ER_RDONLY"; - Errors2[Errors2["ZIP_ER_NOPASSWD"] = 26] = "ZIP_ER_NOPASSWD"; - Errors2[Errors2["ZIP_ER_WRONGPASSWD"] = 27] = "ZIP_ER_WRONGPASSWD"; - Errors2[Errors2["ZIP_ER_OPNOTSUPP"] = 28] = "ZIP_ER_OPNOTSUPP"; - Errors2[Errors2["ZIP_ER_INUSE"] = 29] = "ZIP_ER_INUSE"; - Errors2[Errors2["ZIP_ER_TELL"] = 30] = "ZIP_ER_TELL"; - Errors2[Errors2["ZIP_ER_COMPRESSED_DATA"] = 31] = "ZIP_ER_COMPRESSED_DATA"; - return Errors2; -})(Errors || {}); -const makeInterface = (emZip) => ({ - // Those are getters because they can change after memory growth - get HEAPU8() { - return emZip.HEAPU8; - }, - errors: Errors, - SEEK_SET: 0, - SEEK_CUR: 1, - SEEK_END: 2, - ZIP_CHECKCONS: 4, - ZIP_EXCL: 2, - ZIP_RDONLY: 16, - ZIP_FL_OVERWRITE: 8192, - ZIP_FL_COMPRESSED: 4, - ZIP_OPSYS_DOS: 0, - ZIP_OPSYS_AMIGA: 1, - ZIP_OPSYS_OPENVMS: 2, - ZIP_OPSYS_UNIX: 3, - ZIP_OPSYS_VM_CMS: 4, - ZIP_OPSYS_ATARI_ST: 5, - ZIP_OPSYS_OS_2: 6, - ZIP_OPSYS_MACINTOSH: 7, - ZIP_OPSYS_Z_SYSTEM: 8, - ZIP_OPSYS_CPM: 9, - ZIP_OPSYS_WINDOWS_NTFS: 10, - ZIP_OPSYS_MVS: 11, - ZIP_OPSYS_VSE: 12, - ZIP_OPSYS_ACORN_RISC: 13, - ZIP_OPSYS_VFAT: 14, - ZIP_OPSYS_ALTERNATE_MVS: 15, - ZIP_OPSYS_BEOS: 16, - ZIP_OPSYS_TANDEM: 17, - ZIP_OPSYS_OS_400: 18, - ZIP_OPSYS_OS_X: 19, - ZIP_CM_DEFAULT: -1, - ZIP_CM_STORE: 0, - ZIP_CM_DEFLATE: 8, - uint08S: emZip._malloc(1), - uint32S: emZip._malloc(4), - malloc: emZip._malloc, - free: emZip._free, - getValue: emZip.getValue, - openFromSource: emZip.cwrap(`zip_open_from_source`, `number`, [`number`, `number`, `number`]), - close: emZip.cwrap(`zip_close`, `number`, [`number`]), - discard: emZip.cwrap(`zip_discard`, null, [`number`]), - getError: emZip.cwrap(`zip_get_error`, `number`, [`number`]), - getName: emZip.cwrap(`zip_get_name`, `string`, [`number`, `number`, `number`]), - getNumEntries: emZip.cwrap(`zip_get_num_entries`, `number`, [`number`, `number`]), - delete: emZip.cwrap(`zip_delete`, `number`, [`number`, `number`]), - statIndex: emZip.cwrap(`zip_stat_index`, `number`, [`number`, ...number64, `number`, `number`]), - fopenIndex: emZip.cwrap(`zip_fopen_index`, `number`, [`number`, ...number64, `number`]), - fread: emZip.cwrap(`zip_fread`, `number`, [`number`, `number`, `number`, `number`]), - fclose: emZip.cwrap(`zip_fclose`, `number`, [`number`]), - dir: { - add: emZip.cwrap(`zip_dir_add`, `number`, [`number`, `string`]) - }, - file: { - add: emZip.cwrap(`zip_file_add`, `number`, [`number`, `string`, `number`, `number`]), - getError: emZip.cwrap(`zip_file_get_error`, `number`, [`number`]), - getExternalAttributes: emZip.cwrap(`zip_file_get_external_attributes`, `number`, [`number`, ...number64, `number`, `number`, `number`]), - setExternalAttributes: emZip.cwrap(`zip_file_set_external_attributes`, `number`, [`number`, ...number64, `number`, `number`, `number`]), - setMtime: emZip.cwrap(`zip_file_set_mtime`, `number`, [`number`, ...number64, `number`, `number`]), - setCompression: emZip.cwrap(`zip_set_file_compression`, `number`, [`number`, ...number64, `number`, `number`]) - }, - ext: { - countSymlinks: emZip.cwrap(`zip_ext_count_symlinks`, `number`, [`number`]) - }, - error: { - initWithCode: emZip.cwrap(`zip_error_init_with_code`, null, [`number`, `number`]), - strerror: emZip.cwrap(`zip_error_strerror`, `string`, [`number`]) - }, - name: { - locate: emZip.cwrap(`zip_name_locate`, `number`, [`number`, `string`, `number`]) - }, - source: { - fromUnattachedBuffer: emZip.cwrap(`zip_source_buffer_create`, `number`, [`number`, ...number64, `number`, `number`]), - fromBuffer: emZip.cwrap(`zip_source_buffer`, `number`, [`number`, `number`, ...number64, `number`]), - free: emZip.cwrap(`zip_source_free`, null, [`number`]), - keep: emZip.cwrap(`zip_source_keep`, null, [`number`]), - open: emZip.cwrap(`zip_source_open`, `number`, [`number`]), - close: emZip.cwrap(`zip_source_close`, `number`, [`number`]), - seek: emZip.cwrap(`zip_source_seek`, `number`, [`number`, ...number64, `number`]), - tell: emZip.cwrap(`zip_source_tell`, `number`, [`number`]), - read: emZip.cwrap(`zip_source_read`, `number`, [`number`, `number`, `number`]), - error: emZip.cwrap(`zip_source_error`, `number`, [`number`]) - }, - struct: { - statS: emZip.cwrap(`zipstruct_statS`, `number`, []), - statSize: emZip.cwrap(`zipstruct_stat_size`, `number`, [`number`]), - statCompSize: emZip.cwrap(`zipstruct_stat_comp_size`, `number`, [`number`]), - statCompMethod: emZip.cwrap(`zipstruct_stat_comp_method`, `number`, [`number`]), - statMtime: emZip.cwrap(`zipstruct_stat_mtime`, `number`, [`number`]), - statCrc: emZip.cwrap(`zipstruct_stat_crc`, `number`, [`number`]), - errorS: emZip.cwrap(`zipstruct_errorS`, `number`, []), - errorCodeZip: emZip.cwrap(`zipstruct_error_code_zip`, `number`, [`number`]) - } -}); - -function getArchivePart(path, extension) { - let idx = path.indexOf(extension); - if (idx <= 0) - return null; - let nextCharIdx = idx; - while (idx >= 0) { - nextCharIdx = idx + extension.length; - if (path[nextCharIdx] === ppath.sep) - break; - if (path[idx - 1] === ppath.sep) - return null; - idx = path.indexOf(extension, nextCharIdx); - } - if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep) - return null; - return path.slice(0, nextCharIdx); -} -class ZipOpenFS extends MountFS { - static async openPromise(fn, opts) { - const zipOpenFs = new ZipOpenFS(opts); - try { - return await fn(zipOpenFs); - } finally { - zipOpenFs.saveAndClose(); - } - } - constructor(opts = {}) { - const fileExtensions = opts.fileExtensions; - const readOnlyArchives = opts.readOnlyArchives; - const getMountPoint = typeof fileExtensions === `undefined` ? (path) => getArchivePart(path, `.zip`) : (path) => { - for (const extension of fileExtensions) { - const result = getArchivePart(path, extension); - if (result) { - return result; - } - } - return null; - }; - const factorySync = (baseFs, p) => { - return new ZipFS(p, { - baseFs, - readOnly: readOnlyArchives, - stats: baseFs.statSync(p), - customZipImplementation: opts.customZipImplementation - }); - }; - const factoryPromise = async (baseFs, p) => { - const zipOptions = { - baseFs, - readOnly: readOnlyArchives, - stats: await baseFs.statPromise(p), - customZipImplementation: opts.customZipImplementation - }; - return () => { - return new ZipFS(p, zipOptions); - }; - }; - super({ - ...opts, - factorySync, - factoryPromise, - getMountPoint - }); - } -} - -class LibzipError extends Error { - code; - constructor(message, code) { - super(message); - this.name = `Libzip Error`; - this.code = code; - } -} -class LibZipImpl { - libzip; - lzSource; - zip; - listings; - symlinkCount; - filesShouldBeCached = true; - constructor(opts) { - const buffer = `buffer` in opts ? opts.buffer : opts.baseFs.readFileSync(opts.path); - this.libzip = getInstance(); - const errPtr = this.libzip.malloc(4); - try { - let flags = 0; - if (opts.readOnly) - flags |= this.libzip.ZIP_RDONLY; - const lzSource = this.allocateUnattachedSource(buffer); - try { - this.zip = this.libzip.openFromSource(lzSource, flags, errPtr); - this.lzSource = lzSource; - } catch (error) { - this.libzip.source.free(lzSource); - throw error; - } - if (this.zip === 0) { - const error = this.libzip.struct.errorS(); - this.libzip.error.initWithCode(error, this.libzip.getValue(errPtr, `i32`)); - throw this.makeLibzipError(error); - } - } finally { - this.libzip.free(errPtr); - } - const entryCount = this.libzip.getNumEntries(this.zip, 0); - const listings = new Array(entryCount); - for (let t = 0; t < entryCount; ++t) - listings[t] = this.libzip.getName(this.zip, t, 0); - this.listings = listings; - this.symlinkCount = this.libzip.ext.countSymlinks(this.zip); - if (this.symlinkCount === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - getSymlinkCount() { - return this.symlinkCount; - } - getListings() { - return this.listings; - } - stat(entry) { - const stat = this.libzip.struct.statS(); - const rc = this.libzip.statIndex(this.zip, entry, 0, 0, stat); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const size = this.libzip.struct.statSize(stat) >>> 0; - const mtime = this.libzip.struct.statMtime(stat) >>> 0; - const crc = this.libzip.struct.statCrc(stat) >>> 0; - return { size, mtime, crc }; - } - makeLibzipError(error) { - const errorCode = this.libzip.struct.errorCodeZip(error); - const strerror = this.libzip.error.strerror(error); - const libzipError = new LibzipError(strerror, this.libzip.errors[errorCode]); - if (errorCode === this.libzip.errors.ZIP_ER_CHANGED) - throw new Error(`Assertion failed: Unexpected libzip error: ${libzipError.message}`); - return libzipError; - } - setFileSource(target, compression, buffer) { - const lzSource = this.allocateSource(buffer); - try { - const newIndex = this.libzip.file.add(this.zip, target, lzSource, this.libzip.ZIP_FL_OVERWRITE); - if (newIndex === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - if (compression !== null) { - const rc = this.libzip.file.setCompression(this.zip, newIndex, 0, compression[0], compression[1]); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - return newIndex; - } catch (error) { - this.libzip.source.free(lzSource); - throw error; - } - } - setMtime(entry, mtime) { - const rc = this.libzip.file.setMtime(this.zip, entry, 0, mtime, 0); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - getExternalAttributes(index) { - const attrs = this.libzip.file.getExternalAttributes(this.zip, index, 0, 0, this.libzip.uint08S, this.libzip.uint32S); - if (attrs === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const opsys = this.libzip.getValue(this.libzip.uint08S, `i8`) >>> 0; - const attributes = this.libzip.getValue(this.libzip.uint32S, `i32`) >>> 0; - return [opsys, attributes]; - } - setExternalAttributes(index, opsys, attributes) { - const rc = this.libzip.file.setExternalAttributes(this.zip, index, 0, 0, opsys, attributes); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - locate(name) { - return this.libzip.name.locate(this.zip, name, 0); - } - getFileSource(index) { - const stat = this.libzip.struct.statS(); - const rc = this.libzip.statIndex(this.zip, index, 0, 0, stat); - if (rc === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - const size = this.libzip.struct.statCompSize(stat); - const compressionMethod = this.libzip.struct.statCompMethod(stat); - const buffer = this.libzip.malloc(size); - try { - const file = this.libzip.fopenIndex(this.zip, index, 0, this.libzip.ZIP_FL_COMPRESSED); - if (file === 0) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - try { - const rc2 = this.libzip.fread(file, buffer, size, 0); - if (rc2 === -1) - throw this.makeLibzipError(this.libzip.file.getError(file)); - else if (rc2 < size) - throw new Error(`Incomplete read`); - else if (rc2 > size) - throw new Error(`Overread`); - const memory = this.libzip.HEAPU8.subarray(buffer, buffer + size); - const data = Buffer.from(memory); - return { data, compressionMethod }; - } finally { - this.libzip.fclose(file); - } - } finally { - this.libzip.free(buffer); - } - } - deleteEntry(index) { - const rc = this.libzip.delete(this.zip, index); - if (rc === -1) { - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - } - addDirectory(path) { - const index = this.libzip.dir.add(this.zip, path); - if (index === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - return index; - } - getBufferAndClose() { - try { - this.libzip.source.keep(this.lzSource); - if (this.libzip.close(this.zip) === -1) - throw this.makeLibzipError(this.libzip.getError(this.zip)); - if (this.libzip.source.open(this.lzSource) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_END) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - const size = this.libzip.source.tell(this.lzSource); - if (size === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - if (this.libzip.source.seek(this.lzSource, 0, 0, this.libzip.SEEK_SET) === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - const buffer = this.libzip.malloc(size); - if (!buffer) - throw new Error(`Couldn't allocate enough memory`); - try { - const rc = this.libzip.source.read(this.lzSource, buffer, size); - if (rc === -1) - throw this.makeLibzipError(this.libzip.source.error(this.lzSource)); - else if (rc < size) - throw new Error(`Incomplete read`); - else if (rc > size) - throw new Error(`Overread`); - let result = Buffer.from(this.libzip.HEAPU8.subarray(buffer, buffer + size)); - if (process.env.YARN_IS_TEST_ENV && process.env.YARN_ZIP_DATA_EPILOGUE) - result = Buffer.concat([result, Buffer.from(process.env.YARN_ZIP_DATA_EPILOGUE)]); - return result; - } finally { - this.libzip.free(buffer); - } - } finally { - this.libzip.source.close(this.lzSource); - this.libzip.source.free(this.lzSource); - } - } - allocateBuffer(content) { - if (!Buffer.isBuffer(content)) - content = Buffer.from(content); - const buffer = this.libzip.malloc(content.byteLength); - if (!buffer) - throw new Error(`Couldn't allocate enough memory`); - const heap = new Uint8Array(this.libzip.HEAPU8.buffer, buffer, content.byteLength); - heap.set(content); - return { buffer, byteLength: content.byteLength }; - } - allocateUnattachedSource(content) { - const error = this.libzip.struct.errorS(); - const { buffer, byteLength } = this.allocateBuffer(content); - const source = this.libzip.source.fromUnattachedBuffer(buffer, byteLength, 0, 1, error); - if (source === 0) { - this.libzip.free(error); - throw this.makeLibzipError(error); - } - return source; - } - allocateSource(content) { - const { buffer, byteLength } = this.allocateBuffer(content); - const source = this.libzip.source.fromBuffer(this.zip, buffer, byteLength, 0, 1); - if (source === 0) { - this.libzip.free(buffer); - throw this.makeLibzipError(this.libzip.getError(this.zip)); - } - return source; - } - discard() { - this.libzip.discard(this.zip); - } -} - -const ZIP_UNIX = 3; -const STORE = 0; -const DEFLATE = 8; -const DEFAULT_COMPRESSION_LEVEL = `mixed`; -function toUnixTimestamp(time) { - if (typeof time === `string` && String(+time) === time) - return +time; - if (typeof time === `number` && Number.isFinite(time)) { - if (time < 0) { - return Date.now() / 1e3; - } else { - return time; - } - } - if (nodeUtils.types.isDate(time)) - return time.getTime() / 1e3; - throw new Error(`Invalid time`); -} -function makeEmptyArchive() { - return Buffer.from([ - 80, - 75, - 5, - 6, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ]); -} -class ZipFS extends BasePortableFakeFS { - baseFs; - path; - stats; - level; - zipImpl; - listings = /* @__PURE__ */ new Map(); - entries = /* @__PURE__ */ new Map(); - /** - * A cache of indices mapped to file sources. - * Populated by `setFileSource` calls. - * Required for supporting read after write. - */ - fileSources = /* @__PURE__ */ new Map(); - symlinkCount; - fds = /* @__PURE__ */ new Map(); - nextFd = 0; - ready = false; - readOnly = false; - constructor(source, opts = {}) { - super(); - if (opts.readOnly) - this.readOnly = true; - const pathOptions = opts; - this.level = typeof pathOptions.level !== `undefined` ? pathOptions.level : DEFAULT_COMPRESSION_LEVEL; - const ZipImplCls = opts.customZipImplementation ?? LibZipImpl; - if (typeof source === `string`) { - const { baseFs = new NodeFS() } = pathOptions; - this.baseFs = baseFs; - this.path = source; - } else { - this.path = null; - this.baseFs = null; - } - if (opts.stats) { - this.stats = opts.stats; - } else { - if (typeof source === `string`) { - try { - this.stats = this.baseFs.statSync(source); - } catch (error) { - if (error.code === `ENOENT` && pathOptions.create) { - this.stats = makeDefaultStats(); - } else { - throw error; - } - } - } else { - this.stats = makeDefaultStats(); - } - } - if (typeof source === `string`) { - if (opts.create) { - this.zipImpl = new ZipImplCls({ buffer: makeEmptyArchive(), readOnly: this.readOnly }); - } else { - this.zipImpl = new ZipImplCls({ path: source, baseFs: this.baseFs, readOnly: this.readOnly, size: this.stats.size }); - } - } else { - this.zipImpl = new ZipImplCls({ buffer: source ?? makeEmptyArchive(), readOnly: this.readOnly }); - } - this.listings.set(PortablePath.root, /* @__PURE__ */ new Set()); - const listings = this.zipImpl.getListings(); - for (let t = 0; t < listings.length; t++) { - const raw = listings[t]; - if (ppath.isAbsolute(raw)) - continue; - const p = ppath.resolve(PortablePath.root, raw); - this.registerEntry(p, t); - if (raw.endsWith(`/`)) { - this.registerListing(p); - } - } - this.symlinkCount = this.zipImpl.getSymlinkCount(); - this.ready = true; - } - getExtractHint(hints) { - for (const fileName of this.entries.keys()) { - const ext = this.pathUtils.extname(fileName); - if (hints.relevantExtensions.has(ext)) { - return true; - } - } - return false; - } - getAllFiles() { - return Array.from(this.entries.keys()); - } - getRealPath() { - if (!this.path) - throw new Error(`ZipFS don't have real paths when loaded from a buffer`); - return this.path; - } - prepareClose() { - if (!this.ready) - throw EBUSY(`archive closed, close`); - unwatchAllFiles(this); - } - getBufferAndClose() { - this.prepareClose(); - if (this.entries.size === 0) { - this.discardAndClose(); - return makeEmptyArchive(); - } - try { - return this.zipImpl.getBufferAndClose(); - } finally { - this.ready = false; - } - } - discardAndClose() { - this.prepareClose(); - this.zipImpl.discard(); - this.ready = false; - } - saveAndClose() { - if (!this.path || !this.baseFs) - throw new Error(`ZipFS cannot be saved and must be discarded when loaded from a buffer`); - if (this.readOnly) { - this.discardAndClose(); - return; - } - const newMode = this.baseFs.existsSync(this.path) || this.stats.mode === DEFAULT_MODE ? void 0 : this.stats.mode; - this.baseFs.writeFileSync(this.path, this.getBufferAndClose(), { mode: newMode }); - this.ready = false; - } - resolve(p) { - return ppath.resolve(PortablePath.root, p); - } - async openPromise(p, flags, mode) { - return this.openSync(p, flags, mode); - } - openSync(p, flags, mode) { - const fd = this.nextFd++; - this.fds.set(fd, { cursor: 0, p }); - return fd; - } - hasOpenFileHandles() { - return !!this.fds.size; - } - async opendirPromise(p, opts) { - return this.opendirSync(p, opts); - } - opendirSync(p, opts = {}) { - const resolvedP = this.resolveFilename(`opendir '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`opendir '${p}'`); - const directoryListing = this.listings.get(resolvedP); - if (!directoryListing) - throw ENOTDIR(`opendir '${p}'`); - const entries = [...directoryListing]; - const fd = this.openSync(resolvedP, `r`); - const onClose = () => { - this.closeSync(fd); - }; - return opendir(this, resolvedP, entries, { onClose }); - } - async readPromise(fd, buffer, offset, length, position) { - return this.readSync(fd, buffer, offset, length, position); - } - readSync(fd, buffer, offset = 0, length = buffer.byteLength, position = -1) { - const entry = this.fds.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`read`); - const realPosition = position === -1 || position === null ? entry.cursor : position; - const source = this.readFileSync(entry.p); - source.copy(buffer, offset, realPosition, realPosition + length); - const bytesRead = Math.max(0, Math.min(source.length - realPosition, length)); - if (position === -1 || position === null) - entry.cursor += bytesRead; - return bytesRead; - } - async writePromise(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return this.writeSync(fd, buffer, position); - } else { - return this.writeSync(fd, buffer, offset, length, position); - } - } - writeSync(fd, buffer, offset, length, position) { - const entry = this.fds.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`read`); - throw new Error(`Unimplemented`); - } - async closePromise(fd) { - return this.closeSync(fd); - } - closeSync(fd) { - const entry = this.fds.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`read`); - this.fds.delete(fd); - } - createReadStream(p, { encoding } = {}) { - if (p === null) - throw new Error(`Unimplemented`); - const fd = this.openSync(p, `r`); - const stream$1 = Object.assign( - new stream.PassThrough({ - emitClose: true, - autoDestroy: true, - destroy: (error, callback) => { - clearImmediate(immediate); - this.closeSync(fd); - callback(error); - } - }), - { - close() { - stream$1.destroy(); - }, - bytesRead: 0, - path: p, - // "This property is `true` if the underlying file has not been opened yet" - pending: false - } - ); - const immediate = setImmediate(async () => { - try { - const data = await this.readFilePromise(p, encoding); - stream$1.bytesRead = data.length; - stream$1.end(data); - } catch (error) { - stream$1.destroy(error); - } - }); - return stream$1; - } - createWriteStream(p, { encoding } = {}) { - if (this.readOnly) - throw EROFS(`open '${p}'`); - if (p === null) - throw new Error(`Unimplemented`); - const chunks = []; - const fd = this.openSync(p, `w`); - const stream$1 = Object.assign( - new stream.PassThrough({ - autoDestroy: true, - emitClose: true, - destroy: (error, callback) => { - try { - if (error) { - callback(error); - } else { - this.writeFileSync(p, Buffer.concat(chunks), encoding); - callback(null); - } - } catch (err) { - callback(err); - } finally { - this.closeSync(fd); - } - } - }), - { - close() { - stream$1.destroy(); - }, - bytesWritten: 0, - path: p, - // "This property is `true` if the underlying file has not been opened yet" - pending: false - } - ); - stream$1.on(`data`, (chunk) => { - const chunkBuffer = Buffer.from(chunk); - stream$1.bytesWritten += chunkBuffer.length; - chunks.push(chunkBuffer); - }); - return stream$1; - } - async realpathPromise(p) { - return this.realpathSync(p); - } - realpathSync(p) { - const resolvedP = this.resolveFilename(`lstat '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`lstat '${p}'`); - return resolvedP; - } - async existsPromise(p) { - return this.existsSync(p); - } - existsSync(p) { - if (!this.ready) - throw EBUSY(`archive closed, existsSync '${p}'`); - if (this.symlinkCount === 0) { - const resolvedP2 = ppath.resolve(PortablePath.root, p); - return this.entries.has(resolvedP2) || this.listings.has(resolvedP2); - } - let resolvedP; - try { - resolvedP = this.resolveFilename(`stat '${p}'`, p, void 0, false); - } catch { - return false; - } - if (resolvedP === void 0) - return false; - return this.entries.has(resolvedP) || this.listings.has(resolvedP); - } - async accessPromise(p, mode) { - return this.accessSync(p, mode); - } - accessSync(p, mode = fs.constants.F_OK) { - const resolvedP = this.resolveFilename(`access '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`access '${p}'`); - if (this.readOnly && mode & fs.constants.W_OK) { - throw EROFS(`access '${p}'`); - } - } - async statPromise(p, opts = { bigint: false }) { - if (opts.bigint) - return this.statSync(p, { bigint: true }); - return this.statSync(p); - } - statSync(p, opts = { bigint: false, throwIfNoEntry: true }) { - const resolvedP = this.resolveFilename(`stat '${p}'`, p, void 0, opts.throwIfNoEntry); - if (resolvedP === void 0) - return void 0; - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) { - if (opts.throwIfNoEntry === false) - return void 0; - throw ENOENT(`stat '${p}'`); - } - if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) - throw ENOTDIR(`stat '${p}'`); - return this.statImpl(`stat '${p}'`, resolvedP, opts); - } - async fstatPromise(fd, opts) { - return this.fstatSync(fd, opts); - } - fstatSync(fd, opts) { - const entry = this.fds.get(fd); - if (typeof entry === `undefined`) - throw EBADF(`fstatSync`); - const { p } = entry; - const resolvedP = this.resolveFilename(`stat '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`stat '${p}'`); - if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) - throw ENOTDIR(`stat '${p}'`); - return this.statImpl(`fstat '${p}'`, resolvedP, opts); - } - async lstatPromise(p, opts = { bigint: false }) { - if (opts.bigint) - return this.lstatSync(p, { bigint: true }); - return this.lstatSync(p); - } - lstatSync(p, opts = { bigint: false, throwIfNoEntry: true }) { - const resolvedP = this.resolveFilename(`lstat '${p}'`, p, false, opts.throwIfNoEntry); - if (resolvedP === void 0) - return void 0; - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) { - if (opts.throwIfNoEntry === false) - return void 0; - throw ENOENT(`lstat '${p}'`); - } - if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) - throw ENOTDIR(`lstat '${p}'`); - return this.statImpl(`lstat '${p}'`, resolvedP, opts); - } - statImpl(reason, p, opts = {}) { - const entry = this.entries.get(p); - if (typeof entry !== `undefined`) { - const stat = this.zipImpl.stat(entry); - const crc = stat.crc; - const size = stat.size; - const mtimeMs = stat.mtime * 1e3; - const uid = this.stats.uid; - const gid = this.stats.gid; - const blksize = 512; - const blocks = Math.ceil(stat.size / blksize); - const atimeMs = mtimeMs; - const birthtimeMs = mtimeMs; - const ctimeMs = mtimeMs; - const atime = new Date(atimeMs); - const birthtime = new Date(birthtimeMs); - const ctime = new Date(ctimeMs); - const mtime = new Date(mtimeMs); - const type = this.listings.has(p) ? fs.constants.S_IFDIR : this.isSymbolicLink(entry) ? fs.constants.S_IFLNK : fs.constants.S_IFREG; - const defaultMode = type === fs.constants.S_IFDIR ? 493 : 420; - const mode = type | this.getUnixMode(entry, defaultMode) & 511; - const statInstance = Object.assign(new StatEntry(), { uid, gid, size, blksize, blocks, atime, birthtime, ctime, mtime, atimeMs, birthtimeMs, ctimeMs, mtimeMs, mode, crc }); - return opts.bigint === true ? convertToBigIntStats(statInstance) : statInstance; - } - if (this.listings.has(p)) { - const uid = this.stats.uid; - const gid = this.stats.gid; - const size = 0; - const blksize = 512; - const blocks = 0; - const atimeMs = this.stats.mtimeMs; - const birthtimeMs = this.stats.mtimeMs; - const ctimeMs = this.stats.mtimeMs; - const mtimeMs = this.stats.mtimeMs; - const atime = new Date(atimeMs); - const birthtime = new Date(birthtimeMs); - const ctime = new Date(ctimeMs); - const mtime = new Date(mtimeMs); - const mode = fs.constants.S_IFDIR | 493; - const crc = 0; - const statInstance = Object.assign(new StatEntry(), { uid, gid, size, blksize, blocks, atime, birthtime, ctime, mtime, atimeMs, birthtimeMs, ctimeMs, mtimeMs, mode, crc }); - return opts.bigint === true ? convertToBigIntStats(statInstance) : statInstance; - } - throw new Error(`Unreachable`); - } - getUnixMode(index, defaultMode) { - const [opsys, attributes] = this.zipImpl.getExternalAttributes(index); - if (opsys !== ZIP_UNIX) - return defaultMode; - return attributes >>> 16; - } - registerListing(p) { - const existingListing = this.listings.get(p); - if (existingListing) - return existingListing; - const parentListing = this.registerListing(ppath.dirname(p)); - parentListing.add(ppath.basename(p)); - const newListing = /* @__PURE__ */ new Set(); - this.listings.set(p, newListing); - return newListing; - } - registerEntry(p, index) { - const parentListing = this.registerListing(ppath.dirname(p)); - parentListing.add(ppath.basename(p)); - this.entries.set(p, index); - } - unregisterListing(p) { - this.listings.delete(p); - const parentListing = this.listings.get(ppath.dirname(p)); - parentListing?.delete(ppath.basename(p)); - } - unregisterEntry(p) { - this.unregisterListing(p); - const entry = this.entries.get(p); - this.entries.delete(p); - if (typeof entry === `undefined`) - return; - this.fileSources.delete(entry); - if (this.isSymbolicLink(entry)) { - this.symlinkCount--; - } - } - deleteEntry(p, index) { - this.unregisterEntry(p); - this.zipImpl.deleteEntry(index); - } - resolveFilename(reason, p, resolveLastComponent = true, throwIfNoEntry = true) { - if (!this.ready) - throw EBUSY(`archive closed, ${reason}`); - let resolvedP = ppath.resolve(PortablePath.root, p); - if (resolvedP === `/`) - return PortablePath.root; - const fileIndex = this.entries.get(resolvedP); - if (resolveLastComponent && fileIndex !== void 0) { - if (this.symlinkCount !== 0 && this.isSymbolicLink(fileIndex)) { - const target = this.getFileSource(fileIndex).toString(); - return this.resolveFilename(reason, ppath.resolve(ppath.dirname(resolvedP), target), true, throwIfNoEntry); - } else { - return resolvedP; - } - } - while (true) { - const parentP = this.resolveFilename(reason, ppath.dirname(resolvedP), true, throwIfNoEntry); - if (parentP === void 0) - return parentP; - const isDir = this.listings.has(parentP); - const doesExist = this.entries.has(parentP); - if (!isDir && !doesExist) { - if (throwIfNoEntry === false) - return void 0; - throw ENOENT(reason); - } - if (!isDir) - throw ENOTDIR(reason); - resolvedP = ppath.resolve(parentP, ppath.basename(resolvedP)); - if (!resolveLastComponent || this.symlinkCount === 0) - break; - const index = this.zipImpl.locate(resolvedP.slice(1)); - if (index === -1) - break; - if (this.isSymbolicLink(index)) { - const target = this.getFileSource(index).toString(); - resolvedP = ppath.resolve(ppath.dirname(resolvedP), target); - } else { - break; - } - } - return resolvedP; - } - setFileSource(p, content) { - const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content); - const target = ppath.relative(PortablePath.root, p); - let compression = null; - if (this.level !== `mixed`) { - const method = this.level === 0 ? STORE : DEFLATE; - compression = [method, this.level]; - } - const newIndex = this.zipImpl.setFileSource(target, compression, buffer); - this.fileSources.set(newIndex, buffer); - return newIndex; - } - isSymbolicLink(index) { - if (this.symlinkCount === 0) - return false; - const [opsys, attrs] = this.zipImpl.getExternalAttributes(index); - if (opsys !== ZIP_UNIX) - return false; - const attributes = attrs >>> 16; - return (attributes & fs.constants.S_IFMT) === fs.constants.S_IFLNK; - } - getFileSource(index, opts = { asyncDecompress: false }) { - const cachedFileSource = this.fileSources.get(index); - if (typeof cachedFileSource !== `undefined`) - return cachedFileSource; - const { data, compressionMethod } = this.zipImpl.getFileSource(index); - if (compressionMethod === STORE) { - if (this.zipImpl.filesShouldBeCached) - this.fileSources.set(index, data); - return data; - } else if (compressionMethod === DEFLATE) { - if (opts.asyncDecompress) { - return new Promise((resolve, reject) => { - zlib__default.default.inflateRaw(data, (error, result) => { - if (error) { - reject(error); - } else { - if (this.zipImpl.filesShouldBeCached) - this.fileSources.set(index, result); - resolve(result); - } - }); - }); - } else { - const decompressedData = zlib__default.default.inflateRawSync(data); - if (this.zipImpl.filesShouldBeCached) - this.fileSources.set(index, decompressedData); - return decompressedData; - } - } else { - throw new Error(`Unsupported compression method: ${compressionMethod}`); - } - } - async fchmodPromise(fd, mask) { - return this.chmodPromise(this.fdToPath(fd, `fchmod`), mask); - } - fchmodSync(fd, mask) { - return this.chmodSync(this.fdToPath(fd, `fchmodSync`), mask); - } - async chmodPromise(p, mask) { - return this.chmodSync(p, mask); - } - chmodSync(p, mask) { - if (this.readOnly) - throw EROFS(`chmod '${p}'`); - mask &= 493; - const resolvedP = this.resolveFilename(`chmod '${p}'`, p, false); - const entry = this.entries.get(resolvedP); - if (typeof entry === `undefined`) - throw new Error(`Assertion failed: The entry should have been registered (${resolvedP})`); - const oldMod = this.getUnixMode(entry, fs.constants.S_IFREG | 0); - const newMod = oldMod & ~511 | mask; - this.zipImpl.setExternalAttributes(entry, ZIP_UNIX, newMod << 16); - } - async fchownPromise(fd, uid, gid) { - return this.chownPromise(this.fdToPath(fd, `fchown`), uid, gid); - } - fchownSync(fd, uid, gid) { - return this.chownSync(this.fdToPath(fd, `fchownSync`), uid, gid); - } - async chownPromise(p, uid, gid) { - return this.chownSync(p, uid, gid); - } - chownSync(p, uid, gid) { - throw new Error(`Unimplemented`); - } - async renamePromise(oldP, newP) { - return this.renameSync(oldP, newP); - } - renameSync(oldP, newP) { - throw new Error(`Unimplemented`); - } - async copyFilePromise(sourceP, destP, flags) { - const { indexSource, indexDest, resolvedDestP } = this.prepareCopyFile(sourceP, destP, flags); - const source = await this.getFileSource(indexSource, { asyncDecompress: true }); - const newIndex = this.setFileSource(resolvedDestP, source); - if (newIndex !== indexDest) { - this.registerEntry(resolvedDestP, newIndex); - } - } - copyFileSync(sourceP, destP, flags = 0) { - const { indexSource, indexDest, resolvedDestP } = this.prepareCopyFile(sourceP, destP, flags); - const source = this.getFileSource(indexSource); - const newIndex = this.setFileSource(resolvedDestP, source); - if (newIndex !== indexDest) { - this.registerEntry(resolvedDestP, newIndex); - } - } - prepareCopyFile(sourceP, destP, flags = 0) { - if (this.readOnly) - throw EROFS(`copyfile '${sourceP} -> '${destP}'`); - if ((flags & fs.constants.COPYFILE_FICLONE_FORCE) !== 0) - throw ENOSYS(`unsupported clone operation`, `copyfile '${sourceP}' -> ${destP}'`); - const resolvedSourceP = this.resolveFilename(`copyfile '${sourceP} -> ${destP}'`, sourceP); - const indexSource = this.entries.get(resolvedSourceP); - if (typeof indexSource === `undefined`) - throw EINVAL(`copyfile '${sourceP}' -> '${destP}'`); - const resolvedDestP = this.resolveFilename(`copyfile '${sourceP}' -> ${destP}'`, destP); - const indexDest = this.entries.get(resolvedDestP); - if ((flags & (fs.constants.COPYFILE_EXCL | fs.constants.COPYFILE_FICLONE_FORCE)) !== 0 && typeof indexDest !== `undefined`) - throw EEXIST(`copyfile '${sourceP}' -> '${destP}'`); - return { - indexSource, - resolvedDestP, - indexDest - }; - } - async appendFilePromise(p, content, opts) { - if (this.readOnly) - throw EROFS(`open '${p}'`); - if (typeof opts === `undefined`) - opts = { flag: `a` }; - else if (typeof opts === `string`) - opts = { flag: `a`, encoding: opts }; - else if (typeof opts.flag === `undefined`) - opts = { flag: `a`, ...opts }; - return this.writeFilePromise(p, content, opts); - } - appendFileSync(p, content, opts = {}) { - if (this.readOnly) - throw EROFS(`open '${p}'`); - if (typeof opts === `undefined`) - opts = { flag: `a` }; - else if (typeof opts === `string`) - opts = { flag: `a`, encoding: opts }; - else if (typeof opts.flag === `undefined`) - opts = { flag: `a`, ...opts }; - return this.writeFileSync(p, content, opts); - } - fdToPath(fd, reason) { - const path = this.fds.get(fd)?.p; - if (typeof path === `undefined`) - throw EBADF(reason); - return path; - } - async writeFilePromise(p, content, opts) { - const { encoding, mode, index, resolvedP } = this.prepareWriteFile(p, opts); - if (index !== void 0 && typeof opts === `object` && opts.flag && opts.flag.includes(`a`)) - content = Buffer.concat([await this.getFileSource(index, { asyncDecompress: true }), Buffer.from(content)]); - if (encoding !== null) - content = content.toString(encoding); - const newIndex = this.setFileSource(resolvedP, content); - if (newIndex !== index) - this.registerEntry(resolvedP, newIndex); - if (mode !== null) { - await this.chmodPromise(resolvedP, mode); - } - } - writeFileSync(p, content, opts) { - const { encoding, mode, index, resolvedP } = this.prepareWriteFile(p, opts); - if (index !== void 0 && typeof opts === `object` && opts.flag && opts.flag.includes(`a`)) - content = Buffer.concat([this.getFileSource(index), Buffer.from(content)]); - if (encoding !== null) - content = content.toString(encoding); - const newIndex = this.setFileSource(resolvedP, content); - if (newIndex !== index) - this.registerEntry(resolvedP, newIndex); - if (mode !== null) { - this.chmodSync(resolvedP, mode); - } - } - prepareWriteFile(p, opts) { - if (typeof p === `number`) - p = this.fdToPath(p, `read`); - if (this.readOnly) - throw EROFS(`open '${p}'`); - const resolvedP = this.resolveFilename(`open '${p}'`, p); - if (this.listings.has(resolvedP)) - throw EISDIR(`open '${p}'`); - let encoding = null, mode = null; - if (typeof opts === `string`) { - encoding = opts; - } else if (typeof opts === `object`) { - ({ - encoding = null, - mode = null - } = opts); - } - const index = this.entries.get(resolvedP); - return { - encoding, - mode, - resolvedP, - index - }; - } - async unlinkPromise(p) { - return this.unlinkSync(p); - } - unlinkSync(p) { - if (this.readOnly) - throw EROFS(`unlink '${p}'`); - const resolvedP = this.resolveFilename(`unlink '${p}'`, p); - if (this.listings.has(resolvedP)) - throw EISDIR(`unlink '${p}'`); - const index = this.entries.get(resolvedP); - if (typeof index === `undefined`) - throw EINVAL(`unlink '${p}'`); - this.deleteEntry(resolvedP, index); - } - async utimesPromise(p, atime, mtime) { - return this.utimesSync(p, atime, mtime); - } - utimesSync(p, atime, mtime) { - if (this.readOnly) - throw EROFS(`utimes '${p}'`); - const resolvedP = this.resolveFilename(`utimes '${p}'`, p); - this.utimesImpl(resolvedP, mtime); - } - async lutimesPromise(p, atime, mtime) { - return this.lutimesSync(p, atime, mtime); - } - lutimesSync(p, atime, mtime) { - if (this.readOnly) - throw EROFS(`lutimes '${p}'`); - const resolvedP = this.resolveFilename(`utimes '${p}'`, p, false); - this.utimesImpl(resolvedP, mtime); - } - utimesImpl(resolvedP, mtime) { - if (this.listings.has(resolvedP)) { - if (!this.entries.has(resolvedP)) - this.hydrateDirectory(resolvedP); - } - const entry = this.entries.get(resolvedP); - if (entry === void 0) - throw new Error(`Unreachable`); - this.zipImpl.setMtime(entry, toUnixTimestamp(mtime)); - } - async mkdirPromise(p, opts) { - return this.mkdirSync(p, opts); - } - mkdirSync(p, { mode = 493, recursive = false } = {}) { - if (recursive) - return this.mkdirpSync(p, { chmod: mode }); - if (this.readOnly) - throw EROFS(`mkdir '${p}'`); - const resolvedP = this.resolveFilename(`mkdir '${p}'`, p); - if (this.entries.has(resolvedP) || this.listings.has(resolvedP)) - throw EEXIST(`mkdir '${p}'`); - this.hydrateDirectory(resolvedP); - this.chmodSync(resolvedP, mode); - return void 0; - } - async rmdirPromise(p, opts) { - return this.rmdirSync(p, opts); - } - rmdirSync(p, { recursive = false } = {}) { - if (this.readOnly) - throw EROFS(`rmdir '${p}'`); - if (recursive) { - this.removeSync(p); - return; - } - const resolvedP = this.resolveFilename(`rmdir '${p}'`, p); - const directoryListing = this.listings.get(resolvedP); - if (!directoryListing) - throw ENOTDIR(`rmdir '${p}'`); - if (directoryListing.size > 0) - throw ENOTEMPTY(`rmdir '${p}'`); - const index = this.entries.get(resolvedP); - if (typeof index === `undefined`) - throw EINVAL(`rmdir '${p}'`); - this.deleteEntry(p, index); - } - async rmPromise(p, opts) { - return this.rmSync(p, opts); - } - rmSync(p, { recursive = false } = {}) { - if (this.readOnly) - throw EROFS(`rm '${p}'`); - if (recursive) { - this.removeSync(p); - return; - } - const resolvedP = this.resolveFilename(`rm '${p}'`, p); - const directoryListing = this.listings.get(resolvedP); - if (!directoryListing) - throw ENOTDIR(`rm '${p}'`); - if (directoryListing.size > 0) - throw ENOTEMPTY(`rm '${p}'`); - const index = this.entries.get(resolvedP); - if (typeof index === `undefined`) - throw EINVAL(`rm '${p}'`); - this.deleteEntry(p, index); - } - hydrateDirectory(resolvedP) { - const index = this.zipImpl.addDirectory(ppath.relative(PortablePath.root, resolvedP)); - this.registerListing(resolvedP); - this.registerEntry(resolvedP, index); - return index; - } - async linkPromise(existingP, newP) { - return this.linkSync(existingP, newP); - } - linkSync(existingP, newP) { - throw EOPNOTSUPP(`link '${existingP}' -> '${newP}'`); - } - async symlinkPromise(target, p) { - return this.symlinkSync(target, p); - } - symlinkSync(target, p) { - if (this.readOnly) - throw EROFS(`symlink '${target}' -> '${p}'`); - const resolvedP = this.resolveFilename(`symlink '${target}' -> '${p}'`, p); - if (this.listings.has(resolvedP)) - throw EISDIR(`symlink '${target}' -> '${p}'`); - if (this.entries.has(resolvedP)) - throw EEXIST(`symlink '${target}' -> '${p}'`); - const index = this.setFileSource(resolvedP, target); - this.registerEntry(resolvedP, index); - this.zipImpl.setExternalAttributes(index, ZIP_UNIX, (fs.constants.S_IFLNK | 511) << 16); - this.symlinkCount += 1; - } - async readFilePromise(p, encoding) { - if (typeof encoding === `object`) - encoding = encoding ? encoding.encoding : void 0; - const data = await this.readFileBuffer(p, { asyncDecompress: true }); - return encoding ? data.toString(encoding) : data; - } - readFileSync(p, encoding) { - if (typeof encoding === `object`) - encoding = encoding ? encoding.encoding : void 0; - const data = this.readFileBuffer(p); - return encoding ? data.toString(encoding) : data; - } - readFileBuffer(p, opts = { asyncDecompress: false }) { - if (typeof p === `number`) - p = this.fdToPath(p, `read`); - const resolvedP = this.resolveFilename(`open '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`open '${p}'`); - if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) - throw ENOTDIR(`open '${p}'`); - if (this.listings.has(resolvedP)) - throw EISDIR(`read`); - const entry = this.entries.get(resolvedP); - if (entry === void 0) - throw new Error(`Unreachable`); - return this.getFileSource(entry, opts); - } - async readdirPromise(p, opts) { - return this.readdirSync(p, opts); - } - readdirSync(p, opts) { - const resolvedP = this.resolveFilename(`scandir '${p}'`, p); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`scandir '${p}'`); - const directoryListing = this.listings.get(resolvedP); - if (!directoryListing) - throw ENOTDIR(`scandir '${p}'`); - if (opts?.recursive) { - if (opts?.withFileTypes) { - const entries = Array.from(directoryListing, (name) => { - return Object.assign(this.statImpl(`lstat`, ppath.join(p, name)), { - name, - path: PortablePath.dot, - parentPath: PortablePath.dot - }); - }); - for (const entry of entries) { - if (!entry.isDirectory()) - continue; - const subPath = ppath.join(entry.path, entry.name); - const subListing = this.listings.get(ppath.join(resolvedP, subPath)); - for (const child of subListing) { - entries.push(Object.assign(this.statImpl(`lstat`, ppath.join(p, subPath, child)), { - name: child, - path: subPath, - parentPath: subPath - })); - } - } - return entries; - } else { - const entries = [...directoryListing]; - for (const subPath of entries) { - const subListing = this.listings.get(ppath.join(resolvedP, subPath)); - if (typeof subListing === `undefined`) - continue; - for (const child of subListing) { - entries.push(ppath.join(subPath, child)); - } - } - return entries; - } - } else if (opts?.withFileTypes) { - return Array.from(directoryListing, (name) => { - return Object.assign(this.statImpl(`lstat`, ppath.join(p, name)), { - name, - path: void 0, - parentPath: void 0 - }); - }); - } else { - return [...directoryListing]; - } - } - async readlinkPromise(p) { - const entry = this.prepareReadlink(p); - return (await this.getFileSource(entry, { asyncDecompress: true })).toString(); - } - readlinkSync(p) { - const entry = this.prepareReadlink(p); - return this.getFileSource(entry).toString(); - } - prepareReadlink(p) { - const resolvedP = this.resolveFilename(`readlink '${p}'`, p, false); - if (!this.entries.has(resolvedP) && !this.listings.has(resolvedP)) - throw ENOENT(`readlink '${p}'`); - if (p[p.length - 1] === `/` && !this.listings.has(resolvedP)) - throw ENOTDIR(`open '${p}'`); - if (this.listings.has(resolvedP)) - throw EINVAL(`readlink '${p}'`); - const entry = this.entries.get(resolvedP); - if (entry === void 0) - throw new Error(`Unreachable`); - if (!this.isSymbolicLink(entry)) - throw EINVAL(`readlink '${p}'`); - return entry; - } - async truncatePromise(p, len = 0) { - const resolvedP = this.resolveFilename(`open '${p}'`, p); - const index = this.entries.get(resolvedP); - if (typeof index === `undefined`) - throw EINVAL(`open '${p}'`); - const source = await this.getFileSource(index, { asyncDecompress: true }); - const truncated = Buffer.alloc(len, 0); - source.copy(truncated); - return await this.writeFilePromise(p, truncated); - } - truncateSync(p, len = 0) { - const resolvedP = this.resolveFilename(`open '${p}'`, p); - const index = this.entries.get(resolvedP); - if (typeof index === `undefined`) - throw EINVAL(`open '${p}'`); - const source = this.getFileSource(index); - const truncated = Buffer.alloc(len, 0); - source.copy(truncated); - return this.writeFileSync(p, truncated); - } - async ftruncatePromise(fd, len) { - return this.truncatePromise(this.fdToPath(fd, `ftruncate`), len); - } - ftruncateSync(fd, len) { - return this.truncateSync(this.fdToPath(fd, `ftruncateSync`), len); - } - watch(p, a, b) { - let persistent; - switch (typeof a) { - case `function`: - case `string`: - case `undefined`: - { - persistent = true; - } - break; - default: - { - ({ persistent = true } = a); - } - break; - } - if (!persistent) - return { on: () => { - }, close: () => { - } }; - const interval = setInterval(() => { - }, 24 * 60 * 60 * 1e3); - return { - on: () => { - }, - close: () => { - clearInterval(interval); - } - }; - } - watchFile(p, a, b) { - const resolvedP = ppath.resolve(PortablePath.root, p); - return watchFile(this, resolvedP, a, b); - } - unwatchFile(p, cb) { - const resolvedP = ppath.resolve(PortablePath.root, p); - return unwatchFile(this, resolvedP, cb); - } -} - -const SIGNATURE = { - CENTRAL_DIRECTORY: 33639248, - END_OF_CENTRAL_DIRECTORY: 101010256 -}; -const noCommentCDSize = 22; -class JsZipImpl { - fd; - baseFs; - entries; - filesShouldBeCached = false; - constructor(opts) { - if (`buffer` in opts) - throw new Error(`Buffer based zip archives are not supported`); - if (!opts.readOnly) - throw new Error(`Writable zip archives are not supported`); - this.baseFs = opts.baseFs; - this.fd = this.baseFs.openSync(opts.path, `r`); - try { - this.entries = JsZipImpl.readZipSync(this.fd, this.baseFs, opts.size); - } catch (error) { - this.baseFs.closeSync(this.fd); - this.fd = `closed`; - throw error; - } - } - static readZipSync(fd, baseFs, fileSize) { - if (fileSize < noCommentCDSize) - throw new Error(`Invalid ZIP file: EOCD not found`); - let eocdOffset = -1; - let eocdBuffer = Buffer.alloc(noCommentCDSize); - baseFs.readSync( - fd, - eocdBuffer, - 0, - noCommentCDSize, - fileSize - noCommentCDSize - ); - if (eocdBuffer.readUInt32LE(0) === SIGNATURE.END_OF_CENTRAL_DIRECTORY) { - eocdOffset = 0; - } else { - const bufferSize = Math.min(65557, fileSize); - eocdBuffer = Buffer.alloc(bufferSize); - baseFs.readSync( - fd, - eocdBuffer, - 0, - bufferSize, - Math.max(0, fileSize - bufferSize) - ); - for (let i = eocdBuffer.length - 4; i >= 0; i--) { - if (eocdBuffer.readUInt32LE(i) === SIGNATURE.END_OF_CENTRAL_DIRECTORY) { - eocdOffset = i; - break; - } - } - if (eocdOffset === -1) { - throw new Error(`Not a zip archive`); - } - } - const totalEntries = eocdBuffer.readUInt16LE(eocdOffset + 10); - const centralDirSize = eocdBuffer.readUInt32LE(eocdOffset + 12); - const centralDirOffset = eocdBuffer.readUInt32LE(eocdOffset + 16); - const commentLength = eocdBuffer.readUInt16LE(eocdOffset + 20); - if (eocdOffset + commentLength + noCommentCDSize > eocdBuffer.length) - throw new Error(`Zip archive inconsistent`); - if (totalEntries == 65535 || centralDirSize == 4294967295 || centralDirOffset == 4294967295) - throw new Error(`Zip 64 is not supported`); - if (centralDirSize > fileSize) - throw new Error(`Zip archive inconsistent`); - if (totalEntries > centralDirSize / 46) - throw new Error(`Zip archive inconsistent`); - const cdBuffer = Buffer.alloc(centralDirSize); - if (baseFs.readSync(fd, cdBuffer, 0, cdBuffer.length, centralDirOffset) !== cdBuffer.length) - throw new Error(`Zip archive inconsistent`); - const entries = []; - let offset = 0; - let index = 0; - let sumCompressedSize = 0; - while (index < totalEntries) { - if (offset + 46 > cdBuffer.length) - throw new Error(`Zip archive inconsistent`); - if (cdBuffer.readUInt32LE(offset) !== SIGNATURE.CENTRAL_DIRECTORY) - throw new Error(`Zip archive inconsistent`); - const versionMadeBy = cdBuffer.readUInt16LE(offset + 4); - const os = versionMadeBy >>> 8; - const flags = cdBuffer.readUInt16LE(offset + 8); - if ((flags & 1) !== 0) - throw new Error(`Encrypted zip files are not supported`); - const compressionMethod = cdBuffer.readUInt16LE(offset + 10); - const crc = cdBuffer.readUInt32LE(offset + 16); - const nameLength = cdBuffer.readUInt16LE(offset + 28); - const extraLength = cdBuffer.readUInt16LE(offset + 30); - const commentLength2 = cdBuffer.readUInt16LE(offset + 32); - const localHeaderOffset = cdBuffer.readUInt32LE(offset + 42); - const name = cdBuffer.toString(`utf8`, offset + 46, offset + 46 + nameLength).replaceAll(`\0`, ` `); - if (name.includes(`\0`)) - throw new Error(`Invalid ZIP file`); - const compressedSize = cdBuffer.readUInt32LE(offset + 20); - const externalAttributes = cdBuffer.readUInt32LE(offset + 38); - entries.push({ - name, - os, - mtime: SAFE_TIME, - //we dont care, - crc, - compressionMethod, - isSymbolicLink: os === ZIP_UNIX && (externalAttributes >>> 16 & S_IFMT) === S_IFLNK, - size: cdBuffer.readUInt32LE(offset + 24), - compressedSize, - externalAttributes, - localHeaderOffset - }); - sumCompressedSize += compressedSize; - index += 1; - offset += 46 + nameLength + extraLength + commentLength2; - } - if (sumCompressedSize > fileSize) - throw new Error(`Zip archive inconsistent`); - if (offset !== cdBuffer.length) - throw new Error(`Zip archive inconsistent`); - return entries; - } - getExternalAttributes(index) { - const entry = this.entries[index]; - return [entry.os, entry.externalAttributes]; - } - getListings() { - return this.entries.map((e) => e.name); - } - getSymlinkCount() { - let count = 0; - for (const entry of this.entries) - if (entry.isSymbolicLink) - count += 1; - return count; - } - stat(index) { - const entry = this.entries[index]; - return { - crc: entry.crc, - mtime: entry.mtime, - size: entry.size - }; - } - locate(name) { - for (let ind = 0; ind < this.entries.length; ind++) - if (this.entries[ind].name === name) - return ind; - return -1; - } - getFileSource(index) { - if (this.fd === `closed`) - throw new Error(`ZIP file is closed`); - const entry = this.entries[index]; - const localHeaderBuf = Buffer.alloc(30); - this.baseFs.readSync( - this.fd, - localHeaderBuf, - 0, - localHeaderBuf.length, - entry.localHeaderOffset - ); - const nameLength = localHeaderBuf.readUInt16LE(26); - const extraLength = localHeaderBuf.readUInt16LE(28); - const buffer = Buffer.alloc(entry.compressedSize); - if (this.baseFs.readSync(this.fd, buffer, 0, entry.compressedSize, entry.localHeaderOffset + 30 + nameLength + extraLength) !== entry.compressedSize) - throw new Error(`Invalid ZIP file`); - return { data: buffer, compressionMethod: entry.compressionMethod }; - } - discard() { - if (this.fd !== `closed`) { - this.baseFs.closeSync(this.fd); - this.fd = `closed`; - } - } - addDirectory(path) { - throw new Error(`Not implemented`); - } - deleteEntry(index) { - throw new Error(`Not implemented`); - } - setMtime(index, mtime) { - throw new Error(`Not implemented`); - } - getBufferAndClose() { - throw new Error(`Not implemented`); - } - setFileSource(target, compression, buffer) { - throw new Error(`Not implemented`); - } - setExternalAttributes(index, opsys, attributes) { - throw new Error(`Not implemented`); - } -} - -setFactory(() => { - const emZip = createModule(); - return makeInterface(emZip); -}); - -var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => { - ErrorCode2["API_ERROR"] = `API_ERROR`; - ErrorCode2["BUILTIN_NODE_RESOLUTION_FAILED"] = `BUILTIN_NODE_RESOLUTION_FAILED`; - ErrorCode2["EXPORTS_RESOLUTION_FAILED"] = `EXPORTS_RESOLUTION_FAILED`; - ErrorCode2["MISSING_DEPENDENCY"] = `MISSING_DEPENDENCY`; - ErrorCode2["MISSING_PEER_DEPENDENCY"] = `MISSING_PEER_DEPENDENCY`; - ErrorCode2["QUALIFIED_PATH_RESOLUTION_FAILED"] = `QUALIFIED_PATH_RESOLUTION_FAILED`; - ErrorCode2["INTERNAL"] = `INTERNAL`; - ErrorCode2["UNDECLARED_DEPENDENCY"] = `UNDECLARED_DEPENDENCY`; - ErrorCode2["UNSUPPORTED"] = `UNSUPPORTED`; - return ErrorCode2; -})(ErrorCode || {}); -const MODULE_NOT_FOUND_ERRORS = /* @__PURE__ */ new Set([ - "BUILTIN_NODE_RESOLUTION_FAILED" /* BUILTIN_NODE_RESOLUTION_FAILED */, - "MISSING_DEPENDENCY" /* MISSING_DEPENDENCY */, - "MISSING_PEER_DEPENDENCY" /* MISSING_PEER_DEPENDENCY */, - "QUALIFIED_PATH_RESOLUTION_FAILED" /* QUALIFIED_PATH_RESOLUTION_FAILED */, - "UNDECLARED_DEPENDENCY" /* UNDECLARED_DEPENDENCY */ -]); -function makeError(pnpCode, message, data = {}, code) { - code ??= MODULE_NOT_FOUND_ERRORS.has(pnpCode) ? `MODULE_NOT_FOUND` : pnpCode; - const propertySpec = { - configurable: true, - writable: true, - enumerable: false - }; - return Object.defineProperties(new Error(message), { - code: { - ...propertySpec, - value: code - }, - pnpCode: { - ...propertySpec, - value: pnpCode - }, - data: { - ...propertySpec, - value: data - } - }); -} -function getIssuerModule(parent) { - let issuer = parent; - while (issuer && (issuer.id === `[eval]` || issuer.id === `` || !issuer.filename)) - issuer = issuer.parent; - return issuer || null; -} -function getPathForDisplay(p) { - return npath.normalize(npath.fromPortablePath(p)); -} - -const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10)); -const WATCH_MODE_MESSAGE_USES_ARRAYS = major > 19 || major === 19 && minor >= 2 || major === 18 && minor >= 13; - -function readPackageScope(checkPath) { - const rootSeparatorIndex = checkPath.indexOf(npath.sep); - let separatorIndex; - do { - separatorIndex = checkPath.lastIndexOf(npath.sep); - checkPath = checkPath.slice(0, separatorIndex); - if (checkPath.endsWith(`${npath.sep}node_modules`)) - return false; - const pjson = readPackage(checkPath + npath.sep); - if (pjson) { - return { - data: pjson, - path: checkPath - }; - } - } while (separatorIndex > rootSeparatorIndex); - return false; -} -function readPackage(requestPath) { - const jsonPath = npath.resolve(requestPath, `package.json`); - if (!fs__default.default.existsSync(jsonPath)) - return null; - return JSON.parse(fs__default.default.readFileSync(jsonPath, `utf8`)); -} -function ERR_REQUIRE_ESM(filename, parentPath = null) { - const basename = parentPath && path__default.default.basename(filename) === path__default.default.basename(parentPath) ? filename : path__default.default.basename(filename); - const msg = `require() of ES Module ${filename}${parentPath ? ` from ${parentPath}` : ``} not supported. -Instead change the require of ${basename} in ${parentPath} to a dynamic import() which is available in all CommonJS modules.`; - const err = new Error(msg); - err.code = `ERR_REQUIRE_ESM`; - return err; -} -function reportRequiredFilesToWatchMode(files) { - if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { - files = files.map((filename) => npath.fromPortablePath(VirtualFS.resolveVirtual(npath.toPortablePath(filename)))); - if (WATCH_MODE_MESSAGE_USES_ARRAYS) { - process.send({ "watch:require": files }); - } else { - for (const filename of files) { - process.send({ "watch:require": filename }); - } - } - } -} - -function applyPatch(pnpapi, opts) { - let enableNativeHooks = true; - process.versions.pnp = String(pnpapi.VERSIONS.std); - const moduleExports = require$$0__default.default; - moduleExports.findPnpApi = (lookupSource) => { - const lookupPath = lookupSource instanceof URL ? url.fileURLToPath(lookupSource) : lookupSource; - const apiPath = opts.manager.findApiPathFor(lookupPath); - if (apiPath === null) - return null; - const apiEntry = opts.manager.getApiEntry(apiPath, true); - return apiEntry.instance.findPackageLocator(lookupPath) ? apiEntry.instance : null; - }; - function getRequireStack(parent) { - const requireStack = []; - for (let cursor = parent; cursor; cursor = cursor.parent) - requireStack.push(cursor.filename || cursor.id); - return requireStack; - } - const originalModuleLoad = require$$0.Module._load; - require$$0.Module._load = function(request, parent, isMain) { - if (request === `pnpapi`) { - const parentApiPath = opts.manager.getApiPathFromParent(parent); - if (parentApiPath) { - return opts.manager.getApiEntry(parentApiPath, true).instance; - } - } - return originalModuleLoad.call(require$$0.Module, request, parent, isMain); - }; - function getIssuerSpecsFromPaths(paths) { - return paths.map((path) => ({ - apiPath: opts.manager.findApiPathFor(path), - path, - module: null - })); - } - function getIssuerSpecsFromModule(module) { - if (module && module.id !== `` && module.id !== `internal/preload` && !module.parent && !module.filename && module.paths.length > 0) { - return [{ - apiPath: opts.manager.findApiPathFor(module.paths[0]), - path: module.paths[0], - module - }]; - } - const issuer = getIssuerModule(module); - if (issuer !== null) { - const path = npath.dirname(issuer.filename); - const apiPath = opts.manager.getApiPathFromParent(issuer); - return [{ apiPath, path, module }]; - } else { - const path = process.cwd(); - const apiPath = opts.manager.findApiPathFor(npath.join(path, `[file]`)) ?? opts.manager.getApiPathFromParent(null); - return [{ apiPath, path, module }]; - } - } - function makeFakeParent(path) { - const fakeParent = new require$$0.Module(``); - const fakeFilePath = npath.join(path, `[file]`); - fakeParent.paths = require$$0.Module._nodeModulePaths(fakeFilePath); - return fakeParent; - } - const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:@[^/]+\/)?[^/]+)\/*(.*|)$/; - const originalModuleResolveFilename = require$$0.Module._resolveFilename; - require$$0.Module._resolveFilename = function(request, parent, isMain, options) { - if (require$$0.isBuiltin(request)) - return request; - if (!enableNativeHooks) - return originalModuleResolveFilename.call(require$$0.Module, request, parent, isMain, options); - if (options && options.plugnplay === false) { - const { plugnplay, ...forwardedOptions } = options; - try { - enableNativeHooks = false; - return originalModuleResolveFilename.call(require$$0.Module, request, parent, isMain, forwardedOptions); - } finally { - enableNativeHooks = true; - } - } - if (options) { - const optionNames = new Set(Object.keys(options)); - optionNames.delete(`paths`); - optionNames.delete(`plugnplay`); - optionNames.delete(`conditions`); - if (optionNames.size > 0) { - throw makeError( - ErrorCode.UNSUPPORTED, - `Some options passed to require() aren't supported by PnP yet (${Array.from(optionNames).join(`, `)})` - ); - } - } - const issuerSpecs = options && options.paths ? getIssuerSpecsFromPaths(options.paths) : getIssuerSpecsFromModule(parent); - if (request.match(pathRegExp) === null) { - const parentDirectory = parent?.filename != null ? npath.dirname(parent.filename) : null; - const absoluteRequest = npath.isAbsolute(request) ? request : parentDirectory !== null ? npath.resolve(parentDirectory, request) : null; - if (absoluteRequest !== null) { - const apiPath = parent && parentDirectory === npath.dirname(absoluteRequest) ? opts.manager.getApiPathFromParent(parent) : opts.manager.findApiPathFor(absoluteRequest); - if (apiPath !== null) { - issuerSpecs.unshift({ - apiPath, - path: parentDirectory, - module: null - }); - } - } - } - let firstError; - for (const { apiPath, path, module } of issuerSpecs) { - let resolution; - const issuerApi = apiPath !== null ? opts.manager.getApiEntry(apiPath, true).instance : null; - try { - if (issuerApi !== null) { - resolution = issuerApi.resolveRequest(request, path !== null ? `${path}/` : null, { - conditions: options?.conditions - }); - } else { - if (path === null) - throw new Error(`Assertion failed: Expected the path to be set`); - resolution = originalModuleResolveFilename.call(require$$0.Module, request, module || makeFakeParent(path), isMain, { - conditions: options?.conditions - }); - } - } catch (error) { - firstError = firstError || error; - continue; - } - if (resolution !== null) { - return resolution; - } - } - const requireStack = getRequireStack(parent); - Object.defineProperty(firstError, `requireStack`, { - configurable: true, - writable: true, - enumerable: false, - value: requireStack - }); - if (requireStack.length > 0) - firstError.message += ` -Require stack: -- ${requireStack.join(` -- `)}`; - if (typeof firstError.pnpCode === `string`) - Error.captureStackTrace(firstError); - throw firstError; - }; - const originalFindPath = require$$0.Module._findPath; - require$$0.Module._findPath = function(request, paths, isMain) { - if (request === `pnpapi`) - return false; - if (!enableNativeHooks) - return originalFindPath.call(require$$0.Module, request, paths, isMain); - const isAbsolute = npath.isAbsolute(request); - if (isAbsolute) - paths = [``]; - else if (!paths || paths.length === 0) - return false; - for (const path of paths) { - let resolution; - try { - const pnpApiPath = opts.manager.findApiPathFor(isAbsolute ? request : path); - if (pnpApiPath !== null) { - const api = opts.manager.getApiEntry(pnpApiPath, true).instance; - resolution = api.resolveRequest(request, path) || false; - } else { - resolution = originalFindPath.call(require$$0.Module, request, [path], isMain); - } - } catch { - continue; - } - if (resolution) { - return resolution; - } - } - return false; - }; - if (!process.features.require_module) { - const originalExtensionJSFunction = require$$0.Module._extensions[`.js`]; - require$$0.Module._extensions[`.js`] = function(module, filename) { - if (filename.endsWith(`.js`)) { - const pkg = readPackageScope(filename); - if (pkg && pkg.data?.type === `module`) { - const err = ERR_REQUIRE_ESM(filename, module.parent?.filename); - Error.captureStackTrace(err); - throw err; - } - } - originalExtensionJSFunction.call(this, module, filename); - }; - } - const originalDlopen = process.dlopen; - process.dlopen = function(...args) { - const [module, filename, ...rest] = args; - return originalDlopen.call( - this, - module, - npath.fromPortablePath(VirtualFS.resolveVirtual(npath.toPortablePath(filename))), - ...rest - ); - }; - const originalEmit = process.emit; - process.emit = function(name, data, ...args) { - if (name === `warning` && typeof data === `object` && data.name === `ExperimentalWarning` && (data.message.includes(`--experimental-loader`) || data.message.includes(`Custom ESM Loaders is an experimental feature`))) - return false; - return originalEmit.apply(process, arguments); - }; - patchFs(fs__default.default, new PosixFS(opts.fakeFs)); -} - -function hydrateRuntimeState(data, { basePath }) { - const portablePath = npath.toPortablePath(basePath); - const absolutePortablePath = ppath.resolve(portablePath); - const ignorePattern = data.ignorePatternData !== null ? new RegExp(data.ignorePatternData) : null; - const packageLocatorsByLocations = /* @__PURE__ */ new Map(); - const packageRegistry = new Map(data.packageRegistryData.map(([packageName, packageStoreData]) => { - return [packageName, new Map(packageStoreData.map(([packageReference, packageInformationData]) => { - if (packageName === null !== (packageReference === null)) - throw new Error(`Assertion failed: The name and reference should be null, or neither should`); - const discardFromLookup = packageInformationData.discardFromLookup ?? false; - const packageLocator = { name: packageName, reference: packageReference }; - const entry = packageLocatorsByLocations.get(packageInformationData.packageLocation); - if (!entry) { - packageLocatorsByLocations.set(packageInformationData.packageLocation, { locator: packageLocator, discardFromLookup }); - } else { - entry.discardFromLookup = entry.discardFromLookup && discardFromLookup; - if (!discardFromLookup) { - entry.locator = packageLocator; - } - } - let resolvedPackageLocation = null; - return [packageReference, { - packageDependencies: new Map(packageInformationData.packageDependencies), - packagePeers: new Set(packageInformationData.packagePeers), - linkType: packageInformationData.linkType, - discardFromLookup, - // we only need this for packages that are used by the currently running script - // this is a lazy getter because `ppath.join` has some overhead - get packageLocation() { - return resolvedPackageLocation || (resolvedPackageLocation = ppath.join(absolutePortablePath, packageInformationData.packageLocation)); - } - }]; - }))]; - })); - const fallbackExclusionList = new Map(data.fallbackExclusionList.map(([packageName, packageReferences]) => { - return [packageName, new Set(packageReferences)]; - })); - const fallbackPool = new Map(data.fallbackPool); - const dependencyTreeRoots = data.dependencyTreeRoots; - const enableTopLevelFallback = data.enableTopLevelFallback; - return { - basePath: portablePath, - dependencyTreeRoots, - enableTopLevelFallback, - fallbackExclusionList, - pnpZipBackend: data.pnpZipBackend, - fallbackPool, - ignorePattern, - packageLocatorsByLocations, - packageRegistry - }; -} - -const ArrayIsArray = Array.isArray; -const JSONStringify = JSON.stringify; -const ObjectGetOwnPropertyNames = Object.getOwnPropertyNames; -const ObjectPrototypeHasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); -const RegExpPrototypeExec = (obj, string) => RegExp.prototype.exec.call(obj, string); -const RegExpPrototypeSymbolReplace = (obj, ...rest) => RegExp.prototype[Symbol.replace].apply(obj, rest); -const StringPrototypeEndsWith = (str, ...rest) => String.prototype.endsWith.apply(str, rest); -const StringPrototypeIncludes = (str, ...rest) => String.prototype.includes.apply(str, rest); -const StringPrototypeLastIndexOf = (str, ...rest) => String.prototype.lastIndexOf.apply(str, rest); -const StringPrototypeIndexOf = (str, ...rest) => String.prototype.indexOf.apply(str, rest); -const StringPrototypeReplace = (str, ...rest) => String.prototype.replace.apply(str, rest); -const StringPrototypeSlice = (str, ...rest) => String.prototype.slice.apply(str, rest); -const StringPrototypeStartsWith = (str, ...rest) => String.prototype.startsWith.apply(str, rest); -const SafeMap = Map; -const JSONParse = JSON.parse; - -function createErrorType(code, messageCreator, errorType) { - return class extends errorType { - constructor(...args) { - super(messageCreator(...args)); - this.code = code; - this.name = `${errorType.name} [${code}]`; - } - }; -} -const ERR_PACKAGE_IMPORT_NOT_DEFINED = createErrorType( - `ERR_PACKAGE_IMPORT_NOT_DEFINED`, - (specifier, packagePath, base) => { - return `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath}package.json` : ``} imported from ${base}`; - }, - TypeError -); -const ERR_INVALID_MODULE_SPECIFIER = createErrorType( - `ERR_INVALID_MODULE_SPECIFIER`, - (request, reason, base = void 0) => { - return `Invalid module "${request}" ${reason}${base ? ` imported from ${base}` : ``}`; - }, - TypeError -); -const ERR_INVALID_PACKAGE_TARGET = createErrorType( - `ERR_INVALID_PACKAGE_TARGET`, - (pkgPath, key, target, isImport = false, base = void 0) => { - const relError = typeof target === `string` && !isImport && target.length && !StringPrototypeStartsWith(target, `./`); - if (key === `.`) { - assert__default.default(isImport === false); - return `Invalid "exports" main target ${JSONStringify(target)} defined in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; - } - return `Invalid "${isImport ? `imports` : `exports`}" target ${JSONStringify( - target - )} defined for '${key}' in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; - }, - Error -); -const ERR_INVALID_PACKAGE_CONFIG = createErrorType( - `ERR_INVALID_PACKAGE_CONFIG`, - (path, base, message) => { - return `Invalid package config ${path}${base ? ` while importing ${base}` : ``}${message ? `. ${message}` : ``}`; - }, - Error -); -const ERR_PACKAGE_PATH_NOT_EXPORTED = createErrorType( - "ERR_PACKAGE_PATH_NOT_EXPORTED", - (pkgPath, subpath, base = void 0) => { - if (subpath === ".") - return `No "exports" main defined in ${pkgPath}package.json${base ? ` imported from ${base}` : ""}`; - return `Package subpath '${subpath}' is not defined by "exports" in ${pkgPath}package.json${base ? ` imported from ${base}` : ""}`; - }, - Error -); - -function filterOwnProperties(source, keys) { - const filtered = /* @__PURE__ */ Object.create(null); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (ObjectPrototypeHasOwnProperty(source, key)) { - filtered[key] = source[key]; - } - } - return filtered; -} - -const packageJSONCache = new SafeMap(); -function getPackageConfig(path, specifier, base, readFileSyncFn) { - const existing = packageJSONCache.get(path); - if (existing !== void 0) { - return existing; - } - const source = readFileSyncFn(path); - if (source === void 0) { - const packageConfig2 = { - pjsonPath: path, - exists: false, - main: void 0, - name: void 0, - type: "none", - exports: void 0, - imports: void 0 - }; - packageJSONCache.set(path, packageConfig2); - return packageConfig2; - } - let packageJSON; - try { - packageJSON = JSONParse(source); - } catch (error) { - throw new ERR_INVALID_PACKAGE_CONFIG( - path, - (base ? `"${specifier}" from ` : "") + url.fileURLToPath(base || specifier), - error.message - ); - } - let { imports, main, name, type } = filterOwnProperties(packageJSON, [ - "imports", - "main", - "name", - "type" - ]); - const exports = ObjectPrototypeHasOwnProperty(packageJSON, "exports") ? packageJSON.exports : void 0; - if (typeof imports !== "object" || imports === null) { - imports = void 0; - } - if (typeof main !== "string") { - main = void 0; - } - if (typeof name !== "string") { - name = void 0; - } - if (type !== "module" && type !== "commonjs") { - type = "none"; - } - const packageConfig = { - pjsonPath: path, - exists: true, - main, - name, - type, - exports, - imports - }; - packageJSONCache.set(path, packageConfig); - return packageConfig; -} -function getPackageScopeConfig(resolved, readFileSyncFn) { - let packageJSONUrl = new URL("./package.json", resolved); - while (true) { - const packageJSONPath2 = packageJSONUrl.pathname; - if (StringPrototypeEndsWith(packageJSONPath2, "node_modules/package.json")) { - break; - } - const packageConfig2 = getPackageConfig( - url.fileURLToPath(packageJSONUrl), - resolved, - void 0, - readFileSyncFn - ); - if (packageConfig2.exists) { - return packageConfig2; - } - const lastPackageJSONUrl = packageJSONUrl; - packageJSONUrl = new URL("../package.json", packageJSONUrl); - if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) { - break; - } - } - const packageJSONPath = url.fileURLToPath(packageJSONUrl); - const packageConfig = { - pjsonPath: packageJSONPath, - exists: false, - main: void 0, - name: void 0, - type: "none", - exports: void 0, - imports: void 0 - }; - packageJSONCache.set(packageJSONPath, packageConfig); - return packageConfig; -} - -function throwImportNotDefined(specifier, packageJSONUrl, base) { - throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( - specifier, - packageJSONUrl && url.fileURLToPath(new URL(".", packageJSONUrl)), - url.fileURLToPath(base) - ); -} -function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { - const reason = `request is not a valid subpath for the "${internal ? "imports" : "exports"}" resolution of ${url.fileURLToPath(packageJSONUrl)}`; - throw new ERR_INVALID_MODULE_SPECIFIER( - subpath, - reason, - base && url.fileURLToPath(base) - ); -} -function throwInvalidPackageTarget(subpath, target, packageJSONUrl, internal, base) { - if (typeof target === "object" && target !== null) { - target = JSONStringify(target, null, ""); - } else { - target = `${target}`; - } - throw new ERR_INVALID_PACKAGE_TARGET( - url.fileURLToPath(new URL(".", packageJSONUrl)), - subpath, - target, - internal, - base && url.fileURLToPath(base) - ); -} -const invalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i; -const patternRegEx = /\*/g; -function resolvePackageTargetString(target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { - if (subpath !== "" && !pattern && target[target.length - 1] !== "/") - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - if (!StringPrototypeStartsWith(target, "./")) { - if (internal && !StringPrototypeStartsWith(target, "../") && !StringPrototypeStartsWith(target, "/")) { - let isURL = false; - try { - new URL(target); - isURL = true; - } catch { - } - if (!isURL) { - const exportTarget = pattern ? RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : target + subpath; - return exportTarget; - } - } - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - } - if (RegExpPrototypeExec( - invalidSegmentRegEx, - StringPrototypeSlice(target, 2) - ) !== null) - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - const resolved = new URL(target, packageJSONUrl); - const resolvedPath = resolved.pathname; - const packagePath = new URL(".", packageJSONUrl).pathname; - if (!StringPrototypeStartsWith(resolvedPath, packagePath)) - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - if (subpath === "") return resolved; - if (RegExpPrototypeExec(invalidSegmentRegEx, subpath) !== null) { - const request = pattern ? StringPrototypeReplace(match, "*", () => subpath) : match + subpath; - throwInvalidSubpath(request, packageJSONUrl, internal, base); - } - if (pattern) { - return new URL( - RegExpPrototypeSymbolReplace(patternRegEx, resolved.href, () => subpath) - ); - } - return new URL(subpath, resolved); -} -function isArrayIndex(key) { - const keyNum = +key; - if (`${keyNum}` !== key) return false; - return keyNum >= 0 && keyNum < 4294967295; -} -function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, base, pattern, internal, conditions) { - if (typeof target === "string") { - return resolvePackageTargetString( - target, - subpath, - packageSubpath, - packageJSONUrl, - base, - pattern, - internal); - } else if (ArrayIsArray(target)) { - if (target.length === 0) { - return null; - } - let lastException; - for (let i = 0; i < target.length; i++) { - const targetItem = target[i]; - let resolveResult; - try { - resolveResult = resolvePackageTarget( - packageJSONUrl, - targetItem, - subpath, - packageSubpath, - base, - pattern, - internal, - conditions - ); - } catch (e) { - lastException = e; - if (e.code === "ERR_INVALID_PACKAGE_TARGET") { - continue; - } - throw e; - } - if (resolveResult === void 0) { - continue; - } - if (resolveResult === null) { - lastException = null; - continue; - } - return resolveResult; - } - if (lastException === void 0 || lastException === null) - return lastException; - throw lastException; - } else if (typeof target === "object" && target !== null) { - const keys = ObjectGetOwnPropertyNames(target); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (isArrayIndex(key)) { - throw new ERR_INVALID_PACKAGE_CONFIG( - url.fileURLToPath(packageJSONUrl), - base, - '"exports" cannot contain numeric property keys.' - ); - } - } - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (key === "default" || conditions.has(key)) { - const conditionalTarget = target[key]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - conditionalTarget, - subpath, - packageSubpath, - base, - pattern, - internal, - conditions - ); - if (resolveResult === void 0) continue; - return resolveResult; - } - } - return void 0; - } else if (target === null) { - return null; - } - throwInvalidPackageTarget( - packageSubpath, - target, - packageJSONUrl, - internal, - base - ); -} -function patternKeyCompare(a, b) { - const aPatternIndex = StringPrototypeIndexOf(a, "*"); - const bPatternIndex = StringPrototypeIndexOf(b, "*"); - const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; - const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; - if (baseLenA > baseLenB) return -1; - if (baseLenB > baseLenA) return 1; - if (aPatternIndex === -1) return 1; - if (bPatternIndex === -1) return -1; - if (a.length > b.length) return -1; - if (b.length > a.length) return 1; - return 0; -} -function isConditionalExportsMainSugar(exports, packageJSONUrl, base) { - if (typeof exports === "string" || ArrayIsArray(exports)) return true; - if (typeof exports !== "object" || exports === null) return false; - const keys = ObjectGetOwnPropertyNames(exports); - let isConditionalSugar = false; - let i = 0; - for (let j = 0; j < keys.length; j++) { - const key = keys[j]; - const curIsConditionalSugar = key === "" || key[0] !== "."; - if (i++ === 0) { - isConditionalSugar = curIsConditionalSugar; - } else if (isConditionalSugar !== curIsConditionalSugar) { - throw new ERR_INVALID_PACKAGE_CONFIG( - url.fileURLToPath(packageJSONUrl), - base, - `"exports" cannot contain some keys starting with '.' and some not. The exports object must either be an object of package subpath keys or an object of main entry condition name keys only.` - ); - } - } - return isConditionalSugar; -} -function throwExportsNotFound(subpath, packageJSONUrl, base) { - throw new ERR_PACKAGE_PATH_NOT_EXPORTED( - url.fileURLToPath(new URL(".", packageJSONUrl)), - subpath, - base && url.fileURLToPath(base) - ); -} -const emittedPackageWarnings = /* @__PURE__ */ new Set(); -function emitTrailingSlashPatternDeprecation(match, pjsonUrl, base) { - const pjsonPath = url.fileURLToPath(pjsonUrl); - if (emittedPackageWarnings.has(pjsonPath + "|" + match)) return; - emittedPackageWarnings.add(pjsonPath + "|" + match); - process.emitWarning( - `Use of deprecated trailing slash pattern mapping "${match}" in the "exports" field module resolution of the package at ${pjsonPath}${base ? ` imported from ${url.fileURLToPath(base)}` : ""}. Mapping specifiers ending in "/" is no longer supported.`, - "DeprecationWarning", - "DEP0155" - ); -} -function packageExportsResolve({ - packageJSONUrl, - packageSubpath, - exports, - base, - conditions -}) { - if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) - exports = { ".": exports }; - if (ObjectPrototypeHasOwnProperty(exports, packageSubpath) && !StringPrototypeIncludes(packageSubpath, "*") && !StringPrototypeEndsWith(packageSubpath, "/")) { - const target = exports[packageSubpath]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - target, - "", - packageSubpath, - base, - false, - false, - conditions - ); - if (resolveResult == null) { - throwExportsNotFound(packageSubpath, packageJSONUrl, base); - } - return resolveResult; - } - let bestMatch = ""; - let bestMatchSubpath; - const keys = ObjectGetOwnPropertyNames(exports); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const patternIndex = StringPrototypeIndexOf(key, "*"); - if (patternIndex !== -1 && StringPrototypeStartsWith( - packageSubpath, - StringPrototypeSlice(key, 0, patternIndex) - )) { - if (StringPrototypeEndsWith(packageSubpath, "/")) - emitTrailingSlashPatternDeprecation( - packageSubpath, - packageJSONUrl, - base - ); - const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); - if (packageSubpath.length >= key.length && StringPrototypeEndsWith(packageSubpath, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, "*") === patternIndex) { - bestMatch = key; - bestMatchSubpath = StringPrototypeSlice( - packageSubpath, - patternIndex, - packageSubpath.length - patternTrailer.length - ); - } - } - } - if (bestMatch) { - const target = exports[bestMatch]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - target, - bestMatchSubpath, - bestMatch, - base, - true, - false, - conditions - ); - if (resolveResult == null) { - throwExportsNotFound(packageSubpath, packageJSONUrl, base); - } - return resolveResult; - } - throwExportsNotFound(packageSubpath, packageJSONUrl, base); -} -function packageImportsResolve({ name, base, conditions, readFileSyncFn }) { - if (name === "#" || StringPrototypeStartsWith(name, "#/") || StringPrototypeEndsWith(name, "/")) { - const reason = "is not a valid internal imports specifier name"; - throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, url.fileURLToPath(base)); - } - let packageJSONUrl; - const packageConfig = getPackageScopeConfig(base, readFileSyncFn); - if (packageConfig.exists) { - packageJSONUrl = url.pathToFileURL(packageConfig.pjsonPath); - const imports = packageConfig.imports; - if (imports) { - if (ObjectPrototypeHasOwnProperty(imports, name) && !StringPrototypeIncludes(name, "*")) { - const resolveResult = resolvePackageTarget( - packageJSONUrl, - imports[name], - "", - name, - base, - false, - true, - conditions - ); - if (resolveResult != null) { - return resolveResult; - } - } else { - let bestMatch = ""; - let bestMatchSubpath; - const keys = ObjectGetOwnPropertyNames(imports); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const patternIndex = StringPrototypeIndexOf(key, "*"); - if (patternIndex !== -1 && StringPrototypeStartsWith( - name, - StringPrototypeSlice(key, 0, patternIndex) - )) { - const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); - if (name.length >= key.length && StringPrototypeEndsWith(name, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, "*") === patternIndex) { - bestMatch = key; - bestMatchSubpath = StringPrototypeSlice( - name, - patternIndex, - name.length - patternTrailer.length - ); - } - } - } - if (bestMatch) { - const target = imports[bestMatch]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - target, - bestMatchSubpath, - bestMatch, - base, - true, - true, - conditions - ); - if (resolveResult != null) { - return resolveResult; - } - } - } - } - } - throwImportNotDefined(name, packageJSONUrl, base); -} - -const flagSymbol = Symbol('arg flag'); - -class ArgError extends Error { - constructor(msg, code) { - super(msg); - this.name = 'ArgError'; - this.code = code; - - Object.setPrototypeOf(this, ArgError.prototype); - } -} - -function arg( - opts, - { - argv = process.argv.slice(2), - permissive = false, - stopAtPositional = false - } = {} -) { - if (!opts) { - throw new ArgError( - 'argument specification object is required', - 'ARG_CONFIG_NO_SPEC' - ); - } - - const result = { _: [] }; - - const aliases = {}; - const handlers = {}; - - for (const key of Object.keys(opts)) { - if (!key) { - throw new ArgError( - 'argument key cannot be an empty string', - 'ARG_CONFIG_EMPTY_KEY' - ); - } - - if (key[0] !== '-') { - throw new ArgError( - `argument key must start with '-' but found: '${key}'`, - 'ARG_CONFIG_NONOPT_KEY' - ); - } - - if (key.length === 1) { - throw new ArgError( - `argument key must have a name; singular '-' keys are not allowed: ${key}`, - 'ARG_CONFIG_NONAME_KEY' - ); - } - - if (typeof opts[key] === 'string') { - aliases[key] = opts[key]; - continue; - } - - let type = opts[key]; - let isFlag = false; - - if ( - Array.isArray(type) && - type.length === 1 && - typeof type[0] === 'function' - ) { - const [fn] = type; - type = (value, name, prev = []) => { - prev.push(fn(value, name, prev[prev.length - 1])); - return prev; - }; - isFlag = fn === Boolean || fn[flagSymbol] === true; - } else if (typeof type === 'function') { - isFlag = type === Boolean || type[flagSymbol] === true; - } else { - throw new ArgError( - `type missing or not a function or valid array type: ${key}`, - 'ARG_CONFIG_VAD_TYPE' - ); - } - - if (key[1] !== '-' && key.length > 2) { - throw new ArgError( - `short argument keys (with a single hyphen) must have only one character: ${key}`, - 'ARG_CONFIG_SHORTOPT_TOOLONG' - ); - } - - handlers[key] = [type, isFlag]; - } - - for (let i = 0, len = argv.length; i < len; i++) { - const wholeArg = argv[i]; - - if (stopAtPositional && result._.length > 0) { - result._ = result._.concat(argv.slice(i)); - break; - } - - if (wholeArg === '--') { - result._ = result._.concat(argv.slice(i + 1)); - break; - } - - if (wholeArg.length > 1 && wholeArg[0] === '-') { - /* eslint-disable operator-linebreak */ - const separatedArguments = - wholeArg[1] === '-' || wholeArg.length === 2 - ? [wholeArg] - : wholeArg - .slice(1) - .split('') - .map((a) => `-${a}`); - /* eslint-enable operator-linebreak */ - - for (let j = 0; j < separatedArguments.length; j++) { - const arg = separatedArguments[j]; - const [originalArgName, argStr] = - arg[1] === '-' ? arg.split(/=(.*)/, 2) : [arg, undefined]; - - let argName = originalArgName; - while (argName in aliases) { - argName = aliases[argName]; - } - - if (!(argName in handlers)) { - if (permissive) { - result._.push(arg); - continue; - } else { - throw new ArgError( - `unknown or unexpected option: ${originalArgName}`, - 'ARG_UNKNOWN_OPTION' - ); - } - } - - const [type, isFlag] = handlers[argName]; - - if (!isFlag && j + 1 < separatedArguments.length) { - throw new ArgError( - `option requires argument (but was followed by another short argument): ${originalArgName}`, - 'ARG_MISSING_REQUIRED_SHORTARG' - ); - } - - if (isFlag) { - result[argName] = type(true, argName, result[argName]); - } else if (argStr === undefined) { - if ( - argv.length < i + 2 || - (argv[i + 1].length > 1 && - argv[i + 1][0] === '-' && - !( - argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/) && - (type === Number || - // eslint-disable-next-line no-undef - (typeof BigInt !== 'undefined' && type === BigInt)) - )) - ) { - const extended = - originalArgName === argName ? '' : ` (alias for ${argName})`; - throw new ArgError( - `option requires argument: ${originalArgName}${extended}`, - 'ARG_MISSING_REQUIRED_LONGARG' - ); - } - - result[argName] = type(argv[i + 1], argName, result[argName]); - ++i; - } else { - result[argName] = type(argStr, argName, result[argName]); - } - } - } else { - result._.push(wholeArg); - } - } - - return result; -} - -arg.flag = (fn) => { - fn[flagSymbol] = true; - return fn; -}; - -// Utility types -arg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1); - -// Expose error class -arg.ArgError = ArgError; - -var arg_1 = arg; - -/** - @license - The MIT License (MIT) - - Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ -function getOptionValue(opt) { - parseOptions(); - return options[opt]; -} -let options; -function parseOptions() { - if (!options) { - options = { - "--conditions": [], - ...parseArgv(getNodeOptionsEnvArgv()), - ...parseArgv(process.execArgv) - }; - } -} -function parseArgv(argv) { - return arg_1( - { - "--conditions": [String], - "-C": "--conditions" - }, - { - argv, - permissive: true - } - ); -} -function getNodeOptionsEnvArgv() { - const errors = []; - const envArgv = ParseNodeOptionsEnvVar(process.env.NODE_OPTIONS || "", errors); - if (errors.length !== 0) ; - return envArgv; -} -function ParseNodeOptionsEnvVar(node_options, errors) { - const env_argv = []; - let is_in_string = false; - let will_start_new_arg = true; - for (let index = 0; index < node_options.length; ++index) { - let c = node_options[index]; - if (c === "\\" && is_in_string) { - if (index + 1 === node_options.length) { - errors.push("invalid value for NODE_OPTIONS (invalid escape)\n"); - return env_argv; - } else { - c = node_options[++index]; - } - } else if (c === " " && !is_in_string) { - will_start_new_arg = true; - continue; - } else if (c === '"') { - is_in_string = !is_in_string; - continue; - } - if (will_start_new_arg) { - env_argv.push(c); - will_start_new_arg = false; - } else { - env_argv[env_argv.length - 1] += c; - } - } - if (is_in_string) { - errors.push("invalid value for NODE_OPTIONS (unterminated string)\n"); - } - return env_argv; -} - -function makeApi(runtimeState, opts) { - const alwaysWarnOnFallback = Number(process.env.PNP_ALWAYS_WARN_ON_FALLBACK) > 0; - const debugLevel = Number(process.env.PNP_DEBUG_LEVEL); - const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/; - const isStrictRegExp = /^(\/|\.{1,2}(\/|$))/; - const isDirRegExp = /\/$/; - const isRelativeRegexp = /^\.{0,2}\//; - const topLevelLocator = { name: null, reference: null }; - const fallbackLocators = []; - const emittedWarnings = /* @__PURE__ */ new Set(); - if (runtimeState.enableTopLevelFallback === true) - fallbackLocators.push(topLevelLocator); - if (opts.compatibilityMode !== false) { - for (const name of [`react-scripts`, `gatsby`]) { - const packageStore = runtimeState.packageRegistry.get(name); - if (packageStore) { - for (const reference of packageStore.keys()) { - if (reference === null) { - throw new Error(`Assertion failed: This reference shouldn't be null`); - } else { - fallbackLocators.push({ name, reference }); - } - } - } - } - } - const { - ignorePattern, - packageRegistry, - packageLocatorsByLocations - } = runtimeState; - function makeLogEntry(name, args) { - return { - fn: name, - args, - error: null, - result: null - }; - } - function trace(entry) { - const colors = process.stderr?.hasColors?.() ?? process.stdout.isTTY; - const c = (n, str) => `\x1B[${n}m${str}\x1B[0m`; - const error = entry.error; - if (error) - console.error(c(`31;1`, `\u2716 ${entry.error?.message.replace(/\n.*/s, ``)}`)); - else - console.error(c(`33;1`, `\u203C Resolution`)); - if (entry.args.length > 0) - console.error(); - for (const arg of entry.args) - console.error(` ${c(`37;1`, `In \u2190`)} ${nodeUtils.inspect(arg, { colors, compact: true })}`); - if (entry.result) { - console.error(); - console.error(` ${c(`37;1`, `Out \u2192`)} ${nodeUtils.inspect(entry.result, { colors, compact: true })}`); - } - const stack = new Error().stack.match(/(?<=^ +)at.*/gm)?.slice(2) ?? []; - if (stack.length > 0) { - console.error(); - for (const line of stack) { - console.error(` ${c(`38;5;244`, line)}`); - } - } - console.error(); - } - function maybeLog(name, fn) { - if (opts.allowDebug === false) - return fn; - if (Number.isFinite(debugLevel)) { - if (debugLevel >= 2) { - return (...args) => { - const logEntry = makeLogEntry(name, args); - try { - return logEntry.result = fn(...args); - } catch (error) { - throw logEntry.error = error; - } finally { - trace(logEntry); - } - }; - } else if (debugLevel >= 1) { - return (...args) => { - try { - return fn(...args); - } catch (error) { - const logEntry = makeLogEntry(name, args); - logEntry.error = error; - trace(logEntry); - throw error; - } - }; - } - } - return fn; - } - function getPackageInformationSafe(packageLocator) { - const packageInformation = getPackageInformation(packageLocator); - if (!packageInformation) { - throw makeError( - ErrorCode.INTERNAL, - `Couldn't find a matching entry in the dependency tree for the specified parent (this is probably an internal error)` - ); - } - return packageInformation; - } - function isDependencyTreeRoot(packageLocator) { - if (packageLocator.name === null) - return true; - for (const dependencyTreeRoot of runtimeState.dependencyTreeRoots) - if (dependencyTreeRoot.name === packageLocator.name && dependencyTreeRoot.reference === packageLocator.reference) - return true; - return false; - } - const defaultExportsConditions = /* @__PURE__ */ new Set([ - `node`, - `require`, - ...getOptionValue(`--conditions`) - ]); - function applyNodeExportsResolution(unqualifiedPath, conditions = defaultExportsConditions, issuer) { - const locator = findPackageLocator(ppath.join(unqualifiedPath, `internal.js`), { - resolveIgnored: true, - includeDiscardFromLookup: true - }); - if (locator === null) { - throw makeError( - ErrorCode.INTERNAL, - `The locator that owns the "${unqualifiedPath}" path can't be found inside the dependency tree (this is probably an internal error)` - ); - } - const { packageLocation } = getPackageInformationSafe(locator); - const manifestPath = ppath.join(packageLocation, Filename.manifest); - if (!opts.fakeFs.existsSync(manifestPath)) - return null; - const pkgJson = JSON.parse(opts.fakeFs.readFileSync(manifestPath, `utf8`)); - if (pkgJson.exports == null) - return null; - let subpath = ppath.contains(packageLocation, unqualifiedPath); - if (subpath === null) { - throw makeError( - ErrorCode.INTERNAL, - `unqualifiedPath doesn't contain the packageLocation (this is probably an internal error)` - ); - } - if (subpath !== `.` && !isRelativeRegexp.test(subpath)) - subpath = `./${subpath}`; - try { - const resolvedExport = packageExportsResolve({ - packageJSONUrl: url.pathToFileURL(npath.fromPortablePath(manifestPath)), - packageSubpath: subpath, - exports: pkgJson.exports, - base: issuer ? url.pathToFileURL(npath.fromPortablePath(issuer)) : null, - conditions - }); - return npath.toPortablePath(url.fileURLToPath(resolvedExport)); - } catch (error) { - throw makeError( - ErrorCode.EXPORTS_RESOLUTION_FAILED, - error.message, - { unqualifiedPath: getPathForDisplay(unqualifiedPath), locator, pkgJson, subpath: getPathForDisplay(subpath), conditions }, - error.code - ); - } - } - function applyNodeExtensionResolution(unqualifiedPath, candidates, { extensions }) { - let stat; - try { - candidates.push(unqualifiedPath); - stat = opts.fakeFs.statSync(unqualifiedPath); - } catch { - } - if (stat && !stat.isDirectory()) - return opts.fakeFs.realpathSync(unqualifiedPath); - if (stat && stat.isDirectory()) { - let pkgJson; - try { - pkgJson = JSON.parse(opts.fakeFs.readFileSync(ppath.join(unqualifiedPath, Filename.manifest), `utf8`)); - } catch { - } - let nextUnqualifiedPath; - if (pkgJson && pkgJson.main) - nextUnqualifiedPath = ppath.resolve(unqualifiedPath, pkgJson.main); - if (nextUnqualifiedPath && nextUnqualifiedPath !== unqualifiedPath) { - const resolution = applyNodeExtensionResolution(nextUnqualifiedPath, candidates, { extensions }); - if (resolution !== null) { - return resolution; - } - } - } - for (let i = 0, length = extensions.length; i < length; i++) { - const candidateFile = `${unqualifiedPath}${extensions[i]}`; - candidates.push(candidateFile); - if (opts.fakeFs.existsSync(candidateFile)) { - return candidateFile; - } - } - if (stat && stat.isDirectory()) { - for (let i = 0, length = extensions.length; i < length; i++) { - const candidateFile = ppath.format({ dir: unqualifiedPath, name: `index`, ext: extensions[i] }); - candidates.push(candidateFile); - if (opts.fakeFs.existsSync(candidateFile)) { - return candidateFile; - } - } - } - return null; - } - function makeFakeModule(path) { - const fakeModule = new require$$0.Module(path, null); - fakeModule.filename = path; - fakeModule.paths = require$$0.Module._nodeModulePaths(path); - return fakeModule; - } - function callNativeResolution(request, issuer) { - if (issuer.endsWith(`/`)) - issuer = ppath.join(issuer, `internal.js`); - return require$$0.Module._resolveFilename(npath.fromPortablePath(request), makeFakeModule(npath.fromPortablePath(issuer)), false, { plugnplay: false }); - } - function isPathIgnored(path) { - if (ignorePattern === null) - return false; - const subPath = ppath.contains(runtimeState.basePath, path); - if (subPath === null) - return false; - if (ignorePattern.test(subPath.replace(/\/$/, ``))) { - return true; - } else { - return false; - } - } - const VERSIONS = { std: 3, resolveVirtual: 1, getAllLocators: 1 }; - const topLevel = topLevelLocator; - function getPackageInformation({ name, reference }) { - const packageInformationStore = packageRegistry.get(name); - if (!packageInformationStore) - return null; - const packageInformation = packageInformationStore.get(reference); - if (!packageInformation) - return null; - return packageInformation; - } - function findPackageDependents({ name, reference }) { - const dependents = []; - for (const [dependentName, packageInformationStore] of packageRegistry) { - if (dependentName === null) - continue; - for (const [dependentReference, packageInformation] of packageInformationStore) { - if (dependentReference === null) - continue; - const dependencyReference = packageInformation.packageDependencies.get(name); - if (dependencyReference !== reference) - continue; - if (dependentName === name && dependentReference === reference) - continue; - dependents.push({ - name: dependentName, - reference: dependentReference - }); - } - } - return dependents; - } - function findBrokenPeerDependencies(dependency, initialPackage) { - const brokenPackages = /* @__PURE__ */ new Map(); - const alreadyVisited = /* @__PURE__ */ new Set(); - const traversal = (currentPackage) => { - const identifier = JSON.stringify(currentPackage.name); - if (alreadyVisited.has(identifier)) - return; - alreadyVisited.add(identifier); - const dependents = findPackageDependents(currentPackage); - for (const dependent of dependents) { - const dependentInformation = getPackageInformationSafe(dependent); - if (dependentInformation.packagePeers.has(dependency)) { - traversal(dependent); - } else { - let brokenSet = brokenPackages.get(dependent.name); - if (typeof brokenSet === `undefined`) - brokenPackages.set(dependent.name, brokenSet = /* @__PURE__ */ new Set()); - brokenSet.add(dependent.reference); - } - } - }; - traversal(initialPackage); - const brokenList = []; - for (const name of [...brokenPackages.keys()].sort()) - for (const reference of [...brokenPackages.get(name)].sort()) - brokenList.push({ name, reference }); - return brokenList; - } - function findPackageLocator(location, { resolveIgnored = false, includeDiscardFromLookup = false } = {}) { - if (isPathIgnored(location) && !resolveIgnored) - return null; - let relativeLocation = ppath.relative(runtimeState.basePath, location); - if (!relativeLocation.match(isStrictRegExp)) - relativeLocation = `./${relativeLocation}`; - if (!relativeLocation.endsWith(`/`)) - relativeLocation = `${relativeLocation}/`; - do { - const entry = packageLocatorsByLocations.get(relativeLocation); - if (typeof entry === `undefined` || entry.discardFromLookup && !includeDiscardFromLookup) { - relativeLocation = relativeLocation.substring(0, relativeLocation.lastIndexOf(`/`, relativeLocation.length - 2) + 1); - continue; - } - return entry.locator; - } while (relativeLocation !== ``); - return null; - } - function tryReadFile(filePath) { - try { - return opts.fakeFs.readFileSync(npath.toPortablePath(filePath), `utf8`); - } catch (err) { - if (err.code === `ENOENT`) - return void 0; - throw err; - } - } - function resolveToUnqualified(request, issuer, { considerBuiltins = true } = {}) { - if (request.startsWith(`#`)) - throw new Error(`resolveToUnqualified can not handle private import mappings`); - if (request === `pnpapi`) - return npath.toPortablePath(opts.pnpapiResolution); - if (considerBuiltins && require$$0.isBuiltin(request)) - return null; - const requestForDisplay = getPathForDisplay(request); - const issuerForDisplay = issuer && getPathForDisplay(issuer); - if (issuer && isPathIgnored(issuer)) { - if (!ppath.isAbsolute(request) || findPackageLocator(request) === null) { - const result = callNativeResolution(request, issuer); - if (result === false) { - throw makeError( - ErrorCode.BUILTIN_NODE_RESOLUTION_FAILED, - `The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer was explicitely ignored by the regexp) - -Require request: "${requestForDisplay}" -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay } - ); - } - return npath.toPortablePath(result); - } - } - let unqualifiedPath; - const dependencyNameMatch = request.match(pathRegExp); - if (!dependencyNameMatch) { - if (ppath.isAbsolute(request)) { - unqualifiedPath = ppath.normalize(request); - } else { - if (!issuer) { - throw makeError( - ErrorCode.API_ERROR, - `The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute`, - { request: requestForDisplay, issuer: issuerForDisplay } - ); - } - const absoluteIssuer = ppath.resolve(issuer); - if (issuer.match(isDirRegExp)) { - unqualifiedPath = ppath.normalize(ppath.join(absoluteIssuer, request)); - } else { - unqualifiedPath = ppath.normalize(ppath.join(ppath.dirname(absoluteIssuer), request)); - } - } - } else { - if (!issuer) { - throw makeError( - ErrorCode.API_ERROR, - `The resolveToUnqualified function must be called with a valid issuer when the path isn't a builtin nor absolute`, - { request: requestForDisplay, issuer: issuerForDisplay } - ); - } - const [, dependencyName, subPath] = dependencyNameMatch; - const issuerLocator = findPackageLocator(issuer); - if (!issuerLocator) { - const result = callNativeResolution(request, issuer); - if (result === false) { - throw makeError( - ErrorCode.BUILTIN_NODE_RESOLUTION_FAILED, - `The builtin node resolution algorithm was unable to resolve the requested module (it didn't go through the pnp resolver because the issuer doesn't seem to be part of the Yarn-managed dependency tree). - -Require path: "${requestForDisplay}" -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay } - ); - } - return npath.toPortablePath(result); - } - const issuerInformation = getPackageInformationSafe(issuerLocator); - let dependencyReference = issuerInformation.packageDependencies.get(dependencyName); - let fallbackReference = null; - if (dependencyReference == null) { - if (issuerLocator.name !== null) { - const exclusionEntry = runtimeState.fallbackExclusionList.get(issuerLocator.name); - const canUseFallbacks = !exclusionEntry || !exclusionEntry.has(issuerLocator.reference); - if (canUseFallbacks) { - for (let t = 0, T = fallbackLocators.length; t < T; ++t) { - const fallbackInformation = getPackageInformationSafe(fallbackLocators[t]); - const reference = fallbackInformation.packageDependencies.get(dependencyName); - if (reference == null) - continue; - if (alwaysWarnOnFallback) - fallbackReference = reference; - else - dependencyReference = reference; - break; - } - if (runtimeState.enableTopLevelFallback) { - if (dependencyReference == null && fallbackReference === null) { - const reference = runtimeState.fallbackPool.get(dependencyName); - if (reference != null) { - fallbackReference = reference; - } - } - } - } - } - } - let error = null; - if (dependencyReference === null) { - if (isDependencyTreeRoot(issuerLocator)) { - error = makeError( - ErrorCode.MISSING_PEER_DEPENDENCY, - `Your application tried to access ${dependencyName} (a peer dependency); this isn't allowed as there is no ancestor to satisfy the requirement. Use a devDependency if needed. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay, dependencyName } - ); - } else { - const brokenAncestors = findBrokenPeerDependencies(dependencyName, issuerLocator); - if (brokenAncestors.every((ancestor) => isDependencyTreeRoot(ancestor))) { - error = makeError( - ErrorCode.MISSING_PEER_DEPENDENCY, - `${issuerLocator.name} tried to access ${dependencyName} (a peer dependency) but it isn't provided by your application; this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay}) -${brokenAncestors.map((ancestorLocator) => `Ancestor breaking the chain: ${ancestorLocator.name}@${ancestorLocator.reference} -`).join(``)} -`, - { request: requestForDisplay, issuer: issuerForDisplay, issuerLocator: Object.assign({}, issuerLocator), dependencyName, brokenAncestors } - ); - } else { - error = makeError( - ErrorCode.MISSING_PEER_DEPENDENCY, - `${issuerLocator.name} tried to access ${dependencyName} (a peer dependency) but it isn't provided by its ancestors; this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay}) - -${brokenAncestors.map((ancestorLocator) => `Ancestor breaking the chain: ${ancestorLocator.name}@${ancestorLocator.reference} -`).join(``)} -`, - { request: requestForDisplay, issuer: issuerForDisplay, issuerLocator: Object.assign({}, issuerLocator), dependencyName, brokenAncestors } - ); - } - } - } else if (dependencyReference === void 0) { - if (!considerBuiltins && require$$0.isBuiltin(request)) { - if (isDependencyTreeRoot(issuerLocator)) { - error = makeError( - ErrorCode.UNDECLARED_DEPENDENCY, - `Your application tried to access ${dependencyName}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${dependencyName} isn't otherwise declared in your dependencies, this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay, dependencyName } - ); - } else { - error = makeError( - ErrorCode.UNDECLARED_DEPENDENCY, - `${issuerLocator.name} tried to access ${dependencyName}. While this module is usually interpreted as a Node builtin, your resolver is running inside a non-Node resolution context where such builtins are ignored. Since ${dependencyName} isn't otherwise declared in ${issuerLocator.name}'s dependencies, this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay, issuerLocator: Object.assign({}, issuerLocator), dependencyName } - ); - } - } else { - if (isDependencyTreeRoot(issuerLocator)) { - error = makeError( - ErrorCode.UNDECLARED_DEPENDENCY, - `Your application tried to access ${dependencyName}, but it isn't declared in your dependencies; this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerForDisplay} -`, - { request: requestForDisplay, issuer: issuerForDisplay, dependencyName } - ); - } else { - error = makeError( - ErrorCode.UNDECLARED_DEPENDENCY, - `${issuerLocator.name} tried to access ${dependencyName}, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound. - -Required package: ${dependencyName}${dependencyName !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay}) -`, - { request: requestForDisplay, issuer: issuerForDisplay, issuerLocator: Object.assign({}, issuerLocator), dependencyName } - ); - } - } - } - if (dependencyReference == null) { - if (fallbackReference === null || error === null) - throw error || new Error(`Assertion failed: Expected an error to have been set`); - dependencyReference = fallbackReference; - const message = error.message.replace(/\n.*/g, ``); - error.message = message; - if (!emittedWarnings.has(message) && debugLevel !== 0) { - emittedWarnings.add(message); - process.emitWarning(error); - } - } - const dependencyLocator = Array.isArray(dependencyReference) ? { name: dependencyReference[0], reference: dependencyReference[1] } : { name: dependencyName, reference: dependencyReference }; - const dependencyInformation = getPackageInformationSafe(dependencyLocator); - if (!dependencyInformation.packageLocation) { - throw makeError( - ErrorCode.MISSING_DEPENDENCY, - `A dependency seems valid but didn't get installed for some reason. This might be caused by a partial install, such as dev vs prod. - -Required package: ${dependencyLocator.name}@${dependencyLocator.reference}${dependencyLocator.name !== requestForDisplay ? ` (via "${requestForDisplay}")` : ``} -Required by: ${issuerLocator.name}@${issuerLocator.reference} (via ${issuerForDisplay}) -`, - { request: requestForDisplay, issuer: issuerForDisplay, dependencyLocator: Object.assign({}, dependencyLocator) } - ); - } - const dependencyLocation = dependencyInformation.packageLocation; - if (subPath) { - unqualifiedPath = ppath.join(dependencyLocation, subPath); - } else { - unqualifiedPath = dependencyLocation; - } - } - return ppath.normalize(unqualifiedPath); - } - function resolveUnqualifiedExport(request, unqualifiedPath, conditions = defaultExportsConditions, issuer) { - if (isStrictRegExp.test(request)) - return unqualifiedPath; - const unqualifiedExportPath = applyNodeExportsResolution(unqualifiedPath, conditions, issuer); - if (unqualifiedExportPath) { - return ppath.normalize(unqualifiedExportPath); - } else { - return unqualifiedPath; - } - } - function resolveUnqualified(unqualifiedPath, { extensions = Object.keys(require$$0.Module._extensions) } = {}) { - const candidates = []; - const qualifiedPath = applyNodeExtensionResolution(unqualifiedPath, candidates, { extensions }); - if (qualifiedPath) { - return ppath.normalize(qualifiedPath); - } else { - reportRequiredFilesToWatchMode(candidates.map((candidate) => npath.fromPortablePath(candidate))); - const unqualifiedPathForDisplay = getPathForDisplay(unqualifiedPath); - const containingPackage = findPackageLocator(unqualifiedPath); - if (containingPackage) { - const { packageLocation } = getPackageInformationSafe(containingPackage); - let exists = true; - try { - opts.fakeFs.accessSync(packageLocation); - } catch (err) { - if (err?.code === `ENOENT`) { - exists = false; - } else { - const readableError = (err?.message ?? err ?? `empty exception thrown`).replace(/^[A-Z]/, ($0) => $0.toLowerCase()); - throw makeError(ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, `Required package exists but could not be accessed (${readableError}). - -Missing package: ${containingPackage.name}@${containingPackage.reference} -Expected package location: ${getPathForDisplay(packageLocation)} -`, { unqualifiedPath: unqualifiedPathForDisplay, extensions }); - } - } - if (!exists) { - const errorMessage = packageLocation.includes(`/unplugged/`) ? `Required unplugged package missing from disk. This may happen when switching branches without running installs (unplugged packages must be fully materialized on disk to work).` : `Required package missing from disk. If you keep your packages inside your repository then restarting the Node process may be enough. Otherwise, try to run an install first.`; - throw makeError( - ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, - `${errorMessage} - -Missing package: ${containingPackage.name}@${containingPackage.reference} -Expected package location: ${getPathForDisplay(packageLocation)} -`, - { unqualifiedPath: unqualifiedPathForDisplay, extensions } - ); - } - } - throw makeError( - ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED, - `Qualified path resolution failed: we looked for the following paths, but none could be accessed. - -Source path: ${unqualifiedPathForDisplay} -${candidates.map((candidate) => `Not found: ${getPathForDisplay(candidate)} -`).join(``)}`, - { unqualifiedPath: unqualifiedPathForDisplay, extensions } - ); - } - } - function resolvePrivateRequest(request, issuer, opts2) { - if (!issuer) - throw new Error(`Assertion failed: An issuer is required to resolve private import mappings`); - const resolved = packageImportsResolve({ - name: request, - base: url.pathToFileURL(npath.fromPortablePath(issuer)), - conditions: opts2.conditions ?? defaultExportsConditions, - readFileSyncFn: tryReadFile - }); - if (resolved instanceof URL) { - return resolveUnqualified(npath.toPortablePath(url.fileURLToPath(resolved)), { extensions: opts2.extensions }); - } else { - if (resolved.startsWith(`#`)) - throw new Error(`Mapping from one private import to another isn't allowed`); - return resolveRequest(resolved, issuer, opts2); - } - } - function resolveRequest(request, issuer, opts2 = {}) { - try { - if (request.startsWith(`#`)) - return resolvePrivateRequest(request, issuer, opts2); - const { considerBuiltins, extensions, conditions } = opts2; - const unqualifiedPath = resolveToUnqualified(request, issuer, { considerBuiltins }); - if (request === `pnpapi`) - return unqualifiedPath; - if (unqualifiedPath === null) - return null; - const isIssuerIgnored = () => issuer !== null ? isPathIgnored(issuer) : false; - const remappedPath = (!considerBuiltins || !require$$0.isBuiltin(request)) && !isIssuerIgnored() ? resolveUnqualifiedExport(request, unqualifiedPath, conditions, issuer) : unqualifiedPath; - return resolveUnqualified(remappedPath, { extensions }); - } catch (error) { - if (Object.hasOwn(error, `pnpCode`)) - Object.assign(error.data, { request: getPathForDisplay(request), issuer: issuer && getPathForDisplay(issuer) }); - throw error; - } - } - function resolveVirtual(request) { - const normalized = ppath.normalize(request); - const resolved = VirtualFS.resolveVirtual(normalized); - return resolved !== normalized ? resolved : null; - } - return { - VERSIONS, - topLevel, - getLocator: (name, referencish) => { - if (Array.isArray(referencish)) { - return { name: referencish[0], reference: referencish[1] }; - } else { - return { name, reference: referencish }; - } - }, - getDependencyTreeRoots: () => { - return [...runtimeState.dependencyTreeRoots]; - }, - getAllLocators() { - const locators = []; - for (const [name, entry] of packageRegistry) - for (const reference of entry.keys()) - if (name !== null && reference !== null) - locators.push({ name, reference }); - return locators; - }, - getPackageInformation: (locator) => { - const info = getPackageInformation(locator); - if (info === null) - return null; - const packageLocation = npath.fromPortablePath(info.packageLocation); - const nativeInfo = { ...info, packageLocation }; - return nativeInfo; - }, - findPackageLocator: (path) => { - return findPackageLocator(npath.toPortablePath(path)); - }, - resolveToUnqualified: maybeLog(`resolveToUnqualified`, (request, issuer, opts2) => { - const portableIssuer = issuer !== null ? npath.toPortablePath(issuer) : null; - const resolution = resolveToUnqualified(npath.toPortablePath(request), portableIssuer, opts2); - if (resolution === null) - return null; - return npath.fromPortablePath(resolution); - }), - resolveUnqualified: maybeLog(`resolveUnqualified`, (unqualifiedPath, opts2) => { - return npath.fromPortablePath(resolveUnqualified(npath.toPortablePath(unqualifiedPath), opts2)); - }), - resolveRequest: maybeLog(`resolveRequest`, (request, issuer, opts2) => { - const portableIssuer = issuer !== null ? npath.toPortablePath(issuer) : null; - const resolution = resolveRequest(npath.toPortablePath(request), portableIssuer, opts2); - if (resolution === null) - return null; - return npath.fromPortablePath(resolution); - }), - resolveVirtual: maybeLog(`resolveVirtual`, (path) => { - const result = resolveVirtual(npath.toPortablePath(path)); - if (result !== null) { - return npath.fromPortablePath(result); - } else { - return null; - } - }) - }; -} - -function makeManager(pnpapi, opts) { - const initialApiPath = npath.toPortablePath(pnpapi.resolveToUnqualified(`pnpapi`, null)); - const initialApiStats = opts.fakeFs.statSync(npath.toPortablePath(initialApiPath)); - const apiMetadata = /* @__PURE__ */ new Map([ - [initialApiPath, { - instance: pnpapi, - stats: initialApiStats, - lastRefreshCheck: Date.now() - }] - ]); - function loadApiInstance(pnpApiPath) { - const nativePath = npath.fromPortablePath(pnpApiPath); - const module = new require$$0.Module(nativePath, null); - module.load(nativePath); - return module.exports; - } - function refreshApiEntry(pnpApiPath, apiEntry) { - const timeNow = Date.now(); - if (timeNow - apiEntry.lastRefreshCheck < 500) - return; - apiEntry.lastRefreshCheck = timeNow; - const stats = opts.fakeFs.statSync(pnpApiPath); - if (stats.mtime > apiEntry.stats.mtime) { - process.emitWarning(`[Warning] The runtime detected new information in a PnP file; reloading the API instance (${npath.fromPortablePath(pnpApiPath)})`); - apiEntry.stats = stats; - apiEntry.instance = loadApiInstance(pnpApiPath); - } - } - function getApiEntry(pnpApiPath, refresh = false) { - let apiEntry = apiMetadata.get(pnpApiPath); - if (typeof apiEntry !== `undefined`) { - if (refresh) { - refreshApiEntry(pnpApiPath, apiEntry); - } - } else { - apiMetadata.set(pnpApiPath, apiEntry = { - instance: loadApiInstance(pnpApiPath), - stats: opts.fakeFs.statSync(pnpApiPath), - lastRefreshCheck: Date.now() - }); - } - return apiEntry; - } - const findApiPathCache = /* @__PURE__ */ new Map(); - function addToCacheAndReturn(start, end, target) { - if (target !== null) { - target = VirtualFS.resolveVirtual(target); - target = opts.fakeFs.realpathSync(target); - } - let curr; - let next = start; - do { - curr = next; - findApiPathCache.set(curr, target); - next = ppath.dirname(curr); - } while (curr !== end); - return target; - } - function findApiPathFor(modulePath) { - let bestCandidate = null; - for (const [apiPath, apiEntry] of apiMetadata) { - const locator = apiEntry.instance.findPackageLocator(modulePath); - if (!locator) - continue; - if (apiMetadata.size === 1) - return apiPath; - const packageInformation = apiEntry.instance.getPackageInformation(locator); - if (!packageInformation) - throw new Error(`Assertion failed: Couldn't get package information for '${modulePath}'`); - if (!bestCandidate) - bestCandidate = { packageLocation: packageInformation.packageLocation, apiPaths: [] }; - if (packageInformation.packageLocation === bestCandidate.packageLocation) { - bestCandidate.apiPaths.push(apiPath); - } else if (packageInformation.packageLocation.length > bestCandidate.packageLocation.length) { - bestCandidate = { packageLocation: packageInformation.packageLocation, apiPaths: [apiPath] }; - } - } - if (bestCandidate) { - if (bestCandidate.apiPaths.length === 1) - return bestCandidate.apiPaths[0]; - const controlSegment = bestCandidate.apiPaths.map((apiPath) => ` ${npath.fromPortablePath(apiPath)}`).join(` -`); - throw new Error(`Unable to locate pnpapi, the module '${modulePath}' is controlled by multiple pnpapi instances. -This is usually caused by using the global cache (enableGlobalCache: true) - -Controlled by: -${controlSegment} -`); - } - const start = ppath.resolve(npath.toPortablePath(modulePath)); - let curr; - let next = start; - do { - curr = next; - const cached = findApiPathCache.get(curr); - if (cached !== void 0) - return addToCacheAndReturn(start, curr, cached); - const cjsCandidate = ppath.join(curr, Filename.pnpCjs); - if (opts.fakeFs.existsSync(cjsCandidate) && opts.fakeFs.statSync(cjsCandidate).isFile()) - return addToCacheAndReturn(start, curr, cjsCandidate); - const legacyCjsCandidate = ppath.join(curr, Filename.pnpJs); - if (opts.fakeFs.existsSync(legacyCjsCandidate) && opts.fakeFs.statSync(legacyCjsCandidate).isFile()) - return addToCacheAndReturn(start, curr, legacyCjsCandidate); - next = ppath.dirname(curr); - } while (curr !== PortablePath.root); - return addToCacheAndReturn(start, curr, null); - } - const moduleToApiPathCache = /* @__PURE__ */ new WeakMap(); - function getApiPathFromParent(parent) { - if (parent == null) - return initialApiPath; - let apiPath = moduleToApiPathCache.get(parent); - if (typeof apiPath !== `undefined`) - return apiPath; - apiPath = parent.filename ? findApiPathFor(parent.filename) : null; - moduleToApiPathCache.set(parent, apiPath); - return apiPath; - } - return { - getApiPathFromParent, - findApiPathFor, - getApiEntry - }; -} - -const localFs = { ...fs__default.default }; -const nodeFs = new NodeFS(localFs); -const defaultRuntimeState = $$SETUP_STATE(hydrateRuntimeState); -const defaultPnpapiResolution = __filename; -const customZipImplementation = defaultRuntimeState.pnpZipBackend === `js` ? JsZipImpl : void 0; -const defaultFsLayer = new VirtualFS({ - baseFs: new ZipOpenFS({ - customZipImplementation, - baseFs: nodeFs, - maxOpenFiles: 80, - readOnlyArchives: true - }) -}); -class DynamicFS extends ProxiedFS { - baseFs = defaultFsLayer; - constructor() { - super(ppath); - } - mapToBase(p) { - return p; - } - mapFromBase(p) { - return p; - } -} -const dynamicFsLayer = new DynamicFS(); -let manager; -const defaultApi = Object.assign(makeApi(defaultRuntimeState, { - fakeFs: dynamicFsLayer, - pnpapiResolution: defaultPnpapiResolution -}), { - /** - * Can be used to generate a different API than the default one (for example - * to map it on `/` rather than the local directory path, or to use a - * different FS layer than the default one). - */ - makeApi: ({ - basePath = void 0, - fakeFs = dynamicFsLayer, - pnpapiResolution = defaultPnpapiResolution, - ...rest - }) => { - const apiRuntimeState = typeof basePath !== `undefined` ? $$SETUP_STATE(hydrateRuntimeState, basePath) : defaultRuntimeState; - return makeApi(apiRuntimeState, { - fakeFs, - pnpapiResolution, - ...rest - }); - }, - /** - * Will inject the specified API into the environment, monkey-patching FS. Is - * automatically called when the hook is loaded through `--require`. - */ - setup: (api) => { - applyPatch(api || defaultApi, { - fakeFs: defaultFsLayer, - manager - }); - dynamicFsLayer.baseFs = new NodeFS(fs__default.default); - } -}); -manager = makeManager(defaultApi, { - fakeFs: dynamicFsLayer -}); -if (module.parent && module.parent.id === `internal/preload`) { - defaultApi.setup(); - if (module.filename) { - delete require$$0__default.default._cache[module.filename]; - } -} -if (process.mainModule === module) { - const reportError = (code, message, data) => { - process.stdout.write(`${JSON.stringify([{ code, message, data }, null])} -`); - }; - const reportSuccess = (resolution) => { - process.stdout.write(`${JSON.stringify([null, resolution])} -`); - }; - const processResolution = (request, issuer) => { - try { - reportSuccess(defaultApi.resolveRequest(request, issuer)); - } catch (error) { - reportError(error.code, error.message, error.data); - } - }; - const processRequest = (data) => { - try { - const [request, issuer] = JSON.parse(data); - processResolution(request, issuer); - } catch (error) { - reportError(`INVALID_JSON`, error.message, error.data); - } - }; - if (process.argv.length > 2) { - if (process.argv.length !== 4) { - process.stderr.write(`Usage: ${process.argv[0]} ${process.argv[1]} -`); - process.exitCode = 64; - } else { - processResolution(process.argv[2], process.argv[3]); - } - } else { - let buffer = ``; - const decoder = new StringDecoder__default.default.StringDecoder(); - process.stdin.on(`data`, (chunk) => { - buffer += decoder.write(chunk); - do { - const index = buffer.indexOf(` -`); - if (index === -1) - break; - const line = buffer.slice(0, index); - buffer = buffer.slice(index + 1); - processRequest(line); - } while (true); - }); - } -} - -module.exports = defaultApi; diff --git a/.pnp.loader.mjs b/.pnp.loader.mjs deleted file mode 100644 index 2d5a584..0000000 --- a/.pnp.loader.mjs +++ /dev/null @@ -1,2126 +0,0 @@ -/* eslint-disable */ -// @ts-nocheck - -import fs from 'fs'; -import { URL as URL$1, fileURLToPath, pathToFileURL } from 'url'; -import path from 'path'; -import { createHash } from 'crypto'; -import { EOL } from 'os'; -import esmModule, { createRequire, isBuiltin } from 'module'; -import assert from 'assert'; - -const SAFE_TIME = 456789e3; - -const PortablePath = { - root: `/`, - dot: `.`, - parent: `..` -}; -const npath = Object.create(path); -const ppath = Object.create(path.posix); -npath.cwd = () => process.cwd(); -ppath.cwd = process.platform === `win32` ? () => toPortablePath(process.cwd()) : process.cwd; -if (process.platform === `win32`) { - ppath.resolve = (...segments) => { - if (segments.length > 0 && ppath.isAbsolute(segments[0])) { - return path.posix.resolve(...segments); - } else { - return path.posix.resolve(ppath.cwd(), ...segments); - } - }; -} -const contains = function(pathUtils, from, to) { - from = pathUtils.normalize(from); - to = pathUtils.normalize(to); - if (from === to) - return `.`; - if (!from.endsWith(pathUtils.sep)) - from = from + pathUtils.sep; - if (to.startsWith(from)) { - return to.slice(from.length); - } else { - return null; - } -}; -npath.contains = (from, to) => contains(npath, from, to); -ppath.contains = (from, to) => contains(ppath, from, to); -const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/; -const UNC_WINDOWS_PATH_REGEXP = /^\/\/(\.\/)?(.*)$/; -const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/; -const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/; -function fromPortablePathWin32(p) { - let portablePathMatch, uncPortablePathMatch; - if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP)) - p = portablePathMatch[1]; - else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP)) - p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`; - else - return p; - return p.replace(/\//g, `\\`); -} -function toPortablePathWin32(p) { - p = p.replace(/\\/g, `/`); - let windowsPathMatch, uncWindowsPathMatch; - if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP)) - p = `/${windowsPathMatch[1]}`; - else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP)) - p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`; - return p; -} -const toPortablePath = process.platform === `win32` ? toPortablePathWin32 : (p) => p; -const fromPortablePath = process.platform === `win32` ? fromPortablePathWin32 : (p) => p; -npath.fromPortablePath = fromPortablePath; -npath.toPortablePath = toPortablePath; -function convertPath(targetPathUtils, sourcePath) { - return targetPathUtils === npath ? fromPortablePath(sourcePath) : toPortablePath(sourcePath); -} - -const defaultTime = new Date(SAFE_TIME * 1e3); -const defaultTimeMs = defaultTime.getTime(); -async function copyPromise(destinationFs, destination, sourceFs, source, opts) { - const normalizedDestination = destinationFs.pathUtils.normalize(destination); - const normalizedSource = sourceFs.pathUtils.normalize(source); - const prelayout = []; - const postlayout = []; - const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : await sourceFs.lstatPromise(normalizedSource); - await destinationFs.mkdirpPromise(destinationFs.pathUtils.dirname(destination), { utimes: [atime, mtime] }); - await copyImpl(prelayout, postlayout, destinationFs, normalizedDestination, sourceFs, normalizedSource, { ...opts, didParentExist: true }); - for (const operation of prelayout) - await operation(); - await Promise.all(postlayout.map((operation) => { - return operation(); - })); -} -async function copyImpl(prelayout, postlayout, destinationFs, destination, sourceFs, source, opts) { - const destinationStat = opts.didParentExist ? await maybeLStat(destinationFs, destination) : null; - const sourceStat = await sourceFs.lstatPromise(source); - const { atime, mtime } = opts.stableTime ? { atime: defaultTime, mtime: defaultTime } : sourceStat; - let updated; - switch (true) { - case sourceStat.isDirectory(): - { - updated = await copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - case sourceStat.isFile(): - { - updated = await copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - case sourceStat.isSymbolicLink(): - { - updated = await copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } - break; - default: { - throw new Error(`Unsupported file type (${sourceStat.mode})`); - } - } - if (opts.linkStrategy?.type !== `HardlinkFromIndex` || !sourceStat.isFile()) { - if (updated || destinationStat?.mtime?.getTime() !== mtime.getTime() || destinationStat?.atime?.getTime() !== atime.getTime()) { - postlayout.push(() => destinationFs.lutimesPromise(destination, atime, mtime)); - updated = true; - } - if (destinationStat === null || (destinationStat.mode & 511) !== (sourceStat.mode & 511)) { - postlayout.push(() => destinationFs.chmodPromise(destination, sourceStat.mode & 511)); - updated = true; - } - } - return updated; -} -async function maybeLStat(baseFs, p) { - try { - return await baseFs.lstatPromise(p); - } catch { - return null; - } -} -async function copyFolder(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null && !destinationStat.isDirectory()) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - let updated = false; - if (destinationStat === null) { - prelayout.push(async () => { - try { - await destinationFs.mkdirPromise(destination, { mode: sourceStat.mode }); - } catch (err) { - if (err.code !== `EEXIST`) { - throw err; - } - } - }); - updated = true; - } - const entries = await sourceFs.readdirPromise(source); - const nextOpts = opts.didParentExist && !destinationStat ? { ...opts, didParentExist: false } : opts; - if (opts.stableSort) { - for (const entry of entries.sort()) { - if (await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts)) { - updated = true; - } - } - } else { - const entriesUpdateStatus = await Promise.all(entries.map(async (entry) => { - await copyImpl(prelayout, postlayout, destinationFs, destinationFs.pathUtils.join(destination, entry), sourceFs, sourceFs.pathUtils.join(source, entry), nextOpts); - })); - if (entriesUpdateStatus.some((status) => status)) { - updated = true; - } - } - return updated; -} -async function copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, linkStrategy) { - const sourceHash = await sourceFs.checksumFilePromise(source, { algorithm: `sha1` }); - const defaultMode = 420; - const sourceMode = sourceStat.mode & 511; - const indexFileName = `${sourceHash}${sourceMode !== defaultMode ? sourceMode.toString(8) : ``}`; - const indexPath = destinationFs.pathUtils.join(linkStrategy.indexPath, sourceHash.slice(0, 2), `${indexFileName}.dat`); - let AtomicBehavior; - ((AtomicBehavior2) => { - AtomicBehavior2[AtomicBehavior2["Lock"] = 0] = "Lock"; - AtomicBehavior2[AtomicBehavior2["Rename"] = 1] = "Rename"; - })(AtomicBehavior || (AtomicBehavior = {})); - let atomicBehavior = 1 /* Rename */; - let indexStat = await maybeLStat(destinationFs, indexPath); - if (destinationStat) { - const isDestinationHardlinkedFromIndex = indexStat && destinationStat.dev === indexStat.dev && destinationStat.ino === indexStat.ino; - const isIndexModified = indexStat?.mtimeMs !== defaultTimeMs; - if (isDestinationHardlinkedFromIndex) { - if (isIndexModified && linkStrategy.autoRepair) { - atomicBehavior = 0 /* Lock */; - indexStat = null; - } - } - if (!isDestinationHardlinkedFromIndex) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - } - const tempPath = !indexStat && atomicBehavior === 1 /* Rename */ ? `${indexPath}.${Math.floor(Math.random() * 4294967296).toString(16).padStart(8, `0`)}` : null; - let tempPathCleaned = false; - prelayout.push(async () => { - if (!indexStat) { - if (atomicBehavior === 0 /* Lock */) { - await destinationFs.lockPromise(indexPath, async () => { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(indexPath, content); - }); - } - if (atomicBehavior === 1 /* Rename */ && tempPath) { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(tempPath, content); - try { - await destinationFs.linkPromise(tempPath, indexPath); - } catch (err) { - if (err.code === `EEXIST`) { - tempPathCleaned = true; - await destinationFs.unlinkPromise(tempPath); - } else { - throw err; - } - } - } - } - if (!destinationStat) { - await destinationFs.linkPromise(indexPath, destination); - } - }); - postlayout.push(async () => { - if (!indexStat) { - await destinationFs.lutimesPromise(indexPath, defaultTime, defaultTime); - if (sourceMode !== defaultMode) { - await destinationFs.chmodPromise(indexPath, sourceMode); - } - } - if (tempPath && !tempPathCleaned) { - await destinationFs.unlinkPromise(tempPath); - } - }); - return false; -} -async function copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - prelayout.push(async () => { - const content = await sourceFs.readFilePromise(source); - await destinationFs.writeFilePromise(destination, content); - }); - return true; -} -async function copyFile(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (opts.linkStrategy?.type === `HardlinkFromIndex`) { - return copyFileViaIndex(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts, opts.linkStrategy); - } else { - return copyFileDirect(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts); - } -} -async function copySymlink(prelayout, postlayout, destinationFs, destination, destinationStat, sourceFs, source, sourceStat, opts) { - if (destinationStat !== null) { - if (opts.overwrite) { - prelayout.push(async () => destinationFs.removePromise(destination)); - destinationStat = null; - } else { - return false; - } - } - prelayout.push(async () => { - await destinationFs.symlinkPromise(convertPath(destinationFs.pathUtils, await sourceFs.readlinkPromise(source)), destination); - }); - return true; -} - -class FakeFS { - pathUtils; - constructor(pathUtils) { - this.pathUtils = pathUtils; - } - async *genTraversePromise(init, { stableSort = false } = {}) { - const stack = [init]; - while (stack.length > 0) { - const p = stack.shift(); - const entry = await this.lstatPromise(p); - if (entry.isDirectory()) { - const entries = await this.readdirPromise(p); - if (stableSort) { - for (const entry2 of entries.sort()) { - stack.push(this.pathUtils.join(p, entry2)); - } - } else { - throw new Error(`Not supported`); - } - } else { - yield p; - } - } - } - async checksumFilePromise(path, { algorithm = `sha512` } = {}) { - const fd = await this.openPromise(path, `r`); - try { - const CHUNK_SIZE = 65536; - const chunk = Buffer.allocUnsafeSlow(CHUNK_SIZE); - const hash = createHash(algorithm); - let bytesRead = 0; - while ((bytesRead = await this.readPromise(fd, chunk, 0, CHUNK_SIZE)) !== 0) - hash.update(bytesRead === CHUNK_SIZE ? chunk : chunk.slice(0, bytesRead)); - return hash.digest(`hex`); - } finally { - await this.closePromise(fd); - } - } - async removePromise(p, { recursive = true, maxRetries = 5 } = {}) { - let stat; - try { - stat = await this.lstatPromise(p); - } catch (error) { - if (error.code === `ENOENT`) { - return; - } else { - throw error; - } - } - if (stat.isDirectory()) { - if (recursive) { - const entries = await this.readdirPromise(p); - await Promise.all(entries.map((entry) => { - return this.removePromise(this.pathUtils.resolve(p, entry)); - })); - } - for (let t = 0; t <= maxRetries; t++) { - try { - await this.rmdirPromise(p); - break; - } catch (error) { - if (error.code !== `EBUSY` && error.code !== `ENOTEMPTY`) { - throw error; - } else if (t < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, t * 100)); - } - } - } - } else { - await this.unlinkPromise(p); - } - } - removeSync(p, { recursive = true } = {}) { - let stat; - try { - stat = this.lstatSync(p); - } catch (error) { - if (error.code === `ENOENT`) { - return; - } else { - throw error; - } - } - if (stat.isDirectory()) { - if (recursive) - for (const entry of this.readdirSync(p)) - this.removeSync(this.pathUtils.resolve(p, entry)); - this.rmdirSync(p); - } else { - this.unlinkSync(p); - } - } - async mkdirpPromise(p, { chmod, utimes } = {}) { - p = this.resolve(p); - if (p === this.pathUtils.dirname(p)) - return void 0; - const parts = p.split(this.pathUtils.sep); - let createdDirectory; - for (let u = 2; u <= parts.length; ++u) { - const subPath = parts.slice(0, u).join(this.pathUtils.sep); - if (!this.existsSync(subPath)) { - try { - await this.mkdirPromise(subPath); - } catch (error) { - if (error.code === `EEXIST`) { - continue; - } else { - throw error; - } - } - createdDirectory ??= subPath; - if (chmod != null) - await this.chmodPromise(subPath, chmod); - if (utimes != null) { - await this.utimesPromise(subPath, utimes[0], utimes[1]); - } else { - const parentStat = await this.statPromise(this.pathUtils.dirname(subPath)); - await this.utimesPromise(subPath, parentStat.atime, parentStat.mtime); - } - } - } - return createdDirectory; - } - mkdirpSync(p, { chmod, utimes } = {}) { - p = this.resolve(p); - if (p === this.pathUtils.dirname(p)) - return void 0; - const parts = p.split(this.pathUtils.sep); - let createdDirectory; - for (let u = 2; u <= parts.length; ++u) { - const subPath = parts.slice(0, u).join(this.pathUtils.sep); - if (!this.existsSync(subPath)) { - try { - this.mkdirSync(subPath); - } catch (error) { - if (error.code === `EEXIST`) { - continue; - } else { - throw error; - } - } - createdDirectory ??= subPath; - if (chmod != null) - this.chmodSync(subPath, chmod); - if (utimes != null) { - this.utimesSync(subPath, utimes[0], utimes[1]); - } else { - const parentStat = this.statSync(this.pathUtils.dirname(subPath)); - this.utimesSync(subPath, parentStat.atime, parentStat.mtime); - } - } - } - return createdDirectory; - } - async copyPromise(destination, source, { baseFs = this, overwrite = true, stableSort = false, stableTime = false, linkStrategy = null } = {}) { - return await copyPromise(this, destination, baseFs, source, { overwrite, stableSort, stableTime, linkStrategy }); - } - copySync(destination, source, { baseFs = this, overwrite = true } = {}) { - const stat = baseFs.lstatSync(source); - const exists = this.existsSync(destination); - if (stat.isDirectory()) { - this.mkdirpSync(destination); - const directoryListing = baseFs.readdirSync(source); - for (const entry of directoryListing) { - this.copySync(this.pathUtils.join(destination, entry), baseFs.pathUtils.join(source, entry), { baseFs, overwrite }); - } - } else if (stat.isFile()) { - if (!exists || overwrite) { - if (exists) - this.removeSync(destination); - const content = baseFs.readFileSync(source); - this.writeFileSync(destination, content); - } - } else if (stat.isSymbolicLink()) { - if (!exists || overwrite) { - if (exists) - this.removeSync(destination); - const target = baseFs.readlinkSync(source); - this.symlinkSync(convertPath(this.pathUtils, target), destination); - } - } else { - throw new Error(`Unsupported file type (file: ${source}, mode: 0o${stat.mode.toString(8).padStart(6, `0`)})`); - } - const mode = stat.mode & 511; - this.chmodSync(destination, mode); - } - async changeFilePromise(p, content, opts = {}) { - if (Buffer.isBuffer(content)) { - return this.changeFileBufferPromise(p, content, opts); - } else { - return this.changeFileTextPromise(p, content, opts); - } - } - async changeFileBufferPromise(p, content, { mode } = {}) { - let current = Buffer.alloc(0); - try { - current = await this.readFilePromise(p); - } catch { - } - if (Buffer.compare(current, content) === 0) - return; - await this.writeFilePromise(p, content, { mode }); - } - async changeFileTextPromise(p, content, { automaticNewlines, mode } = {}) { - let current = ``; - try { - current = await this.readFilePromise(p, `utf8`); - } catch { - } - const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; - if (current === normalizedContent) - return; - await this.writeFilePromise(p, normalizedContent, { mode }); - } - changeFileSync(p, content, opts = {}) { - if (Buffer.isBuffer(content)) { - return this.changeFileBufferSync(p, content, opts); - } else { - return this.changeFileTextSync(p, content, opts); - } - } - changeFileBufferSync(p, content, { mode } = {}) { - let current = Buffer.alloc(0); - try { - current = this.readFileSync(p); - } catch { - } - if (Buffer.compare(current, content) === 0) - return; - this.writeFileSync(p, content, { mode }); - } - changeFileTextSync(p, content, { automaticNewlines = false, mode } = {}) { - let current = ``; - try { - current = this.readFileSync(p, `utf8`); - } catch { - } - const normalizedContent = automaticNewlines ? normalizeLineEndings(current, content) : content; - if (current === normalizedContent) - return; - this.writeFileSync(p, normalizedContent, { mode }); - } - async movePromise(fromP, toP) { - try { - await this.renamePromise(fromP, toP); - } catch (error) { - if (error.code === `EXDEV`) { - await this.copyPromise(toP, fromP); - await this.removePromise(fromP); - } else { - throw error; - } - } - } - moveSync(fromP, toP) { - try { - this.renameSync(fromP, toP); - } catch (error) { - if (error.code === `EXDEV`) { - this.copySync(toP, fromP); - this.removeSync(fromP); - } else { - throw error; - } - } - } - async lockPromise(affectedPath, callback) { - const lockPath = `${affectedPath}.flock`; - const interval = 1e3 / 60; - const startTime = Date.now(); - let fd = null; - const isAlive = async () => { - let pid; - try { - [pid] = await this.readJsonPromise(lockPath); - } catch { - return Date.now() - startTime < 500; - } - try { - process.kill(pid, 0); - return true; - } catch { - return false; - } - }; - while (fd === null) { - try { - fd = await this.openPromise(lockPath, `wx`); - } catch (error) { - if (error.code === `EEXIST`) { - if (!await isAlive()) { - try { - await this.unlinkPromise(lockPath); - continue; - } catch { - } - } - if (Date.now() - startTime < 60 * 1e3) { - await new Promise((resolve) => setTimeout(resolve, interval)); - } else { - throw new Error(`Couldn't acquire a lock in a reasonable time (via ${lockPath})`); - } - } else { - throw error; - } - } - } - await this.writePromise(fd, JSON.stringify([process.pid])); - try { - return await callback(); - } finally { - try { - await this.closePromise(fd); - await this.unlinkPromise(lockPath); - } catch { - } - } - } - async readJsonPromise(p) { - const content = await this.readFilePromise(p, `utf8`); - try { - return JSON.parse(content); - } catch (error) { - error.message += ` (in ${p})`; - throw error; - } - } - readJsonSync(p) { - const content = this.readFileSync(p, `utf8`); - try { - return JSON.parse(content); - } catch (error) { - error.message += ` (in ${p})`; - throw error; - } - } - async writeJsonPromise(p, data, { compact = false } = {}) { - const space = compact ? 0 : 2; - return await this.writeFilePromise(p, `${JSON.stringify(data, null, space)} -`); - } - writeJsonSync(p, data, { compact = false } = {}) { - const space = compact ? 0 : 2; - return this.writeFileSync(p, `${JSON.stringify(data, null, space)} -`); - } - async preserveTimePromise(p, cb) { - const stat = await this.lstatPromise(p); - const result = await cb(); - if (typeof result !== `undefined`) - p = result; - await this.lutimesPromise(p, stat.atime, stat.mtime); - } - async preserveTimeSync(p, cb) { - const stat = this.lstatSync(p); - const result = cb(); - if (typeof result !== `undefined`) - p = result; - this.lutimesSync(p, stat.atime, stat.mtime); - } -} -class BasePortableFakeFS extends FakeFS { - constructor() { - super(ppath); - } -} -function getEndOfLine(content) { - const matches = content.match(/\r?\n/g); - if (matches === null) - return EOL; - const crlf = matches.filter((nl) => nl === `\r -`).length; - const lf = matches.length - crlf; - return crlf > lf ? `\r -` : ` -`; -} -function normalizeLineEndings(originalContent, newContent) { - return newContent.replace(/\r?\n/g, getEndOfLine(originalContent)); -} - -class ProxiedFS extends FakeFS { - getExtractHint(hints) { - return this.baseFs.getExtractHint(hints); - } - resolve(path) { - return this.mapFromBase(this.baseFs.resolve(this.mapToBase(path))); - } - getRealPath() { - return this.mapFromBase(this.baseFs.getRealPath()); - } - async openPromise(p, flags, mode) { - return this.baseFs.openPromise(this.mapToBase(p), flags, mode); - } - openSync(p, flags, mode) { - return this.baseFs.openSync(this.mapToBase(p), flags, mode); - } - async opendirPromise(p, opts) { - return Object.assign(await this.baseFs.opendirPromise(this.mapToBase(p), opts), { path: p }); - } - opendirSync(p, opts) { - return Object.assign(this.baseFs.opendirSync(this.mapToBase(p), opts), { path: p }); - } - async readPromise(fd, buffer, offset, length, position) { - return await this.baseFs.readPromise(fd, buffer, offset, length, position); - } - readSync(fd, buffer, offset, length, position) { - return this.baseFs.readSync(fd, buffer, offset, length, position); - } - async writePromise(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return await this.baseFs.writePromise(fd, buffer, offset); - } else { - return await this.baseFs.writePromise(fd, buffer, offset, length, position); - } - } - writeSync(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return this.baseFs.writeSync(fd, buffer, offset); - } else { - return this.baseFs.writeSync(fd, buffer, offset, length, position); - } - } - async closePromise(fd) { - return this.baseFs.closePromise(fd); - } - closeSync(fd) { - this.baseFs.closeSync(fd); - } - createReadStream(p, opts) { - return this.baseFs.createReadStream(p !== null ? this.mapToBase(p) : p, opts); - } - createWriteStream(p, opts) { - return this.baseFs.createWriteStream(p !== null ? this.mapToBase(p) : p, opts); - } - async realpathPromise(p) { - return this.mapFromBase(await this.baseFs.realpathPromise(this.mapToBase(p))); - } - realpathSync(p) { - return this.mapFromBase(this.baseFs.realpathSync(this.mapToBase(p))); - } - async existsPromise(p) { - return this.baseFs.existsPromise(this.mapToBase(p)); - } - existsSync(p) { - return this.baseFs.existsSync(this.mapToBase(p)); - } - accessSync(p, mode) { - return this.baseFs.accessSync(this.mapToBase(p), mode); - } - async accessPromise(p, mode) { - return this.baseFs.accessPromise(this.mapToBase(p), mode); - } - async statPromise(p, opts) { - return this.baseFs.statPromise(this.mapToBase(p), opts); - } - statSync(p, opts) { - return this.baseFs.statSync(this.mapToBase(p), opts); - } - async fstatPromise(fd, opts) { - return this.baseFs.fstatPromise(fd, opts); - } - fstatSync(fd, opts) { - return this.baseFs.fstatSync(fd, opts); - } - lstatPromise(p, opts) { - return this.baseFs.lstatPromise(this.mapToBase(p), opts); - } - lstatSync(p, opts) { - return this.baseFs.lstatSync(this.mapToBase(p), opts); - } - async fchmodPromise(fd, mask) { - return this.baseFs.fchmodPromise(fd, mask); - } - fchmodSync(fd, mask) { - return this.baseFs.fchmodSync(fd, mask); - } - async chmodPromise(p, mask) { - return this.baseFs.chmodPromise(this.mapToBase(p), mask); - } - chmodSync(p, mask) { - return this.baseFs.chmodSync(this.mapToBase(p), mask); - } - async fchownPromise(fd, uid, gid) { - return this.baseFs.fchownPromise(fd, uid, gid); - } - fchownSync(fd, uid, gid) { - return this.baseFs.fchownSync(fd, uid, gid); - } - async chownPromise(p, uid, gid) { - return this.baseFs.chownPromise(this.mapToBase(p), uid, gid); - } - chownSync(p, uid, gid) { - return this.baseFs.chownSync(this.mapToBase(p), uid, gid); - } - async renamePromise(oldP, newP) { - return this.baseFs.renamePromise(this.mapToBase(oldP), this.mapToBase(newP)); - } - renameSync(oldP, newP) { - return this.baseFs.renameSync(this.mapToBase(oldP), this.mapToBase(newP)); - } - async copyFilePromise(sourceP, destP, flags = 0) { - return this.baseFs.copyFilePromise(this.mapToBase(sourceP), this.mapToBase(destP), flags); - } - copyFileSync(sourceP, destP, flags = 0) { - return this.baseFs.copyFileSync(this.mapToBase(sourceP), this.mapToBase(destP), flags); - } - async appendFilePromise(p, content, opts) { - return this.baseFs.appendFilePromise(this.fsMapToBase(p), content, opts); - } - appendFileSync(p, content, opts) { - return this.baseFs.appendFileSync(this.fsMapToBase(p), content, opts); - } - async writeFilePromise(p, content, opts) { - return this.baseFs.writeFilePromise(this.fsMapToBase(p), content, opts); - } - writeFileSync(p, content, opts) { - return this.baseFs.writeFileSync(this.fsMapToBase(p), content, opts); - } - async unlinkPromise(p) { - return this.baseFs.unlinkPromise(this.mapToBase(p)); - } - unlinkSync(p) { - return this.baseFs.unlinkSync(this.mapToBase(p)); - } - async utimesPromise(p, atime, mtime) { - return this.baseFs.utimesPromise(this.mapToBase(p), atime, mtime); - } - utimesSync(p, atime, mtime) { - return this.baseFs.utimesSync(this.mapToBase(p), atime, mtime); - } - async lutimesPromise(p, atime, mtime) { - return this.baseFs.lutimesPromise(this.mapToBase(p), atime, mtime); - } - lutimesSync(p, atime, mtime) { - return this.baseFs.lutimesSync(this.mapToBase(p), atime, mtime); - } - async mkdirPromise(p, opts) { - return this.baseFs.mkdirPromise(this.mapToBase(p), opts); - } - mkdirSync(p, opts) { - return this.baseFs.mkdirSync(this.mapToBase(p), opts); - } - async rmdirPromise(p, opts) { - return this.baseFs.rmdirPromise(this.mapToBase(p), opts); - } - rmdirSync(p, opts) { - return this.baseFs.rmdirSync(this.mapToBase(p), opts); - } - async rmPromise(p, opts) { - return this.baseFs.rmPromise(this.mapToBase(p), opts); - } - rmSync(p, opts) { - return this.baseFs.rmSync(this.mapToBase(p), opts); - } - async linkPromise(existingP, newP) { - return this.baseFs.linkPromise(this.mapToBase(existingP), this.mapToBase(newP)); - } - linkSync(existingP, newP) { - return this.baseFs.linkSync(this.mapToBase(existingP), this.mapToBase(newP)); - } - async symlinkPromise(target, p, type) { - const mappedP = this.mapToBase(p); - if (this.pathUtils.isAbsolute(target)) - return this.baseFs.symlinkPromise(this.mapToBase(target), mappedP, type); - const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); - const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); - return this.baseFs.symlinkPromise(mappedTarget, mappedP, type); - } - symlinkSync(target, p, type) { - const mappedP = this.mapToBase(p); - if (this.pathUtils.isAbsolute(target)) - return this.baseFs.symlinkSync(this.mapToBase(target), mappedP, type); - const mappedAbsoluteTarget = this.mapToBase(this.pathUtils.join(this.pathUtils.dirname(p), target)); - const mappedTarget = this.baseFs.pathUtils.relative(this.baseFs.pathUtils.dirname(mappedP), mappedAbsoluteTarget); - return this.baseFs.symlinkSync(mappedTarget, mappedP, type); - } - async readFilePromise(p, encoding) { - return this.baseFs.readFilePromise(this.fsMapToBase(p), encoding); - } - readFileSync(p, encoding) { - return this.baseFs.readFileSync(this.fsMapToBase(p), encoding); - } - readdirPromise(p, opts) { - return this.baseFs.readdirPromise(this.mapToBase(p), opts); - } - readdirSync(p, opts) { - return this.baseFs.readdirSync(this.mapToBase(p), opts); - } - async readlinkPromise(p) { - return this.mapFromBase(await this.baseFs.readlinkPromise(this.mapToBase(p))); - } - readlinkSync(p) { - return this.mapFromBase(this.baseFs.readlinkSync(this.mapToBase(p))); - } - async truncatePromise(p, len) { - return this.baseFs.truncatePromise(this.mapToBase(p), len); - } - truncateSync(p, len) { - return this.baseFs.truncateSync(this.mapToBase(p), len); - } - async ftruncatePromise(fd, len) { - return this.baseFs.ftruncatePromise(fd, len); - } - ftruncateSync(fd, len) { - return this.baseFs.ftruncateSync(fd, len); - } - watch(p, a, b) { - return this.baseFs.watch( - this.mapToBase(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - watchFile(p, a, b) { - return this.baseFs.watchFile( - this.mapToBase(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - unwatchFile(p, cb) { - return this.baseFs.unwatchFile(this.mapToBase(p), cb); - } - fsMapToBase(p) { - if (typeof p === `number`) { - return p; - } else { - return this.mapToBase(p); - } - } -} - -function direntToPortable(dirent) { - const portableDirent = dirent; - if (typeof dirent.path === `string`) - portableDirent.path = npath.toPortablePath(dirent.path); - return portableDirent; -} -class NodeFS extends BasePortableFakeFS { - realFs; - constructor(realFs = fs) { - super(); - this.realFs = realFs; - } - getExtractHint() { - return false; - } - getRealPath() { - return PortablePath.root; - } - resolve(p) { - return ppath.resolve(p); - } - async openPromise(p, flags, mode) { - return await new Promise((resolve, reject) => { - this.realFs.open(npath.fromPortablePath(p), flags, mode, this.makeCallback(resolve, reject)); - }); - } - openSync(p, flags, mode) { - return this.realFs.openSync(npath.fromPortablePath(p), flags, mode); - } - async opendirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (typeof opts !== `undefined`) { - this.realFs.opendir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.opendir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }).then((dir) => { - const dirWithFixedPath = dir; - Object.defineProperty(dirWithFixedPath, `path`, { - value: p, - configurable: true, - writable: true - }); - return dirWithFixedPath; - }); - } - opendirSync(p, opts) { - const dir = typeof opts !== `undefined` ? this.realFs.opendirSync(npath.fromPortablePath(p), opts) : this.realFs.opendirSync(npath.fromPortablePath(p)); - const dirWithFixedPath = dir; - Object.defineProperty(dirWithFixedPath, `path`, { - value: p, - configurable: true, - writable: true - }); - return dirWithFixedPath; - } - async readPromise(fd, buffer, offset = 0, length = 0, position = -1) { - return await new Promise((resolve, reject) => { - this.realFs.read(fd, buffer, offset, length, position, (error, bytesRead) => { - if (error) { - reject(error); - } else { - resolve(bytesRead); - } - }); - }); - } - readSync(fd, buffer, offset, length, position) { - return this.realFs.readSync(fd, buffer, offset, length, position); - } - async writePromise(fd, buffer, offset, length, position) { - return await new Promise((resolve, reject) => { - if (typeof buffer === `string`) { - return this.realFs.write(fd, buffer, offset, this.makeCallback(resolve, reject)); - } else { - return this.realFs.write(fd, buffer, offset, length, position, this.makeCallback(resolve, reject)); - } - }); - } - writeSync(fd, buffer, offset, length, position) { - if (typeof buffer === `string`) { - return this.realFs.writeSync(fd, buffer, offset); - } else { - return this.realFs.writeSync(fd, buffer, offset, length, position); - } - } - async closePromise(fd) { - await new Promise((resolve, reject) => { - this.realFs.close(fd, this.makeCallback(resolve, reject)); - }); - } - closeSync(fd) { - this.realFs.closeSync(fd); - } - createReadStream(p, opts) { - const realPath = p !== null ? npath.fromPortablePath(p) : p; - return this.realFs.createReadStream(realPath, opts); - } - createWriteStream(p, opts) { - const realPath = p !== null ? npath.fromPortablePath(p) : p; - return this.realFs.createWriteStream(realPath, opts); - } - async realpathPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.realpath(npath.fromPortablePath(p), {}, this.makeCallback(resolve, reject)); - }).then((path) => { - return npath.toPortablePath(path); - }); - } - realpathSync(p) { - return npath.toPortablePath(this.realFs.realpathSync(npath.fromPortablePath(p), {})); - } - async existsPromise(p) { - return await new Promise((resolve) => { - this.realFs.exists(npath.fromPortablePath(p), resolve); - }); - } - accessSync(p, mode) { - return this.realFs.accessSync(npath.fromPortablePath(p), mode); - } - async accessPromise(p, mode) { - return await new Promise((resolve, reject) => { - this.realFs.access(npath.fromPortablePath(p), mode, this.makeCallback(resolve, reject)); - }); - } - existsSync(p) { - return this.realFs.existsSync(npath.fromPortablePath(p)); - } - async statPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.stat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.stat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - statSync(p, opts) { - if (opts) { - return this.realFs.statSync(npath.fromPortablePath(p), opts); - } else { - return this.realFs.statSync(npath.fromPortablePath(p)); - } - } - async fstatPromise(fd, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.fstat(fd, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.fstat(fd, this.makeCallback(resolve, reject)); - } - }); - } - fstatSync(fd, opts) { - if (opts) { - return this.realFs.fstatSync(fd, opts); - } else { - return this.realFs.fstatSync(fd); - } - } - async lstatPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.lstat(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.lstat(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - lstatSync(p, opts) { - if (opts) { - return this.realFs.lstatSync(npath.fromPortablePath(p), opts); - } else { - return this.realFs.lstatSync(npath.fromPortablePath(p)); - } - } - async fchmodPromise(fd, mask) { - return await new Promise((resolve, reject) => { - this.realFs.fchmod(fd, mask, this.makeCallback(resolve, reject)); - }); - } - fchmodSync(fd, mask) { - return this.realFs.fchmodSync(fd, mask); - } - async chmodPromise(p, mask) { - return await new Promise((resolve, reject) => { - this.realFs.chmod(npath.fromPortablePath(p), mask, this.makeCallback(resolve, reject)); - }); - } - chmodSync(p, mask) { - return this.realFs.chmodSync(npath.fromPortablePath(p), mask); - } - async fchownPromise(fd, uid, gid) { - return await new Promise((resolve, reject) => { - this.realFs.fchown(fd, uid, gid, this.makeCallback(resolve, reject)); - }); - } - fchownSync(fd, uid, gid) { - return this.realFs.fchownSync(fd, uid, gid); - } - async chownPromise(p, uid, gid) { - return await new Promise((resolve, reject) => { - this.realFs.chown(npath.fromPortablePath(p), uid, gid, this.makeCallback(resolve, reject)); - }); - } - chownSync(p, uid, gid) { - return this.realFs.chownSync(npath.fromPortablePath(p), uid, gid); - } - async renamePromise(oldP, newP) { - return await new Promise((resolve, reject) => { - this.realFs.rename(npath.fromPortablePath(oldP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); - }); - } - renameSync(oldP, newP) { - return this.realFs.renameSync(npath.fromPortablePath(oldP), npath.fromPortablePath(newP)); - } - async copyFilePromise(sourceP, destP, flags = 0) { - return await new Promise((resolve, reject) => { - this.realFs.copyFile(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags, this.makeCallback(resolve, reject)); - }); - } - copyFileSync(sourceP, destP, flags = 0) { - return this.realFs.copyFileSync(npath.fromPortablePath(sourceP), npath.fromPortablePath(destP), flags); - } - async appendFilePromise(p, content, opts) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.appendFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.appendFile(fsNativePath, content, this.makeCallback(resolve, reject)); - } - }); - } - appendFileSync(p, content, opts) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.appendFileSync(fsNativePath, content, opts); - } else { - this.realFs.appendFileSync(fsNativePath, content); - } - } - async writeFilePromise(p, content, opts) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.writeFile(fsNativePath, content, opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.writeFile(fsNativePath, content, this.makeCallback(resolve, reject)); - } - }); - } - writeFileSync(p, content, opts) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - if (opts) { - this.realFs.writeFileSync(fsNativePath, content, opts); - } else { - this.realFs.writeFileSync(fsNativePath, content); - } - } - async unlinkPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.unlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - }); - } - unlinkSync(p) { - return this.realFs.unlinkSync(npath.fromPortablePath(p)); - } - async utimesPromise(p, atime, mtime) { - return await new Promise((resolve, reject) => { - this.realFs.utimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); - }); - } - utimesSync(p, atime, mtime) { - this.realFs.utimesSync(npath.fromPortablePath(p), atime, mtime); - } - async lutimesPromise(p, atime, mtime) { - return await new Promise((resolve, reject) => { - this.realFs.lutimes(npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject)); - }); - } - lutimesSync(p, atime, mtime) { - this.realFs.lutimesSync(npath.fromPortablePath(p), atime, mtime); - } - async mkdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - this.realFs.mkdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - }); - } - mkdirSync(p, opts) { - return this.realFs.mkdirSync(npath.fromPortablePath(p), opts); - } - async rmdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.rmdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.rmdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - rmdirSync(p, opts) { - return this.realFs.rmdirSync(npath.fromPortablePath(p), opts); - } - async rmPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - this.realFs.rm(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } else { - this.realFs.rm(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - rmSync(p, opts) { - return this.realFs.rmSync(npath.fromPortablePath(p), opts); - } - async linkPromise(existingP, newP) { - return await new Promise((resolve, reject) => { - this.realFs.link(npath.fromPortablePath(existingP), npath.fromPortablePath(newP), this.makeCallback(resolve, reject)); - }); - } - linkSync(existingP, newP) { - return this.realFs.linkSync(npath.fromPortablePath(existingP), npath.fromPortablePath(newP)); - } - async symlinkPromise(target, p, type) { - return await new Promise((resolve, reject) => { - this.realFs.symlink(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type, this.makeCallback(resolve, reject)); - }); - } - symlinkSync(target, p, type) { - return this.realFs.symlinkSync(npath.fromPortablePath(target.replace(/\/+$/, ``)), npath.fromPortablePath(p), type); - } - async readFilePromise(p, encoding) { - return await new Promise((resolve, reject) => { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - this.realFs.readFile(fsNativePath, encoding, this.makeCallback(resolve, reject)); - }); - } - readFileSync(p, encoding) { - const fsNativePath = typeof p === `string` ? npath.fromPortablePath(p) : p; - return this.realFs.readFileSync(fsNativePath, encoding); - } - async readdirPromise(p, opts) { - return await new Promise((resolve, reject) => { - if (opts) { - if (opts.recursive && process.platform === `win32`) { - if (opts.withFileTypes) { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback((results) => resolve(results.map(direntToPortable)), reject)); - } else { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback((results) => resolve(results.map(npath.toPortablePath)), reject)); - } - } else { - this.realFs.readdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject)); - } - } else { - this.realFs.readdir(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - } - }); - } - readdirSync(p, opts) { - if (opts) { - if (opts.recursive && process.platform === `win32`) { - if (opts.withFileTypes) { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts).map(direntToPortable); - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts).map(npath.toPortablePath); - } - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p), opts); - } - } else { - return this.realFs.readdirSync(npath.fromPortablePath(p)); - } - } - async readlinkPromise(p) { - return await new Promise((resolve, reject) => { - this.realFs.readlink(npath.fromPortablePath(p), this.makeCallback(resolve, reject)); - }).then((path) => { - return npath.toPortablePath(path); - }); - } - readlinkSync(p) { - return npath.toPortablePath(this.realFs.readlinkSync(npath.fromPortablePath(p))); - } - async truncatePromise(p, len) { - return await new Promise((resolve, reject) => { - this.realFs.truncate(npath.fromPortablePath(p), len, this.makeCallback(resolve, reject)); - }); - } - truncateSync(p, len) { - return this.realFs.truncateSync(npath.fromPortablePath(p), len); - } - async ftruncatePromise(fd, len) { - return await new Promise((resolve, reject) => { - this.realFs.ftruncate(fd, len, this.makeCallback(resolve, reject)); - }); - } - ftruncateSync(fd, len) { - return this.realFs.ftruncateSync(fd, len); - } - watch(p, a, b) { - return this.realFs.watch( - npath.fromPortablePath(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - watchFile(p, a, b) { - return this.realFs.watchFile( - npath.fromPortablePath(p), - // @ts-expect-error - reason TBS - a, - b - ); - } - unwatchFile(p, cb) { - return this.realFs.unwatchFile(npath.fromPortablePath(p), cb); - } - makeCallback(resolve, reject) { - return (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }; - } -} - -const NUMBER_REGEXP = /^[0-9]+$/; -const VIRTUAL_REGEXP = /^(\/(?:[^/]+\/)*?(?:\$\$virtual|__virtual__))((?:\/((?:[^/]+-)?[a-f0-9]+)(?:\/([^/]+))?)?((?:\/.*)?))$/; -const VALID_COMPONENT = /^([^/]+-)?[a-f0-9]+$/; -class VirtualFS extends ProxiedFS { - baseFs; - static makeVirtualPath(base, component, to) { - if (ppath.basename(base) !== `__virtual__`) - throw new Error(`Assertion failed: Virtual folders must be named "__virtual__"`); - if (!ppath.basename(component).match(VALID_COMPONENT)) - throw new Error(`Assertion failed: Virtual components must be ended by an hexadecimal hash`); - const target = ppath.relative(ppath.dirname(base), to); - const segments = target.split(`/`); - let depth = 0; - while (depth < segments.length && segments[depth] === `..`) - depth += 1; - const finalSegments = segments.slice(depth); - const fullVirtualPath = ppath.join(base, component, String(depth), ...finalSegments); - return fullVirtualPath; - } - static resolveVirtual(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match || !match[3] && match[5]) - return p; - const target = ppath.dirname(match[1]); - if (!match[3] || !match[4]) - return target; - const isnum = NUMBER_REGEXP.test(match[4]); - if (!isnum) - return p; - const depth = Number(match[4]); - const backstep = `../`.repeat(depth); - const subpath = match[5] || `.`; - return VirtualFS.resolveVirtual(ppath.join(target, backstep, subpath)); - } - constructor({ baseFs = new NodeFS() } = {}) { - super(ppath); - this.baseFs = baseFs; - } - getExtractHint(hints) { - return this.baseFs.getExtractHint(hints); - } - getRealPath() { - return this.baseFs.getRealPath(); - } - realpathSync(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match) - return this.baseFs.realpathSync(p); - if (!match[5]) - return p; - const realpath = this.baseFs.realpathSync(this.mapToBase(p)); - return VirtualFS.makeVirtualPath(match[1], match[3], realpath); - } - async realpathPromise(p) { - const match = p.match(VIRTUAL_REGEXP); - if (!match) - return await this.baseFs.realpathPromise(p); - if (!match[5]) - return p; - const realpath = await this.baseFs.realpathPromise(this.mapToBase(p)); - return VirtualFS.makeVirtualPath(match[1], match[3], realpath); - } - mapToBase(p) { - if (p === ``) - return p; - if (this.pathUtils.isAbsolute(p)) - return VirtualFS.resolveVirtual(p); - const resolvedRoot = VirtualFS.resolveVirtual(this.baseFs.resolve(PortablePath.dot)); - const resolvedP = VirtualFS.resolveVirtual(this.baseFs.resolve(p)); - return ppath.relative(resolvedRoot, resolvedP) || PortablePath.dot; - } - mapFromBase(p) { - return p; - } -} - -const URL = Number(process.versions.node.split('.', 1)[0]) < 20 ? URL$1 : globalThis.URL; - -const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10)); -const WATCH_MODE_MESSAGE_USES_ARRAYS = major > 19 || major === 19 && minor >= 2 || major === 18 && minor >= 13; -const HAS_LAZY_LOADED_TRANSLATORS = major === 20 && minor < 6 || major === 19 && minor >= 3; -const SUPPORTS_IMPORT_ATTRIBUTES = major >= 21 || major === 20 && minor >= 10 || major === 18 && minor >= 20; -const SUPPORTS_IMPORT_ATTRIBUTES_ONLY = major >= 22; - -function readPackageScope(checkPath) { - const rootSeparatorIndex = checkPath.indexOf(npath.sep); - let separatorIndex; - do { - separatorIndex = checkPath.lastIndexOf(npath.sep); - checkPath = checkPath.slice(0, separatorIndex); - if (checkPath.endsWith(`${npath.sep}node_modules`)) - return false; - const pjson = readPackage(checkPath + npath.sep); - if (pjson) { - return { - data: pjson, - path: checkPath - }; - } - } while (separatorIndex > rootSeparatorIndex); - return false; -} -function readPackage(requestPath) { - const jsonPath = npath.resolve(requestPath, `package.json`); - if (!fs.existsSync(jsonPath)) - return null; - return JSON.parse(fs.readFileSync(jsonPath, `utf8`)); -} - -async function tryReadFile$1(path2) { - try { - return await fs.promises.readFile(path2, `utf8`); - } catch (error) { - if (error.code === `ENOENT`) - return null; - throw error; - } -} -function tryParseURL(str, base) { - try { - return new URL(str, base); - } catch { - return null; - } -} -let entrypointPath = null; -function setEntrypointPath(file) { - entrypointPath = file; -} -function getFileFormat(filepath) { - const ext = path.extname(filepath); - switch (ext) { - case `.mjs`: { - return `module`; - } - case `.cjs`: { - return `commonjs`; - } - case `.wasm`: { - throw new Error( - `Unknown file extension ".wasm" for ${filepath}` - ); - } - case `.json`: { - return `json`; - } - case `.js`: { - const pkg = readPackageScope(filepath); - if (!pkg) - return `commonjs`; - return pkg.data.type ?? `commonjs`; - } - default: { - if (entrypointPath !== filepath) - return null; - const pkg = readPackageScope(filepath); - if (!pkg) - return `commonjs`; - if (pkg.data.type === `module`) - return null; - return pkg.data.type ?? `commonjs`; - } - } -} - -async function load$1(urlString, context, nextLoad) { - const url = tryParseURL(urlString); - if (url?.protocol !== `file:`) - return nextLoad(urlString, context, nextLoad); - const filePath = fileURLToPath(url); - const format = getFileFormat(filePath); - if (!format) - return nextLoad(urlString, context, nextLoad); - if (format === `json`) { - if (SUPPORTS_IMPORT_ATTRIBUTES_ONLY) { - if (context.importAttributes?.type !== `json`) { - const err = new TypeError(`[ERR_IMPORT_ATTRIBUTE_MISSING]: Module "${urlString}" needs an import attribute of "type: json"`); - err.code = `ERR_IMPORT_ATTRIBUTE_MISSING`; - throw err; - } - } else { - const type = `importAttributes` in context ? context.importAttributes?.type : context.importAssertions?.type; - if (type !== `json`) { - const err = new TypeError(`[ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "${urlString}" needs an import ${SUPPORTS_IMPORT_ATTRIBUTES ? `attribute` : `assertion`} of type "json"`); - err.code = `ERR_IMPORT_ASSERTION_TYPE_MISSING`; - throw err; - } - } - } - if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) { - const pathToSend = pathToFileURL( - npath.fromPortablePath( - VirtualFS.resolveVirtual(npath.toPortablePath(filePath)) - ) - ).href; - process.send({ - "watch:import": WATCH_MODE_MESSAGE_USES_ARRAYS ? [pathToSend] : pathToSend - }); - } - return { - format, - source: format === `commonjs` ? void 0 : await fs.promises.readFile(filePath, `utf8`), - shortCircuit: true - }; -} - -const ArrayIsArray = Array.isArray; -const JSONStringify = JSON.stringify; -const ObjectGetOwnPropertyNames = Object.getOwnPropertyNames; -const ObjectPrototypeHasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); -const RegExpPrototypeExec = (obj, string) => RegExp.prototype.exec.call(obj, string); -const RegExpPrototypeSymbolReplace = (obj, ...rest) => RegExp.prototype[Symbol.replace].apply(obj, rest); -const StringPrototypeEndsWith = (str, ...rest) => String.prototype.endsWith.apply(str, rest); -const StringPrototypeIncludes = (str, ...rest) => String.prototype.includes.apply(str, rest); -const StringPrototypeLastIndexOf = (str, ...rest) => String.prototype.lastIndexOf.apply(str, rest); -const StringPrototypeIndexOf = (str, ...rest) => String.prototype.indexOf.apply(str, rest); -const StringPrototypeReplace = (str, ...rest) => String.prototype.replace.apply(str, rest); -const StringPrototypeSlice = (str, ...rest) => String.prototype.slice.apply(str, rest); -const StringPrototypeStartsWith = (str, ...rest) => String.prototype.startsWith.apply(str, rest); -const SafeMap = Map; -const JSONParse = JSON.parse; - -function createErrorType(code, messageCreator, errorType) { - return class extends errorType { - constructor(...args) { - super(messageCreator(...args)); - this.code = code; - this.name = `${errorType.name} [${code}]`; - } - }; -} -const ERR_PACKAGE_IMPORT_NOT_DEFINED = createErrorType( - `ERR_PACKAGE_IMPORT_NOT_DEFINED`, - (specifier, packagePath, base) => { - return `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath}package.json` : ``} imported from ${base}`; - }, - TypeError -); -const ERR_INVALID_MODULE_SPECIFIER = createErrorType( - `ERR_INVALID_MODULE_SPECIFIER`, - (request, reason, base = void 0) => { - return `Invalid module "${request}" ${reason}${base ? ` imported from ${base}` : ``}`; - }, - TypeError -); -const ERR_INVALID_PACKAGE_TARGET = createErrorType( - `ERR_INVALID_PACKAGE_TARGET`, - (pkgPath, key, target, isImport = false, base = void 0) => { - const relError = typeof target === `string` && !isImport && target.length && !StringPrototypeStartsWith(target, `./`); - if (key === `.`) { - assert(isImport === false); - return `Invalid "exports" main target ${JSONStringify(target)} defined in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; - } - return `Invalid "${isImport ? `imports` : `exports`}" target ${JSONStringify( - target - )} defined for '${key}' in the package config ${pkgPath}package.json${base ? ` imported from ${base}` : ``}${relError ? `; targets must start with "./"` : ``}`; - }, - Error -); -const ERR_INVALID_PACKAGE_CONFIG = createErrorType( - `ERR_INVALID_PACKAGE_CONFIG`, - (path, base, message) => { - return `Invalid package config ${path}${base ? ` while importing ${base}` : ``}${message ? `. ${message}` : ``}`; - }, - Error -); - -function filterOwnProperties(source, keys) { - const filtered = /* @__PURE__ */ Object.create(null); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (ObjectPrototypeHasOwnProperty(source, key)) { - filtered[key] = source[key]; - } - } - return filtered; -} - -const packageJSONCache = new SafeMap(); -function getPackageConfig(path, specifier, base, readFileSyncFn) { - const existing = packageJSONCache.get(path); - if (existing !== void 0) { - return existing; - } - const source = readFileSyncFn(path); - if (source === void 0) { - const packageConfig2 = { - pjsonPath: path, - exists: false, - main: void 0, - name: void 0, - type: "none", - exports: void 0, - imports: void 0 - }; - packageJSONCache.set(path, packageConfig2); - return packageConfig2; - } - let packageJSON; - try { - packageJSON = JSONParse(source); - } catch (error) { - throw new ERR_INVALID_PACKAGE_CONFIG( - path, - (base ? `"${specifier}" from ` : "") + fileURLToPath(base || specifier), - error.message - ); - } - let { imports, main, name, type } = filterOwnProperties(packageJSON, [ - "imports", - "main", - "name", - "type" - ]); - const exports = ObjectPrototypeHasOwnProperty(packageJSON, "exports") ? packageJSON.exports : void 0; - if (typeof imports !== "object" || imports === null) { - imports = void 0; - } - if (typeof main !== "string") { - main = void 0; - } - if (typeof name !== "string") { - name = void 0; - } - if (type !== "module" && type !== "commonjs") { - type = "none"; - } - const packageConfig = { - pjsonPath: path, - exists: true, - main, - name, - type, - exports, - imports - }; - packageJSONCache.set(path, packageConfig); - return packageConfig; -} -function getPackageScopeConfig(resolved, readFileSyncFn) { - let packageJSONUrl = new URL("./package.json", resolved); - while (true) { - const packageJSONPath2 = packageJSONUrl.pathname; - if (StringPrototypeEndsWith(packageJSONPath2, "node_modules/package.json")) { - break; - } - const packageConfig2 = getPackageConfig( - fileURLToPath(packageJSONUrl), - resolved, - void 0, - readFileSyncFn - ); - if (packageConfig2.exists) { - return packageConfig2; - } - const lastPackageJSONUrl = packageJSONUrl; - packageJSONUrl = new URL("../package.json", packageJSONUrl); - if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) { - break; - } - } - const packageJSONPath = fileURLToPath(packageJSONUrl); - const packageConfig = { - pjsonPath: packageJSONPath, - exists: false, - main: void 0, - name: void 0, - type: "none", - exports: void 0, - imports: void 0 - }; - packageJSONCache.set(packageJSONPath, packageConfig); - return packageConfig; -} - -function throwImportNotDefined(specifier, packageJSONUrl, base) { - throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( - specifier, - packageJSONUrl && fileURLToPath(new URL(".", packageJSONUrl)), - fileURLToPath(base) - ); -} -function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) { - const reason = `request is not a valid subpath for the "${internal ? "imports" : "exports"}" resolution of ${fileURLToPath(packageJSONUrl)}`; - throw new ERR_INVALID_MODULE_SPECIFIER( - subpath, - reason, - base && fileURLToPath(base) - ); -} -function throwInvalidPackageTarget(subpath, target, packageJSONUrl, internal, base) { - if (typeof target === "object" && target !== null) { - target = JSONStringify(target, null, ""); - } else { - target = `${target}`; - } - throw new ERR_INVALID_PACKAGE_TARGET( - fileURLToPath(new URL(".", packageJSONUrl)), - subpath, - target, - internal, - base && fileURLToPath(base) - ); -} -const invalidSegmentRegEx = /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i; -const patternRegEx = /\*/g; -function resolvePackageTargetString(target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) { - if (subpath !== "" && !pattern && target[target.length - 1] !== "/") - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - if (!StringPrototypeStartsWith(target, "./")) { - if (internal && !StringPrototypeStartsWith(target, "../") && !StringPrototypeStartsWith(target, "/")) { - let isURL = false; - try { - new URL(target); - isURL = true; - } catch { - } - if (!isURL) { - const exportTarget = pattern ? RegExpPrototypeSymbolReplace(patternRegEx, target, () => subpath) : target + subpath; - return exportTarget; - } - } - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - } - if (RegExpPrototypeExec( - invalidSegmentRegEx, - StringPrototypeSlice(target, 2) - ) !== null) - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - const resolved = new URL(target, packageJSONUrl); - const resolvedPath = resolved.pathname; - const packagePath = new URL(".", packageJSONUrl).pathname; - if (!StringPrototypeStartsWith(resolvedPath, packagePath)) - throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); - if (subpath === "") return resolved; - if (RegExpPrototypeExec(invalidSegmentRegEx, subpath) !== null) { - const request = pattern ? StringPrototypeReplace(match, "*", () => subpath) : match + subpath; - throwInvalidSubpath(request, packageJSONUrl, internal, base); - } - if (pattern) { - return new URL( - RegExpPrototypeSymbolReplace(patternRegEx, resolved.href, () => subpath) - ); - } - return new URL(subpath, resolved); -} -function isArrayIndex(key) { - const keyNum = +key; - if (`${keyNum}` !== key) return false; - return keyNum >= 0 && keyNum < 4294967295; -} -function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath, base, pattern, internal, conditions) { - if (typeof target === "string") { - return resolvePackageTargetString( - target, - subpath, - packageSubpath, - packageJSONUrl, - base, - pattern, - internal); - } else if (ArrayIsArray(target)) { - if (target.length === 0) { - return null; - } - let lastException; - for (let i = 0; i < target.length; i++) { - const targetItem = target[i]; - let resolveResult; - try { - resolveResult = resolvePackageTarget( - packageJSONUrl, - targetItem, - subpath, - packageSubpath, - base, - pattern, - internal, - conditions - ); - } catch (e) { - lastException = e; - if (e.code === "ERR_INVALID_PACKAGE_TARGET") { - continue; - } - throw e; - } - if (resolveResult === void 0) { - continue; - } - if (resolveResult === null) { - lastException = null; - continue; - } - return resolveResult; - } - if (lastException === void 0 || lastException === null) - return lastException; - throw lastException; - } else if (typeof target === "object" && target !== null) { - const keys = ObjectGetOwnPropertyNames(target); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (isArrayIndex(key)) { - throw new ERR_INVALID_PACKAGE_CONFIG( - fileURLToPath(packageJSONUrl), - base, - '"exports" cannot contain numeric property keys.' - ); - } - } - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - if (key === "default" || conditions.has(key)) { - const conditionalTarget = target[key]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - conditionalTarget, - subpath, - packageSubpath, - base, - pattern, - internal, - conditions - ); - if (resolveResult === void 0) continue; - return resolveResult; - } - } - return void 0; - } else if (target === null) { - return null; - } - throwInvalidPackageTarget( - packageSubpath, - target, - packageJSONUrl, - internal, - base - ); -} -function patternKeyCompare(a, b) { - const aPatternIndex = StringPrototypeIndexOf(a, "*"); - const bPatternIndex = StringPrototypeIndexOf(b, "*"); - const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; - const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; - if (baseLenA > baseLenB) return -1; - if (baseLenB > baseLenA) return 1; - if (aPatternIndex === -1) return 1; - if (bPatternIndex === -1) return -1; - if (a.length > b.length) return -1; - if (b.length > a.length) return 1; - return 0; -} -function packageImportsResolve({ name, base, conditions, readFileSyncFn }) { - if (name === "#" || StringPrototypeStartsWith(name, "#/") || StringPrototypeEndsWith(name, "/")) { - const reason = "is not a valid internal imports specifier name"; - throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); - } - let packageJSONUrl; - const packageConfig = getPackageScopeConfig(base, readFileSyncFn); - if (packageConfig.exists) { - packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); - const imports = packageConfig.imports; - if (imports) { - if (ObjectPrototypeHasOwnProperty(imports, name) && !StringPrototypeIncludes(name, "*")) { - const resolveResult = resolvePackageTarget( - packageJSONUrl, - imports[name], - "", - name, - base, - false, - true, - conditions - ); - if (resolveResult != null) { - return resolveResult; - } - } else { - let bestMatch = ""; - let bestMatchSubpath; - const keys = ObjectGetOwnPropertyNames(imports); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const patternIndex = StringPrototypeIndexOf(key, "*"); - if (patternIndex !== -1 && StringPrototypeStartsWith( - name, - StringPrototypeSlice(key, 0, patternIndex) - )) { - const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); - if (name.length >= key.length && StringPrototypeEndsWith(name, patternTrailer) && patternKeyCompare(bestMatch, key) === 1 && StringPrototypeLastIndexOf(key, "*") === patternIndex) { - bestMatch = key; - bestMatchSubpath = StringPrototypeSlice( - name, - patternIndex, - name.length - patternTrailer.length - ); - } - } - } - if (bestMatch) { - const target = imports[bestMatch]; - const resolveResult = resolvePackageTarget( - packageJSONUrl, - target, - bestMatchSubpath, - bestMatch, - base, - true, - true, - conditions - ); - if (resolveResult != null) { - return resolveResult; - } - } - } - } - } - throwImportNotDefined(name, packageJSONUrl, base); -} - -let findPnpApi = esmModule.findPnpApi; -if (!findPnpApi) { - const require = createRequire(import.meta.url); - const pnpApi = require(structuredClone(`./.pnp.cjs`)); - pnpApi.setup(); - findPnpApi = esmModule.findPnpApi; -} -const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/; -const isRelativeRegexp = /^\.{0,2}\//; -function tryReadFile(filePath) { - try { - return fs.readFileSync(filePath, `utf8`); - } catch (err) { - if (err.code === `ENOENT`) - return void 0; - throw err; - } -} -async function resolvePrivateRequest(specifier, issuer, context, nextResolve) { - const resolved = packageImportsResolve({ - name: specifier, - base: pathToFileURL(issuer), - conditions: new Set(context.conditions), - readFileSyncFn: tryReadFile - }); - if (resolved instanceof URL) { - return { url: resolved.href, shortCircuit: true }; - } else { - if (resolved.startsWith(`#`)) - throw new Error(`Mapping from one private import to another isn't allowed`); - return resolve$1(resolved, context, nextResolve); - } -} -async function resolve$1(originalSpecifier, context, nextResolve) { - if (!findPnpApi || isBuiltin(originalSpecifier)) - return nextResolve(originalSpecifier, context, nextResolve); - let specifier = originalSpecifier; - const url = tryParseURL(specifier, isRelativeRegexp.test(specifier) ? context.parentURL : void 0); - if (url) { - if (url.protocol !== `file:`) - return nextResolve(originalSpecifier, context, nextResolve); - specifier = fileURLToPath(url); - } - const { parentURL, conditions = [] } = context; - const issuer = parentURL && tryParseURL(parentURL)?.protocol === `file:` ? fileURLToPath(parentURL) : process.cwd(); - const pnpapi = findPnpApi(issuer) ?? (url ? findPnpApi(specifier) : null); - if (!pnpapi) - return nextResolve(originalSpecifier, context, nextResolve); - if (specifier.startsWith(`#`)) - return resolvePrivateRequest(specifier, issuer, context, nextResolve); - const dependencyNameMatch = specifier.match(pathRegExp); - let allowLegacyResolve = false; - if (dependencyNameMatch) { - const [, dependencyName, subPath] = dependencyNameMatch; - if (subPath === `` && dependencyName !== `pnpapi`) { - const resolved = pnpapi.resolveToUnqualified(`${dependencyName}/package.json`, issuer); - if (resolved) { - const content = await tryReadFile$1(resolved); - if (content) { - const pkg = JSON.parse(content); - allowLegacyResolve = pkg.exports == null; - } - } - } - } - let result; - try { - result = pnpapi.resolveRequest(specifier, issuer, { - conditions: new Set(conditions), - // TODO: Handle --experimental-specifier-resolution=node - extensions: allowLegacyResolve ? void 0 : [] - }); - } catch (err) { - if (err instanceof Error && `code` in err && err.code === `MODULE_NOT_FOUND`) - err.code = `ERR_MODULE_NOT_FOUND`; - throw err; - } - if (!result) - throw new Error(`Resolving '${specifier}' from '${issuer}' failed`); - const resultURL = pathToFileURL(result); - if (url) { - resultURL.search = url.search; - resultURL.hash = url.hash; - } - if (!parentURL) - setEntrypointPath(fileURLToPath(resultURL)); - return { - url: resultURL.href, - shortCircuit: true - }; -} - -if (!HAS_LAZY_LOADED_TRANSLATORS) { - const binding = process.binding(`fs`); - const originalReadFile = binding.readFileUtf8 || binding.readFileSync; - if (originalReadFile) { - binding[originalReadFile.name] = function(...args) { - try { - return fs.readFileSync(args[0], { - encoding: `utf8`, - // @ts-expect-error - The docs says it needs to be a string but - // links to https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#file-system-flags - // which says it can be a number which matches the implementation. - flag: args[1] - }); - } catch { - } - return originalReadFile.apply(this, args); - }; - } else { - const binding2 = process.binding(`fs`); - const originalfstat = binding2.fstat; - const ZIP_MASK = 4278190080; - const ZIP_MAGIC = 704643072; - binding2.fstat = function(...args) { - const [fd, useBigint, req] = args; - if ((fd & ZIP_MASK) === ZIP_MAGIC && useBigint === false && req === void 0) { - try { - const stats = fs.fstatSync(fd); - return new Float64Array([ - stats.dev, - stats.mode, - stats.nlink, - stats.uid, - stats.gid, - stats.rdev, - stats.blksize, - stats.ino, - stats.size, - stats.blocks - // atime sec - // atime ns - // mtime sec - // mtime ns - // ctime sec - // ctime ns - // birthtime sec - // birthtime ns - ]); - } catch { - } - } - return originalfstat.apply(this, args); - }; - } -} - -const resolve = resolve$1; -const load = load$1; - -export { load, resolve }; diff --git a/.yarnrc.yml b/.yarnrc.yml index 09b1012..3186f3f 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1 @@ -nodeLinker: pnp +nodeLinker: node-modules diff --git a/artifacts/edn-renderer-genui-final.png b/artifacts/edn-renderer-genui-final.png deleted file mode 100644 index a381cfdeda4a1bc6a593cde90c9bb4d3f204525f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 283243 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'(6}7}4X|vV((b7g#smZPm=RLN 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�QPU7+^osJd&w_CA@wv09X3F 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 Mkud{{`|CgXKa3Ozz5oCK diff --git a/calcit.cirru b/calcit.cirru index 45f9861..1c5d18b 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -90,9 +90,14 @@ let store $ :store reel states $ :states store - relay $ or (:relay store) ({}) - renderer $ or (:renderer store) ({}) + relay $ or (:relay store) {} + renderer $ or (:renderer store) {} drawer-open? $ get-in states ([] :drawer :data :show?) + selected-channel $ :selected-channel relay + channels $ if + list? $ :channels relay + :channels relay + [] drawer-plugin $ use-drawer (>> states :drawer) {} :style $ {} (:width 420) (:min-width 0) (:max-width "|calc(100vw - 20px)") (:padding "|14px 14px 18px") (:gap 14) (:background-color |#fffaf4) (:border-left "|1px solid #ead8c7") (:box-shadow "|-10px 0 30px hsla(24, 35%, 18%, 0.12)") (:overflow |auto) @@ -121,7 +126,7 @@ div {} $ :style {} (:font-size 13) (:line-height |1.6) (:color |#7b6451) - <> "|Inspect relay status, latest request ids, and the exact Cirru EDN payload being rendered." + <> "|Inspect relay status, request ids, available channels, and the exact Cirru EDN payload being rendered." button $ {} (:class-name css/button) (:inner-text |Close) :style $ {} (:padding "|8px 12px") (:align-self |flex-start) :on-click $ fn (e d!) (on-close d!) @@ -133,17 +138,16 @@ {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) <> $ str "|Relay: " or (:status relay) |idle - div - {} $ :style - {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) - <> $ str "|Layout: " - or (:layout-id renderer) |waiting if-let request-id $ :last-request renderer div {} $ :style {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) <> $ str "|Request: " request-id + div + {} $ :style + {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) + <> $ str "|Channels: " (count channels) div {} $ :style {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 18) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") @@ -151,7 +155,25 @@ {} $ :style {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) <> |Relay - div ({}) (<> "|Listening on relay channel genui.") + div ({}) + if (some? selected-channel) + <> $ str "|Selected channel: " selected-channel + <> "|No channel selected yet." + if + > (count channels) 0 + list-> + {} $ :style + {} (:display |flex) (:gap 8) (:flex-wrap |wrap) + -> channels .to-list $ map-indexed + fn (idx channel) + [] idx $ div + {} $ :style + {} (:padding "|6px 10px") (:border-radius 999) + :background-color $ if (= channel selected-channel) |#f3d7ba |#fff + :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" + :font-size 12 + :color |#6b4a32 + <> channel if-let client-id $ :client-id relay div ({}) @@ -183,7 +205,7 @@ :style $ {} (:width |100%) (:min-height |280px) (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #dcc8b6") (:background-color |#fff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) when dev? $ comp-reel (>> states :reel) reel ({}) help-alert $ use-alert (>> states :help) - {} $ :text "|Use the drawer to inspect relay status, request ids, and incoming Cirru EDN without reserving a permanent sidebar." + {} $ :text "|Use the drawer to inspect relay status, request ids, channel state, and incoming Cirru EDN without reserving a permanent sidebar." div {} $ :style {} (:min-height |100vh) (:padding 20) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) @@ -200,7 +222,9 @@ div {} $ :style {} (:padding "|4px 10px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#8b6244) - <> "|Preview-first workspace" + if (some? selected-channel) + <> $ str "|Channel: " selected-channel + <> "|Choose channel" div {} $ :style {} (:display |flex) (:gap 8) (:flex-wrap |wrap) (:align-items |center) (:justify-content |flex-end) @@ -212,9 +236,10 @@ div {} $ :style {} (:padding "|6px 10px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#6b4a32) - <> $ if + if some? $ :layout-id renderer - , "|Layout ready" "|Layout waiting" + <> "|Layout ready" + <> "|Layout waiting" button $ {} (:class-name css/button) (:inner-text "|Open drawer") :style $ {} (:padding "|8px 12px") :on-click $ fn (e d!) (.show drawer-plugin d!) @@ -233,6 +258,27 @@ {} $ :style {} (:padding "|12px 14px") (:border-radius 16) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 12) <> $ str "|Validation error: " render-error + if + > (count channels) 1 + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 12) (:padding 18) (:border-radius 20) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:margin-top 12) + div + {} $ :style + {} (:font-size 15) (:font-weight |600) (:color |#6b4528) + <> "|Available channels" + list-> + {} $ :style + {} (:display |flex) (:gap 10) (:flex-wrap |wrap) + -> channels .to-list $ map-indexed + fn (idx channel) + [] idx $ button + {} (:class-name css/button) (:inner-text channel) + :style $ {} (:padding "|8px 12px") + :background-color $ if (= channel selected-channel) |#e8b488 |#fff + :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" + :on-click $ fn (e d!) + d! $ :: :select-channel channel div {} $ :style {} (:display |flex) (:flex-direction |column) (:gap 16) (:padding 22) (:border-radius 24) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") (:min-height |420px) (:margin-top 12) @@ -240,13 +286,21 @@ {} $ :style {} (:font-size 20) (:font-weight |600) <> "|Rendered Preview" - if-let - layout $ :layout renderer - comp-layout-node layout + if (nil? selected-channel) div {} $ :style {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) - <> "|Waiting for a validated genui layout from the relay." + if + > (count channels) 1 + <> "|Select a channel to start receiving validated layouts." + <> "|Waiting for an active relay channel. Open this page with `?channel=` and send payloads with `edn-relay send --channel ''`." + if-let + layout $ :layout renderer + comp-layout-node layout + div + {} $ :style + {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) + <> "|Waiting for a validated payload from the relay." .render drawer-plugin .render help-alert :examples $ [] @@ -598,7 +652,10 @@ :examples $ [] |build-status-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn build-status-payload () $ {} (:status |ok) (:kind |status) (:renderer |edn-renderer) (:title current-page-title) (:page_url current-page-url) (:commands relay-commands) + defn build-status-payload (relay) + {} (:status |ok) (:kind |status) (:renderer |edn-renderer) (:title current-page-title) (:page_url current-page-url) (:commands relay-commands) + :channel $ :selected-channel relay + :channels $ or (:channels relay) ([]) :examples $ [] |component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -645,6 +702,16 @@ :code $ quote def current-page-url $ .-href js/location :examples $ [] + |current-url-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn current-url-channel () $ let + params $ new js/URLSearchParams (.-search js/location) + value $ .!get params |channel + if + and (some? value) + > (count value) 0 + , value nil + :examples $ [] |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def dev? $ = |dev (get-env |mode |release) @@ -659,14 +726,14 @@ |protocol-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def protocol-docs $ [] - {} (:name |genui) (:summary "|`genui` 频道用于发送布局树,renderer 校验并渲染后回 ack。") - {} (:name |renderer) (:summary "|`renderer` 频道用于查询帮助文档、skill 和状态信息。") - {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,并订阅 `genui`/`renderer` 频道。") - {} (:name |ack) (:summary "|CLI 请求最终通过 `ack` 拿到 renderer 的处理结果。") + {} (:name |channel) (:summary "|每个 renderer 连接只订阅一个当前 channel;URL 上的 `?channel=` 可以直接指定或创建它。") + {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,服务端会返回 `hello-ok` 和当前活跃 channel 列表。") + {} (:name |channel-state) (:summary "|当活跃 channel 列表变化时,relay 会广播 `channel-state`。") + {} (:name |ack) (:summary "|同一个请求允许多个 receiver 收到事件,但 sender 只接受第一条 `ack`。") :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def relay-commands $ [] |genui |help |skill |status |current |open + def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote (def renderer-help-overview "|使用 `edn-relay help` 查询当前 renderer 支持的能力;可以用 `edn-relay help protocol` 看协议摘要,用 `edn-relay help examples` 看示例,用 `edn-relay help chart mermaid` 过滤组件帮助。") @@ -709,7 +776,7 @@ :examples $ [] |site $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) (:relay-channel |genui) (:relay-docs-channel |renderer) + def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) :examples $ [] |skill-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -736,7 +803,15 @@ when and config/dev? $ not= op :states js/console.log |Dispatch: op - reset! *reel $ reel-updater updater @*reel op + let + next-reel $ reel-updater updater @*reel op + reset! *reel next-reel + when + and (some? @*ws) + tag-match op + (:select-channel _) true + _ false + sync-selected-channel! @*ws :examples $ [] |ensure-relay! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -748,8 +823,8 @@ .!toISOString $ new js/Date reset! *ws ws .!addEventListener ws |open $ fn (event) - send-relay-frame! ws $ {} (:kind |hello) (:role |browser) (:client_id client-id) - :channels $ [] (:relay-channel config/site) (:relay-docs-channel config/site) + dispatch! $ :: :relay-connected client-id ([]) + sync-selected-channel! ws .!addEventListener ws |message $ fn (event) handle-relay-message! ws $ .-data event .!addEventListener ws |error $ fn (event) @@ -758,6 +833,20 @@ dispatch! $ :: :relay-status |closed "|Relay connection closed, retrying..." flipped js/setTimeout 2000 ensure-relay! :examples $ [] + |handle-channel-state! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn handle-channel-state! (ws client-id channels) + let + normalized $ if (list? channels) channels ([]) + if (some? client-id) + dispatch! $ :: :relay-connected client-id normalized + dispatch! $ :: :relay-channels normalized + when + and + nil? $ selected-relay-channel + = 1 $ count normalized + dispatch! $ :: :select-channel (first normalized) + :examples $ [] |handle-genui-event! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn handle-genui-event! (ws frame) @@ -778,20 +867,28 @@ let frame $ parse-cirru-edn raw kind $ :kind frame + payload $ :payload frame + op $ if (map? payload) (:op payload) nil + selected $ selected-relay-channel if (= kind |hello-ok) - dispatch! $ :: :relay-connected (:client_id frame) - if (= kind |event) - if - = (:channel frame) (:relay-channel config/site) - handle-genui-event! ws frame + handle-channel-state! ws (:client_id frame) (:channels frame) + if (= kind |channel-state) + handle-channel-state! ws nil $ :channels frame + if (= kind |event) if - = (:channel frame) (:relay-docs-channel config/site) - handle-renderer-event! ws frame + and (some? selected) + = (:channel frame) selected + if + includes? ([] |help |skill |status) op + handle-renderer-event! ws frame + handle-genui-event! ws frame do |ignored - if (= kind |error) - dispatch! $ :: :relay-status |error - or (:error frame) "|Relay error" - do |ignored + if (= kind |warning) + .!warn js/console $ or (:error frame) "|Relay warning" + if (= kind |error) + dispatch! $ :: :relay-status |error + or (:error frame) "|Relay error" + do |ignored :examples $ [] |handle-renderer-event! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -805,6 +902,7 @@ list? $ :topics payload :topics payload [] + relay $ get-in @*reel ([] :store :relay) do (.!debug js/console "|[renderer] request" payload) if (= op |help) let @@ -813,7 +911,7 @@ if (= op |skill) send-genui-ack! ws request-id true (config/build-skill-payload) nil if (= op |status) - send-genui-ack! ws request-id true (config/build-status-payload) nil + send-genui-ack! ws request-id true (config/build-status-payload relay) nil send-genui-ack! ws request-id false nil $ str "|Unsupported renderer op: " op :examples $ [] |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -832,6 +930,9 @@ raw $ js/localStorage.getItem (:storage-key config/site) when (some? raw) dispatch! $ :: :hydrate-storage (parse-cirru-edn raw) + if-let + url-channel $ config/current-url-channel + dispatch! $ :: :select-channel url-channel ensure-relay! println "|App started." :examples $ [] @@ -860,6 +961,10 @@ :code $ quote defn render-app! () $ render! mount-target (comp-container @*reel) dispatch! :examples $ [] + |selected-relay-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn selected-relay-channel () $ get-in @*reel ([] :store :relay :selected-channel) + :examples $ [] |send-genui-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn send-genui-ack! (ws request-id ok? payload error-message) @@ -870,6 +975,15 @@ defn send-relay-frame! (ws frame) .!send ws $ format-cirru-edn frame :examples $ [] + |sync-selected-channel! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn sync-selected-channel! (ws) + let + client-id $ get-in @*reel ([] :store :relay :client-id) + selected $ selected-relay-channel + channels $ if (some? selected) ([] selected) ([]) + send-relay-frame! ws $ {} (:kind |hello) (:role |receiver) (:client_id client-id) (:channels channels) + :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote ns app.main $ :require @@ -890,7 +1004,8 @@ def store $ {} :states $ {} :cursor $ [] - :relay $ {} (:status |idle) (:client-id nil) (:last-error nil) + :relay $ {} (:status |idle) (:client-id nil) (:last-error nil) (:selected-channel nil) + :channels $ [] :renderer $ {} (:layout nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) :examples $ [] :ns $ %{} :NsEntry (:doc |) @@ -903,11 +1018,22 @@ tag-match op (:states cursor s) (update-states store cursor s) (:hydrate-storage data) data - (:relay-connected client-id) + (:relay-connected client-id channels) -> store assoc-in ([] :relay :status) |ready assoc-in ([] :relay :client-id) client-id + assoc-in ([] :relay :channels) channels assoc-in ([] :relay :last-error) nil + (:relay-channels channels) + assoc-in store ([] :relay :channels) channels + (:select-channel channel) + -> store + assoc-in ([] :relay :selected-channel) channel + assoc-in ([] :renderer :layout) nil + assoc-in ([] :renderer :layout-id) nil + assoc-in ([] :renderer :layout-source) | + assoc-in ([] :renderer :last-request) nil + assoc-in ([] :renderer :last-error) nil (:relay-status status message) -> store assoc-in ([] :relay :status) status From b5b14f0ca84bfda794862bf02523796bf1611242 Mon Sep 17 00:00:00 2001 From: tiye Date: Wed, 27 May 2026 15:59:56 +0800 Subject: [PATCH 06/26] update docs and UI hints --- Agents.md | 4 +- COMPONENTS.md | 2 +- README.md | 26 +++++++--- SKILL.md | 141 +++++++++++++++++++++++--------------------------- calcit.cirru | 26 +++++++--- 5 files changed, 108 insertions(+), 91 deletions(-) diff --git a/Agents.md b/Agents.md index bd0c699..8d74f33 100644 --- a/Agents.md +++ b/Agents.md @@ -76,10 +76,10 @@ relay 联调命令(端口 9100,系统 9001/9002 被 macOS 占用): ```bash edn-relay serve --bind 127.0.0.1:9100 -edn-relay genui --server ws://127.0.0.1:9100 "$(cat /tmp/layout.cirru)" --timeout-secs 10 +edn-relay send --server ws://127.0.0.1:9100 --channel genui "$(cat /tmp/layout.cirru)" --timeout-secs 10 ``` -genui 布局文件用 2 空格缩进(不要用 tab),示例: +布局文件用 2 空格缩进(不要用 tab),示例: ``` {} diff --git a/COMPONENTS.md b/COMPONENTS.md index 7737ceb..5f5b8d4 100644 --- a/COMPONENTS.md +++ b/COMPONENTS.md @@ -9,7 +9,7 @@ ``` CLI / 脚本 - │ edn-relay genui --server ws://... '' + │ edn-relay send --channel genui '' ▼ edn-relay (relay) │ WebSocket 消息路由 (genui 频道) diff --git a/README.md b/README.md index c517d90..6a27535 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ `edn-renderer` is the browser frontend for the `edn-relay` workflow. -It keeps a websocket connection to the relay, listens on the `genui` channel, -stores the incoming Cirru EDN layout DSL, renders the layout, and sends an ack -back to the CLI. +It keeps a websocket connection to the relay, subscribes to a chosen channel +such as `genui`, stores the incoming Cirru EDN layout DSL, renders the layout, +and sends an ack back to the CLI. The sidebar is collapsed by default so more horizontal space is reserved for the rendered result. @@ -15,9 +15,9 @@ The current implementation already supports the full loop: 1. start `edn-relay serve` 2. open `edn-renderer` in a browser -3. run `edn-relay genui ` from the CLI +3. run `edn-relay send --channel genui ` from the CLI 4. render the DSL in the page -5. confirm success with a returned layout id +5. confirm success with a returned ack payload and layout id Reference screenshot: @@ -33,6 +33,12 @@ cr js yarn vite --host 127.0.0.1 --port 3010 ``` +Published bootstrap page: + +- `https://r.tiye.me/Erigeron/edn-renderer/` +- supports `?channel=` to preselect a channel +- supports `?server=` or `?port=` to override the relay websocket target + If Vite fails because `rolldown` native bindings are missing, run `yarn install` again so the unplugged package is materialized on disk. @@ -50,6 +56,12 @@ Open the page in a browser. In parallel, run the relay: edn-relay serve ``` +If you want the published bootstrap page instead of a local dev server, run: + +```bash +edn-relay open-published --channel genui +``` + Then send a layout DSL from the CLI: ```bash @@ -68,12 +80,12 @@ LAYOUT=$(cat <<'EOF' EOF ) -edn-relay genui --server ws://127.0.0.1:9001 "$LAYOUT" +edn-relay send --channel genui "$LAYOUT" ``` Expected result: -- CLI prints `genui ok ` +- CLI prints an `ack` frame whose payload contains `:status |ok` and `:layout_id` - the page shows the layout id and request id - the renderer preview updates immediately diff --git a/SKILL.md b/SKILL.md index 56ee5d5..a00ccea 100644 --- a/SKILL.md +++ b/SKILL.md @@ -24,61 +24,52 @@ 这份 skill 采用下面这些约定,后续其他项目也建议遵守: -### 2.1 relay 一次只服务一个当前目标 +### 2.1 relay CLI 默认无状态 -不再要求命令行里反复指定: +当前约定是: -- 是哪个服务 -- 连接哪个端口 -- websocket 地址是什么 - -使用层面的约定应该是: - -- 一次只启动一个当前 relay 服务 -- CLI 默认只连接这个当前 relay -- 业务命令默认都发给这个当前 relay +- relay 服务默认监听 `127.0.0.1:9100` +- 请求类命令默认连接 `ws://127.0.0.1:9100` +- 如果 relay 不在默认地址,命令上显式传 `--server ...` +- channel 始终由每次命令上的 `--channel ...` 指定 也就是说,使用时优先是这种形式: - `edn-relay serve` -- `edn-relay genui ''` -- `edn-relay help` -- `edn-relay skill` -- `edn-relay status` -- `edn-relay current` -- `edn-relay open` - -而不是每次都写: +- `edn-relay channels` +- `edn-relay send --channel genui ''` +- `edn-relay help --channel genui` +- `edn-relay skill --channel genui` +- `edn-relay status --channel genui` +- `edn-relay open --channel genui` -- `--server ...` -- `--bind ...` -- `ws://127.0.0.1:xxxx` +只有 relay 地址不是默认值时,才补充: -### 2.2 统一持久化状态 +- `--server ws://127.0.0.1:xxxx` +- 或 `serve --bind 127.0.0.1:xxxx` -relay 需要维护一份持久化状态,用来记住当前连接上下文。 +### 2.2 channel 显式指定,不维护当前上下文 -这类状态包括但不限于: +relay CLI 不维护“当前 renderer / 当前 channel / 最近一次上下文”这类持久化状态。 -- 当前 relay 连接信息 -- 当前目标 renderer 信息 -- 默认 channel -- 最近一次使用的上下文 -- 文档缓存 / skill 缓存 +用户体验应该依赖: -具体存储位置和实现方式属于 relay 内部实现细节,不应该要求 CLI 使用者记住。 +- 默认 relay 地址 +- 每次命令显式的 `--channel` +- `edn-relay channels` 查看当前有哪些 receiver 已建立 channel +- `edn-relay open-published --channel ...` 快速拉起一个浏览器 receiver ### 2.3 文档通过协议查询,不靠手工记忆 具体业务能力,不应该靠用户记命令细节,而应该通过: -- `edn-relay help` +- `edn-relay help --channel ` -让 relay 按协议向当前 renderer 查询文档,再决定怎么使用。 +让 relay 按协议向目标 channel 上的 renderer 查询文档,再决定怎么使用。 也就是说: -1. 先运行 `edn-relay help` +1. 先运行 `edn-relay help --channel ` 2. 由 relay 从 renderer 侧拉取可用文档 3. 再根据返回的文档去发 payload 或查询具体组件 @@ -94,12 +85,12 @@ relay 需要维护一份持久化状态,用来记住当前连接上下文。 约定上,renderer 应该在运行时把这份 skill 暴露给: -- `edn-relay skill` +- `edn-relay skill --channel ` 也就是说,后续的使用习惯应该是: -1. `edn-relay skill` -2. relay 从当前 renderer 拿到 skill 内容 +1. `edn-relay skill --channel ` +2. relay 从目标 channel 上的 renderer 拿到 skill 内容 3. CLI 侧展示或检索 skill 这样其他项目不必直接读取仓库文件,也能通过运行中的 renderer 获取最新 skill。 @@ -120,7 +111,7 @@ relay 需要维护一份持久化状态,用来记住当前连接上下文。 - 查询单个组件,比如 `chart` - 批量查询多个组件,比如 `chart`、`mermaid`、`markdown` -这样 `edn-relay help` 才能真正成为一个可检索接口,而不只是打印一段静态帮助。 +这样 `edn-relay help --channel ` 才能真正成为一个可检索接口,而不只是打印一段静态帮助。 --- @@ -150,7 +141,7 @@ yarn vite 这里不强调具体端口号,重点是: - renderer 页面能打开 -- renderer 能连接当前 relay +- renderer 能连接目标 relay - renderer 能通过协议提供文档、skill、组件说明和渲染能力 ### 3.3 打开浏览器页面 @@ -176,7 +167,7 @@ chrome-devtools list_console_messages 面向使用者时,更推荐展示这种形式: ```bash -edn-relay genui ' +edn-relay send --channel genui ' {} :type |card :text "|CLI Demo" @@ -189,21 +180,21 @@ edn-relay genui ' 重点是: -- 不再反复出现 `--server` -- 不再要求用户知道 websocket 地址 -- 当前 relay 目标由 relay 自己维护的持久化上下文决定 +- 默认情况下不需要反复出现 `--server` +- 不要求用户手动记 websocket 地址 +- channel 由每次命令上的 `--channel` 显式决定 ### 4.2 先问 help,再发业务 payload 推荐流程不是“先猜功能”,而是: -1. 运行 `edn-relay help` +1. 运行 `edn-relay help --channel ` 2. 从 renderer 返回的文档里看当前支持什么 -3. 再运行 `edn-relay genui ...` +3. 再运行 `edn-relay send --channel ...` 如果要查询 skill,则走: -1. 运行 `edn-relay skill` +1. 运行 `edn-relay skill --channel ` 2. 从 renderer 返回 skill 内容 3. 再根据 skill 决定怎么调试、怎么探索能力 @@ -217,28 +208,28 @@ edn-relay genui ' ## 5. 如何发现 renderer 当前支持哪些功能 -### 5.1 首选 `edn-relay help` +### 5.1 首选 `edn-relay help --channel ` 后续面向使用者的统一入口,应该是: ```bash -edn-relay help +edn-relay help --channel genui ``` 这条命令的职责应该是: -- 从当前 renderer 查询可用文档 +- 从目标 channel 上的 renderer 查询可用文档 - 列出可用能力 - 提示支持的组件和业务入口 - 给出下一步可执行的命令建议 已经适合直接使用的形式包括: -- `edn-relay help` -- `edn-relay help protocol` -- `edn-relay help examples` -- `edn-relay help chart mermaid` -- `edn-relay help card-demo` +- `edn-relay help --channel genui` +- `edn-relay help --channel genui protocol` +- `edn-relay help --channel genui examples` +- `edn-relay help --channel genui chart mermaid` +- `edn-relay help --channel genui card-demo` ### 5.2 组件级帮助应该可检索 @@ -261,9 +252,9 @@ edn-relay help ### 5.3 skill 作为更高层的操作指南 -`edn-relay help` 偏向能力目录。 +`edn-relay help --channel ` 偏向能力目录。 -`edn-relay skill` 偏向操作经验和工作流。 +`edn-relay skill --channel ` 偏向操作经验和工作流。 两者的分工建议是: @@ -274,7 +265,7 @@ edn-relay help ## 6. 在开发 renderer 本身时,仍然可以这样查实现 -虽然面向使用者的推荐入口是 `edn-relay help` / `edn-relay skill`,但开发 renderer 自己时,仍然可以直接查代码。 +虽然面向使用者的推荐入口是 `edn-relay help --channel ` / `edn-relay skill --channel `,但开发 renderer 自己时,仍然可以直接查代码。 推荐顺序: @@ -331,12 +322,12 @@ chrome-devtools take_screenshot --fullPage --filePath artifacts/debug.png 优先检查: - relay 是否已启动 -- relay 的持久化上下文是否已经正确写入 -- renderer 是否成功连接当前 relay +- `--server` 是否指向了正确的 relay +- renderer 是否成功连接目标 relay ### 8.2 文档 / skill 发现问题 -如果 `edn-relay help` 或 `edn-relay skill` 没有返回预期内容,优先怀疑: +如果 `edn-relay help --channel ` 或 `edn-relay skill --channel ` 没有返回预期内容,优先怀疑: - renderer 侧没有把文档通过协议暴露出去 - skill 没有被 renderer 在运行时正确暴露 @@ -366,18 +357,18 @@ chrome-devtools take_screenshot --fullPage --filePath artifacts/debug.png 如果别的项目想复用 `edn-renderer` 这套能力,建议直接沿用下面的接口习惯: -1. `edn-relay serve` 启动当前 relay -2. `edn-relay current` 查看当前 relay 上下文 -3. `edn-relay status` 查询当前 renderer 状态 -4. `edn-relay help` 查询当前 renderer 暴露的文档 -5. `edn-relay skill` 查询当前 renderer 暴露的操作技能 -6. `edn-relay genui ...` 发送实际 payload +1. `edn-relay serve` 启动 relay +2. `edn-relay channels` 查看当前有哪些 channel 已连上 receiver +3. `edn-relay status --channel ` 查询目标 renderer 状态 +4. `edn-relay help --channel ` 查询目标 renderer 暴露的文档 +5. `edn-relay skill --channel ` 查询目标 renderer 暴露的操作技能 +6. `edn-relay send --channel ...` 发送实际 payload 7. 浏览器里用 drawer 和 console 做细节验证 也就是说,别的项目最好复用的是: -- 单实例 relay 的交互模型 -- relay 的持久化上下文模型 +- 默认 relay 地址 + 可选 `--server` 的无状态模型 +- channel 显式指定的交互模型 - renderer 侧帮助文档 / skill 的协议暴露方式 - 组件文档的可检索暴露方式 @@ -388,13 +379,13 @@ chrome-devtools take_screenshot --fullPage --filePath artifacts/debug.png 为了方便长期复用,建议把下面这些能力稳定下来: - `edn-relay serve` -- `edn-relay current` -- `edn-relay status` -- `edn-relay open` -- `edn-relay help` -- `edn-relay skill` -- `edn-relay genui` -- relay 的持久化上下文机制 +- `edn-relay channels` +- `edn-relay status --channel ` +- `edn-relay open --channel ` +- `edn-relay help --channel ` +- `edn-relay skill --channel ` +- `edn-relay send --channel ` +- 默认 relay 地址 + 可选 `--server` 的连接约定 - renderer 侧的文档查询协议 - renderer 侧的 skill 查询协议 - 组件说明的可检索暴露结构 diff --git a/calcit.cirru b/calcit.cirru index 1c5d18b..89ae793 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -702,16 +702,29 @@ :code $ quote def current-page-url $ .-href js/location :examples $ [] - |current-url-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |current-url-param $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn current-url-channel () $ let + defn current-url-param (key) $ let params $ new js/URLSearchParams (.-search js/location) - value $ .!get params |channel + value $ .!get params key if and (some? value) > (count value) 0 , value nil :examples $ [] + |current-url-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn current-url-channel () $ current-url-param |channel + :examples $ [] + |current-relay-url $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn current-relay-url () $ let + server $ current-url-param |server + port $ current-url-param |port + or server $ if (some? port) + str |ws://127.0.0.1: port + :relay-url site + :examples $ [] |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def dev? $ = |dev (get-env |mode |release) @@ -726,14 +739,14 @@ |protocol-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def protocol-docs $ [] - {} (:name |channel) (:summary "|每个 renderer 连接只订阅一个当前 channel;URL 上的 `?channel=` 可以直接指定或创建它。") + {} (:name |channel) (:summary "|每个 renderer 连接只订阅一个当前 channel;URL 上的 `?channel=` 可以直接指定或创建它。发布版页面还支持 `?server=` 或 `?port=` 指向 relay。") {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,服务端会返回 `hello-ok` 和当前活跃 channel 列表。") {} (:name |channel-state) (:summary "|当活跃 channel 列表变化时,relay 会广播 `channel-state`。") {} (:name |ack) (:summary "|同一个请求允许多个 receiver 收到事件,但 sender 只接受第一条 `ack`。") :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def relay-commands $ [] |send |help |skill |status |open + def relay-commands $ [] |channels |open-published |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote (def renderer-help-overview "|使用 `edn-relay help` 查询当前 renderer 支持的能力;可以用 `edn-relay help protocol` 看协议摘要,用 `edn-relay help examples` 看示例,用 `edn-relay help chart mermaid` 过滤组件帮助。") @@ -818,7 +831,8 @@ defn ensure-relay! () $ when (nil? @*ws) dispatch! $ :: :relay-status |connecting nil let - ws $ new js/WebSocket (:relay-url config/site) + relay-url $ config/current-relay-url + ws $ new js/WebSocket relay-url client-id $ str |renderer- .!toISOString $ new js/Date reset! *ws ws From ebe3a4e426eae66debacd88a862121fb641fc00c Mon Sep 17 00:00:00 2001 From: tiye Date: Thu, 28 May 2026 02:22:59 +0800 Subject: [PATCH 07/26] try more tags in protocol; add MathML core support --- SKILL.md | 74 ++++++-- calcit.cirru | 463 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 401 insertions(+), 136 deletions(-) diff --git a/SKILL.md b/SKILL.md index a00ccea..8be104a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -57,7 +57,7 @@ relay CLI 不维护“当前 renderer / 当前 channel / 最近一次上下文 - 默认 relay 地址 - 每次命令显式的 `--channel` - `edn-relay channels` 查看当前有哪些 receiver 已建立 channel -- `edn-relay open-published --channel ...` 快速拉起一个浏览器 receiver +- `edn-relay open --channel ...` 在已连接的 renderer 页面之间快速打开目标 receiver ### 2.3 文档通过协议查询,不靠手工记忆 @@ -95,6 +95,13 @@ relay CLI 不维护“当前 renderer / 当前 channel / 最近一次上下文 这样其他项目不必直接读取仓库文件,也能通过运行中的 renderer 获取最新 skill。 +默认 `edn-relay skill --channel ` 更适合返回简短工作流总览;如果要某一类技能,再追加 topic,例如: + +- `edn-relay skill --channel genui workflow` +- `edn-relay skill --channel genui math` +- `edn-relay skill --channel genui validation` +- `edn-relay skill --channel genui full` + ### 2.5 组件说明要支持检索 组件文档不要只写成一长段不可检索的文本。 @@ -102,17 +109,30 @@ relay CLI 不维护“当前 renderer / 当前 channel / 最近一次上下文 更推荐的约定是: - 支持按名称查询 -- 支持批量列出组件 +- 支持显式列出全部组件 - 支持同时返回参数、说明、示例 例如概念上,应该能支持这类查询: -- 查询全部组件 -- 查询单个组件,比如 `chart` +- 查询全部组件,比如 `edn-relay help --channel genui components` +- 查询单个组件,比如 `chart`、`mermaid`、`math` - 批量查询多个组件,比如 `chart`、`mermaid`、`markdown` 这样 `edn-relay help --channel ` 才能真正成为一个可检索接口,而不只是打印一段静态帮助。 +### 2.6 渐进披露优先 + +默认帮助不应该一次返回所有组件细节和全部案例。 + +更推荐的顺序是: + +1. `edn-relay help --channel ` 先看总览 +2. `edn-relay help --channel components` 或 `math` 再看具体组件 +3. `edn-relay help --channel examples` 或 `math-fraction-demo` 再看案例 +4. 最后再运行 `edn-relay send --channel ...` + +这样可以避免 CLI 一上来就把全部配置细节、组件说明和案例一起打印出来。 + --- ## 3. 推荐使用方式 @@ -198,12 +218,43 @@ edn-relay send --channel genui ' 2. 从 renderer 返回 skill 内容 3. 再根据 skill 决定怎么调试、怎么探索能力 +如果只想看某一块技能,而不是整份操作手册,则优先: + +1. `edn-relay skill --channel workflow` +2. `edn-relay skill --channel math` +3. 只有确实需要完整文档时再用 `edn-relay skill --channel full` + 如果要查看当前 renderer 页面状态,则可以: 1. 运行 `edn-relay status` 2. 查看当前 renderer 标题、页面地址和可用命令 3. 需要直接打开页面时再运行 `edn-relay open` +### 4.3 MathML 也走同样流程 + +如果要试 MathML Core,推荐顺序仍然是: + +1. `edn-relay help --channel genui math` +2. `edn-relay help --channel genui math-fraction-demo` +3. 再发送最小公式 payload + +例如: + +```bash +edn-relay send --channel genui ' +{} + :type |math + :display |block + :expr $ [] |mfrac + [] |mrow + [] |mi |a + [] |mo |+ + [] |mi |b + [] |msqrt + [] |mi |c +' +``` + --- ## 5. 如何发现 renderer 当前支持哪些功能 @@ -219,17 +270,20 @@ edn-relay help --channel genui 这条命令的职责应该是: - 从目标 channel 上的 renderer 查询可用文档 -- 列出可用能力 +- 默认只列出可用能力和下一步建议 - 提示支持的组件和业务入口 - 给出下一步可执行的命令建议 已经适合直接使用的形式包括: - `edn-relay help --channel genui` +- `edn-relay help --channel genui components` - `edn-relay help --channel genui protocol` - `edn-relay help --channel genui examples` +- `edn-relay help --channel genui math` - `edn-relay help --channel genui chart mermaid` - `edn-relay help --channel genui card-demo` +- `edn-relay help --channel genui math-fraction-demo` ### 5.2 组件级帮助应该可检索 @@ -237,13 +291,13 @@ edn-relay help --channel genui 目前推荐按下面几类来查: -- 组件:例如 `chart`、`mermaid` +- 组件:例如 `chart`、`mermaid`、`math` - 协议:例如 `protocol` -- 示例:例如 `examples`、`card-demo` +- 示例:例如 `examples`、`card-demo`、`math-fraction-demo` 比如概念上应该支持: -- 列出全部组件 +- 显式列出全部组件 - 查询指定组件的说明 - 批量查询多个组件 - 返回示例 payload @@ -252,9 +306,9 @@ edn-relay help --channel genui ### 5.3 skill 作为更高层的操作指南 -`edn-relay help --channel ` 偏向能力目录。 +`edn-relay help --channel ` 偏向能力目录,并且应该默认保持简短。 -`edn-relay skill --channel ` 偏向操作经验和工作流。 +`edn-relay skill --channel ` 偏向操作经验和工作流,也应该默认保持简短;完整手册只在显式请求 `full` 时返回。 两者的分工建议是: diff --git a/calcit.cirru b/calcit.cirru index 89ae793..185ec12 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -15,7 +15,18 @@ :examples $ [] |LayoutNode $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def LayoutNode $ defenum LayoutNode (:column :list) (:row :list) (:card :dynamic :list) (:text :string) (:badge :string) (:divider) (:button :string) (:input :dynamic :dynamic :dynamic) (:markdown :string) (:mermaid :string) (:chart :dynamic :dynamic :list) + def LayoutNode $ defenum LayoutNode (:column :list) (:row :list) (:card :dynamic :list) (:text :string) (:badge :string) (:divider) (:button :string) (:input :dynamic :dynamic :dynamic) (:markdown :string) (:mermaid :string) (:chart :dynamic :dynamic :list) (:math :dynamic :dynamic) + :examples $ [] + |append-mathml-child! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn append-mathml-child! (el child path) + if (string? child) + .!appendChild el $ .!createTextNode js/document child + if (number? child) + .!appendChild el $ .!createTextNode js/document (str child) + if (list? child) + .!appendChild el $ build-mathml-element child path + raise $ str path "| invalid MathML child, expected string, number, or list" :examples $ [] |build-echarts-option $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -63,6 +74,30 @@ :series $ [] {} (:type |scatter) (:data values) :examples $ [] + |build-mathml-element $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-mathml-element (expr path) + let + tag-name $ first expr + children $ or (rest expr) ([]) + el $ .!createElementNS js/document mathml-namespace tag-name + do + foldl children nil $ fn (acc child) + do + append-mathml-child! el child $ str path |/ tag-name + , acc + , el + :examples $ [] + |build-mathml-root $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-mathml-root (expr display) + let + root $ .!createElementNS js/document mathml-namespace |math + do + .!setAttribute root |display $ math-display-value display + .!appendChild root $ build-mathml-element expr |math + , root + :examples $ [] |build-mermaid-render-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn build-mermaid-render-payload (text) @@ -206,103 +241,104 @@ when dev? $ comp-reel (>> states :reel) reel ({}) help-alert $ use-alert (>> states :help) {} $ :text "|Use the drawer to inspect relay status, request ids, channel state, and incoming Cirru EDN without reserving a permanent sidebar." - div - {} $ :style - {} (:min-height |100vh) (:padding 20) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) + [] (effect-page-title selected-channel) div {} $ :style - {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 12) (:padding "|12px 14px") (:border-radius 20) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:flex-wrap |wrap) + {} (:min-height |100vh) (:padding 16) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) div {} $ :style - {} (:display |flex) (:align-items |center) (:gap 10) (:flex-wrap |wrap) + {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 8) (:padding "|8px 10px") (:border-radius 16) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:flex-wrap |wrap) div {} $ :style - {} (:font-size 22) (:font-weight |700) (:line-height |1.1) - <> "|EDN Renderer" + {} (:display |flex) (:align-items |center) (:gap 8) (:flex-wrap |wrap) + div + {} $ :style + {} (:font-size 18) (:font-weight |700) (:line-height |1) + <> "|EDN Renderer" + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#8b6244) + if (some? selected-channel) + <> $ str "|Channel: " selected-channel + <> "|Choose channel" div {} $ :style - {} (:padding "|4px 10px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#8b6244) - if (some? selected-channel) - <> $ str "|Channel: " selected-channel - <> "|Choose channel" - div - {} $ :style - {} (:display |flex) (:gap 8) (:flex-wrap |wrap) (:align-items |center) (:justify-content |flex-end) + {} (:display |flex) (:gap 6) (:flex-wrap |wrap) (:align-items |center) (:justify-content |flex-end) + div + {} $ :style + {} (:padding "|4px 8px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#6b4a32) + <> $ str "|Relay: " + or (:status relay) |idle + div + {} $ :style + {} (:padding "|4px 8px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#6b4a32) + if + some? $ :layout-id renderer + <> |Ready + <> |Waiting + button $ {} (:class-name css/button) (:inner-text |Drawer) + :style $ {} (:padding "|6px 10px") + :on-click $ fn (e d!) (.show drawer-plugin d!) + button $ {} (:class-name css/button) (:inner-text |Tips) + :style $ {} (:padding "|6px 10px") + :on-click $ fn (e d!) (.show help-alert d!) + if-let + relay-error $ :last-error relay div {} $ :style - {} (:padding "|6px 10px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#6b4a32) - <> $ str "|Relay: " - or (:status relay) |idle + {} (:padding "|10px 12px") (:border-radius 14) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 10) + <> $ str "|Relay error: " relay-error + if-let + render-error $ :last-error renderer div {} $ :style - {} (:padding "|6px 10px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 12) (:color |#6b4a32) - if - some? $ :layout-id renderer - <> "|Layout ready" - <> "|Layout waiting" - button $ {} (:class-name css/button) (:inner-text "|Open drawer") - :style $ {} (:padding "|8px 12px") - :on-click $ fn (e d!) (.show drawer-plugin d!) - button $ {} (:class-name css/button) (:inner-text |Tips) - :style $ {} (:padding "|8px 12px") - :on-click $ fn (e d!) (.show help-alert d!) - if-let - relay-error $ :last-error relay - div - {} $ :style - {} (:padding "|12px 14px") (:border-radius 16) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 12) - <> $ str "|Relay error: " relay-error - if-let - render-error $ :last-error renderer - div - {} $ :style - {} (:padding "|12px 14px") (:border-radius 16) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 12) - <> $ str "|Validation error: " render-error - if - > (count channels) 1 - div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 12) (:padding 18) (:border-radius 20) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:margin-top 12) + {} (:padding "|10px 12px") (:border-radius 14) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 10) + <> $ str "|Validation error: " render-error + if + > (count channels) 1 div {} $ :style - {} (:font-size 15) (:font-weight |600) (:color |#6b4528) - <> "|Available channels" - list-> - {} $ :style - {} (:display |flex) (:gap 10) (:flex-wrap |wrap) - -> channels .to-list $ map-indexed - fn (idx channel) - [] idx $ button - {} (:class-name css/button) (:inner-text channel) - :style $ {} (:padding "|8px 12px") - :background-color $ if (= channel selected-channel) |#e8b488 |#fff - :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" - :on-click $ fn (e d!) - d! $ :: :select-channel channel - div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 16) (:padding 22) (:border-radius 24) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") (:min-height |420px) (:margin-top 12) + {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 16) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:margin-top 10) + div + {} $ :style + {} (:font-size 13) (:font-weight |600) (:color |#6b4528) + <> "|Available channels" + list-> + {} $ :style + {} (:display |flex) (:gap 8) (:flex-wrap |wrap) + -> channels .to-list $ map-indexed + fn (idx channel) + [] idx $ button + {} (:class-name css/button) (:inner-text channel) + :style $ {} (:padding "|6px 10px") + :background-color $ if (= channel selected-channel) |#e8b488 |#fff + :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" + :on-click $ fn (e d!) + d! $ :: :select-channel channel div {} $ :style - {} (:font-size 20) (:font-weight |600) - <> "|Rendered Preview" - if (nil? selected-channel) + {} (:display |flex) (:flex-direction |column) (:gap 14) (:padding 18) (:border-radius 20) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") (:min-height |420px) (:margin-top 10) div {} $ :style - {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) - if - > (count channels) 1 - <> "|Select a channel to start receiving validated layouts." - <> "|Waiting for an active relay channel. Open this page with `?channel=` and send payloads with `edn-relay send --channel ''`." - if-let - layout $ :layout renderer - comp-layout-node layout + {} (:font-size 18) (:font-weight |600) + <> "|Rendered Preview" + if (nil? selected-channel) div {} $ :style - {} (:padding 32) (:border-radius 18) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 15) (:line-height |1.7) (:color |#8b6c52) - <> "|Waiting for a validated payload from the relay." - .render drawer-plugin - .render help-alert + {} (:padding 28) (:border-radius 16) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 14) (:line-height |1.7) (:color |#8b6c52) + if + > (count channels) 1 + <> "|Select a channel to start receiving validated layouts." + <> "|Waiting for an active relay channel. Open this page with `?channel=` and send payloads with `edn-relay send --channel ''`." + if-let + layout $ :layout renderer + comp-layout-node layout + div + {} $ :style + {} (:padding 28) (:border-radius 16) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 14) (:line-height |1.7) (:color |#8b6c52) + <> "|Waiting for a validated payload from the relay." + .render drawer-plugin + .render help-alert :examples $ [] |comp-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -369,6 +405,7 @@ {} $ :style {} (:padding 18) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #e8d7ca") comp-chart-block series kind title + (:math expr display) (comp-math-block expr display) _ $ div {} $ :style {} (:padding 12) (:border "|1px solid #f08c6c") (:border-radius 12) (:background-color |#fff4ef) (:color |#9b3d15) @@ -422,6 +459,19 @@ {} (:font-size 15) (:line-height |1.7) (:color |#2e241c) (:white-space |pre-wrap) <> line :examples $ [] + |comp-math-block $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defcomp comp-math-block (expr display) + let + block? $ = (math-display-value display) |block + [] (effect-mathml expr display) + div $ {} (:class-name |mathml-host) + :style $ merge + {} (:padding 18) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #e8d7ca") (:overflow |auto) (:color |#2e241c) (:max-width |100%) (:align-self |flex-start) + if block? + {} $ :width |100% + {} $ :width |fit-content + :examples $ [] |comp-mermaid-block $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defcomp comp-mermaid-block (text) @@ -465,6 +515,14 @@ :update $ do (.!debug js/console "|[echarts] update") (render-chart) :unmount $ do (.!debug js/console "|[echarts] unmount") (dispose-chart) :examples $ [] + |effect-mathml $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defeffect effect-mathml (expr display) (action el at?) + case-default action nil + :mount $ render-mathml-on el expr display + :update $ render-mathml-on el expr display + :unmount $ set! (.-innerHTML el) | + :examples $ [] |effect-mermaid $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defeffect effect-mermaid (text) (action el at?) @@ -481,6 +539,16 @@ render-mermaid-on el payload :unmount $ .!debug js/console "|[mermaid] unmount" :examples $ [] + |effect-page-title $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defeffect effect-page-title (selected-channel) (action el at?) + let + next-title $ page-title-text selected-channel + case-default action nil + :mount $ set! (.-title js/document) next-title + :update $ set! (.-title js/document) next-title + :unmount $ set! (.-title js/document) (page-title-text nil) + :examples $ [] |ensure-mermaid! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn ensure-mermaid! () $ when @@ -488,6 +556,19 @@ .!initialize mermaid-lib $ js-object (:startOnLoad false) (:securityLevel |loose) (:theme |neutral) reset! *mermaid-ready true :examples $ [] + |math-display-value $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn math-display-value (display) + if (= display |block) |block |inline + :examples $ [] + |mathml-namespace $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def mathml-namespace |http://www.w3.org/1998/Math/MathML) + :examples $ [] + |page-title-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn page-title-text (selected-channel) + str |EDN-Renderer/ $ or selected-channel |waiting + :examples $ [] |parse-layout-children $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn parse-layout-children (children path) @@ -575,11 +656,24 @@ and string? $ :label item number? $ :value item - , item $ raise (str path "| chart series item requires string :label and number :value") + , true $ raise (str path "| chart series item requires string :label and number :value") %:: LayoutNode :chart or (:kind node) |bar or (:title node) | , series + |math $ do + validate-mathml-expr (:expr node) (str path |.expr) + %:: LayoutNode :math (:expr node) + math-display-value $ :display node + :examples $ [] + |render-mathml-on $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn render-mathml-on (el expr display) + let + root $ build-mathml-root expr display + do + set! (.-innerHTML el) | + .!appendChild el root :examples $ [] |render-mermaid-on $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -623,6 +717,35 @@ :code $ quote defn validate-layout-node (node path) (parse-layout-node node path) :examples $ [] + |validate-mathml-child $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn validate-mathml-child (child path) + if + or (string? child) (number? child) + , child $ if (list? child) (validate-mathml-expr child path) + raise $ str path "| invalid MathML child, expected string, number, or list" + :examples $ [] + |validate-mathml-expr $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn validate-mathml-expr (expr path) + if + not $ list? expr + raise $ str path "| math node requires list field :expr" + if (empty? expr) + raise $ str path "| math expression should not be empty" + let + tag-name $ first expr + children $ or (rest expr) ([]) + if + not $ string? tag-name + raise $ str path "| math expression requires string tag name" + do + foldl children nil $ fn (acc child) + do + validate-mathml-child child $ str path |/ tag-name + , acc + , expr + :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote ns app.comp.container $ :require (respo-ui.css :as css) @@ -641,19 +764,39 @@ defn build-help-payload (topics) let normalized-topics $ if (list? topics) topics ([]) - {} (:status |ok) (:kind |help) (:renderer |edn-renderer) (:summary renderer-help-overview) (:commands relay-commands) (:topics normalized-topics) + {} (:status :ok) (:kind :help) (:renderer |edn-renderer) (:summary renderer-help-overview) (:commands relay-commands) (:topics normalized-topics) :components $ select-component-docs normalized-topics :protocol_docs $ select-protocol-docs normalized-topics :examples $ select-example-docs normalized-topics :examples $ [] |build-skill-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn build-skill-payload () $ {} (:status |ok) (:kind |skill) (:renderer |edn-renderer) (:title "|edn-renderer Skill") (:text skill-text) + defn build-skill-payload (topics) + let + normalized-topics $ if (list? topics) topics ([]) + full? $ includes? normalized-topics |full + text $ if full? skill-text + if (empty? normalized-topics) skill-overview $ build-skill-text normalized-topics + {} (:status :ok) (:kind :skill) (:renderer |edn-renderer) + :title $ if full? "|edn-renderer Skill (full)" "|edn-renderer Skill" + :text text + :examples $ [] + |build-skill-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-skill-text (topics) + let + sections $ select-skill-sections topics + if (empty? sections) skill-overview $ foldl sections skill-overview + fn (acc item) + str acc "|\n\n## " (:title item) "|\n\n" $ :text item :examples $ [] |build-status-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn build-status-payload (relay) - {} (:status |ok) (:kind |status) (:renderer |edn-renderer) (:title current-page-title) (:page_url current-page-url) (:commands relay-commands) + {} (:status :ok) (:kind :status) (:renderer |edn-renderer) + :title $ current-page-title + :page_url $ current-page-url + :commands relay-commands :channel $ :selected-channel relay :channels $ or (:channels relay) ([]) :examples $ [] @@ -687,6 +830,9 @@ {} (:name |chart) (:summary "|ECharts 图表节点,支持 `bar`/`line`/`pie`/`scatter`。") :fields $ [] |kind |title |series :example "|{}\n :type |chart\n :kind |line\n :title \"|Traffic\"\n :series $ []\n {} (:label |Mon) (:value 120)" + {} (:name |math) (:summary "|MathML Core 节点,使用 Cirru EDN list 简写 `:expr` 描述公式,再由浏览器原生 MathML 渲染。") + :fields $ [] |expr |display + :example "|{}\n :type |math\n :display |block\n :expr $ [] |mfrac\n [] |mrow\n [] |mi |a\n [] |mo |+\n [] |mi |b\n [] |msqrt\n [] |mi |c" {} (:name |button) (:summary "|只读展示按钮。") :fields $ [] |text :example "|{} (:type |button) (:text |Confirm)" @@ -696,34 +842,33 @@ :examples $ [] |current-page-title $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def current-page-title $ .-title js/document + defn current-page-title () $ .-title js/document :examples $ [] |current-page-url $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def current-page-url $ .-href js/location + defn current-page-url () $ .-href js/location :examples $ [] - |current-url-param $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |current-relay-url $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn current-url-param (key) $ let - params $ new js/URLSearchParams (.-search js/location) - value $ .!get params key - if - and (some? value) - > (count value) 0 - , value nil + defn current-relay-url () $ let + server $ current-url-param |server + port $ current-url-param |port + or server $ if (some? port) (str |ws://127.0.0.1: port) (:relay-url site) :examples $ [] |current-url-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn current-url-channel () $ current-url-param |channel :examples $ [] - |current-relay-url $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |current-url-param $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn current-relay-url () $ let - server $ current-url-param |server - port $ current-url-param |port - or server $ if (some? port) - str |ws://127.0.0.1: port - :relay-url site + defn current-url-param (key) + let + params $ new js/URLSearchParams (.-search js/location) + value $ .!get params key + if + and (some? value) + > (count value) 0 + , value nil :examples $ [] |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -734,6 +879,8 @@ def example-docs $ [] {} (:name |card-demo) (:summary "|最小 card 示例。") (:payload "|{}\n :type |card\n :text \"|CLI Demo\"\n :children $ []\n {} (:type |badge) (:text |preview)\n {} (:type |text) (:text \"|Hello from CLI\")") {} (:name |chart-demo) (:summary "|折线图示例。") (:payload "|{}\n :type |chart\n :kind |line\n :title \"|Traffic trend\"\n :series $ []\n {} (:label |Mon) (:value 120)\n {} (:label |Tue) (:value 132)\n {} (:label |Wed) (:value 148)") + {} (:name |math-fraction-demo) (:summary "|MathML 分式示例。") (:payload "|{}\n :type |math\n :display |block\n :expr $ [] |mfrac\n [] |mrow\n [] |mi |a\n [] |mo |+\n [] |mi |b\n [] |msqrt\n [] |mi |c") + {} (:name |math-quadratic-demo) (:summary "|MathML 二次方程求根公式。") (:payload "|{}\n :type |math\n :display |block\n :expr $ [] |mfrac\n [] |mrow\n [] |mo |−\n [] |mi |b\n [] |mo |±\n [] |msqrt\n [] |mrow\n [] |msup\n [] |mi |b\n [] |mn |2\n [] |mo |−\n [] |mn |4\n [] |mi |a\n [] |mi |c\n [] |mrow\n [] |mn |2\n [] |mi |a") {} (:name |mermaid-demo) (:summary "|Mermaid 流程图示例。") (:payload "|{}\n :type |mermaid\n :text \"|flowchart LR\\n A --> B\\n B --> C\"") :examples $ [] |protocol-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -746,17 +893,17 @@ :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def relay-commands $ [] |channels |open-published |send |help |skill |status |open + def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def renderer-help-overview "|使用 `edn-relay help` 查询当前 renderer 支持的能力;可以用 `edn-relay help protocol` 看协议摘要,用 `edn-relay help examples` 看示例,用 `edn-relay help chart mermaid` 过滤组件帮助。") + :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`examples`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") :examples $ [] |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn select-component-docs (topics) let normalized-topics $ if (list? topics) topics ([]) - if (empty? normalized-topics) component-docs $ foldl component-docs ([]) + if (includes? normalized-topics |components) component-docs $ foldl component-docs ([]) fn (acc item) if includes? normalized-topics $ :name item @@ -787,10 +934,33 @@ append acc item , acc :examples $ [] + |select-skill-sections $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn select-skill-sections (topics) + let + normalized-topics $ if (list? topics) topics ([]) + if (includes? normalized-topics |all) skill-sections $ foldl skill-sections ([]) + fn (acc item) + if + includes? normalized-topics $ :name item + append acc item + , acc + :examples $ [] |site $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) :examples $ [] + |skill-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def skill-overview "|使用 `edn-relay skill --channel ` 获取高层工作流;默认只返回总览。需要细节时追加 topic,例如 `workflow`、`help`、`math`、`validation`;如果确实要整份文档,再用 `full`。") + :examples $ [] + |skill-sections $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def skill-sections $ [] + {} (:name |workflow) (:title |Workflow) (:text "|1. 先 `edn-relay help --channel ` 看总览。\n2. 再用 `help --channel ` 把范围收窄到组件、协议或示例。\n3. 最后才运行 `edn-relay send --channel ...` 发 payload。") + {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看具体案例,用 `edn-relay help --channel math-fraction-demo`。") + {} (:name |math) (:title |MathML) (:text "|MathML Core 已通过 `math` 节点暴露。推荐顺序是先查 `edn-relay help --channel genui math`,再查 `math-fraction-demo`,最后发送 `:type |math` + `:expr` 的 Cirru EDN payload。") + {} (:name |validation) (:title |Validation) (:text "|浏览器验证优先看 `chrome-devtools take_snapshot` 和 `chrome-devtools list_console_messages`;CLI 侧优先看 `status`、`help`、`skill` 是否和当前页面一致。") + :examples $ [] |skill-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def skill-text $ slurp-file |SKILL.md @@ -871,7 +1041,7 @@ let layout $ -> payload validate-layout source $ format-cirru-edn payload - ack-payload $ {} (:status |ok) (:layout_id layout-id) + ack-payload $ {} (:status :ok) (:layout_id layout-id) dispatch! $ :: :genui-applied request-id layout-id layout source send-genui-ack! ws request-id true ack-payload nil :examples $ [] @@ -880,9 +1050,9 @@ defn handle-relay-message! (ws raw) let frame $ parse-cirru-edn raw - kind $ :kind frame + kind $ protocol-name (:kind frame) payload $ :payload frame - op $ if (map? payload) (:op payload) nil + request $ parse-renderer-request payload selected $ selected-relay-channel if (= kind |hello-ok) handle-channel-state! ws (:client_id frame) (:channels frame) @@ -892,10 +1062,11 @@ if and (some? selected) = (:channel frame) selected - if - includes? ([] |help |skill |status) op - handle-renderer-event! ws frame - handle-genui-event! ws frame + tag-match request + (:help _) (handle-renderer-event! ws frame) + (:skill _) (handle-renderer-event! ws frame) + (:status) (handle-renderer-event! ws frame) + _ $ handle-genui-event! ws frame do |ignored if (= kind |warning) .!warn js/console $ or (:error frame) "|Relay warning" @@ -910,23 +1081,19 @@ let request-id $ :id frame payload $ :payload frame - op $ if (map? payload) (:op payload) nil - topics $ if - and (map? payload) - list? $ :topics payload - :topics payload - [] + request $ parse-renderer-request payload relay $ get-in @*reel ([] :store :relay) do (.!debug js/console "|[renderer] request" payload) - if (= op |help) - let - response-payload $ config/build-help-payload topics - send-genui-ack! ws request-id true response-payload nil - if (= op |skill) - send-genui-ack! ws request-id true (config/build-skill-payload) nil - if (= op |status) - send-genui-ack! ws request-id true (config/build-status-payload relay) nil - send-genui-ack! ws request-id false nil $ str "|Unsupported renderer op: " op + tag-match request + (:help topics) + let + response-payload $ config/build-help-payload topics + send-genui-ack! ws request-id true response-payload nil + (:skill topics) + send-genui-ack! ws request-id true (config/build-skill-payload topics) nil + (:status) + send-genui-ack! ws request-id true (config/build-status-payload relay) nil + (:invalid) (send-genui-ack! ws request-id false nil "|Unsupported renderer request") :examples $ [] |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -954,6 +1121,42 @@ :code $ quote def mount-target $ js/document.querySelector |.app :examples $ [] + |normalize-renderer-topics $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn normalize-renderer-topics (topics) + if (list? topics) + foldl topics ([]) + fn (acc item) + if + or (string? item) (tag? item) + append acc $ turn-string item + , acc + [] + :examples $ [] + |parse-renderer-request $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn parse-renderer-request (payload) + if (tuple? payload) + tag-match payload + (:help) + :: :help $ [] + (:help topics) + :: :help $ normalize-renderer-topics topics + (:skill) + :: :skill $ [] + (:skill topics) + :: :skill $ normalize-renderer-topics topics + (:status) (:: :status) + _ $ :: :invalid + if (map? payload) + let + op-name $ protocol-name (:op payload) + topics $ normalize-renderer-topics (:topics payload) + if (= op-name |help) (:: :help topics) + if (= op-name |skill) (:: :skill topics) + if (= op-name |status) (:: :status) (:: :invalid) + :: :invalid + :examples $ [] |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn persist-storage! () @@ -961,6 +1164,14 @@ js/localStorage.setItem (:storage-key config/site) format-cirru-edn $ :store @*reel :examples $ [] + |protocol-name $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn protocol-name (x) + if + or (string? x) (tag? x) + turn-string x + , nil + :examples $ [] |reload! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn reload! () $ if (nil? build-errors) @@ -982,7 +1193,7 @@ |send-genui-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn send-genui-ack! (ws request-id ok? payload error-message) - send-relay-frame! ws $ {} (:kind |ack) (:id request-id) (:ok ok?) (:payload payload) (:error error-message) + send-relay-frame! ws $ {} (:kind :ack) (:id request-id) (:ok ok?) (:payload payload) (:error error-message) :examples $ [] |send-relay-frame! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -996,7 +1207,7 @@ client-id $ get-in @*reel ([] :store :relay :client-id) selected $ selected-relay-channel channels $ if (some? selected) ([] selected) ([]) - send-relay-frame! ws $ {} (:kind |hello) (:role |receiver) (:client_id client-id) (:channels channels) + send-relay-frame! ws $ {} (:kind :hello) (:role :receiver) (:client_id client-id) (:channels channels) :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote From 71c05e9c7bfae86926f1bffdcafbf5e35ab707f9 Mon Sep 17 00:00:00 2001 From: tiye Date: Thu, 28 May 2026 02:29:33 +0800 Subject: [PATCH 08/26] return validation message back to sender --- calcit.cirru | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 185ec12..69c71b0 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -1037,13 +1037,25 @@ let request-id $ :id frame payload $ :payload frame + source $ format-cirru-edn payload layout-id $ str |layout- request-id - let - layout $ -> payload validate-layout - source $ format-cirru-edn payload - ack-payload $ {} (:status :ok) (:layout_id layout-id) - dispatch! $ :: :genui-applied request-id layout-id layout source - send-genui-ack! ws request-id true ack-payload nil + try + let + layout $ -> payload validate-layout + ack-payload $ {} (:status :ok) (:layout_id layout-id) + do + dispatch! $ :: :genui-applied request-id layout-id layout source + send-genui-ack! ws request-id true ack-payload nil + fn (error) + let + message $ if + some? $ .-message error + .-message error + str error + do + dispatch! $ :: :genui-failed request-id message source + .!warn js/console "|[renderer] validation failed" error + send-genui-ack! ws request-id false nil message :examples $ [] |handle-relay-message! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote From 6787f558df04317fa0c8d2d21138d716a8d5ac86 Mon Sep 17 00:00:00 2001 From: tiye Date: Thu, 28 May 2026 20:36:43 +0800 Subject: [PATCH 09/26] working on partial detect/update operations --- README.md | 80 +++++++++++-- SKILL.md | 29 +++++ calcit.cirru | 328 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 411 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 6a27535..49e4ed0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ The current implementation already supports the full loop: 4. render the DSL in the page 5. confirm success with a returned ack payload and layout id +It also supports inspection and local mutation of the currently rendered layout +through relay requests, so a CLI agent can read a summarized tree, fetch one +node's full DSL, and patch or replace that node without resending the whole page. + Reference screenshot: - [artifacts/edn-renderer-genui-final.png](artifacts/edn-renderer-genui-final.png) @@ -67,16 +71,16 @@ Then send a layout DSL from the CLI: ```bash LAYOUT=$(cat <<'EOF' {} - :type |card - :text "|CLI Demo" - :children $ [] - {} (:type |badge) (:text |preview) - {} (:type |divider) - {} (:type |text) (:text "|Hello from installed CLI") - {} (:type |row) - :children $ [] - {} (:type |button) (:text |Confirm) - {} (:type |input) (:name |email) (:placeholder |Email) + :type |card + :text "|CLI Demo" + :children $ [] + {} (:type |badge) (:text |preview) + {} (:type |divider) + {} (:type |text) (:text "|Hello from installed CLI") + {} (:type |row) + :children $ [] + {} (:type |button) (:text |Confirm) + {} (:type |input) (:name |email) (:placeholder |Email) EOF ) @@ -89,6 +93,62 @@ Expected result: - the page shows the layout id and request id - the renderer preview updates immediately +## Layout Inspection And Editing + +Once a layout is already loaded in the page, you can inspect and edit it with +small relay payloads. + +Get a token-efficient layout summary tree: + +```bash +edn-relay send --channel genui ' +{} + :op :layout +' +``` + +Inspect one node by 1-based path such as `2.1`: + +```bash +edn-relay send --channel genui ' +{} + :op :node + :path |2.1 +' +``` + +Patch one node in place by merging a subset of fields: + +```bash +edn-relay send --channel genui ' +{} + :op :patch + :path |1 + :changes $ {} + :text |Updated +' +``` + +Replace one node's DSL completely: + +```bash +edn-relay send --channel genui ' +{} + :op :replace + :path |2.1 + :node $ {} + :type |text + :text |Replaced +' +``` + +Path rules: + +- `root` means the whole layout +- `1`, `2`, `3` address root children with 1-based indices +- `2.1` means the first child under the second root child +- `layout` returns summaries, while `node` returns the full DSL for that path + ## DSL Components Current built-in nodes: diff --git a/SKILL.md b/SKILL.md index 8be104a..e564797 100644 --- a/SKILL.md +++ b/SKILL.md @@ -79,6 +79,8 @@ relay CLI 不维护“当前 renderer / 当前 channel / 最近一次上下文 - renderer 新增组件后,CLI 能自动发现 - 其他项目接入时,不需要额外抄一份命令说明 +对于已经渲染出来的页面,也应该通过协议暴露“继续编辑”的能力,而不是要求外部每次都重发整棵树。 + ### 2.4 skill 也要通过 renderer 暴露 `SKILL.md` 不应只是仓库里的静态说明文件。 @@ -218,6 +220,33 @@ edn-relay send --channel genui ' 2. 从 renderer 返回 skill 内容 3. 再根据 skill 决定怎么调试、怎么探索能力 +### 4.3 已有 layout 的渐进编辑 + +如果页面上已经有一个 layout,不应该要求用户每次都重发整份 DSL。 + +更推荐的顺序是: + +1. 先用 `edn-relay send --channel '{}` + `:op :layout` 获取整个 layout 的 summary tree +2. summary 里每个节点都带 1-based path,例如 `1`、`2.1`、`3.2.4` +3. 要看某个节点的完整 DSL,再发 `:op :node` + `:path` +4. 只改属性时,用 `:op :patch` + `:changes` +5. 要改结构时,再用 `:op :replace` + `:node` + +这样可以让 agent 先看轮廓,再钻到局部,最后只更新需要修改的节点。 + +推荐的 payload 形式: + +- `:op :layout` 获取 summary tree,默认隐藏节点细节,只返回 `:type`、`:path`、`:child-count` 和少量摘要字段 +- `:op :node` + `:path |2.1` 获取某个节点完整 DSL +- `:op :patch` + `:path |1.2` + `:changes {...}` 局部合并字段 +- `:op :replace` + `:path |1.2` + `:node {...}` 完整替换节点 + +路径约定: + +- `root` 表示整棵 layout +- 其他路径用 children 的 1-based 索引拼接,例如 `2.1.3` +- `2.1.3` 的含义是“根节点的第 2 个子节点下的第 1 个子节点下的第 3 个子节点” + 如果只想看某一块技能,而不是整份操作手册,则优先: 1. `edn-relay skill --channel workflow` diff --git a/calcit.cirru b/calcit.cirru index 69c71b0..d660ff6 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -792,13 +792,16 @@ :examples $ [] |build-status-payload $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn build-status-payload (relay) + defn build-status-payload (relay renderer) {} (:status :ok) (:kind :status) (:renderer |edn-renderer) :title $ current-page-title :page_url $ current-page-url :commands relay-commands :channel $ :selected-channel relay :channels $ or (:channels relay) ([]) + :layout_id $ :layout-id renderer + :last_request $ :last-request renderer + :layout_ready? $ some? (:layout-dsl renderer) :examples $ [] |component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -890,13 +893,17 @@ {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,服务端会返回 `hello-ok` 和当前活跃 channel 列表。") {} (:name |channel-state) (:summary "|当活跃 channel 列表变化时,relay 会广播 `channel-state`。") {} (:name |ack) (:summary "|同一个请求允许多个 receiver 收到事件,但 sender 只接受第一条 `ack`。") + {} (:name |layout) (:summary "|用 `edn-relay send --channel '{}` + `:op :layout` 查询当前 layout 概览;默认返回隐藏细节的 summary tree,也可带 `:path` 查询某个子树。") + {} (:name |node) (:summary "|用 `:op :node` + `:path \"1.2.3\"` 读取某个节点的完整 DSL。路径使用 1-based children 索引,`root` 表示整棵树。") + {} (:name |patch) (:summary "|用 `:op :patch` + `:path` + `:changes` 局部合并节点属性,renderer 会重新验证整棵 layout,成功后立即局部更新页面。") + {} (:name |replace) (:summary "|用 `:op :replace` + `:path` + `:node` 直接替换某个节点 DSL,适合结构性修改。") :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`examples`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") + :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`examples`、`layout`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") :examples $ [] |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -951,13 +958,14 @@ def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) :examples $ [] |skill-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def skill-overview "|使用 `edn-relay skill --channel ` 获取高层工作流;默认只返回总览。需要细节时追加 topic,例如 `workflow`、`help`、`math`、`validation`;如果确实要整份文档,再用 `full`。") + :code $ quote (def skill-overview "|使用 `edn-relay skill --channel ` 获取高层工作流;默认只返回总览。需要细节时追加 topic,例如 `workflow`、`help`、`layout`、`math`、`validation`;如果确实要整份文档,再用 `full`。") :examples $ [] |skill-sections $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def skill-sections $ [] {} (:name |workflow) (:title |Workflow) (:text "|1. 先 `edn-relay help --channel ` 看总览。\n2. 再用 `help --channel ` 把范围收窄到组件、协议或示例。\n3. 最后才运行 `edn-relay send --channel ...` 发 payload。") {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看具体案例,用 `edn-relay help --channel math-fraction-demo`。") + {} (:name |layout) (:title "|Layout Editing") (:text "|先用 `edn-relay send --channel '{}` + `:op :layout` 获取隐藏细节的 summary tree,再用 `:op :node` + `:path \"1.2.3\"` 读取完整 DSL。局部修改时优先 `:op :patch` + `:changes` 设置属性;需要换整个节点结构时再用 `:op :replace`。") {} (:name |math) (:title |MathML) (:text "|MathML Core 已通过 `math` 节点暴露。推荐顺序是先查 `edn-relay help --channel genui math`,再查 `math-fraction-demo`,最后发送 `:type |math` + `:expr` 的 Cirru EDN payload。") {} (:name |validation) (:title |Validation) (:text "|浏览器验证优先看 `chrome-devtools take_snapshot` 和 `chrome-devtools list_console_messages`;CLI 侧优先看 `status`、`help`、`skill` 是否和当前页面一致。") :examples $ [] @@ -1044,7 +1052,7 @@ layout $ -> payload validate-layout ack-payload $ {} (:status :ok) (:layout_id layout-id) do - dispatch! $ :: :genui-applied request-id layout-id layout source + dispatch! $ :: :genui-applied request-id layout-id layout payload source send-genui-ack! ws request-id true ack-payload nil fn (error) let @@ -1078,6 +1086,10 @@ (:help _) (handle-renderer-event! ws frame) (:skill _) (handle-renderer-event! ws frame) (:status) (handle-renderer-event! ws frame) + (:layout _) (handle-renderer-event! ws frame) + (:node _) (handle-renderer-event! ws frame) + (:patch _ _) (handle-renderer-event! ws frame) + (:replace _ _) (handle-renderer-event! ws frame) _ $ handle-genui-event! ws frame do |ignored if (= kind |warning) @@ -1095,17 +1107,76 @@ payload $ :payload frame request $ parse-renderer-request payload relay $ get-in @*reel ([] :store :relay) + renderer $ get-in @*reel ([] :store :renderer) do (.!debug js/console "|[renderer] request" payload) - tag-match request - (:help topics) + try + tag-match request + (:help topics) + let + response-payload $ config/build-help-payload topics + send-genui-ack! ws request-id true response-payload nil + (:skill topics) + send-genui-ack! ws request-id true (config/build-skill-payload topics) nil + (:status) + send-genui-ack! ws request-id true (config/build-status-payload relay renderer) nil + (:layout path) + if-let + layout-dsl $ :layout-dsl renderer + let + target-node $ layout-node-at-path layout-dsl path + response-payload $ {} (:status :ok) (:kind :layout) (:layout_id (:layout-id renderer)) (:path (layout-path-display path)) (:summary (summarize-layout-node target-node path)) + send-genui-ack! ws request-id true response-payload nil + send-genui-ack! ws request-id false nil "|No layout loaded in renderer" + (:node path) + if-let + layout-dsl $ :layout-dsl renderer + let + target-node $ layout-node-at-path layout-dsl path + response-payload $ {} (:status :ok) (:kind :node) (:layout_id (:layout-id renderer)) (:path (layout-path-display path)) (:dsl target-node) + :source $ format-cirru-edn target-node + :summary $ summarize-layout-node target-node path + send-genui-ack! ws request-id true response-payload nil + send-genui-ack! ws request-id false nil "|No layout loaded in renderer" + (:patch path changes) + if-let + layout-dsl $ :layout-dsl renderer + let + next-dsl $ merge-layout-node-at-path layout-dsl path changes + next-layout $ validate-layout next-dsl + next-source $ format-cirru-edn next-dsl + next-layout-id $ str |layout- request-id + response-payload $ {} (:status :ok) (:kind :patch) (:layout_id next-layout-id) (:path (layout-path-display path)) + :dsl $ layout-node-at-path next-dsl path + :summary $ summarize-layout-node (layout-node-at-path next-dsl path) path + do + dispatch! $ :: :layout-mutated request-id next-layout-id next-layout next-dsl next-source + send-genui-ack! ws request-id true response-payload nil + send-genui-ack! ws request-id false nil "|No layout loaded in renderer" + (:replace path next-node) + if-let + layout-dsl $ :layout-dsl renderer + let + next-dsl $ replace-layout-node-at-path layout-dsl path next-node + next-layout $ validate-layout next-dsl + next-source $ format-cirru-edn next-dsl + next-layout-id $ str |layout- request-id + response-payload $ {} (:status :ok) (:kind :replace) (:layout_id next-layout-id) (:path (layout-path-display path)) + :dsl $ layout-node-at-path next-dsl path + :summary $ summarize-layout-node (layout-node-at-path next-dsl path) path + do + dispatch! $ :: :layout-mutated request-id next-layout-id next-layout next-dsl next-source + send-genui-ack! ws request-id true response-payload nil + send-genui-ack! ws request-id false nil "|No layout loaded in renderer" + (:invalid) (send-genui-ack! ws request-id false nil "|Unsupported renderer request") + fn (error) let - response-payload $ config/build-help-payload topics - send-genui-ack! ws request-id true response-payload nil - (:skill topics) - send-genui-ack! ws request-id true (config/build-skill-payload topics) nil - (:status) - send-genui-ack! ws request-id true (config/build-status-payload relay) nil - (:invalid) (send-genui-ack! ws request-id false nil "|Unsupported renderer request") + message $ if + some? $ .-message error + .-message error + str error + do + .!warn js/console "|[renderer] request failed" error + send-genui-ack! ws request-id false nil message :examples $ [] |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1145,6 +1216,104 @@ , acc [] :examples $ [] + |layout-path-display $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-path-display (path) + if (empty? path) + , |root + layout-path-display-iter path | + :examples $ [] + |layout-path-display-iter $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-path-display-iter (path acc) + if (empty? path) + , acc + let + step $ str $ first path + recur (rest path) + if (= acc |) + , step + str acc |. step + :examples $ [] + |layout-path-segment-pattern $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def layout-path-segment-pattern $ new js/RegExp "|^([0-9]+)$") + :examples $ [] + |layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-node-at-path (node path) + if (empty? path) + , node + let + children $ or (:children node) ([]) + step $ first path + child $ pick-layout-child children step + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if (nil? child) + raise $ str "|Missing layout node at path " (layout-path-display path) + recur child $ rest path + :examples $ [] + |merge-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn merge-layout-node-at-path (node path changes) + if + not $ map? changes + raise "|Patch request expects map field :changes" + if (empty? path) + merge node changes + let + children $ or (:children node) ([]) + step $ first path + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if (nil? (pick-layout-child children step)) + raise $ str "|Missing layout node at path " (layout-path-display path) + assoc node :children + -> children .to-list $ map-indexed + fn (idx child) + if (= (inc idx) step) + merge-layout-node-at-path child (rest path) changes + , child + :examples $ [] + |normalize-layout-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn normalize-layout-path (path) + if (nil? path) + [] + if (number? path) + let + segment $ parse-layout-path-segment path + if (some? segment) ([] segment) $ , nil + if (list? path) + foldl path ([]) + fn (acc item) + if (nil? acc) + , nil + let + segment $ parse-layout-path-segment item + if (some? segment) + append acc segment + , nil + if + or (string? path) (tag? path) + let + raw $ turn-string path + if + or (= raw |) (= raw |root) + [] + foldl (split raw |.) ([]) + fn (acc item) + if (nil? acc) + , nil + let + segment $ parse-layout-path-segment item + if (some? segment) + append acc segment + , nil + , nil + :examples $ [] |parse-renderer-request $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn parse-renderer-request (payload) @@ -1159,6 +1328,27 @@ (:skill topics) :: :skill $ normalize-renderer-topics topics (:status) (:: :status) + (:layout) (:: :layout $ []) + (:layout path) + if-let + normalized $ normalize-layout-path path + :: :layout normalized + :: :invalid + (:node path) + if-let + normalized $ normalize-layout-path path + :: :node normalized + :: :invalid + (:patch path changes) + if-let + normalized $ normalize-layout-path path + :: :patch normalized changes + :: :invalid + (:replace path next-node) + if-let + normalized $ normalize-layout-path path + :: :replace normalized next-node + :: :invalid _ $ :: :invalid if (map? payload) let @@ -1166,9 +1356,105 @@ topics $ normalize-renderer-topics (:topics payload) if (= op-name |help) (:: :help topics) if (= op-name |skill) (:: :skill topics) - if (= op-name |status) (:: :status) (:: :invalid) + if (= op-name |status) + :: :status + if (= op-name |layout) + if-let + normalized $ normalize-layout-path (:path payload) + :: :layout normalized + :: :invalid + if (= op-name |node) + if-let + normalized $ normalize-layout-path (:path payload) + :: :node normalized + :: :invalid + if (= op-name |patch) + if-let + normalized $ normalize-layout-path (:path payload) + :: :patch normalized (:changes payload) + :: :invalid + if (= op-name |replace) + if-let + normalized $ normalize-layout-path (:path payload) + :: :replace normalized $ or (:node payload) (:dsl payload) + :: :invalid + :: :invalid :: :invalid :examples $ [] + |parse-layout-path-segment $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn parse-layout-path-segment (item) + if (number? item) + if (> item 0) (, item) $ , nil + if + or (string? item) (tag? item) + let + text $ turn-string item + if (some? (.!match text layout-path-segment-pattern)) + js/parseInt text 10 + , nil + , nil + :examples $ [] + |pick-layout-child $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn pick-layout-child (children position) + if + not $ list? children + , nil + if + not $ > position 0 + , nil + if (empty? children) + , nil + if (= position 1) + first children + recur (rest children) $ dec position + :examples $ [] + |replace-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn replace-layout-node-at-path (node path next-node) + if (empty? path) + , next-node + let + children $ or (:children node) ([]) + step $ first path + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if (nil? (pick-layout-child children step)) + raise $ str "|Missing layout node at path " (layout-path-display path) + assoc node :children + -> children .to-list $ map-indexed + fn (idx child) + if (= (inc idx) step) + replace-layout-node-at-path child (rest path) next-node + , child + :examples $ [] + |summarize-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn summarize-layout-node (node path) + let + node-type $ :type node + children $ if (list? (:children node)) (:children node) ([]) + base $ {} (:path (layout-path-display path)) (:type node-type) (:child-count (count children)) + meta $ case-default node-type + {} + |card $ if (string? (:text node)) ({} (:title (:text node))) ({}) + |text $ if (string? (:text node)) ({} (:text (first (split-lines (:text node))))) ({}) + |badge $ if (string? (:text node)) ({} (:text (:text node))) ({}) + |button $ if (string? (:text node)) ({} (:text (:text node))) ({}) + |input $ {} (:name (:name node)) (:placeholder (:placeholder node)) + |markdown $ {} (:lines (count (split-lines (or (:text node) |)))) + |mermaid $ {} (:lines (count (split-lines (or (:text node) |)))) + |chart $ {} (:kind (or (:kind node) |bar)) (:title (or (:title node) |)) (:series-count (count (or (:series node) ([])))) + |math $ {} (:display (or (:display node) |inline)) (:expr-tag (first (or (:expr node) ([])))) + merge base $ if (> (count children) 0) + assoc meta :children + -> children .to-list $ map-indexed + fn (idx child) + summarize-layout-node child $ append path (inc idx) + , meta + :examples $ [] |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn persist-storage! () @@ -1243,7 +1529,7 @@ :cursor $ [] :relay $ {} (:status |idle) (:client-id nil) (:last-error nil) (:selected-channel nil) :channels $ [] - :renderer $ {} (:layout nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) + :renderer $ {} (:layout nil) (:layout-dsl nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.schema) @@ -1267,6 +1553,7 @@ -> store assoc-in ([] :relay :selected-channel) channel assoc-in ([] :renderer :layout) nil + assoc-in ([] :renderer :layout-dsl) nil assoc-in ([] :renderer :layout-id) nil assoc-in ([] :renderer :layout-source) | assoc-in ([] :renderer :last-request) nil @@ -1275,9 +1562,10 @@ -> store assoc-in ([] :relay :status) status assoc-in ([] :relay :last-error) message - (:genui-applied request-id layout-id layout source) + (:genui-applied request-id layout-id layout layout-dsl source) -> store assoc-in ([] :renderer :layout) layout + assoc-in ([] :renderer :layout-dsl) layout-dsl assoc-in ([] :renderer :layout-id) layout-id assoc-in ([] :renderer :layout-source) source assoc-in ([] :renderer :last-request) request-id @@ -1287,6 +1575,14 @@ assoc-in ([] :renderer :layout-source) source assoc-in ([] :renderer :last-request) request-id assoc-in ([] :renderer :last-error) message + (:layout-mutated request-id layout-id layout layout-dsl source) + -> store + assoc-in ([] :renderer :layout) layout + assoc-in ([] :renderer :layout-dsl) layout-dsl + assoc-in ([] :renderer :layout-id) layout-id + assoc-in ([] :renderer :layout-source) source + assoc-in ([] :renderer :last-request) request-id + assoc-in ([] :renderer :last-error) nil _ $ do (eprintln "|unknown op:" op) store :examples $ [] :ns $ %{} :NsEntry (:doc |) From 01adb52bb1425ce733b59ede2f372cdf10a93996 Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 29 May 2026 01:23:34 +0800 Subject: [PATCH 10/26] Improve renderer editing guidance --- calcit.cirru | 363 +++++++++++++++++++++++++++------------------------ 1 file changed, 193 insertions(+), 170 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index d660ff6..40427fa 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -881,6 +881,10 @@ :code $ quote def example-docs $ [] {} (:name |card-demo) (:summary "|最小 card 示例。") (:payload "|{}\n :type |card\n :text \"|CLI Demo\"\n :children $ []\n {} (:type |badge) (:text |preview)\n {} (:type |text) (:text \"|Hello from CLI\")") + {} (:name |layout-summary-demo) (:summary "|查询当前 layout summary tree。") (:payload "|{}\n :op :layout") + {} (:name |layout-node-demo) (:summary "|按路径读取一个节点的完整 DSL。") (:payload "|{}\n :op :node\n :path |1.2") + {} (:name |layout-patch-demo) (:summary "|按路径局部更新节点属性。") (:payload "|{}\n :op :patch\n :path |1\n :changes $ {} (:text \"|Updated title\")") + {} (:name |layout-replace-demo) (:summary "|按路径替换整棵子树。") (:payload "|{}\n :op :replace\n :path |2.1\n :node $ {} (:type |text) (:text \"|Replaced from CLI\")") {} (:name |chart-demo) (:summary "|折线图示例。") (:payload "|{}\n :type |chart\n :kind |line\n :title \"|Traffic trend\"\n :series $ []\n {} (:label |Mon) (:value 120)\n {} (:label |Tue) (:value 132)\n {} (:label |Wed) (:value 148)") {} (:name |math-fraction-demo) (:summary "|MathML 分式示例。") (:payload "|{}\n :type |math\n :display |block\n :expr $ [] |mfrac\n [] |mrow\n [] |mi |a\n [] |mo |+\n [] |mi |b\n [] |msqrt\n [] |mi |c") {} (:name |math-quadratic-demo) (:summary "|MathML 二次方程求根公式。") (:payload "|{}\n :type |math\n :display |block\n :expr $ [] |mfrac\n [] |mrow\n [] |mo |−\n [] |mi |b\n [] |mo |±\n [] |msqrt\n [] |mrow\n [] |msup\n [] |mi |b\n [] |mn |2\n [] |mo |−\n [] |mn |4\n [] |mi |a\n [] |mi |c\n [] |mrow\n [] |mn |2\n [] |mi |a") @@ -893,17 +897,18 @@ {} (:name |hello) (:summary "|浏览器连接 relay 后先发送 `hello`,服务端会返回 `hello-ok` 和当前活跃 channel 列表。") {} (:name |channel-state) (:summary "|当活跃 channel 列表变化时,relay 会广播 `channel-state`。") {} (:name |ack) (:summary "|同一个请求允许多个 receiver 收到事件,但 sender 只接受第一条 `ack`。") - {} (:name |layout) (:summary "|用 `edn-relay send --channel '{}` + `:op :layout` 查询当前 layout 概览;默认返回隐藏细节的 summary tree,也可带 `:path` 查询某个子树。") - {} (:name |node) (:summary "|用 `:op :node` + `:path \"1.2.3\"` 读取某个节点的完整 DSL。路径使用 1-based children 索引,`root` 表示整棵树。") - {} (:name |patch) (:summary "|用 `:op :patch` + `:path` + `:changes` 局部合并节点属性,renderer 会重新验证整棵 layout,成功后立即局部更新页面。") - {} (:name |replace) (:summary "|用 `:op :replace` + `:path` + `:node` 直接替换某个节点 DSL,适合结构性修改。") + {} (:name |editing) (:summary "|局部编辑推荐顺序是先 `:layout` 看 summary tree,再用 `:node` 读取完整 DSL,最后按改动大小选择 `:patch` 或 `:replace`。成功响应都会回新的 `:layout_id`,并附当前节点 `:summary`,变更类操作还会附 `:dsl`。") + {} (:name |layout) (:summary "|CLI 上优先用 `{:op :layout}` 查询当前 layout 概览;可附 `:path` 只看某个子树。summary 节点会带 `:path`、`:type`、`:child-count` 和少量摘要字段。") + {} (:name |node) (:summary "|用 `:op :node` + `:path \"1.2.3\"` 读取某个节点的完整 DSL。路径使用 1-based children 索引,`root` 表示整棵树;成功时同时返回 `:dsl`、`:source` 和 `:summary`。") + {} (:name |patch) (:summary "|用 `:op :patch` + `:path` + `:changes` 局部合并节点属性,renderer 会重新验证整棵 layout;成功后立即局部更新页面,并返回目标节点新的 `:dsl` 与 `:summary`。") + {} (:name |replace) (:summary "|用 `:op :replace` + `:path` + `:node` 直接替换某个节点 DSL,适合结构性修改;成功后同样会重新验证整棵树,并返回替换结果。") :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`examples`、`layout`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") + :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`editing`、`examples`、`layout`、`layout-patch-demo`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") :examples $ [] |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -964,10 +969,11 @@ :code $ quote def skill-sections $ [] {} (:name |workflow) (:title |Workflow) (:text "|1. 先 `edn-relay help --channel ` 看总览。\n2. 再用 `help --channel ` 把范围收窄到组件、协议或示例。\n3. 最后才运行 `edn-relay send --channel ...` 发 payload。") - {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看具体案例,用 `edn-relay help --channel math-fraction-demo`。") - {} (:name |layout) (:title "|Layout Editing") (:text "|先用 `edn-relay send --channel '{}` + `:op :layout` 获取隐藏细节的 summary tree,再用 `:op :node` + `:path \"1.2.3\"` 读取完整 DSL。局部修改时优先 `:op :patch` + `:changes` 设置属性;需要换整个节点结构时再用 `:op :replace`。") + {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看局部编辑流程,用 `edn-relay help --channel editing`;要看具体案例,用 `edn-relay help --channel layout-patch-demo`。") + {} (:name |editing) (:title "|Editing Workflow") (:text "|先用 `edn-relay send --channel '{}` + `:op :layout` 获取隐藏细节的 summary tree,再用 `:op :node` + `:path` 读取完整 DSL。只改属性时优先 `:patch` + `:changes`,结构变化再用 `:replace`。每次成功都会回新的 `:layout_id`、目标节点 `:summary`,并在 `node/patch/replace` 返回完整 `:dsl`。") + {} (:name |layout) (:title "|Layout Editing") (:text "|局部编辑默认走 `layout -> node -> patch/replace`。路径使用 1-based children 索引,`root` 表示整棵树;如果 agent 还不确定 payload 形状,先查 `layout-summary-demo`、`layout-node-demo`、`layout-patch-demo`。") {} (:name |math) (:title |MathML) (:text "|MathML Core 已通过 `math` 节点暴露。推荐顺序是先查 `edn-relay help --channel genui math`,再查 `math-fraction-demo`,最后发送 `:type |math` + `:expr` 的 Cirru EDN payload。") - {} (:name |validation) (:title |Validation) (:text "|浏览器验证优先看 `chrome-devtools take_snapshot` 和 `chrome-devtools list_console_messages`;CLI 侧优先看 `status`、`help`、`skill` 是否和当前页面一致。") + {} (:name |validation) (:title |Validation) (:text "|浏览器验证优先看 `chrome-devtools take_snapshot` 和 `chrome-devtools list_console_messages`;CLI 侧优先看 `status`、`help`、`skill` 是否和当前页面一致。局部编辑失败时,优先检查返回的 `ack false` 和对应 `:path`。") :examples $ [] |skill-text $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1124,7 +1130,10 @@ layout-dsl $ :layout-dsl renderer let target-node $ layout-node-at-path layout-dsl path - response-payload $ {} (:status :ok) (:kind :layout) (:layout_id (:layout-id renderer)) (:path (layout-path-display path)) (:summary (summarize-layout-node target-node path)) + response-payload $ {} (:status :ok) (:kind :layout) + :layout_id $ :layout-id renderer + :path $ layout-path-display path + :summary $ summarize-layout-node target-node path send-genui-ack! ws request-id true response-payload nil send-genui-ack! ws request-id false nil "|No layout loaded in renderer" (:node path) @@ -1132,7 +1141,10 @@ layout-dsl $ :layout-dsl renderer let target-node $ layout-node-at-path layout-dsl path - response-payload $ {} (:status :ok) (:kind :node) (:layout_id (:layout-id renderer)) (:path (layout-path-display path)) (:dsl target-node) + response-payload $ {} (:status :ok) (:kind :node) + :layout_id $ :layout-id renderer + :path $ layout-path-display path + :dsl target-node :source $ format-cirru-edn target-node :summary $ summarize-layout-node target-node path send-genui-ack! ws request-id true response-payload nil @@ -1145,7 +1157,8 @@ next-layout $ validate-layout next-dsl next-source $ format-cirru-edn next-dsl next-layout-id $ str |layout- request-id - response-payload $ {} (:status :ok) (:kind :patch) (:layout_id next-layout-id) (:path (layout-path-display path)) + response-payload $ {} (:status :ok) (:kind :patch) (:layout_id next-layout-id) + :path $ layout-path-display path :dsl $ layout-node-at-path next-dsl path :summary $ summarize-layout-node (layout-node-at-path next-dsl path) path do @@ -1160,7 +1173,8 @@ next-layout $ validate-layout next-dsl next-source $ format-cirru-edn next-dsl next-layout-id $ str |layout- request-id - response-payload $ {} (:status :ok) (:kind :replace) (:layout_id next-layout-id) (:path (layout-path-display path)) + response-payload $ {} (:status :ok) (:kind :replace) (:layout_id next-layout-id) + :path $ layout-path-display path :dsl $ layout-node-at-path next-dsl path :summary $ summarize-layout-node (layout-node-at-path next-dsl path) path do @@ -1174,9 +1188,38 @@ some? $ .-message error .-message error str error - do - .!warn js/console "|[renderer] request failed" error - send-genui-ack! ws request-id false nil message + do (.!warn js/console "|[renderer] request failed" error) (send-genui-ack! ws request-id false nil message) + :examples $ [] + |layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-node-at-path (node path) + if (empty? path) node $ let + children $ or (:children node) ([]) + step $ first path + child $ pick-layout-child children step + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if (nil? child) + raise $ str "|Missing layout node at path " (layout-path-display path) + recur child $ rest path + :examples $ [] + |layout-path-display $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-path-display (path) + if (empty? path) |root $ layout-path-display-iter path | + :examples $ [] + |layout-path-display-iter $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn layout-path-display-iter (path acc) + if (empty? path) acc $ let + step $ str (first path) + recur (rest path) + if (= acc |) step $ str acc |. step + :examples $ [] + |layout-path-segment-pattern $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def layout-path-segment-pattern $ new js/RegExp "|^([0-9]+)$" :examples $ [] |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1200,102 +1243,47 @@ ensure-relay! println "|App started." :examples $ [] - |mount-target $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - def mount-target $ js/document.querySelector |.app - :examples $ [] - |normalize-renderer-topics $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn normalize-renderer-topics (topics) - if (list? topics) - foldl topics ([]) - fn (acc item) - if - or (string? item) (tag? item) - append acc $ turn-string item - , acc - [] - :examples $ [] - |layout-path-display $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn layout-path-display (path) - if (empty? path) - , |root - layout-path-display-iter path | - :examples $ [] - |layout-path-display-iter $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn layout-path-display-iter (path acc) - if (empty? path) - , acc - let - step $ str $ first path - recur (rest path) - if (= acc |) - , step - str acc |. step - :examples $ [] - |layout-path-segment-pattern $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def layout-path-segment-pattern $ new js/RegExp "|^([0-9]+)$") - :examples $ [] - |layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn layout-node-at-path (node path) - if (empty? path) - , node - let - children $ or (:children node) ([]) - step $ first path - child $ pick-layout-child children step - if - not $ list? children - raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" - if (nil? child) - raise $ str "|Missing layout node at path " (layout-path-display path) - recur child $ rest path - :examples $ [] |merge-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn merge-layout-node-at-path (node path changes) if not $ map? changes raise "|Patch request expects map field :changes" - if (empty? path) - merge node changes + if (empty? path) (merge node changes) let children $ or (:children node) ([]) step $ first path if not $ list? children raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" - if (nil? (pick-layout-child children step)) + if + nil? $ pick-layout-child children step raise $ str "|Missing layout node at path " (layout-path-display path) - assoc node :children - -> children .to-list $ map-indexed - fn (idx child) - if (= (inc idx) step) - merge-layout-node-at-path child (rest path) changes - , child + assoc node :children $ -> children .to-list + map-indexed $ fn (idx child) + if + = (inc idx) step + merge-layout-node-at-path child (rest path) changes + , child + :examples $ [] + |mount-target $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def mount-target $ js/document.querySelector |.app :examples $ [] |normalize-layout-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn normalize-layout-path (path) - if (nil? path) - [] + if (nil? path) ([]) if (number? path) let segment $ parse-layout-path-segment path - if (some? segment) ([] segment) $ , nil + if (some? segment) ([] segment) nil if (list? path) foldl path ([]) fn (acc item) - if (nil? acc) - , nil - let - segment $ parse-layout-path-segment item - if (some? segment) - append acc segment - , nil + if (nil? acc) nil $ let + segment $ parse-layout-path-segment item + if (some? segment) (append acc segment) nil if or (string? path) (tag? path) let @@ -1305,14 +1293,37 @@ [] foldl (split raw |.) ([]) fn (acc item) - if (nil? acc) - , nil - let - segment $ parse-layout-path-segment item - if (some? segment) - append acc segment - , nil + if (nil? acc) nil $ let + segment $ parse-layout-path-segment item + if (some? segment) (append acc segment) nil + , nil + :examples $ [] + |normalize-renderer-topics $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn normalize-renderer-topics (topics) + if (list? topics) + foldl topics ([]) + fn (acc item) + if + or (string? item) (tag? item) + append acc $ turn-string item + , acc + [] + :examples $ [] + |parse-layout-path-segment $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn parse-layout-path-segment (item) + if (number? item) + if (> item 0) item nil + if + or (string? item) (tag? item) + let + text $ turn-string item + if + some? $ .!match text layout-path-segment-pattern + js/parseInt text 10 , nil + , nil :examples $ [] |parse-renderer-request $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1328,7 +1339,8 @@ (:skill topics) :: :skill $ normalize-renderer-topics topics (:status) (:: :status) - (:layout) (:: :layout $ []) + (:layout) + :: :layout $ [] (:layout path) if-let normalized $ normalize-layout-path path @@ -1356,8 +1368,7 @@ topics $ normalize-renderer-topics (:topics payload) if (= op-name |help) (:: :help topics) if (= op-name |skill) (:: :skill topics) - if (= op-name |status) - :: :status + if (= op-name |status) (:: :status) if (= op-name |layout) if-let normalized $ normalize-layout-path (:path payload) @@ -1371,7 +1382,7 @@ if (= op-name |patch) if-let normalized $ normalize-layout-path (:path payload) - :: :patch normalized (:changes payload) + :: :patch normalized $ :changes payload :: :invalid if (= op-name |replace) if-let @@ -1381,86 +1392,23 @@ :: :invalid :: :invalid :examples $ [] - |parse-layout-path-segment $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - defn parse-layout-path-segment (item) - if (number? item) - if (> item 0) (, item) $ , nil - if - or (string? item) (tag? item) - let - text $ turn-string item - if (some? (.!match text layout-path-segment-pattern)) - js/parseInt text 10 - , nil - , nil + defn persist-storage! () + println "|Saved at" $ .!toISOString (new js/Date) + js/localStorage.setItem (:storage-key config/site) + format-cirru-edn $ :store @*reel :examples $ [] |pick-layout-child $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn pick-layout-child (children position) if not $ list? children - , nil - if + , nil $ if not $ > position 0 , nil - if (empty? children) - , nil - if (= position 1) - first children - recur (rest children) $ dec position - :examples $ [] - |replace-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn replace-layout-node-at-path (node path next-node) - if (empty? path) - , next-node - let - children $ or (:children node) ([]) - step $ first path - if - not $ list? children - raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" - if (nil? (pick-layout-child children step)) - raise $ str "|Missing layout node at path " (layout-path-display path) - assoc node :children - -> children .to-list $ map-indexed - fn (idx child) - if (= (inc idx) step) - replace-layout-node-at-path child (rest path) next-node - , child - :examples $ [] - |summarize-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn summarize-layout-node (node path) - let - node-type $ :type node - children $ if (list? (:children node)) (:children node) ([]) - base $ {} (:path (layout-path-display path)) (:type node-type) (:child-count (count children)) - meta $ case-default node-type - {} - |card $ if (string? (:text node)) ({} (:title (:text node))) ({}) - |text $ if (string? (:text node)) ({} (:text (first (split-lines (:text node))))) ({}) - |badge $ if (string? (:text node)) ({} (:text (:text node))) ({}) - |button $ if (string? (:text node)) ({} (:text (:text node))) ({}) - |input $ {} (:name (:name node)) (:placeholder (:placeholder node)) - |markdown $ {} (:lines (count (split-lines (or (:text node) |)))) - |mermaid $ {} (:lines (count (split-lines (or (:text node) |)))) - |chart $ {} (:kind (or (:kind node) |bar)) (:title (or (:title node) |)) (:series-count (count (or (:series node) ([])))) - |math $ {} (:display (or (:display node) |inline)) (:expr-tag (first (or (:expr node) ([])))) - merge base $ if (> (count children) 0) - assoc meta :children - -> children .to-list $ map-indexed - fn (idx child) - summarize-layout-node child $ append path (inc idx) - , meta - :examples $ [] - |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn persist-storage! () - println "|Saved at" $ .!toISOString (new js/Date) - js/localStorage.setItem (:storage-key config/site) - format-cirru-edn $ :store @*reel + if (empty? children) nil $ if (= position 1) (first children) + recur (rest children) (dec position) :examples $ [] |protocol-name $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1484,6 +1432,25 @@ :code $ quote defn render-app! () $ render! mount-target (comp-container @*reel) dispatch! :examples $ [] + |replace-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn replace-layout-node-at-path (node path next-node) + if (empty? path) next-node $ let + children $ or (:children node) ([]) + step $ first path + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if + nil? $ pick-layout-child children step + raise $ str "|Missing layout node at path " (layout-path-display path) + assoc node :children $ -> children .to-list + map-indexed $ fn (idx child) + if + = (inc idx) step + replace-layout-node-at-path child (rest path) next-node + , child + :examples $ [] |selected-relay-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn selected-relay-channel () $ get-in @*reel ([] :store :relay :selected-channel) @@ -1498,6 +1465,62 @@ defn send-relay-frame! (ws frame) .!send ws $ format-cirru-edn frame :examples $ [] + |summarize-layout-node $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn summarize-layout-node (node path) + let + node-type $ :type node + children $ if + list? $ :children node + :children node + [] + base $ {} + :path $ layout-path-display path + :type node-type + :child-count $ count children + meta $ case-default node-type ({}) + |card $ if + string? $ :text node + {} $ :title (:text node) + {} + |text $ if + string? $ :text node + {} $ :text + first $ split-lines (:text node) + {} + |badge $ if + string? $ :text node + {} $ :text (:text node) + {} + |button $ if + string? $ :text node + {} $ :text (:text node) + {} + |input $ {} + :name $ :name node + :placeholder $ :placeholder node + |markdown $ {} + :lines $ count + split-lines $ or (:text node) | + |mermaid $ {} + :lines $ count + split-lines $ or (:text node) | + |chart $ {} + :kind $ or (:kind node) |bar + :title $ or (:title node) | + :series-count $ count + or (:series node) ([]) + |math $ {} + :display $ or (:display node) |inline + :expr-tag $ first + or (:expr node) ([]) + merge base $ if + > (count children) 0 + assoc meta :children $ -> children .to-list + map-indexed $ fn (idx child) + summarize-layout-node child $ append path (inc idx) + , meta + :examples $ [] |sync-selected-channel! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn sync-selected-channel! (ws) From 3a4bef50e04e02a75d9bcadc052a6ec8ac4507df Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 29 May 2026 01:38:42 +0800 Subject: [PATCH 11/26] Add message history drawer --- calcit.cirru | 333 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 205 insertions(+), 128 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 40427fa..aa1756a 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -133,210 +133,242 @@ list? $ :channels relay :channels relay [] + history $ if + list? $ :history renderer + :history renderer + [] + selected-history $ :selected-history renderer drawer-plugin $ use-drawer (>> states :drawer) {} - :style $ {} (:width 420) (:min-width 0) (:max-width "|calc(100vw - 20px)") (:padding "|14px 14px 18px") (:gap 14) (:background-color |#fffaf4) (:border-left "|1px solid #ead8c7") (:box-shadow "|-10px 0 30px hsla(24, 35%, 18%, 0.12)") (:overflow |auto) + :style $ {} (:width 820) (:min-width 0) (:max-width "|calc(100vw - 24px)") (:padding "|12px 12px 14px") (:gap 12) (:background-color |#f7f7f3) (:border-left "|1px solid #d7d7cf") (:box-shadow "|-12px 0 28px hsla(210, 8%, 18%, 0.12)") (:overflow |auto) :container-style $ if drawer-open? {} (:position :fixed) (:top 0) (:right 0) (:bottom 0) (:left 0) (:z-index |40) {} - :backdrop-style $ {} (:background-color "|hsla(28, 40%, 16%, 0.16)") (:backdrop-filter "|blur(10px)") + :backdrop-style $ {} (:background-color "|hsla(210, 10%, 18%, 0.08)") (:backdrop-filter "|blur(8px)") :render $ fn (on-close) div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 14) + {} (:display |flex) (:flex-direction |column) (:gap 12) div {} $ :style - {} (:display |flex) (:justify-content |space-between) (:align-items |flex-start) (:gap 12) + {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 10) (:flex-wrap |wrap) div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 6) - div - {} $ :style - {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) - <> "|Live session" + {} (:display |flex) (:align-items |center) (:gap 8) (:flex-wrap |wrap) div {} $ :style - {} (:font-size 22) (:font-weight |700) (:line-height |1.1) (:color |#2b2018) - <> "|Renderer diagnostics" + {} (:font-size 15) (:font-weight |700) (:color |#1f2933) + <> |History div {} $ :style - {} (:font-size 13) (:line-height |1.6) (:color |#7b6451) - <> "|Inspect relay status, request ids, available channels, and the exact Cirru EDN payload being rendered." + {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Messages: " (count history) + if (some? selected-channel) + div + {} $ :style + {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Channel: " selected-channel button $ {} (:class-name css/button) (:inner-text |Close) - :style $ {} (:padding "|8px 12px") (:align-self |flex-start) + :style $ {} (:padding "|5px 9px") (:font-size 11) :on-click $ fn (e d!) (on-close d!) div {} $ :style - {} (:display |flex) (:gap 10) (:flex-wrap |wrap) - div - {} $ :style - {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) - <> $ str "|Relay: " - or (:status relay) |idle - if-let - request-id $ :last-request renderer - div - {} $ :style - {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) - <> $ str "|Request: " request-id - div - {} $ :style - {} (:padding "|8px 12px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 13) (:color |#6b4a32) - <> $ str "|Channels: " (count channels) - div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 18) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") + {} (:display |flex) (:align-items |stretch) (:gap 12) (:flex-wrap |wrap) div {} $ :style - {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) - <> |Relay - div ({}) - if (some? selected-channel) - <> $ str "|Selected channel: " selected-channel - <> "|No channel selected yet." - if - > (count channels) 0 - list-> - {} $ :style - {} (:display |flex) (:gap 8) (:flex-wrap |wrap) - -> channels .to-list $ map-indexed - fn (idx channel) - [] idx $ div - {} $ :style - {} (:padding "|6px 10px") (:border-radius 999) - :background-color $ if (= channel selected-channel) |#f3d7ba |#fff - :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" - :font-size 12 - :color |#6b4a32 - <> channel - if-let - client-id $ :client-id relay - div ({}) - <> $ str "|Client: " client-id - if-let - relay-error $ :last-error relay + {} (:width 290) (:max-width |100%) (:display |flex) (:flex-direction |column) (:gap 8) div {} $ :style - {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) - <> $ str "|Relay error: " relay-error - div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 18) (:background-color |#fffdf9) (:border "|1px solid #ead8c7") + {} (:font-size 12) (:font-weight |600) (:color |#4b5563) + <> |Messages + if + > (count history) 0 + list-> + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) + -> history .to-list $ map-indexed + fn (idx item) + let + active? $ and + some? selected-history + = (:raw item) (:raw selected-history) + meta-channel $ or (:channel item) |session + meta-request $ or (:request-id item) |- + [] idx $ div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :opacity $ if (:matched? item) |1 |0.72 + :on-click $ fn (e d!) + d! $ :: :select-history item + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:summary item) |Message + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ str + or (:kind item) |unknown + , | / meta-channel | / meta-request + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|No relay messages yet." div {} $ :style - {} (:font-size 12) (:font-weight |700) (:letter-spacing |1px) (:text-transform |uppercase) (:color |#8b6244) - <> "|Latest payload" - if-let - render-error $ :last-error renderer + {} (:flex |1) (:min-width 280) (:display |flex) (:flex-direction |column) (:gap 8) div {} $ :style - {} (:font-size 13) (:line-height |1.5) (:color |#a23f1a) - <> $ str "|Validation error: " render-error - textarea $ {} - :value $ or (:layout-source renderer) | - :read-only true - :spell-check false - :placeholder "|Incoming Cirru EDN layout payload will appear here." - :style $ {} (:width |100%) (:min-height |280px) (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #dcc8b6") (:background-color |#fff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) + {} (:font-size 12) (:font-weight |600) (:color |#4b5563) + <> |Detail + if-let (item selected-history) + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 8) + div + {} $ :style + {} (:font-size 14) (:font-weight |700) (:color |#1f2933) + <> $ or (:summary item) |Message + div + {} $ :style + {} (:display |flex) (:gap 6) (:flex-wrap |wrap) + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Kind: " + or (:kind item) |unknown + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Channel: " + or (:channel item) |session + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Request: " + or (:request-id item) |- + if (:matched? item) + div $ {} + div + {} $ :style + {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#f4f4f1) (:border "|1px solid #deded8") (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|This message did not match the current channel filter." + textarea $ {} + :value $ or (:raw item) | + :read-only true + :spell-check false + :placeholder "|Relay frame detail" + :style $ {} (:width |100%) (:min-height "|calc(100vh - 320px)") (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #d7d7cf") (:background-color |#ffffff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|Click a message to inspect its raw relay frame." when dev? $ comp-reel (>> states :reel) reel ({}) help-alert $ use-alert (>> states :help) - {} $ :text "|Use the drawer to inspect relay status, request ids, channel state, and incoming Cirru EDN without reserving a permanent sidebar." - [] (effect-page-title selected-channel) + {} $ :text "|Use History to inspect recent relay frames and the raw Cirru EDN being rendered." + do (effect-page-title selected-channel) div {} $ :style - {} (:min-height |100vh) (:padding 16) (:box-sizing |border-box) (:background-color |#f6efe6) (:color |#2b2018) (:font-family |Avenir) + {} (:min-height |100vh) (:padding 12) (:box-sizing |border-box) (:background-color |#efefeb) (:color |#1f2933) (:font-family |Avenir) div {} $ :style - {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 8) (:padding "|8px 10px") (:border-radius 16) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:flex-wrap |wrap) + {} (:display |flex) (:justify-content |space-between) (:align-items |center) (:gap 8) (:padding "|6px 8px") (:border-radius 12) (:background-color |#fbfbf8) (:border "|1px solid #d7d7cf") (:flex-wrap |wrap) div {} $ :style - {} (:display |flex) (:align-items |center) (:gap 8) (:flex-wrap |wrap) + {} (:display |flex) (:align-items |center) (:gap 6) (:flex-wrap |wrap) div {} $ :style - {} (:font-size 18) (:font-weight |700) (:line-height |1) + {} (:font-size 15) (:font-weight |700) (:line-height |1) <> "|EDN Renderer" div {} $ :style - {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#fff) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#8b6244) + {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) if (some? selected-channel) <> $ str "|Channel: " selected-channel - <> "|Choose channel" + <> "|No channel" div {} $ :style {} (:display |flex) (:gap 6) (:flex-wrap |wrap) (:align-items |center) (:justify-content |flex-end) div {} $ :style - {} (:padding "|4px 8px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#6b4a32) + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#f4f4f1) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) <> $ str "|Relay: " or (:status relay) |idle div {} $ :style - {} (:padding "|4px 8px") (:border-radius 999) (:background-color |#fff7ef) (:border "|1px solid #ead8c7") (:font-size 11) (:color |#6b4a32) + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#f4f4f1) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) if some? $ :layout-id renderer <> |Ready <> |Waiting - button $ {} (:class-name css/button) (:inner-text |Drawer) - :style $ {} (:padding "|6px 10px") + button $ {} (:class-name css/button) + :inner-text $ str "|History " (count history) + :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) (.show drawer-plugin d!) button $ {} (:class-name css/button) (:inner-text |Tips) - :style $ {} (:padding "|6px 10px") + :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) (.show help-alert d!) if-let relay-error $ :last-error relay div {} $ :style - {} (:padding "|10px 12px") (:border-radius 14) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 10) + {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 12) (:line-height |1.5) (:color |#a23f1a) (:margin-top 8) <> $ str "|Relay error: " relay-error if-let render-error $ :last-error renderer div {} $ :style - {} (:padding "|10px 12px") (:border-radius 14) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 13) (:line-height |1.6) (:color |#a23f1a) (:margin-top 10) + {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 12) (:line-height |1.5) (:color |#a23f1a) (:margin-top 8) <> $ str "|Validation error: " render-error if > (count channels) 1 div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) (:padding 14) (:border-radius 16) (:background-color |#fffaf4) (:border "|1px solid #ead8c7") (:margin-top 10) + {} (:display |flex) (:flex-direction |column) (:gap 6) (:padding 10) (:border-radius 12) (:background-color |#fbfbf8) (:border "|1px solid #d7d7cf") (:margin-top 8) div {} $ :style - {} (:font-size 13) (:font-weight |600) (:color |#6b4528) - <> "|Available channels" + {} (:font-size 12) (:font-weight |600) (:color |#4b5563) + <> |Channels list-> {} $ :style - {} (:display |flex) (:gap 8) (:flex-wrap |wrap) + {} (:display |flex) (:gap 6) (:flex-wrap |wrap) -> channels .to-list $ map-indexed fn (idx channel) [] idx $ button {} (:class-name css/button) (:inner-text channel) - :style $ {} (:padding "|6px 10px") - :background-color $ if (= channel selected-channel) |#e8b488 |#fff - :border $ if (= channel selected-channel) "|1px solid #cf8b5d" "|1px solid #ead8c7" + :style $ {} (:padding "|5px 8px") (:font-size 11) + :background-color $ if (= channel selected-channel) |#dce3ea |#ffffff + :border $ if (= channel selected-channel) "|1px solid #8a8f98" "|1px solid #d7d7cf" :on-click $ fn (e d!) d! $ :: :select-channel channel div {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 14) (:padding 18) (:border-radius 20) (:background-color |#fff7ef) (:border "|1px solid #ecdccf") (:min-height |420px) (:margin-top 10) + {} (:display |flex) (:flex-direction |column) (:gap 10) (:padding 12) (:border-radius 16) (:background-color |#fbfbf8) (:border "|1px solid #d7d7cf") (:min-height |420px) (:margin-top 8) div {} $ :style - {} (:font-size 18) (:font-weight |600) - <> "|Rendered Preview" + {} (:font-size 13) (:font-weight |600) (:color |#4b5563) + <> |Preview if (nil? selected-channel) div {} $ :style - {} (:padding 28) (:border-radius 16) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 14) (:line-height |1.7) (:color |#8b6c52) + {} (:padding 20) (:border-radius 14) (:border "|1px dashed #d7d7cf") (:background-color |#f5f5f1) (:font-size 13) (:line-height |1.6) (:color |#6b7280) if > (count channels) 1 - <> "|Select a channel to start receiving validated layouts." - <> "|Waiting for an active relay channel. Open this page with `?channel=` and send payloads with `edn-relay send --channel ''`." + <> "|Select a channel to start receiving layouts." + <> "|Waiting for a relay channel. Open this page with ?channel=." if-let layout $ :layout renderer comp-layout-node layout div {} $ :style - {} (:padding 28) (:border-radius 16) (:border "|1px dashed #d7bca4") (:background-color |#fffbf6) (:font-size 14) (:line-height |1.7) (:color |#8b6c52) - <> "|Waiting for a validated payload from the relay." + {} (:padding 20) (:border-radius 14) (:border "|1px dashed #d7d7cf") (:background-color |#f5f5f1) (:font-size 13) (:line-height |1.6) (:color |#6b7280) + <> "|Waiting for validated payloads." .render drawer-plugin .render help-alert :examples $ [] @@ -1080,30 +1112,61 @@ payload $ :payload frame request $ parse-renderer-request payload selected $ selected-relay-channel - if (= kind |hello-ok) - handle-channel-state! ws (:client_id frame) (:channels frame) - if (= kind |channel-state) - handle-channel-state! ws nil $ :channels frame - if (= kind |event) - if + summary $ if (= kind |hello-ok) "|Relay connected" + if (= kind |channel-state) "|Channel list updated" $ if (= kind |warning) + or (:error frame) "|Relay warning" + if (= kind |error) + or (:error frame) "|Relay error" + if (= kind |event) + tag-match request + (:help _) "|Renderer help" + (:skill _) "|Renderer skill" + (:status) "|Renderer status" + (:layout path) + str "|Layout summary " $ layout-path-display path + (:node path) + str "|Layout node " $ layout-path-display path + (:patch path _) + str "|Layout patch " $ layout-path-display path + (:replace path _) + str "|Layout replace " $ layout-path-display path + _ "|Layout payload" + str "|Relay " kind + do + dispatch! $ :: :record-relay-message + {} (:kind kind) + :channel $ :channel frame + :request-id $ :id frame + :matched? $ if (= kind |event) and (some? selected) = (:channel frame) selected - tag-match request - (:help _) (handle-renderer-event! ws frame) - (:skill _) (handle-renderer-event! ws frame) - (:status) (handle-renderer-event! ws frame) - (:layout _) (handle-renderer-event! ws frame) - (:node _) (handle-renderer-event! ws frame) - (:patch _ _) (handle-renderer-event! ws frame) - (:replace _ _) (handle-renderer-event! ws frame) - _ $ handle-genui-event! ws frame - do |ignored - if (= kind |warning) - .!warn js/console $ or (:error frame) "|Relay warning" - if (= kind |error) - dispatch! $ :: :relay-status |error - or (:error frame) "|Relay error" + , true + :summary summary + :raw raw + if (= kind |hello-ok) + handle-channel-state! ws (:client_id frame) (:channels frame) + if (= kind |channel-state) + handle-channel-state! ws nil $ :channels frame + if (= kind |event) + if + and (some? selected) + = (:channel frame) selected + tag-match request + (:help _) (handle-renderer-event! ws frame) + (:skill _) (handle-renderer-event! ws frame) + (:status) (handle-renderer-event! ws frame) + (:layout _) (handle-renderer-event! ws frame) + (:node _) (handle-renderer-event! ws frame) + (:patch _ _) (handle-renderer-event! ws frame) + (:replace _ _) (handle-renderer-event! ws frame) + _ $ handle-genui-event! ws frame do |ignored + if (= kind |warning) + .!warn js/console $ or (:error frame) "|Relay warning" + if (= kind |error) + dispatch! $ :: :relay-status |error + or (:error frame) "|Relay error" + do |ignored :examples $ [] |handle-renderer-event! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1553,6 +1616,8 @@ :relay $ {} (:status |idle) (:client-id nil) (:last-error nil) (:selected-channel nil) :channels $ [] :renderer $ {} (:layout nil) (:layout-dsl nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) + :history $ [] + :selected-history nil :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.schema) @@ -1585,6 +1650,18 @@ -> store assoc-in ([] :relay :status) status assoc-in ([] :relay :last-error) message + (:record-relay-message entry) + -> store + assoc-in ([] :renderer :history) + append + if + list? $ get-in store ([] :renderer :history) + get-in store $ [] :renderer :history + [] + , entry + assoc-in ([] :renderer :selected-history) entry + (:select-history entry) + assoc-in store ([] :renderer :selected-history) entry (:genui-applied request-id layout-id layout layout-dsl source) -> store assoc-in ([] :renderer :layout) layout From a4efb86db5e6594a70d9a4edcee9a9fdc3b2621a Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 29 May 2026 09:09:23 +0800 Subject: [PATCH 12/26] Fix history drawer selection check --- calcit.cirru | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index aa1756a..d1283a8 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -189,9 +189,9 @@ -> history .to-list $ map-indexed fn (idx item) let - active? $ and - some? selected-history - = (:raw item) (:raw selected-history) + active? $ if-let (current selected-history) + = (:raw item) (:raw current) + , false meta-channel $ or (:channel item) |session meta-request $ or (:request-id item) |- [] idx $ div From dc4d64aee1332ed8fbb4a63883e88c40b80cd33e Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 29 May 2026 20:33:46 +0800 Subject: [PATCH 13/26] Add saved report library workflow --- README.md | 21 ++++ calcit.cirru | 297 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 285 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 49e4ed0..63d8676 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,27 @@ Expected result: - the page shows the layout id and request id - the renderer preview updates immediately +## Save And Library + +When a meaningful layout is already loaded in the page, the top bar exposes 2 +extra actions: + +- `Save`: asks relay to persist the current report under `~/.config/ed-relay//` +- `Library`: opens the saved report list for the current channel and lets you load one entry back into the preview + +The browser does not write local files directly. Instead it sends a generic +relay request to the reserved internal channel `__relay_store__`, and relay +stores opaque Cirru EDN files on disk. + +Validated flow: + +1. send a layout into `genui` +2. click `Save` +3. confirm a new `.cirru` file appears under `~/.config/ed-relay/genui/` +4. click `Library` +5. click a saved report entry +6. confirm the preview loads the saved layout back into the page + ## Layout Inspection And Editing Once a layout is already loaded in the page, you can inspect and edit it with diff --git a/calcit.cirru b/calcit.cirru index d1283a8..24493a1 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -138,6 +138,13 @@ :history renderer [] selected-history $ :selected-history renderer + storage-status $ or (:storage-status renderer) |idle + storage-error $ :storage-error renderer + storage-entries $ if + list? $ :storage-entries renderer + :storage-entries renderer + [] + selected-storage $ :selected-storage renderer drawer-plugin $ use-drawer (>> states :drawer) {} :style $ {} (:width 820) (:min-width 0) (:max-width "|calc(100vw - 24px)") (:padding "|12px 12px 14px") (:gap 12) (:background-color |#f7f7f3) (:border-left "|1px solid #d7d7cf") (:box-shadow "|-12px 0 28px hsla(210, 8%, 18%, 0.12)") (:overflow |auto) @@ -168,6 +175,10 @@ {} $ :style {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) <> $ str "|Channel: " selected-channel + div + {} $ :style + {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Reports: " (count storage-entries) button $ {} (:class-name css/button) (:inner-text |Close) :style $ {} (:padding "|5px 9px") (:font-size 11) :on-click $ fn (e d!) (on-close d!) @@ -180,6 +191,44 @@ div {} $ :style {} (:font-size 12) (:font-weight |600) (:color |#4b5563) + <> "|Saved Reports" + if + > (count storage-entries) 0 + list-> + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height 220) (:overflow |auto) + -> storage-entries .to-list $ map-indexed + fn (idx item) + let + active? $ if-let (current selected-storage) + = (:name item) (:name current) + , false + [] idx $ div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :on-click $ fn (e d!) + d! $ :: :load-stored-report (:name item) + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:name item) "|Saved report" + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ or (:path item) | + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|No saved reports for current channel yet." + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#4b5563) (:margin-top 4) <> |Messages if > (count history) 0 @@ -311,6 +360,22 @@ :inner-text $ str "|History " (count history) :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) (.show drawer-plugin d!) + button $ {} + :class-name css/button + :inner-text |Library + :disabled $ nil? selected-channel + :style $ {} (:padding "|5px 8px") (:font-size 11) + :on-click $ fn (e d!) + do + (.show drawer-plugin d!) + d! $ :: :request-storage-list + button $ {} + :class-name css/button + :inner-text |Save + :disabled $ nil? (:layout-dsl renderer) + :style $ {} (:padding "|5px 8px") (:font-size 11) + :on-click $ fn (e d!) + d! $ :: :save-current-report button $ {} (:class-name css/button) (:inner-text |Tips) :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) (.show help-alert d!) @@ -326,6 +391,12 @@ {} $ :style {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 12) (:line-height |1.5) (:color |#a23f1a) (:margin-top 8) <> $ str "|Validation error: " render-error + if-let + current-storage-error storage-error + div + {} $ :style + {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff7e9) (:border "|1px solid #e8c48f") (:font-size 12) (:line-height |1.5) (:color |#8a541d) (:margin-top 8) + <> $ str "|Storage error: " current-storage-error if > (count channels) 1 div @@ -934,13 +1005,14 @@ {} (:name |node) (:summary "|用 `:op :node` + `:path \"1.2.3\"` 读取某个节点的完整 DSL。路径使用 1-based children 索引,`root` 表示整棵树;成功时同时返回 `:dsl`、`:source` 和 `:summary`。") {} (:name |patch) (:summary "|用 `:op :patch` + `:path` + `:changes` 局部合并节点属性,renderer 会重新验证整棵 layout;成功后立即局部更新页面,并返回目标节点新的 `:dsl` 与 `:summary`。") {} (:name |replace) (:summary "|用 `:op :replace` + `:path` + `:node` 直接替换某个节点 DSL,适合结构性修改;成功后同样会重新验证整棵树,并返回替换结果。") + {} (:name |storage) (:summary "|页面上的 `Save` 和 `Library` 通过 relay 保留 channel `__relay_store__` 工作。`Save` 会把当前 report 落到 `~/.config/ed-relay//`,`Library` 会列出同 channel 的 `.cirru` 文件并加载回当前预览。") :examples $ [] |relay-commands $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`editing`、`examples`、`layout`、`layout-patch-demo`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") + :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`storage`、`editing`、`examples`、`layout`、`layout-patch-demo`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") :examples $ [] |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -995,13 +1067,14 @@ def site $ {} (:storage-key |workflow) (:relay-url |ws://127.0.0.1:9100) :examples $ [] |skill-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def skill-overview "|使用 `edn-relay skill --channel ` 获取高层工作流;默认只返回总览。需要细节时追加 topic,例如 `workflow`、`help`、`layout`、`math`、`validation`;如果确实要整份文档,再用 `full`。") + :code $ quote (def skill-overview "|使用 `edn-relay skill --channel ` 获取高层工作流;默认只返回总览。需要细节时追加 topic,例如 `workflow`、`help`、`storage`、`layout`、`math`、`validation`;如果确实要整份文档,再用 `full`。") :examples $ [] |skill-sections $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def skill-sections $ [] {} (:name |workflow) (:title |Workflow) (:text "|1. 先 `edn-relay help --channel ` 看总览。\n2. 再用 `help --channel ` 把范围收窄到组件、协议或示例。\n3. 最后才运行 `edn-relay send --channel ...` 发 payload。") {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看局部编辑流程,用 `edn-relay help --channel editing`;要看具体案例,用 `edn-relay help --channel layout-patch-demo`。") + {} (:name |storage) (:title |Storage) (:text "|页面上已有有效 report 时,可以直接点 `Save` 把当前内容保存到 `~/.config/ed-relay//`。点 `Library` 会请求 relay 列出当前 channel 下的 `.cirru` 文件,并在点击条目后把保存时的 layout 加载回当前预览。CLI 想查这个能力时,优先用 `edn-relay help --channel storage`。") {} (:name |editing) (:title "|Editing Workflow") (:text "|先用 `edn-relay send --channel '{}` + `:op :layout` 获取隐藏细节的 summary tree,再用 `:op :node` + `:path` 读取完整 DSL。只改属性时优先 `:patch` + `:changes`,结构变化再用 `:replace`。每次成功都会回新的 `:layout_id`、目标节点 `:summary`,并在 `node/patch/replace` 返回完整 `:dsl`。") {} (:name |layout) (:title "|Layout Editing") (:text "|局部编辑默认走 `layout -> node -> patch/replace`。路径使用 1-based children 索引,`root` 表示整棵树;如果 agent 还不确定 payload 形状,先查 `layout-summary-demo`、`layout-node-demo`、`layout-patch-demo`。") {} (:name |math) (:title |MathML) (:text "|MathML Core 已通过 `math` 节点暴露。推荐顺序是先查 `edn-relay help --channel genui math`,再查 `math-fraction-demo`,最后发送 `:type |math` + `:expr` 的 Cirru EDN payload。") @@ -1026,6 +1099,30 @@ |*ws $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote (defatom *ws nil) :examples $ [] + |internal-storage-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def internal-storage-channel |__relay_store__) + :examples $ [] + |build-saved-report-entry $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn build-saved-report-entry (relay renderer) + let + channel $ :selected-channel relay + layout-dsl $ :layout-dsl renderer + layout-id $ :layout-id renderer + request-id $ :last-request renderer + saved-at $ .!toISOString (new js/Date) + source $ if (some? (:layout-source renderer)) (:layout-source renderer) + format-cirru-edn layout-dsl + {} + :kind :saved-report + :channel channel + :title $ str (or channel |report) | / (or layout-id |snapshot) + :layout_id layout-id + :request_id request-id + :saved_at saved-at + :layout layout-dsl + :source source + :examples $ [] |dispatch! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn dispatch! (op) @@ -1035,12 +1132,14 @@ let next-reel $ reel-updater updater @*reel op reset! *reel next-reel - when - and (some? @*ws) - tag-match op - (:select-channel _) true - _ false - sync-selected-channel! @*ws + if (some? @*ws) + tag-match op + (:select-channel _) (sync-selected-channel! @*ws) + (:request-storage-list) (request-storage-list! @*ws) + (:save-current-report) (request-storage-save! @*ws) + (:load-stored-report name) (request-storage-load! @*ws name) + _ $ , nil + , nil :examples $ [] |ensure-relay! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1112,26 +1211,31 @@ payload $ :payload frame request $ parse-renderer-request payload selected $ selected-relay-channel + pending-storage $ get-in @*reel ([] :store :renderer :storage-pending) summary $ if (= kind |hello-ok) "|Relay connected" - if (= kind |channel-state) "|Channel list updated" $ if (= kind |warning) - or (:error frame) "|Relay warning" - if (= kind |error) - or (:error frame) "|Relay error" - if (= kind |event) - tag-match request - (:help _) "|Renderer help" - (:skill _) "|Renderer skill" - (:status) "|Renderer status" - (:layout path) - str "|Layout summary " $ layout-path-display path - (:node path) - str "|Layout node " $ layout-path-display path - (:patch path _) - str "|Layout patch " $ layout-path-display path - (:replace path _) - str "|Layout replace " $ layout-path-display path - _ "|Layout payload" - str "|Relay " kind + if (= kind |channel-state) "|Channel list updated" + if (= kind |warning) + or (:error frame) "|Relay warning" + if (= kind |error) + or (:error frame) "|Relay error" + if + and (= kind |ack) (some? pending-storage) (= (:id frame) (:request-id pending-storage)) + str "|Storage " $ or (:op pending-storage) |request + if (= kind |event) + tag-match request + (:help _) "|Renderer help" + (:skill _) "|Renderer skill" + (:status) "|Renderer status" + (:layout path) + str "|Layout summary " $ layout-path-display path + (:node path) + str "|Layout node " $ layout-path-display path + (:patch path _) + str "|Layout patch " $ layout-path-display path + (:replace path _) + str "|Layout replace " $ layout-path-display path + _ "|Layout payload" + str "|Relay " kind do dispatch! $ :: :record-relay-message {} (:kind kind) @@ -1161,12 +1265,15 @@ (:replace _ _) (handle-renderer-event! ws frame) _ $ handle-genui-event! ws frame do |ignored - if (= kind |warning) - .!warn js/console $ or (:error frame) "|Relay warning" - if (= kind |error) - dispatch! $ :: :relay-status |error - or (:error frame) "|Relay error" - do |ignored + if + and (= kind |ack) (some? pending-storage) (= (:id frame) (:request-id pending-storage)) + handle-storage-ack! frame + if (= kind |warning) + .!warn js/console $ or (:error frame) "|Relay warning" + if (= kind |error) + dispatch! $ :: :relay-status |error + or (:error frame) "|Relay error" + do |ignored :examples $ [] |handle-renderer-event! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1481,6 +1588,50 @@ turn-string x , nil :examples $ [] + |request-storage-list! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn request-storage-list! (ws) + if-let + channel $ selected-relay-channel + let + request-id $ str |storage-list- (.!now js/Date) + do + dispatch! $ :: :storage-pending request-id |list + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :list) (:channel channel)) + dispatch! $ :: :storage-failed nil "|Select a channel before browsing saved reports" + :examples $ [] + |request-storage-load! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn request-storage-load! (ws name) + if-let + channel $ selected-relay-channel + let + request-id $ str |storage-load- (.!now js/Date) + do + dispatch! $ :: :storage-pending request-id |load + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :load) (:channel channel) (:name name)) + dispatch! $ :: :storage-failed nil "|Select a channel before loading saved reports" + :examples $ [] + |request-storage-save! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn request-storage-save! (ws) + let + relay $ get-in @*reel ([] :store :relay) + renderer $ get-in @*reel ([] :store :renderer) + if + and (some? (:selected-channel relay)) (some? (:layout-dsl renderer)) + let + request-id $ str |storage-save- (.!now js/Date) + entry $ build-saved-report-entry relay renderer + file-name $ str + or (:selected-channel relay) |report + , |- + or (:layout-id renderer) (:last-request renderer) |snapshot + do + dispatch! $ :: :storage-pending request-id |save + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :save) (:channel (:selected-channel relay)) (:name file-name) (:entry entry)) + dispatch! $ :: :storage-failed nil "|A validated layout is required before saving a report" + :examples $ [] |reload! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn reload! () $ if (nil? build-errors) @@ -1518,6 +1669,42 @@ :code $ quote defn selected-relay-channel () $ get-in @*reel ([] :store :relay :selected-channel) :examples $ [] + |handle-storage-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn handle-storage-ack! (frame) + let + request-id $ :id frame + pending $ get-in @*reel ([] :store :renderer :storage-pending) + when + and (some? pending) (= request-id (:request-id pending)) + if (:ok frame) + try + let + payload $ :payload frame + kind $ protocol-name (:kind payload) + if (= kind |storage-list) + dispatch! $ :: :storage-listed request-id + if (list? (:entries payload)) (:entries payload) ([]) + if (= kind |storage-save) + do + dispatch! $ :: :storage-saved request-id payload + when (some? @*ws) (request-storage-list! @*ws) + if (= kind |storage-load) + let + entry $ :entry payload + layout-dsl $ :layout entry + source $ or (:source payload) (:source entry) (format-cirru-edn layout-dsl) + layout-id $ or (:layout_id entry) (str |saved- (:name payload)) + layout $ validate-layout layout-dsl + dispatch! $ :: :storage-loaded request-id payload layout-id layout layout-dsl source + dispatch! $ :: :storage-failed request-id "|Unsupported storage ack payload" + fn (error) + let + message $ if (some? (.-message error)) (.-message error) (str error) + dispatch! $ :: :storage-failed request-id message + dispatch! $ :: :storage-failed request-id + or (:error frame) "|Storage request failed" + :examples $ [] |send-genui-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn send-genui-ack! (ws request-id ok? payload error-message) @@ -1618,6 +1805,11 @@ :renderer $ {} (:layout nil) (:layout-dsl nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) :history $ [] :selected-history nil + :storage-status |idle + :storage-error nil + :storage-pending nil + :storage-entries $ [] + :selected-storage nil :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.schema) @@ -1646,6 +1838,11 @@ assoc-in ([] :renderer :layout-source) | assoc-in ([] :renderer :last-request) nil assoc-in ([] :renderer :last-error) nil + assoc-in ([] :renderer :storage-status) |idle + assoc-in ([] :renderer :storage-error) nil + assoc-in ([] :renderer :storage-pending) nil + assoc-in ([] :renderer :storage-entries) ([]) + assoc-in ([] :renderer :selected-storage) nil (:relay-status status message) -> store assoc-in ([] :relay :status) status @@ -1662,6 +1859,40 @@ assoc-in ([] :renderer :selected-history) entry (:select-history entry) assoc-in store ([] :renderer :selected-history) entry + (:storage-pending request-id op-name) + -> store + assoc-in ([] :renderer :storage-pending) $ {} (:request-id request-id) (:op op-name) + assoc-in ([] :renderer :storage-status) |working + assoc-in ([] :renderer :storage-error) nil + (:storage-saved request-id entry) + -> store + assoc-in ([] :renderer :storage-pending) nil + assoc-in ([] :renderer :storage-status) |saved + assoc-in ([] :renderer :storage-error) nil + assoc-in ([] :renderer :selected-storage) entry + (:storage-listed request-id entries) + -> store + assoc-in ([] :renderer :storage-pending) nil + assoc-in ([] :renderer :storage-status) |ready + assoc-in ([] :renderer :storage-error) nil + assoc-in ([] :renderer :storage-entries) entries + (:storage-loaded request-id entry layout-id layout layout-dsl source) + -> store + assoc-in ([] :renderer :storage-pending) nil + assoc-in ([] :renderer :storage-status) |loaded + assoc-in ([] :renderer :storage-error) nil + assoc-in ([] :renderer :selected-storage) entry + assoc-in ([] :renderer :layout) layout + assoc-in ([] :renderer :layout-dsl) layout-dsl + assoc-in ([] :renderer :layout-id) layout-id + assoc-in ([] :renderer :layout-source) source + assoc-in ([] :renderer :last-request) request-id + assoc-in ([] :renderer :last-error) nil + (:storage-failed request-id message) + -> store + assoc-in ([] :renderer :storage-pending) nil + assoc-in ([] :renderer :storage-status) |error + assoc-in ([] :renderer :storage-error) message (:genui-applied request-id layout-id layout layout-dsl source) -> store assoc-in ([] :renderer :layout) layout From ebbd93a0d56f72494f213b997cafee33400f1db3 Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 02:45:23 +0800 Subject: [PATCH 14/26] fix library panel again --- calcit.cirru | 265 +++++++++++++++++++++++++++++---------------------- 1 file changed, 153 insertions(+), 112 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 24493a1..db51c3d 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -145,6 +145,7 @@ :storage-entries renderer [] selected-storage $ :selected-storage renderer + drawer-view $ or (:drawer-view renderer) |history drawer-plugin $ use-drawer (>> states :drawer) {} :style $ {} (:width 820) (:min-width 0) (:max-width "|calc(100vw - 24px)") (:padding "|12px 12px 14px") (:gap 12) (:background-color |#f7f7f3) (:border-left "|1px solid #d7d7cf") (:box-shadow "|-12px 0 28px hsla(210, 8%, 18%, 0.12)") (:overflow |auto) @@ -165,7 +166,7 @@ div {} $ :style {} (:font-size 15) (:font-weight |700) (:color |#1f2933) - <> |History + <> $ if (= drawer-view |library) |Library |History div {} $ :style {} (:padding "|2px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) @@ -191,84 +192,81 @@ div {} $ :style {} (:font-size 12) (:font-weight |600) (:color |#4b5563) - <> "|Saved Reports" - if - > (count storage-entries) 0 - list-> - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height 220) (:overflow |auto) - -> storage-entries .to-list $ map-indexed - fn (idx item) - let - active? $ if-let (current selected-storage) - = (:name item) (:name current) - , false - [] idx $ div - {} - :style $ {} (:padding "|8px 10px") (:border-radius 12) - :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" - :background-color $ if active? |#eef1f4 |#ffffff - :cursor |pointer - :display |flex - :flex-direction |column - :gap 4 - :on-click $ fn (e d!) - d! $ :: :load-stored-report (:name item) - div - {} $ :style - {} (:font-size 12) (:font-weight |600) (:color |#1f2933) - <> $ or (:name item) "|Saved report" - div - {} $ :style - {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) - <> $ or (:path item) | - div - {} $ :style - {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|No saved reports for current channel yet." - div - {} $ :style - {} (:font-size 12) (:font-weight |600) (:color |#4b5563) (:margin-top 4) - <> |Messages - if - > (count history) 0 - list-> - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) - -> history .to-list $ map-indexed - fn (idx item) - let - active? $ if-let (current selected-history) - = (:raw item) (:raw current) - , false - meta-channel $ or (:channel item) |session - meta-request $ or (:request-id item) |- - [] idx $ div - {} - :style $ {} (:padding "|8px 10px") (:border-radius 12) - :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" - :background-color $ if active? |#eef1f4 |#ffffff - :cursor |pointer - :display |flex - :flex-direction |column - :gap 4 - :opacity $ if (:matched? item) |1 |0.72 - :on-click $ fn (e d!) - d! $ :: :select-history item - div - {} $ :style - {} (:font-size 12) (:font-weight |600) (:color |#1f2933) - <> $ or (:summary item) |Message - div - {} $ :style - {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) - <> $ str - or (:kind item) |unknown - , | / meta-channel | / meta-request - div - {} $ :style - {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|No relay messages yet." + <> $ if (= drawer-view |library) "|Saved Reports" |Messages + if (= drawer-view |library) + if + > (count storage-entries) 0 + list-> + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) + -> storage-entries .to-list $ map-indexed + fn (idx item) + let + active? $ if-let (current selected-storage) + = (:name item) (:name current) + , false + [] idx $ div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :on-click $ fn (e d!) + d! $ :: :load-stored-report (:name item) + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:name item) "|Saved report" + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ or (:path item) | + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|No saved reports for current channel yet." + if + > (count history) 0 + list-> + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) + -> history .to-list $ map-indexed + fn (idx item) + let + active? $ if-let (current selected-history) + = (:raw item) (:raw current) + , false + meta-channel $ or (:channel item) |session + meta-request $ or (:request-id item) |- + [] idx $ div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :opacity $ if (:matched? item) |1 |0.72 + :on-click $ fn (e d!) + d! $ :: :select-history item + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:summary item) |Message + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ str + or (:kind item) |unknown + , | / meta-channel | / meta-request + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|No relay messages yet." div {} $ :style {} (:flex |1) (:min-width 280) (:display |flex) (:flex-direction |column) (:gap 8) @@ -276,48 +274,82 @@ {} $ :style {} (:font-size 12) (:font-weight |600) (:color |#4b5563) <> |Detail - if-let (item selected-history) - div - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 8) - div - {} $ :style - {} (:font-size 14) (:font-weight |700) (:color |#1f2933) - <> $ or (:summary item) |Message + if (= drawer-view |library) + if-let (item selected-storage) div {} $ :style - {} (:display |flex) (:gap 6) (:flex-wrap |wrap) + {} (:display |flex) (:flex-direction |column) (:gap 8) div {} $ :style - {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) - <> $ str "|Kind: " - or (:kind item) |unknown + {} (:font-size 14) (:font-weight |700) (:color |#1f2933) + <> $ or (:name item) "|Saved report" div {} $ :style - {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) - <> $ str "|Channel: " - or (:channel item) |session + {} (:display |flex) (:gap 6) (:flex-wrap |wrap) + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Status: " storage-status + if (some? selected-channel) + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Channel: " selected-channel + textarea $ {} + :value $ format-cirru-edn $ {} + :name $ or (:name item) |- + :path $ or (:path item) |- + :status storage-status + :read-only true + :spell-check false + :placeholder "|Saved report detail" + :style $ {} (:width |100%) (:min-height "|calc(100vh - 320px)") (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #d7d7cf") (:background-color |#ffffff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|Click a saved report to load it into the current preview." + if-let (item selected-history) + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 8) div {} $ :style - {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) - <> $ str "|Request: " - or (:request-id item) |- - if (:matched? item) - div $ {} + {} (:font-size 14) (:font-weight |700) (:color |#1f2933) + <> $ or (:summary item) |Message div {} $ :style - {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#f4f4f1) (:border "|1px solid #deded8") (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|This message did not match the current channel filter." - textarea $ {} - :value $ or (:raw item) | - :read-only true - :spell-check false - :placeholder "|Relay frame detail" - :style $ {} (:width |100%) (:min-height "|calc(100vh - 320px)") (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #d7d7cf") (:background-color |#ffffff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) - div - {} $ :style - {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|Click a message to inspect its raw relay frame." + {} (:display |flex) (:gap 6) (:flex-wrap |wrap) + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Kind: " + or (:kind item) |unknown + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Channel: " + or (:channel item) |session + div + {} $ :style + {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) + <> $ str "|Request: " + or (:request-id item) |- + if (:matched? item) + div $ {} + div + {} $ :style + {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#f4f4f1) (:border "|1px solid #deded8") (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|This message did not match the current channel filter." + textarea $ {} + :value $ or (:raw item) | + :read-only true + :spell-check false + :placeholder "|Relay frame detail" + :style $ {} (:width |100%) (:min-height "|calc(100vh - 320px)") (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #d7d7cf") (:background-color |#ffffff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|Click a message to inspect its raw relay frame." when dev? $ comp-reel (>> states :reel) reel ({}) help-alert $ use-alert (>> states :help) {} $ :text "|Use History to inspect recent relay frames and the raw Cirru EDN being rendered." @@ -359,7 +391,10 @@ button $ {} (:class-name css/button) :inner-text $ str "|History " (count history) :style $ {} (:padding "|5px 8px") (:font-size 11) - :on-click $ fn (e d!) (.show drawer-plugin d!) + :on-click $ fn (e d!) + do + d! $ :: :open-history-drawer + (.show drawer-plugin d!) button $ {} :class-name css/button :inner-text |Library @@ -367,6 +402,7 @@ :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) do + d! $ :: :open-library-drawer (.show drawer-plugin d!) d! $ :: :request-storage-list button $ {} @@ -1805,6 +1841,7 @@ :renderer $ {} (:layout nil) (:layout-dsl nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) :history $ [] :selected-history nil + :drawer-view |history :storage-status |idle :storage-error nil :storage-pending nil @@ -1859,6 +1896,10 @@ assoc-in ([] :renderer :selected-history) entry (:select-history entry) assoc-in store ([] :renderer :selected-history) entry + (:open-history-drawer) + assoc-in store ([] :renderer :drawer-view) |history + (:open-library-drawer) + assoc-in store ([] :renderer :drawer-view) |library (:storage-pending request-id op-name) -> store assoc-in ([] :renderer :storage-pending) $ {} (:request-id request-id) (:op op-name) From ecdede0f546a989545917199086c1d994a1001e6 Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 03:12:47 +0800 Subject: [PATCH 15/26] Fix renderer history drawer and storage flow --- calcit.cirru | 263 +++++++++++++++++++++++++++------------------------ 1 file changed, 140 insertions(+), 123 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index db51c3d..22d3858 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -296,10 +296,11 @@ {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) <> $ str "|Channel: " selected-channel textarea $ {} - :value $ format-cirru-edn $ {} - :name $ or (:name item) |- - :path $ or (:path item) |- - :status storage-status + :value $ format-cirru-edn + {} + :name $ or (:name item) |- + :path $ or (:path item) |- + :status storage-status :read-only true :spell-check false :placeholder "|Saved report detail" @@ -394,20 +395,16 @@ :on-click $ fn (e d!) do d! $ :: :open-history-drawer - (.show drawer-plugin d!) - button $ {} - :class-name css/button - :inner-text |Library + .show drawer-plugin d! + button $ {} (:class-name css/button) (:inner-text |Library) :disabled $ nil? selected-channel :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) do d! $ :: :open-library-drawer - (.show drawer-plugin d!) + .show drawer-plugin d! d! $ :: :request-storage-list - button $ {} - :class-name css/button - :inner-text |Save + button $ {} (:class-name css/button) (:inner-text |Save) :disabled $ nil? (:layout-dsl renderer) :style $ {} (:padding "|5px 8px") (:font-size 11) :on-click $ fn (e d!) @@ -427,8 +424,7 @@ {} $ :style {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff1ec) (:border "|1px solid #f0c4b4") (:font-size 12) (:line-height |1.5) (:color |#a23f1a) (:margin-top 8) <> $ str "|Validation error: " render-error - if-let - current-storage-error storage-error + if-let (current-storage-error storage-error) div {} $ :style {} (:padding "|8px 10px") (:border-radius 12) (:background-color |#fff7e9) (:border "|1px solid #e8c48f") (:font-size 12) (:line-height |1.5) (:color |#8a541d) (:margin-top 8) @@ -1135,9 +1131,6 @@ |*ws $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote (defatom *ws nil) :examples $ [] - |internal-storage-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def internal-storage-channel |__relay_store__) - :examples $ [] |build-saved-report-entry $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn build-saved-report-entry (relay renderer) @@ -1147,12 +1140,12 @@ layout-id $ :layout-id renderer request-id $ :last-request renderer saved-at $ .!toISOString (new js/Date) - source $ if (some? (:layout-source renderer)) (:layout-source renderer) + source $ if + some? $ :layout-source renderer + :layout-source renderer format-cirru-edn layout-dsl - {} - :kind :saved-report - :channel channel - :title $ str (or channel |report) | / (or layout-id |snapshot) + {} (:kind :saved-report) (:channel channel) + :title $ str (or channel |report) | / (or layout-id |snapshot) :layout_id layout-id :request_id request-id :saved_at saved-at @@ -1174,7 +1167,7 @@ (:request-storage-list) (request-storage-list! @*ws) (:save-current-report) (request-storage-save! @*ws) (:load-stored-report name) (request-storage-load! @*ws name) - _ $ , nil + _ nil , nil :examples $ [] |ensure-relay! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -1249,29 +1242,29 @@ selected $ selected-relay-channel pending-storage $ get-in @*reel ([] :store :renderer :storage-pending) summary $ if (= kind |hello-ok) "|Relay connected" - if (= kind |channel-state) "|Channel list updated" - if (= kind |warning) - or (:error frame) "|Relay warning" - if (= kind |error) - or (:error frame) "|Relay error" - if - and (= kind |ack) (some? pending-storage) (= (:id frame) (:request-id pending-storage)) - str "|Storage " $ or (:op pending-storage) |request - if (= kind |event) - tag-match request - (:help _) "|Renderer help" - (:skill _) "|Renderer skill" - (:status) "|Renderer status" - (:layout path) - str "|Layout summary " $ layout-path-display path - (:node path) - str "|Layout node " $ layout-path-display path - (:patch path _) - str "|Layout patch " $ layout-path-display path - (:replace path _) - str "|Layout replace " $ layout-path-display path - _ "|Layout payload" - str "|Relay " kind + if (= kind |channel-state) "|Channel list updated" $ if (= kind |warning) + or (:error frame) "|Relay warning" + if (= kind |error) + or (:error frame) "|Relay error" + if + and (= kind |ack) (some? pending-storage) + = (:id frame) (:request-id pending-storage) + str "|Storage " $ or (:op pending-storage) |request + if (= kind |event) + tag-match request + (:help _) "|Renderer help" + (:skill _) "|Renderer skill" + (:status) "|Renderer status" + (:layout path) + str "|Layout summary " $ layout-path-display path + (:node path) + str "|Layout node " $ layout-path-display path + (:patch path _) + str "|Layout patch " $ layout-path-display path + (:replace path _) + str "|Layout replace " $ layout-path-display path + _ "|Layout payload" + str "|Relay " kind do dispatch! $ :: :record-relay-message {} (:kind kind) @@ -1302,7 +1295,8 @@ _ $ handle-genui-event! ws frame do |ignored if - and (= kind |ack) (some? pending-storage) (= (:id frame) (:request-id pending-storage)) + and (= kind |ack) (some? pending-storage) + = (:id frame) (:request-id pending-storage) handle-storage-ack! frame if (= kind |warning) .!warn js/console $ or (:error frame) "|Relay warning" @@ -1396,6 +1390,53 @@ str error do (.!warn js/console "|[renderer] request failed" error) (send-genui-ack! ws request-id false nil message) :examples $ [] + |handle-storage-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn handle-storage-ack! (frame) + let + request-id $ :id frame + pending $ get-in @*reel ([] :store :renderer :storage-pending) + when + and (some? pending) + = request-id $ :request-id pending + if (:ok frame) + try + let + payload $ :payload frame + kind $ protocol-name (:kind payload) + if (= kind |storage-list) + dispatch! $ :: :storage-listed request-id + if + list? $ :entries payload + :entries payload + [] + if (= kind |storage-save) + do + dispatch! $ :: :storage-saved request-id payload + when (some? @*ws) (request-storage-list! @*ws) + if (= kind |storage-load) + let + entry $ :entry payload + layout-dsl $ :layout entry + source $ or (:source payload) (:source entry) (format-cirru-edn layout-dsl) + layout-id $ or (:layout_id entry) + str |saved- $ :name payload + layout $ validate-layout layout-dsl + dispatch! $ :: :storage-loaded request-id payload layout-id layout layout-dsl source + dispatch! $ :: :storage-failed request-id "|Unsupported storage ack payload" + fn (error) + let + message $ if + some? $ .-message error + .-message error + str error + dispatch! $ :: :storage-failed request-id message + dispatch! $ :: :storage-failed request-id + or (:error frame) "|Storage request failed" + :examples $ [] + |internal-storage-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote (def internal-storage-channel |__relay_store__) + :examples $ [] |layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn layout-node-at-path (node path) @@ -1624,6 +1665,39 @@ turn-string x , nil :examples $ [] + |reload! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn reload! () $ if (nil? build-errors) + do (remove-watch *reel :changes) (clear-cache!) + add-watch *reel :changes $ fn (reel prev) (render-app!) + reset! *reel $ refresh-reel @*reel schema/store updater + ensure-relay! + hud! |ok~ |Ok + hud! |error build-errors + :examples $ [] + |render-app! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn render-app! () $ render! mount-target (comp-container @*reel) dispatch! + :examples $ [] + |replace-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn replace-layout-node-at-path (node path next-node) + if (empty? path) next-node $ let + children $ or (:children node) ([]) + step $ first path + if + not $ list? children + raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" + if + nil? $ pick-layout-child children step + raise $ str "|Missing layout node at path " (layout-path-display path) + assoc node :children $ -> children .to-list + map-indexed $ fn (idx child) + if + = (inc idx) step + replace-layout-node-at-path child (rest path) next-node + , child + :examples $ [] |request-storage-list! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn request-storage-list! (ws) @@ -1633,7 +1707,8 @@ request-id $ str |storage-list- (.!now js/Date) do dispatch! $ :: :storage-pending request-id |list - send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :list) (:channel channel)) + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) + :payload $ {} (:op :list) (:channel channel) dispatch! $ :: :storage-failed nil "|Select a channel before browsing saved reports" :examples $ [] |request-storage-load! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -1645,7 +1720,8 @@ request-id $ str |storage-load- (.!now js/Date) do dispatch! $ :: :storage-pending request-id |load - send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :load) (:channel channel) (:name name)) + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) + :payload $ {} (:op :load) (:channel channel) (:name name) dispatch! $ :: :storage-failed nil "|Select a channel before loading saved reports" :examples $ [] |request-storage-save! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -1655,92 +1731,29 @@ relay $ get-in @*reel ([] :store :relay) renderer $ get-in @*reel ([] :store :renderer) if - and (some? (:selected-channel relay)) (some? (:layout-dsl renderer)) + and + some? $ :selected-channel relay + some? $ :layout-dsl renderer let request-id $ str |storage-save- (.!now js/Date) entry $ build-saved-report-entry relay renderer file-name $ str or (:selected-channel relay) |report , |- - or (:layout-id renderer) (:last-request renderer) |snapshot + or (:layout-id renderer) (:last-request renderer) |snapshot do dispatch! $ :: :storage-pending request-id |save - send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) (:payload $ {} (:op :save) (:channel (:selected-channel relay)) (:name file-name) (:entry entry)) + send-relay-frame! ws $ {} (:kind :request) (:id request-id) (:channel internal-storage-channel) + :payload $ {} (:op :save) + :channel $ :selected-channel relay + :name file-name + :entry entry dispatch! $ :: :storage-failed nil "|A validated layout is required before saving a report" :examples $ [] - |reload! $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn reload! () $ if (nil? build-errors) - do (remove-watch *reel :changes) (clear-cache!) - add-watch *reel :changes $ fn (reel prev) (render-app!) - reset! *reel $ refresh-reel @*reel schema/store updater - ensure-relay! - hud! |ok~ |Ok - hud! |error build-errors - :examples $ [] - |render-app! $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn render-app! () $ render! mount-target (comp-container @*reel) dispatch! - :examples $ [] - |replace-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn replace-layout-node-at-path (node path next-node) - if (empty? path) next-node $ let - children $ or (:children node) ([]) - step $ first path - if - not $ list? children - raise $ str "|Path " (layout-path-display path) "| requires a parent with :children" - if - nil? $ pick-layout-child children step - raise $ str "|Missing layout node at path " (layout-path-display path) - assoc node :children $ -> children .to-list - map-indexed $ fn (idx child) - if - = (inc idx) step - replace-layout-node-at-path child (rest path) next-node - , child - :examples $ [] |selected-relay-channel $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn selected-relay-channel () $ get-in @*reel ([] :store :relay :selected-channel) :examples $ [] - |handle-storage-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote - defn handle-storage-ack! (frame) - let - request-id $ :id frame - pending $ get-in @*reel ([] :store :renderer :storage-pending) - when - and (some? pending) (= request-id (:request-id pending)) - if (:ok frame) - try - let - payload $ :payload frame - kind $ protocol-name (:kind payload) - if (= kind |storage-list) - dispatch! $ :: :storage-listed request-id - if (list? (:entries payload)) (:entries payload) ([]) - if (= kind |storage-save) - do - dispatch! $ :: :storage-saved request-id payload - when (some? @*ws) (request-storage-list! @*ws) - if (= kind |storage-load) - let - entry $ :entry payload - layout-dsl $ :layout entry - source $ or (:source payload) (:source entry) (format-cirru-edn layout-dsl) - layout-id $ or (:layout_id entry) (str |saved- (:name payload)) - layout $ validate-layout layout-dsl - dispatch! $ :: :storage-loaded request-id payload layout-id layout layout-dsl source - dispatch! $ :: :storage-failed request-id "|Unsupported storage ack payload" - fn (error) - let - message $ if (some? (.-message error)) (.-message error) (str error) - dispatch! $ :: :storage-failed request-id message - dispatch! $ :: :storage-failed request-id - or (:error frame) "|Storage request failed" - :examples $ [] |send-genui-ack! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn send-genui-ack! (ws request-id ok? payload error-message) @@ -1813,7 +1826,7 @@ let client-id $ get-in @*reel ([] :store :relay :client-id) selected $ selected-relay-channel - channels $ if (some? selected) ([] selected) ([]) + channels $ if (some? selected) ([] selected internal-storage-channel) ([] internal-storage-channel) send-relay-frame! ws $ {} (:kind :hello) (:role :receiver) (:client_id client-id) (:channels channels) :examples $ [] :ns $ %{} :NsEntry (:doc |) @@ -1900,9 +1913,13 @@ assoc-in store ([] :renderer :drawer-view) |history (:open-library-drawer) assoc-in store ([] :renderer :drawer-view) |library + (:request-storage-list) store + (:save-current-report) store + (:load-stored-report _) store (:storage-pending request-id op-name) -> store - assoc-in ([] :renderer :storage-pending) $ {} (:request-id request-id) (:op op-name) + assoc-in ([] :renderer :storage-pending) + {} (:request-id request-id) (:op op-name) assoc-in ([] :renderer :storage-status) |working assoc-in ([] :renderer :storage-error) nil (:storage-saved request-id entry) From 8f3eb1ec3a22678b880f3a2600992f5a4c06bf53 Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 10:35:54 +0800 Subject: [PATCH 16/26] upgrade calcit deps --- deps.cirru | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deps.cirru b/deps.cirru index d1d3aa7..2494c54 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,6 +1,7 @@ {} (:calcit-version |0.12.35) - :dependencies $ {} (|Respo/alerts.calcit |0.10.10) (|Respo/reel.calcit |0.6.3) + :dependencies $ {} (|Respo/alerts.calcit |0.10.11) + |Respo/reel.calcit |0.6.3 |Respo/respo-ui.calcit |0.6.4 - |Respo/respo.calcit |0.16.43 + |Respo/respo.calcit |0.16.45 |calcit-lang/memof |0.0.23 From 591e0661d0d6e03986889229bad5cdd6fd74a40b Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 15:18:48 +0800 Subject: [PATCH 17/26] fix library UI, add test guide file --- README.md | 2 + TESTING.md | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++ calcit.cirru | 268 +++++++++++++++++++++++++----------------- 3 files changed, 486 insertions(+), 109 deletions(-) create mode 100644 TESTING.md diff --git a/README.md b/README.md index 63d8676..09693dc 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,8 @@ Compile the Calcit app with: cr js ``` +For a dedicated renderer, CLI, and browser validation checklist, see [TESTING.md](TESTING.md). + For isolated browser validation, use the `chrome-devtools` workflow described in [Agents.md](Agents.md). diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..b22af50 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,325 @@ +# Testing Guide + +This repository already had validation notes spread across [README.md](README.md), [SKILL.md](SKILL.md), and [Agents.md](Agents.md), but it did not have one dedicated test workflow document. + +This file is the canonical checklist for validating the renderer end to end. + +## Scope + +The validation flow is split into 3 layers: + +1. Renderer build validation +2. Relay CLI capability validation +3. Browser UI and interaction validation + +Run them in this order so failures stay easy to localize. + +## 1. Prerequisites + +Renderer workspace: + +```bash +cr js +yarn vite --host 127.0.0.1 --port 3010 +``` + +Relay workspace: + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- serve --bind 127.0.0.1:9100 +``` + +Browser: + +```bash +chrome-devtools status +chrome-devtools start --headless false +chrome-devtools new_page 'http://127.0.0.1:3010?channel=genui&server=ws://127.0.0.1:9100' +``` + +Expected readiness signals: + +- `cr js` succeeds +- Vite serves the page on `127.0.0.1:3010` +- relay is listening on `127.0.0.1:9100` +- browser page shows channel `genui` +- browser console has no fatal initialization error + +## 2. Renderer Build Validation + +Use this as the narrowest pre-flight check after any Calcit change. + +```bash +cr js +``` + +Use this before browser checks when dependencies or bundling-related code changed. + +```bash +yarn vite build --base=./ +``` + +Expected result: + +- build succeeds +- generated `js-out/` files update cleanly +- existing warnings may be recorded separately, but new failures must be treated as regressions + +## 3. Relay CLI Validation + +These checks verify that the renderer is reachable and that protocol-facing commands still work. + +### 3.1 Status + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- status --server ws://127.0.0.1:9100 --channel genui +``` + +Expected result: + +- returns renderer name `edn-renderer` +- includes page URL +- includes commands such as `send`, `help`, `skill`, `status`, `open` + +### 3.2 Help and Skill + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- help --server ws://127.0.0.1:9100 --channel genui +cargo run -- help --server ws://127.0.0.1:9100 --channel genui components +cargo run -- skill --server ws://127.0.0.1:9100 --channel genui workflow +``` + +Expected result: + +- `help` returns a short overview by default +- topic queries return narrowed content instead of the whole manual +- `skill workflow` returns the intended workflow summary + +### 3.3 Send Layout Payloads + +Simple smoke payload: + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :type |card + :text "|CLI Demo" + :children $ [] + {} (:type |badge) (:text |preview) + {} (:type |text) (:text "|Hello from CLI") +' +``` + +Expected result: + +- relay prints an `ack` with `:status :ok` +- payload includes a `:layout_id` +- browser preview updates immediately + +## 4. Browser UI Basic Validation + +Once the page is open, validate the visible browser workflow. + +### 4.1 Top bar and preview + +Check these items: + +- channel badge shows the selected channel +- relay badge becomes `ready` +- preview area switches from waiting state to rendered layout after a send +- `History` count increases when frames arrive + +### 4.2 History drawer + +Check these items: + +- clicking `History` opens the drawer +- left list shows recent relay frames +- selecting one entry updates the detail panel +- raw relay frame text is visible in the detail textarea + +### 4.3 Library drawer + +Check these items: + +- `Save` is enabled when a valid layout is loaded +- clicking `Save` persists an entry for the current channel +- clicking `Library` opens the saved report list +- the drawer shows one local `current workspace` snapshot entry when a workspace layout exists +- clicking a saved report switches the preview to that saved layout +- clicking `current workspace` switches the preview back to the local workspace snapshot +- `current workspace` is only a local item and must not increase the saved report count + +## 5. Layout Editing Validation + +These checks verify the request-response editing flow after a non-trivial layout has already been loaded. + +### 5.1 Summary tree + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :op :layout +' +``` + +Expected result: + +- returns `:kind :layout` +- includes `:summary` +- summary nodes contain `:path`, `:type`, and `:child-count` + +### 5.2 Read one node + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :op :node + :path |1 +' +``` + +Expected result: + +- returns `:kind :node` +- includes `:dsl`, `:source`, and `:summary` + +### 5.3 Patch one node + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :op :patch + :path |1 + :changes $ {} + :text |Updated title +' +``` + +Expected result: + +- returns `:kind :patch` +- preview updates without resending the whole layout +- history records the patch request and ack + +### 5.4 Replace one node + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :op :replace + :path |1.2 + :node $ {} + :type |markdown + :text "|## Replaced\n- alpha\n- beta" +' +``` + +Expected result: + +- returns `:kind :replace` +- browser preview updates in place +- response includes the updated node summary + +## 6. Complex Scenario Cases + +Use these payloads when validating richer renderer capabilities after substantial changes. + +### 6.1 Mixed dashboard case + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :type |column + :children $ [] + {} (:type |card) (:text "|Weekly Report") + :children $ [] + {} (:type |text) (:text "|System health summary") + {} (:type |badge) (:text |healthy) + {} (:type |divider) + {} (:type |markdown) (:text "|## Notes\n- renderer ok\n- relay ok\n> investigate warnings later") + {} (:type |row) + :children $ [] + {} (:type |chart) (:kind |line) (:title "|Traffic") + :series $ [] + {} (:label |Mon) (:value 120) + {} (:label |Tue) (:value 132) + {} (:label |Wed) (:value 148) + {} (:type |mermaid) (:text "|flowchart LR\n CLI --> Relay\n Relay --> Renderer\n Renderer --> Browser") +' +``` + +Verify: + +- markdown block renders headings, bullets, and quote text +- chart renders without console failures +- Mermaid block renders to SVG instead of staying on placeholder text + +### 6.2 Math case + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :type |math + :display |block + :expr $ [] |mfrac + [] |mrow + [] |mo |− + [] |mi |b + [] |mo |± + [] |msqrt + [] |mrow + [] |msup + [] |mi |b + [] |mn |2 + [] |mo |− + [] |mn |4 + [] |mi |a + [] |mi |c + [] |mrow + [] |mn |2 + [] |mi |a +' +``` + +Verify: + +- browser shows rendered MathML output +- no validation error banner appears +- relay ack stays successful + +## 7. Failure Triage + +Use this mapping when a validation step fails: + +- `cr js` fails: fix renderer snapshot or Calcit syntax first +- `status/help/skill/send` fails before ack: inspect relay server and selected channel +- browser page stays in waiting state: inspect page URL channel and websocket target +- Mermaid or chart renders fail while ack succeeds: inspect browser console and DOM output +- library flow is wrong but send flow is correct: inspect `__relay_store__` request path and drawer state + +## 8. Minimum Regression Set + +For small changes, run at least this subset: + +1. `cr js` +2. `cargo run -- status --server ws://127.0.0.1:9100 --channel genui` +3. one `send` smoke payload +4. one browser snapshot check + +For state-management or drawer changes, also include: + +1. `Save` +2. `Library` +3. workspace snapshot switching +4. one `:patch` request \ No newline at end of file diff --git a/calcit.cirru b/calcit.cirru index 22d3858..7d7493f 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -145,6 +145,11 @@ :storage-entries renderer [] selected-storage $ :selected-storage renderer + workspace-entry $ :workspace-entry renderer + detail-status $ if + and (some? selected-storage) + = (:kind selected-storage) :workspace-report + , |workspace storage-status drawer-view $ or (:drawer-view renderer) |history drawer-plugin $ use-drawer (>> states :drawer) {} @@ -192,42 +197,71 @@ div {} $ :style {} (:font-size 12) (:font-weight |600) (:color |#4b5563) - <> $ if (= drawer-view |library) "|Saved Reports" |Messages + <> $ if (= drawer-view |library) "|Library Items" |Messages if (= drawer-view |library) - if - > (count storage-entries) 0 - list-> - {} $ :style - {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) - -> storage-entries .to-list $ map-indexed - fn (idx item) - let - active? $ if-let (current selected-storage) - = (:name item) (:name current) - , false - [] idx $ div - {} - :style $ {} (:padding "|8px 10px") (:border-radius 12) - :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" - :background-color $ if active? |#eef1f4 |#ffffff - :cursor |pointer - :display |flex - :flex-direction |column - :gap 4 - :on-click $ fn (e d!) - d! $ :: :load-stored-report (:name item) - div - {} $ :style - {} (:font-size 12) (:font-weight |600) (:color |#1f2933) - <> $ or (:name item) "|Saved report" - div - {} $ :style - {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) - <> $ or (:path item) | - div - {} $ :style - {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|No saved reports for current channel yet." + div + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) (:max-height "|calc(100vh - 180px)") (:overflow |auto) + if-let (item workspace-entry) + let + active? $ if-let (current selected-storage) + = (:kind current) :workspace-report + , false + div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :on-click $ fn (e d!) + d! $ :: :load-workspace-report + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:name item) "|Current workspace" + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ or (:path item) | + if + > (count storage-entries) 0 + list-> + {} $ :style + {} (:display |flex) (:flex-direction |column) (:gap 6) + -> storage-entries .to-list $ map-indexed + fn (idx item) + let + active? $ if-let (current selected-storage) + if + = (:kind current) :workspace-report + , false $ = (:name item) (:name current) + , false + [] idx $ div + {} + :style $ {} (:padding "|8px 10px") (:border-radius 12) + :border $ if active? "|1px solid #8a8f98" "|1px solid #d7d7cf" + :background-color $ if active? |#eef1f4 |#ffffff + :cursor |pointer + :display |flex + :flex-direction |column + :gap 4 + :on-click $ fn (e d!) + d! $ :: :load-stored-report (:name item) + div + {} $ :style + {} (:font-size 12) (:font-weight |600) (:color |#1f2933) + <> $ or (:name item) "|Saved report" + div + {} $ :style + {} (:font-size 11) (:line-height |1.5) (:color |#6b7280) + <> $ or (:path item) | + div + {} $ :style + {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) + <> "|No saved reports for current channel yet." if > (count history) 0 list-> @@ -289,7 +323,7 @@ div {} $ :style {} (:padding "|3px 8px") (:border-radius 999) (:background-color |#ffffff) (:border "|1px solid #d7d7cf") (:font-size 11) (:color |#4b5563) - <> $ str "|Status: " storage-status + <> $ str "|Status: " detail-status if (some? selected-channel) div {} $ :style @@ -300,15 +334,15 @@ {} :name $ or (:name item) |- :path $ or (:path item) |- - :status storage-status + :status detail-status :read-only true :spell-check false - :placeholder "|Saved report detail" + :placeholder "|Library item detail" :style $ {} (:width |100%) (:min-height "|calc(100vh - 320px)") (:padding 12) (:box-sizing |border-box) (:border-radius 14) (:border "|1px solid #d7d7cf") (:background-color |#ffffff) (:font-family |Monaco) (:font-size 12) (:line-height |1.6) (:resize |vertical) div {} $ :style {} (:padding "|10px 12px") (:border-radius 12) (:border "|1px dashed #d7d7cf") (:background-color |#fcfcfa) (:font-size 12) (:line-height |1.6) (:color |#6b7280) - <> "|Click a saved report to load it into the current preview." + <> "|Click the current workspace snapshot or a saved report to load it into the preview." if-let (item selected-history) div {} $ :style @@ -1860,6 +1894,7 @@ :storage-pending nil :storage-entries $ [] :selected-storage nil + :workspace-entry nil :examples $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote (ns app.schema) @@ -1882,96 +1917,111 @@ (:select-channel channel) -> store assoc-in ([] :relay :selected-channel) channel - assoc-in ([] :renderer :layout) nil - assoc-in ([] :renderer :layout-dsl) nil - assoc-in ([] :renderer :layout-id) nil - assoc-in ([] :renderer :layout-source) | - assoc-in ([] :renderer :last-request) nil - assoc-in ([] :renderer :last-error) nil - assoc-in ([] :renderer :storage-status) |idle - assoc-in ([] :renderer :storage-error) nil - assoc-in ([] :renderer :storage-pending) nil - assoc-in ([] :renderer :storage-entries) ([]) - assoc-in ([] :renderer :selected-storage) nil + update :renderer $ fn (renderer) + -> (or renderer {}) (assoc :layout nil) (assoc :layout-dsl nil) (assoc :layout-id nil) (assoc :layout-source |) (assoc :last-request nil) (assoc :last-error nil) (assoc :storage-status |idle) (assoc :storage-error nil) (assoc :storage-pending nil) + assoc :storage-entries $ [] + assoc :selected-storage nil + assoc :workspace-entry nil (:relay-status status message) -> store assoc-in ([] :relay :status) status assoc-in ([] :relay :last-error) message (:record-relay-message entry) - -> store - assoc-in ([] :renderer :history) - append - if - list? $ get-in store ([] :renderer :history) - get-in store $ [] :renderer :history + update store :renderer $ fn (renderer) + let + prev-renderer $ or renderer {} + prev-history $ if + list? $ :history prev-renderer + :history prev-renderer [] - , entry - assoc-in ([] :renderer :selected-history) entry + -> prev-renderer + assoc :history $ append prev-history entry + assoc :selected-history entry (:select-history entry) - assoc-in store ([] :renderer :selected-history) entry + update store :renderer $ fn (renderer) + assoc (or renderer {}) :selected-history entry (:open-history-drawer) - assoc-in store ([] :renderer :drawer-view) |history + update store :renderer $ fn (renderer) + assoc (or renderer {}) :drawer-view |history (:open-library-drawer) - assoc-in store ([] :renderer :drawer-view) |library + update store :renderer $ fn (renderer) + assoc (or renderer {}) :drawer-view |library (:request-storage-list) store (:save-current-report) store (:load-stored-report _) store + (:load-workspace-report) + update store :renderer $ fn (renderer) + let + prev-renderer $ or renderer {} + entry $ :workspace-entry prev-renderer + if-let (current-entry entry) + -> prev-renderer (assoc :selected-storage current-entry) + assoc :layout $ :layout-data current-entry + assoc :layout-dsl $ :layout-dsl current-entry + assoc :layout-id $ :layout_id current-entry + assoc :layout-source $ :source current-entry + assoc :last-request $ :request_id current-entry + assoc :last-error nil + assoc :storage-status |workspace + assoc :storage-error nil + , prev-renderer (:storage-pending request-id op-name) - -> store - assoc-in ([] :renderer :storage-pending) - {} (:request-id request-id) (:op op-name) - assoc-in ([] :renderer :storage-status) |working - assoc-in ([] :renderer :storage-error) nil + update store :renderer $ fn (renderer) + -> (or renderer {}) + assoc :storage-pending $ {} (:request-id request-id) (:op op-name) + assoc :storage-status |working + assoc :storage-error nil (:storage-saved request-id entry) - -> store - assoc-in ([] :renderer :storage-pending) nil - assoc-in ([] :renderer :storage-status) |saved - assoc-in ([] :renderer :storage-error) nil - assoc-in ([] :renderer :selected-storage) entry + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :storage-pending nil) (assoc :storage-status |saved) (assoc :storage-error nil) (assoc :selected-storage entry) (:storage-listed request-id entries) - -> store - assoc-in ([] :renderer :storage-pending) nil - assoc-in ([] :renderer :storage-status) |ready - assoc-in ([] :renderer :storage-error) nil - assoc-in ([] :renderer :storage-entries) entries + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :storage-pending nil) (assoc :storage-status |ready) (assoc :storage-error nil) (assoc :storage-entries entries) (:storage-loaded request-id entry layout-id layout layout-dsl source) - -> store - assoc-in ([] :renderer :storage-pending) nil - assoc-in ([] :renderer :storage-status) |loaded - assoc-in ([] :renderer :storage-error) nil - assoc-in ([] :renderer :selected-storage) entry - assoc-in ([] :renderer :layout) layout - assoc-in ([] :renderer :layout-dsl) layout-dsl - assoc-in ([] :renderer :layout-id) layout-id - assoc-in ([] :renderer :layout-source) source - assoc-in ([] :renderer :last-request) request-id - assoc-in ([] :renderer :last-error) nil + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :storage-pending nil) (assoc :storage-status |loaded) (assoc :storage-error nil) (assoc :selected-storage entry) (assoc :layout layout) (assoc :layout-dsl layout-dsl) (assoc :layout-id layout-id) (assoc :layout-source source) (assoc :last-request request-id) (assoc :last-error nil) (:storage-failed request-id message) - -> store - assoc-in ([] :renderer :storage-pending) nil - assoc-in ([] :renderer :storage-status) |error - assoc-in ([] :renderer :storage-error) message + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :storage-pending nil) (assoc :storage-status |error) (assoc :storage-error message) (:genui-applied request-id layout-id layout layout-dsl source) - -> store - assoc-in ([] :renderer :layout) layout - assoc-in ([] :renderer :layout-dsl) layout-dsl - assoc-in ([] :renderer :layout-id) layout-id - assoc-in ([] :renderer :layout-source) source - assoc-in ([] :renderer :last-request) request-id - assoc-in ([] :renderer :last-error) nil + let + entry $ -> + {} $ :kind :workspace-report + assoc :channel $ get-in store ([] :relay :selected-channel) + assoc :name $ str + or + get-in store $ [] :relay :selected-channel + , |workspace + , "| / current workspace" + assoc :path "|Local workspace snapshot. Not saved in library." + assoc :layout_id layout-id + assoc :request_id request-id + assoc :layout-data layout + assoc :layout-dsl layout-dsl + assoc :source source + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :layout layout) (assoc :layout-dsl layout-dsl) (assoc :layout-id layout-id) (assoc :layout-source source) (assoc :last-request request-id) (assoc :last-error nil) (assoc :workspace-entry entry) (assoc :selected-storage entry) (assoc :storage-status |workspace) (assoc :storage-error nil) (:genui-failed request-id message source) - -> store - assoc-in ([] :renderer :layout-source) source - assoc-in ([] :renderer :last-request) request-id - assoc-in ([] :renderer :last-error) message + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :layout-source source) (assoc :last-request request-id) (assoc :last-error message) (:layout-mutated request-id layout-id layout layout-dsl source) - -> store - assoc-in ([] :renderer :layout) layout - assoc-in ([] :renderer :layout-dsl) layout-dsl - assoc-in ([] :renderer :layout-id) layout-id - assoc-in ([] :renderer :layout-source) source - assoc-in ([] :renderer :last-request) request-id - assoc-in ([] :renderer :last-error) nil + let + entry $ -> + {} $ :kind :workspace-report + assoc :channel $ get-in store ([] :relay :selected-channel) + assoc :name $ str + or + get-in store $ [] :relay :selected-channel + , |workspace + , "| / current workspace" + assoc :path "|Local workspace snapshot. Not saved in library." + assoc :layout_id layout-id + assoc :request_id request-id + assoc :layout-data layout + assoc :layout-dsl layout-dsl + assoc :source source + update store :renderer $ fn (renderer) + -> (or renderer {}) (assoc :layout layout) (assoc :layout-dsl layout-dsl) (assoc :layout-id layout-id) (assoc :layout-source source) (assoc :last-request request-id) (assoc :last-error nil) (assoc :workspace-entry entry) (assoc :selected-storage entry) (assoc :storage-status |workspace) (assoc :storage-error nil) _ $ do (eprintln "|unknown op:" op) store :examples $ [] :ns $ %{} :NsEntry (:doc |) From 91eda43c7ac4304e3688c1c06abb86d911924a85 Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 15:47:17 +0800 Subject: [PATCH 18/26] try some test scripts --- TESTING.md | 85 +++++++++++++++++++++++- calcit.cirru | 76 +++++++++++++-------- test/README.md | 61 +++++++++++++++++ test/cases/math-quadratic.cirru | 20 ++++++ test/cases/mixed-dashboard.cirru | 17 +++++ test/ci-renderer-dsl.sh | 8 +++ test/library-smoke.sh | 109 +++++++++++++++++++++++++++++++ test/snapshot-smoke.sh | 64 ++++++++++++++++++ test/ui-basic-smoke.sh | 52 +++++++++++++++ 9 files changed, 463 insertions(+), 29 deletions(-) create mode 100644 test/README.md create mode 100644 test/cases/math-quadratic.cirru create mode 100644 test/cases/mixed-dashboard.cirru create mode 100644 test/ci-renderer-dsl.sh create mode 100644 test/library-smoke.sh create mode 100644 test/snapshot-smoke.sh create mode 100644 test/ui-basic-smoke.sh diff --git a/TESTING.md b/TESTING.md index b22af50..4e9dfc2 100644 --- a/TESTING.md +++ b/TESTING.md @@ -4,6 +4,41 @@ This repository already had validation notes spread across [README.md](README.md This file is the canonical checklist for validating the renderer end to end. +## Local vs CI + +Use 2 different standards for validation: + +- local validation should aim to catch functional problems quickly, even if the checks are only partial +- CI validation only needs scripted renderer DSL logic checks; it does not need browser style correctness or DOM-structure correctness + +Practical rule: + +- local: run `cr js`, snapshot-based renderer checks, and partial browser smoke checks +- CI: run the snapshot-based renderer DSL script only + +## Latest Verified Run + +Validated on 2026-05-30 against a local Vite page and a relay listening on `ws://127.0.0.1:9100`. + +Observed results: + +- `cr js` succeeded +- `edn-relay status/help/skill --channel genui` all returned expected payloads +- mixed dashboard case rendered markdown, Mermaid, and chart content successfully +- `:layout`, `:node`, `:patch`, and `:replace` all returned successful acks +- `Save` created `.cirru` files under `~/.config/ed-relay/genui/` +- `Library` showed persisted reports plus one local `current workspace` snapshot entry +- switching from a saved report back to `current workspace` restored the local preview state +- MathML block rendering succeeded for the quadratic-formula case + +Known non-blocking observations from that run: + +- ECharts logged one initial size warning before the container settled +- the active `genui` channel had more than one receiver, so relay logged duplicate-ack warnings +- browser tooling reported one accessibility issue about a form field without an id or name attribute + +When validating newly added renderer request handlers, prefer a fresh page or a dedicated channel such as `snapshot-e2e`; older receiver tabs can keep stale HMR closures and reply with outdated request behavior. + ## Scope The validation flow is split into 3 layers: @@ -124,6 +159,8 @@ Expected result: Once the page is open, validate the visible browser workflow. +This layer is intentionally functional rather than visual. The goal is to catch broken behavior, not to assert exact styling or DOM structure. + ### 4.1 Top bar and preview Check these items: @@ -174,6 +211,25 @@ Expected result: - includes `:summary` - summary nodes contain `:path`, `:type`, and `:child-count` +### 5.1b Stable snapshot tree + +Use this for most scripted CLI checks. It intentionally returns a trimmed tree rather than the full DSL. + +```bash +cd /Users/chenyong/repo/worktools/cirru_edn_relay +cargo run -- send --server ws://127.0.0.1:9100 --channel genui ' +{} + :op :snapshot +' +``` + +Expected result: + +- returns `:kind :snapshot` +- includes `:tree` +- tree only contains stable summary fields such as `:path`, `:type`, `:child-count`, and a small set of node-specific metadata +- scripts can grep this payload without depending on the full layout DSL or browser DOM + ### 5.2 Read one node ```bash @@ -322,4 +378,31 @@ For state-management or drawer changes, also include: 1. `Save` 2. `Library` 3. workspace snapshot switching -4. one `:patch` request \ No newline at end of file +4. one `:patch` request + +## 9. Test Directory Layout + +The repository keeps partial automation assets under [test/README.md](test/README.md). + +- prefer renderer-side `:snapshot` checks for scripted CLI validation +- keep bash orchestration under `test/*.sh` +- keep reusable Cirru payloads under `test/cases/` +- keep browser automation partial and pragmatic; use `chrome-devtools` for stable checks such as page open, top-bar text, drawer text, and saved-report switching + +## 10. Recommended Commands + +Local functional smoke: + +```bash +cr js +bash test/snapshot-smoke.sh +PAGE_URL='http://127.0.0.1:3013/?channel=genui&server=ws://127.0.0.1:9100' bash test/ui-basic-smoke.sh +PAGE_URL='http://127.0.0.1:3013/?channel=library-e2e&server=ws://127.0.0.1:9100' bash test/library-smoke.sh +``` + +CI renderer DSL validation: + +```bash +cr js +bash test/ci-renderer-dsl.sh +``` diff --git a/calcit.cirru b/calcit.cirru index 7d7493f..1cfbc59 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -1051,6 +1051,7 @@ def example-docs $ [] {} (:name |card-demo) (:summary "|最小 card 示例。") (:payload "|{}\n :type |card\n :text \"|CLI Demo\"\n :children $ []\n {} (:type |badge) (:text |preview)\n {} (:type |text) (:text \"|Hello from CLI\")") {} (:name |layout-summary-demo) (:summary "|查询当前 layout summary tree。") (:payload "|{}\n :op :layout") + {} (:name |layout-snapshot-demo) (:summary "|查询当前 layout 的稳定裁剪快照,适合脚本化验证。") (:payload "|{}\n :op :snapshot") {} (:name |layout-node-demo) (:summary "|按路径读取一个节点的完整 DSL。") (:payload "|{}\n :op :node\n :path |1.2") {} (:name |layout-patch-demo) (:summary "|按路径局部更新节点属性。") (:payload "|{}\n :op :patch\n :path |1\n :changes $ {} (:text \"|Updated title\")") {} (:name |layout-replace-demo) (:summary "|按路径替换整棵子树。") (:payload "|{}\n :op :replace\n :path |2.1\n :node $ {} (:type |text) (:text \"|Replaced from CLI\")") @@ -1068,6 +1069,7 @@ {} (:name |ack) (:summary "|同一个请求允许多个 receiver 收到事件,但 sender 只接受第一条 `ack`。") {} (:name |editing) (:summary "|局部编辑推荐顺序是先 `:layout` 看 summary tree,再用 `:node` 读取完整 DSL,最后按改动大小选择 `:patch` 或 `:replace`。成功响应都会回新的 `:layout_id`,并附当前节点 `:summary`,变更类操作还会附 `:dsl`。") {} (:name |layout) (:summary "|CLI 上优先用 `{:op :layout}` 查询当前 layout 概览;可附 `:path` 只看某个子树。summary 节点会带 `:path`、`:type`、`:child-count` 和少量摘要字段。") + {} (:name |snapshot) (:summary "|脚本化验证优先用 `{:op :snapshot}` 查询稳定的裁剪版 layout 树;默认返回整棵树,也支持 `:path` 只抓某个子树。返回字段刻意裁剪,避免测试依赖完整 DSL。") {} (:name |node) (:summary "|用 `:op :node` + `:path \"1.2.3\"` 读取某个节点的完整 DSL。路径使用 1-based children 索引,`root` 表示整棵树;成功时同时返回 `:dsl`、`:source` 和 `:summary`。") {} (:name |patch) (:summary "|用 `:op :patch` + `:path` + `:changes` 局部合并节点属性,renderer 会重新验证整棵 layout;成功后立即局部更新页面,并返回目标节点新的 `:dsl` 与 `:summary`。") {} (:name |replace) (:summary "|用 `:op :replace` + `:path` + `:node` 直接替换某个节点 DSL,适合结构性修改;成功后同样会重新验证整棵树,并返回替换结果。") @@ -1078,7 +1080,7 @@ def relay-commands $ [] |send |help |skill |status |open :examples $ [] |renderer-help-overview $ %{} :CodeEntry (:doc |) (:schema :dynamic) - :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`storage`、`editing`、`examples`、`layout`、`layout-patch-demo`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") + :code $ quote (def renderer-help-overview "|默认 `edn-relay help --channel ` 只返回总览;需要细节时再追加 topic,例如 `components`、`math`、`protocol`、`storage`、`editing`、`examples`、`layout`、`snapshot`、`layout-patch-demo`、`math-fraction-demo`,避免一次返回全部组件配置和案例。") :examples $ [] |select-component-docs $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1139,10 +1141,10 @@ :code $ quote def skill-sections $ [] {} (:name |workflow) (:title |Workflow) (:text "|1. 先 `edn-relay help --channel ` 看总览。\n2. 再用 `help --channel ` 把范围收窄到组件、协议或示例。\n3. 最后才运行 `edn-relay send --channel ...` 发 payload。") - {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看局部编辑流程,用 `edn-relay help --channel editing`;要看具体案例,用 `edn-relay help --channel layout-patch-demo`。") + {} (:name |help) (:title "|Help Queries") (:text "|默认 `help` 只返回总览。要列出全部组件,用 `edn-relay help --channel components`;要看 MathML,用 `edn-relay help --channel math`;要看局部编辑流程,用 `edn-relay help --channel editing`;要看脚本化快照接口,用 `edn-relay help --channel snapshot`;要看具体案例,用 `edn-relay help --channel layout-patch-demo`。") {} (:name |storage) (:title |Storage) (:text "|页面上已有有效 report 时,可以直接点 `Save` 把当前内容保存到 `~/.config/ed-relay//`。点 `Library` 会请求 relay 列出当前 channel 下的 `.cirru` 文件,并在点击条目后把保存时的 layout 加载回当前预览。CLI 想查这个能力时,优先用 `edn-relay help --channel storage`。") - {} (:name |editing) (:title "|Editing Workflow") (:text "|先用 `edn-relay send --channel '{}` + `:op :layout` 获取隐藏细节的 summary tree,再用 `:op :node` + `:path` 读取完整 DSL。只改属性时优先 `:patch` + `:changes`,结构变化再用 `:replace`。每次成功都会回新的 `:layout_id`、目标节点 `:summary`,并在 `node/patch/replace` 返回完整 `:dsl`。") - {} (:name |layout) (:title "|Layout Editing") (:text "|局部编辑默认走 `layout -> node -> patch/replace`。路径使用 1-based children 索引,`root` 表示整棵树;如果 agent 还不确定 payload 形状,先查 `layout-summary-demo`、`layout-node-demo`、`layout-patch-demo`。") + {} (:name |editing) (:title "|Editing Workflow") (:text "|先用 `edn-relay send --channel '{}` + `:op :snapshot` 或 `:op :layout` 获取裁剪过的概览树,再用 `:op :node` + `:path` 读取完整 DSL。只改属性时优先 `:patch` + `:changes`,结构变化再用 `:replace`。每次成功都会回新的 `:layout_id`、目标节点 `:summary`,并在 `node/patch/replace` 返回完整 `:dsl`。") + {} (:name |layout) (:title "|Layout Editing") (:text "|局部编辑默认走 `snapshot/layout -> node -> patch/replace`。脚本化验证优先 `snapshot`,人工排查优先 `layout`。路径使用 1-based children 索引,`root` 表示整棵树;如果 agent 还不确定 payload 形状,先查 `layout-summary-demo`、`layout-snapshot-demo`、`layout-node-demo`、`layout-patch-demo`。") {} (:name |math) (:title |MathML) (:text "|MathML Core 已通过 `math` 节点暴露。推荐顺序是先查 `edn-relay help --channel genui math`,再查 `math-fraction-demo`,最后发送 `:type |math` + `:expr` 的 Cirru EDN payload。") {} (:name |validation) (:title |Validation) (:text "|浏览器验证优先看 `chrome-devtools take_snapshot` 和 `chrome-devtools list_console_messages`;CLI 侧优先看 `status`、`help`、`skill` 是否和当前页面一致。局部编辑失败时,优先检查返回的 `ack false` 和对应 `:path`。") :examples $ [] @@ -1289,6 +1291,8 @@ (:help _) "|Renderer help" (:skill _) "|Renderer skill" (:status) "|Renderer status" + (:snapshot path) + str "|Layout snapshot " $ layout-path-display path (:layout path) str "|Layout summary " $ layout-path-display path (:node path) @@ -1322,6 +1326,7 @@ (:help _) (handle-renderer-event! ws frame) (:skill _) (handle-renderer-event! ws frame) (:status) (handle-renderer-event! ws frame) + (:snapshot _) (handle-renderer-event! ws frame) (:layout _) (handle-renderer-event! ws frame) (:node _) (handle-renderer-event! ws frame) (:patch _ _) (handle-renderer-event! ws frame) @@ -1359,6 +1364,16 @@ send-genui-ack! ws request-id true (config/build-skill-payload topics) nil (:status) send-genui-ack! ws request-id true (config/build-status-payload relay renderer) nil + (:snapshot path) + if-let + layout-dsl $ :layout-dsl renderer + let + target-node $ layout-node-at-path layout-dsl path + response-payload $ {} (:status :ok) (:kind :snapshot) + :path $ layout-path-display path + :tree $ summarize-layout-node target-node path + send-genui-ack! ws request-id true response-payload nil + send-genui-ack! ws request-id false nil "|No layout loaded in renderer" (:layout path) if-let layout-dsl $ :layout-dsl renderer @@ -1620,6 +1635,13 @@ (:skill topics) :: :skill $ normalize-renderer-topics topics (:status) (:: :status) + (:snapshot) + :: :snapshot $ [] + (:snapshot path) + if-let + normalized $ normalize-layout-path path + :: :snapshot normalized + :: :invalid (:layout) :: :layout $ [] (:layout path) @@ -1647,30 +1669,28 @@ let op-name $ protocol-name (:op payload) topics $ normalize-renderer-topics (:topics payload) - if (= op-name |help) (:: :help topics) - if (= op-name |skill) (:: :skill topics) - if (= op-name |status) (:: :status) - if (= op-name |layout) - if-let - normalized $ normalize-layout-path (:path payload) - :: :layout normalized - :: :invalid - if (= op-name |node) - if-let - normalized $ normalize-layout-path (:path payload) - :: :node normalized - :: :invalid - if (= op-name |patch) - if-let - normalized $ normalize-layout-path (:path payload) - :: :patch normalized $ :changes payload - :: :invalid - if (= op-name |replace) - if-let - normalized $ normalize-layout-path (:path payload) - :: :replace normalized $ or (:node payload) (:dsl payload) - :: :invalid - :: :invalid + case-default op-name (:: :invalid) + |help $ :: :help topics + |skill $ :: :skill topics + |status $ :: :status + |snapshot $ let + normalized $ normalize-layout-path (:path payload) + if (some? normalized) (:: :snapshot normalized) (:: :invalid) + |layout $ let + normalized $ normalize-layout-path (:path payload) + if (some? normalized) (:: :layout normalized) (:: :invalid) + |node $ let + normalized $ normalize-layout-path (:path payload) + if (some? normalized) (:: :node normalized) (:: :invalid) + |patch $ let + normalized $ normalize-layout-path (:path payload) + if (some? normalized) (:: :patch normalized $ :changes payload) (:: :invalid) + |replace $ let + normalized $ normalize-layout-path (:path payload) + if + some? normalized + :: :replace normalized $ or (:node payload) (:dsl payload) + :: :invalid :: :invalid :examples $ [] |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..aabbb7d --- /dev/null +++ b/test/README.md @@ -0,0 +1,61 @@ +# Test Assets + +This directory keeps partial automation for renderer validation. + +## Strategy + +Validation is split into 3 levels: + +1. renderer build checks +2. relay-to-renderer scripted checks +3. browser-assisted partial UI checks + +The main design choice is to avoid depending on browser DOM details when the renderer can already export a stable trimmed tree. + +Expected usage: + +- local development: run levels 1, 2, and selected level-3 smoke scripts to catch obvious behavior regressions +- CI: only run level 1 plus the renderer-side snapshot script; do not require browser style or DOM-structure assertions + +## Renderer-side snapshot interface + +Use the renderer request below for most scripted validation: + +```cirru +{} + :op :snapshot +``` + +Optional subtree request: + +```cirru +{} + :op :snapshot + :path |2.1 +``` + +This returns a trimmed tree rather than the full DSL. It is intentionally closer to an assertion snapshot than a debugging dump. + +Recommended usage: + +1. send a case payload +2. request `:snapshot` +3. grep the returned tree for stable lines such as node type, path, child-count, chart kind, mermaid line count, or math expr tag + +Prefer a dedicated test channel such as `snapshot-e2e` for this flow. If an older page is still subscribed to the same channel, relay may receive an outdated ack from a stale HMR closure first. + +## Files + +- [test/cases/mixed-dashboard.cirru](cases/mixed-dashboard.cirru): mixed markdown, chart, and Mermaid case +- [test/cases/math-quadratic.cirru](cases/math-quadratic.cirru): MathML case +- [test/ci-renderer-dsl.sh](ci-renderer-dsl.sh): CI entrypoint for renderer DSL logic validation +- [test/library-smoke.sh](library-smoke.sh): local-only Library and workspace switching smoke test +- [test/snapshot-smoke.sh](snapshot-smoke.sh): scripted relay and renderer validation using `:snapshot` +- [test/ui-basic-smoke.sh](ui-basic-smoke.sh): partial browser automation using `chrome-devtools` + +## Conventions + +- prefer Calcit-side protocol support over custom JS helpers when possible +- keep orchestration in bash +- treat browser automation as partial smoke coverage, not as the only source of truth +- browser scripts should assert text-level behavior and feature availability, not exact style or DOM shape diff --git a/test/cases/math-quadratic.cirru b/test/cases/math-quadratic.cirru new file mode 100644 index 0000000..5187ce5 --- /dev/null +++ b/test/cases/math-quadratic.cirru @@ -0,0 +1,20 @@ +{} + :type |math + :display |block + :expr $ [] |mfrac + [] |mrow + [] |mo |− + [] |mi |b + [] |mo |± + [] |msqrt + [] |mrow + [] |msup + [] |mi |b + [] |mn |2 + [] |mo |− + [] |mn |4 + [] |mi |a + [] |mi |c + [] |mrow + [] |mn |2 + [] |mi |a \ No newline at end of file diff --git a/test/cases/mixed-dashboard.cirru b/test/cases/mixed-dashboard.cirru new file mode 100644 index 0000000..0959e4b --- /dev/null +++ b/test/cases/mixed-dashboard.cirru @@ -0,0 +1,17 @@ +{} + :type |column + :children $ [] + {} (:type |card) (:text "|Weekly Report") + :children $ [] + {} (:type |text) (:text "|System health summary") + {} (:type |badge) (:text |healthy) + {} (:type |divider) + {} (:type |markdown) (:text "|## Notes\n- renderer ok\n- relay ok\n> investigate warnings later") + {} (:type |row) + :children $ [] + {} (:type |chart) (:kind |line) (:title "|Traffic") + :series $ [] + {} (:label |Mon) (:value 120) + {} (:label |Tue) (:value 132) + {} (:label |Wed) (:value 148) + {} (:type |mermaid) (:text "|flowchart LR\n CLI --> Relay\n Relay --> Renderer\n Renderer --> Browser") \ No newline at end of file diff --git a/test/ci-renderer-dsl.sh b/test/ci-renderer-dsl.sh new file mode 100644 index 0000000..c2e723e --- /dev/null +++ b/test/ci-renderer-dsl.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) + +cd "$ROOT_DIR" +bash test/snapshot-smoke.sh diff --git a/test/library-smoke.sh b/test/library-smoke.sh new file mode 100644 index 0000000..8de3934 --- /dev/null +++ b/test/library-smoke.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) +RELAY_BIN=${RELAY_BIN:-/Users/chenyong/repo/worktools/cirru_edn_relay/target/debug/edn-relay} +SERVER=${SERVER:-ws://127.0.0.1:9100} +CHANNEL=${CHANNEL:-library-e2e} +PAGE_URL=${PAGE_URL:-http://127.0.0.1:3010/?channel=${CHANNEL}&server=${SERVER}} + +extract_uid() { + local label=$1 + local snapshot=$2 + awk -v label="$label" ' + index($0, label) { + match($0, /uid=[^ ]+/) + if (RSTART > 0) { + print substr($0, RSTART + 4, RLENGTH - 4) + exit + } + } + ' <<<"$snapshot" +} + +assert_snapshot_contains() { + local snapshot=$1 + local text=$2 + if ! grep -Fq "$text" <<<"$snapshot"; then + echo "Missing snapshot text: $text" >&2 + exit 1 + fi +} + +send_payload_file() { + local payload_file=$1 + "$RELAY_BIN" send --server "$SERVER" --channel "$CHANNEL" "$(cat "$payload_file")" >/dev/null +} + +wait_for_receiver() { + "$RELAY_BIN" status --server "$SERVER" --channel "$CHANNEL" >/dev/null +} + +count_saved_files() { + find "$HOME/.config/ed-relay/$CHANNEL" -maxdepth 1 -name '*.cirru' 2>/dev/null | wc -l | tr -d ' ' +} + +extract_first_saved_report_uid() { + local snapshot=$1 + awk ' + /StaticText ".*\.cirru"/ { + match($0, /uid=[^ ]+/) + if (RSTART > 0) { + print substr($0, RSTART + 4, RLENGTH - 4) + exit + } + } + ' <<<"$snapshot" +} + +chrome-devtools new_page "$PAGE_URL" >/dev/null +snapshot=$(chrome-devtools take_snapshot) + +assert_snapshot_contains "$snapshot" 'StaticText "EDN Renderer"' +assert_snapshot_contains "$snapshot" "StaticText \"Channel: $CHANNEL\"" + +wait_for_receiver +send_payload_file "$ROOT_DIR/test/cases/mixed-dashboard.cirru" +before_count=$(count_saved_files) +snapshot=$(chrome-devtools take_snapshot) + +save_uid=$(extract_uid 'button "Save"' "$snapshot") +library_uid=$(extract_uid 'button "Library"' "$snapshot") + +if [[ -z "$save_uid" || -z "$library_uid" ]]; then + echo 'Could not locate Save or Library button in browser snapshot' >&2 + exit 1 +fi + +chrome-devtools click "$save_uid" >/dev/null +after_count=$(count_saved_files) +if [[ "$after_count" -le "$before_count" ]]; then + echo 'Save did not create a new persisted report file' >&2 + exit 1 +fi + +chrome-devtools click "$library_uid" >/dev/null +library_snapshot=$(chrome-devtools take_snapshot) +assert_snapshot_contains "$library_snapshot" 'StaticText "Library Items"' +assert_snapshot_contains "$library_snapshot" 'StaticText "current workspace"' + +saved_report_uid=$(extract_first_saved_report_uid "$library_snapshot") +workspace_uid=$(extract_uid "StaticText \"$CHANNEL / current workspace\"" "$library_snapshot") + +if [[ -z "$saved_report_uid" || -z "$workspace_uid" ]]; then + echo 'Could not locate saved report entry or current workspace entry in Library snapshot' >&2 + exit 1 +fi + +chrome-devtools click "$saved_report_uid" >/dev/null +saved_snapshot=$(chrome-devtools take_snapshot) +assert_snapshot_contains "$saved_snapshot" 'StaticText "Status: loaded"' + +chrome-devtools click "$workspace_uid" >/dev/null +workspace_snapshot=$(chrome-devtools take_snapshot) +assert_snapshot_contains "$workspace_snapshot" 'StaticText "Status: workspace"' + +echo 'library smoke passed' + +workspace_uid=$(extract_uid 'StaticText "' \ No newline at end of file diff --git a/test/snapshot-smoke.sh b/test/snapshot-smoke.sh new file mode 100644 index 0000000..078088f --- /dev/null +++ b/test/snapshot-smoke.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) +RELAY_BIN=${RELAY_BIN:-/Users/chenyong/repo/worktools/cirru_edn_relay/target/debug/edn-relay} +SERVER=${SERVER:-ws://127.0.0.1:9100} +CHANNEL=${CHANNEL:-snapshot-e2e} + +send_payload() { + local payload_file=$1 + "$RELAY_BIN" send --server "$SERVER" --channel "$CHANNEL" "$(cat "$payload_file")" +} + +snapshot_payload() { + cat <<'EOF' +{} + :op :snapshot +EOF +} + +snapshot_subtree_payload() { + local path=$1 + cat <&2 + exit 1 + fi +} + +echo '[1/4] Send mixed dashboard case' +send_output=$(send_payload "$ROOT_DIR/test/cases/mixed-dashboard.cirru") +assert_contains "$send_output" '(:status :ok)' + +echo '[2/4] Verify stable root snapshot' +snapshot_output=$("$RELAY_BIN" send --server "$SERVER" --channel "$CHANNEL" "$(snapshot_payload)") +assert_contains "$snapshot_output" '(:kind :snapshot)' +assert_contains "$snapshot_output" '(:type |column)' +assert_contains "$snapshot_output" '(:path |2.2)' +assert_contains "$snapshot_output" '(:type |mermaid)' +assert_contains "$snapshot_output" '(:kind |line)' + +echo '[3/4] Verify subtree snapshot' +subtree_output=$("$RELAY_BIN" send --server "$SERVER" --channel "$CHANNEL" "$(snapshot_subtree_payload '1')") +assert_contains "$subtree_output" '(:path |1)' +assert_contains "$subtree_output" '(:title "|Weekly Report")' + +echo '[4/4] Send MathML case and verify snapshot' +math_send_output=$(send_payload "$ROOT_DIR/test/cases/math-quadratic.cirru") +assert_contains "$math_send_output" '(:status :ok)' +math_snapshot_output=$("$RELAY_BIN" send --server "$SERVER" --channel "$CHANNEL" "$(snapshot_payload)") +assert_contains "$math_snapshot_output" '(:type |math)' +assert_contains "$math_snapshot_output" '(:expr-tag |mfrac)' + +echo 'snapshot smoke passed' \ No newline at end of file diff --git a/test/ui-basic-smoke.sh b/test/ui-basic-smoke.sh new file mode 100644 index 0000000..d2b7d16 --- /dev/null +++ b/test/ui-basic-smoke.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +set -euo pipefail + +PAGE_URL=${PAGE_URL:-http://127.0.0.1:3010/?channel=genui&server=ws://127.0.0.1:9100} + +extract_uid() { + local label=$1 + local snapshot=$2 + awk -v label="$label" ' + index($0, label) { + match($0, /uid=[^ ]+/) + if (RSTART > 0) { + print substr($0, RSTART + 4, RLENGTH - 4) + exit + } + } + ' <<<"$snapshot" +} + +assert_snapshot_contains() { + local snapshot=$1 + local text=$2 + if ! grep -Fq "$text" <<<"$snapshot"; then + echo "Missing snapshot text: $text" >&2 + exit 1 + fi +} + +chrome-devtools new_page "$PAGE_URL" >/dev/null +snapshot=$(chrome-devtools take_snapshot) + +assert_snapshot_contains "$snapshot" 'StaticText "EDN Renderer"' +assert_snapshot_contains "$snapshot" 'StaticText "Channel: genui"' + +library_uid=$(extract_uid 'button "Library"' "$snapshot") +history_uid=$(extract_uid 'button "History' "$snapshot") + +if [[ -z "$library_uid" || -z "$history_uid" ]]; then + echo 'Could not locate Library or History button in browser snapshot' >&2 + exit 1 +fi + +chrome-devtools click "$history_uid" >/dev/null +history_snapshot=$(chrome-devtools take_snapshot) +assert_snapshot_contains "$history_snapshot" 'StaticText "Detail"' + +chrome-devtools click "$library_uid" >/dev/null +library_snapshot=$(chrome-devtools take_snapshot) +assert_snapshot_contains "$library_snapshot" 'StaticText "Library Items"' + +echo 'ui basic smoke passed' \ No newline at end of file From 2cbeaae8fa5f5a24b53f286955751066e9e2f436 Mon Sep 17 00:00:00 2001 From: tiye Date: Sat, 30 May 2026 21:21:00 +0800 Subject: [PATCH 19/26] try tome tests; cache render state --- TESTING.md | 5 +++ calcit.cirru | 72 +++++++++++++++++++++++++++++++++---- test/README.md | 1 + test/channel-cache-smoke.sh | 45 +++++++++++++++++++++++ test/ci-renderer-dsl.sh | 1 + 5 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 test/channel-cache-smoke.sh diff --git a/TESTING.md b/TESTING.md index 4e9dfc2..da75c56 100644 --- a/TESTING.md +++ b/TESTING.md @@ -406,3 +406,8 @@ CI renderer DSL validation: cr js bash test/ci-renderer-dsl.sh ``` + +That CI entrypoint currently covers: + +- snapshot-based renderer DSL checks +- pure renderer-state regression for channel cache restore on switch-away and switch-back diff --git a/calcit.cirru b/calcit.cirru index 1cfbc59..b4c29ef 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -1909,6 +1909,7 @@ :history $ [] :selected-history nil :drawer-view |history + :channel-cache $ {} :storage-status |idle :storage-error nil :storage-pending nil @@ -1920,6 +1921,63 @@ :code $ quote (ns app.schema) |app.updater $ %{} :FileEntry :defs $ {} + |cache-renderer-channel-state $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn cache-renderer-channel-state (renderer channel) + if (some? channel) + assoc renderer :channel-cache $ assoc + or (:channel-cache renderer) {} + , channel $ pick-renderer-channel-state renderer + , renderer + :examples $ [] + |pick-renderer-channel-state $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn pick-renderer-channel-state (renderer) + {} (:layout $ :layout renderer) (:layout-dsl $ :layout-dsl renderer) (:layout-id $ :layout-id renderer) (:layout-source $ :layout-source renderer) (:last-request $ :last-request renderer) (:last-error $ :last-error renderer) + :storage-status $ :storage-status renderer + :storage-error $ :storage-error renderer + :storage-pending $ :storage-pending renderer + :storage-entries $ :storage-entries renderer + :selected-storage $ :selected-storage renderer + :workspace-entry $ :workspace-entry renderer + :examples $ [] + |renderer-channel-default $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + def renderer-channel-default $ {} + :layout nil + :layout-dsl nil + :layout-id nil + :layout-source | + :last-request nil + :last-error nil + :storage-status |idle + :storage-error nil + :storage-pending nil + :storage-entries $ [] + :selected-storage nil + :workspace-entry nil + :examples $ [] + |restore-renderer-channel-state $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :code $ quote + defn restore-renderer-channel-state (renderer channel) + let + next-state $ merge renderer-channel-default $ get + or (:channel-cache renderer) {} + , channel + -> renderer + assoc :layout $ :layout next-state + assoc :layout-dsl $ :layout-dsl next-state + assoc :layout-id $ :layout-id next-state + assoc :layout-source $ :layout-source next-state + assoc :last-request $ :last-request next-state + assoc :last-error $ :last-error next-state + assoc :storage-status $ :storage-status next-state + assoc :storage-error $ :storage-error next-state + assoc :storage-pending $ :storage-pending next-state + assoc :storage-entries $ :storage-entries next-state + assoc :selected-storage $ :selected-storage next-state + assoc :workspace-entry $ :workspace-entry next-state + :examples $ [] |updater $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn updater (store op op-id op-time) @@ -1935,13 +1993,13 @@ (:relay-channels channels) assoc-in store ([] :relay :channels) channels (:select-channel channel) - -> store - assoc-in ([] :relay :selected-channel) channel - update :renderer $ fn (renderer) - -> (or renderer {}) (assoc :layout nil) (assoc :layout-dsl nil) (assoc :layout-id nil) (assoc :layout-source |) (assoc :last-request nil) (assoc :last-error nil) (assoc :storage-status |idle) (assoc :storage-error nil) (assoc :storage-pending nil) - assoc :storage-entries $ [] - assoc :selected-storage nil - assoc :workspace-entry nil + let + current-channel $ get-in store ([] :relay :selected-channel) + prev-renderer $ or (:renderer store) {} + cached-renderer $ cache-renderer-channel-state prev-renderer current-channel + -> store + assoc-in ([] :relay :selected-channel) channel + assoc :renderer $ restore-renderer-channel-state cached-renderer channel (:relay-status status message) -> store assoc-in ([] :relay :status) status diff --git a/test/README.md b/test/README.md index aabbb7d..f83fdc9 100644 --- a/test/README.md +++ b/test/README.md @@ -49,6 +49,7 @@ Prefer a dedicated test channel such as `snapshot-e2e` for this flow. If an olde - [test/cases/mixed-dashboard.cirru](cases/mixed-dashboard.cirru): mixed markdown, chart, and Mermaid case - [test/cases/math-quadratic.cirru](cases/math-quadratic.cirru): MathML case - [test/ci-renderer-dsl.sh](ci-renderer-dsl.sh): CI entrypoint for renderer DSL logic validation +- [test/channel-cache-smoke.sh](channel-cache-smoke.sh): pure renderer-state regression check for channel switching cache restore - [test/library-smoke.sh](library-smoke.sh): local-only Library and workspace switching smoke test - [test/snapshot-smoke.sh](snapshot-smoke.sh): scripted relay and renderer validation using `:snapshot` - [test/ui-basic-smoke.sh](ui-basic-smoke.sh): partial browser automation using `chrome-devtools` diff --git a/test/channel-cache-smoke.sh b/test/channel-cache-smoke.sh new file mode 100644 index 0000000..c5ba801 --- /dev/null +++ b/test/channel-cache-smoke.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) + +cd "$ROOT_DIR" +node - <<'EOF' +import * as core from './js-out/calcit.core.mjs' +import { cache_renderer_channel_state, restore_renderer_channel_state } from './js-out/app.updater.mjs' +import { store } from './js-out/app.schema.mjs' + +const tags = core.init_tags(['renderer', 'layout-id', 'workspace-entry', 'channel-cache']) + +let renderer = core.get(store, tags.renderer) +renderer = core.assoc(renderer, tags['layout-id'], 'layout-a') +renderer = core.assoc(renderer, tags['workspace-entry'], core._$n__$M_(tags['layout-id'], 'layout-a')) + +renderer = cache_renderer_channel_state(renderer, 'alpha') +renderer = core.assoc(renderer, tags['layout-id'], null) +renderer = core.assoc(renderer, tags['workspace-entry'], null) +renderer = restore_renderer_channel_state(renderer, 'alpha') + +const restoredLayoutId = core.get(renderer, tags['layout-id']) +const restoredWorkspaceEntry = core.get(renderer, tags['workspace-entry']) +const restoredWorkspaceLayoutId = core.get(restoredWorkspaceEntry, tags['layout-id']) +const cachedChannels = core.count(core.keys(core.get(renderer, tags['channel-cache']))) + +if (restoredLayoutId !== 'layout-a') { + console.error('Expected layout-id to be restored for alpha, got:', restoredLayoutId) + process.exit(1) +} + +if (restoredWorkspaceLayoutId !== 'layout-a') { + console.error('Expected workspace-entry layout-id to be restored for alpha, got:', restoredWorkspaceLayoutId) + process.exit(1) +} + +if (cachedChannels < 1) { + console.error('Expected at least one cached channel entry, got:', cachedChannels) + process.exit(1) +} + +console.log('channel cache smoke passed') +EOF \ No newline at end of file diff --git a/test/ci-renderer-dsl.sh b/test/ci-renderer-dsl.sh index c2e723e..baa7c8a 100644 --- a/test/ci-renderer-dsl.sh +++ b/test/ci-renderer-dsl.sh @@ -6,3 +6,4 @@ ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd) cd "$ROOT_DIR" bash test/snapshot-smoke.sh +bash test/channel-cache-smoke.sh From 712de95a09b18d6073e0e446db0afc7464db4397 Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 10 Jul 2026 00:20:40 +0800 Subject: [PATCH 20/26] chore: upgrade deps and add :js-ffi schemas - caps upgrade --all: calcit-version 0.12.35 -> 0.12.49 - Add :features #{} :js-ffi to 10 defs using JS FFI - Remaining 23 warnings from respo-alerts.calcit (external) --- calcit.cirru | 95 ++++++++++++++++++++++++++++++++++++---------------- deps.cirru | 12 +++---- package.json | 2 +- yarn.lock | 10 +++--- 4 files changed, 79 insertions(+), 40 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index b4c29ef..62b1fb7 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -662,7 +662,7 @@ :style $ {} (:min-height |120px) (:padding 12) (:border-radius 12) (:background-color |#fff) (:overflow |auto) (:border "|1px solid #eadccf") (:color |#6f5743) (:font-size 13) (:line-height |1.6) (:white-space |pre-wrap) :innerHTML $ if (nil? svg-str) "|Rendering Mermaid diagram..." svg-str :examples $ [] - |effect-echarts $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |effect-echarts $ %{} :CodeEntry (:doc |) :code $ quote defeffect effect-echarts (option) (action el at?) let @@ -684,7 +684,11 @@ :update $ do (.!debug js/console "|[echarts] update") (render-chart) :unmount $ do (.!debug js/console "|[echarts] unmount") (dispose-chart) :examples $ [] - |effect-mathml $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic + :features $ #{} :js-ffi + |effect-mathml $ %{} :CodeEntry (:doc |) :code $ quote defeffect effect-mathml (expr display) (action el at?) case-default action nil @@ -692,6 +696,10 @@ :update $ render-mathml-on el expr display :unmount $ set! (.-innerHTML el) | :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic :dynamic + :features $ #{} :js-ffi |effect-mermaid $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defeffect effect-mermaid (text) (action el at?) @@ -708,7 +716,7 @@ render-mermaid-on el payload :unmount $ .!debug js/console "|[mermaid] unmount" :examples $ [] - |effect-page-title $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |effect-page-title $ %{} :CodeEntry (:doc |) :code $ quote defeffect effect-page-title (selected-channel) (action el at?) let @@ -718,6 +726,10 @@ :update $ set! (.-title js/document) next-title :unmount $ set! (.-title js/document) (page-title-text nil) :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic + :features $ #{} :js-ffi |ensure-mermaid! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn ensure-mermaid! () $ when @@ -835,7 +847,7 @@ %:: LayoutNode :math (:expr node) math-display-value $ :display node :examples $ [] - |render-mathml-on $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |render-mathml-on $ %{} :CodeEntry (:doc |) :code $ quote defn render-mathml-on (el expr display) let @@ -844,7 +856,11 @@ set! (.-innerHTML el) | .!appendChild el root :examples $ [] - |render-mermaid-on $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic :dynamic :dynamic + :features $ #{} :js-ffi + |render-mermaid-on $ %{} :CodeEntry (:doc |) :code $ quote defn render-mermaid-on (el payload) hint-fn $ {} (:async true) @@ -878,6 +894,10 @@ reset! *rendered-svgs $ assoc @*rendered-svgs source false .!error js/console "|[mermaid] render failed" error :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic :dynamic + :features $ #{} :js-ffi |validate-layout $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn validate-layout (layout) (parse-layout-node layout |root) @@ -1031,7 +1051,7 @@ :code $ quote defn current-url-channel () $ current-url-param |channel :examples $ [] - |current-url-param $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |current-url-param $ %{} :CodeEntry (:doc |) :code $ quote defn current-url-param (key) let @@ -1042,6 +1062,10 @@ > (count value) 0 , value nil :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic + :features $ #{} :js-ffi |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote def dev? $ = |dev (get-env |mode |release) @@ -1167,7 +1191,7 @@ |*ws $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote (defatom *ws nil) :examples $ [] - |build-saved-report-entry $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |build-saved-report-entry $ %{} :CodeEntry (:doc |) :code $ quote defn build-saved-report-entry (relay renderer) let @@ -1188,6 +1212,10 @@ :layout layout-dsl :source source :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :dynamic :dynamic + :features $ #{} :js-ffi |dispatch! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn dispatch! (op) @@ -1206,7 +1234,7 @@ _ nil , nil :examples $ [] - |ensure-relay! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |ensure-relay! $ %{} :CodeEntry (:doc |) :code $ quote defn ensure-relay! () $ when (nil? @*ws) dispatch! $ :: :relay-status |connecting nil @@ -1227,6 +1255,10 @@ dispatch! $ :: :relay-status |closed "|Relay connection closed, retrying..." flipped js/setTimeout 2000 ensure-relay! :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] + :features $ #{} :js-ffi |handle-channel-state! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn handle-channel-state! (ws client-id channels) @@ -1513,10 +1545,14 @@ recur (rest path) if (= acc |) step $ str acc |. step :examples $ [] - |layout-path-segment-pattern $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |layout-path-segment-pattern $ %{} :CodeEntry (:doc |) :code $ quote def layout-path-segment-pattern $ new js/RegExp "|^([0-9]+)$" :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] + :features $ #{} :js-ffi |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn main! () @@ -1684,22 +1720,27 @@ if (some? normalized) (:: :node normalized) (:: :invalid) |patch $ let normalized $ normalize-layout-path (:path payload) - if (some? normalized) (:: :patch normalized $ :changes payload) (:: :invalid) + if (some? normalized) + :: :patch normalized $ :changes payload + :: :invalid |replace $ let normalized $ normalize-layout-path (:path payload) - if - some? normalized + if (some? normalized) :: :replace normalized $ or (:node payload) (:dsl payload) :: :invalid :: :invalid :examples $ [] - |persist-storage! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |persist-storage! $ %{} :CodeEntry (:doc |) :code $ quote defn persist-storage! () println "|Saved at" $ .!toISOString (new js/Date) js/localStorage.setItem (:storage-key config/site) format-cirru-edn $ :store @*reel :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] + :features $ #{} :js-ffi |pick-layout-child $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn pick-layout-child (children position) @@ -1927,13 +1968,19 @@ if (some? channel) assoc renderer :channel-cache $ assoc or (:channel-cache renderer) {} - , channel $ pick-renderer-channel-state renderer + , channel (pick-renderer-channel-state renderer) , renderer :examples $ [] |pick-renderer-channel-state $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn pick-renderer-channel-state (renderer) - {} (:layout $ :layout renderer) (:layout-dsl $ :layout-dsl renderer) (:layout-id $ :layout-id renderer) (:layout-source $ :layout-source renderer) (:last-request $ :last-request renderer) (:last-error $ :last-error renderer) + {} + :layout $ :layout renderer + :layout-dsl $ :layout-dsl renderer + :layout-id $ :layout-id renderer + :layout-source $ :layout-source renderer + :last-request $ :last-request renderer + :last-error $ :last-error renderer :storage-status $ :storage-status renderer :storage-error $ :storage-error renderer :storage-pending $ :storage-pending renderer @@ -1943,16 +1990,7 @@ :examples $ [] |renderer-channel-default $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote - def renderer-channel-default $ {} - :layout nil - :layout-dsl nil - :layout-id nil - :layout-source | - :last-request nil - :last-error nil - :storage-status |idle - :storage-error nil - :storage-pending nil + def renderer-channel-default $ {} (:layout nil) (:layout-dsl nil) (:layout-id nil) (:layout-source |) (:last-request nil) (:last-error nil) (:storage-status |idle) (:storage-error nil) (:storage-pending nil) :storage-entries $ [] :selected-storage nil :workspace-entry nil @@ -1961,9 +1999,10 @@ :code $ quote defn restore-renderer-channel-state (renderer channel) let - next-state $ merge renderer-channel-default $ get - or (:channel-cache renderer) {} - , channel + next-state $ merge renderer-channel-default + get + or (:channel-cache renderer) {} + , channel -> renderer assoc :layout $ :layout next-state assoc :layout-dsl $ :layout-dsl next-state diff --git a/deps.cirru b/deps.cirru index 2494c54..86e6394 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,7 +1,7 @@ -{} (:calcit-version |0.12.35) - :dependencies $ {} (|Respo/alerts.calcit |0.10.11) - |Respo/reel.calcit |0.6.3 - |Respo/respo-ui.calcit |0.6.4 - |Respo/respo.calcit |0.16.45 - |calcit-lang/memof |0.0.23 +{} (:calcit-version |0.12.49) + :dependencies $ {} (|Respo/alerts.calcit |0.10.12) + |Respo/reel.calcit |0.6.4 + |Respo/respo-ui.calcit |0.6.5 + |Respo/respo.calcit |0.16.48 + |calcit-lang/memof |0.0.24 diff --git a/package.json b/package.json index f672acf..8348183 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@calcit/procs": "^0.12.35", + "@calcit/procs": "^0.12.49", "echarts": "^6.1.0", "mermaid": "^11.15.0" }, diff --git a/yarn.lock b/yarn.lock index bfe6fc0..5d09579 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,14 +22,14 @@ __metadata: languageName: node linkType: hard -"@calcit/procs@npm:^0.12.35": - version: 0.12.35 - resolution: "@calcit/procs@npm:0.12.35" +"@calcit/procs@npm:^0.12.49": + version: 0.12.49 + resolution: "@calcit/procs@npm:0.12.49" dependencies: "@calcit/ternary-tree": "npm:0.0.26" "@cirru/parser.ts": "npm:^0.0.9" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/b9d8e2926911e81e56635653c596c2b217fe260ae768a132df49dfd2e7f4ee7707882802e06be0f191a4b5e7df6c415f78abb8d8fb8fb06176611139badc2fde + checksum: 10c0/45db4a02e75ed95e306ad9f979cc8ced6bca9a2dccb8948802f41e50b69b2c8b637034e13a96213fd0151e88e95b9d8c5e1843c8a6d5b5ba5b8fac88498794eb languageName: node linkType: hard @@ -1958,7 +1958,7 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@calcit/procs": "npm:^0.12.35" + "@calcit/procs": "npm:^0.12.49" bottom-tip: "npm:^0.1.5" echarts: "npm:^6.1.0" mermaid: "npm:^11.15.0" From bcffd8386ee0718ea84dfd841690ac24291212a1 Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 10 Jul 2026 00:20:58 +0800 Subject: [PATCH 21/26] fix: bump setup-cr 0.0.8->0.0.9 --- .github/workflows/upload.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload.yaml b/.github/workflows/upload.yaml index b9c13ed..d53b6b2 100644 --- a/.github/workflows/upload.yaml +++ b/.github/workflows/upload.yaml @@ -18,7 +18,7 @@ jobs: - run: corepack enable && corepack prepare yarn@4.12.0 --activate - - uses: calcit-lang/setup-cr@0.0.8 + - uses: calcit-lang/setup-cr@0.0.9 - name: "compiles to js" run: > From 1bf688deda38c8718691e0e0cf01ec7c0fe19ea1 Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 10 Jul 2026 00:24:51 +0800 Subject: [PATCH 22/26] chore: upgrade alerts 0.10.12->0.10.13; fix main! schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - caps upgrade --all picks up respo-alerts.calcit 0.10.13 (with :js-ffi) - Add missing :js-ffi to app.main/main! - Now 0 warnings 🎉 --- calcit.cirru | 6 +++++- deps.cirru | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 62b1fb7..6a7678b 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -1553,7 +1553,7 @@ {} (:return :dynamic) :args $ [] :features $ #{} :js-ffi - |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |main! $ %{} :CodeEntry (:doc |) :code $ quote defn main! () println "|Running mode:" $ if config/dev? |dev |release @@ -1575,6 +1575,10 @@ ensure-relay! println "|App started." :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] + :features $ #{} :js-ffi |merge-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote defn merge-layout-node-at-path (node path changes) diff --git a/deps.cirru b/deps.cirru index 86e6394..b214cb1 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,6 +1,6 @@ {} (:calcit-version |0.12.49) - :dependencies $ {} (|Respo/alerts.calcit |0.10.12) + :dependencies $ {} (|Respo/alerts.calcit |0.10.13) |Respo/reel.calcit |0.6.4 |Respo/respo-ui.calcit |0.6.5 |Respo/respo.calcit |0.16.48 From 3beac3d0ef59c02baf04b7f830dda6d8619fa9cb Mon Sep 17 00:00:00 2001 From: tiye Date: Fri, 10 Jul 2026 01:56:48 +0800 Subject: [PATCH 23/26] docs: rename RFC to full date, add form-state-events-transform RFC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename 2026-05 -> 2026-05-26 based on git history (first commit date) - Add RFC: edn-renderer 的表单状态、事件与数据转换 --- ...2026-05-26-mermaid-rendering-stability.md} | 0 .../2026-07-10-form-state-events-transform.md | 262 ++++++++++++++++++ 2 files changed, 262 insertions(+) rename RFCs/{2026-05-mermaid-rendering-stability.md => 2026-05-26-mermaid-rendering-stability.md} (100%) create mode 100644 RFCs/2026-07-10-form-state-events-transform.md diff --git a/RFCs/2026-05-mermaid-rendering-stability.md b/RFCs/2026-05-26-mermaid-rendering-stability.md similarity index 100% rename from RFCs/2026-05-mermaid-rendering-stability.md rename to RFCs/2026-05-26-mermaid-rendering-stability.md diff --git a/RFCs/2026-07-10-form-state-events-transform.md b/RFCs/2026-07-10-form-state-events-transform.md new file mode 100644 index 0000000..024c66a --- /dev/null +++ b/RFCs/2026-07-10-form-state-events-transform.md @@ -0,0 +1,262 @@ +# RFC: edn-renderer 的表单状态、事件与数据转换(草稿) + +## 状态 + +Draft + +## 背景 + +`edn-renderer` 目前是一个**纯展示型**的 DSL 渲染器:`LayoutNode`(一个 +`defenum`,包含 `:column` `:row` `:card` `:text` `:badge` `:divider` `:button` +`:input` `:markdown` `:mermaid` `:chart` `:math`)由 `comp-layout-node` 只读渲染。 +`button` 目前写死 `:disabled true`,`input` 没有接 `on-input`——CLI 可以下发 +布局并检查/局部修改(`patch`/`replace`),但页面本身从不把用户交互回传出去。 + +我们想让 `edn-renderer` 支持简单的**表单**:字段持有本地状态,一个提交动作把 +收集到的值回传给 CLI,部分字段还能由其他字段派生/格式化得到。 + +本 RFC 只覆盖设计,以下范围已经确定: + +- 回传 CLI 的时机是**只在提交时回传一次**(不做逐字符的事件流量)。 +- 数据转换覆盖**派生/计算字段**和**展示前的格式化**,不是自由脚本能力。 +- 新增表单控件:`select`、`checkbox`、`radio`、`textarea`(在已有的 `input` + 之外)。 + +## 目标 + +- 字段在布局停留期间持有本地、可编辑的状态。 +- 一个 `submit` 式动作把所有字段值收集成一个 map,通过 relay 回传给 CLI。 +- 部分字段能用一棵**安全、白名单化**的表达式树从其他字段计算得到(派生值、 + 格式化展示),绝不使用 `eval`。 +- 扩展要贴合现有架构(`defenum` + `match` 渲染、`*reel`/`dispatch!`/`updater` + reducer、relay 请求/响应协议),不引入新范式。 + +## 非目标 + +- 逐字符(每次按键)实时回传给 CLI。 +- DSL 内的任意代码执行/脚本能力(安全边界,见下文)。 +- 字段级校验的完整交互体验(错误态样式、异步校验器)——本 RFC 只包含 + 必填/格式校验需要的最小挂钩,完整校验体验留给后续 RFC。 + +## 设计 + +### 1. LayoutNode 枚举扩展 + +在 `LayoutNode` 这个 `defenum` +([app.comp.container/LayoutNode](../js-out/app.comp.container.mjs))上新增 +以下分支(其余既有分支不变): + +```cirru +defenum LayoutNode + :select :string :list :string + :checkbox :string :string :bool + :radio :string :list :string + :textarea :string :string :string + :form :list :string + :computed :dynamic :string +``` + +每个分支的字段含义: + +| 分支 | 字段 1 | 字段 2 | 字段 3 | +|------|--------|--------|--------| +| `:select` | `name`(字段名) | `options`(选项列表) | `value`(当前值) | +| `:checkbox` | `name` | `label`(勾选框文案) | `checked`(是否勾选) | +| `:radio` | `name` | `options` | `value` | +| `:textarea` | `name` | `placeholder` | `value` | +| `:form` | `children`(子字段节点) | `submit-label`(提交按钮文案) | — | +| `:computed` | `transform`(转换树,见 §4) | `format`(展示格式化模板) | — | + +- `select`/`radio` 的 `options` 是一组 `{} (:label ..) (:value ..)` 形式的 map(跟 + `chart` 的 `:series` 用同一套约定),例如: + + ```cirru + {} + :type |select + :name |city + :options $ [] + {} (:label |北京) (:value |bj) + {} (:label |上海) (:value |sh) + :value |bj + ``` + +- `input`/`textarea` 接上真正的双向绑定:输入时触发 `on-input`,改为 + `dispatch!` 一个 `:form-field-changed` op,而不是纯装饰。 +- `button` 新增可选的 `:action` 字段。`:action :submit` 让它触发提交流程, + 而不是渲染成 `:disabled true`。 +- `form` 是新增的容器(和 `card`类似),子节点是各个字段;它决定提交时收集 + 哪些字段(见下文),并渲染提交按钮。 +- `computed` 渲染只读的派生文本,同时覆盖"派生字段"和"展示前格式化"这两个 + 已确认的使用场景。 + +所有新增分支都走既有的校验路径:`parse-layout-node`/`validate-layout-node`, +格式不对时和现在一样快速失败。 + +### 2. 状态模型 + +在 `*reel` 的 `:renderer` 下新增一个 `:form-state` map +([app.updater/updater](../js-out/app.updater.mjs)),以字段的 `:name` 为 +key: + +```cirru +{} + :price 12 + :qty 3 + :agree true +``` + +`updater` 里新增的 reducer 分支,沿用现有的 `update store :renderer (fn +(renderer) ...)` 模式(和 `:relay-status`、`:select-history` 等现有分支 +一致): + +```cirru +tag-match op + (:form-field-changed name value) + update store :renderer $ fn (renderer) + assoc-in (or renderer {}) ([] :form-state name) value + (:form-reset) + update store :renderer $ fn (renderer) + assoc (or renderer {}) :form-state $ {} +``` + +字段初始值来自 DSL 里各字段节点自身的 `:value`/`:checked`(这样 CLI 下发 +布局时可以预填表单),缺省则为空字符串/`false`。 + +### 3. 事件流程 + +- **本地编辑**(打字、切换 checkbox、选 select 选项):纯本地 `dispatch!`, + 更新 `:form-state`,不产生 relay 流量——符合"只在提交时回传一次"的 + 决定。 +- **提交**:点击 `form` 内、`:action :submit` 的 `button` 时: + 1. 读取当前 `:form-state`(只取这个 `form` 子树下声明过的字段名——用 + 和 `layout-node-at-path`/`summarize-layout-node` 一样的方式遍历子树)。 + 2. 计算这个 form 内所有 `computed` 字段,补全派生值。 + 3. 通过既有的 `send-relay-frame!`(和 `send-genui-ack!` 走同一条发送 + 路径)发送一个新的 relay 事件帧,例如: + + ```cirru + {} + :kind :event + :channel selected + :payload $ {} + :op :form-submitted + :layout_id $ :layout-id renderer + :data $ {} + :price 12 + :qty 3 + :full-name "|Jane Doe" + ``` + + 4. 同时本地 `dispatch!` 一个 `:form-submitted` op,让页面能展示"已提交" + 的确认状态(纯 UI 反馈)。 + + 待确认问题:需要对照 `edn-relay` 的文档/`help` 确认,一个不挂在某个入站 + `request-id` 上的 `:kind :event` 帧,relay 是否已经会广播给 CLI 侧订阅了 + 该 channel 的一方,还是需要给 relay 补一个"renderer 主动推送帧"的小能力。 + 本 RFC 先假设协议里已有的按 channel 广播语义可以直接复用。 + +### 4. 数据转换(安全的表达式树) + +因为 DSL payload 来自不受信任的远端 CLI,转换能力必须保持**声明式、 +白名单化**——绝不把 DSL 里的字符串当 JS/Calcit 代码 `eval`(见"安全考虑")。 + +沿用这个项目已有的建模方式——`LayoutNode` 本身就是一个 `defenum`——转换树 +也建成一个 `Transform` `defenum`,而不是靠字符串 tag 拼出来的临时 map: + +```cirru +defenum Transform + :field :string + :const :dynamic + :add :list + :sub :list + :mul :list + :div :list + :concat :list + :format :string :list + :round :list + :default :list + :if :list +``` + +`:add`/`:sub`/`:mul`/`:div`/`:concat`/`:round`/`:default`/`:if` 的字段都是 +`:list`,装的是嵌套的子 `Transform`;`:format` 除了子 `Transform` 列表,还带 +一个模板字符串。 + +初始白名单覆盖"派生/计算字段"和"展示前格式化"这两个场景: + +| 变体 | 参数 | 用途 | +|------|------|------| +| `:field` | 字段名 | 读取 `:form-state` 里的一个字段 | +| `:const` | 字面量 | 常量值 | +| `:add` `:sub` `:mul` `:div` | 2 个数值子转换 | 数值字段间的算术 | +| `:concat` | N 个字符串子转换 | 拼接文本(比如拼出全名) | +| `:format` | 模板字符串 + N 个子转换 | `"|{0} - {1}"` 这类插值展示 | +| `:round` | 1 个数值子转换 + 精度 | 数字展示格式化 | +| `:default` | 1 个子转换 + 兜底值 | 处理空字段 | +| `:if` | 3 个子转换(条件/then/else) | 简单的条件展示 | + +DSL 线上格式(CLI 下发的 EDN,还没解析成 `Transform` 枚举前)继续沿用整个 +项目通用的"map + tag 字段"约定,用 `:kind` 标注转换节点类型(避免跟 +`LayoutNode` 的 `:type` 混淆): + +```cirru +{} + :kind |add + :args $ [] + {} (:kind |field) (:name |price) + {} (:kind |field) (:name |qty) +``` + +新增一个 `eval-transform` 函数(放在 `app.comp.container`,或者新开一个 +`app.transform` 命名空间),对解析后的 `Transform` 值做 `match`,从 +`:form-state` 里查字段值。不做"按字符串动态派发到任意函数",白名单就是这个 +封闭的 `match`。 + +`computed` 布局节点携带一棵转换树 + 可选的 `:format`(比如 +`"|¥{value}"`),在渲染成文本之前应用在最终的标量结果上。 + +### 5. Relay 协议触点 + +- `handle-genui-event!`/`handle-renderer-event!` 的请求侧不需要改动——布局 + 仍然是 CLI → renderer 单向下发。 +- 新增:renderer → CLI 的提交推送(`:op :form-submitted`,见 §3)。这是 + 唯一新增的协议面,其余都是内部状态。 + +## 安全考虑 + +- **不对远端字符串做 `eval`。** 转换树是封闭词表的数据,由一个白名单化的 + Calcit 函数解释执行,而不是从 DSL 里读出来的 JS 源码或 Calcit 代码。这样 + 可以避免通过精心构造的布局 payload 触发远程代码执行(对应 OWASP Top 10 + 里 A03/A08 这类风险)。 +- **限制转换树的深度/规模。** `eval-transform` 应该拒绝超过一定深度/参数 + 个数的树,避免病态或超大 payload 导致过度递归(基础的资源耗尽防护),和 + 现有 `validate-layout` 的快速失败行为保持一致。 +- **表单数据只包含 DSL 声明过的内容。** 提交 payload 只由 `form` 子树里 + 声明过的字段名构成,不是把 renderer/app 内部状态原样导出。 + +## 实施计划 + +1. 扩展 `LayoutNode` 这个 `defenum` + `parse-layout-node`/ + `validate-layout-node`,支持 `select`、`checkbox`、`radio`、`textarea`、 + `form`、`computed`。先做只读渲染,验证 DSL 形状端到端可用,暂不接状态。 +2. 在 `*reel` 里加 `:form-state`,在 `updater` 里加 + `:form-field-changed`/`:form-reset` op,给 `input`/`textarea`/`select`/ + `checkbox`/`radio` 接上 `on-input`/`on-change` 派发这些 op。 +3. 新增 `Transform` `defenum` + `eval-transform` 白名单解释器,实现 + `computed` 节点的渲染。 +4. 让 `form` 内 `:action :submit` 的 `button` 收集该子树的表单状态、跑一遍 + `computed` 字段、发送 `:form-submitted` relay 事件;再加一个本地 + `:form-submitted` op 用于 UI 上的确认状态。 +5. 更新 [COMPONENTS.md](../COMPONENTS.md) 和 [SKILL.md](../SKILL.md),补上 + 新节点类型、转换算子表、`form-submitted` 事件的形状——这些文档本身就是 + 通过 `edn-relay help`/`skill` 实时暴露出去的。 +6. 在 [test/](../test/) 下补 fixture:字段状态回环、提交 payload 形状、 + 转换白名单求值(包括故意用一个不在白名单里的算子/超大转换树,验证会被 + 拒绝)。 + +## 待确认问题 + +- 和 `edn-relay` 对一下 renderer → CLI 的主动推送帧协议(见 §3)。 +- 是否需要在提交前就把字段级必填/格式校验展示出来(而不是只靠 CLI 拒绝 + 不合法的 `:form-submitted` 数据)?本 RFC 先不做,标记为大概率的 + 后续跟进项。 From a442b3f4d39f226d9b4a248bbb89d3de139dcc61 Mon Sep 17 00:00:00 2001 From: tiye Date: Mon, 1 Jun 2026 13:56:40 +0800 Subject: [PATCH 24/26] upgrade deps; refine bar chart style --- calcit.cirru | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 6a7678b..d04fce8 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -43,19 +43,33 @@ {} {} $ :title {} $ :text title + category-axis $ {} + :type |category + :data names + :axisLabel $ {} + :interval 0 + :rotate 20 + :hideOverlap false + grid-part $ {} + :grid $ {} + :left 48 + :right 24 + :top 72 + :bottom 72 + :containLabel true base $ merge {} (:animation false) :tooltip $ {} - , title-part + , title-part grid-part case-default normalized-kind merge base $ {} - :xAxis $ {} (:type |category) (:data names) + :xAxis category-axis :yAxis $ {} (:type |value) :series $ [] {} (:type |bar) (:data values) |line $ merge base {} - :xAxis $ {} (:type |category) (:data names) + :xAxis category-axis :yAxis $ {} (:type |value) :series $ [] {} (:type |line) (:data values) @@ -69,7 +83,7 @@ :value $ :value item |scatter $ merge base {} - :xAxis $ {} (:type |category) (:data names) + :xAxis category-axis :yAxis $ {} (:type |value) :series $ [] {} (:type |scatter) (:data values) From bdc868d33199ac973ab7ed3938e691ed3c61fda7 Mon Sep 17 00:00:00 2001 From: tiye Date: Thu, 11 Jun 2026 12:23:36 +0800 Subject: [PATCH 25/26] upgrade calcit deps --- calcit.cirru | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index d04fce8..50ce208 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -43,33 +43,21 @@ {} {} $ :title {} $ :text title - category-axis $ {} - :type |category - :data names - :axisLabel $ {} - :interval 0 - :rotate 20 - :hideOverlap false + category-axis $ {} (:type |category) (:data names) + :axisLabel $ {} (:interval 0) (:rotate 20) (:hideOverlap false) grid-part $ {} - :grid $ {} - :left 48 - :right 24 - :top 72 - :bottom 72 - :containLabel true + :grid $ {} (:left 48) (:right 24) (:top 72) (:bottom 72) (:containLabel true) base $ merge {} (:animation false) :tooltip $ {} , title-part grid-part case-default normalized-kind - merge base $ {} - :xAxis category-axis + merge base $ {} (:xAxis category-axis) :yAxis $ {} (:type |value) :series $ [] {} (:type |bar) (:data values) |line $ merge base - {} - :xAxis category-axis + {} (:xAxis category-axis) :yAxis $ {} (:type |value) :series $ [] {} (:type |line) (:data values) @@ -82,8 +70,7 @@ :name $ :label item :value $ :value item |scatter $ merge base - {} - :xAxis category-axis + {} (:xAxis category-axis) :yAxis $ {} (:type |value) :series $ [] {} (:type |scatter) (:data values) From 540d4f95554985880c85ed4b72834650eea61f79 Mon Sep 17 00:00:00 2001 From: tiye Date: Mon, 20 Jul 2026 17:34:30 +0800 Subject: [PATCH 26/26] upgrade deps --- calcit.cirru | 28 ++++++++++++++-------------- deps.cirru | 5 ++--- package.json | 2 +- yarn.lock | 10 +++++----- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/calcit.cirru b/calcit.cirru index 50ce208..6d34561 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -687,7 +687,7 @@ :examples $ [] :schema $ :: :fn {} (:return :dynamic) - :args $ [] :dynamic + :args $ [] :map :features $ #{} :js-ffi |effect-mathml $ %{} :CodeEntry (:doc |) :code $ quote @@ -699,7 +699,7 @@ :examples $ [] :schema $ :: :fn {} (:return :dynamic) - :args $ [] :dynamic :dynamic + :args $ [] :list :string :features $ #{} :js-ffi |effect-mermaid $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -729,7 +729,7 @@ :examples $ [] :schema $ :: :fn {} (:return :dynamic) - :args $ [] :dynamic + :args $ [] :string :features $ #{} :js-ffi |ensure-mermaid! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -858,8 +858,8 @@ .!appendChild el root :examples $ [] :schema $ :: :fn - {} (:return :dynamic) - :args $ [] :dynamic :dynamic :dynamic + {} (:return :unit) + :args $ [] :js-object :list :string :features $ #{} :js-ffi |render-mermaid-on $ %{} :CodeEntry (:doc |) :code $ quote @@ -896,8 +896,8 @@ .!error js/console "|[mermaid] render failed" error :examples $ [] :schema $ :: :fn - {} (:return :dynamic) - :args $ [] :dynamic :dynamic + {} (:return :unit) + :args $ [] :js-object :map :features $ #{} :js-ffi |validate-layout $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1064,8 +1064,8 @@ , value nil :examples $ [] :schema $ :: :fn - {} (:return :dynamic) - :args $ [] :dynamic + {} (:return :string) + :args $ [] :string :features $ #{} :js-ffi |dev? $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1214,8 +1214,8 @@ :source source :examples $ [] :schema $ :: :fn - {} (:return :dynamic) - :args $ [] :dynamic :dynamic + {} (:return :map) + :args $ [] :map :map :features $ #{} :js-ffi |dispatch! $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -1257,7 +1257,7 @@ flipped js/setTimeout 2000 ensure-relay! :examples $ [] :schema $ :: :fn - {} (:return :dynamic) + {} (:return :unit) :args $ [] :features $ #{} :js-ffi |handle-channel-state! $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -1577,7 +1577,7 @@ println "|App started." :examples $ [] :schema $ :: :fn - {} (:return :dynamic) + {} (:return :unit) :args $ [] :features $ #{} :js-ffi |merge-layout-node-at-path $ %{} :CodeEntry (:doc |) (:schema :dynamic) @@ -1743,7 +1743,7 @@ format-cirru-edn $ :store @*reel :examples $ [] :schema $ :: :fn - {} (:return :dynamic) + {} (:return :unit) :args $ [] :features $ #{} :js-ffi |pick-layout-child $ %{} :CodeEntry (:doc |) (:schema :dynamic) diff --git a/deps.cirru b/deps.cirru index b214cb1..676735c 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,7 +1,6 @@ - -{} (:calcit-version |0.12.49) +{} (:calcit-version |0.12.50) :dependencies $ {} (|Respo/alerts.calcit |0.10.13) |Respo/reel.calcit |0.6.4 |Respo/respo-ui.calcit |0.6.5 - |Respo/respo.calcit |0.16.48 + |Respo/respo.calcit |0.16.51 |calcit-lang/memof |0.0.24 diff --git a/package.json b/package.json index 8348183..93dd1ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@calcit/procs": "^0.12.49", + "@calcit/procs": "^0.12.50", "echarts": "^6.1.0", "mermaid": "^11.15.0" }, diff --git a/yarn.lock b/yarn.lock index 5d09579..f8eef4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,14 +22,14 @@ __metadata: languageName: node linkType: hard -"@calcit/procs@npm:^0.12.49": - version: 0.12.49 - resolution: "@calcit/procs@npm:0.12.49" +"@calcit/procs@npm:^0.12.50": + version: 0.12.50 + resolution: "@calcit/procs@npm:0.12.50" dependencies: "@calcit/ternary-tree": "npm:0.0.26" "@cirru/parser.ts": "npm:^0.0.9" "@cirru/writer.ts": "npm:^0.1.9" - checksum: 10c0/45db4a02e75ed95e306ad9f979cc8ced6bca9a2dccb8948802f41e50b69b2c8b637034e13a96213fd0151e88e95b9d8c5e1843c8a6d5b5ba5b8fac88498794eb + checksum: 10c0/415f3b5a8edff378801da4b04c8ec621e24fc51e6ef97e3077271b8fe16fb870f0349791ed28dca0e245429808d789aebc14c8da0cdc8479a6cf7f082782422d languageName: node linkType: hard @@ -1958,7 +1958,7 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@calcit/procs": "npm:^0.12.49" + "@calcit/procs": "npm:^0.12.50" bottom-tip: "npm:^0.1.5" echarts: "npm:^6.1.0" mermaid: "npm:^11.15.0"