@black-cape/microstore 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025, MicroStore Contributors
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,794 @@
1
+ # MicroStore
2
+
3
+ ![MicroStore Logo](https://github.com/black-cape/microstore/raw/refs/heads/main/logo.png)
4
+
5
+ **A powerful TypeScript React data normalization library that provides a single source of truth for your application state.**
6
+
7
+ MicroStore is an abstract data normalization layer for React projects that eliminates data duplication across AJAX / fetch requests and provides reactive access to normalized records. Built on [TinyBase](https://tinybase.org/), it automatically interprets REST API responses and maintains a consistent, normalized data record layer that your UI can reactively subscribe to. Each component using the reactivity layer still receives immutable copies of each record, but they will always be in sync.
8
+
9
+ ## 🚀 Features
10
+
11
+ - **🎯 Single Source of Truth**: Eliminates data duplication by normalizing records across all API requests
12
+ - **⚡ Reactive Updates**: Components automatically re-render when normalized data changes
13
+ - **🔄 Automatic REST Interpretation**: Built-in support for Ember REST Adapter and fastapi-cruddy-framework response formats
14
+ - **🔌 Provider Agnostic**: Works with any AJAX library (ky, axios, fetch) or query cache (React Query, SWR)
15
+ - **🛡️ TypeScript First**: Fully typed with comprehensive schema validation
16
+ - **🎨 Transform System**: Flexible field and record-level data transformations
17
+ - **⚡ Performance Optimized**: Built on TinyBase for efficient storage and queries
18
+
19
+ ## 📦 Installation
20
+
21
+ ```bash
22
+ npm install @black-cape/microstore
23
+ # or
24
+ yarn add @black-cape/microstore
25
+ # or
26
+ pnpm add @black-cape/microstore
27
+ ```
28
+
29
+ ## ⚛️ React Compatibility
30
+
31
+ MicroStore supports both **React 18** and **React 19**:
32
+
33
+ - ✅ **React 18.0+**: Full compatibility with all features
34
+ - ✅ **React 19.0+**: Full compatibility with latest React features
35
+ - 🔧 **React Compiler**: Optional support for React's experimental compiler
36
+
37
+ ### Testing Compatibility
38
+
39
+ Test your specific React version:
40
+
41
+ ```bash
42
+ # Test with React 18
43
+ npm install react@^18.0.0 react-dom@^18.0.0
44
+ npm run test:compatibility
45
+
46
+ # Test with React 19
47
+ npm install react@^19.0.0 react-dom@^19.0.0
48
+ npm run test:compatibility
49
+ ```
50
+
51
+ ## 🏃 Quick Start
52
+
53
+ ### 1. Define Your Schemas
54
+
55
+ ```typescript
56
+ import { MicroStore, MicroStoreProvider, useReactive } from 'microstore';
57
+
58
+ // Define your data schemas
59
+ const schemas = {
60
+ user: {
61
+ id: { type: 'string', primaryKey: true },
62
+ name: { type: 'string' },
63
+ email: { type: 'string' },
64
+ tags: { type: 'string', transform: 'json' }, // Will serialized / deserialized into and out of tinybase as JSON
65
+ preferences: { type: 'string', transform: 'json' } // Will serialized / deserialized into and out of tinybase as JSON
66
+ },
67
+ post: {
68
+ id: { type: 'string', primaryKey: true },
69
+ title: { type: 'string' },
70
+ content: { type: 'string' },
71
+ userId: { type: 'string' },
72
+ tags: { type: 'string', transform: 'json' } // Will serialized / deserialized into and out of tinybase as JSON
73
+ }
74
+ } as const;
75
+ ```
76
+
77
+ ### 2. Setup the Provider
78
+
79
+ ```typescript
80
+ import { MicroStore, MicroStoreProvider } from 'microstore';
81
+
82
+ const store = new MicroStore({ schemas });
83
+
84
+ function App() {
85
+ return (
86
+ <MicroStoreProvider store={store}>
87
+ <UserList />
88
+ </MicroStoreProvider>
89
+ );
90
+ }
91
+ ```
92
+
93
+ ### 3. Use with React Query (or any data fetcher)
94
+
95
+ ```typescript
96
+ import { useQuery } from '@tanstack/react-query';
97
+ import { useReactive, useMicroStore } from 'microstore';
98
+
99
+ function UserList() {
100
+ const store = useMicroStore();
101
+
102
+ // Fetch data with React Query
103
+ const { data: rawUsers } = useQuery({
104
+ queryKey: ['users'],
105
+ queryFn: async () => {
106
+ const response = await fetch('/api/users');
107
+ const data = await response.json();
108
+
109
+ // Push the response into MicroStore for normalization
110
+ store?.pushPayload('GET', data);
111
+
112
+ return data.users; // Return the raw array for React Query
113
+ }
114
+ });
115
+
116
+ // useReactive ensures components get the normalized, single-source-of-truth data
117
+ const users = useReactive('user', rawUsers || []);
118
+
119
+ return (
120
+ <ul>
121
+ {users.map(user => (
122
+ <UserItem key={user.id} user={user} />
123
+ ))}
124
+ </ul>
125
+ );
126
+ }
127
+
128
+ function UserItem({ user }) {
129
+ // This component will automatically re-render if this user
130
+ // is updated anywhere else in the application!
131
+ return (
132
+ <li>
133
+ {user.name} - {user.email}
134
+ <small>Joined: {user.createdAt.toLocaleDateString()}</small>
135
+ </li>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ## 🔌 Core Classes and Hooks
141
+
142
+ ### `MicroStore`
143
+
144
+ The main store class that handles data normalization, storage, and retrieval.
145
+
146
+ ```typescript
147
+ const store = new MicroStore({
148
+ schemas,
149
+ fieldTransforms?: FieldTransforms,
150
+ recordTransforms?: RecordTransforms,
151
+ interpreter?: MicrostoreInterpreter
152
+ });
153
+
154
+ // Push data from API responses
155
+ store.pushPayload('GET', response);
156
+ store.pushRecord('user', userData, 'POST');
157
+ store.pushRecords('user', userArray, 'GET');
158
+
159
+ // Direct data access
160
+ const user = store.peekRecord<User>('user', '123');
161
+ const allUsers = store.peekAll<User>('user');
162
+
163
+ // Data management
164
+ store.unloadRecord('user', '123');
165
+ store.unloadAll('user');
166
+ store.reset();
167
+ ```
168
+
169
+ ### `useReactive<T>(type: string, data: T[]): T[]`
170
+
171
+ React hook that wraps an array of records to provide reactive updates from the normalized store.
172
+
173
+ ```typescript
174
+ function UserList() {
175
+ const { data } = useQuery(['users'], fetchUsers);
176
+
177
+ // Returns normalized users that update reactively
178
+ // You can update individual records in MicroStore using
179
+ // websockets, for instance, or if you have many components
180
+ // querying for users on screen at different times, any REST response
181
+ // that has the latest information on user x would cause user x
182
+ // to synchronize across all components without additional queries
183
+ const users = useReactive('user', data?.users || []);
184
+
185
+ return <div>{users.map(user => <User key={JSON.stringify(user)} user={user} />)}</div>;
186
+ }
187
+ ```
188
+
189
+ ### `RESTInterpreter`
190
+
191
+ Built-in interpreter for standard REST API responses. Supports:
192
+
193
+ - **[Ember REST Adapter](https://api.emberjs.com/ember-data/release/classes/restadapter) format**
194
+ - **[fastapi-cruddy-framework](https://github.com/mdconaway/fastapi-cruddy-framework) format**
195
+ - Custom pluralized resource names
196
+
197
+ ```typescript
198
+ // Automatically normalizes responses like:
199
+ {
200
+ "users": [
201
+ { "id": "1", "name": "John" },
202
+ { "id": "2", "name": "Jane" }
203
+ ],
204
+ "posts": [
205
+ { "id": "1", "userId": "1", "title": "Hello World" }
206
+ ],
207
+ "meta": { "total": 100 }
208
+ }
209
+ ```
210
+
211
+ ### `MicroStoreProvider`
212
+
213
+ React context provider that makes the store available to child components.
214
+
215
+ ```typescript
216
+ <MicroStoreProvider store={store}>
217
+ <App />
218
+ </MicroStoreProvider>
219
+ ```
220
+
221
+ ## 🔄 Data Flow
222
+
223
+ 1. **API Request**: Use any HTTP client (fetch, ky, axios) or query library (React Query, SWR)
224
+ 2. **Normalization**: Push response data into MicroStore via `pushPayload()`
225
+ 3. **Storage**: Data is normalized, deduplicated, and stored in TinyBase
226
+ 4. **Reactive Access**: Components use `useReactive()` to get live, normalized data
227
+ 5. **Updates**: Any changes to normalized data automatically trigger component re-renders
228
+
229
+ ## 🛠️ Advanced Features
230
+
231
+ ### Custom Field Transforms
232
+
233
+ Transform data at the field level during serialization/deserialization:
234
+
235
+ ```typescript
236
+ const customTransforms = {
237
+ date: {
238
+ serialize: (value: Date) => value.toISOString(),
239
+ deserialize: (value: string) => new Date(value)
240
+ },
241
+ currency: {
242
+ serialize: (value: number) => Math.round(value * 100), // Store as cents
243
+ deserialize: (value: number) => value / 100 // Display as dollars
244
+ }
245
+ };
246
+
247
+ const store = new MicroStore({
248
+ schemas: {
249
+ product: {
250
+ id: { type: 'string', primaryKey: true },
251
+ name: { type: 'string' },
252
+ price: { type: 'number', transform: 'currency' },
253
+ createdAt: { type: 'string', transform: 'date' }
254
+ }
255
+ },
256
+ fieldTransforms: customTransforms
257
+ });
258
+ ```
259
+
260
+ ### Custom Record Transforms
261
+
262
+ Transform entire records during serialization/deserialization:
263
+
264
+ ```typescript
265
+ const recordTransforms = {
266
+ user: {
267
+ serialize: (user: User) => ({
268
+ ...user // you COULD omit the computed field (displayName) here, but it will be clipped out automatically by the field schema during storage
269
+ }),
270
+ deserialize: (userData: any) => ({
271
+ ...userData,
272
+ displayName: userData.fullName || userData.name // Computed field for UI
273
+ })
274
+ }
275
+ };
276
+ ```
277
+
278
+ ### Practical Zod Integration Example
279
+
280
+ You can use record transforms to integrate [Zod](https://zod.dev/) for robust type validation and transformation:
281
+
282
+ ```typescript
283
+ import { z } from 'zod';
284
+
285
+ // Define Zod schema for validation and type inference
286
+ const UserSchema = z.object({
287
+ id: z.string(),
288
+ email: z.string().email(),
289
+ name: z.string(),
290
+ createdAt: z.date(),
291
+ preferences: z.object({
292
+ theme: z.enum(['light', 'dark']),
293
+ notifications: z.boolean()
294
+ }),
295
+ // Computed properties available only in class instances
296
+ getDisplayName: z.function().returns(z.string()).optional()
297
+ });
298
+
299
+ // Create a User class with methods
300
+ class User {
301
+ constructor(
302
+ public id: string,
303
+ public email: string,
304
+ public name: string,
305
+ public createdAt: Date,
306
+ public preferences: { theme: 'light' | 'dark'; notifications: boolean }
307
+ ) {}
308
+
309
+ getDisplayName(): string {
310
+ return `${this.name} (${this.email})`;
311
+ }
312
+
313
+ toJSON() {
314
+ // Convert class instance to plain object for API serialization
315
+ return {
316
+ id: this.id,
317
+ email: this.email,
318
+ name: this.name,
319
+ createdAt: this.createdAt,
320
+ preferences: this.preferences
321
+ };
322
+ }
323
+
324
+ static fromJSON(data: any): User {
325
+ // Validate and create User instance from plain object
326
+ const validated = UserSchema.omit({ getDisplayName: true }).parse(data);
327
+ return new User(
328
+ validated.id,
329
+ validated.email,
330
+ validated.name,
331
+ validated.createdAt,
332
+ validated.preferences
333
+ );
334
+ }
335
+ }
336
+
337
+ // Configure record transforms with Zod validation
338
+ const recordTransforms = {
339
+ user: {
340
+ // serialize: receives User class instance, returns plain object for TinyBase storage
341
+ serialize: (user: User) => {
342
+ return user.toJSON(); // Convert class instance to POJO
343
+ },
344
+ // deserialize: receives POJO from TinyBase, returns User class instance for components
345
+ deserialize: (userData: any) => {
346
+ return User.fromJSON(userData); // Validate and convert to class instance
347
+ }
348
+ }
349
+ };
350
+
351
+ // Configure MicroStore with Zod-powered transforms
352
+ const store = new MicroStore({
353
+ schemas: {
354
+ user: {
355
+ id: { type: 'string', primaryKey: true },
356
+ email: { type: 'string' },
357
+ name: { type: 'string' },
358
+ createdAt: { type: 'string', transform: 'json' }, // Dates serialized as ISO strings
359
+ preferences: { type: 'string', transform: 'json' } // Objects serialized as JSON
360
+ }
361
+ },
362
+ recordTransforms
363
+ });
364
+
365
+ // Usage in components - you receive fully validated User class instances
366
+ function UserProfile({ userId }: { userId: string }) {
367
+ const user = store.peekRecord<User>('user', userId);
368
+
369
+ return (
370
+ <div>
371
+ <h1>{user?.getDisplayName()}</h1> {/* Class method available */}
372
+ <p>Theme: {user?.preferences.theme}</p>
373
+ <p>Notifications: {user?.preferences.notifications ? 'On' : 'Off'}</p>
374
+ </div>
375
+ );
376
+ }
377
+ ```
378
+
379
+ **Benefits of Zod Integration:**
380
+
381
+ - **Runtime Validation**: Ensures data integrity when deserializing from storage
382
+ - **Type Safety**: Full TypeScript support with inferred types
383
+ - **Class Methods**: Enable rich domain models with behavior, not just data
384
+ - **Error Handling**: Automatic validation errors for malformed data
385
+ - **Schema Evolution**: Easy to update schemas as your API evolves
386
+
387
+ Your `deserialize` function receives a POJO (plain old javascript object) format object _after_ it has already been run through `tinybase` field level `deserialize` functions. (So your arrays will be arrays, objects will be objects, etc) You can then take this simple POJO record and transform it into more complex types that cannot be represented in raw JSON, like class instances, dates, etc. If you create a `zod`-based deserialize function, the expectation of the correlated `serialize` method handler would be to receive a record object in its `zod` format, and to then convert it into its pure POJO format before it is then delegated to the final field-level transformers before being pushed into `tinybase` for reactivity.
388
+
389
+ ### Generating Schemas with ZodSchematizer
390
+
391
+ You can automatically generate MicroStore schemas from Zod models using TinyBase's [ZodSchematizer](https://tinybase.org/api/schematizer-zod/interfaces/schematizer/zodschematizer/):
392
+
393
+ ```bash
394
+ # Install the ZodSchematizer
395
+ npm install tinybase schematizer-zod zod
396
+ ```
397
+
398
+ ```typescript
399
+ import { z } from 'zod';
400
+ import { createZodSchematizer } from 'schematizer-zod';
401
+
402
+ // Define your Zod models
403
+ const UserZodModel = z.object({
404
+ id: z.string(),
405
+ email: z.string().email(),
406
+ name: z.string(),
407
+ age: z.number().min(0).max(120),
408
+ isActive: z.boolean(),
409
+ createdAt: z.date(), // Date object in application
410
+ preferences: z.object({
411
+ theme: z.enum(['light', 'dark']),
412
+ notifications: z.boolean()
413
+ }),
414
+ tags: z.array(z.string())
415
+ });
416
+
417
+ const PostZodModel = z.object({
418
+ id: z.string(),
419
+ title: z.string(),
420
+ content: z.string(),
421
+ userId: z.string(),
422
+ publishedAt: z.date().nullable(),
423
+ metadata: z.object({
424
+ readTime: z.number(),
425
+ wordCount: z.number()
426
+ })
427
+ });
428
+
429
+ // Create the ZodSchematizer
430
+ const schematizer = createZodSchematizer({
431
+ user: UserZodModel,
432
+ post: PostZodModel
433
+ });
434
+
435
+ // Generate base TinyBase schemas
436
+ const baseTinyBaseSchemas = schematizer.getTablesSchema();
437
+
438
+ // Define custom field transforms for complex types
439
+ const customFieldTransforms = {
440
+ date: {
441
+ // serialize: convert Date object to ISO string for TinyBase storage
442
+ serialize: (value: Date) => value.toISOString(),
443
+ // deserialize: convert ISO string back to Date object for application use
444
+ deserialize: (value: string) => new Date(value)
445
+ }
446
+ };
447
+
448
+ // Convert to MicroStore schemas by adding MicroStore-specific properties
449
+ const microStoreSchemas = {
450
+ user: {
451
+ ...baseTinyBaseSchemas.user,
452
+ // Override the id field to mark it as primary key
453
+ id: { ...baseTinyBaseSchemas.user.id, primaryKey: true },
454
+ // Add transforms for complex fields
455
+ createdAt: { type: 'string', transform: 'date' }, // Use custom date transform
456
+ preferences: { type: 'string', transform: 'json' },
457
+ tags: { type: 'string', transform: 'json' }
458
+ },
459
+ post: {
460
+ ...baseTinyBaseSchemas.post,
461
+ // Override the id field to mark it as primary key
462
+ id: { ...baseTinyBaseSchemas.post.id, primaryKey: true },
463
+ // Add transforms for complex fields
464
+ publishedAt: { type: 'string', transform: 'date' }, // Use custom date transform
465
+ metadata: { type: 'string', transform: 'json' }
466
+ }
467
+ } as const;
468
+
469
+ // Create MicroStore with generated schemas and custom transforms
470
+ const store = new MicroStore({
471
+ schemas: microStoreSchemas,
472
+ fieldTransforms: customFieldTransforms, // Add custom field transforms
473
+ recordTransforms: {
474
+ user: {
475
+ // serialize: receives Zod model instance, returns POJO for TinyBase storage
476
+ serialize: (user: z.infer<typeof UserZodModel>) => {
477
+ // Convert Zod model to plain object
478
+ return {
479
+ id: user.id,
480
+ email: user.email,
481
+ name: user.name,
482
+ age: user.age,
483
+ isActive: user.isActive,
484
+ createdAt: user.createdAt, // Date object - will be converted by field transform
485
+ preferences: user.preferences,
486
+ tags: user.tags
487
+ };
488
+ },
489
+ // deserialize: receives POJO from TinyBase, returns Zod-validated model
490
+ deserialize: (data: any) => {
491
+ return UserZodModel.parse(data); // createdAt will be Date object from field transform
492
+ }
493
+ },
494
+ post: {
495
+ // serialize: receives Zod model instance, returns POJO for TinyBase storage
496
+ serialize: (post: z.infer<typeof PostZodModel>) => {
497
+ // Convert Zod model to plain object
498
+ return {
499
+ id: post.id,
500
+ title: post.title,
501
+ content: post.content,
502
+ userId: post.userId,
503
+ publishedAt: post.publishedAt, // Date object - will be converted by field transform
504
+ metadata: post.metadata
505
+ };
506
+ },
507
+ // deserialize: receives POJO from TinyBase, returns Zod-validated model
508
+ deserialize: (data: any) => {
509
+ return PostZodModel.parse(data); // publishedAt will be Date object from field transform
510
+ }
511
+ }
512
+ }
513
+ });
514
+ ```
515
+
516
+ **Benefits of ZodSchematizer:**
517
+
518
+ - **Automatic Schema Generation**: Convert Zod models directly to TinyBase/MicroStore schemas
519
+ - **Type Consistency**: Ensure your validation schemas match your storage schemas
520
+ - **Reduced Boilerplate**: Less manual schema definition
521
+ - **Schema Evolution**: Update Zod models and regenerate schemas automatically
522
+ - **Validation Integration**: Natural integration between Zod validation and MicroStore storage
523
+
524
+ **Workflow:**
525
+
526
+ 1. Define your domain models using Zod schemas
527
+ 2. Use ZodSchematizer to generate base TinyBase schemas
528
+ 3. Create custom field transforms for complex types (Date, etc.)
529
+ 4. Enhance generated schemas with MicroStore properties (`primaryKey`, `transform`)
530
+ 5. Add record transforms with serialize returning POJOs and deserialize returning validated models
531
+ 6. Create MicroStore instance with enhanced schemas and field transforms
532
+
533
+ ### Custom Interpreters
534
+
535
+ Create custom interpreters for non-standard API formats:
536
+
537
+ ```typescript
538
+ function GraphQLInterpreter(data: any, options: any) {
539
+ // Handle GraphQL responses, JSON:API, or any other format
540
+ return {
541
+ data: [
542
+ {
543
+ type: 'user',
544
+ data: data.data.users
545
+ }
546
+ ],
547
+ meta: data.meta
548
+ };
549
+ }
550
+
551
+ const store = new MicroStore({
552
+ schemas,
553
+ interpreter: GraphQLInterpreter
554
+ });
555
+ ```
556
+
557
+ ## 🎯 Use Cases
558
+
559
+ ### ✅ Perfect For
560
+
561
+ - **React applications with multiple data sources** that need consistent state
562
+ - **Applications fetching the same entities** from different API endpoints
563
+ - **Complex UIs** where the same data appears in multiple components
564
+ - **Real-time applications** that need reactive updates across components or use websockets
565
+ - **Data-heavy applications** that need efficient normalization and deduplication
566
+
567
+ ## 🔗 Integration Examples
568
+
569
+ ### With React Query
570
+
571
+ ```typescript
572
+ function useUsers() {
573
+ const store = useMicroStore();
574
+
575
+ return useQuery({
576
+ queryKey: ['users'],
577
+ queryFn: async () => {
578
+ const response = await api.get('/users');
579
+ store?.pushPayload('GET', response.data);
580
+ return response.data.users;
581
+ }
582
+ });
583
+ }
584
+
585
+ function UserList() {
586
+ const { data } = useUsers();
587
+ const users = useReactive('user', data || []);
588
+ return <div>{/* Render users */}</div>;
589
+ }
590
+ ```
591
+
592
+ ### With SWR
593
+
594
+ ```typescript
595
+ function useUsers() {
596
+ const store = useMicroStore();
597
+
598
+ return useSWR('/api/users', async (url) => {
599
+ const response = await fetch(url);
600
+ const data = await response.json();
601
+ store?.pushPayload('GET', data);
602
+ return data.users;
603
+ });
604
+ }
605
+ ```
606
+
607
+ ### With Ky
608
+
609
+ ```typescript
610
+ const api = ky.create({
611
+ hooks: {
612
+ afterResponse: [
613
+ async (request, _options, response) => {
614
+ const data = await response.json();
615
+ store.pushPayload(request.method, data);
616
+ return response;
617
+ }
618
+ ]
619
+ }
620
+ });
621
+ ```
622
+
623
+ - If you use the `afterResponse` hook in your global `ky` api, you DON'T need to manually push data into your store in your `queryFn`s! The data will automagically be in your `MicroStore` and all you need to do is pass the IDs you want to render to `useReactive` in your components! This is thanks to the `RESTInterpreter` which will digest all of your Ember RESTAdapter compliant responses automatically. If you need to see a server framework that responds with the correct format, checkout [fastapi-cruddy-framework](https://github.com/mdconaway/fastapi-cruddy-framework)
624
+
625
+ ## 📚 API Reference
626
+
627
+ ### MicroStore Methods
628
+
629
+ | Method | Description |
630
+ | ---------------------------------------------- | ------------------------------------- |
631
+ | `pushPayload(method, data, options?)` | Normalize and store API response data |
632
+ | `pushRecord(type, record, method, options?)` | Store a single record |
633
+ | `pushRecords(type, records, method, options?)` | Store multiple records |
634
+ | `peekRecord<T>(type, id)` | Get a single record by ID |
635
+ | `peekAll<T>(type)` | Get all records of a type |
636
+ | `unloadRecord(type, id)` | Remove a record from store |
637
+ | `unloadAll(type)` | Remove all records of a type |
638
+ | `reset()` | Clear entire store |
639
+
640
+ ### Schema Options
641
+
642
+ | Property | Type | Description |
643
+ | ------------- | ----------------------------------- | -------------------------------- |
644
+ | `type` | `'string' \| 'number' \| 'boolean'` | Field data type |
645
+ | `primaryKey?` | `boolean` | Mark field as primary key |
646
+ | `transform?` | `string` | Apply named transform to field |
647
+ | `default?` | `any` | Default value for field |
648
+ | `allowNull?` | `boolean` | Available if using tinybase >= 7 |
649
+
650
+ ## 📋 TODO
651
+
652
+ ### 🔗 TinyBase Relationships Support
653
+
654
+ Add support for [TinyBase Relationships](https://tinybase.org/api/relationships/) to enable automatic relationship management between schemas:
655
+
656
+ - [ ] **Schema Relationship Definitions**: Allow defining relationships directly in schema configuration
657
+ - [ ] **Automatic Relationship Creation**: Auto-generate TinyBase relationships based on schema definitions
658
+ - [ ] **Relationship Queries**: Extend query capabilities to leverage relationships for efficient data access
659
+ - [ ] **Reactive Relationship Hooks**: Create hooks that reactively update when related data changes
660
+ - [ ] **Foreign Key Validation**: Validate and maintain referential integrity across related records
661
+ - [ ] **Cascade Operations**: Support cascade delete/update operations through relationships
662
+
663
+ Example future API:
664
+
665
+ ### One-to-One Relationships (using belongsTo)
666
+
667
+ ```typescript
668
+ const schemas = {
669
+ user: {
670
+ id: { type: 'string', primaryKey: true },
671
+ name: { type: 'string' },
672
+ email: { type: 'string' }
673
+ },
674
+ profile: {
675
+ id: { type: 'string', primaryKey: true },
676
+ userId: { type: 'string' }, // References user.id
677
+ user: { belongsTo: 'user', key: 'userId' } // Virtual relationship field (feeds off of local foreign key)
678
+ bio: { type: 'string' },
679
+ avatar: { type: 'string' }
680
+ }
681
+ } as const;
682
+
683
+ // Future hooks
684
+ const profile = useRelationship(user, 'profile'); // Get user's profile (inverse hasOne)
685
+ const user = useRelationship(profile, 'user'); // Get profile's user (belongsTo)
686
+ ```
687
+
688
+ ### One-to-Many Relationships (belongsTo + hasMany)
689
+
690
+ ```typescript
691
+ const schemas = {
692
+ user: {
693
+ id: { type: 'string', primaryKey: true },
694
+ name: { type: 'string' },
695
+ posts: { hasMany: 'post', inverse: 'user' } // Virtual field for relationship
696
+ },
697
+ post: {
698
+ id: { type: 'string', primaryKey: true },
699
+ title: { type: 'string' },
700
+ content: { type: 'string' },
701
+ userId: { type: 'string' }, // Stores actual foreign key
702
+ user: { belongsTo: 'user', key: 'userId' } // Virtual relationship field (feeds off of local foreign key)
703
+ }
704
+ } as const;
705
+
706
+ // Future hooks
707
+ const posts = useRelationship(user, 'posts'); // Get all posts for a user (hasMany)
708
+ const author = useRelationship(post, 'user'); // Get post's author (belongsTo)
709
+ ```
710
+
711
+ ### Many-to-Many Relationships (through junction model)
712
+
713
+ ```typescript
714
+ const schemas = {
715
+ user: {
716
+ id: { type: 'string', primaryKey: true },
717
+ name: { type: 'string' },
718
+ userTags: { hasMany: 'userTag', inverse: 'user' },
719
+ tags: { hasMany: 'tag', through: 'userTags.tag' } // Through relationship
720
+ },
721
+ tag: {
722
+ id: { type: 'string', primaryKey: true },
723
+ name: { type: 'string' },
724
+ color: { type: 'string' },
725
+ userTags: { hasMany: 'userTag', inverse: 'tag' },
726
+ users: { hasMany: 'user', through: 'userTags.user' } // Through relationship
727
+ },
728
+ userTag: {
729
+ id: { type: 'string', primaryKey: true },
730
+ userId: { type: 'string' },
731
+ tagId: { type: 'string' },
732
+ user: { belongsTo: 'user', key: 'userId' },
733
+ tag: { belongsTo: 'tag', key: 'tagId' },
734
+ createdAt: { type: 'string' } // Junction tables can have additional fields
735
+ }
736
+ } as const;
737
+
738
+ // Future hooks for many-to-many
739
+ const userTags = useRelationship(user, 'tags'); // Get user's tags (through userTags)
740
+ const tagUsers = useRelationship(tag, 'users'); // Get tag's users (through userTags)
741
+ const userTagJunctions = useRelationship(user, 'userTags'); // Get actual junction records
742
+
743
+ // Post tagging example
744
+ const schemas = {
745
+ post: {
746
+ id: { type: 'string', primaryKey: true },
747
+ title: { type: 'string' },
748
+ postTags: { hasMany: 'postTag', inverse: 'post' },
749
+ tags: { hasMany: 'tag', through: 'postTags.tag' }
750
+ },
751
+ tag: {
752
+ id: { type: 'string', primaryKey: true },
753
+ name: { type: 'string' },
754
+ postTags: { hasMany: 'postTag', inverse: 'tag' },
755
+ posts: { hasMany: 'post', through: 'postTags.post' }
756
+ },
757
+ postTag: {
758
+ id: { type: 'string', primaryKey: true },
759
+ postId: { type: 'string' },
760
+ tagId: { type: 'string' },
761
+ post: { belongsTo: 'post', key: 'postId' },
762
+ tag: { belongsTo: 'tag', key: 'tagId' }
763
+ }
764
+ } as const;
765
+
766
+ // Complex many-to-many usage
767
+ const tagsForPost = useRelationship(post, 'tags'); // Get all tags for a post
768
+ const postsForTag = useRelationship(tag, 'posts'); // Get all posts with a tag
769
+ ```
770
+
771
+ ## 🤝 Contributing
772
+
773
+ Contributions are welcome! Please read our [contributing guidelines](./CONTRIBUTING.md) and submit pull requests to our repository.
774
+
775
+ We especially welcome contributions in these areas:
776
+
777
+ - 🧪 **Testing**: Help us add comprehensive test coverage
778
+ - 📖 **Documentation**: Improve examples and API documentation
779
+ - 🔗 **Relationships**: Implement TinyBase relationships support
780
+ - 🎯 **Transformers**: Add more field and record transform types
781
+ - 💡 **Examples**: Create real-world usage examples
782
+
783
+ See our [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines on development workflow, code style, and pull request requirements.
784
+
785
+ ## 📄 License
786
+
787
+ ISC License - see LICENSE file for details.
788
+
789
+ ## 🔗 Related Projects
790
+
791
+ - [TinyBase](https://tinybase.org/) - The reactive data store powering MicroStore
792
+ - [fastapi-cruddy-framework](https://github.com/mdconaway/fastapi-cruddy-framework) - Compatible REST API framework
793
+ - [React Query](https://tanstack.com/query) - Recommended for data fetching
794
+ - [SWR](https://swr.vercel.app/) - Alternative data fetching solution
@@ -0,0 +1,88 @@
1
+ import { CellSchema, Row } from 'tinybase/with-schemas';
2
+ import { Store, Queries, Row as Row$1 } from 'tinybase';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ type CallableWithReturn<T> = (data: any) => T;
7
+ type RecordCallableWithReturn<T> = (data: Record<string, any>) => T;
8
+ type RowCallableWithReturn<T> = (data: Row<any, any>) => T;
9
+ type InterpretableWithReturn<T> = (data: Record<string, any>, options: Record<string, any>) => T;
10
+ type InterpreterTypedBatch = {
11
+ data: Record<string, any>[];
12
+ type: string;
13
+ };
14
+ type InterpreterReturnValue = {
15
+ data: InterpreterTypedBatch[];
16
+ meta: Record<string, any> | undefined;
17
+ };
18
+ type FieldTransform = {
19
+ serialize: CallableWithReturn<any>;
20
+ deserialize: CallableWithReturn<any>;
21
+ };
22
+ type RecordTransform = {
23
+ serialize: RecordCallableWithReturn<any>;
24
+ deserialize: RowCallableWithReturn<any>;
25
+ };
26
+ type MicroStoreCellSchema = CellSchema & {
27
+ transform?: string;
28
+ primaryKey?: boolean;
29
+ };
30
+ type MicrostoreInterpreter = InterpretableWithReturn<InterpreterReturnValue>;
31
+ type ConventionalSchema = Record<string, CellSchema>;
32
+ type ConventionalSchemas = Record<string, ConventionalSchema>;
33
+ type MicroStoreSchema = Record<string, MicroStoreCellSchema>;
34
+ type MicroStoreSchemas = Record<string, MicroStoreSchema>;
35
+ type FieldTransforms = Record<string, FieldTransform>;
36
+ type RecordTransforms = Record<string, RecordTransform>;
37
+ type RawRow = Record<string, any>;
38
+ type RawRecord = Record<any, any>;
39
+ type MethodType = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | string;
40
+ type MicroStoreOptions = {
41
+ schemas: MicroStoreSchemas;
42
+ recordTransforms?: RecordTransforms;
43
+ fieldTransforms?: FieldTransforms;
44
+ interpreter?: MicrostoreInterpreter;
45
+ };
46
+
47
+ declare function RESTInterpreter(data: Record<string, any>, _options: Record<string, any>): InterpreterReturnValue;
48
+
49
+ declare class MicroStore {
50
+ schemas: MicroStoreSchemas;
51
+ fieldTransforms: FieldTransforms;
52
+ recordTransforms: RecordTransforms;
53
+ store: Store;
54
+ queries: Queries;
55
+ interpreter: MicrostoreInterpreter;
56
+ private primaryKeys;
57
+ constructor(options: MicroStoreOptions);
58
+ private transformSchemas;
59
+ getPrimaryKey(type: string): string | undefined;
60
+ getSchema(type: string): MicroStoreSchema | null;
61
+ getRecordTransform(type: string | undefined): RecordTransform | undefined;
62
+ getFieldTransform(type: string | undefined): FieldTransform | undefined;
63
+ serialize(row: RawRow, schema: MicroStoreSchema): RawRow;
64
+ deserialize(row: Row$1, schema: MicroStoreSchema): RawRow;
65
+ pushRecord(type: string, data: RawRecord, method: MethodType, options?: Record<string, any>): InterpreterReturnValue | undefined;
66
+ pushRecords(type: string, data: RawRecord[], method: MethodType, options?: Record<string, any>): InterpreterReturnValue | undefined;
67
+ pushPayload(method: MethodType, data: any, options?: Record<string, any>): InterpreterReturnValue | undefined;
68
+ peekRecord<T>(type: string, id: string): T | undefined;
69
+ peekAll<T>(type: string): T[];
70
+ unloadRecord<T>(type: string, id: string): T | undefined;
71
+ unloadAll(type: string): void;
72
+ reset(): void;
73
+ getStore(): Store;
74
+ getQueries(): Queries;
75
+ }
76
+
77
+ declare function useMicroStore(): MicroStore | null;
78
+ type WithExistingStore = {
79
+ store: MicroStore;
80
+ children: ReactNode;
81
+ };
82
+ declare function MicroStoreProvider(props: WithExistingStore): react_jsx_runtime.JSX.Element;
83
+
84
+ declare function useReactive<T>(type: string, data: T[]): T[];
85
+
86
+ declare const json: FieldTransform;
87
+
88
+ export { type CallableWithReturn, type ConventionalSchema, type ConventionalSchemas, type FieldTransform, type FieldTransforms, type InterpretableWithReturn, type InterpreterReturnValue, type InterpreterTypedBatch, type MethodType, MicroStore, type MicroStoreCellSchema, type MicroStoreOptions, MicroStoreProvider, type MicroStoreSchema, type MicroStoreSchemas, type MicrostoreInterpreter, RESTInterpreter, type RawRecord, type RawRow, type RecordCallableWithReturn, type RecordTransform, type RecordTransforms, type RowCallableWithReturn, json, useMicroStore, useReactive };
@@ -0,0 +1,88 @@
1
+ import { CellSchema, Row } from 'tinybase/with-schemas';
2
+ import { Store, Queries, Row as Row$1 } from 'tinybase';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ type CallableWithReturn<T> = (data: any) => T;
7
+ type RecordCallableWithReturn<T> = (data: Record<string, any>) => T;
8
+ type RowCallableWithReturn<T> = (data: Row<any, any>) => T;
9
+ type InterpretableWithReturn<T> = (data: Record<string, any>, options: Record<string, any>) => T;
10
+ type InterpreterTypedBatch = {
11
+ data: Record<string, any>[];
12
+ type: string;
13
+ };
14
+ type InterpreterReturnValue = {
15
+ data: InterpreterTypedBatch[];
16
+ meta: Record<string, any> | undefined;
17
+ };
18
+ type FieldTransform = {
19
+ serialize: CallableWithReturn<any>;
20
+ deserialize: CallableWithReturn<any>;
21
+ };
22
+ type RecordTransform = {
23
+ serialize: RecordCallableWithReturn<any>;
24
+ deserialize: RowCallableWithReturn<any>;
25
+ };
26
+ type MicroStoreCellSchema = CellSchema & {
27
+ transform?: string;
28
+ primaryKey?: boolean;
29
+ };
30
+ type MicrostoreInterpreter = InterpretableWithReturn<InterpreterReturnValue>;
31
+ type ConventionalSchema = Record<string, CellSchema>;
32
+ type ConventionalSchemas = Record<string, ConventionalSchema>;
33
+ type MicroStoreSchema = Record<string, MicroStoreCellSchema>;
34
+ type MicroStoreSchemas = Record<string, MicroStoreSchema>;
35
+ type FieldTransforms = Record<string, FieldTransform>;
36
+ type RecordTransforms = Record<string, RecordTransform>;
37
+ type RawRow = Record<string, any>;
38
+ type RawRecord = Record<any, any>;
39
+ type MethodType = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | string;
40
+ type MicroStoreOptions = {
41
+ schemas: MicroStoreSchemas;
42
+ recordTransforms?: RecordTransforms;
43
+ fieldTransforms?: FieldTransforms;
44
+ interpreter?: MicrostoreInterpreter;
45
+ };
46
+
47
+ declare function RESTInterpreter(data: Record<string, any>, _options: Record<string, any>): InterpreterReturnValue;
48
+
49
+ declare class MicroStore {
50
+ schemas: MicroStoreSchemas;
51
+ fieldTransforms: FieldTransforms;
52
+ recordTransforms: RecordTransforms;
53
+ store: Store;
54
+ queries: Queries;
55
+ interpreter: MicrostoreInterpreter;
56
+ private primaryKeys;
57
+ constructor(options: MicroStoreOptions);
58
+ private transformSchemas;
59
+ getPrimaryKey(type: string): string | undefined;
60
+ getSchema(type: string): MicroStoreSchema | null;
61
+ getRecordTransform(type: string | undefined): RecordTransform | undefined;
62
+ getFieldTransform(type: string | undefined): FieldTransform | undefined;
63
+ serialize(row: RawRow, schema: MicroStoreSchema): RawRow;
64
+ deserialize(row: Row$1, schema: MicroStoreSchema): RawRow;
65
+ pushRecord(type: string, data: RawRecord, method: MethodType, options?: Record<string, any>): InterpreterReturnValue | undefined;
66
+ pushRecords(type: string, data: RawRecord[], method: MethodType, options?: Record<string, any>): InterpreterReturnValue | undefined;
67
+ pushPayload(method: MethodType, data: any, options?: Record<string, any>): InterpreterReturnValue | undefined;
68
+ peekRecord<T>(type: string, id: string): T | undefined;
69
+ peekAll<T>(type: string): T[];
70
+ unloadRecord<T>(type: string, id: string): T | undefined;
71
+ unloadAll(type: string): void;
72
+ reset(): void;
73
+ getStore(): Store;
74
+ getQueries(): Queries;
75
+ }
76
+
77
+ declare function useMicroStore(): MicroStore | null;
78
+ type WithExistingStore = {
79
+ store: MicroStore;
80
+ children: ReactNode;
81
+ };
82
+ declare function MicroStoreProvider(props: WithExistingStore): react_jsx_runtime.JSX.Element;
83
+
84
+ declare function useReactive<T>(type: string, data: T[]): T[];
85
+
86
+ declare const json: FieldTransform;
87
+
88
+ export { type CallableWithReturn, type ConventionalSchema, type ConventionalSchemas, type FieldTransform, type FieldTransforms, type InterpretableWithReturn, type InterpreterReturnValue, type InterpreterTypedBatch, type MethodType, MicroStore, type MicroStoreCellSchema, type MicroStoreOptions, MicroStoreProvider, type MicroStoreSchema, type MicroStoreSchemas, type MicrostoreInterpreter, RESTInterpreter, type RawRecord, type RawRow, type RecordCallableWithReturn, type RecordTransform, type RecordTransforms, type RowCallableWithReturn, json, useMicroStore, useReactive };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var emberInflector=require('ember-inflector'),lodashEs=require('lodash-es'),tinybase=require('tinybase'),react=require('react'),uiReact=require('tinybase/ui-react'),jsxRuntime=require('react/jsx-runtime');var K=Object.defineProperty;var z=(o,e,r)=>e in o?K(o,e,{enumerable:true,configurable:true,writable:true,value:r}):o[e]=r;var f=(o,e,r)=>z(o,typeof e!="symbol"?e+"":e,r);function l(o,e){let r={data:[],meta:void 0},s;return o!==void 0&&Object.keys(o).forEach(t=>{let i=o[t];if(t!=="meta"){let n=[],c=lodashEs.camelCase(emberInflector.singularize(t));lodashEs.isArray(i)?i.forEach(a=>{n.push(a);}):n.push(i),r.data.push({data:n,type:c});}else s=o[t];}),r.meta=s,r}var p={serialize(o){return o?JSON.stringify(o):null},deserialize(o){return o?JSON.parse(o):null}};var N=["transform","primaryKey"],q={json:p},y=class{constructor(e){f(this,"schemas");f(this,"fieldTransforms");f(this,"recordTransforms");f(this,"store");f(this,"queries");f(this,"interpreter");f(this,"primaryKeys");this.primaryKeys={},this.schemas=e.schemas?e.schemas:{},this.fieldTransforms={...e.fieldTransforms?e.fieldTransforms:{},...q},this.recordTransforms=e.recordTransforms?e.recordTransforms:{},this.interpreter=e.interpreter?e.interpreter:l,this.store=tinybase.createStore(),this.store.setTablesSchema(this.transformSchemas()),this.queries=tinybase.createQueries(this.store);}transformSchemas(){let e={};return Object.keys(this.schemas).forEach(r=>{if(e[r]={},Object.keys(this.schemas[r]).forEach(s=>{if(e[r][s]=lodashEs.omit(this.schemas[r][s],N),this.schemas[r][s].primaryKey&&this.schemas[r][s].type==="string"){if(this.primaryKeys[r])throw Error(`More than one primary key defined for schema ${r}`);this.primaryKeys[r]=s;}}),!this.getPrimaryKey(r)&&this.schemas[r].id&&this.schemas[r].id.type==="string"&&(this.primaryKeys[r]="id"),!this.getPrimaryKey(r))throw Error(`No primary key defined for schema ${r}. This schema must have an 'id' field of "type": "string", or another field of "type": "string" with "primaryKey": true.`)}),e}getPrimaryKey(e){let r=this.primaryKeys[e];return r||void 0}getSchema(e){return e&&Object.hasOwn(this.schemas,e)?this.schemas[e]:null}getRecordTransform(e){if(e)return this.recordTransforms[e]?this.recordTransforms[e]:void 0}getFieldTransform(e){if(e)return this.fieldTransforms[e]?this.fieldTransforms[e]:void 0}serialize(e,r){let s={};return Object.keys(e).forEach(t=>{let i=r[t];if(i){let n=this.getFieldTransform(i.transform);s[t]=n?n.serialize(e[t]):e[t];}}),s}deserialize(e,r){let s={};return Object.keys(e).forEach(t=>{let i=r[t];if(i){let n=i.transform&&this.fieldTransforms[i.transform]?this.fieldTransforms[i.transform]:void 0;s[t]=n?n.deserialize(e[t]):e[t];}}),s}pushRecord(e,r,s,t={}){return this.pushRecords(e,[r],s,t)}pushRecords(e,r,s,t={}){let i=this.getRecordTransform(e),n=lodashEs.camelCase(emberInflector.pluralize(e));if(!i)return this.pushPayload(s,{[n]:r},t);let c=r.map(a=>i.serialize(a));return this.pushPayload(s,{[n]:c},t)}pushPayload(e,r,s={}){try{let t=this.interpreter(r,s);return t.data.forEach(i=>{let n=this.getSchema(i.type);if(n){let c=this.getPrimaryKey(i.type)||"id";e==="DELETE"?i.data.forEach(a=>{this.store.delRow(i.type,a[c]);}):e==="PATCH"?i.data.forEach(a=>{this.store.getRow(i.type,a[c])?this.store.setPartialRow(i.type,a[c],this.serialize(a,n)):this.store.setRow(i.type,a[c],this.serialize(a,n));}):i.data.forEach(a=>{this.store.setRow(i.type,a[c],this.serialize(a,n));});}}),t}catch(t){console.warn(t);}}peekRecord(e,r){let s=this.getSchema(e),t=this.getPrimaryKey(e),i=this.getRecordTransform(e),n=i?i.deserialize:c=>c;if(s&&t){let c=this.getStore().getRow(e,r);if(c)return n(this.deserialize(c,s))}}peekAll(e){let r=this.getSchema(e),s=this.getPrimaryKey(e),t=this.getRecordTransform(e),i=t?t.deserialize:n=>n;if(r&&s){let n=this.getStore().getTable(e);return Object.entries(n).map(([c,a])=>i(this.deserialize(a,r)))}return []}unloadRecord(e,r){let s=this.peekRecord(e,r);return s&&this.getStore().delRow(e,r),s}unloadAll(e){this.getSchema(e)&&this.getStore().delTable(e);}reset(){Object.entries(this.schemas).forEach(([e,r])=>{this.unloadAll(e);});}getStore(){return this.store}getQueries(){return this.queries}};var w=react.createContext(null);function g(){return react.useContext(w)}function J(o){let e=react.useMemo(()=>o.store,[o]);return jsxRuntime.jsx(w.Provider,{value:e,children:jsxRuntime.jsx(uiReact.Provider,{store:e.getStore(),queries:e.getQueries(),children:o.children})})}function U(o,e){return o?o.map(r=>r[e]):[]}function G(o,e,r,s,t,i){t.setQueryDefinition(s,o,({select:n,where:c})=>{Object.keys(e).forEach(a=>{n(a);}),c(a=>i.includes(a(r)));});}function X(o,e){let r=g(),s=r?.getSchema(o),t=r?.getPrimaryKey(o);if(!s||!t)throw new Error(`No MicroStore schema defined for type ${o}`);let[i,n]=react.useState(crypto.randomUUID()),c=uiReact.useQueries(),a=uiReact.useResultTable(i,c),[d,E]=react.useState(""),u=react.useRef("");react.useEffect(()=>{if(c){let m=U(e,t),h=JSON.stringify(m);d!==h&&u.current!==h&&(Object.entries(a).length>0&&(u.current=h),E(h),G(o,s,t,i,c,m));}},[e,t,s,o,c,i,d,a,u]);let R=r?.getRecordTransform(o),P=R?R.deserialize:m=>m,S=[];return e.forEach(m=>{let h=m[t],T=a[h];if(T){let v=r?.deserialize(T,s);S.push(P(v));}}),S}exports.MicroStore=y;exports.MicroStoreProvider=J;exports.RESTInterpreter=l;exports.json=p;exports.useMicroStore=g;exports.useReactive=X;//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interpreter.ts","../src/transforms.ts","../src/microstore.ts","../src/provider.tsx","../src/reactive.ts"],"names":["RESTInterpreter","data","_options","typedPayload","meta","k","content","typedData","typeName","camelCase","singularize","isArray","item","json","microStoreReservedKeys","defaultFieldTransforms","MicroStore","options","__publicField","createStore","createQueries","transformedSchemas","k2","omit","type","possiblePK","row","schema","serializedRow","field","transform","deserializedRow","method","typeKey","pluralize","rawData","batchedPayload","payload","pk","e","id","transformer","effectiveTransformer","x","table","_","record","schemaName","StoreContext","createContext","useMicroStore","useContext","MicroStoreProvider","props","store","useMemo","jsx","TinyBaseProvider","generateFilter","obj","resetQueryDefinition","queryName","queries","filter","select","where","getCell","useReactive","uniqueHookID","useState","useQueries","rows","useResultTable","lastFilter","setLastFilter","airBrake","useRef","useEffect","stringifiedFilter","returnData","identifier","possibleRow","thing"],"mappings":"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","file":"index.js","sourcesContent":["// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { singularize } from 'ember-inflector';\nimport { camelCase, isArray } from 'lodash-es';\nimport type { InterpreterReturnValue } from './types';\n\nexport function RESTInterpreter(data: Record<string, any>, _options: Record<string, any>) {\n const typedPayload: InterpreterReturnValue = {\n data: [],\n meta: undefined\n };\n let meta: Record<string, any> | undefined = undefined;\n\n if (data !== undefined) {\n Object.keys(data).forEach((k) => {\n const content = data[k];\n if (k !== 'meta') {\n const typedData: Record<string, any>[] = [];\n const typeName = camelCase(singularize(k));\n if (isArray(content)) {\n content.forEach((item: any) => {\n typedData.push(item);\n });\n } else {\n typedData.push(content);\n }\n typedPayload.data.push({\n data: typedData,\n type: typeName\n });\n } else {\n meta = data[k];\n }\n });\n }\n typedPayload.meta = meta;\n return typedPayload;\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport type { FieldTransform } from './types';\n\nexport const json: FieldTransform = {\n serialize(data) {\n return data ? JSON.stringify(data) : null;\n },\n deserialize(data) {\n return data ? JSON.parse(data) : null;\n }\n};\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { pluralize } from 'ember-inflector';\nimport { camelCase, omit } from 'lodash-es';\nimport { createQueries, createStore, type CellSchema, type Queries, type Row, type Store } from 'tinybase';\nimport { RESTInterpreter } from './interpreter';\nimport { json } from './transforms';\nimport type {\n ConventionalSchema,\n ConventionalSchemas,\n FieldTransforms,\n MethodType,\n MicrostoreInterpreter,\n MicroStoreOptions,\n MicroStoreSchema,\n MicroStoreSchemas,\n RawRecord,\n RawRow,\n RecordTransforms\n} from './types';\n\nconst microStoreReservedKeys = ['transform', 'primaryKey'];\nconst defaultFieldTransforms = {\n json\n};\n\nexport class MicroStore {\n schemas: MicroStoreSchemas;\n fieldTransforms: FieldTransforms;\n recordTransforms: RecordTransforms;\n store: Store;\n queries: Queries;\n interpreter: MicrostoreInterpreter;\n private primaryKeys: Record<string, string>;\n\n constructor(options: MicroStoreOptions) {\n this.primaryKeys = {};\n this.schemas = options.schemas ? options.schemas : {};\n // Thats right, we even support our own field and record level transforms!\n this.fieldTransforms = { ...(options.fieldTransforms ? options.fieldTransforms : {}), ...defaultFieldTransforms };\n this.recordTransforms = options.recordTransforms ? options.recordTransforms : {};\n this.interpreter = options.interpreter ? options.interpreter : RESTInterpreter;\n this.store = createStore();\n this.store.setTablesSchema(this.transformSchemas());\n this.queries = createQueries(this.store);\n }\n\n private transformSchemas() {\n // we have to remove custom properties or tinybase\n // will ignore our fields.\n const transformedSchemas: ConventionalSchemas = {};\n Object.keys(this.schemas).forEach((k) => {\n transformedSchemas[k] = <ConventionalSchema>{};\n Object.keys(this.schemas[k]).forEach((k2) => {\n transformedSchemas[k][k2] = omit(this.schemas[k][k2], microStoreReservedKeys) as CellSchema;\n const possiblePK: boolean | undefined = this.schemas[k][k2]['primaryKey'];\n if (possiblePK && this.schemas[k][k2]['type'] === 'string') {\n if (this.primaryKeys[k]) {\n throw Error(`More than one primary key defined for schema ${k}`);\n }\n this.primaryKeys[k] = k2;\n }\n });\n // Default PK to id\n if (!this.getPrimaryKey(k) && this.schemas[k]['id'] && this.schemas[k]['id']['type'] === 'string') {\n this.primaryKeys[k] = 'id';\n }\n if (!this.getPrimaryKey(k)) {\n throw Error(\n `No primary key defined for schema ${k}. This schema must have an 'id' field of \"type\": \"string\", or another field of \"type\": \"string\" with \"primaryKey\": true.`\n );\n }\n });\n return transformedSchemas;\n }\n\n getPrimaryKey(type: string) {\n const possiblePK = this.primaryKeys[type];\n return possiblePK ? possiblePK : undefined;\n }\n\n getSchema(type: string) {\n if (type) {\n if (Object.hasOwn(this.schemas, type)) {\n return this.schemas[type];\n }\n }\n return null;\n }\n\n getRecordTransform(type: string | undefined) {\n if (type) {\n return this.recordTransforms[type] ? this.recordTransforms[type] : undefined;\n }\n return undefined;\n }\n\n getFieldTransform(type: string | undefined) {\n if (type) {\n return this.fieldTransforms[type] ? this.fieldTransforms[type] : undefined;\n }\n return undefined;\n }\n\n serialize(row: RawRow, schema: MicroStoreSchema) {\n const serializedRow: RawRow = {};\n Object.keys(row).forEach((k) => {\n const field = schema[k];\n if (field) {\n const transform = this.getFieldTransform(field.transform);\n serializedRow[k] = transform ? transform.serialize(row[k]) : row[k];\n }\n });\n return serializedRow;\n }\n\n deserialize(row: Row, schema: MicroStoreSchema) {\n const deserializedRow: RawRow = {};\n Object.keys(row).forEach((k) => {\n const field = schema[k];\n if (field) {\n const transform =\n field.transform && this.fieldTransforms[field.transform] ? this.fieldTransforms[field.transform] : undefined;\n deserializedRow[k] = transform ? transform.deserialize(row[k]) : row[k];\n }\n });\n return deserializedRow;\n }\n\n // pushRecord is a sugar method to take a single record and push it into the store using whatever verb was used\n pushRecord(type: string, data: RawRecord, method: MethodType, options: Record<string, any> = {}) {\n return this.pushRecords(type, [data], method, options);\n }\n\n // pushRecords handles an incoming data (\"row\"s) that are in application level format (with dates, blobs, or other complex types that can't be POJO'd)\n pushRecords(type: string, data: RawRecord[], method: MethodType, options: Record<string, any> = {}) {\n const transform = this.getRecordTransform(type);\n const typeKey = camelCase(pluralize(type));\n if (!transform) {\n return this.pushPayload(method, { [typeKey]: data }, options);\n }\n const rawData = data.map((item) => {\n return transform.serialize(item);\n });\n return this.pushPayload(method, { [typeKey]: rawData }, options);\n }\n\n // pushPayload handles incoming data (many \"row\"s) that are in POJO format\n pushPayload(method: MethodType, data: any, options: Record<string, any> = {}) {\n try {\n const batchedPayload = this.interpreter(data, options);\n batchedPayload.data.forEach((payload) => {\n const schema = this.getSchema(payload.type);\n // Only process typed batches that match a defined data schema\n if (schema) {\n const pk = this.getPrimaryKey(payload.type) || 'id';\n if (method === 'DELETE') {\n payload.data.forEach((row) => {\n this.store.delRow(payload.type, row[pk]);\n });\n } else if (method === 'PATCH') {\n payload.data.forEach((row) => {\n // Only try a partial row update if a row already exists\n if (this.store.getRow(payload.type, row[pk])) {\n this.store.setPartialRow(payload.type, row[pk], this.serialize(row, schema));\n } else {\n this.store.setRow(payload.type, row[pk], this.serialize(row, schema));\n }\n });\n } else {\n payload.data.forEach((row) => {\n this.store.setRow(payload.type, row[pk], this.serialize(row, schema));\n });\n }\n }\n });\n return batchedPayload;\n } catch (e) {\n console.warn(e);\n }\n }\n\n peekRecord<T>(type: string, id: string): T | undefined {\n const schema = this.getSchema(type);\n const pk = this.getPrimaryKey(type);\n const transformer = this.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n if (schema && pk) {\n const row = this.getStore().getRow(type, id);\n if (row) {\n return effectiveTransformer(this.deserialize(row, schema));\n }\n }\n return undefined;\n }\n\n peekAll<T>(type: string): T[] {\n const schema = this.getSchema(type);\n const pk = this.getPrimaryKey(type);\n const transformer = this.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n if (schema && pk) {\n const table = this.getStore().getTable(type);\n return Object.entries(table).map(([_, row]) => {\n const thing = effectiveTransformer(this.deserialize(row, schema));\n return thing as T;\n });\n }\n return [];\n }\n\n unloadRecord<T>(type: string, id: string): T | undefined {\n const record = this.peekRecord<T>(type, id);\n if (record) {\n this.getStore().delRow(type, id);\n }\n return record;\n }\n\n unloadAll(type: string) {\n if (this.getSchema(type)) {\n this.getStore().delTable(type);\n }\n }\n\n reset() {\n Object.entries(this.schemas).forEach(([schemaName, _]) => {\n this.unloadAll(schemaName);\n });\n }\n\n // Need to get the raw tinybase store? use this method\n getStore() {\n return this.store;\n }\n\n // Need to get the raw tinybase query views? use this method\n getQueries() {\n return this.queries;\n }\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { Provider as TinyBaseProvider } from 'tinybase/ui-react';\nimport type { MicroStore } from './microstore';\n\nconst StoreContext = createContext<MicroStore | null>(null);\n\nexport function useMicroStore(): MicroStore | null {\n const store = useContext(StoreContext);\n\n return store;\n}\n\ntype WithExistingStore = { store: MicroStore; children: ReactNode };\n\nexport function MicroStoreProvider(props: WithExistingStore) {\n const store = useMemo(() => props.store, [props]);\n\n // TODO: Add store.relationships, which can be passed to tinybase\n return (\n <StoreContext.Provider value={store}>\n <TinyBaseProvider store={store.getStore()} queries={store.getQueries()}>\n {props.children}\n </TinyBaseProvider>\n </StoreContext.Provider>\n );\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\n// This whole file will get \"genericized\" so that it can wrap any useQuery that we currently use\n// Right now it is hard coded to do object stuff...\n// The same logic can be made more abstract and receive a couple more\n// arguments to then handle ANYTHING. :)\nimport { useEffect, useRef, useState } from 'react';\nimport { type Queries } from 'tinybase';\nimport { useQueries, useResultTable } from 'tinybase/ui-react';\nimport { useMicroStore } from './provider';\nimport type { MicroStoreSchema } from './types';\n\nfunction generateFilter(data: any[] | undefined, pk: string) {\n return data ? data.map((obj) => obj[pk]) : [];\n}\n\nfunction resetQueryDefinition(\n type: string,\n schema: MicroStoreSchema,\n pk: string,\n queryName: string,\n queries: Queries,\n filter: string[]\n) {\n // So what exactly does this do? It returns all the same tinybase rows that are\n // returned by the same React query being referenced by this reactive wrapper!!\n // That way, when the rows from useResultTable change due to some localized store\n // update through the MicroStore engine, we see those changes reflected without\n // additional server traffic!\n queries.setQueryDefinition(queryName, type, ({ select, where }) => {\n Object.keys(schema).forEach((k) => {\n select(k);\n });\n where((getCell) => filter.includes(<string>getCell(pk)));\n });\n}\n\n// Wrap the objects in tinybase rows to make them react at a record level\n// to individual record changes\nexport function useReactive<T>(type: string, data: T[]): T[] {\n const store = useMicroStore();\n const schema = store?.getSchema(type);\n const pk = store?.getPrimaryKey(type);\n if (!schema || !pk) {\n throw new Error(`No MicroStore schema defined for type ${type}`);\n }\n const [uniqueHookID, _] = useState(crypto.randomUUID());\n const queries = useQueries();\n const rows = useResultTable(uniqueHookID, queries);\n const [lastFilter, setLastFilter] = useState('');\n // Air Brake prevents render loops if a user does something very silly like chasing\n // records in a circle\n const airBrake = useRef('');\n\n // Why is this effect important? Because you ONLY want to update the thing\n // that is bubbling out new rows (the query) if the supporting REST request\n // with its own internal IDs has changed. We\n useEffect(() => {\n if (queries) {\n const filter = generateFilter(data, pk);\n const stringifiedFilter = JSON.stringify(filter);\n if (lastFilter !== stringifiedFilter && airBrake.current !== stringifiedFilter) {\n if (Object.entries(rows).length > 0) {\n airBrake.current = stringifiedFilter;\n }\n setLastFilter(stringifiedFilter);\n resetQueryDefinition(type, schema, pk, uniqueHookID, queries, filter);\n }\n }\n }, [data, pk, schema, type, queries, uniqueHookID, lastFilter, rows, airBrake]);\n\n const transformer = store?.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n const returnData: T[] = [];\n data.forEach((item: any) => {\n const identifier = item[pk];\n const possibleRow = rows[identifier];\n if (possibleRow) {\n const thing: any = store?.deserialize(possibleRow, schema);\n returnData.push(effectiveTransformer(thing) as T);\n }\n });\n return returnData;\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import {singularize,pluralize}from'ember-inflector';import {camelCase,isArray,omit}from'lodash-es';import {createStore,createQueries}from'tinybase';import {createContext,useContext,useMemo,useState,useRef,useEffect}from'react';import {Provider,useQueries,useResultTable}from'tinybase/ui-react';import {jsx}from'react/jsx-runtime';var K=Object.defineProperty;var z=(o,e,r)=>e in o?K(o,e,{enumerable:true,configurable:true,writable:true,value:r}):o[e]=r;var f=(o,e,r)=>z(o,typeof e!="symbol"?e+"":e,r);function l(o,e){let r={data:[],meta:void 0},s;return o!==void 0&&Object.keys(o).forEach(t=>{let i=o[t];if(t!=="meta"){let n=[],c=camelCase(singularize(t));isArray(i)?i.forEach(a=>{n.push(a);}):n.push(i),r.data.push({data:n,type:c});}else s=o[t];}),r.meta=s,r}var p={serialize(o){return o?JSON.stringify(o):null},deserialize(o){return o?JSON.parse(o):null}};var N=["transform","primaryKey"],q={json:p},y=class{constructor(e){f(this,"schemas");f(this,"fieldTransforms");f(this,"recordTransforms");f(this,"store");f(this,"queries");f(this,"interpreter");f(this,"primaryKeys");this.primaryKeys={},this.schemas=e.schemas?e.schemas:{},this.fieldTransforms={...e.fieldTransforms?e.fieldTransforms:{},...q},this.recordTransforms=e.recordTransforms?e.recordTransforms:{},this.interpreter=e.interpreter?e.interpreter:l,this.store=createStore(),this.store.setTablesSchema(this.transformSchemas()),this.queries=createQueries(this.store);}transformSchemas(){let e={};return Object.keys(this.schemas).forEach(r=>{if(e[r]={},Object.keys(this.schemas[r]).forEach(s=>{if(e[r][s]=omit(this.schemas[r][s],N),this.schemas[r][s].primaryKey&&this.schemas[r][s].type==="string"){if(this.primaryKeys[r])throw Error(`More than one primary key defined for schema ${r}`);this.primaryKeys[r]=s;}}),!this.getPrimaryKey(r)&&this.schemas[r].id&&this.schemas[r].id.type==="string"&&(this.primaryKeys[r]="id"),!this.getPrimaryKey(r))throw Error(`No primary key defined for schema ${r}. This schema must have an 'id' field of "type": "string", or another field of "type": "string" with "primaryKey": true.`)}),e}getPrimaryKey(e){let r=this.primaryKeys[e];return r||void 0}getSchema(e){return e&&Object.hasOwn(this.schemas,e)?this.schemas[e]:null}getRecordTransform(e){if(e)return this.recordTransforms[e]?this.recordTransforms[e]:void 0}getFieldTransform(e){if(e)return this.fieldTransforms[e]?this.fieldTransforms[e]:void 0}serialize(e,r){let s={};return Object.keys(e).forEach(t=>{let i=r[t];if(i){let n=this.getFieldTransform(i.transform);s[t]=n?n.serialize(e[t]):e[t];}}),s}deserialize(e,r){let s={};return Object.keys(e).forEach(t=>{let i=r[t];if(i){let n=i.transform&&this.fieldTransforms[i.transform]?this.fieldTransforms[i.transform]:void 0;s[t]=n?n.deserialize(e[t]):e[t];}}),s}pushRecord(e,r,s,t={}){return this.pushRecords(e,[r],s,t)}pushRecords(e,r,s,t={}){let i=this.getRecordTransform(e),n=camelCase(pluralize(e));if(!i)return this.pushPayload(s,{[n]:r},t);let c=r.map(a=>i.serialize(a));return this.pushPayload(s,{[n]:c},t)}pushPayload(e,r,s={}){try{let t=this.interpreter(r,s);return t.data.forEach(i=>{let n=this.getSchema(i.type);if(n){let c=this.getPrimaryKey(i.type)||"id";e==="DELETE"?i.data.forEach(a=>{this.store.delRow(i.type,a[c]);}):e==="PATCH"?i.data.forEach(a=>{this.store.getRow(i.type,a[c])?this.store.setPartialRow(i.type,a[c],this.serialize(a,n)):this.store.setRow(i.type,a[c],this.serialize(a,n));}):i.data.forEach(a=>{this.store.setRow(i.type,a[c],this.serialize(a,n));});}}),t}catch(t){console.warn(t);}}peekRecord(e,r){let s=this.getSchema(e),t=this.getPrimaryKey(e),i=this.getRecordTransform(e),n=i?i.deserialize:c=>c;if(s&&t){let c=this.getStore().getRow(e,r);if(c)return n(this.deserialize(c,s))}}peekAll(e){let r=this.getSchema(e),s=this.getPrimaryKey(e),t=this.getRecordTransform(e),i=t?t.deserialize:n=>n;if(r&&s){let n=this.getStore().getTable(e);return Object.entries(n).map(([c,a])=>i(this.deserialize(a,r)))}return []}unloadRecord(e,r){let s=this.peekRecord(e,r);return s&&this.getStore().delRow(e,r),s}unloadAll(e){this.getSchema(e)&&this.getStore().delTable(e);}reset(){Object.entries(this.schemas).forEach(([e,r])=>{this.unloadAll(e);});}getStore(){return this.store}getQueries(){return this.queries}};var w=createContext(null);function g(){return useContext(w)}function J(o){let e=useMemo(()=>o.store,[o]);return jsx(w.Provider,{value:e,children:jsx(Provider,{store:e.getStore(),queries:e.getQueries(),children:o.children})})}function U(o,e){return o?o.map(r=>r[e]):[]}function G(o,e,r,s,t,i){t.setQueryDefinition(s,o,({select:n,where:c})=>{Object.keys(e).forEach(a=>{n(a);}),c(a=>i.includes(a(r)));});}function X(o,e){let r=g(),s=r?.getSchema(o),t=r?.getPrimaryKey(o);if(!s||!t)throw new Error(`No MicroStore schema defined for type ${o}`);let[i,n]=useState(crypto.randomUUID()),c=useQueries(),a=useResultTable(i,c),[d,E]=useState(""),u=useRef("");useEffect(()=>{if(c){let m=U(e,t),h=JSON.stringify(m);d!==h&&u.current!==h&&(Object.entries(a).length>0&&(u.current=h),E(h),G(o,s,t,i,c,m));}},[e,t,s,o,c,i,d,a,u]);let R=r?.getRecordTransform(o),P=R?R.deserialize:m=>m,S=[];return e.forEach(m=>{let h=m[t],T=a[h];if(T){let v=r?.deserialize(T,s);S.push(P(v));}}),S}export{y as MicroStore,J as MicroStoreProvider,l as RESTInterpreter,p as json,g as useMicroStore,X as useReactive};//# sourceMappingURL=index.mjs.map
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interpreter.ts","../src/transforms.ts","../src/microstore.ts","../src/provider.tsx","../src/reactive.ts"],"names":["RESTInterpreter","data","_options","typedPayload","meta","k","content","typedData","typeName","camelCase","singularize","isArray","item","json","microStoreReservedKeys","defaultFieldTransforms","MicroStore","options","__publicField","createStore","createQueries","transformedSchemas","k2","omit","type","possiblePK","row","schema","serializedRow","field","transform","deserializedRow","method","typeKey","pluralize","rawData","batchedPayload","payload","pk","e","id","transformer","effectiveTransformer","x","table","_","record","schemaName","StoreContext","createContext","useMicroStore","useContext","MicroStoreProvider","props","store","useMemo","jsx","TinyBaseProvider","generateFilter","obj","resetQueryDefinition","queryName","queries","filter","select","where","getCell","useReactive","uniqueHookID","useState","useQueries","rows","useResultTable","lastFilter","setLastFilter","airBrake","useRef","useEffect","stringifiedFilter","returnData","identifier","possibleRow","thing"],"mappings":"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","file":"index.mjs","sourcesContent":["// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { singularize } from 'ember-inflector';\nimport { camelCase, isArray } from 'lodash-es';\nimport type { InterpreterReturnValue } from './types';\n\nexport function RESTInterpreter(data: Record<string, any>, _options: Record<string, any>) {\n const typedPayload: InterpreterReturnValue = {\n data: [],\n meta: undefined\n };\n let meta: Record<string, any> | undefined = undefined;\n\n if (data !== undefined) {\n Object.keys(data).forEach((k) => {\n const content = data[k];\n if (k !== 'meta') {\n const typedData: Record<string, any>[] = [];\n const typeName = camelCase(singularize(k));\n if (isArray(content)) {\n content.forEach((item: any) => {\n typedData.push(item);\n });\n } else {\n typedData.push(content);\n }\n typedPayload.data.push({\n data: typedData,\n type: typeName\n });\n } else {\n meta = data[k];\n }\n });\n }\n typedPayload.meta = meta;\n return typedPayload;\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport type { FieldTransform } from './types';\n\nexport const json: FieldTransform = {\n serialize(data) {\n return data ? JSON.stringify(data) : null;\n },\n deserialize(data) {\n return data ? JSON.parse(data) : null;\n }\n};\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { pluralize } from 'ember-inflector';\nimport { camelCase, omit } from 'lodash-es';\nimport { createQueries, createStore, type CellSchema, type Queries, type Row, type Store } from 'tinybase';\nimport { RESTInterpreter } from './interpreter';\nimport { json } from './transforms';\nimport type {\n ConventionalSchema,\n ConventionalSchemas,\n FieldTransforms,\n MethodType,\n MicrostoreInterpreter,\n MicroStoreOptions,\n MicroStoreSchema,\n MicroStoreSchemas,\n RawRecord,\n RawRow,\n RecordTransforms\n} from './types';\n\nconst microStoreReservedKeys = ['transform', 'primaryKey'];\nconst defaultFieldTransforms = {\n json\n};\n\nexport class MicroStore {\n schemas: MicroStoreSchemas;\n fieldTransforms: FieldTransforms;\n recordTransforms: RecordTransforms;\n store: Store;\n queries: Queries;\n interpreter: MicrostoreInterpreter;\n private primaryKeys: Record<string, string>;\n\n constructor(options: MicroStoreOptions) {\n this.primaryKeys = {};\n this.schemas = options.schemas ? options.schemas : {};\n // Thats right, we even support our own field and record level transforms!\n this.fieldTransforms = { ...(options.fieldTransforms ? options.fieldTransforms : {}), ...defaultFieldTransforms };\n this.recordTransforms = options.recordTransforms ? options.recordTransforms : {};\n this.interpreter = options.interpreter ? options.interpreter : RESTInterpreter;\n this.store = createStore();\n this.store.setTablesSchema(this.transformSchemas());\n this.queries = createQueries(this.store);\n }\n\n private transformSchemas() {\n // we have to remove custom properties or tinybase\n // will ignore our fields.\n const transformedSchemas: ConventionalSchemas = {};\n Object.keys(this.schemas).forEach((k) => {\n transformedSchemas[k] = <ConventionalSchema>{};\n Object.keys(this.schemas[k]).forEach((k2) => {\n transformedSchemas[k][k2] = omit(this.schemas[k][k2], microStoreReservedKeys) as CellSchema;\n const possiblePK: boolean | undefined = this.schemas[k][k2]['primaryKey'];\n if (possiblePK && this.schemas[k][k2]['type'] === 'string') {\n if (this.primaryKeys[k]) {\n throw Error(`More than one primary key defined for schema ${k}`);\n }\n this.primaryKeys[k] = k2;\n }\n });\n // Default PK to id\n if (!this.getPrimaryKey(k) && this.schemas[k]['id'] && this.schemas[k]['id']['type'] === 'string') {\n this.primaryKeys[k] = 'id';\n }\n if (!this.getPrimaryKey(k)) {\n throw Error(\n `No primary key defined for schema ${k}. This schema must have an 'id' field of \"type\": \"string\", or another field of \"type\": \"string\" with \"primaryKey\": true.`\n );\n }\n });\n return transformedSchemas;\n }\n\n getPrimaryKey(type: string) {\n const possiblePK = this.primaryKeys[type];\n return possiblePK ? possiblePK : undefined;\n }\n\n getSchema(type: string) {\n if (type) {\n if (Object.hasOwn(this.schemas, type)) {\n return this.schemas[type];\n }\n }\n return null;\n }\n\n getRecordTransform(type: string | undefined) {\n if (type) {\n return this.recordTransforms[type] ? this.recordTransforms[type] : undefined;\n }\n return undefined;\n }\n\n getFieldTransform(type: string | undefined) {\n if (type) {\n return this.fieldTransforms[type] ? this.fieldTransforms[type] : undefined;\n }\n return undefined;\n }\n\n serialize(row: RawRow, schema: MicroStoreSchema) {\n const serializedRow: RawRow = {};\n Object.keys(row).forEach((k) => {\n const field = schema[k];\n if (field) {\n const transform = this.getFieldTransform(field.transform);\n serializedRow[k] = transform ? transform.serialize(row[k]) : row[k];\n }\n });\n return serializedRow;\n }\n\n deserialize(row: Row, schema: MicroStoreSchema) {\n const deserializedRow: RawRow = {};\n Object.keys(row).forEach((k) => {\n const field = schema[k];\n if (field) {\n const transform =\n field.transform && this.fieldTransforms[field.transform] ? this.fieldTransforms[field.transform] : undefined;\n deserializedRow[k] = transform ? transform.deserialize(row[k]) : row[k];\n }\n });\n return deserializedRow;\n }\n\n // pushRecord is a sugar method to take a single record and push it into the store using whatever verb was used\n pushRecord(type: string, data: RawRecord, method: MethodType, options: Record<string, any> = {}) {\n return this.pushRecords(type, [data], method, options);\n }\n\n // pushRecords handles an incoming data (\"row\"s) that are in application level format (with dates, blobs, or other complex types that can't be POJO'd)\n pushRecords(type: string, data: RawRecord[], method: MethodType, options: Record<string, any> = {}) {\n const transform = this.getRecordTransform(type);\n const typeKey = camelCase(pluralize(type));\n if (!transform) {\n return this.pushPayload(method, { [typeKey]: data }, options);\n }\n const rawData = data.map((item) => {\n return transform.serialize(item);\n });\n return this.pushPayload(method, { [typeKey]: rawData }, options);\n }\n\n // pushPayload handles incoming data (many \"row\"s) that are in POJO format\n pushPayload(method: MethodType, data: any, options: Record<string, any> = {}) {\n try {\n const batchedPayload = this.interpreter(data, options);\n batchedPayload.data.forEach((payload) => {\n const schema = this.getSchema(payload.type);\n // Only process typed batches that match a defined data schema\n if (schema) {\n const pk = this.getPrimaryKey(payload.type) || 'id';\n if (method === 'DELETE') {\n payload.data.forEach((row) => {\n this.store.delRow(payload.type, row[pk]);\n });\n } else if (method === 'PATCH') {\n payload.data.forEach((row) => {\n // Only try a partial row update if a row already exists\n if (this.store.getRow(payload.type, row[pk])) {\n this.store.setPartialRow(payload.type, row[pk], this.serialize(row, schema));\n } else {\n this.store.setRow(payload.type, row[pk], this.serialize(row, schema));\n }\n });\n } else {\n payload.data.forEach((row) => {\n this.store.setRow(payload.type, row[pk], this.serialize(row, schema));\n });\n }\n }\n });\n return batchedPayload;\n } catch (e) {\n console.warn(e);\n }\n }\n\n peekRecord<T>(type: string, id: string): T | undefined {\n const schema = this.getSchema(type);\n const pk = this.getPrimaryKey(type);\n const transformer = this.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n if (schema && pk) {\n const row = this.getStore().getRow(type, id);\n if (row) {\n return effectiveTransformer(this.deserialize(row, schema));\n }\n }\n return undefined;\n }\n\n peekAll<T>(type: string): T[] {\n const schema = this.getSchema(type);\n const pk = this.getPrimaryKey(type);\n const transformer = this.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n if (schema && pk) {\n const table = this.getStore().getTable(type);\n return Object.entries(table).map(([_, row]) => {\n const thing = effectiveTransformer(this.deserialize(row, schema));\n return thing as T;\n });\n }\n return [];\n }\n\n unloadRecord<T>(type: string, id: string): T | undefined {\n const record = this.peekRecord<T>(type, id);\n if (record) {\n this.getStore().delRow(type, id);\n }\n return record;\n }\n\n unloadAll(type: string) {\n if (this.getSchema(type)) {\n this.getStore().delTable(type);\n }\n }\n\n reset() {\n Object.entries(this.schemas).forEach(([schemaName, _]) => {\n this.unloadAll(schemaName);\n });\n }\n\n // Need to get the raw tinybase store? use this method\n getStore() {\n return this.store;\n }\n\n // Need to get the raw tinybase query views? use this method\n getQueries() {\n return this.queries;\n }\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\nimport { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport { Provider as TinyBaseProvider } from 'tinybase/ui-react';\nimport type { MicroStore } from './microstore';\n\nconst StoreContext = createContext<MicroStore | null>(null);\n\nexport function useMicroStore(): MicroStore | null {\n const store = useContext(StoreContext);\n\n return store;\n}\n\ntype WithExistingStore = { store: MicroStore; children: ReactNode };\n\nexport function MicroStoreProvider(props: WithExistingStore) {\n const store = useMemo(() => props.store, [props]);\n\n // TODO: Add store.relationships, which can be passed to tinybase\n return (\n <StoreContext.Provider value={store}>\n <TinyBaseProvider store={store.getStore()} queries={store.getQueries()}>\n {props.children}\n </TinyBaseProvider>\n </StoreContext.Provider>\n );\n}\n","// This software is provided to the United States Government (USG) with SBIR Data Rights as defined at Federal Acquisition Regulation 52.227-14, \"Rights in Data-SBIR Program\" (May 2014) SBIR Rights Notice (Dec 2026) These SBIR data are furnished with SBIR rights under Contract No. H9241522D0001. For a period of 19 years, unless extended in accordance with FAR 27.409(h), after acceptance of all items to be delivered under this contract, the Government will use these data for Government purposes only, and they shall not be disclosed outside the Government (including disclosure for procurement purposes) during such period without permission of the Contractor, except that, subject to the foregoing use and disclosure prohibitions, these data may be disclosed for use by support Contractors. After the protection period, the Government has a paid-up license to use, and to authorize others to use on its behalf, these data for Government purposes, but is relieved of all disclosure prohibitions and assumes no liability for unauthorized use of these data by third parties. This notice shall be affixed to any reproductions of these data, in whole or in part.\n// This whole file will get \"genericized\" so that it can wrap any useQuery that we currently use\n// Right now it is hard coded to do object stuff...\n// The same logic can be made more abstract and receive a couple more\n// arguments to then handle ANYTHING. :)\nimport { useEffect, useRef, useState } from 'react';\nimport { type Queries } from 'tinybase';\nimport { useQueries, useResultTable } from 'tinybase/ui-react';\nimport { useMicroStore } from './provider';\nimport type { MicroStoreSchema } from './types';\n\nfunction generateFilter(data: any[] | undefined, pk: string) {\n return data ? data.map((obj) => obj[pk]) : [];\n}\n\nfunction resetQueryDefinition(\n type: string,\n schema: MicroStoreSchema,\n pk: string,\n queryName: string,\n queries: Queries,\n filter: string[]\n) {\n // So what exactly does this do? It returns all the same tinybase rows that are\n // returned by the same React query being referenced by this reactive wrapper!!\n // That way, when the rows from useResultTable change due to some localized store\n // update through the MicroStore engine, we see those changes reflected without\n // additional server traffic!\n queries.setQueryDefinition(queryName, type, ({ select, where }) => {\n Object.keys(schema).forEach((k) => {\n select(k);\n });\n where((getCell) => filter.includes(<string>getCell(pk)));\n });\n}\n\n// Wrap the objects in tinybase rows to make them react at a record level\n// to individual record changes\nexport function useReactive<T>(type: string, data: T[]): T[] {\n const store = useMicroStore();\n const schema = store?.getSchema(type);\n const pk = store?.getPrimaryKey(type);\n if (!schema || !pk) {\n throw new Error(`No MicroStore schema defined for type ${type}`);\n }\n const [uniqueHookID, _] = useState(crypto.randomUUID());\n const queries = useQueries();\n const rows = useResultTable(uniqueHookID, queries);\n const [lastFilter, setLastFilter] = useState('');\n // Air Brake prevents render loops if a user does something very silly like chasing\n // records in a circle\n const airBrake = useRef('');\n\n // Why is this effect important? Because you ONLY want to update the thing\n // that is bubbling out new rows (the query) if the supporting REST request\n // with its own internal IDs has changed. We\n useEffect(() => {\n if (queries) {\n const filter = generateFilter(data, pk);\n const stringifiedFilter = JSON.stringify(filter);\n if (lastFilter !== stringifiedFilter && airBrake.current !== stringifiedFilter) {\n if (Object.entries(rows).length > 0) {\n airBrake.current = stringifiedFilter;\n }\n setLastFilter(stringifiedFilter);\n resetQueryDefinition(type, schema, pk, uniqueHookID, queries, filter);\n }\n }\n }, [data, pk, schema, type, queries, uniqueHookID, lastFilter, rows, airBrake]);\n\n const transformer = store?.getRecordTransform(type);\n const effectiveTransformer = transformer ? transformer.deserialize : (x: any) => x;\n const returnData: T[] = [];\n data.forEach((item: any) => {\n const identifier = item[pk];\n const possibleRow = rows[identifier];\n if (possibleRow) {\n const thing: any = store?.deserialize(possibleRow, schema);\n returnData.push(effectiveTransformer(thing) as T);\n }\n });\n return returnData;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@black-cape/microstore",
3
+ "version": "0.0.1",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "build:watch": "tsup --watch",
13
+ "dev": "tsup --watch",
14
+ "type-check": "tsc --noEmit",
15
+ "lint": "tsc --noEmit --pretty",
16
+ "format": "prettier --write \"src/**/*.{ts,tsx}\"",
17
+ "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
18
+ "test:compatibility": "node test-compatibility.js",
19
+ "clean": "rm -rf dist",
20
+ "prepare": "npm run build",
21
+ "prepublishOnly": "npm run clean && npm run build"
22
+ },
23
+ "keywords": [
24
+ "react",
25
+ "typescript",
26
+ "state-management",
27
+ "data-normalization",
28
+ "normalizer",
29
+ "reactive",
30
+ "store",
31
+ "rest",
32
+ "api",
33
+ "crud",
34
+ "tinybase",
35
+ "single-source-of-truth",
36
+ "react-hooks",
37
+ "cache",
38
+ "orm",
39
+ "immutable",
40
+ "real-time",
41
+ "rest-envelope"
42
+ ],
43
+ "homepage": "https://github.com/black-cape/microstore",
44
+ "repository": "https://github.com/black-cape/microstore",
45
+ "authors": [
46
+ {
47
+ "name": "Michael Conaway",
48
+ "email": "mdconaway@noreply.github.com"
49
+ }
50
+ ],
51
+ "organization": {
52
+ "name": "Black Cape",
53
+ "url": "https://github.com/black-cape"
54
+ },
55
+ "license": "ISC",
56
+ "description": "",
57
+ "dependencies": {
58
+ "ember-inflector": "^6.0.0",
59
+ "lodash-es": "^4.17.22",
60
+ "tinybase": ">=5.4.9"
61
+ },
62
+ "peerDependencies": {
63
+ "react": "^18.0.0 || ^19.0.0",
64
+ "react-dom": "^18.0.0 || ^19.0.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "react-compiler-runtime": {
68
+ "optional": true
69
+ }
70
+ },
71
+ "devDependencies": {
72
+ "@types/lodash-es": "^4.17.12",
73
+ "@types/react": "^19.0.0",
74
+ "@types/react-dom": "^19.0.0",
75
+ "prettier": "^3.8.0",
76
+ "prettier-plugin-organize-imports": "^4.3.0",
77
+ "react": "^18.3.1",
78
+ "react-dom": "^18.3.1",
79
+ "tsup": "^8.0.0",
80
+ "typescript": "^5.0.0"
81
+ }
82
+ }