@btravstack/temporal-worker 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +36 -13
- package/dist/index.d.cts +13 -12
- package/dist/index.d.mts +14 -13
- package/dist/index.mjs +37 -14
- package/dist/schedule.cjs +25 -14
- package/dist/schedule.d.cts +18 -13
- package/dist/schedule.d.mts +18 -13
- package/dist/schedule.mjs +25 -14
- package/package.json +11 -12
package/dist/index.cjs
CHANGED
|
@@ -151,7 +151,17 @@ const activityUnits = (host, observers, unit) => (invocation, next) => {
|
|
|
151
151
|
*
|
|
152
152
|
* The workflow id becomes the `traceId` — minted outside this process and steady
|
|
153
153
|
* across every retry, so all attempts join up in a log. An activity with no
|
|
154
|
-
* workflow falls back to the activity id
|
|
154
|
+
* workflow falls back to the activity id; `workflowExecution` is optional
|
|
155
|
+
* because Temporal's own `Info` declares it so, "not set if the activity was
|
|
156
|
+
* not started by a Workflow", which a worker polling a task queue never sees.
|
|
157
|
+
*
|
|
158
|
+
* **A REUSED workflow id merges its runs into one trace**, and that is the
|
|
159
|
+
* trade rather than an oversight: Temporal allows an id again once the previous
|
|
160
|
+
* execution closed, so `order-42` run twice is one `traceId` over two runs.
|
|
161
|
+
* `runId` would separate them and is the wrong correlation for this transport —
|
|
162
|
+
* server-minted, opaque, and not what an operator greps for. A deployment that
|
|
163
|
+
* needs per-run traces gives its workflows unique ids, which is what a Schedule
|
|
164
|
+
* already does.
|
|
155
165
|
*/
|
|
156
166
|
const metaFor = () => {
|
|
157
167
|
const info = (0, _temporalio_activity.activityInfo)();
|
|
@@ -165,9 +175,9 @@ const metaFor = () => {
|
|
|
165
175
|
//#region src/temporal-runtime.ts
|
|
166
176
|
/**
|
|
167
177
|
* Where the Temporal service is, as a service: `temporal()` binds it from the
|
|
168
|
-
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`)
|
|
169
|
-
* `TEMPORAL_NAMESPACE` (default `default`)
|
|
170
|
-
* in the graph may read it.
|
|
178
|
+
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`),
|
|
179
|
+
* `TEMPORAL_NAMESPACE` (default `default`) and the shutdown budgets below —
|
|
180
|
+
* unless pinned, and anything else in the graph may read it.
|
|
171
181
|
*/
|
|
172
182
|
var TemporalConfig = class extends (0, _btravstack_di.Port)("TemporalConfig") {};
|
|
173
183
|
/**
|
|
@@ -206,14 +216,14 @@ const DEFAULT_FORCE_MS = 15e3;
|
|
|
206
216
|
const durationMs = (duration) => duration === void 0 ? void 0 : (0, _temporalio_common.msToNumber)(duration);
|
|
207
217
|
/**
|
|
208
218
|
* The Temporal starter: a module providing the runtime, its configuration
|
|
209
|
-
* (bound from `
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
219
|
+
* (bound from the `TEMPORAL_*` variables unless pinned) and the connection (a
|
|
220
|
+
* resource opened with the scope and closed with it; a service that will not
|
|
221
|
+
* answer is a modeled `TemporalUnreachable`). Import it next to the
|
|
222
|
+
* application, export `TemporalRuntime`, provide the activities — the
|
|
213
223
|
* activities port is a need of this module.
|
|
214
224
|
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
225
|
+
* Each configuration field is pinned on its own: a pinned field reads nothing
|
|
226
|
+
* from the environment, and a field left unpinned still does.
|
|
217
227
|
*/
|
|
218
228
|
const temporal = (options) => {
|
|
219
229
|
const { address, namespace } = options;
|
|
@@ -289,26 +299,39 @@ const createWorker = (host, connection, config, activities, options, observers)
|
|
|
289
299
|
shutdownForceTime: config.forceAfterMs
|
|
290
300
|
}), startFailed)).map((worker) => poll(worker, taskQueue, namespace));
|
|
291
301
|
};
|
|
302
|
+
/**
|
|
303
|
+
* The `Serving` over a started worker.
|
|
304
|
+
*
|
|
305
|
+
* @internal Exported for `temporal-runtime.spec.ts` alone: the `stopped`
|
|
306
|
+
* channel is about a worker's `run()` REJECTING mid-flight, and there is no way
|
|
307
|
+
* to make a real one do that without taking the shared Temporal server down for
|
|
308
|
+
* every other spec. A stub worker is what reaches that arm.
|
|
309
|
+
*/
|
|
292
310
|
const poll = (worker, taskQueue, namespace) => {
|
|
293
311
|
const running = (0, unthrown.fromSafePromise)(worker.run());
|
|
294
312
|
const stopPolling = () => {
|
|
295
313
|
if (worker.getState() === "RUNNING") worker.shutdown();
|
|
296
314
|
};
|
|
297
315
|
let deadline;
|
|
298
|
-
const
|
|
316
|
+
const awaitRun = () => deadline === void 0 ? running : (0, _btravstack_core.releasedBy)(deadline, running);
|
|
317
|
+
let asked = false;
|
|
318
|
+
const withdrawn = () => (0, unthrown.fromSafePromise)(new Promise(() => {}));
|
|
299
319
|
return {
|
|
300
320
|
info: {
|
|
301
321
|
taskQueue,
|
|
302
322
|
namespace
|
|
303
323
|
},
|
|
324
|
+
stopped: () => running.recoverDefect(() => (0, unthrown.Ok)(void 0)).flatMap(() => asked ? withdrawn() : (0, unthrown.OkAsync)()),
|
|
304
325
|
drain: (signal) => {
|
|
326
|
+
asked = true;
|
|
305
327
|
deadline = signal;
|
|
306
328
|
stopPolling();
|
|
307
|
-
return
|
|
329
|
+
return awaitRun();
|
|
308
330
|
},
|
|
309
331
|
stop: () => {
|
|
332
|
+
asked = true;
|
|
310
333
|
stopPolling();
|
|
311
|
-
return
|
|
334
|
+
return awaitRun();
|
|
312
335
|
}
|
|
313
336
|
};
|
|
314
337
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -134,9 +134,9 @@ type WorkflowSource = {
|
|
|
134
134
|
declare const TemporalConfig_base: import("@btravstack/di").PortClass<"TemporalConfig">;
|
|
135
135
|
/**
|
|
136
136
|
* Where the Temporal service is, as a service: `temporal()` binds it from the
|
|
137
|
-
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`)
|
|
138
|
-
* `TEMPORAL_NAMESPACE` (default `default`)
|
|
139
|
-
* in the graph may read it.
|
|
137
|
+
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`),
|
|
138
|
+
* `TEMPORAL_NAMESPACE` (default `default`) and the shutdown budgets below —
|
|
139
|
+
* unless pinned, and anything else in the graph may read it.
|
|
140
140
|
*/
|
|
141
141
|
export declare class TemporalConfig extends TemporalConfig_base<{
|
|
142
142
|
readonly address: string;
|
|
@@ -220,9 +220,10 @@ type TemporalTuning<Unit extends AnyUnitModule | undefined = undefined> = {
|
|
|
220
220
|
readonly gracePeriod?: Duration;
|
|
221
221
|
/**
|
|
222
222
|
* The unit module the worker forks around every activity attempt it
|
|
223
|
-
* dispatches, with
|
|
224
|
-
*
|
|
225
|
-
*
|
|
223
|
+
* dispatches, seeded with the validated input on `ActivityInput(contract)`.
|
|
224
|
+
* Built after the activity is invoked, before it runs; torn down when the
|
|
225
|
+
* unit closes. What it exports is what a piece may declare on its own
|
|
226
|
+
* `unit:` record.
|
|
226
227
|
*/
|
|
227
228
|
readonly unit?: {
|
|
228
229
|
readonly activity?: Unit;
|
|
@@ -240,14 +241,14 @@ type TemporalOptions<C extends ContractDefinition, Unit extends AnyUnitModule |
|
|
|
240
241
|
type Provided = TemporalRuntime | TemporalConfig | TemporalConnection;
|
|
241
242
|
/**
|
|
242
243
|
* The Temporal starter: a module providing the runtime, its configuration
|
|
243
|
-
* (bound from `
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
244
|
+
* (bound from the `TEMPORAL_*` variables unless pinned) and the connection (a
|
|
245
|
+
* resource opened with the scope and closed with it; a service that will not
|
|
246
|
+
* answer is a modeled `TemporalUnreachable`). Import it next to the
|
|
247
|
+
* application, export `TemporalRuntime`, provide the activities — the
|
|
247
248
|
* activities port is a need of this module.
|
|
248
249
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
250
|
+
* Each configuration field is pinned on its own: a pinned field reads nothing
|
|
251
|
+
* from the environment, and a field left unpinned still does.
|
|
251
252
|
*/
|
|
252
253
|
export 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>>;
|
|
253
254
|
//#endregion
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { ConfigInvalid, Env } from "@btravstack/config";
|
|
|
3
3
|
import { Runtime, RuntimePort } from "@btravstack/core";
|
|
4
4
|
import { DeclareActivitiesHandlerOptions, EmptyContext } from "@temporal-contract/worker/activity";
|
|
5
5
|
import { Duration } from "@temporalio/common";
|
|
6
|
-
import { NativeConnection, WorkflowBundleWithSourceMap } from "@temporalio/worker";
|
|
6
|
+
import { NativeConnection, Worker, WorkflowBundleWithSourceMap } from "@temporalio/worker";
|
|
7
7
|
import { ContractDefinition } from "@temporal-contract/contract";
|
|
8
8
|
//#region src/unit.d.ts
|
|
9
9
|
/** What a bound unit module exports — the port instances an activity of that kind may read. */
|
|
@@ -134,9 +134,9 @@ type WorkflowSource = {
|
|
|
134
134
|
declare const TemporalConfig_base: import("@btravstack/di").PortClass<"TemporalConfig">;
|
|
135
135
|
/**
|
|
136
136
|
* Where the Temporal service is, as a service: `temporal()` binds it from the
|
|
137
|
-
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`)
|
|
138
|
-
* `TEMPORAL_NAMESPACE` (default `default`)
|
|
139
|
-
* in the graph may read it.
|
|
137
|
+
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`),
|
|
138
|
+
* `TEMPORAL_NAMESPACE` (default `default`) and the shutdown budgets below —
|
|
139
|
+
* unless pinned, and anything else in the graph may read it.
|
|
140
140
|
*/
|
|
141
141
|
export declare class TemporalConfig extends TemporalConfig_base<{
|
|
142
142
|
readonly address: string;
|
|
@@ -220,9 +220,10 @@ type TemporalTuning<Unit extends AnyUnitModule | undefined = undefined> = {
|
|
|
220
220
|
readonly gracePeriod?: Duration;
|
|
221
221
|
/**
|
|
222
222
|
* The unit module the worker forks around every activity attempt it
|
|
223
|
-
* dispatches, with
|
|
224
|
-
*
|
|
225
|
-
*
|
|
223
|
+
* dispatches, seeded with the validated input on `ActivityInput(contract)`.
|
|
224
|
+
* Built after the activity is invoked, before it runs; torn down when the
|
|
225
|
+
* unit closes. What it exports is what a piece may declare on its own
|
|
226
|
+
* `unit:` record.
|
|
226
227
|
*/
|
|
227
228
|
readonly unit?: {
|
|
228
229
|
readonly activity?: Unit;
|
|
@@ -240,14 +241,14 @@ type TemporalOptions<C extends ContractDefinition, Unit extends AnyUnitModule |
|
|
|
240
241
|
type Provided = TemporalRuntime | TemporalConfig | TemporalConnection;
|
|
241
242
|
/**
|
|
242
243
|
* The Temporal starter: a module providing the runtime, its configuration
|
|
243
|
-
* (bound from `
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
244
|
+
* (bound from the `TEMPORAL_*` variables unless pinned) and the connection (a
|
|
245
|
+
* resource opened with the scope and closed with it; a service that will not
|
|
246
|
+
* answer is a modeled `TemporalUnreachable`). Import it next to the
|
|
247
|
+
* application, export `TemporalRuntime`, provide the activities — the
|
|
247
248
|
* activities port is a need of this module.
|
|
248
249
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
250
|
+
* Each configuration field is pinned on its own: a pinned field reads nothing
|
|
251
|
+
* from the environment, and a field left unpinned still does.
|
|
251
252
|
*/
|
|
252
253
|
export 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>>;
|
|
253
254
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { Observers, RuntimePort, RuntimeStartFailed, noObserver, observe, releas
|
|
|
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
|
-
import { TaggedError, fromPromise, fromSafePromise, fromThrowable } from "unthrown";
|
|
7
|
+
import { Ok, OkAsync, TaggedError, fromPromise, fromSafePromise, fromThrowable } from "unthrown";
|
|
8
8
|
import { activityInfo } from "@temporalio/activity";
|
|
9
9
|
//#region src/unit.ts
|
|
10
10
|
/**
|
|
@@ -150,7 +150,17 @@ const activityUnits = (host, observers, unit) => (invocation, next) => {
|
|
|
150
150
|
*
|
|
151
151
|
* The workflow id becomes the `traceId` — minted outside this process and steady
|
|
152
152
|
* across every retry, so all attempts join up in a log. An activity with no
|
|
153
|
-
* workflow falls back to the activity id
|
|
153
|
+
* workflow falls back to the activity id; `workflowExecution` is optional
|
|
154
|
+
* because Temporal's own `Info` declares it so, "not set if the activity was
|
|
155
|
+
* not started by a Workflow", which a worker polling a task queue never sees.
|
|
156
|
+
*
|
|
157
|
+
* **A REUSED workflow id merges its runs into one trace**, and that is the
|
|
158
|
+
* trade rather than an oversight: Temporal allows an id again once the previous
|
|
159
|
+
* execution closed, so `order-42` run twice is one `traceId` over two runs.
|
|
160
|
+
* `runId` would separate them and is the wrong correlation for this transport —
|
|
161
|
+
* server-minted, opaque, and not what an operator greps for. A deployment that
|
|
162
|
+
* needs per-run traces gives its workflows unique ids, which is what a Schedule
|
|
163
|
+
* already does.
|
|
154
164
|
*/
|
|
155
165
|
const metaFor = () => {
|
|
156
166
|
const info = activityInfo();
|
|
@@ -164,9 +174,9 @@ const metaFor = () => {
|
|
|
164
174
|
//#region src/temporal-runtime.ts
|
|
165
175
|
/**
|
|
166
176
|
* Where the Temporal service is, as a service: `temporal()` binds it from the
|
|
167
|
-
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`)
|
|
168
|
-
* `TEMPORAL_NAMESPACE` (default `default`)
|
|
169
|
-
* in the graph may read it.
|
|
177
|
+
* environment — `TEMPORAL_ADDRESS` (default `127.0.0.1:7233`),
|
|
178
|
+
* `TEMPORAL_NAMESPACE` (default `default`) and the shutdown budgets below —
|
|
179
|
+
* unless pinned, and anything else in the graph may read it.
|
|
170
180
|
*/
|
|
171
181
|
var TemporalConfig = class extends Port("TemporalConfig") {};
|
|
172
182
|
/**
|
|
@@ -205,14 +215,14 @@ const DEFAULT_FORCE_MS = 15e3;
|
|
|
205
215
|
const durationMs = (duration) => duration === void 0 ? void 0 : msToNumber(duration);
|
|
206
216
|
/**
|
|
207
217
|
* The Temporal starter: a module providing the runtime, its configuration
|
|
208
|
-
* (bound from `
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
218
|
+
* (bound from the `TEMPORAL_*` variables unless pinned) and the connection (a
|
|
219
|
+
* resource opened with the scope and closed with it; a service that will not
|
|
220
|
+
* answer is a modeled `TemporalUnreachable`). Import it next to the
|
|
221
|
+
* application, export `TemporalRuntime`, provide the activities — the
|
|
212
222
|
* activities port is a need of this module.
|
|
213
223
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
224
|
+
* Each configuration field is pinned on its own: a pinned field reads nothing
|
|
225
|
+
* from the environment, and a field left unpinned still does.
|
|
216
226
|
*/
|
|
217
227
|
const temporal = (options) => {
|
|
218
228
|
const { address, namespace } = options;
|
|
@@ -288,26 +298,39 @@ const createWorker = (host, connection, config, activities, options, observers)
|
|
|
288
298
|
shutdownForceTime: config.forceAfterMs
|
|
289
299
|
}), startFailed)).map((worker) => poll(worker, taskQueue, namespace));
|
|
290
300
|
};
|
|
301
|
+
/**
|
|
302
|
+
* The `Serving` over a started worker.
|
|
303
|
+
*
|
|
304
|
+
* @internal Exported for `temporal-runtime.spec.ts` alone: the `stopped`
|
|
305
|
+
* channel is about a worker's `run()` REJECTING mid-flight, and there is no way
|
|
306
|
+
* to make a real one do that without taking the shared Temporal server down for
|
|
307
|
+
* every other spec. A stub worker is what reaches that arm.
|
|
308
|
+
*/
|
|
291
309
|
const poll = (worker, taskQueue, namespace) => {
|
|
292
310
|
const running = fromSafePromise(worker.run());
|
|
293
311
|
const stopPolling = () => {
|
|
294
312
|
if (worker.getState() === "RUNNING") worker.shutdown();
|
|
295
313
|
};
|
|
296
314
|
let deadline;
|
|
297
|
-
const
|
|
315
|
+
const awaitRun = () => deadline === void 0 ? running : releasedBy(deadline, running);
|
|
316
|
+
let asked = false;
|
|
317
|
+
const withdrawn = () => fromSafePromise(new Promise(() => {}));
|
|
298
318
|
return {
|
|
299
319
|
info: {
|
|
300
320
|
taskQueue,
|
|
301
321
|
namespace
|
|
302
322
|
},
|
|
323
|
+
stopped: () => running.recoverDefect(() => Ok(void 0)).flatMap(() => asked ? withdrawn() : OkAsync()),
|
|
303
324
|
drain: (signal) => {
|
|
325
|
+
asked = true;
|
|
304
326
|
deadline = signal;
|
|
305
327
|
stopPolling();
|
|
306
|
-
return
|
|
328
|
+
return awaitRun();
|
|
307
329
|
},
|
|
308
330
|
stop: () => {
|
|
331
|
+
asked = true;
|
|
309
332
|
stopPolling();
|
|
310
|
-
return
|
|
333
|
+
return awaitRun();
|
|
311
334
|
}
|
|
312
335
|
};
|
|
313
336
|
};
|
package/dist/schedule.cjs
CHANGED
|
@@ -16,24 +16,29 @@ let _temporal_contract_client = require("@temporal-contract/client");
|
|
|
16
16
|
* This recovers exactly that one error into an `update`. Every other error
|
|
17
17
|
* stays on the channel, still typed.
|
|
18
18
|
*
|
|
19
|
-
* **`spec
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* **`spec`, the action's `workflowType` and `args`, and `policies` are
|
|
20
|
+
* reconciled**; everything else about an existing schedule is left as it
|
|
21
|
+
* stands.
|
|
22
22
|
*
|
|
23
|
+
* - **`args` and `workflowType` are written, and they are checked on the way
|
|
24
|
+
* through.** The typed handle's `update` validates the returned action's
|
|
25
|
+
* `args` against the named workflow's input schema before anything is
|
|
26
|
+
* persisted, and answers `WorkflowValidationError` — the same error `create`
|
|
27
|
+
* answers, already on this channel. So a deploy that changed what the
|
|
28
|
+
* workflow takes is either written or refused, never reported `"updated"`
|
|
29
|
+
* over a server still holding the old action. `workflowType` rides with them
|
|
30
|
+
* because it is what selects the schema those args are checked against.
|
|
23
31
|
* - **`state` is deliberately preserved.** A schedule an operator paused stays
|
|
24
32
|
* paused across a deploy, because unpausing it is a decision a person made
|
|
25
33
|
* and a deploy is not the place to reverse it.
|
|
26
|
-
* - **`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* shut. A deploy that changes what a schedule RUNS — its args, its workflow,
|
|
32
|
-
* its policies — should say so explicitly: delete the schedule and create it,
|
|
33
|
-
* or reach `getHandle(id).update(...)` directly and own the shape.
|
|
34
|
+
* - **`memo`, `searchAttributes` and the action's own overrides are
|
|
35
|
+
* preserved**, because rebuilding the action wholesale means reproducing
|
|
36
|
+
* `create`'s own assembly here — the task queue off the contract, the search
|
|
37
|
+
* attribute translation, eight optional overrides — which is a copy that
|
|
38
|
+
* drifts. A deploy changing one of those deletes the schedule and creates it.
|
|
34
39
|
*
|
|
35
|
-
* So: after this call the schedule FIRES when the arguments say
|
|
36
|
-
*
|
|
40
|
+
* So: after this call the schedule FIRES when the arguments say, and RUNS what
|
|
41
|
+
* they say.
|
|
37
42
|
*
|
|
38
43
|
* ```ts
|
|
39
44
|
* await ensureSchedule(client.for(orderContract).schedule, "sweepStaleOrders", {
|
|
@@ -45,7 +50,13 @@ let _temporal_contract_client = require("@temporal-contract/client");
|
|
|
45
50
|
*/
|
|
46
51
|
const ensureSchedule = (schedules, workflow, options) => schedules.create(workflow, options).map(() => "created").flatMapErrCases((matcher) => matcher.with(unthrown.P.tag(_temporal_contract_client.SCHEDULE_ALREADY_EXISTS_ERROR_TAG), () => schedules.getHandle(options.scheduleId).update((previous) => ({
|
|
47
52
|
...previous,
|
|
48
|
-
spec: options.spec
|
|
53
|
+
spec: options.spec,
|
|
54
|
+
action: {
|
|
55
|
+
...previous.action,
|
|
56
|
+
workflowType: workflow,
|
|
57
|
+
args: [options.args]
|
|
58
|
+
},
|
|
59
|
+
...options.policies === void 0 ? {} : { policies: options.policies }
|
|
49
60
|
})).map(() => "updated")).with(unthrown.P.tag("@temporal-contract/WorkflowNotInContractError"), (error) => (0, unthrown.ErrAsync)(error)).with(unthrown.P.tag("@temporal-contract/WorkflowValidationError"), (error) => (0, unthrown.ErrAsync)(error)));
|
|
50
61
|
//#endregion
|
|
51
62
|
exports.ensureSchedule = ensureSchedule;
|
package/dist/schedule.d.cts
CHANGED
|
@@ -18,24 +18,29 @@ export type ScheduleOutcome = "created" | "updated";
|
|
|
18
18
|
* This recovers exactly that one error into an `update`. Every other error
|
|
19
19
|
* stays on the channel, still typed.
|
|
20
20
|
*
|
|
21
|
-
* **`spec
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* **`spec`, the action's `workflowType` and `args`, and `policies` are
|
|
22
|
+
* reconciled**; everything else about an existing schedule is left as it
|
|
23
|
+
* stands.
|
|
24
24
|
*
|
|
25
|
+
* - **`args` and `workflowType` are written, and they are checked on the way
|
|
26
|
+
* through.** The typed handle's `update` validates the returned action's
|
|
27
|
+
* `args` against the named workflow's input schema before anything is
|
|
28
|
+
* persisted, and answers `WorkflowValidationError` — the same error `create`
|
|
29
|
+
* answers, already on this channel. So a deploy that changed what the
|
|
30
|
+
* workflow takes is either written or refused, never reported `"updated"`
|
|
31
|
+
* over a server still holding the old action. `workflowType` rides with them
|
|
32
|
+
* because it is what selects the schema those args are checked against.
|
|
25
33
|
* - **`state` is deliberately preserved.** A schedule an operator paused stays
|
|
26
34
|
* paused across a deploy, because unpausing it is a decision a person made
|
|
27
35
|
* and a deploy is not the place to reverse it.
|
|
28
|
-
* - **`
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* shut. A deploy that changes what a schedule RUNS — its args, its workflow,
|
|
34
|
-
* its policies — should say so explicitly: delete the schedule and create it,
|
|
35
|
-
* or reach `getHandle(id).update(...)` directly and own the shape.
|
|
36
|
+
* - **`memo`, `searchAttributes` and the action's own overrides are
|
|
37
|
+
* preserved**, because rebuilding the action wholesale means reproducing
|
|
38
|
+
* `create`'s own assembly here — the task queue off the contract, the search
|
|
39
|
+
* attribute translation, eight optional overrides — which is a copy that
|
|
40
|
+
* drifts. A deploy changing one of those deletes the schedule and creates it.
|
|
36
41
|
*
|
|
37
|
-
* So: after this call the schedule FIRES when the arguments say
|
|
38
|
-
*
|
|
42
|
+
* So: after this call the schedule FIRES when the arguments say, and RUNS what
|
|
43
|
+
* they say.
|
|
39
44
|
*
|
|
40
45
|
* ```ts
|
|
41
46
|
* await ensureSchedule(client.for(orderContract).schedule, "sweepStaleOrders", {
|
package/dist/schedule.d.mts
CHANGED
|
@@ -18,24 +18,29 @@ export type ScheduleOutcome = "created" | "updated";
|
|
|
18
18
|
* This recovers exactly that one error into an `update`. Every other error
|
|
19
19
|
* stays on the channel, still typed.
|
|
20
20
|
*
|
|
21
|
-
* **`spec
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* **`spec`, the action's `workflowType` and `args`, and `policies` are
|
|
22
|
+
* reconciled**; everything else about an existing schedule is left as it
|
|
23
|
+
* stands.
|
|
24
24
|
*
|
|
25
|
+
* - **`args` and `workflowType` are written, and they are checked on the way
|
|
26
|
+
* through.** The typed handle's `update` validates the returned action's
|
|
27
|
+
* `args` against the named workflow's input schema before anything is
|
|
28
|
+
* persisted, and answers `WorkflowValidationError` — the same error `create`
|
|
29
|
+
* answers, already on this channel. So a deploy that changed what the
|
|
30
|
+
* workflow takes is either written or refused, never reported `"updated"`
|
|
31
|
+
* over a server still holding the old action. `workflowType` rides with them
|
|
32
|
+
* because it is what selects the schema those args are checked against.
|
|
25
33
|
* - **`state` is deliberately preserved.** A schedule an operator paused stays
|
|
26
34
|
* paused across a deploy, because unpausing it is a decision a person made
|
|
27
35
|
* and a deploy is not the place to reverse it.
|
|
28
|
-
* - **`
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* shut. A deploy that changes what a schedule RUNS — its args, its workflow,
|
|
34
|
-
* its policies — should say so explicitly: delete the schedule and create it,
|
|
35
|
-
* or reach `getHandle(id).update(...)` directly and own the shape.
|
|
36
|
+
* - **`memo`, `searchAttributes` and the action's own overrides are
|
|
37
|
+
* preserved**, because rebuilding the action wholesale means reproducing
|
|
38
|
+
* `create`'s own assembly here — the task queue off the contract, the search
|
|
39
|
+
* attribute translation, eight optional overrides — which is a copy that
|
|
40
|
+
* drifts. A deploy changing one of those deletes the schedule and creates it.
|
|
36
41
|
*
|
|
37
|
-
* So: after this call the schedule FIRES when the arguments say
|
|
38
|
-
*
|
|
42
|
+
* So: after this call the schedule FIRES when the arguments say, and RUNS what
|
|
43
|
+
* they say.
|
|
39
44
|
*
|
|
40
45
|
* ```ts
|
|
41
46
|
* await ensureSchedule(client.for(orderContract).schedule, "sweepStaleOrders", {
|
package/dist/schedule.mjs
CHANGED
|
@@ -15,24 +15,29 @@ import { SCHEDULE_ALREADY_EXISTS_ERROR_TAG } from "@temporal-contract/client";
|
|
|
15
15
|
* This recovers exactly that one error into an `update`. Every other error
|
|
16
16
|
* stays on the channel, still typed.
|
|
17
17
|
*
|
|
18
|
-
* **`spec
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* **`spec`, the action's `workflowType` and `args`, and `policies` are
|
|
19
|
+
* reconciled**; everything else about an existing schedule is left as it
|
|
20
|
+
* stands.
|
|
21
21
|
*
|
|
22
|
+
* - **`args` and `workflowType` are written, and they are checked on the way
|
|
23
|
+
* through.** The typed handle's `update` validates the returned action's
|
|
24
|
+
* `args` against the named workflow's input schema before anything is
|
|
25
|
+
* persisted, and answers `WorkflowValidationError` — the same error `create`
|
|
26
|
+
* answers, already on this channel. So a deploy that changed what the
|
|
27
|
+
* workflow takes is either written or refused, never reported `"updated"`
|
|
28
|
+
* over a server still holding the old action. `workflowType` rides with them
|
|
29
|
+
* because it is what selects the schema those args are checked against.
|
|
22
30
|
* - **`state` is deliberately preserved.** A schedule an operator paused stays
|
|
23
31
|
* paused across a deploy, because unpausing it is a decision a person made
|
|
24
32
|
* and a deploy is not the place to reverse it.
|
|
25
|
-
* - **`
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* shut. A deploy that changes what a schedule RUNS — its args, its workflow,
|
|
31
|
-
* its policies — should say so explicitly: delete the schedule and create it,
|
|
32
|
-
* or reach `getHandle(id).update(...)` directly and own the shape.
|
|
33
|
+
* - **`memo`, `searchAttributes` and the action's own overrides are
|
|
34
|
+
* preserved**, because rebuilding the action wholesale means reproducing
|
|
35
|
+
* `create`'s own assembly here — the task queue off the contract, the search
|
|
36
|
+
* attribute translation, eight optional overrides — which is a copy that
|
|
37
|
+
* drifts. A deploy changing one of those deletes the schedule and creates it.
|
|
33
38
|
*
|
|
34
|
-
* So: after this call the schedule FIRES when the arguments say
|
|
35
|
-
*
|
|
39
|
+
* So: after this call the schedule FIRES when the arguments say, and RUNS what
|
|
40
|
+
* they say.
|
|
36
41
|
*
|
|
37
42
|
* ```ts
|
|
38
43
|
* await ensureSchedule(client.for(orderContract).schedule, "sweepStaleOrders", {
|
|
@@ -44,7 +49,13 @@ import { SCHEDULE_ALREADY_EXISTS_ERROR_TAG } from "@temporal-contract/client";
|
|
|
44
49
|
*/
|
|
45
50
|
const ensureSchedule = (schedules, workflow, options) => schedules.create(workflow, options).map(() => "created").flatMapErrCases((matcher) => matcher.with(P.tag(SCHEDULE_ALREADY_EXISTS_ERROR_TAG), () => schedules.getHandle(options.scheduleId).update((previous) => ({
|
|
46
51
|
...previous,
|
|
47
|
-
spec: options.spec
|
|
52
|
+
spec: options.spec,
|
|
53
|
+
action: {
|
|
54
|
+
...previous.action,
|
|
55
|
+
workflowType: workflow,
|
|
56
|
+
args: [options.args]
|
|
57
|
+
},
|
|
58
|
+
...options.policies === void 0 ? {} : { policies: options.policies }
|
|
48
59
|
})).map(() => "updated")).with(P.tag("@temporal-contract/WorkflowNotInContractError"), (error) => ErrAsync(error)).with(P.tag("@temporal-contract/WorkflowValidationError"), (error) => ErrAsync(error)));
|
|
49
60
|
//#endregion
|
|
50
61
|
export { ensureSchedule };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/temporal-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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,6 +54,11 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
+
"@btravstack/config": "0.16.0",
|
|
58
|
+
"@btravstack/core": "0.16.0",
|
|
59
|
+
"@btravstack/di": "0.16.0",
|
|
60
|
+
"@btravstack/internal-test-infra": "0.0.0",
|
|
61
|
+
"@btravstack/testing": "0.16.0",
|
|
57
62
|
"@btravstack/tsconfig": "0.4.0",
|
|
58
63
|
"@temporal-contract/client": "8.0.0-beta.9",
|
|
59
64
|
"@temporal-contract/contract": "8.0.0-beta.9",
|
|
@@ -70,24 +75,19 @@
|
|
|
70
75
|
"typescript": "7.0.2",
|
|
71
76
|
"unthrown": "5.8.0",
|
|
72
77
|
"vitest": "4.1.11",
|
|
73
|
-
"zod": "4.5.4"
|
|
74
|
-
"@btravstack/config": "0.14.0",
|
|
75
|
-
"@btravstack/core": "0.14.0",
|
|
76
|
-
"@btravstack/di": "0.14.0",
|
|
77
|
-
"@btravstack/internal-test-infra": "0.0.0",
|
|
78
|
-
"@btravstack/testing": "0.14.0"
|
|
78
|
+
"zod": "4.5.4"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
+
"@btravstack/config": "^0.16.0",
|
|
82
|
+
"@btravstack/core": "^0.16.0",
|
|
83
|
+
"@btravstack/di": "^0.16.0",
|
|
81
84
|
"@temporal-contract/client": "^8.0.0-beta.9",
|
|
82
85
|
"@temporal-contract/contract": "^8.0.0-beta.9",
|
|
83
86
|
"@temporal-contract/worker": "^8.0.0-beta.9",
|
|
84
87
|
"@temporalio/activity": "^1.22.0",
|
|
85
88
|
"@temporalio/common": "^1.22.0",
|
|
86
89
|
"@temporalio/worker": "^1.22.0",
|
|
87
|
-
"unthrown": "^5.0.0"
|
|
88
|
-
"@btravstack/config": "^0.14.0",
|
|
89
|
-
"@btravstack/core": "^0.14.0",
|
|
90
|
-
"@btravstack/di": "^0.14.0"
|
|
90
|
+
"unthrown": "^5.0.0"
|
|
91
91
|
},
|
|
92
92
|
"peerDependenciesMeta": {
|
|
93
93
|
"@temporal-contract/client": {
|
|
@@ -101,7 +101,6 @@
|
|
|
101
101
|
"build": "tsdown src/index.ts src/schedule.ts --format cjs,esm --dts --clean",
|
|
102
102
|
"dev": "tsdown src/index.ts src/schedule.ts --format cjs,esm --dts --watch",
|
|
103
103
|
"test": "vitest run --coverage",
|
|
104
|
-
"test:types": "tsc --noEmit -p tsconfig.test-d.json",
|
|
105
104
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test-d.json"
|
|
106
105
|
}
|
|
107
106
|
}
|