@dsh-workbench/shared 1.1.7 → 1.1.9
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/dist/api/blocker-table.d.ts +1 -0
- package/dist/api/blocker-table.js +1 -1
- package/dist/components/SelectField.css +0 -6
- package/dist/components/SelectField.d.ts +2 -3
- package/dist/components/SelectField.js +9 -30
- package/dist/components/SelectFieldNodes.d.ts +3 -1
- package/dist/components/SelectFieldNodes.js +2 -1
- package/dist/components/SelectFieldWheel.d.ts +9 -0
- package/dist/components/SelectFieldWheel.js +35 -0
- package/dist/config/constants.d.ts +4 -4
- package/dist/config/constants.js +4 -4
- package/dist/contract/capabilities.d.ts +29 -0
- package/dist/contract/capabilities.js +1 -0
- package/dist/contract/needs.d.ts +3 -3
- package/dist/contract/needs.js +1 -1
- package/dist/contract/plugin-source.d.ts +19 -0
- package/dist/contract/protocol/bundle-install.d.ts +3 -1
- package/dist/contract/protocol/bundle-install.js +3 -0
- package/dist/contract/protocol/market-index.d.ts +1 -0
- package/dist/contract/protocol/platform.d.ts +1 -1
- package/dist/contract/runner.d.ts +6 -0
- package/dist/contract/runner.js +1 -0
- package/dist/contract/unit.d.ts +4 -4
- package/dist/host/adapter-contract.d.ts +3 -0
- package/dist/host/adapter-contract.js +93 -0
- package/dist/host/adapter-set.js +6 -4
- package/dist/host/adapter-types.d.ts +1 -1
- package/dist/host/bundle-manager.d.ts +3 -0
- package/dist/host/bundle-manager.js +181 -0
- package/dist/host/cli.js +3 -1
- package/dist/host/config-sync.d.ts +48 -0
- package/dist/host/config-sync.js +342 -0
- package/dist/host/dsh-plugins.d.ts +5 -0
- package/dist/host/dsh-plugins.js +51 -0
- package/dist/host/extension.js +1 -1
- package/dist/host/flow/check.d.ts +9 -0
- package/dist/host/flow/check.js +179 -0
- package/dist/host/flow/expr.d.ts +30 -0
- package/dist/host/flow/expr.js +99 -0
- package/dist/host/flow/kit.d.ts +14 -0
- package/dist/host/flow/kit.js +13 -0
- package/dist/host/flow/run.d.ts +13 -0
- package/dist/host/flow/run.js +287 -0
- package/dist/host/flow/types.d.ts +188 -0
- package/dist/host/flow/types.js +1 -0
- package/dist/host/host-actions.d.ts +2 -0
- package/dist/host/host-actions.js +5 -0
- package/dist/host/host-kit.d.ts +19 -0
- package/dist/host/host-kit.js +14 -0
- package/dist/host/http.d.ts +2 -6
- package/dist/host/http.js +2 -2
- package/dist/host/installed-module.d.ts +23 -0
- package/dist/host/installed-module.js +264 -0
- package/dist/host/lib-install.d.ts +7 -9
- package/dist/host/lib-install.js +9 -12
- package/dist/host/lib-runners.d.ts +3 -5
- package/dist/host/lib-runners.js +2 -2
- package/dist/host/local-declaration.d.ts +7 -0
- package/dist/host/local-declaration.js +33 -0
- package/dist/host/market-index-claim.d.ts +0 -1
- package/dist/host/market-index-claim.js +0 -3
- package/dist/host/market-index-doc.d.ts +1 -45
- package/dist/host/market-index-parse.d.ts +9 -4
- package/dist/host/market-index-parse.js +17 -92
- package/dist/host/market-index.d.ts +5 -6
- package/dist/host/market-index.js +29 -87
- package/dist/host/module-root.d.ts +1 -0
- package/dist/host/module-root.js +5 -0
- package/dist/host/npm-registry.d.ts +10 -0
- package/dist/host/npm-registry.js +16 -0
- package/dist/host/opencli-runner.d.ts +2 -1
- package/dist/host/package-content.d.ts +7 -0
- package/dist/host/package-content.js +69 -0
- package/dist/host/package-kind.d.ts +13 -0
- package/dist/host/package-kind.js +51 -0
- package/dist/host/package-route.d.ts +31 -0
- package/dist/host/package-route.js +57 -0
- package/dist/host/paths.d.ts +11 -19
- package/dist/host/paths.js +16 -43
- package/dist/host/platform.js +1 -1
- package/dist/host/plugin-ledger.d.ts +8 -8
- package/dist/host/plugin-ledger.js +23 -16
- package/dist/host/plugin.d.ts +1 -2
- package/dist/host/plugin.js +4 -5
- package/dist/host/port-types.d.ts +42 -9
- package/dist/host/preflight.d.ts +3 -2
- package/dist/host/profile-create.d.ts +18 -0
- package/dist/host/profile-create.js +118 -0
- package/dist/host/profile-registries.d.ts +10 -0
- package/dist/host/profile-registries.js +150 -0
- package/dist/host/registry-declaration.d.ts +21 -0
- package/dist/host/registry-declaration.js +33 -0
- package/dist/host/release-probe.d.ts +34 -0
- package/dist/host/release-probe.js +88 -0
- package/dist/host/runner.d.ts +26 -0
- package/dist/host/runner.js +50 -0
- package/dist/host/shell.d.ts +0 -2
- package/dist/host/shell.js +0 -7
- package/dist/host/spec.js +1 -1
- package/dist/host/supply-index.d.ts +1 -0
- package/dist/host/supply-index.js +8 -7
- package/dist/host/supply-port.js +13 -3
- package/dist/host/unit-face.d.ts +2 -1
- package/dist/host/version.js +11 -6
- package/dist/host.d.ts +24 -6
- package/dist/host.js +23 -6
- package/dist/i18n/en.js +19 -20
- package/dist/i18n/index.d.ts +20 -21
- package/dist/i18n/runtime-terms.d.ts +15 -0
- package/dist/i18n/runtime-terms.js +22 -0
- package/dist/i18n/zh.d.ts +19 -20
- package/dist/i18n/zh.js +19 -20
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/testing/fake-adapter-set.d.ts +5 -0
- package/dist/testing/fake-adapter-set.js +150 -0
- package/dist/testing/needs.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/api/__tests__/blocker-table.test.ts +25 -0
- package/src/api/{workbench-client.test.ts → __tests__/workbench-client.test.ts} +2 -2
- package/src/api/blocker-table.ts +6 -2
- package/src/components/SelectField.css +0 -6
- package/src/components/SelectField.tsx +7 -36
- package/src/components/SelectFieldNodes.tsx +4 -1
- package/src/components/{Card.test.tsx → __tests__/Card.test.tsx} +2 -2
- package/src/components/{CardAnchor.test.tsx → __tests__/CardAnchor.test.tsx} +3 -3
- package/src/components/{CardHead.test.tsx → __tests__/CardHead.test.tsx} +1 -1
- package/src/components/{ChoiceGroup.test.tsx → __tests__/ChoiceGroup.test.tsx} +2 -2
- package/src/components/{ConfirmBar.test.tsx → __tests__/ConfirmBar.test.tsx} +5 -5
- package/src/components/{Field.test.tsx → __tests__/Field.test.tsx} +1 -1
- package/src/components/{Form.test.tsx → __tests__/Form.test.tsx} +5 -5
- package/src/components/{FormActions.test.tsx → __tests__/FormActions.test.tsx} +3 -3
- package/src/components/{FormRows.test.tsx → __tests__/FormRows.test.tsx} +2 -2
- package/src/components/{FormTypes.test.ts → __tests__/FormTypes.test.ts} +1 -1
- package/src/components/{FormWiring.test.tsx → __tests__/FormWiring.test.tsx} +4 -4
- package/src/components/{Link.test.tsx → __tests__/Link.test.tsx} +1 -1
- package/src/components/{Rows.test.tsx → __tests__/Rows.test.tsx} +2 -2
- package/src/components/{SelectField.test.tsx → __tests__/SelectField.test.tsx} +7 -37
- package/src/components/{Spinner.test.tsx → __tests__/Spinner.test.tsx} +4 -4
- package/src/config/{constants.test.ts → __tests__/constants.test.ts} +1 -1
- package/src/config/{storage.test.ts → __tests__/storage.test.ts} +1 -1
- package/src/config/constants.ts +4 -4
- package/src/contract/capabilities.ts +30 -0
- package/src/contract/needs.ts +4 -4
- package/src/contract/plugin-source.ts +24 -0
- package/src/contract/protocol/bundle-install.ts +7 -1
- package/src/contract/protocol/market-index.ts +1 -0
- package/src/contract/protocol/platform.ts +1 -1
- package/src/contract/runner.ts +6 -0
- package/src/contract/unit.ts +4 -4
- package/src/host/{extension.test.ts → __tests__/extension.test.ts} +4 -4
- package/src/host/{install-failure.test.ts → __tests__/install-failure.test.ts} +1 -1
- package/src/host/{installed-sources.test.ts → __tests__/installed-sources.test.ts} +1 -1
- package/src/host/__tests__/lib-runners.test.ts +11 -2
- package/src/host/__tests__/market-index-assemble.test.ts +26 -0
- package/src/host/__tests__/market-index.test.ts +41 -51
- package/src/host/__tests__/plugin-ledger.test.ts +19 -6
- package/src/host/{recipe.test.ts → __tests__/recipe.test.ts} +3 -3
- package/src/host/__tests__/runtime-layout.test.ts +14 -0
- package/src/host/__tests__/supply-index.test.ts +2 -2
- package/src/host/__tests__/supply-port.test.ts +32 -0
- package/src/host/adapter-contract.ts +93 -0
- package/src/host/adapter-set.ts +6 -4
- package/src/host/adapter-types.ts +1 -1
- package/src/host/bundle-manager.ts +195 -0
- package/src/host/cli.ts +3 -1
- package/src/host/config-sync.ts +386 -0
- package/src/host/dsh-plugins.ts +60 -0
- package/src/host/extension.ts +1 -1
- package/src/host/flow/__tests__/advanced.test.ts +302 -0
- package/src/host/flow/__tests__/check.test.ts +169 -0
- package/src/host/flow/__tests__/run.test.ts +301 -0
- package/src/host/flow/check.ts +216 -0
- package/src/host/flow/expr.ts +94 -0
- package/src/host/flow/kit.ts +30 -0
- package/src/host/flow/run.ts +325 -0
- package/src/host/flow/types.ts +199 -0
- package/src/host/host-actions.ts +7 -0
- package/src/host/host-kit.ts +26 -0
- package/src/host/http.ts +6 -8
- package/src/host/installed-module.ts +272 -0
- package/src/host/lib-install.ts +14 -12
- package/src/host/lib-runners.ts +6 -8
- package/src/host/local-declaration.ts +37 -0
- package/src/host/market-index-claim.ts +0 -4
- package/src/host/market-index-doc.ts +1 -53
- package/src/host/market-index-parse.ts +21 -85
- package/src/host/market-index.ts +33 -127
- package/src/host/module-root.ts +6 -0
- package/src/host/npm-registry.ts +21 -0
- package/src/host/package-content.ts +76 -0
- package/src/host/package-kind.ts +63 -0
- package/src/host/package-route.ts +93 -0
- package/src/host/paths.ts +27 -52
- package/src/host/platform.ts +1 -1
- package/src/host/plugin-ledger.ts +25 -18
- package/src/host/plugin.ts +4 -5
- package/src/host/port-types.ts +43 -9
- package/src/host/preflight.ts +3 -2
- package/src/host/profile-create.ts +133 -0
- package/src/host/profile-registries.ts +145 -0
- package/src/host/registry-declaration.ts +70 -0
- package/src/host/release-probe.ts +202 -0
- package/src/host/shell.ts +0 -9
- package/src/host/spec.ts +1 -1
- package/src/host/supply-index.ts +9 -7
- package/src/host/supply-port.ts +13 -3
- package/src/host/unit-face.ts +2 -1
- package/src/host/version.ts +12 -7
- package/src/host.ts +34 -6
- package/src/i18n/{index.test.ts → __tests__/index.test.ts} +3 -3
- package/src/i18n/{merge.test.ts → __tests__/merge.test.ts} +1 -1
- package/src/i18n/{zh.test.ts → __tests__/zh.test.ts} +2 -2
- package/src/i18n/en.ts +19 -21
- package/src/i18n/runtime-terms.ts +26 -0
- package/src/i18n/zh.ts +19 -21
- package/src/index.ts +4 -0
- package/src/lib/{board-fact.test.ts → __tests__/board-fact.test.ts} +1 -1
- package/src/lib/{busy.test.ts → __tests__/busy.test.ts} +1 -1
- package/src/lib/{form-problem.test.ts → __tests__/form-problem.test.ts} +1 -1
- package/src/lib/{git-address.test.ts → __tests__/git-address.test.ts} +1 -1
- package/src/lib/{react.test.ts → __tests__/react.test.ts} +1 -1
- package/src/lib/{text.test.ts → __tests__/text.test.ts} +1 -1
- package/src/lib/{ui.test.ts → __tests__/ui.test.ts} +1 -1
- package/src/testing/__tests__/fake-adapter-set.test.ts +28 -0
- package/src/testing/fake-adapter-set.ts +173 -0
- package/src/testing/needs.ts +1 -1
- package/src/testing.ts +1 -0
- package/src/host/__tests__/command-name.test.ts +0 -16
- package/src/host/__tests__/opencli-runner.test.ts +0 -120
- package/src/host/channel-plugin.ts +0 -55
- package/src/host/command-name.ts +0 -15
- package/src/host/opencli-runner.ts +0 -76
- package/src/host/paths.test.ts +0 -26
- package/src/host/plugin-source.ts +0 -137
- package/src/host/runtime-links.test.ts +0 -31
- package/src/host/runtime-links.ts +0 -23
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { localDirOf } from './spec.js';
|
|
4
|
+
export { localDirOf };
|
|
5
|
+
import { declarationProblemsOf } from '../contract/unit-declaration.js';
|
|
6
|
+
function asRecord(value) {
|
|
7
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function packageManifestOf(dir) {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
12
|
+
return asRecord(parsed);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function dshRecordOf(dir) {
|
|
19
|
+
return packageManifestOf(dir)?.dsh;
|
|
20
|
+
}
|
|
21
|
+
export function packageNameOf(dir) {
|
|
22
|
+
const name = packageManifestOf(dir)?.name;
|
|
23
|
+
return typeof name === 'string' && name.trim() !== '' ? name.trim() : undefined;
|
|
24
|
+
}
|
|
25
|
+
export function pathDeclarationOf(spec) {
|
|
26
|
+
const dir = localDirOf(spec);
|
|
27
|
+
return dir === undefined ? undefined : asRecord(dshRecordOf(dir))?.workbench;
|
|
28
|
+
}
|
|
29
|
+
export function declarationRefusalOf(name, raw) {
|
|
30
|
+
if (raw === undefined)
|
|
31
|
+
return [];
|
|
32
|
+
return declarationProblemsOf(name, raw);
|
|
33
|
+
}
|
|
@@ -15,6 +15,5 @@ export interface MarketClaimInput {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function marketArtifactPath(name: string): string;
|
|
17
17
|
export declare function marketArtifactRoot(name: string): string;
|
|
18
|
-
export declare function marketArtifactPathFor(name: string, version: string): string;
|
|
19
18
|
export declare function entryUnitOf(entry: MarketIndexEntry): string | undefined;
|
|
20
19
|
export declare function claimOf(index: MarketIndex, input: MarketClaimInput): MarketClaim;
|
|
@@ -45,9 +45,6 @@ export function marketArtifactPath(name) {
|
|
|
45
45
|
export function marketArtifactRoot(name) {
|
|
46
46
|
return `${MARKET_ARTIFACTS_DIR}/${name}`;
|
|
47
47
|
}
|
|
48
|
-
export function marketArtifactPathFor(name, version) {
|
|
49
|
-
return `${MARKET_ARTIFACTS_DIR}/${name}/${version}`;
|
|
50
|
-
}
|
|
51
48
|
export function entryUnitOf(entry) {
|
|
52
49
|
const unit = entry.metadata?.unit?.trim();
|
|
53
50
|
if (unit !== undefined && unit !== '')
|
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
import type { MarketIndex,
|
|
2
|
-
export interface MarketVersionDoc {
|
|
3
|
-
readonly name: string;
|
|
4
|
-
readonly version: string;
|
|
5
|
-
readonly source: MarketIndexSource;
|
|
6
|
-
readonly publishTimestamp?: number;
|
|
7
|
-
readonly engines?: MarketIndexEngines;
|
|
8
|
-
readonly title?: MarketLocalizedText;
|
|
9
|
-
readonly description?: MarketLocalizedText;
|
|
10
|
-
readonly author?: string;
|
|
11
|
-
readonly owner?: string;
|
|
12
|
-
readonly homepage?: string;
|
|
13
|
-
readonly icon?: string;
|
|
14
|
-
readonly metadata?: MarketIndexEntryMetadata;
|
|
15
|
-
}
|
|
16
|
-
export interface MarketMetaDoc {
|
|
17
|
-
readonly name: string;
|
|
18
|
-
readonly epoch?: number;
|
|
19
|
-
readonly publishTimestamp: number;
|
|
20
|
-
readonly title?: MarketLocalizedText;
|
|
21
|
-
readonly description?: MarketLocalizedText;
|
|
22
|
-
readonly author?: string;
|
|
23
|
-
readonly owner?: string;
|
|
24
|
-
readonly homepage?: string;
|
|
25
|
-
readonly icon?: string;
|
|
26
|
-
readonly metadata?: MarketIndexEntryMetadata;
|
|
27
|
-
}
|
|
28
|
-
export type MarketEntryDoc = MarketVersionDoc | MarketMetaDoc;
|
|
29
|
-
export type MarketEntryDocParse = {
|
|
30
|
-
readonly ok: true;
|
|
31
|
-
readonly doc: MarketEntryDoc;
|
|
32
|
-
} | {
|
|
33
|
-
readonly ok: false;
|
|
34
|
-
readonly problems: readonly MarketIndexProblem[];
|
|
35
|
-
};
|
|
1
|
+
import type { MarketIndex, MarketIndexProblem } from '../contract/protocol/market-index.js';
|
|
36
2
|
export interface MarketEntryFile {
|
|
37
3
|
readonly path: string;
|
|
38
4
|
readonly text: string;
|
|
@@ -44,13 +10,3 @@ export type MarketIndexAssemble = {
|
|
|
44
10
|
readonly ok: false;
|
|
45
11
|
readonly problems: readonly MarketIndexProblem[];
|
|
46
12
|
};
|
|
47
|
-
export interface MarketDocFields {
|
|
48
|
-
readonly name: string;
|
|
49
|
-
readonly title?: MarketLocalizedText;
|
|
50
|
-
readonly description?: MarketLocalizedText;
|
|
51
|
-
readonly author?: string;
|
|
52
|
-
readonly owner?: string;
|
|
53
|
-
readonly homepage?: string;
|
|
54
|
-
readonly icon?: string;
|
|
55
|
-
readonly metadata?: MarketIndexEntryMetadata;
|
|
56
|
-
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { DiscoverFilter, IndexSourceSpec, MarketIndex, MarketIndexEntry, MarketIndexParse, MarketIndexProblem, MarketIndexVersion, MarketSourceConfig } from '../contract/protocol/market-index.js';
|
|
2
|
-
import type { MarketEntryDocParse } from './market-index-doc.js';
|
|
3
2
|
export declare function compareSemver(left: string, right: string): number | undefined;
|
|
4
3
|
export declare function httpUrlProblem(url: string): string | undefined;
|
|
5
4
|
export declare function gitUrlOk(url: string): boolean;
|
|
@@ -18,6 +17,12 @@ export type SourceConfigParse = {
|
|
|
18
17
|
readonly problem: MarketIndexProblem;
|
|
19
18
|
};
|
|
20
19
|
export declare function parseSourceConfig(raw: unknown): SourceConfigParse;
|
|
21
|
-
export declare function
|
|
22
|
-
export
|
|
23
|
-
|
|
20
|
+
export declare function marketEntryPath(dir: string, name: string): string;
|
|
21
|
+
export type MarketEntryParse = {
|
|
22
|
+
readonly ok: true;
|
|
23
|
+
readonly entry: MarketIndexEntry;
|
|
24
|
+
} | {
|
|
25
|
+
readonly ok: false;
|
|
26
|
+
readonly problems: readonly MarketIndexProblem[];
|
|
27
|
+
};
|
|
28
|
+
export declare function parseMarketEntryJson(text: string): MarketEntryParse;
|
|
@@ -384,102 +384,27 @@ export function parseSourceConfig(raw) {
|
|
|
384
384
|
},
|
|
385
385
|
};
|
|
386
386
|
}
|
|
387
|
-
function docFieldsOf(record, at) {
|
|
388
|
-
const name = asText(record['name']);
|
|
389
|
-
if (name === '' || name.length > 214 || !NPM_NAME.test(name)) {
|
|
390
|
-
return problem('name', `不是合法 npm 包名:${JSON.stringify(name)}`, at);
|
|
391
|
-
}
|
|
392
|
-
const icon = asText(record['icon']);
|
|
393
|
-
if (icon !== '') {
|
|
394
|
-
const badIcon = iconProblem(icon);
|
|
395
|
-
if (badIcon !== undefined)
|
|
396
|
-
return badIcon;
|
|
397
|
-
}
|
|
398
|
-
const owner = asText(record['owner']);
|
|
399
|
-
if (owner !== '') {
|
|
400
|
-
const badOwner = ownerProblem(owner);
|
|
401
|
-
if (badOwner !== undefined)
|
|
402
|
-
return badOwner;
|
|
403
|
-
}
|
|
404
|
-
const metadata = metadataOf(record['metadata']);
|
|
405
|
-
if (metadata?.unit !== undefined && !UNIT_ID.test(metadata.unit)) {
|
|
406
|
-
return problem('unit', `metadata.unit 只能是小写字母开头的 a-z0-9-(现在是 ${JSON.stringify(metadata.unit)})`, at);
|
|
407
|
-
}
|
|
408
|
-
const title = asLocalized(record['title']);
|
|
409
|
-
const description = asLocalized(record['description']);
|
|
410
|
-
const author = asText(record['author']);
|
|
411
|
-
const homepage = asText(record['homepage']);
|
|
412
|
-
return {
|
|
413
|
-
name,
|
|
414
|
-
...(title === undefined ? {} : { title }),
|
|
415
|
-
...(description === undefined ? {} : { description }),
|
|
416
|
-
...(author === '' ? {} : { author }),
|
|
417
|
-
...(owner === '' ? {} : { owner }),
|
|
418
|
-
...(homepage === '' ? {} : { homepage }),
|
|
419
|
-
...(icon === '' ? {} : { icon }),
|
|
420
|
-
...(metadata === undefined ? {} : { metadata }),
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
export function parseMarketEntryDoc(raw) {
|
|
424
|
-
const record = asRecord(raw);
|
|
425
|
-
if (record === undefined)
|
|
426
|
-
return { ok: false, problems: [problem('entry', '条目文件必须是一个 JSON 对象')] };
|
|
427
|
-
const fields = docFieldsOf(record, 'entry');
|
|
428
|
-
if ('kind' in fields)
|
|
429
|
-
return { ok: false, problems: [fields] };
|
|
430
|
-
const version = asText(record['version']);
|
|
431
|
-
const timestamp = record['publishTimestamp'];
|
|
432
|
-
if (timestamp !== undefined && (!Number.isInteger(timestamp) || timestamp < 0)) {
|
|
433
|
-
return { ok: false, problems: [problem('timestamp', 'publishTimestamp 必须是 Unix 秒整数', 'entry')] };
|
|
434
|
-
}
|
|
435
|
-
if (version === '') {
|
|
436
|
-
if (timestamp === undefined) {
|
|
437
|
-
return { ok: false, problems: [problem('timestamp', '元数据文件要有 publishTimestamp', 'entry')] };
|
|
438
|
-
}
|
|
439
|
-
const epoch = record['epoch'];
|
|
440
|
-
if (epoch !== undefined && (!Number.isInteger(epoch) || epoch < 0)) {
|
|
441
|
-
return { ok: false, problems: [problem('timestamp', 'epoch 必须是非负整数', 'entry')] };
|
|
442
|
-
}
|
|
443
|
-
return {
|
|
444
|
-
ok: true,
|
|
445
|
-
doc: { ...fields, publishTimestamp: timestamp, ...(epoch === undefined ? {} : { epoch: epoch }) },
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
if (!isValidVersion(version)) {
|
|
449
|
-
return { ok: false, problems: [problem('version', `版本号不是合法 SemVer:${JSON.stringify(version)}`, 'entry')] };
|
|
450
|
-
}
|
|
451
|
-
const source = sourceOf(record['source'], 'entry.source');
|
|
452
|
-
if ('kind' in source)
|
|
453
|
-
return { ok: false, problems: [source] };
|
|
454
|
-
const engines = asRecord(record['engines']);
|
|
455
|
-
const dsh = engines === undefined ? '' : asText(engines['dsh']);
|
|
456
|
-
return {
|
|
457
|
-
ok: true,
|
|
458
|
-
doc: {
|
|
459
|
-
...fields,
|
|
460
|
-
version,
|
|
461
|
-
source,
|
|
462
|
-
...(timestamp === undefined ? {} : { publishTimestamp: timestamp }),
|
|
463
|
-
...(dsh === '' ? {} : { engines: { dsh } }),
|
|
464
|
-
},
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
387
|
function indexPathBase(dir) {
|
|
468
388
|
return dir.replace(/^\/+|\/+$/g, '') || MARKET_INDEX_DIR;
|
|
469
389
|
}
|
|
470
|
-
export function marketEntryPath(dir, name
|
|
390
|
+
export function marketEntryPath(dir, name) {
|
|
471
391
|
if (!NPM_NAME.test(name))
|
|
472
392
|
throw new Error(`不是合法包名:${JSON.stringify(name)}`);
|
|
473
|
-
|
|
474
|
-
throw new Error(`不是合法版本号:${JSON.stringify(version)}`);
|
|
475
|
-
return `${indexPathBase(dir)}/${name}/${version}.json`;
|
|
393
|
+
return `${indexPathBase(dir)}/${name}.json`;
|
|
476
394
|
}
|
|
477
|
-
export function
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
395
|
+
export function parseMarketEntryJson(text) {
|
|
396
|
+
let raw;
|
|
397
|
+
try {
|
|
398
|
+
raw = JSON.parse(text);
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
return {
|
|
402
|
+
ok: false,
|
|
403
|
+
problems: [problem('not-json', `JSON 解析失败:${error instanceof Error ? error.message : String(error)}`, 'entry')],
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const parsed = entryOf(raw, 'entry');
|
|
407
|
+
if ('kind' in parsed)
|
|
408
|
+
return { ok: false, problems: [parsed] };
|
|
409
|
+
return { ok: true, entry: parsed };
|
|
485
410
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { MarketIndex, MarketIndexEntry
|
|
2
|
-
import type { MarketEntryFile, MarketIndexAssemble
|
|
1
|
+
import type { MarketIndex, MarketIndexEntry } from '../contract/protocol/market-index.js';
|
|
2
|
+
import type { MarketEntryFile, MarketIndexAssemble } from './market-index-doc.js';
|
|
3
3
|
export declare const MARKET_ENTRIES_README_LINES: readonly string[];
|
|
4
4
|
export declare const MARKET_INDEX_README_LINES: readonly string[];
|
|
5
5
|
export { marketBranchOk, marketInstallTarget, marketText } from '../lib/market-index.js';
|
|
6
|
-
export type {
|
|
7
|
-
export { compareSemver, filterMarketIndex, gitUrlOk, httpUrlProblem, marketEntryLabels, marketEntryPath,
|
|
6
|
+
export type { MarketEntryFile, MarketIndexAssemble } from './market-index-doc.js';
|
|
7
|
+
export { compareSemver, filterMarketIndex, gitUrlOk, httpUrlProblem, marketEntryLabels, marketEntryPath, marketSourceKey, parseMarketEntryJson, parseMarketIndex, parseMarketIndexJson, parseSourceConfig, sortMarketVersions, upsertMarketEntry, } from './market-index-parse.js';
|
|
8
8
|
export type { SourceConfigParse } from './market-index-parse.js';
|
|
9
|
-
export declare function marketVersionDocOf(entry: MarketIndexEntry, version: MarketIndexVersion): MarketVersionDoc;
|
|
10
|
-
export declare function marketMetaDocOf(entry: MarketIndexEntry, epoch: number, at: number): MarketMetaDoc;
|
|
11
9
|
export declare function assembleMarketIndex(files: readonly MarketEntryFile[]): MarketIndexAssemble;
|
|
10
|
+
export declare function marketEntryFileOf(entry: MarketIndexEntry, dir?: string): MarketEntryFile;
|
|
12
11
|
export declare function marketEntryFilesOf(index: MarketIndex, dir?: string): readonly MarketEntryFile[];
|
|
13
12
|
export declare function collectMarketEntryFiles(dir: string): readonly MarketEntryFile[];
|
|
14
13
|
export declare function marketEntryFilesOfLegacy(text: string, dir?: string): readonly MarketEntryFile[] | undefined;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join, relative, sep } from 'node:path';
|
|
3
3
|
import { MARKET_INDEX_DIR, MARKET_INDEX_FORMAT } from '../contract/protocol/market-index.js';
|
|
4
|
-
import {
|
|
4
|
+
import { entryUnitOf } from './market-index-claim.js';
|
|
5
|
+
import { marketEntryPath, parseMarketEntryJson, parseMarketIndexJson, sortMarketVersions } from './market-index-parse.js';
|
|
5
6
|
export const MARKET_ENTRIES_README_LINES = [
|
|
6
7
|
'# 索引目录',
|
|
7
8
|
'',
|
|
8
|
-
'
|
|
9
|
+
'一个包一个文件:`market/<包名>.json`,里面是这一个包的元数据与所有版本;',
|
|
9
10
|
'',
|
|
10
|
-
'`
|
|
11
|
+
'产物在 `artifacts/<包名>/`(只放最新一版,老版本按条目里的 commit 取历史)。',
|
|
12
|
+
'',
|
|
13
|
+
'发布时只改自己包的文件;包与包之间不共享文件。',
|
|
11
14
|
];
|
|
12
15
|
export const MARKET_INDEX_README_LINES = [
|
|
13
16
|
'# 模块市场索引',
|
|
14
17
|
'',
|
|
15
|
-
'这个仓库给 DeepSeek Harness
|
|
18
|
+
'这个仓库给 DeepSeek Harness 工作台当模块市场索引:一个包一个文件 `market/<包名>.json`,',
|
|
19
|
+
'产物在 `artifacts/<包名>/`;老仓库的 `market.json` 会在第一次发布时自动拆开。',
|
|
16
20
|
'',
|
|
17
21
|
'## 发布模块',
|
|
18
22
|
'',
|
|
@@ -22,8 +26,7 @@ export const MARKET_INDEX_README_LINES = [
|
|
|
22
26
|
'dsh-wb-module release --index <本仓库地址> --index-policy artifacts-in-repo',
|
|
23
27
|
'```',
|
|
24
28
|
'',
|
|
25
|
-
'
|
|
26
|
-
'发布只新增文件、不改已有文件,两台机器同时发也不会冲突;老条目的 `packages/<包名>/` 路径继续可用。',
|
|
29
|
+
'发布只会改自己包的那一个文件;包与包之间不会碰同一个文件。',
|
|
27
30
|
'',
|
|
28
31
|
'一个包名归第一次发布的人(条目文件里的 `owner`)。换一个署名发同名会被拒;要换人,先把这一条的 `owner` 改成新的人。',
|
|
29
32
|
'同一个市场里,一个模块 id(`metadata.unit`)只能被一个条目使用;另一个包名想用同一个 id / 产物目录也会被拒。',
|
|
@@ -34,107 +37,46 @@ export const MARKET_INDEX_README_LINES = [
|
|
|
34
37
|
'',
|
|
35
38
|
];
|
|
36
39
|
export { marketBranchOk, marketInstallTarget, marketText } from '../lib/market-index.js';
|
|
37
|
-
export { compareSemver, filterMarketIndex, gitUrlOk, httpUrlProblem, marketEntryLabels, marketEntryPath,
|
|
40
|
+
export { compareSemver, filterMarketIndex, gitUrlOk, httpUrlProblem, marketEntryLabels, marketEntryPath, marketSourceKey, parseMarketEntryJson, parseMarketIndex, parseMarketIndexJson, parseSourceConfig, sortMarketVersions, upsertMarketEntry, } from './market-index-parse.js';
|
|
38
41
|
function problem(kind, detail, at) {
|
|
39
42
|
return { kind, detail, ...(at === undefined ? {} : { at }) };
|
|
40
43
|
}
|
|
41
|
-
function docFieldsFromEntry(entry) {
|
|
42
|
-
return {
|
|
43
|
-
name: entry.name,
|
|
44
|
-
...(entry.title === undefined ? {} : { title: entry.title }),
|
|
45
|
-
...(entry.description === undefined ? {} : { description: entry.description }),
|
|
46
|
-
...(entry.author === undefined ? {} : { author: entry.author }),
|
|
47
|
-
...(entry.owner === undefined ? {} : { owner: entry.owner }),
|
|
48
|
-
...(entry.homepage === undefined ? {} : { homepage: entry.homepage }),
|
|
49
|
-
...(entry.icon === undefined ? {} : { icon: entry.icon }),
|
|
50
|
-
...(entry.metadata === undefined ? {} : { metadata: entry.metadata }),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export function marketVersionDocOf(entry, version) {
|
|
54
|
-
return { ...docFieldsFromEntry(entry), ...version };
|
|
55
|
-
}
|
|
56
|
-
export function marketMetaDocOf(entry, epoch, at) {
|
|
57
|
-
return { ...docFieldsFromEntry(entry), epoch, publishTimestamp: at };
|
|
58
|
-
}
|
|
59
44
|
export function assembleMarketIndex(files) {
|
|
60
45
|
const problems = [];
|
|
61
|
-
const
|
|
46
|
+
const entries = new Map();
|
|
62
47
|
const ordered = [...files].sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
63
48
|
for (const file of ordered) {
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
raw = JSON.parse(file.text);
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
problems.push(problem('not-json', `JSON 解析失败:${error instanceof Error ? error.message : String(error)}`, file.path));
|
|
49
|
+
if (!file.path.endsWith('.json'))
|
|
70
50
|
continue;
|
|
71
|
-
|
|
72
|
-
const parsed = parseMarketEntryDoc(raw);
|
|
51
|
+
const parsed = parseMarketEntryJson(file.text);
|
|
73
52
|
if (!parsed.ok) {
|
|
74
53
|
for (const one of parsed.problems)
|
|
75
54
|
problems.push({ ...one, at: file.path });
|
|
76
55
|
continue;
|
|
77
56
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (group.versions.has(doc.version)) {
|
|
83
|
-
problems.push(problem('duplicate-version', `版本重复:${doc.version}`, file.path));
|
|
84
|
-
}
|
|
85
|
-
group.versions.set(doc.version, {
|
|
86
|
-
version: doc.version,
|
|
87
|
-
source: doc.source,
|
|
88
|
-
...(doc.publishTimestamp === undefined ? {} : { publishTimestamp: doc.publishTimestamp }),
|
|
89
|
-
...(doc.engines === undefined ? {} : { engines: doc.engines }),
|
|
90
|
-
});
|
|
91
|
-
const newerFallback = group.fallback === undefined ||
|
|
92
|
-
at > group.fallback.at ||
|
|
93
|
-
(at === group.fallback.at && file.path > group.fallback.path);
|
|
94
|
-
if (newerFallback)
|
|
95
|
-
group.fallback = { at, path: file.path, doc };
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
const epoch = doc.epoch ?? 0;
|
|
99
|
-
const newer = group.meta === undefined ||
|
|
100
|
-
epoch > group.meta.epoch ||
|
|
101
|
-
(epoch === group.meta.epoch && at > group.meta.at) ||
|
|
102
|
-
(epoch === group.meta.epoch && at === group.meta.at && file.path > group.meta.path);
|
|
103
|
-
if (newer)
|
|
104
|
-
group.meta = { epoch, at, path: file.path, doc };
|
|
57
|
+
const entry = parsed.entry;
|
|
58
|
+
if (entries.has(entry.name)) {
|
|
59
|
+
problems.push(problem('duplicate-name', `插件名重复:${entry.name}`, file.path));
|
|
60
|
+
continue;
|
|
105
61
|
}
|
|
106
|
-
|
|
62
|
+
entries.set(entry.name, { ...entry, versions: sortMarketVersions(entry.versions) });
|
|
107
63
|
}
|
|
108
64
|
if (problems.length > 0)
|
|
109
65
|
return { ok: false, problems };
|
|
110
|
-
const plugins = [...
|
|
111
|
-
const meta = group.meta?.doc ?? group.fallback?.doc;
|
|
112
|
-
return {
|
|
113
|
-
name,
|
|
114
|
-
versions: sortMarketVersions([...group.versions.values()]),
|
|
115
|
-
...(meta?.title === undefined ? {} : { title: meta.title }),
|
|
116
|
-
...(meta?.description === undefined ? {} : { description: meta.description }),
|
|
117
|
-
...(meta?.author === undefined ? {} : { author: meta.author }),
|
|
118
|
-
...(meta?.owner === undefined ? {} : { owner: meta.owner }),
|
|
119
|
-
...(meta?.homepage === undefined ? {} : { homepage: meta.homepage }),
|
|
120
|
-
...(meta?.icon === undefined ? {} : { icon: meta.icon }),
|
|
121
|
-
...(meta?.metadata === undefined ? {} : { metadata: meta.metadata }),
|
|
122
|
-
};
|
|
123
|
-
});
|
|
124
|
-
plugins.sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
|
|
66
|
+
const plugins = [...entries.values()].sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
|
|
125
67
|
return { ok: true, index: { format: MARKET_INDEX_FORMAT, plugins } };
|
|
126
68
|
}
|
|
69
|
+
export function marketEntryFileOf(entry, dir = MARKET_INDEX_DIR) {
|
|
70
|
+
return { path: marketEntryPath(dir, entry.name), text: `${JSON.stringify(entry, null, 2)}\n` };
|
|
71
|
+
}
|
|
127
72
|
export function marketEntryFilesOf(index, dir = MARKET_INDEX_DIR) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return files;
|
|
73
|
+
return index.plugins.map((entry) => marketEntryFileOf(healedUnit(entry), dir));
|
|
74
|
+
}
|
|
75
|
+
function healedUnit(entry) {
|
|
76
|
+
if (entry.metadata?.unit !== undefined)
|
|
77
|
+
return entry;
|
|
78
|
+
const unit = entryUnitOf(entry);
|
|
79
|
+
return unit === undefined ? entry : { ...entry, metadata: { ...entry.metadata, unit } };
|
|
138
80
|
}
|
|
139
81
|
export function collectMarketEntryFiles(dir) {
|
|
140
82
|
if (!existsSync(dir))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function packageRootOf(moduleUrl: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function npmRegistryOf(source: {
|
|
2
|
+
readonly registryEnv?: string;
|
|
3
|
+
readonly registryDefault?: string;
|
|
4
|
+
}, env: Readonly<Record<string, string | undefined>>): {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly value: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly ok: false;
|
|
9
|
+
readonly env: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function envValue(env, name) {
|
|
2
|
+
const value = env[name]?.trim();
|
|
3
|
+
return value === undefined || value === '' ? { ok: false } : { ok: true, value };
|
|
4
|
+
}
|
|
5
|
+
export function npmRegistryOf(source, env) {
|
|
6
|
+
const name = source.registryEnv ?? '';
|
|
7
|
+
if (name !== '') {
|
|
8
|
+
const fromEnv = envValue(env, name);
|
|
9
|
+
if (fromEnv.ok)
|
|
10
|
+
return fromEnv;
|
|
11
|
+
}
|
|
12
|
+
const fallback = source.registryDefault?.trim();
|
|
13
|
+
if (fallback === undefined || fallback === '')
|
|
14
|
+
return { ok: false, env: name };
|
|
15
|
+
return { ok: true, value: fallback };
|
|
16
|
+
}
|
|
@@ -2,9 +2,10 @@ import type { DataRequest, DataResult } from '../contract/data.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* 一条 opencli 命令的执行器:同一形状的 `execFile('opencli', args)`,结果只留 stdout。
|
|
4
4
|
*/
|
|
5
|
-
export type
|
|
5
|
+
export type LibRunner = (args: readonly string[]) => Promise<{
|
|
6
6
|
readonly stdout: string;
|
|
7
7
|
}>;
|
|
8
|
+
export type OpencliRunner = LibRunner;
|
|
8
9
|
/**
|
|
9
10
|
* 读缝(`DataReader.read`)的形状;模块直接把自己的 `data.read` 传进来即可。
|
|
10
11
|
*/
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DshShell } from '../api/dsh.js';
|
|
2
|
+
export declare function withPackageDir<T>(options: {
|
|
3
|
+
readonly shell?: DshShell | undefined;
|
|
4
|
+
readonly spec: string;
|
|
5
|
+
readonly registry?: string | undefined;
|
|
6
|
+
readonly cacheDir?: string | undefined;
|
|
7
|
+
}, visit: (dir: string) => T): Promise<T | undefined>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { INSTALL_TIMEOUT_MS } from '../config/constants.js';
|
|
5
|
+
import { parseJsonLoose } from '../lib/text.js';
|
|
6
|
+
import { quoteShellArg } from './shell-text.js';
|
|
7
|
+
import { runShell } from './shell.js';
|
|
8
|
+
import { splitNpmSpec } from './package-kind.js';
|
|
9
|
+
import { localDirOf } from './local-declaration.js';
|
|
10
|
+
function cacheFlag(cacheDir) {
|
|
11
|
+
return cacheDir === undefined ? '' : ` --cache ${quoteShellArg(cacheDir)}`;
|
|
12
|
+
}
|
|
13
|
+
function packedFileFrom(stdout, root) {
|
|
14
|
+
const parsed = parseJsonLoose(stdout);
|
|
15
|
+
if (Array.isArray(parsed)) {
|
|
16
|
+
const first = parsed[0];
|
|
17
|
+
const filename = typeof first === 'object' && first !== null ? first.filename : undefined;
|
|
18
|
+
if (typeof filename === 'string' && filename.trim() !== '') {
|
|
19
|
+
const at = path.join(root, path.basename(filename.trim()));
|
|
20
|
+
if (fs.existsSync(at))
|
|
21
|
+
return at;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const found = fs.readdirSync(root).filter((name) => name.endsWith('.tgz'));
|
|
26
|
+
return found.length === 1 ? path.join(root, found[0] ?? '') : undefined;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function withPackageDir(options, visit) {
|
|
33
|
+
const local = localDirOf(options.spec);
|
|
34
|
+
if (local !== undefined)
|
|
35
|
+
return visit(local);
|
|
36
|
+
if (splitNpmSpec(options.spec) === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
const shell = options.shell;
|
|
39
|
+
if (shell === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
let root;
|
|
42
|
+
try {
|
|
43
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'wb-package-'));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const at = options.registry === undefined ? '' : ` --registry ${quoteShellArg(options.registry)}`;
|
|
50
|
+
const packed = await runShell(shell, `npm pack ${quoteShellArg(options.spec)} --ignore-scripts --json --pack-destination ${quoteShellArg(root)}${cacheFlag(options.cacheDir)}${at}`, { timeoutMs: INSTALL_TIMEOUT_MS }).catch(() => undefined);
|
|
51
|
+
if (packed === undefined || packed.exitCode !== 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const file = packedFileFrom(packed.stdout, root);
|
|
54
|
+
if (file === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
const unpacked = await runShell(shell, `tar -xzf ${quoteShellArg(file)} -C ${quoteShellArg(root)}`, {
|
|
57
|
+
timeoutMs: INSTALL_TIMEOUT_MS,
|
|
58
|
+
}).catch(() => undefined);
|
|
59
|
+
if (unpacked === undefined || unpacked.exitCode !== 0)
|
|
60
|
+
return undefined;
|
|
61
|
+
const dir = path.join(root, 'package');
|
|
62
|
+
if (!fs.existsSync(dir))
|
|
63
|
+
return undefined;
|
|
64
|
+
return visit(dir);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { splitNpmSpec } from './spec.js';
|
|
2
|
+
export type { NpmRef } from './spec.js';
|
|
3
|
+
export type PackageKind = 'runtime-plugin' | 'dsh-bundle' | 'both' | 'neither';
|
|
4
|
+
export interface PackageVerdict {
|
|
5
|
+
readonly kind: PackageKind;
|
|
6
|
+
readonly pkg?: string;
|
|
7
|
+
readonly pluginName?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function isCommandFile(name: string): boolean;
|
|
10
|
+
export declare function pluginManifestOf(dir: string, manifestFile: string): Readonly<Record<string, unknown>> | undefined;
|
|
11
|
+
export declare function hasCommandFile(dir: string): boolean;
|
|
12
|
+
export declare function dshMarkersOf(raw: unknown): boolean;
|
|
13
|
+
export declare function verdictOf(dir: string, manifestFile: string): PackageVerdict | undefined;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export { splitNpmSpec } from './spec.js';
|
|
4
|
+
import { dshRecordOf, packageManifestOf, packageNameOf } from './local-declaration.js';
|
|
5
|
+
import { pluginAt } from './plugin.js';
|
|
6
|
+
function asRecord(value) {
|
|
7
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function isCommandFile(name) {
|
|
10
|
+
if (name.endsWith('.d.ts') || name.endsWith('.test.ts'))
|
|
11
|
+
return false;
|
|
12
|
+
if (name.endsWith('.d.js'))
|
|
13
|
+
return false;
|
|
14
|
+
return name.endsWith('.ts') || name.endsWith('.js');
|
|
15
|
+
}
|
|
16
|
+
export function pluginManifestOf(dir, manifestFile) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, manifestFile), 'utf8'));
|
|
19
|
+
if (Array.isArray(parsed))
|
|
20
|
+
return undefined;
|
|
21
|
+
return asRecord(parsed);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function hasCommandFile(dir) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.readdirSync(dir).some(isCommandFile);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function dshMarkersOf(raw) {
|
|
36
|
+
const record = asRecord(raw);
|
|
37
|
+
return record !== undefined && ('bundle' in record || 'workbench' in record);
|
|
38
|
+
}
|
|
39
|
+
export function verdictOf(dir, manifestFile) {
|
|
40
|
+
const plugin = pluginAt(dir, manifestFile);
|
|
41
|
+
if (packageManifestOf(dir) === undefined && plugin === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
const dsh = dshMarkersOf(dshRecordOf(dir));
|
|
44
|
+
const kind = plugin !== undefined && dsh ? 'both' : plugin !== undefined ? 'runtime-plugin' : dsh ? 'dsh-bundle' : 'neither';
|
|
45
|
+
const pkg = packageNameOf(dir);
|
|
46
|
+
return {
|
|
47
|
+
kind,
|
|
48
|
+
...(pkg === undefined ? {} : { pkg }),
|
|
49
|
+
...(plugin?.name === undefined ? {} : { pluginName: plugin.name }),
|
|
50
|
+
};
|
|
51
|
+
}
|