@oimdb/react 3.0.0 → 3.2.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.
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install @oimdb/react @oimdb/core
11
11
 
12
12
  ## Why one binding
13
13
 
14
- Subscribing in this engine is one call: `OIMEffectService.INSTANCE.subscribeEffect(db, sourceSlots, sourceComputeds, body)`.
14
+ Subscribing in this engine is one call: `OIMEffectService.INSTANCE.subscribeEffect(db, body, sourceSlots?, sourceComputeds?, sourceTrackingComputeds?)`.
15
15
  It carries no value; what changed is read off the slots afterwards. So a component needs
16
16
  exactly three things: the roots a write can reach, the derivations to ask before reading,
17
17
  and the derive function. Together they make up `TOIMBinding`, and `useOIMValue` is the hook that applies it through
@@ -35,18 +35,18 @@ instance, a store slot, and every store door of the service takes that store slo
35
35
  Whatever a read mints is swept at the end of the frame unless something took a real reference to it.
36
36
 
37
37
  ```ts setup
38
- import { OIMDB, createOIMBucketService, createOIMEntityService, type TOIMEntitySlot } from '@oimdb/core';
38
+ import { OIMDB, type TOIMEntitySlot, createOIMSlotCollectionService, createOIMSlotContentEntityService, OIMSlotContentPositionalArrayBucketService, OIMBucketCommands, createOIMSingleComputedService, OIMSingleSlotStoreService, createOIMSingleSlotService, createOIMComputedService, oimSameValueZeroGuard, type TOIMComputedSlot } from '@oimdb/core';
39
39
 
40
40
  type TCard = { id: string; deckId: string; title: string; rank: number };
41
41
 
42
42
  export const db = new OIMDB();
43
43
 
44
- export const cardService = createOIMEntityService<TCard>(db, { keyOf: (card) => card.id });
44
+ export const cardService = createOIMSlotCollectionService({ slotContentService: createOIMSlotContentEntityService<TCard>() });
45
45
  export const cards = cardService.createStoreSlot(db);
46
46
 
47
47
  // A bucket keeps the order the caller writes; a sorted list is
48
- // `createOIMComputedServiceSorted` over an index.
49
- export const byDeckService = createOIMBucketService<TOIMEntitySlot<TCard>>(db);
48
+ // `createOIMSortedComputedService` over an index.
49
+ export const byDeckService = createOIMSlotCollectionService({ slotContentService: new OIMSlotContentPositionalArrayBucketService<TOIMEntitySlot<TCard>>({ bucketCommands: new OIMBucketCommands({ maxCommandsPerSlot: undefined }) }) });
50
50
  export const byDeck = byDeckService.createStoreSlot(db);
51
51
 
52
52
  // A BUCKET'S SLOTS ARE YOURS. A write moves the card's value and files nothing, so the
@@ -54,9 +54,9 @@ export const byDeck = byDeckService.createStoreSlot(db);
54
54
  // bucket by key: it mints one if there is none and hands back the bucket itself,
55
55
  // counted. `owner.unholdSlot` gives that count back, once the bucket's own slots keep it.
56
56
  export function setDeck(deckId: string, cardsInDeck: readonly TCard[]): void {
57
- cardService.slotContentService.upsertEntities(cards, cardsInDeck);
57
+ cardService.writeInBatch(cards, () => (cardsInDeck).map(entity => cardService.writeSlotContentByKey(cards, entity.id, entity)));
58
58
  const bucket = byDeckService.holdSlotByKey(byDeck, deckId);
59
- byDeckService.slotContentService.writeBucketMemberSlots(
59
+ byDeckService.slotContentService.writeSlotContent(
60
60
  bucket, cardsInDeck.map((card) => cardService.getSlotByKey(cards, card.id)),
61
61
  );
62
62
  bucket.owner.unholdSlot(bucket);
@@ -77,7 +77,7 @@ function App() {
77
77
 
78
78
  // One entity, by key.
79
79
  function Card({ pk }: { pk: string }) {
80
- const card = useOIMEntity(cardService, cards, pk);
80
+ const card = useOIMEntity(cards, pk);
81
81
  return <span>{card?.title}</span>;
82
82
  }
83
83
 
@@ -85,7 +85,7 @@ function Card({ pk }: { pk: string }) {
85
85
  // mounted, and gives the hold back when it unmounts or when `deckId` moves —
86
86
  // there is no release to remember here.
87
87
  function DeckList({ deckId }: { deckId: string }) {
88
- const deckCards = useOIMBucketSlotsByKey(byDeckService, byDeck, deckId);
88
+ const deckCards = useOIMBucketSlotsByKey(byDeck, deckId);
89
89
  return <ul>{deckCards.map((s) => <li key={String(s.key)}>{s.entity?.title}</li>)}</ul>;
90
90
  }
91
91
  ```
@@ -101,8 +101,9 @@ throw when it does not.
101
101
 
102
102
  ## The hooks
103
103
 
104
- Every hook that reads a collection takes the collection service first and the store slot second.
105
- The parameter is typed as a `Pick` of `OIMSlotCollectionService` with only the members the hook calls.
104
+ Every hook that reads a collection by key takes the store slot alone and calls the service it carries,
105
+ `storeSlot.slotService`, typed as a `Pick` of `OIMSlotCollectionService` with only the members the hook calls.
106
+ A store aggregate passes its `storeSlot`.
106
107
 
107
108
  - **`useOIMValue(binding)`**: the binding. It subscribes one effect over the binding's roots and
108
109
  derivations (the effect holds each of them for as long as it lives), settles the derivations before
@@ -112,7 +113,7 @@ The parameter is typed as a `Pick` of `OIMSlotCollectionService` with only the m
112
113
  - **`useOIMComputedValue(computedSlot)`**: the value of ONE computed slot, re-rendering
113
114
  when it changes. It is `useOIMValue` over `sourceComputeds: [computedSlot]`, with the
114
115
  value read off `computedSlot.value` after `useOIMValue` settles it.
115
- - **`useOIMEntity(entityCollectionService, entityStoreSlot, key)`**: one entity, by key. The key is resolved
116
+ - **`useOIMEntity(entityStoreSlot, key)`**: one entity, by key. The key is resolved
116
117
  to an identity ON COMMIT, not on render, through `holdSlotByKey(entityStoreSlot, key)`: a render is not a
117
118
  lifetime, and an identity a discarded render minted would have no hold to give back. The render path reads
118
119
  `getSlotByKey(entityStoreSlot, key)`, so the first paint shows current state. That read mints when the key
@@ -127,45 +128,40 @@ The parameter is typed as a `Pick` of `OIMSlotCollectionService` with only the m
127
128
  bucket's order, read through `bucketCollectionService.slotContentService.getBucketMemberSlots`. The array is
128
129
  copied only when the bucket slot's change stamp (`changedAtRevision`, `changedAtEpoch`) moved. That stamp is
129
130
  reliable here because the caller holds the bucket: a write to a slot nothing holds is not stamped.
130
- - **`useOIMBucketSlotsByKey(bucketCollectionService, bucketStoreSlot, key)`**: one bucket by key, as an array of
131
+ - **`useOIMBucketSlotsByKey(bucketStoreSlot, key)`**: one bucket by key, as an array of
131
132
  entity slots. It resolves the bucket on commit, holds it for as long as the component is mounted, and gives the
132
133
  hold back on unmount or when the key changes. It copies the members on every render: the bucket it reads during a
133
134
  render may be held by nothing yet, and an unheld bucket's stamp does not move. **The key may be built inline**,
134
- including the array that keys an `OIMSlotStoreServiceTreeDriven`; the hook settles it by asking the store which
135
+ including the array that keys an `OIMSlotTreeStoreService`; the hook settles it by asking the store which
135
136
  bucket each key names.
136
- - **`useOIMSlotsByKeys(slotCollectionService, storeSlot, keys)`**: several identities of one store slot,
137
+ - **`useOIMSlotsByKeys(storeSlot, keys)`**: several identities of one store slot,
137
138
  resolved by key and held for as long as the component is mounted. It returns the slots and takes no subscription,
138
139
  so use it when a component reads **derivations**: a computed slot exists only while something holds it, and a bare
139
140
  read mints one that the next `flush()` takes straight back. See [Holds per component](#holds-per-component).
140
- - **`useOIMComputedValueByKey(computedCollectionService, computedStoreSlot, key)`**: the value of one key of a
141
- computed store slot. Pass `computedService.computedCollectionService`. It has the same body as `useOIMEntity`:
141
+ - **`useOIMComputedValueByKey(computedStoreSlot, key)`**: the value of one key of a
142
+ computed store slot. It has the same body as `useOIMEntity`:
142
143
  it resolves with `holdSlotByKey` on commit, subscribes over that computed slot, gives the resolve-hold straight
143
144
  back, and reads the value through `computedSlot.computedValueService.getValueForSlot`.
144
145
  - **`useOIMComputedCollection(computedService, derive, createSources, deps)`**: a keyed computed store slot the
145
146
  component owns. The SERVICE is yours, built once at module scope with `createOIMComputedService(options)`; the hook
146
147
  only calls `computedService.createComputedStoreSlot(db, derive, createSources)` in a `useMemo` keyed on `deps`, and
147
148
  returns that store slot. `derive` and `createSources` may be `undefined` when the service already has them.
148
- Read it by key with `useOIMComputedValueByKey(computedService.computedCollectionService, computedStoreSlot, key)`.
149
- - **`useOIMSingleComputedValue(computedService, derive, createSources, deps)`**: a single computed the component
150
- owns, read under the key `undefined`. The service is a void-keyed `createOIMComputedService` with
151
- `computedSlotStoreService: OIMSlotStoreServiceSingleSlotDriven.INSTANCE`, built once at module scope. Per mount
152
- the hook builds a store slot through `useOIMComputedCollection` and reads it through `useOIMComputedValueByKey`,
153
- so a flush between render and subscribe, which releases the slot the render minted, is safe.
154
- - **`useOIMSingleEntity(singleSlotService, singleSlotStoreSlot)`** and
155
- **`useOIMSingleBucketSlots(singleSlotService, singleSlotStoreSlot)`**: the single entity and the single bucket.
149
+ Read it by key with `useOIMComputedValueByKey(computedStoreSlot, key)`.
150
+ - **`useOIMSingleComputedValue(computedService, derive, createSources, deps)`**: a single computed the
151
+ component owns. The service is `createOIMSingleComputedService<V>()`, built once at module scope; `derive` and
152
+ `createSources` receive the computed slot. Per mount the hook builds one store slot and reads its slot through
153
+ `computedSingleSlotService`, so a flush between render and subscribe, which releases the slot the render minted,
154
+ is safe.
155
+ - **`useOIMSingleEntity(singleSlotStoreSlot)`** and
156
+ **`useOIMSingleBucketSlots(singleSlotStoreSlot)`**: the single entity and the single bucket.
156
157
  See [Singles](#singles).
157
158
  - **`useOIMDB()`**: the `OIMDB` from `OIMDBContext`. It throws if used outside `<OIMDBProvider>`.
158
159
 
159
160
  ```tsx
160
- import { OIMSlotStoreServiceSingleSlotDriven, createOIMComputedService } from '@oimdb/core';
161
- import type { TOIMComputedSlot, TOIMSingleSlotStore } from '@oimdb/core';
162
161
  import { useOIMSingleComputedValue } from '@oimdb/react';
163
162
 
164
163
  // Module scope: one service per kind of computed, shared by every component.
165
- const totalRankService = createOIMComputedService<void, number, TOIMSingleSlotStore<TOIMComputedSlot<number, void>>>({
166
- computedSlotStoreService:
167
- OIMSlotStoreServiceSingleSlotDriven.INSTANCE as OIMSlotStoreServiceSingleSlotDriven<TOIMComputedSlot<number, void>>,
168
- });
164
+ const totalRankService = createOIMSingleComputedService<number>();
169
165
 
170
166
  function TotalRank({ ids }: { ids: readonly string[] }) {
171
167
  // Per mount: one store slot with its own derive and sources.
@@ -181,28 +177,27 @@ function TotalRank({ ids }: { ids: readonly string[] }) {
181
177
 
182
178
  ## Singles
183
179
 
184
- A single is a store slot that holds ONE identity and has no key: `createOIMSingleEntityService(db)` and
185
- `createOIMSingleBucketService(db)` hand back an `OIMSingleSlotService`, read through `getSingleSlot(storeSlot)` and
186
- `holdSingleSlot(storeSlot)`. The two single hooks take that service (typed as a `Pick` of `OIMSingleSlotService`)
187
- and its store slot, and have the by-key hooks' mechanics without the key: the render reads `getSingleSlot`, whose
180
+ A single is a store slot that holds ONE identity and has no key: `createOIMSingleSlotService()` and
181
+ `createOIMSingleSlotService()` hand back an `OIMSingleSlotService`, read through `getSingleSlot(storeSlot)` and
182
+ `holdSingleSlot(storeSlot)`. The two single hooks take the store slot and call its `slotService` (typed as a
183
+ `Pick` of `OIMSingleSlotService`), and have the by-key hooks' mechanics without the key: the render reads `getSingleSlot`, whose
188
184
  mint the next `flush()` takes back if no commit follows; the commit resolves with `holdSingleSlot`, subscribes over
189
185
  that slot and gives the resolve-hold straight back, so a single released while nothing watched it is minted afresh.
190
186
 
191
187
  ```tsx
192
- import { createOIMSingleBucketService, createOIMSingleEntityService } from '@oimdb/core';
193
188
  import { useOIMSingleBucketSlots, useOIMSingleEntity } from '@oimdb/react';
194
189
 
195
190
  type TSettings = { theme: string };
196
191
 
197
- const settingsService = createOIMSingleEntityService<TSettings>(db);
192
+ const settingsService = createOIMSingleSlotService({ slotContentService: createOIMSlotContentEntityService<TSettings>() });
198
193
  const settings = settingsService.createStoreSlot(db);
199
194
 
200
- const pickedService = createOIMSingleBucketService<TOIMEntitySlot<TCard>>(db);
195
+ const pickedService = createOIMSingleSlotService({ slotContentService: new OIMSlotContentPositionalArrayBucketService<TOIMEntitySlot<TCard>>({ bucketCommands: new OIMBucketCommands({ maxCommandsPerSlot: undefined }) }) });
201
196
  const picked = pickedService.createStoreSlot(db);
202
197
 
203
198
  function Toolbar() {
204
- const theme = useOIMSingleEntity(settingsService, settings)?.theme;
205
- const pickedCards = useOIMSingleBucketSlots(pickedService, picked);
199
+ const theme = useOIMSingleEntity(settings)?.theme;
200
+ const pickedCards = useOIMSingleBucketSlots(picked);
206
201
  return <span>{theme}: {pickedCards.length} picked</span>;
207
202
  }
208
203
  ```
@@ -212,7 +207,7 @@ A single computed is `useOIMSingleComputedValue`.
212
207
 
213
208
  ## Binding a derivation
214
209
 
215
- `createOIMComputedService` and `createOIMComputedServiceBucketMemo` both hand back a service of computed slots,
210
+ `createOIMComputedService` and `createOIMBucketMemoComputedService` both hand back a service of computed slots,
216
211
  whose store slot holds them. You can either hold the key where the lifetime is (a module, your own store) and bind
217
212
  it with `useOIMValue`, or let the component own the lifetime with `useOIMSlotsByKeys`.
218
213
 
@@ -222,7 +217,7 @@ asked and never woken. Only a computed slot's `computedValueService.getValueForS
222
217
 
223
218
  **No wake is owed for what happened before the subscription.** An effect is stamped with the channel clock at
224
219
  birth (`ranAtRevision` / `ranAtEpoch`), so a frame runs the body only when a source moved *after* the subscription.
225
- Every hook here subscribes with `subscribeEffect`; `OIMEffectService.INSTANCE.createEffectImmediate` is the door
220
+ Every hook here subscribes with `subscribeEffect`; `OIMEffectService.INSTANCE.createAndRunEffect` is the door
226
221
  that delivers a value at once.
227
222
 
228
223
  ## Holds per component
@@ -232,18 +227,17 @@ resolves each key through `holdSlotByKey(storeSlot, key)` and keeps that hold, a
232
227
  moves) it gives back exactly the holds that commit took.
233
228
 
234
229
  ```tsx
235
- import { createOIMComputedService, oimSameValueZeroGuard } from '@oimdb/core';
236
230
  import { useOIMSlotsByKeys } from '@oimdb/react';
237
231
 
238
232
  const rankService = createOIMComputedService<string, number>({
239
- createSources: (id) => ({ sourceSlots: [cardService.getSlotByKey(cards, id)] }),
233
+ createSources: ({ key: id }) => ({ sourceSlots: [cardService.getSlotByKey(cards, id)] }),
240
234
  derive: (computedSlot) => cardService.getSlotByKey(cards, computedSlot.key).entity?.rank ?? -1,
241
235
  sameValueGuard: oimSameValueZeroGuard,
242
236
  });
243
237
  const ranks = rankService.createComputedStoreSlot(db);
244
238
 
245
239
  function Hand({ ids }: { ids: readonly string[] }) {
246
- const slots = useOIMSlotsByKeys(rankService.computedCollectionService, ranks, ids);
240
+ const slots = useOIMSlotsByKeys(ranks, ids);
247
241
  return <ul>{slots.map((s) => <li key={String(s.key)}>{s.computedValueService.getValueForSlot(s)}</li>)}</ul>;
248
242
  }
249
243
  ```
@@ -298,7 +292,7 @@ identity are one key.
298
292
 
299
293
  ## Things worth knowing
300
294
 
301
- - **The snapshot `useOIMValue` compares is a local counter, not the value.** `createOIMEntityWriterInPlace` keeps
295
+ - **The snapshot `useOIMValue` compares is a local counter, not the value.** `createOIMInPlaceEntityWriter` keeps
302
296
  an entity's object reference stable, a bucket hands its slots back as the same live container, and a column
303
297
  subscription fires only for its own column; a value-comparing binding would fire on none of the three. The counter
304
298
  is bumped ON SUBSCRIBING too, so a write that lands between the render and the subscription is still shown. The
@@ -310,9 +304,9 @@ identity are one key.
310
304
  is one per kind of computed; a component that owns a computed gets only a store slot from
311
305
  `useOIMComputedCollection` / `useOIMSingleComputedValue`.
312
306
  - **Nothing re-files an entity when it is written into a bucket you maintain.** Write the buckets in the same code
313
- that writes the entity, or use an index service (`createOIMIndexService`), which files entities by the key it
307
+ that writes the entity, or use an index service (`createOIMSlotCollectionIndexService`), which files entities by the key it
314
308
  derives from each one.
315
- - **A bucket never sorts.** A list sorted by a column is `createOIMComputedServiceSorted({ indexService, sortColumns })`
309
+ - **A bucket never sorts.** A list sorted by a column is `createOIMSortedComputedService({ indexService, sortColumns })`
316
310
  over an index service, with `createSortedStoreSlot(indexStoreSlot)`.
317
311
 
318
312
  ## Reference
package/dist/index.cjs CHANGED
@@ -71,12 +71,12 @@ var OIMSnapshotVersion = class {
71
71
  this.version = 0;
72
72
  this.getVersion = () => this.version;
73
73
  }
74
- subscribeEffect(channelMap, sourceSlots, sourceComputeds, onStoreChange) {
74
+ subscribeEffect(channelMap, onStoreChange, sourceSlots, sourceComputeds) {
75
75
  this.version++;
76
- return import_core.OIMEffectService.INSTANCE.subscribeEffect(channelMap, sourceSlots, sourceComputeds, () => {
76
+ return import_core.OIMEffectService.INSTANCE.subscribeEffect(channelMap, () => {
77
77
  this.version++;
78
78
  onStoreChange();
79
- });
79
+ }, sourceSlots, sourceComputeds);
80
80
  }
81
81
  };
82
82
 
@@ -96,9 +96,9 @@ function useOIMValue(binding) {
96
96
  snapshotVersion,
97
97
  subscribe: (onStoreChange) => snapshotVersion.subscribeEffect(
98
98
  db,
99
+ onStoreChange,
99
100
  binding.sourceSlots ?? import_core2.OIM_FROZEN_EMPTY_SLOTS,
100
- sourceComputeds,
101
- onStoreChange
101
+ sourceComputeds
102
102
  )
103
103
  };
104
104
  }, [db, binding, sourceComputeds]);
@@ -123,17 +123,28 @@ var import_core3 = require("@oimdb/core");
123
123
 
124
124
  // src/oimAssertOneChannel.ts
125
125
  function oimAssertOneChannel(channelMap, slot, door) {
126
- const storeSlot = slot.storeSlot;
127
- if (storeSlot === void 0 || storeSlot.channelSlot === channelMap.defaultChannelSlot) return;
126
+ if (slot.storeSlot.channelSlot === channelMap.defaultChannelSlot) return;
128
127
  throw new Error(
129
128
  `${door}: the slot was filed on another OIMDB than the one <OIMDBProvider> was given, so a write would never wake this component. Build the services and their store slots on the one OIMDB the provider holds.`
130
129
  );
131
130
  }
132
131
 
132
+ // src/oimSubscribeHeldSlot.ts
133
+ function oimSubscribeHeldSlot(channelMap, hookName, snapshotVersion, holdingSlot, sourcesOfSlot, onStoreChange) {
134
+ try {
135
+ oimAssertOneChannel(channelMap, holdingSlot, hookName);
136
+ const [sourceSlots, sourceComputeds] = sourcesOfSlot(holdingSlot);
137
+ return snapshotVersion.subscribeEffect(channelMap, onStoreChange, sourceSlots, sourceComputeds);
138
+ } finally {
139
+ holdingSlot.owner.unholdSlot(holdingSlot);
140
+ }
141
+ }
142
+
133
143
  // src/useOIMSlotByKey.ts
134
144
  var sourcesOfRootSlot = (rootSlot) => [[rootSlot], import_core3.OIM_FROZEN_EMPTY_SLOTS];
135
- function useOIMSlotByKey(hookName, slotCollectionService, storeSlot, key, sourcesOfSlot = sourcesOfRootSlot) {
145
+ function useOIMSlotByKey(hookName, storeSlot, key, sourcesOfSlot = sourcesOfRootSlot) {
136
146
  const db = useOIMDB();
147
+ const slotCollectionService = storeSlot.slotService;
137
148
  const lastSeenKeyRef = (0, import_react6.useRef)(key);
138
149
  const settledKeyRef = (0, import_react6.useRef)(key);
139
150
  if (key !== lastSeenKeyRef.current) {
@@ -150,18 +161,12 @@ function useOIMSlotByKey(hookName, slotCollectionService, storeSlot, key, source
150
161
  snapshotVersion,
151
162
  subscribe: (onStoreChange) => {
152
163
  const holdingSlot = slotCollectionService.holdSlotByKey(storeSlot, settledKey);
153
- try {
154
- oimAssertOneChannel(db, holdingSlot, hookName);
155
- const [sourceSlots, sourceComputeds] = sourcesOfSlot(holdingSlot);
156
- const disposeEffect = snapshotVersion.subscribeEffect(db, sourceSlots, sourceComputeds, onStoreChange);
157
- subscribedSlot = holdingSlot;
158
- return () => {
159
- subscribedSlot = void 0;
160
- disposeEffect();
161
- };
162
- } finally {
163
- holdingSlot.owner.unholdSlot(holdingSlot);
164
- }
164
+ const effectDisposer = oimSubscribeHeldSlot(db, hookName, snapshotVersion, holdingSlot, sourcesOfSlot, onStoreChange);
165
+ subscribedSlot = holdingSlot;
166
+ return () => {
167
+ subscribedSlot = void 0;
168
+ effectDisposer();
169
+ };
165
170
  },
166
171
  getSlot: () => subscribedSlot ?? slotCollectionService.getSlotByKey(storeSlot, settledKey)
167
172
  };
@@ -171,14 +176,17 @@ function useOIMSlotByKey(hookName, slotCollectionService, storeSlot, key, source
171
176
  }
172
177
 
173
178
  // src/useOIMEntity.ts
174
- function useOIMEntity(entityCollectionService, entityStoreSlot, key) {
175
- return useOIMSlotByKey("useOIMEntity", entityCollectionService, entityStoreSlot, key).entity;
179
+ function useOIMEntity(entityStoreSlot, key) {
180
+ return useOIMSlotByKey("useOIMEntity", entityStoreSlot, key).entity;
176
181
  }
177
182
 
178
183
  // src/useOIMSingleSlot.ts
179
184
  var import_react7 = require("react");
180
- function useOIMSingleSlot(hookName, singleSlotService, singleSlotStoreSlot) {
185
+ var import_core4 = require("@oimdb/core");
186
+ var sourcesOfRootSlot2 = (rootSlot) => [[rootSlot], import_core4.OIM_FROZEN_EMPTY_SLOTS];
187
+ function useOIMSingleSlot(hookName, singleSlotStoreSlot, sourcesOfSlot = sourcesOfRootSlot2) {
181
188
  const db = useOIMDB();
189
+ const singleSlotService = singleSlotStoreSlot.slotService;
182
190
  const store = (0, import_react7.useMemo)(() => {
183
191
  const snapshotVersion = new OIMSnapshotVersion();
184
192
  let subscribedSlot = void 0;
@@ -186,45 +194,40 @@ function useOIMSingleSlot(hookName, singleSlotService, singleSlotStoreSlot) {
186
194
  snapshotVersion,
187
195
  subscribe: (onStoreChange) => {
188
196
  const holdingSlot = singleSlotService.holdSingleSlot(singleSlotStoreSlot);
189
- try {
190
- oimAssertOneChannel(db, holdingSlot, hookName);
191
- const disposeEffect = snapshotVersion.subscribeEffect(db, [holdingSlot], [], onStoreChange);
192
- subscribedSlot = holdingSlot;
193
- return () => {
194
- subscribedSlot = void 0;
195
- disposeEffect();
196
- };
197
- } finally {
198
- holdingSlot.owner.unholdSlot(holdingSlot);
199
- }
197
+ const effectDisposer = oimSubscribeHeldSlot(db, hookName, snapshotVersion, holdingSlot, sourcesOfSlot, onStoreChange);
198
+ subscribedSlot = holdingSlot;
199
+ return () => {
200
+ subscribedSlot = void 0;
201
+ effectDisposer();
202
+ };
200
203
  },
201
204
  getSlot: () => subscribedSlot ?? singleSlotService.getSingleSlot(singleSlotStoreSlot)
202
205
  };
203
- }, [hookName, db, singleSlotService, singleSlotStoreSlot]);
206
+ }, [hookName, db, singleSlotService, singleSlotStoreSlot, sourcesOfSlot]);
204
207
  useOIMSnapshotVersion(store.snapshotVersion, store.subscribe);
205
208
  return store.getSlot();
206
209
  }
207
210
 
208
211
  // src/useOIMSingleEntity.ts
209
- function useOIMSingleEntity(singleSlotService, singleSlotStoreSlot) {
210
- return useOIMSingleSlot("useOIMSingleEntity", singleSlotService, singleSlotStoreSlot).entity;
212
+ function useOIMSingleEntity(singleSlotStoreSlot) {
213
+ return useOIMSingleSlot("useOIMSingleEntity", singleSlotStoreSlot).entity;
211
214
  }
212
215
 
213
216
  // src/useOIMSingleBucketSlots.ts
214
- function useOIMSingleBucketSlots(singleSlotService, singleSlotStoreSlot) {
215
- const bucketSlot = useOIMSingleSlot("useOIMSingleBucketSlots", singleSlotService, singleSlotStoreSlot);
216
- return Array.from(singleSlotService.slotContentService.getBucketMemberSlots(bucketSlot));
217
+ function useOIMSingleBucketSlots(singleSlotStoreSlot) {
218
+ const bucketSlot = useOIMSingleSlot("useOIMSingleBucketSlots", singleSlotStoreSlot);
219
+ return Array.from(singleSlotStoreSlot.slotService.slotContentService.getBucketMemberSlots(bucketSlot));
217
220
  }
218
221
 
219
222
  // src/useOIMField.ts
220
223
  var import_react8 = require("react");
221
224
 
222
225
  // src/holdWhileSubscribed.ts
223
- var import_core4 = require("@oimdb/core");
226
+ var import_core5 = require("@oimdb/core");
224
227
  function holdWhileSubscribed(slots, subscribe) {
225
- (0, import_core4.oimHoldSlots)(slots);
228
+ (0, import_core5.oimHoldSlots)(slots);
226
229
  const giveEveryHoldBack = () => {
227
- (0, import_core4.oimUnholdSlotsIfAlive)(slots);
230
+ (0, import_core5.oimUnholdSlotsIfAlive)(slots);
228
231
  };
229
232
  let disposer;
230
233
  try {
@@ -253,9 +256,8 @@ function useOIMField(entityCollectionService, slot, name) {
253
256
  snapshotVersion,
254
257
  subscribe: (onStoreChange) => holdWhileSubscribed(holds, () => snapshotVersion.subscribeEffect(
255
258
  db,
256
- [entityCollectionService.getColumn(slot, name)],
257
- [],
258
- onStoreChange
259
+ onStoreChange,
260
+ [entityCollectionService.columnSlotService.getColumn(slot, name)]
259
261
  ))
260
262
  };
261
263
  }, [db, entityCollectionService, slot, name]);
@@ -280,32 +282,30 @@ function useOIMBucketSlots(bucketCollectionService, bucketSlot) {
280
282
  }
281
283
 
282
284
  // src/useOIMBucketSlotsByKey.ts
283
- function useOIMBucketSlotsByKey(bucketCollectionService, bucketStoreSlot, key) {
284
- const bucketSlot = useOIMSlotByKey("useOIMBucketSlotsByKey", bucketCollectionService, bucketStoreSlot, key);
285
- return Array.from(bucketCollectionService.slotContentService.getBucketMemberSlots(bucketSlot));
285
+ function useOIMBucketSlotsByKey(bucketStoreSlot, key) {
286
+ const bucketSlot = useOIMSlotByKey("useOIMBucketSlotsByKey", bucketStoreSlot, key);
287
+ return Array.from(bucketStoreSlot.slotService.slotContentService.getBucketMemberSlots(bucketSlot));
286
288
  }
287
289
 
288
290
  // src/useOIMSlotsByKeys.ts
289
291
  var import_react10 = require("react");
290
- var import_core5 = require("@oimdb/core");
291
- function useOIMSlotsByKeys(slotCollectionService, storeSlot, keys) {
292
+ var import_core6 = require("@oimdb/core");
293
+ function useOIMSlotsByKeys(storeSlot, keys) {
292
294
  const settledKeys = (0, import_react10.useRef)(keys);
293
295
  const previousKeys = settledKeys.current;
294
- if (!(0, import_core5.oimSameKeySequence)(previousKeys, keys)) settledKeys.current = keys;
296
+ if (!(0, import_core6.oimSameKeySequence)(previousKeys, keys)) settledKeys.current = keys;
295
297
  const keysToHold = settledKeys.current;
296
298
  const [, resolveAgain] = (0, import_react10.useState)(0);
297
299
  const resolvedSlots = (0, import_react10.useRef)(void 0);
298
300
  const resolvedKeys = (0, import_react10.useRef)(keysToHold);
299
- const resolvedSlotCollectionService = (0, import_react10.useRef)(slotCollectionService);
300
301
  const resolvedStoreSlot = (0, import_react10.useRef)(storeSlot);
301
- if (resolvedSlots.current === void 0 || resolvedKeys.current !== keysToHold || resolvedSlotCollectionService.current !== slotCollectionService || resolvedStoreSlot.current !== storeSlot) {
302
+ if (resolvedSlots.current === void 0 || resolvedKeys.current !== keysToHold || resolvedStoreSlot.current !== storeSlot) {
302
303
  const slots2 = [];
303
304
  for (let i = 0; i < keysToHold.length; i++) {
304
- slots2.push(slotCollectionService.getSlotByKey(storeSlot, keysToHold[i]));
305
+ slots2.push(storeSlot.slotService.getSlotByKey(storeSlot, keysToHold[i]));
305
306
  }
306
307
  resolvedSlots.current = slots2;
307
308
  resolvedKeys.current = keysToHold;
308
- resolvedSlotCollectionService.current = slotCollectionService;
309
309
  resolvedStoreSlot.current = storeSlot;
310
310
  }
311
311
  const slots = resolvedSlots.current;
@@ -314,7 +314,7 @@ function useOIMSlotsByKeys(slotCollectionService, storeSlot, keys) {
314
314
  const heldSlots = [];
315
315
  let someKeyNamesAnotherIdentityNow = false;
316
316
  for (let i = 0; i < keysToHold.length; i++) {
317
- const slot = slotCollectionService.holdSlotByKey(storeSlot, keysToHold[i]);
317
+ const slot = storeSlot.slotService.holdSlotByKey(storeSlot, keysToHold[i]);
318
318
  heldSlots.push(slot);
319
319
  if (slot !== renderedSlots?.[i]) someKeyNamesAnotherIdentityNow = true;
320
320
  }
@@ -328,26 +328,22 @@ function useOIMSlotsByKeys(slotCollectionService, storeSlot, keys) {
328
328
  heldSlot.owner.unholdSlot(heldSlot);
329
329
  }
330
330
  };
331
- }, [slotCollectionService, storeSlot, keysToHold]);
331
+ }, [storeSlot, keysToHold]);
332
332
  return slots;
333
333
  }
334
334
 
335
335
  // src/useOIMComputedValueByKey.ts
336
- var import_core6 = require("@oimdb/core");
337
- var sourcesOfComputedSlot = (computedSlot) => [import_core6.OIM_FROZEN_EMPTY_SLOTS, [computedSlot]];
338
- function useOIMComputedValueByKey(computedCollectionService, computedStoreSlot, key) {
339
- const computedSlot = useOIMSlotByKey(
340
- "useOIMComputedValueByKey",
341
- computedCollectionService,
342
- computedStoreSlot,
343
- key,
344
- sourcesOfComputedSlot
345
- );
336
+ var import_core7 = require("@oimdb/core");
337
+ var sourcesOfComputedSlot = (computedSlot) => [import_core7.OIM_FROZEN_EMPTY_SLOTS, [computedSlot]];
338
+ function useOIMComputedValueByKey(computedStoreSlot, key) {
339
+ const computedSlot = useOIMSlotByKey("useOIMComputedValueByKey", computedStoreSlot, key, sourcesOfComputedSlot);
346
340
  return computedSlot.computedValueService.getValueForSlot(computedSlot);
347
341
  }
348
342
 
349
343
  // src/useOIMSingleComputedValue.ts
350
344
  var import_react11 = require("react");
345
+ var import_core8 = require("@oimdb/core");
346
+ var sourcesOfComputedSlot2 = (computedSlot) => [import_core8.OIM_FROZEN_EMPTY_SLOTS, [computedSlot]];
351
347
  function useOIMSingleComputedValue(computedService, derive, createSources, dependencyList) {
352
348
  const db = useOIMDB();
353
349
  const computedStoreSlot = (0, import_react11.useMemo)(
@@ -355,11 +351,8 @@ function useOIMSingleComputedValue(computedService, derive, createSources, depen
355
351
  // eslint-disable-next-line react-hooks/exhaustive-deps -- the caller's list is checked at the call site
356
352
  [db, computedService, ...dependencyList]
357
353
  );
358
- return useOIMComputedValueByKey(
359
- computedService.computedCollectionService,
360
- computedStoreSlot,
361
- void 0
362
- );
354
+ const computedSlot = useOIMSingleSlot("useOIMSingleComputedValue", computedStoreSlot, sourcesOfComputedSlot2);
355
+ return computedSlot.computedValueService.getValueForSlot(computedSlot);
363
356
  }
364
357
 
365
358
  // src/useOIMComputedCollection.ts
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { DependencyList } from 'react';
3
- import { OIMDB, TOIMSlot, TOIMComputedSlot, TOIMStoreKey, OIMSlotCollectionService, TOIMEntitySlot, TOIMStoreSlot, OIMSingleSlotService, TOIMSingleSlotStore, TOIMBucketSlot, OIMSlotContentBucketService, TOIMEntityColumnName, TOIMColumnSlot, TOIMEntityColumnValue, TOIMIndexableSlot, OIMComputedService, TOIMComputedSources, TOIMComputedStoreSlot } from '@oimdb/core';
3
+ import { OIMDB, TOIMSlot, TOIMComputedContentSlot, TOIMStoreKey, TOIMStoreSlot, TOIMEntitySlot, OIMSlotCollectionService, TOIMSingleSlotStore, TOIMEntityContentSlot, OIMSingleSlotService, TOIMBucketContentSlot, OIMSlotContentBucketService, TOIMEntityColumnName, OIMColumnSlotService, TOIMColumnSlot, OIMColumnSlotOwner, TOIMEntityColumnValue, TOIMBucketSlot, TOIMIndexableSlot, TOIMComputedSlot, OIMSingleComputedService, TOIMComputedSources, OIMComputedService, TOIMComputedStoreSlot } from '@oimdb/core';
4
4
 
5
5
  declare const OIMDBContext: React.Context<OIMDB | undefined>;
6
6
 
@@ -13,7 +13,7 @@ declare function useOIMDB(): OIMDB;
13
13
 
14
14
  type TOIMBinding<TValue> = {
15
15
  readonly sourceSlots?: readonly TOIMSlot[] | undefined;
16
- readonly sourceComputeds?: readonly TOIMComputedSlot[] | undefined;
16
+ readonly sourceComputeds?: readonly TOIMComputedContentSlot[] | undefined;
17
17
  readonly derive: () => TValue;
18
18
  };
19
19
 
@@ -26,32 +26,36 @@ type TOIMBinding<TValue> = {
26
26
  */
27
27
  declare function useOIMValue<TValue>(binding: TOIMBinding<TValue>): TValue;
28
28
 
29
- declare function useOIMComputedValue<TValue>(computedSlot: TOIMComputedSlot<TValue>): TValue;
29
+ declare function useOIMComputedValue<TValue>(computedSlot: TOIMComputedContentSlot<TValue>): TValue;
30
30
 
31
- declare function useOIMEntity<TEntity extends object, TKey = TOIMStoreKey, TStore = unknown>(entityCollectionService: Pick<OIMSlotCollectionService<TKey, TOIMEntitySlot<TEntity, TKey>, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>, entityStoreSlot: TOIMStoreSlot<TStore, TOIMEntitySlot<TEntity, TKey>>, key: TKey): TEntity | undefined;
31
+ declare function useOIMEntity<TEntity extends object, TKey extends NonNullable<unknown> = TOIMStoreKey, TStore = unknown>(entityStoreSlot: TOIMStoreSlot<TStore, TOIMEntitySlot<TEntity, TKey>, unknown, Pick<OIMSlotCollectionService<TKey, TOIMEntitySlot<TEntity, TKey>, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>>, key: TKey): TEntity | undefined;
32
32
 
33
- declare function useOIMSingleEntity<TEntity extends object>(singleSlotService: Pick<OIMSingleSlotService<TOIMEntitySlot<TEntity, void>, unknown>, 'getSingleSlot' | 'holdSingleSlot'>, singleSlotStoreSlot: TOIMStoreSlot<TOIMSingleSlotStore<TOIMEntitySlot<TEntity, void>>, TOIMEntitySlot<TEntity, void>>): TEntity | undefined;
33
+ declare function useOIMSingleEntity<TEntity extends object>(singleSlotStoreSlot: TOIMStoreSlot<TOIMSingleSlotStore<TOIMEntityContentSlot<TEntity>>, TOIMEntityContentSlot<TEntity>, unknown, Pick<OIMSingleSlotService<TOIMEntityContentSlot<TEntity>, unknown>, 'getSingleSlot' | 'holdSingleSlot'>>): TEntity | undefined;
34
34
 
35
- declare function useOIMSingleBucketSlots<TEntity extends object, TSlots = unknown>(singleSlotService: Pick<OIMSingleSlotService<TOIMBucketSlot<TSlots, void>, Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>>, 'getSingleSlot' | 'holdSingleSlot' | 'slotContentService'>, singleSlotStoreSlot: TOIMStoreSlot<TOIMSingleSlotStore<TOIMBucketSlot<TSlots, void>>, TOIMBucketSlot<TSlots, void>>): readonly TOIMEntitySlot<TEntity>[];
35
+ declare function useOIMSingleBucketSlots<TEntity extends object, TSlots = unknown>(singleSlotStoreSlot: TOIMStoreSlot<TOIMSingleSlotStore<TOIMBucketContentSlot<TSlots>>, TOIMBucketContentSlot<TSlots>, unknown, Pick<OIMSingleSlotService<TOIMBucketContentSlot<TSlots>, Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>>, 'getSingleSlot' | 'holdSingleSlot' | 'slotContentService'>>): readonly TOIMEntitySlot<TEntity>[];
36
36
 
37
37
  /**
38
38
  * One field of one identity. It cannot go through {@link useOIMValue}: a column identity is
39
39
  * minted INSIDE the subscription, so there is no root slot a binding could name. `slot` is an
40
40
  * ENTITY SLOT because an entity slot is what can be taken out from under this component — ../README.md.
41
41
  */
42
- declare function useOIMField<TEntity extends object, TName extends TOIMEntityColumnName<TEntity>, TKey = TOIMStoreKey>(entityCollectionService: Pick<OIMSlotCollectionService<TKey, TOIMEntitySlot<TEntity, TKey>, unknown, unknown, TName, TOIMColumnSlot>, 'getColumn'>, slot: TOIMEntitySlot<TEntity, TKey>, name: TName): TOIMEntityColumnValue<TEntity, TName> | undefined;
42
+ declare function useOIMField<TEntity extends object, TName extends TOIMEntityColumnName<TEntity>, TKey extends NonNullable<unknown> = TOIMStoreKey>(entityCollectionService: {
43
+ readonly columnSlotService: Pick<OIMColumnSlotService<TOIMEntitySlot<TEntity, TKey>, TName, TOIMColumnSlot, OIMColumnSlotOwner<TOIMSlot, TOIMColumnSlot>>, 'getColumn'>;
44
+ }, slot: TOIMEntitySlot<TEntity, TKey>, name: TName): TOIMEntityColumnValue<TEntity, TName> | undefined;
43
45
 
44
46
  /** A bucket's slots, copied out of the service's LIVE container, in the collection's order. */
45
- declare function useOIMBucketSlots<TEntity extends object, TSlots = unknown>(bucketCollectionService: Pick<OIMSlotCollectionService<unknown, TOIMBucketSlot<TSlots>, unknown, Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>>, 'slotContentService'>, bucketSlot: TOIMBucketSlot<TSlots>): readonly TOIMEntitySlot<TEntity>[];
47
+ declare function useOIMBucketSlots<TEntity extends object, TSlots = unknown>(bucketCollectionService: {
48
+ readonly slotContentService: Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>;
49
+ }, bucketSlot: TOIMBucketContentSlot<TSlots>): readonly TOIMEntitySlot<TEntity>[];
46
50
 
47
- declare function useOIMBucketSlotsByKey<TEntity extends object, TSlots = unknown, TKey = TOIMStoreKey, TStore = unknown>(bucketCollectionService: Pick<OIMSlotCollectionService<TKey, TOIMBucketSlot<TSlots, TKey>, TStore, Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>>, 'getSlotByKey' | 'holdSlotByKey' | 'slotContentService'>, bucketStoreSlot: TOIMStoreSlot<TStore, TOIMBucketSlot<TSlots, TKey>>, key: TKey): readonly TOIMEntitySlot<TEntity>[];
51
+ declare function useOIMBucketSlotsByKey<TEntity extends object, TSlots = unknown, TKey extends NonNullable<unknown> = TOIMStoreKey, TStore = unknown>(bucketStoreSlot: TOIMStoreSlot<TStore, TOIMBucketSlot<TSlots, TKey>, unknown, Pick<OIMSlotCollectionService<TKey, TOIMBucketSlot<TSlots, TKey>, TStore, Pick<OIMSlotContentBucketService<TOIMEntitySlot<TEntity>, TSlots>, 'getBucketMemberSlots'>>, 'getSlotByKey' | 'holdSlotByKey' | 'slotContentService'>>, key: TKey): readonly TOIMEntitySlot<TEntity>[];
48
52
 
49
- declare function useOIMSlotsByKeys<TKey, TSlot extends TOIMIndexableSlot<TKey>, TStore = unknown>(slotCollectionService: Pick<OIMSlotCollectionService<TKey, TSlot, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>, storeSlot: TOIMStoreSlot<TStore, TSlot>, keys: readonly TKey[]): readonly TSlot[];
53
+ declare function useOIMSlotsByKeys<TKey extends NonNullable<unknown>, TSlot extends TOIMIndexableSlot<TKey>, TStore = unknown>(storeSlot: TOIMStoreSlot<TStore, TSlot, unknown, Pick<OIMSlotCollectionService<TKey, TSlot, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>>, keys: readonly TKey[]): readonly TSlot[];
50
54
 
51
- declare function useOIMComputedValueByKey<TValue, TKey = TOIMStoreKey, TStore = unknown>(computedCollectionService: Pick<OIMSlotCollectionService<TKey, TOIMComputedSlot<TValue, TKey>, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>, computedStoreSlot: TOIMStoreSlot<TStore, TOIMComputedSlot<TValue, TKey>>, key: TKey): TValue;
55
+ declare function useOIMComputedValueByKey<TValue, TKey extends NonNullable<unknown> = TOIMStoreKey, TStore = unknown>(computedStoreSlot: TOIMStoreSlot<TStore, TOIMComputedSlot<TValue, TKey>, unknown, Pick<OIMSlotCollectionService<TKey, TOIMComputedSlot<TValue, TKey>, TStore, unknown>, 'getSlotByKey' | 'holdSlotByKey'>>, key: TKey): TValue;
52
56
 
53
- declare function useOIMSingleComputedValue<TValue>(computedService: OIMComputedService<void, TValue, TOIMSingleSlotStore<TOIMComputedSlot<TValue, void>>>, derive: ((computedSlot: TOIMComputedSlot<TValue, void>) => TValue) | undefined, createSources: ((key: void, storeSlot: TOIMStoreSlot | undefined) => Partial<TOIMComputedSources>) | undefined, dependencyList: DependencyList): TValue;
57
+ declare function useOIMSingleComputedValue<TValue>(computedService: OIMSingleComputedService<TValue>, derive: ((computedSlot: TOIMComputedContentSlot<TValue>) => TValue) | undefined, createSources: ((computedSlot: TOIMComputedContentSlot<TValue>) => Partial<TOIMComputedSources>) | undefined, dependencyList: DependencyList): TValue;
54
58
 
55
- declare function useOIMComputedCollection<TKey, TValue, TStore>(computedService: OIMComputedService<TKey, TValue, TStore>, derive: ((computedSlot: TOIMComputedSlot<TValue, TKey>) => TValue) | undefined, createSources: ((key: TKey, storeSlot: TOIMStoreSlot | undefined) => Partial<TOIMComputedSources>) | undefined, dependencyList: DependencyList): TOIMComputedStoreSlot<TStore, TValue, TKey>;
59
+ declare function useOIMComputedCollection<TKey extends NonNullable<unknown>, TValue, TStore>(computedService: OIMComputedService<TKey, TValue, TStore>, derive: ((computedSlot: TOIMComputedSlot<TValue, TKey>) => TValue) | undefined, createSources: ((computedSlot: TOIMComputedSlot<TValue, TKey>) => Partial<TOIMComputedSources>) | undefined, dependencyList: DependencyList): TOIMComputedStoreSlot<TStore, TValue, TOIMComputedSlot<TValue, TKey>, OIMComputedService<TKey, TValue, TStore>['computedCollectionService']>;
56
60
 
57
61
  export { OIMDBContext, OIMDBProvider, type TOIMBinding, useOIMBucketSlots, useOIMBucketSlotsByKey, useOIMComputedCollection, useOIMComputedValue, useOIMComputedValueByKey, useOIMDB, useOIMEntity, useOIMField, useOIMSingleBucketSlots, useOIMSingleComputedValue, useOIMSingleEntity, useOIMSlotsByKeys, useOIMValue };