@ductape/sdk 0.1.145 → 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,
@@ -420,8 +422,18 @@ class FeatureExecutor {
420
422
  const inputStr = stringifyProcessorValue(inputPayload);
421
423
  const encKey = this.getProcessorResultEncryptionKey();
422
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
+ });
423
435
  this.trace('execution.persistence.sent', { status, payload: resultData });
424
- await this.processorApiService.saveResultsBatch([...this.pendingProcessorResults.splice(0), processorResult], this.getAuthPayload());
436
+ await this.processorApiService.saveResultsBatch([...this.pendingProcessorResults.splice(0), ...assetResults, processorResult], this.getAuthPayload());
425
437
  this.trace('execution.persistence.acknowledged', { status, process_id: this.state.feature_id });
426
438
  debugLog('[FeatureExecutor] persistExecutionResult SUCCESS', { feature_id: this.state.feature_id, status });
427
439
  }
@@ -458,7 +470,7 @@ class FeatureExecutor {
458
470
  * Processor results for steps always have component: 'feature_step' and step_type set to the step kind.
459
471
  */
460
472
  async persistStepResult(step, status, output, error, duration, input) {
461
- var _a;
473
+ var _a, _b;
462
474
  const resolvedInput = input !== null && input !== void 0 ? input : step.input;
463
475
  const stepType = this.getStepTypeForResult(step);
464
476
  debugLog('[FeatureExecutor] persistStepResult ENTRY', {
@@ -479,8 +491,8 @@ class FeatureExecutor {
479
491
  const resultStr = stringifyProcessorValue(value);
480
492
  const inputStr = stringifyProcessorValue(resolvedInput);
481
493
  const encKey = this.getProcessorResultEncryptionKey();
482
- 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
483
- 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 : {}));
484
496
  this.trace('step.persistence.sent', {
485
497
  step_tag: step.tag,
486
498
  step_type: stepType,
@@ -557,6 +569,7 @@ class FeatureExecutor {
557
569
  session_id: this.sessionLogFields.session_id,
558
570
  session_tag: this.sessionLogFields.session_tag,
559
571
  session_user_id: this.sessionLogFields.session_user_id,
572
+ asset_span_sink: this.assetSpans,
560
573
  });
561
574
  return (0, context_1.runWithExecutionContext)(context, () => this.executeInContext());
562
575
  }
@@ -825,6 +838,12 @@ class FeatureExecutor {
825
838
  process_id: `${this.state.feature_id}:${step.tag}`,
826
839
  parent_process_id: this.state.feature_id,
827
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
+ });
828
847
  return (0, context_1.runWithExecutionContext)(context, () => this.executeStepInContext(step));
829
848
  }
830
849
  async executeStepInContext(step) {
@@ -845,62 +864,67 @@ class FeatureExecutor {
845
864
  step_tag: step.tag,
846
865
  input: sanitizeForLog(resolvedInput),
847
866
  });
848
- let output;
849
- // Route steps to dedicated services; only action and notification use ProcessorService.
850
- switch (step.type) {
851
- case types_1.FeatureStepType.ACTION:
852
- output = await this.executeActionStep(step, resolvedInput);
853
- break;
854
- case types_1.FeatureStepType.DATABASE_ACTION:
855
- output = await this.executeDatabaseStep(step, resolvedInput);
856
- break;
857
- case types_1.FeatureStepType.NOTIFICATION:
858
- output = await this.executeNotificationStep(step, resolvedInput);
859
- break;
860
- case types_1.FeatureStepType.STORAGE:
861
- output = await this.executeStorageStep(step, resolvedInput);
862
- break;
863
- case types_1.FeatureStepType.PUBLISH:
864
- case types_1.FeatureStepType.PRODUCE:
865
- output = await this.executeProduceStep(step, resolvedInput);
866
- break;
867
- case types_1.FeatureStepType.GRAPH:
868
- output = await this.executeGraphStep(step, resolvedInput);
869
- break;
870
- case types_1.FeatureStepType.VECTOR:
871
- output = await this.executeVectorStep(step, resolvedInput);
872
- break;
873
- case types_1.FeatureStepType.SESSION:
874
- output = await this.executeSessionStep(step, resolvedInput);
875
- break;
876
- case types_1.FeatureStepType.FUNCTION:
877
- output = await this.executeFunctionStep(step, resolvedInput);
878
- break;
879
- case types_1.FeatureStepType.QUOTA:
880
- output = await this.executeQuotaStep(step, resolvedInput);
881
- break;
882
- case types_1.FeatureStepType.FALLBACK:
883
- output = await this.executeFallbackStep(step, resolvedInput);
884
- break;
885
- case types_1.FeatureStepType.CHILD_FEATURE:
886
- output = await this.executeChildWorkflowStep(step, resolvedInput);
887
- break;
888
- case types_1.FeatureStepType.SLEEP:
889
- await this.executeSleepStep(step, resolvedInput);
890
- output = { slept: true };
891
- break;
892
- case types_1.FeatureStepType.WAIT_FOR_SIGNAL:
893
- output = await this.executeWaitForSignalStep(step, resolvedInput);
894
- break;
895
- case types_1.FeatureStepType.CHECKPOINT:
896
- await this.executeCheckpointStep(step, resolvedInput);
897
- output = { checkpoint: step.tag };
898
- break;
899
- default:
900
- stepLog('Step type not implemented (passthrough)', { tag: step.tag, type: step.type });
901
- debugLog('[FeatureExecutor] executeStep unknown step type (passthrough)', { step_tag: step.tag, step_type: step.type });
902
- output = resolvedInput;
903
- }
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();
904
928
  // For action steps with no app: if step.output is a template with $ refs (e.g. $Sequence{main}{step-a}{value}-b),
905
929
  // resolve it so downstream steps get the correct value (checkpoint / closure-style steps).
906
930
  if (step.type === types_1.FeatureStepType.ACTION &&
@@ -954,9 +978,40 @@ class FeatureExecutor {
954
978
  };
955
979
  }
956
980
  }
957
- /**
958
- * Execute an action step
959
- */
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. */
960
1015
  async executeActionStep(step, input) {
961
1016
  var _a, _b;
962
1017
  debugLog('[FeatureExecutor] executeActionStep', { feature_id: this.state.feature_id, step_tag: step.tag, app: step.app });