@xtalpi/agentic-lab-skills 0.0.9 → 0.0.11
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/README.md +1 -1
- package/package.json +14 -14
- package/skills/lab-flow-designer/SKILL.md +612 -593
- package/skills/lab-flow-designer/embedded-template/SKILL.md +103 -88
- package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +21 -12
- package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +21 -12
- package/skills/lab-flow-designer/embedded-template/scripts//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.js +142 -142
- package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +114 -99
- package/skills/lab-flow-designer/references/agentic-lab-processer.md +122 -78
- package/skills/lab-flow-designer/references/agentic-lab-sdk.md +534 -361
- package/skills/lab-flow-designer/references/rhea-api/README.md +7 -7
- package/skills/lab-flow-designer/references/rhea-api/execute_process_batch.md +58 -58
- package/skills/lab-flow-designer/references/skill-package-layout.md +268 -204
- package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +216 -208
- package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/250/241/346/235/277.md +192 -169
- package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/347/244/272/344/276/213.md +207 -197
- package/skills/lab-flow-designer/testing/test-processer.mjs +1240 -1075
- package/skills/lab-nocobase-flow-generator/SKILL.md +164 -164
- package/skills/lab-nocobase-flow-generator/examples/setting/350/241/250/350/216/267/345/217/226/345/244/226/351/203/250/346/234/215/345/212/241.js +70 -70
- package/skills/lab-nocobase-flow-generator/examples//346/237/245/350/257/242/345/214/226/345/255/246/345/223/201/344/277/241/346/201/257.js +30 -30
- package/skills/lab-nocobase-flow-generator/references/doc-standard.md +84 -84
- package/skills/lab-nocobase-flow-generator/references/runtime-api.md +224 -224
- package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/346/250/241/346/235/277.md +121 -121
- package/skills/lab-nocobase-flow-generator/templates//350/204/232/346/234/254/351/200/273/350/276/221/346/226/207/346/241/243/347/244/272/344/276/213.md +67 -67
- package/skills/lab-orbit-component-builder/SKILL.md +353 -353
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.env.local.example +27 -27
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintignore +7 -7
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintrc.cjs +88 -88
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/.nvmrc +1 -1
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/AgenticAppAPI.md +268 -268
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/Jenkinsfile +106 -106
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/OrbitAPI.md +453 -453
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/README.md +176 -176
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/public/index.html +12 -12
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/App.vue +151 -151
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/components/DevOpenerLauncher.vue +143 -143
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +77 -77
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +308 -308
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/mockXNBBitable.ts +119 -119
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/shims-vue.d.ts +6 -6
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/utils/devOpenerHost.ts +75 -75
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/index.html +13 -13
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +60 -60
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/api/agenticlabTickets.ts +110 -110
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/bitable.ts +4 -4
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/custom-page.ts +4 -4
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/index.ts +1 -1
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +19 -19
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/context.ts +15 -15
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/xnb-context.ts +70 -70
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +189 -189
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperCellDemo.ts +257 -257
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +555 -555
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerInitParams.ts +158 -158
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerTicketIds.ts +32 -32
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/orbitHttpClient.ts +110 -110
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts +92 -92
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +67 -67
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +140 -140
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +45 -45
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +170 -170
- package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +58 -58
- package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +65 -65
- package/skills/lab-orbit-component-builder/references/orbit-vue-conventions.md +133 -133
- package/skills/lab-orbit-component-builder/references/pool-schema-to-columns.md +67 -67
- package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +179 -179
- package/skills/lab-orbit-component-builder/references/xnb-context-vue-props.md +49 -49
- package/skills/lab-orbit-component-builder/references/xnbitable-vue-parity.md +32 -32
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 弹层 / 子页由 opener 打开时,用 URL `?trace=...` 与 opener `postMessage` 拉取完整初始化参数。
|
|
3
|
-
*
|
|
4
|
-
* **子页发送**:`{ msg: 'get-data', trace }`
|
|
5
|
-
*
|
|
6
|
-
* **宿主回包(流程页标准)**:
|
|
7
|
-
* `{ msg: 'get-data-response', trace: '<同一 trace>', ticket_ids, valve_id, ... }`
|
|
8
|
-
*
|
|
9
|
-
* **兼容旧约定**:`{ msg: 'data', trace, payload: <对象> }` 或 `msg: 'data'` 且字段平铺在根上。
|
|
10
|
-
*
|
|
11
|
-
* 监听器内校验 `e.source === window.opener`(跨源 postMessage 仍适用)。
|
|
12
|
-
*
|
|
13
|
-
* Dev 无 opener 或缺 trace 时返回 `skipped`;development 可读 `VITE_DEV_OPENER_INIT_JSON` 作回退。
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export type OpenerInitSkipped = 'no-opener-or-closed' | 'no-trace'
|
|
17
|
-
|
|
18
|
-
/** 流程页 / AgenticLab 宿主标准回包 msg */
|
|
19
|
-
export const OPENER_MSG_GET_DATA_RESPONSE = 'get-data-response'
|
|
20
|
-
|
|
21
|
-
/** 模板 / dev 兼容回包 msg */
|
|
22
|
-
export const OPENER_MSG_DATA = 'data'
|
|
23
|
-
|
|
24
|
-
export interface OpenerInitDeps {
|
|
25
|
-
loadingShow: () => void
|
|
26
|
-
loadingHide: () => void
|
|
27
|
-
notifyError: (msg: unknown, ...rest: unknown[]) => void
|
|
28
|
-
/** 等待 opener 回包超时,默认 10000ms */
|
|
29
|
-
timeoutMs?: number
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface OpenerInitResult {
|
|
33
|
-
ok: boolean
|
|
34
|
-
trace: string | null
|
|
35
|
-
/** 规范化后的业务参数(含 ticket_ids、valve_id 等) */
|
|
36
|
-
payload: unknown
|
|
37
|
-
skipped?: OpenerInitSkipped
|
|
38
|
-
/** 为 true 时表示 payload 来自 `VITE_DEV_OPENER_INIT_JSON`(仅 development) */
|
|
39
|
-
devFromEnv?: boolean
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 解析 `.env.local` 中的 `VITE_DEV_OPENER_INIT_JSON`;非 development 或未配置时返回 `null`。
|
|
44
|
-
*/
|
|
45
|
-
export function parseDevOpenerInitFromEnv (): unknown | null {
|
|
46
|
-
if (!import.meta.env.DEV) return null
|
|
47
|
-
const raw = import.meta.env.VITE_DEV_OPENER_INIT_JSON
|
|
48
|
-
if (raw == null || typeof raw !== 'string' || raw.trim() === '') return null
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(raw) as unknown
|
|
51
|
-
} catch {
|
|
52
|
-
console.warn('[openerInitParams] VITE_DEV_OPENER_INIT_JSON 不是合法 JSON,已忽略')
|
|
53
|
-
return null
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function devPayloadWhenSkipped (): OpenerInitResult | null {
|
|
58
|
-
const dev = parseDevOpenerInitFromEnv()
|
|
59
|
-
if (dev == null) return null
|
|
60
|
-
return { ok: true, trace: null, payload: dev, devFromEnv: true }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** 部分浏览器下 `e.source` 与缓存的 `opener` 引用不完全相等,需兼容 WindowProxy */
|
|
64
|
-
function isMessageFromOpener (e: MessageEvent, opener: Window): boolean {
|
|
65
|
-
if (e.source == null) return false
|
|
66
|
-
return e.source === opener || e.source === window.opener
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function omitMsgTrace (d: Record<string, unknown>): Record<string, unknown> {
|
|
70
|
-
const out: Record<string, unknown> = {}
|
|
71
|
-
for (const [k, v] of Object.entries(d)) {
|
|
72
|
-
if (k !== 'msg' && k !== 'trace') {
|
|
73
|
-
out[k] = v
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return out
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* 将宿主 postMessage 回包规范为业务 payload(供 `openerTicketIds` 等使用)。
|
|
81
|
-
* @returns 匹配 trace 且 msg 合法时返回对象,否则 `null`
|
|
82
|
-
*/
|
|
83
|
-
export function parseOpenerReplyMessage (
|
|
84
|
-
d: Record<string, unknown>,
|
|
85
|
-
trace: string
|
|
86
|
-
): unknown | null {
|
|
87
|
-
if (String(d.trace) !== String(trace)) return null
|
|
88
|
-
|
|
89
|
-
if (d.msg === OPENER_MSG_GET_DATA_RESPONSE) {
|
|
90
|
-
return omitMsgTrace(d)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (d.msg === OPENER_MSG_DATA) {
|
|
94
|
-
if ('payload' in d && d.payload != null && typeof d.payload === 'object') {
|
|
95
|
-
return d.payload
|
|
96
|
-
}
|
|
97
|
-
const flat = omitMsgTrace(d)
|
|
98
|
-
return Object.keys(flat).length > 0 ? flat : null
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return null
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* 拉取 opener 初始化参数
|
|
106
|
-
*/
|
|
107
|
-
export async function fetchOpenerInitPayload (deps: OpenerInitDeps): Promise<OpenerInitResult> {
|
|
108
|
-
const { loadingShow, loadingHide, notifyError, timeoutMs = 120000 } = deps
|
|
109
|
-
loadingShow()
|
|
110
|
-
try {
|
|
111
|
-
const opener = window.opener as Window | null
|
|
112
|
-
if (opener == null || opener.closed) {
|
|
113
|
-
const fromEnv = devPayloadWhenSkipped()
|
|
114
|
-
if (fromEnv != null) return fromEnv
|
|
115
|
-
return { ok: false, trace: null, payload: null, skipped: 'no-opener-or-closed' }
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const searchParams = new URLSearchParams(window.location.search)
|
|
119
|
-
const trace = searchParams.get('trace')
|
|
120
|
-
if (trace == null || trace === '') {
|
|
121
|
-
const fromEnv = devPayloadWhenSkipped()
|
|
122
|
-
if (fromEnv != null) return fromEnv
|
|
123
|
-
return { ok: false, trace: null, payload: null, skipped: 'no-trace' }
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const payload = await new Promise<unknown>((resolve: (v: unknown) => void, reject: (e?: unknown) => void) => {
|
|
127
|
-
const timer = window.setTimeout(() => {
|
|
128
|
-
window.removeEventListener('message', onMessage)
|
|
129
|
-
const err = new Error('Timeout')
|
|
130
|
-
notifyError(err, '', '初始化超时')
|
|
131
|
-
reject(err)
|
|
132
|
-
}, timeoutMs)
|
|
133
|
-
|
|
134
|
-
const onMessage = (e: MessageEvent): void => {
|
|
135
|
-
if (!isMessageFromOpener(e, opener)) return
|
|
136
|
-
const d = e.data as Record<string, unknown> | null
|
|
137
|
-
if (d == null || typeof d !== 'object') return
|
|
138
|
-
|
|
139
|
-
const parsed = parseOpenerReplyMessage(d, trace)
|
|
140
|
-
if (parsed == null) return
|
|
141
|
-
|
|
142
|
-
window.clearTimeout(timer)
|
|
143
|
-
window.removeEventListener('message', onMessage)
|
|
144
|
-
resolve(parsed)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
window.addEventListener('message', onMessage)
|
|
148
|
-
opener.postMessage({ msg: 'get-data', trace }, '*')
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
return { ok: true, trace, payload }
|
|
152
|
-
} catch (err) {
|
|
153
|
-
notifyError(err, '', '初始化失败')
|
|
154
|
-
throw err
|
|
155
|
-
} finally {
|
|
156
|
-
loadingHide()
|
|
157
|
-
}
|
|
158
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 弹层 / 子页由 opener 打开时,用 URL `?trace=...` 与 opener `postMessage` 拉取完整初始化参数。
|
|
3
|
+
*
|
|
4
|
+
* **子页发送**:`{ msg: 'get-data', trace }`
|
|
5
|
+
*
|
|
6
|
+
* **宿主回包(流程页标准)**:
|
|
7
|
+
* `{ msg: 'get-data-response', trace: '<同一 trace>', ticket_ids, valve_id, ... }`
|
|
8
|
+
*
|
|
9
|
+
* **兼容旧约定**:`{ msg: 'data', trace, payload: <对象> }` 或 `msg: 'data'` 且字段平铺在根上。
|
|
10
|
+
*
|
|
11
|
+
* 监听器内校验 `e.source === window.opener`(跨源 postMessage 仍适用)。
|
|
12
|
+
*
|
|
13
|
+
* Dev 无 opener 或缺 trace 时返回 `skipped`;development 可读 `VITE_DEV_OPENER_INIT_JSON` 作回退。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type OpenerInitSkipped = 'no-opener-or-closed' | 'no-trace'
|
|
17
|
+
|
|
18
|
+
/** 流程页 / AgenticLab 宿主标准回包 msg */
|
|
19
|
+
export const OPENER_MSG_GET_DATA_RESPONSE = 'get-data-response'
|
|
20
|
+
|
|
21
|
+
/** 模板 / dev 兼容回包 msg */
|
|
22
|
+
export const OPENER_MSG_DATA = 'data'
|
|
23
|
+
|
|
24
|
+
export interface OpenerInitDeps {
|
|
25
|
+
loadingShow: () => void
|
|
26
|
+
loadingHide: () => void
|
|
27
|
+
notifyError: (msg: unknown, ...rest: unknown[]) => void
|
|
28
|
+
/** 等待 opener 回包超时,默认 10000ms */
|
|
29
|
+
timeoutMs?: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface OpenerInitResult {
|
|
33
|
+
ok: boolean
|
|
34
|
+
trace: string | null
|
|
35
|
+
/** 规范化后的业务参数(含 ticket_ids、valve_id 等) */
|
|
36
|
+
payload: unknown
|
|
37
|
+
skipped?: OpenerInitSkipped
|
|
38
|
+
/** 为 true 时表示 payload 来自 `VITE_DEV_OPENER_INIT_JSON`(仅 development) */
|
|
39
|
+
devFromEnv?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 解析 `.env.local` 中的 `VITE_DEV_OPENER_INIT_JSON`;非 development 或未配置时返回 `null`。
|
|
44
|
+
*/
|
|
45
|
+
export function parseDevOpenerInitFromEnv (): unknown | null {
|
|
46
|
+
if (!import.meta.env.DEV) return null
|
|
47
|
+
const raw = import.meta.env.VITE_DEV_OPENER_INIT_JSON
|
|
48
|
+
if (raw == null || typeof raw !== 'string' || raw.trim() === '') return null
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(raw) as unknown
|
|
51
|
+
} catch {
|
|
52
|
+
console.warn('[openerInitParams] VITE_DEV_OPENER_INIT_JSON 不是合法 JSON,已忽略')
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function devPayloadWhenSkipped (): OpenerInitResult | null {
|
|
58
|
+
const dev = parseDevOpenerInitFromEnv()
|
|
59
|
+
if (dev == null) return null
|
|
60
|
+
return { ok: true, trace: null, payload: dev, devFromEnv: true }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 部分浏览器下 `e.source` 与缓存的 `opener` 引用不完全相等,需兼容 WindowProxy */
|
|
64
|
+
function isMessageFromOpener (e: MessageEvent, opener: Window): boolean {
|
|
65
|
+
if (e.source == null) return false
|
|
66
|
+
return e.source === opener || e.source === window.opener
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function omitMsgTrace (d: Record<string, unknown>): Record<string, unknown> {
|
|
70
|
+
const out: Record<string, unknown> = {}
|
|
71
|
+
for (const [k, v] of Object.entries(d)) {
|
|
72
|
+
if (k !== 'msg' && k !== 'trace') {
|
|
73
|
+
out[k] = v
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 将宿主 postMessage 回包规范为业务 payload(供 `openerTicketIds` 等使用)。
|
|
81
|
+
* @returns 匹配 trace 且 msg 合法时返回对象,否则 `null`
|
|
82
|
+
*/
|
|
83
|
+
export function parseOpenerReplyMessage (
|
|
84
|
+
d: Record<string, unknown>,
|
|
85
|
+
trace: string
|
|
86
|
+
): unknown | null {
|
|
87
|
+
if (String(d.trace) !== String(trace)) return null
|
|
88
|
+
|
|
89
|
+
if (d.msg === OPENER_MSG_GET_DATA_RESPONSE) {
|
|
90
|
+
return omitMsgTrace(d)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (d.msg === OPENER_MSG_DATA) {
|
|
94
|
+
if ('payload' in d && d.payload != null && typeof d.payload === 'object') {
|
|
95
|
+
return d.payload
|
|
96
|
+
}
|
|
97
|
+
const flat = omitMsgTrace(d)
|
|
98
|
+
return Object.keys(flat).length > 0 ? flat : null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 拉取 opener 初始化参数
|
|
106
|
+
*/
|
|
107
|
+
export async function fetchOpenerInitPayload (deps: OpenerInitDeps): Promise<OpenerInitResult> {
|
|
108
|
+
const { loadingShow, loadingHide, notifyError, timeoutMs = 120000 } = deps
|
|
109
|
+
loadingShow()
|
|
110
|
+
try {
|
|
111
|
+
const opener = window.opener as Window | null
|
|
112
|
+
if (opener == null || opener.closed) {
|
|
113
|
+
const fromEnv = devPayloadWhenSkipped()
|
|
114
|
+
if (fromEnv != null) return fromEnv
|
|
115
|
+
return { ok: false, trace: null, payload: null, skipped: 'no-opener-or-closed' }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const searchParams = new URLSearchParams(window.location.search)
|
|
119
|
+
const trace = searchParams.get('trace')
|
|
120
|
+
if (trace == null || trace === '') {
|
|
121
|
+
const fromEnv = devPayloadWhenSkipped()
|
|
122
|
+
if (fromEnv != null) return fromEnv
|
|
123
|
+
return { ok: false, trace: null, payload: null, skipped: 'no-trace' }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const payload = await new Promise<unknown>((resolve: (v: unknown) => void, reject: (e?: unknown) => void) => {
|
|
127
|
+
const timer = window.setTimeout(() => {
|
|
128
|
+
window.removeEventListener('message', onMessage)
|
|
129
|
+
const err = new Error('Timeout')
|
|
130
|
+
notifyError(err, '', '初始化超时')
|
|
131
|
+
reject(err)
|
|
132
|
+
}, timeoutMs)
|
|
133
|
+
|
|
134
|
+
const onMessage = (e: MessageEvent): void => {
|
|
135
|
+
if (!isMessageFromOpener(e, opener)) return
|
|
136
|
+
const d = e.data as Record<string, unknown> | null
|
|
137
|
+
if (d == null || typeof d !== 'object') return
|
|
138
|
+
|
|
139
|
+
const parsed = parseOpenerReplyMessage(d, trace)
|
|
140
|
+
if (parsed == null) return
|
|
141
|
+
|
|
142
|
+
window.clearTimeout(timer)
|
|
143
|
+
window.removeEventListener('message', onMessage)
|
|
144
|
+
resolve(parsed)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
window.addEventListener('message', onMessage)
|
|
148
|
+
opener.postMessage({ msg: 'get-data', trace }, '*')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
return { ok: true, trace, payload }
|
|
152
|
+
} catch (err) {
|
|
153
|
+
notifyError(err, '', '初始化失败')
|
|
154
|
+
throw err
|
|
155
|
+
} finally {
|
|
156
|
+
loadingHide()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 从 opener 回包 `payload` 中解析 `ticket_ids`,供 `GET /api/ticket/list` 的 `ticket_id__in` 使用。
|
|
3
|
-
* 支持:`string[]`、`string`(逗号/分号/空白分隔)。
|
|
4
|
-
*/
|
|
5
|
-
export function extractTicketIdsFromPayload (payload: unknown): string[] {
|
|
6
|
-
if (payload == null || typeof payload !== 'object') return []
|
|
7
|
-
const raw = (payload as Record<string, unknown>).ticket_ids
|
|
8
|
-
if (Array.isArray(raw)) {
|
|
9
|
-
return raw.map((x: unknown) => String(x).trim()).filter((s: string) => s.length > 0)
|
|
10
|
-
}
|
|
11
|
-
if (typeof raw === 'string') {
|
|
12
|
-
return raw
|
|
13
|
-
.split(/[,;\s]+/)
|
|
14
|
-
.map((s: string) => s.trim())
|
|
15
|
-
.filter((s: string) => s.length > 0)
|
|
16
|
-
}
|
|
17
|
-
return []
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 从 opener `payload` 解析 **`valve_id`**,供 `POST /api/execution/complete` 请求体使用。
|
|
22
|
-
*/
|
|
23
|
-
export function extractValveIdFromPayload (payload: unknown): number | null {
|
|
24
|
-
if (payload == null || typeof payload !== 'object') return null
|
|
25
|
-
const raw = (payload as Record<string, unknown>).valve_id
|
|
26
|
-
if (typeof raw === 'number' && Number.isFinite(raw)) return raw
|
|
27
|
-
if (typeof raw === 'string' && raw.trim() !== '') {
|
|
28
|
-
const n = Number(raw)
|
|
29
|
-
if (Number.isFinite(n)) return n
|
|
30
|
-
}
|
|
31
|
-
return null
|
|
32
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 从 opener 回包 `payload` 中解析 `ticket_ids`,供 `GET /api/ticket/list` 的 `ticket_id__in` 使用。
|
|
3
|
+
* 支持:`string[]`、`string`(逗号/分号/空白分隔)。
|
|
4
|
+
*/
|
|
5
|
+
export function extractTicketIdsFromPayload (payload: unknown): string[] {
|
|
6
|
+
if (payload == null || typeof payload !== 'object') return []
|
|
7
|
+
const raw = (payload as Record<string, unknown>).ticket_ids
|
|
8
|
+
if (Array.isArray(raw)) {
|
|
9
|
+
return raw.map((x: unknown) => String(x).trim()).filter((s: string) => s.length > 0)
|
|
10
|
+
}
|
|
11
|
+
if (typeof raw === 'string') {
|
|
12
|
+
return raw
|
|
13
|
+
.split(/[,;\s]+/)
|
|
14
|
+
.map((s: string) => s.trim())
|
|
15
|
+
.filter((s: string) => s.length > 0)
|
|
16
|
+
}
|
|
17
|
+
return []
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从 opener `payload` 解析 **`valve_id`**,供 `POST /api/execution/complete` 请求体使用。
|
|
22
|
+
*/
|
|
23
|
+
export function extractValveIdFromPayload (payload: unknown): number | null {
|
|
24
|
+
if (payload == null || typeof payload !== 'object') return null
|
|
25
|
+
const raw = (payload as Record<string, unknown>).valve_id
|
|
26
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) return raw
|
|
27
|
+
if (typeof raw === 'string' && raw.trim() !== '') {
|
|
28
|
+
const n = Number(raw)
|
|
29
|
+
if (Number.isFinite(n)) return n
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Orbit 运行时 HTTP:优先 `window.xnb.http.client.request`(与 Book 代理、鉴权一致),
|
|
3
|
-
* 无注入时回退 `fetch`。约定见仓库根目录 `OrbitAPI.md` §6.3。
|
|
4
|
-
*
|
|
5
|
-
* 请求体形状与 **`dev/src/global.d.ts`** 中 `XnbHttpClient.request` 对齐,避免与宿主注入类型冲突。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type OrbitHttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'
|
|
9
|
-
|
|
10
|
-
export interface OrbitRequestJsonInput {
|
|
11
|
-
method: OrbitHttpMethod
|
|
12
|
-
/** 完整 URL(已含 origin),如 `https://host/api/ticket/list` */
|
|
13
|
-
url: string
|
|
14
|
-
headers?: Record<string, string>
|
|
15
|
-
/** GET 时作为 query;值会转成 string */
|
|
16
|
-
query?: Record<string, string | number | boolean | undefined | null>
|
|
17
|
-
/** POST/PUT/PATCH 时作为 JSON 体(仅在使用 Orbit `Body.json` 时) */
|
|
18
|
-
jsonBody?: Record<string, unknown>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** 与 `dev/src/global.d.ts` → `XnbHttpClient.request` 的 `options` 一致 */
|
|
22
|
-
export interface XnbHttpClientRequestOptions {
|
|
23
|
-
method: string
|
|
24
|
-
url: string
|
|
25
|
-
headers?: Record<string, string>
|
|
26
|
-
responseType?: number
|
|
27
|
-
query?: Record<string, string>
|
|
28
|
-
body?: { type: string; data: unknown }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getXnbHttp (): Window['xnb']['http'] | undefined {
|
|
32
|
-
return window.xnb?.http
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 将 `client.request` 返回值规范为「业务 JSON 根对象」。
|
|
37
|
-
* 兼容 `res` 或 `res.data` 为 `{ code, data, msg }` 的形态。
|
|
38
|
-
*/
|
|
39
|
-
export function unwrapOrbitClientData (res: unknown): unknown {
|
|
40
|
-
if (res == null) return res
|
|
41
|
-
if (typeof res !== 'object') return res
|
|
42
|
-
const r = res as Record<string, unknown>
|
|
43
|
-
if ('code' in r && ('data' in r || 'msg' in r)) return res
|
|
44
|
-
if ('data' in r) {
|
|
45
|
-
const inner = r.data
|
|
46
|
-
if (inner != null && typeof inner === 'object' && 'code' in (inner as Record<string, unknown>)) {
|
|
47
|
-
return inner
|
|
48
|
-
}
|
|
49
|
-
return inner
|
|
50
|
-
}
|
|
51
|
-
return res
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 使用 Orbit `http.client.request`(若存在)或回退 `fetch` 发 JSON 请求。
|
|
56
|
-
*/
|
|
57
|
-
export async function orbitRequestJson (input: OrbitRequestJsonInput): Promise<unknown> {
|
|
58
|
-
const http = getXnbHttp()
|
|
59
|
-
const client = http?.client
|
|
60
|
-
const RT = http?.ResponseType
|
|
61
|
-
const Body = http?.Body
|
|
62
|
-
|
|
63
|
-
if (client?.request != null && RT?.JSON != null) {
|
|
64
|
-
const headers: Record<string, string> = {
|
|
65
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
66
|
-
...(input.headers ?? {})
|
|
67
|
-
}
|
|
68
|
-
const req: XnbHttpClientRequestOptions = {
|
|
69
|
-
method: input.method,
|
|
70
|
-
url: input.url,
|
|
71
|
-
headers,
|
|
72
|
-
responseType: RT.JSON
|
|
73
|
-
}
|
|
74
|
-
if (input.query != null) {
|
|
75
|
-
const q: Record<string, string> = {}
|
|
76
|
-
for (const [k, v] of Object.entries(input.query)) {
|
|
77
|
-
if (v !== undefined && v !== null) {
|
|
78
|
-
q[k] = String(v)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (Object.keys(q).length > 0) req.query = q
|
|
82
|
-
}
|
|
83
|
-
if (input.jsonBody != null && Body?.json != null) {
|
|
84
|
-
req.body = Body.json(input.jsonBody) as { type: string; data: unknown }
|
|
85
|
-
}
|
|
86
|
-
const raw = await client.request(req)
|
|
87
|
-
return unwrapOrbitClientData(raw.data ?? raw)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (input.method !== 'get') {
|
|
91
|
-
throw new Error('orbitRequestJson: 非 GET 且无 window.xnb.http.client 时无法回退 fetch')
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const u = new URL(input.url)
|
|
95
|
-
if (input.query != null) {
|
|
96
|
-
for (const [k, v] of Object.entries(input.query)) {
|
|
97
|
-
if (v !== undefined && v !== null) {
|
|
98
|
-
u.searchParams.set(k, String(v))
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const res = await fetch(u.toString(), {
|
|
103
|
-
method: 'GET',
|
|
104
|
-
headers: input.headers ?? { Accept: 'application/json' }
|
|
105
|
-
})
|
|
106
|
-
if (!res.ok) {
|
|
107
|
-
throw new Error(`HTTP ${String(res.status)}`)
|
|
108
|
-
}
|
|
109
|
-
return await res.json() as unknown
|
|
110
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Orbit 运行时 HTTP:优先 `window.xnb.http.client.request`(与 Book 代理、鉴权一致),
|
|
3
|
+
* 无注入时回退 `fetch`。约定见仓库根目录 `OrbitAPI.md` §6.3。
|
|
4
|
+
*
|
|
5
|
+
* 请求体形状与 **`dev/src/global.d.ts`** 中 `XnbHttpClient.request` 对齐,避免与宿主注入类型冲突。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type OrbitHttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'
|
|
9
|
+
|
|
10
|
+
export interface OrbitRequestJsonInput {
|
|
11
|
+
method: OrbitHttpMethod
|
|
12
|
+
/** 完整 URL(已含 origin),如 `https://host/api/ticket/list` */
|
|
13
|
+
url: string
|
|
14
|
+
headers?: Record<string, string>
|
|
15
|
+
/** GET 时作为 query;值会转成 string */
|
|
16
|
+
query?: Record<string, string | number | boolean | undefined | null>
|
|
17
|
+
/** POST/PUT/PATCH 时作为 JSON 体(仅在使用 Orbit `Body.json` 时) */
|
|
18
|
+
jsonBody?: Record<string, unknown>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 与 `dev/src/global.d.ts` → `XnbHttpClient.request` 的 `options` 一致 */
|
|
22
|
+
export interface XnbHttpClientRequestOptions {
|
|
23
|
+
method: string
|
|
24
|
+
url: string
|
|
25
|
+
headers?: Record<string, string>
|
|
26
|
+
responseType?: number
|
|
27
|
+
query?: Record<string, string>
|
|
28
|
+
body?: { type: string; data: unknown }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getXnbHttp (): Window['xnb']['http'] | undefined {
|
|
32
|
+
return window.xnb?.http
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 将 `client.request` 返回值规范为「业务 JSON 根对象」。
|
|
37
|
+
* 兼容 `res` 或 `res.data` 为 `{ code, data, msg }` 的形态。
|
|
38
|
+
*/
|
|
39
|
+
export function unwrapOrbitClientData (res: unknown): unknown {
|
|
40
|
+
if (res == null) return res
|
|
41
|
+
if (typeof res !== 'object') return res
|
|
42
|
+
const r = res as Record<string, unknown>
|
|
43
|
+
if ('code' in r && ('data' in r || 'msg' in r)) return res
|
|
44
|
+
if ('data' in r) {
|
|
45
|
+
const inner = r.data
|
|
46
|
+
if (inner != null && typeof inner === 'object' && 'code' in (inner as Record<string, unknown>)) {
|
|
47
|
+
return inner
|
|
48
|
+
}
|
|
49
|
+
return inner
|
|
50
|
+
}
|
|
51
|
+
return res
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 使用 Orbit `http.client.request`(若存在)或回退 `fetch` 发 JSON 请求。
|
|
56
|
+
*/
|
|
57
|
+
export async function orbitRequestJson (input: OrbitRequestJsonInput): Promise<unknown> {
|
|
58
|
+
const http = getXnbHttp()
|
|
59
|
+
const client = http?.client
|
|
60
|
+
const RT = http?.ResponseType
|
|
61
|
+
const Body = http?.Body
|
|
62
|
+
|
|
63
|
+
if (client?.request != null && RT?.JSON != null) {
|
|
64
|
+
const headers: Record<string, string> = {
|
|
65
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
66
|
+
...(input.headers ?? {})
|
|
67
|
+
}
|
|
68
|
+
const req: XnbHttpClientRequestOptions = {
|
|
69
|
+
method: input.method,
|
|
70
|
+
url: input.url,
|
|
71
|
+
headers,
|
|
72
|
+
responseType: RT.JSON
|
|
73
|
+
}
|
|
74
|
+
if (input.query != null) {
|
|
75
|
+
const q: Record<string, string> = {}
|
|
76
|
+
for (const [k, v] of Object.entries(input.query)) {
|
|
77
|
+
if (v !== undefined && v !== null) {
|
|
78
|
+
q[k] = String(v)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (Object.keys(q).length > 0) req.query = q
|
|
82
|
+
}
|
|
83
|
+
if (input.jsonBody != null && Body?.json != null) {
|
|
84
|
+
req.body = Body.json(input.jsonBody) as { type: string; data: unknown }
|
|
85
|
+
}
|
|
86
|
+
const raw = await client.request(req)
|
|
87
|
+
return unwrapOrbitClientData(raw.data ?? raw)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (input.method !== 'get') {
|
|
91
|
+
throw new Error('orbitRequestJson: 非 GET 且无 window.xnb.http.client 时无法回退 fetch')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const u = new URL(input.url)
|
|
95
|
+
if (input.query != null) {
|
|
96
|
+
for (const [k, v] of Object.entries(input.query)) {
|
|
97
|
+
if (v !== undefined && v !== null) {
|
|
98
|
+
u.searchParams.set(k, String(v))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const res = await fetch(u.toString(), {
|
|
103
|
+
method: 'GET',
|
|
104
|
+
headers: input.headers ?? { Accept: 'application/json' }
|
|
105
|
+
})
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
throw new Error(`HTTP ${String(res.status)}`)
|
|
108
|
+
}
|
|
109
|
+
return await res.json() as unknown
|
|
110
|
+
}
|