@contractspec/example.workflow-system 3.7.6 → 3.8.2

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 (57) hide show
  1. package/README.md +72 -119
  2. package/dist/approval/approval.event.js +1 -1
  3. package/dist/approval/approval.handler.d.ts +1 -1
  4. package/dist/approval/index.d.ts +4 -4
  5. package/dist/approval/index.js +1 -1
  6. package/dist/browser/approval/approval.event.js +1 -1
  7. package/dist/browser/approval/index.js +1 -1
  8. package/dist/browser/entities/index.js +166 -166
  9. package/dist/browser/handlers/index.js +43 -43
  10. package/dist/browser/handlers/workflow.handlers.js +43 -43
  11. package/dist/browser/index.js +1935 -1548
  12. package/dist/browser/instance/index.js +210 -210
  13. package/dist/browser/instance/instance.event.js +1 -1
  14. package/dist/browser/shared/demo-scenario.js +213 -0
  15. package/dist/browser/ui/WorkflowDashboard.visualizations.js +239 -0
  16. package/dist/browser/ui/hooks/index.js +0 -47
  17. package/dist/browser/ui/hooks/useWorkflowList.js +6 -4
  18. package/dist/browser/ui/index.js +6 -4
  19. package/dist/browser/ui/renderers/index.js +409 -73
  20. package/dist/browser/ui/renderers/workflow.markdown.js +409 -73
  21. package/dist/browser/visualizations/catalog.js +132 -0
  22. package/dist/browser/visualizations/index.js +133 -0
  23. package/dist/browser/visualizations/selectors.js +195 -0
  24. package/dist/entities/index.d.ts +53 -53
  25. package/dist/entities/index.js +166 -166
  26. package/dist/example.test.d.ts +1 -0
  27. package/dist/handlers/index.js +43 -43
  28. package/dist/handlers/workflow.handlers.js +43 -43
  29. package/dist/index.d.ts +6 -5
  30. package/dist/index.js +1935 -1548
  31. package/dist/instance/index.d.ts +3 -3
  32. package/dist/instance/index.js +210 -210
  33. package/dist/instance/instance.event.js +1 -1
  34. package/dist/instance/instance.handler.d.ts +1 -1
  35. package/dist/shared/demo-scenario.d.ts +43 -0
  36. package/dist/shared/demo-scenario.js +214 -0
  37. package/dist/shared/index.d.ts +1 -1
  38. package/dist/shared/mock-data.d.ts +1 -1
  39. package/dist/ui/WorkflowDashboard.visualizations.d.ts +4 -0
  40. package/dist/ui/WorkflowDashboard.visualizations.js +240 -0
  41. package/dist/ui/hooks/index.js +0 -47
  42. package/dist/ui/hooks/useWorkflowList.d.ts +2 -1
  43. package/dist/ui/hooks/useWorkflowList.js +6 -4
  44. package/dist/ui/index.d.ts +1 -1
  45. package/dist/ui/index.js +6 -4
  46. package/dist/ui/renderers/index.js +409 -73
  47. package/dist/ui/renderers/workflow.markdown.js +409 -73
  48. package/dist/visualizations/catalog.d.ts +11 -0
  49. package/dist/visualizations/catalog.js +133 -0
  50. package/dist/visualizations/index.d.ts +2 -0
  51. package/dist/visualizations/index.js +134 -0
  52. package/dist/visualizations/selectors.d.ts +11 -0
  53. package/dist/visualizations/selectors.js +196 -0
  54. package/dist/visualizations/selectors.test.d.ts +1 -0
  55. package/dist/workflow/index.d.ts +4 -4
  56. package/dist/workflow/workflow.handler.d.ts +1 -1
  57. package/package.json +71 -10
@@ -112,82 +112,160 @@ var ApprovalCommentEntity = defineEntity({
112
112
  indexes: [index.on(["approvalRequestId", "createdAt"])]
113
113
  });
114
114
 
115
- // src/entities/workflow.ts
115
+ // src/entities/instance.ts
116
116
  import {
117
117
  defineEntity as defineEntity2,
118
118
  defineEntityEnum as defineEntityEnum2,
119
119
  field as field2,
120
120
  index as index2
121
121
  } from "@contractspec/lib.schema";
122
- var WorkflowStatusEnum = defineEntityEnum2({
123
- name: "WorkflowStatus",
124
- values: ["DRAFT", "ACTIVE", "DEPRECATED", "ARCHIVED"],
122
+ var InstanceStatusEnum = defineEntityEnum2({
123
+ name: "InstanceStatus",
124
+ values: [
125
+ "PENDING",
126
+ "RUNNING",
127
+ "WAITING",
128
+ "PAUSED",
129
+ "COMPLETED",
130
+ "CANCELLED",
131
+ "FAILED",
132
+ "TIMEOUT"
133
+ ],
125
134
  schema: "workflow",
126
- description: "Status of a workflow definition."
135
+ description: "Status of a workflow instance."
127
136
  });
128
- var WorkflowTriggerTypeEnum = defineEntityEnum2({
129
- name: "WorkflowTriggerType",
130
- values: ["MANUAL", "EVENT", "SCHEDULED", "API"],
137
+ var StepExecutionStatusEnum = defineEntityEnum2({
138
+ name: "StepExecutionStatus",
139
+ values: [
140
+ "PENDING",
141
+ "ACTIVE",
142
+ "COMPLETED",
143
+ "SKIPPED",
144
+ "FAILED",
145
+ "TIMEOUT"
146
+ ],
131
147
  schema: "workflow",
132
- description: "What triggers workflow instantiation."
148
+ description: "Status of a step execution."
133
149
  });
134
- var WorkflowDefinitionEntity = defineEntity2({
135
- name: "WorkflowDefinition",
136
- description: "A workflow blueprint that defines the process structure.",
150
+ var WorkflowInstanceEntity = defineEntity2({
151
+ name: "WorkflowInstance",
152
+ description: "A running instance of a workflow definition.",
137
153
  schema: "workflow",
138
- map: "workflow_definition",
154
+ map: "workflow_instance",
139
155
  fields: {
140
- id: field2.id({ description: "Unique workflow definition ID" }),
141
- name: field2.string({ description: "Human-readable workflow name" }),
142
- key: field2.string({
143
- description: 'Unique key for referencing (e.g., "purchase_approval")'
156
+ id: field2.id({ description: "Unique instance ID" }),
157
+ workflowDefinitionId: field2.foreignKey(),
158
+ referenceId: field2.string({
159
+ isOptional: true,
160
+ description: "External reference (e.g., order ID)"
144
161
  }),
145
- description: field2.string({
162
+ referenceType: field2.string({
146
163
  isOptional: true,
147
- description: "Detailed description"
164
+ description: 'Type of reference (e.g., "Order")'
148
165
  }),
149
- version: field2.int({
150
- default: 1,
151
- description: "Version number for versioning definitions"
166
+ status: field2.enum("InstanceStatus", { default: "PENDING" }),
167
+ currentStepId: field2.string({
168
+ isOptional: true,
169
+ description: "Current step being executed"
170
+ }),
171
+ contextData: field2.json({ description: "Data context for this instance" }),
172
+ triggeredBy: field2.foreignKey({
173
+ description: "User who triggered the workflow"
152
174
  }),
153
- status: field2.enum("WorkflowStatus", { default: "DRAFT" }),
154
- triggerType: field2.enum("WorkflowTriggerType", { default: "MANUAL" }),
155
- triggerConfig: field2.json({
175
+ triggerSource: field2.string({
156
176
  isOptional: true,
157
- description: "Trigger-specific configuration"
177
+ description: 'Source of trigger (e.g., "api", "ui")'
178
+ }),
179
+ organizationId: field2.foreignKey(),
180
+ priority: field2.int({
181
+ default: 0,
182
+ description: "Processing priority (higher = more urgent)"
158
183
  }),
159
- initialStepId: field2.string({
184
+ dueAt: field2.dateTime({
160
185
  isOptional: true,
161
- description: "First step when workflow starts"
186
+ description: "When this instance should complete"
162
187
  }),
163
- featureFlagKey: field2.string({
188
+ outcome: field2.string({
164
189
  isOptional: true,
165
- description: "Feature flag to control availability"
190
+ description: 'Final outcome (e.g., "approved", "rejected")'
166
191
  }),
167
- settings: field2.json({
192
+ resultData: field2.json({
168
193
  isOptional: true,
169
- description: "Workflow-wide settings"
194
+ description: "Final result data"
170
195
  }),
171
- metadata: field2.json({ isOptional: true, description: "Custom metadata" }),
172
- organizationId: field2.foreignKey({ description: "Owning organization" }),
173
- createdBy: field2.foreignKey({
174
- description: "User who created this workflow"
196
+ errorMessage: field2.string({
197
+ isOptional: true,
198
+ description: "Error message if failed"
175
199
  }),
176
200
  createdAt: field2.createdAt(),
177
201
  updatedAt: field2.updatedAt(),
178
- publishedAt: field2.dateTime({
202
+ startedAt: field2.dateTime({ isOptional: true }),
203
+ completedAt: field2.dateTime({ isOptional: true }),
204
+ workflowDefinition: field2.belongsTo("WorkflowDefinition", ["workflowDefinitionId"], ["id"]),
205
+ currentStep: field2.belongsTo("WorkflowStep", ["currentStepId"], ["id"]),
206
+ stepExecutions: field2.hasMany("StepExecution"),
207
+ approvalRequests: field2.hasMany("ApprovalRequest")
208
+ },
209
+ indexes: [
210
+ index2.on(["organizationId", "status"]),
211
+ index2.on(["workflowDefinitionId", "status"]),
212
+ index2.on(["referenceType", "referenceId"]),
213
+ index2.on(["triggeredBy", "status"]),
214
+ index2.on(["status", "dueAt"]),
215
+ index2.on(["createdAt"])
216
+ ],
217
+ enums: [InstanceStatusEnum]
218
+ });
219
+ var StepExecutionEntity = defineEntity2({
220
+ name: "StepExecution",
221
+ description: "Execution record of a step within a workflow instance.",
222
+ schema: "workflow",
223
+ map: "step_execution",
224
+ fields: {
225
+ id: field2.id({ description: "Unique execution ID" }),
226
+ workflowInstanceId: field2.foreignKey(),
227
+ workflowStepId: field2.foreignKey(),
228
+ status: field2.enum("StepExecutionStatus", { default: "PENDING" }),
229
+ executionOrder: field2.int({
230
+ default: 0,
231
+ description: "Order of execution within instance"
232
+ }),
233
+ inputData: field2.json({
179
234
  isOptional: true,
180
- description: "When workflow was activated"
235
+ description: "Data when entering step"
236
+ }),
237
+ outputData: field2.json({
238
+ isOptional: true,
239
+ description: "Data when exiting step"
240
+ }),
241
+ actionTaken: field2.string({
242
+ isOptional: true,
243
+ description: 'Action that caused transition (e.g., "approve")'
181
244
  }),
182
- steps: field2.hasMany("WorkflowStep"),
183
- instances: field2.hasMany("WorkflowInstance")
245
+ transitionedTo: field2.string({
246
+ isOptional: true,
247
+ description: "Step key transitioned to"
248
+ }),
249
+ executedBy: field2.string({
250
+ isOptional: true,
251
+ description: "User who completed this step"
252
+ }),
253
+ errorMessage: field2.string({ isOptional: true }),
254
+ errorDetails: field2.json({ isOptional: true }),
255
+ retryCount: field2.int({ default: 0 }),
256
+ createdAt: field2.createdAt(),
257
+ updatedAt: field2.updatedAt(),
258
+ startedAt: field2.dateTime({ isOptional: true }),
259
+ completedAt: field2.dateTime({ isOptional: true }),
260
+ workflowInstance: field2.belongsTo("WorkflowInstance", ["workflowInstanceId"], ["id"], { onDelete: "Cascade" }),
261
+ workflowStep: field2.belongsTo("WorkflowStep", ["workflowStepId"], ["id"])
184
262
  },
185
263
  indexes: [
186
- index2.unique(["organizationId", "key", "version"]),
187
- index2.on(["organizationId", "status"]),
188
- index2.on(["key", "version"])
264
+ index2.on(["workflowInstanceId", "executionOrder"]),
265
+ index2.on(["workflowInstanceId", "workflowStepId"]),
266
+ index2.on(["status"])
189
267
  ],
190
- enums: [WorkflowStatusEnum, WorkflowTriggerTypeEnum]
268
+ enums: [StepExecutionStatusEnum]
191
269
  });
192
270
 
193
271
  // src/entities/step.ts
@@ -310,160 +388,82 @@ var WorkflowStepEntity = defineEntity3({
310
388
  enums: [StepTypeEnum, ApprovalModeEnum]
311
389
  });
312
390
 
313
- // src/entities/instance.ts
391
+ // src/entities/workflow.ts
314
392
  import {
315
393
  defineEntity as defineEntity4,
316
394
  defineEntityEnum as defineEntityEnum4,
317
395
  field as field4,
318
396
  index as index4
319
397
  } from "@contractspec/lib.schema";
320
- var InstanceStatusEnum = defineEntityEnum4({
321
- name: "InstanceStatus",
322
- values: [
323
- "PENDING",
324
- "RUNNING",
325
- "WAITING",
326
- "PAUSED",
327
- "COMPLETED",
328
- "CANCELLED",
329
- "FAILED",
330
- "TIMEOUT"
331
- ],
398
+ var WorkflowStatusEnum = defineEntityEnum4({
399
+ name: "WorkflowStatus",
400
+ values: ["DRAFT", "ACTIVE", "DEPRECATED", "ARCHIVED"],
332
401
  schema: "workflow",
333
- description: "Status of a workflow instance."
402
+ description: "Status of a workflow definition."
334
403
  });
335
- var StepExecutionStatusEnum = defineEntityEnum4({
336
- name: "StepExecutionStatus",
337
- values: [
338
- "PENDING",
339
- "ACTIVE",
340
- "COMPLETED",
341
- "SKIPPED",
342
- "FAILED",
343
- "TIMEOUT"
344
- ],
404
+ var WorkflowTriggerTypeEnum = defineEntityEnum4({
405
+ name: "WorkflowTriggerType",
406
+ values: ["MANUAL", "EVENT", "SCHEDULED", "API"],
345
407
  schema: "workflow",
346
- description: "Status of a step execution."
408
+ description: "What triggers workflow instantiation."
347
409
  });
348
- var WorkflowInstanceEntity = defineEntity4({
349
- name: "WorkflowInstance",
350
- description: "A running instance of a workflow definition.",
410
+ var WorkflowDefinitionEntity = defineEntity4({
411
+ name: "WorkflowDefinition",
412
+ description: "A workflow blueprint that defines the process structure.",
351
413
  schema: "workflow",
352
- map: "workflow_instance",
414
+ map: "workflow_definition",
353
415
  fields: {
354
- id: field4.id({ description: "Unique instance ID" }),
355
- workflowDefinitionId: field4.foreignKey(),
356
- referenceId: field4.string({
357
- isOptional: true,
358
- description: "External reference (e.g., order ID)"
359
- }),
360
- referenceType: field4.string({
361
- isOptional: true,
362
- description: 'Type of reference (e.g., "Order")'
416
+ id: field4.id({ description: "Unique workflow definition ID" }),
417
+ name: field4.string({ description: "Human-readable workflow name" }),
418
+ key: field4.string({
419
+ description: 'Unique key for referencing (e.g., "purchase_approval")'
363
420
  }),
364
- status: field4.enum("InstanceStatus", { default: "PENDING" }),
365
- currentStepId: field4.string({
421
+ description: field4.string({
366
422
  isOptional: true,
367
- description: "Current step being executed"
423
+ description: "Detailed description"
368
424
  }),
369
- contextData: field4.json({ description: "Data context for this instance" }),
370
- triggeredBy: field4.foreignKey({
371
- description: "User who triggered the workflow"
425
+ version: field4.int({
426
+ default: 1,
427
+ description: "Version number for versioning definitions"
372
428
  }),
373
- triggerSource: field4.string({
429
+ status: field4.enum("WorkflowStatus", { default: "DRAFT" }),
430
+ triggerType: field4.enum("WorkflowTriggerType", { default: "MANUAL" }),
431
+ triggerConfig: field4.json({
374
432
  isOptional: true,
375
- description: 'Source of trigger (e.g., "api", "ui")'
376
- }),
377
- organizationId: field4.foreignKey(),
378
- priority: field4.int({
379
- default: 0,
380
- description: "Processing priority (higher = more urgent)"
433
+ description: "Trigger-specific configuration"
381
434
  }),
382
- dueAt: field4.dateTime({
435
+ initialStepId: field4.string({
383
436
  isOptional: true,
384
- description: "When this instance should complete"
437
+ description: "First step when workflow starts"
385
438
  }),
386
- outcome: field4.string({
439
+ featureFlagKey: field4.string({
387
440
  isOptional: true,
388
- description: 'Final outcome (e.g., "approved", "rejected")'
441
+ description: "Feature flag to control availability"
389
442
  }),
390
- resultData: field4.json({
443
+ settings: field4.json({
391
444
  isOptional: true,
392
- description: "Final result data"
445
+ description: "Workflow-wide settings"
393
446
  }),
394
- errorMessage: field4.string({
395
- isOptional: true,
396
- description: "Error message if failed"
447
+ metadata: field4.json({ isOptional: true, description: "Custom metadata" }),
448
+ organizationId: field4.foreignKey({ description: "Owning organization" }),
449
+ createdBy: field4.foreignKey({
450
+ description: "User who created this workflow"
397
451
  }),
398
452
  createdAt: field4.createdAt(),
399
453
  updatedAt: field4.updatedAt(),
400
- startedAt: field4.dateTime({ isOptional: true }),
401
- completedAt: field4.dateTime({ isOptional: true }),
402
- workflowDefinition: field4.belongsTo("WorkflowDefinition", ["workflowDefinitionId"], ["id"]),
403
- currentStep: field4.belongsTo("WorkflowStep", ["currentStepId"], ["id"]),
404
- stepExecutions: field4.hasMany("StepExecution"),
405
- approvalRequests: field4.hasMany("ApprovalRequest")
406
- },
407
- indexes: [
408
- index4.on(["organizationId", "status"]),
409
- index4.on(["workflowDefinitionId", "status"]),
410
- index4.on(["referenceType", "referenceId"]),
411
- index4.on(["triggeredBy", "status"]),
412
- index4.on(["status", "dueAt"]),
413
- index4.on(["createdAt"])
414
- ],
415
- enums: [InstanceStatusEnum]
416
- });
417
- var StepExecutionEntity = defineEntity4({
418
- name: "StepExecution",
419
- description: "Execution record of a step within a workflow instance.",
420
- schema: "workflow",
421
- map: "step_execution",
422
- fields: {
423
- id: field4.id({ description: "Unique execution ID" }),
424
- workflowInstanceId: field4.foreignKey(),
425
- workflowStepId: field4.foreignKey(),
426
- status: field4.enum("StepExecutionStatus", { default: "PENDING" }),
427
- executionOrder: field4.int({
428
- default: 0,
429
- description: "Order of execution within instance"
430
- }),
431
- inputData: field4.json({
432
- isOptional: true,
433
- description: "Data when entering step"
434
- }),
435
- outputData: field4.json({
436
- isOptional: true,
437
- description: "Data when exiting step"
438
- }),
439
- actionTaken: field4.string({
454
+ publishedAt: field4.dateTime({
440
455
  isOptional: true,
441
- description: 'Action that caused transition (e.g., "approve")'
442
- }),
443
- transitionedTo: field4.string({
444
- isOptional: true,
445
- description: "Step key transitioned to"
446
- }),
447
- executedBy: field4.string({
448
- isOptional: true,
449
- description: "User who completed this step"
456
+ description: "When workflow was activated"
450
457
  }),
451
- errorMessage: field4.string({ isOptional: true }),
452
- errorDetails: field4.json({ isOptional: true }),
453
- retryCount: field4.int({ default: 0 }),
454
- createdAt: field4.createdAt(),
455
- updatedAt: field4.updatedAt(),
456
- startedAt: field4.dateTime({ isOptional: true }),
457
- completedAt: field4.dateTime({ isOptional: true }),
458
- workflowInstance: field4.belongsTo("WorkflowInstance", ["workflowInstanceId"], ["id"], { onDelete: "Cascade" }),
459
- workflowStep: field4.belongsTo("WorkflowStep", ["workflowStepId"], ["id"])
458
+ steps: field4.hasMany("WorkflowStep"),
459
+ instances: field4.hasMany("WorkflowInstance")
460
460
  },
461
461
  indexes: [
462
- index4.on(["workflowInstanceId", "executionOrder"]),
463
- index4.on(["workflowInstanceId", "workflowStepId"]),
464
- index4.on(["status"])
462
+ index4.unique(["organizationId", "key", "version"]),
463
+ index4.on(["organizationId", "status"]),
464
+ index4.on(["key", "version"])
465
465
  ],
466
- enums: [StepExecutionStatusEnum]
466
+ enums: [WorkflowStatusEnum, WorkflowTriggerTypeEnum]
467
467
  });
468
468
  // src/entities/index.ts
469
469
  var workflowSystemEntities = [
@@ -54,9 +54,19 @@ function rowToApproval(row) {
54
54
  };
55
55
  }
56
56
  function createWorkflowHandlers(db) {
57
+ function normalizeSql(sql) {
58
+ let placeholderIndex = 0;
59
+ return sql.replace(/\?/g, () => `$${++placeholderIndex}`);
60
+ }
61
+ async function queryRows(sql, params = []) {
62
+ return (await db.query(normalizeSql(sql), params)).rows;
63
+ }
64
+ async function execute(sql, params = []) {
65
+ await db.execute(normalizeSql(sql), params);
66
+ }
57
67
  async function listDefinitions(input) {
58
68
  const { projectId, status, search, limit = 20, offset = 0 } = input;
59
- let whereClause = "WHERE projectId = ?";
69
+ let whereClause = 'WHERE "projectId" = ?';
60
70
  const params = [projectId];
61
71
  if (status && status !== "all") {
62
72
  whereClause += " AND status = ?";
@@ -66,9 +76,9 @@ function createWorkflowHandlers(db) {
66
76
  whereClause += " AND name LIKE ?";
67
77
  params.push(`%${search}%`);
68
78
  }
69
- const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params)).rows;
79
+ const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params);
70
80
  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;
81
+ const rows = await queryRows(`SELECT * FROM workflow_definition ${whereClause} ORDER BY "updatedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
72
82
  return {
73
83
  definitions: rows.map(rowToDefinition),
74
84
  total
@@ -77,7 +87,7 @@ function createWorkflowHandlers(db) {
77
87
  async function createDefinition(input, context) {
78
88
  const id = generateId("wfdef");
79
89
  const now = new Date().toISOString();
80
- await db.execute(`INSERT INTO workflow_definition (id, projectId, organizationId, name, description, type, status, createdAt, updatedAt)
90
+ await execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
81
91
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
82
92
  id,
83
93
  context.projectId,
@@ -89,15 +99,15 @@ function createWorkflowHandlers(db) {
89
99
  now,
90
100
  now
91
101
  ]);
92
- const rows = (await db.query(`SELECT * FROM workflow_definition WHERE id = ?`, [id])).rows;
102
+ const rows = await queryRows(`SELECT * FROM workflow_definition WHERE id = ?`, [id]);
93
103
  return rowToDefinition(rows[0]);
94
104
  }
95
105
  async function addStep(input) {
96
106
  const id = generateId("wfstep");
97
107
  const now = new Date().toISOString();
98
- const maxOrderResult = (await db.query(`SELECT MAX(stepOrder) as maxOrder FROM workflow_step WHERE definitionId = ?`, [input.definitionId])).rows;
108
+ const maxOrderResult = await queryRows(`SELECT MAX("stepOrder") as maxOrder FROM workflow_step WHERE "definitionId" = ?`, [input.definitionId]);
99
109
  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)
110
+ await execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "autoApproveCondition", "timeoutHours", "createdAt")
101
111
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
102
112
  id,
103
113
  input.definitionId,
@@ -110,11 +120,11 @@ function createWorkflowHandlers(db) {
110
120
  input.timeoutHours ?? null,
111
121
  now
112
122
  ]);
113
- const rows = (await db.query(`SELECT * FROM workflow_step WHERE id = ?`, [id])).rows;
123
+ const rows = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [id]);
114
124
  return rowToStep(rows[0]);
115
125
  }
116
126
  async function getSteps(definitionId) {
117
- const rows = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? ORDER BY stepOrder`, [definitionId])).rows;
127
+ const rows = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder"`, [definitionId]);
118
128
  return rows.map(rowToStep);
119
129
  }
120
130
  async function listInstances(input) {
@@ -126,10 +136,10 @@ function createWorkflowHandlers(db) {
126
136
  limit = 20,
127
137
  offset = 0
128
138
  } = input;
129
- let whereClause = "WHERE projectId = ?";
139
+ let whereClause = 'WHERE "projectId" = ?';
130
140
  const params = [projectId];
131
141
  if (definitionId) {
132
- whereClause += " AND definitionId = ?";
142
+ whereClause += ' AND "definitionId" = ?';
133
143
  params.push(definitionId);
134
144
  }
135
145
  if (status && status !== "all") {
@@ -137,12 +147,12 @@ function createWorkflowHandlers(db) {
137
147
  params.push(status);
138
148
  }
139
149
  if (requestedBy) {
140
- whereClause += " AND requestedBy = ?";
150
+ whereClause += ' AND "requestedBy" = ?';
141
151
  params.push(requestedBy);
142
152
  }
143
- const countResult = (await db.query(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params)).rows;
153
+ const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params);
144
154
  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;
155
+ const rows = await queryRows(`SELECT * FROM workflow_instance ${whereClause} ORDER BY "startedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
146
156
  return {
147
157
  instances: rows.map(rowToInstance),
148
158
  total
@@ -151,9 +161,9 @@ function createWorkflowHandlers(db) {
151
161
  async function startInstance(input, context) {
152
162
  const id = generateId("wfinst");
153
163
  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;
164
+ const steps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder" LIMIT 1`, [input.definitionId]);
155
165
  const firstStepId = steps[0]?.id ?? null;
156
- await db.execute(`INSERT INTO workflow_instance (id, projectId, definitionId, status, currentStepId, data, requestedBy, startedAt)
166
+ await execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt")
157
167
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
158
168
  id,
159
169
  context.projectId,
@@ -165,36 +175,32 @@ function createWorkflowHandlers(db) {
165
175
  now
166
176
  ]);
167
177
  if (firstStepId) {
168
- await db.execute(`INSERT INTO workflow_approval (id, instanceId, stepId, status, createdAt)
178
+ await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
169
179
  VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
170
180
  }
171
- const rows = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [id])).rows;
181
+ const rows = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [id]);
172
182
  return rowToInstance(rows[0]);
173
183
  }
174
184
  async function approveStep(input, context) {
175
185
  const now = new Date().toISOString();
176
- const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
177
- input.instanceId
178
- ])).rows;
186
+ const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
179
187
  if (!instances[0]) {
180
188
  throw new Error("NOT_FOUND");
181
189
  }
182
190
  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'`, [
191
+ await execute(`UPDATE workflow_approval SET status = 'APPROVED', "actorId" = ?, comment = ?, "decidedAt" = ?
192
+ WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
185
193
  context.actorId,
186
194
  input.comment ?? null,
187
195
  now,
188
196
  input.instanceId,
189
197
  instance.currentStepId
190
198
  ]);
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;
199
+ const currentStep = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [instance.currentStepId]);
200
+ const nextSteps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? AND "stepOrder" > ? ORDER BY "stepOrder" LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0]);
195
201
  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)
202
+ await execute(`UPDATE workflow_instance SET "currentStepId" = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
203
+ await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
198
204
  VALUES (?, ?, ?, ?, ?)`, [
199
205
  generateId("wfappr"),
200
206
  input.instanceId,
@@ -203,37 +209,31 @@ function createWorkflowHandlers(db) {
203
209
  now
204
210
  ]);
205
211
  } else {
206
- await db.execute(`UPDATE workflow_instance SET status = 'COMPLETED', currentStepId = NULL, completedAt = ? WHERE id = ?`, [now, input.instanceId]);
212
+ await execute(`UPDATE workflow_instance SET status = 'COMPLETED', "currentStepId" = NULL, "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
207
213
  }
208
- const updated = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
209
- input.instanceId
210
- ])).rows;
214
+ const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
211
215
  return rowToInstance(updated[0]);
212
216
  }
213
217
  async function rejectStep(input, context) {
214
218
  const now = new Date().toISOString();
215
- const instances = (await db.query(`SELECT * FROM workflow_instance WHERE id = ?`, [
216
- input.instanceId
217
- ])).rows;
219
+ const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
218
220
  if (!instances[0]) {
219
221
  throw new Error("NOT_FOUND");
220
222
  }
221
- await db.execute(`UPDATE workflow_approval SET status = 'REJECTED', actorId = ?, comment = ?, decidedAt = ?
222
- WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
223
+ await execute(`UPDATE workflow_approval SET status = 'REJECTED', "actorId" = ?, comment = ?, "decidedAt" = ?
224
+ WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
223
225
  context.actorId,
224
226
  input.reason,
225
227
  now,
226
228
  input.instanceId,
227
229
  instances[0].currentStepId
228
230
  ]);
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;
231
+ await execute(`UPDATE workflow_instance SET status = 'REJECTED', "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
232
+ const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
233
233
  return rowToInstance(updated[0]);
234
234
  }
235
235
  async function getApprovals(instanceId) {
236
- const rows = (await db.query(`SELECT * FROM workflow_approval WHERE instanceId = ? ORDER BY createdAt`, [instanceId])).rows;
236
+ const rows = await queryRows(`SELECT * FROM workflow_approval WHERE "instanceId" = ? ORDER BY "createdAt"`, [instanceId]);
237
237
  return rows.map(rowToApproval);
238
238
  }
239
239
  return {