dshmarket 1.66.25 → 1.66.26
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/lib/check.js +29 -2
- package/lib/verify.js +35 -11
- package/package.json +1 -1
- package/src/check.ts +27 -2
- package/src/verify.ts +39 -11
package/lib/check.js
CHANGED
|
@@ -313,6 +313,33 @@ function parseSemver(value) {
|
|
|
313
313
|
pre: m[4] === undefined ? [] : m[4].split('.'),
|
|
314
314
|
};
|
|
315
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Parse a comparator's target the way node-semver does: a PARTIAL version in
|
|
318
|
+
* a range is zero-filled, so `<0.3` is `<0.3.0` and `>=2` is `>=2.0.0`.
|
|
319
|
+
*
|
|
320
|
+
* Without this, every prerelease floor written as a partial — the
|
|
321
|
+
* `@deepseek-ai/dsh-*` peers of a whole family of connector plugins say
|
|
322
|
+
* `>=0.1.5 <0.3`, `>=0.1.7-rc <2`, `>=0.2.0-rc.0 <0.3` — parsed as unknown,
|
|
323
|
+
* `satisfiesRange` answered null, and the gate stood aside: measured against
|
|
324
|
+
* the host's own evaluator over 699 live catalog manifests, those were the
|
|
325
|
+
* last 7 of 2796 (package, runtime) pairs where the host refuses a plugin
|
|
326
|
+
* and the market still promised a restart would apply it.
|
|
327
|
+
*
|
|
328
|
+
* A BARE partial (`1.2`) is a different thing in semver — an x-range that
|
|
329
|
+
* means `>=1.2.0 <1.3.0` — so it deliberately stays unknown here rather than
|
|
330
|
+
* being mistaken for the exact version `1.2.0`.
|
|
331
|
+
*/
|
|
332
|
+
function parseComparatorTarget(target, op) {
|
|
333
|
+
const direct = parseSemver(target);
|
|
334
|
+
if (direct !== null)
|
|
335
|
+
return direct;
|
|
336
|
+
if (op === '')
|
|
337
|
+
return null;
|
|
338
|
+
const parts = target.trim().split('.');
|
|
339
|
+
if (parts.length > 3 || parts.some(part => !/^\d+$/u.test(part)))
|
|
340
|
+
return null;
|
|
341
|
+
return parseSemver([...parts, ...Array(3 - parts.length).fill('0')].join('.'));
|
|
342
|
+
}
|
|
316
343
|
function comparePre(a, b) {
|
|
317
344
|
const len = Math.max(a.length, b.length);
|
|
318
345
|
for (let i = 0; i < len; i += 1) {
|
|
@@ -441,7 +468,7 @@ export function satisfiesRange(version, range, options = {}) {
|
|
|
441
468
|
const m = /^(\^|~|>=|<=|>|<)?(.*)$/.exec(p);
|
|
442
469
|
const op = m?.[1] ?? '';
|
|
443
470
|
const target = (m?.[2] ?? '').trim();
|
|
444
|
-
const tv =
|
|
471
|
+
const tv = parseComparatorTarget(target, op);
|
|
445
472
|
if (tv === null)
|
|
446
473
|
return null;
|
|
447
474
|
const major = tv.major;
|
|
@@ -488,7 +515,7 @@ export function satisfiesRange(version, range, options = {}) {
|
|
|
488
515
|
// prerelease admission gate. Otherwise an unknown protocol such as
|
|
489
516
|
// `workspace:^` or `catalog:default` is misreported as a definite false
|
|
490
517
|
// whenever the resolved version happens to carry a prerelease tag.
|
|
491
|
-
if (
|
|
518
|
+
if (parseComparatorTarget(target, m[1] ?? '') === null)
|
|
492
519
|
return null;
|
|
493
520
|
return { op: m[1] ?? '', target };
|
|
494
521
|
};
|
package/lib/verify.js
CHANGED
|
@@ -186,6 +186,19 @@ const isDshPeer = (peer) => peer === '@deepseek-ai/dsh' || peer.startsWith('@dee
|
|
|
186
186
|
export function hostPeerGate(activeProfileDir, name, facts) {
|
|
187
187
|
return peerGateAt(join(activeProfileDir, 'node_modules', name), name, facts);
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The unmet requirement, if any, and the remedy side it points at.
|
|
191
|
+
*
|
|
192
|
+
* Which side of the range the runtime sits on decides the remedy: below the
|
|
193
|
+
* floor, upgrading the host genuinely helps; above the cap (#757's own
|
|
194
|
+
* shape) no upgrade can satisfy it, and suggesting one points the wrong way
|
|
195
|
+
* (#758).
|
|
196
|
+
*/
|
|
197
|
+
function unmetOf(peer, range, runtimeVersion) {
|
|
198
|
+
const floor = /[0-9]+\.[0-9]+\.[0-9]+[-+0-9A-Za-z.]*/.exec(range);
|
|
199
|
+
const hostTooOld = floor !== null && compareSemver(runtimeVersion, floor[0]) < 0;
|
|
200
|
+
return { peer, range, runtimeVersion, hostTooOld };
|
|
201
|
+
}
|
|
189
202
|
/** Same prediction for a patch plugin resolved outside the profile's flat node_modules. */
|
|
190
203
|
function peerGateAt(packageDir, name, facts) {
|
|
191
204
|
if (facts.runtimeVersion === null)
|
|
@@ -201,22 +214,33 @@ function peerGateAt(packageDir, name, facts) {
|
|
|
201
214
|
if (version !== '' && (facts.exemptions[`${name}@${version}`] ?? []).includes(facts.runtimeVersion)) {
|
|
202
215
|
return null;
|
|
203
216
|
}
|
|
217
|
+
// An own `engines.dsh` range decides ALONE and peerDependencies are then
|
|
218
|
+
// ignored — the host's rule, read from unmetRequirements in
|
|
219
|
+
// @deepseek-ai/dsh-app-boot, and the reason a gate that only read peers
|
|
220
|
+
// was too lenient on 62 of 2796 (package, runtime) pairs measured over the
|
|
221
|
+
// live catalog: `dsh-file-explorer-kit` (engines.dsh ^0.1.1) and
|
|
222
|
+
// `dsh-workspace-kit` (^0.1.2-rc.1) declare NO dsh peer at all, so the
|
|
223
|
+
// family that refuses them was invisible here and the card promised a
|
|
224
|
+
// restart the host would not honour. A non-string range throws inside the
|
|
225
|
+
// host's own evaluator; that is not a verdict this can predict, so the
|
|
226
|
+
// old answer stands (#757).
|
|
227
|
+
const engines = manifest.engines?.dsh;
|
|
228
|
+
if (engines !== undefined) {
|
|
229
|
+
if (typeof engines !== 'string')
|
|
230
|
+
return null;
|
|
231
|
+
const range = engines.trim();
|
|
232
|
+
// `workspace:` is substituted only for PEERS: the host counts it as an
|
|
233
|
+
// unmet range for engines, so an empty or invalid range stays unmet too.
|
|
234
|
+
const satisfied = range === '' ? false : satisfiesRange(facts.runtimeVersion, range, { includePrerelease: true });
|
|
235
|
+
return satisfied ? null : unmetOf('engines.dsh', range, facts.runtimeVersion);
|
|
236
|
+
}
|
|
204
237
|
for (const [peer, requirement] of Object.entries(manifest.peerDependencies ?? {})) {
|
|
205
238
|
if (!isDshPeer(peer) || typeof requirement !== 'string')
|
|
206
239
|
continue;
|
|
207
240
|
const range = requirement.trim();
|
|
208
241
|
const satisfied = range === '' ? false : satisfiesRange(facts.runtimeVersion, range, { includePrerelease: true });
|
|
209
|
-
if (satisfied === false)
|
|
210
|
-
|
|
211
|
-
// below the floor, upgrading the host genuinely helps; above the cap
|
|
212
|
-
// (#757's own shape) no upgrade can satisfy it, and suggesting one
|
|
213
|
-
// points the wrong way (#758).
|
|
214
|
-
const floor = /[0-9]+\.[0-9]+\.[0-9]+[-+0-9A-Za-z.]*/.exec(range);
|
|
215
|
-
let hostTooOld = false;
|
|
216
|
-
if (floor !== null)
|
|
217
|
-
hostTooOld = compareSemver(facts.runtimeVersion, floor[0]) < 0;
|
|
218
|
-
return { peer, range, runtimeVersion: facts.runtimeVersion, hostTooOld };
|
|
219
|
-
}
|
|
242
|
+
if (satisfied === false)
|
|
243
|
+
return unmetOf(peer, range, facts.runtimeVersion);
|
|
220
244
|
}
|
|
221
245
|
return null;
|
|
222
246
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.66.
|
|
4
|
+
"version": "1.66.26",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
package/src/check.ts
CHANGED
|
@@ -520,6 +520,31 @@ function parseSemver(value: string): Semver | null {
|
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
+
/**
|
|
524
|
+
* Parse a comparator's target the way node-semver does: a PARTIAL version in
|
|
525
|
+
* a range is zero-filled, so `<0.3` is `<0.3.0` and `>=2` is `>=2.0.0`.
|
|
526
|
+
*
|
|
527
|
+
* Without this, every prerelease floor written as a partial — the
|
|
528
|
+
* `@deepseek-ai/dsh-*` peers of a whole family of connector plugins say
|
|
529
|
+
* `>=0.1.5 <0.3`, `>=0.1.7-rc <2`, `>=0.2.0-rc.0 <0.3` — parsed as unknown,
|
|
530
|
+
* `satisfiesRange` answered null, and the gate stood aside: measured against
|
|
531
|
+
* the host's own evaluator over 699 live catalog manifests, those were the
|
|
532
|
+
* last 7 of 2796 (package, runtime) pairs where the host refuses a plugin
|
|
533
|
+
* and the market still promised a restart would apply it.
|
|
534
|
+
*
|
|
535
|
+
* A BARE partial (`1.2`) is a different thing in semver — an x-range that
|
|
536
|
+
* means `>=1.2.0 <1.3.0` — so it deliberately stays unknown here rather than
|
|
537
|
+
* being mistaken for the exact version `1.2.0`.
|
|
538
|
+
*/
|
|
539
|
+
function parseComparatorTarget(target: string, op: string): Semver | null {
|
|
540
|
+
const direct = parseSemver(target)
|
|
541
|
+
if (direct !== null) return direct
|
|
542
|
+
if (op === '') return null
|
|
543
|
+
const parts = target.trim().split('.')
|
|
544
|
+
if (parts.length > 3 || parts.some(part => !/^\d+$/u.test(part))) return null
|
|
545
|
+
return parseSemver([...parts, ...Array<string>(3 - parts.length).fill('0')].join('.'))
|
|
546
|
+
}
|
|
547
|
+
|
|
523
548
|
function comparePre(a: string[], b: string[]): number {
|
|
524
549
|
const len = Math.max(a.length, b.length)
|
|
525
550
|
for (let i = 0; i < len; i += 1) {
|
|
@@ -643,7 +668,7 @@ export function satisfiesRange(
|
|
|
643
668
|
const m = /^(\^|~|>=|<=|>|<)?(.*)$/.exec(p)
|
|
644
669
|
const op = m?.[1] ?? ''
|
|
645
670
|
const target = (m?.[2] ?? '').trim()
|
|
646
|
-
const tv =
|
|
671
|
+
const tv = parseComparatorTarget(target, op)
|
|
647
672
|
if (tv === null) return null
|
|
648
673
|
const major = tv.major
|
|
649
674
|
const minor = tv.minor
|
|
@@ -688,7 +713,7 @@ export function satisfiesRange(
|
|
|
688
713
|
// prerelease admission gate. Otherwise an unknown protocol such as
|
|
689
714
|
// `workspace:^` or `catalog:default` is misreported as a definite false
|
|
690
715
|
// whenever the resolved version happens to carry a prerelease tag.
|
|
691
|
-
if (
|
|
716
|
+
if (parseComparatorTarget(target, m[1] ?? '') === null) return null
|
|
692
717
|
return { op: m[1] ?? '', target }
|
|
693
718
|
}
|
|
694
719
|
|
package/src/verify.ts
CHANGED
|
@@ -251,10 +251,28 @@ export function hostPeerGate(activeProfileDir: string, name: string, facts: Host
|
|
|
251
251
|
return peerGateAt(join(activeProfileDir, 'node_modules', name), name, facts)
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* The unmet requirement, if any, and the remedy side it points at.
|
|
256
|
+
*
|
|
257
|
+
* Which side of the range the runtime sits on decides the remedy: below the
|
|
258
|
+
* floor, upgrading the host genuinely helps; above the cap (#757's own
|
|
259
|
+
* shape) no upgrade can satisfy it, and suggesting one points the wrong way
|
|
260
|
+
* (#758).
|
|
261
|
+
*/
|
|
262
|
+
function unmetOf(peer: string, range: string, runtimeVersion: string): PeerGate {
|
|
263
|
+
const floor = /[0-9]+\.[0-9]+\.[0-9]+[-+0-9A-Za-z.]*/.exec(range)
|
|
264
|
+
const hostTooOld = floor !== null && compareSemver(runtimeVersion, floor[0]) < 0
|
|
265
|
+
return { peer, range, runtimeVersion, hostTooOld }
|
|
266
|
+
}
|
|
267
|
+
|
|
254
268
|
/** Same prediction for a patch plugin resolved outside the profile's flat node_modules. */
|
|
255
269
|
function peerGateAt(packageDir: string, name: string, facts: HostRuntimeFacts): PeerGate | null {
|
|
256
270
|
if (facts.runtimeVersion === null) return null
|
|
257
|
-
let manifest: {
|
|
271
|
+
let manifest: {
|
|
272
|
+
version?: unknown
|
|
273
|
+
engines?: Record<string, unknown>
|
|
274
|
+
peerDependencies?: Record<string, unknown>
|
|
275
|
+
}
|
|
258
276
|
try {
|
|
259
277
|
manifest = JSON.parse(
|
|
260
278
|
readFileSync(join(packageDir, 'package.json'), 'utf8'),
|
|
@@ -266,20 +284,30 @@ function peerGateAt(packageDir: string, name: string, facts: HostRuntimeFacts):
|
|
|
266
284
|
if (version !== '' && (facts.exemptions[`${name}@${version}`] ?? []).includes(facts.runtimeVersion)) {
|
|
267
285
|
return null
|
|
268
286
|
}
|
|
287
|
+
// An own `engines.dsh` range decides ALONE and peerDependencies are then
|
|
288
|
+
// ignored — the host's rule, read from unmetRequirements in
|
|
289
|
+
// @deepseek-ai/dsh-app-boot, and the reason a gate that only read peers
|
|
290
|
+
// was too lenient on 62 of 2796 (package, runtime) pairs measured over the
|
|
291
|
+
// live catalog: `dsh-file-explorer-kit` (engines.dsh ^0.1.1) and
|
|
292
|
+
// `dsh-workspace-kit` (^0.1.2-rc.1) declare NO dsh peer at all, so the
|
|
293
|
+
// family that refuses them was invisible here and the card promised a
|
|
294
|
+
// restart the host would not honour. A non-string range throws inside the
|
|
295
|
+
// host's own evaluator; that is not a verdict this can predict, so the
|
|
296
|
+
// old answer stands (#757).
|
|
297
|
+
const engines = manifest.engines?.dsh
|
|
298
|
+
if (engines !== undefined) {
|
|
299
|
+
if (typeof engines !== 'string') return null
|
|
300
|
+
const range = engines.trim()
|
|
301
|
+
// `workspace:` is substituted only for PEERS: the host counts it as an
|
|
302
|
+
// unmet range for engines, so an empty or invalid range stays unmet too.
|
|
303
|
+
const satisfied = range === '' ? false : satisfiesRange(facts.runtimeVersion, range, { includePrerelease: true })
|
|
304
|
+
return satisfied ? null : unmetOf('engines.dsh', range, facts.runtimeVersion)
|
|
305
|
+
}
|
|
269
306
|
for (const [peer, requirement] of Object.entries(manifest.peerDependencies ?? {})) {
|
|
270
307
|
if (!isDshPeer(peer) || typeof requirement !== 'string') continue
|
|
271
308
|
const range = requirement.trim()
|
|
272
309
|
const satisfied = range === '' ? false : satisfiesRange(facts.runtimeVersion, range, { includePrerelease: true })
|
|
273
|
-
if (satisfied === false)
|
|
274
|
-
// Which side of the range the runtime sits on decides the remedy:
|
|
275
|
-
// below the floor, upgrading the host genuinely helps; above the cap
|
|
276
|
-
// (#757's own shape) no upgrade can satisfy it, and suggesting one
|
|
277
|
-
// points the wrong way (#758).
|
|
278
|
-
const floor = /[0-9]+\.[0-9]+\.[0-9]+[-+0-9A-Za-z.]*/.exec(range)
|
|
279
|
-
let hostTooOld = false
|
|
280
|
-
if (floor !== null) hostTooOld = compareSemver(facts.runtimeVersion, floor[0]) < 0
|
|
281
|
-
return { peer, range, runtimeVersion: facts.runtimeVersion, hostTooOld }
|
|
282
|
-
}
|
|
310
|
+
if (satisfied === false) return unmetOf(peer, range, facts.runtimeVersion)
|
|
283
311
|
}
|
|
284
312
|
return null
|
|
285
313
|
}
|