okmodel 0.5.1 → 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 +29 -14
- 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 +5 -4
- package/dist/dialects/pg/fn/index.js +2 -2
- package/dist/dialects/pg/index.d.ts +2 -1
- package/dist/dialects/pg/index.js +23 -17
- package/dist/dialects/pg/introspect.d.ts +7 -0
- package/dist/dialects/pg/model.d.ts +7 -2
- 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/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/conflict.d.ts +2 -2
- package/dist/runtime/drift.d.ts +9 -0
- package/dist/runtime/inflight.d.ts +14 -0
- package/dist/runtime/pg/bun.js +4 -3
- package/dist/runtime/pg/pg.js +3 -3
- package/dist/runtime/pg/pglite.js +4 -3
- package/dist/runtime/pg/postgresjs.js +3 -3
- package/dist/runtime/plan.d.ts +18 -10
- 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/{6qd27mx0.js → 1f0xa3cb.js} +5 -5
- package/dist/shared/1nw1f3vs.js +23 -0
- package/dist/shared/{96qcj21c.js → 1z04fa85.js} +19 -9
- package/dist/shared/{qm7cdtvr.js → 2cg3wnxc.js} +296 -60
- package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
- package/dist/shared/{gbtcc2d7.js → 2z7wv5tj.js} +10 -35
- package/dist/shared/{k8v5v5wj.js → 32cnm7kn.js} +6 -1
- package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
- package/dist/shared/{2pqx26h8.js → 3fqwmr09.js} +31 -22
- package/dist/shared/{zqrnhwsq.js → 3qnhc7w5.js} +16 -9
- package/dist/shared/{r2sp8hrc.js → 5d8t84hg.js} +12 -3
- package/dist/shared/{87jvvhnt.js → 5j36z8dn.js} +16 -5
- 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/{v36c5g6p.js → 8x72s86k.js} +400 -13
- package/dist/shared/{nxdp2ze3.js → 929h19xp.js} +107 -5
- package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
- package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
- package/dist/shared/{efmy2gcg.js → ag4c7516.js} +1 -1
- package/dist/shared/b3awxn7h.js +45 -0
- package/dist/shared/b4v57v3x.js +9 -0
- package/dist/shared/bdk4sty6.js +58 -0
- package/dist/shared/{51mvgj8w.js → c58secgj.js} +100 -7
- 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/{sycnx8gt.js → f0f1n281.js} +18 -8
- package/dist/shared/{f5cw5q5r.js → g90m9b0g.js} +2 -1
- package/dist/shared/{pf0d8f77.js → gwn6v667.js} +1 -1
- package/dist/shared/{8rsy3hxz.js → h15jx9bc.js} +1 -1
- package/dist/shared/{5enne4ma.js → hazqqecq.js} +17 -93
- package/dist/shared/{jwtxdj06.js → j3rkk835.js} +32 -9
- package/dist/shared/{hf22e0wt.js → j60w0dvr.js} +19 -18
- 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/{0t22wwh1.js → mb2ycs1p.js} +4 -2
- package/dist/shared/{cr74e96e.js → n076ydsc.js} +1 -1
- package/dist/shared/{cctw17hj.js → nx9hh281.js} +31 -28
- package/dist/shared/{gde73es6.js → p248q79y.js} +56 -55
- package/dist/shared/{janx09m5.js → p6bjzq9q.js} +11 -7
- package/dist/shared/{6rk2gen5.js → pjr6xrm5.js} +2 -2
- package/dist/shared/{wq9n6pwr.js → pz6rj8ay.js} +82 -9
- package/dist/shared/{caw8b9d8.js → q3j1tq7a.js} +35 -22
- package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
- package/dist/shared/{tm5ab8we.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/{ntneex2a.js → rpycarcr.js} +6 -6
- package/dist/shared/{aj2qxyhj.js → sbdme3vy.js} +14 -2
- package/dist/shared/{qv2ydck9.js → t3g0k88x.js} +638 -67
- package/dist/shared/{ga3sfy93.js → ttmg09vd.js} +535 -200
- package/dist/shared/{kfjp0gck.js → venqrjna.js} +8 -4
- package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
- package/dist/shared/{32zqc1dw.js → vmfkhszw.js} +31 -7
- package/dist/shared/{krxx77g0.js → wkme6tep.js} +10 -8
- package/dist/shared/{ps7a6h14.js → wt5rzv1h.js} +12 -8
- package/dist/shared/wxr8cwns.js +6 -0
- package/dist/shared/{6mnp99hx.js → wynzeggs.js} +16 -12
- package/dist/shared/x3jc53kg.js +220 -0
- package/dist/shared/{sdc7r2ww.js → xq0mvhts.js} +8 -8
- package/dist/shared/xqve9598.js +53 -0
- package/dist/shared/{47x8x9ze.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/{nkaxvt9q.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/{r8x5qhd7.js → zm0mafk4.js} +10 -4
- package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
- package/dist/tooling/cli.js +41 -15
- 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/config.d.ts +3 -2
- package/dist/tooling/migrate/files.d.ts +11 -1
- package/dist/tooling/migrate/index.js +14 -14
- package/dist/tooling/migrate/lint.d.ts +1 -1
- 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 +6 -3
- 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 +20 -3
- package/dist/tooling/migrate/text.d.ts +42 -0
- package/dist/tooling/node-version.d.ts +21 -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 +11 -5
- package/package.json +5 -2
- package/dist/shared/7zwwyh0v.js +0 -448
- package/dist/shared/q58y17hy.js +0 -21
|
@@ -1,26 +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 {
|
|
14
|
+
projectPath,
|
|
15
|
+
loadMigrations,
|
|
16
|
+
loadHeadSnapshot,
|
|
16
17
|
loadBuiltSchema,
|
|
17
18
|
loadConfig
|
|
18
|
-
} from "../../shared/
|
|
19
|
+
} from "../../shared/t3g0k88x.js";
|
|
19
20
|
import {
|
|
20
21
|
assertTargetPolicy,
|
|
21
22
|
selectTarget
|
|
22
|
-
} from "../../shared/
|
|
23
|
-
import"../../shared/
|
|
23
|
+
} from "../../shared/ttmg09vd.js";
|
|
24
|
+
import"../../shared/9s0ypp87.js";
|
|
24
25
|
import {
|
|
25
26
|
OkmError2
|
|
26
27
|
} from "../../shared/52szzakm.js";
|
|
@@ -30,12 +31,11 @@ function defineConfig(config) {
|
|
|
30
31
|
return config;
|
|
31
32
|
}
|
|
32
33
|
// src/tooling/migrate/provision.ts
|
|
33
|
-
import { join } from "node:path";
|
|
34
34
|
async function provision(target, cwd = process.cwd()) {
|
|
35
35
|
const config = await loadConfig(cwd);
|
|
36
36
|
const selected = selectTarget(config, target);
|
|
37
37
|
assertTargetPolicy(selected, "provision");
|
|
38
|
-
const directory =
|
|
38
|
+
const directory = projectPath(cwd, config.migrations ?? "migrations");
|
|
39
39
|
const migrations = loadMigrations(directory);
|
|
40
40
|
const head = loadHeadSnapshot(directory);
|
|
41
41
|
if (head === undefined) {
|
|
@@ -58,7 +58,7 @@ async function provision(target, cwd = process.cwd()) {
|
|
|
58
58
|
reference: readReference(built.tables, built.casing, head.catalog),
|
|
59
59
|
only: true
|
|
60
60
|
},
|
|
61
|
-
...config.roles !== undefined ? { migrationRole: config.roles.migration } : {},
|
|
61
|
+
...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
|
|
62
62
|
lintDirectory: directory
|
|
63
63
|
});
|
|
64
64
|
if (report.applied.length === 0)
|
|
@@ -8,7 +8,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 {
|
|
11
|
+
import type { MigrationPlan } from "./plan.js";
|
|
12
12
|
/** One finding. `place` is `step N`, `step N statement M`, or `table.column`. */
|
|
13
13
|
export type Finding = {
|
|
14
14
|
readonly severity: LintSeverity;
|
|
@@ -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;
|
|
@@ -128,9 +128,12 @@ 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,
|
|
132
|
-
*
|
|
133
|
-
* quote is still open.
|
|
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.
|
|
134
137
|
*
|
|
135
138
|
* @param text - SQL file text
|
|
136
139
|
* @returns The plan. The header class is kept when the file has no steps
|
|
@@ -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[];
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
* quote, and `''` is one quote inside it. `E'...'` still treats a backslash
|
|
7
7
|
* as an escape. A dollar quote runs until the same tag; a different tag
|
|
8
8
|
* inside it is text, so `$outer$ ... $inner$ ... $inner$ ... $outer$` stays
|
|
9
|
-
* one span.
|
|
10
|
-
*
|
|
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.
|
|
11
14
|
*/
|
|
12
15
|
/** Statements of `sql`, comments removed, with no trailing semicolon. */
|
|
13
16
|
export declare function sqlStatements(sql: string): readonly string[];
|
|
@@ -15,8 +18,22 @@ export declare function sqlStatements(sql: string): readonly string[];
|
|
|
15
18
|
* Whether `sql` still has an open quote or block comment.
|
|
16
19
|
*
|
|
17
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.
|
|
18
22
|
*
|
|
19
23
|
* @param sql - Step text accumulated so far
|
|
20
|
-
* @returns `true` when a later blank line is still inside that quote
|
|
24
|
+
* @returns `true` when a later blank line is still inside that quote or body
|
|
21
25
|
*/
|
|
22
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;
|
|
@@ -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
|
};
|
|
@@ -5,15 +5,21 @@
|
|
|
5
5
|
* writes as tenant A against that row's key. A returned row, or an update or
|
|
6
6
|
* delete that touches it, is a leak. The row is deleted afterwards. Global
|
|
7
7
|
* tables are skipped and listed.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* A table is tenant when its predicate says so (D199). Composite tenancy changes
|
|
9
|
+
* one key at a time. A path table is checked through a parent that belongs to
|
|
10
|
+
* tenant B.
|
|
11
|
+
* Tenant views and materialized views are read the same way. The rows are
|
|
12
|
+
* inserted into the underlying tables, never into the view. A global view is
|
|
13
|
+
* skipped and listed with its `global("reason")`.
|
|
10
14
|
*/
|
|
11
15
|
import { type FactoryHost } from "./factories.js";
|
|
12
|
-
/** What {@link isolation} checked, and the global
|
|
16
|
+
/** What {@link isolation} checked, and the global objects it skipped. */
|
|
13
17
|
export type IsolationReport = {
|
|
14
18
|
/** Tenant tables whose queries stayed inside tenant A. */
|
|
15
19
|
readonly checked: readonly string[];
|
|
16
|
-
/**
|
|
20
|
+
/** Tenant views and materialized views whose reads stayed inside tenant A. */
|
|
21
|
+
readonly views: readonly string[];
|
|
22
|
+
/** Global tables and views, with the reason from `global("reason")`. */
|
|
17
23
|
readonly skipped: readonly {
|
|
18
24
|
readonly table: string;
|
|
19
25
|
readonly reason: string;
|
|
@@ -26,6 +32,6 @@ export type IsolationReport = {
|
|
|
26
32
|
* already inserted is not reused as tenant B's row.
|
|
27
33
|
*
|
|
28
34
|
* @param host - The open harness
|
|
29
|
-
* @returns The tables that were checked and the global
|
|
35
|
+
* @returns The tables and views that were checked, and the global objects that were not
|
|
30
36
|
*/
|
|
31
37
|
export declare function isolation(host: FactoryHost): Promise<IsolationReport>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okmodel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "okmodel is a catalog-first TypeScript ORM, PostgreSQL first.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"migrations",
|
|
@@ -142,6 +142,9 @@
|
|
|
142
142
|
"bench": "bun ./packages/bench/src/run.ts",
|
|
143
143
|
"catalog-bench": "bun ./scripts/catalog-bench.ts",
|
|
144
144
|
"attest": "ATTEST_shouldFormat=0 ATTEST_updateSnapshots=0 bun ./packages/attest/src/measure.bench.ts",
|
|
145
|
+
"site:dev": "bun --cwd site dev",
|
|
146
|
+
"site:build": "bun --cwd site build",
|
|
147
|
+
"site:check": "bun --cwd site check",
|
|
145
148
|
"publish:dry": "bun publish --dry-run",
|
|
146
149
|
"prepublishOnly": "bun run build"
|
|
147
150
|
},
|
|
@@ -178,6 +181,6 @@
|
|
|
178
181
|
}
|
|
179
182
|
},
|
|
180
183
|
"engines": {
|
|
181
|
-
"node": ">=22"
|
|
184
|
+
"node": ">=22.18"
|
|
182
185
|
}
|
|
183
186
|
}
|