okmodel 0.5.0 → 0.5.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 (80) hide show
  1. package/README.md +9 -6
  2. package/dist/dialects/pg/filter-parse.d.ts +8 -2
  3. package/dist/dialects/pg/fn/index.js +8 -0
  4. package/dist/dialects/pg/index.js +8 -2
  5. package/dist/dialects/pg/ops/or.d.ts +1 -1
  6. package/dist/dialects/pg/temporal.d.ts +5 -76
  7. package/dist/dialects/pg/view/index.js +1 -1
  8. package/dist/runtime/client.d.ts +6 -1
  9. package/dist/runtime/conflict.d.ts +2 -1
  10. package/dist/runtime/pg/bun.js +5 -5
  11. package/dist/runtime/pg/pg.js +5 -5
  12. package/dist/runtime/pg/pglite.js +5 -5
  13. package/dist/runtime/pg/postgresjs.js +5 -5
  14. package/dist/runtime/plan.d.ts +13 -0
  15. package/dist/runtime/tenancy/index.js +1 -1
  16. package/dist/runtime/traits/index.js +4 -4
  17. package/dist/runtime/validate/index.js +3 -3
  18. package/dist/shared/{ckjbyrt9.js → 0t22wwh1.js} +5 -9
  19. package/dist/shared/{jdgqqx31.js → 2pqx26h8.js} +23 -8
  20. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  21. package/dist/shared/{9tgmr1gh.js → 32zqc1dw.js} +18 -7
  22. package/dist/shared/{47xzb9fc.js → 47x8x9ze.js} +1 -1
  23. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  24. package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
  25. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  26. package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
  27. package/dist/shared/{bzsjtd7c.js → 6mnp99hx.js} +7 -6
  28. package/dist/shared/{ykv7wh5b.js → 6qd27mx0.js} +2 -2
  29. package/dist/shared/{h4nqvjkp.js → 6rk2gen5.js} +2 -2
  30. package/dist/shared/{r73pt594.js → 7zwwyh0v.js} +32 -10
  31. package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
  32. package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
  33. package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
  34. package/dist/shared/{xsq7v6wp.js → aj2qxyhj.js} +2 -2
  35. package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
  36. package/dist/shared/{xhh1srw3.js → cctw17hj.js} +23 -8
  37. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  38. package/dist/shared/{tq9afp8d.js → cr74e96e.js} +1 -1
  39. package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
  40. package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
  41. package/dist/shared/{ayz64vzj.js → ga3sfy93.js} +357 -3
  42. package/dist/shared/{qgvt8nx1.js → gbtcc2d7.js} +44 -3
  43. package/dist/shared/{kdkxh3kb.js → gde73es6.js} +5 -7
  44. package/dist/shared/{bt9sfq0z.js → hf22e0wt.js} +51 -17
  45. package/dist/shared/{990a291j.js → janx09m5.js} +6 -5
  46. package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
  47. package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
  48. package/dist/shared/{r6pj4fq2.js → kfjp0gck.js} +2 -2
  49. package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
  50. package/dist/shared/{h8ne16fq.js → nkaxvt9q.js} +1 -1
  51. package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
  52. package/dist/shared/{nkc9sfqb.js → nxdp2ze3.js} +40 -4
  53. package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
  54. package/dist/shared/{qy9rdq0g.js → ps7a6h14.js} +6 -6
  55. package/dist/shared/{rasvvzgz.js → q58y17hy.js} +5 -5
  56. package/dist/shared/{p7pfx624.js → qm7cdtvr.js} +22 -9
  57. package/dist/shared/{xdpj5kkm.js → qv2ydck9.js} +149 -209
  58. package/dist/shared/{k7cc9nzk.js → r2sp8hrc.js} +6 -3
  59. package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
  60. package/dist/shared/{fyqkk1m0.js → sdc7r2ww.js} +6 -5
  61. package/dist/shared/{wxn4b7pp.js → sycnx8gt.js} +42 -5
  62. package/dist/shared/{dxjp14rk.js → tm5ab8we.js} +2 -2
  63. package/dist/shared/{g516zdsb.js → v36c5g6p.js} +78 -19
  64. package/dist/shared/wq9n6pwr.js +139 -0
  65. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  66. package/dist/shared/{922qbgnw.js → zqrnhwsq.js} +3 -3
  67. package/dist/tooling/cli.js +68 -21
  68. package/dist/tooling/migrate/commands.d.ts +1 -2
  69. package/dist/tooling/migrate/drift.d.ts +1 -1
  70. package/dist/tooling/migrate/estimate.d.ts +14 -1
  71. package/dist/tooling/migrate/index.js +8 -7
  72. package/dist/tooling/migrate/lint.d.ts +3 -3
  73. package/dist/tooling/migrate/plan.d.ts +7 -3
  74. package/dist/tooling/migrate/policy.d.ts +11 -0
  75. package/dist/tooling/migrate/sql-lex.d.ts +22 -0
  76. package/dist/tooling/testing/factories.d.ts +10 -0
  77. package/dist/tooling/testing/index.js +1 -1
  78. package/dist/tooling/testing/isolation.d.ts +4 -3
  79. package/package.json +6 -5
  80. package/dist/shared/0v1g424p.js +0 -26
package/README.md CHANGED
@@ -80,9 +80,11 @@ bun add okmodel @electric-sql/pglite
80
80
 
81
81
  Bun.sql needs no extra package: `import { connect } from "okmodel/pg/bun"`. okmodel has no runtime dependencies. Each installed driver is an optional peer.
82
82
 
83
+ The `okm` bin's shebang is node, so `bunx okm` runs it with Node even inside a Bun project. Run `bun --bun okm` when the command must use the Bun runtime (Bun.sql runs only there). A `.ts` config file under plain Node needs Node 22.18 or newer for type stripping.
84
+
83
85
  ## Quickstart
84
86
 
85
- This builds two tables, applies them, and runs one script. The script writes an author and a note, then reads the note with its author. `DATABASE_URL` is a direct Postgres URL, not a pooler.
87
+ This builds two tables, applies them, and runs one script. The script writes an author and a note, then reads the note with its author. `DATABASE_URL` is a direct Postgres URL, not a pooler. Port `6432` and port `6543` count as a pooler. Apply, seed, and check also refuse when the backend pid changes between two statements (OKM1854), unless `--allow-pooler` is set.
86
88
 
87
89
  ### Configure
88
90
 
@@ -373,7 +375,7 @@ if (!(await db.notes.exists({ where: { id: note.id } }))) {
373
375
 
374
376
  ### Update
375
377
 
376
- `update` changes the columns in `set` on the rows that match `where`. A call with no `where` is OKM1102. `.all(reason)` is the form that means every row.
378
+ `update` changes the columns in `set` on the rows that match `where`. A write with no effective predicate, including undefined values, is OKM1102. An empty `or()` branch is OKM1121. `.all(reason)` is the form that means every row.
377
379
 
378
380
  ```ts
379
381
  // where picks the row. set names the new title.
@@ -382,7 +384,7 @@ await db.notes.update({ where: { id: note.id }, set: { title: "next" } });
382
384
 
383
385
  ### Delete
384
386
 
385
- `delete` removes the rows that match `where`. A call with no `where` is OKM1102. `.all(reason)` is the form that means every row.
387
+ `delete` removes the rows that match `where`. A write with no effective predicate, including undefined values, is OKM1102. An empty `or()` branch is OKM1121. `.all(reason)` is the form that means every row.
386
388
 
387
389
  ```ts
388
390
  // where picks the row. Without it the call is refused.
@@ -828,7 +830,7 @@ export const app = schema({
828
830
 
829
831
  ### Views
830
832
 
831
- A view is a saved query. You give it the SQL and the columns that query returns. `db.views.active.find({ limit: 20 })` reads `active`. That handle has no insert or update. There is no query-builder form yet. `okm check` reprints the query on the connected server before it compares, so a difference that is only how that server prints the query is not drift. A materialized view that refreshes while people are reading it needs a unique index, or the declaration is OKM1822. See [known limits](https://github.com/omqkhafi/okmodel/blob/main/docs/known-limits.md).
833
+ A view is a saved query. You give it the SQL and the columns that query returns. `db.views.active.find({ limit: 20 })` reads `active`. That handle has no insert or update. View handles are camelCased: a view named `active_projects` reads as `db.views.activeProjects`. There is no query-builder form yet. `okm check` reprints the query on the connected server before it compares, so a difference that is only how that server prints the query is not drift. A materialized view refreshes with `REFRESH MATERIALIZED VIEW`: the plan populates it with a plain refresh first (Postgres rejects `CONCURRENTLY` on an empty view), then refreshes concurrently when the declaration sets `refresh: "concurrently"`. A materialized view that refreshes while people are reading it needs a unique index, or the declaration is OKM1822. See [known limits](https://github.com/omqkhafi/okmodel/blob/main/docs/known-limits.md).
832
834
 
833
835
  `views.ts`:
834
836
 
@@ -945,7 +947,7 @@ update "public"."notes" set "title" = 'noted' where "title" = 'hello' and ("id"
945
947
 
946
948
  ### Testing
947
949
 
948
- `okmodel/testing` opens a real pool. `factories` insert rows, `expectQueries` counts statements, and `isolation()` checks that tenant A cannot see tenant B. `okm seed <file>` calls the file's default export with that harness. The target must already be migrated. A protected target is refused unless `--allow-protected`.
950
+ `okmodel/testing` opens a real pool. `factories` insert rows, `expectQueries` counts statements, and `isolation()` checks that tenant A cannot see the row it inserted for tenant B. Each call uses a new pair of tenant ids, deletes what it inserted, and names a factory that is missing. `okm seed <file>` calls the file's default export with that harness. The target must already be migrated. A protected target is refused unless `--allow-protected`.
949
951
 
950
952
  `factory.ts`:
951
953
 
@@ -1071,7 +1073,7 @@ The worked example is a project tracker: workspaces as tenants, archive, a funct
1071
1073
 
1072
1074
  ### Startup
1073
1075
 
1074
- `connect()` compares the app with `okm_history`. A database that is ahead by expand migrations still opens. Ahead by a contract migration, or behind the app, is OKM1520 and names the migration. `okm migrate status` prints the same states, plus `failed at step N (resume with okm migrate apply)`.
1076
+ `connect()` compares the app with `okm_history`. A database that is ahead by expand migrations still opens. Ahead by a contract migration, or behind the app, is OKM1520 and names the migration. `okm migrate status` prints the same states, plus `failed at step N (resume with okm migrate apply)`, where N is the stored step index. Apply's own `failed at step N` counts from 1, the same number the linter prints.
1075
1077
 
1076
1078
  ### Protected targets
1077
1079
 
@@ -1144,6 +1146,7 @@ Status is on the [board](https://github.com/users/omqkhafi/projects/1). Each rel
1144
1146
  - [x] [0.3](https://github.com/omqkhafi/okmodel/milestone/3) — Extensions, domains, functions, triggers, views, roles, and grants.
1145
1147
  - [x] [0.4](https://github.com/omqkhafi/okmodel/milestone/4) — Safer migration plans, backfill, drift checks, provisioning, reference data, and a testing package.
1146
1148
  - [x] [0.5](https://github.com/omqkhafi/okmodel/milestone/5) — A primary with replicas, read routing, and a reference app.
1149
+ - [ ] [0.5.1](https://github.com/omqkhafi/okmodel/milestone/7) — QA fixes for 0.5.0.
1147
1150
 
1148
1151
  `okmodel/internal` has no stability promise. Names on that subpath can change or disappear in any release.
1149
1152
 
@@ -3,13 +3,19 @@
3
3
  *
4
4
  * Loaded the first time `parse()` is called. `filters()` validates the spec
5
5
  * before this module exists.
6
+ *
7
+ * A request key is equality when the value is a scalar and `eq` is allowed.
8
+ * An object value names operators: `eq`, `in` (`inList`), `notIn`, `lt`,
9
+ * `lte`, `gt`, `gte`, `between`, `startsWith`, `endsWith`, `contains`,
10
+ * `like`, and `ilike`. `sort` is a field name from the sort list.
6
11
  */
7
12
  import type { ParsedFilters } from "./table.js";
8
13
  /**
9
14
  * Reads one request object.
10
15
  *
11
- * A plain value is equality when `eq` is allowlisted. `sort` is a field name
12
- * from the sort list. JSON cannot add an operator.
16
+ * Lookups use own properties, so `constructor`, `__proto__`, `toString`, and
17
+ * `hasOwnProperty` are ordinary keys. A key that is not in `allow` is OKM1123.
18
+ * `sort` is the exception.
13
19
  *
14
20
  * @param allow - Fields and the operators each one accepts
15
21
  * @param sort - Fields a caller may sort by
@@ -5,6 +5,9 @@ import {
5
5
  import {
6
6
  staticNamespace2
7
7
  } from "../../../shared/t4tbsy9h.js";
8
+ import {
9
+ assertIdentifier2
10
+ } from "../../../shared/nfzc5z0w.js";
8
11
  import {
9
12
  OkmError2
10
13
  } from "../../../shared/52szzakm.js";
@@ -26,6 +29,11 @@ function fn(name, options) {
26
29
  fix: { summary: "Set search_path on the function to the schemas it is allowed to see." }
27
30
  });
28
31
  }
32
+ if (options.searchPath !== undefined && options.searchPath.trim().length > 0) {
33
+ for (const part of options.searchPath.split(",")) {
34
+ assertIdentifier2(part.trim(), `function ${name} search_path`);
35
+ }
36
+ }
29
37
  const argTypes = (options.arguments ?? []).map((argument) => argument.type);
30
38
  const schema = options.schema ?? "public";
31
39
  return {
@@ -20,7 +20,7 @@ import {
20
20
  many2,
21
21
  manyThrough2,
22
22
  schema2
23
- } from "../../shared/3p4b1fg0.js";
23
+ } from "../../shared/5enne4ma.js";
24
24
  import {
25
25
  domainType
26
26
  } from "../../shared/g7ez901n.js";
@@ -32,7 +32,7 @@ import {
32
32
  table2,
33
33
  index2,
34
34
  sql2
35
- } from "../../shared/jsd07prz.js";
35
+ } from "../../shared/ntneex2a.js";
36
36
  import {
37
37
  text2,
38
38
  varchar2,
@@ -59,6 +59,7 @@ import {
59
59
  tag
60
60
  } from "../../shared/cn0n68ft.js";
61
61
  import {
62
+ OkmError2,
62
63
  catalogError2
63
64
  } from "../../shared/52szzakm.js";
64
65
  var __defProp = Object.defineProperty;
@@ -909,6 +910,11 @@ function notIn(values) {
909
910
  }
910
911
  // src/dialects/pg/ops/or.ts
911
912
  function or(branches) {
913
+ if (arguments.length !== 1 || !Array.isArray(branches)) {
914
+ throw new OkmError2("OKM1121", "Pass an array: or([a, b]).", {
915
+ fix: { summary: "Pass an array: or([a, b])." }
916
+ });
917
+ }
912
918
  return tag("or", branches);
913
919
  }
914
920
  // src/dialects/pg/ops/overlaps.ts
@@ -3,7 +3,7 @@ import { type Or } from "../operators.js";
3
3
  * Disjunction of where objects.
4
4
  *
5
5
  * @typeParam V - One branch
6
- * @param branches - Where objects, or-ed
6
+ * @param branches - Where objects, or-ed. One array: `or([a, b])`
7
7
  * @returns A tagged operator
8
8
  */
9
9
  export declare function or<const V>(branches: readonly V[]): Or<V>;
@@ -114,100 +114,29 @@ export declare function decodeDuration(wire: string): Temporal.Duration;
114
114
  */
115
115
  export declare function intervalFields(fields: string): string;
116
116
  /**
117
- * Temporal is in the runtime (Node and Bun) and not in this repository's
118
- * TypeScript libs. These declarations name the methods the codecs call.
117
+ * Empty names so `Temporal.Instant` and the other codec types merge with
118
+ * TypeScript's Temporal lib and with `temporal-polyfill`. Method signatures
119
+ * live in `temporal.local.d.ts` for this repository's typecheck only. They
120
+ * are not shipped: a second `toString` or `var Temporal` conflicts with those
121
+ * libraries (QA-L2).
119
122
  */
120
123
  declare global {
121
124
  /** Instant, plain date-time, date, time, and duration. */
122
125
  namespace Temporal {
123
126
  /** UTC instant. */
124
127
  interface Instant {
125
- /**
126
- * @param options - Fractional digits to keep
127
- * @returns ISO-8601 text
128
- */
129
- toString(options?: {
130
- readonly fractionalSecondDigits?: number;
131
- }): string;
132
128
  }
133
129
  /** Date and time without a zone. */
134
130
  interface PlainDateTime {
135
- /**
136
- * @param options - Fractional digits to keep
137
- * @returns ISO-8601 text
138
- */
139
- toString(options?: {
140
- readonly fractionalSecondDigits?: number;
141
- }): string;
142
131
  }
143
132
  /** Calendar date. */
144
133
  interface PlainDate {
145
- /**
146
- * @returns `YYYY-MM-DD`
147
- */
148
- toString(): string;
149
- /**
150
- * @param duration - Days to add
151
- * @returns A later or earlier date
152
- */
153
- add(duration: {
154
- readonly days: number;
155
- }): Temporal.PlainDate;
156
134
  }
157
135
  /** Time of day. */
158
136
  interface PlainTime {
159
- /**
160
- * @param options - Fractional digits to keep
161
- * @returns Time text
162
- */
163
- toString(options?: {
164
- readonly fractionalSecondDigits?: number;
165
- }): string;
166
137
  }
167
138
  /** ISO-8601 duration. */
168
139
  interface Duration {
169
- /**
170
- * @returns ISO-8601 duration text
171
- */
172
- toString(): string;
173
140
  }
174
141
  }
175
- /** Temporal constructors used by the codecs. */
176
- var Temporal: {
177
- readonly Instant: {
178
- /**
179
- * @param item - ISO-8601 instant
180
- * @returns An instant
181
- */
182
- from(item: string): Temporal.Instant;
183
- };
184
- readonly PlainDateTime: {
185
- /**
186
- * @param item - ISO-8601 date-time
187
- * @returns A plain date-time
188
- */
189
- from(item: string): Temporal.PlainDateTime;
190
- };
191
- readonly PlainDate: {
192
- /**
193
- * @param item - `YYYY-MM-DD`
194
- * @returns A date
195
- */
196
- from(item: string): Temporal.PlainDate;
197
- };
198
- readonly PlainTime: {
199
- /**
200
- * @param item - Time text
201
- * @returns A time
202
- */
203
- from(item: string): Temporal.PlainTime;
204
- };
205
- readonly Duration: {
206
- /**
207
- * @param item - ISO-8601 duration, or numeric fields
208
- * @returns A duration
209
- */
210
- from(item: string | Readonly<Record<string, number>>): Temporal.Duration;
211
- };
212
- };
213
142
  }
@@ -9,7 +9,7 @@ import {
9
9
  } from "../../../shared/dyj86cmj.js";
10
10
  import {
11
11
  snakeCase
12
- } from "../../../shared/jsd07prz.js";
12
+ } from "../../../shared/ntneex2a.js";
13
13
  import {
14
14
  encodeText,
15
15
  decodeText
@@ -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 on a tenant schema, which every unique includes
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): ConflictPlan | undefined;
24
25
  /**
25
26
  * Appends the conflict clause, or wraps a first-or-create insert.
26
27
  *
@@ -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
  open5
6
- } from "../../shared/sf8m6tfy.js";
6
+ } from "../../shared/k8v5v5wj.js";
7
7
  import {
8
8
  BUNSQL_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/hf22e0wt.js";
15
15
 
16
16
  // src/runtime/pg/bun.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, open5));
19
+ return import("../../shared/nxdp2ze3.js").then((mod) => mod.default(target, options, open5));
20
20
  }
21
21
  const owned = !isPool(target);
22
22
  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/96qcj21c.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/hf22e0wt.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/nxdp2ze3.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,22 @@
1
1
  import {
2
2
  DriverError2
3
- } from "../../shared/kyxb62mc.js";
3
+ } from "../../shared/31vm914h.js";
4
4
  import {
5
5
  open3
6
- } from "../../shared/fa9vt245.js";
6
+ } from "../../shared/87jvvhnt.js";
7
7
  import {
8
8
  PGLITE_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/hf22e0wt.js";
15
15
 
16
16
  // src/runtime/pg/pglite.ts
17
17
  async function connect(target, options) {
18
18
  if (typeof target === "object" && !isPool(target)) {
19
- return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open3));
19
+ return import("../../shared/nxdp2ze3.js").then((mod) => mod.default(target, options, open3));
20
20
  }
21
21
  const pool = isPool(target) ? target : await open3({
22
22
  ...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/krxx77g0.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/hf22e0wt.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/nxdp2ze3.js").then((mod) => mod.default(target, options, open2));
20
20
  }
21
21
  const owned = !isPool(target);
22
22
  const pool = isPool(target) ? target : open2({
@@ -288,6 +288,19 @@ export declare function indexes(schema: QuerySchema): ReadonlyMap<string, Indexe
288
288
  export declare function quote(name: string): string;
289
289
  export declare function list(names: readonly string[]): string;
290
290
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
291
+ /**
292
+ * Whether `where` constrains a row.
293
+ *
294
+ * A field value is a leaf. Anything other than `undefined` counts, including
295
+ * `null`, objects, arrays, and operators. The walk does not enter a field value.
296
+ * It enters the where object's own keys, `and`, `or`, and `not` when `not` is
297
+ * the where itself. An `or()` branch with no predicate is OKM1121. `or([])`
298
+ * matches nothing.
299
+ *
300
+ * @param where - Caller filter, or one `and` / `or` / `not` operand
301
+ * @returns `false` when the filter matches every row
302
+ */
303
+ export declare function effectivePredicate(where: unknown): boolean;
291
304
  /**
292
305
  * Records the fix text for a code thrown from a chunk that loads on first use.
293
306
  *
@@ -11,7 +11,7 @@ import {
11
11
  } from "../../shared/tmbdywx0.js";
12
12
  import {
13
13
  quote
14
- } from "../../shared/qgvt8nx1.js";
14
+ } from "../../shared/gbtcc2d7.js";
15
15
  import {
16
16
  assertIdentifier2
17
17
  } from "../../shared/nfzc5z0w.js";
@@ -21,7 +21,7 @@ import {
21
21
  } from "../../shared/dyj86cmj.js";
22
22
  import {
23
23
  snakeCase
24
- } from "../../shared/jsd07prz.js";
24
+ } from "../../shared/ntneex2a.js";
25
25
  import {
26
26
  ColumnBuilder2,
27
27
  retarget
@@ -38,10 +38,10 @@ import {
38
38
  queryHandle,
39
39
  attachHttp,
40
40
  settleCall
41
- } from "../../shared/bt9sfq0z.js";
41
+ } from "../../shared/hf22e0wt.js";
42
42
  import {
43
43
  installArchiveInspect
44
- } from "../../shared/qgvt8nx1.js";
44
+ } from "../../shared/gbtcc2d7.js";
45
45
  import {
46
46
  fitIdentifier2
47
47
  } from "../../shared/nfzc5z0w.js";
@@ -459,7 +459,7 @@ function refuse(host, table) {
459
459
  }
460
460
  var archiveMod;
461
461
  function loadArchive() {
462
- archiveMod ??= import("../../shared/ckjbyrt9.js");
462
+ archiveMod ??= import("../../shared/0t22wwh1.js");
463
463
  return archiveMod;
464
464
  }
465
465
  async function settle(host, table, mods) {
@@ -3,10 +3,10 @@ import {
3
3
  } from "../../shared/9m80mkac.js";
4
4
  import {
5
5
  addSchemaHook
6
- } from "../../shared/3p4b1fg0.js";
6
+ } from "../../shared/5enne4ma.js";
7
7
  import {
8
8
  queryHandle
9
- } from "../../shared/bt9sfq0z.js";
9
+ } from "../../shared/hf22e0wt.js";
10
10
  import {
11
11
  OkmError2
12
12
  } from "../../shared/52szzakm.js";
@@ -14,7 +14,7 @@ import {
14
14
  // src/runtime/validate/load.ts
15
15
  var engine;
16
16
  function loadEngine() {
17
- engine ??= import("../../shared/c8aghh05.js");
17
+ engine ??= import("../../shared/8rsy3hxz.js");
18
18
  return engine;
19
19
  }
20
20
 
@@ -12,16 +12,14 @@ import {
12
12
  indexes,
13
13
  quote,
14
14
  isRecord,
15
+ effectivePredicate,
15
16
  registerFailFix,
16
17
  fail
17
- } from "./qgvt8nx1.js";
18
+ } from "./gbtcc2d7.js";
18
19
  import {
19
20
  safetyInstalled,
20
21
  runSafety
21
22
  } from "./a5sxz52y.js";
22
- import {
23
- isOperator
24
- } from "./cn0n68ft.js";
25
23
  import {
26
24
  OkmError2
27
25
  } from "./52szzakm.js";
@@ -362,11 +360,9 @@ function requireFilter(table, where, mods, op) {
362
360
  }
363
361
  return;
364
362
  }
365
- if (isOperator(where))
366
- return;
367
- if (isRecord(where) && Object.keys(where).length > 0)
368
- return;
369
- fail("OKM1102", `${op} on ${table.model.name} needs a where. Pass a filter, or .all(reason).`);
363
+ if (!effectivePredicate(where)) {
364
+ fail("OKM1102", `${op} on ${table.model.name} needs a where. Pass a filter, or .all(reason).`);
365
+ }
370
366
  }
371
367
  function callOptions(input) {
372
368
  const signal = input.signal;
@@ -1,20 +1,22 @@
1
1
  import {
2
2
  open2
3
- } from "./wqem4jed.js";
3
+ } from "./krxx77g0.js";
4
4
  import"./8bwbwy8h.js";
5
5
  import {
6
6
  createdOf,
7
7
  testing2
8
- } from "./g516zdsb.js";
8
+ } from "./v36c5g6p.js";
9
9
  import"./n442h54c.js";
10
10
  import {
11
- assertTargetPolicy,
12
- assertDirectConnection,
13
- selectTarget,
14
11
  loadConfig,
15
12
  loadSchema
16
- } from "./xdpj5kkm.js";
17
- import"./ayz64vzj.js";
13
+ } from "./qv2ydck9.js";
14
+ import {
15
+ assertTargetPolicy,
16
+ assertDirectConnection,
17
+ assertStableBackend,
18
+ selectTarget
19
+ } from "./ga3sfy93.js";
18
20
  import"./8xsbagad.js";
19
21
  import {
20
22
  OkmError2
@@ -34,9 +36,22 @@ async function seedProject(cwd, file, flags) {
34
36
  fix: { summary: "Pass okm seed <file> a module in this project." }
35
37
  });
36
38
  }
37
- assertDirectConnection(target.url, flags.allowPooler || config.allowPooler === true);
39
+ const allowPooler = flags.allowPooler || config.allowPooler === true;
40
+ assertDirectConnection(target.url, allowPooler);
38
41
  const schema = await loadSchema(cwd, config.schema);
39
42
  const pool = open2({ url: target.url, max: 1 });
43
+ const held = await pool.reserve?.();
44
+ if (held !== undefined) {
45
+ try {
46
+ await assertStableBackend(async () => {
47
+ const result = await held.execute("select pg_backend_pid()::text");
48
+ const cell = result.rows[0]?.[0];
49
+ return cell === null || cell === undefined ? "" : String(cell);
50
+ }, allowPooler);
51
+ } finally {
52
+ await held.release();
53
+ }
54
+ }
40
55
  const harness = await testing2(schema, { driver: pool, migrate: false });
41
56
  try {
42
57
  const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
@@ -75,7 +75,7 @@ function mapDriverError2(error, batchIndex) {
75
75
  if (error instanceof DriverError2) {
76
76
  return batchIndex === undefined ? error : error.at(batchIndex);
77
77
  }
78
- const message = error instanceof Error ? error.message : "driver error";
78
+ const message = messageOf(error);
79
79
  return new DriverError2(message, driverFields({
80
80
  sqlstate: sqlstateOf(error),
81
81
  constraint: errorField2(error, "constraint_name") ?? errorField2(error, "constraint"),
@@ -86,6 +86,14 @@ function mapDriverError2(error, batchIndex) {
86
86
  batchIndex
87
87
  }));
88
88
  }
89
+ function messageOf(error) {
90
+ if (!(error instanceof Error))
91
+ return "driver error";
92
+ if (error.message.length > 0)
93
+ return error.message;
94
+ const first = error.errors?.[0];
95
+ return first instanceof Error && first.message.length > 0 ? first.message : "driver error";
96
+ }
89
97
  function driverFields(input) {
90
98
  const fields = {};
91
99
  if (input.sqlstate !== undefined)