@linxin666/dsh-remote-web-ui 0.1.15 → 0.1.17
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.i18n.yaml +2 -2
- package/README.md +9 -5
- package/README.zh.md +1 -1
- package/assets/apple-touch-icon.png +0 -0
- package/lib/client.js +58 -10
- package/lib/client.js.map +1 -1
- package/lib/index.js +183 -22
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -1
- package/lib/types/client/UpdatePanel.js +2 -0
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/settings-form.d.ts +62 -0
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +61 -12
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +36 -10
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +19 -2
- package/lib/types/mobile-routes.d.ts.map +1 -1
- package/lib/types/mobile-routes.js +32 -0
- package/lib/types/update.d.ts +46 -4
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +134 -8
- package/package.json +2 -1
- package/src/client/PluginSettingsCard.tsx +7 -1
- package/src/client/UpdatePanel.tsx +2 -0
- package/src/client/locales.ts +4 -0
- package/src/client/settings-card.module.css +3 -0
- package/src/client/settings-form.ts +114 -12
- package/src/index.ts +34 -10
- package/src/mobile-api.ts +19 -2
- package/src/mobile-routes.ts +33 -0
- package/src/update.ts +145 -9
package/src/update.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Remote update support for the dsh-web-ui family — host half. Detects the
|
|
3
3
|
* installed aggregate package (@linxin666/dsh-web-ui-all) and its family
|
|
4
4
|
* children, probes the npm registry for newer releases, and runs the actual
|
|
5
|
-
* update as `pnpm update` inside the owning dsh profile directory.
|
|
5
|
+
* update as `pnpm update --latest` inside the owning dsh profile directory.
|
|
6
6
|
*
|
|
7
7
|
* Pure logic with injected seams (manifest reading, registry fetches, process
|
|
8
8
|
* spawning) so the whole surface is unit-testable without touching disk,
|
|
@@ -184,16 +184,19 @@ export interface UpdateCheckDeps {
|
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
186
|
* Resolve the anchor package's manifest path. The aggregate package is the
|
|
187
|
-
* canonical entry point; this plugin's own package is the fallback.
|
|
187
|
+
* canonical entry point; this plugin's own package is the fallback. Both a
|
|
188
|
+
* throwing resolve and an undefined return mean "not installed" and move on
|
|
189
|
+
* to the next candidate.
|
|
188
190
|
* @param resolve - a Node resolve implementation scoped to the host process.
|
|
189
191
|
* @returns the absolute manifest path, or undefined when neither is installed.
|
|
190
192
|
*/
|
|
191
193
|
export function resolveAnchorManifest(
|
|
192
|
-
resolve: (specifier: string) => string,
|
|
194
|
+
resolve: (specifier: string) => string | undefined,
|
|
193
195
|
): string | undefined {
|
|
194
196
|
for (const name of [AGGREGATE_PACKAGE, SELF_PACKAGE]) {
|
|
195
197
|
try {
|
|
196
|
-
|
|
198
|
+
const path = resolve(name + '/package.json')
|
|
199
|
+
if (path !== undefined) return path
|
|
197
200
|
} catch {
|
|
198
201
|
// Not installed — try the next candidate.
|
|
199
202
|
}
|
|
@@ -280,14 +283,22 @@ export async function fetchLatestVersion(
|
|
|
280
283
|
}
|
|
281
284
|
}
|
|
282
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Sentinel for an installed version that could not be read (missing manifest
|
|
288
|
+
* or resolve failure). It is a real-looking version so `checkUpdates` can
|
|
289
|
+
* render the affected row, but the verified-update comparison must never
|
|
290
|
+
* treat it as evidence — a read failure is not a version that "moved".
|
|
291
|
+
*/
|
|
292
|
+
const VERSION_UNKNOWN = '0.0.0'
|
|
293
|
+
|
|
283
294
|
/** The resolved current version of one family package (probe failure tolerated). */
|
|
284
295
|
function readInstalledVersion(resolve: (specifier: string) => string | undefined, name: string): string {
|
|
285
296
|
try {
|
|
286
297
|
const path = resolve(name + '/package.json')
|
|
287
298
|
const version = path === undefined ? undefined : readManifest(path)?.version
|
|
288
|
-
return typeof version === 'string' ? version :
|
|
299
|
+
return typeof version === 'string' ? version : VERSION_UNKNOWN
|
|
289
300
|
} catch {
|
|
290
|
-
return
|
|
301
|
+
return VERSION_UNKNOWN
|
|
291
302
|
}
|
|
292
303
|
}
|
|
293
304
|
|
|
@@ -359,6 +370,10 @@ export type UpdateErrorCode =
|
|
|
359
370
|
| 'link'
|
|
360
371
|
/** pnpm exited non-zero. */
|
|
361
372
|
| 'pnpm-failed'
|
|
373
|
+
/** pnpm exited 0 but the installed versions did not move. */
|
|
374
|
+
| 'stale'
|
|
375
|
+
/** pnpm exited 0 but the post-run version check could not verify the install. */
|
|
376
|
+
| 'verify-failed'
|
|
362
377
|
|
|
363
378
|
/** Result of one update run. */
|
|
364
379
|
export interface UpdateRunResult {
|
|
@@ -397,6 +412,17 @@ const OUTPUT_CAP = 16 * 1024
|
|
|
397
412
|
*/
|
|
398
413
|
const WIN_CMD_MISSING_RE = /not recognized as an internal or external command/i
|
|
399
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Bypass for pnpm 11's supply-chain gate: `minimumReleaseAge` (default 24 h)
|
|
417
|
+
* silently skips same-day releases — `pnpm update --latest` then exits 0
|
|
418
|
+
* without moving anything and the post-run verification would misreport a
|
|
419
|
+
* stale no-op. The update here is explicitly user-initiated and the panel
|
|
420
|
+
* shows exactly which versions are being installed, so the gate only adds a
|
|
421
|
+
* confusing silent no-op; override it per-invocation instead of asking every
|
|
422
|
+
* user to edit the profile's pnpm-workspace.yaml.
|
|
423
|
+
*/
|
|
424
|
+
const MIN_RELEASE_AGE_OVERRIDE = '--config.minimumReleaseAge=0'
|
|
425
|
+
|
|
400
426
|
/**
|
|
401
427
|
* Run the update inside the profile directory. Tries pnpm first, falls back
|
|
402
428
|
* to corepack and then npx when the previous command is missing (ENOENT);
|
|
@@ -419,10 +445,18 @@ export function runUpdate(deps: UpdateRunDeps): Promise<UpdateRunResult> {
|
|
|
419
445
|
}
|
|
420
446
|
// Ordered fallback chain: each is tried only when the previous one is
|
|
421
447
|
// missing on PATH (ENOENT on the spawn error event, never on close).
|
|
448
|
+
// `--latest` is required: dsh plugin writes exact-version specs (e.g.
|
|
449
|
+
// "0.1.12" without a range), and plain `pnpm update` treats an exact spec
|
|
450
|
+
// as pinned — it prints "Already up to date" and exits 0 without moving
|
|
451
|
+
// the installed version, so the panel would report a false success.
|
|
452
|
+
// `MIN_RELEASE_AGE_OVERRIDE` rides along every candidate: without it the
|
|
453
|
+
// pnpm 11 minimumReleaseAge gate (default 24 h) silently keeps same-day
|
|
454
|
+
// releases in place, which the post-run verification would then have to
|
|
455
|
+
// surface as `stale` even though the update is legitimate.
|
|
422
456
|
const candidates: ReadonlyArray<{ command: string; args: string[] }> = [
|
|
423
|
-
{ command: 'pnpm', args: ['update', ...packages] },
|
|
424
|
-
{ command: 'corepack', args: ['pnpm', 'update', ...packages] },
|
|
425
|
-
{ command: 'npx', args: ['--yes', 'pnpm', 'update', ...packages] },
|
|
457
|
+
{ command: 'pnpm', args: ['update', '--latest', MIN_RELEASE_AGE_OVERRIDE, ...packages] },
|
|
458
|
+
{ command: 'corepack', args: ['pnpm', 'update', '--latest', MIN_RELEASE_AGE_OVERRIDE, ...packages] },
|
|
459
|
+
{ command: 'npx', args: ['--yes', 'pnpm', 'update', '--latest', MIN_RELEASE_AGE_OVERRIDE, ...packages] },
|
|
426
460
|
]
|
|
427
461
|
// `output` accumulates across candidates for UI display; `currentOutput`
|
|
428
462
|
// is reset per candidate and carries only that candidate's own diagnostics
|
|
@@ -523,3 +557,105 @@ export function runUpdate(deps: UpdateRunDeps): Promise<UpdateRunResult> {
|
|
|
523
557
|
runCandidate(0)
|
|
524
558
|
})
|
|
525
559
|
}
|
|
560
|
+
|
|
561
|
+
/** Seam set for the verified update run (run + post-run status check). */
|
|
562
|
+
export interface UpdateRunVerifiedDeps {
|
|
563
|
+
/** The pnpm run (profile dir + package list). */
|
|
564
|
+
run: UpdateRunDeps
|
|
565
|
+
/** The post-run status check (same seams as checkUpdates). */
|
|
566
|
+
check: UpdateCheckDeps
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Run the update, then verify the installed versions actually moved. pnpm
|
|
571
|
+
* exits 0 even when it silently kept the installed versions — runUpdate
|
|
572
|
+
* overrides pnpm 11's `minimumReleaseAge` gate (default 24 h) with
|
|
573
|
+
* `--config.minimumReleaseAge=0`, but an older pnpm without the override or
|
|
574
|
+
* another silent no-op can still keep versions in place — so a green exit
|
|
575
|
+
* alone must not report a misleading "update complete". Re-read the
|
|
576
|
+
* installed versions afterwards and surface a `stale` failure (with the
|
|
577
|
+
* captured pnpm output) when nothing moved, so the panel can tell the user
|
|
578
|
+
* how to unblock the gate instead of claiming success.
|
|
579
|
+
*
|
|
580
|
+
* The stale decision anchors on the pre-run installed versions, not on the
|
|
581
|
+
* registry latest: under a lenient gate pnpm may move 0.1.12 -> 0.1.13 while
|
|
582
|
+
* latest stays 0.1.15 — that is a real update, not stale. The post-run anchor
|
|
583
|
+
* path is re-resolved rather than reused from boot time: pnpm removes the old
|
|
584
|
+
* version's .pnpm directory on update, so a boot-time captured path would
|
|
585
|
+
* fail to read and collapse a successful update into a bogus 'missing'.
|
|
586
|
+
*
|
|
587
|
+
* A green exit whose verification has nothing to compare (registry probe
|
|
588
|
+
* outage, missing anchor, non-npm mode) is reported as `verify-failed` — it
|
|
589
|
+
* must never read as success. A green exit where no package moved but the
|
|
590
|
+
* post-run check could not prove the install is fully current (a partial
|
|
591
|
+
* probe failure hides whether a gate kept something back) is also
|
|
592
|
+
* `verify-failed`: it must not collapse into a success either.
|
|
593
|
+
* @param deps - the run and check seams.
|
|
594
|
+
* @returns the run result; `stale` when exit 0 left every version in place,
|
|
595
|
+
* `verify-failed` when the post-run check could not verify anything.
|
|
596
|
+
*/
|
|
597
|
+
export async function runUpdateVerified(deps: UpdateRunVerifiedDeps): Promise<UpdateRunResult> {
|
|
598
|
+
// Pre-run snapshot: the installed versions the update starts from. Only
|
|
599
|
+
// successfully-read versions are recorded — a read failure (VERSION_UNKNOWN)
|
|
600
|
+
// is not a baseline and must not look like a version that "moved", or a
|
|
601
|
+
// green exit that left everything in place could be mistaken for success.
|
|
602
|
+
const before = new Map<string, string>()
|
|
603
|
+
for (const name of deps.run.packages) {
|
|
604
|
+
const version = readInstalledVersion(deps.check.resolve, name)
|
|
605
|
+
if (version !== VERSION_UNKNOWN) before.set(name, version)
|
|
606
|
+
}
|
|
607
|
+
const result = await runUpdate(deps.run)
|
|
608
|
+
if (!result.ok) return result
|
|
609
|
+
// Re-resolve the anchor after the run: pnpm removes the old version's
|
|
610
|
+
// .pnpm directory, so a boot-time captured path no longer reads. Fall back
|
|
611
|
+
// to the provided path only when nothing resolves now.
|
|
612
|
+
const anchorManifestPath = resolveAnchorManifest(deps.check.resolve) ?? deps.check.anchorManifestPath
|
|
613
|
+
const status = await checkUpdates({ ...deps.check, anchorManifestPath })
|
|
614
|
+
// A green exit with no comparable result is not a success: registry probe
|
|
615
|
+
// outage, missing anchor, or a non-npm install mode all mean the post-run
|
|
616
|
+
// check could not verify anything.
|
|
617
|
+
if (status.error !== undefined || status.mode !== 'npm') {
|
|
618
|
+
return {
|
|
619
|
+
...result,
|
|
620
|
+
ok: false,
|
|
621
|
+
errorCode: 'verify-failed',
|
|
622
|
+
error: 'pnpm exited 0 but the post-run version check could not verify the install',
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
// A package "moved" only when a known pre-run version differs from a known
|
|
626
|
+
// post-run version. A read failure on either side (VERSION_UNKNOWN) is not
|
|
627
|
+
// evidence of movement and must not turn a no-op update into a success; a
|
|
628
|
+
// package without a pre-run baseline is ignored — only the packages pnpm
|
|
629
|
+
// was told to update count as evidence.
|
|
630
|
+
const moved = status.packages.some(packageStatus => {
|
|
631
|
+
const beforeVersion = before.get(packageStatus.name)
|
|
632
|
+
if (beforeVersion === undefined || packageStatus.current === VERSION_UNKNOWN) return false
|
|
633
|
+
return packageStatus.current !== beforeVersion
|
|
634
|
+
})
|
|
635
|
+
if (moved) return result
|
|
636
|
+
// Nothing moved. A green exit is only a true no-op when the post-run check
|
|
637
|
+
// proves the install is already current: every probe succeeded and nothing
|
|
638
|
+
// is outdated. If a probe failed we cannot tell "already up to date" from
|
|
639
|
+
// "a gate silently kept it back", so report verify-failed rather than a
|
|
640
|
+
// false success.
|
|
641
|
+
if (status.outdated) {
|
|
642
|
+
// stale: the check ran, a newer release exists, and no package moved
|
|
643
|
+
// (e.g. the minimumReleaseAge gate kept everything in place).
|
|
644
|
+
return {
|
|
645
|
+
...result,
|
|
646
|
+
ok: false,
|
|
647
|
+
errorCode: 'stale',
|
|
648
|
+
error: 'pnpm exited 0 but the installed versions did not change',
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
const unverifiable = status.packages.some(packageStatus => packageStatus.latest === undefined)
|
|
652
|
+
if (unverifiable) {
|
|
653
|
+
return {
|
|
654
|
+
...result,
|
|
655
|
+
ok: false,
|
|
656
|
+
errorCode: 'verify-failed',
|
|
657
|
+
error: 'pnpm exited 0 but the post-run version check could not verify the install',
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return result
|
|
661
|
+
}
|