@btravstack/temporal-worker 0.48.0 → 0.49.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 CHANGED
@@ -160,6 +160,9 @@ itself, inside its own error qualifier, so a contract it cannot satisfy is a
160
160
  startup `Err`, not a defect. `runtimeInfo()` reads `{ taskQueue, namespace }`
161
161
  back once the worker is polling.
162
162
 
163
+ Activity-piece DI identities include the contract task queue. Separate worker
164
+ contracts can reuse keys without sharing a port; wire activity names stay unchanged.
165
+
163
166
  A worker polling for several workflows can be several slices instead of one
164
167
  record: `TemporalWorkflowActivities(contract, key)({ inject: { name: Dep }, unit, fork, sync })` mints a
165
168
  provider for ONE workflow's activities (or a contract-global activity),
@@ -218,6 +221,8 @@ optional peers. See
218
221
 
219
222
  ## Options
220
223
 
224
+ - `taskQueue`: pins `TEMPORAL_TASK_QUEUE`, defaulting to the contract queue; see the [reference](https://btravstack.github.io/btravstack/reference/temporal-worker).
225
+
221
226
  `TemporalModule(name)({...})` takes the starter's options plus `activities`
222
227
  and the module lists (`imports`, `provides`, `exports`, `needs`). It is the
223
228
  only way to compose the starter, which is not exported:
package/dist/index.d.mts CHANGED
@@ -58,7 +58,7 @@ declare const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
58
58
  * slices claiming one workflow di's duplicate-provider defect rather than a
59
59
  * silent merge.
60
60
  */
61
- type WorkflowActivitiesPortOf<C extends ContractDefinition, K extends ActivitiesKeyOf<C>> = PortClassOf<`${typeof WORKFLOW_ACTIVITIES_PREFIX}${K}`, ActivitiesRecordOf<C>[K]>;
61
+ type WorkflowActivitiesPortOf<C extends ContractDefinition, K extends ActivitiesKeyOf<C>> = PortClassOf<`${typeof WORKFLOW_ACTIVITIES_PREFIX}${C["taskQueue"]}:${K}`, ActivitiesRecordOf<C>[K]>;
62
62
  /**
63
63
  * What a minted piece returns. A piece naming its own `fork` owes that
64
64
  * module's unmet needs, and leaves nothing for the root's `unit.activity` to
@@ -66,6 +66,7 @@ type WorkflowActivitiesPortOf<C extends ContractDefinition, K extends Activities
66
66
  */
67
67
  type MintedActivities<C extends ContractDefinition, K extends ActivitiesKeyOf<C>, N, U extends Readonly<Record<string, AnyPort>>, M extends AnyUnitModule | undefined> = Provider<InstanceType<WorkflowActivitiesPortOf<C, K>>, never, N | UnitNeedsOf$1<M>> & {
68
68
  readonly port: WorkflowActivitiesPortOf<C, K>;
69
+ readonly _activityKey?: K;
69
70
  /** Phantom: the ports this piece injects, which the root's `unit.activity` must export. */
70
71
  readonly _declared?: undefined extends M ? InstanceType<U[keyof U]> : never;
71
72
  };
@@ -80,16 +81,16 @@ type ScopedActivitiesOf<C extends ContractDefinition, K extends ActivitiesKeyOf<
80
81
  *
81
82
  * A worker that polls one queue for several workflows is several pieces, each
82
83
  * declaring the services its own activities call;
83
- * `TemporalActivities(contract)([...])` composes them. `contract` is read for
84
- * its TYPE only, and types both `key` and the record — so an activity the
85
- * workflow does not declare is a compile error here rather than at startup.
84
+ * `TemporalActivities(contract)([...])` composes them. The contract types both
85
+ * `key` and the record and supplies the task queue that namespaces the port.
86
+ * An undeclared activity is a compile error rather than a startup failure.
86
87
  *
87
88
  * `key` is any top-level key of the activities record, which includes a
88
89
  * contract-global activity as well as a workflow; the name is imprecise in that
89
90
  * one case deliberately, since narrowing it would lock such a contract out of
90
91
  * the split.
91
92
  *
92
- * There is no name to give: the key IS the port's name.
93
+ * The contract's task queue and the key identify the port; wire activity names stay unchanged.
93
94
  *
94
95
  * `unit` declares the ports the activities read off `context.unit`, resolved
95
96
  * out of the fork the attempt opened; the root's `unit.activity` module must
@@ -140,6 +141,7 @@ declare const TemporalConfig_base: import("@btravstack/di").PortClass<"TemporalC
140
141
  export declare class TemporalConfig extends TemporalConfig_base<{
141
142
  readonly address: string;
142
143
  readonly namespace: string;
144
+ readonly taskQueue: string;
143
145
  /** Temporal's `shutdownGraceTime`, in milliseconds. */
144
146
  readonly gracePeriodMs: number;
145
147
  /** Temporal's `shutdownForceTime`, in milliseconds. */
@@ -191,6 +193,8 @@ type TemporalTuning<Unit extends AnyUnitModule | undefined = undefined, Converte
191
193
  readonly address?: string;
192
194
  /** Pins `TemporalConfig.namespace` instead of reading `TEMPORAL_NAMESPACE`. */
193
195
  readonly namespace?: string;
196
+ /** Physical polling queue, independent of the logical contract and DI identities. Pins TEMPORAL_TASK_QUEUE. */
197
+ readonly taskQueue?: string;
194
198
  /**
195
199
  * Pins `TemporalConfig.forceAfterMs` instead of reading
196
200
  * `TEMPORAL_FORCE_AFTER_MS` (default `15_000`) — Temporal's
@@ -244,7 +248,7 @@ type TemporalOptions<C extends ContractDefinition, Unit extends AnyUnitModule |
244
248
  //#endregion
245
249
  //#region src/temporal-module.d.ts
246
250
  /** The starter's own module, as the sugar adds it to the application's imports. */
247
- type TemporalStarter<C extends ContractDefinition, Unit extends AnyUnitModule | undefined, Converter extends DataConverterPort> = Module<TemporalRuntime | TemporalConfig | TemporalConnection, ConfigInvalid | TemporalUnreachable, EnvReading<never, "TEMPORAL_ADDRESS" | "TEMPORAL_NAMESPACE" | "TEMPORAL_GRACE_PERIOD_MS" | "TEMPORAL_FORCE_AFTER_MS"> | Scope | ActivitiesInstanceOf<C> | UnitNeedsOf$1<Unit> | InstanceType<Converter>>;
251
+ type TemporalStarter<C extends ContractDefinition, Unit extends AnyUnitModule | undefined, Converter extends DataConverterPort> = Module<TemporalRuntime | TemporalConfig | TemporalConnection, ConfigInvalid | TemporalUnreachable, EnvReading<never, "TEMPORAL_ADDRESS" | "TEMPORAL_NAMESPACE" | "TEMPORAL_TASK_QUEUE" | "TEMPORAL_GRACE_PERIOD_MS" | "TEMPORAL_FORCE_AFTER_MS"> | Scope | ActivitiesInstanceOf<C> | UnitNeedsOf$1<Unit> | InstanceType<Converter>>;
248
252
  /** The application's imports plus the starter — the tuple `Module(name)` is handed. */
249
253
  type Imports<I extends readonly AnyModule[], C extends ContractDefinition, Unit extends AnyUnitModule | undefined, Converter extends DataConverterPort> = readonly [...I, TemporalStarter<C, Unit, Converter>];
250
254
  /** The activities provider plus the application's own — the tuple `Module(name)` is handed. */
@@ -292,6 +296,7 @@ export declare const TemporalModule: <const Name extends string>(name: Name) =>
292
296
  /** One piece of the activities record — what `TemporalWorkflowActivities(contract, key)(…)` returns. */
293
297
  type PieceOf<C extends ContractDefinition> = { readonly [K in ActivitiesKeyOf<C>]: {
294
298
  readonly port: WorkflowActivitiesPortOf<C, K>;
299
+ readonly _activityKey?: K;
295
300
  /** Phantom, carrying the ports the piece injects off `context.unit`. */
296
301
  readonly _declared?: unknown;
297
302
  }; }[ActivitiesKeyOf<C>];
@@ -299,11 +304,9 @@ type PieceOf<C extends ContractDefinition> = { readonly [K in ActivitiesKeyOf<C>
299
304
  type DeclaredOf<T extends readonly {
300
305
  readonly _declared?: unknown;
301
306
  }[]> = NonNullable<T[number]["_declared"]>;
302
- /** The key a piece carries, read back off its port id. */
307
+ /** The contract key a piece carries independently of its queue namespace. */
303
308
  type KeyOfPiece<P> = P extends {
304
- readonly port: {
305
- readonly portId: `${typeof WORKFLOW_ACTIVITIES_PREFIX}${infer K}`;
306
- };
309
+ readonly _activityKey?: infer K;
307
310
  } ? K : never;
308
311
  /** The record's top-level keys no piece in `T` covers. */
309
312
  type Uncovered<C extends ContractDefinition, T extends readonly PieceOf<C>[]> = Exclude<ActivitiesKeyOf<C>, KeyOfPiece<T[number]>>;
package/dist/index.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import { Observers, RuntimePort, RuntimeStartFailed, composeByPrefix, dispatchUnit, infrastructureExports, mintPiece, noObserverMember, releasedBy, withUnitRecord } from "@btravstack/core";
2
2
  import { Module, Port, Provider } from "@btravstack/di";
3
- import { Config, Env } from "@btravstack/config";
3
+ import { Config, ConfigFieldInvalid, Env } from "@btravstack/config";
4
4
  import { declareActivitiesHandler } from "@temporal-contract/worker/activity";
5
5
  import { msToNumber } from "@temporalio/common";
6
6
  import { NativeConnection, Worker } from "@temporalio/worker";
7
7
  import { Ok, OkAsync, TaggedError, fromPromise, fromSafePromise, fromThrowable } from "unthrown";
8
8
  import { activityInfo } from "@temporalio/activity";
9
+ import { defineContractBinding } from "@temporal-contract/contract";
9
10
  //#region src/unit.ts
10
11
  /**
11
12
  * One entry of the activities record — a contract-global activity's own
@@ -61,16 +62,16 @@ const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
61
62
  *
62
63
  * A worker that polls one queue for several workflows is several pieces, each
63
64
  * declaring the services its own activities call;
64
- * `TemporalActivities(contract)([...])` composes them. `contract` is read for
65
- * its TYPE only, and types both `key` and the record — so an activity the
66
- * workflow does not declare is a compile error here rather than at startup.
65
+ * `TemporalActivities(contract)([...])` composes them. The contract types both
66
+ * `key` and the record and supplies the task queue that namespaces the port.
67
+ * An undeclared activity is a compile error rather than a startup failure.
67
68
  *
68
69
  * `key` is any top-level key of the activities record, which includes a
69
70
  * contract-global activity as well as a workflow; the name is imprecise in that
70
71
  * one case deliberately, since narrowing it would lock such a contract out of
71
72
  * the split.
72
73
  *
73
- * There is no name to give: the key IS the port's name.
74
+ * The contract's task queue and the key identify the port; wire activity names stay unchanged.
74
75
  *
75
76
  * `unit` declares the ports the activities read off `context.unit`, resolved
76
77
  * out of the fork the attempt opened; the root's `unit.activity` module must
@@ -84,7 +85,7 @@ const WORKFLOW_ACTIVITIES_PREFIX = "TemporalWorkflowActivities:";
84
85
  * declares are then checked against this module, here.
85
86
  */
86
87
  const TemporalWorkflowActivities = (contract, key) => {
87
- const port = class extends Port(`${WORKFLOW_ACTIVITIES_PREFIX}${key}`) {};
88
+ const port = class extends Port(`${WORKFLOW_ACTIVITIES_PREFIX}${contract.taskQueue}:${key}`) {};
88
89
  return (options) => mintPiece(port, (record, entry) => withFork(withUnit(record, entry), options.fork))(options);
89
90
  };
90
91
  //#endregion
@@ -154,6 +155,17 @@ const metaFor = () => {
154
155
  };
155
156
  };
156
157
  //#endregion
158
+ //#region src/task-queue.ts
159
+ const taskQueueField = (contract) => {
160
+ const field = Config.string("TEMPORAL_TASK_QUEUE", { default: contract.taskQueue });
161
+ const check = (taskQueue) => fromThrowable(() => defineContractBinding(contract, { taskQueue }).taskQueue, (cause) => new ConfigFieldInvalid({ reason: String(cause) }))();
162
+ return {
163
+ variable: field.variable,
164
+ parse: (raw) => field.parse(raw).flatMap(check),
165
+ check
166
+ };
167
+ };
168
+ //#endregion
157
169
  //#region src/temporal-runtime.ts
158
170
  /**
159
171
  * Where the Temporal service is, as a service: the starter binds it from the
@@ -214,6 +226,7 @@ const temporal = (options) => {
214
226
  const config = Config.provider(TemporalConfig)(Config.object({
215
227
  address: Config.pinned(address, Config.string("TEMPORAL_ADDRESS", { default: "127.0.0.1:7233" })),
216
228
  namespace: Config.pinned(namespace, Config.string("TEMPORAL_NAMESPACE", { default: "default" })),
229
+ taskQueue: Config.pinned(options.taskQueue, taskQueueField(options.contract)),
217
230
  gracePeriodMs: Config.pinned(durationMs(options.gracePeriod), Config.integer("TEMPORAL_GRACE_PERIOD_MS", {
218
231
  default: DEFAULT_GRACE_MS,
219
232
  min: 0
@@ -269,8 +282,7 @@ const startFailed = (cause) => new RuntimeStartFailed({
269
282
  });
270
283
  const heldByWorker = (cause) => cause instanceof Error && cause.name === "IllegalStateError";
271
284
  const createWorker = (host, connection, config, activities, options, observers, dataConverter) => {
272
- const { taskQueue } = options.contract;
273
- const { namespace } = config;
285
+ const { namespace, taskQueue } = config;
274
286
  return fromThrowable(() => declareActivitiesHandler({
275
287
  contract: options.contract,
276
288
  middleware: activityUnits(host, observers, (key) => forkOf(activities[key]) ?? options.unit?.activity),
@@ -377,7 +389,7 @@ const TemporalModule = (name) => (options) => {
377
389
  * and two slices claiming one key are di's duplicate-provider defect at build.
378
390
  */
379
391
  const TemporalActivities = (contract) => {
380
- return composeByPrefix(TemporalActivitiesPort, WORKFLOW_ACTIVITIES_PREFIX, withUnit);
392
+ return composeByPrefix(TemporalActivitiesPort, `${WORKFLOW_ACTIVITIES_PREFIX}${contract.taskQueue}:`, withUnit);
381
393
  };
382
394
  //#endregion
383
395
  export { ActivityInput, TemporalActivities, TemporalConfig, TemporalConnection, TemporalModule, TemporalRuntime, TemporalUnreachable, TemporalWorkflowActivities };
@@ -1,6 +1,6 @@
1
1
  import { AsyncResult } from "unthrown";
2
- import { ScheduleNotFoundError, TypedScheduleClient, TypedScheduleCreateOptions, WorkflowValidationError } from "@temporal-contract/client";
3
2
  import { ContractDefinition } from "@temporal-contract/contract";
3
+ import { ScheduleNotFoundError, TypedScheduleClient, TypedScheduleCreateOptions, WorkflowValidationError } from "@temporal-contract/client";
4
4
  //#region src/schedule.d.ts
5
5
  /** Whether the schedule had to be created, or was already there and had its spec reconciled. */
6
6
  export type ScheduleOutcome = "created" | "updated";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/temporal-worker",
3
- "version": "0.48.0",
3
+ "version": "0.49.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",
@@ -52,15 +52,15 @@
52
52
  }
53
53
  },
54
54
  "devDependencies": {
55
- "@btravstack/config": "0.48.0",
56
- "@btravstack/core": "0.48.0",
57
- "@btravstack/di": "0.48.0",
55
+ "@btravstack/config": "0.49.0",
56
+ "@btravstack/core": "0.49.0",
57
+ "@btravstack/di": "0.49.0",
58
58
  "@btravstack/internal-test-infra": "0.0.0",
59
- "@btravstack/testing": "0.48.0",
59
+ "@btravstack/testing": "0.49.0",
60
60
  "@btravstack/tsconfig": "0.4.0",
61
- "@temporal-contract/client": "8.0.0-beta.14",
62
- "@temporal-contract/contract": "8.0.0-beta.14",
63
- "@temporal-contract/worker": "8.0.0-beta.14",
61
+ "@temporal-contract/client": "8.0.0-beta.15",
62
+ "@temporal-contract/contract": "8.0.0-beta.15",
63
+ "@temporal-contract/worker": "8.0.0-beta.15",
64
64
  "@temporalio/activity": "1.24.0",
65
65
  "@temporalio/client": "1.24.0",
66
66
  "@temporalio/common": "1.24.0",
@@ -78,12 +78,12 @@
78
78
  "zod": "4.6.7"
79
79
  },
80
80
  "peerDependencies": {
81
- "@btravstack/config": "^0.48.0",
82
- "@btravstack/core": "^0.48.0",
83
- "@btravstack/di": "^0.48.0",
84
- "@temporal-contract/client": "^8.0.0-beta.14",
85
- "@temporal-contract/contract": "^8.0.0-beta.14",
86
- "@temporal-contract/worker": "^8.0.0-beta.14",
81
+ "@btravstack/config": "^0.49.0",
82
+ "@btravstack/core": "^0.49.0",
83
+ "@btravstack/di": "^0.49.0",
84
+ "@temporal-contract/client": "^8.0.0-beta.15",
85
+ "@temporal-contract/contract": "^8.0.0-beta.15",
86
+ "@temporal-contract/worker": "^8.0.0-beta.15",
87
87
  "@temporalio/activity": "^1.24.0",
88
88
  "@temporalio/common": "^1.24.0",
89
89
  "@temporalio/worker": "^1.24.0",