@bushizhanghan/dsh-publish-gate 0.3.3 → 0.3.4

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
@@ -30,7 +30,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
30
30
  | 工具 | 作用 |
31
31
  |---|---|
32
32
  | `publish_gate` | 全量门禁:清单 + 产物 + 运行时 + 浏览器半边 + 网络,返回 `safeToPublish`(`skipPack` / `skipTools` / `skipClient` / `skipNetwork` 可逐层跳过) |
33
- | `publish_gate_tools` | 只跑运行时层:在子进程里 `apply(ctx)`,看注册了哪些工具、有没有缺 `execute`、重名、`presentCall` 抛错、`card` 用了宿主不认识的值 | 传 `call: ["工具名"]` 可以**真的调一次**(空参数):注册永远不会执行 `execute`,所以「一调就抛 ReferenceError」这类 bug 只有这一条能看见。默认不调——有些工具会开程序或写文件,你点名才算同意。 |
33
+ | `publish_gate_tools` | 只跑运行时层:在子进程里 `apply(ctx)`,看注册了哪些工具、有没有缺 `execute`、重名、`presentCall` 抛错、`card` 用了宿主不认识的值 | 传 `call: ["工具名"]` 可以**真的调一次**(空参数):注册永远不会执行 `execute`,所以「一调就抛 ReferenceError」这类 bug 只有这一条能看见。默认不调——有些工具会开程序或写文件,你点名才算同意。超时单独算 `tool-call-slow`(warn),不跟「真的抛了」混在一起:`of_env` 这种要起 WSL 的工具本来就慢。 |
34
34
  | `publish_gate_client` | 只跑浏览器半边:在子进程里假造 `window.__ModuleLoader__`,看 bundle 会不会加载、`apply` 会不会抛、到底往哪个服务注册了东西、`inject` 有没有漏 |
35
35
  | `publish_gate_verify` | 发后校验:`npm pack --dry-run` 的 shasum 和 registry 上的 `dist.shasum` 逐字节比,比版本号猜准 |
36
36
  | `publish_gate_patch` | 只查 `cordis.patch.yml`——「插件装了但工具没出现」的第一个怀疑对象 |
@@ -183,7 +183,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
183
183
  ## 开发
184
184
 
185
185
  ```sh
186
- node --test # 66 个引擎单测
186
+ node --test # 67 个引擎单测
187
187
  node test/integration.test.mjs # 26 条集成断言(9 个真实插件作 fixture)
188
188
  ```
189
189
 
package/lib/engine.js CHANGED
@@ -1079,17 +1079,21 @@ try {
1079
1079
  // bug the author cannot see any other way - registration never runs execute.
1080
1080
  stage = 'call'
1081
1081
  const wanted = new Set(JSON.parse(process.env.PG_CALLS || '[]'))
1082
+ const budget = Number(process.env.PG_CALL_MS || 8000)
1082
1083
  const calls = []
1083
1084
  for (const entry of registered) {
1084
1085
  if (!wanted.has(entry.name) || !entry.run) continue
1085
1086
  try {
1086
1087
  await Promise.race([
1087
1088
  Promise.resolve(entry.run({}, { signal: undefined })),
1088
- new Promise((_, reject) => setTimeout(() => reject(new Error('timed out after 8s')), 8000)),
1089
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timed out')), budget)),
1089
1090
  ])
1090
1091
  calls.push({ name: entry.name, ok: true })
1091
1092
  } catch (error) {
1092
- calls.push({ name: entry.name, ok: false, error: String((error && error.message) || error) })
1093
+ const message = String((error && error.message) || error)
1094
+ // A slow tool is not a broken tool: of_env shells out to WSL and takes
1095
+ // seconds by design. Only a real throw is a defect.
1096
+ calls.push({ name: entry.name, ok: false, timedOut: message === 'timed out', error: message })
1093
1097
  }
1094
1098
  }
1095
1099
  emit({ ok: true, tools: registered.map(({ run, ...rest }) => rest), badParamTypes, calls })
@@ -1107,7 +1111,11 @@ export async function checkToolRegistrations(dir, opts = {}) {
1107
1111
  timeout: opts.timeoutMs ?? 20_000,
1108
1112
  maxBuffer: 4 * 1024 * 1024,
1109
1113
  windowsHide: true,
1110
- env: { ...process.env, PG_CALLS: JSON.stringify(Array.isArray(opts.calls) ? opts.calls : []) },
1114
+ env: {
1115
+ ...process.env,
1116
+ PG_CALLS: JSON.stringify(Array.isArray(opts.calls) ? opts.calls : []),
1117
+ PG_CALL_MS: String(opts.callTimeoutMs ?? 8000),
1118
+ },
1111
1119
  })
1112
1120
  const line = String(stdout).split('\n').filter((l) => l.startsWith('@@PG@@')).pop()
1113
1121
  if (!line) throw new Error('the child printed no result')
@@ -1154,6 +1162,18 @@ export async function checkToolRegistrations(dir, opts = {}) {
1154
1162
  const seen = new Set()
1155
1163
  for (const c of Array.isArray(payload.calls) ? payload.calls : []) {
1156
1164
  if (c.ok) continue
1165
+ if (c.timedOut) {
1166
+ out.push(
1167
+ F(
1168
+ 'tool-call-slow',
1169
+ 'warn',
1170
+ `tool "${c.name}" did not answer within the call budget`,
1171
+ 'A slow tool is not a broken one — this may shell out or wait on a service. Reported so a hang is not mistaken for a pass, and NOT counted as a defect.',
1172
+ 'Raise callTimeoutMs if the tool is legitimately slow, or check that it returns at all.',
1173
+ ),
1174
+ )
1175
+ continue
1176
+ }
1157
1177
  out.push(
1158
1178
  F(
1159
1179
  'tool-call-throws',
@@ -1675,7 +1695,7 @@ const RULE_ORDER = [
1675
1695
  'inject-missing-tools', 'inject-missing-web', 'inject-missing-webServer', 'inject-missing-timer',
1676
1696
  'inject-unused-service',
1677
1697
  'tool-missing-name', 'tool-duplicate-name', 'tool-missing-execute', 'tool-present-call-throws',
1678
- 'tool-bad-card-kind', 'tool-missing-output-schema', 'tool-none-registered', 'tool-bad-parameter-type',
1698
+ 'tool-bad-card-kind', 'tool-missing-output-schema', 'tool-none-registered', 'tool-bad-parameter-type', 'tool-call-throws', 'tool-call-slow',
1679
1699
  'lib-debugger', 'lib-console-log', 'lib-hardcoded-path', 'lib-url-pathname', 'node-too-old', 'stale-build', 'hand-edited-build',
1680
1700
  'pack-omits-lib', 'pack-omits-patch', 'pack-omits-client', 'pack-oversize', 'pack-omits-readme', 'pack-omits-license', 'pack-ships-secret',
1681
1701
  'pack-unexpected-dir', 'pack-script-build', 'pack-unavailable',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bushizhanghan/dsh-publish-gate",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Helps DSH plugin AUTHORS publish their plugins: a gate that turns every silent failure in the publish path into a deterministic check. Manifest layer — package.json completeness, BOM, YAML/JSON parseability, entry paths that point at no file, a patch the manifest never declares, patch insert correctness, a build whose lib/ is older than src/, inject services that are missing or misspelled, npm scope permissions, GitHub repo and token state, market entry format, LICENSE/README, tests. Artifact layer — runs `npm pack --dry-run` and reads what npm will really ship: code missing from the tarball, an unpacked patch, oversize packages, dev directories. Runtime layer — imports the entry in an isolated child process, calls apply(ctx), and reports tools that never register, missing execute, duplicate names, throwing presentCall and card kinds the host does not render. Browser half — loads the client bundle against a fake window.__ModuleLoader__ and reports a bundle that never loads, returns no apply, throws, registers nothing, or uses a service its own inject array omits, because a panel that never appears looks like a UI bug, not a packaging one. Then GitHub + npm + market, and a post-publish shasum check that proves the registry holds these exact bytes. 帮助插件作者一键发布:写完插件后跑一遍门禁(清单 / 产物 / 运行时 / 浏览器半边),把发布链路上的每个静默坑变成确定性检查,然后 GitHub + npm + 市场一键三连。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",