@sanoma-ai/workflows 0.6.0 → 0.7.1
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 +52 -14
- package/dist/apply.d.ts +2 -1
- package/dist/apply.js +29 -7
- package/dist/apply.js.map +1 -1
- package/dist/approvals.d.ts +27 -2
- package/dist/approvals.js +73 -9
- package/dist/approvals.js.map +1 -1
- package/dist/call.d.ts +3 -2
- package/dist/call.js +38 -30
- package/dist/call.js.map +1 -1
- package/dist/cedar.js +24 -9
- package/dist/cedar.js.map +1 -1
- package/dist/change.d.ts +219 -0
- package/dist/change.js +150 -0
- package/dist/change.js.map +1 -0
- package/dist/chats.d.ts +43 -0
- package/dist/chats.js +48 -0
- package/dist/chats.js.map +1 -0
- package/dist/client.d.ts +52 -6
- package/dist/client.js +88 -47
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +75 -8
- package/dist/config.js +103 -13
- package/dist/config.js.map +1 -1
- package/dist/constructors.d.ts +37 -1
- package/dist/constructors.js +6 -2
- package/dist/constructors.js.map +1 -1
- package/dist/database.d.ts +83 -0
- package/dist/database.js +92 -0
- package/dist/database.js.map +1 -0
- package/dist/db.d.ts +19 -1
- package/dist/db.js +51 -7
- package/dist/db.js.map +1 -1
- package/dist/deadlines.d.ts +91 -0
- package/dist/deadlines.js +165 -0
- package/dist/deadlines.js.map +1 -0
- package/dist/define.d.ts +25 -4
- package/dist/define.js +40 -0
- package/dist/define.js.map +1 -1
- package/dist/demo.d.ts +28 -0
- package/dist/demo.js +72 -0
- package/dist/demo.js.map +1 -0
- package/dist/describe.d.ts +5 -2
- package/dist/describe.js +10 -6
- package/dist/describe.js.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/fake.d.ts +34 -0
- package/dist/fake.js +134 -1
- package/dist/fake.js.map +1 -1
- package/dist/grid.d.ts +34 -0
- package/dist/grid.js +62 -0
- package/dist/grid.js.map +1 -0
- package/dist/host.d.ts +12 -2
- package/dist/host.js +2 -2
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +11 -2
- package/dist/ledger.js.map +1 -1
- package/dist/lint.js +2 -2
- package/dist/lint.js.map +1 -1
- package/dist/op.d.ts +21 -2
- package/dist/op.js.map +1 -1
- package/dist/person.d.ts +5 -0
- package/dist/person.js +3 -1
- package/dist/person.js.map +1 -1
- package/dist/policy.d.ts +7 -1
- package/dist/policy.js +2 -1
- package/dist/policy.js.map +1 -1
- package/dist/resource.d.ts +13 -1
- package/dist/resource.js +9 -1
- package/dist/resource.js.map +1 -1
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +15 -4
- package/dist/resources.js.map +1 -1
- package/dist/run.d.ts +5 -0
- package/dist/run.js.map +1 -1
- package/dist/sandbox.d.ts +38 -10
- package/dist/sandbox.js +66 -35
- package/dist/sandbox.js.map +1 -1
- package/dist/scenario.d.ts +18 -1
- package/dist/scenario.js +77 -27
- package/dist/scenario.js.map +1 -1
- package/dist/scenarios.d.ts +10 -1
- package/dist/scenarios.js +19 -4
- package/dist/scenarios.js.map +1 -1
- package/dist/schedules.d.ts +66 -0
- package/dist/schedules.js +158 -0
- package/dist/schedules.js.map +1 -0
- package/dist/schema.d.ts +6 -0
- package/dist/schema.js +13 -3
- package/dist/schema.js.map +1 -1
- package/dist/shared.d.ts +30 -2
- package/dist/shared.js +42 -4
- package/dist/shared.js.map +1 -1
- package/dist/source.d.ts +185 -2
- package/dist/source.js +9 -0
- package/dist/source.js.map +1 -1
- package/dist/supervisor.d.ts +39 -0
- package/dist/supervisor.js +68 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/version.d.ts +3 -1
- package/dist/version.js +3 -1
- package/dist/version.js.map +1 -1
- package/dist/wakeup-driver.d.ts +128 -0
- package/dist/wakeup-driver.js +216 -0
- package/dist/wakeup-driver.js.map +1 -0
- package/dist/wakeup-service.d.ts +83 -0
- package/dist/wakeup-service.js +415 -0
- package/dist/wakeup-service.js.map +1 -0
- package/dist/wakeup-type.d.ts +43 -0
- package/dist/wakeup-type.js +80 -0
- package/dist/wakeup-type.js.map +1 -0
- package/dist/wakeup.d.ts +77 -0
- package/dist/wakeup.js +78 -0
- package/dist/wakeup.js.map +1 -0
- package/dist/worker.d.ts +39 -1
- package/dist/worker.js +125 -38
- package/dist/worker.js.map +1 -1
- package/oxlint.json +3 -1
- package/package.json +10 -2
- package/src/apply.ts +25 -6
- package/src/approvals.ts +89 -16
- package/src/call.ts +40 -29
- package/src/cedar.ts +24 -6
- package/src/change.ts +222 -0
- package/src/chats.ts +114 -0
- package/src/client.ts +145 -55
- package/src/config.ts +149 -21
- package/src/constructors.ts +8 -2
- package/src/database.ts +152 -0
- package/src/db.ts +58 -9
- package/src/deadlines.ts +228 -0
- package/src/define.ts +71 -4
- package/src/demo.ts +87 -0
- package/src/describe.ts +18 -6
- package/src/errors.ts +14 -0
- package/src/fake.ts +149 -0
- package/src/grid.ts +100 -0
- package/src/host.ts +12 -4
- package/src/index.ts +66 -3
- package/src/ledger.ts +15 -3
- package/src/lint.ts +2 -2
- package/src/op.ts +22 -2
- package/src/person.ts +4 -1
- package/src/policy.ts +7 -1
- package/src/resource.ts +22 -3
- package/src/resources.ts +17 -4
- package/src/run.ts +5 -0
- package/src/sandbox.ts +97 -37
- package/src/scenario.ts +97 -28
- package/src/scenarios.ts +21 -5
- package/src/schedules.ts +206 -0
- package/src/schema.ts +16 -3
- package/src/shared.ts +51 -4
- package/src/source.ts +176 -3
- package/src/supervisor.ts +102 -0
- package/src/version.ts +3 -1
- package/src/wakeup-driver.ts +270 -0
- package/src/wakeup-service.ts +504 -0
- package/src/wakeup-type.ts +90 -0
- package/src/wakeup.ts +139 -0
- package/src/worker.ts +176 -43
package/dist/db.js
CHANGED
|
@@ -9,6 +9,16 @@ export const CREDENTIALS_TABLE = "sanoma_credentials";
|
|
|
9
9
|
export const LEDGER_TABLE = "sanoma_ledger";
|
|
10
10
|
/** Where the collections keep their records (`collection.ts`): one row per collection and key, the record as JSON. */
|
|
11
11
|
export const DATA_TABLE = "sanoma_data";
|
|
12
|
+
/**
|
|
13
|
+
* The instance's side of its wakeup: one row, made with the table. `at` is the deadline last
|
|
14
|
+
* registered, `stopped_at` when the worker last stopped itself (the schedules backfill from it),
|
|
15
|
+
* `last_error` why the last registration failed.
|
|
16
|
+
*/
|
|
17
|
+
export const WAKEUP_TABLE = "sanoma_wakeup";
|
|
18
|
+
/** The operator agent's chats (`chats.ts`): who each is for, its engine and its draft. */
|
|
19
|
+
export const CHATS_TABLE = "sanoma_chats";
|
|
20
|
+
/** Each chat's messages, the AI SDK's UI messages as JSON, in `seq` order. */
|
|
21
|
+
export const CHAT_MESSAGES_TABLE = "sanoma_chat_messages";
|
|
12
22
|
/** The runtime's tables, each with the word the messages name it by. */
|
|
13
23
|
const TABLES = [
|
|
14
24
|
[
|
|
@@ -29,6 +39,22 @@ const TABLES = [
|
|
|
29
39
|
`CREATE TABLE IF NOT EXISTS ${DATA_TABLE} (collection text NOT NULL, key text NOT NULL, record jsonb NOT NULL, ` +
|
|
30
40
|
"updated_at timestamptz NOT NULL DEFAULT now(), updated_by text NOT NULL, PRIMARY KEY (collection, key))",
|
|
31
41
|
],
|
|
42
|
+
[
|
|
43
|
+
"wakeup",
|
|
44
|
+
`CREATE TABLE IF NOT EXISTS ${WAKEUP_TABLE} (id integer PRIMARY KEY CHECK (id = 1), at timestamptz, ` +
|
|
45
|
+
`stopped_at timestamptz, last_error text); INSERT INTO ${WAKEUP_TABLE} (id) VALUES (1) ON CONFLICT (id) DO NOTHING`,
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"chats",
|
|
49
|
+
`CREATE TABLE IF NOT EXISTS ${CHATS_TABLE} (id text PRIMARY KEY, person text NOT NULL, engine text NOT NULL, ` +
|
|
50
|
+
"draft_id text, title text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()); " +
|
|
51
|
+
`CREATE INDEX IF NOT EXISTS ${CHATS_TABLE}_person ON ${CHATS_TABLE} (person, created_at)`,
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"chat messages",
|
|
55
|
+
`CREATE TABLE IF NOT EXISTS ${CHAT_MESSAGES_TABLE} (chat_id text NOT NULL REFERENCES ${CHATS_TABLE} (id) ` +
|
|
56
|
+
"ON DELETE CASCADE, id text NOT NULL, seq integer NOT NULL, message jsonb NOT NULL, PRIMARY KEY (chat_id, id))",
|
|
57
|
+
],
|
|
32
58
|
];
|
|
33
59
|
/** The Postgres error code of `err`, or an empty string when it has none. */
|
|
34
60
|
export const pgCode = (err) => err?.code?.toString() ?? "";
|
|
@@ -116,7 +142,7 @@ const prepared = new Map();
|
|
|
116
142
|
* Makes the database at `databaseUrl` ready for the runtime, once per process and URL: the
|
|
117
143
|
* database itself and DBOS's tables when they are not there yet (as a worker's launch makes
|
|
118
144
|
* them; credentials may be set, and a ledger read, before any worker has run), then the
|
|
119
|
-
* credentials, ledger and
|
|
145
|
+
* credentials, ledger, data, wakeup and chat tables, and its idle-transaction timeout (`guardIdleTransactions`).
|
|
120
146
|
* Two processes preparing one database both succeed: the second waits for the first
|
|
121
147
|
* (`lockPreparation`). A preparation that fails is forgotten, so the next call tries again.
|
|
122
148
|
*/
|
|
@@ -162,12 +188,7 @@ async function prepare(databaseUrl) {
|
|
|
162
188
|
* cannot see (the lock's holder is this connection, not the migration's).
|
|
163
189
|
*/
|
|
164
190
|
async function lockPreparation(databaseUrl) {
|
|
165
|
-
const conn = await
|
|
166
|
-
if (pgCode(err) !== NO_DATABASE)
|
|
167
|
-
throw err;
|
|
168
|
-
await createDatabase(databaseUrl);
|
|
169
|
-
return connect(databaseUrl);
|
|
170
|
-
});
|
|
191
|
+
const conn = await connectMaking(databaseUrl);
|
|
171
192
|
try {
|
|
172
193
|
const ask = "SELECT pg_try_advisory_lock(hashtext('sanoma.prepareDatabase')) AS locked";
|
|
173
194
|
while (!(await conn.query(ask)).rows[0].locked)
|
|
@@ -179,6 +200,15 @@ async function lockPreparation(databaseUrl) {
|
|
|
179
200
|
throw err;
|
|
180
201
|
}
|
|
181
202
|
}
|
|
203
|
+
/** A connection to the database at `databaseUrl`, made first when it is not there (`createDatabase`). */
|
|
204
|
+
export async function connectMaking(databaseUrl) {
|
|
205
|
+
return connect(databaseUrl).catch(async (err) => {
|
|
206
|
+
if (pgCode(err) !== NO_DATABASE)
|
|
207
|
+
throw err;
|
|
208
|
+
await createDatabase(databaseUrl);
|
|
209
|
+
return connect(databaseUrl);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
182
212
|
async function connect(databaseUrl) {
|
|
183
213
|
const conn = new Client({ connectionString: databaseUrl, connectionTimeoutMillis: 10_000 });
|
|
184
214
|
// An error nobody listens for ends the process; the query in flight fails with it.
|
|
@@ -243,4 +273,18 @@ export async function createTable(db, ddl, what) {
|
|
|
243
273
|
throw new Error(`could not prepare the ${what} table: ${errorMessage(err)}`, { cause: err });
|
|
244
274
|
}
|
|
245
275
|
}
|
|
276
|
+
// The wakeup row's writes: one worker registers per instance (the supervisor runs one at a time,
|
|
277
|
+
// and a deploy stops the old container before the new one starts), so none names a worker.
|
|
278
|
+
/** Records that the wakeup took `at`. */
|
|
279
|
+
export async function recordWakeupRegistration(db, at) {
|
|
280
|
+
await db.query(`UPDATE ${WAKEUP_TABLE} SET at = $1, last_error = NULL WHERE id = 1`, [at]);
|
|
281
|
+
}
|
|
282
|
+
/** Records why the last registration failed. */
|
|
283
|
+
export async function recordWakeupError(db, error) {
|
|
284
|
+
await db.query(`UPDATE ${WAKEUP_TABLE} SET last_error = $1 WHERE id = 1`, [error]);
|
|
285
|
+
}
|
|
286
|
+
/** Records that the worker stops itself now. */
|
|
287
|
+
export async function recordWakeupStop(db) {
|
|
288
|
+
await db.query(`UPDATE ${WAKEUP_TABLE} SET stopped_at = now() WHERE id = 1`);
|
|
289
|
+
}
|
|
246
290
|
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C,mDAAmD;AACnD,MAAM,CAAC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AACtD,mDAAmD;AACnD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAC5C,sHAAsH;AACtH,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Db } from "./db.ts";
|
|
2
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
3
|
+
import type { DriverFn, Op } from "./op.ts";
|
|
4
|
+
import type { Secrets } from "./secret.ts";
|
|
5
|
+
import type { WakeupRegistration } from "./wakeup.ts";
|
|
6
|
+
/** What a run blocked in a `ctx` wait waits for: a sleep, until its deadline, or an approval's decision, which has none. */
|
|
7
|
+
export type Wait = {
|
|
8
|
+
kind: "sleep";
|
|
9
|
+
until: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "approval";
|
|
12
|
+
};
|
|
13
|
+
/** A deadline besides the sleeping runs', such as a schedule's next fire; undefined for none. */
|
|
14
|
+
export type DeadlineSource = () => Date | undefined;
|
|
15
|
+
/** What a worker shows of its runs' deadlines (`Worker.deadlines`). */
|
|
16
|
+
export interface Deadlines {
|
|
17
|
+
/**
|
|
18
|
+
* Adds a deadline besides the sleeping runs', read each time `nextDeadline` is: a fixed time
|
|
19
|
+
* (a schedule's next fire), never one counted from now, which would never let the worker stop.
|
|
20
|
+
*/
|
|
21
|
+
addSource(source: DeadlineSource): void;
|
|
22
|
+
/** The earliest deadline: a sleeping run's `until`, or a source's. Undefined when there is none. */
|
|
23
|
+
nextDeadline(): Date | undefined;
|
|
24
|
+
/** How many runs are inside their body and in no wrapped wait: executing, or replaying. */
|
|
25
|
+
readonly busy: number;
|
|
26
|
+
/** What the run waits for while it is blocked in a wrapped wait; undefined otherwise. */
|
|
27
|
+
waitOf(runId: string): Wait | undefined;
|
|
28
|
+
}
|
|
29
|
+
/** The registry a worker's runs report to, through `running` and `waiting`. */
|
|
30
|
+
export interface DeadlineRegistry extends Deadlines {
|
|
31
|
+
/** Runs a run's body, counted busy but while it is in `waiting`. */
|
|
32
|
+
running<T>(runId: string, body: () => Promise<T>): Promise<T>;
|
|
33
|
+
/** Runs `fn`, the live wait itself (`DBOS.sleep`, `DBOS.recv`), with the run counted as waiting for `wait`. */
|
|
34
|
+
waiting<T>(runId: string, wait: Wait, fn: () => Promise<T>): Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
/** A registry of its own for each worker: two copies of the package in one process never share one. */
|
|
37
|
+
export declare function deadlineRegistry(): DeadlineRegistry;
|
|
38
|
+
/** The worker `idle` asks about: the app, the version it runs, and its queue. */
|
|
39
|
+
export type IdleScope = Pick<ResolvedConfig, "appName" | "version" | "queueName">;
|
|
40
|
+
/**
|
|
41
|
+
* True when a run on `queue` (none for a run started outside a queue) is this worker's to run: on
|
|
42
|
+
* its own queue, or on one of DBOS's, where recovery puts runs.
|
|
43
|
+
*/
|
|
44
|
+
export declare const onOwnQueue: (queue: string | undefined, queueName: string) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* True when nothing this worker would run can run within `horizonMs`: no run of its is executing
|
|
47
|
+
* or replaying; none of its version is queued (or queued without one, while it is the latest) on
|
|
48
|
+
* its queue or DBOS's own (recovery queues there); each PENDING run of its version is blocked in
|
|
49
|
+
* a wrapped wait; and the next deadline is past the horizon. A run of another version or queue
|
|
50
|
+
* is not this worker's to run, so it never holds the worker up.
|
|
51
|
+
*/
|
|
52
|
+
export declare function idle(deadlines: Deadlines, scope: IdleScope, horizonMs: number): Promise<boolean>;
|
|
53
|
+
/** What `onStopped` hears when the worker stopped itself: the deadline it registered, null for none. */
|
|
54
|
+
export interface SelfStop {
|
|
55
|
+
at: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** What the stop rule acts on: the wakeup row, the wakeup's register operation, and the worker. */
|
|
58
|
+
export interface StopRule {
|
|
59
|
+
/** The wakeup row's database. */
|
|
60
|
+
db: () => Promise<Db>;
|
|
61
|
+
/** The declared wakeup's binding, else its declaration's id: what scopes a registration's idempotency key to this instance. */
|
|
62
|
+
wakeup: string;
|
|
63
|
+
/** The wakeup type's register operation, and its driver (none when the connector attaches none). */
|
|
64
|
+
register: {
|
|
65
|
+
op: Op;
|
|
66
|
+
fn: DriverFn | undefined;
|
|
67
|
+
};
|
|
68
|
+
secrets: Secrets;
|
|
69
|
+
deadlines: Deadlines;
|
|
70
|
+
idle: () => Promise<boolean>;
|
|
71
|
+
everyMs: number;
|
|
72
|
+
/** Stops the worker (`Worker.stop`). */
|
|
73
|
+
stop: () => Promise<void>;
|
|
74
|
+
onStopped?: ((stopped: SelfStop) => void) | undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks every `everyMs` whether the worker is `idle`; when it is, registers the next deadline
|
|
78
|
+
* with the wakeup, replacing what it held (none when there is no deadline at all), and stops the
|
|
79
|
+
* worker, then tells `onStopped`. A failed registration is recorded on the row and logged once for
|
|
80
|
+
* each new message, and the worker keeps running: the next check tries again. A run that arrives
|
|
81
|
+
* while it registers keeps the worker up, since the worker must be idle twice, with the same
|
|
82
|
+
* deadline, before it stops. `close` ends the checks.
|
|
83
|
+
*/
|
|
84
|
+
export declare function stopWhenIdle(rule: StopRule): {
|
|
85
|
+
close(): void;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Calls the wakeup's register operation, as `SanomaClient.call` calls a driver: its input parsed,
|
|
89
|
+
* a `CallContext` of the runtime's, and no ledger record, which is a run's.
|
|
90
|
+
*/
|
|
91
|
+
export declare function registerWakeup({ register: { op, fn }, secrets, wakeup }: Pick<StopRule, "register" | "secrets" | "wakeup">, registration: WakeupRegistration): Promise<void>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { DBOS } from "@dbos-inc/dbos-sdk";
|
|
3
|
+
import { dbosStatusesOf } from "./client.js";
|
|
4
|
+
import { recordWakeupError, recordWakeupRegistration, recordWakeupStop } from "./db.js";
|
|
5
|
+
import { warn } from "./log.js";
|
|
6
|
+
import { errorMessage, WAKEUP_AGENT } from "./shared.js";
|
|
7
|
+
/** A registry of its own for each worker: two copies of the package in one process never share one. */
|
|
8
|
+
export function deadlineRegistry() {
|
|
9
|
+
// Each run inside its body, with what it waits for while it is blocked.
|
|
10
|
+
const runs = new Map();
|
|
11
|
+
const sources = [];
|
|
12
|
+
return {
|
|
13
|
+
addSource(source) {
|
|
14
|
+
sources.push(source);
|
|
15
|
+
},
|
|
16
|
+
nextDeadline() {
|
|
17
|
+
const times = [
|
|
18
|
+
...[...runs.values()].flatMap((wait) => (wait?.kind === "sleep" ? [wait.until] : [])),
|
|
19
|
+
...sources.flatMap((source) => source()?.getTime() ?? []),
|
|
20
|
+
];
|
|
21
|
+
return times.length ? new Date(Math.min(...times)) : undefined;
|
|
22
|
+
},
|
|
23
|
+
get busy() {
|
|
24
|
+
return [...runs.values()].filter((wait) => wait === undefined).length;
|
|
25
|
+
},
|
|
26
|
+
waitOf: (runId) => runs.get(runId),
|
|
27
|
+
async running(runId, body) {
|
|
28
|
+
runs.set(runId, undefined);
|
|
29
|
+
try {
|
|
30
|
+
return await body();
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
runs.delete(runId);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
async waiting(runId, wait, fn) {
|
|
37
|
+
// Every wait is inside a run's body; one outside is left uncounted rather than counted busy for good.
|
|
38
|
+
if (!runs.has(runId))
|
|
39
|
+
return fn();
|
|
40
|
+
runs.set(runId, wait);
|
|
41
|
+
try {
|
|
42
|
+
return await fn();
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (runs.has(runId))
|
|
46
|
+
runs.set(runId, undefined);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* True when a run on `queue` (none for a run started outside a queue) is this worker's to run: on
|
|
53
|
+
* its own queue, or on one of DBOS's, where recovery puts runs.
|
|
54
|
+
*/
|
|
55
|
+
export const onOwnQueue = (queue, queueName) => !queue || queue === queueName || queue.startsWith("_dbos_");
|
|
56
|
+
/**
|
|
57
|
+
* True when nothing this worker would run can run within `horizonMs`: no run of its is executing
|
|
58
|
+
* or replaying; none of its version is queued (or queued without one, while it is the latest) on
|
|
59
|
+
* its queue or DBOS's own (recovery queues there); each PENDING run of its version is blocked in
|
|
60
|
+
* a wrapped wait; and the next deadline is past the horizon. A run of another version or queue
|
|
61
|
+
* is not this worker's to run, so it never holds the worker up.
|
|
62
|
+
*/
|
|
63
|
+
export async function idle(deadlines, scope, horizonMs) {
|
|
64
|
+
if (deadlines.busy > 0)
|
|
65
|
+
return false;
|
|
66
|
+
const next = deadlines.nextDeadline();
|
|
67
|
+
if (next && next.getTime() <= Date.now() + horizonMs)
|
|
68
|
+
return false;
|
|
69
|
+
const runs = await DBOS.listWorkflows({
|
|
70
|
+
applicationName: scope.appName,
|
|
71
|
+
status: dbosStatusesOf("queued", "running"),
|
|
72
|
+
loadInput: false,
|
|
73
|
+
loadOutput: false,
|
|
74
|
+
});
|
|
75
|
+
const ours = runs.filter((r) => onOwnQueue(r.queueName, scope.queueName));
|
|
76
|
+
if (ours.some((r) => r.status === "PENDING" && r.applicationVersion === scope.version && !deadlines.waitOf(r.workflowID)))
|
|
77
|
+
return false;
|
|
78
|
+
const queued = ours.filter((r) => r.status !== "PENDING");
|
|
79
|
+
if (queued.some((r) => r.applicationVersion === scope.version))
|
|
80
|
+
return false;
|
|
81
|
+
// DBOS gives a run queued without a version to the app's latest version.
|
|
82
|
+
if (!queued.some((r) => !r.applicationVersion))
|
|
83
|
+
return true;
|
|
84
|
+
return (await DBOS.getLatestApplicationVersion()).versionName !== scope.version;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Checks every `everyMs` whether the worker is `idle`; when it is, registers the next deadline
|
|
88
|
+
* with the wakeup, replacing what it held (none when there is no deadline at all), and stops the
|
|
89
|
+
* worker, then tells `onStopped`. A failed registration is recorded on the row and logged once for
|
|
90
|
+
* each new message, and the worker keeps running: the next check tries again. A run that arrives
|
|
91
|
+
* while it registers keeps the worker up, since the worker must be idle twice, with the same
|
|
92
|
+
* deadline, before it stops. `close` ends the checks.
|
|
93
|
+
*/
|
|
94
|
+
export function stopWhenIdle(rule) {
|
|
95
|
+
let closed = false;
|
|
96
|
+
let timer;
|
|
97
|
+
let lastProblem;
|
|
98
|
+
const problem = (message) => {
|
|
99
|
+
if (message !== lastProblem)
|
|
100
|
+
warn(message);
|
|
101
|
+
lastProblem = message;
|
|
102
|
+
};
|
|
103
|
+
const tryStop = async () => {
|
|
104
|
+
if (!(await rule.idle()))
|
|
105
|
+
return false;
|
|
106
|
+
const next = rule.deadlines.nextDeadline();
|
|
107
|
+
const at = next?.toISOString() ?? null;
|
|
108
|
+
const db = await rule.db();
|
|
109
|
+
if (at !== null) {
|
|
110
|
+
try {
|
|
111
|
+
await registerWakeup(rule, { at });
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
const message = errorMessage(err);
|
|
115
|
+
await recordWakeupError(db, message);
|
|
116
|
+
problem(`could not register the wakeup at ${at}, so the worker keeps running: ${message}`);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
await recordWakeupRegistration(db, at);
|
|
120
|
+
}
|
|
121
|
+
lastProblem = undefined;
|
|
122
|
+
if (closed || !(await rule.idle()) || rule.deadlines.nextDeadline()?.toISOString() !== next?.toISOString())
|
|
123
|
+
return false;
|
|
124
|
+
await recordWakeupStop(db);
|
|
125
|
+
await rule.stop().catch((err) => warn(`the worker could not stop cleanly: ${errorMessage(err)}`));
|
|
126
|
+
rule.onStopped?.({ at });
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
const check = async () => {
|
|
130
|
+
timer = undefined;
|
|
131
|
+
if (closed)
|
|
132
|
+
return;
|
|
133
|
+
const stopped = await tryStop().catch((err) => {
|
|
134
|
+
problem(`could not check whether the worker may stop: ${errorMessage(err)}`);
|
|
135
|
+
return false;
|
|
136
|
+
});
|
|
137
|
+
if (!stopped && !closed)
|
|
138
|
+
schedule();
|
|
139
|
+
};
|
|
140
|
+
const schedule = () => {
|
|
141
|
+
timer = setTimeout(() => void check(), rule.everyMs);
|
|
142
|
+
timer.unref();
|
|
143
|
+
};
|
|
144
|
+
schedule();
|
|
145
|
+
return {
|
|
146
|
+
close() {
|
|
147
|
+
closed = true;
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Calls the wakeup's register operation, as `SanomaClient.call` calls a driver: its input parsed,
|
|
154
|
+
* a `CallContext` of the runtime's, and no ledger record, which is a run's.
|
|
155
|
+
*/
|
|
156
|
+
export async function registerWakeup({ register: { op, fn }, secrets, wakeup }, registration) {
|
|
157
|
+
if (!fn)
|
|
158
|
+
throw new Error(`${op.id} has no driver: the connector package must attach one`);
|
|
159
|
+
// Fresh each time: a repeat of an earlier `at` after another registration must re-arm it, so no
|
|
160
|
+
// two registrations share a key (real drivers never dedupe on it anyway).
|
|
161
|
+
const key = `wakeup:${wakeup}:${randomUUID()}`;
|
|
162
|
+
const context = { idempotencyKey: key, runId: key, opId: op.id, attempt: 1, secrets, by: WAKEUP_AGENT };
|
|
163
|
+
await fn(op.input.parse(registration), context);
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=deadlines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadlines.js","sourceRoot":"","sources":["../src/deadlines.ts"],"names":[],"mappings":"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"}
|
package/dist/define.d.ts
CHANGED
|
@@ -72,12 +72,23 @@ export interface ApprovalState extends Omit<ApprovalRequest, "covers"> {
|
|
|
72
72
|
input?: unknown;
|
|
73
73
|
/** For a policy request: the policies that held the call, by id, when the policy names them. */
|
|
74
74
|
reasons?: string[];
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* `withdrawn`: it no longer stands, as when its change got a new commit. A pending approval is
|
|
77
|
+
* withdrawn by a message (`SanomaClient.withdraw`), an approved one by its run
|
|
78
|
+
* (`withdrawApprovals`); either way it lets nothing through and takes no decision.
|
|
79
|
+
*/
|
|
80
|
+
status: "pending" | "approved" | "rejected" | "withdrawn";
|
|
81
|
+
/** Why it was withdrawn, once it is. */
|
|
82
|
+
withdrawnReason?: string;
|
|
83
|
+
/** When it was withdrawn, once it is. */
|
|
84
|
+
withdrawnAt?: number;
|
|
85
|
+
/** Who withdrew it, when someone did rather than its run (`withdrawApprovals`). */
|
|
86
|
+
withdrawnBy?: string;
|
|
76
87
|
requestedAt: number;
|
|
77
88
|
decidedBy?: string;
|
|
78
89
|
decidedAt?: number;
|
|
79
90
|
note?: string;
|
|
80
|
-
/** The `id` of the decision message the run decided with, when it had one. */
|
|
91
|
+
/** The `id` of the decision or withdrawal message the run decided with, when it had one. */
|
|
81
92
|
decidedWith?: string;
|
|
82
93
|
/** Set when the run's own actor decided it: nobody else could (`mayDecide`). */
|
|
83
94
|
alone?: true;
|
|
@@ -154,7 +165,7 @@ type CtxOps<O extends Op> = {
|
|
|
154
165
|
};
|
|
155
166
|
};
|
|
156
167
|
interface ApprovalCtx {
|
|
157
|
-
/** Waits, durably, until the approver (or someone in the approver group) approves. Throws `RejectedError` on a rejection. */
|
|
168
|
+
/** Waits, durably, until the approver (or someone in the approver group) approves. Throws `RejectedError` on a rejection, `WithdrawnError` on a withdrawal. */
|
|
158
169
|
approval(title: string, request: ApprovalRequest): Promise<ApprovalResult>;
|
|
159
170
|
}
|
|
160
171
|
interface SleepCtx {
|
|
@@ -186,11 +197,21 @@ export type Ctx<U extends readonly Use[]> = BaseCtx & CtxOps<OpsOf<U>> & ("appro
|
|
|
186
197
|
* What JSON Schema can't express is left open. Throws when zod can't convert the schema at all.
|
|
187
198
|
*/
|
|
188
199
|
export declare function jsonSchemaOf(schema: z.ZodType, io?: "input" | "output", refs?: ReadonlyMap<unknown, string>): Record<string, unknown>;
|
|
200
|
+
/**
|
|
201
|
+
* How a workflow starts: `manual` (by hand, or from code), or on a schedule, `schedule` a cron
|
|
202
|
+
* expression of five fields (minute, hour, day of month, month, day of week) read in `timezone`,
|
|
203
|
+
* an IANA time zone (`Europe/Amsterdam`), UTC without one. A scheduled run is started by the
|
|
204
|
+
* runtime's `schedule` agent with the input `{}`, as the policy allows; it can still be started by hand.
|
|
205
|
+
*/
|
|
206
|
+
export type Trigger = "manual" | {
|
|
207
|
+
readonly schedule: string;
|
|
208
|
+
readonly timezone?: string;
|
|
209
|
+
};
|
|
189
210
|
export interface WorkflowDefinition<U extends readonly Use[] = readonly Use[], S extends z.ZodType = z.ZodType> {
|
|
190
211
|
readonly kind: "workflow";
|
|
191
212
|
readonly name: string;
|
|
192
213
|
readonly title?: string;
|
|
193
|
-
readonly trigger:
|
|
214
|
+
readonly trigger: Trigger;
|
|
194
215
|
readonly input: S;
|
|
195
216
|
/** Every operation and built-in the workflow may call. `ctx` is built from this list and nothing else. */
|
|
196
217
|
readonly uses: U;
|
package/dist/define.js
CHANGED
|
@@ -70,6 +70,11 @@ export function defineWorkflow(def) {
|
|
|
70
70
|
if (!/^[a-z][a-z0-9-]*$/.test(def.name)) {
|
|
71
71
|
throw new Error(`Workflow name "${def.name}" must be lowercase letters, digits and dashes`);
|
|
72
72
|
}
|
|
73
|
+
checkTrigger(def.name, def.trigger);
|
|
74
|
+
// A scheduled run starts with no input of its own.
|
|
75
|
+
if (def.trigger !== "manual" && !acceptsEmpty(def.input)) {
|
|
76
|
+
throw new Error(`Workflow "${def.name}": a scheduled run starts with the input {}, which its input schema refuses: make each field optional or give it a default`);
|
|
77
|
+
}
|
|
73
78
|
const file = callerFile();
|
|
74
79
|
if (file === undefined) {
|
|
75
80
|
throw new Error(`Workflow "${def.name}" was defined from code with no file (an eval, or a bundle without one): ` +
|
|
@@ -77,6 +82,41 @@ export function defineWorkflow(def) {
|
|
|
77
82
|
}
|
|
78
83
|
return Object.freeze({ kind: "workflow", ...def, file });
|
|
79
84
|
}
|
|
85
|
+
/** True when `{}` parses: a schema zod can check only asynchronously is taken as accepting it, and the run parses it. */
|
|
86
|
+
function acceptsEmpty(schema) {
|
|
87
|
+
try {
|
|
88
|
+
return schema.safeParse({}).success;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Throws what is wrong with a workflow's `trigger`: not `manual` or a schedule, five fields, in a time zone there is. */
|
|
95
|
+
function checkTrigger(name, trigger) {
|
|
96
|
+
if (trigger === "manual")
|
|
97
|
+
return;
|
|
98
|
+
const { schedule, timezone } = (typeof trigger === "object" && trigger !== null ? trigger : {});
|
|
99
|
+
if (typeof schedule !== "string") {
|
|
100
|
+
throw new Error(`Workflow "${name}": its trigger is "manual" or { schedule, timezone? }`);
|
|
101
|
+
}
|
|
102
|
+
if (schedule.trim().split(/\s+/).length !== 5) {
|
|
103
|
+
throw new Error(`Workflow "${name}": its schedule "${schedule}" is a cron expression of five fields: minute, hour, day of month, month, day of week`);
|
|
104
|
+
}
|
|
105
|
+
if (timezone !== undefined && !isTimeZone(timezone)) {
|
|
106
|
+
throw new Error(`Workflow "${name}": its timezone "${String(timezone)}" is no IANA time zone, such as "Europe/Amsterdam"`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const isTimeZone = (zone) => {
|
|
110
|
+
// An offset (`+05:30`), which Intl takes too, is no IANA zone name.
|
|
111
|
+
if (typeof zone !== "string" || /^[+-]/.test(zone))
|
|
112
|
+
return false;
|
|
113
|
+
try {
|
|
114
|
+
return Boolean(new Intl.DateTimeFormat("en", { timeZone: zone }));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
80
120
|
/**
|
|
81
121
|
* The file of the code that called the function calling this one (`defineWorkflow`'s caller),
|
|
82
122
|
* from V8's call sites, as a path; undefined where there is none.
|
package/dist/define.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAW,MAAM,SAAS,CAAC;AAexC,MAAM,CAAC,MAAM,SAAS,GAAyB,CAAC,CAAC,MAAM,CAAC;IACtD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC;IAClD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC,QAAQ,EAAE;CAC9E,CAAC,CAAC;AAQH,MAAM,CAAC,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAK,CAClD,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EACrE,EAAE,KAAK,EAAE,iDAAiD,EAAE,CAC7D,CAAC;AAEF,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAW,EAAE,CAAW,EAAW,EAAE,CAChE,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC;AAEjF,MAAM,MAAM,GAAG,qCAAqC,CAAC;AAErD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC;KACpB,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EACxE,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB;KACA,QAAQ,EAAE,CAAC;AAcd;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAC3C;IACE,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACnF,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC,QAAQ,EAAE;CACpE,EACD,EAAE,KAAK,EAAE,0DAA0D,EAAE,CACtE,CAAC;
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAW,MAAM,SAAS,CAAC;AAexC,MAAM,CAAC,MAAM,SAAS,GAAyB,CAAC,CAAC,MAAM,CAAC;IACtD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC;IAClD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC,QAAQ,EAAE;CAC9E,CAAC,CAAC;AAQH,MAAM,CAAC,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAK,CAClD,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EACrE,EAAE,KAAK,EAAE,iDAAiD,EAAE,CAC7D,CAAC;AAEF,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAW,EAAE,CAAW,EAAW,EAAE,CAChE,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC;AAEjF,MAAM,MAAM,GAAG,qCAAqC,CAAC;AAErD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC;KACpB,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EACxE,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB;KACA,QAAQ,EAAE,CAAC;AAcd;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAC3C;IACE,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACnF,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC,QAAQ,EAAE;CACpE,EACD,EAAE,KAAK,EAAE,0DAA0D,EAAE,CACtE,CAAC;AAgEF,MAAM,IAAI,GAAG,mGAAmG,CAAC;AACjH,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAC;AAExG,sDAAsD;AACtD,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;CAC5G,CAAmC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,YAAY,CAAC;IACrC,EAAE,EAAE,QAAQ;IACZ,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,QAAQ;CACf,CAAmC,CAAC;AAErC,yEAAyE;AACzE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAC/B,CAAC,CAAC,MAAM,CAAgB,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,UAAU,EAAE,qDAAqD,CAAC,EAC9G,EAAE,KAAK,EAAE,wDAAwD,EAAE,CACpE,CAAC;AAgEF;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAiB,EACjB,EAAE,GAAuB,OAAO,EAChC,IAAmC;IAEnC,OAAO,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;QAC5B,EAAE;QACF,MAAM,EAAE,eAAe;QACvB,eAAe,EAAE,KAAK;QACtB,2FAA2F;QAC3F,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI;YAChB,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE;gBAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC9D,IAAI,GAAG,KAAK,SAAS;oBAAE,OAAO;gBAC9B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;oBAAE,OAAQ,UAAsC,CAAC,GAAG,CAAC,CAAC;gBAC/F,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3C,CAAC;SACF,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AA0BD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EAAoC,EAAY,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAEnH,MAAM,UAAU,cAAc,CAC5B,GAAoD;IAEpD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,IAAI,gDAAgD,CAAC,CAAC;IAC9F,CAAC;IACD,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACpC,mDAAmD;IACnD,IAAI,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,IAAI,4HAA4H,CAClJ,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,aAAa,GAAG,CAAC,IAAI,2EAA2E;YAC9F,qEAAqE,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,yHAAyH;AACzH,SAAS,YAAY,CAAC,MAAiB;IACrC,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,0HAA0H;AAC1H,SAAS,YAAY,CAAC,IAAY,EAAE,OAAgB;IAClD,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO;IACjC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAG7F,CAAC;IACF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,uDAAuD,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,oBAAoB,QAAQ,uFAAuF,CACrI,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,aAAa,IAAI,oBAAoB,MAAM,CAAC,QAAQ,CAAC,oDAAoD,CAC1G,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAa,EAAW,EAAE;IAC5C,oEAAoE;IACpE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,6GAA6G;IAC7G,OAAO,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AACpD,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,UAAU,CAAC,MAA0B;IACnD,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,IAAI,SAAS,CAAC;IAC7D,IAAI,CAAC;QACH,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
package/dist/demo.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SanomaClient } from "./client.ts";
|
|
2
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
3
|
+
import { type Moment } from "./scenario.ts";
|
|
4
|
+
/** What the last `reseed` of an instance did: what the demo's banner shows. */
|
|
5
|
+
export interface DemoState {
|
|
6
|
+
/** When it was seeded, as an ISO timestamp. */
|
|
7
|
+
seededAt: string;
|
|
8
|
+
/** The worlds applied, by scenario name. */
|
|
9
|
+
worlds: string[];
|
|
10
|
+
/** Each moment started and left waiting: who it is for, where they land, its scenario and its run. */
|
|
11
|
+
moments: (Moment & {
|
|
12
|
+
scenario: string;
|
|
13
|
+
runId: string;
|
|
14
|
+
})[];
|
|
15
|
+
/** What could not be done, one line each: a feature file that did not load, a world or a moment that failed. */
|
|
16
|
+
problems: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Seeds the demo instance afresh: empties the collections' table, resets the instance's fakes,
|
|
20
|
+
* applies every world (a scenario with no `When`) to the instance (`applyGivens`: its records
|
|
21
|
+
* saved through the collections' driver, its vendor objects put in the fakes), then starts each
|
|
22
|
+
* moment as a sandbox run by its person, left undriven, so its first approval waits for them.
|
|
23
|
+
* `resolved` is the resolution the worker and `client` run on, made with `fakes: "instance"`.
|
|
24
|
+
* A world or a moment that fails is a problem in the state it returns, and the rest go on.
|
|
25
|
+
*/
|
|
26
|
+
export declare function reseed(client: SanomaClient, resolved: ResolvedConfig): Promise<DemoState>;
|
|
27
|
+
/** What the instance's last `reseed` did, or undefined before its first. */
|
|
28
|
+
export declare const demoState: (resolved: ResolvedConfig) => DemoState | undefined;
|
package/dist/demo.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DATA_TABLE } from "./db.js";
|
|
2
|
+
import { applyGivens } from "./sandbox.js";
|
|
3
|
+
import { loadScenarios } from "./scenario.js";
|
|
4
|
+
import { memorySecrets } from "./secret.js";
|
|
5
|
+
import { errorMessage } from "./shared.js";
|
|
6
|
+
/** Each instance's last reseed, by its resolution. */
|
|
7
|
+
const states = new WeakMap();
|
|
8
|
+
/**
|
|
9
|
+
* Seeds the demo instance afresh: empties the collections' table, resets the instance's fakes,
|
|
10
|
+
* applies every world (a scenario with no `When`) to the instance (`applyGivens`: its records
|
|
11
|
+
* saved through the collections' driver, its vendor objects put in the fakes), then starts each
|
|
12
|
+
* moment as a sandbox run by its person, left undriven, so its first approval waits for them.
|
|
13
|
+
* `resolved` is the resolution the worker and `client` run on, made with `fakes: "instance"`.
|
|
14
|
+
* A world or a moment that fails is a problem in the state it returns, and the rest go on.
|
|
15
|
+
*/
|
|
16
|
+
export async function reseed(client, resolved) {
|
|
17
|
+
const fakes = resolved.instanceFakes;
|
|
18
|
+
if (!fakes)
|
|
19
|
+
throw new Error('reseed seeds an instance on fakes: resolve its config with `fakes: "instance"`');
|
|
20
|
+
// Read first, so scenarios that cannot be read at all leave the instance as it was.
|
|
21
|
+
const { scenarios, errors } = await loadScenarios(resolved);
|
|
22
|
+
if (resolved.dataStore)
|
|
23
|
+
await (await resolved.dataStore.db()).query(`TRUNCATE ${DATA_TABLE}`);
|
|
24
|
+
for (const fake of fakes.values())
|
|
25
|
+
fake.reset();
|
|
26
|
+
const state = {
|
|
27
|
+
seededAt: new Date().toISOString(),
|
|
28
|
+
worlds: [],
|
|
29
|
+
moments: [],
|
|
30
|
+
problems: errors.map((e) => e.message),
|
|
31
|
+
};
|
|
32
|
+
const secrets = memorySecrets();
|
|
33
|
+
for (const world of scenarios.filter((s) => s.workflow === undefined)) {
|
|
34
|
+
try {
|
|
35
|
+
await applyGivens(world, {
|
|
36
|
+
kind: "instance",
|
|
37
|
+
drivers: resolved.drivers,
|
|
38
|
+
fakes,
|
|
39
|
+
ops: resolved.ops,
|
|
40
|
+
call: (i, opId) => ({
|
|
41
|
+
idempotencyKey: `world:${world.name}:${i}`,
|
|
42
|
+
runId: `world:${world.name}`,
|
|
43
|
+
opId,
|
|
44
|
+
attempt: 1,
|
|
45
|
+
secrets,
|
|
46
|
+
// Who its records are by: whoever it says is acting, else the runtime's own agent.
|
|
47
|
+
by: world.startedBy ?? "scenarios",
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
state.worlds.push(world.name);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
state.problems.push(`world "${world.name}": ${errorMessage(err)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const s of scenarios) {
|
|
57
|
+
if (!s.moment || s.workflow === undefined)
|
|
58
|
+
continue;
|
|
59
|
+
try {
|
|
60
|
+
const runId = await client.start(s.workflow, s.input, { sandbox: s.name, startedBy: s.moment.person });
|
|
61
|
+
state.moments.push({ ...s.moment, scenario: s.name, runId });
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
state.problems.push(`moment "${s.name}": ${errorMessage(err)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
states.set(resolved, state);
|
|
68
|
+
return state;
|
|
69
|
+
}
|
|
70
|
+
/** What the instance's last `reseed` did, or undefined before its first. */
|
|
71
|
+
export const demoState = (resolved) => states.get(resolved);
|
|
72
|
+
//# sourceMappingURL=demo.js.map
|
package/dist/demo.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demo.js","sourceRoot":"","sources":["../src/demo.ts"],"names":[],"mappings":"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"}
|