@contractspec/example.workflow-system 3.7.7 → 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 (37) hide show
  1. package/README.md +3 -0
  2. package/dist/browser/handlers/index.js +43 -43
  3. package/dist/browser/handlers/workflow.handlers.js +43 -43
  4. package/dist/browser/index.js +572 -183
  5. package/dist/browser/shared/demo-scenario.js +213 -0
  6. package/dist/browser/ui/WorkflowDashboard.visualizations.js +239 -0
  7. package/dist/browser/ui/hooks/index.js +0 -47
  8. package/dist/browser/ui/hooks/useWorkflowList.js +5 -3
  9. package/dist/browser/ui/index.js +5 -3
  10. package/dist/browser/ui/renderers/index.js +409 -73
  11. package/dist/browser/ui/renderers/workflow.markdown.js +409 -73
  12. package/dist/browser/visualizations/catalog.js +132 -0
  13. package/dist/browser/visualizations/index.js +133 -0
  14. package/dist/browser/visualizations/selectors.js +195 -0
  15. package/dist/example.test.d.ts +1 -0
  16. package/dist/handlers/index.js +43 -43
  17. package/dist/handlers/workflow.handlers.js +43 -43
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +572 -183
  20. package/dist/shared/demo-scenario.d.ts +43 -0
  21. package/dist/shared/demo-scenario.js +214 -0
  22. package/dist/ui/WorkflowDashboard.visualizations.d.ts +4 -0
  23. package/dist/ui/WorkflowDashboard.visualizations.js +240 -0
  24. package/dist/ui/hooks/index.js +0 -47
  25. package/dist/ui/hooks/useWorkflowList.d.ts +2 -1
  26. package/dist/ui/hooks/useWorkflowList.js +5 -3
  27. package/dist/ui/index.js +5 -3
  28. package/dist/ui/renderers/index.js +409 -73
  29. package/dist/ui/renderers/workflow.markdown.js +409 -73
  30. package/dist/visualizations/catalog.d.ts +11 -0
  31. package/dist/visualizations/catalog.js +133 -0
  32. package/dist/visualizations/index.d.ts +2 -0
  33. package/dist/visualizations/index.js +134 -0
  34. package/dist/visualizations/selectors.d.ts +11 -0
  35. package/dist/visualizations/selectors.js +196 -0
  36. package/dist/visualizations/selectors.test.d.ts +1 -0
  37. package/package.json +69 -8
package/README.md CHANGED
@@ -11,6 +11,7 @@ Website: https://contractspec.io
11
11
  - Multi-entity domain (workflow, instance, approval).
12
12
  - Per-entity schema/enum/event/handler/operations pattern.
13
13
  - React UI with WorkflowDashboard, hooks, and renderers.
14
+ - Contract-backed visualizations for workflow status, throughput, and workload comparison.
14
15
  - Capability and feature definition patterns.
15
16
  - Seeder and test-spec patterns.
16
17
 
@@ -19,6 +20,7 @@ Website: https://contractspec.io
19
20
  From `packages/examples/workflow-system`:
20
21
  - `bun run dev`
21
22
  - `bun run build`
23
+ - `bun run test`
22
24
  - `bun run typecheck`
23
25
 
24
26
  ## Usage
@@ -63,6 +65,7 @@ Use `@contractspec/example.workflow-system` as a reference implementation, or im
63
65
  - `bun run build:bundle` — contractspec-bun-build transpile
64
66
  - `bun run build:types` — contractspec-bun-build types
65
67
  - `bun run prebuild` — contractspec-bun-build prebuild
68
+ - `bun run test` — bun test
66
69
 
67
70
  ## Recent Updates
68
71
 
@@ -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 {
@@ -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 {