@dsh-workbench/adapter-supply 1.1.13 → 1.1.14-beta.0
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.md +1 -1
- package/dist/plugin-source.js +7 -4
- package/package.json +3 -3
- package/dist/bundle-manager.d.ts +0 -11
- package/dist/bundle-manager.js +0 -181
- package/dist/dsh-plugins.d.ts +0 -5
- package/dist/dsh-plugins.js +0 -51
- package/dist/installed-module.d.ts +0 -51
- package/dist/installed-module.js +0 -265
- package/dist/package-content.d.ts +0 -6
- package/dist/package-content.js +0 -64
- package/dist/package-kind.d.ts +0 -21
- package/dist/package-kind.js +0 -73
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ import { profileDirOf, profileModules, readInstalledModule } from '@dsh-workbenc
|
|
|
17
17
|
|
|
18
18
|
## 边界
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
依赖 `@dsh-workbench/shared` 的宿主契约与纯工具,以及 `@dsh-workbench/adapter-runtime`(复用 DataReader 与路径);不读取 core 内部文件,不拥有 profile 的 self-update。
|
|
21
21
|
|
|
22
22
|
## 验证
|
|
23
23
|
|
package/dist/plugin-source.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { npmRegistryOf, quoteShellArg } from '@dsh-workbench/shared/host';
|
|
2
3
|
import { npmCacheDir, toolchainOpencliDir, vendorPeerLink, vendorPrefix } from '@dsh-workbench/adapter-runtime';
|
|
3
4
|
export { npmRegistryOf } from '@dsh-workbench/shared/host';
|
|
@@ -32,16 +33,18 @@ function npmSource(source, env, options) {
|
|
|
32
33
|
if (name === '')
|
|
33
34
|
return { ok: false, problem: 'malformed' };
|
|
34
35
|
const prefix = vendorPrefix();
|
|
35
|
-
const
|
|
36
|
+
const pnpm = process.env.DSH_WB_PNPM?.trim() || 'pnpm';
|
|
37
|
+
const dirInPrefix = join(prefix, 'node_modules', name);
|
|
36
38
|
const peer = vendorPeerLink();
|
|
37
39
|
const commands = [
|
|
38
40
|
{
|
|
39
41
|
line: [
|
|
40
|
-
|
|
41
|
-
`--
|
|
42
|
+
`${pnpm} add`,
|
|
43
|
+
`--dir ${quoteShellArg(prefix)}`,
|
|
42
44
|
quoteShellArg(`${name}@${version}`),
|
|
43
45
|
`--registry ${quoteShellArg(registry.value)}`,
|
|
44
|
-
`--
|
|
46
|
+
`--store-dir ${quoteShellArg(npmCacheDir())}`,
|
|
47
|
+
`--allow-build=${quoteShellArg(name)}`,
|
|
45
48
|
].join(' '),
|
|
46
49
|
},
|
|
47
50
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/adapter-supply",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14-beta.0",
|
|
4
4
|
"description": "Workbench supply adapter package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"directory": "packages/adapter-supply"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@dsh-workbench/
|
|
26
|
-
"@dsh-workbench/
|
|
25
|
+
"@dsh-workbench/adapter-runtime": "1.1.14-beta.0",
|
|
26
|
+
"@dsh-workbench/shared": "1.1.14-beta.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.19.0",
|
package/dist/bundle-manager.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
-
import type { BundleCancellation, BundleChange, BundleInspection, BundleInstallRequest, BundleRegistries, BundleRegistry } from '@dsh-workbench/shared/contract/protocol/bundle-install';
|
|
3
|
-
export interface BundleManager {
|
|
4
|
-
registries(): Promise<BundleRegistries | null>;
|
|
5
|
-
inspect(spec: string, registry?: BundleRegistry): Promise<BundleInspection | null>;
|
|
6
|
-
install(request: BundleInstallRequest): Promise<BundleChange | null>;
|
|
7
|
-
remove(name: string): Promise<BundleChange | null>;
|
|
8
|
-
setEnabled?(name: string, enabled: boolean): Promise<BundleChange | null>;
|
|
9
|
-
cancel(requestId: string): Promise<BundleCancellation | null>;
|
|
10
|
-
}
|
|
11
|
-
export declare function readBundleManager(ctx: DshHostContext): BundleManager | undefined;
|
package/dist/bundle-manager.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { RESTART_REASONS } from '@dsh-workbench/shared/contract/protocol/bundle-install';
|
|
2
|
-
const MANAGER_SERVICE = 'pluginManager';
|
|
3
|
-
const APPLICATIONS = ['applied', 'restart-required', 'overridden', 'failed', 'cancelled'];
|
|
4
|
-
const STAGES = ['install', 'enable', 'remove'];
|
|
5
|
-
const PROBLEMS = [
|
|
6
|
-
'invalid-spec',
|
|
7
|
-
'already-installed',
|
|
8
|
-
'not-found',
|
|
9
|
-
'not-a-package',
|
|
10
|
-
'not-a-bundle',
|
|
11
|
-
'not-a-workbench-module',
|
|
12
|
-
'network',
|
|
13
|
-
'unknown',
|
|
14
|
-
];
|
|
15
|
-
const SPEC_KINDS = ['registry', 'path', 'git', 'tarball'];
|
|
16
|
-
const FAILURE_KINDS = [
|
|
17
|
-
'pnpm-missing',
|
|
18
|
-
'timeout',
|
|
19
|
-
'not-found',
|
|
20
|
-
'no-matching-version',
|
|
21
|
-
'network',
|
|
22
|
-
'disk-full',
|
|
23
|
-
'permission',
|
|
24
|
-
'build-blocked',
|
|
25
|
-
'integrity',
|
|
26
|
-
'unknown',
|
|
27
|
-
];
|
|
28
|
-
const CANCELLATIONS = ['cancelled', 'too-late', 'not-running'];
|
|
29
|
-
function asRecord(value) {
|
|
30
|
-
return typeof value === 'object' && value !== null ? value : undefined;
|
|
31
|
-
}
|
|
32
|
-
function asText(value) {
|
|
33
|
-
return typeof value === 'string' ? value : '';
|
|
34
|
-
}
|
|
35
|
-
function pick(allowed, value) {
|
|
36
|
-
return typeof value === 'string' && allowed.includes(value) ? value : undefined;
|
|
37
|
-
}
|
|
38
|
-
function asRegistry(value) {
|
|
39
|
-
if (value === null)
|
|
40
|
-
return null;
|
|
41
|
-
return typeof value === 'string' ? value : undefined;
|
|
42
|
-
}
|
|
43
|
-
function asTextArray(value) {
|
|
44
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
45
|
-
}
|
|
46
|
-
function asRegistries(raw) {
|
|
47
|
-
const record = asRecord(raw);
|
|
48
|
-
if (record === undefined)
|
|
49
|
-
return null;
|
|
50
|
-
const registry = asRegistry(record.registry);
|
|
51
|
-
if (registry === undefined)
|
|
52
|
-
return null;
|
|
53
|
-
return {
|
|
54
|
-
registry,
|
|
55
|
-
fallbackRegistries: asTextArray(record.fallbackRegistries),
|
|
56
|
-
resolved: typeof record.resolved === 'string' ? record.resolved : null,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function asPackageResult(raw) {
|
|
60
|
-
const record = asRecord(raw);
|
|
61
|
-
if (record === undefined)
|
|
62
|
-
return undefined;
|
|
63
|
-
const kind = pick(FAILURE_KINDS, record.kind);
|
|
64
|
-
return {
|
|
65
|
-
exitCode: typeof record.exitCode === 'number' ? record.exitCode : -1,
|
|
66
|
-
output: asText(record.output),
|
|
67
|
-
truncated: record.truncated === true,
|
|
68
|
-
logPath: asText(record.logPath),
|
|
69
|
-
...(kind === undefined ? {} : { kind }),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function asChange(raw) {
|
|
73
|
-
const record = asRecord(raw);
|
|
74
|
-
if (record === undefined)
|
|
75
|
-
return null;
|
|
76
|
-
const application = pick(APPLICATIONS, record.application);
|
|
77
|
-
const stage = pick(STAGES, record.stage);
|
|
78
|
-
if (application === undefined || stage === undefined)
|
|
79
|
-
return null;
|
|
80
|
-
const error = asRecord(record.error);
|
|
81
|
-
const packageResult = asPackageResult(record.packageResult);
|
|
82
|
-
const pendingBuilds = asTextArray(record.pendingBuilds);
|
|
83
|
-
const restartReason = pick(RESTART_REASONS, record.restartReason);
|
|
84
|
-
const failedAt = pick(['registry', 'spec-host'], record.failedAt);
|
|
85
|
-
const bundle = asText(record.bundle);
|
|
86
|
-
const version = asText(record.version);
|
|
87
|
-
const errorCode = error === undefined ? '' : asText(error.code);
|
|
88
|
-
const errorDetail = error === undefined ? '' : asText(error.diagnostic);
|
|
89
|
-
return {
|
|
90
|
-
application,
|
|
91
|
-
stage,
|
|
92
|
-
changed: record.changed === true,
|
|
93
|
-
target: asText(record.target),
|
|
94
|
-
...(bundle === '' ? {} : { bundle }),
|
|
95
|
-
...(version === '' ? {} : { version }),
|
|
96
|
-
...(errorCode === '' ? {} : { errorCode }),
|
|
97
|
-
...(errorDetail === '' ? {} : { errorDetail }),
|
|
98
|
-
...(packageResult === undefined ? {} : { packageResult }),
|
|
99
|
-
...(pendingBuilds.length === 0 ? {} : { pendingBuilds }),
|
|
100
|
-
...(restartReason === undefined ? {} : { restartReason }),
|
|
101
|
-
...(failedAt === undefined ? {} : { failedAt }),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function asInspection(raw) {
|
|
105
|
-
const record = asRecord(raw);
|
|
106
|
-
if (record === undefined)
|
|
107
|
-
return null;
|
|
108
|
-
const status = asText(record.status);
|
|
109
|
-
if (status === 'refused') {
|
|
110
|
-
const problem = pick(PROBLEMS, record.problem) ?? 'unknown';
|
|
111
|
-
const registries = Array.isArray(record.registries)
|
|
112
|
-
? record.registries.map(asRegistry).filter((item) => item !== undefined)
|
|
113
|
-
: [];
|
|
114
|
-
return {
|
|
115
|
-
status: 'refused',
|
|
116
|
-
problem,
|
|
117
|
-
reason: asText(record.reason),
|
|
118
|
-
...(registries.length === 0 ? {} : { registries }),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
if (status !== 'accepted')
|
|
122
|
-
return null;
|
|
123
|
-
const kind = pick(SPEC_KINDS, record.kind);
|
|
124
|
-
const registry = asRegistry(record.registry);
|
|
125
|
-
if (kind === undefined || registry === undefined)
|
|
126
|
-
return null;
|
|
127
|
-
const name = asText(record.name);
|
|
128
|
-
const version = asText(record.version);
|
|
129
|
-
const description = asText(record.description);
|
|
130
|
-
const host = asText(record.host);
|
|
131
|
-
return {
|
|
132
|
-
status: 'accepted',
|
|
133
|
-
kind,
|
|
134
|
-
bundle: typeof record.bundle === 'boolean' ? record.bundle : null,
|
|
135
|
-
registry,
|
|
136
|
-
...(name === '' ? {} : { name }),
|
|
137
|
-
...(version === '' ? {} : { version }),
|
|
138
|
-
...(description === '' ? {} : { description }),
|
|
139
|
-
...(host === '' ? {} : { host }),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
export function readBundleManager(ctx) {
|
|
143
|
-
const face = ctx.get(MANAGER_SERVICE);
|
|
144
|
-
if (face === undefined)
|
|
145
|
-
return undefined;
|
|
146
|
-
if (typeof face.installBundle !== 'function' || typeof face.removeBundle !== 'function')
|
|
147
|
-
return undefined;
|
|
148
|
-
return {
|
|
149
|
-
async registries() {
|
|
150
|
-
if (typeof face.registries !== 'function')
|
|
151
|
-
return null;
|
|
152
|
-
return asRegistries(await face.registries());
|
|
153
|
-
},
|
|
154
|
-
async inspect(spec, registry) {
|
|
155
|
-
if (typeof face.inspect !== 'function')
|
|
156
|
-
return null;
|
|
157
|
-
return asInspection(await face.inspect(spec, registry === undefined ? undefined : { registry }));
|
|
158
|
-
},
|
|
159
|
-
async install(request) {
|
|
160
|
-
const options = {
|
|
161
|
-
...(request.registry === undefined ? {} : { registry: request.registry }),
|
|
162
|
-
...(request.requestId === undefined ? {} : { requestId: request.requestId }),
|
|
163
|
-
...(request.approvedBuilds === undefined ? {} : { approvedBuilds: request.approvedBuilds }),
|
|
164
|
-
};
|
|
165
|
-
return asChange(await face.installBundle?.(request.spec, options));
|
|
166
|
-
},
|
|
167
|
-
async remove(name) {
|
|
168
|
-
return asChange(await face.removeBundle?.(name));
|
|
169
|
-
},
|
|
170
|
-
async setEnabled(name, enabled) {
|
|
171
|
-
if (typeof face.setBundleEnabled !== 'function')
|
|
172
|
-
return null;
|
|
173
|
-
return asChange(await face.setBundleEnabled(name, enabled));
|
|
174
|
-
},
|
|
175
|
-
async cancel(requestId) {
|
|
176
|
-
if (typeof face.cancelInstall !== 'function')
|
|
177
|
-
return null;
|
|
178
|
-
return pick(CANCELLATIONS, (asRecord(await face.cancelInstall(requestId)) ?? {}).status) ?? null;
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
}
|
package/dist/dsh-plugins.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
-
import type { RouteHandler } from '@dsh-workbench/shared/host';
|
|
3
|
-
import type { DshPluginsSnapshot } from '@dsh-workbench/shared/contract/protocol/dsh-plugins';
|
|
4
|
-
export declare function readDshPlugins(ctx: DshHostContext): Promise<DshPluginsSnapshot>;
|
|
5
|
-
export declare function dshPluginsHandler(ctx: DshHostContext): RouteHandler;
|
package/dist/dsh-plugins.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const MANAGER_SERVICE = 'pluginManager';
|
|
2
|
-
function asRecord(value) {
|
|
3
|
-
return typeof value === 'object' && value !== null ? value : undefined;
|
|
4
|
-
}
|
|
5
|
-
function asArray(value) {
|
|
6
|
-
if (Array.isArray(value))
|
|
7
|
-
return value;
|
|
8
|
-
const record = asRecord(value);
|
|
9
|
-
if (record === undefined)
|
|
10
|
-
return [];
|
|
11
|
-
for (const key of ['rows', 'items', 'list', 'bundles', 'plugins']) {
|
|
12
|
-
const inner = record[key];
|
|
13
|
-
if (Array.isArray(inner))
|
|
14
|
-
return inner;
|
|
15
|
-
}
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
function asText(value) {
|
|
19
|
-
return typeof value === 'string' ? value : '';
|
|
20
|
-
}
|
|
21
|
-
function rowOf(value) {
|
|
22
|
-
const record = asRecord(value);
|
|
23
|
-
if (record === undefined)
|
|
24
|
-
return undefined;
|
|
25
|
-
const name = asText(record.name) || asText(record.id);
|
|
26
|
-
if (name === '')
|
|
27
|
-
return undefined;
|
|
28
|
-
return { name, version: asText(record.version), enabled: record.disabled !== true && record.enabled !== false };
|
|
29
|
-
}
|
|
30
|
-
function rowsOf(value) {
|
|
31
|
-
const rows = [];
|
|
32
|
-
for (const item of asArray(value)) {
|
|
33
|
-
const row = rowOf(item);
|
|
34
|
-
if (row !== undefined)
|
|
35
|
-
rows.push(row);
|
|
36
|
-
}
|
|
37
|
-
return rows;
|
|
38
|
-
}
|
|
39
|
-
export async function readDshPlugins(ctx) {
|
|
40
|
-
const manager = ctx.get(MANAGER_SERVICE);
|
|
41
|
-
if (manager === undefined)
|
|
42
|
-
return { available: false, bundles: [], plugins: [] };
|
|
43
|
-
const [bundles, plugins] = await Promise.all([
|
|
44
|
-
manager.listBundles === undefined ? undefined : manager.listBundles(),
|
|
45
|
-
manager.listPlugins === undefined ? undefined : manager.listPlugins(),
|
|
46
|
-
]);
|
|
47
|
-
return { available: true, bundles: rowsOf(bundles), plugins: rowsOf(plugins) };
|
|
48
|
-
}
|
|
49
|
-
export function dshPluginsHandler(ctx) {
|
|
50
|
-
return () => readDshPlugins(ctx);
|
|
51
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
-
export interface UnitDeclarationSource {
|
|
3
|
-
readonly pkg: string;
|
|
4
|
-
readonly declaration: unknown;
|
|
5
|
-
}
|
|
6
|
-
export type InstalledModule = {
|
|
7
|
-
readonly kind: 'module';
|
|
8
|
-
readonly pkg: string;
|
|
9
|
-
readonly unit: string;
|
|
10
|
-
readonly version: string;
|
|
11
|
-
readonly registry?: string;
|
|
12
|
-
readonly local?: boolean;
|
|
13
|
-
} | {
|
|
14
|
-
readonly kind: 'not-module';
|
|
15
|
-
readonly pkg: string;
|
|
16
|
-
} | {
|
|
17
|
-
readonly kind: 'unknown';
|
|
18
|
-
readonly pkg: string;
|
|
19
|
-
};
|
|
20
|
-
export declare function isPackageName(value: string): boolean;
|
|
21
|
-
export declare function profileDirOf(ctx: DshHostContext): string | null;
|
|
22
|
-
export interface ProfileModule {
|
|
23
|
-
readonly pkg: string;
|
|
24
|
-
readonly unit: string;
|
|
25
|
-
readonly version: string;
|
|
26
|
-
readonly registry?: string;
|
|
27
|
-
readonly local?: boolean;
|
|
28
|
-
readonly fromRegistry?: boolean;
|
|
29
|
-
}
|
|
30
|
-
export declare function profileManifestOf(profileDir: string | null): Readonly<Record<string, unknown>> | undefined;
|
|
31
|
-
export declare function profileModules(profileDir: string | null): readonly ProfileModule[];
|
|
32
|
-
export declare function createDeclarationLookup(profileDir: string | null): (unit: string) => UnitDeclarationSource | undefined;
|
|
33
|
-
export declare function moduleDeclarationOf(profileDir: string | null, pkg: string): unknown;
|
|
34
|
-
export interface DeclarationGap {
|
|
35
|
-
readonly pkg: string;
|
|
36
|
-
readonly problems: readonly string[];
|
|
37
|
-
}
|
|
38
|
-
export declare function declarationGaps(profileDir: string | null): readonly DeclarationGap[] | undefined;
|
|
39
|
-
export declare function declaredPluginCommands(profileDir: string | null): ReadonlyMap<string, ReadonlySet<string>>;
|
|
40
|
-
export declare function readInstalledModule(profileDir: string | null, pkg: string): InstalledModule;
|
|
41
|
-
export declare function isPackedArtifactSpec(spec: string): boolean;
|
|
42
|
-
export declare function isLocalDependencySpec(spec: string): boolean;
|
|
43
|
-
export declare function isRegistryDependencySpec(spec: string): boolean;
|
|
44
|
-
export interface ProfilePackage {
|
|
45
|
-
readonly pkg: string;
|
|
46
|
-
readonly version: string;
|
|
47
|
-
readonly registry?: string;
|
|
48
|
-
readonly local?: boolean;
|
|
49
|
-
}
|
|
50
|
-
export declare function readProfilePackage(profileDir: string | null, pkg: string): ProfilePackage | undefined;
|
|
51
|
-
export declare function packageVersionAt(packageRoot: string): string | undefined;
|
package/dist/installed-module.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { declarationProblemsOf, packageSourceOf, readDeclaration, scopeOf } from '@dsh-workbench/shared/host';
|
|
4
|
-
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
5
|
-
function asRecord(value) {
|
|
6
|
-
return typeof value === 'object' && value !== null ? value : undefined;
|
|
7
|
-
}
|
|
8
|
-
function asText(value) {
|
|
9
|
-
return typeof value === 'string' ? value : '';
|
|
10
|
-
}
|
|
11
|
-
export function isPackageName(value) {
|
|
12
|
-
return value.length <= 214 && PACKAGE_NAME.test(value);
|
|
13
|
-
}
|
|
14
|
-
export function profileDirOf(ctx) {
|
|
15
|
-
const profile = ctx.get('profileContext');
|
|
16
|
-
const dir = typeof profile?.dir === 'string' ? profile.dir.trim() : '';
|
|
17
|
-
return dir === '' ? null : dir;
|
|
18
|
-
}
|
|
19
|
-
function stringsOf(value) {
|
|
20
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
21
|
-
}
|
|
22
|
-
export function profileManifestOf(profileDir) {
|
|
23
|
-
if (profileDir === null)
|
|
24
|
-
return undefined;
|
|
25
|
-
try {
|
|
26
|
-
return asRecord(JSON.parse(fs.readFileSync(path.join(profileDir, 'package.json'), 'utf8')));
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export function profileModules(profileDir) {
|
|
33
|
-
const record = profileManifestOf(profileDir);
|
|
34
|
-
if (record === undefined)
|
|
35
|
-
return [];
|
|
36
|
-
const bundles = stringsOf(asRecord(asRecord(record.dsh)?.profile)?.bundles);
|
|
37
|
-
const dependencies = asRecord(record.dependencies) ?? {};
|
|
38
|
-
const names = bundles.length > 0 ? bundles : Object.keys(dependencies);
|
|
39
|
-
const found = [];
|
|
40
|
-
for (const pkg of names) {
|
|
41
|
-
const read = readInstalledModule(profileDir, pkg);
|
|
42
|
-
if (read.kind === 'module') {
|
|
43
|
-
const spec = asText(dependencies[read.pkg]);
|
|
44
|
-
found.push({
|
|
45
|
-
pkg: read.pkg,
|
|
46
|
-
unit: read.unit,
|
|
47
|
-
version: read.version,
|
|
48
|
-
...(read.registry === undefined ? {} : { registry: read.registry }),
|
|
49
|
-
local: isLocalDependencySpec(spec),
|
|
50
|
-
fromRegistry: isRegistryDependencySpec(spec),
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return found;
|
|
55
|
-
}
|
|
56
|
-
export function createDeclarationLookup(profileDir) {
|
|
57
|
-
let roster;
|
|
58
|
-
const scan = () => new Map(profileModules(profileDir).map((row) => [row.unit, row.pkg]));
|
|
59
|
-
return (unit) => {
|
|
60
|
-
roster ??= scan();
|
|
61
|
-
let pkg = roster.get(unit);
|
|
62
|
-
if (pkg === undefined) {
|
|
63
|
-
roster = scan();
|
|
64
|
-
pkg = roster.get(unit);
|
|
65
|
-
}
|
|
66
|
-
return pkg === undefined ? undefined : { pkg, declaration: moduleDeclarationOf(profileDir, pkg) };
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
function registryFromLockfile(profileDir, pkg) {
|
|
70
|
-
let text;
|
|
71
|
-
try {
|
|
72
|
-
text = fs.readFileSync(path.join(profileDir, 'pnpm-lock.yaml'), 'utf8');
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
const at = text.indexOf(` ${pkg}@`);
|
|
78
|
-
const quoted = at === -1 ? text.indexOf(` '${pkg}@`) : at;
|
|
79
|
-
if (quoted === -1)
|
|
80
|
-
return undefined;
|
|
81
|
-
const headerEnd = text.indexOf('\n', quoted);
|
|
82
|
-
const end = headerEnd === -1 ? -1 : text.indexOf('\n ', headerEnd + 1);
|
|
83
|
-
const block = text.slice(quoted, end === -1 ? quoted + 900 : end);
|
|
84
|
-
const match = /tarball:\s*([^\s,}]+)/.exec(block);
|
|
85
|
-
const tarball = match?.[1];
|
|
86
|
-
if (tarball === undefined || !tarball.startsWith('http'))
|
|
87
|
-
return undefined;
|
|
88
|
-
const cut = tarball.lastIndexOf(`/${pkg}/`);
|
|
89
|
-
if (cut <= 'https://'.length)
|
|
90
|
-
return undefined;
|
|
91
|
-
return tarball.slice(0, cut);
|
|
92
|
-
}
|
|
93
|
-
function unquoted(value) {
|
|
94
|
-
const quote = value.charAt(0);
|
|
95
|
-
if (value.length >= 2 && (quote === '"' || quote === "'") && value.endsWith(quote)) {
|
|
96
|
-
return value.slice(1, -1).trim();
|
|
97
|
-
}
|
|
98
|
-
return value;
|
|
99
|
-
}
|
|
100
|
-
function registryFromNpmrc(profileDir, pkg) {
|
|
101
|
-
let text;
|
|
102
|
-
try {
|
|
103
|
-
text = fs.readFileSync(path.join(profileDir, '.npmrc'), 'utf8');
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
const scope = scopeOf(pkg);
|
|
109
|
-
let fallback;
|
|
110
|
-
for (const line of text.split('\n')) {
|
|
111
|
-
const trimmed = line.trim();
|
|
112
|
-
if (trimmed === '' || trimmed.startsWith('#') || trimmed.startsWith(';'))
|
|
113
|
-
continue;
|
|
114
|
-
const separator = trimmed.indexOf('=');
|
|
115
|
-
if (separator <= 0)
|
|
116
|
-
continue;
|
|
117
|
-
const key = trimmed.slice(0, separator).trim();
|
|
118
|
-
const value = unquoted(trimmed.slice(separator + 1).trim());
|
|
119
|
-
if (value === '')
|
|
120
|
-
continue;
|
|
121
|
-
if (scope !== null && key === `${scope}:registry`)
|
|
122
|
-
return value;
|
|
123
|
-
if (key === 'registry')
|
|
124
|
-
fallback = value;
|
|
125
|
-
}
|
|
126
|
-
return fallback;
|
|
127
|
-
}
|
|
128
|
-
function registryOf(profileDir, pkg, manifest) {
|
|
129
|
-
const declared = asText(asRecord(manifest.publishConfig)?.registry);
|
|
130
|
-
if (declared !== '')
|
|
131
|
-
return declared;
|
|
132
|
-
const recorded = packageSourceOf(profileDir, pkg);
|
|
133
|
-
if (recorded !== undefined)
|
|
134
|
-
return recorded.registry;
|
|
135
|
-
const locked = registryFromLockfile(profileDir, pkg);
|
|
136
|
-
return locked === undefined ? registryFromNpmrc(profileDir, pkg) : locked;
|
|
137
|
-
}
|
|
138
|
-
export function moduleDeclarationOf(profileDir, pkg) {
|
|
139
|
-
if (profileDir === null || !isPackageName(pkg))
|
|
140
|
-
return undefined;
|
|
141
|
-
try {
|
|
142
|
-
const text = fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8');
|
|
143
|
-
return asRecord(asRecord(asRecord(JSON.parse(text))?.dsh)?.workbench);
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
export function declarationGaps(profileDir) {
|
|
150
|
-
if (profileManifestOf(profileDir) === undefined)
|
|
151
|
-
return undefined;
|
|
152
|
-
const gaps = [];
|
|
153
|
-
for (const module of profileModules(profileDir)) {
|
|
154
|
-
const problems = declarationProblemsOf(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
155
|
-
if (problems.length > 0)
|
|
156
|
-
gaps.push({ pkg: module.pkg, problems });
|
|
157
|
-
}
|
|
158
|
-
return gaps;
|
|
159
|
-
}
|
|
160
|
-
export function declaredPluginCommands(profileDir) {
|
|
161
|
-
const declared = new Map();
|
|
162
|
-
for (const module of profileModules(profileDir)) {
|
|
163
|
-
const read = readDeclaration(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
164
|
-
for (const need of read.manifest?.needs ?? []) {
|
|
165
|
-
if (need.kind !== 'extension')
|
|
166
|
-
continue;
|
|
167
|
-
const name = need.name.trim();
|
|
168
|
-
if (name === '')
|
|
169
|
-
continue;
|
|
170
|
-
const commands = declared.get(name) ?? new Set();
|
|
171
|
-
for (const command of need.commands ?? [])
|
|
172
|
-
commands.add(command);
|
|
173
|
-
declared.set(name, commands);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return declared;
|
|
177
|
-
}
|
|
178
|
-
export function readInstalledModule(profileDir, pkg) {
|
|
179
|
-
if (profileDir === null || !isPackageName(pkg))
|
|
180
|
-
return { kind: 'unknown', pkg };
|
|
181
|
-
let manifest;
|
|
182
|
-
try {
|
|
183
|
-
manifest = JSON.parse(fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8'));
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
return { kind: 'unknown', pkg };
|
|
187
|
-
}
|
|
188
|
-
const record = asRecord(manifest);
|
|
189
|
-
if (record === undefined)
|
|
190
|
-
return { kind: 'unknown', pkg };
|
|
191
|
-
const declared = asRecord(asRecord(record.dsh)?.workbench);
|
|
192
|
-
const unit = declared === undefined ? '' : asText(declared.unit);
|
|
193
|
-
if (unit === '')
|
|
194
|
-
return { kind: 'not-module', pkg };
|
|
195
|
-
const registry = registryOf(profileDir, pkg, record);
|
|
196
|
-
return {
|
|
197
|
-
kind: 'module',
|
|
198
|
-
pkg,
|
|
199
|
-
unit,
|
|
200
|
-
version: asText(record.version),
|
|
201
|
-
...(registry === undefined ? {} : { registry }),
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
const PACKED_ARTIFACT = /\.(?:tgz|tar\.gz|tar)$/i;
|
|
205
|
-
export function isPackedArtifactSpec(spec) {
|
|
206
|
-
const trimmed = spec.trim();
|
|
207
|
-
const pkgPath = trimmed.startsWith('file:') ? trimmed.slice('file:'.length) : trimmed;
|
|
208
|
-
return PACKED_ARTIFACT.test(pkgPath);
|
|
209
|
-
}
|
|
210
|
-
export function isLocalDependencySpec(spec) {
|
|
211
|
-
const trimmed = spec.trim();
|
|
212
|
-
if (isPackedArtifactSpec(trimmed))
|
|
213
|
-
return false;
|
|
214
|
-
return (trimmed.startsWith('link:') ||
|
|
215
|
-
trimmed.startsWith('file:') ||
|
|
216
|
-
trimmed.startsWith('./') ||
|
|
217
|
-
trimmed.startsWith('../') ||
|
|
218
|
-
trimmed.startsWith('/'));
|
|
219
|
-
}
|
|
220
|
-
const NON_REGISTRY_SPEC = /^(?:git\+|git:|github:|gitlab:|bitbucket:|https?:|ssh:|workspace:)/i;
|
|
221
|
-
export function isRegistryDependencySpec(spec) {
|
|
222
|
-
const trimmed = spec.trim();
|
|
223
|
-
if (trimmed === '')
|
|
224
|
-
return true;
|
|
225
|
-
if (isLocalDependencySpec(trimmed) || isPackedArtifactSpec(trimmed))
|
|
226
|
-
return false;
|
|
227
|
-
return !NON_REGISTRY_SPEC.test(trimmed);
|
|
228
|
-
}
|
|
229
|
-
export function readProfilePackage(profileDir, pkg) {
|
|
230
|
-
if (profileDir === null || !isPackageName(pkg))
|
|
231
|
-
return undefined;
|
|
232
|
-
let manifest;
|
|
233
|
-
try {
|
|
234
|
-
manifest = JSON.parse(fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8'));
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
const record = asRecord(manifest);
|
|
240
|
-
if (record === undefined)
|
|
241
|
-
return undefined;
|
|
242
|
-
const version = asText(record.version);
|
|
243
|
-
if (version === '')
|
|
244
|
-
return undefined;
|
|
245
|
-
const registry = registryOf(profileDir, pkg, record);
|
|
246
|
-
const dependencies = asRecord(profileManifestOf(profileDir)?.dependencies) ?? {};
|
|
247
|
-
const local = isLocalDependencySpec(asText(dependencies[pkg]));
|
|
248
|
-
return {
|
|
249
|
-
pkg,
|
|
250
|
-
version,
|
|
251
|
-
...(registry === undefined ? {} : { registry }),
|
|
252
|
-
...(local ? { local: true } : {}),
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
export function packageVersionAt(packageRoot) {
|
|
256
|
-
let manifest;
|
|
257
|
-
try {
|
|
258
|
-
manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
const version = asText(asRecord(manifest)?.version);
|
|
264
|
-
return version === '' ? undefined : version;
|
|
265
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { DshShell } from '@dsh-workbench/shared';
|
|
2
|
-
export declare function withPackageDir<T>(options: {
|
|
3
|
-
readonly shell?: DshShell | undefined;
|
|
4
|
-
readonly spec: string;
|
|
5
|
-
readonly registry?: string | undefined;
|
|
6
|
-
}, visit: (dir: string) => T): Promise<T | undefined>;
|
package/dist/package-content.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { npmCacheDir } from '@dsh-workbench/adapter-runtime';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { INSTALL_TIMEOUT_MS, parseJsonLoose, quoteShellArg, runShell } from '@dsh-workbench/shared/host';
|
|
6
|
-
import { splitNpmSpec } from './package-kind.js';
|
|
7
|
-
import { localDirOf } from './registry-declaration.js';
|
|
8
|
-
function packedFileFrom(stdout, root) {
|
|
9
|
-
const parsed = parseJsonLoose(stdout);
|
|
10
|
-
if (Array.isArray(parsed)) {
|
|
11
|
-
const first = parsed[0];
|
|
12
|
-
const filename = typeof first === 'object' && first !== null ? first.filename : undefined;
|
|
13
|
-
if (typeof filename === 'string' && filename.trim() !== '') {
|
|
14
|
-
const at = path.join(root, path.basename(filename.trim()));
|
|
15
|
-
if (fs.existsSync(at))
|
|
16
|
-
return at;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
const found = fs.readdirSync(root).filter((name) => name.endsWith('.tgz'));
|
|
21
|
-
return found.length === 1 ? path.join(root, found[0] ?? '') : undefined;
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export async function withPackageDir(options, visit) {
|
|
28
|
-
const local = localDirOf(options.spec);
|
|
29
|
-
if (local !== undefined)
|
|
30
|
-
return visit(local);
|
|
31
|
-
if (splitNpmSpec(options.spec) === undefined)
|
|
32
|
-
return undefined;
|
|
33
|
-
const shell = options.shell;
|
|
34
|
-
if (shell === undefined)
|
|
35
|
-
return undefined;
|
|
36
|
-
let root;
|
|
37
|
-
try {
|
|
38
|
-
root = fs.mkdtempSync(path.join(os.tmpdir(), 'wb-package-'));
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
const at = options.registry === undefined ? '' : ` --registry ${quoteShellArg(options.registry)}`;
|
|
45
|
-
const packed = await runShell(shell, `npm pack ${quoteShellArg(options.spec)} --ignore-scripts --json --pack-destination ${quoteShellArg(root)} --cache ${quoteShellArg(npmCacheDir())}${at}`, { timeoutMs: INSTALL_TIMEOUT_MS }).catch(() => undefined);
|
|
46
|
-
if (packed === undefined || packed.exitCode !== 0)
|
|
47
|
-
return undefined;
|
|
48
|
-
const file = packedFileFrom(packed.stdout, root);
|
|
49
|
-
if (file === undefined)
|
|
50
|
-
return undefined;
|
|
51
|
-
const unpacked = await runShell(shell, `tar -xzf ${quoteShellArg(file)} -C ${quoteShellArg(root)}`, {
|
|
52
|
-
timeoutMs: INSTALL_TIMEOUT_MS,
|
|
53
|
-
}).catch(() => undefined);
|
|
54
|
-
if (unpacked === undefined || unpacked.exitCode !== 0)
|
|
55
|
-
return undefined;
|
|
56
|
-
const dir = path.join(root, 'package');
|
|
57
|
-
if (!fs.existsSync(dir))
|
|
58
|
-
return undefined;
|
|
59
|
-
return visit(dir);
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
63
|
-
}
|
|
64
|
-
}
|
package/dist/package-kind.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type PackageKind = 'runtime-plugin' | 'dsh-bundle' | 'both' | 'neither';
|
|
2
|
-
export interface PackageVerdict {
|
|
3
|
-
readonly kind: PackageKind;
|
|
4
|
-
readonly pkg?: string;
|
|
5
|
-
readonly pluginName?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface NpmRef {
|
|
8
|
-
readonly name: string;
|
|
9
|
-
readonly version?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare const OPENCLI_MANIFEST_FILE = "opencli-plugin.json";
|
|
12
|
-
export declare function splitNpmSpec(spec: string): NpmRef | undefined;
|
|
13
|
-
export declare function isCommandFile(name: string): boolean;
|
|
14
|
-
export declare function pluginManifestOf(dir: string): Readonly<Record<string, unknown>> | undefined;
|
|
15
|
-
export declare function hasCommandFile(dir: string): boolean;
|
|
16
|
-
export declare function pluginAt(dir: string): {
|
|
17
|
-
readonly name?: string;
|
|
18
|
-
} | undefined;
|
|
19
|
-
export declare function dshMarkersOf(raw: unknown): boolean;
|
|
20
|
-
export declare function verdictOf(dir: string): PackageVerdict | undefined;
|
|
21
|
-
export declare function refusalReason(): string;
|
package/dist/package-kind.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { isPackageName } from '@dsh-workbench/shared/host';
|
|
4
|
-
import { dshRecordOf, packageManifestOf, packageNameOf } from './registry-declaration.js';
|
|
5
|
-
export const OPENCLI_MANIFEST_FILE = 'opencli-plugin.json';
|
|
6
|
-
const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]*$/;
|
|
7
|
-
export function splitNpmSpec(spec) {
|
|
8
|
-
const value = spec.trim();
|
|
9
|
-
const at = value.lastIndexOf('@');
|
|
10
|
-
const name = at > 0 ? value.slice(0, at) : value;
|
|
11
|
-
const version = at > 0 ? value.slice(at + 1) : '';
|
|
12
|
-
if (!isPackageName(name))
|
|
13
|
-
return undefined;
|
|
14
|
-
if (version !== '' && !VERSION.test(version))
|
|
15
|
-
return undefined;
|
|
16
|
-
return { name, ...(version === '' ? {} : { version }) };
|
|
17
|
-
}
|
|
18
|
-
function asRecord(value) {
|
|
19
|
-
return typeof value === 'object' && value !== null ? value : undefined;
|
|
20
|
-
}
|
|
21
|
-
export function isCommandFile(name) {
|
|
22
|
-
if (name.endsWith('.d.ts') || name.endsWith('.test.ts'))
|
|
23
|
-
return false;
|
|
24
|
-
if (name.endsWith('.d.js'))
|
|
25
|
-
return false;
|
|
26
|
-
return name.endsWith('.ts') || name.endsWith('.js');
|
|
27
|
-
}
|
|
28
|
-
export function pluginManifestOf(dir) {
|
|
29
|
-
try {
|
|
30
|
-
const parsed = JSON.parse(fs.readFileSync(path.join(dir, OPENCLI_MANIFEST_FILE), 'utf8'));
|
|
31
|
-
if (Array.isArray(parsed))
|
|
32
|
-
return undefined;
|
|
33
|
-
return asRecord(parsed);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export function hasCommandFile(dir) {
|
|
40
|
-
try {
|
|
41
|
-
return fs.readdirSync(dir).some(isCommandFile);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
export function pluginAt(dir) {
|
|
48
|
-
const manifest = pluginManifestOf(dir);
|
|
49
|
-
if (manifest === undefined || !hasCommandFile(dir))
|
|
50
|
-
return undefined;
|
|
51
|
-
const name = manifest.name;
|
|
52
|
-
return typeof name === 'string' && name.trim() !== '' ? { name: name.trim() } : {};
|
|
53
|
-
}
|
|
54
|
-
export function dshMarkersOf(raw) {
|
|
55
|
-
const record = asRecord(raw);
|
|
56
|
-
return record !== undefined && ('bundle' in record || 'workbench' in record);
|
|
57
|
-
}
|
|
58
|
-
export function verdictOf(dir) {
|
|
59
|
-
const plugin = pluginAt(dir);
|
|
60
|
-
if (packageManifestOf(dir) === undefined && plugin === undefined)
|
|
61
|
-
return undefined;
|
|
62
|
-
const dsh = dshMarkersOf(dshRecordOf(dir));
|
|
63
|
-
const kind = plugin !== undefined && dsh ? 'both' : plugin !== undefined ? 'runtime-plugin' : dsh ? 'dsh-bundle' : 'neither';
|
|
64
|
-
const pkg = packageNameOf(dir);
|
|
65
|
-
return {
|
|
66
|
-
kind,
|
|
67
|
-
...(pkg === undefined ? {} : { pkg }),
|
|
68
|
-
...(plugin?.name === undefined ? {} : { pluginName: plugin.name }),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
export function refusalReason() {
|
|
72
|
-
return '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上';
|
|
73
|
-
}
|