@tanstack/db 0.0.22 → 0.0.24

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.
Files changed (64) hide show
  1. package/dist/cjs/collection.cjs +14 -6
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +10 -9
  4. package/dist/cjs/local-storage.cjs +1 -1
  5. package/dist/cjs/local-storage.cjs.map +1 -1
  6. package/dist/cjs/proxy.cjs +21 -0
  7. package/dist/cjs/proxy.cjs.map +1 -1
  8. package/dist/cjs/query/builder/index.cjs +72 -0
  9. package/dist/cjs/query/builder/index.cjs.map +1 -1
  10. package/dist/cjs/query/builder/index.d.cts +64 -0
  11. package/dist/cjs/query/compiler/index.cjs +44 -8
  12. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  13. package/dist/cjs/query/compiler/index.d.cts +4 -7
  14. package/dist/cjs/query/compiler/joins.cjs +14 -6
  15. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  16. package/dist/cjs/query/compiler/joins.d.cts +4 -8
  17. package/dist/cjs/query/compiler/types.d.cts +10 -0
  18. package/dist/cjs/query/optimizer.cjs +283 -0
  19. package/dist/cjs/query/optimizer.cjs.map +1 -0
  20. package/dist/cjs/query/optimizer.d.cts +42 -0
  21. package/dist/cjs/transactions.cjs.map +1 -1
  22. package/dist/cjs/transactions.d.cts +5 -5
  23. package/dist/cjs/types.d.cts +35 -10
  24. package/dist/cjs/utils.cjs +42 -0
  25. package/dist/cjs/utils.cjs.map +1 -0
  26. package/dist/cjs/utils.d.cts +18 -0
  27. package/dist/esm/collection.d.ts +10 -9
  28. package/dist/esm/collection.js +14 -6
  29. package/dist/esm/collection.js.map +1 -1
  30. package/dist/esm/local-storage.js +1 -1
  31. package/dist/esm/local-storage.js.map +1 -1
  32. package/dist/esm/proxy.js +21 -0
  33. package/dist/esm/proxy.js.map +1 -1
  34. package/dist/esm/query/builder/index.d.ts +64 -0
  35. package/dist/esm/query/builder/index.js +72 -0
  36. package/dist/esm/query/builder/index.js.map +1 -1
  37. package/dist/esm/query/compiler/index.d.ts +4 -7
  38. package/dist/esm/query/compiler/index.js +44 -8
  39. package/dist/esm/query/compiler/index.js.map +1 -1
  40. package/dist/esm/query/compiler/joins.d.ts +4 -8
  41. package/dist/esm/query/compiler/joins.js +14 -6
  42. package/dist/esm/query/compiler/joins.js.map +1 -1
  43. package/dist/esm/query/compiler/types.d.ts +10 -0
  44. package/dist/esm/query/optimizer.d.ts +42 -0
  45. package/dist/esm/query/optimizer.js +283 -0
  46. package/dist/esm/query/optimizer.js.map +1 -0
  47. package/dist/esm/transactions.d.ts +5 -5
  48. package/dist/esm/transactions.js.map +1 -1
  49. package/dist/esm/types.d.ts +35 -10
  50. package/dist/esm/utils.d.ts +18 -0
  51. package/dist/esm/utils.js +42 -0
  52. package/dist/esm/utils.js.map +1 -0
  53. package/package.json +1 -1
  54. package/src/collection.ts +62 -21
  55. package/src/local-storage.ts +2 -2
  56. package/src/proxy.ts +24 -0
  57. package/src/query/builder/index.ts +104 -0
  58. package/src/query/compiler/index.ts +85 -18
  59. package/src/query/compiler/joins.ts +21 -13
  60. package/src/query/compiler/types.ts +12 -0
  61. package/src/query/optimizer.ts +738 -0
  62. package/src/transactions.ts +8 -12
  63. package/src/types.ts +69 -14
  64. package/src/utils.ts +86 -0
@@ -1 +1 @@
1
- {"version":3,"file":"local-storage.cjs","sources":["../../src/local-storage.ts"],"sourcesContent":["import type {\n CollectionConfig,\n DeleteMutationFnParams,\n InsertMutationFnParams,\n ResolveType,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Storage API interface - subset of DOM Storage that we need\n */\nexport type StorageApi = Pick<Storage, `getItem` | `setItem` | `removeItem`>\n\n/**\n * Storage event API - subset of Window for 'storage' events only\n */\nexport type StorageEventApi = {\n addEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n removeEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n}\n\n/**\n * Internal storage format that includes version tracking\n */\ninterface StoredItem<T> {\n versionKey: string\n data: T\n}\n\n/**\n * Configuration interface for localStorage collection options\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n *\n * @remarks\n * Type resolution follows a priority order:\n * 1. If you provide an explicit type via generic parameter, it will be used\n * 2. If no explicit type is provided but a schema is, the schema's output type will be inferred\n * 3. If neither explicit type nor schema is provided, the fallback type will be used\n *\n * You should provide EITHER an explicit type OR a schema, but not both, as they would conflict.\n */\nexport interface LocalStorageCollectionConfig<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n> {\n /**\n * The key to use for storing the collection data in localStorage/sessionStorage\n */\n storageKey: string\n\n /**\n * Storage API to use (defaults to window.localStorage)\n * Can be any object that implements the Storage interface (e.g., sessionStorage)\n */\n storage?: StorageApi\n\n /**\n * Storage event API to use for cross-tab synchronization (defaults to window)\n * Can be any object that implements addEventListener/removeEventListener for storage events\n */\n storageEventApi?: StorageEventApi\n\n /**\n * Collection identifier (defaults to \"local-collection:{storageKey}\" if not provided)\n */\n id?: string\n schema?: TSchema\n getKey: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`getKey`]\n sync?: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`sync`]\n\n /**\n * Optional asynchronous handler function called before an insert operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onInsert?: (\n params: InsertMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before an update operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onUpdate?: (\n params: UpdateMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before a delete operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onDelete?: (\n params: DeleteMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n}\n\n/**\n * Type for the clear utility function\n */\nexport type ClearStorageFn = () => void\n\n/**\n * Type for the getStorageSize utility function\n */\nexport type GetStorageSizeFn = () => number\n\n/**\n * LocalStorage collection utilities type\n */\nexport interface LocalStorageCollectionUtils extends UtilsRecord {\n clearStorage: ClearStorageFn\n getStorageSize: GetStorageSizeFn\n}\n\n/**\n * Validates that a value can be JSON serialized\n * @param value - The value to validate for JSON serialization\n * @param operation - The operation type being performed (for error messages)\n * @throws Error if the value cannot be JSON serialized\n */\nfunction validateJsonSerializable(value: any, operation: string): void {\n try {\n JSON.stringify(value)\n } catch (error) {\n throw new Error(\n `Cannot ${operation} item because it cannot be JSON serialized: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n }\n}\n\n/**\n * Generate a UUID for version tracking\n * @returns A unique identifier string for tracking data versions\n */\nfunction generateUuid(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Creates localStorage collection options for use with a standard Collection\n *\n * This function creates a collection that persists data to localStorage/sessionStorage\n * and synchronizes changes across browser tabs using storage events.\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param config - Configuration options for the localStorage collection\n * @returns Collection options with utilities including clearStorage and getStorageSize\n *\n * @example\n * // Basic localStorage collection\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with custom storage\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * storage: window.sessionStorage, // Use sessionStorage instead\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with mutation handlers\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * },\n * })\n * )\n */\nexport function localStorageCollectionOptions<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n>(config: LocalStorageCollectionConfig<TExplicit, TSchema, TFallback>) {\n type ResolvedType = ResolveType<TExplicit, TSchema, TFallback>\n\n // Validate required parameters\n if (!config.storageKey) {\n throw new Error(`[LocalStorageCollection] storageKey must be provided.`)\n }\n\n // Default to window.localStorage if no storage is provided\n const storage =\n config.storage ||\n (typeof window !== `undefined` ? window.localStorage : null)\n\n if (!storage) {\n throw new Error(\n `[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`\n )\n }\n\n // Default to window for storage events if not provided\n const storageEventApi =\n config.storageEventApi || (typeof window !== `undefined` ? window : null)\n\n if (!storageEventApi) {\n throw new Error(\n `[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`\n )\n }\n\n // Track the last known state to detect changes\n const lastKnownData = new Map<string | number, StoredItem<ResolvedType>>()\n\n // Create the sync configuration\n const sync = createLocalStorageSync<ResolvedType>(\n config.storageKey,\n storage,\n storageEventApi,\n config.getKey,\n lastKnownData\n )\n\n /**\n * Manual trigger function for local sync updates\n * Forces a check for storage changes and updates the collection if needed\n */\n const triggerLocalSync = () => {\n if (sync.manualTrigger) {\n sync.manualTrigger()\n }\n }\n\n /**\n * Save data to storage\n * @param dataMap - Map of items with version tracking to save to storage\n */\n const saveToStorage = (\n dataMap: Map<string | number, StoredItem<ResolvedType>>\n ): void => {\n try {\n // Convert Map to object format for storage\n const objectData: Record<string, StoredItem<ResolvedType>> = {}\n dataMap.forEach((storedItem, key) => {\n objectData[String(key)] = storedItem\n })\n const serialized = JSON.stringify(objectData)\n storage.setItem(config.storageKey, serialized)\n } catch (error) {\n console.error(\n `[LocalStorageCollection] Error saving data to storage key \"${config.storageKey}\":`,\n error\n )\n throw error\n }\n }\n\n /**\n * Removes all collection data from the configured storage\n */\n const clearStorage: ClearStorageFn = (): void => {\n storage.removeItem(config.storageKey)\n }\n\n /**\n * Get the size of the stored data in bytes (approximate)\n * @returns The approximate size in bytes of the stored collection data\n */\n const getStorageSize: GetStorageSizeFn = (): number => {\n const data = storage.getItem(config.storageKey)\n return data ? new Blob([data]).size : 0\n }\n\n /*\n * Create wrapper handlers for direct persistence operations that perform actual storage operations\n * Wraps the user's onInsert handler to also save changes to localStorage\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `insert`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onInsert) {\n handlerResult = (await config.onInsert(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Add new items with version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `update`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onUpdate) {\n handlerResult = (await config.onUpdate(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Update items with new version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<ResolvedType>\n ) => {\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onDelete) {\n handlerResult = (await config.onDelete(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Remove items\n params.transaction.mutations.forEach((mutation) => {\n // For delete operations, mutation.original contains the full object\n const key = config.getKey(mutation.original)\n currentData.delete(key)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n // Extract standard Collection config properties\n const {\n storageKey: _storageKey,\n storage: _storage,\n storageEventApi: _storageEventApi,\n onInsert: _onInsert,\n onUpdate: _onUpdate,\n onDelete: _onDelete,\n id,\n ...restConfig\n } = config\n\n // Default id to a pattern based on storage key if not provided\n const collectionId = id ?? `local-collection:${config.storageKey}`\n\n return {\n ...restConfig,\n id: collectionId,\n sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n clearStorage,\n getStorageSize,\n },\n }\n}\n\n/**\n * Load data from storage and return as a Map\n * @param storageKey - The key used to store data in the storage API\n * @param storage - The storage API to load from (localStorage, sessionStorage, etc.)\n * @returns Map of stored items with version tracking, or empty Map if loading fails\n */\nfunction loadFromStorage<T extends object>(\n storageKey: string,\n storage: StorageApi\n): Map<string | number, StoredItem<T>> {\n try {\n const rawData = storage.getItem(storageKey)\n if (!rawData) {\n return new Map()\n }\n\n const parsed = JSON.parse(rawData)\n const dataMap = new Map<string | number, StoredItem<T>>()\n\n // Handle object format where keys map to StoredItem values\n if (\n typeof parsed === `object` &&\n parsed !== null &&\n !Array.isArray(parsed)\n ) {\n Object.entries(parsed).forEach(([key, value]) => {\n // Runtime check to ensure the value has the expected StoredItem structure\n if (\n value &&\n typeof value === `object` &&\n `versionKey` in value &&\n `data` in value\n ) {\n const storedItem = value as StoredItem<T>\n dataMap.set(key, storedItem)\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`\n )\n }\n })\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`\n )\n }\n\n return dataMap\n } catch (error) {\n console.warn(\n `[LocalStorageCollection] Error loading data from storage key \"${storageKey}\":`,\n error\n )\n return new Map()\n }\n}\n\n/**\n * Internal function to create localStorage sync configuration\n * Creates a sync configuration that handles localStorage persistence and cross-tab synchronization\n * @param storageKey - The key used for storing data in localStorage\n * @param storage - The storage API to use (localStorage, sessionStorage, etc.)\n * @param storageEventApi - The event API for listening to storage changes\n * @param getKey - Function to extract the key from an item\n * @param lastKnownData - Map tracking the last known state for change detection\n * @returns Sync configuration with manual trigger capability\n */\nfunction createLocalStorageSync<T extends object>(\n storageKey: string,\n storage: StorageApi,\n storageEventApi: StorageEventApi,\n getKey: (item: T) => string | number,\n lastKnownData: Map<string | number, StoredItem<T>>\n): SyncConfig<T> & { manualTrigger?: () => void } {\n let syncParams: Parameters<SyncConfig<T>[`sync`]>[0] | null = null\n\n /**\n * Compare two Maps to find differences using version keys\n * @param oldData - The previous state of stored items\n * @param newData - The current state of stored items\n * @returns Array of changes with type, key, and value information\n */\n const findChanges = (\n oldData: Map<string | number, StoredItem<T>>,\n newData: Map<string | number, StoredItem<T>>\n ): Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> => {\n const changes: Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> = []\n\n // Check for deletions and updates\n oldData.forEach((oldStoredItem, key) => {\n const newStoredItem = newData.get(key)\n if (!newStoredItem) {\n changes.push({ type: `delete`, key, value: oldStoredItem.data })\n } else if (oldStoredItem.versionKey !== newStoredItem.versionKey) {\n changes.push({ type: `update`, key, value: newStoredItem.data })\n }\n })\n\n // Check for insertions\n newData.forEach((newStoredItem, key) => {\n if (!oldData.has(key)) {\n changes.push({ type: `insert`, key, value: newStoredItem.data })\n }\n })\n\n return changes\n }\n\n /**\n * Process storage changes and update collection\n * Loads new data from storage, compares with last known state, and applies changes\n */\n const processStorageChanges = () => {\n if (!syncParams) return\n\n const { begin, write, commit } = syncParams\n\n // Load the new data\n const newData = loadFromStorage<T>(storageKey, storage)\n\n // Find the specific changes\n const changes = findChanges(lastKnownData, newData)\n\n if (changes.length > 0) {\n begin()\n changes.forEach(({ type, value }) => {\n if (value) {\n validateJsonSerializable(value, type)\n write({ type, value })\n }\n })\n commit()\n\n // Update lastKnownData\n lastKnownData.clear()\n newData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n }\n }\n\n const syncConfig: SyncConfig<T> & { manualTrigger?: () => void } = {\n sync: (params: Parameters<SyncConfig<T>[`sync`]>[0]) => {\n const { begin, write, commit } = params\n\n // Store sync params for later use\n syncParams = params\n\n // Initial load\n const initialData = loadFromStorage<T>(storageKey, storage)\n if (initialData.size > 0) {\n begin()\n initialData.forEach((storedItem) => {\n validateJsonSerializable(storedItem.data, `load`)\n write({ type: `insert`, value: storedItem.data })\n })\n commit()\n }\n\n // Update lastKnownData\n lastKnownData.clear()\n initialData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n\n // Listen for storage events from other tabs\n const handleStorageEvent = (event: StorageEvent) => {\n // Only respond to changes to our specific key and from our storage\n if (event.key !== storageKey || event.storageArea !== storage) {\n return\n }\n\n processStorageChanges()\n }\n\n // Add storage event listener for cross-tab sync\n storageEventApi.addEventListener(`storage`, handleStorageEvent)\n\n // Note: Cleanup is handled automatically by the collection when it's disposed\n },\n\n /**\n * Get sync metadata - returns storage key information\n * @returns Object containing storage key and storage type metadata\n */\n getSyncMetadata: () => ({\n storageKey,\n storageType:\n storage === (typeof window !== `undefined` ? window.localStorage : null)\n ? `localStorage`\n : `custom`,\n }),\n\n // Manual trigger function for local updates\n manualTrigger: processStorageChanges,\n }\n\n return syncConfig\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-storage.cjs","sources":["../../src/local-storage.ts"],"sourcesContent":["import type {\n CollectionConfig,\n DeleteMutationFnParams,\n InsertMutationFnParams,\n ResolveType,\n SyncConfig,\n UpdateMutationFnParams,\n UtilsRecord,\n} from \"./types\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\n\n/**\n * Storage API interface - subset of DOM Storage that we need\n */\nexport type StorageApi = Pick<Storage, `getItem` | `setItem` | `removeItem`>\n\n/**\n * Storage event API - subset of Window for 'storage' events only\n */\nexport type StorageEventApi = {\n addEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n removeEventListener: (\n type: `storage`,\n listener: (event: StorageEvent) => void\n ) => void\n}\n\n/**\n * Internal storage format that includes version tracking\n */\ninterface StoredItem<T> {\n versionKey: string\n data: T\n}\n\n/**\n * Configuration interface for localStorage collection options\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n *\n * @remarks\n * Type resolution follows a priority order:\n * 1. If you provide an explicit type via generic parameter, it will be used\n * 2. If no explicit type is provided but a schema is, the schema's output type will be inferred\n * 3. If neither explicit type nor schema is provided, the fallback type will be used\n *\n * You should provide EITHER an explicit type OR a schema, but not both, as they would conflict.\n */\nexport interface LocalStorageCollectionConfig<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n> {\n /**\n * The key to use for storing the collection data in localStorage/sessionStorage\n */\n storageKey: string\n\n /**\n * Storage API to use (defaults to window.localStorage)\n * Can be any object that implements the Storage interface (e.g., sessionStorage)\n */\n storage?: StorageApi\n\n /**\n * Storage event API to use for cross-tab synchronization (defaults to window)\n * Can be any object that implements addEventListener/removeEventListener for storage events\n */\n storageEventApi?: StorageEventApi\n\n /**\n * Collection identifier (defaults to \"local-collection:{storageKey}\" if not provided)\n */\n id?: string\n schema?: TSchema\n getKey: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`getKey`]\n sync?: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>>[`sync`]\n\n /**\n * Optional asynchronous handler function called before an insert operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onInsert?: (\n params: InsertMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before an update operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onUpdate?: (\n params: UpdateMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n\n /**\n * Optional asynchronous handler function called before a delete operation\n * @param params Object containing transaction and collection information\n * @returns Promise resolving to any value\n */\n onDelete?: (\n params: DeleteMutationFnParams<ResolveType<TExplicit, TSchema, TFallback>>\n ) => Promise<any>\n}\n\n/**\n * Type for the clear utility function\n */\nexport type ClearStorageFn = () => void\n\n/**\n * Type for the getStorageSize utility function\n */\nexport type GetStorageSizeFn = () => number\n\n/**\n * LocalStorage collection utilities type\n */\nexport interface LocalStorageCollectionUtils extends UtilsRecord {\n clearStorage: ClearStorageFn\n getStorageSize: GetStorageSizeFn\n}\n\n/**\n * Validates that a value can be JSON serialized\n * @param value - The value to validate for JSON serialization\n * @param operation - The operation type being performed (for error messages)\n * @throws Error if the value cannot be JSON serialized\n */\nfunction validateJsonSerializable(value: any, operation: string): void {\n try {\n JSON.stringify(value)\n } catch (error) {\n throw new Error(\n `Cannot ${operation} item because it cannot be JSON serialized: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n }\n}\n\n/**\n * Generate a UUID for version tracking\n * @returns A unique identifier string for tracking data versions\n */\nfunction generateUuid(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Creates localStorage collection options for use with a standard Collection\n *\n * This function creates a collection that persists data to localStorage/sessionStorage\n * and synchronizes changes across browser tabs using storage events.\n *\n * @template TExplicit - The explicit type of items in the collection (highest priority)\n * @template TSchema - The schema type for validation and type inference (second priority)\n * @template TFallback - The fallback type if no explicit or schema type is provided\n * @param config - Configuration options for the localStorage collection\n * @returns Collection options with utilities including clearStorage and getStorageSize\n *\n * @example\n * // Basic localStorage collection\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with custom storage\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * storage: window.sessionStorage, // Use sessionStorage instead\n * getKey: (item) => item.id,\n * })\n * )\n *\n * @example\n * // localStorage collection with mutation handlers\n * const collection = createCollection(\n * localStorageCollectionOptions({\n * storageKey: 'todos',\n * getKey: (item) => item.id,\n * onInsert: async ({ transaction }) => {\n * console.log('Item inserted:', transaction.mutations[0].modified)\n * },\n * })\n * )\n */\nexport function localStorageCollectionOptions<\n TExplicit = unknown,\n TSchema extends StandardSchemaV1 = never,\n TFallback extends object = Record<string, unknown>,\n>(config: LocalStorageCollectionConfig<TExplicit, TSchema, TFallback>) {\n type ResolvedType = ResolveType<TExplicit, TSchema, TFallback>\n\n // Validate required parameters\n if (!config.storageKey) {\n throw new Error(`[LocalStorageCollection] storageKey must be provided.`)\n }\n\n // Default to window.localStorage if no storage is provided\n const storage =\n config.storage ||\n (typeof window !== `undefined` ? window.localStorage : null)\n\n if (!storage) {\n throw new Error(\n `[LocalStorageCollection] No storage available. Please provide a storage option or ensure window.localStorage is available.`\n )\n }\n\n // Default to window for storage events if not provided\n const storageEventApi =\n config.storageEventApi || (typeof window !== `undefined` ? window : null)\n\n if (!storageEventApi) {\n throw new Error(\n `[LocalStorageCollection] No storage event API available. Please provide a storageEventApi option or ensure window is available.`\n )\n }\n\n // Track the last known state to detect changes\n const lastKnownData = new Map<string | number, StoredItem<ResolvedType>>()\n\n // Create the sync configuration\n const sync = createLocalStorageSync<ResolvedType>(\n config.storageKey,\n storage,\n storageEventApi,\n config.getKey,\n lastKnownData\n )\n\n /**\n * Manual trigger function for local sync updates\n * Forces a check for storage changes and updates the collection if needed\n */\n const triggerLocalSync = () => {\n if (sync.manualTrigger) {\n sync.manualTrigger()\n }\n }\n\n /**\n * Save data to storage\n * @param dataMap - Map of items with version tracking to save to storage\n */\n const saveToStorage = (\n dataMap: Map<string | number, StoredItem<ResolvedType>>\n ): void => {\n try {\n // Convert Map to object format for storage\n const objectData: Record<string, StoredItem<ResolvedType>> = {}\n dataMap.forEach((storedItem, key) => {\n objectData[String(key)] = storedItem\n })\n const serialized = JSON.stringify(objectData)\n storage.setItem(config.storageKey, serialized)\n } catch (error) {\n console.error(\n `[LocalStorageCollection] Error saving data to storage key \"${config.storageKey}\":`,\n error\n )\n throw error\n }\n }\n\n /**\n * Removes all collection data from the configured storage\n */\n const clearStorage: ClearStorageFn = (): void => {\n storage.removeItem(config.storageKey)\n }\n\n /**\n * Get the size of the stored data in bytes (approximate)\n * @returns The approximate size in bytes of the stored collection data\n */\n const getStorageSize: GetStorageSizeFn = (): number => {\n const data = storage.getItem(config.storageKey)\n return data ? new Blob([data]).size : 0\n }\n\n /*\n * Create wrapper handlers for direct persistence operations that perform actual storage operations\n * Wraps the user's onInsert handler to also save changes to localStorage\n */\n const wrappedOnInsert = async (\n params: InsertMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `insert`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onInsert) {\n handlerResult = (await config.onInsert(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Add new items with version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnUpdate = async (\n params: UpdateMutationFnParams<ResolvedType>\n ) => {\n // Validate that all values in the transaction can be JSON serialized\n params.transaction.mutations.forEach((mutation) => {\n validateJsonSerializable(mutation.modified, `update`)\n })\n\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onUpdate) {\n handlerResult = (await config.onUpdate(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Update items with new version keys\n params.transaction.mutations.forEach((mutation) => {\n const key = config.getKey(mutation.modified)\n const storedItem: StoredItem<ResolvedType> = {\n versionKey: generateUuid(),\n data: mutation.modified,\n }\n currentData.set(key, storedItem)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n const wrappedOnDelete = async (\n params: DeleteMutationFnParams<ResolvedType>\n ) => {\n // Call the user handler BEFORE persisting changes (if provided)\n let handlerResult: any = {}\n if (config.onDelete) {\n handlerResult = (await config.onDelete(params)) ?? {}\n }\n\n // Always persist to storage\n // Load current data from storage\n const currentData = loadFromStorage<ResolvedType>(\n config.storageKey,\n storage\n )\n\n // Remove items\n params.transaction.mutations.forEach((mutation) => {\n // For delete operations, mutation.original contains the full object\n const key = config.getKey(mutation.original as ResolvedType)\n currentData.delete(key)\n })\n\n // Save to storage\n saveToStorage(currentData)\n\n // Manually trigger local sync since storage events don't fire for current tab\n triggerLocalSync()\n\n return handlerResult\n }\n\n // Extract standard Collection config properties\n const {\n storageKey: _storageKey,\n storage: _storage,\n storageEventApi: _storageEventApi,\n onInsert: _onInsert,\n onUpdate: _onUpdate,\n onDelete: _onDelete,\n id,\n ...restConfig\n } = config\n\n // Default id to a pattern based on storage key if not provided\n const collectionId = id ?? `local-collection:${config.storageKey}`\n\n return {\n ...restConfig,\n id: collectionId,\n sync,\n onInsert: wrappedOnInsert,\n onUpdate: wrappedOnUpdate,\n onDelete: wrappedOnDelete,\n utils: {\n clearStorage,\n getStorageSize,\n },\n }\n}\n\n/**\n * Load data from storage and return as a Map\n * @param storageKey - The key used to store data in the storage API\n * @param storage - The storage API to load from (localStorage, sessionStorage, etc.)\n * @returns Map of stored items with version tracking, or empty Map if loading fails\n */\nfunction loadFromStorage<T extends object>(\n storageKey: string,\n storage: StorageApi\n): Map<string | number, StoredItem<T>> {\n try {\n const rawData = storage.getItem(storageKey)\n if (!rawData) {\n return new Map()\n }\n\n const parsed = JSON.parse(rawData)\n const dataMap = new Map<string | number, StoredItem<T>>()\n\n // Handle object format where keys map to StoredItem values\n if (\n typeof parsed === `object` &&\n parsed !== null &&\n !Array.isArray(parsed)\n ) {\n Object.entries(parsed).forEach(([key, value]) => {\n // Runtime check to ensure the value has the expected StoredItem structure\n if (\n value &&\n typeof value === `object` &&\n `versionKey` in value &&\n `data` in value\n ) {\n const storedItem = value as StoredItem<T>\n dataMap.set(key, storedItem)\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\" for key \"${key}\".`\n )\n }\n })\n } else {\n throw new Error(\n `[LocalStorageCollection] Invalid data format in storage key \"${storageKey}\". Expected object format.`\n )\n }\n\n return dataMap\n } catch (error) {\n console.warn(\n `[LocalStorageCollection] Error loading data from storage key \"${storageKey}\":`,\n error\n )\n return new Map()\n }\n}\n\n/**\n * Internal function to create localStorage sync configuration\n * Creates a sync configuration that handles localStorage persistence and cross-tab synchronization\n * @param storageKey - The key used for storing data in localStorage\n * @param storage - The storage API to use (localStorage, sessionStorage, etc.)\n * @param storageEventApi - The event API for listening to storage changes\n * @param getKey - Function to extract the key from an item\n * @param lastKnownData - Map tracking the last known state for change detection\n * @returns Sync configuration with manual trigger capability\n */\nfunction createLocalStorageSync<T extends object>(\n storageKey: string,\n storage: StorageApi,\n storageEventApi: StorageEventApi,\n _getKey: (item: T) => string | number,\n lastKnownData: Map<string | number, StoredItem<T>>\n): SyncConfig<T> & { manualTrigger?: () => void } {\n let syncParams: Parameters<SyncConfig<T>[`sync`]>[0] | null = null\n\n /**\n * Compare two Maps to find differences using version keys\n * @param oldData - The previous state of stored items\n * @param newData - The current state of stored items\n * @returns Array of changes with type, key, and value information\n */\n const findChanges = (\n oldData: Map<string | number, StoredItem<T>>,\n newData: Map<string | number, StoredItem<T>>\n ): Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> => {\n const changes: Array<{\n type: `insert` | `update` | `delete`\n key: string | number\n value?: T\n }> = []\n\n // Check for deletions and updates\n oldData.forEach((oldStoredItem, key) => {\n const newStoredItem = newData.get(key)\n if (!newStoredItem) {\n changes.push({ type: `delete`, key, value: oldStoredItem.data })\n } else if (oldStoredItem.versionKey !== newStoredItem.versionKey) {\n changes.push({ type: `update`, key, value: newStoredItem.data })\n }\n })\n\n // Check for insertions\n newData.forEach((newStoredItem, key) => {\n if (!oldData.has(key)) {\n changes.push({ type: `insert`, key, value: newStoredItem.data })\n }\n })\n\n return changes\n }\n\n /**\n * Process storage changes and update collection\n * Loads new data from storage, compares with last known state, and applies changes\n */\n const processStorageChanges = () => {\n if (!syncParams) return\n\n const { begin, write, commit } = syncParams\n\n // Load the new data\n const newData = loadFromStorage<T>(storageKey, storage)\n\n // Find the specific changes\n const changes = findChanges(lastKnownData, newData)\n\n if (changes.length > 0) {\n begin()\n changes.forEach(({ type, value }) => {\n if (value) {\n validateJsonSerializable(value, type)\n write({ type, value })\n }\n })\n commit()\n\n // Update lastKnownData\n lastKnownData.clear()\n newData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n }\n }\n\n const syncConfig: SyncConfig<T> & { manualTrigger?: () => void } = {\n sync: (params: Parameters<SyncConfig<T>[`sync`]>[0]) => {\n const { begin, write, commit } = params\n\n // Store sync params for later use\n syncParams = params\n\n // Initial load\n const initialData = loadFromStorage<T>(storageKey, storage)\n if (initialData.size > 0) {\n begin()\n initialData.forEach((storedItem) => {\n validateJsonSerializable(storedItem.data, `load`)\n write({ type: `insert`, value: storedItem.data })\n })\n commit()\n }\n\n // Update lastKnownData\n lastKnownData.clear()\n initialData.forEach((storedItem, key) => {\n lastKnownData.set(key, storedItem)\n })\n\n // Listen for storage events from other tabs\n const handleStorageEvent = (event: StorageEvent) => {\n // Only respond to changes to our specific key and from our storage\n if (event.key !== storageKey || event.storageArea !== storage) {\n return\n }\n\n processStorageChanges()\n }\n\n // Add storage event listener for cross-tab sync\n storageEventApi.addEventListener(`storage`, handleStorageEvent)\n\n // Note: Cleanup is handled automatically by the collection when it's disposed\n },\n\n /**\n * Get sync metadata - returns storage key information\n * @returns Object containing storage key and storage type metadata\n */\n getSyncMetadata: () => ({\n storageKey,\n storageType:\n storage === (typeof window !== `undefined` ? window.localStorage : null)\n ? `localStorage`\n : `custom`,\n }),\n\n // Manual trigger function for local updates\n manualTrigger: processStorageChanges,\n }\n\n return syncConfig\n}\n"],"names":[],"mappings":"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"}
@@ -254,6 +254,27 @@ function createChangeProxy(target, parent) {
254
254
  return value;
255
255
  }
256
256
  if (typeof value === `function`) {
257
+ if (Array.isArray(ptarget)) {
258
+ const methodName = prop.toString();
259
+ const modifyingMethods = /* @__PURE__ */ new Set([
260
+ `pop`,
261
+ `push`,
262
+ `shift`,
263
+ `unshift`,
264
+ `splice`,
265
+ `sort`,
266
+ `reverse`,
267
+ `fill`,
268
+ `copyWithin`
269
+ ]);
270
+ if (modifyingMethods.has(methodName)) {
271
+ return function(...args) {
272
+ const result = value.apply(changeTracker.copy_, args);
273
+ markChanged(changeTracker);
274
+ return result;
275
+ };
276
+ }
277
+ }
257
278
  if (ptarget instanceof Map || ptarget instanceof Set) {
258
279
  const methodName = prop.toString();
259
280
  const modifyingMethods = /* @__PURE__ */ new Set([
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.cjs","sources":["../../src/proxy.ts"],"sourcesContent":["/**\n * A utility for creating a proxy that captures changes to an object\n * and provides a way to retrieve those changes.\n */\n\n/**\n * Simple debug utility that only logs when debug mode is enabled\n * Set DEBUG to true in localStorage to enable debug logging\n */\nfunction debugLog(...args: Array<unknown>): void {\n // Check if we're in a browser environment\n const isBrowser =\n typeof window !== `undefined` && typeof localStorage !== `undefined`\n\n // In browser, check localStorage for debug flag\n if (isBrowser && localStorage.getItem(`DEBUG`) === `true`) {\n console.log(`[proxy]`, ...args)\n }\n // In Node.js environment, check for environment variable (though this is primarily for browser)\n else if (\n // true\n !isBrowser &&\n typeof process !== `undefined` &&\n process.env.DEBUG === `true`\n ) {\n console.log(`[proxy]`, ...args)\n }\n}\n\n// Add TypedArray interface with proper type\ninterface TypedArray {\n length: number\n [index: number]: number\n}\n\n// Update type for ChangeTracker\ninterface ChangeTracker<T extends object> {\n originalObject: T\n modified: boolean\n copy_: T\n proxyCount: number\n assigned_: Record<string | symbol, boolean>\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n target: T\n}\n\n/**\n * Deep clones an object while preserving special types like Date and RegExp\n */\n\nfunction deepClone<T extends unknown>(\n obj: T,\n visited = new WeakMap<object, unknown>()\n): T {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle primitive types\n if (typeof obj !== `object`) {\n return obj\n }\n\n // If we've already cloned this object, return the cached clone\n if (visited.has(obj as object)) {\n return visited.get(obj as object) as T\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n const arrayClone = [] as Array<unknown>\n visited.set(obj as object, arrayClone)\n obj.forEach((item, index) => {\n arrayClone[index] = deepClone(item, visited)\n })\n return arrayClone as unknown as T\n }\n\n // Handle TypedArrays\n if (ArrayBuffer.isView(obj) && !(obj instanceof DataView)) {\n // Get the constructor to create a new instance of the same type\n const TypedArrayConstructor = Object.getPrototypeOf(obj).constructor\n const clone = new TypedArrayConstructor(\n (obj as unknown as TypedArray).length\n ) as unknown as TypedArray\n visited.set(obj as object, clone)\n\n // Copy the values\n for (let i = 0; i < (obj as unknown as TypedArray).length; i++) {\n clone[i] = (obj as unknown as TypedArray)[i]!\n }\n\n return clone as unknown as T\n }\n\n if (obj instanceof Map) {\n const clone = new Map() as Map<unknown, unknown>\n visited.set(obj as object, clone)\n obj.forEach((value, key) => {\n clone.set(key, deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n if (obj instanceof Set) {\n const clone = new Set()\n visited.set(obj as object, clone)\n obj.forEach((value) => {\n clone.add(deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n const clone = {} as Record<string | symbol, unknown>\n visited.set(obj as object, clone)\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n clone[key] = deepClone(\n (obj as Record<string | symbol, unknown>)[key],\n visited\n )\n }\n }\n\n const symbolProps = Object.getOwnPropertySymbols(obj)\n for (const sym of symbolProps) {\n clone[sym] = deepClone(\n (obj as Record<string | symbol, unknown>)[sym],\n visited\n )\n }\n\n return clone as T\n}\n\n/**\n * Deep equality check that handles special types like Date, RegExp, Map, and Set\n */\nfunction deepEqual<T>(a: T, b: T): boolean {\n // Handle primitive types\n if (a === b) return true\n\n // If either is null or not an object, they're not equal\n if (\n a === null ||\n b === null ||\n typeof a !== `object` ||\n typeof b !== `object`\n ) {\n return false\n }\n\n // Handle Date objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n // Handle RegExp objects\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Handle Map objects\n if (a instanceof Map && b instanceof Map) {\n if (a.size !== b.size) return false\n\n const entries = Array.from(a.entries())\n for (const [key, val] of entries) {\n if (!b.has(key) || !deepEqual(val, b.get(key))) {\n return false\n }\n }\n\n return true\n }\n\n // Handle Set objects\n if (a instanceof Set && b instanceof Set) {\n if (a.size !== b.size) return false\n\n // Convert to arrays for comparison\n const aValues = Array.from(a)\n const bValues = Array.from(b)\n\n // Simple comparison for primitive values\n if (aValues.every((val) => typeof val !== `object`)) {\n return aValues.every((val) => b.has(val))\n }\n\n // For objects in sets, we need to do a more complex comparison\n // This is a simplified approach and may not work for all cases\n return aValues.length === bValues.length\n }\n\n // Handle arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n\n return true\n }\n\n // Handle TypedArrays\n if (\n ArrayBuffer.isView(a) &&\n ArrayBuffer.isView(b) &&\n !(a instanceof DataView) &&\n !(b instanceof DataView)\n ) {\n const typedA = a as unknown as TypedArray\n const typedB = b as unknown as TypedArray\n if (typedA.length !== typedB.length) return false\n\n for (let i = 0; i < typedA.length; i++) {\n if (typedA[i] !== typedB[i]) return false\n }\n\n return true\n }\n\n // Handle plain objects\n const keysA = Object.keys(a as object)\n const keysB = Object.keys(b as object)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(b, key) &&\n deepEqual((a as any)[key], (b as any)[key])\n )\n}\n\nlet count = 0\nfunction getProxyCount() {\n count += 1\n return count\n}\n\n/**\n * Creates a proxy that tracks changes to the target object\n *\n * @param target The object to proxy\n * @param parent Optional parent information\n * @returns An object containing the proxy and a function to get the changes\n */\nexport function createChangeProxy<\n T extends Record<string | symbol, any | undefined>,\n>(\n target: T,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n): {\n proxy: T\n\n getChanges: () => Record<string | symbol, any>\n} {\n const changeProxyCache = new Map<object, object>()\n\n function memoizedCreateChangeProxy<\n TInner extends Record<string | symbol, any | undefined>,\n >(\n innerTarget: TInner,\n innerParent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n ): {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n } {\n debugLog(`Object ID:`, innerTarget.constructor.name)\n if (changeProxyCache.has(innerTarget)) {\n return changeProxyCache.get(innerTarget) as {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n }\n } else {\n const changeProxy = createChangeProxy(innerTarget, innerParent)\n changeProxyCache.set(innerTarget, changeProxy)\n return changeProxy\n }\n }\n // Create a WeakMap to cache proxies for nested objects\n // This prevents creating multiple proxies for the same object\n // and handles circular references\n const proxyCache = new Map<object, object>()\n\n // Create a change tracker to track changes to the object\n const changeTracker: ChangeTracker<T> = {\n copy_: deepClone(target),\n originalObject: deepClone(target),\n proxyCount: getProxyCount(),\n modified: false,\n assigned_: {},\n parent,\n target, // Store reference to the target object\n }\n\n debugLog(\n `createChangeProxy called for target`,\n target,\n changeTracker.proxyCount\n )\n // Mark this object and all its ancestors as modified\n // Also propagate the actual changes up the chain\n function markChanged(state: ChangeTracker<object>) {\n if (!state.modified) {\n state.modified = true\n }\n\n // Propagate the change up the parent chain\n if (state.parent) {\n debugLog(`propagating change to parent`)\n\n // Update parent's copy with this object's current state\n state.parent.tracker.copy_[state.parent.prop] = state.copy_\n state.parent.tracker.assigned_[state.parent.prop] = true\n\n // Mark parent as changed\n markChanged(state.parent.tracker)\n }\n }\n\n // Check if all properties in the current state have reverted to original values\n function checkIfReverted(\n state: ChangeTracker<Record<string | symbol, unknown>>\n ): boolean {\n debugLog(\n `checkIfReverted called with assigned keys:`,\n Object.keys(state.assigned_)\n )\n\n // If there are no assigned properties, object is unchanged\n if (\n Object.keys(state.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(state.assigned_).length === 0\n ) {\n debugLog(`No assigned properties, returning true`)\n return true\n }\n\n // Check each assigned regular property\n for (const prop in state.assigned_) {\n // If this property is marked as assigned\n if (state.assigned_[prop] === true) {\n const currentValue = state.copy_[prop]\n const originalValue = (state.originalObject as any)[prop]\n\n debugLog(\n `Checking property ${String(prop)}, current:`,\n currentValue,\n `original:`,\n originalValue\n )\n\n // If the value is not equal to original, something is still changed\n if (!deepEqual(currentValue, originalValue)) {\n debugLog(`Property ${String(prop)} is different, returning false`)\n return false\n }\n } else if (state.assigned_[prop] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Property ${String(prop)} was deleted, returning false`)\n return false\n }\n }\n\n // Check each assigned symbol property\n const symbolProps = Object.getOwnPropertySymbols(state.assigned_)\n for (const sym of symbolProps) {\n if (state.assigned_[sym] === true) {\n const currentValue = (state.copy_ as any)[sym]\n const originalValue = (state.originalObject as any)[sym]\n\n // If the value is not equal to original, something is still changed\n if (!deepEqual(currentValue, originalValue)) {\n debugLog(`Symbol property is different, returning false`)\n return false\n }\n } else if (state.assigned_[sym] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Symbol property was deleted, returning false`)\n return false\n }\n }\n\n debugLog(`All properties match original values, returning true`)\n // All assigned properties match their original values\n return true\n }\n\n // Update parent status based on child changes\n function checkParentStatus(\n parentState: ChangeTracker<Record<string | symbol, unknown>>,\n childProp: string | symbol\n ) {\n debugLog(`checkParentStatus called for child prop:`, childProp)\n\n // Check if all properties of the parent are reverted\n const isReverted = checkIfReverted(parentState)\n debugLog(`Parent checkIfReverted returned:`, isReverted)\n\n if (isReverted) {\n debugLog(`Parent is fully reverted, clearing tracking`)\n // If everything is reverted, clear the tracking\n parentState.modified = false\n parentState.assigned_ = {}\n\n // Continue up the chain\n if (parentState.parent) {\n debugLog(`Continuing up the parent chain`)\n checkParentStatus(parentState.parent.tracker, parentState.parent.prop)\n }\n }\n }\n\n // Create a proxy for the target object\n function createObjectProxy<TObj extends object>(obj: TObj): TObj {\n debugLog(`createObjectProxy`, obj)\n // If we've already created a proxy for this object, return it\n if (proxyCache.has(obj)) {\n debugLog(`proxyCache found match`)\n return proxyCache.get(obj) as TObj\n }\n\n // Create a proxy for the object\n const proxy = new Proxy(obj, {\n get(ptarget, prop) {\n debugLog(`get`, ptarget, prop)\n const value =\n changeTracker.copy_[prop as keyof T] ??\n changeTracker.originalObject[prop as keyof T]\n\n const originalValue = changeTracker.originalObject[prop as keyof T]\n\n debugLog(`value (at top of proxy get)`, value)\n\n // If it's a getter, return the value directly\n const desc = Object.getOwnPropertyDescriptor(ptarget, prop)\n if (desc?.get) {\n return value\n }\n\n // If the value is a function, bind it to the ptarget\n if (typeof value === `function`) {\n // For Map and Set methods that modify the collection\n if (ptarget instanceof Map || ptarget instanceof Set) {\n const methodName = prop.toString()\n const modifyingMethods = new Set([\n `set`,\n `delete`,\n `clear`,\n `add`,\n `pop`,\n `push`,\n `shift`,\n `unshift`,\n `splice`,\n `sort`,\n `reverse`,\n ])\n\n if (modifyingMethods.has(methodName)) {\n return function (...args: Array<unknown>) {\n const result = value.apply(changeTracker.copy_, args)\n markChanged(changeTracker)\n return result\n }\n }\n\n // Handle iterator methods for Map and Set\n const iteratorMethods = new Set([\n `entries`,\n `keys`,\n `values`,\n `forEach`,\n Symbol.iterator,\n ])\n\n if (iteratorMethods.has(methodName) || prop === Symbol.iterator) {\n return function (this: unknown, ...args: Array<unknown>) {\n const result = value.apply(changeTracker.copy_, args)\n\n // For forEach, we need to wrap the callback to track changes\n if (methodName === `forEach`) {\n const callback = args[0]\n if (typeof callback === `function`) {\n // Replace the original callback with our wrapped version\n const wrappedCallback = function (\n this: unknown,\n // eslint-disable-next-line\n value: unknown,\n key: unknown,\n collection: unknown\n ) {\n // Call the original callback\n const cbresult = callback.call(\n this,\n value,\n key,\n collection\n )\n // Mark as changed since the callback might have modified the value\n markChanged(changeTracker)\n return cbresult\n }\n // Call forEach with our wrapped callback\n return value.apply(ptarget, [\n wrappedCallback,\n ...args.slice(1),\n ])\n }\n }\n\n // For iterators (entries, keys, values, Symbol.iterator)\n if (\n methodName === `entries` ||\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // If it's an iterator, we need to wrap the returned iterator\n // to track changes when the values are accessed and potentially modified\n const originalIterator = result\n\n // Create a proxy for the iterator that will mark changes when next() is called\n return {\n next() {\n const nextResult = originalIterator.next()\n\n // If we have a value and it's an object, we need to track it\n if (\n !nextResult.done &&\n nextResult.value &&\n typeof nextResult.value === `object`\n ) {\n // For entries, the value is a [key, value] pair\n if (\n methodName === `entries` &&\n Array.isArray(nextResult.value) &&\n nextResult.value.length === 2\n ) {\n // The value is at index 1 in the [key, value] pair\n if (\n nextResult.value[1] &&\n typeof nextResult.value[1] === `object`\n ) {\n // Create a proxy for the value and replace it in the result\n const { proxy: valueProxy } =\n memoizedCreateChangeProxy(nextResult.value[1], {\n tracker: changeTracker,\n prop:\n typeof nextResult.value[0] === `symbol`\n ? nextResult.value[0]\n : String(nextResult.value[0]),\n })\n nextResult.value[1] = valueProxy\n }\n } else if (\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // If the value is an object, create a proxy for it\n if (\n typeof nextResult.value === `object` &&\n nextResult.value !== null\n ) {\n // For Set, we need to track the whole object\n // For Map, we would need the key, but we don't have it here\n // So we'll use a symbol as a placeholder\n const tempKey = Symbol(`iterator-value`)\n const { proxy: valueProxy } =\n memoizedCreateChangeProxy(nextResult.value, {\n tracker: changeTracker,\n prop: tempKey,\n })\n nextResult.value = valueProxy\n }\n }\n }\n\n return nextResult\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n\n return result\n }\n }\n }\n return value.bind(ptarget)\n }\n\n // If the value is an object, create a proxy for it\n if (\n value &&\n typeof value === `object` &&\n !((value as any) instanceof Date) &&\n !((value as any) instanceof RegExp)\n ) {\n // Create a parent reference for the nested object\n const nestedParent = {\n tracker: changeTracker,\n prop: String(prop),\n }\n\n // Create a proxy for the nested object\n const { proxy: nestedProxy } = memoizedCreateChangeProxy(\n originalValue,\n nestedParent\n )\n\n // Cache the proxy\n proxyCache.set(value, nestedProxy)\n\n return nestedProxy\n }\n\n return value\n },\n\n set(_sobj, prop, value) {\n const currentValue = changeTracker.copy_[prop as keyof T]\n debugLog(\n `set called for property ${String(prop)}, current:`,\n currentValue,\n `new:`,\n value\n )\n\n // Only track the change if the value is actually different\n if (!deepEqual(currentValue, value)) {\n // Check if the new value is equal to the original value\n // Important: Use the originalObject to get the true original value\n const originalValue = changeTracker.originalObject[prop as keyof T]\n const isRevertToOriginal = deepEqual(value, originalValue)\n debugLog(\n `value:`,\n value,\n `original:`,\n originalValue,\n `isRevertToOriginal:`,\n isRevertToOriginal\n )\n\n if (isRevertToOriginal) {\n debugLog(`Reverting property ${String(prop)} to original value`)\n // If the value is reverted to its original state, remove it from changes\n delete changeTracker.assigned_[prop.toString()]\n\n // Make sure the copy is updated with the original value\n debugLog(`Updating copy with original value for ${String(prop)}`)\n changeTracker.copy_[prop as keyof T] = deepClone(originalValue)\n\n // Check if all properties in this object have been reverted\n debugLog(`Checking if all properties reverted`)\n const allReverted = checkIfReverted(changeTracker)\n debugLog(`All reverted:`, allReverted)\n\n if (allReverted) {\n debugLog(`All properties reverted, clearing tracking`)\n // If all have been reverted, clear tracking\n changeTracker.modified = false\n changeTracker.assigned_ = {}\n\n // If we're a nested object, check if the parent needs updating\n if (parent) {\n debugLog(`Updating parent for property:`, parent.prop)\n checkParentStatus(parent.tracker, parent.prop)\n }\n } else {\n // Some properties are still changed\n debugLog(`Some properties still changed, keeping modified flag`)\n changeTracker.modified = true\n }\n } else {\n debugLog(`Setting new value for property ${String(prop)}`)\n\n // Set the value on the copy\n changeTracker.copy_[prop as keyof T] = value\n\n // Track that this property was assigned - store using the actual property (symbol or string)\n changeTracker.assigned_[prop.toString()] = true\n\n // Mark this object and its ancestors as modified\n debugLog(`Marking object and ancestors as modified`, changeTracker)\n markChanged(changeTracker)\n }\n } else {\n debugLog(`Value unchanged, not tracking`)\n }\n\n return true\n },\n\n defineProperty(_ptarget, prop, descriptor) {\n // const result = Reflect.defineProperty(\n // changeTracker.copy_,\n // prop,\n // descriptor\n // )\n // if (result) {\n if (`value` in descriptor) {\n changeTracker.copy_[prop as keyof T] = deepClone(descriptor.value)\n changeTracker.assigned_[prop.toString()] = true\n markChanged(changeTracker)\n }\n // }\n // return result\n return true\n },\n\n deleteProperty(dobj, prop) {\n debugLog(`deleteProperty`, dobj, prop)\n const stringProp = typeof prop === `symbol` ? prop.toString() : prop\n\n if (stringProp in dobj) {\n // Check if the property exists in the original object\n const hadPropertyInOriginal =\n stringProp in changeTracker.originalObject\n\n // Delete the property from the copy\n // Use type assertion to tell TypeScript this is allowed\n delete (changeTracker.copy_ as Record<string | symbol, unknown>)[prop]\n\n // If the property didn't exist in the original object, removing it\n // should revert to the original state\n if (!hadPropertyInOriginal) {\n delete changeTracker.copy_[stringProp]\n delete changeTracker.assigned_[stringProp]\n\n // If this is the last change and we're not a nested object,\n // mark the object as unmodified\n if (\n Object.keys(changeTracker.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(changeTracker.assigned_).length === 0\n ) {\n changeTracker.modified = false\n } else {\n // We still have changes, keep as modified\n changeTracker.modified = true\n }\n } else {\n // Mark this property as deleted\n changeTracker.assigned_[stringProp] = false\n changeTracker.copy_[stringProp as keyof T] = undefined as T[keyof T]\n markChanged(changeTracker)\n }\n }\n\n return true\n },\n })\n\n // Cache the proxy\n proxyCache.set(obj, proxy)\n\n return proxy\n }\n\n // Create a proxy for the target object\n const proxy = createObjectProxy(target)\n\n // Return the proxy and a function to get the changes\n return {\n proxy,\n getChanges: () => {\n debugLog(`getChanges called, modified:`, changeTracker.modified)\n debugLog(changeTracker)\n\n // First, check if the object is still considered modified\n if (!changeTracker.modified) {\n debugLog(`Object not modified, returning empty object`)\n return {}\n }\n\n // If we have a copy, return it directly\n // Check if valueObj is actually an object\n if (\n typeof changeTracker.copy_ !== `object` ||\n Array.isArray(changeTracker.copy_)\n ) {\n return changeTracker.copy_\n }\n\n if (Object.keys(changeTracker.assigned_).length === 0) {\n return changeTracker.copy_\n }\n\n const result: Record<string, any | undefined> = {}\n\n // Iterate through keys in keyObj\n for (const key in changeTracker.copy_) {\n // If the key's value is true and the key exists in valueObj\n if (\n changeTracker.assigned_[key] === true &&\n key in changeTracker.copy_\n ) {\n result[key] = changeTracker.copy_[key]\n }\n }\n debugLog(`Returning copy:`, result)\n return result as unknown as Record<string | symbol, unknown>\n },\n }\n}\n\n/**\n * Creates proxies for an array of objects and tracks changes to each\n *\n * @param targets Array of objects to proxy\n * @returns An object containing the array of proxies and a function to get all changes\n */\nexport function createArrayChangeProxy<T extends object>(\n targets: Array<T>\n): {\n proxies: Array<T>\n getChanges: () => Array<Record<string | symbol, unknown>>\n} {\n const proxiesWithChanges = targets.map((target) => createChangeProxy(target))\n\n return {\n proxies: proxiesWithChanges.map((p) => p.proxy),\n getChanges: () => proxiesWithChanges.map((p) => p.getChanges()),\n }\n}\n\n/**\n * Creates a proxy for an object, passes it to a callback function,\n * and returns the changes made by the callback\n *\n * @param target The object to proxy\n * @param callback Function that receives the proxy and can make changes to it\n * @returns The changes made to the object\n */\nexport function withChangeTracking<T extends object>(\n target: T,\n callback: (proxy: T) => void\n): Record<string | symbol, unknown> {\n const { proxy, getChanges } = createChangeProxy(target)\n\n callback(proxy)\n\n return getChanges()\n}\n\n/**\n * Creates proxies for an array of objects, passes them to a callback function,\n * and returns the changes made by the callback for each object\n *\n * @param targets Array of objects to proxy\n * @param callback Function that receives the proxies and can make changes to them\n * @returns Array of changes made to each object\n */\nexport function withArrayChangeTracking<T extends object>(\n targets: Array<T>,\n callback: (proxies: Array<T>) => void\n): Array<Record<string | symbol, unknown>> {\n const { proxies, getChanges } = createArrayChangeProxy(targets)\n\n callback(proxies)\n\n return getChanges()\n}\n"],"names":["clone","proxy","value"],"mappings":"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"}
1
+ {"version":3,"file":"proxy.cjs","sources":["../../src/proxy.ts"],"sourcesContent":["/**\n * A utility for creating a proxy that captures changes to an object\n * and provides a way to retrieve those changes.\n */\n\n/**\n * Simple debug utility that only logs when debug mode is enabled\n * Set DEBUG to true in localStorage to enable debug logging\n */\nfunction debugLog(...args: Array<unknown>): void {\n // Check if we're in a browser environment\n const isBrowser =\n typeof window !== `undefined` && typeof localStorage !== `undefined`\n\n // In browser, check localStorage for debug flag\n if (isBrowser && localStorage.getItem(`DEBUG`) === `true`) {\n console.log(`[proxy]`, ...args)\n }\n // In Node.js environment, check for environment variable (though this is primarily for browser)\n else if (\n // true\n !isBrowser &&\n typeof process !== `undefined` &&\n process.env.DEBUG === `true`\n ) {\n console.log(`[proxy]`, ...args)\n }\n}\n\n// Add TypedArray interface with proper type\ninterface TypedArray {\n length: number\n [index: number]: number\n}\n\n// Update type for ChangeTracker\ninterface ChangeTracker<T extends object> {\n originalObject: T\n modified: boolean\n copy_: T\n proxyCount: number\n assigned_: Record<string | symbol, boolean>\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n target: T\n}\n\n/**\n * Deep clones an object while preserving special types like Date and RegExp\n */\n\nfunction deepClone<T extends unknown>(\n obj: T,\n visited = new WeakMap<object, unknown>()\n): T {\n // Handle null and undefined\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle primitive types\n if (typeof obj !== `object`) {\n return obj\n }\n\n // If we've already cloned this object, return the cached clone\n if (visited.has(obj as object)) {\n return visited.get(obj as object) as T\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n const arrayClone = [] as Array<unknown>\n visited.set(obj as object, arrayClone)\n obj.forEach((item, index) => {\n arrayClone[index] = deepClone(item, visited)\n })\n return arrayClone as unknown as T\n }\n\n // Handle TypedArrays\n if (ArrayBuffer.isView(obj) && !(obj instanceof DataView)) {\n // Get the constructor to create a new instance of the same type\n const TypedArrayConstructor = Object.getPrototypeOf(obj).constructor\n const clone = new TypedArrayConstructor(\n (obj as unknown as TypedArray).length\n ) as unknown as TypedArray\n visited.set(obj as object, clone)\n\n // Copy the values\n for (let i = 0; i < (obj as unknown as TypedArray).length; i++) {\n clone[i] = (obj as unknown as TypedArray)[i]!\n }\n\n return clone as unknown as T\n }\n\n if (obj instanceof Map) {\n const clone = new Map() as Map<unknown, unknown>\n visited.set(obj as object, clone)\n obj.forEach((value, key) => {\n clone.set(key, deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n if (obj instanceof Set) {\n const clone = new Set()\n visited.set(obj as object, clone)\n obj.forEach((value) => {\n clone.add(deepClone(value, visited))\n })\n return clone as unknown as T\n }\n\n const clone = {} as Record<string | symbol, unknown>\n visited.set(obj as object, clone)\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n clone[key] = deepClone(\n (obj as Record<string | symbol, unknown>)[key],\n visited\n )\n }\n }\n\n const symbolProps = Object.getOwnPropertySymbols(obj)\n for (const sym of symbolProps) {\n clone[sym] = deepClone(\n (obj as Record<string | symbol, unknown>)[sym],\n visited\n )\n }\n\n return clone as T\n}\n\n/**\n * Deep equality check that handles special types like Date, RegExp, Map, and Set\n */\nfunction deepEqual<T>(a: T, b: T): boolean {\n // Handle primitive types\n if (a === b) return true\n\n // If either is null or not an object, they're not equal\n if (\n a === null ||\n b === null ||\n typeof a !== `object` ||\n typeof b !== `object`\n ) {\n return false\n }\n\n // Handle Date objects\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n }\n\n // Handle RegExp objects\n if (a instanceof RegExp && b instanceof RegExp) {\n return a.source === b.source && a.flags === b.flags\n }\n\n // Handle Map objects\n if (a instanceof Map && b instanceof Map) {\n if (a.size !== b.size) return false\n\n const entries = Array.from(a.entries())\n for (const [key, val] of entries) {\n if (!b.has(key) || !deepEqual(val, b.get(key))) {\n return false\n }\n }\n\n return true\n }\n\n // Handle Set objects\n if (a instanceof Set && b instanceof Set) {\n if (a.size !== b.size) return false\n\n // Convert to arrays for comparison\n const aValues = Array.from(a)\n const bValues = Array.from(b)\n\n // Simple comparison for primitive values\n if (aValues.every((val) => typeof val !== `object`)) {\n return aValues.every((val) => b.has(val))\n }\n\n // For objects in sets, we need to do a more complex comparison\n // This is a simplified approach and may not work for all cases\n return aValues.length === bValues.length\n }\n\n // Handle arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false\n\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n\n return true\n }\n\n // Handle TypedArrays\n if (\n ArrayBuffer.isView(a) &&\n ArrayBuffer.isView(b) &&\n !(a instanceof DataView) &&\n !(b instanceof DataView)\n ) {\n const typedA = a as unknown as TypedArray\n const typedB = b as unknown as TypedArray\n if (typedA.length !== typedB.length) return false\n\n for (let i = 0; i < typedA.length; i++) {\n if (typedA[i] !== typedB[i]) return false\n }\n\n return true\n }\n\n // Handle plain objects\n const keysA = Object.keys(a as object)\n const keysB = Object.keys(b as object)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(b, key) &&\n deepEqual((a as any)[key], (b as any)[key])\n )\n}\n\nlet count = 0\nfunction getProxyCount() {\n count += 1\n return count\n}\n\n/**\n * Creates a proxy that tracks changes to the target object\n *\n * @param target The object to proxy\n * @param parent Optional parent information\n * @returns An object containing the proxy and a function to get the changes\n */\nexport function createChangeProxy<\n T extends Record<string | symbol, any | undefined>,\n>(\n target: T,\n parent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n): {\n proxy: T\n\n getChanges: () => Record<string | symbol, any>\n} {\n const changeProxyCache = new Map<object, object>()\n\n function memoizedCreateChangeProxy<\n TInner extends Record<string | symbol, any | undefined>,\n >(\n innerTarget: TInner,\n innerParent?: {\n tracker: ChangeTracker<Record<string | symbol, unknown>>\n prop: string | symbol\n }\n ): {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n } {\n debugLog(`Object ID:`, innerTarget.constructor.name)\n if (changeProxyCache.has(innerTarget)) {\n return changeProxyCache.get(innerTarget) as {\n proxy: TInner\n getChanges: () => Record<string | symbol, any>\n }\n } else {\n const changeProxy = createChangeProxy(innerTarget, innerParent)\n changeProxyCache.set(innerTarget, changeProxy)\n return changeProxy\n }\n }\n // Create a WeakMap to cache proxies for nested objects\n // This prevents creating multiple proxies for the same object\n // and handles circular references\n const proxyCache = new Map<object, object>()\n\n // Create a change tracker to track changes to the object\n const changeTracker: ChangeTracker<T> = {\n copy_: deepClone(target),\n originalObject: deepClone(target),\n proxyCount: getProxyCount(),\n modified: false,\n assigned_: {},\n parent,\n target, // Store reference to the target object\n }\n\n debugLog(\n `createChangeProxy called for target`,\n target,\n changeTracker.proxyCount\n )\n // Mark this object and all its ancestors as modified\n // Also propagate the actual changes up the chain\n function markChanged(state: ChangeTracker<object>) {\n if (!state.modified) {\n state.modified = true\n }\n\n // Propagate the change up the parent chain\n if (state.parent) {\n debugLog(`propagating change to parent`)\n\n // Update parent's copy with this object's current state\n state.parent.tracker.copy_[state.parent.prop] = state.copy_\n state.parent.tracker.assigned_[state.parent.prop] = true\n\n // Mark parent as changed\n markChanged(state.parent.tracker)\n }\n }\n\n // Check if all properties in the current state have reverted to original values\n function checkIfReverted(\n state: ChangeTracker<Record<string | symbol, unknown>>\n ): boolean {\n debugLog(\n `checkIfReverted called with assigned keys:`,\n Object.keys(state.assigned_)\n )\n\n // If there are no assigned properties, object is unchanged\n if (\n Object.keys(state.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(state.assigned_).length === 0\n ) {\n debugLog(`No assigned properties, returning true`)\n return true\n }\n\n // Check each assigned regular property\n for (const prop in state.assigned_) {\n // If this property is marked as assigned\n if (state.assigned_[prop] === true) {\n const currentValue = state.copy_[prop]\n const originalValue = (state.originalObject as any)[prop]\n\n debugLog(\n `Checking property ${String(prop)}, current:`,\n currentValue,\n `original:`,\n originalValue\n )\n\n // If the value is not equal to original, something is still changed\n if (!deepEqual(currentValue, originalValue)) {\n debugLog(`Property ${String(prop)} is different, returning false`)\n return false\n }\n } else if (state.assigned_[prop] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Property ${String(prop)} was deleted, returning false`)\n return false\n }\n }\n\n // Check each assigned symbol property\n const symbolProps = Object.getOwnPropertySymbols(state.assigned_)\n for (const sym of symbolProps) {\n if (state.assigned_[sym] === true) {\n const currentValue = (state.copy_ as any)[sym]\n const originalValue = (state.originalObject as any)[sym]\n\n // If the value is not equal to original, something is still changed\n if (!deepEqual(currentValue, originalValue)) {\n debugLog(`Symbol property is different, returning false`)\n return false\n }\n } else if (state.assigned_[sym] === false) {\n // Property was deleted, so it's different from original\n debugLog(`Symbol property was deleted, returning false`)\n return false\n }\n }\n\n debugLog(`All properties match original values, returning true`)\n // All assigned properties match their original values\n return true\n }\n\n // Update parent status based on child changes\n function checkParentStatus(\n parentState: ChangeTracker<Record<string | symbol, unknown>>,\n childProp: string | symbol\n ) {\n debugLog(`checkParentStatus called for child prop:`, childProp)\n\n // Check if all properties of the parent are reverted\n const isReverted = checkIfReverted(parentState)\n debugLog(`Parent checkIfReverted returned:`, isReverted)\n\n if (isReverted) {\n debugLog(`Parent is fully reverted, clearing tracking`)\n // If everything is reverted, clear the tracking\n parentState.modified = false\n parentState.assigned_ = {}\n\n // Continue up the chain\n if (parentState.parent) {\n debugLog(`Continuing up the parent chain`)\n checkParentStatus(parentState.parent.tracker, parentState.parent.prop)\n }\n }\n }\n\n // Create a proxy for the target object\n function createObjectProxy<TObj extends object>(obj: TObj): TObj {\n debugLog(`createObjectProxy`, obj)\n // If we've already created a proxy for this object, return it\n if (proxyCache.has(obj)) {\n debugLog(`proxyCache found match`)\n return proxyCache.get(obj) as TObj\n }\n\n // Create a proxy for the object\n const proxy = new Proxy(obj, {\n get(ptarget, prop) {\n debugLog(`get`, ptarget, prop)\n const value =\n changeTracker.copy_[prop as keyof T] ??\n changeTracker.originalObject[prop as keyof T]\n\n const originalValue = changeTracker.originalObject[prop as keyof T]\n\n debugLog(`value (at top of proxy get)`, value)\n\n // If it's a getter, return the value directly\n const desc = Object.getOwnPropertyDescriptor(ptarget, prop)\n if (desc?.get) {\n return value\n }\n\n // If the value is a function, bind it to the ptarget\n if (typeof value === `function`) {\n // For Array methods that modify the array\n if (Array.isArray(ptarget)) {\n const methodName = prop.toString()\n const modifyingMethods = new Set([\n `pop`,\n `push`,\n `shift`,\n `unshift`,\n `splice`,\n `sort`,\n `reverse`,\n `fill`,\n `copyWithin`,\n ])\n\n if (modifyingMethods.has(methodName)) {\n return function (...args: Array<unknown>) {\n const result = value.apply(changeTracker.copy_, args)\n markChanged(changeTracker)\n return result\n }\n }\n }\n\n // For Map and Set methods that modify the collection\n if (ptarget instanceof Map || ptarget instanceof Set) {\n const methodName = prop.toString()\n const modifyingMethods = new Set([\n `set`,\n `delete`,\n `clear`,\n `add`,\n `pop`,\n `push`,\n `shift`,\n `unshift`,\n `splice`,\n `sort`,\n `reverse`,\n ])\n\n if (modifyingMethods.has(methodName)) {\n return function (...args: Array<unknown>) {\n const result = value.apply(changeTracker.copy_, args)\n markChanged(changeTracker)\n return result\n }\n }\n\n // Handle iterator methods for Map and Set\n const iteratorMethods = new Set([\n `entries`,\n `keys`,\n `values`,\n `forEach`,\n Symbol.iterator,\n ])\n\n if (iteratorMethods.has(methodName) || prop === Symbol.iterator) {\n return function (this: unknown, ...args: Array<unknown>) {\n const result = value.apply(changeTracker.copy_, args)\n\n // For forEach, we need to wrap the callback to track changes\n if (methodName === `forEach`) {\n const callback = args[0]\n if (typeof callback === `function`) {\n // Replace the original callback with our wrapped version\n const wrappedCallback = function (\n this: unknown,\n // eslint-disable-next-line\n value: unknown,\n key: unknown,\n collection: unknown\n ) {\n // Call the original callback\n const cbresult = callback.call(\n this,\n value,\n key,\n collection\n )\n // Mark as changed since the callback might have modified the value\n markChanged(changeTracker)\n return cbresult\n }\n // Call forEach with our wrapped callback\n return value.apply(ptarget, [\n wrappedCallback,\n ...args.slice(1),\n ])\n }\n }\n\n // For iterators (entries, keys, values, Symbol.iterator)\n if (\n methodName === `entries` ||\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // If it's an iterator, we need to wrap the returned iterator\n // to track changes when the values are accessed and potentially modified\n const originalIterator = result\n\n // Create a proxy for the iterator that will mark changes when next() is called\n return {\n next() {\n const nextResult = originalIterator.next()\n\n // If we have a value and it's an object, we need to track it\n if (\n !nextResult.done &&\n nextResult.value &&\n typeof nextResult.value === `object`\n ) {\n // For entries, the value is a [key, value] pair\n if (\n methodName === `entries` &&\n Array.isArray(nextResult.value) &&\n nextResult.value.length === 2\n ) {\n // The value is at index 1 in the [key, value] pair\n if (\n nextResult.value[1] &&\n typeof nextResult.value[1] === `object`\n ) {\n // Create a proxy for the value and replace it in the result\n const { proxy: valueProxy } =\n memoizedCreateChangeProxy(nextResult.value[1], {\n tracker: changeTracker,\n prop:\n typeof nextResult.value[0] === `symbol`\n ? nextResult.value[0]\n : String(nextResult.value[0]),\n })\n nextResult.value[1] = valueProxy\n }\n } else if (\n methodName === `values` ||\n methodName === Symbol.iterator.toString() ||\n prop === Symbol.iterator\n ) {\n // If the value is an object, create a proxy for it\n if (\n typeof nextResult.value === `object` &&\n nextResult.value !== null\n ) {\n // For Set, we need to track the whole object\n // For Map, we would need the key, but we don't have it here\n // So we'll use a symbol as a placeholder\n const tempKey = Symbol(`iterator-value`)\n const { proxy: valueProxy } =\n memoizedCreateChangeProxy(nextResult.value, {\n tracker: changeTracker,\n prop: tempKey,\n })\n nextResult.value = valueProxy\n }\n }\n }\n\n return nextResult\n },\n [Symbol.iterator]() {\n return this\n },\n }\n }\n\n return result\n }\n }\n }\n return value.bind(ptarget)\n }\n\n // If the value is an object, create a proxy for it\n if (\n value &&\n typeof value === `object` &&\n !((value as any) instanceof Date) &&\n !((value as any) instanceof RegExp)\n ) {\n // Create a parent reference for the nested object\n const nestedParent = {\n tracker: changeTracker,\n prop: String(prop),\n }\n\n // Create a proxy for the nested object\n const { proxy: nestedProxy } = memoizedCreateChangeProxy(\n originalValue,\n nestedParent\n )\n\n // Cache the proxy\n proxyCache.set(value, nestedProxy)\n\n return nestedProxy\n }\n\n return value\n },\n\n set(_sobj, prop, value) {\n const currentValue = changeTracker.copy_[prop as keyof T]\n debugLog(\n `set called for property ${String(prop)}, current:`,\n currentValue,\n `new:`,\n value\n )\n\n // Only track the change if the value is actually different\n if (!deepEqual(currentValue, value)) {\n // Check if the new value is equal to the original value\n // Important: Use the originalObject to get the true original value\n const originalValue = changeTracker.originalObject[prop as keyof T]\n const isRevertToOriginal = deepEqual(value, originalValue)\n debugLog(\n `value:`,\n value,\n `original:`,\n originalValue,\n `isRevertToOriginal:`,\n isRevertToOriginal\n )\n\n if (isRevertToOriginal) {\n debugLog(`Reverting property ${String(prop)} to original value`)\n // If the value is reverted to its original state, remove it from changes\n delete changeTracker.assigned_[prop.toString()]\n\n // Make sure the copy is updated with the original value\n debugLog(`Updating copy with original value for ${String(prop)}`)\n changeTracker.copy_[prop as keyof T] = deepClone(originalValue)\n\n // Check if all properties in this object have been reverted\n debugLog(`Checking if all properties reverted`)\n const allReverted = checkIfReverted(changeTracker)\n debugLog(`All reverted:`, allReverted)\n\n if (allReverted) {\n debugLog(`All properties reverted, clearing tracking`)\n // If all have been reverted, clear tracking\n changeTracker.modified = false\n changeTracker.assigned_ = {}\n\n // If we're a nested object, check if the parent needs updating\n if (parent) {\n debugLog(`Updating parent for property:`, parent.prop)\n checkParentStatus(parent.tracker, parent.prop)\n }\n } else {\n // Some properties are still changed\n debugLog(`Some properties still changed, keeping modified flag`)\n changeTracker.modified = true\n }\n } else {\n debugLog(`Setting new value for property ${String(prop)}`)\n\n // Set the value on the copy\n changeTracker.copy_[prop as keyof T] = value\n\n // Track that this property was assigned - store using the actual property (symbol or string)\n changeTracker.assigned_[prop.toString()] = true\n\n // Mark this object and its ancestors as modified\n debugLog(`Marking object and ancestors as modified`, changeTracker)\n markChanged(changeTracker)\n }\n } else {\n debugLog(`Value unchanged, not tracking`)\n }\n\n return true\n },\n\n defineProperty(_ptarget, prop, descriptor) {\n // const result = Reflect.defineProperty(\n // changeTracker.copy_,\n // prop,\n // descriptor\n // )\n // if (result) {\n if (`value` in descriptor) {\n changeTracker.copy_[prop as keyof T] = deepClone(descriptor.value)\n changeTracker.assigned_[prop.toString()] = true\n markChanged(changeTracker)\n }\n // }\n // return result\n return true\n },\n\n deleteProperty(dobj, prop) {\n debugLog(`deleteProperty`, dobj, prop)\n const stringProp = typeof prop === `symbol` ? prop.toString() : prop\n\n if (stringProp in dobj) {\n // Check if the property exists in the original object\n const hadPropertyInOriginal =\n stringProp in changeTracker.originalObject\n\n // Delete the property from the copy\n // Use type assertion to tell TypeScript this is allowed\n delete (changeTracker.copy_ as Record<string | symbol, unknown>)[prop]\n\n // If the property didn't exist in the original object, removing it\n // should revert to the original state\n if (!hadPropertyInOriginal) {\n delete changeTracker.copy_[stringProp]\n delete changeTracker.assigned_[stringProp]\n\n // If this is the last change and we're not a nested object,\n // mark the object as unmodified\n if (\n Object.keys(changeTracker.assigned_).length === 0 &&\n Object.getOwnPropertySymbols(changeTracker.assigned_).length === 0\n ) {\n changeTracker.modified = false\n } else {\n // We still have changes, keep as modified\n changeTracker.modified = true\n }\n } else {\n // Mark this property as deleted\n changeTracker.assigned_[stringProp] = false\n changeTracker.copy_[stringProp as keyof T] = undefined as T[keyof T]\n markChanged(changeTracker)\n }\n }\n\n return true\n },\n })\n\n // Cache the proxy\n proxyCache.set(obj, proxy)\n\n return proxy\n }\n\n // Create a proxy for the target object\n const proxy = createObjectProxy(target)\n\n // Return the proxy and a function to get the changes\n return {\n proxy,\n getChanges: () => {\n debugLog(`getChanges called, modified:`, changeTracker.modified)\n debugLog(changeTracker)\n\n // First, check if the object is still considered modified\n if (!changeTracker.modified) {\n debugLog(`Object not modified, returning empty object`)\n return {}\n }\n\n // If we have a copy, return it directly\n // Check if valueObj is actually an object\n if (\n typeof changeTracker.copy_ !== `object` ||\n Array.isArray(changeTracker.copy_)\n ) {\n return changeTracker.copy_\n }\n\n if (Object.keys(changeTracker.assigned_).length === 0) {\n return changeTracker.copy_\n }\n\n const result: Record<string, any | undefined> = {}\n\n // Iterate through keys in keyObj\n for (const key in changeTracker.copy_) {\n // If the key's value is true and the key exists in valueObj\n if (\n changeTracker.assigned_[key] === true &&\n key in changeTracker.copy_\n ) {\n result[key] = changeTracker.copy_[key]\n }\n }\n debugLog(`Returning copy:`, result)\n return result as unknown as Record<string | symbol, unknown>\n },\n }\n}\n\n/**\n * Creates proxies for an array of objects and tracks changes to each\n *\n * @param targets Array of objects to proxy\n * @returns An object containing the array of proxies and a function to get all changes\n */\nexport function createArrayChangeProxy<T extends object>(\n targets: Array<T>\n): {\n proxies: Array<T>\n getChanges: () => Array<Record<string | symbol, unknown>>\n} {\n const proxiesWithChanges = targets.map((target) => createChangeProxy(target))\n\n return {\n proxies: proxiesWithChanges.map((p) => p.proxy),\n getChanges: () => proxiesWithChanges.map((p) => p.getChanges()),\n }\n}\n\n/**\n * Creates a proxy for an object, passes it to a callback function,\n * and returns the changes made by the callback\n *\n * @param target The object to proxy\n * @param callback Function that receives the proxy and can make changes to it\n * @returns The changes made to the object\n */\nexport function withChangeTracking<T extends object>(\n target: T,\n callback: (proxy: T) => void\n): Record<string | symbol, unknown> {\n const { proxy, getChanges } = createChangeProxy(target)\n\n callback(proxy)\n\n return getChanges()\n}\n\n/**\n * Creates proxies for an array of objects, passes them to a callback function,\n * and returns the changes made by the callback for each object\n *\n * @param targets Array of objects to proxy\n * @param callback Function that receives the proxies and can make changes to them\n * @returns Array of changes made to each object\n */\nexport function withArrayChangeTracking<T extends object>(\n targets: Array<T>,\n callback: (proxies: Array<T>) => void\n): Array<Record<string | symbol, unknown>> {\n const { proxies, getChanges } = createArrayChangeProxy(targets)\n\n callback(proxies)\n\n return getChanges()\n}\n"],"names":["clone","proxy","value"],"mappings":"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"}
@@ -112,6 +112,78 @@ class BaseQueryBuilder {
112
112
  join: [...existingJoins, joinClause]
113
113
  });
114
114
  }
115
+ /**
116
+ * Perform a LEFT JOIN with another table or subquery
117
+ *
118
+ * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
119
+ * @param onCallback - A function that receives table references and returns the join condition
120
+ * @returns A QueryBuilder with the left joined table available
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * // Left join users with posts
125
+ * query
126
+ * .from({ users: usersCollection })
127
+ * .leftJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
128
+ * ```
129
+ */
130
+ leftJoin(source, onCallback) {
131
+ return this.join(source, onCallback, `left`);
132
+ }
133
+ /**
134
+ * Perform a RIGHT JOIN with another table or subquery
135
+ *
136
+ * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
137
+ * @param onCallback - A function that receives table references and returns the join condition
138
+ * @returns A QueryBuilder with the right joined table available
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * // Right join users with posts
143
+ * query
144
+ * .from({ users: usersCollection })
145
+ * .rightJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
146
+ * ```
147
+ */
148
+ rightJoin(source, onCallback) {
149
+ return this.join(source, onCallback, `right`);
150
+ }
151
+ /**
152
+ * Perform an INNER JOIN with another table or subquery
153
+ *
154
+ * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
155
+ * @param onCallback - A function that receives table references and returns the join condition
156
+ * @returns A QueryBuilder with the inner joined table available
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * // Inner join users with posts
161
+ * query
162
+ * .from({ users: usersCollection })
163
+ * .innerJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
164
+ * ```
165
+ */
166
+ innerJoin(source, onCallback) {
167
+ return this.join(source, onCallback, `inner`);
168
+ }
169
+ /**
170
+ * Perform a FULL JOIN with another table or subquery
171
+ *
172
+ * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
173
+ * @param onCallback - A function that receives table references and returns the join condition
174
+ * @returns A QueryBuilder with the full joined table available
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * // Full join users with posts
179
+ * query
180
+ * .from({ users: usersCollection })
181
+ * .fullJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
182
+ * ```
183
+ */
184
+ fullJoin(source, onCallback) {
185
+ return this.join(source, onCallback, `full`);
186
+ }
115
187
  /**
116
188
  * Filter rows based on a condition
117
189
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../../src/query/builder/index.ts"],"sourcesContent":["import { CollectionImpl } from \"../../collection.js\"\nimport { CollectionRef, QueryRef } from \"../ir.js\"\nimport { createRefProxy, isRefProxy, toExpression } from \"./ref-proxy.js\"\nimport type { NamespacedRow } from \"../../types.js\"\nimport type {\n Aggregate,\n BasicExpression,\n JoinClause,\n OrderBy,\n OrderByClause,\n OrderByDirection,\n QueryIR,\n} from \"../ir.js\"\nimport type {\n Context,\n GroupByCallback,\n JoinOnCallback,\n MergeContext,\n MergeContextWithJoinType,\n OrderByCallback,\n RefProxyForContext,\n ResultTypeFromSelect,\n SchemaFromSource,\n SelectObject,\n Source,\n WhereCallback,\n WithResult,\n} from \"./types.js\"\n\nexport class BaseQueryBuilder<TContext extends Context = Context> {\n private readonly query: Partial<QueryIR> = {}\n\n constructor(query: Partial<QueryIR> = {}) {\n this.query = { ...query }\n }\n\n /**\n * Creates a CollectionRef or QueryRef from a source object\n * @param source - An object with a single key-value pair\n * @param context - Context string for error messages (e.g., \"from clause\", \"join clause\")\n * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference\n */\n private _createRefForSource<TSource extends Source>(\n source: TSource,\n context: string\n ): [string, CollectionRef | QueryRef] {\n if (Object.keys(source).length !== 1) {\n throw new Error(`Only one source is allowed in the ${context}`)\n }\n\n const alias = Object.keys(source)[0]!\n const sourceValue = source[alias]\n\n let ref: CollectionRef | QueryRef\n\n if (sourceValue instanceof CollectionImpl) {\n ref = new CollectionRef(sourceValue, alias)\n } else if (sourceValue instanceof BaseQueryBuilder) {\n const subQuery = sourceValue._getQuery()\n if (!(subQuery as Partial<QueryIR>).from) {\n throw new Error(\n `A sub query passed to a ${context} must have a from clause itself`\n )\n }\n ref = new QueryRef(subQuery, alias)\n } else {\n throw new Error(`Invalid source`)\n }\n\n return [alias, ref]\n }\n\n /**\n * Specify the source table or subquery for the query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @returns A QueryBuilder with the specified source\n *\n * @example\n * ```ts\n * // Query from a collection\n * query.from({ users: usersCollection })\n *\n * // Query from a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query.from({ activeUsers })\n * ```\n */\n from<TSource extends Source>(\n source: TSource\n ): QueryBuilder<{\n baseSchema: SchemaFromSource<TSource>\n schema: SchemaFromSource<TSource>\n fromSourceName: keyof TSource & string\n hasJoins: false\n }> {\n const [, from] = this._createRefForSource(source, `from clause`)\n\n return new BaseQueryBuilder({\n ...this.query,\n from,\n }) as any\n }\n\n /**\n * Join another table or subquery to the current query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')\n * @returns A QueryBuilder with the joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n *\n * // Inner join with explicit type\n * query\n * .from({ u: usersCollection })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')\n * ```\n *\n * // Join with a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query\n * .from({ activeUsers })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))\n */\n join<\n TSource extends Source,\n TJoinType extends `inner` | `left` | `right` | `full` = `left`,\n >(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >,\n type: TJoinType = `left` as TJoinType\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>\n > {\n const [alias, from] = this._createRefForSource(source, `join clause`)\n\n // Create a temporary context for the callback\n const currentAliases = this._getCurrentAliases()\n const newAliases = [...currentAliases, alias]\n const refProxy = createRefProxy(newAliases) as RefProxyForContext<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n\n // Get the join condition expression\n const onExpression = onCallback(refProxy)\n\n // Extract left and right from the expression\n // For now, we'll assume it's an eq function with two arguments\n let left: BasicExpression\n let right: BasicExpression\n\n if (\n onExpression.type === `func` &&\n onExpression.name === `eq` &&\n onExpression.args.length === 2\n ) {\n left = onExpression.args[0]!\n right = onExpression.args[1]!\n } else {\n throw new Error(`Join condition must be an equality expression`)\n }\n\n const joinClause: JoinClause = {\n from,\n type,\n left,\n right,\n }\n\n const existingJoins = this.query.join || []\n\n return new BaseQueryBuilder({\n ...this.query,\n join: [...existingJoins, joinClause],\n }) as any\n }\n\n /**\n * Filter rows based on a condition\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the where condition applied\n *\n * @example\n * ```ts\n * // Simple condition\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n *\n * // Multiple conditions\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => and(\n * gt(users.age, 18),\n * eq(users.active, true)\n * ))\n *\n * // Multiple where calls are ANDed together\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n * .where(({users}) => eq(users.active, true))\n * ```\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingWhere = this.query.where || []\n\n return new BaseQueryBuilder({\n ...this.query,\n where: [...existingWhere, expression],\n }) as any\n }\n\n /**\n * Filter grouped rows based on aggregate conditions\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the having condition applied\n *\n * @example\n * ```ts\n * // Filter groups by count\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .having(({posts}) => gt(count(posts.id), 5))\n *\n * // Filter by average\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(avg(orders.total), 100))\n *\n * // Multiple having calls are ANDed together\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(count(orders.id), 5))\n * .having(({orders}) => gt(avg(orders.total), 100))\n * ```\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingHaving = this.query.having || []\n\n return new BaseQueryBuilder({\n ...this.query,\n having: [...existingHaving, expression],\n }) as any\n }\n\n /**\n * Select specific columns or computed values from the query\n *\n * @param callback - A function that receives table references and returns an object with selected fields or expressions\n * @returns A QueryBuilder that returns only the selected fields\n *\n * @example\n * ```ts\n * // Select specific columns\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * name: users.name,\n * email: users.email\n * }))\n *\n * // Select with computed values\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * fullName: concat(users.firstName, ' ', users.lastName),\n * ageInMonths: mul(users.age, 12)\n * }))\n *\n * // Select with aggregates (requires GROUP BY)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count(posts.id)\n * }))\n * ```\n */\n select<TSelectObject extends SelectObject>(\n callback: (refs: RefProxyForContext<TContext>) => TSelectObject\n ): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const selectObject = callback(refProxy)\n\n // Check if any tables were spread during the callback\n const spreadSentinels = (refProxy as any).__spreadSentinels as Set<string>\n\n // Convert the select object to use expressions, including spread sentinels\n const select: Record<string, BasicExpression | Aggregate> = {}\n\n // First, add spread sentinels for any tables that were spread\n for (const spreadAlias of spreadSentinels) {\n const sentinelKey = `__SPREAD_SENTINEL__${spreadAlias}`\n select[sentinelKey] = toExpression(spreadAlias) // Use alias as a simple reference\n }\n\n // Then add the explicit select fields\n for (const [key, value] of Object.entries(selectObject)) {\n if (isRefProxy(value)) {\n select[key] = toExpression(value)\n } else if (\n typeof value === `object` &&\n `type` in value &&\n (value.type === `agg` || value.type === `func`)\n ) {\n select[key] = value as BasicExpression | Aggregate\n } else {\n select[key] = toExpression(value)\n }\n }\n\n return new BaseQueryBuilder({\n ...this.query,\n select,\n fnSelect: undefined, // remove the fnSelect clause if it exists\n }) as any\n }\n\n /**\n * Sort the query results by one or more columns\n *\n * @param callback - A function that receives table references and returns the field to sort by\n * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')\n * @returns A QueryBuilder with the ordering applied\n *\n * @example\n * ```ts\n * // Sort by a single column\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.name)\n *\n * // Sort descending\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.createdAt, 'desc')\n *\n * // Multiple sorts (chain orderBy calls)\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.lastName)\n * .orderBy(({users}) => users.firstName)\n * ```\n */\n orderBy(\n callback: OrderByCallback<TContext>,\n direction: OrderByDirection = `asc`\n ): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n // Create the new OrderBy structure with expression and direction\n const orderByClause: OrderByClause = {\n expression: toExpression(result),\n direction,\n }\n\n const existingOrderBy: OrderBy = this.query.orderBy || []\n\n return new BaseQueryBuilder({\n ...this.query,\n orderBy: [...existingOrderBy, orderByClause],\n }) as any\n }\n\n /**\n * Group rows by one or more columns for aggregation\n *\n * @param callback - A function that receives table references and returns the field(s) to group by\n * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)\n *\n * @example\n * ```ts\n * // Group by a single column\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count()\n * }))\n *\n * // Group by multiple columns\n * query\n * .from({ sales: salesCollection })\n * .groupBy(({sales}) => [sales.region, sales.category])\n * .select(({sales, sum}) => ({\n * region: sales.region,\n * category: sales.category,\n * totalSales: sum(sales.amount)\n * }))\n * ```\n */\n groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n const newExpressions = Array.isArray(result)\n ? result.map((r) => toExpression(r))\n : [toExpression(result)]\n\n // Replace existing groupBy expressions instead of extending them\n return new BaseQueryBuilder({\n ...this.query,\n groupBy: newExpressions,\n }) as any\n }\n\n /**\n * Limit the number of rows returned by the query\n * `orderBy` is required for `limit`\n *\n * @param count - Maximum number of rows to return\n * @returns A QueryBuilder with the limit applied\n *\n * @example\n * ```ts\n * // Get top 5 posts by likes\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.likes, 'desc')\n * .limit(5)\n * ```\n */\n limit(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n limit: count,\n }) as any\n }\n\n /**\n * Skip a number of rows before returning results\n * `orderBy` is required for `offset`\n *\n * @param count - Number of rows to skip\n * @returns A QueryBuilder with the offset applied\n *\n * @example\n * ```ts\n * // Get second page of results\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.createdAt, 'desc')\n * .offset(page * pageSize)\n * .limit(pageSize)\n * ```\n */\n offset(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n offset: count,\n }) as any\n }\n\n /**\n * Specify that the query should return distinct rows.\n * Deduplicates rows based on the selected columns.\n * @returns A QueryBuilder with distinct enabled\n *\n * @example\n * ```ts\n * // Get countries our users are from\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => users.country)\n * .distinct()\n * ```\n */\n distinct(): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n distinct: true,\n }) as any\n }\n\n // Helper methods\n private _getCurrentAliases(): Array<string> {\n const aliases: Array<string> = []\n\n // Add the from alias\n if (this.query.from) {\n aliases.push(this.query.from.alias)\n }\n\n // Add join aliases\n if (this.query.join) {\n for (const join of this.query.join) {\n aliases.push(join.from.alias)\n }\n }\n\n return aliases\n }\n\n /**\n * Functional variants of the query builder\n * These are imperative function that are called for ery row.\n * Warning: that these cannot be optimized by the query compiler, and may prevent\n * some type of optimizations being possible.\n * @example\n * ```ts\n * q.fn.select((row) => ({\n * name: row.user.name.toUpperCase(),\n * age: row.user.age + 1,\n * }))\n * ```\n */\n get fn() {\n const builder = this\n return {\n /**\n * Select fields using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns the selected value\n * @returns A QueryBuilder with functional selection applied\n *\n * @example\n * ```ts\n * // Functional select (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.select(row => ({\n * name: row.users.name.toUpperCase(),\n * age: row.users.age + 1,\n * }))\n * ```\n */\n select<TFuncSelectResult>(\n callback: (row: TContext[`schema`]) => TFuncSelectResult\n ): QueryBuilder<WithResult<TContext, TFuncSelectResult>> {\n return new BaseQueryBuilder({\n ...builder.query,\n select: undefined, // remove the select clause if it exists\n fnSelect: callback,\n })\n },\n /**\n * Filter rows using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns a boolean\n * @returns A QueryBuilder with functional filtering applied\n *\n * @example\n * ```ts\n * // Functional where (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.where(row => row.users.name.startsWith('A'))\n * ```\n */\n where(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnWhere: [\n ...(builder.query.fnWhere || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n /**\n * Filter grouped rows using a function that operates on each aggregated row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives an aggregated row and returns a boolean\n * @returns A QueryBuilder with functional having filter applied\n *\n * @example\n * ```ts\n * // Functional having (not optimized)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .fn.having(row => row.count > 5)\n * ```\n */\n having(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnHaving: [\n ...(builder.query.fnHaving || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n }\n }\n\n _getQuery(): QueryIR {\n if (!this.query.from) {\n throw new Error(`Query must have a from clause`)\n }\n return this.query as QueryIR\n }\n}\n\n// Internal function to build a query from a callback\n// used by liveQueryCollectionOptions.query\nexport function buildQuery<TContext extends Context>(\n fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>\n): QueryIR {\n const result = fn(new BaseQueryBuilder())\n return getQueryIR(result)\n}\n\n// Internal function to get the QueryIR from a builder\nexport function getQueryIR(\n builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder\n): QueryIR {\n return (builder as unknown as BaseQueryBuilder)._getQuery()\n}\n\n// Type-only exports for the query builder\nexport type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>\n\nexport type InitialQueryBuilderConstructor = new () => InitialQueryBuilder\n\nexport type QueryBuilder<TContext extends Context> = Omit<\n BaseQueryBuilder<TContext>,\n `from` | `_getQuery`\n>\n\n// Main query builder class alias with the constructor type modified to hide all\n// but the from method on the initial instance\nexport const Query: InitialQueryBuilderConstructor = BaseQueryBuilder\n\n// Helper type to extract context from a QueryBuilder\nexport type ExtractContext<T> =\n T extends BaseQueryBuilder<infer TContext>\n ? TContext\n : T extends QueryBuilder<infer TContext>\n ? TContext\n : never\n\n// Export the types from types.ts for convenience\nexport type { Context, Source, GetResult } from \"./types.js\"\n"],"names":["CollectionImpl","CollectionRef","QueryRef","refProxy","createRefProxy","toExpression","isRefProxy"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../../src/query/builder/index.ts"],"sourcesContent":["import { CollectionImpl } from \"../../collection.js\"\nimport { CollectionRef, QueryRef } from \"../ir.js\"\nimport { createRefProxy, isRefProxy, toExpression } from \"./ref-proxy.js\"\nimport type { NamespacedRow } from \"../../types.js\"\nimport type {\n Aggregate,\n BasicExpression,\n JoinClause,\n OrderBy,\n OrderByClause,\n OrderByDirection,\n QueryIR,\n} from \"../ir.js\"\nimport type {\n Context,\n GroupByCallback,\n JoinOnCallback,\n MergeContext,\n MergeContextWithJoinType,\n OrderByCallback,\n RefProxyForContext,\n ResultTypeFromSelect,\n SchemaFromSource,\n SelectObject,\n Source,\n WhereCallback,\n WithResult,\n} from \"./types.js\"\n\nexport class BaseQueryBuilder<TContext extends Context = Context> {\n private readonly query: Partial<QueryIR> = {}\n\n constructor(query: Partial<QueryIR> = {}) {\n this.query = { ...query }\n }\n\n /**\n * Creates a CollectionRef or QueryRef from a source object\n * @param source - An object with a single key-value pair\n * @param context - Context string for error messages (e.g., \"from clause\", \"join clause\")\n * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference\n */\n private _createRefForSource<TSource extends Source>(\n source: TSource,\n context: string\n ): [string, CollectionRef | QueryRef] {\n if (Object.keys(source).length !== 1) {\n throw new Error(`Only one source is allowed in the ${context}`)\n }\n\n const alias = Object.keys(source)[0]!\n const sourceValue = source[alias]\n\n let ref: CollectionRef | QueryRef\n\n if (sourceValue instanceof CollectionImpl) {\n ref = new CollectionRef(sourceValue, alias)\n } else if (sourceValue instanceof BaseQueryBuilder) {\n const subQuery = sourceValue._getQuery()\n if (!(subQuery as Partial<QueryIR>).from) {\n throw new Error(\n `A sub query passed to a ${context} must have a from clause itself`\n )\n }\n ref = new QueryRef(subQuery, alias)\n } else {\n throw new Error(`Invalid source`)\n }\n\n return [alias, ref]\n }\n\n /**\n * Specify the source table or subquery for the query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @returns A QueryBuilder with the specified source\n *\n * @example\n * ```ts\n * // Query from a collection\n * query.from({ users: usersCollection })\n *\n * // Query from a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query.from({ activeUsers })\n * ```\n */\n from<TSource extends Source>(\n source: TSource\n ): QueryBuilder<{\n baseSchema: SchemaFromSource<TSource>\n schema: SchemaFromSource<TSource>\n fromSourceName: keyof TSource & string\n hasJoins: false\n }> {\n const [, from] = this._createRefForSource(source, `from clause`)\n\n return new BaseQueryBuilder({\n ...this.query,\n from,\n }) as any\n }\n\n /**\n * Join another table or subquery to the current query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')\n * @returns A QueryBuilder with the joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n *\n * // Inner join with explicit type\n * query\n * .from({ u: usersCollection })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')\n * ```\n *\n * // Join with a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query\n * .from({ activeUsers })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))\n */\n join<\n TSource extends Source,\n TJoinType extends `inner` | `left` | `right` | `full` = `left`,\n >(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >,\n type: TJoinType = `left` as TJoinType\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>\n > {\n const [alias, from] = this._createRefForSource(source, `join clause`)\n\n // Create a temporary context for the callback\n const currentAliases = this._getCurrentAliases()\n const newAliases = [...currentAliases, alias]\n const refProxy = createRefProxy(newAliases) as RefProxyForContext<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n\n // Get the join condition expression\n const onExpression = onCallback(refProxy)\n\n // Extract left and right from the expression\n // For now, we'll assume it's an eq function with two arguments\n let left: BasicExpression\n let right: BasicExpression\n\n if (\n onExpression.type === `func` &&\n onExpression.name === `eq` &&\n onExpression.args.length === 2\n ) {\n left = onExpression.args[0]!\n right = onExpression.args[1]!\n } else {\n throw new Error(`Join condition must be an equality expression`)\n }\n\n const joinClause: JoinClause = {\n from,\n type,\n left,\n right,\n }\n\n const existingJoins = this.query.join || []\n\n return new BaseQueryBuilder({\n ...this.query,\n join: [...existingJoins, joinClause],\n }) as any\n }\n\n /**\n * Perform a LEFT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the left joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .leftJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n leftJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `left`>\n > {\n return this.join(source, onCallback, `left`)\n }\n\n /**\n * Perform a RIGHT JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the right joined table available\n *\n * @example\n * ```ts\n * // Right join users with posts\n * query\n * .from({ users: usersCollection })\n * .rightJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n rightJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `right`>\n > {\n return this.join(source, onCallback, `right`)\n }\n\n /**\n * Perform an INNER JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the inner joined table available\n *\n * @example\n * ```ts\n * // Inner join users with posts\n * query\n * .from({ users: usersCollection })\n * .innerJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n innerJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `inner`>\n > {\n return this.join(source, onCallback, `inner`)\n }\n\n /**\n * Perform a FULL JOIN with another table or subquery\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @returns A QueryBuilder with the full joined table available\n *\n * @example\n * ```ts\n * // Full join users with posts\n * query\n * .from({ users: usersCollection })\n * .fullJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n * ```\n */\n fullJoin<TSource extends Source>(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, `full`>\n > {\n return this.join(source, onCallback, `full`)\n }\n\n /**\n * Filter rows based on a condition\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the where condition applied\n *\n * @example\n * ```ts\n * // Simple condition\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n *\n * // Multiple conditions\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => and(\n * gt(users.age, 18),\n * eq(users.active, true)\n * ))\n *\n * // Multiple where calls are ANDed together\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n * .where(({users}) => eq(users.active, true))\n * ```\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingWhere = this.query.where || []\n\n return new BaseQueryBuilder({\n ...this.query,\n where: [...existingWhere, expression],\n }) as any\n }\n\n /**\n * Filter grouped rows based on aggregate conditions\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the having condition applied\n *\n * @example\n * ```ts\n * // Filter groups by count\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .having(({posts}) => gt(count(posts.id), 5))\n *\n * // Filter by average\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(avg(orders.total), 100))\n *\n * // Multiple having calls are ANDed together\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(count(orders.id), 5))\n * .having(({orders}) => gt(avg(orders.total), 100))\n * ```\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingHaving = this.query.having || []\n\n return new BaseQueryBuilder({\n ...this.query,\n having: [...existingHaving, expression],\n }) as any\n }\n\n /**\n * Select specific columns or computed values from the query\n *\n * @param callback - A function that receives table references and returns an object with selected fields or expressions\n * @returns A QueryBuilder that returns only the selected fields\n *\n * @example\n * ```ts\n * // Select specific columns\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * name: users.name,\n * email: users.email\n * }))\n *\n * // Select with computed values\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * fullName: concat(users.firstName, ' ', users.lastName),\n * ageInMonths: mul(users.age, 12)\n * }))\n *\n * // Select with aggregates (requires GROUP BY)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count(posts.id)\n * }))\n * ```\n */\n select<TSelectObject extends SelectObject>(\n callback: (refs: RefProxyForContext<TContext>) => TSelectObject\n ): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const selectObject = callback(refProxy)\n\n // Check if any tables were spread during the callback\n const spreadSentinels = (refProxy as any).__spreadSentinels as Set<string>\n\n // Convert the select object to use expressions, including spread sentinels\n const select: Record<string, BasicExpression | Aggregate> = {}\n\n // First, add spread sentinels for any tables that were spread\n for (const spreadAlias of spreadSentinels) {\n const sentinelKey = `__SPREAD_SENTINEL__${spreadAlias}`\n select[sentinelKey] = toExpression(spreadAlias) // Use alias as a simple reference\n }\n\n // Then add the explicit select fields\n for (const [key, value] of Object.entries(selectObject)) {\n if (isRefProxy(value)) {\n select[key] = toExpression(value)\n } else if (\n typeof value === `object` &&\n `type` in value &&\n (value.type === `agg` || value.type === `func`)\n ) {\n select[key] = value as BasicExpression | Aggregate\n } else {\n select[key] = toExpression(value)\n }\n }\n\n return new BaseQueryBuilder({\n ...this.query,\n select,\n fnSelect: undefined, // remove the fnSelect clause if it exists\n }) as any\n }\n\n /**\n * Sort the query results by one or more columns\n *\n * @param callback - A function that receives table references and returns the field to sort by\n * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')\n * @returns A QueryBuilder with the ordering applied\n *\n * @example\n * ```ts\n * // Sort by a single column\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.name)\n *\n * // Sort descending\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.createdAt, 'desc')\n *\n * // Multiple sorts (chain orderBy calls)\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.lastName)\n * .orderBy(({users}) => users.firstName)\n * ```\n */\n orderBy(\n callback: OrderByCallback<TContext>,\n direction: OrderByDirection = `asc`\n ): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n // Create the new OrderBy structure with expression and direction\n const orderByClause: OrderByClause = {\n expression: toExpression(result),\n direction,\n }\n\n const existingOrderBy: OrderBy = this.query.orderBy || []\n\n return new BaseQueryBuilder({\n ...this.query,\n orderBy: [...existingOrderBy, orderByClause],\n }) as any\n }\n\n /**\n * Group rows by one or more columns for aggregation\n *\n * @param callback - A function that receives table references and returns the field(s) to group by\n * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)\n *\n * @example\n * ```ts\n * // Group by a single column\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count()\n * }))\n *\n * // Group by multiple columns\n * query\n * .from({ sales: salesCollection })\n * .groupBy(({sales}) => [sales.region, sales.category])\n * .select(({sales, sum}) => ({\n * region: sales.region,\n * category: sales.category,\n * totalSales: sum(sales.amount)\n * }))\n * ```\n */\n groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n const newExpressions = Array.isArray(result)\n ? result.map((r) => toExpression(r))\n : [toExpression(result)]\n\n // Replace existing groupBy expressions instead of extending them\n return new BaseQueryBuilder({\n ...this.query,\n groupBy: newExpressions,\n }) as any\n }\n\n /**\n * Limit the number of rows returned by the query\n * `orderBy` is required for `limit`\n *\n * @param count - Maximum number of rows to return\n * @returns A QueryBuilder with the limit applied\n *\n * @example\n * ```ts\n * // Get top 5 posts by likes\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.likes, 'desc')\n * .limit(5)\n * ```\n */\n limit(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n limit: count,\n }) as any\n }\n\n /**\n * Skip a number of rows before returning results\n * `orderBy` is required for `offset`\n *\n * @param count - Number of rows to skip\n * @returns A QueryBuilder with the offset applied\n *\n * @example\n * ```ts\n * // Get second page of results\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.createdAt, 'desc')\n * .offset(page * pageSize)\n * .limit(pageSize)\n * ```\n */\n offset(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n offset: count,\n }) as any\n }\n\n /**\n * Specify that the query should return distinct rows.\n * Deduplicates rows based on the selected columns.\n * @returns A QueryBuilder with distinct enabled\n *\n * @example\n * ```ts\n * // Get countries our users are from\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => users.country)\n * .distinct()\n * ```\n */\n distinct(): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n distinct: true,\n }) as any\n }\n\n // Helper methods\n private _getCurrentAliases(): Array<string> {\n const aliases: Array<string> = []\n\n // Add the from alias\n if (this.query.from) {\n aliases.push(this.query.from.alias)\n }\n\n // Add join aliases\n if (this.query.join) {\n for (const join of this.query.join) {\n aliases.push(join.from.alias)\n }\n }\n\n return aliases\n }\n\n /**\n * Functional variants of the query builder\n * These are imperative function that are called for ery row.\n * Warning: that these cannot be optimized by the query compiler, and may prevent\n * some type of optimizations being possible.\n * @example\n * ```ts\n * q.fn.select((row) => ({\n * name: row.user.name.toUpperCase(),\n * age: row.user.age + 1,\n * }))\n * ```\n */\n get fn() {\n const builder = this\n return {\n /**\n * Select fields using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns the selected value\n * @returns A QueryBuilder with functional selection applied\n *\n * @example\n * ```ts\n * // Functional select (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.select(row => ({\n * name: row.users.name.toUpperCase(),\n * age: row.users.age + 1,\n * }))\n * ```\n */\n select<TFuncSelectResult>(\n callback: (row: TContext[`schema`]) => TFuncSelectResult\n ): QueryBuilder<WithResult<TContext, TFuncSelectResult>> {\n return new BaseQueryBuilder({\n ...builder.query,\n select: undefined, // remove the select clause if it exists\n fnSelect: callback,\n })\n },\n /**\n * Filter rows using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns a boolean\n * @returns A QueryBuilder with functional filtering applied\n *\n * @example\n * ```ts\n * // Functional where (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.where(row => row.users.name.startsWith('A'))\n * ```\n */\n where(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnWhere: [\n ...(builder.query.fnWhere || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n /**\n * Filter grouped rows using a function that operates on each aggregated row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives an aggregated row and returns a boolean\n * @returns A QueryBuilder with functional having filter applied\n *\n * @example\n * ```ts\n * // Functional having (not optimized)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .fn.having(row => row.count > 5)\n * ```\n */\n having(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnHaving: [\n ...(builder.query.fnHaving || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n }\n }\n\n _getQuery(): QueryIR {\n if (!this.query.from) {\n throw new Error(`Query must have a from clause`)\n }\n return this.query as QueryIR\n }\n}\n\n// Internal function to build a query from a callback\n// used by liveQueryCollectionOptions.query\nexport function buildQuery<TContext extends Context>(\n fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>\n): QueryIR {\n const result = fn(new BaseQueryBuilder())\n return getQueryIR(result)\n}\n\n// Internal function to get the QueryIR from a builder\nexport function getQueryIR(\n builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder\n): QueryIR {\n return (builder as unknown as BaseQueryBuilder)._getQuery()\n}\n\n// Type-only exports for the query builder\nexport type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>\n\nexport type InitialQueryBuilderConstructor = new () => InitialQueryBuilder\n\nexport type QueryBuilder<TContext extends Context> = Omit<\n BaseQueryBuilder<TContext>,\n `from` | `_getQuery`\n>\n\n// Main query builder class alias with the constructor type modified to hide all\n// but the from method on the initial instance\nexport const Query: InitialQueryBuilderConstructor = BaseQueryBuilder\n\n// Helper type to extract context from a QueryBuilder\nexport type ExtractContext<T> =\n T extends BaseQueryBuilder<infer TContext>\n ? TContext\n : T extends QueryBuilder<infer TContext>\n ? TContext\n : never\n\n// Export the types from types.ts for convenience\nexport type { Context, Source, GetResult } from \"./types.js\"\n"],"names":["CollectionImpl","CollectionRef","QueryRef","refProxy","createRefProxy","toExpression","isRefProxy"],"mappings":"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"}