@coji/durably 0.10.0 → 0.11.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/README.md +2 -2
- package/dist/{index-BjlCb0gP.d.ts → index-fppJjkF-.d.ts} +84 -52
- package/dist/index.d.ts +45 -95
- package/dist/index.js +482 -389
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/docs/llms.md +174 -58
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -53,6 +53,8 @@ function createEventEmitter() {
|
|
|
53
53
|
|
|
54
54
|
// src/job.ts
|
|
55
55
|
import { prettifyError } from "zod";
|
|
56
|
+
var noop = () => {
|
|
57
|
+
};
|
|
56
58
|
function validateJobInputOrThrow(schema, input, context) {
|
|
57
59
|
const result = schema.safeParse(input);
|
|
58
60
|
if (!result.success) {
|
|
@@ -75,7 +77,7 @@ function createJobRegistry() {
|
|
|
75
77
|
}
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
|
-
function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
80
|
+
function createJobHandle(jobDef, storage, eventEmitter, registry, labelsSchema) {
|
|
79
81
|
const existingJob = registry.get(jobDef.name);
|
|
80
82
|
if (existingJob) {
|
|
81
83
|
if (existingJob.jobDef === jobDef) {
|
|
@@ -91,6 +93,9 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
|
91
93
|
name: jobDef.name,
|
|
92
94
|
async trigger(input, options) {
|
|
93
95
|
const validatedInput = validateJobInputOrThrow(inputSchema, input);
|
|
96
|
+
if (labelsSchema && options?.labels) {
|
|
97
|
+
validateJobInputOrThrow(labelsSchema, options.labels, "labels");
|
|
98
|
+
}
|
|
94
99
|
const run = await storage.createRun({
|
|
95
100
|
jobName: jobDef.name,
|
|
96
101
|
input: validatedInput,
|
|
@@ -112,30 +117,57 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
|
112
117
|
return new Promise((resolve, reject) => {
|
|
113
118
|
let timeoutId;
|
|
114
119
|
let resolved = false;
|
|
120
|
+
const unsubscribes = [];
|
|
115
121
|
const cleanup = () => {
|
|
116
122
|
if (resolved) return;
|
|
117
123
|
resolved = true;
|
|
118
|
-
|
|
119
|
-
unsubscribeFail();
|
|
124
|
+
for (const unsub of unsubscribes) unsub();
|
|
120
125
|
if (timeoutId) {
|
|
121
126
|
clearTimeout(timeoutId);
|
|
122
127
|
}
|
|
123
128
|
};
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
unsubscribes.push(
|
|
130
|
+
eventEmitter.on("run:complete", (event) => {
|
|
131
|
+
if (event.runId === run.id && !resolved) {
|
|
132
|
+
cleanup();
|
|
133
|
+
resolve({
|
|
134
|
+
id: run.id,
|
|
135
|
+
output: event.output
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
unsubscribes.push(
|
|
141
|
+
eventEmitter.on("run:fail", (event) => {
|
|
142
|
+
if (event.runId === run.id && !resolved) {
|
|
143
|
+
cleanup();
|
|
144
|
+
reject(new Error(event.error));
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
if (options?.onProgress) {
|
|
149
|
+
const onProgress = options.onProgress;
|
|
150
|
+
unsubscribes.push(
|
|
151
|
+
eventEmitter.on("run:progress", (event) => {
|
|
152
|
+
if (event.runId === run.id && !resolved) {
|
|
153
|
+
void Promise.resolve(onProgress(event.progress)).catch(noop);
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (options?.onLog) {
|
|
159
|
+
const onLog = options.onLog;
|
|
160
|
+
unsubscribes.push(
|
|
161
|
+
eventEmitter.on("log:write", (event) => {
|
|
162
|
+
if (event.runId === run.id && !resolved) {
|
|
163
|
+
const { level, message, data, stepName } = event;
|
|
164
|
+
void Promise.resolve(
|
|
165
|
+
onLog({ level, message, data, stepName })
|
|
166
|
+
).catch(noop);
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
}
|
|
139
171
|
storage.getRun(run.id).then((currentRun) => {
|
|
140
172
|
if (resolved || !currentRun) return;
|
|
141
173
|
if (currentRun.status === "completed") {
|
|
@@ -148,6 +180,10 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
|
148
180
|
cleanup();
|
|
149
181
|
reject(new Error(currentRun.error || "Run failed"));
|
|
150
182
|
}
|
|
183
|
+
}).catch((error) => {
|
|
184
|
+
if (resolved) return;
|
|
185
|
+
cleanup();
|
|
186
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
151
187
|
});
|
|
152
188
|
if (options?.timeout !== void 0) {
|
|
153
189
|
timeoutId = setTimeout(() => {
|
|
@@ -178,6 +214,13 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
|
178
214
|
normalized[i].input,
|
|
179
215
|
`at index ${i}`
|
|
180
216
|
);
|
|
217
|
+
if (labelsSchema && normalized[i].options?.labels) {
|
|
218
|
+
validateJobInputOrThrow(
|
|
219
|
+
labelsSchema,
|
|
220
|
+
normalized[i].options?.labels,
|
|
221
|
+
`labels at index ${i}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
181
224
|
validated.push({
|
|
182
225
|
input: validatedInput,
|
|
183
226
|
options: normalized[i].options
|
|
@@ -222,6 +265,7 @@ function createJobHandle(jobDef, storage, eventEmitter, registry) {
|
|
|
222
265
|
name: jobDef.name,
|
|
223
266
|
inputSchema,
|
|
224
267
|
outputSchema,
|
|
268
|
+
labelsSchema,
|
|
225
269
|
fn: jobDef.run,
|
|
226
270
|
jobDef,
|
|
227
271
|
handle
|
|
@@ -460,11 +504,19 @@ function createKyselyStorage(db) {
|
|
|
460
504
|
query = query.where("durably_runs.status", "=", filter.status);
|
|
461
505
|
}
|
|
462
506
|
if (filter?.jobName) {
|
|
463
|
-
|
|
507
|
+
if (Array.isArray(filter.jobName)) {
|
|
508
|
+
if (filter.jobName.length > 0) {
|
|
509
|
+
query = query.where("durably_runs.job_name", "in", filter.jobName);
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
query = query.where("durably_runs.job_name", "=", filter.jobName);
|
|
513
|
+
}
|
|
464
514
|
}
|
|
465
515
|
if (filter?.labels) {
|
|
466
|
-
|
|
467
|
-
|
|
516
|
+
const labels = filter.labels;
|
|
517
|
+
validateLabels(labels);
|
|
518
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
519
|
+
if (value === void 0) continue;
|
|
468
520
|
query = query.where(
|
|
469
521
|
sql`json_extract(durably_runs.labels, ${`$."${key}"`})`,
|
|
470
522
|
"=",
|
|
@@ -933,12 +985,16 @@ function createDurablyInstance(state, jobs) {
|
|
|
933
985
|
jobDef,
|
|
934
986
|
storage,
|
|
935
987
|
eventEmitter,
|
|
936
|
-
jobRegistry
|
|
988
|
+
jobRegistry,
|
|
989
|
+
state.labelsSchema
|
|
937
990
|
);
|
|
938
991
|
newHandles[key] = handle;
|
|
939
992
|
}
|
|
940
993
|
const mergedJobs = { ...jobs, ...newHandles };
|
|
941
|
-
return createDurablyInstance(
|
|
994
|
+
return createDurablyInstance(
|
|
995
|
+
state,
|
|
996
|
+
mergedJobs
|
|
997
|
+
);
|
|
942
998
|
},
|
|
943
999
|
getRun: storage.getRun,
|
|
944
1000
|
getRuns: storage.getRuns,
|
|
@@ -955,93 +1011,35 @@ function createDurablyInstance(state, jobs) {
|
|
|
955
1011
|
subscribe(runId) {
|
|
956
1012
|
let closed = false;
|
|
957
1013
|
let cleanup = null;
|
|
1014
|
+
const closeEvents = /* @__PURE__ */ new Set(["run:complete", "run:delete"]);
|
|
1015
|
+
const subscribedEvents = [
|
|
1016
|
+
"run:start",
|
|
1017
|
+
"run:complete",
|
|
1018
|
+
"run:fail",
|
|
1019
|
+
"run:cancel",
|
|
1020
|
+
"run:delete",
|
|
1021
|
+
"run:retry",
|
|
1022
|
+
"run:progress",
|
|
1023
|
+
"step:start",
|
|
1024
|
+
"step:complete",
|
|
1025
|
+
"step:fail",
|
|
1026
|
+
"log:write"
|
|
1027
|
+
];
|
|
958
1028
|
return new ReadableStream({
|
|
959
1029
|
start: (controller) => {
|
|
960
|
-
const
|
|
961
|
-
|
|
1030
|
+
const unsubscribes = subscribedEvents.map(
|
|
1031
|
+
(type) => eventEmitter.on(type, (event) => {
|
|
1032
|
+
if (closed || event.runId !== runId) return;
|
|
962
1033
|
controller.enqueue(event);
|
|
963
|
-
|
|
964
|
-
});
|
|
965
|
-
const unsubscribeComplete = eventEmitter.on(
|
|
966
|
-
"run:complete",
|
|
967
|
-
(event) => {
|
|
968
|
-
if (!closed && event.runId === runId) {
|
|
969
|
-
controller.enqueue(event);
|
|
1034
|
+
if (closeEvents.has(type)) {
|
|
970
1035
|
closed = true;
|
|
971
1036
|
cleanup?.();
|
|
972
1037
|
controller.close();
|
|
973
1038
|
}
|
|
974
|
-
}
|
|
975
|
-
);
|
|
976
|
-
const unsubscribeFail = eventEmitter.on("run:fail", (event) => {
|
|
977
|
-
if (!closed && event.runId === runId) {
|
|
978
|
-
controller.enqueue(event);
|
|
979
|
-
}
|
|
980
|
-
});
|
|
981
|
-
const unsubscribeCancel = eventEmitter.on("run:cancel", (event) => {
|
|
982
|
-
if (!closed && event.runId === runId) {
|
|
983
|
-
controller.enqueue(event);
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
const unsubscribeDelete = eventEmitter.on("run:delete", (event) => {
|
|
987
|
-
if (!closed && event.runId === runId) {
|
|
988
|
-
controller.enqueue(event);
|
|
989
|
-
closed = true;
|
|
990
|
-
cleanup?.();
|
|
991
|
-
controller.close();
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
const unsubscribeRetry = eventEmitter.on("run:retry", (event) => {
|
|
995
|
-
if (!closed && event.runId === runId) {
|
|
996
|
-
controller.enqueue(event);
|
|
997
|
-
}
|
|
998
|
-
});
|
|
999
|
-
const unsubscribeProgress = eventEmitter.on(
|
|
1000
|
-
"run:progress",
|
|
1001
|
-
(event) => {
|
|
1002
|
-
if (!closed && event.runId === runId) {
|
|
1003
|
-
controller.enqueue(event);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
);
|
|
1007
|
-
const unsubscribeStepStart = eventEmitter.on(
|
|
1008
|
-
"step:start",
|
|
1009
|
-
(event) => {
|
|
1010
|
-
if (!closed && event.runId === runId) {
|
|
1011
|
-
controller.enqueue(event);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
);
|
|
1015
|
-
const unsubscribeStepComplete = eventEmitter.on(
|
|
1016
|
-
"step:complete",
|
|
1017
|
-
(event) => {
|
|
1018
|
-
if (!closed && event.runId === runId) {
|
|
1019
|
-
controller.enqueue(event);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1039
|
+
})
|
|
1022
1040
|
);
|
|
1023
|
-
const unsubscribeStepFail = eventEmitter.on("step:fail", (event) => {
|
|
1024
|
-
if (!closed && event.runId === runId) {
|
|
1025
|
-
controller.enqueue(event);
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
const unsubscribeLog = eventEmitter.on("log:write", (event) => {
|
|
1029
|
-
if (!closed && event.runId === runId) {
|
|
1030
|
-
controller.enqueue(event);
|
|
1031
|
-
}
|
|
1032
|
-
});
|
|
1033
1041
|
cleanup = () => {
|
|
1034
|
-
|
|
1035
|
-
unsubscribeComplete();
|
|
1036
|
-
unsubscribeFail();
|
|
1037
|
-
unsubscribeCancel();
|
|
1038
|
-
unsubscribeDelete();
|
|
1039
|
-
unsubscribeRetry();
|
|
1040
|
-
unsubscribeProgress();
|
|
1041
|
-
unsubscribeStepStart();
|
|
1042
|
-
unsubscribeStepComplete();
|
|
1043
|
-
unsubscribeStepFail();
|
|
1044
|
-
unsubscribeLog();
|
|
1042
|
+
for (const unsub of unsubscribes) unsub();
|
|
1045
1043
|
};
|
|
1046
1044
|
},
|
|
1047
1045
|
cancel: () => {
|
|
@@ -1158,10 +1156,18 @@ function createDurably(options) {
|
|
|
1158
1156
|
eventEmitter,
|
|
1159
1157
|
jobRegistry,
|
|
1160
1158
|
worker,
|
|
1159
|
+
labelsSchema: options.labels,
|
|
1161
1160
|
migrating: null,
|
|
1162
1161
|
migrated: false
|
|
1163
1162
|
};
|
|
1164
|
-
|
|
1163
|
+
const instance = createDurablyInstance(
|
|
1164
|
+
state,
|
|
1165
|
+
{}
|
|
1166
|
+
);
|
|
1167
|
+
if (options.jobs) {
|
|
1168
|
+
return instance.register(options.jobs);
|
|
1169
|
+
}
|
|
1170
|
+
return instance;
|
|
1165
1171
|
}
|
|
1166
1172
|
|
|
1167
1173
|
// src/define-job.ts
|
|
@@ -1400,6 +1406,14 @@ function createThrottledSSEController(inner, throttleMs) {
|
|
|
1400
1406
|
}
|
|
1401
1407
|
|
|
1402
1408
|
// src/server.ts
|
|
1409
|
+
var VALID_STATUSES = [
|
|
1410
|
+
"pending",
|
|
1411
|
+
"running",
|
|
1412
|
+
"completed",
|
|
1413
|
+
"failed",
|
|
1414
|
+
"cancelled"
|
|
1415
|
+
];
|
|
1416
|
+
var VALID_STATUSES_SET = new Set(VALID_STATUSES);
|
|
1403
1417
|
function parseLabelsFromParams(searchParams) {
|
|
1404
1418
|
const labels = {};
|
|
1405
1419
|
for (const [key, value] of searchParams.entries()) {
|
|
@@ -1409,6 +1423,51 @@ function parseLabelsFromParams(searchParams) {
|
|
|
1409
1423
|
}
|
|
1410
1424
|
return Object.keys(labels).length > 0 ? labels : void 0;
|
|
1411
1425
|
}
|
|
1426
|
+
function parseRunFilter(url) {
|
|
1427
|
+
const jobNames = url.searchParams.getAll("jobName");
|
|
1428
|
+
const statusParam = url.searchParams.get("status");
|
|
1429
|
+
const limitParam = url.searchParams.get("limit");
|
|
1430
|
+
const offsetParam = url.searchParams.get("offset");
|
|
1431
|
+
const labels = parseLabelsFromParams(url.searchParams);
|
|
1432
|
+
if (statusParam && !VALID_STATUSES_SET.has(statusParam)) {
|
|
1433
|
+
return errorResponse(
|
|
1434
|
+
`Invalid status: ${statusParam}. Must be one of: ${VALID_STATUSES.join(", ")}`,
|
|
1435
|
+
400
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
let limit;
|
|
1439
|
+
if (limitParam) {
|
|
1440
|
+
limit = Number.parseInt(limitParam, 10);
|
|
1441
|
+
if (Number.isNaN(limit) || limit < 0) {
|
|
1442
|
+
return errorResponse("Invalid limit: must be a non-negative integer", 400);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
let offset;
|
|
1446
|
+
if (offsetParam) {
|
|
1447
|
+
offset = Number.parseInt(offsetParam, 10);
|
|
1448
|
+
if (Number.isNaN(offset) || offset < 0) {
|
|
1449
|
+
return errorResponse(
|
|
1450
|
+
"Invalid offset: must be a non-negative integer",
|
|
1451
|
+
400
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return {
|
|
1456
|
+
jobName: jobNames.length > 0 ? jobNames : void 0,
|
|
1457
|
+
status: statusParam,
|
|
1458
|
+
labels,
|
|
1459
|
+
limit,
|
|
1460
|
+
offset
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function parseRunsSubscribeFilter(url) {
|
|
1464
|
+
const jobNames = url.searchParams.getAll("jobName");
|
|
1465
|
+
const labels = parseLabelsFromParams(url.searchParams);
|
|
1466
|
+
return {
|
|
1467
|
+
jobName: jobNames.length > 0 ? jobNames : void 0,
|
|
1468
|
+
labels
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1412
1471
|
function matchesLabels(eventLabels, filterLabels) {
|
|
1413
1472
|
for (const [key, value] of Object.entries(filterLabels)) {
|
|
1414
1473
|
if (eventLabels[key] !== value) return false;
|
|
@@ -1417,310 +1476,344 @@ function matchesLabels(eventLabels, filterLabels) {
|
|
|
1417
1476
|
}
|
|
1418
1477
|
function createDurablyHandler(durably, options) {
|
|
1419
1478
|
const throttleMs = options?.sseThrottleMs ?? 100;
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1479
|
+
const auth = options?.auth;
|
|
1480
|
+
if (auth && !auth.authenticate) {
|
|
1481
|
+
throw new Error(
|
|
1482
|
+
"createDurablyHandler: auth.authenticate is required when auth is provided"
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
async function withErrorHandling(fn) {
|
|
1486
|
+
try {
|
|
1487
|
+
return await fn();
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
if (error instanceof Response) throw error;
|
|
1490
|
+
return errorResponse(getErrorMessage(error), 500);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
async function requireRunAccess(url, ctx, operation) {
|
|
1494
|
+
const runId = getRequiredQueryParam(url, "runId");
|
|
1495
|
+
if (runId instanceof Response) return runId;
|
|
1496
|
+
const run = await durably.getRun(runId);
|
|
1497
|
+
if (!run) return errorResponse("Run not found", 404);
|
|
1498
|
+
if (auth?.onRunAccess && ctx !== void 0) {
|
|
1499
|
+
await auth.onRunAccess(ctx, run, {
|
|
1500
|
+
operation
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
return { run, runId };
|
|
1504
|
+
}
|
|
1505
|
+
async function handleTrigger(request, ctx) {
|
|
1506
|
+
return withErrorHandling(async () => {
|
|
1507
|
+
const body = await request.json();
|
|
1508
|
+
if (!body.jobName) {
|
|
1509
|
+
return errorResponse("jobName is required", 400);
|
|
1434
1510
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
if (path === "/cancel") return handler.cancel(request);
|
|
1511
|
+
const job = durably.getJob(body.jobName);
|
|
1512
|
+
if (!job) {
|
|
1513
|
+
return errorResponse(`Job not found: ${body.jobName}`, 404);
|
|
1439
1514
|
}
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1515
|
+
if (auth?.onTrigger && ctx !== void 0) {
|
|
1516
|
+
await auth.onTrigger(ctx, body);
|
|
1442
1517
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
try {
|
|
1447
|
-
const body = await request.json();
|
|
1448
|
-
if (!body.jobName) {
|
|
1449
|
-
return errorResponse("jobName is required", 400);
|
|
1450
|
-
}
|
|
1451
|
-
const job = durably.getJob(body.jobName);
|
|
1452
|
-
if (!job) {
|
|
1453
|
-
return errorResponse(`Job not found: ${body.jobName}`, 404);
|
|
1454
|
-
}
|
|
1455
|
-
const run = await job.trigger(body.input ?? {}, {
|
|
1518
|
+
const run = await job.trigger(
|
|
1519
|
+
body.input ?? {},
|
|
1520
|
+
{
|
|
1456
1521
|
idempotencyKey: body.idempotencyKey,
|
|
1457
1522
|
concurrencyKey: body.concurrencyKey,
|
|
1458
1523
|
labels: body.labels
|
|
1459
|
-
}
|
|
1460
|
-
const response = { runId: run.id };
|
|
1461
|
-
return jsonResponse(response);
|
|
1462
|
-
} catch (error) {
|
|
1463
|
-
return errorResponse(getErrorMessage(error), 500);
|
|
1464
|
-
}
|
|
1465
|
-
},
|
|
1466
|
-
subscribe(request) {
|
|
1467
|
-
const url = new URL(request.url);
|
|
1468
|
-
const runId = getRequiredQueryParam(url, "runId");
|
|
1469
|
-
if (runId instanceof Response) return runId;
|
|
1470
|
-
const stream = durably.subscribe(runId);
|
|
1471
|
-
const sseStream = createThrottledSSEStreamFromReader(
|
|
1472
|
-
stream.getReader(),
|
|
1473
|
-
throttleMs
|
|
1524
|
+
}
|
|
1474
1525
|
);
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1526
|
+
const response = { runId: run.id };
|
|
1527
|
+
return jsonResponse(response);
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
async function handleSubscribe(url, ctx) {
|
|
1531
|
+
const result = await requireRunAccess(url, ctx, "subscribe");
|
|
1532
|
+
if (result instanceof Response) return result;
|
|
1533
|
+
const stream = durably.subscribe(result.runId);
|
|
1534
|
+
const sseStream = createThrottledSSEStreamFromReader(
|
|
1535
|
+
stream.getReader(),
|
|
1536
|
+
throttleMs
|
|
1537
|
+
);
|
|
1538
|
+
return createSSEResponse(sseStream);
|
|
1539
|
+
}
|
|
1540
|
+
async function handleRuns(url, ctx) {
|
|
1541
|
+
return withErrorHandling(async () => {
|
|
1542
|
+
const filterOrError = parseRunFilter(url);
|
|
1543
|
+
if (filterOrError instanceof Response) return filterOrError;
|
|
1544
|
+
let filter = filterOrError;
|
|
1545
|
+
if (auth?.scopeRuns && ctx !== void 0) {
|
|
1546
|
+
filter = await auth.scopeRuns(ctx, filter);
|
|
1495
1547
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1548
|
+
const runs = await durably.getRuns(filter);
|
|
1549
|
+
return jsonResponse(runs.map(toClientRun));
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
async function handleRun(url, ctx) {
|
|
1553
|
+
return withErrorHandling(async () => {
|
|
1554
|
+
const result = await requireRunAccess(url, ctx, "read");
|
|
1555
|
+
if (result instanceof Response) return result;
|
|
1556
|
+
return jsonResponse(toClientRun(result.run));
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
async function handleSteps(url, ctx) {
|
|
1560
|
+
return withErrorHandling(async () => {
|
|
1561
|
+
const result = await requireRunAccess(url, ctx, "steps");
|
|
1562
|
+
if (result instanceof Response) return result;
|
|
1563
|
+
const steps = await durably.storage.getSteps(result.runId);
|
|
1564
|
+
return jsonResponse(steps);
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
async function handleRetry(url, ctx) {
|
|
1568
|
+
return withErrorHandling(async () => {
|
|
1569
|
+
const result = await requireRunAccess(url, ctx, "retry");
|
|
1570
|
+
if (result instanceof Response) return result;
|
|
1571
|
+
await durably.retry(result.runId);
|
|
1572
|
+
return successResponse();
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
async function handleCancel(url, ctx) {
|
|
1576
|
+
return withErrorHandling(async () => {
|
|
1577
|
+
const result = await requireRunAccess(url, ctx, "cancel");
|
|
1578
|
+
if (result instanceof Response) return result;
|
|
1579
|
+
await durably.cancel(result.runId);
|
|
1580
|
+
return successResponse();
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
async function handleDelete(url, ctx) {
|
|
1584
|
+
return withErrorHandling(async () => {
|
|
1585
|
+
const result = await requireRunAccess(url, ctx, "delete");
|
|
1586
|
+
if (result instanceof Response) return result;
|
|
1587
|
+
await durably.deleteRun(result.runId);
|
|
1588
|
+
return successResponse();
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
async function handleRunsSubscribe(url, ctx) {
|
|
1592
|
+
let filter;
|
|
1593
|
+
if (ctx !== void 0 && auth?.scopeRunsSubscribe) {
|
|
1594
|
+
const parsed = parseRunsSubscribeFilter(
|
|
1595
|
+
url
|
|
1596
|
+
);
|
|
1597
|
+
filter = await auth.scopeRunsSubscribe(ctx, parsed);
|
|
1598
|
+
} else if (ctx !== void 0 && auth?.scopeRuns) {
|
|
1599
|
+
const parsed = parseRunsSubscribeFilter(
|
|
1600
|
+
url
|
|
1601
|
+
);
|
|
1602
|
+
const scoped = await auth.scopeRuns(
|
|
1603
|
+
ctx,
|
|
1604
|
+
{
|
|
1605
|
+
...parsed
|
|
1505
1606
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1607
|
+
);
|
|
1608
|
+
filter = { jobName: scoped.jobName, labels: scoped.labels };
|
|
1609
|
+
} else {
|
|
1610
|
+
filter = parseRunsSubscribeFilter(url);
|
|
1611
|
+
}
|
|
1612
|
+
return createRunsSSEStream(filter);
|
|
1613
|
+
}
|
|
1614
|
+
function createRunsSSEStream(filter) {
|
|
1615
|
+
const jobNameFilter = Array.isArray(filter.jobName) ? filter.jobName : filter.jobName ? [filter.jobName] : [];
|
|
1616
|
+
const labelsFilter = filter.labels;
|
|
1617
|
+
const matchesFilter = (jobName, labels) => {
|
|
1618
|
+
if (jobNameFilter.length > 0 && !jobNameFilter.includes(jobName))
|
|
1619
|
+
return false;
|
|
1620
|
+
if (labelsFilter && (!labels || !matchesLabels(labels, labelsFilter)))
|
|
1621
|
+
return false;
|
|
1622
|
+
return true;
|
|
1623
|
+
};
|
|
1624
|
+
const sseStream = createSSEStreamFromSubscriptions(
|
|
1625
|
+
(innerCtrl) => {
|
|
1626
|
+
const { controller: ctrl, dispose } = createThrottledSSEController(
|
|
1627
|
+
innerCtrl,
|
|
1628
|
+
throttleMs
|
|
1629
|
+
);
|
|
1630
|
+
const unsubscribes = [
|
|
1631
|
+
durably.on("run:trigger", (event) => {
|
|
1632
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1633
|
+
ctrl.enqueue({
|
|
1634
|
+
type: "run:trigger",
|
|
1635
|
+
runId: event.runId,
|
|
1636
|
+
jobName: event.jobName,
|
|
1637
|
+
labels: event.labels
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
}),
|
|
1641
|
+
durably.on("run:start", (event) => {
|
|
1642
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1643
|
+
ctrl.enqueue({
|
|
1644
|
+
type: "run:start",
|
|
1645
|
+
runId: event.runId,
|
|
1646
|
+
jobName: event.jobName,
|
|
1647
|
+
labels: event.labels
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
}),
|
|
1651
|
+
durably.on("run:complete", (event) => {
|
|
1652
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1653
|
+
ctrl.enqueue({
|
|
1654
|
+
type: "run:complete",
|
|
1655
|
+
runId: event.runId,
|
|
1656
|
+
jobName: event.jobName,
|
|
1657
|
+
labels: event.labels
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
}),
|
|
1661
|
+
durably.on("run:fail", (event) => {
|
|
1662
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1663
|
+
ctrl.enqueue({
|
|
1664
|
+
type: "run:fail",
|
|
1665
|
+
runId: event.runId,
|
|
1666
|
+
jobName: event.jobName,
|
|
1667
|
+
labels: event.labels
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
}),
|
|
1671
|
+
durably.on("run:cancel", (event) => {
|
|
1672
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1673
|
+
ctrl.enqueue({
|
|
1674
|
+
type: "run:cancel",
|
|
1675
|
+
runId: event.runId,
|
|
1676
|
+
jobName: event.jobName,
|
|
1677
|
+
labels: event.labels
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
}),
|
|
1681
|
+
durably.on("run:delete", (event) => {
|
|
1682
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1683
|
+
ctrl.enqueue({
|
|
1684
|
+
type: "run:delete",
|
|
1685
|
+
runId: event.runId,
|
|
1686
|
+
jobName: event.jobName,
|
|
1687
|
+
labels: event.labels
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
}),
|
|
1691
|
+
durably.on("run:retry", (event) => {
|
|
1692
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1693
|
+
ctrl.enqueue({
|
|
1694
|
+
type: "run:retry",
|
|
1695
|
+
runId: event.runId,
|
|
1696
|
+
jobName: event.jobName,
|
|
1697
|
+
labels: event.labels
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
}),
|
|
1701
|
+
durably.on("run:progress", (event) => {
|
|
1702
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1703
|
+
ctrl.enqueue({
|
|
1704
|
+
type: "run:progress",
|
|
1705
|
+
runId: event.runId,
|
|
1706
|
+
jobName: event.jobName,
|
|
1707
|
+
progress: event.progress,
|
|
1708
|
+
labels: event.labels
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
}),
|
|
1712
|
+
durably.on("step:start", (event) => {
|
|
1713
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1714
|
+
ctrl.enqueue({
|
|
1715
|
+
type: "step:start",
|
|
1716
|
+
runId: event.runId,
|
|
1717
|
+
jobName: event.jobName,
|
|
1718
|
+
stepName: event.stepName,
|
|
1719
|
+
stepIndex: event.stepIndex,
|
|
1720
|
+
labels: event.labels
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
}),
|
|
1724
|
+
durably.on("step:complete", (event) => {
|
|
1725
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1726
|
+
ctrl.enqueue({
|
|
1727
|
+
type: "step:complete",
|
|
1728
|
+
runId: event.runId,
|
|
1729
|
+
jobName: event.jobName,
|
|
1730
|
+
stepName: event.stepName,
|
|
1731
|
+
stepIndex: event.stepIndex,
|
|
1732
|
+
labels: event.labels
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
}),
|
|
1736
|
+
durably.on("step:fail", (event) => {
|
|
1737
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1738
|
+
ctrl.enqueue({
|
|
1739
|
+
type: "step:fail",
|
|
1740
|
+
runId: event.runId,
|
|
1741
|
+
jobName: event.jobName,
|
|
1742
|
+
stepName: event.stepName,
|
|
1743
|
+
stepIndex: event.stepIndex,
|
|
1744
|
+
error: event.error,
|
|
1745
|
+
labels: event.labels
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
}),
|
|
1749
|
+
durably.on("step:cancel", (event) => {
|
|
1750
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1751
|
+
ctrl.enqueue({
|
|
1752
|
+
type: "step:cancel",
|
|
1753
|
+
runId: event.runId,
|
|
1754
|
+
jobName: event.jobName,
|
|
1755
|
+
stepName: event.stepName,
|
|
1756
|
+
stepIndex: event.stepIndex,
|
|
1757
|
+
labels: event.labels
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
}),
|
|
1761
|
+
durably.on("log:write", (event) => {
|
|
1762
|
+
if (matchesFilter(event.jobName, event.labels)) {
|
|
1763
|
+
ctrl.enqueue({
|
|
1764
|
+
type: "log:write",
|
|
1765
|
+
runId: event.runId,
|
|
1766
|
+
jobName: event.jobName,
|
|
1767
|
+
labels: event.labels,
|
|
1768
|
+
stepName: event.stepName,
|
|
1769
|
+
level: event.level,
|
|
1770
|
+
message: event.message,
|
|
1771
|
+
data: event.data
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
})
|
|
1775
|
+
];
|
|
1776
|
+
return [...unsubscribes, dispose];
|
|
1542
1777
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1778
|
+
);
|
|
1779
|
+
return createSSEResponse(sseStream);
|
|
1780
|
+
}
|
|
1781
|
+
return {
|
|
1782
|
+
async handle(request, basePath) {
|
|
1545
1783
|
try {
|
|
1784
|
+
let ctx;
|
|
1785
|
+
if (auth?.authenticate) {
|
|
1786
|
+
ctx = await auth.authenticate(request);
|
|
1787
|
+
}
|
|
1788
|
+
if (options?.onRequest) {
|
|
1789
|
+
await options.onRequest();
|
|
1790
|
+
}
|
|
1546
1791
|
const url = new URL(request.url);
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1792
|
+
const path = url.pathname.replace(basePath, "");
|
|
1793
|
+
const method = request.method;
|
|
1794
|
+
if (method === "GET") {
|
|
1795
|
+
if (path === "/subscribe") return await handleSubscribe(url, ctx);
|
|
1796
|
+
if (path === "/runs") return await handleRuns(url, ctx);
|
|
1797
|
+
if (path === "/run") return await handleRun(url, ctx);
|
|
1798
|
+
if (path === "/steps") return await handleSteps(url, ctx);
|
|
1799
|
+
if (path === "/runs/subscribe")
|
|
1800
|
+
return await handleRunsSubscribe(url, ctx);
|
|
1801
|
+
}
|
|
1802
|
+
if (method === "POST") {
|
|
1803
|
+
if (path === "/trigger") return await handleTrigger(request, ctx);
|
|
1804
|
+
if (path === "/retry") return await handleRetry(url, ctx);
|
|
1805
|
+
if (path === "/cancel") return await handleCancel(url, ctx);
|
|
1806
|
+
}
|
|
1807
|
+
if (method === "DELETE") {
|
|
1808
|
+
if (path === "/run") return await handleDelete(url, ctx);
|
|
1809
|
+
}
|
|
1810
|
+
return new Response("Not Found", { status: 404 });
|
|
1551
1811
|
} catch (error) {
|
|
1812
|
+
if (error instanceof Response) return error;
|
|
1552
1813
|
return errorResponse(getErrorMessage(error), 500);
|
|
1553
1814
|
}
|
|
1554
|
-
},
|
|
1555
|
-
runsSubscribe(request) {
|
|
1556
|
-
const url = new URL(request.url);
|
|
1557
|
-
const jobNameFilter = url.searchParams.get("jobName");
|
|
1558
|
-
const labelsFilter = parseLabelsFromParams(url.searchParams);
|
|
1559
|
-
const matchesFilter = (jobName, labels) => {
|
|
1560
|
-
if (jobNameFilter && jobName !== jobNameFilter) return false;
|
|
1561
|
-
if (labelsFilter && (!labels || !matchesLabels(labels, labelsFilter)))
|
|
1562
|
-
return false;
|
|
1563
|
-
return true;
|
|
1564
|
-
};
|
|
1565
|
-
const sseStream = createSSEStreamFromSubscriptions(
|
|
1566
|
-
(innerCtrl) => {
|
|
1567
|
-
const { controller: ctrl, dispose } = createThrottledSSEController(
|
|
1568
|
-
innerCtrl,
|
|
1569
|
-
throttleMs
|
|
1570
|
-
);
|
|
1571
|
-
const unsubscribes = [
|
|
1572
|
-
durably.on("run:trigger", (event) => {
|
|
1573
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1574
|
-
ctrl.enqueue({
|
|
1575
|
-
type: "run:trigger",
|
|
1576
|
-
runId: event.runId,
|
|
1577
|
-
jobName: event.jobName,
|
|
1578
|
-
labels: event.labels
|
|
1579
|
-
});
|
|
1580
|
-
}
|
|
1581
|
-
}),
|
|
1582
|
-
durably.on("run:start", (event) => {
|
|
1583
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1584
|
-
ctrl.enqueue({
|
|
1585
|
-
type: "run:start",
|
|
1586
|
-
runId: event.runId,
|
|
1587
|
-
jobName: event.jobName,
|
|
1588
|
-
labels: event.labels
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
}),
|
|
1592
|
-
durably.on("run:complete", (event) => {
|
|
1593
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1594
|
-
ctrl.enqueue({
|
|
1595
|
-
type: "run:complete",
|
|
1596
|
-
runId: event.runId,
|
|
1597
|
-
jobName: event.jobName,
|
|
1598
|
-
labels: event.labels
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
}),
|
|
1602
|
-
durably.on("run:fail", (event) => {
|
|
1603
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1604
|
-
ctrl.enqueue({
|
|
1605
|
-
type: "run:fail",
|
|
1606
|
-
runId: event.runId,
|
|
1607
|
-
jobName: event.jobName,
|
|
1608
|
-
labels: event.labels
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
}),
|
|
1612
|
-
durably.on("run:cancel", (event) => {
|
|
1613
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1614
|
-
ctrl.enqueue({
|
|
1615
|
-
type: "run:cancel",
|
|
1616
|
-
runId: event.runId,
|
|
1617
|
-
jobName: event.jobName,
|
|
1618
|
-
labels: event.labels
|
|
1619
|
-
});
|
|
1620
|
-
}
|
|
1621
|
-
}),
|
|
1622
|
-
durably.on("run:delete", (event) => {
|
|
1623
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1624
|
-
ctrl.enqueue({
|
|
1625
|
-
type: "run:delete",
|
|
1626
|
-
runId: event.runId,
|
|
1627
|
-
jobName: event.jobName,
|
|
1628
|
-
labels: event.labels
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
}),
|
|
1632
|
-
durably.on("run:retry", (event) => {
|
|
1633
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1634
|
-
ctrl.enqueue({
|
|
1635
|
-
type: "run:retry",
|
|
1636
|
-
runId: event.runId,
|
|
1637
|
-
jobName: event.jobName,
|
|
1638
|
-
labels: event.labels
|
|
1639
|
-
});
|
|
1640
|
-
}
|
|
1641
|
-
}),
|
|
1642
|
-
durably.on("run:progress", (event) => {
|
|
1643
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1644
|
-
ctrl.enqueue({
|
|
1645
|
-
type: "run:progress",
|
|
1646
|
-
runId: event.runId,
|
|
1647
|
-
jobName: event.jobName,
|
|
1648
|
-
progress: event.progress,
|
|
1649
|
-
labels: event.labels
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
}),
|
|
1653
|
-
durably.on("step:start", (event) => {
|
|
1654
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1655
|
-
ctrl.enqueue({
|
|
1656
|
-
type: "step:start",
|
|
1657
|
-
runId: event.runId,
|
|
1658
|
-
jobName: event.jobName,
|
|
1659
|
-
stepName: event.stepName,
|
|
1660
|
-
stepIndex: event.stepIndex,
|
|
1661
|
-
labels: event.labels
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
}),
|
|
1665
|
-
durably.on("step:complete", (event) => {
|
|
1666
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1667
|
-
ctrl.enqueue({
|
|
1668
|
-
type: "step:complete",
|
|
1669
|
-
runId: event.runId,
|
|
1670
|
-
jobName: event.jobName,
|
|
1671
|
-
stepName: event.stepName,
|
|
1672
|
-
stepIndex: event.stepIndex,
|
|
1673
|
-
labels: event.labels
|
|
1674
|
-
});
|
|
1675
|
-
}
|
|
1676
|
-
}),
|
|
1677
|
-
durably.on("step:fail", (event) => {
|
|
1678
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1679
|
-
ctrl.enqueue({
|
|
1680
|
-
type: "step:fail",
|
|
1681
|
-
runId: event.runId,
|
|
1682
|
-
jobName: event.jobName,
|
|
1683
|
-
stepName: event.stepName,
|
|
1684
|
-
stepIndex: event.stepIndex,
|
|
1685
|
-
error: event.error,
|
|
1686
|
-
labels: event.labels
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
|
-
}),
|
|
1690
|
-
durably.on("step:cancel", (event) => {
|
|
1691
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1692
|
-
ctrl.enqueue({
|
|
1693
|
-
type: "step:cancel",
|
|
1694
|
-
runId: event.runId,
|
|
1695
|
-
jobName: event.jobName,
|
|
1696
|
-
stepName: event.stepName,
|
|
1697
|
-
stepIndex: event.stepIndex,
|
|
1698
|
-
labels: event.labels
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
}),
|
|
1702
|
-
durably.on("log:write", (event) => {
|
|
1703
|
-
if (matchesFilter(event.jobName, event.labels)) {
|
|
1704
|
-
ctrl.enqueue({
|
|
1705
|
-
type: "log:write",
|
|
1706
|
-
runId: event.runId,
|
|
1707
|
-
jobName: event.jobName,
|
|
1708
|
-
labels: event.labels,
|
|
1709
|
-
stepName: event.stepName,
|
|
1710
|
-
level: event.level,
|
|
1711
|
-
message: event.message,
|
|
1712
|
-
data: event.data
|
|
1713
|
-
});
|
|
1714
|
-
}
|
|
1715
|
-
})
|
|
1716
|
-
];
|
|
1717
|
-
return [...unsubscribes, dispose];
|
|
1718
|
-
}
|
|
1719
|
-
);
|
|
1720
|
-
return createSSEResponse(sseStream);
|
|
1721
1815
|
}
|
|
1722
1816
|
};
|
|
1723
|
-
return handler;
|
|
1724
1817
|
}
|
|
1725
1818
|
export {
|
|
1726
1819
|
CancelledError,
|