okmodel 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +32 -16
  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/object.d.ts +1 -0
  13. package/dist/contracts/catalog/types.d.ts +15 -8
  14. package/dist/contracts/connection.d.ts +13 -0
  15. package/dist/contracts/driver.d.ts +18 -5
  16. package/dist/contracts/error.d.ts +17 -1
  17. package/dist/contracts/generator.d.ts +63 -0
  18. package/dist/contracts/index.d.ts +2 -2
  19. package/dist/contracts/index.js +1 -1
  20. package/dist/contracts/internal.js +11 -9
  21. package/dist/contracts/location.d.ts +24 -0
  22. package/dist/contracts/rows.d.ts +15 -0
  23. package/dist/dialects/pg/archive-bind.d.ts +22 -0
  24. package/dist/dialects/pg/column.d.ts +126 -9
  25. package/dist/dialects/pg/compile.d.ts +1 -0
  26. package/dist/dialects/pg/custom.d.ts +6 -0
  27. package/dist/dialects/pg/filter-parse.d.ts +19 -0
  28. package/dist/dialects/pg/filters.d.ts +19 -0
  29. package/dist/dialects/pg/index.d.ts +11 -2
  30. package/dist/dialects/pg/index.js +159 -1063
  31. package/dist/dialects/pg/keys.d.ts +19 -3
  32. package/dist/dialects/pg/model.d.ts +223 -2
  33. package/dist/dialects/pg/operator-fit.d.ts +86 -0
  34. package/dist/dialects/pg/operators.d.ts +39 -1
  35. package/dist/dialects/pg/ops/arr-append.d.ts +9 -0
  36. package/dist/dialects/pg/ops/arr-ns.d.ts +7 -0
  37. package/dist/dialects/pg/ops/arr-remove.d.ts +9 -0
  38. package/dist/dialects/pg/ops/containedBy.d.ts +9 -0
  39. package/dist/dialects/pg/ops/contains.d.ts +8 -4
  40. package/dist/dialects/pg/ops/hasAnyKey.d.ts +8 -0
  41. package/dist/dialects/pg/ops/hasKey.d.ts +8 -0
  42. package/dist/dialects/pg/ops/json-ns.d.ts +6 -0
  43. package/dist/dialects/pg/ops/json-set.d.ts +12 -0
  44. package/dist/dialects/pg/ops/matches.d.ts +16 -0
  45. package/dist/dialects/pg/ops/overlaps.d.ts +9 -0
  46. package/dist/dialects/pg/ops/path.d.ts +13 -0
  47. package/dist/dialects/pg/preset.d.ts +76 -0
  48. package/dist/dialects/pg/relations.d.ts +59 -2
  49. package/dist/dialects/pg/schema.d.ts +109 -5
  50. package/dist/dialects/pg/table.d.ts +130 -11
  51. package/dist/dialects/pg/temporal.d.ts +9 -7
  52. package/dist/dialects/pg/tenancy.d.ts +94 -0
  53. package/dist/dialects/pg/through.d.ts +21 -0
  54. package/dist/dialects/pg/trait.d.ts +141 -0
  55. package/dist/dialects/pg/where.d.ts +37 -0
  56. package/dist/runtime/aggregate.d.ts +25 -0
  57. package/dist/runtime/archive-handle.d.ts +18 -0
  58. package/dist/runtime/archive-rules.d.ts +12 -0
  59. package/dist/runtime/archive.d.ts +73 -0
  60. package/dist/runtime/batch.d.ts +20 -0
  61. package/dist/runtime/client.d.ts +114 -5
  62. package/dist/runtime/conflict.d.ts +2 -1
  63. package/dist/runtime/exposure.d.ts +27 -0
  64. package/dist/runtime/hookm.d.ts +39 -0
  65. package/dist/runtime/ids/index.d.ts +11 -0
  66. package/dist/runtime/ids/index.js +193 -0
  67. package/dist/runtime/ids/okid-extended.d.ts +19 -0
  68. package/dist/runtime/ids/okid-generator.d.ts +21 -0
  69. package/dist/runtime/ids/okid-shared.d.ts +89 -0
  70. package/dist/runtime/ids/okid.d.ts +24 -0
  71. package/dist/runtime/ids/uuid.d.ts +21 -0
  72. package/dist/runtime/lock.d.ts +22 -0
  73. package/dist/runtime/operator-sql.d.ts +8 -0
  74. package/dist/runtime/page.d.ts +25 -0
  75. package/dist/runtime/pg/bun.d.ts +30 -0
  76. package/dist/runtime/pg/bun.js +52 -0
  77. package/dist/runtime/pg/pg.d.ts +29 -0
  78. package/dist/runtime/pg/pg.js +52 -0
  79. package/dist/runtime/pg/pglite.js +16 -17
  80. package/dist/runtime/pg/postgresjs.js +14 -24
  81. package/dist/runtime/plan.d.ts +146 -7
  82. package/dist/runtime/preset-stack.d.ts +17 -0
  83. package/dist/runtime/presets.d.ts +48 -0
  84. package/dist/runtime/read-stream.d.ts +26 -0
  85. package/dist/runtime/safety/index.d.ts +159 -0
  86. package/dist/runtime/safety/index.js +294 -0
  87. package/dist/runtime/safety-hook.d.ts +42 -0
  88. package/dist/runtime/server-check.d.ts +34 -0
  89. package/dist/runtime/tenancy/index.d.ts +37 -0
  90. package/dist/runtime/tenancy/index.js +365 -0
  91. package/dist/runtime/trait-read.d.ts +30 -0
  92. package/dist/runtime/traits/index.d.ts +128 -0
  93. package/dist/runtime/traits/index.js +509 -0
  94. package/dist/runtime/transaction.d.ts +19 -0
  95. package/dist/runtime/tx.d.ts +22 -7
  96. package/dist/runtime/types.d.ts +376 -27
  97. package/dist/runtime/validate/bind.d.ts +11 -0
  98. package/dist/runtime/validate/closed.d.ts +16 -0
  99. package/dist/runtime/validate/engine.d.ts +41 -0
  100. package/dist/runtime/validate/hook.d.ts +17 -0
  101. package/dist/runtime/validate/index.d.ts +21 -0
  102. package/dist/runtime/validate/index.js +175 -0
  103. package/dist/runtime/validate/load.d.ts +13 -0
  104. package/dist/runtime/validate/places.d.ts +41 -0
  105. package/dist/runtime/validate/rules.d.ts +108 -0
  106. package/dist/runtime/validate/surface.d.ts +102 -0
  107. package/dist/runtime/write.d.ts +30 -5
  108. package/dist/shared/{fgqgsb5z.js → 0qkxbm1f.js} +2 -2
  109. package/dist/shared/0v1g424p.js +26 -0
  110. package/dist/shared/10tjenmd.js +210 -0
  111. package/dist/shared/{xqbhg6cn.js → 1dp5knt7.js} +95 -10
  112. package/dist/shared/38crxbgn.js +229 -0
  113. package/dist/shared/3a5ne750.js +158 -0
  114. package/dist/shared/4b9da9d7.js +138 -0
  115. package/dist/shared/4e4c6vq2.js +62 -0
  116. package/dist/shared/{x5x59srr.js → 4jzzghr6.js} +416 -83
  117. package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
  118. package/dist/shared/{05w44d8g.js → 5z4q2zda.js} +567 -18
  119. package/dist/shared/672t5vym.js +12 -0
  120. package/dist/shared/7m88x3e9.js +45 -0
  121. package/dist/shared/7mht49yd.js +1091 -0
  122. package/dist/shared/{mtkwv8wx.js → 7p2wbz7f.js} +1 -1
  123. package/dist/shared/83eaefwr.js +10 -0
  124. package/dist/shared/96ps4g1z.js +32 -0
  125. package/dist/shared/97g2jnrw.js +76 -0
  126. package/dist/shared/{xy79j5hq.js → 9k6vetvf.js} +25 -36
  127. package/dist/shared/{w8samvk1.js → a1gdp7j9.js} +1 -1
  128. package/dist/shared/a5sxz52y.js +13 -0
  129. package/dist/shared/{dynq16vb.js → b066g80h.js} +8 -6
  130. package/dist/shared/cstgkzdx.js +154 -0
  131. package/dist/shared/{523ft3e8.js → d04j53v8.js} +6 -5
  132. package/dist/shared/{y9m15dfv.js → d2whjz4k.js} +15 -9
  133. package/dist/shared/dd4f8mnx.js +307 -0
  134. package/dist/shared/e6wg00wd.js +164 -0
  135. package/dist/shared/e7a268fy.js +195 -0
  136. package/dist/shared/{kprcwaha.js → eaxwrgby.js} +4 -3
  137. package/dist/shared/{dkn3y60a.js → ecdzej21.js} +76 -112
  138. package/dist/shared/{hadrfb9h.js → edwqqryd.js} +17 -14
  139. package/dist/shared/{m3ypagh5.js → eqmtn1bj.js} +57 -5
  140. package/dist/shared/g1b5zzj9.js +75 -0
  141. package/dist/shared/g9f0zqce.js +376 -0
  142. package/dist/shared/ge03bfca.js +64 -0
  143. package/dist/shared/{yd1kzwn2.js → jns2wx4t.js} +55 -12
  144. package/dist/shared/k3ted12y.js +33 -0
  145. package/dist/shared/k7dwkhvq.js +31 -0
  146. package/dist/shared/{hd293j9g.js → kyxb62mc.js} +14 -2
  147. package/dist/shared/m9tv7xn7.js +404 -0
  148. package/dist/shared/mrq77sgw.js +55 -0
  149. package/dist/shared/mxmcdsh5.js +450 -0
  150. package/dist/shared/n442h54c.js +14 -0
  151. package/dist/shared/n47rd821.js +442 -0
  152. package/dist/shared/n4jqhpqz.js +27 -0
  153. package/dist/shared/{mkjmqjv4.js → nfzc5z0w.js} +2 -104
  154. package/dist/shared/nsva2a6b.js +42 -0
  155. package/dist/shared/ps43134h.js +69 -0
  156. package/dist/shared/{8b04d28c.js → py3nh7jy.js} +2 -2
  157. package/dist/shared/qa5dsc7z.js +86 -0
  158. package/dist/shared/{g5fhqmwe.js → qxa0yb9j.js} +6 -4
  159. package/dist/shared/r6nwkxnt.js +86 -0
  160. package/dist/shared/{jjb7yht9.js → rtnaqre0.js} +3 -3
  161. package/dist/shared/rwmrejjz.js +26 -0
  162. package/dist/shared/{zhc5vmgf.js → s6gqfn5j.js} +2 -2
  163. package/dist/shared/sacqha1y.js +70 -0
  164. package/dist/shared/{qk3hxfja.js → sfh4pk75.js} +5 -5
  165. package/dist/shared/{j5cn2963.js → t4tbsy9h.js} +13 -64
  166. package/dist/shared/{ngk8gj61.js → tbszx8mz.js} +15 -1
  167. package/dist/shared/v18jva5b.js +90 -0
  168. package/dist/shared/x9v1qgeb.js +201 -0
  169. package/dist/shared/xatfg0gn.js +193 -0
  170. package/dist/shared/xkz8g6by.js +225 -0
  171. package/dist/shared/{rgs0st28.js → xrfnq5k8.js} +237 -434
  172. package/dist/shared/ytg3xf9p.js +149 -0
  173. package/dist/shared/yw5c610b.js +48 -0
  174. package/dist/shared/zd93g3rq.js +31 -0
  175. package/dist/shared/zt8y0wzb.js +132 -0
  176. package/dist/shared/{p0cbr3g2.js → zwmqxb0g.js} +1 -1
  177. package/dist/tooling/cli.js +14 -10
  178. package/dist/tooling/migrate/commands.d.ts +10 -0
  179. package/dist/tooling/migrate/engine.d.ts +36 -0
  180. package/package.json +41 -4
  181. package/dist/shared/gk7cs7sp.js +0 -566
@@ -0,0 +1,509 @@
1
+ import {
2
+ checkPresetNames,
3
+ duplicatePreset
4
+ } from "../../shared/nsva2a6b.js";
5
+ import {
6
+ checkTrait
7
+ } from "../../shared/g1b5zzj9.js";
8
+ import {
9
+ timestamptz2
10
+ } from "../../shared/xkz8g6by.js";
11
+ import {
12
+ uuid2
13
+ } from "../../shared/ps43134h.js";
14
+ import {
15
+ snakeCase
16
+ } from "../../shared/x9v1qgeb.js";
17
+ import {
18
+ ColumnBuilder2,
19
+ retarget
20
+ } from "../../shared/dd4f8mnx.js";
21
+ import {
22
+ definition,
23
+ unavailable
24
+ } from "../../shared/xatfg0gn.js";
25
+ import {
26
+ compareText
27
+ } from "../../shared/t4tbsy9h.js";
28
+ import {
29
+ queryHandle,
30
+ attachHttp,
31
+ settleCall
32
+ } from "../../shared/mxmcdsh5.js";
33
+ import {
34
+ installArchiveInspect
35
+ } from "../../shared/xrfnq5k8.js";
36
+ import {
37
+ OkmError2,
38
+ catalogError2,
39
+ throwNamed2
40
+ } from "../../shared/52szzakm.js";
41
+
42
+ // src/dialects/pg/archive-bind.ts
43
+ function rewriteArchivable(self, tables, casing, schemaTraits, cascade) {
44
+ const byName = new Map(tables.map((item) => [item.name, item]));
45
+ const accepted = tables.map((item) => item.name);
46
+ const edges = foreignKeys(tables, casing, byName);
47
+ const archiveSql = casing === "snake" ? "archived_at" : "archivedAt";
48
+ const idSql = casing === "snake" ? "archive_id" : "archiveId";
49
+ const predicate = `"${archiveSql.replaceAll('"', '""')}" is null`;
50
+ return tables.map((item) => {
51
+ if (!applies(self, item, schemaTraits))
52
+ return item;
53
+ return rewriteOne(item, cascade, byName, accepted, edges, casing, schemaTraits, archiveSql, idSql, predicate);
54
+ });
55
+ }
56
+ function rewriteOne(item, cascade, byName, accepted, edges, casing, schemaTraits, archiveSql, idSql, predicate) {
57
+ const links = childLinks(item, cascade, byName, accepted, edges, casing, schemaTraits, archiveSql, idSql);
58
+ const parents = parentLinks(item, byName, edges, casing, schemaTraits, archiveSql, idSql);
59
+ const primary = primaryFields(item);
60
+ if ((links.length > 0 || parents.length > 0) && primary.length === 0) {
61
+ catalogError2("OKM1020", `Table ${item.name} is archivable and needs a primary key.`);
62
+ }
63
+ const options = item.options;
64
+ const partials = [];
65
+ const named = options?.unique;
66
+ if (named !== undefined) {
67
+ for (const fields of Object.values(named)) {
68
+ partials.push(partialIndex(fields.map((field) => fieldSql(field, item.columns[field], casing)), predicate));
69
+ }
70
+ }
71
+ let columns;
72
+ for (const [field, builder] of Object.entries(item.columns)) {
73
+ if (!(builder instanceof ColumnBuilder2) || builder.state.unique === undefined)
74
+ continue;
75
+ columns ??= { ...item.columns };
76
+ columns[field] = retarget(builder, { dropUnique: true });
77
+ partials.push(partialIndex([fieldSql(field, builder, casing)], predicate));
78
+ }
79
+ const prior = options?.indexes;
80
+ const indexes = partials.length === 0 ? undefined : (handles) => {
81
+ const calls = prior === undefined ? [] : prior(handles);
82
+ return [...partials, ...Array.isArray(calls) ? calls : []];
83
+ };
84
+ const archiveModel = {
85
+ at: archiveSql,
86
+ id: idSql,
87
+ atField: "archivedAt",
88
+ idField: "archiveId",
89
+ cascade: links,
90
+ parents
91
+ };
92
+ return {
93
+ ...item,
94
+ ...columns !== undefined ? { columns } : {},
95
+ ...named !== undefined || indexes !== undefined ? { options: nextOptions(item.options, indexes) } : {},
96
+ archiveModel
97
+ };
98
+ }
99
+ function childLinks(item, cascade, byName, accepted, edges, casing, schemaTraits, archiveSql, idSql) {
100
+ const seen = new Set;
101
+ const links = [];
102
+ for (const name of cascade) {
103
+ if (seen.has(name))
104
+ catalogError2("OKM1020", `Cascade ${item.name} names ${name} more than once.`);
105
+ seen.add(name);
106
+ if (name === item.name)
107
+ catalogError2("OKM1020", `Cascade ${item.name} names itself.`);
108
+ const child = byName.get(name);
109
+ if (child === undefined) {
110
+ throwNamed2("OKM1020", name, accepted, `Cascade ${item.name} names ${name}, which is not a table. Accepted names: ${list(accepted)}.`);
111
+ }
112
+ if (!isArchivable(child, schemaTraits)) {
113
+ catalogError2("OKM1020", `Cascade ${item.name} names ${name}, which is not archivable.`);
114
+ }
115
+ const matches = edges.filter((edge) => edge.from === name && edge.to === item.name);
116
+ if (matches.length !== 1) {
117
+ const hint = matches.map((edge) => edge.field).sort();
118
+ const which = hint.length === 0 ? "none" : hint.join(", ");
119
+ catalogError2("OKM1021", `Cascade ${item.name} names ${name}, which has ${matches.length === 0 ? "no" : "more than one"} foreign key to ${item.name}. Accepted columns: ${which}.`);
120
+ }
121
+ const edge = matches[0];
122
+ if (edge === undefined) {
123
+ catalogError2("OKM1021", `Cascade ${item.name} names ${name}, which has no foreign key to ${item.name}. Accepted columns: none.`);
124
+ }
125
+ links.push({
126
+ table: name,
127
+ sql: tableSql(child, casing),
128
+ at: archiveSql,
129
+ id: idSql,
130
+ local: edge.local,
131
+ remote: edge.remote
132
+ });
133
+ }
134
+ return links;
135
+ }
136
+ function parentLinks(item, byName, edges, casing, schemaTraits, archiveSql, idSql) {
137
+ const parents = [];
138
+ for (const edge of edges) {
139
+ if (edge.from !== item.name)
140
+ continue;
141
+ const parent = byName.get(edge.to);
142
+ if (parent === undefined || !isArchivable(parent, schemaTraits))
143
+ continue;
144
+ parents.push({
145
+ table: parent.name,
146
+ sql: tableSql(parent, casing),
147
+ at: archiveSql,
148
+ id: idSql,
149
+ local: edge.local,
150
+ remote: edge.remote
151
+ });
152
+ }
153
+ parents.sort((left, right) => compareText(left.table, right.table));
154
+ return parents;
155
+ }
156
+ function foreignKeys(tables, casing, byName) {
157
+ const edges = [];
158
+ for (const item of tables) {
159
+ for (const [field, builder] of Object.entries(item.columns)) {
160
+ if (!(builder instanceof ColumnBuilder2))
161
+ continue;
162
+ const reference = builder.state.references;
163
+ if (reference === undefined)
164
+ continue;
165
+ const target = byName.get(reference.table);
166
+ if (target === undefined)
167
+ continue;
168
+ edges.push({
169
+ from: item.name,
170
+ field,
171
+ to: reference.table,
172
+ local: localNames(item, field, builder, reference, casing),
173
+ remote: remoteNames(target, reference, casing)
174
+ });
175
+ }
176
+ }
177
+ return edges;
178
+ }
179
+ function localNames(item, field, builder, reference, casing) {
180
+ const names = [fieldSql(field, builder, casing)];
181
+ for (const extra of reference.along ?? [])
182
+ names.push(fieldSql(extra, item.columns[extra], casing));
183
+ return names;
184
+ }
185
+ function remoteNames(target, reference, casing) {
186
+ if (reference.columns !== undefined) {
187
+ return reference.columns.map((name) => fieldSql(name, target.columns[name], casing));
188
+ }
189
+ return primaryFields(target).map((name) => fieldSql(name, target.columns[name], casing));
190
+ }
191
+ function applies(self, item, schemaTraits) {
192
+ const options = item.options;
193
+ const own = Array.isArray(options?.traits) ? options.traits : [];
194
+ if (own.includes(self))
195
+ return true;
196
+ if (options?.omitDefaults !== undefined)
197
+ return false;
198
+ return schemaTraits?.includes(self) ?? false;
199
+ }
200
+ function isArchivable(item, schemaTraits) {
201
+ const options = item.options;
202
+ const own = Array.isArray(options?.traits) ? options.traits : [];
203
+ for (const trait of own)
204
+ if (marked(trait))
205
+ return true;
206
+ if (options?.omitDefaults !== undefined)
207
+ return false;
208
+ if (schemaTraits === undefined)
209
+ return false;
210
+ for (const trait of schemaTraits)
211
+ if (marked(trait))
212
+ return true;
213
+ return false;
214
+ }
215
+ function marked(value) {
216
+ return value["~archive"] === true;
217
+ }
218
+ function nextOptions(options, indexes) {
219
+ const record = { ...options };
220
+ delete record.unique;
221
+ if (indexes !== undefined)
222
+ record.indexes = indexes;
223
+ return record;
224
+ }
225
+ function partialIndex(columns, predicate) {
226
+ return {
227
+ columns,
228
+ isUnique: true,
229
+ predicate,
230
+ unique() {
231
+ return partialIndex(columns, predicate);
232
+ }
233
+ };
234
+ }
235
+ function primaryFields(item) {
236
+ const listed = item.options?.primaryKey;
237
+ if (listed !== undefined && listed.length > 0)
238
+ return [...listed];
239
+ const fields = [];
240
+ for (const [field, builder] of Object.entries(item.columns)) {
241
+ if (builder instanceof ColumnBuilder2 && builder.state.primaryKey === true)
242
+ fields.push(field);
243
+ }
244
+ return fields;
245
+ }
246
+ function fieldSql(field, builder, casing) {
247
+ if (builder instanceof ColumnBuilder2 && builder.state.sqlName !== undefined)
248
+ return builder.state.sqlName;
249
+ return casing === "snake" ? snakeCase(field) : field;
250
+ }
251
+ function tableSql(item, casing) {
252
+ const named = item.options?.sqlName;
253
+ return named ?? (casing === "snake" ? snakeCase(item.name) : item.name);
254
+ }
255
+ function list(names) {
256
+ return names.join(", ");
257
+ }
258
+
259
+ // src/runtime/archive-rules.ts
260
+ function installArchiveContributions() {
261
+ installArchiveInspect(archiveContributions);
262
+ }
263
+ function archiveContributions(model, view) {
264
+ const archive = model?.archive;
265
+ if (archive === undefined)
266
+ return;
267
+ const rules = [
268
+ view === "with" ? { rule: "archive", contribution: "with archived", provenance: "caller" } : view === "only" ? { rule: "archive", contribution: "only archived", provenance: "caller" } : { rule: "archive", contribution: "active set", provenance: "planner" }
269
+ ];
270
+ for (const child of archive.cascade) {
271
+ rules.push({
272
+ rule: "archive",
273
+ contribution: `cascade ${child.table}`,
274
+ provenance: "catalog"
275
+ });
276
+ }
277
+ return rules;
278
+ }
279
+
280
+ // src/runtime/archive-handle.ts
281
+ function attachArchive(target, ctx) {
282
+ const table = ctx.table;
283
+ const reopen = ctx.reopen;
284
+ const session = ctx.session;
285
+ if (table === undefined || reopen === undefined || session === undefined)
286
+ return;
287
+ const host = session;
288
+ const mods = { uses: ctx.uses };
289
+ target.archive = (input, options = {}) => lifecycle(host, "archive", table, input, options, mods);
290
+ target.restore = (input, options = {}) => lifecycle(host, "restore", table, input, options, mods);
291
+ target.withArchived = () => {
292
+ refuse(host, table);
293
+ return reopen("with");
294
+ };
295
+ target.onlyArchived = () => {
296
+ refuse(host, table);
297
+ return reopen("only");
298
+ };
299
+ }
300
+ function lifecycle(host, op, table, input, options, mods) {
301
+ refuse(host, table);
302
+ return queryHandle(() => run(host, op, table, input, options, mods), {
303
+ "~plan": async () => {
304
+ await host.connected;
305
+ const [mod, planned] = await Promise.all([loadArchive(), settle(host, table, mods)]);
306
+ try {
307
+ return mod.prepareArchive(host.schema, op, table, input, options, planned, host.scope);
308
+ } catch (error) {
309
+ return settleCall(host, error);
310
+ }
311
+ },
312
+ sql() {
313
+ return Promise.all([loadArchive(), settle(host, table, mods)]).then(([mod, planned]) => mod.explainArchive(host.schema, op, table, input, options, planned, host.scope));
314
+ },
315
+ expect(count) {
316
+ return lifecycle(host, op, table, input, options, { ...mods, expect: count });
317
+ },
318
+ all(reason) {
319
+ return lifecycle(host, op, table, input, options, { ...mods, all: reason });
320
+ }
321
+ });
322
+ }
323
+ function refuse(host, table) {
324
+ if (host.schema.model[table]?.archive !== undefined)
325
+ return;
326
+ throw attachHttp(host.http, new OkmError2("OKM1052", `Table ${table} is not archivable.`, {
327
+ fix: { summary: "Call archive and restore only on an archivable table." }
328
+ }));
329
+ }
330
+ var archiveMod;
331
+ function loadArchive() {
332
+ archiveMod ??= import("../../shared/m9tv7xn7.js");
333
+ return archiveMod;
334
+ }
335
+ async function settle(host, table, mods) {
336
+ if (mods.uses === undefined)
337
+ return mods;
338
+ const { resolve } = await import("../../shared/r6nwkxnt.js");
339
+ return { ...mods, presets: resolve(host.schema, table, mods.uses).wheres };
340
+ }
341
+ async function run(host, op, table, input, options, mods) {
342
+ await host.connected;
343
+ const mod = await loadArchive();
344
+ try {
345
+ return await mod.executeArchive(host, op, table, input, options, await settle(host, table, mods), host.scope);
346
+ } catch (error) {
347
+ return settleCall(host, error);
348
+ }
349
+ }
350
+
351
+ // src/runtime/traits/index.ts
352
+ function trait(name, input) {
353
+ if (input.presets !== undefined)
354
+ checkPresetNames(Object.keys(input.presets), `trait ${name}`);
355
+ if (input.methods !== undefined) {
356
+ unavailable("Trait option methods is not available yet. It arrives in 0.2.");
357
+ }
358
+ if (input.requires !== undefined) {
359
+ unavailable("Trait option requires is not available yet. It arrives in 0.2.");
360
+ }
361
+ const checked = checkTrait({
362
+ name,
363
+ fields: input.fields,
364
+ ...input.touch !== undefined ? { touch: input.touch } : {},
365
+ ...input.sealed !== undefined ? { sealed: input.sealed } : {}
366
+ }, `trait(${name})`);
367
+ markFields(checked.fields, name);
368
+ const sealed = sealTouch(checked.sealed, checked.touch);
369
+ return {
370
+ name,
371
+ fields: checked.fields,
372
+ ...checked.touch !== undefined ? { touch: checked.touch } : {},
373
+ ...sealed !== undefined ? { sealed } : {},
374
+ ...input.presets !== undefined ? { presets: input.presets } : {},
375
+ apply(model, ctx) {
376
+ addFields(name, checked.fields, model, ctx);
377
+ if (input.presets !== undefined)
378
+ addPresets(name, input.presets, model, ctx);
379
+ }
380
+ };
381
+ }
382
+ function addPresets(name, presets, model, ctx) {
383
+ const merged = model.presets = { ...model.presets };
384
+ const from = model.presetFrom ??= {};
385
+ for (const key of Object.keys(presets)) {
386
+ if (Object.hasOwn(merged, key)) {
387
+ const first = from[key];
388
+ duplicatePreset(key, ctx.table, first === undefined ? `table ${ctx.table}` : `trait ${first}`, `trait ${name}`);
389
+ }
390
+ merged[key] = presets[key];
391
+ from[key] = name;
392
+ }
393
+ }
394
+ function timestamps(options) {
395
+ if (options !== undefined) {
396
+ for (const key of Object.keys(options)) {
397
+ if (key !== "enforce") {
398
+ definition(`timestamps() option ${key} is not supported. Accepted options: enforce.`);
399
+ }
400
+ }
401
+ if (options.enforce === "trigger") {
402
+ unavailable(`timestamps({ enforce: "trigger" }) is not available yet. It arrives in 0.3.`);
403
+ }
404
+ if (options.enforce !== undefined) {
405
+ definition(`timestamps() enforce ${String(options.enforce)} must be trigger.`);
406
+ }
407
+ }
408
+ return trait("timestamps", {
409
+ fields: {
410
+ createdAt: stamped(),
411
+ updatedAt: stamped()
412
+ },
413
+ touch: ["updatedAt"],
414
+ sealed: ["createdAt", "updatedAt"]
415
+ });
416
+ }
417
+ function stamped() {
418
+ return timestamptz2().defaultSql("now()").guarded();
419
+ }
420
+ function archivable(options) {
421
+ installArchiveContributions();
422
+ const cascade = readCascade(options);
423
+ const base = trait("archivable", {
424
+ fields: {
425
+ archivedAt: timestamptz2().nullable().guarded(),
426
+ archiveId: uuid2().nullable().guarded()
427
+ },
428
+ sealed: ["archivedAt", "archiveId"]
429
+ });
430
+ const self = {
431
+ ...base,
432
+ "~archive": true,
433
+ apply(model, ctx) {
434
+ base.apply(model, ctx);
435
+ model.archive = { at: "archivedAt", id: "archiveId", cascade };
436
+ },
437
+ rewrite(tables, casing, schemaTraits) {
438
+ return rewriteArchivable(self, tables, casing, schemaTraits, cascade);
439
+ },
440
+ hook: attachArchive
441
+ };
442
+ return self;
443
+ }
444
+ function readCascade(options) {
445
+ if (options === undefined)
446
+ return [];
447
+ for (const key of Object.keys(options)) {
448
+ if (key !== "strategy" && key !== "cascade") {
449
+ definition(`archivable() option ${key} is not supported. Accepted options: strategy, cascade.`);
450
+ }
451
+ }
452
+ if (options.strategy === "table") {
453
+ unavailable('archivable({ strategy: "table" }) is not available yet. It arrives once archived snapshots can follow migrations.');
454
+ }
455
+ if (options.strategy !== undefined && options.strategy !== "column") {
456
+ definition(`archivable() strategy ${String(options.strategy)} must be column.`);
457
+ }
458
+ const cascade = options.cascade;
459
+ if (cascade === undefined)
460
+ return [];
461
+ if (!Array.isArray(cascade))
462
+ definition("archivable() cascade must be a list of table names.");
463
+ const names = [];
464
+ for (const name of cascade) {
465
+ if (typeof name !== "string" || name.length === 0) {
466
+ definition("archivable() cascade must name tables.");
467
+ }
468
+ names.push(name);
469
+ }
470
+ return names;
471
+ }
472
+ function markFields(fields, name) {
473
+ for (const field of Object.keys(fields)) {
474
+ const builder = fields[field];
475
+ if (typeof builder === "object" && builder !== null)
476
+ Object.assign(builder, { trait: name });
477
+ }
478
+ }
479
+ function sealTouch(sealed, touch) {
480
+ if (touch === undefined)
481
+ return sealed;
482
+ if (sealed === undefined)
483
+ return touch;
484
+ const names = [...sealed];
485
+ for (const field of touch)
486
+ if (!names.includes(field))
487
+ names.push(field);
488
+ return names;
489
+ }
490
+ function addFields(name, fields, model, ctx) {
491
+ for (const field of Object.keys(fields)) {
492
+ const builder = fields[field];
493
+ if (builder === undefined)
494
+ continue;
495
+ if (Object.hasOwn(model.columns, field)) {
496
+ const previous = model.columns[field].trait;
497
+ const which = typeof previous === "string" ? `trait ${previous} already adds it` : `${ctx.table} already declares it`;
498
+ throw new OkmError2("OKM1012", `Trait ${name} adds ${ctx.table}.${field}, and ${which}.`, {
499
+ fix: { summary: "Rename the table field or drop one of the traits." }
500
+ });
501
+ }
502
+ model.columns[field] = builder;
503
+ }
504
+ }
505
+ export {
506
+ archivable,
507
+ timestamps,
508
+ trait
509
+ };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Public `tx()` (spec §15, §15.2).
3
+ *
4
+ * One transaction reserves one connection of the primary. Every statement of
5
+ * the callback, its savepoints, and its batches run on that connection, and it
6
+ * goes back to the pool at commit, rollback, or cancellation. The callback gets
7
+ * a client built on a pool that is a facade over that connection, so reads,
8
+ * writes, includes, presets, tenancy, and archive handling are the ones the
9
+ * caller's client already has. Loaded the first time `tx()` is called.
10
+ */
11
+ import { type Session } from "./client.js";
12
+ /**
13
+ * Runs `fn` in a transaction, or in a savepoint when one is already open.
14
+ *
15
+ * @param session - Client session
16
+ * @param args - `[fn]` or `[options, fn]`
17
+ * @returns What `fn` returned
18
+ */
19
+ export declare function tx(session: Session, args: readonly unknown[]): Promise<unknown>;
@@ -1,21 +1,36 @@
1
1
  /**
2
- * Internal transaction runner for multi-statement writes.
2
+ * Writes on one endpoint.
3
3
  *
4
- * One statement uses the pool. Several statements use `batch`, which reserves
5
- * a connection, begins, and commits. Inside an open transaction, `batch` is a
6
- * savepoint and does not commit the outer transaction (D124). A connection
7
- * lost at commit is `outcome_unknown`. Public `tx` is P29.
4
+ * One statement uses the pool. Several statements use `batch`, which is atomic:
5
+ * the adapter begins, commits, and rolls back, and inside an open transaction
6
+ * the unit is a savepoint that never commits the outer transaction (D124). A
7
+ * connection lost at commit is `outcome_unknown`. The interactive transaction
8
+ * is `transaction.ts`; the public `batch` is `batch.ts`. Both load on first use.
8
9
  */
9
10
  import type { DriverConnection, DriverPool, ExecuteOptions, ExecuteResult, Statement } from "../contracts/driver.js";
11
+ import type { Hookm, Timeouts } from "./types.js";
12
+ /** What a write needs from the client: a pool, the default statement timeout, and observers. */
13
+ export type RunHost = {
14
+ readonly pool: DriverPool;
15
+ readonly timeouts?: Timeouts | undefined;
16
+ readonly hookm?: readonly Hookm[] | undefined;
17
+ /** Set inside a transaction. A unit of several statements is a savepoint there, not a transaction. */
18
+ readonly tx?: object | undefined;
19
+ };
10
20
  /**
11
21
  * Runs write statements as one atomic unit.
12
22
  *
13
- * @param pool - Endpoint pool
23
+ * A call with no `timeout` takes `timeouts.statement`. Notices go to
24
+ * `hookm.onNotice`. A unit of several statements is a transaction to
25
+ * `hookm.onTransaction`.
26
+ *
27
+ * @param host - Pool, default timeout, and observers
14
28
  * @param statements - Statements in order
15
29
  * @param options - Signal and timeout for the whole unit
30
+ * @param atomic - Use the driver's atomic `batch` even for one statement, so a failure carries `batchIndex`
16
31
  * @returns One result per statement
17
32
  */
18
- export declare function runWrite(pool: DriverPool, statements: readonly Statement[], options: ExecuteOptions | undefined): Promise<readonly ExecuteResult[]>;
33
+ export declare function runWrite(host: RunHost, statements: readonly Statement[], options: ExecuteOptions | undefined, atomic?: true): Promise<readonly ExecuteResult[]>;
19
34
  /**
20
35
  * Runs statements on a connection the caller already reserved.
21
36
  *