@dsh-workbench/shared 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/context-ref.d.ts +0 -12
- package/dist/api/context-ref.js +0 -40
- package/dist/api/facts.d.ts +0 -6
- package/dist/api/facts.js +0 -8
- package/dist/api/long-task.d.ts +8 -0
- package/dist/api/long-task.js +10 -0
- package/dist/components/ConfirmBar.css +26 -0
- package/dist/components/ConfirmBar.d.ts +36 -0
- package/dist/components/ConfirmBar.js +26 -0
- package/dist/components/Form.d.ts +7 -5
- package/dist/components/Form.js +5 -3
- package/dist/components/FormNodes.d.ts +17 -13
- package/dist/components/FormNodes.js +13 -5
- package/dist/components/FormTypes.d.ts +39 -14
- package/dist/components/MockStrip.css +11 -1
- package/dist/components/StateDot.d.ts +5 -0
- package/dist/components/StateDot.js +10 -0
- package/dist/config/constants.d.ts +2 -0
- package/dist/config/constants.js +2 -0
- package/dist/host/__tests__/fakes.d.ts +41 -0
- package/dist/host/__tests__/fakes.js +76 -0
- package/dist/host/__tests__/preflight-fixtures.d.ts +19 -0
- package/dist/host/__tests__/preflight-fixtures.js +47 -0
- package/dist/host/board-fact.d.ts +8 -0
- package/dist/host/board-fact.js +16 -0
- package/dist/host/browser-read.js +2 -1
- package/dist/host/context-inject.d.ts +24 -0
- package/dist/host/context-inject.js +74 -0
- package/dist/host/context-ref.d.ts +13 -0
- package/dist/host/context-ref.js +41 -0
- package/dist/host/diy-debug.d.ts +80 -0
- package/dist/host/diy-debug.js +88 -0
- package/dist/host/diy-record.d.ts +135 -0
- package/dist/host/diy-record.js +162 -0
- package/dist/host/events.d.ts +9 -0
- package/dist/host/events.js +70 -0
- package/dist/host/facts.d.ts +6 -0
- package/dist/host/facts.js +9 -0
- package/dist/host/git.js +1 -1
- package/dist/host/http.d.ts +2 -2
- package/dist/host/http.js +3 -3
- package/dist/host/long-task.d.ts +5 -0
- package/dist/host/long-task.js +3 -0
- package/dist/host/mount-units.d.ts +52 -0
- package/dist/host/mount-units.js +186 -0
- package/dist/host/opencli-runtime.js +1 -1
- package/dist/host/opencli-window.js +1 -1
- package/dist/host/opencli.js +2 -1
- package/dist/host/panel-state.d.ts +120 -0
- package/dist/host/panel-state.js +176 -0
- package/dist/host/plugin-source.js +1 -1
- package/dist/host/preflight.d.ts +58 -0
- package/dist/host/preflight.js +233 -0
- package/dist/host/probe-cache.d.ts +6 -0
- package/dist/host/probe-cache.js +19 -0
- package/dist/host/probe-libs.d.ts +45 -0
- package/dist/host/probe-libs.js +61 -0
- package/dist/host/probe-units.d.ts +17 -0
- package/dist/host/probe-units.js +102 -0
- package/dist/host/probe-verdict.d.ts +7 -0
- package/dist/host/probe-verdict.js +143 -0
- package/dist/host/shell-text.d.ts +13 -0
- package/dist/host/shell-text.js +20 -0
- package/dist/host/tool-scope.d.ts +32 -0
- package/dist/host/tool-scope.js +84 -0
- package/dist/host/unit-declaration.d.ts +65 -0
- package/dist/host/unit-declaration.js +213 -0
- package/dist/host/unit-readiness.d.ts +7 -0
- package/dist/host/unit-readiness.js +5 -0
- package/dist/host/unit-registry.d.ts +9 -0
- package/dist/host/unit-registry.js +33 -0
- package/dist/host/version-range.d.ts +2 -0
- package/dist/host/version-range.js +73 -0
- package/dist/host.d.ts +7 -4
- package/dist/host.js +7 -4
- package/dist/i18n/en.js +4 -0
- package/dist/i18n/index.d.ts +5 -1
- package/dist/i18n/zh.d.ts +4 -0
- package/dist/i18n/zh.js +4 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.js +1 -4
- package/dist/lib/board-fact.d.ts +0 -7
- package/dist/lib/board-fact.js +0 -15
- package/dist/lib/diy-record.d.ts +38 -1
- package/dist/lib/diy-record.js +66 -0
- package/dist/lib/text.d.ts +0 -13
- package/dist/lib/text.js +0 -20
- package/dist/testing/declaration.js +1 -1
- package/package.json +1 -1
- package/src/api/__tests__/facts.test.ts +0 -13
- package/src/api/__tests__/long-task.test.ts +15 -0
- package/src/api/context-ref.ts +0 -39
- package/src/api/facts.ts +0 -9
- package/src/api/long-task.ts +11 -0
- package/src/components/ConfirmBar.css +26 -0
- package/src/components/ConfirmBar.test.tsx +130 -0
- package/src/components/ConfirmBar.tsx +63 -0
- package/src/components/Form.test.tsx +15 -0
- package/src/components/Form.tsx +7 -5
- package/src/components/FormNodes.tsx +50 -26
- package/src/components/FormTypes.test.ts +31 -0
- package/src/components/FormTypes.ts +46 -15
- package/src/components/FormWiring.test.tsx +1 -0
- package/src/config/constants.ts +3 -0
- package/src/host/__tests__/board-fact.test.ts +47 -0
- package/src/{lib → host/__tests__}/diy-debug.test.ts +1 -1
- package/src/{lib → host/__tests__}/diy-record.test.ts +137 -1
- package/src/host/__tests__/facts.test.ts +13 -0
- package/src/host/__tests__/long-task.test.ts +42 -0
- package/src/{lib → host/__tests__}/panel-state.test.ts +1 -1
- package/src/host/__tests__/preflight-route.test.ts +33 -0
- package/src/host/__tests__/shell-text.test.ts +44 -0
- package/src/host/board-fact.ts +14 -0
- package/src/host/browser-read.ts +2 -1
- package/src/host/context-ref.ts +40 -0
- package/src/{lib → host}/diy-record.ts +95 -1
- package/src/host/facts.ts +10 -0
- package/src/host/git.ts +1 -1
- package/src/host/http.ts +4 -4
- package/src/host/long-task.ts +9 -0
- package/src/host/opencli-runtime.ts +1 -1
- package/src/host/opencli-window.ts +1 -1
- package/src/host/opencli.ts +2 -1
- package/src/host/plugin-source.ts +1 -1
- package/src/host/shell-text.ts +21 -0
- package/src/{api → host}/unit-declaration.ts +1 -1
- package/src/host.ts +7 -4
- package/src/i18n/en.ts +4 -0
- package/src/i18n/zh.ts +4 -0
- package/src/index.ts +1 -4
- package/src/lib/board-fact.test.ts +1 -33
- package/src/lib/board-fact.ts +0 -13
- package/src/lib/text.test.ts +1 -43
- package/src/lib/text.ts +0 -22
- package/src/testing/declaration.ts +1 -1
- package/dist/lib/lint-probe.d.ts +0 -1
- package/dist/lib/lint-probe.js +0 -6
- /package/src/{api → host}/__tests__/unit-declaration.test.ts +0 -0
- /package/src/{lib → host}/diy-debug.ts +0 -0
- /package/src/{lib → host}/panel-state.ts +0 -0
|
@@ -15,12 +15,6 @@ export interface ContextRef {
|
|
|
15
15
|
* @returns 形如 kind:part/part;分段里的 / 不转义,所以分段名本身不能含 /。
|
|
16
16
|
*/
|
|
17
17
|
export declare function encodeRef(ref: ContextRef): string;
|
|
18
|
-
/**
|
|
19
|
-
* 把 kind:part/part 文本读回引用。
|
|
20
|
-
* @param text 待解析文本。
|
|
21
|
-
* @returns 没冒号、冒号在首尾、种类不是小写字母开头、或冒号后为空时给 undefined;不抛异常。
|
|
22
|
-
*/
|
|
23
|
-
export declare function decodeRef(text: string): ContextRef | undefined;
|
|
24
18
|
/**
|
|
25
19
|
* 拼出 @ 后面那半截(标记名加已编码文本),比较与拼接都用它。
|
|
26
20
|
* @param encoded encodeRef 的结果。
|
|
@@ -33,9 +27,3 @@ export declare function markerBody(encoded: string): string;
|
|
|
33
27
|
* @returns 形如 @工作台:file:src/a.ts 的一段文本。
|
|
34
28
|
*/
|
|
35
29
|
export declare function buildMarker(ref: ContextRef): string;
|
|
36
|
-
/**
|
|
37
|
-
* 从一段文本里按出现顺序捞出所有标记引用,同一个引用只留第一次。
|
|
38
|
-
* @param text 任意文本;null / undefined 当空串。
|
|
39
|
-
* @returns 解析不出来的标记会被丢掉,所以结果只含合法引用,可能为空数组。
|
|
40
|
-
*/
|
|
41
|
-
export declare function parseMarkers(text: string): ContextRef[];
|
package/dist/api/context-ref.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* 引用标记的固定名字;完整标记是 @<这个名字>:<编码文本>,改它等于作废历史会话里已写下的引用。
|
|
3
3
|
*/
|
|
4
4
|
export const CONTEXT_MARKER_NAME = '工作台';
|
|
5
|
-
const MARKER = new RegExp(`@${CONTEXT_MARKER_NAME}:([a-z][a-z0-9-]*:[A-Za-z0-9._~%#/-]+)`, 'g');
|
|
6
|
-
const TRAILING_PUNCTUATION = /[.,;:!?]+$/;
|
|
7
5
|
/**
|
|
8
6
|
* 把引用拼回标记体里的那一段文本(不含 @ 与标记名)。
|
|
9
7
|
* @param ref 要拼的引用。
|
|
@@ -12,23 +10,6 @@ const TRAILING_PUNCTUATION = /[.,;:!?]+$/;
|
|
|
12
10
|
export function encodeRef(ref) {
|
|
13
11
|
return `${ref.kind}:${ref.parts.join('/')}`;
|
|
14
12
|
}
|
|
15
|
-
/**
|
|
16
|
-
* 把 kind:part/part 文本读回引用。
|
|
17
|
-
* @param text 待解析文本。
|
|
18
|
-
* @returns 没冒号、冒号在首尾、种类不是小写字母开头、或冒号后为空时给 undefined;不抛异常。
|
|
19
|
-
*/
|
|
20
|
-
export function decodeRef(text) {
|
|
21
|
-
const at = text.indexOf(':');
|
|
22
|
-
if (at <= 0 || at === text.length - 1)
|
|
23
|
-
return undefined;
|
|
24
|
-
const kind = text.slice(0, at);
|
|
25
|
-
if (!/^[a-z][a-z0-9-]*$/.test(kind))
|
|
26
|
-
return undefined;
|
|
27
|
-
const parts = text.slice(at + 1).split('/').filter((part) => part !== '');
|
|
28
|
-
if (parts.length === 0)
|
|
29
|
-
return undefined;
|
|
30
|
-
return { kind, parts };
|
|
31
|
-
}
|
|
32
13
|
/**
|
|
33
14
|
* 拼出 @ 后面那半截(标记名加已编码文本),比较与拼接都用它。
|
|
34
15
|
* @param encoded encodeRef 的结果。
|
|
@@ -45,24 +26,3 @@ export function markerBody(encoded) {
|
|
|
45
26
|
export function buildMarker(ref) {
|
|
46
27
|
return `@${markerBody(encodeRef(ref))}`;
|
|
47
28
|
}
|
|
48
|
-
/**
|
|
49
|
-
* 从一段文本里按出现顺序捞出所有标记引用,同一个引用只留第一次。
|
|
50
|
-
* @param text 任意文本;null / undefined 当空串。
|
|
51
|
-
* @returns 解析不出来的标记会被丢掉,所以结果只含合法引用,可能为空数组。
|
|
52
|
-
*/
|
|
53
|
-
export function parseMarkers(text) {
|
|
54
|
-
const seen = new Set();
|
|
55
|
-
const refs = [];
|
|
56
|
-
for (const match of String(text ?? '').matchAll(MARKER)) {
|
|
57
|
-
const raw = match[1].replace(TRAILING_PUNCTUATION, '');
|
|
58
|
-
const ref = decodeRef(raw);
|
|
59
|
-
if (ref === undefined)
|
|
60
|
-
continue;
|
|
61
|
-
const key = encodeRef(ref);
|
|
62
|
-
if (seen.has(key))
|
|
63
|
-
continue;
|
|
64
|
-
seen.add(key);
|
|
65
|
-
refs.push(ref);
|
|
66
|
-
}
|
|
67
|
-
return refs;
|
|
68
|
-
}
|
package/dist/api/facts.d.ts
CHANGED
|
@@ -10,12 +10,6 @@ export interface FactFrame {
|
|
|
10
10
|
* 范围查法的后缀:cmdb/* 这类名字表示「这个前缀下的一族」,不是一条事实。
|
|
11
11
|
*/
|
|
12
12
|
export declare const FACT_SCOPE_SUFFIX = "*";
|
|
13
|
-
/**
|
|
14
|
-
* 由单元 id 拼出它那一族事实的范围名。
|
|
15
|
-
* @param unit 单元 id,不带后缀。
|
|
16
|
-
* @returns <unit>* —— 永远是范围名,不是某一条事实。
|
|
17
|
-
*/
|
|
18
|
-
export declare function unitFactScope(unit: string): string;
|
|
19
13
|
/**
|
|
20
14
|
* 判一个名字是不是范围查法,不是问这条事实存不存在。
|
|
21
15
|
* @param name 事实名或范围名。
|
package/dist/api/facts.js
CHANGED
|
@@ -2,14 +2,6 @@
|
|
|
2
2
|
* 范围查法的后缀:cmdb/* 这类名字表示「这个前缀下的一族」,不是一条事实。
|
|
3
3
|
*/
|
|
4
4
|
export const FACT_SCOPE_SUFFIX = '*';
|
|
5
|
-
/**
|
|
6
|
-
* 由单元 id 拼出它那一族事实的范围名。
|
|
7
|
-
* @param unit 单元 id,不带后缀。
|
|
8
|
-
* @returns <unit>* —— 永远是范围名,不是某一条事实。
|
|
9
|
-
*/
|
|
10
|
-
export function unitFactScope(unit) {
|
|
11
|
-
return `${unit}${FACT_SCOPE_SUFFIX}`;
|
|
12
|
-
}
|
|
13
5
|
/**
|
|
14
6
|
* 判一个名字是不是范围查法,不是问这条事实存不存在。
|
|
15
7
|
* @param name 事实名或范围名。
|
package/dist/api/long-task.d.ts
CHANGED
|
@@ -58,6 +58,14 @@ export declare function longTaskRunsOf<T>(state: {
|
|
|
58
58
|
* @returns ongoing / done / error / idle;needs-login 也算进行中;`stopped` 落进 idle —— 灰点:不是成功、不是出错,是用户自己按停的。
|
|
59
59
|
*/
|
|
60
60
|
export declare function longTaskDot(status: LongTaskStatus): 'ongoing' | 'done' | 'error' | 'idle';
|
|
61
|
+
/**
|
|
62
|
+
* 这一趟还在不在跑(`running` 与 `needs-login` 都算)。
|
|
63
|
+
* 卡片要判"这一行要不要摆停止钮""这套环境里有没有活着的跑"时用这个 —— **不是** `longTaskDot`:
|
|
64
|
+
* 那一件是状态点的**长相**(四档),归 `LongTaskRow` 自己用,不该被调用点拿去当判据。
|
|
65
|
+
* @param status 长任务的五种态。
|
|
66
|
+
* @returns 在跑 `true`;成功 / 失败 / 用户按停都是 `false`。
|
|
67
|
+
*/
|
|
68
|
+
export declare function longTaskIsLive(status: LongTaskStatus): boolean;
|
|
61
69
|
/**
|
|
62
70
|
* 停止钮的三档:没上膛 / 上了膛 / 请求在飞。
|
|
63
71
|
*/
|
package/dist/api/long-task.js
CHANGED
|
@@ -55,6 +55,16 @@ export function longTaskDot(status) {
|
|
|
55
55
|
return 'ongoing';
|
|
56
56
|
return 'idle';
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* 这一趟还在不在跑(`running` 与 `needs-login` 都算)。
|
|
60
|
+
* 卡片要判"这一行要不要摆停止钮""这套环境里有没有活着的跑"时用这个 —— **不是** `longTaskDot`:
|
|
61
|
+
* 那一件是状态点的**长相**(四档),归 `LongTaskRow` 自己用,不该被调用点拿去当判据。
|
|
62
|
+
* @param status 长任务的五种态。
|
|
63
|
+
* @returns 在跑 `true`;成功 / 失败 / 用户按停都是 `false`。
|
|
64
|
+
*/
|
|
65
|
+
export function longTaskIsLive(status) {
|
|
66
|
+
return status === 'running' || status === 'needs-login';
|
|
67
|
+
}
|
|
58
68
|
/**
|
|
59
69
|
* 算停止钮现在在哪一档。
|
|
60
70
|
* @param state armed = 已经按过第一下、在等第二下;busy = 停止请求已经在飞。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.wb-confirmBar {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--wb-len-8);
|
|
5
|
+
width: 100%;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
padding: var(--wb-len-10) var(--wb-len-12);
|
|
8
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
9
|
+
border-radius: var(--dsw-radius-md);
|
|
10
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
11
|
+
}
|
|
12
|
+
.wb-confirmBarTitle {
|
|
13
|
+
margin: 0;
|
|
14
|
+
font-size: var(--dsw-font-xxs-12-font-size);
|
|
15
|
+
font-weight: 600;
|
|
16
|
+
line-height: var(--wb-line-18);
|
|
17
|
+
color: var(--dsw-alias-label-primary);
|
|
18
|
+
overflow-wrap: anywhere;
|
|
19
|
+
}
|
|
20
|
+
.wb-confirmBarHint {
|
|
21
|
+
margin: 0;
|
|
22
|
+
font-size: var(--dsw-font-xxxs-11-font-size);
|
|
23
|
+
line-height: var(--wb-line-16);
|
|
24
|
+
color: var(--dsw-alias-label-secondary);
|
|
25
|
+
overflow-wrap: anywhere;
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ReactNode } from '../lib/react.js';
|
|
2
|
+
import { type FormAction } from './FormActions.js';
|
|
3
|
+
import './ConfirmBar.css';
|
|
4
|
+
/**
|
|
5
|
+
* `ConfirmBar` 的一格键值:`label` 在左、`value` 在右(走 `Rows` 的标签列)。
|
|
6
|
+
*/
|
|
7
|
+
export interface ConfirmRow {
|
|
8
|
+
readonly key: string;
|
|
9
|
+
readonly label: ReactNode;
|
|
10
|
+
readonly value: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `ConfirmBar` 的契约:标题、说明、键值、失败那句与动作。
|
|
14
|
+
*/
|
|
15
|
+
export interface ConfirmBarProps {
|
|
16
|
+
readonly title: ReactNode;
|
|
17
|
+
readonly hint?: ReactNode | undefined;
|
|
18
|
+
readonly rows?: readonly ConfirmRow[] | undefined;
|
|
19
|
+
readonly actions: readonly FormAction[];
|
|
20
|
+
readonly problem?: ReactNode | undefined;
|
|
21
|
+
readonly busy?: boolean | undefined;
|
|
22
|
+
readonly label?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 「先问一句再动手」那一条:标题 + 说明 + 键值 + 失败那句 + 动作区,形状一次收口。
|
|
26
|
+
* **要不要问**归调用点(ADR-0054 §2 规则三),这一件只回答"问的时候长什么样"。
|
|
27
|
+
* @param props.title - 那一句问话("发布到 prod?")。
|
|
28
|
+
* @param props.hint - 标题下面那句说明或正文;不给不占位。
|
|
29
|
+
* @param props.rows - 键值两格(键值列宽与分隔线归 `Rows` / `Row`);不给或空数组不渲染。值那一格可以放控件。
|
|
30
|
+
* @param props.actions - 动作清单(归 `FormActions`:一个区一颗 `primary`、永远在右、窄了折行)。
|
|
31
|
+
* @param props.problem - 失败那句(走 `Note tone="problem"`,读屏是 `alert`);不给不占位。
|
|
32
|
+
* @param props.busy - 整条在忙(只喂 `aria-busy`;按钮自己的转圈与禁用归 `actions` 里那一颗)。
|
|
33
|
+
* @param props.label - 读屏的组名;不给时 `title` 是字符串就拿它顶上,两者都没有就不设。
|
|
34
|
+
* @returns 一个 `role="group"` 的条;`actions` 空数组时最后那一块不渲染。
|
|
35
|
+
*/
|
|
36
|
+
export declare function ConfirmBar({ title, hint, rows, actions, problem, busy, label }: ConfirmBarProps): JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { h } from '../lib/react.js';
|
|
2
|
+
import { FormActions } from './FormActions.js';
|
|
3
|
+
import { Note } from './Note.js';
|
|
4
|
+
import { Row, Rows } from './Rows.js';
|
|
5
|
+
import './ConfirmBar.css';
|
|
6
|
+
/**
|
|
7
|
+
* 「先问一句再动手」那一条:标题 + 说明 + 键值 + 失败那句 + 动作区,形状一次收口。
|
|
8
|
+
* **要不要问**归调用点(ADR-0054 §2 规则三),这一件只回答"问的时候长什么样"。
|
|
9
|
+
* @param props.title - 那一句问话("发布到 prod?")。
|
|
10
|
+
* @param props.hint - 标题下面那句说明或正文;不给不占位。
|
|
11
|
+
* @param props.rows - 键值两格(键值列宽与分隔线归 `Rows` / `Row`);不给或空数组不渲染。值那一格可以放控件。
|
|
12
|
+
* @param props.actions - 动作清单(归 `FormActions`:一个区一颗 `primary`、永远在右、窄了折行)。
|
|
13
|
+
* @param props.problem - 失败那句(走 `Note tone="problem"`,读屏是 `alert`);不给不占位。
|
|
14
|
+
* @param props.busy - 整条在忙(只喂 `aria-busy`;按钮自己的转圈与禁用归 `actions` 里那一颗)。
|
|
15
|
+
* @param props.label - 读屏的组名;不给时 `title` 是字符串就拿它顶上,两者都没有就不设。
|
|
16
|
+
* @returns 一个 `role="group"` 的条;`actions` 空数组时最后那一块不渲染。
|
|
17
|
+
*/
|
|
18
|
+
export function ConfirmBar({ title, hint, rows, actions, problem, busy, label }) {
|
|
19
|
+
const name = label ?? (typeof title === 'string' ? title : undefined);
|
|
20
|
+
return (h("div", { className: "wb-confirmBar", role: "group", "aria-label": name, "aria-busy": busy },
|
|
21
|
+
h("p", { className: "wb-confirmBarTitle" }, title),
|
|
22
|
+
hint === undefined ? null : h("p", { className: "wb-confirmBarHint" }, hint),
|
|
23
|
+
rows === undefined || rows.length === 0 ? null : (h(Rows, null, rows.map((row) => (h(Row, { key: row.key, label: row.label, value: row.value }))))),
|
|
24
|
+
problem === undefined ? null : (h(Note, { tone: "problem", role: "alert" }, problem)),
|
|
25
|
+
h(FormActions, { actions: actions })));
|
|
26
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { FormProps } from './FormTypes.js';
|
|
1
|
+
import type { FormProps, FormValues } from './FormTypes.js';
|
|
2
2
|
import './Form.css';
|
|
3
3
|
/**
|
|
4
4
|
* 整张表单:`fields` 进,行、错误、操作区、(被挡住的)提交都由它安排 —— 它自己一个值都不存,
|
|
5
|
-
*
|
|
5
|
+
* 值仍归调用点(`value` 与 `onChange` 写在字段配置的同一格里,`Form` 只是把两者原样递给那一格的 `control`)。
|
|
6
|
+
* 写了 `Form<Values>` 就按 `Values` 把每一格的键与值类型钉住(`Values` 里会 `visible: false` 的键写成可选键);
|
|
7
|
+
* 不写就是默认档,键值都不钉。
|
|
6
8
|
* 还没提交过时一格错都不显示;第一次提交之后每次渲染重算,改好了当场消。
|
|
7
9
|
* 提交那一拍永远先算一遍:有错就不调 `onSubmit`,并把第一个有错的 `FormRow` 带进视口、聚焦它里面的控件。
|
|
8
10
|
* 回车与点提交钮走的是同一条路:真 `<form>` 的隐式提交在**只有一个文本框**时也会 fire,所以 `busy` / `disabled` 时回车同样不提交。
|
|
9
11
|
* 调用点只管数据与语义:行怎么排、错误摆哪、什么时候校验、按钮在哪,这里没有口子。
|
|
10
|
-
* @param props.fields - 每格的键 / 标签 / 值 / 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
|
|
11
|
-
* @param props.onSubmit - 校验全过的那一拍,回的是从 `fields`
|
|
12
|
+
* @param props.fields - 每格的键 / 标签 / 值 / 值怎么改(`onChange`)/ 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
|
|
13
|
+
* @param props.onSubmit - 校验全过的那一拍,回的是从 `fields` 现取的键值表(按 `visibleFieldsOf` 过滤过,不可见那格不在里面);被挡住时不会被调。
|
|
12
14
|
* @param props.submit - 提交钮文案;`busyLabel` 是提交中换的那句,不给就沿用 `label`。
|
|
13
15
|
* @param props.cancel - 取消钮;给了就排在提交钮左边,`busy` 时一起禁用。
|
|
14
16
|
* @param props.extra - 其余动作(default / danger),排在最左,`busy` 时一起禁用。
|
|
@@ -17,4 +19,4 @@ import './Form.css';
|
|
|
17
19
|
* @param props.problem - 整表级那句错(提交失败),落在行与操作区之间;不给就不画。
|
|
18
20
|
* @returns 一个 `<form>`(`noValidate`:报错形状归 `FormRow`,不走浏览器的气泡)。
|
|
19
21
|
*/
|
|
20
|
-
export declare function Form(props: FormProps): JSX.Element;
|
|
22
|
+
export declare function Form<Values extends object = FormValues>(props: FormProps<Values>): JSX.Element;
|
package/dist/components/Form.js
CHANGED
|
@@ -7,13 +7,15 @@ import { revealFirstProblem } from '../lib/form-problem.js';
|
|
|
7
7
|
import './Form.css';
|
|
8
8
|
/**
|
|
9
9
|
* 整张表单:`fields` 进,行、错误、操作区、(被挡住的)提交都由它安排 —— 它自己一个值都不存,
|
|
10
|
-
*
|
|
10
|
+
* 值仍归调用点(`value` 与 `onChange` 写在字段配置的同一格里,`Form` 只是把两者原样递给那一格的 `control`)。
|
|
11
|
+
* 写了 `Form<Values>` 就按 `Values` 把每一格的键与值类型钉住(`Values` 里会 `visible: false` 的键写成可选键);
|
|
12
|
+
* 不写就是默认档,键值都不钉。
|
|
11
13
|
* 还没提交过时一格错都不显示;第一次提交之后每次渲染重算,改好了当场消。
|
|
12
14
|
* 提交那一拍永远先算一遍:有错就不调 `onSubmit`,并把第一个有错的 `FormRow` 带进视口、聚焦它里面的控件。
|
|
13
15
|
* 回车与点提交钮走的是同一条路:真 `<form>` 的隐式提交在**只有一个文本框**时也会 fire,所以 `busy` / `disabled` 时回车同样不提交。
|
|
14
16
|
* 调用点只管数据与语义:行怎么排、错误摆哪、什么时候校验、按钮在哪,这里没有口子。
|
|
15
|
-
* @param props.fields - 每格的键 / 标签 / 值 / 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
|
|
16
|
-
* @param props.onSubmit - 校验全过的那一拍,回的是从 `fields`
|
|
17
|
+
* @param props.fields - 每格的键 / 标签 / 值 / 值怎么改(`onChange`)/ 校验 / 控件;`visible: false` 的那格当它不存在(不渲染、不校验、不进 `values`)。
|
|
18
|
+
* @param props.onSubmit - 校验全过的那一拍,回的是从 `fields` 现取的键值表(按 `visibleFieldsOf` 过滤过,不可见那格不在里面);被挡住时不会被调。
|
|
17
19
|
* @param props.submit - 提交钮文案;`busyLabel` 是提交中换的那句,不给就沿用 `label`。
|
|
18
20
|
* @param props.cancel - 取消钮;给了就排在提交钮左边,`busy` 时一起禁用。
|
|
19
21
|
* @param props.extra - 其余动作(default / danger),排在最左,`busy` 时一起禁用。
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import type { ReactNode } from '../lib/react.js';
|
|
2
2
|
import type { FormAction } from './FormActions.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { FormFieldOf, FormValues } from './FormTypes.js';
|
|
4
4
|
/**
|
|
5
5
|
* 只剩要显示的那些格子 —— `visible: false` 的当它不存在:不渲染、不校验、不进 `values`,也不参与找第一处错误。
|
|
6
6
|
* @param fields - 表单的字段清单。
|
|
7
7
|
* @returns 保序的新数组,`visible` 不给或为 `true` 的都在;全不可见时给空数组。
|
|
8
8
|
*/
|
|
9
|
-
export declare function visibleFieldsOf(fields: readonly
|
|
9
|
+
export declare function visibleFieldsOf<Values extends object>(fields: readonly FormFieldOf<Values>[]): readonly FormFieldOf<Values>[];
|
|
10
10
|
/**
|
|
11
11
|
* 找出装配错的键:**假值**的键(空串 / `undefined` / `null`,运行期真漏给 `key` 就是这条)每次出现都算一处,
|
|
12
12
|
* 以及同一张表单里出现两次的键 —— 这两样都会让 `id` 撞车、`values` 少一格。
|
|
13
13
|
* @param fields - 表单的字段清单(通常是已经过滤过可见性的那份)。
|
|
14
14
|
* @returns 出问题的键的清单、按首次出现顺序去重;缺失的键一律记成空串(报的时候印成 `(空 key)`),空表给空数组。
|
|
15
15
|
*/
|
|
16
|
-
export declare function duplicateKeysOf(fields: readonly
|
|
16
|
+
export declare function duplicateKeysOf(fields: readonly {
|
|
17
|
+
readonly key: string;
|
|
18
|
+
}[]): readonly string[];
|
|
17
19
|
/**
|
|
18
20
|
* 装配错了要当场诊断:`key` 缺失或重复时给 `console.error` —— 但照渲不误,不 throw。
|
|
19
21
|
* 形式与理由同 `FormActions` 里那颗多出来的 `primary`:装配的小错不该让 `CardBoundary` 把用户的整张卡变空白。
|
|
20
22
|
* @param fields - 表单的字段清单(通常是已经过滤过可见性的那份)。
|
|
21
23
|
*/
|
|
22
|
-
export declare function reportDuplicateKeys(fields: readonly
|
|
24
|
+
export declare function reportDuplicateKeys(fields: readonly {
|
|
25
|
+
readonly key: string;
|
|
26
|
+
}[]): void;
|
|
23
27
|
/**
|
|
24
28
|
* 把每格的 `value` 收成一张键值表交给 `onSubmit` —— `Form` 自己一个值都不存,这张表就是从 `fields` 现取的快照。
|
|
25
29
|
* @param fields - 表单的字段清单;取的就是它们的 `value`,`value` 是数组的那格(多选)原样带出、不复制。
|
|
26
30
|
* @returns 键为字段 `key`、值为该格 `value` 的表;`fields` 为空时给空表,不是 `undefined`。
|
|
27
31
|
*/
|
|
28
|
-
export declare function formValuesOf(fields: readonly
|
|
32
|
+
export declare function formValuesOf<Values extends object>(fields: readonly FormFieldOf<Values>[]): Readonly<Values>;
|
|
29
33
|
/**
|
|
30
34
|
* 算每格现在该不该报错:还没提交过(`show` 为 false)时一格都不报 —— 不边打字边骂人。
|
|
31
35
|
* `validate` 返回**真值**才算错,假值(`undefined` / `null` / `false` / 空串)一律当"这格没错" ——
|
|
@@ -34,7 +38,7 @@ export declare function formValuesOf(fields: readonly FormField[]): Readonly<Rec
|
|
|
34
38
|
* @param show - 提交过没有;false 时全给 `undefined`,true 时逐格调 `field.validate` 并按真值收。
|
|
35
39
|
* @returns 键为 `key`、值为那句文案或 `undefined` 的表;没给 `validate` 的格子也是 `undefined`。
|
|
36
40
|
*/
|
|
37
|
-
export declare function formProblemsOf(fields: readonly
|
|
41
|
+
export declare function formProblemsOf<Values extends object>(fields: readonly FormFieldOf<Values>[], show: boolean): Readonly<Record<string, ReactNode | undefined>>;
|
|
38
42
|
/**
|
|
39
43
|
* 按字段顺序取第一处错误 —— 提交被挡住时要带进视口的就是这一格。
|
|
40
44
|
* 判的是**真值**(与 `formProblemsOf` 同一口径):假值那种"错误文案"渲染出来本来就是空的,不算错。
|
|
@@ -42,16 +46,16 @@ export declare function formProblemsOf(fields: readonly FormField[], show: boole
|
|
|
42
46
|
* @param problems - `formProblemsOf` 那张表,也可以是调用点自己拼的(同样按真值判)。
|
|
43
47
|
* @returns 第一格有错的字段;一格都没错时给 `undefined`。
|
|
44
48
|
*/
|
|
45
|
-
export declare function formFirstProblemOf(fields: readonly
|
|
49
|
+
export declare function formFirstProblemOf<Values extends object>(fields: readonly FormFieldOf<Values>[], problems: Readonly<Record<string, ReactNode | undefined>>): FormFieldOf<Values> | undefined;
|
|
46
50
|
/**
|
|
47
51
|
* 提交那一拍的结果:有错就挡住(并点出第一处错在哪一格),没错就把键值表交出去。
|
|
48
52
|
*/
|
|
49
|
-
export type FormSubmitOutcome = {
|
|
53
|
+
export type FormSubmitOutcome<Values extends object = FormValues> = {
|
|
50
54
|
readonly blocked: true;
|
|
51
|
-
readonly first:
|
|
55
|
+
readonly first: FormFieldOf<Values>;
|
|
52
56
|
} | {
|
|
53
57
|
readonly blocked: false;
|
|
54
|
-
readonly values: Readonly<
|
|
58
|
+
readonly values: Readonly<Values>;
|
|
55
59
|
};
|
|
56
60
|
/**
|
|
57
61
|
* 算提交那一拍该干什么 —— `Form` 的提交钮点下去、以及 `<form>` 的 Enter 提交,走的都是它。
|
|
@@ -60,7 +64,7 @@ export type FormSubmitOutcome = {
|
|
|
60
64
|
* @returns `blocked: true` 时带上是按字段顺序数第一格有错的字段(拿它去 `revealFirstProblem`);
|
|
61
65
|
* `blocked: false` 时带上从 `fields` 现取的键值表。空表一定给 `blocked: false` 与空 values。
|
|
62
66
|
*/
|
|
63
|
-
export declare function formSubmitOf(fields: readonly
|
|
67
|
+
export declare function formSubmitOf<Values extends object>(fields: readonly FormFieldOf<Values>[]): FormSubmitOutcome<Values>;
|
|
64
68
|
/**
|
|
65
69
|
* 组表单的操作区:调用点给的其余动作在最左,取消在中间,提交永远是最右那颗 `primary`。
|
|
66
70
|
* @param args.extra - 其余动作(default / danger),保持调用点给的顺序;不给就是没有。
|
|
@@ -95,8 +99,8 @@ export declare function formActionsOf(args: {
|
|
|
95
99
|
* @param args.busy - 提交中:每格的绑定 `disabled` 为真。
|
|
96
100
|
* @returns `FormRow` 组成的数组;没有可画的格子时给空数组。
|
|
97
101
|
*/
|
|
98
|
-
export declare function formRowsOf(args: {
|
|
99
|
-
readonly fields: readonly
|
|
102
|
+
export declare function formRowsOf<Values extends object>(args: {
|
|
103
|
+
readonly fields: readonly FormFieldOf<Values>[];
|
|
100
104
|
readonly problems: Readonly<Record<string, ReactNode | undefined>>;
|
|
101
105
|
readonly showProblem: boolean;
|
|
102
106
|
readonly busy: boolean;
|
|
@@ -65,11 +65,14 @@ export function formProblemsOf(fields, show) {
|
|
|
65
65
|
const values = formValuesOf(fields);
|
|
66
66
|
const problems = {};
|
|
67
67
|
for (const field of fields) {
|
|
68
|
-
const found = show ?
|
|
68
|
+
const found = show ? problemOf(field, values) : undefined;
|
|
69
69
|
problems[field.key] = found ? found : undefined;
|
|
70
70
|
}
|
|
71
71
|
return problems;
|
|
72
72
|
}
|
|
73
|
+
function problemOf(field, values) {
|
|
74
|
+
return field.validate?.(field.value, values);
|
|
75
|
+
}
|
|
73
76
|
/**
|
|
74
77
|
* 按字段顺序取第一处错误 —— 提交被挡住时要带进视口的就是这一格。
|
|
75
78
|
* 判的是**真值**(与 `formProblemsOf` 同一口径):假值那种"错误文案"渲染出来本来就是空的,不算错。
|
|
@@ -144,10 +147,15 @@ export function formRowsOf(args) {
|
|
|
144
147
|
hint: field.hint,
|
|
145
148
|
problem: args.showProblem ? args.problems[field.key] : undefined,
|
|
146
149
|
layout: field.layout,
|
|
147
|
-
}, field.
|
|
150
|
+
}, controlOf(field, args.problems, args.showProblem, args.busy)));
|
|
151
|
+
}
|
|
152
|
+
function controlOf(field, problems, showProblem, busy) {
|
|
153
|
+
return field.control({
|
|
148
154
|
id: `wb-form-${field.key}`,
|
|
149
155
|
name: field.key,
|
|
150
|
-
invalid:
|
|
151
|
-
disabled:
|
|
152
|
-
|
|
156
|
+
invalid: showProblem && Boolean(problems[field.key]),
|
|
157
|
+
disabled: busy || field.disabled === true,
|
|
158
|
+
value: field.value,
|
|
159
|
+
onChange: field.onChange,
|
|
160
|
+
});
|
|
153
161
|
}
|
|
@@ -1,35 +1,56 @@
|
|
|
1
1
|
import type { ReactNode } from '../lib/react.js';
|
|
2
2
|
import type { FormAction } from './FormActions.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* 默认档一格控件的值:单值(`TextField` / `SelectField` / `ChoiceGroup` 单选),或一组值(`ChoiceGroup` 多选)。
|
|
5
|
+
* 写了 `Form<Values>` 的类型参数之后,每一格的值类型由 `Values[那一格的键]` 定 —— 这个联合只是**没写类型参数**时的默认档。
|
|
5
6
|
*/
|
|
6
7
|
export type FormValue = string | readonly string[];
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* 默认档的 `Values`:键是任意 `string`、值是 `FormValue` —— 不写 `Form<Values>` 的类型参数时就是它,
|
|
10
|
+
* 于是 `fields` 的键与值都不钉(老写法照旧能编),代价是每格拿到的都是那个联合、要自己收窄。
|
|
9
11
|
*/
|
|
10
|
-
export
|
|
12
|
+
export type FormValues = Readonly<Record<string, FormValue>>;
|
|
13
|
+
/**
|
|
14
|
+
* `Form` 递给控件那一格的绑定:`id` / `name` / `invalid` / `disabled` 由 `Form` 定;
|
|
15
|
+
* `value` / `onChange` 就是这一格自己那份配置里的那两个**原样转手**(`Form` 不持有值)——
|
|
16
|
+
* 控件接上即可,别再自己接 model:值只写在字段配置里那一次。
|
|
17
|
+
*/
|
|
18
|
+
export interface FormFieldBinding<K extends string = string, V = FormValue> {
|
|
11
19
|
readonly id: string;
|
|
12
|
-
readonly name:
|
|
20
|
+
readonly name: K;
|
|
13
21
|
readonly invalid: boolean;
|
|
14
22
|
readonly disabled: boolean;
|
|
23
|
+
readonly value: V;
|
|
24
|
+
readonly onChange: (next: V) => void;
|
|
15
25
|
}
|
|
16
26
|
/**
|
|
17
|
-
*
|
|
27
|
+
* 一格字段的配置:键、标签、值、值怎么改(`onChange`)、怎么算错(`validate`)、控件本身 ——
|
|
28
|
+
* 排布、时机、阻断都不写在这里,那些归 `Form`。`value` 与 `onChange` 就在同一格里,`control` 从绑定直接取
|
|
29
|
+
* (`(binding) => …({ value: binding.value, onChange: binding.onChange })`),值不再有第二处。
|
|
18
30
|
* `validate` 是纯函数、同步、只读 `values`:返回**真值**才算这一格错,假值(`undefined` / `null` / `false` / 空串)
|
|
19
31
|
* 一律当"没错"—— 所以 `(value) => value.trim() === '' && '必填'` 这种用 `&&` 算文案的写法是对的,
|
|
20
32
|
* 它的返回值就是错误行那句话。
|
|
21
33
|
*/
|
|
22
|
-
export interface FormField {
|
|
23
|
-
readonly key:
|
|
34
|
+
export interface FormField<K extends string = string, V = FormValue, Vals extends object = FormValues> {
|
|
35
|
+
readonly key: K;
|
|
24
36
|
readonly label: ReactNode;
|
|
25
37
|
readonly hint?: ReactNode | undefined;
|
|
26
38
|
readonly layout?: 'inline' | 'stacked' | undefined;
|
|
27
|
-
readonly value:
|
|
39
|
+
readonly value: V;
|
|
40
|
+
readonly onChange: (next: V) => void;
|
|
28
41
|
readonly visible?: boolean | undefined;
|
|
29
42
|
readonly disabled?: boolean | undefined;
|
|
30
|
-
readonly validate?: (value:
|
|
31
|
-
readonly control: (binding: FormFieldBinding) => ReactNode;
|
|
43
|
+
readonly validate?: (value: V, values: Readonly<Vals>) => ReactNode | undefined;
|
|
44
|
+
readonly control: (binding: FormFieldBinding<K, V>) => ReactNode;
|
|
32
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* 把 `Values` 的每一键映射成一格字段 —— 写 `Form<Values>` 时 `fields` 就是这个联合的数组:
|
|
48
|
+
* `{ key: 'env', … }` 那一格的 `value` / `onChange` / `validate` 只认 `Values['env']`,
|
|
49
|
+
* `control` 拿到的绑定也只带它。要"键值都不钉"的松档就干脆别写类型参数(默认档 `FormValues`),别手写这个类型。
|
|
50
|
+
*/
|
|
51
|
+
export type FormFieldOf<Values extends object> = {
|
|
52
|
+
[K in keyof Values & string]: FormField<K, Values[K], Values>;
|
|
53
|
+
}[keyof Values & string];
|
|
33
54
|
/**
|
|
34
55
|
* 提交钮:文案归调用点,位置、禁用与忙态归 `Form`。
|
|
35
56
|
*/
|
|
@@ -45,11 +66,15 @@ export interface FormCancel {
|
|
|
45
66
|
readonly onClick: () => void;
|
|
46
67
|
}
|
|
47
68
|
/**
|
|
48
|
-
* `Form`
|
|
69
|
+
* `Form` 的契约:`fields` 配置进,排布、校验时机、提交阻断、操作区全归组件。
|
|
70
|
+
* `Values` 是**那张值表**的形状(不写就是默认档 `FormValues`):`fields` 的键只能取它的键,
|
|
71
|
+
* 每格的 `value` / `onChange` / `validate` 只认它那一键的值类型,`onSubmit` 与 `validate` 第二参收到的也是它。
|
|
72
|
+
* **会 `visible: false` 的键写成可选键**(`branch?: string`)—— 那一格不渲染、不校验、不进 `values`(规则五),
|
|
73
|
+
* 可选键让"它可能不在表里"这件事在类型上也成立;永远可见的键照常必填。
|
|
49
74
|
*/
|
|
50
|
-
export interface FormProps {
|
|
51
|
-
readonly fields: readonly
|
|
52
|
-
readonly onSubmit: (values: Readonly<
|
|
75
|
+
export interface FormProps<Values extends object = FormValues> {
|
|
76
|
+
readonly fields: readonly FormFieldOf<Values>[];
|
|
77
|
+
readonly onSubmit: (values: Readonly<Values>) => void;
|
|
53
78
|
readonly submit: FormSubmit;
|
|
54
79
|
readonly cancel?: FormCancel | undefined;
|
|
55
80
|
readonly extra?: readonly FormAction[] | undefined;
|
|
@@ -36,4 +36,14 @@
|
|
|
36
36
|
border-color: transparent;
|
|
37
37
|
background: var(--dsw-alias-brand-primary);
|
|
38
38
|
color: var(--dsw-alias-label-primary-foreground);
|
|
39
|
-
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.wb-mockChipOn:hover {
|
|
42
|
+
border-color: transparent;
|
|
43
|
+
background: var(--dsw-alias-brand-primary);
|
|
44
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.wb-panelBody.wb-panelMany .wb-mockStrip {
|
|
48
|
+
display: none;
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { h } from '../lib/react.js';
|
|
2
|
+
export function StateDot({ state, size }) {
|
|
3
|
+
if (state === 'ongoing') {
|
|
4
|
+
return (h("svg", { className: "wb-loading", viewBox: "0 0 24 24", width: size, height: size, "aria-hidden": "true", style: { display: 'block' } },
|
|
5
|
+
h("g", { className: "wb-loadingMotion" },
|
|
6
|
+
h("circle", { className: "wb-loadingTrack", cx: 12, cy: 12, r: 9.5 }),
|
|
7
|
+
h("circle", { className: "wb-loadingArc", cx: 12, cy: 12, r: 9.5 }))));
|
|
8
|
+
}
|
|
9
|
+
return (h("span", { className: "wb-stateDot", "data-state": state, style: { width: size, height: size }, "aria-hidden": "true" }));
|
|
10
|
+
}
|
|
@@ -48,6 +48,8 @@ export declare const OPENCLI_TIMEOUT_MS = 120000;
|
|
|
48
48
|
export declare const OPENCLI_MAX_CONCURRENT = 4;
|
|
49
49
|
/** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
|
|
50
50
|
export declare const WARM_TIMEOUT_MS = 20000;
|
|
51
|
+
/** 体检结果算「还新鲜」的时长(毫秒):期内再来的强制体检直接回上一次那份快照。 */
|
|
52
|
+
export declare const PREFLIGHT_FRESH_MS = 30000;
|
|
51
53
|
/** 装 / 卸模块包的超时(毫秒):pnpm 与取包内容都按它算。 */
|
|
52
54
|
export declare const INSTALL_TIMEOUT_MS = 300000;
|
|
53
55
|
/** 查 registry 版本那一次请求的超时(毫秒)。 */
|
package/dist/config/constants.js
CHANGED
|
@@ -51,6 +51,8 @@ export const OPENCLI_TIMEOUT_MS = 120_000;
|
|
|
51
51
|
export const OPENCLI_MAX_CONCURRENT = 4;
|
|
52
52
|
/** 启动预热站点那一跳的超时(毫秒);超时按未热对待,不挡面板。 */
|
|
53
53
|
export const WARM_TIMEOUT_MS = 20_000;
|
|
54
|
+
/** 体检结果算「还新鲜」的时长(毫秒):期内再来的强制体检直接回上一次那份快照。 */
|
|
55
|
+
export const PREFLIGHT_FRESH_MS = 30_000;
|
|
54
56
|
/** 装 / 卸模块包的超时(毫秒):pnpm 与取包内容都按它算。 */
|
|
55
57
|
export const INSTALL_TIMEOUT_MS = 300_000;
|
|
56
58
|
/** 查 registry 版本那一次请求的超时(毫秒)。 */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { DshShell } from '../../api/dsh.js';
|
|
2
|
+
import type { Opencli } from '../../host/opencli.js';
|
|
3
|
+
export interface ShellCall {
|
|
4
|
+
command: string;
|
|
5
|
+
workdir?: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
onExpiry?: string;
|
|
8
|
+
stdoutMaxBytes?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ShellReply {
|
|
11
|
+
stdout?: string;
|
|
12
|
+
stderr?: string;
|
|
13
|
+
exitCode?: number | null;
|
|
14
|
+
timedOut?: boolean;
|
|
15
|
+
aborted?: boolean;
|
|
16
|
+
failWith?: Error;
|
|
17
|
+
}
|
|
18
|
+
export declare function createFakeShell(replies?: ShellReply[] | ((call: ShellCall, index: number) => ShellReply)): {
|
|
19
|
+
shell: DshShell;
|
|
20
|
+
calls: ShellCall[];
|
|
21
|
+
peak: () => number;
|
|
22
|
+
commands: () => string[];
|
|
23
|
+
};
|
|
24
|
+
export interface OpencliCall {
|
|
25
|
+
args: readonly string[];
|
|
26
|
+
key?: string;
|
|
27
|
+
workdir?: string;
|
|
28
|
+
refresh?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function createFakeOpencli(replies?: Array<unknown | Error> | ((call: OpencliCall, index: number) => unknown | Error)): {
|
|
31
|
+
opencli: Opencli;
|
|
32
|
+
calls: OpencliCall[];
|
|
33
|
+
keys: () => (string | undefined)[];
|
|
34
|
+
};
|
|
35
|
+
export declare function createFakeLogger(): {
|
|
36
|
+
logger: {
|
|
37
|
+
info: () => void;
|
|
38
|
+
warn: (message: string) => void;
|
|
39
|
+
};
|
|
40
|
+
warnings: string[];
|
|
41
|
+
};
|