dsh-mini-utility-dock 0.6.0 → 0.7.1
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
|
|
59
|
+
`dsh-plugin-docs` (another bin of this package) checks the bilingual documentation pairs for structural consistency. 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/`
|
|
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
|
|
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
|
package/bin/dsh-plugin-docs.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Bilingual structure check for the documentation
|
|
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
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
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
|
|
15
|
-
// since that revision — a one-sided edit is a missing translation.
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
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
|
}
|
package/bin/dsh-plugin-parity.js
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
// a Host that parses to no hostname skipping the allowlist in one copy while
|
|
9
9
|
// another denies it.
|
|
10
10
|
//
|
|
11
|
-
// The copies are
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
11
|
+
// The consumer copies are embedded blocks generated from this package's `dist/`
|
|
12
|
+
// files by the dock CLI — `dist/loopback.js` (the predicates), `dist/guard.js`
|
|
13
|
+
// (the enforcement policy) and `dist/host-http.js` (the response glue: JSON
|
|
14
|
+
// replies, the POST gate, the browser authorizer). So this checker asserts three
|
|
15
|
+
// things a generator alone cannot:
|
|
16
16
|
//
|
|
17
17
|
// * every embedded block is byte-identical in every member (a hand edit to
|
|
18
18
|
// one copy, or a repo that never re-ran `loopback:sync` / `guard:sync` /
|
|
@@ -293,8 +293,8 @@ for (const block of BLOCKS) {
|
|
|
293
293
|
// Every pattern here matches a CODE SHAPE, not a word. These files are scanned
|
|
294
294
|
// as text with the generated blocks dropped, so a bare keyword also matches
|
|
295
295
|
// prose: a doc comment explaining why a private helper is NOT a second guard
|
|
296
|
-
//
|
|
297
|
-
//
|
|
296
|
+
// would read as a decision. An explanation is not enforcement; reading a
|
|
297
|
+
// header or declaring a function is.
|
|
298
298
|
const ENFORCEMENT = [
|
|
299
299
|
[/LOOPBACK_HOSTNAMES\s*=(?!=)/, 'redefines LOOPBACK_HOSTNAMES'],
|
|
300
300
|
[/(?:const|function|let)\s+isLoopbackName\b/, 'defines a private isLoopbackName'],
|
|
@@ -3,6 +3,19 @@
|
|
|
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.1 - 2026-09-29
|
|
7
|
+
|
|
8
|
+
### Maintenance
|
|
9
|
+
|
|
10
|
+
- No interface changed: only the explanatory text in `bin/` and `test/` and two README phrasings were rewritten; `dist/`, the CLI arguments and the `--config` contract are untouched.
|
|
11
|
+
|
|
12
|
+
## 0.7.0 - 2026-09-29
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `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`.
|
|
17
|
+
- 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.
|
|
18
|
+
|
|
6
19
|
## 0.6.0 - 2026-09-28
|
|
7
20
|
|
|
8
21
|
- 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,19 @@
|
|
|
3
3
|
Release Notes 由对应版本段生成;最新版本在前。
|
|
4
4
|
英文版见 [CHANGELOG.en.md](CHANGELOG.en.md)。
|
|
5
5
|
|
|
6
|
+
## 0.7.1 - 2026-09-29
|
|
7
|
+
|
|
8
|
+
### 维护
|
|
9
|
+
|
|
10
|
+
- 无接口变更:`dsh-plugin-docs` 与 `dsh-plugin-parity` 的参数、输出和 `dist/` 片段字节均未改动,本次只改写 bin 与测试中的说明文字,并把 README 里「结构对齐」的说法改为「结构一致性」。
|
|
11
|
+
|
|
12
|
+
## 0.7.0 - 2026-09-29
|
|
13
|
+
|
|
14
|
+
### 变更
|
|
15
|
+
|
|
16
|
+
- `dsh-plugin-docs` 不再预设文档名。检查哪些文档对、各自的路径与比较形状由调用仓的 `--config <module>` 提供,该模块导出 `{ name, zh, en, shape? }` 数组;`shape` 缺省为 `markdown`(比标题层级序列与代码围栏语言),`changelog` 比版本段、分类段与小节条目数。未在配置中声明的文档不参与比较,因此调用方的 `docs:check` 需要带上 `--config`。
|
|
17
|
+
- 本包的 `CHANGELOG.md`、`CHANGELOG.en.md`、`RELEASING.md` 移入 `docs/`,仓库根目录只留两份 README、`LICENSE` 与 `AGENTS.md`;npm 包内的两份 CHANGELOG 随新路径发布。
|
|
18
|
+
|
|
6
19
|
## 0.6.0 - 2026-09-28
|
|
7
20
|
|
|
8
21
|
- 新增 `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.
|
|
3
|
+
"version": "0.7.1",
|
|
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": {
|