@dsh-workbench/module-tools 0.2.14 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,7 @@ dsh-wb-module publish # 发货前的清单:门禁 + 三张表 +
26
26
  | `source` | 源码边界:不直接引 dsh 的包、不引不是种子词的 `@deepseek-ai/*` |
27
27
  | `primitives` | dsh 原语只从模块表(`kit`)拿 |
28
28
  | `kit` | 这份外壳的 kit 成员到底有哪些(缺什么会当场点名) |
29
- | `cards` | 卡片渲染:**七态 × zh/en 各渲一遍**(外壳侧输入 idle / loading / loading-刷新保留旧值 / ready / ready-空容器 / unreadable-error / unreadable-能力闸缺插件;`useApiResource` 与 `useCapabilityGate` 由判据喂,`useUnitBoard` 用模块仓里装的那份**真壳**)—— 不许抛、不许空白、文字里不许出现 `undefined` / `NaN` / `[object Object]` / `null`、不许裸键名、要套外壳的卡壳、清单 `titleKey` 在两语字典里都有落点。**不断言"画的是哪一态那一屏"**(那归模块自己的逐态测试与 `design.json` 状态矩阵);模块没装外壳时逐态那半跳过、单次全桩渲染照旧跑 |
29
+ | `cards` | 卡片渲染:**八态 × zh/en 各渲一遍**(外壳侧输入 idle / loading / loading-刷新保留旧值(含旧时间戳 `at`)/ ready / ready-空容器 / unreadable-error / unreadable-能力闸缺插件 / unreadable-能力闸缺命令;`useApiResource` 与 `useCapabilityGate` 由判据喂,`useUnitBoard` 用模块仓里装的那份**真壳**)—— 不许抛、不许空白、文字里不许出现 `undefined` / `NaN` / `[object Object]` / `null`、不许裸键名、要套外壳的卡壳、清单 `titleKey` 在两语字典里都有落点。**不断言"画的是哪一态那一屏"**(那归模块自己的逐态测试与 `design.json` 状态矩阵);模块没装外壳时逐态那半跳过、单次全桩渲染照旧跑 |
30
30
  | `standalone` | 独立构建:产物不依赖工坊里跑 |
31
31
  | `markers` | 公开包无内部标识(内网域名、企业内部名) |
32
32
  | `style` | 样式归属:模块不许定义外壳类(`wb-*`)的规则(判据看**复合选择器**,`:global(.wb-x)` 也算,只有同一个复合选择器里带模块自己的类才放行);**裸 `<a>` 与裸交互控件(`<button>` / `<input>` / `<select>`)都判红** —— 前者用 kit 的 `Link`,后者用 `Button` / `TextField` / `SelectField` / `ChoiceGroup`;只有 `<textarea>` 与 `input[type=hidden\|file]` 例外(两处都没有那种形态);**手写的「标签 + 控件」行(标签元素紧邻 `SelectField` / `TextField` / `ChoiceGroup` / `Input`)与模块自己给 `field`/`form` 容器设的行距(`gap` / 上下 `margin`)也判红** —— 这一层归 kit 的 `FormRow(s)`([ADR-0052](../../docs/adr/0052-the-form-row-is-one-component.md):面 → 行 → 控件);现存违规收进 `FORM_ROW_EXEMPT`(棘轮:迁一处删一行,且表里已经不再命中的行会当场报红);**手写的操作区(借了 `wb-formActions` 槽类手摆按钮)也判红** —— 操作区归 kit 的 `FormActions`([ADR-0054](../../docs/adr/0054-the-action-area-is-one-component.md):顺序 / 一个区一颗 `primary` / `danger` 的位置 / 间距 / 窄容器换行都由它定,调用点只给数据与语义),现存违规收进 `FORM_ACTIONS_EXEMPT`(同一套棘轮:迁一处删一行,表里不再命中的行当场报红) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/module-tools",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "模块包的构建与门禁工具:build(单文件 ESM 宿主侧 + 套 loader 壳的浏览器侧)、check(契约/产物边界/源码边界/卡片渲染)。模块包不再各带一份构建脚本。",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "esbuild": "^0.28.2",
24
- "@dsh-workbench/shared": "0.2.14"
24
+ "@dsh-workbench/shared": "0.2.15"
25
25
  },
26
26
  "scripts": {
27
27
  "selfcheck": "node scripts/selfcheck.mjs"
@@ -19,6 +19,11 @@ const FORBIDDEN = [
19
19
 
20
20
  const WORKSPACE = { path: '/tmp/repo', workspaceId: 'wb-card-probe' };
21
21
 
22
+ // 刷新那一帧的旧时间戳(`at`)。真壳在 `ready` 那一步写 `Date.now()`,同一个 identity 重读时
23
+ // 把它原样带进 loading 相(`use-api-resource.ts:102`);这里给一个固定的过去时刻,判据不依赖
24
+ // "现在几点"(同一个值每次跑都一样,负例才好断言)。
25
+ const STALE_AT = 1_700_000_000_000;
26
+
22
27
  function anyStub(path = 'stub') {
23
28
  const target = function () {
24
29
  return anyStub(`${path}()`);
@@ -98,13 +103,14 @@ const SNAPSHOTS = [
98
103
  resource: { phase: 'error', error: { code: 'network', message: '宿主没有响应' } },
99
104
  workspace: WORKSPACE,
100
105
  },
101
- // P2 第一条:刷新形态(ADR-0022「刷新保留旧值」)。载荷与 `loading` 只差一格 —— `value` 在,
102
- // 而且是旧那份。`use-api-resource.ts:100-104` 在同一个 identity 重读时给的就是这一相,卡片
103
- // 若把"loading 就没有值"写死,脏就出在这一帧。注意它按 spec 只带 `value`、不带 `at`
104
- // (真实那一相还会带 `prev.at`)—— 这是与真壳的一处已知出入,记在 spec 的 §7.3。
106
+ // P2 第一条:刷新形态(ADR-0022「刷新保留旧值」)。载荷与 `loading` 只差两格 —— `value` 与
107
+ // `at` 都在,而且是旧那份。`use-api-resource.ts:100-104` 在同一个 identity 重读时给的就是这一相
108
+ // (`{ phase: 'loading', value: prev.value, ...(prev.at === undefined ? {} : { at: prev.at }) }`,
109
+ // 而 `prev.at` 是 `ready` 那一步写的 `Date.now()`,所以真实这一相两格一起在)。卡片若把
110
+ // "loading 就没有值"写死,脏就出在这一帧;若把旧值当新值、或拿旧 `at` 当"刚读到"去算,脏也出在这一帧。
105
111
  {
106
112
  name: 'loading(刷新:保留旧值)',
107
- resource: { phase: 'loading', value: containerStub('api', 2) },
113
+ resource: { phase: 'loading', value: containerStub('api', 2), at: STALE_AT },
108
114
  workspace: WORKSPACE,
109
115
  },
110
116
  // P2 第二条:能力闸。`useCapabilityGate` 给一个**明确的** `missing-plugin`(不是 `null`):
@@ -124,6 +130,26 @@ const SNAPSHOTS = [
124
130
  detail: '逐态探针:缺 demo-plugin',
125
131
  }),
126
132
  },
133
+ // P2 补记:能力闸的第二种 status —— `missing-commands`(插件在、命令旧)。它与缺插件那一帧
134
+ // 差的是两格:`status` 换掉,且 `fix` / `command` 都在(真壳 `probe-verdict.ts:109-116`:命令落后
135
+ // 的插件状态是 `outdated`,于是 `fix: 'update'`;`:158-160` 再带上那条升级命令)。这两格正好把
136
+ // `blockerOfCapability` 里 `install` 那一段走通:`fixAction`(`blocker.fix.update`)+ `copyAction`
137
+ // (`blocker.copy` + `command` 原文,`blocker-table.ts:109-123`),加上照旧的 retry —— 缺插件那一帧
138
+ // 两格都没给,install 是空的,只走到 retry。`sourceUnknown` 在 `fix` 在时不成立,所以不开市场那条。
139
+ {
140
+ name: 'unreadable(能力闸:缺命令)',
141
+ resource: { phase: 'ready', value: containerStub('api', 2) },
142
+ workspace: WORKSPACE,
143
+ capability: (unit) => ({
144
+ unit,
145
+ status: 'missing-commands',
146
+ required: ['demo-plugin'],
147
+ missing: ['demo.command'],
148
+ detail: '逐态探针:demo-plugin 装在,缺 demo.command',
149
+ fix: 'update',
150
+ command: 'opencli plugin update demo-plugin',
151
+ }),
152
+ },
127
153
  ];
128
154
 
129
155
  function fakeReact() {
@@ -193,32 +193,10 @@ const FORM_ROW_EXEMPT = new Map([]);
193
193
  // §12 是发版后的迁移顺序。这是**棘轮**:迁一处删一行;某个包在眼前却一处都不再命中时,
194
194
  // 那条豁免当场报红(见 run() 里的 staleActionRows)。键同形:`<包名>:<相对模块根的文件路径>`。
195
195
  // 2026-10-17 实测(只读 pduiv2):8 处 / 6 个文件 —— 全部收在这里,一条都不许写成"全部"。
196
- const FORM_ACTIONS_EXEMPT = new Map([
197
- [
198
- '@pagoda-dsh-workbench/module-argocd:src/views/BindIdentity.tsx',
199
- ':112(无候选那屏的 重试 / 去设置)· :165(绑定确认那屏的 重试 / 确认,就是这次换按钮那屏)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §2/§12)',
200
- ],
201
- [
202
- '@pagoda-dsh-workbench/module-argocd:src/views/ChooseEnvs.tsx',
203
- ':39(选择环境那屏)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §12)',
204
- ],
205
- [
206
- '@pagoda-dsh-workbench/module-argocd:src/views/ArgocdCard.tsx',
207
- ':105(卡片上那组 取消 / 重试 / 确认)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §12)',
208
- ],
209
- [
210
- '@pagoda-dsh-workbench/module-cmdb:src/views/CmdbSettingsPanes.tsx',
211
- ':55(清除确认那一对 取消 / 确认清除)· :76(编辑 / 清除入口)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §12)',
212
- ],
213
- [
214
- '@pagoda-dsh-workbench/module-cmdb:src/views/ChooseEnvsBody.tsx',
215
- ':77(选环境那一屏)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §12)',
216
- ],
217
- [
218
- '@pagoda-dsh-workbench/module-coding-analysis:src/views/EnvSettings.tsx',
219
- ':150(环境设置那一屏)—— 等外壳发出 FormActions 后随它迁(ADR-0054 §12)',
220
- ],
221
- ]);
196
+ // 2026-10-17 清空:外壳 0.2.14(含 `FormActions`)发出后,那 8 处逐条迁完(ADR-0054 §12 的 runbook),
197
+ // 按棘轮一行一行删到空。留着这张空表是这条判据的棘轮位:谁再借 `wb-formActions` 手摆操作区,
198
+ // 直接报红(不再有豁免这一层)。
199
+ const FORM_ACTIONS_EXEMPT = new Map([]);
222
200
 
223
201
  /** 裸控件出现的位置 —— 逐行看不了(一个元素会跨行),所以在全文里按标签找,再回推行号。 */
224
202
  function bareControlsIn(text) {