pbtsdb 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +22 -0
- package/dist/index.js +34 -12
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.d.ts
CHANGED
|
@@ -312,6 +312,28 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
312
312
|
* ```
|
|
313
313
|
*/
|
|
314
314
|
syncMode?: 'eager' | 'on-demand';
|
|
315
|
+
/**
|
|
316
|
+
* Whether to ignore PocketBase auto-cancellation errors.
|
|
317
|
+
*
|
|
318
|
+
* PocketBase automatically cancels pending requests when a new request is made
|
|
319
|
+
* to the same endpoint. This can throw ClientResponseError with a message
|
|
320
|
+
* containing "autocancelled". When this option is true, such errors are
|
|
321
|
+
* silently ignored and the existing cached data is returned for the cancelled request.
|
|
322
|
+
*
|
|
323
|
+
* @default true
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* // Default: auto-cancellation errors are ignored
|
|
328
|
+
* const collection = createCollection<Schema>(pb, queryClient)('books');
|
|
329
|
+
*
|
|
330
|
+
* // Explicitly handle auto-cancellation errors
|
|
331
|
+
* const collection = createCollection<Schema>(pb, queryClient)('books', {
|
|
332
|
+
* ignoreAutoCancellation: false
|
|
333
|
+
* });
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
ignoreAutoCancellation?: boolean;
|
|
315
337
|
}
|
|
316
338
|
|
|
317
339
|
/**
|
package/dist/index.js
CHANGED
|
@@ -138,20 +138,34 @@ function createCollection(pb, queryClient) {
|
|
|
138
138
|
return (collectionName, options) => {
|
|
139
139
|
const expandStores = options?.expand;
|
|
140
140
|
const expandString = expandStores ? Object.keys(expandStores).sort().join(",") : void 0;
|
|
141
|
+
const ignoreAutoCancellation = options?.ignoreAutoCancellation ?? true;
|
|
141
142
|
async function fetchRecords(loadOptions) {
|
|
142
143
|
const filter = convertToPocketBaseFilter(loadOptions?.where);
|
|
143
144
|
const sort = convertToPocketBaseSort(loadOptions?.orderBy);
|
|
144
145
|
const limit = loadOptions?.limit;
|
|
145
146
|
let items;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
147
|
+
try {
|
|
148
|
+
if (limit) {
|
|
149
|
+
const result = await pb.collection(collectionName).getList(1, limit, {
|
|
150
|
+
filter,
|
|
151
|
+
sort,
|
|
152
|
+
skipTotal: true,
|
|
153
|
+
// Optimize by skipping total count
|
|
154
|
+
expand: expandString
|
|
155
|
+
});
|
|
156
|
+
items = result.items;
|
|
157
|
+
} else {
|
|
158
|
+
items = await pb.collection(collectionName).getFullList({
|
|
159
|
+
filter,
|
|
160
|
+
sort,
|
|
161
|
+
expand: expandString
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (ignoreAutoCancellation && error instanceof Error && error.message.includes("autocancelled")) {
|
|
166
|
+
return queryClient.getQueryData([collectionName]) ?? [];
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
155
169
|
}
|
|
156
170
|
if (expandStores) {
|
|
157
171
|
for (const record of items) {
|
|
@@ -185,14 +199,22 @@ function createCollection(pb, queryClient) {
|
|
|
185
199
|
getKey: (item) => {
|
|
186
200
|
const record = item;
|
|
187
201
|
if (!record || typeof record !== "object" || !("id" in record)) {
|
|
188
|
-
throw new Error(
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Record in collection '${collectionName}' is missing required 'id' field. Received: ${JSON.stringify(item)}`
|
|
204
|
+
);
|
|
189
205
|
}
|
|
190
206
|
return record.id;
|
|
191
207
|
},
|
|
192
208
|
onInsert: options?.onInsert === false ? void 0 : options?.onInsert ?? (async ({ transaction }) => {
|
|
193
209
|
await Promise.all(
|
|
194
210
|
transaction.mutations.map(async (mutation) => {
|
|
195
|
-
const {
|
|
211
|
+
const {
|
|
212
|
+
created,
|
|
213
|
+
updated,
|
|
214
|
+
collectionId,
|
|
215
|
+
collectionName: _,
|
|
216
|
+
...data
|
|
217
|
+
} = mutation.modified;
|
|
196
218
|
await pb.collection(collectionName).create(data);
|
|
197
219
|
})
|
|
198
220
|
);
|
|
@@ -227,7 +249,7 @@ function createCollection(pb, queryClient) {
|
|
|
227
249
|
collection.utils.writeInsert(event.record);
|
|
228
250
|
break;
|
|
229
251
|
case "update":
|
|
230
|
-
collection.utils.
|
|
252
|
+
collection.utils.writeUpsert(event.record);
|
|
231
253
|
break;
|
|
232
254
|
case "delete":
|
|
233
255
|
if (event.record && "id" in event.record) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pocketbase-query-converter.ts","../src/logger.ts","../src/collection.ts","../src/react.tsx","../src/util.ts"],"names":["createTanStackCollection"],"mappings":"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","file":"index.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 { createCollection as createTanStackCollection, type Collection, type LoadSubsetOptions } from \"@tanstack/react-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 {\n SchemaDeclaration,\n CreateCollectionOptions,\n BaseRecord,\n} 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 { expand: infer E }\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 { omitOnInsert: infer O extends readonly import('./types').OmittableFields<ExtractRecordType<Schema, C>>[] }\n ? import('./types').ComputeInsertType<ExtractRecordType<Schema, C>, O>\n : ExtractRecordType<Schema, C>\n> & CollectionSubscriptionHelpers;\n\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 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 if (!filter && !sort && !limit && !expandString) {\n items = await pb.collection(collectionName).getFullList() as unknown as RecordType[];\n } else {\n const result = await pb.collection(collectionName).getList(1, limit || 500, {\n filter,\n sort,\n expand: expandString,\n });\n items = result.items as unknown as RecordType[];\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 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(`Record in collection '${collectionName}' is missing required 'id' field. Received: ${JSON.stringify(item)}`);\n }\n return record.id;\n },\n onInsert: options?.onInsert === false ? undefined : options?.onInsert ?? (async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map(async (mutation) => {\n const { created, updated, collectionId, collectionName: _, ...data } = mutation.modified as unknown as Record<string, unknown>;\n await pb.collection(collectionName).create(data);\n })\n );\n }),\n onUpdate: options?.onUpdate === false ? undefined : options?.onUpdate ?? (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 }),\n onDelete: options?.onDelete === false ? undefined : options?.onDelete ?? (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 })\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.writeUpdate(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","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","/**\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/pocketbase-query-converter.ts","../src/logger.ts","../src/collection.ts","../src/react.tsx","../src/util.ts"],"names":["createTanStackCollection"],"mappings":";;;;;;;AAKA,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,CAAC,CAAA,KAAM,CAAA,EAAG,QAAQ,CAAA,GAAA,EAAM,WAAA,CAAY,CAAC,CAAC,CAAA,CAAE,CAAA;AAC5E,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,IAAA,KAAoB;AACtD,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;;;AC7EA,IAAM,aAAA,GAAwB;AAAA,EAC1B,KAAA,EAAO,CAAC,GAAA,EAAa,OAAA,KAAqB;AAEtC,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,EAAe;AAExC,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,SAAA,EAAY,GAAG,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,IAChD;AAAA,EACJ,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,GAAA,EAAa,OAAA,KAAqB;AAErC,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;AAErC,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;AAEtC,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;;;ACAO,SAAS,gBAAA,CAAmD,IAAgB,WAAA,EAA0B;AACzG,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;AAElE,IAAA,eAAe,aAAa,WAAA,EAAgE;AACxF,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;AACJ,MAAA,IAAI;AACA,QAAA,IAAI,KAAA,EAAO;AAEP,UAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA,CAAE,OAAA,CAAQ,GAAG,KAAA,EAAO;AAAA,YACjE,MAAA;AAAA,YACA,IAAA;AAAA,YACA,SAAA,EAAW,IAAA;AAAA;AAAA,YACX,MAAA,EAAQ;AAAA,WACX,CAAA;AACD,UAAA,KAAA,GAAQ,MAAA,CAAO,KAAA;AAAA,QACnB,CAAA,MAAO;AAEH,UAAA,KAAA,GAAS,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,EAAE,WAAA,CAAY;AAAA,YACrD,MAAA;AAAA,YACA,IAAA;AAAA,YACA,MAAA,EAAQ;AAAA,WACX,CAAA;AAAA,QACL;AAAA,MACJ,SAAS,KAAA,EAAO;AACZ,QAAA,IAAI,0BAA0B,KAAA,YAAiB,KAAA,IAAS,MAAM,OAAA,CAAQ,QAAA,CAAS,eAAe,CAAA,EAAG;AAC7F,UAAA,OAAO,YAAY,YAAA,CAA2B,CAAC,cAAc,CAAC,KAAK,EAAC;AAAA,QACxE;AACA,QAAA,MAAM,KAAA;AAAA,MACV;AAEA,MAAA,IAAI,YAAA,EAAc;AACd,QAAA,KAAA,MAAW,UAAU,KAAA,EAAO;AACxB,UAAA,MAAM,aAAc,MAAA,CAAuE,MAAA;AAC3F,UAAA,IAAI,CAAC,UAAA,EAAY;AAEjB,UAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AACnD,YAAA,MAAM,WAAA,GAAc,aAAa,GAAG,CAAA;AACpC,YAAA,IAAI,CAAC,YAAY,KAAA,EAAO;AACxB,YAAA,IAAI,CAAC,WAAA,CAAY,OAAA,EAAQ,EAAG;AACxB,cAAA,IAAI,WAAA,CAAY,MAAA,EAAQ,QAAA,KAAa,WAAA,EAAa;AAC9C,gBAAA,MAAM,WAAA,CAAY,MAAM,SAAA,EAAU;AAAA,cACtC,CAAA,MAAO;AACH,gBAAA,MAAA,CAAO,IAAA,CAAK,CAAA,YAAA,EAAe,GAAG,CAAA,IAAA,EAAO,cAAc,CAAA,+BAAA,CAAiC,CAAA;AACpF,gBAAA;AAAA,cACJ;AAAA,YACJ;AACA,YAAA,MAAM,SAAS,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA;AACpD,YAAA,WAAA,CAAY,KAAA,CAAM,YAAY,MAAM,CAAA;AAAA,UACxC;AAAA,QACJ;AAAA,MACJ;AAEA,MAAA,OAAO,KAAA;AAAA,IACX;AAEA,IAAA,MAAM,oBAAoB,sBAAA,CAAuB;AAAA,MAC7C,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,CAAa,GAAA,CAAI,IAAA,EAAM,iBAA0D,CAAA;AAAA,MAC5F,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,OAAO,QAAA,KAAa;AAC1C,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;AAAA,MACJ,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,OAAO,QAAA,KAAa;AAC1C,YAAA,MAAM,eAAe,QAAA,CAAS,QAAA;AAC9B,YAAA,MAAM,EAAA,CAAG,WAAW,cAAc,CAAA,CAAE,OAAO,YAAA,CAAa,EAAA,EAAI,SAAS,OAAO,CAAA;AAAA,UAChF,CAAC;AAAA,SACL;AAAA,MACJ,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,OAAO,QAAA,KAAa;AAC1C,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;AAAA,MACJ,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;AAG/C,IAAA,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAA0C;AACnE,MAAA,IAAI,CAAC,WAAW,KAAA,EAAO;AAEvB,MAAA,UAAA,CAAW,KAAA,CAAM,WAAW,MAAM;AAC9B,QAAA,QAAQ,MAAM,MAAA;AAAQ,UAClB,KAAK,QAAA;AACD,YAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,MAAM,CAAA;AACzC,YAAA;AAAA,UACJ,KAAK,QAAA;AACD,YAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,MAAM,CAAA;AACzC,YAAA;AAAA,UACJ,KAAK,QAAA;AACD,YAAA,IAAI,KAAA,CAAM,MAAA,IAAU,IAAA,IAAQ,KAAA,CAAM,MAAA,EAAQ;AACtC,cAAA,UAAA,CAAW,KAAA,CAAM,WAAA,CAAa,KAAA,CAAM,MAAA,CAA0B,EAAE,CAAA;AAAA,YACpE;AACA,YAAA;AAAA;AACR,MACJ,CAAC,CAAA;AAAA,IACL,CAAA;AAGA,IAAA,MAAM,oBAAoB,YAAY;AAClC,MAAA,IAAI,YAAA,EAAc;AAGlB,MAAA,IAAI,CAAC,mBAAA,EAAqB;AACtB,QAAA,mBAAA,GAAsB,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACjD,UAAA,mBAAA,GAAsB,OAAA;AAAA,QAC1B,CAAC,CAAA;AAAA,MACL;AAEA,MAAA,IAAI;AACA,QAAA,aAAA,GAAgB,MAAM,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA,CAAE,SAAA,CAAU,KAAK,mBAAmB,CAAA;AACtF,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,KAAA,CAAM,oDAAA,EAAsD,EAAE,cAAA,EAAgB,OAAO,CAAA;AAAA,MAChG;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,OAAA,CAAc,CAAC,OAAA,KAAY;AACjC,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,CAAG,oBAAA,EAAsB,CAAC,KAAA,KAAwE;AACzG,MAAA,MAAM,WAAW,KAAA,CAAM,eAAA;AACvB,MAAA,MAAM,gBAAgB,KAAA,CAAM,uBAAA;AAE5B,MAAA,IAAI,QAAA,GAAW,CAAA,IAAK,aAAA,KAAkB,CAAA,EAAG;AAErC,QAAA,iBAAA,EAAkB,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACtC,CAAA,MAAA,IAAW,QAAA,KAAa,CAAA,IAAK,aAAA,GAAgB,CAAA,EAAG;AAE5C,QAAA,gBAAA,EAAiB,CAAE,MAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACrC;AAAA,IACJ,CAAC,CAAA;AAGD,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;ACnPO,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;;;AC9IO,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":"index.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/react-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\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 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 })),\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 })),\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 })),\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","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","/**\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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pbtsdb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Type-safe PocketBase integration with TanStack Query and TanStack DB",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pocketbase",
|
|
@@ -61,24 +61,24 @@
|
|
|
61
61
|
"react-dom": ">=18.0.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@tanstack/query-db-collection": ">=1.0.
|
|
65
|
-
"@tanstack/react-db": ">=0.1.
|
|
66
|
-
"@tanstack/react-query": ">=5.90.
|
|
67
|
-
"@testing-library/react": ">=16.3.
|
|
68
|
-
"@types/node": ">=
|
|
69
|
-
"@types/react": ">=19.2.
|
|
64
|
+
"@tanstack/query-db-collection": ">=1.0.21",
|
|
65
|
+
"@tanstack/react-db": ">=0.1.68",
|
|
66
|
+
"@tanstack/react-query": ">=5.90.20",
|
|
67
|
+
"@testing-library/react": ">=16.3.2",
|
|
68
|
+
"@types/node": ">=25.0.10",
|
|
69
|
+
"@types/react": ">=19.2.10",
|
|
70
70
|
"biome": ">=0.3.3",
|
|
71
71
|
"dotenv": ">=17.2.3",
|
|
72
72
|
"eventsource": ">=4.1.0",
|
|
73
|
-
"jsdom": ">=27.
|
|
73
|
+
"jsdom": ">=27.4.0",
|
|
74
74
|
"npm-run-all": ">=4.1.5",
|
|
75
|
-
"pocketbase": ">=0.26.
|
|
76
|
-
"react": ">=19.2.
|
|
77
|
-
"react-dom": ">=19.2.
|
|
75
|
+
"pocketbase": ">=0.26.7",
|
|
76
|
+
"react": ">=19.2.4",
|
|
77
|
+
"react-dom": ">=19.2.4",
|
|
78
78
|
"start-server-and-test": ">=2.1.3",
|
|
79
79
|
"tsup": "^8.5.1",
|
|
80
80
|
"tsx": ">=4.21.0",
|
|
81
81
|
"typescript": ">=5.9.3",
|
|
82
|
-
"vitest": ">=4.0.
|
|
82
|
+
"vitest": ">=4.0.18"
|
|
83
83
|
}
|
|
84
84
|
}
|