@vielzeug/vault 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -19
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +2 -3
- package/dist/_dev.js.map +1 -1
- package/dist/adapter-core.cjs +1 -1
- package/dist/adapter-core.cjs.map +1 -1
- package/dist/adapter-core.d.ts.map +1 -1
- package/dist/adapter-core.js +2 -2
- package/dist/adapter-core.js.map +1 -1
- package/dist/adapters/indexeddb.cjs +1 -1
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.ts +46 -2
- package/dist/adapters/indexeddb.d.ts.map +1 -1
- package/dist/adapters/indexeddb.js +76 -56
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/sqlite.cjs.map +1 -1
- package/dist/adapters/sqlite.d.ts +2 -2
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/adapters/webstorage.cjs.map +1 -1
- package/dist/adapters/webstorage.d.ts.map +1 -1
- package/dist/adapters/webstorage.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/indexeddb.cjs +1 -1
- package/dist/indexeddb.d.ts +3 -4
- package/dist/indexeddb.d.ts.map +1 -1
- package/dist/indexeddb.js +1 -2
- package/dist/prune.cjs +1 -1
- package/dist/prune.cjs.map +1 -1
- package/dist/prune.d.ts +8 -19
- package/dist/prune.d.ts.map +1 -1
- package/dist/prune.js +9 -13
- package/dist/prune.js.map +1 -1
- package/dist/query.cjs +1 -1
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.ts +5 -11
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +1 -4
- package/dist/query.js.map +1 -1
- package/dist/sqlite.d.ts +1 -2
- package/dist/sqlite.d.ts.map +1 -1
- package/dist/ttl.cjs +1 -1
- package/dist/ttl.cjs.map +1 -1
- package/dist/ttl.d.ts +8 -12
- package/dist/ttl.d.ts.map +1 -1
- package/dist/ttl.js +5 -5
- package/dist/ttl.js.map +1 -1
- package/dist/types.cjs +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +22 -34
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -19
- package/dist/types.js.map +1 -1
- package/dist/vault.cjs +1 -1
- package/dist/vault.cjs.map +1 -1
- package/dist/vault.iife.js +1 -1
- package/dist/vault.iife.js.map +1 -1
- package/dist/vault.js +1 -1
- package/dist/vault.js.map +1 -1
- package/package.json +1 -1
- package/dist/migration.cjs +0 -2
- package/dist/migration.cjs.map +0 -1
- package/dist/migration.d.ts +0 -37
- package/dist/migration.d.ts.map +0 -1
- package/dist/migration.js +0 -25
- package/dist/migration.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, TtlMs, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: TtlMs): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: TtlMs): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webstorage.cjs","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webstorage.d.ts","sourceRoot":"","sources":["../../src/adapters/webstorage.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,eAAe,EAAE,MAAM,WAAW,CAAC;AASxD,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"webstorage.d.ts","sourceRoot":"","sources":["../../src/adapters/webstorage.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,eAAe,EAAE,MAAM,WAAW,CAAC;AASxD,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAmB,UAAU,EAAE,MAAM,UAAU,CAAC;AAK3F,KAAK,iBAAiB,CAAC,CAAC,SAAS,SAAS,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG;IACpE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,KAAK,QAAQ,GAAG,OAAO,CAAC;CAClF,CAAC;AA2VF,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMpG;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAMtG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, TtlMs, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: TtlMs): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: TtlMs): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webstorage.js","names":[],"sources":["../../src/adapters/webstorage.ts"],"sourcesContent":["import { buildAdapterOps, type StorageBackend } from '../adapter-core';\nimport { VaultError, VaultQuotaError } from '../errors';\nimport {\n decodeStorageTableFromKey,\n encodeDbPrefix,\n encodeStorageKey,\n encodeStorageTablePrefix,\n getRecordKey,\n} from '../internal';\nimport { isExpired, parseStored } from '../ttl';\nimport type { AnySchema, BaseAdapterOptions, KeyOf, RecordOf, VaultStore } from '../types';\n\n// Firefox historically threw 'NS_ERROR_DOM_QUOTA_REACHED'; modern browsers use the standard name.\nconst QUOTA_ERROR_NAMES = new Set(['QuotaExceededError', 'NS_ERROR_DOM_QUOTA_REACHED']);\n\ntype WebStorageOptions<S extends AnySchema> = BaseAdapterOptions<S> & {\n name: string;\n /**\n * Called when localStorage/sessionStorage quota is exceeded on a write.\n * Return `'ignore'` to silently drop the write, or `'throw'` (default) to rethrow the error.\n */\n onQuotaExceeded?: (table: keyof S, error: VaultQuotaError) => 'ignore' | 'throw';\n};\n\nfunction createWebStorageAdapter<S extends AnySchema>(\n options: WebStorageOptions<S> & {\n getStorage: () => Storage;\n storageLabel: string;\n },\n): VaultStore<S> {\n const { getStorage, logger, name, onMetrics, onQuotaExceeded, schema, storageLabel, validators } = options;\n\n let resolvedStorage: Storage;\n\n try {\n resolvedStorage = getStorage();\n } catch (cause) {\n throw new VaultError(\n `${storageLabel} is not available in this environment (private browsing or sandboxed iframe?)`,\n {\n cause,\n },\n );\n }\n\n const storage = (): Storage => resolvedStorage;\n\n const prefixMap = new Map(Object.keys(schema).map((table) => [table, encodeStorageTablePrefix(name, table)]));\n const getPrefix = (table: string): string => {\n const cached = prefixMap.get(table);\n\n if (!cached) throw new VaultError(`table \"${table}\" not in schema`);\n\n return cached;\n };\n\n const writeItem = (table: keyof S, storageKey: string, value: unknown): void => {\n try {\n storage().setItem(storageKey, JSON.stringify(value));\n } catch (error) {\n if (error instanceof DOMException && QUOTA_ERROR_NAMES.has(error.name)) {\n const wrappedError = new VaultQuotaError(`${storageLabel} quota exceeded while writing record`, {\n cause: error,\n });\n\n if (onQuotaExceeded?.(table, wrappedError) === 'ignore') return;\n\n throw wrappedError;\n }\n\n throw error;\n }\n };\n\n // Per-instance registry of all storage keys owned by this adapter instance.\n // Populated once at construction; kept current by every mutation.\n const ownedKeys = new Set<string>();\n\n const initOwnedKeys = (): void => {\n const dbPrefix = encodeDbPrefix(name);\n\n for (let i = 0; i < resolvedStorage.length; i++) {\n const key = resolvedStorage.key(i);\n\n if (key?.startsWith(dbPrefix)) ownedKeys.add(key);\n }\n };\n\n initOwnedKeys();\n\n /**\n * Reads a single entry without side effects. Returns the live value,\n * or `undefined` if missing/expired/corrupt.\n *\n * Callers that want to evict stale entries must call `evict(storageKey)` explicitly.\n * This design avoids the `{ cleanup?: boolean }` flag that required callers to remember\n * to pass `{ cleanup: false }` inside loops.\n */\n const parseEntry = <T extends object>(storageKey: string): T | undefined => {\n const raw = storage().getItem(storageKey);\n\n if (!raw) return undefined;\n\n try {\n const stored = parseStored<T>(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) return undefined;\n\n return stored.value;\n } catch {\n return undefined;\n }\n };\n\n /** Removes a stale/expired/corrupt entry from storage and the owned-keys registry. */\n const evict = (storageKey: string): void => {\n storage().removeItem(storageKey);\n ownedKeys.delete(storageKey);\n };\n\n const core: StorageBackend<S> = {\n async clear<K extends keyof S & string>(table: K): Promise<void> {\n const target = storage();\n const prefix = getPrefix(table);\n const toRemove: string[] = [];\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) toRemove.push(key);\n }\n\n for (const key of toRemove) {\n target.removeItem(key);\n ownedKeys.delete(key);\n }\n },\n\n async count<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n let liveCount = 0;\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n } else {\n liveCount += 1;\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return liveCount;\n },\n\n async delete<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n\n return true;\n }\n\n if (ownedKeys.has(storageKey)) evict(storageKey);\n\n return false;\n },\n\n async deleteMany<K extends keyof S & string>(table: K, keys: KeyOf<S, K>[]): Promise<number> {\n let deleted = 0;\n\n for (const key of keys) {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value !== undefined) {\n evict(storageKey);\n deleted += 1;\n } else if (ownedKeys.has(storageKey)) {\n evict(storageKey);\n }\n }\n\n return deleted;\n },\n\n async get<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<RecordOf<S, K> | undefined> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value;\n },\n\n async getAll<K extends keyof S & string>(table: K): Promise<RecordOf<S, K>[]> {\n const records: RecordOf<S, K>[] = [];\n const expiredKeys: string[] = [];\n const prefix = getPrefix(table);\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n records.push(value);\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return records;\n },\n\n async getAllKeys<K extends keyof S & string>(table: K): Promise<KeyOf<S, K>[]> {\n const prefix = getPrefix(table);\n const keys: KeyOf<S, K>[] = [];\n const expiredStorageKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined) {\n expiredStorageKeys.push(storageKey);\n continue;\n }\n\n // Extract the record key from the already-decoded value (avoids a second parse).\n keys.push((value as Record<string, unknown>)[schema[table].key] as KeyOf<S, K>);\n }\n\n for (const storageKey of expiredStorageKeys) {\n evict(storageKey);\n }\n\n return keys;\n },\n\n async getRawCount<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n let count = 0;\n\n for (const key of ownedKeys) {\n if (key.startsWith(prefix)) count += 1;\n }\n\n return count;\n },\n\n async has<K extends keyof S & string>(table: K, key: KeyOf<S, K>): Promise<boolean> {\n const storageKey = encodeStorageKey(name, table, key);\n const value = parseEntry<RecordOf<S, K>>(storageKey);\n\n if (value === undefined && ownedKeys.has(storageKey)) evict(storageKey);\n\n return value !== undefined;\n },\n\n async pruneExpiredInTable<K extends keyof S & string>(table: K): Promise<number> {\n const prefix = getPrefix(table);\n const expiredKeys: string[] = [];\n\n for (const storageKey of ownedKeys) {\n if (!storageKey.startsWith(prefix)) continue;\n\n const raw = storage().getItem(storageKey);\n\n if (raw === null) {\n expiredKeys.push(storageKey);\n continue;\n }\n\n try {\n const stored = parseStored(JSON.parse(raw) as unknown);\n\n if (!stored || isExpired(stored.expiresAt)) {\n expiredKeys.push(storageKey);\n }\n } catch {\n expiredKeys.push(storageKey);\n }\n }\n\n for (const storageKey of expiredKeys) {\n evict(storageKey);\n }\n\n return expiredKeys.length;\n },\n\n async put<K extends keyof S & string>(table: K, value: RecordOf<S, K>, ttl?: number): Promise<void> {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n },\n\n async putAll<K extends keyof S & string>(table: K, values: RecordOf<S, K>[], ttl?: number): Promise<void> {\n const expiresAt = ttl !== undefined ? Date.now() + ttl : undefined;\n\n for (const value of values) {\n const storageKey = encodeStorageKey(name, table, getRecordKey(schema, table, value));\n\n writeItem(table, storageKey, expiresAt === undefined ? { value } : { expiresAt, value });\n ownedKeys.add(storageKey);\n }\n },\n };\n\n return buildAdapterOps(schema, core, {\n logger,\n onCrossTabMessage(notify) {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return undefined;\n }\n\n const listener = (event: StorageEvent) => {\n if (event.storageArea && event.storageArea !== resolvedStorage) return;\n\n if (event.key === null) {\n // storage.clear() from another tab — all keys are gone; purge ownedKeys\n ownedKeys.clear();\n\n for (const table of Object.keys(schema)) {\n notify(table as keyof S & string);\n }\n\n return;\n }\n\n const tableName = decodeStorageTableFromKey(name, event.key);\n\n if (tableName && Object.hasOwn(schema, tableName)) {\n if (event.newValue === null) {\n ownedKeys.delete(event.key);\n } else {\n ownedKeys.add(event.key);\n }\n\n notify(tableName as keyof S & string);\n }\n };\n\n window.addEventListener('storage', listener);\n\n return () => window.removeEventListener('storage', listener);\n },\n onMetrics,\n schema,\n validators,\n });\n}\n\nexport function createLocalStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.localStorage : localStorage),\n storageLabel: 'localStorage',\n });\n}\n\nexport function createSessionStorage<S extends AnySchema>(options: WebStorageOptions<S>): VaultStore<S> {\n return createWebStorageAdapter({\n ...options,\n getStorage: () => (typeof window !== 'undefined' ? window.sessionStorage : sessionStorage),\n storageLabel: 'sessionStorage',\n });\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./ttl.cjs"),n=require("./prune.cjs"),r=require("./types.cjs");exports.VaultDisposedError=e.VaultDisposedError,exports.VaultError=e.VaultError,exports.VaultMigrationError=e.VaultMigrationError,exports.VaultQuotaError=e.VaultQuotaError,exports.VaultScopeError=e.VaultScopeError,exports.isExpired=t.isExpired,exports.scheduleExpiredPrune=n.scheduleExpiredPrune,exports.table=r.table,exports.ttl=t.ttl;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { VaultDisposedError, VaultError, VaultMigrationError, VaultQuotaError, V
|
|
|
2
2
|
export { scheduleExpiredPrune } from './prune';
|
|
3
3
|
export type { QueryBuilder } from './query';
|
|
4
4
|
export { isExpired, ttl } from './ttl';
|
|
5
|
-
export type { AnySchema, BaseAdapterOptions, DebugInfo, DebugStats, IterableVaultStore, KeyOf, MetricsEvent, Observer, RecordOf, RecordValidator, SchemaEntry,
|
|
5
|
+
export type { AnySchema, BaseAdapterOptions, DebugInfo, DebugStats, IterableVaultStore, KeyOf, MetricsEvent, Observer, RecordOf, RecordValidator, SchemaEntry, TableValidators, TransactionalVaultStore, Unsubscribe, VaultKey, VaultLogger, VaultStore, } from './types';
|
|
6
6
|
export { table } from './types';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,WAAW,EACX,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACjH,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,WAAW,EACX,eAAe,EACf,uBAAuB,EACvB,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VaultDisposedError as e, VaultError as t, VaultMigrationError as n, VaultQuotaError as r, VaultScopeError as i } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { isExpired as a, ttl as o } from "./ttl.js";
|
|
3
|
+
import { scheduleExpiredPrune as s } from "./prune.js";
|
|
4
4
|
import { table as c } from "./types.js";
|
|
5
|
-
export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError,
|
|
5
|
+
export { e as VaultDisposedError, t as VaultError, n as VaultMigrationError, r as VaultQuotaError, i as VaultScopeError, a as isExpired, s as scheduleExpiredPrune, c as table, o as ttl };
|
package/dist/indexeddb.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/indexeddb.cjs")
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/indexeddb.cjs");exports.createIndexedDB=e.createIndexedDB,exports.defineMigration=e.defineMigration;
|
package/dist/indexeddb.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
export {
|
|
4
|
-
export type { IndexedDbVaultStore, MigrationContext, MigrationFn, TransactionContext } from './types';
|
|
1
|
+
export type { IndexedDbVaultStore, MigrationContext, MigrationFn, MigrationStep, } from './adapters/indexeddb';
|
|
2
|
+
export { createIndexedDB, defineMigration } from './adapters/indexeddb';
|
|
3
|
+
export type { TransactionContext } from './types';
|
|
5
4
|
//# sourceMappingURL=indexeddb.d.ts.map
|
package/dist/indexeddb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/indexeddb.js
CHANGED
package/dist/prune.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./ttl.cjs");function t(t,n){e.assertPositiveFinite(n.interval,`scheduleExpiredPrune: interval`);let r=setInterval(()=>{t.pruneExpired().catch(e=>{n.onError&&n.onError(e)})},n.interval),i=()=>clearInterval(r);return n.signal?.addEventListener(`abort`,i,{once:!0}),i}exports.scheduleExpiredPrune=t;
|
|
2
2
|
//# sourceMappingURL=prune.cjs.map
|
package/dist/prune.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prune.cjs","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"prune.cjs","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { assertPositiveFinite } from './ttl';\nimport type { AnySchema, VaultStore } from './types';\n\n/**\n * Schedules periodic `pruneExpired()` calls. Returns a `stop` function.\n *\n * Pass `signal: store.disposalSignal` to auto-cancel when the store is torn down.\n * Pass `onError` to handle non-disposal failures explicitly; without it, errors\n * are logged via the dev channel and the schedule continues.\n *\n * ```ts\n * const stop = scheduleExpiredPrune(db, {\n * interval: ttl.hours(1),\n * signal: db.disposalSignal,\n * });\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<VaultStore<S>, 'pruneExpired'>,\n options: {\n interval: number;\n onError?: (err: unknown) => void;\n signal?: AbortSignal;\n },\n): () => void {\n assertPositiveFinite(options.interval, 'scheduleExpiredPrune: interval');\n\n const id = setInterval(() => {\n void adapter.pruneExpired().catch((err) => {\n if (options.onError) options.onError(err);\n else logError('scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this.', err);\n });\n }, options.interval);\n\n const stop = (): void => clearInterval(id);\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n}\n"],"mappings":"mDAkBA,SAAgB,EACd,EACA,EAKY,CACZ,EAAA,qBAAqB,EAAQ,SAAU,gCAAgC,EAEvE,IAAM,EAAK,gBAAkB,CAC3B,EAAa,aAAa,CAAC,CAAC,MAAO,GAAQ,CACrC,EAAQ,SAAS,EAAQ,QAAQ,CAAG,CAE1C,CAAC,CACH,EAAG,EAAQ,QAAQ,EAEb,MAAmB,cAAc,CAAE,EAIzC,OAFA,EAAQ,QAAQ,iBAAiB,QAAS,EAAM,CAAE,KAAM,EAAK,CAAC,EAEvD,CACT"}
|
package/dist/prune.d.ts
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
import type { AnySchema, VaultStore } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* Schedules periodic
|
|
4
|
-
* Returns a `stop` function to cancel the schedule.
|
|
3
|
+
* Schedules periodic `pruneExpired()` calls. Returns a `stop` function.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Pass `signal: store.disposalSignal` to auto-cancel when the store is torn down.
|
|
6
|
+
* Pass `onError` to handle non-disposal failures explicitly; without it, errors
|
|
7
|
+
* are logged via the dev channel and the schedule continues.
|
|
8
8
|
*
|
|
9
9
|
* ```ts
|
|
10
|
-
* const
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* const stop = scheduleExpiredPrune(db, {
|
|
11
|
+
* interval: ttl.hours(1),
|
|
12
|
+
* signal: db.disposalSignal,
|
|
13
|
+
* });
|
|
13
14
|
* ```
|
|
14
15
|
*/
|
|
15
16
|
export declare function scheduleExpiredPrune<S extends AnySchema>(adapter: Pick<VaultStore<S>, 'pruneExpired'>, options: {
|
|
16
17
|
interval: number;
|
|
17
|
-
/**
|
|
18
|
-
* Called when `pruneExpired()` throws an error that is NOT a `VaultDisposedError`.
|
|
19
|
-
* `VaultDisposedError` always stops the schedule automatically.
|
|
20
|
-
* Without this callback, non-disposal errors are silently swallowed.
|
|
21
|
-
*/
|
|
22
18
|
onError?: (err: unknown) => void;
|
|
23
|
-
/**
|
|
24
|
-
* When aborted, stops the schedule. Useful for tying the schedule lifetime
|
|
25
|
-
* to an adapter's `disposalSignal`:
|
|
26
|
-
* ```ts
|
|
27
|
-
* scheduleExpiredPrune(db, { interval: ttl.hours(1), signal: db.disposalSignal });
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
19
|
signal?: AbortSignal;
|
|
31
20
|
}): () => void;
|
|
32
21
|
//# sourceMappingURL=prune.d.ts.map
|
package/dist/prune.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD
|
|
1
|
+
{"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,SAAS,EACtD,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,EAC5C,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GACA,MAAM,IAAI,CAeZ"}
|
package/dist/prune.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
import { VaultDisposedError as e, VaultError as t } from "./errors.js";
|
|
2
1
|
import "./_dev.js";
|
|
2
|
+
import { assertPositiveFinite as e } from "./ttl.js";
|
|
3
3
|
//#region src/prune.ts
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
r.signal?.addEventListener("abort", a, { once: !0 });
|
|
10
|
-
let o = setInterval(() => {
|
|
11
|
-
i && n.pruneExpired().catch((t) => {
|
|
12
|
-
t instanceof e ? (i = !1, clearInterval(o)) : r.onError ? r.onError(t) : `${String(t)}`;
|
|
4
|
+
function t(t, n) {
|
|
5
|
+
e(n.interval, "scheduleExpiredPrune: interval");
|
|
6
|
+
let r = setInterval(() => {
|
|
7
|
+
t.pruneExpired().catch((e) => {
|
|
8
|
+
n.onError && n.onError(e);
|
|
13
9
|
});
|
|
14
|
-
},
|
|
15
|
-
return
|
|
10
|
+
}, n.interval), i = () => clearInterval(r);
|
|
11
|
+
return n.signal?.addEventListener("abort", i, { once: !0 }), i;
|
|
16
12
|
}
|
|
17
13
|
//#endregion
|
|
18
|
-
export {
|
|
14
|
+
export { t as scheduleExpiredPrune };
|
|
19
15
|
|
|
20
16
|
//# sourceMappingURL=prune.js.map
|
package/dist/prune.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prune.js","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"prune.js","names":[],"sources":["../src/prune.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { assertPositiveFinite } from './ttl';\nimport type { AnySchema, VaultStore } from './types';\n\n/**\n * Schedules periodic `pruneExpired()` calls. Returns a `stop` function.\n *\n * Pass `signal: store.disposalSignal` to auto-cancel when the store is torn down.\n * Pass `onError` to handle non-disposal failures explicitly; without it, errors\n * are logged via the dev channel and the schedule continues.\n *\n * ```ts\n * const stop = scheduleExpiredPrune(db, {\n * interval: ttl.hours(1),\n * signal: db.disposalSignal,\n * });\n * ```\n */\nexport function scheduleExpiredPrune<S extends AnySchema>(\n adapter: Pick<VaultStore<S>, 'pruneExpired'>,\n options: {\n interval: number;\n onError?: (err: unknown) => void;\n signal?: AbortSignal;\n },\n): () => void {\n assertPositiveFinite(options.interval, 'scheduleExpiredPrune: interval');\n\n const id = setInterval(() => {\n void adapter.pruneExpired().catch((err) => {\n if (options.onError) options.onError(err);\n else logError('scheduleExpiredPrune: pruneExpired() threw — pass onError to handle this.', err);\n });\n }, options.interval);\n\n const stop = (): void => clearInterval(id);\n\n options.signal?.addEventListener('abort', stop, { once: true });\n\n return stop;\n}\n"],"mappings":";;;AAkBA,SAAgB,EACd,GACA,GAKY;CACZ,EAAqB,EAAQ,UAAU,gCAAgC;CAEvE,IAAM,IAAK,kBAAkB;EAC3B,EAAa,aAAa,CAAC,CAAC,OAAO,MAAQ;GACzC,AAAI,EAAQ,WAAS,EAAQ,QAAQ,CAAG;EAE1C,CAAC;CACH,GAAG,EAAQ,QAAQ,GAEb,UAAmB,cAAc,CAAE;CAIzC,OAFA,EAAQ,QAAQ,iBAAiB,SAAS,GAAM,EAAE,MAAM,GAAK,CAAC,GAEvD;AACT"}
|
package/dist/query.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");async function t(e,t){let n=await e.source();for(let e of t)n=e.apply(n);return n}function n(t,n){if(!Number.isInteger(t)||t<0)throw new e.VaultError(`${n} must be a non-negative integer`);return t}function r(e,t){return{deleteMany:e.deleteMany,source:t}}function i(a,o=[]){let s=e=>i(a,[...o,e]);return{between(e,t,n){let c=String(e);if(o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({lower:t,type:`between`,upper:n})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{lower:t,type:`between`,upper:n})),o)}return s({apply:r=>r.filter(r=>{let i=r[e];return i>=t&&i<=n})})},count(){return t(a,o).then(e=>e.length)},async delete(){if(!a.deleteMany)throw new e.VaultError(`query.delete is not available for this adapter context`);let n=await t(a,o);return a.deleteMany(n)},equals(e,t){let n=String(e);if(o.length===0){if(a.getRange&&a.keyField===n)return i(r(a,()=>a.getRange({type:`eq`,value:t})));if(a.getIndexRange&&a.indexedFields?.has(n))return i(r(a,()=>a.getIndexRange(n,{type:`eq`,value:t})))}return i(a,[...o,{apply:n=>n.filter(n=>n[e]===t)}])},async exists(){return o.length===0?a.source().then(e=>e.length>0):t(a,o).then(e=>e.length>0)},filter(e){return s({apply:t=>t.filter(e)})},first(){return o.length===0?a.source().then(e=>e[0]):t(a,o).then(e=>e[0])},limit(e){let t=n(e,`query.limit`);return s({apply:e=>e.slice(0,t),isNonFilter:!0})},offset(e){let t=n(e,`query.offset`);return s({apply:e=>e.slice(t),isNonFilter:!0})},orderBy(e,t=`asc`){return s({apply:n=>{let r=t===`asc`?1:-1;return[...n].sort((t,n)=>{let i=t[e],a=n[e];return i===a?0:i>a?r:-r})},isNonFilter:!0})},startsWith(e,t,{ignoreCase:n=!1}={}){let c=String(e);if(!n&&t.length>0&&o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({prefix:t,type:`starts`})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{prefix:t,type:`starts`})),o)}let l=n?t.toLowerCase():t;return s({apply:t=>t.filter(t=>{let r=t[e];return typeof r==`string`&&(n?r.toLowerCase():r).startsWith(l)})})},toArray(){return t(a,o)}
|
|
1
|
+
const e=require("./errors.cjs");async function t(e,t){let n=await e.source();for(let e of t)n=e.apply(n);return n}function n(t,n){if(!Number.isInteger(t)||t<0)throw new e.VaultError(`${n} must be a non-negative integer`);return t}function r(e,t){return{deleteMany:e.deleteMany,source:t}}function i(a,o=[]){let s=e=>i(a,[...o,e]);return{between(e,t,n){let c=String(e);if(o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({lower:t,type:`between`,upper:n})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{lower:t,type:`between`,upper:n})),o)}return s({apply:r=>r.filter(r=>{let i=r[e];return i>=t&&i<=n})})},count(){return t(a,o.filter(e=>!e.isNonFilter)).then(e=>e.length)},async delete(){if(!a.deleteMany)throw new e.VaultError(`query.delete is not available for this adapter context`);let n=await t(a,o);return a.deleteMany(n)},equals(e,t){let n=String(e);if(o.length===0){if(a.getRange&&a.keyField===n)return i(r(a,()=>a.getRange({type:`eq`,value:t})));if(a.getIndexRange&&a.indexedFields?.has(n))return i(r(a,()=>a.getIndexRange(n,{type:`eq`,value:t})))}return i(a,[...o,{apply:n=>n.filter(n=>n[e]===t)}])},async exists(){return o.length===0?a.source().then(e=>e.length>0):t(a,o).then(e=>e.length>0)},filter(e){return s({apply:t=>t.filter(e)})},first(){return o.length===0?a.source().then(e=>e[0]):t(a,o).then(e=>e[0])},limit(e){let t=n(e,`query.limit`);return s({apply:e=>e.slice(0,t),isNonFilter:!0})},offset(e){let t=n(e,`query.offset`);return s({apply:e=>e.slice(t),isNonFilter:!0})},orderBy(e,t=`asc`){return s({apply:n=>{let r=t===`asc`?1:-1;return[...n].sort((t,n)=>{let i=t[e],a=n[e];return i===a?0:i>a?r:-r})},isNonFilter:!0})},startsWith(e,t,{ignoreCase:n=!1}={}){let c=String(e);if(!n&&t.length>0&&o.length===0){if(a.getRange&&a.keyField===c)return i(r(a,()=>a.getRange({prefix:t,type:`starts`})),o);if(a.getIndexRange&&a.indexedFields?.has(c))return i(r(a,()=>a.getIndexRange(c,{prefix:t,type:`starts`})),o)}let l=n?t.toLowerCase():t;return s({apply:t=>t.filter(t=>{let r=t[e];return typeof r==`string`&&(n?r.toLowerCase():r).startsWith(l)})})},toArray(){return t(a,o)}}}exports.createQueryBuilder=i;
|
|
2
2
|
//# sourceMappingURL=query.cjs.map
|
package/dist/query.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.cjs","names":[],"sources":["../src/query.ts"],"sourcesContent":["import { VaultError } from './errors';\n\ntype Predicate<T> = (value: T, index: number, array: T[]) => boolean;\n/**\n * `isNonFilter`: when true this op is excluded from `totalCount()` — it does not restrict\n * *which* records match (limit, offset, orderBy), only how results are presented.\n */\ntype QueryOp<T> = { apply: (data: T[]) => T[]; isNonFilter?: boolean };\ntype ComparableFieldKeys<T extends object> = {\n [K in keyof T]-?: Extract<NonNullable<T[K]>, number | string> extends never ? never : K;\n}[keyof T];\n\n/**\n * A primary-key range hint that can be pushed down to native storage backends (e.g. IndexedDB).\n * When `QueryContext.getRange` and `keyField` are present, a matching first filter op replaces\n * the full-table `source()` scan with a targeted range fetch.\n */\nexport type NativeRange =\n | { type: 'eq'; value: unknown }\n | { lower: unknown; type: 'between'; upper: unknown }\n | { prefix: string; type: 'starts' };\n\nexport type QueryContext<T extends object> = {\n deleteMany?: (records: T[]) => Promise<number>;\n /**\n * When present alongside `indexedFields`, replaces `source()` for secondary-index filter ops.\n * Only activated when the first op is `equals`, `between`, or `startsWith` on a field that\n * has an index registered in the schema. IndexedDB uses `IDBIndex.getAll(range)` under the hood.\n */\n getIndexRange?: (field: string, range: NativeRange) => Promise<T[]>;\n /**\n * When present alongside `keyField`, replaces `source()` for primary-key filter ops.\n * Only activated when the first op is an `equals`, `between`, or case-sensitive `startsWith`\n * on `keyField`. All remaining ops still run in-memory against the range result.\n */\n getRange?: (range: NativeRange) => Promise<T[]>;\n /** Fields that have secondary indexes — used to detect when a filter op can use `getIndexRange`. */\n indexedFields?: ReadonlySet<string>;\n /** Primary key field name — used to detect when a filter op can be pushed to `getRange`. */\n keyField?: string;\n source: () => Promise<T[]>;\n};\n\n/* -------------------- Public interfaces -------------------- */\n\n/**\n * Shared query methods. `T` is the base record type; `N` is the progressively-narrowed type\n * accumulated by `equals()` calls.\n */\ntype ChainedQuery<T extends object, N extends T, Self extends ChainedQuery<T, N, Self>> = {\n /**\n * Filter records where `field` is between `lower` and `upper` (inclusive).\n * Preserves any type narrowing already accumulated by prior `equals()` calls.\n */\n between<K extends ComparableFieldKeys<T>>(\n field: K,\n lower: Extract<NonNullable<T[K]>, number | string>,\n upper: Extract<NonNullable<T[K]>, number | string>,\n ): QueryBuilder<T, N>;\n /**\n * Returns the number of records matching all applied operations, including `limit` and `offset`.\n * To get the full filtered set size regardless of pagination (e.g. for \"page X of N\" UIs),\n * use `totalCount()` instead.\n */\n count(): Promise<number>;\n /**\n * Filter records where `field` exactly equals `value`.\n * The return type is narrowed to `QueryBuilder<T & Record<K, V>>` so that subsequent\n * `.toArray()`, `.first()`, etc. reflect the equality constraint in their result type.\n *\n * ```ts\n * // result: (User & { role: 'admin' })[]\n * const admins = await db.query('users').equals('role', 'admin').toArray();\n * ```\n */\n equals<K extends keyof T & string, V extends T[K]>(field: K, value: V): QueryBuilder<T & Record<K, V>>;\n /**\n * Returns `true` if at least one record matches all applied filter operations.\n * Equivalent to `(await query.first()) !== undefined` but makes the intent explicit.\n * Presentation-only ops (`limit`, `offset`, `orderBy`) are respected before checking.\n */\n exists(): Promise<boolean>;\n filter(fn: Predicate<N>): Self;\n first(): Promise<N | undefined>;\n limit(n: number): Self;\n offset(n: number): Self;\n orderBy<K extends keyof T>(field: K, direction?: 'asc' | 'desc'): Self;\n /**\n * Filter records where `field` starts with `prefix`.\n * Preserves any type narrowing already accumulated by prior `equals()` calls.\n *\n * **Index push-down** is only active when `ignoreCase` is `false` (default) and this\n * is the first operation in the chain. With `ignoreCase: true`, a full-table scan is\n * always performed regardless of whether the field has an index.\n */\n startsWith<K extends keyof T>(field: K, prefix: string, options?: { ignoreCase?: boolean }): QueryBuilder<T, N>;\n toArray(): Promise<N[]>;\n /**\n * Returns the number of records matching the applied filter predicates.\n * Presentation-only ops (`limit`, `offset`, `orderBy`) are intentionally ignored — this\n * always counts the full filtered set, making paginated total-count queries possible\n * without a second query.\n */\n totalCount(): Promise<number>;\n};\n\n/** Extends the shared query API with `delete()`. Available on stores and IndexedDB transaction callbacks. */\nexport interface QueryBuilder<T extends object, N extends T = T> extends ChainedQuery<T, N, QueryBuilder<T, N>> {\n delete(): Promise<number>;\n}\n\n/* -------------------- Helpers -------------------- */\n\nasync function applyOps<T extends object>(ctx: QueryContext<T>, ops: readonly QueryOp<T>[]): Promise<T[]> {\n let data = await ctx.source();\n\n for (const op of ops) {\n data = op.apply(data);\n }\n\n return data;\n}\n\nfunction assertNonNegativeInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 0) {\n throw new VaultError(`${name} must be a non-negative integer`);\n }\n\n return value;\n}\n\n/* -------------------- Push-down helpers -------------------- */\n\n/** Build a new QueryContext with source replaced by a range/index fetch. Drops range hints since push-down is done. */\nfunction pushDownContext<T extends object>(ctx: QueryContext<T>, newSource: () => Promise<T[]>): QueryContext<T> {\n return { deleteMany: ctx.deleteMany, source: newSource };\n}\n\n/* -------------------- Factory -------------------- */\n\nexport function createQueryBuilder<T extends object, N extends T = T>(\n ctx: QueryContext<T>,\n ops: readonly QueryOp<T>[] = [],\n): QueryBuilder<T, N> {\n const append = (op: QueryOp<T>): QueryBuilder<T, N> => createQueryBuilder<T, N>(ctx, [...ops, op]);\n\n return {\n between(field, lower, upper) {\n const fieldStr = String(field);\n\n if (ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getRange!({ lower, type: 'between', upper })),\n ops,\n );\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getIndexRange!(fieldStr, { lower, type: 'between', upper })),\n ops,\n );\n }\n }\n\n return append({\n apply: (data) =>\n data.filter((r) => {\n const v = r[field] as number | string;\n\n return v >= lower && v <= upper;\n }),\n }) as unknown as QueryBuilder<T, N>;\n },\n count(): Promise<number> {\n return applyOps(ctx, ops).then((r) => r.length);\n },\n async delete(): Promise<number> {\n if (!ctx.deleteMany) {\n throw new VaultError('query.delete is not available for this adapter context');\n }\n\n const records = await applyOps(ctx, ops);\n\n return ctx.deleteMany(records);\n },\n equals<K extends keyof T & string, V extends T[K]>(field: K, value: V): QueryBuilder<T & Record<K, V>> {\n const fieldStr = String(field);\n\n if (ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder<T & Record<K, V>>(\n pushDownContext(\n ctx as unknown as QueryContext<T & Record<K, V>>,\n () => ctx.getRange!({ type: 'eq', value }) as Promise<(T & Record<K, V>)[]>,\n ),\n ) as QueryBuilder<T & Record<K, V>>;\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder<T & Record<K, V>>(\n pushDownContext(\n ctx as unknown as QueryContext<T & Record<K, V>>,\n () => ctx.getIndexRange!(fieldStr, { type: 'eq', value }) as Promise<(T & Record<K, V>)[]>,\n ),\n ) as QueryBuilder<T & Record<K, V>>;\n }\n }\n\n return createQueryBuilder<T & Record<K, V>>(ctx as unknown as QueryContext<T & Record<K, V>>, [\n ...(ops as unknown as QueryOp<T & Record<K, V>>[]),\n { apply: (data) => data.filter((r) => r[field] === value) as (T & Record<K, V>)[] },\n ]);\n },\n async exists(): Promise<boolean> {\n if (ops.length === 0) return ctx.source().then((r) => r.length > 0);\n\n return applyOps(ctx, ops).then((r) => r.length > 0);\n },\n filter(fn) {\n return append({ apply: (data) => data.filter(fn as Predicate<T>) });\n },\n first(): Promise<N | undefined> {\n if (ops.length === 0) return ctx.source().then((r) => r[0] as N | undefined);\n\n return applyOps(ctx, ops).then((r) => r[0] as N | undefined);\n },\n limit(n) {\n const safeN = assertNonNegativeInteger(n, 'query.limit');\n\n return append({ apply: (data) => data.slice(0, safeN), isNonFilter: true });\n },\n offset(n) {\n const safeN = assertNonNegativeInteger(n, 'query.offset');\n\n return append({ apply: (data) => data.slice(safeN), isNonFilter: true });\n },\n orderBy(field, direction = 'asc') {\n return append({\n apply: (data) => {\n const sign = direction === 'asc' ? 1 : -1;\n\n return [...data].sort((a, b) => {\n const av = a[field] as number | string;\n const bv = b[field] as number | string;\n\n if (av === bv) return 0;\n\n return av > bv ? sign : -sign;\n });\n },\n isNonFilter: true,\n });\n },\n startsWith(field, prefix, { ignoreCase = false } = {}) {\n const fieldStr = String(field);\n\n if (!ignoreCase && prefix.length > 0 && ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getRange!({ prefix, type: 'starts' })),\n ops,\n );\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getIndexRange!(fieldStr, { prefix, type: 'starts' })),\n ops,\n );\n }\n }\n\n const needle = ignoreCase ? prefix.toLowerCase() : prefix;\n\n return append({\n apply: (data) =>\n data.filter((r) => {\n const v = r[field];\n\n if (typeof v !== 'string') return false;\n\n const haystack = ignoreCase ? v.toLowerCase() : v;\n\n return haystack.startsWith(needle);\n }),\n }) as unknown as QueryBuilder<T, N>;\n },\n toArray(): Promise<N[]> {\n return applyOps(ctx, ops) as Promise<N[]>;\n },\n totalCount(): Promise<number> {\n const filterOps = ops.filter((op) => !op.isNonFilter);\n\n return applyOps(ctx, filterOps).then((r) => r.length);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query.cjs","names":[],"sources":["../src/query.ts"],"sourcesContent":["import { VaultError } from './errors';\n\ntype Predicate<T> = (value: T, index: number, array: T[]) => boolean;\n/**\n * `isNonFilter`: when true this op is excluded from `count()` — it does not restrict\n * *which* records match (limit, offset, orderBy), only how results are presented.\n */\ntype QueryOp<T> = { apply: (data: T[]) => T[]; isNonFilter?: boolean };\ntype ComparableFieldKeys<T extends object> = {\n [K in keyof T]-?: Extract<NonNullable<T[K]>, number | string> extends never ? never : K;\n}[keyof T];\n\n/**\n * A primary-key range hint that can be pushed down to native storage backends (e.g. IndexedDB).\n * When `QueryContext.getRange` and `keyField` are present, a matching first filter op replaces\n * the full-table `source()` scan with a targeted range fetch.\n */\nexport type NativeRange =\n | { type: 'eq'; value: unknown }\n | { lower: unknown; type: 'between'; upper: unknown }\n | { prefix: string; type: 'starts' };\n\nexport type QueryContext<T extends object> = {\n deleteMany?: (records: T[]) => Promise<number>;\n /**\n * When present alongside `indexedFields`, replaces `source()` for secondary-index filter ops.\n * Only activated when the first op is `equals`, `between`, or `startsWith` on a field that\n * has an index registered in the schema. IndexedDB uses `IDBIndex.getAll(range)` under the hood.\n */\n getIndexRange?: (field: string, range: NativeRange) => Promise<T[]>;\n /**\n * When present alongside `keyField`, replaces `source()` for primary-key filter ops.\n * Only activated when the first op is an `equals`, `between`, or case-sensitive `startsWith`\n * on `keyField`. All remaining ops still run in-memory against the range result.\n */\n getRange?: (range: NativeRange) => Promise<T[]>;\n /** Fields that have secondary indexes — used to detect when a filter op can use `getIndexRange`. */\n indexedFields?: ReadonlySet<string>;\n /** Primary key field name — used to detect when a filter op can be pushed to `getRange`. */\n keyField?: string;\n source: () => Promise<T[]>;\n};\n\n/* -------------------- Public interfaces -------------------- */\n\n/**\n * Shared query methods. `T` is the base record type; `N` is the progressively-narrowed type\n * accumulated by `equals()` calls.\n */\ntype ChainedQuery<T extends object, N extends T, Self extends ChainedQuery<T, N, Self>> = {\n /**\n * Filter records where `field` is between `lower` and `upper` (inclusive).\n * Preserves any type narrowing already accumulated by prior `equals()` calls.\n */\n between<K extends ComparableFieldKeys<T>>(\n field: K,\n lower: Extract<NonNullable<T[K]>, number | string>,\n upper: Extract<NonNullable<T[K]>, number | string>,\n ): QueryBuilder<T, N>;\n /**\n * Returns the number of records matching the applied filter predicates.\n * Presentation-only ops (`limit`, `offset`, `orderBy`) are intentionally ignored — this\n * always counts the full filtered set, making paginated total-count queries possible\n * without a second query.\n */\n count(): Promise<number>;\n /**\n * Filter records where `field` exactly equals `value`.\n * The return type is narrowed to `QueryBuilder<T & Record<K, V>>` so that subsequent\n * `.toArray()`, `.first()`, etc. reflect the equality constraint in their result type.\n *\n * ```ts\n * // result: (User & { role: 'admin' })[]\n * const admins = await db.query('users').equals('role', 'admin').toArray();\n * ```\n */\n equals<K extends keyof T & string, V extends T[K]>(field: K, value: V): QueryBuilder<T & Record<K, V>>;\n /**\n * Returns `true` if at least one record matches all applied filter operations.\n * Equivalent to `(await query.first()) !== undefined` but makes the intent explicit.\n * Presentation-only ops (`limit`, `offset`, `orderBy`) are respected before checking.\n */\n exists(): Promise<boolean>;\n filter(fn: Predicate<N>): Self;\n first(): Promise<N | undefined>;\n limit(n: number): Self;\n offset(n: number): Self;\n orderBy<K extends keyof T>(field: K, direction?: 'asc' | 'desc'): Self;\n /**\n * Filter records where `field` starts with `prefix`.\n * Preserves any type narrowing already accumulated by prior `equals()` calls.\n *\n * **Index push-down** is only active when `ignoreCase` is `false` (default) and this\n * is the first operation in the chain. With `ignoreCase: true`, a full-table scan is\n * always performed regardless of whether the field has an index.\n */\n startsWith<K extends keyof T>(field: K, prefix: string, options?: { ignoreCase?: boolean }): QueryBuilder<T, N>;\n toArray(): Promise<N[]>;\n};\n\n/** Extends the shared query API with `delete()`. Available on stores and IndexedDB transaction callbacks. */\nexport interface QueryBuilder<T extends object, N extends T = T> extends ChainedQuery<T, N, QueryBuilder<T, N>> {\n delete(): Promise<number>;\n}\n\n/* -------------------- Helpers -------------------- */\n\nasync function applyOps<T extends object>(ctx: QueryContext<T>, ops: readonly QueryOp<T>[]): Promise<T[]> {\n let data = await ctx.source();\n\n for (const op of ops) {\n data = op.apply(data);\n }\n\n return data;\n}\n\nfunction assertNonNegativeInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 0) {\n throw new VaultError(`${name} must be a non-negative integer`);\n }\n\n return value;\n}\n\n/* -------------------- Push-down helpers -------------------- */\n\n/** Build a new QueryContext with source replaced by a range/index fetch. Drops range hints since push-down is done. */\nfunction pushDownContext<T extends object>(ctx: QueryContext<T>, newSource: () => Promise<T[]>): QueryContext<T> {\n return { deleteMany: ctx.deleteMany, source: newSource };\n}\n\n/* -------------------- Factory -------------------- */\n\nexport function createQueryBuilder<T extends object, N extends T = T>(\n ctx: QueryContext<T>,\n ops: readonly QueryOp<T>[] = [],\n): QueryBuilder<T, N> {\n const append = (op: QueryOp<T>): QueryBuilder<T, N> => createQueryBuilder<T, N>(ctx, [...ops, op]);\n\n return {\n between(field, lower, upper) {\n const fieldStr = String(field);\n\n if (ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getRange!({ lower, type: 'between', upper })),\n ops,\n );\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getIndexRange!(fieldStr, { lower, type: 'between', upper })),\n ops,\n );\n }\n }\n\n return append({\n apply: (data) =>\n data.filter((r) => {\n const v = r[field] as number | string;\n\n return v >= lower && v <= upper;\n }),\n }) as unknown as QueryBuilder<T, N>;\n },\n count(): Promise<number> {\n const filterOps = ops.filter((op) => !op.isNonFilter);\n\n return applyOps(ctx, filterOps).then((r) => r.length);\n },\n async delete(): Promise<number> {\n if (!ctx.deleteMany) {\n throw new VaultError('query.delete is not available for this adapter context');\n }\n\n const records = await applyOps(ctx, ops);\n\n return ctx.deleteMany(records);\n },\n equals<K extends keyof T & string, V extends T[K]>(field: K, value: V): QueryBuilder<T & Record<K, V>> {\n const fieldStr = String(field);\n\n if (ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder<T & Record<K, V>>(\n pushDownContext(\n ctx as unknown as QueryContext<T & Record<K, V>>,\n () => ctx.getRange!({ type: 'eq', value }) as Promise<(T & Record<K, V>)[]>,\n ),\n ) as QueryBuilder<T & Record<K, V>>;\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder<T & Record<K, V>>(\n pushDownContext(\n ctx as unknown as QueryContext<T & Record<K, V>>,\n () => ctx.getIndexRange!(fieldStr, { type: 'eq', value }) as Promise<(T & Record<K, V>)[]>,\n ),\n ) as QueryBuilder<T & Record<K, V>>;\n }\n }\n\n return createQueryBuilder<T & Record<K, V>>(ctx as unknown as QueryContext<T & Record<K, V>>, [\n ...(ops as unknown as QueryOp<T & Record<K, V>>[]),\n { apply: (data) => data.filter((r) => r[field] === value) as (T & Record<K, V>)[] },\n ]);\n },\n async exists(): Promise<boolean> {\n if (ops.length === 0) return ctx.source().then((r) => r.length > 0);\n\n return applyOps(ctx, ops).then((r) => r.length > 0);\n },\n filter(fn) {\n return append({ apply: (data) => data.filter(fn as Predicate<T>) });\n },\n first(): Promise<N | undefined> {\n if (ops.length === 0) return ctx.source().then((r) => r[0] as N | undefined);\n\n return applyOps(ctx, ops).then((r) => r[0] as N | undefined);\n },\n limit(n) {\n const safeN = assertNonNegativeInteger(n, 'query.limit');\n\n return append({ apply: (data) => data.slice(0, safeN), isNonFilter: true });\n },\n offset(n) {\n const safeN = assertNonNegativeInteger(n, 'query.offset');\n\n return append({ apply: (data) => data.slice(safeN), isNonFilter: true });\n },\n orderBy(field, direction = 'asc') {\n return append({\n apply: (data) => {\n const sign = direction === 'asc' ? 1 : -1;\n\n return [...data].sort((a, b) => {\n const av = a[field] as number | string;\n const bv = b[field] as number | string;\n\n if (av === bv) return 0;\n\n return av > bv ? sign : -sign;\n });\n },\n isNonFilter: true,\n });\n },\n startsWith(field, prefix, { ignoreCase = false } = {}) {\n const fieldStr = String(field);\n\n if (!ignoreCase && prefix.length > 0 && ops.length === 0) {\n if (ctx.getRange && ctx.keyField === fieldStr) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getRange!({ prefix, type: 'starts' })),\n ops,\n );\n }\n\n if (ctx.getIndexRange && ctx.indexedFields?.has(fieldStr)) {\n return createQueryBuilder(\n pushDownContext(ctx, () => ctx.getIndexRange!(fieldStr, { prefix, type: 'starts' })),\n ops,\n );\n }\n }\n\n const needle = ignoreCase ? prefix.toLowerCase() : prefix;\n\n return append({\n apply: (data) =>\n data.filter((r) => {\n const v = r[field];\n\n if (typeof v !== 'string') return false;\n\n const haystack = ignoreCase ? v.toLowerCase() : v;\n\n return haystack.startsWith(needle);\n }),\n }) as unknown as QueryBuilder<T, N>;\n },\n toArray(): Promise<N[]> {\n return applyOps(ctx, ops) as Promise<N[]>;\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type Predicate<T> = (value: T, index: number, array: T[]) => boolean;
|
|
2
2
|
/**
|
|
3
|
-
* `isNonFilter`: when true this op is excluded from `
|
|
3
|
+
* `isNonFilter`: when true this op is excluded from `count()` — it does not restrict
|
|
4
4
|
* *which* records match (limit, offset, orderBy), only how results are presented.
|
|
5
5
|
*/
|
|
6
6
|
type QueryOp<T> = {
|
|
@@ -57,9 +57,10 @@ type ChainedQuery<T extends object, N extends T, Self extends ChainedQuery<T, N,
|
|
|
57
57
|
*/
|
|
58
58
|
between<K extends ComparableFieldKeys<T>>(field: K, lower: Extract<NonNullable<T[K]>, number | string>, upper: Extract<NonNullable<T[K]>, number | string>): QueryBuilder<T, N>;
|
|
59
59
|
/**
|
|
60
|
-
* Returns the number of records matching
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* Returns the number of records matching the applied filter predicates.
|
|
61
|
+
* Presentation-only ops (`limit`, `offset`, `orderBy`) are intentionally ignored — this
|
|
62
|
+
* always counts the full filtered set, making paginated total-count queries possible
|
|
63
|
+
* without a second query.
|
|
63
64
|
*/
|
|
64
65
|
count(): Promise<number>;
|
|
65
66
|
/**
|
|
@@ -96,13 +97,6 @@ type ChainedQuery<T extends object, N extends T, Self extends ChainedQuery<T, N,
|
|
|
96
97
|
ignoreCase?: boolean;
|
|
97
98
|
}): QueryBuilder<T, N>;
|
|
98
99
|
toArray(): Promise<N[]>;
|
|
99
|
-
/**
|
|
100
|
-
* Returns the number of records matching the applied filter predicates.
|
|
101
|
-
* Presentation-only ops (`limit`, `offset`, `orderBy`) are intentionally ignored — this
|
|
102
|
-
* always counts the full filtered set, making paginated total-count queries possible
|
|
103
|
-
* without a second query.
|
|
104
|
-
*/
|
|
105
|
-
totalCount(): Promise<number>;
|
|
106
100
|
};
|
|
107
101
|
/** Extends the shared query API with `delete()`. Available on stores and IndexedDB transaction callbacks. */
|
|
108
102
|
export interface QueryBuilder<T extends object, N extends T = T> extends ChainedQuery<T, N, QueryBuilder<T, N>> {
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAEA,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC;AACrE;;;GAGG;AACH,KAAK,OAAO,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AACvE,KAAK,mBAAmB,CAAC,CAAC,SAAS,MAAM,IAAI;KAC1C,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,CAAC;CACxF,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACnD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI;IAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACpE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAChD,oGAAoG;IACpG,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CAC5B,CAAC;AAIF;;;GAGG;AACH,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,SAAS,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI;IACxF;;;OAGG;IACH,OAAO,CAAC,CAAC,SAAS,mBAAmB,CAAC,CAAC,CAAC,EACtC,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,EAClD,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,GACjD,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/dist/query.js
CHANGED
|
@@ -38,7 +38,7 @@ function i(a, o = []) {
|
|
|
38
38
|
}) });
|
|
39
39
|
},
|
|
40
40
|
count() {
|
|
41
|
-
return t(a, o).then((e) => e.length);
|
|
41
|
+
return t(a, o.filter((e) => !e.isNonFilter)).then((e) => e.length);
|
|
42
42
|
},
|
|
43
43
|
async delete() {
|
|
44
44
|
if (!a.deleteMany) throw new e("query.delete is not available for this adapter context");
|
|
@@ -114,9 +114,6 @@ function i(a, o = []) {
|
|
|
114
114
|
},
|
|
115
115
|
toArray() {
|
|
116
116
|
return t(a, o);
|
|
117
|
-
},
|
|
118
|
-
totalCount() {
|
|
119
|
-
return t(a, o.filter((e) => !e.isNonFilter)).then((e) => e.length);
|
|
120
117
|
}
|
|
121
118
|
};
|
|
122
119
|
}
|