@contractspec/example.workflow-system 1.57.0 → 1.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approval/approval.enum.d.ts +2 -7
- package/dist/approval/approval.enum.d.ts.map +1 -1
- package/dist/approval/approval.enum.js +20 -26
- package/dist/approval/approval.event.d.ts +108 -114
- package/dist/approval/approval.event.d.ts.map +1 -1
- package/dist/approval/approval.event.js +103 -210
- package/dist/approval/approval.handler.d.ts +17 -18
- package/dist/approval/approval.handler.d.ts.map +1 -1
- package/dist/approval/approval.operations.d.ts +429 -435
- package/dist/approval/approval.operations.d.ts.map +1 -1
- package/dist/approval/approval.operations.js +364 -339
- package/dist/approval/approval.schema.d.ts +86 -91
- package/dist/approval/approval.schema.d.ts.map +1 -1
- package/dist/approval/approval.schema.js +71 -107
- package/dist/approval/index.d.ts +8 -5
- package/dist/approval/index.d.ts.map +1 -0
- package/dist/approval/index.js +484 -5
- package/dist/browser/approval/approval.enum.js +22 -0
- package/dist/browser/approval/approval.event.js +112 -0
- package/dist/browser/approval/approval.operations.js +369 -0
- package/dist/browser/approval/approval.schema.js +73 -0
- package/dist/browser/approval/index.js +484 -0
- package/dist/browser/docs/index.js +103 -0
- package/dist/browser/docs/workflow-system.docblock.js +103 -0
- package/dist/browser/entities/approval.js +119 -0
- package/dist/browser/entities/index.js +508 -0
- package/dist/browser/entities/instance.js +161 -0
- package/dist/browser/entities/step.js +124 -0
- package/dist/browser/entities/workflow.js +82 -0
- package/dist/browser/example.js +42 -0
- package/dist/browser/handlers/index.js +253 -0
- package/dist/browser/handlers/workflow.handlers.js +253 -0
- package/dist/browser/index.js +3120 -0
- package/dist/browser/instance/index.js +677 -0
- package/dist/browser/instance/instance.enum.js +15 -0
- package/dist/browser/instance/instance.event.js +164 -0
- package/dist/browser/instance/instance.handler.js +356 -0
- package/dist/browser/instance/instance.operations.js +9 -0
- package/dist/browser/instance/instance.schema.js +101 -0
- package/dist/browser/presentations/index.js +109 -0
- package/dist/browser/seeders/index.js +3 -0
- package/dist/browser/shared/index.js +3 -0
- package/dist/browser/shared/mock-data.js +11 -0
- package/dist/browser/shared/types.js +0 -0
- package/dist/browser/state-machine/index.js +6 -0
- package/dist/browser/tests/operations.test-spec.js +6 -0
- package/dist/browser/ui/WorkflowDashboard.js +3 -0
- package/dist/browser/ui/hooks/index.js +50 -0
- package/dist/browser/ui/hooks/useWorkflowList.js +50 -0
- package/dist/browser/ui/index.js +54 -0
- package/dist/browser/ui/renderers/index.js +227 -0
- package/dist/browser/ui/renderers/workflow.markdown.js +227 -0
- package/dist/browser/workflow/index.js +21 -0
- package/dist/browser/workflow/workflow.enum.js +36 -0
- package/dist/browser/workflow/workflow.event.js +6 -0
- package/dist/browser/workflow/workflow.handler.js +5 -0
- package/dist/browser/workflow/workflow.operations.js +8 -0
- package/dist/browser/workflow/workflow.schema.js +151 -0
- package/dist/browser/workflow-system.capability.js +5 -0
- package/dist/browser/workflow-system.feature.js +3 -0
- package/dist/docs/index.d.ts +2 -1
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +104 -1
- package/dist/docs/workflow-system.docblock.d.ts +2 -1
- package/dist/docs/workflow-system.docblock.d.ts.map +1 -0
- package/dist/docs/workflow-system.docblock.js +45 -56
- package/dist/entities/approval.d.ts +35 -40
- package/dist/entities/approval.d.ts.map +1 -1
- package/dist/entities/approval.js +116 -124
- package/dist/entities/index.d.ts +132 -137
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +506 -29
- package/dist/entities/instance.d.ts +46 -51
- package/dist/entities/instance.d.ts.map +1 -1
- package/dist/entities/instance.js +158 -164
- package/dist/entities/step.d.ts +31 -36
- package/dist/entities/step.d.ts.map +1 -1
- package/dist/entities/step.js +122 -132
- package/dist/entities/workflow.d.ts +22 -27
- package/dist/entities/workflow.d.ts.map +1 -1
- package/dist/entities/workflow.js +80 -99
- package/dist/example.d.ts +2 -6
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +41 -55
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +254 -3
- package/dist/handlers/workflow.handlers.d.ts +107 -106
- package/dist/handlers/workflow.handlers.d.ts.map +1 -1
- package/dist/handlers/workflow.handlers.js +237 -246
- package/dist/index.d.ts +15 -26
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3121 -26
- package/dist/instance/index.d.ts +8 -5
- package/dist/instance/index.d.ts.map +1 -0
- package/dist/instance/index.js +677 -5
- package/dist/instance/instance.enum.d.ts +1 -6
- package/dist/instance/instance.enum.d.ts.map +1 -1
- package/dist/instance/instance.enum.js +14 -18
- package/dist/instance/instance.event.d.ts +313 -319
- package/dist/instance/instance.event.d.ts.map +1 -1
- package/dist/instance/instance.event.js +151 -279
- package/dist/instance/instance.handler.d.ts +21 -22
- package/dist/instance/instance.handler.d.ts.map +1 -1
- package/dist/instance/instance.handler.js +352 -89
- package/dist/instance/instance.operations.d.ts +819 -825
- package/dist/instance/instance.operations.d.ts.map +1 -1
- package/dist/instance/instance.operations.js +10 -464
- package/dist/instance/instance.schema.d.ts +196 -201
- package/dist/instance/instance.schema.d.ts.map +1 -1
- package/dist/instance/instance.schema.js +97 -167
- package/dist/presentations/index.d.ts +23 -28
- package/dist/presentations/index.d.ts.map +1 -1
- package/dist/presentations/index.js +104 -334
- package/dist/seeders/index.d.ts +4 -8
- package/dist/seeders/index.d.ts.map +1 -1
- package/dist/seeders/index.js +4 -19
- package/dist/shared/index.d.ts +6 -3
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -3
- package/dist/shared/mock-data.d.ts +16 -16
- package/dist/shared/mock-data.d.ts.map +1 -1
- package/dist/shared/mock-data.js +11 -11
- package/dist/shared/types.d.ts +69 -72
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +1 -0
- package/dist/state-machine/index.d.ts +92 -95
- package/dist/state-machine/index.d.ts.map +1 -1
- package/dist/state-machine/index.js +6 -157
- package/dist/tests/operations.test-spec.d.ts +4 -9
- package/dist/tests/operations.test-spec.d.ts.map +1 -1
- package/dist/tests/operations.test-spec.js +7 -123
- package/dist/ui/WorkflowDashboard.d.ts +1 -6
- package/dist/ui/WorkflowDashboard.d.ts.map +1 -1
- package/dist/ui/WorkflowDashboard.js +3 -222
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.d.ts.map +1 -0
- package/dist/ui/hooks/index.js +51 -5
- package/dist/ui/hooks/useWorkflowList.d.ts +15 -19
- package/dist/ui/hooks/useWorkflowList.d.ts.map +1 -1
- package/dist/ui/hooks/useWorkflowList.js +47 -51
- package/dist/ui/index.d.ts +7 -6
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +55 -6
- package/dist/ui/renderers/index.d.ts +2 -2
- package/dist/ui/renderers/index.d.ts.map +1 -0
- package/dist/ui/renderers/index.js +227 -2
- package/dist/ui/renderers/workflow.markdown.d.ts +13 -14
- package/dist/ui/renderers/workflow.markdown.d.ts.map +1 -1
- package/dist/ui/renderers/workflow.markdown.js +223 -229
- package/dist/workflow/index.d.ts +8 -5
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +22 -6
- package/dist/workflow/workflow.enum.d.ts +4 -9
- package/dist/workflow/workflow.enum.d.ts.map +1 -1
- package/dist/workflow/workflow.enum.js +32 -42
- package/dist/workflow/workflow.event.d.ts +112 -118
- package/dist/workflow/workflow.event.d.ts.map +1 -1
- package/dist/workflow/workflow.event.js +7 -150
- package/dist/workflow/workflow.handler.d.ts +23 -24
- package/dist/workflow/workflow.handler.d.ts.map +1 -1
- package/dist/workflow/workflow.handler.js +6 -66
- package/dist/workflow/workflow.operations.d.ts +847 -853
- package/dist/workflow/workflow.operations.d.ts.map +1 -1
- package/dist/workflow/workflow.operations.js +9 -345
- package/dist/workflow/workflow.schema.d.ts +229 -234
- package/dist/workflow/workflow.schema.d.ts.map +1 -1
- package/dist/workflow/workflow.schema.js +146 -243
- package/dist/workflow-system.capability.d.ts +3 -8
- package/dist/workflow-system.capability.d.ts.map +1 -1
- package/dist/workflow-system.capability.js +6 -34
- package/dist/workflow-system.feature.d.ts +1 -6
- package/dist/workflow-system.feature.d.ts.map +1 -1
- package/dist/workflow-system.feature.js +4 -346
- package/package.json +415 -93
- package/dist/approval/approval.enum.js.map +0 -1
- package/dist/approval/approval.event.js.map +0 -1
- package/dist/approval/approval.handler.js +0 -72
- package/dist/approval/approval.handler.js.map +0 -1
- package/dist/approval/approval.operations.js.map +0 -1
- package/dist/approval/approval.schema.js.map +0 -1
- package/dist/docs/workflow-system.docblock.js.map +0 -1
- package/dist/entities/approval.js.map +0 -1
- package/dist/entities/index.js.map +0 -1
- package/dist/entities/instance.js.map +0 -1
- package/dist/entities/step.js.map +0 -1
- package/dist/entities/workflow.js.map +0 -1
- package/dist/example.js.map +0 -1
- package/dist/handlers/workflow.handlers.js.map +0 -1
- package/dist/instance/instance.enum.js.map +0 -1
- package/dist/instance/instance.event.js.map +0 -1
- package/dist/instance/instance.handler.js.map +0 -1
- package/dist/instance/instance.operations.js.map +0 -1
- package/dist/instance/instance.schema.js.map +0 -1
- package/dist/presentations/index.js.map +0 -1
- package/dist/seeders/index.js.map +0 -1
- package/dist/shared/mock-data.js.map +0 -1
- package/dist/state-machine/index.js.map +0 -1
- package/dist/tests/operations.test-spec.js.map +0 -1
- package/dist/ui/WorkflowDashboard.js.map +0 -1
- package/dist/ui/hooks/useWorkflowList.js.map +0 -1
- package/dist/ui/renderers/workflow.markdown.js.map +0 -1
- package/dist/workflow/workflow.enum.js.map +0 -1
- package/dist/workflow/workflow.event.js.map +0 -1
- package/dist/workflow/workflow.handler.js.map +0 -1
- package/dist/workflow/workflow.operations.js.map +0 -1
- package/dist/workflow/workflow.schema.js.map +0 -1
- package/dist/workflow-system.capability.js.map +0 -1
- package/dist/workflow-system.feature.js.map +0 -1
|
@@ -0,0 +1,3120 @@
|
|
|
1
|
+
// src/approval/approval.enum.ts
|
|
2
|
+
import { defineEnum } from "@contractspec/lib.schema";
|
|
3
|
+
var ApprovalStatusEnum = defineEnum("ApprovalStatus", [
|
|
4
|
+
"PENDING",
|
|
5
|
+
"APPROVED",
|
|
6
|
+
"REJECTED",
|
|
7
|
+
"DELEGATED",
|
|
8
|
+
"ESCALATED",
|
|
9
|
+
"WITHDRAWN",
|
|
10
|
+
"EXPIRED"
|
|
11
|
+
]);
|
|
12
|
+
var ApprovalDecisionEnum = defineEnum("ApprovalDecision", [
|
|
13
|
+
"APPROVE",
|
|
14
|
+
"REJECT",
|
|
15
|
+
"REQUEST_CHANGES",
|
|
16
|
+
"DELEGATE",
|
|
17
|
+
"ABSTAIN"
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
// src/approval/approval.event.ts
|
|
21
|
+
import { defineEvent, defineSchemaModel } from "@contractspec/lib.contracts";
|
|
22
|
+
import { ScalarTypeEnum } from "@contractspec/lib.schema";
|
|
23
|
+
var ApprovalRequestedPayload = defineSchemaModel({
|
|
24
|
+
name: "ApprovalRequestedEventPayload",
|
|
25
|
+
description: "Payload when approval is requested",
|
|
26
|
+
fields: {
|
|
27
|
+
requestId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
28
|
+
instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
29
|
+
workflowKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
30
|
+
approverId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
31
|
+
approverRole: {
|
|
32
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
33
|
+
isOptional: true
|
|
34
|
+
},
|
|
35
|
+
title: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
36
|
+
dueAt: { type: ScalarTypeEnum.DateTime(), isOptional: true },
|
|
37
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
var ApprovalDecidedPayload = defineSchemaModel({
|
|
41
|
+
name: "ApprovalDecidedEventPayload",
|
|
42
|
+
description: "Payload when approval decision is made",
|
|
43
|
+
fields: {
|
|
44
|
+
requestId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
45
|
+
instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
46
|
+
decision: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
47
|
+
decidedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
48
|
+
comment: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
49
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
var ApprovalDelegatedPayload = defineSchemaModel({
|
|
53
|
+
name: "ApprovalDelegatedEventPayload",
|
|
54
|
+
description: "Payload when approval is delegated",
|
|
55
|
+
fields: {
|
|
56
|
+
requestId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
57
|
+
instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
58
|
+
fromUserId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
59
|
+
toUserId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
60
|
+
reason: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
61
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
var ApprovalEscalatedPayload = defineSchemaModel({
|
|
65
|
+
name: "ApprovalEscalatedEventPayload",
|
|
66
|
+
description: "Payload when approval is escalated",
|
|
67
|
+
fields: {
|
|
68
|
+
requestId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
69
|
+
instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
70
|
+
escalationLevel: {
|
|
71
|
+
type: ScalarTypeEnum.Int_unsecure(),
|
|
72
|
+
isOptional: false
|
|
73
|
+
},
|
|
74
|
+
escalatedTo: {
|
|
75
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
76
|
+
isOptional: false
|
|
77
|
+
},
|
|
78
|
+
reason: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
79
|
+
timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
var ApprovalRequestedEvent = defineEvent({
|
|
83
|
+
meta: {
|
|
84
|
+
key: "workflow.approval.requested",
|
|
85
|
+
version: "1.0.0",
|
|
86
|
+
description: "An approval has been requested.",
|
|
87
|
+
stability: "stable",
|
|
88
|
+
owners: ["@workflow-team"],
|
|
89
|
+
tags: ["workflow", "approval", "requested"]
|
|
90
|
+
},
|
|
91
|
+
payload: ApprovalRequestedPayload
|
|
92
|
+
});
|
|
93
|
+
var ApprovalDecidedEvent = defineEvent({
|
|
94
|
+
meta: {
|
|
95
|
+
key: "workflow.approval.decided",
|
|
96
|
+
version: "1.0.0",
|
|
97
|
+
description: "An approval decision has been made.",
|
|
98
|
+
stability: "stable",
|
|
99
|
+
owners: ["@workflow-team"],
|
|
100
|
+
tags: ["workflow", "approval", "decided"]
|
|
101
|
+
},
|
|
102
|
+
payload: ApprovalDecidedPayload
|
|
103
|
+
});
|
|
104
|
+
var ApprovalDelegatedEvent = defineEvent({
|
|
105
|
+
meta: {
|
|
106
|
+
key: "workflow.approval.delegated",
|
|
107
|
+
version: "1.0.0",
|
|
108
|
+
description: "An approval has been delegated.",
|
|
109
|
+
stability: "stable",
|
|
110
|
+
owners: ["@workflow-team"],
|
|
111
|
+
tags: ["workflow", "approval", "delegated"]
|
|
112
|
+
},
|
|
113
|
+
payload: ApprovalDelegatedPayload
|
|
114
|
+
});
|
|
115
|
+
var ApprovalEscalatedEvent = defineEvent({
|
|
116
|
+
meta: {
|
|
117
|
+
key: "workflow.approval.escalated",
|
|
118
|
+
version: "1.0.0",
|
|
119
|
+
description: "An approval has been escalated.",
|
|
120
|
+
stability: "stable",
|
|
121
|
+
owners: ["@workflow-team"],
|
|
122
|
+
tags: ["workflow", "approval", "escalated"]
|
|
123
|
+
},
|
|
124
|
+
payload: ApprovalEscalatedPayload
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/shared/mock-data.ts
|
|
128
|
+
var mockDataStore = {
|
|
129
|
+
workflows: new Map,
|
|
130
|
+
steps: new Map,
|
|
131
|
+
instances: new Map,
|
|
132
|
+
approvals: new Map,
|
|
133
|
+
stepExecutions: new Map
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/state-machine/index.ts
|
|
137
|
+
class BasicStateMachineEngine {
|
|
138
|
+
canTransition(definition, state, action, context) {
|
|
139
|
+
if (state.status !== "RUNNING" && state.status !== "WAITING") {
|
|
140
|
+
return {
|
|
141
|
+
allowed: false,
|
|
142
|
+
reason: `Workflow is ${state.status}, cannot transition`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const currentStep = definition.steps[state.currentStepKey];
|
|
146
|
+
if (!currentStep) {
|
|
147
|
+
return {
|
|
148
|
+
allowed: false,
|
|
149
|
+
reason: `Step ${state.currentStepKey} not found`
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const transition = currentStep.transitions[action];
|
|
153
|
+
if (!transition) {
|
|
154
|
+
return {
|
|
155
|
+
allowed: false,
|
|
156
|
+
reason: `Action ${action} not available in step ${state.currentStepKey}`
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (currentStep.allowedRoles && currentStep.allowedRoles.length > 0) {
|
|
160
|
+
const hasRole = currentStep.allowedRoles.some((role) => context.userRoles.includes(role));
|
|
161
|
+
if (!hasRole) {
|
|
162
|
+
return {
|
|
163
|
+
allowed: false,
|
|
164
|
+
reason: `User lacks required role for this action`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (typeof transition === "object" && transition.allowedRoles && transition.allowedRoles.length > 0) {
|
|
169
|
+
const hasRole = transition.allowedRoles.some((role) => context.userRoles.includes(role));
|
|
170
|
+
if (!hasRole) {
|
|
171
|
+
return {
|
|
172
|
+
allowed: false,
|
|
173
|
+
reason: `User lacks required role for action ${action}`
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { allowed: true };
|
|
178
|
+
}
|
|
179
|
+
getAvailableActions(definition, state, context) {
|
|
180
|
+
if (state.status !== "RUNNING" && state.status !== "WAITING") {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
const currentStep = definition.steps[state.currentStepKey];
|
|
184
|
+
if (!currentStep) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
return Object.keys(currentStep.transitions).filter((action) => {
|
|
188
|
+
const result = this.canTransition(definition, state, action, context);
|
|
189
|
+
return result.allowed;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
transition(definition, state, action, context) {
|
|
193
|
+
const validation = this.canTransition(definition, state, action, context);
|
|
194
|
+
if (!validation.allowed) {
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
previousStepKey: state.currentStepKey,
|
|
198
|
+
currentStepKey: state.currentStepKey,
|
|
199
|
+
status: state.status,
|
|
200
|
+
error: validation.reason
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const currentStep = definition.steps[state.currentStepKey];
|
|
204
|
+
if (!currentStep) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
previousStepKey: state.currentStepKey,
|
|
208
|
+
currentStepKey: state.currentStepKey,
|
|
209
|
+
status: state.status,
|
|
210
|
+
error: `Current step ${state.currentStepKey} not found`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const transition = currentStep.transitions[action];
|
|
214
|
+
if (!transition) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
previousStepKey: state.currentStepKey,
|
|
218
|
+
currentStepKey: state.currentStepKey,
|
|
219
|
+
status: state.status,
|
|
220
|
+
error: `Transition for action ${action} not found`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const targetStepKey = typeof transition === "string" ? transition : transition.targetStepKey;
|
|
224
|
+
const targetStep = definition.steps[targetStepKey];
|
|
225
|
+
if (!targetStep) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
previousStepKey: state.currentStepKey,
|
|
229
|
+
currentStepKey: state.currentStepKey,
|
|
230
|
+
status: state.status,
|
|
231
|
+
error: `Target step ${targetStepKey} not found`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
let newStatus = "RUNNING";
|
|
235
|
+
if (targetStep.type === "END") {
|
|
236
|
+
newStatus = "COMPLETED";
|
|
237
|
+
} else if (targetStep.type === "APPROVAL" || targetStep.type === "WAIT") {
|
|
238
|
+
newStatus = "WAITING";
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
success: true,
|
|
242
|
+
previousStepKey: state.currentStepKey,
|
|
243
|
+
currentStepKey: targetStepKey,
|
|
244
|
+
status: newStatus
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
evaluateCondition(expression, contextData) {
|
|
248
|
+
try {
|
|
249
|
+
const match = expression.match(/^(\w+)\s*(>=|<=|>|<|===|!==|==|!=)\s*(.+)$/);
|
|
250
|
+
if (match) {
|
|
251
|
+
const [, prop, operator, value] = match;
|
|
252
|
+
if (!prop || !operator || value === undefined) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
const propValue = contextData[prop];
|
|
256
|
+
const compareValue = JSON.parse(value);
|
|
257
|
+
switch (operator) {
|
|
258
|
+
case ">":
|
|
259
|
+
return Number(propValue) > Number(compareValue);
|
|
260
|
+
case "<":
|
|
261
|
+
return Number(propValue) < Number(compareValue);
|
|
262
|
+
case ">=":
|
|
263
|
+
return Number(propValue) >= Number(compareValue);
|
|
264
|
+
case "<=":
|
|
265
|
+
return Number(propValue) <= Number(compareValue);
|
|
266
|
+
case "===":
|
|
267
|
+
case "==":
|
|
268
|
+
return propValue === compareValue;
|
|
269
|
+
case "!==":
|
|
270
|
+
case "!=":
|
|
271
|
+
return propValue !== compareValue;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (expression in contextData) {
|
|
275
|
+
return Boolean(contextData[expression]);
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function createStateMachineEngine() {
|
|
284
|
+
return new BasicStateMachineEngine;
|
|
285
|
+
}
|
|
286
|
+
function buildStateMachineDefinition(workflow, steps) {
|
|
287
|
+
const stepMap = {};
|
|
288
|
+
for (const step of steps) {
|
|
289
|
+
stepMap[step.key] = {
|
|
290
|
+
key: step.key,
|
|
291
|
+
name: step.name,
|
|
292
|
+
type: step.type,
|
|
293
|
+
transitions: step.transitions,
|
|
294
|
+
approvalMode: step.approvalMode,
|
|
295
|
+
allowedRoles: step.approverRoles,
|
|
296
|
+
timeoutSeconds: step.timeoutSeconds,
|
|
297
|
+
conditionExpression: step.conditionExpression
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const startStep = steps.find((s) => s.type === "START");
|
|
301
|
+
const initialStepKey = startStep?.key ?? steps[0]?.key ?? "";
|
|
302
|
+
return {
|
|
303
|
+
key: workflow.key,
|
|
304
|
+
name: workflow.name,
|
|
305
|
+
version: workflow.version,
|
|
306
|
+
initialStepKey,
|
|
307
|
+
steps: stepMap
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function createInitialState(definition, contextData = {}) {
|
|
311
|
+
return {
|
|
312
|
+
currentStepKey: definition.initialStepKey,
|
|
313
|
+
status: "RUNNING",
|
|
314
|
+
contextData,
|
|
315
|
+
history: []
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/approval/approval.schema.ts
|
|
320
|
+
import { defineSchemaModel as defineSchemaModel2, ScalarTypeEnum as ScalarTypeEnum2 } from "@contractspec/lib.schema";
|
|
321
|
+
var ApprovalRequestModel = defineSchemaModel2({
|
|
322
|
+
name: "ApprovalRequestModel",
|
|
323
|
+
description: "An approval request",
|
|
324
|
+
fields: {
|
|
325
|
+
id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
326
|
+
workflowInstanceId: {
|
|
327
|
+
type: ScalarTypeEnum2.String_unsecure(),
|
|
328
|
+
isOptional: false
|
|
329
|
+
},
|
|
330
|
+
stepExecutionId: {
|
|
331
|
+
type: ScalarTypeEnum2.String_unsecure(),
|
|
332
|
+
isOptional: false
|
|
333
|
+
},
|
|
334
|
+
approverId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
335
|
+
approverRole: {
|
|
336
|
+
type: ScalarTypeEnum2.String_unsecure(),
|
|
337
|
+
isOptional: true
|
|
338
|
+
},
|
|
339
|
+
title: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
340
|
+
description: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
|
|
341
|
+
status: { type: ApprovalStatusEnum, isOptional: false },
|
|
342
|
+
decision: { type: ApprovalDecisionEnum, isOptional: true },
|
|
343
|
+
decisionComment: {
|
|
344
|
+
type: ScalarTypeEnum2.String_unsecure(),
|
|
345
|
+
isOptional: true
|
|
346
|
+
},
|
|
347
|
+
decidedAt: { type: ScalarTypeEnum2.DateTime(), isOptional: true },
|
|
348
|
+
dueAt: { type: ScalarTypeEnum2.DateTime(), isOptional: true },
|
|
349
|
+
contextSnapshot: { type: ScalarTypeEnum2.JSON(), isOptional: true },
|
|
350
|
+
sequenceOrder: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false },
|
|
351
|
+
createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false }
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
var ApprovalCommentModel = defineSchemaModel2({
|
|
355
|
+
name: "ApprovalCommentModel",
|
|
356
|
+
description: "A comment on an approval",
|
|
357
|
+
fields: {
|
|
358
|
+
id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
359
|
+
approvalRequestId: {
|
|
360
|
+
type: ScalarTypeEnum2.String_unsecure(),
|
|
361
|
+
isOptional: false
|
|
362
|
+
},
|
|
363
|
+
authorId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
364
|
+
content: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
|
|
365
|
+
isInternal: { type: ScalarTypeEnum2.Boolean(), isOptional: false },
|
|
366
|
+
createdAt: { type: ScalarTypeEnum2.DateTime(), isOptional: false }
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// src/approval/approval.operations.ts
|
|
371
|
+
import {
|
|
372
|
+
defineCommand,
|
|
373
|
+
defineQuery
|
|
374
|
+
} from "@contractspec/lib.contracts/operations";
|
|
375
|
+
import { defineSchemaModel as defineSchemaModel3, ScalarTypeEnum as ScalarTypeEnum3 } from "@contractspec/lib.schema";
|
|
376
|
+
var OWNERS = ["@example.workflow-system"];
|
|
377
|
+
var SubmitDecisionContract = defineCommand({
|
|
378
|
+
meta: {
|
|
379
|
+
key: "workflow.approval.decide",
|
|
380
|
+
version: "1.0.0",
|
|
381
|
+
stability: "stable",
|
|
382
|
+
owners: [...OWNERS],
|
|
383
|
+
tags: ["workflow", "approval", "decision"],
|
|
384
|
+
description: "Submit an approval decision (approve/reject).",
|
|
385
|
+
goal: "Allow approvers to make decisions on requests.",
|
|
386
|
+
context: "Approval inbox, workflow detail."
|
|
387
|
+
},
|
|
388
|
+
io: {
|
|
389
|
+
input: defineSchemaModel3({
|
|
390
|
+
name: "ApproveRejectInput",
|
|
391
|
+
fields: {
|
|
392
|
+
requestId: {
|
|
393
|
+
type: ScalarTypeEnum3.String_unsecure(),
|
|
394
|
+
isOptional: false
|
|
395
|
+
},
|
|
396
|
+
decision: { type: ApprovalDecisionEnum, isOptional: false },
|
|
397
|
+
comment: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true },
|
|
398
|
+
data: { type: ScalarTypeEnum3.JSON(), isOptional: true }
|
|
399
|
+
}
|
|
400
|
+
}),
|
|
401
|
+
output: ApprovalRequestModel
|
|
402
|
+
},
|
|
403
|
+
policy: { auth: "user" },
|
|
404
|
+
sideEffects: {
|
|
405
|
+
emits: [
|
|
406
|
+
{
|
|
407
|
+
key: "workflow.approval.decided",
|
|
408
|
+
version: "1.0.0",
|
|
409
|
+
when: "Decision is made",
|
|
410
|
+
payload: ApprovalRequestModel
|
|
411
|
+
}
|
|
412
|
+
],
|
|
413
|
+
audit: ["workflow.approval.decided"]
|
|
414
|
+
},
|
|
415
|
+
acceptance: {
|
|
416
|
+
scenarios: [
|
|
417
|
+
{
|
|
418
|
+
key: "approve-request-happy-path",
|
|
419
|
+
given: ["Approval request is pending", "User is assignee"],
|
|
420
|
+
when: ["User approves request"],
|
|
421
|
+
then: ["Request is approved", "ApprovalDecided event is emitted"]
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
examples: [
|
|
425
|
+
{
|
|
426
|
+
key: "approve-basic",
|
|
427
|
+
input: {
|
|
428
|
+
requestId: "req-123",
|
|
429
|
+
decision: "approve",
|
|
430
|
+
comment: "Looks good"
|
|
431
|
+
},
|
|
432
|
+
output: { id: "req-123", status: "approved" }
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
var DelegateApprovalContract = defineCommand({
|
|
438
|
+
meta: {
|
|
439
|
+
key: "workflow.approval.delegate",
|
|
440
|
+
version: "1.0.0",
|
|
441
|
+
stability: "stable",
|
|
442
|
+
owners: [...OWNERS],
|
|
443
|
+
tags: ["workflow", "approval", "delegate"],
|
|
444
|
+
description: "Delegate an approval request to another user.",
|
|
445
|
+
goal: "Allow approvers to pass approval to others.",
|
|
446
|
+
context: "Approval inbox."
|
|
447
|
+
},
|
|
448
|
+
io: {
|
|
449
|
+
input: defineSchemaModel3({
|
|
450
|
+
name: "DelegateInput",
|
|
451
|
+
fields: {
|
|
452
|
+
requestId: {
|
|
453
|
+
type: ScalarTypeEnum3.String_unsecure(),
|
|
454
|
+
isOptional: false
|
|
455
|
+
},
|
|
456
|
+
delegateTo: {
|
|
457
|
+
type: ScalarTypeEnum3.String_unsecure(),
|
|
458
|
+
isOptional: false
|
|
459
|
+
},
|
|
460
|
+
reason: { type: ScalarTypeEnum3.String_unsecure(), isOptional: true }
|
|
461
|
+
}
|
|
462
|
+
}),
|
|
463
|
+
output: ApprovalRequestModel
|
|
464
|
+
},
|
|
465
|
+
policy: { auth: "user" },
|
|
466
|
+
sideEffects: {
|
|
467
|
+
emits: [
|
|
468
|
+
{
|
|
469
|
+
key: "workflow.approval.delegated",
|
|
470
|
+
version: "1.0.0",
|
|
471
|
+
when: "Approval is delegated",
|
|
472
|
+
payload: ApprovalRequestModel
|
|
473
|
+
}
|
|
474
|
+
],
|
|
475
|
+
audit: ["workflow.approval.delegated"]
|
|
476
|
+
},
|
|
477
|
+
acceptance: {
|
|
478
|
+
scenarios: [
|
|
479
|
+
{
|
|
480
|
+
key: "delegate-approval-happy-path",
|
|
481
|
+
given: ["Approval request is pending", "User is assignee"],
|
|
482
|
+
when: ["User delegates to another user"],
|
|
483
|
+
then: ["Assignee is updated", "ApprovalDelegated event is emitted"]
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
examples: [
|
|
487
|
+
{
|
|
488
|
+
key: "delegate-to-manager",
|
|
489
|
+
input: {
|
|
490
|
+
requestId: "req-123",
|
|
491
|
+
delegateTo: "user-456",
|
|
492
|
+
reason: "Out of office"
|
|
493
|
+
},
|
|
494
|
+
output: { id: "req-123", assigneeId: "user-456" }
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
var AddApprovalCommentContract = defineCommand({
|
|
500
|
+
meta: {
|
|
501
|
+
key: "workflow.approval.comment.add",
|
|
502
|
+
version: "1.0.0",
|
|
503
|
+
stability: "stable",
|
|
504
|
+
owners: [...OWNERS],
|
|
505
|
+
tags: ["workflow", "approval", "comment"],
|
|
506
|
+
description: "Add a comment to an approval request.",
|
|
507
|
+
goal: "Allow discussion on approval requests.",
|
|
508
|
+
context: "Approval detail view."
|
|
509
|
+
},
|
|
510
|
+
io: {
|
|
511
|
+
input: defineSchemaModel3({
|
|
512
|
+
name: "AddCommentInput",
|
|
513
|
+
fields: {
|
|
514
|
+
requestId: {
|
|
515
|
+
type: ScalarTypeEnum3.String_unsecure(),
|
|
516
|
+
isOptional: false
|
|
517
|
+
},
|
|
518
|
+
content: { type: ScalarTypeEnum3.NonEmptyString(), isOptional: false },
|
|
519
|
+
isInternal: { type: ScalarTypeEnum3.Boolean(), isOptional: true }
|
|
520
|
+
}
|
|
521
|
+
}),
|
|
522
|
+
output: ApprovalCommentModel
|
|
523
|
+
},
|
|
524
|
+
policy: { auth: "user" },
|
|
525
|
+
sideEffects: {
|
|
526
|
+
emits: [
|
|
527
|
+
{
|
|
528
|
+
key: "workflow.approval.comment.added",
|
|
529
|
+
version: "1.0.0",
|
|
530
|
+
when: "Comment is added",
|
|
531
|
+
payload: ApprovalCommentModel
|
|
532
|
+
}
|
|
533
|
+
]
|
|
534
|
+
},
|
|
535
|
+
acceptance: {
|
|
536
|
+
scenarios: [
|
|
537
|
+
{
|
|
538
|
+
key: "add-comment-happy-path",
|
|
539
|
+
given: ["Approval request exists"],
|
|
540
|
+
when: ["User adds a comment"],
|
|
541
|
+
then: ["Comment is added", "CommentAdded event is emitted"]
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
examples: [
|
|
545
|
+
{
|
|
546
|
+
key: "add-question",
|
|
547
|
+
input: {
|
|
548
|
+
requestId: "req-123",
|
|
549
|
+
content: "Can you clarify budget?",
|
|
550
|
+
isInternal: false
|
|
551
|
+
},
|
|
552
|
+
output: { id: "com-789", content: "Can you clarify budget?" }
|
|
553
|
+
}
|
|
554
|
+
]
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
var ListMyApprovalsContract = defineQuery({
|
|
558
|
+
meta: {
|
|
559
|
+
key: "workflow.approval.list.mine",
|
|
560
|
+
version: "1.0.0",
|
|
561
|
+
stability: "stable",
|
|
562
|
+
owners: [...OWNERS],
|
|
563
|
+
tags: ["workflow", "approval", "list", "inbox"],
|
|
564
|
+
description: "List approval requests assigned to current user.",
|
|
565
|
+
goal: "Show pending approvals in user inbox.",
|
|
566
|
+
context: "Approval inbox, dashboard widget."
|
|
567
|
+
},
|
|
568
|
+
io: {
|
|
569
|
+
input: defineSchemaModel3({
|
|
570
|
+
name: "ListMyApprovalsInput",
|
|
571
|
+
fields: {
|
|
572
|
+
status: { type: ApprovalStatusEnum, isOptional: true },
|
|
573
|
+
limit: {
|
|
574
|
+
type: ScalarTypeEnum3.Int_unsecure(),
|
|
575
|
+
isOptional: true,
|
|
576
|
+
defaultValue: 20
|
|
577
|
+
},
|
|
578
|
+
offset: {
|
|
579
|
+
type: ScalarTypeEnum3.Int_unsecure(),
|
|
580
|
+
isOptional: true,
|
|
581
|
+
defaultValue: 0
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}),
|
|
585
|
+
output: defineSchemaModel3({
|
|
586
|
+
name: "ListMyApprovalsOutput",
|
|
587
|
+
fields: {
|
|
588
|
+
requests: {
|
|
589
|
+
type: ApprovalRequestModel,
|
|
590
|
+
isArray: true,
|
|
591
|
+
isOptional: false
|
|
592
|
+
},
|
|
593
|
+
total: { type: ScalarTypeEnum3.Int_unsecure(), isOptional: false },
|
|
594
|
+
pendingCount: {
|
|
595
|
+
type: ScalarTypeEnum3.Int_unsecure(),
|
|
596
|
+
isOptional: false
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
})
|
|
600
|
+
},
|
|
601
|
+
policy: { auth: "user" },
|
|
602
|
+
acceptance: {
|
|
603
|
+
scenarios: [
|
|
604
|
+
{
|
|
605
|
+
key: "list-approvals-happy-path",
|
|
606
|
+
given: ["User has assigned approvals"],
|
|
607
|
+
when: ["User lists approvals"],
|
|
608
|
+
then: ["List of requests is returned"]
|
|
609
|
+
}
|
|
610
|
+
],
|
|
611
|
+
examples: [
|
|
612
|
+
{
|
|
613
|
+
key: "list-pending",
|
|
614
|
+
input: { status: "pending", limit: 10 },
|
|
615
|
+
output: { requests: [], total: 2, pendingCount: 2 }
|
|
616
|
+
}
|
|
617
|
+
]
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
var GetApprovalContract = defineQuery({
|
|
621
|
+
meta: {
|
|
622
|
+
key: "workflow.approval.get",
|
|
623
|
+
version: "1.0.0",
|
|
624
|
+
stability: "stable",
|
|
625
|
+
owners: [...OWNERS],
|
|
626
|
+
tags: ["workflow", "approval", "get"],
|
|
627
|
+
description: "Get an approval request with details.",
|
|
628
|
+
goal: "View approval request details.",
|
|
629
|
+
context: "Approval detail view."
|
|
630
|
+
},
|
|
631
|
+
io: {
|
|
632
|
+
input: defineSchemaModel3({
|
|
633
|
+
name: "GetApprovalInput",
|
|
634
|
+
fields: {
|
|
635
|
+
requestId: {
|
|
636
|
+
type: ScalarTypeEnum3.String_unsecure(),
|
|
637
|
+
isOptional: false
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}),
|
|
641
|
+
output: ApprovalRequestModel
|
|
642
|
+
},
|
|
643
|
+
policy: { auth: "user" },
|
|
644
|
+
acceptance: {
|
|
645
|
+
scenarios: [
|
|
646
|
+
{
|
|
647
|
+
key: "get-approval-happy-path",
|
|
648
|
+
given: ["Approval request exists"],
|
|
649
|
+
when: ["User requests approval details"],
|
|
650
|
+
then: ["Approval details are returned"]
|
|
651
|
+
}
|
|
652
|
+
],
|
|
653
|
+
examples: [
|
|
654
|
+
{
|
|
655
|
+
key: "get-basic",
|
|
656
|
+
input: { requestId: "req-123" },
|
|
657
|
+
output: { id: "req-123", status: "pending" }
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
// src/handlers/workflow.handlers.ts
|
|
663
|
+
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
664
|
+
var { generateId } = web;
|
|
665
|
+
function rowToDefinition(row) {
|
|
666
|
+
return {
|
|
667
|
+
id: row.id,
|
|
668
|
+
projectId: row.projectId,
|
|
669
|
+
organizationId: row.organizationId,
|
|
670
|
+
name: row.name,
|
|
671
|
+
description: row.description ?? undefined,
|
|
672
|
+
type: row.type,
|
|
673
|
+
status: row.status,
|
|
674
|
+
createdAt: new Date(row.createdAt),
|
|
675
|
+
updatedAt: new Date(row.updatedAt)
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function rowToStep(row) {
|
|
679
|
+
return {
|
|
680
|
+
id: row.id,
|
|
681
|
+
definitionId: row.definitionId,
|
|
682
|
+
name: row.name,
|
|
683
|
+
description: row.description ?? undefined,
|
|
684
|
+
stepOrder: row.stepOrder,
|
|
685
|
+
type: row.type,
|
|
686
|
+
requiredRoles: row.requiredRoles ? JSON.parse(row.requiredRoles) : [],
|
|
687
|
+
autoApproveCondition: row.autoApproveCondition ?? undefined,
|
|
688
|
+
timeoutHours: row.timeoutHours ?? undefined,
|
|
689
|
+
createdAt: new Date(row.createdAt)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function rowToInstance(row) {
|
|
693
|
+
return {
|
|
694
|
+
id: row.id,
|
|
695
|
+
projectId: row.projectId,
|
|
696
|
+
definitionId: row.definitionId,
|
|
697
|
+
status: row.status,
|
|
698
|
+
currentStepId: row.currentStepId ?? undefined,
|
|
699
|
+
data: row.data ? JSON.parse(row.data) : undefined,
|
|
700
|
+
requestedBy: row.requestedBy,
|
|
701
|
+
startedAt: new Date(row.startedAt),
|
|
702
|
+
completedAt: row.completedAt ? new Date(row.completedAt) : undefined
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function rowToApproval(row) {
|
|
706
|
+
return {
|
|
707
|
+
id: row.id,
|
|
708
|
+
instanceId: row.instanceId,
|
|
709
|
+
stepId: row.stepId,
|
|
710
|
+
status: row.status,
|
|
711
|
+
actorId: row.actorId ?? undefined,
|
|
712
|
+
comment: row.comment ?? undefined,
|
|
713
|
+
decidedAt: row.decidedAt ? new Date(row.decidedAt) : undefined,
|
|
714
|
+
createdAt: new Date(row.createdAt)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function createWorkflowHandlers(db) {
|
|
718
|
+
async function listDefinitions(input) {
|
|
719
|
+
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
720
|
+
let whereClause = "WHERE projectId = ?";
|
|
721
|
+
const params = [projectId];
|
|
722
|
+
if (status && status !== "all") {
|
|
723
|
+
whereClause += " AND status = ?";
|
|
724
|
+
params.push(status);
|
|
725
|
+
}
|
|
726
|
+
if (search) {
|
|
727
|
+
whereClause += " AND name LIKE ?";
|
|
728
|
+
params.push(`%${search}%`);
|
|
729
|
+
}
|
|
730
|
+
const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params)).rows;
|
|
731
|
+
const total = countResult[0]?.count ?? 0;
|
|
732
|
+
const rows = (await db.query(`SELECT * FROM workflow_definition ${whereClause} ORDER BY updatedAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
733
|
+
return {
|
|
734
|
+
definitions: rows.map(rowToDefinition),
|
|
735
|
+
total
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
async function createDefinition(input, context) {
|
|
739
|
+
const id = generateId("wfdef");
|
|
740
|
+
const now = new Date().toISOString();
|
|
741
|
+
await db.execute(`INSERT INTO workflow_definition (id, projectId, organizationId, name, description, type, status, createdAt, updatedAt)
|
|
742
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
743
|
+
id,
|
|
744
|
+
context.projectId,
|
|
745
|
+
context.organizationId,
|
|
746
|
+
input.name,
|
|
747
|
+
input.description ?? null,
|
|
748
|
+
input.type ?? "APPROVAL",
|
|
749
|
+
"DRAFT",
|
|
750
|
+
now,
|
|
751
|
+
now
|
|
752
|
+
]);
|
|
753
|
+
const rows = (await db.query(`SELECT * FROM workflow_definition WHERE id = ?`, [id])).rows;
|
|
754
|
+
return rowToDefinition(rows[0]);
|
|
755
|
+
}
|
|
756
|
+
async function addStep(input) {
|
|
757
|
+
const id = generateId("wfstep");
|
|
758
|
+
const now = new Date().toISOString();
|
|
759
|
+
const maxOrderResult = (await db.query(`SELECT MAX(stepOrder) as maxOrder FROM workflow_step WHERE definitionId = ?`, [input.definitionId])).rows;
|
|
760
|
+
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
761
|
+
await db.execute(`INSERT INTO workflow_step (id, definitionId, name, description, stepOrder, type, requiredRoles, autoApproveCondition, timeoutHours, createdAt)
|
|
762
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
763
|
+
id,
|
|
764
|
+
input.definitionId,
|
|
765
|
+
input.name,
|
|
766
|
+
input.description ?? null,
|
|
767
|
+
nextOrder,
|
|
768
|
+
input.type ?? "APPROVAL",
|
|
769
|
+
JSON.stringify(input.requiredRoles),
|
|
770
|
+
input.autoApproveCondition ?? null,
|
|
771
|
+
input.timeoutHours ?? null,
|
|
772
|
+
now
|
|
773
|
+
]);
|
|
774
|
+
const rows = (await db.query(`SELECT * FROM workflow_step WHERE id = ?`, [id])).rows;
|
|
775
|
+
return rowToStep(rows[0]);
|
|
776
|
+
}
|
|
777
|
+
async function getSteps(definitionId) {
|
|
778
|
+
const rows = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? ORDER BY stepOrder`, [definitionId])).rows;
|
|
779
|
+
return rows.map(rowToStep);
|
|
780
|
+
}
|
|
781
|
+
async function listInstances(input) {
|
|
782
|
+
const {
|
|
783
|
+
projectId,
|
|
784
|
+
definitionId,
|
|
785
|
+
status,
|
|
786
|
+
requestedBy,
|
|
787
|
+
limit = 20,
|
|
788
|
+
offset = 0
|
|
789
|
+
} = input;
|
|
790
|
+
let whereClause = "WHERE projectId = ?";
|
|
791
|
+
const params = [projectId];
|
|
792
|
+
if (definitionId) {
|
|
793
|
+
whereClause += " AND definitionId = ?";
|
|
794
|
+
params.push(definitionId);
|
|
795
|
+
}
|
|
796
|
+
if (status && status !== "all") {
|
|
797
|
+
whereClause += " AND status = ?";
|
|
798
|
+
params.push(status);
|
|
799
|
+
}
|
|
800
|
+
if (requestedBy) {
|
|
801
|
+
whereClause += " AND requestedBy = ?";
|
|
802
|
+
params.push(requestedBy);
|
|
803
|
+
}
|
|
804
|
+
const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params)).rows;
|
|
805
|
+
const total = countResult[0]?.count ?? 0;
|
|
806
|
+
const rows = (await db.query(`SELECT * FROM workflow_instance ${whereClause} ORDER BY startedAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
807
|
+
return {
|
|
808
|
+
instances: rows.map(rowToInstance),
|
|
809
|
+
total
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
async function startInstance(input, context) {
|
|
813
|
+
const id = generateId("wfinst");
|
|
814
|
+
const now = new Date().toISOString();
|
|
815
|
+
const steps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? ORDER BY stepOrder LIMIT 1`, [input.definitionId])).rows;
|
|
816
|
+
const firstStepId = steps[0]?.id ?? null;
|
|
817
|
+
await db.execute(`INSERT INTO workflow_instance (id, projectId, definitionId, status, currentStepId, data, requestedBy, startedAt)
|
|
818
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
819
|
+
id,
|
|
820
|
+
context.projectId,
|
|
821
|
+
input.definitionId,
|
|
822
|
+
firstStepId ? "IN_PROGRESS" : "PENDING",
|
|
823
|
+
firstStepId,
|
|
824
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
825
|
+
context.requestedBy,
|
|
826
|
+
now
|
|
827
|
+
]);
|
|
828
|
+
if (firstStepId) {
|
|
829
|
+
await db.execute(`INSERT INTO workflow_approval (id, instanceId, stepId, status, createdAt)
|
|
830
|
+
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
831
|
+
}
|
|
832
|
+
const rows = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [id])).rows;
|
|
833
|
+
return rowToInstance(rows[0]);
|
|
834
|
+
}
|
|
835
|
+
async function approveStep(input, context) {
|
|
836
|
+
const now = new Date().toISOString();
|
|
837
|
+
const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
838
|
+
input.instanceId
|
|
839
|
+
])).rows;
|
|
840
|
+
if (!instances[0]) {
|
|
841
|
+
throw new Error("NOT_FOUND");
|
|
842
|
+
}
|
|
843
|
+
const instance = instances[0];
|
|
844
|
+
await db.execute(`UPDATE workflow_approval SET status = 'APPROVED', actorId = ?, comment = ?, decidedAt = ?
|
|
845
|
+
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
846
|
+
context.actorId,
|
|
847
|
+
input.comment ?? null,
|
|
848
|
+
now,
|
|
849
|
+
input.instanceId,
|
|
850
|
+
instance.currentStepId
|
|
851
|
+
]);
|
|
852
|
+
const currentStep = (await db.query(`SELECT * FROM workflow_step WHERE id = ?`, [
|
|
853
|
+
instance.currentStepId
|
|
854
|
+
])).rows;
|
|
855
|
+
const nextSteps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? AND stepOrder > ? ORDER BY stepOrder LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0])).rows;
|
|
856
|
+
if (nextSteps[0]) {
|
|
857
|
+
await db.execute(`UPDATE workflow_instance SET currentStepId = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
858
|
+
await db.execute(`INSERT INTO workflow_approval (id, instanceId, stepId, status, createdAt)
|
|
859
|
+
VALUES (?, ?, ?, ?, ?)`, [
|
|
860
|
+
generateId("wfappr"),
|
|
861
|
+
input.instanceId,
|
|
862
|
+
nextSteps[0].id,
|
|
863
|
+
"PENDING",
|
|
864
|
+
now
|
|
865
|
+
]);
|
|
866
|
+
} else {
|
|
867
|
+
await db.execute(`UPDATE workflow_instance SET status = 'COMPLETED', currentStepId = NULL, completedAt = ? WHERE id = ?`, [now, input.instanceId]);
|
|
868
|
+
}
|
|
869
|
+
const updated = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
870
|
+
input.instanceId
|
|
871
|
+
])).rows;
|
|
872
|
+
return rowToInstance(updated[0]);
|
|
873
|
+
}
|
|
874
|
+
async function rejectStep(input, context) {
|
|
875
|
+
const now = new Date().toISOString();
|
|
876
|
+
const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
877
|
+
input.instanceId
|
|
878
|
+
])).rows;
|
|
879
|
+
if (!instances[0]) {
|
|
880
|
+
throw new Error("NOT_FOUND");
|
|
881
|
+
}
|
|
882
|
+
await db.execute(`UPDATE workflow_approval SET status = 'REJECTED', actorId = ?, comment = ?, decidedAt = ?
|
|
883
|
+
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
884
|
+
context.actorId,
|
|
885
|
+
input.reason,
|
|
886
|
+
now,
|
|
887
|
+
input.instanceId,
|
|
888
|
+
instances[0].currentStepId
|
|
889
|
+
]);
|
|
890
|
+
await db.execute(`UPDATE workflow_instance SET status = 'REJECTED', completedAt = ? WHERE id = ?`, [now, input.instanceId]);
|
|
891
|
+
const updated = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
892
|
+
input.instanceId
|
|
893
|
+
])).rows;
|
|
894
|
+
return rowToInstance(updated[0]);
|
|
895
|
+
}
|
|
896
|
+
async function getApprovals(instanceId) {
|
|
897
|
+
const rows = (await db.query(`SELECT * FROM workflow_approval WHERE instanceId = ? ORDER BY createdAt`, [instanceId])).rows;
|
|
898
|
+
return rows.map(rowToApproval);
|
|
899
|
+
}
|
|
900
|
+
return {
|
|
901
|
+
listDefinitions,
|
|
902
|
+
createDefinition,
|
|
903
|
+
addStep,
|
|
904
|
+
getSteps,
|
|
905
|
+
listInstances,
|
|
906
|
+
startInstance,
|
|
907
|
+
approveStep,
|
|
908
|
+
rejectStep,
|
|
909
|
+
getApprovals
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/workflow/workflow.enum.ts
|
|
914
|
+
import { defineEnum as defineEnum2 } from "@contractspec/lib.schema";
|
|
915
|
+
var WorkflowStatusEnum = defineEnum2("WorkflowStatus", [
|
|
916
|
+
"DRAFT",
|
|
917
|
+
"ACTIVE",
|
|
918
|
+
"DEPRECATED",
|
|
919
|
+
"ARCHIVED"
|
|
920
|
+
]);
|
|
921
|
+
var TriggerTypeEnum = defineEnum2("WorkflowTriggerType", [
|
|
922
|
+
"MANUAL",
|
|
923
|
+
"EVENT",
|
|
924
|
+
"SCHEDULED",
|
|
925
|
+
"API"
|
|
926
|
+
]);
|
|
927
|
+
var StepTypeEnum = defineEnum2("StepType", [
|
|
928
|
+
"START",
|
|
929
|
+
"APPROVAL",
|
|
930
|
+
"TASK",
|
|
931
|
+
"CONDITION",
|
|
932
|
+
"PARALLEL",
|
|
933
|
+
"WAIT",
|
|
934
|
+
"ACTION",
|
|
935
|
+
"END"
|
|
936
|
+
]);
|
|
937
|
+
var ApprovalModeEnum = defineEnum2("ApprovalMode", [
|
|
938
|
+
"ANY",
|
|
939
|
+
"ALL",
|
|
940
|
+
"MAJORITY",
|
|
941
|
+
"SEQUENTIAL"
|
|
942
|
+
]);
|
|
943
|
+
|
|
944
|
+
// src/workflow/workflow.schema.ts
|
|
945
|
+
import { defineSchemaModel as defineSchemaModel4, ScalarTypeEnum as ScalarTypeEnum4 } from "@contractspec/lib.schema";
|
|
946
|
+
var WorkflowStepModel = defineSchemaModel4({
|
|
947
|
+
name: "WorkflowStepModel",
|
|
948
|
+
description: "A step in a workflow definition",
|
|
949
|
+
fields: {
|
|
950
|
+
id: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
951
|
+
key: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
952
|
+
name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
953
|
+
description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
954
|
+
type: { type: StepTypeEnum, isOptional: false },
|
|
955
|
+
position: { type: ScalarTypeEnum4.Int_unsecure(), isOptional: false },
|
|
956
|
+
transitions: { type: ScalarTypeEnum4.JSON(), isOptional: false },
|
|
957
|
+
approvalMode: { type: ApprovalModeEnum, isOptional: true },
|
|
958
|
+
approverRoles: {
|
|
959
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
960
|
+
isArray: true,
|
|
961
|
+
isOptional: true
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
var WorkflowDefinitionModel = defineSchemaModel4({
|
|
966
|
+
name: "WorkflowDefinitionModel",
|
|
967
|
+
description: "A workflow definition",
|
|
968
|
+
fields: {
|
|
969
|
+
id: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
970
|
+
name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
971
|
+
key: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
972
|
+
description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
973
|
+
version: { type: ScalarTypeEnum4.String_unsecure(), isOptional: false },
|
|
974
|
+
status: { type: WorkflowStatusEnum, isOptional: false },
|
|
975
|
+
triggerType: { type: TriggerTypeEnum, isOptional: false },
|
|
976
|
+
initialStepId: {
|
|
977
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
978
|
+
isOptional: true
|
|
979
|
+
},
|
|
980
|
+
featureFlagKey: {
|
|
981
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
982
|
+
isOptional: true
|
|
983
|
+
},
|
|
984
|
+
organizationId: {
|
|
985
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
986
|
+
isOptional: false
|
|
987
|
+
},
|
|
988
|
+
createdAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false },
|
|
989
|
+
updatedAt: { type: ScalarTypeEnum4.DateTime(), isOptional: false },
|
|
990
|
+
steps: { type: WorkflowStepModel, isArray: true, isOptional: true }
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
var CreateWorkflowInputModel = defineSchemaModel4({
|
|
994
|
+
name: "CreateWorkflowInput",
|
|
995
|
+
description: "Input for creating a workflow definition",
|
|
996
|
+
fields: {
|
|
997
|
+
name: { type: ScalarTypeEnum4.NonEmptyString(), isOptional: false },
|
|
998
|
+
key: { type: ScalarTypeEnum4.NonEmptyString(), isOptional: false },
|
|
999
|
+
description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
1000
|
+
triggerType: { type: TriggerTypeEnum, isOptional: true },
|
|
1001
|
+
triggerConfig: { type: ScalarTypeEnum4.JSON(), isOptional: true },
|
|
1002
|
+
featureFlagKey: {
|
|
1003
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1004
|
+
isOptional: true
|
|
1005
|
+
},
|
|
1006
|
+
settings: { type: ScalarTypeEnum4.JSON(), isOptional: true }
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
var UpdateWorkflowInputModel = defineSchemaModel4({
|
|
1010
|
+
name: "UpdateWorkflowInput",
|
|
1011
|
+
description: "Input for updating a workflow definition",
|
|
1012
|
+
fields: {
|
|
1013
|
+
workflowId: {
|
|
1014
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1015
|
+
isOptional: false
|
|
1016
|
+
},
|
|
1017
|
+
name: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
1018
|
+
description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
1019
|
+
triggerType: { type: TriggerTypeEnum, isOptional: true },
|
|
1020
|
+
triggerConfig: { type: ScalarTypeEnum4.JSON(), isOptional: true },
|
|
1021
|
+
featureFlagKey: {
|
|
1022
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1023
|
+
isOptional: true
|
|
1024
|
+
},
|
|
1025
|
+
settings: { type: ScalarTypeEnum4.JSON(), isOptional: true }
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
var AddStepInputModel = defineSchemaModel4({
|
|
1029
|
+
name: "AddStepInput",
|
|
1030
|
+
description: "Input for adding a step to a workflow",
|
|
1031
|
+
fields: {
|
|
1032
|
+
workflowId: {
|
|
1033
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1034
|
+
isOptional: false
|
|
1035
|
+
},
|
|
1036
|
+
key: { type: ScalarTypeEnum4.NonEmptyString(), isOptional: false },
|
|
1037
|
+
name: { type: ScalarTypeEnum4.NonEmptyString(), isOptional: false },
|
|
1038
|
+
description: { type: ScalarTypeEnum4.String_unsecure(), isOptional: true },
|
|
1039
|
+
type: { type: StepTypeEnum, isOptional: false },
|
|
1040
|
+
position: { type: ScalarTypeEnum4.Int_unsecure(), isOptional: true },
|
|
1041
|
+
transitions: { type: ScalarTypeEnum4.JSON(), isOptional: false },
|
|
1042
|
+
approvalMode: { type: ApprovalModeEnum, isOptional: true },
|
|
1043
|
+
approverRoles: {
|
|
1044
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1045
|
+
isArray: true,
|
|
1046
|
+
isOptional: true
|
|
1047
|
+
},
|
|
1048
|
+
approverUserIds: {
|
|
1049
|
+
type: ScalarTypeEnum4.String_unsecure(),
|
|
1050
|
+
isArray: true,
|
|
1051
|
+
isOptional: true
|
|
1052
|
+
},
|
|
1053
|
+
timeoutSeconds: { type: ScalarTypeEnum4.Int_unsecure(), isOptional: true },
|
|
1054
|
+
slaSeconds: { type: ScalarTypeEnum4.Int_unsecure(), isOptional: true }
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
// src/workflow/workflow.operations.ts
|
|
1059
|
+
import {
|
|
1060
|
+
defineCommand as defineCommand2,
|
|
1061
|
+
defineQuery as defineQuery2
|
|
1062
|
+
} from "@contractspec/lib.contracts/operations";
|
|
1063
|
+
import { defineSchemaModel as defineSchemaModel5, ScalarTypeEnum as ScalarTypeEnum5 } from "@contractspec/lib.schema";
|
|
1064
|
+
var OWNERS2 = ["@example.workflow-system"];
|
|
1065
|
+
var CreateWorkflowContract = defineCommand2({
|
|
1066
|
+
meta: {
|
|
1067
|
+
key: "workflow.definition.create",
|
|
1068
|
+
version: "1.0.0",
|
|
1069
|
+
stability: "stable",
|
|
1070
|
+
owners: [...OWNERS2],
|
|
1071
|
+
tags: ["workflow", "definition", "create"],
|
|
1072
|
+
description: "Create a new workflow definition.",
|
|
1073
|
+
goal: "Allow users to define new workflow blueprints.",
|
|
1074
|
+
context: "Workflow designer, admin panel."
|
|
1075
|
+
},
|
|
1076
|
+
io: {
|
|
1077
|
+
input: CreateWorkflowInputModel,
|
|
1078
|
+
output: WorkflowDefinitionModel
|
|
1079
|
+
},
|
|
1080
|
+
policy: { auth: "user" },
|
|
1081
|
+
sideEffects: {
|
|
1082
|
+
emits: [
|
|
1083
|
+
{
|
|
1084
|
+
key: "workflow.definition.created",
|
|
1085
|
+
version: "1.0.0",
|
|
1086
|
+
when: "Workflow is created",
|
|
1087
|
+
payload: WorkflowDefinitionModel
|
|
1088
|
+
}
|
|
1089
|
+
],
|
|
1090
|
+
audit: ["workflow.definition.created"]
|
|
1091
|
+
},
|
|
1092
|
+
acceptance: {
|
|
1093
|
+
scenarios: [
|
|
1094
|
+
{
|
|
1095
|
+
key: "create-workflow-happy-path",
|
|
1096
|
+
given: ["User is admin"],
|
|
1097
|
+
when: ["User creates new workflow definition"],
|
|
1098
|
+
then: [
|
|
1099
|
+
"Definition is created",
|
|
1100
|
+
"WorkflowDefinitionCreated event is emitted"
|
|
1101
|
+
]
|
|
1102
|
+
}
|
|
1103
|
+
],
|
|
1104
|
+
examples: [
|
|
1105
|
+
{
|
|
1106
|
+
key: "create-onboarding",
|
|
1107
|
+
input: {
|
|
1108
|
+
key: "onboarding-v1",
|
|
1109
|
+
name: "Employee Onboarding",
|
|
1110
|
+
version: "1.0.0"
|
|
1111
|
+
},
|
|
1112
|
+
output: { id: "def-123", status: "draft" }
|
|
1113
|
+
}
|
|
1114
|
+
]
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
var UpdateWorkflowContract = defineCommand2({
|
|
1118
|
+
meta: {
|
|
1119
|
+
key: "workflow.definition.update",
|
|
1120
|
+
version: "1.0.0",
|
|
1121
|
+
stability: "stable",
|
|
1122
|
+
owners: [...OWNERS2],
|
|
1123
|
+
tags: ["workflow", "definition", "update"],
|
|
1124
|
+
description: "Update an existing workflow definition.",
|
|
1125
|
+
goal: "Allow users to modify workflow blueprints.",
|
|
1126
|
+
context: "Workflow designer."
|
|
1127
|
+
},
|
|
1128
|
+
io: {
|
|
1129
|
+
input: UpdateWorkflowInputModel,
|
|
1130
|
+
output: WorkflowDefinitionModel
|
|
1131
|
+
},
|
|
1132
|
+
policy: { auth: "user" },
|
|
1133
|
+
sideEffects: {
|
|
1134
|
+
emits: [
|
|
1135
|
+
{
|
|
1136
|
+
key: "workflow.definition.updated",
|
|
1137
|
+
version: "1.0.0",
|
|
1138
|
+
when: "Workflow is updated",
|
|
1139
|
+
payload: WorkflowDefinitionModel
|
|
1140
|
+
}
|
|
1141
|
+
],
|
|
1142
|
+
audit: ["workflow.definition.updated"]
|
|
1143
|
+
},
|
|
1144
|
+
acceptance: {
|
|
1145
|
+
scenarios: [
|
|
1146
|
+
{
|
|
1147
|
+
key: "update-workflow-happy-path",
|
|
1148
|
+
given: ["Workflow definition exists"],
|
|
1149
|
+
when: ["User updates definition"],
|
|
1150
|
+
then: [
|
|
1151
|
+
"Definition is updated",
|
|
1152
|
+
"WorkflowDefinitionUpdated event is emitted"
|
|
1153
|
+
]
|
|
1154
|
+
}
|
|
1155
|
+
],
|
|
1156
|
+
examples: [
|
|
1157
|
+
{
|
|
1158
|
+
key: "update-name",
|
|
1159
|
+
input: { workflowId: "def-123", name: "New Employee Onboarding" },
|
|
1160
|
+
output: { id: "def-123", name: "New Employee Onboarding" }
|
|
1161
|
+
}
|
|
1162
|
+
]
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
var AddStepContract = defineCommand2({
|
|
1166
|
+
meta: {
|
|
1167
|
+
key: "workflow.step.add",
|
|
1168
|
+
version: "1.0.0",
|
|
1169
|
+
stability: "stable",
|
|
1170
|
+
owners: [...OWNERS2],
|
|
1171
|
+
tags: ["workflow", "step", "add"],
|
|
1172
|
+
description: "Add a step to a workflow definition.",
|
|
1173
|
+
goal: "Build workflow structure step by step.",
|
|
1174
|
+
context: "Workflow designer."
|
|
1175
|
+
},
|
|
1176
|
+
io: {
|
|
1177
|
+
input: AddStepInputModel,
|
|
1178
|
+
output: WorkflowStepModel
|
|
1179
|
+
},
|
|
1180
|
+
policy: { auth: "user" },
|
|
1181
|
+
sideEffects: {
|
|
1182
|
+
emits: [
|
|
1183
|
+
{
|
|
1184
|
+
key: "workflow.step.added",
|
|
1185
|
+
version: "1.0.0",
|
|
1186
|
+
when: "Step is added",
|
|
1187
|
+
payload: WorkflowStepModel
|
|
1188
|
+
}
|
|
1189
|
+
],
|
|
1190
|
+
audit: ["workflow.step.added"]
|
|
1191
|
+
},
|
|
1192
|
+
acceptance: {
|
|
1193
|
+
scenarios: [
|
|
1194
|
+
{
|
|
1195
|
+
key: "add-step-happy-path",
|
|
1196
|
+
given: ["Workflow definition exists"],
|
|
1197
|
+
when: ["User adds a step"],
|
|
1198
|
+
then: ["Step is added", "StepAdded event is emitted"]
|
|
1199
|
+
}
|
|
1200
|
+
],
|
|
1201
|
+
examples: [
|
|
1202
|
+
{
|
|
1203
|
+
key: "add-approval-step",
|
|
1204
|
+
input: {
|
|
1205
|
+
workflowId: "def-123",
|
|
1206
|
+
stepKey: "approve-contract",
|
|
1207
|
+
type: "approval"
|
|
1208
|
+
},
|
|
1209
|
+
output: { id: "step-456", key: "approve-contract" }
|
|
1210
|
+
}
|
|
1211
|
+
]
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
var PublishWorkflowContract = defineCommand2({
|
|
1215
|
+
meta: {
|
|
1216
|
+
key: "workflow.definition.publish",
|
|
1217
|
+
version: "1.0.0",
|
|
1218
|
+
stability: "stable",
|
|
1219
|
+
owners: [...OWNERS2],
|
|
1220
|
+
tags: ["workflow", "definition", "publish"],
|
|
1221
|
+
description: "Publish a workflow definition to make it available for use.",
|
|
1222
|
+
goal: "Activate workflow for production use.",
|
|
1223
|
+
context: "Workflow designer, deployment."
|
|
1224
|
+
},
|
|
1225
|
+
io: {
|
|
1226
|
+
input: defineSchemaModel5({
|
|
1227
|
+
name: "PublishWorkflowInput",
|
|
1228
|
+
fields: {
|
|
1229
|
+
workflowId: {
|
|
1230
|
+
type: ScalarTypeEnum5.String_unsecure(),
|
|
1231
|
+
isOptional: false
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}),
|
|
1235
|
+
output: WorkflowDefinitionModel
|
|
1236
|
+
},
|
|
1237
|
+
policy: { auth: "user" },
|
|
1238
|
+
sideEffects: {
|
|
1239
|
+
emits: [
|
|
1240
|
+
{
|
|
1241
|
+
key: "workflow.definition.published",
|
|
1242
|
+
version: "1.0.0",
|
|
1243
|
+
when: "Workflow is published",
|
|
1244
|
+
payload: WorkflowDefinitionModel
|
|
1245
|
+
}
|
|
1246
|
+
],
|
|
1247
|
+
audit: ["workflow.definition.published"]
|
|
1248
|
+
},
|
|
1249
|
+
acceptance: {
|
|
1250
|
+
scenarios: [
|
|
1251
|
+
{
|
|
1252
|
+
key: "publish-workflow-happy-path",
|
|
1253
|
+
given: ["Workflow definition is valid"],
|
|
1254
|
+
when: ["User publishes workflow"],
|
|
1255
|
+
then: ["Workflow becomes active", "WorkflowPublished event is emitted"]
|
|
1256
|
+
}
|
|
1257
|
+
],
|
|
1258
|
+
examples: [
|
|
1259
|
+
{
|
|
1260
|
+
key: "publish-onboarding",
|
|
1261
|
+
input: { workflowId: "def-123" },
|
|
1262
|
+
output: { id: "def-123", status: "published" }
|
|
1263
|
+
}
|
|
1264
|
+
]
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
var ListWorkflowsContract = defineQuery2({
|
|
1268
|
+
meta: {
|
|
1269
|
+
key: "workflow.definition.list",
|
|
1270
|
+
version: "1.0.0",
|
|
1271
|
+
stability: "stable",
|
|
1272
|
+
owners: [...OWNERS2],
|
|
1273
|
+
tags: ["workflow", "definition", "list"],
|
|
1274
|
+
description: "List workflow definitions with filtering.",
|
|
1275
|
+
goal: "Browse and search available workflows.",
|
|
1276
|
+
context: "Workflow list, search."
|
|
1277
|
+
},
|
|
1278
|
+
io: {
|
|
1279
|
+
input: defineSchemaModel5({
|
|
1280
|
+
name: "ListWorkflowsInput",
|
|
1281
|
+
fields: {
|
|
1282
|
+
status: { type: WorkflowStatusEnum, isOptional: true },
|
|
1283
|
+
search: { type: ScalarTypeEnum5.String_unsecure(), isOptional: true },
|
|
1284
|
+
limit: {
|
|
1285
|
+
type: ScalarTypeEnum5.Int_unsecure(),
|
|
1286
|
+
isOptional: true,
|
|
1287
|
+
defaultValue: 20
|
|
1288
|
+
},
|
|
1289
|
+
offset: {
|
|
1290
|
+
type: ScalarTypeEnum5.Int_unsecure(),
|
|
1291
|
+
isOptional: true,
|
|
1292
|
+
defaultValue: 0
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}),
|
|
1296
|
+
output: defineSchemaModel5({
|
|
1297
|
+
name: "ListWorkflowsOutput",
|
|
1298
|
+
fields: {
|
|
1299
|
+
workflows: {
|
|
1300
|
+
type: WorkflowDefinitionModel,
|
|
1301
|
+
isArray: true,
|
|
1302
|
+
isOptional: false
|
|
1303
|
+
},
|
|
1304
|
+
total: { type: ScalarTypeEnum5.Int_unsecure(), isOptional: false }
|
|
1305
|
+
}
|
|
1306
|
+
})
|
|
1307
|
+
},
|
|
1308
|
+
policy: { auth: "user" },
|
|
1309
|
+
acceptance: {
|
|
1310
|
+
scenarios: [
|
|
1311
|
+
{
|
|
1312
|
+
key: "list-workflows-happy-path",
|
|
1313
|
+
given: ["Workflow definitions exist"],
|
|
1314
|
+
when: ["User lists workflows"],
|
|
1315
|
+
then: ["List of workflows is returned"]
|
|
1316
|
+
}
|
|
1317
|
+
],
|
|
1318
|
+
examples: [
|
|
1319
|
+
{
|
|
1320
|
+
key: "list-all",
|
|
1321
|
+
input: { limit: 10 },
|
|
1322
|
+
output: { workflows: [], total: 5 }
|
|
1323
|
+
}
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
var GetWorkflowContract = defineQuery2({
|
|
1328
|
+
meta: {
|
|
1329
|
+
key: "workflow.definition.get",
|
|
1330
|
+
version: "1.0.0",
|
|
1331
|
+
stability: "stable",
|
|
1332
|
+
owners: [...OWNERS2],
|
|
1333
|
+
tags: ["workflow", "definition", "get"],
|
|
1334
|
+
description: "Get a workflow definition with all steps.",
|
|
1335
|
+
goal: "View workflow details.",
|
|
1336
|
+
context: "Workflow designer, detail view."
|
|
1337
|
+
},
|
|
1338
|
+
io: {
|
|
1339
|
+
input: defineSchemaModel5({
|
|
1340
|
+
name: "GetWorkflowInput",
|
|
1341
|
+
fields: {
|
|
1342
|
+
workflowId: {
|
|
1343
|
+
type: ScalarTypeEnum5.String_unsecure(),
|
|
1344
|
+
isOptional: false
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}),
|
|
1348
|
+
output: WorkflowDefinitionModel
|
|
1349
|
+
},
|
|
1350
|
+
policy: { auth: "user" },
|
|
1351
|
+
acceptance: {
|
|
1352
|
+
scenarios: [
|
|
1353
|
+
{
|
|
1354
|
+
key: "get-workflow-happy-path",
|
|
1355
|
+
given: ["Workflow definition exists"],
|
|
1356
|
+
when: ["User requests workflow details"],
|
|
1357
|
+
then: ["Workflow details are returned"]
|
|
1358
|
+
}
|
|
1359
|
+
],
|
|
1360
|
+
examples: [
|
|
1361
|
+
{
|
|
1362
|
+
key: "get-details",
|
|
1363
|
+
input: { workflowId: "def-123" },
|
|
1364
|
+
output: { id: "def-123", name: "Employee Onboarding" }
|
|
1365
|
+
}
|
|
1366
|
+
]
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
// src/workflow/workflow.event.ts
|
|
1371
|
+
import { defineEvent as defineEvent2, defineSchemaModel as defineSchemaModel6 } from "@contractspec/lib.contracts";
|
|
1372
|
+
import { ScalarTypeEnum as ScalarTypeEnum6 } from "@contractspec/lib.schema";
|
|
1373
|
+
var WorkflowDefinitionPayload = defineSchemaModel6({
|
|
1374
|
+
name: "WorkflowDefinitionEventPayload",
|
|
1375
|
+
description: "Payload for workflow definition events",
|
|
1376
|
+
fields: {
|
|
1377
|
+
workflowId: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1378
|
+
key: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1379
|
+
name: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1380
|
+
version: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1381
|
+
organizationId: {
|
|
1382
|
+
type: ScalarTypeEnum6.String_unsecure(),
|
|
1383
|
+
isOptional: false
|
|
1384
|
+
},
|
|
1385
|
+
createdBy: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1386
|
+
timestamp: { type: ScalarTypeEnum6.DateTime(), isOptional: false }
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
var StepAddedPayload = defineSchemaModel6({
|
|
1390
|
+
name: "StepAddedEventPayload",
|
|
1391
|
+
description: "Payload when a step is added",
|
|
1392
|
+
fields: {
|
|
1393
|
+
stepId: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1394
|
+
workflowId: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1395
|
+
stepKey: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1396
|
+
stepType: { type: ScalarTypeEnum6.String_unsecure(), isOptional: false },
|
|
1397
|
+
position: { type: ScalarTypeEnum6.Int_unsecure(), isOptional: false },
|
|
1398
|
+
timestamp: { type: ScalarTypeEnum6.DateTime(), isOptional: false }
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
var WorkflowCreatedEvent = defineEvent2({
|
|
1402
|
+
meta: {
|
|
1403
|
+
key: "workflow.definition.created",
|
|
1404
|
+
version: "1.0.0",
|
|
1405
|
+
description: "A new workflow definition has been created.",
|
|
1406
|
+
stability: "stable",
|
|
1407
|
+
owners: ["@workflow-team"],
|
|
1408
|
+
tags: ["workflow", "definition", "created"]
|
|
1409
|
+
},
|
|
1410
|
+
payload: WorkflowDefinitionPayload
|
|
1411
|
+
});
|
|
1412
|
+
var WorkflowUpdatedEvent = defineEvent2({
|
|
1413
|
+
meta: {
|
|
1414
|
+
key: "workflow.definition.updated",
|
|
1415
|
+
version: "1.0.0",
|
|
1416
|
+
description: "A workflow definition has been updated.",
|
|
1417
|
+
stability: "stable",
|
|
1418
|
+
owners: ["@workflow-team"],
|
|
1419
|
+
tags: ["workflow", "definition", "updated"]
|
|
1420
|
+
},
|
|
1421
|
+
payload: WorkflowDefinitionPayload
|
|
1422
|
+
});
|
|
1423
|
+
var WorkflowPublishedEvent = defineEvent2({
|
|
1424
|
+
meta: {
|
|
1425
|
+
key: "workflow.definition.published",
|
|
1426
|
+
version: "1.0.0",
|
|
1427
|
+
description: "A workflow definition has been published and is now active.",
|
|
1428
|
+
stability: "stable",
|
|
1429
|
+
owners: ["@workflow-team"],
|
|
1430
|
+
tags: ["workflow", "definition", "published"]
|
|
1431
|
+
},
|
|
1432
|
+
payload: WorkflowDefinitionPayload
|
|
1433
|
+
});
|
|
1434
|
+
var StepAddedEvent = defineEvent2({
|
|
1435
|
+
meta: {
|
|
1436
|
+
key: "workflow.step.added",
|
|
1437
|
+
version: "1.0.0",
|
|
1438
|
+
description: "A step has been added to a workflow definition.",
|
|
1439
|
+
stability: "stable",
|
|
1440
|
+
owners: ["@workflow-team"],
|
|
1441
|
+
tags: ["workflow", "step", "added"]
|
|
1442
|
+
},
|
|
1443
|
+
payload: StepAddedPayload
|
|
1444
|
+
});
|
|
1445
|
+
// src/instance/instance.enum.ts
|
|
1446
|
+
import { defineEnum as defineEnum3 } from "@contractspec/lib.schema";
|
|
1447
|
+
var InstanceStatusEnum = defineEnum3("InstanceStatus", [
|
|
1448
|
+
"PENDING",
|
|
1449
|
+
"RUNNING",
|
|
1450
|
+
"WAITING",
|
|
1451
|
+
"PAUSED",
|
|
1452
|
+
"COMPLETED",
|
|
1453
|
+
"CANCELLED",
|
|
1454
|
+
"FAILED",
|
|
1455
|
+
"TIMEOUT"
|
|
1456
|
+
]);
|
|
1457
|
+
|
|
1458
|
+
// src/instance/instance.schema.ts
|
|
1459
|
+
import { defineSchemaModel as defineSchemaModel7, ScalarTypeEnum as ScalarTypeEnum7 } from "@contractspec/lib.schema";
|
|
1460
|
+
var WorkflowInstanceModel = defineSchemaModel7({
|
|
1461
|
+
name: "WorkflowInstanceModel",
|
|
1462
|
+
description: "A running workflow instance",
|
|
1463
|
+
fields: {
|
|
1464
|
+
id: { type: ScalarTypeEnum7.String_unsecure(), isOptional: false },
|
|
1465
|
+
workflowDefinitionId: {
|
|
1466
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1467
|
+
isOptional: false
|
|
1468
|
+
},
|
|
1469
|
+
referenceId: { type: ScalarTypeEnum7.String_unsecure(), isOptional: true },
|
|
1470
|
+
referenceType: {
|
|
1471
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1472
|
+
isOptional: true
|
|
1473
|
+
},
|
|
1474
|
+
status: { type: InstanceStatusEnum, isOptional: false },
|
|
1475
|
+
currentStepId: {
|
|
1476
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1477
|
+
isOptional: true
|
|
1478
|
+
},
|
|
1479
|
+
contextData: { type: ScalarTypeEnum7.JSON(), isOptional: true },
|
|
1480
|
+
triggeredBy: { type: ScalarTypeEnum7.String_unsecure(), isOptional: false },
|
|
1481
|
+
organizationId: {
|
|
1482
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1483
|
+
isOptional: false
|
|
1484
|
+
},
|
|
1485
|
+
priority: { type: ScalarTypeEnum7.Int_unsecure(), isOptional: false },
|
|
1486
|
+
dueAt: { type: ScalarTypeEnum7.DateTime(), isOptional: true },
|
|
1487
|
+
outcome: { type: ScalarTypeEnum7.String_unsecure(), isOptional: true },
|
|
1488
|
+
resultData: { type: ScalarTypeEnum7.JSON(), isOptional: true },
|
|
1489
|
+
errorMessage: {
|
|
1490
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1491
|
+
isOptional: true
|
|
1492
|
+
},
|
|
1493
|
+
createdAt: { type: ScalarTypeEnum7.DateTime(), isOptional: false },
|
|
1494
|
+
startedAt: { type: ScalarTypeEnum7.DateTime(), isOptional: true },
|
|
1495
|
+
completedAt: { type: ScalarTypeEnum7.DateTime(), isOptional: true }
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
var StartWorkflowInputModel = defineSchemaModel7({
|
|
1499
|
+
name: "StartWorkflowInput",
|
|
1500
|
+
description: "Input for starting a workflow",
|
|
1501
|
+
fields: {
|
|
1502
|
+
workflowKey: { type: ScalarTypeEnum7.NonEmptyString(), isOptional: false },
|
|
1503
|
+
contextData: { type: ScalarTypeEnum7.JSON(), isOptional: true },
|
|
1504
|
+
referenceId: { type: ScalarTypeEnum7.String_unsecure(), isOptional: true },
|
|
1505
|
+
referenceType: {
|
|
1506
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1507
|
+
isOptional: true
|
|
1508
|
+
},
|
|
1509
|
+
priority: { type: ScalarTypeEnum7.Int_unsecure(), isOptional: true },
|
|
1510
|
+
dueAt: { type: ScalarTypeEnum7.DateTime(), isOptional: true }
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
var TransitionInputModel = defineSchemaModel7({
|
|
1514
|
+
name: "TransitionInput",
|
|
1515
|
+
description: "Input for transitioning a workflow",
|
|
1516
|
+
fields: {
|
|
1517
|
+
instanceId: { type: ScalarTypeEnum7.String_unsecure(), isOptional: false },
|
|
1518
|
+
action: { type: ScalarTypeEnum7.NonEmptyString(), isOptional: false },
|
|
1519
|
+
data: { type: ScalarTypeEnum7.JSON(), isOptional: true },
|
|
1520
|
+
comment: { type: ScalarTypeEnum7.String_unsecure(), isOptional: true }
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
var TransitionResultModel = defineSchemaModel7({
|
|
1524
|
+
name: "TransitionResult",
|
|
1525
|
+
description: "Result of a workflow transition",
|
|
1526
|
+
fields: {
|
|
1527
|
+
success: { type: ScalarTypeEnum7.Boolean(), isOptional: false },
|
|
1528
|
+
instance: { type: WorkflowInstanceModel, isOptional: false },
|
|
1529
|
+
previousStepKey: {
|
|
1530
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1531
|
+
isOptional: true
|
|
1532
|
+
},
|
|
1533
|
+
currentStepKey: {
|
|
1534
|
+
type: ScalarTypeEnum7.String_unsecure(),
|
|
1535
|
+
isOptional: true
|
|
1536
|
+
},
|
|
1537
|
+
message: { type: ScalarTypeEnum7.String_unsecure(), isOptional: true }
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1541
|
+
// src/instance/instance.operations.ts
|
|
1542
|
+
import {
|
|
1543
|
+
defineCommand as defineCommand3,
|
|
1544
|
+
defineQuery as defineQuery3
|
|
1545
|
+
} from "@contractspec/lib.contracts/operations";
|
|
1546
|
+
import { defineSchemaModel as defineSchemaModel8, ScalarTypeEnum as ScalarTypeEnum8 } from "@contractspec/lib.schema";
|
|
1547
|
+
var OWNERS3 = ["@example.workflow-system"];
|
|
1548
|
+
var StartWorkflowContract = defineCommand3({
|
|
1549
|
+
meta: {
|
|
1550
|
+
key: "workflow.instance.start",
|
|
1551
|
+
version: "1.0.0",
|
|
1552
|
+
stability: "stable",
|
|
1553
|
+
owners: [...OWNERS3],
|
|
1554
|
+
tags: ["workflow", "instance", "start"],
|
|
1555
|
+
description: "Start a new workflow instance.",
|
|
1556
|
+
goal: "Initiate a workflow for a business process.",
|
|
1557
|
+
context: "Order creation, request submission, etc."
|
|
1558
|
+
},
|
|
1559
|
+
io: {
|
|
1560
|
+
input: StartWorkflowInputModel,
|
|
1561
|
+
output: WorkflowInstanceModel
|
|
1562
|
+
},
|
|
1563
|
+
policy: { auth: "user" },
|
|
1564
|
+
sideEffects: {
|
|
1565
|
+
emits: [
|
|
1566
|
+
{
|
|
1567
|
+
key: "workflow.instance.started",
|
|
1568
|
+
version: "1.0.0",
|
|
1569
|
+
when: "Workflow starts",
|
|
1570
|
+
payload: WorkflowInstanceModel
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
key: "workflow.step.entered",
|
|
1574
|
+
version: "1.0.0",
|
|
1575
|
+
when: "First step entered",
|
|
1576
|
+
payload: WorkflowInstanceModel
|
|
1577
|
+
}
|
|
1578
|
+
],
|
|
1579
|
+
audit: ["workflow.instance.started"]
|
|
1580
|
+
},
|
|
1581
|
+
acceptance: {
|
|
1582
|
+
scenarios: [
|
|
1583
|
+
{
|
|
1584
|
+
key: "start-workflow-happy-path",
|
|
1585
|
+
given: ["Workflow definition exists"],
|
|
1586
|
+
when: ["User starts workflow"],
|
|
1587
|
+
then: ["Instance is created and started"]
|
|
1588
|
+
}
|
|
1589
|
+
],
|
|
1590
|
+
examples: [
|
|
1591
|
+
{
|
|
1592
|
+
key: "start-onboarding",
|
|
1593
|
+
input: {
|
|
1594
|
+
workflowKey: "onboarding-v1",
|
|
1595
|
+
context: { employeeId: "emp-123" }
|
|
1596
|
+
},
|
|
1597
|
+
output: { id: "inst-456", status: "running" }
|
|
1598
|
+
}
|
|
1599
|
+
]
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
var TransitionWorkflowContract = defineCommand3({
|
|
1603
|
+
meta: {
|
|
1604
|
+
key: "workflow.instance.transition",
|
|
1605
|
+
version: "1.0.0",
|
|
1606
|
+
stability: "stable",
|
|
1607
|
+
owners: [...OWNERS3],
|
|
1608
|
+
tags: ["workflow", "instance", "transition", "state-machine"],
|
|
1609
|
+
description: "Transition a workflow instance to the next step.",
|
|
1610
|
+
goal: "Move workflow forward based on action.",
|
|
1611
|
+
context: "Task completion, approval decisions."
|
|
1612
|
+
},
|
|
1613
|
+
io: {
|
|
1614
|
+
input: TransitionInputModel,
|
|
1615
|
+
output: TransitionResultModel
|
|
1616
|
+
},
|
|
1617
|
+
policy: { auth: "user" },
|
|
1618
|
+
sideEffects: {
|
|
1619
|
+
emits: [
|
|
1620
|
+
{
|
|
1621
|
+
key: "workflow.step.exited",
|
|
1622
|
+
version: "1.0.0",
|
|
1623
|
+
when: "Step is exited",
|
|
1624
|
+
payload: WorkflowInstanceModel
|
|
1625
|
+
},
|
|
1626
|
+
{
|
|
1627
|
+
key: "workflow.step.entered",
|
|
1628
|
+
version: "1.0.0",
|
|
1629
|
+
when: "New step is entered",
|
|
1630
|
+
payload: WorkflowInstanceModel
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
key: "workflow.instance.completed",
|
|
1634
|
+
version: "1.0.0",
|
|
1635
|
+
when: "Workflow reaches end",
|
|
1636
|
+
payload: WorkflowInstanceModel
|
|
1637
|
+
}
|
|
1638
|
+
],
|
|
1639
|
+
audit: ["workflow.instance.transitioned"]
|
|
1640
|
+
},
|
|
1641
|
+
acceptance: {
|
|
1642
|
+
scenarios: [
|
|
1643
|
+
{
|
|
1644
|
+
key: "transition-workflow-happy-path",
|
|
1645
|
+
given: ["Workflow instance is waiting at step"],
|
|
1646
|
+
when: ["User provides input"],
|
|
1647
|
+
then: ["Instance moves to next step"]
|
|
1648
|
+
}
|
|
1649
|
+
],
|
|
1650
|
+
examples: [
|
|
1651
|
+
{
|
|
1652
|
+
key: "complete-task",
|
|
1653
|
+
input: {
|
|
1654
|
+
instanceId: "inst-456",
|
|
1655
|
+
action: "complete",
|
|
1656
|
+
data: { approved: true }
|
|
1657
|
+
},
|
|
1658
|
+
output: { success: true, nextStep: "notify-hr" }
|
|
1659
|
+
}
|
|
1660
|
+
]
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
var PauseWorkflowContract = defineCommand3({
|
|
1664
|
+
meta: {
|
|
1665
|
+
key: "workflow.instance.pause",
|
|
1666
|
+
version: "1.0.0",
|
|
1667
|
+
stability: "stable",
|
|
1668
|
+
owners: [...OWNERS3],
|
|
1669
|
+
tags: ["workflow", "instance", "pause"],
|
|
1670
|
+
description: "Pause a running workflow instance.",
|
|
1671
|
+
goal: "Temporarily halt workflow execution.",
|
|
1672
|
+
context: "Administrative action, emergency stop."
|
|
1673
|
+
},
|
|
1674
|
+
io: {
|
|
1675
|
+
input: defineSchemaModel8({
|
|
1676
|
+
name: "PauseResumeInput",
|
|
1677
|
+
fields: {
|
|
1678
|
+
instanceId: {
|
|
1679
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1680
|
+
isOptional: false
|
|
1681
|
+
},
|
|
1682
|
+
reason: { type: ScalarTypeEnum8.String_unsecure(), isOptional: true }
|
|
1683
|
+
}
|
|
1684
|
+
}),
|
|
1685
|
+
output: WorkflowInstanceModel
|
|
1686
|
+
},
|
|
1687
|
+
policy: { auth: "user" },
|
|
1688
|
+
sideEffects: {
|
|
1689
|
+
emits: [
|
|
1690
|
+
{
|
|
1691
|
+
key: "workflow.instance.paused",
|
|
1692
|
+
version: "1.0.0",
|
|
1693
|
+
when: "Workflow is paused",
|
|
1694
|
+
payload: WorkflowInstanceModel
|
|
1695
|
+
}
|
|
1696
|
+
],
|
|
1697
|
+
audit: ["workflow.instance.paused"]
|
|
1698
|
+
},
|
|
1699
|
+
acceptance: {
|
|
1700
|
+
scenarios: [
|
|
1701
|
+
{
|
|
1702
|
+
key: "pause-workflow-happy-path",
|
|
1703
|
+
given: ["Workflow is running"],
|
|
1704
|
+
when: ["Admin pauses workflow"],
|
|
1705
|
+
then: ["Instance status becomes PAUSED"]
|
|
1706
|
+
}
|
|
1707
|
+
],
|
|
1708
|
+
examples: [
|
|
1709
|
+
{
|
|
1710
|
+
key: "pause-maintenance",
|
|
1711
|
+
input: { instanceId: "inst-456", reason: "System maintenance" },
|
|
1712
|
+
output: { id: "inst-456", status: "paused" }
|
|
1713
|
+
}
|
|
1714
|
+
]
|
|
1715
|
+
}
|
|
1716
|
+
});
|
|
1717
|
+
var ResumeWorkflowContract = defineCommand3({
|
|
1718
|
+
meta: {
|
|
1719
|
+
key: "workflow.instance.resume",
|
|
1720
|
+
version: "1.0.0",
|
|
1721
|
+
stability: "stable",
|
|
1722
|
+
owners: [...OWNERS3],
|
|
1723
|
+
tags: ["workflow", "instance", "resume"],
|
|
1724
|
+
description: "Resume a paused workflow instance.",
|
|
1725
|
+
goal: "Continue workflow execution.",
|
|
1726
|
+
context: "Administrative action."
|
|
1727
|
+
},
|
|
1728
|
+
io: {
|
|
1729
|
+
input: defineSchemaModel8({
|
|
1730
|
+
name: "PauseResumeInput",
|
|
1731
|
+
fields: {
|
|
1732
|
+
instanceId: {
|
|
1733
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1734
|
+
isOptional: false
|
|
1735
|
+
},
|
|
1736
|
+
reason: { type: ScalarTypeEnum8.String_unsecure(), isOptional: true }
|
|
1737
|
+
}
|
|
1738
|
+
}),
|
|
1739
|
+
output: WorkflowInstanceModel
|
|
1740
|
+
},
|
|
1741
|
+
policy: { auth: "user" },
|
|
1742
|
+
sideEffects: {
|
|
1743
|
+
emits: [
|
|
1744
|
+
{
|
|
1745
|
+
key: "workflow.instance.resumed",
|
|
1746
|
+
version: "1.0.0",
|
|
1747
|
+
when: "Workflow is resumed",
|
|
1748
|
+
payload: WorkflowInstanceModel
|
|
1749
|
+
}
|
|
1750
|
+
],
|
|
1751
|
+
audit: ["workflow.instance.resumed"]
|
|
1752
|
+
},
|
|
1753
|
+
acceptance: {
|
|
1754
|
+
scenarios: [
|
|
1755
|
+
{
|
|
1756
|
+
key: "resume-workflow-happy-path",
|
|
1757
|
+
given: ["Workflow is paused"],
|
|
1758
|
+
when: ["Admin resumes workflow"],
|
|
1759
|
+
then: ["Instance status becomes RUNNING"]
|
|
1760
|
+
}
|
|
1761
|
+
],
|
|
1762
|
+
examples: [
|
|
1763
|
+
{
|
|
1764
|
+
key: "resume-normal",
|
|
1765
|
+
input: { instanceId: "inst-456", reason: "Issue resolved" },
|
|
1766
|
+
output: { id: "inst-456", status: "running" }
|
|
1767
|
+
}
|
|
1768
|
+
]
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
var CancelWorkflowContract = defineCommand3({
|
|
1772
|
+
meta: {
|
|
1773
|
+
key: "workflow.instance.cancel",
|
|
1774
|
+
version: "1.0.0",
|
|
1775
|
+
stability: "stable",
|
|
1776
|
+
owners: [...OWNERS3],
|
|
1777
|
+
tags: ["workflow", "instance", "cancel"],
|
|
1778
|
+
description: "Cancel a workflow instance.",
|
|
1779
|
+
goal: "Terminate workflow without completion.",
|
|
1780
|
+
context: "User request, system cancellation."
|
|
1781
|
+
},
|
|
1782
|
+
io: {
|
|
1783
|
+
input: defineSchemaModel8({
|
|
1784
|
+
name: "CancelWorkflowInput",
|
|
1785
|
+
fields: {
|
|
1786
|
+
instanceId: {
|
|
1787
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1788
|
+
isOptional: false
|
|
1789
|
+
},
|
|
1790
|
+
reason: {
|
|
1791
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1792
|
+
isOptional: false
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}),
|
|
1796
|
+
output: WorkflowInstanceModel
|
|
1797
|
+
},
|
|
1798
|
+
policy: { auth: "user" },
|
|
1799
|
+
sideEffects: {
|
|
1800
|
+
emits: [
|
|
1801
|
+
{
|
|
1802
|
+
key: "workflow.instance.cancelled",
|
|
1803
|
+
version: "1.0.0",
|
|
1804
|
+
when: "Workflow is cancelled",
|
|
1805
|
+
payload: WorkflowInstanceModel
|
|
1806
|
+
}
|
|
1807
|
+
],
|
|
1808
|
+
audit: ["workflow.instance.cancelled"]
|
|
1809
|
+
},
|
|
1810
|
+
acceptance: {
|
|
1811
|
+
scenarios: [
|
|
1812
|
+
{
|
|
1813
|
+
key: "cancel-workflow-happy-path",
|
|
1814
|
+
given: ["Workflow is running"],
|
|
1815
|
+
when: ["User cancels workflow"],
|
|
1816
|
+
then: ["Instance status becomes CANCELLED"]
|
|
1817
|
+
}
|
|
1818
|
+
],
|
|
1819
|
+
examples: [
|
|
1820
|
+
{
|
|
1821
|
+
key: "cancel-mistake",
|
|
1822
|
+
input: { instanceId: "inst-456", reason: "Created by mistake" },
|
|
1823
|
+
output: { id: "inst-456", status: "cancelled" }
|
|
1824
|
+
}
|
|
1825
|
+
]
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
var ListInstancesContract = defineQuery3({
|
|
1829
|
+
meta: {
|
|
1830
|
+
key: "workflow.instance.list",
|
|
1831
|
+
version: "1.0.0",
|
|
1832
|
+
stability: "stable",
|
|
1833
|
+
owners: [...OWNERS3],
|
|
1834
|
+
tags: ["workflow", "instance", "list"],
|
|
1835
|
+
description: "List workflow instances with filtering.",
|
|
1836
|
+
goal: "Browse and search running workflows.",
|
|
1837
|
+
context: "Dashboard, monitoring."
|
|
1838
|
+
},
|
|
1839
|
+
io: {
|
|
1840
|
+
input: defineSchemaModel8({
|
|
1841
|
+
name: "ListInstancesInput",
|
|
1842
|
+
fields: {
|
|
1843
|
+
workflowKey: {
|
|
1844
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1845
|
+
isOptional: true
|
|
1846
|
+
},
|
|
1847
|
+
status: { type: InstanceStatusEnum, isOptional: true },
|
|
1848
|
+
referenceType: {
|
|
1849
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1850
|
+
isOptional: true
|
|
1851
|
+
},
|
|
1852
|
+
referenceId: {
|
|
1853
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1854
|
+
isOptional: true
|
|
1855
|
+
},
|
|
1856
|
+
triggeredBy: {
|
|
1857
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1858
|
+
isOptional: true
|
|
1859
|
+
},
|
|
1860
|
+
limit: {
|
|
1861
|
+
type: ScalarTypeEnum8.Int_unsecure(),
|
|
1862
|
+
isOptional: true,
|
|
1863
|
+
defaultValue: 20
|
|
1864
|
+
},
|
|
1865
|
+
offset: {
|
|
1866
|
+
type: ScalarTypeEnum8.Int_unsecure(),
|
|
1867
|
+
isOptional: true,
|
|
1868
|
+
defaultValue: 0
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}),
|
|
1872
|
+
output: defineSchemaModel8({
|
|
1873
|
+
name: "ListInstancesOutput",
|
|
1874
|
+
fields: {
|
|
1875
|
+
instances: {
|
|
1876
|
+
type: WorkflowInstanceModel,
|
|
1877
|
+
isArray: true,
|
|
1878
|
+
isOptional: false
|
|
1879
|
+
},
|
|
1880
|
+
total: { type: ScalarTypeEnum8.Int_unsecure(), isOptional: false }
|
|
1881
|
+
}
|
|
1882
|
+
})
|
|
1883
|
+
},
|
|
1884
|
+
policy: { auth: "user" },
|
|
1885
|
+
acceptance: {
|
|
1886
|
+
scenarios: [
|
|
1887
|
+
{
|
|
1888
|
+
key: "list-instances-happy-path",
|
|
1889
|
+
given: ["Workflow instances exist"],
|
|
1890
|
+
when: ["User lists instances"],
|
|
1891
|
+
then: ["List of instances is returned"]
|
|
1892
|
+
}
|
|
1893
|
+
],
|
|
1894
|
+
examples: [
|
|
1895
|
+
{
|
|
1896
|
+
key: "list-running",
|
|
1897
|
+
input: { status: "running", limit: 10 },
|
|
1898
|
+
output: { instances: [], total: 5 }
|
|
1899
|
+
}
|
|
1900
|
+
]
|
|
1901
|
+
}
|
|
1902
|
+
});
|
|
1903
|
+
var GetInstanceContract = defineQuery3({
|
|
1904
|
+
meta: {
|
|
1905
|
+
key: "workflow.instance.get",
|
|
1906
|
+
version: "1.0.0",
|
|
1907
|
+
stability: "stable",
|
|
1908
|
+
owners: [...OWNERS3],
|
|
1909
|
+
tags: ["workflow", "instance", "get"],
|
|
1910
|
+
description: "Get a workflow instance with details.",
|
|
1911
|
+
goal: "View workflow instance details.",
|
|
1912
|
+
context: "Instance detail view."
|
|
1913
|
+
},
|
|
1914
|
+
io: {
|
|
1915
|
+
input: defineSchemaModel8({
|
|
1916
|
+
name: "GetInstanceInput",
|
|
1917
|
+
fields: {
|
|
1918
|
+
instanceId: {
|
|
1919
|
+
type: ScalarTypeEnum8.String_unsecure(),
|
|
1920
|
+
isOptional: false
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}),
|
|
1924
|
+
output: WorkflowInstanceModel
|
|
1925
|
+
},
|
|
1926
|
+
policy: { auth: "user" },
|
|
1927
|
+
acceptance: {
|
|
1928
|
+
scenarios: [
|
|
1929
|
+
{
|
|
1930
|
+
key: "get-instance-happy-path",
|
|
1931
|
+
given: ["Instance exists"],
|
|
1932
|
+
when: ["User requests instance details"],
|
|
1933
|
+
then: ["Instance details are returned"]
|
|
1934
|
+
}
|
|
1935
|
+
],
|
|
1936
|
+
examples: [
|
|
1937
|
+
{
|
|
1938
|
+
key: "get-details",
|
|
1939
|
+
input: { instanceId: "inst-456" },
|
|
1940
|
+
output: { id: "inst-456", workflowKey: "onboarding-v1" }
|
|
1941
|
+
}
|
|
1942
|
+
]
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
// src/instance/instance.event.ts
|
|
1947
|
+
import { defineEvent as defineEvent3, defineSchemaModel as defineSchemaModel9 } from "@contractspec/lib.contracts";
|
|
1948
|
+
import { ScalarTypeEnum as ScalarTypeEnum9 } from "@contractspec/lib.schema";
|
|
1949
|
+
var InstanceEventPayload = defineSchemaModel9({
|
|
1950
|
+
name: "InstanceEventPayload",
|
|
1951
|
+
description: "Base payload for instance events",
|
|
1952
|
+
fields: {
|
|
1953
|
+
instanceId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1954
|
+
workflowId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1955
|
+
workflowKey: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1956
|
+
status: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1957
|
+
referenceId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: true },
|
|
1958
|
+
referenceType: {
|
|
1959
|
+
type: ScalarTypeEnum9.String_unsecure(),
|
|
1960
|
+
isOptional: true
|
|
1961
|
+
},
|
|
1962
|
+
triggeredBy: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1963
|
+
organizationId: {
|
|
1964
|
+
type: ScalarTypeEnum9.String_unsecure(),
|
|
1965
|
+
isOptional: false
|
|
1966
|
+
},
|
|
1967
|
+
timestamp: { type: ScalarTypeEnum9.DateTime(), isOptional: false }
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
var StepTransitionPayload = defineSchemaModel9({
|
|
1971
|
+
name: "StepTransitionEventPayload",
|
|
1972
|
+
description: "Payload for step transition events",
|
|
1973
|
+
fields: {
|
|
1974
|
+
instanceId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1975
|
+
workflowId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1976
|
+
fromStepKey: { type: ScalarTypeEnum9.String_unsecure(), isOptional: true },
|
|
1977
|
+
toStepKey: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1978
|
+
action: { type: ScalarTypeEnum9.String_unsecure(), isOptional: true },
|
|
1979
|
+
executedBy: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1980
|
+
timestamp: { type: ScalarTypeEnum9.DateTime(), isOptional: false }
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
var InstanceCompletedPayload = defineSchemaModel9({
|
|
1984
|
+
name: "InstanceCompletedEventPayload",
|
|
1985
|
+
description: "Payload when instance completes",
|
|
1986
|
+
fields: {
|
|
1987
|
+
instanceId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1988
|
+
workflowId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1989
|
+
workflowKey: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1990
|
+
outcome: { type: ScalarTypeEnum9.String_unsecure(), isOptional: false },
|
|
1991
|
+
referenceId: { type: ScalarTypeEnum9.String_unsecure(), isOptional: true },
|
|
1992
|
+
referenceType: {
|
|
1993
|
+
type: ScalarTypeEnum9.String_unsecure(),
|
|
1994
|
+
isOptional: true
|
|
1995
|
+
},
|
|
1996
|
+
duration: { type: ScalarTypeEnum9.Int_unsecure(), isOptional: false },
|
|
1997
|
+
timestamp: { type: ScalarTypeEnum9.DateTime(), isOptional: false }
|
|
1998
|
+
}
|
|
1999
|
+
});
|
|
2000
|
+
var InstanceStartedEvent = defineEvent3({
|
|
2001
|
+
meta: {
|
|
2002
|
+
key: "workflow.instance.started",
|
|
2003
|
+
version: "1.0.0",
|
|
2004
|
+
description: "A new workflow instance has been started.",
|
|
2005
|
+
stability: "stable",
|
|
2006
|
+
owners: ["@workflow-team"],
|
|
2007
|
+
tags: ["workflow", "instance", "started"]
|
|
2008
|
+
},
|
|
2009
|
+
payload: InstanceEventPayload
|
|
2010
|
+
});
|
|
2011
|
+
var StepEnteredEvent = defineEvent3({
|
|
2012
|
+
meta: {
|
|
2013
|
+
key: "workflow.step.entered",
|
|
2014
|
+
version: "1.0.0",
|
|
2015
|
+
description: "A workflow instance has entered a new step.",
|
|
2016
|
+
stability: "stable",
|
|
2017
|
+
owners: ["@workflow-team"],
|
|
2018
|
+
tags: ["workflow", "step", "entered"]
|
|
2019
|
+
},
|
|
2020
|
+
payload: StepTransitionPayload
|
|
2021
|
+
});
|
|
2022
|
+
var StepExitedEvent = defineEvent3({
|
|
2023
|
+
meta: {
|
|
2024
|
+
key: "workflow.step.exited",
|
|
2025
|
+
version: "1.0.0",
|
|
2026
|
+
description: "A workflow instance has exited a step.",
|
|
2027
|
+
stability: "stable",
|
|
2028
|
+
owners: ["@workflow-team"],
|
|
2029
|
+
tags: ["workflow", "step", "exited"]
|
|
2030
|
+
},
|
|
2031
|
+
payload: StepTransitionPayload
|
|
2032
|
+
});
|
|
2033
|
+
var InstanceCompletedEvent = defineEvent3({
|
|
2034
|
+
meta: {
|
|
2035
|
+
key: "workflow.instance.completed",
|
|
2036
|
+
version: "1.0.0",
|
|
2037
|
+
description: "A workflow instance has completed.",
|
|
2038
|
+
stability: "stable",
|
|
2039
|
+
owners: ["@workflow-team"],
|
|
2040
|
+
tags: ["workflow", "instance", "completed"]
|
|
2041
|
+
},
|
|
2042
|
+
payload: InstanceCompletedPayload
|
|
2043
|
+
});
|
|
2044
|
+
var InstanceCancelledEvent = defineEvent3({
|
|
2045
|
+
meta: {
|
|
2046
|
+
key: "workflow.instance.cancelled",
|
|
2047
|
+
version: "1.0.0",
|
|
2048
|
+
description: "A workflow instance has been cancelled.",
|
|
2049
|
+
stability: "stable",
|
|
2050
|
+
owners: ["@workflow-team"],
|
|
2051
|
+
tags: ["workflow", "instance", "cancelled"]
|
|
2052
|
+
},
|
|
2053
|
+
payload: InstanceEventPayload
|
|
2054
|
+
});
|
|
2055
|
+
var InstancePausedEvent = defineEvent3({
|
|
2056
|
+
meta: {
|
|
2057
|
+
key: "workflow.instance.paused",
|
|
2058
|
+
version: "1.0.0",
|
|
2059
|
+
description: "A workflow instance has been paused.",
|
|
2060
|
+
stability: "stable",
|
|
2061
|
+
owners: ["@workflow-team"],
|
|
2062
|
+
tags: ["workflow", "instance", "paused"]
|
|
2063
|
+
},
|
|
2064
|
+
payload: InstanceEventPayload
|
|
2065
|
+
});
|
|
2066
|
+
var InstanceResumedEvent = defineEvent3({
|
|
2067
|
+
meta: {
|
|
2068
|
+
key: "workflow.instance.resumed",
|
|
2069
|
+
version: "1.0.0",
|
|
2070
|
+
description: "A workflow instance has been resumed.",
|
|
2071
|
+
stability: "stable",
|
|
2072
|
+
owners: ["@workflow-team"],
|
|
2073
|
+
tags: ["workflow", "instance", "resumed"]
|
|
2074
|
+
},
|
|
2075
|
+
payload: InstanceEventPayload
|
|
2076
|
+
});
|
|
2077
|
+
var InstanceFailedEvent = defineEvent3({
|
|
2078
|
+
meta: {
|
|
2079
|
+
key: "workflow.instance.failed",
|
|
2080
|
+
version: "1.0.0",
|
|
2081
|
+
description: "A workflow instance has failed.",
|
|
2082
|
+
stability: "stable",
|
|
2083
|
+
owners: ["@workflow-team"],
|
|
2084
|
+
tags: ["workflow", "instance", "failed"]
|
|
2085
|
+
},
|
|
2086
|
+
payload: InstanceEventPayload
|
|
2087
|
+
});
|
|
2088
|
+
var InstanceTimedOutEvent = defineEvent3({
|
|
2089
|
+
meta: {
|
|
2090
|
+
key: "workflow.instance.timedOut",
|
|
2091
|
+
version: "1.0.0",
|
|
2092
|
+
description: "A workflow instance has timed out.",
|
|
2093
|
+
stability: "stable",
|
|
2094
|
+
owners: ["@workflow-team"],
|
|
2095
|
+
tags: ["workflow", "instance", "timeout"]
|
|
2096
|
+
},
|
|
2097
|
+
payload: InstanceEventPayload
|
|
2098
|
+
});
|
|
2099
|
+
// src/presentations/index.ts
|
|
2100
|
+
import { definePresentation, StabilityEnum } from "@contractspec/lib.contracts";
|
|
2101
|
+
var WorkflowDesignerPresentation = definePresentation({
|
|
2102
|
+
meta: {
|
|
2103
|
+
key: "workflow.designer",
|
|
2104
|
+
version: "1.0.0",
|
|
2105
|
+
title: "Workflow Designer",
|
|
2106
|
+
description: "Visual workflow designer with drag-and-drop steps",
|
|
2107
|
+
domain: "workflow-system",
|
|
2108
|
+
owners: ["@workflow-team"],
|
|
2109
|
+
tags: ["workflow", "designer", "admin"],
|
|
2110
|
+
stability: StabilityEnum.Experimental,
|
|
2111
|
+
goal: "Building and modifying workflow definitions",
|
|
2112
|
+
context: "Workflow administration and setup"
|
|
2113
|
+
},
|
|
2114
|
+
source: {
|
|
2115
|
+
type: "component",
|
|
2116
|
+
framework: "react",
|
|
2117
|
+
componentKey: "WorkflowDesigner",
|
|
2118
|
+
props: WorkflowDefinitionModel
|
|
2119
|
+
},
|
|
2120
|
+
targets: ["react"],
|
|
2121
|
+
policy: {
|
|
2122
|
+
flags: ["workflow.designer.enabled"]
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
2125
|
+
var WorkflowListPresentation = definePresentation({
|
|
2126
|
+
meta: {
|
|
2127
|
+
key: "workflow.definition.viewList",
|
|
2128
|
+
version: "1.0.0",
|
|
2129
|
+
title: "Workflow List",
|
|
2130
|
+
description: "List of workflow definitions with status and actions",
|
|
2131
|
+
domain: "workflow-system",
|
|
2132
|
+
owners: ["@workflow-team"],
|
|
2133
|
+
tags: ["workflow", "list", "admin"],
|
|
2134
|
+
stability: StabilityEnum.Experimental,
|
|
2135
|
+
goal: "Overview of all defined workflows",
|
|
2136
|
+
context: "Workflow management dashboard"
|
|
2137
|
+
},
|
|
2138
|
+
source: {
|
|
2139
|
+
type: "component",
|
|
2140
|
+
framework: "react",
|
|
2141
|
+
componentKey: "WorkflowDefinitionList",
|
|
2142
|
+
props: WorkflowDefinitionModel
|
|
2143
|
+
},
|
|
2144
|
+
targets: ["react", "markdown"],
|
|
2145
|
+
policy: {
|
|
2146
|
+
flags: ["workflow.enabled"]
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
var WorkflowDetailPresentation = definePresentation({
|
|
2150
|
+
meta: {
|
|
2151
|
+
key: "workflow.definition.detail",
|
|
2152
|
+
version: "1.0.0",
|
|
2153
|
+
title: "Workflow Details",
|
|
2154
|
+
description: "Detailed view of a workflow definition with steps",
|
|
2155
|
+
domain: "workflow-system",
|
|
2156
|
+
owners: ["@workflow-team"],
|
|
2157
|
+
tags: ["workflow", "detail"],
|
|
2158
|
+
stability: StabilityEnum.Experimental,
|
|
2159
|
+
goal: "Viewing technical details of a workflow definition",
|
|
2160
|
+
context: "Workflow inspection and debugging"
|
|
2161
|
+
},
|
|
2162
|
+
source: {
|
|
2163
|
+
type: "component",
|
|
2164
|
+
framework: "react",
|
|
2165
|
+
componentKey: "WorkflowDefinitionDetail",
|
|
2166
|
+
props: WorkflowDefinitionModel
|
|
2167
|
+
},
|
|
2168
|
+
targets: ["react", "markdown"],
|
|
2169
|
+
policy: {
|
|
2170
|
+
flags: ["workflow.enabled"]
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
var InstanceListPresentation = definePresentation({
|
|
2174
|
+
meta: {
|
|
2175
|
+
key: "workflow.instance.viewList",
|
|
2176
|
+
version: "1.0.0",
|
|
2177
|
+
title: "Instance List",
|
|
2178
|
+
description: "List of workflow instances with status and progress",
|
|
2179
|
+
domain: "workflow-system",
|
|
2180
|
+
owners: ["@workflow-team"],
|
|
2181
|
+
tags: ["workflow", "instance", "list"],
|
|
2182
|
+
stability: StabilityEnum.Experimental,
|
|
2183
|
+
goal: "Monitoring active and past workflow executions",
|
|
2184
|
+
context: "Operations monitoring"
|
|
2185
|
+
},
|
|
2186
|
+
source: {
|
|
2187
|
+
type: "component",
|
|
2188
|
+
framework: "react",
|
|
2189
|
+
componentKey: "WorkflowInstanceList",
|
|
2190
|
+
props: WorkflowInstanceModel
|
|
2191
|
+
},
|
|
2192
|
+
targets: ["react", "markdown"],
|
|
2193
|
+
policy: {
|
|
2194
|
+
flags: ["workflow.enabled"]
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
var InstanceDetailPresentation = definePresentation({
|
|
2198
|
+
meta: {
|
|
2199
|
+
key: "workflow.instance.detail",
|
|
2200
|
+
version: "1.0.0",
|
|
2201
|
+
title: "Instance Details",
|
|
2202
|
+
description: "Detailed view of a workflow instance with step timeline",
|
|
2203
|
+
domain: "workflow-system",
|
|
2204
|
+
owners: ["@workflow-team"],
|
|
2205
|
+
tags: ["workflow", "instance", "detail", "timeline"],
|
|
2206
|
+
stability: StabilityEnum.Experimental,
|
|
2207
|
+
goal: "Detailed inspection of a specific workflow instance",
|
|
2208
|
+
context: "Case management and troubleshooting"
|
|
2209
|
+
},
|
|
2210
|
+
source: {
|
|
2211
|
+
type: "component",
|
|
2212
|
+
framework: "react",
|
|
2213
|
+
componentKey: "WorkflowInstanceDetail",
|
|
2214
|
+
props: WorkflowInstanceModel
|
|
2215
|
+
},
|
|
2216
|
+
targets: ["react", "markdown"],
|
|
2217
|
+
policy: {
|
|
2218
|
+
flags: ["workflow.enabled"]
|
|
2219
|
+
}
|
|
2220
|
+
});
|
|
2221
|
+
var ProgressTrackerPresentation = definePresentation({
|
|
2222
|
+
meta: {
|
|
2223
|
+
key: "workflow.instance.progress",
|
|
2224
|
+
version: "1.0.0",
|
|
2225
|
+
title: "Progress Tracker",
|
|
2226
|
+
description: "Visual progress tracker showing current step in workflow",
|
|
2227
|
+
domain: "workflow-system",
|
|
2228
|
+
owners: ["@workflow-team"],
|
|
2229
|
+
tags: ["workflow", "progress", "widget"],
|
|
2230
|
+
stability: StabilityEnum.Experimental,
|
|
2231
|
+
goal: "Quick view of current progress for a workflow",
|
|
2232
|
+
context: "Embedded progress indicator in entity views"
|
|
2233
|
+
},
|
|
2234
|
+
source: {
|
|
2235
|
+
type: "component",
|
|
2236
|
+
framework: "react",
|
|
2237
|
+
componentKey: "WorkflowProgressTracker",
|
|
2238
|
+
props: WorkflowInstanceModel
|
|
2239
|
+
},
|
|
2240
|
+
targets: ["react"],
|
|
2241
|
+
policy: {
|
|
2242
|
+
flags: ["workflow.enabled"]
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
var ApprovalInboxPresentation = definePresentation({
|
|
2246
|
+
meta: {
|
|
2247
|
+
key: "workflow.approval.inbox",
|
|
2248
|
+
version: "1.0.0",
|
|
2249
|
+
title: "Approval Inbox",
|
|
2250
|
+
description: "Inbox showing pending approval requests for current user",
|
|
2251
|
+
domain: "workflow-system",
|
|
2252
|
+
owners: ["@workflow-team"],
|
|
2253
|
+
tags: ["workflow", "approval", "inbox"],
|
|
2254
|
+
stability: StabilityEnum.Experimental,
|
|
2255
|
+
goal: "Managing personal workload of approval requests",
|
|
2256
|
+
context: "Personal task management"
|
|
2257
|
+
},
|
|
2258
|
+
source: {
|
|
2259
|
+
type: "component",
|
|
2260
|
+
framework: "react",
|
|
2261
|
+
componentKey: "ApprovalInbox",
|
|
2262
|
+
props: ApprovalRequestModel
|
|
2263
|
+
},
|
|
2264
|
+
targets: ["react", "markdown"],
|
|
2265
|
+
policy: {
|
|
2266
|
+
flags: ["workflow.approvals.enabled"]
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
var ApprovalDetailPresentation = definePresentation({
|
|
2270
|
+
meta: {
|
|
2271
|
+
key: "workflow.approval.detail",
|
|
2272
|
+
version: "1.0.0",
|
|
2273
|
+
title: "Approval Details",
|
|
2274
|
+
description: "Detailed approval request view with context and actions",
|
|
2275
|
+
domain: "workflow-system",
|
|
2276
|
+
owners: ["@workflow-team"],
|
|
2277
|
+
tags: ["workflow", "approval", "detail"],
|
|
2278
|
+
stability: StabilityEnum.Experimental,
|
|
2279
|
+
goal: "Decision support for an approval request",
|
|
2280
|
+
context: "Specific approval action"
|
|
2281
|
+
},
|
|
2282
|
+
source: {
|
|
2283
|
+
type: "component",
|
|
2284
|
+
framework: "react",
|
|
2285
|
+
componentKey: "ApprovalRequestDetail",
|
|
2286
|
+
props: ApprovalRequestModel
|
|
2287
|
+
},
|
|
2288
|
+
targets: ["react", "markdown"],
|
|
2289
|
+
policy: {
|
|
2290
|
+
flags: ["workflow.approvals.enabled"]
|
|
2291
|
+
}
|
|
2292
|
+
});
|
|
2293
|
+
var ApprovalFormPresentation = definePresentation({
|
|
2294
|
+
meta: {
|
|
2295
|
+
key: "workflow.approval.form",
|
|
2296
|
+
version: "1.0.0",
|
|
2297
|
+
title: "Approval Form",
|
|
2298
|
+
description: "Form for submitting approval decisions",
|
|
2299
|
+
domain: "workflow-system",
|
|
2300
|
+
owners: ["@workflow-team"],
|
|
2301
|
+
tags: ["workflow", "approval", "form"],
|
|
2302
|
+
stability: StabilityEnum.Experimental,
|
|
2303
|
+
goal: "Submitting a decision on an approval request",
|
|
2304
|
+
context: "Approval decision dialog"
|
|
2305
|
+
},
|
|
2306
|
+
source: {
|
|
2307
|
+
type: "component",
|
|
2308
|
+
framework: "react",
|
|
2309
|
+
componentKey: "ApprovalDecisionForm"
|
|
2310
|
+
},
|
|
2311
|
+
targets: ["react"],
|
|
2312
|
+
policy: {
|
|
2313
|
+
flags: ["workflow.approvals.enabled"]
|
|
2314
|
+
}
|
|
2315
|
+
});
|
|
2316
|
+
var PendingApprovalsBadgePresentation = definePresentation({
|
|
2317
|
+
meta: {
|
|
2318
|
+
key: "workflow.approval.badge",
|
|
2319
|
+
version: "1.0.0",
|
|
2320
|
+
title: "Pending Approvals Badge",
|
|
2321
|
+
description: "Badge showing count of pending approvals",
|
|
2322
|
+
domain: "workflow-system",
|
|
2323
|
+
owners: ["@workflow-team"],
|
|
2324
|
+
tags: ["workflow", "approval", "badge", "widget"],
|
|
2325
|
+
stability: StabilityEnum.Experimental,
|
|
2326
|
+
goal: "Visual notification of pending approvals",
|
|
2327
|
+
context: "Global navigation or sidebar"
|
|
2328
|
+
},
|
|
2329
|
+
source: {
|
|
2330
|
+
type: "component",
|
|
2331
|
+
framework: "react",
|
|
2332
|
+
componentKey: "PendingApprovalsBadge"
|
|
2333
|
+
},
|
|
2334
|
+
targets: ["react"],
|
|
2335
|
+
policy: {
|
|
2336
|
+
flags: ["workflow.approvals.enabled"]
|
|
2337
|
+
}
|
|
2338
|
+
});
|
|
2339
|
+
var WorkflowMetricsPresentation = definePresentation({
|
|
2340
|
+
meta: {
|
|
2341
|
+
key: "workflow.metrics",
|
|
2342
|
+
version: "1.0.0",
|
|
2343
|
+
title: "Workflow Metrics",
|
|
2344
|
+
description: "Dashboard widget showing workflow metrics and statistics",
|
|
2345
|
+
domain: "workflow-system",
|
|
2346
|
+
owners: ["@workflow-team"],
|
|
2347
|
+
tags: ["workflow", "metrics", "dashboard"],
|
|
2348
|
+
stability: StabilityEnum.Experimental,
|
|
2349
|
+
goal: "Monitoring throughput and bottlenecks",
|
|
2350
|
+
context: "System performance dashboard"
|
|
2351
|
+
},
|
|
2352
|
+
source: {
|
|
2353
|
+
type: "component",
|
|
2354
|
+
framework: "react",
|
|
2355
|
+
componentKey: "WorkflowMetricsDashboard"
|
|
2356
|
+
},
|
|
2357
|
+
targets: ["react", "markdown"],
|
|
2358
|
+
policy: {
|
|
2359
|
+
flags: ["workflow.metrics.enabled"]
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
var WorkflowSystemPresentations = {
|
|
2363
|
+
WorkflowDesignerPresentation,
|
|
2364
|
+
WorkflowListPresentation,
|
|
2365
|
+
WorkflowDetailPresentation,
|
|
2366
|
+
InstanceListPresentation,
|
|
2367
|
+
InstanceDetailPresentation,
|
|
2368
|
+
ProgressTrackerPresentation,
|
|
2369
|
+
ApprovalInboxPresentation,
|
|
2370
|
+
ApprovalDetailPresentation,
|
|
2371
|
+
ApprovalFormPresentation,
|
|
2372
|
+
PendingApprovalsBadgePresentation,
|
|
2373
|
+
WorkflowMetricsPresentation
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
// src/workflow-system.feature.ts
|
|
2377
|
+
import { defineFeature } from "@contractspec/lib.contracts";
|
|
2378
|
+
var WorkflowSystemFeature = defineFeature({
|
|
2379
|
+
meta: {
|
|
2380
|
+
key: "workflow-system",
|
|
2381
|
+
title: "Workflow & Approval System",
|
|
2382
|
+
description: "State machine-based workflow engine with role-based approvals, delegation, and escalation",
|
|
2383
|
+
domain: "workflow",
|
|
2384
|
+
owners: ["@workflow-team"],
|
|
2385
|
+
tags: ["workflow", "approval", "state-machine", "automation"],
|
|
2386
|
+
stability: "experimental",
|
|
2387
|
+
version: "1.0.0"
|
|
2388
|
+
},
|
|
2389
|
+
operations: [
|
|
2390
|
+
{ key: "workflow.definition.create", version: "1.0.0" },
|
|
2391
|
+
{ key: "workflow.definition.update", version: "1.0.0" },
|
|
2392
|
+
{ key: "workflow.step.add", version: "1.0.0" },
|
|
2393
|
+
{ key: "workflow.definition.publish", version: "1.0.0" },
|
|
2394
|
+
{ key: "workflow.definition.list", version: "1.0.0" },
|
|
2395
|
+
{ key: "workflow.definition.get", version: "1.0.0" },
|
|
2396
|
+
{ key: "workflow.instance.start", version: "1.0.0" },
|
|
2397
|
+
{ key: "workflow.instance.transition", version: "1.0.0" },
|
|
2398
|
+
{ key: "workflow.instance.pause", version: "1.0.0" },
|
|
2399
|
+
{ key: "workflow.instance.resume", version: "1.0.0" },
|
|
2400
|
+
{ key: "workflow.instance.cancel", version: "1.0.0" },
|
|
2401
|
+
{ key: "workflow.instance.list", version: "1.0.0" },
|
|
2402
|
+
{ key: "workflow.instance.get", version: "1.0.0" },
|
|
2403
|
+
{ key: "workflow.approval.decide", version: "1.0.0" },
|
|
2404
|
+
{ key: "workflow.approval.delegate", version: "1.0.0" },
|
|
2405
|
+
{ key: "workflow.approval.comment.add", version: "1.0.0" },
|
|
2406
|
+
{ key: "workflow.approval.list.mine", version: "1.0.0" },
|
|
2407
|
+
{ key: "workflow.approval.get", version: "1.0.0" }
|
|
2408
|
+
],
|
|
2409
|
+
events: [
|
|
2410
|
+
{ key: "workflow.definition.created", version: "1.0.0" },
|
|
2411
|
+
{ key: "workflow.definition.updated", version: "1.0.0" },
|
|
2412
|
+
{ key: "workflow.definition.published", version: "1.0.0" },
|
|
2413
|
+
{ key: "workflow.step.added", version: "1.0.0" },
|
|
2414
|
+
{ key: "workflow.instance.started", version: "1.0.0" },
|
|
2415
|
+
{ key: "workflow.step.entered", version: "1.0.0" },
|
|
2416
|
+
{ key: "workflow.step.exited", version: "1.0.0" },
|
|
2417
|
+
{ key: "workflow.instance.completed", version: "1.0.0" },
|
|
2418
|
+
{ key: "workflow.instance.cancelled", version: "1.0.0" },
|
|
2419
|
+
{ key: "workflow.instance.paused", version: "1.0.0" },
|
|
2420
|
+
{ key: "workflow.instance.resumed", version: "1.0.0" },
|
|
2421
|
+
{ key: "workflow.instance.failed", version: "1.0.0" },
|
|
2422
|
+
{ key: "workflow.instance.timedOut", version: "1.0.0" },
|
|
2423
|
+
{ key: "workflow.approval.requested", version: "1.0.0" },
|
|
2424
|
+
{ key: "workflow.approval.decided", version: "1.0.0" },
|
|
2425
|
+
{ key: "workflow.approval.delegated", version: "1.0.0" },
|
|
2426
|
+
{ key: "workflow.approval.escalated", version: "1.0.0" }
|
|
2427
|
+
],
|
|
2428
|
+
presentations: [
|
|
2429
|
+
{ key: "workflow.designer", version: "1.0.0" },
|
|
2430
|
+
{ key: "workflow.definition.viewList", version: "1.0.0" },
|
|
2431
|
+
{ key: "workflow.definition.detail", version: "1.0.0" },
|
|
2432
|
+
{ key: "workflow.instance.viewList", version: "1.0.0" },
|
|
2433
|
+
{ key: "workflow.instance.detail", version: "1.0.0" },
|
|
2434
|
+
{ key: "workflow.instance.progress", version: "1.0.0" },
|
|
2435
|
+
{ key: "workflow.approval.inbox", version: "1.0.0" },
|
|
2436
|
+
{ key: "workflow.approval.detail", version: "1.0.0" },
|
|
2437
|
+
{ key: "workflow.approval.form", version: "1.0.0" },
|
|
2438
|
+
{ key: "workflow.approval.badge", version: "1.0.0" },
|
|
2439
|
+
{ key: "workflow.metrics", version: "1.0.0" }
|
|
2440
|
+
],
|
|
2441
|
+
opToPresentation: [
|
|
2442
|
+
{
|
|
2443
|
+
op: { key: "workflow.definition.list", version: "1.0.0" },
|
|
2444
|
+
pres: { key: "workflow.definition.viewList", version: "1.0.0" }
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
op: { key: "workflow.instance.list", version: "1.0.0" },
|
|
2448
|
+
pres: { key: "workflow.instance.viewList", version: "1.0.0" }
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
op: { key: "workflow.approval.list.mine", version: "1.0.0" },
|
|
2452
|
+
pres: { key: "workflow.approval.inbox", version: "1.0.0" }
|
|
2453
|
+
},
|
|
2454
|
+
{
|
|
2455
|
+
op: { key: "workflow.approval.decide", version: "1.0.0" },
|
|
2456
|
+
pres: { key: "workflow.approval.form", version: "1.0.0" }
|
|
2457
|
+
}
|
|
2458
|
+
],
|
|
2459
|
+
presentationsTargets: [
|
|
2460
|
+
{ key: "workflow.designer", version: "1.0.0", targets: ["react"] },
|
|
2461
|
+
{
|
|
2462
|
+
key: "workflow.definition.viewList",
|
|
2463
|
+
version: "1.0.0",
|
|
2464
|
+
targets: ["react", "markdown"]
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
key: "workflow.definition.detail",
|
|
2468
|
+
version: "1.0.0",
|
|
2469
|
+
targets: ["react", "markdown"]
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
key: "workflow.instance.viewList",
|
|
2473
|
+
version: "1.0.0",
|
|
2474
|
+
targets: ["react", "markdown"]
|
|
2475
|
+
},
|
|
2476
|
+
{
|
|
2477
|
+
key: "workflow.instance.detail",
|
|
2478
|
+
version: "1.0.0",
|
|
2479
|
+
targets: ["react", "markdown"]
|
|
2480
|
+
},
|
|
2481
|
+
{ key: "workflow.instance.progress", version: "1.0.0", targets: ["react"] },
|
|
2482
|
+
{
|
|
2483
|
+
key: "workflow.approval.inbox",
|
|
2484
|
+
version: "1.0.0",
|
|
2485
|
+
targets: ["react", "markdown"]
|
|
2486
|
+
},
|
|
2487
|
+
{
|
|
2488
|
+
key: "workflow.approval.detail",
|
|
2489
|
+
version: "1.0.0",
|
|
2490
|
+
targets: ["react", "markdown"]
|
|
2491
|
+
},
|
|
2492
|
+
{
|
|
2493
|
+
key: "workflow.metrics",
|
|
2494
|
+
version: "1.0.0",
|
|
2495
|
+
targets: ["react", "markdown"]
|
|
2496
|
+
}
|
|
2497
|
+
],
|
|
2498
|
+
capabilities: {
|
|
2499
|
+
requires: [
|
|
2500
|
+
{ key: "identity", version: "1.0.0" },
|
|
2501
|
+
{ key: "audit-trail", version: "1.0.0" },
|
|
2502
|
+
{ key: "notifications", version: "1.0.0" },
|
|
2503
|
+
{ key: "feature-flags", version: "1.0.0" }
|
|
2504
|
+
],
|
|
2505
|
+
provides: [
|
|
2506
|
+
{ key: "workflow", version: "1.0.0" },
|
|
2507
|
+
{ key: "approval", version: "1.0.0" },
|
|
2508
|
+
{ key: "state-machine", version: "1.0.0" }
|
|
2509
|
+
]
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
// src/ui/renderers/workflow.markdown.ts
|
|
2514
|
+
var mockWorkflowDefinitions = [
|
|
2515
|
+
{
|
|
2516
|
+
id: "wf-1",
|
|
2517
|
+
name: "Purchase Approval",
|
|
2518
|
+
type: "APPROVAL",
|
|
2519
|
+
steps: [
|
|
2520
|
+
{
|
|
2521
|
+
id: "s1",
|
|
2522
|
+
name: "Manager Review",
|
|
2523
|
+
order: 1,
|
|
2524
|
+
requiredRoles: ["manager"]
|
|
2525
|
+
},
|
|
2526
|
+
{
|
|
2527
|
+
id: "s2",
|
|
2528
|
+
name: "Finance Review",
|
|
2529
|
+
order: 2,
|
|
2530
|
+
requiredRoles: ["finance"]
|
|
2531
|
+
},
|
|
2532
|
+
{ id: "s3", name: "Final Approval", order: 3, requiredRoles: ["admin"] }
|
|
2533
|
+
],
|
|
2534
|
+
status: "ACTIVE"
|
|
2535
|
+
},
|
|
2536
|
+
{
|
|
2537
|
+
id: "wf-2",
|
|
2538
|
+
name: "Leave Request",
|
|
2539
|
+
type: "APPROVAL",
|
|
2540
|
+
steps: [
|
|
2541
|
+
{
|
|
2542
|
+
id: "s1",
|
|
2543
|
+
name: "Supervisor Approval",
|
|
2544
|
+
order: 1,
|
|
2545
|
+
requiredRoles: ["supervisor"]
|
|
2546
|
+
},
|
|
2547
|
+
{ id: "s2", name: "HR Review", order: 2, requiredRoles: ["hr"] }
|
|
2548
|
+
],
|
|
2549
|
+
status: "ACTIVE"
|
|
2550
|
+
},
|
|
2551
|
+
{
|
|
2552
|
+
id: "wf-3",
|
|
2553
|
+
name: "Document Review",
|
|
2554
|
+
type: "SEQUENTIAL",
|
|
2555
|
+
steps: [
|
|
2556
|
+
{ id: "s1", name: "Author Review", order: 1, requiredRoles: ["author"] },
|
|
2557
|
+
{ id: "s2", name: "Peer Review", order: 2, requiredRoles: ["reviewer"] },
|
|
2558
|
+
{ id: "s3", name: "Publish", order: 3, requiredRoles: ["publisher"] }
|
|
2559
|
+
],
|
|
2560
|
+
status: "DRAFT"
|
|
2561
|
+
}
|
|
2562
|
+
];
|
|
2563
|
+
var mockWorkflowInstances = [
|
|
2564
|
+
{
|
|
2565
|
+
id: "inst-1",
|
|
2566
|
+
definitionId: "wf-1",
|
|
2567
|
+
definitionName: "Purchase Approval",
|
|
2568
|
+
status: "IN_PROGRESS",
|
|
2569
|
+
currentStepId: "s2",
|
|
2570
|
+
startedAt: "2024-01-15T10:00:00Z",
|
|
2571
|
+
requestedBy: "John Doe"
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
id: "inst-2",
|
|
2575
|
+
definitionId: "wf-1",
|
|
2576
|
+
definitionName: "Purchase Approval",
|
|
2577
|
+
status: "COMPLETED",
|
|
2578
|
+
currentStepId: null,
|
|
2579
|
+
startedAt: "2024-01-10T09:00:00Z",
|
|
2580
|
+
completedAt: "2024-01-12T14:00:00Z",
|
|
2581
|
+
requestedBy: "Jane Smith"
|
|
2582
|
+
},
|
|
2583
|
+
{
|
|
2584
|
+
id: "inst-3",
|
|
2585
|
+
definitionId: "wf-2",
|
|
2586
|
+
definitionName: "Leave Request",
|
|
2587
|
+
status: "PENDING",
|
|
2588
|
+
currentStepId: "s1",
|
|
2589
|
+
startedAt: "2024-01-16T08:00:00Z",
|
|
2590
|
+
requestedBy: "Bob Wilson"
|
|
2591
|
+
}
|
|
2592
|
+
];
|
|
2593
|
+
var workflowDashboardMarkdownRenderer = {
|
|
2594
|
+
target: "markdown",
|
|
2595
|
+
render: async (desc) => {
|
|
2596
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowDashboard") {
|
|
2597
|
+
throw new Error("workflowDashboardMarkdownRenderer: not WorkflowDashboard");
|
|
2598
|
+
}
|
|
2599
|
+
const definitions = mockWorkflowDefinitions;
|
|
2600
|
+
const instances = mockWorkflowInstances;
|
|
2601
|
+
const activeDefinitions = definitions.filter((d) => d.status === "ACTIVE");
|
|
2602
|
+
const pendingInstances = instances.filter((i) => i.status === "PENDING");
|
|
2603
|
+
const inProgressInstances = instances.filter((i) => i.status === "IN_PROGRESS");
|
|
2604
|
+
const completedInstances = instances.filter((i) => i.status === "COMPLETED");
|
|
2605
|
+
const lines = [
|
|
2606
|
+
"# Workflow Dashboard",
|
|
2607
|
+
"",
|
|
2608
|
+
"> Workflow and approval management overview",
|
|
2609
|
+
"",
|
|
2610
|
+
"## Summary",
|
|
2611
|
+
"",
|
|
2612
|
+
"| Metric | Value |",
|
|
2613
|
+
"|--------|-------|",
|
|
2614
|
+
`| Active Workflows | ${activeDefinitions.length} |`,
|
|
2615
|
+
`| Pending Approvals | ${pendingInstances.length} |`,
|
|
2616
|
+
`| In Progress | ${inProgressInstances.length} |`,
|
|
2617
|
+
`| Completed | ${completedInstances.length} |`,
|
|
2618
|
+
"",
|
|
2619
|
+
"## Active Workflow Definitions",
|
|
2620
|
+
""
|
|
2621
|
+
];
|
|
2622
|
+
if (activeDefinitions.length === 0) {
|
|
2623
|
+
lines.push("_No active workflow definitions._");
|
|
2624
|
+
} else {
|
|
2625
|
+
lines.push("| Name | Type | Steps | Status |");
|
|
2626
|
+
lines.push("|------|------|-------|--------|");
|
|
2627
|
+
for (const def of activeDefinitions) {
|
|
2628
|
+
lines.push(`| ${def.name} | ${def.type} | ${def.steps.length} | ${def.status} |`);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
lines.push("");
|
|
2632
|
+
lines.push("## Recent Instances");
|
|
2633
|
+
lines.push("");
|
|
2634
|
+
if (instances.length === 0) {
|
|
2635
|
+
lines.push("_No workflow instances._");
|
|
2636
|
+
} else {
|
|
2637
|
+
lines.push("| Workflow | Requested By | Status | Started |");
|
|
2638
|
+
lines.push("|----------|--------------|--------|---------|");
|
|
2639
|
+
for (const inst of instances.slice(0, 10)) {
|
|
2640
|
+
const startedDate = new Date(inst.startedAt).toLocaleDateString();
|
|
2641
|
+
lines.push(`| ${inst.definitionName} | ${inst.requestedBy} | ${inst.status} | ${startedDate} |`);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return {
|
|
2645
|
+
mimeType: "text/markdown",
|
|
2646
|
+
body: lines.join(`
|
|
2647
|
+
`)
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
};
|
|
2651
|
+
var workflowDefinitionListMarkdownRenderer = {
|
|
2652
|
+
target: "markdown",
|
|
2653
|
+
render: async (desc) => {
|
|
2654
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowDefinitionList") {
|
|
2655
|
+
throw new Error("workflowDefinitionListMarkdownRenderer: not WorkflowDefinitionList");
|
|
2656
|
+
}
|
|
2657
|
+
const definitions = mockWorkflowDefinitions;
|
|
2658
|
+
const lines = [
|
|
2659
|
+
"# Workflow Definitions",
|
|
2660
|
+
"",
|
|
2661
|
+
"> Configure automated approval and process workflows",
|
|
2662
|
+
""
|
|
2663
|
+
];
|
|
2664
|
+
for (const def of definitions) {
|
|
2665
|
+
lines.push(`## ${def.name}`);
|
|
2666
|
+
lines.push("");
|
|
2667
|
+
lines.push(`**Type:** ${def.type} | **Status:** ${def.status}`);
|
|
2668
|
+
lines.push("");
|
|
2669
|
+
lines.push("### Steps");
|
|
2670
|
+
lines.push("");
|
|
2671
|
+
for (const step of def.steps) {
|
|
2672
|
+
lines.push(`${step.order}. **${step.name}** - Roles: ${step.requiredRoles.join(", ")}`);
|
|
2673
|
+
}
|
|
2674
|
+
lines.push("");
|
|
2675
|
+
}
|
|
2676
|
+
return {
|
|
2677
|
+
mimeType: "text/markdown",
|
|
2678
|
+
body: lines.join(`
|
|
2679
|
+
`)
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
var workflowInstanceDetailMarkdownRenderer = {
|
|
2684
|
+
target: "markdown",
|
|
2685
|
+
render: async (desc) => {
|
|
2686
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "WorkflowInstanceDetail") {
|
|
2687
|
+
throw new Error("workflowInstanceDetailMarkdownRenderer: not WorkflowInstanceDetail");
|
|
2688
|
+
}
|
|
2689
|
+
const instance = mockWorkflowInstances[0];
|
|
2690
|
+
if (!instance) {
|
|
2691
|
+
return {
|
|
2692
|
+
mimeType: "text/markdown",
|
|
2693
|
+
body: `# No Workflow Instances
|
|
2694
|
+
|
|
2695
|
+
No workflow instances available.`
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
const definition = mockWorkflowDefinitions.find((d) => d.id === instance.definitionId);
|
|
2699
|
+
const lines = [
|
|
2700
|
+
`# Workflow: ${instance.definitionName}`,
|
|
2701
|
+
"",
|
|
2702
|
+
`**Instance ID:** ${instance.id}`,
|
|
2703
|
+
`**Status:** ${instance.status}`,
|
|
2704
|
+
`**Requested By:** ${instance.requestedBy}`,
|
|
2705
|
+
`**Started:** ${new Date(instance.startedAt).toLocaleString()}`,
|
|
2706
|
+
"",
|
|
2707
|
+
"## Steps Progress",
|
|
2708
|
+
""
|
|
2709
|
+
];
|
|
2710
|
+
if (definition) {
|
|
2711
|
+
for (const step of definition.steps) {
|
|
2712
|
+
const isCurrent = step.id === instance.currentStepId;
|
|
2713
|
+
const isCompleted = definition.steps.indexOf(step) < definition.steps.findIndex((s) => s.id === instance.currentStepId);
|
|
2714
|
+
let status = "⬜ Pending";
|
|
2715
|
+
if (isCompleted)
|
|
2716
|
+
status = "✅ Completed";
|
|
2717
|
+
if (isCurrent)
|
|
2718
|
+
status = "\uD83D\uDD04 In Progress";
|
|
2719
|
+
lines.push(`- ${status} **${step.name}**`);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
lines.push("");
|
|
2723
|
+
lines.push("## Actions");
|
|
2724
|
+
lines.push("");
|
|
2725
|
+
lines.push("- **Approve** - Move to next step");
|
|
2726
|
+
lines.push("- **Reject** - Reject and return");
|
|
2727
|
+
lines.push("- **Delegate** - Assign to another approver");
|
|
2728
|
+
return {
|
|
2729
|
+
mimeType: "text/markdown",
|
|
2730
|
+
body: lines.join(`
|
|
2731
|
+
`)
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
// src/ui/hooks/useWorkflowList.ts
|
|
2736
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2737
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
2738
|
+
"use client";
|
|
2739
|
+
function useWorkflowList(projectId = "local-project") {
|
|
2740
|
+
const { handlers } = useTemplateRuntime();
|
|
2741
|
+
const workflow = handlers.workflow;
|
|
2742
|
+
const [definitions, setDefinitions] = useState([]);
|
|
2743
|
+
const [instances, setInstances] = useState([]);
|
|
2744
|
+
const [loading, setLoading] = useState(true);
|
|
2745
|
+
const [error, setError] = useState(null);
|
|
2746
|
+
const fetchData = useCallback(async () => {
|
|
2747
|
+
try {
|
|
2748
|
+
setLoading(true);
|
|
2749
|
+
setError(null);
|
|
2750
|
+
const [defResult, instResult] = await Promise.all([
|
|
2751
|
+
workflow.listDefinitions({ projectId, limit: 100 }),
|
|
2752
|
+
workflow.listInstances({ projectId, limit: 100 })
|
|
2753
|
+
]);
|
|
2754
|
+
setDefinitions(defResult.definitions);
|
|
2755
|
+
setInstances(instResult.instances);
|
|
2756
|
+
} catch (err) {
|
|
2757
|
+
setError(err instanceof Error ? err : new Error("Failed to load workflows"));
|
|
2758
|
+
} finally {
|
|
2759
|
+
setLoading(false);
|
|
2760
|
+
}
|
|
2761
|
+
}, [handlers, projectId]);
|
|
2762
|
+
useEffect(() => {
|
|
2763
|
+
fetchData();
|
|
2764
|
+
}, [fetchData]);
|
|
2765
|
+
const stats = {
|
|
2766
|
+
totalDefinitions: definitions.length,
|
|
2767
|
+
activeDefinitions: definitions.filter((d) => d.status === "ACTIVE").length,
|
|
2768
|
+
totalInstances: instances.length,
|
|
2769
|
+
pendingInstances: instances.filter((i) => i.status === "PENDING").length,
|
|
2770
|
+
completedInstances: instances.filter((i) => i.status === "COMPLETED").length,
|
|
2771
|
+
rejectedInstances: instances.filter((i) => i.status === "REJECTED").length
|
|
2772
|
+
};
|
|
2773
|
+
return {
|
|
2774
|
+
definitions,
|
|
2775
|
+
instances,
|
|
2776
|
+
loading,
|
|
2777
|
+
error,
|
|
2778
|
+
stats,
|
|
2779
|
+
refetch: fetchData
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
// src/ui/WorkflowDashboard.tsx
|
|
2784
|
+
import { useState as useState2 } from "react";
|
|
2785
|
+
import {
|
|
2786
|
+
Button,
|
|
2787
|
+
ErrorState,
|
|
2788
|
+
LoaderBlock,
|
|
2789
|
+
StatCard,
|
|
2790
|
+
StatCardGroup
|
|
2791
|
+
} from "@contractspec/lib.design-system";
|
|
2792
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
2793
|
+
"use client";
|
|
2794
|
+
var STATUS_COLORS = {
|
|
2795
|
+
ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
2796
|
+
DRAFT: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400",
|
|
2797
|
+
ARCHIVED: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400",
|
|
2798
|
+
PENDING: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
|
|
2799
|
+
IN_PROGRESS: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
|
|
2800
|
+
COMPLETED: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
|
2801
|
+
REJECTED: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400",
|
|
2802
|
+
CANCELLED: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
|
|
2803
|
+
};
|
|
2804
|
+
function WorkflowDashboard() {
|
|
2805
|
+
const [activeTab, setActiveTab] = useState2("definitions");
|
|
2806
|
+
const { definitions, instances, loading, error, stats, refetch } = useWorkflowList();
|
|
2807
|
+
const tabs = [
|
|
2808
|
+
{ id: "definitions", label: "Definitions", icon: "\uD83D\uDCCB" },
|
|
2809
|
+
{ id: "instances", label: "Instances", icon: "\uD83D\uDD04" }
|
|
2810
|
+
];
|
|
2811
|
+
if (loading) {
|
|
2812
|
+
return /* @__PURE__ */ jsxDEV(LoaderBlock, {
|
|
2813
|
+
label: "Loading Workflows..."
|
|
2814
|
+
}, undefined, false, undefined, this);
|
|
2815
|
+
}
|
|
2816
|
+
if (error) {
|
|
2817
|
+
return /* @__PURE__ */ jsxDEV(ErrorState, {
|
|
2818
|
+
title: "Failed to load Workflows",
|
|
2819
|
+
description: error.message,
|
|
2820
|
+
onRetry: refetch,
|
|
2821
|
+
retryLabel: "Retry"
|
|
2822
|
+
}, undefined, false, undefined, this);
|
|
2823
|
+
}
|
|
2824
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
2825
|
+
className: "space-y-6",
|
|
2826
|
+
children: [
|
|
2827
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
2828
|
+
className: "flex items-center justify-between",
|
|
2829
|
+
children: [
|
|
2830
|
+
/* @__PURE__ */ jsxDEV("h2", {
|
|
2831
|
+
className: "text-2xl font-bold",
|
|
2832
|
+
children: "Workflow System"
|
|
2833
|
+
}, undefined, false, undefined, this),
|
|
2834
|
+
/* @__PURE__ */ jsxDEV(Button, {
|
|
2835
|
+
onClick: () => alert("Create workflow modal"),
|
|
2836
|
+
children: [
|
|
2837
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
2838
|
+
className: "mr-2",
|
|
2839
|
+
children: "+"
|
|
2840
|
+
}, undefined, false, undefined, this),
|
|
2841
|
+
" New Workflow"
|
|
2842
|
+
]
|
|
2843
|
+
}, undefined, true, undefined, this)
|
|
2844
|
+
]
|
|
2845
|
+
}, undefined, true, undefined, this),
|
|
2846
|
+
/* @__PURE__ */ jsxDEV(StatCardGroup, {
|
|
2847
|
+
children: [
|
|
2848
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
2849
|
+
label: "Workflows",
|
|
2850
|
+
value: stats.totalDefinitions,
|
|
2851
|
+
hint: `${stats.activeDefinitions} active`
|
|
2852
|
+
}, undefined, false, undefined, this),
|
|
2853
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
2854
|
+
label: "Instances",
|
|
2855
|
+
value: stats.totalInstances,
|
|
2856
|
+
hint: "total runs"
|
|
2857
|
+
}, undefined, false, undefined, this),
|
|
2858
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
2859
|
+
label: "Pending",
|
|
2860
|
+
value: stats.pendingInstances,
|
|
2861
|
+
hint: "awaiting action"
|
|
2862
|
+
}, undefined, false, undefined, this),
|
|
2863
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
2864
|
+
label: "Completed",
|
|
2865
|
+
value: stats.completedInstances,
|
|
2866
|
+
hint: "finished"
|
|
2867
|
+
}, undefined, false, undefined, this)
|
|
2868
|
+
]
|
|
2869
|
+
}, undefined, true, undefined, this),
|
|
2870
|
+
/* @__PURE__ */ jsxDEV("nav", {
|
|
2871
|
+
className: "bg-muted flex gap-1 rounded-lg p-1",
|
|
2872
|
+
role: "tablist",
|
|
2873
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxDEV(Button, {
|
|
2874
|
+
type: "button",
|
|
2875
|
+
role: "tab",
|
|
2876
|
+
"aria-selected": activeTab === tab.id,
|
|
2877
|
+
onClick: () => setActiveTab(tab.id),
|
|
2878
|
+
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
2879
|
+
children: [
|
|
2880
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
2881
|
+
children: tab.icon
|
|
2882
|
+
}, undefined, false, undefined, this),
|
|
2883
|
+
tab.label
|
|
2884
|
+
]
|
|
2885
|
+
}, tab.id, true, undefined, this))
|
|
2886
|
+
}, undefined, false, undefined, this),
|
|
2887
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
2888
|
+
className: "min-h-[400px]",
|
|
2889
|
+
role: "tabpanel",
|
|
2890
|
+
children: [
|
|
2891
|
+
activeTab === "definitions" && /* @__PURE__ */ jsxDEV("div", {
|
|
2892
|
+
className: "border-border rounded-lg border",
|
|
2893
|
+
children: /* @__PURE__ */ jsxDEV("table", {
|
|
2894
|
+
className: "w-full",
|
|
2895
|
+
children: [
|
|
2896
|
+
/* @__PURE__ */ jsxDEV("thead", {
|
|
2897
|
+
className: "border-border bg-muted/30 border-b",
|
|
2898
|
+
children: /* @__PURE__ */ jsxDEV("tr", {
|
|
2899
|
+
children: [
|
|
2900
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2901
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2902
|
+
children: "Name"
|
|
2903
|
+
}, undefined, false, undefined, this),
|
|
2904
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2905
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2906
|
+
children: "Type"
|
|
2907
|
+
}, undefined, false, undefined, this),
|
|
2908
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2909
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2910
|
+
children: "Status"
|
|
2911
|
+
}, undefined, false, undefined, this),
|
|
2912
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2913
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2914
|
+
children: "Created"
|
|
2915
|
+
}, undefined, false, undefined, this)
|
|
2916
|
+
]
|
|
2917
|
+
}, undefined, true, undefined, this)
|
|
2918
|
+
}, undefined, false, undefined, this),
|
|
2919
|
+
/* @__PURE__ */ jsxDEV("tbody", {
|
|
2920
|
+
className: "divide-border divide-y",
|
|
2921
|
+
children: [
|
|
2922
|
+
definitions.map((def) => /* @__PURE__ */ jsxDEV("tr", {
|
|
2923
|
+
className: "hover:bg-muted/50",
|
|
2924
|
+
children: [
|
|
2925
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
2926
|
+
className: "px-4 py-3",
|
|
2927
|
+
children: [
|
|
2928
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
2929
|
+
className: "font-medium",
|
|
2930
|
+
children: def.name
|
|
2931
|
+
}, undefined, false, undefined, this),
|
|
2932
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
2933
|
+
className: "text-muted-foreground text-sm",
|
|
2934
|
+
children: def.description
|
|
2935
|
+
}, undefined, false, undefined, this)
|
|
2936
|
+
]
|
|
2937
|
+
}, undefined, true, undefined, this),
|
|
2938
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
2939
|
+
className: "px-4 py-3 font-mono text-sm",
|
|
2940
|
+
children: def.type
|
|
2941
|
+
}, undefined, false, undefined, this),
|
|
2942
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
2943
|
+
className: "px-4 py-3",
|
|
2944
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
2945
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[def.status] ?? ""}`,
|
|
2946
|
+
children: def.status
|
|
2947
|
+
}, undefined, false, undefined, this)
|
|
2948
|
+
}, undefined, false, undefined, this),
|
|
2949
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
2950
|
+
className: "text-muted-foreground px-4 py-3 text-sm",
|
|
2951
|
+
children: def.createdAt.toLocaleDateString()
|
|
2952
|
+
}, undefined, false, undefined, this)
|
|
2953
|
+
]
|
|
2954
|
+
}, def.id, true, undefined, this)),
|
|
2955
|
+
definitions.length === 0 && /* @__PURE__ */ jsxDEV("tr", {
|
|
2956
|
+
children: /* @__PURE__ */ jsxDEV("td", {
|
|
2957
|
+
colSpan: 4,
|
|
2958
|
+
className: "text-muted-foreground px-4 py-8 text-center",
|
|
2959
|
+
children: "No workflow definitions found"
|
|
2960
|
+
}, undefined, false, undefined, this)
|
|
2961
|
+
}, undefined, false, undefined, this)
|
|
2962
|
+
]
|
|
2963
|
+
}, undefined, true, undefined, this)
|
|
2964
|
+
]
|
|
2965
|
+
}, undefined, true, undefined, this)
|
|
2966
|
+
}, undefined, false, undefined, this),
|
|
2967
|
+
activeTab === "instances" && /* @__PURE__ */ jsxDEV("div", {
|
|
2968
|
+
className: "border-border rounded-lg border",
|
|
2969
|
+
children: /* @__PURE__ */ jsxDEV("table", {
|
|
2970
|
+
className: "w-full",
|
|
2971
|
+
children: [
|
|
2972
|
+
/* @__PURE__ */ jsxDEV("thead", {
|
|
2973
|
+
className: "border-border bg-muted/30 border-b",
|
|
2974
|
+
children: /* @__PURE__ */ jsxDEV("tr", {
|
|
2975
|
+
children: [
|
|
2976
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2977
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2978
|
+
children: "Instance ID"
|
|
2979
|
+
}, undefined, false, undefined, this),
|
|
2980
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2981
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2982
|
+
children: "Status"
|
|
2983
|
+
}, undefined, false, undefined, this),
|
|
2984
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2985
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2986
|
+
children: "Requested By"
|
|
2987
|
+
}, undefined, false, undefined, this),
|
|
2988
|
+
/* @__PURE__ */ jsxDEV("th", {
|
|
2989
|
+
className: "px-4 py-3 text-left text-sm font-medium",
|
|
2990
|
+
children: "Started"
|
|
2991
|
+
}, undefined, false, undefined, this)
|
|
2992
|
+
]
|
|
2993
|
+
}, undefined, true, undefined, this)
|
|
2994
|
+
}, undefined, false, undefined, this),
|
|
2995
|
+
/* @__PURE__ */ jsxDEV("tbody", {
|
|
2996
|
+
className: "divide-border divide-y",
|
|
2997
|
+
children: [
|
|
2998
|
+
instances.map((inst) => /* @__PURE__ */ jsxDEV("tr", {
|
|
2999
|
+
className: "hover:bg-muted/50",
|
|
3000
|
+
children: [
|
|
3001
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
3002
|
+
className: "px-4 py-3 font-mono text-sm",
|
|
3003
|
+
children: inst.id
|
|
3004
|
+
}, undefined, false, undefined, this),
|
|
3005
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
3006
|
+
className: "px-4 py-3",
|
|
3007
|
+
children: /* @__PURE__ */ jsxDEV("span", {
|
|
3008
|
+
className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[inst.status] ?? ""}`,
|
|
3009
|
+
children: inst.status
|
|
3010
|
+
}, undefined, false, undefined, this)
|
|
3011
|
+
}, undefined, false, undefined, this),
|
|
3012
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
3013
|
+
className: "px-4 py-3 text-sm",
|
|
3014
|
+
children: inst.requestedBy
|
|
3015
|
+
}, undefined, false, undefined, this),
|
|
3016
|
+
/* @__PURE__ */ jsxDEV("td", {
|
|
3017
|
+
className: "text-muted-foreground px-4 py-3 text-sm",
|
|
3018
|
+
children: inst.startedAt.toLocaleDateString()
|
|
3019
|
+
}, undefined, false, undefined, this)
|
|
3020
|
+
]
|
|
3021
|
+
}, inst.id, true, undefined, this)),
|
|
3022
|
+
instances.length === 0 && /* @__PURE__ */ jsxDEV("tr", {
|
|
3023
|
+
children: /* @__PURE__ */ jsxDEV("td", {
|
|
3024
|
+
colSpan: 4,
|
|
3025
|
+
className: "text-muted-foreground px-4 py-8 text-center",
|
|
3026
|
+
children: "No workflow instances found"
|
|
3027
|
+
}, undefined, false, undefined, this)
|
|
3028
|
+
}, undefined, false, undefined, this)
|
|
3029
|
+
]
|
|
3030
|
+
}, undefined, true, undefined, this)
|
|
3031
|
+
]
|
|
3032
|
+
}, undefined, true, undefined, this)
|
|
3033
|
+
}, undefined, false, undefined, this)
|
|
3034
|
+
]
|
|
3035
|
+
}, undefined, true, undefined, this)
|
|
3036
|
+
]
|
|
3037
|
+
}, undefined, true, undefined, this);
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
// src/ui/hooks/index.ts
|
|
3041
|
+
"use client";
|
|
3042
|
+
export {
|
|
3043
|
+
workflowInstanceDetailMarkdownRenderer,
|
|
3044
|
+
workflowDefinitionListMarkdownRenderer,
|
|
3045
|
+
workflowDashboardMarkdownRenderer,
|
|
3046
|
+
useWorkflowList,
|
|
3047
|
+
mockDataStore,
|
|
3048
|
+
createWorkflowHandlers,
|
|
3049
|
+
createStateMachineEngine,
|
|
3050
|
+
createInitialState,
|
|
3051
|
+
buildStateMachineDefinition,
|
|
3052
|
+
WorkflowUpdatedEvent,
|
|
3053
|
+
WorkflowSystemPresentations,
|
|
3054
|
+
WorkflowSystemFeature,
|
|
3055
|
+
WorkflowStepModel,
|
|
3056
|
+
WorkflowStatusEnum,
|
|
3057
|
+
WorkflowPublishedEvent,
|
|
3058
|
+
WorkflowMetricsPresentation,
|
|
3059
|
+
WorkflowListPresentation,
|
|
3060
|
+
WorkflowInstanceModel,
|
|
3061
|
+
WorkflowDetailPresentation,
|
|
3062
|
+
WorkflowDesignerPresentation,
|
|
3063
|
+
WorkflowDefinitionModel,
|
|
3064
|
+
WorkflowDashboard,
|
|
3065
|
+
WorkflowCreatedEvent,
|
|
3066
|
+
UpdateWorkflowInputModel,
|
|
3067
|
+
UpdateWorkflowContract,
|
|
3068
|
+
TriggerTypeEnum,
|
|
3069
|
+
TransitionWorkflowContract,
|
|
3070
|
+
TransitionResultModel,
|
|
3071
|
+
TransitionInputModel,
|
|
3072
|
+
SubmitDecisionContract,
|
|
3073
|
+
StepTypeEnum,
|
|
3074
|
+
StepExitedEvent,
|
|
3075
|
+
StepEnteredEvent,
|
|
3076
|
+
StepAddedEvent,
|
|
3077
|
+
StartWorkflowInputModel,
|
|
3078
|
+
StartWorkflowContract,
|
|
3079
|
+
ResumeWorkflowContract,
|
|
3080
|
+
PublishWorkflowContract,
|
|
3081
|
+
ProgressTrackerPresentation,
|
|
3082
|
+
PendingApprovalsBadgePresentation,
|
|
3083
|
+
PauseWorkflowContract,
|
|
3084
|
+
ListWorkflowsContract,
|
|
3085
|
+
ListMyApprovalsContract,
|
|
3086
|
+
ListInstancesContract,
|
|
3087
|
+
InstanceTimedOutEvent,
|
|
3088
|
+
InstanceStatusEnum,
|
|
3089
|
+
InstanceStartedEvent,
|
|
3090
|
+
InstanceResumedEvent,
|
|
3091
|
+
InstancePausedEvent,
|
|
3092
|
+
InstanceListPresentation,
|
|
3093
|
+
InstanceFailedEvent,
|
|
3094
|
+
InstanceDetailPresentation,
|
|
3095
|
+
InstanceCompletedEvent,
|
|
3096
|
+
InstanceCancelledEvent,
|
|
3097
|
+
GetWorkflowContract,
|
|
3098
|
+
GetInstanceContract,
|
|
3099
|
+
GetApprovalContract,
|
|
3100
|
+
DelegateApprovalContract,
|
|
3101
|
+
CreateWorkflowInputModel,
|
|
3102
|
+
CreateWorkflowContract,
|
|
3103
|
+
CancelWorkflowContract,
|
|
3104
|
+
BasicStateMachineEngine,
|
|
3105
|
+
ApprovalStatusEnum,
|
|
3106
|
+
ApprovalRequestedEvent,
|
|
3107
|
+
ApprovalRequestModel,
|
|
3108
|
+
ApprovalModeEnum,
|
|
3109
|
+
ApprovalInboxPresentation,
|
|
3110
|
+
ApprovalFormPresentation,
|
|
3111
|
+
ApprovalEscalatedEvent,
|
|
3112
|
+
ApprovalDetailPresentation,
|
|
3113
|
+
ApprovalDelegatedEvent,
|
|
3114
|
+
ApprovalDecisionEnum,
|
|
3115
|
+
ApprovalDecidedEvent,
|
|
3116
|
+
ApprovalCommentModel,
|
|
3117
|
+
AddStepInputModel,
|
|
3118
|
+
AddStepContract,
|
|
3119
|
+
AddApprovalCommentContract
|
|
3120
|
+
};
|