@sanoma-ai/workflows 0.11.12 → 0.11.14
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/CLOUD-IDENTITY.md +12 -4
- package/KMS-SECRETS.md +3 -1
- package/README.md +4 -2
- package/dist/bundled.d.ts +2 -2
- package/dist/chats.d.ts +3 -3
- package/dist/chats.js +34 -20
- package/dist/chats.js.map +1 -1
- package/dist/client.d.ts +17 -4
- package/dist/client.js +74 -63
- package/dist/client.js.map +1 -1
- package/dist/cloud-identity.js +14 -12
- package/dist/cloud-identity.js.map +1 -1
- package/dist/collection.d.ts +2 -2
- package/dist/collection.js +53 -42
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +11 -6
- package/dist/config.js +15 -13
- package/dist/config.js.map +1 -1
- package/dist/connection.d.ts +2 -0
- package/dist/connection.js +112 -40
- package/dist/connection.js.map +1 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/credentials.js +33 -29
- package/dist/credentials.js.map +1 -1
- package/dist/db/company.d.ts +1152 -0
- package/dist/db/company.js +118 -0
- package/dist/db/company.js.map +1 -0
- package/dist/db/dbos.d.ts +111 -0
- package/dist/db/dbos.js +15 -0
- package/dist/db/dbos.js.map +1 -0
- package/dist/db/identity.d.ts +367 -0
- package/dist/db/identity.js +48 -0
- package/dist/db/identity.js.map +1 -0
- package/dist/db/index.d.ts +7 -0
- package/dist/db/index.js +12 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/kms.d.ts +224 -0
- package/dist/db/kms.js +29 -0
- package/dist/db/kms.js.map +1 -0
- package/dist/db/migrate.d.ts +66 -0
- package/dist/db/migrate.js +39 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/wakeup.d.ts +271 -0
- package/dist/db/wakeup.js +34 -0
- package/dist/db/wakeup.js.map +1 -0
- package/dist/db.d.ts +42 -39
- package/dist/db.js +129 -169
- package/dist/db.js.map +1 -1
- package/dist/deadlines.js +8 -4
- package/dist/deadlines.js.map +1 -1
- package/dist/deployment-key.d.ts +4 -1
- package/dist/deployment-key.js +10 -3
- package/dist/deployment-key.js.map +1 -1
- package/dist/host-derive.d.ts +12 -3
- package/dist/host-derive.js +21 -8
- package/dist/host-derive.js.map +1 -1
- package/dist/identity-provisioning.d.ts +2 -2
- package/dist/identity-provisioning.js +75 -42
- package/dist/identity-provisioning.js.map +1 -1
- package/dist/identity-service.js +18 -15
- package/dist/identity-service.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kms-secrets.d.ts +9 -0
- package/dist/kms-secrets.js +59 -27
- package/dist/kms-secrets.js.map +1 -1
- package/dist/ledger.d.ts +1 -1
- package/dist/ledger.js +22 -14
- package/dist/ledger.js.map +1 -1
- package/dist/op.d.ts +27 -6
- package/dist/op.js.map +1 -1
- package/dist/run-status.js +7 -3
- package/dist/run-status.js.map +1 -1
- package/dist/schedules.d.ts +1 -1
- package/dist/schedules.js +5 -7
- package/dist/schedules.js.map +1 -1
- package/dist/schema.d.ts +6 -5
- package/dist/schema.js +11 -7
- package/dist/schema.js.map +1 -1
- package/dist/shared.d.ts +6 -23
- package/dist/shared.js +6 -5
- package/dist/shared.js.map +1 -1
- package/dist/storage-provisioning.d.ts +2 -0
- package/dist/storage-provisioning.js +51 -50
- package/dist/storage-provisioning.js.map +1 -1
- package/dist/tokens.d.ts +2 -2
- package/dist/tokens.js +31 -20
- package/dist/tokens.js.map +1 -1
- package/dist/version.d.ts +4 -4
- package/dist/version.js +5 -5
- package/dist/version.js.map +1 -1
- package/dist/wakeup-service.d.ts +0 -4
- package/dist/wakeup-service.js +102 -93
- package/dist/wakeup-service.js.map +1 -1
- package/dist/worker.d.ts +7 -0
- package/dist/worker.js +26 -21
- package/dist/worker.js.map +1 -1
- package/drizzle/company/0000_tables.sql +104 -0
- package/drizzle/company/0001_wakeup_row.sql +2 -0
- package/drizzle/company/meta/0000_snapshot.json +652 -0
- package/drizzle/company/meta/0001_snapshot.json +652 -0
- package/drizzle/company/meta/_journal.json +20 -0
- package/drizzle/wakeup/0000_tables.sql +26 -0
- package/drizzle/wakeup/meta/0000_snapshot.json +200 -0
- package/drizzle/wakeup/meta/_journal.json +13 -0
- package/oxlint.json +2 -1
- package/package.json +11 -2
- package/src/bundled.ts +2 -2
- package/src/chats.ts +41 -42
- package/src/client.ts +81 -64
- package/src/cloud-identity.ts +23 -24
- package/src/collection.ts +58 -50
- package/src/config.ts +29 -19
- package/src/connection.ts +132 -102
- package/src/credentials.ts +37 -33
- package/src/db/company.ts +173 -0
- package/src/db/dbos.ts +17 -0
- package/src/db/identity.ts +61 -0
- package/src/db/index.ts +12 -0
- package/src/db/kms.ts +41 -0
- package/src/db/migrate.ts +71 -0
- package/src/db/wakeup.ts +41 -0
- package/src/db.ts +151 -182
- package/src/deadlines.ts +10 -4
- package/src/deployment-key.ts +11 -3
- package/src/host-derive.ts +26 -9
- package/src/identity-provisioning.ts +85 -57
- package/src/identity-service.ts +19 -19
- package/src/index.ts +3 -0
- package/src/kms-secrets.ts +64 -56
- package/src/ledger.ts +23 -18
- package/src/op.ts +28 -6
- package/src/run-status.ts +7 -6
- package/src/schedules.ts +6 -8
- package/src/schema.ts +11 -8
- package/src/shared.ts +6 -17
- package/src/storage-provisioning.ts +47 -52
- package/src/tokens.ts +43 -33
- package/src/version.ts +5 -5
- package/src/wakeup-service.ts +117 -131
- package/src/worker.ts +38 -21
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { index, integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
3
|
+
// The wakeup service's tables (`wakeup-service.ts`), in the service's own database: never a
|
|
4
|
+
// company's, which every company database gets. Migrations under `drizzle/wakeup/`.
|
|
5
|
+
const when = () => timestamp({ withTimezone: true });
|
|
6
|
+
/** One account per party that registers instances, its token's hash alone. */
|
|
7
|
+
export const wakeupAccounts = pgTable("sanoma_wakeup_accounts", {
|
|
8
|
+
id: uuid().primaryKey(),
|
|
9
|
+
tokenHash: text().notNull().unique(),
|
|
10
|
+
label: text().notNull(),
|
|
11
|
+
createdAt: when().notNull().defaultNow(),
|
|
12
|
+
revokedAt: when(),
|
|
13
|
+
});
|
|
14
|
+
/** Each registered instance: its URL, its token's hash and its registered wake. */
|
|
15
|
+
export const wakeupInstances = pgTable("sanoma_wakeup_instances", {
|
|
16
|
+
id: uuid().primaryKey(),
|
|
17
|
+
accountId: uuid()
|
|
18
|
+
.notNull()
|
|
19
|
+
.references(() => wakeupAccounts.id),
|
|
20
|
+
url: text().notNull(),
|
|
21
|
+
tokenHash: text().notNull().unique(),
|
|
22
|
+
at: when(),
|
|
23
|
+
calledAt: when(),
|
|
24
|
+
attempts: integer().notNull().default(0),
|
|
25
|
+
nextAttemptAt: when(),
|
|
26
|
+
lastError: text(),
|
|
27
|
+
createdAt: when().notNull().defaultNow(),
|
|
28
|
+
}, (t) => [
|
|
29
|
+
index("sanoma_wakeup_instances_at")
|
|
30
|
+
.on(t.at)
|
|
31
|
+
.where(sql `${t.at} IS NOT NULL`),
|
|
32
|
+
index("sanoma_wakeup_instances_account").on(t.accountId),
|
|
33
|
+
]);
|
|
34
|
+
//# sourceMappingURL=wakeup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wakeup.js","sourceRoot":"","sources":["../../src/db/wakeup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAErF,4FAA4F;AAC5F,oFAAoF;AAEpF,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAErD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,wBAAwB,EAAE;IAC9D,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE;IACvB,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IACpC,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACvB,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;IACxC,SAAS,EAAE,IAAI,EAAE;CAClB,CAAC,CAAC;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CACpC,yBAAyB,EACzB;IACE,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE;IACvB,SAAS,EAAE,IAAI,EAAE;SACd,OAAO,EAAE;SACT,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC;IACtC,GAAG,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACrB,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;IACpC,EAAE,EAAE,IAAI,EAAE;IACV,QAAQ,EAAE,IAAI,EAAE;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,aAAa,EAAE,IAAI,EAAE;IACrB,SAAS,EAAE,IAAI,EAAE;IACjB,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CACzC,EACD,CAAC,CAAC,EAAE,EAAE,CAAC;IACL,KAAK,CAAC,4BAA4B,CAAC;SAChC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACR,KAAK,CAAC,GAAG,CAAA,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC;IAClC,KAAK,CAAC,iCAAiC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;CACzD,CACF,CAAC"}
|
package/dist/db.d.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { DBOSClient } from "@dbos-inc/dbos-sdk";
|
|
2
|
+
import { type NodePgClient, type NodePgQueryResultHKT } from "drizzle-orm/node-postgres";
|
|
3
|
+
import { type PgDatabase } from "drizzle-orm/pg-core";
|
|
2
4
|
import { Client, Pool } from "pg";
|
|
3
|
-
/** A connection or a pool: what the queries take. */
|
|
4
|
-
export type Db = Pick<Pool | Client, "query">;
|
|
5
|
-
/** Where the credentials set from the app live. */
|
|
6
|
-
export declare const CREDENTIALS_TABLE = "sanoma_credentials";
|
|
7
|
-
/** Where the Postgres ledger keeps its records. */
|
|
8
|
-
export declare const LEDGER_TABLE = "sanoma_ledger";
|
|
9
|
-
/** Where the collections keep their records (`collection.ts`): one row per collection and key, the record as JSON. */
|
|
10
|
-
export declare const DATA_TABLE = "sanoma_data";
|
|
11
5
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `last_error` why the last registration failed.
|
|
6
|
+
* A database, or a transaction on one (`db.transaction`): what every store takes, so a store runs
|
|
7
|
+
* inside a caller's transaction as it runs on its own. Made by `databaseOn`.
|
|
15
8
|
*/
|
|
16
|
-
export
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
export type Db = PgDatabase<NodePgQueryResultHKT>;
|
|
10
|
+
/**
|
|
11
|
+
* Drizzle over `client`, a `pg` pool or connection the caller owns and ends (`db.$client`). Column
|
|
12
|
+
* names are the schema's properties in snake_case (`casing`), as the generated migrations write them.
|
|
13
|
+
*
|
|
14
|
+
* A statement that fails throws the `pg` error itself, as `client.query` would, not Drizzle's
|
|
15
|
+
* `DrizzleQueryError`, whose message carries the statement and its parameters (a credential's
|
|
16
|
+
* sealed value, a person's email, a record): `pgCode` reads the code, and no message or log
|
|
17
|
+
* repeats what was sent. `db.test.ts` holds Drizzle to this with a sentinel parameter.
|
|
18
|
+
*/
|
|
19
|
+
export declare function databaseOn<C extends NodePgClient>(client: C): import("drizzle-orm/node-postgres").NodePgDatabase<Record<string, never>> & {
|
|
20
|
+
$client: NodePgClient extends C ? Pool : C;
|
|
21
|
+
};
|
|
23
22
|
/** The Postgres error code of `err`, or an empty string when it has none. */
|
|
24
23
|
export declare const pgCode: (err: unknown) => string;
|
|
25
24
|
/** True for the failure of a create that another process won: what was to be made is there. */
|
|
@@ -45,8 +44,8 @@ export declare const DBOS_DATABASE_OPTIONS: {
|
|
|
45
44
|
* `version`, so DBOS gives them to a worker of that version: those it stamped, each with its status
|
|
46
45
|
* (a PENDING one is the caller's to queue again). DBOS has no call for it (a run's version is set at
|
|
47
46
|
* its start, `forkWorkflow` makes a new id, `rewindWorkflow` takes only an ended run), so this is the
|
|
48
|
-
* runtime's one write to DBOS's own tables
|
|
49
|
-
*
|
|
47
|
+
* runtime's one write to DBOS's own tables (`db/dbos.ts`, dbos-sdk 5.2.11). One statement: of two
|
|
48
|
+
* workers stamping a run, one does.
|
|
50
49
|
*/
|
|
51
50
|
export declare function stampVersion(db: Db, ids: readonly string[], version: string): Promise<{
|
|
52
51
|
id: string;
|
|
@@ -57,17 +56,19 @@ export declare function stampVersion(db: Db, ids: readonly string[], version: st
|
|
|
57
56
|
* the runtime, its tests and `@sanoma-ai/testing` make one (`DBOS_DATABASE_OPTIONS`).
|
|
58
57
|
*/
|
|
59
58
|
export declare const dbosClient: (databaseUrl: string, applicationName?: string) => Promise<DBOSClient>;
|
|
60
|
-
/** A
|
|
61
|
-
export interface
|
|
62
|
-
db(): Promise<
|
|
59
|
+
/** A database opened on first use, once prepared, and ended by `close`. */
|
|
60
|
+
export interface LazyDb {
|
|
61
|
+
db(): Promise<Db>;
|
|
62
|
+
/** The pool `db()` runs on, once the database is prepared. */
|
|
63
|
+
pool(): Promise<Pool>;
|
|
63
64
|
close(): Promise<void>;
|
|
64
65
|
}
|
|
65
66
|
/**
|
|
66
|
-
* A
|
|
67
|
-
* database is prepared (`prepareDatabase`). A preparation that fails lets the pool go, so the
|
|
68
|
-
* next
|
|
67
|
+
* A database for `what` on a pool at `databaseUrl`, opened on the first `db()` (or `pool()`) once
|
|
68
|
+
* the database is prepared (`prepareDatabase`). A preparation that fails lets the pool go, so the
|
|
69
|
+
* next call tries again. `close` ends the pool, for good: a call after it is refused.
|
|
69
70
|
*/
|
|
70
|
-
export declare function
|
|
71
|
+
export declare function lazyDb(databaseUrl: string, what: string): LazyDb;
|
|
71
72
|
/**
|
|
72
73
|
* From now on, `prepareDatabase` makes nothing: it only checks that DBOS's tables and the
|
|
73
74
|
* runtime's are there, and refuses, naming `by`, what prepares them, when they are not. For a
|
|
@@ -79,21 +80,23 @@ export declare function checkPreparedOnly(by: string): void;
|
|
|
79
80
|
* Makes the database at `databaseUrl` ready for the runtime, once per process and URL: the
|
|
80
81
|
* database itself and DBOS's tables when they are not there yet (as a worker's launch makes
|
|
81
82
|
* them; credentials may be set, and a ledger read, before any worker has run), then the
|
|
82
|
-
*
|
|
83
|
-
* Two processes preparing one database both
|
|
84
|
-
* (`lockPreparation`). A preparation that fails is
|
|
83
|
+
* runtime's tables, by the migrations the database has not had (`COMPANY_MIGRATIONS`), and its
|
|
84
|
+
* idle-transaction timeout (`guardIdleTransactions`). Two processes preparing one database both
|
|
85
|
+
* succeed: the second waits for the first (`lockPreparation`). A preparation that fails is
|
|
86
|
+
* forgotten, so the next call tries again.
|
|
85
87
|
*/
|
|
86
88
|
export declare function prepareDatabase(databaseUrl: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Runs `make` on a connection holding the preparation's session lock of the database at
|
|
91
|
+
* `databaseUrl` (made first when missing), so a second process waits, then finds everything made.
|
|
92
|
+
* For what makes a database's tables: `prepareDatabase`, and the wakeup service's own.
|
|
93
|
+
*/
|
|
94
|
+
export declare function underPreparationLock<T>(databaseUrl: string, make: (db: Db) => Promise<T>): Promise<T>;
|
|
87
95
|
/** A connection to the database at `databaseUrl`, made first when it is not there (`createDatabase`). */
|
|
88
96
|
export declare function connectMaking(databaseUrl: string): Promise<Client>;
|
|
89
97
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
98
|
+
* Marks a sealed invite request's `nonce` spent until `expiresAt`, when the row goes: true the
|
|
99
|
+
* first time, false for one spent already, whichever process asks. A row, not memory: the app
|
|
100
|
+
* keeps nothing across requests.
|
|
92
101
|
*/
|
|
93
|
-
export declare function
|
|
94
|
-
/** Records that the wakeup took `at`. */
|
|
95
|
-
export declare function recordWakeupRegistration(db: Db, at: string): Promise<void>;
|
|
96
|
-
/** Records why the last registration failed. */
|
|
97
|
-
export declare function recordWakeupError(db: Db, error: string): Promise<void>;
|
|
98
|
-
/** Records that the worker stops itself now. */
|
|
99
|
-
export declare function recordWakeupStop(db: Db): Promise<void>;
|
|
102
|
+
export declare function spendInviteRequest(db: Db, nonce: string, expiresAt: Date): Promise<boolean>;
|
package/dist/db.js
CHANGED
|
@@ -1,100 +1,49 @@
|
|
|
1
1
|
import { DBOS, DBOSClient } from "@dbos-inc/dbos-sdk";
|
|
2
2
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { and, eq, inArray, lte, ne, sql } from "drizzle-orm";
|
|
4
|
+
import { DrizzleQueryError } from "drizzle-orm/errors";
|
|
5
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
6
|
+
import { PgPreparedQuery } from "drizzle-orm/pg-core";
|
|
7
|
+
import { Client, Pool } from "pg";
|
|
8
|
+
import { inviteRequests } from "./db/company.js";
|
|
9
|
+
import { workflowStatus } from "./db/dbos.js";
|
|
10
|
+
import { applyMigrations, COMPANY_MIGRATIONS, migrationsApplied } from "./db/migrate.js";
|
|
4
11
|
import { warn } from "./log.js";
|
|
5
12
|
import { errorMessage } from "./shared.js";
|
|
6
|
-
/** Where the credentials set from the app live. */
|
|
7
|
-
export const CREDENTIALS_TABLE = "sanoma_credentials";
|
|
8
|
-
/** Where the Postgres ledger keeps its records. */
|
|
9
|
-
export const LEDGER_TABLE = "sanoma_ledger";
|
|
10
|
-
/** Where the collections keep their records (`collection.ts`): one row per collection and key, the record as JSON. */
|
|
11
|
-
export const DATA_TABLE = "sanoma_data";
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Drizzle over `client`, a `pg` pool or connection the caller owns and ends (`db.$client`). Column
|
|
15
|
+
* names are the schema's properties in snake_case (`casing`), as the generated migrations write them.
|
|
16
|
+
*
|
|
17
|
+
* A statement that fails throws the `pg` error itself, as `client.query` would, not Drizzle's
|
|
18
|
+
* `DrizzleQueryError`, whose message carries the statement and its parameters (a credential's
|
|
19
|
+
* sealed value, a person's email, a record): `pgCode` reads the code, and no message or log
|
|
20
|
+
* repeats what was sent. `db.test.ts` holds Drizzle to this with a sentinel parameter.
|
|
16
21
|
*/
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
account text NOT NULL, scopes jsonb NOT NULL, revision text NOT NULL, definition text NOT NULL,
|
|
43
|
-
credential text, location jsonb, status text NOT NULL DEFAULT 'connected', updated_by text NOT NULL,
|
|
44
|
-
updated_at timestamptz NOT NULL DEFAULT now());
|
|
45
|
-
ALTER TABLE sanoma_connections ADD COLUMN IF NOT EXISTS location jsonb`,
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"credentials",
|
|
49
|
-
// A table from before 0.10.0 (one row per name, each value sealed to its name alone) holds
|
|
50
|
-
// nothing 0.10.0 can read: it goes, and its credentials are set again.
|
|
51
|
-
// One table for the look and the drop: the one the name resolves to.
|
|
52
|
-
`DO $$ BEGIN
|
|
53
|
-
IF to_regclass('${CREDENTIALS_TABLE}') IS NOT NULL AND NOT EXISTS (
|
|
54
|
-
SELECT FROM pg_attribute
|
|
55
|
-
WHERE attrelid = to_regclass('${CREDENTIALS_TABLE}') AND attname = 'namespace' AND NOT attisdropped
|
|
56
|
-
) THEN
|
|
57
|
-
DROP TABLE ${CREDENTIALS_TABLE};
|
|
58
|
-
END IF;
|
|
59
|
-
END $$; ` +
|
|
60
|
-
`CREATE TABLE IF NOT EXISTS ${CREDENTIALS_TABLE} (namespace text NOT NULL, name text NOT NULL, value text NOT NULL, ` +
|
|
61
|
-
"version text NOT NULL, set_by text NOT NULL, set_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY(namespace, name))",
|
|
62
|
-
],
|
|
63
|
-
[
|
|
64
|
-
"ledger",
|
|
65
|
-
// `n` is the write order, `id` the record's, which the first write of takes, and `seq` the
|
|
66
|
-
// record's place in its run: a run reads in `seq` order, then write order.
|
|
67
|
-
`CREATE TABLE IF NOT EXISTS ${LEDGER_TABLE} (n bigserial PRIMARY KEY, id text NOT NULL UNIQUE, ` +
|
|
68
|
-
`run_id text NOT NULL, seq integer NOT NULL, record jsonb NOT NULL); ` +
|
|
69
|
-
`CREATE INDEX IF NOT EXISTS ${LEDGER_TABLE}_run_seq ON ${LEDGER_TABLE} (run_id, seq, n)`,
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
"data",
|
|
73
|
-
`CREATE TABLE IF NOT EXISTS ${DATA_TABLE} (collection text NOT NULL, key text NOT NULL, record jsonb NOT NULL, ` +
|
|
74
|
-
"updated_at timestamptz NOT NULL DEFAULT now(), updated_by text NOT NULL, PRIMARY KEY (collection, key))",
|
|
75
|
-
],
|
|
76
|
-
[
|
|
77
|
-
"wakeup",
|
|
78
|
-
`CREATE TABLE IF NOT EXISTS ${WAKEUP_TABLE} (id integer PRIMARY KEY CHECK (id = 1), at timestamptz, ` +
|
|
79
|
-
`stopped_at timestamptz, last_error text); INSERT INTO ${WAKEUP_TABLE} (id) VALUES (1) ON CONFLICT (id) DO NOTHING`,
|
|
80
|
-
],
|
|
81
|
-
[
|
|
82
|
-
"chats",
|
|
83
|
-
`CREATE TABLE IF NOT EXISTS ${CHATS_TABLE} (id text PRIMARY KEY, person text NOT NULL, engine text NOT NULL, ` +
|
|
84
|
-
"draft_id text, title text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()); " +
|
|
85
|
-
`CREATE INDEX IF NOT EXISTS ${CHATS_TABLE}_person ON ${CHATS_TABLE} (person, created_at)`,
|
|
86
|
-
],
|
|
87
|
-
[
|
|
88
|
-
"chat messages",
|
|
89
|
-
`CREATE TABLE IF NOT EXISTS ${CHAT_MESSAGES_TABLE} (chat_id text NOT NULL REFERENCES ${CHATS_TABLE} (id) ` +
|
|
90
|
-
"ON DELETE CASCADE, id text NOT NULL, seq integer NOT NULL, message jsonb NOT NULL, PRIMARY KEY (chat_id, id))",
|
|
91
|
-
],
|
|
92
|
-
[
|
|
93
|
-
"api tokens",
|
|
94
|
-
`CREATE TABLE IF NOT EXISTS ${API_TOKENS_TABLE} (id text PRIMARY KEY, hash text NOT NULL UNIQUE, ` +
|
|
95
|
-
"subject text NOT NULL, groups jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz)",
|
|
96
|
-
],
|
|
97
|
-
];
|
|
22
|
+
export function databaseOn(client) {
|
|
23
|
+
rethrowCause();
|
|
24
|
+
return drizzle(client, { casing: "snake_case" });
|
|
25
|
+
}
|
|
26
|
+
let rethrowing = false;
|
|
27
|
+
/**
|
|
28
|
+
* Makes every prepared query of this copy of Drizzle throw the driver's error in place of
|
|
29
|
+
* `DrizzleQueryError` (drizzle-orm 0.45.4 wraps in `PgPreparedQuery.queryWithCache`, the one
|
|
30
|
+
* place every query passes). Once per copy of the module.
|
|
31
|
+
*/
|
|
32
|
+
function rethrowCause() {
|
|
33
|
+
if (rethrowing)
|
|
34
|
+
return;
|
|
35
|
+
rethrowing = true;
|
|
36
|
+
const prototype = PgPreparedQuery.prototype;
|
|
37
|
+
const wrapped = prototype.queryWithCache;
|
|
38
|
+
prototype.queryWithCache = async function (...args) {
|
|
39
|
+
try {
|
|
40
|
+
return await wrapped.apply(this, args);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw err instanceof DrizzleQueryError && err.cause !== undefined ? err.cause : err;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
98
47
|
/** The Postgres error code of `err`, or an empty string when it has none. */
|
|
99
48
|
export const pgCode = (err) => err?.code?.toString() ?? "";
|
|
100
49
|
/**
|
|
@@ -129,15 +78,16 @@ export const DBOS_DATABASE_OPTIONS = { systemDatabaseIdleTransactionTimeoutMs: 0
|
|
|
129
78
|
* `version`, so DBOS gives them to a worker of that version: those it stamped, each with its status
|
|
130
79
|
* (a PENDING one is the caller's to queue again). DBOS has no call for it (a run's version is set at
|
|
131
80
|
* its start, `forkWorkflow` makes a new id, `rewindWorkflow` takes only an ended run), so this is the
|
|
132
|
-
* runtime's one write to DBOS's own tables
|
|
133
|
-
*
|
|
81
|
+
* runtime's one write to DBOS's own tables (`db/dbos.ts`, dbos-sdk 5.2.11). One statement: of two
|
|
82
|
+
* workers stamping a run, one does.
|
|
134
83
|
*/
|
|
135
84
|
export async function stampVersion(db, ids, version) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
85
|
+
const stamped = await db
|
|
86
|
+
.update(workflowStatus)
|
|
87
|
+
.set({ applicationVersion: version })
|
|
88
|
+
.where(and(inArray(workflowStatus.workflowUuid, [...ids]), eq(workflowStatus.name, "change"), inArray(workflowStatus.status, ["PENDING", "ENQUEUED"]), ne(workflowStatus.applicationVersion, version)))
|
|
89
|
+
.returning({ id: workflowStatus.workflowUuid, status: workflowStatus.status });
|
|
90
|
+
return stamped.map((r) => ({ id: r.id, pending: r.status === "PENDING" }));
|
|
141
91
|
}
|
|
142
92
|
/**
|
|
143
93
|
* A DBOS client of the app `applicationName` on the database at `databaseUrl`, as every client of
|
|
@@ -159,33 +109,39 @@ const INSUFFICIENT_PRIVILEGE = "42501";
|
|
|
159
109
|
/** Postgres's code for a database that is not there (`invalid_catalog_name`). */
|
|
160
110
|
const NO_DATABASE = "3D000";
|
|
161
111
|
/**
|
|
162
|
-
* A
|
|
163
|
-
* database is prepared (`prepareDatabase`). A preparation that fails lets the pool go, so the
|
|
164
|
-
* next
|
|
112
|
+
* A database for `what` on a pool at `databaseUrl`, opened on the first `db()` (or `pool()`) once
|
|
113
|
+
* the database is prepared (`prepareDatabase`). A preparation that fails lets the pool go, so the
|
|
114
|
+
* next call tries again. `close` ends the pool, for good: a call after it is refused.
|
|
165
115
|
*/
|
|
166
|
-
export function
|
|
116
|
+
export function lazyDb(databaseUrl, what) {
|
|
167
117
|
let pool;
|
|
118
|
+
let db;
|
|
168
119
|
let closed = false;
|
|
120
|
+
async function opened() {
|
|
121
|
+
if (closed)
|
|
122
|
+
throw new Error(`the ${what} store is closed`);
|
|
123
|
+
const current = (pool ??= openPool(databaseUrl, what));
|
|
124
|
+
try {
|
|
125
|
+
await prepareDatabase(databaseUrl);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
if (pool === current)
|
|
129
|
+
pool = db = undefined;
|
|
130
|
+
await current.end().catch(() => undefined);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
return current;
|
|
134
|
+
}
|
|
169
135
|
return {
|
|
170
136
|
async db() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const current = (pool ??= openPool(databaseUrl, what));
|
|
174
|
-
try {
|
|
175
|
-
await prepareDatabase(databaseUrl);
|
|
176
|
-
}
|
|
177
|
-
catch (err) {
|
|
178
|
-
if (pool === current)
|
|
179
|
-
pool = undefined;
|
|
180
|
-
await current.end().catch(() => undefined);
|
|
181
|
-
throw err;
|
|
182
|
-
}
|
|
183
|
-
return current;
|
|
137
|
+
const current = await opened();
|
|
138
|
+
return (db ??= databaseOn(current));
|
|
184
139
|
},
|
|
140
|
+
pool: opened,
|
|
185
141
|
async close() {
|
|
186
142
|
closed = true;
|
|
187
143
|
const current = pool;
|
|
188
|
-
pool = undefined;
|
|
144
|
+
pool = db = undefined;
|
|
189
145
|
await current?.end();
|
|
190
146
|
},
|
|
191
147
|
};
|
|
@@ -207,9 +163,10 @@ export function checkPreparedOnly(by) {
|
|
|
207
163
|
* Makes the database at `databaseUrl` ready for the runtime, once per process and URL: the
|
|
208
164
|
* database itself and DBOS's tables when they are not there yet (as a worker's launch makes
|
|
209
165
|
* them; credentials may be set, and a ledger read, before any worker has run), then the
|
|
210
|
-
*
|
|
211
|
-
* Two processes preparing one database both
|
|
212
|
-
* (`lockPreparation`). A preparation that fails is
|
|
166
|
+
* runtime's tables, by the migrations the database has not had (`COMPANY_MIGRATIONS`), and its
|
|
167
|
+
* idle-transaction timeout (`guardIdleTransactions`). Two processes preparing one database both
|
|
168
|
+
* succeed: the second waits for the first (`lockPreparation`). A preparation that fails is
|
|
169
|
+
* forgotten, so the next call tries again.
|
|
213
170
|
*/
|
|
214
171
|
export function prepareDatabase(databaseUrl) {
|
|
215
172
|
let preparing = prepared.get(databaseUrl);
|
|
@@ -227,32 +184,46 @@ export function prepareDatabase(databaseUrl) {
|
|
|
227
184
|
/** The error a preparation fails with, its cause `err`. */
|
|
228
185
|
const unprepared = (err) => new Error(`could not prepare the database: ${errorMessage(err)}`, { cause: err });
|
|
229
186
|
async function prepare(databaseUrl) {
|
|
230
|
-
|
|
231
|
-
throw unprepared(err);
|
|
232
|
-
});
|
|
233
|
-
try {
|
|
187
|
+
await underPreparationLock(databaseUrl, async (db) => {
|
|
234
188
|
await DBOS.migrate(databaseUrl).catch((err) => {
|
|
235
189
|
// A DBOS table made meanwhile by a release from before the lock, mid-upgrade: it is there.
|
|
236
190
|
if (!madeAlready(err))
|
|
237
191
|
throw unprepared(err);
|
|
238
192
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
193
|
+
await applyMigrations(db, COMPANY_MIGRATIONS).catch((err) => {
|
|
194
|
+
throw unprepared(err);
|
|
195
|
+
});
|
|
196
|
+
await guardIdleTransactions(db);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Runs `make` on a connection holding the preparation's session lock of the database at
|
|
201
|
+
* `databaseUrl` (made first when missing), so a second process waits, then finds everything made.
|
|
202
|
+
* For what makes a database's tables: `prepareDatabase`, and the wakeup service's own.
|
|
203
|
+
*/
|
|
204
|
+
export async function underPreparationLock(databaseUrl, make) {
|
|
205
|
+
const db = await lockPreparation(databaseUrl).catch((err) => {
|
|
206
|
+
throw unprepared(err);
|
|
207
|
+
});
|
|
208
|
+
try {
|
|
209
|
+
return await make(db);
|
|
242
210
|
}
|
|
243
211
|
finally {
|
|
244
212
|
// Ending the session releases the lock; a failure to end it must not hide the preparation's.
|
|
245
|
-
await
|
|
213
|
+
await db.$client.end().catch(() => undefined);
|
|
246
214
|
}
|
|
247
215
|
}
|
|
248
|
-
/**
|
|
249
|
-
|
|
250
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Refuses when the database at `databaseUrl` lacks DBOS's tables or the runtime's (every
|
|
218
|
+
* migration of `COMPANY_MIGRATIONS`, by its journal): `by` makes them, not this process.
|
|
219
|
+
*/
|
|
251
220
|
async function checkPrepared(databaseUrl, by) {
|
|
252
221
|
const conn = await connect(databaseUrl);
|
|
253
222
|
try {
|
|
254
|
-
const
|
|
255
|
-
const {
|
|
223
|
+
const db = databaseOn(conn);
|
|
224
|
+
const { rows } = await db.execute(sql `SELECT to_regclass('dbos.workflow_status') IS NOT NULL AS dbos`);
|
|
225
|
+
const dbos = rows[0]?.dbos === true;
|
|
226
|
+
const ours = await migrationsApplied(db, COMPANY_MIGRATIONS);
|
|
256
227
|
if (!dbos || !ours) {
|
|
257
228
|
const missing = [...(dbos ? [] : ["DBOS's"]), ...(ours ? [] : ["Sanoma's"])].join(" and ");
|
|
258
229
|
throw new Error(`the database has no ${missing} tables yet: ${by} makes them, and this process never does`);
|
|
@@ -265,21 +236,22 @@ async function checkPrepared(databaseUrl, by) {
|
|
|
265
236
|
/**
|
|
266
237
|
* A connection to the database at `databaseUrl` (made first when missing) holding the
|
|
267
238
|
* preparation's session lock until it ends, so a second process waits, then finds everything
|
|
268
|
-
* made. Racing `DBOS.migrate`s lose to each other in many ways (40P01, XX000),
|
|
269
|
-
*
|
|
270
|
-
* statement: `CREATE INDEX CONCURRENTLY` waits
|
|
271
|
-
* cannot see (the lock's holder is this
|
|
239
|
+
* made. Racing `DBOS.migrate`s lose to each other in many ways (40P01, XX000), and Drizzle's
|
|
240
|
+
* migrator takes no lock of its own, so the race is prevented rather than each loss forgiven.
|
|
241
|
+
* The lock is polled, never waited for by a blocked statement: `CREATE INDEX CONCURRENTLY` waits
|
|
242
|
+
* on that statement's snapshot, a deadlock Postgres cannot see (the lock's holder is this
|
|
243
|
+
* connection, not the migration's).
|
|
272
244
|
*/
|
|
273
245
|
async function lockPreparation(databaseUrl) {
|
|
274
|
-
const
|
|
246
|
+
const db = databaseOn(await connectMaking(databaseUrl));
|
|
275
247
|
try {
|
|
276
|
-
const ask =
|
|
277
|
-
while (
|
|
248
|
+
const ask = sql `SELECT pg_try_advisory_lock(hashtext('sanoma.prepareDatabase')) AS locked`;
|
|
249
|
+
while ((await db.execute(ask)).rows[0]?.locked !== true)
|
|
278
250
|
await delay(50);
|
|
279
|
-
return
|
|
251
|
+
return db;
|
|
280
252
|
}
|
|
281
253
|
catch (err) {
|
|
282
|
-
await
|
|
254
|
+
await db.$client.end().catch(() => undefined);
|
|
283
255
|
throw err;
|
|
284
256
|
}
|
|
285
257
|
}
|
|
@@ -309,7 +281,7 @@ async function createDatabase(databaseUrl) {
|
|
|
309
281
|
url.pathname = "/postgres";
|
|
310
282
|
const admin = await connect(url.toString());
|
|
311
283
|
try {
|
|
312
|
-
await admin.
|
|
284
|
+
await databaseOn(admin).execute(sql `CREATE DATABASE ${sql.identifier(name)}`);
|
|
313
285
|
}
|
|
314
286
|
catch (err) {
|
|
315
287
|
if (!madeAlready(err))
|
|
@@ -327,13 +299,13 @@ async function createDatabase(databaseUrl) {
|
|
|
327
299
|
*/
|
|
328
300
|
async function guardIdleTransactions(db) {
|
|
329
301
|
try {
|
|
330
|
-
await db.
|
|
302
|
+
await db.execute(sql `DO $$ BEGIN
|
|
331
303
|
IF current_setting('idle_in_transaction_session_timeout') = '0' AND NOT EXISTS (
|
|
332
304
|
SELECT FROM pg_db_role_setting s JOIN pg_database d ON d.oid = s.setdatabase, unnest(s.setconfig) c
|
|
333
305
|
WHERE d.datname = current_database() AND s.setrole = 0 AND c LIKE 'idle_in_transaction_session_timeout=%'
|
|
334
306
|
) THEN
|
|
335
307
|
EXECUTE format('ALTER DATABASE %I SET idle_in_transaction_session_timeout = %L', current_database(),
|
|
336
|
-
'${IDLE_IN_TRANSACTION_TIMEOUT}');
|
|
308
|
+
${sql.raw(`'${IDLE_IN_TRANSACTION_TIMEOUT}'`)});
|
|
337
309
|
END IF;
|
|
338
310
|
END $$`);
|
|
339
311
|
}
|
|
@@ -343,31 +315,19 @@ async function guardIdleTransactions(db) {
|
|
|
343
315
|
}
|
|
344
316
|
}
|
|
345
317
|
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
318
|
+
* Marks a sealed invite request's `nonce` spent until `expiresAt`, when the row goes: true the
|
|
319
|
+
* first time, false for one spent already, whichever process asks. A row, not memory: the app
|
|
320
|
+
* keeps nothing across requests.
|
|
348
321
|
*/
|
|
349
|
-
export async function
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// The wakeup row's writes: one worker registers per instance (the supervisor runs one at a time,
|
|
360
|
-
// and a deploy stops the old container before the new one starts), so none names a worker.
|
|
361
|
-
/** Records that the wakeup took `at`. */
|
|
362
|
-
export async function recordWakeupRegistration(db, at) {
|
|
363
|
-
await db.query(`UPDATE ${WAKEUP_TABLE} SET at = $1, last_error = NULL WHERE id = 1`, [at]);
|
|
364
|
-
}
|
|
365
|
-
/** Records why the last registration failed. */
|
|
366
|
-
export async function recordWakeupError(db, error) {
|
|
367
|
-
await db.query(`UPDATE ${WAKEUP_TABLE} SET last_error = $1 WHERE id = 1`, [error]);
|
|
368
|
-
}
|
|
369
|
-
/** Records that the worker stops itself now. */
|
|
370
|
-
export async function recordWakeupStop(db) {
|
|
371
|
-
await db.query(`UPDATE ${WAKEUP_TABLE} SET stopped_at = now() WHERE id = 1`);
|
|
322
|
+
export async function spendInviteRequest(db, nonce, expiresAt) {
|
|
323
|
+
// Rows go an hour after they expire, not at it: the app's clock decides when a request is
|
|
324
|
+
// past, and a database clock ahead of it must not forget a nonce the app still accepts.
|
|
325
|
+
await db.delete(inviteRequests).where(lte(inviteRequests.expiresAt, sql `now() - interval '1 hour'`));
|
|
326
|
+
const spent = await db
|
|
327
|
+
.insert(inviteRequests)
|
|
328
|
+
.values({ nonce, expiresAt })
|
|
329
|
+
.onConflictDoNothing({ target: inviteRequests.nonce })
|
|
330
|
+
.returning({ nonce: inviteRequests.nonce });
|
|
331
|
+
return spent.length === 1;
|
|
372
332
|
}
|
|
373
333
|
//# 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,
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|