okmodel 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -18
- package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
- package/dist/adapters/pg/postgres-extra.d.ts +22 -0
- package/dist/contracts/catalog/document.d.ts +19 -5
- package/dist/contracts/catalog/object.d.ts +2 -1
- package/dist/contracts/catalog/policy.d.ts +48 -0
- package/dist/contracts/catalog/types.d.ts +25 -1
- package/dist/contracts/catalog/view.d.ts +2 -0
- package/dist/contracts/internal.js +1 -1
- package/dist/contracts/location.d.ts +10 -0
- package/dist/dialects/pg/column.d.ts +13 -2
- package/dist/dialects/pg/ddl.d.ts +37 -1
- package/dist/dialects/pg/emit.d.ts +19 -1
- package/dist/dialects/pg/errors.d.ts +32 -1
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/pg-trgm.js +1 -1
- package/dist/dialects/pg/filter-parse.d.ts +10 -3
- package/dist/dialects/pg/fn/index.js +10 -2
- package/dist/dialects/pg/index.d.ts +2 -1
- package/dist/dialects/pg/index.js +29 -17
- package/dist/dialects/pg/introspect.d.ts +7 -0
- package/dist/dialects/pg/model.d.ts +7 -2
- package/dist/dialects/pg/ops/or.d.ts +1 -1
- package/dist/dialects/pg/owner.d.ts +42 -0
- package/dist/dialects/pg/path-endpoint.d.ts +20 -0
- package/dist/dialects/pg/role/check.d.ts +3 -0
- package/dist/dialects/pg/role/index.d.ts +71 -6
- package/dist/dialects/pg/role/sql.d.ts +11 -0
- package/dist/dialects/pg/schema.d.ts +2 -0
- package/dist/dialects/pg/table.d.ts +25 -3
- package/dist/dialects/pg/temporal.d.ts +5 -76
- package/dist/dialects/pg/tenancy.d.ts +45 -3
- package/dist/dialects/pg/view/client.d.ts +19 -1
- package/dist/dialects/pg/view/index.d.ts +4 -0
- package/dist/dialects/pg/view/index.js +21 -178
- package/dist/dialects/pg/view/refresh.d.ts +24 -0
- package/dist/runtime/batch.d.ts +1 -1
- package/dist/runtime/client.d.ts +6 -1
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/drift.d.ts +9 -0
- package/dist/runtime/inflight.d.ts +14 -0
- package/dist/runtime/pg/bun.js +6 -5
- package/dist/runtime/pg/pg.js +5 -5
- package/dist/runtime/pg/pglite.js +6 -5
- package/dist/runtime/pg/postgresjs.js +5 -5
- package/dist/runtime/plan.d.ts +22 -1
- package/dist/runtime/predicate.d.ts +19 -0
- package/dist/runtime/safety/index.js +1 -1
- package/dist/runtime/tenancy/composite.d.ts +26 -0
- package/dist/runtime/tenancy/context.d.ts +16 -0
- package/dist/runtime/tenancy/index.d.ts +9 -5
- package/dist/runtime/tenancy/index.js +1508 -37
- package/dist/runtime/tenancy/path.d.ts +87 -0
- package/dist/runtime/tenancy/rls.d.ts +67 -0
- package/dist/runtime/tenancy/via.d.ts +22 -0
- package/dist/runtime/traits/index.js +17 -11
- package/dist/runtime/types.d.ts +13 -1
- package/dist/runtime/validate/index.js +4 -4
- package/dist/runtime/write.d.ts +1 -1
- package/dist/shared/09p049xa.js +702 -0
- package/dist/shared/{ykv7wh5b.js → 1f0xa3cb.js} +5 -5
- package/dist/shared/1nw1f3vs.js +23 -0
- package/dist/shared/{6p5k20sm.js → 1z04fa85.js} +21 -11
- package/dist/shared/{p7pfx624.js → 2cg3wnxc.js} +314 -65
- package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
- package/dist/shared/{qgvt8nx1.js → 2z7wv5tj.js} +21 -5
- package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
- package/dist/shared/{sf8m6tfy.js → 32cnm7kn.js} +8 -3
- package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
- package/dist/shared/{jdgqqx31.js → 3fqwmr09.js} +38 -14
- package/dist/shared/{922qbgnw.js → 3qnhc7w5.js} +16 -9
- package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
- package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
- package/dist/shared/{k7cc9nzk.js → 5d8t84hg.js} +15 -3
- package/dist/shared/{fa9vt245.js → 5j36z8dn.js} +17 -6
- package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
- package/dist/shared/8dqhth8c.js +12 -0
- package/dist/shared/8etn7d8w.js +62 -0
- package/dist/shared/{g516zdsb.js → 8x72s86k.js} +476 -30
- package/dist/shared/{nkc9sfqb.js → 929h19xp.js} +145 -7
- package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
- package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
- package/dist/shared/{b066g80h.js → ag4c7516.js} +2 -2
- package/dist/shared/b3awxn7h.js +45 -0
- package/dist/shared/b4v57v3x.js +9 -0
- package/dist/shared/bdk4sty6.js +58 -0
- package/dist/shared/{s6gqfn5j.js → c58secgj.js} +125 -8
- package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
- package/dist/shared/d5azex6s.js +6 -0
- package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
- package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
- package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
- package/dist/shared/{wxn4b7pp.js → f0f1n281.js} +59 -12
- package/dist/shared/{gdh9mrtp.js → g90m9b0g.js} +3 -2
- package/dist/shared/{tbszx8mz.js → gwn6v667.js} +2 -2
- package/dist/shared/{c8aghh05.js → h15jx9bc.js} +8 -2
- package/dist/shared/{3p4b1fg0.js → hazqqecq.js} +17 -93
- package/dist/shared/{4ef7mmqf.js → j3rkk835.js} +34 -11
- package/dist/shared/{bt9sfq0z.js → j60w0dvr.js} +56 -21
- package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
- package/dist/shared/k68eedmy.js +85 -0
- package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
- package/dist/shared/{ckjbyrt9.js → mb2ycs1p.js} +7 -9
- package/dist/shared/{tq9afp8d.js → n076ydsc.js} +1 -1
- package/dist/shared/{xhh1srw3.js → nx9hh281.js} +50 -32
- package/dist/shared/{kdkxh3kb.js → p248q79y.js} +58 -59
- package/dist/shared/{990a291j.js → p6bjzq9q.js} +14 -9
- package/dist/shared/{h4nqvjkp.js → pjr6xrm5.js} +2 -2
- package/dist/shared/pz6rj8ay.js +212 -0
- package/dist/shared/{5vdm4sgp.js → q3j1tq7a.js} +36 -23
- package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
- package/dist/shared/{dxjp14rk.js → r11n8grd.js} +2 -2
- package/dist/shared/r1fdd0f6.js +19 -0
- package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
- package/dist/shared/rhs3xe4k.js +7 -0
- package/dist/shared/{jsd07prz.js → rpycarcr.js} +6 -6
- package/dist/shared/{xsq7v6wp.js → sbdme3vy.js} +14 -2
- package/dist/shared/{xdpj5kkm.js → t3g0k88x.js} +775 -264
- package/dist/shared/{ayz64vzj.js → ttmg09vd.js} +708 -19
- package/dist/shared/{r6pj4fq2.js → venqrjna.js} +8 -4
- package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
- package/dist/shared/{9tgmr1gh.js → vmfkhszw.js} +44 -9
- package/dist/shared/{wqem4jed.js → wkme6tep.js} +11 -9
- package/dist/shared/{qy9rdq0g.js → wt5rzv1h.js} +15 -11
- package/dist/shared/wxr8cwns.js +6 -0
- package/dist/shared/{bzsjtd7c.js → wynzeggs.js} +19 -14
- package/dist/shared/x3jc53kg.js +220 -0
- package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
- package/dist/shared/{fyqkk1m0.js → xq0mvhts.js} +10 -9
- package/dist/shared/xqve9598.js +53 -0
- package/dist/shared/{47xzb9fc.js → y2t3jktd.js} +2 -2
- package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
- package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
- package/dist/shared/{h8ne16fq.js → ys0xzyaj.js} +1 -1
- package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
- package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
- package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
- package/dist/shared/{fb00y8fp.js → zm0mafk4.js} +12 -6
- package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
- package/dist/tooling/cli.js +97 -24
- package/dist/tooling/migrate/apply.d.ts +14 -0
- package/dist/tooling/migrate/check.d.ts +3 -4
- package/dist/tooling/migrate/classify.d.ts +4 -0
- package/dist/tooling/migrate/commands.d.ts +1 -2
- package/dist/tooling/migrate/config.d.ts +3 -2
- package/dist/tooling/migrate/drift.d.ts +1 -1
- package/dist/tooling/migrate/estimate.d.ts +14 -1
- package/dist/tooling/migrate/files.d.ts +11 -1
- package/dist/tooling/migrate/index.js +17 -16
- package/dist/tooling/migrate/lint.d.ts +4 -4
- package/dist/tooling/migrate/lost.d.ts +28 -0
- package/dist/tooling/migrate/paths.d.ts +14 -0
- package/dist/tooling/migrate/plan.d.ts +10 -3
- package/dist/tooling/migrate/policy.d.ts +11 -0
- package/dist/tooling/migrate/runner.d.ts +25 -0
- package/dist/tooling/migrate/sandbox.d.ts +57 -0
- package/dist/tooling/migrate/sql-lex.d.ts +39 -0
- package/dist/tooling/migrate/text.d.ts +42 -0
- package/dist/tooling/node-version.d.ts +21 -0
- package/dist/tooling/testing/factories.d.ts +10 -0
- package/dist/tooling/testing/facts.d.ts +33 -5
- package/dist/tooling/testing/index.d.ts +1 -1
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +15 -8
- package/package.json +10 -6
- package/dist/shared/0v1g424p.js +0 -26
- package/dist/shared/r73pt594.js +0 -426
- package/dist/shared/rasvvzgz.js +0 -21
|
@@ -49,6 +49,14 @@ export type ApplyRequest = {
|
|
|
49
49
|
* `SET ROLE` before any statement. That statement is not a plan step.
|
|
50
50
|
*/
|
|
51
51
|
readonly migrationRole?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Application role.
|
|
54
|
+
*
|
|
55
|
+
* After the plan, this role receives `SELECT` on `okm_meta` and nothing
|
|
56
|
+
* else. `connect()` can then read the stored hash. Check scratch paths
|
|
57
|
+
* leave this unset.
|
|
58
|
+
*/
|
|
59
|
+
readonly appRole?: string;
|
|
52
60
|
/**
|
|
53
61
|
* Schema the migration role may create in.
|
|
54
62
|
*
|
|
@@ -95,6 +103,12 @@ export type ApplyReport = {
|
|
|
95
103
|
/** Set when the runner issued `SET ROLE` at session start. */
|
|
96
104
|
readonly setRole?: string;
|
|
97
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Creates `okm_meta`, `okm_history`, and `okm_backfill` when they are absent.
|
|
108
|
+
*
|
|
109
|
+
* @param execute - Runs one statement on the apply or check connection
|
|
110
|
+
*/
|
|
111
|
+
export declare function ensureMigrationTables(execute: (sql: string) => Promise<unknown>): Promise<void>;
|
|
98
112
|
/**
|
|
99
113
|
* Groups pending steps into the transactions apply will run.
|
|
100
114
|
*
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `okm migrate check` proves a migration history is sound (D196).
|
|
3
3
|
*
|
|
4
|
-
* The command replays every file into a scratch schema
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* linter. Nothing here changes apply.
|
|
4
|
+
* The command replays every file into a scratch schema inside one transaction
|
|
5
|
+
* and rolls that transaction back. It also checks the previous catalog, the
|
|
6
|
+
* head, and the linter. Nothing here changes apply.
|
|
8
7
|
*/
|
|
9
8
|
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
10
9
|
import { type MigrationClass } from "./classify.js";
|
|
@@ -76,6 +76,10 @@ export declare const STEP_CLASS: {
|
|
|
76
76
|
readonly "rename-sequence": "contract";
|
|
77
77
|
readonly "backfill-expand": "expand";
|
|
78
78
|
readonly "backfill-contract": "contract";
|
|
79
|
+
readonly "enable-rls": "expand";
|
|
80
|
+
readonly "create-policy": "expand";
|
|
81
|
+
readonly "drop-policy": "contract";
|
|
82
|
+
readonly "disable-rls": "contract";
|
|
79
83
|
readonly "raw-sql": "unclassified";
|
|
80
84
|
};
|
|
81
85
|
/** A planner operation. */
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* The bin wrapper imports {@link run}. Importing this module does not parse
|
|
5
5
|
* `process.argv`.
|
|
6
6
|
*/
|
|
7
|
-
import { OkmError } from "../../contracts/error.js";
|
|
8
7
|
/** Where a command reads the project and writes its text. */
|
|
9
8
|
export type CommandIo = {
|
|
10
9
|
readonly cwd?: string;
|
|
@@ -18,7 +17,7 @@ export type CommandIo = {
|
|
|
18
17
|
* @param error - Failure from a command
|
|
19
18
|
* @returns The stderr text, including the trailing newline
|
|
20
19
|
*/
|
|
21
|
-
export declare function formatFailure(error:
|
|
20
|
+
export declare function formatFailure(error: unknown, verbose?: boolean): string;
|
|
22
21
|
/**
|
|
23
22
|
* Runs one `okm` invocation.
|
|
24
23
|
*
|
|
@@ -59,10 +59,11 @@ export type MigrateConfig = {
|
|
|
59
59
|
readonly statementTimeoutMs?: number;
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
|
-
* Migration role and
|
|
62
|
+
* Migration role, application role, and the optional reporting role.
|
|
63
63
|
*
|
|
64
64
|
* A name is external unless it is also listed in `managed`. The plan creates
|
|
65
|
-
* a managed role and never drops one.
|
|
65
|
+
* a managed role and never drops one. `reporting` receives select only.
|
|
66
|
+
* `columns` grants select or update on named columns.
|
|
66
67
|
*/
|
|
67
68
|
readonly roles?: RolesInput;
|
|
68
69
|
};
|
|
@@ -18,7 +18,7 @@ import type { RolesInput } from "../../dialects/pg/role/index.js";
|
|
|
18
18
|
* @param author - Schema catalog, including roles when the config names them
|
|
19
19
|
* @param roles - `defineConfig({ roles })`, when set
|
|
20
20
|
*/
|
|
21
|
-
export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput): Promise<void>;
|
|
21
|
+
export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput, allowPooler?: boolean): Promise<void>;
|
|
22
22
|
/**
|
|
23
23
|
* Turns a postgres.js handle into the query shape introspection uses.
|
|
24
24
|
*
|
|
@@ -40,7 +40,20 @@ export declare function readRowEstimates(url: string, schema: string, tables: re
|
|
|
40
40
|
* @param estimates - `reltuples` for tables that exist on the target
|
|
41
41
|
* @returns The text after `-- lock: `
|
|
42
42
|
*/
|
|
43
|
-
export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>): string;
|
|
43
|
+
export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>, aliases?: ReadonlyMap<string, string>): string;
|
|
44
|
+
/**
|
|
45
|
+
* Maps a rename destination to the table that still has `reltuples`.
|
|
46
|
+
*
|
|
47
|
+
* The rename statement names the old table. Later steps name the new one,
|
|
48
|
+
* which is not in `pg_class` yet.
|
|
49
|
+
*
|
|
50
|
+
* @param steps - Plan steps
|
|
51
|
+
* @returns New name to old name
|
|
52
|
+
*/
|
|
53
|
+
export declare function renameAliases(steps: readonly {
|
|
54
|
+
readonly kind?: string;
|
|
55
|
+
readonly sql: string;
|
|
56
|
+
}[]): ReadonlyMap<string, string>;
|
|
44
57
|
/**
|
|
45
58
|
* Compact phrase for one estimate.
|
|
46
59
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Migration files on disk: SQL from {@link formatPlan} plus the catalog hash.
|
|
3
3
|
*/
|
|
4
4
|
import { parseCatalog } from "../../contracts/catalog/document.js";
|
|
5
|
-
import { type PlanStep } from "./plan.js";
|
|
5
|
+
import { type MigrationPlan, type PlanStep } from "./plan.js";
|
|
6
6
|
/** One migration the applier can run and resume. */
|
|
7
7
|
export type StoredMigration = {
|
|
8
8
|
readonly id: string;
|
|
@@ -25,6 +25,16 @@ export type StoredMigration = {
|
|
|
25
25
|
* @returns Migrations in apply order
|
|
26
26
|
*/
|
|
27
27
|
export declare function loadMigrations(directory: string): readonly StoredMigration[];
|
|
28
|
+
/**
|
|
29
|
+
* Reads one migration SQL file.
|
|
30
|
+
*
|
|
31
|
+
* Line endings and a UTF-8 BOM are normalized. A non-empty file with no steps
|
|
32
|
+
* and no `-- no steps` line is refused.
|
|
33
|
+
*
|
|
34
|
+
* @param path - `*.sql` path
|
|
35
|
+
* @returns The plan
|
|
36
|
+
*/
|
|
37
|
+
export declare function readMigrationPlan(path: string): MigrationPlan;
|
|
28
38
|
/**
|
|
29
39
|
* Loads the last migration's catalog, which is the head snapshot.
|
|
30
40
|
*
|
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import"../../shared/
|
|
2
|
-
import"../../shared/
|
|
1
|
+
import"../../shared/wkme6tep.js";
|
|
2
|
+
import"../../shared/qq3899ht.js";
|
|
3
3
|
import"../../shared/n442h54c.js";
|
|
4
4
|
import {
|
|
5
5
|
applyTarget2,
|
|
6
6
|
backfillTiming2
|
|
7
|
-
} from "../../shared/
|
|
7
|
+
} from "../../shared/nx9hh281.js";
|
|
8
8
|
import {
|
|
9
|
-
loadMigrations,
|
|
10
|
-
loadHeadSnapshot,
|
|
11
9
|
readReference
|
|
12
|
-
} from "../../shared/
|
|
13
|
-
import"../../shared/
|
|
14
|
-
import"../../shared/
|
|
10
|
+
} from "../../shared/p248q79y.js";
|
|
11
|
+
import"../../shared/knmes2kh.js";
|
|
12
|
+
import"../../shared/vjpnzwjz.js";
|
|
15
13
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
projectPath,
|
|
15
|
+
loadMigrations,
|
|
16
|
+
loadHeadSnapshot,
|
|
18
17
|
loadBuiltSchema,
|
|
19
18
|
loadConfig
|
|
20
|
-
} from "../../shared/
|
|
21
|
-
import
|
|
22
|
-
|
|
19
|
+
} from "../../shared/t3g0k88x.js";
|
|
20
|
+
import {
|
|
21
|
+
assertTargetPolicy,
|
|
22
|
+
selectTarget
|
|
23
|
+
} from "../../shared/ttmg09vd.js";
|
|
24
|
+
import"../../shared/9s0ypp87.js";
|
|
23
25
|
import {
|
|
24
26
|
OkmError2
|
|
25
27
|
} from "../../shared/52szzakm.js";
|
|
@@ -29,12 +31,11 @@ function defineConfig(config) {
|
|
|
29
31
|
return config;
|
|
30
32
|
}
|
|
31
33
|
// src/tooling/migrate/provision.ts
|
|
32
|
-
import { join } from "node:path";
|
|
33
34
|
async function provision(target, cwd = process.cwd()) {
|
|
34
35
|
const config = await loadConfig(cwd);
|
|
35
36
|
const selected = selectTarget(config, target);
|
|
36
37
|
assertTargetPolicy(selected, "provision");
|
|
37
|
-
const directory =
|
|
38
|
+
const directory = projectPath(cwd, config.migrations ?? "migrations");
|
|
38
39
|
const migrations = loadMigrations(directory);
|
|
39
40
|
const head = loadHeadSnapshot(directory);
|
|
40
41
|
if (head === undefined) {
|
|
@@ -57,7 +58,7 @@ async function provision(target, cwd = process.cwd()) {
|
|
|
57
58
|
reference: readReference(built.tables, built.casing, head.catalog),
|
|
58
59
|
only: true
|
|
59
60
|
},
|
|
60
|
-
...config.roles !== undefined ? { migrationRole: config.roles.migration } : {},
|
|
61
|
+
...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
|
|
61
62
|
lintDirectory: directory
|
|
62
63
|
});
|
|
63
64
|
if (report.applied.length === 0)
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Runs the migration linter.
|
|
3
3
|
*
|
|
4
4
|
* The linter reads a plan and the two catalogs. It does not connect.
|
|
5
|
-
* An override is a `-- okm-allow` line on the
|
|
6
|
-
*
|
|
5
|
+
* An override is a `-- okm-allow` line on the step. It silences that code
|
|
6
|
+
* on every statement of the step, and only with a reason.
|
|
7
7
|
*/
|
|
8
8
|
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
9
9
|
import { OkmError } from "../../contracts/error.js";
|
|
10
10
|
import { type LintSeverity } from "./lint-rules.js";
|
|
11
|
-
import {
|
|
12
|
-
/** One finding. `place` is `step N` or `table.column
|
|
11
|
+
import type { MigrationPlan } from "./plan.js";
|
|
12
|
+
/** One finding. `place` is `step N`, `step N statement M`, or `table.column`. */
|
|
13
13
|
export type Finding = {
|
|
14
14
|
readonly severity: LintSeverity;
|
|
15
15
|
readonly code: string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A session that dies in the middle of a command.
|
|
3
|
+
*
|
|
4
|
+
* A commit that was sent and never answered stays OKM1401. Every other
|
|
5
|
+
* dropped connection is unavailable. Cleanup that throws is ignored so the
|
|
6
|
+
* original failure is the one the command reports.
|
|
7
|
+
*/
|
|
8
|
+
import { OkmError } from "../../contracts/error.js";
|
|
9
|
+
/**
|
|
10
|
+
* Maps a lost session to unavailable or OKM1401.
|
|
11
|
+
*
|
|
12
|
+
* An {@link OkmError} that already has one of those kinds is returned. An
|
|
13
|
+
* `invalid` error whose cause is a lost session keeps its message and takes
|
|
14
|
+
* that kind. Anything else returns `undefined`.
|
|
15
|
+
*
|
|
16
|
+
* @param error - Caught value
|
|
17
|
+
* @returns The session failure, or `undefined` when this is some other error
|
|
18
|
+
*/
|
|
19
|
+
export declare function sessionFailure(error: unknown): OkmError | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Runs cleanup and ignores its failure.
|
|
22
|
+
*
|
|
23
|
+
* A dead session must not replace the error the command is already reporting,
|
|
24
|
+
* and it must not leave the process waiting on that session.
|
|
25
|
+
*
|
|
26
|
+
* @param run - Close, release, or unlock
|
|
27
|
+
*/
|
|
28
|
+
export declare function finishQuiet(run: () => Promise<unknown>): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config paths resolved against the project directory.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a config path against `cwd`.
|
|
6
|
+
*
|
|
7
|
+
* An absolute segment stays absolute. A relative segment, including `..`,
|
|
8
|
+
* is resolved from `cwd`.
|
|
9
|
+
*
|
|
10
|
+
* @param cwd - Project directory
|
|
11
|
+
* @param parts - `schema`, `tables`, `migrations`, `out`, a seed file, or a child
|
|
12
|
+
* @returns An absolute path
|
|
13
|
+
*/
|
|
14
|
+
export declare function projectPath(cwd: string, ...parts: readonly string[]): string;
|
|
@@ -70,10 +70,10 @@ export type PlanStep = {
|
|
|
70
70
|
*/
|
|
71
71
|
readonly behavior?: "change";
|
|
72
72
|
/**
|
|
73
|
-
* `-- okm-allow` lines
|
|
73
|
+
* `-- okm-allow` lines on the step.
|
|
74
74
|
*
|
|
75
|
-
* A reason silences that code
|
|
76
|
-
*
|
|
75
|
+
* A reason silences that code on every statement of the step. An empty
|
|
76
|
+
* reason, or a code no statement triggered, is OKM1510 and silences nothing.
|
|
77
77
|
*/
|
|
78
78
|
readonly allows?: readonly StepAllow[];
|
|
79
79
|
};
|
|
@@ -128,6 +128,13 @@ export declare function formatPlan(plan: MigrationPlan, lockText?: (step: PlanSt
|
|
|
128
128
|
/**
|
|
129
129
|
* Reads a plan written by {@link formatPlan}.
|
|
130
130
|
*
|
|
131
|
+
* A blank line ends a step. A blank line inside a quote, a dollar quote, a
|
|
132
|
+
* block comment, or a `BEGIN ATOMIC` routine body stays in that step, and so
|
|
133
|
+
* does a `--` line while that quote or body is still open.
|
|
134
|
+
*
|
|
135
|
+
* `\r\n` and a lone `\r` are read as `\n`. A leading UTF-8 BOM is stripped.
|
|
136
|
+
* UTF-16 is refused.
|
|
137
|
+
*
|
|
131
138
|
* @param text - SQL file text
|
|
132
139
|
* @returns The plan. The header class is kept when the file has no steps
|
|
133
140
|
*/
|
|
@@ -56,6 +56,17 @@ export declare function assertTargetAlias(targets: readonly TargetRecord[]): voi
|
|
|
56
56
|
* @param allowPooler - The invocation opted in
|
|
57
57
|
*/
|
|
58
58
|
export declare function assertDirectConnection(url: string, allowPooler: boolean): void;
|
|
59
|
+
/**
|
|
60
|
+
* Refuses a session whose backend pid changes between two statements.
|
|
61
|
+
*
|
|
62
|
+
* The two reads are separate round trips and are not wrapped in a
|
|
63
|
+
* transaction. A transaction-mode pooler can hand back a different backend
|
|
64
|
+
* for the second one. `--allow-pooler` skips the probe.
|
|
65
|
+
*
|
|
66
|
+
* @param readPid - Reads `pg_backend_pid()` on the session under test
|
|
67
|
+
* @param allowPooler - The invocation opted in
|
|
68
|
+
*/
|
|
69
|
+
export declare function assertStableBackend(readPid: () => Promise<string>, allowPooler: boolean): Promise<void>;
|
|
59
70
|
/**
|
|
60
71
|
* Lists configured targets.
|
|
61
72
|
*
|
|
@@ -95,7 +95,32 @@ export declare function assumeRole(connection: DriverConnection, role: string, s
|
|
|
95
95
|
* @param schema - Schema the migration writes
|
|
96
96
|
* @param role - Role that was just created
|
|
97
97
|
*/
|
|
98
|
+
/**
|
|
99
|
+
* Grants the application role `SELECT` on `okm_meta`.
|
|
100
|
+
*
|
|
101
|
+
* No other privilege. When the session has `SET ROLE`, the grant runs as
|
|
102
|
+
* the session user and the role is restored. A second apply grants the
|
|
103
|
+
* same privilege again.
|
|
104
|
+
*
|
|
105
|
+
* @param connection - Reserved connection
|
|
106
|
+
* @param role - Application role. Omitted when the config names none
|
|
107
|
+
* @param session - Role the runner already assumed
|
|
108
|
+
*/
|
|
109
|
+
export declare function grantAppMeta(connection: DriverConnection, role: string | undefined, session: {
|
|
110
|
+
setRole?: string;
|
|
111
|
+
}): Promise<void>;
|
|
98
112
|
export declare function grantSchema(connection: DriverConnection, schema: string, role: string): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Fails when `GRANT USAGE ON SCHEMA` did not add the role.
|
|
115
|
+
*
|
|
116
|
+
* Postgres warns and changes nothing when the acting role does not own the
|
|
117
|
+
* schema. A later check then reports the grant forever. The migration role
|
|
118
|
+
* has to own the schema first.
|
|
119
|
+
*
|
|
120
|
+
* @param connection - Reserved connection, in the grant's transaction
|
|
121
|
+
* @param sql - The statement that just ran
|
|
122
|
+
*/
|
|
123
|
+
export declare function assertSchemaUsageLanded(connection: DriverConnection, sql: string): Promise<void>;
|
|
99
124
|
/**
|
|
100
125
|
* Reports whether Postgres refuses to run `sql` inside a transaction.
|
|
101
126
|
*
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replays `okm migrate check` inside one transaction and rolls it back.
|
|
3
|
+
*
|
|
4
|
+
* The scratch schema is created in that transaction. Nothing here updates a
|
|
5
|
+
* system catalog or a real ACL. A step that cannot run in a transaction is
|
|
6
|
+
* rewritten to the transactional form, or skipped and named.
|
|
7
|
+
*/
|
|
8
|
+
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
9
|
+
import type { MigrateConfig } from "./config.js";
|
|
10
|
+
import { type MigrationPlan, type PlanStep } from "./plan.js";
|
|
11
|
+
/** One migration the check replays. */
|
|
12
|
+
export type CheckMigration = {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly catalog: Catalog;
|
|
15
|
+
readonly steps: readonly PlanStep[];
|
|
16
|
+
};
|
|
17
|
+
/** The first migration whose replay does not match its catalog. */
|
|
18
|
+
export type ReplayStop = {
|
|
19
|
+
readonly index: number;
|
|
20
|
+
readonly back: MigrationPlan;
|
|
21
|
+
};
|
|
22
|
+
/** Replay result. `skips` are statements check did not execute. */
|
|
23
|
+
export type ReplayOutcome = {
|
|
24
|
+
readonly skips: readonly string[];
|
|
25
|
+
readonly stop?: ReplayStop;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Replays `history` in one transaction and rolls it back.
|
|
29
|
+
*
|
|
30
|
+
* @param url - Target URL
|
|
31
|
+
* @param scratch - Schema created inside the transaction
|
|
32
|
+
* @param config - Roles and timeouts
|
|
33
|
+
* @param allowPooler - Passed through to the pooler guard
|
|
34
|
+
* @param history - Migrations in apply order
|
|
35
|
+
* @returns Skipped statements, and the first catalog mismatch
|
|
36
|
+
*/
|
|
37
|
+
export declare function replayCheckHistory(url: string, scratch: string, config: MigrateConfig, allowPooler: boolean, history: readonly CheckMigration[]): Promise<ReplayOutcome>;
|
|
38
|
+
/**
|
|
39
|
+
* Compares a head snapshot with a full replay. Both run in rolled-back transactions.
|
|
40
|
+
*
|
|
41
|
+
* @param url - Target URL
|
|
42
|
+
* @param provided - Scratch schema for the snapshot
|
|
43
|
+
* @param replayed - Scratch schema for the history
|
|
44
|
+
* @param config - Roles and timeouts
|
|
45
|
+
* @param allowPooler - Passed through to the pooler guard
|
|
46
|
+
* @param history - Migrations in apply order
|
|
47
|
+
* @returns The difference, or `undefined` when the catalogs match
|
|
48
|
+
*/
|
|
49
|
+
export declare function proveCheckSnapshot(url: string, provided: string, replayed: string, config: MigrateConfig, allowPooler: boolean, history: readonly CheckMigration[]): Promise<string | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Rewrites `"public".` to the scratch schema.
|
|
52
|
+
*
|
|
53
|
+
* @param steps - Plan steps
|
|
54
|
+
* @param schema - Scratch schema
|
|
55
|
+
* @returns The same steps, aimed at `schema`
|
|
56
|
+
*/
|
|
57
|
+
export declare function retargetSteps(steps: readonly PlanStep[], schema: string): readonly PlanStep[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits one migration step into statements.
|
|
3
|
+
*
|
|
4
|
+
* Comments are removed. A semicolon splits only at the top level.
|
|
5
|
+
* `standard_conforming_strings` is on: a plain `'...'` ends on a single
|
|
6
|
+
* quote, and `''` is one quote inside it. `E'...'` still treats a backslash
|
|
7
|
+
* as an escape. A dollar quote runs until the same tag; a different tag
|
|
8
|
+
* inside it is text, so `$outer$ ... $inner$ ... $inner$ ... $outer$` stays
|
|
9
|
+
* one span. A `BEGIN ATOMIC` body of `CREATE FUNCTION` or `CREATE PROCEDURE`
|
|
10
|
+
* stays in that statement: a semicolon inside it does not split, and
|
|
11
|
+
* `CASE ... END` does not close the body. `BEGIN ATOMIC` elsewhere still
|
|
12
|
+
* splits. An unterminated string, quoted identifier, block comment, dollar
|
|
13
|
+
* quote, or atomic body is one opaque tail. This function does not throw.
|
|
14
|
+
*/
|
|
15
|
+
/** Statements of `sql`, comments removed, with no trailing semicolon. */
|
|
16
|
+
export declare function sqlStatements(sql: string): readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Whether `sql` still has an open quote or block comment.
|
|
19
|
+
*
|
|
20
|
+
* A line comment does not count: it ends at the newline or at the end.
|
|
21
|
+
* An unclosed `BEGIN ATOMIC` routine body counts too.
|
|
22
|
+
*
|
|
23
|
+
* @param sql - Step text accumulated so far
|
|
24
|
+
* @returns `true` when a later blank line is still inside that quote or body
|
|
25
|
+
*/
|
|
26
|
+
export declare function sqlQuoteOpen(sql: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Transaction control in a migration step.
|
|
29
|
+
*
|
|
30
|
+
* Dollar-quoted bodies are scanned only for a `DO` block. `CREATE FUNCTION`
|
|
31
|
+
* and `CREATE PROCEDURE` may commit inside their own body. A `BEGIN ATOMIC`
|
|
32
|
+
* body stays one statement, so its `END` is not transaction control. A bare
|
|
33
|
+
* `BEGIN` or `END` inside a `DO` body is plpgsql and is ignored. `ROLLBACK TO`
|
|
34
|
+
* does not end the outer transaction.
|
|
35
|
+
*
|
|
36
|
+
* @param sql - Step text
|
|
37
|
+
* @returns The command, or `undefined` when the step stays in one transaction
|
|
38
|
+
*/
|
|
39
|
+
export declare function transactionControl(sql: string): string | undefined;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration file text before it is parsed.
|
|
3
|
+
*
|
|
4
|
+
* Line endings and a UTF-8 BOM are normalized here, once, so every reader
|
|
5
|
+
* sees the same plan. UTF-16 is refused. The kind stays `invalid`.
|
|
6
|
+
*/
|
|
7
|
+
import { OkmError } from "../../contracts/error.js";
|
|
8
|
+
/** Why a migration file was refused before it was applied. */
|
|
9
|
+
export type MigrationTextReason = "no-steps" | "utf16";
|
|
10
|
+
/**
|
|
11
|
+
* A migration file this version will not apply.
|
|
12
|
+
*
|
|
13
|
+
* `reason` is `no-steps` or `utf16`. The kind stays `invalid`.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MigrationTextError extends OkmError {
|
|
16
|
+
/** Which check refused the file. */
|
|
17
|
+
readonly reason: MigrationTextReason;
|
|
18
|
+
/**
|
|
19
|
+
* @param reason - Which check failed
|
|
20
|
+
* @param message - What the author should change
|
|
21
|
+
*/
|
|
22
|
+
constructor(reason: MigrationTextReason, message: string);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Strips a UTF-8 BOM and turns `\r\n` and a lone `\r` into `\n`.
|
|
26
|
+
*
|
|
27
|
+
* @param text - File text
|
|
28
|
+
* @param label - File name used when the text is UTF-16
|
|
29
|
+
* @returns Text `parsePlan` can split on `\n`
|
|
30
|
+
*/
|
|
31
|
+
export declare function prepareMigrationText(text: string, label?: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Refuses a non-empty file that produced no steps and has no `-- no steps` line.
|
|
34
|
+
*
|
|
35
|
+
* An intentional empty plan from `formatPlan` carries that line.
|
|
36
|
+
* Anything else would apply as "nothing to apply" and skip the linter.
|
|
37
|
+
*
|
|
38
|
+
* @param text - Text already passed through {@link prepareMigrationText}
|
|
39
|
+
* @param stepCount - Steps the parser returned
|
|
40
|
+
* @param label - File name in the error
|
|
41
|
+
*/
|
|
42
|
+
export declare function assertMigrationSteps(text: string, stepCount: number, label: string): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node version `okm` needs in order to load `okmodel.config.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Node 22.18 unflags type stripping. Bun loads TypeScript itself, so the
|
|
5
|
+
* check does not apply when `bun --bun` is the runtime.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Whether `version` can strip types in a `.ts` config.
|
|
9
|
+
*
|
|
10
|
+
* @param version - `process.version`, such as `v22.18.0`
|
|
11
|
+
* @returns `true` for Node 22.18 and every newer major
|
|
12
|
+
*/
|
|
13
|
+
export declare function nodeSupportsConfigTypes(version: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Startup text when this process cannot load a TypeScript config.
|
|
16
|
+
*
|
|
17
|
+
* @param version - `process.version`
|
|
18
|
+
* @param bunRuntime - `true` when Bun is running the process
|
|
19
|
+
* @returns The stderr line, or `undefined` when the process can continue
|
|
20
|
+
*/
|
|
21
|
+
export declare function nodeStartupMessage(version: string, bunRuntime: boolean): string | undefined;
|
|
@@ -114,4 +114,14 @@ export declare function bindFactories(host: FactoryHost, definitions: Readonly<R
|
|
|
114
114
|
* @returns The inserted row
|
|
115
115
|
*/
|
|
116
116
|
export declare function insertRow(host: FactoryHost, table: string, overrides: Readonly<Record<string, unknown>> | undefined, children: Readonly<Record<string, number>> | undefined, inherited: string | undefined): Promise<Record<string, unknown>>;
|
|
117
|
+
/**
|
|
118
|
+
* Drops a cached factory row.
|
|
119
|
+
*
|
|
120
|
+
* `isolation()` deletes the row it inserted. A later table must not reuse that id.
|
|
121
|
+
*
|
|
122
|
+
* @param host - The open harness
|
|
123
|
+
* @param table - Table name
|
|
124
|
+
* @param tenant - Tenant scope, when the table is tenanted
|
|
125
|
+
*/
|
|
126
|
+
export declare function forgetCached(host: FactoryHost, table: string, tenant: string | undefined): void;
|
|
117
127
|
export {};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* What a factory needs from a compiled schema: required columns, foreign keys,
|
|
3
3
|
* and which tables are tenant or global.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `schemaPerTenant` and `databasePerTenant` are not in
|
|
5
|
+
* A table is tenant when its predicate binds a scope, and global when
|
|
6
|
+
* `global("reason")` opted it out. Composite and path use that probe.
|
|
7
|
+
* `schemaPerTenant` and `databasePerTenant` are not in this version.
|
|
8
8
|
*/
|
|
9
9
|
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
10
10
|
import type { QuerySchema, RelationModel } from "../../dialects/pg/model.js";
|
|
@@ -46,6 +46,25 @@ export type TableFacts = {
|
|
|
46
46
|
/** `global("reason")`. Absent on a tenant table and on a schema with no tenancy. */
|
|
47
47
|
readonly globalReason: string | undefined;
|
|
48
48
|
};
|
|
49
|
+
/** One view or materialized view. Isolation reads it; it does not insert into it. */
|
|
50
|
+
export type ViewFacts = {
|
|
51
|
+
/** Client name, camelCase. */
|
|
52
|
+
readonly name: string;
|
|
53
|
+
/** SQL name. */
|
|
54
|
+
readonly sql: string;
|
|
55
|
+
readonly kind: "view" | "materializedView";
|
|
56
|
+
/** Author SQL. */
|
|
57
|
+
readonly query: string;
|
|
58
|
+
readonly columns: readonly {
|
|
59
|
+
readonly field: string;
|
|
60
|
+
readonly dataType: string;
|
|
61
|
+
readonly guarded: boolean;
|
|
62
|
+
}[];
|
|
63
|
+
/** The predicate binds a scope. A global view is not tenant. */
|
|
64
|
+
readonly tenant: boolean;
|
|
65
|
+
/** `global("reason")` on the view. Absent when the view is not global. */
|
|
66
|
+
readonly globalReason: string | undefined;
|
|
67
|
+
};
|
|
49
68
|
/**
|
|
50
69
|
* Scope value used to ask a tenancy object whether a table is tenant.
|
|
51
70
|
*
|
|
@@ -56,13 +75,22 @@ export declare const tenantProbe = "00000000-0000-4000-8000-000000000001";
|
|
|
56
75
|
/**
|
|
57
76
|
* Reads every table in declaration order.
|
|
58
77
|
*
|
|
59
|
-
* Views
|
|
60
|
-
* of their own to insert, so they are left out.
|
|
78
|
+
* Views are read by {@link readViews}. They are not tables, so they are left out here.
|
|
61
79
|
*
|
|
62
80
|
* @param schema - Compiled schema
|
|
63
81
|
* @returns One facts object per table
|
|
64
82
|
*/
|
|
65
83
|
export declare function readFacts(schema: TestingSchema): readonly TableFacts[];
|
|
84
|
+
/**
|
|
85
|
+
* Reads every view and materialized view in model order.
|
|
86
|
+
*
|
|
87
|
+
* A global view is not tenant. The reason comes from the view declaration,
|
|
88
|
+
* which is not stored on the catalog.
|
|
89
|
+
*
|
|
90
|
+
* @param schema - Compiled schema
|
|
91
|
+
* @returns One facts object per view
|
|
92
|
+
*/
|
|
93
|
+
export declare function readViews(schema: TestingSchema): readonly ViewFacts[];
|
|
66
94
|
/**
|
|
67
95
|
* A column the factory must set when the definition and the overrides omit it.
|
|
68
96
|
*
|
|
@@ -72,7 +72,7 @@ export type Testing<S extends TestingSchema = TestingSchema> = {
|
|
|
72
72
|
/**
|
|
73
73
|
* Runs the cross-tenant check.
|
|
74
74
|
*
|
|
75
|
-
* @returns Checked tenant tables and skipped global
|
|
75
|
+
* @returns Checked tenant tables and views, and skipped global objects
|
|
76
76
|
*/
|
|
77
77
|
isolation(): Promise<IsolationReport>;
|
|
78
78
|
/** Closes the client and the pool. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
testing2
|
|
3
|
-
} from "../../shared/
|
|
4
|
-
import"../../shared/
|
|
5
|
-
import"../../shared/
|
|
3
|
+
} from "../../shared/8x72s86k.js";
|
|
4
|
+
import"../../shared/9s0ypp87.js";
|
|
5
|
+
import"../../shared/qq3899ht.js";
|
|
6
6
|
export {
|
|
7
7
|
testing2 as testing
|
|
8
8
|
};
|