@contractspec/example.workflow-system 3.8.9 → 3.8.10

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.
Files changed (95) hide show
  1. package/dist/approval/approval.enum.js +1 -22
  2. package/dist/approval/approval.event.js +1 -112
  3. package/dist/approval/approval.operations.js +1 -369
  4. package/dist/approval/approval.schema.js +1 -73
  5. package/dist/approval/index.js +1 -484
  6. package/dist/browser/approval/approval.enum.js +1 -22
  7. package/dist/browser/approval/approval.event.js +1 -112
  8. package/dist/browser/approval/approval.operations.js +1 -369
  9. package/dist/browser/approval/approval.schema.js +1 -73
  10. package/dist/browser/approval/index.js +1 -484
  11. package/dist/browser/docs/index.js +5 -49
  12. package/dist/browser/docs/workflow-system.docblock.js +5 -49
  13. package/dist/browser/entities/approval.js +1 -119
  14. package/dist/browser/entities/index.js +1 -508
  15. package/dist/browser/entities/instance.js +1 -161
  16. package/dist/browser/entities/step.js +1 -124
  17. package/dist/browser/entities/workflow.js +1 -82
  18. package/dist/browser/example.js +1 -42
  19. package/dist/browser/handlers/index.js +8 -253
  20. package/dist/browser/handlers/workflow.handlers.js +8 -253
  21. package/dist/browser/index.js +13 -3524
  22. package/dist/browser/instance/index.js +1 -677
  23. package/dist/browser/instance/instance.enum.js +1 -15
  24. package/dist/browser/instance/instance.event.js +1 -164
  25. package/dist/browser/instance/instance.handler.js +1 -356
  26. package/dist/browser/instance/instance.operations.js +1 -9
  27. package/dist/browser/instance/instance.schema.js +1 -101
  28. package/dist/browser/presentations/index.js +1 -109
  29. package/dist/browser/seeders/index.js +1 -3
  30. package/dist/browser/shared/demo-scenario.js +1 -213
  31. package/dist/browser/shared/index.js +1 -3
  32. package/dist/browser/shared/mock-data.js +1 -11
  33. package/dist/browser/state-machine/index.js +1 -6
  34. package/dist/browser/tests/operations.test-spec.js +1 -6
  35. package/dist/browser/ui/WorkflowDashboard.js +1 -3
  36. package/dist/browser/ui/WorkflowDashboard.visualizations.js +1 -239
  37. package/dist/browser/ui/hooks/index.js +1 -3
  38. package/dist/browser/ui/hooks/useWorkflowList.js +1 -52
  39. package/dist/browser/ui/index.js +1 -56
  40. package/dist/browser/ui/renderers/index.js +5 -562
  41. package/dist/browser/ui/renderers/workflow.markdown.js +5 -562
  42. package/dist/browser/visualizations/catalog.js +1 -132
  43. package/dist/browser/visualizations/index.js +1 -133
  44. package/dist/browser/visualizations/selectors.js +1 -195
  45. package/dist/browser/workflow/index.js +1 -21
  46. package/dist/browser/workflow/workflow.enum.js +1 -36
  47. package/dist/browser/workflow/workflow.event.js +1 -6
  48. package/dist/browser/workflow/workflow.handler.js +1 -5
  49. package/dist/browser/workflow/workflow.operations.js +1 -8
  50. package/dist/browser/workflow/workflow.schema.js +1 -151
  51. package/dist/browser/workflow-system.capability.js +1 -5
  52. package/dist/browser/workflow-system.feature.js +1 -3
  53. package/dist/docs/index.js +5 -49
  54. package/dist/docs/workflow-system.docblock.js +5 -49
  55. package/dist/entities/approval.js +1 -119
  56. package/dist/entities/index.js +1 -508
  57. package/dist/entities/instance.js +1 -161
  58. package/dist/entities/step.js +1 -124
  59. package/dist/entities/workflow.js +1 -82
  60. package/dist/example.js +1 -42
  61. package/dist/handlers/index.js +8 -253
  62. package/dist/handlers/workflow.handlers.js +8 -253
  63. package/dist/index.js +13 -3524
  64. package/dist/instance/index.js +1 -677
  65. package/dist/instance/instance.enum.js +1 -15
  66. package/dist/instance/instance.event.js +1 -164
  67. package/dist/instance/instance.handler.js +1 -356
  68. package/dist/instance/instance.operations.js +1 -9
  69. package/dist/instance/instance.schema.js +1 -101
  70. package/dist/presentations/index.js +1 -109
  71. package/dist/seeders/index.js +1 -3
  72. package/dist/shared/demo-scenario.js +1 -213
  73. package/dist/shared/index.js +1 -3
  74. package/dist/shared/mock-data.js +1 -11
  75. package/dist/state-machine/index.js +1 -6
  76. package/dist/tests/operations.test-spec.js +1 -6
  77. package/dist/ui/WorkflowDashboard.js +1 -3
  78. package/dist/ui/WorkflowDashboard.visualizations.js +1 -239
  79. package/dist/ui/hooks/index.js +1 -3
  80. package/dist/ui/hooks/useWorkflowList.js +1 -52
  81. package/dist/ui/index.js +1 -56
  82. package/dist/ui/renderers/index.js +5 -562
  83. package/dist/ui/renderers/workflow.markdown.js +5 -562
  84. package/dist/visualizations/catalog.js +1 -132
  85. package/dist/visualizations/index.js +1 -133
  86. package/dist/visualizations/selectors.js +1 -195
  87. package/dist/workflow/index.js +1 -21
  88. package/dist/workflow/workflow.enum.js +1 -36
  89. package/dist/workflow/workflow.event.js +1 -6
  90. package/dist/workflow/workflow.handler.js +1 -5
  91. package/dist/workflow/workflow.operations.js +1 -8
  92. package/dist/workflow/workflow.schema.js +1 -151
  93. package/dist/workflow-system.capability.js +1 -5
  94. package/dist/workflow-system.feature.js +1 -3
  95. package/package.json +8 -8
@@ -1,15 +1 @@
1
- // src/instance/instance.enum.ts
2
- import { defineEnum } from "@contractspec/lib.schema";
3
- var InstanceStatusEnum = defineEnum("InstanceStatus", [
4
- "PENDING",
5
- "RUNNING",
6
- "WAITING",
7
- "PAUSED",
8
- "COMPLETED",
9
- "CANCELLED",
10
- "FAILED",
11
- "TIMEOUT"
12
- ]);
13
- export {
14
- InstanceStatusEnum
15
- };
1
+ import{defineEnum as g}from"@contractspec/lib.schema";var k=g("InstanceStatus",["PENDING","RUNNING","WAITING","PAUSED","COMPLETED","CANCELLED","FAILED","TIMEOUT"]);export{k as InstanceStatusEnum};
@@ -1,164 +1 @@
1
- // src/instance/instance.event.ts
2
- import { defineEvent } from "@contractspec/lib.contracts-spec";
3
- import { defineSchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";
4
- var InstanceEventPayload = defineSchemaModel({
5
- name: "InstanceEventPayload",
6
- description: "Base payload for instance events",
7
- fields: {
8
- instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
9
- workflowId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
10
- workflowKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
11
- status: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
12
- referenceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
13
- referenceType: {
14
- type: ScalarTypeEnum.String_unsecure(),
15
- isOptional: true
16
- },
17
- triggeredBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
18
- organizationId: {
19
- type: ScalarTypeEnum.String_unsecure(),
20
- isOptional: false
21
- },
22
- timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
23
- }
24
- });
25
- var StepTransitionPayload = defineSchemaModel({
26
- name: "StepTransitionEventPayload",
27
- description: "Payload for step transition events",
28
- fields: {
29
- instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
30
- workflowId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
31
- fromStepKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
32
- toStepKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
33
- action: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
34
- executedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
35
- timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
36
- }
37
- });
38
- var InstanceCompletedPayload = defineSchemaModel({
39
- name: "InstanceCompletedEventPayload",
40
- description: "Payload when instance completes",
41
- fields: {
42
- instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
43
- workflowId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
44
- workflowKey: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
45
- outcome: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
46
- referenceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
47
- referenceType: {
48
- type: ScalarTypeEnum.String_unsecure(),
49
- isOptional: true
50
- },
51
- duration: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
52
- timestamp: { type: ScalarTypeEnum.DateTime(), isOptional: false }
53
- }
54
- });
55
- var InstanceStartedEvent = defineEvent({
56
- meta: {
57
- key: "workflow.instance.started",
58
- version: "1.0.0",
59
- description: "A new workflow instance has been started.",
60
- stability: "stable",
61
- owners: ["@workflow-team"],
62
- tags: ["workflow", "instance", "started"]
63
- },
64
- payload: InstanceEventPayload
65
- });
66
- var StepEnteredEvent = defineEvent({
67
- meta: {
68
- key: "workflow.step.entered",
69
- version: "1.0.0",
70
- description: "A workflow instance has entered a new step.",
71
- stability: "stable",
72
- owners: ["@workflow-team"],
73
- tags: ["workflow", "step", "entered"]
74
- },
75
- payload: StepTransitionPayload
76
- });
77
- var StepExitedEvent = defineEvent({
78
- meta: {
79
- key: "workflow.step.exited",
80
- version: "1.0.0",
81
- description: "A workflow instance has exited a step.",
82
- stability: "stable",
83
- owners: ["@workflow-team"],
84
- tags: ["workflow", "step", "exited"]
85
- },
86
- payload: StepTransitionPayload
87
- });
88
- var InstanceCompletedEvent = defineEvent({
89
- meta: {
90
- key: "workflow.instance.completed",
91
- version: "1.0.0",
92
- description: "A workflow instance has completed.",
93
- stability: "stable",
94
- owners: ["@workflow-team"],
95
- tags: ["workflow", "instance", "completed"]
96
- },
97
- payload: InstanceCompletedPayload
98
- });
99
- var InstanceCancelledEvent = defineEvent({
100
- meta: {
101
- key: "workflow.instance.cancelled",
102
- version: "1.0.0",
103
- description: "A workflow instance has been cancelled.",
104
- stability: "stable",
105
- owners: ["@workflow-team"],
106
- tags: ["workflow", "instance", "cancelled"]
107
- },
108
- payload: InstanceEventPayload
109
- });
110
- var InstancePausedEvent = defineEvent({
111
- meta: {
112
- key: "workflow.instance.paused",
113
- version: "1.0.0",
114
- description: "A workflow instance has been paused.",
115
- stability: "stable",
116
- owners: ["@workflow-team"],
117
- tags: ["workflow", "instance", "paused"]
118
- },
119
- payload: InstanceEventPayload
120
- });
121
- var InstanceResumedEvent = defineEvent({
122
- meta: {
123
- key: "workflow.instance.resumed",
124
- version: "1.0.0",
125
- description: "A workflow instance has been resumed.",
126
- stability: "stable",
127
- owners: ["@workflow-team"],
128
- tags: ["workflow", "instance", "resumed"]
129
- },
130
- payload: InstanceEventPayload
131
- });
132
- var InstanceFailedEvent = defineEvent({
133
- meta: {
134
- key: "workflow.instance.failed",
135
- version: "1.0.0",
136
- description: "A workflow instance has failed.",
137
- stability: "stable",
138
- owners: ["@workflow-team"],
139
- tags: ["workflow", "instance", "failed"]
140
- },
141
- payload: InstanceEventPayload
142
- });
143
- var InstanceTimedOutEvent = defineEvent({
144
- meta: {
145
- key: "workflow.instance.timedOut",
146
- version: "1.0.0",
147
- description: "A workflow instance has timed out.",
148
- stability: "stable",
149
- owners: ["@workflow-team"],
150
- tags: ["workflow", "instance", "timeout"]
151
- },
152
- payload: InstanceEventPayload
153
- });
154
- export {
155
- StepExitedEvent,
156
- StepEnteredEvent,
157
- InstanceTimedOutEvent,
158
- InstanceStartedEvent,
159
- InstanceResumedEvent,
160
- InstancePausedEvent,
161
- InstanceFailedEvent,
162
- InstanceCompletedEvent,
163
- InstanceCancelledEvent
164
- };
1
+ import{defineEvent as j}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as q,ScalarTypeEnum as g}from"@contractspec/lib.schema";var k=q({name:"InstanceEventPayload",description:"Base payload for instance events",fields:{instanceId:{type:g.String_unsecure(),isOptional:!1},workflowId:{type:g.String_unsecure(),isOptional:!1},workflowKey:{type:g.String_unsecure(),isOptional:!1},status:{type:g.String_unsecure(),isOptional:!1},referenceId:{type:g.String_unsecure(),isOptional:!0},referenceType:{type:g.String_unsecure(),isOptional:!0},triggeredBy:{type:g.String_unsecure(),isOptional:!1},organizationId:{type:g.String_unsecure(),isOptional:!1},timestamp:{type:g.DateTime(),isOptional:!1}}}),w=q({name:"StepTransitionEventPayload",description:"Payload for step transition events",fields:{instanceId:{type:g.String_unsecure(),isOptional:!1},workflowId:{type:g.String_unsecure(),isOptional:!1},fromStepKey:{type:g.String_unsecure(),isOptional:!0},toStepKey:{type:g.String_unsecure(),isOptional:!1},action:{type:g.String_unsecure(),isOptional:!0},executedBy:{type:g.String_unsecure(),isOptional:!1},timestamp:{type:g.DateTime(),isOptional:!1}}}),x=q({name:"InstanceCompletedEventPayload",description:"Payload when instance completes",fields:{instanceId:{type:g.String_unsecure(),isOptional:!1},workflowId:{type:g.String_unsecure(),isOptional:!1},workflowKey:{type:g.String_unsecure(),isOptional:!1},outcome:{type:g.String_unsecure(),isOptional:!1},referenceId:{type:g.String_unsecure(),isOptional:!0},referenceType:{type:g.String_unsecure(),isOptional:!0},duration:{type:g.Int_unsecure(),isOptional:!1},timestamp:{type:g.DateTime(),isOptional:!1}}}),B=j({meta:{key:"workflow.instance.started",version:"1.0.0",description:"A new workflow instance has been started.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","started"]},payload:k}),D=j({meta:{key:"workflow.step.entered",version:"1.0.0",description:"A workflow instance has entered a new step.",stability:"stable",owners:["@workflow-team"],tags:["workflow","step","entered"]},payload:w}),F=j({meta:{key:"workflow.step.exited",version:"1.0.0",description:"A workflow instance has exited a step.",stability:"stable",owners:["@workflow-team"],tags:["workflow","step","exited"]},payload:w}),G=j({meta:{key:"workflow.instance.completed",version:"1.0.0",description:"A workflow instance has completed.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","completed"]},payload:x}),H=j({meta:{key:"workflow.instance.cancelled",version:"1.0.0",description:"A workflow instance has been cancelled.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","cancelled"]},payload:k}),J=j({meta:{key:"workflow.instance.paused",version:"1.0.0",description:"A workflow instance has been paused.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","paused"]},payload:k}),K=j({meta:{key:"workflow.instance.resumed",version:"1.0.0",description:"A workflow instance has been resumed.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","resumed"]},payload:k}),L=j({meta:{key:"workflow.instance.failed",version:"1.0.0",description:"A workflow instance has failed.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","failed"]},payload:k}),N=j({meta:{key:"workflow.instance.timedOut",version:"1.0.0",description:"A workflow instance has timed out.",stability:"stable",owners:["@workflow-team"],tags:["workflow","instance","timeout"]},payload:k});export{F as StepExitedEvent,D as StepEnteredEvent,N as InstanceTimedOutEvent,B as InstanceStartedEvent,K as InstanceResumedEvent,J as InstancePausedEvent,L as InstanceFailedEvent,G as InstanceCompletedEvent,H as InstanceCancelledEvent};
@@ -1,356 +1 @@
1
- // src/shared/mock-data.ts
2
- var mockDataStore = {
3
- workflows: new Map,
4
- steps: new Map,
5
- instances: new Map,
6
- approvals: new Map,
7
- stepExecutions: new Map
8
- };
9
-
10
- // src/state-machine/index.ts
11
- class BasicStateMachineEngine {
12
- canTransition(definition, state, action, context) {
13
- if (state.status !== "RUNNING" && state.status !== "WAITING") {
14
- return {
15
- allowed: false,
16
- reason: `Workflow is ${state.status}, cannot transition`
17
- };
18
- }
19
- const currentStep = definition.steps[state.currentStepKey];
20
- if (!currentStep) {
21
- return {
22
- allowed: false,
23
- reason: `Step ${state.currentStepKey} not found`
24
- };
25
- }
26
- const transition = currentStep.transitions[action];
27
- if (!transition) {
28
- return {
29
- allowed: false,
30
- reason: `Action ${action} not available in step ${state.currentStepKey}`
31
- };
32
- }
33
- if (currentStep.allowedRoles && currentStep.allowedRoles.length > 0) {
34
- const hasRole = currentStep.allowedRoles.some((role) => context.userRoles.includes(role));
35
- if (!hasRole) {
36
- return {
37
- allowed: false,
38
- reason: `User lacks required role for this action`
39
- };
40
- }
41
- }
42
- if (typeof transition === "object" && transition.allowedRoles && transition.allowedRoles.length > 0) {
43
- const hasRole = transition.allowedRoles.some((role) => context.userRoles.includes(role));
44
- if (!hasRole) {
45
- return {
46
- allowed: false,
47
- reason: `User lacks required role for action ${action}`
48
- };
49
- }
50
- }
51
- return { allowed: true };
52
- }
53
- getAvailableActions(definition, state, context) {
54
- if (state.status !== "RUNNING" && state.status !== "WAITING") {
55
- return [];
56
- }
57
- const currentStep = definition.steps[state.currentStepKey];
58
- if (!currentStep) {
59
- return [];
60
- }
61
- return Object.keys(currentStep.transitions).filter((action) => {
62
- const result = this.canTransition(definition, state, action, context);
63
- return result.allowed;
64
- });
65
- }
66
- transition(definition, state, action, context) {
67
- const validation = this.canTransition(definition, state, action, context);
68
- if (!validation.allowed) {
69
- return {
70
- success: false,
71
- previousStepKey: state.currentStepKey,
72
- currentStepKey: state.currentStepKey,
73
- status: state.status,
74
- error: validation.reason
75
- };
76
- }
77
- const currentStep = definition.steps[state.currentStepKey];
78
- if (!currentStep) {
79
- return {
80
- success: false,
81
- previousStepKey: state.currentStepKey,
82
- currentStepKey: state.currentStepKey,
83
- status: state.status,
84
- error: `Current step ${state.currentStepKey} not found`
85
- };
86
- }
87
- const transition = currentStep.transitions[action];
88
- if (!transition) {
89
- return {
90
- success: false,
91
- previousStepKey: state.currentStepKey,
92
- currentStepKey: state.currentStepKey,
93
- status: state.status,
94
- error: `Transition for action ${action} not found`
95
- };
96
- }
97
- const targetStepKey = typeof transition === "string" ? transition : transition.targetStepKey;
98
- const targetStep = definition.steps[targetStepKey];
99
- if (!targetStep) {
100
- return {
101
- success: false,
102
- previousStepKey: state.currentStepKey,
103
- currentStepKey: state.currentStepKey,
104
- status: state.status,
105
- error: `Target step ${targetStepKey} not found`
106
- };
107
- }
108
- let newStatus = "RUNNING";
109
- if (targetStep.type === "END") {
110
- newStatus = "COMPLETED";
111
- } else if (targetStep.type === "APPROVAL" || targetStep.type === "WAIT") {
112
- newStatus = "WAITING";
113
- }
114
- return {
115
- success: true,
116
- previousStepKey: state.currentStepKey,
117
- currentStepKey: targetStepKey,
118
- status: newStatus
119
- };
120
- }
121
- evaluateCondition(expression, contextData) {
122
- try {
123
- const match = expression.match(/^(\w+)\s*(>=|<=|>|<|===|!==|==|!=)\s*(.+)$/);
124
- if (match) {
125
- const [, prop, operator, value] = match;
126
- if (!prop || !operator || value === undefined) {
127
- return false;
128
- }
129
- const propValue = contextData[prop];
130
- const compareValue = JSON.parse(value);
131
- switch (operator) {
132
- case ">":
133
- return Number(propValue) > Number(compareValue);
134
- case "<":
135
- return Number(propValue) < Number(compareValue);
136
- case ">=":
137
- return Number(propValue) >= Number(compareValue);
138
- case "<=":
139
- return Number(propValue) <= Number(compareValue);
140
- case "===":
141
- case "==":
142
- return propValue === compareValue;
143
- case "!==":
144
- case "!=":
145
- return propValue !== compareValue;
146
- }
147
- }
148
- if (expression in contextData) {
149
- return Boolean(contextData[expression]);
150
- }
151
- return false;
152
- } catch {
153
- return false;
154
- }
155
- }
156
- }
157
- function createStateMachineEngine() {
158
- return new BasicStateMachineEngine;
159
- }
160
- function buildStateMachineDefinition(workflow, steps) {
161
- const stepMap = {};
162
- for (const step of steps) {
163
- stepMap[step.key] = {
164
- key: step.key,
165
- name: step.name,
166
- type: step.type,
167
- transitions: step.transitions,
168
- approvalMode: step.approvalMode,
169
- allowedRoles: step.approverRoles,
170
- timeoutSeconds: step.timeoutSeconds,
171
- conditionExpression: step.conditionExpression
172
- };
173
- }
174
- const startStep = steps.find((s) => s.type === "START");
175
- const initialStepKey = startStep?.key ?? steps[0]?.key ?? "";
176
- return {
177
- key: workflow.key,
178
- name: workflow.name,
179
- version: workflow.version,
180
- initialStepKey,
181
- steps: stepMap
182
- };
183
- }
184
- function createInitialState(definition, contextData = {}) {
185
- return {
186
- currentStepKey: definition.initialStepKey,
187
- status: "RUNNING",
188
- contextData,
189
- history: []
190
- };
191
- }
192
-
193
- // src/approval/approval.handler.ts
194
- async function createApprovalRequests(instance, step, _context) {
195
- const now = new Date;
196
- for (let i = 0;i < step.approverRoles.length; i++) {
197
- const role = step.approverRoles[i];
198
- const id = `approval_${Date.now()}_${i}`;
199
- const request = {
200
- id,
201
- workflowInstanceId: instance.id,
202
- stepExecutionId: `exec_${instance.id}_${step.id}`,
203
- approverId: `user_${role}`,
204
- approverRole: role,
205
- title: `Approval required for ${step.name}`,
206
- description: step.description,
207
- status: "PENDING",
208
- contextSnapshot: instance.contextData,
209
- sequenceOrder: i,
210
- createdAt: now,
211
- updatedAt: now
212
- };
213
- mockDataStore.approvals.set(id, request);
214
- }
215
- }
216
- async function handleSubmitDecision(input, context) {
217
- const request = mockDataStore.approvals.get(input.requestId);
218
- if (!request) {
219
- throw new Error(`Approval request ${input.requestId} not found`);
220
- }
221
- if (request.approverId !== context.userId && !context.userRoles.includes(request.approverRole ?? "")) {
222
- throw new Error("User is not authorized to make this decision");
223
- }
224
- const now = new Date;
225
- request.decision = input.decision;
226
- request.decisionComment = input.comment;
227
- request.decidedAt = now;
228
- request.updatedAt = now;
229
- if (input.decision === "APPROVE") {
230
- request.status = "APPROVED";
231
- await handleTransitionWorkflow({
232
- instanceId: request.workflowInstanceId,
233
- action: "approve",
234
- data: input.data,
235
- comment: input.comment
236
- }, context);
237
- } else if (input.decision === "REJECT") {
238
- request.status = "REJECTED";
239
- await handleTransitionWorkflow({
240
- instanceId: request.workflowInstanceId,
241
- action: "reject",
242
- data: input.data,
243
- comment: input.comment
244
- }, context);
245
- }
246
- return request;
247
- }
248
- async function handleListMyApprovals(input, context) {
249
- let requests = Array.from(mockDataStore.approvals.values()).filter((r) => r.approverId === context.userId || context.userRoles.includes(r.approverRole ?? ""));
250
- const pendingCount = requests.filter((r) => r.status === "PENDING").length;
251
- if (input.status) {
252
- requests = requests.filter((r) => r.status === input.status);
253
- }
254
- const total = requests.length;
255
- const offset = input.offset ?? 0;
256
- const limit = input.limit ?? 20;
257
- requests = requests.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).slice(offset, offset + limit);
258
- return { requests, total, pendingCount };
259
- }
260
-
261
- // src/instance/instance.handler.ts
262
- async function handleStartWorkflow(input, context) {
263
- const workflow = Array.from(mockDataStore.workflows.values()).find((w) => w.key === input.workflowKey && w.status === "ACTIVE" && w.organizationId === context.organizationId);
264
- if (!workflow) {
265
- throw new Error(`Active workflow ${input.workflowKey} not found`);
266
- }
267
- const id = `inst_${Date.now()}`;
268
- const now = new Date;
269
- const instance = {
270
- id,
271
- workflowDefinitionId: workflow.id,
272
- referenceId: input.referenceId,
273
- referenceType: input.referenceType,
274
- status: "RUNNING",
275
- currentStepId: workflow.initialStepId,
276
- contextData: input.contextData ?? {},
277
- triggeredBy: context.userId,
278
- organizationId: context.organizationId,
279
- priority: input.priority ?? 0,
280
- dueAt: input.dueAt,
281
- createdAt: now,
282
- updatedAt: now,
283
- startedAt: now
284
- };
285
- mockDataStore.instances.set(id, instance);
286
- if (workflow.initialStepId) {
287
- const firstStep = mockDataStore.steps.get(workflow.initialStepId);
288
- if (firstStep?.type === "APPROVAL") {
289
- instance.status = "WAITING";
290
- await createApprovalRequests(instance, firstStep, context);
291
- }
292
- }
293
- return instance;
294
- }
295
- async function handleTransitionWorkflow(input, context) {
296
- const instance = mockDataStore.instances.get(input.instanceId);
297
- if (!instance) {
298
- throw new Error(`Instance ${input.instanceId} not found`);
299
- }
300
- const workflow = mockDataStore.workflows.get(instance.workflowDefinitionId);
301
- if (!workflow) {
302
- throw new Error(`Workflow ${instance.workflowDefinitionId} not found`);
303
- }
304
- const steps = Array.from(mockDataStore.steps.values()).filter((s) => s.workflowDefinitionId === workflow.id);
305
- const definition = buildStateMachineDefinition({
306
- key: workflow.key,
307
- name: workflow.name,
308
- version: workflow.version,
309
- initialStepId: workflow.initialStepId ?? null
310
- }, steps);
311
- const currentStep = steps.find((s) => s.id === instance.currentStepId);
312
- const state = {
313
- currentStepKey: currentStep?.key ?? "",
314
- status: instance.status,
315
- contextData: instance.contextData,
316
- history: []
317
- };
318
- const transitionContext = {
319
- userId: context.userId,
320
- userRoles: context.userRoles,
321
- data: input.data
322
- };
323
- const engine = createStateMachineEngine();
324
- const result = engine.transition(definition, state, input.action, transitionContext);
325
- if (!result.success) {
326
- return {
327
- success: false,
328
- instance,
329
- message: result.error
330
- };
331
- }
332
- const previousStepKey = currentStep?.key;
333
- const newStep = steps.find((s) => s.key === result.currentStepKey);
334
- instance.currentStepId = newStep?.id;
335
- instance.status = result.status;
336
- instance.contextData = { ...instance.contextData, ...input.data };
337
- instance.updatedAt = new Date;
338
- if (result.status === "COMPLETED") {
339
- instance.completedAt = new Date;
340
- instance.outcome = input.action;
341
- }
342
- if (newStep?.type === "APPROVAL") {
343
- instance.status = "WAITING";
344
- await createApprovalRequests(instance, newStep, context);
345
- }
346
- return {
347
- success: true,
348
- instance,
349
- previousStepKey,
350
- currentStepKey: result.currentStepKey ?? undefined
351
- };
352
- }
353
- export {
354
- handleTransitionWorkflow,
355
- handleStartWorkflow
356
- };
1
+ var J={workflows:new Map,steps:new Map,instances:new Map,approvals:new Map,stepExecutions:new Map};class Y{canTransition(W,R,I,z){if(R.status!=="RUNNING"&&R.status!=="WAITING")return{allowed:!1,reason:`Workflow is ${R.status}, cannot transition`};let j=W.steps[R.currentStepKey];if(!j)return{allowed:!1,reason:`Step ${R.currentStepKey} not found`};let b=j.transitions[I];if(!b)return{allowed:!1,reason:`Action ${I} not available in step ${R.currentStepKey}`};if(j.allowedRoles&&j.allowedRoles.length>0){if(!j.allowedRoles.some((F)=>z.userRoles.includes(F)))return{allowed:!1,reason:"User lacks required role for this action"}}if(typeof b==="object"&&b.allowedRoles&&b.allowedRoles.length>0){if(!b.allowedRoles.some((F)=>z.userRoles.includes(F)))return{allowed:!1,reason:`User lacks required role for action ${I}`}}return{allowed:!0}}getAvailableActions(W,R,I){if(R.status!=="RUNNING"&&R.status!=="WAITING")return[];let z=W.steps[R.currentStepKey];if(!z)return[];return Object.keys(z.transitions).filter((j)=>{return this.canTransition(W,R,j,I).allowed})}transition(W,R,I,z){let j=this.canTransition(W,R,I,z);if(!j.allowed)return{success:!1,previousStepKey:R.currentStepKey,currentStepKey:R.currentStepKey,status:R.status,error:j.reason};let b=W.steps[R.currentStepKey];if(!b)return{success:!1,previousStepKey:R.currentStepKey,currentStepKey:R.currentStepKey,status:R.status,error:`Current step ${R.currentStepKey} not found`};let A=b.transitions[I];if(!A)return{success:!1,previousStepKey:R.currentStepKey,currentStepKey:R.currentStepKey,status:R.status,error:`Transition for action ${I} not found`};let F=typeof A==="string"?A:A.targetStepKey,L=W.steps[F];if(!L)return{success:!1,previousStepKey:R.currentStepKey,currentStepKey:R.currentStepKey,status:R.status,error:`Target step ${F} not found`};let O="RUNNING";if(L.type==="END")O="COMPLETED";else if(L.type==="APPROVAL"||L.type==="WAIT")O="WAITING";return{success:!0,previousStepKey:R.currentStepKey,currentStepKey:F,status:O}}evaluateCondition(W,R){try{let I=W.match(/^(\w+)\s*(>=|<=|>|<|===|!==|==|!=)\s*(.+)$/);if(I){let[,z,j,b]=I;if(!z||!j||b===void 0)return!1;let A=R[z],F=JSON.parse(b);switch(j){case">":return Number(A)>Number(F);case"<":return Number(A)<Number(F);case">=":return Number(A)>=Number(F);case"<=":return Number(A)<=Number(F);case"===":case"==":return A===F;case"!==":case"!=":return A!==F}}if(W in R)return Boolean(R[W]);return!1}catch{return!1}}}function Z(){return new Y}function _(W,R){let I={};for(let b of R)I[b.key]={key:b.key,name:b.name,type:b.type,transitions:b.transitions,approvalMode:b.approvalMode,allowedRoles:b.approverRoles,timeoutSeconds:b.timeoutSeconds,conditionExpression:b.conditionExpression};let j=R.find((b)=>b.type==="START")?.key??R[0]?.key??"";return{key:W.key,name:W.name,version:W.version,initialStepKey:j,steps:I}}function H(W,R={}){return{currentStepKey:W.initialStepKey,status:"RUNNING",contextData:R,history:[]}}async function X(W,R,I){let z=new Date;for(let j=0;j<R.approverRoles.length;j++){let b=R.approverRoles[j],A=`approval_${Date.now()}_${j}`,F={id:A,workflowInstanceId:W.id,stepExecutionId:`exec_${W.id}_${R.id}`,approverId:`user_${b}`,approverRole:b,title:`Approval required for ${R.name}`,description:R.description,status:"PENDING",contextSnapshot:W.contextData,sequenceOrder:j,createdAt:z,updatedAt:z};J.approvals.set(A,F)}}async function B(W,R){let I=J.approvals.get(W.requestId);if(!I)throw Error(`Approval request ${W.requestId} not found`);if(I.approverId!==R.userId&&!R.userRoles.includes(I.approverRole??""))throw Error("User is not authorized to make this decision");let z=new Date;if(I.decision=W.decision,I.decisionComment=W.comment,I.decidedAt=z,I.updatedAt=z,W.decision==="APPROVE")I.status="APPROVED",await Q({instanceId:I.workflowInstanceId,action:"approve",data:W.data,comment:W.comment},R);else if(W.decision==="REJECT")I.status="REJECTED",await Q({instanceId:I.workflowInstanceId,action:"reject",data:W.data,comment:W.comment},R);return I}async function C(W,R){let I=Array.from(J.approvals.values()).filter((F)=>F.approverId===R.userId||R.userRoles.includes(F.approverRole??"")),z=I.filter((F)=>F.status==="PENDING").length;if(W.status)I=I.filter((F)=>F.status===W.status);let j=I.length,b=W.offset??0,A=W.limit??20;return I=I.sort((F,L)=>L.createdAt.getTime()-F.createdAt.getTime()).slice(b,b+A),{requests:I,total:j,pendingCount:z}}async function V(W,R){let I=Array.from(J.workflows.values()).find((A)=>A.key===W.workflowKey&&A.status==="ACTIVE"&&A.organizationId===R.organizationId);if(!I)throw Error(`Active workflow ${W.workflowKey} not found`);let z=`inst_${Date.now()}`,j=new Date,b={id:z,workflowDefinitionId:I.id,referenceId:W.referenceId,referenceType:W.referenceType,status:"RUNNING",currentStepId:I.initialStepId,contextData:W.contextData??{},triggeredBy:R.userId,organizationId:R.organizationId,priority:W.priority??0,dueAt:W.dueAt,createdAt:j,updatedAt:j,startedAt:j};if(J.instances.set(z,b),I.initialStepId){let A=J.steps.get(I.initialStepId);if(A?.type==="APPROVAL")b.status="WAITING",await X(b,A,R)}return b}async function Q(W,R){let I=J.instances.get(W.instanceId);if(!I)throw Error(`Instance ${W.instanceId} not found`);let z=J.workflows.get(I.workflowDefinitionId);if(!z)throw Error(`Workflow ${I.workflowDefinitionId} not found`);let j=Array.from(J.steps.values()).filter((N)=>N.workflowDefinitionId===z.id),b=_({key:z.key,name:z.name,version:z.version,initialStepId:z.initialStepId??null},j),A=j.find((N)=>N.id===I.currentStepId),F={currentStepKey:A?.key??"",status:I.status,contextData:I.contextData,history:[]},L={userId:R.userId,userRoles:R.userRoles,data:W.data},M=Z().transition(b,F,W.action,L);if(!M.success)return{success:!1,instance:I,message:M.error};let $=A?.key,P=j.find((N)=>N.key===M.currentStepKey);if(I.currentStepId=P?.id,I.status=M.status,I.contextData={...I.contextData,...W.data},I.updatedAt=new Date,M.status==="COMPLETED")I.completedAt=new Date,I.outcome=W.action;if(P?.type==="APPROVAL")I.status="WAITING",await X(I,P,R);return{success:!0,instance:I,previousStepKey:$,currentStepKey:M.currentStepKey??void 0}}export{Q as handleTransitionWorkflow,V as handleStartWorkflow};
@@ -1,9 +1 @@
1
- export {
2
- TransitionWorkflowContract,
3
- StartWorkflowContract,
4
- ResumeWorkflowContract,
5
- PauseWorkflowContract,
6
- ListInstancesContract,
7
- GetInstanceContract,
8
- CancelWorkflowContract
9
- };
1
+ export{TransitionWorkflowContract,StartWorkflowContract,ResumeWorkflowContract,PauseWorkflowContract,ListInstancesContract,GetInstanceContract,CancelWorkflowContract};
@@ -1,101 +1 @@
1
- // src/instance/instance.enum.ts
2
- import { defineEnum } from "@contractspec/lib.schema";
3
- var InstanceStatusEnum = defineEnum("InstanceStatus", [
4
- "PENDING",
5
- "RUNNING",
6
- "WAITING",
7
- "PAUSED",
8
- "COMPLETED",
9
- "CANCELLED",
10
- "FAILED",
11
- "TIMEOUT"
12
- ]);
13
-
14
- // src/instance/instance.schema.ts
15
- import { defineSchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";
16
- var WorkflowInstanceModel = defineSchemaModel({
17
- name: "WorkflowInstanceModel",
18
- description: "A running workflow instance",
19
- fields: {
20
- id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
21
- workflowDefinitionId: {
22
- type: ScalarTypeEnum.String_unsecure(),
23
- isOptional: false
24
- },
25
- referenceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
26
- referenceType: {
27
- type: ScalarTypeEnum.String_unsecure(),
28
- isOptional: true
29
- },
30
- status: { type: InstanceStatusEnum, isOptional: false },
31
- currentStepId: {
32
- type: ScalarTypeEnum.String_unsecure(),
33
- isOptional: true
34
- },
35
- contextData: { type: ScalarTypeEnum.JSON(), isOptional: true },
36
- triggeredBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
37
- organizationId: {
38
- type: ScalarTypeEnum.String_unsecure(),
39
- isOptional: false
40
- },
41
- priority: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
42
- dueAt: { type: ScalarTypeEnum.DateTime(), isOptional: true },
43
- outcome: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
44
- resultData: { type: ScalarTypeEnum.JSON(), isOptional: true },
45
- errorMessage: {
46
- type: ScalarTypeEnum.String_unsecure(),
47
- isOptional: true
48
- },
49
- createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
50
- startedAt: { type: ScalarTypeEnum.DateTime(), isOptional: true },
51
- completedAt: { type: ScalarTypeEnum.DateTime(), isOptional: true }
52
- }
53
- });
54
- var StartWorkflowInputModel = defineSchemaModel({
55
- name: "StartWorkflowInput",
56
- description: "Input for starting a workflow",
57
- fields: {
58
- workflowKey: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
59
- contextData: { type: ScalarTypeEnum.JSON(), isOptional: true },
60
- referenceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
61
- referenceType: {
62
- type: ScalarTypeEnum.String_unsecure(),
63
- isOptional: true
64
- },
65
- priority: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
66
- dueAt: { type: ScalarTypeEnum.DateTime(), isOptional: true }
67
- }
68
- });
69
- var TransitionInputModel = defineSchemaModel({
70
- name: "TransitionInput",
71
- description: "Input for transitioning a workflow",
72
- fields: {
73
- instanceId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
74
- action: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
75
- data: { type: ScalarTypeEnum.JSON(), isOptional: true },
76
- comment: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
77
- }
78
- });
79
- var TransitionResultModel = defineSchemaModel({
80
- name: "TransitionResult",
81
- description: "Result of a workflow transition",
82
- fields: {
83
- success: { type: ScalarTypeEnum.Boolean(), isOptional: false },
84
- instance: { type: WorkflowInstanceModel, isOptional: false },
85
- previousStepKey: {
86
- type: ScalarTypeEnum.String_unsecure(),
87
- isOptional: true
88
- },
89
- currentStepKey: {
90
- type: ScalarTypeEnum.String_unsecure(),
91
- isOptional: true
92
- },
93
- message: { type: ScalarTypeEnum.String_unsecure(), isOptional: true }
94
- }
95
- });
96
- export {
97
- WorkflowInstanceModel,
98
- TransitionResultModel,
99
- TransitionInputModel,
100
- StartWorkflowInputModel
101
- };
1
+ import{defineEnum as v}from"@contractspec/lib.schema";var q=v("InstanceStatus",["PENDING","RUNNING","WAITING","PAUSED","COMPLETED","CANCELLED","FAILED","TIMEOUT"]);import{defineSchemaModel as j,ScalarTypeEnum as g}from"@contractspec/lib.schema";var x=j({name:"WorkflowInstanceModel",description:"A running workflow instance",fields:{id:{type:g.String_unsecure(),isOptional:!1},workflowDefinitionId:{type:g.String_unsecure(),isOptional:!1},referenceId:{type:g.String_unsecure(),isOptional:!0},referenceType:{type:g.String_unsecure(),isOptional:!0},status:{type:q,isOptional:!1},currentStepId:{type:g.String_unsecure(),isOptional:!0},contextData:{type:g.JSON(),isOptional:!0},triggeredBy:{type:g.String_unsecure(),isOptional:!1},organizationId:{type:g.String_unsecure(),isOptional:!1},priority:{type:g.Int_unsecure(),isOptional:!1},dueAt:{type:g.DateTime(),isOptional:!0},outcome:{type:g.String_unsecure(),isOptional:!0},resultData:{type:g.JSON(),isOptional:!0},errorMessage:{type:g.String_unsecure(),isOptional:!0},createdAt:{type:g.DateTime(),isOptional:!1},startedAt:{type:g.DateTime(),isOptional:!0},completedAt:{type:g.DateTime(),isOptional:!0}}}),D=j({name:"StartWorkflowInput",description:"Input for starting a workflow",fields:{workflowKey:{type:g.NonEmptyString(),isOptional:!1},contextData:{type:g.JSON(),isOptional:!0},referenceId:{type:g.String_unsecure(),isOptional:!0},referenceType:{type:g.String_unsecure(),isOptional:!0},priority:{type:g.Int_unsecure(),isOptional:!0},dueAt:{type:g.DateTime(),isOptional:!0}}}),F=j({name:"TransitionInput",description:"Input for transitioning a workflow",fields:{instanceId:{type:g.String_unsecure(),isOptional:!1},action:{type:g.NonEmptyString(),isOptional:!1},data:{type:g.JSON(),isOptional:!0},comment:{type:g.String_unsecure(),isOptional:!0}}}),G=j({name:"TransitionResult",description:"Result of a workflow transition",fields:{success:{type:g.Boolean(),isOptional:!1},instance:{type:x,isOptional:!1},previousStepKey:{type:g.String_unsecure(),isOptional:!0},currentStepKey:{type:g.String_unsecure(),isOptional:!0},message:{type:g.String_unsecure(),isOptional:!0}}});export{x as WorkflowInstanceModel,G as TransitionResultModel,F as TransitionInputModel,D as StartWorkflowInputModel};