@oimdb/react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,496 @@
1
+ # @oimdb/react
2
+
3
+ React integration for OIMDB - Hooks for selection and subscription with reactive collections and indexes.
4
+
5
+ ## Overview
6
+
7
+ `@oimdb/react` provides React hooks that work with OIMDB reactive objects (`OIMReactiveCollection` and `OIMReactiveIndex`). The library includes both direct hooks for component-level usage and React Context utilities for application-wide data management.
8
+
9
+ ## Features
10
+
11
+ - **Reactive Integration**: Hooks work with `OIMReactiveCollection` and `OIMReactiveIndex` from `@oimdb/core`
12
+ - **Automatic Subscription**: Uses `useSyncExternalStore` for optimal React 18+ performance
13
+ - **Event Coalescing**: Leverages OIMDB's built-in event coalescing for efficient updates
14
+ - **Type Safety**: Full TypeScript support with advanced generic type inference
15
+ - **Context Support**: Optional React Context for centralized collection management
16
+ - **Flexible Usage**: Use hooks directly or through context provider pattern
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @oimdb/react @oimdb/core
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Basic Setup
27
+
28
+ ```typescript
29
+ import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
30
+ import {
31
+ useSelectEntitiesByPks,
32
+ selectEntitiesByIndexKey,
33
+ selectEntityByPk
34
+ } from '@oimdb/react';
35
+
36
+ // Create event queue and reactive collections
37
+ const queue = new OIMEventQueue();
38
+ const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
39
+ const usersCollection = new OIMRICollection(queue, {
40
+ collectionOpts: { selectPk: (user: User) => user.id },
41
+ indexes: { byTeam: userTeamIndex },
42
+ });
43
+ ```
44
+
45
+ ### Single Entity Selection
46
+
47
+ ```typescript
48
+ function UserProfile({ userId }: { userId: string }) {
49
+ const user = selectEntityByPk(usersCollection, userId);
50
+
51
+ if (!user) return <div>Loading...</div>;
52
+
53
+ return (
54
+ <div>
55
+ <h2>{user.name}</h2>
56
+ <p>Email: {user.email}</p>
57
+ </div>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ### Multiple Entities Selection
63
+
64
+ ```typescript
65
+ function UserList({ userIds }: { userIds: string[] }) {
66
+ const users = useSelectEntitiesByPks(usersCollection, userIds);
67
+
68
+ return (
69
+ <ul>
70
+ {users.map((user, index) => (
71
+ <li key={user?.id || index}>
72
+ {user ? user.name : 'Loading...'}
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ### Index-based Selection
81
+
82
+ ```typescript
83
+ function TeamMembers({ teamId }: { teamId: string }) {
84
+ const teamUsers = selectEntitiesByIndexKey(
85
+ usersCollection,
86
+ usersCollection.indexes.byTeam,
87
+ teamId
88
+ );
89
+
90
+ return (
91
+ <div>
92
+ {teamUsers.map((user, index) => (
93
+ <div key={user?.id || index}>
94
+ {user ? `${user.name} (${user.role})` : 'Loading...'}
95
+ </div>
96
+ ))}
97
+ </div>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## React Context Integration
103
+
104
+ For applications with multiple collections, use the React Context pattern for centralized management:
105
+
106
+ ### Context Setup
107
+
108
+ ```typescript
109
+ import {
110
+ OIMRICollectionsProvider,
111
+ useOIMCollectionsContext,
112
+ StrictCollectionsDictionary
113
+ } from '@oimdb/react';
114
+
115
+ interface User {
116
+ id: string;
117
+ name: string;
118
+ teamId: string;
119
+ }
120
+
121
+ interface Team {
122
+ id: string;
123
+ name: string;
124
+ }
125
+
126
+ function createCollections() {
127
+ const queue = new OIMEventQueue();
128
+
129
+ const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
130
+ const usersCollection = new OIMRICollection(queue, {
131
+ collectionOpts: { selectPk: (user: User) => user.id },
132
+ indexes: { byTeam: userTeamIndex },
133
+ });
134
+
135
+ const teamsCollection = new OIMRICollection(queue, {
136
+ collectionOpts: { selectPk: (team: Team) => team.id },
137
+ indexes: {},
138
+ });
139
+
140
+ return { users: usersCollection, teams: teamsCollection } as const;
141
+ }
142
+
143
+ type AppCollections = StrictCollectionsDictionary<ReturnType<typeof createCollections>>;
144
+ ```
145
+
146
+ ### Provider Setup
147
+
148
+ ```typescript
149
+ function App() {
150
+ const collections = React.useMemo(() => createCollections(), []);
151
+
152
+ return (
153
+ <OIMRICollectionsProvider collections={collections}>
154
+ <UserDashboard />
155
+ </OIMRICollectionsProvider>
156
+ );
157
+ }
158
+ ```
159
+
160
+ ### Using Context in Components
161
+
162
+ ```typescript
163
+ function UserDashboard() {
164
+ const { users, teams } = useOIMCollectionsContext<AppCollections>();
165
+
166
+ // Use collections with hooks
167
+ const allUsers = useSelectEntitiesByPks(users, []);
168
+ const teamMembers = selectEntitiesByIndexKey(
169
+ users,
170
+ users.indexes.byTeam,
171
+ 'team1'
172
+ );
173
+
174
+ return (
175
+ <div>
176
+ <h2>All Users: {allUsers.length}</h2>
177
+ <h3>Team 1 Members: {teamMembers.length}</h3>
178
+ </div>
179
+ );
180
+ }
181
+ ```
182
+
183
+ ### Custom Context
184
+
185
+ For multiple independent contexts:
186
+
187
+ ```typescript
188
+ const UserContext = createOIMCollectionsContext<{ users: typeof usersCollection }>();
189
+
190
+ function UserProvider({ children }: { children: React.ReactNode }) {
191
+ const collections = React.useMemo(() => ({ users: usersCollection }), []);
192
+
193
+ return (
194
+ <OIMRICollectionsProvider collections={collections} context={UserContext}>
195
+ {children}
196
+ </OIMRICollectionsProvider>
197
+ );
198
+ }
199
+
200
+ function UserComponent() {
201
+ const { users } = useOIMCollectionsContext(UserContext);
202
+ // Use users collection...
203
+ }
204
+ ```
205
+
206
+ ## API Reference
207
+
208
+ ### `selectEntityByPk(reactiveCollection, pk)`
209
+
210
+ Subscribes to a single entity from a reactive collection.
211
+
212
+ **Parameters:**
213
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
214
+ - `pk: TPk` - Primary key of the entity
215
+
216
+ **Returns:**
217
+ - `TEntity | undefined` - Entity data or undefined if not found
218
+
219
+ ### `useSelectEntitiesByPks(reactiveCollection, pks)`
220
+
221
+ Subscribes to multiple entities from a reactive collection.
222
+
223
+ **Parameters:**
224
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
225
+ - `pks: readonly TPk[]` - Array of primary keys
226
+
227
+ **Returns:**
228
+ - `(TEntity | undefined)[]` - Array of entities (undefined for missing entities)
229
+
230
+ ### `selectEntitiesByIndexKey(reactiveCollection, reactiveIndex, key)`
231
+
232
+ Subscribes to entities indexed by a specific key.
233
+
234
+ **Parameters:**
235
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
236
+ - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
237
+ - `key: TKey` - Index key to query
238
+
239
+ **Returns:**
240
+ - `(TEntity | undefined)[]` - Array of entities for the given index key
241
+
242
+ ### `selectEntitiesByIndexKeys(reactiveCollection, reactiveIndex, keys)`
243
+
244
+ Subscribes to entities indexed by multiple keys.
245
+
246
+ **Parameters:**
247
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
248
+ - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
249
+ - `keys: readonly TKey[]` - Array of index keys to query
250
+
251
+ **Returns:**
252
+ - `(TEntity | undefined)[]` - Array of entities for the given index keys
253
+
254
+ ### `useSelectPksByIndexKey(reactiveIndex, key)`
255
+
256
+ Subscribes to primary keys indexed by a specific key.
257
+
258
+ **Parameters:**
259
+ - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
260
+ - `key: TKey` - Index key to query
261
+
262
+ **Returns:**
263
+ - `TPk[]` - Array of primary keys for the given index key
264
+
265
+ ### `useSelectPksByIndexKeys(reactiveIndex, keys)`
266
+
267
+ Subscribes to primary keys indexed by multiple keys.
268
+
269
+ **Parameters:**
270
+ - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
271
+ - `keys: readonly TKey[]` - Array of index keys to query
272
+
273
+ **Returns:**
274
+ - `Map<TKey, TPk[]>` - Map of index keys to their corresponding primary keys
275
+
276
+ ## Context API Reference
277
+
278
+ ### `OIMRICollectionsProvider<T>`
279
+
280
+ Provider component for collections context.
281
+
282
+ **Props:**
283
+ - `collections: T` - Dictionary of reactive collections
284
+ - `children: ReactNode` - React children
285
+ - `context?: React.Context<OIMContextValue<T>>` - Optional custom context
286
+
287
+ ### `useOIMCollectionsContext<T>(context?)`
288
+
289
+ Hook to access collections from context.
290
+
291
+ **Parameters:**
292
+ - `context?: React.Context<OIMContextValue<T>>` - Optional custom context
293
+
294
+ **Returns:**
295
+ - `T` - Collections dictionary with full type safety
296
+
297
+ **Throws:**
298
+ - Error if used outside of provider
299
+
300
+ ### `createOIMCollectionsContext<T>()`
301
+
302
+ Creates a custom collections context with specific typing.
303
+
304
+ **Returns:**
305
+ - `React.Context<OIMContextValue<T>>` - Typed React context
306
+
307
+ ### Type Utilities
308
+
309
+ #### `StrictCollectionsDictionary<T>`
310
+
311
+ Ensures collections dictionary maintains exact types.
312
+
313
+ #### `CollectionsDictionary`
314
+
315
+ Base type for any collections dictionary.
316
+
317
+ #### `ExtractEntityType<T>`, `ExtractPkType<T>`, etc.
318
+
319
+ Type utilities for extracting specific types from collections.
320
+
321
+ ## Architecture
322
+
323
+ ### Reactive Collections Integration
324
+
325
+ The hooks work directly with OIMDB reactive objects:
326
+
327
+ ```typescript
328
+ // Use reactive collections and indexes directly
329
+ const user = selectEntityByPk(reactiveCollection, 'user123');
330
+ const posts = selectEntitiesByIndexKey(reactiveCollection, reactiveIndex, 'tech');
331
+ ```
332
+
333
+ ### Event Subscription
334
+
335
+ Hooks automatically subscribe to OIMDB reactive events using `useSyncExternalStore`:
336
+
337
+ - **Collection updates**: Subscribe to `reactiveCollection.updateEventEmitter`
338
+ - **Index updates**: Subscribe to `reactiveIndex.updateEventEmitter`
339
+ - **Optimized subscriptions**: Subscribe only to specific keys for efficient updates
340
+ - **Automatic cleanup**: Unsubscribe when component unmounts
341
+
342
+ ### Performance
343
+
344
+ - **React 18+ Integration**: Uses `useSyncExternalStore` for optimal performance
345
+ - **Event Coalescing**: OIMDB's built-in event coalescing reduces unnecessary re-renders
346
+ - **Key-specific subscriptions**: Only listen to changes for relevant data
347
+ - **Memory Management**: Automatic cleanup prevents memory leaks
348
+ - **Efficient batching**: Updates are batched through React's concurrent features
349
+
350
+ ## Examples
351
+
352
+ ### Complete Example
353
+
354
+ ```typescript
355
+ import React from 'react';
356
+ import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
357
+ import { selectEntityByPk, useSelectEntitiesByPks, selectEntitiesByIndexKey } from '@oimdb/react';
358
+
359
+ interface User {
360
+ id: string;
361
+ name: string;
362
+ email: string;
363
+ teamId: string;
364
+ }
365
+
366
+ // Setup
367
+ function createUserCollection() {
368
+ const queue = new OIMEventQueue();
369
+ const teamIndex = new OIMReactiveIndexManual<string, string>(queue);
370
+
371
+ return new OIMRICollection(queue, {
372
+ collectionOpts: { selectPk: (user: User) => user.id },
373
+ indexes: { byTeam: teamIndex },
374
+ });
375
+ }
376
+
377
+ const usersCollection = createUserCollection();
378
+
379
+ // Component
380
+ function UserProfile({ userId }: { userId: string }) {
381
+ const user = selectEntityByPk(usersCollection, userId);
382
+
383
+ if (!user) return <div>Loading...</div>;
384
+
385
+ return <h2>{user.name}</h2>;
386
+ }
387
+
388
+ function TeamDashboard({ teamId }: { teamId: string }) {
389
+ const teamMembers = selectEntitiesByIndexKey(
390
+ usersCollection,
391
+ usersCollection.indexes.byTeam,
392
+ teamId
393
+ );
394
+
395
+ return (
396
+ <div>
397
+ <h3>Team Members ({teamMembers.length})</h3>
398
+ {teamMembers.map(user => (
399
+ <div key={user.id}>{user.name}</div>
400
+ ))}
401
+ </div>
402
+ );
403
+ }
404
+ ```
405
+
406
+ ### With Context Provider
407
+
408
+ ```typescript
409
+ import {
410
+ OIMRICollectionsProvider,
411
+ useOIMCollectionsContext
412
+ } from '@oimdb/react';
413
+
414
+ function App() {
415
+ const collections = React.useMemo(() => ({
416
+ users: createUserCollection(),
417
+ // ... other collections
418
+ }), []);
419
+
420
+ return (
421
+ <OIMRICollectionsProvider collections={collections}>
422
+ <Dashboard />
423
+ </OIMRICollectionsProvider>
424
+ );
425
+ }
426
+
427
+ function Dashboard() {
428
+ const { users } = useOIMCollectionsContext();
429
+ const allUsers = useSelectEntitiesByPks(users, []);
430
+
431
+ return <div>Total Users: {allUsers.length}</div>;
432
+ }
433
+ ```
434
+
435
+ ## Migration from v0.x
436
+
437
+ The v1.x API has changed significantly to work with reactive collections:
438
+
439
+ ### Hook Name Changes
440
+
441
+ ```typescript
442
+ // v0.x - Abstract storage interfaces
443
+ const user = useEntity(userStorage, 'user123');
444
+ const users = useEntities(userStorage, userIds);
445
+ const posts = useIndex(postStorage, categoryIndex, 'tech');
446
+
447
+ // v1.x - Reactive collections
448
+ const user = selectEntityByPk(reactiveCollection, 'user123');
449
+ const users = useSelectEntitiesByPks(reactiveCollection, userIds);
450
+ const posts = selectEntitiesByIndexKey(reactiveCollection, reactiveIndex, 'tech');
451
+ ```
452
+
453
+ ### Collection Creation
454
+
455
+ ```typescript
456
+ // v0.x - With DX layer
457
+ const db = createDb({ scheduler: 'microtask' });
458
+ const users = db.createCollection<User>();
459
+ const user = useEntity(users.advanced.collection, userId);
460
+
461
+ // v1.x - Direct reactive collections
462
+ const queue = new OIMEventQueue();
463
+ const usersCollection = new OIMRICollection(queue, {
464
+ collectionOpts: { selectPk: (user: User) => user.id },
465
+ indexes: {},
466
+ });
467
+ const user = selectEntityByPk(usersCollection, userId);
468
+ ```
469
+
470
+ ### Context API
471
+
472
+ ```typescript
473
+ // v0.x - No context support
474
+
475
+ // v1.x - Full context support
476
+ const collections = { users: usersCollection };
477
+ <OIMRICollectionsProvider collections={collections}>
478
+ <App />
479
+ </OIMRICollectionsProvider>
480
+ ```
481
+
482
+ ### Key Changes
483
+
484
+ - **Hook naming**: More explicit names like `selectEntityByPk` vs `useEntity`
485
+ - **Parameters**: Direct reactive collection objects instead of storage abstractions
486
+ - **Context**: New context API for centralized collection management
487
+ - **Type safety**: Enhanced TypeScript support with better inference
488
+
489
+ ## Dependencies
490
+
491
+ - `@oimdb/core` - Core OIMDB functionality
492
+ - `react` - React hooks and components
493
+
494
+ ## License
495
+
496
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ OIMRICollectionsContext: () => OIMRICollectionsContext,
24
+ OIMRICollectionsProvider: () => OIMRICollectionsProvider,
25
+ createOIMCollectionsContext: () => createOIMCollectionsContext,
26
+ selectEntitiesByIndexKey: () => selectEntitiesByIndexKey,
27
+ selectEntitiesByIndexKeys: () => selectEntitiesByIndexKeys,
28
+ selectEntityByPk: () => selectEntityByPk,
29
+ useOIMCollectionsContext: () => useOIMCollectionsContext,
30
+ useSelectEntitiesByPks: () => useSelectEntitiesByPks,
31
+ useSelectPksByIndexKey: () => useSelectPksByIndexKey,
32
+ useSelectPksByIndexKeys: () => useSelectPksByIndexKeys
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/hooks/index.ts
37
+ var import_react = require("react");
38
+ var import_react2 = require("react");
39
+ var useSelectPksByIndexKeys = (reactiveIndex, keys) => {
40
+ const subscribe = (0, import_react.useMemo)(() => {
41
+ return (onStoreChange) => reactiveIndex.updateEventEmitter.subscribeOnKeys(
42
+ keys,
43
+ onStoreChange
44
+ );
45
+ }, [keys, reactiveIndex.updateEventEmitter]);
46
+ const getSnapshot = (0, import_react.useMemo)(
47
+ () => () => keys.map((key) => reactiveIndex.getPksByKey(key)),
48
+ [keys, reactiveIndex]
49
+ );
50
+ const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
51
+ return snapshot;
52
+ };
53
+ var useSelectPksByIndexKey = (reactiveIndex, key) => {
54
+ const subscribe = (0, import_react.useMemo)(() => {
55
+ return (onStoreChange) => reactiveIndex.updateEventEmitter.subscribeOnKey(key, onStoreChange);
56
+ }, [key, reactiveIndex.updateEventEmitter]);
57
+ const getSnapshot = (0, import_react.useMemo)(
58
+ () => () => reactiveIndex.getPksByKey(key),
59
+ [key, reactiveIndex]
60
+ );
61
+ const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
62
+ return snapshot;
63
+ };
64
+ var selectEntityByPk = (reactiveCollection, pk) => {
65
+ const subscribe = (0, import_react.useMemo)(() => {
66
+ return (onStoreChange) => reactiveCollection.updateEventEmitter.subscribeOnKey(
67
+ pk,
68
+ onStoreChange
69
+ );
70
+ }, [pk, reactiveCollection.updateEventEmitter]);
71
+ const getSnapshot = (0, import_react.useMemo)(
72
+ () => () => reactiveCollection.getOneByPk(pk),
73
+ [pk, reactiveCollection]
74
+ );
75
+ const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
76
+ return snapshot;
77
+ };
78
+ var useSelectEntitiesByPks = (reactiveCollection, pks) => {
79
+ const subscribe = (0, import_react.useMemo)(() => {
80
+ return (onStoreChange) => reactiveCollection.updateEventEmitter.subscribeOnKeys(
81
+ pks,
82
+ onStoreChange
83
+ );
84
+ }, [pks, reactiveCollection.updateEventEmitter]);
85
+ const getSnapshot = (0, import_react.useMemo)(
86
+ () => () => pks.map((pk) => reactiveCollection.getOneByPk(pk)),
87
+ [pks, reactiveCollection]
88
+ );
89
+ const snapshot = (0, import_react2.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
90
+ return snapshot;
91
+ };
92
+ var selectEntitiesByIndexKey = (reactiveCollection, reactiveIndex, key) => {
93
+ const pks = useSelectPksByIndexKey(reactiveIndex, key);
94
+ return useSelectEntitiesByPks(reactiveCollection, pks);
95
+ };
96
+ var unique = (array) => {
97
+ return Array.from(new Set(array));
98
+ };
99
+ var selectEntitiesByIndexKeys = (reactiveCollection, reactiveIndex, keys) => {
100
+ const pks = useSelectPksByIndexKeys(reactiveIndex, keys);
101
+ const pksArray = (0, import_react.useMemo)(
102
+ () => unique(Array.from(pks.values()).flat()),
103
+ [pks]
104
+ );
105
+ return useSelectEntitiesByPks(reactiveCollection, pksArray);
106
+ };
107
+
108
+ // src/context/index.tsx
109
+ var import_react3 = require("react");
110
+ var import_jsx_runtime = require("react/jsx-runtime");
111
+ var OIMRICollectionsContext = (0, import_react3.createContext)(null);
112
+ function createOIMCollectionsContext() {
113
+ return (0, import_react3.createContext)(null);
114
+ }
115
+ function OIMRICollectionsProvider({
116
+ collections,
117
+ children,
118
+ context = OIMRICollectionsContext
119
+ }) {
120
+ const Provider = context.Provider;
121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, { value: collections, children });
122
+ }
123
+ function useOIMCollectionsContext(context) {
124
+ const contextToUse = context || OIMRICollectionsContext;
125
+ const collections = (0, import_react3.useContext)(contextToUse);
126
+ if (!collections) {
127
+ throw new Error(
128
+ "useOIMCollectionsContext must be used within an OIMRICollectionsProvider"
129
+ );
130
+ }
131
+ return collections;
132
+ }
133
+ // Annotate the CommonJS export names for ESM import in node:
134
+ 0 && (module.exports = {
135
+ OIMRICollectionsContext,
136
+ OIMRICollectionsProvider,
137
+ createOIMCollectionsContext,
138
+ selectEntitiesByIndexKey,
139
+ selectEntitiesByIndexKeys,
140
+ selectEntityByPk,
141
+ useOIMCollectionsContext,
142
+ useSelectEntitiesByPks,
143
+ useSelectPksByIndexKey,
144
+ useSelectPksByIndexKeys
145
+ });
@@ -0,0 +1,45 @@
1
+ import { TOIMPk, OIMIndex, OIMReactiveIndex, OIMReactiveCollection, OIMRICollection } from '@oimdb/core';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { ReactNode } from 'react';
5
+
6
+ declare const useSelectPksByIndexKeys: <TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, keys: readonly TKey[]) => (readonly TPk[])[];
7
+ declare const useSelectPksByIndexKey: <TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, key: TKey) => readonly TPk[];
8
+ declare const selectEntityByPk: <TEntity extends object, TPk extends TOIMPk>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, pk: TPk) => TEntity | undefined;
9
+ declare const useSelectEntitiesByPks: <TEntity extends object, TPk extends TOIMPk>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, pks: readonly TPk[]) => (TEntity | undefined)[];
10
+ declare const selectEntitiesByIndexKey: <TEntity extends object, TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, key: TKey) => (TEntity | undefined)[];
11
+ declare const selectEntitiesByIndexKeys: <TEntity extends object, TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, keys: readonly TKey[]) => (TEntity | undefined)[];
12
+
13
+ type AnyOIMRICollection = OIMRICollection<object, TOIMPk, string, TOIMPk, any, any>;
14
+ type CollectionsDictionary = Record<string, AnyOIMRICollection>;
15
+ type TypedCollectionsDictionary<T extends CollectionsDictionary> = T;
16
+ type OIMContextValue<T extends CollectionsDictionary = CollectionsDictionary> = T | null;
17
+ declare const OIMRICollectionsContext: React.Context<OIMContextValue<CollectionsDictionary>>;
18
+ declare function createOIMCollectionsContext<T extends CollectionsDictionary>(): React.Context<OIMContextValue<T>>;
19
+ interface OIMRICollectionsProviderProps<T extends CollectionsDictionary> {
20
+ collections: T;
21
+ children: ReactNode;
22
+ context?: React.Context<OIMContextValue<T>>;
23
+ }
24
+ declare function OIMRICollectionsProvider<T extends CollectionsDictionary>({ collections, children, context, }: OIMRICollectionsProviderProps<T>): react_jsx_runtime.JSX.Element;
25
+ declare function useOIMCollectionsContext<T extends CollectionsDictionary = CollectionsDictionary>(context?: React.Context<OIMContextValue<T>>): T;
26
+ type ExtractEntityType<T> = T extends OIMRICollection<infer E, TOIMPk, string, TOIMPk, any, any> ? E : never;
27
+ type ExtractPkType<T> = T extends OIMRICollection<object, infer P, string, TOIMPk, any, any> ? P : never;
28
+ type ExtractIndexNameType<T> = T extends OIMRICollection<object, TOIMPk, infer IN, TOIMPk, any, any> ? IN : never;
29
+ type ExtractIndexKeyType<T> = T extends OIMRICollection<object, TOIMPk, string, infer IK, any, any> ? IK : never;
30
+ type CreateCollectionsDictionary<T extends Record<string, AnyOIMRICollection>> = {
31
+ [K in keyof T]: T[K];
32
+ };
33
+ type ExtractCollectionSignature<T> = T extends OIMRICollection<infer E, infer P, infer IN, infer IK, infer I, infer RI> ? {
34
+ entity: E;
35
+ pk: P;
36
+ indexName: IN;
37
+ indexKey: IK;
38
+ index: I;
39
+ reactiveIndex: RI;
40
+ } : never;
41
+ type StrictCollectionsDictionary<T> = {
42
+ [K in keyof T]: T[K] extends OIMRICollection<infer E, infer P, infer IN, infer IK, infer I, infer RI> ? OIMRICollection<E, P, IN, IK, I, RI> : never;
43
+ };
44
+
45
+ export { type AnyOIMRICollection, type CollectionsDictionary, type CreateCollectionsDictionary, type ExtractCollectionSignature, type ExtractEntityType, type ExtractIndexKeyType, type ExtractIndexNameType, type ExtractPkType, type OIMContextValue, OIMRICollectionsContext, OIMRICollectionsProvider, type OIMRICollectionsProviderProps, type StrictCollectionsDictionary, type TypedCollectionsDictionary, createOIMCollectionsContext, selectEntitiesByIndexKey, selectEntitiesByIndexKeys, selectEntityByPk, useOIMCollectionsContext, useSelectEntitiesByPks, useSelectPksByIndexKey, useSelectPksByIndexKeys };
@@ -0,0 +1,45 @@
1
+ import { TOIMPk, OIMIndex, OIMReactiveIndex, OIMReactiveCollection, OIMRICollection } from '@oimdb/core';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import * as React from 'react';
4
+ import { ReactNode } from 'react';
5
+
6
+ declare const useSelectPksByIndexKeys: <TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, keys: readonly TKey[]) => (readonly TPk[])[];
7
+ declare const useSelectPksByIndexKey: <TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, key: TKey) => readonly TPk[];
8
+ declare const selectEntityByPk: <TEntity extends object, TPk extends TOIMPk>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, pk: TPk) => TEntity | undefined;
9
+ declare const useSelectEntitiesByPks: <TEntity extends object, TPk extends TOIMPk>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, pks: readonly TPk[]) => (TEntity | undefined)[];
10
+ declare const selectEntitiesByIndexKey: <TEntity extends object, TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, key: TKey) => (TEntity | undefined)[];
11
+ declare const selectEntitiesByIndexKeys: <TEntity extends object, TPk extends TOIMPk, TKey extends TOIMPk, TIndex extends OIMIndex<TKey, TPk>>(reactiveCollection: OIMReactiveCollection<TEntity, TPk>, reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>, keys: readonly TKey[]) => (TEntity | undefined)[];
12
+
13
+ type AnyOIMRICollection = OIMRICollection<object, TOIMPk, string, TOIMPk, any, any>;
14
+ type CollectionsDictionary = Record<string, AnyOIMRICollection>;
15
+ type TypedCollectionsDictionary<T extends CollectionsDictionary> = T;
16
+ type OIMContextValue<T extends CollectionsDictionary = CollectionsDictionary> = T | null;
17
+ declare const OIMRICollectionsContext: React.Context<OIMContextValue<CollectionsDictionary>>;
18
+ declare function createOIMCollectionsContext<T extends CollectionsDictionary>(): React.Context<OIMContextValue<T>>;
19
+ interface OIMRICollectionsProviderProps<T extends CollectionsDictionary> {
20
+ collections: T;
21
+ children: ReactNode;
22
+ context?: React.Context<OIMContextValue<T>>;
23
+ }
24
+ declare function OIMRICollectionsProvider<T extends CollectionsDictionary>({ collections, children, context, }: OIMRICollectionsProviderProps<T>): react_jsx_runtime.JSX.Element;
25
+ declare function useOIMCollectionsContext<T extends CollectionsDictionary = CollectionsDictionary>(context?: React.Context<OIMContextValue<T>>): T;
26
+ type ExtractEntityType<T> = T extends OIMRICollection<infer E, TOIMPk, string, TOIMPk, any, any> ? E : never;
27
+ type ExtractPkType<T> = T extends OIMRICollection<object, infer P, string, TOIMPk, any, any> ? P : never;
28
+ type ExtractIndexNameType<T> = T extends OIMRICollection<object, TOIMPk, infer IN, TOIMPk, any, any> ? IN : never;
29
+ type ExtractIndexKeyType<T> = T extends OIMRICollection<object, TOIMPk, string, infer IK, any, any> ? IK : never;
30
+ type CreateCollectionsDictionary<T extends Record<string, AnyOIMRICollection>> = {
31
+ [K in keyof T]: T[K];
32
+ };
33
+ type ExtractCollectionSignature<T> = T extends OIMRICollection<infer E, infer P, infer IN, infer IK, infer I, infer RI> ? {
34
+ entity: E;
35
+ pk: P;
36
+ indexName: IN;
37
+ indexKey: IK;
38
+ index: I;
39
+ reactiveIndex: RI;
40
+ } : never;
41
+ type StrictCollectionsDictionary<T> = {
42
+ [K in keyof T]: T[K] extends OIMRICollection<infer E, infer P, infer IN, infer IK, infer I, infer RI> ? OIMRICollection<E, P, IN, IK, I, RI> : never;
43
+ };
44
+
45
+ export { type AnyOIMRICollection, type CollectionsDictionary, type CreateCollectionsDictionary, type ExtractCollectionSignature, type ExtractEntityType, type ExtractIndexKeyType, type ExtractIndexNameType, type ExtractPkType, type OIMContextValue, OIMRICollectionsContext, OIMRICollectionsProvider, type OIMRICollectionsProviderProps, type StrictCollectionsDictionary, type TypedCollectionsDictionary, createOIMCollectionsContext, selectEntitiesByIndexKey, selectEntitiesByIndexKeys, selectEntityByPk, useOIMCollectionsContext, useSelectEntitiesByPks, useSelectPksByIndexKey, useSelectPksByIndexKeys };
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ // src/hooks/index.ts
2
+ import { useMemo } from "react";
3
+ import { useSyncExternalStore } from "react";
4
+ var useSelectPksByIndexKeys = (reactiveIndex, keys) => {
5
+ const subscribe = useMemo(() => {
6
+ return (onStoreChange) => reactiveIndex.updateEventEmitter.subscribeOnKeys(
7
+ keys,
8
+ onStoreChange
9
+ );
10
+ }, [keys, reactiveIndex.updateEventEmitter]);
11
+ const getSnapshot = useMemo(
12
+ () => () => keys.map((key) => reactiveIndex.getPksByKey(key)),
13
+ [keys, reactiveIndex]
14
+ );
15
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
16
+ return snapshot;
17
+ };
18
+ var useSelectPksByIndexKey = (reactiveIndex, key) => {
19
+ const subscribe = useMemo(() => {
20
+ return (onStoreChange) => reactiveIndex.updateEventEmitter.subscribeOnKey(key, onStoreChange);
21
+ }, [key, reactiveIndex.updateEventEmitter]);
22
+ const getSnapshot = useMemo(
23
+ () => () => reactiveIndex.getPksByKey(key),
24
+ [key, reactiveIndex]
25
+ );
26
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
27
+ return snapshot;
28
+ };
29
+ var selectEntityByPk = (reactiveCollection, pk) => {
30
+ const subscribe = useMemo(() => {
31
+ return (onStoreChange) => reactiveCollection.updateEventEmitter.subscribeOnKey(
32
+ pk,
33
+ onStoreChange
34
+ );
35
+ }, [pk, reactiveCollection.updateEventEmitter]);
36
+ const getSnapshot = useMemo(
37
+ () => () => reactiveCollection.getOneByPk(pk),
38
+ [pk, reactiveCollection]
39
+ );
40
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
41
+ return snapshot;
42
+ };
43
+ var useSelectEntitiesByPks = (reactiveCollection, pks) => {
44
+ const subscribe = useMemo(() => {
45
+ return (onStoreChange) => reactiveCollection.updateEventEmitter.subscribeOnKeys(
46
+ pks,
47
+ onStoreChange
48
+ );
49
+ }, [pks, reactiveCollection.updateEventEmitter]);
50
+ const getSnapshot = useMemo(
51
+ () => () => pks.map((pk) => reactiveCollection.getOneByPk(pk)),
52
+ [pks, reactiveCollection]
53
+ );
54
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
55
+ return snapshot;
56
+ };
57
+ var selectEntitiesByIndexKey = (reactiveCollection, reactiveIndex, key) => {
58
+ const pks = useSelectPksByIndexKey(reactiveIndex, key);
59
+ return useSelectEntitiesByPks(reactiveCollection, pks);
60
+ };
61
+ var unique = (array) => {
62
+ return Array.from(new Set(array));
63
+ };
64
+ var selectEntitiesByIndexKeys = (reactiveCollection, reactiveIndex, keys) => {
65
+ const pks = useSelectPksByIndexKeys(reactiveIndex, keys);
66
+ const pksArray = useMemo(
67
+ () => unique(Array.from(pks.values()).flat()),
68
+ [pks]
69
+ );
70
+ return useSelectEntitiesByPks(reactiveCollection, pksArray);
71
+ };
72
+
73
+ // src/context/index.tsx
74
+ import { createContext, useContext } from "react";
75
+ import { jsx } from "react/jsx-runtime";
76
+ var OIMRICollectionsContext = createContext(null);
77
+ function createOIMCollectionsContext() {
78
+ return createContext(null);
79
+ }
80
+ function OIMRICollectionsProvider({
81
+ collections,
82
+ children,
83
+ context = OIMRICollectionsContext
84
+ }) {
85
+ const Provider = context.Provider;
86
+ return /* @__PURE__ */ jsx(Provider, { value: collections, children });
87
+ }
88
+ function useOIMCollectionsContext(context) {
89
+ const contextToUse = context || OIMRICollectionsContext;
90
+ const collections = useContext(contextToUse);
91
+ if (!collections) {
92
+ throw new Error(
93
+ "useOIMCollectionsContext must be used within an OIMRICollectionsProvider"
94
+ );
95
+ }
96
+ return collections;
97
+ }
98
+ export {
99
+ OIMRICollectionsContext,
100
+ OIMRICollectionsProvider,
101
+ createOIMCollectionsContext,
102
+ selectEntitiesByIndexKey,
103
+ selectEntitiesByIndexKeys,
104
+ selectEntityByPk,
105
+ useOIMCollectionsContext,
106
+ useSelectEntitiesByPks,
107
+ useSelectPksByIndexKey,
108
+ useSelectPksByIndexKeys
109
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@oimdb/react",
3
+ "version": "1.0.0",
4
+ "description": "React integration for OIMDB - Hooks for selection and subscription with external storage",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "sideEffects": false,
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/abaikov/oimdb.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/abaikov/oimdb/issues"
16
+ },
17
+ "homepage": "https://github.com/abaikov/oimdb#readme",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "tsup src/index.ts --dts --format esm,cjs",
27
+ "clean": "rimraf dist"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "dependencies": {
33
+ "@oimdb/core": "^1.0.0"
34
+ },
35
+ "peerDependencies": {
36
+ "@oimdb/core": "^1.0.0",
37
+ "react": "^18.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "tsup": "^8.5.0",
41
+ "typescript": "^5.2.2",
42
+ "rimraf": "^5.0.5",
43
+ "react": "^18.0.0",
44
+ "@types/react": "^18.0.0"
45
+ },
46
+ "author": "Andrei Baikov",
47
+ "license": "MIT"
48
+ }