@hatchet-dev/typescript-sdk 1.34.0-alpha.0 → 1.34.0-alpha.2

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 (60) hide show
  1. package/clients/admin/rpc.d.ts +1 -1
  2. package/clients/admin/rpc.js +3 -3
  3. package/clients/admin/trigger-request.d.ts +18 -0
  4. package/clients/admin/trigger-request.js +66 -0
  5. package/clients/event/event-client.d.ts +4 -17
  6. package/clients/event/event-client.js +5 -43
  7. package/clients/event/rpc.d.ts +22 -3
  8. package/clients/event/rpc.js +66 -3
  9. package/clients/hatchet-client/hatchet-logger.js +2 -1
  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/scripts/check-edge-entry.mjs +72 -14
  47. package/scripts/check-edge-entry.test.ts +57 -0
  48. package/util/batch.js +2 -1
  49. package/util/config-loader/token.d.ts +5 -0
  50. package/util/config-loader/token.js +8 -0
  51. package/util/errors/bulk-trigger-partial-error.d.ts +17 -0
  52. package/util/errors/bulk-trigger-partial-error.js +55 -0
  53. package/v1/client/admin.d.ts +4 -54
  54. package/v1/client/admin.js +9 -72
  55. package/v1/client/client.d.ts +6 -0
  56. package/v1/client/client.js +7 -0
  57. package/v1/client/features/runs.d.ts +9 -27
  58. package/v1/client/features/runs.js +10 -55
  59. package/version.d.ts +1 -1
  60. package/version.js +1 -1
@@ -1,20 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  /* eslint-disable no-console */
3
- // Verifies the edge entry point (`@hatchet-dev/typescript-sdk/edge`) imports nothing
4
- // from Node. Bundles dist/edge/index.js for a workerd-like browser target and fails on
5
- // any `node:` specifier or Node builtin reached from it, transitively.
3
+ // Verifies that an entry point imports nothing from Node. Bundles the entry for a
4
+ // workerd-like browser target and fails on any `node:` specifier or Node builtin reached
5
+ // from it, transitively, on any package named with `--forbid`, and on any SDK module in the
6
+ // bundle that touches a Node-only global (which a bundler cannot see, since nothing imports
7
+ // them): `process`, `Buffer`, `setImmediate`, a timer's `.unref()`, `AbortSignal.timeout`
8
+ // and `AbortSignal.any`. A use the module feature-detects (`typeof setImmediate`,
9
+ // `timer.unref?.()`, `globalThis.Buffer`) is allowed.
6
10
  //
7
- // Run after `pnpm run tsc:build` (the `check:edge` script does both). An alternative
8
- // entry can be given as the first argument to inspect another module, for example
9
- // `node scripts/check-edge-entry.mjs dist/index.js` to see what the root reaches.
11
+ // Run after `pnpm run tsc:build`:
12
+ // node scripts/check-edge-entry.mjs # dist/edge/index.js
13
+ // node scripts/check-edge-entry.mjs dist/core/index.js --forbid axios,nice-grpc
14
+ // The `check:edge` and `check:core` scripts build first and then run the two entries.
10
15
  import { builtinModules } from 'node:module';
11
- import { existsSync } from 'node:fs';
16
+ import { existsSync, readFileSync } from 'node:fs';
12
17
  import { resolve, dirname } from 'node:path';
13
18
  import { fileURLToPath } from 'node:url';
14
- import { build } from 'esbuild';
19
+ import { build, transformSync } from 'esbuild';
15
20
 
16
21
  const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
17
- const entry = resolve(root, process.argv[2] ?? 'dist/edge/index.js');
22
+
23
+ const args = process.argv.slice(2);
24
+ const forbidden = new Set();
25
+ let entryArg = 'dist/edge/index.js';
26
+ for (let i = 0; i < args.length; i += 1) {
27
+ if (args[i] === '--forbid') {
28
+ for (const pkg of (args[i + 1] ?? '').split(',')) if (pkg) forbidden.add(pkg);
29
+ i += 1;
30
+ } else {
31
+ entryArg = args[i];
32
+ }
33
+ }
34
+
35
+ const entry = resolve(root, entryArg);
36
+ const label = entryArg.replace(/^dist\//, '').replace(/\/index\.js$/, '');
18
37
 
19
38
  if (!existsSync(entry)) {
20
39
  console.error(`entry not built: ${entry} is missing. Run \`pnpm run tsc:build\` first.`);
@@ -63,22 +82,61 @@ const result = await build({
63
82
  });
64
83
 
65
84
  const bundled = Object.keys(result.metafile.inputs);
66
- const sdkInputs = bundled.filter((f) => f.startsWith('dist/'));
85
+ const sdkInputs = bundled.filter((f) => !f.includes('node_modules/'));
67
86
  const packageInputs = [...new Set(bundled.filter((f) => f.includes('node_modules/')).map((f) => {
68
87
  const m = f.match(/node_modules\/(?:\.pnpm\/[^/]+\/node_modules\/)?((?:@[^/]+\/)?[^/]+)/);
69
88
  return m ? m[1] : f;
70
89
  }))].sort();
71
90
 
72
- console.log(`edge entry: bundled ${sdkInputs.length} SDK module(s) and ${packageInputs.length} package(s)`);
91
+ console.log(`${label} entry: bundled ${sdkInputs.length} SDK module(s) and ${packageInputs.length} package(s)`);
73
92
  if (packageInputs.length) console.log(`packages: ${packageInputs.join(', ')}`);
74
93
 
94
+ let failed = false;
95
+
75
96
  if (violations.size > 0) {
76
- console.error('\nedge entry reaches Node builtins:');
97
+ failed = true;
98
+ console.error(`\n${label} entry reaches Node builtins:`);
77
99
  for (const [specifier, importers] of [...violations.entries()].sort()) {
78
100
  console.error(` ${specifier}`);
79
101
  for (const importer of [...importers].sort()) console.error(` from ${importer}`);
80
102
  }
81
- process.exit(1);
82
103
  }
83
104
 
84
- console.log('edge entry is free of Node builtins');
105
+ const forbiddenHits = packageInputs.filter((pkg) => forbidden.has(pkg));
106
+ if (forbiddenHits.length > 0) {
107
+ failed = true;
108
+ console.error(`\n${label} entry bundles forbidden package(s): ${forbiddenHits.join(', ')}`);
109
+ }
110
+
111
+ // These are globals in Node that workerd and browsers lack, or that some runtimes the entry
112
+ // targets lack (`AbortSignal.any`, `AbortSignal.timeout`); a module that reads them fails at
113
+ // runtime without ever importing anything. A module that feature-detects one may use it: a
114
+ // `globalThis.Buffer` access (which the generated bindings guard) and an optional
115
+ // `.unref?.()` call are their own guards, and a `typeof` check of the global anywhere in the
116
+ // module allows its use in that module.
117
+ const nodeGlobals = [
118
+ { what: 'the process or Buffer globals', pattern: /(?<!globalThis\.)\b(?:process|Buffer)\s*\./ },
119
+ { what: 'setImmediate', pattern: /(?<![.\w$])setImmediate\s*\(/, guard: /typeof\s+setImmediate\b/ },
120
+ { what: "a timer's .unref()", pattern: /(?<!\?)\.unref\s*\(/ },
121
+ {
122
+ what: 'AbortSignal.timeout or AbortSignal.any',
123
+ pattern: /\bAbortSignal\s*\.\s*(?:timeout|any)\b/,
124
+ guard: /typeof\s+AbortSignal\s*\.\s*(?:timeout|any)\b/,
125
+ },
126
+ ];
127
+ // Comments describe these globals without touching them, so they are stripped first. esbuild
128
+ // drops them while leaving string literals alone, which a regex over `//` cannot promise.
129
+ const stripComments = (source) => transformSync(source, { loader: 'js', legalComments: 'none' }).code;
130
+ const sources = new Map(sdkInputs.map((f) => [f, stripComments(readFileSync(resolve(root, f), 'utf8'))]));
131
+ for (const { what, pattern, guard } of nodeGlobals) {
132
+ const hits = sdkInputs.filter((f) => pattern.test(sources.get(f)) && !(guard && guard.test(sources.get(f))));
133
+ if (hits.length > 0) {
134
+ failed = true;
135
+ console.error(`\n${label} entry bundles SDK module(s) that use ${what}:`);
136
+ for (const f of hits) console.error(` ${f}`);
137
+ }
138
+ }
139
+
140
+ if (failed) process.exit(1);
141
+
142
+ console.log(`${label} entry is free of Node builtins${forbidden.size ? ' and forbidden packages' : ''}`);
@@ -0,0 +1,57 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
5
+
6
+ const script = resolve(__dirname, 'check-edge-entry.mjs');
7
+
8
+ /** Runs the checker on a module holding `source`, as if it were an SDK module in the bundle. */
9
+ function check(source: string): { status: number | null; stderr: string } {
10
+ const dir = mkdtempSync(join(tmpdir(), 'check-edge-entry-'));
11
+ try {
12
+ const entry = join(dir, 'entry.js');
13
+ writeFileSync(entry, source);
14
+ const result = spawnSync(process.execPath, [script, entry], { encoding: 'utf8' });
15
+ return { status: result.status, stderr: result.stderr };
16
+ } finally {
17
+ rmSync(dir, { recursive: true, force: true });
18
+ }
19
+ }
20
+
21
+ describe('check-edge-entry', () => {
22
+ it('passes a module that uses none of the Node-only globals', () => {
23
+ const result = check(`
24
+ // setImmediate(() => {}) in a comment is fine, as is AbortSignal.any in prose.
25
+ /* process.exit() described here is fine too */
26
+ export const wait = (ms) => new Promise((r) => setTimeout(r, ms));
27
+ export const url = 'https://example.com//path';
28
+ `);
29
+ expect(result.stderr).toBe('');
30
+ expect(result.status).toBe(0);
31
+ });
32
+
33
+ it.each([
34
+ ['setImmediate', 'export const tick = () => setImmediate(() => {});', /setImmediate/],
35
+ ['process.nextTick', 'export const tick = () => process.nextTick(() => {});', /process or Buffer/],
36
+ ['Buffer', "export const bytes = Buffer.from('x');", /process or Buffer/],
37
+ ['Buffer after // inside a string', "export const p = 'a//b'; export const bytes = Buffer.from('x');", /process or Buffer/],
38
+ ['.unref(', 'export const timer = setTimeout(() => {}, 1); timer.unref();', /\.unref\(\)/],
39
+ ['AbortSignal.timeout', 'export const signal = AbortSignal.timeout(5);', /AbortSignal\.timeout or AbortSignal\.any/],
40
+ ['AbortSignal.any', 'export const signal = AbortSignal.any([]);', /AbortSignal\.timeout or AbortSignal\.any/],
41
+ ])('fails a module that uses %s', (_name, source, message) => {
42
+ const result = check(source);
43
+ expect(result.status).toBe(1);
44
+ expect(result.stderr).toMatch(message);
45
+ });
46
+
47
+ it.each([
48
+ ['typeof setImmediate', "export const tick = typeof setImmediate === 'function' ? (f) => setImmediate(f) : (f) => setTimeout(f, 0);"],
49
+ ['an optional .unref?.()', 'export const timer = setTimeout(() => {}, 1); timer.unref?.();'],
50
+ ['typeof AbortSignal.any', "export const has = typeof AbortSignal.any === 'function';"],
51
+ ['globalThis.Buffer', "export const bytes = globalThis.Buffer ? globalThis.Buffer.from('x') : new Uint8Array();"],
52
+ ])('allows a feature-detected %s', (_name, source) => {
53
+ const result = check(source);
54
+ expect(result.stderr).toBe('');
55
+ expect(result.status).toBe(0);
56
+ });
57
+ });
package/util/batch.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.batch = batch;
4
+ const utf8 = new TextEncoder();
4
5
  function batch(payloads, numElm, maxBytes) {
5
6
  const batches = [];
6
7
  let currentBatchPayloads = [];
@@ -8,7 +9,7 @@ function batch(payloads, numElm, maxBytes) {
8
9
  let currentBatchSize = 0;
9
10
  for (let i = 0; i < payloads.length; i += 1) {
10
11
  const request = payloads[i];
11
- const requestSize = Buffer.byteLength(JSON.stringify(request), 'utf8');
12
+ const requestSize = utf8.encode(JSON.stringify(request)).byteLength;
12
13
  // Check if adding this request would exceed either the payload limit or batch size
13
14
  if (currentBatchPayloads.length > 0 &&
14
15
  (currentBatchSize + requestSize > maxBytes || currentBatchPayloads.length >= numElm)) {
@@ -1,4 +1,9 @@
1
1
  export declare function getTenantIdFromJWT(token: string): string;
2
+ /**
3
+ * The engine address a token was issued with, or `undefined` when the claim is absent. The
4
+ * core client needs only this claim, so it must not fail on a token that lacks `server_url`.
5
+ */
6
+ export declare function getGrpcBroadcastAddressFromJWT(token: string): string | undefined;
2
7
  export declare function getAddressesFromJWT(token: string): {
3
8
  serverUrl: string;
4
9
  grpcBroadcastAddress: string;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getTenantIdFromJWT = getTenantIdFromJWT;
4
+ exports.getGrpcBroadcastAddressFromJWT = getGrpcBroadcastAddressFromJWT;
4
5
  exports.getAddressesFromJWT = getAddressesFromJWT;
5
6
  const parse_1 = require("../parse");
6
7
  function getTenantIdFromJWT(token) {
@@ -10,6 +11,13 @@ function getTenantIdFromJWT(token) {
10
11
  }
11
12
  return claims.sub;
12
13
  }
14
+ /**
15
+ * The engine address a token was issued with, or `undefined` when the claim is absent. The
16
+ * core client needs only this claim, so it must not fail on a token that lacks `server_url`.
17
+ */
18
+ function getGrpcBroadcastAddressFromJWT(token) {
19
+ return extractClaimsFromJWT(token).grpc_broadcast_address || undefined;
20
+ }
13
21
  function getAddressesFromJWT(token) {
14
22
  const claims = extractClaimsFromJWT(token);
15
23
  if (claims.server_url === undefined || claims.grpc_broadcast_address === undefined) {
@@ -0,0 +1,17 @@
1
+ import HatchetError from './hatchet-error';
2
+ /**
3
+ * A bulk trigger that failed after at least one of its batches was accepted. The runs the
4
+ * accepted batches created exist and are running; a caller that retries the whole request
5
+ * would create them again, so `refs` carries their references, in input order, for the
6
+ * caller to keep or cancel. `cause` is the error the failed batch raised, which is a
7
+ * `BulkTriggerIdempotencyCollisionError` when that batch collided on an idempotency key.
8
+ */
9
+ export declare class BulkTriggerPartialError<Ref = unknown> extends HatchetError {
10
+ /** The references of every run created before the failed batch, in input order. */
11
+ refs: Ref[];
12
+ /** The zero-based index of the batch that failed. */
13
+ failedBatchIndex: number;
14
+ /** The error the failed batch raised. */
15
+ cause: unknown;
16
+ constructor(refs: Ref[], failedBatchIndex: number, cause: unknown);
17
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.BulkTriggerPartialError = void 0;
37
+ const hatchet_error_1 = __importStar(require("./hatchet-error"));
38
+ /**
39
+ * A bulk trigger that failed after at least one of its batches was accepted. The runs the
40
+ * accepted batches created exist and are running; a caller that retries the whole request
41
+ * would create them again, so `refs` carries their references, in input order, for the
42
+ * caller to keep or cancel. `cause` is the error the failed batch raised, which is a
43
+ * `BulkTriggerIdempotencyCollisionError` when that batch collided on an idempotency key.
44
+ */
45
+ class BulkTriggerPartialError extends hatchet_error_1.default {
46
+ constructor(refs, failedBatchIndex, cause) {
47
+ super(`bulk trigger failed at batch ${failedBatchIndex} after ${refs.length} runs were created: ${(0, hatchet_error_1.getErrorMessage)(cause)}`, { cause });
48
+ this.name = 'BulkTriggerPartialError';
49
+ this.refs = refs;
50
+ this.failedBatchIndex = failedBatchIndex;
51
+ this.cause = cause;
52
+ Object.setPrototypeOf(this, new.target.prototype);
53
+ }
54
+ }
55
+ exports.BulkTriggerPartialError = BulkTriggerPartialError;
@@ -1,18 +1,13 @@
1
1
  import { ClientConfig } from '../../clients/hatchet-client/client-config';
2
2
  import WorkflowRunRef from '../../util/workflow-run-ref';
3
- import { Priority, RateLimitDuration, RunsClient, WorkerLabelComparator } from './..';
3
+ import { RateLimitDuration, RunsClient } from './..';
4
4
  import { RunListenerClient } from '../../clients/listeners/run-listener/child-listener-client';
5
5
  import { Api } from '../../clients/rest/generated/Api';
6
6
  import { WorkflowServiceClient } from '../../protoc/workflows';
7
7
  import { AdminServiceClient, CreateWorkflowVersionRequest } from '../../protoc/v1/workflows';
8
8
  import { type Transport } from '../../clients/transport';
9
+ import type { TriggerRun, TriggerRunOptions } from '../../core/types';
9
10
  import { Logger } from '../../util/logger';
10
- type DesiredWorkerLabelOpt = {
11
- value: string | number;
12
- required?: boolean;
13
- weight?: number;
14
- comparator?: WorkerLabelComparator;
15
- };
16
11
  export type WorkflowRun<T = object> = {
17
12
  workflowName: string;
18
13
  input: T;
@@ -58,27 +53,7 @@ export declare class AdminClient {
58
53
  * @important This method is instrumented by HatchetInstrumentor._patchRunWorkflow.
59
54
  * Keep the signature in sync with the instrumentor wrapper.
60
55
  */
61
- runWorkflow<Q = object, P = object>(workflowName: string, input: Q, options?: {
62
- parentId?: string | undefined;
63
- /**
64
- * (optional) the parent task run external id.
65
- *
66
- * This is the field understood by the workflows gRPC API (`parent_task_run_external_id`).
67
- */
68
- parentTaskRunExternalId?: string | undefined;
69
- /**
70
- * @deprecated Use `parentTaskRunExternalId` instead.
71
- * Kept for backward compatibility; will be mapped to `parentTaskRunExternalId`.
72
- */
73
- parentStepRunId?: string | undefined;
74
- childIndex?: number | undefined;
75
- childKey?: string | undefined;
76
- additionalMetadata?: Record<string, string> | undefined;
77
- desiredWorkerId?: string | undefined;
78
- priority?: Priority;
79
- desiredWorkerLabels?: Record<string, DesiredWorkerLabelOpt>;
80
- _standaloneTaskName?: string | undefined;
81
- }): Promise<WorkflowRunRef<P>>;
56
+ runWorkflow<Q = object, P = object>(workflowName: string, input: Q, options?: TriggerRunOptions): Promise<WorkflowRunRef<P>>;
82
57
  /**
83
58
  * Run multiple workflows runs with the given input and options. This will create new workflow runs and return their IDs.
84
59
  * Order is preserved in the response.
@@ -88,31 +63,6 @@ export declare class AdminClient {
88
63
  * @important This method is instrumented by HatchetInstrumentor._patchRunWorkflows.
89
64
  * Keep the signature in sync with the instrumentor wrapper.
90
65
  */
91
- runWorkflows<Q = object, P = object>(workflowRuns: Array<{
92
- workflowName: string;
93
- input: Q;
94
- options?: {
95
- parentId?: string | undefined;
96
- /**
97
- * (optional) the parent task run external id.
98
- *
99
- * This is the field understood by the workflows gRPC API (`parent_task_run_external_id`).
100
- */
101
- parentTaskRunExternalId?: string | undefined;
102
- /**
103
- * @deprecated Use `parentTaskRunExternalId` instead.
104
- * Kept for backward compatibility; will be mapped to `parentTaskRunExternalId`.
105
- */
106
- parentStepRunId?: string | undefined;
107
- childIndex?: number | undefined;
108
- childKey?: string | undefined;
109
- additionalMetadata?: Record<string, string> | undefined;
110
- desiredWorkerId?: string | undefined;
111
- priority?: Priority;
112
- desiredWorkerLabels?: Record<string, DesiredWorkerLabelOpt>;
113
- _standaloneTaskName?: string | undefined;
114
- };
115
- }>, batchSize?: number): Promise<WorkflowRunRef<P>[]>;
66
+ runWorkflows<Q = object, P = object>(workflowRuns: Array<TriggerRun<Q>>, batchSize?: number): Promise<WorkflowRunRef<P>[]>;
116
67
  putRateLimit(key: string, limit: number, duration?: RateLimitDuration): Promise<void>;
117
68
  }
118
- export {};
@@ -8,17 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __rest = (this && this.__rest) || function (s, e) {
12
- var t = {};
13
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
- t[p] = s[p];
15
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
- t[p[i]] = s[p[i]];
19
- }
20
- return t;
21
- };
22
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
23
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
24
13
  };
@@ -26,51 +15,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
15
  exports.AdminClient = void 0;
27
16
  const hatchet_error_1 = __importDefault(require("../../util/errors/hatchet-error"));
28
17
  const idempotency_collision_error_1 = require("../../util/errors/idempotency-collision-error");
29
- const bulk_trigger_idempotency_collision_error_1 = require("../../util/errors/bulk-trigger-idempotency-collision-error");
30
18
  const workflow_run_ref_1 = __importDefault(require("../../util/workflow-run-ref"));
31
- const connect_1 = require("@connectrpc/connect");
32
- const workflows_pb_1 = require("../../protoc-es/v1/workflows_pb");
33
19
  const nice_grpc_1 = require("nice-grpc");
34
20
  const grpc_helpers_1 = require("../../util/grpc-helpers");
35
21
  const config_loader_1 = require("../../util/config-loader");
36
22
  const child_listener_client_1 = require("../../clients/listeners/run-listener/child-listener-client");
37
23
  const workflows_1 = require("../../protoc/workflows");
38
24
  const rpc_1 = require("../../clients/admin/rpc");
25
+ const trigger_request_1 = require("../../clients/admin/trigger-request");
39
26
  const transport_1 = require("../../clients/transport");
40
27
  const retrier_1 = require("../../util/retrier");
41
28
  const batch_1 = require("../../util/batch");
42
- const apply_namespace_1 = require("../../util/apply-namespace");
43
- /**
44
- * The engine answers a trigger whose idempotency key is already taken with `ALREADY_EXISTS`
45
- * and attaches the collision details to the status. The Connect client decodes those details
46
- * from the trailer, so the error carries everything needed to build the SDK's collision errors.
47
- */
48
- function isAlreadyExists(e) {
49
- return e instanceof connect_1.ConnectError && e.code === connect_1.Code.AlreadyExists;
50
- }
51
- function extractExistingRunId(e) {
52
- var _a;
53
- const [detail] = e.findDetails(workflows_pb_1.IdempotencyCollisionErrorSchema);
54
- return (_a = detail === null || detail === void 0 ? void 0 : detail.existingRunExternalId) !== null && _a !== void 0 ? _a : '';
55
- }
56
- function extractBulkTriggerCollision(e) {
57
- const [detail] = e.findDetails(workflows_pb_1.BulkTriggerIdempotencyCollisionErrorSchema);
58
- if (!detail)
59
- return null;
60
- return new bulk_trigger_idempotency_collision_error_1.BulkTriggerIdempotencyCollisionError(detail.successfulWorkflowRunExternalIds, detail.collisions.map((c) => new idempotency_collision_error_1.IdempotencyCollisionError(c.existingRunExternalId)));
61
- }
62
- function convertDesiredWorkerLabels(labels) {
63
- return Object.fromEntries(Object.entries(labels).map(([key, label]) => [
64
- key,
65
- {
66
- strValue: typeof label.value === 'string' ? label.value : undefined,
67
- intValue: typeof label.value === 'number' ? label.value : undefined,
68
- required: label.required,
69
- weight: label.weight,
70
- comparator: label.comparator,
71
- },
72
- ]));
73
- }
74
29
  class AdminClient {
75
30
  constructor(config, api, runs, transport = (0, transport_1.createNodeTransport)(config)) {
76
31
  this.config = config;
@@ -111,24 +66,16 @@ class AdminClient {
111
66
  runWorkflow(workflowName, input, options) {
112
67
  return __awaiter(this, void 0, void 0, function* () {
113
68
  try {
114
- const computedName = (0, apply_namespace_1.applyNamespace)(workflowName, this.config.namespace).toLowerCase();
115
- const inputStr = JSON.stringify(input);
116
- const opts = options !== null && options !== void 0 ? options : {};
117
- const { additionalMetadata, parentStepRunId, parentTaskRunExternalId, desiredWorkerLabels } = opts, rest = __rest(opts, ["additionalMetadata", "parentStepRunId", "parentTaskRunExternalId", "desiredWorkerLabels"]);
118
- const request = Object.assign(Object.assign({ name: computedName, input: inputStr }, rest), {
119
- // API expects `parentTaskRunExternalId`; accept old names as aliases.
120
- parentTaskRunExternalId: parentTaskRunExternalId !== null && parentTaskRunExternalId !== void 0 ? parentTaskRunExternalId : parentStepRunId, additionalMetadata: additionalMetadata ? JSON.stringify(additionalMetadata) : undefined, priority: opts.priority, desiredWorkerLabels: desiredWorkerLabels
121
- ? convertDesiredWorkerLabels(desiredWorkerLabels)
122
- : {} });
123
- const resp = yield (0, retrier_1.retrier)(() => __awaiter(this, void 0, void 0, function* () { return this.workflowsGrpc.triggerWorkflow(request); }), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !isAlreadyExists(e) }));
69
+ const request = (0, trigger_request_1.buildTriggerWorkflowRequest)(workflowName, input, options, this.config.namespace);
70
+ const resp = yield (0, retrier_1.retrier)(() => __awaiter(this, void 0, void 0, function* () { return this.workflowsGrpc.triggerWorkflow(request); }), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !(0, trigger_request_1.isAlreadyExists)(e) }));
124
71
  const id = resp.workflowRunId;
125
72
  const ref = new workflow_run_ref_1.default(id, this.listenerClient, this.runs, options === null || options === void 0 ? void 0 : options.parentId, options === null || options === void 0 ? void 0 : options._standaloneTaskName);
126
73
  yield ref.getWorkflowRunId();
127
74
  return ref;
128
75
  }
129
76
  catch (e) {
130
- if (isAlreadyExists(e)) {
131
- throw new idempotency_collision_error_1.IdempotencyCollisionError(extractExistingRunId(e));
77
+ if ((0, trigger_request_1.isAlreadyExists)(e)) {
78
+ throw new idempotency_collision_error_1.IdempotencyCollisionError((0, trigger_request_1.extractExistingRunId)(e));
132
79
  }
133
80
  throw new hatchet_error_1.default(e instanceof Error ? e.message : String(e));
134
81
  }
@@ -147,17 +94,7 @@ class AdminClient {
147
94
  return __awaiter(this, arguments, void 0, function* (workflowRuns, batchSize = 500) {
148
95
  var _a;
149
96
  // Prepare workflows to be triggered in bulk
150
- const workflowRequests = workflowRuns.map(({ workflowName, input, options }) => {
151
- const computedName = (0, apply_namespace_1.applyNamespace)(workflowName, this.config.namespace).toLowerCase();
152
- const inputStr = JSON.stringify(input);
153
- const opts = options !== null && options !== void 0 ? options : {};
154
- const { additionalMetadata, parentStepRunId, parentTaskRunExternalId, desiredWorkerLabels } = opts, rest = __rest(opts, ["additionalMetadata", "parentStepRunId", "parentTaskRunExternalId", "desiredWorkerLabels"]);
155
- return Object.assign(Object.assign({ name: computedName, input: inputStr }, rest), {
156
- // API expects `parentTaskRunExternalId`; accept old names as aliases.
157
- parentTaskRunExternalId: parentTaskRunExternalId !== null && parentTaskRunExternalId !== void 0 ? parentTaskRunExternalId : parentStepRunId, additionalMetadata: additionalMetadata ? JSON.stringify(additionalMetadata) : undefined, desiredWorkerLabels: desiredWorkerLabels
158
- ? convertDesiredWorkerLabels(desiredWorkerLabels)
159
- : {} });
160
- });
97
+ const workflowRequests = workflowRuns.map(({ workflowName, input, options }) => (0, trigger_request_1.buildTriggerWorkflowRequest)(workflowName, input, options, this.config.namespace));
161
98
  const batches = (0, batch_1.batch)(workflowRequests, batchSize, (_a = this.config.grpc_max_send_message_length) !== null && _a !== void 0 ? _a : 4 * 1024 * 1024);
162
99
  this.logger.debug(`batching ${batches.length} batches`);
163
100
  try {
@@ -168,7 +105,7 @@ class AdminClient {
168
105
  workflows: payloads,
169
106
  });
170
107
  // Call the bulk trigger workflow method for this batch
171
- const bulkTriggerWorkflowResponse = yield (0, retrier_1.retrier)(() => __awaiter(this, void 0, void 0, function* () { return this.workflowsGrpc.bulkTriggerWorkflow(request); }), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !isAlreadyExists(e) }));
108
+ const bulkTriggerWorkflowResponse = yield (0, retrier_1.retrier)(() => __awaiter(this, void 0, void 0, function* () { return this.workflowsGrpc.bulkTriggerWorkflow(request); }), this.logger, Object.assign(Object.assign({}, this.config.retrier), { shouldRetry: (e) => !(0, trigger_request_1.isAlreadyExists)(e) }));
172
109
  this.logger.debug(`batch ${batchIndex + 1} of ${batches.length}`);
173
110
  // Map the results back to their original indices
174
111
  const batchResults = bulkTriggerWorkflowResponse.workflowRunIds.map((resp, index) => {
@@ -181,8 +118,8 @@ class AdminClient {
181
118
  return results;
182
119
  }
183
120
  catch (e) {
184
- if (isAlreadyExists(e)) {
185
- const collision = extractBulkTriggerCollision(e);
121
+ if ((0, trigger_request_1.isAlreadyExists)(e)) {
122
+ const collision = (0, trigger_request_1.extractBulkTriggerCollision)(e);
186
123
  if (collision)
187
124
  throw collision;
188
125
  }
@@ -13,6 +13,7 @@ import { DispatcherClient } from '../../clients/dispatcher/dispatcher-client';
13
13
  import { Logger } from '../../util/logger';
14
14
  import { RunListenerClient } from '../../clients/listeners/run-listener/child-listener-client';
15
15
  import { DurableListenerClient } from '../../clients/listeners/durable-listener/durable-listener-client';
16
+ import { type Transport } from '../../clients/transport';
16
17
  import { CreateTaskWorkflowOpts, CreateWorkflowOpts, RunOpts, BaseWorkflowDeclaration, WorkflowDeclaration, TaskWorkflowDeclaration, CreateDurableTaskWorkflowOpts, CreateBatchTaskWorkflowOpts } from '../declaration';
17
18
  import { BatchTaskFn } from '../task';
18
19
  import type { LegacyWorkflow } from '../../legacy/legacy-transformer';
@@ -241,6 +242,11 @@ export declare class HatchetClient<GlobalInput extends Record<string, any> = {},
241
242
  * @internal
242
243
  */
243
244
  get listener(): RunListenerClient;
245
+ /**
246
+ * The Connect transport the client's unary RPCs go through
247
+ * @internal
248
+ */
249
+ get transport(): Transport;
244
250
  /**
245
251
  * @deprecated use client.events instead
246
252
  * @hidden
@@ -321,6 +321,13 @@ class HatchetClient {
321
321
  get listener() {
322
322
  return this._listener;
323
323
  }
324
+ /**
325
+ * The Connect transport the client's unary RPCs go through
326
+ * @internal
327
+ */
328
+ get transport() {
329
+ return this._transport;
330
+ }
324
331
  /**
325
332
  * @deprecated use client.events instead
326
333
  * @hidden
@@ -1,32 +1,13 @@
1
1
  import WorkflowRunRef from '../../../util/workflow-run-ref';
2
- import { V1TaskStatus } from '../../../clients/rest/generated/data-contracts';
3
2
  import { RunListenerClient } from '../../../clients/listeners/run-listener/child-listener-client';
4
3
  import { WorkflowsClient } from './workflows';
5
4
  import { HatchetClient } from '../client';
6
- import { runStatusToJSON } from '../../../protoc/v1/workflows';
7
- export type RunDetail = {
8
- status: V1TaskStatus;
9
- done: boolean;
10
- input: unknown;
11
- additionalMetadata: unknown;
12
- isEvicted: boolean;
13
- taskRuns: Record<string, TaskRunDetail>;
14
- };
15
- export type TaskRunDetail = {
16
- externalId: string;
17
- readableId: string;
18
- status: V1TaskStatus;
19
- output: unknown;
20
- error?: string;
21
- isEvicted: boolean;
22
- };
5
+ import { runStatusToJSON } from '../../../core/run-detail';
6
+ import type { RunDetail, RunFilterBase, TaskRunDetail } from '../../../core/types';
7
+ export type { RunDetail, TaskRunDetail };
23
8
  export { runStatusToJSON };
24
- export type RunFilter = {
25
- since?: Date;
26
- until?: Date;
27
- statuses?: V1TaskStatus[];
9
+ export type RunFilter = RunFilterBase & {
28
10
  workflowNames?: string[];
29
- additionalMetadata?: Record<string, string>;
30
11
  };
31
12
  export type CancelRunOpts = {
32
13
  ids?: string[];
@@ -90,10 +71,11 @@ export declare class RunsClient {
90
71
  tenantId: string;
91
72
  workflows: WorkflowsClient;
92
73
  listener: RunListenerClient;
93
- private _config;
94
- private _adminGrpc;
74
+ private _transport;
75
+ private _adminRpc;
95
76
  constructor(client: HatchetClient);
96
- private get adminGrpc();
77
+ /** The v1 admin RPCs on the client's Connect transport, the same path the core client uses. */
78
+ private get adminRpc();
97
79
  /**
98
80
  * Gets a task or workflow run by its ID.
99
81
  * @param run - The ID of the run to get.
@@ -105,7 +87,7 @@ export declare class RunsClient {
105
87
  * @param run - The ID of the run to get the status of.
106
88
  * @returns A promise that resolves to the status of the run.
107
89
  */
108
- get_status<T = any>(run: string | WorkflowRunRef<T>): Promise<V1TaskStatus>;
90
+ get_status<T = any>(run: string | WorkflowRunRef<T>): Promise<import("../../../clients/rest/generated/data-contracts").V1TaskStatus>;
109
91
  /**
110
92
  * Gets run details
111
93
  * @param run - The workflow run ID (string) or a WorkflowRunRef.