dsh-plugin-upgrader 1.0.0 → 1.0.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.
- 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,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle metadata helpers.
|
|
3
|
+
*
|
|
4
|
+
* These translate what the profile manager reports into the fields the tab
|
|
5
|
+
* renders. Pure functions again: given the same bundle they answer the same
|
|
6
|
+
* thing, which is what makes them testable.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/bundle
|
|
9
|
+
*/
|
|
10
|
+
/** How a profile dependency was declared. */
|
|
11
|
+
export type SourceKind = 'registry' | 'path' | 'git' | 'tarball' | 'unknown';
|
|
12
|
+
/** The display languages this bundle ships. */
|
|
13
|
+
export type Locale = 'zh' | 'en';
|
|
14
|
+
/**
|
|
15
|
+
* Classify how a profile dependency was declared.
|
|
16
|
+
*
|
|
17
|
+
* @param source - `BundleInfo.source`.
|
|
18
|
+
* @returns the source family.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sourceKind(source: unknown): SourceKind;
|
|
21
|
+
/**
|
|
22
|
+
* Localized metadata value for the active display language.
|
|
23
|
+
*
|
|
24
|
+
* @param text - a string or `{ en, zh, … }`.
|
|
25
|
+
* @param locale - the requested language.
|
|
26
|
+
* @returns the best available text.
|
|
27
|
+
*/
|
|
28
|
+
export declare function pickText(text: unknown, locale: Locale): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Keep an icon only when it is cheap to send.
|
|
31
|
+
*
|
|
32
|
+
* Bundle metadata often embeds a base64 data URL, which would bloat every
|
|
33
|
+
* overview response by ~150 KB.
|
|
34
|
+
*
|
|
35
|
+
* @param icon - the manifest icon.
|
|
36
|
+
* @param limit - the largest inline data URL worth sending.
|
|
37
|
+
* @returns a usable reference.
|
|
38
|
+
*/
|
|
39
|
+
export declare function inlineIcon(icon: unknown, limit: number): string | undefined;
|
|
40
|
+
/** What `pluginManager.registries()` reports. */
|
|
41
|
+
export interface RegistryReport {
|
|
42
|
+
registry?: string | null;
|
|
43
|
+
resolved?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Registry base URL: `null` is pnpm's own configured registry.
|
|
47
|
+
*
|
|
48
|
+
* @param registries - manager answer.
|
|
49
|
+
* @returns a URL without a trailing slash.
|
|
50
|
+
*/
|
|
51
|
+
export declare function registryBase(registries: RegistryReport | undefined | null): string;
|
|
52
|
+
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../src/host/bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,6CAA6C;AAC7C,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAA;AAE5E,+CAA+C;AAC/C,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;AAEhC;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,CAUtD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3E;AAED,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAIlF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle metadata helpers.
|
|
3
|
+
*
|
|
4
|
+
* These translate what the profile manager reports into the fields the tab
|
|
5
|
+
* renders. Pure functions again: given the same bundle they answer the same
|
|
6
|
+
* thing, which is what makes them testable.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/bundle
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Classify how a profile dependency was declared.
|
|
12
|
+
*
|
|
13
|
+
* @param source - `BundleInfo.source`.
|
|
14
|
+
* @returns the source family.
|
|
15
|
+
*/
|
|
16
|
+
export function sourceKind(source) {
|
|
17
|
+
if (typeof source !== 'string' || source.trim() === '')
|
|
18
|
+
return 'unknown';
|
|
19
|
+
const value = source.trim();
|
|
20
|
+
if (value.startsWith('file:') || value.startsWith('link:') || value.startsWith('.') || value.startsWith('/')) {
|
|
21
|
+
return 'path';
|
|
22
|
+
}
|
|
23
|
+
if (/^(git|git\+|github:|https?:\/\/[^/]*\.git)/.test(value))
|
|
24
|
+
return 'git';
|
|
25
|
+
if (/\.(tgz|tar\.gz)$/.test(value))
|
|
26
|
+
return 'tarball';
|
|
27
|
+
if (/^(npm:|[\^~]?\d|[\^~]?(latest|next|beta|canary|rc)\b)/.test(value))
|
|
28
|
+
return 'registry';
|
|
29
|
+
return 'unknown';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Localized metadata value for the active display language.
|
|
33
|
+
*
|
|
34
|
+
* @param text - a string or `{ en, zh, … }`.
|
|
35
|
+
* @param locale - the requested language.
|
|
36
|
+
* @returns the best available text.
|
|
37
|
+
*/
|
|
38
|
+
export function pickText(text, locale) {
|
|
39
|
+
if (typeof text === 'string')
|
|
40
|
+
return text;
|
|
41
|
+
if (text !== null && typeof text === 'object') {
|
|
42
|
+
const record = text;
|
|
43
|
+
return record[locale] ?? record.en ?? record.zh ?? Object.values(record)[0];
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Keep an icon only when it is cheap to send.
|
|
49
|
+
*
|
|
50
|
+
* Bundle metadata often embeds a base64 data URL, which would bloat every
|
|
51
|
+
* overview response by ~150 KB.
|
|
52
|
+
*
|
|
53
|
+
* @param icon - the manifest icon.
|
|
54
|
+
* @param limit - the largest inline data URL worth sending.
|
|
55
|
+
* @returns a usable reference.
|
|
56
|
+
*/
|
|
57
|
+
export function inlineIcon(icon, limit) {
|
|
58
|
+
if (typeof icon !== 'string' || icon === '')
|
|
59
|
+
return undefined;
|
|
60
|
+
return icon.startsWith('data:') && icon.length > limit ? undefined : icon;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Registry base URL: `null` is pnpm's own configured registry.
|
|
64
|
+
*
|
|
65
|
+
* @param registries - manager answer.
|
|
66
|
+
* @returns a URL without a trailing slash.
|
|
67
|
+
*/
|
|
68
|
+
export function registryBase(registries) {
|
|
69
|
+
const candidate = registries?.registry ?? registries?.resolved ?? null;
|
|
70
|
+
const value = typeof candidate === 'string' && candidate !== '' ? candidate : 'https://registry.npmjs.org/';
|
|
71
|
+
return value.replace(/\/+$/, '');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/host/bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAe;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IACxE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IAC3B,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7G,OAAO,MAAM,CAAA;IACf,CAAC;IACD,IAAI,4CAA4C,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1E,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IACpD,IAAI,uDAAuD,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,UAAU,CAAA;IAC1F,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAa,EAAE,MAAc;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACzC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,IAA0C,CAAA;QACzD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,KAAa;IACrD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IAC7D,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;AAC3E,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,UAA6C;IACxE,MAAM,SAAS,GAAG,UAAU,EAAE,QAAQ,IAAI,UAAU,EAAE,QAAQ,IAAI,IAAI,CAAA;IACtE,MAAM,KAAK,GAAG,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,6BAA6B,CAAA;IAC3G,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAClC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP helpers shared by every route.
|
|
3
|
+
*
|
|
4
|
+
* The host half answers a small, fixed set of JSON endpoints, so these helpers
|
|
5
|
+
* stay deliberately narrow: one trust fence, one bounded body reader, one
|
|
6
|
+
* response writer.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/http
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
11
|
+
/**
|
|
12
|
+
* Whether a socket address names the loopback range.
|
|
13
|
+
*
|
|
14
|
+
* @param address - `req.socket.remoteAddress`.
|
|
15
|
+
* @returns true for 127/8 and ::1.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Request-level trust fence for a route that can install packages.
|
|
20
|
+
*
|
|
21
|
+
* This endpoint can add and remove packages, so it must not be reachable from a
|
|
22
|
+
* page on another origin: a remote address outside loopback is refused, a
|
|
23
|
+
* cross-site fetch is refused, and a stated `Origin` must match the served host.
|
|
24
|
+
*
|
|
25
|
+
* @param req - the incoming request.
|
|
26
|
+
* @returns true when the request comes from this machine and origin.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isLoopbackRequest(req: IncomingMessage): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Read a bounded JSON object body.
|
|
31
|
+
*
|
|
32
|
+
* @param req - the request.
|
|
33
|
+
* @param maxBytes - cap before the body is dropped.
|
|
34
|
+
* @returns the parsed object, or `null` when the body is absent, oversized, or
|
|
35
|
+
* not a JSON object.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readJsonBody(req: IncomingMessage, maxBytes?: number): Promise<Record<string, unknown> | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Write one JSON response.
|
|
40
|
+
*
|
|
41
|
+
* @param res - the response.
|
|
42
|
+
* @param status - HTTP status.
|
|
43
|
+
* @param body - JSON payload.
|
|
44
|
+
*/
|
|
45
|
+
export declare function writeJson(res: ServerResponse, status: number, body: unknown): void;
|
|
46
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/host/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAOtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAY/D;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,QAAQ,SAAa,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CA6BzC;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CASlF"}
|
package/lib/host/http.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a socket address names the loopback range.
|
|
3
|
+
*
|
|
4
|
+
* @param address - `req.socket.remoteAddress`.
|
|
5
|
+
* @returns true for 127/8 and ::1.
|
|
6
|
+
*/
|
|
7
|
+
export function isLoopbackAddress(address) {
|
|
8
|
+
if (address === undefined)
|
|
9
|
+
return false;
|
|
10
|
+
const normalized = address.toLowerCase();
|
|
11
|
+
if (normalized === '::1')
|
|
12
|
+
return true;
|
|
13
|
+
const v4 = normalized.startsWith('::ffff:') ? normalized.slice(7) : normalized;
|
|
14
|
+
const parts = v4.split('.');
|
|
15
|
+
return parts.length === 4 && parts[0] === '127' && parts.every((part) => /^\d{1,3}$/.test(part));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Request-level trust fence for a route that can install packages.
|
|
19
|
+
*
|
|
20
|
+
* This endpoint can add and remove packages, so it must not be reachable from a
|
|
21
|
+
* page on another origin: a remote address outside loopback is refused, a
|
|
22
|
+
* cross-site fetch is refused, and a stated `Origin` must match the served host.
|
|
23
|
+
*
|
|
24
|
+
* @param req - the incoming request.
|
|
25
|
+
* @returns true when the request comes from this machine and origin.
|
|
26
|
+
*/
|
|
27
|
+
export function isLoopbackRequest(req) {
|
|
28
|
+
if (!isLoopbackAddress(req.socket?.remoteAddress))
|
|
29
|
+
return false;
|
|
30
|
+
const host = req.headers.host;
|
|
31
|
+
if (typeof host !== 'string')
|
|
32
|
+
return false;
|
|
33
|
+
if (req.headers['sec-fetch-site'] === 'cross-site')
|
|
34
|
+
return false;
|
|
35
|
+
const origin = req.headers.origin;
|
|
36
|
+
if (origin === undefined)
|
|
37
|
+
return true;
|
|
38
|
+
try {
|
|
39
|
+
return new URL(origin).host === new URL(`http://${host}`).host;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read a bounded JSON object body.
|
|
47
|
+
*
|
|
48
|
+
* @param req - the request.
|
|
49
|
+
* @param maxBytes - cap before the body is dropped.
|
|
50
|
+
* @returns the parsed object, or `null` when the body is absent, oversized, or
|
|
51
|
+
* not a JSON object.
|
|
52
|
+
*/
|
|
53
|
+
export async function readJsonBody(req, maxBytes = 256 * 1024) {
|
|
54
|
+
const chunks = [];
|
|
55
|
+
let size = 0;
|
|
56
|
+
try {
|
|
57
|
+
for await (const chunk of req) {
|
|
58
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
59
|
+
size += buffer.length;
|
|
60
|
+
if (size > maxBytes) {
|
|
61
|
+
req.destroy();
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
chunks.push(buffer);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
let text = Buffer.concat(chunks).toString('utf8');
|
|
71
|
+
// A UTF-8 BOM survives `JSON.parse` on some clients and fails the parse.
|
|
72
|
+
if (text.charCodeAt(0) === 0xfeff)
|
|
73
|
+
text = text.slice(1);
|
|
74
|
+
if (text === '')
|
|
75
|
+
return null;
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(text);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
84
|
+
? parsed
|
|
85
|
+
: null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Write one JSON response.
|
|
89
|
+
*
|
|
90
|
+
* @param res - the response.
|
|
91
|
+
* @param status - HTTP status.
|
|
92
|
+
* @param body - JSON payload.
|
|
93
|
+
*/
|
|
94
|
+
export function writeJson(res, status, body) {
|
|
95
|
+
const payload = JSON.stringify(body);
|
|
96
|
+
res.writeHead(status, {
|
|
97
|
+
'content-type': 'application/json; charset=utf-8',
|
|
98
|
+
'cache-control': 'no-store',
|
|
99
|
+
'referrer-policy': 'no-referrer',
|
|
100
|
+
'content-length': Buffer.byteLength(payload),
|
|
101
|
+
});
|
|
102
|
+
res.end(payload);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/host/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installing one version, and putting the plugin's state back afterwards.
|
|
3
|
+
*
|
|
4
|
+
* `installBundle` refuses a spec that names an already-installed dependency, so
|
|
5
|
+
* replacing a version means removing first. These helpers wrap that two-step in
|
|
6
|
+
* one call and undo the side effects a reinstall has on the enabled flag.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-plugin-upgrader/lib/install
|
|
9
|
+
*/
|
|
10
|
+
import type { ChangeResult, PluginManager, StepRecord } from './manager.ts';
|
|
11
|
+
/** Progress sink for one job. */
|
|
12
|
+
export type LogSink = (message: string) => void;
|
|
13
|
+
/** One step's outcome, including every failure stage. */
|
|
14
|
+
export interface StepOutcome {
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
spec: string;
|
|
18
|
+
requestId: string;
|
|
19
|
+
ok: boolean;
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
application?: ChangeResult['application'];
|
|
22
|
+
changed?: boolean;
|
|
23
|
+
stage?: 'install' | 'remove' | 'enable';
|
|
24
|
+
restartReason?: string;
|
|
25
|
+
message?: string;
|
|
26
|
+
detail?: string;
|
|
27
|
+
pendingBuilds?: string[];
|
|
28
|
+
warnings?: string[];
|
|
29
|
+
packageResult?: {
|
|
30
|
+
exitCode: number;
|
|
31
|
+
kind?: string;
|
|
32
|
+
timedOut?: boolean;
|
|
33
|
+
tail?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Allowed per-step actions. */
|
|
37
|
+
export declare const ACTIONS: Set<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Re-apply the enabled state a step asked for after the package was replaced.
|
|
40
|
+
*
|
|
41
|
+
* `installBundle` activates what it installs, so a disabled plugin would
|
|
42
|
+
* otherwise come back enabled; the bundle switch is the persistent lever.
|
|
43
|
+
*
|
|
44
|
+
* @param manager - the plugin manager.
|
|
45
|
+
* @param step - the step that just ran.
|
|
46
|
+
* @param wanted - the state to restore, if any.
|
|
47
|
+
* @param log - progress sink.
|
|
48
|
+
* @returns the state now in effect, if known.
|
|
49
|
+
*/
|
|
50
|
+
export declare function restoreEnabledState(manager: PluginManager, step: Pick<StepRecord, 'name'>, wanted: boolean | undefined, log: LogSink): Promise<boolean | undefined>;
|
|
51
|
+
/**
|
|
52
|
+
* Run one step: install the exact version, falling back to remove + install,
|
|
53
|
+
* then restore the plugin's previous enabled state.
|
|
54
|
+
*
|
|
55
|
+
* Every outcome — including a thrown manager call — comes back as one record so
|
|
56
|
+
* the job log can report per-plugin success without a second error channel.
|
|
57
|
+
*
|
|
58
|
+
* @param manager - the plugin manager.
|
|
59
|
+
* @param step - the requested change.
|
|
60
|
+
* @param log - progress sink.
|
|
61
|
+
* @returns the outcome record.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runStep(manager: PluginManager, step: StepRecord, log: LogSink): Promise<StepOutcome>;
|
|
64
|
+
/**
|
|
65
|
+
* Whether a step names something this plugin is willing to install.
|
|
66
|
+
*
|
|
67
|
+
* @param step - one element of the request body.
|
|
68
|
+
* @returns an error code, or undefined when valid.
|
|
69
|
+
*/
|
|
70
|
+
export declare function validateStep(step: unknown): string | undefined;
|
|
71
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/host/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE3E,iCAAiC;AACjC,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;AAE/C,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,WAAW,CAAC,EAAE,YAAY,CAAC,aAAa,CAAC,CAAA;IACzC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACvC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,aAAa,CAAC,EAAE;QACd,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF;AAED,gCAAgC;AAChC,eAAO,MAAM,OAAO,aAAyC,CAAA;AAE7D;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAuB9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,WAAW,CAAC,CA4FtB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAM9D"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/** Allowed per-step actions. */
|
|
2
|
+
export const ACTIONS = new Set(['upgrade', 'switch']);
|
|
3
|
+
/**
|
|
4
|
+
* Re-apply the enabled state a step asked for after the package was replaced.
|
|
5
|
+
*
|
|
6
|
+
* `installBundle` activates what it installs, so a disabled plugin would
|
|
7
|
+
* otherwise come back enabled; the bundle switch is the persistent lever.
|
|
8
|
+
*
|
|
9
|
+
* @param manager - the plugin manager.
|
|
10
|
+
* @param step - the step that just ran.
|
|
11
|
+
* @param wanted - the state to restore, if any.
|
|
12
|
+
* @param log - progress sink.
|
|
13
|
+
* @returns the state now in effect, if known.
|
|
14
|
+
*/
|
|
15
|
+
export async function restoreEnabledState(manager, step, wanted, log) {
|
|
16
|
+
if (wanted === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
let current;
|
|
19
|
+
try {
|
|
20
|
+
const bundles = await manager.listBundles();
|
|
21
|
+
const entry = Array.isArray(bundles) ? bundles.find((item) => item?.name === step.name) : undefined;
|
|
22
|
+
current = entry?.enabled;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
current = undefined;
|
|
26
|
+
}
|
|
27
|
+
if (current === wanted)
|
|
28
|
+
return current;
|
|
29
|
+
log(`${wanted ? 'enable' : 'disable'} ${step.name} (restoring the previous state)`);
|
|
30
|
+
try {
|
|
31
|
+
const outcome = await manager.setBundleEnabled(step.name, wanted);
|
|
32
|
+
if (outcome?.error !== undefined) {
|
|
33
|
+
log(` ! could not restore the enabled state: ${outcome.error.code}`);
|
|
34
|
+
return current;
|
|
35
|
+
}
|
|
36
|
+
return wanted;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
log(` ! could not restore the enabled state: ${error instanceof Error ? error.message : String(error)}`);
|
|
40
|
+
return current;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Run one step: install the exact version, falling back to remove + install,
|
|
45
|
+
* then restore the plugin's previous enabled state.
|
|
46
|
+
*
|
|
47
|
+
* Every outcome — including a thrown manager call — comes back as one record so
|
|
48
|
+
* the job log can report per-plugin success without a second error channel.
|
|
49
|
+
*
|
|
50
|
+
* @param manager - the plugin manager.
|
|
51
|
+
* @param step - the requested change.
|
|
52
|
+
* @param log - progress sink.
|
|
53
|
+
* @returns the outcome record.
|
|
54
|
+
*/
|
|
55
|
+
export async function runStep(manager, step, log) {
|
|
56
|
+
const spec = `${step.name}@${step.version}`;
|
|
57
|
+
const requestId = globalThis.crypto?.randomUUID?.() ?? `pu-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
58
|
+
const buildApproval = Array.isArray(step.approveBuilds) && step.approveBuilds.length > 0
|
|
59
|
+
? { approvedBuilds: step.approveBuilds }
|
|
60
|
+
: {};
|
|
61
|
+
const base = { name: step.name, version: step.version, spec, requestId };
|
|
62
|
+
const restoreEnabled = typeof step.enabled === 'boolean' ? step.enabled : undefined;
|
|
63
|
+
log(`install ${spec}`);
|
|
64
|
+
const options = {
|
|
65
|
+
saveExact: true,
|
|
66
|
+
...(restoreEnabled === undefined ? {} : { enabled: restoreEnabled }),
|
|
67
|
+
...buildApproval,
|
|
68
|
+
};
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = await manager.installBundle(spec, { ...options, requestId });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
...base,
|
|
76
|
+
ok: false,
|
|
77
|
+
stage: 'install',
|
|
78
|
+
message: error instanceof Error ? error.message : String(error),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (result?.error?.code === 'already-installed') {
|
|
82
|
+
log(`remove ${step.name} (replacing an installed dependency)`);
|
|
83
|
+
let removed;
|
|
84
|
+
try {
|
|
85
|
+
removed = await manager.removeBundle(step.name);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
ok: false,
|
|
91
|
+
stage: 'remove',
|
|
92
|
+
application: 'failed',
|
|
93
|
+
message: `removal failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (removed?.application === 'failed' || removed?.error !== undefined) {
|
|
97
|
+
return {
|
|
98
|
+
...base,
|
|
99
|
+
ok: false,
|
|
100
|
+
stage: 'remove',
|
|
101
|
+
application: removed?.application,
|
|
102
|
+
message: removed?.error?.code ?? 'removal refused',
|
|
103
|
+
detail: removed?.error?.diagnostic,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
result = await manager.installBundle(spec, { ...options, requestId: undefined });
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
...base,
|
|
112
|
+
ok: false,
|
|
113
|
+
stage: 'install',
|
|
114
|
+
application: 'failed',
|
|
115
|
+
message: `install after removal failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const enabled = await restoreEnabledState(manager, step, restoreEnabled, log);
|
|
120
|
+
return {
|
|
121
|
+
...base,
|
|
122
|
+
...(enabled === undefined ? {} : { enabled }),
|
|
123
|
+
ok: result?.application === 'applied' || result?.application === 'restart-required',
|
|
124
|
+
application: result?.application,
|
|
125
|
+
changed: result?.changed === true,
|
|
126
|
+
stage: result?.stage,
|
|
127
|
+
restartReason: result?.restartReason,
|
|
128
|
+
version: result?.version ?? step.version,
|
|
129
|
+
message: result?.error === undefined ? undefined : result.error.code,
|
|
130
|
+
detail: result?.error?.diagnostic,
|
|
131
|
+
pendingBuilds: result?.pendingBuilds,
|
|
132
|
+
warnings: result?.warnings,
|
|
133
|
+
packageResult: result?.packageResult === undefined
|
|
134
|
+
? undefined
|
|
135
|
+
: {
|
|
136
|
+
exitCode: result.packageResult.exitCode,
|
|
137
|
+
kind: result.packageResult.kind,
|
|
138
|
+
timedOut: result.packageResult.timedOut,
|
|
139
|
+
tail: typeof result.packageResult.output === 'string'
|
|
140
|
+
? result.packageResult.output.slice(-1600)
|
|
141
|
+
: undefined,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Whether a step names something this plugin is willing to install.
|
|
147
|
+
*
|
|
148
|
+
* @param step - one element of the request body.
|
|
149
|
+
* @returns an error code, or undefined when valid.
|
|
150
|
+
*/
|
|
151
|
+
export function validateStep(step) {
|
|
152
|
+
if (step === null || typeof step !== 'object')
|
|
153
|
+
return 'invalid-step';
|
|
154
|
+
const record = step;
|
|
155
|
+
if (typeof record.name !== 'string' || typeof record.version !== 'string')
|
|
156
|
+
return 'invalid-step';
|
|
157
|
+
if (record.action !== undefined && !ACTIONS.has(String(record.action)))
|
|
158
|
+
return 'invalid-action';
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/host/install.ts"],"names":[],"mappings":"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"}
|