@temporal-contract/client 0.0.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.
@@ -0,0 +1,17 @@
1
+
2
+ > @temporal-contract/client@0.0.1 build /home/runner/work/temporal-contract/temporal-contract/packages/client
3
+ > tsdown src/index.ts --format cjs,esm --dts --clean
4
+
5
+ ℹ tsdown v0.17.2 powered by rolldown v1.0.0-beta.53
6
+ ℹ entry: src/index.ts
7
+ ℹ tsconfig: tsconfig.json
8
+ ℹ Build start
9
+ ℹ [CJS] dist/index.cjs 8.38 kB │ gzip: 1.77 kB
10
+ ℹ [CJS] 1 files, total: 8.38 kB
11
+ ℹ [ESM] dist/index.mjs 8.12 kB │ gzip: 1.74 kB
12
+ ℹ [ESM] dist/index.d.mts 6.70 kB │ gzip: 1.62 kB
13
+ ℹ [ESM] 2 files, total: 14.81 kB
14
+ ✔ Build complete in 3603ms
15
+ ℹ [CJS] dist/index.d.cts 6.70 kB │ gzip: 1.62 kB
16
+ ℹ [CJS] 1 files, total: 6.70 kB
17
+ ✔ Build complete in 3605ms
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Benoit TRAVERS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # @temporal-contract/client
2
+
3
+ > Type-safe client for consuming Temporal workflows
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @temporal-contract/client @temporal-contract/contract @temporalio/client zod
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Connection } from '@temporalio/client';
15
+ import { TypedClient } from '@temporal-contract/client';
16
+ import { myContract } from './contract';
17
+
18
+ // Connect to Temporal
19
+ const connection = await Connection.connect({ address: 'localhost:7233' });
20
+
21
+ // Create typed client
22
+ const client = TypedClient.create(myContract, {
23
+ connection,
24
+ namespace: 'default',
25
+ });
26
+
27
+ // Execute workflow (fully typed!)
28
+ const result = await client.executeWorkflow('processOrder', {
29
+ workflowId: 'order-123',
30
+ args: { orderId: 'ORD-123', customerId: 'CUST-456' },
31
+ });
32
+
33
+ console.log(result.status); // 'success' | 'failed' — typed!
34
+ ```
35
+
36
+ ## API
37
+
38
+ ### `TypedClient.create(contract, options)`
39
+
40
+ Creates a type-safe Temporal client.
41
+
42
+ **Returns:** Type-safe client with these methods:
43
+
44
+ #### `executeWorkflow(name, options)`
45
+
46
+ Execute and wait for result:
47
+
48
+ ```typescript
49
+ const result = await client.executeWorkflow('processOrder', {
50
+ workflowId: 'order-123',
51
+ args: { orderId: 'ORD-123', customerId: 'CUST-456' },
52
+ });
53
+ ```
54
+
55
+ #### `startWorkflow(name, options)` / `getHandle(name, workflowId)`
56
+
57
+ Get a typed workflow handle for signals/queries/updates:
58
+
59
+ ```typescript
60
+ const handle = await client.startWorkflow('processOrder', {
61
+ workflowId: 'order-456',
62
+ args: { orderId: 'ORD-456' },
63
+ });
64
+
65
+ // Type-safe queries, signals, updates
66
+ await handle.queries.getStatus();
67
+ await handle.signals.cancel({ reason: 'Customer request' });
68
+ await handle.updates.changeAmount({ newAmount: 150 });
69
+ ```
70
+
71
+ ## Error Handling
72
+
73
+ Custom error classes with contextual information:
74
+
75
+ ```typescript
76
+ import { WorkflowValidationError, WorkflowNotFoundError } from '@temporal-contract/client';
77
+
78
+ try {
79
+ await client.executeWorkflow('processOrder', { args: invalidData });
80
+ } catch (error) {
81
+ if (error instanceof WorkflowValidationError) {
82
+ console.error('Validation:', error.zodError.errors);
83
+ } else if (error instanceof WorkflowNotFoundError) {
84
+ console.error('Available:', error.availableWorkflows);
85
+ }
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Learn More
92
+
93
+ - [Main README](../../README.md) — Quick start guide
94
+ - [Worker Implementation](../../docs/CONTRACT_HANDLER.md) — Implementing workers
95
+
96
+ ## License
97
+
98
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,269 @@
1
+ let __temporalio_client = require("@temporalio/client");
2
+ let zod = require("zod");
3
+
4
+ //#region src/errors.ts
5
+ /**
6
+ * Base error class for typed client errors
7
+ */
8
+ var TypedClientError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "TypedClientError";
12
+ if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ };
15
+ /**
16
+ * Error thrown when a workflow is not found in the contract
17
+ */
18
+ var WorkflowNotFoundError = class extends TypedClientError {
19
+ constructor(workflowName) {
20
+ super(`Workflow "${workflowName}" not found in contract`);
21
+ this.workflowName = workflowName;
22
+ this.name = "WorkflowNotFoundError";
23
+ }
24
+ };
25
+ /**
26
+ * Error thrown when workflow input or output validation fails
27
+ */
28
+ var WorkflowValidationError = class extends TypedClientError {
29
+ constructor(workflowName, phase, zodError) {
30
+ super(`Validation failed for workflow "${workflowName}" ${phase}: ${zodError.message}`);
31
+ this.workflowName = workflowName;
32
+ this.phase = phase;
33
+ this.zodError = zodError;
34
+ this.name = "WorkflowValidationError";
35
+ }
36
+ };
37
+ /**
38
+ * Error thrown when query input or output validation fails
39
+ */
40
+ var QueryValidationError = class extends TypedClientError {
41
+ constructor(queryName, phase, zodError) {
42
+ super(`Validation failed for query "${queryName}" ${phase}: ${zodError.message}`);
43
+ this.queryName = queryName;
44
+ this.phase = phase;
45
+ this.zodError = zodError;
46
+ this.name = "QueryValidationError";
47
+ }
48
+ };
49
+ /**
50
+ * Error thrown when signal input validation fails
51
+ */
52
+ var SignalValidationError = class extends TypedClientError {
53
+ constructor(signalName, zodError) {
54
+ super(`Validation failed for signal "${signalName}" input: ${zodError.message}`);
55
+ this.signalName = signalName;
56
+ this.zodError = zodError;
57
+ this.name = "SignalValidationError";
58
+ }
59
+ };
60
+ /**
61
+ * Error thrown when update input or output validation fails
62
+ */
63
+ var UpdateValidationError = class extends TypedClientError {
64
+ constructor(updateName, phase, zodError) {
65
+ super(`Validation failed for update "${updateName}" ${phase}: ${zodError.message}`);
66
+ this.updateName = updateName;
67
+ this.phase = phase;
68
+ this.zodError = zodError;
69
+ this.name = "UpdateValidationError";
70
+ }
71
+ };
72
+
73
+ //#endregion
74
+ //#region src/client.ts
75
+ /**
76
+ * Typed Temporal client based on a contract
77
+ *
78
+ * Provides type-safe methods to start and execute workflows
79
+ * defined in the contract.
80
+ */
81
+ var TypedClient = class TypedClient {
82
+ constructor(contract, client) {
83
+ this.contract = contract;
84
+ this.client = client;
85
+ }
86
+ /**
87
+ * Create a typed Temporal client from a contract
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const connection = await Connection.connect();
92
+ * const client = TypedClient.create(myContract, {
93
+ * connection,
94
+ * namespace: 'default',
95
+ * });
96
+ *
97
+ * const result = await client.executeWorkflow('processOrder', {
98
+ * workflowId: 'order-123',
99
+ * args: [...],
100
+ * });
101
+ * ```
102
+ */
103
+ static create(contract, options) {
104
+ return new TypedClient(contract, new __temporalio_client.Client(options));
105
+ }
106
+ /**
107
+ * Start a workflow and return a typed handle
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const handle = await client.startWorkflow('processOrder', {
112
+ * workflowId: 'order-123',
113
+ * args: ['ORD-123', 'CUST-456', [{ productId: 'PROD-1', quantity: 2 }]],
114
+ * workflowExecutionTimeout: '1 day',
115
+ * retry: { maximumAttempts: 3 },
116
+ * });
117
+ *
118
+ * const result = await handle.result();
119
+ * ```
120
+ */
121
+ async startWorkflow(workflowName, { args, ...temporalOptions }) {
122
+ const definition = this.contract.workflows[workflowName];
123
+ if (!definition) throw new WorkflowNotFoundError(String(workflowName));
124
+ let validatedInput;
125
+ try {
126
+ validatedInput = definition.input.parse(args);
127
+ } catch (error) {
128
+ if (error instanceof zod.ZodError) throw new WorkflowValidationError(String(workflowName), "input", error);
129
+ throw error;
130
+ }
131
+ const handle = await this.client.workflow.start(workflowName, {
132
+ ...temporalOptions,
133
+ taskQueue: this.contract.taskQueue,
134
+ args: [validatedInput]
135
+ });
136
+ return this.createTypedHandle(handle, definition);
137
+ }
138
+ /**
139
+ * Execute a workflow (start and wait for result)
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const result = await client.executeWorkflow('processOrder', {
144
+ * workflowId: 'order-123',
145
+ * args: ['ORD-123', 'CUST-456', [{ productId: 'PROD-1', quantity: 2 }]],
146
+ * workflowExecutionTimeout: '1 day',
147
+ * retry: { maximumAttempts: 3 },
148
+ * });
149
+ *
150
+ * console.log(result.status); // fully typed!
151
+ * ```
152
+ */
153
+ async executeWorkflow(workflowName, { args, ...temporalOptions }) {
154
+ const definition = this.contract.workflows[workflowName];
155
+ if (!definition) throw new WorkflowNotFoundError(String(workflowName));
156
+ let validatedInput;
157
+ try {
158
+ validatedInput = definition.input.parse(args);
159
+ } catch (error) {
160
+ if (error instanceof zod.ZodError) throw new WorkflowValidationError(String(workflowName), "input", error);
161
+ throw error;
162
+ }
163
+ const result = await this.client.workflow.execute(workflowName, {
164
+ ...temporalOptions,
165
+ taskQueue: this.contract.taskQueue,
166
+ args: [validatedInput]
167
+ });
168
+ try {
169
+ return definition.output.parse(result);
170
+ } catch (error) {
171
+ if (error instanceof zod.ZodError) throw new WorkflowValidationError(String(workflowName), "output", error);
172
+ throw error;
173
+ }
174
+ }
175
+ /**
176
+ * Get a handle to an existing workflow
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const handle = await client.getHandle('processOrder', 'order-123');
181
+ * const result = await handle.result();
182
+ * ```
183
+ */
184
+ async getHandle(workflowName, workflowId) {
185
+ const definition = this.contract.workflows[workflowName];
186
+ if (!definition) throw new WorkflowNotFoundError(String(workflowName));
187
+ const handle = this.client.workflow.getHandle(workflowId);
188
+ return this.createTypedHandle(handle, definition);
189
+ }
190
+ createTypedHandle(handle, definition) {
191
+ const queries = {};
192
+ for (const [queryName, queryDef] of Object.entries(definition.queries ?? {})) queries[queryName] = async (args) => {
193
+ let validatedInput;
194
+ try {
195
+ validatedInput = queryDef.input.parse(args);
196
+ } catch (error) {
197
+ if (error instanceof zod.ZodError) throw new QueryValidationError(queryName, "input", error);
198
+ throw error;
199
+ }
200
+ const result = await handle.query(queryName, validatedInput);
201
+ try {
202
+ return queryDef.output.parse(result);
203
+ } catch (error) {
204
+ if (error instanceof zod.ZodError) throw new QueryValidationError(queryName, "output", error);
205
+ throw error;
206
+ }
207
+ };
208
+ const signals = {};
209
+ for (const [signalName, signalDef] of Object.entries(definition.signals ?? {})) signals[signalName] = async (args) => {
210
+ let validatedInput;
211
+ try {
212
+ validatedInput = signalDef.input.parse(args);
213
+ } catch (error) {
214
+ if (error instanceof zod.ZodError) throw new SignalValidationError(signalName, error);
215
+ throw error;
216
+ }
217
+ await handle.signal(signalName, validatedInput);
218
+ };
219
+ const updates = {};
220
+ for (const [updateName, updateDef] of Object.entries(definition.updates ?? {})) updates[updateName] = async (args) => {
221
+ let validatedInput;
222
+ try {
223
+ validatedInput = updateDef.input.parse(args);
224
+ } catch (error) {
225
+ if (error instanceof zod.ZodError) throw new UpdateValidationError(updateName, "input", error);
226
+ throw error;
227
+ }
228
+ const result = await handle.executeUpdate(updateName, { args: [validatedInput] });
229
+ try {
230
+ return updateDef.output.parse(result);
231
+ } catch (error) {
232
+ if (error instanceof zod.ZodError) throw new UpdateValidationError(updateName, "output", error);
233
+ throw error;
234
+ }
235
+ };
236
+ return {
237
+ workflowId: handle.workflowId,
238
+ queries,
239
+ signals,
240
+ updates,
241
+ result: async () => {
242
+ const result = await handle.result();
243
+ try {
244
+ return definition.output.parse(result);
245
+ } catch (error) {
246
+ if (error instanceof zod.ZodError) throw new WorkflowValidationError(handle.workflowId, "output", error);
247
+ throw error;
248
+ }
249
+ },
250
+ terminate: async (reason) => {
251
+ await handle.terminate(reason);
252
+ },
253
+ cancel: async () => {
254
+ await handle.cancel();
255
+ },
256
+ describe: () => handle.describe(),
257
+ fetchHistory: () => handle.fetchHistory()
258
+ };
259
+ }
260
+ };
261
+
262
+ //#endregion
263
+ exports.QueryValidationError = QueryValidationError;
264
+ exports.SignalValidationError = SignalValidationError;
265
+ exports.TypedClient = TypedClient;
266
+ exports.TypedClientError = TypedClientError;
267
+ exports.UpdateValidationError = UpdateValidationError;
268
+ exports.WorkflowNotFoundError = WorkflowNotFoundError;
269
+ exports.WorkflowValidationError = WorkflowValidationError;
@@ -0,0 +1,190 @@
1
+ import { ClientOptions, WorkflowHandle, WorkflowOptions, WorkflowStartOptions } from "@temporalio/client";
2
+ import { ClientInferInput, ClientInferOutput, ClientInferWorkflowQueries, ClientInferWorkflowSignals, ClientInferWorkflowUpdates, ContractDefinition, WorkflowDefinition } from "@temporal-contract/contract";
3
+ import { z } from "zod";
4
+
5
+ //#region src/client.d.ts
6
+
7
+ /**
8
+ * Extended options for starting workflows with Temporal-specific features
9
+ * Combines required workflowId with optional Temporal workflow options
10
+ */
11
+ type TypedWorkflowStartOptions = Pick<WorkflowStartOptions, "workflowId" | "workflowIdReusePolicy" | "workflowExecutionTimeout" | "workflowRunTimeout" | "workflowTaskTimeout" | "retry" | "memo" | "searchAttributes" | "cronSchedule"> & Pick<WorkflowOptions, "workflowId">;
12
+ /**
13
+ * Typed workflow handle with validated results, queries, signals and updates
14
+ */
15
+ interface TypedWorkflowHandle<TWorkflow extends WorkflowDefinition> {
16
+ workflowId: string;
17
+ /**
18
+ * Type-safe queries based on workflow definition
19
+ */
20
+ queries: ClientInferWorkflowQueries<TWorkflow>;
21
+ /**
22
+ * Type-safe signals based on workflow definition
23
+ */
24
+ signals: ClientInferWorkflowSignals<TWorkflow>;
25
+ /**
26
+ * Type-safe updates based on workflow definition
27
+ */
28
+ updates: ClientInferWorkflowUpdates<TWorkflow>;
29
+ result: () => Promise<ClientInferOutput<TWorkflow>>;
30
+ terminate: (reason?: string) => Promise<void>;
31
+ cancel: () => Promise<void>;
32
+ /**
33
+ * Get workflow execution description including status and metadata
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const handle = await client.getHandle('processOrder', 'order-123');
38
+ * const description = await handle.describe();
39
+ * console.log(description.workflowExecutionInfo.status); // RUNNING, COMPLETED, etc.
40
+ * ```
41
+ */
42
+ describe: () => ReturnType<WorkflowHandle["describe"]>;
43
+ /**
44
+ * Fetch the workflow execution history
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const handle = await client.getHandle('processOrder', 'order-123');
49
+ * const history = handle.fetchHistory();
50
+ * for await (const event of history) {
51
+ * console.log(event);
52
+ * }
53
+ * ```
54
+ */
55
+ fetchHistory: () => ReturnType<WorkflowHandle["fetchHistory"]>;
56
+ }
57
+ /**
58
+ * Typed Temporal client based on a contract
59
+ *
60
+ * Provides type-safe methods to start and execute workflows
61
+ * defined in the contract.
62
+ */
63
+ declare class TypedClient<TContract extends ContractDefinition> {
64
+ private readonly contract;
65
+ private readonly client;
66
+ private constructor();
67
+ /**
68
+ * Create a typed Temporal client from a contract
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const connection = await Connection.connect();
73
+ * const client = TypedClient.create(myContract, {
74
+ * connection,
75
+ * namespace: 'default',
76
+ * });
77
+ *
78
+ * const result = await client.executeWorkflow('processOrder', {
79
+ * workflowId: 'order-123',
80
+ * args: [...],
81
+ * });
82
+ * ```
83
+ */
84
+ static create<TContract extends ContractDefinition>(contract: TContract, options: ClientOptions): TypedClient<TContract>;
85
+ /**
86
+ * Start a workflow and return a typed handle
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const handle = await client.startWorkflow('processOrder', {
91
+ * workflowId: 'order-123',
92
+ * args: ['ORD-123', 'CUST-456', [{ productId: 'PROD-1', quantity: 2 }]],
93
+ * workflowExecutionTimeout: '1 day',
94
+ * retry: { maximumAttempts: 3 },
95
+ * });
96
+ *
97
+ * const result = await handle.result();
98
+ * ```
99
+ */
100
+ startWorkflow<TWorkflowName extends keyof TContract["workflows"]>(workflowName: TWorkflowName, {
101
+ args,
102
+ ...temporalOptions
103
+ }: TypedWorkflowStartOptions & {
104
+ args: ClientInferInput<TContract["workflows"][TWorkflowName]>;
105
+ }): Promise<TypedWorkflowHandle<TContract["workflows"][TWorkflowName]>>;
106
+ /**
107
+ * Execute a workflow (start and wait for result)
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const result = await client.executeWorkflow('processOrder', {
112
+ * workflowId: 'order-123',
113
+ * args: ['ORD-123', 'CUST-456', [{ productId: 'PROD-1', quantity: 2 }]],
114
+ * workflowExecutionTimeout: '1 day',
115
+ * retry: { maximumAttempts: 3 },
116
+ * });
117
+ *
118
+ * console.log(result.status); // fully typed!
119
+ * ```
120
+ */
121
+ executeWorkflow<TWorkflowName extends keyof TContract["workflows"]>(workflowName: TWorkflowName, {
122
+ args,
123
+ ...temporalOptions
124
+ }: TypedWorkflowStartOptions & {
125
+ args: ClientInferInput<TContract["workflows"][TWorkflowName]>;
126
+ }): Promise<ClientInferOutput<TContract["workflows"][TWorkflowName]>>;
127
+ /**
128
+ * Get a handle to an existing workflow
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * const handle = await client.getHandle('processOrder', 'order-123');
133
+ * const result = await handle.result();
134
+ * ```
135
+ */
136
+ getHandle<TWorkflowName extends keyof TContract["workflows"]>(workflowName: TWorkflowName, workflowId: string): Promise<TypedWorkflowHandle<TContract["workflows"][TWorkflowName]>>;
137
+ private createTypedHandle;
138
+ }
139
+ //#endregion
140
+ //#region src/errors.d.ts
141
+ /**
142
+ * Base error class for typed client errors
143
+ */
144
+ declare class TypedClientError extends Error {
145
+ constructor(message: string);
146
+ }
147
+ /**
148
+ * Error thrown when a workflow is not found in the contract
149
+ */
150
+ declare class WorkflowNotFoundError extends TypedClientError {
151
+ readonly workflowName: string;
152
+ constructor(workflowName: string);
153
+ }
154
+ /**
155
+ * Error thrown when workflow input or output validation fails
156
+ */
157
+ declare class WorkflowValidationError extends TypedClientError {
158
+ readonly workflowName: string;
159
+ readonly phase: "input" | "output";
160
+ readonly zodError: z.ZodError;
161
+ constructor(workflowName: string, phase: "input" | "output", zodError: z.ZodError);
162
+ }
163
+ /**
164
+ * Error thrown when query input or output validation fails
165
+ */
166
+ declare class QueryValidationError extends TypedClientError {
167
+ readonly queryName: string;
168
+ readonly phase: "input" | "output";
169
+ readonly zodError: z.ZodError;
170
+ constructor(queryName: string, phase: "input" | "output", zodError: z.ZodError);
171
+ }
172
+ /**
173
+ * Error thrown when signal input validation fails
174
+ */
175
+ declare class SignalValidationError extends TypedClientError {
176
+ readonly signalName: string;
177
+ readonly zodError: z.ZodError;
178
+ constructor(signalName: string, zodError: z.ZodError);
179
+ }
180
+ /**
181
+ * Error thrown when update input or output validation fails
182
+ */
183
+ declare class UpdateValidationError extends TypedClientError {
184
+ readonly updateName: string;
185
+ readonly phase: "input" | "output";
186
+ readonly zodError: z.ZodError;
187
+ constructor(updateName: string, phase: "input" | "output", zodError: z.ZodError);
188
+ }
189
+ //#endregion
190
+ export { QueryValidationError, SignalValidationError, TypedClient, TypedClientError, type TypedWorkflowHandle, type TypedWorkflowStartOptions, UpdateValidationError, WorkflowNotFoundError, WorkflowValidationError };