@open-mercato/shared 0.6.2-develop.3400.2.d948268f50 → 0.6.2-develop.3406.1.2b403f40da
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/.turbo/turbo-build.log +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/modules/workflows/builder.js +47 -0
- package/dist/modules/workflows/builder.js.map +7 -0
- package/dist/modules/workflows/factory.js +14 -0
- package/dist/modules/workflows/factory.js.map +7 -0
- package/dist/modules/workflows/index.js +7 -0
- package/dist/modules/workflows/index.js.map +7 -0
- package/dist/modules/workflows/types.js +1 -0
- package/dist/modules/workflows/types.js.map +7 -0
- package/dist/modules/workflows.js +2 -0
- package/dist/modules/workflows.js.map +7 -0
- package/package.json +2 -2
- package/src/modules/workflows/__tests__/builder.test.ts +394 -0
- package/src/modules/workflows/builder.ts +144 -0
- package/src/modules/workflows/factory.ts +23 -0
- package/src/modules/workflows/index.ts +15 -0
- package/src/modules/workflows/types.ts +190 -0
- package/src/modules/workflows.ts +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[build:shared] found
|
|
1
|
+
[build:shared] found 209 entry points
|
|
2
2
|
[build:shared] built successfully
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.2-develop.
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.2-develop.3406.1.2b403f40da'\nexport const appVersion = APP_VERSION\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function defineWorkflow(config) {
|
|
2
|
+
const steps = config.steps.map((step) => ({
|
|
3
|
+
stepId: step.stepId,
|
|
4
|
+
stepName: step.stepName,
|
|
5
|
+
stepType: step.stepType,
|
|
6
|
+
...step.description !== void 0 && { description: step.description },
|
|
7
|
+
...step.config !== void 0 && { config: step.config },
|
|
8
|
+
...step.userTaskConfig !== void 0 && { userTaskConfig: step.userTaskConfig },
|
|
9
|
+
...step.subWorkflowConfig !== void 0 && { subWorkflowConfig: step.subWorkflowConfig },
|
|
10
|
+
...step.signalConfig !== void 0 && { signalConfig: step.signalConfig },
|
|
11
|
+
...step.activities !== void 0 && { activities: step.activities },
|
|
12
|
+
...step.timeout !== void 0 && { timeout: step.timeout },
|
|
13
|
+
...step.retryPolicy !== void 0 && { retryPolicy: step.retryPolicy },
|
|
14
|
+
...step.preConditions !== void 0 && { preConditions: step.preConditions }
|
|
15
|
+
}));
|
|
16
|
+
const transitions = config.transitions.map((t) => ({
|
|
17
|
+
transitionId: t.transitionId,
|
|
18
|
+
fromStepId: t.fromStepId,
|
|
19
|
+
toStepId: t.toStepId,
|
|
20
|
+
trigger: t.trigger,
|
|
21
|
+
...t.transitionName !== void 0 && { transitionName: t.transitionName },
|
|
22
|
+
...t.preConditions !== void 0 && { preConditions: t.preConditions },
|
|
23
|
+
...t.postConditions !== void 0 && { postConditions: t.postConditions },
|
|
24
|
+
...t.activities !== void 0 && { activities: t.activities },
|
|
25
|
+
...t.continueOnActivityFailure !== void 0 && { continueOnActivityFailure: t.continueOnActivityFailure },
|
|
26
|
+
...t.priority !== void 0 && { priority: t.priority }
|
|
27
|
+
}));
|
|
28
|
+
return {
|
|
29
|
+
workflowId: config.workflowId,
|
|
30
|
+
workflowName: config.workflowName,
|
|
31
|
+
description: config.description ?? null,
|
|
32
|
+
version: config.version ?? 1,
|
|
33
|
+
enabled: config.enabled ?? true,
|
|
34
|
+
metadata: config.metadata ?? null,
|
|
35
|
+
definition: {
|
|
36
|
+
steps,
|
|
37
|
+
transitions,
|
|
38
|
+
...config.triggers !== void 0 && { triggers: config.triggers }
|
|
39
|
+
},
|
|
40
|
+
moduleId: ""
|
|
41
|
+
// Set by generator/factory at registration time
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
defineWorkflow
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/workflows/builder.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Code-Based Workflow Definitions \u2014 Builder API\n *\n * Provides compile-time type safety for step IDs in transitions.\n * The `const` modifier on TSteps tells TypeScript to infer literal types\n * for stepId values. Transitions constrain fromStepId/toStepId to this union.\n *\n * @example\n * ```typescript\n * const workflow = defineWorkflow({\n * workflowId: 'sales.order-approval',\n * workflowName: 'Order Approval',\n * steps: [\n * { stepId: 'start', stepName: 'Start', stepType: 'START' },\n * { stepId: 'review', stepName: 'Review', stepType: 'USER_TASK' },\n * { stepId: 'end', stepName: 'End', stepType: 'END' },\n * ] as const,\n * transitions: [\n * { transitionId: 'to-review', fromStepId: 'start', toStepId: 'review', trigger: 'auto' },\n * // Compile error: '\"typo\"' is not assignable to '\"start\" | \"review\" | \"end\"'\n * // { transitionId: 'bad', fromStepId: 'typo', toStepId: 'end', trigger: 'auto' },\n * ],\n * })\n * ```\n */\n\nimport type {\n CodeWorkflowDefinition,\n CodeStepDefinition,\n CodeTransitionDefinition,\n CodeTriggerDefinition,\n CodeActivityDefinition,\n WorkflowStepType,\n TransitionTrigger,\n} from './types'\n\n// ============================================================================\n// Builder Config Type (with const generic for type inference)\n// ============================================================================\n\ninterface WorkflowBuilderConfig<TSteps extends readonly StepInput<string>[]> {\n workflowId: string\n workflowName: string\n description?: string\n version?: number\n enabled?: boolean\n metadata?: { tags?: string[]; category?: string; icon?: string }\n steps: TSteps\n transitions: TransitionInput<TSteps[number]['stepId']>[]\n triggers?: CodeTriggerDefinition[]\n}\n\ninterface StepInput<TStepId extends string> {\n stepId: TStepId\n stepName: string\n stepType: WorkflowStepType\n description?: string\n config?: Record<string, unknown>\n userTaskConfig?: CodeStepDefinition['userTaskConfig']\n subWorkflowConfig?: CodeStepDefinition['subWorkflowConfig']\n signalConfig?: CodeStepDefinition['signalConfig']\n activities?: CodeActivityDefinition[]\n timeout?: string\n retryPolicy?: { maxAttempts: number; backoffMs: number }\n preConditions?: CodeStepDefinition['preConditions']\n}\n\ninterface TransitionInput<TStepId extends string> {\n transitionId: string\n fromStepId: TStepId\n toStepId: TStepId\n transitionName?: string\n trigger: TransitionTrigger\n preConditions?: CodeTransitionDefinition['preConditions']\n postConditions?: CodeTransitionDefinition['postConditions']\n activities?: CodeActivityDefinition[]\n continueOnActivityFailure?: boolean\n priority?: number\n}\n\n// ============================================================================\n// defineWorkflow \u2014 Main Builder Function\n// ============================================================================\n\n/**\n * Define a code-based workflow with compile-time step ID safety.\n *\n * Use `as const` on the steps array for full type inference:\n * ```typescript\n * defineWorkflow({\n * steps: [...] as const,\n * transitions: [...], // fromStepId/toStepId auto-constrained\n * })\n * ```\n *\n * The returned `CodeWorkflowDefinition` has `moduleId` set to empty string.\n * The generator sets the correct `moduleId` at registration time.\n */\nexport function defineWorkflow<const TSteps extends readonly StepInput<string>[]>(\n config: WorkflowBuilderConfig<TSteps>,\n): CodeWorkflowDefinition {\n const steps: CodeStepDefinition[] = config.steps.map((step) => ({\n stepId: step.stepId,\n stepName: step.stepName,\n stepType: step.stepType,\n ...(step.description !== undefined && { description: step.description }),\n ...(step.config !== undefined && { config: step.config }),\n ...(step.userTaskConfig !== undefined && { userTaskConfig: step.userTaskConfig }),\n ...(step.subWorkflowConfig !== undefined && { subWorkflowConfig: step.subWorkflowConfig }),\n ...(step.signalConfig !== undefined && { signalConfig: step.signalConfig }),\n ...(step.activities !== undefined && { activities: step.activities }),\n ...(step.timeout !== undefined && { timeout: step.timeout }),\n ...(step.retryPolicy !== undefined && { retryPolicy: step.retryPolicy }),\n ...(step.preConditions !== undefined && { preConditions: step.preConditions }),\n }))\n\n const transitions: CodeTransitionDefinition[] = config.transitions.map((t) => ({\n transitionId: t.transitionId,\n fromStepId: t.fromStepId,\n toStepId: t.toStepId,\n trigger: t.trigger,\n ...(t.transitionName !== undefined && { transitionName: t.transitionName }),\n ...(t.preConditions !== undefined && { preConditions: t.preConditions }),\n ...(t.postConditions !== undefined && { postConditions: t.postConditions }),\n ...(t.activities !== undefined && { activities: t.activities }),\n ...(t.continueOnActivityFailure !== undefined && { continueOnActivityFailure: t.continueOnActivityFailure }),\n ...(t.priority !== undefined && { priority: t.priority }),\n }))\n\n return {\n workflowId: config.workflowId,\n workflowName: config.workflowName,\n description: config.description ?? null,\n version: config.version ?? 1,\n enabled: config.enabled ?? true,\n metadata: config.metadata ?? null,\n definition: {\n steps,\n transitions,\n ...(config.triggers !== undefined && { triggers: config.triggers }),\n },\n moduleId: '', // Set by generator/factory at registration time\n }\n}\n"],
|
|
5
|
+
"mappings": "AAkGO,SAAS,eACd,QACwB;AACxB,QAAM,QAA8B,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC9D,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,GAAI,KAAK,gBAAgB,UAAa,EAAE,aAAa,KAAK,YAAY;AAAA,IACtE,GAAI,KAAK,WAAW,UAAa,EAAE,QAAQ,KAAK,OAAO;AAAA,IACvD,GAAI,KAAK,mBAAmB,UAAa,EAAE,gBAAgB,KAAK,eAAe;AAAA,IAC/E,GAAI,KAAK,sBAAsB,UAAa,EAAE,mBAAmB,KAAK,kBAAkB;AAAA,IACxF,GAAI,KAAK,iBAAiB,UAAa,EAAE,cAAc,KAAK,aAAa;AAAA,IACzE,GAAI,KAAK,eAAe,UAAa,EAAE,YAAY,KAAK,WAAW;AAAA,IACnE,GAAI,KAAK,YAAY,UAAa,EAAE,SAAS,KAAK,QAAQ;AAAA,IAC1D,GAAI,KAAK,gBAAgB,UAAa,EAAE,aAAa,KAAK,YAAY;AAAA,IACtE,GAAI,KAAK,kBAAkB,UAAa,EAAE,eAAe,KAAK,cAAc;AAAA,EAC9E,EAAE;AAEF,QAAM,cAA0C,OAAO,YAAY,IAAI,CAAC,OAAO;AAAA,IAC7E,cAAc,EAAE;AAAA,IAChB,YAAY,EAAE;AAAA,IACd,UAAU,EAAE;AAAA,IACZ,SAAS,EAAE;AAAA,IACX,GAAI,EAAE,mBAAmB,UAAa,EAAE,gBAAgB,EAAE,eAAe;AAAA,IACzE,GAAI,EAAE,kBAAkB,UAAa,EAAE,eAAe,EAAE,cAAc;AAAA,IACtE,GAAI,EAAE,mBAAmB,UAAa,EAAE,gBAAgB,EAAE,eAAe;AAAA,IACzE,GAAI,EAAE,eAAe,UAAa,EAAE,YAAY,EAAE,WAAW;AAAA,IAC7D,GAAI,EAAE,8BAA8B,UAAa,EAAE,2BAA2B,EAAE,0BAA0B;AAAA,IAC1G,GAAI,EAAE,aAAa,UAAa,EAAE,UAAU,EAAE,SAAS;AAAA,EACzD,EAAE;AAEF,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO,eAAe;AAAA,IACnC,SAAS,OAAO,WAAW;AAAA,IAC3B,SAAS,OAAO,WAAW;AAAA,IAC3B,UAAU,OAAO,YAAY;AAAA,IAC7B,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA,GAAI,OAAO,aAAa,UAAa,EAAE,UAAU,OAAO,SAAS;AAAA,IACnE;AAAA,IACA,UAAU;AAAA;AAAA,EACZ;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function createWorkflowsModuleConfig(options) {
|
|
2
|
+
const workflows = options.workflows.map((wf) => ({
|
|
3
|
+
...wf,
|
|
4
|
+
moduleId: options.moduleId
|
|
5
|
+
}));
|
|
6
|
+
return {
|
|
7
|
+
moduleId: options.moduleId,
|
|
8
|
+
workflows
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
createWorkflowsModuleConfig
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/workflows/factory.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Code-Based Workflow Definitions \u2014 Module Config Factory\n *\n * Analogous to createModuleEvents() for events.\n * Creates a WorkflowsModuleConfig that the generator aggregates.\n */\n\nimport type { CodeWorkflowDefinition, WorkflowsModuleConfig } from './types'\n\nexport function createWorkflowsModuleConfig(options: {\n moduleId: string\n workflows: CodeWorkflowDefinition[]\n}): WorkflowsModuleConfig {\n const workflows = options.workflows.map((wf) => ({\n ...wf,\n moduleId: options.moduleId,\n }))\n\n return {\n moduleId: options.moduleId,\n workflows,\n }\n}\n"],
|
|
5
|
+
"mappings": "AASO,SAAS,4BAA4B,SAGlB;AACxB,QAAM,YAAY,QAAQ,UAAU,IAAI,CAAC,QAAQ;AAAA,IAC/C,GAAG;AAAA,IACH,UAAU,QAAQ;AAAA,EACpB,EAAE;AAEF,SAAO;AAAA,IACL,UAAU,QAAQ;AAAA,IAClB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/workflows/index.ts"],
|
|
4
|
+
"sourcesContent": ["export type {\n CodeWorkflowDefinition,\n CodeWorkflowDefinitionData,\n CodeStepDefinition,\n CodeTransitionDefinition,\n CodeTriggerDefinition,\n CodeActivityDefinition,\n WorkflowStepType,\n TransitionTrigger,\n ActivityType,\n WorkflowsModuleConfig,\n} from './types'\n\nexport { defineWorkflow } from './builder'\nexport { createWorkflowsModuleConfig } from './factory'\n"],
|
|
5
|
+
"mappings": "AAaA,SAAS,sBAAsB;AAC/B,SAAS,mCAAmC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/shared",
|
|
3
|
-
"version": "0.6.2-develop.
|
|
3
|
+
"version": "0.6.2-develop.3406.1.2b403f40da",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@mikro-orm/core": "^7.0.15",
|
|
93
93
|
"@mikro-orm/decorators": "^7.0.15",
|
|
94
94
|
"@mikro-orm/postgresql": "^7.0.15",
|
|
95
|
-
"@open-mercato/cache": "0.6.2-develop.
|
|
95
|
+
"@open-mercato/cache": "0.6.2-develop.3406.1.2b403f40da",
|
|
96
96
|
"dotenv": "^17.4.2",
|
|
97
97
|
"rate-limiter-flexible": "^11.1.0",
|
|
98
98
|
"reflect-metadata": "^0.2.2",
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { describe, test, expect } from '@jest/globals'
|
|
2
|
+
import { defineWorkflow } from '../builder'
|
|
3
|
+
import { createWorkflowsModuleConfig } from '../factory'
|
|
4
|
+
|
|
5
|
+
const minimalSteps = [
|
|
6
|
+
{ stepId: 'start', stepName: 'Start', stepType: 'START' as const },
|
|
7
|
+
{ stepId: 'end', stepName: 'End', stepType: 'END' as const },
|
|
8
|
+
] as const
|
|
9
|
+
|
|
10
|
+
const minimalTransitions = [
|
|
11
|
+
{ transitionId: 'to-end', fromStepId: 'start', toStepId: 'end', trigger: 'auto' as const },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
describe('defineWorkflow()', () => {
|
|
15
|
+
test('returns a CodeWorkflowDefinition with correct top-level structure', () => {
|
|
16
|
+
const workflow = defineWorkflow({
|
|
17
|
+
workflowId: 'sales.order-approval',
|
|
18
|
+
workflowName: 'Order Approval',
|
|
19
|
+
steps: minimalSteps,
|
|
20
|
+
transitions: minimalTransitions,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
expect(workflow.workflowId).toBe('sales.order-approval')
|
|
24
|
+
expect(workflow.workflowName).toBe('Order Approval')
|
|
25
|
+
expect(workflow).toHaveProperty('definition')
|
|
26
|
+
expect(workflow).not.toHaveProperty('steps')
|
|
27
|
+
expect(workflow.definition.steps).toBeDefined()
|
|
28
|
+
expect(workflow.definition.transitions).toBeDefined()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('maps steps with correct stepType, stepName, stepId', () => {
|
|
32
|
+
const workflow = defineWorkflow({
|
|
33
|
+
workflowId: 'test.workflow',
|
|
34
|
+
workflowName: 'Test',
|
|
35
|
+
steps: [
|
|
36
|
+
{ stepId: 'start', stepName: 'Start Step', stepType: 'START' as const },
|
|
37
|
+
{ stepId: 'review', stepName: 'Review Step', stepType: 'USER_TASK' as const },
|
|
38
|
+
{ stepId: 'end', stepName: 'End Step', stepType: 'END' as const },
|
|
39
|
+
] as const,
|
|
40
|
+
transitions: [
|
|
41
|
+
{ transitionId: 't1', fromStepId: 'start', toStepId: 'review', trigger: 'auto' as const },
|
|
42
|
+
{ transitionId: 't2', fromStepId: 'review', toStepId: 'end', trigger: 'manual' as const },
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
expect(workflow.definition.steps).toHaveLength(3)
|
|
47
|
+
expect(workflow.definition.steps[0]).toMatchObject({ stepId: 'start', stepName: 'Start Step', stepType: 'START' })
|
|
48
|
+
expect(workflow.definition.steps[1]).toMatchObject({ stepId: 'review', stepName: 'Review Step', stepType: 'USER_TASK' })
|
|
49
|
+
expect(workflow.definition.steps[2]).toMatchObject({ stepId: 'end', stepName: 'End Step', stepType: 'END' })
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('maps transitions with correct fromStepId and toStepId', () => {
|
|
53
|
+
const workflow = defineWorkflow({
|
|
54
|
+
workflowId: 'test.workflow',
|
|
55
|
+
workflowName: 'Test',
|
|
56
|
+
steps: minimalSteps,
|
|
57
|
+
transitions: [
|
|
58
|
+
{
|
|
59
|
+
transitionId: 'start-to-end',
|
|
60
|
+
fromStepId: 'start',
|
|
61
|
+
toStepId: 'end',
|
|
62
|
+
trigger: 'auto' as const,
|
|
63
|
+
transitionName: 'Auto Complete',
|
|
64
|
+
priority: 1,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
expect(workflow.definition.transitions).toHaveLength(1)
|
|
70
|
+
expect(workflow.definition.transitions[0]).toMatchObject({
|
|
71
|
+
transitionId: 'start-to-end',
|
|
72
|
+
fromStepId: 'start',
|
|
73
|
+
toStepId: 'end',
|
|
74
|
+
trigger: 'auto',
|
|
75
|
+
transitionName: 'Auto Complete',
|
|
76
|
+
priority: 1,
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('sets moduleId to empty string', () => {
|
|
81
|
+
const workflow = defineWorkflow({
|
|
82
|
+
workflowId: 'test.workflow',
|
|
83
|
+
workflowName: 'Test',
|
|
84
|
+
steps: minimalSteps,
|
|
85
|
+
transitions: minimalTransitions,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
expect(workflow.moduleId).toBe('')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('defaults enabled to true when not specified', () => {
|
|
92
|
+
const workflow = defineWorkflow({
|
|
93
|
+
workflowId: 'test.workflow',
|
|
94
|
+
workflowName: 'Test',
|
|
95
|
+
steps: minimalSteps,
|
|
96
|
+
transitions: minimalTransitions,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(workflow.enabled).toBe(true)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('respects explicit enabled: false', () => {
|
|
103
|
+
const workflow = defineWorkflow({
|
|
104
|
+
workflowId: 'test.workflow',
|
|
105
|
+
workflowName: 'Test',
|
|
106
|
+
enabled: false,
|
|
107
|
+
steps: minimalSteps,
|
|
108
|
+
transitions: minimalTransitions,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
expect(workflow.enabled).toBe(false)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('defaults version to 1 when not specified', () => {
|
|
115
|
+
const workflow = defineWorkflow({
|
|
116
|
+
workflowId: 'test.workflow',
|
|
117
|
+
workflowName: 'Test',
|
|
118
|
+
steps: minimalSteps,
|
|
119
|
+
transitions: minimalTransitions,
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
expect(workflow.version).toBe(1)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('sets version correctly when specified', () => {
|
|
126
|
+
const workflow = defineWorkflow({
|
|
127
|
+
workflowId: 'test.workflow',
|
|
128
|
+
workflowName: 'Test',
|
|
129
|
+
version: 3,
|
|
130
|
+
steps: minimalSteps,
|
|
131
|
+
transitions: minimalTransitions,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
expect(workflow.version).toBe(3)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('defaults description to null when not specified', () => {
|
|
138
|
+
const workflow = defineWorkflow({
|
|
139
|
+
workflowId: 'test.workflow',
|
|
140
|
+
workflowName: 'Test',
|
|
141
|
+
steps: minimalSteps,
|
|
142
|
+
transitions: minimalTransitions,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
expect(workflow.description).toBeNull()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('sets description when specified', () => {
|
|
149
|
+
const workflow = defineWorkflow({
|
|
150
|
+
workflowId: 'test.workflow',
|
|
151
|
+
workflowName: 'Test',
|
|
152
|
+
description: 'Handles order approvals',
|
|
153
|
+
steps: minimalSteps,
|
|
154
|
+
transitions: minimalTransitions,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
expect(workflow.description).toBe('Handles order approvals')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('defaults metadata to null when not specified', () => {
|
|
161
|
+
const workflow = defineWorkflow({
|
|
162
|
+
workflowId: 'test.workflow',
|
|
163
|
+
workflowName: 'Test',
|
|
164
|
+
steps: minimalSteps,
|
|
165
|
+
transitions: minimalTransitions,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
expect(workflow.metadata).toBeNull()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
test('sets metadata when specified', () => {
|
|
172
|
+
const workflow = defineWorkflow({
|
|
173
|
+
workflowId: 'test.workflow',
|
|
174
|
+
workflowName: 'Test',
|
|
175
|
+
metadata: { tags: ['sales', 'approval'], category: 'sales', icon: 'check' },
|
|
176
|
+
steps: minimalSteps,
|
|
177
|
+
transitions: minimalTransitions,
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
expect(workflow.metadata).toEqual({ tags: ['sales', 'approval'], category: 'sales', icon: 'check' })
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('omits triggers from definition when not specified', () => {
|
|
184
|
+
const workflow = defineWorkflow({
|
|
185
|
+
workflowId: 'test.workflow',
|
|
186
|
+
workflowName: 'Test',
|
|
187
|
+
steps: minimalSteps,
|
|
188
|
+
transitions: minimalTransitions,
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
expect(workflow.definition.triggers).toBeUndefined()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('includes triggers in definition when specified', () => {
|
|
195
|
+
const trigger = {
|
|
196
|
+
triggerId: 'on-order-created',
|
|
197
|
+
name: 'Order Created',
|
|
198
|
+
eventPattern: 'sales.order.created',
|
|
199
|
+
enabled: true,
|
|
200
|
+
priority: 0,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const workflow = defineWorkflow({
|
|
204
|
+
workflowId: 'test.workflow',
|
|
205
|
+
workflowName: 'Test',
|
|
206
|
+
steps: minimalSteps,
|
|
207
|
+
transitions: minimalTransitions,
|
|
208
|
+
triggers: [trigger],
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(workflow.definition.triggers).toHaveLength(1)
|
|
212
|
+
expect(workflow.definition.triggers![0]).toMatchObject(trigger)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('does not include optional step fields when not provided', () => {
|
|
216
|
+
const workflow = defineWorkflow({
|
|
217
|
+
workflowId: 'test.workflow',
|
|
218
|
+
workflowName: 'Test',
|
|
219
|
+
steps: [{ stepId: 'start', stepName: 'Start', stepType: 'START' as const }] as const,
|
|
220
|
+
transitions: [],
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
const step = workflow.definition.steps[0]
|
|
224
|
+
expect(step).not.toHaveProperty('description')
|
|
225
|
+
expect(step).not.toHaveProperty('config')
|
|
226
|
+
expect(step).not.toHaveProperty('userTaskConfig')
|
|
227
|
+
expect(step).not.toHaveProperty('activities')
|
|
228
|
+
expect(step).not.toHaveProperty('timeout')
|
|
229
|
+
expect(step).not.toHaveProperty('retryPolicy')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
test('includes optional step fields when provided', () => {
|
|
233
|
+
const workflow = defineWorkflow({
|
|
234
|
+
workflowId: 'test.workflow',
|
|
235
|
+
workflowName: 'Test',
|
|
236
|
+
steps: [
|
|
237
|
+
{
|
|
238
|
+
stepId: 'review',
|
|
239
|
+
stepName: 'Review',
|
|
240
|
+
stepType: 'USER_TASK' as const,
|
|
241
|
+
description: 'Manager reviews the order',
|
|
242
|
+
timeout: 'P1D',
|
|
243
|
+
retryPolicy: { maxAttempts: 3, backoffMs: 1000 },
|
|
244
|
+
userTaskConfig: { assignedTo: 'manager', slaDuration: 'P1D' },
|
|
245
|
+
},
|
|
246
|
+
] as const,
|
|
247
|
+
transitions: [],
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const step = workflow.definition.steps[0]
|
|
251
|
+
expect(step.description).toBe('Manager reviews the order')
|
|
252
|
+
expect(step.timeout).toBe('P1D')
|
|
253
|
+
expect(step.retryPolicy).toEqual({ maxAttempts: 3, backoffMs: 1000 })
|
|
254
|
+
expect(step.userTaskConfig).toMatchObject({ assignedTo: 'manager', slaDuration: 'P1D' })
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
test('does not include optional transition fields when not provided', () => {
|
|
258
|
+
const workflow = defineWorkflow({
|
|
259
|
+
workflowId: 'test.workflow',
|
|
260
|
+
workflowName: 'Test',
|
|
261
|
+
steps: minimalSteps,
|
|
262
|
+
transitions: [{ transitionId: 't1', fromStepId: 'start', toStepId: 'end', trigger: 'auto' as const }],
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
const transition = workflow.definition.transitions[0]
|
|
266
|
+
expect(transition).not.toHaveProperty('transitionName')
|
|
267
|
+
expect(transition).not.toHaveProperty('preConditions')
|
|
268
|
+
expect(transition).not.toHaveProperty('postConditions')
|
|
269
|
+
expect(transition).not.toHaveProperty('activities')
|
|
270
|
+
expect(transition).not.toHaveProperty('continueOnActivityFailure')
|
|
271
|
+
expect(transition).not.toHaveProperty('priority')
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('works correctly with as const steps (key type safety feature)', () => {
|
|
275
|
+
// TypeScript should infer literal step IDs from `as const`.
|
|
276
|
+
// This test ensures the runtime behavior is correct when the pattern is used.
|
|
277
|
+
const workflow = defineWorkflow({
|
|
278
|
+
workflowId: 'sales.order-approval',
|
|
279
|
+
workflowName: 'Order Approval',
|
|
280
|
+
steps: [
|
|
281
|
+
{ stepId: 'start', stepName: 'Start', stepType: 'START' as const },
|
|
282
|
+
{ stepId: 'review', stepName: 'Review', stepType: 'USER_TASK' as const },
|
|
283
|
+
{ stepId: 'end', stepName: 'End', stepType: 'END' as const },
|
|
284
|
+
] as const,
|
|
285
|
+
transitions: [
|
|
286
|
+
{ transitionId: 'to-review', fromStepId: 'start', toStepId: 'review', trigger: 'auto' as const },
|
|
287
|
+
{ transitionId: 'to-end', fromStepId: 'review', toStepId: 'end', trigger: 'manual' as const },
|
|
288
|
+
],
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
expect(workflow.definition.steps.map((s) => s.stepId)).toEqual(['start', 'review', 'end'])
|
|
292
|
+
expect(workflow.definition.transitions.map((t) => t.fromStepId)).toEqual(['start', 'review'])
|
|
293
|
+
expect(workflow.definition.transitions.map((t) => t.toStepId)).toEqual(['review', 'end'])
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
describe('createWorkflowsModuleConfig()', () => {
|
|
298
|
+
const workflowA = defineWorkflow({
|
|
299
|
+
workflowId: 'test.workflow-a',
|
|
300
|
+
workflowName: 'Workflow A',
|
|
301
|
+
steps: minimalSteps,
|
|
302
|
+
transitions: minimalTransitions,
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
const workflowB = defineWorkflow({
|
|
306
|
+
workflowId: 'test.workflow-b',
|
|
307
|
+
workflowName: 'Workflow B',
|
|
308
|
+
steps: minimalSteps,
|
|
309
|
+
transitions: minimalTransitions,
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test('returns WorkflowsModuleConfig with correct structure', () => {
|
|
313
|
+
const config = createWorkflowsModuleConfig({
|
|
314
|
+
moduleId: 'sales',
|
|
315
|
+
workflows: [workflowA],
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
expect(config).toHaveProperty('moduleId')
|
|
319
|
+
expect(config).toHaveProperty('workflows')
|
|
320
|
+
expect(Array.isArray(config.workflows)).toBe(true)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
test('sets moduleId on the config', () => {
|
|
324
|
+
const config = createWorkflowsModuleConfig({
|
|
325
|
+
moduleId: 'sales',
|
|
326
|
+
workflows: [workflowA],
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
expect(config.moduleId).toBe('sales')
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
test('sets moduleId on all workflows', () => {
|
|
333
|
+
const config = createWorkflowsModuleConfig({
|
|
334
|
+
moduleId: 'sales',
|
|
335
|
+
workflows: [workflowA, workflowB],
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
expect(config.workflows[0].moduleId).toBe('sales')
|
|
339
|
+
expect(config.workflows[1].moduleId).toBe('sales')
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
test('preserves workflow properties after moduleId assignment', () => {
|
|
343
|
+
const workflow = defineWorkflow({
|
|
344
|
+
workflowId: 'sales.order-approval',
|
|
345
|
+
workflowName: 'Order Approval',
|
|
346
|
+
description: 'Approves orders',
|
|
347
|
+
version: 2,
|
|
348
|
+
enabled: false,
|
|
349
|
+
metadata: { tags: ['sales'], category: 'approvals' },
|
|
350
|
+
steps: minimalSteps,
|
|
351
|
+
transitions: minimalTransitions,
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
const config = createWorkflowsModuleConfig({
|
|
355
|
+
moduleId: 'sales',
|
|
356
|
+
workflows: [workflow],
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
const result = config.workflows[0]
|
|
360
|
+
expect(result.workflowId).toBe('sales.order-approval')
|
|
361
|
+
expect(result.workflowName).toBe('Order Approval')
|
|
362
|
+
expect(result.description).toBe('Approves orders')
|
|
363
|
+
expect(result.version).toBe(2)
|
|
364
|
+
expect(result.enabled).toBe(false)
|
|
365
|
+
expect(result.metadata).toEqual({ tags: ['sales'], category: 'approvals' })
|
|
366
|
+
expect(result.definition.steps).toHaveLength(2)
|
|
367
|
+
expect(result.definition.transitions).toHaveLength(1)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
test('does not mutate the original workflow objects', () => {
|
|
371
|
+
const workflow = defineWorkflow({
|
|
372
|
+
workflowId: 'test.workflow',
|
|
373
|
+
workflowName: 'Test',
|
|
374
|
+
steps: minimalSteps,
|
|
375
|
+
transitions: minimalTransitions,
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
expect(workflow.moduleId).toBe('')
|
|
379
|
+
|
|
380
|
+
createWorkflowsModuleConfig({ moduleId: 'sales', workflows: [workflow] })
|
|
381
|
+
|
|
382
|
+
expect(workflow.moduleId).toBe('')
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
test('handles empty workflows array', () => {
|
|
386
|
+
const config = createWorkflowsModuleConfig({
|
|
387
|
+
moduleId: 'sales',
|
|
388
|
+
workflows: [],
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
expect(config.moduleId).toBe('sales')
|
|
392
|
+
expect(config.workflows).toHaveLength(0)
|
|
393
|
+
})
|
|
394
|
+
})
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-Based Workflow Definitions — Builder API
|
|
3
|
+
*
|
|
4
|
+
* Provides compile-time type safety for step IDs in transitions.
|
|
5
|
+
* The `const` modifier on TSteps tells TypeScript to infer literal types
|
|
6
|
+
* for stepId values. Transitions constrain fromStepId/toStepId to this union.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const workflow = defineWorkflow({
|
|
11
|
+
* workflowId: 'sales.order-approval',
|
|
12
|
+
* workflowName: 'Order Approval',
|
|
13
|
+
* steps: [
|
|
14
|
+
* { stepId: 'start', stepName: 'Start', stepType: 'START' },
|
|
15
|
+
* { stepId: 'review', stepName: 'Review', stepType: 'USER_TASK' },
|
|
16
|
+
* { stepId: 'end', stepName: 'End', stepType: 'END' },
|
|
17
|
+
* ] as const,
|
|
18
|
+
* transitions: [
|
|
19
|
+
* { transitionId: 'to-review', fromStepId: 'start', toStepId: 'review', trigger: 'auto' },
|
|
20
|
+
* // Compile error: '"typo"' is not assignable to '"start" | "review" | "end"'
|
|
21
|
+
* // { transitionId: 'bad', fromStepId: 'typo', toStepId: 'end', trigger: 'auto' },
|
|
22
|
+
* ],
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
CodeWorkflowDefinition,
|
|
29
|
+
CodeStepDefinition,
|
|
30
|
+
CodeTransitionDefinition,
|
|
31
|
+
CodeTriggerDefinition,
|
|
32
|
+
CodeActivityDefinition,
|
|
33
|
+
WorkflowStepType,
|
|
34
|
+
TransitionTrigger,
|
|
35
|
+
} from './types'
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Builder Config Type (with const generic for type inference)
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
interface WorkflowBuilderConfig<TSteps extends readonly StepInput<string>[]> {
|
|
42
|
+
workflowId: string
|
|
43
|
+
workflowName: string
|
|
44
|
+
description?: string
|
|
45
|
+
version?: number
|
|
46
|
+
enabled?: boolean
|
|
47
|
+
metadata?: { tags?: string[]; category?: string; icon?: string }
|
|
48
|
+
steps: TSteps
|
|
49
|
+
transitions: TransitionInput<TSteps[number]['stepId']>[]
|
|
50
|
+
triggers?: CodeTriggerDefinition[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface StepInput<TStepId extends string> {
|
|
54
|
+
stepId: TStepId
|
|
55
|
+
stepName: string
|
|
56
|
+
stepType: WorkflowStepType
|
|
57
|
+
description?: string
|
|
58
|
+
config?: Record<string, unknown>
|
|
59
|
+
userTaskConfig?: CodeStepDefinition['userTaskConfig']
|
|
60
|
+
subWorkflowConfig?: CodeStepDefinition['subWorkflowConfig']
|
|
61
|
+
signalConfig?: CodeStepDefinition['signalConfig']
|
|
62
|
+
activities?: CodeActivityDefinition[]
|
|
63
|
+
timeout?: string
|
|
64
|
+
retryPolicy?: { maxAttempts: number; backoffMs: number }
|
|
65
|
+
preConditions?: CodeStepDefinition['preConditions']
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface TransitionInput<TStepId extends string> {
|
|
69
|
+
transitionId: string
|
|
70
|
+
fromStepId: TStepId
|
|
71
|
+
toStepId: TStepId
|
|
72
|
+
transitionName?: string
|
|
73
|
+
trigger: TransitionTrigger
|
|
74
|
+
preConditions?: CodeTransitionDefinition['preConditions']
|
|
75
|
+
postConditions?: CodeTransitionDefinition['postConditions']
|
|
76
|
+
activities?: CodeActivityDefinition[]
|
|
77
|
+
continueOnActivityFailure?: boolean
|
|
78
|
+
priority?: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// defineWorkflow — Main Builder Function
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Define a code-based workflow with compile-time step ID safety.
|
|
87
|
+
*
|
|
88
|
+
* Use `as const` on the steps array for full type inference:
|
|
89
|
+
* ```typescript
|
|
90
|
+
* defineWorkflow({
|
|
91
|
+
* steps: [...] as const,
|
|
92
|
+
* transitions: [...], // fromStepId/toStepId auto-constrained
|
|
93
|
+
* })
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* The returned `CodeWorkflowDefinition` has `moduleId` set to empty string.
|
|
97
|
+
* The generator sets the correct `moduleId` at registration time.
|
|
98
|
+
*/
|
|
99
|
+
export function defineWorkflow<const TSteps extends readonly StepInput<string>[]>(
|
|
100
|
+
config: WorkflowBuilderConfig<TSteps>,
|
|
101
|
+
): CodeWorkflowDefinition {
|
|
102
|
+
const steps: CodeStepDefinition[] = config.steps.map((step) => ({
|
|
103
|
+
stepId: step.stepId,
|
|
104
|
+
stepName: step.stepName,
|
|
105
|
+
stepType: step.stepType,
|
|
106
|
+
...(step.description !== undefined && { description: step.description }),
|
|
107
|
+
...(step.config !== undefined && { config: step.config }),
|
|
108
|
+
...(step.userTaskConfig !== undefined && { userTaskConfig: step.userTaskConfig }),
|
|
109
|
+
...(step.subWorkflowConfig !== undefined && { subWorkflowConfig: step.subWorkflowConfig }),
|
|
110
|
+
...(step.signalConfig !== undefined && { signalConfig: step.signalConfig }),
|
|
111
|
+
...(step.activities !== undefined && { activities: step.activities }),
|
|
112
|
+
...(step.timeout !== undefined && { timeout: step.timeout }),
|
|
113
|
+
...(step.retryPolicy !== undefined && { retryPolicy: step.retryPolicy }),
|
|
114
|
+
...(step.preConditions !== undefined && { preConditions: step.preConditions }),
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
const transitions: CodeTransitionDefinition[] = config.transitions.map((t) => ({
|
|
118
|
+
transitionId: t.transitionId,
|
|
119
|
+
fromStepId: t.fromStepId,
|
|
120
|
+
toStepId: t.toStepId,
|
|
121
|
+
trigger: t.trigger,
|
|
122
|
+
...(t.transitionName !== undefined && { transitionName: t.transitionName }),
|
|
123
|
+
...(t.preConditions !== undefined && { preConditions: t.preConditions }),
|
|
124
|
+
...(t.postConditions !== undefined && { postConditions: t.postConditions }),
|
|
125
|
+
...(t.activities !== undefined && { activities: t.activities }),
|
|
126
|
+
...(t.continueOnActivityFailure !== undefined && { continueOnActivityFailure: t.continueOnActivityFailure }),
|
|
127
|
+
...(t.priority !== undefined && { priority: t.priority }),
|
|
128
|
+
}))
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
workflowId: config.workflowId,
|
|
132
|
+
workflowName: config.workflowName,
|
|
133
|
+
description: config.description ?? null,
|
|
134
|
+
version: config.version ?? 1,
|
|
135
|
+
enabled: config.enabled ?? true,
|
|
136
|
+
metadata: config.metadata ?? null,
|
|
137
|
+
definition: {
|
|
138
|
+
steps,
|
|
139
|
+
transitions,
|
|
140
|
+
...(config.triggers !== undefined && { triggers: config.triggers }),
|
|
141
|
+
},
|
|
142
|
+
moduleId: '', // Set by generator/factory at registration time
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-Based Workflow Definitions — Module Config Factory
|
|
3
|
+
*
|
|
4
|
+
* Analogous to createModuleEvents() for events.
|
|
5
|
+
* Creates a WorkflowsModuleConfig that the generator aggregates.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CodeWorkflowDefinition, WorkflowsModuleConfig } from './types'
|
|
9
|
+
|
|
10
|
+
export function createWorkflowsModuleConfig(options: {
|
|
11
|
+
moduleId: string
|
|
12
|
+
workflows: CodeWorkflowDefinition[]
|
|
13
|
+
}): WorkflowsModuleConfig {
|
|
14
|
+
const workflows = options.workflows.map((wf) => ({
|
|
15
|
+
...wf,
|
|
16
|
+
moduleId: options.moduleId,
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
moduleId: options.moduleId,
|
|
21
|
+
workflows,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
CodeWorkflowDefinition,
|
|
3
|
+
CodeWorkflowDefinitionData,
|
|
4
|
+
CodeStepDefinition,
|
|
5
|
+
CodeTransitionDefinition,
|
|
6
|
+
CodeTriggerDefinition,
|
|
7
|
+
CodeActivityDefinition,
|
|
8
|
+
WorkflowStepType,
|
|
9
|
+
TransitionTrigger,
|
|
10
|
+
ActivityType,
|
|
11
|
+
WorkflowsModuleConfig,
|
|
12
|
+
} from './types'
|
|
13
|
+
|
|
14
|
+
export { defineWorkflow } from './builder'
|
|
15
|
+
export { createWorkflowsModuleConfig } from './factory'
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-Based Workflow Definitions — Shared Types
|
|
3
|
+
*
|
|
4
|
+
* These types are used by the builder API, generator extension, and in-memory registry.
|
|
5
|
+
* They mirror the JSONB shape stored in workflow_definitions.definition but add
|
|
6
|
+
* generic type safety for step ID references in transitions.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Step Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export type WorkflowStepType =
|
|
14
|
+
| 'START'
|
|
15
|
+
| 'END'
|
|
16
|
+
| 'USER_TASK'
|
|
17
|
+
| 'AUTOMATED'
|
|
18
|
+
| 'PARALLEL_FORK'
|
|
19
|
+
| 'PARALLEL_JOIN'
|
|
20
|
+
| 'SUB_WORKFLOW'
|
|
21
|
+
| 'WAIT_FOR_SIGNAL'
|
|
22
|
+
| 'WAIT_FOR_TIMER'
|
|
23
|
+
|
|
24
|
+
export type TransitionTrigger = 'auto' | 'manual' | 'signal' | 'timer'
|
|
25
|
+
|
|
26
|
+
export type ActivityType =
|
|
27
|
+
| 'SEND_EMAIL'
|
|
28
|
+
| 'CALL_API'
|
|
29
|
+
| 'UPDATE_ENTITY'
|
|
30
|
+
| 'EMIT_EVENT'
|
|
31
|
+
| 'CALL_WEBHOOK'
|
|
32
|
+
| 'EXECUTE_FUNCTION'
|
|
33
|
+
| 'WAIT'
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Activity Definition
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
export interface CodeActivityDefinition {
|
|
40
|
+
activityId: string
|
|
41
|
+
activityName: string
|
|
42
|
+
activityType: ActivityType
|
|
43
|
+
config: Record<string, unknown>
|
|
44
|
+
async?: boolean
|
|
45
|
+
retryPolicy?: {
|
|
46
|
+
maxAttempts: number
|
|
47
|
+
initialIntervalMs: number
|
|
48
|
+
backoffCoefficient: number
|
|
49
|
+
maxIntervalMs: number
|
|
50
|
+
}
|
|
51
|
+
timeout?: string
|
|
52
|
+
compensation?: {
|
|
53
|
+
activityId: string
|
|
54
|
+
automatic?: boolean
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Step Definition (generic over step IDs)
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export interface CodeStepDefinition<TStepId extends string = string> {
|
|
63
|
+
stepId: TStepId
|
|
64
|
+
stepName: string
|
|
65
|
+
stepType: WorkflowStepType
|
|
66
|
+
description?: string
|
|
67
|
+
config?: Record<string, unknown>
|
|
68
|
+
userTaskConfig?: {
|
|
69
|
+
formSchema?: Record<string, unknown>
|
|
70
|
+
assignedTo?: string | string[]
|
|
71
|
+
assignmentRule?: string
|
|
72
|
+
slaDuration?: string
|
|
73
|
+
escalationRules?: Array<{
|
|
74
|
+
trigger: 'sla_breach' | 'no_progress' | 'custom'
|
|
75
|
+
action: 'reassign' | 'notify' | 'escalate'
|
|
76
|
+
escalateTo?: string
|
|
77
|
+
notifyUsers?: string[]
|
|
78
|
+
}>
|
|
79
|
+
}
|
|
80
|
+
subWorkflowConfig?: {
|
|
81
|
+
subWorkflowId: string
|
|
82
|
+
version?: number
|
|
83
|
+
inputMapping?: Record<string, string>
|
|
84
|
+
outputMapping?: Record<string, string>
|
|
85
|
+
timeoutMs?: number
|
|
86
|
+
}
|
|
87
|
+
signalConfig?: {
|
|
88
|
+
signalName: string
|
|
89
|
+
timeout?: string
|
|
90
|
+
}
|
|
91
|
+
activities?: CodeActivityDefinition[]
|
|
92
|
+
timeout?: string
|
|
93
|
+
retryPolicy?: {
|
|
94
|
+
maxAttempts: number
|
|
95
|
+
backoffMs: number
|
|
96
|
+
}
|
|
97
|
+
preConditions?: Array<{
|
|
98
|
+
ruleId: string
|
|
99
|
+
required?: boolean
|
|
100
|
+
validationMessage?: Record<string, string>
|
|
101
|
+
}>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Transition Definition (generic over step IDs)
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
export interface CodeTransitionDefinition<TStepId extends string = string> {
|
|
109
|
+
transitionId: string
|
|
110
|
+
fromStepId: TStepId
|
|
111
|
+
toStepId: TStepId
|
|
112
|
+
transitionName?: string
|
|
113
|
+
trigger: TransitionTrigger
|
|
114
|
+
preConditions?: Array<{
|
|
115
|
+
ruleId: string
|
|
116
|
+
required?: boolean
|
|
117
|
+
}>
|
|
118
|
+
postConditions?: Array<{
|
|
119
|
+
ruleId: string
|
|
120
|
+
required?: boolean
|
|
121
|
+
}>
|
|
122
|
+
activities?: CodeActivityDefinition[]
|
|
123
|
+
continueOnActivityFailure?: boolean
|
|
124
|
+
priority?: number
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Trigger Definition
|
|
129
|
+
// ============================================================================
|
|
130
|
+
|
|
131
|
+
export interface CodeTriggerDefinition {
|
|
132
|
+
triggerId: string
|
|
133
|
+
name: string
|
|
134
|
+
description?: string | null
|
|
135
|
+
eventPattern: string
|
|
136
|
+
config?: {
|
|
137
|
+
filterConditions?: Array<{
|
|
138
|
+
field: string
|
|
139
|
+
operator: string
|
|
140
|
+
value: unknown
|
|
141
|
+
}>
|
|
142
|
+
contextMapping?: Array<{
|
|
143
|
+
targetKey: string
|
|
144
|
+
sourceExpression: string
|
|
145
|
+
defaultValue?: unknown
|
|
146
|
+
}>
|
|
147
|
+
debounceMs?: number
|
|
148
|
+
maxConcurrentInstances?: number
|
|
149
|
+
entityType?: string
|
|
150
|
+
} | null
|
|
151
|
+
enabled: boolean
|
|
152
|
+
priority: number
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Workflow Definition Data (matches JSONB shape)
|
|
157
|
+
// ============================================================================
|
|
158
|
+
|
|
159
|
+
export interface CodeWorkflowDefinitionData {
|
|
160
|
+
steps: CodeStepDefinition[]
|
|
161
|
+
transitions: CodeTransitionDefinition[]
|
|
162
|
+
triggers?: CodeTriggerDefinition[]
|
|
163
|
+
queries?: unknown[]
|
|
164
|
+
signals?: unknown[]
|
|
165
|
+
timers?: unknown[]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Code Workflow Definition (output of defineWorkflow)
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
export interface CodeWorkflowDefinition {
|
|
173
|
+
workflowId: string
|
|
174
|
+
workflowName: string
|
|
175
|
+
description: string | null
|
|
176
|
+
version: number
|
|
177
|
+
enabled: boolean
|
|
178
|
+
metadata: { tags?: string[]; category?: string; icon?: string } | null
|
|
179
|
+
definition: CodeWorkflowDefinitionData
|
|
180
|
+
moduleId: string
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Module Config (for generator discovery)
|
|
185
|
+
// ============================================================================
|
|
186
|
+
|
|
187
|
+
export interface WorkflowsModuleConfig {
|
|
188
|
+
moduleId: string
|
|
189
|
+
workflows: CodeWorkflowDefinition[]
|
|
190
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './workflows/index'
|