@btravstack/temporal-worker 0.11.0 → 0.13.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 +48 -15
- package/dist/index.cjs +133 -36
- package/dist/index.d.cts +200 -58
- package/dist/index.d.mts +200 -58
- package/dist/index.mjs +133 -37
- package/package.json +15 -15
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,119 @@
|
|
|
1
|
-
import { AnyModule, AnyPort, AnyProvider, Exportable, Module, NeedsGate, PortClassOf, PortInstance, Provider, Scope } from "@btravstack/di";
|
|
1
|
+
import { AnyModule, AnyPort, AnyProvider, Exportable, Module, NeedsGate, PortClassOf, PortInstance, Provider, Scope, ServiceOf } from "@btravstack/di";
|
|
2
2
|
import { ConfigInvalid, Env } from "@btravstack/config";
|
|
3
3
|
import { Runtime, RuntimePort } from "@btravstack/core";
|
|
4
|
-
import { DeclareActivitiesHandlerOptions } from "@temporal-contract/worker/activity";
|
|
4
|
+
import { DeclareActivitiesHandlerOptions, EmptyContext } from "@temporal-contract/worker/activity";
|
|
5
5
|
import { Duration } from "@temporalio/common";
|
|
6
6
|
import { NativeConnection, WorkflowBundleWithSourceMap } from "@temporalio/worker";
|
|
7
7
|
import { ContractDefinition } from "@temporal-contract/contract";
|
|
8
|
+
//#region src/unit.d.ts
|
|
9
|
+
/** What a bound unit module exports — the port instances an activity of that kind may read. */
|
|
10
|
+
type UnitExportsOf<M> = M extends Module<infer X, never, unknown> ? X : never;
|
|
11
|
+
/** The declared `unit:` record, as the services an activity reads off `context.unit`. */
|
|
12
|
+
type UnitRecordOf<U extends Readonly<Record<string, AnyPort>>> = { readonly [N in keyof U]: ServiceOf<InstanceType<U[N]>>; };
|
|
13
|
+
/**
|
|
14
|
+
* A piece injecting a port the bound `unit.activity` module does not export.
|
|
15
|
+
* `unknown` when every declared port is covered — including the empty case, so
|
|
16
|
+
* a worker whose pieces declare no `unit:` is gated on nothing.
|
|
17
|
+
*/
|
|
18
|
+
type UnitGate<Unit, Declared> = [Exclude<NonNullable<Declared>, UnitExportsOf<Unit>>] extends [never] ? unknown : {
|
|
19
|
+
readonly "UNIT DOES NOT PROVIDE — a piece injects a port the bound unit module does not export": Exclude<NonNullable<Declared>, UnitExportsOf<Unit>>;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/workflow-activities.d.ts
|
|
23
|
+
/**
|
|
24
|
+
* `ActivitiesOf<C, …>` is a `NoInfer`-wrapped conditional TypeScript refuses to
|
|
25
|
+
* index by a generic key. Routing it through `infer` resolves it to a plain
|
|
26
|
+
* object type first, which IS indexable.
|
|
27
|
+
*/
|
|
28
|
+
type ActivitiesRecordOf<C extends ContractDefinition, TContext extends Record<string, unknown> | EmptyContext = EmptyContext> = ActivitiesOf<C, TContext> extends (infer Resolved) ? Resolved : never;
|
|
29
|
+
/** The top-level keys of `C`'s activities record: a workflow that declares activities, or a contract-global activity. */
|
|
30
|
+
type ActivitiesKeyOf<C extends ContractDefinition> = keyof ActivitiesRecordOf<C> & string;
|
|
31
|
+
/**
|
|
32
|
+
* One entry's validated input, reached through the implementation's own second
|
|
33
|
+
* parameter — a workflow key carries a record of implementations, a
|
|
34
|
+
* contract-global one carries the implementation itself. `WorkerInferInput` is
|
|
35
|
+
* declared by `@temporal-contract/worker` and exported from none of its
|
|
36
|
+
* subpaths, so the by-index route is the only one open.
|
|
37
|
+
*/
|
|
38
|
+
type InputOfEntry<V> = V extends ((helpers: never, input: infer I) => unknown) ? I : V extends Readonly<Record<string, unknown>> ? InputOfEntry<V[keyof V]> : never;
|
|
39
|
+
/** The validated activity input, typed by the contract: the union of every activity's own input. */
|
|
40
|
+
type ActivityInputOf<C extends ContractDefinition> = InputOfEntry<ActivitiesRecordOf<C>[ActivitiesKeyOf<C>]>;
|
|
41
|
+
/** The seeded port's class, typed for `C`: what `ActivityInput(contract)` answers. */
|
|
42
|
+
type ActivityInputPortOf<C extends ContractDefinition> = PortClassOf<"ActivityInput", ActivityInputOf<C>>;
|
|
43
|
+
/**
|
|
44
|
+
* The seed's port instance, as it appears in a needs union — subtracted from
|
|
45
|
+
* what a bound `unit.activity` module still owes, since the fork is what
|
|
46
|
+
* discharges it.
|
|
47
|
+
*/
|
|
48
|
+
type ActivityInputInstance = PortInstance<"ActivityInput", unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* The validated activity input as a port: the one thing the worker seeds the
|
|
51
|
+
* fork with, so a `unit.activity` module derives a tenant — or anything else —
|
|
52
|
+
* from the invocation rather than from an ambient record.
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* const Input = ActivityInput(orderContract);
|
|
56
|
+
* const ActivityUnit = Module("ActivityUnit")({
|
|
57
|
+
* needs: [Input],
|
|
58
|
+
* provides: [Provider(Tenant)({ inject: { input: Input }, sync: ({ input }) => input.tenantId })],
|
|
59
|
+
* exports: [Tenant],
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* `contract` is read for its TYPE only. One `Port(...)` call fixed per contract
|
|
64
|
+
* at the type level, the move `TemporalActivitiesPort` makes, so a module built
|
|
65
|
+
* for one contract cannot read another's input.
|
|
66
|
+
*/
|
|
67
|
+
declare const ActivityInput: <C extends ContractDefinition>(contract: C) => ActivityInputPortOf<C>;
|
|
68
|
+
/** The prefix a piece's port id carries; the composing form strips it to recover the key. */
|
|
69
|
+
declare const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
|
|
70
|
+
/**
|
|
71
|
+
* The port one piece targets. Its id carries the key, which is what makes two
|
|
72
|
+
* slices claiming one workflow di's duplicate-provider defect rather than a
|
|
73
|
+
* silent merge.
|
|
74
|
+
*/
|
|
75
|
+
type WorkflowActivitiesPortOf<C extends ContractDefinition, K extends ActivitiesKeyOf<C>> = PortClassOf<`${typeof WORKFLOW_ACTIVITIES_PREFIX}${K}`, ActivitiesRecordOf<C>[K]>;
|
|
76
|
+
/** What a minted piece returns. */
|
|
77
|
+
type MintedActivities<C extends ContractDefinition, K extends ActivitiesKeyOf<C>, N, U extends Readonly<Record<string, AnyPort>>> = Provider<InstanceType<WorkflowActivitiesPortOf<C, K>>, never, N> & {
|
|
78
|
+
readonly port: WorkflowActivitiesPortOf<C, K>;
|
|
79
|
+
/** The declared `unit:` record, which the wrapper resolves against. */
|
|
80
|
+
readonly unit: U;
|
|
81
|
+
/** Phantom: the ports this piece injects, which the root's `unit.activity` must export. */
|
|
82
|
+
readonly _declared?: InstanceType<U[keyof U]>;
|
|
83
|
+
};
|
|
84
|
+
/** The activities `sync` hands back, typed by the record THIS piece declared. */
|
|
85
|
+
type ScopedActivitiesOf<C extends ContractDefinition, K extends ActivitiesKeyOf<C>, U extends Readonly<Record<string, AnyPort>>> = ActivitiesRecordOf<C, {
|
|
86
|
+
readonly unit: UnitRecordOf<U>;
|
|
87
|
+
}>[K & keyof ActivitiesRecordOf<C, {
|
|
88
|
+
readonly unit: UnitRecordOf<U>;
|
|
89
|
+
}>];
|
|
90
|
+
/**
|
|
91
|
+
* One workflow's activities, as a provider on a port of its own.
|
|
92
|
+
*
|
|
93
|
+
* A worker that polls one queue for several workflows is several pieces, each
|
|
94
|
+
* declaring the services its own activities call;
|
|
95
|
+
* `TemporalActivities(contract)([...])` composes them. `contract` is read for
|
|
96
|
+
* its TYPE only, and types both `key` and the record — so an activity the
|
|
97
|
+
* workflow does not declare is a compile error here rather than at startup.
|
|
98
|
+
*
|
|
99
|
+
* `key` is any top-level key of the activities record, which includes a
|
|
100
|
+
* contract-global activity as well as a workflow; the name is imprecise in that
|
|
101
|
+
* one case deliberately, since narrowing it would lock such a contract out of
|
|
102
|
+
* the split.
|
|
103
|
+
*
|
|
104
|
+
* There is no name to give: the key IS the port's name.
|
|
105
|
+
*
|
|
106
|
+
* `unit` declares the ports the activities read off `context.unit`, resolved
|
|
107
|
+
* out of the fork the attempt opened; the root's `unit.activity` module must
|
|
108
|
+
* export every one of them.
|
|
109
|
+
*/
|
|
110
|
+
declare const TemporalWorkflowActivities: <C extends ContractDefinition, const K extends ActivitiesKeyOf<C>>(contract: C, key: K) => <const D extends Readonly<Record<string, AnyPort>>, const U extends Readonly<Record<string, AnyPort>> = Record<never, never>>(options: {
|
|
111
|
+
readonly inject: D;
|
|
112
|
+
/** The unit-scoped ports these activities read off `context.unit`. */
|
|
113
|
+
readonly unit?: U;
|
|
114
|
+
readonly sync: (services: { readonly [N in keyof D]: ServiceOf<InstanceType<D[N]>>; }) => ScopedActivitiesOf<C, K, U>;
|
|
115
|
+
}) => MintedActivities<C, K, InstanceType<D[keyof D]>, U>;
|
|
116
|
+
//#endregion
|
|
8
117
|
//#region src/temporal-runtime.d.ts
|
|
9
118
|
/** What the worker publishes once it is polling, read back through `RunningApp.runtimeInfo()`. */
|
|
10
119
|
type TemporalInfo = {
|
|
@@ -58,8 +167,29 @@ declare class TemporalUnreachable extends TemporalUnreachable_base<{
|
|
|
58
167
|
}
|
|
59
168
|
/** The runtime's port: what `temporal()` provides, and what the module `start` boots must export. */
|
|
60
169
|
declare class TemporalRuntime extends RuntimePort<Runtime<never, TemporalInfo>> {}
|
|
61
|
-
/**
|
|
62
|
-
|
|
170
|
+
/**
|
|
171
|
+
* A module `unit.activity` may bind, as the upper bound `temporal()`
|
|
172
|
+
* constrains its own `Unit` type parameter to. `Module`'s `_exports` channel
|
|
173
|
+
* is contravariant, so `Exports = never` — never `unknown` — is what makes a
|
|
174
|
+
* REAL module's own (necessarily narrower) export type assignable to this
|
|
175
|
+
* bound: `(x: Concrete) => void` is assignable to `(x: never) => void`, not
|
|
176
|
+
* to `(x: unknown) => void`.
|
|
177
|
+
*/
|
|
178
|
+
type AnyUnitModule = Module<never, never, unknown>;
|
|
179
|
+
/**
|
|
180
|
+
* The needs a bound `unit.activity` module still owes, or `never` when none is
|
|
181
|
+
* bound. `Scope` is excluded, since nothing can ever provide it — the same
|
|
182
|
+
* exemption `NeedsGate` itself carries — and so is the activity input, which
|
|
183
|
+
* the fork's own seed discharges.
|
|
184
|
+
*/
|
|
185
|
+
type UnitNeedsOf<Unit> = Unit extends Module<never, never, infer N> ? Exclude<N, Scope | ActivityInputInstance> : never;
|
|
186
|
+
/**
|
|
187
|
+
* The activity implementations `declareActivitiesHandler` takes for `C`.
|
|
188
|
+
* `TContext` is what an implementation reads off `helpers.context` — left
|
|
189
|
+
* empty for the port the composed record lands on, and the declared
|
|
190
|
+
* `{ unit }` record for the piece that declared it.
|
|
191
|
+
*/
|
|
192
|
+
type ActivitiesOf<C extends ContractDefinition, TContext extends Record<string, unknown> | EmptyContext = EmptyContext> = DeclareActivitiesHandlerOptions<C, TContext>["activities"];
|
|
63
193
|
/** The activities port class, typed for `C`: what `TemporalActivities(contract)(…).port` is. */
|
|
64
194
|
type ActivitiesPortOf<C extends ContractDefinition> = PortClassOf<"TemporalActivities", ActivitiesOf<C>>;
|
|
65
195
|
/** The activities port's instance for `C` — the module's one need. */
|
|
@@ -69,7 +199,7 @@ type ActivitiesInstanceOf<C extends ContractDefinition> = PortInstance<"Temporal
|
|
|
69
199
|
* `TemporalModule` — spelled once so the two cannot drift, which is what a
|
|
70
200
|
* second copy of an option list always eventually does.
|
|
71
201
|
*/
|
|
72
|
-
type TemporalTuning = {
|
|
202
|
+
type TemporalTuning<Unit extends AnyUnitModule | undefined = undefined> = {
|
|
73
203
|
/** Pins `TemporalConfig.address` instead of reading `TEMPORAL_ADDRESS`. */
|
|
74
204
|
readonly address?: string;
|
|
75
205
|
/** Pins `TemporalConfig.namespace` instead of reading `TEMPORAL_NAMESPACE`. */
|
|
@@ -88,8 +218,17 @@ type TemporalTuning = {
|
|
|
88
218
|
* `shutdownGraceTime`.
|
|
89
219
|
*/
|
|
90
220
|
readonly gracePeriod?: Duration;
|
|
221
|
+
/**
|
|
222
|
+
* The unit module the worker forks around every activity attempt it
|
|
223
|
+
* dispatches, with no seed. Built after the activity is invoked, torn down
|
|
224
|
+
* when the unit closes — the point where a later phase seeds it with the
|
|
225
|
+
* workflow's tenant.
|
|
226
|
+
*/
|
|
227
|
+
readonly unit?: {
|
|
228
|
+
readonly activity?: Unit;
|
|
229
|
+
};
|
|
91
230
|
};
|
|
92
|
-
type TemporalOptions<C extends ContractDefinition> = TemporalTuning & {
|
|
231
|
+
type TemporalOptions<C extends ContractDefinition, Unit extends AnyUnitModule | undefined = undefined> = TemporalTuning<Unit> & {
|
|
93
232
|
/**
|
|
94
233
|
* The contract; the task queue this worker polls is read off it, and the
|
|
95
234
|
* activities port is typed by it. The starter calls
|
|
@@ -110,56 +249,28 @@ type Provided = TemporalRuntime | TemporalConfig | TemporalConnection;
|
|
|
110
249
|
* With both configuration fields pinned the module reads nothing from the
|
|
111
250
|
* environment; pin only one and the other still comes from it.
|
|
112
251
|
*/
|
|
113
|
-
declare const temporal: <C extends ContractDefinition>(options: TemporalOptions<C>) => Module<Provided, ConfigInvalid | TemporalUnreachable, Env | Scope | ActivitiesInstanceOf<C>>;
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region src/workflow-activities.d.ts
|
|
116
|
-
/**
|
|
117
|
-
* `ActivitiesOf<C>` is a `NoInfer`-wrapped conditional TypeScript refuses to
|
|
118
|
-
* index by a generic key. Routing it through `infer` resolves it to a plain
|
|
119
|
-
* object type first, which IS indexable.
|
|
120
|
-
*/
|
|
121
|
-
type ActivitiesRecordOf<C extends ContractDefinition> = ActivitiesOf<C> extends (infer Resolved) ? Resolved : never;
|
|
122
|
-
/** The top-level keys of `C`'s activities record: a workflow that declares activities, or a contract-global activity. */
|
|
123
|
-
type ActivitiesKeyOf<C extends ContractDefinition> = keyof ActivitiesRecordOf<C> & string;
|
|
124
|
-
/** The prefix a piece's port id carries; the composing form strips it to recover the key. */
|
|
125
|
-
declare const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
|
|
126
|
-
/**
|
|
127
|
-
* The port one piece targets. Its id carries the key, which is what makes two
|
|
128
|
-
* slices claiming one workflow di's duplicate-provider defect rather than a
|
|
129
|
-
* silent merge.
|
|
130
|
-
*/
|
|
131
|
-
type WorkflowActivitiesPortOf<C extends ContractDefinition, K extends ActivitiesKeyOf<C>> = PortClassOf<`${typeof WORKFLOW_ACTIVITIES_PREFIX}${K}`, ActivitiesRecordOf<C>[K]>;
|
|
132
|
-
/**
|
|
133
|
-
* One workflow's activities, as a provider on a port of its own.
|
|
134
|
-
*
|
|
135
|
-
* A worker that polls one queue for several workflows is several pieces, each
|
|
136
|
-
* declaring the services its own activities call;
|
|
137
|
-
* `TemporalActivities(contract)([...])` composes them. `contract` is read for
|
|
138
|
-
* its TYPE only, and types both `key` and the record — so an activity the
|
|
139
|
-
* workflow does not declare is a compile error here rather than at startup.
|
|
140
|
-
*
|
|
141
|
-
* `key` is any top-level key of the activities record, which includes a
|
|
142
|
-
* contract-global activity as well as a workflow; the name is imprecise in that
|
|
143
|
-
* one case deliberately, since narrowing it would lock such a contract out of
|
|
144
|
-
* the split.
|
|
145
|
-
*
|
|
146
|
-
* There is no name to give: the key IS the port's name.
|
|
147
|
-
*/
|
|
148
|
-
declare const TemporalWorkflowActivities: <C extends ContractDefinition, const K extends ActivitiesKeyOf<C>>(contract: C, key: K) => ReturnType<typeof Provider<WorkflowActivitiesPortOf<C, K>>>;
|
|
252
|
+
declare const temporal: <C extends ContractDefinition, Unit extends AnyUnitModule | undefined = undefined>(options: TemporalOptions<C, Unit>) => Module<Provided, ConfigInvalid | TemporalUnreachable, Env | Scope | ActivitiesInstanceOf<C> | UnitNeedsOf<Unit>>;
|
|
149
253
|
//#endregion
|
|
150
254
|
//#region src/temporal-module.d.ts
|
|
151
255
|
/** The starter's own module, as the sugar adds it to the application's imports. */
|
|
152
|
-
type TemporalStarter<C extends ContractDefinition> = Module<TemporalRuntime | TemporalConfig | TemporalConnection, ConfigInvalid | TemporalUnreachable, Env | Scope | ActivitiesInstanceOf<C>>;
|
|
256
|
+
type TemporalStarter<C extends ContractDefinition, Unit extends AnyUnitModule | undefined> = Module<TemporalRuntime | TemporalConfig | TemporalConnection, ConfigInvalid | TemporalUnreachable, Env | Scope | ActivitiesInstanceOf<C> | UnitNeedsOf<Unit>>;
|
|
153
257
|
/** The application's imports plus the starter — the tuple `Module(name)` is handed. */
|
|
154
|
-
type Imports<I extends readonly AnyModule[], C extends ContractDefinition> = readonly [...I, TemporalStarter<C>];
|
|
258
|
+
type Imports<I extends readonly AnyModule[], C extends ContractDefinition, Unit extends AnyUnitModule | undefined> = readonly [...I, TemporalStarter<C, Unit>];
|
|
155
259
|
/** The activities provider plus the application's own — the tuple `Module(name)` is handed. */
|
|
156
260
|
type Provides<P extends readonly AnyProvider[], C extends ContractDefinition, ActivitiesError, ActivitiesNeeds> = readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P];
|
|
157
|
-
type TemporalModuleOptions<C extends ContractDefinition, ActivitiesError, ActivitiesNeeds, I extends readonly AnyModule[], P extends readonly AnyProvider[], X extends readonly Exportable<Imports<I, C>, Provides<P, C, ActivitiesError, ActivitiesNeeds>>[], N extends readonly AnyPort[]> = TemporalTuning & {
|
|
261
|
+
type TemporalModuleOptions<C extends ContractDefinition, ActivitiesError, ActivitiesNeeds, Declared, Unit extends AnyUnitModule | undefined, I extends readonly AnyModule[], P extends readonly AnyProvider[], X extends readonly Exportable<Imports<I, C, Unit>, Provides<P, C, ActivitiesError, ActivitiesNeeds>>[], N extends readonly AnyPort[]> = TemporalTuning<Unit> & {
|
|
158
262
|
/** The `temporal-contract` contract; the task queue this worker polls is read off it. */
|
|
159
263
|
readonly contract: C;
|
|
160
264
|
readonly workflows: WorkflowSource;
|
|
161
|
-
/**
|
|
162
|
-
|
|
265
|
+
/**
|
|
266
|
+
* The application's activity implementations — what
|
|
267
|
+
* `TemporalActivities(contract)(…)` returns. Its `_declaredUnit` phantom
|
|
268
|
+
* carries every port its pieces read off `context.unit`, which is what
|
|
269
|
+
* {@link UnitGate} checks `unit.activity` against.
|
|
270
|
+
*/
|
|
271
|
+
readonly activities: Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds> & {
|
|
272
|
+
readonly _declaredUnit?: Declared;
|
|
273
|
+
};
|
|
163
274
|
readonly imports?: I;
|
|
164
275
|
readonly provides?: P;
|
|
165
276
|
/** The application's own exports; `TemporalRuntime` is added, since `start` resolves it. */
|
|
@@ -169,7 +280,7 @@ type TemporalModuleOptions<C extends ContractDefinition, ActivitiesError, Activi
|
|
|
169
280
|
* over the augmented tuples below, so forgetting one is an error at THIS call.
|
|
170
281
|
*/
|
|
171
282
|
readonly needs?: N;
|
|
172
|
-
} & NeedsGate<Imports<I, C>, Provides<P, C, ActivitiesError, ActivitiesNeeds>, N>;
|
|
283
|
+
} & NeedsGate<Imports<I, C, Unit>, Provides<P, C, ActivitiesError, ActivitiesNeeds>, N> & UnitGate<Unit, Declared>;
|
|
173
284
|
/**
|
|
174
285
|
* `Module(name)({...})` for a Temporal worker deployment: everything a di module
|
|
175
286
|
* takes, plus the contract, the activities provider and the workflow source. The
|
|
@@ -187,11 +298,17 @@ type TemporalModuleOptions<C extends ContractDefinition, ActivitiesError, Activi
|
|
|
187
298
|
* await runMain(OrderTemporalWorker);
|
|
188
299
|
* ```
|
|
189
300
|
*/
|
|
190
|
-
declare const TemporalModule: <const Name extends string>(name: Name) => <C extends ContractDefinition, ActivitiesError, ActivitiesNeeds, const I extends readonly AnyModule[] = [], const P extends readonly AnyProvider[] = [], const X extends readonly Exportable<Imports<I, C>, Provides<P, C, ActivitiesError, ActivitiesNeeds>>[] = [], const N extends readonly AnyPort[] = []>(options: TemporalModuleOptions<C, ActivitiesError, ActivitiesNeeds, I, P, X, N>) => Module<(Extract<(readonly [typeof TemporalRuntime, ...X])[number], AnyModule> extends (infer T) ? T extends Extract<(readonly [typeof TemporalRuntime, ...X])[number], AnyModule> ? T extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [typeof TemporalRuntime, ...X])[number] extends (infer E_1) ? E_1 extends AnyPort ? InstanceType<E_1> : E_1 extends AnyProvider ? InstanceType<E_1["port"]> : never : never), ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_1) ? T_1 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_1 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[1] | ((readonly [...I, TemporalStarter<C>])[number] extends (infer T_2) ? T_2 extends (readonly [...I, TemporalStarter<C>])[number] ? T_2 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[1], Exclude<((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[2], ((readonly [...I, TemporalStarter<C>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[0]> | Exclude<((readonly [...I, TemporalStarter<C>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[2], ((readonly [...I, TemporalStarter<C>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[0]>>;
|
|
301
|
+
declare const TemporalModule: <const Name extends string>(name: Name) => <C extends ContractDefinition, ActivitiesError, ActivitiesNeeds, Declared = never, Unit extends AnyUnitModule | undefined = undefined, const I extends readonly AnyModule[] = [], const P extends readonly AnyProvider[] = [], const X extends readonly Exportable<Imports<I, C, Unit>, Provides<P, C, ActivitiesError, ActivitiesNeeds>>[] = [], const N extends readonly AnyPort[] = []>(options: TemporalModuleOptions<C, ActivitiesError, ActivitiesNeeds, Declared, Unit, I, P, X, N>) => Module<(Extract<(readonly [typeof TemporalRuntime, ...X])[number], AnyModule> extends (infer T) ? T extends Extract<(readonly [typeof TemporalRuntime, ...X])[number], AnyModule> ? T extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [typeof TemporalRuntime, ...X])[number] extends (infer E_1) ? E_1 extends AnyPort ? InstanceType<E_1> : E_1 extends AnyProvider ? InstanceType<E_1["port"]> : never : never), ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_1) ? T_1 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_1 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[1] | ((readonly [...I, TemporalStarter<C, Unit>])[number] extends (infer T_2) ? T_2 extends (readonly [...I, TemporalStarter<C, Unit>])[number] ? T_2 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[1], Exclude<((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[2], ((readonly [...I, TemporalStarter<C, Unit>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C, Unit>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[0]> | Exclude<((readonly [...I, TemporalStarter<C, Unit>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C, Unit>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[2], ((readonly [...I, TemporalStarter<C, Unit>])[number] extends (infer T_4) ? T_4 extends (readonly [...I, TemporalStarter<C, Unit>])[number] ? T_4 extends Module<infer X_1, infer E, infer N_1> ? readonly [X_1, E, N_1] : never : never : never)[0] | ((readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] extends (infer T_3) ? T_3 extends (readonly [Provider<ActivitiesInstanceOf<C>, ActivitiesError, ActivitiesNeeds>, ...P])[number] ? T_3 extends Provider<infer P_1, infer E_2, infer N_2> ? readonly [P_1, E_2, N_2] : never : never : never)[0]>>;
|
|
191
302
|
/** One piece of the activities record — what `TemporalWorkflowActivities(contract, key)(…)` returns. */
|
|
192
303
|
type PieceOf<C extends ContractDefinition> = { readonly [K in ActivitiesKeyOf<C>]: {
|
|
193
304
|
readonly port: WorkflowActivitiesPortOf<C, K>;
|
|
305
|
+
/** Phantom, carrying the ports the piece injects off `context.unit`. */
|
|
306
|
+
readonly _declared?: unknown;
|
|
194
307
|
}; }[ActivitiesKeyOf<C>];
|
|
308
|
+
/** Every port the pieces in `T` inject off `context.unit` — what the root's `unit.activity` must export. */
|
|
309
|
+
type DeclaredOf<T extends readonly {
|
|
310
|
+
readonly _declared?: unknown;
|
|
311
|
+
}[]> = NonNullable<T[number]["_declared"]>;
|
|
195
312
|
/** The key a piece carries, read back off its port id. */
|
|
196
313
|
type KeyOfPiece<P> = P extends {
|
|
197
314
|
readonly port: {
|
|
@@ -213,6 +330,29 @@ type Uncovered<C extends ContractDefinition, T extends readonly PieceOf<C>[]> =
|
|
|
213
330
|
* hand.
|
|
214
331
|
*/
|
|
215
332
|
type Refuse<T extends readonly unknown[], Marker extends string, Detail> = T extends readonly [...infer Head, unknown] ? readonly [...Head, readonly [Marker, Detail]] : readonly [readonly [Marker, Detail]];
|
|
333
|
+
/**
|
|
334
|
+
* The record arm: the whole activities record from one `sync`, with one `unit:`
|
|
335
|
+
* record shared by every entry in it.
|
|
336
|
+
*
|
|
337
|
+
* It is `{ inject, unit?, sync }` — di's other arms are gone — for PARITY, not
|
|
338
|
+
* for a type-system reason: `value` could have carried the record just as well,
|
|
339
|
+
* since `U` infers from `unit` and never from the arm. `@btravstack/http-server`'s
|
|
340
|
+
* `api.OrpcRouter(contract)` is `{ inject, unit?, sync }` and so are all three
|
|
341
|
+
* packages' piece factories, so one arm across the family is one surface to
|
|
342
|
+
* learn and one to keep.
|
|
343
|
+
*/
|
|
344
|
+
type Whole<C extends ContractDefinition> = <const D extends Readonly<Record<string, AnyPort>>, const U extends Readonly<Record<string, AnyPort>> = Record<never, never>>(options: {
|
|
345
|
+
readonly inject: D;
|
|
346
|
+
/** The unit-scoped ports every activity in the record reads off `context.unit`. */
|
|
347
|
+
readonly unit?: U;
|
|
348
|
+
readonly sync: (services: { readonly [N in keyof D]: ServiceOf<InstanceType<D[N]>>; }) => ActivitiesOf<C, {
|
|
349
|
+
readonly unit: UnitRecordOf<U>;
|
|
350
|
+
}>;
|
|
351
|
+
}) => Provider<ActivitiesInstanceOf<C>, never, InstanceType<D[keyof D]>> & {
|
|
352
|
+
readonly port: ActivitiesPortOf<C>;
|
|
353
|
+
/** Phantom: the declared ports, which `TemporalModule` gates `unit.activity` against. */
|
|
354
|
+
readonly _declaredUnit?: InstanceType<U[keyof U]>;
|
|
355
|
+
};
|
|
216
356
|
/**
|
|
217
357
|
* The composing arm. Declared LAST in the intersection below on purpose:
|
|
218
358
|
* TypeScript reports the last overload's failure, so a non-covering array is
|
|
@@ -224,6 +364,8 @@ type Refuse<T extends readonly unknown[], Marker extends string, Detail> = T ext
|
|
|
224
364
|
*/
|
|
225
365
|
type Compose<C extends ContractDefinition> = <const T extends readonly PieceOf<C>[]>(pieces: [Uncovered<C, T>] extends [never] ? T : Refuse<T, "UNCOVERED ACTIVITIES — the contract declares a workflow this array does not cover", Uncovered<C, T>>) => Provider<ActivitiesInstanceOf<C>, never, InstanceType<T[number]["port"]>> & {
|
|
226
366
|
readonly port: ActivitiesPortOf<C>;
|
|
367
|
+
/** Phantom: the union of every piece's declared ports, which `TemporalModule` gates `unit.activity` against. */
|
|
368
|
+
readonly _declaredUnit?: DeclaredOf<T>;
|
|
227
369
|
};
|
|
228
370
|
/**
|
|
229
371
|
* The activities as a provider, from the contract. Two call forms, one port.
|
|
@@ -233,13 +375,13 @@ type Compose<C extends ContractDefinition> = <const T extends readonly PieceOf<C
|
|
|
233
375
|
* TemporalActivities(orderContract)([fulfillOrder, chargeOrder])
|
|
234
376
|
* ```
|
|
235
377
|
*
|
|
236
|
-
* The first is
|
|
237
|
-
*
|
|
238
|
-
* `TemporalWorkflowActivities(contract, key)` builds: they are
|
|
239
|
-
* deps, keyed by the contract key each piece's port id carries,
|
|
240
|
-
* record IS the activities record. Every key must be covered,
|
|
241
|
-
* claiming one key are di's duplicate-provider defect at build.
|
|
242
|
-
*/
|
|
243
|
-
declare const TemporalActivities: <C extends ContractDefinition>(contract: C) =>
|
|
378
|
+
* The first is `{ inject, unit?, sync }`, whose `sync` hands back the whole
|
|
379
|
+
* activities record — one `unit:` record for every entry in it. The second
|
|
380
|
+
* takes the pieces `TemporalWorkflowActivities(contract, key)` builds: they are
|
|
381
|
+
* the provider's deps, keyed by the contract key each piece's port id carries,
|
|
382
|
+
* so the services record IS the activities record. Every key must be covered,
|
|
383
|
+
* and two slices claiming one key are di's duplicate-provider defect at build.
|
|
384
|
+
*/
|
|
385
|
+
declare const TemporalActivities: <C extends ContractDefinition>(contract: C) => Whole<C> & Compose<C>;
|
|
244
386
|
//#endregion
|
|
245
|
-
export { type ActivitiesInstanceOf, type ActivitiesPortOf, TemporalActivities, TemporalConfig, TemporalConnection, type TemporalInfo, TemporalModule, type TemporalModuleOptions, type TemporalOptions, TemporalRuntime, TemporalUnreachable, TemporalWorkflowActivities, type WorkflowActivitiesPortOf, type WorkflowSource, temporal };
|
|
387
|
+
export { type ActivitiesInstanceOf, type ActivitiesPortOf, ActivityInput, type ActivityInputOf, type ActivityInputPortOf, TemporalActivities, TemporalConfig, TemporalConnection, type TemporalInfo, TemporalModule, type TemporalModuleOptions, type TemporalOptions, TemporalRuntime, TemporalUnreachable, TemporalWorkflowActivities, type WorkflowActivitiesPortOf, type WorkflowSource, temporal };
|
package/dist/index.mjs
CHANGED
|
@@ -6,10 +6,123 @@ import { msToNumber } from "@temporalio/common";
|
|
|
6
6
|
import { NativeConnection, Worker } from "@temporalio/worker";
|
|
7
7
|
import { TaggedError, fromPromise, fromSafePromise, fromThrowable } from "unthrown";
|
|
8
8
|
import { activityInfo } from "@temporalio/activity";
|
|
9
|
+
//#region src/unit.ts
|
|
10
|
+
/**
|
|
11
|
+
* Where `activityUnits` leaves the forked context for the piece wrappers to
|
|
12
|
+
* read. A symbol rather than a name: the middleware merges it into the context
|
|
13
|
+
* every activity sees, and a string key would appear on the record an activity
|
|
14
|
+
* written against the `{ inject, sync }` arm destructures.
|
|
15
|
+
*/
|
|
16
|
+
const UNIT_SCOPE = Symbol("@btravstack/temporal-worker/unit-scope");
|
|
17
|
+
/**
|
|
18
|
+
* The declared record, as a getter per name resolved on read out of the fork.
|
|
19
|
+
* Neither writable nor configurable — an activity reads what the fork holds, and
|
|
20
|
+
* cannot reshape the record under the next attempt. With no `unit.activity`
|
|
21
|
+
* bound there is nothing to resolve from, so the record is empty whatever was
|
|
22
|
+
* declared.
|
|
23
|
+
*/
|
|
24
|
+
const unitRecordOf = (forked, record) => {
|
|
25
|
+
const unit = {};
|
|
26
|
+
if (forked === void 0) return unit;
|
|
27
|
+
for (const [name, port] of Object.entries(record)) Object.defineProperty(unit, name, {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: () => forked.get(port)
|
|
30
|
+
});
|
|
31
|
+
return unit;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* One entry of the activities record — a contract-global activity's own
|
|
35
|
+
* implementation, or the record of them a workflow key carries — with its
|
|
36
|
+
* piece's declared record put on `context.unit`. Applied once per piece as di
|
|
37
|
+
* constructs it, so an attempt costs one record and one context object.
|
|
38
|
+
*/
|
|
39
|
+
const withUnit = (record, entry) => {
|
|
40
|
+
const wrap = (implementation) => (helpers, input) => {
|
|
41
|
+
const { [UNIT_SCOPE]: forked, ...rest } = helpers.context ?? {};
|
|
42
|
+
return implementation({
|
|
43
|
+
...helpers,
|
|
44
|
+
context: {
|
|
45
|
+
...rest,
|
|
46
|
+
unit: unitRecordOf(forked, record)
|
|
47
|
+
}
|
|
48
|
+
}, input);
|
|
49
|
+
};
|
|
50
|
+
return typeof entry === "function" ? wrap(entry) : Object.fromEntries(Object.entries(entry).map(([name, implementation]) => [name, wrap(implementation)]));
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/workflow-activities.ts
|
|
54
|
+
/** One id, declared once, so no contract instantiating it warns about a duplicate. */
|
|
55
|
+
const ActivityInputPort = Port("ActivityInput");
|
|
56
|
+
/**
|
|
57
|
+
* The validated activity input as a port: the one thing the worker seeds the
|
|
58
|
+
* fork with, so a `unit.activity` module derives a tenant — or anything else —
|
|
59
|
+
* from the invocation rather than from an ambient record.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* const Input = ActivityInput(orderContract);
|
|
63
|
+
* const ActivityUnit = Module("ActivityUnit")({
|
|
64
|
+
* needs: [Input],
|
|
65
|
+
* provides: [Provider(Tenant)({ inject: { input: Input }, sync: ({ input }) => input.tenantId })],
|
|
66
|
+
* exports: [Tenant],
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* `contract` is read for its TYPE only. One `Port(...)` call fixed per contract
|
|
71
|
+
* at the type level, the move `TemporalActivitiesPort` makes, so a module built
|
|
72
|
+
* for one contract cannot read another's input.
|
|
73
|
+
*/
|
|
74
|
+
const ActivityInput = (contract) => {
|
|
75
|
+
return ActivityInputPort;
|
|
76
|
+
};
|
|
77
|
+
/** The prefix a piece's port id carries; the composing form strips it to recover the key. */
|
|
78
|
+
const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
|
|
79
|
+
/**
|
|
80
|
+
* One workflow's activities, as a provider on a port of its own.
|
|
81
|
+
*
|
|
82
|
+
* A worker that polls one queue for several workflows is several pieces, each
|
|
83
|
+
* declaring the services its own activities call;
|
|
84
|
+
* `TemporalActivities(contract)([...])` composes them. `contract` is read for
|
|
85
|
+
* its TYPE only, and types both `key` and the record — so an activity the
|
|
86
|
+
* workflow does not declare is a compile error here rather than at startup.
|
|
87
|
+
*
|
|
88
|
+
* `key` is any top-level key of the activities record, which includes a
|
|
89
|
+
* contract-global activity as well as a workflow; the name is imprecise in that
|
|
90
|
+
* one case deliberately, since narrowing it would lock such a contract out of
|
|
91
|
+
* the split.
|
|
92
|
+
*
|
|
93
|
+
* There is no name to give: the key IS the port's name.
|
|
94
|
+
*
|
|
95
|
+
* `unit` declares the ports the activities read off `context.unit`, resolved
|
|
96
|
+
* out of the fork the attempt opened; the root's `unit.activity` module must
|
|
97
|
+
* export every one of them.
|
|
98
|
+
*/
|
|
99
|
+
const TemporalWorkflowActivities = (contract, key) => {
|
|
100
|
+
const port = class extends Port(`${WORKFLOW_ACTIVITIES_PREFIX}${key}`) {};
|
|
101
|
+
return (options) => {
|
|
102
|
+
const record = options.unit ?? {};
|
|
103
|
+
return Object.assign(Provider(port)({
|
|
104
|
+
inject: options.inject,
|
|
105
|
+
sync: (services) => withUnit(record, options.sync(services))
|
|
106
|
+
}), { unit: record });
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
9
110
|
//#region src/activity-units.ts
|
|
10
111
|
/**
|
|
11
|
-
* Open one kernel unit per activity attempt
|
|
12
|
-
*
|
|
112
|
+
* Open one kernel unit per activity attempt, forking `unit` — when one is
|
|
113
|
+
* bound — after the activity is invoked, before it runs; the fork is torn down
|
|
114
|
+
* when the unit closes. It is seeded with the validated input on
|
|
115
|
+
* `ActivityInput(contract)`, so a unit module derives a tenant from the
|
|
116
|
+
* invocation rather than reading one off an ambient record. With no `unit`
|
|
117
|
+
* bound, `next()` runs unchanged, so the ambient `currentUnit()` record is what
|
|
118
|
+
* an adapter reads.
|
|
119
|
+
*
|
|
120
|
+
* The forked context rides the invocation's context under {@link UNIT_SCOPE},
|
|
121
|
+
* where each piece's own wrapper turns it into the typed `context.unit` record
|
|
122
|
+
* that piece declared — the records live with the pieces, so a hand-composed
|
|
123
|
+
* `temporal()` gets them without threading a second option through the starter,
|
|
124
|
+
* and the middleware never has to map Temporal's FLAT activity name back to the
|
|
125
|
+
* workflow key its piece was minted under.
|
|
13
126
|
*
|
|
14
127
|
* **That includes the kernel's per-unit `AbortSignal`**, and it is the only
|
|
15
128
|
* route to it: an activity has no parameter to receive one through. Temporal's
|
|
@@ -19,13 +132,13 @@ import { activityInfo } from "@temporalio/activity";
|
|
|
19
132
|
* No `Result`-unwrapping boundary, deliberately: `declareActivitiesHandler` owns
|
|
20
133
|
* the mapping from a settled `Result` to an activity failure.
|
|
21
134
|
*/
|
|
22
|
-
const activityUnits = (host, observers) => (
|
|
135
|
+
const activityUnits = (host, observers, unit) => (invocation, next) => {
|
|
23
136
|
const settle = observe(observers, {
|
|
24
137
|
component: "temporal",
|
|
25
138
|
name: "attempt",
|
|
26
139
|
attributes: { activity: activityInfo().activityType }
|
|
27
140
|
});
|
|
28
|
-
return host.run(metaFor(), () => next()).tap(() => settle({ outcome: "ok" })).tapFailure((failure) => settle({
|
|
141
|
+
return host.run(metaFor(), (scope) => unit === void 0 ? next() : scope.fork(unit, [[ActivityInputPort, invocation.input]]).flatMap((forked) => next({ context: { [UNIT_SCOPE]: forked } }))).tap(() => settle({ outcome: "ok" })).tapFailure((failure) => settle({
|
|
29
142
|
outcome: "error",
|
|
30
143
|
cause: failure.tag === "Err" ? failure.error : failure.cause
|
|
31
144
|
}));
|
|
@@ -163,7 +276,7 @@ const createWorker = (host, connection, config, activities, options, observers)
|
|
|
163
276
|
const { namespace } = config;
|
|
164
277
|
return fromThrowable(() => declareActivitiesHandler({
|
|
165
278
|
contract: options.contract,
|
|
166
|
-
middleware: activityUnits(host, observers),
|
|
279
|
+
middleware: activityUnits(host, observers, options.unit?.activity),
|
|
167
280
|
activities
|
|
168
281
|
}), startFailed)().toAsync().flatMap((handler) => fromPromise(Worker.create({
|
|
169
282
|
connection,
|
|
@@ -199,30 +312,6 @@ const poll = (worker, taskQueue, namespace) => {
|
|
|
199
312
|
};
|
|
200
313
|
};
|
|
201
314
|
//#endregion
|
|
202
|
-
//#region src/workflow-activities.ts
|
|
203
|
-
/** The prefix a piece's port id carries; the composing form strips it to recover the key. */
|
|
204
|
-
const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
|
|
205
|
-
/**
|
|
206
|
-
* One workflow's activities, as a provider on a port of its own.
|
|
207
|
-
*
|
|
208
|
-
* A worker that polls one queue for several workflows is several pieces, each
|
|
209
|
-
* declaring the services its own activities call;
|
|
210
|
-
* `TemporalActivities(contract)([...])` composes them. `contract` is read for
|
|
211
|
-
* its TYPE only, and types both `key` and the record — so an activity the
|
|
212
|
-
* workflow does not declare is a compile error here rather than at startup.
|
|
213
|
-
*
|
|
214
|
-
* `key` is any top-level key of the activities record, which includes a
|
|
215
|
-
* contract-global activity as well as a workflow; the name is imprecise in that
|
|
216
|
-
* one case deliberately, since narrowing it would lock such a contract out of
|
|
217
|
-
* the split.
|
|
218
|
-
*
|
|
219
|
-
* There is no name to give: the key IS the port's name.
|
|
220
|
-
*/
|
|
221
|
-
const TemporalWorkflowActivities = (contract, key) => {
|
|
222
|
-
const port = class extends Port(`${WORKFLOW_ACTIVITIES_PREFIX}${key}`) {};
|
|
223
|
-
return Provider(port);
|
|
224
|
-
};
|
|
225
|
-
//#endregion
|
|
226
315
|
//#region src/temporal-module.ts
|
|
227
316
|
/**
|
|
228
317
|
* `Module(name)({...})` for a Temporal worker deployment: everything a di module
|
|
@@ -262,12 +351,12 @@ const TemporalModule = (name) => (options) => {
|
|
|
262
351
|
* TemporalActivities(orderContract)([fulfillOrder, chargeOrder])
|
|
263
352
|
* ```
|
|
264
353
|
*
|
|
265
|
-
* The first is
|
|
266
|
-
*
|
|
267
|
-
* `TemporalWorkflowActivities(contract, key)` builds: they are
|
|
268
|
-
* deps, keyed by the contract key each piece's port id carries,
|
|
269
|
-
* record IS the activities record. Every key must be covered,
|
|
270
|
-
* claiming one key are di's duplicate-provider defect at build.
|
|
354
|
+
* The first is `{ inject, unit?, sync }`, whose `sync` hands back the whole
|
|
355
|
+
* activities record — one `unit:` record for every entry in it. The second
|
|
356
|
+
* takes the pieces `TemporalWorkflowActivities(contract, key)` builds: they are
|
|
357
|
+
* the provider's deps, keyed by the contract key each piece's port id carries,
|
|
358
|
+
* so the services record IS the activities record. Every key must be covered,
|
|
359
|
+
* and two slices claiming one key are di's duplicate-provider defect at build.
|
|
271
360
|
*/
|
|
272
361
|
const TemporalActivities = (contract) => {
|
|
273
362
|
const build = Provider(TemporalActivitiesPort);
|
|
@@ -275,7 +364,14 @@ const TemporalActivities = (contract) => {
|
|
|
275
364
|
inject: Object.fromEntries(pieces.map((piece) => [piece.port.portId.slice(27), piece.port])),
|
|
276
365
|
sync: (services) => services
|
|
277
366
|
});
|
|
278
|
-
|
|
367
|
+
const whole = (options) => {
|
|
368
|
+
const record = options.unit ?? {};
|
|
369
|
+
return build({
|
|
370
|
+
inject: options.inject,
|
|
371
|
+
sync: (services) => Object.fromEntries(Object.entries(options.sync(services)).map(([key, entry]) => [key, withUnit(record, entry)]))
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
return ((first) => Array.isArray(first) ? compose(first) : whole(first));
|
|
279
375
|
};
|
|
280
376
|
//#endregion
|
|
281
|
-
export { TemporalActivities, TemporalConfig, TemporalConnection, TemporalModule, TemporalRuntime, TemporalUnreachable, TemporalWorkflowActivities, temporal };
|
|
377
|
+
export { ActivityInput, TemporalActivities, TemporalConfig, TemporalConnection, TemporalModule, TemporalRuntime, TemporalUnreachable, TemporalWorkflowActivities, temporal };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/temporal-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "The Temporal worker runtime for @btravstack/core: one unit per activity attempt, and a drain that honours the kernel's deadline",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graceful-shutdown",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@btravstack/tsconfig": "0.
|
|
58
|
-
"@temporal-contract/client": "8.0.0-beta.
|
|
59
|
-
"@temporal-contract/contract": "8.0.0-beta.
|
|
60
|
-
"@temporal-contract/worker": "8.0.0-beta.
|
|
57
|
+
"@btravstack/tsconfig": "0.4.0",
|
|
58
|
+
"@temporal-contract/client": "8.0.0-beta.9",
|
|
59
|
+
"@temporal-contract/contract": "8.0.0-beta.9",
|
|
60
|
+
"@temporal-contract/worker": "8.0.0-beta.9",
|
|
61
61
|
"@temporalio/activity": "1.23.0",
|
|
62
62
|
"@temporalio/client": "1.23.0",
|
|
63
63
|
"@temporalio/common": "1.23.0",
|
|
@@ -71,23 +71,23 @@
|
|
|
71
71
|
"unthrown": "5.7.0",
|
|
72
72
|
"vitest": "4.1.11",
|
|
73
73
|
"zod": "4.5.3",
|
|
74
|
-
"@btravstack/config": "0.
|
|
75
|
-
"@btravstack/core": "0.
|
|
76
|
-
"@btravstack/di": "0.
|
|
74
|
+
"@btravstack/config": "0.13.0",
|
|
75
|
+
"@btravstack/core": "0.13.0",
|
|
76
|
+
"@btravstack/di": "0.13.0",
|
|
77
77
|
"@btravstack/internal-test-infra": "0.0.0",
|
|
78
|
-
"@btravstack/testing": "0.
|
|
78
|
+
"@btravstack/testing": "0.13.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@temporal-contract/client": "^8.0.0-beta.
|
|
82
|
-
"@temporal-contract/contract": "^8.0.0-beta.
|
|
83
|
-
"@temporal-contract/worker": "^8.0.0-beta.
|
|
81
|
+
"@temporal-contract/client": "^8.0.0-beta.9",
|
|
82
|
+
"@temporal-contract/contract": "^8.0.0-beta.9",
|
|
83
|
+
"@temporal-contract/worker": "^8.0.0-beta.9",
|
|
84
84
|
"@temporalio/activity": "^1.22.0",
|
|
85
85
|
"@temporalio/common": "^1.22.0",
|
|
86
86
|
"@temporalio/worker": "^1.22.0",
|
|
87
87
|
"unthrown": "^5.0.0",
|
|
88
|
-
"@btravstack/config": "^0.
|
|
89
|
-
"@btravstack/core": "^0.
|
|
90
|
-
"@btravstack/di": "^0.
|
|
88
|
+
"@btravstack/config": "^0.13.0",
|
|
89
|
+
"@btravstack/core": "^0.13.0",
|
|
90
|
+
"@btravstack/di": "^0.13.0"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@temporal-contract/client": {
|