@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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/errors.ts","../src/adapters/dexie.ts","../src/validation.ts","../src/db.ts","../src/storage.ts","../src/api/properties.ts","../src/api/app.ts","../src/stores/unified.ts","../src/schema.ts","../src/api/users.ts","../src/debug.ts","../src/api/favorites.ts","../src/adapters/zustand-unified.ts","../src/adapters/structured-store.ts","../src/units/favorites.ts"],"sourcesContent":["export class SchemaMismatchError extends Error {\n constructor(message: string, public detail?: unknown) {\n super(message);\n this.name = \"SchemaMismatchError\";\n }\n}\n\nexport class OpenDBError extends Error {\n constructor(message: string, public detail?: unknown) {\n super(message);\n this.name = \"OpenDBError\";\n }\n}\n","import Dexie, { type Table } from \"dexie\";\nimport { SCHEMA_VERSION } from \"../schema\";\n\nexport interface KV {\n key: string;\n value: unknown;\n}\n\nexport interface OrmDexieTables {\n users: Table<any, string>;\n kv: Table<KV, string>;\n}\n\nexport class OrmDexie extends Dexie {\n users!: OrmDexieTables[\"users\"];\n kv!: OrmDexieTables[\"kv\"];\n\n constructor(name = \"inresi-orm\") {\n super(name);\n this.version(SCHEMA_VERSION).stores({\n users: \"uuid\",\n kv: \"key\",\n });\n\n this.users = this.table(\"users\");\n this.kv = this.table(\"kv\");\n }\n}\n","// src/validation.ts\nimport type { z } from \"zod\";\nimport { SchemaMismatchError } from \"./errors\";\n\nexport type ValidationMode = \"strict\" | \"warn\" | \"silent\";\nlet mode: ValidationMode = \"strict\";\nlet onIssue: ((ctx: string, details: unknown) => void) | undefined;\n\nexport function configureValidation(opts: {\n mode?: ValidationMode;\n onIssue?: (ctx: string, details: unknown) => void;\n}) {\n if (opts.mode) mode = opts.mode;\n onIssue = opts.onIssue;\n}\n\nexport function validate<T>(\n schema: z.ZodType<T>,\n value: unknown,\n ctx: string,\n): T | null {\n const res = schema.safeParse(value);\n if (res.success) return res.data;\n\n const details = res.error.flatten();\n onIssue?.(ctx, details);\n\n if (mode === \"strict\")\n throw new SchemaMismatchError(`ORM schema mismatch in ${ctx}`, details);\n if (mode === \"warn\") console.warn(`ORM schema mismatch in ${ctx}`, details); // eslint-disable-line no-console\n return null; // silent/warn → let caller decide how to proceed\n}\n","// src/db.ts\nimport { OrmDexie } from \"./adapters/dexie\";\nimport { OpenDBError } from \"./errors\";\nimport { SCHEMA_VERSION, UserSchema } from \"./schema\";\nimport {\n validate,\n configureValidation,\n type ValidationMode,\n} from \"./validation\";\nimport type { Table } from \"dexie\";\nimport { z } from \"zod\";\n\nconst MANIFEST_KEY = \"manifest\";\nconst ManifestSchema = z.object({ schemaVersion: z.number().int() });\ntype Manifest = z.infer<typeof ManifestSchema>;\n\nexport type OrmOptions = {\n dbName?: string;\n onReset?: (reason: \"incompatible\" | \"versionchange\" | \"blocked\") => void;\n onError?: (err: unknown) => void;\n validation?: {\n mode?: ValidationMode; // 'strict' | 'warn' | 'silent'\n onIssue?: (ctx: string, details: unknown) => void;\n validateReads?: boolean; // default: true\n dropInvalidOnRead?: boolean; // default: true (when validateReads)\n };\n};\n\nlet db: OrmDexie | null = null;\nlet openPromise: Promise<OrmDexie> | null = null;\n\n// Prevent double-installing hooks on the same instance\nconst VALIDATION_INSTALLED = Symbol(\"validationInstalled\");\n\nfunction installValidationHooks(\n instance: OrmDexie,\n vopts: OrmOptions[\"validation\"] | undefined,\n) {\n const anyDb = instance as any;\n if (anyDb[VALIDATION_INSTALLED]) return;\n anyDb[VALIDATION_INSTALLED] = true;\n\n const writeHook = <T>(table: Table<T, any>, schema: any, name: string) => {\n table.hook(\"creating\", (_pk, obj) => {\n const v = validate(schema, obj, `${name}.creating`);\n if (!v) throw new Error(`Rejected invalid ${name} on create`);\n return v as T; // allow coercion/stripping\n });\n table.hook(\"updating\", (mods, _pk, obj) => {\n const next = { ...(obj as any), ...(mods as any) };\n const v = validate(schema, next, `${name}.updating`);\n if (!v) return {}; // cancel update in warn/silent\n return mods;\n });\n };\n\n writeHook(instance.users, UserSchema, \"users\");\n\n const shouldValidateReads = vopts?.validateReads ?? true;\n if (shouldValidateReads) {\n const dropInvalid = vopts?.dropInvalidOnRead ?? true;\n const readHook = <T>(table: Table<T, any>, schema: any, name: string) => {\n table.hook(\"reading\", (obj) => {\n const v = validate(schema, obj, `${name}.reading`);\n if (v) return v;\n return dropInvalid ? undefined : obj; // pass-through if you prefer\n });\n };\n readHook(instance.users, UserSchema, \"users\");\n }\n}\n\nfunction isProbablyDev(): boolean {\n try {\n // @ts-ignore\n if (typeof import.meta !== \"undefined\" && import.meta.env)\n // @ts-ignore\n return !!import.meta.env.DEV;\n } catch {}\n try {\n if (typeof process !== \"undefined\" && process.env)\n return process.env.NODE_ENV !== \"production\";\n } catch {}\n return false;\n}\n\nexport async function getDB(opts: OrmOptions = {}): Promise<OrmDexie> {\n if (db) return db;\n if (openPromise) return openPromise;\n\n const name = opts.dbName ?? \"inresi-orm\";\n\n openPromise = (async () => {\n try {\n // Configure validation once per open\n const defaultMode: ValidationMode = isProbablyDev() ? \"warn\" : \"strict\";\n configureValidation({\n mode: opts.validation?.mode ?? defaultMode,\n onIssue: opts.validation?.onIssue,\n });\n\n const instance = new OrmDexie(name);\n\n // Multi-tab friendliness\n instance.on(\"versionchange\", () => {\n try {\n instance.close();\n } finally {\n opts.onReset?.(\"versionchange\");\n }\n });\n instance.on(\"blocked\", () => {\n opts.onReset?.(\"blocked\");\n });\n\n await instance.open();\n\n // Validate/initialize manifest\n const row = await instance.kv.get(MANIFEST_KEY);\n const saved = (row?.value ?? null) as unknown;\n\n if (!saved) {\n await instance.transaction(\"rw\", instance.kv, async () => {\n await instance.kv.put({\n key: MANIFEST_KEY,\n value: { schemaVersion: SCHEMA_VERSION },\n });\n });\n installValidationHooks(instance, opts.validation);\n db = instance;\n return instance;\n }\n\n const parsed = ManifestSchema.safeParse(saved as Manifest);\n if (!parsed.success || parsed.data.schemaVersion !== SCHEMA_VERSION) {\n await instance.delete();\n const fresh = new OrmDexie(name);\n fresh.on(\"versionchange\", () => {\n try {\n fresh.close();\n } finally {\n opts.onReset?.(\"versionchange\");\n }\n });\n fresh.on(\"blocked\", () => {\n opts.onReset?.(\"blocked\");\n });\n await fresh.open();\n await fresh.kv.put({\n key: MANIFEST_KEY,\n value: { schemaVersion: SCHEMA_VERSION },\n });\n installValidationHooks(fresh, opts.validation);\n opts.onReset?.(\"incompatible\");\n db = fresh;\n return fresh;\n }\n\n installValidationHooks(instance, opts.validation);\n db = instance;\n return instance;\n } catch (e) {\n opts.onError?.(e);\n throw new OpenDBError(\"Failed to open IndexedDB\", e);\n } finally {\n openPromise = null; // allow future reuse/retry\n }\n })();\n\n return openPromise;\n}\n\nexport async function resetDB(dbName?: string) {\n const name = dbName ?? \"inresi-orm\";\n if (db) {\n try {\n await db.close();\n } catch {\n /* ignore */\n }\n }\n const instance = new OrmDexie(name);\n await instance.delete();\n db = null;\n}\n","import { getDB } from \"./db\";\n\nexport interface AsyncStringStorage {\n getItem(name: string): Promise<string | null>;\n setItem(name: string, value: string): Promise<void>;\n removeItem(name: string): Promise<void>;\n}\n\ntype storeKeys = \"property\" | \"user\" | \"app\";\n\n// Generic typed KV helpers (store raw objects in the ORM's kv table)\nexport async function kvGet<T>(key: storeKeys): Promise<T | null> {\n const db = await getDB();\n const row = await db.kv.get(key);\n return (row?.value as T | undefined) ?? null;\n}\nexport async function kvSet<T>(key: storeKeys, value: T): Promise<void> {\n const db = await getDB();\n await db.kv.put({ key, value });\n}\nexport async function kvRemove(key: storeKeys): Promise<void> {\n const db = await getDB();\n await db.kv.delete(key);\n}\n\nexport function createORMStringStorage(opts?: {\n prefix?: string;\n fallbackToMemory?: boolean;\n}): AsyncStringStorage {\n const mem = new Map<string, string>(); // optional fallback\n const prefix = opts?.prefix ?? \"\";\n\n const toKey = (name: string) => `${prefix}${name}`;\n\n return {\n async getItem(name) {\n try {\n const db = await getDB();\n const row = await db.kv.get(toKey(name));\n return row ? JSON.stringify(row.value) : null;\n } catch (e) {\n if (opts?.fallbackToMemory) return mem.get(toKey(name)) ?? null;\n throw e;\n }\n },\n async setItem(name, value) {\n try {\n const db = await getDB();\n await db.kv.put({ key: toKey(name), value: JSON.parse(value) });\n } catch (e) {\n if (opts?.fallbackToMemory) {\n mem.set(toKey(name), value);\n return;\n }\n throw e;\n }\n },\n async removeItem(name) {\n try {\n const db = await getDB();\n await db.kv.delete(toKey(name));\n } catch (e) {\n if (opts?.fallbackToMemory) {\n mem.delete(toKey(name));\n return;\n }\n throw e;\n }\n },\n };\n}\n","import { z } from \"zod\";\nimport { kvGet, kvSet } from \"../storage\";\n\n// Property-related schemas\nexport const ViewedUnitSchema = z.object({\n unitId: z.string(),\n viewedDate: z.string(),\n});\n\nexport const TourContactDataSchema = z.object({\n timezone: z.string(),\n favouriteUnits: z.array(z.string()).optional(),\n preferences: z.record(z.unknown()),\n});\n\nexport const PropertyDataSchema = z.object({\n id: z.string(),\n slug: z.string(),\n favoritedUnits: z.array(z.string()),\n tourContactedOn: z.string().nullable(),\n viewedUnits: z.array(ViewedUnitSchema),\n questionnaireResults: z.unknown().nullable().optional(), // Generic for IFilters\n tourContactData: TourContactDataSchema.nullable().optional(),\n});\n\nexport const PropertyStoreDataSchema = z.object({\n data: z.record(PropertyDataSchema),\n propertySlug: z.string().nullable(),\n propertyId: z.string().nullable(),\n hasPreviouslySearched: z.array(z.string()),\n});\n\nexport type ViewedUnit = z.infer<typeof ViewedUnitSchema>;\nexport type TourContactData = z.infer<typeof TourContactDataSchema>;\nexport type PropertyData = z.infer<typeof PropertyDataSchema>;\nexport type PropertyStoreData = z.infer<typeof PropertyStoreDataSchema>;\n\n// Default state\nconst defaultPropertyStoreData: PropertyStoreData = {\n data: {},\n propertySlug: null,\n propertyId: null,\n hasPreviouslySearched: [],\n};\n\n// Core property store API\nexport class PropertyStore {\n private async getState(): Promise<PropertyStoreData> {\n const state = await kvGet<PropertyStoreData>(\"property\");\n return state ?? defaultPropertyStoreData;\n }\n\n private async setState(updater: (state: PropertyStoreData) => PropertyStoreData): Promise<void> {\n const currentState = await this.getState();\n const newState = updater(currentState);\n await kvSet(\"property\", newState);\n }\n\n // Basic state operations\n async setData(value: Record<string, PropertyData>): Promise<void> {\n await this.setState(state => ({ ...state, data: value }));\n }\n\n async setPropertySlug(slug: string): Promise<void> {\n await this.setState(state => ({ ...state, propertySlug: slug }));\n }\n\n async setPropertyId(id: string): Promise<void> {\n await this.setState(state => ({ ...state, propertyId: id }));\n }\n\n async removeData(key: string): Promise<void> {\n await this.setState(state => ({\n ...state,\n data: Object.entries(state.data)\n .filter(([k]) => k !== key)\n .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}),\n }));\n }\n\n async clearData(): Promise<void> {\n await this.setState(state => ({ ...state, data: {} }));\n }\n\n async setHasPreviouslySearched(slug: string): Promise<void> {\n await this.setState(state => ({\n ...state,\n hasPreviouslySearched: Array.from(\n new Set([...state.hasPreviouslySearched, slug])\n ),\n }));\n }\n\n // Property-specific operations\n async setTourContactedOn(): Promise<void> {\n await this.setState(state => {\n const propertyId = state.propertyId;\n if (!propertyId) return state;\n\n const property = state.data[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n data: {\n ...state.data,\n [propertyId]: {\n ...property,\n tourContactedOn: new Date().toISOString(),\n },\n },\n };\n });\n }\n\n async getTourContactedOn(): Promise<string | null> {\n const state = await this.getState();\n const propertyId = state.propertyId;\n if (!propertyId) return null;\n\n return state.data[propertyId]?.tourContactedOn ?? null;\n }\n\n async setQuestionnaireResults(results: unknown): Promise<void> {\n await this.setState(state => {\n const propertyId = state.propertyId;\n if (!propertyId) return state;\n\n const property = state.data[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n data: {\n ...state.data,\n [propertyId]: {\n ...property,\n questionnaireResults: results,\n },\n },\n };\n });\n }\n\n async setTourContactData(data: TourContactData): Promise<void> {\n await this.setState(state => {\n const propertyId = state.propertyId;\n if (!propertyId) return state;\n\n const property = state.data[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n data: {\n ...state.data,\n [propertyId]: {\n ...property,\n tourContactData: data,\n },\n },\n };\n });\n }\n\n async toggleFavorite(unitId: string): Promise<void> {\n await this.setState(state => {\n const propertyId = state.propertyId;\n if (!propertyId) return state;\n\n const property = state.data[propertyId];\n if (!property) return state;\n\n const isFavorited = property.favoritedUnits.includes(unitId);\n const updatedFavoritedUnits = isFavorited\n ? property.favoritedUnits.filter((id) => id !== unitId)\n : [...property.favoritedUnits, unitId];\n\n return {\n ...state,\n data: {\n ...state.data,\n [propertyId]: {\n ...property,\n favoritedUnits: updatedFavoritedUnits,\n },\n },\n };\n });\n }\n\n async markUnitAsViewed(unitId: string, slug: string): Promise<void> {\n const today = new Date();\n const formattedDate = `${String(today.getMonth() + 1).padStart(\n 2,\n \"0\"\n )}/${String(today.getDate()).padStart(2, \"0\")}`;\n\n await this.setState(state => {\n const propertyId = state.propertyId;\n if (!propertyId) return state;\n\n const property = state.data[propertyId];\n if (!property) return state;\n\n const updatedViewedUnits = [\n // Remove existing entry if it exists\n ...property.viewedUnits.filter((u) => u.unitId !== unitId),\n // Add updated one\n {\n unitId,\n viewedDate: formattedDate,\n },\n ];\n\n return {\n ...state,\n data: {\n ...state.data,\n [propertyId]: {\n ...property,\n viewedUnits: updatedViewedUnits,\n },\n },\n };\n });\n\n // Note: This opens a new window - you might want to handle this in the consuming app\n if (typeof window !== 'undefined') {\n window.open(`//${slug}`, \"_blank\");\n }\n }\n\n // Utility methods for getting specific data\n async getUnitState(unitId: string): Promise<{\n isFavorite: boolean;\n viewedDate: string;\n }> {\n const state = await this.getState();\n const property = state.propertyId ? state.data[state.propertyId] : null;\n\n return {\n isFavorite: property?.favoritedUnits.includes(unitId) ?? false,\n viewedDate:\n property?.viewedUnits.find((u) => u.unitId === unitId)?.viewedDate ?? \"\",\n };\n }\n\n async getPropertyData(propertyId?: string): Promise<PropertyData | null> {\n const state = await this.getState();\n const id = propertyId ?? state.propertyId;\n return id ? state.data[id] ?? null : null;\n }\n\n async getCurrentProperty(): Promise<PropertyData | null> {\n const state = await this.getState();\n return state.propertyId ? state.data[state.propertyId] ?? null : null;\n }\n\n async getFullState(): Promise<PropertyStoreData> {\n return this.getState();\n }\n\n // Initialize property if it doesn't exist\n async initializeProperty(propertyId: string, slug: string): Promise<void> {\n await this.setState(state => {\n if (state.data[propertyId]) {\n return { ...state, propertyId, propertySlug: slug };\n }\n\n return {\n ...state,\n propertyId,\n propertySlug: slug,\n data: {\n ...state.data,\n [propertyId]: {\n id: propertyId,\n slug,\n favoritedUnits: [],\n tourContactedOn: null,\n viewedUnits: [],\n questionnaireResults: null,\n tourContactData: null,\n },\n },\n };\n });\n }\n}\n\n// Export singleton instance\nexport const propertyStore = new PropertyStore();","import { z } from \"zod\";\nimport { kvGet, kvSet } from \"../storage\";\n\n// App-related schemas\nexport const UnitDataSchema = z.object({\n isFavorite: z.boolean().optional(),\n viewedDate: z.string().optional(),\n});\n\nexport const FiltersSchema = z.object({\n availability: z.union([z.string(), z.array(z.string())]).nullable().optional(),\n bedrooms: z.array(z.number()).nullable().optional(),\n cost: z.number().nullable().optional(),\n highlights: z.array(z.string()).optional(),\n});\n\nexport const QueryParamsSchema = z.object({\n limit: z.number().default(10),\n page: z.number().default(1),\n availability: z.array(z.string()).optional(),\n bedrooms: z.array(z.number()).optional(),\n cost: z.number().nullable().optional(),\n highlights: z.array(z.string()).optional(),\n});\n\nexport const AppStoreDataSchema = z.object({\n data: z.record(UnitDataSchema),\n filters: FiltersSchema,\n tempFilters: FiltersSchema,\n apiFilters: QueryParamsSchema,\n resultsMode: z.enum([\"all\", \"bestFit\", \"closestMatch\", \"favorites\"]),\n propertySlug: z.string().nullable(),\n resolvedQuestionnaireValues: z.record(z.array(z.string())),\n sortBy: z.enum([\"relevance\", \"newest\", \"priceLowToHigh\", \"priceHighToLow\"]),\n filtersLoaded: z.boolean(),\n});\n\nexport type UnitData = z.infer<typeof UnitDataSchema>;\nexport type Filters = z.infer<typeof FiltersSchema>;\nexport type QueryParams = z.infer<typeof QueryParamsSchema>;\nexport type AppStoreData = z.infer<typeof AppStoreDataSchema>;\nexport type ResultsMode = \"all\" | \"bestFit\" | \"closestMatch\" | \"favorites\";\nexport type SortBy = \"relevance\" | \"newest\" | \"priceLowToHigh\" | \"priceHighToLow\";\n\n// Default values\nconst defaultFilters: Filters = {\n availability: undefined,\n bedrooms: undefined,\n cost: undefined,\n highlights: undefined,\n};\n\nconst defaultAppStoreData: AppStoreData = {\n data: {},\n filters: defaultFilters,\n tempFilters: defaultFilters,\n apiFilters: {\n limit: 10,\n page: 1,\n },\n resultsMode: \"all\",\n propertySlug: null,\n resolvedQuestionnaireValues: {},\n sortBy: \"relevance\",\n filtersLoaded: false,\n};\n\n// Core app store API\nexport class AppStore {\n private async getState(): Promise<AppStoreData> {\n const state = await kvGet<AppStoreData>(\"app\");\n return state ?? defaultAppStoreData;\n }\n\n private async setState(updater: (state: AppStoreData) => AppStoreData): Promise<void> {\n const currentState = await this.getState();\n const newState = updater(currentState);\n await kvSet(\"app\", newState);\n }\n\n // Unit data operations\n async setData(key: string, value: UnitData): Promise<void> {\n await this.setState(state => ({\n ...state,\n data: { ...state.data, [key]: value }\n }));\n }\n\n async removeData(key: string): Promise<void> {\n await this.setState(state => {\n const { [key]: removed, ...rest } = state.data;\n return { ...state, data: rest };\n });\n }\n\n async clearData(): Promise<void> {\n await this.setState(state => ({ ...state, data: {} }));\n }\n\n // Filter operations\n async setFilters(filters: Partial<Filters>): Promise<void> {\n await this.setState(state => ({\n ...state,\n filters: { ...state.filters, ...filters }\n }));\n }\n\n async setTempFilters(filters: Partial<Filters>): Promise<void> {\n await this.setState(state => ({\n ...state,\n tempFilters: { ...state.tempFilters, ...filters }\n }));\n }\n\n async setFiltersToDefault(): Promise<void> {\n await this.setState(state => ({ ...state, filters: defaultFilters }));\n }\n\n async setApiFilters(filters: Partial<QueryParams>): Promise<void> {\n await this.setState(state => ({\n ...state,\n apiFilters: { ...state.apiFilters, ...filters }\n }));\n }\n\n // Results and sorting\n async setResultsMode(mode: ResultsMode): Promise<void> {\n await this.setState(state => ({ ...state, resultsMode: mode }));\n }\n\n async setSortBy(sortBy: SortBy): Promise<void> {\n await this.setState(state => ({ ...state, sortBy }));\n }\n\n // Property operations\n async setPropertySlug(slug: string): Promise<void> {\n await this.setState(state => ({ ...state, propertySlug: slug }));\n }\n\n async getResultsUrl(): Promise<string | null> {\n const state = await this.getState();\n return state.propertySlug ? `/${state.propertySlug}/results` : null;\n }\n\n // Questionnaire values\n async setResolvedQuestionnaireValues(name: string, values: string[]): Promise<void> {\n await this.setState(state => ({\n ...state,\n resolvedQuestionnaireValues: {\n ...state.resolvedQuestionnaireValues,\n [name]: values\n }\n }));\n }\n\n // Complex filter operations\n async handleTempFilterChange<K extends keyof Filters>(\n key: K,\n value: Filters[K]\n ): Promise<void> {\n await this.setState(state => ({\n ...state,\n tempFilters: { ...state.tempFilters, [key]: value }\n }));\n }\n\n async commitTempFilterChange<K extends keyof Filters>(\n key: K,\n defaultValue: Filters[K]\n ): Promise<void> {\n const state = await this.getState();\n const value = state.tempFilters[key] ?? defaultValue;\n\n await this.handleTempFilterChange(key, value);\n await this.submitFilterUpdate();\n }\n\n async handleFilterCommitIndexDB(newFilters: Partial<Filters>): Promise<void> {\n await this.setState(state => {\n const apiParams: QueryParams = {\n ...state.apiFilters,\n availability: (newFilters.availability as string[]) || [],\n bedrooms: newFilters.bedrooms || [],\n cost: newFilters.cost || null,\n highlights: newFilters.highlights || [],\n };\n return {\n ...state,\n filters: { ...state.filters, ...newFilters },\n apiFilters: apiParams,\n };\n });\n }\n\n async commitAvailabilityChange(): Promise<void> {\n await this.submitFilterUpdate();\n }\n\n async submitFilterUpdate(): Promise<void> {\n await this.setState(state => {\n try {\n const apiParams: QueryParams = {\n ...state.apiFilters,\n availability: (state.filters.availability as string[]) || [],\n bedrooms: state.filters.bedrooms || [],\n cost: state.filters.cost || null,\n highlights: state.filters.highlights || [],\n };\n\n // Note: updateParams function call would need to be handled in consuming app\n // You might want to emit an event or use a callback for this\n\n return {\n ...state,\n apiFilters: apiParams,\n };\n } catch (error) {\n console.error(\"Error submitting filter update:\", error);\n return state;\n }\n });\n }\n\n // Persistence operations\n async loadPersistedFilters(): Promise<void> {\n // This method is now redundant since we're always loading from IndexedDB\n // But we'll keep it for compatibility and mark filters as loaded\n await this.setState(state => ({ ...state, filtersLoaded: true }));\n }\n\n // Utility methods\n async getUnitData(unitId: string): Promise<UnitData | null> {\n const state = await this.getState();\n return state.data[unitId] ?? null;\n }\n\n async getFilters(): Promise<Filters> {\n const state = await this.getState();\n return state.filters;\n }\n\n async getTempFilters(): Promise<Filters> {\n const state = await this.getState();\n return state.tempFilters;\n }\n\n async getApiFilters(): Promise<QueryParams> {\n const state = await this.getState();\n return state.apiFilters;\n }\n\n async getFullState(): Promise<AppStoreData> {\n return this.getState();\n }\n\n // Initialize with default values if needed\n async initialize(): Promise<void> {\n const state = await this.getState();\n if (Object.keys(state.data).length === 0 && !state.filtersLoaded) {\n await this.setState(state => ({\n ...defaultAppStoreData,\n ...state,\n filtersLoaded: true\n }));\n }\n }\n}\n\n// Export singleton instance\nexport const appStore = new AppStore();","import { z } from \"zod\";\nimport { kvGet, kvSet } from \"../storage\";\n\n// Combined store schemas\nexport const UnitDataSchema = z.object({\n isFavorite: z.boolean().optional(),\n viewedDate: z.string().optional(),\n});\n\nexport const ViewedUnitSchema = z.object({\n unitId: z.string(),\n viewedDate: z.string(),\n});\n\nexport const TourContactDataSchema = z.object({\n timezone: z.string(),\n favouriteUnits: z.array(z.string()).optional(),\n preferences: z.record(z.unknown()),\n});\n\nexport const PropertyDataSchema = z.object({\n id: z.string(),\n slug: z.string(),\n favoritedUnits: z.array(z.string()),\n tourContactedOn: z.string().nullable(),\n viewedUnits: z.array(ViewedUnitSchema),\n questionnaireResults: z.unknown().nullable().optional(),\n tourContactData: TourContactDataSchema.nullable().optional(),\n});\n\nexport const FiltersSchema = z.object({\n availability: z.union([z.string(), z.array(z.string())]).nullable().optional(),\n bedrooms: z.array(z.number()).nullable().optional(),\n cost: z.number().nullable().optional(),\n highlights: z.array(z.string()).optional(),\n});\n\nexport const QueryParamsSchema = z.object({\n limit: z.number().default(10),\n page: z.number().default(1),\n availability: z.array(z.string()).optional(),\n bedrooms: z.array(z.number()).optional(),\n cost: z.number().nullable().optional(),\n highlights: z.array(z.string()).optional(),\n});\n\nexport const UnifiedStoreDataSchema = z.object({\n // Property-related data\n properties: z.record(PropertyDataSchema),\n currentPropertyId: z.string().nullable(),\n currentPropertySlug: z.string().nullable(),\n hasPreviouslySearched: z.array(z.string()),\n\n // App-level data\n filters: FiltersSchema,\n tempFilters: FiltersSchema,\n apiFilters: QueryParamsSchema,\n resultsMode: z.enum([\"all\", \"bestFit\", \"closestMatch\", \"favorites\"]),\n resolvedQuestionnaireValues: z.record(z.array(z.string())),\n sortBy: z.enum([\"relevance\", \"newest\", \"priceLowToHigh\", \"priceHighToLow\"]),\n filtersLoaded: z.boolean(),\n});\n\nexport type UnitData = z.infer<typeof UnitDataSchema>;\nexport type ViewedUnit = z.infer<typeof ViewedUnitSchema>;\nexport type TourContactData = z.infer<typeof TourContactDataSchema>;\nexport type PropertyData = z.infer<typeof PropertyDataSchema>;\nexport type Filters = z.infer<typeof FiltersSchema>;\nexport type QueryParams = z.infer<typeof QueryParamsSchema>;\nexport type UnifiedStoreData = z.infer<typeof UnifiedStoreDataSchema>;\nexport type ResultsMode = \"all\" | \"bestFit\" | \"closestMatch\" | \"favorites\";\nexport type SortBy = \"relevance\" | \"newest\" | \"priceLowToHigh\" | \"priceHighToLow\";\n\n// Default values\nconst defaultFilters: Filters = {\n availability: undefined,\n bedrooms: undefined,\n cost: undefined,\n highlights: undefined,\n};\n\nconst defaultUnifiedStoreData: UnifiedStoreData = {\n // Property data\n properties: {},\n currentPropertyId: null,\n currentPropertySlug: null,\n hasPreviouslySearched: [],\n\n // App data\n filters: defaultFilters,\n tempFilters: defaultFilters,\n apiFilters: {\n limit: 10,\n page: 1,\n },\n resultsMode: \"all\",\n resolvedQuestionnaireValues: {},\n sortBy: \"relevance\",\n filtersLoaded: false,\n};\n\n// Unified store class\nexport class UnifiedStore {\n private async getState(): Promise<UnifiedStoreData> {\n const state = await kvGet<UnifiedStoreData>(\"app\");\n if (!state) {\n return defaultUnifiedStoreData;\n }\n\n // Ensure all required fields exist (defensive programming)\n return {\n ...defaultUnifiedStoreData,\n ...state,\n properties: state.properties ?? {},\n };\n }\n\n private async setState(updater: (state: UnifiedStoreData) => UnifiedStoreData): Promise<void> {\n const currentState = await this.getState();\n const newState = updater(currentState);\n await kvSet(\"app\", newState);\n }\n\n // === PROPERTY OPERATIONS ===\n\n async initializeProperty(propertyId: string, slug: string): Promise<void> {\n await this.setState(state => {\n if (state.properties && state.properties[propertyId]) {\n return {\n ...state,\n currentPropertyId: propertyId,\n currentPropertySlug: slug\n };\n }\n\n return {\n ...state,\n currentPropertyId: propertyId,\n currentPropertySlug: slug,\n properties: {\n ...state.properties,\n [propertyId]: {\n id: propertyId,\n slug,\n favoritedUnits: [],\n tourContactedOn: null,\n viewedUnits: [],\n questionnaireResults: null,\n tourContactData: null,\n },\n },\n };\n });\n }\n\n async setCurrentProperty(propertyId: string, slug?: string): Promise<void> {\n await this.setState(state => ({\n ...state,\n currentPropertyId: propertyId,\n currentPropertySlug: slug || state.currentPropertySlug,\n }));\n }\n\n async setCurrentPropertySlug(slug: string): Promise<void> {\n await this.setState(state => ({\n ...state,\n currentPropertySlug: slug\n }));\n }\n\n async setHasPreviouslySearched(slug: string): Promise<void> {\n await this.setState(state => ({\n ...state,\n hasPreviouslySearched: Array.from(\n new Set([...state.hasPreviouslySearched, slug])\n ),\n }));\n }\n\n async toggleFavorite(unitId: string): Promise<void> {\n await this.setState(state => {\n const propertyId = state.currentPropertyId;\n if (!propertyId) return state;\n\n const property = state.properties[propertyId];\n if (!property) return state;\n\n const isFavorited = property.favoritedUnits.includes(unitId);\n const updatedFavoritedUnits = isFavorited\n ? property.favoritedUnits.filter((id) => id !== unitId)\n : [...property.favoritedUnits, unitId];\n\n return {\n ...state,\n properties: {\n ...state.properties,\n [propertyId]: {\n ...property,\n favoritedUnits: updatedFavoritedUnits,\n },\n },\n };\n });\n }\n\n async markUnitAsViewed(unitId: string, slug: string): Promise<void> {\n const today = new Date();\n const formattedDate = `${String(today.getMonth() + 1).padStart(\n 2,\n \"0\"\n )}/${String(today.getDate()).padStart(2, \"0\")}`;\n\n await this.setState(state => {\n const propertyId = state.currentPropertyId;\n if (!propertyId) return state;\n\n const property = state.properties[propertyId];\n if (!property) return state;\n\n const updatedViewedUnits = [\n ...property.viewedUnits.filter((u) => u.unitId !== unitId),\n { unitId, viewedDate: formattedDate },\n ];\n\n return {\n ...state,\n properties: {\n ...state.properties,\n [propertyId]: {\n ...property,\n viewedUnits: updatedViewedUnits,\n },\n },\n };\n });\n\n if (typeof window !== 'undefined') {\n window.open(`//${slug}`, \"_blank\");\n }\n }\n\n async setTourContactedOn(): Promise<void> {\n await this.setState(state => {\n const propertyId = state.currentPropertyId;\n if (!propertyId) return state;\n\n const property = state.properties[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n properties: {\n ...state.properties,\n [propertyId]: {\n ...property,\n tourContactedOn: new Date().toISOString(),\n },\n },\n };\n });\n }\n\n async getTourContactedOn(): Promise<string | null> {\n const state = await this.getState();\n const propertyId = state.currentPropertyId;\n if (!propertyId) return null;\n\n return state.properties[propertyId]?.tourContactedOn ?? null;\n }\n\n async setQuestionnaireResults(results: unknown): Promise<void> {\n await this.setState(state => {\n const propertyId = state.currentPropertyId;\n if (!propertyId) return state;\n\n const property = state.properties[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n properties: {\n ...state.properties,\n [propertyId]: {\n ...property,\n questionnaireResults: results,\n },\n },\n };\n });\n }\n\n async setTourContactData(data: TourContactData): Promise<void> {\n await this.setState(state => {\n const propertyId = state.currentPropertyId;\n if (!propertyId) return state;\n\n const property = state.properties[propertyId];\n if (!property) return state;\n\n return {\n ...state,\n properties: {\n ...state.properties,\n [propertyId]: {\n ...property,\n tourContactData: data,\n },\n },\n };\n });\n }\n\n\n\n\n\n\n\n // === FILTER OPERATIONS ===\n\n async setFilters(filters: Partial<Filters>): Promise<void> {\n await this.setState(state => ({\n ...state,\n filters: { ...state.filters, ...filters }\n }));\n }\n\n async setTempFilters(filters: Partial<Filters>): Promise<void> {\n await this.setState(state => ({\n ...state,\n tempFilters: { ...state.tempFilters, ...filters }\n }));\n }\n\n async setFiltersToDefault(): Promise<void> {\n await this.setState(state => ({ ...state, filters: defaultFilters }));\n }\n\n async setApiFilters(filters: Partial<QueryParams>): Promise<void> {\n await this.setState(state => ({\n ...state,\n apiFilters: { ...state.apiFilters, ...filters }\n }));\n }\n\n async handleTempFilterChange<K extends keyof Filters>(\n key: K,\n value: Filters[K]\n ): Promise<void> {\n await this.setState(state => ({\n ...state,\n tempFilters: { ...state.tempFilters, [key]: value }\n }));\n }\n\n async commitTempFilterChange<K extends keyof Filters>(\n key: K,\n defaultValue: Filters[K]\n ): Promise<void> {\n const state = await this.getState();\n const value = state.tempFilters[key] ?? defaultValue;\n\n await this.handleTempFilterChange(key, value);\n await this.submitFilterUpdate();\n }\n\n async handleFilterCommitIndexDB(newFilters: Partial<Filters>): Promise<void> {\n await this.setState(state => {\n const apiParams: QueryParams = {\n ...state.apiFilters,\n availability: (newFilters.availability as string[]) || [],\n bedrooms: newFilters.bedrooms || [],\n cost: newFilters.cost || null,\n highlights: newFilters.highlights || [],\n };\n return {\n ...state,\n filters: { ...state.filters, ...newFilters },\n apiFilters: apiParams,\n };\n });\n }\n\n async commitAvailabilityChange(): Promise<void> {\n await this.submitFilterUpdate();\n }\n\n async submitFilterUpdate(): Promise<void> {\n await this.setState(state => {\n const apiParams: QueryParams = {\n ...state.apiFilters,\n availability: (state.filters.availability as string[]) || [],\n bedrooms: state.filters.bedrooms || [],\n cost: state.filters.cost || null,\n highlights: state.filters.highlights || [],\n };\n\n return {\n ...state,\n apiFilters: apiParams,\n };\n });\n }\n\n // === RESULTS AND SORTING ===\n\n async setResultsMode(mode: ResultsMode): Promise<void> {\n await this.setState(state => ({ ...state, resultsMode: mode }));\n }\n\n async setSortBy(sortBy: SortBy): Promise<void> {\n await this.setState(state => ({ ...state, sortBy }));\n }\n\n // === QUESTIONNAIRE ===\n\n async setResolvedQuestionnaireValues(name: string, values: string[]): Promise<void> {\n await this.setState(state => ({\n ...state,\n resolvedQuestionnaireValues: {\n ...state.resolvedQuestionnaireValues,\n [name]: values\n }\n }));\n }\n\n // === UTILITY METHODS ===\n\n async getUnitState(unitId: string): Promise<{\n isFavorite: boolean;\n viewedDate: string;\n }> {\n const state = await this.getState();\n const property = state.currentPropertyId ? state.properties[state.currentPropertyId] : null;\n\n return {\n isFavorite: property?.favoritedUnits.includes(unitId) ?? false,\n viewedDate:\n property?.viewedUnits.find((u) => u.unitId === unitId)?.viewedDate ?? \"\",\n };\n }\n\n async getResultsUrl(): Promise<string | null> {\n const state = await this.getState();\n return state.currentPropertySlug ? `/${state.currentPropertySlug}/results` : null;\n }\n\n async getCurrentProperty(): Promise<PropertyData | null> {\n const state = await this.getState();\n return state.currentPropertyId ? state.properties[state.currentPropertyId] ?? null : null;\n }\n\n async getPropertyData(propertyId?: string): Promise<PropertyData | null> {\n const state = await this.getState();\n const id = propertyId ?? state.currentPropertyId;\n return id ? state.properties[id] ?? null : null;\n }\n\n async getFullState(): Promise<UnifiedStoreData> {\n return this.getState();\n }\n\n async initialize(): Promise<void> {\n const state = await this.getState();\n if (Object.keys(state.properties).length === 0 && !state.filtersLoaded) {\n await this.setState(state => ({\n ...defaultUnifiedStoreData,\n ...state,\n filtersLoaded: true\n }));\n }\n }\n\n async loadPersistedFilters(): Promise<void> {\n // Mark filters as loaded since we're always loading from IndexedDB\n await this.setState(state => ({ ...state, filtersLoaded: true }));\n }\n\n // === LEGACY COMPATIBILITY METHODS ===\n\n // Property store compatibility\n async setData(value: Record<string, PropertyData>): Promise<void> {\n await this.setState(state => ({ ...state, properties: value }));\n }\n\n async setPropertySlug(slug: string): Promise<void> {\n await this.setCurrentPropertySlug(slug);\n }\n\n async setPropertyId(id: string): Promise<void> {\n await this.setCurrentProperty(id);\n }\n\n async removeData(key: string): Promise<void> {\n await this.setState(state => {\n const { [key]: removed, ...rest } = state.properties;\n return { ...state, properties: rest };\n });\n }\n\n async clearData(): Promise<void> {\n await this.setState(state => ({ ...state, properties: {} }));\n }\n}\n\n// Export singleton instance\nexport const store = new UnifiedStore();","import { z } from \"zod\";\n\nexport const SCHEMA_VERSION = 1 as const;\n\n// Entities\nexport const UserSchema = z.object({\n uuid: z.string().uuid(),\n});\nexport type User = z.infer<typeof UserSchema>;\n\n// favorites\nexport const FavoritesSchema = z.object({\n propertyId: z.string() || z.number(),\n unitIds: z.array(z.string()),\n});\nexport type Favorites = z.infer<typeof FavoritesSchema>;\n\n// Re-export property schemas from the API module\nexport {\n ViewedUnitSchema,\n TourContactDataSchema,\n PropertyDataSchema,\n PropertyStoreDataSchema,\n type ViewedUnit,\n type TourContactData,\n type PropertyData,\n type PropertyStoreData,\n} from \"./api/properties\";\n\n// Re-export app schemas from the API module\nexport {\n UnitDataSchema,\n FiltersSchema,\n QueryParamsSchema,\n AppStoreDataSchema,\n type UnitData,\n type Filters,\n type QueryParams,\n type AppStoreData,\n type ResultsMode,\n type SortBy,\n} from \"./api/app\";\n\n// Re-export unified store schemas\nexport {\n UnifiedStoreDataSchema,\n type UnifiedStoreData,\n} from \"./stores/unified\";\n","import { getDB } from \"../db\";\nimport { UserSchema, type User } from \"../schema\";\n\nconst USER_POINTER_KEY = \"user\";\n\nexport type IdGenerator = () => string;\nexport const defaultIdGenerator: IdGenerator = () =>\n typeof globalThis.crypto?.randomUUID === \"function\"\n ? globalThis.crypto.randomUUID()\n : (() => {\n const b =\n globalThis.crypto?.getRandomValues?.(new Uint8Array(16)) ??\n Uint8Array.from({ length: 16 }, () => (Math.random() * 256) | 0);\n b[6] = (b[6] & 0x0f) | 0x40; // v4\n b[8] = (b[8] & 0x3f) | 0x80; // variant\n const h = [...b].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n })();\n\nconst getPointerUuid = (row: any) => row?.value?.useruuid ?? row?.value?.uuid;\n\nexport async function ensureUser(\n gen: IdGenerator = defaultIdGenerator,\n): Promise<User> {\n const db = await getDB();\n\n // Fast path\n const ptrUuid = getPointerUuid(await db.kv.get(USER_POINTER_KEY));\n if (ptrUuid) {\n const existing = await db.users.get(ptrUuid);\n if (existing) return UserSchema.parse(existing);\n }\n\n // Race-safe creation\n try {\n return await db.transaction(\"rw\", db.kv, db.users, async () => {\n const insideUuid = getPointerUuid(await db.kv.get(USER_POINTER_KEY));\n if (insideUuid) {\n const existing = await db.users.get(insideUuid);\n if (existing) return UserSchema.parse(existing);\n }\n\n const uuid = gen();\n await db.kv.add({ key: USER_POINTER_KEY, value: { useruuid: uuid } }); // claim pointer\n const newUser: User = UserSchema.parse({\n uuid,\n createdAt: new Date().toISOString(),\n });\n await db.users.add(newUser);\n return newUser;\n });\n } catch (e: any) {\n if (e?.name === \"ConstraintError\") {\n // Lost the race → read winner\n const uuid = getPointerUuid(await db.kv.get(USER_POINTER_KEY));\n if (uuid) {\n const winner = await db.users.get(uuid);\n if (winner) return UserSchema.parse(winner);\n }\n }\n throw e;\n }\n}\n\nexport const getUserUUID = async (gen?: IdGenerator) =>\n (await ensureUser(gen)).uuid;\n","import { getDB } from \"./db\";\n\nexport async function debugDump(): Promise<Record<string, unknown[]>> {\n const db = await getDB();\n const out: Record<string, unknown[]> = {};\n for (const t of db.tables) {\n out[t.name] = await t.toArray();\n }\n return out;\n}\n\nexport async function exportJSON(filename = \"inresi-orm-export.json\"): Promise<void> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n throw new Error(\"exportJSON can only run in a browser.\");\n }\n const snapshot = await debugDump();\n const blob = new Blob([JSON.stringify(snapshot, null, 2)], { type: \"application/json\" });\n const a = document.createElement(\"a\");\n a.href = URL.createObjectURL(blob);\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(a.href);\n}\n","// src/api/favourites.ts\nimport { getDB } from \"../db\";\nimport { ensureUser } from \"./users\";\nimport { FavoritesSchema } from \"../schema\";\n\nconst keyFor = (userId: string, propertyId: string) =>\n `favorites:${userId}:${propertyId}`;\n\nexport async function getFavoritedUnitsForProperty(\n propertyId: string | number,\n): Promise<string[]> {\n const db = await getDB();\n const { uuid } = await ensureUser();\n const key = keyFor(uuid, String(propertyId));\n const row = await db.kv.get(key);\n const parsed = row ? FavoritesSchema.safeParse(row.value) : null;\n return parsed?.success ? parsed.data.unitIds : [];\n}\n\nexport async function setFavoriteUnit(\n propertyId: string | number,\n unitId: string,\n on: boolean,\n): Promise<string[]> {\n const db = await getDB();\n const { uuid } = await ensureUser();\n const key = keyFor(uuid, String(propertyId));\n\n return db.transaction(\"rw\", db.kv, async () => {\n const row = await db.kv.get(key);\n const current =\n row && FavoritesSchema.safeParse(row.value).success\n ? (row!.value as any)\n : { unitIds: [] as string[], updatedAt: new Date().toISOString() };\n\n const nextSet = new Set<string>(current.unitIds);\n on ? nextSet.add(unitId) : nextSet.delete(unitId);\n\n const next = {\n unitIds: [...nextSet],\n updatedAt: new Date().toISOString(),\n };\n const validated = FavoritesSchema.parse(next);\n await db.kv.put({ key, value: validated });\n return validated.unitIds;\n });\n}\n\nexport async function toggleFavoriteUnit(\n propertyId: string | number,\n unitId: string,\n): Promise<string[]> {\n const current = await getFavoritedUnitsForProperty(propertyId);\n const on = !current.includes(unitId);\n return setFavoriteUnit(propertyId, unitId, on);\n}\n\nexport async function isUnitFavorited(\n propertyId: string | number,\n unitId: string,\n): Promise<boolean> {\n const list = await getFavoritedUnitsForProperty(propertyId);\n return list.includes(unitId);\n}\n","import { store, type UnifiedStoreData, type UnitData, type PropertyData, type Filters, type QueryParams, type ResultsMode, type SortBy, type TourContactData } from \"../stores/unified\";\n\n// Unified Zustand store interface\nexport interface ZustandUnifiedStoreState {\n // Property data\n properties: Record<string, PropertyData>;\n currentPropertyId: string | null;\n currentPropertySlug: string | null;\n hasPreviouslySearched: string[];\n\n // App data\n units: Record<string, UnitData>;\n filters: Filters;\n tempFilters: Filters;\n apiFilters: QueryParams;\n resultsMode: ResultsMode;\n resolvedQuestionnaireValues: Record<string, string[]>;\n sortBy: SortBy;\n filtersLoaded: boolean;\n\n // === PROPERTY OPERATIONS ===\n initializeProperty: (propertyId: string, slug: string) => Promise<void>;\n setCurrentProperty: (propertyId: string, slug?: string) => Promise<void>;\n setCurrentPropertySlug: (slug: string) => Promise<void>;\n setHasPreviouslySearched: (slug: string) => Promise<void>;\n toggleFavorite: (unitId: string) => Promise<void>;\n markUnitAsViewed: (unitId: string, slug: string) => Promise<void>;\n setTourContactedOn: () => Promise<void>;\n getTourContactedOn: () => Promise<string | null>;\n setQuestionnaireResults: (results: unknown) => Promise<void>;\n setTourContactData: (data: TourContactData) => Promise<void>;\n\n\n // === FILTER OPERATIONS ===\n setFilters: (filters: Partial<Filters>) => Promise<void>;\n setTempFilters: (filters: Partial<Filters>) => Promise<void>;\n setFiltersToDefault: () => Promise<void>;\n setApiFilters: (filters: Partial<QueryParams>) => Promise<void>;\n handleTempFilterChange: <K extends keyof Filters>(key: K, value: Filters[K]) => Promise<void>;\n commitTempFilterChange: <K extends keyof Filters>(key: K, defaultValue: Filters[K]) => Promise<void>;\n handleFilterCommitIndexDB: (newFilters: Partial<Filters>) => Promise<void>;\n commitAvailabilityChange: () => Promise<void>;\n submitFilterUpdate: () => Promise<void>;\n\n // === RESULTS AND SORTING ===\n setResultsMode: (mode: ResultsMode) => Promise<void>;\n setSortBy: (sortBy: SortBy) => Promise<void>;\n\n // === QUESTIONNAIRE ===\n setResolvedQuestionnaireValues: (name: string, values: string[]) => Promise<void>;\n\n // === UTILITY METHODS ===\n getUnitState: (unitId: string) => {\n isFavorite: boolean;\n viewedDate: string;\n };\n getResultsUrl: () => Promise<string | null>;\n getCurrentProperty: () => Promise<PropertyData | null>;\n getPropertyData: (propertyId?: string) => Promise<PropertyData | null>;\n\n // === LEGACY COMPATIBILITY ===\n setData: (value: Record<string, PropertyData>) => Promise<void>;\n setPropertySlug: (slug: string) => Promise<void>;\n setPropertyId: (id: string) => Promise<void>;\n removeData: (key: string) => Promise<void>;\n clearData: () => Promise<void>;\n loadPersistedFilters: () => Promise<void>;\n\n // === INTERNAL ===\n _hydrate: () => Promise<void>;\n _initialize: () => Promise<void>;\n}\n\nexport function createZustandUnifiedStore(options?: {\n onFilterUpdate?: (apiParams: QueryParams) => void;\n}) {\n return (set: any, get: any): ZustandUnifiedStoreState => {\n // Helper to update local state after ORM operations\n const syncState = async () => {\n const ormState = await store.getFullState();\n set({\n properties: ormState.properties,\n currentPropertyId: ormState.currentPropertyId,\n currentPropertySlug: ormState.currentPropertySlug,\n hasPreviouslySearched: ormState.hasPreviouslySearched,\n filters: ormState.filters,\n tempFilters: ormState.tempFilters,\n apiFilters: ormState.apiFilters,\n resultsMode: ormState.resultsMode,\n resolvedQuestionnaireValues: ormState.resolvedQuestionnaireValues,\n sortBy: ormState.sortBy,\n filtersLoaded: ormState.filtersLoaded,\n });\n };\n\n // Helper to notify of filter updates\n const notifyFilterUpdate = async () => {\n if (options?.onFilterUpdate) {\n const apiFilters = (await store.getFullState()).apiFilters;\n options.onFilterUpdate(apiFilters);\n }\n };\n\n return {\n // Initial state\n properties: {},\n currentPropertyId: null,\n currentPropertySlug: null,\n hasPreviouslySearched: [],\n units: {},\n filters: {\n availability: undefined,\n bedrooms: undefined,\n cost: undefined,\n highlights: undefined,\n },\n tempFilters: {\n availability: undefined,\n bedrooms: undefined,\n cost: undefined,\n highlights: undefined,\n },\n apiFilters: {\n limit: 10,\n page: 1,\n },\n resultsMode: \"all\",\n resolvedQuestionnaireValues: {},\n sortBy: \"relevance\",\n filtersLoaded: false,\n\n // === PROPERTY OPERATIONS ===\n async initializeProperty(propertyId, slug) {\n await store.initializeProperty(propertyId, slug);\n await syncState();\n },\n\n async setCurrentProperty(propertyId, slug) {\n await store.setCurrentProperty(propertyId, slug);\n set({\n currentPropertyId: propertyId,\n ...(slug && { currentPropertySlug: slug })\n });\n },\n\n async setCurrentPropertySlug(slug) {\n await store.setCurrentPropertySlug(slug);\n set({ currentPropertySlug: slug });\n },\n\n async setHasPreviouslySearched(slug) {\n await store.setHasPreviouslySearched(slug);\n await syncState();\n },\n\n async toggleFavorite(unitId) {\n await store.toggleFavorite(unitId);\n await syncState();\n },\n\n async markUnitAsViewed(unitId, slug) {\n await store.markUnitAsViewed(unitId, slug);\n await syncState();\n },\n\n async setTourContactedOn() {\n await store.setTourContactedOn();\n await syncState();\n },\n\n getTourContactedOn: store.getTourContactedOn.bind(store),\n\n async setQuestionnaireResults(results) {\n await store.setQuestionnaireResults(results);\n await syncState();\n },\n\n async setTourContactData(data) {\n await store.setTourContactData(data);\n await syncState();\n },\n\n\n // === FILTER OPERATIONS ===\n async setFilters(filters) {\n await store.setFilters(filters);\n const state = get();\n set({ filters: { ...state.filters, ...filters } });\n },\n\n async setTempFilters(filters) {\n await store.setTempFilters(filters);\n const state = get();\n set({ tempFilters: { ...state.tempFilters, ...filters } });\n },\n\n async setFiltersToDefault() {\n await store.setFiltersToDefault();\n const defaultFilters = {\n availability: undefined,\n bedrooms: undefined,\n cost: undefined,\n highlights: undefined,\n };\n set({ filters: defaultFilters });\n },\n\n async setApiFilters(filters) {\n await store.setApiFilters(filters);\n const state = get();\n set({ apiFilters: { ...state.apiFilters, ...filters } });\n },\n\n async handleTempFilterChange(key, value) {\n await store.handleTempFilterChange(key, value);\n const state = get();\n set({ tempFilters: { ...state.tempFilters, [key]: value } });\n },\n\n async commitTempFilterChange(key, defaultValue) {\n await store.commitTempFilterChange(key, defaultValue);\n await syncState();\n await notifyFilterUpdate();\n },\n\n async handleFilterCommitIndexDB(newFilters) {\n await store.handleFilterCommitIndexDB(newFilters);\n await syncState();\n await notifyFilterUpdate();\n },\n\n async commitAvailabilityChange() {\n await store.commitAvailabilityChange();\n await syncState();\n await notifyFilterUpdate();\n },\n\n async submitFilterUpdate() {\n await store.submitFilterUpdate();\n await syncState();\n await notifyFilterUpdate();\n },\n\n // === RESULTS AND SORTING ===\n async setResultsMode(mode) {\n await store.setResultsMode(mode);\n set({ resultsMode: mode });\n },\n\n async setSortBy(sortBy) {\n await store.setSortBy(sortBy);\n set({ sortBy });\n },\n\n // === QUESTIONNAIRE ===\n async setResolvedQuestionnaireValues(name, values) {\n await store.setResolvedQuestionnaireValues(name, values);\n const state = get();\n set({\n resolvedQuestionnaireValues: {\n ...state.resolvedQuestionnaireValues,\n [name]: values,\n }\n });\n },\n\n // === UTILITY METHODS ===\n getUnitState(unitId: string) {\n const state = get();\n const property = state.currentPropertyId ? state.properties[state.currentPropertyId] : null;\n\n return {\n isFavorite: property?.favoritedUnits.includes(unitId) ?? false,\n viewedDate:\n property?.viewedUnits.find((u: any) => u.unitId === unitId)?.viewedDate ?? \"\",\n };\n },\n\n getResultsUrl: store.getResultsUrl.bind(store),\n getCurrentProperty: store.getCurrentProperty.bind(store),\n getPropertyData: store.getPropertyData.bind(store),\n\n // === LEGACY COMPATIBILITY ===\n async setData(value) {\n await store.setData(value);\n set({ properties: value });\n },\n\n async setPropertySlug(slug) {\n await store.setPropertySlug(slug);\n set({ currentPropertySlug: slug });\n },\n\n async setPropertyId(id) {\n await store.setPropertyId(id);\n set({ currentPropertyId: id });\n },\n\n async removeData(key) {\n await store.removeData(key);\n await syncState();\n },\n\n async clearData() {\n await store.clearData();\n set({ properties: {} });\n },\n\n async loadPersistedFilters() {\n await store.loadPersistedFilters();\n set({ filtersLoaded: true });\n },\n\n // === INTERNAL ===\n async _hydrate() {\n await syncState();\n },\n\n async _initialize() {\n await store.initialize();\n await syncState();\n },\n };\n };\n}\n\n// Helper hook factory for unit state\nexport function createUseUnitState() {\n return (useStore: any) => (unitId: string) =>\n useStore((state: ZustandUnifiedStoreState) => state.getUnitState(unitId));\n}\n\n// Export types for consuming apps\nexport type {\n UnitData,\n PropertyData,\n Filters,\n QueryParams,\n ResultsMode,\n SortBy,\n TourContactData\n} from \"../stores/unified\";","import {\n type ZustandUnifiedStoreState,\n createZustandUnifiedStore,\n type Filters,\n type UnitData,\n type TourContactData,\n type QueryParams\n} from \"./zustand-unified\";\n\nexport interface StructuredStoreActions {\n property: {\n unit: {\n favorites: {\n toggle: (unitId: string) => Promise<void>;\n };\n viewed: {\n mark: (unitId: string, slug: string) => Promise<void>;\n };\n };\n questionnaire: {\n setResults: (results: unknown) => Promise<void>;\n };\n tour: {\n setContactedOn: () => Promise<void>;\n getContactedOn: () => Promise<string | null>;\n setContactData: (data: TourContactData) => Promise<void>;\n };\n };\n\n filters: {\n set: (filters: Partial<Filters>) => Promise<void>;\n setTemp: (filters: Partial<Filters>) => Promise<void>;\n setToDefault: () => Promise<void>;\n commitTemp: <K extends keyof Filters>(key: K, defaultValue: Filters[K]) => Promise<void>;\n commitAvailability: () => Promise<void>;\n submit: () => Promise<void>;\n };\n}\n\nexport function createStructuredStoreActions(store: ZustandUnifiedStoreState): StructuredStoreActions {\n return {\n property: {\n unit: {\n favorites: {\n toggle: store.toggleFavorite.bind(store),\n },\n viewed: {\n mark: store.markUnitAsViewed.bind(store),\n },\n },\n questionnaire: {\n setResults: store.setQuestionnaireResults.bind(store),\n },\n tour: {\n setContactedOn: store.setTourContactedOn.bind(store),\n getContactedOn: store.getTourContactedOn.bind(store),\n setContactData: store.setTourContactData.bind(store),\n },\n },\n filters: {\n set: store.setFilters.bind(store),\n setTemp: store.setTempFilters.bind(store),\n setToDefault: store.setFiltersToDefault.bind(store),\n commitTemp: store.commitTempFilterChange.bind(store),\n commitAvailability: store.commitAvailabilityChange.bind(store),\n submit: store.submitFilterUpdate.bind(store),\n },\n };\n}\n\nexport type StructuredStore = ZustandUnifiedStoreState & StructuredStoreActions;\n\nexport function createStructuredStore(options?: {\n onFilterUpdate?: (apiParams: QueryParams) => void;\n}) {\n return (set: any, get: any): StructuredStore => {\n const baseStore = createZustandUnifiedStore(options)(set, get);\n const actions = createStructuredStoreActions(baseStore);\n\n return {\n ...baseStore,\n ...actions,\n };\n };\n}","// src/units/favorites.ts\nimport * as api from \"../api/favorites\";\n\nconst favorites = {\n async isFavorite(propertyId: string | number, unitId: string): Promise<boolean> {\n return api.isUnitFavorited(propertyId, unitId);\n },\n async toggle(propertyId: string | number, unitId: string): Promise<string[]> {\n return api.toggleFavoriteUnit(propertyId, unitId);\n },\n async set(propertyId: string | number, unitId: string, on: boolean): Promise<string[]> {\n return api.setFavoriteUnit(propertyId, unitId, on);\n },\n async getAll(propertyId: string | number): Promise<string[]> {\n return api.getFavoritedUnitsForProperty(propertyId);\n },\n};\n\nexport {favorites}\n"],"names":["SchemaMismatchError","message","detail","OpenDBError","OrmDexie","Dexie","name","SCHEMA_VERSION","mode","onIssue","configureValidation","opts","validate","schema","value","ctx","res","details","MANIFEST_KEY","ManifestSchema","z","db","openPromise","VALIDATION_INSTALLED","installValidationHooks","instance","vopts","anyDb","table","_pk","obj","v","mods","next","UserSchema","dropInvalid","isProbablyDev","__vite_import_meta_env__","getDB","defaultMode","_a","_b","row","saved","parsed","fresh","_c","e","_d","resetDB","dbName","kvGet","key","kvSet","kvRemove","createORMStringStorage","mem","prefix","toKey","ViewedUnitSchema","TourContactDataSchema","PropertyDataSchema","PropertyStoreDataSchema","defaultPropertyStoreData","PropertyStore","updater","currentState","newState","state","slug","id","k","acc","propertyId","property","results","data","unitId","updatedFavoritedUnits","today","formattedDate","updatedViewedUnits","u","propertyStore","UnitDataSchema","FiltersSchema","QueryParamsSchema","AppStoreDataSchema","UnifiedStoreDataSchema","defaultFilters","defaultUnifiedStoreData","UnifiedStore","filters","defaultValue","newFilters","apiParams","sortBy","values","removed","rest","store","FavoritesSchema","USER_POINTER_KEY","defaultIdGenerator","b","h","x","getPointerUuid","ensureUser","gen","ptrUuid","existing","insideUuid","uuid","newUser","winner","getUserUUID","debugDump","out","t","exportJSON","filename","snapshot","blob","a","keyFor","userId","getFavoritedUnitsForProperty","setFavoriteUnit","on","current","nextSet","validated","toggleFavoriteUnit","isUnitFavorited","createZustandUnifiedStore","options","set","get","syncState","ormState","notifyFilterUpdate","apiFilters","createUseUnitState","useStore","createStructuredStoreActions","createStructuredStore","baseStore","actions","favorites","api.isUnitFavorited","api.toggleFavoriteUnit","api.setFavoriteUnit","api.getFavoritedUnitsForProperty"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superbright/indexeddb-orm",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Vite + TypeScript starter for an IndexedDB ORM (Dexie + Zod) with playground.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -9,32 +9,15 @@
9
9
  "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "import": {
13
- "types": "./dist/index.d.ts",
14
- "default": "./dist/index.es.js"
15
- },
16
- "require": {
17
- "types": "./dist/index.d.ts",
18
- "default": "./dist/index.cjs.js"
19
- }
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.cjs"
20
15
  }
21
16
  },
22
17
  "sideEffects": false,
23
- "scripts": {
24
- "clean": "rimraf dist",
25
- "build": "pnpm clean && vite build && tsc -p tsconfig.build.json",
26
- "prepare": "pnpm build",
27
- "start": "vite --clearScreen false",
28
- "dev": "concurrently -k -n PLAY,BUILD \"pnpm:start\" \"pnpm:watch\"",
29
- "watch": "concurrently -k -n TYPES,BUNDLE \"pnpm:watch:types\" \"pnpm:watch:bundle\"",
30
- "watch:types": "tsc -w -p tsconfig.build.json",
31
- "watch:bundle": "vite build --watch",
32
- "test": "vitest run",
33
- "test:watch": "vitest",
34
- "format": "pnpm format:write",
35
- "format:write": "prettier --write .",
36
- "format:check": "prettier --check ."
37
- },
18
+ "files": [
19
+ "dist"
20
+ ],
38
21
  "dependencies": {
39
22
  "dexie": "^4.0.8",
40
23
  "zod": "^3.23.8"
@@ -49,5 +32,19 @@
49
32
  "typescript": "^5.4.5",
50
33
  "vite": "^5.4.0",
51
34
  "vitest": "^2.0.5"
35
+ },
36
+ "scripts": {
37
+ "clean": "rimraf dist",
38
+ "build": "pnpm clean && vite build && tsc -p tsconfig.build.json",
39
+ "start": "vite --clearScreen false",
40
+ "dev": "concurrently -k -n PLAY,BUILD \"pnpm:start\" \"pnpm:watch\"",
41
+ "watch": "concurrently -k -n TYPES,BUNDLE \"pnpm:watch:types\" \"pnpm:watch:bundle\"",
42
+ "watch:types": "tsc -w -p tsconfig.build.json",
43
+ "watch:bundle": "vite build --watch",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "format": "pnpm format:write",
47
+ "format:write": "prettier --write .",
48
+ "format:check": "prettier --check ."
52
49
  }
53
- }
50
+ }
package/.prettierignore DELETED
@@ -1,9 +0,0 @@
1
- dist
2
- node_modules
3
- coverage
4
- *.log
5
- .vite
6
- .vscode
7
- .DS_Store
8
- *.zip
9
- inresi-orm-export.json
package/.prettierrc.cjs DELETED
@@ -1,13 +0,0 @@
1
- /** @type {import("prettier").Config} */
2
- export default {
3
- printWidth: 100,
4
- tabWidth: 2,
5
- useTabs: false,
6
- semi: true,
7
- singleQuote: true,
8
- trailingComma: "all",
9
- bracketSpacing: true,
10
- arrowParens: "always",
11
- endOfLine: "lf",
12
- plugins: ["prettier-plugin-packagejson"],
13
- };
@@ -1,164 +0,0 @@
1
- /**
2
- * Consuming App Integration Guide
3
- *
4
- * Replace your existing stores with a single unified ORM-backed store
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 for the UNIFIED STORE:
13
- import { create } from "zustand";
14
- import { devtools } from "zustand/middleware";
15
- import {
16
- createZustandPropertyStore, // More intuitive name (alias for createZustandUnifiedStore)
17
- createUseUnitState,
18
- type ZustandUnifiedStoreState,
19
- type Filters,
20
- type QueryParams,
21
- type UnitData,
22
- type TourContactData
23
- } from "@superbright/indexeddb-orm";
24
-
25
- // 3. Replace BOTH your property store AND app store with one unified store:
26
-
27
- // This replaces both usePropertyStore AND useStore
28
- export const useStore = create<ZustandUnifiedStoreState>()(
29
- devtools(
30
- createZustandPropertyStore({
31
- // Optional: callback for when filters are updated
32
- onFilterUpdate: (apiParams: QueryParams) => {
33
- // Replace your updateParams call here
34
- // updateParams(apiParams);
35
- console.log("Filters updated:", apiParams);
36
- }
37
- }),
38
- { name: "property-store" }
39
- )
40
- );
41
-
42
- // 4. Create the unit state hook (same as before):
43
- export const useUnitState = createUseUnitState()(useStore);
44
-
45
- // 5. Export store instance (replaces both propertyStore.getState and old useStore.getState):
46
- export const propertyStore = useStore.getState;
47
-
48
- // 6. Initialize the unified store in your app startup (e.g., main.tsx or App.tsx):
49
- /*
50
- import { useStore } from './stores/propertyStore'; // Keep the same file name
51
-
52
- async function initializeApp() {
53
- // Initialize and hydrate the unified store with data from IndexedDB
54
- await useStore.getState()._initialize();
55
- await useStore.getState()._hydrate();
56
-
57
- // ... rest of your app initialization
58
- }
59
-
60
- // Call this on app startup
61
- initializeApp();
62
- */
63
-
64
- // 7. Usage in components remains largely the same:
65
- /*
66
- function MyComponent() {
67
- const { data, propertyId } = usePropertyStore();
68
- const unitState = useUnitState("unit-123");
69
-
70
- // NOTE: These are now async operations
71
- const handleToggleFavorite = async () => {
72
- await usePropertyStore.getState().toggleFavorite("unit-123");
73
- };
74
-
75
- const handleMarkViewed = async () => {
76
- await usePropertyStore.getState().markUnitAsViewed("unit-123", "property-slug");
77
- };
78
-
79
- return (
80
- <div>
81
- <p>Property: {propertyId}</p>
82
- <p>Is favorite: {unitState.isFavorite}</p>
83
- <p>Viewed: {unitState.viewedDate}</p>
84
- <button onClick={handleToggleFavorite}>Toggle Favorite</button>
85
- <button onClick={handleMarkViewed}>Mark as Viewed</button>
86
- </div>
87
- );
88
- }
89
- */
90
-
91
- // 7. Usage examples - the API combines both stores:
92
- /*
93
- function PropertyComponent() {
94
- const {
95
- // Property data (was usePropertyStore)
96
- properties,
97
- currentPropertyId,
98
- currentPropertySlug,
99
-
100
- // App data (was useStore)
101
- units,
102
- filters,
103
- resultsMode,
104
- sortBy
105
- } = useStore();
106
-
107
- const unitState = useUnitState("unit-123");
108
-
109
- // Property operations
110
- const handleToggleFavorite = async () => {
111
- await useStore.getState().toggleFavorite("unit-123");
112
- };
113
-
114
- const handleMarkViewed = async () => {
115
- await useStore.getState().markUnitAsViewed("unit-123", "property-slug");
116
- };
117
-
118
- // App operations
119
- const handleFilterChange = async () => {
120
- await useStore.getState().setFilters({ bedrooms: [1, 2] });
121
- };
122
-
123
- const handleSetUnitData = async () => {
124
- await useStore.getState().setUnitData("unit-123", { isFavorite: true });
125
- };
126
-
127
- return (
128
- <div>
129
- <p>Property: {currentPropertyId}</p>
130
- <p>Unit is favorite: {unitState.isFavorite}</p>
131
- <p>Results mode: {resultsMode}</p>
132
- <button onClick={handleToggleFavorite}>Toggle Favorite</button>
133
- <button onClick={handleFilterChange}>Set Filters</button>
134
- </div>
135
- );
136
- }
137
- */
138
-
139
- // 8. Key breaking changes to be aware of:
140
- /*
141
- BREAKING CHANGES:
142
- - Two stores combined into one unified store
143
- - All store methods are now async (return Promise<void>)
144
- - Must call _initialize() and _hydrate() on app startup
145
- - No more persist middleware configuration needed
146
- - No more custom IndexedDB storage setup
147
- - Property data is now in 'properties' object, current property tracked separately
148
- - toggleFavorite() no longer takes propertyId parameter (uses currentPropertyId)
149
- - loadPersistedFilters() is now redundant (always loads from IndexedDB)
150
- - availabilityOptions array should be handled in UI components
151
- - updateParams callback is now handled in store configuration
152
-
153
- BENEFITS:
154
- - Single source of truth for all app state
155
- - Better type safety with Zod validation
156
- - Centralized storage logic in ORM
157
- - Better error handling and validation
158
- - No more manual storage configuration
159
- - More consistent API across all operations
160
- - Better performance with native IndexedDB operations
161
- - Automatic state synchronization
162
- - Built-in callbacks for external integrations
163
- - Easier state management and debugging
164
- */
@@ -1,157 +0,0 @@
1
- # Complete Store Migration Summary
2
-
3
- This document provides a complete overview of migrating both the Property Store and App Store from the consuming application into a single unified IndexedDB ORM store.
4
-
5
- ## What Was Extracted
6
-
7
- ### Unified Store (Combines Both Previous Stores)
8
- - **Original**: Two separate Zustand stores (property + app) with different storage mechanisms
9
- - **New**: Single unified ORM-backed store with Zustand adapter
10
- - **Key Features**:
11
- - **Property data**: Multiple properties, current property tracking, favorites, viewed units, tour contacts, questionnaire results
12
- - **App data**: Unit data, filters, temp filters, API filters, results mode, sorting, questionnaire values
13
-
14
- ## Architecture Changes
15
-
16
- ### Before (Consuming App)
17
- ```
18
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
19
- │ Property Store │ │ Persist │ │ ORM String │
20
- │ (Zustand) │───▶│ Middleware │───▶│ Storage │
21
- └─────────────────┘ └──────────────────┘ └─────────────────┘
22
-
23
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
24
- │ App Store │ │ Persist │ │ idb-keyval │
25
- │ (Zustand) │───▶│ Middleware │───▶│ Custom Storage │
26
- └─────────────────┘ └──────────────────┘ └─────────────────┘
27
- ```
28
-
29
- ### After (Unified ORM-Centric)
30
- ```
31
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
32
- │ Zustand │ │ Unified ORM │ │ IndexedDB │
33
- │ Store │───▶│ Store Adapter │───▶│ (Native) │
34
- │ (UI Layer) │ │ │ │ │
35
- └─────────────────┘ └──────────────────┘ └─────────────────┘
36
-
37
-
38
- ┌──────────────────┐
39
- │ Direct ORM API │
40
- │ (Server/Logic) │
41
- └──────────────────┘
42
- ```
43
-
44
- ## New Unified ORM API
45
-
46
- ### UnifiedStore (Direct ORM Usage)
47
- ```typescript
48
- import { store } from "@superbright/indexeddb-orm";
49
-
50
- // Property operations
51
- await store.initializeProperty("prop-123", "property-slug");
52
- await store.setCurrentProperty("prop-123", "property-slug");
53
- await store.toggleFavorite("unit-456");
54
- await store.markUnitAsViewed("unit-456", "property-slug");
55
-
56
- // App operations
57
- await store.setUnitData("unit-123", { isFavorite: true });
58
- await store.setFilters({ bedrooms: [1, 2], cost: 2000 });
59
- await store.setResultsMode("favorites");
60
-
61
- // Get data
62
- const unitState = await store.getUnitState("unit-456");
63
- const fullState = await store.getFullState();
64
- ```
65
-
66
- ## Zustand Integration
67
-
68
- ### Unified Store (Replaces Both Previous Stores)
69
- ```typescript
70
- import { create } from "zustand";
71
- import { devtools } from "zustand/middleware";
72
- import { createZustandUnifiedStore } from "@superbright/indexeddb-orm";
73
-
74
- // This replaces BOTH usePropertyStore AND useStore
75
- export const useStore = create()(
76
- devtools(
77
- createZustandUnifiedStore({
78
- onFilterUpdate: (apiParams) => {
79
- // Handle filter updates (e.g., call updateParams)
80
- updateParams(apiParams);
81
- }
82
- }),
83
- { name: "property-store" } // Keep familiar naming
84
- )
85
- );
86
-
87
- // Usage combines both property and app data
88
- const {
89
- // Property data
90
- properties, currentPropertyId, currentPropertySlug,
91
- // App data
92
- units, filters, resultsMode, sortBy
93
- } = useStore();
94
- ```
95
-
96
- ## Migration Checklist
97
-
98
- ### ✅ Completed in ORM
99
- - [x] UnifiedStore class combining both property and app functionality
100
- - [x] Zustand adapter for unified store
101
- - [x] Type definitions with Zod validation
102
- - [x] Schema exports and integration
103
- - [x] Legacy compatibility methods for smooth migration
104
- - [x] Documentation and migration guides
105
- - [x] Example usage code
106
- - [x] Single storage mechanism for all data
107
-
108
- ### 🔄 Required in Consuming App
109
- - [ ] Remove old persist middleware imports
110
- - [ ] Remove custom storage setup (idb-keyval, createORMStringStorage)
111
- - [ ] Replace BOTH stores with single unified store
112
- - [ ] Update imports to use unified store types
113
- - [ ] Add store initialization calls in app startup
114
- - [ ] Update component handlers to be async
115
- - [ ] Handle updateParams callback in store configuration
116
- - [ ] Remove availabilityOptions from store state (handle in UI)
117
- - [ ] Update component state selectors (properties vs data)
118
- - [ ] Test all existing functionality
119
-
120
- ## Benefits
121
-
122
- 1. **Single Source of Truth**: All app state in one unified store
123
- 2. **Centralized Logic**: All storage logic lives in the ORM
124
- 3. **Type Safety**: Full TypeScript + Zod validation
125
- 4. **Better Performance**: Direct IndexedDB operations
126
- 5. **Simplified Architecture**: One store instead of two
127
- 6. **Testability**: Easier to mock and test centralized logic
128
- 7. **Flexibility**: Can use direct ORM API or Zustand adapters
129
- 8. **Error Handling**: Built-in validation and error handling
130
- 9. **Consistent API**: Same patterns for all operations
131
- 10. **Easier Debugging**: Single store to inspect and debug
132
-
133
- ## Breaking Changes Summary
134
-
135
- ### All Stores
136
- - All methods are now async
137
- - Must call initialization methods on app startup
138
- - No more persist middleware configuration
139
-
140
- ### Property Store Specific
141
- - `toggleFavorite()` no longer takes propertyId parameter
142
- - Questionnaire results can be any type (not just IFilters)
143
-
144
- ### App Store Specific
145
- - `loadPersistedFilters()` is redundant (always loads from IndexedDB)
146
- - `availabilityOptions` should be handled in UI components
147
- - `updateParams` callback moved to store configuration
148
-
149
- ## Next Steps
150
-
151
- 1. **Update Consuming App**: Follow the migration guides to update both stores
152
- 2. **Test Thoroughly**: Ensure all existing functionality works
153
- 3. **Remove Dead Code**: Clean up old storage-related code
154
- 4. **Add Error Handling**: Leverage the improved error handling in the ORM
155
- 5. **Consider Direct ORM Usage**: For server-side or complex logic, consider using the ORM APIs directly
156
-
157
- The migration provides a solid foundation for scalable, type-safe data management while maintaining compatibility with existing Zustand-based UI patterns.