@dsh-workbench/shared 0.3.11 → 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/api/blocker-table.d.ts +3 -3
- package/dist/api/blocker-table.js +47 -23
- package/dist/api/dsh.d.ts +1 -1
- package/dist/api/workbench-client.js +2 -1
- package/dist/components/Activity.css +0 -1
- package/dist/components/ApprovalDetail.css +1 -2
- package/dist/components/ApprovalDetail.d.ts +1 -1
- package/dist/components/ApprovalPlan.css +0 -1
- package/dist/components/Card.css +0 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/CardBoundary.d.ts +1 -1
- package/dist/components/CardHead.css +0 -1
- package/dist/components/CardHead.d.ts +1 -1
- package/dist/components/ChoiceGroup.css +1 -2
- package/dist/components/ConfirmBar.d.ts +1 -1
- package/dist/components/ConfirmBar.js +1 -1
- package/dist/components/Form.css +1 -1
- package/dist/components/FormActions.js +1 -1
- package/dist/components/FormNodes.js +5 -1
- package/dist/components/FormRows.css +1 -0
- package/dist/components/FormRows.d.ts +4 -2
- package/dist/components/FormRows.js +5 -4
- package/dist/components/LongTaskRow.css +1 -2
- package/dist/components/LongTaskRow.d.ts +1 -1
- package/dist/components/LongTaskRow.js +1 -1
- package/dist/components/Note.css +0 -1
- package/dist/components/ResourceStates.css +0 -1
- package/dist/components/SettingsSection.css +0 -1
- package/dist/components/Spinner.css +0 -1
- package/dist/components/Stack.css +0 -1
- package/dist/components/StateBlock.css +0 -1
- package/dist/components/board-module.d.ts +3 -3
- package/dist/components/icons.d.ts +1 -1
- package/dist/components/icons.js +11 -4
- package/dist/config/constants.d.ts +2 -4
- package/dist/config/constants.js +2 -7
- package/dist/contract/adapters.d.ts +10 -0
- package/dist/contract/cli.d.ts +3 -0
- package/dist/contract/command.d.ts +22 -0
- package/dist/contract/data.d.ts +127 -0
- package/dist/contract/extension.d.ts +14 -0
- package/dist/contract/extension.js +1 -0
- package/dist/{api → contract}/facts.d.ts +12 -3
- package/dist/{api → contract}/facts.js +19 -1
- package/dist/contract/git.d.ts +38 -0
- package/dist/contract/git.js +1 -0
- package/dist/contract/lib.d.ts +4 -0
- package/dist/contract/lib.js +1 -0
- package/dist/contract/needs.d.ts +132 -0
- package/dist/contract/needs.js +232 -0
- package/dist/contract/plugin-source.d.ts +1 -0
- package/dist/contract/plugin-source.js +1 -0
- package/dist/contract/protocol/bundle-install.d.ts +136 -0
- package/dist/contract/protocol/bundle-install.js +12 -0
- package/dist/contract/protocol/config-sync.d.ts +55 -0
- package/dist/contract/protocol/config-sync.js +2 -0
- package/dist/contract/protocol/diy.d.ts +4 -0
- package/dist/contract/protocol/diy.js +4 -0
- package/dist/contract/protocol/dsh-plugins.d.ts +11 -0
- package/dist/contract/protocol/dsh-plugins.js +1 -0
- package/dist/contract/protocol/long-tasks.d.ts +13 -0
- package/dist/contract/protocol/long-tasks.js +1 -0
- package/dist/contract/protocol/market-index.d.ts +195 -0
- package/dist/contract/protocol/market-index.js +7 -0
- package/dist/contract/protocol/platform.d.ts +44 -0
- package/dist/contract/protocol/platform.js +1 -0
- package/dist/contract/protocol/preflight-wake.d.ts +7 -0
- package/dist/contract/protocol/preflight-wake.js +1 -0
- package/dist/contract/protocol/profile-create.d.ts +30 -0
- package/dist/contract/protocol/profile-create.js +2 -0
- package/dist/contract/protocol/sidebar-edition.d.ts +14 -0
- package/dist/contract/protocol/sidebar-edition.js +2 -0
- package/dist/contract/protocol/unit-manifests.d.ts +6 -0
- package/dist/contract/protocol/unit-manifests.js +1 -0
- package/dist/contract/unit-board.js +1 -0
- package/dist/contract/unit-capability.d.ts +34 -0
- package/dist/contract/unit-capability.js +1 -0
- package/dist/{host → contract}/unit-declaration.d.ts +7 -5
- package/dist/{api → contract}/unit-declaration.js +19 -13
- package/dist/contract/unit.d.ts +57 -0
- package/dist/contract/unit.js +24 -0
- package/dist/contract/workbench-contract.d.ts +15 -0
- package/dist/contract/workbench-contract.js +1 -0
- package/dist/host/adapter-set.d.ts +26 -0
- package/dist/host/adapter-set.js +244 -0
- package/dist/host/adapter-types.d.ts +79 -0
- package/dist/host/adapter-types.js +23 -0
- package/dist/host/adapters.d.ts +28 -0
- package/dist/host/adapters.js +9 -0
- package/dist/host/browser.d.ts +3 -0
- package/dist/host/browser.js +3 -0
- package/dist/host/changelog.d.ts +21 -0
- package/dist/host/changelog.js +188 -0
- package/dist/host/channel-plugin.d.ts +15 -0
- package/dist/host/channel-plugin.js +42 -0
- package/dist/host/cli.d.ts +4 -0
- package/dist/host/cli.js +7 -0
- package/dist/host/command-name.d.ts +1 -0
- package/dist/host/command-name.js +14 -0
- package/dist/host/data-error.d.ts +13 -0
- package/dist/host/data-error.js +20 -0
- package/dist/host/data-root.js +3 -0
- package/dist/host/diy-debug.js +2 -1
- package/dist/host/diy-record.js +2 -1
- package/dist/host/extension.d.ts +7 -0
- package/dist/host/extension.js +39 -0
- package/dist/host/facts.d.ts +1 -1
- package/dist/host/facts.js +3 -2
- package/dist/host/git.d.ts +2 -33
- package/dist/host/git.js +2 -2
- package/dist/host/http.js +2 -2
- package/dist/host/lib-capabilities.d.ts +22 -0
- package/dist/host/lib-capabilities.js +37 -0
- package/dist/host/lib-install.d.ts +9 -0
- package/dist/host/lib-install.js +10 -0
- package/dist/host/long-task.d.ts +1 -1
- package/dist/host/long-task.js +4 -3
- package/dist/host/market-index.d.ts +22 -0
- package/dist/host/market-index.js +379 -0
- package/dist/host/needs.d.ts +4 -0
- package/dist/host/needs.js +2 -0
- package/dist/host/patch.d.ts +9 -0
- package/dist/host/patch.js +78 -0
- package/dist/host/paths.d.ts +12 -0
- package/dist/host/paths.js +28 -0
- package/dist/host/platform.d.ts +14 -0
- package/dist/host/platform.js +40 -0
- package/dist/host/plugin-source.d.ts +8 -6
- package/dist/host/plugin-source.js +43 -33
- package/dist/host/plugin.d.ts +4 -0
- package/dist/host/plugin.js +39 -0
- package/dist/host/port-types.d.ts +192 -0
- package/dist/host/port-types.js +1 -0
- package/dist/host/preflight.d.ts +28 -0
- package/dist/host/preflight.js +1 -0
- package/dist/host/profile-port.d.ts +4 -0
- package/dist/host/profile-port.js +4 -0
- package/dist/host/provision.d.ts +2 -2
- package/dist/host/provision.js +5 -3
- package/dist/host/recipe-rules.d.ts +5 -0
- package/dist/host/recipe-rules.js +382 -0
- package/dist/host/recipe.d.ts +14 -0
- package/dist/host/recipe.js +283 -0
- package/dist/host/release-packages.generated.d.ts +1 -0
- package/dist/host/release-packages.generated.js +10 -0
- package/dist/host/shell-text.d.ts +9 -2
- package/dist/host/shell-text.js +15 -3
- package/dist/host/shell.d.ts +2 -0
- package/dist/host/shell.js +7 -0
- package/dist/host/spec.d.ts +8 -0
- package/dist/host/spec.js +24 -0
- package/dist/host/supply-index.d.ts +34 -0
- package/dist/host/supply-index.js +202 -0
- package/dist/host/supply-port.d.ts +5 -0
- package/dist/host/supply-port.js +39 -0
- package/dist/host/time.d.ts +12 -0
- package/dist/host/time.js +19 -0
- package/dist/host/tokens.d.ts +26 -0
- package/dist/host/tokens.js +154 -0
- package/dist/host/tool-kit.d.ts +0 -6
- package/dist/host/tool-kit.js +0 -22
- package/dist/host/unit-face.d.ts +12 -4
- package/dist/host/version-range.d.ts +2 -0
- package/dist/host/version-range.js +13 -0
- package/dist/host/version.d.ts +4 -0
- package/dist/host/version.js +27 -0
- package/dist/host.d.ts +53 -16
- package/dist/host.js +53 -16
- package/dist/i18n/en.js +225 -71
- package/dist/i18n/index.d.ts +299 -73
- package/dist/i18n/merge.js +1 -1
- package/dist/i18n/need-text.d.ts +26 -0
- package/dist/i18n/need-text.js +26 -0
- package/dist/i18n/settings.d.ts +70 -0
- package/dist/i18n/settings.js +136 -0
- package/dist/i18n/zh.d.ts +298 -72
- package/dist/i18n/zh.js +227 -73
- package/dist/index.d.ts +27 -7
- package/dist/index.js +27 -7
- package/dist/{api → lib}/dsh-client.d.ts +11 -1
- package/dist/lib/dsh-client.js +1 -0
- package/dist/lib/market-index.d.ts +7 -0
- package/dist/lib/market-index.js +25 -0
- package/dist/lib/react.d.ts +0 -40
- package/dist/lib/react.js +0 -36
- package/dist/lib/ui.js +3 -9
- package/dist/lib/version.d.ts +4 -0
- package/dist/lib/version.js +21 -0
- package/dist/testing/data.d.ts +13 -0
- package/dist/testing/data.js +46 -0
- package/dist/testing/declaration.d.ts +1 -1
- package/dist/testing/declaration.js +1 -1
- package/dist/testing/host-fakes.d.ts +0 -13
- package/dist/testing/host-fakes.js +0 -24
- package/dist/testing/needs.d.ts +16 -0
- package/dist/testing/needs.js +46 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +2 -0
- package/package.json +22 -4
- package/src/api/__tests__/blocker-table.test.ts +135 -86
- package/src/api/blocker-table.ts +52 -26
- package/src/api/dsh.ts +1 -1
- package/src/api/host-write.ts +0 -1
- package/src/api/workbench-client.test.ts +2 -2
- package/src/api/workbench-client.ts +2 -1
- package/src/components/Activity.css +0 -1
- package/src/components/ApprovalDetail.css +1 -2
- package/src/components/ApprovalDetail.tsx +1 -1
- package/src/components/ApprovalPlan.css +0 -1
- package/src/components/Card.css +0 -1
- package/src/components/Card.tsx +1 -1
- package/src/components/CardAnchor.test.tsx +1 -1
- package/src/components/CardBoundary.tsx +1 -1
- package/src/components/CardHead.css +0 -1
- package/src/components/CardHead.tsx +1 -1
- package/src/components/ChoiceGroup.css +1 -2
- package/src/components/ConfirmBar.tsx +1 -1
- package/src/components/Form.css +1 -1
- package/src/components/Form.test.tsx +6 -0
- package/src/components/Form.tsx +1 -1
- package/src/components/FormActions.tsx +1 -1
- package/src/components/FormNodes.tsx +6 -1
- package/src/components/FormRows.css +1 -0
- package/src/components/FormRows.test.tsx +30 -0
- package/src/components/FormRows.tsx +9 -5
- package/src/components/LongTaskRow.css +1 -2
- package/src/components/LongTaskRow.tsx +1 -1
- package/src/components/Note.css +0 -1
- package/src/components/ResourceStates.css +0 -1
- package/src/components/SelectFieldNodes.tsx +0 -1
- package/src/components/SettingsSection.css +0 -1
- package/src/components/Spinner.css +0 -1
- package/src/components/Stack.css +0 -1
- package/src/components/StateBlock.css +0 -1
- package/src/components/__tests__/long-task-row.test.ts +1 -1
- package/src/components/board-module.ts +3 -3
- package/src/components/icons.tsx +16 -8
- package/src/config/constants.ts +2 -8
- package/src/{api → contract}/__tests__/facts.test.ts +73 -25
- package/src/{api → contract}/__tests__/long-task.test.ts +3 -3
- package/src/contract/__tests__/needs.test.ts +74 -0
- package/src/{host → contract}/__tests__/unit-declaration.test.ts +1 -1
- package/src/contract/__tests__/unit.test.ts +43 -0
- package/src/contract/adapters.ts +9 -0
- package/src/contract/cli.ts +3 -0
- package/src/contract/command.ts +25 -0
- package/src/contract/data.ts +146 -0
- package/src/contract/extension.ts +20 -0
- package/src/{api → contract}/facts.ts +34 -4
- package/src/contract/git.ts +20 -0
- package/src/contract/lib.ts +5 -0
- package/src/contract/needs.ts +331 -0
- package/src/contract/plugin-source.ts +1 -0
- package/src/contract/protocol/bundle-install.ts +198 -0
- package/src/contract/protocol/config-sync.ts +65 -0
- package/src/contract/protocol/diy.ts +4 -0
- package/src/contract/protocol/dsh-plugins.ts +13 -0
- package/src/contract/protocol/long-tasks.ts +16 -0
- package/src/contract/protocol/market-index.ts +246 -0
- package/src/contract/protocol/platform.ts +47 -0
- package/src/contract/protocol/preflight-wake.ts +17 -0
- package/src/contract/protocol/profile-create.ts +36 -0
- package/src/contract/protocol/sidebar-edition.ts +19 -0
- package/src/contract/protocol/unit-manifests.ts +8 -0
- package/src/{api → contract}/unit-board.ts +0 -1
- package/src/contract/unit-capability.ts +37 -0
- package/src/{host → contract}/unit-declaration.ts +22 -18
- package/src/contract/unit.ts +76 -0
- package/src/contract/workbench-contract.ts +30 -0
- package/src/host/__tests__/adapter-set.test.ts +228 -0
- package/src/host/__tests__/adapters.test.ts +27 -0
- package/src/host/__tests__/changelog.test.ts +102 -0
- package/src/host/__tests__/command-name.test.ts +16 -0
- package/src/host/__tests__/diy-record.test.ts +3 -3
- package/src/host/__tests__/facts.test.ts +5 -5
- package/src/host/__tests__/long-task.test.ts +1 -1
- package/src/host/__tests__/market-index.test.ts +179 -0
- package/src/host/__tests__/plugin-ledger.test.ts +10 -10
- package/src/host/__tests__/provision.test.ts +7 -6
- package/src/host/__tests__/shell-text.test.ts +16 -1
- package/src/host/__tests__/supply-index.test.ts +137 -0
- package/src/host/__tests__/tokens.test.ts +110 -0
- package/src/host/__tests__/version-range.test.ts +21 -0
- package/src/host/adapter-set.ts +299 -0
- package/src/host/adapter-types.ts +105 -0
- package/src/host/adapters.ts +50 -0
- package/src/host/browser.ts +3 -0
- package/src/host/changelog.ts +212 -0
- package/src/host/channel-plugin.ts +55 -0
- package/src/host/cli.ts +11 -0
- package/src/host/command-name.ts +15 -0
- package/src/host/data-error.ts +21 -0
- package/src/host/data-root.ts +2 -0
- package/src/host/diy-debug.ts +2 -1
- package/src/host/diy-record.ts +2 -1
- package/src/host/extension.test.ts +55 -0
- package/src/host/extension.ts +57 -0
- package/src/host/facts.ts +4 -3
- package/src/host/git.ts +8 -16
- package/src/host/http.ts +2 -2
- package/src/host/lib-capabilities.ts +52 -0
- package/src/host/lib-install.ts +18 -0
- package/src/host/long-task.ts +4 -3
- package/src/host/market-index.ts +383 -0
- package/src/host/needs.ts +6 -0
- package/src/host/patch.ts +93 -0
- package/src/host/paths.ts +37 -0
- package/src/host/platform.ts +48 -0
- package/src/host/plugin-source.ts +48 -56
- package/src/host/plugin.ts +39 -0
- package/src/host/port-types.ts +223 -0
- package/src/host/preflight.ts +31 -0
- package/src/host/profile-port.ts +9 -0
- package/src/host/provision.ts +7 -4
- package/src/host/recipe-rules.ts +330 -0
- package/src/host/recipe.test.ts +334 -0
- package/src/host/recipe.ts +323 -0
- package/src/host/release-packages.generated.ts +10 -0
- package/src/host/shell-text.ts +15 -3
- package/src/host/shell.ts +9 -0
- package/src/host/spec.ts +32 -0
- package/src/host/supply-index.ts +256 -0
- package/src/host/supply-port.ts +45 -0
- package/src/host/time.ts +22 -0
- package/src/host/tokens.ts +186 -0
- package/src/host/tool-kit.ts +0 -35
- package/src/host/unit-face.ts +12 -4
- package/src/host/version-range.ts +13 -0
- package/src/host/version.ts +30 -0
- package/src/host.ts +53 -16
- package/src/i18n/en.ts +225 -71
- package/src/i18n/merge.test.ts +6 -6
- package/src/i18n/merge.ts +1 -1
- package/src/i18n/need-text.ts +27 -0
- package/src/i18n/settings.ts +141 -0
- package/src/i18n/zh.ts +228 -73
- package/src/index.ts +27 -7
- package/src/lib/blocker-action.ts +0 -1
- package/src/{api → lib}/dsh-client.ts +12 -1
- package/src/lib/market-index.ts +36 -0
- package/src/lib/react.ts +0 -40
- package/src/lib/text.test.ts +2 -2
- package/src/lib/ui.test.ts +6 -5
- package/src/lib/ui.ts +3 -9
- package/src/testing/__tests__/declaration.test.ts +1 -1
- package/src/testing/data.ts +66 -0
- package/src/testing/declaration.ts +2 -2
- package/src/testing/host-fakes.ts +0 -38
- package/src/testing/needs.ts +80 -0
- package/src/testing.ts +2 -0
- package/dist/api/unit-declaration.d.ts +0 -65
- package/dist/api/unit.d.ts +0 -85
- package/dist/api/unit.js +0 -8
- package/dist/api/workbench-contract.d.ts +0 -44
- package/dist/components/MockStrip.css +0 -39
- package/dist/components/MockStrip.d.ts +0 -15
- package/dist/components/MockStrip.js +0 -8
- package/dist/components/SaveBar.css +0 -7
- package/dist/host/browser-find.d.ts +0 -38
- package/dist/host/browser-find.js +0 -242
- package/dist/host/browser-read.d.ts +0 -28
- package/dist/host/browser-read.js +0 -243
- package/dist/host/opencli-runtime.d.ts +0 -25
- package/dist/host/opencli-runtime.js +0 -56
- package/dist/host/opencli-window.d.ts +0 -2
- package/dist/host/opencli-window.js +0 -8
- package/dist/host/opencli.d.ts +0 -37
- package/dist/host/opencli.js +0 -155
- package/dist/host/unit-declaration.js +0 -213
- package/dist/host/unit-libs.d.ts +0 -37
- package/dist/lib/diy-debug.d.ts +0 -80
- package/dist/lib/diy-debug.js +0 -88
- package/dist/lib/diy-record.d.ts +0 -135
- package/dist/lib/diy-record.js +0 -162
- package/dist/lib/lint-probe.d.ts +0 -1
- package/dist/lib/lint-probe.js +0 -6
- package/dist/lib/panel-state.d.ts +0 -120
- package/dist/lib/panel-state.js +0 -176
- package/src/api/__tests__/unit.test.ts +0 -38
- package/src/api/unit.ts +0 -93
- package/src/api/workbench-contract.ts +0 -59
- package/src/host/__tests__/browser-find.test.ts +0 -330
- package/src/host/__tests__/browser-read.test.ts +0 -228
- package/src/host/__tests__/opencli-runtime.test.ts +0 -46
- package/src/host/browser-find.ts +0 -311
- package/src/host/browser-read.ts +0 -311
- package/src/host/opencli-runtime.ts +0 -79
- package/src/host/opencli-window.ts +0 -11
- package/src/host/opencli.ts +0 -213
- package/src/host/unit-libs.ts +0 -41
- /package/dist/{api/blocker.js → contract/adapters.js} +0 -0
- /package/dist/{api → contract}/blocker.d.ts +0 -0
- /package/dist/{api/dsh-client.js → contract/blocker.js} +0 -0
- /package/dist/{api/unit-board.js → contract/cli.js} +0 -0
- /package/dist/{api/workbench-contract.js → contract/command.js} +0 -0
- /package/dist/{host/unit-libs.js → contract/data.js} +0 -0
- /package/dist/{api → contract}/long-task.d.ts +0 -0
- /package/dist/{api → contract}/long-task.js +0 -0
- /package/dist/{api → contract}/unit-board.d.ts +0 -0
- /package/src/{api → contract}/blocker.ts +0 -0
- /package/src/{api → contract}/long-task.ts +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Need, NeedKind } from './needs.js';
|
|
2
|
+
import type { ProvisionProblem } from './plugin-source.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 一条 need 探出来的样子:状态、给人看的一句、以及能照做的修法。
|
|
6
|
+
*/
|
|
7
|
+
export interface NeedState {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly kind: NeedKind;
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly status: 'ok' | 'missing' | 'outdated' | 'unsatisfied' | 'unchecked';
|
|
12
|
+
readonly detail: string;
|
|
13
|
+
readonly command?: string;
|
|
14
|
+
readonly missing?: readonly string[];
|
|
15
|
+
readonly installed?: string;
|
|
16
|
+
readonly latest?: string;
|
|
17
|
+
readonly provision?: 'ready' | ProvisionProblem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 一个单元的能力结论:unit 一级只做汇总,缺什么逐条看 needs。
|
|
22
|
+
*/
|
|
23
|
+
export interface UnitCapability {
|
|
24
|
+
readonly unit: string;
|
|
25
|
+
readonly status: 'ok' | 'degraded' | 'missing' | 'outdated' | 'unchecked' | 'unsupported' | 'failed';
|
|
26
|
+
readonly needs: readonly NeedState[];
|
|
27
|
+
readonly detail?: string;
|
|
28
|
+
readonly released?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 探一个单元的输入:单元 id 加它声明的 need 列表。
|
|
33
|
+
*/
|
|
34
|
+
export interface NeedProbeInput {
|
|
35
|
+
readonly unit: string;
|
|
36
|
+
readonly needs: readonly Need[];
|
|
37
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { UnitHostNeeds, UnitManifest } from '../contract/unit.js';
|
|
2
|
+
import { needsProblemsOf, type Need } from './needs.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 声明协议当前版本;模块清单的 dsh.workbench.apiVersion 要写这个整数。
|
|
5
6
|
*/
|
|
6
|
-
export const WORKBENCH_API_VERSION =
|
|
7
|
+
export const WORKBENCH_API_VERSION = 2;
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* 外壳愿意认的协议版本;不在表里的模块当场被拒,不猜语义。
|
|
@@ -36,11 +37,11 @@ export interface UnitDeclaration {
|
|
|
36
37
|
readonly card: UnitCardDeclaration | null;
|
|
37
38
|
readonly tools: readonly UnitToolDeclaration[];
|
|
38
39
|
readonly host: UnitHostNeeds;
|
|
39
|
-
readonly
|
|
40
|
+
readonly needs?: readonly Need[];
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
+
* 协议认得的全部键;写了别的键这份清单会被拒(`protocol` 判据与运行时同一套),拼错当场报出来。
|
|
44
45
|
*/
|
|
45
46
|
export const DECLARATION_KEYS: readonly string[] = [
|
|
46
47
|
'apiVersion',
|
|
@@ -48,11 +49,11 @@ export const DECLARATION_KEYS: readonly string[] = [
|
|
|
48
49
|
'card',
|
|
49
50
|
'tools',
|
|
50
51
|
'host',
|
|
51
|
-
'
|
|
52
|
+
'needs',
|
|
52
53
|
];
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
* 协议要求必写的键;
|
|
56
|
+
* 协议要求必写的键;needs 不在其中(没有环境需要就不写)。
|
|
56
57
|
*/
|
|
57
58
|
export const DECLARATION_REQUIRED_KEYS: readonly string[] = [
|
|
58
59
|
'apiVersion',
|
|
@@ -104,13 +105,19 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
104
105
|
if (version === undefined) {
|
|
105
106
|
return [
|
|
106
107
|
`${subject}: dsh.workbench 没有 apiVersion —— 协议从 v${WORKBENCH_API_VERSION} 起,` +
|
|
107
|
-
`写 \`"apiVersion": ${WORKBENCH_API_VERSION}\`。缺它 =
|
|
108
|
+
`写 \`"apiVersion": ${WORKBENCH_API_VERSION}\`。缺它 = 外壳不认这个模块`,
|
|
108
109
|
];
|
|
109
110
|
}
|
|
110
111
|
if (!Number.isInteger(version) || !SUPPORTED_API_VERSIONS.includes(version as number)) {
|
|
112
|
+
const hint =
|
|
113
|
+
version === 1
|
|
114
|
+
? ` —— v2 把 \`env\` 的三张表(command / plugins / libs)换成了 \`needs\`:` +
|
|
115
|
+
'把外部需要逐条写成 `{ kind, name, purpose, ... }`(kind 是 bin / extension / lib / env),' +
|
|
116
|
+
'再把 apiVersion 改成 2'
|
|
117
|
+
: '';
|
|
111
118
|
return [
|
|
112
119
|
`${subject}: dsh.workbench.apiVersion 是 ${JSON.stringify(version)},这里只认 ` +
|
|
113
|
-
`${SUPPORTED_API_VERSIONS.join(' / ')}
|
|
120
|
+
`${SUPPORTED_API_VERSIONS.join(' / ')}(要整数)${hint}`,
|
|
114
121
|
];
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -120,7 +127,7 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
120
127
|
if (unknown.length > 0) {
|
|
121
128
|
problems.push(
|
|
122
129
|
`${subject}: dsh.workbench 里有协议 v${WORKBENCH_API_VERSION} 不认识的键 ${listOf(unknown)} —— ` +
|
|
123
|
-
`只有 ${listOf(DECLARATION_KEYS)}
|
|
130
|
+
`只有 ${listOf(DECLARATION_KEYS)}。(协议外的键会让这份清单被拒:那句声明等于没写)`,
|
|
124
131
|
);
|
|
125
132
|
}
|
|
126
133
|
|
|
@@ -224,13 +231,6 @@ export function declarationProblemsOf(subject: string, raw: unknown): readonly s
|
|
|
224
231
|
}
|
|
225
232
|
}
|
|
226
233
|
|
|
227
|
-
if (raw.env !== undefined && !isRecord(raw.env)) {
|
|
228
|
-
problems.push(
|
|
229
|
-
`${subject}: dsh.workbench.env 要是对象(command / plugins / libs)—— ` +
|
|
230
|
-
`语义由门禁的 env 判据查,这里只查形状`,
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
234
|
return problems;
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -241,6 +241,7 @@ export interface DeclarationRead {
|
|
|
241
241
|
readonly declaration?: UnitDeclaration;
|
|
242
242
|
readonly manifest?: UnitManifest;
|
|
243
243
|
readonly problems: readonly string[];
|
|
244
|
+
readonly warnings: readonly string[];
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
/**
|
|
@@ -251,8 +252,10 @@ export interface DeclarationRead {
|
|
|
251
252
|
*/
|
|
252
253
|
export function readDeclaration(subject: string, raw: unknown): DeclarationRead {
|
|
253
254
|
const problems = declarationProblemsOf(subject, raw);
|
|
254
|
-
if (problems.length > 0) return { problems };
|
|
255
|
+
if (problems.length > 0) return { problems, warnings: [] };
|
|
255
256
|
const declaration = raw as UnitDeclaration;
|
|
257
|
+
const needsRead = needsProblemsOf(subject, declaration.needs);
|
|
258
|
+
if (needsRead.errors.length > 0) return { problems: needsRead.errors, warnings: needsRead.warnings };
|
|
256
259
|
const card = declaration.card;
|
|
257
260
|
return {
|
|
258
261
|
declaration,
|
|
@@ -274,7 +277,8 @@ export function readDeclaration(subject: string, raw: unknown): DeclarationRead
|
|
|
274
277
|
inject: [...(declaration.host.inject ?? [])],
|
|
275
278
|
...(declaration.host.optional === undefined ? {} : { optional: [...declaration.host.optional] }),
|
|
276
279
|
},
|
|
277
|
-
...(declaration.
|
|
280
|
+
...(declaration.needs === undefined ? {} : { needs: declaration.needs }),
|
|
278
281
|
},
|
|
282
|
+
warnings: needsRead.warnings,
|
|
279
283
|
};
|
|
280
284
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ExtensionNeed, LibNeed, Need } from './needs.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 一张卡片在清单里的静态声明(文案键加排序位);默认显不显示由运行期面板配置定。
|
|
5
|
+
*/
|
|
6
|
+
export interface UnitCard {
|
|
7
|
+
readonly titleKey: string;
|
|
8
|
+
readonly order: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 一条工具在清单里的样子:全名加会不会写;审批按 writes 认领。
|
|
13
|
+
*/
|
|
14
|
+
export interface UnitTool {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly writes: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 模块要外壳的哪些服务:inject 缺一个就挂不上,optional 缺了照挂。
|
|
21
|
+
*/
|
|
22
|
+
export interface UnitHostNeeds {
|
|
23
|
+
readonly inject: readonly string[];
|
|
24
|
+
readonly optional?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 已装插件与源上最新版那一对,用来判该不该提示升级;latest 缺省表示还没查到。
|
|
29
|
+
*/
|
|
30
|
+
export interface PluginRelease {
|
|
31
|
+
readonly installed: string;
|
|
32
|
+
readonly latest?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 装配期用的模块清单:单元 id、卡片、工具、宿主服务与环境需要,比声明少一层协议包装。
|
|
37
|
+
*/
|
|
38
|
+
export interface UnitManifest {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly card: UnitCard | null;
|
|
41
|
+
readonly tools: readonly UnitTool[];
|
|
42
|
+
readonly host: UnitHostNeeds;
|
|
43
|
+
readonly needs?: readonly Need[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 模块声明的全部环境需要(没写就是空表)。
|
|
48
|
+
* @param manifest 模块清单。
|
|
49
|
+
* @returns need 列表。
|
|
50
|
+
*/
|
|
51
|
+
export function needsOf(manifest: UnitManifest): readonly Need[] {
|
|
52
|
+
return manifest.needs ?? [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 宿主能代装的那些需要:只有写了 `sources` 的 extension / lib 算。
|
|
57
|
+
* @param manifest 模块清单。
|
|
58
|
+
* @returns 可代装的 need 列表。
|
|
59
|
+
*/
|
|
60
|
+
export function provisionableNeedsOf(manifest: UnitManifest): readonly (ExtensionNeed | LibNeed)[] {
|
|
61
|
+
return needsOf(manifest).filter(
|
|
62
|
+
(need): need is ExtensionNeed | LibNeed =>
|
|
63
|
+
(need.kind === 'extension' || need.kind === 'lib') && (need.sources ?? []).length > 0,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 市场门槛要看的那些需要:声明了 extension / lib 就算(不要求写了来源)。
|
|
69
|
+
* @param manifest 模块清单。
|
|
70
|
+
* @returns extension / lib 的 need 列表。
|
|
71
|
+
*/
|
|
72
|
+
export function supplyNeedsOf(manifest: UnitManifest): readonly (ExtensionNeed | LibNeed)[] {
|
|
73
|
+
return needsOf(manifest).filter(
|
|
74
|
+
(need): need is ExtensionNeed | LibNeed => need.kind === 'extension' || need.kind === 'lib',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { NeedState } from '../contract/unit-capability.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 供给动作的那几档(装 / 升 / 卸,模块、共用依赖与插件各一套);写口按它分派。
|
|
5
|
+
*/
|
|
6
|
+
export type ProvisionAction =
|
|
7
|
+
| 'install'
|
|
8
|
+
| 'update'
|
|
9
|
+
| 'uninstall'
|
|
10
|
+
| 'update-dependency'
|
|
11
|
+
| 'install-bundle'
|
|
12
|
+
| 'install-version'
|
|
13
|
+
| 'update-bundle'
|
|
14
|
+
| 'remove-bundle'
|
|
15
|
+
| 'cancel-install'
|
|
16
|
+
| 'update-plugin'
|
|
17
|
+
| 'uninstall-plugin'
|
|
18
|
+
| 'update-workbench';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 宿主对「这个单元现在能不能用」的那份读数:状态汇总加逐条 need 的细节。
|
|
22
|
+
*/
|
|
23
|
+
export interface CapabilityPayload {
|
|
24
|
+
readonly unit: string;
|
|
25
|
+
readonly status: 'ok' | 'degraded' | 'missing' | 'outdated' | 'unchecked' | 'unsupported' | 'failed';
|
|
26
|
+
readonly needs: readonly NeedState[];
|
|
27
|
+
readonly detail?: string;
|
|
28
|
+
readonly released?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import type { DshHostContext } from '../../api/dsh.js';
|
|
6
|
+
import { loadAdapterSet, resolveAdapterSet } from '../adapter-set.js';
|
|
7
|
+
|
|
8
|
+
const dirs: string[] = [];
|
|
9
|
+
|
|
10
|
+
function profileOf(): string {
|
|
11
|
+
const dir = mkdtempSync(join(tmpdir(), 'wb-adapter-set-'));
|
|
12
|
+
dirs.push(dir);
|
|
13
|
+
writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'fixture-profile', private: true, type: 'module' }));
|
|
14
|
+
return dir;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function install(dir: string, name: string, source: string): void {
|
|
18
|
+
const pkg = join(dir, 'node_modules', name);
|
|
19
|
+
mkdirSync(pkg, { recursive: true });
|
|
20
|
+
writeFileSync(
|
|
21
|
+
join(pkg, 'package.json'),
|
|
22
|
+
JSON.stringify({ name, version: '1.0.0', type: 'module', exports: { '.': './index.js' } }),
|
|
23
|
+
);
|
|
24
|
+
writeFileSync(join(pkg, 'index.js'), source);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function installEsmOnly(dir: string, name: string, source: string): void {
|
|
28
|
+
const pkg = join(dir, 'node_modules', name);
|
|
29
|
+
mkdirSync(pkg, { recursive: true });
|
|
30
|
+
writeFileSync(
|
|
31
|
+
join(pkg, 'package.json'),
|
|
32
|
+
JSON.stringify({ name, version: '1.0.0', type: 'module', exports: { '.': { import: './index.js' } } }),
|
|
33
|
+
);
|
|
34
|
+
writeFileSync(join(pkg, 'index.js'), source);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function installDual(dir: string, name: string, esmSource: string, cjsSource: string): void {
|
|
38
|
+
const pkg = join(dir, 'node_modules', name);
|
|
39
|
+
mkdirSync(pkg, { recursive: true });
|
|
40
|
+
writeFileSync(
|
|
41
|
+
join(pkg, 'package.json'),
|
|
42
|
+
JSON.stringify({
|
|
43
|
+
name,
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
exports: { '.': { import: './index.mjs', require: './index.cjs' } },
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
writeFileSync(join(pkg, 'index.mjs'), esmSource);
|
|
49
|
+
writeFileSync(join(pkg, 'index.cjs'), cjsSource);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ctxOf(dir: string | null): DshHostContext {
|
|
53
|
+
return {
|
|
54
|
+
get: (key: string) => (key === 'profileContext' && dir !== null ? { dir } : undefined),
|
|
55
|
+
} as unknown as DshHostContext;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const FULL = `{
|
|
59
|
+
apiVersion: 1,
|
|
60
|
+
id: 'fixture',
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
data: {
|
|
63
|
+
read: async () => ({ ok: true, data: {} }),
|
|
64
|
+
request: async () => ({ ok: true, data: {} }),
|
|
65
|
+
requestMany: async () => ({}),
|
|
66
|
+
submit: async () => ({ ok: true, data: {} }),
|
|
67
|
+
},
|
|
68
|
+
supply: {
|
|
69
|
+
resolvePluginSource: () => ({ ok: false, problem: 'not-declared' }),
|
|
70
|
+
libInstallCommand: () => '',
|
|
71
|
+
pluginInstallPlan: () => ({ ok: false, problem: 'not-declared' }),
|
|
72
|
+
latestVersion: async () => undefined,
|
|
73
|
+
missingReleaseParts: async () => [],
|
|
74
|
+
probeRelease: async () => undefined,
|
|
75
|
+
splitNpmSpec: () => undefined,
|
|
76
|
+
},
|
|
77
|
+
profile: {
|
|
78
|
+
dirOf: () => null,
|
|
79
|
+
dshPlugins: () => () => ({ ok: true, plugins: [] }),
|
|
80
|
+
selfUpdateView: () => undefined,
|
|
81
|
+
},
|
|
82
|
+
probe: {
|
|
83
|
+
probeUnits: async () => [],
|
|
84
|
+
registerPreflight: () => {},
|
|
85
|
+
declaredPluginCommands: () => new Map(),
|
|
86
|
+
},
|
|
87
|
+
}`;
|
|
88
|
+
|
|
89
|
+
afterEach(() => {
|
|
90
|
+
for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('loadAdapterSet:从 profile 解析 set,失败不抛', () => {
|
|
94
|
+
it('没配置 → not-configured(不是错误)', async () => {
|
|
95
|
+
const loaded = await loadAdapterSet(ctxOf(profileOf()), undefined);
|
|
96
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'not-configured' });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('不是包名 → malformed', async () => {
|
|
100
|
+
const loaded = await loadAdapterSet(ctxOf(profileOf()), './local-set');
|
|
101
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'malformed' });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('没有 profile → no-profile', async () => {
|
|
105
|
+
const loaded = await loadAdapterSet(ctxOf(null), 'fixture-set');
|
|
106
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'no-profile' });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('没有 profile 时先报 no-profile,不先纠结包名', async () => {
|
|
110
|
+
const loaded = await loadAdapterSet(ctxOf(null), './local-set');
|
|
111
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'no-profile' });
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('profile 里没装 → not-resolvable', async () => {
|
|
115
|
+
const loaded = await loadAdapterSet(ctxOf(profileOf()), 'fixture-set');
|
|
116
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'not-resolvable' });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('只导出 import 条件的 ESM 包也能从 profile 解析', async () => {
|
|
120
|
+
const dir = profileOf();
|
|
121
|
+
installEsmOnly(dir, 'esm-set', `export function createAdapterSet() { return ${FULL}; }`);
|
|
122
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'esm-set');
|
|
123
|
+
expect(loaded.ok).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('双条件包走 import 那一份入口,不是 require 的 CJS 面', async () => {
|
|
127
|
+
const dir = profileOf();
|
|
128
|
+
installDual(
|
|
129
|
+
dir,
|
|
130
|
+
'dual-set',
|
|
131
|
+
`export function createAdapterSet() { return ${FULL}; }`,
|
|
132
|
+
'module.exports = { notTheFactory: 1 };',
|
|
133
|
+
);
|
|
134
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'dual-set');
|
|
135
|
+
expect(loaded.ok).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('正常 set → ok,返回 core 成员', async () => {
|
|
139
|
+
const dir = profileOf();
|
|
140
|
+
install(dir, 'fixture-set', `export function createAdapterSet() { return ${FULL}; }`);
|
|
141
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
142
|
+
expect(loaded.ok).toBe(true);
|
|
143
|
+
if (loaded.ok) {
|
|
144
|
+
expect(loaded.spec).toBe('fixture-set');
|
|
145
|
+
expect(loaded.set.data).toBeDefined();
|
|
146
|
+
expect(loaded.set.probe).toBeDefined();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('没有工厂 → bad-export', async () => {
|
|
151
|
+
const dir = profileOf();
|
|
152
|
+
install(dir, 'fixture-set', 'export const nope = 1;');
|
|
153
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
154
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'bad-export' });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('工厂抛错 → factory-failed', async () => {
|
|
158
|
+
const dir = profileOf();
|
|
159
|
+
install(dir, 'fixture-set', 'export function createAdapterSet() { throw new Error("boom"); }');
|
|
160
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
161
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'factory-failed' });
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('协议版本不对 → incompatible-version', async () => {
|
|
165
|
+
const dir = profileOf();
|
|
166
|
+
install(dir, 'fixture-set', `export function createAdapterSet() { return { ...${FULL}, apiVersion: 2 }; }`);
|
|
167
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
168
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'incompatible-version' });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('少成员 → missing-member,点名缺哪个', async () => {
|
|
172
|
+
const dir = profileOf();
|
|
173
|
+
install(
|
|
174
|
+
dir,
|
|
175
|
+
'fixture-set',
|
|
176
|
+
'export function createAdapterSet() { return { apiVersion: 1, id: "fixture", version: "1.0.0", data: { read: async () => ({ ok: true, data: {} }) }, profile: { dirOf: () => null }, probe: { probeUnits: async () => [] } }; }',
|
|
177
|
+
);
|
|
178
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
179
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'missing-member' });
|
|
180
|
+
if (!loaded.ok) expect(loaded.detail).toContain('supply');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('缺 id / version → missing-member,点名', async () => {
|
|
184
|
+
const dir = profileOf();
|
|
185
|
+
install(
|
|
186
|
+
dir,
|
|
187
|
+
'fixture-set',
|
|
188
|
+
'export function createAdapterSet() { return { apiVersion: 1, data: { read: async () => ({ ok: true, data: {} }) }, supply: { resolvePluginSource: () => ({ ok: false, problem: "not-declared" }) }, profile: { dirOf: () => null }, probe: { probeUnits: async () => [] } }; }',
|
|
189
|
+
);
|
|
190
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
191
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'missing-member' });
|
|
192
|
+
if (!loaded.ok) expect(loaded.detail).toContain('id');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('关键方法不是函数 → missing-member', async () => {
|
|
196
|
+
const dir = profileOf();
|
|
197
|
+
install(
|
|
198
|
+
dir,
|
|
199
|
+
'fixture-set',
|
|
200
|
+
'export function createAdapterSet() { return { apiVersion: 1, id: "fixture", version: "1.0.0", data: {}, supply: { resolvePluginSource: () => ({ ok: false, problem: "not-declared" }) }, profile: { dirOf: () => null }, probe: { probeUnits: async () => [] } }; }',
|
|
201
|
+
);
|
|
202
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
203
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'missing-member' });
|
|
204
|
+
if (!loaded.ok) expect(loaded.detail).toContain('data.read');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('完整 set → 不建、不 seed 默认集合', async () => {
|
|
208
|
+
const dir = profileOf();
|
|
209
|
+
install(
|
|
210
|
+
dir,
|
|
211
|
+
'full-set',
|
|
212
|
+
`export function createAdapterSet() { return ${FULL.replace(' probe: {', ' modules: new Proxy({}, { get: () => () => undefined }), runtime: new Proxy({}, { get: () => () => undefined }), plugins: new Proxy({}, { get: () => () => undefined }), actions: new Proxy({}, { get: () => () => undefined }),\n probe: {')}; }`,
|
|
213
|
+
);
|
|
214
|
+
const resolved = await resolveAdapterSet(ctxOf(dir), 'full-set', async () => {
|
|
215
|
+
throw new Error('完整 set 不该建默认');
|
|
216
|
+
});
|
|
217
|
+
expect(resolved.load.ok).toBe(true);
|
|
218
|
+
expect(resolved.usedDefaults).toBe(false);
|
|
219
|
+
expect(resolved.set.id).toBe('fixture');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('入口加载失败 → import-failed', async () => {
|
|
223
|
+
const dir = profileOf();
|
|
224
|
+
install(dir, 'fixture-set', 'export function createAdapterSet( {');
|
|
225
|
+
const loaded = await loadAdapterSet(ctxOf(dir), 'fixture-set');
|
|
226
|
+
expect(loaded).toMatchObject({ ok: false, failure: 'import-failed' });
|
|
227
|
+
});
|
|
228
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { DshHostContext } from '../../api/dsh.js';
|
|
3
|
+
import type { WorkbenchAdapters } from '../adapters.js';
|
|
4
|
+
import { adaptersOf, WORKBENCH_ADAPTERS } from '../adapters.js';
|
|
5
|
+
|
|
6
|
+
describe('适配器端口:apply 期读自己刚 provide 的服务', () => {
|
|
7
|
+
it('严格 get 还看不见时,按非严格读得到', () => {
|
|
8
|
+
const adapters = {} as WorkbenchAdapters;
|
|
9
|
+
const seen: Array<boolean | undefined> = [];
|
|
10
|
+
const ctx = {
|
|
11
|
+
get: (name: string, strict?: boolean) => {
|
|
12
|
+
if (name !== WORKBENCH_ADAPTERS) return undefined;
|
|
13
|
+
seen.push(strict);
|
|
14
|
+
return strict === false ? adapters : undefined;
|
|
15
|
+
},
|
|
16
|
+
} as unknown as DshHostContext;
|
|
17
|
+
|
|
18
|
+
expect(adaptersOf(ctx)).toBe(adapters);
|
|
19
|
+
expect(seen).toEqual([false]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('真的没有 provide 时照旧抛', () => {
|
|
23
|
+
const ctx = { get: () => undefined } as unknown as DshHostContext;
|
|
24
|
+
|
|
25
|
+
expect(() => adaptersOf(ctx)).toThrow('workbenchAdapters 未提供');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
CHANGELOG_FILE,
|
|
4
|
+
appendChangelogEntry,
|
|
5
|
+
changelogHasEntry,
|
|
6
|
+
changelogSectionOf,
|
|
7
|
+
changelogSkeleton,
|
|
8
|
+
parseChangelog,
|
|
9
|
+
} from '../changelog.js';
|
|
10
|
+
|
|
11
|
+
const SAMPLE = [
|
|
12
|
+
'# 更新日志',
|
|
13
|
+
'',
|
|
14
|
+
'## v1.2.0',
|
|
15
|
+
'',
|
|
16
|
+
'`2026-10-26`',
|
|
17
|
+
'',
|
|
18
|
+
'### ✨ 新增',
|
|
19
|
+
'',
|
|
20
|
+
'- 模块详情能看说明与更新日志',
|
|
21
|
+
'',
|
|
22
|
+
'### 🐛 修复',
|
|
23
|
+
'',
|
|
24
|
+
'- 发布审批会显示这一版的更新日志',
|
|
25
|
+
'',
|
|
26
|
+
'## v1.1.0',
|
|
27
|
+
'',
|
|
28
|
+
'`2026-10-01`',
|
|
29
|
+
'',
|
|
30
|
+
'- 旧版本的一条',
|
|
31
|
+
'',
|
|
32
|
+
].join('\n');
|
|
33
|
+
|
|
34
|
+
describe('parseChangelog', () => {
|
|
35
|
+
it('按 v 前缀的版本标题切段,正文保留分类与列表', () => {
|
|
36
|
+
const sections = parseChangelog(SAMPLE);
|
|
37
|
+
expect(sections.map((one) => one.version)).toEqual(['1.2.0', '1.1.0']);
|
|
38
|
+
expect(sections[0]?.body).toContain('`2026-10-26`');
|
|
39
|
+
expect(sections[0]?.body).toContain('### ✨ 新增');
|
|
40
|
+
expect(sections[0]?.body).toContain('- 模块详情能看说明与更新日志');
|
|
41
|
+
expect(sections[1]?.body).toContain('- 旧版本的一条');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('文件名常量是 CHANGELOG.md', () => {
|
|
45
|
+
expect(CHANGELOG_FILE).toBe('CHANGELOG.md');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('changelogSectionOf / changelogHasEntry', () => {
|
|
50
|
+
it('认 v 前缀、方括号与不带方括号的版本标题', () => {
|
|
51
|
+
expect(changelogSectionOf(SAMPLE, 'v1.2.0')).toContain('更新日志');
|
|
52
|
+
expect(changelogSectionOf(SAMPLE, '1.1.0')).toContain('旧版本的一条');
|
|
53
|
+
expect(changelogSectionOf('# 更新日志\n\n## [1.0.0] - 2026-10-01\n\n- 一条\n', '1.0.0')).toBe('- 一条');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('只有版本标题和日期不算一条记录', () => {
|
|
57
|
+
const empty = '# 更新日志\n\n## v1.0.0\n\n`2026-10-26`\n\n### ✨ 新增\n';
|
|
58
|
+
expect(changelogHasEntry(empty, '1.0.0')).toBe(false);
|
|
59
|
+
expect(changelogHasEntry(SAMPLE, '1.2.0')).toBe(true);
|
|
60
|
+
expect(changelogHasEntry(SAMPLE, '9.9.9')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('appendChangelogEntry', () => {
|
|
65
|
+
it('新版本插在最前面,带日期行与 emoji 分类', () => {
|
|
66
|
+
const next = appendChangelogEntry(SAMPLE, { version: '1.3.0', kind: 'added', note: '新的东西', date: '2026-10-27' });
|
|
67
|
+
expect(parseChangelog(next).map((one) => one.version)).toEqual(['1.3.0', '1.2.0', '1.1.0']);
|
|
68
|
+
expect(next).toContain('## v1.3.0');
|
|
69
|
+
expect(next).toContain('`2026-10-27`');
|
|
70
|
+
expect(next).toContain('### ✨ 新增');
|
|
71
|
+
expect(changelogHasEntry(next, '1.3.0')).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('已有版本号往对应分类下追加,不新建一段', () => {
|
|
75
|
+
const next = appendChangelogEntry(SAMPLE, { version: '1.2.0', kind: 'fixed', note: '又修了一条' });
|
|
76
|
+
const sections = parseChangelog(next);
|
|
77
|
+
expect(sections.filter((one) => one.version === '1.2.0')).toHaveLength(1);
|
|
78
|
+
expect(sections[0]?.body).toContain('- 又修了一条');
|
|
79
|
+
expect(sections[0]?.body.indexOf('- 又修了一条')).toBeGreaterThan(
|
|
80
|
+
sections[0]?.body.indexOf('- 发布审批会显示这一版的更新日志') ?? 0,
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('空文件从骨架起,只留标题', () => {
|
|
85
|
+
const next = appendChangelogEntry('', { version: '0.1.0', kind: 'added', note: '第一次发布' });
|
|
86
|
+
expect(next.startsWith('# 更新日志')).toBe(true);
|
|
87
|
+
expect(next).not.toContain('Unreleased');
|
|
88
|
+
expect(parseChangelog(next).map((one) => one.version)).toEqual(['0.1.0']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('这一版还没有该分类时补一个小节', () => {
|
|
92
|
+
const next = appendChangelogEntry(SAMPLE, { version: '1.2.0', kind: 'removed', note: '删掉旧入口' });
|
|
93
|
+
expect(changelogSectionOf(next, '1.2.0')).toContain('### 🗑️ 移除');
|
|
94
|
+
expect(changelogHasEntry(next, '1.2.0')).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('changelogSkeleton', () => {
|
|
99
|
+
it('只有标题,不给假条目', () => {
|
|
100
|
+
expect(changelogSkeleton()).toBe('# 更新日志\n');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { commandNamesMatch } from '../command-name.js';
|
|
3
|
+
|
|
4
|
+
describe('commandNamesMatch:声明的命令名与解析出来的命令路径', () => {
|
|
5
|
+
it('缺省 opencli 不管写成名字还是绝对路径都算同一个', () => {
|
|
6
|
+
expect(commandNamesMatch('opencli', '/Users/me/.dsh/workbench/opencli/bin/opencli')).toBe(true);
|
|
7
|
+
expect(commandNamesMatch('opencli.cmd', 'C:\\dsh\\opencli.cmd')).toBe(true);
|
|
8
|
+
expect(commandNamesMatch('opencli.exe', 'C:\\dsh\\opencli.exe')).toBe(true);
|
|
9
|
+
expect(commandNamesMatch('gitops', '/usr/local/bin/gitops')).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('不同的命令不算同一个', () => {
|
|
13
|
+
expect(commandNamesMatch('gitops', '/usr/local/bin/opencli')).toBe(false);
|
|
14
|
+
expect(commandNamesMatch('', 'opencli')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -14,8 +14,8 @@ const pact = (sessionId: string, now = 1000, unit = 'demo'): RecordPact => ({
|
|
|
14
14
|
workspace: '/w/diy',
|
|
15
15
|
unit,
|
|
16
16
|
name: unit,
|
|
17
|
-
url: 'https://
|
|
18
|
-
title: '环境 ·
|
|
17
|
+
url: 'https://inventory.example.com/env',
|
|
18
|
+
title: '环境 · Inventory',
|
|
19
19
|
action: '发布',
|
|
20
20
|
expect: { method: 'POST', urlContains: '/api/publish' },
|
|
21
21
|
captureFile: '/w/diy/shots/rec-demo.jsonl',
|
|
@@ -34,7 +34,7 @@ describe('录制交接:一台机器一次一场,谁开谁收工', () => {
|
|
|
34
34
|
|
|
35
35
|
expect(state.phase).toBe('armed');
|
|
36
36
|
expect(state.unit).toBe('demo');
|
|
37
|
-
expect(state.url).toBe('https://
|
|
37
|
+
expect(state.url).toBe('https://inventory.example.com/env');
|
|
38
38
|
expect(state.action).toBe('发布');
|
|
39
39
|
expect(state.expect).toEqual({ method: 'POST', urlContains: '/api/publish' });
|
|
40
40
|
expect(state.captureFile).toBe('/w/diy/shots/rec-demo.jsonl');
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { isFactScope, scopePrefixOf } from '../../
|
|
2
|
+
import { isFactScope, scopePrefixOf } from '../../contract/facts.js';
|
|
3
3
|
import { unitFactScope } from '../facts.js';
|
|
4
4
|
|
|
5
5
|
describe('unitFactScope', () => {
|
|
6
6
|
it('命名空间 = 模块 id + `*`("这个模块的数据全作废"只写一个字符串)', () => {
|
|
7
|
-
expect(unitFactScope('
|
|
8
|
-
expect(isFactScope('
|
|
7
|
+
expect(unitFactScope('inventory')).toBe('inventory*');
|
|
8
|
+
expect(isFactScope('inventory*')).toBe(true);
|
|
9
9
|
expect(isFactScope('*')).toBe(false);
|
|
10
|
-
expect(isFactScope('
|
|
11
|
-
expect(scopePrefixOf('
|
|
10
|
+
expect(isFactScope('inventory/summary')).toBe(false);
|
|
11
|
+
expect(scopePrefixOf('inventory*')).toBe('inventory');
|
|
12
12
|
});
|
|
13
13
|
});
|