@oimdb/core 3.12.0 → 3.12.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.
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/llms.txt +3 -3
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1810,7 +1810,12 @@ type TOIMDerivedCollectionOptions<TDerived extends object, TPk extends TOIMKey>
|
|
|
1810
1810
|
* listens to the source and keeps itself in sync — add / update / remove.
|
|
1811
1811
|
*
|
|
1812
1812
|
* It is a first-class `OIMReactiveCollection`, so it can be indexed, selected,
|
|
1813
|
-
* and derived-from again
|
|
1813
|
+
* and derived-from again — a CHAIN of one-to-one projections, not a join. There
|
|
1814
|
+
* is deliberately no multi-source variant: a joined row would need a primary key
|
|
1815
|
+
* nobody references, and an identity nobody uses is not an identity. Combine
|
|
1816
|
+
* across collections with one `OIMComputed`, or, if something really does
|
|
1817
|
+
* reference the combined thing, give it its own collection and write to it.
|
|
1818
|
+
* Maintenance runs on the source's
|
|
1814
1819
|
* synchronous UPDATE channel, so a chain of derivations stays consistent within
|
|
1815
1820
|
* the same flush; delivery to keyed subscribers is batched on flush as usual.
|
|
1816
1821
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1810,7 +1810,12 @@ type TOIMDerivedCollectionOptions<TDerived extends object, TPk extends TOIMKey>
|
|
|
1810
1810
|
* listens to the source and keeps itself in sync — add / update / remove.
|
|
1811
1811
|
*
|
|
1812
1812
|
* It is a first-class `OIMReactiveCollection`, so it can be indexed, selected,
|
|
1813
|
-
* and derived-from again
|
|
1813
|
+
* and derived-from again — a CHAIN of one-to-one projections, not a join. There
|
|
1814
|
+
* is deliberately no multi-source variant: a joined row would need a primary key
|
|
1815
|
+
* nobody references, and an identity nobody uses is not an identity. Combine
|
|
1816
|
+
* across collections with one `OIMComputed`, or, if something really does
|
|
1817
|
+
* reference the combined thing, give it its own collection and write to it.
|
|
1818
|
+
* Maintenance runs on the source's
|
|
1814
1819
|
* synchronous UPDATE channel, so a chain of derivations stays consistent within
|
|
1815
1820
|
* the same flush; delivery to keyed subscribers is batched on flush as usual.
|
|
1816
1821
|
*
|
package/llms.txt
CHANGED
|
@@ -203,7 +203,7 @@ rows.subscribeOnKey('team1', () => { for (const row of rows.getByKey('team1')) {
|
|
|
203
203
|
|
|
204
204
|
### Derived collections
|
|
205
205
|
|
|
206
|
-
`OIMDerivedCollection(queue, source, derive, opts?)` is a reactive collection whose entities are derived **one-to-one** from a source collection — `derived[pk] = derive(source[pk], pk)`, keyed by the SOURCE pk — kept in sync (add / update / remove) automatically. It is itself a first-class `OIMReactiveCollection`, so it can be indexed, selected, and derived-from again
|
|
206
|
+
`OIMDerivedCollection(queue, source, derive, opts?)` is a reactive collection whose entities are derived **one-to-one** from a source collection — `derived[pk] = derive(source[pk], pk)`, keyed by the SOURCE pk — kept in sync (add / update / remove) automatically. It is itself a first-class `OIMReactiveCollection`, so it can be indexed, selected, and derived-from again — a CHAIN of one-to-one projections, NOT a join. There is deliberately no multi-source derived collection: a joined row would need a pk nobody references, and an identity nobody uses is not an identity; combine across collections with one `OIMComputed`, or give the combined thing its own collection if something genuinely references it; maintenance runs on the source's **synchronous** update channel, so a chain of derivations stays consistent within a flush (delivery to keyed subscribers is batched on flush as usual). It parallels `OIMDerivedCollectionIndex*` and needs no compute runtime.
|
|
207
207
|
|
|
208
208
|
```ts
|
|
209
209
|
const nameView = new OIMDerivedCollection(queue, users,
|
|
@@ -437,12 +437,12 @@ db.users.byTeamOrdered.list('t1', render); // → bindableLists.children (
|
|
|
437
437
|
| `exoSelector(selector)` / `exoComputed(computed)` | an `OIMSelector` / `OIMComputed` → a bindable |
|
|
438
438
|
| `exoKeyed(keyBindable, makeSelector)` | a MOVING key: repoints the selector as the key changes |
|
|
439
439
|
| `exoCombine(sources, fn)` | multi-source derive (Exodra's own `derive` is single-source) |
|
|
440
|
-
| `
|
|
440
|
+
| `exoRows(keys, render)` | identity-stable children for any items |
|
|
441
441
|
| `exoList(stream, key, render)` | OIMDB ordered command stream → Exodra `bindableList` (commands map 1:1 to `TExoListOp`) |
|
|
442
442
|
|
|
443
443
|
Each named index becomes a CALLABLE member of that name carrying `pks`, `rows`, `subscribe` (and `list` on ordered ones), so the caller never picks a method to match the index kind and never passes the index object again. Any key accepts `TKey | TOIMExodraReadable<TKey>`. Dispatch is structural: array-based `getPksByKey` returns `TPk[]`, set-based returns `Set<TPk>`, keyless ones have none; composite indexes are the same classes with an array key, resolved per call via `Array.isArray(key)`. **`list` exists only on array-based indexes** — a stream is derived by diffing an order and a set has none; enforced in the types, and it builds/memoizes the stream itself. `indexes` is a second argument because a kit does not own its indexes (`kit.indexFactory` creates them, the app keeps them); if core ever lets a kit hold named indexes this becomes optional without a break.
|
|
444
444
|
|
|
445
|
-
**No function in this package takes an equality option, and there is no options type.** Equality belongs to whoever owns the value: `OIMSelector.areEqual` for selectors (override it), `OIMComputed`'s `compare` for computeds (pass at construction), your own `fn` for `exoCombine` (return a stable reference when content is unchanged), your own `subscribe` callback for `exoSource(read, subscribe)` (compare there and don't call `onChange` — that callback IS the extension point). Those owners run BELOW the bridge, so a filter here could only reject what they passed, never resurrect what they dropped; in-place entity updaters need no configuration anywhere. `exoCombine` drops recomputes landing on the same value, collapsing the N notifications N sources produce for one logical change — not glitch-free though, so put real fan-in in ONE `OIMComputed` forwarded via `exoComputed`. `
|
|
445
|
+
**No function in this package takes an equality option, and there is no options type.** Equality belongs to whoever owns the value: `OIMSelector.areEqual` for selectors (override it), `OIMComputed`'s `compare` for computeds (pass at construction), your own `fn` for `exoCombine` (return a stable reference when content is unchanged), your own `subscribe` callback for `exoSource(read, subscribe)` (compare there and don't call `onChange` — that callback IS the extension point). Those owners run BELOW the bridge, so a filter here could only reject what they passed, never resurrect what they dropped; in-place entity updaters need no configuration anywhere. `exoCombine` drops recomputes landing on the same value, collapsing the N notifications N sources produce for one logical change — not glitch-free though, so put real fan-in in ONE `OIMComputed` forwarded via `exoComputed`. `exoRows` reads are idempotent (same array reference back, no render, no eviction, until the key sequence changes) and require UNIQUE keys — a duplicate throws rather than placing one schema object twice. `exoList` memoizes `render` per item, so repeated `snapshot()` calls return the same row objects (identity is what Exodra reconciles on); the memo is pruned against the live order on snapshot and amortized past 2x the live row count. Reads are length-aligned WITH HOLES by default (`readonly (T | undefined)[]`, matching `@oimdb/react`) — a missing entity is a real state of the store and hiding it is not the bridge's call. Dropping holes is explicit and never a flag: `<index>.compact(key)` filters (may be SHORTER — on MANUAL `setPks` pks that silently swallows a torn state), `<index>.unsafeDense(key)` filters and checks NOTHING and just retypes to `readonly T[]` — correct for a DERIVED index, which is dense by construction, and `unsafe` because the guarantee is the caller's. Pure aliases of a single core call (`getOneByPk`, `subscribeOnKey`, `getPksByKey`) are NOT exported — call core directly. Pair `exoDb` with Exodra's own DI: `createContextKey<typeof db>('db')`, created by the app since this package pulls types only. Exodra bucket bindings subscribe at *build* (not `onExoMount`); `<index>.subscribe(key, onChange)` covers `onExoMount`-scoped needs. No function takes more than one shape of argument: a fixed key and a moving key are different members (`<index>(key)` vs `<index>.for(keyBindable)`), never one member that inspects what it was given. `exoCombine`'s FIRST emit after subscribing always propagates — OIMDB sources fire synchronously on subscribe and a binding is built from `getValue()` then wired a moment later, so suppressing it leaves the region frozen at its build-time value. Sorting and filtering belong in the index (`derivedArrayIndex({ orderBy })`, or emit no key for excluded entities), not in `rows` — there they are maintained incrementally instead of recomputed per read. The PRIMITIVES are the load-bearing API: screens that sort/filter beyond an index, key rows off related data, or fan in across collections are built from `exoRows` over an `OIMComputed` forwarded by `exoComputed`, and that is the intended path, not a fallback. Patterns: ordered column → order in the index (`derivedArrayIndex({ orderBy })`), never a per-render sort; filtered list → emit no key for excluded entities; a row that follows another collection → bind INSIDE the row (`derive(exoSelector(other.select.byPk(id)), …)`) so only that node updates; a key is IDENTITY, never invalidation — `key: t => t.id` and nothing else; a composite key re-derives the whole key sequence per read and rebuilds rows whose markup did not change (2000 rows, one added comment: 2.44ms + 1 wasted rebuild vs 0.73ms + zero when the count is bound inside the row), so bind what varies WITHIN a row via `derive(db.comments.byTask.pks(pk), c => c.length)`; `<select>` options → `all()`, never a one-off `getAll()` at build; aggregate → ONE `OIMComputed` + `exoComputed`; moving selection → `<index>.for(keyBindable)`; `onExoMount` scope → `<index>.subscribe(key, onChange)`. Write-side is intentionally omitted (apps write via orchestration, e.g. cnstra, not from views).
|
|
446
446
|
|
|
447
447
|
|
|
448
448
|
## Persist (`@oimdb/persist`)
|
package/package.json
CHANGED