okmodel 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -12
- package/dist/contracts/catalog/document.d.ts +20 -0
- package/dist/contracts/driver.d.ts +7 -0
- package/dist/dialects/pg/filter-parse.d.ts +8 -2
- package/dist/dialects/pg/fn/index.js +8 -0
- package/dist/dialects/pg/index.js +8 -2
- package/dist/dialects/pg/introspect.d.ts +6 -0
- package/dist/dialects/pg/ops/or.d.ts +1 -1
- package/dist/dialects/pg/role/sql.d.ts +10 -0
- package/dist/dialects/pg/schema.d.ts +15 -6
- package/dist/dialects/pg/temporal.d.ts +5 -76
- package/dist/dialects/pg/view/index.d.ts +20 -6
- package/dist/dialects/pg/view/index.js +1 -1
- package/dist/dialects/pg/view/scratch.d.ts +2 -2
- package/dist/runtime/client.d.ts +6 -1
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/pg/bun.d.ts +12 -7
- package/dist/runtime/pg/bun.js +7 -4
- package/dist/runtime/pg/pg.d.ts +12 -6
- package/dist/runtime/pg/pg.js +7 -4
- package/dist/runtime/pg/pglite.d.ts +11 -3
- package/dist/runtime/pg/pglite.js +7 -4
- package/dist/runtime/pg/postgresjs.d.ts +12 -6
- package/dist/runtime/pg/postgresjs.js +7 -4
- package/dist/runtime/plan.d.ts +16 -1
- package/dist/runtime/tenancy/index.js +1 -1
- package/dist/runtime/topology.d.ts +191 -0
- package/dist/runtime/traits/index.js +7 -7
- package/dist/runtime/types.d.ts +57 -2
- package/dist/runtime/validate/index.js +3 -3
- package/dist/shared/{radd3z4g.js → 0t22wwh1.js} +5 -9
- package/dist/shared/{ady1ms74.js → 2pqx26h8.js} +25 -10
- package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
- package/dist/shared/{jns2wx4t.js → 32zqc1dw.js} +18 -7
- package/dist/shared/{nxb42q7h.js → 47x8x9ze.js} +2 -2
- package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
- package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
- package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
- package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
- package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
- package/dist/shared/{c5g81ngb.js → 6mnp99hx.js} +11 -8
- package/dist/shared/{hqjsxnz7.js → 6qd27mx0.js} +5 -4
- package/dist/shared/{v7tpnhnz.js → 6rk2gen5.js} +5 -3
- package/dist/shared/{2321avmn.js → 7zwwyh0v.js} +46 -16
- package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
- package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
- package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
- package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
- package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
- package/dist/shared/{jgxzjzcz.js → aj2qxyhj.js} +2 -2
- package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
- package/dist/shared/{s4mb6a4b.js → cctw17hj.js} +74 -15
- package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
- package/dist/shared/{axhk06q1.js → cr74e96e.js} +1 -1
- package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
- package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
- package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
- package/dist/shared/{n5y9zyj1.js → ga3sfy93.js} +369 -14
- package/dist/shared/{xrfnq5k8.js → gbtcc2d7.js} +55 -5
- package/dist/shared/{7nk68fj8.js → gde73es6.js} +20 -15
- package/dist/shared/{kkzwp3d2.js → hf22e0wt.js} +55 -19
- package/dist/shared/{tny2t3sm.js → janx09m5.js} +8 -7
- package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
- package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
- package/dist/shared/{qn0srwwq.js → kfjp0gck.js} +5 -3
- package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
- package/dist/shared/{edwqqryd.js → nkaxvt9q.js} +1 -1
- package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
- package/dist/shared/nxdp2ze3.js +1197 -0
- package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
- package/dist/shared/{84ptn35q.js → ps7a6h14.js} +10 -9
- package/dist/shared/{kj680nxv.js → q58y17hy.js} +9 -7
- package/dist/shared/{awrsqrn7.js → qm7cdtvr.js} +22 -9
- package/dist/shared/{q6zsyh31.js → qv2ydck9.js} +156 -205
- package/dist/shared/{kafaa5zv.js → r2sp8hrc.js} +28 -8
- package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
- package/dist/shared/{yjw02enp.js → sdc7r2ww.js} +8 -7
- package/dist/shared/sycnx8gt.js +289 -0
- package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
- package/dist/shared/{zg24x0hm.js → tm5ab8we.js} +10 -2
- package/dist/shared/{ygem90rn.js → v36c5g6p.js} +85 -21
- package/dist/shared/wq9n6pwr.js +139 -0
- package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
- package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
- package/dist/shared/{sr5ms7b3.js → zqrnhwsq.js} +3 -3
- package/dist/tooling/cli.js +70 -23
- package/dist/tooling/migrate/apply.d.ts +2 -0
- package/dist/tooling/migrate/commands.d.ts +1 -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 +7 -0
- package/dist/tooling/migrate/index.js +12 -9
- package/dist/tooling/migrate/lint.d.ts +3 -3
- package/dist/tooling/migrate/plan.d.ts +7 -3
- package/dist/tooling/migrate/policy.d.ts +11 -0
- package/dist/tooling/migrate/sql-lex.d.ts +22 -0
- package/dist/tooling/testing/factories.d.ts +10 -0
- package/dist/tooling/testing/facts.d.ts +3 -0
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +4 -3
- package/package.json +6 -5
- package/dist/shared/0v1g424p.js +0 -26
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tag
|
|
3
|
+
} from "./cn0n68ft.js";
|
|
4
|
+
import {
|
|
5
|
+
OkmError2
|
|
6
|
+
} from "./52szzakm.js";
|
|
7
|
+
|
|
8
|
+
// src/dialects/pg/filter-parse.ts
|
|
9
|
+
var PARSED_OPS = ",eq,in,inList,notIn,lt,lte,gt,gte,between,startsWith,endsWith,contains,like,ilike,";
|
|
10
|
+
var PARSED_FIX = "Use eq, in, notIn, lt, lte, gt, gte, between, startsWith, endsWith, contains, like, or ilike.";
|
|
11
|
+
function parseQuery(allow, sort, query) {
|
|
12
|
+
if (typeof query !== "object" || query === null || Array.isArray(query)) {
|
|
13
|
+
throw new OkmError2("OKM1121", "filters parse expects an object of fields.");
|
|
14
|
+
}
|
|
15
|
+
for (const field of Object.keys(allow)) {
|
|
16
|
+
for (const op of allow[field] ?? []) {
|
|
17
|
+
if (PARSED_OPS.includes(`,${op},`))
|
|
18
|
+
continue;
|
|
19
|
+
throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
|
|
20
|
+
fix: { summary: PARSED_FIX }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const whereParts = [];
|
|
25
|
+
let orderBy;
|
|
26
|
+
for (const key of Object.keys(query)) {
|
|
27
|
+
const value = query[key];
|
|
28
|
+
if (key === "sort" && typeof value === "string" && sort.includes(value)) {
|
|
29
|
+
orderBy = { [value]: "asc" };
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!Object.hasOwn(allow, key)) {
|
|
33
|
+
throw new OkmError2("OKM1123", `Field ${key} is not in the allow list.`, {
|
|
34
|
+
fix: { summary: "Remove the field, or add it to filters({ allow })." }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const ops = allow[key] ?? [];
|
|
38
|
+
whereParts.push(...clauses(key, ops, value));
|
|
39
|
+
}
|
|
40
|
+
const where = combine(whereParts);
|
|
41
|
+
return orderBy === undefined ? { where } : { where, orderBy };
|
|
42
|
+
}
|
|
43
|
+
function clauses(field, ops, value) {
|
|
44
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
45
|
+
if (!ops.includes("eq")) {
|
|
46
|
+
throw new OkmError2("OKM1120", `filters() cannot apply eq on ${field}.`, {
|
|
47
|
+
fix: { summary: "Add eq to that field, or send an object of allowed operators." }
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return [{ [field]: value }];
|
|
51
|
+
}
|
|
52
|
+
const applied = [];
|
|
53
|
+
for (const op of Object.keys(value)) {
|
|
54
|
+
if (!Object.hasOwn(value, op))
|
|
55
|
+
continue;
|
|
56
|
+
if (!ops.includes(op) && !(op === "inList" && ops.includes("in"))) {
|
|
57
|
+
throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
|
|
58
|
+
fix: { summary: "Send an operator that field's allow list names." }
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
applied.push(one(field, op, value[op]));
|
|
62
|
+
}
|
|
63
|
+
if (applied.length === 0) {
|
|
64
|
+
throw new OkmError2("OKM1120", `filters() cannot apply a value on ${field}.`, {
|
|
65
|
+
fix: { summary: "Send eq as a scalar, or an object of allowed operators." }
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return applied;
|
|
69
|
+
}
|
|
70
|
+
function one(field, op, value) {
|
|
71
|
+
switch (op) {
|
|
72
|
+
case "eq":
|
|
73
|
+
return {
|
|
74
|
+
[field]: value !== null && typeof value === "object" ? tag("eq", value) : value
|
|
75
|
+
};
|
|
76
|
+
case "in":
|
|
77
|
+
case "inList":
|
|
78
|
+
return { [field]: tag("inList", list(value, op)) };
|
|
79
|
+
case "notIn":
|
|
80
|
+
return { [field]: tag("notIn", list(value, op)) };
|
|
81
|
+
case "lt":
|
|
82
|
+
return { [field]: tag("lt", value) };
|
|
83
|
+
case "lte":
|
|
84
|
+
return { [field]: tag("lte", value) };
|
|
85
|
+
case "gt":
|
|
86
|
+
return { [field]: tag("gt", value) };
|
|
87
|
+
case "gte":
|
|
88
|
+
return { [field]: tag("gte", value) };
|
|
89
|
+
case "between": {
|
|
90
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
91
|
+
throw new OkmError2("OKM1120", `between on ${field} needs two bounds.`, {
|
|
92
|
+
fix: { summary: "Send between as [from, to]." }
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return { [field]: tag("between", [value[0], value[1]]) };
|
|
96
|
+
}
|
|
97
|
+
case "startsWith":
|
|
98
|
+
return { [field]: tag("startsWith", text(value, op)) };
|
|
99
|
+
case "endsWith":
|
|
100
|
+
return { [field]: tag("endsWith", text(value, op)) };
|
|
101
|
+
case "contains":
|
|
102
|
+
return { [field]: tag("contains", value) };
|
|
103
|
+
case "like":
|
|
104
|
+
return { [field]: tag("like", text(value, op)) };
|
|
105
|
+
case "ilike":
|
|
106
|
+
return { [field]: tag("ilike", text(value, op)) };
|
|
107
|
+
default:
|
|
108
|
+
throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
|
|
109
|
+
fix: { summary: "Use an operator parse applies." }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function combine(parts) {
|
|
114
|
+
const first = parts[0];
|
|
115
|
+
if (first === undefined)
|
|
116
|
+
return {};
|
|
117
|
+
if (parts.length === 1)
|
|
118
|
+
return first;
|
|
119
|
+
return tag("and", parts);
|
|
120
|
+
}
|
|
121
|
+
function list(value, op) {
|
|
122
|
+
if (!Array.isArray(value)) {
|
|
123
|
+
throw new OkmError2("OKM1120", `${op} needs an array.`, {
|
|
124
|
+
fix: { summary: "Send that operator an array of values." }
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function text(value, op) {
|
|
130
|
+
if (typeof value !== "string") {
|
|
131
|
+
throw new OkmError2("OKM1120", `${op} needs a string.`, {
|
|
132
|
+
fix: { summary: "Send that operator a string." }
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
parseQuery
|
|
139
|
+
};
|
|
@@ -11,7 +11,7 @@ async function assertCompatible(pool, schema, recordedHash, source, options) {
|
|
|
11
11
|
if (code.artifact !== undefined) {
|
|
12
12
|
const artifact = code.artifact;
|
|
13
13
|
await loadOnce(`text\x00${artifact.hash}`, async () => {
|
|
14
|
-
const { loadTrustedCatalog } = await import("./
|
|
14
|
+
const { loadTrustedCatalog } = await import("./5fqx9yh4.js");
|
|
15
15
|
loadTrustedCatalog(artifact.text, artifact.hash);
|
|
16
16
|
});
|
|
17
17
|
} else if (code.fromFile) {
|
|
@@ -25,7 +25,7 @@ async function assertCompatible(pool, schema, recordedHash, source, options) {
|
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
-
const { loadTrustedCatalog } = await import("./
|
|
28
|
+
const { loadTrustedCatalog } = await import("./5fqx9yh4.js");
|
|
29
29
|
loadTrustedCatalog(text, code.hash);
|
|
30
30
|
});
|
|
31
31
|
}
|
|
@@ -39,7 +39,7 @@ async function codeHash(schema, source) {
|
|
|
39
39
|
const listed = await readHashFile(source.catalogDir);
|
|
40
40
|
if (listed !== undefined)
|
|
41
41
|
return { hash: listed, fromFile: true };
|
|
42
|
-
const { catalogHash } = await import("./
|
|
42
|
+
const { catalogHash } = await import("./5fqx9yh4.js");
|
|
43
43
|
return { hash: catalogHash(schemaCatalog(schema)), fromFile: false };
|
|
44
44
|
}
|
|
45
45
|
function schemaCatalog(schema) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withHttp
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./hf22e0wt.js";
|
|
4
4
|
import {
|
|
5
5
|
decodeResult
|
|
6
|
-
} from "./
|
|
6
|
+
} from "./gbtcc2d7.js";
|
|
7
7
|
import {
|
|
8
8
|
OkmError2
|
|
9
9
|
} from "./52szzakm.js";
|
|
@@ -17,7 +17,7 @@ async function* streamRows(session, call, prepare, log) {
|
|
|
17
17
|
})));
|
|
18
18
|
}
|
|
19
19
|
const { plan, params } = await prepare();
|
|
20
|
-
const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./
|
|
20
|
+
const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./nkaxvt9q.js")).decodeIncluded;
|
|
21
21
|
for await (const chunk of session.pool.stream(plan.text, params)) {
|
|
22
22
|
const decoded = decode(plan, chunk, call.table);
|
|
23
23
|
if (!Array.isArray(decoded))
|
package/dist/tooling/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import"../shared/
|
|
1
|
+
import"../shared/8bwbwy8h.js";
|
|
2
2
|
import"../shared/n442h54c.js";
|
|
3
|
-
import"../shared/
|
|
3
|
+
import"../shared/krxx77g0.js";
|
|
4
4
|
import {
|
|
5
5
|
formatFindings,
|
|
6
6
|
hasError,
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
generateProject,
|
|
10
10
|
planProject,
|
|
11
11
|
checkProject
|
|
12
|
-
} from "../shared/
|
|
13
|
-
import"../shared/
|
|
14
|
-
import"../shared/
|
|
12
|
+
} from "../shared/qv2ydck9.js";
|
|
13
|
+
import"../shared/ga3sfy93.js";
|
|
14
|
+
import"../shared/8xsbagad.js";
|
|
15
15
|
import {
|
|
16
16
|
OkmError2
|
|
17
17
|
} from "../shared/52szzakm.js";
|
|
@@ -22,14 +22,28 @@ import { dirname, join } from "node:path";
|
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
|
|
24
24
|
// src/tooling/migrate/commands.ts
|
|
25
|
-
function formatFailure(error) {
|
|
26
|
-
|
|
27
|
-
${
|
|
25
|
+
function formatFailure(error, verbose = false) {
|
|
26
|
+
const mapped = OkmError2.from(error);
|
|
27
|
+
let text = `error ${mapped.code}: ${mapped.message}
|
|
28
|
+
${mapped.fix.summary}
|
|
28
29
|
`;
|
|
30
|
+
if (verbose && error instanceof Error && error.stack !== undefined) {
|
|
31
|
+
text += `${error.stack}
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
return text;
|
|
29
35
|
}
|
|
30
36
|
async function run(argv, io) {
|
|
31
37
|
const cwd = io?.cwd ?? process.cwd();
|
|
32
38
|
const stdout = io?.stdout ?? ((text) => process.stdout.write(text));
|
|
39
|
+
if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
|
|
40
|
+
stdout(commandsTable());
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
44
|
+
stdout(commandHelp(argv.filter((arg) => arg !== "--help" && arg !== "-h")));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
33
47
|
const [command, ...rest] = argv;
|
|
34
48
|
if (command === "build") {
|
|
35
49
|
const directory = await buildProject(cwd);
|
|
@@ -57,12 +71,12 @@ async function run(argv, io) {
|
|
|
57
71
|
return;
|
|
58
72
|
}
|
|
59
73
|
if (command === "dev") {
|
|
60
|
-
const { devProject } = await import("../shared/
|
|
74
|
+
const { devProject } = await import("../shared/sdc7r2ww.js");
|
|
61
75
|
stdout(await devProject(cwd));
|
|
62
76
|
return;
|
|
63
77
|
}
|
|
64
78
|
if (command === "push") {
|
|
65
|
-
const { pushProject } = await import("../shared/
|
|
79
|
+
const { pushProject } = await import("../shared/q58y17hy.js");
|
|
66
80
|
stdout(await pushProject(cwd, invoke(splitFlags(rest))));
|
|
67
81
|
return;
|
|
68
82
|
}
|
|
@@ -81,17 +95,17 @@ async function run(argv, io) {
|
|
|
81
95
|
return;
|
|
82
96
|
}
|
|
83
97
|
if (sub === "apply") {
|
|
84
|
-
const { applyProject } = await import("../shared/
|
|
98
|
+
const { applyProject } = await import("../shared/q58y17hy.js");
|
|
85
99
|
stdout(await applyProject(cwd, invoke(parsed), stdout));
|
|
86
100
|
return;
|
|
87
101
|
}
|
|
88
102
|
if (sub === "status") {
|
|
89
|
-
const { statusProject } = await import("../shared/
|
|
103
|
+
const { statusProject } = await import("../shared/6mnp99hx.js");
|
|
90
104
|
stdout(await statusProject(cwd, invoke(parsed)));
|
|
91
105
|
return;
|
|
92
106
|
}
|
|
93
107
|
if (sub === "check") {
|
|
94
|
-
const { checkMigrations } = await import("../shared/
|
|
108
|
+
const { checkMigrations } = await import("../shared/7zwwyh0v.js");
|
|
95
109
|
const provision = parsed.flags.includes("--provision") || rest.slice(1).includes("--provision");
|
|
96
110
|
stdout(await checkMigrations(cwd, invoke(parsed), { provision }));
|
|
97
111
|
return;
|
|
@@ -104,13 +118,13 @@ async function run(argv, io) {
|
|
|
104
118
|
fix: { summary: "Pass okm seed <file>." }
|
|
105
119
|
});
|
|
106
120
|
}
|
|
107
|
-
const { seedProject } = await import("../shared/
|
|
121
|
+
const { seedProject } = await import("../shared/2pqx26h8.js");
|
|
108
122
|
stdout(await seedProject(cwd, parsed.name, invoke(parsed)));
|
|
109
123
|
return;
|
|
110
124
|
}
|
|
111
125
|
if (command === "doctor") {
|
|
112
126
|
const parsed = splitFlags(rest);
|
|
113
|
-
const { doctorProject } = await import("../shared/
|
|
127
|
+
const { doctorProject } = await import("../shared/ps7a6h14.js");
|
|
114
128
|
stdout(await doctorProject(cwd, parsed.name, invoke(parsed)));
|
|
115
129
|
return;
|
|
116
130
|
}
|
|
@@ -122,7 +136,7 @@ async function run(argv, io) {
|
|
|
122
136
|
});
|
|
123
137
|
}
|
|
124
138
|
if (sub === "list" || sub === "check") {
|
|
125
|
-
const { extProject } = await import("../shared/
|
|
139
|
+
const { extProject } = await import("../shared/janx09m5.js");
|
|
126
140
|
stdout(await extProject(cwd, sub, invoke(splitFlags(rest.slice(1)))));
|
|
127
141
|
return;
|
|
128
142
|
}
|
|
@@ -210,6 +224,42 @@ function splitFlags(args) {
|
|
|
210
224
|
statementTimeoutMs
|
|
211
225
|
};
|
|
212
226
|
}
|
|
227
|
+
var COMMANDS = [
|
|
228
|
+
["build", "Write the schema build."],
|
|
229
|
+
["check", "Lint the schema and compare it with one target."],
|
|
230
|
+
["dev", "Watch the schema and reload."],
|
|
231
|
+
["doctor [code]", "Print a target report, or one error code."],
|
|
232
|
+
["ext list", "List extensions the server can install."],
|
|
233
|
+
["ext check", "Compare declared extensions with the server."],
|
|
234
|
+
["generate [name]", "Write the next migration."],
|
|
235
|
+
["migrate plan <name>", "Print the plan and its lock estimates."],
|
|
236
|
+
["migrate apply", "Apply pending migrations."],
|
|
237
|
+
["migrate status", "Print whether the target is current."],
|
|
238
|
+
["migrate check", "Replay migration history in a scratch schema."],
|
|
239
|
+
["push", "Apply the schema diff to one target."],
|
|
240
|
+
["seed <file>", "Run a seed file on one target."]
|
|
241
|
+
];
|
|
242
|
+
function commandsTable() {
|
|
243
|
+
const lines = ["okm <command>", ""];
|
|
244
|
+
for (const [name, summary] of COMMANDS)
|
|
245
|
+
lines.push(` ${name.padEnd(24)} ${summary}`);
|
|
246
|
+
lines.push("", "okm <command> --help prints that command.", "");
|
|
247
|
+
return `${lines.join(`
|
|
248
|
+
`)}
|
|
249
|
+
`;
|
|
250
|
+
}
|
|
251
|
+
function commandHelp(argv) {
|
|
252
|
+
const key = argv.join(" ");
|
|
253
|
+
const found = COMMANDS.find(([name]) => name === key || name.startsWith(`${key} `));
|
|
254
|
+
if (found === undefined) {
|
|
255
|
+
return `Unknown command ${key}.
|
|
256
|
+
|
|
257
|
+
${commandsTable()}`;
|
|
258
|
+
}
|
|
259
|
+
return `${found[0]}
|
|
260
|
+
${found[1]}
|
|
261
|
+
`;
|
|
262
|
+
}
|
|
213
263
|
function numberFlag(flag, value) {
|
|
214
264
|
const parsed = value === undefined ? Number.NaN : Number(value);
|
|
215
265
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -226,16 +276,13 @@ var packageJsonPath = join(here, "..", "..", "package.json");
|
|
|
226
276
|
if (process.argv.includes("--version")) {
|
|
227
277
|
process.stdout.write(`${readPackageVersion(packageJsonPath)}
|
|
228
278
|
`);
|
|
229
|
-
} else
|
|
279
|
+
} else {
|
|
230
280
|
try {
|
|
231
281
|
await run(process.argv.slice(2));
|
|
232
282
|
} catch (error) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} else {
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
283
|
+
const verbose = process.argv.includes("--verbose") || process.env.OKM_DEBUG === "1";
|
|
284
|
+
process.stderr.write(formatFailure(error, verbose));
|
|
285
|
+
process.exitCode = 1;
|
|
239
286
|
}
|
|
240
287
|
}
|
|
241
288
|
function readPackageVersion(path) {
|
|
@@ -90,6 +90,8 @@ export type ApplyRequest = {
|
|
|
90
90
|
export type ApplyReport = {
|
|
91
91
|
readonly target: string;
|
|
92
92
|
readonly applied: readonly string[];
|
|
93
|
+
/** Migrations whose stored hash moved from the pre-D208 hash to the startup hash. */
|
|
94
|
+
readonly restamped?: readonly string[];
|
|
93
95
|
/** Set when the runner issued `SET ROLE` at session start. */
|
|
94
96
|
readonly setRole?: string;
|
|
95
97
|
};
|
|
@@ -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
|
*
|
|
@@ -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
|
*
|
|
@@ -7,6 +7,13 @@ import { type PlanStep } from "./plan.js";
|
|
|
7
7
|
export type StoredMigration = {
|
|
8
8
|
readonly id: string;
|
|
9
9
|
readonly catalogHash: string;
|
|
10
|
+
/**
|
|
11
|
+
* The hash written before D208, which counted roles and grants.
|
|
12
|
+
*
|
|
13
|
+
* Set only when the catalog has them. Apply moves a stored copy of it to
|
|
14
|
+
* {@link StoredMigration.catalogHash}.
|
|
15
|
+
*/
|
|
16
|
+
readonly legacyHash?: string;
|
|
10
17
|
readonly steps: readonly PlanStep[];
|
|
11
18
|
};
|
|
12
19
|
/**
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
import"../../shared/
|
|
2
|
-
import"../../shared/
|
|
1
|
+
import"../../shared/krxx77g0.js";
|
|
2
|
+
import"../../shared/8bwbwy8h.js";
|
|
3
3
|
import"../../shared/n442h54c.js";
|
|
4
4
|
import {
|
|
5
5
|
applyTarget2,
|
|
6
6
|
backfillTiming2
|
|
7
|
-
} from "../../shared/
|
|
7
|
+
} from "../../shared/cctw17hj.js";
|
|
8
8
|
import {
|
|
9
9
|
loadMigrations,
|
|
10
10
|
loadHeadSnapshot,
|
|
11
11
|
readReference
|
|
12
|
-
} from "../../shared/
|
|
12
|
+
} from "../../shared/gde73es6.js";
|
|
13
|
+
import"../../shared/szm0cksa.js";
|
|
14
|
+
import"../../shared/fancm5pt.js";
|
|
13
15
|
import {
|
|
14
|
-
assertTargetPolicy,
|
|
15
|
-
selectTarget,
|
|
16
16
|
loadBuiltSchema,
|
|
17
17
|
loadConfig
|
|
18
|
-
} from "../../shared/
|
|
19
|
-
import
|
|
20
|
-
|
|
18
|
+
} from "../../shared/qv2ydck9.js";
|
|
19
|
+
import {
|
|
20
|
+
assertTargetPolicy,
|
|
21
|
+
selectTarget
|
|
22
|
+
} from "../../shared/ga3sfy93.js";
|
|
23
|
+
import"../../shared/8xsbagad.js";
|
|
21
24
|
import {
|
|
22
25
|
OkmError2
|
|
23
26
|
} from "../../shared/52szzakm.js";
|
|
@@ -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
11
|
import { type MigrationPlan } from "./plan.js";
|
|
12
|
-
/** One finding. `place` is `step N` or `table.column
|
|
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;
|
|
@@ -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,10 @@ 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, or
|
|
132
|
+
* a block comment stays in that step, and so does a `--` line while the
|
|
133
|
+
* quote is still open.
|
|
134
|
+
*
|
|
131
135
|
* @param text - SQL file text
|
|
132
136
|
* @returns The plan. The header class is kept when the file has no steps
|
|
133
137
|
*/
|
|
@@ -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
|
*
|
|
@@ -0,0 +1,22 @@
|
|
|
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. An unterminated string, quoted identifier, block comment, or
|
|
10
|
+
* dollar quote is one opaque tail. This function does not throw.
|
|
11
|
+
*/
|
|
12
|
+
/** Statements of `sql`, comments removed, with no trailing semicolon. */
|
|
13
|
+
export declare function sqlStatements(sql: string): readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Whether `sql` still has an open quote or block comment.
|
|
16
|
+
*
|
|
17
|
+
* A line comment does not count: it ends at the newline or at the end.
|
|
18
|
+
*
|
|
19
|
+
* @param sql - Step text accumulated so far
|
|
20
|
+
* @returns `true` when a later blank line is still inside that quote
|
|
21
|
+
*/
|
|
22
|
+
export declare function sqlQuoteOpen(sql: string): boolean;
|
|
@@ -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 {};
|
|
@@ -56,6 +56,9 @@ export declare const tenantProbe = "00000000-0000-4000-8000-000000000001";
|
|
|
56
56
|
/**
|
|
57
57
|
* Reads every table in declaration order.
|
|
58
58
|
*
|
|
59
|
+
* Views and materialized views are in the query model too. They have no rows
|
|
60
|
+
* of their own to insert, so they are left out.
|
|
61
|
+
*
|
|
59
62
|
* @param schema - Compiled schema
|
|
60
63
|
* @returns One facts object per table
|
|
61
64
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
testing2
|
|
3
|
-
} from "../../shared/
|
|
4
|
-
import"../../shared/
|
|
5
|
-
import"../../shared/
|
|
3
|
+
} from "../../shared/v36c5g6p.js";
|
|
4
|
+
import"../../shared/8xsbagad.js";
|
|
5
|
+
import"../../shared/8bwbwy8h.js";
|
|
6
6
|
export {
|
|
7
7
|
testing2 as testing
|
|
8
8
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
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
|
|
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.
|
|
7
8
|
* Column tenancy is the strategy this version has. A table is tenant when its
|
|
8
9
|
* predicate says so (D199).
|
|
9
10
|
*/
|