@temporal-contract/worker 2.3.1 → 3.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/README.md +46 -7
- package/dist/activity.cjs +22 -13
- package/dist/activity.d.cts +15 -13
- package/dist/activity.d.cts.map +1 -1
- package/dist/activity.d.mts +15 -13
- package/dist/activity.d.mts.map +1 -1
- package/dist/activity.mjs +22 -14
- package/dist/activity.mjs.map +1 -1
- package/dist/errors-BNnNzSwE.d.cts +213 -0
- package/dist/errors-BNnNzSwE.d.cts.map +1 -0
- package/dist/errors-BNnNzSwE.d.mts +213 -0
- package/dist/errors-BNnNzSwE.d.mts.map +1 -0
- package/dist/{internal-DcM-YWYX.cjs → internal-Clwokr1z.cjs} +108 -134
- package/dist/{internal-D8Dl9D43.mjs → internal-DqYK4YQK.mjs} +105 -131
- package/dist/internal-DqYK4YQK.mjs.map +1 -0
- package/dist/workflow.cjs +56 -51
- package/dist/workflow.d.cts +59 -40
- package/dist/workflow.d.cts.map +1 -1
- package/dist/workflow.d.mts +59 -40
- package/dist/workflow.d.mts.map +1 -1
- package/dist/workflow.mjs +39 -34
- package/dist/workflow.mjs.map +1 -1
- package/package.json +21 -17
- package/dist/errors-BP48RaOI.d.cts +0 -208
- package/dist/errors-BP48RaOI.d.cts.map +0 -1
- package/dist/errors-BP48RaOI.d.mts +0 -208
- package/dist/errors-BP48RaOI.d.mts.map +0 -1
- package/dist/internal-D8Dl9D43.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,13 +15,13 @@ pnpm add @temporal-contract/worker @temporal-contract/contract @temporalio/workf
|
|
|
15
15
|
```typescript
|
|
16
16
|
// activities.ts
|
|
17
17
|
import { declareActivitiesHandler, ApplicationFailure } from "@temporal-contract/worker/activity";
|
|
18
|
-
import {
|
|
18
|
+
import { fromPromise } from "unthrown";
|
|
19
19
|
|
|
20
20
|
export const activities = declareActivitiesHandler({
|
|
21
21
|
contract: myContract,
|
|
22
22
|
activities: {
|
|
23
23
|
sendEmail: ({ to, body }) =>
|
|
24
|
-
|
|
24
|
+
fromPromise(emailService.send({ to, body }), (error) =>
|
|
25
25
|
ApplicationFailure.create({
|
|
26
26
|
type: "EMAIL_FAILED",
|
|
27
27
|
message: error instanceof Error ? error.message : "Failed to send email",
|
|
@@ -65,7 +65,7 @@ run().catch(console.error);
|
|
|
65
65
|
|
|
66
66
|
### Child Workflows
|
|
67
67
|
|
|
68
|
-
Execute child workflows with type-safe `
|
|
68
|
+
Execute child workflows with type-safe `AsyncResult`. Supports both same-contract and cross-contract child workflows:
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
71
|
// workflows.ts
|
|
@@ -117,13 +117,52 @@ export const parentWorkflow = declareWorkflow({
|
|
|
117
117
|
});
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
### Per-activity options
|
|
121
|
+
|
|
122
|
+
`activityOptions` sets defaults for every activity reachable from the workflow.
|
|
123
|
+
To override options for individual activities, add `activityOptionsByName`. Each
|
|
124
|
+
entry shallow-merges over the defaults — the override wins on every property it
|
|
125
|
+
sets, including the whole nested `retry` block.
|
|
126
|
+
|
|
127
|
+
The override value is Temporal's full `ActivityOptions`, so `taskQueue` is
|
|
128
|
+
available too. That lets you route specific activities to dedicated worker pools
|
|
129
|
+
(e.g. a concurrency-capped queue for LLM calls) while the rest of the workflow
|
|
130
|
+
stays on the default queue — without dropping to a raw `proxyActivities` call,
|
|
131
|
+
which would bypass the Zod input/output validation:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
export const extractLayout = declareWorkflow({
|
|
135
|
+
workflowName: "extractLayout",
|
|
136
|
+
contract: myContract,
|
|
137
|
+
activityOptions: { startToCloseTimeout: "10 minutes" }, // default for all
|
|
138
|
+
activityOptionsByName: {
|
|
139
|
+
// LLM activity → dedicated, concurrency-capped queue.
|
|
140
|
+
extractLayoutChunk: { taskQueue: "gemini-pro" },
|
|
141
|
+
// Slow payment gateway → longer timeout + aggressive retry.
|
|
142
|
+
chargePayment: {
|
|
143
|
+
startToCloseTimeout: "5 minutes",
|
|
144
|
+
retry: { maximumAttempts: 5 },
|
|
145
|
+
},
|
|
146
|
+
// Activities not listed here fall through to the default queue/options.
|
|
147
|
+
},
|
|
148
|
+
implementation: async ({ activities }, input) => {
|
|
149
|
+
// Each call still validates input/output against the contract.
|
|
150
|
+
const layout = await activities.extractLayoutChunk({ docId: input.docId });
|
|
151
|
+
return { layout };
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Activity names are typed against the contract (workflow-local + global), so
|
|
157
|
+
typos surface as TypeScript errors rather than silently running with defaults.
|
|
158
|
+
|
|
120
159
|
## Documentation
|
|
121
160
|
|
|
122
|
-
📖 **[Read the full documentation →](https://
|
|
161
|
+
📖 **[Read the full documentation →](https://btravstack.github.io/temporal-contract)**
|
|
123
162
|
|
|
124
|
-
- [API Reference](https://
|
|
125
|
-
- [Worker Implementation Guide](https://
|
|
126
|
-
- [Examples](https://
|
|
163
|
+
- [API Reference](https://btravstack.github.io/temporal-contract/api/worker)
|
|
164
|
+
- [Worker Implementation Guide](https://btravstack.github.io/temporal-contract/guide/worker-implementation)
|
|
165
|
+
- [Examples](https://btravstack.github.io/temporal-contract/examples/)
|
|
127
166
|
|
|
128
167
|
## License
|
|
129
168
|
|
package/dist/activity.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_internal = require("./internal-
|
|
2
|
+
const require_internal = require("./internal-Clwokr1z.cjs");
|
|
3
3
|
let _temporalio_common = require("@temporalio/common");
|
|
4
4
|
//#region src/activity.ts
|
|
5
5
|
/**
|
|
@@ -7,7 +7,7 @@ let _temporalio_common = require("@temporalio/common");
|
|
|
7
7
|
*
|
|
8
8
|
* This wraps all activity implementations with:
|
|
9
9
|
* - Validation at network boundaries
|
|
10
|
-
* - `
|
|
10
|
+
* - `AsyncResult<T, ApplicationFailure>` pattern for explicit error handling
|
|
11
11
|
* - Automatic conversion from Result to Promise (throwing on Error)
|
|
12
12
|
*
|
|
13
13
|
* TypeScript ensures ALL activities (global + workflow-specific) are implemented.
|
|
@@ -17,15 +17,15 @@ let _temporalio_common = require("@temporalio/common");
|
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';
|
|
20
|
-
* import {
|
|
20
|
+
* import { fromPromise } from 'unthrown';
|
|
21
21
|
* import myContract from './contract';
|
|
22
22
|
*
|
|
23
23
|
* export const activities = declareActivitiesHandler({
|
|
24
24
|
* contract: myContract,
|
|
25
25
|
* activities: {
|
|
26
|
-
* // Activity returns
|
|
26
|
+
* // Activity returns AsyncResult instead of throwing.
|
|
27
27
|
* sendEmail: (args) =>
|
|
28
|
-
*
|
|
28
|
+
* fromPromise(
|
|
29
29
|
* emailService.send(args),
|
|
30
30
|
* (error) =>
|
|
31
31
|
* // Wrap technical errors in ApplicationFailure. `nonRetryable`
|
|
@@ -53,10 +53,11 @@ let _temporalio_common = require("@temporalio/common");
|
|
|
53
53
|
*
|
|
54
54
|
* @remarks
|
|
55
55
|
* The wrapper accepts implementations in the
|
|
56
|
-
* `
|
|
56
|
+
* `AsyncResult<T, ApplicationFailure>` shape and produces ordinary
|
|
57
57
|
* Promise-returning Temporal handlers (`err(...)` → thrown
|
|
58
58
|
* `ApplicationFailure`; `ok(...)` → output validated against the
|
|
59
|
-
* contract and resolved). It does
|
|
59
|
+
* contract and resolved; `defect` → original cause re-thrown). It does
|
|
60
|
+
* **not** hide Temporal's
|
|
60
61
|
* `@temporalio/activity` runtime: inside the body you can still call
|
|
61
62
|
* `Context.current()` from `@temporalio/activity` to access heartbeats
|
|
62
63
|
* (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt
|
|
@@ -72,12 +73,19 @@ function declareActivitiesHandler(options) {
|
|
|
72
73
|
const input = require_internal.extractHandlerInput(args);
|
|
73
74
|
const inputResult = await activityDef.input["~standard"].validate(input);
|
|
74
75
|
if (inputResult.issues) throw new require_internal.ActivityInputValidationError(activityName, inputResult.issues);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
return (await activityImpl(inputResult.value)).match({
|
|
77
|
+
ok: async (value) => {
|
|
78
|
+
const outputResult = await activityDef.output["~standard"].validate(value);
|
|
79
|
+
if (outputResult.issues) throw new require_internal.ActivityOutputValidationError(activityName, outputResult.issues);
|
|
80
|
+
return outputResult.value;
|
|
81
|
+
},
|
|
82
|
+
err: (error) => {
|
|
83
|
+
throw error;
|
|
84
|
+
},
|
|
85
|
+
defect: (cause) => {
|
|
86
|
+
throw cause;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
81
89
|
};
|
|
82
90
|
}
|
|
83
91
|
if (contract.activities) for (const [activityName, impl] of Object.entries(activities)) {
|
|
@@ -108,4 +116,5 @@ Object.defineProperty(exports, "ApplicationFailure", {
|
|
|
108
116
|
return _temporalio_common.ApplicationFailure;
|
|
109
117
|
}
|
|
110
118
|
});
|
|
119
|
+
exports.ValidationError = require_internal.ValidationError;
|
|
111
120
|
exports.declareActivitiesHandler = declareActivitiesHandler;
|
package/dist/activity.d.cts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { n as ActivityInputValidationError, r as ActivityOutputValidationError, t as ActivityDefinitionNotFoundError, v as WorkerInferInput, y as WorkerInferOutput } from "./errors-
|
|
1
|
+
import { f as ValidationError, n as ActivityInputValidationError, r as ActivityOutputValidationError, t as ActivityDefinitionNotFoundError, v as WorkerInferInput, y as WorkerInferOutput } from "./errors-BNnNzSwE.cjs";
|
|
2
2
|
import { ActivityDefinition, ContractDefinition } from "@temporal-contract/contract";
|
|
3
|
-
import {
|
|
3
|
+
import { AsyncResult } from "unthrown";
|
|
4
4
|
import { ApplicationFailure, ApplicationFailure as ApplicationFailure$1 } from "@temporalio/common";
|
|
5
5
|
|
|
6
6
|
//#region src/activity.d.ts
|
|
7
7
|
/**
|
|
8
|
-
* Activity implementation using
|
|
8
|
+
* Activity implementation using unthrown's `AsyncResult`.
|
|
9
9
|
*
|
|
10
|
-
* Returns `
|
|
10
|
+
* Returns `AsyncResult<Output, ApplicationFailure>` for explicit error
|
|
11
11
|
* handling instead of throwing. The wrapper rethrows `err()` payloads at
|
|
12
12
|
* the activity boundary; Temporal recognizes `ApplicationFailure` natively
|
|
13
13
|
* and applies the configured retry policy (with `nonRetryable: true`
|
|
14
|
-
* opting an instance out per-call).
|
|
14
|
+
* opting an instance out per-call). An unexpected throw surfaces as a
|
|
15
|
+
* `defect` and is re-thrown with its original cause.
|
|
15
16
|
*/
|
|
16
|
-
type ResultActivityImplementation<TActivity extends ActivityDefinition> = (args: WorkerInferInput<TActivity>) =>
|
|
17
|
+
type ResultActivityImplementation<TActivity extends ActivityDefinition> = (args: WorkerInferInput<TActivity>) => AsyncResult<WorkerInferOutput<TActivity>, ApplicationFailure$1>;
|
|
17
18
|
/**
|
|
18
19
|
* Map of all activity implementations for a contract (global + all workflow-specific).
|
|
19
20
|
*
|
|
@@ -63,7 +64,7 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
63
64
|
*
|
|
64
65
|
* This wraps all activity implementations with:
|
|
65
66
|
* - Validation at network boundaries
|
|
66
|
-
* - `
|
|
67
|
+
* - `AsyncResult<T, ApplicationFailure>` pattern for explicit error handling
|
|
67
68
|
* - Automatic conversion from Result to Promise (throwing on Error)
|
|
68
69
|
*
|
|
69
70
|
* TypeScript ensures ALL activities (global + workflow-specific) are implemented.
|
|
@@ -73,15 +74,15 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
73
74
|
* @example
|
|
74
75
|
* ```ts
|
|
75
76
|
* import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';
|
|
76
|
-
* import {
|
|
77
|
+
* import { fromPromise } from 'unthrown';
|
|
77
78
|
* import myContract from './contract';
|
|
78
79
|
*
|
|
79
80
|
* export const activities = declareActivitiesHandler({
|
|
80
81
|
* contract: myContract,
|
|
81
82
|
* activities: {
|
|
82
|
-
* // Activity returns
|
|
83
|
+
* // Activity returns AsyncResult instead of throwing.
|
|
83
84
|
* sendEmail: (args) =>
|
|
84
|
-
*
|
|
85
|
+
* fromPromise(
|
|
85
86
|
* emailService.send(args),
|
|
86
87
|
* (error) =>
|
|
87
88
|
* // Wrap technical errors in ApplicationFailure. `nonRetryable`
|
|
@@ -109,10 +110,11 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
109
110
|
*
|
|
110
111
|
* @remarks
|
|
111
112
|
* The wrapper accepts implementations in the
|
|
112
|
-
* `
|
|
113
|
+
* `AsyncResult<T, ApplicationFailure>` shape and produces ordinary
|
|
113
114
|
* Promise-returning Temporal handlers (`err(...)` → thrown
|
|
114
115
|
* `ApplicationFailure`; `ok(...)` → output validated against the
|
|
115
|
-
* contract and resolved). It does
|
|
116
|
+
* contract and resolved; `defect` → original cause re-thrown). It does
|
|
117
|
+
* **not** hide Temporal's
|
|
116
118
|
* `@temporalio/activity` runtime: inside the body you can still call
|
|
117
119
|
* `Context.current()` from `@temporalio/activity` to access heartbeats
|
|
118
120
|
* (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt
|
|
@@ -122,5 +124,5 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
122
124
|
*/
|
|
123
125
|
declare function declareActivitiesHandler<TContract extends ContractDefinition>(options: DeclareActivitiesHandlerOptions<TContract>): ActivitiesHandler<TContract>;
|
|
124
126
|
//#endregion
|
|
125
|
-
export { ActivitiesHandler, ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, declareActivitiesHandler };
|
|
127
|
+
export { ActivitiesHandler, ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, ValidationError, declareActivitiesHandler };
|
|
126
128
|
//# sourceMappingURL=activity.d.cts.map
|
package/dist/activity.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.d.cts","names":[],"sources":["../src/activity.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"activity.d.cts","names":[],"sources":["../src/activity.ts"],"mappings":";;;;;;AAgCwD;;;;;;;;;;AAAA,KAYnD,4BAAA,mBAA+C,kBAAA,KAClD,IAAA,EAAM,gBAAA,CAAiB,SAAA,MACpB,WAAA,CAAY,iBAAA,CAAkB,SAAA,GAAY,oBAAA;;;;;;;;;;;;AAAkB;AAAA;;;;;;;;;;;KAyB5D,uCAAA,mBAA0D,kBAAA,KAE5D,SAAA,uBAAgC,MAAA,SAAe,kBAAA,IAC5C,+BAAA,CAAgC,SAAA,8CAIZ,SAAA,gBAAyB,SAAA,cAAuB,SAAA,wBAAiC,MAAA,SAEnG,kBAAA,IAEE,+BAAA,CAAgC,SAAA,cAAuB,SAAA;AAAA,KAI5D,+BAAA,qBAAoD,MAAA,SAAe,kBAAA,mBAC1D,WAAA,GAAc,4BAAA,CAA6B,WAAA,CAAY,CAAA;;;;KAMhE,+BAAA,mBAAkD,kBAAA;EACrD,QAAA,EAAU,SAAA;EACV,UAAA,EAAY,uCAAA,CAAwC,SAAA;AAAA;AAAA,KAGjD,sBAAA,mBAAyC,kBAAA,KAC5C,IAAA,EAAM,gBAAA,CAAiB,SAAA,MACpB,OAAA,CAAQ,iBAAA,CAAkB,SAAA;AAAA,KAE1B,yBAAA,qBAA8C,MAAA,SAAe,kBAAA,mBACpD,WAAA,GAAc,sBAAA,CAAuB,WAAA,CAAY,CAAA;AAAA,KAG1D,mBAAA,OAA0B,CAAA,oBAAqB,CAAA,EAAG,CAAC,6BACtD,CAAA,sBAEE,CAAA;;;;;;;;AA3BsE;KAsC9D,iBAAA,mBAAoC,kBAAA,KAE7C,SAAA,uBAAgC,MAAA,SAAe,kBAAA,IAC5C,yBAAA,CAA0B,SAAA,wBAG5B,mBAAA,uBAEwB,SAAA,gBAAyB,SAAA,cAAuB,SAAA,wBAAiC,MAAA,SAEnG,kBAAA,IAEE,yBAAA,CAA0B,SAAA,cAAuB,SAAA,8BAE/C,SAAA;;;;;;;;;;;;;;;;;;AA/CwD;AAAA;;;;;;;;;;;;;;;;;AAQP;AAAA;;;;;;;;;;;;;;;;;;;;AAKvB;AAAA;;;;;;iBAoGxB,wBAAA,mBAA2C,kBAAA,EACzD,OAAA,EAAS,+BAAA,CAAgC,SAAA,IACxC,iBAAA,CAAkB,SAAA"}
|
package/dist/activity.d.mts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { n as ActivityInputValidationError, r as ActivityOutputValidationError, t as ActivityDefinitionNotFoundError, v as WorkerInferInput, y as WorkerInferOutput } from "./errors-
|
|
1
|
+
import { f as ValidationError, n as ActivityInputValidationError, r as ActivityOutputValidationError, t as ActivityDefinitionNotFoundError, v as WorkerInferInput, y as WorkerInferOutput } from "./errors-BNnNzSwE.mjs";
|
|
2
2
|
import { ActivityDefinition, ContractDefinition } from "@temporal-contract/contract";
|
|
3
3
|
import { ApplicationFailure, ApplicationFailure as ApplicationFailure$1 } from "@temporalio/common";
|
|
4
|
-
import {
|
|
4
|
+
import { AsyncResult } from "unthrown";
|
|
5
5
|
|
|
6
6
|
//#region src/activity.d.ts
|
|
7
7
|
/**
|
|
8
|
-
* Activity implementation using
|
|
8
|
+
* Activity implementation using unthrown's `AsyncResult`.
|
|
9
9
|
*
|
|
10
|
-
* Returns `
|
|
10
|
+
* Returns `AsyncResult<Output, ApplicationFailure>` for explicit error
|
|
11
11
|
* handling instead of throwing. The wrapper rethrows `err()` payloads at
|
|
12
12
|
* the activity boundary; Temporal recognizes `ApplicationFailure` natively
|
|
13
13
|
* and applies the configured retry policy (with `nonRetryable: true`
|
|
14
|
-
* opting an instance out per-call).
|
|
14
|
+
* opting an instance out per-call). An unexpected throw surfaces as a
|
|
15
|
+
* `defect` and is re-thrown with its original cause.
|
|
15
16
|
*/
|
|
16
|
-
type ResultActivityImplementation<TActivity extends ActivityDefinition> = (args: WorkerInferInput<TActivity>) =>
|
|
17
|
+
type ResultActivityImplementation<TActivity extends ActivityDefinition> = (args: WorkerInferInput<TActivity>) => AsyncResult<WorkerInferOutput<TActivity>, ApplicationFailure$1>;
|
|
17
18
|
/**
|
|
18
19
|
* Map of all activity implementations for a contract (global + all workflow-specific).
|
|
19
20
|
*
|
|
@@ -63,7 +64,7 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
63
64
|
*
|
|
64
65
|
* This wraps all activity implementations with:
|
|
65
66
|
* - Validation at network boundaries
|
|
66
|
-
* - `
|
|
67
|
+
* - `AsyncResult<T, ApplicationFailure>` pattern for explicit error handling
|
|
67
68
|
* - Automatic conversion from Result to Promise (throwing on Error)
|
|
68
69
|
*
|
|
69
70
|
* TypeScript ensures ALL activities (global + workflow-specific) are implemented.
|
|
@@ -73,15 +74,15 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
73
74
|
* @example
|
|
74
75
|
* ```ts
|
|
75
76
|
* import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';
|
|
76
|
-
* import {
|
|
77
|
+
* import { fromPromise } from 'unthrown';
|
|
77
78
|
* import myContract from './contract';
|
|
78
79
|
*
|
|
79
80
|
* export const activities = declareActivitiesHandler({
|
|
80
81
|
* contract: myContract,
|
|
81
82
|
* activities: {
|
|
82
|
-
* // Activity returns
|
|
83
|
+
* // Activity returns AsyncResult instead of throwing.
|
|
83
84
|
* sendEmail: (args) =>
|
|
84
|
-
*
|
|
85
|
+
* fromPromise(
|
|
85
86
|
* emailService.send(args),
|
|
86
87
|
* (error) =>
|
|
87
88
|
* // Wrap technical errors in ApplicationFailure. `nonRetryable`
|
|
@@ -109,10 +110,11 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
109
110
|
*
|
|
110
111
|
* @remarks
|
|
111
112
|
* The wrapper accepts implementations in the
|
|
112
|
-
* `
|
|
113
|
+
* `AsyncResult<T, ApplicationFailure>` shape and produces ordinary
|
|
113
114
|
* Promise-returning Temporal handlers (`err(...)` → thrown
|
|
114
115
|
* `ApplicationFailure`; `ok(...)` → output validated against the
|
|
115
|
-
* contract and resolved). It does
|
|
116
|
+
* contract and resolved; `defect` → original cause re-thrown). It does
|
|
117
|
+
* **not** hide Temporal's
|
|
116
118
|
* `@temporalio/activity` runtime: inside the body you can still call
|
|
117
119
|
* `Context.current()` from `@temporalio/activity` to access heartbeats
|
|
118
120
|
* (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt
|
|
@@ -122,5 +124,5 @@ type ActivitiesHandler<TContract extends ContractDefinition> = (TContract["activ
|
|
|
122
124
|
*/
|
|
123
125
|
declare function declareActivitiesHandler<TContract extends ContractDefinition>(options: DeclareActivitiesHandlerOptions<TContract>): ActivitiesHandler<TContract>;
|
|
124
126
|
//#endregion
|
|
125
|
-
export { ActivitiesHandler, ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, declareActivitiesHandler };
|
|
127
|
+
export { ActivitiesHandler, ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, ValidationError, declareActivitiesHandler };
|
|
126
128
|
//# sourceMappingURL=activity.d.mts.map
|
package/dist/activity.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.d.mts","names":[],"sources":["../src/activity.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"activity.d.mts","names":[],"sources":["../src/activity.ts"],"mappings":";;;;;;AAgCwD;;;;;;;;;;AAAA,KAYnD,4BAAA,mBAA+C,kBAAA,KAClD,IAAA,EAAM,gBAAA,CAAiB,SAAA,MACpB,WAAA,CAAY,iBAAA,CAAkB,SAAA,GAAY,oBAAA;;;;;;;;;;;;AAAkB;AAAA;;;;;;;;;;;KAyB5D,uCAAA,mBAA0D,kBAAA,KAE5D,SAAA,uBAAgC,MAAA,SAAe,kBAAA,IAC5C,+BAAA,CAAgC,SAAA,8CAIZ,SAAA,gBAAyB,SAAA,cAAuB,SAAA,wBAAiC,MAAA,SAEnG,kBAAA,IAEE,+BAAA,CAAgC,SAAA,cAAuB,SAAA;AAAA,KAI5D,+BAAA,qBAAoD,MAAA,SAAe,kBAAA,mBAC1D,WAAA,GAAc,4BAAA,CAA6B,WAAA,CAAY,CAAA;;;;KAMhE,+BAAA,mBAAkD,kBAAA;EACrD,QAAA,EAAU,SAAA;EACV,UAAA,EAAY,uCAAA,CAAwC,SAAA;AAAA;AAAA,KAGjD,sBAAA,mBAAyC,kBAAA,KAC5C,IAAA,EAAM,gBAAA,CAAiB,SAAA,MACpB,OAAA,CAAQ,iBAAA,CAAkB,SAAA;AAAA,KAE1B,yBAAA,qBAA8C,MAAA,SAAe,kBAAA,mBACpD,WAAA,GAAc,sBAAA,CAAuB,WAAA,CAAY,CAAA;AAAA,KAG1D,mBAAA,OAA0B,CAAA,oBAAqB,CAAA,EAAG,CAAC,6BACtD,CAAA,sBAEE,CAAA;;;;;;;;AA3BsE;KAsC9D,iBAAA,mBAAoC,kBAAA,KAE7C,SAAA,uBAAgC,MAAA,SAAe,kBAAA,IAC5C,yBAAA,CAA0B,SAAA,wBAG5B,mBAAA,uBAEwB,SAAA,gBAAyB,SAAA,cAAuB,SAAA,wBAAiC,MAAA,SAEnG,kBAAA,IAEE,yBAAA,CAA0B,SAAA,cAAuB,SAAA,8BAE/C,SAAA;;;;;;;;;;;;;;;;;;AA/CwD;AAAA;;;;;;;;;;;;;;;;;AAQP;AAAA;;;;;;;;;;;;;;;;;;;;AAKvB;AAAA;;;;;;iBAoGxB,wBAAA,mBAA2C,kBAAA,EACzD,OAAA,EAAS,+BAAA,CAAgC,SAAA,IACxC,iBAAA,CAAkB,SAAA"}
|
package/dist/activity.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as extractHandlerInput, c as ActivityDefinitionNotFoundError, l as ActivityInputValidationError, u as ActivityOutputValidationError, y as ValidationError } from "./internal-DqYK4YQK.mjs";
|
|
2
2
|
import { ApplicationFailure } from "@temporalio/common";
|
|
3
3
|
//#region src/activity.ts
|
|
4
4
|
/**
|
|
@@ -6,7 +6,7 @@ import { ApplicationFailure } from "@temporalio/common";
|
|
|
6
6
|
*
|
|
7
7
|
* This wraps all activity implementations with:
|
|
8
8
|
* - Validation at network boundaries
|
|
9
|
-
* - `
|
|
9
|
+
* - `AsyncResult<T, ApplicationFailure>` pattern for explicit error handling
|
|
10
10
|
* - Automatic conversion from Result to Promise (throwing on Error)
|
|
11
11
|
*
|
|
12
12
|
* TypeScript ensures ALL activities (global + workflow-specific) are implemented.
|
|
@@ -16,15 +16,15 @@ import { ApplicationFailure } from "@temporalio/common";
|
|
|
16
16
|
* @example
|
|
17
17
|
* ```ts
|
|
18
18
|
* import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';
|
|
19
|
-
* import {
|
|
19
|
+
* import { fromPromise } from 'unthrown';
|
|
20
20
|
* import myContract from './contract';
|
|
21
21
|
*
|
|
22
22
|
* export const activities = declareActivitiesHandler({
|
|
23
23
|
* contract: myContract,
|
|
24
24
|
* activities: {
|
|
25
|
-
* // Activity returns
|
|
25
|
+
* // Activity returns AsyncResult instead of throwing.
|
|
26
26
|
* sendEmail: (args) =>
|
|
27
|
-
*
|
|
27
|
+
* fromPromise(
|
|
28
28
|
* emailService.send(args),
|
|
29
29
|
* (error) =>
|
|
30
30
|
* // Wrap technical errors in ApplicationFailure. `nonRetryable`
|
|
@@ -52,10 +52,11 @@ import { ApplicationFailure } from "@temporalio/common";
|
|
|
52
52
|
*
|
|
53
53
|
* @remarks
|
|
54
54
|
* The wrapper accepts implementations in the
|
|
55
|
-
* `
|
|
55
|
+
* `AsyncResult<T, ApplicationFailure>` shape and produces ordinary
|
|
56
56
|
* Promise-returning Temporal handlers (`err(...)` → thrown
|
|
57
57
|
* `ApplicationFailure`; `ok(...)` → output validated against the
|
|
58
|
-
* contract and resolved). It does
|
|
58
|
+
* contract and resolved; `defect` → original cause re-thrown). It does
|
|
59
|
+
* **not** hide Temporal's
|
|
59
60
|
* `@temporalio/activity` runtime: inside the body you can still call
|
|
60
61
|
* `Context.current()` from `@temporalio/activity` to access heartbeats
|
|
61
62
|
* (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt
|
|
@@ -71,12 +72,19 @@ function declareActivitiesHandler(options) {
|
|
|
71
72
|
const input = extractHandlerInput(args);
|
|
72
73
|
const inputResult = await activityDef.input["~standard"].validate(input);
|
|
73
74
|
if (inputResult.issues) throw new ActivityInputValidationError(activityName, inputResult.issues);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
return (await activityImpl(inputResult.value)).match({
|
|
76
|
+
ok: async (value) => {
|
|
77
|
+
const outputResult = await activityDef.output["~standard"].validate(value);
|
|
78
|
+
if (outputResult.issues) throw new ActivityOutputValidationError(activityName, outputResult.issues);
|
|
79
|
+
return outputResult.value;
|
|
80
|
+
},
|
|
81
|
+
err: (error) => {
|
|
82
|
+
throw error;
|
|
83
|
+
},
|
|
84
|
+
defect: (cause) => {
|
|
85
|
+
throw cause;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
80
88
|
};
|
|
81
89
|
}
|
|
82
90
|
if (contract.activities) for (const [activityName, impl] of Object.entries(activities)) {
|
|
@@ -98,6 +106,6 @@ function declareActivitiesHandler(options) {
|
|
|
98
106
|
return wrappedActivities;
|
|
99
107
|
}
|
|
100
108
|
//#endregion
|
|
101
|
-
export { ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, declareActivitiesHandler };
|
|
109
|
+
export { ActivityDefinitionNotFoundError, ActivityInputValidationError, ActivityOutputValidationError, ApplicationFailure, ValidationError, declareActivitiesHandler };
|
|
102
110
|
|
|
103
111
|
//# sourceMappingURL=activity.mjs.map
|
package/dist/activity.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.mjs","names":[],"sources":["../src/activity.ts"],"sourcesContent":["// Entry point for activity implementations.\n//\n// Activities run *outside* the workflow sandbox, so they use neverthrow's\n// `ResultAsync` directly. Workflow code (see workflow.ts) uses the same\n// neverthrow API — neverthrow's evaluation is compatible with Temporal's\n// deterministic replay machinery.\n//\n// Errors flow through Temporal's `ApplicationFailure` (re-exported from\n// `@temporalio/common`) — it's the SDK's first-class failure shape, so we\n// don't wrap it in a custom class. `ApplicationFailure` exposes\n// `nonRetryable`, `type`, `details`, and `category` natively, and survives\n// the activity → workflow serialization boundary unchanged.\nimport { ActivityDefinition, ContractDefinition } from \"@temporal-contract/contract\";\nimport type { ResultAsync } from \"neverthrow\";\nimport { ApplicationFailure } from \"@temporalio/common\";\nimport { WorkerInferInput, WorkerInferOutput } from \"./types.js\";\nimport {\n ActivityDefinitionNotFoundError,\n ActivityInputValidationError,\n ActivityOutputValidationError,\n} from \"./errors.js\";\nimport { extractHandlerInput } from \"./internal.js\";\n\nexport {\n ActivityDefinitionNotFoundError,\n ActivityInputValidationError,\n ActivityOutputValidationError,\n} from \"./errors.js\";\n\n// Re-export the canonical activity-failure class so consumers don't need\n// a separate `@temporalio/common` import to construct one.\nexport { ApplicationFailure } from \"@temporalio/common\";\n\n/**\n * Activity implementation using neverthrow's `ResultAsync`.\n *\n * Returns `ResultAsync<Output, ApplicationFailure>` for explicit error\n * handling instead of throwing. The wrapper rethrows `err()` payloads at\n * the activity boundary; Temporal recognizes `ApplicationFailure` natively\n * and applies the configured retry policy (with `nonRetryable: true`\n * opting an instance out per-call).\n */\ntype ResultActivityImplementation<TActivity extends ActivityDefinition> = (\n args: WorkerInferInput<TActivity>,\n) => ResultAsync<WorkerInferOutput<TActivity>, ApplicationFailure>;\n\n/**\n * Map of all activity implementations for a contract (global + all workflow-specific).\n *\n * **Shape note — input is nested by workflow, output is flat.** This\n * asymmetry is deliberate:\n *\n * - The implementation map you write **mirrors the contract's structure**:\n * global activities sit at the root, workflow-local activities nest\n * under their owning workflow's name. Mirroring the contract gives\n * IDE autocomplete that matches `defineContract`, prevents typos that\n * put a workflow-local activity at the global level, and makes\n * ownership visible at definition time.\n * - The handler returned by {@link declareActivitiesHandler} (see\n * {@link ActivitiesHandler}) is **flat** because Temporal's worker\n * sees a single activity namespace at runtime —\n * `proxyActivities<...>()` resolves names from one map regardless of\n * which workflow consumes them. `defineContract` enforces no name\n * collisions across global + workflow-local scopes, so the flat\n * output has no ambiguity to resolve.\n *\n * In short: write nested (mirror the contract); the wrapper flattens\n * for Temporal.\n */\ntype ContractResultActivitiesImplementations<TContract extends ContractDefinition> =\n // Global activities\n (TContract[\"activities\"] extends Record<string, ActivityDefinition>\n ? ResultActivitiesImplementations<TContract[\"activities\"]>\n : {}) &\n // All workflow-specific activities merged\n {\n [TWorkflow in keyof TContract[\"workflows\"]]: TContract[\"workflows\"][TWorkflow][\"activities\"] extends Record<\n string,\n ActivityDefinition\n >\n ? ResultActivitiesImplementations<TContract[\"workflows\"][TWorkflow][\"activities\"]>\n : {};\n };\n\ntype ResultActivitiesImplementations<TActivities extends Record<string, ActivityDefinition>> = {\n [K in keyof TActivities]: ResultActivityImplementation<TActivities[K]>;\n};\n\n/**\n * Options for creating activities handler\n */\ntype DeclareActivitiesHandlerOptions<TContract extends ContractDefinition> = {\n contract: TContract;\n activities: ContractResultActivitiesImplementations<TContract>;\n};\n\ntype ActivityImplementation<TActivity extends ActivityDefinition> = (\n args: WorkerInferInput<TActivity>,\n) => Promise<WorkerInferOutput<TActivity>>;\n\ntype ActivitiesImplementations<TActivities extends Record<string, ActivityDefinition>> = {\n [K in keyof TActivities]: ActivityImplementation<TActivities[K]>;\n};\n\ntype UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\n/**\n * Activities handler ready for Temporal's `Worker.create({ activities })`.\n *\n * Flat shape: every activity (global + all workflow-local) lives at the\n * root of the returned map. See the doc comment on\n * {@link ContractResultActivitiesImplementations} for why the input you\n * write is nested by workflow while this output is flat.\n */\nexport type ActivitiesHandler<TContract extends ContractDefinition> =\n // Global activities\n (TContract[\"activities\"] extends Record<string, ActivityDefinition>\n ? ActivitiesImplementations<TContract[\"activities\"]>\n : {}) &\n // All workflow-specific activities merged at root level (flat)\n UnionToIntersection<\n {\n [TWorkflow in keyof TContract[\"workflows\"]]: TContract[\"workflows\"][TWorkflow][\"activities\"] extends Record<\n string,\n ActivityDefinition\n >\n ? ActivitiesImplementations<TContract[\"workflows\"][TWorkflow][\"activities\"]>\n : {};\n }[keyof TContract[\"workflows\"]]\n >;\n\n/**\n * Create a typed activities handler with automatic validation and Result pattern.\n *\n * This wraps all activity implementations with:\n * - Validation at network boundaries\n * - `ResultAsync<T, ApplicationFailure>` pattern for explicit error handling\n * - Automatic conversion from Result to Promise (throwing on Error)\n *\n * TypeScript ensures ALL activities (global + workflow-specific) are implemented.\n *\n * Use this to create the activities object for the Temporal Worker.\n *\n * @example\n * ```ts\n * import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';\n * import { ResultAsync, errAsync, okAsync } from 'neverthrow';\n * import myContract from './contract';\n *\n * export const activities = declareActivitiesHandler({\n * contract: myContract,\n * activities: {\n * // Activity returns ResultAsync instead of throwing.\n * sendEmail: (args) =>\n * ResultAsync.fromPromise(\n * emailService.send(args),\n * (error) =>\n * // Wrap technical errors in ApplicationFailure. `nonRetryable`\n * // is per-instance: set it to true on permanent failures so\n * // Temporal stops retrying immediately.\n * ApplicationFailure.create({\n * type: 'EMAIL_SEND_FAILED',\n * message: 'Failed to send email',\n * nonRetryable: false,\n * cause: error instanceof Error ? error : undefined,\n * }),\n * ).map(() => ({ sent: true })),\n * },\n * });\n *\n * // Use with Temporal Worker\n * import { Worker } from '@temporalio/worker';\n *\n * const worker = await Worker.create({\n * workflowsPath: require.resolve('./workflows'),\n * activities: activities,\n * taskQueue: contract.taskQueue,\n * });\n * ```\n *\n * @remarks\n * The wrapper accepts implementations in the\n * `ResultAsync<T, ApplicationFailure>` shape and produces ordinary\n * Promise-returning Temporal handlers (`err(...)` → thrown\n * `ApplicationFailure`; `ok(...)` → output validated against the\n * contract and resolved). It does **not** hide Temporal's\n * `@temporalio/activity` runtime: inside the body you can still call\n * `Context.current()` from `@temporalio/activity` to access heartbeats\n * (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt\n * number, workflow IDs), and the async-completion task token. See the\n * \"Working with the Activity Context\" section of the worker\n * implementation guide for end-to-end examples.\n */\nexport function declareActivitiesHandler<TContract extends ContractDefinition>(\n options: DeclareActivitiesHandlerOptions<TContract>,\n): ActivitiesHandler<TContract> {\n const { contract, activities } = options;\n\n // Prepare Temporal-compatible activities with validation and Result unwrapping\n const wrappedActivities = {} as ActivitiesHandler<TContract>;\n\n // Helper to create a wrapped implementation from a definition and impl\n function makeWrapped(\n activityName: string,\n activityDef: ActivityDefinition,\n activityImpl: (args: unknown) => ResultAsync<unknown, ApplicationFailure>,\n ) {\n return async (...args: unknown[]) => {\n const input = extractHandlerInput(args);\n\n // Validate input\n const inputResult = await activityDef.input[\"~standard\"].validate(input);\n if (inputResult.issues) {\n throw new ActivityInputValidationError(activityName, inputResult.issues);\n }\n\n // Execute neverthrow activity (returns ResultAsync<T, ApplicationFailure>);\n // awaiting yields a Result<T, ApplicationFailure>.\n const result = await activityImpl(inputResult.value);\n\n // Process result: validate output or throw error\n if (result.isOk()) {\n // Validate output on success\n const outputResult = await activityDef.output[\"~standard\"].validate(result.value);\n if (outputResult.issues) {\n throw new ActivityOutputValidationError(activityName, outputResult.issues);\n }\n return outputResult.value;\n } else {\n // Convert err(...) payload to thrown ApplicationFailure for Temporal.\n // Temporal recognizes this class natively and applies the\n // configured retry policy (honoring `nonRetryable: true`).\n throw result.error;\n }\n };\n }\n\n // 1) Wrap global activities defined directly under contract.activities\n if (contract.activities) {\n for (const [activityName, impl] of Object.entries(activities)) {\n // Skip workflow namespaces if present at root\n if (contract.workflows && activityName in contract.workflows) {\n continue;\n }\n\n const activityDef = contract.activities[activityName];\n if (!activityDef) {\n throw new ActivityDefinitionNotFoundError(activityName, Object.keys(contract.activities));\n }\n\n // Assign wrapped global activity\n (wrappedActivities as Record<string, unknown>)[activityName] = makeWrapped(\n activityName,\n activityDef,\n impl as (args: unknown) => ResultAsync<unknown, ApplicationFailure>,\n );\n }\n }\n\n // 2) Wrap workflow-scoped activities at root level (flat)\n if (contract.workflows) {\n for (const [workflowName, workflowDef] of Object.entries(contract.workflows)) {\n const wfActivitiesImpl = (activities as Record<string, unknown>)[workflowName] as\n | Record<string, unknown>\n | undefined;\n if (!wfActivitiesImpl) {\n // If no implementations provided for this workflow, skip (TypeScript typing should enforce completeness for declared ones)\n continue;\n }\n\n const wfDefs = workflowDef.activities ?? {};\n\n for (const [activityName, impl] of Object.entries(wfActivitiesImpl)) {\n const activityDef = wfDefs[activityName];\n if (!activityDef) {\n throw new ActivityDefinitionNotFoundError(\n `${workflowName}.${activityName}`,\n Object.keys(wfDefs),\n );\n }\n\n // Assign workflow activity directly at root level (flat structure)\n (wrappedActivities as Record<string, unknown>)[activityName] = makeWrapped(\n `${workflowName}.${activityName}`,\n activityDef,\n impl as (args: unknown) => ResultAsync<unknown, ApplicationFailure>,\n );\n }\n }\n }\n\n return wrappedActivities;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqMA,SAAgB,yBACd,SAC8B;CAC9B,MAAM,EAAE,UAAU,eAAe;CAGjC,MAAM,oBAAoB,CAAC;CAG3B,SAAS,YACP,cACA,aACA,cACA;EACA,OAAO,OAAO,GAAG,SAAoB;GACnC,MAAM,QAAQ,oBAAoB,IAAI;GAGtC,MAAM,cAAc,MAAM,YAAY,MAAM,YAAY,CAAC,SAAS,KAAK;GACvE,IAAI,YAAY,QACd,MAAM,IAAI,6BAA6B,cAAc,YAAY,MAAM;GAKzE,MAAM,SAAS,MAAM,aAAa,YAAY,KAAK;GAGnD,IAAI,OAAO,KAAK,GAAG;IAEjB,MAAM,eAAe,MAAM,YAAY,OAAO,YAAY,CAAC,SAAS,OAAO,KAAK;IAChF,IAAI,aAAa,QACf,MAAM,IAAI,8BAA8B,cAAc,aAAa,MAAM;IAE3E,OAAO,aAAa;GACtB,OAIE,MAAM,OAAO;EAEjB;CACF;CAGA,IAAI,SAAS,YACX,KAAK,MAAM,CAAC,cAAc,SAAS,OAAO,QAAQ,UAAU,GAAG;EAE7D,IAAI,SAAS,aAAa,gBAAgB,SAAS,WACjD;EAGF,MAAM,cAAc,SAAS,WAAW;EACxC,IAAI,CAAC,aACH,MAAM,IAAI,gCAAgC,cAAc,OAAO,KAAK,SAAS,UAAU,CAAC;EAI1F,kBAA+C,gBAAgB,YAC7D,cACA,aACA,IACF;CACF;CAIF,IAAI,SAAS,WACX,KAAK,MAAM,CAAC,cAAc,gBAAgB,OAAO,QAAQ,SAAS,SAAS,GAAG;EAC5E,MAAM,mBAAoB,WAAuC;EAGjE,IAAI,CAAC,kBAEH;EAGF,MAAM,SAAS,YAAY,cAAc,CAAC;EAE1C,KAAK,MAAM,CAAC,cAAc,SAAS,OAAO,QAAQ,gBAAgB,GAAG;GACnE,MAAM,cAAc,OAAO;GAC3B,IAAI,CAAC,aACH,MAAM,IAAI,gCACR,GAAG,aAAa,GAAG,gBACnB,OAAO,KAAK,MAAM,CACpB;GAIF,kBAA+C,gBAAgB,YAC7D,GAAG,aAAa,GAAG,gBACnB,aACA,IACF;EACF;CACF;CAGF,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"activity.mjs","names":[],"sources":["../src/activity.ts"],"sourcesContent":["// Entry point for activity implementations.\n//\n// Activities run *outside* the workflow sandbox, so they use unthrown's\n// `AsyncResult` directly. Workflow code (see workflow.ts) uses the same\n// unthrown API — unthrown's evaluation is compatible with Temporal's\n// deterministic replay machinery.\n//\n// Errors flow through Temporal's `ApplicationFailure` (re-exported from\n// `@temporalio/common`) — it's the SDK's first-class failure shape, so we\n// don't wrap it in a custom class. `ApplicationFailure` exposes\n// `nonRetryable`, `type`, `details`, and `category` natively, and survives\n// the activity → workflow serialization boundary unchanged.\nimport { ActivityDefinition, ContractDefinition } from \"@temporal-contract/contract\";\nimport type { AsyncResult } from \"unthrown\";\nimport { ApplicationFailure } from \"@temporalio/common\";\nimport { WorkerInferInput, WorkerInferOutput } from \"./types.js\";\nimport {\n ActivityDefinitionNotFoundError,\n ActivityInputValidationError,\n ActivityOutputValidationError,\n} from \"./errors.js\";\nimport { extractHandlerInput } from \"./internal.js\";\n\nexport {\n ActivityDefinitionNotFoundError,\n ActivityInputValidationError,\n ActivityOutputValidationError,\n ValidationError,\n} from \"./errors.js\";\n\n// Re-export the canonical activity-failure class so consumers don't need\n// a separate `@temporalio/common` import to construct one.\nexport { ApplicationFailure } from \"@temporalio/common\";\n\n/**\n * Activity implementation using unthrown's `AsyncResult`.\n *\n * Returns `AsyncResult<Output, ApplicationFailure>` for explicit error\n * handling instead of throwing. The wrapper rethrows `err()` payloads at\n * the activity boundary; Temporal recognizes `ApplicationFailure` natively\n * and applies the configured retry policy (with `nonRetryable: true`\n * opting an instance out per-call). An unexpected throw surfaces as a\n * `defect` and is re-thrown with its original cause.\n */\ntype ResultActivityImplementation<TActivity extends ActivityDefinition> = (\n args: WorkerInferInput<TActivity>,\n) => AsyncResult<WorkerInferOutput<TActivity>, ApplicationFailure>;\n\n/**\n * Map of all activity implementations for a contract (global + all workflow-specific).\n *\n * **Shape note — input is nested by workflow, output is flat.** This\n * asymmetry is deliberate:\n *\n * - The implementation map you write **mirrors the contract's structure**:\n * global activities sit at the root, workflow-local activities nest\n * under their owning workflow's name. Mirroring the contract gives\n * IDE autocomplete that matches `defineContract`, prevents typos that\n * put a workflow-local activity at the global level, and makes\n * ownership visible at definition time.\n * - The handler returned by {@link declareActivitiesHandler} (see\n * {@link ActivitiesHandler}) is **flat** because Temporal's worker\n * sees a single activity namespace at runtime —\n * `proxyActivities<...>()` resolves names from one map regardless of\n * which workflow consumes them. `defineContract` enforces no name\n * collisions across global + workflow-local scopes, so the flat\n * output has no ambiguity to resolve.\n *\n * In short: write nested (mirror the contract); the wrapper flattens\n * for Temporal.\n */\ntype ContractResultActivitiesImplementations<TContract extends ContractDefinition> =\n // Global activities\n (TContract[\"activities\"] extends Record<string, ActivityDefinition>\n ? ResultActivitiesImplementations<TContract[\"activities\"]>\n : {}) &\n // All workflow-specific activities merged\n {\n [TWorkflow in keyof TContract[\"workflows\"]]: TContract[\"workflows\"][TWorkflow][\"activities\"] extends Record<\n string,\n ActivityDefinition\n >\n ? ResultActivitiesImplementations<TContract[\"workflows\"][TWorkflow][\"activities\"]>\n : {};\n };\n\ntype ResultActivitiesImplementations<TActivities extends Record<string, ActivityDefinition>> = {\n [K in keyof TActivities]: ResultActivityImplementation<TActivities[K]>;\n};\n\n/**\n * Options for creating activities handler\n */\ntype DeclareActivitiesHandlerOptions<TContract extends ContractDefinition> = {\n contract: TContract;\n activities: ContractResultActivitiesImplementations<TContract>;\n};\n\ntype ActivityImplementation<TActivity extends ActivityDefinition> = (\n args: WorkerInferInput<TActivity>,\n) => Promise<WorkerInferOutput<TActivity>>;\n\ntype ActivitiesImplementations<TActivities extends Record<string, ActivityDefinition>> = {\n [K in keyof TActivities]: ActivityImplementation<TActivities[K]>;\n};\n\ntype UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\n/**\n * Activities handler ready for Temporal's `Worker.create({ activities })`.\n *\n * Flat shape: every activity (global + all workflow-local) lives at the\n * root of the returned map. See the doc comment on\n * {@link ContractResultActivitiesImplementations} for why the input you\n * write is nested by workflow while this output is flat.\n */\nexport type ActivitiesHandler<TContract extends ContractDefinition> =\n // Global activities\n (TContract[\"activities\"] extends Record<string, ActivityDefinition>\n ? ActivitiesImplementations<TContract[\"activities\"]>\n : {}) &\n // All workflow-specific activities merged at root level (flat)\n UnionToIntersection<\n {\n [TWorkflow in keyof TContract[\"workflows\"]]: TContract[\"workflows\"][TWorkflow][\"activities\"] extends Record<\n string,\n ActivityDefinition\n >\n ? ActivitiesImplementations<TContract[\"workflows\"][TWorkflow][\"activities\"]>\n : {};\n }[keyof TContract[\"workflows\"]]\n >;\n\n/**\n * Create a typed activities handler with automatic validation and Result pattern.\n *\n * This wraps all activity implementations with:\n * - Validation at network boundaries\n * - `AsyncResult<T, ApplicationFailure>` pattern for explicit error handling\n * - Automatic conversion from Result to Promise (throwing on Error)\n *\n * TypeScript ensures ALL activities (global + workflow-specific) are implemented.\n *\n * Use this to create the activities object for the Temporal Worker.\n *\n * @example\n * ```ts\n * import { declareActivitiesHandler, ApplicationFailure } from '@temporal-contract/worker/activity';\n * import { fromPromise } from 'unthrown';\n * import myContract from './contract';\n *\n * export const activities = declareActivitiesHandler({\n * contract: myContract,\n * activities: {\n * // Activity returns AsyncResult instead of throwing.\n * sendEmail: (args) =>\n * fromPromise(\n * emailService.send(args),\n * (error) =>\n * // Wrap technical errors in ApplicationFailure. `nonRetryable`\n * // is per-instance: set it to true on permanent failures so\n * // Temporal stops retrying immediately.\n * ApplicationFailure.create({\n * type: 'EMAIL_SEND_FAILED',\n * message: 'Failed to send email',\n * nonRetryable: false,\n * cause: error instanceof Error ? error : undefined,\n * }),\n * ).map(() => ({ sent: true })),\n * },\n * });\n *\n * // Use with Temporal Worker\n * import { Worker } from '@temporalio/worker';\n *\n * const worker = await Worker.create({\n * workflowsPath: require.resolve('./workflows'),\n * activities: activities,\n * taskQueue: contract.taskQueue,\n * });\n * ```\n *\n * @remarks\n * The wrapper accepts implementations in the\n * `AsyncResult<T, ApplicationFailure>` shape and produces ordinary\n * Promise-returning Temporal handlers (`err(...)` → thrown\n * `ApplicationFailure`; `ok(...)` → output validated against the\n * contract and resolved; `defect` → original cause re-thrown). It does\n * **not** hide Temporal's\n * `@temporalio/activity` runtime: inside the body you can still call\n * `Context.current()` from `@temporalio/activity` to access heartbeats\n * (`heartbeat(details)`, `heartbeatDetails`), activity info (attempt\n * number, workflow IDs), and the async-completion task token. See the\n * \"Working with the Activity Context\" section of the worker\n * implementation guide for end-to-end examples.\n */\nexport function declareActivitiesHandler<TContract extends ContractDefinition>(\n options: DeclareActivitiesHandlerOptions<TContract>,\n): ActivitiesHandler<TContract> {\n const { contract, activities } = options;\n\n // Prepare Temporal-compatible activities with validation and Result unwrapping\n const wrappedActivities = {} as ActivitiesHandler<TContract>;\n\n // Helper to create a wrapped implementation from a definition and impl\n function makeWrapped(\n activityName: string,\n activityDef: ActivityDefinition,\n activityImpl: (args: unknown) => AsyncResult<unknown, ApplicationFailure>,\n ) {\n return async (...args: unknown[]) => {\n const input = extractHandlerInput(args);\n\n // Validate input\n const inputResult = await activityDef.input[\"~standard\"].validate(input);\n if (inputResult.issues) {\n throw new ActivityInputValidationError(activityName, inputResult.issues);\n }\n\n // Execute unthrown activity (returns AsyncResult<T, ApplicationFailure>);\n // awaiting yields a Result<T, ApplicationFailure>.\n const result = await activityImpl(inputResult.value);\n\n // Fold the three channels: validate output on `ok`, surface the modeled\n // `ApplicationFailure` on `err`, and re-throw a `defect`'s original cause\n // (an unexpected throw inside the activity is a bug, not a domain error).\n return result.match({\n ok: async (value) => {\n const outputResult = await activityDef.output[\"~standard\"].validate(value);\n if (outputResult.issues) {\n throw new ActivityOutputValidationError(activityName, outputResult.issues);\n }\n return outputResult.value;\n },\n // Convert err(...) payload to thrown ApplicationFailure for Temporal.\n // Temporal recognizes this class natively and applies the configured\n // retry policy (honoring `nonRetryable: true`).\n err: (error) => {\n throw error;\n },\n // A defect is an *unanticipated* throw inside the activity. Re-throw the\n // original cause unwrapped: Temporal wraps a non-`ApplicationFailure`\n // error as `ApplicationFailure(type: \"Error\")` and applies the default\n // (retryable) policy — preserving the pre-unthrown behaviour where an\n // uncaught activity throw was simply retried. We deliberately do NOT\n // coerce it to `nonRetryable`: not every unexpected throw is permanent\n // (a transient I/O fault is also \"unmodeled\"), and forcing fail-fast\n // here would silently change retry semantics. An activity that wants a\n // permanent failure should return `err(ApplicationFailure.create({\n // nonRetryable: true }))` explicitly.\n defect: (cause) => {\n throw cause;\n },\n });\n };\n }\n\n // 1) Wrap global activities defined directly under contract.activities\n if (contract.activities) {\n for (const [activityName, impl] of Object.entries(activities)) {\n // Skip workflow namespaces if present at root\n if (contract.workflows && activityName in contract.workflows) {\n continue;\n }\n\n const activityDef = contract.activities[activityName];\n if (!activityDef) {\n throw new ActivityDefinitionNotFoundError(activityName, Object.keys(contract.activities));\n }\n\n // Assign wrapped global activity\n (wrappedActivities as Record<string, unknown>)[activityName] = makeWrapped(\n activityName,\n activityDef,\n impl as (args: unknown) => AsyncResult<unknown, ApplicationFailure>,\n );\n }\n }\n\n // 2) Wrap workflow-scoped activities at root level (flat)\n if (contract.workflows) {\n for (const [workflowName, workflowDef] of Object.entries(contract.workflows)) {\n const wfActivitiesImpl = (activities as Record<string, unknown>)[workflowName] as\n | Record<string, unknown>\n | undefined;\n if (!wfActivitiesImpl) {\n // If no implementations provided for this workflow, skip (TypeScript typing should enforce completeness for declared ones)\n continue;\n }\n\n const wfDefs = workflowDef.activities ?? {};\n\n for (const [activityName, impl] of Object.entries(wfActivitiesImpl)) {\n const activityDef = wfDefs[activityName];\n if (!activityDef) {\n throw new ActivityDefinitionNotFoundError(\n `${workflowName}.${activityName}`,\n Object.keys(wfDefs),\n );\n }\n\n // Assign workflow activity directly at root level (flat structure)\n (wrappedActivities as Record<string, unknown>)[activityName] = makeWrapped(\n `${workflowName}.${activityName}`,\n activityDef,\n impl as (args: unknown) => AsyncResult<unknown, ApplicationFailure>,\n );\n }\n }\n }\n\n return wrappedActivities;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwMA,SAAgB,yBACd,SAC8B;CAC9B,MAAM,EAAE,UAAU,eAAe;CAGjC,MAAM,oBAAoB,CAAC;CAG3B,SAAS,YACP,cACA,aACA,cACA;EACA,OAAO,OAAO,GAAG,SAAoB;GACnC,MAAM,QAAQ,oBAAoB,IAAI;GAGtC,MAAM,cAAc,MAAM,YAAY,MAAM,YAAY,CAAC,SAAS,KAAK;GACvE,IAAI,YAAY,QACd,MAAM,IAAI,6BAA6B,cAAc,YAAY,MAAM;GAUzE,QAAO,MALc,aAAa,YAAY,KAAK,EAAA,CAKrC,MAAM;IAClB,IAAI,OAAO,UAAU;KACnB,MAAM,eAAe,MAAM,YAAY,OAAO,YAAY,CAAC,SAAS,KAAK;KACzE,IAAI,aAAa,QACf,MAAM,IAAI,8BAA8B,cAAc,aAAa,MAAM;KAE3E,OAAO,aAAa;IACtB;IAIA,MAAM,UAAU;KACd,MAAM;IACR;IAWA,SAAS,UAAU;KACjB,MAAM;IACR;GACF,CAAC;EACH;CACF;CAGA,IAAI,SAAS,YACX,KAAK,MAAM,CAAC,cAAc,SAAS,OAAO,QAAQ,UAAU,GAAG;EAE7D,IAAI,SAAS,aAAa,gBAAgB,SAAS,WACjD;EAGF,MAAM,cAAc,SAAS,WAAW;EACxC,IAAI,CAAC,aACH,MAAM,IAAI,gCAAgC,cAAc,OAAO,KAAK,SAAS,UAAU,CAAC;EAI1F,kBAA+C,gBAAgB,YAC7D,cACA,aACA,IACF;CACF;CAIF,IAAI,SAAS,WACX,KAAK,MAAM,CAAC,cAAc,gBAAgB,OAAO,QAAQ,SAAS,SAAS,GAAG;EAC5E,MAAM,mBAAoB,WAAuC;EAGjE,IAAI,CAAC,kBAEH;EAGF,MAAM,SAAS,YAAY,cAAc,CAAC;EAE1C,KAAK,MAAM,CAAC,cAAc,SAAS,OAAO,QAAQ,gBAAgB,GAAG;GACnE,MAAM,cAAc,OAAO;GAC3B,IAAI,CAAC,aACH,MAAM,IAAI,gCACR,GAAG,aAAa,GAAG,gBACnB,OAAO,KAAK,MAAM,CACpB;GAIF,kBAA+C,gBAAgB,YAC7D,GAAG,aAAa,GAAG,gBACnB,aACA,IACF;EACF;CACF;CAGF,OAAO;AACT"}
|