@dsh-workbench/shared 0.3.10 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +19 -1
- package/dist/api/host-write.d.ts +0 -15
- package/dist/api/host-write.js +1 -35
- 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/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 -7
- package/dist/components/icons.js +11 -16
- package/dist/config/constants.d.ts +2 -2
- package/dist/config/constants.js +2 -2
- package/dist/config/storage.d.ts +0 -4
- package/dist/config/storage.js +0 -11
- package/dist/contract/adapters.d.ts +10 -0
- package/dist/contract/cli.d.ts +3 -0
- package/dist/contract/data.d.ts +126 -0
- package/dist/{api → contract}/facts.d.ts +12 -3
- package/dist/{api → contract}/facts.js +19 -1
- 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.d.ts +18 -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/{api → 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 +243 -0
- package/dist/host/adapter-types.d.ts +79 -0
- package/dist/host/adapter-types.js +23 -0
- package/dist/host/adapters.d.ts +22 -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/facts.d.ts +1 -1
- package/dist/host/facts.js +3 -2
- package/dist/host/git.js +2 -2
- package/dist/host/http.js +1 -1
- 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 +420 -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 +191 -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/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-approval.d.ts +12 -0
- package/dist/host/tool-approval.js +4 -0
- package/dist/host/tool-kit.js +1 -1
- package/dist/host/unit-face.d.ts +5 -4
- package/dist/host/version-range.d.ts +2 -0
- package/dist/host/version-range.js +73 -0
- package/dist/host/version.d.ts +3 -0
- package/dist/host/version.js +29 -0
- package/dist/host.d.ts +47 -16
- package/dist/host.js +47 -16
- package/dist/i18n/en.js +259 -86
- package/dist/i18n/index.d.ts +333 -88
- 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 +332 -87
- package/dist/i18n/zh.js +260 -87
- package/dist/index.d.ts +24 -8
- package/dist/index.js +24 -8
- package/dist/lib/blocker-action.d.ts +0 -15
- package/dist/lib/blocker-action.js +1 -25
- package/dist/{api → lib}/dsh-client.d.ts +12 -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-types.d.ts +5 -0
- package/dist/lib/react-types.js +1 -0
- package/dist/lib/react.d.ts +1 -40
- package/dist/lib/react.js +0 -36
- package/dist/lib/ui.js +3 -9
- 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 +18 -2
- package/src/api/__tests__/blocker-table.test.ts +135 -86
- package/src/api/blocker-table.ts +52 -26
- package/src/api/dsh.ts +17 -1
- package/src/api/host-write.ts +0 -43
- 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.tsx +1 -1
- package/src/components/FormActions.tsx +1 -1
- 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 -24
- package/src/config/constants.ts +2 -2
- package/src/config/storage.test.ts +1 -25
- package/src/config/storage.ts +0 -12
- 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/data.ts +145 -0
- package/src/{api → contract}/facts.ts +34 -4
- 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/contract/unit-board.ts +23 -0
- 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 +298 -0
- package/src/host/adapter-types.ts +105 -0
- package/src/host/adapters.ts +44 -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/facts.ts +4 -3
- package/src/host/git.ts +2 -2
- package/src/host/http.ts +1 -1
- 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 +413 -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 +222 -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/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-approval.ts +17 -0
- package/src/host/tool-kit.ts +1 -1
- package/src/host/unit-face.ts +5 -4
- package/src/host/version-range.ts +71 -0
- package/src/host/version.ts +30 -0
- package/src/host.ts +47 -16
- package/src/i18n/en.ts +261 -86
- 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 +263 -87
- package/src/index.ts +24 -8
- package/src/lib/blocker-action.ts +0 -28
- package/src/{api → lib}/dsh-client.ts +16 -2
- package/src/lib/market-index.ts +36 -0
- package/src/lib/react-types.ts +5 -0
- package/src/lib/react.ts +1 -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-board.d.ts +0 -39
- package/dist/api/unit-board.js +0 -45
- 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.d.ts +0 -65
- 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__/host-write.test.ts +0 -75
- package/src/api/__tests__/unit-board.test.ts +0 -76
- package/src/api/__tests__/unit.test.ts +0 -38
- package/src/api/unit-board.ts +0 -69
- 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/src/lib/blocker-action.test.ts +0 -40
- /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/workbench-contract.js → contract/cli.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/src/{api → contract}/blocker.ts +0 -0
- /package/src/{api → contract}/long-task.ts +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
.wb-choiceMenu {
|
|
3
2
|
display: flex;
|
|
4
3
|
flex-direction: column;
|
|
@@ -180,4 +179,4 @@
|
|
|
180
179
|
.wb-radio:focus-visible {
|
|
181
180
|
outline: var(--dsw-focus-ring-width, var(--wb-len-2)) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
182
181
|
outline-offset: 2px;
|
|
183
|
-
}
|
|
182
|
+
}
|
|
@@ -29,7 +29,7 @@ export interface ConfirmBarProps {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* 「先问一句再动手」那一条:标题 + 说明 + 键值 + 失败那句 + 动作区,形状一次收口。
|
|
32
|
-
*
|
|
32
|
+
* **要不要问**归调用点,这一件只回答"问的时候长什么样"。
|
|
33
33
|
* @param props.title - 那一句问话("发布到 prod?")。
|
|
34
34
|
* @param props.hint - 标题下面那句说明或正文;不给不占位。
|
|
35
35
|
* @param props.rows - 键值两格(键值列宽与分隔线归 `Rows` / `Row`);不给或空数组不渲染。值那一格可以放控件。
|
package/src/components/Form.css
CHANGED
package/src/components/Form.tsx
CHANGED
|
@@ -46,7 +46,7 @@ function reportExtraPrimaries(actions: readonly FormAction[]): void {
|
|
|
46
46
|
if (keys.length < 2) return;
|
|
47
47
|
console.error(
|
|
48
48
|
`[dsh-workbench] FormActions 一个区里出现了 ${keys.length} 颗 primary(${keys.join('、')})—— ` +
|
|
49
|
-
'
|
|
49
|
+
'一个区只许一颗主操作:多余的这颗该改成 default',
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { longTaskDot, longTaskStopPress, longTaskStopStage, LONG_TASK_STOP_ARM_MS, type LongTaskStatus } from '../
|
|
1
|
+
import { longTaskDot, longTaskStopPress, longTaskStopStage, LONG_TASK_STOP_ARM_MS, type LongTaskStatus } from '../contract/long-task.js';
|
|
2
2
|
import { Link } from './Link.js';
|
|
3
3
|
import { Fragment, h, useEffect, useState } from '../lib/react.js';
|
|
4
4
|
import {
|
package/src/components/Note.css
CHANGED
package/src/components/Stack.css
CHANGED
|
@@ -43,7 +43,7 @@ describe('两按停止钮:接线(这仓的组件测试是 node 环境,读
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('★★ 上膛后 3 秒自己落膛:用 LONG_TASK_STOP_ARM_MS,卸载 / 落膛都清掉计时器', () => {
|
|
46
|
-
expect(ROW).toContain("from '../
|
|
46
|
+
expect(ROW).toContain("from '../contract/long-task.js'");
|
|
47
47
|
expect(ROW).toContain('setTimeout(() => setArmed(false), LONG_TASK_STOP_ARM_MS)');
|
|
48
48
|
expect(ROW).toContain('clearTimeout(timer)');
|
|
49
49
|
expect(ROW).toContain('}, [armed]);');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { SessionsSnapshot, Translate, WorkspaceSnapshot, WorkspaceView } from '../
|
|
3
|
-
import type { UnitManifest } from '../
|
|
4
|
-
import type { CapabilityPayload } from '../
|
|
2
|
+
import type { SessionsSnapshot, Translate, WorkspaceSnapshot, WorkspaceView } from '../lib/dsh-client.js';
|
|
3
|
+
import type { UnitManifest } from '../contract/unit.js';
|
|
4
|
+
import type { CapabilityPayload } from '../contract/workbench-contract.js';
|
|
5
5
|
import type { Dictionaries } from '../i18n/index.js';
|
|
6
6
|
|
|
7
7
|
/**
|
package/src/components/icons.tsx
CHANGED
|
@@ -51,20 +51,28 @@ export function IconConversation({ size = 16 }: IconProps) {
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
const BRAND_FRAME = 'M10 6H54A5 5 0 0 1 59 11V43A5 5 0 0 1 54 48H10A5 5 0 0 1 5 43V11A5 5 0 0 1 10 6Z M12 10H52A3 3 0 0 1 55 13V41A3 3 0 0 1 52 44H12A3 3 0 0 1 9 41V13A3 3 0 0 1 12 10Z';
|
|
55
|
+
|
|
56
|
+
const BRAND_TAIL =
|
|
57
|
+
'M8.844 13.742C8.967 12.328 8.45 10.4 8.45 9.65C8.45 8.94 8.88 8.43 9.6 8.43C11.285 8.43 12.106 8.281 12.685 8.104C13.71 7.791 14.585 6.768 15.055 5.945C15.137 5.803 14.99 5.641 14.829 5.671C13.829 5.86 12.828 5.376 11.827 4.978C10.659 4.514 9.491 4.707 8.935 4.876C8.805 4.915 8.658 4.819 8.636 4.686C8.468 3.643 7.405 2.615 5.498 2.238C4.54 2.048 3.748 1.574 3.347 1.202C3.252 1.113 3.088 1.125 3.03 1.242C2.628 2.059 2.168 3.82 5.248 6.115C5.82 6.494 6.31 6.785 6.574 7.637C6.72 8.104 6.157 9.168 6.061 9.368C5.157 11.27 5.089 12.19 4.926 13.742Z';
|
|
58
|
+
|
|
59
|
+
const BRAND_TAIL_TRANSFORM = 'translate(14.6263 10.5853) scale(1.95)';
|
|
60
|
+
|
|
54
61
|
/**
|
|
55
|
-
*
|
|
62
|
+
* 品牌标:一台显示器里放着 dsh 的鲸鱼尾巴;默认 24px(它是品牌物,别拿它当通用图标)。
|
|
56
63
|
* @param props.size - 边长像素,同时给 `width` 与 `height`。
|
|
57
64
|
* @returns 一枚 `aria-hidden` 的 svg。
|
|
58
65
|
*/
|
|
59
66
|
export function IconBrandMark({ size = 24 }: IconProps) {
|
|
60
67
|
return (
|
|
61
|
-
<Shell size={size}
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
<Shell size={size} viewBox="0 0 64 64">
|
|
69
|
+
<path d={BRAND_FRAME} fill="currentColor" fillRule="evenodd" />
|
|
70
|
+
<rect x={27.5} y={48} width={9} height={4.75} fill="currentColor" />
|
|
71
|
+
<rect x={19} y={52.75} width={26} height={4.75} rx={2.375} fill="currentColor" />
|
|
72
|
+
<g transform={BRAND_TAIL_TRANSFORM}>
|
|
73
|
+
<path d={BRAND_TAIL} fill="currentColor" />
|
|
74
|
+
<circle cx={6.885} cy={13.742} r={1.95} fill="currentColor" />
|
|
75
|
+
</g>
|
|
68
76
|
</Shell>
|
|
69
77
|
);
|
|
70
78
|
}
|
|
@@ -117,22 +125,6 @@ export function IconNpm({ size = 16 }: IconProps) {
|
|
|
117
125
|
);
|
|
118
126
|
}
|
|
119
127
|
|
|
120
|
-
/**
|
|
121
|
-
* 四宫格:"模块"这个概念的通用单色图标(侧栏、面板那颗钮、市场节的标题);默认 16px。
|
|
122
|
-
* @param props.size - 边长像素,同时给 `width` 与 `height`。
|
|
123
|
-
* @returns 一枚 `aria-hidden` 的 svg。
|
|
124
|
-
*/
|
|
125
|
-
export function IconModules({ size = 16 }: IconProps) {
|
|
126
|
-
return (
|
|
127
|
-
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
128
|
-
<rect x="2.5" y="2.5" width="4.6" height="4.6" rx="1.2" stroke="currentColor" strokeWidth="1.2" />
|
|
129
|
-
<rect x="8.9" y="2.5" width="4.6" height="4.6" rx="1.2" stroke="currentColor" strokeWidth="1.2" />
|
|
130
|
-
<rect x="2.5" y="8.9" width="4.6" height="4.6" rx="1.2" stroke="currentColor" strokeWidth="1.2" />
|
|
131
|
-
<rect x="8.9" y="8.9" width="4.6" height="4.6" rx="1.2" stroke="currentColor" strokeWidth="1.2" />
|
|
132
|
-
</svg>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
128
|
/**
|
|
137
129
|
* 模块类型标:渐变方块 + 白四宫格,用在"这是个模块"的行首;品牌物,颜色不跟主题走。默认 16px。
|
|
138
130
|
* @param props.size - 边长像素,同时给 `width` 与 `height`。
|
package/src/config/constants.ts
CHANGED
|
@@ -40,8 +40,8 @@ export const PANEL_STATE_PREFIX = `${API_PREFIX}/${PANEL_STATE_PATH}`;
|
|
|
40
40
|
/** 钉住的 opencli 插件版本:`opencli-runtime` 按它校验、取那一份。 */
|
|
41
41
|
export const OPENCLI_PIN = "1.8.8";
|
|
42
42
|
|
|
43
|
-
/** 缺陷看板套件的 dsh 服务名(`
|
|
44
|
-
export const
|
|
43
|
+
/** 缺陷看板套件的 dsh 服务名(`workbenchIssues`):套件照它登记自己那份服务。 */
|
|
44
|
+
export const ISSUES_SERVICE = "workbenchIssues";
|
|
45
45
|
|
|
46
46
|
/** 面板套件的 dsh 服务名(`workbenchPanels`):宿主经它取套件公布的清单与订阅。 */
|
|
47
47
|
export const PANELS_SERVICE = "workbenchPanels";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { readText, writeText } from './storage.js';
|
|
3
3
|
|
|
4
4
|
function fakeStorage(options: { throwOnWrite?: boolean } = {}) {
|
|
5
5
|
const data = new Map<string, string>();
|
|
@@ -44,30 +44,6 @@ describe('storage', () => {
|
|
|
44
44
|
expect(fake.data.has('k')).toBe(false);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
it('没存过时读回 null / 默认值', () => {
|
|
48
|
-
useStorage();
|
|
49
|
-
expect(readText('missing')).toBeNull();
|
|
50
|
-
expect(readFlag('missing', true)).toBe(true);
|
|
51
|
-
expect(readFlag('missing', false)).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('布尔开关按字面量往返', () => {
|
|
55
|
-
useStorage();
|
|
56
|
-
writeFlag('on', true);
|
|
57
|
-
writeFlag('off', false);
|
|
58
|
-
expect(readFlag('on', false)).toBe(true);
|
|
59
|
-
expect(readFlag('off', true)).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('只有字面量 "true" 算真:脏数据不因为"非空"就把开关打开', () => {
|
|
63
|
-
useStorage();
|
|
64
|
-
writeText('weird', 'yes');
|
|
65
|
-
writeText('one', '1');
|
|
66
|
-
expect(readFlag('weird', true)).toBe(false);
|
|
67
|
-
expect(readFlag('one', true)).toBe(false);
|
|
68
|
-
expect(readFlag('never', true)).toBe(true);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
47
|
it('localStorage 抛异常时静默降级(隐私模式不该让面板崩掉)', () => {
|
|
72
48
|
useStorage({ throwOnWrite: true });
|
|
73
49
|
expect(() => writeText('k', 'v')).not.toThrow();
|
package/src/config/storage.ts
CHANGED
|
@@ -15,15 +15,3 @@ export function writeText(key: string, value: string | null): void {
|
|
|
15
15
|
} catch {
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
/** 读一个布尔开关:键不存在时返回 `fallback`,只有存进去的 `'true'` 算真。 */
|
|
20
|
-
export function readFlag(key: string, fallback: boolean): boolean {
|
|
21
|
-
const raw = readText(key);
|
|
22
|
-
if (raw === null) return fallback;
|
|
23
|
-
return raw === 'true';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** 把一个布尔开关写进 localStorage(存 `'true'` / `'false'`);失败静默。 */
|
|
27
|
-
export function writeFlag(key: string, value: boolean): void {
|
|
28
|
-
writeText(key, value ? 'true' : 'false');
|
|
29
|
-
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
parseFactFrame,
|
|
6
6
|
type FactFrame,
|
|
7
7
|
type FactTransport,
|
|
8
|
+
type FactTransportMode,
|
|
8
9
|
} from '../facts.js';
|
|
9
10
|
|
|
10
11
|
function fakeTransport(): {
|
|
@@ -52,17 +53,17 @@ describe('createFactChannel', () => {
|
|
|
52
53
|
it('★ 之后的帧只惊动它点名的事实(别的读者不该白跑一次)', () => {
|
|
53
54
|
const wire = fakeTransport();
|
|
54
55
|
const channel = createFactChannel({ transport: wire.transport });
|
|
55
|
-
const
|
|
56
|
+
const inventory = vi.fn();
|
|
56
57
|
const issues = vi.fn();
|
|
57
|
-
channel.subscribe('
|
|
58
|
+
channel.subscribe('inventory/summary', inventory);
|
|
58
59
|
channel.subscribe('issues', issues);
|
|
59
60
|
channel.applyFrame({ epoch: 'e1', rev: 1, facts: [] });
|
|
60
61
|
|
|
61
|
-
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['
|
|
62
|
+
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['inventory/summary'] });
|
|
62
63
|
|
|
63
|
-
expect(
|
|
64
|
+
expect(inventory).toHaveBeenCalledTimes(1);
|
|
64
65
|
expect(issues).not.toHaveBeenCalled();
|
|
65
|
-
expect(channel.revisionOf('
|
|
66
|
+
expect(channel.revisionOf('inventory/summary')).toBe(2);
|
|
66
67
|
expect(channel.revisionOf('issues')).toBe(0);
|
|
67
68
|
});
|
|
68
69
|
|
|
@@ -132,33 +133,33 @@ describe('createFactChannel', () => {
|
|
|
132
133
|
const summary = vi.fn();
|
|
133
134
|
const builds = vi.fn();
|
|
134
135
|
const issues = vi.fn();
|
|
135
|
-
channel.subscribe('
|
|
136
|
-
channel.subscribe('
|
|
137
|
-
channel.subscribe('
|
|
136
|
+
channel.subscribe('inventory/summary', summary);
|
|
137
|
+
channel.subscribe('inventory/builds?env=innettest', builds);
|
|
138
|
+
channel.subscribe('issues/list', issues);
|
|
138
139
|
channel.applyFrame({ epoch: 'e1', rev: 1, facts: [] });
|
|
139
140
|
|
|
140
|
-
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['capability', 'panels', '
|
|
141
|
+
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['capability', 'panels', 'inventory*'] });
|
|
141
142
|
|
|
142
143
|
expect(summary).toHaveBeenCalledTimes(1);
|
|
143
144
|
expect(builds).toHaveBeenCalledTimes(1);
|
|
144
145
|
expect(issues).not.toHaveBeenCalled();
|
|
145
|
-
expect(channel.revisionOf('
|
|
146
|
+
expect(channel.revisionOf('inventory/summary')).toBeGreaterThan(1);
|
|
146
147
|
});
|
|
147
148
|
|
|
148
149
|
it('命名空间不许凭空造出订阅(订阅表只由读者写),也不许越界到别的模块', () => {
|
|
149
150
|
const wire = fakeTransport();
|
|
150
151
|
const channel = createFactChannel({ transport: wire.transport });
|
|
151
|
-
const
|
|
152
|
+
const demoAnalysis = vi.fn();
|
|
152
153
|
const lookalike = vi.fn();
|
|
153
|
-
channel.subscribe('
|
|
154
|
-
channel.subscribe('
|
|
154
|
+
channel.subscribe('analysis/summary', demoAnalysis);
|
|
155
|
+
channel.subscribe('inventory-extra/summary', lookalike);
|
|
155
156
|
channel.applyFrame({ epoch: 'e1', rev: 1, facts: [] });
|
|
156
157
|
|
|
157
|
-
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['
|
|
158
|
+
channel.applyFrame({ epoch: 'e1', rev: 2, facts: ['inventory*'] });
|
|
158
159
|
|
|
159
|
-
expect(
|
|
160
|
+
expect(demoAnalysis).not.toHaveBeenCalled();
|
|
160
161
|
expect(lookalike).not.toHaveBeenCalled();
|
|
161
|
-
expect(channel.revisionOf('
|
|
162
|
+
expect(channel.revisionOf('inventory/never-subscribed')).toBe(0);
|
|
162
163
|
expect(channel.subscriberCount()).toBe(2);
|
|
163
164
|
});
|
|
164
165
|
|
|
@@ -166,26 +167,26 @@ describe('createFactChannel', () => {
|
|
|
166
167
|
const wire = fakeTransport();
|
|
167
168
|
const channel = createFactChannel({ transport: wire.transport });
|
|
168
169
|
const listener = vi.fn();
|
|
169
|
-
channel.subscribe('
|
|
170
|
+
channel.subscribe('inventory/builds?env=innettest', listener);
|
|
170
171
|
|
|
171
|
-
channel.applyFrame({ epoch: 'e1', rev: 4, facts: ['
|
|
172
|
+
channel.applyFrame({ epoch: 'e1', rev: 4, facts: ['inventory*'] });
|
|
172
173
|
|
|
173
174
|
expect(listener).not.toHaveBeenCalled();
|
|
174
|
-
expect(channel.revisionOf('
|
|
175
|
+
expect(channel.revisionOf('inventory/builds?env=innettest')).toBeGreaterThan(0);
|
|
175
176
|
});
|
|
176
177
|
|
|
177
178
|
it('refresh(命名空间) 只惊动它下面的事实(本地失效也按模块点名,不是一律全量)', () => {
|
|
178
179
|
const wire = fakeTransport();
|
|
179
180
|
const channel = createFactChannel({ transport: wire.transport });
|
|
180
|
-
const
|
|
181
|
+
const inventory = vi.fn();
|
|
181
182
|
const issues = vi.fn();
|
|
182
|
-
channel.subscribe('
|
|
183
|
-
channel.subscribe('
|
|
183
|
+
channel.subscribe('inventory/summary', inventory);
|
|
184
|
+
channel.subscribe('issues/list', issues);
|
|
184
185
|
channel.applyFrame({ epoch: 'e1', rev: 1, facts: [] });
|
|
185
186
|
|
|
186
|
-
channel.refresh('
|
|
187
|
+
channel.refresh('inventory*');
|
|
187
188
|
|
|
188
|
-
expect(
|
|
189
|
+
expect(inventory).toHaveBeenCalledTimes(1);
|
|
189
190
|
expect(issues).not.toHaveBeenCalled();
|
|
190
191
|
});
|
|
191
192
|
|
|
@@ -204,6 +205,53 @@ describe('createFactChannel', () => {
|
|
|
204
205
|
|
|
205
206
|
expect(seen).toEqual([4]);
|
|
206
207
|
});
|
|
208
|
+
|
|
209
|
+
it('★ 传输来路:没建连是 null,报上来就换档,同一档不重复惊动', () => {
|
|
210
|
+
const wire: { report: ((mode: FactTransportMode) => void) | null } = { report: null };
|
|
211
|
+
const channel = createFactChannel({
|
|
212
|
+
transport: {
|
|
213
|
+
start(handlers) {
|
|
214
|
+
wire.report = handlers.onMode ?? null;
|
|
215
|
+
return () => {};
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
const listener = vi.fn();
|
|
220
|
+
expect(channel.mode()).toBeNull();
|
|
221
|
+
channel.subscribeMode(listener);
|
|
222
|
+
channel.subscribe('panels', vi.fn());
|
|
223
|
+
|
|
224
|
+
wire.report?.('polling');
|
|
225
|
+
expect(channel.mode()).toBe('polling');
|
|
226
|
+
wire.report?.('polling');
|
|
227
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
228
|
+
|
|
229
|
+
wire.report?.('sse');
|
|
230
|
+
expect(channel.mode()).toBe('sse');
|
|
231
|
+
expect(listener).toHaveBeenCalledTimes(2);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('★ 最后一个订阅走掉、传输停了,来路回到没建连', () => {
|
|
235
|
+
const wire: { report: ((mode: FactTransportMode) => void) | null } = { report: null };
|
|
236
|
+
const channel = createFactChannel({
|
|
237
|
+
transport: {
|
|
238
|
+
start(handlers) {
|
|
239
|
+
wire.report = handlers.onMode ?? null;
|
|
240
|
+
return () => {};
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
const listener = vi.fn();
|
|
245
|
+
channel.subscribeMode(listener);
|
|
246
|
+
const off = channel.subscribe('panels', vi.fn());
|
|
247
|
+
wire.report?.('sse');
|
|
248
|
+
listener.mockClear();
|
|
249
|
+
|
|
250
|
+
off();
|
|
251
|
+
|
|
252
|
+
expect(channel.mode()).toBeNull();
|
|
253
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
254
|
+
});
|
|
207
255
|
});
|
|
208
256
|
|
|
209
257
|
describe('parseFactFrame', () => {
|
|
@@ -255,7 +303,7 @@ describe('parseFactFrame', () => {
|
|
|
255
303
|
|
|
256
304
|
describe('factOfRoute', () => {
|
|
257
305
|
it('事实名就是路由名(不另起一套词汇,读与订阅不可能各说各话)', () => {
|
|
258
|
-
expect(factOfRoute('
|
|
306
|
+
expect(factOfRoute('inventory/summary')).toBe('inventory/summary');
|
|
259
307
|
expect(factOfRoute('capability')).toBe('capability');
|
|
260
308
|
});
|
|
261
309
|
});
|
|
@@ -13,16 +13,16 @@ import {
|
|
|
13
13
|
|
|
14
14
|
describe('runsRouteOf', () => {
|
|
15
15
|
it('★ 事实名 = 路由名(三处同一个来源,改一处不可能漏另一处)', () => {
|
|
16
|
-
expect(runsRouteOf('
|
|
16
|
+
expect(runsRouteOf('inventory')).toBe('inventory/runs');
|
|
17
17
|
expect(runsRouteOf('issues')).toBe('issues/runs');
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
describe('stopRouteOf(停止那条写路由)', () => {
|
|
22
22
|
it('★ 就是读路由 + `/stop`(读口与写口同一个单元名,不另造一套命名)', () => {
|
|
23
|
-
expect(stopRouteOf('
|
|
23
|
+
expect(stopRouteOf('inventory')).toBe('inventory/runs/stop');
|
|
24
24
|
expect(stopRouteOf('issues')).toBe('issues/runs/stop');
|
|
25
|
-
expect(stopRouteOf('
|
|
25
|
+
expect(stopRouteOf('inventory')).toBe(`${runsRouteOf('inventory')}/stop`);
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
NEED_KINDS,
|
|
4
|
+
NEED_KIND_SPECS,
|
|
5
|
+
SUPPLY_SOURCE_KINDS,
|
|
6
|
+
needProblemsOf,
|
|
7
|
+
needsProblemsOf,
|
|
8
|
+
} from '../needs.js';
|
|
9
|
+
|
|
10
|
+
const npm = { kind: 'npm', package: '@acme/plugin-demo', registryDefault: 'https://registry.test/' };
|
|
11
|
+
const base = { kind: 'lib', name: 'demo', purpose: '测试', sources: [npm] };
|
|
12
|
+
|
|
13
|
+
function errorsOf(need: unknown): readonly string[] {
|
|
14
|
+
return needProblemsOf('demo', need, 0).errors;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('needs 校验矩阵', () => {
|
|
18
|
+
it('★ 四种 kind 的最小声明都过;注册表与 NEED_KINDS 一一对应', () => {
|
|
19
|
+
expect(
|
|
20
|
+
needsProblemsOf('demo', [
|
|
21
|
+
{ kind: 'bin', name: 'gitops', purpose: '部署' },
|
|
22
|
+
{ kind: 'extension', runtime: 'opencli', name: 'demo', purpose: '面板', sources: [npm] },
|
|
23
|
+
base,
|
|
24
|
+
{ kind: 'env', name: 'DSH_TOKEN', secret: true, purpose: '只读接口' },
|
|
25
|
+
]).errors,
|
|
26
|
+
).toEqual([]);
|
|
27
|
+
expect(Object.keys(NEED_KIND_SPECS).sort()).toEqual([...NEED_KINDS].sort());
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('未知 kind / 未知 source.kind → 红,并点名', () => {
|
|
31
|
+
const unknownKind = errorsOf({ kind: 'mcp', name: 'x', purpose: '测试' });
|
|
32
|
+
expect(unknownKind.join(' ')).toContain('needs[0].kind 不认识');
|
|
33
|
+
const unknownSource = errorsOf({ ...base, sources: [{ kind: 'tarball', url: 'x' }] });
|
|
34
|
+
expect(unknownSource.join(' ')).toContain('sources[0].kind 不认识');
|
|
35
|
+
expect(new Set(Object.values(NEED_KIND_SPECS).flatMap((spec) => spec.sources))).toEqual(
|
|
36
|
+
new Set(SUPPLY_SOURCE_KINDS),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('bin / env 写了 sources → 红(没有安装路径)', () => {
|
|
41
|
+
expect(errorsOf({ kind: 'bin', name: 'x', purpose: '测试', sources: [npm] }).join(' ')).toContain(
|
|
42
|
+
'不支持 sources',
|
|
43
|
+
);
|
|
44
|
+
expect(errorsOf({ kind: 'env', name: 'X', purpose: '测试', sources: [npm] }).join(' ')).toContain(
|
|
45
|
+
'不支持 sources',
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('lib 只认 npm 源;sources 空数组 → 红(不代装就整条删掉)', () => {
|
|
50
|
+
expect(errorsOf({ ...base, sources: [{ kind: 'git', url: 'github:acme/demo' }] }).join(' ')).toContain(
|
|
51
|
+
'只支持 npm',
|
|
52
|
+
);
|
|
53
|
+
expect(errorsOf({ ...base, sources: [] }).join(' ')).toContain('空数组');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('必填项:purpose、extension.runtime、name、required 类型、id 空串', () => {
|
|
57
|
+
expect(errorsOf({ ...base, purpose: '' }).join(' ')).toContain('purpose 没写');
|
|
58
|
+
expect(errorsOf({ kind: 'extension', name: 'x', purpose: '测试' }).join(' ')).toContain('runtime 没写');
|
|
59
|
+
expect(errorsOf({ ...base, name: '' }).join(' ')).toContain('name 没写');
|
|
60
|
+
expect(errorsOf({ ...base, required: 'yes' }).join(' ')).toContain('required 要是布尔');
|
|
61
|
+
expect(errorsOf({ ...base, id: '' }).join(' ')).toContain('id 是空串');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('★ need 内未知字段只是警告;x- 前缀直接忽略(不为可选字段升协议版本)', () => {
|
|
65
|
+
const read = needProblemsOf('demo', { ...base, future: 1, 'x-vendor': true }, 0);
|
|
66
|
+
expect(read.errors).toEqual([]);
|
|
67
|
+
expect(read.warnings.join(' ')).toContain('不认识的字段 future');
|
|
68
|
+
expect(read.warnings.join(' ')).not.toContain('x-vendor');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('没写 needs → 空表不报', () => {
|
|
72
|
+
expect(needsProblemsOf('demo', undefined)).toEqual({ errors: [], warnings: [] });
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Need } from '../needs.js';
|
|
3
|
+
import { needsOf, provisionableNeedsOf, type UnitManifest } from '../unit.js';
|
|
4
|
+
|
|
5
|
+
function manifest(needs?: readonly Need[]): UnitManifest {
|
|
6
|
+
return {
|
|
7
|
+
id: 'issues',
|
|
8
|
+
card: { titleKey: 'issues.mine', order: 10 },
|
|
9
|
+
tools: [],
|
|
10
|
+
host: { inject: [] },
|
|
11
|
+
...(needs === undefined ? {} : { needs }),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const npm = { kind: 'npm' as const, registryEnv: 'WB_NPM', registryDefault: 'https://registry.example/' };
|
|
16
|
+
|
|
17
|
+
describe('provisionableNeedsOf(缺来源时那条回退口径,宿主与浏览器共用一份)', () => {
|
|
18
|
+
it('写了 sources 的 extension / lib → 宿主可代装', () => {
|
|
19
|
+
const needs: Need[] = [
|
|
20
|
+
{ kind: 'extension', runtime: 'opencli', name: 'demo', purpose: '测试', sources: [npm] },
|
|
21
|
+
{ kind: 'lib', name: 'opencli-workflow', purpose: '测试', sources: [npm] },
|
|
22
|
+
];
|
|
23
|
+
expect(provisionableNeedsOf(manifest(needs)).map((need) => need.name)).toEqual([
|
|
24
|
+
'demo',
|
|
25
|
+
'opencli-workflow',
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('★ 没写 sources 的 extension、bin、env 都不算供给 —— 它们不是市场门槛,只是「我自己装,你帮我判」', () => {
|
|
30
|
+
const needs: Need[] = [
|
|
31
|
+
{ kind: 'extension', runtime: 'opencli', name: 'demo', purpose: '测试' },
|
|
32
|
+
{ kind: 'bin', name: 'gitops', purpose: '测试' },
|
|
33
|
+
{ kind: 'env', name: 'DSH_TOKEN', purpose: '测试' },
|
|
34
|
+
];
|
|
35
|
+
expect(provisionableNeedsOf(manifest(needs))).toEqual([]);
|
|
36
|
+
expect(provisionableNeedsOf(manifest([]))).toEqual([]);
|
|
37
|
+
expect(provisionableNeedsOf(manifest())).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('needsOf 没写就是空表', () => {
|
|
41
|
+
expect(needsOf(manifest())).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
});
|