@tasker-systems/tasker 0.1.3 → 0.1.5
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 +47 -86
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.js +374 -295
- package/dist/events/index.js.map +1 -1
- package/dist/ffi/index.d.ts +323 -242
- package/dist/ffi/index.js +66 -1896
- package/dist/ffi/index.js.map +1 -1
- package/dist/{index-CTl8lGpU.d.ts → index-Bvdub2HH.d.ts} +77 -36
- package/dist/index.d.ts +75 -141
- package/dist/index.js +622 -2491
- package/dist/index.js.map +1 -1
- package/package.json +14 -7
- package/{native/libtasker_ts-darwin-arm64.dylib → tasker_ts.darwin-arm64.node} +0 -0
- package/{native/libtasker_ts-linux-x64.so → tasker_ts.linux-x64-gnu.node} +0 -0
- package/dist/koffi-3HFAASOB.node +0 -0
- package/dist/koffi-AHHUCM3C.node +0 -0
- package/dist/koffi-AVDVVSXH.node +0 -0
- package/dist/koffi-BMO5K7B3.node +0 -0
- package/dist/koffi-G4D35B2D.node +0 -0
- package/dist/koffi-GG4SDSYA.node +0 -0
- package/dist/koffi-GOENU54R.node +0 -0
- package/dist/koffi-IDX6JEDH.node +0 -0
- package/dist/koffi-KFZAXWPQ.node +0 -0
- package/dist/koffi-LOH6WKRQ.node +0 -0
- package/dist/koffi-LUY2JHJP.node +0 -0
- package/dist/koffi-OMHWL3D6.node +0 -0
- package/dist/koffi-QKY2KSXW.node +0 -0
- package/dist/koffi-ROB3FRHA.node +0 -0
- package/dist/koffi-SE4ZI36U.node +0 -0
- package/dist/koffi-X3YT67KE.node +0 -0
- package/dist/koffi-X7JMBSZH.node +0 -0
- package/dist/koffi-YNQDUF3Q.node +0 -0
- package/dist/runtime-interface-D940vUzy.d.ts +0 -694
package/dist/events/index.js
CHANGED
|
@@ -208,7 +208,7 @@ if (process.env.TASKER_ENV !== "production") {
|
|
|
208
208
|
}
|
|
209
209
|
var log = pino(loggerOptions);
|
|
210
210
|
var EventPoller = class {
|
|
211
|
-
|
|
211
|
+
module;
|
|
212
212
|
config;
|
|
213
213
|
emitter;
|
|
214
214
|
state = "stopped";
|
|
@@ -221,12 +221,12 @@ var EventPoller = class {
|
|
|
221
221
|
/**
|
|
222
222
|
* Create a new EventPoller.
|
|
223
223
|
*
|
|
224
|
-
* @param
|
|
224
|
+
* @param module - The napi-rs module for polling events
|
|
225
225
|
* @param emitter - The event emitter to dispatch events to (required, no fallback)
|
|
226
226
|
* @param config - Optional configuration for polling behavior
|
|
227
227
|
*/
|
|
228
|
-
constructor(
|
|
229
|
-
this.
|
|
228
|
+
constructor(module, emitter, config = {}) {
|
|
229
|
+
this.module = module;
|
|
230
230
|
this.emitter = emitter;
|
|
231
231
|
this.config = {
|
|
232
232
|
pollIntervalMs: config.pollIntervalMs ?? 10,
|
|
@@ -293,13 +293,6 @@ var EventPoller = class {
|
|
|
293
293
|
log.debug({ component: "event-poller" }, "Already running, returning early");
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
|
-
log.debug(
|
|
297
|
-
{ component: "event-poller", runtimeLoaded: this.runtime.isLoaded },
|
|
298
|
-
"Checking runtime.isLoaded"
|
|
299
|
-
);
|
|
300
|
-
if (!this.runtime.isLoaded) {
|
|
301
|
-
throw new Error("Runtime not loaded. Call runtime.load() first.");
|
|
302
|
-
}
|
|
303
296
|
this.state = "running";
|
|
304
297
|
this.pollCount = 0;
|
|
305
298
|
this.cycleCount = 0;
|
|
@@ -360,17 +353,17 @@ var EventPoller = class {
|
|
|
360
353
|
try {
|
|
361
354
|
let eventsProcessed = 0;
|
|
362
355
|
for (let i = 0; i < this.config.maxEventsPerCycle; i++) {
|
|
363
|
-
const event = this.
|
|
356
|
+
const event = this.module.pollStepEvents();
|
|
364
357
|
if (event === null) {
|
|
365
358
|
break;
|
|
366
359
|
}
|
|
367
360
|
eventsProcessed++;
|
|
368
|
-
const handlerCallable = event.
|
|
361
|
+
const handlerCallable = event.stepDefinition.handlerCallable;
|
|
369
362
|
log.info(
|
|
370
363
|
{
|
|
371
364
|
component: "event-poller",
|
|
372
365
|
operation: "event_received",
|
|
373
|
-
stepUuid: event.
|
|
366
|
+
stepUuid: event.stepUuid,
|
|
374
367
|
handlerCallable,
|
|
375
368
|
eventIndex: i
|
|
376
369
|
},
|
|
@@ -382,7 +375,7 @@ var EventPoller = class {
|
|
|
382
375
|
this.checkStarvation();
|
|
383
376
|
}
|
|
384
377
|
if (this.pollCount % this.config.cleanupInterval === 0) {
|
|
385
|
-
this.
|
|
378
|
+
this.module.cleanupTimeouts();
|
|
386
379
|
}
|
|
387
380
|
if (this.pollCount % this.config.metricsInterval === 0) {
|
|
388
381
|
this.emitMetrics();
|
|
@@ -403,12 +396,12 @@ var EventPoller = class {
|
|
|
403
396
|
* Handle a step event
|
|
404
397
|
*/
|
|
405
398
|
handleStepEvent(event) {
|
|
406
|
-
const handlerCallable = event.
|
|
399
|
+
const handlerCallable = event.stepDefinition.handlerCallable;
|
|
407
400
|
log.debug(
|
|
408
401
|
{
|
|
409
402
|
component: "event-poller",
|
|
410
403
|
operation: "handle_step_event",
|
|
411
|
-
stepUuid: event.
|
|
404
|
+
stepUuid: event.stepUuid,
|
|
412
405
|
handlerCallable,
|
|
413
406
|
hasCallback: !!this.stepEventCallback
|
|
414
407
|
},
|
|
@@ -419,7 +412,7 @@ var EventPoller = class {
|
|
|
419
412
|
{
|
|
420
413
|
component: "event-poller",
|
|
421
414
|
emitterInstanceId: this.emitter.getInstanceId(),
|
|
422
|
-
stepUuid: event.
|
|
415
|
+
stepUuid: event.stepUuid,
|
|
423
416
|
listenerCount: listenerCountBefore,
|
|
424
417
|
eventName: StepEventNames.STEP_EXECUTION_RECEIVED
|
|
425
418
|
},
|
|
@@ -433,7 +426,7 @@ var EventPoller = class {
|
|
|
433
426
|
log.info(
|
|
434
427
|
{
|
|
435
428
|
component: "event-poller",
|
|
436
|
-
stepUuid: event.
|
|
429
|
+
stepUuid: event.stepUuid,
|
|
437
430
|
emitResult,
|
|
438
431
|
listenerCountAfter: this.emitter.listenerCount(StepEventNames.STEP_EXECUTION_RECEIVED),
|
|
439
432
|
eventName: StepEventNames.STEP_EXECUTION_RECEIVED
|
|
@@ -444,7 +437,7 @@ var EventPoller = class {
|
|
|
444
437
|
log.error(
|
|
445
438
|
{
|
|
446
439
|
component: "event-poller",
|
|
447
|
-
stepUuid: event.
|
|
440
|
+
stepUuid: event.stepUuid,
|
|
448
441
|
error: emitError instanceof Error ? emitError.message : String(emitError),
|
|
449
442
|
stack: emitError instanceof Error ? emitError.stack : void 0
|
|
450
443
|
},
|
|
@@ -453,7 +446,7 @@ var EventPoller = class {
|
|
|
453
446
|
}
|
|
454
447
|
if (this.stepEventCallback) {
|
|
455
448
|
log.debug(
|
|
456
|
-
{ component: "event-poller", stepUuid: event.
|
|
449
|
+
{ component: "event-poller", stepUuid: event.stepUuid },
|
|
457
450
|
"Invoking step event callback"
|
|
458
451
|
);
|
|
459
452
|
this.stepEventCallback(event).catch((error) => {
|
|
@@ -461,7 +454,7 @@ var EventPoller = class {
|
|
|
461
454
|
});
|
|
462
455
|
} else {
|
|
463
456
|
log.warn(
|
|
464
|
-
{ component: "event-poller", stepUuid: event.
|
|
457
|
+
{ component: "event-poller", stepUuid: event.stepUuid },
|
|
465
458
|
"No step event callback registered!"
|
|
466
459
|
);
|
|
467
460
|
}
|
|
@@ -471,9 +464,9 @@ var EventPoller = class {
|
|
|
471
464
|
*/
|
|
472
465
|
checkStarvation() {
|
|
473
466
|
try {
|
|
474
|
-
this.
|
|
475
|
-
const metrics = this.
|
|
476
|
-
if (metrics.
|
|
467
|
+
this.module.checkStarvationWarnings();
|
|
468
|
+
const metrics = this.module.getFfiDispatchMetrics();
|
|
469
|
+
if (metrics.starvationDetected) {
|
|
477
470
|
this.emitter.emitStarvationDetected(metrics);
|
|
478
471
|
}
|
|
479
472
|
} catch (error) {
|
|
@@ -485,7 +478,7 @@ var EventPoller = class {
|
|
|
485
478
|
*/
|
|
486
479
|
emitMetrics() {
|
|
487
480
|
try {
|
|
488
|
-
const metrics = this.
|
|
481
|
+
const metrics = this.module.getFfiDispatchMetrics();
|
|
489
482
|
this.emitter.emitMetricsUpdated(metrics);
|
|
490
483
|
if (this.metricsCallback) {
|
|
491
484
|
this.metricsCallback(metrics);
|
|
@@ -510,23 +503,8 @@ var EventPoller = class {
|
|
|
510
503
|
}
|
|
511
504
|
}
|
|
512
505
|
};
|
|
513
|
-
function createEventPoller(
|
|
514
|
-
return new EventPoller(
|
|
515
|
-
}
|
|
516
|
-
function getLoggingRuntime() {
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
519
|
-
function toFfiFields(fields) {
|
|
520
|
-
if (!fields) {
|
|
521
|
-
return {};
|
|
522
|
-
}
|
|
523
|
-
const result = {};
|
|
524
|
-
for (const [key, value] of Object.entries(fields)) {
|
|
525
|
-
if (value !== void 0) {
|
|
526
|
-
result[key] = value;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return result;
|
|
506
|
+
function createEventPoller(module, emitter, config) {
|
|
507
|
+
return new EventPoller(module, emitter, config);
|
|
530
508
|
}
|
|
531
509
|
function fallbackLog(level, message, fields) {
|
|
532
510
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -534,52 +512,28 @@ function fallbackLog(level, message, fields) {
|
|
|
534
512
|
console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}${fieldsStr}`);
|
|
535
513
|
}
|
|
536
514
|
function logError(message, fields) {
|
|
537
|
-
|
|
538
|
-
if (!runtime) {
|
|
515
|
+
{
|
|
539
516
|
fallbackLog("error", message, fields);
|
|
540
517
|
return;
|
|
541
518
|
}
|
|
542
|
-
try {
|
|
543
|
-
runtime.logError(message, toFfiFields(fields));
|
|
544
|
-
} catch {
|
|
545
|
-
fallbackLog("error", message, fields);
|
|
546
|
-
}
|
|
547
519
|
}
|
|
548
520
|
function logWarn(message, fields) {
|
|
549
|
-
|
|
550
|
-
if (!runtime) {
|
|
521
|
+
{
|
|
551
522
|
fallbackLog("warn", message, fields);
|
|
552
523
|
return;
|
|
553
524
|
}
|
|
554
|
-
try {
|
|
555
|
-
runtime.logWarn(message, toFfiFields(fields));
|
|
556
|
-
} catch {
|
|
557
|
-
fallbackLog("warn", message, fields);
|
|
558
|
-
}
|
|
559
525
|
}
|
|
560
526
|
function logInfo(message, fields) {
|
|
561
|
-
|
|
562
|
-
if (!runtime) {
|
|
527
|
+
{
|
|
563
528
|
fallbackLog("info", message, fields);
|
|
564
529
|
return;
|
|
565
530
|
}
|
|
566
|
-
try {
|
|
567
|
-
runtime.logInfo(message, toFfiFields(fields));
|
|
568
|
-
} catch {
|
|
569
|
-
fallbackLog("info", message, fields);
|
|
570
|
-
}
|
|
571
531
|
}
|
|
572
532
|
function logDebug(message, fields) {
|
|
573
|
-
|
|
574
|
-
if (!runtime) {
|
|
533
|
+
{
|
|
575
534
|
fallbackLog("debug", message, fields);
|
|
576
535
|
return;
|
|
577
536
|
}
|
|
578
|
-
try {
|
|
579
|
-
runtime.logDebug(message, toFfiFields(fields));
|
|
580
|
-
} catch {
|
|
581
|
-
fallbackLog("debug", message, fields);
|
|
582
|
-
}
|
|
583
537
|
}
|
|
584
538
|
|
|
585
539
|
// src/types/step-context.ts
|
|
@@ -598,9 +552,9 @@ var StepContext = class _StepContext {
|
|
|
598
552
|
inputData;
|
|
599
553
|
/** Results from dependent steps */
|
|
600
554
|
dependencyResults;
|
|
601
|
-
/** Handler-specific configuration (from
|
|
555
|
+
/** Handler-specific configuration (from stepDefinition.handlerInitialization) */
|
|
602
556
|
stepConfig;
|
|
603
|
-
/** Step-specific inputs (from
|
|
557
|
+
/** Step-specific inputs (from workflowStep.inputs, used for batch cursor config) */
|
|
604
558
|
stepInputs;
|
|
605
559
|
/** Current retry attempt number */
|
|
606
560
|
retryCount;
|
|
@@ -622,16 +576,15 @@ var StepContext = class _StepContext {
|
|
|
622
576
|
/**
|
|
623
577
|
* Create a StepContext from an FFI event.
|
|
624
578
|
*
|
|
625
|
-
*
|
|
626
|
-
* the task_sequence_step payload.
|
|
579
|
+
* TAS-290: All field access uses camelCase (napi-rs auto-converts).
|
|
627
580
|
*
|
|
628
581
|
* The FFI data structure mirrors the Ruby TaskSequenceStepWrapper:
|
|
629
582
|
* - task.context -> inputData (task context with user inputs)
|
|
630
|
-
* -
|
|
631
|
-
* -
|
|
632
|
-
* -
|
|
633
|
-
* -
|
|
634
|
-
* -
|
|
583
|
+
* - dependencyResults -> results from parent steps
|
|
584
|
+
* - stepDefinition.handlerInitialization -> stepConfig
|
|
585
|
+
* - workflowStep.attempts -> retryCount
|
|
586
|
+
* - workflowStep.maxAttempts -> maxRetries
|
|
587
|
+
* - workflowStep.inputs -> stepInputs
|
|
635
588
|
*
|
|
636
589
|
* @param event - The FFI step event
|
|
637
590
|
* @param handlerName - Name of the handler to execute
|
|
@@ -640,19 +593,18 @@ var StepContext = class _StepContext {
|
|
|
640
593
|
static fromFfiEvent(event, handlerName) {
|
|
641
594
|
const task = event.task ?? {};
|
|
642
595
|
const inputData = task.context ?? {};
|
|
643
|
-
const dependencyResults = event.
|
|
644
|
-
const stepDefinition = event.
|
|
645
|
-
const
|
|
646
|
-
const
|
|
647
|
-
const workflowStep = event.workflow_step ?? {};
|
|
596
|
+
const dependencyResults = event.dependencyResults ?? {};
|
|
597
|
+
const stepDefinition = event.stepDefinition ?? {};
|
|
598
|
+
const stepConfig = stepDefinition.handlerInitialization ?? {};
|
|
599
|
+
const workflowStep = event.workflowStep ?? {};
|
|
648
600
|
const retryCount = workflowStep.attempts ?? 0;
|
|
649
|
-
const maxRetries = workflowStep.
|
|
601
|
+
const maxRetries = workflowStep.maxAttempts ?? 3;
|
|
650
602
|
const stepInputs = workflowStep.inputs ?? {};
|
|
651
603
|
return new _StepContext({
|
|
652
604
|
event,
|
|
653
|
-
taskUuid: event.
|
|
654
|
-
stepUuid: event.
|
|
655
|
-
correlationId: event.
|
|
605
|
+
taskUuid: event.taskUuid,
|
|
606
|
+
stepUuid: event.stepUuid,
|
|
607
|
+
correlationId: event.correlationId,
|
|
656
608
|
handlerName,
|
|
657
609
|
inputData,
|
|
658
610
|
dependencyResults,
|
|
@@ -782,10 +734,14 @@ var StepContext = class _StepContext {
|
|
|
782
734
|
/**
|
|
783
735
|
* Get the raw checkpoint data from the workflow step.
|
|
784
736
|
*
|
|
737
|
+
* Note: Checkpoint data from the database uses snake_case keys
|
|
738
|
+
* (cursor, items_processed, accumulated_results) because it's stored
|
|
739
|
+
* as a serde_json::Value that passes through napi-rs as-is.
|
|
740
|
+
*
|
|
785
741
|
* @returns The checkpoint data object or null if not set
|
|
786
742
|
*/
|
|
787
743
|
get checkpoint() {
|
|
788
|
-
const workflowStep = this.event.
|
|
744
|
+
const workflowStep = this.event.workflowStep ?? {};
|
|
789
745
|
return workflowStep.checkpoint ?? null;
|
|
790
746
|
}
|
|
791
747
|
/**
|
|
@@ -885,6 +841,130 @@ var StepContext = class _StepContext {
|
|
|
885
841
|
}
|
|
886
842
|
};
|
|
887
843
|
|
|
844
|
+
// src/types/step-handler-result.ts
|
|
845
|
+
var StepHandlerResult = class _StepHandlerResult {
|
|
846
|
+
/** Whether the handler executed successfully */
|
|
847
|
+
success;
|
|
848
|
+
/** Handler output data (success case) */
|
|
849
|
+
result;
|
|
850
|
+
/** Error message (failure case) */
|
|
851
|
+
errorMessage;
|
|
852
|
+
/** Error type/category for classification */
|
|
853
|
+
errorType;
|
|
854
|
+
/** Optional application-specific error code */
|
|
855
|
+
errorCode;
|
|
856
|
+
/** Whether the error is retryable */
|
|
857
|
+
retryable;
|
|
858
|
+
/** Additional execution metadata */
|
|
859
|
+
metadata;
|
|
860
|
+
/** Orchestration metadata for workflow coordination hints (e.g., backoff, headers) */
|
|
861
|
+
orchestrationMetadata;
|
|
862
|
+
constructor(params) {
|
|
863
|
+
this.success = params.success;
|
|
864
|
+
this.result = params.result ?? null;
|
|
865
|
+
this.errorMessage = params.errorMessage ?? null;
|
|
866
|
+
this.errorType = params.errorType ?? null;
|
|
867
|
+
this.errorCode = params.errorCode ?? null;
|
|
868
|
+
this.retryable = params.retryable ?? true;
|
|
869
|
+
this.metadata = params.metadata ?? {};
|
|
870
|
+
this.orchestrationMetadata = params.orchestrationMetadata ?? null;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Create a successful handler result.
|
|
874
|
+
*
|
|
875
|
+
* This is the primary factory method for creating success results.
|
|
876
|
+
* Aligned with Ruby and Python worker APIs.
|
|
877
|
+
*
|
|
878
|
+
* @param result - The handler output data
|
|
879
|
+
* @param metadata - Optional additional metadata
|
|
880
|
+
* @returns A StepHandlerResult indicating success
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* ```typescript
|
|
884
|
+
* return StepHandlerResult.success(
|
|
885
|
+
* { processed: 100, skipped: 5 }
|
|
886
|
+
* );
|
|
887
|
+
* ```
|
|
888
|
+
*/
|
|
889
|
+
static success(result, metadata) {
|
|
890
|
+
return new _StepHandlerResult({
|
|
891
|
+
success: true,
|
|
892
|
+
result,
|
|
893
|
+
metadata: metadata ?? {}
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Create a failure handler result.
|
|
898
|
+
*
|
|
899
|
+
* @param message - Human-readable error message
|
|
900
|
+
* @param errorType - Error type/category for classification. Use ErrorType enum.
|
|
901
|
+
* @param retryable - Whether the error is retryable (default: true)
|
|
902
|
+
* @param metadata - Optional additional metadata
|
|
903
|
+
* @param errorCode - Optional application-specific error code
|
|
904
|
+
* @returns A StepHandlerResult indicating failure
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```typescript
|
|
908
|
+
* return StepHandlerResult.failure(
|
|
909
|
+
* 'Invalid input format',
|
|
910
|
+
* ErrorType.VALIDATION_ERROR,
|
|
911
|
+
* false
|
|
912
|
+
* );
|
|
913
|
+
* ```
|
|
914
|
+
*
|
|
915
|
+
* @example With error code
|
|
916
|
+
* ```typescript
|
|
917
|
+
* return StepHandlerResult.failure(
|
|
918
|
+
* 'Gateway timeout',
|
|
919
|
+
* ErrorType.TIMEOUT,
|
|
920
|
+
* true,
|
|
921
|
+
* { duration_ms: 30000 },
|
|
922
|
+
* 'GATEWAY_TIMEOUT'
|
|
923
|
+
* );
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
static failure(message, errorType = "handler_error" /* HANDLER_ERROR */, retryable = true, metadata, errorCode) {
|
|
927
|
+
return new _StepHandlerResult({
|
|
928
|
+
success: false,
|
|
929
|
+
errorMessage: message,
|
|
930
|
+
// ErrorType enum values are already strings, so this works directly
|
|
931
|
+
errorType,
|
|
932
|
+
errorCode: errorCode ?? null,
|
|
933
|
+
retryable,
|
|
934
|
+
metadata: metadata ?? {}
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Check if this result indicates success.
|
|
939
|
+
*/
|
|
940
|
+
isSuccess() {
|
|
941
|
+
return this.success;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Check if this result indicates failure.
|
|
945
|
+
*/
|
|
946
|
+
isFailure() {
|
|
947
|
+
return !this.success;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Convert to JSON for serialization.
|
|
951
|
+
*
|
|
952
|
+
* Uses snake_case keys to match the Rust FFI contract.
|
|
953
|
+
*/
|
|
954
|
+
toJSON() {
|
|
955
|
+
return {
|
|
956
|
+
success: this.success,
|
|
957
|
+
result: this.result,
|
|
958
|
+
error_message: this.errorMessage,
|
|
959
|
+
error_type: this.errorType,
|
|
960
|
+
error_code: this.errorCode,
|
|
961
|
+
retryable: this.retryable,
|
|
962
|
+
metadata: this.metadata,
|
|
963
|
+
orchestration_metadata: this.orchestrationMetadata
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
|
|
888
968
|
// src/subscriber/step-execution-subscriber.ts
|
|
889
969
|
var loggerOptions2 = {
|
|
890
970
|
name: "step-subscriber",
|
|
@@ -900,7 +980,7 @@ var pinoLog = pino(loggerOptions2);
|
|
|
900
980
|
var StepExecutionSubscriber = class {
|
|
901
981
|
emitter;
|
|
902
982
|
registry;
|
|
903
|
-
|
|
983
|
+
module;
|
|
904
984
|
workerId;
|
|
905
985
|
maxConcurrent;
|
|
906
986
|
handlerTimeoutMs;
|
|
@@ -913,13 +993,13 @@ var StepExecutionSubscriber = class {
|
|
|
913
993
|
*
|
|
914
994
|
* @param emitter - The event emitter to subscribe to (required, no fallback)
|
|
915
995
|
* @param registry - The handler registry for resolving step handlers
|
|
916
|
-
* @param
|
|
996
|
+
* @param module - The napi-rs module for submitting results (required, no fallback)
|
|
917
997
|
* @param config - Optional configuration for execution behavior
|
|
918
998
|
*/
|
|
919
|
-
constructor(emitter, registry,
|
|
999
|
+
constructor(emitter, registry, module, config = {}) {
|
|
920
1000
|
this.emitter = emitter;
|
|
921
1001
|
this.registry = registry;
|
|
922
|
-
this.
|
|
1002
|
+
this.module = module;
|
|
923
1003
|
this.workerId = config.workerId ?? `typescript-worker-${process.pid}`;
|
|
924
1004
|
this.maxConcurrent = config.maxConcurrent ?? 10;
|
|
925
1005
|
this.handlerTimeoutMs = config.handlerTimeoutMs ?? 3e5;
|
|
@@ -956,8 +1036,8 @@ var StepExecutionSubscriber = class {
|
|
|
956
1036
|
pinoLog.info(
|
|
957
1037
|
{
|
|
958
1038
|
component: "subscriber",
|
|
959
|
-
eventId: payload.event.
|
|
960
|
-
stepUuid: payload.event.
|
|
1039
|
+
eventId: payload.event.eventId,
|
|
1040
|
+
stepUuid: payload.event.stepUuid
|
|
961
1041
|
},
|
|
962
1042
|
"Received step event in subscriber callback!"
|
|
963
1043
|
);
|
|
@@ -1057,7 +1137,7 @@ var StepExecutionSubscriber = class {
|
|
|
1057
1137
|
pinoLog.info(
|
|
1058
1138
|
{
|
|
1059
1139
|
component: "subscriber",
|
|
1060
|
-
eventId: event.
|
|
1140
|
+
eventId: event.eventId,
|
|
1061
1141
|
running: this.running,
|
|
1062
1142
|
activeHandlers: this.activeHandlers,
|
|
1063
1143
|
maxConcurrent: this.maxConcurrent
|
|
@@ -1066,7 +1146,7 @@ var StepExecutionSubscriber = class {
|
|
|
1066
1146
|
);
|
|
1067
1147
|
if (!this.running) {
|
|
1068
1148
|
pinoLog.warn(
|
|
1069
|
-
{ component: "subscriber", eventId: event.
|
|
1149
|
+
{ component: "subscriber", eventId: event.eventId },
|
|
1070
1150
|
"Received event while stopped, ignoring"
|
|
1071
1151
|
);
|
|
1072
1152
|
return;
|
|
@@ -1083,14 +1163,14 @@ var StepExecutionSubscriber = class {
|
|
|
1083
1163
|
return;
|
|
1084
1164
|
}
|
|
1085
1165
|
pinoLog.info(
|
|
1086
|
-
{ component: "subscriber", eventId: event.
|
|
1166
|
+
{ component: "subscriber", eventId: event.eventId },
|
|
1087
1167
|
"About to call processEvent()"
|
|
1088
1168
|
);
|
|
1089
1169
|
this.processEvent(event).catch((error) => {
|
|
1090
1170
|
pinoLog.error(
|
|
1091
1171
|
{
|
|
1092
1172
|
component: "subscriber",
|
|
1093
|
-
eventId: event.
|
|
1173
|
+
eventId: event.eventId,
|
|
1094
1174
|
error: error instanceof Error ? error.message : String(error),
|
|
1095
1175
|
stack: error instanceof Error ? error.stack : void 0
|
|
1096
1176
|
},
|
|
@@ -1100,37 +1180,65 @@ var StepExecutionSubscriber = class {
|
|
|
1100
1180
|
}
|
|
1101
1181
|
/**
|
|
1102
1182
|
* Process a step execution event.
|
|
1183
|
+
*
|
|
1184
|
+
* All paths produce a StepHandlerResult — the handler's result or a
|
|
1185
|
+
* system-level failure. This mirrors Python's pattern where system errors
|
|
1186
|
+
* (handler not found, timeout, uncaught exception) become
|
|
1187
|
+
* StepHandlerResult.failure() with appropriate error_type and retryable.
|
|
1103
1188
|
*/
|
|
1104
1189
|
async processEvent(event) {
|
|
1105
|
-
pinoLog.info({ component: "subscriber", eventId: event.
|
|
1190
|
+
pinoLog.info({ component: "subscriber", eventId: event.eventId }, "processEvent() starting");
|
|
1106
1191
|
this.activeHandlers++;
|
|
1107
1192
|
const startTime = Date.now();
|
|
1193
|
+
const { handlerResult, handlerName, handlerWasInvoked } = await this.resolveAndExecuteHandler(event);
|
|
1194
|
+
const executionTimeMs = Date.now() - startTime;
|
|
1195
|
+
await this.submitResult(event, handlerResult, executionTimeMs);
|
|
1196
|
+
this.emitCompletionEvents(
|
|
1197
|
+
event,
|
|
1198
|
+
handlerResult,
|
|
1199
|
+
handlerName,
|
|
1200
|
+
executionTimeMs,
|
|
1201
|
+
handlerWasInvoked
|
|
1202
|
+
);
|
|
1203
|
+
this.activeHandlers--;
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Resolve handler from registry and execute it, returning a StepHandlerResult.
|
|
1207
|
+
*
|
|
1208
|
+
* System-level failures (no handler name, handler not found, uncaught exception)
|
|
1209
|
+
* are converted to StepHandlerResult.failure() with appropriate error_type and
|
|
1210
|
+
* retryable — the caller always gets a result, never an exception.
|
|
1211
|
+
*/
|
|
1212
|
+
async resolveAndExecuteHandler(event) {
|
|
1213
|
+
let handlerName = null;
|
|
1108
1214
|
try {
|
|
1109
|
-
|
|
1215
|
+
handlerName = this.extractHandlerName(event);
|
|
1110
1216
|
pinoLog.info(
|
|
1111
|
-
{ component: "subscriber", eventId: event.
|
|
1217
|
+
{ component: "subscriber", eventId: event.eventId, handlerName },
|
|
1112
1218
|
"Extracted handler name"
|
|
1113
1219
|
);
|
|
1114
1220
|
if (!handlerName) {
|
|
1115
1221
|
pinoLog.error(
|
|
1116
|
-
{ component: "subscriber", eventId: event.
|
|
1222
|
+
{ component: "subscriber", eventId: event.eventId },
|
|
1117
1223
|
"No handler name found!"
|
|
1118
1224
|
);
|
|
1119
|
-
|
|
1120
|
-
|
|
1225
|
+
return {
|
|
1226
|
+
handlerResult: StepHandlerResult.failure(
|
|
1227
|
+
"No handler name found in step definition",
|
|
1228
|
+
"permanent_error" /* PERMANENT_ERROR */,
|
|
1229
|
+
false
|
|
1230
|
+
),
|
|
1231
|
+
handlerName,
|
|
1232
|
+
handlerWasInvoked: false
|
|
1233
|
+
};
|
|
1121
1234
|
}
|
|
1122
1235
|
pinoLog.info(
|
|
1123
|
-
{
|
|
1124
|
-
component: "subscriber",
|
|
1125
|
-
eventId: event.event_id,
|
|
1126
|
-
stepUuid: event.step_uuid,
|
|
1127
|
-
handlerName
|
|
1128
|
-
},
|
|
1236
|
+
{ component: "subscriber", eventId: event.eventId, stepUuid: event.stepUuid, handlerName },
|
|
1129
1237
|
"Processing step event"
|
|
1130
1238
|
);
|
|
1131
1239
|
this.emitter.emit(StepEventNames.STEP_EXECUTION_STARTED, {
|
|
1132
|
-
eventId: event.
|
|
1133
|
-
stepUuid: event.
|
|
1240
|
+
eventId: event.eventId,
|
|
1241
|
+
stepUuid: event.stepUuid,
|
|
1134
1242
|
handlerName,
|
|
1135
1243
|
timestamp: /* @__PURE__ */ new Date()
|
|
1136
1244
|
});
|
|
@@ -1142,64 +1250,80 @@ var StepExecutionSubscriber = class {
|
|
|
1142
1250
|
);
|
|
1143
1251
|
if (!handler) {
|
|
1144
1252
|
pinoLog.error({ component: "subscriber", handlerName }, "Handler not found in registry!");
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
{ component: "subscriber", handlerName },
|
|
1152
|
-
"StepContext created, executing handler"
|
|
1153
|
-
);
|
|
1154
|
-
const result = await this.executeWithTimeout(
|
|
1155
|
-
() => handler.call(context),
|
|
1156
|
-
this.handlerTimeoutMs
|
|
1157
|
-
);
|
|
1158
|
-
pinoLog.info(
|
|
1159
|
-
{ component: "subscriber", handlerName, success: result.success },
|
|
1160
|
-
"Handler execution completed"
|
|
1161
|
-
);
|
|
1162
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1163
|
-
await this.submitResult(event, result, executionTimeMs);
|
|
1164
|
-
if (result.success) {
|
|
1165
|
-
this.emitter.emit(StepEventNames.STEP_EXECUTION_COMPLETED, {
|
|
1166
|
-
eventId: event.event_id,
|
|
1167
|
-
stepUuid: event.step_uuid,
|
|
1253
|
+
return {
|
|
1254
|
+
handlerResult: StepHandlerResult.failure(
|
|
1255
|
+
`Handler not found: ${handlerName}`,
|
|
1256
|
+
"handler_not_found",
|
|
1257
|
+
false
|
|
1258
|
+
),
|
|
1168
1259
|
handlerName,
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
});
|
|
1172
|
-
} else {
|
|
1173
|
-
this.emitter.emit(StepEventNames.STEP_EXECUTION_FAILED, {
|
|
1174
|
-
eventId: event.event_id,
|
|
1175
|
-
stepUuid: event.step_uuid,
|
|
1176
|
-
handlerName,
|
|
1177
|
-
error: result.errorMessage,
|
|
1178
|
-
executionTimeMs,
|
|
1179
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1180
|
-
});
|
|
1260
|
+
handlerWasInvoked: false
|
|
1261
|
+
};
|
|
1181
1262
|
}
|
|
1182
|
-
this.
|
|
1263
|
+
return await this.invokeHandler(event, handler, handlerName);
|
|
1183
1264
|
} catch (error) {
|
|
1184
|
-
this.errorCount++;
|
|
1185
1265
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1266
|
+
const errorTypeName = error instanceof Error ? error.constructor.name : "Error";
|
|
1186
1267
|
logError("Handler execution failed", {
|
|
1187
1268
|
component: "subscriber",
|
|
1188
|
-
event_id: event.
|
|
1189
|
-
step_uuid: event.
|
|
1269
|
+
event_id: event.eventId,
|
|
1270
|
+
step_uuid: event.stepUuid,
|
|
1190
1271
|
error_message: errorMessage
|
|
1191
1272
|
});
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1273
|
+
return {
|
|
1274
|
+
handlerResult: StepHandlerResult.failure(errorMessage, errorTypeName, true, {
|
|
1275
|
+
traceback: error instanceof Error ? error.stack : void 0
|
|
1276
|
+
}),
|
|
1277
|
+
handlerName,
|
|
1278
|
+
handlerWasInvoked: false
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Create context and invoke the handler, returning its result.
|
|
1284
|
+
*
|
|
1285
|
+
* handlerWasInvoked is true only if the handler returned (not threw).
|
|
1286
|
+
*/
|
|
1287
|
+
async invokeHandler(event, handler, handlerName) {
|
|
1288
|
+
pinoLog.info({ component: "subscriber", handlerName }, "Creating StepContext from FFI event");
|
|
1289
|
+
const context = StepContext.fromFfiEvent(event, handlerName);
|
|
1290
|
+
pinoLog.info(
|
|
1291
|
+
{ component: "subscriber", handlerName },
|
|
1292
|
+
"StepContext created, executing handler"
|
|
1293
|
+
);
|
|
1294
|
+
const handlerResult = await this.executeWithTimeout(
|
|
1295
|
+
() => handler.call(context),
|
|
1296
|
+
this.handlerTimeoutMs
|
|
1297
|
+
);
|
|
1298
|
+
pinoLog.info(
|
|
1299
|
+
{ component: "subscriber", handlerName, success: handlerResult.success },
|
|
1300
|
+
"Handler execution completed"
|
|
1301
|
+
);
|
|
1302
|
+
return { handlerResult, handlerName, handlerWasInvoked: true };
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Update counters and emit observability events after step completion.
|
|
1306
|
+
*
|
|
1307
|
+
* A handler returning failure() is still "processed" — the handler ran and
|
|
1308
|
+
* gave a definitive answer. Only system-level errors (handler not found,
|
|
1309
|
+
* timeout, uncaught exception) count as "errors".
|
|
1310
|
+
*/
|
|
1311
|
+
emitCompletionEvents(event, handlerResult, handlerName, executionTimeMs, handlerWasInvoked) {
|
|
1312
|
+
const name = handlerName ?? "unknown";
|
|
1313
|
+
if (handlerResult.success || handlerWasInvoked) {
|
|
1314
|
+
this.processedCount++;
|
|
1315
|
+
} else {
|
|
1316
|
+
this.errorCount++;
|
|
1202
1317
|
}
|
|
1318
|
+
const eventName = handlerResult.success ? StepEventNames.STEP_EXECUTION_COMPLETED : StepEventNames.STEP_EXECUTION_FAILED;
|
|
1319
|
+
this.emitter.emit(eventName, {
|
|
1320
|
+
eventId: event.eventId,
|
|
1321
|
+
stepUuid: event.stepUuid,
|
|
1322
|
+
handlerName: name,
|
|
1323
|
+
...handlerResult.success ? {} : { error: handlerResult.errorMessage },
|
|
1324
|
+
executionTimeMs,
|
|
1325
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
1326
|
+
});
|
|
1203
1327
|
}
|
|
1204
1328
|
/**
|
|
1205
1329
|
* Execute a function with a timeout.
|
|
@@ -1218,18 +1342,10 @@ var StepExecutionSubscriber = class {
|
|
|
1218
1342
|
/**
|
|
1219
1343
|
* Extract handler name from FFI event.
|
|
1220
1344
|
*
|
|
1221
|
-
*
|
|
1345
|
+
* TAS-290: With napi-rs, handler callable is flattened to stepDefinition.handlerCallable
|
|
1222
1346
|
*/
|
|
1223
1347
|
extractHandlerName(event) {
|
|
1224
|
-
|
|
1225
|
-
if (!stepDefinition) {
|
|
1226
|
-
return null;
|
|
1227
|
-
}
|
|
1228
|
-
const handler = stepDefinition.handler;
|
|
1229
|
-
if (!handler) {
|
|
1230
|
-
return null;
|
|
1231
|
-
}
|
|
1232
|
-
return handler.callable || null;
|
|
1348
|
+
return event.stepDefinition?.handlerCallable || null;
|
|
1233
1349
|
}
|
|
1234
1350
|
/**
|
|
1235
1351
|
* Submit a handler result via FFI.
|
|
@@ -1238,23 +1354,13 @@ var StepExecutionSubscriber = class {
|
|
|
1238
1354
|
* instead of the normal completion path.
|
|
1239
1355
|
*/
|
|
1240
1356
|
async submitResult(event, result, executionTimeMs) {
|
|
1241
|
-
pinoLog.info(
|
|
1242
|
-
{ component: "subscriber", eventId: event.event_id, runtimeLoaded: this.runtime.isLoaded },
|
|
1243
|
-
"submitResult() called"
|
|
1244
|
-
);
|
|
1245
|
-
if (!this.runtime.isLoaded) {
|
|
1246
|
-
pinoLog.error(
|
|
1247
|
-
{ component: "subscriber", eventId: event.event_id },
|
|
1248
|
-
"Cannot submit result: runtime not loaded!"
|
|
1249
|
-
);
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1357
|
+
pinoLog.info({ component: "subscriber", eventId: event.eventId }, "submitResult() called");
|
|
1252
1358
|
if (result.metadata?.checkpoint_yield === true) {
|
|
1253
1359
|
await this.submitCheckpointYield(event, result);
|
|
1254
1360
|
return;
|
|
1255
1361
|
}
|
|
1256
|
-
const
|
|
1257
|
-
await this.sendCompletionViaFfi(event,
|
|
1362
|
+
const serdeResult = this.buildStepExecutionResult(event, result, executionTimeMs);
|
|
1363
|
+
await this.sendCompletionViaFfi(event, serdeResult, result.success);
|
|
1258
1364
|
}
|
|
1259
1365
|
/**
|
|
1260
1366
|
* TAS-125: Submit a checkpoint yield via FFI.
|
|
@@ -1264,165 +1370,139 @@ var StepExecutionSubscriber = class {
|
|
|
1264
1370
|
*/
|
|
1265
1371
|
async submitCheckpointYield(event, result) {
|
|
1266
1372
|
pinoLog.info(
|
|
1267
|
-
{ component: "subscriber", eventId: event.
|
|
1373
|
+
{ component: "subscriber", eventId: event.eventId },
|
|
1268
1374
|
"submitCheckpointYield() called - handler yielded checkpoint"
|
|
1269
1375
|
);
|
|
1270
1376
|
const resultData = result.result ?? {};
|
|
1271
1377
|
const checkpointData = {
|
|
1272
|
-
|
|
1378
|
+
stepUuid: event.stepUuid,
|
|
1273
1379
|
cursor: resultData.cursor ?? 0,
|
|
1274
|
-
|
|
1380
|
+
itemsProcessed: resultData.items_processed ?? 0
|
|
1275
1381
|
};
|
|
1276
1382
|
const accumulatedResults = resultData.accumulated_results;
|
|
1277
1383
|
if (accumulatedResults !== void 0) {
|
|
1278
|
-
checkpointData.
|
|
1384
|
+
checkpointData.accumulatedResults = accumulatedResults;
|
|
1279
1385
|
}
|
|
1280
1386
|
try {
|
|
1281
|
-
const success = this.
|
|
1387
|
+
const success = this.module.checkpointYieldStepEvent(event.eventId, checkpointData);
|
|
1282
1388
|
if (success) {
|
|
1283
1389
|
pinoLog.info(
|
|
1284
1390
|
{
|
|
1285
1391
|
component: "subscriber",
|
|
1286
|
-
eventId: event.
|
|
1392
|
+
eventId: event.eventId,
|
|
1287
1393
|
cursor: checkpointData.cursor,
|
|
1288
|
-
itemsProcessed: checkpointData.
|
|
1394
|
+
itemsProcessed: checkpointData.itemsProcessed
|
|
1289
1395
|
},
|
|
1290
1396
|
"Checkpoint yield submitted successfully - step will be re-dispatched"
|
|
1291
1397
|
);
|
|
1292
1398
|
this.emitter.emit(StepEventNames.STEP_CHECKPOINT_YIELD_SENT, {
|
|
1293
|
-
eventId: event.
|
|
1294
|
-
stepUuid: event.
|
|
1399
|
+
eventId: event.eventId,
|
|
1400
|
+
stepUuid: event.stepUuid,
|
|
1295
1401
|
cursor: checkpointData.cursor,
|
|
1296
|
-
itemsProcessed: checkpointData.
|
|
1402
|
+
itemsProcessed: checkpointData.itemsProcessed,
|
|
1297
1403
|
timestamp: /* @__PURE__ */ new Date()
|
|
1298
1404
|
});
|
|
1299
1405
|
logInfo("Checkpoint yield submitted", {
|
|
1300
1406
|
component: "subscriber",
|
|
1301
|
-
event_id: event.
|
|
1302
|
-
step_uuid: event.
|
|
1407
|
+
event_id: event.eventId,
|
|
1408
|
+
step_uuid: event.stepUuid,
|
|
1303
1409
|
cursor: String(checkpointData.cursor),
|
|
1304
|
-
items_processed: String(checkpointData.
|
|
1410
|
+
items_processed: String(checkpointData.itemsProcessed)
|
|
1305
1411
|
});
|
|
1306
1412
|
} else {
|
|
1307
1413
|
pinoLog.error(
|
|
1308
|
-
{ component: "subscriber", eventId: event.
|
|
1414
|
+
{ component: "subscriber", eventId: event.eventId },
|
|
1309
1415
|
"Checkpoint yield rejected by Rust - event may not be in pending map"
|
|
1310
1416
|
);
|
|
1311
1417
|
logError("Checkpoint yield rejected", {
|
|
1312
1418
|
component: "subscriber",
|
|
1313
|
-
event_id: event.
|
|
1314
|
-
step_uuid: event.
|
|
1419
|
+
event_id: event.eventId,
|
|
1420
|
+
step_uuid: event.stepUuid
|
|
1315
1421
|
});
|
|
1316
1422
|
}
|
|
1317
1423
|
} catch (error) {
|
|
1318
1424
|
pinoLog.error(
|
|
1319
1425
|
{
|
|
1320
1426
|
component: "subscriber",
|
|
1321
|
-
eventId: event.
|
|
1427
|
+
eventId: event.eventId,
|
|
1322
1428
|
error: error instanceof Error ? error.message : String(error)
|
|
1323
1429
|
},
|
|
1324
1430
|
"Checkpoint yield failed with error"
|
|
1325
1431
|
);
|
|
1326
1432
|
logError("Failed to submit checkpoint yield", {
|
|
1327
1433
|
component: "subscriber",
|
|
1328
|
-
event_id: event.
|
|
1434
|
+
event_id: event.eventId,
|
|
1329
1435
|
error_message: error instanceof Error ? error.message : String(error)
|
|
1330
1436
|
});
|
|
1331
1437
|
}
|
|
1332
1438
|
}
|
|
1333
1439
|
/**
|
|
1334
|
-
*
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
event_id: event.event_id
|
|
1341
|
-
});
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1344
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1345
|
-
const executionResult = this.buildErrorExecutionResult(event, errorMessage, executionTimeMs);
|
|
1346
|
-
const accepted = await this.sendCompletionViaFfi(event, executionResult, false);
|
|
1347
|
-
if (accepted) {
|
|
1348
|
-
this.errorCount++;
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Build a StepExecutionResult from a handler result.
|
|
1440
|
+
* Build a NapiStepExecutionResult from a StepHandlerResult.
|
|
1441
|
+
*
|
|
1442
|
+
* The subscriber's only job here is to WRAP the handler result with
|
|
1443
|
+
* execution metadata (timing, worker_id, step_uuid). All handler decisions
|
|
1444
|
+
* (success, retryable, errorType, errorCode, orchestrationMetadata) are
|
|
1445
|
+
* passed through faithfully — the subscriber does not re-interpret them.
|
|
1353
1446
|
*
|
|
1354
|
-
*
|
|
1447
|
+
* This mirrors Python's _submit_result() which calls
|
|
1448
|
+
* StepExecutionResult.success_result() or .failure_result() passing
|
|
1449
|
+
* handler fields straight through.
|
|
1450
|
+
*
|
|
1451
|
+
* napi-rs #[napi(object)] maps Option<T> to `?: T`. With
|
|
1452
|
+
* exactOptionalPropertyTypes, optional props must be OMITTED (not null
|
|
1453
|
+
* or undefined) — hence the conditional spread pattern.
|
|
1355
1454
|
*/
|
|
1356
|
-
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1455
|
+
buildStepExecutionResult(event, result, executionTimeMs) {
|
|
1456
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1457
|
+
const metadata = {
|
|
1458
|
+
executionTimeMs,
|
|
1459
|
+
completedAt: now,
|
|
1460
|
+
...this.workerId != null && { workerId: this.workerId },
|
|
1461
|
+
...Object.keys(result.metadata).length > 0 && { custom: result.metadata },
|
|
1462
|
+
// Pass through handler's classification — subscriber doesn't interpret these
|
|
1463
|
+
...result.retryable != null && { retryable: result.retryable },
|
|
1464
|
+
...result.errorType != null && { errorType: result.errorType },
|
|
1465
|
+
...result.errorCode != null && { errorCode: result.errorCode }
|
|
1466
|
+
};
|
|
1467
|
+
const napiResult = {
|
|
1468
|
+
stepUuid: event.stepUuid,
|
|
1359
1469
|
success: result.success,
|
|
1360
1470
|
result: result.result ?? {},
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
worker_id: this.workerId,
|
|
1364
|
-
handler_name: this.extractHandlerName(event) ?? "unknown",
|
|
1365
|
-
attempt_number: event.workflow_step?.attempts ?? 1,
|
|
1366
|
-
retryable: result.retryable ?? false,
|
|
1367
|
-
...result.metadata
|
|
1368
|
-
},
|
|
1369
|
-
status: result.success ? "completed" : "failed"
|
|
1471
|
+
status: result.success ? "completed" : "failed",
|
|
1472
|
+
metadata
|
|
1370
1473
|
};
|
|
1371
1474
|
if (!result.success) {
|
|
1372
|
-
|
|
1475
|
+
napiResult.error = {
|
|
1373
1476
|
message: result.errorMessage ?? "Unknown error",
|
|
1374
|
-
|
|
1375
|
-
retryable: result.retryable
|
|
1376
|
-
status_code: null,
|
|
1377
|
-
backtrace: null
|
|
1477
|
+
...result.errorType != null && { errorType: result.errorType },
|
|
1478
|
+
...result.retryable != null && { retryable: result.retryable }
|
|
1378
1479
|
};
|
|
1379
1480
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
*
|
|
1385
|
-
* IMPORTANT: metadata.retryable must be set for Rust's is_retryable() to work correctly.
|
|
1386
|
-
*/
|
|
1387
|
-
buildErrorExecutionResult(event, errorMessage, executionTimeMs) {
|
|
1388
|
-
return {
|
|
1389
|
-
step_uuid: event.step_uuid,
|
|
1390
|
-
success: false,
|
|
1391
|
-
result: {},
|
|
1392
|
-
metadata: {
|
|
1393
|
-
execution_time_ms: executionTimeMs,
|
|
1394
|
-
worker_id: this.workerId,
|
|
1395
|
-
retryable: true
|
|
1396
|
-
},
|
|
1397
|
-
status: "error",
|
|
1398
|
-
error: {
|
|
1399
|
-
message: errorMessage,
|
|
1400
|
-
error_type: "handler_error",
|
|
1401
|
-
retryable: true,
|
|
1402
|
-
status_code: null,
|
|
1403
|
-
backtrace: null
|
|
1404
|
-
}
|
|
1405
|
-
};
|
|
1481
|
+
if (result.orchestrationMetadata != null) {
|
|
1482
|
+
napiResult.orchestrationMetadata = result.orchestrationMetadata;
|
|
1483
|
+
}
|
|
1484
|
+
return napiResult;
|
|
1406
1485
|
}
|
|
1407
1486
|
/**
|
|
1408
1487
|
* Send a completion result to Rust via FFI and handle the response.
|
|
1409
1488
|
*
|
|
1410
1489
|
* @returns true if the completion was accepted by Rust, false otherwise
|
|
1411
1490
|
*/
|
|
1412
|
-
async sendCompletionViaFfi(event,
|
|
1491
|
+
async sendCompletionViaFfi(event, napiResult, isSuccess) {
|
|
1413
1492
|
pinoLog.info(
|
|
1414
1493
|
{
|
|
1415
1494
|
component: "subscriber",
|
|
1416
|
-
eventId: event.
|
|
1417
|
-
stepUuid: event.
|
|
1418
|
-
|
|
1495
|
+
eventId: event.eventId,
|
|
1496
|
+
stepUuid: event.stepUuid,
|
|
1497
|
+
success: napiResult.success,
|
|
1498
|
+
status: napiResult.status
|
|
1419
1499
|
},
|
|
1420
|
-
"About to call
|
|
1500
|
+
"About to call module.completeStepEvent()"
|
|
1421
1501
|
);
|
|
1422
1502
|
try {
|
|
1423
|
-
const ffiResult = this.
|
|
1503
|
+
const ffiResult = this.module.completeStepEvent(event.eventId, napiResult);
|
|
1424
1504
|
if (ffiResult) {
|
|
1425
|
-
this.handleFfiSuccess(event,
|
|
1505
|
+
this.handleFfiSuccess(event, isSuccess);
|
|
1426
1506
|
return true;
|
|
1427
1507
|
}
|
|
1428
1508
|
this.handleFfiRejection(event);
|
|
@@ -1435,23 +1515,22 @@ var StepExecutionSubscriber = class {
|
|
|
1435
1515
|
/**
|
|
1436
1516
|
* Handle successful FFI completion submission.
|
|
1437
1517
|
*/
|
|
1438
|
-
handleFfiSuccess(event,
|
|
1518
|
+
handleFfiSuccess(event, isSuccess) {
|
|
1439
1519
|
pinoLog.info(
|
|
1440
|
-
{ component: "subscriber", eventId: event.
|
|
1520
|
+
{ component: "subscriber", eventId: event.eventId, success: isSuccess },
|
|
1441
1521
|
"completeStepEvent() returned TRUE - completion accepted by Rust"
|
|
1442
1522
|
);
|
|
1443
1523
|
this.emitter.emit(StepEventNames.STEP_COMPLETION_SENT, {
|
|
1444
|
-
eventId: event.
|
|
1445
|
-
stepUuid: event.
|
|
1524
|
+
eventId: event.eventId,
|
|
1525
|
+
stepUuid: event.stepUuid,
|
|
1446
1526
|
success: isSuccess,
|
|
1447
1527
|
timestamp: /* @__PURE__ */ new Date()
|
|
1448
1528
|
});
|
|
1449
1529
|
logDebug("Step result submitted", {
|
|
1450
1530
|
component: "subscriber",
|
|
1451
|
-
event_id: event.
|
|
1452
|
-
step_uuid: event.
|
|
1453
|
-
success: String(isSuccess)
|
|
1454
|
-
execution_time_ms: String(executionResult.metadata.execution_time_ms)
|
|
1531
|
+
event_id: event.eventId,
|
|
1532
|
+
step_uuid: event.stepUuid,
|
|
1533
|
+
success: String(isSuccess)
|
|
1455
1534
|
});
|
|
1456
1535
|
}
|
|
1457
1536
|
/**
|
|
@@ -1461,15 +1540,15 @@ var StepExecutionSubscriber = class {
|
|
|
1461
1540
|
pinoLog.error(
|
|
1462
1541
|
{
|
|
1463
1542
|
component: "subscriber",
|
|
1464
|
-
eventId: event.
|
|
1465
|
-
stepUuid: event.
|
|
1543
|
+
eventId: event.eventId,
|
|
1544
|
+
stepUuid: event.stepUuid
|
|
1466
1545
|
},
|
|
1467
1546
|
"completeStepEvent() returned FALSE - completion REJECTED by Rust! Event may not be in pending map."
|
|
1468
1547
|
);
|
|
1469
1548
|
logError("FFI completion rejected", {
|
|
1470
1549
|
component: "subscriber",
|
|
1471
|
-
event_id: event.
|
|
1472
|
-
step_uuid: event.
|
|
1550
|
+
event_id: event.eventId,
|
|
1551
|
+
step_uuid: event.stepUuid
|
|
1473
1552
|
});
|
|
1474
1553
|
}
|
|
1475
1554
|
/**
|
|
@@ -1479,7 +1558,7 @@ var StepExecutionSubscriber = class {
|
|
|
1479
1558
|
pinoLog.error(
|
|
1480
1559
|
{
|
|
1481
1560
|
component: "subscriber",
|
|
1482
|
-
eventId: event.
|
|
1561
|
+
eventId: event.eventId,
|
|
1483
1562
|
error: error instanceof Error ? error.message : String(error),
|
|
1484
1563
|
stack: error instanceof Error ? error.stack : void 0
|
|
1485
1564
|
},
|
|
@@ -1487,7 +1566,7 @@ var StepExecutionSubscriber = class {
|
|
|
1487
1566
|
);
|
|
1488
1567
|
logError("Failed to submit step result", {
|
|
1489
1568
|
component: "subscriber",
|
|
1490
|
-
event_id: event.
|
|
1569
|
+
event_id: event.eventId,
|
|
1491
1570
|
error_message: error instanceof Error ? error.message : String(error)
|
|
1492
1571
|
});
|
|
1493
1572
|
}
|
|
@@ -1513,17 +1592,17 @@ var EventSystem = class {
|
|
|
1513
1592
|
/**
|
|
1514
1593
|
* Create a new EventSystem.
|
|
1515
1594
|
*
|
|
1516
|
-
* @param
|
|
1595
|
+
* @param module - The napi-rs module for polling events and submitting results
|
|
1517
1596
|
* @param registry - The handler registry for resolving step handlers
|
|
1518
1597
|
* @param config - Optional configuration for poller and subscriber
|
|
1519
1598
|
*/
|
|
1520
|
-
constructor(
|
|
1599
|
+
constructor(module, registry, config = {}) {
|
|
1521
1600
|
this.emitter = new TaskerEventEmitter();
|
|
1522
|
-
this.poller = new EventPoller(
|
|
1601
|
+
this.poller = new EventPoller(module, this.emitter, config.poller);
|
|
1523
1602
|
this.subscriber = new StepExecutionSubscriber(
|
|
1524
1603
|
this.emitter,
|
|
1525
1604
|
registry,
|
|
1526
|
-
|
|
1605
|
+
module,
|
|
1527
1606
|
config.subscriber
|
|
1528
1607
|
);
|
|
1529
1608
|
}
|
|
@@ -1553,11 +1632,11 @@ var EventSystem = class {
|
|
|
1553
1632
|
{
|
|
1554
1633
|
component: "event-system",
|
|
1555
1634
|
debugListener: true,
|
|
1556
|
-
eventId: payload.event?.
|
|
1557
|
-
stepUuid: payload.event?.
|
|
1635
|
+
eventId: payload.event?.eventId,
|
|
1636
|
+
stepUuid: payload.event?.stepUuid,
|
|
1558
1637
|
eventName: StepEventNames.STEP_EXECUTION_RECEIVED
|
|
1559
1638
|
},
|
|
1560
|
-
|
|
1639
|
+
`DEBUG LISTENER: Received ${StepEventNames.STEP_EXECUTION_RECEIVED} event!`
|
|
1561
1640
|
);
|
|
1562
1641
|
}
|
|
1563
1642
|
);
|