|
| 1 | +--- |
| 2 | +"@objectstack/spec": major |
| 3 | +"@objectstack/driver-memory": major |
| 4 | +"@objectstack/driver-mongodb": major |
| 5 | +"@objectstack/driver-sql": major |
| 6 | +--- |
| 7 | + |
| 8 | +refactor(spec)!: retire the 31 inert `DriverCapabilities` bits — declared by every driver, read by nothing (#4634, ADR-0049) |
| 9 | + |
| 10 | +The #4484 findStream close-out left one loose end: `DriverCapabilities.streaming` |
| 11 | +described a contract method that no longer exists — and a full liveness audit of |
| 12 | +the record (#4634, across objectstack + cloud, objectui confirmed clean) found |
| 13 | +`streaming` was not the exception but the rule. Of 34 declared bits, **three** |
| 14 | +have a decision-making reader and **thirty-one** were written by every driver |
| 15 | +and consulted by no engine, planner, REST layer or renderer: |
| 16 | + |
| 17 | +- Their `.describe()` strings promised engine adaptation that was never built |
| 18 | + ("If false, ObjectQL will fetch all records and filter in memory" — no such |
| 19 | + fallback ever keyed off the bit). |
| 20 | +- Zero readers let values go WRONG unnoticed: `SqlDriver` declared |
| 21 | + `streaming: false` while implementing `findStream`; `InMemoryDriver` declared |
| 22 | + `streaming: true` over a full-table read — the exact inverse of the guarantee. |
| 23 | +- The real mechanism everywhere else is **method presence**: transactions gate |
| 24 | + on `driver.beginTransaction`, aggregate pushdown on |
| 25 | + `typeof driver.aggregate === 'function'`, schema sync on |
| 26 | + `typeof driver.syncSchema === 'function'`, and the REQUIRED CRUD/bulk methods |
| 27 | + are called unconditionally. |
| 28 | + |
| 29 | +Survivors (each with a named reader — the bits method presence cannot carry): |
| 30 | + |
| 31 | +| bit | reader | |
| 32 | +|---|---| |
| 33 | +| `queryDateGranularity` | engine aggregate dispatch (`engine.ts`), `checkDateBucketParity` (`@objectstack/verify`) | |
| 34 | +| `autonumber` | engine defers autonumber generation to the driver (`engine.ts`) | |
| 35 | +| `batchSchemaSync` | engine ANDs it with `syncSchemasBatch` presence (`engine.ts` / `plugin.ts`) | |
| 36 | + |
| 37 | +Migration (FROM → TO): |
| 38 | + |
| 39 | +- Any of the 31 bits (`create`/`read`/`update`/`delete`, `bulkCreate`/ |
| 40 | + `bulkUpdate`/`bulkDelete`, `transactions`/`savepoints`/`isolationLevels`, |
| 41 | + `queryFilters`/`queryAggregations`/`querySorting`/`queryPagination`/ |
| 42 | + `queryWindowFunctions`/`querySubqueries`/`queryCTE`/`joins`, |
| 43 | + `fullTextSearch`/`jsonQuery`/`geospatialQuery`/`streaming`/`jsonFields`/ |
| 44 | + `arrayFields`/`vectorSearch`, `schemaSync`/`migrations`/`indexes`, |
| 45 | + `connectionPooling`/`preparedStatements`/`queryCache`) in a `supports` |
| 46 | + literal or a `DriverConfig.capabilities` object → **delete the key**. Each is |
| 47 | + tombstoned (`retiredKey()`), not silently stripped: authoring one is a `tsc` |
| 48 | + error against `IDataDriver.supports` and a parse error carrying the per-key |
| 49 | + prescription, which names the mechanism that actually decides the behaviour. |
| 50 | +- `batchSchemaSync` dropped its `.default(false)` for `.optional()` — absence |
| 51 | + already meant `false` at both readers, so `supports: {}` is now a valid, |
| 52 | + minimal advertisement. If you read `capabilities.batchSchemaSync` from a |
| 53 | + *parsed* config and relied on the materialised `false`, treat absence as |
| 54 | + `false` (both engine readers always did). |
| 55 | +- Driver packages: `InMemoryDriver.supports` is now `{}`, |
| 56 | + `MongoDBDriver.supports` is `{ batchSchemaSync: true }`, `SqlDriver.supports` |
| 57 | + is `{ queryDateGranularity, autonumber: true, batchSchemaSync: false }`. |
| 58 | + Reading a removed bit off these literals no longer type-checks — and no code |
| 59 | + in any repository did. |
| 60 | +- A future capability (streaming reads, vector search, …) returns **with its |
| 61 | + caller and its reader in the same change** — the enforce route of ADR-0049 — |
| 62 | + never as a dangling boolean. |
| 63 | + |
| 64 | +The retirement kit: 31 `retiredKey()` tombstones on the non-strict schema |
| 65 | +(parse + `tsc` both audible; the schema IS parsed via |
| 66 | +`DriverConfigSchema.capabilities` and its SQL/NoSQL extensions); ADR-0087 D3 |
| 67 | +semantic migration `driver-capabilities-inert-bits-removed` (a driver is CODE, |
| 68 | +never stack metadata — `supports` lives in driver classes and `DriverConfig` |
| 69 | +is plugin TS configuration, so there is no stored row or stack source for a D2 |
| 70 | +conversion to rewrite; the stack-tree neighbour `datasource.capabilities` was |
| 71 | +retired separately in #4583); baselines (`authorable-surface.json` [RETIRED] |
| 72 | +lines, `json-schema.manifest.json`) regenerated deliberately; compiler-API pin |
| 73 | +asserting every retired bit is unwritable (`undefined`) and every live bit is |
| 74 | +not, sabotage-verified both ways (S1 schema resurrection, S2 driver literal |
| 75 | +resurrection). |
| 76 | + |
| 77 | +No runtime behaviour changes — that impossibility is the point: every removed |
| 78 | +bit had zero readers, and the three live bits keep theirs. |
0 commit comments