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
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cross-tenant isolation check.
|
|
3
3
|
*
|
|
4
|
-
* For every tenant table, insert one row as tenant B and run the
|
|
5
|
-
*
|
|
6
|
-
* delete that touches
|
|
7
|
-
*
|
|
8
|
-
* predicate says so (D199).
|
|
4
|
+
* For every tenant table, insert one row as tenant B and run the reads and
|
|
5
|
+
* writes as tenant A against that row's key. A returned row, or an update or
|
|
6
|
+
* delete that touches it, is a leak. The row is deleted afterwards. Global
|
|
7
|
+
* tables are skipped and listed.
|
|
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")`.
|
|
9
14
|
*/
|
|
10
15
|
import { type FactoryHost } from "./factories.js";
|
|
11
|
-
/** What {@link isolation} checked, and the global
|
|
16
|
+
/** What {@link isolation} checked, and the global objects it skipped. */
|
|
12
17
|
export type IsolationReport = {
|
|
13
18
|
/** Tenant tables whose queries stayed inside tenant A. */
|
|
14
19
|
readonly checked: readonly string[];
|
|
15
|
-
/**
|
|
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")`. */
|
|
16
23
|
readonly skipped: readonly {
|
|
17
24
|
readonly table: string;
|
|
18
25
|
readonly reason: string;
|
|
@@ -25,6 +32,6 @@ export type IsolationReport = {
|
|
|
25
32
|
* already inserted is not reused as tenant B's row.
|
|
26
33
|
*
|
|
27
34
|
* @param host - The open harness
|
|
28
|
-
* @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
|
|
29
36
|
*/
|
|
30
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",
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
"okm": "./dist/okm.js",
|
|
24
24
|
"okmodel": "./dist/okmodel.js"
|
|
25
25
|
},
|
|
26
|
-
"workspaces": [
|
|
27
|
-
"packages/*"
|
|
28
|
-
],
|
|
29
26
|
"files": [
|
|
30
27
|
"dist",
|
|
31
28
|
"README.md",
|
|
@@ -112,7 +109,7 @@
|
|
|
112
109
|
}
|
|
113
110
|
},
|
|
114
111
|
"scripts": {
|
|
115
|
-
"build": "rm -rf dist && bun build ./src/contracts/index.ts ./src/contracts/internal.ts ./src/dialects/pg/index.ts ./src/runtime/pg/postgresjs.ts ./src/runtime/pg/pglite.ts ./src/runtime/pg/pg.ts ./src/runtime/pg/bun.ts ./src/runtime/ids/index.ts ./src/runtime/safety/index.ts ./src/runtime/traits/index.ts ./src/runtime/tenancy/index.ts ./src/runtime/validate/index.ts ./src/dialects/pg/ext/index.ts ./src/dialects/pg/fn/index.ts ./src/dialects/pg/view/index.ts ./src/dialects/pg/ext/citext.ts ./src/dialects/pg/ext/pg-trgm.ts ./src/tooling/migrate/index.ts ./src/tooling/testing/index.ts ./src/tooling/cli.ts --outdir ./dist --root ./src --format esm --target node --splitting --chunk-naming=shared/[hash].js --external postgres --external @electric-sql/pglite --external pg --external bun && bunx tsc -p tsconfig.build.json && bun ./scripts/write-bin-wrappers.ts",
|
|
112
|
+
"build": "rm -rf dist && bun build ./src/contracts/index.ts ./src/contracts/internal.ts ./src/dialects/pg/index.ts ./src/runtime/pg/postgresjs.ts ./src/runtime/pg/pglite.ts ./src/runtime/pg/pg.ts ./src/runtime/pg/bun.ts ./src/runtime/ids/index.ts ./src/runtime/safety/index.ts ./src/runtime/traits/index.ts ./src/runtime/tenancy/index.ts ./src/runtime/validate/index.ts ./src/dialects/pg/ext/index.ts ./src/dialects/pg/fn/index.ts ./src/dialects/pg/view/index.ts ./src/dialects/pg/ext/citext.ts ./src/dialects/pg/ext/pg-trgm.ts ./src/tooling/migrate/index.ts ./src/tooling/testing/index.ts ./src/tooling/cli.ts --outdir ./dist --root ./src --format esm --target node --splitting --chunk-naming=shared/[hash].js --external postgres --external @electric-sql/pglite --external pg --external bun && bunx tsc -p tsconfig.build.json && rm -f ./dist/dialects/pg/temporal.local.d.ts && bun ./scripts/write-bin-wrappers.ts",
|
|
116
113
|
"driver-bench": "bun ./scripts/driver-bench.ts",
|
|
117
114
|
"typecheck": "bunx tsc --noEmit -p tsconfig.json",
|
|
118
115
|
"editor-check": "bun ./scripts/editor-check.ts",
|
|
@@ -129,6 +126,8 @@
|
|
|
129
126
|
"compiler-api": "bun ./scripts/compiler-api.ts",
|
|
130
127
|
"publint": "publint run . --strict --pack bun",
|
|
131
128
|
"attw": "bun ./scripts/attw.ts",
|
|
129
|
+
"prepack": "bun ./scripts/strip-workspaces.ts strip",
|
|
130
|
+
"postpack": "bun ./scripts/strip-workspaces.ts restore",
|
|
132
131
|
"size": "bun ./scripts/size.ts",
|
|
133
132
|
"bundle-purity": "bun ./scripts/bundle-purity.ts",
|
|
134
133
|
"test": "bun test",
|
|
@@ -143,6 +142,9 @@
|
|
|
143
142
|
"bench": "bun ./packages/bench/src/run.ts",
|
|
144
143
|
"catalog-bench": "bun ./scripts/catalog-bench.ts",
|
|
145
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",
|
|
146
148
|
"publish:dry": "bun publish --dry-run",
|
|
147
149
|
"prepublishOnly": "bun run build"
|
|
148
150
|
},
|
|
@@ -157,6 +159,8 @@
|
|
|
157
159
|
"oxlint": "^1.86.0",
|
|
158
160
|
"oxlint-tsgolint": "^7.0.2003",
|
|
159
161
|
"publint": "^0.3.24",
|
|
162
|
+
"temporal-polyfill": "^1.0.5",
|
|
163
|
+
"temporal-spec": "^1.0.1",
|
|
160
164
|
"typescript": "7",
|
|
161
165
|
"typescript-editor": "npm:typescript@6.0.3"
|
|
162
166
|
},
|
|
@@ -177,6 +181,6 @@
|
|
|
177
181
|
}
|
|
178
182
|
},
|
|
179
183
|
"engines": {
|
|
180
|
-
"node": ">=22"
|
|
184
|
+
"node": ">=22.18"
|
|
181
185
|
}
|
|
182
186
|
}
|
package/dist/shared/0v1g424p.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
OkmError2
|
|
3
|
-
} from "./52szzakm.js";
|
|
4
|
-
|
|
5
|
-
// src/dialects/pg/filter-parse.ts
|
|
6
|
-
function parseQuery(allow, sort, query) {
|
|
7
|
-
if (typeof query !== "object" || query === null || Array.isArray(query)) {
|
|
8
|
-
throw new OkmError2("OKM1121", "filters parse expects an object of fields.");
|
|
9
|
-
}
|
|
10
|
-
const where = {};
|
|
11
|
-
let orderBy;
|
|
12
|
-
for (const key of Object.keys(query)) {
|
|
13
|
-
const value = query[key];
|
|
14
|
-
if (key === "sort" && typeof value === "string" && sort.includes(value)) {
|
|
15
|
-
orderBy = { [value]: "asc" };
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (allow[key]?.includes("eq") === true && (value === null || typeof value !== "object")) {
|
|
19
|
-
where[key] = value;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return orderBy === undefined ? { where } : { where, orderBy };
|
|
23
|
-
}
|
|
24
|
-
export {
|
|
25
|
-
parseQuery
|
|
26
|
-
};
|
package/dist/shared/r73pt594.js
DELETED
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseCatalog2
|
|
3
|
-
} from "./8bwbwy8h.js";
|
|
4
|
-
import"./wqem4jed.js";
|
|
5
|
-
import"./n442h54c.js";
|
|
6
|
-
import {
|
|
7
|
-
applyTarget2,
|
|
8
|
-
backfillTiming2
|
|
9
|
-
} from "./xhh1srw3.js";
|
|
10
|
-
import {
|
|
11
|
-
sealViews,
|
|
12
|
-
catalogQuery2,
|
|
13
|
-
managedRoleOptions2
|
|
14
|
-
} from "./wxn4b7pp.js";
|
|
15
|
-
import {
|
|
16
|
-
loadMigrations,
|
|
17
|
-
catalogsEqual,
|
|
18
|
-
snapshotDifference
|
|
19
|
-
} from "./kdkxh3kb.js";
|
|
20
|
-
import"./szm0cksa.js";
|
|
21
|
-
import {
|
|
22
|
-
introspectSchema
|
|
23
|
-
} from "./fancm5pt.js";
|
|
24
|
-
import {
|
|
25
|
-
lintMigrationDirectory,
|
|
26
|
-
hasError,
|
|
27
|
-
lintRefusal,
|
|
28
|
-
selectTarget,
|
|
29
|
-
openProject
|
|
30
|
-
} from "./xdpj5kkm.js";
|
|
31
|
-
import {
|
|
32
|
-
canonicalTypeName,
|
|
33
|
-
classOf,
|
|
34
|
-
strictestClass,
|
|
35
|
-
planMigration,
|
|
36
|
-
parsePlan
|
|
37
|
-
} from "./ayz64vzj.js";
|
|
38
|
-
import {
|
|
39
|
-
quoteIdent
|
|
40
|
-
} from "./8xsbagad.js";
|
|
41
|
-
import {
|
|
42
|
-
catalog,
|
|
43
|
-
isDomain
|
|
44
|
-
} from "./g7ez901n.js";
|
|
45
|
-
import {
|
|
46
|
-
identityKey2
|
|
47
|
-
} from "./t4tbsy9h.js";
|
|
48
|
-
import {
|
|
49
|
-
OkmError2
|
|
50
|
-
} from "./52szzakm.js";
|
|
51
|
-
|
|
52
|
-
// src/tooling/migrate/check.ts
|
|
53
|
-
import { readFileSync } from "node:fs";
|
|
54
|
-
import { join } from "node:path";
|
|
55
|
-
import postgres from "postgres";
|
|
56
|
-
var CHECK_SCHEMA_PREFIX = "okm_check_";
|
|
57
|
-
var HISTORY_CODE = "OKM1547";
|
|
58
|
-
var PREVIOUS_CODE = "OKM1548";
|
|
59
|
-
var STALE_CODE = "OKM1549";
|
|
60
|
-
var HISTORY_FIX = "Regenerate the migration so the stored catalog matches the schema the SQL applies.";
|
|
61
|
-
var FORK_FIX = "Generate the later migration from the previous migration's catalog, not from an older parent.";
|
|
62
|
-
var PREVIOUS_FIX = "Keep every table, column, constraint, and type the previous catalog relies on, or classify the migration as contract.";
|
|
63
|
-
var STALE_FIX = "Run okm generate.";
|
|
64
|
-
async function checkMigrations(cwd, flags, options) {
|
|
65
|
-
const opened = await openProject(cwd);
|
|
66
|
-
const target = selectTarget(opened.config, flags.target);
|
|
67
|
-
if (target.protected) {
|
|
68
|
-
throw new OkmError2("OKM1850", `okm migrate check writes a scratch schema and refuses protected target ${target.name}. Point it at a throwaway Postgres.`, {
|
|
69
|
-
kind: "forbidden",
|
|
70
|
-
fix: {
|
|
71
|
-
summary: "Point okm migrate check at a throwaway Postgres. --allow-protected does not apply."
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const directory = join(cwd, opened.config.migrations ?? "migrations");
|
|
76
|
-
const history = readHistory(directory);
|
|
77
|
-
if (options?.provision === true) {
|
|
78
|
-
const equivalence = await proveSnapshot(opened.config, target.url, target.name, flags, history);
|
|
79
|
-
if (equivalence !== undefined)
|
|
80
|
-
throwFailure([equivalence]);
|
|
81
|
-
}
|
|
82
|
-
const previous = previousFailures(history);
|
|
83
|
-
const stale = staleFailure(history, opened.built.catalog);
|
|
84
|
-
const lint = lintFailure(directory, history, opened.config.lintFrom);
|
|
85
|
-
const replayed = await replayHistory(opened.config, target.url, target.name, flags, history);
|
|
86
|
-
const failures = [...replayed, ...previous, ...stale, ...lint];
|
|
87
|
-
if (failures.length > 0)
|
|
88
|
-
throwFailure(failures);
|
|
89
|
-
return `ok ${String(history.length)} migrations
|
|
90
|
-
`;
|
|
91
|
-
}
|
|
92
|
-
function previousCatalogGaps(before, after) {
|
|
93
|
-
const afterBy = new Map(after.objects.map((object) => [identityKey2(object.identity), object]));
|
|
94
|
-
const gaps = [];
|
|
95
|
-
for (const object of before.objects) {
|
|
96
|
-
if (!checkedKind(object.kind))
|
|
97
|
-
continue;
|
|
98
|
-
const next = afterBy.get(identityKey2(object.identity));
|
|
99
|
-
if (next === undefined || next.kind !== object.kind) {
|
|
100
|
-
gaps.push(`${objectLabel(object)} is missing`);
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (object.kind === "column" && next.kind === "column") {
|
|
104
|
-
gaps.push(...columnGaps(object, next));
|
|
105
|
-
}
|
|
106
|
-
if (object.kind === "type" && next.kind === "type" && !sameType(object.definition, next.definition)) {
|
|
107
|
-
gaps.push(`${objectLabel(object)} changed`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return gaps;
|
|
111
|
-
}
|
|
112
|
-
function recomputedMigrationClass(plan, before, after) {
|
|
113
|
-
if (plan.steps.length === 0)
|
|
114
|
-
return "expand";
|
|
115
|
-
if (plan.steps.some((step) => step.kind === undefined)) {
|
|
116
|
-
return planMigration({ before, after, name: plan.name }).class;
|
|
117
|
-
}
|
|
118
|
-
return strictestClass(plan.steps.map((step) => classOf(step.kind ?? "raw-sql")));
|
|
119
|
-
}
|
|
120
|
-
function migrationAllowsPreviousGaps(plan, before, after) {
|
|
121
|
-
return recomputedMigrationClass(plan, before, after) === "contract" && plan.class !== "expand";
|
|
122
|
-
}
|
|
123
|
-
function previousFailures(history) {
|
|
124
|
-
const failures = [];
|
|
125
|
-
let before = catalog([]);
|
|
126
|
-
for (const migration of history) {
|
|
127
|
-
const gaps = previousCatalogGaps(before, migration.catalog);
|
|
128
|
-
if (gaps.length > 0 && !migrationAllowsPreviousGaps(migration.plan, before, migration.catalog)) {
|
|
129
|
-
const stated = migration.plan.class === "expand" ? "expand" : recomputedMigrationClass(migration.plan, before, migration.catalog);
|
|
130
|
-
failures.push({
|
|
131
|
-
code: PREVIOUS_CODE,
|
|
132
|
-
message: `${migration.id}: previous catalog is not satisfied (${gaps.join("; ")}). The migration is classified ${stated}.`,
|
|
133
|
-
fix: PREVIOUS_FIX
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
before = migration.catalog;
|
|
137
|
-
}
|
|
138
|
-
return failures;
|
|
139
|
-
}
|
|
140
|
-
function staleFailure(history, head) {
|
|
141
|
-
const last = history.at(-1)?.catalog ?? catalog([]);
|
|
142
|
-
if (catalogsEqual(last, head))
|
|
143
|
-
return [];
|
|
144
|
-
return [
|
|
145
|
-
{
|
|
146
|
-
code: STALE_CODE,
|
|
147
|
-
message: "The last migration's catalog does not match the schema.",
|
|
148
|
-
fix: STALE_FIX
|
|
149
|
-
}
|
|
150
|
-
];
|
|
151
|
-
}
|
|
152
|
-
function lintFailure(directory, history, lintFrom) {
|
|
153
|
-
const findings = lintMigrationDirectory(directory, lintIds(history, lintFrom));
|
|
154
|
-
if (!hasError(findings))
|
|
155
|
-
return [];
|
|
156
|
-
const refusal = lintRefusal(findings);
|
|
157
|
-
return [{ code: refusal.code, message: refusal.message, fix: refusal.fix.summary }];
|
|
158
|
-
}
|
|
159
|
-
function lintIds(history, lintFrom) {
|
|
160
|
-
if (lintFrom === undefined)
|
|
161
|
-
return;
|
|
162
|
-
const ids = history.map((migration) => migration.id);
|
|
163
|
-
const start = ids.indexOf(lintFrom);
|
|
164
|
-
if (start < 0) {
|
|
165
|
-
throw new OkmError2("invalid", `lintFrom ${lintFrom} is not a migration id.`, {
|
|
166
|
-
fix: {
|
|
167
|
-
summary: "Set lintFrom to a migration id. Files before that id are the adoption baseline and are not linted."
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
return new Set(ids.slice(start));
|
|
172
|
-
}
|
|
173
|
-
async function proveSnapshot(config, url, target, flags, history) {
|
|
174
|
-
const head = history.at(-1);
|
|
175
|
-
if (head === undefined)
|
|
176
|
-
return;
|
|
177
|
-
const sql = postgres(url, {
|
|
178
|
-
max: 1,
|
|
179
|
-
connect_timeout: 5,
|
|
180
|
-
idle_timeout: 1,
|
|
181
|
-
onnotice: () => {}
|
|
182
|
-
});
|
|
183
|
-
const provided = `${CHECK_SCHEMA_PREFIX}p${crypto.randomUUID().replaceAll("-", "")}`;
|
|
184
|
-
const replayed = `${CHECK_SCHEMA_PREFIX}r${crypto.randomUUID().replaceAll("-", "")}`;
|
|
185
|
-
const timing = {
|
|
186
|
-
...flags.lockTimeoutMs !== undefined ? { lockTimeoutMs: flags.lockTimeoutMs } : config.timeouts?.lock !== undefined ? { lockTimeoutMs: config.timeouts.lock } : {},
|
|
187
|
-
...flags.statementTimeoutMs !== undefined ? { statementTimeoutMs: flags.statementTimeoutMs } : config.timeouts?.statement !== undefined ? { statementTimeoutMs: config.timeouts.statement } : {},
|
|
188
|
-
...backfillTiming2(config.backfill)
|
|
189
|
-
};
|
|
190
|
-
try {
|
|
191
|
-
await sql.unsafe(`create schema ${quoteIdent(provided)}`);
|
|
192
|
-
await sql.unsafe(`create schema ${quoteIdent(replayed)}`);
|
|
193
|
-
const role = config.roles === undefined ? {} : { migrationRole: config.roles.migration, schema: provided };
|
|
194
|
-
await applyTarget2({
|
|
195
|
-
url,
|
|
196
|
-
target,
|
|
197
|
-
protected: false,
|
|
198
|
-
allowProtected: false,
|
|
199
|
-
allowPooler: flags.allowPooler || config.allowPooler === true,
|
|
200
|
-
searchPath: provided,
|
|
201
|
-
...timing,
|
|
202
|
-
migrations: history.map((migration) => ({
|
|
203
|
-
id: migration.id,
|
|
204
|
-
catalogHash: migration.catalogHash,
|
|
205
|
-
steps: migration.steps
|
|
206
|
-
})),
|
|
207
|
-
snapshot: {
|
|
208
|
-
catalog: head.catalog,
|
|
209
|
-
migrationId: head.id,
|
|
210
|
-
catalogHash: head.catalogHash,
|
|
211
|
-
reference: [],
|
|
212
|
-
only: true
|
|
213
|
-
},
|
|
214
|
-
...role
|
|
215
|
-
});
|
|
216
|
-
await applyTarget2({
|
|
217
|
-
url,
|
|
218
|
-
target,
|
|
219
|
-
protected: false,
|
|
220
|
-
allowProtected: false,
|
|
221
|
-
allowPooler: flags.allowPooler || config.allowPooler === true,
|
|
222
|
-
searchPath: replayed,
|
|
223
|
-
...timing,
|
|
224
|
-
migrations: history.map((migration) => ({
|
|
225
|
-
id: migration.id,
|
|
226
|
-
catalogHash: migration.catalogHash,
|
|
227
|
-
steps: retargetSteps(migration.steps, replayed)
|
|
228
|
-
})),
|
|
229
|
-
...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: replayed } : {}
|
|
230
|
-
});
|
|
231
|
-
const liveProvided = await introspectScratch(sql, provided, config);
|
|
232
|
-
const liveReplayed = await introspectScratch(sql, replayed, config);
|
|
233
|
-
const difference = snapshotDifference(liveProvided, liveReplayed);
|
|
234
|
-
if (difference === undefined)
|
|
235
|
-
return;
|
|
236
|
-
return {
|
|
237
|
-
code: "OKM1521",
|
|
238
|
-
message: `Provisioning from the snapshot does not match the replayed history.
|
|
239
|
-
${difference}`,
|
|
240
|
-
fix: "Regenerate the snapshot from the history. okm migrate check --provision is the command that reports this."
|
|
241
|
-
};
|
|
242
|
-
} finally {
|
|
243
|
-
await sql.unsafe(`drop schema if exists ${quoteIdent(provided)} cascade`).catch(() => {
|
|
244
|
-
return;
|
|
245
|
-
});
|
|
246
|
-
await sql.unsafe(`drop schema if exists ${quoteIdent(replayed)} cascade`).catch(() => {
|
|
247
|
-
return;
|
|
248
|
-
});
|
|
249
|
-
await sql.end({ timeout: 5 });
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
async function replayHistory(config, url, target, flags, history) {
|
|
253
|
-
if (history.length === 0)
|
|
254
|
-
return [];
|
|
255
|
-
const sql = postgres(url, {
|
|
256
|
-
max: 1,
|
|
257
|
-
connect_timeout: 5,
|
|
258
|
-
idle_timeout: 1,
|
|
259
|
-
onnotice: () => {}
|
|
260
|
-
});
|
|
261
|
-
const scratch = `${CHECK_SCHEMA_PREFIX}${crypto.randomUUID().replaceAll("-", "")}`;
|
|
262
|
-
const failures = [];
|
|
263
|
-
try {
|
|
264
|
-
await sql.unsafe(`create schema ${quoteIdent(scratch)}`);
|
|
265
|
-
const runner = catalogQuery2(sql);
|
|
266
|
-
for (let index = 0;index < history.length; index += 1) {
|
|
267
|
-
const migration = history[index];
|
|
268
|
-
if (migration === undefined)
|
|
269
|
-
continue;
|
|
270
|
-
await applyTarget2({
|
|
271
|
-
url,
|
|
272
|
-
target,
|
|
273
|
-
protected: false,
|
|
274
|
-
allowProtected: false,
|
|
275
|
-
allowPooler: flags.allowPooler || config.allowPooler === true,
|
|
276
|
-
searchPath: scratch,
|
|
277
|
-
...flags.lockTimeoutMs !== undefined ? { lockTimeoutMs: flags.lockTimeoutMs } : config.timeouts?.lock !== undefined ? { lockTimeoutMs: config.timeouts.lock } : {},
|
|
278
|
-
...flags.statementTimeoutMs !== undefined ? { statementTimeoutMs: flags.statementTimeoutMs } : config.timeouts?.statement !== undefined ? { statementTimeoutMs: config.timeouts.statement } : {},
|
|
279
|
-
...backfillTiming2(config.backfill),
|
|
280
|
-
migrations: [
|
|
281
|
-
{
|
|
282
|
-
id: migration.id,
|
|
283
|
-
catalogHash: migration.catalogHash,
|
|
284
|
-
steps: retargetSteps(migration.steps, scratch)
|
|
285
|
-
}
|
|
286
|
-
],
|
|
287
|
-
...config.roles !== undefined ? { migrationRole: config.roles.migration, schema: scratch } : {}
|
|
288
|
-
});
|
|
289
|
-
const live = await introspectScratch(sql, scratch, config);
|
|
290
|
-
const sealed = await sealViews(runner, migration.catalog);
|
|
291
|
-
const back = planMigration({ before: live, after: sealed, name: migration.id });
|
|
292
|
-
if (back.steps.length === 0)
|
|
293
|
-
continue;
|
|
294
|
-
failures.push(historyFailure(history, index, back));
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
} finally {
|
|
298
|
-
await sql.unsafe(`drop schema if exists ${quoteIdent(scratch)} cascade`).catch(() => {
|
|
299
|
-
return;
|
|
300
|
-
});
|
|
301
|
-
await sql.end({ timeout: 5 });
|
|
302
|
-
}
|
|
303
|
-
return failures;
|
|
304
|
-
}
|
|
305
|
-
async function introspectScratch(sql, schema, config) {
|
|
306
|
-
await sql.unsafe(`set search_path to ${quoteIdent(schema)}`);
|
|
307
|
-
return introspectSchema(catalogQuery2(sql), schema, "public", managedRoleOptions2(config.roles));
|
|
308
|
-
}
|
|
309
|
-
function historyFailure(history, index, back) {
|
|
310
|
-
const migration = history[index];
|
|
311
|
-
const id = migration?.id ?? "migration";
|
|
312
|
-
const fork = migration === undefined ? undefined : forkMessage(history, index);
|
|
313
|
-
if (fork !== undefined)
|
|
314
|
-
return { code: HISTORY_CODE, message: fork, fix: FORK_FIX };
|
|
315
|
-
const steps = back.steps.map((step) => step.sql).join(`
|
|
316
|
-
`);
|
|
317
|
-
return {
|
|
318
|
-
code: HISTORY_CODE,
|
|
319
|
-
message: `${id}: history differs from the stored catalog
|
|
320
|
-
${steps}`,
|
|
321
|
-
fix: HISTORY_FIX
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
function forkMessage(history, index) {
|
|
325
|
-
const current = history[index];
|
|
326
|
-
const previous = history[index - 1];
|
|
327
|
-
if (current === undefined || previous === undefined)
|
|
328
|
-
return;
|
|
329
|
-
if (samePlan(previous.catalog, current.catalog, current.plan))
|
|
330
|
-
return;
|
|
331
|
-
for (let parentIndex = 0;parentIndex < index; parentIndex += 1) {
|
|
332
|
-
const parent = parentIndex === 0 ? catalog([]) : history[parentIndex - 1]?.catalog;
|
|
333
|
-
if (parent === undefined)
|
|
334
|
-
continue;
|
|
335
|
-
if (!samePlan(parent, current.catalog, current.plan))
|
|
336
|
-
continue;
|
|
337
|
-
return `migration ${current.id} was generated from a different parent than ${previous.id}`;
|
|
338
|
-
}
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
function samePlan(before, after, file) {
|
|
342
|
-
try {
|
|
343
|
-
return sameSql(planMigration({ before, after, name: "parent" }), file);
|
|
344
|
-
} catch (error) {
|
|
345
|
-
if (error instanceof OkmError2 && error.code === "OKM1530")
|
|
346
|
-
return false;
|
|
347
|
-
throw error;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
function sameSql(planned, file) {
|
|
351
|
-
if (planned.steps.length !== file.steps.length)
|
|
352
|
-
return false;
|
|
353
|
-
return planned.steps.every((step, index) => step.sql === file.steps[index]?.sql);
|
|
354
|
-
}
|
|
355
|
-
function retargetSteps(steps, schema) {
|
|
356
|
-
const from = `${quoteIdent("public")}.`;
|
|
357
|
-
const to = `${quoteIdent(schema)}.`;
|
|
358
|
-
const fromDefaults = `in schema ${quoteIdent("public")} `;
|
|
359
|
-
const toDefaults = `in schema ${quoteIdent(schema)} `;
|
|
360
|
-
return steps.map((step) => ({
|
|
361
|
-
...step,
|
|
362
|
-
sql: step.sql.replaceAll(from, to).replaceAll(fromDefaults, toDefaults),
|
|
363
|
-
...step.backfill === undefined ? {} : { backfill: { ...step.backfill, table: step.backfill.table.replaceAll(from, to) } }
|
|
364
|
-
}));
|
|
365
|
-
}
|
|
366
|
-
function readHistory(directory) {
|
|
367
|
-
return loadMigrations(directory).map((migration) => ({
|
|
368
|
-
id: migration.id,
|
|
369
|
-
catalogHash: migration.catalogHash,
|
|
370
|
-
steps: migration.steps,
|
|
371
|
-
plan: parsePlan(readFileSync(join(directory, `${migration.id}.sql`), "utf8")),
|
|
372
|
-
catalog: parseCatalog2(readFileSync(join(directory, `${migration.id}.catalog.json`), "utf8"))
|
|
373
|
-
}));
|
|
374
|
-
}
|
|
375
|
-
function throwFailure(failures) {
|
|
376
|
-
const first = failures[0];
|
|
377
|
-
if (first === undefined)
|
|
378
|
-
throw new Error("okm migrate check has no failure to report.");
|
|
379
|
-
const message = failures.length === 1 ? first.message : failures.map((failure) => `${failure.code} ${failure.message}`).join(`
|
|
380
|
-
`);
|
|
381
|
-
throw new OkmError2(first.code, message, { fix: { summary: first.fix } });
|
|
382
|
-
}
|
|
383
|
-
function checkedKind(kind) {
|
|
384
|
-
return kind === "table" || kind === "column" || kind === "constraint" || kind === "type";
|
|
385
|
-
}
|
|
386
|
-
function columnGaps(before, after) {
|
|
387
|
-
const gaps = [];
|
|
388
|
-
const place = objectLabel(before);
|
|
389
|
-
if (canonicalTypeName(before.definition.dataType) !== canonicalTypeName(after.definition.dataType)) {
|
|
390
|
-
gaps.push(`${place} changed type from ${before.definition.dataType} to ${after.definition.dataType}`);
|
|
391
|
-
}
|
|
392
|
-
if (before.definition.nullable && !after.definition.nullable && !columnHasDefault(after)) {
|
|
393
|
-
gaps.push(`${place} nullability tightened without a default`);
|
|
394
|
-
}
|
|
395
|
-
return gaps;
|
|
396
|
-
}
|
|
397
|
-
function columnHasDefault(column) {
|
|
398
|
-
return column.definition.defaultExpression !== undefined || column.definition.identity !== undefined || column.definition.generated !== undefined;
|
|
399
|
-
}
|
|
400
|
-
function sameType(before, after) {
|
|
401
|
-
const beforeDomain = isDomain(before);
|
|
402
|
-
const afterDomain = isDomain(after);
|
|
403
|
-
if (beforeDomain !== afterDomain)
|
|
404
|
-
return false;
|
|
405
|
-
if (beforeDomain && afterDomain) {
|
|
406
|
-
return canonicalTypeName(before.base) === canonicalTypeName(after.base) && before.check === after.check;
|
|
407
|
-
}
|
|
408
|
-
if (!beforeDomain && !afterDomain) {
|
|
409
|
-
if (before.labels.length !== after.labels.length)
|
|
410
|
-
return false;
|
|
411
|
-
return before.labels.every((label, index) => label === after.labels[index]);
|
|
412
|
-
}
|
|
413
|
-
return false;
|
|
414
|
-
}
|
|
415
|
-
function objectLabel(object) {
|
|
416
|
-
if (object.kind === "column" || object.kind === "constraint") {
|
|
417
|
-
return `${object.kind} ${object.identity.parent.name}.${object.identity.name}`;
|
|
418
|
-
}
|
|
419
|
-
if (object.kind === "table" || object.kind === "type") {
|
|
420
|
-
return `${object.kind} ${object.identity.name}`;
|
|
421
|
-
}
|
|
422
|
-
return object.kind;
|
|
423
|
-
}
|
|
424
|
-
export {
|
|
425
|
-
checkMigrations
|
|
426
|
-
};
|
package/dist/shared/rasvvzgz.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
applyTarget2,
|
|
3
|
-
applyProject2,
|
|
4
|
-
pushProject2,
|
|
5
|
-
backfillTiming2
|
|
6
|
-
} from "./xhh1srw3.js";
|
|
7
|
-
import"./kdkxh3kb.js";
|
|
8
|
-
import"./szm0cksa.js";
|
|
9
|
-
import"./fancm5pt.js";
|
|
10
|
-
import"./xdpj5kkm.js";
|
|
11
|
-
import"./ayz64vzj.js";
|
|
12
|
-
import"./n442h54c.js";
|
|
13
|
-
import"./8xsbagad.js";
|
|
14
|
-
import"./8bwbwy8h.js";
|
|
15
|
-
import"./wqem4jed.js";
|
|
16
|
-
export {
|
|
17
|
-
applyProject2 as applyProject,
|
|
18
|
-
applyTarget2 as applyTarget,
|
|
19
|
-
backfillTiming2 as backfillTiming,
|
|
20
|
-
pushProject2 as pushProject
|
|
21
|
-
};
|