pbtsdb 0.3.0 → 0.5.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 CHANGED
@@ -1,427 +1,9 @@
1
- import { Collection, InsertMutationFn, UpdateMutationFn, DeleteMutationFn, BaseCollectionConfig } from '@tanstack/db';
1
+ export { CreateCollectionOptions, ExcludeUndefined, ExtractRecordType, ExtractRelations, Logger, OmittableFields, ParseExpandFields, RelationAsCollection, SchemaDeclaration, WithExpand, createCollection, newRecordId, resetLogger, setLogger } from './core.js';
2
2
  export { BTreeIndex, BasicIndex, DeltaEvent, DeltaType, EffectConfig, EffectContext, IndexConstructor, ReverseIndex, createEffect, toArray } from '@tanstack/db';
3
- import PocketBase from 'pocketbase';
4
- import { Collection as Collection$1 } from '@tanstack/react-db';
5
- import { QueryCollectionUtils } from '@tanstack/query-db-collection';
6
- import { QueryClient } from '@tanstack/react-query';
7
3
  import React, { ReactNode } from 'react';
8
-
9
- /**
10
- * Base record type required by PocketBase collections.
11
- * All records must have an 'id' field.
12
- */
13
- interface BaseRecord {
14
- id: string;
15
- }
16
- /**
17
- * Schema declaration for type-safe collection management.
18
- * Define your PocketBase collections with their record types and relations.
19
- *
20
- * @example
21
- * ```ts
22
- * interface MySchema extends SchemaDeclaration {
23
- * users: {
24
- * type: UserRecord;
25
- * relations: {
26
- * org: OrgRecord;
27
- * };
28
- * };
29
- * }
30
- * ```
31
- */
32
- interface SchemaDeclaration {
33
- [collectionName: string]: {
34
- type: BaseRecord;
35
- relations?: {
36
- [fieldName: string]: BaseRecord | BaseRecord[];
37
- };
38
- };
39
- }
40
- /**
41
- * Extracts the record type from a schema collection.
42
- * @internal
43
- */
44
- type ExtractRecordType<Schema extends SchemaDeclaration, CollectionName extends keyof Schema> = Schema[CollectionName]['type'];
45
- /**
46
- * Valid field names that can be omitted during insert operations.
47
- * Excludes 'id' which is always required for TanStack DB record tracking.
48
- * @internal
49
- */
50
- type OmittableFields<T extends object> = Exclude<keyof T, 'id'>;
51
- /**
52
- * Computes the insert input type by making specified fields optional.
53
- * Used to support omitting server-generated fields (created, updated) during insertion.
54
- *
55
- * IMPORTANT: The 'id' field can NEVER be omitted as TanStack DB requires it for record tracking.
56
- *
57
- * @example
58
- * ```ts
59
- * type BookInsert = ComputeInsertType<Books, ['created', 'updated']>
60
- * // Result: Omit<Books, 'created' | 'updated'> & Partial<Pick<Books, 'created' | 'updated'>>
61
- * ```
62
- * @internal
63
- */
64
- type ComputeInsertType<T extends object, OmitFields extends readonly OmittableFields<T>[]> = Omit<T, OmitFields[number]> & Partial<Pick<T, OmitFields[number]>>;
65
- /**
66
- * Extracts the relations object from a schema collection.
67
- * Returns never if the collection has no relations defined.
68
- * @internal
69
- */
70
- type ExtractRelations<Schema extends SchemaDeclaration, CollectionName extends keyof Schema> = Schema[CollectionName] extends {
71
- relations: infer R;
72
- } ? R : never;
73
- /**
74
- * Parses comma-separated relation field names into a union type.
75
- * Recursively processes "field1,field2,field3" into "field1" | "field2" | "field3".
76
- *
77
- * @example
78
- * ParseExpandFields<"customer,address"> => "customer" | "address"
79
- * @internal
80
- */
81
- type ParseExpandFields<T extends string> = T extends `${infer Field},${infer Rest}` ? Field | ParseExpandFields<Rest> : T;
82
- /**
83
- * Builds the expand object type based on field names.
84
- * If expand fields are provided, adds an optional `expand` property with properly typed relations.
85
- *
86
- * @example
87
- * ```ts
88
- * // Without expand
89
- * WithExpand<Schema, 'jobs', undefined> => JobRecord
90
- *
91
- * // With expand
92
- * WithExpand<Schema, 'jobs', 'customer'> => JobRecord & {
93
- * expand?: { customer?: CustomerRecord }
94
- * }
95
- * ```
96
- */
97
- type WithExpand<Schema extends SchemaDeclaration, CollectionName extends keyof Schema, ExpandFields extends string | undefined> = ExpandFields extends string ? ExtractRecordType<Schema, CollectionName> & {
98
- expand?: {
99
- [K in ParseExpandFields<ExpandFields>]?: K extends keyof ExtractRelations<Schema, CollectionName> ? ExtractRelations<Schema, CollectionName>[K] extends Array<infer U> ? U[] : ExtractRelations<Schema, CollectionName>[K] : never;
100
- };
101
- } : ExtractRecordType<Schema, CollectionName>;
102
- /**
103
- * Removes undefined from a union type.
104
- * Used to unwrap optional relation types.
105
- *
106
- * @example
107
- * ExcludeUndefined<Customer | undefined> => Customer
108
- * @internal
109
- */
110
- type ExcludeUndefined<T> = T extends (infer U) | undefined ? U : T;
111
- /**
112
- * Converts a schema relation type to its corresponding Collection constraint.
113
- * Handles both single relations (T) and array relations (T[]).
114
- * Accepts collections with any insert type to support omitOnInsert configurations.
115
- *
116
- * Uses constraint (extends Collection<T, ...>) rather than exact type to allow
117
- * collections with different TInput types (from omitOnInsert) to be compatible.
118
- *
119
- * @example
120
- * RelationAsCollection<Customer> => Collection<Customer, string | number, any, any, any>
121
- * RelationAsCollection<Customer[]> => Collection<Customer, string | number, any, any, any>
122
- * @internal
123
- */
124
- 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>;
125
- /**
126
- * Configuration for per-collection expand - maps relation field names to their target collections.
127
- * Relations configured here are automatically expanded on every fetch and auto-upserted into target collections.
128
- *
129
- * @example
130
- * ```ts
131
- * const authorsCollection = createCollection<Schema>(pb, queryClient)('authors');
132
- * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {
133
- * expand: {
134
- * author: authorsCollection // Always expand 'author', upsert into authorsCollection
135
- * }
136
- * });
137
- * ```
138
- */
139
- type ExpandConfig<Schema extends SchemaDeclaration, CollectionName extends keyof Schema> = ExtractRelations<Schema, CollectionName> extends never ? Record<string, never> : Partial<{
140
- [K in keyof ExtractRelations<Schema, CollectionName>]: RelationAsCollection<ExcludeUndefined<ExtractRelations<Schema, CollectionName>[K]>>;
141
- }>;
142
- /**
143
- * Options for creating a collection.
144
- */
145
- interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionName extends keyof Schema> {
146
- /**
147
- * Configure relations to automatically expand on every fetch.
148
- * Maps relation field names to their target collections for auto-upsert.
149
- *
150
- * Expanded records are automatically inserted into their target collections.
151
- *
152
- * @example
153
- * ```ts
154
- * const authorsCollection = createCollection<Schema>(pb, queryClient)('authors');
155
- * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {
156
- * expand: {
157
- * author: authorsCollection // Always expand, auto-upsert into authorsCollection
158
- * }
159
- * });
160
- *
161
- * // Expand is automatic - no .expand() call needed
162
- * const { data } = useLiveQuery((q) => q.from({ books: booksCollection }));
163
- * // data[0].expand.author is typed and populated
164
- * ```
165
- */
166
- expand?: ExpandConfig<Schema, CollectionName>;
167
- /**
168
- * Fields that can be omitted during insert operations.
169
- * Useful for server-generated fields like 'created', 'updated'.
170
- *
171
- * When specified, the insert() method will accept records without these fields,
172
- * and the omitted fields become optional in the insert input type.
173
- *
174
- * **Type safety:** Only valid field names from the record type are accepted.
175
- * **IMPORTANT:** The 'id' field can NEVER be omitted as TanStack DB requires it.
176
- *
177
- * @example
178
- * ```ts
179
- * // Allow inserting without created, updated (server-generated timestamps)
180
- * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {
181
- * omitOnInsert: ['created', 'updated'] as const
182
- * });
183
- *
184
- * // Now insert() accepts records without those fields
185
- * booksCollection.insert({
186
- * id: newRecordId(), // id is always required
187
- * title: 'New Book',
188
- * isbn: '1234567890',
189
- * genre: 'Fiction',
190
- * author: authorId
191
- * // created, updated are optional
192
- * });
193
- * ```
194
- */
195
- omitOnInsert?: readonly OmittableFields<ExtractRecordType<Schema, CollectionName>>[];
196
- /**
197
- * Custom handler for insert mutations.
198
- *
199
- * **Default behavior (not provided):** Automatically creates records in PocketBase,
200
- * excluding auto-generated fields (id, created, updated, collectionId, collectionName).
201
- *
202
- * **Custom handler:** Provide your own handler to customize insert behavior.
203
- *
204
- * **Disable:** Set to `false` to disable insert mutations entirely (will throw error if insert is called).
205
- *
206
- * @example
207
- * ```ts
208
- * // Use default automatic handler (recommended)
209
- * const collection = createCollection<Schema>(pb, queryClient)('books');
210
- *
211
- * // Custom handler
212
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
213
- * onInsert: async ({ transaction }) => {
214
- * for (const mutation of transaction.mutations) {
215
- * await customInsertLogic(mutation.modified);
216
- * }
217
- * await queryClient.invalidateQueries({ queryKey: ['books'] });
218
- * }
219
- * });
220
- *
221
- * // Disable inserts (read-only collection)
222
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
223
- * onInsert: false
224
- * });
225
- * ```
226
- */
227
- onInsert?: InsertMutationFn<ExtractRecordType<Schema, CollectionName>> | false;
228
- /**
229
- * Custom handler for update mutations.
230
- *
231
- * **Default behavior (not provided):** Automatically updates records in PocketBase
232
- * with the changed fields.
233
- *
234
- * **Custom handler:** Provide your own handler to customize update behavior.
235
- *
236
- * **Disable:** Set to `false` to disable update mutations entirely (will throw error if update is called).
237
- *
238
- * @example
239
- * ```ts
240
- * // Use default automatic handler (recommended)
241
- * const collection = createCollection<Schema>(pb, queryClient)('books');
242
- *
243
- * // Custom handler
244
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
245
- * onUpdate: async ({ transaction }) => {
246
- * for (const mutation of transaction.mutations) {
247
- * await customUpdateLogic(mutation.original.id, mutation.changes);
248
- * }
249
- * await queryClient.invalidateQueries({ queryKey: ['books'] });
250
- * }
251
- * });
252
- *
253
- * // Disable updates (read-only collection)
254
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
255
- * onUpdate: false
256
- * });
257
- * ```
258
- */
259
- onUpdate?: UpdateMutationFn<ExtractRecordType<Schema, CollectionName>> | false;
260
- /**
261
- * Custom handler for delete mutations.
262
- *
263
- * **Default behavior (not provided):** Automatically deletes records from PocketBase.
264
- *
265
- * **Custom handler:** Provide your own handler to customize delete behavior.
266
- *
267
- * **Disable:** Set to `false` to disable delete mutations entirely (will throw error if delete is called).
268
- *
269
- * @example
270
- * ```ts
271
- * // Use default automatic handler (recommended)
272
- * const collection = createCollection<Schema>(pb, queryClient)('books');
273
- *
274
- * // Custom handler
275
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
276
- * onDelete: async ({ transaction }) => {
277
- * for (const mutation of transaction.mutations) {
278
- * await customDeleteLogic(mutation.original.id);
279
- * }
280
- * await queryClient.invalidateQueries({ queryKey: ['books'] });
281
- * }
282
- * });
283
- *
284
- * // Disable deletes (read-only collection)
285
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
286
- * onDelete: false
287
- * });
288
- * ```
289
- */
290
- onDelete?: DeleteMutationFn<ExtractRecordType<Schema, CollectionName>> | false;
291
- /**
292
- * Sync mode for the collection. Controls when and how data is fetched from PocketBase.
293
- *
294
- * - `'eager'` (default): Fetches all data immediately when collection is created.
295
- * Queries are evaluated client-side against the cached data. Fast for small datasets
296
- * but loads entire collection into memory. Matches TanStack DB default.
297
- *
298
- * - `'on-demand'`: Fetches data only when queries execute. Each query with different
299
- * filters/sorting triggers a new fetch from PocketBase. Enables true server-side
300
- * filtering and is better for large datasets.
301
- *
302
- * @default 'eager'
303
- *
304
- * @example
305
- * ```ts
306
- * // Default: eager mode - client-side filtering
307
- * const collection = createCollection<Schema>(pb, queryClient)('books');
308
- *
309
- * // On-demand mode - server-side filtering
310
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
311
- * syncMode: 'on-demand'
312
- * });
313
- * ```
314
- */
315
- syncMode?: 'eager' | 'on-demand';
316
- /**
317
- * Whether to ignore PocketBase auto-cancellation errors.
318
- *
319
- * PocketBase automatically cancels pending requests when a new request is made
320
- * to the same endpoint. This can throw ClientResponseError with a message
321
- * containing "autocancelled". When this option is true, such errors are
322
- * silently ignored and the existing cached data is returned for the cancelled request.
323
- *
324
- * @default true
325
- *
326
- * @example
327
- * ```ts
328
- * // Default: auto-cancellation errors are ignored
329
- * const collection = createCollection<Schema>(pb, queryClient)('books');
330
- *
331
- * // Explicitly handle auto-cancellation errors
332
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
333
- * ignoreAutoCancellation: false
334
- * });
335
- * ```
336
- */
337
- ignoreAutoCancellation?: boolean;
338
- /**
339
- * Additional options passed directly to the underlying TanStack DB collection.
340
- * Use this to configure indexing, garbage collection, comparison functions,
341
- * and any other TanStack DB collection options not explicitly exposed by pbtsdb.
342
- *
343
- * Options set here are spread into the `queryCollectionOptions()` call.
344
- * Fields managed by pbtsdb (`getKey`, `syncMode`, `onInsert`, `onUpdate`,
345
- * `onDelete`, `schema`) are excluded from the type.
346
- *
347
- * @example
348
- * ```ts
349
- * import { BasicIndex } from 'pbtsdb'
350
- * const collection = createCollection<Schema>(pb, queryClient)('books', {
351
- * collectionOptions: {
352
- * autoIndex: 'eager',
353
- * defaultIndexType: BasicIndex,
354
- * gcTime: 60000,
355
- * }
356
- * });
357
- * ```
358
- */
359
- collectionOptions?: Omit<Partial<BaseCollectionConfig<ExtractRecordType<Schema, CollectionName>, string | number>>, 'getKey' | 'syncMode' | 'onInsert' | 'onUpdate' | 'onDelete' | 'schema'>;
360
- }
361
-
362
- /**
363
- * Compute the record type with expand property when expand option is configured.
364
- * @internal
365
- */
366
- type WithExpandFromConfig<Schema extends SchemaDeclaration, C extends keyof Schema, Opts> = Opts extends {
367
- expand: infer E;
368
- } ? ExtractRecordType<Schema, C> & {
369
- expand?: {
370
- [K in keyof E]: K extends keyof ExtractRelations<Schema, C> ? ExtractRelations<Schema, C>[K] extends Array<infer U> ? U[] : ExtractRelations<Schema, C>[K] : never;
371
- };
372
- } : ExtractRecordType<Schema, C>;
373
- /**
374
- * Subscription helpers added to collection instances.
375
- * @internal
376
- */
377
- interface CollectionSubscriptionHelpers {
378
- /** The PocketBase collection name */
379
- collectionName: string;
380
- /** Wait for subscription to be established (useful in tests) */
381
- waitForSubscription: (timeout?: number) => Promise<void>;
382
- /** Check if collection has an active subscription */
383
- isSubscribed: () => boolean;
384
- }
385
- /**
386
- * Inferred collection type from config options.
387
- * @internal
388
- */
389
- type InferCollectionType<Schema extends SchemaDeclaration, C extends keyof Schema, Opts extends CreateCollectionOptions<Schema, C>> = Collection$1<WithExpandFromConfig<Schema, C, Opts>, string | number, QueryCollectionUtils<WithExpandFromConfig<Schema, C, Opts>, string | number, WithExpandFromConfig<Schema, C, Opts>>, never, Opts extends {
390
- omitOnInsert: infer O extends readonly OmittableFields<ExtractRecordType<Schema, C>>[];
391
- } ? ComputeInsertType<ExtractRecordType<Schema, C>, O> : ExtractRecordType<Schema, C>> & CollectionSubscriptionHelpers;
392
- /**
393
- * Creates a type-safe TanStack DB collection backed by PocketBase.
394
- * Use this when you need fine-grained control or need to create collections with dependencies.
395
- *
396
- * @param pb - PocketBase client instance
397
- * @param queryClient - TanStack Query client
398
- * @returns A curried function that takes collection name and options
399
- *
400
- * @example
401
- * Basic usage:
402
- * ```ts
403
- * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {});
404
- *
405
- * // Use directly
406
- * const books = await booksCollection.getFullList();
407
- * ```
408
- *
409
- * @example
410
- * With auto-expand relations:
411
- * ```ts
412
- * const authorsCollection = createCollection<Schema>(pb, queryClient)('authors', {});
413
- * const booksCollection = createCollection<Schema>(pb, queryClient)('books', {
414
- * expand: {
415
- * author: authorsCollection // Always expand, auto-upsert into authorsCollection
416
- * }
417
- * });
418
- *
419
- * // Expand is automatic - no .expand() call needed
420
- * const { data } = useLiveQuery((q) => q.from({ books: booksCollection }));
421
- * // data[0].expand.author is typed and populated
422
- * ```
423
- */
424
- declare function createCollection<Schema extends SchemaDeclaration>(pb: PocketBase, queryClient: QueryClient): <C extends keyof Schema & string, Opts extends CreateCollectionOptions<Schema, C> = CreateCollectionOptions<Schema, C>>(collectionName: C, options?: Opts) => InferCollectionType<Schema, C, Opts>;
4
+ import 'pocketbase';
5
+ import '@tanstack/query-db-collection';
6
+ import '@tanstack/react-query';
425
7
 
426
8
  /**
427
9
  * UseStore hook type for variadic collection access.
@@ -547,86 +129,4 @@ interface ReactProviderResult<CollectionsMap> {
547
129
  */
548
130
  declare function createReactProvider<CollectionsMap extends Record<string, any>>(collections: CollectionsMap): ReactProviderResult<CollectionsMap>;
549
131
 
550
- /**
551
- * Logger interface for subscription events and internal operations.
552
- * Users can provide their own implementation to integrate with external logging services.
553
- */
554
- interface Logger {
555
- /**
556
- * Log debug-level messages (typically only shown in development).
557
- * @param msg - The message to log
558
- * @param context - Optional context object with additional information
559
- */
560
- debug: (msg: string, context?: object) => void;
561
- /**
562
- * Log info-level messages.
563
- * @param msg - The message to log
564
- * @param context - Optional context object with additional information
565
- */
566
- info: (msg: string, context?: object) => void;
567
- /**
568
- * Log warning-level messages.
569
- * @param msg - The message to log
570
- * @param context - Optional context object with additional information
571
- */
572
- warn: (msg: string, context?: object) => void;
573
- /**
574
- * Log error-level messages.
575
- * @param msg - The message to log
576
- * @param context - Optional context object with additional information
577
- */
578
- error: (msg: string, context?: object) => void;
579
- }
580
- /**
581
- * Set a custom logger implementation.
582
- * This allows users to integrate with their own logging services (e.g., Sentry, LogRocket, etc.).
583
- *
584
- * @param customLogger - The custom logger implementation
585
- *
586
- * @example
587
- * ```ts
588
- * import { setLogger } from 'pbtsdb';
589
- *
590
- * // Integration with a custom logging service
591
- * setLogger({
592
- * debug: (msg, context) => myLogger.debug(msg, context),
593
- * warn: (msg, context) => myLogger.warn(msg, context),
594
- * error: (msg, context) => {
595
- * myLogger.error(msg, context);
596
- * Sentry.captureMessage(msg, { level: 'error', extra: context });
597
- * },
598
- * });
599
- * ```
600
- *
601
- * @example
602
- * ```ts
603
- * // Disable all logging
604
- * setLogger({
605
- * debug: () => {},
606
- * warn: () => {},
607
- * error: () => {},
608
- * });
609
- * ```
610
- */
611
- declare function setLogger(customLogger: Logger): void;
612
- /**
613
- * Reset the logger to the default implementation.
614
- */
615
- declare function resetLogger(): void;
616
-
617
- /**
618
- * Generates a new PocketBase-compatible record ID.
619
- * Returns a 15-character alphanumeric string (lowercase letters and numbers).
620
- *
621
- * PocketBase uses 15-character IDs for records, formatted as lowercase alphanumeric.
622
- *
623
- * @returns A 15-character alphanumeric string suitable for use as a PocketBase record ID
624
- *
625
- * @example
626
- * ```ts
627
- * const id = newRecordId(); // "a1b2c3d4e5f6g7h"
628
- * ```
629
- */
630
- declare function newRecordId(): string;
631
-
632
- export { type CreateCollectionOptions, type ExcludeUndefined, type ExtractRecordType, type ExtractRelations, type Logger, type OmittableFields, type ParseExpandFields, type ReactProviderResult, type RelationAsCollection, type SchemaDeclaration, type WithExpand, createCollection, createReactProvider, newRecordId, resetLogger, setLogger };
132
+ export { type ReactProviderResult, createReactProvider };