dsh-plugin-upgrader 1.0.0 → 1.0.2
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 +28 -0
- package/README.en.md +52 -21
- package/README.md +57 -20
- package/lib/client.js +959 -0
- package/lib/host/bundle.d.ts +52 -0
- package/lib/host/bundle.d.ts.map +1 -0
- package/lib/host/bundle.js +73 -0
- package/lib/host/bundle.js.map +1 -0
- package/lib/host/http.d.ts +46 -0
- package/lib/host/http.d.ts.map +1 -0
- package/lib/host/http.js +104 -0
- package/lib/host/http.js.map +1 -0
- package/lib/host/install.d.ts +71 -0
- package/lib/host/install.d.ts.map +1 -0
- package/lib/host/install.js +161 -0
- package/lib/host/install.js.map +1 -0
- package/lib/host/manager.d.ts +151 -0
- package/lib/host/manager.d.ts.map +1 -0
- package/lib/host/manager.js +28 -0
- package/lib/host/manager.js.map +1 -0
- package/lib/host/overview.d.ts +52 -0
- package/lib/host/overview.d.ts.map +1 -0
- package/lib/host/overview.js +109 -0
- package/lib/host/overview.js.map +1 -0
- package/lib/host/registry.d.ts +116 -0
- package/lib/host/registry.d.ts.map +1 -0
- package/lib/host/registry.js +177 -0
- package/lib/host/registry.js.map +1 -0
- package/lib/host/repo.d.ts +62 -0
- package/lib/host/repo.d.ts.map +1 -0
- package/lib/host/repo.js +102 -0
- package/lib/host/repo.js.map +1 -0
- package/lib/host/version.d.ts +67 -0
- package/lib/host/version.d.ts.map +1 -0
- package/lib/host/version.js +114 -0
- package/lib/host/version.js.map +1 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +254 -0
- package/lib/index.js.map +1 -0
- package/package.json +32 -14
- package/screenshots/history.png +0 -0
- package/screenshots/selection.png +0 -0
- package/screenshots.json +0 -4
- package/src/client.js +0 -1136
- package/src/index.js +0 -275
- package/src/lib/bundle.js +0 -64
- package/src/lib/http.js +0 -93
- package/src/lib/install.js +0 -163
- package/src/lib/overview.js +0 -108
- package/src/lib/registry.js +0 -188
- package/src/lib/repo.js +0 -91
- package/src/lib/version.js +0 -107
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of the host services this plugin uses.
|
|
3
|
+
*
|
|
4
|
+
* The published `@deepseek-ai/dsh-plugin-manager` types describe the manager as
|
|
5
|
+
* the DSH shell sees it, including branded ids and fields this plugin never
|
|
6
|
+
* touches. Depending on the whole surface would tie the build to one exact
|
|
7
|
+
* runtime build, so the plugin declares the handful of methods it calls and
|
|
8
|
+
* accepts the live service structurally. The shapes below follow the runtime
|
|
9
|
+
* contract and are the only place a contract change has to be edited.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-plugin-upgrader/lib/manager
|
|
12
|
+
*/
|
|
13
|
+
/** Reasons a profile control cannot modify its target. */
|
|
14
|
+
export type ReadOnlyReason = 'management-required' | 'unaddressable';
|
|
15
|
+
/** Localizable management failure. */
|
|
16
|
+
export interface ManagementError {
|
|
17
|
+
code: string;
|
|
18
|
+
diagnostic?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Display metadata a bundle manifest can carry, per language. */
|
|
21
|
+
export interface LocalizedMeta {
|
|
22
|
+
title?: unknown;
|
|
23
|
+
description?: unknown;
|
|
24
|
+
icon?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** One row a bundle's patch declares. */
|
|
27
|
+
export interface BundleRowInfo {
|
|
28
|
+
rowId: string;
|
|
29
|
+
moduleName: string;
|
|
30
|
+
entryId?: string;
|
|
31
|
+
}
|
|
32
|
+
/** One shipped, profile-installed, or discoverable on-demand bundle. */
|
|
33
|
+
export interface BundleInfo {
|
|
34
|
+
name: string;
|
|
35
|
+
official: boolean;
|
|
36
|
+
availability: 'installation' | 'profile' | 'missing';
|
|
37
|
+
installTarget?: {
|
|
38
|
+
spec: string;
|
|
39
|
+
version: string;
|
|
40
|
+
};
|
|
41
|
+
version?: string;
|
|
42
|
+
meta?: LocalizedMeta;
|
|
43
|
+
description?: string;
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
installed: boolean;
|
|
46
|
+
source?: string;
|
|
47
|
+
optional: boolean;
|
|
48
|
+
removable: boolean;
|
|
49
|
+
readOnlyReason?: ReadOnlyReason;
|
|
50
|
+
error?: ManagementError;
|
|
51
|
+
rows: BundleRowInfo[];
|
|
52
|
+
overrides: string[];
|
|
53
|
+
}
|
|
54
|
+
/** How a package operation failed. */
|
|
55
|
+
export type InstallFailureKind = 'pnpm-missing' | 'timeout' | 'not-found' | 'no-matching-version' | 'network' | 'disk-full' | 'permission' | 'build-blocked' | 'integrity' | 'unknown';
|
|
56
|
+
/** Package operation completion. */
|
|
57
|
+
export interface PackageResult {
|
|
58
|
+
exitCode: number;
|
|
59
|
+
output: string;
|
|
60
|
+
truncated: boolean;
|
|
61
|
+
logPath: string;
|
|
62
|
+
kind?: InstallFailureKind;
|
|
63
|
+
timedOut?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Persisted change and its application outcome. */
|
|
66
|
+
export interface ChangeResult {
|
|
67
|
+
changed: boolean;
|
|
68
|
+
application: 'applied' | 'restart-required' | 'overridden' | 'failed' | 'cancelled';
|
|
69
|
+
restartReason?: string;
|
|
70
|
+
stage: 'install' | 'enable' | 'remove';
|
|
71
|
+
target: string;
|
|
72
|
+
enabled?: boolean;
|
|
73
|
+
error?: ManagementError;
|
|
74
|
+
warnings?: string[];
|
|
75
|
+
packageResult?: PackageResult;
|
|
76
|
+
bundle?: string;
|
|
77
|
+
version?: string;
|
|
78
|
+
pendingBuilds?: string[];
|
|
79
|
+
approvedBuilds?: string[];
|
|
80
|
+
registries?: (string | null)[];
|
|
81
|
+
failedAt?: 'registry' | 'spec-host';
|
|
82
|
+
}
|
|
83
|
+
/** What one installation asks for. */
|
|
84
|
+
export interface InstallBundleOptions {
|
|
85
|
+
enabled?: boolean;
|
|
86
|
+
saveExact?: boolean;
|
|
87
|
+
requestId?: string;
|
|
88
|
+
approvedBuilds?: string[];
|
|
89
|
+
registry?: string | null;
|
|
90
|
+
}
|
|
91
|
+
/** What `registries()` reports. */
|
|
92
|
+
export interface RegistriesReport {
|
|
93
|
+
registry: string | null;
|
|
94
|
+
fallbackRegistries: readonly string[];
|
|
95
|
+
resolved: string | null;
|
|
96
|
+
}
|
|
97
|
+
/** One step of a batch job, as the browser sends it. */
|
|
98
|
+
export interface StepRecord {
|
|
99
|
+
name: string;
|
|
100
|
+
version: string;
|
|
101
|
+
action?: 'upgrade' | 'switch';
|
|
102
|
+
enabled?: boolean;
|
|
103
|
+
approveBuilds?: string[];
|
|
104
|
+
}
|
|
105
|
+
/** The manager methods this plugin calls. */
|
|
106
|
+
export interface PluginManager {
|
|
107
|
+
listBundles(): Promise<BundleInfo[]>;
|
|
108
|
+
registries(): Promise<RegistriesReport>;
|
|
109
|
+
installBundle(spec: string, options?: InstallBundleOptions): Promise<ChangeResult>;
|
|
110
|
+
removeBundle(name: string): Promise<ChangeResult>;
|
|
111
|
+
setBundleEnabled(name: string, enabled: boolean): Promise<ChangeResult>;
|
|
112
|
+
cancelInstall(requestId: string): Promise<{
|
|
113
|
+
status: 'cancelled' | 'too-late' | 'not-running';
|
|
114
|
+
}>;
|
|
115
|
+
}
|
|
116
|
+
/** The host plugin context, as far as this plugin reads it. */
|
|
117
|
+
export interface HostContext {
|
|
118
|
+
get(name: string, strict: false): unknown;
|
|
119
|
+
effect(callback: () => () => void, label?: string): void;
|
|
120
|
+
webServer: WebServer;
|
|
121
|
+
}
|
|
122
|
+
/** The host web-server service. */
|
|
123
|
+
export interface WebServer {
|
|
124
|
+
register(route: WebRoute): () => void;
|
|
125
|
+
}
|
|
126
|
+
/** One route the plugin contributes. */
|
|
127
|
+
export interface WebRoute {
|
|
128
|
+
kind: 'exact' | 'prefix';
|
|
129
|
+
/** Absolute pathname, no trailing slash. */
|
|
130
|
+
path: string;
|
|
131
|
+
handler: (req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse) => void | Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
/** One cached packument, as the overview cache stores it. */
|
|
134
|
+
export interface PackumentEntryLike {
|
|
135
|
+
at: number;
|
|
136
|
+
data: unknown;
|
|
137
|
+
}
|
|
138
|
+
/** One cached tag lookup, as the overview cache stores it. */
|
|
139
|
+
export interface TagEntryLike {
|
|
140
|
+
at: number;
|
|
141
|
+
tags?: Set<string>;
|
|
142
|
+
error?: string;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Narrow `unknown` to the manager, without asserting the whole surface blindly.
|
|
146
|
+
*
|
|
147
|
+
* @param value - whatever the context returned.
|
|
148
|
+
* @returns the manager, or `undefined` when the service is absent.
|
|
149
|
+
*/
|
|
150
|
+
export declare function asPluginManager(value: unknown): PluginManager | undefined;
|
|
151
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/host/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of the host services this plugin uses.
|
|
3
|
+
*
|
|
4
|
+
* The published `@deepseek-ai/dsh-plugin-manager` types describe the manager as
|
|
5
|
+
* the DSH shell sees it, including branded ids and fields this plugin never
|
|
6
|
+
* touches. Depending on the whole surface would tie the build to one exact
|
|
7
|
+
* runtime build, so the plugin declares the handful of methods it calls and
|
|
8
|
+
* accepts the live service structurally. The shapes below follow the runtime
|
|
9
|
+
* contract and are the only place a contract change has to be edited.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-plugin-upgrader/lib/manager
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Narrow `unknown` to the manager, without asserting the whole surface blindly.
|
|
15
|
+
*
|
|
16
|
+
* @param value - whatever the context returned.
|
|
17
|
+
* @returns the manager, or `undefined` when the service is absent.
|
|
18
|
+
*/
|
|
19
|
+
export function asPluginManager(value) {
|
|
20
|
+
if (value === null || typeof value !== 'object')
|
|
21
|
+
return undefined;
|
|
22
|
+
const candidate = value;
|
|
23
|
+
if (typeof candidate.listBundles !== 'function' || typeof candidate.installBundle !== 'function') {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return candidate;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/host/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA2JH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,SAAS,GAAG,KAA+B,CAAA;IACjD,IAAI,OAAO,SAAS,CAAC,WAAW,KAAK,UAAU,IAAI,OAAO,SAAS,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;QACjG,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,SAA0B,CAAA;AACnC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type SourceKind } from './bundle.ts';
|
|
2
|
+
import { type PackumentEntry, type TagEntry, type VersionRow } from './registry.ts';
|
|
3
|
+
import type { BundleInfo, HostContext, ManagementError, RegistriesReport, ReadOnlyReason } from './manager.ts';
|
|
4
|
+
/** One installed bundle, flattened for the tab. */
|
|
5
|
+
export interface OverviewRow {
|
|
6
|
+
name: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
official: boolean;
|
|
9
|
+
availability: BundleInfo['availability'];
|
|
10
|
+
installed: boolean;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
removable: boolean;
|
|
13
|
+
managed: boolean;
|
|
14
|
+
source?: string;
|
|
15
|
+
kind: SourceKind;
|
|
16
|
+
title: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
icon?: string;
|
|
19
|
+
readOnlyReason?: ReadOnlyReason;
|
|
20
|
+
error: ManagementError | null;
|
|
21
|
+
latest?: string;
|
|
22
|
+
repoUrl?: string;
|
|
23
|
+
upgradable: boolean;
|
|
24
|
+
versions: VersionRow[];
|
|
25
|
+
tags: Record<string, string>;
|
|
26
|
+
totalVersions: number;
|
|
27
|
+
versionError?: string;
|
|
28
|
+
}
|
|
29
|
+
/** The whole overview response body. */
|
|
30
|
+
export interface Overview {
|
|
31
|
+
generatedAt: number;
|
|
32
|
+
registry: string;
|
|
33
|
+
registries: RegistriesReport;
|
|
34
|
+
runtimeVersion: string | null;
|
|
35
|
+
bundles: OverviewRow[];
|
|
36
|
+
}
|
|
37
|
+
/** Options for one overview build. */
|
|
38
|
+
export interface OverviewOptions {
|
|
39
|
+
force?: boolean;
|
|
40
|
+
locale?: 'zh' | 'en';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the overview the tab renders.
|
|
44
|
+
*
|
|
45
|
+
* @param ctx - the plugin context.
|
|
46
|
+
* @param cache - packument cache.
|
|
47
|
+
* @param tagCache - repository tag cache.
|
|
48
|
+
* @param options - refresh + language.
|
|
49
|
+
* @returns the overview payload.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildOverview(ctx: HostContext, cache: Map<string, PackumentEntry>, tagCache: Map<string, TagEntry>, options: OverviewOptions): Promise<Overview>;
|
|
52
|
+
//# sourceMappingURL=overview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../src/host/overview.ts"],"names":[],"mappings":"AAWA,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7F,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,UAAU,EAChB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAK9G,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,CAAA;IACxC,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,UAAU,EAAE,CAAA;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wCAAwC;AACxC,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,gBAAgB,CAAA;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EAClC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC/B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAyFnB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The overview payload the Versions tab renders.
|
|
3
|
+
*
|
|
4
|
+
* One request answers everything the tab needs — installed bundles, their
|
|
5
|
+
* published versions, which of them are upgradable — because a per-bundle
|
|
6
|
+
* request would turn one page open into a dozen round trips.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/overview
|
|
9
|
+
*/
|
|
10
|
+
import { compareVersions } from './version.js';
|
|
11
|
+
import { repoUrlOf } from './repo.js';
|
|
12
|
+
import { inlineIcon, pickText, registryBase, sourceKind } from './bundle.js';
|
|
13
|
+
import { cachedTags, fetchPackument, shapePackument, } from './registry.js';
|
|
14
|
+
/** Inline data-URL icons above this size are dropped from the overview payload. */
|
|
15
|
+
const INLINE_ICON_LIMIT = 2048;
|
|
16
|
+
/**
|
|
17
|
+
* Build the overview the tab renders.
|
|
18
|
+
*
|
|
19
|
+
* @param ctx - the plugin context.
|
|
20
|
+
* @param cache - packument cache.
|
|
21
|
+
* @param tagCache - repository tag cache.
|
|
22
|
+
* @param options - refresh + language.
|
|
23
|
+
* @returns the overview payload.
|
|
24
|
+
*/
|
|
25
|
+
export async function buildOverview(ctx, cache, tagCache, options) {
|
|
26
|
+
const manager = ctx.get('pluginManager', false);
|
|
27
|
+
if (manager === null || manager === undefined) {
|
|
28
|
+
throw Object.assign(new Error('plugin manager unavailable'), { status: 503 });
|
|
29
|
+
}
|
|
30
|
+
const [bundles, registries] = await Promise.all([manager.listBundles(), manager.registries()]);
|
|
31
|
+
const base = registryBase(registries);
|
|
32
|
+
const locale = options.locale === 'zh' ? 'zh' : 'en';
|
|
33
|
+
const rows = await Promise.all(bundles.filter((bundle) => bundle.installed === true).map(async (bundle) => {
|
|
34
|
+
const kind = sourceKind(bundle.source);
|
|
35
|
+
/** Official rows live inside the installation and cannot be replaced. */
|
|
36
|
+
const managed = bundle.availability === 'profile' && bundle.installed === true;
|
|
37
|
+
const entry = {
|
|
38
|
+
name: bundle.name,
|
|
39
|
+
version: bundle.version,
|
|
40
|
+
official: bundle.official === true,
|
|
41
|
+
availability: bundle.availability,
|
|
42
|
+
installed: bundle.installed === true,
|
|
43
|
+
enabled: bundle.enabled === true,
|
|
44
|
+
removable: bundle.removable === true,
|
|
45
|
+
managed,
|
|
46
|
+
source: bundle.source,
|
|
47
|
+
kind,
|
|
48
|
+
title: pickText(bundle.meta?.title, locale) ?? bundle.name,
|
|
49
|
+
description: pickText(bundle.meta?.description ?? bundle.description, locale),
|
|
50
|
+
icon: inlineIcon(bundle.meta?.icon, INLINE_ICON_LIMIT),
|
|
51
|
+
readOnlyReason: bundle.readOnlyReason,
|
|
52
|
+
error: bundle.error ?? null,
|
|
53
|
+
latest: undefined,
|
|
54
|
+
repoUrl: undefined,
|
|
55
|
+
upgradable: false,
|
|
56
|
+
versions: [],
|
|
57
|
+
tags: {},
|
|
58
|
+
totalVersions: 0,
|
|
59
|
+
versionError: undefined,
|
|
60
|
+
};
|
|
61
|
+
if (bundle.version === undefined) {
|
|
62
|
+
entry.versionError = managed ? 'version unknown' : 'not a profile-managed package';
|
|
63
|
+
return entry;
|
|
64
|
+
}
|
|
65
|
+
if (!managed) {
|
|
66
|
+
entry.versionError = 'not a profile-managed package';
|
|
67
|
+
return entry;
|
|
68
|
+
}
|
|
69
|
+
if (kind !== 'registry' && kind !== 'unknown') {
|
|
70
|
+
entry.versionError = `${kind} source: no registry history`;
|
|
71
|
+
return entry;
|
|
72
|
+
}
|
|
73
|
+
const { data, error } = await fetchPackument(base, bundle.name, cache, options.force === true);
|
|
74
|
+
if (error !== undefined || data === undefined) {
|
|
75
|
+
entry.versionError = error ?? 'registry lookup failed';
|
|
76
|
+
return entry;
|
|
77
|
+
}
|
|
78
|
+
const repoUrl = repoUrlOf(data);
|
|
79
|
+
const tagNames = repoUrl === undefined
|
|
80
|
+
? undefined
|
|
81
|
+
: await cachedTags(repoUrl, tagCache, options.force === true);
|
|
82
|
+
const shaped = shapePackument(data, bundle.version, tagNames);
|
|
83
|
+
entry.latest = shaped.latest;
|
|
84
|
+
entry.repoUrl = shaped.repoUrl;
|
|
85
|
+
entry.versions = shaped.versions;
|
|
86
|
+
entry.tags = shaped.tags;
|
|
87
|
+
entry.totalVersions = shaped.totalVersions ?? shaped.versions.length;
|
|
88
|
+
entry.versionError = shaped.versionError;
|
|
89
|
+
entry.upgradable =
|
|
90
|
+
entry.latest !== undefined && compareVersions(entry.latest, bundle.version) > 0;
|
|
91
|
+
return entry;
|
|
92
|
+
}));
|
|
93
|
+
rows.sort((left, right) => {
|
|
94
|
+
if (left.upgradable !== right.upgradable)
|
|
95
|
+
return left.upgradable ? -1 : 1;
|
|
96
|
+
if (left.managed !== right.managed)
|
|
97
|
+
return left.managed ? -1 : 1;
|
|
98
|
+
return left.name.localeCompare(right.name);
|
|
99
|
+
});
|
|
100
|
+
const runtime = ctx.get('dshVersion', false);
|
|
101
|
+
return {
|
|
102
|
+
generatedAt: Date.now(),
|
|
103
|
+
registry: base,
|
|
104
|
+
registries,
|
|
105
|
+
runtimeVersion: typeof runtime === 'string' ? runtime : null,
|
|
106
|
+
bundles: rows,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=overview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.js","sourceRoot":"","sources":["../../src/host/overview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { type PackumentLike, type RepoManifest } from './repo.ts';
|
|
2
|
+
/** How long one registry packument stays fresh. */
|
|
3
|
+
export declare const PACKUMENT_TTL_MS: number;
|
|
4
|
+
/** Registry lookups must not hang a page render. */
|
|
5
|
+
export declare const PACKUMENT_TIMEOUT_MS: number;
|
|
6
|
+
/** Version lists are capped so one huge package cannot flood the tab. */
|
|
7
|
+
export declare const MAX_VERSIONS = 80;
|
|
8
|
+
/**
|
|
9
|
+
* How long one repository's tag list stays fresh. The GitHub API allows 60
|
|
10
|
+
* unauthenticated requests an hour and one tag list costs a request per 100
|
|
11
|
+
* tags, so this is deliberately much longer than the packument TTL.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TAGS_TTL_MS: number;
|
|
14
|
+
/** A failed tag lookup is retried sooner than a good one is refreshed. */
|
|
15
|
+
export declare const TAGS_RETRY_MS: number;
|
|
16
|
+
/** Tag lookups must not hang a page render. */
|
|
17
|
+
export declare const TAGS_TIMEOUT_MS: number;
|
|
18
|
+
/** A version manifest, as far as this module reads it. */
|
|
19
|
+
export interface VersionManifest extends RepoManifest {
|
|
20
|
+
deprecated?: unknown;
|
|
21
|
+
engines?: {
|
|
22
|
+
dsh?: unknown;
|
|
23
|
+
} | undefined;
|
|
24
|
+
dsh?: {
|
|
25
|
+
engines?: {
|
|
26
|
+
dsh?: unknown;
|
|
27
|
+
};
|
|
28
|
+
} | undefined;
|
|
29
|
+
}
|
|
30
|
+
/** The packument fields this module reads beyond {@link PackumentLike}. */
|
|
31
|
+
export interface FullPackument extends PackumentLike {
|
|
32
|
+
versions?: Record<string, VersionManifest | undefined> | undefined;
|
|
33
|
+
time?: Record<string, unknown> | undefined;
|
|
34
|
+
}
|
|
35
|
+
/** One cached packument. */
|
|
36
|
+
export interface PackumentEntry {
|
|
37
|
+
at: number;
|
|
38
|
+
data: unknown;
|
|
39
|
+
}
|
|
40
|
+
/** One cached tag lookup. */
|
|
41
|
+
export interface TagEntry {
|
|
42
|
+
at: number;
|
|
43
|
+
tags?: Set<string>;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
/** One version line the tab renders. */
|
|
47
|
+
export interface VersionRow {
|
|
48
|
+
version: string;
|
|
49
|
+
time?: string;
|
|
50
|
+
deprecated?: string;
|
|
51
|
+
engine?: string;
|
|
52
|
+
current: boolean;
|
|
53
|
+
latest: boolean;
|
|
54
|
+
/** A GitHub path such as `/releases/tag/v1.2.3`, relative to `repoUrl`. */
|
|
55
|
+
url?: string;
|
|
56
|
+
}
|
|
57
|
+
/** The packument slice the tab renders. */
|
|
58
|
+
export interface ShapedPackument {
|
|
59
|
+
latest?: string;
|
|
60
|
+
repoUrl?: string;
|
|
61
|
+
versions: VersionRow[];
|
|
62
|
+
tags: Record<string, string>;
|
|
63
|
+
totalVersions?: number;
|
|
64
|
+
versionError?: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fetch one package's packument, with a short-lived cache.
|
|
68
|
+
*
|
|
69
|
+
* @param base - registry base URL.
|
|
70
|
+
* @param packageName - package name.
|
|
71
|
+
* @param cache - shared cache.
|
|
72
|
+
* @param force - bypass a fresh cache entry.
|
|
73
|
+
* @returns the document or a reason.
|
|
74
|
+
*/
|
|
75
|
+
export declare function fetchPackument(base: string, packageName: string, cache: Map<string, PackumentEntry>, force: boolean): Promise<{
|
|
76
|
+
data?: unknown;
|
|
77
|
+
error?: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Read a repository's tag names from the GitHub API.
|
|
81
|
+
*
|
|
82
|
+
* The git ref advertisement (`/info/refs?service=git-upload-pack`) would avoid
|
|
83
|
+
* the rate limit but stalls for most repositories behind this network, while the
|
|
84
|
+
* REST list answers in well under a second. Only the first page is read: the
|
|
85
|
+
* version list itself is capped at 80 newest rows, and tags are ordered newest
|
|
86
|
+
* first, so a repository with more than 100 tags still resolves every row that
|
|
87
|
+
* can be shown.
|
|
88
|
+
*
|
|
89
|
+
* @param repoUrl - the repository URL.
|
|
90
|
+
* @param at - the time the lookup started.
|
|
91
|
+
* @returns the outcome.
|
|
92
|
+
*/
|
|
93
|
+
export declare function fetchTags(repoUrl: string, at: number): Promise<TagEntry>;
|
|
94
|
+
/**
|
|
95
|
+
* The tag names of a repository, cached.
|
|
96
|
+
*
|
|
97
|
+
* A tag list is the only way to know whether `/releases/tag/v<version>` exists.
|
|
98
|
+
* A failed lookup is cached too, but for less time: the tab must still render
|
|
99
|
+
* (with repository-level links) while GitHub is rate limiting.
|
|
100
|
+
*
|
|
101
|
+
* @param repoUrl - the repository URL.
|
|
102
|
+
* @param cache - shared cache.
|
|
103
|
+
* @param force - bypass a fresh cache entry.
|
|
104
|
+
* @returns the tag names, or `undefined` when unknown.
|
|
105
|
+
*/
|
|
106
|
+
export declare function cachedTags(repoUrl: string, cache: Map<string, TagEntry>, force: boolean): Promise<Set<string> | undefined>;
|
|
107
|
+
/**
|
|
108
|
+
* Trim one packument down to what the tab renders.
|
|
109
|
+
*
|
|
110
|
+
* @param packument - the full registry document.
|
|
111
|
+
* @param installedVersion - the version in the profile.
|
|
112
|
+
* @param tagNames - the repository's tag names, when known.
|
|
113
|
+
* @returns the slice.
|
|
114
|
+
*/
|
|
115
|
+
export declare function shapePackument(packument: FullPackument | undefined, installedVersion: string | undefined, tagNames: ReadonlySet<string> | undefined): ShapedPackument;
|
|
116
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/host/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry and GitHub lookups.
|
|
3
|
+
*
|
|
4
|
+
* The browser half cannot reach npm reliably (CORS, private registries, tokens)
|
|
5
|
+
* and cannot reach the GitHub API at all without leaking rate limits to every
|
|
6
|
+
* page load, so both lookups happen here, behind a cache.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/registry
|
|
9
|
+
*/
|
|
10
|
+
import { compareVersions, releasePathOf } from './version.js';
|
|
11
|
+
import { githubUrlOf } from './repo.js';
|
|
12
|
+
/** How long one registry packument stays fresh. */
|
|
13
|
+
export const PACKUMENT_TTL_MS = 5 * 60 * 1000;
|
|
14
|
+
/** Registry lookups must not hang a page render. */
|
|
15
|
+
export const PACKUMENT_TIMEOUT_MS = 10 * 1000;
|
|
16
|
+
/** Version lists are capped so one huge package cannot flood the tab. */
|
|
17
|
+
export const MAX_VERSIONS = 80;
|
|
18
|
+
/**
|
|
19
|
+
* How long one repository's tag list stays fresh. The GitHub API allows 60
|
|
20
|
+
* unauthenticated requests an hour and one tag list costs a request per 100
|
|
21
|
+
* tags, so this is deliberately much longer than the packument TTL.
|
|
22
|
+
*/
|
|
23
|
+
export const TAGS_TTL_MS = 30 * 60 * 1000;
|
|
24
|
+
/** A failed tag lookup is retried sooner than a good one is refreshed. */
|
|
25
|
+
export const TAGS_RETRY_MS = 5 * 60 * 1000;
|
|
26
|
+
/** Tag lookups must not hang a page render. */
|
|
27
|
+
export const TAGS_TIMEOUT_MS = 8 * 1000;
|
|
28
|
+
/**
|
|
29
|
+
* Fetch one package's packument, with a short-lived cache.
|
|
30
|
+
*
|
|
31
|
+
* @param base - registry base URL.
|
|
32
|
+
* @param packageName - package name.
|
|
33
|
+
* @param cache - shared cache.
|
|
34
|
+
* @param force - bypass a fresh cache entry.
|
|
35
|
+
* @returns the document or a reason.
|
|
36
|
+
*/
|
|
37
|
+
export async function fetchPackument(base, packageName, cache, force) {
|
|
38
|
+
const key = `${base}|${packageName}`;
|
|
39
|
+
const hit = cache.get(key);
|
|
40
|
+
if (force !== true && hit !== undefined && Date.now() - hit.at < PACKUMENT_TTL_MS) {
|
|
41
|
+
return { data: hit.data };
|
|
42
|
+
}
|
|
43
|
+
const url = `${base}/${packageName.startsWith('@') ? '@' + encodeURIComponent(packageName.slice(1)) : encodeURIComponent(packageName)}`;
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(url, {
|
|
46
|
+
headers: { accept: 'application/json' },
|
|
47
|
+
signal: AbortSignal.timeout(PACKUMENT_TIMEOUT_MS),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
return { error: `registry ${response.status}${response.status === 404 ? ' (not found)' : ''}` };
|
|
51
|
+
}
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
cache.set(key, { at: Date.now(), data });
|
|
54
|
+
return { data };
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
return { error: `registry unreachable: ${message}` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read a repository's tag names from the GitHub API.
|
|
63
|
+
*
|
|
64
|
+
* The git ref advertisement (`/info/refs?service=git-upload-pack`) would avoid
|
|
65
|
+
* the rate limit but stalls for most repositories behind this network, while the
|
|
66
|
+
* REST list answers in well under a second. Only the first page is read: the
|
|
67
|
+
* version list itself is capped at 80 newest rows, and tags are ordered newest
|
|
68
|
+
* first, so a repository with more than 100 tags still resolves every row that
|
|
69
|
+
* can be shown.
|
|
70
|
+
*
|
|
71
|
+
* @param repoUrl - the repository URL.
|
|
72
|
+
* @param at - the time the lookup started.
|
|
73
|
+
* @returns the outcome.
|
|
74
|
+
*/
|
|
75
|
+
export async function fetchTags(repoUrl, at) {
|
|
76
|
+
const match = /^https?:\/\/github\.com\/([^/]+)\/([^/#?]+?)(?:\.git)?\/?$/.exec(repoUrl);
|
|
77
|
+
if (match === null)
|
|
78
|
+
return { error: 'not a github repository', at };
|
|
79
|
+
const [, owner, repo] = match;
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/tags?per_page=100`, {
|
|
82
|
+
headers: { accept: 'application/vnd.github+json', 'user-agent': 'dsh-plugin-upgrader' },
|
|
83
|
+
signal: AbortSignal.timeout(TAGS_TIMEOUT_MS),
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const suffix = response.status === 403 || response.status === 429 ? ' (rate limited)' : '';
|
|
87
|
+
return { error: `github ${response.status}${suffix}`, at };
|
|
88
|
+
}
|
|
89
|
+
const list = await response.json();
|
|
90
|
+
if (!Array.isArray(list))
|
|
91
|
+
return { error: 'github returned no tag list', at };
|
|
92
|
+
const tags = new Set();
|
|
93
|
+
for (const entry of list) {
|
|
94
|
+
if (typeof entry?.name === 'string')
|
|
95
|
+
tags.add(entry.name);
|
|
96
|
+
}
|
|
97
|
+
return { tags, at };
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
return { error: `github unreachable: ${message}`, at };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The tag names of a repository, cached.
|
|
106
|
+
*
|
|
107
|
+
* A tag list is the only way to know whether `/releases/tag/v<version>` exists.
|
|
108
|
+
* A failed lookup is cached too, but for less time: the tab must still render
|
|
109
|
+
* (with repository-level links) while GitHub is rate limiting.
|
|
110
|
+
*
|
|
111
|
+
* @param repoUrl - the repository URL.
|
|
112
|
+
* @param cache - shared cache.
|
|
113
|
+
* @param force - bypass a fresh cache entry.
|
|
114
|
+
* @returns the tag names, or `undefined` when unknown.
|
|
115
|
+
*/
|
|
116
|
+
export async function cachedTags(repoUrl, cache, force) {
|
|
117
|
+
const hit = cache.get(repoUrl);
|
|
118
|
+
if (hit !== undefined && force !== true) {
|
|
119
|
+
const ttl = hit.tags === undefined ? TAGS_RETRY_MS : TAGS_TTL_MS;
|
|
120
|
+
if (Date.now() - hit.at < ttl)
|
|
121
|
+
return hit.tags;
|
|
122
|
+
}
|
|
123
|
+
const outcome = await fetchTags(repoUrl, Date.now());
|
|
124
|
+
cache.set(repoUrl, outcome);
|
|
125
|
+
return outcome.tags;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Trim one packument down to what the tab renders.
|
|
129
|
+
*
|
|
130
|
+
* @param packument - the full registry document.
|
|
131
|
+
* @param installedVersion - the version in the profile.
|
|
132
|
+
* @param tagNames - the repository's tag names, when known.
|
|
133
|
+
* @returns the slice.
|
|
134
|
+
*/
|
|
135
|
+
export function shapePackument(packument, installedVersion, tagNames) {
|
|
136
|
+
const versionsRecord = packument?.versions;
|
|
137
|
+
if (versionsRecord === null || versionsRecord === undefined || typeof versionsRecord !== 'object') {
|
|
138
|
+
return { versions: [], tags: {}, versionError: 'registry returned no versions' };
|
|
139
|
+
}
|
|
140
|
+
const rawTags = packument?.['dist-tags'];
|
|
141
|
+
const tags = rawTags !== null && rawTags !== undefined && typeof rawTags === 'object'
|
|
142
|
+
? rawTags
|
|
143
|
+
: {};
|
|
144
|
+
const newest = tags.latest;
|
|
145
|
+
const timeRecord = packument?.time !== null && packument?.time !== undefined && typeof packument.time === 'object'
|
|
146
|
+
? packument.time
|
|
147
|
+
: {};
|
|
148
|
+
const names = Object.keys(versionsRecord);
|
|
149
|
+
const newestManifest = typeof newest === 'string' ? versionsRecord[newest] : undefined;
|
|
150
|
+
const repoUrl = githubUrlOf(packument, newestManifest);
|
|
151
|
+
const rows = names.map((version) => {
|
|
152
|
+
const manifest = versionsRecord[version] ?? {};
|
|
153
|
+
const engine = manifest?.engines?.dsh ?? manifest?.dsh?.engines?.dsh;
|
|
154
|
+
return {
|
|
155
|
+
version,
|
|
156
|
+
time: typeof timeRecord[version] === 'string' ? timeRecord[version] : undefined,
|
|
157
|
+
deprecated: typeof manifest.deprecated === 'string' ? manifest.deprecated : undefined,
|
|
158
|
+
engine: typeof engine === 'string' ? engine : undefined,
|
|
159
|
+
current: version === installedVersion,
|
|
160
|
+
latest: version === newest,
|
|
161
|
+
url: repoUrl === undefined ? undefined : releasePathOf(tagNames, version),
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
rows.sort((left, right) => compareVersions(right.version, left.version));
|
|
165
|
+
const kept = rows.slice(0, MAX_VERSIONS);
|
|
166
|
+
const currentRow = rows.find((row) => row.current);
|
|
167
|
+
if (currentRow !== undefined && !kept.includes(currentRow))
|
|
168
|
+
kept.push(currentRow);
|
|
169
|
+
return {
|
|
170
|
+
latest: typeof newest === 'string' ? newest : undefined,
|
|
171
|
+
repoUrl,
|
|
172
|
+
versions: kept,
|
|
173
|
+
tags: Object.fromEntries(Object.entries(tags).filter((entry) => typeof entry[1] === 'string')),
|
|
174
|
+
totalVersions: rows.length,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=registry.js.map
|