@openfn/ws-worker 0.2.12 → 0.3.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/CHANGELOG.md +20 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +214 -63
- package/dist/start.js +214 -63
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# ws-worker
|
|
2
2
|
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Don't log compiler and runtime version logs
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 419d310: Throttle attempt events to better preserve their sequencing
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 598c669: Make edge conditions more stable if state is not passed
|
|
18
|
+
- 6e906a7: Better handling of job-error
|
|
19
|
+
- Updated dependencies [02ab459]
|
|
20
|
+
- @openfn/runtime@0.2.2
|
|
21
|
+
- @openfn/engine-multi@0.2.4
|
|
22
|
+
|
|
3
23
|
## 0.2.12
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { EventEmitter } from 'node:events';
|
|
|
2
2
|
import Koa from 'koa';
|
|
3
3
|
import { SanitizePolicies, Logger } from '@openfn/logger';
|
|
4
4
|
import { Channel as Channel$1 } from 'phoenix';
|
|
5
|
-
import {
|
|
5
|
+
import { ExecutionPlan } from '@openfn/runtime';
|
|
6
6
|
import { RuntimeEngine } from '@openfn/engine-multi';
|
|
7
|
+
import { Server } from 'http';
|
|
7
8
|
|
|
8
9
|
type ExitReasonStrings =
|
|
9
10
|
| 'success'
|
|
@@ -23,7 +24,8 @@ type Node = {
|
|
|
23
24
|
id: string;
|
|
24
25
|
body?: string;
|
|
25
26
|
adaptor?: string;
|
|
26
|
-
|
|
27
|
+
credential?: object;
|
|
28
|
+
credential_id?: string;
|
|
27
29
|
type?: 'webhook' | 'cron'; // trigger only
|
|
28
30
|
state?: any; // Initial state / defaults
|
|
29
31
|
};
|
|
@@ -138,6 +140,7 @@ declare type RunStartPayload = {
|
|
|
138
140
|
run_id: string;
|
|
139
141
|
attempt_id?: string;
|
|
140
142
|
input_dataclip_id?: string;
|
|
143
|
+
versions: Record<string, string>;
|
|
141
144
|
};
|
|
142
145
|
declare type RunStartReply = void;
|
|
143
146
|
declare const RUN_COMPLETE = "run:complete";
|
|
@@ -155,6 +158,7 @@ declare type Context = {
|
|
|
155
158
|
channel: Channel;
|
|
156
159
|
state: AttemptState;
|
|
157
160
|
logger: Logger;
|
|
161
|
+
engine: RuntimeEngine;
|
|
158
162
|
onFinish: (result: any) => void;
|
|
159
163
|
};
|
|
160
164
|
|
package/dist/index.js
CHANGED
|
@@ -165,14 +165,11 @@ var startWorkloop = (app, logger, minBackoff, maxBackoff, maxWorkers) => {
|
|
|
165
165
|
};
|
|
166
166
|
var workloop_default = startWorkloop;
|
|
167
167
|
|
|
168
|
-
// src/api/execute.ts
|
|
169
|
-
import crypto2 from "node:crypto";
|
|
170
|
-
|
|
171
168
|
// src/util/convert-attempt.ts
|
|
172
169
|
import crypto from "node:crypto";
|
|
173
170
|
var conditions = {
|
|
174
|
-
on_job_success: (upstreamId) => `Boolean(!state
|
|
175
|
-
on_job_failure: (upstreamId) => `Boolean(state
|
|
171
|
+
on_job_success: (upstreamId) => `Boolean(!state?.errors?.["${upstreamId}"] ?? true)`,
|
|
172
|
+
on_job_failure: (upstreamId) => `Boolean(state?.errors && state.errors["${upstreamId}"])`,
|
|
176
173
|
always: (_upstreamId) => null
|
|
177
174
|
};
|
|
178
175
|
var mapEdgeCondition = (edge) => {
|
|
@@ -219,7 +216,7 @@ var convert_attempt_default = (attempt) => {
|
|
|
219
216
|
const id = job.id || crypto.randomUUID();
|
|
220
217
|
nodes[id] = {
|
|
221
218
|
id,
|
|
222
|
-
configuration: job.credential_id,
|
|
219
|
+
configuration: job.credential || job.credential_id,
|
|
223
220
|
expression: job.body,
|
|
224
221
|
adaptor: job.adaptor
|
|
225
222
|
};
|
|
@@ -328,6 +325,201 @@ var calculateAttemptExitReason = (state) => {
|
|
|
328
325
|
return { reason: "success", error_type: null, error_message: null };
|
|
329
326
|
};
|
|
330
327
|
|
|
328
|
+
// src/events/run-complete.ts
|
|
329
|
+
import crypto2 from "node:crypto";
|
|
330
|
+
function onRunComplete({ channel, state }, event, error) {
|
|
331
|
+
const dataclipId = crypto2.randomUUID();
|
|
332
|
+
const run_id = state.activeRun;
|
|
333
|
+
const job_id = state.activeJob;
|
|
334
|
+
if (!state.dataclips) {
|
|
335
|
+
state.dataclips = {};
|
|
336
|
+
}
|
|
337
|
+
const outputState = event.state || {};
|
|
338
|
+
state.dataclips[dataclipId] = event.state;
|
|
339
|
+
delete state.activeRun;
|
|
340
|
+
delete state.activeJob;
|
|
341
|
+
state.lastDataclipId = dataclipId;
|
|
342
|
+
event.next?.forEach((nextJobId) => {
|
|
343
|
+
state.inputDataclips[nextJobId] = dataclipId;
|
|
344
|
+
});
|
|
345
|
+
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
346
|
+
job_id,
|
|
347
|
+
event.state,
|
|
348
|
+
error
|
|
349
|
+
);
|
|
350
|
+
state.reasons[job_id] = { reason, error_message, error_type };
|
|
351
|
+
const evt = {
|
|
352
|
+
run_id,
|
|
353
|
+
job_id,
|
|
354
|
+
output_dataclip_id: dataclipId,
|
|
355
|
+
output_dataclip: stringify_default(outputState),
|
|
356
|
+
reason,
|
|
357
|
+
error_message,
|
|
358
|
+
error_type,
|
|
359
|
+
mem: event.mem,
|
|
360
|
+
duration: event.duration,
|
|
361
|
+
thread_id: event.threadId
|
|
362
|
+
};
|
|
363
|
+
return sendEvent(channel, RUN_COMPLETE, evt);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/events/run-start.ts
|
|
367
|
+
import crypto3 from "node:crypto";
|
|
368
|
+
import { timestamp } from "@openfn/logger";
|
|
369
|
+
|
|
370
|
+
// package.json
|
|
371
|
+
var package_default = {
|
|
372
|
+
name: "@openfn/ws-worker",
|
|
373
|
+
version: "0.3.1",
|
|
374
|
+
description: "A Websocket Worker to connect Lightning to a Runtime Engine",
|
|
375
|
+
main: "dist/index.js",
|
|
376
|
+
type: "module",
|
|
377
|
+
scripts: {
|
|
378
|
+
test: "pnpm ava --serial",
|
|
379
|
+
"test:types": "pnpm tsc --noEmit --project tsconfig.json",
|
|
380
|
+
build: "tsup --config tsup.config.js",
|
|
381
|
+
"build:watch": "pnpm build --watch",
|
|
382
|
+
start: "ts-node-esm --transpile-only src/start.ts",
|
|
383
|
+
"start:prod": "node dist/start.js",
|
|
384
|
+
"start:watch": "nodemon -e ts,js --watch ../runtime-manager/dist --watch ./src --exec 'pnpm start'",
|
|
385
|
+
pack: "pnpm pack --pack-destination ../../dist"
|
|
386
|
+
},
|
|
387
|
+
bin: {
|
|
388
|
+
worker: "dist/start.js"
|
|
389
|
+
},
|
|
390
|
+
author: "Open Function Group <admin@openfn.org>",
|
|
391
|
+
license: "ISC",
|
|
392
|
+
dependencies: {
|
|
393
|
+
"@koa/router": "^12.0.0",
|
|
394
|
+
"@openfn/engine-multi": "workspace:*",
|
|
395
|
+
"@openfn/logger": "workspace:*",
|
|
396
|
+
"@openfn/runtime": "workspace:*",
|
|
397
|
+
"@types/koa-logger": "^3.1.2",
|
|
398
|
+
"@types/ws": "^8.5.6",
|
|
399
|
+
"fast-safe-stringify": "^2.1.1",
|
|
400
|
+
figures: "^5.0.0",
|
|
401
|
+
"human-id": "^4.1.0",
|
|
402
|
+
jose: "^4.14.6",
|
|
403
|
+
koa: "^2.13.4",
|
|
404
|
+
"koa-bodyparser": "^4.4.0",
|
|
405
|
+
"koa-logger": "^3.2.1",
|
|
406
|
+
phoenix: "^1.7.7",
|
|
407
|
+
ws: "^8.14.1"
|
|
408
|
+
},
|
|
409
|
+
devDependencies: {
|
|
410
|
+
"@openfn/lightning-mock": "workspace:*",
|
|
411
|
+
"@types/koa": "^2.13.5",
|
|
412
|
+
"@types/koa-bodyparser": "^4.3.10",
|
|
413
|
+
"@types/koa__router": "^12.0.1",
|
|
414
|
+
"@types/node": "^18.15.3",
|
|
415
|
+
"@types/nodemon": "1.19.3",
|
|
416
|
+
"@types/phoenix": "^1.6.2",
|
|
417
|
+
"@types/yargs": "^17.0.12",
|
|
418
|
+
ava: "5.1.0",
|
|
419
|
+
nodemon: "3.0.1",
|
|
420
|
+
"ts-node": "^10.9.1",
|
|
421
|
+
tslib: "^2.4.0",
|
|
422
|
+
tsup: "^6.2.3",
|
|
423
|
+
typescript: "^4.6.4",
|
|
424
|
+
yargs: "^17.6.2"
|
|
425
|
+
},
|
|
426
|
+
files: [
|
|
427
|
+
"dist",
|
|
428
|
+
"README.md",
|
|
429
|
+
"CHANGELOG.md"
|
|
430
|
+
]
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/util/versions.ts
|
|
434
|
+
import { mainSymbols } from "figures";
|
|
435
|
+
var { triangleRightSmall: t } = mainSymbols;
|
|
436
|
+
var versions_default = (runId, versions, adaptor) => {
|
|
437
|
+
let longest = "compiler".length;
|
|
438
|
+
for (const v in versions) {
|
|
439
|
+
longest = Math.max(v.length, longest);
|
|
440
|
+
}
|
|
441
|
+
const { node, compiler, engine, worker, runtime, ...adaptors } = versions;
|
|
442
|
+
const prefix = (str2) => ` ${t} ${str2.padEnd(longest + 4, " ")}`;
|
|
443
|
+
let str = `Versions for run ${runId}:
|
|
444
|
+
${prefix("node.js")}${versions.node || "unknown"}
|
|
445
|
+
${prefix("worker")}${versions.worker || "unknown"}
|
|
446
|
+
${prefix("engine")}${versions.engine || "unknown"}`;
|
|
447
|
+
if (Object.keys(adaptors).length) {
|
|
448
|
+
let allAdaptors = Object.keys(adaptors);
|
|
449
|
+
if (adaptor) {
|
|
450
|
+
allAdaptors = allAdaptors.filter((name) => adaptor.startsWith(name));
|
|
451
|
+
}
|
|
452
|
+
str += "\n" + allAdaptors.sort().map((adaptorName) => `${prefix(adaptorName)}${adaptors[adaptorName]}`).join("\n");
|
|
453
|
+
}
|
|
454
|
+
return str;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/events/run-start.ts
|
|
458
|
+
async function onRunStart(context, event) {
|
|
459
|
+
const time = (timestamp() - BigInt(1e7)).toString();
|
|
460
|
+
const { channel, state } = context;
|
|
461
|
+
state.activeRun = crypto3.randomUUID();
|
|
462
|
+
state.activeJob = event.jobId;
|
|
463
|
+
const job = state.plan.jobs.find(({ id }) => id === event.jobId);
|
|
464
|
+
const input_dataclip_id = state.inputDataclips[event.jobId];
|
|
465
|
+
const versions = {
|
|
466
|
+
worker: package_default.version,
|
|
467
|
+
...event.versions
|
|
468
|
+
};
|
|
469
|
+
const versionLogContext = {
|
|
470
|
+
...context,
|
|
471
|
+
state: {
|
|
472
|
+
...state,
|
|
473
|
+
activeRun: state.activeRun
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
await sendEvent(channel, RUN_START, {
|
|
477
|
+
run_id: state.activeRun,
|
|
478
|
+
job_id: state.activeJob,
|
|
479
|
+
input_dataclip_id,
|
|
480
|
+
versions
|
|
481
|
+
});
|
|
482
|
+
const versionMessage = versions_default(
|
|
483
|
+
versionLogContext.state.activeRun,
|
|
484
|
+
versions,
|
|
485
|
+
job?.adaptor
|
|
486
|
+
);
|
|
487
|
+
await onJobLog(versionLogContext, {
|
|
488
|
+
time,
|
|
489
|
+
message: [versionMessage],
|
|
490
|
+
level: "info",
|
|
491
|
+
name: "VER"
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/util/throttle.ts
|
|
497
|
+
var createThrottler = () => {
|
|
498
|
+
const q = [];
|
|
499
|
+
let activePromise;
|
|
500
|
+
const add = (fn) => {
|
|
501
|
+
return (...args) => new Promise((resolve, reject) => {
|
|
502
|
+
q.push({ fn, args, resolve, reject });
|
|
503
|
+
shift();
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
const shift = () => {
|
|
507
|
+
if (activePromise) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const next = q.shift();
|
|
511
|
+
if (next) {
|
|
512
|
+
const { fn, args, resolve, reject } = next;
|
|
513
|
+
activePromise = fn(...args).then(resolve).catch(reject).finally(() => {
|
|
514
|
+
activePromise = void 0;
|
|
515
|
+
shift();
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
return add;
|
|
520
|
+
};
|
|
521
|
+
var throttle_default = createThrottler;
|
|
522
|
+
|
|
331
523
|
// src/api/execute.ts
|
|
332
524
|
var enc = new TextDecoder("utf-8");
|
|
333
525
|
var eventMap = {
|
|
@@ -341,7 +533,8 @@ function execute(channel, engine, logger, plan, options = {}, onFinish = (_resul
|
|
|
341
533
|
}) {
|
|
342
534
|
logger.info("executing ", plan.id);
|
|
343
535
|
const state = create_attempt_state_default(plan, options);
|
|
344
|
-
const context = { channel, state, logger, onFinish };
|
|
536
|
+
const context = { channel, state, logger, engine, onFinish };
|
|
537
|
+
const throttle = throttle_default();
|
|
345
538
|
const addEvent = (eventName, handler) => {
|
|
346
539
|
const wrappedFn = async (event) => {
|
|
347
540
|
const lightningEvent = eventMap[eventName] ?? eventName;
|
|
@@ -361,13 +554,13 @@ function execute(channel, engine, logger, plan, options = {}, onFinish = (_resul
|
|
|
361
554
|
};
|
|
362
555
|
const listeners = Object.assign(
|
|
363
556
|
{},
|
|
364
|
-
addEvent("workflow-start", onWorkflowStart),
|
|
365
|
-
addEvent("job-start",
|
|
366
|
-
addEvent("job-complete",
|
|
367
|
-
addEvent("job-error", onJobError),
|
|
368
|
-
addEvent("workflow-log", onJobLog),
|
|
369
|
-
addEvent("workflow-complete", onWorkflowComplete),
|
|
370
|
-
addEvent("workflow-error", onWorkflowError)
|
|
557
|
+
addEvent("workflow-start", throttle(onWorkflowStart)),
|
|
558
|
+
addEvent("job-start", throttle(onRunStart)),
|
|
559
|
+
addEvent("job-complete", throttle(onRunComplete)),
|
|
560
|
+
addEvent("job-error", throttle(onJobError)),
|
|
561
|
+
addEvent("workflow-log", throttle(onJobLog)),
|
|
562
|
+
addEvent("workflow-complete", throttle(onWorkflowComplete)),
|
|
563
|
+
addEvent("workflow-error", throttle(onWorkflowError))
|
|
371
564
|
);
|
|
372
565
|
engine.listen(plan.id, listeners);
|
|
373
566
|
const resolvers = {
|
|
@@ -396,59 +589,17 @@ function execute(channel, engine, logger, plan, options = {}, onFinish = (_resul
|
|
|
396
589
|
return context;
|
|
397
590
|
}
|
|
398
591
|
var sendEvent = (channel, event, payload) => new Promise((resolve, reject) => {
|
|
399
|
-
channel.push(event, payload).receive("error", reject).receive("timeout", () =>
|
|
592
|
+
channel.push(event, payload).receive("error", reject).receive("timeout", () => {
|
|
593
|
+
reject(new Error("timeout"));
|
|
594
|
+
}).receive("ok", resolve);
|
|
400
595
|
});
|
|
401
|
-
function onJobStart({ channel, state }, event) {
|
|
402
|
-
state.activeRun = crypto2.randomUUID();
|
|
403
|
-
state.activeJob = event.jobId;
|
|
404
|
-
const input_dataclip_id = state.inputDataclips[event.jobId];
|
|
405
|
-
return sendEvent(channel, RUN_START, {
|
|
406
|
-
run_id: state.activeRun,
|
|
407
|
-
job_id: state.activeJob,
|
|
408
|
-
input_dataclip_id
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
596
|
function onJobError(context, event) {
|
|
412
|
-
const { state
|
|
413
|
-
if (state
|
|
414
|
-
|
|
597
|
+
const { state, error, jobId } = event;
|
|
598
|
+
if (state?.errors?.[jobId]?.message === error.message) {
|
|
599
|
+
return onRunComplete(context, event);
|
|
415
600
|
} else {
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
function onJobComplete({ channel, state }, event, error) {
|
|
420
|
-
const dataclipId = crypto2.randomUUID();
|
|
421
|
-
const run_id = state.activeRun;
|
|
422
|
-
const job_id = state.activeJob;
|
|
423
|
-
if (!state.dataclips) {
|
|
424
|
-
state.dataclips = {};
|
|
601
|
+
return onRunComplete(context, event, event.error);
|
|
425
602
|
}
|
|
426
|
-
state.dataclips[dataclipId] = event.state;
|
|
427
|
-
delete state.activeRun;
|
|
428
|
-
delete state.activeJob;
|
|
429
|
-
state.lastDataclipId = dataclipId;
|
|
430
|
-
event.next?.forEach((nextJobId) => {
|
|
431
|
-
state.inputDataclips[nextJobId] = dataclipId;
|
|
432
|
-
});
|
|
433
|
-
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
434
|
-
job_id,
|
|
435
|
-
event.state,
|
|
436
|
-
error
|
|
437
|
-
);
|
|
438
|
-
state.reasons[job_id] = { reason, error_message, error_type };
|
|
439
|
-
const evt = {
|
|
440
|
-
run_id,
|
|
441
|
-
job_id,
|
|
442
|
-
output_dataclip_id: dataclipId,
|
|
443
|
-
output_dataclip: stringify_default(event.state),
|
|
444
|
-
reason,
|
|
445
|
-
error_message,
|
|
446
|
-
error_type,
|
|
447
|
-
mem: event.mem,
|
|
448
|
-
duration: event.duration,
|
|
449
|
-
thread_id: event.threadId
|
|
450
|
-
};
|
|
451
|
-
return sendEvent(channel, RUN_COMPLETE, evt);
|
|
452
603
|
}
|
|
453
604
|
function onWorkflowStart({ channel }, _event) {
|
|
454
605
|
return sendEvent(channel, ATTEMPT_START);
|
package/dist/start.js
CHANGED
|
@@ -5151,14 +5151,11 @@ var startWorkloop = (app, logger2, minBackoff2, maxBackoff2, maxWorkers) => {
|
|
|
5151
5151
|
};
|
|
5152
5152
|
var workloop_default = startWorkloop;
|
|
5153
5153
|
|
|
5154
|
-
// src/api/execute.ts
|
|
5155
|
-
import crypto3 from "node:crypto";
|
|
5156
|
-
|
|
5157
5154
|
// src/util/convert-attempt.ts
|
|
5158
5155
|
import crypto2 from "node:crypto";
|
|
5159
5156
|
var conditions = {
|
|
5160
|
-
on_job_success: (upstreamId) => `Boolean(!state
|
|
5161
|
-
on_job_failure: (upstreamId) => `Boolean(state
|
|
5157
|
+
on_job_success: (upstreamId) => `Boolean(!state?.errors?.["${upstreamId}"] ?? true)`,
|
|
5158
|
+
on_job_failure: (upstreamId) => `Boolean(state?.errors && state.errors["${upstreamId}"])`,
|
|
5162
5159
|
always: (_upstreamId) => null
|
|
5163
5160
|
};
|
|
5164
5161
|
var mapEdgeCondition = (edge) => {
|
|
@@ -5205,7 +5202,7 @@ var convert_attempt_default = (attempt) => {
|
|
|
5205
5202
|
const id = job.id || crypto2.randomUUID();
|
|
5206
5203
|
nodes[id] = {
|
|
5207
5204
|
id,
|
|
5208
|
-
configuration: job.credential_id,
|
|
5205
|
+
configuration: job.credential || job.credential_id,
|
|
5209
5206
|
expression: job.body,
|
|
5210
5207
|
adaptor: job.adaptor
|
|
5211
5208
|
};
|
|
@@ -5314,6 +5311,201 @@ var calculateAttemptExitReason = (state) => {
|
|
|
5314
5311
|
return { reason: "success", error_type: null, error_message: null };
|
|
5315
5312
|
};
|
|
5316
5313
|
|
|
5314
|
+
// src/events/run-complete.ts
|
|
5315
|
+
import crypto3 from "node:crypto";
|
|
5316
|
+
function onRunComplete({ channel, state }, event, error) {
|
|
5317
|
+
const dataclipId = crypto3.randomUUID();
|
|
5318
|
+
const run_id = state.activeRun;
|
|
5319
|
+
const job_id = state.activeJob;
|
|
5320
|
+
if (!state.dataclips) {
|
|
5321
|
+
state.dataclips = {};
|
|
5322
|
+
}
|
|
5323
|
+
const outputState = event.state || {};
|
|
5324
|
+
state.dataclips[dataclipId] = event.state;
|
|
5325
|
+
delete state.activeRun;
|
|
5326
|
+
delete state.activeJob;
|
|
5327
|
+
state.lastDataclipId = dataclipId;
|
|
5328
|
+
event.next?.forEach((nextJobId) => {
|
|
5329
|
+
state.inputDataclips[nextJobId] = dataclipId;
|
|
5330
|
+
});
|
|
5331
|
+
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
5332
|
+
job_id,
|
|
5333
|
+
event.state,
|
|
5334
|
+
error
|
|
5335
|
+
);
|
|
5336
|
+
state.reasons[job_id] = { reason, error_message, error_type };
|
|
5337
|
+
const evt = {
|
|
5338
|
+
run_id,
|
|
5339
|
+
job_id,
|
|
5340
|
+
output_dataclip_id: dataclipId,
|
|
5341
|
+
output_dataclip: stringify_default(outputState),
|
|
5342
|
+
reason,
|
|
5343
|
+
error_message,
|
|
5344
|
+
error_type,
|
|
5345
|
+
mem: event.mem,
|
|
5346
|
+
duration: event.duration,
|
|
5347
|
+
thread_id: event.threadId
|
|
5348
|
+
};
|
|
5349
|
+
return sendEvent(channel, RUN_COMPLETE, evt);
|
|
5350
|
+
}
|
|
5351
|
+
|
|
5352
|
+
// src/events/run-start.ts
|
|
5353
|
+
import crypto4 from "node:crypto";
|
|
5354
|
+
import { timestamp } from "@openfn/logger";
|
|
5355
|
+
|
|
5356
|
+
// package.json
|
|
5357
|
+
var package_default = {
|
|
5358
|
+
name: "@openfn/ws-worker",
|
|
5359
|
+
version: "0.3.1",
|
|
5360
|
+
description: "A Websocket Worker to connect Lightning to a Runtime Engine",
|
|
5361
|
+
main: "dist/index.js",
|
|
5362
|
+
type: "module",
|
|
5363
|
+
scripts: {
|
|
5364
|
+
test: "pnpm ava --serial",
|
|
5365
|
+
"test:types": "pnpm tsc --noEmit --project tsconfig.json",
|
|
5366
|
+
build: "tsup --config tsup.config.js",
|
|
5367
|
+
"build:watch": "pnpm build --watch",
|
|
5368
|
+
start: "ts-node-esm --transpile-only src/start.ts",
|
|
5369
|
+
"start:prod": "node dist/start.js",
|
|
5370
|
+
"start:watch": "nodemon -e ts,js --watch ../runtime-manager/dist --watch ./src --exec 'pnpm start'",
|
|
5371
|
+
pack: "pnpm pack --pack-destination ../../dist"
|
|
5372
|
+
},
|
|
5373
|
+
bin: {
|
|
5374
|
+
worker: "dist/start.js"
|
|
5375
|
+
},
|
|
5376
|
+
author: "Open Function Group <admin@openfn.org>",
|
|
5377
|
+
license: "ISC",
|
|
5378
|
+
dependencies: {
|
|
5379
|
+
"@koa/router": "^12.0.0",
|
|
5380
|
+
"@openfn/engine-multi": "workspace:*",
|
|
5381
|
+
"@openfn/logger": "workspace:*",
|
|
5382
|
+
"@openfn/runtime": "workspace:*",
|
|
5383
|
+
"@types/koa-logger": "^3.1.2",
|
|
5384
|
+
"@types/ws": "^8.5.6",
|
|
5385
|
+
"fast-safe-stringify": "^2.1.1",
|
|
5386
|
+
figures: "^5.0.0",
|
|
5387
|
+
"human-id": "^4.1.0",
|
|
5388
|
+
jose: "^4.14.6",
|
|
5389
|
+
koa: "^2.13.4",
|
|
5390
|
+
"koa-bodyparser": "^4.4.0",
|
|
5391
|
+
"koa-logger": "^3.2.1",
|
|
5392
|
+
phoenix: "^1.7.7",
|
|
5393
|
+
ws: "^8.14.1"
|
|
5394
|
+
},
|
|
5395
|
+
devDependencies: {
|
|
5396
|
+
"@openfn/lightning-mock": "workspace:*",
|
|
5397
|
+
"@types/koa": "^2.13.5",
|
|
5398
|
+
"@types/koa-bodyparser": "^4.3.10",
|
|
5399
|
+
"@types/koa__router": "^12.0.1",
|
|
5400
|
+
"@types/node": "^18.15.3",
|
|
5401
|
+
"@types/nodemon": "1.19.3",
|
|
5402
|
+
"@types/phoenix": "^1.6.2",
|
|
5403
|
+
"@types/yargs": "^17.0.12",
|
|
5404
|
+
ava: "5.1.0",
|
|
5405
|
+
nodemon: "3.0.1",
|
|
5406
|
+
"ts-node": "^10.9.1",
|
|
5407
|
+
tslib: "^2.4.0",
|
|
5408
|
+
tsup: "^6.2.3",
|
|
5409
|
+
typescript: "^4.6.4",
|
|
5410
|
+
yargs: "^17.6.2"
|
|
5411
|
+
},
|
|
5412
|
+
files: [
|
|
5413
|
+
"dist",
|
|
5414
|
+
"README.md",
|
|
5415
|
+
"CHANGELOG.md"
|
|
5416
|
+
]
|
|
5417
|
+
};
|
|
5418
|
+
|
|
5419
|
+
// src/util/versions.ts
|
|
5420
|
+
import { mainSymbols } from "figures";
|
|
5421
|
+
var { triangleRightSmall: t } = mainSymbols;
|
|
5422
|
+
var versions_default = (runId, versions, adaptor) => {
|
|
5423
|
+
let longest = "compiler".length;
|
|
5424
|
+
for (const v in versions) {
|
|
5425
|
+
longest = Math.max(v.length, longest);
|
|
5426
|
+
}
|
|
5427
|
+
const { node, compiler, engine, worker, runtime, ...adaptors } = versions;
|
|
5428
|
+
const prefix = (str2) => ` ${t} ${str2.padEnd(longest + 4, " ")}`;
|
|
5429
|
+
let str = `Versions for run ${runId}:
|
|
5430
|
+
${prefix("node.js")}${versions.node || "unknown"}
|
|
5431
|
+
${prefix("worker")}${versions.worker || "unknown"}
|
|
5432
|
+
${prefix("engine")}${versions.engine || "unknown"}`;
|
|
5433
|
+
if (Object.keys(adaptors).length) {
|
|
5434
|
+
let allAdaptors = Object.keys(adaptors);
|
|
5435
|
+
if (adaptor) {
|
|
5436
|
+
allAdaptors = allAdaptors.filter((name) => adaptor.startsWith(name));
|
|
5437
|
+
}
|
|
5438
|
+
str += "\n" + allAdaptors.sort().map((adaptorName) => `${prefix(adaptorName)}${adaptors[adaptorName]}`).join("\n");
|
|
5439
|
+
}
|
|
5440
|
+
return str;
|
|
5441
|
+
};
|
|
5442
|
+
|
|
5443
|
+
// src/events/run-start.ts
|
|
5444
|
+
async function onRunStart(context, event) {
|
|
5445
|
+
const time = (timestamp() - BigInt(1e7)).toString();
|
|
5446
|
+
const { channel, state } = context;
|
|
5447
|
+
state.activeRun = crypto4.randomUUID();
|
|
5448
|
+
state.activeJob = event.jobId;
|
|
5449
|
+
const job = state.plan.jobs.find(({ id }) => id === event.jobId);
|
|
5450
|
+
const input_dataclip_id = state.inputDataclips[event.jobId];
|
|
5451
|
+
const versions = {
|
|
5452
|
+
worker: package_default.version,
|
|
5453
|
+
...event.versions
|
|
5454
|
+
};
|
|
5455
|
+
const versionLogContext = {
|
|
5456
|
+
...context,
|
|
5457
|
+
state: {
|
|
5458
|
+
...state,
|
|
5459
|
+
activeRun: state.activeRun
|
|
5460
|
+
}
|
|
5461
|
+
};
|
|
5462
|
+
await sendEvent(channel, RUN_START, {
|
|
5463
|
+
run_id: state.activeRun,
|
|
5464
|
+
job_id: state.activeJob,
|
|
5465
|
+
input_dataclip_id,
|
|
5466
|
+
versions
|
|
5467
|
+
});
|
|
5468
|
+
const versionMessage = versions_default(
|
|
5469
|
+
versionLogContext.state.activeRun,
|
|
5470
|
+
versions,
|
|
5471
|
+
job?.adaptor
|
|
5472
|
+
);
|
|
5473
|
+
await onJobLog(versionLogContext, {
|
|
5474
|
+
time,
|
|
5475
|
+
message: [versionMessage],
|
|
5476
|
+
level: "info",
|
|
5477
|
+
name: "VER"
|
|
5478
|
+
});
|
|
5479
|
+
return;
|
|
5480
|
+
}
|
|
5481
|
+
|
|
5482
|
+
// src/util/throttle.ts
|
|
5483
|
+
var createThrottler = () => {
|
|
5484
|
+
const q = [];
|
|
5485
|
+
let activePromise;
|
|
5486
|
+
const add = (fn) => {
|
|
5487
|
+
return (...args2) => new Promise((resolve5, reject) => {
|
|
5488
|
+
q.push({ fn, args: args2, resolve: resolve5, reject });
|
|
5489
|
+
shift();
|
|
5490
|
+
});
|
|
5491
|
+
};
|
|
5492
|
+
const shift = () => {
|
|
5493
|
+
if (activePromise) {
|
|
5494
|
+
return;
|
|
5495
|
+
}
|
|
5496
|
+
const next = q.shift();
|
|
5497
|
+
if (next) {
|
|
5498
|
+
const { fn, args: args2, resolve: resolve5, reject } = next;
|
|
5499
|
+
activePromise = fn(...args2).then(resolve5).catch(reject).finally(() => {
|
|
5500
|
+
activePromise = void 0;
|
|
5501
|
+
shift();
|
|
5502
|
+
});
|
|
5503
|
+
}
|
|
5504
|
+
};
|
|
5505
|
+
return add;
|
|
5506
|
+
};
|
|
5507
|
+
var throttle_default = createThrottler;
|
|
5508
|
+
|
|
5317
5509
|
// src/api/execute.ts
|
|
5318
5510
|
var enc = new TextDecoder("utf-8");
|
|
5319
5511
|
var eventMap = {
|
|
@@ -5327,7 +5519,8 @@ function execute(channel, engine, logger2, plan, options = {}, onFinish = (_resu
|
|
|
5327
5519
|
}) {
|
|
5328
5520
|
logger2.info("executing ", plan.id);
|
|
5329
5521
|
const state = create_attempt_state_default(plan, options);
|
|
5330
|
-
const context = { channel, state, logger: logger2, onFinish };
|
|
5522
|
+
const context = { channel, state, logger: logger2, engine, onFinish };
|
|
5523
|
+
const throttle = throttle_default();
|
|
5331
5524
|
const addEvent = (eventName, handler) => {
|
|
5332
5525
|
const wrappedFn = async (event) => {
|
|
5333
5526
|
const lightningEvent = eventMap[eventName] ?? eventName;
|
|
@@ -5347,13 +5540,13 @@ function execute(channel, engine, logger2, plan, options = {}, onFinish = (_resu
|
|
|
5347
5540
|
};
|
|
5348
5541
|
const listeners = Object.assign(
|
|
5349
5542
|
{},
|
|
5350
|
-
addEvent("workflow-start", onWorkflowStart),
|
|
5351
|
-
addEvent("job-start",
|
|
5352
|
-
addEvent("job-complete",
|
|
5353
|
-
addEvent("job-error", onJobError),
|
|
5354
|
-
addEvent("workflow-log", onJobLog),
|
|
5355
|
-
addEvent("workflow-complete", onWorkflowComplete),
|
|
5356
|
-
addEvent("workflow-error", onWorkflowError)
|
|
5543
|
+
addEvent("workflow-start", throttle(onWorkflowStart)),
|
|
5544
|
+
addEvent("job-start", throttle(onRunStart)),
|
|
5545
|
+
addEvent("job-complete", throttle(onRunComplete)),
|
|
5546
|
+
addEvent("job-error", throttle(onJobError)),
|
|
5547
|
+
addEvent("workflow-log", throttle(onJobLog)),
|
|
5548
|
+
addEvent("workflow-complete", throttle(onWorkflowComplete)),
|
|
5549
|
+
addEvent("workflow-error", throttle(onWorkflowError))
|
|
5357
5550
|
);
|
|
5358
5551
|
engine.listen(plan.id, listeners);
|
|
5359
5552
|
const resolvers = {
|
|
@@ -5382,60 +5575,18 @@ function execute(channel, engine, logger2, plan, options = {}, onFinish = (_resu
|
|
|
5382
5575
|
return context;
|
|
5383
5576
|
}
|
|
5384
5577
|
var sendEvent = (channel, event, payload) => new Promise((resolve5, reject) => {
|
|
5385
|
-
channel.push(event, payload).receive("error", reject).receive("timeout", () =>
|
|
5578
|
+
channel.push(event, payload).receive("error", reject).receive("timeout", () => {
|
|
5579
|
+
reject(new Error("timeout"));
|
|
5580
|
+
}).receive("ok", resolve5);
|
|
5386
5581
|
});
|
|
5387
|
-
function onJobStart({ channel, state }, event) {
|
|
5388
|
-
state.activeRun = crypto3.randomUUID();
|
|
5389
|
-
state.activeJob = event.jobId;
|
|
5390
|
-
const input_dataclip_id = state.inputDataclips[event.jobId];
|
|
5391
|
-
return sendEvent(channel, RUN_START, {
|
|
5392
|
-
run_id: state.activeRun,
|
|
5393
|
-
job_id: state.activeJob,
|
|
5394
|
-
input_dataclip_id
|
|
5395
|
-
});
|
|
5396
|
-
}
|
|
5397
5582
|
function onJobError(context, event) {
|
|
5398
|
-
const { state
|
|
5399
|
-
if (state
|
|
5400
|
-
|
|
5583
|
+
const { state, error, jobId } = event;
|
|
5584
|
+
if (state?.errors?.[jobId]?.message === error.message) {
|
|
5585
|
+
return onRunComplete(context, event);
|
|
5401
5586
|
} else {
|
|
5402
|
-
|
|
5587
|
+
return onRunComplete(context, event, event.error);
|
|
5403
5588
|
}
|
|
5404
5589
|
}
|
|
5405
|
-
function onJobComplete({ channel, state }, event, error) {
|
|
5406
|
-
const dataclipId = crypto3.randomUUID();
|
|
5407
|
-
const run_id = state.activeRun;
|
|
5408
|
-
const job_id = state.activeJob;
|
|
5409
|
-
if (!state.dataclips) {
|
|
5410
|
-
state.dataclips = {};
|
|
5411
|
-
}
|
|
5412
|
-
state.dataclips[dataclipId] = event.state;
|
|
5413
|
-
delete state.activeRun;
|
|
5414
|
-
delete state.activeJob;
|
|
5415
|
-
state.lastDataclipId = dataclipId;
|
|
5416
|
-
event.next?.forEach((nextJobId) => {
|
|
5417
|
-
state.inputDataclips[nextJobId] = dataclipId;
|
|
5418
|
-
});
|
|
5419
|
-
const { reason, error_message, error_type } = calculateJobExitReason(
|
|
5420
|
-
job_id,
|
|
5421
|
-
event.state,
|
|
5422
|
-
error
|
|
5423
|
-
);
|
|
5424
|
-
state.reasons[job_id] = { reason, error_message, error_type };
|
|
5425
|
-
const evt = {
|
|
5426
|
-
run_id,
|
|
5427
|
-
job_id,
|
|
5428
|
-
output_dataclip_id: dataclipId,
|
|
5429
|
-
output_dataclip: stringify_default(event.state),
|
|
5430
|
-
reason,
|
|
5431
|
-
error_message,
|
|
5432
|
-
error_type,
|
|
5433
|
-
mem: event.mem,
|
|
5434
|
-
duration: event.duration,
|
|
5435
|
-
thread_id: event.threadId
|
|
5436
|
-
};
|
|
5437
|
-
return sendEvent(channel, RUN_COMPLETE, evt);
|
|
5438
|
-
}
|
|
5439
5590
|
function onWorkflowStart({ channel }, _event) {
|
|
5440
5591
|
return sendEvent(channel, ATTEMPT_START);
|
|
5441
5592
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfn/ws-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "A Websocket Worker to connect Lightning to a Runtime Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@types/koa-logger": "^3.1.2",
|
|
15
15
|
"@types/ws": "^8.5.6",
|
|
16
16
|
"fast-safe-stringify": "^2.1.1",
|
|
17
|
+
"figures": "^5.0.0",
|
|
17
18
|
"human-id": "^4.1.0",
|
|
18
19
|
"jose": "^4.14.6",
|
|
19
20
|
"koa": "^2.13.4",
|
|
@@ -21,9 +22,9 @@
|
|
|
21
22
|
"koa-logger": "^3.2.1",
|
|
22
23
|
"phoenix": "^1.7.7",
|
|
23
24
|
"ws": "^8.14.1",
|
|
24
|
-
"@openfn/engine-multi": "0.2.
|
|
25
|
-
"@openfn/
|
|
26
|
-
"@openfn/
|
|
25
|
+
"@openfn/engine-multi": "0.2.4",
|
|
26
|
+
"@openfn/runtime": "0.2.2",
|
|
27
|
+
"@openfn/logger": "0.0.19"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@types/koa": "^2.13.5",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"tsup": "^6.2.3",
|
|
41
42
|
"typescript": "^4.6.4",
|
|
42
43
|
"yargs": "^17.6.2",
|
|
43
|
-
"@openfn/lightning-mock": "1.1.
|
|
44
|
+
"@openfn/lightning-mock": "1.1.6"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"dist",
|