@coji/durably 0.6.0 → 0.6.1
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/chunk-UCUP6NMJ.js +22 -0
- package/dist/chunk-UCUP6NMJ.js.map +1 -0
- package/dist/index-CHQw-b_O.d.ts +632 -0
- package/dist/index.d.ts +5 -631
- package/dist/index.js +271 -353
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -8
- package/dist/plugins/index.js +3 -6
- package/dist/plugins/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
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:
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
174
|
+
const validatedInput = validateJobInputOrThrow(
|
|
175
|
+
inputSchema,
|
|
176
|
+
normalized[i].input,
|
|
177
|
+
`at index ${i}`
|
|
178
|
+
);
|
|
170
179
|
validated.push({
|
|
171
|
-
payload:
|
|
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:
|
|
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
|
|
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/
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
|
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
|
|
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
|
|
1121
|
-
status: 200,
|
|
1122
|
-
headers: { "Content-Type": "application/json" }
|
|
1123
|
-
});
|
|
1208
|
+
return jsonResponse(response);
|
|
1124
1209
|
} catch (error) {
|
|
1125
|
-
|
|
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
|
|
1135
|
-
if (
|
|
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
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
|
1187
|
-
status: 200,
|
|
1188
|
-
headers: { "Content-Type": "application/json" }
|
|
1189
|
-
});
|
|
1236
|
+
return jsonResponse(runs);
|
|
1190
1237
|
} catch (error) {
|
|
1191
|
-
|
|
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
|
|
1202
|
-
if (
|
|
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
|
|
1211
|
-
status: 404,
|
|
1212
|
-
headers: { "Content-Type": "application/json" }
|
|
1213
|
-
});
|
|
1248
|
+
return errorResponse("Run not found", 404);
|
|
1214
1249
|
}
|
|
1215
|
-
return
|
|
1216
|
-
status: 200,
|
|
1217
|
-
headers: { "Content-Type": "application/json" }
|
|
1218
|
-
});
|
|
1250
|
+
return jsonResponse(run);
|
|
1219
1251
|
} catch (error) {
|
|
1220
|
-
|
|
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
|
|
1231
|
-
if (
|
|
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
|
|
1239
|
-
status: 200,
|
|
1240
|
-
headers: { "Content-Type": "application/json" }
|
|
1241
|
-
});
|
|
1261
|
+
return successResponse();
|
|
1242
1262
|
} catch (error) {
|
|
1243
|
-
|
|
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
|
|
1254
|
-
if (
|
|
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
|
|
1262
|
-
status: 200,
|
|
1263
|
-
headers: { "Content-Type": "application/json" }
|
|
1264
|
-
});
|
|
1272
|
+
return successResponse();
|
|
1265
1273
|
} catch (error) {
|
|
1266
|
-
|
|
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
|
|
1277
|
-
if (
|
|
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
|
|
1285
|
-
status: 200,
|
|
1286
|
-
headers: { "Content-Type": "application/json" }
|
|
1287
|
-
});
|
|
1283
|
+
return successResponse();
|
|
1288
1284
|
} catch (error) {
|
|
1289
|
-
|
|
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
|
|
1300
|
-
if (
|
|
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
|
|
1308
|
-
status: 200,
|
|
1309
|
-
headers: { "Content-Type": "application/json" }
|
|
1310
|
-
});
|
|
1294
|
+
return jsonResponse(steps);
|
|
1311
1295
|
} catch (error) {
|
|
1312
|
-
|
|
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
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
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
|
-
|
|
1443
|
-
if (
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
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;
|