@hatchet-dev/typescript-sdk 1.17.2 → 1.19.0

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 (35) hide show
  1. package/clients/event/event-client.d.ts +8 -0
  2. package/clients/event/event-client.js +8 -0
  3. package/clients/hatchet-client/client-config.d.ts +45 -0
  4. package/clients/hatchet-client/client-config.js +14 -1
  5. package/opentelemetry/hatchet-exporter.d.ts +36 -0
  6. package/opentelemetry/hatchet-exporter.js +145 -0
  7. package/opentelemetry/hatchet-span-context.d.ts +22 -0
  8. package/opentelemetry/hatchet-span-context.js +27 -0
  9. package/opentelemetry/index.d.ts +3 -0
  10. package/opentelemetry/index.js +7 -0
  11. package/opentelemetry/instrumentor.d.ts +78 -0
  12. package/opentelemetry/instrumentor.js +592 -0
  13. package/opentelemetry/types.d.ts +6 -0
  14. package/opentelemetry/types.js +8 -0
  15. package/package.json +6 -1
  16. package/util/config-loader/config-loader.d.ts +1 -0
  17. package/util/config-loader/config-loader.js +18 -4
  18. package/util/opentelemetry.d.ts +34 -0
  19. package/util/opentelemetry.js +38 -0
  20. package/v1/client/admin.d.ts +6 -0
  21. package/v1/client/admin.js +6 -0
  22. package/v1/client/features/schedules.d.ts +3 -0
  23. package/v1/client/features/schedules.js +3 -0
  24. package/v1/client/worker/worker-internal.d.ts +10 -2
  25. package/v1/client/worker/worker-internal.js +13 -2
  26. package/v1/examples/opentelemetry_instrumentation/run.d.ts +1 -0
  27. package/v1/examples/opentelemetry_instrumentation/run.js +57 -0
  28. package/v1/examples/opentelemetry_instrumentation/setup.d.ts +9 -0
  29. package/v1/examples/opentelemetry_instrumentation/setup.js +26 -0
  30. package/v1/examples/opentelemetry_instrumentation/worker.d.ts +7 -0
  31. package/v1/examples/opentelemetry_instrumentation/worker.js +123 -0
  32. package/v1/examples/webhooks/workflow.d.ts +61 -0
  33. package/v1/examples/webhooks/workflow.js +58 -1
  34. package/version.d.ts +1 -1
  35. package/version.js +1 -1
@@ -0,0 +1,592 @@
1
+ "use strict";
2
+ /**
3
+ * Hatchet OpenTelemetry Instrumentor
4
+ *
5
+ * This module provides automatic instrumentation for Hatchet SDK operations
6
+ * including workflow runs, event pushes, and step executions.
7
+ *
8
+ * The instrumentor follows the OpenTelemetry instrumentation pattern,
9
+ * patching module prototypes to automatically instrument all instances.
10
+ */
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.HatchetInstrumentor = void 0;
22
+ const version_1 = require("../version");
23
+ const opentelemetry_1 = require("../util/opentelemetry");
24
+ const parse_1 = require("../util/parse");
25
+ const types_1 = require("./types");
26
+ const hatchet_span_context_1 = require("./hatchet-span-context");
27
+ try {
28
+ require.resolve('@opentelemetry/api');
29
+ require.resolve('@opentelemetry/instrumentation');
30
+ }
31
+ catch (_a) {
32
+ throw new Error('To use HatchetInstrumentor, you must install OpenTelemetry packages: npm install @opentelemetry/api @opentelemetry/instrumentation');
33
+ }
34
+ /* eslint-disable @typescript-eslint/no-require-imports */
35
+ const otelApi = require('@opentelemetry/api');
36
+ const otelInstrumentation = require('@opentelemetry/instrumentation');
37
+ /* eslint-enable @typescript-eslint/no-require-imports */
38
+ const { context, propagation, SpanKind, SpanStatusCode, diag } = otelApi;
39
+ const { InstrumentationBase, InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, isWrapped, } = otelInstrumentation;
40
+ const INSTRUMENTOR_NAME = '@hatchet-dev/typescript-sdk';
41
+ // FIXME: refactor version check to use the new pattern introduced in #2954
42
+ const SUPPORTED_VERSIONS = ['>=1.16.0'];
43
+ function extractContext(carrier) {
44
+ return propagation.extract(context.active(), carrier !== null && carrier !== void 0 ? carrier : {});
45
+ }
46
+ function injectContext(carrier) {
47
+ propagation.inject(context.active(), carrier);
48
+ }
49
+ function getActionOtelAttributes(action, excludedAttributes = [], workerId) {
50
+ const attributes = {
51
+ [opentelemetry_1.OTelAttribute.TENANT_ID]: action.tenantId,
52
+ [opentelemetry_1.OTelAttribute.WORKER_ID]: workerId,
53
+ [opentelemetry_1.OTelAttribute.WORKFLOW_RUN_ID]: action.workflowRunId,
54
+ [opentelemetry_1.OTelAttribute.STEP_ID]: action.taskId,
55
+ [opentelemetry_1.OTelAttribute.STEP_RUN_ID]: action.taskRunExternalId,
56
+ [opentelemetry_1.OTelAttribute.RETRY_COUNT]: action.retryCount,
57
+ [opentelemetry_1.OTelAttribute.PARENT_WORKFLOW_RUN_ID]: action.parentWorkflowRunId,
58
+ [opentelemetry_1.OTelAttribute.CHILD_WORKFLOW_INDEX]: action.childWorkflowIndex,
59
+ [opentelemetry_1.OTelAttribute.CHILD_WORKFLOW_KEY]: action.childWorkflowKey,
60
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: action.actionPayload,
61
+ [opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: action.jobName,
62
+ [opentelemetry_1.OTelAttribute.ACTION_NAME]: action.actionId,
63
+ [opentelemetry_1.OTelAttribute.STEP_NAME]: action.taskName,
64
+ [opentelemetry_1.OTelAttribute.WORKFLOW_ID]: action.workflowId,
65
+ [opentelemetry_1.OTelAttribute.WORKFLOW_VERSION_ID]: action.workflowVersionId,
66
+ };
67
+ const filtered = { instrumentor: 'hatchet' };
68
+ for (const [key, value] of Object.entries(attributes)) {
69
+ if (!excludedAttributes.includes(key) && value !== undefined && value !== '') {
70
+ filtered[key] = value;
71
+ }
72
+ }
73
+ return filtered;
74
+ }
75
+ function filterAttributes(attributes, excludedAttributes = []) {
76
+ const filtered = { instrumentor: 'hatchet' };
77
+ for (const [key, value] of Object.entries(attributes)) {
78
+ if (!excludedAttributes.includes(key) &&
79
+ value !== undefined &&
80
+ value !== null &&
81
+ value !== '' &&
82
+ value !== '{}' &&
83
+ value !== '[]') {
84
+ filtered[`hatchet.${key}`] =
85
+ typeof value === 'object' ? JSON.stringify(value) : value;
86
+ }
87
+ }
88
+ return filtered;
89
+ }
90
+ /**
91
+ * HatchetInstrumentor provides OpenTelemetry instrumentation for Hatchet SDK v1.
92
+ *
93
+ * It automatically instruments:
94
+ * - Workflow runs (runWorkflow, runWorkflows)
95
+ * - Scheduled workflow runs (schedules.create)
96
+ * - Event pushes (push, bulkPush)
97
+ * - Step executions (handleStartStepRun, handleCancelStepRun)
98
+ *
99
+ * Traceparent context is automatically propagated through metadata.
100
+ *
101
+ * The instrumentor uses the global tracer/meter providers by default.
102
+ * Use `setTracerProvider()` and `setMeterProvider()` to configure custom providers.
103
+ */
104
+ class HatchetInstrumentor extends InstrumentationBase {
105
+ constructor(config = {}) {
106
+ const mergedConfig = Object.assign(Object.assign({}, types_1.DEFAULT_CONFIG), config);
107
+ super(INSTRUMENTOR_NAME, version_1.HATCHET_VERSION, mergedConfig);
108
+ this._getConfig = () => this.getConfig();
109
+ if (mergedConfig.enableHatchetCollector) {
110
+ this._setupHatchetCollector(config.clientConfig, config.bspConfig);
111
+ }
112
+ }
113
+ /**
114
+ * Sets up the Hatchet OTLP exporter on the current TracerProvider.
115
+ * Loads client config from environment if not provided.
116
+ */
117
+ _setupHatchetCollector(clientConfig, bspConfig) {
118
+ try {
119
+ /* eslint-disable @typescript-eslint/no-require-imports */
120
+ const { createHatchetSpanProcessor } = require('./hatchet-exporter.js');
121
+ let config = clientConfig;
122
+ if (!config) {
123
+ const { ConfigLoader } = require('../util/config-loader/config-loader');
124
+ config = ConfigLoader.loadClientConfig();
125
+ }
126
+ const processor = createHatchetSpanProcessor(config, bspConfig);
127
+ try {
128
+ const sdkTrace = require('@opentelemetry/sdk-trace-base');
129
+ /* eslint-enable @typescript-eslint/no-require-imports */
130
+ const globalProvider = otelApi.trace.getTracerProvider();
131
+ if (!(globalProvider instanceof sdkTrace.BasicTracerProvider)) {
132
+ const sdkTracerProvider = new sdkTrace.BasicTracerProvider({
133
+ spanProcessors: [processor],
134
+ });
135
+ otelApi.trace.setGlobalTracerProvider(sdkTracerProvider);
136
+ }
137
+ else {
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ globalProvider.addSpanProcessor(processor);
140
+ }
141
+ }
142
+ catch (_a) {
143
+ diag.warn('hatchet instrumentation: @opentelemetry/sdk-trace-base is required for enableHatchetCollector');
144
+ return;
145
+ }
146
+ diag.info('hatchet instrumentation: Hatchet OTLP collector enabled');
147
+ }
148
+ catch (e) {
149
+ diag.warn(`hatchet instrumentation: Failed to set up Hatchet collector: ${e}`);
150
+ }
151
+ }
152
+ setConfig(config = {}) {
153
+ super.setConfig(Object.assign(Object.assign({}, types_1.DEFAULT_CONFIG), config));
154
+ }
155
+ init() {
156
+ const eventClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/clients/event/event-client.js', SUPPORTED_VERSIONS, this.patchEventClient.bind(this), this.unpatchEventClient.bind(this));
157
+ const adminClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/admin.js', SUPPORTED_VERSIONS, this.patchAdminClient.bind(this), this.unpatchAdminClient.bind(this));
158
+ const scheduleClientModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/features/schedules.js', SUPPORTED_VERSIONS, this.patchScheduleClient.bind(this), this.unpatchScheduleClient.bind(this));
159
+ const workerModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/worker/worker-internal.js', SUPPORTED_VERSIONS, this.patchWorker.bind(this), this.unpatchWorker.bind(this));
160
+ const durableContextModuleFile = new InstrumentationNodeModuleFile('@hatchet-dev/typescript-sdk/v1/client/worker/context.js', SUPPORTED_VERSIONS, this.patchDurableContext.bind(this), this.unpatchDurableContext.bind(this));
161
+ const moduleDefinition = new InstrumentationNodeModuleDefinition(INSTRUMENTOR_NAME, SUPPORTED_VERSIONS, undefined, undefined, [
162
+ eventClientModuleFile,
163
+ adminClientModuleFile,
164
+ workerModuleFile,
165
+ scheduleClientModuleFile,
166
+ durableContextModuleFile,
167
+ ]);
168
+ return [moduleDefinition];
169
+ }
170
+ patchEventClient(moduleExports, _moduleVersion) {
171
+ var _a;
172
+ const exports = moduleExports;
173
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.EventClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
174
+ diag.debug('hatchet instrumentation: EventClient not found in module exports');
175
+ return moduleExports;
176
+ }
177
+ this._patchPushEvent(exports.EventClient.prototype);
178
+ this._patchBulkPushEvent(exports.EventClient.prototype);
179
+ return moduleExports;
180
+ }
181
+ unpatchEventClient(moduleExports, _moduleVersion) {
182
+ var _a;
183
+ const exports = moduleExports;
184
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.EventClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
185
+ return moduleExports;
186
+ }
187
+ if (isWrapped(exports.EventClient.prototype.push)) {
188
+ this._unwrap(exports.EventClient.prototype, 'push');
189
+ }
190
+ if (isWrapped(exports.EventClient.prototype.bulkPush)) {
191
+ this._unwrap(exports.EventClient.prototype, 'bulkPush');
192
+ }
193
+ return moduleExports;
194
+ }
195
+ _patchPushEvent(prototype) {
196
+ if (isWrapped(prototype.push)) {
197
+ this._unwrap(prototype, 'push');
198
+ }
199
+ const { tracer, _getConfig: getConfig } = this;
200
+ this._wrap(prototype, 'push', (original) => {
201
+ return function wrappedPush(type, input, options = {}) {
202
+ const attributes = filterAttributes({
203
+ [opentelemetry_1.OTelAttribute.EVENT_KEY]: type,
204
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input),
205
+ [opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: options.additionalMetadata
206
+ ? JSON.stringify(options.additionalMetadata)
207
+ : undefined,
208
+ [opentelemetry_1.OTelAttribute.PRIORITY]: options.priority,
209
+ [opentelemetry_1.OTelAttribute.FILTER_SCOPE]: options.scope,
210
+ }, getConfig().excludedAttributes);
211
+ return tracer.startActiveSpan('hatchet.push_event', {
212
+ kind: SpanKind.PRODUCER,
213
+ attributes,
214
+ }, (span) => {
215
+ var _a;
216
+ const enhancedMetadata = Object.assign({}, ((_a = options.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
217
+ injectContext(enhancedMetadata);
218
+ const enhancedOptions = Object.assign(Object.assign({}, options), { additionalMetadata: enhancedMetadata });
219
+ const result = original.call(this, type, input, enhancedOptions);
220
+ return result.finally(() => {
221
+ span.end();
222
+ });
223
+ });
224
+ };
225
+ });
226
+ }
227
+ _patchBulkPushEvent(prototype) {
228
+ if (isWrapped(prototype.bulkPush)) {
229
+ this._unwrap(prototype, 'bulkPush');
230
+ }
231
+ const { tracer, _getConfig: getConfig } = this;
232
+ this._wrap(prototype, 'bulkPush', (original) => {
233
+ return function wrappedBulkPush(type, inputs, options = {}) {
234
+ const attributes = filterAttributes({
235
+ [opentelemetry_1.OTelAttribute.EVENT_KEY]: type,
236
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(inputs),
237
+ [opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: options.additionalMetadata
238
+ ? JSON.stringify(options.additionalMetadata)
239
+ : undefined,
240
+ [opentelemetry_1.OTelAttribute.PRIORITY]: options.priority,
241
+ }, getConfig().excludedAttributes);
242
+ return tracer.startActiveSpan('hatchet.bulk_push_event', {
243
+ kind: SpanKind.PRODUCER,
244
+ attributes,
245
+ }, (span) => {
246
+ const enhancedInputs = inputs.map((input) => {
247
+ var _a;
248
+ const enhancedMetadata = Object.assign({}, ((_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
249
+ injectContext(enhancedMetadata);
250
+ return Object.assign(Object.assign({}, input), { additionalMetadata: enhancedMetadata });
251
+ });
252
+ const result = original.call(this, type, enhancedInputs, options);
253
+ return result.finally(() => {
254
+ span.end();
255
+ });
256
+ });
257
+ };
258
+ });
259
+ }
260
+ patchAdminClient(moduleExports, _moduleVersion) {
261
+ var _a;
262
+ const exports = moduleExports;
263
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.AdminClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
264
+ diag.debug('hatchet instrumentation: AdminClient not found in module exports');
265
+ return moduleExports;
266
+ }
267
+ this._patchRunWorkflow(exports.AdminClient.prototype);
268
+ this._patchRunWorkflows(exports.AdminClient.prototype);
269
+ return moduleExports;
270
+ }
271
+ unpatchAdminClient(moduleExports, _moduleVersion) {
272
+ var _a;
273
+ const exports = moduleExports;
274
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.AdminClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
275
+ return moduleExports;
276
+ }
277
+ if (isWrapped(exports.AdminClient.prototype.runWorkflow)) {
278
+ this._unwrap(exports.AdminClient.prototype, 'runWorkflow');
279
+ }
280
+ if (isWrapped(exports.AdminClient.prototype.runWorkflows)) {
281
+ this._unwrap(exports.AdminClient.prototype, 'runWorkflows');
282
+ }
283
+ return moduleExports;
284
+ }
285
+ _patchRunWorkflow(prototype) {
286
+ if (isWrapped(prototype.runWorkflow)) {
287
+ this._unwrap(prototype, 'runWorkflow');
288
+ }
289
+ const { tracer, _getConfig: getConfig } = this;
290
+ this._wrap(prototype, 'runWorkflow', (original) => {
291
+ return function wrappedRunWorkflow(workflowName, input, options) {
292
+ return __awaiter(this, void 0, void 0, function* () {
293
+ const attributes = filterAttributes({
294
+ [opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: workflowName,
295
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input),
296
+ [opentelemetry_1.OTelAttribute.PARENT_ID]: options === null || options === void 0 ? void 0 : options.parentId,
297
+ [opentelemetry_1.OTelAttribute.PARENT_STEP_RUN_ID]: options === null || options === void 0 ? void 0 : options.parentStepRunId,
298
+ [opentelemetry_1.OTelAttribute.CHILD_INDEX]: options === null || options === void 0 ? void 0 : options.childIndex,
299
+ [opentelemetry_1.OTelAttribute.CHILD_KEY]: options === null || options === void 0 ? void 0 : options.childKey,
300
+ [opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: (options === null || options === void 0 ? void 0 : options.additionalMetadata)
301
+ ? JSON.stringify(options.additionalMetadata)
302
+ : undefined,
303
+ [opentelemetry_1.OTelAttribute.PRIORITY]: options === null || options === void 0 ? void 0 : options.priority,
304
+ [opentelemetry_1.OTelAttribute.DESIRED_WORKER_ID]: options === null || options === void 0 ? void 0 : options.desiredWorkerId,
305
+ }, getConfig().excludedAttributes);
306
+ return tracer.startActiveSpan('hatchet.run_workflow', {
307
+ kind: SpanKind.PRODUCER,
308
+ attributes,
309
+ }, (span) => {
310
+ var _a;
311
+ const enhancedMetadata = Object.assign({}, ((_a = options === null || options === void 0 ? void 0 : options.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
312
+ injectContext(enhancedMetadata);
313
+ const enhancedOptions = Object.assign(Object.assign({}, options), { additionalMetadata: enhancedMetadata });
314
+ return original
315
+ .call(this, workflowName, input, enhancedOptions)
316
+ .catch((error) => {
317
+ span.recordException(error);
318
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
319
+ throw error;
320
+ })
321
+ .finally(() => {
322
+ span.end();
323
+ });
324
+ });
325
+ });
326
+ };
327
+ });
328
+ }
329
+ _patchRunWorkflows(prototype) {
330
+ if (isWrapped(prototype.runWorkflows)) {
331
+ this._unwrap(prototype, 'runWorkflows');
332
+ }
333
+ const { tracer, _getConfig: getConfig } = this;
334
+ this._wrap(prototype, 'runWorkflows', (original) => {
335
+ return function wrappedRunWorkflows(workflowRuns, batchSize) {
336
+ return __awaiter(this, void 0, void 0, function* () {
337
+ const attributes = filterAttributes({
338
+ [opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: JSON.stringify(workflowRuns.map((r) => r.workflowName)),
339
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(workflowRuns),
340
+ }, getConfig().excludedAttributes);
341
+ return tracer.startActiveSpan('hatchet.run_workflows', {
342
+ kind: SpanKind.PRODUCER,
343
+ attributes,
344
+ }, (span) => {
345
+ const enhancedWorkflowRuns = workflowRuns.map((run) => {
346
+ var _a, _b;
347
+ const enhancedMetadata = Object.assign({}, ((_b = (_a = run.options) === null || _a === void 0 ? void 0 : _a.additionalMetadata) !== null && _b !== void 0 ? _b : {}));
348
+ injectContext(enhancedMetadata);
349
+ return Object.assign(Object.assign({}, run), { options: Object.assign(Object.assign({}, run.options), { additionalMetadata: enhancedMetadata }) });
350
+ });
351
+ return original
352
+ .call(this, enhancedWorkflowRuns, batchSize)
353
+ .catch((error) => {
354
+ span.recordException(error);
355
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
356
+ throw error;
357
+ })
358
+ .finally(() => {
359
+ span.end();
360
+ });
361
+ });
362
+ });
363
+ };
364
+ });
365
+ }
366
+ patchWorker(moduleExports, _moduleVersion) {
367
+ var _a;
368
+ const exports = moduleExports;
369
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.InternalWorker) === null || _a === void 0 ? void 0 : _a.prototype)) {
370
+ diag.debug('hatchet instrumentation: InternalWorker not found in module exports');
371
+ return moduleExports;
372
+ }
373
+ this._patchHandleStartStepRun(exports.InternalWorker.prototype);
374
+ this._patchHandleCancelStepRun(exports.InternalWorker.prototype);
375
+ return moduleExports;
376
+ }
377
+ unpatchWorker(moduleExports, _moduleVersion) {
378
+ var _a;
379
+ const exports = moduleExports;
380
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.InternalWorker) === null || _a === void 0 ? void 0 : _a.prototype)) {
381
+ return moduleExports;
382
+ }
383
+ if (isWrapped(exports.InternalWorker.prototype.handleStartStepRun)) {
384
+ this._unwrap(exports.InternalWorker.prototype, 'handleStartStepRun');
385
+ }
386
+ if (isWrapped(exports.InternalWorker.prototype.handleCancelStepRun)) {
387
+ this._unwrap(exports.InternalWorker.prototype, 'handleCancelStepRun');
388
+ }
389
+ return moduleExports;
390
+ }
391
+ // IMPORTANT: Keep this wrapper's signature in sync with InternalWorker.handleStartStepRun
392
+ _patchHandleStartStepRun(prototype) {
393
+ if (isWrapped(prototype.handleStartStepRun)) {
394
+ this._unwrap(prototype, 'handleStartStepRun');
395
+ }
396
+ const { tracer, _getConfig: getConfig } = this;
397
+ this._wrap(prototype, 'handleStartStepRun', (original) => {
398
+ return function wrappedHandleStartStepRun(action) {
399
+ return __awaiter(this, void 0, void 0, function* () {
400
+ const additionalMetadata = action.additionalMetadata
401
+ ? (0, parse_1.parseJSON)(action.additionalMetadata)
402
+ : undefined;
403
+ const parentContext = extractContext(additionalMetadata);
404
+ const attributes = getActionOtelAttributes(action, getConfig().excludedAttributes, this.workerId);
405
+ let spanName = 'hatchet.start_step_run';
406
+ if (getConfig().includeTaskNameInSpanName) {
407
+ spanName += `.${action.actionId}`;
408
+ }
409
+ // Store hatchet.* attributes in async context so the SpanProcessor
410
+ // can inject them into child spans (mirrors Go/Python attribute propagation).
411
+ const hatchetAttrs = {};
412
+ for (const [key, value] of Object.entries(attributes)) {
413
+ if (value !== undefined) {
414
+ hatchetAttrs[`hatchet.${key}`] = value;
415
+ }
416
+ }
417
+ (0, hatchet_span_context_1.setHatchetSpanAttributes)(hatchetAttrs);
418
+ return tracer.startActiveSpan(spanName, {
419
+ kind: SpanKind.CONSUMER,
420
+ attributes,
421
+ }, parentContext, (span) => {
422
+ return original
423
+ .call(this, action)
424
+ .then((taskError) => {
425
+ if (taskError instanceof Error) {
426
+ span.recordException(taskError);
427
+ span.setStatus({ code: SpanStatusCode.ERROR, message: taskError.message });
428
+ }
429
+ else {
430
+ span.setStatus({ code: SpanStatusCode.OK });
431
+ }
432
+ return taskError;
433
+ })
434
+ .finally(() => {
435
+ span.end();
436
+ });
437
+ });
438
+ });
439
+ };
440
+ });
441
+ }
442
+ _patchHandleCancelStepRun(prototype) {
443
+ if (isWrapped(prototype.handleCancelStepRun)) {
444
+ this._unwrap(prototype, 'handleCancelStepRun');
445
+ }
446
+ const { tracer } = this;
447
+ this._wrap(prototype, 'handleCancelStepRun', (original) => {
448
+ return function wrappedHandleCancelStepRun(action) {
449
+ return __awaiter(this, void 0, void 0, function* () {
450
+ const attributes = {
451
+ instrumentor: 'hatchet',
452
+ [`hatchet.${opentelemetry_1.OTelAttribute.STEP_RUN_ID}`]: action.taskRunExternalId,
453
+ };
454
+ return tracer.startActiveSpan('hatchet.cancel_step_run', {
455
+ kind: SpanKind.CONSUMER,
456
+ attributes,
457
+ }, (span) => {
458
+ const result = original.call(this, action);
459
+ return result.finally(() => {
460
+ span.end();
461
+ });
462
+ });
463
+ });
464
+ };
465
+ });
466
+ }
467
+ patchScheduleClient(moduleExports, _moduleVersion) {
468
+ var _a;
469
+ const exports = moduleExports;
470
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.ScheduleClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
471
+ diag.debug('hatchet instrumentation: ScheduleClient not found in module exports');
472
+ return moduleExports;
473
+ }
474
+ this._patchScheduleCreate(exports.ScheduleClient.prototype);
475
+ return moduleExports;
476
+ }
477
+ unpatchScheduleClient(moduleExports, _moduleVersion) {
478
+ var _a;
479
+ const exports = moduleExports;
480
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.ScheduleClient) === null || _a === void 0 ? void 0 : _a.prototype)) {
481
+ return moduleExports;
482
+ }
483
+ if (isWrapped(exports.ScheduleClient.prototype.create)) {
484
+ this._unwrap(exports.ScheduleClient.prototype, 'create');
485
+ }
486
+ return moduleExports;
487
+ }
488
+ // IMPORTANT: Keep this wrapper's signature in sync with ScheduleClient.create
489
+ _patchScheduleCreate(prototype) {
490
+ if (isWrapped(prototype.create)) {
491
+ this._unwrap(prototype, 'create');
492
+ }
493
+ const { tracer, _getConfig: getConfig } = this;
494
+ this._wrap(prototype, 'create', (original) => {
495
+ return function wrappedCreate(workflow, input) {
496
+ return __awaiter(this, void 0, void 0, function* () {
497
+ const triggerAtIso = input.triggerAt instanceof Date
498
+ ? input.triggerAt.toISOString()
499
+ : new Date(input.triggerAt).toISOString();
500
+ const attributes = filterAttributes({
501
+ [opentelemetry_1.OTelAttribute.WORKFLOW_NAME]: workflow,
502
+ [opentelemetry_1.OTelAttribute.RUN_AT_TIMESTAMPS]: JSON.stringify([triggerAtIso]),
503
+ [opentelemetry_1.OTelAttribute.ACTION_PAYLOAD]: JSON.stringify(input.input),
504
+ [opentelemetry_1.OTelAttribute.ADDITIONAL_METADATA]: input.additionalMetadata
505
+ ? JSON.stringify(input.additionalMetadata)
506
+ : undefined,
507
+ [opentelemetry_1.OTelAttribute.PRIORITY]: input.priority,
508
+ }, getConfig().excludedAttributes);
509
+ return tracer.startActiveSpan('hatchet.schedule_workflow', {
510
+ kind: SpanKind.PRODUCER,
511
+ attributes,
512
+ }, (span) => {
513
+ var _a;
514
+ // Inject traceparent into additionalMetadata for context propagation
515
+ const enhancedMetadata = Object.assign({}, ((_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
516
+ injectContext(enhancedMetadata);
517
+ const enhancedInput = Object.assign(Object.assign({}, input), { additionalMetadata: enhancedMetadata });
518
+ return original
519
+ .call(this, workflow, enhancedInput)
520
+ .catch((error) => {
521
+ span.recordException(error);
522
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error === null || error === void 0 ? void 0 : error.message });
523
+ throw error;
524
+ })
525
+ .finally(() => {
526
+ span.end();
527
+ });
528
+ });
529
+ });
530
+ };
531
+ });
532
+ }
533
+ // --- DurableContext patching ---
534
+ patchDurableContext(moduleExports) {
535
+ var _a;
536
+ const exports = moduleExports;
537
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.DurableContext) === null || _a === void 0 ? void 0 : _a.prototype)) {
538
+ return moduleExports;
539
+ }
540
+ this._patchWaitFor(exports.DurableContext.prototype);
541
+ return moduleExports;
542
+ }
543
+ unpatchDurableContext(moduleExports) {
544
+ var _a;
545
+ const exports = moduleExports;
546
+ if (!((_a = exports === null || exports === void 0 ? void 0 : exports.DurableContext) === null || _a === void 0 ? void 0 : _a.prototype)) {
547
+ return;
548
+ }
549
+ if (isWrapped(exports.DurableContext.prototype.waitFor)) {
550
+ this._unwrap(exports.DurableContext.prototype, 'waitFor');
551
+ }
552
+ }
553
+ // IMPORTANT: Keep this wrapper's signature in sync with DurableContext.waitFor
554
+ _patchWaitFor(prototype) {
555
+ if (isWrapped(prototype.waitFor)) {
556
+ this._unwrap(prototype, 'waitFor');
557
+ }
558
+ const { tracer } = this;
559
+ this._wrap(prototype, 'waitFor', (original) => {
560
+ return function wrappedWaitFor(...args) {
561
+ return __awaiter(this, void 0, void 0, function* () {
562
+ var _a, _b;
563
+ return tracer.startActiveSpan('hatchet.durable.wait_for', {
564
+ kind: SpanKind.INTERNAL,
565
+ attributes: {
566
+ instrumentor: 'hatchet',
567
+ 'hatchet.step_run_id': (_b = (_a = this.action) === null || _a === void 0 ? void 0 : _a.taskRunExternalId) !== null && _b !== void 0 ? _b : '',
568
+ },
569
+ }, (span) => __awaiter(this, void 0, void 0, function* () {
570
+ try {
571
+ const result = yield original.apply(this, args);
572
+ span.setStatus({ code: SpanStatusCode.OK });
573
+ return result;
574
+ }
575
+ catch (error) {
576
+ if (error instanceof Error) {
577
+ span.recordException(error);
578
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
579
+ }
580
+ throw error;
581
+ }
582
+ finally {
583
+ span.end();
584
+ }
585
+ }));
586
+ });
587
+ };
588
+ });
589
+ }
590
+ }
591
+ exports.HatchetInstrumentor = HatchetInstrumentor;
592
+ exports.default = HatchetInstrumentor;
@@ -0,0 +1,6 @@
1
+ export type { OpenTelemetryConfig } from '../clients/hatchet-client/client-config';
2
+ export declare const DEFAULT_CONFIG: {
3
+ excludedAttributes: string[];
4
+ includeTaskNameInSpanName: boolean;
5
+ enableHatchetCollector: boolean;
6
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_CONFIG = void 0;
4
+ exports.DEFAULT_CONFIG = {
5
+ excludedAttributes: [],
6
+ includeTaskNameInSpanName: false,
7
+ enableHatchetCollector: true,
8
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hatchet-dev/typescript-sdk",
3
- "version": "1.17.2",
3
+ "version": "1.19.0",
4
4
  "description": "Background task orchestration & visibility for developers",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -61,6 +61,11 @@
61
61
  "zod-to-json-schema": "^3.24.1"
62
62
  },
63
63
  "optionalDependencies": {
64
+ "@opentelemetry/api": "^1.9.0",
65
+ "@opentelemetry/core": "^2.0.0",
66
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
67
+ "@opentelemetry/instrumentation": "^0.208.0",
68
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
64
69
  "prom-client": "^15.1.3"
65
70
  },
66
71
  "scripts": {
@@ -5,6 +5,7 @@ interface LoadClientConfigOptions {
5
5
  }
6
6
  export declare class ConfigLoader {
7
7
  static loadClientConfig(override?: Partial<ClientConfig>, config?: LoadClientConfigOptions): Partial<ClientConfig>;
8
+ private static parseJsonArray;
8
9
  static get default_yaml_config_path(): string;
9
10
  static createCredentials(config: ClientConfig['tls_config']): ChannelCredentials;
10
11
  static loadYamlConfig(path?: string): ClientConfig | undefined;