@bushizhanghan/dsh-publish-gate 0.9.0 → 0.10.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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0
4
+
5
+ - `client-double-load` (warn): the client probe now evaluates the bundle a second time against the same
6
+ `window`, with a loader that throws on a repeated registration. A bundle inserted by two patches (its own,
7
+ plus any sidebar plugin that injects `dsh-client-modules`) aborted web boot this way. A window-level guard
8
+ makes it a no-op, which is what the recommended bundle shape now shows.
3
9
  ## 0.9.0
4
10
 
5
11
  - `checkChangelog()`: `changelog-missing-version` (warn), `changelog-no-versions` (warn), `no-changelog` (info).
package/README.md CHANGED
@@ -124,6 +124,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
124
124
  | `client-apply-throws` | `apply(ctx)` 抛 |
125
125
  | `client-no-registration` | `apply` 跑完一次服务都没调用——面板不会出现 |
126
126
  | `client-inject-missing-<service>` | `apply` 用了 `ctx.sidebarRightTabs` 但 `inject` 数组里没写它。cordis 不会把没声明的服务给你,于是 `ctx.sidebarRightTabs` 是 `undefined`,`apply` 一碰就抛(或静默什么都没注册) |
127
+ | `client-double-load` | 同一个 bundle 被注入了两次(它自己的 patch 一次,注入 `dsh-client-modules` 的侧边栏插件再一次)。loader 按 id 建工厂,第二次注册直接抛错,整个 Web 启动被中断——不是这个面板没了,是所有面板都没了。用 `if (window.__xLoaded) return; window.__xLoaded = true` 挡一下 |
127
128
  | `client-parse-failed` | 文件有语法错误。**单独判**:`SyntaxError` 说明 bundle 本身是坏的,别的报错说明是沙箱不够真,两者别混为一谈 |
128
129
  | `client-unavailable` | 子进程没跑起来(warn)——这一层没跑,别把它当干净 |
129
130
 
@@ -216,7 +217,7 @@ dsh plugin --profile desktop add @bushizhanghan/dsh-publish-gate
216
217
  ## 开发
217
218
 
218
219
  ```sh
219
- node --test # 97 个引擎单测
220
+ node --test # 99 个引擎单测
220
221
  node test/integration.test.mjs # 27 条集成断言(9 个真实插件作 fixture)
221
222
  ```
222
223
 
package/lib/engine.js CHANGED
@@ -1786,6 +1786,24 @@ if (!records.parseError) {
1786
1786
  else records.evalError = text
1787
1787
  }
1788
1788
  }
1789
+ // The same bundle can reach the page twice: its own patch inserts it, and any
1790
+ // sidebar-style plugin that injects dsh-client-modules inserts it again. The
1791
+ // loader keys factories by id and throws on the second registration, which
1792
+ // aborts web boot. Evaluate a second time against the same window and see
1793
+ // whether load() is called again; a window-level guard makes it a no-op.
1794
+ records.doubleLoad = null
1795
+ if (!records.parseError && records.loaded > 0) {
1796
+ let second = 0
1797
+ const first = globalThis.window.__ModuleLoader__.load
1798
+ globalThis.window.__ModuleLoader__.load = (spec) => {
1799
+ second += 1
1800
+ throw new Error('duplicate factory registration: ' + String(spec && spec.id))
1801
+ }
1802
+ try { new Function(source)() } catch (e) {
1803
+ records.doubleLoad = second > 0 ? String(e && e.message || e) : null
1804
+ }
1805
+ globalThis.window.__ModuleLoader__.load = first
1806
+ }
1789
1807
  process.stdout.write('\\n@@PG@@' + JSON.stringify(records))
1790
1808
  process.exit(0)
1791
1809
  `
@@ -1847,6 +1865,17 @@ export async function checkClientRuntime(dir, opts = {}) {
1847
1865
  if (r.applyError) {
1848
1866
  out.push(F('client-apply-throws', 'error', `apply() in ${target} throws: ${String(r.applyError).slice(0, 160)}`, 'Registration happens inside apply(); a throw there leaves the panel unregistered while the host half keeps working.', 'Wrap the failing registration, or fix the cause.'))
1849
1867
  }
1868
+ if (r.doubleLoad) {
1869
+ out.push(
1870
+ F(
1871
+ 'client-double-load',
1872
+ 'warn',
1873
+ `${target} registers a factory again when evaluated a second time (${String(r.doubleLoad).slice(0, 120)})`,
1874
+ 'A client bundle can reach the page twice: its own patch inserts it, and any sidebar-style plugin that injects dsh-client-modules inserts it again. The loader keys factories by id and throws on the second registration, which aborts web boot — the whole panel disappears, not just this one.',
1875
+ 'Guard the load call with a window-level flag: if (window.__myPluginLoaded) return; window.__myPluginLoaded = true.',
1876
+ ),
1877
+ )
1878
+ }
1850
1879
  if (r.hasApply && !r.applyError && r.registrations.length === 0) {
1851
1880
  out.push(
1852
1881
  F(
@@ -2234,7 +2263,7 @@ const RULE_ORDER = [
2234
2263
  'missing-field', 'not-esm', 'files-omits-lib', 'files-omits-patch', 'no-exports', 'client-without-export', 'client-without-platform',
2235
2264
  'entry-path-missing', 'bundle-patch-not-declared', 'no-publish-config-access', 'private-field-set', 'publish-config-registry',
2236
2265
  'client-file-missing', 'client-no-module-loader', 'client-parse-failed', 'client-load-not-called',
2237
- 'client-factory-throws', 'client-no-apply', 'client-apply-throws', 'client-no-registration',
2266
+ 'client-factory-throws', 'client-no-apply', 'client-apply-throws', 'client-no-registration', 'client-double-load',
2238
2267
  'client-inject-missing-slots', 'client-inject-missing-sidebarRightTabs', 'client-unavailable',
2239
2268
  'no-patch-file', 'no-insert-row', 'insert-missing-name', 'insert-name-mismatch',
2240
2269
  'patch-name-unresolvable', 'no-name-export', 'no-apply-export', 'no-inject', 'require-in-esm',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bushizhanghan/dsh-publish-gate",
3
- "version": "0.9.0",
3
+ "version": "0.10.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",