@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,161 @@
|
|
|
1
|
+
// src/entities/instance.ts
|
|
2
|
+
import {
|
|
3
|
+
defineEntity,
|
|
4
|
+
defineEntityEnum,
|
|
5
|
+
field,
|
|
6
|
+
index
|
|
7
|
+
} from "@contractspec/lib.schema";
|
|
8
|
+
var InstanceStatusEnum = defineEntityEnum({
|
|
9
|
+
name: "InstanceStatus",
|
|
10
|
+
values: [
|
|
11
|
+
"PENDING",
|
|
12
|
+
"RUNNING",
|
|
13
|
+
"WAITING",
|
|
14
|
+
"PAUSED",
|
|
15
|
+
"COMPLETED",
|
|
16
|
+
"CANCELLED",
|
|
17
|
+
"FAILED",
|
|
18
|
+
"TIMEOUT"
|
|
19
|
+
],
|
|
20
|
+
schema: "workflow",
|
|
21
|
+
description: "Status of a workflow instance."
|
|
22
|
+
});
|
|
23
|
+
var StepExecutionStatusEnum = defineEntityEnum({
|
|
24
|
+
name: "StepExecutionStatus",
|
|
25
|
+
values: [
|
|
26
|
+
"PENDING",
|
|
27
|
+
"ACTIVE",
|
|
28
|
+
"COMPLETED",
|
|
29
|
+
"SKIPPED",
|
|
30
|
+
"FAILED",
|
|
31
|
+
"TIMEOUT"
|
|
32
|
+
],
|
|
33
|
+
schema: "workflow",
|
|
34
|
+
description: "Status of a step execution."
|
|
35
|
+
});
|
|
36
|
+
var WorkflowInstanceEntity = defineEntity({
|
|
37
|
+
name: "WorkflowInstance",
|
|
38
|
+
description: "A running instance of a workflow definition.",
|
|
39
|
+
schema: "workflow",
|
|
40
|
+
map: "workflow_instance",
|
|
41
|
+
fields: {
|
|
42
|
+
id: field.id({ description: "Unique instance ID" }),
|
|
43
|
+
workflowDefinitionId: field.foreignKey(),
|
|
44
|
+
referenceId: field.string({
|
|
45
|
+
isOptional: true,
|
|
46
|
+
description: "External reference (e.g., order ID)"
|
|
47
|
+
}),
|
|
48
|
+
referenceType: field.string({
|
|
49
|
+
isOptional: true,
|
|
50
|
+
description: 'Type of reference (e.g., "Order")'
|
|
51
|
+
}),
|
|
52
|
+
status: field.enum("InstanceStatus", { default: "PENDING" }),
|
|
53
|
+
currentStepId: field.string({
|
|
54
|
+
isOptional: true,
|
|
55
|
+
description: "Current step being executed"
|
|
56
|
+
}),
|
|
57
|
+
contextData: field.json({ description: "Data context for this instance" }),
|
|
58
|
+
triggeredBy: field.foreignKey({
|
|
59
|
+
description: "User who triggered the workflow"
|
|
60
|
+
}),
|
|
61
|
+
triggerSource: field.string({
|
|
62
|
+
isOptional: true,
|
|
63
|
+
description: 'Source of trigger (e.g., "api", "ui")'
|
|
64
|
+
}),
|
|
65
|
+
organizationId: field.foreignKey(),
|
|
66
|
+
priority: field.int({
|
|
67
|
+
default: 0,
|
|
68
|
+
description: "Processing priority (higher = more urgent)"
|
|
69
|
+
}),
|
|
70
|
+
dueAt: field.dateTime({
|
|
71
|
+
isOptional: true,
|
|
72
|
+
description: "When this instance should complete"
|
|
73
|
+
}),
|
|
74
|
+
outcome: field.string({
|
|
75
|
+
isOptional: true,
|
|
76
|
+
description: 'Final outcome (e.g., "approved", "rejected")'
|
|
77
|
+
}),
|
|
78
|
+
resultData: field.json({
|
|
79
|
+
isOptional: true,
|
|
80
|
+
description: "Final result data"
|
|
81
|
+
}),
|
|
82
|
+
errorMessage: field.string({
|
|
83
|
+
isOptional: true,
|
|
84
|
+
description: "Error message if failed"
|
|
85
|
+
}),
|
|
86
|
+
createdAt: field.createdAt(),
|
|
87
|
+
updatedAt: field.updatedAt(),
|
|
88
|
+
startedAt: field.dateTime({ isOptional: true }),
|
|
89
|
+
completedAt: field.dateTime({ isOptional: true }),
|
|
90
|
+
workflowDefinition: field.belongsTo("WorkflowDefinition", ["workflowDefinitionId"], ["id"]),
|
|
91
|
+
currentStep: field.belongsTo("WorkflowStep", ["currentStepId"], ["id"]),
|
|
92
|
+
stepExecutions: field.hasMany("StepExecution"),
|
|
93
|
+
approvalRequests: field.hasMany("ApprovalRequest")
|
|
94
|
+
},
|
|
95
|
+
indexes: [
|
|
96
|
+
index.on(["organizationId", "status"]),
|
|
97
|
+
index.on(["workflowDefinitionId", "status"]),
|
|
98
|
+
index.on(["referenceType", "referenceId"]),
|
|
99
|
+
index.on(["triggeredBy", "status"]),
|
|
100
|
+
index.on(["status", "dueAt"]),
|
|
101
|
+
index.on(["createdAt"])
|
|
102
|
+
],
|
|
103
|
+
enums: [InstanceStatusEnum]
|
|
104
|
+
});
|
|
105
|
+
var StepExecutionEntity = defineEntity({
|
|
106
|
+
name: "StepExecution",
|
|
107
|
+
description: "Execution record of a step within a workflow instance.",
|
|
108
|
+
schema: "workflow",
|
|
109
|
+
map: "step_execution",
|
|
110
|
+
fields: {
|
|
111
|
+
id: field.id({ description: "Unique execution ID" }),
|
|
112
|
+
workflowInstanceId: field.foreignKey(),
|
|
113
|
+
workflowStepId: field.foreignKey(),
|
|
114
|
+
status: field.enum("StepExecutionStatus", { default: "PENDING" }),
|
|
115
|
+
executionOrder: field.int({
|
|
116
|
+
default: 0,
|
|
117
|
+
description: "Order of execution within instance"
|
|
118
|
+
}),
|
|
119
|
+
inputData: field.json({
|
|
120
|
+
isOptional: true,
|
|
121
|
+
description: "Data when entering step"
|
|
122
|
+
}),
|
|
123
|
+
outputData: field.json({
|
|
124
|
+
isOptional: true,
|
|
125
|
+
description: "Data when exiting step"
|
|
126
|
+
}),
|
|
127
|
+
actionTaken: field.string({
|
|
128
|
+
isOptional: true,
|
|
129
|
+
description: 'Action that caused transition (e.g., "approve")'
|
|
130
|
+
}),
|
|
131
|
+
transitionedTo: field.string({
|
|
132
|
+
isOptional: true,
|
|
133
|
+
description: "Step key transitioned to"
|
|
134
|
+
}),
|
|
135
|
+
executedBy: field.string({
|
|
136
|
+
isOptional: true,
|
|
137
|
+
description: "User who completed this step"
|
|
138
|
+
}),
|
|
139
|
+
errorMessage: field.string({ isOptional: true }),
|
|
140
|
+
errorDetails: field.json({ isOptional: true }),
|
|
141
|
+
retryCount: field.int({ default: 0 }),
|
|
142
|
+
createdAt: field.createdAt(),
|
|
143
|
+
updatedAt: field.updatedAt(),
|
|
144
|
+
startedAt: field.dateTime({ isOptional: true }),
|
|
145
|
+
completedAt: field.dateTime({ isOptional: true }),
|
|
146
|
+
workflowInstance: field.belongsTo("WorkflowInstance", ["workflowInstanceId"], ["id"], { onDelete: "Cascade" }),
|
|
147
|
+
workflowStep: field.belongsTo("WorkflowStep", ["workflowStepId"], ["id"])
|
|
148
|
+
},
|
|
149
|
+
indexes: [
|
|
150
|
+
index.on(["workflowInstanceId", "executionOrder"]),
|
|
151
|
+
index.on(["workflowInstanceId", "workflowStepId"]),
|
|
152
|
+
index.on(["status"])
|
|
153
|
+
],
|
|
154
|
+
enums: [StepExecutionStatusEnum]
|
|
155
|
+
});
|
|
156
|
+
export {
|
|
157
|
+
WorkflowInstanceEntity,
|
|
158
|
+
StepExecutionStatusEnum,
|
|
159
|
+
StepExecutionEntity,
|
|
160
|
+
InstanceStatusEnum
|
|
161
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/entities/step.ts
|
|
2
|
+
import {
|
|
3
|
+
defineEntity,
|
|
4
|
+
defineEntityEnum,
|
|
5
|
+
field,
|
|
6
|
+
index
|
|
7
|
+
} from "@contractspec/lib.schema";
|
|
8
|
+
var StepTypeEnum = defineEntityEnum({
|
|
9
|
+
name: "StepType",
|
|
10
|
+
values: [
|
|
11
|
+
"START",
|
|
12
|
+
"APPROVAL",
|
|
13
|
+
"TASK",
|
|
14
|
+
"CONDITION",
|
|
15
|
+
"PARALLEL",
|
|
16
|
+
"WAIT",
|
|
17
|
+
"ACTION",
|
|
18
|
+
"END"
|
|
19
|
+
],
|
|
20
|
+
schema: "workflow",
|
|
21
|
+
description: "Type of workflow step."
|
|
22
|
+
});
|
|
23
|
+
var ApprovalModeEnum = defineEntityEnum({
|
|
24
|
+
name: "ApprovalMode",
|
|
25
|
+
values: [
|
|
26
|
+
"ANY",
|
|
27
|
+
"ALL",
|
|
28
|
+
"MAJORITY",
|
|
29
|
+
"SEQUENTIAL"
|
|
30
|
+
],
|
|
31
|
+
schema: "workflow",
|
|
32
|
+
description: "How multiple approvers are handled."
|
|
33
|
+
});
|
|
34
|
+
var WorkflowStepEntity = defineEntity({
|
|
35
|
+
name: "WorkflowStep",
|
|
36
|
+
description: "A single step/state in a workflow definition.",
|
|
37
|
+
schema: "workflow",
|
|
38
|
+
map: "workflow_step",
|
|
39
|
+
fields: {
|
|
40
|
+
id: field.id({ description: "Unique step ID" }),
|
|
41
|
+
workflowDefinitionId: field.foreignKey(),
|
|
42
|
+
name: field.string({ description: "Human-readable step name" }),
|
|
43
|
+
key: field.string({
|
|
44
|
+
description: 'Unique key within workflow (e.g., "manager_approval")'
|
|
45
|
+
}),
|
|
46
|
+
description: field.string({ isOptional: true }),
|
|
47
|
+
type: field.enum("StepType", { default: "TASK" }),
|
|
48
|
+
position: field.int({ default: 0, description: "Order for display" }),
|
|
49
|
+
transitions: field.json({ description: "Map of action -> next step key" }),
|
|
50
|
+
approvalMode: field.enum("ApprovalMode", {
|
|
51
|
+
default: "ANY",
|
|
52
|
+
isOptional: true
|
|
53
|
+
}),
|
|
54
|
+
approverRoles: field.string({
|
|
55
|
+
isArray: true,
|
|
56
|
+
description: "Roles that can approve"
|
|
57
|
+
}),
|
|
58
|
+
approverUserIds: field.string({
|
|
59
|
+
isArray: true,
|
|
60
|
+
description: "Specific users that can approve"
|
|
61
|
+
}),
|
|
62
|
+
escalationConfig: field.json({
|
|
63
|
+
isOptional: true,
|
|
64
|
+
description: "Escalation rules"
|
|
65
|
+
}),
|
|
66
|
+
assigneeRoles: field.string({
|
|
67
|
+
isArray: true,
|
|
68
|
+
description: "Roles that can be assigned"
|
|
69
|
+
}),
|
|
70
|
+
taskTemplate: field.json({
|
|
71
|
+
isOptional: true,
|
|
72
|
+
description: "Template for task creation"
|
|
73
|
+
}),
|
|
74
|
+
conditionExpression: field.string({
|
|
75
|
+
isOptional: true,
|
|
76
|
+
description: "Expression for branching"
|
|
77
|
+
}),
|
|
78
|
+
waitDuration: field.int({
|
|
79
|
+
isOptional: true,
|
|
80
|
+
description: "Wait duration in seconds"
|
|
81
|
+
}),
|
|
82
|
+
waitForEvent: field.string({
|
|
83
|
+
isOptional: true,
|
|
84
|
+
description: "Event name to wait for"
|
|
85
|
+
}),
|
|
86
|
+
actionType: field.string({
|
|
87
|
+
isOptional: true,
|
|
88
|
+
description: "Action to execute"
|
|
89
|
+
}),
|
|
90
|
+
actionConfig: field.json({
|
|
91
|
+
isOptional: true,
|
|
92
|
+
description: "Action parameters"
|
|
93
|
+
}),
|
|
94
|
+
timeoutSeconds: field.int({
|
|
95
|
+
isOptional: true,
|
|
96
|
+
description: "Step timeout"
|
|
97
|
+
}),
|
|
98
|
+
slaSeconds: field.int({ isOptional: true, description: "SLA deadline" }),
|
|
99
|
+
notifyOnEnter: field.json({
|
|
100
|
+
isOptional: true,
|
|
101
|
+
description: "Notification config when entering step"
|
|
102
|
+
}),
|
|
103
|
+
notifyOnExit: field.json({
|
|
104
|
+
isOptional: true,
|
|
105
|
+
description: "Notification config when exiting step"
|
|
106
|
+
}),
|
|
107
|
+
metadata: field.json({ isOptional: true }),
|
|
108
|
+
createdAt: field.createdAt(),
|
|
109
|
+
updatedAt: field.updatedAt(),
|
|
110
|
+
workflowDefinition: field.belongsTo("WorkflowDefinition", ["workflowDefinitionId"], ["id"], { onDelete: "Cascade" }),
|
|
111
|
+
executions: field.hasMany("StepExecution")
|
|
112
|
+
},
|
|
113
|
+
indexes: [
|
|
114
|
+
index.unique(["workflowDefinitionId", "key"]),
|
|
115
|
+
index.on(["workflowDefinitionId", "position"]),
|
|
116
|
+
index.on(["type"])
|
|
117
|
+
],
|
|
118
|
+
enums: [StepTypeEnum, ApprovalModeEnum]
|
|
119
|
+
});
|
|
120
|
+
export {
|
|
121
|
+
WorkflowStepEntity,
|
|
122
|
+
StepTypeEnum,
|
|
123
|
+
ApprovalModeEnum
|
|
124
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/entities/workflow.ts
|
|
2
|
+
import {
|
|
3
|
+
defineEntity,
|
|
4
|
+
defineEntityEnum,
|
|
5
|
+
field,
|
|
6
|
+
index
|
|
7
|
+
} from "@contractspec/lib.schema";
|
|
8
|
+
var WorkflowStatusEnum = defineEntityEnum({
|
|
9
|
+
name: "WorkflowStatus",
|
|
10
|
+
values: ["DRAFT", "ACTIVE", "DEPRECATED", "ARCHIVED"],
|
|
11
|
+
schema: "workflow",
|
|
12
|
+
description: "Status of a workflow definition."
|
|
13
|
+
});
|
|
14
|
+
var WorkflowTriggerTypeEnum = defineEntityEnum({
|
|
15
|
+
name: "WorkflowTriggerType",
|
|
16
|
+
values: ["MANUAL", "EVENT", "SCHEDULED", "API"],
|
|
17
|
+
schema: "workflow",
|
|
18
|
+
description: "What triggers workflow instantiation."
|
|
19
|
+
});
|
|
20
|
+
var WorkflowDefinitionEntity = defineEntity({
|
|
21
|
+
name: "WorkflowDefinition",
|
|
22
|
+
description: "A workflow blueprint that defines the process structure.",
|
|
23
|
+
schema: "workflow",
|
|
24
|
+
map: "workflow_definition",
|
|
25
|
+
fields: {
|
|
26
|
+
id: field.id({ description: "Unique workflow definition ID" }),
|
|
27
|
+
name: field.string({ description: "Human-readable workflow name" }),
|
|
28
|
+
key: field.string({
|
|
29
|
+
description: 'Unique key for referencing (e.g., "purchase_approval")'
|
|
30
|
+
}),
|
|
31
|
+
description: field.string({
|
|
32
|
+
isOptional: true,
|
|
33
|
+
description: "Detailed description"
|
|
34
|
+
}),
|
|
35
|
+
version: field.int({
|
|
36
|
+
default: 1,
|
|
37
|
+
description: "Version number for versioning definitions"
|
|
38
|
+
}),
|
|
39
|
+
status: field.enum("WorkflowStatus", { default: "DRAFT" }),
|
|
40
|
+
triggerType: field.enum("WorkflowTriggerType", { default: "MANUAL" }),
|
|
41
|
+
triggerConfig: field.json({
|
|
42
|
+
isOptional: true,
|
|
43
|
+
description: "Trigger-specific configuration"
|
|
44
|
+
}),
|
|
45
|
+
initialStepId: field.string({
|
|
46
|
+
isOptional: true,
|
|
47
|
+
description: "First step when workflow starts"
|
|
48
|
+
}),
|
|
49
|
+
featureFlagKey: field.string({
|
|
50
|
+
isOptional: true,
|
|
51
|
+
description: "Feature flag to control availability"
|
|
52
|
+
}),
|
|
53
|
+
settings: field.json({
|
|
54
|
+
isOptional: true,
|
|
55
|
+
description: "Workflow-wide settings"
|
|
56
|
+
}),
|
|
57
|
+
metadata: field.json({ isOptional: true, description: "Custom metadata" }),
|
|
58
|
+
organizationId: field.foreignKey({ description: "Owning organization" }),
|
|
59
|
+
createdBy: field.foreignKey({
|
|
60
|
+
description: "User who created this workflow"
|
|
61
|
+
}),
|
|
62
|
+
createdAt: field.createdAt(),
|
|
63
|
+
updatedAt: field.updatedAt(),
|
|
64
|
+
publishedAt: field.dateTime({
|
|
65
|
+
isOptional: true,
|
|
66
|
+
description: "When workflow was activated"
|
|
67
|
+
}),
|
|
68
|
+
steps: field.hasMany("WorkflowStep"),
|
|
69
|
+
instances: field.hasMany("WorkflowInstance")
|
|
70
|
+
},
|
|
71
|
+
indexes: [
|
|
72
|
+
index.unique(["organizationId", "key", "version"]),
|
|
73
|
+
index.on(["organizationId", "status"]),
|
|
74
|
+
index.on(["key", "version"])
|
|
75
|
+
],
|
|
76
|
+
enums: [WorkflowStatusEnum, WorkflowTriggerTypeEnum]
|
|
77
|
+
});
|
|
78
|
+
export {
|
|
79
|
+
WorkflowTriggerTypeEnum,
|
|
80
|
+
WorkflowStatusEnum,
|
|
81
|
+
WorkflowDefinitionEntity
|
|
82
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/example.ts
|
|
2
|
+
import { defineExample } from "@contractspec/lib.contracts";
|
|
3
|
+
var example = defineExample({
|
|
4
|
+
meta: {
|
|
5
|
+
key: "workflow-system",
|
|
6
|
+
version: "1.0.0",
|
|
7
|
+
title: "Workflow / Approval System",
|
|
8
|
+
description: "State-machine driven approvals with RBAC, audit trail, notifications, and jobs.",
|
|
9
|
+
kind: "template",
|
|
10
|
+
visibility: "public",
|
|
11
|
+
stability: "experimental",
|
|
12
|
+
owners: ["@platform.core"],
|
|
13
|
+
tags: ["workflow", "approval", "state-machine", "rbac"]
|
|
14
|
+
},
|
|
15
|
+
docs: {
|
|
16
|
+
rootDocId: "docs.examples.workflow-system",
|
|
17
|
+
goalDocId: "docs.examples.workflow-system.goal",
|
|
18
|
+
usageDocId: "docs.examples.workflow-system.usage",
|
|
19
|
+
constraintsDocId: "docs.examples.workflow-system.constraints"
|
|
20
|
+
},
|
|
21
|
+
entrypoints: {
|
|
22
|
+
packageName: "@contractspec/example.workflow-system",
|
|
23
|
+
feature: "./feature",
|
|
24
|
+
contracts: "./contracts",
|
|
25
|
+
presentations: "./presentations",
|
|
26
|
+
handlers: "./handlers",
|
|
27
|
+
docs: "./docs"
|
|
28
|
+
},
|
|
29
|
+
surfaces: {
|
|
30
|
+
templates: true,
|
|
31
|
+
sandbox: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
modes: ["playground", "specs", "builder", "markdown", "evolution"]
|
|
34
|
+
},
|
|
35
|
+
studio: { enabled: true, installable: true },
|
|
36
|
+
mcp: { enabled: true }
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
var example_default = example;
|
|
40
|
+
export {
|
|
41
|
+
example_default as default
|
|
42
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// src/handlers/workflow.handlers.ts
|
|
2
|
+
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
3
|
+
var { generateId } = web;
|
|
4
|
+
function rowToDefinition(row) {
|
|
5
|
+
return {
|
|
6
|
+
id: row.id,
|
|
7
|
+
projectId: row.projectId,
|
|
8
|
+
organizationId: row.organizationId,
|
|
9
|
+
name: row.name,
|
|
10
|
+
description: row.description ?? undefined,
|
|
11
|
+
type: row.type,
|
|
12
|
+
status: row.status,
|
|
13
|
+
createdAt: new Date(row.createdAt),
|
|
14
|
+
updatedAt: new Date(row.updatedAt)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function rowToStep(row) {
|
|
18
|
+
return {
|
|
19
|
+
id: row.id,
|
|
20
|
+
definitionId: row.definitionId,
|
|
21
|
+
name: row.name,
|
|
22
|
+
description: row.description ?? undefined,
|
|
23
|
+
stepOrder: row.stepOrder,
|
|
24
|
+
type: row.type,
|
|
25
|
+
requiredRoles: row.requiredRoles ? JSON.parse(row.requiredRoles) : [],
|
|
26
|
+
autoApproveCondition: row.autoApproveCondition ?? undefined,
|
|
27
|
+
timeoutHours: row.timeoutHours ?? undefined,
|
|
28
|
+
createdAt: new Date(row.createdAt)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function rowToInstance(row) {
|
|
32
|
+
return {
|
|
33
|
+
id: row.id,
|
|
34
|
+
projectId: row.projectId,
|
|
35
|
+
definitionId: row.definitionId,
|
|
36
|
+
status: row.status,
|
|
37
|
+
currentStepId: row.currentStepId ?? undefined,
|
|
38
|
+
data: row.data ? JSON.parse(row.data) : undefined,
|
|
39
|
+
requestedBy: row.requestedBy,
|
|
40
|
+
startedAt: new Date(row.startedAt),
|
|
41
|
+
completedAt: row.completedAt ? new Date(row.completedAt) : undefined
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function rowToApproval(row) {
|
|
45
|
+
return {
|
|
46
|
+
id: row.id,
|
|
47
|
+
instanceId: row.instanceId,
|
|
48
|
+
stepId: row.stepId,
|
|
49
|
+
status: row.status,
|
|
50
|
+
actorId: row.actorId ?? undefined,
|
|
51
|
+
comment: row.comment ?? undefined,
|
|
52
|
+
decidedAt: row.decidedAt ? new Date(row.decidedAt) : undefined,
|
|
53
|
+
createdAt: new Date(row.createdAt)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function createWorkflowHandlers(db) {
|
|
57
|
+
async function listDefinitions(input) {
|
|
58
|
+
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
59
|
+
let whereClause = "WHERE projectId = ?";
|
|
60
|
+
const params = [projectId];
|
|
61
|
+
if (status && status !== "all") {
|
|
62
|
+
whereClause += " AND status = ?";
|
|
63
|
+
params.push(status);
|
|
64
|
+
}
|
|
65
|
+
if (search) {
|
|
66
|
+
whereClause += " AND name LIKE ?";
|
|
67
|
+
params.push(`%${search}%`);
|
|
68
|
+
}
|
|
69
|
+
const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params)).rows;
|
|
70
|
+
const total = countResult[0]?.count ?? 0;
|
|
71
|
+
const rows = (await db.query(`SELECT * FROM workflow_definition ${whereClause} ORDER BY updatedAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
72
|
+
return {
|
|
73
|
+
definitions: rows.map(rowToDefinition),
|
|
74
|
+
total
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function createDefinition(input, context) {
|
|
78
|
+
const id = generateId("wfdef");
|
|
79
|
+
const now = new Date().toISOString();
|
|
80
|
+
await db.execute(`INSERT INTO workflow_definition (id, projectId, organizationId, name, description, type, status, createdAt, updatedAt)
|
|
81
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
82
|
+
id,
|
|
83
|
+
context.projectId,
|
|
84
|
+
context.organizationId,
|
|
85
|
+
input.name,
|
|
86
|
+
input.description ?? null,
|
|
87
|
+
input.type ?? "APPROVAL",
|
|
88
|
+
"DRAFT",
|
|
89
|
+
now,
|
|
90
|
+
now
|
|
91
|
+
]);
|
|
92
|
+
const rows = (await db.query(`SELECT * FROM workflow_definition WHERE id = ?`, [id])).rows;
|
|
93
|
+
return rowToDefinition(rows[0]);
|
|
94
|
+
}
|
|
95
|
+
async function addStep(input) {
|
|
96
|
+
const id = generateId("wfstep");
|
|
97
|
+
const now = new Date().toISOString();
|
|
98
|
+
const maxOrderResult = (await db.query(`SELECT MAX(stepOrder) as maxOrder FROM workflow_step WHERE definitionId = ?`, [input.definitionId])).rows;
|
|
99
|
+
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
100
|
+
await db.execute(`INSERT INTO workflow_step (id, definitionId, name, description, stepOrder, type, requiredRoles, autoApproveCondition, timeoutHours, createdAt)
|
|
101
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
102
|
+
id,
|
|
103
|
+
input.definitionId,
|
|
104
|
+
input.name,
|
|
105
|
+
input.description ?? null,
|
|
106
|
+
nextOrder,
|
|
107
|
+
input.type ?? "APPROVAL",
|
|
108
|
+
JSON.stringify(input.requiredRoles),
|
|
109
|
+
input.autoApproveCondition ?? null,
|
|
110
|
+
input.timeoutHours ?? null,
|
|
111
|
+
now
|
|
112
|
+
]);
|
|
113
|
+
const rows = (await db.query(`SELECT * FROM workflow_step WHERE id = ?`, [id])).rows;
|
|
114
|
+
return rowToStep(rows[0]);
|
|
115
|
+
}
|
|
116
|
+
async function getSteps(definitionId) {
|
|
117
|
+
const rows = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? ORDER BY stepOrder`, [definitionId])).rows;
|
|
118
|
+
return rows.map(rowToStep);
|
|
119
|
+
}
|
|
120
|
+
async function listInstances(input) {
|
|
121
|
+
const {
|
|
122
|
+
projectId,
|
|
123
|
+
definitionId,
|
|
124
|
+
status,
|
|
125
|
+
requestedBy,
|
|
126
|
+
limit = 20,
|
|
127
|
+
offset = 0
|
|
128
|
+
} = input;
|
|
129
|
+
let whereClause = "WHERE projectId = ?";
|
|
130
|
+
const params = [projectId];
|
|
131
|
+
if (definitionId) {
|
|
132
|
+
whereClause += " AND definitionId = ?";
|
|
133
|
+
params.push(definitionId);
|
|
134
|
+
}
|
|
135
|
+
if (status && status !== "all") {
|
|
136
|
+
whereClause += " AND status = ?";
|
|
137
|
+
params.push(status);
|
|
138
|
+
}
|
|
139
|
+
if (requestedBy) {
|
|
140
|
+
whereClause += " AND requestedBy = ?";
|
|
141
|
+
params.push(requestedBy);
|
|
142
|
+
}
|
|
143
|
+
const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params)).rows;
|
|
144
|
+
const total = countResult[0]?.count ?? 0;
|
|
145
|
+
const rows = (await db.query(`SELECT * FROM workflow_instance ${whereClause} ORDER BY startedAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
146
|
+
return {
|
|
147
|
+
instances: rows.map(rowToInstance),
|
|
148
|
+
total
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function startInstance(input, context) {
|
|
152
|
+
const id = generateId("wfinst");
|
|
153
|
+
const now = new Date().toISOString();
|
|
154
|
+
const steps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? ORDER BY stepOrder LIMIT 1`, [input.definitionId])).rows;
|
|
155
|
+
const firstStepId = steps[0]?.id ?? null;
|
|
156
|
+
await db.execute(`INSERT INTO workflow_instance (id, projectId, definitionId, status, currentStepId, data, requestedBy, startedAt)
|
|
157
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
158
|
+
id,
|
|
159
|
+
context.projectId,
|
|
160
|
+
input.definitionId,
|
|
161
|
+
firstStepId ? "IN_PROGRESS" : "PENDING",
|
|
162
|
+
firstStepId,
|
|
163
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
164
|
+
context.requestedBy,
|
|
165
|
+
now
|
|
166
|
+
]);
|
|
167
|
+
if (firstStepId) {
|
|
168
|
+
await db.execute(`INSERT INTO workflow_approval (id, instanceId, stepId, status, createdAt)
|
|
169
|
+
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
170
|
+
}
|
|
171
|
+
const rows = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [id])).rows;
|
|
172
|
+
return rowToInstance(rows[0]);
|
|
173
|
+
}
|
|
174
|
+
async function approveStep(input, context) {
|
|
175
|
+
const now = new Date().toISOString();
|
|
176
|
+
const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
177
|
+
input.instanceId
|
|
178
|
+
])).rows;
|
|
179
|
+
if (!instances[0]) {
|
|
180
|
+
throw new Error("NOT_FOUND");
|
|
181
|
+
}
|
|
182
|
+
const instance = instances[0];
|
|
183
|
+
await db.execute(`UPDATE workflow_approval SET status = 'APPROVED', actorId = ?, comment = ?, decidedAt = ?
|
|
184
|
+
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
185
|
+
context.actorId,
|
|
186
|
+
input.comment ?? null,
|
|
187
|
+
now,
|
|
188
|
+
input.instanceId,
|
|
189
|
+
instance.currentStepId
|
|
190
|
+
]);
|
|
191
|
+
const currentStep = (await db.query(`SELECT * FROM workflow_step WHERE id = ?`, [
|
|
192
|
+
instance.currentStepId
|
|
193
|
+
])).rows;
|
|
194
|
+
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;
|
|
195
|
+
if (nextSteps[0]) {
|
|
196
|
+
await db.execute(`UPDATE workflow_instance SET currentStepId = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
197
|
+
await db.execute(`INSERT INTO workflow_approval (id, instanceId, stepId, status, createdAt)
|
|
198
|
+
VALUES (?, ?, ?, ?, ?)`, [
|
|
199
|
+
generateId("wfappr"),
|
|
200
|
+
input.instanceId,
|
|
201
|
+
nextSteps[0].id,
|
|
202
|
+
"PENDING",
|
|
203
|
+
now
|
|
204
|
+
]);
|
|
205
|
+
} else {
|
|
206
|
+
await db.execute(`UPDATE workflow_instance SET status = 'COMPLETED', currentStepId = NULL, completedAt = ? WHERE id = ?`, [now, input.instanceId]);
|
|
207
|
+
}
|
|
208
|
+
const updated = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
209
|
+
input.instanceId
|
|
210
|
+
])).rows;
|
|
211
|
+
return rowToInstance(updated[0]);
|
|
212
|
+
}
|
|
213
|
+
async function rejectStep(input, context) {
|
|
214
|
+
const now = new Date().toISOString();
|
|
215
|
+
const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
216
|
+
input.instanceId
|
|
217
|
+
])).rows;
|
|
218
|
+
if (!instances[0]) {
|
|
219
|
+
throw new Error("NOT_FOUND");
|
|
220
|
+
}
|
|
221
|
+
await db.execute(`UPDATE workflow_approval SET status = 'REJECTED', actorId = ?, comment = ?, decidedAt = ?
|
|
222
|
+
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
223
|
+
context.actorId,
|
|
224
|
+
input.reason,
|
|
225
|
+
now,
|
|
226
|
+
input.instanceId,
|
|
227
|
+
instances[0].currentStepId
|
|
228
|
+
]);
|
|
229
|
+
await db.execute(`UPDATE workflow_instance SET status = 'REJECTED', completedAt = ? WHERE id = ?`, [now, input.instanceId]);
|
|
230
|
+
const updated = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
|
|
231
|
+
input.instanceId
|
|
232
|
+
])).rows;
|
|
233
|
+
return rowToInstance(updated[0]);
|
|
234
|
+
}
|
|
235
|
+
async function getApprovals(instanceId) {
|
|
236
|
+
const rows = (await db.query(`SELECT * FROM workflow_approval WHERE instanceId = ? ORDER BY createdAt`, [instanceId])).rows;
|
|
237
|
+
return rows.map(rowToApproval);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
listDefinitions,
|
|
241
|
+
createDefinition,
|
|
242
|
+
addStep,
|
|
243
|
+
getSteps,
|
|
244
|
+
listInstances,
|
|
245
|
+
startInstance,
|
|
246
|
+
approveStep,
|
|
247
|
+
rejectStep,
|
|
248
|
+
getApprovals
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export {
|
|
252
|
+
createWorkflowHandlers
|
|
253
|
+
};
|