@temporal-contract/contract 0.2.0 → 2.0.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.
package/dist/index.cjs CHANGED
@@ -1,6 +1,5 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let zod = require("zod");
3
-
4
3
  //#region src/builder.ts
5
4
  /**
6
5
  * Define a Temporal activity with type-safe input and output schemas.
@@ -66,6 +65,14 @@ function defineSignal(definition) {
66
65
  * Queries allow you to read the current state of a running workflow without
67
66
  * modifying it. They are synchronous and should not perform any mutations.
68
67
  *
68
+ * **Synchronous validation required.** Temporal query handlers must complete
69
+ * synchronously, so the input and output schemas you pass here must validate
70
+ * synchronously. In practice this rules out async refinements (e.g. Zod's
71
+ * `.refine(async (x) => …)`). Standard Schema doesn't expose the sync/async
72
+ * distinction at the type level, so the worker checks at runtime and throws
73
+ * if it ever receives a `Promise` from `~standard.validate`. Use plain Zod /
74
+ * Valibot / ArkType object schemas without async refinements.
75
+ *
69
76
  * @template TQuery - The query definition type with input/output schemas
70
77
  * @param definition - The query definition containing input and output schemas
71
78
  * @returns The same definition with preserved types for type inference
@@ -119,6 +126,45 @@ function defineUpdate(definition) {
119
126
  return definition;
120
127
  }
121
128
  /**
129
+ * Define a typed search attribute on a workflow.
130
+ *
131
+ * Search attributes are indexed on Temporal's visibility store and let you
132
+ * query / filter workflow executions by domain attributes. Declaring them on
133
+ * the contract means the client's workflow-start options and (eventually)
134
+ * the worker's search-attribute reader are constrained to declared keys
135
+ * with the right value types.
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * import { defineSearchAttribute } from '@temporal-contract/contract';
140
+ *
141
+ * defineWorkflow({
142
+ * input: z.object({ orderId: z.string() }),
143
+ * output: z.object({ status: z.string() }),
144
+ * searchAttributes: {
145
+ * customerId: defineSearchAttribute({ kind: 'KEYWORD' }),
146
+ * priority: defineSearchAttribute({ kind: 'INT' }),
147
+ * placedAt: defineSearchAttribute({ kind: 'DATETIME' }),
148
+ * },
149
+ * });
150
+ * ```
151
+ *
152
+ * The seven Temporal kinds map to TypeScript types like so:
153
+ *
154
+ * | kind | TS type |
155
+ * | --------------- | --------- |
156
+ * | `TEXT` | `string` |
157
+ * | `KEYWORD` | `string` |
158
+ * | `INT` | `number` |
159
+ * | `DOUBLE` | `number` |
160
+ * | `BOOL` | `boolean` |
161
+ * | `DATETIME` | `Date` |
162
+ * | `KEYWORD_LIST` | `string[]`|
163
+ */
164
+ function defineSearchAttribute(definition) {
165
+ return definition;
166
+ }
167
+ /**
122
168
  * Define a Temporal workflow with type-safe input, output, and associated operations.
123
169
  *
124
170
  * Workflows are durable functions that orchestrate activities, handle timeouts,
@@ -271,6 +317,19 @@ const updateDefinitionSchema = zod.z.object({
271
317
  output: zod.z.custom((val) => isStandardSchema(val), { message: "output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)" })
272
318
  });
273
319
  /**
320
+ * Schema for validating search attribute definitions
321
+ */
322
+ const searchAttributeKindSchema = zod.z.enum([
323
+ "TEXT",
324
+ "KEYWORD",
325
+ "INT",
326
+ "DOUBLE",
327
+ "BOOL",
328
+ "DATETIME",
329
+ "KEYWORD_LIST"
330
+ ]);
331
+ const searchAttributeDefinitionSchema = zod.z.object({ kind: searchAttributeKindSchema });
332
+ /**
274
333
  * Schema for validating workflow definitions
275
334
  */
276
335
  const workflowDefinitionSchema = zod.z.object({
@@ -279,7 +338,8 @@ const workflowDefinitionSchema = zod.z.object({
279
338
  activities: zod.z.record(identifierSchema, activityDefinitionSchema).optional(),
280
339
  signals: zod.z.record(identifierSchema, signalDefinitionSchema).optional(),
281
340
  queries: zod.z.record(identifierSchema, queryDefinitionSchema).optional(),
282
- updates: zod.z.record(identifierSchema, updateDefinitionSchema).optional()
341
+ updates: zod.z.record(identifierSchema, updateDefinitionSchema).optional(),
342
+ searchAttributes: zod.z.record(identifierSchema, searchAttributeDefinitionSchema).optional()
283
343
  });
284
344
  /**
285
345
  * Schema for validating a contract definition structure
@@ -289,22 +349,36 @@ const contractValidationSchema = zod.z.object({
289
349
  workflows: zod.z.record(identifierSchema, workflowDefinitionSchema).refine((workflows) => Object.keys(workflows).length > 0, { message: "at least one workflow is required" }),
290
350
  activities: zod.z.record(identifierSchema, activityDefinitionSchema).optional()
291
351
  }).superRefine((contract, ctx) => {
292
- if (!contract.activities) return;
293
- for (const [workflowName, workflow] of Object.entries(contract.workflows)) if (workflow.activities) {
294
- for (const activityName of Object.keys(workflow.activities)) if (activityName in contract.activities) {
295
- ctx.addIssue({
296
- code: zod.z.ZodIssueCode.custom,
297
- message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity "${activityName}".`
298
- });
299
- return;
352
+ const GLOBAL_OWNER = Symbol("global");
353
+ const owners = /* @__PURE__ */ new Map();
354
+ if (contract.activities) for (const activityName of Object.keys(contract.activities)) owners.set(activityName, GLOBAL_OWNER);
355
+ for (const [workflowName, workflow] of Object.entries(contract.workflows)) {
356
+ if (!workflow.activities) continue;
357
+ for (const activityName of Object.keys(workflow.activities)) {
358
+ const previousOwner = owners.get(activityName);
359
+ if (previousOwner === GLOBAL_OWNER) {
360
+ ctx.addIssue({
361
+ code: zod.z.ZodIssueCode.custom,
362
+ message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity "${activityName}".`
363
+ });
364
+ continue;
365
+ }
366
+ if (typeof previousOwner === "string") {
367
+ ctx.addIssue({
368
+ code: zod.z.ZodIssueCode.custom,
369
+ message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with the same-named activity in workflow "${previousOwner}". Activities share a single flat namespace at runtime — rename one of them.`
370
+ });
371
+ continue;
372
+ }
373
+ owners.set(activityName, workflowName);
300
374
  }
301
375
  }
302
376
  });
303
-
304
377
  //#endregion
305
378
  exports.defineActivity = defineActivity;
306
379
  exports.defineContract = defineContract;
307
380
  exports.defineQuery = defineQuery;
381
+ exports.defineSearchAttribute = defineSearchAttribute;
308
382
  exports.defineSignal = defineSignal;
309
383
  exports.defineUpdate = defineUpdate;
310
- exports.defineWorkflow = defineWorkflow;
384
+ exports.defineWorkflow = defineWorkflow;
package/dist/index.d.cts CHANGED
@@ -34,16 +34,48 @@ type UpdateDefinition<TInput extends AnySchema = AnySchema, TOutput extends AnyS
34
34
  readonly input: TInput;
35
35
  readonly output: TOutput;
36
36
  };
37
+ /**
38
+ * The seven Temporal search attribute kinds.
39
+ *
40
+ * Mirrors `@temporalio/common`'s `SearchAttributeType` so values flow into
41
+ * Temporal's `typedSearchAttributes` API unchanged.
42
+ */
43
+ type SearchAttributeKind = "TEXT" | "KEYWORD" | "INT" | "DOUBLE" | "BOOL" | "DATETIME" | "KEYWORD_LIST";
44
+ /**
45
+ * Map each {@link SearchAttributeKind} to its TypeScript representation.
46
+ *
47
+ * - `TEXT` / `KEYWORD` → `string`
48
+ * - `INT` / `DOUBLE` → `number`
49
+ * - `BOOL` → `boolean`
50
+ * - `DATETIME` → `Date`
51
+ * - `KEYWORD_LIST` → `string[]`
52
+ */
53
+ type SearchAttributeKindToType<T extends SearchAttributeKind> = {
54
+ TEXT: string;
55
+ KEYWORD: string;
56
+ INT: number;
57
+ DOUBLE: number;
58
+ BOOL: boolean;
59
+ DATETIME: Date;
60
+ KEYWORD_LIST: string[];
61
+ }[T];
62
+ /**
63
+ * Definition of a typed search attribute on a workflow.
64
+ */
65
+ type SearchAttributeDefinition<TKind extends SearchAttributeKind = SearchAttributeKind> = {
66
+ readonly kind: TKind;
67
+ };
37
68
  /**
38
69
  * Definition of a workflow
39
70
  */
40
- type WorkflowDefinition<TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>, TSignals extends Record<string, SignalDefinition> = Record<string, SignalDefinition>, TQueries extends Record<string, QueryDefinition> = Record<string, QueryDefinition>, TUpdates extends Record<string, UpdateDefinition> = Record<string, UpdateDefinition>> = {
71
+ type WorkflowDefinition<TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>, TSignals extends Record<string, SignalDefinition> = Record<string, SignalDefinition>, TQueries extends Record<string, QueryDefinition> = Record<string, QueryDefinition>, TUpdates extends Record<string, UpdateDefinition> = Record<string, UpdateDefinition>, TSearchAttributes extends Record<string, SearchAttributeDefinition> = Record<string, SearchAttributeDefinition>> = {
41
72
  readonly input: AnySchema;
42
73
  readonly output: AnySchema;
43
74
  readonly activities?: TActivities;
44
75
  readonly signals?: TSignals;
45
76
  readonly queries?: TQueries;
46
77
  readonly updates?: TUpdates;
78
+ readonly searchAttributes?: TSearchAttributes;
47
79
  };
48
80
  /**
49
81
  * Contract definition containing workflows and optional global activities
@@ -147,6 +179,14 @@ declare function defineSignal<TSignal extends SignalDefinition>(definition: TSig
147
179
  * Queries allow you to read the current state of a running workflow without
148
180
  * modifying it. They are synchronous and should not perform any mutations.
149
181
  *
182
+ * **Synchronous validation required.** Temporal query handlers must complete
183
+ * synchronously, so the input and output schemas you pass here must validate
184
+ * synchronously. In practice this rules out async refinements (e.g. Zod's
185
+ * `.refine(async (x) => …)`). Standard Schema doesn't expose the sync/async
186
+ * distinction at the type level, so the worker checks at runtime and throws
187
+ * if it ever receives a `Promise` from `~standard.validate`. Use plain Zod /
188
+ * Valibot / ArkType object schemas without async refinements.
189
+ *
150
190
  * @template TQuery - The query definition type with input/output schemas
151
191
  * @param definition - The query definition containing input and output schemas
152
192
  * @returns The same definition with preserved types for type inference
@@ -195,6 +235,43 @@ declare function defineQuery<TQuery extends QueryDefinition>(definition: TQuery)
195
235
  * ```
196
236
  */
197
237
  declare function defineUpdate<TUpdate extends UpdateDefinition>(definition: TUpdate): TUpdate;
238
+ /**
239
+ * Define a typed search attribute on a workflow.
240
+ *
241
+ * Search attributes are indexed on Temporal's visibility store and let you
242
+ * query / filter workflow executions by domain attributes. Declaring them on
243
+ * the contract means the client's workflow-start options and (eventually)
244
+ * the worker's search-attribute reader are constrained to declared keys
245
+ * with the right value types.
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * import { defineSearchAttribute } from '@temporal-contract/contract';
250
+ *
251
+ * defineWorkflow({
252
+ * input: z.object({ orderId: z.string() }),
253
+ * output: z.object({ status: z.string() }),
254
+ * searchAttributes: {
255
+ * customerId: defineSearchAttribute({ kind: 'KEYWORD' }),
256
+ * priority: defineSearchAttribute({ kind: 'INT' }),
257
+ * placedAt: defineSearchAttribute({ kind: 'DATETIME' }),
258
+ * },
259
+ * });
260
+ * ```
261
+ *
262
+ * The seven Temporal kinds map to TypeScript types like so:
263
+ *
264
+ * | kind | TS type |
265
+ * | --------------- | --------- |
266
+ * | `TEXT` | `string` |
267
+ * | `KEYWORD` | `string` |
268
+ * | `INT` | `number` |
269
+ * | `DOUBLE` | `number` |
270
+ * | `BOOL` | `boolean` |
271
+ * | `DATETIME` | `Date` |
272
+ * | `KEYWORD_LIST` | `string[]`|
273
+ */
274
+ declare function defineSearchAttribute<TKind extends SearchAttributeKind>(definition: SearchAttributeDefinition<TKind>): SearchAttributeDefinition<TKind>;
198
275
  /**
199
276
  * Define a Temporal workflow with type-safe input, output, and associated operations.
200
277
  *
@@ -282,5 +359,5 @@ declare function defineWorkflow<TWorkflow extends WorkflowDefinition>(definition
282
359
  */
283
360
  declare function defineContract<TContract extends ContractDefinition>(definition: TContract): TContract;
284
361
  //#endregion
285
- export { type ActivityDefinition, type AnySchema, type ContractDefinition, type InferActivityNames, type InferContractWorkflows, type InferWorkflowNames, type QueryDefinition, type SignalDefinition, type UpdateDefinition, type WorkflowDefinition, defineActivity, defineContract, defineQuery, defineSignal, defineUpdate, defineWorkflow };
362
+ export { type ActivityDefinition, type AnySchema, type ContractDefinition, type InferActivityNames, type InferContractWorkflows, type InferWorkflowNames, type QueryDefinition, type SearchAttributeDefinition, type SearchAttributeKind, type SearchAttributeKindToType, type SignalDefinition, type UpdateDefinition, type WorkflowDefinition, defineActivity, defineContract, defineQuery, defineSearchAttribute, defineSignal, defineUpdate, defineWorkflow };
286
363
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/builder.ts"],"mappings":";;;;;AAOA;;;KAAY,SAAA,GAAY,gBAAA;;AAKxB;;KAAY,kBAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,gBAAA,gBAAgC,SAAA,GAAY,SAAA;EAAA,SAC7C,KAAA,EAAO,MAAA;AAAA;;;;KAMN,eAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,gBAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,kBAAA,qBACU,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA,oBACvD,MAAA,SAAe,gBAAA,IAAoB,MAAA,SAAe,gBAAA,oBAClD,MAAA,SAAe,eAAA,IAAmB,MAAA,SAAe,eAAA,oBACjD,MAAA,SAAe,gBAAA,IAAoB,MAAA,SAAe,gBAAA;EAAA,SAE1D,KAAA,EAAO,SAAA;EAAA,SACP,MAAA,EAAQ,SAAA;EAAA,SACR,UAAA,GAAa,WAAA;EAAA,SACb,OAAA,GAAU,QAAA;EAAA,SACV,OAAA,GAAU,QAAA;EAAA,SACV,OAAA,GAAU,QAAA;AAAA;;AAjCrB;;KAuCY,kBAAA,oBACS,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA,uBACnD,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA;EAAA,SAE/D,SAAA;EAAA,SACA,SAAA,EAAW,UAAA;EAAA,SACX,UAAA,GAAa,WAAA;AAAA;;;;;;;;;;;;;KAgBZ,kBAAA,mBAAqC,kBAAA,UACzC,SAAA;;;;AAnDR;;;;;;KA8DY,kBAAA,mBAAqC,kBAAA,IAC/C,SAAA,uBAAgC,MAAA,SAAe,kBAAA,UACrC,SAAA;;;;;;;;;KAWA,sBAAA,mBAAyC,kBAAA,IAAsB,SAAA;;;;;AA7G3E;;;;;AAKA;;;;;;;;;;;;;;;;;;;;;;iBC8BgB,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA;ADrBH;;;;;;;;;;;;;;;AAOA;;;;;;;;AAPA,iBCgDgB,YAAA,iBAA6B,gBAAA,CAAA,CAAkB,UAAA,EAAY,OAAA,GAAU,OAAA;;;;;;;;;;;;;;;AD9BrF;;;;;;;;;;iBC0DgB,WAAA,gBAA2B,eAAA,CAAA,CAAiB,UAAA,EAAY,MAAA,GAAS,MAAA;;;;;;;;;;;;;AD/CjF;;;;;;;;;;;;;;;;iBC+EgB,YAAA,iBAA6B,gBAAA,CAAA,CAAkB,UAAA,EAAY,OAAA,GAAU,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCrE,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA;;;;;;;;;;;ADrGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA;;;;;;;;;iBCsIgB,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/builder.ts"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -34,16 +34,48 @@ type UpdateDefinition<TInput extends AnySchema = AnySchema, TOutput extends AnyS
34
34
  readonly input: TInput;
35
35
  readonly output: TOutput;
36
36
  };
37
+ /**
38
+ * The seven Temporal search attribute kinds.
39
+ *
40
+ * Mirrors `@temporalio/common`'s `SearchAttributeType` so values flow into
41
+ * Temporal's `typedSearchAttributes` API unchanged.
42
+ */
43
+ type SearchAttributeKind = "TEXT" | "KEYWORD" | "INT" | "DOUBLE" | "BOOL" | "DATETIME" | "KEYWORD_LIST";
44
+ /**
45
+ * Map each {@link SearchAttributeKind} to its TypeScript representation.
46
+ *
47
+ * - `TEXT` / `KEYWORD` → `string`
48
+ * - `INT` / `DOUBLE` → `number`
49
+ * - `BOOL` → `boolean`
50
+ * - `DATETIME` → `Date`
51
+ * - `KEYWORD_LIST` → `string[]`
52
+ */
53
+ type SearchAttributeKindToType<T extends SearchAttributeKind> = {
54
+ TEXT: string;
55
+ KEYWORD: string;
56
+ INT: number;
57
+ DOUBLE: number;
58
+ BOOL: boolean;
59
+ DATETIME: Date;
60
+ KEYWORD_LIST: string[];
61
+ }[T];
62
+ /**
63
+ * Definition of a typed search attribute on a workflow.
64
+ */
65
+ type SearchAttributeDefinition<TKind extends SearchAttributeKind = SearchAttributeKind> = {
66
+ readonly kind: TKind;
67
+ };
37
68
  /**
38
69
  * Definition of a workflow
39
70
  */
40
- type WorkflowDefinition<TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>, TSignals extends Record<string, SignalDefinition> = Record<string, SignalDefinition>, TQueries extends Record<string, QueryDefinition> = Record<string, QueryDefinition>, TUpdates extends Record<string, UpdateDefinition> = Record<string, UpdateDefinition>> = {
71
+ type WorkflowDefinition<TActivities extends Record<string, ActivityDefinition> = Record<string, ActivityDefinition>, TSignals extends Record<string, SignalDefinition> = Record<string, SignalDefinition>, TQueries extends Record<string, QueryDefinition> = Record<string, QueryDefinition>, TUpdates extends Record<string, UpdateDefinition> = Record<string, UpdateDefinition>, TSearchAttributes extends Record<string, SearchAttributeDefinition> = Record<string, SearchAttributeDefinition>> = {
41
72
  readonly input: AnySchema;
42
73
  readonly output: AnySchema;
43
74
  readonly activities?: TActivities;
44
75
  readonly signals?: TSignals;
45
76
  readonly queries?: TQueries;
46
77
  readonly updates?: TUpdates;
78
+ readonly searchAttributes?: TSearchAttributes;
47
79
  };
48
80
  /**
49
81
  * Contract definition containing workflows and optional global activities
@@ -147,6 +179,14 @@ declare function defineSignal<TSignal extends SignalDefinition>(definition: TSig
147
179
  * Queries allow you to read the current state of a running workflow without
148
180
  * modifying it. They are synchronous and should not perform any mutations.
149
181
  *
182
+ * **Synchronous validation required.** Temporal query handlers must complete
183
+ * synchronously, so the input and output schemas you pass here must validate
184
+ * synchronously. In practice this rules out async refinements (e.g. Zod's
185
+ * `.refine(async (x) => …)`). Standard Schema doesn't expose the sync/async
186
+ * distinction at the type level, so the worker checks at runtime and throws
187
+ * if it ever receives a `Promise` from `~standard.validate`. Use plain Zod /
188
+ * Valibot / ArkType object schemas without async refinements.
189
+ *
150
190
  * @template TQuery - The query definition type with input/output schemas
151
191
  * @param definition - The query definition containing input and output schemas
152
192
  * @returns The same definition with preserved types for type inference
@@ -195,6 +235,43 @@ declare function defineQuery<TQuery extends QueryDefinition>(definition: TQuery)
195
235
  * ```
196
236
  */
197
237
  declare function defineUpdate<TUpdate extends UpdateDefinition>(definition: TUpdate): TUpdate;
238
+ /**
239
+ * Define a typed search attribute on a workflow.
240
+ *
241
+ * Search attributes are indexed on Temporal's visibility store and let you
242
+ * query / filter workflow executions by domain attributes. Declaring them on
243
+ * the contract means the client's workflow-start options and (eventually)
244
+ * the worker's search-attribute reader are constrained to declared keys
245
+ * with the right value types.
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * import { defineSearchAttribute } from '@temporal-contract/contract';
250
+ *
251
+ * defineWorkflow({
252
+ * input: z.object({ orderId: z.string() }),
253
+ * output: z.object({ status: z.string() }),
254
+ * searchAttributes: {
255
+ * customerId: defineSearchAttribute({ kind: 'KEYWORD' }),
256
+ * priority: defineSearchAttribute({ kind: 'INT' }),
257
+ * placedAt: defineSearchAttribute({ kind: 'DATETIME' }),
258
+ * },
259
+ * });
260
+ * ```
261
+ *
262
+ * The seven Temporal kinds map to TypeScript types like so:
263
+ *
264
+ * | kind | TS type |
265
+ * | --------------- | --------- |
266
+ * | `TEXT` | `string` |
267
+ * | `KEYWORD` | `string` |
268
+ * | `INT` | `number` |
269
+ * | `DOUBLE` | `number` |
270
+ * | `BOOL` | `boolean` |
271
+ * | `DATETIME` | `Date` |
272
+ * | `KEYWORD_LIST` | `string[]`|
273
+ */
274
+ declare function defineSearchAttribute<TKind extends SearchAttributeKind>(definition: SearchAttributeDefinition<TKind>): SearchAttributeDefinition<TKind>;
198
275
  /**
199
276
  * Define a Temporal workflow with type-safe input, output, and associated operations.
200
277
  *
@@ -282,5 +359,5 @@ declare function defineWorkflow<TWorkflow extends WorkflowDefinition>(definition
282
359
  */
283
360
  declare function defineContract<TContract extends ContractDefinition>(definition: TContract): TContract;
284
361
  //#endregion
285
- export { type ActivityDefinition, type AnySchema, type ContractDefinition, type InferActivityNames, type InferContractWorkflows, type InferWorkflowNames, type QueryDefinition, type SignalDefinition, type UpdateDefinition, type WorkflowDefinition, defineActivity, defineContract, defineQuery, defineSignal, defineUpdate, defineWorkflow };
362
+ export { type ActivityDefinition, type AnySchema, type ContractDefinition, type InferActivityNames, type InferContractWorkflows, type InferWorkflowNames, type QueryDefinition, type SearchAttributeDefinition, type SearchAttributeKind, type SearchAttributeKindToType, type SignalDefinition, type UpdateDefinition, type WorkflowDefinition, defineActivity, defineContract, defineQuery, defineSearchAttribute, defineSignal, defineUpdate, defineWorkflow };
286
363
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/builder.ts"],"mappings":";;;;;AAOA;;;KAAY,SAAA,GAAY,gBAAA;;AAKxB;;KAAY,kBAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,gBAAA,gBAAgC,SAAA,GAAY,SAAA;EAAA,SAC7C,KAAA,EAAO,MAAA;AAAA;;;;KAMN,eAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,gBAAA,gBACK,SAAA,GAAY,SAAA,kBACX,SAAA,GAAY,SAAA;EAAA,SAEnB,KAAA,EAAO,MAAA;EAAA,SACP,MAAA,EAAQ,OAAA;AAAA;;;;KAMP,kBAAA,qBACU,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA,oBACvD,MAAA,SAAe,gBAAA,IAAoB,MAAA,SAAe,gBAAA,oBAClD,MAAA,SAAe,eAAA,IAAmB,MAAA,SAAe,eAAA,oBACjD,MAAA,SAAe,gBAAA,IAAoB,MAAA,SAAe,gBAAA;EAAA,SAE1D,KAAA,EAAO,SAAA;EAAA,SACP,MAAA,EAAQ,SAAA;EAAA,SACR,UAAA,GAAa,WAAA;EAAA,SACb,OAAA,GAAU,QAAA;EAAA,SACV,OAAA,GAAU,QAAA;EAAA,SACV,OAAA,GAAU,QAAA;AAAA;;AAjCrB;;KAuCY,kBAAA,oBACS,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA,uBACnD,MAAA,SAAe,kBAAA,IAAsB,MAAA,SAAe,kBAAA;EAAA,SAE/D,SAAA;EAAA,SACA,SAAA,EAAW,UAAA;EAAA,SACX,UAAA,GAAa,WAAA;AAAA;;;;;;;;;;;;;KAgBZ,kBAAA,mBAAqC,kBAAA,UACzC,SAAA;;;;AAnDR;;;;;;KA8DY,kBAAA,mBAAqC,kBAAA,IAC/C,SAAA,uBAAgC,MAAA,SAAe,kBAAA,UACrC,SAAA;;;;;;;;;KAWA,sBAAA,mBAAyC,kBAAA,IAAsB,SAAA;;;;;AA7G3E;;;;;AAKA;;;;;;;;;;;;;;;;;;;;;;iBC8BgB,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA;ADrBH;;;;;;;;;;;;;;;AAOA;;;;;;;;AAPA,iBCgDgB,YAAA,iBAA6B,gBAAA,CAAA,CAAkB,UAAA,EAAY,OAAA,GAAU,OAAA;;;;;;;;;;;;;;;AD9BrF;;;;;;;;;;iBC0DgB,WAAA,gBAA2B,eAAA,CAAA,CAAiB,UAAA,EAAY,MAAA,GAAS,MAAA;;;;;;;;;;;;;AD/CjF;;;;;;;;;;;;;;;;iBC+EgB,YAAA,iBAA6B,gBAAA,CAAA,CAAkB,UAAA,EAAY,OAAA,GAAU,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCrE,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA;;;;;;;;;;;ADrGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA;;;;;;;;;iBCsIgB,cAAA,mBAAiC,kBAAA,CAAA,CAC/C,UAAA,EAAY,SAAA,GACX,SAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/builder.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
-
3
2
  //#region src/builder.ts
4
3
  /**
5
4
  * Define a Temporal activity with type-safe input and output schemas.
@@ -65,6 +64,14 @@ function defineSignal(definition) {
65
64
  * Queries allow you to read the current state of a running workflow without
66
65
  * modifying it. They are synchronous and should not perform any mutations.
67
66
  *
67
+ * **Synchronous validation required.** Temporal query handlers must complete
68
+ * synchronously, so the input and output schemas you pass here must validate
69
+ * synchronously. In practice this rules out async refinements (e.g. Zod's
70
+ * `.refine(async (x) => …)`). Standard Schema doesn't expose the sync/async
71
+ * distinction at the type level, so the worker checks at runtime and throws
72
+ * if it ever receives a `Promise` from `~standard.validate`. Use plain Zod /
73
+ * Valibot / ArkType object schemas without async refinements.
74
+ *
68
75
  * @template TQuery - The query definition type with input/output schemas
69
76
  * @param definition - The query definition containing input and output schemas
70
77
  * @returns The same definition with preserved types for type inference
@@ -118,6 +125,45 @@ function defineUpdate(definition) {
118
125
  return definition;
119
126
  }
120
127
  /**
128
+ * Define a typed search attribute on a workflow.
129
+ *
130
+ * Search attributes are indexed on Temporal's visibility store and let you
131
+ * query / filter workflow executions by domain attributes. Declaring them on
132
+ * the contract means the client's workflow-start options and (eventually)
133
+ * the worker's search-attribute reader are constrained to declared keys
134
+ * with the right value types.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * import { defineSearchAttribute } from '@temporal-contract/contract';
139
+ *
140
+ * defineWorkflow({
141
+ * input: z.object({ orderId: z.string() }),
142
+ * output: z.object({ status: z.string() }),
143
+ * searchAttributes: {
144
+ * customerId: defineSearchAttribute({ kind: 'KEYWORD' }),
145
+ * priority: defineSearchAttribute({ kind: 'INT' }),
146
+ * placedAt: defineSearchAttribute({ kind: 'DATETIME' }),
147
+ * },
148
+ * });
149
+ * ```
150
+ *
151
+ * The seven Temporal kinds map to TypeScript types like so:
152
+ *
153
+ * | kind | TS type |
154
+ * | --------------- | --------- |
155
+ * | `TEXT` | `string` |
156
+ * | `KEYWORD` | `string` |
157
+ * | `INT` | `number` |
158
+ * | `DOUBLE` | `number` |
159
+ * | `BOOL` | `boolean` |
160
+ * | `DATETIME` | `Date` |
161
+ * | `KEYWORD_LIST` | `string[]`|
162
+ */
163
+ function defineSearchAttribute(definition) {
164
+ return definition;
165
+ }
166
+ /**
121
167
  * Define a Temporal workflow with type-safe input, output, and associated operations.
122
168
  *
123
169
  * Workflows are durable functions that orchestrate activities, handle timeouts,
@@ -270,6 +316,19 @@ const updateDefinitionSchema = z.object({
270
316
  output: z.custom((val) => isStandardSchema(val), { message: "output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)" })
271
317
  });
272
318
  /**
319
+ * Schema for validating search attribute definitions
320
+ */
321
+ const searchAttributeKindSchema = z.enum([
322
+ "TEXT",
323
+ "KEYWORD",
324
+ "INT",
325
+ "DOUBLE",
326
+ "BOOL",
327
+ "DATETIME",
328
+ "KEYWORD_LIST"
329
+ ]);
330
+ const searchAttributeDefinitionSchema = z.object({ kind: searchAttributeKindSchema });
331
+ /**
273
332
  * Schema for validating workflow definitions
274
333
  */
275
334
  const workflowDefinitionSchema = z.object({
@@ -278,7 +337,8 @@ const workflowDefinitionSchema = z.object({
278
337
  activities: z.record(identifierSchema, activityDefinitionSchema).optional(),
279
338
  signals: z.record(identifierSchema, signalDefinitionSchema).optional(),
280
339
  queries: z.record(identifierSchema, queryDefinitionSchema).optional(),
281
- updates: z.record(identifierSchema, updateDefinitionSchema).optional()
340
+ updates: z.record(identifierSchema, updateDefinitionSchema).optional(),
341
+ searchAttributes: z.record(identifierSchema, searchAttributeDefinitionSchema).optional()
282
342
  });
283
343
  /**
284
344
  * Schema for validating a contract definition structure
@@ -288,18 +348,32 @@ const contractValidationSchema = z.object({
288
348
  workflows: z.record(identifierSchema, workflowDefinitionSchema).refine((workflows) => Object.keys(workflows).length > 0, { message: "at least one workflow is required" }),
289
349
  activities: z.record(identifierSchema, activityDefinitionSchema).optional()
290
350
  }).superRefine((contract, ctx) => {
291
- if (!contract.activities) return;
292
- for (const [workflowName, workflow] of Object.entries(contract.workflows)) if (workflow.activities) {
293
- for (const activityName of Object.keys(workflow.activities)) if (activityName in contract.activities) {
294
- ctx.addIssue({
295
- code: z.ZodIssueCode.custom,
296
- message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity "${activityName}".`
297
- });
298
- return;
351
+ const GLOBAL_OWNER = Symbol("global");
352
+ const owners = /* @__PURE__ */ new Map();
353
+ if (contract.activities) for (const activityName of Object.keys(contract.activities)) owners.set(activityName, GLOBAL_OWNER);
354
+ for (const [workflowName, workflow] of Object.entries(contract.workflows)) {
355
+ if (!workflow.activities) continue;
356
+ for (const activityName of Object.keys(workflow.activities)) {
357
+ const previousOwner = owners.get(activityName);
358
+ if (previousOwner === GLOBAL_OWNER) {
359
+ ctx.addIssue({
360
+ code: z.ZodIssueCode.custom,
361
+ message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity "${activityName}".`
362
+ });
363
+ continue;
364
+ }
365
+ if (typeof previousOwner === "string") {
366
+ ctx.addIssue({
367
+ code: z.ZodIssueCode.custom,
368
+ message: `workflow "${workflowName}" has activity "${activityName}" that conflicts with the same-named activity in workflow "${previousOwner}". Activities share a single flat namespace at runtime — rename one of them.`
369
+ });
370
+ continue;
371
+ }
372
+ owners.set(activityName, workflowName);
299
373
  }
300
374
  }
301
375
  });
302
-
303
376
  //#endregion
304
- export { defineActivity, defineContract, defineQuery, defineSignal, defineUpdate, defineWorkflow };
377
+ export { defineActivity, defineContract, defineQuery, defineSearchAttribute, defineSignal, defineUpdate, defineWorkflow };
378
+
305
379
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/builder.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type {\n ActivityDefinition,\n ContractDefinition,\n QueryDefinition,\n SignalDefinition,\n UpdateDefinition,\n WorkflowDefinition,\n} from \"./types.js\";\n\n// Exported builders first (classic functions for hoisting)\n\n/**\n * Define a Temporal activity with type-safe input and output schemas.\n *\n * Activities are the building blocks of Temporal workflows that execute business logic\n * and interact with external services. This function preserves TypeScript types while\n * providing a consistent structure for activity definitions.\n *\n * @template TActivity - The activity definition type with input/output schemas\n * @param definition - The activity definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineActivity } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const sendEmail = defineActivity({\n * input: z.object({\n * to: z.string().email(),\n * subject: z.string(),\n * body: z.string(),\n * }),\n * output: z.object({\n * messageId: z.string(),\n * sentAt: z.date(),\n * }),\n * });\n * ```\n */\nexport function defineActivity<TActivity extends ActivityDefinition>(\n definition: TActivity,\n): TActivity {\n return definition;\n}\n\n/**\n * Define a Temporal signal with type-safe input schema.\n *\n * Signals are asynchronous messages sent to running workflows to update their state\n * or trigger certain behaviors. This function ensures type safety for signal payloads.\n *\n * @template TSignal - The signal definition type with input schema\n * @param definition - The signal definition containing input schema\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineSignal } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const approveOrder = defineSignal({\n * input: z.object({\n * orderId: z.string(),\n * approvedBy: z.string(),\n * }),\n * });\n * ```\n */\nexport function defineSignal<TSignal extends SignalDefinition>(definition: TSignal): TSignal {\n return definition;\n}\n\n/**\n * Define a Temporal query with type-safe input and output schemas.\n *\n * Queries allow you to read the current state of a running workflow without\n * modifying it. They are synchronous and should not perform any mutations.\n *\n * @template TQuery - The query definition type with input/output schemas\n * @param definition - The query definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineQuery } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const getOrderStatus = defineQuery({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({\n * status: z.enum(['pending', 'processing', 'completed', 'failed']),\n * updatedAt: z.date(),\n * }),\n * });\n * ```\n */\nexport function defineQuery<TQuery extends QueryDefinition>(definition: TQuery): TQuery {\n return definition;\n}\n\n/**\n * Define a Temporal update with type-safe input and output schemas.\n *\n * Updates are similar to signals but return a value and wait for the workflow\n * to process them before completing. They provide a synchronous way to modify\n * workflow state and get immediate feedback.\n *\n * @template TUpdate - The update definition type with input/output schemas\n * @param definition - The update definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineUpdate } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const updateOrderQuantity = defineUpdate({\n * input: z.object({\n * orderId: z.string(),\n * newQuantity: z.number().positive(),\n * }),\n * output: z.object({\n * success: z.boolean(),\n * totalPrice: z.number(),\n * }),\n * });\n * ```\n */\nexport function defineUpdate<TUpdate extends UpdateDefinition>(definition: TUpdate): TUpdate {\n return definition;\n}\n\n/**\n * Define a Temporal workflow with type-safe input, output, and associated operations.\n *\n * Workflows are durable functions that orchestrate activities, handle timeouts,\n * and manage long-running processes. This function provides type safety for the\n * entire workflow definition including activities, signals, queries, and updates.\n *\n * @template TWorkflow - The workflow definition type with all associated schemas\n * @param definition - The workflow definition containing input, output, and operations\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineWorkflow, defineActivity, defineSignal } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const processOrder = defineWorkflow({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ success: z.boolean() }),\n * activities: {\n * validatePayment: defineActivity({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ valid: z.boolean() }),\n * }),\n * },\n * signals: {\n * cancel: defineSignal({\n * input: z.object({ reason: z.string() }),\n * }),\n * },\n * });\n * ```\n */\nexport function defineWorkflow<TWorkflow extends WorkflowDefinition>(\n definition: TWorkflow,\n): TWorkflow {\n return definition;\n}\n\n/**\n * Define a complete Temporal contract with type-safe workflows and activities.\n *\n * A contract is the central definition that ties together your Temporal application's\n * workflows and activities. It provides:\n * - Type safety across client, worker, and workflow code\n * - Automatic validation at runtime\n * - Compile-time verification of implementations\n * - Clear API boundaries and documentation\n *\n * The contract validates the structure and ensures:\n * - Task queue is specified\n * - At least one workflow is defined\n * - Valid JavaScript identifiers are used\n * - No conflicts between global and workflow-specific activities\n * - All schemas implement the Standard Schema specification\n *\n * @template TContract - The contract definition type\n * @param definition - The complete contract definition\n * @returns The same definition with preserved types for type inference\n * @throws {Error} If the contract structure is invalid\n *\n * @example\n * ```typescript\n * import { defineContract } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const myContract = defineContract({\n * taskQueue: 'orders',\n * workflows: {\n * processOrder: {\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ success: z.boolean() }),\n * activities: {\n * chargePayment: {\n * input: z.object({ amount: z.number() }),\n * output: z.object({ transactionId: z.string() }),\n * },\n * },\n * },\n * },\n * // Optional global activities shared across workflows\n * activities: {\n * logEvent: {\n * input: z.object({ message: z.string() }),\n * output: z.void(),\n * },\n * },\n * });\n * ```\n */\nexport function defineContract<TContract extends ContractDefinition>(\n definition: TContract,\n): TContract {\n // Validate entire contract structure with Zod (including activity conflicts)\n const validationResult = contractValidationSchema.safeParse(definition);\n\n if (!validationResult.success) {\n const cleanMessage = getCleanErrorMessage(validationResult.error);\n throw new Error(`Contract validation failed: ${cleanMessage}`);\n }\n\n return definition;\n}\n\n/**\n * Check if a value is a Standard Schema compatible schema\n */\nfunction isStandardSchema(value: unknown): value is StandardSchemaV1 {\n // Standard Schema can be either an object or a function (e.g., ArkType)\n if (\n (typeof value !== \"object\" && typeof value !== \"function\") ||\n value === null ||\n !(\"~standard\" in value)\n ) {\n return false;\n }\n\n const standard = (value as Record<string, unknown>)[\"~standard\"];\n\n return (\n typeof standard === \"object\" &&\n standard !== null &&\n (standard as Record<string, unknown>)[\"version\"] === 1 &&\n typeof (standard as Record<string, unknown>)[\"validate\"] === \"function\"\n );\n}\n\n/**\n * Schema for validating JavaScript identifiers (workflow names, activity names, etc.)\n * Allows: letters, digits, underscore, dollar sign\n * Must start with: letter, underscore, or dollar sign\n */\nconst identifierSchema = z\n .string()\n .min(1)\n .regex(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, \"must be a valid JavaScript identifier\");\n\n/**\n * Extract a clean, single-line error message from a Zod validation error.\n *\n * Uses `error.issues` directly (compatible with Zod v4+) rather than parsing\n * `error.message` as JSON, which was a Zod v3 implementation detail.\n */\nfunction getCleanErrorMessage(error: z.ZodError): string {\n const issues = error.issues;\n if (!issues || issues.length === 0) {\n return error.message;\n }\n\n const firstIssue = issues[0];\n if (!firstIssue) {\n return error.message;\n }\n\n // For record key validation errors (invalid_key), surface the nested issue message\n if (\n firstIssue.code === \"invalid_key\" &&\n \"issues\" in firstIssue &&\n Array.isArray((firstIssue as { issues?: unknown[] }).issues) &&\n (firstIssue as { issues: { message?: string }[] }).issues.length > 0\n ) {\n const nestedMessage = (firstIssue as { issues: { message?: string }[] }).issues[0]?.message;\n if (nestedMessage) {\n return nestedMessage;\n }\n }\n\n return firstIssue.message ?? error.message;\n}\n\n/**\n * Schema for validating activity definitions\n * Checks that input and output are Standard Schema compatible schemas\n */\nconst activityDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating signal definitions\n */\nconst signalDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating query definitions\n */\nconst queryDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating update definitions\n */\nconst updateDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating workflow definitions\n */\nconst workflowDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n activities: z.record(identifierSchema, activityDefinitionSchema).optional(),\n signals: z.record(identifierSchema, signalDefinitionSchema).optional(),\n queries: z.record(identifierSchema, queryDefinitionSchema).optional(),\n updates: z.record(identifierSchema, updateDefinitionSchema).optional(),\n});\n\n/**\n * Schema for validating a contract definition structure\n */\nconst contractValidationSchema = z\n .object({\n taskQueue: z.string().trim().min(1, \"taskQueue cannot be empty\"),\n workflows: z\n .record(identifierSchema, workflowDefinitionSchema)\n .refine((workflows) => Object.keys(workflows).length > 0, {\n message: \"at least one workflow is required\",\n }),\n activities: z.record(identifierSchema, activityDefinitionSchema).optional(),\n })\n .superRefine((contract, ctx) => {\n // Check for conflicts between global and workflow-specific activities\n if (!contract.activities) {\n return;\n }\n\n for (const [workflowName, workflow] of Object.entries(contract.workflows)) {\n if (workflow.activities) {\n for (const activityName of Object.keys(workflow.activities)) {\n if (activityName in contract.activities) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `workflow \"${workflowName}\" has activity \"${activityName}\" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity \"${activityName}\".`,\n });\n return;\n }\n }\n }\n }\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/builder.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type {\n ActivityDefinition,\n ContractDefinition,\n QueryDefinition,\n SearchAttributeDefinition,\n SearchAttributeKind,\n SignalDefinition,\n UpdateDefinition,\n WorkflowDefinition,\n} from \"./types.js\";\n\n// Exported builders first (classic functions for hoisting)\n\n/**\n * Define a Temporal activity with type-safe input and output schemas.\n *\n * Activities are the building blocks of Temporal workflows that execute business logic\n * and interact with external services. This function preserves TypeScript types while\n * providing a consistent structure for activity definitions.\n *\n * @template TActivity - The activity definition type with input/output schemas\n * @param definition - The activity definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineActivity } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const sendEmail = defineActivity({\n * input: z.object({\n * to: z.string().email(),\n * subject: z.string(),\n * body: z.string(),\n * }),\n * output: z.object({\n * messageId: z.string(),\n * sentAt: z.date(),\n * }),\n * });\n * ```\n */\nexport function defineActivity<TActivity extends ActivityDefinition>(\n definition: TActivity,\n): TActivity {\n return definition;\n}\n\n/**\n * Define a Temporal signal with type-safe input schema.\n *\n * Signals are asynchronous messages sent to running workflows to update their state\n * or trigger certain behaviors. This function ensures type safety for signal payloads.\n *\n * @template TSignal - The signal definition type with input schema\n * @param definition - The signal definition containing input schema\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineSignal } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const approveOrder = defineSignal({\n * input: z.object({\n * orderId: z.string(),\n * approvedBy: z.string(),\n * }),\n * });\n * ```\n */\nexport function defineSignal<TSignal extends SignalDefinition>(definition: TSignal): TSignal {\n return definition;\n}\n\n/**\n * Define a Temporal query with type-safe input and output schemas.\n *\n * Queries allow you to read the current state of a running workflow without\n * modifying it. They are synchronous and should not perform any mutations.\n *\n * **Synchronous validation required.** Temporal query handlers must complete\n * synchronously, so the input and output schemas you pass here must validate\n * synchronously. In practice this rules out async refinements (e.g. Zod's\n * `.refine(async (x) => …)`). Standard Schema doesn't expose the sync/async\n * distinction at the type level, so the worker checks at runtime and throws\n * if it ever receives a `Promise` from `~standard.validate`. Use plain Zod /\n * Valibot / ArkType object schemas without async refinements.\n *\n * @template TQuery - The query definition type with input/output schemas\n * @param definition - The query definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineQuery } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const getOrderStatus = defineQuery({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({\n * status: z.enum(['pending', 'processing', 'completed', 'failed']),\n * updatedAt: z.date(),\n * }),\n * });\n * ```\n */\nexport function defineQuery<TQuery extends QueryDefinition>(definition: TQuery): TQuery {\n return definition;\n}\n\n/**\n * Define a Temporal update with type-safe input and output schemas.\n *\n * Updates are similar to signals but return a value and wait for the workflow\n * to process them before completing. They provide a synchronous way to modify\n * workflow state and get immediate feedback.\n *\n * @template TUpdate - The update definition type with input/output schemas\n * @param definition - The update definition containing input and output schemas\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineUpdate } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const updateOrderQuantity = defineUpdate({\n * input: z.object({\n * orderId: z.string(),\n * newQuantity: z.number().positive(),\n * }),\n * output: z.object({\n * success: z.boolean(),\n * totalPrice: z.number(),\n * }),\n * });\n * ```\n */\nexport function defineUpdate<TUpdate extends UpdateDefinition>(definition: TUpdate): TUpdate {\n return definition;\n}\n\n/**\n * Define a typed search attribute on a workflow.\n *\n * Search attributes are indexed on Temporal's visibility store and let you\n * query / filter workflow executions by domain attributes. Declaring them on\n * the contract means the client's workflow-start options and (eventually)\n * the worker's search-attribute reader are constrained to declared keys\n * with the right value types.\n *\n * @example\n * ```typescript\n * import { defineSearchAttribute } from '@temporal-contract/contract';\n *\n * defineWorkflow({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ status: z.string() }),\n * searchAttributes: {\n * customerId: defineSearchAttribute({ kind: 'KEYWORD' }),\n * priority: defineSearchAttribute({ kind: 'INT' }),\n * placedAt: defineSearchAttribute({ kind: 'DATETIME' }),\n * },\n * });\n * ```\n *\n * The seven Temporal kinds map to TypeScript types like so:\n *\n * | kind | TS type |\n * | --------------- | --------- |\n * | `TEXT` | `string` |\n * | `KEYWORD` | `string` |\n * | `INT` | `number` |\n * | `DOUBLE` | `number` |\n * | `BOOL` | `boolean` |\n * | `DATETIME` | `Date` |\n * | `KEYWORD_LIST` | `string[]`|\n */\nexport function defineSearchAttribute<TKind extends SearchAttributeKind>(\n definition: SearchAttributeDefinition<TKind>,\n): SearchAttributeDefinition<TKind> {\n return definition;\n}\n\n/**\n * Define a Temporal workflow with type-safe input, output, and associated operations.\n *\n * Workflows are durable functions that orchestrate activities, handle timeouts,\n * and manage long-running processes. This function provides type safety for the\n * entire workflow definition including activities, signals, queries, and updates.\n *\n * @template TWorkflow - The workflow definition type with all associated schemas\n * @param definition - The workflow definition containing input, output, and operations\n * @returns The same definition with preserved types for type inference\n *\n * @example\n * ```typescript\n * import { defineWorkflow, defineActivity, defineSignal } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const processOrder = defineWorkflow({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ success: z.boolean() }),\n * activities: {\n * validatePayment: defineActivity({\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ valid: z.boolean() }),\n * }),\n * },\n * signals: {\n * cancel: defineSignal({\n * input: z.object({ reason: z.string() }),\n * }),\n * },\n * });\n * ```\n */\nexport function defineWorkflow<TWorkflow extends WorkflowDefinition>(\n definition: TWorkflow,\n): TWorkflow {\n return definition;\n}\n\n/**\n * Define a complete Temporal contract with type-safe workflows and activities.\n *\n * A contract is the central definition that ties together your Temporal application's\n * workflows and activities. It provides:\n * - Type safety across client, worker, and workflow code\n * - Automatic validation at runtime\n * - Compile-time verification of implementations\n * - Clear API boundaries and documentation\n *\n * The contract validates the structure and ensures:\n * - Task queue is specified\n * - At least one workflow is defined\n * - Valid JavaScript identifiers are used\n * - No conflicts between global and workflow-specific activities\n * - All schemas implement the Standard Schema specification\n *\n * @template TContract - The contract definition type\n * @param definition - The complete contract definition\n * @returns The same definition with preserved types for type inference\n * @throws {Error} If the contract structure is invalid\n *\n * @example\n * ```typescript\n * import { defineContract } from '@temporal-contract/contract';\n * import { z } from 'zod';\n *\n * export const myContract = defineContract({\n * taskQueue: 'orders',\n * workflows: {\n * processOrder: {\n * input: z.object({ orderId: z.string() }),\n * output: z.object({ success: z.boolean() }),\n * activities: {\n * chargePayment: {\n * input: z.object({ amount: z.number() }),\n * output: z.object({ transactionId: z.string() }),\n * },\n * },\n * },\n * },\n * // Optional global activities shared across workflows\n * activities: {\n * logEvent: {\n * input: z.object({ message: z.string() }),\n * output: z.void(),\n * },\n * },\n * });\n * ```\n */\nexport function defineContract<TContract extends ContractDefinition>(\n definition: TContract,\n): TContract {\n // Validate entire contract structure with Zod (including activity conflicts)\n const validationResult = contractValidationSchema.safeParse(definition);\n\n if (!validationResult.success) {\n const cleanMessage = getCleanErrorMessage(validationResult.error);\n throw new Error(`Contract validation failed: ${cleanMessage}`);\n }\n\n return definition;\n}\n\n/**\n * Check if a value is a Standard Schema compatible schema\n */\nfunction isStandardSchema(value: unknown): value is StandardSchemaV1 {\n // Standard Schema can be either an object or a function (e.g., ArkType)\n if (\n (typeof value !== \"object\" && typeof value !== \"function\") ||\n value === null ||\n !(\"~standard\" in value)\n ) {\n return false;\n }\n\n const standard = (value as Record<string, unknown>)[\"~standard\"];\n\n return (\n typeof standard === \"object\" &&\n standard !== null &&\n (standard as Record<string, unknown>)[\"version\"] === 1 &&\n typeof (standard as Record<string, unknown>)[\"validate\"] === \"function\"\n );\n}\n\n/**\n * Schema for validating JavaScript identifiers (workflow names, activity names, etc.)\n * Allows: letters, digits, underscore, dollar sign\n * Must start with: letter, underscore, or dollar sign\n */\nconst identifierSchema = z\n .string()\n .min(1)\n .regex(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, \"must be a valid JavaScript identifier\");\n\n/**\n * Extract a clean, single-line error message from a Zod validation error.\n *\n * Uses `error.issues` directly (compatible with Zod v4+) rather than parsing\n * `error.message` as JSON, which was a Zod v3 implementation detail.\n */\nfunction getCleanErrorMessage(error: z.ZodError): string {\n const issues = error.issues;\n if (!issues || issues.length === 0) {\n return error.message;\n }\n\n const firstIssue = issues[0];\n if (!firstIssue) {\n return error.message;\n }\n\n // For record key validation errors (invalid_key), surface the nested issue message\n if (\n firstIssue.code === \"invalid_key\" &&\n \"issues\" in firstIssue &&\n Array.isArray((firstIssue as { issues?: unknown[] }).issues) &&\n (firstIssue as { issues: { message?: string }[] }).issues.length > 0\n ) {\n const nestedMessage = (firstIssue as { issues: { message?: string }[] }).issues[0]?.message;\n if (nestedMessage) {\n return nestedMessage;\n }\n }\n\n return firstIssue.message ?? error.message;\n}\n\n/**\n * Schema for validating activity definitions\n * Checks that input and output are Standard Schema compatible schemas\n */\nconst activityDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating signal definitions\n */\nconst signalDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating query definitions\n */\nconst queryDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating update definitions\n */\nconst updateDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n});\n\n/**\n * Schema for validating search attribute definitions\n */\nconst searchAttributeKindSchema = z.enum([\n \"TEXT\",\n \"KEYWORD\",\n \"INT\",\n \"DOUBLE\",\n \"BOOL\",\n \"DATETIME\",\n \"KEYWORD_LIST\",\n]);\n\nconst searchAttributeDefinitionSchema = z.object({\n kind: searchAttributeKindSchema,\n});\n\n/**\n * Schema for validating workflow definitions\n */\nconst workflowDefinitionSchema = z.object({\n input: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"input must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n output: z.custom<StandardSchemaV1>((val) => isStandardSchema(val), {\n message: \"output must be a Standard Schema compatible schema (e.g., Zod, Valibot, ArkType)\",\n }),\n activities: z.record(identifierSchema, activityDefinitionSchema).optional(),\n signals: z.record(identifierSchema, signalDefinitionSchema).optional(),\n queries: z.record(identifierSchema, queryDefinitionSchema).optional(),\n updates: z.record(identifierSchema, updateDefinitionSchema).optional(),\n searchAttributes: z.record(identifierSchema, searchAttributeDefinitionSchema).optional(),\n});\n\n/**\n * Schema for validating a contract definition structure\n */\nconst contractValidationSchema = z\n .object({\n taskQueue: z.string().trim().min(1, \"taskQueue cannot be empty\"),\n workflows: z\n .record(identifierSchema, workflowDefinitionSchema)\n .refine((workflows) => Object.keys(workflows).length > 0, {\n message: \"at least one workflow is required\",\n }),\n activities: z.record(identifierSchema, activityDefinitionSchema).optional(),\n })\n .superRefine((contract, ctx) => {\n // Activities are registered in a single flat namespace at runtime, so any\n // duplicate name silently clobbers another. Catch all collisions here:\n // 1. workflow-specific vs. global, and\n // 2. workflow-specific vs. other workflow-specific.\n //\n // The global owner is tracked with a Symbol rather than a sentinel string\n // because workflow names are only validated as JS identifiers — a user\n // could legitimately name a workflow \"global\", and a string sentinel would\n // misclassify those collisions.\n const GLOBAL_OWNER: unique symbol = Symbol(\"global\");\n type Owner = string | typeof GLOBAL_OWNER;\n const owners = new Map<string, Owner>();\n\n if (contract.activities) {\n for (const activityName of Object.keys(contract.activities)) {\n owners.set(activityName, GLOBAL_OWNER);\n }\n }\n\n for (const [workflowName, workflow] of Object.entries(contract.workflows)) {\n if (!workflow.activities) {\n continue;\n }\n for (const activityName of Object.keys(workflow.activities)) {\n const previousOwner = owners.get(activityName);\n if (previousOwner === GLOBAL_OWNER) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `workflow \"${workflowName}\" has activity \"${activityName}\" that conflicts with a global activity. Consider renaming the workflow-specific activity or removing the global activity \"${activityName}\".`,\n });\n continue;\n }\n if (typeof previousOwner === \"string\") {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `workflow \"${workflowName}\" has activity \"${activityName}\" that conflicts with the same-named activity in workflow \"${previousOwner}\". Activities share a single flat namespace at runtime — rename one of them.`,\n });\n continue;\n }\n owners.set(activityName, workflowName);\n }\n }\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temporal-contract/contract",
3
- "version": "0.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "Contract builder for temporal-contract",
5
5
  "keywords": [
6
6
  "contract",
@@ -11,14 +11,20 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/btravers/temporal-contract/issues"
13
13
  },
14
+ "license": "MIT",
15
+ "author": "Benoit TRAVERS <benoit.travers.fr@gmail.com>",
14
16
  "repository": {
15
17
  "type": "git",
16
18
  "url": "https://github.com/btravers/temporal-contract.git",
17
19
  "directory": "packages/contract"
18
20
  },
19
- "license": "MIT",
20
- "author": "Benoit TRAVERS <benoit.travers.fr@gmail.com>",
21
+ "files": [
22
+ "dist"
23
+ ],
21
24
  "type": "module",
25
+ "main": "./dist/index.cjs",
26
+ "module": "./dist/index.mjs",
27
+ "types": "./dist/index.d.mts",
22
28
  "exports": {
23
29
  ".": {
24
30
  "import": {
@@ -32,26 +38,20 @@
32
38
  },
33
39
  "./package.json": "./package.json"
34
40
  },
35
- "main": "./dist/index.cjs",
36
- "module": "./dist/index.mjs",
37
- "types": "./dist/index.d.mts",
38
- "files": [
39
- "dist"
40
- ],
41
41
  "dependencies": {
42
42
  "@standard-schema/spec": "1.1.0",
43
- "zod": "4.3.6"
43
+ "zod": "4.4.3"
44
44
  },
45
45
  "devDependencies": {
46
- "@vitest/coverage-v8": "4.0.18",
47
- "arktype": "2.1.29",
48
- "tsdown": "0.20.3",
49
- "typedoc": "0.28.17",
50
- "typedoc-plugin-markdown": "4.10.0",
51
- "typescript": "5.9.3",
52
- "valibot": "1.2.0",
53
- "vitest": "4.0.18",
54
- "@temporal-contract/tsconfig": "0.2.0",
46
+ "@vitest/coverage-v8": "4.1.5",
47
+ "arktype": "2.2.0",
48
+ "tsdown": "0.21.10",
49
+ "typedoc": "0.28.19",
50
+ "typedoc-plugin-markdown": "4.11.0",
51
+ "typescript": "6.0.3",
52
+ "valibot": "1.3.1",
53
+ "vitest": "4.1.5",
54
+ "@temporal-contract/tsconfig": "1.0.0",
55
55
  "@temporal-contract/typedoc": "0.1.0"
56
56
  },
57
57
  "scripts": {