okmodel 0.1.0 → 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 +237 -19
  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 +390 -25
  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/{tb4ze1tr.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/{ckb1p0a4.js → 4jzzghr6.js} +416 -83
  117. package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
  118. package/dist/shared/{kynx7vbm.js → 5z4q2zda.js} +604 -21
  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/{ax16zpvm.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/{vkm3q4x1.js → eaxwrgby.js} +4 -3
  137. package/dist/shared/{7ck0dtry.js → ecdzej21.js} +160 -81
  138. package/dist/shared/{px90pf9p.js → edwqqryd.js} +21 -16
  139. package/dist/shared/{g2v6gnc8.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/{79ggp945.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/{4j72449d.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/{f5thc1s0.js → xrfnq5k8.js} +237 -418
  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 +56 -4
  181. package/dist/shared/gk7cs7sp.js +0 -566
@@ -1,3 +1,7 @@
1
+ import {
2
+ safetyInstalled,
3
+ runSafety
4
+ } from "./a5sxz52y.js";
1
5
  import {
2
6
  isOperator,
3
7
  operatorName,
@@ -5,25 +9,131 @@ import {
5
9
  } from "./cn0n68ft.js";
6
10
  import {
7
11
  assertIdentifier2
8
- } from "./mkjmqjv4.js";
12
+ } from "./nfzc5z0w.js";
9
13
  import {
10
14
  OkmError2,
11
- safe2,
12
15
  throwNamed2
13
- } from "./ycwz42az.js";
16
+ } from "./52szzakm.js";
14
17
 
15
18
  // src/runtime/plan.ts
16
- var FIND_OPTIONS = ["include", "limit", "orderBy", "select", "where"];
17
- var ONE_OPTIONS = ["include", "orderBy", "select", "where"];
18
- var FILTER_OPTIONS = ["where"];
19
+ var activeScope;
20
+ var activeView;
21
+ function withRowFilters(scope, view, fn) {
22
+ const previousScope = activeScope;
23
+ const previousView = activeView;
24
+ activeScope = scope;
25
+ activeView = view;
26
+ try {
27
+ return fn();
28
+ } finally {
29
+ activeScope = previousScope;
30
+ activeView = previousView;
31
+ }
32
+ }
33
+ var archiveInspect;
34
+ function installArchiveInspect(fn) {
35
+ archiveInspect = fn;
36
+ }
37
+ function archiveRules(model, view) {
38
+ return archiveInspect?.(model, view);
39
+ }
40
+ var FIND_OPTIONS = [
41
+ "include",
42
+ "limit",
43
+ "orderBy",
44
+ "select",
45
+ "signal",
46
+ "timeout",
47
+ "where"
48
+ ];
49
+ var ONE_OPTIONS = ["include", "orderBy", "select", "signal", "timeout", "where"];
50
+ var FILTER_OPTIONS = ["signal", "timeout", "where"];
51
+ function readOptionNames(op) {
52
+ if (op === "find")
53
+ return FIND_OPTIONS;
54
+ if (op === "one")
55
+ return ONE_OPTIONS;
56
+ return FILTER_OPTIONS;
57
+ }
19
58
  var BASE_RULES = [
20
59
  { rule: "parameterised", contribution: "planner", provenance: "planner" },
21
60
  { rule: "allowlisted", contribution: "catalog", provenance: "catalog" },
22
61
  { rule: "identifier", contribution: "catalog", provenance: "catalog" }
23
62
  ];
24
63
  var indexesOf = new WeakMap;
25
- function verifyRead(rules) {}
64
+ var operatorEmit;
65
+ function installOperatorSql(emit) {
66
+ operatorEmit = emit;
67
+ }
68
+ function readNeedsOperatorSql(schema, call) {
69
+ if (operatorEmit !== undefined)
70
+ return false;
71
+ const table = indexes(schema).get(call.table);
72
+ return table !== undefined && needsOperatorSql(table, call.where);
73
+ }
74
+ function emitRelationFrom(schema, relation, child, sink, parent, alias, depth) {
75
+ if (relation.through !== undefined) {
76
+ const planner = { join: emitJoin, where: emitWhere, indexes, quote };
77
+ relation.through.emit(planner, schema, relation, child, sink, parent, alias, depth);
78
+ return;
79
+ }
80
+ sink.text(quote(child.model.sql));
81
+ sink.text(" ");
82
+ sink.text(alias);
83
+ sink.text(" where ");
84
+ emitJoin(sink, parent, alias, relation);
85
+ }
86
+ var SYNC_OPERATORS = ",lt,lte,gt,gte,between,inList,notIn,";
87
+ var TEXT_OPERATORS = ",startsWith,endsWith,contains,like,ilike,";
88
+ function needsOperatorSql(table, value, key) {
89
+ if (isOperator(value)) {
90
+ const name = operatorName(value);
91
+ if (name === "or" || name === "and") {
92
+ const branches = operatorValue(value);
93
+ return Array.isArray(branches) && branches.some((branch) => needsOperatorSql(table, branch));
94
+ }
95
+ if (name === "not" || name === "eq")
96
+ return needsOperatorSql(table, operatorValue(value), key);
97
+ if (TEXT_OPERATORS.includes(`,${name},`)) {
98
+ const column = key === undefined ? undefined : table.columns.get(key);
99
+ return column !== undefined && !isTextColumn(column.dataType);
100
+ }
101
+ return !SYNC_OPERATORS.includes(`,${name},`);
102
+ }
103
+ if (Array.isArray(value))
104
+ return value.some((item) => needsOperatorSql(table, item, key));
105
+ if (!isRecord(value))
106
+ return false;
107
+ for (const field of Object.keys(value)) {
108
+ if (needsOperatorSql(table, value[field], field))
109
+ return true;
110
+ }
111
+ return false;
112
+ }
113
+ function isTextColumn(dataType) {
114
+ return dataType === "text" || dataType === "citext" || dataType.startsWith("varchar(") || dataType.startsWith("char(") || dataType.startsWith("character");
115
+ }
116
+ function emitLazy(column, ref, name, value, sink) {
117
+ const emit = operatorEmit;
118
+ if (emit === undefined) {
119
+ fail("OKM1121", `Operator ${name} on ${column.field} is not loaded.`);
120
+ }
121
+ emit(column, ref, name, value, sink);
122
+ }
123
+ function verifyRead(schema, call) {
124
+ if (!safetyInstalled())
125
+ return;
126
+ const source = schema.model[call.table]?.source;
127
+ const hatches = [];
128
+ if (call.all !== undefined)
129
+ hatches.push({ name: "all", reason: call.all });
130
+ if (call.scope !== undefined && "unscoped" in call.scope) {
131
+ hatches.push({ name: "unscoped", reason: call.scope.unscoped });
132
+ }
133
+ runSafety(appliedRules(call, source), hatches.length === 0 ? undefined : hatches);
134
+ }
26
135
  function bindCall(schema, call, hooks) {
136
+ verifyRead(schema, call);
27
137
  const params = [];
28
138
  const marks = [];
29
139
  const sink = {
@@ -36,7 +146,6 @@ function bindCall(schema, call, hooks) {
36
146
  }
37
147
  };
38
148
  emit(schema, call, sink, undefined, hooks);
39
- verifyRead(BASE_RULES);
40
149
  return { key: marks.join(""), params };
41
150
  }
42
151
  function compileCall(schema, call, key, hooks) {
@@ -96,8 +205,15 @@ function logicalIntent(call) {
96
205
  all: call.all
97
206
  };
98
207
  }
99
- function appliedRules(call) {
100
- const rules = [...BASE_RULES];
208
+ function appliedRules(call, source) {
209
+ const rules = [];
210
+ for (const rule of BASE_RULES) {
211
+ if (source !== undefined && rule.contribution === "catalog") {
212
+ rules.push({ ...rule, source });
213
+ } else {
214
+ rules.push(rule);
215
+ }
216
+ }
101
217
  if (call.where !== undefined) {
102
218
  rules.push({ rule: "filter", contribution: "caller", provenance: "caller" });
103
219
  }
@@ -108,13 +224,37 @@ function appliedRules(call) {
108
224
  provenance: "caller"
109
225
  });
110
226
  }
227
+ if (call.tenancyRules !== undefined) {
228
+ for (const rule of call.tenancyRules)
229
+ rules.push(rule);
230
+ }
231
+ if (call.ruleLines !== undefined) {
232
+ for (const rule of call.ruleLines)
233
+ rules.push(rule);
234
+ }
111
235
  return rules;
112
236
  }
113
237
  function emit(schema, call, sink, outputs, hooks) {
238
+ const previousScope = activeScope;
239
+ const previousView = activeView;
240
+ activeScope = call.scope;
241
+ activeView = call.archive;
242
+ try {
243
+ emitRead(schema, call, sink, outputs, hooks);
244
+ } finally {
245
+ activeScope = previousScope;
246
+ activeView = previousView;
247
+ }
248
+ }
249
+ function emitRead(schema, call, sink, outputs, hooks) {
114
250
  const table = indexes(schema).get(call.table);
115
251
  if (table === undefined) {
116
252
  throwNamed2("OKM1120", call.table, [...indexes(schema).keys()], `Table ${call.table} is not in the schema. Accepted names: ${list([...indexes(schema).keys()])}.`);
117
253
  }
254
+ if (call.build !== undefined) {
255
+ call.build(schema, table, sink, outputs, call);
256
+ return;
257
+ }
118
258
  rejectOptions(call);
119
259
  const selected = selectedColumns(table, call.select);
120
260
  sink.mark(`${call.op}|${call.table}|`);
@@ -169,9 +309,10 @@ function emit(schema, call, sink, outputs, hooks) {
169
309
  continue;
170
310
  hooks?.join(schema, include, sink, "t", `i${String(index)}`, 0);
171
311
  }
172
- emitWhere(schema, table, call.where, sink, "t", 0);
312
+ emitWhere(schema, table, call.where, sink, "t", 0, false, call.after);
173
313
  emitOrder(table, call.orderBy, sink, "t");
174
314
  emitLimit(call, sink);
315
+ call.tail?.(sink);
175
316
  }
176
317
  function emitJoin(sink, parent, child, relation) {
177
318
  for (let index = 0;index < relation.local.length; index += 1) {
@@ -190,26 +331,56 @@ function emitJoin(sink, parent, child, relation) {
190
331
  sink.text(quote(local));
191
332
  }
192
333
  }
193
- function emitWhere(schema, table, where, sink, alias, depth, appended = false) {
194
- if (where === undefined)
195
- return;
196
- if (isOperator(where) && operatorName(where) === "or") {
197
- sink.text(appended ? " and " : " where ");
198
- emitOr(schema, table, operatorValue(where), sink, alias, depth);
199
- return;
334
+ function holdArchive(table, sink, alias, appended) {
335
+ const archive = table.model.archive;
336
+ if (archive === undefined || activeView === "with")
337
+ return false;
338
+ sink.text(appended ? " and " : " where ");
339
+ sink.text(alias);
340
+ sink.text(".");
341
+ sink.text(quote(archive.at));
342
+ sink.text(activeView === "only" ? " is not null" : " is null");
343
+ sink.mark(activeView === "only" ? "archived|" : "active|");
344
+ return true;
345
+ }
346
+ function emitWhere(schema, table, where, sink, alias, depth, appended = false, extra) {
347
+ const held = schema.tenancy?.predicate({
348
+ table: table.model.name,
349
+ fieldSql: (field) => table.columns.get(field)?.sql,
350
+ encode: table.columns.get(schema.tenancy?.key ?? "")?.encode,
351
+ alias,
352
+ appended,
353
+ scope: activeScope,
354
+ sink
355
+ }) === true;
356
+ const archived = holdArchive(table, sink, alias, appended || held);
357
+ let started = appended || held || archived;
358
+ if (extra !== undefined) {
359
+ sink.text(started ? " and (" : " where (");
360
+ extra(sink, alias);
361
+ sink.text(")");
362
+ started = true;
200
363
  }
201
- if (!isRecord(where)) {
202
- fail("OKM1121", "where must be an object of fields. Wrap an object value with eq.");
364
+ for (const part of isOperator(where) && operatorName(where) === "and" ? operatorValue(where) : [where]) {
365
+ if (part === undefined)
366
+ continue;
367
+ const joined = isOperator(part);
368
+ if (!joined && !isRecord(part))
369
+ fail("OKM1121", WHERE_FIELDS);
370
+ if (!joined && Object.keys(part).length === 0)
371
+ continue;
372
+ sink.text(started ? " and " : " where ");
373
+ started = true;
374
+ if (joined && operatorName(part) === "or")
375
+ emitOr(schema, table, operatorValue(part), sink, alias, depth);
376
+ else
377
+ emitAnd(schema, table, part, sink, alias, depth);
203
378
  }
204
- const keys = Object.keys(where);
205
- if (keys.length === 0)
206
- return;
207
- sink.text(appended ? " and " : " where ");
208
- emitAnd(schema, table, where, sink, alias, depth);
209
379
  }
380
+ var WHERE_FIELDS = "where must be an object of fields. Wrap an object value with eq.";
210
381
  function emitAnd(schema, table, where, sink, alias, depth) {
211
382
  if (!isRecord(where)) {
212
- fail("OKM1121", "where must be an object of fields. Wrap an object value with eq.");
383
+ fail("OKM1121", WHERE_FIELDS);
213
384
  }
214
385
  const keys = Object.keys(where);
215
386
  let wrote = false;
@@ -231,6 +402,7 @@ function emitPredicate(schema, table, key, value, sink, alias, depth) {
231
402
  emitRelationFilter(schema, relation, value, sink, alias, depth);
232
403
  return;
233
404
  }
405
+ schema.tenancy?.guard(table.model.name, key, "where");
234
406
  const column = table.columns.get(key);
235
407
  if (column === undefined) {
236
408
  throwNamed2("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
@@ -238,7 +410,7 @@ function emitPredicate(schema, table, key, value, sink, alias, depth) {
238
410
  sink.mark(`w:${key}`);
239
411
  emitOperand(schema, table, column, value, sink, alias, depth);
240
412
  }
241
- function emitOperand(schema, table, column, value, sink, alias, depth) {
413
+ function emitOperand(schema, table, column, value, sink, alias, depth, wrapped = false) {
242
414
  const ref = `${alias}.${quote(column.sql)}`;
243
415
  if (isOperator(value)) {
244
416
  emitOperator(schema, table, column, ref, operatorName(value), operatorValue(value), sink, alias, depth);
@@ -250,9 +422,8 @@ function emitOperand(schema, table, column, value, sink, alias, depth) {
250
422
  sink.text(" is null");
251
423
  return;
252
424
  }
253
- if (typeof value === "object") {
254
- fail("OKM1121", `Field ${column.field} received an object. Wrap it with eq. eq is the equality form for json and jsonb.`);
255
- }
425
+ if (typeof value === "object")
426
+ takeObject(column, value, !wrapped);
256
427
  sink.mark(":eq");
257
428
  sink.text(ref);
258
429
  sink.text(" = ");
@@ -272,7 +443,7 @@ function emitOperator(schema, table, column, ref, name, value, sink, alias, dept
272
443
  return;
273
444
  }
274
445
  if (name === "eq") {
275
- emitOperand(schema, table, column, value, sink, alias, depth);
446
+ emitOperand(schema, table, column, value, sink, alias, depth, true);
276
447
  return;
277
448
  }
278
449
  if (name === "lt" || name === "lte" || name === "gt" || name === "gte") {
@@ -296,26 +467,25 @@ function emitOperator(schema, table, column, ref, name, value, sink, alias, dept
296
467
  emitIn(column, ref, name, value, sink);
297
468
  return;
298
469
  }
299
- if (name === "startsWith" || name === "contains" || name === "endsWith") {
470
+ if (TEXT_OPERATORS.includes(`,${name},`) && isTextColumn(column.dataType)) {
300
471
  if (typeof value !== "string") {
301
472
  fail("OKM1121", `${name} on ${column.field} needs a string.`);
302
473
  }
303
474
  sink.text(ref);
475
+ if (name === "like" || name === "ilike") {
476
+ sink.text(name === "like" ? " like " : " ilike ");
477
+ sink.param(value);
478
+ return;
479
+ }
304
480
  sink.text(" like ");
305
- sink.param(literalPattern(value, name));
306
- sink.text(" escape E'\\\\'");
307
- return;
308
- }
309
- if (name === "like" || name === "ilike") {
310
- if (typeof value !== "string") {
481
+ if (name !== "startsWith" && name !== "endsWith" && name !== "contains") {
311
482
  fail("OKM1121", `${name} on ${column.field} needs a string.`);
312
483
  }
313
- sink.text(ref);
314
- sink.text(name === "like" ? " like " : " ilike ");
315
- sink.param(value);
484
+ sink.param(literalPattern(value, name));
485
+ sink.text(" escape E'\\\\'");
316
486
  return;
317
487
  }
318
- fail("OKM1121", `${name} is not a value operator on ${column.field}. Use has, none, or every on a relation.`);
488
+ emitLazy(column, ref, name, value, sink);
319
489
  }
320
490
  function emitNot(schema, table, column, ref, value, sink, alias, depth) {
321
491
  if (value === null) {
@@ -332,7 +502,7 @@ function emitNot(schema, table, column, ref, value, sink, alias, depth) {
332
502
  }
333
503
  }
334
504
  sink.text("not (");
335
- emitOperand(schema, table, column, value, sink, alias, depth);
505
+ emitOperand(schema, table, column, value, sink, alias, depth, true);
336
506
  sink.text(")");
337
507
  }
338
508
  function emitIn(column, ref, name, value, sink) {
@@ -402,13 +572,10 @@ function emitRelationFilter(schema, relation, value, sink, alias, depth) {
402
572
  if (name === "none" || name === "every")
403
573
  sink.text("not ");
404
574
  sink.text("exists (select 1 from ");
405
- sink.text(quote(child.model.sql));
406
- sink.text(" ");
407
- sink.text(childAlias);
408
- sink.text(" where ");
409
- emitJoin(sink, alias, childAlias, relation);
575
+ emitRelationFrom(schema, relation, child, sink, alias, childAlias, depth);
410
576
  const inner = operatorValue(value);
411
577
  if (name === "every") {
578
+ emitWhere(schema, child, undefined, sink, childAlias, depth + 1, true);
412
579
  sink.text(" and not (");
413
580
  emitAnd(schema, child, inner ?? {}, sink, childAlias, depth + 1);
414
581
  sink.text(")");
@@ -540,8 +707,7 @@ function parseOrder(field, value) {
540
707
  fail("OKM1120", `orderBy ${field} must be asc or desc.`);
541
708
  }
542
709
  function rejectOptions(call) {
543
- const record = call;
544
- const accepted = call.op === "find" ? FIND_OPTIONS : call.op === "one" ? ONE_OPTIONS : FILTER_OPTIONS;
710
+ const accepted = readOptionNames(call.op);
545
711
  const present = {};
546
712
  if (call.where !== undefined)
547
713
  present.where = call.where;
@@ -569,11 +735,18 @@ function readLimit(value) {
569
735
  return value;
570
736
  }
571
737
  function requireValue(column, value) {
572
- if (value === undefined || value === null || typeof value === "object") {
573
- fail("OKM1121", `Field ${column.field} needs a value. Wrap an object with eq.`);
738
+ if (value === undefined || value === null) {
739
+ fail("OKM1121", `Field ${column.field} needs a value.`);
574
740
  }
741
+ if (typeof value === "object")
742
+ takeObject(column, value);
575
743
  return value;
576
744
  }
745
+ function takeObject(column, value, bare = false) {
746
+ if (bare || !column.accepts?.includes({}.toString.call(value).slice(8, -1))) {
747
+ fail("OKM1121", `Field ${column.field} rejects that object.`);
748
+ }
749
+ }
577
750
  function projectExpr(sql, dataType) {
578
751
  if (dataType === "timestamptz" || dataType === "timestamp")
579
752
  return jsonExpr(sql, dataType);
@@ -630,8 +803,9 @@ function indexes(schema) {
630
803
  }
631
804
  names.sort();
632
805
  const relations = new Map;
633
- for (const relation of model.relations)
806
+ for (const relation of model.relations) {
634
807
  relations.set(relation.name, relation);
808
+ }
635
809
  built.set(model.name, { model, columns, relations, names });
636
810
  }
637
811
  indexesOf.set(schema, built);
@@ -661,9 +835,16 @@ function list(names) {
661
835
  function isRecord(value) {
662
836
  return typeof value === "object" && value !== null && !Array.isArray(value) && !isOperator(value);
663
837
  }
838
+ var FAIL_FIX = {
839
+ OKM1121: "Use eq for an object value, or has, none, or every for a relation.",
840
+ OKM1101: "Pass limit, or call .all(reason)."
841
+ };
842
+ function registerFailFix(code, summary) {
843
+ FAIL_FIX[code] = summary;
844
+ }
664
845
  function fail(code, message) {
665
- const fix = code === "OKM1121" ? { summary: "Use eq for an object value, or has, none, or every for a relation." } : code === "OKM1101" ? { summary: "Pass limit, or call .all(reason)." } : code === "OKM1102" ? { summary: "Pass where, or call .all(reason) to match every row." } : code === "OKM1104" ? { summary: "Set on to the columns of a unique constraint or the primary key." } : code === "OKM1105" ? { summary: "Pass limit on the include, or call .all(reason)." } : code === "OKM1190" ? { summary: "Remove the guarded field. Input cannot set it." } : undefined;
666
- throw new OkmError2(code, message, fix === undefined ? undefined : { fix });
846
+ const summary = FAIL_FIX[code];
847
+ throw new OkmError2(code, message, summary === undefined ? undefined : { fix: { summary } });
667
848
  }
668
849
  function plain(value) {
669
850
  if (isOperator(value)) {
@@ -685,366 +866,4 @@ function hasMany(include) {
685
866
  return Object.keys(include).length > 0;
686
867
  }
687
868
 
688
- // src/runtime/client.ts
689
- var PLAN_LIMIT = 64;
690
- var SINGLE_ENDPOINT = {
691
- endpoint: "primary",
692
- role: "primary",
693
- reason: "single-endpoint"
694
- };
695
- function createClient(schema, pool, options) {
696
- const connected = checkServer(pool, schema, options.http, options.requireMeta === true, callOptions(options), {
697
- ...options.catalog !== undefined ? { catalog: options.catalog } : {},
698
- ...options.catalogDir !== undefined ? { catalogDir: options.catalogDir } : {}
699
- });
700
- const session = {
701
- schema,
702
- pool,
703
- ownsPool: options.ownsPool,
704
- http: options.http,
705
- includeValues: options.includeValues === true,
706
- logger: options.logger,
707
- connected,
708
- cache: new Map
709
- };
710
- const tables = {};
711
- for (const name of Object.keys(schema.model)) {
712
- tables[name] = tableApi(session, name);
713
- }
714
- const client = {
715
- ...tables,
716
- table(name) {
717
- const found = tables[name];
718
- if (found !== undefined)
719
- return found;
720
- try {
721
- throwNamed2("OKM1120", name, Object.keys(tables), `Table ${name} is not in the schema. Accepted names: ${Object.keys(tables).join(", ")}.`);
722
- } catch (error) {
723
- if (error instanceof OkmError2)
724
- throw attachHttp(session.http, error);
725
- throw error;
726
- }
727
- },
728
- close() {
729
- return options.ownsPool ? pool.close() : Promise.resolve();
730
- },
731
- connected
732
- };
733
- return client;
734
- }
735
- function tableApi(session, table) {
736
- return {
737
- find(options = {}) {
738
- return start(session, "find", table, options, {});
739
- },
740
- one(options = {}) {
741
- return start(session, "one", table, options, {});
742
- },
743
- count(options = {}) {
744
- return start(session, "count", table, options, {});
745
- },
746
- exists(options = {}) {
747
- return start(session, "exists", table, options, {});
748
- },
749
- insert(data, options = {}) {
750
- return writeHandle(session, "insert", table, data, options, {});
751
- },
752
- update(target, options = {}) {
753
- return writeHandle(session, "update", table, target, options, {});
754
- },
755
- delete(target, options = {}) {
756
- return writeHandle(session, "delete", table, target, options, {});
757
- }
758
- };
759
- }
760
- var writers;
761
- function loadWrite() {
762
- writers ??= import("./ckb1p0a4.js");
763
- return writers;
764
- }
765
- function writeHandle(session, op, table, input, options, mods) {
766
- let pending;
767
- const run = () => {
768
- pending ??= runWrite(session, op, table, input, options, mods);
769
- return pending;
770
- };
771
- const self = {
772
- then(onFulfilled, onRejected) {
773
- return run().then(onFulfilled, onRejected);
774
- },
775
- catch(onRejected) {
776
- return self.then(undefined, onRejected);
777
- },
778
- finally(fn) {
779
- return self.then((value) => {
780
- fn();
781
- return value;
782
- }, (error) => {
783
- fn();
784
- throw error;
785
- });
786
- },
787
- safe() {
788
- return safe2(run());
789
- },
790
- sql() {
791
- return loadWrite().then((mod) => mod.explainWrite(session.schema, op, table, input, options, mods));
792
- },
793
- expect(count) {
794
- return writeHandle(session, op, table, input, options, { ...mods, expect: count });
795
- },
796
- all(reason) {
797
- return writeHandle(session, op, table, input, options, { ...mods, all: reason });
798
- }
799
- };
800
- return self;
801
- }
802
- async function runWrite(session, op, table, input, options, mods) {
803
- await session.connected;
804
- const mod = await loadWrite();
805
- try {
806
- return await mod.executeWrite({ schema: session.schema, pool: session.pool }, op, table, input, options, mods);
807
- } catch (error) {
808
- throw logged(session, await mapError(session, error));
809
- }
810
- }
811
- function start(session, op, table, options, mods) {
812
- const call = readCall(op, table, options, mods.all);
813
- let prepared;
814
- const prepare = async () => {
815
- if (prepared !== undefined)
816
- return prepared;
817
- const hooks = await hooksFor(call);
818
- const bound = bindCall(session.schema, call, hooks);
819
- let plan = session.cache.get(bound.key);
820
- if (plan === undefined) {
821
- plan = compileCall(session.schema, call, bound.key, hooks);
822
- if (session.cache.size >= PLAN_LIMIT) {
823
- const oldest = session.cache.keys().next().value;
824
- if (oldest !== undefined)
825
- session.cache.delete(oldest);
826
- }
827
- session.cache.set(bound.key, plan);
828
- }
829
- prepared = { plan, params: bound.params };
830
- return prepared;
831
- };
832
- const run = async () => {
833
- await session.connected;
834
- const { plan, params } = await prepare();
835
- try {
836
- const result = await session.pool.execute(plan.text, params, callOptions(mods));
837
- const value = plan.outputs.includes.length === 0 ? decodeResult(plan, result.rows, table) : (await import("./px90pf9p.js")).decodeIncluded(plan, result.rows, table);
838
- if (mods.required === true && value === null) {
839
- throw new OkmError2("not_found", `one() on ${table} matched no row.`, withHttp(session.http, { kind: "not_found", table }));
840
- }
841
- return value;
842
- } catch (error) {
843
- throw logged(session, await mapError(session, error));
844
- }
845
- };
846
- let pending;
847
- const once = () => {
848
- pending ??= run();
849
- return pending;
850
- };
851
- const self = {
852
- then(onFulfilled, onRejected) {
853
- return once().then(onFulfilled, onRejected);
854
- },
855
- catch(onRejected) {
856
- return this.then(undefined, onRejected);
857
- },
858
- finally(fn) {
859
- return this.then((value) => {
860
- fn();
861
- return value;
862
- }, (error) => {
863
- fn();
864
- throw error;
865
- });
866
- },
867
- inspect() {
868
- if (call.include === undefined && prepared !== undefined) {
869
- return inspection(prepared.plan, prepared.params, call);
870
- }
871
- if (call.include === undefined) {
872
- const bound = bindNow(session.schema, call);
873
- prepared = bound;
874
- return inspection(bound.plan, bound.params, call);
875
- }
876
- return prepare().then(({ plan, params }) => inspection(plan, params, call));
877
- },
878
- sql() {
879
- if (call.include === undefined && prepared !== undefined) {
880
- return { text: prepared.plan.text, params: prepared.params };
881
- }
882
- if (call.include === undefined) {
883
- const { plan, params } = bindNow(session.schema, call);
884
- prepared = { plan, params };
885
- return { text: plan.text, params };
886
- }
887
- return prepare().then(({ plan, params }) => ({ text: plan.text, params }));
888
- },
889
- safe() {
890
- return safe2(once());
891
- },
892
- all(reason) {
893
- return start(session, op, table, options, { ...mods, all: reason });
894
- },
895
- required() {
896
- return start(session, op, table, options, { ...mods, required: true });
897
- },
898
- stream() {
899
- return streamRows(session, call, prepare);
900
- }
901
- };
902
- return self;
903
- }
904
- async function* streamRows(session, call, prepare) {
905
- await session.connected;
906
- if (session.pool.capabilities.stream !== true || session.pool.stream === undefined) {
907
- throw logged(session, new OkmError2("OKM1111", "stream needs a driver with stream: true. This driver does not have it.", withHttp(session.http, {
908
- fix: { summary: "Use a driver that sets stream, or read with find." }
909
- })));
910
- }
911
- const { plan, params } = await prepare();
912
- const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./px90pf9p.js")).decodeIncluded;
913
- for await (const chunk of session.pool.stream(plan.text, params)) {
914
- const decoded = decode(plan, chunk, call.table);
915
- if (!Array.isArray(decoded))
916
- continue;
917
- for (const row of decoded)
918
- yield row;
919
- }
920
- }
921
- function readCall(op, table, options, all) {
922
- const record = options;
923
- const accepted = op === "find" ? ["include", "limit", "orderBy", "select", "where"] : op === "one" ? ["include", "orderBy", "select", "where"] : ["where"];
924
- for (const key of Object.keys(record)) {
925
- if (!accepted.includes(key)) {
926
- throwNamed2("OKM1120", key, accepted, `Option ${key} is not accepted by ${op}. Accepted options: ${accepted.join(", ")}.`);
927
- }
928
- }
929
- return {
930
- op,
931
- table,
932
- ...record.where !== undefined ? { where: record.where } : {},
933
- ...record.select !== undefined ? { select: record.select } : {},
934
- ...record.orderBy !== undefined ? { orderBy: record.orderBy } : {},
935
- ...record.limit !== undefined ? { limit: record.limit } : {},
936
- ...record.include !== undefined ? { include: record.include } : {},
937
- ...all !== undefined ? { all } : {}
938
- };
939
- }
940
- async function checkServer(pool, schema, http, requireMeta, options, source) {
941
- let rows;
942
- try {
943
- const result = await pool.execute(SERVER_CHECK, undefined, options);
944
- rows = result.rows;
945
- } catch (error) {
946
- const { mapPostgresError } = await import("./zhc5vmgf.js");
947
- throw mapPostgresError(error, mapOptions(http, false));
948
- }
949
- const version = rows[0]?.[1];
950
- if (version === null || version === undefined || !version.startsWith("PostgreSQL")) {
951
- throw new OkmError2("OKM1801", "The server is not PostgreSQL. The schema dialect is postgres.", withHttp(http, {
952
- fix: { summary: "Open the schema with the Postgres driver that matches it." }
953
- }));
954
- }
955
- const recorded = rows[0]?.[2];
956
- if (recorded === null || recorded === undefined || recorded.length === 0) {
957
- if (requireMeta) {
958
- throw new OkmError2("OKM1520", "okm_meta has no catalog hash.", withHttp(http, {
959
- fix: {
960
- summary: "Apply migrations so okm_meta records the catalog, or omit requireMeta to adopt this database."
961
- }
962
- }));
963
- }
964
- } else {
965
- const { assertCompatible } = await import("./qk3hxfja.js");
966
- try {
967
- await assertCompatible(pool, schema, recorded, source, options);
968
- } catch (error) {
969
- if (error instanceof OkmError2)
970
- throw attachHttp(http, error);
971
- throw error;
972
- }
973
- }
974
- const requires = schema.requires?.postgres;
975
- if (requires === undefined)
976
- return;
977
- const major = Math.floor(Number(rows[0]?.[0] ?? "0") / 1e4);
978
- const match = /^>=(\d+)$/.exec(requires.trim());
979
- const need = match?.[1] === undefined ? undefined : Number(match[1]);
980
- if (need === undefined || major < need) {
981
- throw new OkmError2("OKM1802", `The server is PostgreSQL ${String(major)}. schema({ requires }) asks for ${requires}.`, withHttp(http, {
982
- fix: { summary: "Upgrade the server, or lower requires to a version the server meets." }
983
- }));
984
- }
985
- }
986
- async function mapError(session, error) {
987
- if (error instanceof OkmError2) {
988
- return session.http === undefined ? error : new OkmError2(error.code, error.message, { ...errorFields(error), http: session.http });
989
- }
990
- const { mapPostgresError } = await import("./zhc5vmgf.js");
991
- return mapPostgresError(error, mapOptions(session.http, session.includeValues));
992
- }
993
- function errorFields(error) {
994
- return {
995
- kind: error.kind,
996
- ...error.table !== undefined ? { table: error.table } : {},
997
- ...error.columns.length > 0 ? { columns: error.columns } : {},
998
- fix: error.fix
999
- };
1000
- }
1001
- var includeHooks;
1002
- function hooksFor(call) {
1003
- if (call.include === undefined)
1004
- return Promise.resolve(undefined);
1005
- includeHooks ??= import("./px90pf9p.js").then((mod) => mod.includeHooks);
1006
- return includeHooks;
1007
- }
1008
- function bindNow(schema, call) {
1009
- const bound = bindCall(schema, call);
1010
- const plan = compileCall(schema, call, bound.key);
1011
- return { plan, params: bound.params };
1012
- }
1013
- function inspection(plan, params, call) {
1014
- return {
1015
- intent: logicalIntent(call),
1016
- rules: appliedRules(call),
1017
- plan: { strategy: "postgres:single-statement", statements: 1, fingerprint: plan.fingerprint },
1018
- sql: { text: plan.text, params },
1019
- routing: SINGLE_ENDPOINT
1020
- };
1021
- }
1022
- function withHttp(http, options) {
1023
- return http === undefined ? options : { ...options, http };
1024
- }
1025
- function attachHttp(http, error) {
1026
- if (http === undefined)
1027
- return error;
1028
- return new OkmError2(error.code, error.message, { ...errorFields(error), http });
1029
- }
1030
- function callOptions(input) {
1031
- if (input.signal === undefined && input.timeout === undefined)
1032
- return;
1033
- return {
1034
- ...input.signal !== undefined ? { signal: input.signal } : {},
1035
- ...input.timeout !== undefined ? { timeout: input.timeout } : {}
1036
- };
1037
- }
1038
- var SERVER_CHECK = "select current_setting('server_version_num'), version(), (select (xpath('//catalog_hash/text()', query_to_xml('select catalog_hash from okm_meta where id = ''head''', true, false, '')))[1]::text where to_regclass('okm_meta') is not null)";
1039
- function mapOptions(http, includeValues) {
1040
- return {
1041
- ...http !== undefined ? { http } : {},
1042
- ...includeValues ? { includeValues: true } : {}
1043
- };
1044
- }
1045
- function logged(session, error) {
1046
- session.logger?.error?.({ code: error.code, summary: error.summary });
1047
- return error;
1048
- }
1049
-
1050
- export { decodeResult, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, selectedColumns, rejectKeys, readLimit, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, fail, createClient };
869
+ export { withRowFilters, installArchiveInspect, archiveRules, readOptionNames, installOperatorSql, readNeedsOperatorSql, emitRelationFrom, bindCall, compileCall, decodeResult, logicalIntent, appliedRules, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, selectedColumns, parseOrder, rejectKeys, readLimit, takeObject, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, registerFailFix, fail };