bunqueue-dashboard 0.0.34 → 0.0.36
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 +20 -13
- package/agent/backup/processExecutor.ts +90 -0
- package/agent/backup/resultValidation.ts +85 -0
- package/agent/backup/routes.ts +160 -0
- package/agent/backup/runner.ts +91 -0
- package/agent/backup/standaloneWorker.ts +23 -0
- package/agent/backup/workerExecutor.ts +40 -0
- package/agent/backup/workerFactory.ts +21 -0
- package/agent/backup/workerProtocol.ts +10 -0
- package/agent/db/core.ts +141 -0
- package/agent/db/csvExport.ts +172 -0
- package/agent/db/exportTimeout.ts +190 -0
- package/agent/db/query.ts +82 -0
- package/agent/db/queryTimeout.ts +76 -0
- package/agent/db/schema.ts +79 -0
- package/agent/db/tables.ts +157 -0
- package/agent/db/types.ts +118 -0
- package/agent/db/workerFactory.ts +25 -0
- package/agent/db.ts +43 -1099
- package/agent/errorMessage.ts +9 -0
- package/agent/flow/routes.ts +250 -0
- package/agent/flow/service.ts +266 -0
- package/agent/flow/types.ts +34 -0
- package/agent/flow/validation.ts +230 -0
- package/agent/index.ts +5 -17
- package/agent/logger.ts +121 -17
- package/agent/managedTarget.ts +43 -0
- package/agent/manager/config.ts +84 -0
- package/agent/manager/logs.ts +83 -0
- package/agent/manager/process.ts +233 -0
- package/agent/manager/storage.ts +28 -0
- package/agent/manager/types.ts +38 -0
- package/agent/manager.ts +10 -490
- package/agent/queue/routes.ts +128 -0
- package/agent/queue/runtime.ts +119 -0
- package/agent/queue/types.ts +34 -0
- package/agent/queue/validation.ts +100 -0
- package/agent/server/controlRoutes.ts +153 -0
- package/agent/server/databaseRoutes.ts +165 -0
- package/agent/server/errors.ts +48 -0
- package/agent/server/handler.ts +146 -0
- package/agent/server/jsonBody.ts +109 -0
- package/agent/server/lifecycle.ts +72 -0
- package/agent/server/managedRuntime.ts +41 -0
- package/agent/server/policy.ts +60 -0
- package/agent/server/router.ts +150 -0
- package/agent/server/types.ts +39 -0
- package/agent/server/workflowReadRoutes.ts +80 -0
- package/agent/server.ts +16 -369
- package/agent/shutdown.ts +156 -0
- package/agent/workflow/codec.ts +78 -0
- package/agent/workflow/detail.ts +63 -0
- package/agent/workflow/list.ts +87 -0
- package/agent/workflow/runtime.ts +274 -0
- package/agent/workflow/runtimeConfig.ts +103 -0
- package/agent/workflow/runtimeRoutes.ts +162 -0
- package/agent/workflow/sqlite.ts +52 -0
- package/agent/workflow/stats.ts +79 -0
- package/agent/workflow/types.ts +81 -0
- package/agent/workflow/validation.ts +114 -0
- package/agent/workflows.ts +16 -0
- package/dist/.vite/manifest.json +619 -246
- package/dist/assets/AddJob-UUa2fSXE.js +3 -0
- package/dist/assets/Alerts-7oGYfQMb.js +1 -0
- package/dist/assets/{AreaChart-REK4ZsTC.js → AreaChart-w0ruozXH.js} +1 -1
- package/dist/assets/Benchmark-C2NFp_kj.js +1 -0
- package/dist/assets/BulkAddJobs-CL4yBae-.js +5 -0
- package/dist/assets/Button-BPqpRZJD.js +1 -0
- package/dist/assets/Card-C7NMhqUt.js +1 -0
- package/dist/assets/{CopilotPanel-CIXpcwkH.js → CopilotPanel-BQ2Cgo9p.js} +3 -3
- package/dist/assets/Cron-DfWBhoBf.js +1 -0
- package/dist/assets/CronManager-DAK7RSOX.js +1 -0
- package/dist/assets/Database-DqL7giEj.js +3 -0
- package/dist/assets/Diagnostics-D98YwN-6.js +1 -0
- package/dist/assets/Dlq-BHmA5PTI.js +1 -0
- package/dist/assets/DlqControl-DihxvP-i.js +1 -0
- package/dist/assets/DlqPro-ncgnDtpE.js +1 -0
- package/dist/assets/Flows-B5N5Cil0.js +2 -0
- package/dist/assets/JobInspector-B40nFiGh.js +2 -0
- package/dist/assets/Jobs-Bla3IEef.js +1 -0
- package/dist/assets/JobsPro-CnczVx7x.js +1 -0
- package/dist/assets/Logs-T91Q3cH0.js +1 -0
- package/dist/assets/LogsPro-CMn6-Wwv.js +1 -0
- package/dist/assets/{McpServer-D5rwBg74.js → McpServer-BQBabwW-.js} +2 -2
- package/dist/assets/Metrics-DxT_RI2B.js +1 -0
- package/dist/assets/MetricsPro-Cjen-nAP.js +1 -0
- package/dist/assets/Overview-XN2ej8ph.js +1 -0
- package/dist/assets/PageHeader-BXlZSBcc.js +1 -0
- package/dist/assets/Pagination-Q-vf4hsX.js +1 -0
- package/dist/assets/QueueActions-C-Aferd6.js +1 -0
- package/dist/assets/QueueControl-B8Q9NX6t.js +1 -0
- package/dist/assets/QueueDetail-BMhG0jUB.js +1 -0
- package/dist/assets/QueueDetailPro-BKTTyLR4.js +1 -0
- package/dist/assets/Queues-COqPMoY9.js +1 -0
- package/dist/assets/QueuesOverview-pWdrPy8Z.js +1 -0
- package/dist/assets/S3Backup-BmsZ8xtq.js +1 -0
- package/dist/assets/S3BackupPro-AtlsPUJL.js +3 -0
- package/dist/assets/ServerControl-D3XwHr72.js +3 -0
- package/dist/assets/Settings-BofKZoHn.js +1 -0
- package/dist/assets/StatusBadge-B0I7hNBX.js +1 -0
- package/dist/assets/Usage-DAR9E22v.js +1 -0
- package/dist/assets/UsagePro-Bc30N2Lo.js +1 -0
- package/dist/assets/Webhooks-Bg2k1PU4.js +1 -0
- package/dist/assets/Workers-BqUZMJVQ.js +1 -0
- package/dist/assets/WorkersPro-C9aSadUh.js +1 -0
- package/dist/assets/Workflows-BEFMfHmo.js +1 -0
- package/dist/assets/api-CQ5Z7xR7.js +1 -0
- package/dist/assets/bq-BeBxa743.js +1 -0
- package/dist/assets/cn-zFEt5SO8.js +1 -0
- package/dist/assets/connectionStore-DbBYqHEY.js +1 -0
- package/dist/assets/controlConnectionEpoch-CFSO6237.js +1 -0
- package/dist/assets/feedback-BnGE2oW2.js +1 -0
- package/dist/assets/{flowMutationSafety-DVD1IMr-.js → flowMutationSafety-CoOp94wS.js} +1 -1
- package/dist/assets/{form-DsUq0w8s.js → form-tC20r4Ma.js} +1 -1
- package/dist/assets/format-Cg7VCIZW.js +1 -0
- package/dist/assets/icons-CIvFfU2b.js +1 -0
- package/dist/assets/index-Cui9IwWy.js +8 -0
- package/dist/assets/index-DOze4TKg.css +1 -0
- package/dist/assets/install-Cd9Is10G.js +3 -0
- package/dist/assets/{react-vendor-Bjui4wS4.js → react-vendor-CyjgmGTq.js} +3 -3
- package/dist/assets/responses-kPO4rSu5.js +1 -0
- package/dist/assets/toastStore-CS_9Xndn.js +1 -0
- package/dist/assets/{upstreamPaths-nG8N1XAz.js → upstreamPaths-D_LPsv7b.js} +1 -1
- package/dist/assets/usePolledData-DzlUpBOO.js +1 -0
- package/dist/assets/{useServerActionGuard-CItj3hvh.js → useServerActionGuard-BlH-Kso1.js} +1 -1
- package/dist/assets/useThroughputSeries-C2pa1Wd_.js +1 -0
- package/dist/index.html +17 -6
- package/package.json +15 -3
- package/scripts/embedded.gen.ts +152 -124
- package/scripts/serve.ts +96 -484
- package/scripts/serveHandler.ts +217 -0
- package/scripts/servePolicy.ts +133 -0
- package/dist/assets/AddJob-Cih9F84t.js +0 -3
- package/dist/assets/Alerts-Dp4Vclez.js +0 -1
- package/dist/assets/Benchmark-X_gdZun5.js +0 -1
- package/dist/assets/BulkAddJobs-CXTpYMZf.js +0 -5
- package/dist/assets/Cron-DUHkse77.js +0 -1
- package/dist/assets/CronManager-BeYrLK2E.js +0 -1
- package/dist/assets/Database-CXPb3XGr.js +0 -3
- package/dist/assets/Diagnostics-DDtoiDYT.js +0 -1
- package/dist/assets/Dlq-CX_uRblW.js +0 -1
- package/dist/assets/DlqControl-CazFmSEd.js +0 -1
- package/dist/assets/DlqPro-HSffdtLJ.js +0 -1
- package/dist/assets/Flows-CGmyghKF.js +0 -1
- package/dist/assets/JobInspector-B9i1HnG5.js +0 -2
- package/dist/assets/Jobs-BZGtMnJI.js +0 -1
- package/dist/assets/JobsPro-tmEN8f-P.js +0 -1
- package/dist/assets/Logs-DxUD9V2A.js +0 -1
- package/dist/assets/LogsPro-BTQhvPKj.js +0 -1
- package/dist/assets/Metrics-CSPpBf1m.js +0 -1
- package/dist/assets/MetricsPro-DSn-3qNT.js +0 -1
- package/dist/assets/Overview-StvHYRZ5.js +0 -1
- package/dist/assets/Pagination-eotgGQf9.js +0 -1
- package/dist/assets/QueueActions-CibRLzrz.js +0 -1
- package/dist/assets/QueueControl-DwYNcMfI.js +0 -1
- package/dist/assets/QueueDetail-NMfKwj6E.js +0 -1
- package/dist/assets/QueueDetailPro-C8SBjX09.js +0 -1
- package/dist/assets/Queues-BeTAi8V8.js +0 -1
- package/dist/assets/QueuesOverview-BfQO8NS4.js +0 -1
- package/dist/assets/S3Backup-BYkAtONl.js +0 -1
- package/dist/assets/S3BackupPro-BP1Ch54R.js +0 -2
- package/dist/assets/ServerControl-C85jYgyN.js +0 -3
- package/dist/assets/Settings-DjP28c_5.js +0 -1
- package/dist/assets/Usage-DECeEsO2.js +0 -1
- package/dist/assets/UsagePro-CD7dgt3d.js +0 -1
- package/dist/assets/Webhooks-Ct-x8DD1.js +0 -1
- package/dist/assets/Workers-lP1BXHFq.js +0 -1
- package/dist/assets/WorkersPro-CAUqy3Cp.js +0 -1
- package/dist/assets/cn-CMFYpumL.js +0 -1
- package/dist/assets/connectionStore-Cd9-3Fw0.js +0 -1
- package/dist/assets/index-BWoUj86M.css +0 -1
- package/dist/assets/index-Dr__4DsT.js +0 -8
- package/dist/assets/install-DPCcfOYo.js +0 -3
- package/dist/assets/useThroughputSeries-Bpyg6Jaq.js +0 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { openWorkflowDatabase, workflowSchema } from './sqlite';
|
|
2
|
+
import {
|
|
3
|
+
WORKFLOW_LIMITS,
|
|
4
|
+
WORKFLOW_STATES,
|
|
5
|
+
type WorkflowExecutionState,
|
|
6
|
+
type WorkflowStats,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
export function workflowStats(
|
|
10
|
+
path: string,
|
|
11
|
+
/** @internal Deterministic seam for the SQLite snapshot regression test. */
|
|
12
|
+
afterActiveTotals?: () => void
|
|
13
|
+
): WorkflowStats {
|
|
14
|
+
const db = openWorkflowDatabase(path);
|
|
15
|
+
try {
|
|
16
|
+
const active = workflowSchema(db, 'active');
|
|
17
|
+
const archive = workflowSchema(db, 'archive');
|
|
18
|
+
const states = Object.fromEntries(WORKFLOW_STATES.map((state) => [state, 0])) as Record<
|
|
19
|
+
WorkflowExecutionState,
|
|
20
|
+
number
|
|
21
|
+
>;
|
|
22
|
+
if (!active) {
|
|
23
|
+
return { available: false, activeTotal: 0, archiveTotal: 0, states, workflowNames: [] };
|
|
24
|
+
}
|
|
25
|
+
const snapshot = db.transaction((): WorkflowStats => {
|
|
26
|
+
const stateColumns = WORKFLOW_STATES.map(
|
|
27
|
+
(state, index) => `SUM(CASE WHEN state = '${state}' THEN 1 ELSE 0 END) AS state_${index}`
|
|
28
|
+
).join(', ');
|
|
29
|
+
const totals = db
|
|
30
|
+
.query(`SELECT COUNT(*) AS total, ${stateColumns} FROM workflow_executions`)
|
|
31
|
+
.get() as { total: number } & Record<`state_${number}`, number | null>;
|
|
32
|
+
for (const [index, state] of WORKFLOW_STATES.entries()) {
|
|
33
|
+
states[state] = totals[`state_${index}`] ?? 0;
|
|
34
|
+
}
|
|
35
|
+
if (Object.values(states).reduce((sum, count) => sum + count, 0) !== totals.total) {
|
|
36
|
+
throw new Error('Workflow store contains an unknown execution state');
|
|
37
|
+
}
|
|
38
|
+
afterActiveTotals?.();
|
|
39
|
+
const names = workflowNames(db, Boolean(archive));
|
|
40
|
+
const archiveTotal = archive
|
|
41
|
+
? ((db.query('SELECT COUNT(*) AS count FROM workflow_executions_archive').get() as { count: number }).count ?? 0)
|
|
42
|
+
: 0;
|
|
43
|
+
return {
|
|
44
|
+
available: true,
|
|
45
|
+
activeTotal: totals.total,
|
|
46
|
+
archiveTotal,
|
|
47
|
+
states,
|
|
48
|
+
workflowNames: names,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
return snapshot.deferred();
|
|
52
|
+
} finally {
|
|
53
|
+
db.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function workflowNames(db: import('bun:sqlite').Database, hasArchive: boolean): string[] {
|
|
58
|
+
const rows = db
|
|
59
|
+
.query(
|
|
60
|
+
hasArchive
|
|
61
|
+
? `SELECT workflow_name FROM (
|
|
62
|
+
SELECT workflow_name FROM workflow_executions
|
|
63
|
+
UNION
|
|
64
|
+
SELECT workflow_name FROM workflow_executions_archive
|
|
65
|
+
) ORDER BY workflow_name LIMIT 500`
|
|
66
|
+
: 'SELECT DISTINCT workflow_name FROM workflow_executions ORDER BY workflow_name LIMIT 500'
|
|
67
|
+
)
|
|
68
|
+
.all() as { workflow_name: unknown }[];
|
|
69
|
+
return rows.map((row) => {
|
|
70
|
+
if (
|
|
71
|
+
typeof row.workflow_name !== 'string' ||
|
|
72
|
+
row.workflow_name.length < 1 ||
|
|
73
|
+
row.workflow_name.length > WORKFLOW_LIMITS.filter
|
|
74
|
+
) {
|
|
75
|
+
throw new Error('Workflow store contains an invalid workflow name');
|
|
76
|
+
}
|
|
77
|
+
return row.workflow_name;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const WORKFLOW_STATES = [
|
|
2
|
+
'running',
|
|
3
|
+
'waiting',
|
|
4
|
+
'completed',
|
|
5
|
+
'failed',
|
|
6
|
+
'compensating',
|
|
7
|
+
'compensation-stuck',
|
|
8
|
+
] as const;
|
|
9
|
+
|
|
10
|
+
export type WorkflowExecutionState = (typeof WORKFLOW_STATES)[number];
|
|
11
|
+
export type WorkflowStateFilter = WorkflowExecutionState | 'compensation';
|
|
12
|
+
export type WorkflowStoreKind = 'active' | 'archive';
|
|
13
|
+
|
|
14
|
+
export const WORKFLOW_LIMITS = {
|
|
15
|
+
page: 100,
|
|
16
|
+
offset: 1_000_000,
|
|
17
|
+
filter: 256,
|
|
18
|
+
detailBlob: 4 * 1024 * 1024,
|
|
19
|
+
metaBlob: 256 * 1024,
|
|
20
|
+
jsonNodes: 25_000,
|
|
21
|
+
jsonDepth: 40,
|
|
22
|
+
string: 250_000,
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export interface WorkflowRow {
|
|
26
|
+
id: string;
|
|
27
|
+
workflow_name: string;
|
|
28
|
+
state: string;
|
|
29
|
+
current_node_index: number;
|
|
30
|
+
created_at: number;
|
|
31
|
+
updated_at: number;
|
|
32
|
+
archived_at?: number | null;
|
|
33
|
+
meta?: Uint8Array | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface WorkflowExecutionSummary {
|
|
37
|
+
id: string;
|
|
38
|
+
workflowName: string;
|
|
39
|
+
state: WorkflowExecutionState;
|
|
40
|
+
currentNodeIndex: number;
|
|
41
|
+
createdAt: number;
|
|
42
|
+
updatedAt: number;
|
|
43
|
+
archivedAt?: number;
|
|
44
|
+
rollbackStatus?: string;
|
|
45
|
+
failureReason?: string;
|
|
46
|
+
parentExecutionId?: string;
|
|
47
|
+
definitionHash?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface WorkflowExecutionDetail extends WorkflowExecutionSummary {
|
|
51
|
+
input: unknown;
|
|
52
|
+
steps: Record<string, unknown>;
|
|
53
|
+
resolvedSteps?: string[];
|
|
54
|
+
signals: Record<string, unknown>;
|
|
55
|
+
decisions?: Record<string, unknown>;
|
|
56
|
+
committedAt?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WorkflowStats {
|
|
60
|
+
available: boolean;
|
|
61
|
+
activeTotal: number;
|
|
62
|
+
archiveTotal: number;
|
|
63
|
+
states: Record<WorkflowExecutionState, number>;
|
|
64
|
+
workflowNames: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface WorkflowListOptions {
|
|
68
|
+
kind?: WorkflowStoreKind;
|
|
69
|
+
workflowName?: string;
|
|
70
|
+
state?: WorkflowStateFilter;
|
|
71
|
+
limit?: number;
|
|
72
|
+
offset?: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface WorkflowPage {
|
|
76
|
+
available: boolean;
|
|
77
|
+
executions: WorkflowExecutionSummary[];
|
|
78
|
+
total: number;
|
|
79
|
+
limit: number;
|
|
80
|
+
offset: number;
|
|
81
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { decodeWorkflowObject } from './codec';
|
|
2
|
+
import {
|
|
3
|
+
WORKFLOW_LIMITS,
|
|
4
|
+
WORKFLOW_STATES,
|
|
5
|
+
type WorkflowExecutionState,
|
|
6
|
+
type WorkflowExecutionSummary,
|
|
7
|
+
type WorkflowRow,
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
const STEP_STATES = new Set(['pending', 'running', 'completed', 'failed']);
|
|
11
|
+
const COMPENSATION_STATES = new Set([
|
|
12
|
+
'compensated',
|
|
13
|
+
'compensation-failed',
|
|
14
|
+
'compensation-skipped',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export function validWorkflowState(value: string): value is WorkflowExecutionState {
|
|
18
|
+
return (WORKFLOW_STATES as readonly string[]).includes(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function assertWorkflowRow(row: WorkflowRow): WorkflowExecutionSummary {
|
|
22
|
+
if (
|
|
23
|
+
typeof row.id !== 'string' ||
|
|
24
|
+
typeof row.workflow_name !== 'string' ||
|
|
25
|
+
row.workflow_name.length < 1 ||
|
|
26
|
+
row.workflow_name.length > WORKFLOW_LIMITS.filter ||
|
|
27
|
+
!validWorkflowState(row.state) ||
|
|
28
|
+
!Number.isSafeInteger(row.current_node_index) ||
|
|
29
|
+
!Number.isFinite(row.created_at) ||
|
|
30
|
+
!Number.isFinite(row.updated_at)
|
|
31
|
+
) {
|
|
32
|
+
throw new Error('Workflow store contains an invalid execution row');
|
|
33
|
+
}
|
|
34
|
+
const meta = decodeWorkflowObject(row.meta, WORKFLOW_LIMITS.metaBlob, 'meta');
|
|
35
|
+
return {
|
|
36
|
+
id: row.id,
|
|
37
|
+
workflowName: row.workflow_name,
|
|
38
|
+
state: row.state,
|
|
39
|
+
currentNodeIndex: row.current_node_index,
|
|
40
|
+
createdAt: row.created_at,
|
|
41
|
+
updatedAt: row.updated_at,
|
|
42
|
+
...(typeof row.archived_at === 'number' ? { archivedAt: row.archived_at } : {}),
|
|
43
|
+
...(typeof meta.rollbackStatus === 'string' ? { rollbackStatus: meta.rollbackStatus } : {}),
|
|
44
|
+
...(typeof meta.failureReason === 'string' ? { failureReason: meta.failureReason } : {}),
|
|
45
|
+
...(typeof meta.parentExecutionId === 'string' ? { parentExecutionId: meta.parentExecutionId } : {}),
|
|
46
|
+
...(typeof meta.definitionHash === 'string' ? { definitionHash: meta.definitionHash } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function assertWorkflowSteps(value: unknown): Record<string, unknown> {
|
|
51
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
52
|
+
throw new Error('Workflow steps must decode to an object');
|
|
53
|
+
}
|
|
54
|
+
for (const [name, candidate] of Object.entries(value)) {
|
|
55
|
+
if (!name || candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
56
|
+
throw new Error('Workflow store contains an invalid step record');
|
|
57
|
+
}
|
|
58
|
+
assertStep(candidate as Record<string, unknown>);
|
|
59
|
+
}
|
|
60
|
+
return value as Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function assertStep(step: Record<string, unknown>): void {
|
|
64
|
+
if (
|
|
65
|
+
typeof step.status !== 'string' ||
|
|
66
|
+
!STEP_STATES.has(step.status) ||
|
|
67
|
+
invalidOptionalType(step, 'error', 'string') ||
|
|
68
|
+
invalidOptionalType(step, 'compensatable', 'boolean') ||
|
|
69
|
+
invalidOptionalType(step, 'idempotencyKey', 'string') ||
|
|
70
|
+
invalidOptionalType(step, 'childExecutionId', 'string') ||
|
|
71
|
+
!optionalFinite(step, 'startedAt') ||
|
|
72
|
+
!optionalFinite(step, 'completedAt') ||
|
|
73
|
+
!optionalInteger(step, 'attempts') ||
|
|
74
|
+
!optionalInteger(step, 'loopIndex') ||
|
|
75
|
+
!optionalInteger(step, 'occurrence')
|
|
76
|
+
) {
|
|
77
|
+
throw new Error('Workflow store contains an invalid step record');
|
|
78
|
+
}
|
|
79
|
+
if (Object.hasOwn(step, 'compensation')) assertCompensation(step.compensation);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function assertCompensation(candidate: unknown): void {
|
|
83
|
+
if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
84
|
+
throw new Error('Workflow store contains an invalid compensation record');
|
|
85
|
+
}
|
|
86
|
+
const compensation = candidate as Record<string, unknown>;
|
|
87
|
+
if (
|
|
88
|
+
typeof compensation.status !== 'string' ||
|
|
89
|
+
!COMPENSATION_STATES.has(compensation.status) ||
|
|
90
|
+
typeof compensation.at !== 'number' ||
|
|
91
|
+
!Number.isFinite(compensation.at) ||
|
|
92
|
+
invalidOptionalType(compensation, 'error', 'string')
|
|
93
|
+
) {
|
|
94
|
+
throw new Error('Workflow store contains an invalid compensation record');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function invalidOptionalType(
|
|
99
|
+
record: Record<string, unknown>,
|
|
100
|
+
key: string,
|
|
101
|
+
type: 'string' | 'boolean'
|
|
102
|
+
): boolean {
|
|
103
|
+
return Object.hasOwn(record, key) && typeof record[key] !== type;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function optionalFinite(record: Record<string, unknown>, key: string): boolean {
|
|
107
|
+
return !Object.hasOwn(record, key) ||
|
|
108
|
+
(typeof record[key] === 'number' && Number.isFinite(record[key]));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function optionalInteger(record: Record<string, unknown>, key: string): boolean {
|
|
112
|
+
return !Object.hasOwn(record, key) ||
|
|
113
|
+
(typeof record[key] === 'number' && Number.isSafeInteger(record[key]) && record[key] >= 0);
|
|
114
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only facade for Bunqueue 2.8.57 Workflow Engine observability.
|
|
3
|
+
* Persistence, decoding and validation live behind this stable public module.
|
|
4
|
+
*/
|
|
5
|
+
export { workflowExecution } from './workflow/detail';
|
|
6
|
+
export { workflowExecutions } from './workflow/list';
|
|
7
|
+
export { workflowStats } from './workflow/stats';
|
|
8
|
+
export {
|
|
9
|
+
WORKFLOW_STATES,
|
|
10
|
+
type WorkflowExecutionDetail,
|
|
11
|
+
type WorkflowExecutionState,
|
|
12
|
+
type WorkflowExecutionSummary,
|
|
13
|
+
type WorkflowStateFilter,
|
|
14
|
+
type WorkflowStats,
|
|
15
|
+
type WorkflowStoreKind,
|
|
16
|
+
} from './workflow/types';
|