autotel 4.1.0 → 4.2.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.
Files changed (253) hide show
  1. package/dist/auto.cjs +5 -3
  2. package/dist/auto.cjs.map +1 -1
  3. package/dist/auto.js +3 -3
  4. package/dist/auto.js.map +1 -1
  5. package/dist/chunk-C_NdSu1c.cjs +34 -0
  6. package/dist/correlation-id.cjs +1 -1
  7. package/dist/correlation-id.d.cts.map +1 -1
  8. package/dist/correlation-id.d.ts.map +1 -1
  9. package/dist/correlation-id.js +1 -1
  10. package/dist/decorators.cjs +1 -1
  11. package/dist/decorators.js +1 -1
  12. package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
  13. package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
  14. package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
  15. package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
  16. package/dist/event.cjs +1 -1
  17. package/dist/event.js +1 -1
  18. package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
  19. package/dist/functional-C8B0Qa7o.cjs.map +1 -0
  20. package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
  21. package/dist/functional-r-AUIRy_.js.map +1 -0
  22. package/dist/functional.cjs +1 -1
  23. package/dist/functional.js +1 -1
  24. package/dist/http.cjs +1 -1
  25. package/dist/http.js +1 -1
  26. package/dist/index.cjs +15 -13
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +14 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
  33. package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
  34. package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
  35. package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
  36. package/dist/instrumentation.cjs +2 -2
  37. package/dist/instrumentation.js +2 -2
  38. package/dist/logger.cjs +236 -8
  39. package/dist/logger.cjs.map +1 -0
  40. package/dist/messaging.cjs +1 -1
  41. package/dist/messaging.js +1 -1
  42. package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
  43. package/dist/node-require-CZ_PU448.cjs.map +1 -0
  44. package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
  45. package/dist/node-require-vROmTeJ8.js.map +1 -0
  46. package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
  47. package/dist/operation-context-CKBoA4Qy.js.map +1 -0
  48. package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
  49. package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
  50. package/dist/register.cjs +3 -1
  51. package/dist/register.cjs.map +1 -1
  52. package/dist/register.js +2 -2
  53. package/dist/register.js.map +1 -1
  54. package/dist/semantic-helpers.cjs +1 -1
  55. package/dist/semantic-helpers.js +1 -1
  56. package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
  57. package/dist/stable-hash-ChFBIhNt.js.map +1 -0
  58. package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
  59. package/dist/stable-hash-brKISGf1.cjs.map +1 -0
  60. package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
  61. package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
  62. package/dist/trace-helpers.cjs +1 -1
  63. package/dist/trace-helpers.js +1 -1
  64. package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
  65. package/dist/track-COUuU48p.js.map +1 -0
  66. package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
  67. package/dist/track-Cb3Q4QmS.cjs.map +1 -0
  68. package/dist/validate.cjs +1 -1
  69. package/dist/validate.js +1 -1
  70. package/dist/webhook.cjs +1 -1
  71. package/dist/webhook.js +1 -1
  72. package/dist/workflow-distributed.cjs +1 -1
  73. package/dist/workflow-distributed.js +1 -1
  74. package/dist/workflow.cjs +3 -1
  75. package/dist/workflow.cjs.map +1 -1
  76. package/dist/workflow.d.cts.map +1 -1
  77. package/dist/workflow.d.ts.map +1 -1
  78. package/dist/workflow.js +3 -3
  79. package/dist/workflow.js.map +1 -1
  80. package/dist/yaml-config.cjs +233 -4
  81. package/dist/yaml-config.cjs.map +1 -0
  82. package/dist/yaml-config.d.cts.map +1 -1
  83. package/dist/yaml-config.d.ts.map +1 -1
  84. package/dist/yaml-config.js +8 -7
  85. package/dist/yaml-config.js.map +1 -1
  86. package/package.json +1 -2
  87. package/dist/functional-DtI0u4vx.js.map +0 -1
  88. package/dist/functional-zpzNLhky.cjs.map +0 -1
  89. package/dist/logger-thMPLpOG.cjs +0 -487
  90. package/dist/logger-thMPLpOG.cjs.map +0 -1
  91. package/dist/node-require-DF5QBX6z.cjs.map +0 -1
  92. package/dist/node-require-Db1oDpLj.js.map +0 -1
  93. package/dist/operation-context-C-2hmmtP.js.map +0 -1
  94. package/dist/operation-context-n4_obUwq.cjs.map +0 -1
  95. package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
  96. package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
  97. package/dist/track-D59FfpL0.cjs.map +0 -1
  98. package/dist/track-wc0HafS_.js.map +0 -1
  99. package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
  100. package/dist/yaml-config-Ck2uB0Dp.cjs.map +0 -1
  101. package/src/attribute-redacting-processor.test.ts +0 -763
  102. package/src/attribute-redacting-processor.ts +0 -621
  103. package/src/attributes/attachers.ts +0 -161
  104. package/src/attributes/builders.ts +0 -529
  105. package/src/attributes/domains.ts +0 -42
  106. package/src/attributes/index.ts +0 -81
  107. package/src/attributes/registry.ts +0 -323
  108. package/src/attributes/types.ts +0 -211
  109. package/src/attributes/utils.ts +0 -64
  110. package/src/attributes/validators.ts +0 -266
  111. package/src/attributes.test.ts +0 -292
  112. package/src/auto.ts +0 -67
  113. package/src/autotel-logger.test.ts +0 -548
  114. package/src/autotel-logger.ts +0 -364
  115. package/src/baggage-span-processor.test.ts +0 -202
  116. package/src/baggage-span-processor.ts +0 -100
  117. package/src/business-baggage.test.ts +0 -500
  118. package/src/business-baggage.ts +0 -669
  119. package/src/circuit-breaker.test.ts +0 -341
  120. package/src/circuit-breaker.ts +0 -184
  121. package/src/config.test.ts +0 -94
  122. package/src/config.ts +0 -172
  123. package/src/correlated-events.test.ts +0 -151
  124. package/src/correlated-events.ts +0 -47
  125. package/src/correlation-id.test.ts +0 -163
  126. package/src/correlation-id.ts +0 -206
  127. package/src/db.test.ts +0 -252
  128. package/src/db.ts +0 -447
  129. package/src/decorators.test.ts +0 -153
  130. package/src/decorators.ts +0 -188
  131. package/src/define-event.test.ts +0 -41
  132. package/src/define-event.ts +0 -58
  133. package/src/devtools.ts +0 -60
  134. package/src/drain-pipeline.test.ts +0 -68
  135. package/src/drain-pipeline.ts +0 -199
  136. package/src/drain-toolkit.test.ts +0 -113
  137. package/src/drain-toolkit.ts +0 -129
  138. package/src/enricher-toolkit.test.ts +0 -67
  139. package/src/enricher-toolkit.ts +0 -79
  140. package/src/enrichers.test.ts +0 -150
  141. package/src/enrichers.ts +0 -145
  142. package/src/env-config.test.ts +0 -323
  143. package/src/env-config.ts +0 -309
  144. package/src/error-catalog.test.ts +0 -133
  145. package/src/error-catalog.ts +0 -262
  146. package/src/event-queue.test.ts +0 -864
  147. package/src/event-queue.ts +0 -699
  148. package/src/event-subscriber.ts +0 -262
  149. package/src/event-testing.ts +0 -197
  150. package/src/event.test.ts +0 -1104
  151. package/src/event.ts +0 -988
  152. package/src/events-config.ts +0 -235
  153. package/src/exporters.ts +0 -165
  154. package/src/filtering-span-processor.test.ts +0 -281
  155. package/src/filtering-span-processor.ts +0 -111
  156. package/src/flatten-attributes.test.ts +0 -76
  157. package/src/flatten-attributes.ts +0 -80
  158. package/src/functional.strict-types.typecheck.ts +0 -53
  159. package/src/functional.test.ts +0 -1464
  160. package/src/functional.ts +0 -2539
  161. package/src/functional.types.test.ts +0 -135
  162. package/src/hook.mjs +0 -15
  163. package/src/http.test.ts +0 -485
  164. package/src/http.ts +0 -424
  165. package/src/index.ts +0 -433
  166. package/src/init-auto-redactor.test.ts +0 -53
  167. package/src/init-redactor.test.ts +0 -8
  168. package/src/init.customization.test.ts +0 -665
  169. package/src/init.integrations.test.ts +0 -399
  170. package/src/init.openllmetry.test.ts +0 -194
  171. package/src/init.protocol.test.ts +0 -215
  172. package/src/init.ts +0 -2439
  173. package/src/instrumentation.test.ts +0 -108
  174. package/src/instrumentation.ts +0 -319
  175. package/src/logger.test.ts +0 -125
  176. package/src/logger.ts +0 -341
  177. package/src/messaging-adapters.test.ts +0 -595
  178. package/src/messaging-adapters.ts +0 -583
  179. package/src/messaging-testing.test.ts +0 -573
  180. package/src/messaging-testing.ts +0 -935
  181. package/src/messaging.test.ts +0 -1646
  182. package/src/messaging.ts +0 -2245
  183. package/src/metric-helpers.ts +0 -47
  184. package/src/metric-testing.ts +0 -197
  185. package/src/metric.ts +0 -446
  186. package/src/metrics.test.ts +0 -241
  187. package/src/node-require.ts +0 -123
  188. package/src/operation-context.ts +0 -93
  189. package/src/parse-error.test.ts +0 -73
  190. package/src/parse-error.ts +0 -112
  191. package/src/posthog-logs.test.ts +0 -115
  192. package/src/posthog-logs.ts +0 -77
  193. package/src/pretty-console-exporter.test.ts +0 -545
  194. package/src/pretty-console-exporter.ts +0 -413
  195. package/src/pretty-log-formatter.test.ts +0 -123
  196. package/src/pretty-log-formatter.ts +0 -210
  197. package/src/processors/canonical-log-line-processor.test.ts +0 -523
  198. package/src/processors/canonical-log-line-processor.ts +0 -396
  199. package/src/processors.ts +0 -152
  200. package/src/rate-limiter.test.ts +0 -199
  201. package/src/rate-limiter.ts +0 -98
  202. package/src/redact-values.test.ts +0 -90
  203. package/src/redact-values.ts +0 -34
  204. package/src/register.ts +0 -37
  205. package/src/request-logger.test.ts +0 -545
  206. package/src/request-logger.ts +0 -342
  207. package/src/sampling.test.ts +0 -1060
  208. package/src/sampling.ts +0 -737
  209. package/src/security-schema.test.ts +0 -45
  210. package/src/security-schema.ts +0 -107
  211. package/src/semantic-conventions.ts +0 -15
  212. package/src/semantic-helpers.test.ts +0 -226
  213. package/src/semantic-helpers.ts +0 -438
  214. package/src/shutdown.test.ts +0 -364
  215. package/src/shutdown.ts +0 -246
  216. package/src/span-name-normalizer.test.ts +0 -377
  217. package/src/span-name-normalizer.ts +0 -213
  218. package/src/stable-hash.ts +0 -27
  219. package/src/structured-error.test.ts +0 -191
  220. package/src/structured-error.ts +0 -157
  221. package/src/stub.integration.test.ts +0 -361
  222. package/src/tail-sampling-processor.test.ts +0 -230
  223. package/src/tail-sampling-processor.ts +0 -55
  224. package/src/test-span-collector.test.ts +0 -234
  225. package/src/test-span-collector.ts +0 -150
  226. package/src/testing.ts +0 -705
  227. package/src/trace-context.test.ts +0 -73
  228. package/src/trace-context.ts +0 -567
  229. package/src/trace-helpers.new.test.ts +0 -278
  230. package/src/trace-helpers.test.ts +0 -290
  231. package/src/trace-helpers.ts +0 -710
  232. package/src/trace-hybrid.test.ts +0 -42
  233. package/src/trace-hybrid.ts +0 -37
  234. package/src/tracer-provider.test.ts +0 -183
  235. package/src/tracer-provider.ts +0 -266
  236. package/src/track.test.ts +0 -154
  237. package/src/track.ts +0 -216
  238. package/src/validate.test.ts +0 -287
  239. package/src/validate.ts +0 -307
  240. package/src/validation-attributes.ts +0 -43
  241. package/src/validation.test.ts +0 -330
  242. package/src/validation.ts +0 -246
  243. package/src/variable-name-inference.test.ts +0 -178
  244. package/src/variable-name-inference.ts +0 -242
  245. package/src/webhook.test.ts +0 -649
  246. package/src/webhook.ts +0 -637
  247. package/src/workflow-distributed.test.ts +0 -786
  248. package/src/workflow-distributed.ts +0 -916
  249. package/src/workflow.async-safety.integration.test.ts +0 -345
  250. package/src/workflow.test.ts +0 -647
  251. package/src/workflow.ts +0 -810
  252. package/src/yaml-config.test.ts +0 -373
  253. package/src/yaml-config.ts +0 -351
@@ -1,916 +0,0 @@
1
- /**
2
- * Distributed workflow tracing with cross-service correlation
3
- *
4
- * Enables tracking workflows that span multiple microservices by propagating
5
- * workflow identity (workflowId, stepName, stepIndex) via baggage in message headers.
6
- *
7
- * Unlike local workflow.ts (which uses AsyncLocalStorage), distributed workflows
8
- * propagate context across network boundaries using W3C baggage.
9
- *
10
- * @example Order fulfillment saga across services
11
- * ```typescript
12
- * // Service A: Order Service
13
- * import { traceDistributedWorkflow, WorkflowBaggage } from 'autotel/workflow-distributed';
14
- * import { traceProducer } from 'autotel/messaging';
15
- *
16
- * export const createOrder = traceDistributedWorkflow({
17
- * name: 'OrderFulfillment',
18
- * workflowIdFrom: (order) => order.id,
19
- * version: '1.0.0',
20
- * })(ctx => async (order: Order) => {
21
- * // Workflow baggage is auto-set
22
- * await publishToInventory(order);
23
- * });
24
- *
25
- * const publishToInventory = traceProducer({
26
- * system: 'kafka',
27
- * destination: 'inventory-requests',
28
- * propagateBaggage: true, // Includes workflow.* baggage
29
- * })(ctx => async (order) => {
30
- * await producer.send({ topic: 'inventory-requests', value: order });
31
- * });
32
- *
33
- * // Service B: Inventory Service
34
- * import { traceDistributedStep, WorkflowBaggage } from 'autotel/workflow-distributed';
35
- *
36
- * export const processInventory = traceDistributedStep({
37
- * name: 'ReserveInventory',
38
- * extractBaggage: true, // Extracts workflow.* from headers
39
- * })(ctx => async (message) => {
40
- * const workflow = WorkflowBaggage.get(ctx);
41
- * // workflow.workflowId === order.id (propagated from Service A)
42
- * console.log(`Processing step for workflow ${workflow.workflowId}`);
43
- * await reserveItems(message.items);
44
- * });
45
- * ```
46
- *
47
- * @module
48
- */
49
-
50
- import { context, propagation, SpanKind } from '@opentelemetry/api';
51
- import { createSafeBaggageSchema } from './business-baggage';
52
- import { emitCorrelatedEvent } from './correlated-events';
53
- import { trace } from './functional';
54
- import type { TraceContext } from './trace-context';
55
-
56
- // ============================================================================
57
- // Workflow Baggage Schema
58
- // ============================================================================
59
-
60
- /**
61
- * Workflow baggage field definitions
62
- */
63
- const workflowBaggageFields = {
64
- /** Unique identifier for the workflow instance */
65
- workflowId: { type: 'string' as const, maxLength: 128, required: true },
66
-
67
- /** Name/type of the workflow (e.g., "OrderFulfillment") */
68
- workflowName: { type: 'string' as const, maxLength: 64, required: true },
69
-
70
- /** Version of the workflow definition */
71
- workflowVersion: { type: 'string' as const, maxLength: 32 },
72
-
73
- /** Current step name */
74
- stepName: { type: 'string' as const, maxLength: 64 },
75
-
76
- /** Current step index (0-based) */
77
- stepIndex: { type: 'number' as const },
78
-
79
- /** Total number of steps (if known) */
80
- totalSteps: { type: 'number' as const },
81
-
82
- /** Parent workflow ID (for sub-workflows) */
83
- parentWorkflowId: { type: 'string' as const, maxLength: 128 },
84
-
85
- /** Correlation ID for external systems */
86
- correlationId: { type: 'string' as const, maxLength: 128 },
87
-
88
- /** Workflow priority */
89
- priority: {
90
- type: 'enum' as const,
91
- values: ['low', 'normal', 'high', 'critical'] as const,
92
- },
93
-
94
- /** Initiating user/system */
95
- initiatedBy: { type: 'string' as const, maxLength: 64 },
96
-
97
- /** Workflow start timestamp (ISO) */
98
- startedAt: { type: 'string' as const, maxLength: 30 },
99
- } as const;
100
-
101
- /**
102
- * Pre-built baggage schema for distributed workflows
103
- *
104
- * Use this to read/write workflow context that propagates across services.
105
- *
106
- * @example Setting workflow baggage
107
- * ```typescript
108
- * WorkflowBaggage.set(ctx, {
109
- * workflowId: 'order-12345',
110
- * workflowName: 'OrderFulfillment',
111
- * stepName: 'ReserveInventory',
112
- * stepIndex: 1,
113
- * });
114
- * ```
115
- *
116
- * @example Reading workflow baggage in downstream service
117
- * ```typescript
118
- * const { workflowId, workflowName, stepIndex } = WorkflowBaggage.get(ctx);
119
- * console.log(`Processing ${workflowName} step ${stepIndex}`);
120
- * ```
121
- */
122
- export const WorkflowBaggage = createSafeBaggageSchema(workflowBaggageFields, {
123
- prefix: 'workflow',
124
- hashHighCardinality: false, // Workflow IDs should be traceable
125
- redactPII: false, // Workflow fields are internal identifiers
126
- });
127
-
128
- /**
129
- * Type for workflow baggage values
130
- */
131
- export type WorkflowBaggageValues = {
132
- workflowId: string;
133
- workflowName: string;
134
- workflowVersion?: string;
135
- stepName?: string;
136
- stepIndex?: number;
137
- totalSteps?: number;
138
- parentWorkflowId?: string;
139
- correlationId?: string;
140
- priority?: 'low' | 'normal' | 'high' | 'critical';
141
- initiatedBy?: string;
142
- startedAt?: string;
143
- };
144
-
145
- // ============================================================================
146
- // Types
147
- // ============================================================================
148
-
149
- /**
150
- * Configuration for distributed workflow tracing
151
- */
152
- export interface DistributedWorkflowConfig {
153
- /** Workflow name/type (e.g., "OrderFulfillment", "UserOnboarding") */
154
- name: string;
155
-
156
- /**
157
- * Extract workflow ID from function arguments
158
- *
159
- * Receives all arguments passed to the workflow function, allowing
160
- * multi-parameter handlers to derive workflow IDs from any argument.
161
- *
162
- * @example Single argument
163
- * ```typescript
164
- * workflowIdFrom: (order) => order.id
165
- * ```
166
- *
167
- * @example Multiple arguments (payload + metadata)
168
- * ```typescript
169
- * workflowIdFrom: (payload, metadata) => metadata.correlationId ?? payload.id
170
- * ```
171
- */
172
- workflowIdFrom: (...args: unknown[]) => string;
173
-
174
- /** Workflow version (e.g., "1.0.0", "2023-01-15") */
175
- version?: string;
176
-
177
- /** Total number of steps if known */
178
- totalSteps?: number;
179
-
180
- /** Parent workflow ID (for sub-workflows) */
181
- parentWorkflowId?: string;
182
-
183
- /** Correlation ID for external systems */
184
- correlationId?: string;
185
-
186
- /** Workflow priority */
187
- priority?: 'low' | 'normal' | 'high' | 'critical';
188
-
189
- /** User/system that initiated the workflow */
190
- initiatedBy?: string;
191
-
192
- /** Additional span attributes */
193
- attributes?: Record<string, string | number | boolean>;
194
-
195
- /** Callback on workflow start */
196
- onStart?: (ctx: DistributedWorkflowContext) => void;
197
-
198
- /** Callback on workflow completion */
199
- onComplete?: (ctx: DistributedWorkflowContext, result: unknown) => void;
200
-
201
- /** Callback on workflow error */
202
- onError?: (ctx: DistributedWorkflowContext, error: Error) => void;
203
- }
204
-
205
- /**
206
- * Configuration for distributed workflow step
207
- */
208
- export interface DistributedStepConfig {
209
- /** Step name (e.g., "ReserveInventory", "ChargePayment") */
210
- name: string;
211
-
212
- /**
213
- * Extract baggage from incoming message/request
214
- *
215
- * If true, reads workflow baggage from current context (assumes already extracted).
216
- * If function, extracts from arguments.
217
- *
218
- * @default true
219
- */
220
- extractBaggage?:
221
- | boolean
222
- | ((args: unknown[]) => WorkflowBaggageValues | null);
223
-
224
- /** Override step index (otherwise uses baggage or auto-increments) */
225
- stepIndex?: number;
226
-
227
- /** Additional span attributes */
228
- attributes?: Record<string, string | number | boolean>;
229
-
230
- /** Whether this step is idempotent (safe to retry) */
231
- idempotent?: boolean;
232
-
233
- /** Whether this step is a compensation/rollback step */
234
- isCompensation?: boolean;
235
-
236
- /** Callback on step start */
237
- onStart?: (ctx: DistributedStepContext) => void;
238
-
239
- /** Callback on step completion */
240
- onComplete?: (ctx: DistributedStepContext, result: unknown) => void;
241
-
242
- /** Callback on step error */
243
- onError?: (ctx: DistributedStepContext, error: Error) => void;
244
- }
245
-
246
- /**
247
- * Extended context for distributed workflow root
248
- */
249
- export interface DistributedWorkflowContext extends TraceContext {
250
- /** The workflow ID */
251
- workflowId: string;
252
-
253
- /** The workflow name */
254
- workflowName: string;
255
-
256
- /** The workflow version */
257
- workflowVersion?: string;
258
-
259
- /** Get workflow baggage for propagation to other services */
260
- getWorkflowBaggage(): WorkflowBaggageValues;
261
-
262
- /** Set additional workflow baggage fields */
263
- setWorkflowBaggage(values: Partial<WorkflowBaggageValues>): void;
264
-
265
- /** Get headers with workflow baggage for outgoing requests */
266
- getWorkflowHeaders(): Record<string, string>;
267
-
268
- /** Record workflow step completion (for progress tracking) */
269
- recordStepProgress(stepName: string, stepIndex: number): void;
270
- }
271
-
272
- /**
273
- * Extended context for distributed workflow step
274
- */
275
- export interface DistributedStepContext extends TraceContext {
276
- /** The workflow ID (from baggage) */
277
- workflowId: string | null;
278
-
279
- /** The workflow name (from baggage) */
280
- workflowName: string | null;
281
-
282
- /** The current step name */
283
- stepName: string;
284
-
285
- /** The current step index */
286
- stepIndex: number | null;
287
-
288
- /** Whether this step is a compensation */
289
- isCompensation: boolean;
290
-
291
- /** Get the full workflow baggage */
292
- getWorkflowBaggage(): WorkflowBaggageValues | null;
293
-
294
- /** Update workflow baggage (e.g., increment step index) */
295
- updateWorkflowBaggage(values: Partial<WorkflowBaggageValues>): void;
296
-
297
- /** Get headers with updated workflow baggage for downstream calls */
298
- getWorkflowHeaders(): Record<string, string>;
299
-
300
- /** Mark step as requiring compensation on failure */
301
- requiresCompensation(compensationData?: Record<string, unknown>): void;
302
- }
303
-
304
- // ============================================================================
305
- // Distributed Workflow Tracer
306
- // ============================================================================
307
-
308
- /**
309
- * Create a traced distributed workflow function
310
- *
311
- * Wraps a function as the entry point for a distributed workflow. Automatically:
312
- * - Generates or extracts workflow ID
313
- * - Sets workflow baggage for downstream propagation
314
- * - Creates root span with workflow attributes
315
- *
316
- * @param config - Workflow configuration
317
- * @returns Factory function for the workflow handler
318
- *
319
- * @example Basic usage
320
- * ```typescript
321
- * export const createOrder = traceDistributedWorkflow({
322
- * name: 'OrderFulfillment',
323
- * workflowIdFrom: (order) => order.id,
324
- * version: '1.0.0',
325
- * })(ctx => async (order: Order) => {
326
- * ctx.recordStepProgress('ValidateOrder', 0);
327
- * await validateOrder(order);
328
- *
329
- * ctx.recordStepProgress('ReserveInventory', 1);
330
- * await publishToInventoryService(order);
331
- *
332
- * return { workflowId: ctx.workflowId, status: 'started' };
333
- * });
334
- * ```
335
- */
336
- export function traceDistributedWorkflow<TArgs extends unknown[], TReturn>(
337
- config: DistributedWorkflowConfig,
338
- ) {
339
- const spanName = `workflow.${config.name}`;
340
-
341
- return (
342
- fnFactory: (
343
- ctx: DistributedWorkflowContext,
344
- ) => (...args: TArgs) => Promise<TReturn>,
345
- ): ((...args: TArgs) => Promise<TReturn>) => {
346
- return trace<TArgs, TReturn>(
347
- { name: spanName, spanKind: SpanKind.INTERNAL },
348
- (baseCtx) => {
349
- return async (...args: TArgs) => {
350
- // Extract workflow ID from arguments (spread to allow multi-arg access)
351
- const workflowId = config.workflowIdFrom(...args);
352
- const startedAt = new Date().toISOString();
353
-
354
- // Initialize workflow baggage
355
- const baggageValues: WorkflowBaggageValues = {
356
- workflowId,
357
- workflowName: config.name,
358
- workflowVersion: config.version,
359
- stepIndex: 0,
360
- totalSteps: config.totalSteps,
361
- parentWorkflowId: config.parentWorkflowId,
362
- correlationId: config.correlationId,
363
- priority: config.priority,
364
- initiatedBy: config.initiatedBy,
365
- startedAt,
366
- };
367
-
368
- // Set baggage
369
- WorkflowBaggage.set(baseCtx, baggageValues);
370
-
371
- // Set span attributes
372
- baseCtx.setAttribute('workflow.id', workflowId);
373
- baseCtx.setAttribute('workflow.name', config.name);
374
- if (config.version) {
375
- baseCtx.setAttribute('workflow.version', config.version);
376
- }
377
- if (config.totalSteps) {
378
- baseCtx.setAttribute('workflow.total_steps', config.totalSteps);
379
- }
380
- if (config.parentWorkflowId) {
381
- baseCtx.setAttribute('workflow.parent_id', config.parentWorkflowId);
382
- }
383
- if (config.priority) {
384
- baseCtx.setAttribute('workflow.priority', config.priority);
385
- }
386
- if (config.initiatedBy) {
387
- baseCtx.setAttribute('workflow.initiated_by', config.initiatedBy);
388
- }
389
- baseCtx.setAttribute('workflow.started_at', startedAt);
390
-
391
- // Apply custom attributes
392
- if (config.attributes) {
393
- for (const [key, value] of Object.entries(config.attributes)) {
394
- baseCtx.setAttribute(key, value);
395
- }
396
- }
397
-
398
- // Create extended context
399
- const workflowCtx: DistributedWorkflowContext = {
400
- ...baseCtx,
401
- workflowId,
402
- workflowName: config.name,
403
- workflowVersion: config.version,
404
-
405
- getWorkflowBaggage(): WorkflowBaggageValues {
406
- return { ...baggageValues };
407
- },
408
-
409
- setWorkflowBaggage(values: Partial<WorkflowBaggageValues>): void {
410
- Object.assign(baggageValues, values);
411
- WorkflowBaggage.set(baseCtx, baggageValues);
412
- },
413
-
414
- getWorkflowHeaders(): Record<string, string> {
415
- const headers: Record<string, string> = {};
416
- const ctx = context.active();
417
- propagation.inject(ctx, headers);
418
- return headers;
419
- },
420
-
421
- recordStepProgress(stepName: string, stepIndex: number): void {
422
- baggageValues.stepName = stepName;
423
- baggageValues.stepIndex = stepIndex;
424
- WorkflowBaggage.set(baseCtx, baggageValues);
425
-
426
- emitCorrelatedEvent(baseCtx, 'workflow.step_progress', {
427
- 'workflow.step.name': stepName,
428
- 'workflow.step.index': stepIndex,
429
- });
430
- },
431
- };
432
-
433
- // Call onStart callback
434
- config.onStart?.(workflowCtx);
435
-
436
- // Add start event
437
- emitCorrelatedEvent(baseCtx, 'workflow.started', {
438
- 'workflow.id': workflowId,
439
- 'workflow.name': config.name,
440
- });
441
-
442
- try {
443
- const userFn = fnFactory(workflowCtx);
444
- const result = await userFn(...args);
445
-
446
- // Call onComplete callback
447
- config.onComplete?.(workflowCtx, result);
448
-
449
- // Add completion event
450
- emitCorrelatedEvent(baseCtx, 'workflow.completed', {
451
- 'workflow.id': workflowId,
452
- });
453
-
454
- return result;
455
- } catch (error) {
456
- // Call onError callback
457
- config.onError?.(workflowCtx, error as Error);
458
-
459
- // Add error event
460
- emitCorrelatedEvent(baseCtx, 'workflow.failed', {
461
- 'workflow.id': workflowId,
462
- 'workflow.error': (error as Error).message,
463
- });
464
-
465
- throw error;
466
- }
467
- };
468
- },
469
- );
470
- };
471
- }
472
-
473
- // ============================================================================
474
- // Distributed Step Tracer
475
- // ============================================================================
476
-
477
- /**
478
- * Create a traced distributed workflow step
479
- *
480
- * Use in downstream services to trace steps that are part of a distributed workflow.
481
- * Automatically extracts workflow baggage from the current context.
482
- *
483
- * @param config - Step configuration
484
- * @returns Factory function for the step handler
485
- *
486
- * @example Consumer in downstream service
487
- * ```typescript
488
- * export const processInventory = traceConsumer({
489
- * system: 'kafka',
490
- * destination: 'inventory-requests',
491
- * extractBaggage: true, // Extracts workflow.* from headers
492
- * })(ctx => {
493
- * // Wrap inner logic with traceDistributedStep
494
- * return traceDistributedStep({
495
- * name: 'ReserveInventory',
496
- * })(stepCtx => async (message) => {
497
- * console.log(`Processing workflow ${stepCtx.workflowId}`);
498
- * await reserveItems(message.items);
499
- * })(message);
500
- * });
501
- * ```
502
- *
503
- * @example Standalone step handler
504
- * ```typescript
505
- * export const reserveInventory = traceDistributedStep({
506
- * name: 'ReserveInventory',
507
- * idempotent: true,
508
- * })(ctx => async (request: InventoryRequest) => {
509
- * if (ctx.workflowId) {
510
- * console.log(`Part of workflow ${ctx.workflowId}, step ${ctx.stepIndex}`);
511
- * }
512
- * return await inventoryService.reserve(request.items);
513
- * });
514
- * ```
515
- */
516
- export function traceDistributedStep<TArgs extends unknown[], TReturn>(
517
- config: DistributedStepConfig,
518
- ) {
519
- const spanName = `workflow.step.${config.name}`;
520
-
521
- return (
522
- fnFactory: (
523
- ctx: DistributedStepContext,
524
- ) => (...args: TArgs) => Promise<TReturn>,
525
- ): ((...args: TArgs) => Promise<TReturn>) => {
526
- return trace<TArgs, TReturn>(
527
- { name: spanName, spanKind: SpanKind.INTERNAL },
528
- (baseCtx) => {
529
- return async (...args: TArgs) => {
530
- // Extract workflow baggage
531
- let baggageValues: WorkflowBaggageValues | null = null;
532
-
533
- const extractBaggage = config.extractBaggage ?? true;
534
- if (typeof extractBaggage === 'function') {
535
- baggageValues = extractBaggage(args);
536
- } else if (extractBaggage) {
537
- // Read from current context
538
- const extracted = WorkflowBaggage.get(baseCtx);
539
- if (extracted.workflowId && extracted.workflowName) {
540
- baggageValues = extracted as WorkflowBaggageValues;
541
- }
542
- }
543
-
544
- // Determine step index
545
- // If explicit stepIndex provided in config, use it
546
- // Otherwise, auto-increment from baggage if available
547
- let stepIndex: number | null;
548
- if (config.stepIndex !== undefined) {
549
- stepIndex = config.stepIndex;
550
- } else if (baggageValues?.stepIndex === undefined) {
551
- stepIndex = null;
552
- } else {
553
- // Auto-increment from previous step
554
- stepIndex = baggageValues.stepIndex + 1;
555
- }
556
-
557
- // Update baggage with current step
558
- if (baggageValues) {
559
- baggageValues.stepName = config.name;
560
- if (stepIndex !== null) {
561
- baggageValues.stepIndex = stepIndex;
562
- }
563
- WorkflowBaggage.set(baseCtx, baggageValues);
564
- }
565
-
566
- // Set span attributes
567
- baseCtx.setAttribute('workflow.step.name', config.name);
568
- if (stepIndex !== null) {
569
- baseCtx.setAttribute('workflow.step.index', stepIndex);
570
- }
571
- if (config.idempotent !== undefined) {
572
- baseCtx.setAttribute('workflow.step.idempotent', config.idempotent);
573
- }
574
- if (config.isCompensation) {
575
- baseCtx.setAttribute('workflow.step.is_compensation', true);
576
- }
577
-
578
- // Add workflow context attributes if available
579
- if (baggageValues) {
580
- baseCtx.setAttribute('workflow.id', baggageValues.workflowId);
581
- baseCtx.setAttribute('workflow.name', baggageValues.workflowName);
582
- if (baggageValues.workflowVersion) {
583
- baseCtx.setAttribute(
584
- 'workflow.version',
585
- baggageValues.workflowVersion,
586
- );
587
- }
588
- if (baggageValues.totalSteps) {
589
- baseCtx.setAttribute(
590
- 'workflow.total_steps',
591
- baggageValues.totalSteps,
592
- );
593
- }
594
- }
595
-
596
- // Apply custom attributes
597
- if (config.attributes) {
598
- for (const [key, value] of Object.entries(config.attributes)) {
599
- baseCtx.setAttribute(key, value);
600
- }
601
- }
602
-
603
- // Compensation data storage
604
- let compensationData: Record<string, unknown> | undefined;
605
-
606
- // Create extended context
607
- const stepCtx: DistributedStepContext = {
608
- ...baseCtx,
609
- workflowId: baggageValues?.workflowId ?? null,
610
- workflowName: baggageValues?.workflowName ?? null,
611
- stepName: config.name,
612
- stepIndex,
613
- isCompensation: config.isCompensation ?? false,
614
-
615
- getWorkflowBaggage(): WorkflowBaggageValues | null {
616
- return baggageValues ? { ...baggageValues } : null;
617
- },
618
-
619
- updateWorkflowBaggage(
620
- values: Partial<WorkflowBaggageValues>,
621
- ): void {
622
- if (baggageValues) {
623
- Object.assign(baggageValues, values);
624
- WorkflowBaggage.set(baseCtx, baggageValues);
625
- }
626
- },
627
-
628
- getWorkflowHeaders(): Record<string, string> {
629
- const headers: Record<string, string> = {};
630
- const ctx = context.active();
631
- propagation.inject(ctx, headers);
632
- return headers;
633
- },
634
-
635
- requiresCompensation(data?: Record<string, unknown>): void {
636
- compensationData = data;
637
- baseCtx.setAttribute('workflow.step.requires_compensation', true);
638
- emitCorrelatedEvent(
639
- baseCtx,
640
- 'workflow.step.compensation_registered',
641
- {
642
- 'workflow.step.name': config.name,
643
- ...(data && {
644
- 'workflow.step.compensation_data': JSON.stringify(data),
645
- }),
646
- },
647
- );
648
- },
649
- };
650
-
651
- // Call onStart callback
652
- config.onStart?.(stepCtx);
653
-
654
- // Add start event
655
- emitCorrelatedEvent(baseCtx, 'workflow.step.started', {
656
- 'workflow.step.name': config.name,
657
- ...(baggageValues && { 'workflow.id': baggageValues.workflowId }),
658
- });
659
-
660
- try {
661
- const userFn = fnFactory(stepCtx);
662
- const result = await userFn(...args);
663
-
664
- // Call onComplete callback
665
- config.onComplete?.(stepCtx, result);
666
-
667
- // Add completion event
668
- emitCorrelatedEvent(baseCtx, 'workflow.step.completed', {
669
- 'workflow.step.name': config.name,
670
- });
671
-
672
- return result;
673
- } catch (error) {
674
- // Call onError callback
675
- config.onError?.(stepCtx, error as Error);
676
-
677
- // Add error event with compensation info if registered
678
- emitCorrelatedEvent(baseCtx, 'workflow.step.failed', {
679
- 'workflow.step.name': config.name,
680
- 'workflow.step.error': (error as Error).message,
681
- ...(compensationData && {
682
- 'workflow.step.requires_compensation': true,
683
- }),
684
- });
685
-
686
- throw error;
687
- }
688
- };
689
- },
690
- );
691
- };
692
- }
693
-
694
- // ============================================================================
695
- // Utility Functions
696
- // ============================================================================
697
-
698
- /**
699
- * Generate a unique workflow ID
700
- *
701
- * @param prefix - Optional prefix for the ID
702
- * @returns A unique workflow ID
703
- *
704
- * @example
705
- * ```typescript
706
- * const workflowId = generateWorkflowId('order'); // "order-abc123def456"
707
- * ```
708
- */
709
- export function generateWorkflowId(prefix?: string): string {
710
- const random = Math.random().toString(36).slice(2, 15);
711
- const timestamp = Date.now().toString(36);
712
- const id = `${timestamp}-${random}`;
713
- return prefix ? `${prefix}-${id}` : id;
714
- }
715
-
716
- /**
717
- * Check if the current context is part of a distributed workflow
718
- *
719
- * @param ctx - The trace context
720
- * @returns True if workflow baggage is present
721
- */
722
- export function isInDistributedWorkflow(ctx: TraceContext): boolean {
723
- const baggage = WorkflowBaggage.get(ctx);
724
- return !!(baggage.workflowId && baggage.workflowName);
725
- }
726
-
727
- /**
728
- * Get workflow progress information
729
- *
730
- * @param ctx - The trace context
731
- * @returns Progress info or null if not in a workflow
732
- */
733
- export function getWorkflowProgress(ctx: TraceContext): {
734
- workflowId: string;
735
- workflowName: string;
736
- currentStep: string | null;
737
- currentStepIndex: number | null;
738
- totalSteps: number | null;
739
- percentComplete: number | null;
740
- } | null {
741
- const baggage = WorkflowBaggage.get(ctx);
742
- if (!baggage.workflowId || !baggage.workflowName) {
743
- return null;
744
- }
745
-
746
- const percentComplete =
747
- baggage.totalSteps && baggage.stepIndex !== undefined
748
- ? Math.round(((baggage.stepIndex + 1) / baggage.totalSteps) * 100)
749
- : null;
750
-
751
- return {
752
- workflowId: baggage.workflowId,
753
- workflowName: baggage.workflowName,
754
- currentStep: baggage.stepName ?? null,
755
- currentStepIndex: baggage.stepIndex ?? null,
756
- totalSteps: baggage.totalSteps ?? null,
757
- percentComplete,
758
- };
759
- }
760
-
761
- /**
762
- * Create workflow correlation headers for manual propagation
763
- *
764
- * Use when you need to manually add workflow context to outgoing requests.
765
- *
766
- * @param values - Workflow baggage values
767
- * @returns Headers object with workflow baggage
768
- *
769
- * @example
770
- * ```typescript
771
- * const headers = createWorkflowHeaders({
772
- * workflowId: 'order-123',
773
- * workflowName: 'OrderFulfillment',
774
- * stepIndex: 2,
775
- * });
776
- *
777
- * await fetch('/api/inventory', { headers });
778
- * ```
779
- */
780
- export function createWorkflowHeaders(
781
- values: Partial<WorkflowBaggageValues>,
782
- ): Record<string, string> {
783
- const headers: Record<string, string> = {};
784
-
785
- // Build baggage string
786
- const baggageEntries: string[] = [];
787
-
788
- if (values.workflowId) {
789
- baggageEntries.push(
790
- `workflow.workflowId=${encodeURIComponent(values.workflowId)}`,
791
- );
792
- }
793
- if (values.workflowName) {
794
- baggageEntries.push(
795
- `workflow.workflowName=${encodeURIComponent(values.workflowName)}`,
796
- );
797
- }
798
- if (values.workflowVersion) {
799
- baggageEntries.push(
800
- `workflow.workflowVersion=${encodeURIComponent(values.workflowVersion)}`,
801
- );
802
- }
803
- if (values.stepName) {
804
- baggageEntries.push(
805
- `workflow.stepName=${encodeURIComponent(values.stepName)}`,
806
- );
807
- }
808
- if (values.stepIndex !== undefined) {
809
- baggageEntries.push(`workflow.stepIndex=${values.stepIndex}`);
810
- }
811
- if (values.totalSteps !== undefined) {
812
- baggageEntries.push(`workflow.totalSteps=${values.totalSteps}`);
813
- }
814
- if (values.priority) {
815
- baggageEntries.push(`workflow.priority=${values.priority}`);
816
- }
817
- if (values.correlationId) {
818
- baggageEntries.push(
819
- `workflow.correlationId=${encodeURIComponent(values.correlationId)}`,
820
- );
821
- }
822
- if (values.parentWorkflowId) {
823
- baggageEntries.push(
824
- `workflow.parentWorkflowId=${encodeURIComponent(values.parentWorkflowId)}`,
825
- );
826
- }
827
- if (values.initiatedBy) {
828
- baggageEntries.push(
829
- `workflow.initiatedBy=${encodeURIComponent(values.initiatedBy)}`,
830
- );
831
- }
832
- if (values.startedAt) {
833
- baggageEntries.push(
834
- `workflow.startedAt=${encodeURIComponent(values.startedAt)}`,
835
- );
836
- }
837
-
838
- if (baggageEntries.length > 0) {
839
- headers['baggage'] = baggageEntries.join(',');
840
- }
841
-
842
- return headers;
843
- }
844
-
845
- /**
846
- * Parse workflow context from baggage header
847
- *
848
- * @param baggageHeader - The baggage header value
849
- * @returns Parsed workflow values or null
850
- */
851
- export function parseWorkflowFromBaggage(
852
- baggageHeader: string,
853
- ): Partial<WorkflowBaggageValues> | null {
854
- if (!baggageHeader) {
855
- return null;
856
- }
857
-
858
- const values: Partial<WorkflowBaggageValues> = {};
859
- const entries = baggageHeader.split(',');
860
-
861
- for (const entry of entries) {
862
- const [key, value] = entry.trim().split('=');
863
- if (!key || !value) continue;
864
-
865
- const decodedValue = decodeURIComponent(value);
866
-
867
- switch (key) {
868
- case 'workflow.workflowId': {
869
- values.workflowId = decodedValue;
870
- break;
871
- }
872
- case 'workflow.workflowName': {
873
- values.workflowName = decodedValue;
874
- break;
875
- }
876
- case 'workflow.workflowVersion': {
877
- values.workflowVersion = decodedValue;
878
- break;
879
- }
880
- case 'workflow.stepName': {
881
- values.stepName = decodedValue;
882
- break;
883
- }
884
- case 'workflow.stepIndex': {
885
- values.stepIndex = Number.parseInt(decodedValue, 10);
886
- break;
887
- }
888
- case 'workflow.totalSteps': {
889
- values.totalSteps = Number.parseInt(decodedValue, 10);
890
- break;
891
- }
892
- case 'workflow.priority': {
893
- values.priority = decodedValue as WorkflowBaggageValues['priority'];
894
- break;
895
- }
896
- case 'workflow.correlationId': {
897
- values.correlationId = decodedValue;
898
- break;
899
- }
900
- case 'workflow.parentWorkflowId': {
901
- values.parentWorkflowId = decodedValue;
902
- break;
903
- }
904
- case 'workflow.initiatedBy': {
905
- values.initiatedBy = decodedValue;
906
- break;
907
- }
908
- case 'workflow.startedAt': {
909
- values.startedAt = decodedValue;
910
- break;
911
- }
912
- }
913
- }
914
-
915
- return Object.keys(values).length > 0 ? values : null;
916
- }