dsh-mini-utility-dock 0.6.0 → 0.7.0

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.en.md CHANGED
@@ -56,11 +56,11 @@ A consumer's `npm test` runs `loopback:check` / `guard:check` / `http:check`, co
56
56
 
57
57
  `dsh-plugin-parity` (a bin provided by this package) checks that cross-repo property directly: every host block compared in isolation after stripping the block's common indentation (the three, `dsh-host-http` included), pin agreement, block position, and a behavioral comparison. It is a manual diagnostic and **does not run in CI**: the property cannot hold while peers sit on a different branch. The member list is caller-supplied via `--member <repo>:<export>` — the guard factory's export name is per-repository by fragment design, so the tool presupposes no members; `--fleet <repo>` names the single member that opts into fleet mode, and only its guard is checked for "the relaxation stays bounded", while naming a repository that is not a member fails with a non-zero exit; without `--member` it runs the static checks only and discovers every checkout that embeds the guard block. `--self-test` needs no checkout at all: it exercises the block extractor and the block-dropping step against inline samples, so run it first whenever either changes.
58
58
 
59
- `dsh-plugin-docs` (another bin of this package) checks the documentation pairs for structural alignment: the two READMEs share heading levels and code-fence languages, the two CHANGELOGs expose the same releases — the `Unreleased` section included — with sections and item counts (section titles normalized through a bilingual category map); `--base <revision>` additionally requires both files of a pair to change together — a one-sided edit is a missing translation. When that revision is the all-zero null OID — which is what `github.event.before` carries for a newly created or force-pushed branch — there is no previous state to compare, so that one requirement is skipped with a note while the structural checks still run. Repositories opt in by pointing their own `docs:check` script at it; this package runs the same check on its own bilingual docs.
59
+ `dsh-plugin-docs` (another bin of this package) checks the bilingual documentation pairs for structural alignment. It names no document of its own: which pairs to check, which directory holds them, and how each is compared come from an array of `{ name, zh, en, shape? }` that the calling repository supplies through `--config <module>`; this package declares its own pairs in `docs.config.mjs` the same way. Two shapes exist. The default, `markdown`, compares the heading-level sequence and the code-fence languages on both sides; content inside a fence is exempt because that is where language-specific examples live. `changelog` compares the releases on both sides, the `Unreleased` section included, together with their sections and item counts, with section titles normalized through a bilingual category map. `--base <revision>` additionally requires both files of each declared pair to have changed since that revision: a one-sided edit is a missing translation. When that revision is the all-zero null OID, which is what `github.event.before` carries for a newly created or force-pushed branch, there is no previous state to compare, so that one requirement is skipped with a note while the structural checks still run. Repositories call it from their own `docs:check` script.
60
60
 
61
61
  ## Development and verification
62
62
 
63
- `dist/` is the source, so one change has to pass three layers: fragment behavior, the CLI, and the documentation pair. This is the full check of this repository (identical to the Verify block in `AGENTS.md`; the release procedure is in `RELEASING.md`):
63
+ `dist/` is the source, so one change has to pass three layers: fragment behavior, the CLI, and the documentation pair. This is the full check of this repository (identical to the Verify block in `AGENTS.md`; the release procedure is in `docs/RELEASING.md`):
64
64
 
65
65
  ```sh
66
66
  npm test
package/README.md CHANGED
@@ -42,7 +42,7 @@ npm run dock:embed -- sync path/to/shared.js
42
42
 
43
43
  ## 约束
44
44
 
45
- - `dist/` 是唯一来源。改片段后跑一遍下面「开发与验证」的命令,并让消费插件重新 `sync`。
45
+ - `dist/` 是唯一来源。修改片段后执行下面「开发与验证」列出的命令,并让消费插件重新 `sync`。
46
46
  - `lib/shared.js` 中三个片段的落点固定:`dsh-loopback-helpers` → `dsh-host-guard` → `dsh-host-http`。守卫直接使用前一块声明的模块级名字,既不重新声明也不 import;`dsh-host-http` 不读上面两块、也不声明它们的名字,故可单独嵌入。落点顺序即 `FRAGMENTS` 顺序;`sync` 自下而上替换块,标记区间彼此交叠的文件会被直接拒绝、不写出任何内容。
47
47
  - `dsh-utility-launcher` 落在 client 半:每页只运行一份该装配(先加载的一方赢得 `window` 互斥并声明菜单座位),其余副本只往座位里投一行。这是运行时事实,不是样式选择。
48
48
  - 片段不得包含 `import` 或 `require`;消费插件必须能独立发布。
@@ -56,11 +56,11 @@ npm run dock:embed -- sync path/to/shared.js
56
56
 
57
57
  `dsh-plugin-parity`(本包提供的 bin)直接检这条跨仓性质:逐块(剥离块的公共缩进后)比对三个 host 块(含 `dsh-host-http`)、pin 一致性断言、块落点顺序断言与行为级对比。它是人工诊断工具,**不在 CI 中运行**:peer 处于不同分支时该性质本就不成立。成员表由调用方经 `--member <repo>:<export>` 提供——守卫工厂的导出名按片段设计是各仓自定的,故本工具不预设任何成员;`--fleet <repo>` 点名唯一启用 fleet 模式的成员,只对它检查「放宽有边界」,点名不在成员表中的仓即判 FAIL 并非零退出;不带 `--member` 时只跑静态检查,自动发现所有嵌入了宿主守卫块的仓。`--self-test` 不需要任何 checkout:它用内联样例检验块提取与「丢块」两步,改动这两个函数后先跑它。
58
58
 
59
- `dsh-plugin-docs`(同样是本包的 bin)校验双语文档的结构对齐:README 中英标题层级与代码围栏一致,CHANGELOG 中英版本段(含 `Unreleased` 段)、分类段与条目数一致(分类标题经双语映射归一);`--base <revision>` 额外要求双语对同改,单边改动即缺翻译。该 revision 是全 0 的 null OID 时(新建分支或强推时 `github.event.before` 就是这个值)没有可比的前态,这一项跳过并给出提示,其余结构校验照常。接入方式是各仓自己的 `docs:check` script 指向它——本包自身的双语文档也由此校验。
59
+ `dsh-plugin-docs`(同样是本包的 bin)校验双语文档的结构对齐。它不预设文档名:检查哪些文档对、文件位于哪个目录、按什么形状比较,都由调用仓通过 `--config <module>` 提供一个 `{ name, zh, en, shape? }` 数组决定;本包用同一种方式声明自己的 `docs.config.mjs`。形状有两种。默认的 `markdown` 比较中英两侧的标题层级序列与代码围栏语言,围栏内的内容豁免,因为语言相关的示例写在那里。`changelog` 比较中英两侧的版本段(含 `Unreleased` 段)、分类段与小节条目数,分类标题经双语映射归一。`--base <revision>` 额外要求声明的每一对文件在该 revision 之后一起改动,单边改动即缺翻译;revision 为全 0 的 null OID(新建分支或强推时 `github.event.before` 即为此值)时没有可比的前态,这一项跳过并给出提示,其余结构校验照常。各仓的 `docs:check` script 调用它。
60
60
 
61
61
  ## 开发与验证
62
62
 
63
- 片段是唯一来源,所以一次改动要同时过片段行为、CLI 与文档这三层;下面是本仓的完整校验(与 `AGENTS.md` 的 Verify 块一致,发布步骤见 `RELEASING.md`):
63
+ 片段是唯一来源,所以一次改动要同时过片段行为、CLI 与文档这三层;下面是本仓的完整校验(与 `AGENTS.md` 的 Verify 块一致,发布步骤见 `docs/RELEASING.md`):
64
64
 
65
65
  ```sh
66
66
  npm test
@@ -1,28 +1,31 @@
1
1
  #!/usr/bin/env node
2
- // Bilingual structure check for the documentation pair a repository ships:
3
- // README.md against README.en.md, CHANGELOG.md against CHANGELOG.en.md.
2
+ // Bilingual structure check for the documentation pairs a repository ships.
4
3
  //
5
- // Verifies, naming no repository and taking no file arguments (the four file
6
- // names are fixed by convention, not passed in):
7
- // * both READMEs share the same heading-level sequence and code-fence
8
- // languages — content inside fences is exempt, since that is where
9
- // language-specific examples live;
10
- // * both CHANGELOGs expose the same releases — the `Unreleased` section
4
+ // The pairs are the repository's own declaration — `--config <module>` points at
5
+ // a module exporting `{ name, zh, en, shape? }` entries, so which documents
6
+ // exist, where they live, and how each is compared is decided there, never here.
7
+ // Two shapes are provided:
8
+ // * `markdown` (the default): both files share the same heading-level sequence
9
+ // and code-fence languages — content inside fences is exempt, since that is
10
+ // where language-specific examples live;
11
+ // * `changelog`: both files expose the same releases — the `Unreleased` section
11
12
  // included, since that is where the newest edits land — with version, date,
12
13
  // per-section item counts, section titles normalized through a bilingual
13
14
  // category map (新增/Added, 修复/Fixed, ...);
14
- // * with `--base <revision>`, both files of each pair changed together
15
- // since that revision — a one-sided edit is a missing translation. An
16
- // all-zero revision is the null OID, which is what `github.event.before`
15
+ // * with `--base <revision>`, both files of every declared pair changed
16
+ // together since that revision — a one-sided edit is a missing translation.
17
+ // An all-zero revision is the null OID, which is what `github.event.before`
17
18
  // carries for a branch that was just created or force-pushed: there is no
18
- // previous revision to diff against, so that one requirement is skipped
19
- // with a note instead of dying inside `git diff`.
19
+ // previous revision to diff against, so that one requirement is skipped with
20
+ // a note instead of dying inside `git diff`.
20
21
  //
21
22
  // Repositories opt in through their own `docs:check` script; this package
22
- // runs the same check on its own bilingual docs.
23
+ // declares its pairs the same way.
23
24
 
24
25
  import { execFileSync } from 'node:child_process'
25
26
  import fs from 'node:fs'
27
+ import path from 'node:path'
28
+ import { pathToFileURL } from 'node:url'
26
29
 
27
30
  const read = (file) => fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n')
28
31
 
@@ -105,16 +108,6 @@ const category = new Map([
105
108
  ['维护', 'maintenance'], ['Maintenance', 'maintenance'],
106
109
  ])
107
110
 
108
- function assertEqual(left, right, message) {
109
- if (JSON.stringify(left) !== JSON.stringify(right)) {
110
- throw new Error(`${message}\nleft: ${JSON.stringify(left)}\nright: ${JSON.stringify(right)}`)
111
- }
112
- }
113
-
114
- const zhReadme = markdownShape('README.md')
115
- const enReadme = markdownShape('README.en.md')
116
- assertEqual(zhReadme, enReadme, 'README structure differs between languages')
117
-
118
111
  const normalizeLog = (file) => changelogShape(file).map((release) => ({
119
112
  version: release.version,
120
113
  date: release.date,
@@ -123,7 +116,47 @@ const normalizeLog = (file) => changelogShape(file).map((release) => ({
123
116
  items,
124
117
  })),
125
118
  }))
126
- assertEqual(normalizeLog('CHANGELOG.md'), normalizeLog('CHANGELOG.en.md'), 'CHANGELOG structure differs between languages')
119
+
120
+ function assertEqual(left, right, message) {
121
+ if (JSON.stringify(left) !== JSON.stringify(right)) {
122
+ throw new Error(`${message}\nleft: ${JSON.stringify(left)}\nright: ${JSON.stringify(right)}`)
123
+ }
124
+ }
125
+
126
+ // The repository declares its own pairs; this tool names none of them. Paths are
127
+ // read from the current directory, so run it from the repository root — `--base`
128
+ // compares against paths as Git reports them.
129
+ async function loadPairs(argv) {
130
+ const index = argv.indexOf('--config')
131
+ const file = argv[index + 1]
132
+ if (index === -1 || !file) {
133
+ throw new Error('--config <module> is required: the module exports the bilingual pairs this repository ships')
134
+ }
135
+ if (!fs.existsSync(file)) throw new Error(`${file} not found — declare the repository's pairs there`)
136
+
137
+ const loaded = await import(pathToFileURL(path.resolve(file)).href)
138
+ const pairs = loaded.pairs ?? loaded.default
139
+
140
+ if (!Array.isArray(pairs) || pairs.length === 0) {
141
+ throw new Error(`${file}: export a non-empty array of pairs`)
142
+ }
143
+ for (const pair of pairs) {
144
+ for (const field of ['name', 'zh', 'en']) {
145
+ if (typeof pair?.[field] !== 'string' || !pair[field]) throw new Error(`${file}: every pair needs name, zh and en`)
146
+ }
147
+ if (pair.shape !== undefined && !shapes[pair.shape]) throw new Error(`${file}: ${pair.name}: unknown shape '${pair.shape}'`)
148
+ }
149
+ return pairs
150
+ }
151
+
152
+ const shapes = { markdown: markdownShape, changelog: normalizeLog }
153
+
154
+ const pairs = await loadPairs(process.argv)
155
+
156
+ for (const pair of pairs) {
157
+ const shape = shapes[pair.shape ?? 'markdown']
158
+ assertEqual(shape(pair.zh), shape(pair.en), `${pair.name} structure differs between languages`)
159
+ }
127
160
 
128
161
  const baseIndex = process.argv.indexOf('--base')
129
162
  if (baseIndex !== -1) {
@@ -142,12 +175,9 @@ if (baseIndex !== -1) {
142
175
  .split(/\r?\n/)
143
176
  .filter(Boolean))
144
177
 
145
- for (const [primary, translation] of [
146
- ['README.md', 'README.en.md'],
147
- ['CHANGELOG.md', 'CHANGELOG.en.md'],
148
- ]) {
149
- if (changed.has(primary) !== changed.has(translation)) {
150
- throw new Error(`${primary} and ${translation} must change together`)
178
+ for (const pair of pairs) {
179
+ if (changed.has(pair.zh) !== changed.has(pair.en)) {
180
+ throw new Error(`${pair.zh} and ${pair.en} must change together`)
151
181
  }
152
182
  }
153
183
  }
@@ -3,6 +3,13 @@
3
3
  Release notes are generated from the matching version section; newest first.
4
4
  For Chinese, see [CHANGELOG.md](CHANGELOG.md).
5
5
 
6
+ ## 0.7.0 - 2026-09-29
7
+
8
+ ### Changed
9
+
10
+ - `dsh-plugin-docs` names no document. Which pairs to check, their paths and the shape each is compared with come from a `--config <module>` the calling repository supplies, exporting an array of `{ name, zh, en, shape? }`. `shape` defaults to `markdown` (heading-level sequence and code-fence languages); `changelog` compares releases, sections and per-section item counts. A document absent from that configuration is not compared, so a caller's `docs:check` passes `--config`.
11
+ - This package's `CHANGELOG.md`, `CHANGELOG.en.md` and `RELEASING.md` move into `docs/`; the repository root keeps the two READMEs, `LICENSE` and `AGENTS.md`. The npm package ships the changelogs at their new paths.
12
+
6
13
  ## 0.6.0 - 2026-09-28
7
14
 
8
15
  - New `dist/host-http.js` (marker `dsh-host-http`): the host half's reply glue becomes one block — `sendJson` (always `no-store`), the POST gate, the browser authorizer, `optionalSessionId` — so a reply policy has one definition. An already published vocabulary (`need_post`, Chinese wording, the echoed `action`) is a `policy` override, so no public identifier changes.
@@ -3,6 +3,13 @@
3
3
  Release Notes 由对应版本段生成;最新版本在前。
4
4
  英文版见 [CHANGELOG.en.md](CHANGELOG.en.md)。
5
5
 
6
+ ## 0.7.0 - 2026-09-29
7
+
8
+ ### 变更
9
+
10
+ - `dsh-plugin-docs` 不再预设文档名。检查哪些文档对、各自的路径与比较形状由调用仓的 `--config <module>` 提供,该模块导出 `{ name, zh, en, shape? }` 数组;`shape` 缺省为 `markdown`(比标题层级序列与代码围栏语言),`changelog` 比版本段、分类段与小节条目数。未在配置中声明的文档不参与比较,因此调用方的 `docs:check` 需要带上 `--config`。
11
+ - 本包的 `CHANGELOG.md`、`CHANGELOG.en.md`、`RELEASING.md` 移入 `docs/`,仓库根目录只留两份 README、`LICENSE` 与 `AGENTS.md`;npm 包内的两份 CHANGELOG 随新路径发布。
12
+
6
13
  ## 0.6.0 - 2026-09-28
7
14
 
8
15
  - 新增 `dist/host-http.js`(标记 `dsh-host-http`):宿主半的响应胶水收成一块——`sendJson`(一律带 `no-store`)、POST 门、浏览器授权器、`optionalSessionId`,回复策略自此只有一份定义。已发布的词汇(`need_post`、中文文案、回带 `action`)由 `policy` 覆盖,公开标识符不变。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mini-utility-dock",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "The DSH plugin family's shared assets: source fragments (the loopback predicates, host request guard and host HTTP glue for host halves, the utility launcher for client halves), the embedding CLI, and the family diagnostics — cross-repo drift check and bilingual docs structure check.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,12 +14,12 @@
14
14
  "README.md",
15
15
  "README.en.md",
16
16
  "LICENSE",
17
- "CHANGELOG.md",
18
- "CHANGELOG.en.md"
17
+ "docs/CHANGELOG.md",
18
+ "docs/CHANGELOG.en.md"
19
19
  ],
20
20
  "scripts": {
21
21
  "dock:embed": "node bin/dsh-mini-utility-dock.js",
22
- "docs:check": "node bin/dsh-plugin-docs.js",
22
+ "docs:check": "node bin/dsh-plugin-docs.js --config docs.config.mjs",
23
23
  "test": "node --test test/*.test.js"
24
24
  },
25
25
  "engines": {