@hatchet-dev/typescript-sdk 1.18.0 → 1.19.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.
@@ -14,6 +14,7 @@ const events_1 = require("../../protoc/events/events");
14
14
  const hatchet_error_1 = require("../../util/errors/hatchet-error");
15
15
  const retrier_1 = require("../../util/retrier");
16
16
  const apply_namespace_1 = require("../../util/apply-namespace");
17
+ const parent_run_context_vars_1 = require("../../v1/parent-run-context-vars");
17
18
  var LogLevel;
18
19
  (function (LogLevel) {
19
20
  LogLevel["INFO"] = "INFO";
@@ -21,6 +22,13 @@ var LogLevel;
21
22
  LogLevel["ERROR"] = "ERROR";
22
23
  LogLevel["DEBUG"] = "DEBUG";
23
24
  })(LogLevel || (exports.LogLevel = LogLevel = {}));
25
+ function injectSourceInfo(metadata) {
26
+ const ctx = parent_run_context_vars_1.parentRunContextManager.getContext();
27
+ if (!(ctx === null || ctx === void 0 ? void 0 : ctx.parentId) || !(ctx === null || ctx === void 0 ? void 0 : ctx.parentTaskRunExternalId)) {
28
+ return metadata;
29
+ }
30
+ return Object.assign(Object.assign({}, metadata), { hatchet__source_workflow_run_id: ctx.parentId, hatchet__source_step_run_id: ctx.parentTaskRunExternalId });
31
+ }
24
32
  class EventClient {
25
33
  constructor(config, channel, factory, api) {
26
34
  this.config = config;
@@ -35,14 +43,14 @@ class EventClient {
35
43
  * Keep the signature in sync with the instrumentor wrapper.
36
44
  */
37
45
  push(type, input, options = {}) {
46
+ var _a;
38
47
  const namespacedType = (0, apply_namespace_1.applyNamespace)(type, this.config.namespace);
48
+ const enhancedMetadata = injectSourceInfo((_a = options.additionalMetadata) !== null && _a !== void 0 ? _a : {});
39
49
  const req = {
40
50
  key: namespacedType,
41
51
  payload: JSON.stringify(input),
42
52
  eventTimestamp: new Date(),
43
- additionalMetadata: options.additionalMetadata
44
- ? JSON.stringify(options.additionalMetadata)
45
- : undefined,
53
+ additionalMetadata: Object.keys(enhancedMetadata).length > 0 ? JSON.stringify(enhancedMetadata) : undefined,
46
54
  priority: options.priority,
47
55
  scope: options.scope,
48
56
  };
@@ -62,19 +70,14 @@ class EventClient {
62
70
  bulkPush(type, inputs, options = {}) {
63
71
  const namespacedType = (0, apply_namespace_1.applyNamespace)(type, this.config.namespace);
64
72
  const events = inputs.map((input) => {
73
+ var _a, _b;
74
+ const baseMeta = (_b = (_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : options.additionalMetadata) !== null && _b !== void 0 ? _b : {};
75
+ const enhanced = injectSourceInfo(baseMeta);
65
76
  return {
66
77
  key: namespacedType,
67
78
  payload: JSON.stringify(input.payload),
68
79
  eventTimestamp: new Date(),
69
- additionalMetadata: (() => {
70
- if (input.additionalMetadata) {
71
- return JSON.stringify(input.additionalMetadata);
72
- }
73
- if (options.additionalMetadata) {
74
- return JSON.stringify(options.additionalMetadata);
75
- }
76
- return undefined;
77
- })(),
80
+ additionalMetadata: Object.keys(enhanced).length > 0 ? JSON.stringify(enhanced) : undefined,
78
81
  priority: input.priority,
79
82
  scope: input.scope,
80
83
  };
@@ -9,16 +9,28 @@
9
9
  * enable_hatchet_otel_collector option.
10
10
  */
11
11
  import type { ClientConfig } from '../clients/hatchet-client/client-config';
12
- type SdkTracerProvider = import('@opentelemetry/sdk-trace-base').BasicTracerProvider;
12
+ declare const BatchSpanProcessor: typeof import("@opentelemetry/sdk-trace-base").BatchSpanProcessor;
13
+ type ReadableSpan = import('@opentelemetry/sdk-trace-base').ReadableSpan;
14
+ type SdkSpan = import('@opentelemetry/sdk-trace-base').Span;
15
+ /**
16
+ * HatchetAttributeSpanProcessor wraps a BatchSpanProcessor and injects
17
+ * hatchet.* attributes into every span created within a step run context.
18
+ * This ensures child spans are queryable by the same attributes (e.g.
19
+ * hatchet.step_run_id) as the parent span.
20
+ */
21
+ declare class HatchetAttributeSpanProcessor extends BatchSpanProcessor {
22
+ onStart(span: SdkSpan): void;
23
+ onEnd(span: ReadableSpan): void;
24
+ }
13
25
  export interface HatchetBspConfig {
14
26
  scheduledDelayMillis?: number;
15
27
  maxExportBatchSize?: number;
16
28
  maxQueueSize?: number;
17
29
  }
18
30
  /**
19
- * Adds the Hatchet OTLP exporter to the given TracerProvider.
20
- * The exporter sends spans to the Hatchet engine's collector endpoint
21
- * using the same connection settings as the Hatchet client.
31
+ * Creates a SpanProcessor that sends spans to the Hatchet engine's
32
+ * collector endpoint using the same connection settings as the Hatchet client.
33
+ * Pass the returned processor to BasicTracerProvider's `spanProcessors` option.
22
34
  */
23
- export declare function addHatchetExporter(tracerProvider: SdkTracerProvider, config: ClientConfig, bspConfig?: HatchetBspConfig): void;
35
+ export declare function createHatchetSpanProcessor(config: ClientConfig, bspConfig?: HatchetBspConfig): InstanceType<typeof HatchetAttributeSpanProcessor>;
24
36
  export {};
@@ -10,14 +10,23 @@
10
10
  * enable_hatchet_otel_collector option.
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.addHatchetExporter = addHatchetExporter;
13
+ exports.createHatchetSpanProcessor = createHatchetSpanProcessor;
14
14
  const hatchet_span_context_1 = require("./hatchet-span-context");
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ let otelDiag;
17
+ try {
18
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
19
+ otelDiag = require('@opentelemetry/api').diag;
20
+ }
21
+ catch (_a) {
22
+ // best-effort
23
+ }
15
24
  try {
16
25
  require.resolve('@opentelemetry/exporter-trace-otlp-grpc');
17
26
  require.resolve('@opentelemetry/sdk-trace-base');
18
27
  require.resolve('@opentelemetry/core');
19
28
  }
20
- catch (_a) {
29
+ catch (_b) {
21
30
  throw new Error('To use HatchetInstrumentor with enableHatchetCollector, you must install: ' +
22
31
  'npm install @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/sdk-trace-base @opentelemetry/core');
23
32
  }
@@ -38,24 +47,38 @@ class HatchetExporterWrapper {
38
47
  this.inner = inner;
39
48
  }
40
49
  export(spans, resultCallback) {
50
+ var _a;
41
51
  if (this.retryAt > 0 && Date.now() < this.retryAt) {
42
52
  resultCallback({ code: ExportResultCode.SUCCESS });
43
53
  return;
44
54
  }
45
- this.inner.export(spans, (result) => {
46
- var _a;
47
- if (result.code !== ExportResultCode.SUCCESS && result.error) {
48
- const err = result.error;
49
- if (err.code === GRPC_STATUS_UNIMPLEMENTED ||
50
- ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toString().includes('UNIMPLEMENTED'))) {
51
- this.retryAt = Date.now() + RETRY_AFTER_MS;
52
- resultCallback({ code: ExportResultCode.SUCCESS });
53
- return;
55
+ try {
56
+ this.inner.export(spans, (result) => {
57
+ var _a;
58
+ if (result.code !== ExportResultCode.SUCCESS && result.error) {
59
+ const err = result.error;
60
+ if (err.code === GRPC_STATUS_UNIMPLEMENTED ||
61
+ ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toString().includes('UNIMPLEMENTED'))) {
62
+ this.retryAt = Date.now() + RETRY_AFTER_MS;
63
+ resultCallback({ code: ExportResultCode.SUCCESS });
64
+ return;
65
+ }
54
66
  }
67
+ this.retryAt = 0;
68
+ resultCallback(result);
69
+ });
70
+ }
71
+ catch (e) {
72
+ if (e instanceof TypeError && ((_a = e.message) === null || _a === void 0 ? void 0 : _a.includes("reading 'name'"))) {
73
+ otelDiag === null || otelDiag === void 0 ? void 0 : otelDiag.error('hatchet instrumentation: OpenTelemetry package version mismatch. ' +
74
+ '@opentelemetry/exporter-trace-otlp-grpc and @opentelemetry/sdk-trace-base must be ' +
75
+ 'from the same release set (1.x + 0.5x.x, or 2.x + 0.20x.x). ' +
76
+ 'See https://github.com/open-telemetry/opentelemetry-js#version-compatibility');
77
+ resultCallback({ code: ExportResultCode.SUCCESS });
78
+ return;
55
79
  }
56
- this.retryAt = 0;
57
- resultCallback(result);
58
- });
80
+ throw e;
81
+ }
59
82
  }
60
83
  shutdown() {
61
84
  return this.inner.shutdown();
@@ -73,9 +96,19 @@ class HatchetExporterWrapper {
73
96
  */
74
97
  class HatchetAttributeSpanProcessor extends BatchSpanProcessor {
75
98
  onStart(span) {
99
+ var _a;
76
100
  const attrs = hatchet_span_context_1.hatchetSpanAttributes.getStore();
77
101
  if (attrs) {
78
- span.setAttributes(attrs);
102
+ const existing = (_a = span.attributes) !== null && _a !== void 0 ? _a : {};
103
+ const filtered = {};
104
+ for (const [key, value] of Object.entries(attrs)) {
105
+ if (!(key in existing)) {
106
+ filtered[key] = value;
107
+ }
108
+ }
109
+ if (Object.keys(filtered).length > 0) {
110
+ span.setAttributes(filtered);
111
+ }
79
112
  }
80
113
  super.onStart(span, undefined);
81
114
  }
@@ -85,10 +118,14 @@ class HatchetAttributeSpanProcessor extends BatchSpanProcessor {
85
118
  }
86
119
  function createHatchetExporter(config) {
87
120
  const insecure = config.tls_config.tls_strategy === 'none';
121
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
122
+ const grpc = require('@grpc/grpc-js');
123
+ const metadata = new grpc.Metadata();
124
+ metadata.set('authorization', `Bearer ${config.token}`);
88
125
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
126
  const opts = {
90
127
  url: `${insecure ? 'http' : 'https'}://${config.host_port}`,
91
- metadata: { authorization: `Bearer ${config.token}` },
128
+ metadata,
92
129
  };
93
130
  if (!insecure && config.tls_config.ca_file) {
94
131
  try {
@@ -106,18 +143,17 @@ function createHatchetExporter(config) {
106
143
  return new OTLPTraceExporter(opts);
107
144
  }
108
145
  /**
109
- * Adds the Hatchet OTLP exporter to the given TracerProvider.
110
- * The exporter sends spans to the Hatchet engine's collector endpoint
111
- * using the same connection settings as the Hatchet client.
146
+ * Creates a SpanProcessor that sends spans to the Hatchet engine's
147
+ * collector endpoint using the same connection settings as the Hatchet client.
148
+ * Pass the returned processor to BasicTracerProvider's `spanProcessors` option.
112
149
  */
113
- function addHatchetExporter(tracerProvider, config, bspConfig) {
150
+ function createHatchetSpanProcessor(config, bspConfig) {
114
151
  const inner = createHatchetExporter(config);
115
152
  const exporter = new HatchetExporterWrapper(inner);
116
153
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
- const processor = new HatchetAttributeSpanProcessor(exporter, {
154
+ return new HatchetAttributeSpanProcessor(exporter, {
118
155
  scheduledDelayMillis: bspConfig === null || bspConfig === void 0 ? void 0 : bspConfig.scheduledDelayMillis,
119
156
  maxExportBatchSize: bspConfig === null || bspConfig === void 0 ? void 0 : bspConfig.maxExportBatchSize,
120
157
  maxQueueSize: bspConfig === null || bspConfig === void 0 ? void 0 : bspConfig.maxQueueSize,
121
158
  });
122
- tracerProvider.addSpanProcessor(processor);
123
159
  }
@@ -46,6 +46,17 @@ function extractContext(carrier) {
46
46
  function injectContext(carrier) {
47
47
  propagation.inject(context.active(), carrier);
48
48
  }
49
+ function injectSourceInfo(carrier) {
50
+ const store = hatchet_span_context_1.hatchetSpanAttributes.getStore();
51
+ if (!store)
52
+ return;
53
+ const wfRunId = store['hatchet.workflow_run_id'];
54
+ const stepRunId = store['hatchet.step_run_id'];
55
+ if (typeof wfRunId === 'string' && typeof stepRunId === 'string') {
56
+ carrier['hatchet__source_workflow_run_id'] = wfRunId;
57
+ carrier['hatchet__source_step_run_id'] = stepRunId;
58
+ }
59
+ }
49
60
  function getActionOtelAttributes(action, excludedAttributes = [], workerId) {
50
61
  const attributes = {
51
62
  [opentelemetry_1.OTelAttribute.TENANT_ID]: action.tenantId,
@@ -117,35 +128,32 @@ class HatchetInstrumentor extends InstrumentationBase {
117
128
  _setupHatchetCollector(clientConfig, bspConfig) {
118
129
  try {
119
130
  /* eslint-disable @typescript-eslint/no-require-imports */
120
- const { addHatchetExporter } = require('./hatchet-exporter.js');
131
+ const { createHatchetSpanProcessor } = require('./hatchet-exporter.js');
121
132
  let config = clientConfig;
122
133
  if (!config) {
123
- // Load config from environment (same as HatchetClient would)
124
134
  const { ConfigLoader } = require('../util/config-loader/config-loader');
125
135
  config = ConfigLoader.loadClientConfig();
126
136
  }
127
- // Get the SDK TracerProvider - either from the global provider or create one
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
- let sdkTracerProvider;
137
+ const processor = createHatchetSpanProcessor(config, bspConfig);
130
138
  try {
131
139
  const sdkTrace = require('@opentelemetry/sdk-trace-base');
132
140
  /* eslint-enable @typescript-eslint/no-require-imports */
133
- // Check if the global tracer provider is an SDK TracerProvider
134
141
  const globalProvider = otelApi.trace.getTracerProvider();
135
- if (globalProvider instanceof sdkTrace.BasicTracerProvider) {
136
- sdkTracerProvider = globalProvider;
142
+ if (!(globalProvider instanceof sdkTrace.BasicTracerProvider)) {
143
+ const sdkTracerProvider = new sdkTrace.BasicTracerProvider({
144
+ spanProcessors: [processor],
145
+ });
146
+ otelApi.trace.setGlobalTracerProvider(sdkTracerProvider);
137
147
  }
138
148
  else {
139
- // Create a new SDK TracerProvider and set it as global
140
- sdkTracerProvider = new sdkTrace.BasicTracerProvider();
141
- sdkTracerProvider.register();
149
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
+ globalProvider.addSpanProcessor(processor);
142
151
  }
143
152
  }
144
153
  catch (_a) {
145
154
  diag.warn('hatchet instrumentation: @opentelemetry/sdk-trace-base is required for enableHatchetCollector');
146
155
  return;
147
156
  }
148
- addHatchetExporter(sdkTracerProvider, config, bspConfig);
149
157
  diag.info('hatchet instrumentation: Hatchet OTLP collector enabled');
150
158
  }
151
159
  catch (e) {
@@ -218,6 +226,7 @@ class HatchetInstrumentor extends InstrumentationBase {
218
226
  var _a;
219
227
  const enhancedMetadata = Object.assign({}, ((_a = options.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
220
228
  injectContext(enhancedMetadata);
229
+ injectSourceInfo(enhancedMetadata);
221
230
  const enhancedOptions = Object.assign(Object.assign({}, options), { additionalMetadata: enhancedMetadata });
222
231
  const result = original.call(this, type, input, enhancedOptions);
223
232
  return result.finally(() => {
@@ -250,6 +259,7 @@ class HatchetInstrumentor extends InstrumentationBase {
250
259
  var _a;
251
260
  const enhancedMetadata = Object.assign({}, ((_a = input.additionalMetadata) !== null && _a !== void 0 ? _a : {}));
252
261
  injectContext(enhancedMetadata);
262
+ injectSourceInfo(enhancedMetadata);
253
263
  return Object.assign(Object.assign({}, input), { additionalMetadata: enhancedMetadata });
254
264
  });
255
265
  const result = original.call(this, type, enhancedInputs, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hatchet-dev/typescript-sdk",
3
- "version": "1.18.0",
3
+ "version": "1.19.1",
4
4
  "description": "Background task orchestration & visibility for developers",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -61,11 +61,12 @@
61
61
  "zod-to-json-schema": "^3.24.1"
62
62
  },
63
63
  "optionalDependencies": {
64
+ "@grpc/grpc-js": "^1.14.3",
64
65
  "@opentelemetry/api": "^1.9.0",
65
- "@opentelemetry/core": "^1.30.1",
66
+ "@opentelemetry/core": "^2.0.0",
66
67
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
67
68
  "@opentelemetry/instrumentation": "^0.208.0",
68
- "@opentelemetry/sdk-trace-base": "^1.30.1",
69
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
69
70
  "prom-client": "^15.1.3"
70
71
  },
71
72
  "scripts": {
@@ -3,3 +3,64 @@ export type WebhookInput = {
3
3
  message: string;
4
4
  };
5
5
  export declare const webhookWorkflow: import("../..").WorkflowDeclaration<WebhookInput, {}, {}>;
6
+ type StripePaymentInput = {
7
+ type: string;
8
+ data: {
9
+ object: {
10
+ customer: string;
11
+ amount: number;
12
+ };
13
+ };
14
+ };
15
+ export declare const handleStripePayment: import("../..").TaskWorkflowDeclaration<StripePaymentInput, {
16
+ customer: string;
17
+ amount: number;
18
+ }, {}, {}, {}, {}>;
19
+ type GitHubPRInput = {
20
+ action: string;
21
+ pull_request: {
22
+ number: number;
23
+ title: string;
24
+ };
25
+ repository: {
26
+ full_name: string;
27
+ };
28
+ };
29
+ export declare const handleGitHubPR: import("../..").TaskWorkflowDeclaration<GitHubPRInput, {
30
+ repo: string;
31
+ pr: number;
32
+ }, {}, {}, {}, {}>;
33
+ type SlackEventInput = {
34
+ event: {
35
+ type: string;
36
+ user: string;
37
+ text: string;
38
+ channel: string;
39
+ };
40
+ };
41
+ export declare const handleSlackMention: import("../..").TaskWorkflowDeclaration<SlackEventInput, {
42
+ handled: true;
43
+ }, {}, {}, {}, {}>;
44
+ type SlackCommandInput = {
45
+ command: string;
46
+ text: string;
47
+ user_name: string;
48
+ response_url: string;
49
+ };
50
+ export declare const handleSlackCommand: import("../..").TaskWorkflowDeclaration<SlackCommandInput, {
51
+ command: string;
52
+ args: string;
53
+ }, {}, {}, {}, {}>;
54
+ type SlackInteractionInput = {
55
+ type: string;
56
+ actions: Array<{
57
+ action_id: string;
58
+ }>;
59
+ user: {
60
+ username: string;
61
+ };
62
+ };
63
+ export declare const handleSlackInteraction: import("../..").TaskWorkflowDeclaration<SlackInteractionInput, {
64
+ action: string;
65
+ }, {}, {}, {}, {}>;
66
+ export {};
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.webhookWorkflow = void 0;
12
+ exports.handleSlackInteraction = exports.handleSlackCommand = exports.handleSlackMention = exports.handleGitHubPR = exports.handleStripePayment = exports.webhookWorkflow = void 0;
13
13
  const hatchet_client_1 = require("../hatchet-client");
14
14
  exports.webhookWorkflow = hatchet_client_1.hatchet.workflow({
15
15
  name: 'webhook-workflow',
@@ -21,3 +21,60 @@ exports.webhookWorkflow.task({
21
21
  return input;
22
22
  }),
23
23
  });
24
+ exports.handleStripePayment = hatchet_client_1.hatchet.task({
25
+ name: 'handle-stripe-payment',
26
+ on: {
27
+ event: 'stripe:payment_intent.succeeded',
28
+ },
29
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
30
+ const { customer, amount } = input.data.object;
31
+ console.log(`Payment of ${amount} from ${customer}`);
32
+ return { customer, amount };
33
+ }),
34
+ });
35
+ exports.handleGitHubPR = hatchet_client_1.hatchet.task({
36
+ name: 'handle-github-pr',
37
+ on: {
38
+ event: 'github:pull_request:opened',
39
+ },
40
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
41
+ const repo = input.repository.full_name;
42
+ const prNumber = input.pull_request.number;
43
+ const { title } = input.pull_request;
44
+ console.log(`PR #${prNumber} opened on ${repo}: ${title}`);
45
+ return { repo, pr: prNumber };
46
+ }),
47
+ });
48
+ exports.handleSlackMention = hatchet_client_1.hatchet.task({
49
+ name: 'handle-slack-mention',
50
+ on: {
51
+ event: 'slack:event:app_mention',
52
+ },
53
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
54
+ const { user, text, channel } = input.event;
55
+ console.log(`Mentioned by ${user} in ${channel}: ${text}`);
56
+ return { handled: true };
57
+ }),
58
+ });
59
+ exports.handleSlackCommand = hatchet_client_1.hatchet.task({
60
+ name: 'handle-slack-command',
61
+ on: {
62
+ event: 'slack:command:/deploy',
63
+ },
64
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
65
+ console.log(`${input.user_name} ran ${input.command} ${input.text}`);
66
+ return { command: input.command, args: input.text };
67
+ }),
68
+ });
69
+ exports.handleSlackInteraction = hatchet_client_1.hatchet.task({
70
+ name: 'handle-slack-interaction',
71
+ on: {
72
+ event: 'slack:interaction:block_actions',
73
+ },
74
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
75
+ const [action] = input.actions;
76
+ console.log(`${input.user.username} clicked button: ${action.action_id}`);
77
+ return { action: action.action_id };
78
+ }),
79
+ });
80
+ // !!
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const HATCHET_VERSION = "1.18.0";
1
+ export declare const HATCHET_VERSION = "1.19.1";
package/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HATCHET_VERSION = void 0;
4
- exports.HATCHET_VERSION = '1.18.0';
4
+ exports.HATCHET_VERSION = '1.19.1';