okmodel 0.1.1 → 0.3.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 +727 -37
- package/dist/adapters/capabilities.d.ts +34 -0
- package/dist/adapters/pg/bunsql-extra.d.ts +17 -0
- package/dist/adapters/pg/bunsql.d.ts +60 -0
- package/dist/adapters/pg/call.d.ts +2 -12
- package/dist/adapters/pg/nodepostgres-extra.d.ts +18 -0
- package/dist/adapters/pg/nodepostgres.d.ts +57 -0
- package/dist/adapters/pg/postgres-extra.d.ts +50 -1
- package/dist/adapters/pg/postgresjs.d.ts +0 -4
- package/dist/adapters/pg/session.d.ts +94 -0
- package/dist/adapters/pg/watch.d.ts +18 -0
- package/dist/contracts/catalog/enum.d.ts +31 -4
- package/dist/contracts/catalog/extension.d.ts +61 -0
- package/dist/contracts/catalog/object.d.ts +1 -0
- package/dist/contracts/catalog/privilege.d.ts +64 -0
- package/dist/contracts/catalog/routine.d.ts +56 -0
- package/dist/contracts/catalog/types.d.ts +179 -18
- package/dist/contracts/catalog/view.d.ts +73 -0
- package/dist/contracts/connection.d.ts +13 -0
- package/dist/contracts/driver.d.ts +18 -5
- package/dist/contracts/error.d.ts +17 -1
- package/dist/contracts/generator.d.ts +63 -0
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/internal.js +11 -9
- package/dist/contracts/location.d.ts +24 -0
- package/dist/contracts/rows.d.ts +15 -0
- package/dist/dialects/pg/archive-bind.d.ts +22 -0
- package/dist/dialects/pg/column.d.ts +129 -10
- package/dist/dialects/pg/compile.d.ts +1 -0
- package/dist/dialects/pg/custom.d.ts +6 -0
- package/dist/dialects/pg/ddl.d.ts +65 -1
- package/dist/dialects/pg/enum.d.ts +10 -8
- package/dist/dialects/pg/ext/citext.d.ts +14 -0
- package/dist/dialects/pg/ext/citext.js +11 -0
- package/dist/dialects/pg/ext/index.d.ts +52 -0
- package/dist/dialects/pg/ext/index.js +6 -0
- package/dist/dialects/pg/ext/pg-trgm.d.ts +53 -0
- package/dist/dialects/pg/ext/pg-trgm.js +42 -0
- package/dist/dialects/pg/filter-parse.d.ts +19 -0
- package/dist/dialects/pg/filters.d.ts +19 -0
- package/dist/dialects/pg/fn/index.d.ts +107 -0
- package/dist/dialects/pg/fn/index.js +175 -0
- package/dist/dialects/pg/fn/touch.d.ts +14 -0
- package/dist/dialects/pg/index.d.ts +11 -2
- package/dist/dialects/pg/index.js +243 -1062
- package/dist/dialects/pg/introspect.d.ts +10 -1
- package/dist/dialects/pg/keys.d.ts +19 -3
- package/dist/dialects/pg/model.d.ts +223 -2
- package/dist/dialects/pg/operator-fit.d.ts +86 -0
- package/dist/dialects/pg/operators.d.ts +46 -1
- package/dist/dialects/pg/ops/arr-append.d.ts +9 -0
- package/dist/dialects/pg/ops/arr-ns.d.ts +7 -0
- package/dist/dialects/pg/ops/arr-remove.d.ts +9 -0
- package/dist/dialects/pg/ops/containedBy.d.ts +9 -0
- package/dist/dialects/pg/ops/contains.d.ts +8 -4
- package/dist/dialects/pg/ops/hasAnyKey.d.ts +8 -0
- package/dist/dialects/pg/ops/hasKey.d.ts +8 -0
- package/dist/dialects/pg/ops/json-ns.d.ts +6 -0
- package/dist/dialects/pg/ops/json-set.d.ts +12 -0
- package/dist/dialects/pg/ops/matches.d.ts +16 -0
- package/dist/dialects/pg/ops/overlaps.d.ts +9 -0
- package/dist/dialects/pg/ops/path.d.ts +13 -0
- package/dist/dialects/pg/preset.d.ts +76 -0
- package/dist/dialects/pg/relations.d.ts +59 -2
- package/dist/dialects/pg/role/check.d.ts +45 -0
- package/dist/dialects/pg/role/index.d.ts +50 -0
- package/dist/dialects/pg/role/sql.d.ts +44 -0
- package/dist/dialects/pg/schema.d.ts +109 -5
- package/dist/dialects/pg/table.d.ts +137 -11
- package/dist/dialects/pg/temporal.d.ts +9 -7
- package/dist/dialects/pg/tenancy.d.ts +103 -0
- package/dist/dialects/pg/through.d.ts +21 -0
- package/dist/dialects/pg/trait.d.ts +141 -0
- package/dist/dialects/pg/view/client.d.ts +30 -0
- package/dist/dialects/pg/view/depend.d.ts +35 -0
- package/dist/dialects/pg/view/index.d.ts +114 -0
- package/dist/dialects/pg/view/index.js +289 -0
- package/dist/dialects/pg/view/scratch.d.ts +21 -0
- package/dist/dialects/pg/where.d.ts +37 -0
- package/dist/runtime/aggregate.d.ts +25 -0
- package/dist/runtime/archive-handle.d.ts +18 -0
- package/dist/runtime/archive-rules.d.ts +12 -0
- package/dist/runtime/archive.d.ts +73 -0
- package/dist/runtime/batch.d.ts +20 -0
- package/dist/runtime/client.d.ts +114 -5
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/exposure.d.ts +27 -0
- package/dist/runtime/hookm.d.ts +39 -0
- package/dist/runtime/ids/index.d.ts +11 -0
- package/dist/runtime/ids/index.js +193 -0
- package/dist/runtime/ids/okid-extended.d.ts +19 -0
- package/dist/runtime/ids/okid-generator.d.ts +21 -0
- package/dist/runtime/ids/okid-shared.d.ts +89 -0
- package/dist/runtime/ids/okid.d.ts +24 -0
- package/dist/runtime/ids/uuid.d.ts +21 -0
- package/dist/runtime/lock.d.ts +22 -0
- package/dist/runtime/operator-sql.d.ts +8 -0
- package/dist/runtime/page.d.ts +25 -0
- package/dist/runtime/pg/bun.d.ts +30 -0
- package/dist/runtime/pg/bun.js +52 -0
- package/dist/runtime/pg/pg.d.ts +29 -0
- package/dist/runtime/pg/pg.js +52 -0
- package/dist/runtime/pg/pglite.js +16 -17
- package/dist/runtime/pg/postgresjs.js +14 -24
- package/dist/runtime/plan.d.ts +146 -7
- package/dist/runtime/preset-stack.d.ts +17 -0
- package/dist/runtime/presets.d.ts +48 -0
- package/dist/runtime/read-stream.d.ts +26 -0
- package/dist/runtime/safety/index.d.ts +159 -0
- package/dist/runtime/safety/index.js +294 -0
- package/dist/runtime/safety-hook.d.ts +42 -0
- package/dist/runtime/server-check.d.ts +34 -0
- package/dist/runtime/tenancy/index.d.ts +37 -0
- package/dist/runtime/tenancy/index.js +368 -0
- package/dist/runtime/trait-read.d.ts +30 -0
- package/dist/runtime/traits/index.d.ts +133 -0
- package/dist/runtime/traits/index.js +646 -0
- package/dist/runtime/transaction.d.ts +19 -0
- package/dist/runtime/tx.d.ts +22 -7
- package/dist/runtime/types.d.ts +376 -27
- package/dist/runtime/validate/bind.d.ts +11 -0
- package/dist/runtime/validate/closed.d.ts +16 -0
- package/dist/runtime/validate/engine.d.ts +41 -0
- package/dist/runtime/validate/hook.d.ts +17 -0
- package/dist/runtime/validate/index.d.ts +21 -0
- package/dist/runtime/validate/index.js +175 -0
- package/dist/runtime/validate/load.d.ts +13 -0
- package/dist/runtime/validate/places.d.ts +41 -0
- package/dist/runtime/validate/rules.d.ts +108 -0
- package/dist/runtime/validate/surface.d.ts +102 -0
- package/dist/runtime/write.d.ts +30 -5
- package/dist/shared/01yr20s5.js +149 -0
- package/dist/shared/0jc3ffzf.js +154 -0
- package/dist/shared/0v1g424p.js +26 -0
- package/dist/shared/1g0drakv.js +225 -0
- package/dist/shared/{kprcwaha.js → 1tfdfvhh.js} +5 -3
- package/dist/shared/{05w44d8g.js → 2ssqrv66.js} +767 -715
- package/dist/shared/3ekj9x92.js +210 -0
- package/dist/shared/49ypnfwt.js +1122 -0
- package/dist/shared/4e4c6vq2.js +62 -0
- package/dist/shared/4m9fn52z.js +70 -0
- package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
- package/dist/shared/5nmdenb6.js +1229 -0
- package/dist/shared/672t5vym.js +12 -0
- package/dist/shared/6bazvqwm.js +86 -0
- package/dist/shared/6w5j844a.js +201 -0
- package/dist/shared/{fgqgsb5z.js → 75xkjv0t.js} +2 -2
- package/dist/shared/7fvtrkwr.js +84 -0
- package/dist/shared/7m88x3e9.js +45 -0
- package/dist/shared/{mtkwv8wx.js → 7p2wbz7f.js} +1 -1
- package/dist/shared/83eaefwr.js +10 -0
- package/dist/shared/94ez8kzh.js +19 -0
- package/dist/shared/96ps4g1z.js +32 -0
- package/dist/shared/{xqbhg6cn.js → 999ade2q.js} +95 -10
- package/dist/shared/9m80mkac.js +158 -0
- package/dist/shared/9x25fxhc.js +150 -0
- package/dist/shared/a5sxz52y.js +13 -0
- package/dist/shared/axhk06q1.js +156 -0
- package/dist/shared/{dynq16vb.js → b066g80h.js} +8 -6
- package/dist/shared/bqha5cg6.js +229 -0
- package/dist/shared/bryqk9k9.js +31 -0
- package/dist/shared/c8aghh05.js +442 -0
- package/dist/shared/c9z5g0fm.js +309 -0
- package/dist/shared/{m3ypagh5.js → dbjej1tw.js} +124 -15
- package/dist/shared/dyj86cmj.js +69 -0
- package/dist/shared/e6wg00wd.js +164 -0
- package/dist/shared/{hadrfb9h.js → edwqqryd.js} +17 -14
- package/dist/shared/{qk3hxfja.js → fptpmvxp.js} +5 -5
- package/dist/shared/fs0ex00p.js +76 -0
- package/dist/shared/{jjb7yht9.js → g4f5kcx9.js} +3 -3
- package/dist/shared/gjthds9j.js +48 -0
- package/dist/shared/hcmdtwgh.js +102 -0
- package/dist/shared/{g5fhqmwe.js → jkc6ws1p.js} +6 -56
- package/dist/shared/{yd1kzwn2.js → jns2wx4t.js} +55 -12
- package/dist/shared/k34e0xf2.js +450 -0
- package/dist/shared/k3ted12y.js +33 -0
- package/dist/shared/k7dwkhvq.js +31 -0
- package/dist/shared/{8b04d28c.js → kgnc8grt.js} +2 -2
- package/dist/shared/{hd293j9g.js → kyxb62mc.js} +14 -2
- package/dist/shared/m78s7898.js +376 -0
- package/dist/shared/m9tv7xn7.js +404 -0
- package/dist/shared/meck53jv.js +99 -0
- package/dist/shared/mt5wswjz.js +90 -0
- package/dist/shared/n0dgs0pb.js +75 -0
- package/dist/shared/n442h54c.js +14 -0
- package/dist/shared/n4jqhpqz.js +27 -0
- package/dist/shared/n7mcc1gk.js +132 -0
- package/dist/shared/ne02ng5c.js +68 -0
- package/dist/shared/{mkjmqjv4.js → nfzc5z0w.js} +2 -104
- package/dist/shared/nsva2a6b.js +42 -0
- package/dist/shared/p24x2334.js +195 -0
- package/dist/shared/qbxzvwwc.js +88 -0
- package/dist/shared/{w8samvk1.js → qybh48nd.js} +1 -1
- package/dist/shared/r6nwkxnt.js +86 -0
- package/dist/shared/{xy79j5hq.js → rrstp3kb.js} +25 -36
- package/dist/shared/rwmrejjz.js +26 -0
- package/dist/shared/{zhc5vmgf.js → s6gqfn5j.js} +2 -2
- package/dist/shared/{523ft3e8.js → scwxrpx2.js} +7 -5
- package/dist/shared/{j5cn2963.js → t4tbsy9h.js} +13 -64
- package/dist/shared/{y9m15dfv.js → t616ynfn.js} +544 -32
- package/dist/shared/{ngk8gj61.js → tbszx8mz.js} +15 -1
- package/dist/shared/th14ygv2.js +69 -0
- package/dist/shared/tmbdywx0.js +178 -0
- package/dist/shared/ty3v3hfk.js +64 -0
- package/dist/shared/wekze34d.js +246 -0
- package/dist/shared/x6wysh83.js +1132 -0
- package/dist/shared/{rgs0st28.js → xrfnq5k8.js} +237 -434
- package/dist/shared/{x5x59srr.js → xva8v7qk.js} +416 -83
- package/dist/shared/{dkn3y60a.js → xw1d69vd.js} +76 -112
- package/dist/shared/y005b761.js +112 -0
- package/dist/shared/ydaz1sv3.js +55 -0
- package/dist/shared/{p0cbr3g2.js → zwmqxb0g.js} +1 -1
- package/dist/tooling/cli.js +40 -12
- package/dist/tooling/migrate/apply.d.ts +16 -0
- package/dist/tooling/migrate/commands.d.ts +12 -1
- package/dist/tooling/migrate/config.d.ts +8 -0
- package/dist/tooling/migrate/doctor.d.ts +25 -0
- package/dist/tooling/migrate/drift.d.ts +19 -0
- package/dist/tooling/migrate/engine.d.ts +36 -0
- package/dist/tooling/migrate/ext.d.ts +17 -0
- package/dist/tooling/migrate/extensions.d.ts +51 -0
- package/dist/tooling/migrate/managed.d.ts +17 -0
- package/dist/tooling/migrate/plan.d.ts +14 -1
- package/dist/tooling/migrate/project.d.ts +25 -1
- package/dist/tooling/migrate/type-name.d.ts +19 -0
- package/package.json +61 -4
- package/dist/shared/gk7cs7sp.js +0 -566
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import {
|
|
2
|
+
viewObject,
|
|
3
|
+
materializedViewObject,
|
|
4
|
+
materializedViewIndex
|
|
5
|
+
} from "../../../shared/y005b761.js";
|
|
6
|
+
import {
|
|
7
|
+
encodeUuid,
|
|
8
|
+
decodeUuid
|
|
9
|
+
} from "../../../shared/dyj86cmj.js";
|
|
10
|
+
import {
|
|
11
|
+
snakeCase
|
|
12
|
+
} from "../../../shared/6w5j844a.js";
|
|
13
|
+
import {
|
|
14
|
+
encodeText,
|
|
15
|
+
decodeText
|
|
16
|
+
} from "../../../shared/4m9fn52z.js";
|
|
17
|
+
import {
|
|
18
|
+
staticNamespace2
|
|
19
|
+
} from "../../../shared/t4tbsy9h.js";
|
|
20
|
+
import {
|
|
21
|
+
OkmError2
|
|
22
|
+
} from "../../../shared/52szzakm.js";
|
|
23
|
+
|
|
24
|
+
// src/dialects/pg/view/client.ts
|
|
25
|
+
function viewClient(name, columns, query, tenancy) {
|
|
26
|
+
const field = camelCase(name);
|
|
27
|
+
const marked = readGlobal(name, tenancy);
|
|
28
|
+
return {
|
|
29
|
+
field,
|
|
30
|
+
install(model, scope, tables, casing, scoped) {
|
|
31
|
+
if (Object.hasOwn(model, field)) {
|
|
32
|
+
throw new OkmError2("OKM1023", `View ${name} uses the client name ${field}.`, {
|
|
33
|
+
fix: { summary: "Give the view a name that no table uses." }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const exposes = exposesKey(columns, scope);
|
|
37
|
+
if (scope !== undefined && readsTenant(query, tables, casing, scoped)) {
|
|
38
|
+
if (!exposes && marked === undefined) {
|
|
39
|
+
throw new OkmError2("OKM1820", `View ${name} reads a tenant table and does not expose ${scope.key}.`, {
|
|
40
|
+
fix: {
|
|
41
|
+
summary: "Expose the tenant key so the predicate can be applied, or declare global(reason)."
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (exposes) {
|
|
46
|
+
scope.scopeView(field);
|
|
47
|
+
scoped.add(name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
model[field] = {
|
|
51
|
+
name: field,
|
|
52
|
+
sql: name,
|
|
53
|
+
primary: [],
|
|
54
|
+
uniques: [],
|
|
55
|
+
columns: columnModels(columns, scope),
|
|
56
|
+
relations: []
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
hook(target, ctx) {
|
|
60
|
+
publish(field, target, ctx);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function publish(field, target, ctx) {
|
|
65
|
+
if (ctx.tables === undefined || ctx.table !== undefined)
|
|
66
|
+
return;
|
|
67
|
+
const handle = ctx.tables[field];
|
|
68
|
+
if (typeof handle !== "object" || handle === null)
|
|
69
|
+
return;
|
|
70
|
+
const source = handle;
|
|
71
|
+
const views = target.views;
|
|
72
|
+
const map = typeof views === "object" && views !== null ? views : {};
|
|
73
|
+
map[field] = {
|
|
74
|
+
find: source.find,
|
|
75
|
+
one: source.one,
|
|
76
|
+
count: source.count,
|
|
77
|
+
exists: source.exists
|
|
78
|
+
};
|
|
79
|
+
target.views = map;
|
|
80
|
+
delete ctx.tables[field];
|
|
81
|
+
delete target[field];
|
|
82
|
+
}
|
|
83
|
+
function columnModels(columns, tenancy) {
|
|
84
|
+
const seen = new Set;
|
|
85
|
+
const models = [];
|
|
86
|
+
for (const column of columns) {
|
|
87
|
+
const field = fieldName(column.name, tenancy);
|
|
88
|
+
if (seen.has(field)) {
|
|
89
|
+
throw new OkmError2("OKM1020", `View column ${column.name} repeats ${field}.`, {
|
|
90
|
+
fix: { summary: "Declare each output column once." }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
seen.add(field);
|
|
94
|
+
const uuid = column.type.toLowerCase() === "uuid";
|
|
95
|
+
const key = tenancy !== undefined && field === tenancy.key;
|
|
96
|
+
models.push({
|
|
97
|
+
field,
|
|
98
|
+
sql: column.name,
|
|
99
|
+
dataType: column.type,
|
|
100
|
+
encode: uuid ? (value) => encodeUuid(textValue(value)) : (value) => encodeText(textValue(value)),
|
|
101
|
+
decode: uuid ? decodeUuid : decodeText,
|
|
102
|
+
hidden: false,
|
|
103
|
+
sensitive: false,
|
|
104
|
+
guarded: key,
|
|
105
|
+
writable: false,
|
|
106
|
+
guardUpdate: false
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return models;
|
|
110
|
+
}
|
|
111
|
+
function exposesKey(columns, tenancy) {
|
|
112
|
+
if (tenancy === undefined)
|
|
113
|
+
return false;
|
|
114
|
+
return columns.some((column) => fieldName(column.name, tenancy) === tenancy.key);
|
|
115
|
+
}
|
|
116
|
+
function readsTenant(query, tables, casing, scoped) {
|
|
117
|
+
for (const name of scoped) {
|
|
118
|
+
if (mentions(query, name))
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
for (const table of tables) {
|
|
122
|
+
const mark = table.options?.tenancy;
|
|
123
|
+
if (isGlobalMark(mark))
|
|
124
|
+
continue;
|
|
125
|
+
const sql = casing === "snake" ? snakeCase(table.name) : table.name;
|
|
126
|
+
if (mentions(query, sql) || mentions(query, table.name))
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
function readGlobal(name, tenancy) {
|
|
132
|
+
if (tenancy === undefined)
|
|
133
|
+
return;
|
|
134
|
+
if (!isGlobalMark(tenancy)) {
|
|
135
|
+
throw new OkmError2("OKM1020", `View ${name} tenancy must be global("reason").`, {
|
|
136
|
+
fix: { summary: 'Pass global("reason") from okmodel/tenancy, or omit tenancy.' }
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return tenancy.reason;
|
|
140
|
+
}
|
|
141
|
+
function isGlobalMark(value) {
|
|
142
|
+
if (typeof value !== "object" || value === null)
|
|
143
|
+
return false;
|
|
144
|
+
const record = value;
|
|
145
|
+
return record.kind === "global" && typeof record.reason === "string" && record.reason.trim().length > 0;
|
|
146
|
+
}
|
|
147
|
+
function fieldName(column, tenancy) {
|
|
148
|
+
const field = camelCase(column);
|
|
149
|
+
if (tenancy !== undefined && (column === tenancy.key || field === tenancy.key))
|
|
150
|
+
return tenancy.key;
|
|
151
|
+
return field;
|
|
152
|
+
}
|
|
153
|
+
function camelCase(name) {
|
|
154
|
+
let out = "";
|
|
155
|
+
let upper = false;
|
|
156
|
+
for (const char of name) {
|
|
157
|
+
if (char === "_") {
|
|
158
|
+
upper = out.length > 0;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
out += upper ? char.toUpperCase() : char;
|
|
162
|
+
upper = false;
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
function mentions(query, name) {
|
|
167
|
+
let pattern = '(^|[^A-Za-z0-9_])"?';
|
|
168
|
+
for (const char of name) {
|
|
169
|
+
if ("\\^$[]().*+?|{}".includes(char))
|
|
170
|
+
pattern += "\\";
|
|
171
|
+
pattern += char;
|
|
172
|
+
}
|
|
173
|
+
pattern += '"?(?![A-Za-z0-9_])';
|
|
174
|
+
return new RegExp(pattern).test(query);
|
|
175
|
+
}
|
|
176
|
+
function textValue(value) {
|
|
177
|
+
if (typeof value !== "string") {
|
|
178
|
+
throw new OkmError2("OKM1121", "A view column value must be text.", {
|
|
179
|
+
fix: { summary: "Pass a string." }
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/dialects/pg/view/index.ts
|
|
186
|
+
function view(name, options) {
|
|
187
|
+
const schema = options.schema ?? "public";
|
|
188
|
+
const columns = options.columns.map((column) => ({ name: column.name, dataType: column.type }));
|
|
189
|
+
const query = bodyText(options.query);
|
|
190
|
+
const bound = viewClient(name, options.columns, query, options.tenancy);
|
|
191
|
+
return {
|
|
192
|
+
name,
|
|
193
|
+
schema,
|
|
194
|
+
install: bound.install,
|
|
195
|
+
hook: bound.hook,
|
|
196
|
+
contribute(peers, built) {
|
|
197
|
+
assertPeers(peers);
|
|
198
|
+
assertNoTable(built, name);
|
|
199
|
+
const provenance = { origin: "file", name };
|
|
200
|
+
return [
|
|
201
|
+
viewObject({
|
|
202
|
+
namespace: staticNamespace2(schema),
|
|
203
|
+
name,
|
|
204
|
+
columns,
|
|
205
|
+
query,
|
|
206
|
+
provenance
|
|
207
|
+
})
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function materializedView(name, options) {
|
|
213
|
+
const schema = options.schema ?? "public";
|
|
214
|
+
const columns = options.columns.map((column) => ({ name: column.name, dataType: column.type }));
|
|
215
|
+
const query = bodyText(options.query);
|
|
216
|
+
const bound = viewClient(name, options.columns, query, options.tenancy);
|
|
217
|
+
const indexes = options.indexes ?? [];
|
|
218
|
+
if (options.refresh === "concurrently" && !indexes.some((index) => index.unique === true)) {
|
|
219
|
+
throw new OkmError2("OKM1822", `Materialized view ${name} refreshes concurrently and has no unique index.`, { fix: { summary: "Add a unique index, or refresh without CONCURRENTLY." } });
|
|
220
|
+
}
|
|
221
|
+
const columnNames = new Set(columns.map((column) => column.name));
|
|
222
|
+
for (const index of indexes) {
|
|
223
|
+
for (const column of index.columns) {
|
|
224
|
+
if (!columnNames.has(column)) {
|
|
225
|
+
throw new OkmError2("OKM1020", `Index on ${name} names column ${column}, which the materialized view does not have.`, { fix: { summary: "Name a column of the materialized view." } });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
name,
|
|
231
|
+
schema,
|
|
232
|
+
install: bound.install,
|
|
233
|
+
hook: bound.hook,
|
|
234
|
+
contribute(peers, built) {
|
|
235
|
+
assertPeers(peers);
|
|
236
|
+
assertNoTable(built, name);
|
|
237
|
+
const namespace = staticNamespace2(schema);
|
|
238
|
+
const provenance = { origin: "file", name };
|
|
239
|
+
const parent = { namespace, name };
|
|
240
|
+
return [
|
|
241
|
+
materializedViewObject({
|
|
242
|
+
namespace,
|
|
243
|
+
name,
|
|
244
|
+
columns,
|
|
245
|
+
query,
|
|
246
|
+
provenance,
|
|
247
|
+
...options.refresh === "concurrently" ? { refresh: "concurrently" } : {}
|
|
248
|
+
}),
|
|
249
|
+
...indexes.map((index) => materializedViewIndex({
|
|
250
|
+
parent,
|
|
251
|
+
...index.name !== undefined ? { name: index.name } : {},
|
|
252
|
+
columns: index.columns,
|
|
253
|
+
...index.unique === true ? { unique: true } : {},
|
|
254
|
+
provenance
|
|
255
|
+
}))
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function bodyText(body) {
|
|
261
|
+
return typeof body === "string" ? body : body.text;
|
|
262
|
+
}
|
|
263
|
+
function assertPeers(peers) {
|
|
264
|
+
if (!Array.isArray(peers)) {
|
|
265
|
+
throw new OkmError2("OKM1020", "schema({ views }) must be a list.", {
|
|
266
|
+
fix: { summary: "Pass an array to schema({ views })." }
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function assertNoTable(built, name) {
|
|
271
|
+
if (!Array.isArray(built))
|
|
272
|
+
return;
|
|
273
|
+
for (const object of built) {
|
|
274
|
+
if (!isRecord(object) || object.kind !== "table" || !isRecord(object.identity))
|
|
275
|
+
continue;
|
|
276
|
+
if (object.identity.name === name) {
|
|
277
|
+
throw new OkmError2("OKM1023", `View ${name} uses the name of a table.`, {
|
|
278
|
+
fix: { summary: "Give the view a name that no table uses." }
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function isRecord(value) {
|
|
284
|
+
return typeof value === "object" && value !== null;
|
|
285
|
+
}
|
|
286
|
+
export {
|
|
287
|
+
materializedView,
|
|
288
|
+
view
|
|
289
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies declared views on a scratch schema.
|
|
3
|
+
*
|
|
4
|
+
* Tables from the catalog are created there, then each view. `pg_get_viewdef`
|
|
5
|
+
* replaces the author query and `pg_depend` supplies column edges (D117).
|
|
6
|
+
* The scratch schema is dropped before this returns. Plans never use it.
|
|
7
|
+
*/
|
|
8
|
+
import type { Catalog } from "../../../contracts/catalog/types.js";
|
|
9
|
+
import type { CatalogQuery } from "../introspect.js";
|
|
10
|
+
/**
|
|
11
|
+
* Reads dependencies and reprints for every view in `source`.
|
|
12
|
+
*
|
|
13
|
+
* The returned catalog matches what introspection prints for those views
|
|
14
|
+
* when the same query is applied. A declared column list that disagrees
|
|
15
|
+
* with the database is OKM1020.
|
|
16
|
+
*
|
|
17
|
+
* @param runner - Database connection. Statements share its session
|
|
18
|
+
* @param source - Catalog whose views are still author SQL
|
|
19
|
+
* @returns A catalog whose views carry the reprint and `pg_depend` edges
|
|
20
|
+
*/
|
|
21
|
+
export declare function sealViews(runner: CatalogQuery, source: Catalog): Promise<Catalog>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where-value types shared by the client and by presets.
|
|
3
|
+
*
|
|
4
|
+
* They sit in the dialect layer so `table({ presets })` can type a preset's
|
|
5
|
+
* `where` without importing the client types. `runtime/types.ts` re-exports
|
|
6
|
+
* the two names callers use.
|
|
7
|
+
*/
|
|
8
|
+
import type { Between, Compare, Containment, Eq, HasAnyKey, HasKey, InList, Matches, Trigram, Not, NotIn, Overlaps, Path, Pattern, RawPattern } from "./operators.js";
|
|
9
|
+
import type { Range } from "./range.js";
|
|
10
|
+
export type IsObject<V> = V extends object ? V extends readonly unknown[] ? false : null extends V ? false : true : false;
|
|
11
|
+
/** Text patterns, including substring `contains`. `matches` is for tsvector, which is also `string`. */
|
|
12
|
+
type TextOps = Pattern<"startsWith"> | Pattern<"contains"> | Pattern<"endsWith"> | RawPattern<"like"> | RawPattern<"ilike"> | Matches | Trigram<"similar"> | Trigram<"wordSimilar">;
|
|
13
|
+
/** Array, jsonb, or range containment, plus overlap where that operator fits. */
|
|
14
|
+
type StructuredOps<V> = Containment<"contains" | "containedBy", V> | Overlaps<V>;
|
|
15
|
+
/** A JSON fragment. Containment matches part of a document, not the whole column type. */
|
|
16
|
+
type JsonFragment = string | number | boolean | null | readonly JsonFragment[] | {
|
|
17
|
+
readonly [key: string]: JsonFragment;
|
|
18
|
+
};
|
|
19
|
+
/** jsonb and json filters. `hasKey` is rejected at runtime on json. */
|
|
20
|
+
type JsonOps = Containment<"contains" | "containedBy", JsonFragment> | HasKey | HasAnyKey | Path<string> | Path<number> | Path<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* A plain object that can be json or jsonb.
|
|
23
|
+
*
|
|
24
|
+
* Arrays, ranges, and bytea are not json. Timestamp values are objects too, and
|
|
25
|
+
* the Temporal declarations are structural, so a timestamp column is rejected
|
|
26
|
+
* at runtime (OKM1124) rather than here.
|
|
27
|
+
*/
|
|
28
|
+
export type IsJsonObject<V> = V extends readonly unknown[] ? false : V extends Range<unknown> ? false : V extends Uint8Array ? false : IsObject<V>;
|
|
29
|
+
/** Operators the column value type can accept. `unknown` keeps every family and the runtime decides. */
|
|
30
|
+
type TypedOps<V> = [unknown] extends [V] ? TextOps | StructuredOps<V> | JsonOps : (V extends string ? TextOps : never) | (V extends readonly unknown[] ? StructuredOps<V> : never) | (V extends Range<unknown> ? StructuredOps<V> : never) | (IsJsonObject<V> extends true ? JsonOps : never);
|
|
31
|
+
/** A where operand. Object columns take `eq`, not a bare object. */
|
|
32
|
+
export type WhereValue<V> = V extends unknown ? (IsObject<V> extends true ? never : V) | null | Eq<V> | Compare<"lt", V> | Compare<"lte", V> | Compare<"gt", V> | Compare<"gte", V> | Between<V> | InList<V> | NotIn<V> | Not<V | null | TextOps | InList<V>> | TypedOps<V> : never;
|
|
33
|
+
/** Field filters. Relation filters are one level, so the type does not cycle. */
|
|
34
|
+
export type FieldWhere<Row> = {
|
|
35
|
+
readonly [K in keyof Row]?: WhereValue<Row[K]> | undefined;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grouped aggregates (spec section 10).
|
|
3
|
+
*
|
|
4
|
+
* `count`, `sum`, `avg`, `min`, and `max` over the rows a `where` selects, per
|
|
5
|
+
* `groupBy` group. The statement is written here and the `where` is written by
|
|
6
|
+
* the planner's `emitWhere`, so the tenant and active-set predicates apply to
|
|
7
|
+
* every aggregate exactly as they apply to a `find`. This module loads on the
|
|
8
|
+
* first `aggregate()`.
|
|
9
|
+
*
|
|
10
|
+
* Not here: `having`, `bucket`, and ordering by an aggregate. A hidden column
|
|
11
|
+
* cannot be grouped or aggregated, so an aggregate never shows what a read hides.
|
|
12
|
+
*/
|
|
13
|
+
import { type Mods, type Session } from "./client.js";
|
|
14
|
+
import { type ArchiveView } from "./plan.js";
|
|
15
|
+
/**
|
|
16
|
+
* Builds the handle for one `aggregate()` call.
|
|
17
|
+
*
|
|
18
|
+
* @param session - Client session
|
|
19
|
+
* @param table - Table name
|
|
20
|
+
* @param options - `where`, `groupBy`, the aggregates, `orderBy`, and `limit`
|
|
21
|
+
* @param mods - `.all` reason, signal, and timeout
|
|
22
|
+
* @param view - Archive visibility
|
|
23
|
+
* @returns A handle that resolves to the grouped rows
|
|
24
|
+
*/
|
|
25
|
+
export declare function build(session: Session, table: string, options: object, mods: Mods, view: ArchiveView | undefined): Promise<unknown> & Record<string, unknown>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table methods `archivable()` adds through the shared client hook.
|
|
3
|
+
*
|
|
4
|
+
* Core does not name `archive` or `restore`. A failed import of the statement
|
|
5
|
+
* chunk rejects the call. The active-set predicate stays in the planner.
|
|
6
|
+
*/
|
|
7
|
+
import type { SchemaHookCtx } from "../dialects/pg/model.js";
|
|
8
|
+
/**
|
|
9
|
+
* Adds `archive`, `restore`, `withArchived`, and `onlyArchived` to one table.
|
|
10
|
+
*
|
|
11
|
+
* A table that is not archivable still receives the methods when the schema
|
|
12
|
+
* imported the trait, and a call throws OKM1052. The throw for a missing model
|
|
13
|
+
* is repeated by the statement chunk if execution reaches it.
|
|
14
|
+
*
|
|
15
|
+
* @param target - The table handle being built
|
|
16
|
+
* @param ctx - Table name, visibility, and the session
|
|
17
|
+
*/
|
|
18
|
+
export declare function attachArchive(target: Record<string, unknown>, ctx: SchemaHookCtx): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phrases the archive safety rule reads.
|
|
3
|
+
*
|
|
4
|
+
* Installed by `archivable()`. A schema that does not use the trait does not
|
|
5
|
+
* load this file, and reads record nothing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Installs the phrase recorder on the planner.
|
|
9
|
+
*
|
|
10
|
+
* Safe to call more than once. The recorder replaces the previous one.
|
|
11
|
+
*/
|
|
12
|
+
export declare function installArchiveContributions(): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `archive` and `restore`.
|
|
3
|
+
*
|
|
4
|
+
* Loaded on the first call. A failed import fails that call. The active-set
|
|
5
|
+
* predicate stays in the planner, so a failed import does not show archived rows.
|
|
6
|
+
*/
|
|
7
|
+
import type { QuerySchema } from "../dialects/pg/model.js";
|
|
8
|
+
import { type CallScope } from "./plan.js";
|
|
9
|
+
import { type RunHost } from "./tx.js";
|
|
10
|
+
import type { PreparedWrite } from "./write.js";
|
|
11
|
+
type LifecycleOp = "archive" | "restore";
|
|
12
|
+
type Mods = {
|
|
13
|
+
readonly all?: string;
|
|
14
|
+
readonly expect?: number;
|
|
15
|
+
/** Filters the chained presets added. The archive handle resolves them. */
|
|
16
|
+
readonly presets?: readonly unknown[];
|
|
17
|
+
};
|
|
18
|
+
type Host = RunHost & {
|
|
19
|
+
readonly schema: QuerySchema;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Archives or restores matching rows.
|
|
23
|
+
*
|
|
24
|
+
* One statement. Cascaded children share the `archiveId`. A restore whose
|
|
25
|
+
* parent is still archived changes nothing and fails.
|
|
26
|
+
*
|
|
27
|
+
* @param host - Schema and pool
|
|
28
|
+
* @param op - `archive` or `restore`
|
|
29
|
+
* @param table - Table name
|
|
30
|
+
* @param input - `{ where }` or, for restore, `{ archiveId }`
|
|
31
|
+
* @param options - `expect`, signal, and timeout
|
|
32
|
+
* @param mods - `.all` and `.expect`
|
|
33
|
+
* @param scope - Tenant value, when the schema has tenancy
|
|
34
|
+
* @returns `{ count, archiveId }` or `{ count }`
|
|
35
|
+
*/
|
|
36
|
+
export declare function executeArchive(host: Host, op: LifecycleOp, table: string, input: unknown, options: object, mods: Mods, scope: CallScope | undefined): Promise<{
|
|
37
|
+
readonly count: number;
|
|
38
|
+
readonly archiveId?: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Plans archive or restore and returns its statement and the function that decodes the result.
|
|
42
|
+
*
|
|
43
|
+
* `batch` runs the statement with those of other operations.
|
|
44
|
+
*
|
|
45
|
+
* @param schema - Connected schema
|
|
46
|
+
* @param op - `archive` or `restore`
|
|
47
|
+
* @param table - Table name
|
|
48
|
+
* @param input - `{ where }` or `{ archiveId }`
|
|
49
|
+
* @param options - `expect`, signal, and timeout
|
|
50
|
+
* @param mods - `.all` and `.expect`
|
|
51
|
+
* @param scope - Tenant value, when the schema has tenancy
|
|
52
|
+
* @returns The statement, the call options, and `finish`
|
|
53
|
+
*/
|
|
54
|
+
export declare function prepareArchive(schema: QuerySchema, op: LifecycleOp, table: string, input: unknown, options: object, mods: Mods, scope: CallScope | undefined): PreparedWrite;
|
|
55
|
+
/**
|
|
56
|
+
* Plans archive or restore and does not run it.
|
|
57
|
+
*
|
|
58
|
+
* @param schema - Connected schema
|
|
59
|
+
* @param op - `archive` or `restore`
|
|
60
|
+
* @param table - Table name
|
|
61
|
+
* @param input - `{ where }` or `{ archiveId }`
|
|
62
|
+
* @param options - `expect`, signal, and timeout
|
|
63
|
+
* @param mods - `.all` and `.expect`
|
|
64
|
+
* @param scope - Tenant value, when the schema has tenancy
|
|
65
|
+
* @returns The statement
|
|
66
|
+
*/
|
|
67
|
+
export declare function explainArchive(schema: QuerySchema, op: LifecycleOp, table: string, input: unknown, options: object, mods: Mods, scope: CallScope | undefined): {
|
|
68
|
+
readonly statements: readonly {
|
|
69
|
+
readonly text: string;
|
|
70
|
+
readonly params: readonly (string | null)[];
|
|
71
|
+
}[];
|
|
72
|
+
};
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public `batch` (spec §15, batch contract).
|
|
3
|
+
*
|
|
4
|
+
* Plans each operation, joins their statements, and runs them as one atomic
|
|
5
|
+
* unit on the primary. Results come back in the order given. A failure rolls
|
|
6
|
+
* everything back and the error carries `batchIndex`, the failing operation,
|
|
7
|
+
* or `null` when the commit failed. Inside `tx()` the unit is a savepoint, so
|
|
8
|
+
* the transaction survives. Loaded the first time a batch is awaited.
|
|
9
|
+
*/
|
|
10
|
+
import { type Session } from "./client.js";
|
|
11
|
+
/**
|
|
12
|
+
* Runs write handles as one atomic unit.
|
|
13
|
+
*
|
|
14
|
+
* @param session - Client session
|
|
15
|
+
* @param ops - Write handles: `insert`, `update`, `delete`, `archive`. A `restore`, or a write with `expect`, is OKM1121 (D183)
|
|
16
|
+
* @param options - `signal` and `timeout` for the whole batch
|
|
17
|
+
* @param replica - Set by `.replica()`. A batch needs the primary (OKM1840)
|
|
18
|
+
* @returns One result per operation, in order
|
|
19
|
+
*/
|
|
20
|
+
export declare function batch(session: Session, ops: unknown, options: object | undefined, replica: true | undefined): Promise<readonly unknown[]>;
|
package/dist/runtime/client.d.ts
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* The plan cache is per client and bounded.
|
|
6
6
|
*/
|
|
7
7
|
import type { DriverPool } from "../contracts/driver.js";
|
|
8
|
-
import {
|
|
9
|
-
import type
|
|
10
|
-
import {
|
|
11
|
-
import type
|
|
12
|
-
type
|
|
8
|
+
import type { IdGenerators } from "../contracts/generator.js";
|
|
9
|
+
import { OkmError, type ErrorStatuses, type OkmErrorOptions } from "../contracts/error.js";
|
|
10
|
+
import type { PresetUse, QuerySchema } from "../dialects/pg/model.js";
|
|
11
|
+
import { type ArchiveView, type Plan, type ReadCall, type CallScope, type ReadOp } from "./plan.js";
|
|
12
|
+
import type { CatalogArtifact, Connected, Hookm, Timeouts } from "./types.js";
|
|
13
|
+
/** A client's connection state. The lazy read modules take it. */
|
|
14
|
+
export type Session = {
|
|
13
15
|
readonly schema: QuerySchema;
|
|
14
16
|
readonly pool: DriverPool;
|
|
15
17
|
readonly ownsPool: boolean;
|
|
@@ -23,6 +25,29 @@ type Session = {
|
|
|
23
25
|
} | undefined;
|
|
24
26
|
readonly connected: Promise<void>;
|
|
25
27
|
readonly cache: Map<string, Plan>;
|
|
28
|
+
readonly generators: IdGenerators | undefined;
|
|
29
|
+
/** Ceilings from `connect({ timeouts })`. */
|
|
30
|
+
readonly timeouts: Timeouts | undefined;
|
|
31
|
+
/** Observers from `connect({ hookm })`. */
|
|
32
|
+
readonly hookm: readonly Hookm[] | undefined;
|
|
33
|
+
/** Set on the client a transaction hands out. `extras` are the methods only that client has. */
|
|
34
|
+
readonly tx?: {
|
|
35
|
+
readonly extras: object;
|
|
36
|
+
readonly depth: number;
|
|
37
|
+
};
|
|
38
|
+
/** Tenant value or an unscoped reason. Absent on the root client. */
|
|
39
|
+
readonly scope?: CallScope;
|
|
40
|
+
/** Shared close. `for()` and `unscoped()` reuse the same promise. */
|
|
41
|
+
readonly closing: {
|
|
42
|
+
current: Promise<void> | undefined;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/** Per-call modifiers of a read. */
|
|
46
|
+
export type Mods = {
|
|
47
|
+
readonly all?: string;
|
|
48
|
+
readonly required?: boolean;
|
|
49
|
+
/** Presets chained on the handle. The presets module resolves them on first use. */
|
|
50
|
+
readonly uses?: PresetUse | undefined;
|
|
26
51
|
};
|
|
27
52
|
/**
|
|
28
53
|
* Builds a client for one pool.
|
|
@@ -40,8 +65,92 @@ export declare function createClient<S extends QuerySchema>(schema: S, pool: Dri
|
|
|
40
65
|
readonly logger?: Session["logger"] | undefined;
|
|
41
66
|
readonly signal?: AbortSignal | undefined;
|
|
42
67
|
readonly timeout?: number | undefined;
|
|
68
|
+
readonly timeouts?: Timeouts | undefined;
|
|
69
|
+
readonly hookm?: readonly Hookm[] | undefined;
|
|
43
70
|
readonly catalog?: CatalogArtifact | undefined;
|
|
44
71
|
readonly catalogDir?: string | undefined;
|
|
45
72
|
readonly requireMeta?: boolean | undefined;
|
|
73
|
+
readonly generators?: IdGenerators | undefined;
|
|
46
74
|
}): Connected<S>;
|
|
75
|
+
export declare function openClient<S extends QuerySchema>(session: Session): Connected<S>;
|
|
76
|
+
/**
|
|
77
|
+
* A query handle that starts its work once, the first time it is awaited.
|
|
78
|
+
*
|
|
79
|
+
* @param run - The work. Called at most once
|
|
80
|
+
* @param extra - Methods that stay on the handle beside `then`
|
|
81
|
+
* @returns The handle
|
|
82
|
+
*/
|
|
83
|
+
export declare function queryHandle<T extends object>(run: () => Promise<unknown>, extra: T): Promise<unknown> & T;
|
|
84
|
+
/** A bound plan and its wire parameters. */
|
|
85
|
+
type Prepared = {
|
|
86
|
+
readonly plan: Plan;
|
|
87
|
+
readonly params: readonly (string | null)[];
|
|
88
|
+
};
|
|
89
|
+
/** Rows as the driver returns them. */
|
|
90
|
+
type Rows = readonly (readonly (string | null)[])[];
|
|
91
|
+
/**
|
|
92
|
+
* Decodes the rows of a prepared read, with includes when the plan has them.
|
|
93
|
+
*
|
|
94
|
+
* @param plan - Compiled plan
|
|
95
|
+
* @param rows - Wire rows
|
|
96
|
+
* @param table - Table name, for `not_unique`
|
|
97
|
+
* @returns Rows, one row or null, a count, or a boolean
|
|
98
|
+
*/
|
|
99
|
+
export declare function decodeRows(plan: Plan, rows: Rows, table: string): Promise<unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* A lazy read handle for one planned call.
|
|
102
|
+
*
|
|
103
|
+
* `find`, `one`, `count`, and `exists` use it, and so do the lazy `page` and
|
|
104
|
+
* `aggregate` modules. It plans once, caches the plan per client, maps the
|
|
105
|
+
* error, and offers `inspect` and `sql`.
|
|
106
|
+
*
|
|
107
|
+
* @param session - Client session
|
|
108
|
+
* @param table - Table name
|
|
109
|
+
* @param call - The read
|
|
110
|
+
* @param mods - Signal and timeout
|
|
111
|
+
* @param decode - Turns the wire rows into the result
|
|
112
|
+
* @param extra - Methods that belong to this read, given the memoised prepare
|
|
113
|
+
* @returns The handle
|
|
114
|
+
*/
|
|
115
|
+
export declare function readHandle(session: Session, table: string, call: ReadCall, mods: Mods, decode: (plan: Plan, rows: Rows) => unknown, extra?: (prepare: () => Promise<Prepared>) => Record<string, unknown>): Promise<unknown> & Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* Builds the logical read from caller options.
|
|
118
|
+
*
|
|
119
|
+
* @param op - Read kind
|
|
120
|
+
* @param table - Table name
|
|
121
|
+
* @param options - `where`, `select`, `orderBy`, `limit`, `include`
|
|
122
|
+
* @param all - Reason given to `.all`
|
|
123
|
+
* @param scope - Tenant scope, when the client has one
|
|
124
|
+
* @param model - Table model
|
|
125
|
+
* @param schema - Connected schema
|
|
126
|
+
* @param view - Archive visibility
|
|
127
|
+
* @returns The call the planner binds
|
|
128
|
+
*/
|
|
129
|
+
export declare function readCall(op: ReadOp, table: string, options: object, all: string | undefined, scope: CallScope | undefined, model: QuerySchema["model"][string] | undefined, schema: QuerySchema, view?: ArchiveView): ReadCall;
|
|
130
|
+
/**
|
|
131
|
+
* Attaches connect's HTTP statuses to options for one {@link OkmError}.
|
|
132
|
+
*
|
|
133
|
+
* @param http - Statuses from `connect({ errors })`
|
|
134
|
+
* @param options - Error options that do not yet name a status map
|
|
135
|
+
* @returns Options `toHttp()` can read
|
|
136
|
+
*/
|
|
137
|
+
export declare function withHttp(http: ErrorStatuses | undefined, options: OkmErrorOptions): OkmErrorOptions;
|
|
138
|
+
/**
|
|
139
|
+
* Copies `error` so {@link OkmError.toHttp} uses the statuses from `connect`.
|
|
140
|
+
*
|
|
141
|
+
* @param http - Statuses from `connect({ errors })`. Omitted, `error` is returned
|
|
142
|
+
* @param error - Failure that did not carry those statuses
|
|
143
|
+
* @returns The error `toHttp()` reads
|
|
144
|
+
*/
|
|
145
|
+
export declare function attachHttp(http: ErrorStatuses | undefined, error: OkmError): OkmError;
|
|
146
|
+
/**
|
|
147
|
+
* Maps a driver or query error and records it on the session logger.
|
|
148
|
+
*
|
|
149
|
+
* The archive hook uses this so a failed statement stays an `OkmError`.
|
|
150
|
+
*
|
|
151
|
+
* @param session - Pool, statuses, and logger
|
|
152
|
+
* @param error - Rejection from the driver or the statement chunk
|
|
153
|
+
* @returns Never. The mapped error is thrown
|
|
154
|
+
*/
|
|
155
|
+
export declare function settleCall(session: Session, error: unknown): Promise<never>;
|
|
47
156
|
export {};
|
|
@@ -17,9 +17,10 @@ export type ConflictPlan = {
|
|
|
17
17
|
*
|
|
18
18
|
* @param table - Target table
|
|
19
19
|
* @param value - The option the caller passed
|
|
20
|
+
* @param allow - Guarded fields this write may set
|
|
20
21
|
* @returns The plan, or `undefined` for the default `"error"`
|
|
21
22
|
*/
|
|
22
|
-
export declare function readConflict(table: Indexed, value: unknown): ConflictPlan | undefined;
|
|
23
|
+
export declare function readConflict(table: Indexed, value: unknown, allow?: ReadonlySet<string>): ConflictPlan | undefined;
|
|
23
24
|
/**
|
|
24
25
|
* Appends the conflict clause, or wraps a first-or-create insert.
|
|
25
26
|
*
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction for hidden and sensitive fields.
|
|
3
|
+
*
|
|
4
|
+
* Loaded the first time a concealed table is inspected or a read of one fails.
|
|
5
|
+
*/
|
|
6
|
+
import { OkmError } from "../contracts/error.js";
|
|
7
|
+
import type { QuerySchema } from "../dialects/pg/model.js";
|
|
8
|
+
import type { ReadCall } from "./plan.js";
|
|
9
|
+
import type { Inspection } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Replaces concealed values in an inspection and records the exposure rules.
|
|
12
|
+
*
|
|
13
|
+
* @param schema - Connected schema
|
|
14
|
+
* @param view - Inspection that still holds the real values
|
|
15
|
+
* @param call - The read, used to encode parameter values and to see `select`
|
|
16
|
+
* @returns The inspection a caller can print
|
|
17
|
+
*/
|
|
18
|
+
export declare function redactView(schema: QuerySchema, view: Inspection, call: ReadCall): Inspection;
|
|
19
|
+
/**
|
|
20
|
+
* Removes concealed values from a read error.
|
|
21
|
+
*
|
|
22
|
+
* @param error - Failure from the driver or the planner
|
|
23
|
+
* @param schema - Connected schema
|
|
24
|
+
* @param call - The read whose hidden or sensitive values may have been echoed
|
|
25
|
+
* @returns The error, with those values replaced
|
|
26
|
+
*/
|
|
27
|
+
export declare function scrubCall(error: OkmError, schema: QuerySchema, call: ReadCall): OkmError;
|