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