@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 +42 -48
- package/dist/index.cjs +66 -73
- package/dist/index.d.cts +17 -13
- package/dist/index.d.ts +17 -13
- package/dist/index.js +61 -68
- package/llms.txt +1011 -1064
- package/package.json +2 -2
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,
|
|
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,
|
|
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 =
|
|
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
|
-
// `
|
|
49
|
-
export const byDeckService =
|
|
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.
|
|
57
|
+
cardService.writeInBatch(cards, () => (cardsInDeck).map(entity => cardService.writeSlotContentByKey(cards, entity.id, entity)));
|
|
58
58
|
const bucket = byDeckService.holdSlotByKey(byDeck, deckId);
|
|
59
|
-
byDeckService.slotContentService.
|
|
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(
|
|
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(
|
|
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
|
|
105
|
-
|
|
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(
|
|
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(
|
|
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 `
|
|
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(
|
|
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(
|
|
141
|
-
computed store slot.
|
|
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(
|
|
149
|
-
- **`useOIMSingleComputedValue(computedService, derive, createSources, deps)`**: a single computed the
|
|
150
|
-
owns
|
|
151
|
-
`
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
- **`useOIMSingleEntity(
|
|
155
|
-
**`useOIMSingleBucketSlots(
|
|
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 =
|
|
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: `
|
|
185
|
-
`
|
|
186
|
-
`holdSingleSlot(storeSlot)`. The two single hooks take
|
|
187
|
-
|
|
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 =
|
|
192
|
+
const settingsService = createOIMSingleSlotService({ slotContentService: createOIMSlotContentEntityService<TSettings>() });
|
|
198
193
|
const settings = settingsService.createStoreSlot(db);
|
|
199
194
|
|
|
200
|
-
const pickedService =
|
|
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(
|
|
205
|
-
const pickedCards = useOIMSingleBucketSlots(
|
|
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 `
|
|
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.
|
|
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(
|
|
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.** `
|
|
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 (`
|
|
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 `
|
|
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
|
|
74
|
+
subscribeEffect(channelMap, onStoreChange, sourceSlots, sourceComputeds) {
|
|
75
75
|
this.version++;
|
|
76
|
-
return import_core.OIMEffectService.INSTANCE.subscribeEffect(channelMap,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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(
|
|
175
|
-
return useOIMSlotByKey("useOIMEntity",
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
subscribedSlot =
|
|
193
|
-
|
|
194
|
-
|
|
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(
|
|
210
|
-
return useOIMSingleSlot("useOIMSingleEntity",
|
|
212
|
+
function useOIMSingleEntity(singleSlotStoreSlot) {
|
|
213
|
+
return useOIMSingleSlot("useOIMSingleEntity", singleSlotStoreSlot).entity;
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
// src/useOIMSingleBucketSlots.ts
|
|
214
|
-
function useOIMSingleBucketSlots(
|
|
215
|
-
const bucketSlot = useOIMSingleSlot("useOIMSingleBucketSlots",
|
|
216
|
-
return Array.from(
|
|
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
|
|
226
|
+
var import_core5 = require("@oimdb/core");
|
|
224
227
|
function holdWhileSubscribed(slots, subscribe) {
|
|
225
|
-
(0,
|
|
228
|
+
(0, import_core5.oimHoldSlots)(slots);
|
|
226
229
|
const giveEveryHoldBack = () => {
|
|
227
|
-
(0,
|
|
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
|
-
|
|
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(
|
|
284
|
-
const bucketSlot = useOIMSlotByKey("useOIMBucketSlotsByKey",
|
|
285
|
-
return Array.from(
|
|
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
|
|
291
|
-
function useOIMSlotsByKeys(
|
|
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,
|
|
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 ||
|
|
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(
|
|
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 =
|
|
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
|
-
}, [
|
|
331
|
+
}, [storeSlot, keysToHold]);
|
|
332
332
|
return slots;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
// src/useOIMComputedValueByKey.ts
|
|
336
|
-
var
|
|
337
|
-
var sourcesOfComputedSlot = (computedSlot) => [
|
|
338
|
-
function useOIMComputedValueByKey(
|
|
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
|
-
|
|
359
|
-
|
|
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,
|
|
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
|
|
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:
|
|
29
|
+
declare function useOIMComputedValue<TValue>(computedSlot: TOIMComputedContentSlot<TValue>): TValue;
|
|
30
30
|
|
|
31
|
-
declare function useOIMEntity<TEntity extends object, TKey = TOIMStoreKey, TStore = unknown>(
|
|
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>(
|
|
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>(
|
|
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:
|
|
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:
|
|
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>(
|
|
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
|
|
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>(
|
|
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:
|
|
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
|
|
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 };
|