@coji/durably 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ withLogPersistence
3
+ } from "./chunk-UCUP6NMJ.js";
4
+
1
5
  // src/durably.ts
2
6
  import { Kysely } from "kysely";
3
7
 
@@ -48,6 +52,15 @@ function createEventEmitter() {
48
52
  }
49
53
 
50
54
  // src/job.ts
55
+ import { prettifyError } from "zod";
56
+ function validateJobInputOrThrow(schema, input, context) {
57
+ const result = schema.safeParse(input);
58
+ if (!result.success) {
59
+ const prefix = context ? `${context}: ` : "";
60
+ throw new Error(`${prefix}Invalid input: ${prettifyError(result.error)}`);
61
+ }
62
+ return result.data;
63
+ }
51
64
  function createJobRegistry() {
52
65
  const jobs = /* @__PURE__ */ new Map();
53
66
  return {
@@ -77,13 +90,10 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
77
90
  const handle = {
78
91
  name: jobDef.name,
79
92
  async trigger(input, options) {
80
- const parseResult = inputSchema.safeParse(input);
81
- if (!parseResult.success) {
82
- throw new Error(`Invalid input: ${parseResult.error.message}`);
83
- }
93
+ const validatedInput = validateJobInputOrThrow(inputSchema, input);
84
94
  const run = await storage.createRun({
85
95
  jobName: jobDef.name,
86
- payload: parseResult.data,
96
+ payload: validatedInput,
87
97
  idempotencyKey: options?.idempotencyKey,
88
98
  concurrencyKey: options?.concurrencyKey
89
99
  });
@@ -91,7 +101,7 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
91
101
  type: "run:trigger",
92
102
  runId: run.id,
93
103
  jobName: jobDef.name,
94
- payload: parseResult.data
104
+ payload: validatedInput
95
105
  });
96
106
  return run;
97
107
  },
@@ -161,14 +171,13 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
161
171
  });
162
172
  const validated = [];
163
173
  for (let i = 0; i < normalized.length; i++) {
164
- const parseResult = inputSchema.safeParse(normalized[i].input);
165
- if (!parseResult.success) {
166
- throw new Error(
167
- `Invalid input at index ${i}: ${parseResult.error.message}`
168
- );
169
- }
174
+ const validatedInput = validateJobInputOrThrow(
175
+ inputSchema,
176
+ normalized[i].input,
177
+ `at index ${i}`
178
+ );
170
179
  validated.push({
171
- payload: parseResult.data,
180
+ payload: validatedInput,
172
181
  options: normalized[i].options
173
182
  });
174
183
  }
@@ -424,7 +433,7 @@ function createKyselyStorage(db) {
424
433
  async getNextPendingRun(excludeConcurrencyKeys) {
425
434
  let query = db.selectFrom("durably_runs").leftJoin("durably_steps", "durably_runs.id", "durably_steps.run_id").selectAll("durably_runs").select(
426
435
  (eb) => eb.fn.count("durably_steps.id").as("step_count")
427
- ).where("durably_runs.status", "=", "pending").groupBy("durably_runs.id").orderBy("durably_runs.created_at", "asc").limit(1);
436
+ ).where("durably_runs.status", "=", "pending").groupBy("durably_runs.id").orderBy("durably_runs.created_at", "asc").orderBy("durably_runs.id", "asc").limit(1);
428
437
  if (excludeConcurrencyKeys.length > 0) {
429
438
  query = query.where(
430
439
  (eb) => eb.or([
@@ -487,6 +496,9 @@ function createKyselyStorage(db) {
487
496
  };
488
497
  }
489
498
 
499
+ // src/worker.ts
500
+ import { prettifyError as prettifyError2 } from "zod";
501
+
490
502
  // src/errors.ts
491
503
  var CancelledError = class extends Error {
492
504
  constructor(runId) {
@@ -494,6 +506,9 @@ var CancelledError = class extends Error {
494
506
  this.name = "CancelledError";
495
507
  }
496
508
  };
509
+ function getErrorMessage(error) {
510
+ return error instanceof Error ? error.message : String(error);
511
+ }
497
512
 
498
513
  // src/context.ts
499
514
  function createStepContext(run, jobName, storage, eventEmitter) {
@@ -641,9 +656,6 @@ function createWorker(config, storage, eventEmitter, jobRegistry) {
641
656
  });
642
657
  }
643
658
  }
644
- function getErrorMessage(error) {
645
- return error instanceof Error ? error.message : String(error);
646
- }
647
659
  async function handleRunSuccess(runId, jobName, output, startTime) {
648
660
  const currentRun = await storage.getRun(runId);
649
661
  if (currentRun?.status === "cancelled") {
@@ -690,7 +702,7 @@ function createWorker(config, storage, eventEmitter, jobRegistry) {
690
702
  updateHeartbeat().catch((error) => {
691
703
  eventEmitter.emit({
692
704
  type: "worker:error",
693
- error: error instanceof Error ? error.message : String(error),
705
+ error: getErrorMessage(error),
694
706
  context: "heartbeat",
695
707
  runId: run.id
696
708
  });
@@ -709,7 +721,7 @@ function createWorker(config, storage, eventEmitter, jobRegistry) {
709
721
  if (job.outputSchema) {
710
722
  const parseResult = job.outputSchema.safeParse(output);
711
723
  if (!parseResult.success) {
712
- throw new Error(`Invalid output: ${parseResult.error.message}`);
724
+ throw new Error(`Invalid output: ${prettifyError2(parseResult.error)}`);
713
725
  }
714
726
  }
715
727
  await handleRunSuccess(run.id, run.jobName, output, startTime);
@@ -1051,22 +1063,104 @@ function defineJob(config) {
1051
1063
  };
1052
1064
  }
1053
1065
 
1054
- // src/plugins/log-persistence.ts
1055
- function withLogPersistence() {
1056
- return {
1057
- name: "log-persistence",
1058
- install(durably) {
1059
- durably.on("log:write", async (event) => {
1060
- await durably.storage.createLog({
1061
- runId: event.runId,
1062
- stepName: event.stepName,
1063
- level: event.level,
1064
- message: event.message,
1065
- data: event.data
1066
- });
1067
- });
1066
+ // src/http.ts
1067
+ var JSON_HEADERS = {
1068
+ "Content-Type": "application/json"
1069
+ };
1070
+ function jsonResponse(data, status = 200) {
1071
+ return new Response(JSON.stringify(data), {
1072
+ status,
1073
+ headers: JSON_HEADERS
1074
+ });
1075
+ }
1076
+ function errorResponse(message, status = 500) {
1077
+ return jsonResponse({ error: message }, status);
1078
+ }
1079
+ function successResponse() {
1080
+ return jsonResponse({ success: true });
1081
+ }
1082
+ function getRequiredQueryParam(url, paramName) {
1083
+ const value = url.searchParams.get(paramName);
1084
+ if (!value) {
1085
+ return errorResponse(`${paramName} query parameter is required`, 400);
1086
+ }
1087
+ return value;
1088
+ }
1089
+
1090
+ // src/sse.ts
1091
+ var SSE_HEADERS = {
1092
+ "Content-Type": "text/event-stream",
1093
+ "Cache-Control": "no-cache",
1094
+ Connection: "keep-alive"
1095
+ };
1096
+ function formatSSE(data) {
1097
+ return `data: ${JSON.stringify(data)}
1098
+
1099
+ `;
1100
+ }
1101
+ function createSSEEncoder() {
1102
+ return new TextEncoder();
1103
+ }
1104
+ function encodeSSE(encoder, data) {
1105
+ return encoder.encode(formatSSE(data));
1106
+ }
1107
+ function createSSEResponse(stream) {
1108
+ return new Response(stream, {
1109
+ status: 200,
1110
+ headers: SSE_HEADERS
1111
+ });
1112
+ }
1113
+ function createSSEStreamFromReader(reader) {
1114
+ const encoder = createSSEEncoder();
1115
+ return new ReadableStream({
1116
+ async start(controller) {
1117
+ try {
1118
+ while (true) {
1119
+ const { done, value } = await reader.read();
1120
+ if (done) {
1121
+ controller.close();
1122
+ break;
1123
+ }
1124
+ controller.enqueue(encodeSSE(encoder, value));
1125
+ }
1126
+ } catch (error) {
1127
+ controller.error(error);
1128
+ }
1129
+ },
1130
+ cancel() {
1131
+ reader.releaseLock();
1068
1132
  }
1069
- };
1133
+ });
1134
+ }
1135
+ function createSSEStreamFromSubscriptions(setup) {
1136
+ const encoder = createSSEEncoder();
1137
+ let closed = false;
1138
+ let unsubscribes = [];
1139
+ return new ReadableStream({
1140
+ start(controller) {
1141
+ const sseController = {
1142
+ enqueue: (data) => {
1143
+ if (closed) return;
1144
+ controller.enqueue(encodeSSE(encoder, data));
1145
+ },
1146
+ close: () => {
1147
+ if (closed) return;
1148
+ closed = true;
1149
+ controller.close();
1150
+ },
1151
+ get closed() {
1152
+ return closed;
1153
+ }
1154
+ };
1155
+ unsubscribes = setup(sseController);
1156
+ },
1157
+ cancel() {
1158
+ closed = true;
1159
+ for (const unsubscribe of unsubscribes) {
1160
+ unsubscribe();
1161
+ }
1162
+ }
1163
+ });
1070
1164
  }
1071
1165
 
1072
1166
  // src/server.ts
@@ -1100,75 +1194,31 @@ function createDurablyHandler(durably, options) {
1100
1194
  try {
1101
1195
  const body = await request.json();
1102
1196
  if (!body.jobName) {
1103
- return new Response(
1104
- JSON.stringify({ error: "jobName is required" }),
1105
- { status: 400, headers: { "Content-Type": "application/json" } }
1106
- );
1197
+ return errorResponse("jobName is required", 400);
1107
1198
  }
1108
1199
  const job = durably.getJob(body.jobName);
1109
1200
  if (!job) {
1110
- return new Response(
1111
- JSON.stringify({ error: `Job not found: ${body.jobName}` }),
1112
- { status: 404, headers: { "Content-Type": "application/json" } }
1113
- );
1201
+ return errorResponse(`Job not found: ${body.jobName}`, 404);
1114
1202
  }
1115
1203
  const run = await job.trigger(body.input ?? {}, {
1116
1204
  idempotencyKey: body.idempotencyKey,
1117
1205
  concurrencyKey: body.concurrencyKey
1118
1206
  });
1119
1207
  const response = { runId: run.id };
1120
- return new Response(JSON.stringify(response), {
1121
- status: 200,
1122
- headers: { "Content-Type": "application/json" }
1123
- });
1208
+ return jsonResponse(response);
1124
1209
  } catch (error) {
1125
- const message = error instanceof Error ? error.message : "Unknown error";
1126
- return new Response(JSON.stringify({ error: message }), {
1127
- status: 500,
1128
- headers: { "Content-Type": "application/json" }
1129
- });
1210
+ return errorResponse(getErrorMessage(error), 500);
1130
1211
  }
1131
1212
  },
1132
1213
  subscribe(request) {
1133
1214
  const url = new URL(request.url);
1134
- const runId = url.searchParams.get("runId");
1135
- if (!runId) {
1136
- return new Response(
1137
- JSON.stringify({ error: "runId query parameter is required" }),
1138
- { status: 400, headers: { "Content-Type": "application/json" } }
1139
- );
1140
- }
1215
+ const runId = getRequiredQueryParam(url, "runId");
1216
+ if (runId instanceof Response) return runId;
1141
1217
  const stream = durably.subscribe(runId);
1142
- const encoder = new TextEncoder();
1143
- const sseStream = new ReadableStream({
1144
- async start(controller) {
1145
- const reader = stream.getReader();
1146
- try {
1147
- while (true) {
1148
- const { done, value } = await reader.read();
1149
- if (done) {
1150
- controller.close();
1151
- break;
1152
- }
1153
- const event = value;
1154
- const data = `data: ${JSON.stringify(event)}
1155
-
1156
- `;
1157
- controller.enqueue(encoder.encode(data));
1158
- }
1159
- } catch (error) {
1160
- controller.error(error);
1161
- }
1162
- }
1163
- });
1164
- return new Response(sseStream, {
1165
- status: 200,
1166
- headers: {
1167
- "Content-Type": "text/event-stream",
1168
- "Cache-Control": "no-cache",
1169
- Connection: "keep-alive"
1170
- }
1171
- });
1218
+ const sseStream = createSSEStreamFromReader(
1219
+ stream.getReader()
1220
+ );
1221
+ return createSSEResponse(sseStream);
1172
1222
  },
1173
1223
  async runs(request) {
1174
1224
  try {
@@ -1183,320 +1233,188 @@ function createDurablyHandler(durably, options) {
1183
1233
  limit: limit ? Number.parseInt(limit, 10) : void 0,
1184
1234
  offset: offset ? Number.parseInt(offset, 10) : void 0
1185
1235
  });
1186
- return new Response(JSON.stringify(runs), {
1187
- status: 200,
1188
- headers: { "Content-Type": "application/json" }
1189
- });
1236
+ return jsonResponse(runs);
1190
1237
  } catch (error) {
1191
- const message = error instanceof Error ? error.message : "Unknown error";
1192
- return new Response(JSON.stringify({ error: message }), {
1193
- status: 500,
1194
- headers: { "Content-Type": "application/json" }
1195
- });
1238
+ return errorResponse(getErrorMessage(error), 500);
1196
1239
  }
1197
1240
  },
1198
1241
  async run(request) {
1199
1242
  try {
1200
1243
  const url = new URL(request.url);
1201
- const runId = url.searchParams.get("runId");
1202
- if (!runId) {
1203
- return new Response(
1204
- JSON.stringify({ error: "runId query parameter is required" }),
1205
- { status: 400, headers: { "Content-Type": "application/json" } }
1206
- );
1207
- }
1244
+ const runId = getRequiredQueryParam(url, "runId");
1245
+ if (runId instanceof Response) return runId;
1208
1246
  const run = await durably.getRun(runId);
1209
1247
  if (!run) {
1210
- return new Response(JSON.stringify({ error: "Run not found" }), {
1211
- status: 404,
1212
- headers: { "Content-Type": "application/json" }
1213
- });
1248
+ return errorResponse("Run not found", 404);
1214
1249
  }
1215
- return new Response(JSON.stringify(run), {
1216
- status: 200,
1217
- headers: { "Content-Type": "application/json" }
1218
- });
1250
+ return jsonResponse(run);
1219
1251
  } catch (error) {
1220
- const message = error instanceof Error ? error.message : "Unknown error";
1221
- return new Response(JSON.stringify({ error: message }), {
1222
- status: 500,
1223
- headers: { "Content-Type": "application/json" }
1224
- });
1252
+ return errorResponse(getErrorMessage(error), 500);
1225
1253
  }
1226
1254
  },
1227
1255
  async retry(request) {
1228
1256
  try {
1229
1257
  const url = new URL(request.url);
1230
- const runId = url.searchParams.get("runId");
1231
- if (!runId) {
1232
- return new Response(
1233
- JSON.stringify({ error: "runId query parameter is required" }),
1234
- { status: 400, headers: { "Content-Type": "application/json" } }
1235
- );
1236
- }
1258
+ const runId = getRequiredQueryParam(url, "runId");
1259
+ if (runId instanceof Response) return runId;
1237
1260
  await durably.retry(runId);
1238
- return new Response(JSON.stringify({ success: true }), {
1239
- status: 200,
1240
- headers: { "Content-Type": "application/json" }
1241
- });
1261
+ return successResponse();
1242
1262
  } catch (error) {
1243
- const message = error instanceof Error ? error.message : "Unknown error";
1244
- return new Response(JSON.stringify({ error: message }), {
1245
- status: 500,
1246
- headers: { "Content-Type": "application/json" }
1247
- });
1263
+ return errorResponse(getErrorMessage(error), 500);
1248
1264
  }
1249
1265
  },
1250
1266
  async cancel(request) {
1251
1267
  try {
1252
1268
  const url = new URL(request.url);
1253
- const runId = url.searchParams.get("runId");
1254
- if (!runId) {
1255
- return new Response(
1256
- JSON.stringify({ error: "runId query parameter is required" }),
1257
- { status: 400, headers: { "Content-Type": "application/json" } }
1258
- );
1259
- }
1269
+ const runId = getRequiredQueryParam(url, "runId");
1270
+ if (runId instanceof Response) return runId;
1260
1271
  await durably.cancel(runId);
1261
- return new Response(JSON.stringify({ success: true }), {
1262
- status: 200,
1263
- headers: { "Content-Type": "application/json" }
1264
- });
1272
+ return successResponse();
1265
1273
  } catch (error) {
1266
- const message = error instanceof Error ? error.message : "Unknown error";
1267
- return new Response(JSON.stringify({ error: message }), {
1268
- status: 500,
1269
- headers: { "Content-Type": "application/json" }
1270
- });
1274
+ return errorResponse(getErrorMessage(error), 500);
1271
1275
  }
1272
1276
  },
1273
1277
  async delete(request) {
1274
1278
  try {
1275
1279
  const url = new URL(request.url);
1276
- const runId = url.searchParams.get("runId");
1277
- if (!runId) {
1278
- return new Response(
1279
- JSON.stringify({ error: "runId query parameter is required" }),
1280
- { status: 400, headers: { "Content-Type": "application/json" } }
1281
- );
1282
- }
1280
+ const runId = getRequiredQueryParam(url, "runId");
1281
+ if (runId instanceof Response) return runId;
1283
1282
  await durably.deleteRun(runId);
1284
- return new Response(JSON.stringify({ success: true }), {
1285
- status: 200,
1286
- headers: { "Content-Type": "application/json" }
1287
- });
1283
+ return successResponse();
1288
1284
  } catch (error) {
1289
- const message = error instanceof Error ? error.message : "Unknown error";
1290
- return new Response(JSON.stringify({ error: message }), {
1291
- status: 500,
1292
- headers: { "Content-Type": "application/json" }
1293
- });
1285
+ return errorResponse(getErrorMessage(error), 500);
1294
1286
  }
1295
1287
  },
1296
1288
  async steps(request) {
1297
1289
  try {
1298
1290
  const url = new URL(request.url);
1299
- const runId = url.searchParams.get("runId");
1300
- if (!runId) {
1301
- return new Response(
1302
- JSON.stringify({ error: "runId query parameter is required" }),
1303
- { status: 400, headers: { "Content-Type": "application/json" } }
1304
- );
1305
- }
1291
+ const runId = getRequiredQueryParam(url, "runId");
1292
+ if (runId instanceof Response) return runId;
1306
1293
  const steps = await durably.storage.getSteps(runId);
1307
- return new Response(JSON.stringify(steps), {
1308
- status: 200,
1309
- headers: { "Content-Type": "application/json" }
1310
- });
1294
+ return jsonResponse(steps);
1311
1295
  } catch (error) {
1312
- const message = error instanceof Error ? error.message : "Unknown error";
1313
- return new Response(JSON.stringify({ error: message }), {
1314
- status: 500,
1315
- headers: { "Content-Type": "application/json" }
1316
- });
1296
+ return errorResponse(getErrorMessage(error), 500);
1317
1297
  }
1318
1298
  },
1319
1299
  runsSubscribe(request) {
1320
1300
  const url = new URL(request.url);
1321
1301
  const jobNameFilter = url.searchParams.get("jobName");
1322
- const encoder = new TextEncoder();
1323
- let closed = false;
1324
- const sseStream = new ReadableStream({
1325
- start(controller) {
1326
- const unsubscribeTrigger = durably.on("run:trigger", (event) => {
1327
- if (closed) return;
1328
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1329
- const data = `data: ${JSON.stringify({
1330
- type: "run:trigger",
1331
- runId: event.runId,
1332
- jobName: event.jobName
1333
- })}
1334
-
1335
- `;
1336
- controller.enqueue(encoder.encode(data));
1337
- });
1338
- const unsubscribeStart = durably.on("run:start", (event) => {
1339
- if (closed) return;
1340
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1341
- const data = `data: ${JSON.stringify({
1342
- type: "run:start",
1343
- runId: event.runId,
1344
- jobName: event.jobName
1345
- })}
1346
-
1347
- `;
1348
- controller.enqueue(encoder.encode(data));
1349
- });
1350
- const unsubscribeComplete = durably.on("run:complete", (event) => {
1351
- if (closed) return;
1352
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1353
- const data = `data: ${JSON.stringify({
1354
- type: "run:complete",
1355
- runId: event.runId,
1356
- jobName: event.jobName
1357
- })}
1358
-
1359
- `;
1360
- controller.enqueue(encoder.encode(data));
1361
- });
1362
- const unsubscribeFail = durably.on("run:fail", (event) => {
1363
- if (closed) return;
1364
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1365
- const data = `data: ${JSON.stringify({
1366
- type: "run:fail",
1367
- runId: event.runId,
1368
- jobName: event.jobName
1369
- })}
1370
-
1371
- `;
1372
- controller.enqueue(encoder.encode(data));
1373
- });
1374
- const unsubscribeCancel = durably.on("run:cancel", (event) => {
1375
- if (closed) return;
1376
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1377
- const data = `data: ${JSON.stringify({
1378
- type: "run:cancel",
1379
- runId: event.runId,
1380
- jobName: event.jobName
1381
- })}
1382
-
1383
- `;
1384
- controller.enqueue(encoder.encode(data));
1385
- });
1386
- const unsubscribeRetry = durably.on("run:retry", (event) => {
1387
- if (closed) return;
1388
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1389
- const data = `data: ${JSON.stringify({
1390
- type: "run:retry",
1391
- runId: event.runId,
1392
- jobName: event.jobName
1393
- })}
1394
-
1395
- `;
1396
- controller.enqueue(encoder.encode(data));
1397
- });
1398
- const unsubscribeProgress = durably.on("run:progress", (event) => {
1399
- if (closed) return;
1400
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1401
- const data = `data: ${JSON.stringify({
1402
- type: "run:progress",
1403
- runId: event.runId,
1404
- jobName: event.jobName,
1405
- progress: event.progress
1406
- })}
1407
-
1408
- `;
1409
- controller.enqueue(encoder.encode(data));
1410
- });
1411
- const unsubscribeStepStart = durably.on("step:start", (event) => {
1412
- if (closed) return;
1413
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1414
- const data = `data: ${JSON.stringify({
1415
- type: "step:start",
1416
- runId: event.runId,
1417
- jobName: event.jobName,
1418
- stepName: event.stepName,
1419
- stepIndex: event.stepIndex
1420
- })}
1421
-
1422
- `;
1423
- controller.enqueue(encoder.encode(data));
1424
- });
1425
- const unsubscribeStepComplete = durably.on(
1426
- "step:complete",
1427
- (event) => {
1428
- if (closed) return;
1429
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1430
- const data = `data: ${JSON.stringify({
1302
+ const matchesFilter = (jobName) => !jobNameFilter || jobName === jobNameFilter;
1303
+ const sseStream = createSSEStreamFromSubscriptions(
1304
+ (ctrl) => [
1305
+ durably.on("run:trigger", (event) => {
1306
+ if (matchesFilter(event.jobName)) {
1307
+ ctrl.enqueue({
1308
+ type: "run:trigger",
1309
+ runId: event.runId,
1310
+ jobName: event.jobName
1311
+ });
1312
+ }
1313
+ }),
1314
+ durably.on("run:start", (event) => {
1315
+ if (matchesFilter(event.jobName)) {
1316
+ ctrl.enqueue({
1317
+ type: "run:start",
1318
+ runId: event.runId,
1319
+ jobName: event.jobName
1320
+ });
1321
+ }
1322
+ }),
1323
+ durably.on("run:complete", (event) => {
1324
+ if (matchesFilter(event.jobName)) {
1325
+ ctrl.enqueue({
1326
+ type: "run:complete",
1327
+ runId: event.runId,
1328
+ jobName: event.jobName
1329
+ });
1330
+ }
1331
+ }),
1332
+ durably.on("run:fail", (event) => {
1333
+ if (matchesFilter(event.jobName)) {
1334
+ ctrl.enqueue({
1335
+ type: "run:fail",
1336
+ runId: event.runId,
1337
+ jobName: event.jobName
1338
+ });
1339
+ }
1340
+ }),
1341
+ durably.on("run:cancel", (event) => {
1342
+ if (matchesFilter(event.jobName)) {
1343
+ ctrl.enqueue({
1344
+ type: "run:cancel",
1345
+ runId: event.runId,
1346
+ jobName: event.jobName
1347
+ });
1348
+ }
1349
+ }),
1350
+ durably.on("run:retry", (event) => {
1351
+ if (matchesFilter(event.jobName)) {
1352
+ ctrl.enqueue({
1353
+ type: "run:retry",
1354
+ runId: event.runId,
1355
+ jobName: event.jobName
1356
+ });
1357
+ }
1358
+ }),
1359
+ durably.on("run:progress", (event) => {
1360
+ if (matchesFilter(event.jobName)) {
1361
+ ctrl.enqueue({
1362
+ type: "run:progress",
1363
+ runId: event.runId,
1364
+ jobName: event.jobName,
1365
+ progress: event.progress
1366
+ });
1367
+ }
1368
+ }),
1369
+ durably.on("step:start", (event) => {
1370
+ if (matchesFilter(event.jobName)) {
1371
+ ctrl.enqueue({
1372
+ type: "step:start",
1373
+ runId: event.runId,
1374
+ jobName: event.jobName,
1375
+ stepName: event.stepName,
1376
+ stepIndex: event.stepIndex
1377
+ });
1378
+ }
1379
+ }),
1380
+ durably.on("step:complete", (event) => {
1381
+ if (matchesFilter(event.jobName)) {
1382
+ ctrl.enqueue({
1431
1383
  type: "step:complete",
1432
1384
  runId: event.runId,
1433
1385
  jobName: event.jobName,
1434
1386
  stepName: event.stepName,
1435
1387
  stepIndex: event.stepIndex
1436
- })}
1437
-
1438
- `;
1439
- controller.enqueue(encoder.encode(data));
1388
+ });
1440
1389
  }
1441
- );
1442
- const unsubscribeStepFail = durably.on("step:fail", (event) => {
1443
- if (closed) return;
1444
- if (jobNameFilter && event.jobName !== jobNameFilter) return;
1445
- const data = `data: ${JSON.stringify({
1446
- type: "step:fail",
1447
- runId: event.runId,
1448
- jobName: event.jobName,
1449
- stepName: event.stepName,
1450
- stepIndex: event.stepIndex,
1451
- error: event.error
1452
- })}
1453
-
1454
- `;
1455
- controller.enqueue(encoder.encode(data));
1456
- });
1457
- const unsubscribeLogWrite = durably.on("log:write", (event) => {
1458
- if (closed) return;
1459
- if (jobNameFilter) return;
1460
- const data = `data: ${JSON.stringify({
1461
- type: "log:write",
1462
- runId: event.runId,
1463
- stepName: event.stepName,
1464
- level: event.level,
1465
- message: event.message,
1466
- data: event.data
1467
- })}
1468
-
1469
- `;
1470
- controller.enqueue(encoder.encode(data));
1471
- });
1472
- controller.cleanup = () => {
1473
- closed = true;
1474
- unsubscribeTrigger();
1475
- unsubscribeStart();
1476
- unsubscribeComplete();
1477
- unsubscribeFail();
1478
- unsubscribeCancel();
1479
- unsubscribeRetry();
1480
- unsubscribeProgress();
1481
- unsubscribeStepStart();
1482
- unsubscribeStepComplete();
1483
- unsubscribeStepFail();
1484
- unsubscribeLogWrite();
1485
- };
1486
- },
1487
- cancel(controller) {
1488
- const cleanup = controller.cleanup;
1489
- if (cleanup) cleanup();
1490
- }
1491
- });
1492
- return new Response(sseStream, {
1493
- status: 200,
1494
- headers: {
1495
- "Content-Type": "text/event-stream",
1496
- "Cache-Control": "no-cache",
1497
- Connection: "keep-alive"
1498
- }
1499
- });
1390
+ }),
1391
+ durably.on("step:fail", (event) => {
1392
+ if (matchesFilter(event.jobName)) {
1393
+ ctrl.enqueue({
1394
+ type: "step:fail",
1395
+ runId: event.runId,
1396
+ jobName: event.jobName,
1397
+ stepName: event.stepName,
1398
+ stepIndex: event.stepIndex,
1399
+ error: event.error
1400
+ });
1401
+ }
1402
+ }),
1403
+ durably.on("log:write", (event) => {
1404
+ if (!jobNameFilter) {
1405
+ ctrl.enqueue({
1406
+ type: "log:write",
1407
+ runId: event.runId,
1408
+ stepName: event.stepName,
1409
+ level: event.level,
1410
+ message: event.message,
1411
+ data: event.data
1412
+ });
1413
+ }
1414
+ })
1415
+ ]
1416
+ );
1417
+ return createSSEResponse(sseStream);
1500
1418
  }
1501
1419
  };
1502
1420
  return handler;