@objectstack/driver-sqlite-wasm 17.0.0-rc.6 → 17.0.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 (2) hide show
  1. package/CHANGELOG.md +1602 -1
  2. package/package.json +6 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,1606 @@
1
1
  # @objectstack/driver-sqlite-wasm
2
2
 
3
+ ## 17.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - d367f03: refactor(drivers)!: 五个驱动的 query 参数跟进 `DriverQuery`,休眠的类型谎言就此没有藏身处 (#6075)
8
+
9
+ #5181(PR #6076)把 `IDataDriver.find/findOne/count/updateMany/deleteMany/explain` 的 query 参数收窄为 `DriverQuery`(`Omit<QueryAST, 'object'>`),并在同一条 changeset 里写明:「把驱动签名一并迁到 `DriverQuery` 是后续的机械收尾」。这就是那次收尾。
10
+
11
+ 在此之前,五个驱动的实现仍旧声明 `query: QueryAST`(turso 侧是 `query: any`)。**它不红,也不会红** —— 方法参数按双变比较,实现声明得比契约宽照样满足契约。但调用方现在**有权**省略 `object`,于是这些实现的类型说 `query.object` 是 `string`,运行期却可能是 `undefined`:一句休眠的谎言,没有任何门拦得住下一个照着它写代码的人。
12
+
13
+ 收尾之后,「驱动读 `query.object`」直接变成编译错误:
14
+
15
+ ```ts
16
+ // 收窄前:编译通过,运行期可能是 undefined —— 谎言
17
+ // 收窄后:error TS2339: Property 'object' does not exist on type 'DriverQuery'.
18
+ const name = query.object;
19
+ ```
20
+
21
+ **零运行时改动。** 本次改的全部是类型注解:五个驱动的六个契约方法签名,以及为让类型自洽而必须跟进的少量私有辅助方法参数(mongodb 的 `buildFindOptions` / `buildSortSpec`,sql 的 `findRows` / `orderKeysFor`,turso 的 `toRemoteQuery` / `toRemoteReadQuery`,memory 的 `performAggregation`)—— 它们都只转发或读取 `where` / `orderBy` / `groupBy` 这些字段,本来就不读 `object`。turso 的几处 `query: any` 一并收紧,多拿回一批本已放弃的检查。emit 无差异,测试全绿(memory 524、mongodb 206、sql 906、sqlite-wasm 254、turso 788)。
22
+
23
+ **迁移面:删掉驱动调用字面量里的 `object:` 键**,与 #5181 是同一句话,只是现在也覆盖了直接按具体驱动类(`SqlDriver` / `MemoryDriver` / …)而非按 `IDataDriver` 取类型的调用方。编译器会逐处指出来(TS2353 `'object' does not exist in type 'DriverQuery'`)。本仓下游 25 个包实测零处需要改动,改动只落在五个驱动自己的测试里。
24
+
25
+ 标 major 的依据与 #5181 一致:**源码级破坏性**(调用点内联字面量),运行时行为零变化。`check:api-surface` 只记录导出的存在与否、不记录签名,因此这条说明同样是该变更唯一的下游载体。
26
+
27
+ `aggregate` / `distinct` / `syncSchemasBatch` 不在本次范围内 —— 它们不是 `IDataDriver` 收窄的那六个方法,其中 `syncSchemasBatch` 的条目里 `object` 是被真实读取的必填键,`expand` 条目里的 `object` 同理命名的是关联对象,都不是冗余。
28
+
29
+ - 62159bd: refactor(driver-sql)!: `SqlDriver.distinct` 的第三参收成裸 `FilterCondition`,一个静默返回全集的写法就此编译不过 (#6320)
30
+
31
+ `distinct` 不在 `IDataDriver` 上,所以 #5181(PR #6076)与 #6075(PR #6210)的收窄都没走到它,#6212 批 A+E(#6355)收的是 `analyzeQuery` / `findWithWindowFunctions`,也没覆盖它。它的方法体一直说得很清楚——`applyFilters(builder, filters)` 拿的是**实参本身**,因此它要的是 `find()` 放在 `query.where` 里的那个值,**不是 query 信封**;`filters?: any` 只是没把这句话写进类型里。
32
+
33
+ ```ts
34
+ // 收窄前后都成立,一处调用点都不用改
35
+ await driver.distinct("orders", "product", { status: "completed" });
36
+ ```
37
+
38
+ **收窄真正买到的东西,是实测出来的,不是推断的。** 三行数据(`Laptop`/`Mouse` 为 `completed`,`Ghost` 为 `pending`),逐个形状喂给 `distinct('orders','product', …)`:
39
+
40
+ | 第三参 | 收窄前 | 收窄后 |
41
+ | :--------------------------- | :--------------------------------- | :----------- |
42
+ | `{ status: 'completed' }` | 返回 `["Laptop","Mouse"]` | 不变 |
43
+ | 省略 | 返回全集 | 不变 |
44
+ | `'completed'`(标量) | **编译通过,返回全集** | **编译错误** |
45
+ | `{ object, where }`(信封) | 抛 `INVALID_FILTER` / 400 | 不变 |
46
+ | `['status','=','completed']` | 抛 `INVALID_FILTER` / 400(#5158) | 不变 |
47
+
48
+ 第三行就是本次消掉的那一格:一个真心想问「completed 订单里有哪些商品」的调用,编译通过,然后拿到**每一个**商品。`applyFilters` 对「真值但非对象、非数组」的 filter 不发射任何谓词(该方法尾注写着这件事),于是过滤条件被整条丢掉。方向是**放宽**——这正是 #6320 与 #5234 同族的那类「静默错答案」。
49
+
50
+ **有一格是任何类型都关不上的,本次如实写进注释而不是假装关上了。** `FilterCondition` 的键**就是字段名**,所以它是开放映射(`[key: string]: any`):`{ object, where }` 在结构上是一个完全合法的 filter——约束两个分别叫 `object` 和 `where` 的列。没有任何注解能把它和正当 filter 分开。#6320 提出的「让反向错配也编译不过」在这个参数上**不可达**,实测确认;能拿到的保证是**运行期响亮失败**:信封里的 `where` 是对象,而没有任何比较值可以是对象,于是 `assertCompilableComparand` 抛 `INVALID_FILTER` / 400。这半边 driver-sql 从来就不是静默的;`driver-memory` 那半边(裸 filter 交给它会静默返回全集)留在 #5499 冻结面内,本次不碰。
51
+
52
+ **零运行时改动**:非测试改动 100% 是一个类型注解加一段注释,无逻辑、无行为、无 emit 差异。
53
+
54
+ **逐处复核了全部 14 个调用点**(本单正文记的是 3 处,实测偏低):driver-sql 11 处、driver-sqlite-wasm 3 处、driver-turso 0 处;其中真正传第三参的是 4 处(driver-sql 2 + driver-sqlite-wasm 2),全部本来就写的裸 filter,**零报错、零 fixture 改动**。
55
+
56
+ **driver-sqlite-wasm 也标 major**:`SqliteWasmDriver extends SqlDriver` 且不覆写 `distinct`,所以它**已发布的 `.d.ts`** 里这个方法的签名同样收窄,它的使用者看到的是同一个变化。该包读的是 driver-sql 构建后的 `dist/*.d.ts` 而非源码,是一处已知门禁盲区,本次用「往参数类型里临时塞一个调用方不可能满足的成员、重建、看调用点是否逐一变红」证明它确实读到了新 d.ts:driver-sql 6 处红、driver-sqlite-wasm 3 处红,与预判逐一相符。
57
+
58
+ ### 迁移
59
+
60
+ 调用点若把**标量**(或任何非 `FilterCondition` 值)交给第三参,编译器会指出来:
61
+
62
+ ```
63
+ error TS2345: Argument of type 'string' is not assignable to parameter of type 'FilterCondition'.
64
+ ```
65
+
66
+ 改法是把它写成它本来就该是的裸 filter 对象(`'completed'` → `{ status: 'completed' }`)。⚠️ 这类调用点在收窄前拿到的是**未过滤的全集**,所以这不是一次等价改写:修完之后返回值会变,而变化后的那个才是调用方本来想要的答案。本仓零处这样的调用点。
67
+
68
+ ⚠️ 无类型的 JS 调用方**既不会拿到编译错误、也不会有任何行为变化**(本次零运行时改动)。对他们而言,上面那条是「你一直没在过滤」的**唯一通知渠道** —— 这也是本次记台账条目的理由,见下。
69
+
70
+ <!-- adr-0087: registered driver-sql-distinct-bare-filter-typed -->
71
+
72
+ ### Minor Changes
73
+
74
+ - 92a67f2: feat(drivers,spec)!: `GroupByNode.alias` is honoured by the SQL faces — one aggregate, one column key (#6401)
75
+
76
+ `GroupByNodeSchema` has declared `alias` ("Alias for the projected group
77
+ value", defaulting to `field`) for as long as the structured `groupBy` entry has
78
+ existed. Exactly one execution path read it. The result: the SAME query came
79
+ back with a different result-column key depending on which path the engine
80
+ happened to take.
81
+
82
+ ```ts
83
+ groupBy: [{ field: "closed_at", dateGranularity: "month", alias: "qtr" }];
84
+ ```
85
+
86
+ - pushed down to a driver ⇒ rows keyed **`closed_at`**
87
+ - run through the in-memory fallback ⇒ rows keyed **`qtr`**
88
+
89
+ And the choice between them is `engine.ts`'s
90
+ `allStructuredSupported && !tzRequiresInMemory` — a driver capability bit and a
91
+ `timezone`, neither of which the caller can see. That is the multi-face
92
+ consistency invariant broken in its quietest form: both answers are valid rows,
93
+ so nothing throws and nothing looks wrong.
94
+
95
+ **Resolved to ENFORCE**, and the leg was chosen by measurement rather than
96
+ taste. ADR-0049 splits on whether the feature already exists: a _dangling_
97
+ promise is removed, a _live_ one with a missing gate is enforced. `alias` is
98
+ live — three consumers read it and change behaviour
99
+ (`in-memory-aggregation.ts`, `MemoryDriver.performAggregation`, and
100
+ `chartAggregateCategoryKey`), and the publish gate _compels_ it:
101
+ `validate-react-page-props.ts` errors `REACT_CHART_AXIS_UNKNOWN` unless a
102
+ chart's category axis is bound to `alias ?? field`, telling the author in so
103
+ many words to "bind it to" the alias. A key the build gate makes you write is
104
+ not a dangling promise. The count of real non-test producers is **zero**, which
105
+ is what makes enforcing safe rather than what argues against it: no shipped
106
+ payload changes its result keys.
107
+
108
+ **What changed, on every SQL face at once** — a fix landing on one and not its
109
+ twin is the #6203 shape, and `TursoDriver` picks its face from `url`:
110
+
111
+ - **`driver-sql`** — both limbs of the structured `groupBy` branch project
112
+ `alias ?? field`: the date-bucket limb aliases the bucket expression to it,
113
+ and the plain limb emits `?? as ??` (only when the name actually moves — an
114
+ alias equal to the field emits no self-rename). `presentedOutput` is now keyed
115
+ by the OUTPUT column, matching how the aggregation branch beside it has always
116
+ worked; an aliased group value went unpresented before.
117
+ - **`driver-turso` REMOTE** — the same projection, `"field" AS "alias"`. The
118
+ alias reaches the statement as a quoted identifier and is therefore held to
119
+ `assertSafeIdentifier`, exactly like `field`.
120
+ - **`driver-sqlite-wasm`** — inherits `SqlDriver`'s compiler; covered by its own
121
+ conformance suite rather than by assumption.
122
+
123
+ **GROUP BY still keys on the FIELD** on every face. Only the projection is
124
+ renamed, so the buckets are unchanged. This is deliberate and pinned: SQLite
125
+ resolves output names in `GROUP BY`, so a face that grouped by the alias would
126
+ look correct here and diverge on a dialect that does not.
127
+
128
+ `having` needed no change and now means one thing: it is applied over the
129
+ aggregated row's own columns, so a filter on a group projection references the
130
+ alias on every path — previously the alias on one path and the field on the
131
+ other.
132
+
133
+ **Conformance.** `AGGREGATION_CASES` (#6409) gains a `groupByAlias` axis and two
134
+ cases. Their VALUES are an existing case verbatim — only the key moves — so they
135
+ can fail only on the key, which is the point: every wrong answer in this area is
136
+ a valid query returning plausible rows. `objectql`'s in-memory fallback is now
137
+ **enrolled** as a fourth face, answering #6409's open question ②: it is the face
138
+ the SQL three were converged onto, so the new behaviour would otherwise be
139
+ pinned against nothing, and reaching it needs no engine at all —
140
+ `applyInMemoryAggregation` is a pure function of rows and an AST.
141
+
142
+ **Reverse verification**, predicted before running. Reverting the in-memory face
143
+ to `g.field`: only the two alias cases move and only ONE fails — the degenerate
144
+ `alias === field` case stays green, which is why both are in the table.
145
+ Reverting the harness to read `c.groupBy` instead of `c.groupByAlias ?? c.groupBy`
146
+ — the copied-neighbour mistake: everything passes on an unmodified face, a false
147
+ GREEN, which is the failure mode that would have made the axis vacuous.
148
+
149
+ **Frozen drivers (#5499), measured from source, not flipped.** `driver-memory`
150
+ already returned `{ field, alias: node.alias ?? node.field }` and projects under
151
+ the alias — it had independently reached the enforce answer, so it needed no
152
+ alignment. `driver-mongodb` is a recorded DEBT row and the defect is wider than
153
+ `alias`: `buildAggregationPipeline` types `groupBy` as `string[]` and builds
154
+ `groupId[field] = '$' + field`, so a structured node — aliased or not — becomes
155
+ the literal key `"[object Object]"`. It cannot take a structured `GroupByNode`
156
+ at all; `mongodb-driver.ts` passes `(query as any).groupBy`, which is why `tsc`
157
+ never saw it. Tracked on #6814.
158
+
159
+ **Compatibility.** A caller who writes `alias` and reads the result under
160
+ `field` on a pushdown path will now find the value under `alias` — which is what
161
+ the key has always meant on the fallback path, and what the chart gate already
162
+ required. Callers who never write `alias` are unaffected: the emitted SQL is
163
+ byte-identical.
164
+
165
+ <!-- adr-0087: not-required (no-migration-prescription) Nothing is retired: `GroupByNodeSchema.alias` keeps its declaration, its spelling and its type — it starts being HONOURED by three faces that parsed and ignored it. There is no tombstone to write and no authored metadata to rewrite, so there is no mechanical transform a migration could prescribe: every stack that validated before validates after, unchanged. The behaviour change is in the RESULT of a runtime query (a result-column key moves from `field` to `alias` on the pushdown path, converging on what the in-memory path and the chart publish gate already required), which the ledger has no channel for and no upgrader could apply a codemod to. The bang is on the changeset because callers who read that column by the field name must move, and the measured non-test producer count for the key is zero. -->
166
+
167
+ - 82397b6: feat(drivers,objectql): `$regex` / `$options` are refused everywhere, and `$icontains` is implemented on the SQL family (#5702)
168
+
169
+ The driver half of the #4706 ruling. #5701 landed the contract (the vocabulary,
170
+ the `RETIRED_FILTER_OPERATORS` prescriptions, the shared text case-set) and
171
+ #5710 flipped the last live producer — `plugin-auth`'s ObjectQL adapter, which
172
+ emitted `$regex` on the authentication path — so the refusal can now land
173
+ without breaking sign-in.
174
+
175
+ **BREAKING for anyone writing `$regex` or `$options` in a filter.** Both are
176
+ refused on every backend with `INVALID_FILTER` / 400 and a message that names
177
+ the replacement. `$regex` was never a declared operator: `driver-sql` compiled
178
+ it to a LIKE-escaped substring (so `a.b` matched only the literal `a.b`),
179
+ `driver-memory` ran it as a real `RegExp` (so the same filter also matched
180
+ `axb`, and an _invalid_ pattern was caught and answered `false` — zero rows, in
181
+ silence), and `objectql`'s `having` did the same. Write `$icontains` for the
182
+ case-insensitive substring search this was almost always used for, `$contains`
183
+ for a case-sensitive one; a pattern that genuinely needs a regex has no
184
+ filter-level replacement.
185
+
186
+ **`$icontains` now runs on the SQL family** — `driver-sql`, `driver-sqlite-wasm`,
187
+ and both of `driver-turso`'s transports (the remote one does not go through
188
+ knex, so it needed its own). It compiles to `LOWER(col) LIKE LOWER(?) ESCAPE ?`
189
+ through the same `applyLike` / `pushLike` that carries the `%` / `_` / `\`
190
+ escaping, as a `fold` parameter rather than a second emitter — a copied emitter
191
+ is where the escape class would have been dropped, and an unescaped `%` matches
192
+ every row. An empty or non-string comparand is refused on the validating walk
193
+ (an empty one matches every row, which widens rather than narrows). On SQLite
194
+ `lower()` folds ASCII only, which IS the contract (#4706 Q1 = A): `$icontains:
195
+ 'café'` does not match `CAFÉ`.
196
+
197
+ <!-- adr-0087: registered filter-regex-options-retired -->
198
+
199
+ `driver-mongodb`'s unknown-operator arm was throwing a bare `Error` with no
200
+ `code` and no `status`, three lines from the helper in its own file that sets
201
+ `INVALID_FILTER` / 400 — a 500-shaped body for a 400-class client mistake. It
202
+ now speaks the same envelope as its three siblings.
203
+
204
+ Two parts of the ruling are deliberately NOT in this change and stay tracked in
205
+ `scripts/check-driver-conformance.mjs`'s ledger: the `$contains` family's
206
+ case-sensitivity (#4706 Q2 = A) needs SQLite's `LIKE` replaced by a case-exact
207
+ construct in the driver, the RLS lowering and the analytics lowering together,
208
+ or one permission rule compiles to two row sets (#6518); and `$icontains` on the
209
+ JS evaluation faces needs the spec vocabulary to take the operator, which cannot
210
+ happen before `driver-memory` has an arm for it (#6520).
211
+
212
+ - f1544e2: feat(driver-sql): compile `$field` to a column-to-column comparison on SQL push-down (#5222)
213
+
214
+ `FieldReferenceSchema` (`{ $field: 'other_column' }`) is declared in the spec and
215
+ genuinely PRODUCED — `compileCelToFilter` emits it whenever a CEL permission/RLS
216
+ rule compares one field to another — but its only implementation was the
217
+ in-memory evaluator. #5041 measured the consequence and installed a loud refusal
218
+ (`INVALID_FILTER` / 400, replacing a bare `TypeError` and, inside an `$in` list,
219
+ a silent zero-row answer), deliberately leaving the capability itself to this
220
+ change. Until now, therefore, one permission rule had two behaviours chosen by
221
+ whether the query reached a database.
222
+
223
+ The six scalar comparison operators — `$eq` / `$ne` / `$gt` / `$gte` / `$lt` /
224
+ `$lte`, including the array-triple authorings that lower to them — now compile
225
+ `{ $field: 'col' }` into a real column reference:
226
+
227
+ ```js
228
+ {
229
+ amount: {
230
+ $gt: {
231
+ $field: "budget";
232
+ }
233
+ }
234
+ } // → where "amount" > "budget"
235
+ ```
236
+
237
+ **Nothing that worked before changes.** This is additive: every shape that
238
+ compiled still compiles identically, and the refusal gate was NARROWED, never
239
+ removed. A minor bump because a previously-400 filter now returns rows.
240
+
241
+ **The refused arm, and why each entry is there** (all keep `INVALID_FILTER` /
242
+ 400):
243
+
244
+ - **Dotted paths** (`{ $field: 'account.owner_id' }`) — maintainer ruling: v1 is
245
+ same-table columns only. No JOIN planning, no alias-qualified columns.
246
+ - **Undeclared columns**, on either side — the `$field` value lands in a SQL
247
+ identifier position, so only fields the object declares are accepted, refused
248
+ at COMPILE time rather than by the database. Federated/external tables
249
+ (ADR-0015), whose column set this driver does not own, are refused wholesale.
250
+ - **The tenant-isolation column**, on either side — a privilege-escalation
251
+ comparison surface. Closed on both sides because the operands of `=` commute.
252
+ - **Cross-class comparisons** (a number against text, a date against text) —
253
+ SQLite orders by storage class first while the in-memory evaluator applies JS
254
+ coercion, so the two paths genuinely disagree and neither answer can be made
255
+ the other. Refused rather than shipped as a silent divergence.
256
+ - **`$in` / `$nin` / `$between` list members** — the in-memory evaluator does not
257
+ resolve a reference inside a list either (`resolveValue` returns an array
258
+ unchanged), so there is no correct semantics for SQL to be equivalent to.
259
+ - **The string operators** (`$contains`, `$startsWith`, …) — a column-side LIKE
260
+ pattern cannot be metacharacter-escaped portably, and an unescaped one is the
261
+ `%`-matches-every-row filter bypass.
262
+ - **The bare `{ field: { $field: 'other' } }` spelling** — what
263
+ `parseFilterAST(['a', '=', { $field: 'b' }])` lowers to. Still refused, because
264
+ the in-memory evaluator answers `false` for it rather than reading it as an
265
+ equality; the refusal now names `$eq` as the spelling that compiles instead of
266
+ falling through to a generic operator list.
267
+
268
+ **Equivalence is proven, not asserted.** A cross-path conformance suite runs each
269
+ supported shape through the in-memory evaluator AND through SQL push-down against
270
+ the same seeded rows, holding both to the same declared id list. Its fixture
271
+ carries every NULL arrangement two columns can be in — target NULL, referent
272
+ NULL, and BOTH NULL — because three-valued SQL against a two-valued JS matcher is
273
+ the one place these paths can genuinely diverge. Every emitted predicate is
274
+ therefore written TOTAL: `{ a: { $eq: { $field: 'b' } } }` matches a row where
275
+ both columns are NULL, which a plain `a = b` would drop, and `$not` over any
276
+ cross-field leaf is its exact complement.
277
+
278
+ The suite runs the full driver axis — SQLite always, live Postgres and MySQL
279
+ when the runner provisions them — and on both SQL drivers: `driver-sqlite-wasm`
280
+ inherits the compiler but executes through its own sql.js dialect, which binds
281
+ the identifier list itself. The dialect axis is not ceremony here: a cross-field
282
+ predicate is the one filter shape whose SQL carries two identifiers and no bound
283
+ value, and the class rule has a different failure per backend — comparing text to
284
+ a number is a silent wrong answer on SQLite (storage classes order before values)
285
+ but `operator does not exist: text > integer` on Postgres. The guard is what
286
+ keeps either from being reached.
287
+
288
+ - 3172831: fix(drivers): text-operator case folding is the CONTRACT's answer, not the dialect's (#6518)
289
+
290
+ The `$contains` family and `$icontains` returned **different rows on different
291
+ databases** for the same filter, because case sensitivity was decided by whatever
292
+ `LIKE` happened to mean on the dialect underneath. Both directions **over-matched**
293
+ — they returned rows the filter excludes, which on an ADR-0021 RLS read scope is
294
+ over-reach rather than a loose filter (#3948):
295
+
296
+ | | `$contains` / `$notContains` / `$startsWith` / `$endsWith` — case-SENSITIVE (#4706 Q2 = A) | `$icontains` — folds ASCII ONLY (#4706 Q1 = A) |
297
+ | :--------------------------- | :----------------------------------------------------------------------------------------- | :--------------------------------------------- |
298
+ | SQLite / turso / sqlite-wasm | ❌ `LIKE` folds ASCII | ✅ `lower()` is ASCII-only |
299
+ | Postgres | ✅ `LIKE` is case-exact | ❌ `LOWER()` folds all of Unicode |
300
+ | MySQL | ❌ follows the column's collation | ❌ `LOWER()` folds all of Unicode |
301
+
302
+ Read across: **each dialect was already right on the half another one got wrong**,
303
+ which is why neither half could be found from one backend alone.
304
+
305
+ ## What now runs
306
+
307
+ The construct is chosen per dialect, in one emitter, so the escaping and the fold
308
+ stay a single code path (an unescaped wildcard is a filter bypass, P0 — #5567):
309
+
310
+ - **SQLite family → `GLOB`.** `LIKE`'s ASCII fold cannot be switched off per
311
+ statement (`PRAGMA case_sensitive_like` is connection-global, so one query would
312
+ redefine every other query on the connection), and `CAST(col AS BLOB) LIKE ?` was
313
+ measured to match _nothing at all_. `GLOB` is case-exact and brings its own
314
+ escaped class — `*`, `?`, `[` as the self-closing classes `[*]`, `[?]`, `[[]`,
315
+ because SQLite's grammar gives `GLOB` no `ESCAPE` clause. `$icontains` keeps
316
+ `lower()` on both operands, still ASCII-only.
317
+ - **Postgres → `LIKE`, unchanged.** Only the fold moved, from `LOWER()` to an
318
+ explicit `translate()` over the 26 ASCII letters. Measured on a live PostgreSQL
319
+ 16 (ICU database): `LOWER('CAFÉ')` is `'café'` — the over-fold — while the
320
+ `translate()` form leaves `É` alone.
321
+ - **MySQL → `LIKE` over `CAST(… AS BINARY)`**, so the comparison is byte-wise and
322
+ no collation decides the case; `$icontains` folds byte-wise over the same binary
323
+ rendering, which is ASCII-only because UTF-8 is self-synchronising.
324
+ - **Any other client** keeps the previous `LIKE` / `LOWER()` shape — it is the only
325
+ form that still runs there — and is recorded as residue rather than left to be
326
+ discovered.
327
+
328
+ `driver-turso`'s remote transport carries the twin (it compiles filters itself and
329
+ inherits nothing), and the two transports are now held to the same rows by a
330
+ parity suite that runs the shared `FILTER_TEXT_CASES` on both.
331
+
332
+ ## Behaviour change — read this before upgrading
333
+
334
+ A filter whose comparand's case did not match the stored text used to match on
335
+ SQLite/turso/sqlite-wasm and may have matched on MySQL. It no longer does:
336
+
337
+ ```ts
338
+ // rows: { id: '1', name: 'ACME Corp' }, { id: '2', name: 'acme corp' }
339
+ {
340
+ name: {
341
+ $contains: "acme";
342
+ }
343
+ } // was ['1','2'] on SQLite → now ['2'] everywhere
344
+ {
345
+ name: {
346
+ $icontains: "acme";
347
+ }
348
+ } // ['1','2'] — unchanged, and now correct on PG/MySQL too
349
+ {
350
+ name: {
351
+ $icontains: "café";
352
+ }
353
+ } // was ['3','4'] on PG/MySQL → now ['4'] everywhere
354
+ ```
355
+
356
+ If you were relying on `$contains` to ignore case, **write `$icontains`** — that is
357
+ the operator for it, and it now folds the same ASCII-only range on every backend.
358
+ Result sets only ever get NARROWER, never wider, so a filter that was already
359
+ correct stays correct.
360
+
361
+ ## Why `minor` rather than `major`
362
+
363
+ No declared surface moves. `$contains` still exists, still takes the same
364
+ comparand, and `filter.zod.ts` is untouched — the case-sensitivity this delivers
365
+ was **already published** as the contract by #5701 (`FILTER_TEXT_CASES`, one
366
+ release earlier in this same v17 major), and the drivers were the half that had
367
+ not caught up. This is Prime Directive #12 applied in the direction it points:
368
+ declared = enforced. It is graded the way its sibling #5702/#6549 was graded for
369
+ the same operator family in the same rc cycle, and it registers nothing in the
370
+ ADR-0087 registries because it retires no authorable key.
371
+
372
+ ## What is deliberately NOT in this change
373
+
374
+ `driver-memory` and `driver-mongodb` still fold case on their query paths — they
375
+ are the #5499 frozen family, so their `FILTER_TEXT_CASES` cells stay honest DEBT
376
+ and are tracked as #6682 (case sensitivity) and #6520 (`$icontains`). The
377
+ `service-analytics` SQL compilers were measured already compliant: they emit
378
+ Postgres-shaped statements, where `LIKE` is case-exact, and that assumption is now
379
+ written down and pinned rather than implied.
380
+
381
+ ### Patch Changes
382
+
383
+ - 0af50a3: fix(driver-sql,service-analytics): a bare-day upper bound covers the whole day on `Field.datetime` (#3777)
384
+
385
+ A bare `YYYY-MM-DD` comparand anchors to midnight UTC. That is right for a
386
+ lower bound and was silently wrong for an upper one: the dashboard date-range
387
+ filter compiles `{ $gte: from, $lte: to }` with bare-day bounds, so on a
388
+ `datetime` column every row created after 00:00 of the `to` day vanished from
389
+ the result — no error, the chart renders, the numbers are just smaller. The
390
+ default configuration hit it: the filter's default field is `created_at`
391
+ (a system-injected `Field.datetime`) and 7 of the 13 presets end "today".
392
+
393
+ The translation is operator-sensitive and half-open, applied at every
394
+ comparison emitter:
395
+
396
+ - `SqlDriver` (and `SqliteWasmDriver` by inheritance): `$lte`/`<=` with a
397
+ bare-day comparand on a `datetime` column compiles to `< next-day-midnight`
398
+ in the column's storage form; `$between [min, max]` with a bare-day max
399
+ decomposes to `>= min AND < next-day(max)`. Both the plain and the
400
+ legacy-repair (mixed-storage) column paths, both `where` spellings.
401
+ - `NativeSQLStrategy`: `dateRange` windows and `lte` filters bind `< next-day`
402
+ instead of an inclusive `BETWEEN`/`<=` when the bound is a bare day.
403
+ - The `/analytics/sql` rendering and the dataset preview evaluator apply the
404
+ same rule, so the echoed SQL and drafted numbers reproduce execution.
405
+
406
+ `@objectstack/core` gains the shared primitive `nextUtcCalendarDay(value)`:
407
+ the next calendar day of a valid bare `YYYY-MM-DD` (else `null` — instants,
408
+ `Date`s and impossible days are never widened).
409
+
410
+ Unchanged on purpose, per the semantics table on #3777: `date`/`time` columns
411
+ (`<= day` is already whole-day-correct there), full-ISO/`Date` comparands
412
+ (instant semantics), and `$gte`/`$gt`/`$lt` (midnight anchoring is correct for
413
+ those). No authored metadata changes: a dashboard's existing
414
+ `{ $gte, $lte }` window now simply includes its final day.
415
+
416
+ - 2e836de: chore(packaging): CHANGELOG.md ships in every npm tarball (#4261)
417
+
418
+ The AGENTS.md post-task checklist requires breaking changesets to carry their
419
+ FROM → TO migration because "this text ships to consumers as `CHANGELOG.md`
420
+ inside the npm package and is what an upgrading agent greps after the tombstone
421
+ error." That delivery path was severed for 68 of the 69 publishable packages:
422
+ npm packs `package.json` / `README*` / `LICENSE*` unconditionally but — unlike
423
+ older npm versions — not `CHANGELOG.md`, and the canonical
424
+ `"files": ["dist", "README.md"]` whitelist never named it. Measured on npm
425
+ 10.9.7: `npm pack --dry-run` on `@objectstack/types` shipped 3 files while its
426
+ 70KB `CHANGELOG.md` stayed behind. Only `@objectstack/spec` listed it
427
+ explicitly.
428
+
429
+ The tombstone-error scenario is precisely the one where the repo is out of
430
+ reach — the upgrading agent has `node_modules` and nothing else — so the
431
+ migration text has to ride in the tarball. Every publishable package now
432
+ declares `CHANGELOG.md` in `files`, and the canonical whitelist is
433
+ `["dist", "README.md", "CHANGELOG.md"]`.
434
+
435
+ The other half is the gate: `check:published-files` gains a fifth invariant,
436
+ COMPLETE — a whitelist that fails to cover `CHANGELOG.md` fails the
437
+ always-required lint job, so the next package cannot silently sever the path
438
+ again. `@objectstack/spec`'s per-package EXTRA_ENTRIES exemption dissolves
439
+ into the canonical set.
440
+
441
+ Consumer-visible change: one more file per install (the package's changelog,
442
+ e.g. 70.8KB for `@objectstack/types`), and `grep -r "removed key"
443
+ node_modules/@objectstack/*/CHANGELOG.md` now finds the migration it was
444
+ promised.
445
+
446
+ - 9881074: test(drivers): the "held to by a gate" claim now has a gate behind it (#4363)
447
+
448
+ Three changesets — filter combinator semantics (#3774), temporal storage form
449
+ (ADR-0053), deterministic paged reads (objectui#3106 / #4363) — each introduced
450
+ a shared case-set in `@objectstack/spec/data` with some version of the claim
451
+ that a future driver "is held to this by a gate rather than by remembering it".
452
+
453
+ There was no gate. The case-sets are exports sitting in a package; nothing
454
+ obliged a driver to import them. Measured on `main`, the matrix had three holes:
455
+ `driver-sqlite-wasm` ran neither pagination case-set, and neither it nor
456
+ `driver-mongodb` ran the filter-logic one — including a hole in the very
457
+ case-set whose changeset made the claim.
458
+
459
+ `scripts/check-driver-conformance.mjs` (`pnpm check:driver-conformance`, wired
460
+ into lint.yml's required job) makes the hole the failure. Every
461
+ (driver × case-set) cell is covered — some file under the package's `src/`
462
+ imports _and drives_ the case-set's marker export — or carries a measured
463
+ DEBT/EXEMPT entry, reconciled in both directions. A third direction, CLASSIFIED,
464
+ holds the other end: a new `*-conformance.ts` fixture nobody classified fails
465
+ the run rather than starting life uncovered, which is the direction that
466
+ actually rots (#4203). It caught an unclassified `TEMPORAL_TIME_CASES` on its
467
+ first run.
468
+
469
+ `driver-sqlite-wasm` gains the pagination suite the gate found missing. It
470
+ inherits `SqlDriver`'s ORDER BY construction, so nothing is re-implemented —
471
+ what the suite pins is that the clause survives a different _engine_: this
472
+ driver swaps knex's transport for a custom sql.js dialect that compiles,
473
+ executes and marshals every row through its own path, and a dialect that
474
+ reordered or dropped the trailing `ORDER BY id` would fail in no other suite.
475
+
476
+ The two filter-logic holes are ledgered as DEBT rather than fixed here, with
477
+ their reasons printed on every run and tracked in #4405. The mongodb row is the
478
+ substantive one: `translateFilter` is an independent FilterCondition backend —
479
+ the fifth, and the one #3774 never enrolled when it counted "the four".
480
+
481
+ - 4384921: fix(spec,drivers): `bypassTenantAudit` becomes a declared driver option, and `findOne` stops accepting a bare id (#4311)
482
+
483
+ Three drivers built with `tsup` and tested with `vitest`, so no `tsc` had ever
484
+ read them. Onboarding them to the #4311 type-check ratchet surfaced 292 errors,
485
+ and most of what looked like sloppy test fixtures was the types being wrong.
486
+
487
+ **`DriverOptions.bypassTenantAudit` is now declared.** It has been live for a
488
+ long time without being on the schema: `SqlDriver.auditMissingTenant` reads it
489
+ to suppress the "tenant-scoped write without `tenantId`" warning, the driver's
490
+ own warning text tells callers to set it, `ObjectQLEngine` sets it for
491
+ system-context calls, and `service-settings` / `service-datasource` pass it on
492
+ every global-scope write. Because the schema never had it, the driver read it
493
+ through `(options as any)` and no caller was type-checked. The declaration
494
+ states the limit as well: it silences a diagnostic and MUST NOT change which
495
+ rows a write touches — suppressing an audit warning is not a permission.
496
+
497
+ The same cast covered `timezone`, `tenantId`, `tenantIds` and `preserveAudit`,
498
+ all long since declared. Those reads now go through `DriverOptions`, so the next
499
+ undeclared option fails the build instead of hiding behind an existing cast.
500
+
501
+ **`SqlDriver.findOne(object, id)` is removed.** An undeclared
502
+ `typeof query === 'string' | 'number'` branch accepted a bare id. It was on no
503
+ contract, nothing outside that package's own tests used it, and the other two
504
+ drivers answered the identical call differently — `MemoryDriver` spreads the
505
+ string into `{0:'t',1:'1'}`, `MongoDBDriver` reads `query.where` as `undefined`
506
+ and returns an arbitrary row. It also bypassed the shared `findRows()` path, so
507
+ it skipped field selection, temporal coercion, unknown-column recovery and the
508
+ `singleRowLookup` ORDER BY decision. Spell an id lookup as the query it is:
509
+
510
+ ```ts
511
+ -(await driver.findOne("task", "t1"));
512
+ +(await driver.findOne("task", { object: "task", where: { id: "t1" } }));
513
+ ```
514
+
515
+ **`SqlDriver.initObjects` declares the `tenancy` it consumes.** Each object is
516
+ fed to `computeAndRecordTenantField`, which reads `obj.tenancy` to pick the
517
+ tenant column and to set or clear the sticky explicit-opt-out — but the
518
+ parameter type listed only `{ name, fields }`, so a caller that spelled the key
519
+ correctly was rejected while the driver read it anyway.
520
+ `registerExternalObject` already had it.
521
+
522
+ **`AnalyticsQueryInput` joins `AnalyticsQuery`.** `timezone` is
523
+ `.default('UTC')`, so the parsed type requires it and an authored literal does
524
+ not have it — the same two-tier split `QueryInput`/`QueryAST` already names on
525
+ the query side. `InMemoryDriver.create`/`bulkCreate` also declare their
526
+ `IDataDriver` return types; without them TS inferred the literal the method
527
+ builds and every other column of the created row disappeared from the caller's
528
+ view.
529
+
530
+ One silent runtime bug fell out of the same pass: a driver test asked for
531
+ `orderBy: [['id', 'asc']]`, the driver reads `item.field`, a tuple has none, and
532
+ the sort never reached SQL. The tuple spelling appears nowhere else.
533
+
534
+ - 06ba036: feat(drivers): `@objectstack/driver-turso` 迁回本仓并公开发布,五个 driver 统一收进 `packages/drivers/` (#4645)
535
+
536
+ `TursoDriver` 一直以 `extends SqlDriver` 的方式**跨仓库继承**本仓的类,自己却住在闭源的
537
+ `objectstack-ai/cloud`(`publishConfig: restricted`)。而本仓的 runtime 早就把 turso 当一等
538
+ 公民——`http-dispatcher.ts` 里环境 provisioning 的偏好顺序第一位就是它,`POST /cloud/environments`
539
+ 的 `driver` 参数示例是 `memory | turso`,`objectql/src/engine.ts` 还带着一段 turso 专属的瞬时
540
+ `fetch failed` 重试。开源侧的代码路径引用着一个自己仓里既测不到也 grep 不到的 driver,闭源侧则
541
+ 在每次 pin bump 时追赶父类的重构。维护者裁定把核心迁回本仓、公开 Apache-2.0 发布。
542
+
543
+ **新包 `@objectstack/driver-turso`(`packages/drivers/driver-turso`,Apache-2.0,`access: public`)**
544
+ 带着它在 cloud 的全部实现与测试落地:`TursoDriver`(local / replica / remote 三种传输模式)、
545
+ `RemoteTransport`(纯 `@libsql/client` 走 HTTP/WebSocket,无原生依赖,可跑 serverless/edge)、
546
+ 驱动的 spec/Studio 元数据,以及 15 个测试文件 538 条断言——全部 hermetic,默认 CI 下不碰网络、
547
+ 不要凭据(remote 面走包内的 sqlite stub)。
548
+
549
+ **留在 cloud(不随迁)**:按租户路由的 `multi-tenant.ts`(云产品差异化能力)及其 schema、
550
+ `vector-poc.test.ts`。因此本包的 barrel **不再导出** `createMultiTenantRouter` /
551
+ `MultiTenantConfig` / `MultiTenantRouter`,也不导出多租户 schema——它们从来不是这个 driver 的
552
+ 一部分,只是曾经同包而已。
553
+
554
+ **目录重组**:五个 `IDataDriver` 实现(`driver-memory` / `driver-mongodb` / `driver-sql` /
555
+ `driver-sqlite-wasm` + 迁入的 `driver-turso`)现在都住在 `packages/drivers/`,
556
+ `knowledge-*` 与 `embedder-*` 留在 `packages/plugins/`。四个存量包**内容零改动**,只有
557
+ `repository.directory` 随目录更新——包名、入口、导出面、行为全部不变,消费者无需改动任何 import。
558
+
559
+ 这也把 turso 交给了本仓的仓库级守卫:`check:driver-conformance` 从磁盘发现 driver 包,
560
+ 迁入即入矩阵(5 drivers × 5 case-sets)。它的 temporal 两格是真绿(local 与 remote 双面套件),
561
+ filter 组合语义与两个分页 case-set 记为 measured DEBT——remote 传输自带一套 `buildWhereSQL` 与
562
+ `LIMIT`/`OFFSET` 拼装,是独立实现,"继承所以没问题"正是这些共享套件存在来证伪的假设。
563
+ 补齐工作跟踪在 #5590。
564
+
565
+ - 9b43ee2: test(drivers): the filter-logic standard now covers the backend it was counted without (#4405)
566
+
567
+ `FILTER_LOGIC_CASES` (#3774) opens by calling itself the standard "the four
568
+ independent FilterCondition backends are each checked against". Five backends
569
+ exist. `driver-mongodb`'s `translateFilter` was missed, not excluded — an
570
+ independent implementation whose `$and`/`$or`/`$not` translation shares no line
571
+ of code with the SQL compiler or the in-memory matcher, and the only one whose
572
+ target language cannot spell the standard directly: MongoDB has no
573
+ document-level `$not` at all (the server answers `unknown top level operator:
574
+ $not`), so a negation has to leave as `$nor`, and a branch's own keys have to
575
+ stay in one document while `$and`/`$or` clauses are lifted beside them. That
576
+ route was never checked against the shared cases. Both DEBT rows the #4363 gate
577
+ recorded are now cleared, and `scripts/check-driver-conformance.mjs` reports
578
+ `ok` for every cell of the matrix.
579
+
580
+ **`driver-mongodb` runs the table twice, and the split is deliberate.**
581
+ `mongodb-filter-logic-translation.test.ts` drives every shared case through
582
+ `translateFilter` and evaluates the emitted MongoDB _document_ over the shared
583
+ fixture — a pure function, no server, so it always runs. That matters here more
584
+ than anywhere: `mongodb-memory-server` downloads a ~123 MB binary from
585
+ fastdl.mongodb.org, and a defect only a downloadable binary can catch is a
586
+ defect nobody catches on a restricted network. Its in-process reader is strict
587
+ by construction — every shape it does not model throws instead of evaluating to
588
+ true, a document-level `$not` included — and its own discrimination is pinned by
589
+ cases that require a widened document to FAIL the case it widens, so "all green"
590
+ cannot mean "the reader says yes to everything".
591
+ `mongodb-filter-logic-conformance.test.ts` runs the same table against a real
592
+ mongod and answers the one question the first half cannot — does MongoDB agree?
593
+ — skipping cleanly (never silently) when the binary is unreachable.
594
+
595
+ **`driver-sqlite-wasm` runs the table through its own engine.** It inherits
596
+ `SqlDriver`'s filter compiler, so nothing is re-implemented; what the suite pins
597
+ is that a nested `(… AND …) OR (… AND …)` survives the custom sql.js dialect
598
+ that compiles, binds and marshals it — the same seam its temporal and pagination
599
+ suites cover for their clauses. Tracked as DEBT rather than EXEMPT because
600
+ "inherits, therefore fine" is the assumption those suites exist to disprove; the
601
+ suite is what disproves it.
602
+
603
+ **No divergence was found.** `translateFilter` answers all seventeen shared
604
+ cases correctly today, `$not`-inside-a-branch and nested `$and`-inside-`$or`
605
+ included, so no translation change ships here — what changes is that the next
606
+ edit to it cannot quietly widen a filter. Both suites were verified to be
607
+ discriminating rather than decorative by reintroducing the #3774 miscompile
608
+ (propagating `or` into a branch's own contents): 15 of the mongodb translation
609
+ suite's 26 tests fail, and 13 of the wasm suite's 18.
610
+
611
+ `packages/spec`'s `filter-logic-conformance.ts` header now says five and names
612
+ the fifth — a code comment; no schema, export or generated artifact moved.
613
+
614
+ - c53aa53: File-backed SQLite now runs `journal_mode = WAL` (#3941).
615
+
616
+ `SqlDriver.connect()` set `auto_vacuum` and left the journal mode alone, so
617
+ every ObjectStack SQLite database ran SQLite's built-in default — a rollback
618
+ journal. That is the worst mode for the shape this platform actually has, which
619
+ is **several processes on one file**: a dev server, `os migrate`,
620
+ `os meta resync`, a test run. Measured, on the same file:
621
+
622
+ | | rollback journal | WAL |
623
+ | :--------------------------------------------- | :------------------------------------------------- | :---------------------------------------------------------------- |
624
+ | writer while another process holds a read open | `SQLITE_BUSY` — committing needs an exclusive lock | proceeds |
625
+ | idle attached connection visible to SQL | no — a lock lasts only as long as its transaction | yes (`locking_mode = EXCLUSIVE` + `BEGIN IMMEDIATE` reports busy) |
626
+
627
+ The second row is why the `os migrate` occupancy check had to inspect file
628
+ descriptors to see a live server at all (#3940): under a rollback journal there
629
+ was nothing in the database to see. That signal stays — it names the process,
630
+ which WAL's lock probe cannot — but the SQL probe is now authoritative for
631
+ databases ObjectStack created rather than a fallback that was blind in practice.
632
+ Concurrent _writers_ still serialize; SQLite allows one at a time in any mode.
633
+
634
+ Journal mode is a persistent property of the file, so an existing database is
635
+ converted in place on the next connect (a header change — no rows are touched)
636
+ and stays converted. Two consequences to plan for:
637
+
638
+ - `app.db-wal` / `app.db-shm` exist beside the database while a connection is
639
+ attached, and `app.db-wal` can hold committed transactions. A clean shutdown
640
+ checkpoints them away; a naive copy of `app.db` alone while a server runs does
641
+ not. Use `sqlite3 app.db ".backup …"`.
642
+ - **WAL does not work on network filesystems** (NFS/SMB). Opt out with
643
+ `OS_DATABASE_SQLITE_JOURNAL_MODE=delete`, or per datasource with
644
+ `sqliteJournalMode: 'delete'` in the driver config (which outranks the env
645
+ var). Either form _applies_ `delete`, so it also converts a database that
646
+ already adopted WAL back — skipping would have stranded it.
647
+
648
+ Nothing here fails a boot, and nothing is assumed: `PRAGMA journal_mode = X`
649
+ answers with the mode actually in force rather than raising on refusal, so the
650
+ reply is read back; and because a filesystem can accept WAL and then fail the
651
+ first read _through_ it, the mode is proven with a read and rolled back to
652
+ `delete` if that fails — with a warning naming the file and the escape hatch.
653
+ `synchronous` is untouched, so durability is exactly what it was. `:memory:`
654
+ databases are left alone, as is `auto_vacuum = INCREMENTAL`, which keeps
655
+ reclaiming under WAL (ADR-0057).
656
+
657
+ `os db clean` now counts `-wal` / `-shm` as part of the database when it measures
658
+ what a `VACUUM` reclaimed, so bytes that were sitting in the log do not read as a
659
+ reclaim of zero.
660
+
661
+ `@objectstack/driver-sqlite-wasm` deliberately stays out of WAL. Its live
662
+ database is in the WASM heap and what reaches disk is a byte image it exports, so
663
+ nothing reads the database across processes and the pragma buys it nothing —
664
+ while still being a persistent header change in the operator's file. sql.js
665
+ _accepts_ the pragma (its VFS is memory-backed), so this had to be declared
666
+ rather than discovered.
667
+
668
+ It also now parks a `-wal` left behind by an unclean native-driver exit rather
669
+ than loading the image beside it: wasm SQLite cannot read that log, and leaving
670
+ it next to a freshly rewritten image would let a later real SQLite replay frames
671
+ that no longer belong to it. The warning names the file it parked and how to
672
+ recover what was in it.
673
+
674
+ - bee5ffe: drivers: every SQL read door routes through the tenant chokepoint (#6792)
675
+
676
+ `SqlDriver.applyTenantScope()` owns read-side tenant isolation for the whole SQL family —
677
+ the `tenantId` early-out, the "object has no tenant field" early-out, the NULL-org
678
+ platform-row rule (#2734) and the ADR-0105 D2 union posture (#3623). Its own docstring
679
+ said "every CRUD method routes through it". Nothing ever checked that, and it was false
680
+ for as long as it had existed. **Three** read doors built their query through
681
+ `getBuilder()` and never arrived:
682
+
683
+ - **`findWithWindowFunctions()`** — the documented #4286 window door. It returns **rows**,
684
+ so on a deployment where the scope would have applied (`options.tenantId` set, object
685
+ has a tenant field) it returned rows belonging to **every** tenant. Measured with two
686
+ tenants seeded plus one NULL-org platform row: `tenantId: 'org_a'` returned
687
+ `[a1, a2, b1, b2, p1]` here against `find()`'s `[a1, a2, p1]` — another tenant's rows,
688
+ handed over at the driver layer.
689
+ - **`analyzeQuery()` / `explain()`** — returns a **plan**, not rows, so this is a smaller
690
+ fix and it is made on its own merits rather than folded into the one above. It is the
691
+ same defect #6577 fixed on these two methods one builder line lower: a plan is only
692
+ worth reading if it explains the statement `find()` would actually run, and a missing
693
+ tenant predicate changes selectivity and therefore which index the planner picks.
694
+ Compiled `select * from account` where `find()` sent the `organization_id` clause.
695
+ - **`distinct()`** — returns one column's **values** for every tenant. This one was in no
696
+ card. #6792 states the opposite, listing `distinct` among the scoped call sites; the
697
+ 13th read site is `aggregate()`. It was found by measuring the invariant rather than
698
+ re-reading it.
699
+
700
+ All three now call `applyTenantScope()` beside their `getBuilder()` line, the position
701
+ `findRows()` uses. They route through the chokepoint rather than re-deriving a predicate:
702
+ a local equality would silently drop NULL-org platform rows (#2734) and collapse group
703
+ reads to active-org reach (#3623). Both of the chokepoint's early-outs are inherited
704
+ unchanged, so an unscoped admin/seed read (no `tenantId`) and any object without a tenant
705
+ field behave exactly as before.
706
+
707
+ **The durable half is a gate, not the three lines.** `pnpm check:tenant-chokepoint`
708
+ (`scripts/check-tenant-chokepoint.mjs`, wired into `.github/workflows/lint.yml`) re-derives
709
+ the invariant from the AST across the `SqlDriver` family on every run: a method that builds
710
+ through `getBuilder(object, options)` must call `applyTenantScope()` on that builder, or
711
+ carry a written exemption. Insert builders are exempt structurally — write-side tenancy is
712
+ `injectTenantOnInsert` — rather than by a name list. It is keyed on the **builder** and not
713
+ on the method signature, because the signature criterion the card sketches ("takes
714
+ `(object, …, options)` and returns rows") misses `distinct` (no `query` parameter) and
715
+ `analyzeQuery` (returns a plan). Verified red against the pre-fix tree, red against a
716
+ newly-added unscoped door, and silent once that door is scoped.
717
+
718
+ The chokepoint docstring no longer asserts the invariant; it names the gate that proves it.
719
+
720
+ If you call these doors directly on a multi-tenant deployment, pass `options.tenantId` as
721
+ you would to `find()` — that is what now takes effect. Callers that never passed it are
722
+ unaffected; that remains the documented unscoped/admin path.
723
+
724
+ - 24915d2: fix(driver-sqlite-wasm): a `RETURNING` write is a write — persist it (#4518)
725
+
726
+ A file-backed `sqlite-wasm` database flushed its schema at boot and then
727
+ recorded nothing else. Every table was on disk; every row written after schema
728
+ sync lived only in the WASM heap and died with the process. Reopening the file
729
+ found a complete, empty database.
730
+
731
+ **Cause.** The Knex dialect picked its execution branch from _"does this
732
+ statement return rows"_ — and then marked the database dirty only on the other,
733
+ row-less branch. `INSERT … RETURNING *` returns rows, so it executed on the
734
+ row-returning branch and never set the flag. Since the `on-disconnect` flush is
735
+ gated on the same flag, nothing rescued it afterwards either: **both** persist
736
+ strategies dropped the write. ObjectQL writes through `RETURNING *` (it hands
737
+ the stored row back to the caller), so this covered essentially all business
738
+ data, along with `knex.raw('INSERT …')` and any other mutation arriving without
739
+ a Knex `method`.
740
+
741
+ **Fix.** "Does this statement change the database?" is now one exported
742
+ predicate — `statementMutatesDatabase(sql, method)` — classifying by Knex method
743
+ _and_ SQL text, applied at a single funnel after execution. It is independent of
744
+ which branch executed the statement, so a mutation can no longer slip through by
745
+ returning rows, by arriving without a method, or by taking a branch that forgot
746
+ to say so. Transaction control still routes to `noteTransactionControl`, which
747
+ keeps deferring flushes until the transaction closes (#1494), and mutating
748
+ `PRAGMA` assignments (`auto_vacuum`, `user_version`) now count as writes too.
749
+
750
+ **What changes for you.** Nothing to author. File-backed wasm SQLite now
751
+ actually persists under `on-write` / `debounced:*`, and `disconnect()` is a real
752
+ durability boundary: when it returns, committed data is on disk. This is what
753
+ `bootStack({ databaseFile })` in `@objectstack/verify` needed to make `stop()` →
754
+ second `bootStack` a genuine cold boot — the suspended-run restart proof
755
+ ADR-0019 promises is now asserted end to end in the dogfood gate. Expect more
756
+ disk writes than before on a file-backed dev database, because previously there
757
+ were almost none.
758
+
759
+ **One internal signature moved.** `WasmSqliteConnection.markDirty(method?)` is
760
+ now `markDirty()`. It used to re-filter the caller's Knex method against its own
761
+ allowlist, which made "did this mutate?" a decision taken in two places that
762
+ could — and did — disagree. If you call it directly, drop the argument; the
763
+ dialect classifies, the connection obeys.
764
+
765
+ - Updated dependencies [50616d9]
766
+ - Updated dependencies [430dcc2]
767
+ - Updated dependencies [690ccf2]
768
+ - Updated dependencies [6a67d7a]
769
+ - Updated dependencies [333a374]
770
+ - Updated dependencies [9fe9c1d]
771
+ - Updated dependencies [3d5c090]
772
+ - Updated dependencies [e5bd768]
773
+ - Updated dependencies [08b5a3d]
774
+ - Updated dependencies [e027b3e]
775
+ - Updated dependencies [e6ac4bd]
776
+ - Updated dependencies [c2429b0]
777
+ - Updated dependencies [445a0c2]
778
+ - Updated dependencies [d99aeb3]
779
+ - Updated dependencies [f6609e6]
780
+ - Updated dependencies [4727eb8]
781
+ - Updated dependencies [a70358a]
782
+ - Updated dependencies [0ecc656]
783
+ - Updated dependencies [06772eb]
784
+ - Updated dependencies [d4e0809]
785
+ - Updated dependencies [80334c7]
786
+ - Updated dependencies [f63cd09]
787
+ - Updated dependencies [97e7e3c]
788
+ - Updated dependencies [ce5242c]
789
+ - Updated dependencies [a7163ea]
790
+ - Updated dependencies [e6e9379]
791
+ - Updated dependencies [5823d59]
792
+ - Updated dependencies [3140f9c]
793
+ - Updated dependencies [9500ba4]
794
+ - Updated dependencies [fa3d0cf]
795
+ - Updated dependencies [af5a224]
796
+ - Updated dependencies [71f76e1]
797
+ - Updated dependencies [37b1346]
798
+ - Updated dependencies [99736a0]
799
+ - Updated dependencies [fe67e34]
800
+ - Updated dependencies [fdb4f50]
801
+ - Updated dependencies [270650f]
802
+ - Updated dependencies [3aef718]
803
+ - Updated dependencies [1bd5652]
804
+ - Updated dependencies [14252d3]
805
+ - Updated dependencies [7fb436c]
806
+ - Updated dependencies [879ea13]
807
+ - Updated dependencies [8828b9e]
808
+ - Updated dependencies [1ea6bce]
809
+ - Updated dependencies [c1dcacd]
810
+ - Updated dependencies [ad303ed]
811
+ - Updated dependencies [32ccb23]
812
+ - Updated dependencies [f5a4ef0]
813
+ - Updated dependencies [2d3e255]
814
+ - Updated dependencies [a8940e4]
815
+ - Updated dependencies [7d7521f]
816
+ - Updated dependencies [5dc4d02]
817
+ - Updated dependencies [f724f69]
818
+ - Updated dependencies [98877c9]
819
+ - Updated dependencies [98877c9]
820
+ - Updated dependencies [53068c1]
821
+ - Updated dependencies [ee58392]
822
+ - Updated dependencies [f16e54e]
823
+ - Updated dependencies [06be54e]
824
+ - Updated dependencies [28ad90e]
825
+ - Updated dependencies [76d74ec]
826
+ - Updated dependencies [201b31f]
827
+ - Updated dependencies [e6b1b69]
828
+ - Updated dependencies [29e28a3]
829
+ - Updated dependencies [c7f4417]
830
+ - Updated dependencies [259459d]
831
+ - Updated dependencies [3f7f14e]
832
+ - Updated dependencies [e2616e0]
833
+ - Updated dependencies [6fdc5c6]
834
+ - Updated dependencies [8b9d71e]
835
+ - Updated dependencies [05154a1]
836
+ - Updated dependencies [33f5e23]
837
+ - Updated dependencies [259af21]
838
+ - Updated dependencies [f8644c7]
839
+ - Updated dependencies [306ca50]
840
+ - Updated dependencies [978fed2]
841
+ - Updated dependencies [cfc293f]
842
+ - Updated dependencies [587fc91]
843
+ - Updated dependencies [de70b42]
844
+ - Updated dependencies [9b6fe7c]
845
+ - Updated dependencies [fb3d99b]
846
+ - Updated dependencies [1986594]
847
+ - Updated dependencies [6968885]
848
+ - Updated dependencies [eaed61f]
849
+ - Updated dependencies [cdfbee2]
850
+ - Updated dependencies [ad4af62]
851
+ - Updated dependencies [debe2f6]
852
+ - Updated dependencies [d44dbfa]
853
+ - Updated dependencies [29c6c9d]
854
+ - Updated dependencies [d21c001]
855
+ - Updated dependencies [ad047d2]
856
+ - Updated dependencies [8c711fb]
857
+ - Updated dependencies [f1cc3a3]
858
+ - Updated dependencies [09e4547]
859
+ - Updated dependencies [97b0798]
860
+ - Updated dependencies [474fe39]
861
+ - Updated dependencies [0bc685a]
862
+ - Updated dependencies [b949059]
863
+ - Updated dependencies [2826d1e]
864
+ - Updated dependencies [be1c52c]
865
+ - Updated dependencies [c5ff96d]
866
+ - Updated dependencies [5a84d41]
867
+ - Updated dependencies [84e7be9]
868
+ - Updated dependencies [91f4c78]
869
+ - Updated dependencies [ddc2527]
870
+ - Updated dependencies [820eff9]
871
+ - Updated dependencies [a6c3f38]
872
+ - Updated dependencies [debc23a]
873
+ - Updated dependencies [0f8ad09]
874
+ - Updated dependencies [553a47f]
875
+ - Updated dependencies [43a7a8d]
876
+ - Updated dependencies [a98085f]
877
+ - Updated dependencies [20b1a9e]
878
+ - Updated dependencies [344a22a]
879
+ - Updated dependencies [4827e91]
880
+ - Updated dependencies [8d895ff]
881
+ - Updated dependencies [86f7a20]
882
+ - Updated dependencies [a3a884d]
883
+ - Updated dependencies [cfed092]
884
+ - Updated dependencies [203a449]
885
+ - Updated dependencies [8f9689f]
886
+ - Updated dependencies [73f69dc]
887
+ - Updated dependencies [04c56aa]
888
+ - Updated dependencies [f6472d7]
889
+ - Updated dependencies [57a3bb3]
890
+ - Updated dependencies [b3efeb7]
891
+ - Updated dependencies [ecb39ea]
892
+ - Updated dependencies [ddd075a]
893
+ - Updated dependencies [88154be]
894
+ - Updated dependencies [db12b88]
895
+ - Updated dependencies [62452c6]
896
+ - Updated dependencies [6f6fec7]
897
+ - Updated dependencies [7d1ff75]
898
+ - Updated dependencies [e8dc61e]
899
+ - Updated dependencies [9c82146]
900
+ - Updated dependencies [5f9a987]
901
+ - Updated dependencies [744b8f5]
902
+ - Updated dependencies [ac37fc6]
903
+ - Updated dependencies [2f3e793]
904
+ - Updated dependencies [4820f55]
905
+ - Updated dependencies [462d9c4]
906
+ - Updated dependencies [78caf51]
907
+ - Updated dependencies [7d21581]
908
+ - Updated dependencies [37785ed]
909
+ - Updated dependencies [62a789b]
910
+ - Updated dependencies [2e284b2]
911
+ - Updated dependencies [d8e8d9c]
912
+ - Updated dependencies [789ad63]
913
+ - Updated dependencies [f2445c9]
914
+ - Updated dependencies [94e749b]
915
+ - Updated dependencies [ea1d916]
916
+ - Updated dependencies [2af1988]
917
+ - Updated dependencies [0af50a3]
918
+ - Updated dependencies [1b49eaf]
919
+ - Updated dependencies [ae31a19]
920
+ - Updated dependencies [2e836de]
921
+ - Updated dependencies [e0f300b]
922
+ - Updated dependencies [0161c7f]
923
+ - Updated dependencies [e900015]
924
+ - Updated dependencies [db02d47]
925
+ - Updated dependencies [b5bdf48]
926
+ - Updated dependencies [23338c3]
927
+ - Updated dependencies [12a19a8]
928
+ - Updated dependencies [5b843fb]
929
+ - Updated dependencies [62b6a2f]
930
+ - Updated dependencies [7e5af5c]
931
+ - Updated dependencies [5b4780b]
932
+ - Updated dependencies [a933452]
933
+ - Updated dependencies [9d1d9c7]
934
+ - Updated dependencies [8140915]
935
+ - Updated dependencies [a019e52]
936
+ - Updated dependencies [e8f8f6c]
937
+ - Updated dependencies [41dcda3]
938
+ - Updated dependencies [7b48cf9]
939
+ - Updated dependencies [b5404f4]
940
+ - Updated dependencies [64fc6d5]
941
+ - Updated dependencies [b746aa0]
942
+ - Updated dependencies [b4487aa]
943
+ - Updated dependencies [1007379]
944
+ - Updated dependencies [65ca83a]
945
+ - Updated dependencies [0bfdf46]
946
+ - Updated dependencies [947d4f9]
947
+ - Updated dependencies [f764691]
948
+ - Updated dependencies [e120a5a]
949
+ - Updated dependencies [e5bd2f6]
950
+ - Updated dependencies [e650d67]
951
+ - Updated dependencies [04476e7]
952
+ - Updated dependencies [67bf2e2]
953
+ - Updated dependencies [eaaf03c]
954
+ - Updated dependencies [d17df80]
955
+ - Updated dependencies [7d0e7b5]
956
+ - Updated dependencies [c6d1cb4]
957
+ - Updated dependencies [6513c17]
958
+ - Updated dependencies [36030ff]
959
+ - Updated dependencies [79228cd]
960
+ - Updated dependencies [6117f7b]
961
+ - Updated dependencies [e533b0b]
962
+ - Updated dependencies [cdf4d9a]
963
+ - Updated dependencies [aee1806]
964
+ - Updated dependencies [c13350b]
965
+ - Updated dependencies [c13350b]
966
+ - Updated dependencies [2c1988c]
967
+ - Updated dependencies [9ca2d85]
968
+ - Updated dependencies [c13350b]
969
+ - Updated dependencies [891d345]
970
+ - Updated dependencies [42e3b01]
971
+ - Updated dependencies [c8124e5]
972
+ - Updated dependencies [a52e2ef]
973
+ - Updated dependencies [5293114]
974
+ - Updated dependencies [376a061]
975
+ - Updated dependencies [c142ced]
976
+ - Updated dependencies [211abdb]
977
+ - Updated dependencies [b3363e9]
978
+ - Updated dependencies [39eb01b]
979
+ - Updated dependencies [eda599e]
980
+ - Updated dependencies [a1a4140]
981
+ - Updated dependencies [7c7e246]
982
+ - Updated dependencies [2ef1807]
983
+ - Updated dependencies [f35cdc5]
984
+ - Updated dependencies [d03fe25]
985
+ - Updated dependencies [217e2e6]
986
+ - Updated dependencies [2672f85]
987
+ - Updated dependencies [20bc357]
988
+ - Updated dependencies [11066f6]
989
+ - Updated dependencies [916af17]
990
+ - Updated dependencies [84c86fb]
991
+ - Updated dependencies [2a2a9fb]
992
+ - Updated dependencies [86a71d1]
993
+ - Updated dependencies [c001422]
994
+ - Updated dependencies [77022a9]
995
+ - Updated dependencies [d5c75e2]
996
+ - Updated dependencies [03d26f7]
997
+ - Updated dependencies [5966c2a]
998
+ - Updated dependencies [2382580]
999
+ - Updated dependencies [9ea2bc5]
1000
+ - Updated dependencies [a2e157c]
1001
+ - Updated dependencies [95c4227]
1002
+ - Updated dependencies [2a61116]
1003
+ - Updated dependencies [52760bf]
1004
+ - Updated dependencies [5543020]
1005
+ - Updated dependencies [880d343]
1006
+ - Updated dependencies [6e82972]
1007
+ - Updated dependencies [d4df105]
1008
+ - Updated dependencies [4615a18]
1009
+ - Updated dependencies [f505689]
1010
+ - Updated dependencies [d9fa683]
1011
+ - Updated dependencies [32d3800]
1012
+ - Updated dependencies [606d577]
1013
+ - Updated dependencies [4384921]
1014
+ - Updated dependencies [d367f03]
1015
+ - Updated dependencies [45e711a]
1016
+ - Updated dependencies [465a0fa]
1017
+ - Updated dependencies [cf5e033]
1018
+ - Updated dependencies [6de592c]
1019
+ - Updated dependencies [d254421]
1020
+ - Updated dependencies [e2798fa]
1021
+ - Updated dependencies [06ba036]
1022
+ - Updated dependencies [3c628ce]
1023
+ - Updated dependencies [c2d9098]
1024
+ - Updated dependencies [0fd8556]
1025
+ - Updated dependencies [3c7bcc0]
1026
+ - Updated dependencies [4b6cac7]
1027
+ - Updated dependencies [7631964]
1028
+ - Updated dependencies [ac471a0]
1029
+ - Updated dependencies [60ae58e]
1030
+ - Updated dependencies [7f62706]
1031
+ - Updated dependencies [667fa44]
1032
+ - Updated dependencies [37e38d1]
1033
+ - Updated dependencies [e906126]
1034
+ - Updated dependencies [ce92674]
1035
+ - Updated dependencies [08363a0]
1036
+ - Updated dependencies [444de5b]
1037
+ - Updated dependencies [0f17114]
1038
+ - Updated dependencies [a227ed7]
1039
+ - Updated dependencies [7cb922e]
1040
+ - Updated dependencies [1d22114]
1041
+ - Updated dependencies [1eb13a0]
1042
+ - Updated dependencies [c52e608]
1043
+ - Updated dependencies [9613396]
1044
+ - Updated dependencies [3f7b4ff]
1045
+ - Updated dependencies [74155c7]
1046
+ - Updated dependencies [b5f9397]
1047
+ - Updated dependencies [ed77493]
1048
+ - Updated dependencies [6908830]
1049
+ - Updated dependencies [8b06bba]
1050
+ - Updated dependencies [58a03d2]
1051
+ - Updated dependencies [2bacd1a]
1052
+ - Updated dependencies [e47b342]
1053
+ - Updated dependencies [4c54037]
1054
+ - Updated dependencies [dc530b4]
1055
+ - Updated dependencies [9f601e8]
1056
+ - Updated dependencies [6a9dec6]
1057
+ - Updated dependencies [0f7157b]
1058
+ - Updated dependencies [4dc1c7d]
1059
+ - Updated dependencies [d9bef45]
1060
+ - Updated dependencies [4dfd002]
1061
+ - Updated dependencies [f549a0d]
1062
+ - Updated dependencies [51c5227]
1063
+ - Updated dependencies [82da264]
1064
+ - Updated dependencies [f586f1a]
1065
+ - Updated dependencies [77be690]
1066
+ - Updated dependencies [4ed7ed4]
1067
+ - Updated dependencies [9b9b70f]
1068
+ - Updated dependencies [f5a9bc2]
1069
+ - Updated dependencies [e59786e]
1070
+ - Updated dependencies [2fa4ca1]
1071
+ - Updated dependencies [bcf1112]
1072
+ - Updated dependencies [baeb4f0]
1073
+ - Updated dependencies [29488cc]
1074
+ - Updated dependencies [881a3cc]
1075
+ - Updated dependencies [f5a2320]
1076
+ - Updated dependencies [ad6317b]
1077
+ - Updated dependencies [811c30c]
1078
+ - Updated dependencies [a4a85c8]
1079
+ - Updated dependencies [859cb83]
1080
+ - Updated dependencies [07a4e26]
1081
+ - Updated dependencies [9774b78]
1082
+ - Updated dependencies [8a88885]
1083
+ - Updated dependencies [deb538f]
1084
+ - Updated dependencies [b49ccfd]
1085
+ - Updated dependencies [c7406b0]
1086
+ - Updated dependencies [5b89711]
1087
+ - Updated dependencies [85d95e7]
1088
+ - Updated dependencies [08cd163]
1089
+ - Updated dependencies [0c8a22f]
1090
+ - Updated dependencies [6f98c2d]
1091
+ - Updated dependencies [5f7669e]
1092
+ - Updated dependencies [becbe53]
1093
+ - Updated dependencies [b127c8b]
1094
+ - Updated dependencies [763931e]
1095
+ - Updated dependencies [ec975f1]
1096
+ - Updated dependencies [168f60f]
1097
+ - Updated dependencies [b07d829]
1098
+ - Updated dependencies [de9af8a]
1099
+ - Updated dependencies [eb4204b]
1100
+ - Updated dependencies [a80302a]
1101
+ - Updated dependencies [a648e96]
1102
+ - Updated dependencies [a47ac06]
1103
+ - Updated dependencies [e4c61a7]
1104
+ - Updated dependencies [cc60165]
1105
+ - Updated dependencies [474f131]
1106
+ - Updated dependencies [081aa6f]
1107
+ - Updated dependencies [91f4c78]
1108
+ - Updated dependencies [050cd82]
1109
+ - Updated dependencies [4d552af]
1110
+ - Updated dependencies [44d677c]
1111
+ - Updated dependencies [c32944d]
1112
+ - Updated dependencies [1dd780f]
1113
+ - Updated dependencies [e8d0c21]
1114
+ - Updated dependencies [244ca86]
1115
+ - Updated dependencies [546ab3c]
1116
+ - Updated dependencies [c4df271]
1117
+ - Updated dependencies [c8d6f6e]
1118
+ - Updated dependencies [0b51bb6]
1119
+ - Updated dependencies [d9971d3]
1120
+ - Updated dependencies [7dc1067]
1121
+ - Updated dependencies [4f13be2]
1122
+ - Updated dependencies [a41ba5c]
1123
+ - Updated dependencies [189854c]
1124
+ - Updated dependencies [5d4de37]
1125
+ - Updated dependencies [0e3a226]
1126
+ - Updated dependencies [92a67f2]
1127
+ - Updated dependencies [9136327]
1128
+ - Updated dependencies [bf0ae99]
1129
+ - Updated dependencies [eb3e650]
1130
+ - Updated dependencies [abeb375]
1131
+ - Updated dependencies [cb3b6cd]
1132
+ - Updated dependencies [73b7234]
1133
+ - Updated dependencies [d2b97c3]
1134
+ - Updated dependencies [61cc079]
1135
+ - Updated dependencies [45dc446]
1136
+ - Updated dependencies [0e96e46]
1137
+ - Updated dependencies [c1d44f7]
1138
+ - Updated dependencies [59b794f]
1139
+ - Updated dependencies [ef4efa8]
1140
+ - Updated dependencies [cbb6a5c]
1141
+ - Updated dependencies [fc3a36a]
1142
+ - Updated dependencies [ab9fb5c]
1143
+ - Updated dependencies [69787f0]
1144
+ - Updated dependencies [5d022a1]
1145
+ - Updated dependencies [042b9ee]
1146
+ - Updated dependencies [f985b3f]
1147
+ - Updated dependencies [795b6e1]
1148
+ - Updated dependencies [d52d4fe]
1149
+ - Updated dependencies [742cebb]
1150
+ - Updated dependencies [175d789]
1151
+ - Updated dependencies [f549a0d]
1152
+ - Updated dependencies [427344c]
1153
+ - Updated dependencies [8af76ae]
1154
+ - Updated dependencies [1d4756e]
1155
+ - Updated dependencies [720c5ad]
1156
+ - Updated dependencies [a8d1e24]
1157
+ - Updated dependencies [b85cc54]
1158
+ - Updated dependencies [a36db28]
1159
+ - Updated dependencies [7a8476f]
1160
+ - Updated dependencies [518ca7a]
1161
+ - Updated dependencies [41642b0]
1162
+ - Updated dependencies [4cca74c]
1163
+ - Updated dependencies [88ef03e]
1164
+ - Updated dependencies [9a4932a]
1165
+ - Updated dependencies [3f8817a]
1166
+ - Updated dependencies [a2443e3]
1167
+ - Updated dependencies [e1554b1]
1168
+ - Updated dependencies [9e2caf3]
1169
+ - Updated dependencies [4856789]
1170
+ - Updated dependencies [81ce41a]
1171
+ - Updated dependencies [85e1e4e]
1172
+ - Updated dependencies [c3f4916]
1173
+ - Updated dependencies [55dbbba]
1174
+ - Updated dependencies [33e0385]
1175
+ - Updated dependencies [dac6a08]
1176
+ - Updated dependencies [72c3c86]
1177
+ - Updated dependencies [2d8dba3]
1178
+ - Updated dependencies [7f1a635]
1179
+ - Updated dependencies [2205363]
1180
+ - Updated dependencies [09fe58d]
1181
+ - Updated dependencies [f9fc874]
1182
+ - Updated dependencies [d62f8eb]
1183
+ - Updated dependencies [d0a5ceb]
1184
+ - Updated dependencies [a7586cd]
1185
+ - Updated dependencies [4c5e80e]
1186
+ - Updated dependencies [ef678d0]
1187
+ - Updated dependencies [4b5702a]
1188
+ - Updated dependencies [011b386]
1189
+ - Updated dependencies [e18a162]
1190
+ - Updated dependencies [394b7a1]
1191
+ - Updated dependencies [ce92674]
1192
+ - Updated dependencies [0f2fdcd]
1193
+ - Updated dependencies [d6d1a50]
1194
+ - Updated dependencies [cf2c9b7]
1195
+ - Updated dependencies [8ffa8b9]
1196
+ - Updated dependencies [d127ff0]
1197
+ - Updated dependencies [674ac99]
1198
+ - Updated dependencies [833b512]
1199
+ - Updated dependencies [36d90fc]
1200
+ - Updated dependencies [7777e8f]
1201
+ - Updated dependencies [9b86cf6]
1202
+ - Updated dependencies [d063a96]
1203
+ - Updated dependencies [8825a06]
1204
+ - Updated dependencies [5087ac6]
1205
+ - Updated dependencies [677b591]
1206
+ - Updated dependencies [cf7c694]
1207
+ - Updated dependencies [ddd0f06]
1208
+ - Updated dependencies [d77d1b7]
1209
+ - Updated dependencies [0f9faa2]
1210
+ - Updated dependencies [2d1ddf0]
1211
+ - Updated dependencies [354b00f]
1212
+ - Updated dependencies [3de535b]
1213
+ - Updated dependencies [fe2e15a]
1214
+ - Updated dependencies [5b79a34]
1215
+ - Updated dependencies [502564d]
1216
+ - Updated dependencies [603cab8]
1217
+ - Updated dependencies [c757854]
1218
+ - Updated dependencies [471839d]
1219
+ - Updated dependencies [507b92a]
1220
+ - Updated dependencies [46365ab]
1221
+ - Updated dependencies [b508244]
1222
+ - Updated dependencies [6146b67]
1223
+ - Updated dependencies [df95346]
1224
+ - Updated dependencies [3dede58]
1225
+ - Updated dependencies [c6b6bb4]
1226
+ - Updated dependencies [594508e]
1227
+ - Updated dependencies [7cf42fe]
1228
+ - Updated dependencies [5966c2a]
1229
+ - Updated dependencies [0045682]
1230
+ - Updated dependencies [7309c81]
1231
+ - Updated dependencies [2f59da0]
1232
+ - Updated dependencies [d56012f]
1233
+ - Updated dependencies [f78dd83]
1234
+ - Updated dependencies [a2cd18a]
1235
+ - Updated dependencies [9051802]
1236
+ - Updated dependencies [20bc1ec]
1237
+ - Updated dependencies [1c625ca]
1238
+ - Updated dependencies [2f8328c]
1239
+ - Updated dependencies [2a6c279]
1240
+ - Updated dependencies [9319586]
1241
+ - Updated dependencies [8c8f0df]
1242
+ - Updated dependencies [8ad609c]
1243
+ - Updated dependencies [bbee302]
1244
+ - Updated dependencies [90c2b15]
1245
+ - Updated dependencies [4638aaa]
1246
+ - Updated dependencies [0222d3c]
1247
+ - Updated dependencies [08863dd]
1248
+ - Updated dependencies [33a5ff4]
1249
+ - Updated dependencies [9e01213]
1250
+ - Updated dependencies [071d0dc]
1251
+ - Updated dependencies [f293d45]
1252
+ - Updated dependencies [56664f5]
1253
+ - Updated dependencies [71f205d]
1254
+ - Updated dependencies [f067930]
1255
+ - Updated dependencies [414395b]
1256
+ - Updated dependencies [42eeb7d]
1257
+ - Updated dependencies [31cbe90]
1258
+ - Updated dependencies [6b7129a]
1259
+ - Updated dependencies [c5adfe1]
1260
+ - Updated dependencies [97ace2a]
1261
+ - Updated dependencies [26e1029]
1262
+ - Updated dependencies [0a936ea]
1263
+ - Updated dependencies [90bbf25]
1264
+ - Updated dependencies [023c00b]
1265
+ - Updated dependencies [eb91eba]
1266
+ - Updated dependencies [42da73d]
1267
+ - Updated dependencies [01e124d]
1268
+ - Updated dependencies [4addd9d]
1269
+ - Updated dependencies [ef7b5ef]
1270
+ - Updated dependencies [9514767]
1271
+ - Updated dependencies [8f20201]
1272
+ - Updated dependencies [155507e]
1273
+ - Updated dependencies [643b7c7]
1274
+ - Updated dependencies [7bba90b]
1275
+ - Updated dependencies [8813b90]
1276
+ - Updated dependencies [108ba8d]
1277
+ - Updated dependencies [2a5f04a]
1278
+ - Updated dependencies [4f740b0]
1279
+ - Updated dependencies [7ce02eb]
1280
+ - Updated dependencies [b4ad984]
1281
+ - Updated dependencies [a9f32df]
1282
+ - Updated dependencies [aeb9b27]
1283
+ - Updated dependencies [7d27da0]
1284
+ - Updated dependencies [d0d5205]
1285
+ - Updated dependencies [4fccace]
1286
+ - Updated dependencies [1a15893]
1287
+ - Updated dependencies [b70e534]
1288
+ - Updated dependencies [7e05d8e]
1289
+ - Updated dependencies [8f1851e]
1290
+ - Updated dependencies [61ea810]
1291
+ - Updated dependencies [2233a85]
1292
+ - Updated dependencies [67452d1]
1293
+ - Updated dependencies [089767f]
1294
+ - Updated dependencies [a13827e]
1295
+ - Updated dependencies [66d99ec]
1296
+ - Updated dependencies [cb43296]
1297
+ - Updated dependencies [b61afc1]
1298
+ - Updated dependencies [79021fc]
1299
+ - Updated dependencies [7733604]
1300
+ - Updated dependencies [40e420f]
1301
+ - Updated dependencies [62dd69a]
1302
+ - Updated dependencies [d13004a]
1303
+ - Updated dependencies [be7360c]
1304
+ - Updated dependencies [e15e679]
1305
+ - Updated dependencies [2ddba89]
1306
+ - Updated dependencies [2ab1257]
1307
+ - Updated dependencies [3fe0ff1]
1308
+ - Updated dependencies [0fc6219]
1309
+ - Updated dependencies [061406d]
1310
+ - Updated dependencies [e4c8b6c]
1311
+ - Updated dependencies [acb10f6]
1312
+ - Updated dependencies [605e190]
1313
+ - Updated dependencies [c6c59f1]
1314
+ - Updated dependencies [b0e78a8]
1315
+ - Updated dependencies [f31cc8d]
1316
+ - Updated dependencies [f343dc4]
1317
+ - Updated dependencies [8269e32]
1318
+ - Updated dependencies [74f7339]
1319
+ - Updated dependencies [a6c35a2]
1320
+ - Updated dependencies [c2f1002]
1321
+ - Updated dependencies [4cc4fb7]
1322
+ - Updated dependencies [97b6658]
1323
+ - Updated dependencies [28d1eb7]
1324
+ - Updated dependencies [06770c0]
1325
+ - Updated dependencies [2c26040]
1326
+ - Updated dependencies [f758cec]
1327
+ - Updated dependencies [5b47ab5]
1328
+ - Updated dependencies [b09d8d9]
1329
+ - Updated dependencies [b09d8d9]
1330
+ - Updated dependencies [8675db6]
1331
+ - Updated dependencies [b09d8d9]
1332
+ - Updated dependencies [27358d5]
1333
+ - Updated dependencies [1c3da1f]
1334
+ - Updated dependencies [c1f344b]
1335
+ - Updated dependencies [3eb1b2b]
1336
+ - Updated dependencies [9c93465]
1337
+ - Updated dependencies [a34fd2e]
1338
+ - Updated dependencies [ebb209c]
1339
+ - Updated dependencies [76bcb83]
1340
+ - Updated dependencies [59b85c0]
1341
+ - Updated dependencies [889ae47]
1342
+ - Updated dependencies [4f4c3fb]
1343
+ - Updated dependencies [78f0be8]
1344
+ - Updated dependencies [6e357ed]
1345
+ - Updated dependencies [d6938bf]
1346
+ - Updated dependencies [35f7fb4]
1347
+ - Updated dependencies [0410522]
1348
+ - Updated dependencies [63b33e6]
1349
+ - Updated dependencies [f163028]
1350
+ - Updated dependencies [814db6d]
1351
+ - Updated dependencies [a5302c7]
1352
+ - Updated dependencies [9c5abf4]
1353
+ - Updated dependencies [82397b6]
1354
+ - Updated dependencies [31e0be9]
1355
+ - Updated dependencies [4bfd455]
1356
+ - Updated dependencies [ffd2ce2]
1357
+ - Updated dependencies [2a44c1d]
1358
+ - Updated dependencies [7084313]
1359
+ - Updated dependencies [f07808c]
1360
+ - Updated dependencies [7ffc3d3]
1361
+ - Updated dependencies [88346ba]
1362
+ - Updated dependencies [4631592]
1363
+ - Updated dependencies [62f8017]
1364
+ - Updated dependencies [32ff033]
1365
+ - Updated dependencies [a831df1]
1366
+ - Updated dependencies [f752ee3]
1367
+ - Updated dependencies [a1b61e0]
1368
+ - Updated dependencies [cd6b9f2]
1369
+ - Updated dependencies [2cb6d3c]
1370
+ - Updated dependencies [af2a095]
1371
+ - Updated dependencies [5ac93d4]
1372
+ - Updated dependencies [695cfbd]
1373
+ - Updated dependencies [0e043d8]
1374
+ - Updated dependencies [93f267f]
1375
+ - Updated dependencies [7445149]
1376
+ - Updated dependencies [ec796d5]
1377
+ - Updated dependencies [071d0dc]
1378
+ - Updated dependencies [0024abf]
1379
+ - Updated dependencies [8dd98bf]
1380
+ - Updated dependencies [e87fea1]
1381
+ - Updated dependencies [c65e529]
1382
+ - Updated dependencies [0848bea]
1383
+ - Updated dependencies [d51bed2]
1384
+ - Updated dependencies [dadd1ad]
1385
+ - Updated dependencies [acbf364]
1386
+ - Updated dependencies [3ca34c1]
1387
+ - Updated dependencies [7adc841]
1388
+ - Updated dependencies [239c3a3]
1389
+ - Updated dependencies [b8b3c64]
1390
+ - Updated dependencies [2f2e63c]
1391
+ - Updated dependencies [4845f85]
1392
+ - Updated dependencies [486d526]
1393
+ - Updated dependencies [94a0bbc]
1394
+ - Updated dependencies [d6bfb3d]
1395
+ - Updated dependencies [8a9c079]
1396
+ - Updated dependencies [7b005b4]
1397
+ - Updated dependencies [cc3555e]
1398
+ - Updated dependencies [a2266a6]
1399
+ - Updated dependencies [d25a0ec]
1400
+ - Updated dependencies [89d7b35]
1401
+ - Updated dependencies [94f7b6a]
1402
+ - Updated dependencies [5c94f83]
1403
+ - Updated dependencies [ea936f3]
1404
+ - Updated dependencies [f98fa65]
1405
+ - Updated dependencies [0c0fbd9]
1406
+ - Updated dependencies [667b83e]
1407
+ - Updated dependencies [f3141d8]
1408
+ - Updated dependencies [7687f7b]
1409
+ - Updated dependencies [5a84d41]
1410
+ - Updated dependencies [fd3013a]
1411
+ - Updated dependencies [85ec26d]
1412
+ - Updated dependencies [73e576f]
1413
+ - Updated dependencies [f6476fc]
1414
+ - Updated dependencies [69ac82c]
1415
+ - Updated dependencies [4ac12ef]
1416
+ - Updated dependencies [833ed84]
1417
+ - Updated dependencies [a18abf3]
1418
+ - Updated dependencies [c6a4eeb]
1419
+ - Updated dependencies [1659072]
1420
+ - Updated dependencies [f450ae7]
1421
+ - Updated dependencies [abceb0d]
1422
+ - Updated dependencies [627b188]
1423
+ - Updated dependencies [8d4eae7]
1424
+ - Updated dependencies [c5a5996]
1425
+ - Updated dependencies [0c302a7]
1426
+ - Updated dependencies [b88f5e8]
1427
+ - Updated dependencies [857a6cf]
1428
+ - Updated dependencies [65a3a84]
1429
+ - Updated dependencies [6633337]
1430
+ - Updated dependencies [21676eb]
1431
+ - Updated dependencies [e9cb9ab]
1432
+ - Updated dependencies [42cc219]
1433
+ - Updated dependencies [d7e0b42]
1434
+ - Updated dependencies [3510e4a]
1435
+ - Updated dependencies [f00d8d4]
1436
+ - Updated dependencies [5326b36]
1437
+ - Updated dependencies [aa4b90d]
1438
+ - Updated dependencies [ccd9397]
1439
+ - Updated dependencies [503be86]
1440
+ - Updated dependencies [647ec8b]
1441
+ - Updated dependencies [54299ca]
1442
+ - Updated dependencies [3264516]
1443
+ - Updated dependencies [ae490ef]
1444
+ - Updated dependencies [e124711]
1445
+ - Updated dependencies [dc61def]
1446
+ - Updated dependencies [bca935b]
1447
+ - Updated dependencies [d92c72d]
1448
+ - Updated dependencies [c54c822]
1449
+ - Updated dependencies [8dcc0f5]
1450
+ - Updated dependencies [75b9e51]
1451
+ - Updated dependencies [f61c8cf]
1452
+ - Updated dependencies [e3ef52b]
1453
+ - Updated dependencies [0a2f233]
1454
+ - Updated dependencies [8621cdd]
1455
+ - Updated dependencies [251e888]
1456
+ - Updated dependencies [07f1822]
1457
+ - Updated dependencies [e336549]
1458
+ - Updated dependencies [3bb9340]
1459
+ - Updated dependencies [1e604c4]
1460
+ - Updated dependencies [04fab5e]
1461
+ - Updated dependencies [183b4c4]
1462
+ - Updated dependencies [7f713b6]
1463
+ - Updated dependencies [d40f43a]
1464
+ - Updated dependencies [2fdb36e]
1465
+ - Updated dependencies [62159bd]
1466
+ - Updated dependencies [193cd5c]
1467
+ - Updated dependencies [f1544e2]
1468
+ - Updated dependencies [7457a09]
1469
+ - Updated dependencies [5aae790]
1470
+ - Updated dependencies [07f1822]
1471
+ - Updated dependencies [d48aad5]
1472
+ - Updated dependencies [5f0852f]
1473
+ - Updated dependencies [c53aa53]
1474
+ - Updated dependencies [6f23667]
1475
+ - Updated dependencies [cde1975]
1476
+ - Updated dependencies [0bc685a]
1477
+ - Updated dependencies [20526f5]
1478
+ - Updated dependencies [efedd28]
1479
+ - Updated dependencies [5d21a48]
1480
+ - Updated dependencies [5278e11]
1481
+ - Updated dependencies [c5eef1d]
1482
+ - Updated dependencies [e5e7ee0]
1483
+ - Updated dependencies [23dba62]
1484
+ - Updated dependencies [e0f300b]
1485
+ - Updated dependencies [761a0ba]
1486
+ - Updated dependencies [c960170]
1487
+ - Updated dependencies [19365b7]
1488
+ - Updated dependencies [ba98e26]
1489
+ - Updated dependencies [b7ed26d]
1490
+ - Updated dependencies [a2ebea2]
1491
+ - Updated dependencies [800bdb0]
1492
+ - Updated dependencies [9d4dfc4]
1493
+ - Updated dependencies [1059965]
1494
+ - Updated dependencies [def5919]
1495
+ - Updated dependencies [ee264b2]
1496
+ - Updated dependencies [60b672e]
1497
+ - Updated dependencies [6b441a8]
1498
+ - Updated dependencies [ce0cfe9]
1499
+ - Updated dependencies [04f1182]
1500
+ - Updated dependencies [be87153]
1501
+ - Updated dependencies [dd0f681]
1502
+ - Updated dependencies [60f0dd8]
1503
+ - Updated dependencies [a87c5cd]
1504
+ - Updated dependencies [a47f338]
1505
+ - Updated dependencies [b3a3d83]
1506
+ - Updated dependencies [7a55913]
1507
+ - Updated dependencies [35accbf]
1508
+ - Updated dependencies [6038de7]
1509
+ - Updated dependencies [fc5f536]
1510
+ - Updated dependencies [bee5ffe]
1511
+ - Updated dependencies [5647006]
1512
+ - Updated dependencies [e654bfd]
1513
+ - Updated dependencies [01a7337]
1514
+ - Updated dependencies [b45c71e]
1515
+ - Updated dependencies [d71ff32]
1516
+ - Updated dependencies [3172831]
1517
+ - Updated dependencies [f8cfbb4]
1518
+ - Updated dependencies [939f579]
1519
+ - Updated dependencies [6e6c872]
1520
+ - Updated dependencies [2598216]
1521
+ - Updated dependencies [11949fc]
1522
+ - Updated dependencies [2c7e62d]
1523
+ - Updated dependencies [eb95d97]
1524
+ - Updated dependencies [b098b0e]
1525
+ - Updated dependencies [4d00b13]
1526
+ - Updated dependencies [1363084]
1527
+ - Updated dependencies [fa5758e]
1528
+ - Updated dependencies [38f7e4f]
1529
+ - Updated dependencies [eb7613c]
1530
+ - Updated dependencies [c57f3cf]
1531
+ - Updated dependencies [ecc9110]
1532
+ - Updated dependencies [e4c2dc8]
1533
+ - Updated dependencies [97faca3]
1534
+ - Updated dependencies [57bab76]
1535
+ - Updated dependencies [c89d18c]
1536
+ - Updated dependencies [1bd2795]
1537
+ - Updated dependencies [f7bd4e2]
1538
+ - Updated dependencies [694c350]
1539
+ - Updated dependencies [361bd5b]
1540
+ - Updated dependencies [acf34e3]
1541
+ - Updated dependencies [aac90a5]
1542
+ - Updated dependencies [3da3da5]
1543
+ - Updated dependencies [1e6ab15]
1544
+ - Updated dependencies [b90086a]
1545
+ - Updated dependencies [8186a70]
1546
+ - Updated dependencies [a329cca]
1547
+ - Updated dependencies [c87ef70]
1548
+ - Updated dependencies [3cb0618]
1549
+ - Updated dependencies [32a0874]
1550
+ - Updated dependencies [6eec18c]
1551
+ - Updated dependencies [4d7bebf]
1552
+ - Updated dependencies [821ac7a]
1553
+ - Updated dependencies [8f81731]
1554
+ - Updated dependencies [7055c22]
1555
+ - Updated dependencies [785a748]
1556
+ - Updated dependencies [3af0354]
1557
+ - Updated dependencies [866ff16]
1558
+ - Updated dependencies [5a85e67]
1559
+ - Updated dependencies [8b50cb3]
1560
+ - Updated dependencies [a0fdc56]
1561
+ - Updated dependencies [b95577a]
1562
+ - Updated dependencies [0dcbc11]
1563
+ - Updated dependencies [2342ee4]
1564
+ - Updated dependencies [d88f3e9]
1565
+ - Updated dependencies [ad5fe25]
1566
+ - Updated dependencies [c183a12]
1567
+ - Updated dependencies [83c161f]
1568
+ - Updated dependencies [d8c4957]
1569
+ - Updated dependencies [b9f930b]
1570
+ - Updated dependencies [f24cb83]
1571
+ - Updated dependencies [5dbbb92]
1572
+ - Updated dependencies [ea90179]
1573
+ - Updated dependencies [1818998]
1574
+ - Updated dependencies [ce92674]
1575
+ - Updated dependencies [5ef0b5b]
1576
+ - Updated dependencies [8c2db68]
1577
+ - Updated dependencies [22b5e54]
1578
+ - Updated dependencies [0166bd5]
1579
+ - Updated dependencies [8064b07]
1580
+ - Updated dependencies [09ee21c]
1581
+ - Updated dependencies [4a56dbd]
1582
+ - Updated dependencies [289d04a]
1583
+ - Updated dependencies [f549a0d]
1584
+ - Updated dependencies [48fbacb]
1585
+ - Updated dependencies [06df4fa]
1586
+ - Updated dependencies [3fc2e48]
1587
+ - Updated dependencies [c9b809f]
1588
+ - Updated dependencies [e8f435c]
1589
+ - Updated dependencies [32386f8]
1590
+ - Updated dependencies [9b702dc]
1591
+ - Updated dependencies [ab16331]
1592
+ - Updated dependencies [41610f6]
1593
+ - Updated dependencies [69f1dfd]
1594
+ - Updated dependencies [bbe05de]
1595
+ - Updated dependencies [355e951]
1596
+ - Updated dependencies [a1dd1e4]
1597
+ - Updated dependencies [a5dcb74]
1598
+ - Updated dependencies [dadb43f]
1599
+ - Updated dependencies [3556b67]
1600
+ - @objectstack/spec@17.0.0
1601
+ - @objectstack/core@17.0.0
1602
+ - @objectstack/driver-sql@17.0.0
1603
+
3
1604
  ## 17.0.0-rc.6
4
1605
 
5
1606
  ### Major Changes
@@ -194,7 +1795,7 @@
194
1795
  `lower()` folds ASCII only, which IS the contract (#4706 Q1 = A): `$icontains:
195
1796
  'café'` does not match `CAFÉ`.
196
1797
 
197
- <!-- adr-0087: registered filter-regex-options-retired -->
1798
+ <!-- adr-0087: registered filter-regex-options-retired -->
198
1799
 
199
1800
  `driver-mongodb`'s unknown-operator arm was throwing a bare `Error` with no
200
1801
  `code` and no `status`, three lines from the helper in its own file that sets