@openfn/ws-worker 0.2.5 → 0.2.7

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # ws-worker
2
2
 
3
+ ## 0.2.7
4
+
5
+ ### Patch Changes
6
+
7
+ - d542aa9: worker: leave attempt channel when finished working
8
+ - Updated dependencies [793d523]
9
+ - Updated dependencies [857c42b]
10
+ - Updated dependencies [f17fb4a]
11
+ - @openfn/engine-multi@0.1.11
12
+ - @openfn/runtime@0.1.4
13
+
14
+ ## 0.2.6
15
+
16
+ ### Patch Changes
17
+
18
+ - 0fb2d58: correctly handle input_dataclip_id for runs
19
+ - Updated dependencies [c8e9d51]
20
+ - Updated dependencies [7f352d2]
21
+ - @openfn/engine-multi@0.1.10
22
+ - @openfn/runtime@0.1.3
23
+
3
24
  ## 0.2.5
4
25
 
5
26
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import Koa from 'koa';
2
2
  import { SanitizePolicies, Logger } from '@openfn/logger';
3
3
  import { Channel as Channel$1 } from 'phoenix';
4
- import { ExecutionPlan } from '@openfn/runtime';
5
4
  import { RuntimeEngine } from '@openfn/engine-multi';
6
5
 
7
6
  type ExitReasonStrings =
@@ -23,6 +22,22 @@ type AttemptOptions = {
23
22
  sanitize?: SanitizePolicies;
24
23
  };
25
24
 
25
+ // Internal server state for each attempt
26
+ type AttemptState = {
27
+ activeRun?: string;
28
+ activeJob?: string;
29
+ plan: ExecutionPlan;
30
+ options: AttemptOptions;
31
+ dataclips: Record<string, any>;
32
+ // For each run, map the input ids
33
+ // TODO better name maybe?
34
+ inputDataclips: Record<string, string>;
35
+ reasons: Record<string, ExitReason>;
36
+
37
+ // final dataclip id
38
+ lastDataclipId?: string;
39
+ };
40
+
26
41
  type ReceiveHook = {
27
42
  receive: (
28
43
  status: 'ok' | 'timeout' | 'error',
@@ -45,20 +60,11 @@ interface Channel extends Channel$1 {
45
60
  // join: () => ReceiveHook;
46
61
  }
47
62
 
48
- declare type AttemptState = {
49
- activeRun?: string;
50
- activeJob?: string;
51
- plan: ExecutionPlan;
52
- options: AttemptOptions;
53
- dataclips: Record<string, any>;
54
- reasons: Record<string, ExitReason>;
55
- lastDataclipId?: string;
56
- };
57
63
  declare type Context = {
58
64
  channel: Channel;
59
65
  state: AttemptState;
60
66
  logger: Logger;
61
- onComplete: (result: any) => void;
67
+ onFinish: (result: any) => void;
62
68
  };
63
69
 
64
70
  declare type CLAIM_ATTEMPT = {
package/dist/index.js CHANGED
@@ -215,6 +215,35 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
215
215
  return value;
216
216
  });
217
217
 
218
+ // src/util/create-attempt-state.ts
219
+ var create_attempt_state_default = (plan, options = {}) => {
220
+ const state = {
221
+ plan,
222
+ lastDataclipId: "",
223
+ dataclips: {},
224
+ inputDataclips: {},
225
+ reasons: {},
226
+ options
227
+ };
228
+ if (typeof plan.initialState === "string") {
229
+ let startNode = plan.jobs[0];
230
+ if (plan.start) {
231
+ startNode = plan.jobs.find(({ id }) => id === plan.start);
232
+ }
233
+ const initialRuns = [];
234
+ if (!startNode.expression) {
235
+ initialRuns.push(...Object.keys(startNode.next));
236
+ } else {
237
+ initialRuns.push(startNode.id);
238
+ }
239
+ initialRuns.forEach((id) => {
240
+ state.inputDataclips[id] = plan.initialState;
241
+ });
242
+ } else {
243
+ }
244
+ return state;
245
+ };
246
+
218
247
  // src/api/reasons.ts
219
248
  var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
220
249
  let reason = "success";
@@ -257,18 +286,11 @@ var eventMap = {
257
286
  "workflow-log": ATTEMPT_LOG,
258
287
  "workflow-complete": ATTEMPT_COMPLETE
259
288
  };
260
- var createAttemptState = (plan, options = {}) => ({
261
- plan,
262
- lastDataclipId: plan.initialState,
263
- dataclips: {},
264
- reasons: {},
265
- options
266
- });
267
- function execute(channel, engine, logger, plan, options = {}, onComplete = (_result) => {
289
+ function execute(channel, engine, logger, plan, options = {}, onFinish = (_result) => {
268
290
  }) {
269
- logger.info("execute...");
270
- const state = createAttemptState(plan, options);
271
- const context = { channel, state, logger, onComplete };
291
+ logger.info("executing ", plan.id);
292
+ const state = create_attempt_state_default(plan, options);
293
+ const context = { channel, state, logger, onFinish };
272
294
  const addEvent = (eventName, handler) => {
273
295
  const wrappedFn = async (event) => {
274
296
  const lightningEvent = eventMap[eventName] ?? eventName;
@@ -312,7 +334,12 @@ function execute(channel, engine, logger, plan, options = {}, onComplete = (_res
312
334
  try {
313
335
  engine.execute(plan, { resolvers, ...options });
314
336
  } catch (e) {
315
- onWorkflowError(context, { workflowId: plan.id, message: e.message });
337
+ onWorkflowError(context, {
338
+ workflowId: plan.id,
339
+ message: e.message,
340
+ type: e.type,
341
+ severity: e.severity
342
+ });
316
343
  }
317
344
  });
318
345
  return context;
@@ -323,10 +350,11 @@ var sendEvent = (channel, event, payload) => new Promise((resolve, reject) => {
323
350
  function onJobStart({ channel, state }, event) {
324
351
  state.activeRun = crypto2.randomUUID();
325
352
  state.activeJob = event.jobId;
353
+ const input_dataclip_id = state.inputDataclips[event.jobId];
326
354
  return sendEvent(channel, RUN_START, {
327
355
  run_id: state.activeRun,
328
356
  job_id: state.activeJob,
329
- input_dataclip_id: state.lastDataclipId
357
+ input_dataclip_id
330
358
  });
331
359
  }
332
360
  function onJobError(context, event) {
@@ -346,47 +374,52 @@ function onJobComplete({ channel, state }, event, error) {
346
374
  }
347
375
  state.dataclips[dataclipId] = event.state;
348
376
  state.lastDataclipId = dataclipId;
377
+ event.next?.forEach((nextJobId) => {
378
+ state.inputDataclips[nextJobId] = dataclipId;
379
+ });
349
380
  delete state.activeRun;
350
381
  delete state.activeJob;
351
- try {
352
- const { reason, error_message, error_type } = calculateJobExitReason(
353
- job_id,
354
- event.state,
355
- error
356
- );
357
- state.reasons[job_id] = { reason, error_message, error_type };
358
- return sendEvent(channel, RUN_COMPLETE, {
359
- run_id,
360
- job_id,
361
- output_dataclip_id: dataclipId,
362
- output_dataclip: stringify_default(event.state),
363
- reason,
364
- error_message,
365
- error_type
366
- });
367
- } catch (e) {
368
- console.log(e);
369
- }
382
+ const { reason, error_message, error_type } = calculateJobExitReason(
383
+ job_id,
384
+ event.state,
385
+ error
386
+ );
387
+ state.reasons[job_id] = { reason, error_message, error_type };
388
+ const evt = {
389
+ run_id,
390
+ job_id,
391
+ output_dataclip_id: dataclipId,
392
+ output_dataclip: stringify_default(event.state),
393
+ reason,
394
+ error_message,
395
+ error_type
396
+ };
397
+ return sendEvent(channel, RUN_COMPLETE, evt);
370
398
  }
371
399
  function onWorkflowStart({ channel }, _event) {
372
400
  return sendEvent(channel, ATTEMPT_START);
373
401
  }
374
- async function onWorkflowComplete({ state, channel, onComplete }, _event) {
402
+ async function onWorkflowComplete({ state, channel, onFinish }, _event) {
375
403
  const result = state.dataclips[state.lastDataclipId];
376
404
  const reason = calculateAttemptExitReason(state);
377
405
  await sendEvent(channel, ATTEMPT_COMPLETE, {
378
406
  final_dataclip_id: state.lastDataclipId,
379
407
  ...reason
380
408
  });
381
- onComplete({ reason, state: result });
409
+ onFinish({ reason, state: result });
382
410
  }
383
- async function onWorkflowError({ state, channel, onComplete }, event) {
384
- const reason = calculateJobExitReason("", { data: {} }, event);
385
- await sendEvent(channel, ATTEMPT_COMPLETE, {
386
- final_dataclip_id: state.lastDataclipId,
387
- ...reason
388
- });
389
- onComplete({ reason });
411
+ async function onWorkflowError({ state, channel, logger, onFinish }, event) {
412
+ try {
413
+ const reason = calculateJobExitReason("", { data: {} }, event);
414
+ await sendEvent(channel, ATTEMPT_COMPLETE, {
415
+ final_dataclip_id: state.lastDataclipId,
416
+ ...reason
417
+ });
418
+ onFinish({ reason });
419
+ } catch (e) {
420
+ logger.error("ERROR in workflow-error handler:", e.message);
421
+ logger.error(e);
422
+ }
390
423
  }
391
424
  function onJobLog({ channel, state }, event) {
392
425
  const timeInMicroseconds = BigInt(event.time) / BigInt(1e3);
@@ -576,8 +609,9 @@ function createServer(engine, options = {}) {
576
609
  plan,
577
610
  options: options2
578
611
  } = await attempt_default(app.socket, token, id, logger);
579
- const onComplete = () => {
612
+ const onFinish = () => {
580
613
  delete app.workflows[id];
614
+ attemptChannel.leave();
581
615
  };
582
616
  const context = execute(
583
617
  attemptChannel,
@@ -585,7 +619,7 @@ function createServer(engine, options = {}) {
585
619
  logger,
586
620
  plan,
587
621
  options2,
588
- onComplete
622
+ onFinish
589
623
  );
590
624
  app.workflows[id] = context;
591
625
  } else {
package/dist/start.js CHANGED
@@ -4939,7 +4939,13 @@ async function createMock() {
4939
4939
  nextState = initialState;
4940
4940
  }
4941
4941
  }
4942
- dispatch("job-complete", { workflowId, jobId, state: nextState, runId });
4942
+ dispatch("job-complete", {
4943
+ workflowId,
4944
+ jobId,
4945
+ state: nextState,
4946
+ runId,
4947
+ next: []
4948
+ });
4943
4949
  return nextState;
4944
4950
  };
4945
4951
  const execute2 = (xplan, options = {
@@ -5197,6 +5203,35 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
5197
5203
  return value;
5198
5204
  });
5199
5205
 
5206
+ // src/util/create-attempt-state.ts
5207
+ var create_attempt_state_default = (plan, options = {}) => {
5208
+ const state = {
5209
+ plan,
5210
+ lastDataclipId: "",
5211
+ dataclips: {},
5212
+ inputDataclips: {},
5213
+ reasons: {},
5214
+ options
5215
+ };
5216
+ if (typeof plan.initialState === "string") {
5217
+ let startNode = plan.jobs[0];
5218
+ if (plan.start) {
5219
+ startNode = plan.jobs.find(({ id }) => id === plan.start);
5220
+ }
5221
+ const initialRuns = [];
5222
+ if (!startNode.expression) {
5223
+ initialRuns.push(...Object.keys(startNode.next));
5224
+ } else {
5225
+ initialRuns.push(startNode.id);
5226
+ }
5227
+ initialRuns.forEach((id) => {
5228
+ state.inputDataclips[id] = plan.initialState;
5229
+ });
5230
+ } else {
5231
+ }
5232
+ return state;
5233
+ };
5234
+
5200
5235
  // src/api/reasons.ts
5201
5236
  var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
5202
5237
  let reason = "success";
@@ -5239,18 +5274,11 @@ var eventMap = {
5239
5274
  "workflow-log": ATTEMPT_LOG,
5240
5275
  "workflow-complete": ATTEMPT_COMPLETE
5241
5276
  };
5242
- var createAttemptState = (plan, options = {}) => ({
5243
- plan,
5244
- lastDataclipId: plan.initialState,
5245
- dataclips: {},
5246
- reasons: {},
5247
- options
5248
- });
5249
- function execute(channel, engine, logger2, plan, options = {}, onComplete = (_result) => {
5277
+ function execute(channel, engine, logger2, plan, options = {}, onFinish = (_result) => {
5250
5278
  }) {
5251
- logger2.info("execute...");
5252
- const state = createAttemptState(plan, options);
5253
- const context = { channel, state, logger: logger2, onComplete };
5279
+ logger2.info("executing ", plan.id);
5280
+ const state = create_attempt_state_default(plan, options);
5281
+ const context = { channel, state, logger: logger2, onFinish };
5254
5282
  const addEvent = (eventName, handler) => {
5255
5283
  const wrappedFn = async (event) => {
5256
5284
  const lightningEvent = eventMap[eventName] ?? eventName;
@@ -5294,7 +5322,12 @@ function execute(channel, engine, logger2, plan, options = {}, onComplete = (_re
5294
5322
  try {
5295
5323
  engine.execute(plan, { resolvers, ...options });
5296
5324
  } catch (e) {
5297
- onWorkflowError(context, { workflowId: plan.id, message: e.message });
5325
+ onWorkflowError(context, {
5326
+ workflowId: plan.id,
5327
+ message: e.message,
5328
+ type: e.type,
5329
+ severity: e.severity
5330
+ });
5298
5331
  }
5299
5332
  });
5300
5333
  return context;
@@ -5305,10 +5338,11 @@ var sendEvent = (channel, event, payload) => new Promise((resolve5, reject) => {
5305
5338
  function onJobStart({ channel, state }, event) {
5306
5339
  state.activeRun = crypto3.randomUUID();
5307
5340
  state.activeJob = event.jobId;
5341
+ const input_dataclip_id = state.inputDataclips[event.jobId];
5308
5342
  return sendEvent(channel, RUN_START, {
5309
5343
  run_id: state.activeRun,
5310
5344
  job_id: state.activeJob,
5311
- input_dataclip_id: state.lastDataclipId
5345
+ input_dataclip_id
5312
5346
  });
5313
5347
  }
5314
5348
  function onJobError(context, event) {
@@ -5328,47 +5362,52 @@ function onJobComplete({ channel, state }, event, error) {
5328
5362
  }
5329
5363
  state.dataclips[dataclipId] = event.state;
5330
5364
  state.lastDataclipId = dataclipId;
5365
+ event.next?.forEach((nextJobId) => {
5366
+ state.inputDataclips[nextJobId] = dataclipId;
5367
+ });
5331
5368
  delete state.activeRun;
5332
5369
  delete state.activeJob;
5333
- try {
5334
- const { reason, error_message, error_type } = calculateJobExitReason(
5335
- job_id,
5336
- event.state,
5337
- error
5338
- );
5339
- state.reasons[job_id] = { reason, error_message, error_type };
5340
- return sendEvent(channel, RUN_COMPLETE, {
5341
- run_id,
5342
- job_id,
5343
- output_dataclip_id: dataclipId,
5344
- output_dataclip: stringify_default(event.state),
5345
- reason,
5346
- error_message,
5347
- error_type
5348
- });
5349
- } catch (e) {
5350
- console.log(e);
5351
- }
5370
+ const { reason, error_message, error_type } = calculateJobExitReason(
5371
+ job_id,
5372
+ event.state,
5373
+ error
5374
+ );
5375
+ state.reasons[job_id] = { reason, error_message, error_type };
5376
+ const evt = {
5377
+ run_id,
5378
+ job_id,
5379
+ output_dataclip_id: dataclipId,
5380
+ output_dataclip: stringify_default(event.state),
5381
+ reason,
5382
+ error_message,
5383
+ error_type
5384
+ };
5385
+ return sendEvent(channel, RUN_COMPLETE, evt);
5352
5386
  }
5353
5387
  function onWorkflowStart({ channel }, _event) {
5354
5388
  return sendEvent(channel, ATTEMPT_START);
5355
5389
  }
5356
- async function onWorkflowComplete({ state, channel, onComplete }, _event) {
5390
+ async function onWorkflowComplete({ state, channel, onFinish }, _event) {
5357
5391
  const result = state.dataclips[state.lastDataclipId];
5358
5392
  const reason = calculateAttemptExitReason(state);
5359
5393
  await sendEvent(channel, ATTEMPT_COMPLETE, {
5360
5394
  final_dataclip_id: state.lastDataclipId,
5361
5395
  ...reason
5362
5396
  });
5363
- onComplete({ reason, state: result });
5397
+ onFinish({ reason, state: result });
5364
5398
  }
5365
- async function onWorkflowError({ state, channel, onComplete }, event) {
5366
- const reason = calculateJobExitReason("", { data: {} }, event);
5367
- await sendEvent(channel, ATTEMPT_COMPLETE, {
5368
- final_dataclip_id: state.lastDataclipId,
5369
- ...reason
5370
- });
5371
- onComplete({ reason });
5399
+ async function onWorkflowError({ state, channel, logger: logger2, onFinish }, event) {
5400
+ try {
5401
+ const reason = calculateJobExitReason("", { data: {} }, event);
5402
+ await sendEvent(channel, ATTEMPT_COMPLETE, {
5403
+ final_dataclip_id: state.lastDataclipId,
5404
+ ...reason
5405
+ });
5406
+ onFinish({ reason });
5407
+ } catch (e) {
5408
+ logger2.error("ERROR in workflow-error handler:", e.message);
5409
+ logger2.error(e);
5410
+ }
5372
5411
  }
5373
5412
  function onJobLog({ channel, state }, event) {
5374
5413
  const timeInMicroseconds = BigInt(event.time) / BigInt(1e3);
@@ -5558,8 +5597,9 @@ function createServer(engine, options = {}) {
5558
5597
  plan,
5559
5598
  options: options2
5560
5599
  } = await attempt_default(app.socket, token, id, logger2);
5561
- const onComplete = () => {
5600
+ const onFinish = () => {
5562
5601
  delete app.workflows[id];
5602
+ attemptChannel.leave();
5563
5603
  };
5564
5604
  const context = execute(
5565
5605
  attemptChannel,
@@ -5567,7 +5607,7 @@ function createServer(engine, options = {}) {
5567
5607
  logger2,
5568
5608
  plan,
5569
5609
  options2,
5570
- onComplete
5610
+ onFinish
5571
5611
  );
5572
5612
  app.workflows[id] = context;
5573
5613
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/ws-worker",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "A Websocket Worker to connect Lightning to a Runtime Engine",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -21,8 +21,8 @@
21
21
  "koa-logger": "^3.2.1",
22
22
  "phoenix": "^1.7.7",
23
23
  "ws": "^8.14.1",
24
- "@openfn/engine-multi": "0.1.9",
25
- "@openfn/runtime": "0.1.2",
24
+ "@openfn/engine-multi": "0.1.11",
25
+ "@openfn/runtime": "0.1.4",
26
26
  "@openfn/logger": "0.0.19"
27
27
  },
28
28
  "devDependencies": {
@@ -40,7 +40,7 @@
40
40
  "tsup": "^6.2.3",
41
41
  "typescript": "^4.6.4",
42
42
  "yargs": "^17.6.2",
43
- "@openfn/lightning-mock": "1.0.10"
43
+ "@openfn/lightning-mock": "1.0.12"
44
44
  },
45
45
  "files": [
46
46
  "dist",