@nbt-dev/components 0.1.0 → 0.1.2
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/core/index.d.ts +2 -0
- package/dist/core/use-workflows.d.ts +68 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/core/index.ts +11 -0
- package/src/core/use-workflows.ts +171 -0
package/dist/core/index.d.ts
CHANGED
|
@@ -7,5 +7,7 @@ export { useLiveBulkRegistry } from "./use-cartridge-info";
|
|
|
7
7
|
export type { BulkEntity, BulkRegistry, LiveRegistryState, } from "./use-cartridge-info";
|
|
8
8
|
export { useGsheetsApi } from "./use-gsheets";
|
|
9
9
|
export type { GsheetSyncConfig, GsheetSaveInput } from "./use-gsheets";
|
|
10
|
+
export { useWorkflowsApi, isWfTerminal, WF_TERMINAL } from "./use-workflows";
|
|
11
|
+
export type { WfStatus, WfListItem, WfExecution, WfEvent, WfDetail, WfStats, WfVersion, WfExecReply, } from "./use-workflows";
|
|
10
12
|
export { getDevToolsToken, setDevToolsToken, clearDevToolsToken, authHeaders, fetchWhoAmI, devToolsSignIn, devToolsSignUp, devToolsSignOut, wsAuthProtocols, fetchDevtoolsSettings, saveDevtoolsSettings, } from "./auth";
|
|
11
13
|
export type { WhoAmI } from "./auth";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export declare const WF_TERMINAL: readonly ["COMPLETED", "FAILED", "CANCELLED", "CONTINUED"];
|
|
2
|
+
export type WfStatus = "RUNNING" | "SUSPENDED" | "COMPLETED" | "FAILED" | "CANCELLED" | "CONTINUED";
|
|
3
|
+
export type WfListItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
status: WfStatus;
|
|
6
|
+
workflowName: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
updatedAt: number;
|
|
9
|
+
lane: string;
|
|
10
|
+
deployVersion: number;
|
|
11
|
+
};
|
|
12
|
+
export type WfExecution = {
|
|
13
|
+
id: string;
|
|
14
|
+
status: WfStatus;
|
|
15
|
+
workflowName: string;
|
|
16
|
+
args?: string;
|
|
17
|
+
result?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
cursor: number;
|
|
20
|
+
lane?: string;
|
|
21
|
+
deployVersion: number;
|
|
22
|
+
createdAt: number;
|
|
23
|
+
updatedAt: number;
|
|
24
|
+
};
|
|
25
|
+
export type WfEvent = {
|
|
26
|
+
id: string;
|
|
27
|
+
seq: number;
|
|
28
|
+
kind: string;
|
|
29
|
+
capability?: string;
|
|
30
|
+
target?: string;
|
|
31
|
+
op?: string;
|
|
32
|
+
payload?: string;
|
|
33
|
+
};
|
|
34
|
+
export type WfDetail = {
|
|
35
|
+
execution: WfExecution;
|
|
36
|
+
events: WfEvent[];
|
|
37
|
+
};
|
|
38
|
+
export type WfStats = {
|
|
39
|
+
writes: number;
|
|
40
|
+
reads: number;
|
|
41
|
+
emails: number;
|
|
42
|
+
fetches: number;
|
|
43
|
+
emits: number;
|
|
44
|
+
retries: number;
|
|
45
|
+
async_inflight: number;
|
|
46
|
+
async_peak: number;
|
|
47
|
+
};
|
|
48
|
+
export type WfVersion = {
|
|
49
|
+
version: number;
|
|
50
|
+
refcount: number;
|
|
51
|
+
current: boolean;
|
|
52
|
+
};
|
|
53
|
+
export type WfExecReply = {
|
|
54
|
+
id: string;
|
|
55
|
+
outcome: string;
|
|
56
|
+
status: string;
|
|
57
|
+
result?: string;
|
|
58
|
+
error?: string;
|
|
59
|
+
};
|
|
60
|
+
export declare function isWfTerminal(status: string): boolean;
|
|
61
|
+
export declare function useWorkflowsApi(): {
|
|
62
|
+
listExecutions: () => Promise<WfListItem[]>;
|
|
63
|
+
getExecution: (id: string) => Promise<WfDetail>;
|
|
64
|
+
getStats: () => Promise<WfStats>;
|
|
65
|
+
getVersions: () => Promise<WfVersion[]>;
|
|
66
|
+
cancel: (id: string) => Promise<WfExecReply>;
|
|
67
|
+
advance: (id: string) => Promise<WfExecReply>;
|
|
68
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -162,6 +162,83 @@ function useGsheetsApi() {
|
|
|
162
162
|
);
|
|
163
163
|
return useMemo(() => ({ list, save, syncNow, remove }), [list, save, syncNow, remove]);
|
|
164
164
|
}
|
|
165
|
+
|
|
166
|
+
// src/core/use-workflows.ts
|
|
167
|
+
import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
168
|
+
var WF_TERMINAL = ["COMPLETED", "FAILED", "CANCELLED", "CONTINUED"];
|
|
169
|
+
async function jsonOrThrow2(r) {
|
|
170
|
+
const body = await r.json().catch(() => ({}));
|
|
171
|
+
if (!r.ok) throw new Error(body?.error ?? `HTTP ${r.status}`);
|
|
172
|
+
return body;
|
|
173
|
+
}
|
|
174
|
+
function isWfTerminal(status) {
|
|
175
|
+
return WF_TERMINAL.includes(status);
|
|
176
|
+
}
|
|
177
|
+
function useWorkflowsApi() {
|
|
178
|
+
const { apiBaseUrl } = useDevToolsConfig();
|
|
179
|
+
const listExecutions = useCallback2(
|
|
180
|
+
async () => jsonOrThrow2(
|
|
181
|
+
await fetch(`${apiBaseUrl}/_console/wf`, {
|
|
182
|
+
credentials: "include",
|
|
183
|
+
headers: authHeaders()
|
|
184
|
+
})
|
|
185
|
+
),
|
|
186
|
+
[apiBaseUrl]
|
|
187
|
+
);
|
|
188
|
+
const getExecution = useCallback2(
|
|
189
|
+
async (id) => jsonOrThrow2(
|
|
190
|
+
await fetch(`${apiBaseUrl}/_console/wf/${encodeURIComponent(id)}`, {
|
|
191
|
+
credentials: "include",
|
|
192
|
+
headers: authHeaders()
|
|
193
|
+
})
|
|
194
|
+
),
|
|
195
|
+
[apiBaseUrl]
|
|
196
|
+
);
|
|
197
|
+
const getStats = useCallback2(
|
|
198
|
+
async () => jsonOrThrow2(
|
|
199
|
+
await fetch(`${apiBaseUrl}/_console/wf/stats`, {
|
|
200
|
+
credentials: "include",
|
|
201
|
+
headers: authHeaders()
|
|
202
|
+
})
|
|
203
|
+
),
|
|
204
|
+
[apiBaseUrl]
|
|
205
|
+
);
|
|
206
|
+
const getVersions = useCallback2(
|
|
207
|
+
async () => jsonOrThrow2(
|
|
208
|
+
await fetch(`${apiBaseUrl}/_console/wf/versions`, {
|
|
209
|
+
credentials: "include",
|
|
210
|
+
headers: authHeaders()
|
|
211
|
+
})
|
|
212
|
+
),
|
|
213
|
+
[apiBaseUrl]
|
|
214
|
+
);
|
|
215
|
+
const cancel = useCallback2(
|
|
216
|
+
async (id) => jsonOrThrow2(
|
|
217
|
+
await fetch(`${apiBaseUrl}/_console/wf/cancel`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
credentials: "include",
|
|
220
|
+
headers: authHeaders({ "content-type": "application/json" }),
|
|
221
|
+
body: JSON.stringify({ id })
|
|
222
|
+
})
|
|
223
|
+
),
|
|
224
|
+
[apiBaseUrl]
|
|
225
|
+
);
|
|
226
|
+
const advance = useCallback2(
|
|
227
|
+
async (id) => jsonOrThrow2(
|
|
228
|
+
await fetch(`${apiBaseUrl}/_console/wf/advance`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
credentials: "include",
|
|
231
|
+
headers: authHeaders({ "content-type": "application/json" }),
|
|
232
|
+
body: JSON.stringify({ id })
|
|
233
|
+
})
|
|
234
|
+
),
|
|
235
|
+
[apiBaseUrl]
|
|
236
|
+
);
|
|
237
|
+
return useMemo2(
|
|
238
|
+
() => ({ listExecutions, getExecution, getStats, getVersions, cancel, advance }),
|
|
239
|
+
[listExecutions, getExecution, getStats, getVersions, cancel, advance]
|
|
240
|
+
);
|
|
241
|
+
}
|
|
165
242
|
export {
|
|
166
243
|
BulkStreamProvider,
|
|
167
244
|
data_table_default as DataTable,
|
|
@@ -172,6 +249,7 @@ export {
|
|
|
172
249
|
NbtEditor,
|
|
173
250
|
NbtLspClient,
|
|
174
251
|
value_popover_default as ValuePopover,
|
|
252
|
+
WF_TERMINAL,
|
|
175
253
|
authHeaders,
|
|
176
254
|
buildEntityGraphModel,
|
|
177
255
|
cartsFromContracts,
|
|
@@ -185,6 +263,7 @@ export {
|
|
|
185
263
|
fetchWhoAmI,
|
|
186
264
|
filterEntityGraphModel,
|
|
187
265
|
getDevToolsToken,
|
|
266
|
+
isWfTerminal,
|
|
188
267
|
lspExtensions,
|
|
189
268
|
nbtLanguage,
|
|
190
269
|
nbtLanguageSupport,
|
|
@@ -196,6 +275,7 @@ export {
|
|
|
196
275
|
useDevToolsConfig,
|
|
197
276
|
useGsheetsApi,
|
|
198
277
|
useLiveBulkRegistry,
|
|
278
|
+
useWorkflowsApi,
|
|
199
279
|
wsAuthProtocols,
|
|
200
280
|
wsBaseFrom
|
|
201
281
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/core/use-cartridge-info.ts", "../src/core/use-gsheets.ts"],
|
|
4
|
-
"sourcesContent": ["import { useEffect, useState } from \"react\";\nimport { useDevToolsConfig } from \"./config\";\nimport { authHeaders } from \"./auth\";\n\n// Live cartridge/entity registry for the Data tab. Instead of the build-time\n// `BULK_REGISTRY` (every cart in the repo, generated by `nbt generate`), we\n// read the daemon's `/_console/contracts` \u2014 which only lists *running*\n// cartridges and carries each entity's `searchFields`. Bulk WS routes are\n// derived live (`/_ws/bulk/<cart>/<entity-lower>`) and fed straight into\n// useBulkStream. So the tab reflects what is actually installed right now.\n\nexport type BulkEntity = {\n name: string;\n route: string;\n searchFields: readonly string[];\n};\nexport type BulkRegistry = Record<string, BulkEntity[]>;\n\ntype Contract = {\n cartridge?: string;\n core?: boolean;\n installed?: boolean;\n owns?: Record<string, { searchFields?: string[] }>;\n};\n\nfunction buildRegistry(\n contracts: Contract[],\n): { reg: BulkRegistry; core: Set<string>; installed: Set<string> } {\n const reg: BulkRegistry = {};\n const core = new Set<string>();\n const installed = new Set<string>();\n for (const c of contracts) {\n const cart = c.cartridge;\n if (!cart || !c.owns) continue;\n const entities: BulkEntity[] = [];\n for (const [name, ent] of Object.entries(c.owns)) {\n const sf = Array.isArray(ent?.searchFields) ? ent.searchFields : [];\n entities.push({\n name,\n route: `/_ws/bulk/${cart}/${name.toLowerCase()}`,\n searchFields: sf,\n });\n }\n if (entities.length > 0) {\n reg[cart] = entities;\n if (c.core) core.add(cart);\n if (c.installed) installed.add(cart);\n }\n }\n return { reg, core, installed };\n}\n\nexport type LiveRegistryState = {\n registry: BulkRegistry;\n carts: string[];\n // Cartridge slugs flagged `core` by the daemon. The Data tab keeps `auth`\n // visible but tucks the rest of these behind a \"more cartridges\" menu so\n // deployed (user) cartridges are what's shown by default.\n coreCarts: Set<string>;\n // Cartridge slugs the user (or boot) explicitly installed/activated. The Data\n // tab shows these (plus `auth`) by default; bundled-but-not-installed carts\n // stay behind the \"more cartridges\" menu until the user opens them.\n installedCarts: Set<string>;\n loading: boolean;\n error: string | null;\n};\n\nexport function useLiveBulkRegistry(): LiveRegistryState {\n const { apiBaseUrl } = useDevToolsConfig();\n const [registry, setRegistry] = useState<BulkRegistry>({});\n const [coreCarts, setCoreCarts] = useState<Set<string>>(new Set());\n const [installedCarts, setInstalledCarts] = useState<Set<string>>(new Set());\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const ac = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n (async () => {\n try {\n const r = await fetch(`${apiBaseUrl}/_console/contracts`, {\n signal: ac.signal,\n credentials: \"include\",\n headers: authHeaders(),\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n const data = (await r.json()) as Contract[];\n if (!Array.isArray(data)) throw new Error(\"malformed contracts response\");\n if (cancelled) return;\n const { reg, core, installed } = buildRegistry(data);\n setRegistry(reg);\n setCoreCarts(core);\n setInstalledCarts(installed);\n } catch (e) {\n if (cancelled || (e as { name?: string }).name === \"AbortError\") return;\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n return () => {\n cancelled = true;\n ac.abort();\n };\n }, [apiBaseUrl]);\n\n const carts = Object.keys(registry).sort();\n return { registry, carts, coreCarts, installedCarts, loading, error };\n}\n", "// Fetchers + hook for the Google Sheets cartridge-sync admin endpoints\n// (/_console/integrations/gsheets/*). Admin-gated server-side \u2014 the requests\n// carry the devtools Bearer token (authHeaders) like the contracts probe.\n\nimport { useCallback, useMemo } from \"react\";\nimport { useDevToolsConfig } from \"./config\";\nimport { authHeaders } from \"./auth\";\n\nexport type GsheetSyncConfig = {\n cartridge: string;\n spreadsheetId: string;\n saEmail: string;\n hasServiceAccount: boolean;\n intervalSeconds: number;\n enabled: boolean;\n lastSyncAt: number; // ms epoch, 0 = never\n status: string; // \"ok\" | \"error\" | \"\"\n lastError: string;\n};\n\nexport type GsheetSaveInput = {\n cartridge: string;\n spreadsheetUrl: string;\n serviceAccountJson?: string; // omit to keep the stored key\n intervalSeconds: number;\n enabled: boolean;\n};\n\nasync function jsonOrThrow(r: Response) {\n const body = await r.json().catch(() => ({}));\n if (!r.ok) throw new Error((body as { error?: string })?.error ?? `HTTP ${r.status}`);\n return body;\n}\n\nexport function useGsheetsApi() {\n const { apiBaseUrl } = useDevToolsConfig();\n\n const list = useCallback(\n async (): Promise<GsheetSyncConfig[]> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const save = useCallback(\n async (input: GsheetSaveInput): Promise<GsheetSyncConfig> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify(input),\n }),\n ),\n [apiBaseUrl],\n );\n\n const syncNow = useCallback(\n async (cartridge: string): Promise<{ ok: boolean; error: string }> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets/sync`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify({ cartridge }),\n }),\n ),\n [apiBaseUrl],\n );\n\n const remove = useCallback(\n async (cartridge: string): Promise<{ ok: boolean }> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets/${encodeURIComponent(cartridge)}`, {\n method: \"DELETE\",\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n return useMemo(() => ({ list, save, syncNow, remove }), [list, save, syncNow, remove]);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
3
|
+
"sources": ["../src/core/use-cartridge-info.ts", "../src/core/use-gsheets.ts", "../src/core/use-workflows.ts"],
|
|
4
|
+
"sourcesContent": ["import { useEffect, useState } from \"react\";\nimport { useDevToolsConfig } from \"./config\";\nimport { authHeaders } from \"./auth\";\n\n// Live cartridge/entity registry for the Data tab. Instead of the build-time\n// `BULK_REGISTRY` (every cart in the repo, generated by `nbt generate`), we\n// read the daemon's `/_console/contracts` \u2014 which only lists *running*\n// cartridges and carries each entity's `searchFields`. Bulk WS routes are\n// derived live (`/_ws/bulk/<cart>/<entity-lower>`) and fed straight into\n// useBulkStream. So the tab reflects what is actually installed right now.\n\nexport type BulkEntity = {\n name: string;\n route: string;\n searchFields: readonly string[];\n};\nexport type BulkRegistry = Record<string, BulkEntity[]>;\n\ntype Contract = {\n cartridge?: string;\n core?: boolean;\n installed?: boolean;\n owns?: Record<string, { searchFields?: string[] }>;\n};\n\nfunction buildRegistry(\n contracts: Contract[],\n): { reg: BulkRegistry; core: Set<string>; installed: Set<string> } {\n const reg: BulkRegistry = {};\n const core = new Set<string>();\n const installed = new Set<string>();\n for (const c of contracts) {\n const cart = c.cartridge;\n if (!cart || !c.owns) continue;\n const entities: BulkEntity[] = [];\n for (const [name, ent] of Object.entries(c.owns)) {\n const sf = Array.isArray(ent?.searchFields) ? ent.searchFields : [];\n entities.push({\n name,\n route: `/_ws/bulk/${cart}/${name.toLowerCase()}`,\n searchFields: sf,\n });\n }\n if (entities.length > 0) {\n reg[cart] = entities;\n if (c.core) core.add(cart);\n if (c.installed) installed.add(cart);\n }\n }\n return { reg, core, installed };\n}\n\nexport type LiveRegistryState = {\n registry: BulkRegistry;\n carts: string[];\n // Cartridge slugs flagged `core` by the daemon. The Data tab keeps `auth`\n // visible but tucks the rest of these behind a \"more cartridges\" menu so\n // deployed (user) cartridges are what's shown by default.\n coreCarts: Set<string>;\n // Cartridge slugs the user (or boot) explicitly installed/activated. The Data\n // tab shows these (plus `auth`) by default; bundled-but-not-installed carts\n // stay behind the \"more cartridges\" menu until the user opens them.\n installedCarts: Set<string>;\n loading: boolean;\n error: string | null;\n};\n\nexport function useLiveBulkRegistry(): LiveRegistryState {\n const { apiBaseUrl } = useDevToolsConfig();\n const [registry, setRegistry] = useState<BulkRegistry>({});\n const [coreCarts, setCoreCarts] = useState<Set<string>>(new Set());\n const [installedCarts, setInstalledCarts] = useState<Set<string>>(new Set());\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n const ac = new AbortController();\n let cancelled = false;\n setLoading(true);\n setError(null);\n (async () => {\n try {\n const r = await fetch(`${apiBaseUrl}/_console/contracts`, {\n signal: ac.signal,\n credentials: \"include\",\n headers: authHeaders(),\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n const data = (await r.json()) as Contract[];\n if (!Array.isArray(data)) throw new Error(\"malformed contracts response\");\n if (cancelled) return;\n const { reg, core, installed } = buildRegistry(data);\n setRegistry(reg);\n setCoreCarts(core);\n setInstalledCarts(installed);\n } catch (e) {\n if (cancelled || (e as { name?: string }).name === \"AbortError\") return;\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n return () => {\n cancelled = true;\n ac.abort();\n };\n }, [apiBaseUrl]);\n\n const carts = Object.keys(registry).sort();\n return { registry, carts, coreCarts, installedCarts, loading, error };\n}\n", "// Fetchers + hook for the Google Sheets cartridge-sync admin endpoints\n// (/_console/integrations/gsheets/*). Admin-gated server-side \u2014 the requests\n// carry the devtools Bearer token (authHeaders) like the contracts probe.\n\nimport { useCallback, useMemo } from \"react\";\nimport { useDevToolsConfig } from \"./config\";\nimport { authHeaders } from \"./auth\";\n\nexport type GsheetSyncConfig = {\n cartridge: string;\n spreadsheetId: string;\n saEmail: string;\n hasServiceAccount: boolean;\n intervalSeconds: number;\n enabled: boolean;\n lastSyncAt: number; // ms epoch, 0 = never\n status: string; // \"ok\" | \"error\" | \"\"\n lastError: string;\n};\n\nexport type GsheetSaveInput = {\n cartridge: string;\n spreadsheetUrl: string;\n serviceAccountJson?: string; // omit to keep the stored key\n intervalSeconds: number;\n enabled: boolean;\n};\n\nasync function jsonOrThrow(r: Response) {\n const body = await r.json().catch(() => ({}));\n if (!r.ok) throw new Error((body as { error?: string })?.error ?? `HTTP ${r.status}`);\n return body;\n}\n\nexport function useGsheetsApi() {\n const { apiBaseUrl } = useDevToolsConfig();\n\n const list = useCallback(\n async (): Promise<GsheetSyncConfig[]> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const save = useCallback(\n async (input: GsheetSaveInput): Promise<GsheetSyncConfig> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify(input),\n }),\n ),\n [apiBaseUrl],\n );\n\n const syncNow = useCallback(\n async (cartridge: string): Promise<{ ok: boolean; error: string }> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets/sync`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify({ cartridge }),\n }),\n ),\n [apiBaseUrl],\n );\n\n const remove = useCallback(\n async (cartridge: string): Promise<{ ok: boolean }> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/integrations/gsheets/${encodeURIComponent(cartridge)}`, {\n method: \"DELETE\",\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n return useMemo(() => ({ list, save, syncNow, remove }), [list, save, syncNow, remove]);\n}\n", "// Fetchers + hook for the workflow-engine introspection endpoints\n// (/_console/wf*). The read routes are ungated like /_console/metrics; the two\n// mutating ones (cancel/advance) are admin-gated server-side, so every request\n// carries the devtools Bearer token (authHeaders) like the contracts probe.\n\nimport { useCallback, useMemo } from \"react\";\nimport { useDevToolsConfig } from \"./config\";\nimport { authHeaders } from \"./auth\";\n\n// Terminal statuses a workflow can settle into \u2014 used to decide whether to keep\n// polling an open instance and whether Cancel is offered.\nexport const WF_TERMINAL = [\"COMPLETED\", \"FAILED\", \"CANCELLED\", \"CONTINUED\"] as const;\n\nexport type WfStatus =\n | \"RUNNING\"\n | \"SUSPENDED\"\n | \"COMPLETED\"\n | \"FAILED\"\n | \"CANCELLED\"\n | \"CONTINUED\";\n\n// Row from GET /_console/wf \u2014 the enriched list (createdAt/updatedAt are unix ms).\nexport type WfListItem = {\n id: string;\n status: WfStatus;\n workflowName: string;\n createdAt: number;\n updatedAt: number;\n lane: string;\n deployVersion: number;\n};\n\n// The execution record from GET /_console/wf/:id (raw entity field names).\nexport type WfExecution = {\n id: string;\n status: WfStatus;\n workflowName: string;\n args?: string;\n result?: string;\n error?: string;\n cursor: number;\n lane?: string;\n deployVersion: number;\n createdAt: number;\n updatedAt: number;\n};\n\n// One journal entry. kind \u2208 HOST_CALL_INTENT | HOST_CALL_RESULT | SUSPENDED |\n// RESUMED | RETRY | COMPLETED | FAILED | CANCELLED | CONTINUED.\nexport type WfEvent = {\n id: string;\n seq: number;\n kind: string;\n capability?: string;\n target?: string;\n op?: string;\n payload?: string;\n};\n\nexport type WfDetail = { execution: WfExecution; events: WfEvent[] };\n\nexport type WfStats = {\n writes: number;\n reads: number;\n emails: number;\n fetches: number;\n emits: number;\n retries: number;\n async_inflight: number;\n async_peak: number;\n};\n\nexport type WfVersion = { version: number; refcount: number; current: boolean };\n\n// The exec-reply shape returned by run/signal/advance/cancel.\nexport type WfExecReply = {\n id: string;\n outcome: string;\n status: string;\n result?: string;\n error?: string;\n};\n\nasync function jsonOrThrow(r: Response) {\n const body = await r.json().catch(() => ({}));\n if (!r.ok) throw new Error((body as { error?: string })?.error ?? `HTTP ${r.status}`);\n return body;\n}\n\nexport function isWfTerminal(status: string): boolean {\n return (WF_TERMINAL as readonly string[]).includes(status);\n}\n\nexport function useWorkflowsApi() {\n const { apiBaseUrl } = useDevToolsConfig();\n\n const listExecutions = useCallback(\n async (): Promise<WfListItem[]> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const getExecution = useCallback(\n async (id: string): Promise<WfDetail> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf/${encodeURIComponent(id)}`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const getStats = useCallback(\n async (): Promise<WfStats> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf/stats`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const getVersions = useCallback(\n async (): Promise<WfVersion[]> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf/versions`, {\n credentials: \"include\",\n headers: authHeaders(),\n }),\n ),\n [apiBaseUrl],\n );\n\n const cancel = useCallback(\n async (id: string): Promise<WfExecReply> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf/cancel`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify({ id }),\n }),\n ),\n [apiBaseUrl],\n );\n\n const advance = useCallback(\n async (id: string): Promise<WfExecReply> =>\n jsonOrThrow(\n await fetch(`${apiBaseUrl}/_console/wf/advance`, {\n method: \"POST\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify({ id }),\n }),\n ),\n [apiBaseUrl],\n );\n\n return useMemo(\n () => ({ listExecutions, getExecution, getStats, getVersions, cancel, advance }),\n [listExecutions, getExecution, getStats, getVersions, cancel, advance],\n );\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,gBAAgB;AAyBpC,SAAS,cACP,WACkE;AAClE,QAAM,MAAoB,CAAC;AAC3B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,KAAK,WAAW;AACzB,UAAM,OAAO,EAAE;AACf,QAAI,CAAC,QAAQ,CAAC,EAAE,KAAM;AACtB,UAAM,WAAyB,CAAC;AAChC,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,EAAE,IAAI,GAAG;AAChD,YAAM,KAAK,MAAM,QAAQ,KAAK,YAAY,IAAI,IAAI,eAAe,CAAC;AAClE,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,OAAO,aAAa,IAAI,IAAI,KAAK,YAAY,CAAC;AAAA,QAC9C,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AACA,QAAI,SAAS,SAAS,GAAG;AACvB,UAAI,IAAI,IAAI;AACZ,UAAI,EAAE,KAAM,MAAK,IAAI,IAAI;AACzB,UAAI,EAAE,UAAW,WAAU,IAAI,IAAI;AAAA,IACrC;AAAA,EACF;AACA,SAAO,EAAE,KAAK,MAAM,UAAU;AAChC;AAiBO,SAAS,sBAAyC;AACvD,QAAM,EAAE,WAAW,IAAI,kBAAkB;AACzC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAuB,CAAC,CAAC;AACzD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAsB,oBAAI,IAAI,CAAC;AACjE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAsB,oBAAI,IAAI,CAAC;AAC3E,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,YAAU,MAAM;AACd,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,YAAY;AAChB,eAAW,IAAI;AACf,aAAS,IAAI;AACb,KAAC,YAAY;AACX,UAAI;AACF,cAAM,IAAI,MAAM,MAAM,GAAG,UAAU,uBAAuB;AAAA,UACxD,QAAQ,GAAG;AAAA,UACX,aAAa;AAAA,UACb,SAAS,YAAY;AAAA,QACvB,CAAC;AACD,YAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,QAAQ,EAAE,MAAM,EAAE;AAC7C,cAAM,OAAQ,MAAM,EAAE,KAAK;AAC3B,YAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACxE,YAAI,UAAW;AACf,cAAM,EAAE,KAAK,MAAM,UAAU,IAAI,cAAc,IAAI;AACnD,oBAAY,GAAG;AACf,qBAAa,IAAI;AACjB,0BAAkB,SAAS;AAAA,MAC7B,SAAS,GAAG;AACV,YAAI,aAAc,EAAwB,SAAS,aAAc;AACjE,iBAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,MACrD,UAAE;AACA,YAAI,CAAC,UAAW,YAAW,KAAK;AAAA,MAClC;AAAA,IACF,GAAG;AACH,WAAO,MAAM;AACX,kBAAY;AACZ,SAAG,MAAM;AAAA,IACX;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,QAAQ,OAAO,KAAK,QAAQ,EAAE,KAAK;AACzC,SAAO,EAAE,UAAU,OAAO,WAAW,gBAAgB,SAAS,MAAM;AACtE;;;AC1GA,SAAS,aAAa,eAAe;AAwBrC,eAAe,YAAY,GAAa;AACtC,QAAM,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC5C,MAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAO,MAA6B,SAAS,QAAQ,EAAE,MAAM,EAAE;AACpF,SAAO;AACT;AAEO,SAAS,gBAAgB;AAC9B,QAAM,EAAE,WAAW,IAAI,kBAAkB;AAEzC,QAAM,OAAO;AAAA,IACX,YACE;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,kCAAkC;AAAA,QACzD,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,OAAO;AAAA,IACX,OAAO,UACL;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,kCAAkC;AAAA,QACzD,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,YAAY,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,QAC3D,MAAM,KAAK,UAAU,KAAK;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,UAAU;AAAA,IACd,OAAO,cACL;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,uCAAuC;AAAA,QAC9D,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,YAAY,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,QAC3D,MAAM,KAAK,UAAU,EAAE,UAAU,CAAC;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,SAAS;AAAA,IACb,OAAO,cACL;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,kCAAkC,mBAAmB,SAAS,CAAC,IAAI;AAAA,QAC1F,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,SAAO,QAAQ,OAAO,EAAE,MAAM,MAAM,SAAS,OAAO,IAAI,CAAC,MAAM,MAAM,SAAS,MAAM,CAAC;AACvF;;;AClFA,SAAS,eAAAA,cAAa,WAAAC,gBAAe;AAM9B,IAAM,cAAc,CAAC,aAAa,UAAU,aAAa,WAAW;AAwE3E,eAAeC,aAAY,GAAa;AACtC,QAAM,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC5C,MAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAO,MAA6B,SAAS,QAAQ,EAAE,MAAM,EAAE;AACpF,SAAO;AACT;AAEO,SAAS,aAAa,QAAyB;AACpD,SAAQ,YAAkC,SAAS,MAAM;AAC3D;AAEO,SAAS,kBAAkB;AAChC,QAAM,EAAE,WAAW,IAAI,kBAAkB;AAEzC,QAAM,iBAAiBC;AAAA,IACrB,YACED;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,gBAAgB;AAAA,QACvC,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,eAAeC;AAAA,IACnB,OAAO,OACLD;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,gBAAgB,mBAAmB,EAAE,CAAC,IAAI;AAAA,QACjE,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,WAAWC;AAAA,IACf,YACED;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,sBAAsB;AAAA,QAC7C,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,cAAcC;AAAA,IAClB,YACED;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,yBAAyB;AAAA,QAChD,aAAa;AAAA,QACb,SAAS,YAAY;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,SAASC;AAAA,IACb,OAAO,OACLD;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,uBAAuB;AAAA,QAC9C,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,YAAY,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,QAC3D,MAAM,KAAK,UAAU,EAAE,GAAG,CAAC;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,UAAUC;AAAA,IACd,OAAO,OACLD;AAAA,MACE,MAAM,MAAM,GAAG,UAAU,wBAAwB;AAAA,QAC/C,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,SAAS,YAAY,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,QAC3D,MAAM,KAAK,UAAU,EAAE,GAAG,CAAC;AAAA,MAC7B,CAAC;AAAA,IACH;AAAA,IACF,CAAC,UAAU;AAAA,EACb;AAEA,SAAOE;AAAA,IACL,OAAO,EAAE,gBAAgB,cAAc,UAAU,aAAa,QAAQ,QAAQ;AAAA,IAC9E,CAAC,gBAAgB,cAAc,UAAU,aAAa,QAAQ,OAAO;AAAA,EACvE;AACF;",
|
|
6
|
+
"names": ["useCallback", "useMemo", "jsonOrThrow", "useCallback", "useMemo"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nbt-dev/components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Reusable React building blocks for NBT-console apps: the CodeMirror NBT editor (+LSP), the entity graph, and the live data table.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/core/index.ts
CHANGED
|
@@ -23,6 +23,17 @@ export type {
|
|
|
23
23
|
} from "./use-cartridge-info";
|
|
24
24
|
export { useGsheetsApi } from "./use-gsheets";
|
|
25
25
|
export type { GsheetSyncConfig, GsheetSaveInput } from "./use-gsheets";
|
|
26
|
+
export { useWorkflowsApi, isWfTerminal, WF_TERMINAL } from "./use-workflows";
|
|
27
|
+
export type {
|
|
28
|
+
WfStatus,
|
|
29
|
+
WfListItem,
|
|
30
|
+
WfExecution,
|
|
31
|
+
WfEvent,
|
|
32
|
+
WfDetail,
|
|
33
|
+
WfStats,
|
|
34
|
+
WfVersion,
|
|
35
|
+
WfExecReply,
|
|
36
|
+
} from "./use-workflows";
|
|
26
37
|
export {
|
|
27
38
|
getDevToolsToken,
|
|
28
39
|
setDevToolsToken,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Fetchers + hook for the workflow-engine introspection endpoints
|
|
2
|
+
// (/_console/wf*). The read routes are ungated like /_console/metrics; the two
|
|
3
|
+
// mutating ones (cancel/advance) are admin-gated server-side, so every request
|
|
4
|
+
// carries the devtools Bearer token (authHeaders) like the contracts probe.
|
|
5
|
+
|
|
6
|
+
import { useCallback, useMemo } from "react";
|
|
7
|
+
import { useDevToolsConfig } from "./config";
|
|
8
|
+
import { authHeaders } from "./auth";
|
|
9
|
+
|
|
10
|
+
// Terminal statuses a workflow can settle into — used to decide whether to keep
|
|
11
|
+
// polling an open instance and whether Cancel is offered.
|
|
12
|
+
export const WF_TERMINAL = ["COMPLETED", "FAILED", "CANCELLED", "CONTINUED"] as const;
|
|
13
|
+
|
|
14
|
+
export type WfStatus =
|
|
15
|
+
| "RUNNING"
|
|
16
|
+
| "SUSPENDED"
|
|
17
|
+
| "COMPLETED"
|
|
18
|
+
| "FAILED"
|
|
19
|
+
| "CANCELLED"
|
|
20
|
+
| "CONTINUED";
|
|
21
|
+
|
|
22
|
+
// Row from GET /_console/wf — the enriched list (createdAt/updatedAt are unix ms).
|
|
23
|
+
export type WfListItem = {
|
|
24
|
+
id: string;
|
|
25
|
+
status: WfStatus;
|
|
26
|
+
workflowName: string;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
updatedAt: number;
|
|
29
|
+
lane: string;
|
|
30
|
+
deployVersion: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// The execution record from GET /_console/wf/:id (raw entity field names).
|
|
34
|
+
export type WfExecution = {
|
|
35
|
+
id: string;
|
|
36
|
+
status: WfStatus;
|
|
37
|
+
workflowName: string;
|
|
38
|
+
args?: string;
|
|
39
|
+
result?: string;
|
|
40
|
+
error?: string;
|
|
41
|
+
cursor: number;
|
|
42
|
+
lane?: string;
|
|
43
|
+
deployVersion: number;
|
|
44
|
+
createdAt: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// One journal entry. kind ∈ HOST_CALL_INTENT | HOST_CALL_RESULT | SUSPENDED |
|
|
49
|
+
// RESUMED | RETRY | COMPLETED | FAILED | CANCELLED | CONTINUED.
|
|
50
|
+
export type WfEvent = {
|
|
51
|
+
id: string;
|
|
52
|
+
seq: number;
|
|
53
|
+
kind: string;
|
|
54
|
+
capability?: string;
|
|
55
|
+
target?: string;
|
|
56
|
+
op?: string;
|
|
57
|
+
payload?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type WfDetail = { execution: WfExecution; events: WfEvent[] };
|
|
61
|
+
|
|
62
|
+
export type WfStats = {
|
|
63
|
+
writes: number;
|
|
64
|
+
reads: number;
|
|
65
|
+
emails: number;
|
|
66
|
+
fetches: number;
|
|
67
|
+
emits: number;
|
|
68
|
+
retries: number;
|
|
69
|
+
async_inflight: number;
|
|
70
|
+
async_peak: number;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type WfVersion = { version: number; refcount: number; current: boolean };
|
|
74
|
+
|
|
75
|
+
// The exec-reply shape returned by run/signal/advance/cancel.
|
|
76
|
+
export type WfExecReply = {
|
|
77
|
+
id: string;
|
|
78
|
+
outcome: string;
|
|
79
|
+
status: string;
|
|
80
|
+
result?: string;
|
|
81
|
+
error?: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
async function jsonOrThrow(r: Response) {
|
|
85
|
+
const body = await r.json().catch(() => ({}));
|
|
86
|
+
if (!r.ok) throw new Error((body as { error?: string })?.error ?? `HTTP ${r.status}`);
|
|
87
|
+
return body;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function isWfTerminal(status: string): boolean {
|
|
91
|
+
return (WF_TERMINAL as readonly string[]).includes(status);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function useWorkflowsApi() {
|
|
95
|
+
const { apiBaseUrl } = useDevToolsConfig();
|
|
96
|
+
|
|
97
|
+
const listExecutions = useCallback(
|
|
98
|
+
async (): Promise<WfListItem[]> =>
|
|
99
|
+
jsonOrThrow(
|
|
100
|
+
await fetch(`${apiBaseUrl}/_console/wf`, {
|
|
101
|
+
credentials: "include",
|
|
102
|
+
headers: authHeaders(),
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
[apiBaseUrl],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const getExecution = useCallback(
|
|
109
|
+
async (id: string): Promise<WfDetail> =>
|
|
110
|
+
jsonOrThrow(
|
|
111
|
+
await fetch(`${apiBaseUrl}/_console/wf/${encodeURIComponent(id)}`, {
|
|
112
|
+
credentials: "include",
|
|
113
|
+
headers: authHeaders(),
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
[apiBaseUrl],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const getStats = useCallback(
|
|
120
|
+
async (): Promise<WfStats> =>
|
|
121
|
+
jsonOrThrow(
|
|
122
|
+
await fetch(`${apiBaseUrl}/_console/wf/stats`, {
|
|
123
|
+
credentials: "include",
|
|
124
|
+
headers: authHeaders(),
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
[apiBaseUrl],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const getVersions = useCallback(
|
|
131
|
+
async (): Promise<WfVersion[]> =>
|
|
132
|
+
jsonOrThrow(
|
|
133
|
+
await fetch(`${apiBaseUrl}/_console/wf/versions`, {
|
|
134
|
+
credentials: "include",
|
|
135
|
+
headers: authHeaders(),
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
[apiBaseUrl],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const cancel = useCallback(
|
|
142
|
+
async (id: string): Promise<WfExecReply> =>
|
|
143
|
+
jsonOrThrow(
|
|
144
|
+
await fetch(`${apiBaseUrl}/_console/wf/cancel`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
credentials: "include",
|
|
147
|
+
headers: authHeaders({ "content-type": "application/json" }),
|
|
148
|
+
body: JSON.stringify({ id }),
|
|
149
|
+
}),
|
|
150
|
+
),
|
|
151
|
+
[apiBaseUrl],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const advance = useCallback(
|
|
155
|
+
async (id: string): Promise<WfExecReply> =>
|
|
156
|
+
jsonOrThrow(
|
|
157
|
+
await fetch(`${apiBaseUrl}/_console/wf/advance`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
credentials: "include",
|
|
160
|
+
headers: authHeaders({ "content-type": "application/json" }),
|
|
161
|
+
body: JSON.stringify({ id }),
|
|
162
|
+
}),
|
|
163
|
+
),
|
|
164
|
+
[apiBaseUrl],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return useMemo(
|
|
168
|
+
() => ({ listExecutions, getExecution, getStats, getVersions, cancel, advance }),
|
|
169
|
+
[listExecutions, getExecution, getStats, getVersions, cancel, advance],
|
|
170
|
+
);
|
|
171
|
+
}
|