@hatchet-dev/typescript-sdk 0.7.4 → 0.8.0-alpha.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.
@@ -3,7 +3,7 @@ import { CreateWorkflowVersionOpts, RateLimitDuration, WorkflowServiceClient } f
3
3
  import { ClientConfig } from '../hatchet-client/client-config';
4
4
  import { Logger } from '../../util/logger';
5
5
  import { Api } from '../rest';
6
- import { WorkflowRunStatus, WorkflowRunStatusList } from '../rest/generated/data-contracts';
6
+ import { WebhookWorkerCreateRequest, WorkflowRunStatus } from '../rest/generated/data-contracts';
7
7
  type WorkflowMetricsQuery = {
8
8
  workflowId?: string;
9
9
  workflowName?: string;
@@ -40,6 +40,7 @@ export declare class AdminClient {
40
40
  */
41
41
  put_workflow(workflow: CreateWorkflowVersionOpts): Promise<void>;
42
42
  put_rate_limit(key: string, limit: number, duration?: RateLimitDuration): Promise<void>;
43
+ webhook_create(data: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../rest/generated/data-contracts").WebhookWorker, any>>;
43
44
  /**
44
45
  * Run a new instance of a workflow with the given input. This will create a new workflow run and return the ID of the
45
46
  * new run.
@@ -88,10 +89,6 @@ export declare class AdminClient {
88
89
  limit?: number | undefined;
89
90
  eventId?: string | undefined;
90
91
  workflowId?: string | undefined;
91
- parentWorkflowRunId?: string | undefined;
92
- parentStepRunId?: string | undefined;
93
- statuses?: WorkflowRunStatusList | undefined;
94
- additionalMetadata?: string[] | undefined;
95
92
  }): Promise<import("../rest/generated/data-contracts").WorkflowRunList>;
96
93
  /**
97
94
  * Schedule a workflow to run at a specific time or times.
@@ -72,6 +72,11 @@ class AdminClient {
72
72
  }
73
73
  });
74
74
  }
75
+ webhook_create(data) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ return this.api.webhookCreate(this.tenantId, data);
78
+ });
79
+ }
75
80
  /**
76
81
  * Run a new instance of a workflow with the given input. This will create a new workflow run and return the ID of the
77
82
  * new run.
@@ -1,14 +1,57 @@
1
1
  import { DispatcherClient as PbDispatcherClient, AssignedAction } from '../../protoc/dispatcher';
2
2
  import { ClientConfig } from '../hatchet-client/client-config';
3
3
  import { Logger } from '../../util/logger';
4
+ import { z } from 'zod';
4
5
  import { DispatcherClient } from './dispatcher-client';
5
6
  import { Heartbeat } from './heartbeat/heartbeat-controller';
6
7
  declare enum ListenStrategy {
7
8
  LISTEN_STRATEGY_V1 = 1,
8
9
  LISTEN_STRATEGY_V2 = 2
9
10
  }
10
- export interface Action extends AssignedAction {
11
- }
11
+ export declare const ActionObject: z.ZodObject<{
12
+ tenantId: z.ZodString;
13
+ jobId: z.ZodString;
14
+ jobName: z.ZodString;
15
+ jobRunId: z.ZodString;
16
+ stepId: z.ZodString;
17
+ stepRunId: z.ZodString;
18
+ actionId: z.ZodString;
19
+ actionType: z.ZodEffects<z.ZodOptional<z.ZodNumber>, number | undefined, unknown>;
20
+ actionPayload: z.ZodString;
21
+ workflowRunId: z.ZodString;
22
+ getGroupKeyRunId: z.ZodOptional<z.ZodString>;
23
+ stepName: z.ZodString;
24
+ retryCount: z.ZodNumber;
25
+ }, "strip", z.ZodTypeAny, {
26
+ tenantId: string;
27
+ stepRunId: string;
28
+ workflowRunId: string;
29
+ jobId: string;
30
+ jobName: string;
31
+ jobRunId: string;
32
+ stepId: string;
33
+ actionId: string;
34
+ actionPayload: string;
35
+ stepName: string;
36
+ retryCount: number;
37
+ getGroupKeyRunId?: string | undefined;
38
+ actionType?: number | undefined;
39
+ }, {
40
+ tenantId: string;
41
+ stepRunId: string;
42
+ workflowRunId: string;
43
+ jobId: string;
44
+ jobName: string;
45
+ jobRunId: string;
46
+ stepId: string;
47
+ actionId: string;
48
+ actionPayload: string;
49
+ stepName: string;
50
+ retryCount: number;
51
+ getGroupKeyRunId?: string | undefined;
52
+ actionType?: unknown;
53
+ }>;
54
+ export type Action = z.infer<typeof ActionObject>;
12
55
  export declare class ActionListener {
13
56
  config: ClientConfig;
14
57
  client: PbDispatcherClient;
@@ -22,7 +65,21 @@ export declare class ActionListener {
22
65
  listenStrategy: ListenStrategy;
23
66
  heartbeat: Heartbeat;
24
67
  constructor(client: DispatcherClient, workerId: string, retryInterval?: number, retryCount?: number);
25
- actions: () => AsyncGenerator<Action, void, unknown>;
68
+ actions: () => AsyncGenerator<{
69
+ tenantId: string;
70
+ stepRunId: string;
71
+ workflowRunId: string;
72
+ jobId: string;
73
+ jobName: string;
74
+ jobRunId: string;
75
+ stepId: string;
76
+ actionId: string;
77
+ actionPayload: string;
78
+ stepName: string;
79
+ retryCount: number;
80
+ getGroupKeyRunId?: string | undefined;
81
+ actionType?: number | undefined;
82
+ }, void, unknown>;
26
83
  setListenStrategy(strategy: ListenStrategy): Promise<void>;
27
84
  getListenStrategy(): Promise<ListenStrategy>;
28
85
  incrementRetries(): Promise<void>;
@@ -32,11 +32,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32
32
  return (mod && mod.__esModule) ? mod : { "default": mod };
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.ActionListener = void 0;
35
+ exports.ActionListener = exports.ActionObject = void 0;
36
+ const dispatcher_1 = require("../../protoc/dispatcher");
36
37
  const nice_grpc_1 = require("nice-grpc");
37
38
  const sleep_1 = __importDefault(require("../../util/sleep"));
38
39
  const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
39
40
  const logger_1 = require("../../util/logger");
41
+ const zod_1 = require("zod");
40
42
  const heartbeat_controller_1 = require("./heartbeat/heartbeat-controller");
41
43
  const DEFAULT_ACTION_LISTENER_RETRY_INTERVAL = 5000; // milliseconds
42
44
  const DEFAULT_ACTION_LISTENER_RETRY_COUNT = 20;
@@ -46,6 +48,21 @@ var ListenStrategy;
46
48
  ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V1"] = 1] = "LISTEN_STRATEGY_V1";
47
49
  ListenStrategy[ListenStrategy["LISTEN_STRATEGY_V2"] = 2] = "LISTEN_STRATEGY_V2";
48
50
  })(ListenStrategy || (ListenStrategy = {}));
51
+ exports.ActionObject = zod_1.z.object({
52
+ tenantId: zod_1.z.string(),
53
+ jobId: zod_1.z.string(),
54
+ jobName: zod_1.z.string(),
55
+ jobRunId: zod_1.z.string(),
56
+ stepId: zod_1.z.string(),
57
+ stepRunId: zod_1.z.string(),
58
+ actionId: zod_1.z.string(),
59
+ actionType: zod_1.z.preprocess((s) => (0, dispatcher_1.actionTypeFromJSON)(s), zod_1.z.number().optional()),
60
+ actionPayload: zod_1.z.string(),
61
+ workflowRunId: zod_1.z.string(),
62
+ getGroupKeyRunId: zod_1.z.string().optional(),
63
+ stepName: zod_1.z.string(),
64
+ retryCount: zod_1.z.number(),
65
+ });
49
66
  class ActionListener {
50
67
  constructor(client, workerId, retryInterval = DEFAULT_ACTION_LISTENER_RETRY_INTERVAL, retryCount = DEFAULT_ACTION_LISTENER_RETRY_COUNT) {
51
68
  this.lastConnectionAttempt = 0;
@@ -29,4 +29,5 @@ export declare class HatchetClient {
29
29
  static init(config?: Partial<ClientConfig>, options?: HatchetClientOptions, axiosConfig?: AxiosRequestConfig): HatchetClient;
30
30
  run(workflow: string | Workflow): Promise<Worker>;
31
31
  worker(workflow: string | Workflow, maxRuns?: number): Promise<Worker>;
32
+ webhooks(workflow: Workflow): import("../worker/handler").WebhookHandler;
32
33
  }
@@ -142,5 +142,11 @@ class HatchetClient {
142
142
  return worker;
143
143
  });
144
144
  }
145
+ webhooks(workflow) {
146
+ const worker = new worker_1.Worker(this, {
147
+ name: workflow.id,
148
+ });
149
+ return worker.getHandler(workflow);
150
+ }
145
151
  }
146
152
  exports.HatchetClient = HatchetClient;
@@ -1,4 +1,4 @@
1
- import { APIMeta, AcceptInviteRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreatePullRequestFromStepRun, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, GetStepRunDiffResponse, LinkGithubRepositoryRequest, ListAPIMetaIntegration, ListAPITokensResponse, ListGithubAppInstallationsResponse, ListGithubBranchesResponse, ListGithubReposResponse, ListPullRequestsResponse, ListSNSIntegrations, ListSlackWebhooks, LogLineLevelField, LogLineList, LogLineOrderByDirection, LogLineOrderByField, LogLineSearch, PullRequestState, RejectInviteRequest, ReplayEventRequest, RerunStepRunRequest, SNSIntegration, StepRun, StepRunEventList, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantResourcePolicy, TriggerWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, Worker, WorkerList, Workflow, WorkflowID, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowVersion, WorkflowVersionDefinition } from './data-contracts';
1
+ import { APIMeta, AcceptInviteRequest, CreateAPITokenRequest, CreateAPITokenResponse, CreatePullRequestFromStepRun, CreateSNSIntegrationRequest, CreateTenantAlertEmailGroupRequest, CreateTenantInviteRequest, CreateTenantRequest, EventData, EventKey, EventKeyList, EventList, EventOrderByDirection, EventOrderByField, EventSearch, GetStepRunDiffResponse, LinkGithubRepositoryRequest, ListAPIMetaIntegration, ListAPITokensResponse, ListGithubAppInstallationsResponse, ListGithubBranchesResponse, ListGithubReposResponse, ListPullRequestsResponse, ListSNSIntegrations, ListSlackWebhooks, LogLineLevelField, LogLineList, LogLineOrderByDirection, LogLineOrderByField, LogLineSearch, PullRequestState, RejectInviteRequest, ReplayEventRequest, RerunStepRunRequest, SNSIntegration, StepRun, StepRunEventList, Tenant, TenantAlertEmailGroup, TenantAlertEmailGroupList, TenantAlertingSettings, TenantInvite, TenantInviteList, TenantMember, TenantMemberList, TenantResourcePolicy, TriggerWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, UpdateTenantRequest, User, UserChangePasswordRequest, UserLoginRequest, UserRegisterRequest, UserTenantMembershipsList, WebhookWorker, WebhookWorkerCreateRequest, WebhookWorkerListResponse, Worker, WorkerList, Workflow, WorkflowID, WorkflowList, WorkflowMetrics, WorkflowRun, WorkflowRunList, WorkflowRunStatus, WorkflowRunStatusList, WorkflowRunsCancelRequest, WorkflowRunsMetrics, WorkflowVersion, WorkflowVersionDefinition } from './data-contracts';
2
2
  import { HttpClient, RequestParams } from './http-client';
3
3
  export declare class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
4
4
  /**
@@ -919,4 +919,21 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
919
919
  * @secure
920
920
  */
921
921
  githubAppListBranches: (ghInstallation: string, ghRepoOwner: string, ghRepoName: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<ListGithubBranchesResponse, any>>;
922
+ /**
923
+ * @description Lists all webhooks
924
+ *
925
+ * @name WebhookList
926
+ * @summary List webhooks
927
+ * @request GET:/api/v1/webhook-workers/{tenant}
928
+ * @secure
929
+ */
930
+ webhookList: (tenant: string, params?: RequestParams) => Promise<import("axios").AxiosResponse<WebhookWorkerListResponse, any>>;
931
+ /**
932
+ * @description Creates a webhook
933
+ *
934
+ * @name WebhookCreate
935
+ * @summary Create a webhook
936
+ * @request POST:/api/v1/webhook-workers/{tenant}/create
937
+ */
938
+ webhookCreate: (tenant: string, data: WebhookWorkerCreateRequest, params?: RequestParams) => Promise<import("axios").AxiosResponse<WebhookWorker, any>>;
922
939
  }
@@ -761,6 +761,23 @@ class Api extends http_client_1.HttpClient {
761
761
  * @secure
762
762
  */
763
763
  this.githubAppListBranches = (ghInstallation, ghRepoOwner, ghRepoName, params = {}) => this.request(Object.assign({ path: `/api/v1/github-app/installations/${ghInstallation}/repos/${ghRepoOwner}/${ghRepoName}/branches`, method: 'GET', secure: true, format: 'json' }, params));
764
+ /**
765
+ * @description Lists all webhooks
766
+ *
767
+ * @name WebhookList
768
+ * @summary List webhooks
769
+ * @request GET:/api/v1/webhook-workers/{tenant}
770
+ * @secure
771
+ */
772
+ this.webhookList = (tenant, params = {}) => this.request(Object.assign({ path: `/api/v1/webhook-workers/${tenant}`, method: 'GET', secure: true, format: 'json' }, params));
773
+ /**
774
+ * @description Creates a webhook
775
+ *
776
+ * @name WebhookCreate
777
+ * @summary Create a webhook
778
+ * @request POST:/api/v1/webhook-workers/{tenant}/create
779
+ */
780
+ this.webhookCreate = (tenant, data, params = {}) => this.request(Object.assign({ path: `/api/v1/webhook-workers/${tenant}/create`, method: 'POST', body: data, type: http_client_1.ContentType.Json, format: 'json' }, params));
764
781
  }
765
782
  }
766
783
  exports.Api = Api;
@@ -894,3 +894,28 @@ export interface WorkflowMetrics {
894
894
  /** The total number of concurrency group keys. */
895
895
  groupKeyCount?: number;
896
896
  }
897
+ export interface WebhookWorker {
898
+ metadata: APIResourceMeta;
899
+ /** The webhook url. */
900
+ url: string;
901
+ /** The secret key for validation. */
902
+ secret: string;
903
+ }
904
+ export interface WebhookWorkerCreateRequest {
905
+ /** The webhook url. */
906
+ url: string;
907
+ /** The workflow IDs or names to register for this webhook worker. If not provided, workflows will be automatically detected. */
908
+ workflows?: string[];
909
+ /**
910
+ * The secret key for validation. If not provided, a random secret will be generated.
911
+ * @minLength 32
912
+ */
913
+ secret?: string;
914
+ }
915
+ export interface WebhookWorkerCreateResponse {
916
+ worker?: WebhookWorker;
917
+ }
918
+ export interface WebhookWorkerListResponse {
919
+ pagination?: PaginationResponse;
920
+ rows?: WebhookWorker[];
921
+ }
@@ -0,0 +1,56 @@
1
+ /// <reference types="node" />
2
+ import { IncomingMessage, ServerResponse } from 'http';
3
+ import { Worker } from './worker';
4
+ export interface HandlerOpts {
5
+ secret: string;
6
+ }
7
+ export declare class WebhookHandler {
8
+ private worker;
9
+ constructor(worker: Worker);
10
+ /**
11
+ * Handles a request with a provided body, secret, and signature.
12
+ *
13
+ * @param {string | undefined} body - The body of the request.
14
+ * @param {string | undefined} secret - The secret used for signature verification.
15
+ * @param {string | string[] | undefined | null} signature - The signature of the request.
16
+ *
17
+ * @throws {HatchetError} - If no signature is provided or the signature is not a string.
18
+ * @throws {HatchetError} - If no secret is provided.
19
+ * @throws {HatchetError} - If no body is provided.
20
+ */
21
+ handle(body: string | undefined, secret: string | undefined, signature: string | string[] | undefined | null): Promise<void>;
22
+ private getHealthcheckResponse;
23
+ /**
24
+ * Express Handler
25
+ *
26
+ * This method is an asynchronous function that returns an Express middleware handler.
27
+ * The handler function is responsible for handling incoming requests and invoking the
28
+ * corresponding logic based on the provided secret.
29
+ *
30
+ * @param {string} secret - The secret key used to authenticate and authorize the incoming requests.
31
+ *
32
+ * @return {Function} - An Express middleware handler function that receives the request and response objects.
33
+ */
34
+ expressHandler({ secret }: HandlerOpts): (req: any, res: any) => void;
35
+ /**
36
+ * A method that returns an HTTP request handler.
37
+ *
38
+ * @param {string} secret - The secret key used for verification.
39
+ *
40
+ * @returns {function} - An HTTP request handler function.
41
+ */
42
+ httpHandler({ secret }: HandlerOpts): (req: IncomingMessage, res: ServerResponse) => void;
43
+ /**
44
+ * A method that returns a Next.js request handler.
45
+ *
46
+ * @param {any} req - The request object received from Next.js.
47
+ * @param {string} secret - The secret key used to verify the request.
48
+ * @return {Promise<Response>} - A Promise that resolves with a Response object.
49
+ */
50
+ nextJSHandler({ secret }: HandlerOpts): {
51
+ GET: () => Promise<Response>;
52
+ POST: (req: Request) => Promise<Response>;
53
+ PUT: (req: Request) => Promise<Response>;
54
+ };
55
+ private getBody;
56
+ }
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.WebhookHandler = void 0;
16
+ const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
17
+ const crypto_1 = require("crypto");
18
+ const action_listener_1 = require("../dispatcher/action-listener");
19
+ class WebhookHandler {
20
+ // eslint-disable-next-line no-useless-constructor,no-empty-function
21
+ constructor(worker) {
22
+ this.worker = worker;
23
+ }
24
+ /**
25
+ * Handles a request with a provided body, secret, and signature.
26
+ *
27
+ * @param {string | undefined} body - The body of the request.
28
+ * @param {string | undefined} secret - The secret used for signature verification.
29
+ * @param {string | string[] | undefined | null} signature - The signature of the request.
30
+ *
31
+ * @throws {HatchetError} - If no signature is provided or the signature is not a string.
32
+ * @throws {HatchetError} - If no secret is provided.
33
+ * @throws {HatchetError} - If no body is provided.
34
+ */
35
+ handle(body, secret, signature) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ if (!signature || typeof signature !== 'string') {
38
+ throw new hatchet_error_1.default('No signature provided');
39
+ }
40
+ if (!secret) {
41
+ throw new hatchet_error_1.default('No secret provided');
42
+ }
43
+ if (!body) {
44
+ throw new hatchet_error_1.default('No body provided');
45
+ }
46
+ // verify hmac signature
47
+ const actualSignature = (0, crypto_1.createHmac)('sha256', secret).update(body).digest('hex');
48
+ if (actualSignature !== signature) {
49
+ throw new hatchet_error_1.default(`Invalid signature, expected ${actualSignature}, got ${signature}`);
50
+ }
51
+ const action = action_listener_1.ActionObject.parse(JSON.parse(body));
52
+ yield this.worker.handleAction(action);
53
+ });
54
+ }
55
+ getHealthcheckResponse() {
56
+ return {
57
+ actions: Object.keys(this.worker.action_registry),
58
+ };
59
+ }
60
+ /**
61
+ * Express Handler
62
+ *
63
+ * This method is an asynchronous function that returns an Express middleware handler.
64
+ * The handler function is responsible for handling incoming requests and invoking the
65
+ * corresponding logic based on the provided secret.
66
+ *
67
+ * @param {string} secret - The secret key used to authenticate and authorize the incoming requests.
68
+ *
69
+ * @return {Function} - An Express middleware handler function that receives the request and response objects.
70
+ */
71
+ expressHandler({ secret }) {
72
+ return (req, res) => {
73
+ if (req.method === 'GET') {
74
+ res.sendStatus(200);
75
+ res.json(this.getHealthcheckResponse());
76
+ return;
77
+ }
78
+ if (req.method !== 'POST') {
79
+ res.sendStatus(405);
80
+ res.json({ error: 'Method not allowed' });
81
+ return;
82
+ }
83
+ this.handle(req.body, req.headers['x-hatchet-signature'], secret)
84
+ .then(() => {
85
+ res.sendStatus(200);
86
+ })
87
+ .catch((e) => {
88
+ res.sendStatus(500);
89
+ this.worker.logger.error(`Error handling request: ${e.message}`);
90
+ });
91
+ };
92
+ }
93
+ /**
94
+ * A method that returns an HTTP request handler.
95
+ *
96
+ * @param {string} secret - The secret key used for verification.
97
+ *
98
+ * @returns {function} - An HTTP request handler function.
99
+ */
100
+ httpHandler({ secret }) {
101
+ return (req, res) => {
102
+ const handle = () => __awaiter(this, void 0, void 0, function* () {
103
+ if (req.method === 'GET') {
104
+ res.writeHead(200, { 'Content-Type': 'application/json' });
105
+ res.write(JSON.stringify(this.getHealthcheckResponse()));
106
+ res.end();
107
+ return;
108
+ }
109
+ if (req.method !== 'POST') {
110
+ res.writeHead(405, { 'Content-Type': 'application/json' });
111
+ res.write(JSON.stringify({ error: 'Method not allowed' }));
112
+ res.end();
113
+ return;
114
+ }
115
+ const body = yield this.getBody(req);
116
+ yield this.handle(body, secret, req.headers['x-hatchet-signature']);
117
+ res.writeHead(200, 'OK');
118
+ res.end();
119
+ });
120
+ handle().catch((e) => {
121
+ this.worker.logger.error(`Error handling request: ${e.message}`);
122
+ res.writeHead(500, 'Internal server error');
123
+ res.end();
124
+ });
125
+ };
126
+ }
127
+ /**
128
+ * A method that returns a Next.js request handler.
129
+ *
130
+ * @param {any} req - The request object received from Next.js.
131
+ * @param {string} secret - The secret key used to verify the request.
132
+ * @return {Promise<Response>} - A Promise that resolves with a Response object.
133
+ */
134
+ nextJSHandler({ secret }) {
135
+ const healthcheck = () => __awaiter(this, void 0, void 0, function* () {
136
+ return new Response(JSON.stringify(this.getHealthcheckResponse()), { status: 200 });
137
+ });
138
+ const f = (req) => __awaiter(this, void 0, void 0, function* () {
139
+ yield this.handle(yield req.text(), secret, req.headers.get('x-hatchet-signature'));
140
+ return new Response('ok', { status: 200 });
141
+ });
142
+ return {
143
+ GET: healthcheck,
144
+ POST: f,
145
+ PUT: f,
146
+ };
147
+ }
148
+ getBody(req) {
149
+ return new Promise((resolve) => {
150
+ let body = '';
151
+ req.on('data', (chunk) => {
152
+ body += chunk;
153
+ });
154
+ req.on('end', () => {
155
+ resolve(body);
156
+ });
157
+ });
158
+ }
159
+ }
160
+ exports.WebhookHandler = WebhookHandler;
@@ -4,6 +4,8 @@ import { StepActionEvent, StepActionEventType, GroupKeyActionEvent, GroupKeyActi
4
4
  import HatchetPromise from '../../util/hatchet-promise/hatchet-promise';
5
5
  import { Workflow } from '../../workflow';
6
6
  import { Logger } from '../../util/logger';
7
+ import { WebhookHandler } from './handler';
8
+ import { WebhookWorkerCreateRequest } from '../rest/generated/data-contracts';
7
9
  import { Context, StepRunFunction } from '../../step';
8
10
  export type ActionRegistry = Record<Action['actionId'], Function>;
9
11
  export declare class Worker {
@@ -18,20 +20,25 @@ export declare class Worker {
18
20
  maxRuns?: number;
19
21
  logger: Logger;
20
22
  registeredWorkflowPromises: Array<Promise<any>>;
23
+ registeredWorkflowIds: string[];
21
24
  constructor(client: HatchetClient, options: {
22
25
  name: string;
23
26
  handleKill?: boolean;
24
27
  maxRuns?: number;
25
28
  });
29
+ private registerActions;
30
+ getHandler(workflow: Workflow): WebhookHandler;
31
+ registerWebhook(webhook: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../rest/generated/data-contracts").WebhookWorker, any>>;
26
32
  registerWorkflow(initWorkflow: Workflow): Promise<void>;
27
33
  register_workflow(initWorkflow: Workflow): Promise<void>;
28
34
  registerAction<T, K>(actionId: string, action: StepRunFunction<T, K>): void;
29
- handleStartStepRun(action: Action): void;
30
- handleStartGroupKeyRun(action: Action): void;
35
+ handleStartStepRun(action: Action): Promise<void>;
36
+ handleStartGroupKeyRun(action: Action): Promise<void>;
31
37
  getStepActionEvent(action: Action, eventType: StepActionEventType, payload?: any): StepActionEvent;
32
38
  getGroupKeyActionEvent(action: Action, eventType: GroupKeyActionEventType, payload?: any): GroupKeyActionEvent;
33
- handleCancelStepRun(action: Action): void;
39
+ handleCancelStepRun(action: Action): Promise<void>;
34
40
  stop(): Promise<void>;
35
41
  exitGracefully(handleKill: boolean): Promise<void>;
36
42
  start(): Promise<void>;
43
+ handleAction(action: Action): Promise<void>;
37
44
  }
@@ -25,13 +25,14 @@ const dispatcher_1 = require("../../protoc/dispatcher");
25
25
  const hatchet_promise_1 = __importDefault(require("../../util/hatchet-promise/hatchet-promise"));
26
26
  const workflows_1 = require("../../protoc/workflows");
27
27
  const logger_1 = require("../../util/logger");
28
- // import sleep from '../../util/sleep.js';
28
+ const handler_1 = require("./handler");
29
29
  const step_1 = require("../../step");
30
30
  class Worker {
31
31
  constructor(client, options) {
32
32
  this.futures = {};
33
33
  this.contexts = {};
34
34
  this.registeredWorkflowPromises = [];
35
+ this.registeredWorkflowIds = [];
35
36
  this.client = client;
36
37
  this.name = this.client.config.namespace + options.name;
37
38
  this.action_registry = {};
@@ -42,6 +43,31 @@ class Worker {
42
43
  this.handle_kill = options.handleKill === undefined ? true : options.handleKill;
43
44
  this.logger = new logger_1.Logger(`Worker/${this.name}`, this.client.config.log_level);
44
45
  }
46
+ registerActions(workflow) {
47
+ var _a;
48
+ const newActions = workflow.steps.reduce((acc, step) => {
49
+ acc[`${workflow.id}:${step.name}`] = step.run;
50
+ return acc;
51
+ }, {});
52
+ const onFailureAction = workflow.onFailure
53
+ ? {
54
+ [`${workflow.id}-on-failure:${workflow.onFailure.name}`]: workflow.onFailure.run,
55
+ }
56
+ : {};
57
+ this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
58
+ this.action_registry = ((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.name)
59
+ ? Object.assign(Object.assign({}, this.action_registry), { [`${workflow.id}:${workflow.concurrency.name}`]: workflow.concurrency.key }) : Object.assign({}, this.action_registry);
60
+ }
61
+ getHandler(workflow) {
62
+ const wf = Object.assign(Object.assign({}, workflow), { id: this.client.config.namespace + workflow.id });
63
+ this.registerActions(wf);
64
+ return new handler_1.WebhookHandler(this);
65
+ }
66
+ registerWebhook(webhook) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ return this.client.admin.webhook_create(Object.assign(Object.assign({}, webhook), { workflows: this.registeredWorkflowIds }));
69
+ });
70
+ }
45
71
  // @deprecated
46
72
  registerWorkflow(initWorkflow) {
47
73
  return __awaiter(this, void 0, void 0, function* () {
@@ -50,7 +76,7 @@ class Worker {
50
76
  }
51
77
  register_workflow(initWorkflow) {
52
78
  return __awaiter(this, void 0, void 0, function* () {
53
- var _a, _b, _c;
79
+ var _a, _b;
54
80
  const workflow = Object.assign(Object.assign({}, initWorkflow), { id: this.client.config.namespace + initWorkflow.id });
55
81
  try {
56
82
  const concurrency = ((_a = workflow.concurrency) === null || _a === void 0 ? void 0 : _a.name)
@@ -78,6 +104,7 @@ class Worker {
78
104
  ],
79
105
  }
80
106
  : undefined;
107
+ this.registeredWorkflowIds.push(workflow.id);
81
108
  const registeredWorkflow = this.client.admin.put_workflow({
82
109
  name: workflow.id,
83
110
  description: workflow.description,
@@ -114,145 +141,153 @@ class Worker {
114
141
  catch (e) {
115
142
  throw new hatchet_error_1.default(`Could not register workflow: ${e.message}`);
116
143
  }
117
- const newActions = workflow.steps.reduce((acc, step) => {
118
- acc[`${workflow.id}:${step.name}`] = step.run;
119
- return acc;
120
- }, {});
121
- const onFailureAction = workflow.onFailure
122
- ? {
123
- [`${workflow.id}-on-failure:${workflow.onFailure.name}`]: workflow.onFailure.run,
124
- }
125
- : {};
126
- this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
127
- this.action_registry = ((_c = workflow.concurrency) === null || _c === void 0 ? void 0 : _c.name)
128
- ? Object.assign(Object.assign({}, this.action_registry), { [`${workflow.id}:${workflow.concurrency.name}`]: workflow.concurrency.key }) : Object.assign({}, this.action_registry);
144
+ this.registerActions(workflow);
129
145
  });
130
146
  }
131
147
  registerAction(actionId, action) {
132
148
  this.action_registry[actionId] = action;
133
149
  }
134
150
  handleStartStepRun(action) {
135
- const { actionId } = action;
136
- try {
137
- const context = new step_1.Context(action, this.client);
138
- this.contexts[action.stepRunId] = context;
139
- const step = this.action_registry[actionId];
140
- if (!step) {
141
- this.logger.error(`Could not find step '${actionId}'`);
142
- return;
143
- }
144
- const run = () => __awaiter(this, void 0, void 0, function* () {
145
- return step(context);
146
- });
147
- const success = (result) => {
148
- this.logger.info(`Step run ${action.stepRunId} succeeded`);
149
- try {
150
- // Send the action event to the dispatcher
151
- const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, result || null);
152
- this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
153
- this.logger.error(`Could not send completed action event: ${e.message}`);
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ const { actionId } = action;
153
+ try {
154
+ const context = new step_1.Context(action, this.client);
155
+ this.contexts[action.stepRunId] = context;
156
+ const step = this.action_registry[actionId];
157
+ if (!step) {
158
+ this.logger.error(`Could not find step '${actionId}'`);
159
+ return;
160
+ }
161
+ const run = () => __awaiter(this, void 0, void 0, function* () {
162
+ return step(context);
163
+ });
164
+ const success = (result) => __awaiter(this, void 0, void 0, function* () {
165
+ this.logger.info(`Step run ${action.stepRunId} succeeded`);
166
+ try {
167
+ // Send the action event to the dispatcher
168
+ const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, result || null);
169
+ yield this.client.dispatcher.sendStepActionEvent(event);
170
+ // delete the run from the futures
171
+ delete this.futures[action.stepRunId];
172
+ }
173
+ catch (actionEventError) {
174
+ this.logger.error(`Could not send completed action event: ${actionEventError.message}`);
154
175
  // send a failure event
155
- const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, e.message);
156
- this.client.dispatcher.sendStepActionEvent(failureEvent).catch((err2) => {
157
- this.logger.error(`Could not send failed action event: ${err2.message}`);
176
+ const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, actionEventError.message);
177
+ try {
178
+ yield this.client.dispatcher.sendStepActionEvent(failureEvent);
179
+ }
180
+ catch (failureEventError) {
181
+ this.logger.error(`Could not send failed action event: ${failureEventError.message}`);
182
+ }
183
+ this.logger.error(`Could not send action event: ${actionEventError.message}`);
184
+ }
185
+ });
186
+ const failure = (error) => __awaiter(this, void 0, void 0, function* () {
187
+ this.logger.error(`Step run ${action.stepRunId} failed: ${error.message}`);
188
+ if (error.stack) {
189
+ this.logger.error(error.stack);
190
+ }
191
+ try {
192
+ // Send the action event to the dispatcher
193
+ const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, {
194
+ message: error === null || error === void 0 ? void 0 : error.message,
195
+ stack: error === null || error === void 0 ? void 0 : error.stack,
158
196
  });
159
- });
160
- // delete the run from the futures
161
- delete this.futures[action.stepRunId];
162
- }
163
- catch (e) {
164
- this.logger.error(`Could not send action event: ${e.message}`);
165
- }
166
- };
167
- const failure = (error) => {
168
- this.logger.error(`Step run ${action.stepRunId} failed: ${error.message}`);
169
- if (error.stack) {
170
- this.logger.error(error.stack);
171
- }
172
- try {
173
- // Send the action event to the dispatcher
174
- const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, {
175
- message: error === null || error === void 0 ? void 0 : error.message,
176
- stack: error === null || error === void 0 ? void 0 : error.stack,
177
- });
178
- this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
197
+ yield this.client.dispatcher.sendStepActionEvent(event);
198
+ // delete the run from the futures
199
+ delete this.futures[action.stepRunId];
200
+ }
201
+ catch (e) {
179
202
  this.logger.error(`Could not send action event: ${e.message}`);
180
- });
181
- // delete the run from the futures
182
- delete this.futures[action.stepRunId];
183
- }
184
- catch (e) {
203
+ }
204
+ });
205
+ const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
206
+ try {
207
+ yield run();
208
+ }
209
+ catch (e) {
210
+ yield failure(e);
211
+ return;
212
+ }
213
+ yield success(null);
214
+ }))());
215
+ this.futures[action.stepRunId] = future;
216
+ // Send the action event to the dispatcher
217
+ const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED);
218
+ this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
185
219
  this.logger.error(`Could not send action event: ${e.message}`);
186
- }
187
- };
188
- const future = new hatchet_promise_1.default(run().then(success).catch(failure));
189
- this.futures[action.stepRunId] = future;
190
- // Send the action event to the dispatcher
191
- const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED);
192
- this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
220
+ });
221
+ yield future.promise;
222
+ }
223
+ catch (e) {
193
224
  this.logger.error(`Could not send action event: ${e.message}`);
194
- });
195
- }
196
- catch (e) {
197
- this.logger.error(`Could not send action event: ${e.message}`);
198
- }
225
+ }
226
+ });
199
227
  }
200
228
  handleStartGroupKeyRun(action) {
201
- const { actionId } = action;
202
- try {
203
- const context = new step_1.Context(action, this.client);
204
- const key = action.getGroupKeyRunId;
205
- this.contexts[key] = context;
206
- this.logger.debug(`Starting group key run ${key}`);
207
- const step = this.action_registry[actionId];
208
- if (!step) {
209
- this.logger.error(`Could not find step '${actionId}'`);
210
- return;
211
- }
212
- const run = () => __awaiter(this, void 0, void 0, function* () {
213
- return step(context);
214
- });
215
- const success = (result) => {
216
- this.logger.info(`Step run ${action.stepRunId} succeeded`);
217
- try {
218
- // Send the action event to the dispatcher
219
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
220
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
221
- this.logger.error(`Could not send action event: ${e.message}`);
222
- });
223
- // delete the run from the futures
224
- delete this.futures[key];
229
+ return __awaiter(this, void 0, void 0, function* () {
230
+ const { actionId } = action;
231
+ try {
232
+ const context = new step_1.Context(action, this.client);
233
+ const key = action.getGroupKeyRunId;
234
+ if (!key) {
235
+ this.logger.error(`No group key run id provided for action ${actionId}`);
236
+ return;
225
237
  }
226
- catch (e) {
227
- this.logger.error(`Could not send action event: ${e.message}`);
238
+ this.contexts[key] = context;
239
+ this.logger.debug(`Starting group key run ${key}`);
240
+ const step = this.action_registry[actionId];
241
+ if (!step) {
242
+ this.logger.error(`Could not find step '${actionId}'`);
243
+ return;
228
244
  }
229
- };
230
- const failure = (error) => {
231
- this.logger.error(`Step run ${key} failed: ${error.message}`);
232
- try {
233
- // Send the action event to the dispatcher
234
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
235
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
245
+ const run = () => __awaiter(this, void 0, void 0, function* () {
246
+ return step(context);
247
+ });
248
+ const success = (result) => {
249
+ this.logger.info(`Step run ${action.stepRunId} succeeded`);
250
+ try {
251
+ // Send the action event to the dispatcher
252
+ const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
253
+ this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
254
+ this.logger.error(`Could not send action event: ${e.message}`);
255
+ });
256
+ // delete the run from the futures
257
+ delete this.futures[key];
258
+ }
259
+ catch (e) {
236
260
  this.logger.error(`Could not send action event: ${e.message}`);
237
- });
238
- // delete the run from the futures
239
- delete this.futures[key];
240
- }
241
- catch (e) {
261
+ }
262
+ };
263
+ const failure = (error) => {
264
+ this.logger.error(`Step run ${key} failed: ${error.message}`);
265
+ try {
266
+ // Send the action event to the dispatcher
267
+ const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
268
+ this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
269
+ this.logger.error(`Could not send action event: ${e.message}`);
270
+ });
271
+ // delete the run from the futures
272
+ delete this.futures[key];
273
+ }
274
+ catch (e) {
275
+ this.logger.error(`Could not send action event: ${e.message}`);
276
+ }
277
+ };
278
+ const future = new hatchet_promise_1.default(run().then(success).catch(failure));
279
+ this.futures[key] = future;
280
+ // Send the action event to the dispatcher
281
+ const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
282
+ this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
242
283
  this.logger.error(`Could not send action event: ${e.message}`);
243
- }
244
- };
245
- const future = new hatchet_promise_1.default(run().then(success).catch(failure));
246
- this.futures[action.getGroupKeyRunId] = future;
247
- // Send the action event to the dispatcher
248
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
249
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
284
+ });
285
+ yield future.promise;
286
+ }
287
+ catch (e) {
250
288
  this.logger.error(`Could not send action event: ${e.message}`);
251
- });
252
- }
253
- catch (e) {
254
- this.logger.error(`Could not send action event: ${e.message}`);
255
- }
289
+ }
290
+ });
256
291
  }
257
292
  getStepActionEvent(action, eventType, payload = '') {
258
293
  return {
@@ -268,6 +303,9 @@ class Worker {
268
303
  };
269
304
  }
270
305
  getGroupKeyActionEvent(action, eventType, payload = '') {
306
+ if (!action.getGroupKeyRunId) {
307
+ throw new hatchet_error_1.default('No group key run id provided');
308
+ }
271
309
  return {
272
310
  workerId: this.name,
273
311
  workflowRunId: action.workflowRunId,
@@ -279,26 +317,29 @@ class Worker {
279
317
  };
280
318
  }
281
319
  handleCancelStepRun(action) {
282
- try {
283
- this.logger.info(`Cancelling step run ${action.stepRunId}`);
284
- const { stepRunId } = action;
285
- const future = this.futures[stepRunId];
286
- const context = this.contexts[stepRunId];
287
- if (context && context.controller) {
288
- context.controller.abort('Cancelled by worker');
289
- delete this.contexts[stepRunId];
320
+ return __awaiter(this, void 0, void 0, function* () {
321
+ try {
322
+ this.logger.info(`Cancelling step run ${action.stepRunId}`);
323
+ const { stepRunId } = action;
324
+ const future = this.futures[stepRunId];
325
+ const context = this.contexts[stepRunId];
326
+ if (context && context.controller) {
327
+ context.controller.abort('Cancelled by worker');
328
+ delete this.contexts[stepRunId];
329
+ }
330
+ if (future) {
331
+ future.promise.catch(() => {
332
+ this.logger.info(`Cancelled step run ${action.stepRunId}`);
333
+ });
334
+ future.cancel('Cancelled by worker');
335
+ yield future.promise;
336
+ delete this.futures[stepRunId];
337
+ }
290
338
  }
291
- if (future) {
292
- future.promise.catch(() => {
293
- this.logger.info(`Cancelled step run ${action.stepRunId}`);
294
- });
295
- future.cancel('Cancelled by worker');
296
- delete this.futures[stepRunId];
339
+ catch (e) {
340
+ this.logger.error(`Could not cancel step run: ${e.message}`);
297
341
  }
298
- }
299
- catch (e) {
300
- this.logger.error(`Could not cancel step run: ${e.message}`);
301
- }
342
+ });
302
343
  }
303
344
  stop() {
304
345
  return __awaiter(this, void 0, void 0, function* () {
@@ -346,18 +387,7 @@ class Worker {
346
387
  _d = false;
347
388
  const action = _c;
348
389
  this.logger.info(`Worker ${this.name} received action ${action.actionId}:${action.actionType}`);
349
- if (action.actionType === dispatcher_1.ActionType.START_STEP_RUN) {
350
- this.handleStartStepRun(action);
351
- }
352
- else if (action.actionType === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
353
- this.handleCancelStepRun(action);
354
- }
355
- else if (action.actionType === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
356
- this.handleStartGroupKeyRun(action);
357
- }
358
- else {
359
- this.logger.error(`Worker ${this.name} received unknown action type ${action.actionType}`);
360
- }
390
+ void this.handleAction(action);
361
391
  }
362
392
  }
363
393
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -379,5 +409,24 @@ class Worker {
379
409
  }
380
410
  });
381
411
  }
412
+ handleAction(action) {
413
+ return __awaiter(this, void 0, void 0, function* () {
414
+ const type = action.actionType
415
+ ? (0, dispatcher_1.actionTypeFromJSON)(action.actionType)
416
+ : dispatcher_1.ActionType.START_STEP_RUN;
417
+ if (type === dispatcher_1.ActionType.START_STEP_RUN) {
418
+ yield this.handleStartStepRun(action);
419
+ }
420
+ else if (type === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
421
+ yield this.handleCancelStepRun(action);
422
+ }
423
+ else if (type === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
424
+ yield this.handleStartGroupKeyRun(action);
425
+ }
426
+ else {
427
+ this.logger.error(`Worker ${this.name} received unknown action type ${type}`);
428
+ }
429
+ });
430
+ }
382
431
  }
383
432
  exports.Worker = Worker;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hatchet-dev/typescript-sdk",
3
- "version": "0.7.4",
3
+ "version": "0.8.0-alpha.1",
4
4
  "description": "Background task orchestration & visibility for developers",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -19,7 +19,7 @@
19
19
  "prepare": "npm run build",
20
20
  "tsc:build": "tsc && resolve-tspaths",
21
21
  "test:unit": "jest --testMatch='**/*.test.ts'",
22
- "test:e2e": "jest --testMatch='**/*.e2e.ts'",
22
+ "test:e2e": "jest --testMatch='**/*.e2e.ts' --runInBand --detectOpenHandles",
23
23
  "test:unit:watch": "jest --testMatch='**/*.test.ts' --watch",
24
24
  "generate": "pnpm run '/generate-.*/'",
25
25
  "generate-api": "npx --yes swagger-cli bundle ./hatchet/api-contracts/openapi/openapi.yaml --outfile openapi.yaml --type yaml && npx swagger-typescript-api -p openapi.yaml -o src/clients/rest/generated -n hatchet.ts --modular --axios",
@@ -100,4 +100,4 @@
100
100
  "yaml": "^2.3.4",
101
101
  "zod": "^3.22.4"
102
102
  }
103
- }
103
+ }
package/util/parse.js CHANGED
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseJSON = void 0;
4
4
  function parseJSON(json) {
5
+ // TODO why is this needed?
6
+ if (json.startsWith('ey')) {
7
+ const decoded = Buffer.from(json, 'base64').toString('utf8');
8
+ return JSON.parse(decoded);
9
+ }
5
10
  try {
6
11
  const firstParse = JSON.parse(json);
7
12
  // Hatchet engine versions <=0.14.0 return JSON as a quoted string which needs to be parsed again.