@bratsos/workflow-engine-console 1.0.0-alpha.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.
@@ -0,0 +1,517 @@
1
+ import { ALL_CAPABILITIES, isStatementTimeout, ConsoleQueryTimeoutError, clampLimit, decodeCursor, ConsoleBadRequestError, encodeCursor, CONSOLE_STATUSES, isConsoleStatus } from './chunk-THOZTVDT.js';
2
+
3
+ // src/prisma-read-port.ts
4
+ function utcParam(position) {
5
+ if (!Number.isInteger(position) || position < 1) {
6
+ throw new Error(
7
+ `utcParam: position must be a positive integer, got ${position}`
8
+ );
9
+ }
10
+ return `($${position}::timestamptz AT TIME ZONE 'UTC')`;
11
+ }
12
+ function toNumber(value) {
13
+ if (value === null || value === void 0) return 0;
14
+ if (typeof value === "number") return Number.isFinite(value) ? value : 0;
15
+ if (typeof value === "bigint") return Number(value);
16
+ if (typeof value === "string") {
17
+ const parsed = Number(value);
18
+ return Number.isFinite(parsed) ? parsed : 0;
19
+ }
20
+ return 0;
21
+ }
22
+ function toDate(value) {
23
+ if (value === null || value === void 0) return null;
24
+ if (value instanceof Date) return value;
25
+ if (typeof value === "string") {
26
+ const parsed = new Date(value);
27
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
28
+ }
29
+ return null;
30
+ }
31
+ function mapRunSummary(row) {
32
+ return {
33
+ id: String(row.id),
34
+ createdAt: toDate(row.createdAt) ?? /* @__PURE__ */ new Date(0),
35
+ updatedAt: toDate(row.updatedAt) ?? /* @__PURE__ */ new Date(0),
36
+ workflowId: String(row.workflowId),
37
+ workflowName: String(row.workflowName),
38
+ workflowType: String(row.workflowType),
39
+ status: row.status,
40
+ startedAt: toDate(row.startedAt),
41
+ completedAt: toDate(row.completedAt),
42
+ duration: row.duration !== null && row.duration !== void 0 ? toNumber(row.duration) : null,
43
+ totalCost: toNumber(row.totalCost),
44
+ totalTokens: toNumber(row.totalTokens),
45
+ priority: toNumber(row.priority),
46
+ definitionVersion: row.definitionVersion !== null && row.definitionVersion !== void 0 ? String(row.definitionVersion) : null,
47
+ redriveCount: toNumber(row.redriveCount)
48
+ };
49
+ }
50
+ function mapStageSummary(row) {
51
+ return {
52
+ id: String(row.id),
53
+ workflowRunId: String(row.workflowRunId),
54
+ stageId: String(row.stageId),
55
+ stageName: String(row.stageName),
56
+ stageNumber: toNumber(row.stageNumber),
57
+ executionGroup: toNumber(row.executionGroup),
58
+ attempt: toNumber(row.attempt),
59
+ status: row.status,
60
+ startedAt: toDate(row.startedAt),
61
+ completedAt: toDate(row.completedAt),
62
+ duration: row.duration !== null && row.duration !== void 0 ? toNumber(row.duration) : null,
63
+ nextPollAt: toDate(row.nextPollAt),
64
+ pollInterval: row.pollInterval !== null && row.pollInterval !== void 0 ? toNumber(row.pollInterval) : null,
65
+ maxWaitUntil: toDate(row.maxWaitUntil),
66
+ errorMessage: row.errorMessage !== null && row.errorMessage !== void 0 ? String(row.errorMessage) : null
67
+ };
68
+ }
69
+ function mapStepSummary(row) {
70
+ return {
71
+ id: String(row.id),
72
+ stageRecordId: String(row.stageRecordId),
73
+ stepId: String(row.stepId),
74
+ seq: toNumber(row.seq),
75
+ kind: String(row.kind),
76
+ status: String(row.status),
77
+ attempt: toNumber(row.attempt),
78
+ leaseExpiresAt: toDate(row.leaseExpiresAt),
79
+ deadlineAt: toDate(row.deadlineAt),
80
+ externalKey: row.externalKey !== null && row.externalKey !== void 0 ? String(row.externalKey) : null,
81
+ error: row.error !== null && row.error !== void 0 ? String(row.error) : null,
82
+ createdAt: toDate(row.createdAt) ?? /* @__PURE__ */ new Date(0),
83
+ updatedAt: toDate(row.updatedAt) ?? /* @__PURE__ */ new Date(0)
84
+ };
85
+ }
86
+ function mapAnnotationSummary(row) {
87
+ return {
88
+ id: String(row.id),
89
+ createdAt: toDate(row.createdAt) ?? /* @__PURE__ */ new Date(0),
90
+ workflowRunId: String(row.workflowRunId),
91
+ workflowStageRecordId: row.workflowStageRecordId !== null && row.workflowStageRecordId !== void 0 ? String(row.workflowStageRecordId) : null,
92
+ attempt: toNumber(row.attempt),
93
+ scope: String(row.scope),
94
+ scopeId: row.scopeId !== null && row.scopeId !== void 0 ? String(row.scopeId) : null,
95
+ actorKind: row.actorKind !== null && row.actorKind !== void 0 ? String(row.actorKind) : null,
96
+ actorId: row.actorId !== null && row.actorId !== void 0 ? String(row.actorId) : null,
97
+ key: String(row.key),
98
+ value: row.value
99
+ };
100
+ }
101
+ function mapLogEntry(row) {
102
+ return {
103
+ id: String(row.id),
104
+ createdAt: toDate(row.createdAt) ?? /* @__PURE__ */ new Date(0),
105
+ workflowRunId: row.workflowRunId !== null && row.workflowRunId !== void 0 ? String(row.workflowRunId) : null,
106
+ workflowStageId: row.workflowStageId !== null && row.workflowStageId !== void 0 ? String(row.workflowStageId) : null,
107
+ level: String(row.level),
108
+ message: String(row.message)
109
+ };
110
+ }
111
+ function mapRunEvent(row) {
112
+ return {
113
+ id: String(row.id),
114
+ workflowRunId: String(row.workflowRunId),
115
+ sequence: toNumber(row.sequence),
116
+ eventType: String(row.eventType),
117
+ occurredAt: toDate(row.occurredAt) ?? /* @__PURE__ */ new Date(0),
118
+ publishedAt: toDate(row.publishedAt),
119
+ retryCount: toNumber(row.retryCount),
120
+ dlqAt: toDate(row.dlqAt)
121
+ };
122
+ }
123
+ function mapSuspendedStage(row) {
124
+ return {
125
+ id: String(row.id),
126
+ workflowRunId: String(row.workflowRunId),
127
+ workflowId: String(row.workflowId),
128
+ stageId: String(row.stageId),
129
+ stageName: String(row.stageName),
130
+ attempt: toNumber(row.attempt),
131
+ nextPollAt: toDate(row.nextPollAt),
132
+ pollInterval: row.pollInterval !== null && row.pollInterval !== void 0 ? toNumber(row.pollInterval) : null,
133
+ maxWaitUntil: toDate(row.maxWaitUntil)
134
+ };
135
+ }
136
+ function mapDeadLetter(row) {
137
+ return {
138
+ id: String(row.id),
139
+ workflowRunId: String(row.workflowRunId),
140
+ sequence: toNumber(row.sequence),
141
+ eventType: String(row.eventType),
142
+ retryCount: toNumber(row.retryCount),
143
+ occurredAt: toDate(row.occurredAt) ?? /* @__PURE__ */ new Date(0),
144
+ dlqAt: toDate(row.dlqAt)
145
+ };
146
+ }
147
+ function mapWorkerInstance(row) {
148
+ return {
149
+ workerId: String(row.workerId),
150
+ runningJobs: toNumber(row.running),
151
+ oldestLockedAt: toDate(row.oldest),
152
+ lastSeenAt: toDate(row.newest)
153
+ };
154
+ }
155
+ function mapCostBucket(row) {
156
+ return {
157
+ key: String(row.key),
158
+ runs: toNumber(row.runs),
159
+ cost: toNumber(row.cost),
160
+ tokens: toNumber(row.tokens)
161
+ };
162
+ }
163
+ var PrismaConsoleReadPort = class {
164
+ capabilities = ALL_CAPABILITIES;
165
+ prisma;
166
+ statusEnumName;
167
+ statementTimeoutMs;
168
+ now;
169
+ constructor(prisma, options) {
170
+ this.prisma = prisma;
171
+ const statusEnumName = options?.statusEnumName ?? "Status";
172
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(statusEnumName)) {
173
+ throw new Error(
174
+ `Invalid statusEnumName: "${statusEnumName}". Must match /^[A-Za-z_][A-Za-z0-9_]*$/`
175
+ );
176
+ }
177
+ this.statusEnumName = statusEnumName;
178
+ const timeoutMs = options?.statementTimeoutMs ?? 15e3;
179
+ if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs < 0) {
180
+ throw new Error(
181
+ `statementTimeoutMs must be a non-negative number, got ${timeoutMs}`
182
+ );
183
+ }
184
+ this.statementTimeoutMs = Math.floor(timeoutMs);
185
+ this.now = options?.now ?? (() => /* @__PURE__ */ new Date());
186
+ }
187
+ /** One `SELECT` against whichever client the surrounding `read` gave us. */
188
+ select(client, sql, params) {
189
+ return client.$queryRawUnsafe(sql, ...params);
190
+ }
191
+ /**
192
+ * Runs one console read, whatever number of statements it takes, and
193
+ * translates a cancelled statement into `ConsoleQueryTimeoutError`.
194
+ *
195
+ * The callback receives the client to issue statements on, and every read
196
+ * that needs more than one statement issues all of them here, inside a
197
+ * single transaction — a run-detail page is six queries, and giving each
198
+ * its own interactive transaction would take six connections out of the
199
+ * consumer's pool to render one page. One transaction also means the six
200
+ * results are a coherent snapshot rather than six moments.
201
+ *
202
+ * The transaction is opened only when we were handed a root client. When
203
+ * `$transaction` is absent we are already inside the caller's transaction
204
+ * — the row-level-security case this package exists for — and we neither
205
+ * open another nor touch `statement_timeout`: `SET LOCAL` there would
206
+ * silently re-time the remainder of a transaction the console did not
207
+ * open and does not get to reconfigure. Timeouts in that case are the
208
+ * caller's to set, and a cancellation is still reported honestly because
209
+ * the catch below does not care who set the limit.
210
+ */
211
+ async read(label, fn) {
212
+ try {
213
+ if (this.statementTimeoutMs > 0 && typeof this.prisma.$transaction === "function") {
214
+ const ms = this.statementTimeoutMs;
215
+ return await this.prisma.$transaction(async (tx) => {
216
+ if (typeof tx.$executeRawUnsafe === "function") {
217
+ await tx.$executeRawUnsafe(`SET LOCAL statement_timeout = ${ms}`);
218
+ }
219
+ return await fn(tx);
220
+ });
221
+ }
222
+ return await fn(this.prisma);
223
+ } catch (error) {
224
+ if (isStatementTimeout(error)) {
225
+ throw new ConsoleQueryTimeoutError(label, this.statementTimeoutMs, {
226
+ cause: error
227
+ });
228
+ }
229
+ throw error;
230
+ }
231
+ }
232
+ /** Convenience for the reads that are a single statement. */
233
+ query(label, sql, params) {
234
+ return this.read(label, (client) => this.select(client, sql, params));
235
+ }
236
+ async listRuns(query) {
237
+ const limit = clampLimit(query.limit);
238
+ const where = [];
239
+ const params = [];
240
+ let paramIdx = 1;
241
+ if (query.cursor !== void 0 && query.cursor !== "") {
242
+ const cursor = decodeCursor(query.cursor);
243
+ if (cursor === null) {
244
+ throw new ConsoleBadRequestError("Invalid cursor.");
245
+ }
246
+ where.push(
247
+ `("createdAt", id) < (${utcParam(paramIdx)}, $${paramIdx + 1})`
248
+ );
249
+ params.push(cursor.createdAt, cursor.id);
250
+ paramIdx += 2;
251
+ }
252
+ const statuses = query.filters?.status ?? [];
253
+ if (statuses.length > 0) {
254
+ const placeholders = statuses.map((status) => {
255
+ params.push(status);
256
+ const placeholder = `$${paramIdx}::"${this.statusEnumName}"`;
257
+ paramIdx += 1;
258
+ return placeholder;
259
+ });
260
+ where.push(`status IN (${placeholders.join(", ")})`);
261
+ }
262
+ if (query.filters?.workflowId !== void 0) {
263
+ where.push(`"workflowId" = $${paramIdx}`);
264
+ params.push(query.filters.workflowId);
265
+ paramIdx += 1;
266
+ }
267
+ if (query.filters?.workflowType !== void 0) {
268
+ where.push(`"workflowType" = $${paramIdx}`);
269
+ params.push(query.filters.workflowType);
270
+ paramIdx += 1;
271
+ }
272
+ if (query.filters?.definitionVersion !== void 0) {
273
+ where.push(`"definitionVersion" = $${paramIdx}`);
274
+ params.push(query.filters.definitionVersion);
275
+ paramIdx += 1;
276
+ }
277
+ if (query.filters?.createdAfter !== void 0) {
278
+ where.push(`"createdAt" >= ${utcParam(paramIdx)}`);
279
+ params.push(query.filters.createdAfter);
280
+ paramIdx += 1;
281
+ }
282
+ if (query.filters?.createdBefore !== void 0) {
283
+ where.push(`"createdAt" < ${utcParam(paramIdx)}`);
284
+ params.push(query.filters.createdBefore);
285
+ paramIdx += 1;
286
+ }
287
+ const whereClause = where.length > 0 ? `
288
+ WHERE ${where.join(" AND ")}` : "";
289
+ const sql = `SELECT id, "createdAt", "updatedAt", "workflowId", "workflowName", "workflowType",
290
+ status, "startedAt", "completedAt", duration, "totalCost", "totalTokens", priority,
291
+ "definitionVersion", "redriveCount"
292
+ FROM "workflow_runs"${whereClause}
293
+ ORDER BY "createdAt" DESC, id DESC
294
+ LIMIT ${limit + 1}`;
295
+ const rows = await this.query("runs.list", sql, params);
296
+ const hasNextPage = rows.length > limit;
297
+ const returnedRows = hasNextPage ? rows.slice(0, limit) : rows;
298
+ const runs = returnedRows.map(mapRunSummary);
299
+ const last = runs.at(-1);
300
+ const nextCursor = hasNextPage && last !== void 0 ? encodeCursor({ createdAt: last.createdAt, id: last.id }) : null;
301
+ return { runs, nextCursor };
302
+ }
303
+ async getRunDetail(runId, options) {
304
+ const stepLimit = clampLimit(options?.stepLimit, 200);
305
+ const annotationLimit = clampLimit(options?.annotationLimit, 200);
306
+ const logLimit = clampLimit(options?.logLimit, 200);
307
+ const eventLimit = clampLimit(options?.eventLimit, 200);
308
+ return this.read("run.detail", async (client) => {
309
+ const runRows = await this.select(
310
+ client,
311
+ `SELECT id, "createdAt", "updatedAt", "workflowId", "workflowName", "workflowType",
312
+ status, "startedAt", "completedAt", duration, "totalCost", "totalTokens", priority,
313
+ "definitionVersion", "redriveCount",
314
+ input, output, config, metadata
315
+ FROM "workflow_runs" WHERE id = $1`,
316
+ [runId]
317
+ );
318
+ const runRow = runRows[0];
319
+ if (!runRow) return null;
320
+ const stageRows = await this.select(
321
+ client,
322
+ `SELECT id, "workflowRunId", "stageId", "stageName", "stageNumber", "executionGroup",
323
+ attempt, status, "startedAt", "completedAt", duration, "nextPollAt",
324
+ "pollInterval", "maxWaitUntil", "errorMessage"
325
+ FROM "workflow_stages" WHERE "workflowRunId" = $1
326
+ ORDER BY "stageNumber" ASC, "stageId" ASC`,
327
+ [runId]
328
+ );
329
+ const stepRows = await this.select(
330
+ client,
331
+ `SELECT s.id, s."stageRecordId", s."stepId", s.seq, s.kind, s.status, s.attempt,
332
+ s."leaseExpiresAt", s."deadlineAt", s."externalKey", s.error, s."createdAt", s."updatedAt"
333
+ FROM "workflow_steps" s
334
+ JOIN "workflow_stages" st ON st.id = s."stageRecordId"
335
+ WHERE st."workflowRunId" = $1
336
+ ORDER BY st."stageNumber" ASC, s.seq ASC
337
+ LIMIT ${stepLimit + 1}`,
338
+ [runId]
339
+ );
340
+ const annotationRows = await this.select(
341
+ client,
342
+ `SELECT id, "createdAt", "workflowRunId", "workflowStageRecordId", attempt, scope,
343
+ "scopeId", "actorKind", "actorId", key, value
344
+ FROM "workflow_annotations" WHERE "workflowRunId" = $1
345
+ ORDER BY "createdAt" DESC, id DESC
346
+ LIMIT ${annotationLimit + 1}`,
347
+ [runId]
348
+ );
349
+ const logRows = await this.select(
350
+ client,
351
+ `SELECT id, "createdAt", "workflowRunId", "workflowStageId", level, message
352
+ FROM "workflow_logs" WHERE "workflowRunId" = $1
353
+ ORDER BY "createdAt" DESC, id DESC
354
+ LIMIT ${logLimit + 1}`,
355
+ [runId]
356
+ );
357
+ const eventRows = await this.select(
358
+ client,
359
+ `SELECT id, "workflowRunId", sequence, "eventType", "occurredAt", "publishedAt",
360
+ "retryCount", "dlqAt"
361
+ FROM "outbox_events" WHERE "workflowRunId" = $1
362
+ ORDER BY sequence ASC
363
+ LIMIT ${eventLimit + 1}`,
364
+ [runId]
365
+ );
366
+ const truncated = {
367
+ steps: stepRows.length > stepLimit,
368
+ annotations: annotationRows.length > annotationLimit,
369
+ logs: logRows.length > logLimit,
370
+ events: eventRows.length > eventLimit
371
+ };
372
+ return {
373
+ run: {
374
+ ...mapRunSummary(runRow),
375
+ input: runRow.input,
376
+ output: runRow.output,
377
+ config: runRow.config,
378
+ metadata: runRow.metadata
379
+ },
380
+ stages: stageRows.map(mapStageSummary),
381
+ steps: stepRows.slice(0, stepLimit).map(mapStepSummary),
382
+ annotations: annotationRows.slice(0, annotationLimit).map(mapAnnotationSummary),
383
+ logs: logRows.slice(0, logLimit).map(mapLogEntry),
384
+ events: eventRows.slice(0, eventLimit).map(mapRunEvent),
385
+ truncated
386
+ };
387
+ });
388
+ }
389
+ async listRunEvents(runId, afterSequence, limit) {
390
+ const clampedLimit = clampLimit(limit, 200);
391
+ const rows = await this.query(
392
+ "run.events.tail",
393
+ `SELECT id, "workflowRunId", sequence, "eventType", "occurredAt", "publishedAt",
394
+ "retryCount", "dlqAt"
395
+ FROM "outbox_events" WHERE "workflowRunId" = $1 AND sequence > $2
396
+ ORDER BY sequence ASC LIMIT ${clampedLimit}`,
397
+ [runId, Math.floor(afterSequence)]
398
+ );
399
+ return rows.map(mapRunEvent);
400
+ }
401
+ async getQueueHealth() {
402
+ const [
403
+ countsResult,
404
+ oldestPendingResult,
405
+ leasesResult,
406
+ overduePollsResult
407
+ ] = await this.read(
408
+ "queue.health",
409
+ async (client) => Promise.all([
410
+ this.select(
411
+ client,
412
+ `SELECT status::text AS status, count(*) AS count FROM "job_queue" GROUP BY status`,
413
+ []
414
+ ),
415
+ this.select(
416
+ client,
417
+ `SELECT min("createdAt") AS oldest FROM "job_queue" WHERE status = $1::"${this.statusEnumName}"`,
418
+ ["PENDING"]
419
+ ),
420
+ this.select(
421
+ client,
422
+ `SELECT min("lockedAt") AS oldest FROM "job_queue" WHERE status = $1::"${this.statusEnumName}" AND "lockedAt" IS NOT NULL`,
423
+ ["RUNNING"]
424
+ ),
425
+ this.select(
426
+ client,
427
+ `SELECT count(*) AS count FROM "job_queue" WHERE status = $1::"${this.statusEnumName}" AND "nextPollAt" IS NOT NULL AND "nextPollAt" <= ${utcParam(2)}`,
428
+ ["SUSPENDED", this.now()]
429
+ )
430
+ ])
431
+ );
432
+ const countsByStatus = Object.fromEntries(
433
+ CONSOLE_STATUSES.map((status) => [status, 0])
434
+ );
435
+ for (const row of countsResult) {
436
+ if (isConsoleStatus(row.status)) {
437
+ countsByStatus[row.status] = toNumber(row.count);
438
+ }
439
+ }
440
+ return {
441
+ countsByStatus,
442
+ oldestPendingAt: toDate(oldestPendingResult[0]?.oldest),
443
+ oldestLeaseAt: toDate(leasesResult[0]?.oldest),
444
+ overduePolls: toNumber(overduePollsResult[0]?.count)
445
+ };
446
+ }
447
+ async listSuspendedStages(limit) {
448
+ const clampedLimit = clampLimit(limit, 50);
449
+ const rows = await this.query(
450
+ "stages.suspended",
451
+ `SELECT st.id, st."workflowRunId", r."workflowId", st."stageId", st."stageName",
452
+ st.attempt, st."nextPollAt", st."pollInterval", st."maxWaitUntil"
453
+ FROM "workflow_stages" st
454
+ JOIN "workflow_runs" r ON r.id = st."workflowRunId"
455
+ WHERE st.status = $1::"${this.statusEnumName}" AND st."nextPollAt" IS NOT NULL
456
+ ORDER BY st."nextPollAt" ASC
457
+ LIMIT ${clampedLimit}`,
458
+ ["SUSPENDED"]
459
+ );
460
+ return rows.map(mapSuspendedStage);
461
+ }
462
+ async listDeadLetters(limit) {
463
+ const clampedLimit = clampLimit(limit, 50);
464
+ const rows = await this.query(
465
+ "outbox.deadLetters",
466
+ `SELECT id, "workflowRunId", sequence, "eventType", "retryCount", "occurredAt", "dlqAt"
467
+ FROM "outbox_events" WHERE "dlqAt" IS NOT NULL
468
+ ORDER BY "dlqAt" DESC LIMIT ${clampedLimit}`,
469
+ []
470
+ );
471
+ return rows.map(mapDeadLetter);
472
+ }
473
+ async listWorkers() {
474
+ const rows = await this.query(
475
+ "workers.list",
476
+ `SELECT "workerId", count(*) AS running, min("lockedAt") AS oldest, max("lockedAt") AS newest
477
+ FROM "job_queue"
478
+ WHERE status = $1::"${this.statusEnumName}" AND "workerId" IS NOT NULL
479
+ GROUP BY "workerId" ORDER BY "workerId" ASC`,
480
+ ["RUNNING"]
481
+ );
482
+ return rows.map(mapWorkerInstance);
483
+ }
484
+ async getCosts(query) {
485
+ const now = this.now();
486
+ const to = query.to ?? now;
487
+ const defaultDays = query.by === "workflow" ? 7 : 30;
488
+ const from = query.from ?? new Date(now.getTime() - defaultDays * 24 * 60 * 60 * 1e3);
489
+ if (query.by === "workflow") {
490
+ const rows = await this.query(
491
+ "costs.byWorkflow",
492
+ `SELECT "workflowId" AS key, count(*) AS runs, sum("totalCost") AS cost, sum("totalTokens") AS tokens
493
+ FROM "workflow_runs" WHERE "createdAt" >= ${utcParam(1)} AND "createdAt" < ${utcParam(2)}
494
+ GROUP BY "workflowId" ORDER BY sum("totalCost") DESC`,
495
+ [from, to]
496
+ );
497
+ return rows.map(mapCostBucket);
498
+ } else {
499
+ const rows = await this.query(
500
+ "costs.byDay",
501
+ `SELECT to_char(date_trunc('day', "createdAt"), 'YYYY-MM-DD') AS key,
502
+ count(*) AS runs, sum("totalCost") AS cost, sum("totalTokens") AS tokens
503
+ FROM "workflow_runs" WHERE "createdAt" >= ${utcParam(1)} AND "createdAt" < ${utcParam(2)}
504
+ GROUP BY 1 ORDER BY 1 DESC`,
505
+ [from, to]
506
+ );
507
+ return rows.map(mapCostBucket);
508
+ }
509
+ }
510
+ };
511
+ function createPrismaConsoleReadPort(prisma, options) {
512
+ return new PrismaConsoleReadPort(prisma, options);
513
+ }
514
+
515
+ export { PrismaConsoleReadPort, createPrismaConsoleReadPort };
516
+ //# sourceMappingURL=chunk-HUHLRNNM.js.map
517
+ //# sourceMappingURL=chunk-HUHLRNNM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prisma-read-port.ts"],"names":[],"mappings":"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","file":"chunk-HUHLRNNM.js","sourcesContent":["import {\n ConsoleBadRequestError,\n ConsoleQueryTimeoutError,\n isStatementTimeout,\n} from \"./errors\";\nimport {\n ALL_CAPABILITIES,\n type AnnotationSummary,\n CONSOLE_STATUSES,\n type ConsoleCapabilities,\n type ConsoleReadPort,\n type ConsoleStatus,\n type CostBucket,\n type CostQuery,\n clampLimit,\n type DeadLetter,\n decodeCursor,\n encodeCursor,\n isConsoleStatus,\n type LogEntry,\n type QueueHealth,\n type RunDetail,\n type RunDetailOptions,\n type RunEvent,\n type RunListPage,\n type RunListQuery,\n type RunSummary,\n type StageSummary,\n type StepSummary,\n type SuspendedStage,\n type WorkerInstance,\n} from \"./read-port\";\n\n/**\n * Minimal structural client contract.\n *\n * This avoids a hard dependency on `@prisma/client` or the engine's internals,\n * letting callers pass either their root PrismaClient or an existing interactive\n * transaction client.\n */\nexport interface ConsolePrismaClient {\n $queryRawUnsafe<T = unknown>(query: string, ...values: unknown[]): Promise<T>;\n $executeRawUnsafe?(query: string, ...values: unknown[]): Promise<number>;\n $transaction?<T>(fn: (tx: ConsolePrismaClient) => Promise<T>): Promise<T>;\n}\n\nexport interface PrismaConsoleReadPortOptions {\n statusEnumName?: string; // default \"Status\"\n statementTimeoutMs?: number; // default 15000; 0 disables\n now?: () => Date; // default () => new Date()\n}\n\n/**\n * Wraps a positional parameter carrying a JS Date in a timestamptz-to-UTC conversion.\n *\n * Mirrors `utc-timestamps.ts` in the engine's Prisma adapter: Prisma binds a Date\n * as timestamptz and Postgres converts it through the session timezone, which skews\n * against naive timestamp columns unless explicitly converted via AT TIME ZONE 'UTC'.\n */\nfunction utcParam(position: number): string {\n if (!Number.isInteger(position) || position < 1) {\n throw new Error(\n `utcParam: position must be a positive integer, got ${position}`,\n );\n }\n return `($${position}::timestamptz AT TIME ZONE 'UTC')`;\n}\n\n/** Converts bigint, number, string, null, or undefined values into numbers, defaulting to 0. */\nfunction toNumber(value: unknown): number {\n if (value === null || value === undefined) return 0;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : 0;\n if (typeof value === \"bigint\") return Number(value);\n if (typeof value === \"string\") {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n return 0;\n}\n\n/** Converts Date, string, null, or undefined values into Date or null. */\nfunction toDate(value: unknown): Date | null {\n if (value === null || value === undefined) return null;\n if (value instanceof Date) return value;\n if (typeof value === \"string\") {\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n }\n return null;\n}\n\ninterface RawRunRow {\n id: string;\n createdAt: Date | string;\n updatedAt: Date | string;\n workflowId: string;\n workflowName: string;\n workflowType: string;\n status: string;\n startedAt: Date | string | null;\n completedAt: Date | string | null;\n duration: number | null;\n totalCost: number | null;\n totalTokens: number | bigint | null;\n priority: number | bigint | null;\n definitionVersion?: string | null;\n redriveCount?: number | bigint | null;\n}\n\ninterface RawRunDetailRow extends RawRunRow {\n input: unknown;\n output: unknown;\n config: unknown;\n metadata: unknown;\n}\n\ninterface RawStageRow {\n id: string;\n workflowRunId: string;\n stageId: string;\n stageName: string;\n stageNumber: number | bigint;\n executionGroup: number | bigint;\n attempt: number | bigint;\n status: string;\n startedAt: Date | string | null;\n completedAt: Date | string | null;\n duration: number | null;\n nextPollAt: Date | string | null;\n pollInterval: number | null;\n maxWaitUntil: Date | string | null;\n errorMessage: string | null;\n}\n\ninterface RawStepRow {\n id: string;\n stageRecordId: string;\n stepId: string;\n seq: number | bigint;\n kind: string;\n status: string;\n attempt: number | bigint;\n leaseExpiresAt: Date | string | null;\n deadlineAt: Date | string | null;\n externalKey: string | null;\n error: string | null;\n createdAt: Date | string;\n updatedAt: Date | string;\n}\n\ninterface RawAnnotationRow {\n id: string;\n createdAt: Date | string;\n workflowRunId: string;\n workflowStageRecordId: string | null;\n attempt: number | bigint;\n scope: string;\n scopeId: string | null;\n actorKind: string | null;\n actorId: string | null;\n key: string;\n value: unknown;\n}\n\ninterface RawLogRow {\n id: string;\n createdAt: Date | string;\n workflowRunId: string | null;\n workflowStageId: string | null;\n level: string;\n message: string;\n}\n\ninterface RawEventRow {\n id: string;\n workflowRunId: string;\n sequence: number | bigint;\n eventType: string;\n occurredAt: Date | string;\n publishedAt: Date | string | null;\n retryCount: number | bigint;\n dlqAt: Date | string | null;\n}\n\ninterface RawSuspendedStageRow {\n id: string;\n workflowRunId: string;\n workflowId: string;\n stageId: string;\n stageName: string;\n attempt: number | bigint;\n nextPollAt: Date | string | null;\n pollInterval: number | null;\n maxWaitUntil: Date | string | null;\n}\n\ninterface RawDeadLetterRow {\n id: string;\n workflowRunId: string;\n sequence: number | bigint;\n eventType: string;\n retryCount: number | bigint;\n occurredAt: Date | string;\n dlqAt: Date | string | null;\n}\n\ninterface RawWorkerRow {\n workerId: string;\n running: number | bigint;\n oldest: Date | string | null;\n newest: Date | string | null;\n}\n\ninterface RawCostRow {\n key: string;\n runs: number | bigint;\n cost: number | null;\n tokens: number | bigint | null;\n}\n\nfunction mapRunSummary(row: RawRunRow): RunSummary {\n return {\n id: String(row.id),\n createdAt: toDate(row.createdAt) ?? new Date(0),\n updatedAt: toDate(row.updatedAt) ?? new Date(0),\n workflowId: String(row.workflowId),\n workflowName: String(row.workflowName),\n workflowType: String(row.workflowType),\n status: row.status as ConsoleStatus,\n startedAt: toDate(row.startedAt),\n completedAt: toDate(row.completedAt),\n duration:\n row.duration !== null && row.duration !== undefined\n ? toNumber(row.duration)\n : null,\n totalCost: toNumber(row.totalCost),\n totalTokens: toNumber(row.totalTokens),\n priority: toNumber(row.priority),\n definitionVersion:\n row.definitionVersion !== null && row.definitionVersion !== undefined\n ? String(row.definitionVersion)\n : null,\n redriveCount: toNumber(row.redriveCount),\n };\n}\n\nfunction mapStageSummary(row: RawStageRow): StageSummary {\n return {\n id: String(row.id),\n workflowRunId: String(row.workflowRunId),\n stageId: String(row.stageId),\n stageName: String(row.stageName),\n stageNumber: toNumber(row.stageNumber),\n executionGroup: toNumber(row.executionGroup),\n attempt: toNumber(row.attempt),\n status: row.status as ConsoleStatus,\n startedAt: toDate(row.startedAt),\n completedAt: toDate(row.completedAt),\n duration:\n row.duration !== null && row.duration !== undefined\n ? toNumber(row.duration)\n : null,\n nextPollAt: toDate(row.nextPollAt),\n pollInterval:\n row.pollInterval !== null && row.pollInterval !== undefined\n ? toNumber(row.pollInterval)\n : null,\n maxWaitUntil: toDate(row.maxWaitUntil),\n errorMessage:\n row.errorMessage !== null && row.errorMessage !== undefined\n ? String(row.errorMessage)\n : null,\n };\n}\n\nfunction mapStepSummary(row: RawStepRow): StepSummary {\n return {\n id: String(row.id),\n stageRecordId: String(row.stageRecordId),\n stepId: String(row.stepId),\n seq: toNumber(row.seq),\n kind: String(row.kind),\n status: String(row.status),\n attempt: toNumber(row.attempt),\n leaseExpiresAt: toDate(row.leaseExpiresAt),\n deadlineAt: toDate(row.deadlineAt),\n externalKey:\n row.externalKey !== null && row.externalKey !== undefined\n ? String(row.externalKey)\n : null,\n error:\n row.error !== null && row.error !== undefined ? String(row.error) : null,\n createdAt: toDate(row.createdAt) ?? new Date(0),\n updatedAt: toDate(row.updatedAt) ?? new Date(0),\n };\n}\n\nfunction mapAnnotationSummary(row: RawAnnotationRow): AnnotationSummary {\n return {\n id: String(row.id),\n createdAt: toDate(row.createdAt) ?? new Date(0),\n workflowRunId: String(row.workflowRunId),\n workflowStageRecordId:\n row.workflowStageRecordId !== null &&\n row.workflowStageRecordId !== undefined\n ? String(row.workflowStageRecordId)\n : null,\n attempt: toNumber(row.attempt),\n scope: String(row.scope),\n scopeId:\n row.scopeId !== null && row.scopeId !== undefined\n ? String(row.scopeId)\n : null,\n actorKind:\n row.actorKind !== null && row.actorKind !== undefined\n ? String(row.actorKind)\n : null,\n actorId:\n row.actorId !== null && row.actorId !== undefined\n ? String(row.actorId)\n : null,\n key: String(row.key),\n value: row.value,\n };\n}\n\nfunction mapLogEntry(row: RawLogRow): LogEntry {\n return {\n id: String(row.id),\n createdAt: toDate(row.createdAt) ?? new Date(0),\n workflowRunId:\n row.workflowRunId !== null && row.workflowRunId !== undefined\n ? String(row.workflowRunId)\n : null,\n workflowStageId:\n row.workflowStageId !== null && row.workflowStageId !== undefined\n ? String(row.workflowStageId)\n : null,\n level: String(row.level),\n message: String(row.message),\n };\n}\n\nfunction mapRunEvent(row: RawEventRow): RunEvent {\n return {\n id: String(row.id),\n workflowRunId: String(row.workflowRunId),\n sequence: toNumber(row.sequence),\n eventType: String(row.eventType),\n occurredAt: toDate(row.occurredAt) ?? new Date(0),\n publishedAt: toDate(row.publishedAt),\n retryCount: toNumber(row.retryCount),\n dlqAt: toDate(row.dlqAt),\n };\n}\n\nfunction mapSuspendedStage(row: RawSuspendedStageRow): SuspendedStage {\n return {\n id: String(row.id),\n workflowRunId: String(row.workflowRunId),\n workflowId: String(row.workflowId),\n stageId: String(row.stageId),\n stageName: String(row.stageName),\n attempt: toNumber(row.attempt),\n nextPollAt: toDate(row.nextPollAt),\n pollInterval:\n row.pollInterval !== null && row.pollInterval !== undefined\n ? toNumber(row.pollInterval)\n : null,\n maxWaitUntil: toDate(row.maxWaitUntil),\n };\n}\n\nfunction mapDeadLetter(row: RawDeadLetterRow): DeadLetter {\n return {\n id: String(row.id),\n workflowRunId: String(row.workflowRunId),\n sequence: toNumber(row.sequence),\n eventType: String(row.eventType),\n retryCount: toNumber(row.retryCount),\n occurredAt: toDate(row.occurredAt) ?? new Date(0),\n dlqAt: toDate(row.dlqAt),\n };\n}\n\nfunction mapWorkerInstance(row: RawWorkerRow): WorkerInstance {\n return {\n workerId: String(row.workerId),\n runningJobs: toNumber(row.running),\n oldestLockedAt: toDate(row.oldest),\n lastSeenAt: toDate(row.newest),\n };\n}\n\nfunction mapCostBucket(row: RawCostRow): CostBucket {\n return {\n key: String(row.key),\n runs: toNumber(row.runs),\n cost: toNumber(row.cost),\n tokens: toNumber(row.tokens),\n };\n}\n\nexport class PrismaConsoleReadPort implements ConsoleReadPort {\n readonly capabilities: ConsoleCapabilities = ALL_CAPABILITIES;\n\n private readonly prisma: ConsolePrismaClient;\n private readonly statusEnumName: string;\n private readonly statementTimeoutMs: number;\n private readonly now: () => Date;\n\n constructor(\n prisma: ConsolePrismaClient,\n options?: PrismaConsoleReadPortOptions,\n ) {\n this.prisma = prisma;\n\n const statusEnumName = options?.statusEnumName ?? \"Status\";\n // The enum name is interpolated directly into SQL as an identifier cast\n // (e.g. $1::\"Status\"). Validate against Postgres identifier syntax to guarantee\n // safe interpolation and prevent SQL injection.\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(statusEnumName)) {\n throw new Error(\n `Invalid statusEnumName: \"${statusEnumName}\". Must match /^[A-Za-z_][A-Za-z0-9_]*$/`,\n );\n }\n this.statusEnumName = statusEnumName;\n\n const timeoutMs = options?.statementTimeoutMs ?? 15000;\n if (\n typeof timeoutMs !== \"number\" ||\n !Number.isFinite(timeoutMs) ||\n timeoutMs < 0\n ) {\n throw new Error(\n `statementTimeoutMs must be a non-negative number, got ${timeoutMs}`,\n );\n }\n this.statementTimeoutMs = Math.floor(timeoutMs);\n\n this.now = options?.now ?? (() => new Date());\n }\n\n /** One `SELECT` against whichever client the surrounding `read` gave us. */\n private select<T>(\n client: ConsolePrismaClient,\n sql: string,\n params: unknown[],\n ): Promise<T[]> {\n return client.$queryRawUnsafe<T[]>(sql, ...params);\n }\n\n /**\n * Runs one console read, whatever number of statements it takes, and\n * translates a cancelled statement into `ConsoleQueryTimeoutError`.\n *\n * The callback receives the client to issue statements on, and every read\n * that needs more than one statement issues all of them here, inside a\n * single transaction — a run-detail page is six queries, and giving each\n * its own interactive transaction would take six connections out of the\n * consumer's pool to render one page. One transaction also means the six\n * results are a coherent snapshot rather than six moments.\n *\n * The transaction is opened only when we were handed a root client. When\n * `$transaction` is absent we are already inside the caller's transaction\n * — the row-level-security case this package exists for — and we neither\n * open another nor touch `statement_timeout`: `SET LOCAL` there would\n * silently re-time the remainder of a transaction the console did not\n * open and does not get to reconfigure. Timeouts in that case are the\n * caller's to set, and a cancellation is still reported honestly because\n * the catch below does not care who set the limit.\n */\n private async read<T>(\n label: string,\n fn: (client: ConsolePrismaClient) => Promise<T>,\n ): Promise<T> {\n try {\n if (\n this.statementTimeoutMs > 0 &&\n typeof this.prisma.$transaction === \"function\"\n ) {\n const ms = this.statementTimeoutMs;\n return await this.prisma.$transaction(async (tx) => {\n if (typeof tx.$executeRawUnsafe === \"function\") {\n // `ms` is a validated integer, and a bare number here is\n // milliseconds. It is the one interpolated value in this file\n // besides the enum name.\n await tx.$executeRawUnsafe(`SET LOCAL statement_timeout = ${ms}`);\n }\n return await fn(tx);\n });\n }\n return await fn(this.prisma);\n } catch (error) {\n if (isStatementTimeout(error)) {\n throw new ConsoleQueryTimeoutError(label, this.statementTimeoutMs, {\n cause: error,\n });\n }\n throw error;\n }\n }\n\n /** Convenience for the reads that are a single statement. */\n private query<T>(\n label: string,\n sql: string,\n params: unknown[],\n ): Promise<T[]> {\n return this.read(label, (client) => this.select<T>(client, sql, params));\n }\n\n async listRuns(query: RunListQuery): Promise<RunListPage> {\n const limit = clampLimit(query.limit);\n const where: string[] = [];\n const params: unknown[] = [];\n let paramIdx = 1;\n\n if (query.cursor !== undefined && query.cursor !== \"\") {\n const cursor = decodeCursor(query.cursor);\n if (cursor === null) {\n throw new ConsoleBadRequestError(\"Invalid cursor.\");\n }\n // Row-wise comparison is load-bearing: Postgres evaluates (a, b) < (c, d)\n // using an index range scan on the composite index (\"createdAt\" DESC, id DESC).\n // Rewriting as `a < c OR (a = c AND b < d)` prevents Postgres from using the index\n // efficiently and degrades into a bitmap scan or sequential scan.\n where.push(\n `(\"createdAt\", id) < (${utcParam(paramIdx)}, $${paramIdx + 1})`,\n );\n params.push(cursor.createdAt, cursor.id);\n paramIdx += 2;\n }\n\n const statuses = query.filters?.status ?? [];\n if (statuses.length > 0) {\n // One placeholder per status rather than `= ANY($1::\"Status\"[])`.\n // Passing a JS array through `$queryRawUnsafe` leaves array encoding to\n // the driver, and the list is a bounded enum of at most seven values,\n // so expanding it keeps every value a bound parameter with no\n // serialisation question and the same plan.\n const placeholders = statuses.map((status) => {\n params.push(status);\n const placeholder = `$${paramIdx}::\"${this.statusEnumName}\"`;\n paramIdx += 1;\n return placeholder;\n });\n where.push(`status IN (${placeholders.join(\", \")})`);\n }\n\n if (query.filters?.workflowId !== undefined) {\n where.push(`\"workflowId\" = $${paramIdx}`);\n params.push(query.filters.workflowId);\n paramIdx += 1;\n }\n\n if (query.filters?.workflowType !== undefined) {\n where.push(`\"workflowType\" = $${paramIdx}`);\n params.push(query.filters.workflowType);\n paramIdx += 1;\n }\n\n if (query.filters?.definitionVersion !== undefined) {\n where.push(`\"definitionVersion\" = $${paramIdx}`);\n params.push(query.filters.definitionVersion);\n paramIdx += 1;\n }\n\n if (query.filters?.createdAfter !== undefined) {\n where.push(`\"createdAt\" >= ${utcParam(paramIdx)}`);\n params.push(query.filters.createdAfter);\n paramIdx += 1;\n }\n\n if (query.filters?.createdBefore !== undefined) {\n where.push(`\"createdAt\" < ${utcParam(paramIdx)}`);\n params.push(query.filters.createdBefore);\n paramIdx += 1;\n }\n\n const whereClause =\n where.length > 0 ? `\\nWHERE ${where.join(\" AND \")}` : \"\";\n const sql = `SELECT id, \"createdAt\", \"updatedAt\", \"workflowId\", \"workflowName\", \"workflowType\",\n status, \"startedAt\", \"completedAt\", duration, \"totalCost\", \"totalTokens\", priority,\n \"definitionVersion\", \"redriveCount\"\nFROM \"workflow_runs\"${whereClause}\nORDER BY \"createdAt\" DESC, id DESC\nLIMIT ${limit + 1}`;\n\n const rows = await this.query<RawRunRow>(\"runs.list\", sql, params);\n const hasNextPage = rows.length > limit;\n const returnedRows = hasNextPage ? rows.slice(0, limit) : rows;\n const runs = returnedRows.map(mapRunSummary);\n\n const last = runs.at(-1);\n const nextCursor =\n hasNextPage && last !== undefined\n ? encodeCursor({ createdAt: last.createdAt, id: last.id })\n : null;\n\n return { runs, nextCursor };\n }\n\n async getRunDetail(\n runId: string,\n options?: RunDetailOptions,\n ): Promise<RunDetail | null> {\n const stepLimit = clampLimit(options?.stepLimit, 200);\n const annotationLimit = clampLimit(options?.annotationLimit, 200);\n const logLimit = clampLimit(options?.logLimit, 200);\n const eventLimit = clampLimit(options?.eventLimit, 200);\n\n // All six statements share one transaction: six interactive transactions\n // to render one page would take six connections out of the consumer's\n // pool, and the results would be six different moments.\n return this.read(\"run.detail\", async (client) => {\n const runRows = await this.select<RawRunDetailRow>(\n client,\n `SELECT id, \"createdAt\", \"updatedAt\", \"workflowId\", \"workflowName\", \"workflowType\",\n status, \"startedAt\", \"completedAt\", duration, \"totalCost\", \"totalTokens\", priority,\n \"definitionVersion\", \"redriveCount\",\n input, output, config, metadata\nFROM \"workflow_runs\" WHERE id = $1`,\n [runId],\n );\n\n // A run that does not exist costs one statement, not six.\n const runRow = runRows[0];\n if (!runRow) return null;\n\n const stageRows = await this.select<RawStageRow>(\n client,\n `SELECT id, \"workflowRunId\", \"stageId\", \"stageName\", \"stageNumber\", \"executionGroup\",\n attempt, status, \"startedAt\", \"completedAt\", duration, \"nextPollAt\",\n \"pollInterval\", \"maxWaitUntil\", \"errorMessage\"\nFROM \"workflow_stages\" WHERE \"workflowRunId\" = $1\nORDER BY \"stageNumber\" ASC, \"stageId\" ASC`,\n [runId],\n );\n\n const stepRows = await this.select<RawStepRow>(\n client,\n `SELECT s.id, s.\"stageRecordId\", s.\"stepId\", s.seq, s.kind, s.status, s.attempt,\n s.\"leaseExpiresAt\", s.\"deadlineAt\", s.\"externalKey\", s.error, s.\"createdAt\", s.\"updatedAt\"\nFROM \"workflow_steps\" s\nJOIN \"workflow_stages\" st ON st.id = s.\"stageRecordId\"\nWHERE st.\"workflowRunId\" = $1\nORDER BY st.\"stageNumber\" ASC, s.seq ASC\nLIMIT ${stepLimit + 1}`,\n [runId],\n );\n\n const annotationRows = await this.select<RawAnnotationRow>(\n client,\n `SELECT id, \"createdAt\", \"workflowRunId\", \"workflowStageRecordId\", attempt, scope,\n \"scopeId\", \"actorKind\", \"actorId\", key, value\nFROM \"workflow_annotations\" WHERE \"workflowRunId\" = $1\nORDER BY \"createdAt\" DESC, id DESC\nLIMIT ${annotationLimit + 1}`,\n [runId],\n );\n\n const logRows = await this.select<RawLogRow>(\n client,\n `SELECT id, \"createdAt\", \"workflowRunId\", \"workflowStageId\", level, message\nFROM \"workflow_logs\" WHERE \"workflowRunId\" = $1\nORDER BY \"createdAt\" DESC, id DESC\nLIMIT ${logLimit + 1}`,\n [runId],\n );\n\n const eventRows = await this.select<RawEventRow>(\n client,\n `SELECT id, \"workflowRunId\", sequence, \"eventType\", \"occurredAt\", \"publishedAt\",\n \"retryCount\", \"dlqAt\"\nFROM \"outbox_events\" WHERE \"workflowRunId\" = $1\nORDER BY sequence ASC\nLIMIT ${eventLimit + 1}`,\n [runId],\n );\n\n // Each list asked for one row more than it will show, so \"there is\n // more than this\" is known without a second count.\n const truncated = {\n steps: stepRows.length > stepLimit,\n annotations: annotationRows.length > annotationLimit,\n logs: logRows.length > logLimit,\n events: eventRows.length > eventLimit,\n };\n\n return {\n run: {\n ...mapRunSummary(runRow),\n input: runRow.input,\n output: runRow.output,\n config: runRow.config,\n metadata: runRow.metadata,\n },\n stages: stageRows.map(mapStageSummary),\n steps: stepRows.slice(0, stepLimit).map(mapStepSummary),\n annotations: annotationRows\n .slice(0, annotationLimit)\n .map(mapAnnotationSummary),\n logs: logRows.slice(0, logLimit).map(mapLogEntry),\n events: eventRows.slice(0, eventLimit).map(mapRunEvent),\n truncated,\n } satisfies RunDetail;\n });\n }\n\n async listRunEvents(\n runId: string,\n afterSequence: number,\n limit?: number,\n ): Promise<RunEvent[]> {\n const clampedLimit = clampLimit(limit, 200);\n const rows = await this.query<RawEventRow>(\n \"run.events.tail\",\n `SELECT id, \"workflowRunId\", sequence, \"eventType\", \"occurredAt\", \"publishedAt\",\n \"retryCount\", \"dlqAt\"\nFROM \"outbox_events\" WHERE \"workflowRunId\" = $1 AND sequence > $2\nORDER BY sequence ASC LIMIT ${clampedLimit}`,\n [runId, Math.floor(afterSequence)],\n );\n return rows.map(mapRunEvent);\n }\n\n async getQueueHealth(): Promise<QueueHealth> {\n const [\n countsResult,\n oldestPendingResult,\n leasesResult,\n overduePollsResult,\n ] = await this.read(\"queue.health\", async (client) =>\n Promise.all([\n this.select<{ status: string; count: unknown }>(\n client,\n `SELECT status::text AS status, count(*) AS count FROM \"job_queue\" GROUP BY status`,\n [],\n ),\n this.select<{ oldest: Date | string | null }>(\n client,\n `SELECT min(\"createdAt\") AS oldest FROM \"job_queue\" WHERE status = $1::\"${this.statusEnumName}\"`,\n [\"PENDING\"],\n ),\n this.select<{ oldest: Date | string | null }>(\n client,\n `SELECT min(\"lockedAt\") AS oldest FROM \"job_queue\" WHERE status = $1::\"${this.statusEnumName}\" AND \"lockedAt\" IS NOT NULL`,\n [\"RUNNING\"],\n ),\n this.select<{ count: unknown }>(\n client,\n `SELECT count(*) AS count FROM \"job_queue\" WHERE status = $1::\"${this.statusEnumName}\" AND \"nextPollAt\" IS NOT NULL AND \"nextPollAt\" <= ${utcParam(2)}`,\n [\"SUSPENDED\", this.now()],\n ),\n ]),\n );\n\n const countsByStatus = Object.fromEntries(\n CONSOLE_STATUSES.map((status) => [status, 0]),\n ) as Record<ConsoleStatus, number>;\n\n for (const row of countsResult) {\n if (isConsoleStatus(row.status)) {\n countsByStatus[row.status] = toNumber(row.count);\n }\n }\n\n return {\n countsByStatus,\n oldestPendingAt: toDate(oldestPendingResult[0]?.oldest),\n oldestLeaseAt: toDate(leasesResult[0]?.oldest),\n overduePolls: toNumber(overduePollsResult[0]?.count),\n };\n }\n\n async listSuspendedStages(limit?: number): Promise<SuspendedStage[]> {\n const clampedLimit = clampLimit(limit, 50);\n const rows = await this.query<RawSuspendedStageRow>(\n \"stages.suspended\",\n `SELECT st.id, st.\"workflowRunId\", r.\"workflowId\", st.\"stageId\", st.\"stageName\",\n st.attempt, st.\"nextPollAt\", st.\"pollInterval\", st.\"maxWaitUntil\"\nFROM \"workflow_stages\" st\nJOIN \"workflow_runs\" r ON r.id = st.\"workflowRunId\"\nWHERE st.status = $1::\"${this.statusEnumName}\" AND st.\"nextPollAt\" IS NOT NULL\nORDER BY st.\"nextPollAt\" ASC\nLIMIT ${clampedLimit}`,\n [\"SUSPENDED\"],\n );\n return rows.map(mapSuspendedStage);\n }\n\n async listDeadLetters(limit?: number): Promise<DeadLetter[]> {\n const clampedLimit = clampLimit(limit, 50);\n const rows = await this.query<RawDeadLetterRow>(\n \"outbox.deadLetters\",\n `SELECT id, \"workflowRunId\", sequence, \"eventType\", \"retryCount\", \"occurredAt\", \"dlqAt\"\nFROM \"outbox_events\" WHERE \"dlqAt\" IS NOT NULL\nORDER BY \"dlqAt\" DESC LIMIT ${clampedLimit}`,\n [],\n );\n return rows.map(mapDeadLetter);\n }\n\n async listWorkers(): Promise<WorkerInstance[]> {\n const rows = await this.query<RawWorkerRow>(\n \"workers.list\",\n `SELECT \"workerId\", count(*) AS running, min(\"lockedAt\") AS oldest, max(\"lockedAt\") AS newest\nFROM \"job_queue\"\nWHERE status = $1::\"${this.statusEnumName}\" AND \"workerId\" IS NOT NULL\nGROUP BY \"workerId\" ORDER BY \"workerId\" ASC`,\n [\"RUNNING\"],\n );\n return rows.map(mapWorkerInstance);\n }\n\n async getCosts(query: CostQuery): Promise<CostBucket[]> {\n const now = this.now();\n const to = query.to ?? now;\n const defaultDays = query.by === \"workflow\" ? 7 : 30;\n const from =\n query.from ?? new Date(now.getTime() - defaultDays * 24 * 60 * 60 * 1000);\n\n if (query.by === \"workflow\") {\n const rows = await this.query<RawCostRow>(\n \"costs.byWorkflow\",\n `SELECT \"workflowId\" AS key, count(*) AS runs, sum(\"totalCost\") AS cost, sum(\"totalTokens\") AS tokens\nFROM \"workflow_runs\" WHERE \"createdAt\" >= ${utcParam(1)} AND \"createdAt\" < ${utcParam(2)}\nGROUP BY \"workflowId\" ORDER BY sum(\"totalCost\") DESC`,\n [from, to],\n );\n return rows.map(mapCostBucket);\n } else {\n const rows = await this.query<RawCostRow>(\n \"costs.byDay\",\n `SELECT to_char(date_trunc('day', \"createdAt\"), 'YYYY-MM-DD') AS key,\n count(*) AS runs, sum(\"totalCost\") AS cost, sum(\"totalTokens\") AS tokens\nFROM \"workflow_runs\" WHERE \"createdAt\" >= ${utcParam(1)} AND \"createdAt\" < ${utcParam(2)}\nGROUP BY 1 ORDER BY 1 DESC`,\n [from, to],\n );\n return rows.map(mapCostBucket);\n }\n }\n}\n\nexport function createPrismaConsoleReadPort(\n prisma: ConsolePrismaClient,\n options?: PrismaConsoleReadPortOptions,\n): ConsoleReadPort {\n return new PrismaConsoleReadPort(prisma, options);\n}\n"]}