@hyzyn/dsh-safe 0.13.0 → 0.13.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.
Files changed (2) hide show
  1. package/lib/repair.js +52 -0
  2. package/package.json +1 -1
package/lib/repair.js CHANGED
@@ -164,6 +164,36 @@ export async function cmdRepair(args, { spawn = spawnSync, log = err, write = ou
164
164
  const e = restored[0]
165
165
  write(`[dsh-safe] ${t('restored')} ${e.name ?? e.id} (id: ${e.id})`)
166
166
  }
167
+
168
+ // 防复发:修复的插件若同时被多个 bundle 挂载(如聚合包已内置同一插件),
169
+ // 恢复后下次启动会 duplicate——自动去重(交互选择保留来源,-y 保留先声明者)
170
+ const knownAfter = collectKnownRows(profile)
171
+ const sources = bundleMountSources(knownAfter, opts.id)
172
+ if (sources.length >= 2) {
173
+ const manifest = readJsonIfExists(profileManifestPath(profile))
174
+ const declared = manifest?.dsh?.profile?.bundles
175
+ const ordered = Array.isArray(declared)
176
+ ? [...sources].sort(
177
+ (a, b) =>
178
+ (declared.indexOf(a) < 0 ? 999 : declared.indexOf(a)) - (declared.indexOf(b) < 0 ? 999 : declared.indexOf(b)),
179
+ )
180
+ : sources
181
+ let keep = ordered[0]
182
+ if (!opts.yes) {
183
+ const rl = createInterface({ input: process.stdin, output: process.stderr })
184
+ log(t('repairDedupePick', { id: opts.id }))
185
+ ordered.forEach((b, i) => log(`${i + 1}. ${b}${i === 0 ? t('repairDedupeDefault') : ''}`))
186
+ const answer = await rl.question(t('repairDedupeChoose'))
187
+ rl.close()
188
+ const pick = Math.min(Math.max(parseInt(answer, 10) || 1, 1), ordered.length)
189
+ keep = ordered[pick - 1]
190
+ }
191
+ const removed = ordered.filter((b) => b !== keep)
192
+ if (removeFromManifestBundles(profile, removed)) {
193
+ log(t('repairDedupeDone', { removed: removed.join(', '), keep }))
194
+ }
195
+ }
196
+
167
197
  log(t('repairDone'))
168
198
  return 0
169
199
  }
@@ -193,6 +223,28 @@ const readJsonIfExists = (path) => {
193
223
  }
194
224
  }
195
225
 
226
+ /** 该 id 的 bundle 挂载来源(profile/home 层是 override,不算挂载来源)。 */
227
+ function bundleMountSources(known, id) {
228
+ return [
229
+ ...new Set(
230
+ known.rows.filter((r) => r.id === id && r.source !== 'profile' && r.source !== 'home').map((r) => r.source),
231
+ ),
232
+ ]
233
+ }
234
+
235
+ /** 从 profile 清单的 bundles 数组移除指定来源;没有变化返回 false。 */
236
+ function removeFromManifestBundles(profile, removeNames) {
237
+ const manifestPath = profileManifestPath(profile)
238
+ const manifest = readJsonIfExists(manifestPath)
239
+ const current = manifest?.dsh?.profile?.bundles
240
+ if (!Array.isArray(current)) return false
241
+ const next = current.filter((b) => !removeNames.includes(b))
242
+ if (next.length === current.length) return false
243
+ manifest.dsh.profile.bundles = next
244
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
245
+ return true
246
+ }
247
+
196
248
  /**
197
249
  * duplicate 类的自动去重:同一 id 被多个 bundle 挂载时,从 profile 清单的
198
250
  * bundles 数组移除一个来源(保留的来源继续提供插件),并摘除无效的禁用行。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyzyn/dsh-safe",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "dsh 启动保险丝:社区插件不兼容导致 dsh 启动失败时,自动禁用坏插件并重试",
5
5
  "type": "module",
6
6
  "license": "MIT",