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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeUuid,
|
|
3
|
+
decodeUuid
|
|
4
|
+
} from "./2jd1sn0e.js";
|
|
5
|
+
import {
|
|
6
|
+
encodeText,
|
|
7
|
+
decodeText
|
|
8
|
+
} from "./ed0b8k4s.js";
|
|
9
|
+
import {
|
|
10
|
+
snakeCase
|
|
11
|
+
} from "./rpycarcr.js";
|
|
12
|
+
import {
|
|
13
|
+
OkmError2
|
|
14
|
+
} from "./52szzakm.js";
|
|
15
|
+
|
|
16
|
+
// src/dialects/pg/view/client.ts
|
|
17
|
+
var globalViews = new WeakMap;
|
|
18
|
+
function viewGlobalReason(model) {
|
|
19
|
+
return globalViews.get(model);
|
|
20
|
+
}
|
|
21
|
+
function viewMentions(query, name) {
|
|
22
|
+
let pattern = '(^|[^A-Za-z0-9_])"?';
|
|
23
|
+
for (const char of name) {
|
|
24
|
+
if ("\\^$[]().*+?|{}".includes(char))
|
|
25
|
+
pattern += "\\";
|
|
26
|
+
pattern += char;
|
|
27
|
+
}
|
|
28
|
+
pattern += '"?(?![A-Za-z0-9_])';
|
|
29
|
+
return new RegExp(pattern).test(query);
|
|
30
|
+
}
|
|
31
|
+
function viewClient(name, columns, query, tenancy, refresh = "plain") {
|
|
32
|
+
const field = camelCase(name);
|
|
33
|
+
const marked = readGlobal(name, tenancy);
|
|
34
|
+
return {
|
|
35
|
+
field,
|
|
36
|
+
install(model, scope, tables, casing, scoped) {
|
|
37
|
+
if (Object.hasOwn(model, field)) {
|
|
38
|
+
throw new OkmError2("OKM1023", `View ${name} uses the client name ${field}.`, {
|
|
39
|
+
fix: { summary: "Give the view a name that no table uses." }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const exposes = exposesKey(columns, scope);
|
|
43
|
+
if (scope !== undefined && readsTenant(query, tables, casing, scoped)) {
|
|
44
|
+
if (!exposes && marked === undefined) {
|
|
45
|
+
throw new OkmError2("OKM1820", `View ${name} reads a tenant table and does not expose ${scope.key}.`, {
|
|
46
|
+
fix: {
|
|
47
|
+
summary: "Expose the tenant key so the predicate can be applied, or declare global(reason)."
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (exposes) {
|
|
52
|
+
scope.scopeView(field);
|
|
53
|
+
scoped.add(name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const built = {
|
|
57
|
+
name: field,
|
|
58
|
+
sql: name,
|
|
59
|
+
primary: [],
|
|
60
|
+
uniques: [],
|
|
61
|
+
columns: columnModels(columns, scope),
|
|
62
|
+
relations: []
|
|
63
|
+
};
|
|
64
|
+
if (marked !== undefined)
|
|
65
|
+
globalViews.set(built, marked);
|
|
66
|
+
model[field] = built;
|
|
67
|
+
},
|
|
68
|
+
hook(target, ctx) {
|
|
69
|
+
if (ctx.table === field && ctx.session !== undefined) {
|
|
70
|
+
if (refresh !== "plain")
|
|
71
|
+
target.refresh = refreshHandle(ctx.session, name, refresh);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
publish(field, target, ctx);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function refreshHandle(session, name, mode) {
|
|
79
|
+
const concurrently = mode === "concurrently";
|
|
80
|
+
return () => lazyRefresh(() => import("./8etn7d8w.js").then((mod) => mod.refreshView(session, name, concurrently)));
|
|
81
|
+
}
|
|
82
|
+
function lazyRefresh(run) {
|
|
83
|
+
let pending;
|
|
84
|
+
const once = () => {
|
|
85
|
+
pending ??= run();
|
|
86
|
+
return pending;
|
|
87
|
+
};
|
|
88
|
+
const self = {
|
|
89
|
+
then(onFulfilled, onRejected) {
|
|
90
|
+
return once().then(onFulfilled, onRejected);
|
|
91
|
+
},
|
|
92
|
+
catch(onRejected) {
|
|
93
|
+
return self.then(undefined, onRejected);
|
|
94
|
+
},
|
|
95
|
+
finally(fn) {
|
|
96
|
+
return self.then((value) => {
|
|
97
|
+
fn();
|
|
98
|
+
return value;
|
|
99
|
+
}, (error) => {
|
|
100
|
+
fn();
|
|
101
|
+
throw error;
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
"~refresh": true
|
|
105
|
+
};
|
|
106
|
+
return self;
|
|
107
|
+
}
|
|
108
|
+
function publish(field, target, ctx) {
|
|
109
|
+
if (ctx.tables === undefined || ctx.table !== undefined)
|
|
110
|
+
return;
|
|
111
|
+
const handle = ctx.tables[field];
|
|
112
|
+
if (typeof handle !== "object" || handle === null)
|
|
113
|
+
return;
|
|
114
|
+
const source = handle;
|
|
115
|
+
const views = target.views;
|
|
116
|
+
const map = typeof views === "object" && views !== null ? views : {};
|
|
117
|
+
map[field] = {
|
|
118
|
+
find: source.find,
|
|
119
|
+
one: source.one,
|
|
120
|
+
count: source.count,
|
|
121
|
+
exists: source.exists,
|
|
122
|
+
...typeof source.refresh === "function" ? { refresh: source.refresh } : {}
|
|
123
|
+
};
|
|
124
|
+
target.views = map;
|
|
125
|
+
delete ctx.tables[field];
|
|
126
|
+
delete target[field];
|
|
127
|
+
}
|
|
128
|
+
function columnModels(columns, tenancy) {
|
|
129
|
+
const seen = new Set;
|
|
130
|
+
const models = [];
|
|
131
|
+
for (const column of columns) {
|
|
132
|
+
const field = fieldName(column.name, tenancy);
|
|
133
|
+
if (seen.has(field)) {
|
|
134
|
+
throw new OkmError2("OKM1020", `View column ${column.name} repeats ${field}.`, {
|
|
135
|
+
fix: { summary: "Declare each output column once." }
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
seen.add(field);
|
|
139
|
+
const uuid = column.type.toLowerCase() === "uuid";
|
|
140
|
+
const key = tenancy !== undefined && field === tenancy.key;
|
|
141
|
+
models.push({
|
|
142
|
+
field,
|
|
143
|
+
sql: column.name,
|
|
144
|
+
dataType: column.type,
|
|
145
|
+
encode: uuid ? (value) => encodeUuid(textValue(value)) : (value) => encodeText(textValue(value)),
|
|
146
|
+
decode: uuid ? decodeUuid : decodeText,
|
|
147
|
+
hidden: false,
|
|
148
|
+
sensitive: false,
|
|
149
|
+
guarded: key,
|
|
150
|
+
writable: false,
|
|
151
|
+
guardUpdate: false
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return models;
|
|
155
|
+
}
|
|
156
|
+
function exposesKey(columns, tenancy) {
|
|
157
|
+
if (tenancy === undefined)
|
|
158
|
+
return false;
|
|
159
|
+
return columns.some((column) => fieldName(column.name, tenancy) === tenancy.key);
|
|
160
|
+
}
|
|
161
|
+
function readsTenant(query, tables, casing, scoped) {
|
|
162
|
+
for (const name of scoped) {
|
|
163
|
+
if (viewMentions(query, name))
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
for (const table of tables) {
|
|
167
|
+
const mark = table.options?.tenancy;
|
|
168
|
+
if (isGlobalMark(mark))
|
|
169
|
+
continue;
|
|
170
|
+
const sql = casing === "snake" ? snakeCase(table.name) : table.name;
|
|
171
|
+
if (viewMentions(query, sql) || viewMentions(query, table.name))
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
function readGlobal(name, tenancy) {
|
|
177
|
+
if (tenancy === undefined)
|
|
178
|
+
return;
|
|
179
|
+
if (!isGlobalMark(tenancy)) {
|
|
180
|
+
throw new OkmError2("OKM1020", `View ${name} tenancy must be global("reason").`, {
|
|
181
|
+
fix: { summary: 'Pass global("reason") from okmodel/tenancy, or omit tenancy.' }
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return tenancy.reason;
|
|
185
|
+
}
|
|
186
|
+
function isGlobalMark(value) {
|
|
187
|
+
if (typeof value !== "object" || value === null)
|
|
188
|
+
return false;
|
|
189
|
+
const record = value;
|
|
190
|
+
return record.kind === "global" && typeof record.reason === "string" && record.reason.trim().length > 0;
|
|
191
|
+
}
|
|
192
|
+
function fieldName(column, tenancy) {
|
|
193
|
+
const field = camelCase(column);
|
|
194
|
+
if (tenancy !== undefined && (column === tenancy.key || field === tenancy.key))
|
|
195
|
+
return tenancy.key;
|
|
196
|
+
return field;
|
|
197
|
+
}
|
|
198
|
+
function camelCase(name) {
|
|
199
|
+
let out = "";
|
|
200
|
+
let upper = false;
|
|
201
|
+
for (const char of name) {
|
|
202
|
+
if (char === "_") {
|
|
203
|
+
upper = out.length > 0;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
out += upper ? char.toUpperCase() : char;
|
|
207
|
+
upper = false;
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
function textValue(value) {
|
|
212
|
+
if (typeof value !== "string") {
|
|
213
|
+
throw new OkmError2("OKM1121", "A view column value must be text.", {
|
|
214
|
+
fix: { summary: "Pass a string." }
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { viewGlobalReason, viewMentions, viewClient };
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import"./
|
|
1
|
+
import"./qq3899ht.js";
|
|
2
2
|
import"./n442h54c.js";
|
|
3
|
-
import"./
|
|
3
|
+
import"./wkme6tep.js";
|
|
4
4
|
import {
|
|
5
|
+
projectPath,
|
|
5
6
|
loadConfig
|
|
6
|
-
} from "./
|
|
7
|
+
} from "./t3g0k88x.js";
|
|
7
8
|
import {
|
|
8
9
|
listTargets
|
|
9
|
-
} from "./
|
|
10
|
-
import"./
|
|
10
|
+
} from "./ttmg09vd.js";
|
|
11
|
+
import"./9s0ypp87.js";
|
|
11
12
|
|
|
12
13
|
// src/tooling/migrate/dev.ts
|
|
13
14
|
import { mkdirSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
15
15
|
async function devProject(cwd) {
|
|
16
16
|
const config = await loadConfig(cwd);
|
|
17
17
|
const named = listTargets(config).find((target) => target.name === "dev");
|
|
18
18
|
if (named !== undefined)
|
|
19
19
|
return `target ${named.name}
|
|
20
20
|
`;
|
|
21
|
-
const directory =
|
|
21
|
+
const directory = projectPath(cwd, config.out ?? ".okm", "dev-db");
|
|
22
22
|
mkdirSync(directory, { recursive: true });
|
|
23
|
-
const { open } = await import("./
|
|
23
|
+
const { open } = await import("./g90m9b0g.js");
|
|
24
24
|
const pool = await open({ dataDir: directory });
|
|
25
25
|
try {
|
|
26
26
|
await pool.execute("select 1");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OkmError2
|
|
3
|
+
} from "./52szzakm.js";
|
|
4
|
+
|
|
5
|
+
// src/dialects/pg/owner.ts
|
|
6
|
+
var MARKS = new WeakMap;
|
|
7
|
+
function withOwner2(table, owner) {
|
|
8
|
+
if (owner !== "external" && owner !== "ignored") {
|
|
9
|
+
throw new OkmError2("invalid", `Table owner ${String(owner)} is not external or ignored.`, {
|
|
10
|
+
kind: "invalid",
|
|
11
|
+
fix: { summary: "Pass external or ignored. Omit the mark for a managed table." }
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
MARKS.set(table, owner);
|
|
15
|
+
return table;
|
|
16
|
+
}
|
|
17
|
+
function tableOwner(table) {
|
|
18
|
+
return MARKS.get(table);
|
|
19
|
+
}
|
|
20
|
+
function stampOwnership(source, marks) {
|
|
21
|
+
if (marks.size === 0)
|
|
22
|
+
return source;
|
|
23
|
+
let changed = false;
|
|
24
|
+
const objects = source.objects.map((object) => {
|
|
25
|
+
const owner = ownerFor(object, marks);
|
|
26
|
+
if (owner === undefined || object.owner === owner)
|
|
27
|
+
return object;
|
|
28
|
+
changed = true;
|
|
29
|
+
return { ...object, owner };
|
|
30
|
+
});
|
|
31
|
+
if (!changed)
|
|
32
|
+
return source;
|
|
33
|
+
return { version: source.version, objects };
|
|
34
|
+
}
|
|
35
|
+
function ownerFor(object, marks) {
|
|
36
|
+
if (object.kind === "table")
|
|
37
|
+
return marks.get(object.identity.name);
|
|
38
|
+
if (object.kind === "column" || object.kind === "index" || object.kind === "constraint" || object.kind === "trigger" || object.kind === "policy") {
|
|
39
|
+
return marks.get(object.identity.parent.name);
|
|
40
|
+
}
|
|
41
|
+
if (object.kind === "sequence") {
|
|
42
|
+
for (const edge of object.dependencies) {
|
|
43
|
+
if (edge.target.kind !== "table")
|
|
44
|
+
continue;
|
|
45
|
+
const owner = marks.get(edge.target.name);
|
|
46
|
+
if (owner !== undefined)
|
|
47
|
+
return owner;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { withOwner2, tableOwner, stampOwnership };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withHttp,
|
|
3
3
|
attachHttp
|
|
4
|
-
} from "./
|
|
4
|
+
} from "./j60w0dvr.js";
|
|
5
5
|
import {
|
|
6
6
|
OkmError2
|
|
7
7
|
} from "./52szzakm.js";
|
|
@@ -31,7 +31,7 @@ async function acceptServer(pool, schema, rows, http, requireMeta, options, sour
|
|
|
31
31
|
}));
|
|
32
32
|
}
|
|
33
33
|
} else {
|
|
34
|
-
const { assertCompatible } = await import("./
|
|
34
|
+
const { assertCompatible } = await import("./ek2wt2dz.js");
|
|
35
35
|
try {
|
|
36
36
|
await assertCompatible(pool, schema, recorded, source, options);
|
|
37
37
|
} catch (error) {
|
|
@@ -4,9 +4,12 @@ function callerLocation(skip) {
|
|
|
4
4
|
`);
|
|
5
5
|
if (lines === undefined)
|
|
6
6
|
return;
|
|
7
|
+
return locationFromLines(lines, skip);
|
|
8
|
+
}
|
|
9
|
+
function locationFromLines(lines, skip) {
|
|
7
10
|
let left = skip;
|
|
8
11
|
for (const line of lines) {
|
|
9
|
-
const slash = line.lastIndexOf("/");
|
|
12
|
+
const slash = Math.max(line.lastIndexOf("/"), line.lastIndexOf("\\"));
|
|
10
13
|
if (slash < 0)
|
|
11
14
|
continue;
|
|
12
15
|
const tail = line.slice(slash + 1);
|
|
@@ -135,8 +135,11 @@ class ColumnBuilder2 {
|
|
|
135
135
|
guarded() {
|
|
136
136
|
return rebuild(this.state, { guarded: true });
|
|
137
137
|
}
|
|
138
|
-
hidden() {
|
|
139
|
-
return rebuild(this.state, {
|
|
138
|
+
hidden(options) {
|
|
139
|
+
return rebuild(this.state, {
|
|
140
|
+
hidden: true,
|
|
141
|
+
...options?.filterable === true ? { filterable: true } : {}
|
|
142
|
+
});
|
|
140
143
|
}
|
|
141
144
|
sensitive() {
|
|
142
145
|
return rebuild(this.state, { sensitive: true });
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DriverError2
|
|
3
|
+
} from "./31vm914h.js";
|
|
1
4
|
import {
|
|
2
5
|
send,
|
|
3
6
|
connectClient
|
|
4
|
-
} from "./
|
|
7
|
+
} from "./1z04fa85.js";
|
|
5
8
|
import {
|
|
6
9
|
isConnectionLost
|
|
7
|
-
} from "./
|
|
10
|
+
} from "./gwn6v667.js";
|
|
8
11
|
import {
|
|
9
12
|
WireSession
|
|
10
13
|
} from "./4az561fn.js";
|
|
@@ -34,16 +37,19 @@ async function checkout(held) {
|
|
|
34
37
|
}, () => {
|
|
35
38
|
counters.reservedBusy = Math.max(0, counters.reservedBusy - 1);
|
|
36
39
|
}, local);
|
|
37
|
-
const guard = (run) => lost ? rejectClosed("
|
|
40
|
+
const guard = (run) => lost ? rejectClosed("CONNECTION_CLOSED") : run().catch((error) => {
|
|
38
41
|
if (isConnectionLost(error))
|
|
39
42
|
lost = true;
|
|
43
|
+
if (error instanceof Error && error.message.includes("Connection terminated")) {
|
|
44
|
+
throw new DriverError2("CONNECTION_CLOSED", { cause: error });
|
|
45
|
+
}
|
|
40
46
|
throw error;
|
|
41
47
|
});
|
|
42
48
|
let released = false;
|
|
43
49
|
return {
|
|
44
50
|
execute: (text, params, options) => guard(() => runCall(isClosed(), options, (watch) => session.query(text, params, watch))),
|
|
45
51
|
batch: (statements, options) => guard(() => runCall(isClosed(), options, async (watch) => {
|
|
46
|
-
const { runAtomicBatch } = await import("./
|
|
52
|
+
const { runAtomicBatch } = await import("./ag4c7516.js");
|
|
47
53
|
return runAtomicBatch(session, statements, watch);
|
|
48
54
|
})),
|
|
49
55
|
cancel() {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
assertNamespace2,
|
|
4
4
|
normaliseEdges,
|
|
5
5
|
assertProvenance
|
|
6
|
-
} from "./
|
|
6
|
+
} from "./358zsv4r.js";
|
|
7
7
|
import {
|
|
8
8
|
assertIdentifier2,
|
|
9
9
|
fitIdentifier2,
|
|
@@ -23,7 +23,11 @@ function viewObject(input) {
|
|
|
23
23
|
const query = readQuery(input.name, input.query);
|
|
24
24
|
assertNamespace2(namespace);
|
|
25
25
|
assertProvenance(input.provenance);
|
|
26
|
-
const definition = {
|
|
26
|
+
const definition = {
|
|
27
|
+
columns,
|
|
28
|
+
query,
|
|
29
|
+
...input.securityInvoker === true ? { securityInvoker: true } : {}
|
|
30
|
+
};
|
|
27
31
|
return {
|
|
28
32
|
kind: "view",
|
|
29
33
|
identity: { kind: "view", namespace, name: input.name },
|
package/dist/tooling/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import"../shared/
|
|
1
|
+
import"../shared/qq3899ht.js";
|
|
2
2
|
import"../shared/n442h54c.js";
|
|
3
|
-
import"../shared/
|
|
3
|
+
import"../shared/wkme6tep.js";
|
|
4
4
|
import {
|
|
5
5
|
formatFindings,
|
|
6
6
|
hasError,
|
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
generateProject,
|
|
10
10
|
planProject,
|
|
11
11
|
checkProject
|
|
12
|
-
} from "../shared/
|
|
13
|
-
import
|
|
14
|
-
|
|
12
|
+
} from "../shared/t3g0k88x.js";
|
|
13
|
+
import {
|
|
14
|
+
sessionFailure
|
|
15
|
+
} from "../shared/ttmg09vd.js";
|
|
16
|
+
import"../shared/9s0ypp87.js";
|
|
15
17
|
import {
|
|
16
18
|
OkmError2
|
|
17
19
|
} from "../shared/52szzakm.js";
|
|
@@ -23,7 +25,7 @@ import { fileURLToPath } from "node:url";
|
|
|
23
25
|
|
|
24
26
|
// src/tooling/migrate/commands.ts
|
|
25
27
|
function formatFailure(error, verbose = false) {
|
|
26
|
-
const mapped = OkmError2.from(error);
|
|
28
|
+
const mapped = sessionFailure(error) ?? OkmError2.from(error);
|
|
27
29
|
let text = `error ${mapped.code}: ${mapped.message}
|
|
28
30
|
${mapped.fix.summary}
|
|
29
31
|
`;
|
|
@@ -71,12 +73,12 @@ async function run(argv, io) {
|
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
73
75
|
if (command === "dev") {
|
|
74
|
-
const { devProject } = await import("../shared/
|
|
76
|
+
const { devProject } = await import("../shared/xq0mvhts.js");
|
|
75
77
|
stdout(await devProject(cwd));
|
|
76
78
|
return;
|
|
77
79
|
}
|
|
78
80
|
if (command === "push") {
|
|
79
|
-
const { pushProject } = await import("../shared/
|
|
81
|
+
const { pushProject } = await import("../shared/1nw1f3vs.js");
|
|
80
82
|
stdout(await pushProject(cwd, invoke(splitFlags(rest))));
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
@@ -95,17 +97,17 @@ async function run(argv, io) {
|
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
if (sub === "apply") {
|
|
98
|
-
const { applyProject } = await import("../shared/
|
|
100
|
+
const { applyProject } = await import("../shared/1nw1f3vs.js");
|
|
99
101
|
stdout(await applyProject(cwd, invoke(parsed), stdout));
|
|
100
102
|
return;
|
|
101
103
|
}
|
|
102
104
|
if (sub === "status") {
|
|
103
|
-
const { statusProject } = await import("../shared/
|
|
105
|
+
const { statusProject } = await import("../shared/wynzeggs.js");
|
|
104
106
|
stdout(await statusProject(cwd, invoke(parsed)));
|
|
105
107
|
return;
|
|
106
108
|
}
|
|
107
109
|
if (sub === "check") {
|
|
108
|
-
const { checkMigrations } = await import("../shared/
|
|
110
|
+
const { checkMigrations } = await import("../shared/09p049xa.js");
|
|
109
111
|
const provision = parsed.flags.includes("--provision") || rest.slice(1).includes("--provision");
|
|
110
112
|
stdout(await checkMigrations(cwd, invoke(parsed), { provision }));
|
|
111
113
|
return;
|
|
@@ -118,13 +120,13 @@ async function run(argv, io) {
|
|
|
118
120
|
fix: { summary: "Pass okm seed <file>." }
|
|
119
121
|
});
|
|
120
122
|
}
|
|
121
|
-
const { seedProject } = await import("../shared/
|
|
123
|
+
const { seedProject } = await import("../shared/3fqwmr09.js");
|
|
122
124
|
stdout(await seedProject(cwd, parsed.name, invoke(parsed)));
|
|
123
125
|
return;
|
|
124
126
|
}
|
|
125
127
|
if (command === "doctor") {
|
|
126
128
|
const parsed = splitFlags(rest);
|
|
127
|
-
const { doctorProject } = await import("../shared/
|
|
129
|
+
const { doctorProject } = await import("../shared/wt5rzv1h.js");
|
|
128
130
|
stdout(await doctorProject(cwd, parsed.name, invoke(parsed)));
|
|
129
131
|
return;
|
|
130
132
|
}
|
|
@@ -136,7 +138,7 @@ async function run(argv, io) {
|
|
|
136
138
|
});
|
|
137
139
|
}
|
|
138
140
|
if (sub === "list" || sub === "check") {
|
|
139
|
-
const { extProject } = await import("../shared/
|
|
141
|
+
const { extProject } = await import("../shared/p6bjzq9q.js");
|
|
140
142
|
stdout(await extProject(cwd, sub, invoke(splitFlags(rest.slice(1)))));
|
|
141
143
|
return;
|
|
142
144
|
}
|
|
@@ -270,10 +272,34 @@ function numberFlag(flag, value) {
|
|
|
270
272
|
return parsed;
|
|
271
273
|
}
|
|
272
274
|
|
|
275
|
+
// src/tooling/node-version.ts
|
|
276
|
+
function nodeSupportsConfigTypes(version) {
|
|
277
|
+
const match = /^v(\d+)\.(\d+)/.exec(version);
|
|
278
|
+
if (match === null)
|
|
279
|
+
return false;
|
|
280
|
+
const major = Number(match[1]);
|
|
281
|
+
const minor = Number(match[2]);
|
|
282
|
+
if (major > 22)
|
|
283
|
+
return true;
|
|
284
|
+
if (major < 22)
|
|
285
|
+
return false;
|
|
286
|
+
return minor >= 18;
|
|
287
|
+
}
|
|
288
|
+
function nodeStartupMessage(version, bunRuntime) {
|
|
289
|
+
if (bunRuntime || nodeSupportsConfigTypes(version))
|
|
290
|
+
return;
|
|
291
|
+
return `okm needs Node.js >=22.18 to load a .ts config (type stripping). This process is ${version}. Run bun --bun okm to use the Bun runtime.
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
|
|
273
295
|
// src/tooling/cli.ts
|
|
274
296
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
275
297
|
var packageJsonPath = join(here, "..", "..", "package.json");
|
|
276
|
-
|
|
298
|
+
var nodeProblem = nodeStartupMessage(process.version, process.versions.bun !== undefined);
|
|
299
|
+
if (nodeProblem !== undefined) {
|
|
300
|
+
process.stderr.write(nodeProblem);
|
|
301
|
+
process.exitCode = 1;
|
|
302
|
+
} else if (process.argv.includes("--version")) {
|
|
277
303
|
process.stdout.write(`${readPackageVersion(packageJsonPath)}
|
|
278
304
|
`);
|
|
279
305
|
} else {
|
|
@@ -49,6 +49,14 @@ export type ApplyRequest = {
|
|
|
49
49
|
* `SET ROLE` before any statement. That statement is not a plan step.
|
|
50
50
|
*/
|
|
51
51
|
readonly migrationRole?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Application role.
|
|
54
|
+
*
|
|
55
|
+
* After the plan, this role receives `SELECT` on `okm_meta` and nothing
|
|
56
|
+
* else. `connect()` can then read the stored hash. Check scratch paths
|
|
57
|
+
* leave this unset.
|
|
58
|
+
*/
|
|
59
|
+
readonly appRole?: string;
|
|
52
60
|
/**
|
|
53
61
|
* Schema the migration role may create in.
|
|
54
62
|
*
|
|
@@ -95,6 +103,12 @@ export type ApplyReport = {
|
|
|
95
103
|
/** Set when the runner issued `SET ROLE` at session start. */
|
|
96
104
|
readonly setRole?: string;
|
|
97
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Creates `okm_meta`, `okm_history`, and `okm_backfill` when they are absent.
|
|
108
|
+
*
|
|
109
|
+
* @param execute - Runs one statement on the apply or check connection
|
|
110
|
+
*/
|
|
111
|
+
export declare function ensureMigrationTables(execute: (sql: string) => Promise<unknown>): Promise<void>;
|
|
98
112
|
/**
|
|
99
113
|
* Groups pending steps into the transactions apply will run.
|
|
100
114
|
*
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `okm migrate check` proves a migration history is sound (D196).
|
|
3
3
|
*
|
|
4
|
-
* The command replays every file into a scratch schema
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* linter. Nothing here changes apply.
|
|
4
|
+
* The command replays every file into a scratch schema inside one transaction
|
|
5
|
+
* and rolls that transaction back. It also checks the previous catalog, the
|
|
6
|
+
* head, and the linter. Nothing here changes apply.
|
|
8
7
|
*/
|
|
9
8
|
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
10
9
|
import { type MigrationClass } from "./classify.js";
|
|
@@ -76,6 +76,10 @@ export declare const STEP_CLASS: {
|
|
|
76
76
|
readonly "rename-sequence": "contract";
|
|
77
77
|
readonly "backfill-expand": "expand";
|
|
78
78
|
readonly "backfill-contract": "contract";
|
|
79
|
+
readonly "enable-rls": "expand";
|
|
80
|
+
readonly "create-policy": "expand";
|
|
81
|
+
readonly "drop-policy": "contract";
|
|
82
|
+
readonly "disable-rls": "contract";
|
|
79
83
|
readonly "raw-sql": "unclassified";
|
|
80
84
|
};
|
|
81
85
|
/** A planner operation. */
|
|
@@ -59,10 +59,11 @@ export type MigrateConfig = {
|
|
|
59
59
|
readonly statementTimeoutMs?: number;
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
|
-
* Migration role and
|
|
62
|
+
* Migration role, application role, and the optional reporting role.
|
|
63
63
|
*
|
|
64
64
|
* A name is external unless it is also listed in `managed`. The plan creates
|
|
65
|
-
* a managed role and never drops one.
|
|
65
|
+
* a managed role and never drops one. `reporting` receives select only.
|
|
66
|
+
* `columns` grants select or update on named columns.
|
|
66
67
|
*/
|
|
67
68
|
readonly roles?: RolesInput;
|
|
68
69
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Migration files on disk: SQL from {@link formatPlan} plus the catalog hash.
|
|
3
3
|
*/
|
|
4
4
|
import { parseCatalog } from "../../contracts/catalog/document.js";
|
|
5
|
-
import { type PlanStep } from "./plan.js";
|
|
5
|
+
import { type MigrationPlan, type PlanStep } from "./plan.js";
|
|
6
6
|
/** One migration the applier can run and resume. */
|
|
7
7
|
export type StoredMigration = {
|
|
8
8
|
readonly id: string;
|
|
@@ -25,6 +25,16 @@ export type StoredMigration = {
|
|
|
25
25
|
* @returns Migrations in apply order
|
|
26
26
|
*/
|
|
27
27
|
export declare function loadMigrations(directory: string): readonly StoredMigration[];
|
|
28
|
+
/**
|
|
29
|
+
* Reads one migration SQL file.
|
|
30
|
+
*
|
|
31
|
+
* Line endings and a UTF-8 BOM are normalized. A non-empty file with no steps
|
|
32
|
+
* and no `-- no steps` line is refused.
|
|
33
|
+
*
|
|
34
|
+
* @param path - `*.sql` path
|
|
35
|
+
* @returns The plan
|
|
36
|
+
*/
|
|
37
|
+
export declare function readMigrationPlan(path: string): MigrationPlan;
|
|
28
38
|
/**
|
|
29
39
|
* Loads the last migration's catalog, which is the head snapshot.
|
|
30
40
|
*
|