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.
Files changed (228) hide show
  1. package/README.md +727 -37
  2. package/dist/adapters/capabilities.d.ts +34 -0
  3. package/dist/adapters/pg/bunsql-extra.d.ts +17 -0
  4. package/dist/adapters/pg/bunsql.d.ts +60 -0
  5. package/dist/adapters/pg/call.d.ts +2 -12
  6. package/dist/adapters/pg/nodepostgres-extra.d.ts +18 -0
  7. package/dist/adapters/pg/nodepostgres.d.ts +57 -0
  8. package/dist/adapters/pg/postgres-extra.d.ts +50 -1
  9. package/dist/adapters/pg/postgresjs.d.ts +0 -4
  10. package/dist/adapters/pg/session.d.ts +94 -0
  11. package/dist/adapters/pg/watch.d.ts +18 -0
  12. package/dist/contracts/catalog/enum.d.ts +31 -4
  13. package/dist/contracts/catalog/extension.d.ts +61 -0
  14. package/dist/contracts/catalog/object.d.ts +1 -0
  15. package/dist/contracts/catalog/privilege.d.ts +64 -0
  16. package/dist/contracts/catalog/routine.d.ts +56 -0
  17. package/dist/contracts/catalog/types.d.ts +179 -18
  18. package/dist/contracts/catalog/view.d.ts +73 -0
  19. package/dist/contracts/connection.d.ts +13 -0
  20. package/dist/contracts/driver.d.ts +18 -5
  21. package/dist/contracts/error.d.ts +17 -1
  22. package/dist/contracts/generator.d.ts +63 -0
  23. package/dist/contracts/index.d.ts +2 -2
  24. package/dist/contracts/index.js +1 -1
  25. package/dist/contracts/internal.js +11 -9
  26. package/dist/contracts/location.d.ts +24 -0
  27. package/dist/contracts/rows.d.ts +15 -0
  28. package/dist/dialects/pg/archive-bind.d.ts +22 -0
  29. package/dist/dialects/pg/column.d.ts +129 -10
  30. package/dist/dialects/pg/compile.d.ts +1 -0
  31. package/dist/dialects/pg/custom.d.ts +6 -0
  32. package/dist/dialects/pg/ddl.d.ts +65 -1
  33. package/dist/dialects/pg/enum.d.ts +10 -8
  34. package/dist/dialects/pg/ext/citext.d.ts +14 -0
  35. package/dist/dialects/pg/ext/citext.js +11 -0
  36. package/dist/dialects/pg/ext/index.d.ts +52 -0
  37. package/dist/dialects/pg/ext/index.js +6 -0
  38. package/dist/dialects/pg/ext/pg-trgm.d.ts +53 -0
  39. package/dist/dialects/pg/ext/pg-trgm.js +42 -0
  40. package/dist/dialects/pg/filter-parse.d.ts +19 -0
  41. package/dist/dialects/pg/filters.d.ts +19 -0
  42. package/dist/dialects/pg/fn/index.d.ts +107 -0
  43. package/dist/dialects/pg/fn/index.js +175 -0
  44. package/dist/dialects/pg/fn/touch.d.ts +14 -0
  45. package/dist/dialects/pg/index.d.ts +11 -2
  46. package/dist/dialects/pg/index.js +243 -1062
  47. package/dist/dialects/pg/introspect.d.ts +10 -1
  48. package/dist/dialects/pg/keys.d.ts +19 -3
  49. package/dist/dialects/pg/model.d.ts +223 -2
  50. package/dist/dialects/pg/operator-fit.d.ts +86 -0
  51. package/dist/dialects/pg/operators.d.ts +46 -1
  52. package/dist/dialects/pg/ops/arr-append.d.ts +9 -0
  53. package/dist/dialects/pg/ops/arr-ns.d.ts +7 -0
  54. package/dist/dialects/pg/ops/arr-remove.d.ts +9 -0
  55. package/dist/dialects/pg/ops/containedBy.d.ts +9 -0
  56. package/dist/dialects/pg/ops/contains.d.ts +8 -4
  57. package/dist/dialects/pg/ops/hasAnyKey.d.ts +8 -0
  58. package/dist/dialects/pg/ops/hasKey.d.ts +8 -0
  59. package/dist/dialects/pg/ops/json-ns.d.ts +6 -0
  60. package/dist/dialects/pg/ops/json-set.d.ts +12 -0
  61. package/dist/dialects/pg/ops/matches.d.ts +16 -0
  62. package/dist/dialects/pg/ops/overlaps.d.ts +9 -0
  63. package/dist/dialects/pg/ops/path.d.ts +13 -0
  64. package/dist/dialects/pg/preset.d.ts +76 -0
  65. package/dist/dialects/pg/relations.d.ts +59 -2
  66. package/dist/dialects/pg/role/check.d.ts +45 -0
  67. package/dist/dialects/pg/role/index.d.ts +50 -0
  68. package/dist/dialects/pg/role/sql.d.ts +44 -0
  69. package/dist/dialects/pg/schema.d.ts +109 -5
  70. package/dist/dialects/pg/table.d.ts +137 -11
  71. package/dist/dialects/pg/temporal.d.ts +9 -7
  72. package/dist/dialects/pg/tenancy.d.ts +103 -0
  73. package/dist/dialects/pg/through.d.ts +21 -0
  74. package/dist/dialects/pg/trait.d.ts +141 -0
  75. package/dist/dialects/pg/view/client.d.ts +30 -0
  76. package/dist/dialects/pg/view/depend.d.ts +35 -0
  77. package/dist/dialects/pg/view/index.d.ts +114 -0
  78. package/dist/dialects/pg/view/index.js +289 -0
  79. package/dist/dialects/pg/view/scratch.d.ts +21 -0
  80. package/dist/dialects/pg/where.d.ts +37 -0
  81. package/dist/runtime/aggregate.d.ts +25 -0
  82. package/dist/runtime/archive-handle.d.ts +18 -0
  83. package/dist/runtime/archive-rules.d.ts +12 -0
  84. package/dist/runtime/archive.d.ts +73 -0
  85. package/dist/runtime/batch.d.ts +20 -0
  86. package/dist/runtime/client.d.ts +114 -5
  87. package/dist/runtime/conflict.d.ts +2 -1
  88. package/dist/runtime/exposure.d.ts +27 -0
  89. package/dist/runtime/hookm.d.ts +39 -0
  90. package/dist/runtime/ids/index.d.ts +11 -0
  91. package/dist/runtime/ids/index.js +193 -0
  92. package/dist/runtime/ids/okid-extended.d.ts +19 -0
  93. package/dist/runtime/ids/okid-generator.d.ts +21 -0
  94. package/dist/runtime/ids/okid-shared.d.ts +89 -0
  95. package/dist/runtime/ids/okid.d.ts +24 -0
  96. package/dist/runtime/ids/uuid.d.ts +21 -0
  97. package/dist/runtime/lock.d.ts +22 -0
  98. package/dist/runtime/operator-sql.d.ts +8 -0
  99. package/dist/runtime/page.d.ts +25 -0
  100. package/dist/runtime/pg/bun.d.ts +30 -0
  101. package/dist/runtime/pg/bun.js +52 -0
  102. package/dist/runtime/pg/pg.d.ts +29 -0
  103. package/dist/runtime/pg/pg.js +52 -0
  104. package/dist/runtime/pg/pglite.js +16 -17
  105. package/dist/runtime/pg/postgresjs.js +14 -24
  106. package/dist/runtime/plan.d.ts +146 -7
  107. package/dist/runtime/preset-stack.d.ts +17 -0
  108. package/dist/runtime/presets.d.ts +48 -0
  109. package/dist/runtime/read-stream.d.ts +26 -0
  110. package/dist/runtime/safety/index.d.ts +159 -0
  111. package/dist/runtime/safety/index.js +294 -0
  112. package/dist/runtime/safety-hook.d.ts +42 -0
  113. package/dist/runtime/server-check.d.ts +34 -0
  114. package/dist/runtime/tenancy/index.d.ts +37 -0
  115. package/dist/runtime/tenancy/index.js +368 -0
  116. package/dist/runtime/trait-read.d.ts +30 -0
  117. package/dist/runtime/traits/index.d.ts +133 -0
  118. package/dist/runtime/traits/index.js +646 -0
  119. package/dist/runtime/transaction.d.ts +19 -0
  120. package/dist/runtime/tx.d.ts +22 -7
  121. package/dist/runtime/types.d.ts +376 -27
  122. package/dist/runtime/validate/bind.d.ts +11 -0
  123. package/dist/runtime/validate/closed.d.ts +16 -0
  124. package/dist/runtime/validate/engine.d.ts +41 -0
  125. package/dist/runtime/validate/hook.d.ts +17 -0
  126. package/dist/runtime/validate/index.d.ts +21 -0
  127. package/dist/runtime/validate/index.js +175 -0
  128. package/dist/runtime/validate/load.d.ts +13 -0
  129. package/dist/runtime/validate/places.d.ts +41 -0
  130. package/dist/runtime/validate/rules.d.ts +108 -0
  131. package/dist/runtime/validate/surface.d.ts +102 -0
  132. package/dist/runtime/write.d.ts +30 -5
  133. package/dist/shared/01yr20s5.js +149 -0
  134. package/dist/shared/0jc3ffzf.js +154 -0
  135. package/dist/shared/0v1g424p.js +26 -0
  136. package/dist/shared/1g0drakv.js +225 -0
  137. package/dist/shared/{kprcwaha.js → 1tfdfvhh.js} +5 -3
  138. package/dist/shared/{05w44d8g.js → 2ssqrv66.js} +767 -715
  139. package/dist/shared/3ekj9x92.js +210 -0
  140. package/dist/shared/49ypnfwt.js +1122 -0
  141. package/dist/shared/4e4c6vq2.js +62 -0
  142. package/dist/shared/4m9fn52z.js +70 -0
  143. package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
  144. package/dist/shared/5nmdenb6.js +1229 -0
  145. package/dist/shared/672t5vym.js +12 -0
  146. package/dist/shared/6bazvqwm.js +86 -0
  147. package/dist/shared/6w5j844a.js +201 -0
  148. package/dist/shared/{fgqgsb5z.js → 75xkjv0t.js} +2 -2
  149. package/dist/shared/7fvtrkwr.js +84 -0
  150. package/dist/shared/7m88x3e9.js +45 -0
  151. package/dist/shared/{mtkwv8wx.js → 7p2wbz7f.js} +1 -1
  152. package/dist/shared/83eaefwr.js +10 -0
  153. package/dist/shared/94ez8kzh.js +19 -0
  154. package/dist/shared/96ps4g1z.js +32 -0
  155. package/dist/shared/{xqbhg6cn.js → 999ade2q.js} +95 -10
  156. package/dist/shared/9m80mkac.js +158 -0
  157. package/dist/shared/9x25fxhc.js +150 -0
  158. package/dist/shared/a5sxz52y.js +13 -0
  159. package/dist/shared/axhk06q1.js +156 -0
  160. package/dist/shared/{dynq16vb.js → b066g80h.js} +8 -6
  161. package/dist/shared/bqha5cg6.js +229 -0
  162. package/dist/shared/bryqk9k9.js +31 -0
  163. package/dist/shared/c8aghh05.js +442 -0
  164. package/dist/shared/c9z5g0fm.js +309 -0
  165. package/dist/shared/{m3ypagh5.js → dbjej1tw.js} +124 -15
  166. package/dist/shared/dyj86cmj.js +69 -0
  167. package/dist/shared/e6wg00wd.js +164 -0
  168. package/dist/shared/{hadrfb9h.js → edwqqryd.js} +17 -14
  169. package/dist/shared/{qk3hxfja.js → fptpmvxp.js} +5 -5
  170. package/dist/shared/fs0ex00p.js +76 -0
  171. package/dist/shared/{jjb7yht9.js → g4f5kcx9.js} +3 -3
  172. package/dist/shared/gjthds9j.js +48 -0
  173. package/dist/shared/hcmdtwgh.js +102 -0
  174. package/dist/shared/{g5fhqmwe.js → jkc6ws1p.js} +6 -56
  175. package/dist/shared/{yd1kzwn2.js → jns2wx4t.js} +55 -12
  176. package/dist/shared/k34e0xf2.js +450 -0
  177. package/dist/shared/k3ted12y.js +33 -0
  178. package/dist/shared/k7dwkhvq.js +31 -0
  179. package/dist/shared/{8b04d28c.js → kgnc8grt.js} +2 -2
  180. package/dist/shared/{hd293j9g.js → kyxb62mc.js} +14 -2
  181. package/dist/shared/m78s7898.js +376 -0
  182. package/dist/shared/m9tv7xn7.js +404 -0
  183. package/dist/shared/meck53jv.js +99 -0
  184. package/dist/shared/mt5wswjz.js +90 -0
  185. package/dist/shared/n0dgs0pb.js +75 -0
  186. package/dist/shared/n442h54c.js +14 -0
  187. package/dist/shared/n4jqhpqz.js +27 -0
  188. package/dist/shared/n7mcc1gk.js +132 -0
  189. package/dist/shared/ne02ng5c.js +68 -0
  190. package/dist/shared/{mkjmqjv4.js → nfzc5z0w.js} +2 -104
  191. package/dist/shared/nsva2a6b.js +42 -0
  192. package/dist/shared/p24x2334.js +195 -0
  193. package/dist/shared/qbxzvwwc.js +88 -0
  194. package/dist/shared/{w8samvk1.js → qybh48nd.js} +1 -1
  195. package/dist/shared/r6nwkxnt.js +86 -0
  196. package/dist/shared/{xy79j5hq.js → rrstp3kb.js} +25 -36
  197. package/dist/shared/rwmrejjz.js +26 -0
  198. package/dist/shared/{zhc5vmgf.js → s6gqfn5j.js} +2 -2
  199. package/dist/shared/{523ft3e8.js → scwxrpx2.js} +7 -5
  200. package/dist/shared/{j5cn2963.js → t4tbsy9h.js} +13 -64
  201. package/dist/shared/{y9m15dfv.js → t616ynfn.js} +544 -32
  202. package/dist/shared/{ngk8gj61.js → tbszx8mz.js} +15 -1
  203. package/dist/shared/th14ygv2.js +69 -0
  204. package/dist/shared/tmbdywx0.js +178 -0
  205. package/dist/shared/ty3v3hfk.js +64 -0
  206. package/dist/shared/wekze34d.js +246 -0
  207. package/dist/shared/x6wysh83.js +1132 -0
  208. package/dist/shared/{rgs0st28.js → xrfnq5k8.js} +237 -434
  209. package/dist/shared/{x5x59srr.js → xva8v7qk.js} +416 -83
  210. package/dist/shared/{dkn3y60a.js → xw1d69vd.js} +76 -112
  211. package/dist/shared/y005b761.js +112 -0
  212. package/dist/shared/ydaz1sv3.js +55 -0
  213. package/dist/shared/{p0cbr3g2.js → zwmqxb0g.js} +1 -1
  214. package/dist/tooling/cli.js +40 -12
  215. package/dist/tooling/migrate/apply.d.ts +16 -0
  216. package/dist/tooling/migrate/commands.d.ts +12 -1
  217. package/dist/tooling/migrate/config.d.ts +8 -0
  218. package/dist/tooling/migrate/doctor.d.ts +25 -0
  219. package/dist/tooling/migrate/drift.d.ts +19 -0
  220. package/dist/tooling/migrate/engine.d.ts +36 -0
  221. package/dist/tooling/migrate/ext.d.ts +17 -0
  222. package/dist/tooling/migrate/extensions.d.ts +51 -0
  223. package/dist/tooling/migrate/managed.d.ts +17 -0
  224. package/dist/tooling/migrate/plan.d.ts +14 -1
  225. package/dist/tooling/migrate/project.d.ts +25 -1
  226. package/dist/tooling/migrate/type-name.d.ts +19 -0
  227. package/package.json +61 -4
  228. 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[]>;
@@ -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 { type ErrorStatuses } from "../contracts/error.js";
9
- import type { QuerySchema } from "../dialects/pg/model.js";
10
- import { type Plan } from "./plan.js";
11
- import type { CatalogArtifact, Connected } from "./types.js";
12
- type Session = {
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;