@saga-a2ui/core 0.5.6-rc.1 → 0.7.6-rc.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.
@@ -1,4 +1,8 @@
1
- import type { ActionPlan, CanceledContext, FailureContext } from './definition.js';
1
+ import type {
2
+ ActionPlan,
3
+ CanceledContext,
4
+ FailureContext,
5
+ } from './definition.js';
2
6
  import type {
3
7
  ActionObserver,
4
8
  CancelCause,
@@ -13,7 +17,12 @@ import { categorize } from './category.js';
13
17
  import { startTimer, type LimitTimer, type ResolvedLimits } from './limits.js';
14
18
  import { readAnswer, type Prerequisite } from './prerequisite.js';
15
19
  import { InvocationResources } from './scope.js';
16
- import { requestMeta, requestTimeout, type ServerCalls, type ServerTransport } from './server.js';
20
+ import {
21
+ requestMeta,
22
+ requestTimeout,
23
+ type ServerCalls,
24
+ type ServerTransport,
25
+ } from './server.js';
17
26
  import { discardOutcome, isThenable } from './thenable.js';
18
27
 
19
28
  // One activation that was admitted, from the user's event to its settlement. The runtime owns every
@@ -48,9 +57,18 @@ export interface InvocationOwner<Event> {
48
57
 
49
58
  let invocationCount = 0;
50
59
 
51
- type Settled = { readonly ok: true; readonly value: unknown } | { readonly ok: false; readonly error: unknown };
60
+ type Settled =
61
+ | { readonly ok: true; readonly value: unknown }
62
+ | { readonly ok: false; readonly error: unknown };
52
63
  type WorkStage = 'prerequisites' | 'running' | 'completing';
53
- type StepKind = 'prerequisite' | 'handler' | 'onSuccess' | 'onFailure' | 'onCanceled' | 'release' | 'onFinally';
64
+ type StepKind =
65
+ | 'prerequisite'
66
+ | 'handler'
67
+ | 'onSuccess'
68
+ | 'onFailure'
69
+ | 'onCanceled'
70
+ | 'release'
71
+ | 'onFinally';
54
72
 
55
73
  /** What telemetry needs to know about a definition. */
56
74
  export interface Observable {
@@ -68,7 +86,12 @@ export function emitObservation(
68
86
  const { observer } = plan;
69
87
  if (observer === undefined) return;
70
88
  try {
71
- const returned: unknown = observer({ label: plan.label, bindingId, elapsedMs: Date.now() - startedAt, ...event });
89
+ const returned: unknown = observer({
90
+ label: plan.label,
91
+ bindingId,
92
+ elapsedMs: Date.now() - startedAt,
93
+ ...event,
94
+ });
72
95
  if (isThenable(returned)) discardOutcome(returned);
73
96
  } catch {
74
97
  // An observer cannot affect the action.
@@ -136,7 +159,10 @@ export class Invocation<Event> {
136
159
  /** What `atActivation` returned; `undefined` for an action without it. */
137
160
  private activation: unknown;
138
161
  private readonly resources: InvocationResources;
139
- private provided: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
162
+ private provided: Record<string, unknown> = Object.create(null) as Record<
163
+ string,
164
+ unknown
165
+ >;
140
166
  private result: unknown;
141
167
  private stage: WorkStage = 'prerequisites';
142
168
  private outcome: Outcome<unknown> | undefined;
@@ -166,7 +192,8 @@ export class Invocation<Event> {
166
192
  this.resources = new InvocationResources({
167
193
  label: plan.label,
168
194
  // The scope is active until the outcome is decided: that is work activity, not freshness.
169
- isActive: () => this.outcome === undefined && this.pendingCancel === undefined,
195
+ isActive: () =>
196
+ this.outcome === undefined && this.pendingCancel === undefined,
170
197
  hookMs: () => this.limits.hookMs,
171
198
  report: (step) => this.emit('cleanup-error', { cleanupStep: step }),
172
199
  });
@@ -218,7 +245,8 @@ export class Invocation<Event> {
218
245
  if (this.replacedFlag) return 'replaced';
219
246
  if (this.owner.isDisposed()) return 'disposed';
220
247
  if (!this.live()) return 'not-live';
221
- if (this.seam.subject !== undefined && this.subjectChanged()) return 'subject-changed';
248
+ if (this.seam.subject !== undefined && this.subjectChanged())
249
+ return 'subject-changed';
222
250
  return undefined;
223
251
  }
224
252
 
@@ -246,7 +274,9 @@ export class Invocation<Event> {
246
274
  this.expireIfOverdue();
247
275
  if (!step.eligible) return false;
248
276
  this.recheck();
249
- return step.eligible && !this.obsoleteFlag && this.staleCause() === undefined;
277
+ return (
278
+ step.eligible && !this.obsoleteFlag && this.staleCause() === undefined
279
+ );
250
280
  }
251
281
 
252
282
  // -------------------------------------------------------------------------------------------
@@ -280,8 +310,10 @@ export class Invocation<Event> {
280
310
  const stage = this.stage;
281
311
  this.controller.abort();
282
312
  this.abandonStep();
283
- if (stage === 'completing') this.decide({ status: 'canceled', cause, stage, result: this.result });
284
- else if (stage === 'running') this.decide({ status: 'canceled', cause, stage });
313
+ if (stage === 'completing')
314
+ this.decide({ status: 'canceled', cause, stage, result: this.result });
315
+ else if (stage === 'running')
316
+ this.decide({ status: 'canceled', cause, stage });
285
317
  else this.decide({ status: 'canceled', cause, stage });
286
318
  }
287
319
 
@@ -318,7 +350,11 @@ export class Invocation<Event> {
318
350
  this.decide({
319
351
  status: 'failed',
320
352
  stage: 'input',
321
- failure: { kind: 'invalid-input', error, category: categorize(error, 'validation') },
353
+ failure: {
354
+ kind: 'invalid-input',
355
+ error,
356
+ category: categorize(error, 'validation'),
357
+ },
322
358
  });
323
359
  return false;
324
360
  }
@@ -364,7 +400,11 @@ export class Invocation<Event> {
364
400
  this.decide({
365
401
  status: 'failed',
366
402
  stage: 'input',
367
- failure: { kind: 'activation-error', error: threw.error, category: categorize(threw.error) },
403
+ failure: {
404
+ kind: 'activation-error',
405
+ error: threw.error,
406
+ category: categorize(threw.error),
407
+ },
368
408
  });
369
409
  } else {
370
410
  this.activation = returned;
@@ -373,7 +413,8 @@ export class Invocation<Event> {
373
413
  // If the callback threw, its failure is already the outcome and its cleanup may have started:
374
414
  // the cancellation had already made the invocation obsolete, and it must not abort or abandon
375
415
  // the step that belongs to that outcome.
376
- if (pending !== undefined && this.outcome === undefined) this.cancel(pending);
416
+ if (pending !== undefined && this.outcome === undefined)
417
+ this.cancel(pending);
377
418
  this.drain();
378
419
  return this.outcome === undefined;
379
420
  }
@@ -429,7 +470,11 @@ export class Invocation<Event> {
429
470
  );
430
471
  }
431
472
 
432
- private afterPrerequisite(current: Prerequisite<object, unknown, unknown>, index: number, settled: Settled): void {
473
+ private afterPrerequisite(
474
+ current: Prerequisite<object, unknown, unknown>,
475
+ index: number,
476
+ settled: Settled,
477
+ ): void {
433
478
  // A deadline that passed unnoticed ends the wait before this answer counts, and so does a change
434
479
  // nobody saw until now.
435
480
  this.expireIfOverdue();
@@ -437,32 +482,64 @@ export class Invocation<Event> {
437
482
  this.recheck();
438
483
  if (this.outcome !== undefined) return;
439
484
  const failure = (failed: PrerequisiteFailure): void => {
440
- this.decide({ status: 'failed', stage: 'prerequisites', failure: failed });
485
+ this.decide({
486
+ status: 'failed',
487
+ stage: 'prerequisites',
488
+ failure: failed,
489
+ });
441
490
  };
442
491
  const broken = (error: unknown): void =>
443
- failure({ kind: 'prerequisite-error', prerequisite: current.label, error, category: categorize(error) });
492
+ failure({
493
+ kind: 'prerequisite-error',
494
+ prerequisite: current.label,
495
+ error,
496
+ category: categorize(error),
497
+ });
444
498
  if (!settled.ok) return broken(settled.error);
445
499
  const answer = readAnswer(settled.value);
446
500
  // A refusal is the application's own answer. Its reason text is not evidence of why.
447
501
  if (answer.kind === 'refuse') {
448
- return failure({ kind: 'refused', prerequisite: current.label, reason: answer.reason, category: 'application' });
502
+ return failure({
503
+ kind: 'refused',
504
+ prerequisite: current.label,
505
+ reason: answer.reason,
506
+ category: 'application',
507
+ });
449
508
  }
450
509
  if (answer.kind === 'malformed') {
451
- return broken(new TypeError(`Prerequisite '${current.label}' must answer with proceed(...) or refuse(...).`));
510
+ return broken(
511
+ new TypeError(
512
+ `Prerequisite '${current.label}' must answer with proceed(...) or refuse(...).`,
513
+ ),
514
+ );
452
515
  }
453
516
  for (const key of Object.keys(answer.provides)) {
454
517
  if (key in this.provided) {
455
- return broken(new TypeError(`Prerequisite '${current.label}' provides '${key}', which an earlier prerequisite already provided.`));
518
+ return broken(
519
+ new TypeError(
520
+ `Prerequisite '${current.label}' provides '${key}', which an earlier prerequisite already provided.`,
521
+ ),
522
+ );
456
523
  }
457
524
  }
458
525
  for (const [key, value] of Object.entries(answer.provides)) {
459
- Object.defineProperty(this.provided, key, { value, enumerable: true, writable: false, configurable: false });
526
+ Object.defineProperty(this.provided, key, {
527
+ value,
528
+ enumerable: true,
529
+ writable: false,
530
+ configurable: false,
531
+ });
460
532
  }
461
533
  this.nextPrerequisite(index + 1);
462
534
  }
463
535
 
464
536
  private providedSnapshot(): Record<string, unknown> {
465
- return Object.freeze(Object.assign(Object.create(null) as Record<string, unknown>, this.provided));
537
+ return Object.freeze(
538
+ Object.assign(
539
+ Object.create(null) as Record<string, unknown>,
540
+ this.provided,
541
+ ),
542
+ );
466
543
  }
467
544
 
468
545
  private startHandler(): void {
@@ -482,7 +559,11 @@ export class Invocation<Event> {
482
559
  this.decide({
483
560
  status: 'failed',
484
561
  stage: 'running',
485
- failure: { kind: 'handler-error', error: settled.error, category: categorize(settled.error) },
562
+ failure: {
563
+ kind: 'handler-error',
564
+ error: settled.error,
565
+ category: categorize(settled.error),
566
+ },
486
567
  });
487
568
  return;
488
569
  }
@@ -500,7 +581,9 @@ export class Invocation<Event> {
500
581
  signal: this.controller.signal,
501
582
  scope: this.resources.invocation,
502
583
  current: () => this.currentFor(step),
503
- ...(this.plan.transport === undefined ? {} : { call: this.serverCall(step, this.plan.transport) }),
584
+ ...(this.plan.transport === undefined
585
+ ? {}
586
+ : { call: this.serverCall(step, this.plan.transport) }),
504
587
  }),
505
588
  );
506
589
  }
@@ -512,21 +595,41 @@ export class Invocation<Event> {
512
595
  * A rejection of the transport is the handler's to see, and arrives as `handler-error` unless
513
596
  * the deadline or a cancellation decided first.
514
597
  */
515
- private serverCall(step: Step, transport: ServerTransport): ServerCalls['call'] {
598
+ private serverCall(
599
+ step: Step,
600
+ transport: ServerTransport,
601
+ ): ServerCalls['call'] {
516
602
  const { label } = this.plan;
517
603
  return async (name, args, options) => {
518
604
  // Reading the options first makes a malformed request fail the same way at any time.
519
- const { timeoutMs: asked, meta: suppliedMeta } = (options ?? {}) as { timeoutMs?: unknown; meta?: unknown };
520
- if (typeof name !== 'string' || name === '') throw new TypeError(`Action '${label}': a server call needs a tool name.`);
605
+ const { timeoutMs: asked, meta: suppliedMeta } = (options ?? {}) as {
606
+ timeoutMs?: unknown;
607
+ meta?: unknown;
608
+ };
609
+ if (typeof name !== 'string' || name === '')
610
+ throw new TypeError(
611
+ `Action '${label}': a server call needs a tool name.`,
612
+ );
521
613
  this.recheck();
522
- const remainingMs = this.limits.deadlineMs - (Date.now() - this.workStartedAt);
523
- if (!step.eligible || this.controller.signal.aborted || remainingMs <= 0) {
524
- throw new Error(`Action '${label}': the invocation stopped waiting for this handler, so the request was not sent.`);
614
+ const remainingMs =
615
+ this.limits.deadlineMs - (Date.now() - this.workStartedAt);
616
+ if (
617
+ !step.eligible ||
618
+ this.controller.signal.aborted ||
619
+ remainingMs <= 0
620
+ ) {
621
+ throw new Error(
622
+ `Action '${label}': the invocation stopped waiting for this handler, so the request was not sent.`,
623
+ );
525
624
  }
526
625
  const timeout = requestTimeout(label, remainingMs, asked);
527
626
  const meta = requestMeta(label, suppliedMeta);
528
627
  return transport.callServerTool(
529
- { name, arguments: args, ...(meta === undefined ? {} : { _meta: meta }) },
628
+ {
629
+ name,
630
+ arguments: args,
631
+ ...(meta === undefined ? {} : { _meta: meta }),
632
+ },
530
633
  { signal: this.controller.signal, timeout },
531
634
  );
532
635
  };
@@ -535,7 +638,10 @@ export class Invocation<Event> {
535
638
  private armDeadline(): void {
536
639
  if (this.deadlineTimer !== undefined || this.outcome !== undefined) return;
537
640
  // Counted from the end of the activation prefix, so progress and a later wait never extend it.
538
- const remaining = Math.max(0, this.limits.deadlineMs - (Date.now() - this.workStartedAt));
641
+ const remaining = Math.max(
642
+ 0,
643
+ this.limits.deadlineMs - (Date.now() - this.workStartedAt),
644
+ );
539
645
  this.deadlineTimer = startTimer(remaining, () => this.onDeadline());
540
646
  }
541
647
 
@@ -552,7 +658,8 @@ export class Invocation<Event> {
552
658
  */
553
659
  private expireIfOverdue(): void {
554
660
  if (this.deadlineTimer === undefined || this.outcome !== undefined) return;
555
- if (Date.now() - this.workStartedAt >= this.limits.deadlineMs) this.onDeadline();
661
+ if (Date.now() - this.workStartedAt >= this.limits.deadlineMs)
662
+ this.onDeadline();
556
663
  }
557
664
 
558
665
  private onDeadline(): void {
@@ -565,8 +672,16 @@ export class Invocation<Event> {
565
672
  this.controller.abort();
566
673
  this.decide(
567
674
  stage === 'prerequisites'
568
- ? { status: 'failed', stage, failure: { kind: 'timeout', category: 'timeout' } }
569
- : { status: 'failed', stage, failure: { kind: 'timeout', category: 'timeout' } },
675
+ ? {
676
+ status: 'failed',
677
+ stage,
678
+ failure: { kind: 'timeout', category: 'timeout' },
679
+ }
680
+ : {
681
+ status: 'failed',
682
+ stage,
683
+ failure: { kind: 'timeout', category: 'timeout' },
684
+ },
570
685
  );
571
686
  }
572
687
 
@@ -602,11 +717,24 @@ export class Invocation<Event> {
602
717
  this.decide({
603
718
  status: 'failed',
604
719
  stage: 'running',
605
- failure: { kind: 'success-hook-error', error: settled.error, result, category: categorize(settled.error) },
720
+ failure: {
721
+ kind: 'success-hook-error',
722
+ error: settled.error,
723
+ result,
724
+ category: categorize(settled.error),
725
+ },
606
726
  });
607
727
  },
608
728
  expire: () =>
609
- this.decide({ status: 'failed', stage: 'running', failure: { kind: 'success-hook-timeout', result, category: 'timeout' } }),
729
+ this.decide({
730
+ status: 'failed',
731
+ stage: 'running',
732
+ failure: {
733
+ kind: 'success-hook-timeout',
734
+ result,
735
+ category: 'timeout',
736
+ },
737
+ }),
610
738
  },
611
739
  );
612
740
  }
@@ -620,13 +748,19 @@ export class Invocation<Event> {
620
748
  this.abandonStep();
621
749
  this.clearDeadline();
622
750
  this.outcome = outcome;
623
- if (this.activating && outcome.status === 'failed' && this.plan.hooks.onFailure === undefined) {
751
+ if (
752
+ this.activating &&
753
+ outcome.status === 'failed' &&
754
+ this.plan.hooks.onFailure === undefined
755
+ ) {
624
756
  const { failure } = outcome;
625
757
  if ('error' in failure) this.rethrow = { error: failure.error };
626
758
  }
627
759
  this.emit('outcome-decided', {
628
760
  status: outcome.status,
629
- ...(outcome.status === 'failed' ? { failureKind: outcome.failure.kind } : {}),
761
+ ...(outcome.status === 'failed'
762
+ ? { failureKind: outcome.failure.kind }
763
+ : {}),
630
764
  });
631
765
  this.owner.decided(this);
632
766
  this.later(() => this.runOutcomeHook());
@@ -634,14 +768,19 @@ export class Invocation<Event> {
634
768
 
635
769
  private runOutcomeHook(): void {
636
770
  const outcome = this.outcome;
637
- if (outcome === undefined || outcome.status === 'succeeded') return this.releaseHeld();
771
+ if (outcome === undefined || outcome.status === 'succeeded')
772
+ return this.releaseHeld();
638
773
  if (outcome.status === 'canceled') {
639
774
  const hook = this.plan.hooks.onCanceled;
640
775
  if (hook === undefined) return this.releaseHeld();
641
- this.runHook('onCanceled', (step) => hook(this.canceledContext(step, outcome)), {
642
- onResult: (settled) => this.afterCleanup('onCanceled', settled),
643
- expire: () => this.cleanupOverran('onCanceled'),
644
- });
776
+ this.runHook(
777
+ 'onCanceled',
778
+ (step) => hook(this.canceledContext(step, outcome)),
779
+ {
780
+ onResult: (settled) => this.afterCleanup('onCanceled', settled),
781
+ expire: () => this.cleanupOverran('onCanceled'),
782
+ },
783
+ );
645
784
  return;
646
785
  }
647
786
  const hook = this.plan.hooks.onFailure;
@@ -652,18 +791,23 @@ export class Invocation<Event> {
652
791
  this.recordCleanup({ step: 'onFailure', reason: 'obsolete' });
653
792
  return this.releaseHeld();
654
793
  }
655
- this.runHook('onFailure', (step) => hook(this.failureContext(step, outcome)), {
656
- onResult: (settled) => this.afterCleanup('onFailure', settled),
657
- expire: () => this.cleanupOverran('onFailure'),
658
- onObsolete: () => {
659
- this.recordCleanup({ step: 'onFailure', reason: 'obsolete' });
660
- this.later(() => this.releaseHeld());
794
+ this.runHook(
795
+ 'onFailure',
796
+ (step) => hook(this.failureContext(step, outcome)),
797
+ {
798
+ onResult: (settled) => this.afterCleanup('onFailure', settled),
799
+ expire: () => this.cleanupOverran('onFailure'),
800
+ onObsolete: () => {
801
+ this.recordCleanup({ step: 'onFailure', reason: 'obsolete' });
802
+ this.later(() => this.releaseHeld());
803
+ },
661
804
  },
662
- });
805
+ );
663
806
  }
664
807
 
665
808
  private afterCleanup(step: CleanupStep, settled: Settled): void {
666
- if (!settled.ok) this.recordCleanup({ step, reason: 'threw', error: settled.error });
809
+ if (!settled.ok)
810
+ this.recordCleanup({ step, reason: 'threw', error: settled.error });
667
811
  this.releaseHeld();
668
812
  }
669
813
 
@@ -692,7 +836,12 @@ export class Invocation<Event> {
692
836
  },
693
837
  },
694
838
  onResult: (settled) => {
695
- if (!settled.ok) this.recordCleanup({ step: 'release', reason: 'threw', error: settled.error });
839
+ if (!settled.ok)
840
+ this.recordCleanup({
841
+ step: 'release',
842
+ reason: 'threw',
843
+ error: settled.error,
844
+ });
696
845
  this.releaseHeld();
697
846
  },
698
847
  },
@@ -705,16 +854,27 @@ export class Invocation<Event> {
705
854
  const hook = this.plan.hooks.onFinally;
706
855
  const outcome = this.outcome;
707
856
  if (hook === undefined || outcome === undefined) return this.settle();
708
- this.runHook('onFinally', (step) => hook({ ...this.hookBase(step), outcome, cleanupErrors: [...this.cleanupErrors], obsolete: this.obsoleteFlag }), {
709
- onResult: (settled) => {
710
- if (!settled.ok) this.emit('cleanup-error', { cleanupStep: 'onFinally' });
711
- this.settle();
712
- },
713
- expire: () => {
714
- this.emit('cleanup-error', { cleanupStep: 'onFinally' });
715
- this.settle();
857
+ this.runHook(
858
+ 'onFinally',
859
+ (step) =>
860
+ hook({
861
+ ...this.hookBase(step),
862
+ outcome,
863
+ cleanupErrors: [...this.cleanupErrors],
864
+ obsolete: this.obsoleteFlag,
865
+ }),
866
+ {
867
+ onResult: (settled) => {
868
+ if (!settled.ok)
869
+ this.emit('cleanup-error', { cleanupStep: 'onFinally' });
870
+ this.settle();
871
+ },
872
+ expire: () => {
873
+ this.emit('cleanup-error', { cleanupStep: 'onFinally' });
874
+ this.settle();
875
+ },
716
876
  },
717
- });
877
+ );
718
878
  }
719
879
 
720
880
  private settle(): void {
@@ -722,7 +882,10 @@ export class Invocation<Event> {
722
882
  this.settledFlag = true;
723
883
  this.clearDeadline();
724
884
  this.stopWatching();
725
- this.emit('finished', this.outcome === undefined ? {} : { status: this.outcome.status });
885
+ this.emit(
886
+ 'finished',
887
+ this.outcome === undefined ? {} : { status: this.outcome.status },
888
+ );
726
889
  this.owner.settled(this);
727
890
  }
728
891
 
@@ -735,7 +898,10 @@ export class Invocation<Event> {
735
898
  // Hook contexts
736
899
  // -------------------------------------------------------------------------------------------
737
900
 
738
- private hookBase(step: Step): { readonly signal: AbortSignal; readonly current: () => boolean } {
901
+ private hookBase(step: Step): {
902
+ readonly signal: AbortSignal;
903
+ readonly current: () => boolean;
904
+ } {
739
905
  return { signal: step.signal, current: () => this.currentFor(step) };
740
906
  }
741
907
 
@@ -745,11 +911,25 @@ export class Invocation<Event> {
745
911
  ): FailureContext<unknown, unknown, unknown, unknown> {
746
912
  const base = this.hookBase(step);
747
913
  const { params, activation } = this;
748
- if (outcome.stage === 'input') return { ...base, stage: outcome.stage, failure: outcome.failure };
914
+ if (outcome.stage === 'input')
915
+ return { ...base, stage: outcome.stage, failure: outcome.failure };
749
916
  if (outcome.stage === 'prerequisites') {
750
- return { ...base, stage: outcome.stage, failure: outcome.failure, params, activation };
917
+ return {
918
+ ...base,
919
+ stage: outcome.stage,
920
+ failure: outcome.failure,
921
+ params,
922
+ activation,
923
+ };
751
924
  }
752
- return { ...base, stage: outcome.stage, failure: outcome.failure, params, activation, provided: this.providedSnapshot() };
925
+ return {
926
+ ...base,
927
+ stage: outcome.stage,
928
+ failure: outcome.failure,
929
+ params,
930
+ activation,
931
+ provided: this.providedSnapshot(),
932
+ };
753
933
  }
754
934
 
755
935
  private canceledContext(
@@ -759,11 +939,27 @@ export class Invocation<Event> {
759
939
  const base = this.hookBase(step);
760
940
  const { cause } = outcome;
761
941
  const { params, activation } = this;
762
- if (outcome.stage === 'prerequisites') return { ...base, stage: outcome.stage, cause, params, activation };
942
+ if (outcome.stage === 'prerequisites')
943
+ return { ...base, stage: outcome.stage, cause, params, activation };
763
944
  if (outcome.stage === 'running') {
764
- return { ...base, stage: outcome.stage, cause, params, activation, provided: this.providedSnapshot() };
945
+ return {
946
+ ...base,
947
+ stage: outcome.stage,
948
+ cause,
949
+ params,
950
+ activation,
951
+ provided: this.providedSnapshot(),
952
+ };
765
953
  }
766
- return { ...base, stage: outcome.stage, cause, params, activation, provided: this.providedSnapshot(), result: outcome.result };
954
+ return {
955
+ ...base,
956
+ stage: outcome.stage,
957
+ cause,
958
+ params,
959
+ activation,
960
+ provided: this.providedSnapshot(),
961
+ result: outcome.result,
962
+ };
767
963
  }
768
964
 
769
965
  // -------------------------------------------------------------------------------------------
@@ -771,15 +967,24 @@ export class Invocation<Event> {
771
967
  // -------------------------------------------------------------------------------------------
772
968
 
773
969
  private runHook(
774
- kind: Extract<StepKind, 'onSuccess' | 'onFailure' | 'onCanceled' | 'onFinally'>,
970
+ kind: Extract<
971
+ StepKind,
972
+ 'onSuccess' | 'onFailure' | 'onCanceled' | 'onFinally'
973
+ >,
775
974
  invoke: (step: Step) => unknown,
776
- handling: { readonly onResult: (settled: Settled) => void; readonly expire: () => void; readonly onObsolete?: () => void },
975
+ handling: {
976
+ readonly onResult: (settled: Settled) => void;
977
+ readonly expire: () => void;
978
+ readonly onObsolete?: () => void;
979
+ },
777
980
  ): void {
778
981
  this.runStep(
779
982
  {
780
983
  kind,
781
984
  budget: { ms: this.limits.hookMs, expire: handling.expire },
782
- ...(handling.onObsolete === undefined ? {} : { onObsolete: handling.onObsolete }),
985
+ ...(handling.onObsolete === undefined
986
+ ? {}
987
+ : { onObsolete: handling.onObsolete }),
783
988
  onResult: handling.onResult,
784
989
  },
785
990
  invoke,
@@ -794,7 +999,12 @@ export class Invocation<Event> {
794
999
  */
795
1000
  private runStep(spec: StepSpec, invoke: (step: Step) => unknown): void {
796
1001
  const own = spec.budget === undefined ? undefined : new AbortController();
797
- const step = new Step(spec.kind, own === undefined ? this.controller.signal : own.signal, own, spec.onObsolete);
1002
+ const step = new Step(
1003
+ spec.kind,
1004
+ own === undefined ? this.controller.signal : own.signal,
1005
+ own,
1006
+ spec.onObsolete,
1007
+ );
798
1008
  this.step = step;
799
1009
  let arrived = false;
800
1010
  const arrive = (settled: Settled): void => {
@@ -868,7 +1078,16 @@ export class Invocation<Event> {
868
1078
  }
869
1079
  }
870
1080
 
871
- private emit(phase: ObservedPhase, extra: Partial<Pick<ObservedEvent, 'status' | 'failureKind' | 'cleanupStep'>> = {}): void {
872
- emitObservation(this.plan, this.owner.bindingId, this.startedAt, { invocationId: this.id, phase, ...extra });
1081
+ private emit(
1082
+ phase: ObservedPhase,
1083
+ extra: Partial<
1084
+ Pick<ObservedEvent, 'status' | 'failureKind' | 'cleanupStep'>
1085
+ > = {},
1086
+ ): void {
1087
+ emitObservation(this.plan, this.owner.bindingId, this.startedAt, {
1088
+ invocationId: this.id,
1089
+ phase,
1090
+ ...extra,
1091
+ });
873
1092
  }
874
1093
  }