@dsh-workbench/shared 1.1.5 → 1.1.7
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/contract/needs.d.ts +3 -0
- package/dist/contract/needs.js +17 -0
- package/dist/contract/protocol/market-index.d.ts +3 -1
- package/dist/contract/protocol/market-index.js +2 -0
- package/dist/host/lib-runners.d.ts +20 -0
- package/dist/host/lib-runners.js +46 -0
- package/dist/host/market-index-claim.d.ts +2 -0
- package/dist/host/market-index-claim.js +7 -0
- package/dist/host/market-index-doc.d.ts +56 -0
- package/dist/host/market-index-doc.js +1 -0
- package/dist/host/market-index-parse.d.ts +23 -0
- package/dist/host/market-index-parse.js +485 -0
- package/dist/host/market-index.d.ts +13 -21
- package/dist/host/market-index.js +120 -368
- package/dist/host/opencli-runner.d.ts +25 -0
- package/dist/host/opencli-runner.js +50 -0
- package/dist/host.d.ts +2 -0
- package/dist/host.js +2 -0
- package/dist/i18n/en.js +10 -4
- package/dist/i18n/index.d.ts +11 -5
- package/dist/i18n/zh.d.ts +10 -4
- package/dist/i18n/zh.js +10 -4
- package/package.json +1 -1
- package/src/contract/__tests__/needs.test.ts +8 -0
- package/src/contract/needs.ts +15 -0
- package/src/contract/protocol/market-index.ts +4 -0
- package/src/host/__tests__/lib-runners.test.ts +82 -0
- package/src/host/__tests__/market-index.test.ts +114 -0
- package/src/host/__tests__/opencli-runner.test.ts +120 -0
- package/src/host/lib-runners.ts +70 -0
- package/src/host/market-index-claim.ts +9 -0
- package/src/host/market-index-doc.ts +62 -0
- package/src/host/market-index-parse.ts +482 -0
- package/src/host/market-index.ts +162 -357
- package/src/host/opencli-runner.ts +76 -0
- package/src/host.ts +2 -0
- package/src/i18n/en.ts +10 -4
- package/src/i18n/zh.ts +10 -4
package/dist/contract/needs.d.ts
CHANGED
|
@@ -76,12 +76,15 @@ export interface ExtensionNeed extends NeedCommon {
|
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* 要在隔离运行时里有一个共用依赖包。
|
|
79
|
+
* `runs` 写它自己会起哪些运行时的命令(如 `["opencli"]`)—— 写了外壳就把那份执行器注入进去
|
|
80
|
+
* (库侧约定 `setOpencliRunner`);不写就按它自己在 PATH 上找。
|
|
79
81
|
*/
|
|
80
82
|
export interface LibNeed extends NeedCommon {
|
|
81
83
|
readonly kind: 'lib';
|
|
82
84
|
readonly name: string;
|
|
83
85
|
readonly version?: string;
|
|
84
86
|
readonly sources?: readonly SupplySource[];
|
|
87
|
+
readonly runs?: readonly string[] | undefined;
|
|
85
88
|
}
|
|
86
89
|
/**
|
|
87
90
|
* 跑起来时要有这个环境变量;`secret` 为真时任何地方都不回显它的值。
|
package/dist/contract/needs.js
CHANGED
|
@@ -22,6 +22,7 @@ export const NEED_KEYS = [
|
|
|
22
22
|
'secret',
|
|
23
23
|
'install',
|
|
24
24
|
'docs',
|
|
25
|
+
'runs',
|
|
25
26
|
];
|
|
26
27
|
const SOURCE_KEYS = ['kind', 'package', 'registryEnv', 'registryDefault', 'url', 'ref', 'path', 'env'];
|
|
27
28
|
const REGISTRY_PATTERN = /^(?:https?|file):\/\//;
|
|
@@ -149,6 +150,20 @@ export function binInstallProblemsOf(raw, at) {
|
|
|
149
150
|
}
|
|
150
151
|
return errors;
|
|
151
152
|
}
|
|
153
|
+
function libRunsProblemsOf(raw, at) {
|
|
154
|
+
if (raw === undefined)
|
|
155
|
+
return [];
|
|
156
|
+
if (!Array.isArray(raw))
|
|
157
|
+
return [`${at}.runs 要是数组`];
|
|
158
|
+
if (raw.length === 0)
|
|
159
|
+
return [`${at}.runs 是空数组 —— 不自己跑命令就整条删掉`];
|
|
160
|
+
const names = raw.map((one) => text(one));
|
|
161
|
+
if (names.some((one) => one === ''))
|
|
162
|
+
return [`${at}.runs 里有空字符串`];
|
|
163
|
+
if (new Set(names).size !== names.length)
|
|
164
|
+
return [`${at}.runs 里有重复的运行时`];
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
152
167
|
function perKindProblemsOf(kind, raw, at) {
|
|
153
168
|
const errors = [];
|
|
154
169
|
if (kind === 'bin' || kind === 'lib' || kind === 'env') {
|
|
@@ -159,6 +174,8 @@ function perKindProblemsOf(kind, raw, at) {
|
|
|
159
174
|
errors.push(...binInstallProblemsOf(raw['install'], at));
|
|
160
175
|
if (raw['docs'] !== undefined && text(raw['docs']) === '')
|
|
161
176
|
errors.push(`${at}.docs 是空串`);
|
|
177
|
+
if (kind === 'lib')
|
|
178
|
+
errors.push(...libRunsProblemsOf(raw['runs'], at));
|
|
162
179
|
if (kind === 'extension') {
|
|
163
180
|
if (text(raw['runtime']) === '')
|
|
164
181
|
errors.push(`${at}.runtime 没写 —— 扩展属于哪个运行时(如 opencli)`);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const MARKET_INDEX_FORMAT = 1;
|
|
2
|
+
export declare const MARKET_INDEX_DIR = "market";
|
|
3
|
+
export declare const MARKET_ARTIFACTS_DIR = "artifacts";
|
|
2
4
|
export declare const MARKET_INDEX_PATH = "market.json";
|
|
3
5
|
export declare const MARKET_DISCOVER_ROUTE = "panels/market-discover";
|
|
4
6
|
export declare const MARKET_DOC_ROUTE = "panels/market-doc";
|
|
@@ -111,7 +113,7 @@ export interface MarketIndex {
|
|
|
111
113
|
readonly metadata?: MarketIndexMetadata;
|
|
112
114
|
readonly plugins: readonly MarketIndexEntry[];
|
|
113
115
|
}
|
|
114
|
-
export type MarketIndexProblemKind = 'not-json' | 'not-object' | 'format' | 'plugins' | 'plugin' | 'name' | 'duplicate-name' | 'versions' | 'version' | 'version-order' | 'source' | 'git-commit' | 'timestamp' | 'icon' | 'owner' | 'unit';
|
|
116
|
+
export type MarketIndexProblemKind = 'not-json' | 'not-object' | 'format' | 'plugins' | 'plugin' | 'entry' | 'name' | 'duplicate-name' | 'versions' | 'version' | 'version-order' | 'duplicate-version' | 'source' | 'git-commit' | 'timestamp' | 'icon' | 'owner' | 'unit';
|
|
115
117
|
export interface MarketIndexProblem {
|
|
116
118
|
readonly kind: MarketIndexProblemKind;
|
|
117
119
|
readonly detail: string;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const MARKET_INDEX_FORMAT = 1;
|
|
2
|
+
export const MARKET_INDEX_DIR = 'market';
|
|
3
|
+
export const MARKET_ARTIFACTS_DIR = 'artifacts';
|
|
2
4
|
export const MARKET_INDEX_PATH = 'market.json';
|
|
3
5
|
export const MARKET_DISCOVER_ROUTE = 'panels/market-discover';
|
|
4
6
|
export const MARKET_DOC_ROUTE = 'panels/market-doc';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LibReader } from '../contract/lib.js';
|
|
2
|
+
import type { OpencliRunner } from './opencli-runner.js';
|
|
3
|
+
export interface LibRuntimes {
|
|
4
|
+
readonly opencli?: OpencliRunner | undefined;
|
|
5
|
+
}
|
|
6
|
+
interface Logger {
|
|
7
|
+
warn?(message: string): void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 给声明了 `runs` 的 lib 注入「跑这个运行时」的执行器:库侧按约定暴露 `setOpencliRunner`
|
|
11
|
+
* (运行时名字首字母大写 + `Runner`),外壳在每次 `load` 之后把执行器交给它。
|
|
12
|
+
*
|
|
13
|
+
* 没暴露的(版本旧)就保持它自己的行为(按 PATH 找),只记一条 warn;不熟悉的运行时同样只 warn。
|
|
14
|
+
* @param libs 原来的读库口。
|
|
15
|
+
* @param runtimes 外壳手上有的执行器,按运行时名字给。
|
|
16
|
+
* @param logger 记 warn 的地方(默认不记)。
|
|
17
|
+
* @returns 包好的读库口;`load` 的签名与返回不变。
|
|
18
|
+
*/
|
|
19
|
+
export declare function withLibRunners(libs: LibReader, runtimes: LibRuntimes, logger?: Logger): LibReader;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function runnerOf(runtimes, runtime) {
|
|
2
|
+
return runtime === 'opencli' ? runtimes.opencli : undefined;
|
|
3
|
+
}
|
|
4
|
+
function setterNameOf(runtime) {
|
|
5
|
+
return `set${runtime.charAt(0).toUpperCase()}${runtime.slice(1)}Runner`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 给声明了 `runs` 的 lib 注入「跑这个运行时」的执行器:库侧按约定暴露 `setOpencliRunner`
|
|
9
|
+
* (运行时名字首字母大写 + `Runner`),外壳在每次 `load` 之后把执行器交给它。
|
|
10
|
+
*
|
|
11
|
+
* 没暴露的(版本旧)就保持它自己的行为(按 PATH 找),只记一条 warn;不熟悉的运行时同样只 warn。
|
|
12
|
+
* @param libs 原来的读库口。
|
|
13
|
+
* @param runtimes 外壳手上有的执行器,按运行时名字给。
|
|
14
|
+
* @param logger 记 warn 的地方(默认不记)。
|
|
15
|
+
* @returns 包好的读库口;`load` 的签名与返回不变。
|
|
16
|
+
*/
|
|
17
|
+
export function withLibRunners(libs, runtimes, logger) {
|
|
18
|
+
const warned = new Set();
|
|
19
|
+
const warnOnce = (key, message) => {
|
|
20
|
+
if (warned.has(key))
|
|
21
|
+
return;
|
|
22
|
+
warned.add(key);
|
|
23
|
+
logger?.warn?.(message);
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
async load(need, entry) {
|
|
27
|
+
const module = await libs.load(need, entry);
|
|
28
|
+
for (const runtime of need.runs ?? []) {
|
|
29
|
+
const runner = runnerOf(runtimes, runtime);
|
|
30
|
+
if (runner === undefined) {
|
|
31
|
+
warnOnce(`${need.name}:${runtime}:unsupported`, `[dsh-workbench] ${need.name} 声明了自己会跑 ${runtime},但工作台没有这个执行器 —— 它会按自己的方式找`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const setter = typeof module === 'object' && module !== null
|
|
35
|
+
? module[setterNameOf(runtime)]
|
|
36
|
+
: undefined;
|
|
37
|
+
if (typeof setter !== 'function') {
|
|
38
|
+
warnOnce(`${need.name}:${entry}:${runtime}:missing`, `[dsh-workbench] ${need.name} 声明了自己会跑 ${runtime},但这一版没有 ${setterNameOf(runtime)} —— 它会按 PATH 找`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
setter.call(module, runner);
|
|
42
|
+
}
|
|
43
|
+
return module;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -14,5 +14,7 @@ export interface MarketClaimInput {
|
|
|
14
14
|
readonly artifactPath?: string | undefined;
|
|
15
15
|
}
|
|
16
16
|
export declare function marketArtifactPath(name: string): string;
|
|
17
|
+
export declare function marketArtifactRoot(name: string): string;
|
|
18
|
+
export declare function marketArtifactPathFor(name: string, version: string): string;
|
|
17
19
|
export declare function entryUnitOf(entry: MarketIndexEntry): string | undefined;
|
|
18
20
|
export declare function claimOf(index: MarketIndex, input: MarketClaimInput): MarketClaim;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MARKET_ARTIFACTS_DIR } from '../contract/protocol/market-index.js';
|
|
1
2
|
function foldedName(name) {
|
|
2
3
|
return name.replace(/\s+/gu, ' ').trim().toLowerCase();
|
|
3
4
|
}
|
|
@@ -41,6 +42,12 @@ export function claimOfEntry(entry, publisher) {
|
|
|
41
42
|
export function marketArtifactPath(name) {
|
|
42
43
|
return `packages/${name}`;
|
|
43
44
|
}
|
|
45
|
+
export function marketArtifactRoot(name) {
|
|
46
|
+
return `${MARKET_ARTIFACTS_DIR}/${name}`;
|
|
47
|
+
}
|
|
48
|
+
export function marketArtifactPathFor(name, version) {
|
|
49
|
+
return `${MARKET_ARTIFACTS_DIR}/${name}/${version}`;
|
|
50
|
+
}
|
|
44
51
|
export function entryUnitOf(entry) {
|
|
45
52
|
const unit = entry.metadata?.unit?.trim();
|
|
46
53
|
if (unit !== undefined && unit !== '')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { MarketIndex, MarketIndexEngines, MarketIndexEntryMetadata, MarketIndexProblem, MarketIndexSource, MarketLocalizedText } from '../contract/protocol/market-index.js';
|
|
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
|
+
};
|
|
36
|
+
export interface MarketEntryFile {
|
|
37
|
+
readonly path: string;
|
|
38
|
+
readonly text: string;
|
|
39
|
+
}
|
|
40
|
+
export type MarketIndexAssemble = {
|
|
41
|
+
readonly ok: true;
|
|
42
|
+
readonly index: MarketIndex;
|
|
43
|
+
} | {
|
|
44
|
+
readonly ok: false;
|
|
45
|
+
readonly problems: readonly MarketIndexProblem[];
|
|
46
|
+
};
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
export declare function compareSemver(left: string, right: string): number | undefined;
|
|
4
|
+
export declare function httpUrlProblem(url: string): string | undefined;
|
|
5
|
+
export declare function gitUrlOk(url: string): boolean;
|
|
6
|
+
export declare function parseMarketIndex(raw: unknown): MarketIndexParse;
|
|
7
|
+
export declare function parseMarketIndexJson(text: string): MarketIndexParse;
|
|
8
|
+
export declare function sortMarketVersions(versions: readonly MarketIndexVersion[]): MarketIndexVersion[];
|
|
9
|
+
export declare function upsertMarketEntry(index: MarketIndex, entry: MarketIndexEntry): MarketIndex;
|
|
10
|
+
export declare function marketEntryLabels(entry: MarketIndexEntry): readonly string[];
|
|
11
|
+
export declare function filterMarketIndex(index: MarketIndex, filter: DiscoverFilter): readonly MarketIndexEntry[];
|
|
12
|
+
export declare function marketSourceKey(source: IndexSourceSpec): string;
|
|
13
|
+
export type SourceConfigParse = {
|
|
14
|
+
readonly ok: true;
|
|
15
|
+
readonly source: MarketSourceConfig;
|
|
16
|
+
} | {
|
|
17
|
+
readonly ok: false;
|
|
18
|
+
readonly problem: MarketIndexProblem;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseSourceConfig(raw: unknown): SourceConfigParse;
|
|
21
|
+
export declare function parseMarketEntryDoc(raw: unknown): MarketEntryDocParse;
|
|
22
|
+
export declare function marketEntryPath(dir: string, name: string, version: string): string;
|
|
23
|
+
export declare function marketMetaPath(dir: string, name: string, epoch: number, suffix: string): string;
|