@bushizhanghan/dsh-publish-gate 0.11.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0
4
+
5
+ - `market-fork-of-listed` / `market-fork-undisclosed` (warn): the repo is a GitHub fork whose parent is
6
+ already in the catalogue, and nothing says so. A submission was blocked for exactly this: the entry
7
+ described the fork as if it were original work, and both plugins claim the same provider id, so installing
8
+ both silently shadows one of them. The check uses the `fork` and `parent.full_name` fields of the repo
9
+ metadata the market layer already fetches.
10
+ ## 0.12.0
11
+
12
+ - `pack-ships-junk` (warn): the tarball carries `.bak-*`, `.orig`, `.swp`, `.log`, `~` or `.DS_Store` debris.
13
+ Naming a directory in `files` takes everything inside it, so a `lib/client.js.bak-20261026` left behind
14
+ during a fix ships to every consumer and stays on npm permanently. Both weapp-loop and page-monitor had
15
+ exactly that file during this session; both were deleted by hand before this rule existed.
3
16
  ## 0.11.0
4
17
 
5
18
  - `tool-no-description` (error), `tool-description-thin` (warn), `tool-no-parameters` (error): the host
package/README.md CHANGED
@@ -87,6 +87,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
87
87
  | `pack-oversize` | 解包 >5 MB 是 error、>1 MB 是 warn,并点名最大的三个文件。测试 fixture 是常见元凶——我的一个插件曾把 20 MB 的算例目录发上去,装了它的人每次都要下 20 MB |
88
88
  | `pack-ships-secret` | tarball 里有 `.env` / `.npmrc` / `*.pem` / `*.key` / `id_rsa`。没写 `files` 字段时 npm 会把 `.gitignore` 没排除的一切都打包,而包一发布就是公开的、npm 不会帮你撤回 |
89
89
  | `pack-unexpected-dir` | `fixtures` / `test` / `tests` / `src` 被打进包里 |
90
+ | `pack-ships-junk` | 包里带上了 `.bak-20261026` 这类备份或编辑器残留。`files: ["lib"]` 会把 `lib/` 里所有东西都打进去——包括你自己源码的上一版,而且发上去就永久公开 |
90
91
  | `pack-script-build` | 有 `prepack` / `prepare`:dry run 用了 `--ignore-scripts`,这次清单**不代表**真正发布时的产物 |
91
92
  | `pack-unavailable` | npm 跑不起来(warn)——产物层没跑,别把它当干净 |
92
93
 
@@ -140,6 +141,8 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
140
141
  | `market-duplicate-entry` | 这个仓库**已经在目录里**。再提一个 PR 就是同一仓库的第二条条目,reviewer 会拒。版本升级根本不需要 PR——目录条目只存 `url` |
141
142
  | `market-npm-name-collision` | 另一个条目也在发同一个 npm 包,安装路径会互相盖 |
142
143
  | `market-repo-too-new` | 仓库创建不满 24 小时。门禁会报 `0.0 days old` 然后**自己重跑**,PR 不会丢,但在那之前什么都不会合 |
144
+ | `market-fork-of-listed` | 这个仓库是某个**已经在目录里**的仓库的 fork。两条条目装的代码抢同一批 id,两个都装就有一个被静默盖掉——而且 reviewer 会把"没写"当成"隐瞒 fork" |
145
+ | `market-fork-undisclosed` | 是 fork,但 README 和 package.json 里都没有上游的 `owner/repo` |
143
146
  | `market-no-repo` | `package.json` 里没有 GitHub 地址,没东西可提交 |
144
147
  | `market-unavailable` | 目录拉不到(warn)——这几条没跑,别把它当干净 |
145
148
 
@@ -220,7 +223,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
220
223
  ## 开发
221
224
 
222
225
  ```sh
223
- node --test # 102 个引擎单测
226
+ node --test # 107 个引擎单测
224
227
  node test/integration.test.mjs # 27 条集成断言(9 个真实插件作 fixture)
225
228
  ```
226
229
 
package/lib/engine.js CHANGED
@@ -931,7 +931,7 @@ export async function checkMarketReadiness(dir, opts = {}) {
931
931
  if (hours < 24) {
932
932
  out.push(
933
933
  F(
934
- 'market-repo-too-new',
934
+ 'market-repo-too-new', 'market-fork-of-listed', 'market-fork-undisclosed',
935
935
  'warn',
936
936
  `the repo is ${hours.toFixed(1)}h old; the submission gate needs 24h`,
937
937
  'The gate fails with a "repo too new" message and reruns on its own, so the PR is not lost — but nothing merges until it passes.',
@@ -941,12 +941,45 @@ export async function checkMarketReadiness(dir, opts = {}) {
941
941
  }
942
942
  }
943
943
  }
944
+ // A fork of a repo that is already listed collides with its own upstream:
945
+ // both register the same provider or tool names, and whichever loads second
946
+ // wins or throws. A reviewer blocked a submission for exactly this and the
947
+ // description said nothing about it.
948
+ const parentSlug = typeof repoInfo?.parent?.full_name === 'string' ? repoInfo.parent.full_name : null
949
+ if (repoInfo?.fork === true && parentSlug) {
950
+ const parentListed = entries.some((e) => typeof e?.url === 'string' && e.url.toLowerCase().includes(parentSlug.toLowerCase()))
951
+ if (parentListed) {
952
+ out.push(
953
+ F(
954
+ 'market-fork-of-listed',
955
+ 'warn',
956
+ `this repo is a fork of ${parentSlug}, which is already in the catalogue`,
957
+ 'Both entries install code that claims the same ids, so a user who installs both gets one silently shadowing the other — and a reviewer reads the missing note as an undisclosed fork.',
958
+ `Say in the entry description that it is a fork of ${parentSlug} and that the two must not be installed together.`,
959
+ ),
960
+ )
961
+ }
962
+ const readme = safeText(join(dir, 'README.md')) || ''
963
+ const desc = typeof json?.description === 'string' ? json.description : ''
964
+ if (!readme.includes(parentSlug) && !desc.includes(parentSlug)) {
965
+ out.push(
966
+ F(
967
+ 'market-fork-undisclosed',
968
+ 'warn',
969
+ `nothing in README or package.json names the upstream ${parentSlug}`,
970
+ 'A fork that does not say what it forked from looks like original work until someone diffs it, and the upstream author finds out from a collision report.',
971
+ `Mention ${parentSlug} in the README and in the market entry description.`,
972
+ ),
973
+ )
974
+ }
975
+ }
944
976
  return out
945
977
  }
946
978
 
947
979
  // =====================================================================
948
980
  // Source sanity: the entry protocol
949
981
  // =====================================================================
982
+
950
983
  export function checkEntrySource(dir) {
951
984
  const out = []
952
985
  const path = join(dir, 'lib', 'index.js')
@@ -1145,6 +1178,22 @@ export async function checkPack(dir, opts = {}) {
1145
1178
  if (has(d)) out.push(F('pack-unexpected-dir', 'warn', `the tarball includes ${d}/`, 'A development directory is being published: it bloats the install and ships code nobody runs.', `Add "!${d}" to files if it is not meant to ship.`))
1146
1179
  }
1147
1180
 
1181
+ // Editor and backup debris rides along whenever "files" names a directory:
1182
+ // `files: ["lib"]` ships lib/client.js.bak-20261026 without asking. npm keeps
1183
+ // it forever, and it is the previous version of your code, in public.
1184
+ const junk = files.map((f) => f.path).filter((p) => /\.(?:bak|orig|rej|swp|tmp|log)$|\.bak[-.]|~$|(?:^|\/)\.DS_Store$|(?:^|\/)node_modules\//i.test(p))
1185
+ if (junk.length > 0) {
1186
+ out.push(
1187
+ F(
1188
+ 'pack-ships-junk',
1189
+ 'warn',
1190
+ `the tarball contains ${junk.length} backup or editor file(s)`,
1191
+ `${junk.slice(0, 3).join(', ')} — naming a directory in "files" takes everything in it, so a .bak copy of your own source ships to every consumer and stays on npm forever.`,
1192
+ 'Delete the backups, or add "!lib/**/*.bak*" to files.',
1193
+ ),
1194
+ )
1195
+ }
1196
+
1148
1197
  const scripts = json?.scripts || {}
1149
1198
  const buildHook = scripts.prepack ? 'prepack' : scripts.prepare ? 'prepare' : null
1150
1199
  if (buildHook) {
@@ -2320,7 +2369,7 @@ const RULE_ORDER = [
2320
2369
  'lib-debugger', 'lib-console-log', 'lib-hardcoded-path', 'lib-url-pathname', 'node-too-old', 'stale-build', 'hand-edited-build',
2321
2370
  'undeclared-import', 'dep-in-dev-only', 'unused-dependency',
2322
2371
  'pack-omits-lib', 'pack-omits-patch', 'pack-omits-client', 'pack-oversize', 'pack-omits-readme', 'pack-omits-license', 'pack-ships-secret',
2323
- 'pack-unexpected-dir', 'pack-script-build', 'pack-unavailable',
2372
+ 'pack-unexpected-dir', 'pack-ships-junk', 'pack-script-build', 'pack-unavailable',
2324
2373
  'no-license', 'license-mismatch', 'version-taken', 'version-behind-latest', 'npm-not-logged-in', 'npm-scope-mismatch', 'npm-whoami-unavailable', 'registry-unreachable', 'publish-not-served',
2325
2374
  'publish-served-mismatch', 'publish-served', 'market-empty', 'market-no-url', 'market-url-not-github',
2326
2375
  'market-no-name', 'market-name-shape', 'market-no-category', 'market-bad-category', 'market-no-desc', 'market-no-en', 'market-no-zh', 'unscoped-name', 'no-git', 'no-readme', 'readme-no-install', 'readme-no-profile', 'readme-wrong-package',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bushizhanghan/dsh-publish-gate",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Pre-publish gate for DSH plugins: manifest, dependencies, artifact, runtime, browser half, traceability and market readiness — plus an input fuzzer that drives every declared parameter with the wrong type. 发布前的七层门禁 + 入参模糊测试。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",