pbtsdb 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -217,27 +217,29 @@ function createCollection(pb, queryClient) {
217
217
  return record.id;
218
218
  },
219
219
  onInsert: options?.onInsert === false ? void 0 : options?.onInsert ?? (async ({ transaction }) => {
220
- await Promise.all(
220
+ const created = await Promise.all(
221
221
  transaction.mutations.map(async (mutation) => {
222
222
  const {
223
- created,
224
- updated,
225
- collectionId,
226
- collectionName: _,
223
+ created: _created,
224
+ updated: _updated,
225
+ collectionId: _collectionId,
226
+ collectionName: _collectionName,
227
227
  ...data
228
228
  } = mutation.modified;
229
- await pb.collection(collectionName).create(data);
229
+ return pb.collection(collectionName).create(data);
230
230
  })
231
231
  );
232
+ writeServerRecords(created);
232
233
  return { refetch: refetchOnMutation };
233
234
  }),
234
235
  onUpdate: options?.onUpdate === false ? void 0 : options?.onUpdate ?? (async ({ transaction }) => {
235
- await Promise.all(
236
+ const updated = await Promise.all(
236
237
  transaction.mutations.map(async (mutation) => {
237
238
  const recordWithId = mutation.original;
238
- await pb.collection(collectionName).update(recordWithId.id, mutation.changes);
239
+ return pb.collection(collectionName).update(recordWithId.id, mutation.changes);
239
240
  })
240
241
  );
242
+ writeServerRecords(updated);
241
243
  return { refetch: refetchOnMutation };
242
244
  }),
243
245
  onDelete: options?.onDelete === false ? void 0 : options?.onDelete ?? (async ({ transaction }) => {
@@ -251,12 +253,42 @@ function createCollection(pb, queryClient) {
251
253
  })
252
254
  });
253
255
  const collection = createCollection$1(collectionOptions);
256
+ function recordUpdatedAt(record) {
257
+ const updated = record?.updated;
258
+ return typeof updated === "string" && updated !== "" ? updated : void 0;
259
+ }
260
+ function isStaleServerRecord(record) {
261
+ const id = record?.id;
262
+ if (typeof id !== "string") return false;
263
+ const incoming = recordUpdatedAt(record);
264
+ if (!incoming) return false;
265
+ const current = recordUpdatedAt(
266
+ collection._state.syncedData.get(id)
267
+ );
268
+ return current !== void 0 && incoming < current;
269
+ }
270
+ function writeServerRecords(records) {
271
+ if (!collection.utils || !collection.isReady()) return;
272
+ const fresh = records.filter((record) => !isStaleServerRecord(record));
273
+ if (fresh.length === 0) return;
274
+ collection.utils.writeUpsert(fresh);
275
+ }
276
+ function isStaleEcho(event) {
277
+ if (event.action !== "create" && event.action !== "update") return false;
278
+ if (!isStaleServerRecord(event.record)) return false;
279
+ logger.debug("Ignoring stale realtime echo", {
280
+ collectionName,
281
+ id: event.record?.id
282
+ });
283
+ return true;
284
+ }
254
285
  let unsubscribeFn = null;
255
286
  let isSubscribed = false;
256
287
  let subscriptionPromise = null;
257
288
  let subscriptionResolve = null;
258
289
  const handleRealtimeEvent = (event) => {
259
290
  if (!collection.utils) return;
291
+ if (isStaleEcho(event)) return;
260
292
  try {
261
293
  collection.utils.writeBatch(() => {
262
294
  switch (event.action) {
@@ -377,5 +409,5 @@ function newRecordId() {
377
409
  }
378
410
 
379
411
  export { createCollection, newRecordId, resetLogger, setLogger };
380
- //# sourceMappingURL=chunk-H4JHOAUH.js.map
381
- //# sourceMappingURL=chunk-H4JHOAUH.js.map
412
+ //# sourceMappingURL=chunk-QN7BGGSA.js.map
413
+ //# sourceMappingURL=chunk-QN7BGGSA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logger.ts","../src/pocketbase-query-converter.ts","../src/collection.ts","../src/util.ts"],"names":["createTanStackCollection"],"mappings":"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","file":"chunk-QN7BGGSA.js","sourcesContent":["/**\n * Logger interface for subscription events and internal operations.\n * Users can provide their own implementation to integrate with external logging services.\n */\nexport interface Logger {\n /**\n * Log debug-level messages (typically only shown in development).\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n debug: (msg: string, context?: object) => void\n\n /**\n * Log info-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n info: (msg: string, context?: object) => void\n\n /**\n * Log warning-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n warn: (msg: string, context?: object) => void\n\n /**\n * Log error-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n error: (msg: string, context?: object) => void\n}\n\n/**\n * Default console-based logger implementation.\n * Only logs debug messages in development mode.\n */\nconst defaultLogger: Logger = {\n debug: (msg: string, context?: object) => {\n if (process.env.NODE_ENV === 'development') {\n console.debug(`[pbtsdb] ${msg}`, context || '')\n }\n },\n info: (msg: string, context?: object) => {\n console.info(`[pbtsdb] ${msg}`, context || '')\n },\n warn: (msg: string, context?: object) => {\n console.warn(`[pbtsdb] ${msg}`, context || '')\n },\n error: (msg: string, context?: object) => {\n console.error(`[pbtsdb] ${msg}`, context || '')\n },\n}\n\n/**\n * Current logger instance (can be replaced by users).\n */\nlet currentLogger: Logger = defaultLogger\n\n/**\n * Internal logger instance used by the library.\n */\nexport const logger: Logger = {\n debug: (msg: string, context?: object) => currentLogger.debug(msg, context),\n info: (msg: string, context?: object) => currentLogger.info(msg, context),\n warn: (msg: string, context?: object) => currentLogger.warn(msg, context),\n error: (msg: string, context?: object) => currentLogger.error(msg, context),\n}\n\n/**\n * Set a custom logger implementation.\n * This allows users to integrate with their own logging services (e.g., Sentry, LogRocket, etc.).\n *\n * @param customLogger - The custom logger implementation\n *\n * @example\n * ```ts\n * import { setLogger } from 'pbtsdb';\n *\n * // Integration with a custom logging service\n * setLogger({\n * debug: (msg, context) => myLogger.debug(msg, context),\n * warn: (msg, context) => myLogger.warn(msg, context),\n * error: (msg, context) => {\n * myLogger.error(msg, context);\n * Sentry.captureMessage(msg, { level: 'error', extra: context });\n * },\n * });\n * ```\n *\n * @example\n * ```ts\n * // Disable all logging\n * setLogger({\n * debug: () => {},\n * warn: () => {},\n * error: () => {},\n * });\n * ```\n */\nexport function setLogger(customLogger: Logger): void {\n currentLogger = customLogger\n}\n\n/**\n * Reset the logger to the default implementation.\n */\nexport function resetLogger(): void {\n currentLogger = defaultLogger\n}\n","import type { IR } from '@tanstack/db'\nimport {\n type FieldPath,\n type ParsedOrderBy,\n parseOrderByExpression,\n parseWhereExpression,\n} from '@tanstack/db'\n\ntype BasicExpression<T = unknown> = IR.BasicExpression<T>\n\nfunction escapeValue(value: unknown): string {\n if (value === null) {\n return 'null'\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false'\n }\n if (typeof value === 'number') {\n return value.toString()\n }\n if (typeof value === 'string') {\n return `\"${value.replace(/\"/g, '\\\\\"')}\"`\n }\n if (value instanceof Date) {\n return `\"${value.toISOString()}\"`\n }\n if (Array.isArray(value)) {\n return `[${value.map(escapeValue).join(',')}]`\n }\n return `\"${String(value)}\"`\n}\n\nfunction fieldPathToString(path: FieldPath): string {\n return path.join('.')\n}\n\nexport function convertToPocketBaseFilter(\n where: BasicExpression<boolean> | undefined | null\n): string | undefined {\n if (!where) {\n return undefined\n }\n\n const result = parseWhereExpression(where, {\n handlers: {\n eq: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} = ${escapeValue(value)}`\n },\n gt: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} > ${escapeValue(value)}`\n },\n gte: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} >= ${escapeValue(value)}`\n },\n lt: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} < ${escapeValue(value)}`\n },\n lte: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} <= ${escapeValue(value)}`\n },\n and: (...conditions: string[]) => {\n if (conditions.length === 0) return ''\n if (conditions.length === 1) return conditions[0]\n return `(${conditions.join(' && ')})`\n },\n or: (...conditions: string[]) => {\n if (conditions.length === 0) return ''\n if (conditions.length === 1) return conditions[0]\n return `(${conditions.join(' || ')})`\n },\n not: (condition: string) => {\n return `!(${condition})`\n },\n in: (field: FieldPath, values: unknown) => {\n const valueArray = Array.isArray(values) ? values : [values]\n const uniqueValues = [...new Set(valueArray)]\n const fieldStr = fieldPathToString(field)\n const conditions = uniqueValues.map(v => `${fieldStr} = ${escapeValue(v)}`)\n return conditions.length > 1 ? `(${conditions.join(' || ')})` : conditions[0]\n },\n like: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} ~ ${escapeValue(value)}`\n },\n isNull: (field: FieldPath) => {\n return `${fieldPathToString(field)} = null`\n },\n isUndefined: (field: FieldPath) => {\n return `${fieldPathToString(field)} = null`\n },\n },\n onUnknownOperator: (operator: string, _args: unknown[]) => {\n throw new Error(\n `Unsupported operator '${operator}' for PocketBase filter conversion. ` +\n `Supported operators: eq, gt, gte, lt, lte, in, like, and, or, not, isNull, isUndefined`\n )\n },\n })\n\n return result || undefined\n}\n\nexport function convertToPocketBaseSort(\n orderBy: IR.OrderBy | undefined | null\n): string | undefined {\n if (!orderBy) {\n return undefined\n }\n\n const sorts = parseOrderByExpression(orderBy)\n\n if (sorts.length === 0) {\n return undefined\n }\n\n return sorts\n .map((sort: ParsedOrderBy) => {\n const field = fieldPathToString(sort.field)\n return sort.direction === 'desc' ? `-${field}` : field\n })\n .join(',')\n}\n","import {\n type Collection,\n createCollection as createTanStackCollection,\n type LoadSubsetOptions,\n} from '@tanstack/db'\nimport {\n DeleteOperationItemNotFoundError,\n type QueryCollectionUtils,\n queryCollectionOptions,\n} from '@tanstack/query-db-collection'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type PocketBase from 'pocketbase'\nimport type { RecordSubscription } from 'pocketbase'\nimport { logger } from './logger'\nimport { convertToPocketBaseFilter, convertToPocketBaseSort } from './pocketbase-query-converter'\nimport type {\n CreateCollectionOptions,\n ExpandTargetCollection,\n ExtractRecordType,\n SchemaDeclaration,\n} from './types'\n\nexport type { BaseRecord, CreateCollectionOptions, SchemaDeclaration } from './types'\n\n/**\n * Extended LoadSubsetOptions that includes PocketBase-specific expand parameter.\n * @internal\n */\ntype ExtendedLoadSubsetOptions = LoadSubsetOptions & {\n pbExpand?: string\n}\n\n/**\n * Compute the record type with expand property when expand option is configured.\n * @internal\n */\ntype WithExpandFromConfig<\n Schema extends SchemaDeclaration,\n C extends keyof Schema,\n Opts,\n> = Opts extends {\n expand: infer E\n}\n ? ExtractRecordType<Schema, C> & {\n expand?: {\n [K in keyof E]: K extends keyof import('./types').ExtractRelations<Schema, C>\n ? import('./types').ExtractRelations<Schema, C>[K] extends Array<infer U>\n ? U[]\n : import('./types').ExtractRelations<Schema, C>[K]\n : never\n }\n }\n : ExtractRecordType<Schema, C>\n\n/**\n * Subscription helpers added to collection instances.\n * @internal\n */\ninterface CollectionSubscriptionHelpers {\n /** The PocketBase collection name */\n collectionName: string\n /** Wait for subscription to be established (useful in tests) */\n waitForSubscription: (timeout?: number) => Promise<void>\n /** Check if collection has an active subscription */\n isSubscribed: () => boolean\n}\n\n/**\n * Inferred collection type from config options.\n * @internal\n */\ntype InferCollectionType<\n Schema extends SchemaDeclaration,\n C extends keyof Schema,\n Opts extends CreateCollectionOptions<Schema, C>,\n> = Collection<\n WithExpandFromConfig<Schema, C, Opts>,\n string | number,\n // TUtils - QueryCollectionUtils from TanStack Query DB Collection\n QueryCollectionUtils<\n WithExpandFromConfig<Schema, C, Opts>,\n string | number,\n WithExpandFromConfig<Schema, C, Opts>\n >,\n // TSchema - we don't use StandardSchema validation\n never,\n Opts extends {\n omitOnInsert: infer O extends readonly import('./types').OmittableFields<\n ExtractRecordType<Schema, C>\n >[]\n }\n ? import('./types').ComputeInsertType<ExtractRecordType<Schema, C>, O>\n : ExtractRecordType<Schema, C>\n> &\n CollectionSubscriptionHelpers\n\n/**\n * Creates a type-safe TanStack DB collection backed by PocketBase.\n * Use this when you need fine-grained control or need to create collections with dependencies.\n *\n * @param pb - PocketBase client instance\n * @param queryClient - TanStack Query client\n * @returns A curried function that takes collection name and options\n *\n * @example\n * Basic usage:\n * ```ts\n * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {});\n *\n * // Use directly\n * const books = await booksCollection.getFullList();\n * ```\n *\n * @example\n * With auto-expand relations:\n * ```ts\n * const authorsCollection = createCollection<Schema>(pb, queryClient)('authors', {});\n * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {\n * expand: {\n * author: authorsCollection // Always expand, auto-upsert into authorsCollection\n * }\n * });\n *\n * // Expand is automatic - no .expand() call needed\n * const { data } = useLiveQuery((q) => q.from({ books: booksCollection }));\n * // data[0].expand.author is typed and populated\n * ```\n */\nexport function createCollection<Schema extends SchemaDeclaration>(\n pb: PocketBase,\n queryClient: QueryClient\n) {\n return <\n C extends keyof Schema & string,\n Opts extends CreateCollectionOptions<Schema, C> = CreateCollectionOptions<Schema, C>,\n >(\n collectionName: C,\n options?: Opts\n ): InferCollectionType<Schema, C, Opts> => {\n type RecordType = ExtractRecordType<Schema, C>\n const expandStores = options?.expand as Record<string, ExpandTargetCollection> | undefined\n const expandString = expandStores ? Object.keys(expandStores).sort().join(',') : undefined\n\n const ignoreAutoCancellation = options?.ignoreAutoCancellation ?? true\n const refetchOnMutation = options?.refetchOnMutation ?? false\n\n async function upsertExpandedRelation(\n key: string,\n value: object | object[],\n stores: Record<string, ExpandTargetCollection>\n ): Promise<void> {\n const targetStore = stores[key]\n if (!targetStore.utils) return\n if (!targetStore.isReady()) {\n if (targetStore.config?.syncMode === 'on-demand') {\n await targetStore._sync.startSync()\n } else {\n logger.warn(\n `not syncing ${key} on ${collectionName} because store is not yet ready`\n )\n return\n }\n }\n const values = Array.isArray(value) ? value : [value]\n targetStore.utils.writeUpsert(values)\n }\n\n async function upsertExpandedRelations(items: RecordType[]): Promise<void> {\n if (!expandStores) return\n for (const record of items) {\n const expandData = (\n record as RecordType & { expand?: Record<string, object | object[]> }\n ).expand\n if (!expandData) continue\n for (const [key, value] of Object.entries(expandData)) {\n await upsertExpandedRelation(key, value, expandStores)\n }\n }\n }\n\n async function fetchItems(loadOptions?: ExtendedLoadSubsetOptions): Promise<RecordType[]> {\n const filter = convertToPocketBaseFilter(loadOptions?.where)\n const sort = convertToPocketBaseSort(loadOptions?.orderBy)\n const limit = loadOptions?.limit\n\n if (limit) {\n // Use getList when limit is specified to avoid fetching all records\n const result = await pb.collection(collectionName).getList(1, limit, {\n filter,\n sort,\n skipTotal: true, // Optimize by skipping total count\n expand: expandString,\n })\n return result.items as unknown as RecordType[]\n }\n // Use getFullList to fetch all records with automatic pagination\n return (await pb.collection(collectionName).getFullList({\n filter,\n sort,\n expand: expandString,\n })) as unknown as RecordType[]\n }\n\n async function fetchRecords(\n loadOptions?: ExtendedLoadSubsetOptions\n ): Promise<RecordType[]> {\n let items: RecordType[]\n try {\n items = await fetchItems(loadOptions)\n } catch (error) {\n if (\n ignoreAutoCancellation &&\n error instanceof Error &&\n error.message.includes('autocancelled')\n ) {\n return queryClient.getQueryData<RecordType[]>([collectionName]) ?? []\n }\n throw error\n }\n\n await upsertExpandedRelations(items)\n\n return items\n }\n\n const collectionOptions = queryCollectionOptions({\n ...options?.collectionOptions,\n queryClient,\n queryKey: [collectionName],\n syncMode: options?.syncMode ?? 'eager',\n queryFn: async (ctx): Promise<RecordType[]> => {\n return fetchRecords(\n ctx.meta?.loadSubsetOptions as ExtendedLoadSubsetOptions | undefined\n )\n },\n getKey: (item: RecordType) => {\n const record = item as unknown as Record<string, unknown>\n if (!record || typeof record !== 'object' || !('id' in record)) {\n throw new Error(\n `Record in collection '${collectionName}' is missing required 'id' field. Received: ${JSON.stringify(item)}`\n )\n }\n return record.id as string\n },\n onInsert:\n options?.onInsert === false\n ? undefined\n : (options?.onInsert ??\n (async ({ transaction }) => {\n const created = await Promise.all(\n transaction.mutations.map(async mutation => {\n const {\n created: _created,\n updated: _updated,\n collectionId: _collectionId,\n collectionName: _collectionName,\n ...data\n } = mutation.modified as unknown as Record<string, unknown>\n return pb.collection(collectionName).create(data)\n })\n )\n writeServerRecords(created)\n return { refetch: refetchOnMutation }\n })),\n onUpdate:\n options?.onUpdate === false\n ? undefined\n : (options?.onUpdate ??\n (async ({ transaction }) => {\n const updated = await Promise.all(\n transaction.mutations.map(async mutation => {\n const recordWithId = mutation.original as { id: string }\n return pb\n .collection(collectionName)\n .update(recordWithId.id, mutation.changes)\n })\n )\n writeServerRecords(updated)\n return { refetch: refetchOnMutation }\n })),\n onDelete:\n options?.onDelete === false\n ? undefined\n : (options?.onDelete ??\n (async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map(async mutation => {\n const recordWithId = mutation.original as { id: string }\n await pb.collection(collectionName).delete(recordWithId.id)\n })\n )\n return { refetch: refetchOnMutation }\n })),\n })\n\n const collection = createTanStackCollection(collectionOptions)\n\n // Read the PocketBase `updated` autodate from a record, if present.\n // Collections without an `updated` field opt out of staleness checks.\n function recordUpdatedAt(record: unknown): string | undefined {\n const updated = (record as { updated?: unknown } | null | undefined)?.updated\n return typeof updated === 'string' && updated !== '' ? updated : undefined\n }\n\n // A server record is stale relative to the synced store when an entry for\n // the same key already holds a strictly-newer `updated` timestamp. PocketBase\n // can redeliver or reorder realtime echoes (and a slow mutation response can\n // resolve after a newer echo), so applying an older write would revert the\n // row. ISO 8601 timestamps sort lexicographically, so string comparison is\n // chronological. When either side lacks a comparable timestamp we cannot\n // tell, so we treat the write as fresh and let it through.\n function isStaleServerRecord(record: unknown): boolean {\n const id = (record as { id?: unknown } | null | undefined)?.id\n if (typeof id !== 'string') return false\n const incoming = recordUpdatedAt(record)\n if (!incoming) return false\n const current = recordUpdatedAt(\n collection._state.syncedData.get(id) as RecordType | undefined\n )\n return current !== undefined && incoming < current\n }\n\n // Write authoritative server records (mutation responses or realtime echoes)\n // into the synced store, dropping any that the store already supersedes.\n // No-op until the collection is ready: writing into the synced store before\n // sync has initialized throws, and with no live query there is nothing to\n // keep in sync — the next query fetches the already-persisted state.\n function writeServerRecords(records: RecordType[]): void {\n if (!collection.utils || !collection.isReady()) return\n const fresh = records.filter(record => !isStaleServerRecord(record))\n if (fresh.length === 0) return\n collection.utils.writeUpsert(fresh)\n }\n\n // Decide whether a realtime echo should be dropped as stale. Under realtime\n // contention PocketBase can redeliver or reorder events, so an echo carrying\n // a pre-mutation value can arrive after the row already moved on (e.g. the\n // local mutation that just wrote the fresh value back). Applying a\n // strictly-older create/update echo would revert the row, so it is ignored.\n // Deletes are terminal and not timestamp-guarded.\n function isStaleEcho(event: RecordSubscription<RecordType>): boolean {\n if (event.action !== 'create' && event.action !== 'update') return false\n if (!isStaleServerRecord(event.record)) return false\n logger.debug('Ignoring stale realtime echo', {\n collectionName,\n id: (event.record as { id?: string } | undefined)?.id,\n })\n return true\n }\n\n // Real-time subscription state\n let unsubscribeFn: (() => Promise<void>) | null = null\n let isSubscribed = false\n let subscriptionPromise: Promise<void> | null = null\n let subscriptionResolve: (() => void) | null = null\n\n // Handle real-time events from PocketBase.\n //\n // The write primitives differ in how they treat a key that is absent from\n // the *synced* store (collection._state.syncedData, which is what they\n // validate against — not the optimistic view exposed by collection.has()):\n // - writeInsert / writeUpsert: idempotent, never throw on an absent key.\n // - writeDelete: throws DeleteOperationItemNotFoundError on an absent key.\n // So only the delete branch can throw, and we make it idempotent below.\n const handleRealtimeEvent = (event: RecordSubscription<RecordType>) => {\n if (!collection.utils) return\n if (isStaleEcho(event)) return\n\n try {\n collection.utils.writeBatch(() => {\n switch (event.action) {\n case 'create':\n collection.utils.writeInsert(event.record)\n break\n case 'update':\n collection.utils.writeUpsert(event.record)\n break\n case 'delete':\n if (event.record && 'id' in event.record) {\n // Throws DeleteOperationItemNotFoundError if the key\n // is no longer in the synced store (see catch below).\n collection.utils.writeDelete((event.record as { id: string }).id)\n }\n break\n }\n })\n } catch (error) {\n // How a delete echo throws: writeDelete fails when its key is already\n // gone from the synced store. That happens when something removed it\n // before the echo arrived:\n // 1. on-demand sync — each useLiveQuery refetches with a server\n // filter, and query-db-collection prunes rows no longer owned by\n // any active query out of the synced store. If that prune (or a\n // concurrent query's reconcile) runs before this client's own\n // delete echo lands, the key is already gone -> throw. This is\n // the on-demand-only race; eager collections have no such second\n // writer to the synced store, so they cannot hit it.\n // 2. a re-delivered SSE delete (e.g. after a reconnect) for a key\n // that was already deleted -> throw on the second echo.\n // In both cases the record is already in its intended end state\n // (gone), so the echo is a no-op and the error is safe to ignore.\n // Anything that is NOT a missing-key delete is a real error: rethrow.\n if (error instanceof DeleteOperationItemNotFoundError) {\n logger.debug('Ignoring delete echo for already-removed record', {\n collectionName,\n id: (event.record as { id?: string } | undefined)?.id,\n })\n } else {\n throw error\n }\n }\n }\n\n // Start PocketBase real-time subscription\n const startSubscription = async () => {\n if (isSubscribed) return\n\n // Create promise before starting so waiters can await it\n if (!subscriptionPromise) {\n subscriptionPromise = new Promise<void>(resolve => {\n subscriptionResolve = resolve\n })\n }\n\n try {\n unsubscribeFn = await pb\n .collection(collectionName)\n .subscribe('*', handleRealtimeEvent)\n isSubscribed = true\n logger.debug('Subscription started', { collectionName })\n // Resolve the promise to notify waiters\n if (subscriptionResolve) {\n subscriptionResolve()\n }\n } catch (error) {\n logger.error('Failed to start subscription', { collectionName, error })\n }\n }\n\n // Stop PocketBase real-time subscription\n const stopSubscription = async () => {\n if (!isSubscribed || !unsubscribeFn) return\n\n try {\n await unsubscribeFn()\n unsubscribeFn = null\n isSubscribed = false\n // Reset promise for next subscription cycle\n subscriptionPromise = null\n subscriptionResolve = null\n logger.debug('Subscription stopped', { collectionName })\n } catch (error) {\n logger.debug('Unsubscribe failed (expected if connection closed)', {\n collectionName,\n error,\n })\n }\n }\n\n // Wait for subscription to be established (for testing)\n const waitForSubscription = async (timeout = 5000): Promise<void> => {\n if (isSubscribed) return\n\n if (!subscriptionPromise) {\n // No subscription in progress, wait for one to start\n await new Promise<void>(resolve => {\n const checkInterval = setInterval(() => {\n if (subscriptionPromise) {\n clearInterval(checkInterval)\n resolve()\n }\n }, 10)\n setTimeout(() => {\n clearInterval(checkInterval)\n resolve()\n }, timeout)\n })\n }\n\n if (subscriptionPromise) {\n await Promise.race([\n subscriptionPromise,\n new Promise<void>((_, reject) =>\n setTimeout(() => reject(new Error('Subscription timeout')), timeout)\n ),\n ])\n }\n }\n\n // Manage subscription based on collection subscriber count\n collection.on(\n 'subscribers:change',\n (event: { subscriberCount: number; previousSubscriberCount: number }) => {\n const newCount = event.subscriberCount\n const previousCount = event.previousSubscriberCount\n\n if (newCount > 0 && previousCount === 0) {\n // First subscriber - start real-time subscription\n startSubscription().catch(() => {})\n } else if (newCount === 0 && previousCount > 0) {\n // Last subscriber removed - stop real-time subscription\n stopSubscription().catch(() => {})\n }\n }\n )\n\n // Add collectionName and subscription helpers\n Object.assign(collection, {\n collectionName,\n waitForSubscription,\n isSubscribed: () => isSubscribed,\n })\n\n return collection as unknown as InferCollectionType<Schema, C, Opts>\n }\n}\n","/**\n * Generates a new PocketBase-compatible record ID.\n * Returns a 15-character alphanumeric string (lowercase letters and numbers).\n *\n * PocketBase uses 15-character IDs for records, formatted as lowercase alphanumeric.\n *\n * @returns A 15-character alphanumeric string suitable for use as a PocketBase record ID\n *\n * @example\n * ```ts\n * const id = newRecordId(); // \"a1b2c3d4e5f6g7h\"\n * ```\n */\nexport function newRecordId(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'\n let result = ''\n for (let i = 0; i < 15; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length))\n }\n return result\n}\n"]}
package/dist/core.d.ts CHANGED
@@ -288,10 +288,11 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
288
288
  onDelete?: DeleteMutationFn<ExtractRecordType<Schema, CollectionName>> | false;
289
289
  /**
290
290
  * If true, refetch the entire collection after a successful insert,
291
- * update, or delete. Defaults to false: pbtsdb relies on its PocketBase
292
- * realtime subscription to deliver server-confirmed rows. Set true if
293
- * you do not trust realtime to reconcile in time (e.g. flaky socket,
294
- * server-side hooks producing fields you must read synchronously).
291
+ * update, or delete. Defaults to false: the built-in insert/update handlers
292
+ * write the server response straight back into the synced layer, and the
293
+ * realtime subscription reconciles everything else. Set true if you do not
294
+ * trust realtime to reconcile in time (e.g. flaky socket, server-side hooks
295
+ * producing fields you must read synchronously).
295
296
  *
296
297
  * Only affects the built-in default handlers. If you supply your own
297
298
  * onInsert/onUpdate/onDelete, you control the return value yourself.
package/dist/core.js CHANGED
@@ -1,3 +1,3 @@
1
- export { BTreeIndex, BasicIndex, ReverseIndex, createCollection, createEffect, newRecordId, resetLogger, setLogger, toArray } from './chunk-H4JHOAUH.js';
1
+ export { BTreeIndex, BasicIndex, ReverseIndex, createCollection, createEffect, newRecordId, resetLogger, setLogger, toArray } from './chunk-QN7BGGSA.js';
2
2
  //# sourceMappingURL=core.js.map
3
3
  //# sourceMappingURL=core.js.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { BTreeIndex, BasicIndex, ReverseIndex, createCollection, createEffect, newRecordId, resetLogger, setLogger, toArray } from './chunk-H4JHOAUH.js';
1
+ export { BTreeIndex, BasicIndex, ReverseIndex, createCollection, createEffect, newRecordId, resetLogger, setLogger, toArray } from './chunk-QN7BGGSA.js';
2
2
  import { createContext, useContext } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pbtsdb",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Type-safe PocketBase integration with TanStack Query and TanStack DB",
5
5
  "keywords": [
6
6
  "pocketbase",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/logger.ts","../src/pocketbase-query-converter.ts","../src/collection.ts","../src/util.ts"],"names":["createTanStackCollection"],"mappings":";;;;;;;AAsCA,IAAM,aAAA,GAAwB;AAAA,EAC1B,KAAA,EAAO,CAAC,GAAA,EAAa,OAAA,KAAqB;AACtC,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,EAAe;AACxC,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,SAAA,EAAY,GAAG,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,IAClD;AAAA,EACJ,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,GAAA,EAAa,OAAA,KAAqB;AACrC,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,SAAA,EAAY,GAAG,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,EACjD,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,GAAA,EAAa,OAAA,KAAqB;AACrC,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,SAAA,EAAY,GAAG,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,EACjD,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,GAAA,EAAa,OAAA,KAAqB;AACtC,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,SAAA,EAAY,GAAG,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,EAClD;AACJ,CAAA;AAKA,IAAI,aAAA,GAAwB,aAAA;AAKrB,IAAM,MAAA,GAAiB;AAAA,EAC1B,OAAO,CAAC,GAAA,EAAa,YAAqB,aAAA,CAAc,KAAA,CAAM,KAAK,OAAO,CAAA;AAAA,EAC1E,MAAM,CAAC,GAAA,EAAa,YAAqB,aAAA,CAAc,IAAA,CAAK,KAAK,OAAO,CAAA;AAAA,EACxE,MAAM,CAAC,GAAA,EAAa,YAAqB,aAAA,CAAc,IAAA,CAAK,KAAK,OAAO,CAAA;AAAA,EACxE,OAAO,CAAC,GAAA,EAAa,YAAqB,aAAA,CAAc,KAAA,CAAM,KAAK,OAAO;AAC9E,CAAA;AAiCO,SAAS,UAAU,YAAA,EAA4B;AAClD,EAAA,aAAA,GAAgB,YAAA;AACpB;AAKO,SAAS,WAAA,GAAoB;AAChC,EAAA,aAAA,GAAgB,aAAA;AACpB;ACpGA,SAAS,YAAY,KAAA,EAAwB;AACzC,EAAA,IAAI,UAAU,IAAA,EAAM;AAChB,IAAA,OAAO,MAAA;AAAA,EACX;AACA,EAAA,IAAI,OAAO,UAAU,SAAA,EAAW;AAC5B,IAAA,OAAO,QAAQ,MAAA,GAAS,OAAA;AAAA,EAC5B;AACA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,IAAA,OAAO,MAAM,QAAA,EAAS;AAAA,EAC1B;AACA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,IAAA,OAAO,CAAA,CAAA,EAAI,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAC,CAAA,CAAA,CAAA;AAAA,EACzC;AACA,EAAA,IAAI,iBAAiB,IAAA,EAAM;AACvB,IAAA,OAAO,CAAA,CAAA,EAAI,KAAA,CAAM,WAAA,EAAa,CAAA,CAAA,CAAA;AAAA,EAClC;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,IAAA,OAAO,IAAI,KAAA,CAAM,GAAA,CAAI,WAAW,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAA;AAAA,EAC/C;AACA,EAAA,OAAO,CAAA,CAAA,EAAI,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA,CAAA;AAC5B;AAEA,SAAS,kBAAkB,IAAA,EAAyB;AAChD,EAAA,OAAO,IAAA,CAAK,KAAK,GAAG,CAAA;AACxB;AAEO,SAAS,0BACZ,KAAA,EACkB;AAClB,EAAA,IAAI,CAAC,KAAA,EAAO;AACR,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,MAAA,GAAS,qBAAqB,KAAA,EAAO;AAAA,IACvC,QAAA,EAAU;AAAA,MACN,EAAA,EAAI,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACtC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,GAAA,EAAM,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC9D,CAAA;AAAA,MACA,EAAA,EAAI,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACtC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,GAAA,EAAM,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC9D,CAAA;AAAA,MACA,GAAA,EAAK,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACvC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,IAAA,EAAO,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC/D,CAAA;AAAA,MACA,EAAA,EAAI,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACtC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,GAAA,EAAM,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC9D,CAAA;AAAA,MACA,GAAA,EAAK,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACvC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,IAAA,EAAO,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC/D,CAAA;AAAA,MACA,GAAA,EAAK,IAAI,UAAA,KAAyB;AAC9B,QAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACpC,QAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,WAAW,CAAC,CAAA;AAChD,QAAA,OAAO,CAAA,CAAA,EAAI,UAAA,CAAW,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,CAAA;AAAA,MACtC,CAAA;AAAA,MACA,EAAA,EAAI,IAAI,UAAA,KAAyB;AAC7B,QAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AACpC,QAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,WAAW,CAAC,CAAA;AAChD,QAAA,OAAO,CAAA,CAAA,EAAI,UAAA,CAAW,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA,CAAA;AAAA,MACtC,CAAA;AAAA,MACA,GAAA,EAAK,CAAC,SAAA,KAAsB;AACxB,QAAA,OAAO,KAAK,SAAS,CAAA,CAAA,CAAA;AAAA,MACzB,CAAA;AAAA,MACA,EAAA,EAAI,CAAC,KAAA,EAAkB,MAAA,KAAoB;AACvC,QAAA,MAAM,aAAa,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,GAAI,MAAA,GAAS,CAAC,MAAM,CAAA;AAC3D,QAAA,MAAM,eAAe,CAAC,GAAG,IAAI,GAAA,CAAI,UAAU,CAAC,CAAA;AAC5C,QAAA,MAAM,QAAA,GAAW,kBAAkB,KAAK,CAAA;AACxC,QAAA,MAAM,UAAA,GAAa,YAAA,CAAa,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,EAAG,QAAQ,CAAA,GAAA,EAAM,WAAA,CAAY,CAAC,CAAC,CAAA,CAAE,CAAA;AAC1E,QAAA,OAAO,UAAA,CAAW,MAAA,GAAS,CAAA,GAAI,CAAA,CAAA,EAAI,UAAA,CAAW,KAAK,MAAM,CAAC,CAAA,CAAA,CAAA,GAAM,UAAA,CAAW,CAAC,CAAA;AAAA,MAChF,CAAA;AAAA,MACA,IAAA,EAAM,CAAC,KAAA,EAAkB,KAAA,KAAmB;AACxC,QAAA,OAAO,GAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,GAAA,EAAM,WAAA,CAAY,KAAK,CAAC,CAAA,CAAA;AAAA,MAC9D,CAAA;AAAA,MACA,MAAA,EAAQ,CAAC,KAAA,KAAqB;AAC1B,QAAA,OAAO,CAAA,EAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,OAAA,CAAA;AAAA,MACtC,CAAA;AAAA,MACA,WAAA,EAAa,CAAC,KAAA,KAAqB;AAC/B,QAAA,OAAO,CAAA,EAAG,iBAAA,CAAkB,KAAK,CAAC,CAAA,OAAA,CAAA;AAAA,MACtC;AAAA,KACJ;AAAA,IACA,iBAAA,EAAmB,CAAC,QAAA,EAAkB,KAAA,KAAqB;AACvD,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,yBAAyB,QAAQ,CAAA,0HAAA;AAAA,OAErC;AAAA,IACJ;AAAA,GACH,CAAA;AAED,EAAA,OAAO,MAAA,IAAU,MAAA;AACrB;AAEO,SAAS,wBACZ,OAAA,EACkB;AAClB,EAAA,IAAI,CAAC,OAAA,EAAS;AACV,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,MAAM,KAAA,GAAQ,uBAAuB,OAAO,CAAA;AAE5C,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACpB,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,OAAO,KAAA,CACF,GAAA,CAAI,CAAC,IAAA,KAAwB;AAC1B,IAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,IAAA,CAAK,KAAK,CAAA;AAC1C,IAAA,OAAO,IAAA,CAAK,SAAA,KAAc,MAAA,GAAS,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,KAAA;AAAA,EACrD,CAAC,CAAA,CACA,IAAA,CAAK,GAAG,CAAA;AACjB;;;ACQO,SAAS,gBAAA,CACZ,IACA,WAAA,EACF;AACE,EAAA,OAAO,CAIH,gBACA,OAAA,KACuC;AAEvC,IAAA,MAAM,eAAe,OAAA,EAAS,MAAA;AAC9B,IAAA,MAAM,YAAA,GAAe,YAAA,GAAe,MAAA,CAAO,IAAA,CAAK,YAAY,EAAE,IAAA,EAAK,CAAE,IAAA,CAAK,GAAG,CAAA,GAAI,MAAA;AAEjF,IAAA,MAAM,sBAAA,GAAyB,SAAS,sBAAA,IAA0B,IAAA;AAClE,IAAA,MAAM,iBAAA,GAAoB,SAAS,iBAAA,IAAqB,KAAA;AAExD,IAAA,eAAe,sBAAA,CACX,GAAA,EACA,KAAA,EACA,MAAA,EACa;AACb,MAAA,MAAM,WAAA,GAAc,OAAO,GAAG,CAAA;AAC9B,MAAA,IAAI,CAAC,YAAY,KAAA,EAAO;AACxB,MAAA,IAAI,CAAC,WAAA,CAAY,OAAA,EAAQ,EAAG;AACxB,QAAA,IAAI,WAAA,CAAY,MAAA,EAAQ,QAAA,KAAa,WAAA,EAAa;AAC9C,UAAA,MAAM,WAAA,CAAY,MAAM,SAAA,EAAU;AAAA,QACtC,CAAA,MAAO;AACH,UAAA,MAAA,CAAO,IAAA;AAAA,YACH,CAAA,YAAA,EAAe,GAAG,CAAA,IAAA,EAAO,cAAc,CAAA,+BAAA;AAAA,WAC3C;AACA,UAAA;AAAA,QACJ;AAAA,MACJ;AACA,MAAA,MAAM,SAAS,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACpD,MAAA,WAAA,CAAY,KAAA,CAAM,YAAY,MAAM,CAAA;AAAA,IACxC;AAEA,IAAA,eAAe,wBAAwB,KAAA,EAAoC;AACvE,MAAA,IAAI,CAAC,YAAA,EAAc;AACnB,MAAA,KAAA,MAAW,UAAU,KAAA,EAAO;AACxB,QAAA,MAAM,aACF,MAAA,CACF,MAAA;AACF,QAAA,IAAI,CAAC,UAAA,EAAY;AACjB,QAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AACnD,UAAA,MAAM,sBAAA,CAAuB,GAAA,EAAK,KAAA,EAAO,YAAY,CAAA;AAAA,QACzD;AAAA,MACJ;AAAA,IACJ;AAEA,IAAA,eAAe,WAAW,WAAA,EAAgE;AACtF,MAAA,MAAM,MAAA,GAAS,yBAAA,CAA0B,WAAA,EAAa,KAAK,CAAA;AAC3D,MAAA,MAAM,IAAA,GAAO,uBAAA,CAAwB,WAAA,EAAa,OAAO,CAAA;AACzD,MAAA,MAAM,QAAQ,WAAA,EAAa,KAAA;AAE3B,MAAA,IAAI,KAAA,EAAO;AAEP,QAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA,CAAE,OAAA,CAAQ,GAAG,KAAA,EAAO;AAAA,UACjE,MAAA;AAAA,UACA,IAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA;AAAA,UACX,MAAA,EAAQ;AAAA,SACX,CAAA;AACD,QAAA,OAAO,MAAA,CAAO,KAAA;AAAA,MAClB;AAEA,MAAA,OAAQ,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,EAAE,WAAA,CAAY;AAAA,QACpD,MAAA;AAAA,QACA,IAAA;AAAA,QACA,MAAA,EAAQ;AAAA,OACX,CAAA;AAAA,IACL;AAEA,IAAA,eAAe,aACX,WAAA,EACqB;AACrB,MAAA,IAAI,KAAA;AACJ,MAAA,IAAI;AACA,QAAA,KAAA,GAAQ,MAAM,WAAW,WAAW,CAAA;AAAA,MACxC,SAAS,KAAA,EAAO;AACZ,QAAA,IACI,0BACA,KAAA,YAAiB,KAAA,IACjB,MAAM,OAAA,CAAQ,QAAA,CAAS,eAAe,CAAA,EACxC;AACE,UAAA,OAAO,YAAY,YAAA,CAA2B,CAAC,cAAc,CAAC,KAAK,EAAC;AAAA,QACxE;AACA,QAAA,MAAM,KAAA;AAAA,MACV;AAEA,MAAA,MAAM,wBAAwB,KAAK,CAAA;AAEnC,MAAA,OAAO,KAAA;AAAA,IACX;AAEA,IAAA,MAAM,oBAAoB,sBAAA,CAAuB;AAAA,MAC7C,GAAG,OAAA,EAAS,iBAAA;AAAA,MACZ,WAAA;AAAA,MACA,QAAA,EAAU,CAAC,cAAc,CAAA;AAAA,MACzB,QAAA,EAAU,SAAS,QAAA,IAAY,OAAA;AAAA,MAC/B,OAAA,EAAS,OAAO,GAAA,KAA+B;AAC3C,QAAA,OAAO,YAAA;AAAA,UACH,IAAI,IAAA,EAAM;AAAA,SACd;AAAA,MACJ,CAAA;AAAA,MACA,MAAA,EAAQ,CAAC,IAAA,KAAqB;AAC1B,QAAA,MAAM,MAAA,GAAS,IAAA;AACf,QAAA,IAAI,CAAC,MAAA,IAAU,OAAO,WAAW,QAAA,IAAY,EAAE,QAAQ,MAAA,CAAA,EAAS;AAC5D,UAAA,MAAM,IAAI,KAAA;AAAA,YACN,yBAAyB,cAAc,CAAA,4CAAA,EAA+C,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,WAC9G;AAAA,QACJ;AACA,QAAA,OAAO,MAAA,CAAO,EAAA;AAAA,MAClB,CAAA;AAAA,MACA,QAAA,EACI,OAAA,EAAS,QAAA,KAAa,KAAA,GAChB,MAAA,GACC,SAAS,QAAA,KACT,OAAO,EAAE,WAAA,EAAY,KAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,UACV,WAAA,CAAY,SAAA,CAAU,GAAA,CAAI,OAAM,QAAA,KAAY;AACxC,YAAA,MAAM;AAAA,cACF,OAAA;AAAA,cACA,OAAA;AAAA,cACA,YAAA;AAAA,cACA,cAAA,EAAgB,CAAA;AAAA,cAChB,GAAG;AAAA,gBACH,QAAA,CAAS,QAAA;AACb,YAAA,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA,CAAE,OAAO,IAAI,CAAA;AAAA,UACnD,CAAC;AAAA,SACL;AACA,QAAA,OAAO,EAAE,SAAS,iBAAA,EAAkB;AAAA,MACxC,CAAA,CAAA;AAAA,MACV,QAAA,EACI,OAAA,EAAS,QAAA,KAAa,KAAA,GAChB,MAAA,GACC,SAAS,QAAA,KACT,OAAO,EAAE,WAAA,EAAY,KAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,UACV,WAAA,CAAY,SAAA,CAAU,GAAA,CAAI,OAAM,QAAA,KAAY;AACxC,YAAA,MAAM,eAAe,QAAA,CAAS,QAAA;AAC9B,YAAA,MAAM,EAAA,CACD,WAAW,cAAc,CAAA,CACzB,OAAO,YAAA,CAAa,EAAA,EAAI,SAAS,OAAO,CAAA;AAAA,UACjD,CAAC;AAAA,SACL;AACA,QAAA,OAAO,EAAE,SAAS,iBAAA,EAAkB;AAAA,MACxC,CAAA,CAAA;AAAA,MACV,QAAA,EACI,OAAA,EAAS,QAAA,KAAa,KAAA,GAChB,MAAA,GACC,SAAS,QAAA,KACT,OAAO,EAAE,WAAA,EAAY,KAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,UACV,WAAA,CAAY,SAAA,CAAU,GAAA,CAAI,OAAM,QAAA,KAAY;AACxC,YAAA,MAAM,eAAe,QAAA,CAAS,QAAA;AAC9B,YAAA,MAAM,GAAG,UAAA,CAAW,cAAc,CAAA,CAAE,MAAA,CAAO,aAAa,EAAE,CAAA;AAAA,UAC9D,CAAC;AAAA,SACL;AACA,QAAA,OAAO,EAAE,SAAS,iBAAA,EAAkB;AAAA,MACxC,CAAA;AAAA,KACb,CAAA;AAED,IAAA,MAAM,UAAA,GAAaA,mBAAyB,iBAAiB,CAAA;AAG7D,IAAA,IAAI,aAAA,GAA8C,IAAA;AAClD,IAAA,IAAI,YAAA,GAAe,KAAA;AACnB,IAAA,IAAI,mBAAA,GAA4C,IAAA;AAChD,IAAA,IAAI,mBAAA,GAA2C,IAAA;AAU/C,IAAA,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAA0C;AACnE,MAAA,IAAI,CAAC,WAAW,KAAA,EAAO;AAEvB,MAAA,IAAI;AACA,QAAA,UAAA,CAAW,KAAA,CAAM,WAAW,MAAM;AAC9B,UAAA,QAAQ,MAAM,MAAA;AAAQ,YAClB,KAAK,QAAA;AACD,cAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,MAAM,CAAA;AACzC,cAAA;AAAA,YACJ,KAAK,QAAA;AACD,cAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,MAAM,CAAA;AACzC,cAAA;AAAA,YACJ,KAAK,QAAA;AACD,cAAA,IAAI,KAAA,CAAM,MAAA,IAAU,IAAA,IAAQ,KAAA,CAAM,MAAA,EAAQ;AAGtC,gBAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAa,KAAA,CAAM,MAAA,CAA0B,EAAE,CAAA;AAAA,cACpE;AACA,cAAA;AAAA;AACR,QACJ,CAAC,CAAA;AAAA,MACL,SAAS,KAAA,EAAO;AAgBZ,QAAA,IAAI,iBAAiB,gCAAA,EAAkC;AACnD,UAAA,MAAA,CAAO,MAAM,iDAAA,EAAmD;AAAA,YAC5D,cAAA;AAAA,YACA,EAAA,EAAK,MAAM,MAAA,EAAwC;AAAA,WACtD,CAAA;AAAA,QACL,CAAA,MAAO;AACH,UAAA,MAAM,KAAA;AAAA,QACV;AAAA,MACJ;AAAA,IACJ,CAAA;AAGA,IAAA,MAAM,oBAAoB,YAAY;AAClC,MAAA,IAAI,YAAA,EAAc;AAGlB,MAAA,IAAI,CAAC,mBAAA,EAAqB;AACtB,QAAA,mBAAA,GAAsB,IAAI,QAAc,CAAA,OAAA,KAAW;AAC/C,UAAA,mBAAA,GAAsB,OAAA;AAAA,QAC1B,CAAC,CAAA;AAAA,MACL;AAEA,MAAA,IAAI;AACA,QAAA,aAAA,GAAgB,MAAM,EAAA,CACjB,UAAA,CAAW,cAAc,CAAA,CACzB,SAAA,CAAU,KAAK,mBAAmB,CAAA;AACvC,QAAA,YAAA,GAAe,IAAA;AACf,QAAA,MAAA,CAAO,KAAA,CAAM,sBAAA,EAAwB,EAAE,cAAA,EAAgB,CAAA;AAEvD,QAAA,IAAI,mBAAA,EAAqB;AACrB,UAAA,mBAAA,EAAoB;AAAA,QACxB;AAAA,MACJ,SAAS,KAAA,EAAO;AACZ,QAAA,MAAA,CAAO,KAAA,CAAM,8BAAA,EAAgC,EAAE,cAAA,EAAgB,OAAO,CAAA;AAAA,MAC1E;AAAA,IACJ,CAAA;AAGA,IAAA,MAAM,mBAAmB,YAAY;AACjC,MAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,aAAA,EAAe;AAErC,MAAA,IAAI;AACA,QAAA,MAAM,aAAA,EAAc;AACpB,QAAA,aAAA,GAAgB,IAAA;AAChB,QAAA,YAAA,GAAe,KAAA;AAEf,QAAA,mBAAA,GAAsB,IAAA;AACtB,QAAA,mBAAA,GAAsB,IAAA;AACtB,QAAA,MAAA,CAAO,KAAA,CAAM,sBAAA,EAAwB,EAAE,cAAA,EAAgB,CAAA;AAAA,MAC3D,SAAS,KAAA,EAAO;AACZ,QAAA,MAAA,CAAO,MAAM,oDAAA,EAAsD;AAAA,UAC/D,cAAA;AAAA,UACA;AAAA,SACH,CAAA;AAAA,MACL;AAAA,IACJ,CAAA;AAGA,IAAA,MAAM,mBAAA,GAAsB,OAAO,OAAA,GAAU,GAAA,KAAwB;AACjE,MAAA,IAAI,YAAA,EAAc;AAElB,MAAA,IAAI,CAAC,mBAAA,EAAqB;AAEtB,QAAA,MAAM,IAAI,QAAc,CAAA,OAAA,KAAW;AAC/B,UAAA,MAAM,aAAA,GAAgB,YAAY,MAAM;AACpC,YAAA,IAAI,mBAAA,EAAqB;AACrB,cAAA,aAAA,CAAc,aAAa,CAAA;AAC3B,cAAA,OAAA,EAAQ;AAAA,YACZ;AAAA,UACJ,GAAG,EAAE,CAAA;AACL,UAAA,UAAA,CAAW,MAAM;AACb,YAAA,aAAA,CAAc,aAAa,CAAA;AAC3B,YAAA,OAAA,EAAQ;AAAA,UACZ,GAAG,OAAO,CAAA;AAAA,QACd,CAAC,CAAA;AAAA,MACL;AAEA,MAAA,IAAI,mBAAA,EAAqB;AACrB,QAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,UACf,mBAAA;AAAA,UACA,IAAI,OAAA;AAAA,YAAc,CAAC,CAAA,EAAG,MAAA,KAClB,UAAA,CAAW,MAAM,MAAA,CAAO,IAAI,KAAA,CAAM,sBAAsB,CAAC,CAAA,EAAG,OAAO;AAAA;AACvE,SACH,CAAA;AAAA,MACL;AAAA,IACJ,CAAA;AAGA,IAAA,UAAA,CAAW,EAAA;AAAA,MACP,oBAAA;AAAA,MACA,CAAC,KAAA,KAAwE;AACrE,QAAA,MAAM,WAAW,KAAA,CAAM,eAAA;AACvB,QAAA,MAAM,gBAAgB,KAAA,CAAM,uBAAA;AAE5B,QAAA,IAAI,QAAA,GAAW,CAAA,IAAK,aAAA,KAAkB,CAAA,EAAG;AAErC,UAAA,iBAAA,EAAkB,CAAE,MAAM,MAAM;AAAA,UAAC,CAAC,CAAA;AAAA,QACtC,CAAA,MAAA,IAAW,QAAA,KAAa,CAAA,IAAK,aAAA,GAAgB,CAAA,EAAG;AAE5C,UAAA,gBAAA,EAAiB,CAAE,MAAM,MAAM;AAAA,UAAC,CAAC,CAAA;AAAA,QACrC;AAAA,MACJ;AAAA,KACJ;AAGA,IAAA,MAAA,CAAO,OAAO,UAAA,EAAY;AAAA,MACtB,cAAA;AAAA,MACA,mBAAA;AAAA,MACA,cAAc,MAAM;AAAA,KACvB,CAAA;AAED,IAAA,OAAO,UAAA;AAAA,EACX,CAAA;AACJ;;;AC9bO,SAAS,WAAA,GAAsB;AAClC,EAAA,MAAM,KAAA,GAAQ,sCAAA;AACd,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AACzB,IAAA,MAAA,IAAU,KAAA,CAAM,OAAO,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,KAAA,CAAM,MAAM,CAAC,CAAA;AAAA,EACnE;AACA,EAAA,OAAO,MAAA;AACX","file":"chunk-H4JHOAUH.js","sourcesContent":["/**\n * Logger interface for subscription events and internal operations.\n * Users can provide their own implementation to integrate with external logging services.\n */\nexport interface Logger {\n /**\n * Log debug-level messages (typically only shown in development).\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n debug: (msg: string, context?: object) => void\n\n /**\n * Log info-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n info: (msg: string, context?: object) => void\n\n /**\n * Log warning-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n warn: (msg: string, context?: object) => void\n\n /**\n * Log error-level messages.\n * @param msg - The message to log\n * @param context - Optional context object with additional information\n */\n error: (msg: string, context?: object) => void\n}\n\n/**\n * Default console-based logger implementation.\n * Only logs debug messages in development mode.\n */\nconst defaultLogger: Logger = {\n debug: (msg: string, context?: object) => {\n if (process.env.NODE_ENV === 'development') {\n console.debug(`[pbtsdb] ${msg}`, context || '')\n }\n },\n info: (msg: string, context?: object) => {\n console.info(`[pbtsdb] ${msg}`, context || '')\n },\n warn: (msg: string, context?: object) => {\n console.warn(`[pbtsdb] ${msg}`, context || '')\n },\n error: (msg: string, context?: object) => {\n console.error(`[pbtsdb] ${msg}`, context || '')\n },\n}\n\n/**\n * Current logger instance (can be replaced by users).\n */\nlet currentLogger: Logger = defaultLogger\n\n/**\n * Internal logger instance used by the library.\n */\nexport const logger: Logger = {\n debug: (msg: string, context?: object) => currentLogger.debug(msg, context),\n info: (msg: string, context?: object) => currentLogger.info(msg, context),\n warn: (msg: string, context?: object) => currentLogger.warn(msg, context),\n error: (msg: string, context?: object) => currentLogger.error(msg, context),\n}\n\n/**\n * Set a custom logger implementation.\n * This allows users to integrate with their own logging services (e.g., Sentry, LogRocket, etc.).\n *\n * @param customLogger - The custom logger implementation\n *\n * @example\n * ```ts\n * import { setLogger } from 'pbtsdb';\n *\n * // Integration with a custom logging service\n * setLogger({\n * debug: (msg, context) => myLogger.debug(msg, context),\n * warn: (msg, context) => myLogger.warn(msg, context),\n * error: (msg, context) => {\n * myLogger.error(msg, context);\n * Sentry.captureMessage(msg, { level: 'error', extra: context });\n * },\n * });\n * ```\n *\n * @example\n * ```ts\n * // Disable all logging\n * setLogger({\n * debug: () => {},\n * warn: () => {},\n * error: () => {},\n * });\n * ```\n */\nexport function setLogger(customLogger: Logger): void {\n currentLogger = customLogger\n}\n\n/**\n * Reset the logger to the default implementation.\n */\nexport function resetLogger(): void {\n currentLogger = defaultLogger\n}\n","import type { IR } from '@tanstack/db'\nimport {\n type FieldPath,\n type ParsedOrderBy,\n parseOrderByExpression,\n parseWhereExpression,\n} from '@tanstack/db'\n\ntype BasicExpression<T = unknown> = IR.BasicExpression<T>\n\nfunction escapeValue(value: unknown): string {\n if (value === null) {\n return 'null'\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false'\n }\n if (typeof value === 'number') {\n return value.toString()\n }\n if (typeof value === 'string') {\n return `\"${value.replace(/\"/g, '\\\\\"')}\"`\n }\n if (value instanceof Date) {\n return `\"${value.toISOString()}\"`\n }\n if (Array.isArray(value)) {\n return `[${value.map(escapeValue).join(',')}]`\n }\n return `\"${String(value)}\"`\n}\n\nfunction fieldPathToString(path: FieldPath): string {\n return path.join('.')\n}\n\nexport function convertToPocketBaseFilter(\n where: BasicExpression<boolean> | undefined | null\n): string | undefined {\n if (!where) {\n return undefined\n }\n\n const result = parseWhereExpression(where, {\n handlers: {\n eq: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} = ${escapeValue(value)}`\n },\n gt: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} > ${escapeValue(value)}`\n },\n gte: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} >= ${escapeValue(value)}`\n },\n lt: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} < ${escapeValue(value)}`\n },\n lte: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} <= ${escapeValue(value)}`\n },\n and: (...conditions: string[]) => {\n if (conditions.length === 0) return ''\n if (conditions.length === 1) return conditions[0]\n return `(${conditions.join(' && ')})`\n },\n or: (...conditions: string[]) => {\n if (conditions.length === 0) return ''\n if (conditions.length === 1) return conditions[0]\n return `(${conditions.join(' || ')})`\n },\n not: (condition: string) => {\n return `!(${condition})`\n },\n in: (field: FieldPath, values: unknown) => {\n const valueArray = Array.isArray(values) ? values : [values]\n const uniqueValues = [...new Set(valueArray)]\n const fieldStr = fieldPathToString(field)\n const conditions = uniqueValues.map(v => `${fieldStr} = ${escapeValue(v)}`)\n return conditions.length > 1 ? `(${conditions.join(' || ')})` : conditions[0]\n },\n like: (field: FieldPath, value: unknown) => {\n return `${fieldPathToString(field)} ~ ${escapeValue(value)}`\n },\n isNull: (field: FieldPath) => {\n return `${fieldPathToString(field)} = null`\n },\n isUndefined: (field: FieldPath) => {\n return `${fieldPathToString(field)} = null`\n },\n },\n onUnknownOperator: (operator: string, _args: unknown[]) => {\n throw new Error(\n `Unsupported operator '${operator}' for PocketBase filter conversion. ` +\n `Supported operators: eq, gt, gte, lt, lte, in, like, and, or, not, isNull, isUndefined`\n )\n },\n })\n\n return result || undefined\n}\n\nexport function convertToPocketBaseSort(\n orderBy: IR.OrderBy | undefined | null\n): string | undefined {\n if (!orderBy) {\n return undefined\n }\n\n const sorts = parseOrderByExpression(orderBy)\n\n if (sorts.length === 0) {\n return undefined\n }\n\n return sorts\n .map((sort: ParsedOrderBy) => {\n const field = fieldPathToString(sort.field)\n return sort.direction === 'desc' ? `-${field}` : field\n })\n .join(',')\n}\n","import {\n type Collection,\n createCollection as createTanStackCollection,\n type LoadSubsetOptions,\n} from '@tanstack/db'\nimport {\n DeleteOperationItemNotFoundError,\n type QueryCollectionUtils,\n queryCollectionOptions,\n} from '@tanstack/query-db-collection'\nimport type { QueryClient } from '@tanstack/react-query'\nimport type PocketBase from 'pocketbase'\nimport type { RecordSubscription } from 'pocketbase'\nimport { logger } from './logger'\nimport { convertToPocketBaseFilter, convertToPocketBaseSort } from './pocketbase-query-converter'\nimport type {\n CreateCollectionOptions,\n ExpandTargetCollection,\n ExtractRecordType,\n SchemaDeclaration,\n} from './types'\n\nexport type { BaseRecord, CreateCollectionOptions, SchemaDeclaration } from './types'\n\n/**\n * Extended LoadSubsetOptions that includes PocketBase-specific expand parameter.\n * @internal\n */\ntype ExtendedLoadSubsetOptions = LoadSubsetOptions & {\n pbExpand?: string\n}\n\n/**\n * Compute the record type with expand property when expand option is configured.\n * @internal\n */\ntype WithExpandFromConfig<\n Schema extends SchemaDeclaration,\n C extends keyof Schema,\n Opts,\n> = Opts extends {\n expand: infer E\n}\n ? ExtractRecordType<Schema, C> & {\n expand?: {\n [K in keyof E]: K extends keyof import('./types').ExtractRelations<Schema, C>\n ? import('./types').ExtractRelations<Schema, C>[K] extends Array<infer U>\n ? U[]\n : import('./types').ExtractRelations<Schema, C>[K]\n : never\n }\n }\n : ExtractRecordType<Schema, C>\n\n/**\n * Subscription helpers added to collection instances.\n * @internal\n */\ninterface CollectionSubscriptionHelpers {\n /** The PocketBase collection name */\n collectionName: string\n /** Wait for subscription to be established (useful in tests) */\n waitForSubscription: (timeout?: number) => Promise<void>\n /** Check if collection has an active subscription */\n isSubscribed: () => boolean\n}\n\n/**\n * Inferred collection type from config options.\n * @internal\n */\ntype InferCollectionType<\n Schema extends SchemaDeclaration,\n C extends keyof Schema,\n Opts extends CreateCollectionOptions<Schema, C>,\n> = Collection<\n WithExpandFromConfig<Schema, C, Opts>,\n string | number,\n // TUtils - QueryCollectionUtils from TanStack Query DB Collection\n QueryCollectionUtils<\n WithExpandFromConfig<Schema, C, Opts>,\n string | number,\n WithExpandFromConfig<Schema, C, Opts>\n >,\n // TSchema - we don't use StandardSchema validation\n never,\n Opts extends {\n omitOnInsert: infer O extends readonly import('./types').OmittableFields<\n ExtractRecordType<Schema, C>\n >[]\n }\n ? import('./types').ComputeInsertType<ExtractRecordType<Schema, C>, O>\n : ExtractRecordType<Schema, C>\n> &\n CollectionSubscriptionHelpers\n\n/**\n * Creates a type-safe TanStack DB collection backed by PocketBase.\n * Use this when you need fine-grained control or need to create collections with dependencies.\n *\n * @param pb - PocketBase client instance\n * @param queryClient - TanStack Query client\n * @returns A curried function that takes collection name and options\n *\n * @example\n * Basic usage:\n * ```ts\n * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {});\n *\n * // Use directly\n * const books = await booksCollection.getFullList();\n * ```\n *\n * @example\n * With auto-expand relations:\n * ```ts\n * const authorsCollection = createCollection<Schema>(pb, queryClient)('authors', {});\n * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {\n * expand: {\n * author: authorsCollection // Always expand, auto-upsert into authorsCollection\n * }\n * });\n *\n * // Expand is automatic - no .expand() call needed\n * const { data } = useLiveQuery((q) => q.from({ books: booksCollection }));\n * // data[0].expand.author is typed and populated\n * ```\n */\nexport function createCollection<Schema extends SchemaDeclaration>(\n pb: PocketBase,\n queryClient: QueryClient\n) {\n return <\n C extends keyof Schema & string,\n Opts extends CreateCollectionOptions<Schema, C> = CreateCollectionOptions<Schema, C>,\n >(\n collectionName: C,\n options?: Opts\n ): InferCollectionType<Schema, C, Opts> => {\n type RecordType = ExtractRecordType<Schema, C>\n const expandStores = options?.expand as Record<string, ExpandTargetCollection> | undefined\n const expandString = expandStores ? Object.keys(expandStores).sort().join(',') : undefined\n\n const ignoreAutoCancellation = options?.ignoreAutoCancellation ?? true\n const refetchOnMutation = options?.refetchOnMutation ?? false\n\n async function upsertExpandedRelation(\n key: string,\n value: object | object[],\n stores: Record<string, ExpandTargetCollection>\n ): Promise<void> {\n const targetStore = stores[key]\n if (!targetStore.utils) return\n if (!targetStore.isReady()) {\n if (targetStore.config?.syncMode === 'on-demand') {\n await targetStore._sync.startSync()\n } else {\n logger.warn(\n `not syncing ${key} on ${collectionName} because store is not yet ready`\n )\n return\n }\n }\n const values = Array.isArray(value) ? value : [value]\n targetStore.utils.writeUpsert(values)\n }\n\n async function upsertExpandedRelations(items: RecordType[]): Promise<void> {\n if (!expandStores) return\n for (const record of items) {\n const expandData = (\n record as RecordType & { expand?: Record<string, object | object[]> }\n ).expand\n if (!expandData) continue\n for (const [key, value] of Object.entries(expandData)) {\n await upsertExpandedRelation(key, value, expandStores)\n }\n }\n }\n\n async function fetchItems(loadOptions?: ExtendedLoadSubsetOptions): Promise<RecordType[]> {\n const filter = convertToPocketBaseFilter(loadOptions?.where)\n const sort = convertToPocketBaseSort(loadOptions?.orderBy)\n const limit = loadOptions?.limit\n\n if (limit) {\n // Use getList when limit is specified to avoid fetching all records\n const result = await pb.collection(collectionName).getList(1, limit, {\n filter,\n sort,\n skipTotal: true, // Optimize by skipping total count\n expand: expandString,\n })\n return result.items as unknown as RecordType[]\n }\n // Use getFullList to fetch all records with automatic pagination\n return (await pb.collection(collectionName).getFullList({\n filter,\n sort,\n expand: expandString,\n })) as unknown as RecordType[]\n }\n\n async function fetchRecords(\n loadOptions?: ExtendedLoadSubsetOptions\n ): Promise<RecordType[]> {\n let items: RecordType[]\n try {\n items = await fetchItems(loadOptions)\n } catch (error) {\n if (\n ignoreAutoCancellation &&\n error instanceof Error &&\n error.message.includes('autocancelled')\n ) {\n return queryClient.getQueryData<RecordType[]>([collectionName]) ?? []\n }\n throw error\n }\n\n await upsertExpandedRelations(items)\n\n return items\n }\n\n const collectionOptions = queryCollectionOptions({\n ...options?.collectionOptions,\n queryClient,\n queryKey: [collectionName],\n syncMode: options?.syncMode ?? 'eager',\n queryFn: async (ctx): Promise<RecordType[]> => {\n return fetchRecords(\n ctx.meta?.loadSubsetOptions as ExtendedLoadSubsetOptions | undefined\n )\n },\n getKey: (item: RecordType) => {\n const record = item as unknown as Record<string, unknown>\n if (!record || typeof record !== 'object' || !('id' in record)) {\n throw new Error(\n `Record in collection '${collectionName}' is missing required 'id' field. Received: ${JSON.stringify(item)}`\n )\n }\n return record.id as string\n },\n onInsert:\n options?.onInsert === false\n ? undefined\n : (options?.onInsert ??\n (async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map(async mutation => {\n const {\n created,\n updated,\n collectionId,\n collectionName: _,\n ...data\n } = mutation.modified as unknown as Record<string, unknown>\n await pb.collection(collectionName).create(data)\n })\n )\n return { refetch: refetchOnMutation }\n })),\n onUpdate:\n options?.onUpdate === false\n ? undefined\n : (options?.onUpdate ??\n (async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map(async mutation => {\n const recordWithId = mutation.original as { id: string }\n await pb\n .collection(collectionName)\n .update(recordWithId.id, mutation.changes)\n })\n )\n return { refetch: refetchOnMutation }\n })),\n onDelete:\n options?.onDelete === false\n ? undefined\n : (options?.onDelete ??\n (async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map(async mutation => {\n const recordWithId = mutation.original as { id: string }\n await pb.collection(collectionName).delete(recordWithId.id)\n })\n )\n return { refetch: refetchOnMutation }\n })),\n })\n\n const collection = createTanStackCollection(collectionOptions)\n\n // Real-time subscription state\n let unsubscribeFn: (() => Promise<void>) | null = null\n let isSubscribed = false\n let subscriptionPromise: Promise<void> | null = null\n let subscriptionResolve: (() => void) | null = null\n\n // Handle real-time events from PocketBase.\n //\n // The write primitives differ in how they treat a key that is absent from\n // the *synced* store (collection._state.syncedData, which is what they\n // validate against — not the optimistic view exposed by collection.has()):\n // - writeInsert / writeUpsert: idempotent, never throw on an absent key.\n // - writeDelete: throws DeleteOperationItemNotFoundError on an absent key.\n // So only the delete branch can throw, and we make it idempotent below.\n const handleRealtimeEvent = (event: RecordSubscription<RecordType>) => {\n if (!collection.utils) return\n\n try {\n collection.utils.writeBatch(() => {\n switch (event.action) {\n case 'create':\n collection.utils.writeInsert(event.record)\n break\n case 'update':\n collection.utils.writeUpsert(event.record)\n break\n case 'delete':\n if (event.record && 'id' in event.record) {\n // Throws DeleteOperationItemNotFoundError if the key\n // is no longer in the synced store (see catch below).\n collection.utils.writeDelete((event.record as { id: string }).id)\n }\n break\n }\n })\n } catch (error) {\n // How a delete echo throws: writeDelete fails when its key is already\n // gone from the synced store. That happens when something removed it\n // before the echo arrived:\n // 1. on-demand sync — each useLiveQuery refetches with a server\n // filter, and query-db-collection prunes rows no longer owned by\n // any active query out of the synced store. If that prune (or a\n // concurrent query's reconcile) runs before this client's own\n // delete echo lands, the key is already gone -> throw. This is\n // the on-demand-only race; eager collections have no such second\n // writer to the synced store, so they cannot hit it.\n // 2. a re-delivered SSE delete (e.g. after a reconnect) for a key\n // that was already deleted -> throw on the second echo.\n // In both cases the record is already in its intended end state\n // (gone), so the echo is a no-op and the error is safe to ignore.\n // Anything that is NOT a missing-key delete is a real error: rethrow.\n if (error instanceof DeleteOperationItemNotFoundError) {\n logger.debug('Ignoring delete echo for already-removed record', {\n collectionName,\n id: (event.record as { id?: string } | undefined)?.id,\n })\n } else {\n throw error\n }\n }\n }\n\n // Start PocketBase real-time subscription\n const startSubscription = async () => {\n if (isSubscribed) return\n\n // Create promise before starting so waiters can await it\n if (!subscriptionPromise) {\n subscriptionPromise = new Promise<void>(resolve => {\n subscriptionResolve = resolve\n })\n }\n\n try {\n unsubscribeFn = await pb\n .collection(collectionName)\n .subscribe('*', handleRealtimeEvent)\n isSubscribed = true\n logger.debug('Subscription started', { collectionName })\n // Resolve the promise to notify waiters\n if (subscriptionResolve) {\n subscriptionResolve()\n }\n } catch (error) {\n logger.error('Failed to start subscription', { collectionName, error })\n }\n }\n\n // Stop PocketBase real-time subscription\n const stopSubscription = async () => {\n if (!isSubscribed || !unsubscribeFn) return\n\n try {\n await unsubscribeFn()\n unsubscribeFn = null\n isSubscribed = false\n // Reset promise for next subscription cycle\n subscriptionPromise = null\n subscriptionResolve = null\n logger.debug('Subscription stopped', { collectionName })\n } catch (error) {\n logger.debug('Unsubscribe failed (expected if connection closed)', {\n collectionName,\n error,\n })\n }\n }\n\n // Wait for subscription to be established (for testing)\n const waitForSubscription = async (timeout = 5000): Promise<void> => {\n if (isSubscribed) return\n\n if (!subscriptionPromise) {\n // No subscription in progress, wait for one to start\n await new Promise<void>(resolve => {\n const checkInterval = setInterval(() => {\n if (subscriptionPromise) {\n clearInterval(checkInterval)\n resolve()\n }\n }, 10)\n setTimeout(() => {\n clearInterval(checkInterval)\n resolve()\n }, timeout)\n })\n }\n\n if (subscriptionPromise) {\n await Promise.race([\n subscriptionPromise,\n new Promise<void>((_, reject) =>\n setTimeout(() => reject(new Error('Subscription timeout')), timeout)\n ),\n ])\n }\n }\n\n // Manage subscription based on collection subscriber count\n collection.on(\n 'subscribers:change',\n (event: { subscriberCount: number; previousSubscriberCount: number }) => {\n const newCount = event.subscriberCount\n const previousCount = event.previousSubscriberCount\n\n if (newCount > 0 && previousCount === 0) {\n // First subscriber - start real-time subscription\n startSubscription().catch(() => {})\n } else if (newCount === 0 && previousCount > 0) {\n // Last subscriber removed - stop real-time subscription\n stopSubscription().catch(() => {})\n }\n }\n )\n\n // Add collectionName and subscription helpers\n Object.assign(collection, {\n collectionName,\n waitForSubscription,\n isSubscribed: () => isSubscribed,\n })\n\n return collection as unknown as InferCollectionType<Schema, C, Opts>\n }\n}\n","/**\n * Generates a new PocketBase-compatible record ID.\n * Returns a 15-character alphanumeric string (lowercase letters and numbers).\n *\n * PocketBase uses 15-character IDs for records, formatted as lowercase alphanumeric.\n *\n * @returns A 15-character alphanumeric string suitable for use as a PocketBase record ID\n *\n * @example\n * ```ts\n * const id = newRecordId(); // \"a1b2c3d4e5f6g7h\"\n * ```\n */\nexport function newRecordId(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'\n let result = ''\n for (let i = 0; i < 15; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length))\n }\n return result\n}\n"]}