@superbright/indexeddb-orm 0.1.1 → 0.1.2

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.
@@ -1,160 +0,0 @@
1
- /**
2
- * App Store Integration Guide
3
- *
4
- * Replace your existing app store (useStore) with this new ORM-backed implementation
5
- */
6
-
7
- // 1. Remove these old imports:
8
- // import { persist, createJSONStorage } from "zustand/middleware";
9
- // import { get, set, del } from "idb-keyval";
10
- // import { createORMStringStorage } from "@superbright/indexeddb-orm";
11
-
12
- // 2. Add these new imports:
13
- import { create } from "zustand";
14
- import { devtools } from "zustand/middleware";
15
- import {
16
- createZustandAppStore,
17
- type ZustandAppStoreState,
18
- type Filters,
19
- type QueryParams,
20
- type UnitData
21
- } from "@superbright/indexeddb-orm";
22
-
23
- // 3. Replace your store creation:
24
- export const useStore = create<ZustandAppStoreState>()(
25
- devtools(
26
- createZustandAppStore({
27
- // Optional: callback for when filters are updated
28
- onFilterUpdate: (apiParams: QueryParams) => {
29
- // This replaces your updateParams call
30
- // Import and call your updateParams function here
31
- // updateParams(apiParams);
32
- console.log("Filters updated:", apiParams);
33
- }
34
- }),
35
- { name: "app-store" }
36
- )
37
- );
38
-
39
- // 4. Initialize the store in your app startup (e.g., main.tsx or App.tsx):
40
- /*
41
- import { useStore } from './stores/appStore';
42
-
43
- async function initializeApp() {
44
- // Initialize and hydrate the store with data from IndexedDB
45
- await useStore.getState()._initialize();
46
- await useStore.getState()._hydrate();
47
-
48
- // ... rest of your app initialization
49
- }
50
-
51
- // Call this on app startup
52
- initializeApp();
53
- */
54
-
55
- // 5. Usage in components remains largely the same, but methods are now async:
56
- /*
57
- function FiltersComponent() {
58
- const {
59
- filters,
60
- tempFilters,
61
- resultsMode,
62
- sortBy,
63
- filtersLoaded
64
- } = useStore();
65
-
66
- // NOTE: These are now async operations
67
- const handleFilterChange = async (key: keyof Filters, value: any) => {
68
- await useStore.getState().handleTempFilterChange(key, value);
69
- };
70
-
71
- const handleCommitFilter = async () => {
72
- await useStore.getState().submitFilterUpdate();
73
- };
74
-
75
- const handleResultsModeChange = async (mode: "all" | "bestFit" | "closestMatch" | "favorites") => {
76
- await useStore.getState().setResultsMode(mode);
77
- };
78
-
79
- const handleSortChange = async (sortBy: "relevance" | "newest" | "priceLowToHigh" | "priceHighToLow") => {
80
- await useStore.getState().setSortBy(sortBy);
81
- };
82
-
83
- return (
84
- <div>
85
- <p>Filters loaded: {filtersLoaded}</p>
86
- <p>Current bedrooms: {filters.bedrooms?.join(", ")}</p>
87
- <p>Results mode: {resultsMode}</p>
88
- <p>Sort by: {sortBy}</p>
89
- <button onClick={() => handleFilterChange("bedrooms", [1, 2])}>
90
- Set 1-2 bedrooms
91
- </button>
92
- <button onClick={handleCommitFilter}>Apply Filters</button>
93
- </div>
94
- );
95
- }
96
-
97
- function UnitComponent({ unitId }: { unitId: string }) {
98
- const { data } = useStore();
99
- const unitData = data[unitId];
100
-
101
- const handleToggleFavorite = async () => {
102
- const currentData = data[unitId] || {};
103
- await useStore.getState().setData(unitId, {
104
- ...currentData,
105
- isFavorite: !currentData.isFavorite
106
- });
107
- };
108
-
109
- const handleMarkViewed = async () => {
110
- const today = new Date();
111
- const formattedDate = `${String(today.getMonth() + 1).padStart(2, "0")}/${String(today.getDate()).padStart(2, "0")}`;
112
-
113
- const currentData = data[unitId] || {};
114
- await useStore.getState().setData(unitId, {
115
- ...currentData,
116
- viewedDate: formattedDate
117
- });
118
- };
119
-
120
- return (
121
- <div>
122
- <p>Unit: {unitId}</p>
123
- <p>Is favorite: {unitData?.isFavorite ? "Yes" : "No"}</p>
124
- <p>Viewed: {unitData?.viewedDate || "Never"}</p>
125
- <button onClick={handleToggleFavorite}>Toggle Favorite</button>
126
- <button onClick={handleMarkViewed}>Mark as Viewed</button>
127
- </div>
128
- );
129
- }
130
- */
131
-
132
- // 6. Key breaking changes to be aware of:
133
- /*
134
- BREAKING CHANGES:
135
- - All store methods are now async (return Promise<void>)
136
- - Must call _initialize() and _hydrate() on app startup
137
- - No more persist middleware configuration needed
138
- - No more custom IndexedDB storage setup
139
- - loadPersistedFilters() is now redundant (always loads from IndexedDB)
140
- - availabilityOptions array is not stored (should be handled in UI)
141
-
142
- BENEFITS:
143
- - Better type safety with Zod validation
144
- - Centralized storage logic in ORM
145
- - Better error handling and validation
146
- - No more manual storage configuration
147
- - More consistent API across different data types
148
- - Better performance with native IndexedDB operations
149
- - Automatic state synchronization
150
- - Built-in filter update callbacks
151
-
152
- MIGRATION NOTES:
153
- 1. The store structure remains the same, but all operations are async
154
- 2. Remove custom IndexedDB setup (idb-keyval, custom storage)
155
- 3. Replace persist middleware with ORM storage
156
- 4. Add initialization calls in app startup
157
- 5. Update component handlers to be async
158
- 6. Handle updateParams callback in store configuration
159
- 7. Remove availabilityOptions from store (handle in UI components)
160
- */
@@ -1,192 +0,0 @@
1
- # Property Store
2
-
3
- The Property Store provides a complete solution for managing property-related data in your application using IndexedDB as the storage backend.
4
-
5
- ## Features
6
-
7
- - **Native IndexedDB storage** - Direct integration with the ORM's storage layer
8
- - **Type-safe operations** - Full TypeScript support with Zod validation
9
- - **Zustand compatibility** - Easy integration with existing Zustand stores
10
- - **Async by default** - All operations are properly async for better performance
11
- - **Centralized state management** - All property data managed in one place
12
-
13
- ## Core API
14
-
15
- ### PropertyStore Class
16
-
17
- The core `PropertyStore` class provides direct access to property data operations:
18
-
19
- ```typescript
20
- import { propertyStore } from "@superbright/indexeddb-orm";
21
-
22
- // Initialize a property
23
- await propertyStore.initializeProperty("prop-123", "property-slug");
24
-
25
- // Set current property context
26
- await propertyStore.setPropertyId("prop-123");
27
- await propertyStore.setPropertySlug("property-slug");
28
-
29
- // Manage favorites
30
- await propertyStore.toggleFavorite("unit-456");
31
-
32
- // Track viewed units
33
- await propertyStore.markUnitAsViewed("unit-456", "property-slug");
34
-
35
- // Store questionnaire results
36
- await propertyStore.setQuestionnaireResults({ maxRent: 2000, petFriendly: true });
37
-
38
- // Get unit state
39
- const unitState = await propertyStore.getUnitState("unit-456");
40
- console.log(unitState.isFavorite, unitState.viewedDate);
41
- ```
42
-
43
- ### Data Structure
44
-
45
- Properties are stored with the following structure:
46
-
47
- ```typescript
48
- interface PropertyData {
49
- id: string;
50
- slug: string;
51
- favoritedUnits: string[];
52
- tourContactedOn: string | null;
53
- viewedUnits: {
54
- unitId: string;
55
- viewedDate: string;
56
- }[];
57
- questionnaireResults?: unknown | null;
58
- tourContactData?: {
59
- timezone: string;
60
- favouriteUnits?: string[];
61
- preferences: Record<string, any>;
62
- } | null;
63
- }
64
- ```
65
-
66
- ## Zustand Integration
67
-
68
- For apps using Zustand, you can create a store that automatically syncs with the ORM:
69
-
70
- ```typescript
71
- import { create } from "zustand";
72
- import { devtools } from "zustand/middleware";
73
- import {
74
- createZustandPropertyStore,
75
- createUseUnitState,
76
- type ZustandPropertyStoreState
77
- } from "@superbright/indexeddb-orm";
78
-
79
- // Create store
80
- export const usePropertyStore = create<ZustandPropertyStoreState>()(
81
- devtools(
82
- createZustandPropertyStore(),
83
- { name: "property-store" }
84
- )
85
- );
86
-
87
- // Initialize on app startup
88
- export async function initializeApp() {
89
- await usePropertyStore.getState()._hydrate();
90
- }
91
-
92
- // Create unit state hook
93
- export const useUnitState = createUseUnitState()(usePropertyStore);
94
-
95
- // Use in components
96
- function MyComponent() {
97
- const { data, propertyId } = usePropertyStore();
98
- const unitState = useUnitState("unit-123");
99
-
100
- const handleToggleFavorite = async () => {
101
- await usePropertyStore.getState().toggleFavorite("unit-123");
102
- };
103
-
104
- return (
105
- <div>
106
- <p>Property: {propertyId}</p>
107
- <p>Is favorite: {unitState.isFavorite}</p>
108
- <button onClick={handleToggleFavorite}>Toggle Favorite</button>
109
- </div>
110
- );
111
- }
112
- ```
113
-
114
- ## Migration from Legacy Store
115
-
116
- If you're migrating from a previous implementation using `createORMStringStorage` with Zustand persist middleware:
117
-
118
- ### Before (Legacy)
119
- ```typescript
120
- import { create } from "zustand";
121
- import { persist, createJSONStorage } from "zustand/middleware";
122
- import { createORMStringStorage } from "@superbright/indexeddb-orm";
123
-
124
- const ormStorage = createORMStringStorage({
125
- fallbackToMemory: true,
126
- });
127
-
128
- export const usePropertyStore = create(
129
- persist(
130
- (set, get) => ({
131
- // ... store implementation
132
- }),
133
- {
134
- name: "property",
135
- storage: createJSONStorage(() => ormStorage),
136
- }
137
- )
138
- );
139
- ```
140
-
141
- ### After (New)
142
- ```typescript
143
- import { create } from "zustand";
144
- import { devtools } from "zustand/middleware";
145
- import { createZustandPropertyStore } from "@superbright/indexeddb-orm";
146
-
147
- export const usePropertyStore = create()(
148
- devtools(
149
- createZustandPropertyStore(),
150
- { name: "property-store" }
151
- )
152
- );
153
-
154
- // Don't forget to hydrate on app startup!
155
- await usePropertyStore.getState()._hydrate();
156
- ```
157
-
158
- ### Key Changes
159
- 1. **No more persist middleware** - Storage is handled natively by the ORM
160
- 2. **All methods are async** - Better performance and error handling
161
- 3. **Type safety** - Built-in validation with Zod schemas
162
- 4. **Explicit hydration** - Must call `_hydrate()` on app startup
163
- 5. **Centralized data** - All property logic lives in the ORM
164
-
165
- ## API Reference
166
-
167
- ### PropertyStore Methods
168
-
169
- - `initializeProperty(propertyId, slug)` - Initialize a new property
170
- - `setPropertyId(id)` - Set current property ID
171
- - `setPropertySlug(slug)` - Set current property slug
172
- - `setData(data)` - Set all property data
173
- - `removeData(key)` - Remove a property by ID
174
- - `clearData()` - Clear all property data
175
- - `toggleFavorite(unitId)` - Toggle unit favorite status
176
- - `markUnitAsViewed(unitId, slug)` - Mark unit as viewed
177
- - `setTourContactedOn()` - Mark tour as contacted
178
- - `getTourContactedOn()` - Get tour contact date
179
- - `setQuestionnaireResults(results)` - Store questionnaire results
180
- - `setTourContactData(data)` - Store tour contact data
181
- - `getUnitState(unitId)` - Get unit favorite/viewed state
182
- - `getPropertyData(propertyId?)` - Get property data
183
- - `getFullState()` - Get complete store state
184
-
185
- ### Zustand Store Actions
186
-
187
- When using the Zustand adapter, all PropertyStore methods are available, plus:
188
-
189
- - `_hydrate()` - Load data from IndexedDB into Zustand state
190
- - `getUnitState(unitId)` - Synchronous unit state getter for React
191
-
192
- All data modification methods automatically sync the Zustand state after updating IndexedDB.
@@ -1,129 +0,0 @@
1
- # Structured Store Migration Guide
2
-
3
- ## Overview
4
-
5
- The `InResiStoreActions` structure has been moved from the consuming app into the ORM library itself. Instead of manually creating action structures in the consuming app, you can now use the new `createStructuredStore` function that provides these actions directly.
6
-
7
- ## Migration
8
-
9
- ### Before (Old Pattern)
10
-
11
- ```typescript
12
- import {
13
- createZustandPropertyStore,
14
- type ZustandUnifiedStoreState,
15
- } from "@superbright/indexeddb-orm";
16
-
17
- type InResiStoreActions = {
18
- property: {
19
- unit: {
20
- favorites: {
21
- toggle: (unitId: string) => Promise<void>;
22
- };
23
- viewed: {
24
- mark: (unitId: string, slug: string) => Promise<void>;
25
- };
26
- };
27
- questionnaire: {
28
- setResults: (results: unknown) => Promise<void>;
29
- };
30
- };
31
- };
32
-
33
- const baseStore = create<ZustandUnifiedStoreState>()(
34
- devtools(createZustandPropertyStore(options))
35
- );
36
-
37
- const inResiStoreActions: InResiStoreActions = {
38
- property: {
39
- unit: {
40
- favorites: {
41
- toggle: async (unitId: string) => {
42
- await baseStore.getState().toggleFavorite(unitId);
43
- },
44
- },
45
- viewed: {
46
- mark: async (unitId: string, slug: string) => {
47
- await baseStore.getState().markUnitAsViewed(unitId, slug);
48
- },
49
- },
50
- },
51
- questionnaire: {
52
- setResults: async (results: unknown) => {
53
- await baseStore.getState().setQuestionnaireResults(results);
54
- },
55
- },
56
- },
57
- };
58
-
59
- export const useInResiStore = Object.assign(baseStore, inResiStoreActions);
60
- ```
61
-
62
- ### After (New Pattern)
63
-
64
- ```typescript
65
- import {
66
- createStructuredStore,
67
- type StructuredStore,
68
- } from "@superbright/indexeddb-orm";
69
-
70
- const baseStore = create<StructuredStore>()(
71
- devtools(createStructuredStore(options))
72
- );
73
-
74
- export const useInResiStore = baseStore;
75
- ```
76
-
77
- ## Usage
78
-
79
- The structured actions are now directly available on the store:
80
-
81
- ```typescript
82
- // Toggle favorite
83
- await useInResiStore.getState().property.unit.favorites.toggle(unitId);
84
-
85
- // Mark unit as viewed
86
- await useInResiStore.getState().property.unit.viewed.mark(unitId, slug);
87
-
88
- // Set questionnaire results
89
- await useInResiStore.getState().property.questionnaire.setResults(results);
90
-
91
- // All original store methods are still available
92
- const currentProperty = await useInResiStore.getState().getCurrentProperty();
93
- const unitState = useInResiStore.getState().getUnitState(unitId);
94
- ```
95
-
96
- ## Available Structured Actions
97
-
98
- The `StructuredStore` type includes all the original `ZustandUnifiedStoreState` methods plus the following structured actions:
99
-
100
- ### Property Actions
101
- - `property.unit.favorites.toggle(unitId: string)` - Toggle unit favorite status
102
- - `property.unit.viewed.mark(unitId: string, slug: string)` - Mark unit as viewed
103
- - `property.questionnaire.setResults(results: unknown)` - Set questionnaire results
104
- - `property.tour.setContactedOn()` - Set tour contacted timestamp
105
- - `property.tour.getContactedOn()` - Get tour contacted timestamp
106
- - `property.tour.setContactData(data: TourContactData)` - Set tour contact data
107
-
108
- ### Unit Actions
109
- - `unit.data.set(unitId: string, data: UnitData)` - Set unit data
110
- - `unit.data.remove(unitId: string)` - Remove unit data
111
- - `unit.data.clear()` - Clear all unit data
112
- - `unit.data.get(unitId: string)` - Get unit data
113
- - `unit.state.get(unitId: string)` - Get unit state (favorite/viewed status)
114
-
115
- ### Filter Actions
116
- - `filters.set(filters: Partial<Filters>)` - Set filters
117
- - `filters.setTemp(filters: Partial<Filters>)` - Set temporary filters
118
- - `filters.setToDefault()` - Reset filters to default
119
- - `filters.commitTemp(key, defaultValue)` - Commit temporary filter changes
120
- - `filters.commitAvailability()` - Commit availability filter changes
121
- - `filters.submit()` - Submit all filter updates
122
-
123
- ## Benefits
124
-
125
- 1. **Centralized**: Actions are defined once in the ORM library
126
- 2. **Type Safe**: Full TypeScript support with proper types
127
- 3. **Consistent**: Same structure across all consuming applications
128
- 4. **Maintainable**: Changes to action signatures only need to be made in one place
129
- 5. **Extensible**: Easy to add new structured actions to the ORM library
@@ -1,164 +0,0 @@
1
- /**
2
- * Example: Unified Store Migration Guide
3
- *
4
- * This shows how to migrate from separate property and app stores
5
- * to the new unified ORM store.
6
- */
7
-
8
- // === NEW APPROACH: Using ORM's unified store ===
9
-
10
- /*
11
- // 1. In your consuming app, create unified Zustand store with ORM backend:
12
-
13
- import { create } from "zustand";
14
- import { devtools } from "zustand/middleware";
15
- import {
16
- createZustandPropertyStore, // More intuitive name
17
- createUseUnitState,
18
- type ZustandUnifiedStoreState,
19
- type QueryParams
20
- } from "@superbright/indexeddb-orm";
21
-
22
- export const useStore = create<ZustandUnifiedStoreState>()(
23
- devtools(
24
- createZustandPropertyStore({
25
- onFilterUpdate: (apiParams: QueryParams) => {
26
- // Handle filter updates (replaces updateParams call)
27
- console.log("Filters updated:", apiParams);
28
- }
29
- }),
30
- { name: "property-store" }
31
- )
32
- );
33
-
34
- // 2. Initialize the store on app startup
35
- export async function initializeStore() {
36
- await useStore.getState()._initialize();
37
- await useStore.getState()._hydrate();
38
- }
39
-
40
- // 3. Create the unit state hook
41
- export const useUnitState = createUseUnitState()(useStore);
42
-
43
- // 4. Export store instance (keep familiar naming)
44
- export const propertyStore = useStore.getState;
45
- */
46
-
47
- // === USAGE EXAMPLES ===
48
-
49
- import {
50
- store,
51
- type TourContactData,
52
- type Filters as UnifiedFilters
53
- } from "../src/stores/unified";
54
- import { createZustandUnifiedStore } from "../src/adapters/zustand-unified";
55
-
56
- // Direct ORM usage (without Zustand):
57
- async function directUnifiedStoreUsage() {
58
- // Initialize the store
59
- await store.initialize();
60
-
61
- // === Property operations ===
62
- // Initialize a property
63
- await store.initializeProperty("prop-123", "property-slug");
64
-
65
- // Set current property
66
- await store.setCurrentProperty("prop-123", "property-slug");
67
-
68
- // Toggle a favorite
69
- await store.toggleFavorite("unit-456");
70
-
71
- // Mark unit as viewed
72
- await store.markUnitAsViewed("unit-456", "property-slug");
73
-
74
- // Set tour contact data
75
- const tourData: TourContactData = {
76
- timezone: "America/New_York",
77
- favouriteUnits: ["unit-456"],
78
- preferences: { bedrooms: 2 }
79
- };
80
- await store.setTourContactData(tourData);
81
-
82
- // Set questionnaire results
83
- await store.setQuestionnaireResults({ maxRent: 2000, petFriendly: true });
84
-
85
- // Mark tour contacted
86
- await store.setTourContactedOn();
87
-
88
- // === App operations ===
89
- // Set unit data
90
- await store.setUnitData("unit-123", { isFavorite: true, viewedDate: "12/25" });
91
-
92
- // Set filters
93
- const filters: Partial<UnifiedFilters> = {
94
- bedrooms: [1, 2],
95
- cost: 2000,
96
- highlights: ["pet-friendly", "balcony"]
97
- };
98
- await store.setFilters(filters);
99
-
100
- // Set results mode
101
- await store.setResultsMode("favorites");
102
-
103
- // Set sorting
104
- await store.setSortBy("priceLowToHigh");
105
-
106
- // Handle questionnaire values
107
- await store.setResolvedQuestionnaireValues("petPreference", ["dogs", "cats"]);
108
-
109
- // === Get data ===
110
- // Get unit state (combines property favorite + app unit data)
111
- const unitState = await store.getUnitState("unit-456");
112
- console.log("Is favorite:", unitState.isFavorite);
113
- console.log("Viewed date:", unitState.viewedDate);
114
-
115
- // Get results URL
116
- const resultsUrl = await store.getResultsUrl();
117
- console.log("Results URL:", resultsUrl);
118
-
119
- // Get current state
120
- const state = await store.getFullState();
121
- console.log("Full state:", state);
122
- }
123
-
124
- // Zustand store creation example:
125
- function createExampleZustandUnifiedStore() {
126
- return createZustandUnifiedStore({
127
- onFilterUpdate: (apiParams) => {
128
- console.log("Filters updated:", apiParams);
129
- // Call your updateParams function here
130
- }
131
- });
132
- }
133
-
134
- // === MIGRATION NOTES ===
135
-
136
- /**
137
- * Key differences from the old approach:
138
- *
139
- * 1. Single unified store instead of separate property and app stores
140
- * 2. No more manual persist middleware configuration
141
- * 3. No more createORMStringStorage or custom IndexedDB setup
142
- * 4. All storage is handled natively by the ORM
143
- * 5. Better TypeScript support with proper schemas
144
- * 6. Validation built-in via Zod schemas
145
- * 7. Async operations are explicit
146
- * 8. Must call _initialize() and _hydrate() on app initialization
147
- *
148
- * Breaking changes:
149
- * - Two stores combined into one unified store
150
- * - All store methods are now async
151
- * - Must initialize store with _initialize() and _hydrate()
152
- * - Property data is now in 'properties' object with currentPropertyId tracking
153
- * - toggleFavorite no longer takes propertyId (uses currentPropertyId)
154
- * - Unit data and property data are separate but related
155
- *
156
- * Benefits:
157
- * - Single source of truth for all app state
158
- * - Centralized data management in ORM
159
- * - Better error handling and validation
160
- * - More consistent API across all operations
161
- * - Easier testing and mocking
162
- * - Better performance with native IndexedDB operations
163
- * - Simplified state management and debugging
164
- */
package/index.html DELETED
@@ -1,29 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>IndexedDB ORM Playground</title>
7
- <style>
8
- :root { font-family: system-ui, sans-serif; }
9
- body { max-width: 800px; margin: 2rem auto; padding: 1rem; }
10
- button { margin: 0.25rem 0.5rem 0.25rem 0; padding: 0.5rem 0.75rem; }
11
- pre { background: #111; color: #eee; padding: 1rem; border-radius: 6px; overflow: auto; }
12
- .row { margin: 0.75rem 0; }
13
- </style>
14
- </head>
15
- <body>
16
- <h1>IndexedDB ORM Playground</h1>
17
- <p>Use these buttons to write/read data, then open DevTools → <strong>Application</strong> → <strong>IndexedDB</strong> → <code>inresi-orm</code>.</p>
18
- <div class="row">
19
- <button id="btn-init">Init DB</button>
20
- <button id="btn-write">Write sample data</button>
21
- <button id="btn-dump">Dump to console</button>
22
- <button id="btn-export">Export JSON</button>
23
- <button id="btn-reset">Reset DB</button>
24
- </div>
25
- <h3>Last dump</h3>
26
- <pre id="dump">(nothing yet)</pre>
27
- <script type="module" src="/playground/main.ts"></script>
28
- </body>
29
- </html>