@hatchet-dev/typescript-sdk 1.34.0-alpha.1 → 1.34.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 (71) hide show
  1. package/README.md +1 -0
  2. package/clients/admin/rpc.d.ts +1 -1
  3. package/clients/admin/rpc.js +3 -3
  4. package/clients/admin/trigger-request.d.ts +18 -0
  5. package/clients/admin/trigger-request.js +66 -0
  6. package/clients/event/event-client.d.ts +4 -17
  7. package/clients/event/event-client.js +5 -43
  8. package/clients/event/rpc.d.ts +22 -3
  9. package/clients/event/rpc.js +66 -3
  10. package/clients/transport/fetch-transport.d.ts +73 -0
  11. package/clients/transport/fetch-transport.js +152 -0
  12. package/clients/transport/grpc-target.js +48 -3
  13. package/clients/transport/index.d.ts +2 -1
  14. package/clients/transport/index.js +6 -1
  15. package/clients/transport/node-transport.js +1 -2
  16. package/clients/transport/transport.d.ts +6 -0
  17. package/clients/transport/transport.js +7 -0
  18. package/clients/transport/ts-proto-client.js +16 -2
  19. package/core/client.d.ts +102 -0
  20. package/core/client.js +250 -0
  21. package/core/features/events.d.ts +25 -0
  22. package/core/features/events.js +57 -0
  23. package/core/features/logs.d.ts +26 -0
  24. package/core/features/logs.js +51 -0
  25. package/core/features/rate-limits.d.ts +19 -0
  26. package/core/features/rate-limits.js +30 -0
  27. package/core/features/runs.d.ts +33 -0
  28. package/core/features/runs.js +84 -0
  29. package/core/features/streams.d.ts +11 -0
  30. package/core/features/streams.js +23 -0
  31. package/core/features/workflows.d.ts +22 -0
  32. package/core/features/workflows.js +46 -0
  33. package/core/index.d.ts +42 -4
  34. package/core/index.js +79 -5
  35. package/core/logger.d.ts +19 -0
  36. package/core/logger.js +45 -0
  37. package/core/run-detail.d.ts +14 -0
  38. package/core/run-detail.js +79 -0
  39. package/core/run-ref.d.ts +63 -0
  40. package/core/run-ref.js +217 -0
  41. package/core/types.d.ts +182 -0
  42. package/core/types.js +2 -0
  43. package/dist/check-edge-entry.mjs +72 -14
  44. package/dist/check-edge-entry.test.ts +57 -0
  45. package/package.json +5 -2
  46. package/protoc/v1/streams.d.ts +79 -0
  47. package/protoc/v1/streams.js +511 -0
  48. package/protoc-es/v1/streams_pb.d.ts +133 -0
  49. package/protoc-es/v1/streams_pb.js +41 -0
  50. package/scripts/check-edge-entry.mjs +72 -14
  51. package/scripts/check-edge-entry.test.ts +57 -0
  52. package/util/batch.js +2 -1
  53. package/util/config-loader/token.d.ts +5 -0
  54. package/util/config-loader/token.js +8 -0
  55. package/util/errors/bulk-trigger-partial-error.d.ts +17 -0
  56. package/util/errors/bulk-trigger-partial-error.js +55 -0
  57. package/v1/client/admin.d.ts +4 -54
  58. package/v1/client/admin.js +9 -72
  59. package/v1/client/client.d.ts +14 -1
  60. package/v1/client/client.js +18 -0
  61. package/v1/client/features/index.d.ts +1 -0
  62. package/v1/client/features/index.js +1 -0
  63. package/v1/client/features/runs.d.ts +9 -27
  64. package/v1/client/features/runs.js +10 -55
  65. package/v1/client/features/streams.d.ts +47 -0
  66. package/v1/client/features/streams.js +210 -0
  67. package/v1/examples/durable-streams/publish-latency.d.ts +15 -0
  68. package/v1/examples/durable-streams/publish-latency.js +50 -0
  69. package/v1/examples/durable_callback_ordering/workflow.js +5 -1
  70. package/version.d.ts +1 -1
  71. package/version.js +1 -1
@@ -1,10 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toConnectCallOptions = exports.metadataToHeaders = exports.headersToMetadata = exports.createTsProtoClient = exports.toProtobufEs = exports.fromProtobufEs = exports.createNodeTransport = exports.createAuthInterceptor = void 0;
3
+ exports.toConnectCallOptions = exports.metadataToHeaders = exports.headersToMetadata = exports.createTsProtoClient = exports.toProtobufEs = exports.fromProtobufEs = exports.resolveServerUrl = exports.limitMessageSizes = exports.createFetchTransport = exports.createNodeTransport = exports.DEFAULT_MAX_MESSAGE_BYTES = exports.createAuthInterceptor = void 0;
4
4
  var transport_1 = require("./transport");
5
5
  Object.defineProperty(exports, "createAuthInterceptor", { enumerable: true, get: function () { return transport_1.createAuthInterceptor; } });
6
+ Object.defineProperty(exports, "DEFAULT_MAX_MESSAGE_BYTES", { enumerable: true, get: function () { return transport_1.DEFAULT_MAX_MESSAGE_BYTES; } });
6
7
  var node_transport_1 = require("./node-transport");
7
8
  Object.defineProperty(exports, "createNodeTransport", { enumerable: true, get: function () { return node_transport_1.createNodeTransport; } });
9
+ var fetch_transport_1 = require("./fetch-transport");
10
+ Object.defineProperty(exports, "createFetchTransport", { enumerable: true, get: function () { return fetch_transport_1.createFetchTransport; } });
11
+ Object.defineProperty(exports, "limitMessageSizes", { enumerable: true, get: function () { return fetch_transport_1.limitMessageSizes; } });
12
+ Object.defineProperty(exports, "resolveServerUrl", { enumerable: true, get: function () { return fetch_transport_1.resolveServerUrl; } });
8
13
  var message_bridge_1 = require("./message-bridge");
9
14
  Object.defineProperty(exports, "fromProtobufEs", { enumerable: true, get: function () { return message_bridge_1.fromProtobufEs; } });
10
15
  Object.defineProperty(exports, "toProtobufEs", { enumerable: true, get: function () { return message_bridge_1.toProtobufEs; } });
@@ -8,7 +8,6 @@ const fs_1 = require("fs");
8
8
  const connect_node_1 = require("@connectrpc/connect-node");
9
9
  const grpc_target_1 = require("./grpc-target");
10
10
  const transport_1 = require("./transport");
11
- const DEFAULT_MAX_MESSAGE_BYTES = 4 * 1024 * 1024;
12
11
  /**
13
12
  * The largest message size Connect can enforce (a 32-bit length prefix). The config schema
14
13
  * accepts any positive integer, as grpc-js does, so a larger configured limit is clamped to it
@@ -16,7 +15,7 @@ const DEFAULT_MAX_MESSAGE_BYTES = 4 * 1024 * 1024;
16
15
  */
17
16
  exports.MAX_MESSAGE_BYTES = 0xffffffff;
18
17
  function messageLimit(configured) {
19
- return Math.min(configured !== null && configured !== void 0 ? configured : DEFAULT_MAX_MESSAGE_BYTES, exports.MAX_MESSAGE_BYTES);
18
+ return Math.min(configured !== null && configured !== void 0 ? configured : transport_1.DEFAULT_MAX_MESSAGE_BYTES, exports.MAX_MESSAGE_BYTES);
20
19
  }
21
20
  /**
22
21
  * The connection settings the `nice-grpc` channel uses (`grpc.keepalive_time_ms`,
@@ -5,6 +5,12 @@ import type { Interceptor, Transport } from '@connectrpc/connect';
5
5
  * interceptors that apply to every call.
6
6
  */
7
7
  export type { Transport };
8
+ /**
9
+ * The largest message either transport accepts or sends unless configured otherwise: the
10
+ * grpc-js default the `grpc_max_recv_message_length` and `grpc_max_send_message_length`
11
+ * settings override.
12
+ */
13
+ export declare const DEFAULT_MAX_MESSAGE_BYTES: number;
8
14
  /**
9
15
  * Sets the bearer token the engine authenticates every call with. The transport applies it to
10
16
  * each request, so callers never pass credentials per call. A token that cannot be carried in a
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MAX_MESSAGE_BYTES = void 0;
3
4
  exports.createAuthInterceptor = createAuthInterceptor;
5
+ /**
6
+ * The largest message either transport accepts or sends unless configured otherwise: the
7
+ * grpc-js default the `grpc_max_recv_message_length` and `grpc_max_send_message_length`
8
+ * settings override.
9
+ */
10
+ exports.DEFAULT_MAX_MESSAGE_BYTES = 4 * 1024 * 1024;
4
11
  /**
5
12
  * The characters a token may contain and still travel in an HTTP header: the printable ASCII
6
13
  * range, which is also what grpc-js accepts in metadata. Anything else would make the header
@@ -111,6 +111,20 @@ function toConnectCallOptions(options) {
111
111
  }
112
112
  return connect;
113
113
  }
114
+ /*
115
+ * Binary metadata is base64 on the wire. These use the standard `btoa`/`atob` rather than
116
+ * `Buffer`, since the fetch transport runs where `Buffer` does not exist.
117
+ */
118
+ function bytesToBase64(bytes) {
119
+ let binary = '';
120
+ for (const byte of bytes) {
121
+ binary += String.fromCharCode(byte);
122
+ }
123
+ return btoa(binary);
124
+ }
125
+ function base64ToBytes(base64) {
126
+ return Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
127
+ }
114
128
  /**
115
129
  * gRPC metadata as HTTP headers: binary values (keys ending in `-bin`) are base64 encoded,
116
130
  * which is how they travel on the wire in every gRPC implementation.
@@ -119,7 +133,7 @@ function metadataToHeaders(metadata) {
119
133
  const headers = new Headers();
120
134
  for (const [key, values] of metadata) {
121
135
  for (const value of values) {
122
- headers.append(key, typeof value === 'string' ? value : Buffer.from(value).toString('base64'));
136
+ headers.append(key, typeof value === 'string' ? value : bytesToBase64(value));
123
137
  }
124
138
  }
125
139
  return headers;
@@ -133,7 +147,7 @@ function headersToMetadata(headers) {
133
147
  const metadata = (0, nice_grpc_common_1.Metadata)();
134
148
  headers.forEach((value, key) => {
135
149
  if (key.endsWith('-bin')) {
136
- metadata.set(key, value.split(',').map((part) => new Uint8Array(Buffer.from(part.trim(), 'base64'))));
150
+ metadata.set(key, value.split(',').map((part) => base64ToBytes(part.trim())));
137
151
  }
138
152
  else {
139
153
  metadata.set(key, value);
@@ -0,0 +1,102 @@
1
+ import type { Transport } from '@connectrpc/connect';
2
+ import type { Logger } from '../util/logger/logger';
3
+ import { BaseWorkflowDeclaration, type RunManyOpt } from '../v1/declaration';
4
+ import type { InputType, OutputType, UnknownInputType } from '../v1/types';
5
+ import { EventsClient } from './features/events';
6
+ import { LogsClient } from './features/logs';
7
+ import { RateLimitsClient } from './features/rate-limits';
8
+ import { RunsClient } from './features/runs';
9
+ import { StreamsClient } from './features/streams';
10
+ import { WorkflowsClient } from './features/workflows';
11
+ import { WorkflowRunRef } from './run-ref';
12
+ import type { CoreClientConfig, TriggerRunOptions } from './types';
13
+ /** A workflow to run: a declaration from `/edge`, anything with a `name`, or the name itself. */
14
+ export type WorkflowRef<I extends InputType = UnknownInputType, O extends OutputType = void> = BaseWorkflowDeclaration<I, O> | {
15
+ name: string;
16
+ } | string;
17
+ /**
18
+ * The resolved client configuration: what the client was built with, plus the values derived
19
+ * from it.
20
+ */
21
+ export interface ResolvedCoreConfig {
22
+ serverUrl: string;
23
+ tenantId: string;
24
+ namespace: string;
25
+ logLevel: CoreClientConfig['logLevel'];
26
+ retrier: CoreClientConfig['retrier'];
27
+ }
28
+ /**
29
+ * The application client for runtimes without gRPC: it triggers runs, pushes events, waits
30
+ * for results and manages workflows over unary Connect calls on a fetch transport, so it runs
31
+ * in Cloudflare Workers, Vercel Functions, Deno, Bun, browsers and Node alike.
32
+ *
33
+ * It is configured from an explicit object only. Nothing is read from the environment or a
34
+ * config file, and no worker can be started from it; workers stay on the Node client.
35
+ *
36
+ * ```typescript
37
+ * import { Hatchet } from './';
38
+ *
39
+ * const hatchet = new Hatchet({ token: env.HATCHET_CLIENT_TOKEN });
40
+ * const output = await hatchet.run('greet', { name: 'world' });
41
+ * ```
42
+ */
43
+ export declare class HatchetCore {
44
+ readonly config: ResolvedCoreConfig;
45
+ /** The transport every call goes through. */
46
+ readonly transport: Transport;
47
+ readonly logger: Logger;
48
+ /** Pushes events. */
49
+ readonly events: EventsClient;
50
+ /** Gets, cancels and replays runs. */
51
+ readonly runs: RunsClient;
52
+ /** Registers workflows. */
53
+ readonly workflows: WorkflowsClient;
54
+ /** Creates and updates rate limits. */
55
+ readonly rateLimits: RateLimitsClient;
56
+ /** Writes task run log lines. */
57
+ readonly logs: LogsClient;
58
+ /** Publishes task run stream chunks. */
59
+ readonly streams: StreamsClient;
60
+ private readonly workflowsRpc;
61
+ private readonly adminRpc;
62
+ private readonly eventsRpc;
63
+ constructor(config: CoreClientConfig);
64
+ /** The same as `new HatchetCore(config)`, mirroring the Node client's `init`. */
65
+ static init(config: CoreClientConfig): HatchetCore;
66
+ /** The tenant the token belongs to. */
67
+ get tenantId(): string;
68
+ /**
69
+ * Triggers a run and returns a reference to it without waiting.
70
+ *
71
+ * The options are the Node client's `admin.runWorkflow` options and go to the engine as
72
+ * is: the workflow name is namespaced and lowercased and the input is JSON. A task
73
+ * declaration's reference resolves to the task's own output.
74
+ */
75
+ runNoWait<I extends InputType = UnknownInputType, O extends OutputType = void>(workflow: WorkflowRef<I, O>, input: I, options?: TriggerRunOptions): Promise<WorkflowRunRef<O>>;
76
+ /** Triggers a run and waits for its output; see `WorkflowRunRef.result()` for the wait. */
77
+ run<I extends InputType = UnknownInputType, O extends OutputType = void>(workflow: WorkflowRef<I, O>, input: I, options?: TriggerRunOptions): Promise<O>;
78
+ /**
79
+ * Triggers many runs of one workflow in batches of 500 (or 4 MB) over `BulkTriggerWorkflow`
80
+ * and returns their references in input order. `runs` takes the `RunManyOpt` entries the
81
+ * declarations' `runMany` takes.
82
+ *
83
+ * Each batch is one call, so a failure part way through leaves the earlier batches' runs
84
+ * created. That failure rejects with a `BulkTriggerPartialError` whose `refs` are those runs'
85
+ * references and whose `cause` is the batch's own error, so the caller can keep or cancel
86
+ * them instead of retrying the whole request and creating them twice. A failure of the
87
+ * first batch rejects with the batch's error itself: a
88
+ * `BulkTriggerIdempotencyCollisionError` on an idempotency key collision, otherwise a
89
+ * `HatchetError`.
90
+ */
91
+ runManyNoWait<I extends InputType = UnknownInputType, O extends OutputType = void>(workflow: WorkflowRef<I, O>, runs: RunManyOpt<I>[]): Promise<WorkflowRunRef<O>[]>;
92
+ /**
93
+ * Triggers many runs of one workflow and waits for all of their outputs, in input order.
94
+ *
95
+ * When any entry sets `opts.returnExceptions`, a failed run does not reject the whole
96
+ * wait: its slot holds an `Error` instead (a task error list is joined with `; `), the way
97
+ * a declaration's `runMany` behaves on the Node client.
98
+ */
99
+ runMany<I extends InputType = UnknownInputType, O extends OutputType = void>(workflow: WorkflowRef<I, O>, runs: RunManyOpt<I>[]): Promise<O[]>;
100
+ /** A reference to an existing run, to wait on, cancel or replay it. */
101
+ runRef<T = unknown>(id: string): WorkflowRunRef<T>;
102
+ }
package/core/client.js ADDED
@@ -0,0 +1,250 @@
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.HatchetCore = void 0;
16
+ const hatchet_error_1 = __importDefault(require("../util/errors/hatchet-error"));
17
+ const idempotency_collision_error_1 = require("../util/errors/idempotency-collision-error");
18
+ const bulk_trigger_partial_error_1 = require("../util/errors/bulk-trigger-partial-error");
19
+ const fetch_transport_1 = require("../clients/transport/fetch-transport");
20
+ const rpc_1 = require("../clients/admin/rpc");
21
+ const trigger_request_1 = require("../clients/admin/trigger-request");
22
+ const rpc_2 = require("../clients/event/rpc");
23
+ const workflows_1 = require("../protoc/workflows");
24
+ const token_1 = require("../util/config-loader/token");
25
+ const batch_1 = require("../util/batch");
26
+ const retrier_1 = require("../util/retrier");
27
+ const declaration_1 = require("../v1/declaration");
28
+ const events_1 = require("./features/events");
29
+ const logs_1 = require("./features/logs");
30
+ const rate_limits_1 = require("./features/rate-limits");
31
+ const runs_1 = require("./features/runs");
32
+ const streams_1 = require("./features/streams");
33
+ const workflows_2 = require("./features/workflows");
34
+ const logger_1 = require("./logger");
35
+ const run_ref_1 = require("./run-ref");
36
+ const BULK_TRIGGER_BATCH_SIZE = 500;
37
+ const BULK_TRIGGER_MAX_BYTES = 4 * 1024 * 1024;
38
+ /**
39
+ * The application client for runtimes without gRPC: it triggers runs, pushes events, waits
40
+ * for results and manages workflows over unary Connect calls on a fetch transport, so it runs
41
+ * in Cloudflare Workers, Vercel Functions, Deno, Bun, browsers and Node alike.
42
+ *
43
+ * It is configured from an explicit object only. Nothing is read from the environment or a
44
+ * config file, and no worker can be started from it; workers stay on the Node client.
45
+ *
46
+ * ```typescript
47
+ * import { Hatchet } from './index.js';
48
+ *
49
+ * const hatchet = new Hatchet({ token: env.HATCHET_CLIENT_TOKEN });
50
+ * const output = await hatchet.run('greet', { name: 'world' });
51
+ * ```
52
+ */
53
+ class HatchetCore {
54
+ constructor(config) {
55
+ var _a, _b, _c;
56
+ if (!config.token) {
57
+ throw new hatchet_error_1.default('a token is required to create a Hatchet client');
58
+ }
59
+ const tenantId = (0, token_1.getTenantIdFromJWT)(config.token);
60
+ // a caller-supplied transport owns its base URL, auth and limits, so no address is resolved
61
+ const serverUrl = config.transport ? ((_a = config.serverUrl) !== null && _a !== void 0 ? _a : '') : resolveServerUrl(config);
62
+ const namespace = normalizeNamespace(config.namespace);
63
+ const logger = (_b = config.logger) !== null && _b !== void 0 ? _b : logger_1.consoleLogger;
64
+ this.config = {
65
+ serverUrl,
66
+ tenantId,
67
+ namespace,
68
+ logLevel: config.logLevel,
69
+ retrier: config.retrier,
70
+ };
71
+ this.logger = logger('HatchetCore', config.logLevel);
72
+ this.transport =
73
+ (_c = config.transport) !== null && _c !== void 0 ? _c : (0, fetch_transport_1.createFetchTransport)({
74
+ token: config.token,
75
+ serverUrl,
76
+ tls: config.tls,
77
+ fetch: config.fetch,
78
+ maxReceiveMessageBytes: config.maxReceiveMessageBytes,
79
+ maxSendMessageBytes: config.maxSendMessageBytes,
80
+ });
81
+ this.workflowsRpc = (0, rpc_1.createWorkflowsRpc)(this.transport);
82
+ this.adminRpc = (0, rpc_1.createV1AdminRpc)(this.transport);
83
+ this.eventsRpc = (0, rpc_2.createEventsRpc)(this.transport);
84
+ const shared = { logger: this.logger, namespace, retrier: config.retrier };
85
+ this.events = new events_1.EventsClient(Object.assign({ rpc: this.eventsRpc }, shared));
86
+ this.runs = new runs_1.RunsClient(this.adminRpc);
87
+ this.workflows = new workflows_2.WorkflowsClient(Object.assign({ rpc: this.adminRpc }, shared));
88
+ this.rateLimits = new rate_limits_1.RateLimitsClient(Object.assign({ rpc: this.workflowsRpc }, shared));
89
+ this.logs = new logs_1.LogsClient(this.eventsRpc);
90
+ this.streams = new streams_1.StreamsClient(this.eventsRpc);
91
+ }
92
+ /** The same as `new HatchetCore(config)`, mirroring the Node client's `init`. */
93
+ static init(config) {
94
+ return new HatchetCore(config);
95
+ }
96
+ /** The tenant the token belongs to. */
97
+ get tenantId() {
98
+ return this.config.tenantId;
99
+ }
100
+ /**
101
+ * Triggers a run and returns a reference to it without waiting.
102
+ *
103
+ * The options are the Node client's `admin.runWorkflow` options and go to the engine as
104
+ * is: the workflow name is namespaced and lowercased and the input is JSON. A task
105
+ * declaration's reference resolves to the task's own output.
106
+ */
107
+ runNoWait(workflow_1, input_1) {
108
+ return __awaiter(this, arguments, void 0, function* (workflow, input, options = {}) {
109
+ var _a;
110
+ const request = (0, trigger_request_1.buildTriggerWorkflowRequest)(workflowName(workflow), input, options, this.config.namespace);
111
+ try {
112
+ const response = yield (0, retrier_1.retrier)(() => this.workflowsRpc.triggerWorkflow(request), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !(0, trigger_request_1.isAlreadyExists)(e) }));
113
+ return new run_ref_1.WorkflowRunRef(response.workflowRunId, this.runs, options.parentId, (_a = options._standaloneTaskName) !== null && _a !== void 0 ? _a : standaloneTaskName(workflow));
114
+ }
115
+ catch (e) {
116
+ if ((0, trigger_request_1.isAlreadyExists)(e)) {
117
+ throw new idempotency_collision_error_1.IdempotencyCollisionError((0, trigger_request_1.extractExistingRunId)(e));
118
+ }
119
+ throw new hatchet_error_1.default(e instanceof Error ? e.message : String(e));
120
+ }
121
+ });
122
+ }
123
+ /** Triggers a run and waits for its output; see `WorkflowRunRef.result()` for the wait. */
124
+ run(workflow_1, input_1) {
125
+ return __awaiter(this, arguments, void 0, function* (workflow, input, options = {}) {
126
+ const ref = yield this.runNoWait(workflow, input, options);
127
+ return ref.result();
128
+ });
129
+ }
130
+ /**
131
+ * Triggers many runs of one workflow in batches of 500 (or 4 MB) over `BulkTriggerWorkflow`
132
+ * and returns their references in input order. `runs` takes the `RunManyOpt` entries the
133
+ * declarations' `runMany` takes.
134
+ *
135
+ * Each batch is one call, so a failure part way through leaves the earlier batches' runs
136
+ * created. That failure rejects with a `BulkTriggerPartialError` whose `refs` are those runs'
137
+ * references and whose `cause` is the batch's own error, so the caller can keep or cancel
138
+ * them instead of retrying the whole request and creating them twice. A failure of the
139
+ * first batch rejects with the batch's error itself: a
140
+ * `BulkTriggerIdempotencyCollisionError` on an idempotency key collision, otherwise a
141
+ * `HatchetError`.
142
+ */
143
+ runManyNoWait(workflow, runs) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ const name = workflowName(workflow);
146
+ const taskName = standaloneTaskName(workflow);
147
+ const requests = runs.map((run) => (0, trigger_request_1.buildTriggerWorkflowRequest)(name, run.input, runOptsToTrigger(run.opts), this.config.namespace));
148
+ const batches = (0, batch_1.batch)(requests, BULK_TRIGGER_BATCH_SIZE, BULK_TRIGGER_MAX_BYTES);
149
+ const refs = [];
150
+ for (const { batchIndex, payloads } of batches) {
151
+ const request = workflows_1.BulkTriggerWorkflowRequest.create({ workflows: payloads });
152
+ let response;
153
+ try {
154
+ response = yield (0, retrier_1.retrier)(() => this.workflowsRpc.bulkTriggerWorkflow(request), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !(0, trigger_request_1.isAlreadyExists)(e) }));
155
+ }
156
+ catch (e) {
157
+ const error = bulkTriggerError(e);
158
+ throw batchIndex === 0 ? error : new bulk_trigger_partial_error_1.BulkTriggerPartialError(refs, batchIndex, error);
159
+ }
160
+ refs.push(...response.workflowRunIds.map((id) => new run_ref_1.WorkflowRunRef(id, this.runs, undefined, taskName)));
161
+ }
162
+ return refs;
163
+ });
164
+ }
165
+ /**
166
+ * Triggers many runs of one workflow and waits for all of their outputs, in input order.
167
+ *
168
+ * When any entry sets `opts.returnExceptions`, a failed run does not reject the whole
169
+ * wait: its slot holds an `Error` instead (a task error list is joined with `; `), the way
170
+ * a declaration's `runMany` behaves on the Node client.
171
+ */
172
+ runMany(workflow, runs) {
173
+ return __awaiter(this, void 0, void 0, function* () {
174
+ const refs = yield this.runManyNoWait(workflow, runs);
175
+ const results = refs.map((ref) => ref.result());
176
+ if (runs.some((run) => { var _a; return (_a = run.opts) === null || _a === void 0 ? void 0 : _a.returnExceptions; })) {
177
+ const settled = yield Promise.allSettled(results);
178
+ return settled.map((s) => (s.status === 'fulfilled' ? s.value : asError(s.reason)));
179
+ }
180
+ return Promise.all(results);
181
+ });
182
+ }
183
+ /** A reference to an existing run, to wait on, cancel or replay it. */
184
+ runRef(id) {
185
+ return this.runs.runRef(id);
186
+ }
187
+ }
188
+ exports.HatchetCore = HatchetCore;
189
+ function workflowName(workflow) {
190
+ return typeof workflow === 'string' ? workflow : workflow.name;
191
+ }
192
+ function standaloneTaskName(workflow) {
193
+ return workflow instanceof declaration_1.TaskWorkflowDeclaration ? workflow._standalone_task_name : undefined;
194
+ }
195
+ /**
196
+ * The error one bulk trigger batch raises: the collision details when the engine attached
197
+ * them, otherwise a `HatchetError`.
198
+ */
199
+ function bulkTriggerError(e) {
200
+ if ((0, trigger_request_1.isAlreadyExists)(e)) {
201
+ const collision = (0, trigger_request_1.extractBulkTriggerCollision)(e);
202
+ if (collision)
203
+ return collision;
204
+ }
205
+ return new hatchet_error_1.default(e instanceof Error ? e.message : String(e));
206
+ }
207
+ /**
208
+ * The rejection of one run as `runMany` returns it under `returnExceptions`: an `Error` as is,
209
+ * a task error list joined with `; `, anything else as its string; the Node declaration's
210
+ * normalization.
211
+ */
212
+ function asError(reason) {
213
+ if (reason instanceof Error)
214
+ return reason;
215
+ return new Error(Array.isArray(reason) ? reason.join('; ') : String(reason));
216
+ }
217
+ /**
218
+ * Maps a declaration's `RunOpts` onto the trigger options. `sticky` only means something
219
+ * inside a worker task and is dropped; `returnExceptions` is read by `runMany` itself.
220
+ */
221
+ function runOptsToTrigger(opts) {
222
+ if (!opts)
223
+ return undefined;
224
+ return {
225
+ additionalMetadata: opts.additionalMetadata,
226
+ priority: opts.priority,
227
+ childKey: opts.childKey,
228
+ desiredWorkerLabels: opts.desiredWorkerLabels,
229
+ };
230
+ }
231
+ /**
232
+ * The engine's base URL: `serverUrl` checked against the TLS strategy, `hostPort` with the
233
+ * TLS strategy, or the `grpc_broadcast_address` the token was issued with.
234
+ */
235
+ function resolveServerUrl(config) {
236
+ if (config.serverUrl || config.hostPort) {
237
+ return (0, fetch_transport_1.resolveServerUrl)(config);
238
+ }
239
+ const hostPort = (0, token_1.getGrpcBroadcastAddressFromJWT)(config.token);
240
+ if (!hostPort) {
241
+ throw new hatchet_error_1.default('the token carries no grpc_broadcast_address claim; set serverUrl or hostPort on the client config');
242
+ }
243
+ return (0, fetch_transport_1.resolveServerUrl)({ hostPort, tls: config.tls });
244
+ }
245
+ /** Namespaces end in an underscore and are lowercase, as the Node client's config loader makes them. */
246
+ function normalizeNamespace(namespace) {
247
+ if (!namespace)
248
+ return '';
249
+ return (namespace.endsWith('_') ? namespace : `${namespace}_`).toLowerCase();
250
+ }
@@ -0,0 +1,25 @@
1
+ import { type RetrierConfig } from '../../util/retrier';
2
+ import type { Logger } from '../../util/logger/logger';
3
+ import type { Event, Events, EventsServiceClient } from '../../protoc/events/events';
4
+ import type { EventWithMetadata, PushEventOptions } from '../types';
5
+ export interface EventsClientDeps {
6
+ rpc: EventsServiceClient;
7
+ logger: Logger;
8
+ namespace?: string;
9
+ retrier?: RetrierConfig;
10
+ }
11
+ /** Pushes events that trigger event-bound workflows. */
12
+ export declare class EventsClient {
13
+ private readonly deps;
14
+ constructor(deps: EventsClientDeps);
15
+ /**
16
+ * Pushes one event. The key is namespaced the way the Node client namespaces it.
17
+ * @returns the stored event
18
+ */
19
+ push<T>(type: string, input: T, options?: PushEventOptions): Promise<Event>;
20
+ /**
21
+ * Pushes many events of one type in a single call.
22
+ * @returns the stored events
23
+ */
24
+ bulkPush<T>(type: string, inputs: EventWithMetadata<T>[], options?: PushEventOptions): Promise<Events>;
25
+ }
@@ -0,0 +1,57 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.EventsClient = void 0;
13
+ const hatchet_error_1 = require("../../util/errors/hatchet-error");
14
+ const retrier_1 = require("../../util/retrier");
15
+ const rpc_1 = require("../../clients/event/rpc");
16
+ /** Pushes events that trigger event-bound workflows. */
17
+ class EventsClient {
18
+ constructor(deps) {
19
+ this.deps = deps;
20
+ }
21
+ /**
22
+ * Pushes one event. The key is namespaced the way the Node client namespaces it.
23
+ * @returns the stored event
24
+ */
25
+ push(type_1, input_1) {
26
+ return __awaiter(this, arguments, void 0, function* (type, input, options = {}) {
27
+ const request = (0, rpc_1.buildPushEventRequest)(type, input, options, this.deps.namespace);
28
+ try {
29
+ const event = yield (0, retrier_1.retrier)(() => this.deps.rpc.push(request), this.deps.logger, this.deps.retrier);
30
+ this.deps.logger.debug(`Event pushed: ${request.key}`);
31
+ return event;
32
+ }
33
+ catch (e) {
34
+ throw (0, hatchet_error_1.toHatchetError)(e);
35
+ }
36
+ });
37
+ }
38
+ /**
39
+ * Pushes many events of one type in a single call.
40
+ * @returns the stored events
41
+ */
42
+ bulkPush(type_1, inputs_1) {
43
+ return __awaiter(this, arguments, void 0, function* (type, inputs, options = {}) {
44
+ var _a, _b;
45
+ const request = (0, rpc_1.buildBulkPushEventRequest)(type, inputs, options, this.deps.namespace);
46
+ try {
47
+ const events = yield (0, retrier_1.retrier)(() => this.deps.rpc.bulkPush(request), this.deps.logger, this.deps.retrier);
48
+ this.deps.logger.debug(`Bulk events pushed for type: ${(_b = (_a = request.events[0]) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : type}`);
49
+ return events;
50
+ }
51
+ catch (e) {
52
+ throw (0, hatchet_error_1.toHatchetError)(e);
53
+ }
54
+ });
55
+ }
56
+ }
57
+ exports.EventsClient = EventsClient;
@@ -0,0 +1,26 @@
1
+ import { LogLevel } from '../../clients/event/rpc';
2
+ import type { EventsServiceClient, PutLogResponse } from '../../protoc/events/events';
3
+ /** The longest log line accepted, in characters; the Node client's `putLog` holds the same limit. */
4
+ export declare const MAX_LOG_LINE_CHARS = 1000;
5
+ export interface PutLogOptions {
6
+ /** Defaults to `INFO`. */
7
+ level?: LogLevel;
8
+ /** The attempt the line belongs to, when the task has retried. */
9
+ retryCount?: number;
10
+ metadata?: Record<string, unknown>;
11
+ }
12
+ /** Writes log lines against a task run, the way `ctx.log` does from a worker. */
13
+ export declare class LogsClient {
14
+ private readonly rpc;
15
+ constructor(rpc: EventsServiceClient);
16
+ /**
17
+ * Stores one log line for a task run. Unlike the Node client's fire-and-forget `putLog`,
18
+ * this resolves once the engine has the line and rejects when it does not, since a
19
+ * serverless runtime may end the invocation as soon as the handler returns.
20
+ *
21
+ * A line over `MAX_LOG_LINE_CHARS` (1,000) characters rejects with a `HatchetError` that
22
+ * states the length, where the Node client warns and drops the line; the message itself is
23
+ * never part of the error.
24
+ */
25
+ put(taskRunExternalId: string, message: string, options?: PutLogOptions): Promise<PutLogResponse>;
26
+ }
@@ -0,0 +1,51 @@
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.LogsClient = exports.MAX_LOG_LINE_CHARS = void 0;
16
+ const rpc_1 = require("../../clients/event/rpc");
17
+ const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
18
+ /** The longest log line accepted, in characters; the Node client's `putLog` holds the same limit. */
19
+ exports.MAX_LOG_LINE_CHARS = 1000;
20
+ /** Writes log lines against a task run, the way `ctx.log` does from a worker. */
21
+ class LogsClient {
22
+ constructor(rpc) {
23
+ this.rpc = rpc;
24
+ }
25
+ /**
26
+ * Stores one log line for a task run. Unlike the Node client's fire-and-forget `putLog`,
27
+ * this resolves once the engine has the line and rejects when it does not, since a
28
+ * serverless runtime may end the invocation as soon as the handler returns.
29
+ *
30
+ * A line over `MAX_LOG_LINE_CHARS` (1,000) characters rejects with a `HatchetError` that
31
+ * states the length, where the Node client warns and drops the line; the message itself is
32
+ * never part of the error.
33
+ */
34
+ put(taskRunExternalId_1, message_1) {
35
+ return __awaiter(this, arguments, void 0, function* (taskRunExternalId, message, options = {}) {
36
+ var _a;
37
+ if (message.length > exports.MAX_LOG_LINE_CHARS) {
38
+ throw new hatchet_error_1.default(`log line is ${message.length} characters, over the ${exports.MAX_LOG_LINE_CHARS}-character limit`);
39
+ }
40
+ return this.rpc.putLog({
41
+ taskRunExternalId,
42
+ createdAt: new Date(),
43
+ message,
44
+ level: (_a = options.level) !== null && _a !== void 0 ? _a : rpc_1.LogLevel.INFO,
45
+ taskRetryCount: options.retryCount,
46
+ metadata: options.metadata ? JSON.stringify(options.metadata) : undefined,
47
+ });
48
+ });
49
+ }
50
+ }
51
+ exports.LogsClient = LogsClient;
@@ -0,0 +1,19 @@
1
+ import { type RetrierConfig } from '../../util/retrier';
2
+ import type { Logger } from '../../util/logger/logger';
3
+ import type { WorkflowServiceClient } from '../../protoc/workflows/workflows';
4
+ import type { CreateRateLimitOpts } from '../types';
5
+ export interface RateLimitsClientDeps {
6
+ rpc: WorkflowServiceClient;
7
+ logger: Logger;
8
+ retrier?: RetrierConfig;
9
+ }
10
+ /** Tenant-wide rate limits that tasks declare against by key. */
11
+ export declare class RateLimitsClient {
12
+ private readonly deps;
13
+ constructor(deps: RateLimitsClientDeps);
14
+ /**
15
+ * Creates or updates a rate limit.
16
+ * @returns the rate limit's key
17
+ */
18
+ put(opts: CreateRateLimitOpts): Promise<string>;
19
+ }