okmodel 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +36 -18
  2. package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
  3. package/dist/adapters/pg/postgres-extra.d.ts +22 -0
  4. package/dist/contracts/catalog/document.d.ts +19 -5
  5. package/dist/contracts/catalog/object.d.ts +2 -1
  6. package/dist/contracts/catalog/policy.d.ts +48 -0
  7. package/dist/contracts/catalog/types.d.ts +25 -1
  8. package/dist/contracts/catalog/view.d.ts +2 -0
  9. package/dist/contracts/internal.js +1 -1
  10. package/dist/contracts/location.d.ts +10 -0
  11. package/dist/dialects/pg/column.d.ts +13 -2
  12. package/dist/dialects/pg/ddl.d.ts +37 -1
  13. package/dist/dialects/pg/emit.d.ts +19 -1
  14. package/dist/dialects/pg/errors.d.ts +32 -1
  15. package/dist/dialects/pg/ext/citext.js +1 -1
  16. package/dist/dialects/pg/ext/index.js +1 -1
  17. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  18. package/dist/dialects/pg/filter-parse.d.ts +10 -3
  19. package/dist/dialects/pg/fn/index.js +10 -2
  20. package/dist/dialects/pg/index.d.ts +2 -1
  21. package/dist/dialects/pg/index.js +29 -17
  22. package/dist/dialects/pg/introspect.d.ts +7 -0
  23. package/dist/dialects/pg/model.d.ts +7 -2
  24. package/dist/dialects/pg/ops/or.d.ts +1 -1
  25. package/dist/dialects/pg/owner.d.ts +42 -0
  26. package/dist/dialects/pg/path-endpoint.d.ts +20 -0
  27. package/dist/dialects/pg/role/check.d.ts +3 -0
  28. package/dist/dialects/pg/role/index.d.ts +71 -6
  29. package/dist/dialects/pg/role/sql.d.ts +11 -0
  30. package/dist/dialects/pg/schema.d.ts +2 -0
  31. package/dist/dialects/pg/table.d.ts +25 -3
  32. package/dist/dialects/pg/temporal.d.ts +5 -76
  33. package/dist/dialects/pg/tenancy.d.ts +45 -3
  34. package/dist/dialects/pg/view/client.d.ts +19 -1
  35. package/dist/dialects/pg/view/index.d.ts +4 -0
  36. package/dist/dialects/pg/view/index.js +21 -178
  37. package/dist/dialects/pg/view/refresh.d.ts +24 -0
  38. package/dist/runtime/batch.d.ts +1 -1
  39. package/dist/runtime/client.d.ts +6 -1
  40. package/dist/runtime/conflict.d.ts +2 -1
  41. package/dist/runtime/drift.d.ts +9 -0
  42. package/dist/runtime/inflight.d.ts +14 -0
  43. package/dist/runtime/pg/bun.js +6 -5
  44. package/dist/runtime/pg/pg.js +5 -5
  45. package/dist/runtime/pg/pglite.js +6 -5
  46. package/dist/runtime/pg/postgresjs.js +5 -5
  47. package/dist/runtime/plan.d.ts +22 -1
  48. package/dist/runtime/predicate.d.ts +19 -0
  49. package/dist/runtime/safety/index.js +1 -1
  50. package/dist/runtime/tenancy/composite.d.ts +26 -0
  51. package/dist/runtime/tenancy/context.d.ts +16 -0
  52. package/dist/runtime/tenancy/index.d.ts +9 -5
  53. package/dist/runtime/tenancy/index.js +1508 -37
  54. package/dist/runtime/tenancy/path.d.ts +87 -0
  55. package/dist/runtime/tenancy/rls.d.ts +67 -0
  56. package/dist/runtime/tenancy/via.d.ts +22 -0
  57. package/dist/runtime/traits/index.js +17 -11
  58. package/dist/runtime/types.d.ts +13 -1
  59. package/dist/runtime/validate/index.js +4 -4
  60. package/dist/runtime/write.d.ts +1 -1
  61. package/dist/shared/09p049xa.js +702 -0
  62. package/dist/shared/{ykv7wh5b.js → 1f0xa3cb.js} +5 -5
  63. package/dist/shared/1nw1f3vs.js +23 -0
  64. package/dist/shared/{6p5k20sm.js → 1z04fa85.js} +21 -11
  65. package/dist/shared/{p7pfx624.js → 2cg3wnxc.js} +314 -65
  66. package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
  67. package/dist/shared/{qgvt8nx1.js → 2z7wv5tj.js} +21 -5
  68. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  69. package/dist/shared/{sf8m6tfy.js → 32cnm7kn.js} +8 -3
  70. package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
  71. package/dist/shared/{jdgqqx31.js → 3fqwmr09.js} +38 -14
  72. package/dist/shared/{922qbgnw.js → 3qnhc7w5.js} +16 -9
  73. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  74. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  75. package/dist/shared/{k7cc9nzk.js → 5d8t84hg.js} +15 -3
  76. package/dist/shared/{fa9vt245.js → 5j36z8dn.js} +17 -6
  77. package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
  78. package/dist/shared/8dqhth8c.js +12 -0
  79. package/dist/shared/8etn7d8w.js +62 -0
  80. package/dist/shared/{g516zdsb.js → 8x72s86k.js} +476 -30
  81. package/dist/shared/{nkc9sfqb.js → 929h19xp.js} +145 -7
  82. package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
  83. package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
  84. package/dist/shared/{b066g80h.js → ag4c7516.js} +2 -2
  85. package/dist/shared/b3awxn7h.js +45 -0
  86. package/dist/shared/b4v57v3x.js +9 -0
  87. package/dist/shared/bdk4sty6.js +58 -0
  88. package/dist/shared/{s6gqfn5j.js → c58secgj.js} +125 -8
  89. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  90. package/dist/shared/d5azex6s.js +6 -0
  91. package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
  92. package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
  93. package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
  94. package/dist/shared/{wxn4b7pp.js → f0f1n281.js} +59 -12
  95. package/dist/shared/{gdh9mrtp.js → g90m9b0g.js} +3 -2
  96. package/dist/shared/{tbszx8mz.js → gwn6v667.js} +2 -2
  97. package/dist/shared/{c8aghh05.js → h15jx9bc.js} +8 -2
  98. package/dist/shared/{3p4b1fg0.js → hazqqecq.js} +17 -93
  99. package/dist/shared/{4ef7mmqf.js → j3rkk835.js} +34 -11
  100. package/dist/shared/{bt9sfq0z.js → j60w0dvr.js} +56 -21
  101. package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
  102. package/dist/shared/k68eedmy.js +85 -0
  103. package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
  104. package/dist/shared/{ckjbyrt9.js → mb2ycs1p.js} +7 -9
  105. package/dist/shared/{tq9afp8d.js → n076ydsc.js} +1 -1
  106. package/dist/shared/{xhh1srw3.js → nx9hh281.js} +50 -32
  107. package/dist/shared/{kdkxh3kb.js → p248q79y.js} +58 -59
  108. package/dist/shared/{990a291j.js → p6bjzq9q.js} +14 -9
  109. package/dist/shared/{h4nqvjkp.js → pjr6xrm5.js} +2 -2
  110. package/dist/shared/pz6rj8ay.js +212 -0
  111. package/dist/shared/{5vdm4sgp.js → q3j1tq7a.js} +36 -23
  112. package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
  113. package/dist/shared/{dxjp14rk.js → r11n8grd.js} +2 -2
  114. package/dist/shared/r1fdd0f6.js +19 -0
  115. package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
  116. package/dist/shared/rhs3xe4k.js +7 -0
  117. package/dist/shared/{jsd07prz.js → rpycarcr.js} +6 -6
  118. package/dist/shared/{xsq7v6wp.js → sbdme3vy.js} +14 -2
  119. package/dist/shared/{xdpj5kkm.js → t3g0k88x.js} +775 -264
  120. package/dist/shared/{ayz64vzj.js → ttmg09vd.js} +708 -19
  121. package/dist/shared/{r6pj4fq2.js → venqrjna.js} +8 -4
  122. package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
  123. package/dist/shared/{9tgmr1gh.js → vmfkhszw.js} +44 -9
  124. package/dist/shared/{wqem4jed.js → wkme6tep.js} +11 -9
  125. package/dist/shared/{qy9rdq0g.js → wt5rzv1h.js} +15 -11
  126. package/dist/shared/wxr8cwns.js +6 -0
  127. package/dist/shared/{bzsjtd7c.js → wynzeggs.js} +19 -14
  128. package/dist/shared/x3jc53kg.js +220 -0
  129. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  130. package/dist/shared/{fyqkk1m0.js → xq0mvhts.js} +10 -9
  131. package/dist/shared/xqve9598.js +53 -0
  132. package/dist/shared/{47xzb9fc.js → y2t3jktd.js} +2 -2
  133. package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
  134. package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
  135. package/dist/shared/{h8ne16fq.js → ys0xzyaj.js} +1 -1
  136. package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
  137. package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
  138. package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
  139. package/dist/shared/{fb00y8fp.js → zm0mafk4.js} +12 -6
  140. package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
  141. package/dist/tooling/cli.js +97 -24
  142. package/dist/tooling/migrate/apply.d.ts +14 -0
  143. package/dist/tooling/migrate/check.d.ts +3 -4
  144. package/dist/tooling/migrate/classify.d.ts +4 -0
  145. package/dist/tooling/migrate/commands.d.ts +1 -2
  146. package/dist/tooling/migrate/config.d.ts +3 -2
  147. package/dist/tooling/migrate/drift.d.ts +1 -1
  148. package/dist/tooling/migrate/estimate.d.ts +14 -1
  149. package/dist/tooling/migrate/files.d.ts +11 -1
  150. package/dist/tooling/migrate/index.js +17 -16
  151. package/dist/tooling/migrate/lint.d.ts +4 -4
  152. package/dist/tooling/migrate/lost.d.ts +28 -0
  153. package/dist/tooling/migrate/paths.d.ts +14 -0
  154. package/dist/tooling/migrate/plan.d.ts +10 -3
  155. package/dist/tooling/migrate/policy.d.ts +11 -0
  156. package/dist/tooling/migrate/runner.d.ts +25 -0
  157. package/dist/tooling/migrate/sandbox.d.ts +57 -0
  158. package/dist/tooling/migrate/sql-lex.d.ts +39 -0
  159. package/dist/tooling/migrate/text.d.ts +42 -0
  160. package/dist/tooling/node-version.d.ts +21 -0
  161. package/dist/tooling/testing/factories.d.ts +10 -0
  162. package/dist/tooling/testing/facts.d.ts +33 -5
  163. package/dist/tooling/testing/index.d.ts +1 -1
  164. package/dist/tooling/testing/index.js +3 -3
  165. package/dist/tooling/testing/isolation.d.ts +15 -8
  166. package/package.json +10 -6
  167. package/dist/shared/0v1g424p.js +0 -26
  168. package/dist/shared/r73pt594.js +0 -426
  169. package/dist/shared/rasvvzgz.js +0 -21
@@ -7,6 +7,23 @@
7
7
  import type { ColumnTenancy } from "../tenancy.js";
8
8
  import type { SchemaHook, TableModel } from "../model.js";
9
9
  import { type AnyTable } from "../table.js";
10
+ /**
11
+ * Reason from `global("reason")` on a view, when the view opted out.
12
+ *
13
+ * The reason is not a catalog field, so the catalog hash does not change.
14
+ *
15
+ * @param model - The view's query model
16
+ * @returns The reason, or `undefined` when the view is not global
17
+ */
18
+ export declare function viewGlobalReason(model: object): string | undefined;
19
+ /**
20
+ * Whether `query` names `name` as an identifier.
21
+ *
22
+ * @param query - Author SQL
23
+ * @param name - Table name, SQL or client
24
+ * @returns Whether the text mentions that name
25
+ */
26
+ export declare function viewMentions(query: string, name: string): boolean;
10
27
  /** What `schema()` passes when it binds a view. */
11
28
  export type ViewInstall = (model: {
12
29
  [name: string]: TableModel;
@@ -18,12 +35,13 @@ export type ViewInstall = (model: {
18
35
  * @param columns - Declared output columns
19
36
  * @param query - Author SQL
20
37
  * @param tenancy - `global("reason")`, when the view opts out
38
+ * @param refresh - `plain` stores no `refresh`. A materialized view is `blocking` or `concurrently`
21
39
  * @returns Methods stored on the declaration
22
40
  */
23
41
  export declare function viewClient(name: string, columns: readonly {
24
42
  readonly name: string;
25
43
  readonly type: string;
26
- }[], query: string, tenancy: unknown): {
44
+ }[], query: string, tenancy: unknown, refresh?: "plain" | "blocking" | "concurrently"): {
27
45
  readonly field: string;
28
46
  readonly install: ViewInstall;
29
47
  readonly hook: SchemaHook;
@@ -43,6 +43,8 @@ export type ViewDeclaration<N extends string = string, C extends readonly ViewCo
43
43
  readonly schema: string;
44
44
  /** Declared columns, for types only. `db.views` reads them. */
45
45
  readonly "~columns"?: C;
46
+ /** `"view"`. A materialized view is `"materialized"`, which adds `refresh()`. */
47
+ readonly "~kind": "view";
46
48
  /**
47
49
  * Catalog record for this declaration.
48
50
  *
@@ -87,6 +89,8 @@ export type MaterializedViewDeclaration<N extends string = string, C extends rea
87
89
  readonly schema: string;
88
90
  /** Declared columns, for types only. `db.views` reads them. */
89
91
  readonly "~columns"?: C;
92
+ /** `"materialized"`. `db.views` adds `refresh()` for this kind only. */
93
+ readonly "~kind": "materialized";
90
94
  /**
91
95
  * Catalog records for this declaration.
92
96
  *
@@ -1,197 +1,33 @@
1
+ import {
2
+ viewClient
3
+ } from "../../../shared/x3jc53kg.js";
1
4
  import {
2
5
  viewObject,
3
6
  materializedViewObject,
4
7
  materializedViewIndex
5
- } from "../../../shared/y005b761.js";
6
- import {
7
- encodeUuid,
8
- decodeUuid
9
- } from "../../../shared/dyj86cmj.js";
10
- import {
11
- snakeCase
12
- } from "../../../shared/jsd07prz.js";
13
- import {
14
- encodeText,
15
- decodeText
16
- } from "../../../shared/4m9fn52z.js";
8
+ } from "../../../shared/zy8r7qvz.js";
17
9
  import {
18
10
  staticNamespace2
19
- } from "../../../shared/t4tbsy9h.js";
11
+ } from "../../../shared/358zsv4r.js";
20
12
  import {
21
13
  OkmError2
22
14
  } from "../../../shared/52szzakm.js";
23
15
 
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
16
  // src/dialects/pg/view/index.ts
186
17
  function view(name, options) {
187
18
  const schema = options.schema ?? "public";
188
19
  const columns = options.columns.map((column) => ({ name: column.name, dataType: column.type }));
189
20
  const query = bodyText(options.query);
190
- const bound = viewClient(name, options.columns, query, options.tenancy);
21
+ const bound = viewClient(name, options.columns, query, options.tenancy, "plain");
22
+ let securityInvoker = false;
191
23
  return {
192
24
  name,
193
25
  schema,
194
- install: bound.install,
26
+ "~kind": "view",
27
+ install(model, tenancy, tables, casing, scoped) {
28
+ bound.install(model, tenancy, tables, casing, scoped);
29
+ securityInvoker = tenancy?.strategy === "rls" && scoped.has(name);
30
+ },
195
31
  hook: bound.hook,
196
32
  contribute(peers, built) {
197
33
  assertPeers(peers);
@@ -203,7 +39,8 @@ function view(name, options) {
203
39
  name,
204
40
  columns,
205
41
  query,
206
- provenance
42
+ provenance,
43
+ ...securityInvoker ? { securityInvoker: true } : {}
207
44
  })
208
45
  ];
209
46
  }
@@ -213,7 +50,12 @@ function materializedView(name, options) {
213
50
  const schema = options.schema ?? "public";
214
51
  const columns = options.columns.map((column) => ({ name: column.name, dataType: column.type }));
215
52
  const query = bodyText(options.query);
216
- const bound = viewClient(name, options.columns, query, options.tenancy);
53
+ if (options.refresh !== undefined && options.refresh !== "concurrently") {
54
+ throw new OkmError2("OKM1120", `Materialized view ${name} refresh must be "concurrently".`, {
55
+ fix: { summary: 'Omit refresh, or pass refresh: "concurrently" with a unique index.' }
56
+ });
57
+ }
58
+ const bound = viewClient(name, options.columns, query, options.tenancy, options.refresh === "concurrently" ? "concurrently" : "blocking");
217
59
  const indexes = options.indexes ?? [];
218
60
  if (options.refresh === "concurrently" && !indexes.some((index) => index.unique === true)) {
219
61
  throw new OkmError2("OKM1822", `Materialized view ${name} refreshes concurrently and has no unique index.`, { fix: { summary: "Add a unique index, or refresh without CONCURRENTLY." } });
@@ -229,6 +71,7 @@ function materializedView(name, options) {
229
71
  return {
230
72
  name,
231
73
  schema,
74
+ "~kind": "materialized",
232
75
  install: bound.install,
233
76
  hook: bound.hook,
234
77
  contribute(peers, built) {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `REFRESH MATERIALIZED VIEW` for one view handle.
3
+ *
4
+ * Loaded the first time `refresh()` runs. A featureless app, and an app that
5
+ * never calls it, do not import this module. The name is unqualified so the
6
+ * session `search_path` selects the schema, matching a view `find`.
7
+ */
8
+ /**
9
+ * Refreshes one materialized view on the primary.
10
+ *
11
+ * `CONCURRENTLY` when the declaration asked for it. The statement timeout is
12
+ * the client's, the same ceiling a write uses. A database failure is mapped
13
+ * and logged like any other statement.
14
+ *
15
+ * The refresh runs on a reserved primary connection with `app.unscoped` set
16
+ * in that same transaction. Under `FORCE ROW LEVEL SECURITY` the owner would
17
+ * otherwise refresh zero rows. `CONCURRENTLY` cannot run inside a transaction,
18
+ * so that form sets the session value and clears it before release.
19
+ *
20
+ * @param session - Pool, timeout, and logger from the client
21
+ * @param name - SQL name of the materialized view
22
+ * @param concurrently - True when the view declares `refresh: "concurrently"`
23
+ */
24
+ export declare function refreshView(session: object, name: string, concurrently: boolean): Promise<void>;
@@ -12,7 +12,7 @@ import { type Session } from "./client.js";
12
12
  * Runs write handles as one atomic unit.
13
13
  *
14
14
  * @param session - Client session
15
- * @param ops - Write handles: `insert`, `update`, `delete`, `archive`. A `restore`, or a write with `expect`, is OKM1121 (D183)
15
+ * @param ops - Write handles: `insert`, `update`, `delete`, `archive`. A `restore`, a write with `expect`, or `refresh()` is OKM1121 (D183, D239)
16
16
  * @param options - `signal` and `timeout` for the whole batch
17
17
  * @param replica - Set by `.replica()`. A batch needs the primary (OKM1840)
18
18
  * @returns One result per operation, in order
@@ -37,9 +37,14 @@ export type Session = {
37
37
  };
38
38
  /** Tenant value or an unscoped reason. Absent on the root client. */
39
39
  readonly scope?: CallScope;
40
- /** Shared close. `for()` and `unscoped()` reuse the same promise. */
40
+ /**
41
+ * Shared close. `for()` and `unscoped()` reuse the same promise.
42
+ *
43
+ * `early` is set when `close()` runs before `connected` settles (QA-M5).
44
+ */
41
45
  readonly closing: {
42
46
  current: Promise<void> | undefined;
47
+ early: boolean;
43
48
  };
44
49
  };
45
50
  /** Per-call modifiers of a read. */
@@ -18,9 +18,10 @@ export type ConflictPlan = {
18
18
  * @param table - Target table
19
19
  * @param value - The option the caller passed
20
20
  * @param allow - Guarded fields this write may set
21
+ * @param tenantKey - The tenant key, or every tenant key when the schema has several. A unique that includes them matches when `on` names the other columns. The target keeps the keys, in the unique's order. Naming a key is OKM1104.
21
22
  * @returns The plan, or `undefined` for the default `"error"`
22
23
  */
23
- export declare function readConflict(table: Indexed, value: unknown, allow?: ReadonlySet<string>): ConflictPlan | undefined;
24
+ export declare function readConflict(table: Indexed, value: unknown, allow?: ReadonlySet<string>, tenantKey?: string | readonly string[]): ConflictPlan | undefined;
24
25
  /**
25
26
  * Appends the conflict clause, or wraps a first-or-create insert.
26
27
  *
@@ -29,3 +29,12 @@ export declare function assertCompatible(pool: DriverPool, schema: QuerySchema,
29
29
  readonly catalog?: CatalogArtifact;
30
30
  readonly catalogDir?: string;
31
31
  }, options: ExecuteOptions | undefined): Promise<void>;
32
+ /**
33
+ * Directory of `catalog.hash` and `catalog.json`.
34
+ *
35
+ * A trailing `/` or `\` is removed so the joined file name is one path.
36
+ *
37
+ * @param catalogDir - Directory the caller passed, or the default `.okm`
38
+ * @returns The directory, without a trailing slash
39
+ */
40
+ export declare function artifactDir(catalogDir: string | undefined): string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Refuses a positive timeout on a driver that cannot cancel.
3
+ *
4
+ * `tx({ timeout })` and `batch({ timeout })` load this with the call. The
5
+ * connect graph does not. The message stays the one {@link noTimeout} throws.
6
+ */
7
+ import type { DriverPool } from "../contracts/driver.js";
8
+ /**
9
+ * Throws OKM1120 when `timeout` is positive and the driver cannot cancel.
10
+ *
11
+ * @param pool - Driver the call will use
12
+ * @param timeout - Milliseconds, when the caller passed one
13
+ */
14
+ export declare function refuseUncancellableTimeout(pool: DriverPool, timeout: number | undefined): Promise<void>;
@@ -1,22 +1,23 @@
1
+ import"../../shared/8dqhth8c.js";
1
2
  import {
2
3
  DriverError2
3
- } from "../../shared/kyxb62mc.js";
4
+ } from "../../shared/31vm914h.js";
4
5
  import {
5
6
  open5
6
- } from "../../shared/sf8m6tfy.js";
7
+ } from "../../shared/32cnm7kn.js";
7
8
  import {
8
9
  BUNSQL_CAPABILITIES,
9
10
  readCapability2,
10
11
  hasCapability2
11
- } from "../../shared/rrstp3kb.js";
12
+ } from "../../shared/cgbamx3e.js";
12
13
  import {
13
14
  createClient
14
- } from "../../shared/bt9sfq0z.js";
15
+ } from "../../shared/j60w0dvr.js";
15
16
 
16
17
  // src/runtime/pg/bun.ts
17
18
  function connect(target, options) {
18
19
  if (typeof target === "object" && !isPool(target)) {
19
- return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open5));
20
+ return import("../../shared/929h19xp.js").then((mod) => mod.default(target, options, open5));
20
21
  }
21
22
  const owned = !isPool(target);
22
23
  const pool = isPool(target) ? target : open5({
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  DriverError2
3
- } from "../../shared/kyxb62mc.js";
3
+ } from "../../shared/31vm914h.js";
4
4
  import {
5
5
  open4
6
- } from "../../shared/6p5k20sm.js";
6
+ } from "../../shared/1z04fa85.js";
7
7
  import {
8
8
  NODE_POSTGRES_CAPABILITIES,
9
9
  readCapability2,
10
10
  hasCapability2
11
- } from "../../shared/rrstp3kb.js";
11
+ } from "../../shared/cgbamx3e.js";
12
12
  import {
13
13
  createClient
14
- } from "../../shared/bt9sfq0z.js";
14
+ } from "../../shared/j60w0dvr.js";
15
15
 
16
16
  // src/runtime/pg/pg.ts
17
17
  function connect(target, options) {
18
18
  if (typeof target === "object" && !isPool(target)) {
19
- return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open4));
19
+ return import("../../shared/929h19xp.js").then((mod) => mod.default(target, options, open4));
20
20
  }
21
21
  const owned = !isPool(target);
22
22
  const pool = isPool(target) ? target : open4({
@@ -1,22 +1,23 @@
1
+ import"../../shared/8dqhth8c.js";
1
2
  import {
2
3
  DriverError2
3
- } from "../../shared/kyxb62mc.js";
4
+ } from "../../shared/31vm914h.js";
4
5
  import {
5
6
  open3
6
- } from "../../shared/fa9vt245.js";
7
+ } from "../../shared/5j36z8dn.js";
7
8
  import {
8
9
  PGLITE_CAPABILITIES,
9
10
  readCapability2,
10
11
  hasCapability2
11
- } from "../../shared/rrstp3kb.js";
12
+ } from "../../shared/cgbamx3e.js";
12
13
  import {
13
14
  createClient
14
- } from "../../shared/bt9sfq0z.js";
15
+ } from "../../shared/j60w0dvr.js";
15
16
 
16
17
  // src/runtime/pg/pglite.ts
17
18
  async function connect(target, options) {
18
19
  if (typeof target === "object" && !isPool(target)) {
19
- return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open3));
20
+ return import("../../shared/929h19xp.js").then((mod) => mod.default(target, options, open3));
20
21
  }
21
22
  const pool = isPool(target) ? target : await open3({
22
23
  ...target !== undefined ? { dataDir: target } : {},
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  DriverError2
3
- } from "../../shared/kyxb62mc.js";
3
+ } from "../../shared/31vm914h.js";
4
4
  import {
5
5
  open2
6
- } from "../../shared/wqem4jed.js";
6
+ } from "../../shared/wkme6tep.js";
7
7
  import {
8
8
  POSTGRESJS_CAPABILITIES,
9
9
  readCapability2,
10
10
  hasCapability2
11
- } from "../../shared/rrstp3kb.js";
11
+ } from "../../shared/cgbamx3e.js";
12
12
  import {
13
13
  createClient
14
- } from "../../shared/bt9sfq0z.js";
14
+ } from "../../shared/j60w0dvr.js";
15
15
 
16
16
  // src/runtime/pg/postgresjs.ts
17
17
  function connect(target, options) {
18
18
  if (typeof target === "object" && !isPool(target)) {
19
- return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open2));
19
+ return import("../../shared/929h19xp.js").then((mod) => mod.default(target, options, open2));
20
20
  }
21
21
  const owned = !isPool(target);
22
22
  const pool = isPool(target) ? target : open2({
@@ -18,6 +18,7 @@ export type RouteConstraint = "auto" | "primary" | "replica";
18
18
  */
19
19
  export type CallScope = {
20
20
  readonly value: string;
21
+ readonly values?: Readonly<Record<string, string>>;
21
22
  } | {
22
23
  readonly unscoped: string;
23
24
  };
@@ -249,6 +250,15 @@ export declare function emitJoin(sink: Sink, parent: string, child: string, rela
249
250
  export declare function emitWhere(schema: QuerySchema, table: Indexed, where: unknown, sink: Sink, alias: string, depth: number, appended?: boolean, extra?: Keyset): void;
250
251
  export declare function emitOrder(table: Indexed, orderBy: unknown, sink: Sink, alias: string): void;
251
252
  export declare function emitPrimaryOrder(table: Indexed, sink: Sink, alias: string): void;
253
+ /**
254
+ * Refuses a hidden field in `where` or `orderBy`.
255
+ *
256
+ * `"filter"` is `hidden({ filterable: true })` and is allowed here.
257
+ * Aggregate loads this on its first call and applies it before the groupBy rule.
258
+ *
259
+ * @param column - The field the caller named
260
+ */
261
+ export declare function refuseHidden(column: ColumnModel): void;
252
262
  export type ParsedInclude = {
253
263
  readonly name: string;
254
264
  readonly relation: RelationModel;
@@ -288,6 +298,16 @@ export declare function indexes(schema: QuerySchema): ReadonlyMap<string, Indexe
288
298
  export declare function quote(name: string): string;
289
299
  export declare function list(names: readonly string[]): string;
290
300
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
301
+ /**
302
+ * Throws OKM1121 for an `or()` that is not an array, or for an empty branch.
303
+ *
304
+ * The write and archive predicate walk uses the same throw. It lives here
305
+ * because the read planner throws it too, and that planner stays in this module.
306
+ *
307
+ * @param empty - `true` when a branch has no predicate
308
+ * @returns Never
309
+ */
310
+ export declare function orFail(empty: boolean): never;
291
311
  /**
292
312
  * Records the fix text for a code thrown from a chunk that loads on first use.
293
313
  *
@@ -303,6 +323,7 @@ export declare function registerFailFix(code: QueryCode, summary: string): void;
303
323
  *
304
324
  * @param code - Spec code
305
325
  * @param message - What failed
326
+ * @param fix - Fix sentence for this call. Omitted, the sentence registered for `code` is used
306
327
  */
307
- export declare function fail(code: QueryCode, message: string): never;
328
+ export declare function fail(code: QueryCode, message: string, fix?: string): never;
308
329
  export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Whether a `where` constrains a row.
3
+ *
4
+ * Write and archive load this module. A read does not. The walk is the same
5
+ * one those paths used when it lived in the planner.
6
+ */
7
+ /**
8
+ * Whether `where` constrains a row.
9
+ *
10
+ * A field value is a leaf. Anything other than `undefined` counts, including
11
+ * `null`, objects, arrays, and operators. The walk does not enter a field value.
12
+ * It enters the where object's own keys, `and`, `or`, and `not` when `not` is
13
+ * the where itself. An `or()` branch with no predicate is OKM1121. `or([])`
14
+ * matches nothing.
15
+ *
16
+ * @param where - Caller filter, or one `and` / `or` / `not` operand
17
+ * @returns `false` when the filter matches every row
18
+ */
19
+ export declare function effectivePredicate(where: unknown): boolean;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  callerLocation
3
- } from "../../shared/k7dwkhvq.js";
3
+ } from "../../shared/y6jdds7g.js";
4
4
  import {
5
5
  installSafetyHook
6
6
  } from "../../shared/a5sxz52y.js";
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Composite tenancy (`compositeTenancy()`).
3
+ *
4
+ * Every tenant table carries the key list, in that order. A column-only app
5
+ * does not import this file. `via()` is a separate import: this file calls a
6
+ * table's own `rewrite` and does not load the path compiler.
7
+ */
8
+ import type { ColumnTenancy } from "../../dialects/pg/tenancy.js";
9
+ /**
10
+ * Composite tenancy for `schema({ tenancy })`.
11
+ *
12
+ * Every table gains each key unless it passes `global("reason")` or `via()`.
13
+ * The keys are guarded uuids, in this order. Insert fills them from `for()`.
14
+ * Reads and writes filter on every key.
15
+ *
16
+ * @typeParam Keys - Field names, at least one
17
+ * @param input - The key list and `uuid`
18
+ * @returns The object `schema()` applies
19
+ */
20
+ export declare function compositeTenancy<const Keys extends readonly [string, ...string[]]>(input: {
21
+ readonly key: Keys;
22
+ readonly type: "uuid";
23
+ }): ColumnTenancy & {
24
+ readonly key: Keys[number];
25
+ readonly keys: Keys;
26
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What a table tenancy `rewrite` receives.
3
+ *
4
+ * `columnTenancy()` and `compositeTenancy()` pass it. `via()` reads it.
5
+ * This file is types only, so it is not in a bundle.
6
+ */
7
+ import type { AnyTable } from "../../dialects/pg/table.js";
8
+ import type { ColumnTenancy } from "../../dialects/pg/tenancy.js";
9
+ /** Context for one table whose tenancy mark carries `rewrite`. */
10
+ export type TableRewriteContext = {
11
+ readonly tables: readonly AnyTable[];
12
+ readonly keys: readonly string[];
13
+ readonly encodes: readonly ((value: string) => string)[];
14
+ readonly api: ColumnTenancy;
15
+ readonly extras: Map<string, readonly string[]>;
16
+ };
@@ -22,16 +22,20 @@ export declare function global(reason: string): {
22
22
  /**
23
23
  * Column tenancy for `schema({ tenancy })`.
24
24
  *
25
- * Every table gains `key` unless it passes `global("reason")`. The key is a
26
- * guarded uuid. Insert fills it from `for()`. Reads and writes filter on it.
25
+ * Every table gains `key` unless it passes `global("reason")` or `via()`.
26
+ * The key is a guarded uuid. Insert fills it from `for()`. Reads and writes
27
+ * filter on it. A list of keys is `compositeTenancy()`.
27
28
  *
28
29
  * @typeParam Key - Field name of the tenant key
29
30
  * @param input - Field name and `uuid`
30
31
  * @returns The object `schema()` applies
31
32
  */
32
- export declare function columnTenancy<const Key extends string>(input: {
33
- readonly key: Key;
33
+ export declare function columnTenancy<const Key>(input: {
34
+ readonly key: Key extends readonly unknown[] ? "columnTenancy() takes one key. Use compositeTenancy()." : Key extends string ? Key : "columnTenancy() key must be a field name.";
34
35
  readonly type: "uuid";
35
36
  }): ColumnTenancy & {
36
- readonly key: Key;
37
+ readonly key: Key extends string ? Key : string;
37
38
  };
39
+ export { compositeTenancy } from "./composite.js";
40
+ export { via } from "./via.js";
41
+ export { RlsRoleError, rlsTenancy, type RlsRoleReason } from "./rls.js";