@ductape/sdk 0.1.144 → 0.1.147

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,24 @@
1
- export type DuctapeExecutionKind = 'feature' | 'step' | 'function' | 'primitive';
1
+ export type DuctapeExecutionKind = 'feature' | 'step' | 'function' | 'primitive' | 'asset';
2
+ export interface DuctapeAssetSpan {
3
+ trace_id: string;
4
+ span_id: string;
5
+ parent_span_id?: string;
6
+ feature_id?: string;
7
+ feature_tag?: string;
8
+ feature_run_id?: string;
9
+ step_tag?: string;
10
+ step_run_id?: string;
11
+ function_invocation_id?: string;
12
+ asset_type: string;
13
+ asset_tag: string;
14
+ asset_operation: string;
15
+ asset_action?: string;
16
+ status: 'success' | 'fail';
17
+ start: number;
18
+ end: number;
19
+ duration_ms: number;
20
+ error_message?: string;
21
+ }
2
22
  export interface DuctapeExecutionContext {
3
23
  workspace_id?: string;
4
24
  product_id?: string;
@@ -28,6 +48,8 @@ export interface DuctapeExecutionContext {
28
48
  idempotency_key?: string;
29
49
  deadline_at?: number;
30
50
  baggage?: Record<string, string>;
51
+ /** Internal per-run span sink. It is never serialized into logs or processor results. */
52
+ asset_span_sink?: DuctapeAssetSpan[];
31
53
  kind: DuctapeExecutionKind;
32
54
  }
33
55
  export declare function currentExecutionContext(): DuctapeExecutionContext | undefined;
@@ -35,3 +57,9 @@ export declare function createExecutionContext(fields: Partial<DuctapeExecutionC
35
57
  export declare function runWithExecutionContext<T>(context: DuctapeExecutionContext, fn: () => T): T;
36
58
  /** Fields safe and useful on processor-result/log records. */
37
59
  export declare function executionContextFields(context?: DuctapeExecutionContext): Record<string, unknown>;
60
+ export declare function withAssetSpan<T>(asset: {
61
+ type: string;
62
+ tag: string;
63
+ operation: string;
64
+ action?: string;
65
+ }, fn: () => Promise<T>): Promise<T>;
@@ -15,6 +15,7 @@ exports.currentExecutionContext = currentExecutionContext;
15
15
  exports.createExecutionContext = createExecutionContext;
16
16
  exports.runWithExecutionContext = runWithExecutionContext;
17
17
  exports.executionContextFields = executionContextFields;
18
+ exports.withAssetSpan = withAssetSpan;
18
19
  const async_hooks_1 = require("async_hooks");
19
20
  const crypto_1 = require("crypto");
20
21
  const storage = new async_hooks_1.AsyncLocalStorage();
@@ -32,7 +33,62 @@ function runWithExecutionContext(context, fn) {
32
33
  function executionContextFields(context = currentExecutionContext()) {
33
34
  if (!context)
34
35
  return {};
35
- const { session: _secretSession, baggage: _baggage, kind: execution_kind } = context, fields = __rest(context, ["session", "baggage", "kind"]);
36
+ const { session: _secretSession, baggage: _baggage, asset_span_sink: _assetSpanSink, kind: execution_kind } = context, fields = __rest(context, ["session", "baggage", "asset_span_sink", "kind"]);
36
37
  return Object.assign(Object.assign({}, fields), { execution_kind });
37
38
  }
39
+ async function withAssetSpan(asset, fn) {
40
+ const parent = currentExecutionContext();
41
+ if (!(parent === null || parent === void 0 ? void 0 : parent.asset_span_sink))
42
+ return fn();
43
+ const context = createExecutionContext({ kind: 'asset' }, parent);
44
+ const start = Date.now();
45
+ try {
46
+ const result = await runWithExecutionContext(context, fn);
47
+ const end = Date.now();
48
+ parent.asset_span_sink.push({
49
+ trace_id: context.trace_id,
50
+ span_id: context.span_id,
51
+ parent_span_id: context.parent_span_id,
52
+ feature_id: context.feature_id,
53
+ feature_tag: context.feature_tag,
54
+ feature_run_id: context.feature_run_id,
55
+ step_tag: context.step_tag,
56
+ step_run_id: context.step_run_id,
57
+ function_invocation_id: context.function_invocation_id,
58
+ asset_type: asset.type,
59
+ asset_tag: asset.tag,
60
+ asset_operation: asset.operation,
61
+ asset_action: asset.action,
62
+ status: 'success',
63
+ start,
64
+ end,
65
+ duration_ms: end - start,
66
+ });
67
+ return result;
68
+ }
69
+ catch (error) {
70
+ const end = Date.now();
71
+ parent.asset_span_sink.push({
72
+ trace_id: context.trace_id,
73
+ span_id: context.span_id,
74
+ parent_span_id: context.parent_span_id,
75
+ feature_id: context.feature_id,
76
+ feature_tag: context.feature_tag,
77
+ feature_run_id: context.feature_run_id,
78
+ step_tag: context.step_tag,
79
+ step_run_id: context.step_run_id,
80
+ function_invocation_id: context.function_invocation_id,
81
+ asset_type: asset.type,
82
+ asset_tag: asset.tag,
83
+ asset_operation: asset.operation,
84
+ asset_action: asset.action,
85
+ status: 'fail',
86
+ start,
87
+ end,
88
+ duration_ms: end - start,
89
+ error_message: error instanceof Error ? error.message : String(error),
90
+ });
91
+ throw error;
92
+ }
93
+ }
38
94
  //# sourceMappingURL=execution-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.js","sourceRoot":"","sources":["../../src/context/execution-context.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAuCA,0DAEC;AAED,wDAYC;AAED,0DAEC;AAGD,wDASC;AAvED,6CAAgD;AAChD,mCAAoC;AAoCpC,MAAM,OAAO,GAAG,IAAI,+BAAiB,EAA2B,CAAC;AAEjE,SAAgB,uBAAuB;IACrC,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,SAAgB,sBAAsB,CACpC,MAAwC,EACxC,SAA8C,uBAAuB,EAAE;;IAEvE,qDACK,MAAM,GACN,MAAM,KACT,IAAI,EAAE,MAAA,MAAA,MAAM,CAAC,IAAI,mCAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,mCAAI,WAAW,EAChD,QAAQ,EAAE,MAAA,MAAA,MAAM,CAAC,QAAQ,mCAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,QAAQ,mCAAI,IAAA,mBAAU,GAAE,EAC7D,OAAO,EAAE,MAAA,MAAM,CAAC,OAAO,mCAAI,IAAA,mBAAU,GAAE,EACvC,cAAc,EAAE,MAAA,MAAM,CAAC,cAAc,mCAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,IACxD;AACJ,CAAC;AAED,SAAgB,uBAAuB,CAAI,OAAgC,EAAE,EAAW;IACtF,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,8DAA8D;AAC9D,SAAgB,sBAAsB,CAAC,OAAO,GAAG,uBAAuB,EAAE;IACxE,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,EACJ,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,cAAc,KAElB,OAAO,EADN,MAAM,UACP,OAAO,EALL,8BAKL,CAAU,CAAC;IACZ,uCAAY,MAAM,KAAE,cAAc,IAAG;AACvC,CAAC"}
1
+ {"version":3,"file":"execution-context.js","sourceRoot":"","sources":["../../src/context/execution-context.ts"],"names":[],"mappings":"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"}
@@ -60,6 +60,8 @@ export declare class FeatureExecutor {
60
60
  private functionInvocations;
61
61
  /** Step telemetry is accumulated and persisted with the terminal feature record in one round trip. */
62
62
  private pendingProcessorResults;
63
+ private assetSpans;
64
+ private stepContexts;
63
65
  constructor(config: IFeatureServiceConfig & {
64
66
  access_key: string;
65
67
  }, feature: IProductFeature, options: IExecuteFeatureOptions, private_key: string, sessionLogFields?: {
@@ -127,9 +129,8 @@ export declare class FeatureExecutor {
127
129
  */
128
130
  private executeStep;
129
131
  private executeStepInContext;
130
- /**
131
- * Execute an action step
132
- */
132
+ private describeStepAsset;
133
+ /** Execute an action step. */
133
134
  private executeActionStep;
134
135
  /**
135
136
  * Execute a database step: use query/insert/update/delete/upsert for DB operations, execute() for named actions.
@@ -165,6 +165,8 @@ class FeatureExecutor {
165
165
  this.functionInvocations = new Map();
166
166
  /** Step telemetry is accumulated and persisted with the terminal feature record in one round trip. */
167
167
  this.pendingProcessorResults = [];
168
+ this.assetSpans = [];
169
+ this.stepContexts = new Map();
168
170
  debugLog('[FeatureExecutor] constructor ENTRY', {
169
171
  feature_tag: feature.tag,
170
172
  product: options.product,
@@ -388,6 +390,9 @@ class FeatureExecutor {
388
390
  token: this.config.token,
389
391
  env_type: this.config.env_type,
390
392
  private_key: this._privateKey,
393
+ access_key: this.config.access_key,
394
+ redis_client: this.config.redis_client,
395
+ preInitializedProductBuilder: this.productBuilder,
391
396
  });
392
397
  }
393
398
  return this._fallbackService;
@@ -417,8 +422,18 @@ class FeatureExecutor {
417
422
  const inputStr = stringifyProcessorValue(inputPayload);
418
423
  const encKey = this.getProcessorResultEncryptionKey();
419
424
  const processorResult = Object.assign(Object.assign({}, (0, context_1.executionContextFields)()), { process_id: this.state.feature_id, product_id: this.productId || null, env: this.state.env, component: types_1.LogEventTypes.FEATURE, status, start: this.state.started_at, end: Date.now(), retryable: true, result: encKey ? (0, processor_utils_1.encrypt)(resultStr, encKey) : resultStr, input: encKey ? (0, processor_utils_1.encrypt)(inputStr, encKey) : inputStr, feature_id: this.state.feature_id, feature_tag: this.state.feature_tag, product_tag: this.state.product, workspace_id: this.config.workspace_id });
425
+ const assetResults = this.assetSpans.splice(0).map((span) => {
426
+ const spanResult = stringifyProcessorValue({
427
+ asset_type: span.asset_type,
428
+ asset_tag: span.asset_tag,
429
+ operation: span.asset_operation,
430
+ action: span.asset_action,
431
+ });
432
+ const spanInput = stringifyProcessorValue({});
433
+ return Object.assign(Object.assign({}, span), { process_id: span.span_id, product_id: this.productId || null, product_tag: this.state.product, workspace_id: this.config.workspace_id, env: this.state.env, component: 'asset_invocation', execution_kind: 'asset', retryable: true, result: encKey ? (0, processor_utils_1.encrypt)(spanResult, encKey) : spanResult, input: encKey ? (0, processor_utils_1.encrypt)(spanInput, encKey) : spanInput, asset_duration_ms: span.duration_ms, asset_error: span.error_message });
434
+ });
420
435
  this.trace('execution.persistence.sent', { status, payload: resultData });
421
- await this.processorApiService.saveResultsBatch([...this.pendingProcessorResults.splice(0), processorResult], this.getAuthPayload());
436
+ await this.processorApiService.saveResultsBatch([...this.pendingProcessorResults.splice(0), ...assetResults, processorResult], this.getAuthPayload());
422
437
  this.trace('execution.persistence.acknowledged', { status, process_id: this.state.feature_id });
423
438
  debugLog('[FeatureExecutor] persistExecutionResult SUCCESS', { feature_id: this.state.feature_id, status });
424
439
  }
@@ -455,7 +470,7 @@ class FeatureExecutor {
455
470
  * Processor results for steps always have component: 'feature_step' and step_type set to the step kind.
456
471
  */
457
472
  async persistStepResult(step, status, output, error, duration, input) {
458
- var _a;
473
+ var _a, _b;
459
474
  const resolvedInput = input !== null && input !== void 0 ? input : step.input;
460
475
  const stepType = this.getStepTypeForResult(step);
461
476
  debugLog('[FeatureExecutor] persistStepResult ENTRY', {
@@ -476,8 +491,8 @@ class FeatureExecutor {
476
491
  const resultStr = stringifyProcessorValue(value);
477
492
  const inputStr = stringifyProcessorValue(resolvedInput);
478
493
  const encKey = this.getProcessorResultEncryptionKey();
479
- const stepResult = Object.assign(Object.assign(Object.assign({}, (0, context_1.executionContextFields)()), { process_id: stepProcessId, product_id: this.productId || null, env: this.state.env, component: types_1.LogEventTypes.FEATURE_STEP, // step results always feature_step
480
- status, start: Date.now() - (duration || 0), end: Date.now(), retryable: true, result: encKey ? (0, processor_utils_1.encrypt)(resultStr, encKey) : resultStr, input: encKey ? (0, processor_utils_1.encrypt)(inputStr, encKey) : inputStr, feature_id: this.state.feature_id, feature_tag: this.state.feature_tag, product_tag: this.state.product, workspace_id: this.config.workspace_id, step_tag: step.tag, step_type: stepType, step_error: error, step_duration_ms: duration }), ((_a = this.functionInvocations.get(step.tag)) !== null && _a !== void 0 ? _a : {}));
494
+ const stepResult = Object.assign(Object.assign(Object.assign(Object.assign({}, (0, context_1.executionContextFields)()), ((_a = this.stepContexts.get(step.tag)) !== null && _a !== void 0 ? _a : {})), { process_id: stepProcessId, product_id: this.productId || null, env: this.state.env, component: types_1.LogEventTypes.FEATURE_STEP, // step results always feature_step
495
+ status, start: Date.now() - (duration || 0), end: Date.now(), retryable: true, result: encKey ? (0, processor_utils_1.encrypt)(resultStr, encKey) : resultStr, input: encKey ? (0, processor_utils_1.encrypt)(inputStr, encKey) : inputStr, feature_id: this.state.feature_id, feature_tag: this.state.feature_tag, product_tag: this.state.product, workspace_id: this.config.workspace_id, step_tag: step.tag, step_type: stepType, step_error: error, step_duration_ms: duration }), ((_b = this.functionInvocations.get(step.tag)) !== null && _b !== void 0 ? _b : {}));
481
496
  this.trace('step.persistence.sent', {
482
497
  step_tag: step.tag,
483
498
  step_type: stepType,
@@ -554,6 +569,7 @@ class FeatureExecutor {
554
569
  session_id: this.sessionLogFields.session_id,
555
570
  session_tag: this.sessionLogFields.session_tag,
556
571
  session_user_id: this.sessionLogFields.session_user_id,
572
+ asset_span_sink: this.assetSpans,
557
573
  });
558
574
  return (0, context_1.runWithExecutionContext)(context, () => this.executeInContext());
559
575
  }
@@ -822,6 +838,12 @@ class FeatureExecutor {
822
838
  process_id: `${this.state.feature_id}:${step.tag}`,
823
839
  parent_process_id: this.state.feature_id,
824
840
  }, parent);
841
+ this.stepContexts.set(step.tag, {
842
+ trace_id: context.trace_id,
843
+ span_id: context.span_id,
844
+ parent_span_id: context.parent_span_id,
845
+ step_run_id: context.step_run_id,
846
+ });
825
847
  return (0, context_1.runWithExecutionContext)(context, () => this.executeStepInContext(step));
826
848
  }
827
849
  async executeStepInContext(step) {
@@ -842,62 +864,67 @@ class FeatureExecutor {
842
864
  step_tag: step.tag,
843
865
  input: sanitizeForLog(resolvedInput),
844
866
  });
845
- let output;
846
- // Route steps to dedicated services; only action and notification use ProcessorService.
847
- switch (step.type) {
848
- case types_1.FeatureStepType.ACTION:
849
- output = await this.executeActionStep(step, resolvedInput);
850
- break;
851
- case types_1.FeatureStepType.DATABASE_ACTION:
852
- output = await this.executeDatabaseStep(step, resolvedInput);
853
- break;
854
- case types_1.FeatureStepType.NOTIFICATION:
855
- output = await this.executeNotificationStep(step, resolvedInput);
856
- break;
857
- case types_1.FeatureStepType.STORAGE:
858
- output = await this.executeStorageStep(step, resolvedInput);
859
- break;
860
- case types_1.FeatureStepType.PUBLISH:
861
- case types_1.FeatureStepType.PRODUCE:
862
- output = await this.executeProduceStep(step, resolvedInput);
863
- break;
864
- case types_1.FeatureStepType.GRAPH:
865
- output = await this.executeGraphStep(step, resolvedInput);
866
- break;
867
- case types_1.FeatureStepType.VECTOR:
868
- output = await this.executeVectorStep(step, resolvedInput);
869
- break;
870
- case types_1.FeatureStepType.SESSION:
871
- output = await this.executeSessionStep(step, resolvedInput);
872
- break;
873
- case types_1.FeatureStepType.FUNCTION:
874
- output = await this.executeFunctionStep(step, resolvedInput);
875
- break;
876
- case types_1.FeatureStepType.QUOTA:
877
- output = await this.executeQuotaStep(step, resolvedInput);
878
- break;
879
- case types_1.FeatureStepType.FALLBACK:
880
- output = await this.executeFallbackStep(step, resolvedInput);
881
- break;
882
- case types_1.FeatureStepType.CHILD_FEATURE:
883
- output = await this.executeChildWorkflowStep(step, resolvedInput);
884
- break;
885
- case types_1.FeatureStepType.SLEEP:
886
- await this.executeSleepStep(step, resolvedInput);
887
- output = { slept: true };
888
- break;
889
- case types_1.FeatureStepType.WAIT_FOR_SIGNAL:
890
- output = await this.executeWaitForSignalStep(step, resolvedInput);
891
- break;
892
- case types_1.FeatureStepType.CHECKPOINT:
893
- await this.executeCheckpointStep(step, resolvedInput);
894
- output = { checkpoint: step.tag };
895
- break;
896
- default:
897
- stepLog('Step type not implemented (passthrough)', { tag: step.tag, type: step.type });
898
- debugLog('[FeatureExecutor] executeStep unknown step type (passthrough)', { step_tag: step.tag, step_type: step.type });
899
- output = resolvedInput;
900
- }
867
+ const asset = this.describeStepAsset(step);
868
+ const runOperation = async () => {
869
+ let value;
870
+ // Route steps to dedicated services; only action and notification use ProcessorService.
871
+ switch (step.type) {
872
+ case types_1.FeatureStepType.ACTION:
873
+ value = await this.executeActionStep(step, resolvedInput);
874
+ break;
875
+ case types_1.FeatureStepType.DATABASE_ACTION:
876
+ value = await this.executeDatabaseStep(step, resolvedInput);
877
+ break;
878
+ case types_1.FeatureStepType.NOTIFICATION:
879
+ value = await this.executeNotificationStep(step, resolvedInput);
880
+ break;
881
+ case types_1.FeatureStepType.STORAGE:
882
+ value = await this.executeStorageStep(step, resolvedInput);
883
+ break;
884
+ case types_1.FeatureStepType.PUBLISH:
885
+ case types_1.FeatureStepType.PRODUCE:
886
+ value = await this.executeProduceStep(step, resolvedInput);
887
+ break;
888
+ case types_1.FeatureStepType.GRAPH:
889
+ value = await this.executeGraphStep(step, resolvedInput);
890
+ break;
891
+ case types_1.FeatureStepType.VECTOR:
892
+ value = await this.executeVectorStep(step, resolvedInput);
893
+ break;
894
+ case types_1.FeatureStepType.SESSION:
895
+ value = await this.executeSessionStep(step, resolvedInput);
896
+ break;
897
+ case types_1.FeatureStepType.FUNCTION:
898
+ value = await this.executeFunctionStep(step, resolvedInput);
899
+ break;
900
+ case types_1.FeatureStepType.QUOTA:
901
+ value = await this.executeQuotaStep(step, resolvedInput);
902
+ break;
903
+ case types_1.FeatureStepType.FALLBACK:
904
+ value = await this.executeFallbackStep(step, resolvedInput);
905
+ break;
906
+ case types_1.FeatureStepType.CHILD_FEATURE:
907
+ value = await this.executeChildWorkflowStep(step, resolvedInput);
908
+ break;
909
+ case types_1.FeatureStepType.SLEEP:
910
+ await this.executeSleepStep(step, resolvedInput);
911
+ value = { slept: true };
912
+ break;
913
+ case types_1.FeatureStepType.WAIT_FOR_SIGNAL:
914
+ value = await this.executeWaitForSignalStep(step, resolvedInput);
915
+ break;
916
+ case types_1.FeatureStepType.CHECKPOINT:
917
+ await this.executeCheckpointStep(step, resolvedInput);
918
+ value = { checkpoint: step.tag };
919
+ break;
920
+ default:
921
+ stepLog('Step type not implemented (passthrough)', { tag: step.tag, type: step.type });
922
+ debugLog('[FeatureExecutor] executeStep unknown step type (passthrough)', { step_tag: step.tag, step_type: step.type });
923
+ value = resolvedInput;
924
+ }
925
+ return value;
926
+ };
927
+ let output = asset ? await (0, context_1.withAssetSpan)(asset, runOperation) : await runOperation();
901
928
  // For action steps with no app: if step.output is a template with $ refs (e.g. $Sequence{main}{step-a}{value}-b),
902
929
  // resolve it so downstream steps get the correct value (checkpoint / closure-style steps).
903
930
  if (step.type === types_1.FeatureStepType.ACTION &&
@@ -951,9 +978,40 @@ class FeatureExecutor {
951
978
  };
952
979
  }
953
980
  }
954
- /**
955
- * Execute an action step
956
- */
981
+ describeStepAsset(step) {
982
+ var _a, _b;
983
+ const operation = step.event || 'execute';
984
+ switch (step.type) {
985
+ case types_1.FeatureStepType.ACTION:
986
+ return step.app ? { type: 'app', tag: step.app, operation, action: step.event } : null;
987
+ case types_1.FeatureStepType.DATABASE_ACTION:
988
+ return { type: 'database', tag: step.database || 'database', operation, action: step.event };
989
+ case types_1.FeatureStepType.GRAPH:
990
+ return { type: 'graph', tag: step.graph || 'graph', operation, action: step.event };
991
+ case types_1.FeatureStepType.VECTOR:
992
+ return { type: 'vector', tag: step.vector || 'vector', operation, action: step.event };
993
+ case types_1.FeatureStepType.STORAGE:
994
+ return { type: 'storage', tag: step.storage || 'storage', operation, action: step.event };
995
+ case types_1.FeatureStepType.NOTIFICATION:
996
+ return { type: 'notification', tag: step.notification || 'notification', operation, action: step.event };
997
+ case types_1.FeatureStepType.PUBLISH:
998
+ case types_1.FeatureStepType.PRODUCE:
999
+ return { type: 'message_broker', tag: step.broker || 'message-broker', operation: 'produce', action: step.event };
1000
+ case types_1.FeatureStepType.SESSION:
1001
+ return { type: 'session', tag: step.session || 'session', operation, action: step.event };
1002
+ case types_1.FeatureStepType.QUOTA:
1003
+ return { type: 'quota', tag: step.quota || 'quota', operation: 'execute' };
1004
+ case types_1.FeatureStepType.FALLBACK:
1005
+ return { type: 'fallback', tag: step.fallback || 'fallback', operation: 'execute' };
1006
+ case types_1.FeatureStepType.CHILD_FEATURE:
1007
+ return { type: 'feature', tag: step.feature || 'feature', operation: 'execute' };
1008
+ case types_1.FeatureStepType.FUNCTION:
1009
+ return { type: 'function', tag: ((_a = step.function_ref) === null || _a === void 0 ? void 0 : _a.namespace) || 'function', operation: ((_b = step.function_ref) === null || _b === void 0 ? void 0 : _b.operation) || operation };
1010
+ default:
1011
+ return null;
1012
+ }
1013
+ }
1014
+ /** Execute an action step. */
957
1015
  async executeActionStep(step, input) {
958
1016
  var _a, _b;
959
1017
  debugLog('[FeatureExecutor] executeActionStep', { feature_id: this.state.feature_id, step_tag: step.tag, app: step.app });