pbtsdb 0.5.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.
@@ -1,8 +1,39 @@
1
1
  import { createCollection as createCollection$1, parseWhereExpression, parseOrderByExpression } from '@tanstack/db';
2
2
  export { BTreeIndex, BasicIndex, ReverseIndex, createEffect, toArray } from '@tanstack/db';
3
- import { queryCollectionOptions } from '@tanstack/query-db-collection';
3
+ import { queryCollectionOptions, DeleteOperationItemNotFoundError } from '@tanstack/query-db-collection';
4
4
 
5
- // src/collection.ts
5
+ // src/core.ts
6
+
7
+ // src/logger.ts
8
+ var defaultLogger = {
9
+ debug: (msg, context) => {
10
+ if (process.env.NODE_ENV === "development") {
11
+ console.debug(`[pbtsdb] ${msg}`, context || "");
12
+ }
13
+ },
14
+ info: (msg, context) => {
15
+ console.info(`[pbtsdb] ${msg}`, context || "");
16
+ },
17
+ warn: (msg, context) => {
18
+ console.warn(`[pbtsdb] ${msg}`, context || "");
19
+ },
20
+ error: (msg, context) => {
21
+ console.error(`[pbtsdb] ${msg}`, context || "");
22
+ }
23
+ };
24
+ var currentLogger = defaultLogger;
25
+ var logger = {
26
+ debug: (msg, context) => currentLogger.debug(msg, context),
27
+ info: (msg, context) => currentLogger.info(msg, context),
28
+ warn: (msg, context) => currentLogger.warn(msg, context),
29
+ error: (msg, context) => currentLogger.error(msg, context)
30
+ };
31
+ function setLogger(customLogger) {
32
+ currentLogger = customLogger;
33
+ }
34
+ function resetLogger() {
35
+ currentLogger = defaultLogger;
36
+ }
6
37
  function escapeValue(value) {
7
38
  if (value === null) {
8
39
  return "null";
@@ -78,7 +109,7 @@ function convertToPocketBaseFilter(where) {
78
109
  return `${fieldPathToString(field)} = null`;
79
110
  }
80
111
  },
81
- onUnknownOperator: (operator, args) => {
112
+ onUnknownOperator: (operator, _args) => {
82
113
  throw new Error(
83
114
  `Unsupported operator '${operator}' for PocketBase filter conversion. Supported operators: eq, gt, gte, lt, lte, in, like, and, or, not, isNull, isUndefined`
84
115
  );
@@ -100,37 +131,6 @@ function convertToPocketBaseSort(orderBy) {
100
131
  }).join(",");
101
132
  }
102
133
 
103
- // src/logger.ts
104
- var defaultLogger = {
105
- debug: (msg, context) => {
106
- if (process.env.NODE_ENV === "development") {
107
- console.log(`[pbtsdb] ${msg}`, context || "");
108
- }
109
- },
110
- info: (msg, context) => {
111
- console.info(`[pbtsdb] ${msg}`, context || "");
112
- },
113
- warn: (msg, context) => {
114
- console.warn(`[pbtsdb] ${msg}`, context || "");
115
- },
116
- error: (msg, context) => {
117
- console.error(`[pbtsdb] ${msg}`, context || "");
118
- }
119
- };
120
- var currentLogger = defaultLogger;
121
- var logger = {
122
- debug: (msg, context) => currentLogger.debug(msg, context),
123
- info: (msg, context) => currentLogger.info(msg, context),
124
- warn: (msg, context) => currentLogger.warn(msg, context),
125
- error: (msg, context) => currentLogger.error(msg, context)
126
- };
127
- function setLogger(customLogger) {
128
- currentLogger = customLogger;
129
- }
130
- function resetLogger() {
131
- currentLogger = defaultLogger;
132
- }
133
-
134
134
  // src/collection.ts
135
135
  function createCollection(pb, queryClient) {
136
136
  return (collectionName, options) => {
@@ -138,54 +138,63 @@ function createCollection(pb, queryClient) {
138
138
  const expandString = expandStores ? Object.keys(expandStores).sort().join(",") : void 0;
139
139
  const ignoreAutoCancellation = options?.ignoreAutoCancellation ?? true;
140
140
  const refetchOnMutation = options?.refetchOnMutation ?? false;
141
- async function fetchRecords(loadOptions) {
141
+ async function upsertExpandedRelation(key, value, stores) {
142
+ const targetStore = stores[key];
143
+ if (!targetStore.utils) return;
144
+ if (!targetStore.isReady()) {
145
+ if (targetStore.config?.syncMode === "on-demand") {
146
+ await targetStore._sync.startSync();
147
+ } else {
148
+ logger.warn(
149
+ `not syncing ${key} on ${collectionName} because store is not yet ready`
150
+ );
151
+ return;
152
+ }
153
+ }
154
+ const values = Array.isArray(value) ? value : [value];
155
+ targetStore.utils.writeUpsert(values);
156
+ }
157
+ async function upsertExpandedRelations(items) {
158
+ if (!expandStores) return;
159
+ for (const record of items) {
160
+ const expandData = record.expand;
161
+ if (!expandData) continue;
162
+ for (const [key, value] of Object.entries(expandData)) {
163
+ await upsertExpandedRelation(key, value, expandStores);
164
+ }
165
+ }
166
+ }
167
+ async function fetchItems(loadOptions) {
142
168
  const filter = convertToPocketBaseFilter(loadOptions?.where);
143
169
  const sort = convertToPocketBaseSort(loadOptions?.orderBy);
144
170
  const limit = loadOptions?.limit;
171
+ if (limit) {
172
+ const result = await pb.collection(collectionName).getList(1, limit, {
173
+ filter,
174
+ sort,
175
+ skipTotal: true,
176
+ // Optimize by skipping total count
177
+ expand: expandString
178
+ });
179
+ return result.items;
180
+ }
181
+ return await pb.collection(collectionName).getFullList({
182
+ filter,
183
+ sort,
184
+ expand: expandString
185
+ });
186
+ }
187
+ async function fetchRecords(loadOptions) {
145
188
  let items;
146
189
  try {
147
- if (limit) {
148
- const result = await pb.collection(collectionName).getList(1, limit, {
149
- filter,
150
- sort,
151
- skipTotal: true,
152
- // Optimize by skipping total count
153
- expand: expandString
154
- });
155
- items = result.items;
156
- } else {
157
- items = await pb.collection(collectionName).getFullList({
158
- filter,
159
- sort,
160
- expand: expandString
161
- });
162
- }
190
+ items = await fetchItems(loadOptions);
163
191
  } catch (error) {
164
192
  if (ignoreAutoCancellation && error instanceof Error && error.message.includes("autocancelled")) {
165
193
  return queryClient.getQueryData([collectionName]) ?? [];
166
194
  }
167
195
  throw error;
168
196
  }
169
- if (expandStores) {
170
- for (const record of items) {
171
- const expandData = record.expand;
172
- if (!expandData) continue;
173
- for (const [key, value] of Object.entries(expandData)) {
174
- const targetStore = expandStores[key];
175
- if (!targetStore.utils) continue;
176
- if (!targetStore.isReady()) {
177
- if (targetStore.config?.syncMode === "on-demand") {
178
- await targetStore._sync.startSync();
179
- } else {
180
- logger.warn(`not syncing ${key} on ${collectionName} because store is not yet ready`);
181
- continue;
182
- }
183
- }
184
- const values = Array.isArray(value) ? value : [value];
185
- targetStore.utils.writeUpsert(values);
186
- }
187
- }
188
- }
197
+ await upsertExpandedRelations(items);
189
198
  return items;
190
199
  }
191
200
  const collectionOptions = queryCollectionOptions({
@@ -194,7 +203,9 @@ function createCollection(pb, queryClient) {
194
203
  queryKey: [collectionName],
195
204
  syncMode: options?.syncMode ?? "eager",
196
205
  queryFn: async (ctx) => {
197
- return fetchRecords(ctx.meta?.loadSubsetOptions);
206
+ return fetchRecords(
207
+ ctx.meta?.loadSubsetOptions
208
+ );
198
209
  },
199
210
  getKey: (item) => {
200
211
  const record = item;
@@ -206,27 +217,29 @@ function createCollection(pb, queryClient) {
206
217
  return record.id;
207
218
  },
208
219
  onInsert: options?.onInsert === false ? void 0 : options?.onInsert ?? (async ({ transaction }) => {
209
- await Promise.all(
220
+ const created = await Promise.all(
210
221
  transaction.mutations.map(async (mutation) => {
211
222
  const {
212
- created,
213
- updated,
214
- collectionId,
215
- collectionName: _,
223
+ created: _created,
224
+ updated: _updated,
225
+ collectionId: _collectionId,
226
+ collectionName: _collectionName,
216
227
  ...data
217
228
  } = mutation.modified;
218
- await pb.collection(collectionName).create(data);
229
+ return pb.collection(collectionName).create(data);
219
230
  })
220
231
  );
232
+ writeServerRecords(created);
221
233
  return { refetch: refetchOnMutation };
222
234
  }),
223
235
  onUpdate: options?.onUpdate === false ? void 0 : options?.onUpdate ?? (async ({ transaction }) => {
224
- await Promise.all(
236
+ const updated = await Promise.all(
225
237
  transaction.mutations.map(async (mutation) => {
226
238
  const recordWithId = mutation.original;
227
- await pb.collection(collectionName).update(recordWithId.id, mutation.changes);
239
+ return pb.collection(collectionName).update(recordWithId.id, mutation.changes);
228
240
  })
229
241
  );
242
+ writeServerRecords(updated);
230
243
  return { refetch: refetchOnMutation };
231
244
  }),
232
245
  onDelete: options?.onDelete === false ? void 0 : options?.onDelete ?? (async ({ transaction }) => {
@@ -240,27 +253,68 @@ function createCollection(pb, queryClient) {
240
253
  })
241
254
  });
242
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
+ }
243
285
  let unsubscribeFn = null;
244
286
  let isSubscribed = false;
245
287
  let subscriptionPromise = null;
246
288
  let subscriptionResolve = null;
247
289
  const handleRealtimeEvent = (event) => {
248
290
  if (!collection.utils) return;
249
- collection.utils.writeBatch(() => {
250
- switch (event.action) {
251
- case "create":
252
- collection.utils.writeInsert(event.record);
253
- break;
254
- case "update":
255
- collection.utils.writeUpsert(event.record);
256
- break;
257
- case "delete":
258
- if (event.record && "id" in event.record) {
259
- collection.utils.writeDelete(event.record.id);
260
- }
261
- break;
291
+ if (isStaleEcho(event)) return;
292
+ try {
293
+ collection.utils.writeBatch(() => {
294
+ switch (event.action) {
295
+ case "create":
296
+ collection.utils.writeInsert(event.record);
297
+ break;
298
+ case "update":
299
+ collection.utils.writeUpsert(event.record);
300
+ break;
301
+ case "delete":
302
+ if (event.record && "id" in event.record) {
303
+ collection.utils.writeDelete(event.record.id);
304
+ }
305
+ break;
306
+ }
307
+ });
308
+ } catch (error) {
309
+ if (error instanceof DeleteOperationItemNotFoundError) {
310
+ logger.debug("Ignoring delete echo for already-removed record", {
311
+ collectionName,
312
+ id: event.record?.id
313
+ });
314
+ } else {
315
+ throw error;
262
316
  }
263
- });
317
+ }
264
318
  };
265
319
  const startSubscription = async () => {
266
320
  if (isSubscribed) return;
@@ -290,7 +344,10 @@ function createCollection(pb, queryClient) {
290
344
  subscriptionResolve = null;
291
345
  logger.debug("Subscription stopped", { collectionName });
292
346
  } catch (error) {
293
- logger.debug("Unsubscribe failed (expected if connection closed)", { collectionName, error });
347
+ logger.debug("Unsubscribe failed (expected if connection closed)", {
348
+ collectionName,
349
+ error
350
+ });
294
351
  }
295
352
  };
296
353
  const waitForSubscription = async (timeout = 5e3) => {
@@ -318,17 +375,20 @@ function createCollection(pb, queryClient) {
318
375
  ]);
319
376
  }
320
377
  };
321
- collection.on("subscribers:change", (event) => {
322
- const newCount = event.subscriberCount;
323
- const previousCount = event.previousSubscriberCount;
324
- if (newCount > 0 && previousCount === 0) {
325
- startSubscription().catch(() => {
326
- });
327
- } else if (newCount === 0 && previousCount > 0) {
328
- stopSubscription().catch(() => {
329
- });
378
+ collection.on(
379
+ "subscribers:change",
380
+ (event) => {
381
+ const newCount = event.subscriberCount;
382
+ const previousCount = event.previousSubscriberCount;
383
+ if (newCount > 0 && previousCount === 0) {
384
+ startSubscription().catch(() => {
385
+ });
386
+ } else if (newCount === 0 && previousCount > 0) {
387
+ stopSubscription().catch(() => {
388
+ });
389
+ }
330
390
  }
331
- });
391
+ );
332
392
  Object.assign(collection, {
333
393
  collectionName,
334
394
  waitForSubscription,
@@ -349,5 +409,5 @@ function newRecordId() {
349
409
  }
350
410
 
351
411
  export { createCollection, newRecordId, resetLogger, setLogger };
352
- //# sourceMappingURL=chunk-UYH4FKXY.js.map
353
- //# sourceMappingURL=chunk-UYH4FKXY.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
@@ -1,8 +1,8 @@
1
1
  import { Collection, InsertMutationFn, UpdateMutationFn, DeleteMutationFn, BaseCollectionConfig } from '@tanstack/db';
2
2
  export { BTreeIndex, BasicIndex, DeltaEvent, DeltaType, EffectConfig, EffectContext, IndexConstructor, ReverseIndex, createEffect, toArray } from '@tanstack/db';
3
- import PocketBase from 'pocketbase';
4
3
  import { QueryCollectionUtils } from '@tanstack/query-db-collection';
5
4
  import { QueryClient } from '@tanstack/react-query';
5
+ import PocketBase from 'pocketbase';
6
6
 
7
7
  /**
8
8
  * Base record type required by PocketBase collections.
@@ -105,7 +105,7 @@ type WithExpand<Schema extends SchemaDeclaration, CollectionName extends keyof S
105
105
  * ExcludeUndefined<Customer | undefined> => Customer
106
106
  * @internal
107
107
  */
108
- type ExcludeUndefined<T> = T extends (infer U) | undefined ? U : T;
108
+ type ExcludeUndefined<T> = T extends infer U | undefined ? U : T;
109
109
  /**
110
110
  * Converts a schema relation type to its corresponding Collection constraint.
111
111
  * Handles both single relations (T) and array relations (T[]).
@@ -115,8 +115,8 @@ type ExcludeUndefined<T> = T extends (infer U) | undefined ? U : T;
115
115
  * collections with different TInput types (from omitOnInsert) to be compatible.
116
116
  *
117
117
  * @example
118
- * RelationAsCollection<Customer> => Collection<Customer, string | number, any, any, any>
119
- * RelationAsCollection<Customer[]> => Collection<Customer, string | number, any, any, any>
118
+ * RelationAsCollection<Customer> => Collection<Customer, string | number, ...>
119
+ * RelationAsCollection<Customer[]> => Collection<Customer, string | number, ...>
120
120
  * @internal
121
121
  */
122
122
  type RelationAsCollection<T> = T extends Array<infer U> ? U extends object ? Collection<U, string | number, any, any, any> : Collection<object, string | number, any, any, any> : T extends object ? Collection<T, string | number, any, any, any> : Collection<object, string | number, any, any, any>;
@@ -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-UYH4FKXY.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.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { CreateCollectionOptions, ExcludeUndefined, ExtractRecordType, ExtractRelations, Logger, OmittableFields, ParseExpandFields, RelationAsCollection, SchemaDeclaration, WithExpand, createCollection, newRecordId, resetLogger, setLogger } from './core.js';
2
1
  export { BTreeIndex, BasicIndex, DeltaEvent, DeltaType, EffectConfig, EffectContext, IndexConstructor, ReverseIndex, createEffect, toArray } from '@tanstack/db';
2
+ export { CreateCollectionOptions, ExcludeUndefined, ExtractRecordType, ExtractRelations, Logger, OmittableFields, ParseExpandFields, RelationAsCollection, SchemaDeclaration, WithExpand, createCollection, newRecordId, resetLogger, setLogger } from './core.js';
3
3
  import React, { ReactNode } from 'react';
4
- import 'pocketbase';
5
4
  import '@tanstack/query-db-collection';
6
5
  import '@tanstack/react-query';
6
+ import 'pocketbase';
7
7
 
8
8
  /**
9
9
  * UseStore hook type for variadic collection access.
@@ -127,6 +127,6 @@ interface ReactProviderResult<CollectionsMap> {
127
127
  * }
128
128
  * ```
129
129
  */
130
- declare function createReactProvider<CollectionsMap extends Record<string, any>>(collections: CollectionsMap): ReactProviderResult<CollectionsMap>;
130
+ declare function createReactProvider<CollectionsMap extends Record<string, unknown>>(collections: CollectionsMap): ReactProviderResult<CollectionsMap>;
131
131
 
132
132
  export { type ReactProviderResult, createReactProvider };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { BTreeIndex, BasicIndex, ReverseIndex, createCollection, createEffect, newRecordId, resetLogger, setLogger, toArray } from './chunk-UYH4FKXY.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
 
@@ -8,7 +8,9 @@ function createReactProvider(collections) {
8
8
  function useStore(...keys) {
9
9
  const context = useContext(Context);
10
10
  if (!context) {
11
- throw new Error("useStore must be used within the Provider returned by createReactProvider");
11
+ throw new Error(
12
+ "useStore must be used within the Provider returned by createReactProvider"
13
+ );
12
14
  }
13
15
  return keys.map((key) => {
14
16
  if (!(key in context)) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":";;;;AA6HO,SAAS,oBACZ,WAAA,EACmC;AACnC,EAAA,MAAM,OAAA,GAAU,cAAqC,IAAI,CAAA;AAEzD,EAAA,MAAM,QAAA,GAA8C,CAAC,EAAE,QAAA,EAAS,qBAC5D,GAAA,CAAC,OAAA,CAAQ,QAAA,EAAR,EAAiB,KAAA,EAAO,WAAA,EAAc,QAAA,EAAS,CAAA;AAGpD,EAAA,SAAS,YACF,IAAA,EACiF;AACpF,IAAA,MAAM,OAAA,GAAU,WAAW,OAAO,CAAA;AAElC,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,MAAM,IAAI,MAAM,2EAA2E,CAAA;AAAA,IAC/F;AAEA,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACrB,MAAA,IAAI,EAAE,OAAO,OAAA,CAAA,EAAU;AACnB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,GAAG,CAAC,CAAA,0BAAA,CAA4B,CAAA;AAAA,MAC1E;AACA,MAAA,OAAO,QAAQ,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACL;AAEA,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA;AAAA,GACJ;AACJ","file":"index.js","sourcesContent":["import React, { createContext, useContext, type ReactNode } from 'react';\n\n/**\n * UseStore hook type for variadic collection access.\n * @internal\n */\ntype UseStoreFn<CollectionsMap> = <K extends (keyof CollectionsMap)[]>(\n ...keys: K\n) => { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never };\n\n/**\n * Return type for createReactProvider function.\n */\nexport interface ReactProviderResult<CollectionsMap> {\n /**\n * React Context Provider component.\n * Wrap your app with this provider to make collections available to useStore.\n */\n Provider: React.FC<{ children: ReactNode }>;\n\n /**\n * Hook to access collections from the provider.\n * Uses variadic arguments for clean syntax with automatic type inference.\n *\n * @example\n * ```tsx\n * // Single collection\n * const [books] = useStore('books');\n *\n * // Multiple collections (no 'as const' needed!)\n * const [books, authors] = useStore('books', 'authors');\n * ```\n */\n useStore: UseStoreFn<CollectionsMap>;\n}\n\n/**\n * Creates a React Provider and useStore hook from a collections map.\n *\n * @param collections - Map of collection keys to Collection instances\n * @returns Object containing Provider component and useStore hook\n *\n * @example\n * Basic usage:\n * ```tsx\n * import { createCollection, createReactProvider } from 'pbtsdb';\n * import { useLiveQuery } from '@tanstack/react-db';\n *\n * // Step 1: Create collections\n * const c = createCollection<Schema>(pb, queryClient);\n * const collections = {\n * books: c('books', {}),\n * authors: c('authors', {}),\n * };\n *\n * // Step 2: Wrap for React\n * const { Provider, useStore } = createReactProvider(collections);\n *\n * // Step 3: Wrap your app\n * function App() {\n * return (\n * <QueryClientProvider client={queryClient}>\n * <Provider>\n * <BooksList />\n * </Provider>\n * </QueryClientProvider>\n * );\n * }\n *\n * // Step 4: Use in components\n * function BooksList() {\n * const [books] = useStore('books');\n * const { data } = useLiveQuery((q) => q.from({ books }));\n * return <div>{data?.map(book => <p key={book.id}>{book.title}</p>)}</div>;\n * }\n * ```\n *\n * @example\n * Variadic useStore pattern:\n * ```tsx\n * function BooksWithAuthors() {\n * const [books, authors] = useStore('books', 'authors');\n *\n * const { data } = useLiveQuery((q) =>\n * q.from({ book: books })\n * .join(\n * { author: authors },\n * ({ book, author }) => eq(book.author, author.id),\n * 'left'\n * )\n * );\n *\n * return <div>...</div>;\n * }\n * ```\n *\n * @example\n * With auto-expand collections:\n * ```tsx\n * const c = createCollection<Schema>(pb, queryClient);\n * const authors = c('authors', {});\n * const books = c('books', {\n * expand: {\n * author: authors\n * }\n * });\n *\n * const { Provider, useStore } = createReactProvider({ authors, books });\n *\n * function BooksWithExpandedAuthors() {\n * const [books] = useStore('books');\n * const { data } = useLiveQuery((q) => q.from({ books }));\n *\n * return (\n * <ul>\n * {data?.map(book => (\n * <li key={book.id}>\n * {book.title} by {book.expand?.author?.name}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function createReactProvider<CollectionsMap extends Record<string, any>>(\n collections: CollectionsMap\n): ReactProviderResult<CollectionsMap> {\n const Context = createContext<CollectionsMap | null>(null);\n\n const Provider: React.FC<{ children: ReactNode }> = ({ children }) => (\n <Context.Provider value={collections}>{children}</Context.Provider>\n );\n\n function useStore<K extends (keyof CollectionsMap)[]>(\n ...keys: K\n ): { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never } {\n const context = useContext(Context);\n\n if (!context) {\n throw new Error('useStore must be used within the Provider returned by createReactProvider');\n }\n\n return keys.map((key) => {\n if (!(key in context)) {\n throw new Error(`Collection \"${String(key)}\" not found in collections`);\n }\n return context[key];\n }) as { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never };\n }\n\n return {\n Provider,\n useStore: useStore as UseStoreFn<CollectionsMap>,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":";;;;AA6HO,SAAS,oBACZ,WAAA,EACmC;AACnC,EAAA,MAAM,OAAA,GAAU,cAAqC,IAAI,CAAA;AAEzD,EAAA,MAAM,QAAA,GAA8C,CAAC,EAAE,QAAA,EAAS,qBAC5D,GAAA,CAAC,OAAA,CAAQ,QAAA,EAAR,EAAiB,KAAA,EAAO,WAAA,EAAc,QAAA,EAAS,CAAA;AAGpD,EAAA,SAAS,YACF,IAAA,EACiF;AACpF,IAAA,MAAM,OAAA,GAAU,WAAW,OAAO,CAAA;AAElC,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AAEA,IAAA,OAAO,IAAA,CAAK,IAAI,CAAA,GAAA,KAAO;AACnB,MAAA,IAAI,EAAE,OAAO,OAAA,CAAA,EAAU;AACnB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,GAAG,CAAC,CAAA,0BAAA,CAA4B,CAAA;AAAA,MAC1E;AACA,MAAA,OAAO,QAAQ,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACL;AAEA,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA;AAAA,GACJ;AACJ","file":"index.js","sourcesContent":["import React, { createContext, type ReactNode, useContext } from 'react'\n\n/**\n * UseStore hook type for variadic collection access.\n * @internal\n */\ntype UseStoreFn<CollectionsMap> = <K extends (keyof CollectionsMap)[]>(\n ...keys: K\n) => { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never }\n\n/**\n * Return type for createReactProvider function.\n */\nexport interface ReactProviderResult<CollectionsMap> {\n /**\n * React Context Provider component.\n * Wrap your app with this provider to make collections available to useStore.\n */\n Provider: React.FC<{ children: ReactNode }>\n\n /**\n * Hook to access collections from the provider.\n * Uses variadic arguments for clean syntax with automatic type inference.\n *\n * @example\n * ```tsx\n * // Single collection\n * const [books] = useStore('books');\n *\n * // Multiple collections (no 'as const' needed!)\n * const [books, authors] = useStore('books', 'authors');\n * ```\n */\n useStore: UseStoreFn<CollectionsMap>\n}\n\n/**\n * Creates a React Provider and useStore hook from a collections map.\n *\n * @param collections - Map of collection keys to Collection instances\n * @returns Object containing Provider component and useStore hook\n *\n * @example\n * Basic usage:\n * ```tsx\n * import { createCollection, createReactProvider } from 'pbtsdb';\n * import { useLiveQuery } from '@tanstack/react-db';\n *\n * // Step 1: Create collections\n * const c = createCollection<Schema>(pb, queryClient);\n * const collections = {\n * books: c('books', {}),\n * authors: c('authors', {}),\n * };\n *\n * // Step 2: Wrap for React\n * const { Provider, useStore } = createReactProvider(collections);\n *\n * // Step 3: Wrap your app\n * function App() {\n * return (\n * <QueryClientProvider client={queryClient}>\n * <Provider>\n * <BooksList />\n * </Provider>\n * </QueryClientProvider>\n * );\n * }\n *\n * // Step 4: Use in components\n * function BooksList() {\n * const [books] = useStore('books');\n * const { data } = useLiveQuery((q) => q.from({ books }));\n * return <div>{data?.map(book => <p key={book.id}>{book.title}</p>)}</div>;\n * }\n * ```\n *\n * @example\n * Variadic useStore pattern:\n * ```tsx\n * function BooksWithAuthors() {\n * const [books, authors] = useStore('books', 'authors');\n *\n * const { data } = useLiveQuery((q) =>\n * q.from({ book: books })\n * .join(\n * { author: authors },\n * ({ book, author }) => eq(book.author, author.id),\n * 'left'\n * )\n * );\n *\n * return <div>...</div>;\n * }\n * ```\n *\n * @example\n * With auto-expand collections:\n * ```tsx\n * const c = createCollection<Schema>(pb, queryClient);\n * const authors = c('authors', {});\n * const books = c('books', {\n * expand: {\n * author: authors\n * }\n * });\n *\n * const { Provider, useStore } = createReactProvider({ authors, books });\n *\n * function BooksWithExpandedAuthors() {\n * const [books] = useStore('books');\n * const { data } = useLiveQuery((q) => q.from({ books }));\n *\n * return (\n * <ul>\n * {data?.map(book => (\n * <li key={book.id}>\n * {book.title} by {book.expand?.author?.name}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function createReactProvider<CollectionsMap extends Record<string, unknown>>(\n collections: CollectionsMap\n): ReactProviderResult<CollectionsMap> {\n const Context = createContext<CollectionsMap | null>(null)\n\n const Provider: React.FC<{ children: ReactNode }> = ({ children }) => (\n <Context.Provider value={collections}>{children}</Context.Provider>\n )\n\n function useStore<K extends (keyof CollectionsMap)[]>(\n ...keys: K\n ): { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never } {\n const context = useContext(Context)\n\n if (!context) {\n throw new Error(\n 'useStore must be used within the Provider returned by createReactProvider'\n )\n }\n\n return keys.map(key => {\n if (!(key in context)) {\n throw new Error(`Collection \"${String(key)}\" not found in collections`)\n }\n return context[key]\n }) as { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never }\n }\n\n return {\n Provider,\n useStore: useStore as UseStoreFn<CollectionsMap>,\n }\n}\n"]}
package/package.json CHANGED
@@ -1,99 +1,102 @@
1
1
  {
2
- "name": "pbtsdb",
3
- "version": "0.5.0",
4
- "description": "Type-safe PocketBase integration with TanStack Query and TanStack DB",
5
- "keywords": [
6
- "pocketbase",
7
- "tanstack",
8
- "tanstack-query",
9
- "tanstack-db",
10
- "database",
11
- "react",
12
- "typescript",
13
- "real-time",
14
- "subscriptions",
15
- "backend-as-a-service"
16
- ],
17
- "license": "MIT",
18
- "author": "Nathan Stitt <nathan@stitt.org>",
19
- "repository": {
20
- "type": "git",
21
- "url": "https://github.com/nathanstitt/pbtsdb.git"
22
- },
23
- "bugs": {
24
- "url": "https://github.com/nathanstitt/pbtsdb/issues"
25
- },
26
- "homepage": "https://github.com/nathanstitt/pbtsdb#readme",
27
- "type": "module",
28
- "main": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
- "exports": {
31
- ".": {
32
- "import": "./dist/index.js",
33
- "types": "./dist/index.d.ts"
2
+ "name": "pbtsdb",
3
+ "version": "0.6.1",
4
+ "description": "Type-safe PocketBase integration with TanStack Query and TanStack DB",
5
+ "keywords": [
6
+ "pocketbase",
7
+ "tanstack",
8
+ "tanstack-query",
9
+ "tanstack-db",
10
+ "database",
11
+ "react",
12
+ "typescript",
13
+ "real-time",
14
+ "subscriptions",
15
+ "backend-as-a-service"
16
+ ],
17
+ "license": "MIT",
18
+ "author": "Nathan Stitt <nathan@stitt.org>",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/nathanstitt/pbtsdb.git"
34
22
  },
35
- "./core": {
36
- "import": "./dist/core.js",
37
- "types": "./dist/core.d.ts"
38
- }
39
- },
40
- "files": [
41
- "dist",
42
- "README.md",
43
- "llms.txt",
44
- "LICENSE"
45
- ],
46
- "scripts": {
47
- "build": "tsup",
48
- "prepublishOnly": "npm run checks && npm test && npm run build",
49
- "db:reset": "rm -rf ./pb_data && pocketbase migrate up",
50
- "checks": "run-p typecheck lint",
51
- "lint": "biome check .",
52
- "lint:fix": "biome check . --write",
53
- "typecheck": "tsc --noEmit --skipLibCheck",
54
- "test": "NODE_OPTIONS='--localstorage-file=.localstorage-test' start-server-and-test test:server http://127.0.0.1:8210/api/health 'vitest $TEST'",
55
- "test:run": "vitest",
56
- "test:server": "./scripts/start-test-server.sh",
57
- "test:server:migrate": "pocketbase migrate --dir ./pb_data"
58
- },
59
- "peerDependencies": {
60
- "@tanstack/query-db-collection": ">=1.0.0",
61
- "@tanstack/react-db": ">=0.1.0",
62
- "@tanstack/react-query": ">=5.0.0",
63
- "pocketbase": ">=0.21.0",
64
- "react": ">=18.0.0",
65
- "react-dom": ">=18.0.0"
66
- },
67
- "peerDependenciesMeta": {
68
- "@tanstack/react-db": {
69
- "optional": true
23
+ "bugs": {
24
+ "url": "https://github.com/nathanstitt/pbtsdb/issues"
25
+ },
26
+ "homepage": "https://github.com/nathanstitt/pbtsdb#readme",
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.js",
33
+ "types": "./dist/index.d.ts"
34
+ },
35
+ "./core": {
36
+ "import": "./dist/core.js",
37
+ "types": "./dist/core.d.ts"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "llms.txt",
44
+ "LICENSE"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "prepublishOnly": "npm run checks && npm test && npm run build",
49
+ "db:reset": "rm -rf ./pb_data && pocketbase migrate up",
50
+ "checks": "run-p typecheck lint",
51
+ "lint": "biome check .",
52
+ "lint:fix": "biome check . --write",
53
+ "typecheck": "tsc --noEmit --skipLibCheck",
54
+ "test": "NODE_OPTIONS='--localstorage-file=.localstorage-test' start-server-and-test test:server http://127.0.0.1:8210/api/health 'vitest $TEST'",
55
+ "test:run": "vitest",
56
+ "test:server": "./scripts/start-test-server.sh",
57
+ "test:server:migrate": "pocketbase migrate --dir ./pb_data"
58
+ },
59
+ "peerDependencies": {
60
+ "@tanstack/query-db-collection": ">=1.0.0",
61
+ "@tanstack/react-db": ">=0.1.0",
62
+ "@tanstack/react-query": ">=5.0.0",
63
+ "pocketbase": ">=0.21.0",
64
+ "react": ">=18.0.0",
65
+ "react-dom": ">=18.0.0"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "@tanstack/react-db": {
69
+ "optional": true
70
+ },
71
+ "react": {
72
+ "optional": true
73
+ },
74
+ "react-dom": {
75
+ "optional": true
76
+ }
70
77
  },
71
- "react": {
72
- "optional": true
78
+ "devDependencies": {
79
+ "@biomejs/biome": "^2.5.0",
80
+ "@tanstack/query-db-collection": ">=1.0.31",
81
+ "@tanstack/react-db": ">=0.1.78",
82
+ "@tanstack/react-query": ">=5.90.20",
83
+ "@testing-library/react": ">=16.3.2",
84
+ "@types/node": ">=25.0.10",
85
+ "@types/react": ">=19.2.10",
86
+ "dotenv": ">=17.2.3",
87
+ "eventsource": ">=4.1.0",
88
+ "jsdom": ">=27.4.0",
89
+ "npm-run-all": ">=4.1.5",
90
+ "pocketbase": ">=0.26.7",
91
+ "react": ">=19.2.4",
92
+ "react-dom": ">=19.2.4",
93
+ "start-server-and-test": ">=2.1.3",
94
+ "tsup": "^8.5.1",
95
+ "tsx": ">=4.21.0",
96
+ "typescript": ">=5.9.3",
97
+ "vitest": ">=4.0.18"
73
98
  },
74
- "react-dom": {
75
- "optional": true
99
+ "overrides": {
100
+ "esbuild": "^0.28.1"
76
101
  }
77
- },
78
- "devDependencies": {
79
- "@tanstack/query-db-collection": ">=1.0.31",
80
- "@tanstack/react-db": ">=0.1.78",
81
- "@tanstack/react-query": ">=5.90.20",
82
- "@testing-library/react": ">=16.3.2",
83
- "@types/node": ">=25.0.10",
84
- "@types/react": ">=19.2.10",
85
- "biome": ">=0.3.3",
86
- "dotenv": ">=17.2.3",
87
- "eventsource": ">=4.1.0",
88
- "jsdom": ">=27.4.0",
89
- "npm-run-all": ">=4.1.5",
90
- "pocketbase": ">=0.26.7",
91
- "react": ">=19.2.4",
92
- "react-dom": ">=19.2.4",
93
- "start-server-and-test": ">=2.1.3",
94
- "tsup": "^8.5.1",
95
- "tsx": ">=4.21.0",
96
- "typescript": ">=5.9.3",
97
- "vitest": ">=4.0.18"
98
- }
99
102
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/pocketbase-query-converter.ts","../src/logger.ts","../src/collection.ts","../src/util.ts"],"names":["createTanStackCollection"],"mappings":"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","file":"chunk-UYH4FKXY.js","sourcesContent":["import { parseWhereExpression, parseOrderByExpression, type FieldPath, type ParsedOrderBy } from '@tanstack/db';\nimport type { IR } 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","/**\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 // Only log debug in development\n if (process.env.NODE_ENV === 'development') {\n // biome-ignore lint/suspicious/noConsoleLog: Debug logging is acceptable in development\n console.log(`[pbtsdb] ${msg}`, context || '');\n }\n },\n info: (msg: string, context?: object) => {\n // biome-ignore lint/suspicious/noConsoleLog: Info logging is acceptable\n console.info(`[pbtsdb] ${msg}`, context || '');\n },\n warn: (msg: string, context?: object) => {\n // biome-ignore lint/suspicious/noConsoleLog: Warning logging is acceptable\n console.warn(`[pbtsdb] ${msg}`, context || '');\n },\n error: (msg: string, context?: object) => {\n // biome-ignore lint/suspicious/noConsoleLog: Error logging is acceptable\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 PocketBase from \"pocketbase\";\nimport type { RecordSubscription } from \"pocketbase\";\nimport {\n createCollection as createTanStackCollection,\n type Collection,\n type LoadSubsetOptions,\n} from \"@tanstack/db\";\nimport { queryCollectionOptions, type QueryCollectionUtils } from \"@tanstack/query-db-collection\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport { convertToPocketBaseFilter, convertToPocketBaseSort } from \"./pocketbase-query-converter\";\nimport type {\n SchemaDeclaration,\n CreateCollectionOptions,\n ExtractRecordType,\n ExpandTargetCollection,\n BaseRecord,\n} from \"./types\";\nimport { logger } from \"./logger\";\n\nexport type { SchemaDeclaration, CreateCollectionOptions, BaseRecord } 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<Schema extends SchemaDeclaration, C extends keyof Schema, Opts> = 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<WithExpandFromConfig<Schema, C, Opts>, string | number, WithExpandFromConfig<Schema, C, Opts>>,\n // TSchema - we don't use StandardSchema validation\n never,\n Opts extends {\n omitOnInsert: infer O extends readonly import(\"./types\").OmittableFields<ExtractRecordType<Schema, C>>[];\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>(pb: PocketBase, queryClient: QueryClient) {\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 fetchRecords(loadOptions?: ExtendedLoadSubsetOptions): Promise<RecordType[]> {\n const filter = convertToPocketBaseFilter(loadOptions?.where);\n const sort = convertToPocketBaseSort(loadOptions?.orderBy);\n const limit = loadOptions?.limit;\n\n let items: RecordType[];\n try {\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 items = result.items as unknown as RecordType[];\n } else {\n // Use getFullList to fetch all records with automatic pagination\n items = (await pb.collection(collectionName).getFullList({\n filter,\n sort,\n expand: expandString,\n })) as unknown as RecordType[];\n }\n } catch (error) {\n if (ignoreAutoCancellation && error instanceof Error && error.message.includes(\"autocancelled\")) {\n return queryClient.getQueryData<RecordType[]>([collectionName]) ?? [];\n }\n throw error;\n }\n\n if (expandStores) {\n for (const record of items) {\n const expandData = (record as RecordType & { expand?: Record<string, object | object[]> }).expand;\n if (!expandData) continue;\n\n for (const [key, value] of Object.entries(expandData)) {\n const targetStore = expandStores[key];\n if (!targetStore.utils) continue;\n if (!targetStore.isReady()) {\n if (targetStore.config?.syncMode === \"on-demand\") {\n await targetStore._sync.startSync();\n } else {\n logger.warn(`not syncing ${key} on ${collectionName} because store is not yet ready`);\n continue;\n }\n }\n const values = Array.isArray(value) ? value : [value];\n targetStore.utils.writeUpsert(values);\n }\n }\n }\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(ctx.meta?.loadSubsetOptions as ExtendedLoadSubsetOptions | undefined);\n },\n getKey: (item: RecordType) => {\n const record = item as any;\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;\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.collection(collectionName).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 const handleRealtimeEvent = (event: RecordSubscription<RecordType>) => {\n if (!collection.utils) return;\n\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 collection.utils.writeDelete((event.record as { id: string }).id);\n }\n break;\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.collection(collectionName).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)\", { collectionName, error });\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(\"subscribers:change\", (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 // Add collectionName and subscription helpers\n Object.assign(collection, {\n collectionName,\n waitForSubscription,\n isSubscribed: () => isSubscribed,\n });\n\n return collection as any;\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"]}