pbtsdb 0.11.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -57
- package/dist/chunk-TTENGDS3.js +3142 -0
- package/dist/chunk-TTENGDS3.js.map +1 -0
- package/dist/core.d.ts +232 -86
- package/dist/core.js +1 -1
- package/dist/index.d.ts +25 -11
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/llms.txt +29 -25
- package/package.json +6 -11
- package/dist/chunk-OBDOVDCI.js +0 -1596
- package/dist/chunk-OBDOVDCI.js.map +0 -1
package/dist/core.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { Collection, InsertMutationFn, UpdateMutationFn, DeleteMutationFn, BaseCollectionConfig, IR } from '@tanstack/db';
|
|
1
|
+
import { Collection, InsertMutationFn, UpdateMutationFn, DeleteMutationFn, BaseCollectionConfig, UtilsRecord, IR } from '@tanstack/db';
|
|
2
2
|
export { BTreeIndex, BasicIndex, DeltaEvent, DeltaType, EffectConfig, EffectContext, IndexConstructor, ReverseIndex, createEffect, materialize, toArray } from '@tanstack/db';
|
|
3
3
|
import PocketBase, { RecordSubscribeOptions } from 'pocketbase';
|
|
4
4
|
export { RecordSubscribeOptions } from 'pocketbase';
|
|
5
|
-
import { QueryCollectionUtils } from '@tanstack/query-db-collection';
|
|
6
|
-
import { QueryClient } from '@tanstack/react-query';
|
|
7
5
|
|
|
8
6
|
/** Which rows a collection's realtime subscription covers. */
|
|
9
7
|
type RealtimeMode = 'collection' | 'query';
|
|
@@ -102,8 +100,8 @@ type RelationAsCollection<T> = T extends Array<infer U> ? U extends object ? Col
|
|
|
102
100
|
interface RelationTarget {
|
|
103
101
|
/** Relation targets of this collection, for nested expand paths. */
|
|
104
102
|
readonly relationTargets: Record<string, RelationTarget> | undefined;
|
|
105
|
-
/** Upsert filed rows into the store. False when the store cannot take them yet. */
|
|
106
|
-
writeFiled: (records: object[]) => Promise<boolean>;
|
|
103
|
+
/** Upsert filed rows into the store for `holder`, a parent's token. False when the store cannot take them yet. */
|
|
104
|
+
writeFiled: (records: object[], holder: object) => Promise<boolean>;
|
|
107
105
|
/**
|
|
108
106
|
* A parent's fetch may file and mark `field` once `settles` resolves; the
|
|
109
107
|
* target's own fetch for that subset waits for it (see docs/internals.md,
|
|
@@ -112,11 +110,16 @@ interface RelationTarget {
|
|
|
112
110
|
expectFiling: (field: string, settles: Promise<void>) => () => void;
|
|
113
111
|
/** Record that every row with `field === value` is now in this collection's store. */
|
|
114
112
|
markSubsetLoaded: (field: string, value: string) => void;
|
|
113
|
+
/** Release rows `holder` filed that no parent row files any more. */
|
|
114
|
+
releaseFiled: (ids: readonly string[], holder: object) => void;
|
|
115
|
+
/** Whether a `writeFiled` would land within the current task: the store is syncing and ready. */
|
|
116
|
+
isReady: () => boolean;
|
|
115
117
|
/**
|
|
116
|
-
* Hold this collection live for a parent
|
|
117
|
-
* rows the parent filed here (query mode only).
|
|
118
|
+
* Hold this collection live for a parent identified by `holder`, with the
|
|
119
|
+
* filters covering the rows the parent filed here (query mode only).
|
|
120
|
+
* Releasing the hold releases every row filed under `holder`.
|
|
118
121
|
*/
|
|
119
|
-
holdLive: () => HeldTarget;
|
|
122
|
+
holdLive: (holder: object) => HeldTarget;
|
|
120
123
|
}
|
|
121
124
|
/**
|
|
122
125
|
* A parent's hold on a relation target, returned by `holdLive`.
|
|
@@ -205,7 +208,7 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
205
208
|
* @example
|
|
206
209
|
* ```ts
|
|
207
210
|
* // Allow inserting without created, updated (server-generated timestamps)
|
|
208
|
-
* const booksCollection = createCollection<Schema>(pb
|
|
211
|
+
* const booksCollection = createCollection<Schema>(pb)('books', {
|
|
209
212
|
* omitOnInsert: ['created', 'updated'] as const
|
|
210
213
|
* });
|
|
211
214
|
*
|
|
@@ -234,20 +237,21 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
234
237
|
* @example
|
|
235
238
|
* ```ts
|
|
236
239
|
* // Use default automatic handler (recommended)
|
|
237
|
-
* const collection = createCollection<Schema>(pb
|
|
240
|
+
* const collection = createCollection<Schema>(pb)('books');
|
|
238
241
|
*
|
|
239
242
|
* // Custom handler
|
|
240
|
-
* const collection = createCollection<Schema>(pb
|
|
243
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
241
244
|
* onInsert: async ({ transaction }) => {
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
245
|
+
* const created = await Promise.all(
|
|
246
|
+
* transaction.mutations.map(mutation => customInsertLogic(mutation.modified))
|
|
247
|
+
* );
|
|
248
|
+
* // Land the server rows before the optimistic state drops
|
|
249
|
+
* await collection.accept(created);
|
|
246
250
|
* }
|
|
247
251
|
* });
|
|
248
252
|
*
|
|
249
253
|
* // Disable inserts (read-only collection)
|
|
250
|
-
* const collection = createCollection<Schema>(pb
|
|
254
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
251
255
|
* onInsert: false
|
|
252
256
|
* });
|
|
253
257
|
* ```
|
|
@@ -266,20 +270,23 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
266
270
|
* @example
|
|
267
271
|
* ```ts
|
|
268
272
|
* // Use default automatic handler (recommended)
|
|
269
|
-
* const collection = createCollection<Schema>(pb
|
|
273
|
+
* const collection = createCollection<Schema>(pb)('books');
|
|
270
274
|
*
|
|
271
275
|
* // Custom handler
|
|
272
|
-
* const collection = createCollection<Schema>(pb
|
|
276
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
273
277
|
* onUpdate: async ({ transaction }) => {
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
+
* const updated = await Promise.all(
|
|
279
|
+
* transaction.mutations.map(mutation =>
|
|
280
|
+
* customUpdateLogic(mutation.original.id, mutation.changes)
|
|
281
|
+
* )
|
|
282
|
+
* );
|
|
283
|
+
* // Land the server rows before the optimistic state drops
|
|
284
|
+
* await collection.accept(updated);
|
|
278
285
|
* }
|
|
279
286
|
* });
|
|
280
287
|
*
|
|
281
288
|
* // Disable updates (read-only collection)
|
|
282
|
-
* const collection = createCollection<Schema>(pb
|
|
289
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
283
290
|
* onUpdate: false
|
|
284
291
|
* });
|
|
285
292
|
* ```
|
|
@@ -297,41 +304,44 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
297
304
|
* @example
|
|
298
305
|
* ```ts
|
|
299
306
|
* // Use default automatic handler (recommended)
|
|
300
|
-
* const collection = createCollection<Schema>(pb
|
|
307
|
+
* const collection = createCollection<Schema>(pb)('books');
|
|
301
308
|
*
|
|
302
309
|
* // Custom handler
|
|
303
|
-
* const collection = createCollection<Schema>(pb
|
|
310
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
304
311
|
* onDelete: async ({ transaction }) => {
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* await
|
|
312
|
+
* const ids = transaction.mutations.map(mutation => mutation.original.id);
|
|
313
|
+
* await Promise.all(ids.map(id => customDeleteLogic(id)));
|
|
314
|
+
* // Remove the rows before the optimistic state drops
|
|
315
|
+
* await collection.evict(ids);
|
|
309
316
|
* }
|
|
310
317
|
* });
|
|
311
318
|
*
|
|
312
319
|
* // Disable deletes (read-only collection)
|
|
313
|
-
* const collection = createCollection<Schema>(pb
|
|
320
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
314
321
|
* onDelete: false
|
|
315
322
|
* });
|
|
316
323
|
* ```
|
|
317
324
|
*/
|
|
318
325
|
onDelete?: DeleteMutationFn<ExtractRecordType<Schema, CollectionName>> | false;
|
|
319
326
|
/**
|
|
320
|
-
* If true,
|
|
321
|
-
* update, or delete
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* Only affects the built-in default handlers.
|
|
328
|
-
* onInsert/onUpdate
|
|
327
|
+
* If true, the built-in handlers reload the collection's live subsets
|
|
328
|
+
* after a successful insert, update, or delete, before the mutation
|
|
329
|
+
* settles. Defaults to false: the built-in handlers write the server
|
|
330
|
+
* response into the synced layer before they settle, and the realtime
|
|
331
|
+
* subscription reconciles everything else. Set true when a server-side
|
|
332
|
+
* hook changes rows you must read right after the mutation.
|
|
333
|
+
*
|
|
334
|
+
* Only affects the built-in default handlers. A custom
|
|
335
|
+
* onInsert/onUpdate should land the server response with
|
|
336
|
+
* `await collection.accept(rows)` before it returns, and a custom
|
|
337
|
+
* onDelete should `await collection.evict(ids)`. A custom handler that
|
|
338
|
+
* needs a refetch can call `await collection.reload()` instead.
|
|
329
339
|
*
|
|
330
340
|
* @default false
|
|
331
341
|
*
|
|
332
342
|
* @example
|
|
333
343
|
* ```ts
|
|
334
|
-
* const collection = createCollection<Schema>(pb
|
|
344
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
335
345
|
* refetchOnMutation: true,
|
|
336
346
|
* });
|
|
337
347
|
* ```
|
|
@@ -353,10 +363,10 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
353
363
|
* @example
|
|
354
364
|
* ```ts
|
|
355
365
|
* // Default: eager mode - client-side filtering
|
|
356
|
-
* const collection = createCollection<Schema>(pb
|
|
366
|
+
* const collection = createCollection<Schema>(pb)('books');
|
|
357
367
|
*
|
|
358
368
|
* // On-demand mode - server-side filtering
|
|
359
|
-
* const collection = createCollection<Schema>(pb
|
|
369
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
360
370
|
* syncMode: 'on-demand'
|
|
361
371
|
* });
|
|
362
372
|
* ```
|
|
@@ -377,7 +387,7 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
377
387
|
*
|
|
378
388
|
* @example
|
|
379
389
|
* ```ts
|
|
380
|
-
* const books = createCollection<Schema>(pb
|
|
390
|
+
* const books = createCollection<Schema>(pb)('books', {
|
|
381
391
|
* syncMode: 'on-demand',
|
|
382
392
|
* realtime: 'query',
|
|
383
393
|
* });
|
|
@@ -385,35 +395,39 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
385
395
|
*/
|
|
386
396
|
realtime?: RealtimeMode;
|
|
387
397
|
/**
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
* @
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
398
|
+
* How long, in milliseconds, an on-demand subset stays loaded after its
|
|
399
|
+
* last live query unsubscribes. A query with an equal request that mounts
|
|
400
|
+
* within the window reuses the rows with no request; realtime keeps them
|
|
401
|
+
* fresh meanwhile. Rows a parent filed through a relation follow the
|
|
402
|
+
* same window, and so does the accepted holder a mutation write-back or
|
|
403
|
+
* `accept()` gives a row. `0` releases a subset as soon as it unloads
|
|
404
|
+
* and keeps an accepted row until a reload or idle. A `reload()`
|
|
405
|
+
* releases every waiting subset.
|
|
406
|
+
*
|
|
407
|
+
* @default 5000
|
|
408
|
+
*/
|
|
409
|
+
subsetGcTime?: number;
|
|
410
|
+
/**
|
|
411
|
+
* Waits, in milliseconds, before each retry of a failed subset or eager
|
|
412
|
+
* load. The last delay repeats until the load succeeds, so a live query
|
|
413
|
+
* stays loading through an outage instead of entering an error state it
|
|
414
|
+
* cannot leave; a `reload()` or a realtime reconnect retries at once. A
|
|
415
|
+
* response the server gave on purpose (a 4xx other than 401, 408 or
|
|
416
|
+
* 429) is not retried and reports its error; a 401 waits for the next
|
|
417
|
+
* auth change. Each delay carries up to a quarter of jitter either way.
|
|
418
|
+
* `[]` disables retries.
|
|
419
|
+
*
|
|
420
|
+
* @default [1000, 2000, 4000, 8000, 15000, 30000]
|
|
407
421
|
*/
|
|
408
|
-
|
|
422
|
+
loadRetryDelays?: readonly number[];
|
|
409
423
|
/**
|
|
410
424
|
* Additional options passed directly to the underlying TanStack DB collection.
|
|
411
425
|
* Use this to configure indexing, garbage collection, comparison functions,
|
|
412
426
|
* and any other TanStack DB collection options not explicitly exposed by pbtsdb.
|
|
413
427
|
*
|
|
414
|
-
* Options set here are spread into the `
|
|
428
|
+
* Options set here are spread into the TanStack DB `createCollection()` call.
|
|
415
429
|
* Fields managed by pbtsdb (`getKey`, `syncMode`, `onInsert`, `onUpdate`,
|
|
416
|
-
* `onDelete`, `schema`) are excluded from the type.
|
|
430
|
+
* `onDelete`, `schema`, `utils`) are excluded from the type.
|
|
417
431
|
*
|
|
418
432
|
* pbtsdb defaults `autoIndex` to `'eager'` with `defaultIndexType: BTreeIndex`
|
|
419
433
|
* so `orderBy` + `limit` queries page lazily; both can be overridden here.
|
|
@@ -421,7 +435,7 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
421
435
|
* @example
|
|
422
436
|
* ```ts
|
|
423
437
|
* import { BasicIndex } from 'pbtsdb'
|
|
424
|
-
* const collection = createCollection<Schema>(pb
|
|
438
|
+
* const collection = createCollection<Schema>(pb)('books', {
|
|
425
439
|
* collectionOptions: {
|
|
426
440
|
* autoIndex: 'off',
|
|
427
441
|
* gcTime: 60000,
|
|
@@ -429,7 +443,7 @@ interface CreateCollectionOptions<Schema extends SchemaDeclaration, CollectionNa
|
|
|
429
443
|
* });
|
|
430
444
|
* ```
|
|
431
445
|
*/
|
|
432
|
-
collectionOptions?: Omit<Partial<BaseCollectionConfig<ExtractRecordType<Schema, CollectionName>, string | number>>, 'getKey' | 'syncMode' | 'onInsert' | 'onUpdate' | 'onDelete' | 'schema'>;
|
|
446
|
+
collectionOptions?: Omit<Partial<BaseCollectionConfig<ExtractRecordType<Schema, CollectionName>, string | number>>, 'getKey' | 'syncMode' | 'onInsert' | 'onUpdate' | 'onDelete' | 'schema' | 'utils'>;
|
|
433
447
|
}
|
|
434
448
|
|
|
435
449
|
type RelationTargets = Record<string, RelationTarget>;
|
|
@@ -452,30 +466,92 @@ interface CreateCollectionFactoryOptions {
|
|
|
452
466
|
subscribeOptions?: () => RecordSubscribeOptions | undefined;
|
|
453
467
|
}
|
|
454
468
|
/**
|
|
455
|
-
*
|
|
469
|
+
* pbtsdb's utilities on `collection.utils`. The same functions are also
|
|
470
|
+
* assigned on the collection itself.
|
|
471
|
+
*/
|
|
472
|
+
interface PbCollectionUtils<T extends object> extends UtilsRecord {
|
|
473
|
+
/**
|
|
474
|
+
* Land rows the server returned as confirmed state, for example a custom
|
|
475
|
+
* endpoint's response. A row older than the stored one is ignored.
|
|
476
|
+
* Resolves when the rows are accepted; they become visible with the
|
|
477
|
+
* settlement of any persisting mutation, so a mutation handler can await it.
|
|
478
|
+
* Throws on an eager collection that is idle: it does not start a full load.
|
|
479
|
+
*/
|
|
480
|
+
accept: (rows: readonly T[]) => Promise<void>;
|
|
481
|
+
/**
|
|
482
|
+
* Refetch every live query's subset (the whole collection in eager mode)
|
|
483
|
+
* and release the realtime-topic and accepted holders of rows the results
|
|
484
|
+
* do not confirm; rows a subset or a parent holds stay. Resolves when the
|
|
485
|
+
* rows are accepted; they become visible with the settlement of any
|
|
486
|
+
* persisting mutation.
|
|
487
|
+
*/
|
|
488
|
+
reload: () => Promise<void>;
|
|
489
|
+
/**
|
|
490
|
+
* Remove rows the server deleted, for example after a custom endpoint
|
|
491
|
+
* deleted them. The rows leave every holder, and a fetch in flight does
|
|
492
|
+
* not put them back. Resolves when the removal is accepted, so a custom
|
|
493
|
+
* delete handler can await it. A no-op while the collection is not
|
|
494
|
+
* syncing.
|
|
495
|
+
*/
|
|
496
|
+
evict: (ids: readonly string[]) => Promise<void>;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Helpers added to collection instances.
|
|
456
500
|
* @internal
|
|
457
501
|
*/
|
|
458
|
-
interface CollectionSubscriptionHelpers {
|
|
502
|
+
interface CollectionSubscriptionHelpers<T extends object> {
|
|
459
503
|
/** The PocketBase collection name */
|
|
460
504
|
collectionName: string;
|
|
461
505
|
/** Wait for subscription to be established (useful in tests) */
|
|
462
506
|
waitForSubscription: (timeout?: number) => Promise<void>;
|
|
463
507
|
/** Check if collection has an active subscription */
|
|
464
508
|
isSubscribed: () => boolean;
|
|
465
|
-
/**
|
|
509
|
+
/**
|
|
510
|
+
* Relation targets declared through `relations`. Relation plumbing, not public API.
|
|
511
|
+
* @internal
|
|
512
|
+
*/
|
|
466
513
|
relationTargets: RelationTargets | undefined;
|
|
467
|
-
/**
|
|
514
|
+
/**
|
|
515
|
+
* Number of relation targets currently held live. Relation plumbing, not public API.
|
|
516
|
+
* @internal
|
|
517
|
+
*/
|
|
468
518
|
heldRelationTargetCount: () => number;
|
|
469
|
-
/**
|
|
519
|
+
/**
|
|
520
|
+
* Record that every row with `field === value` is now in this collection's store. Relation plumbing, not public API.
|
|
521
|
+
* @internal
|
|
522
|
+
*/
|
|
470
523
|
markSubsetLoaded: (field: string, value: string) => void;
|
|
471
|
-
/**
|
|
524
|
+
/**
|
|
525
|
+
* Number of field/value pairs currently marked loaded. Relation plumbing, not public API.
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
472
528
|
loadedSubsetCount: () => number;
|
|
473
|
-
/**
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
529
|
+
/**
|
|
530
|
+
* Hold this collection live as a relation target; see RelationTarget.holdLive. Relation plumbing, not public API.
|
|
531
|
+
* @internal
|
|
532
|
+
*/
|
|
533
|
+
holdLive: (holder: object) => HeldTarget;
|
|
534
|
+
/**
|
|
535
|
+
* Receive rows a parent expanded into this collection; see RelationTarget.writeFiled. Relation plumbing, not public API.
|
|
536
|
+
* @internal
|
|
537
|
+
*/
|
|
538
|
+
writeFiled: (records: object[], holder: object) => Promise<boolean>;
|
|
539
|
+
/**
|
|
540
|
+
* Release rows a parent stopped filing here; see RelationTarget.releaseFiled. Relation plumbing, not public API.
|
|
541
|
+
* @internal
|
|
542
|
+
*/
|
|
543
|
+
releaseFiled: (ids: readonly string[], holder: object) => void;
|
|
544
|
+
/**
|
|
545
|
+
* A parent's fetch may file a subset here; see RelationTarget.expectFiling. Relation plumbing, not public API.
|
|
546
|
+
* @internal
|
|
547
|
+
*/
|
|
478
548
|
expectFiling: (field: string, settles: Promise<void>) => () => void;
|
|
549
|
+
/** See {@link PbCollectionUtils.accept}. */
|
|
550
|
+
accept: PbCollectionUtils<T>['accept'];
|
|
551
|
+
/** See {@link PbCollectionUtils.reload}. */
|
|
552
|
+
reload: PbCollectionUtils<T>['reload'];
|
|
553
|
+
/** See {@link PbCollectionUtils.evict}. */
|
|
554
|
+
evict: PbCollectionUtils<T>['evict'];
|
|
479
555
|
}
|
|
480
556
|
|
|
481
557
|
/**
|
|
@@ -484,7 +560,7 @@ interface CollectionSubscriptionHelpers {
|
|
|
484
560
|
* Views are created by {@link PbCollectionView.fetchRelations} and
|
|
485
561
|
* {@link PbCollectionView.withRealtime}, and compose in either order.
|
|
486
562
|
*/
|
|
487
|
-
type PbCollectionView<Schema extends SchemaDeclaration, C extends keyof Schema & string, Opts> = Collection<ExtractRecordType<Schema, C>, string | number,
|
|
563
|
+
type PbCollectionView<Schema extends SchemaDeclaration, C extends keyof Schema & string, Opts> = Collection<ExtractRecordType<Schema, C>, string | number, PbCollectionUtils<ExtractRecordType<Schema, C>>, never, InsertInputOf<Schema, C, Opts>> & CollectionSubscriptionHelpers<ExtractRecordType<Schema, C>> & {
|
|
488
564
|
/** The PocketBase collection name */
|
|
489
565
|
readonly collectionName: C;
|
|
490
566
|
/** @internal phantom; never present at runtime */
|
|
@@ -516,13 +592,13 @@ type AlwaysFetchRelationsCheck<Opts> = {
|
|
|
516
592
|
* Use this when you need fine-grained control or need to create collections with dependencies.
|
|
517
593
|
*
|
|
518
594
|
* @param pb - PocketBase client instance
|
|
519
|
-
* @param
|
|
595
|
+
* @param factoryOptions - Options applied to every collection this factory creates
|
|
520
596
|
* @returns A curried function that takes collection name and options
|
|
521
597
|
*
|
|
522
598
|
* @example
|
|
523
599
|
* Basic usage:
|
|
524
600
|
* ```ts
|
|
525
|
-
* const booksCollection = createCollection<Schema>(pb
|
|
601
|
+
* const booksCollection = createCollection<Schema>(pb)('books', {});
|
|
526
602
|
*
|
|
527
603
|
* // Use directly
|
|
528
604
|
* const books = await booksCollection.getFullList();
|
|
@@ -531,8 +607,8 @@ type AlwaysFetchRelationsCheck<Opts> = {
|
|
|
531
607
|
* @example
|
|
532
608
|
* With relations fetched on every request:
|
|
533
609
|
* ```ts
|
|
534
|
-
* const authorsCollection = createCollection<Schema>(pb
|
|
535
|
-
* const booksCollection = createCollection<Schema>(pb
|
|
610
|
+
* const authorsCollection = createCollection<Schema>(pb)('authors', {});
|
|
611
|
+
* const booksCollection = createCollection<Schema>(pb)('books', {
|
|
536
612
|
* relations: { author: authorsCollection },
|
|
537
613
|
* alwaysFetchRelations: ['author'],
|
|
538
614
|
* });
|
|
@@ -545,7 +621,7 @@ type AlwaysFetchRelationsCheck<Opts> = {
|
|
|
545
621
|
* @example
|
|
546
622
|
* With a per-query fetchRelations view:
|
|
547
623
|
* ```ts
|
|
548
|
-
* const booksCollection = createCollection<Schema>(pb
|
|
624
|
+
* const booksCollection = createCollection<Schema>(pb)('books', {
|
|
549
625
|
* relations: { author: authorsCollection },
|
|
550
626
|
* });
|
|
551
627
|
*
|
|
@@ -555,7 +631,7 @@ type AlwaysFetchRelationsCheck<Opts> = {
|
|
|
555
631
|
* authorsCollection.get(data[0].author)?.name
|
|
556
632
|
* ```
|
|
557
633
|
*/
|
|
558
|
-
declare function createCollection<Schema extends SchemaDeclaration>(pb: PocketBase,
|
|
634
|
+
declare function createCollection<Schema extends SchemaDeclaration>(pb: PocketBase, factoryOptions?: CreateCollectionFactoryOptions): <C extends keyof Schema & string, const Opts extends CreateCollectionOptions<Schema, C>>(collectionName: C, options?: Opts & AlwaysFetchRelationsCheck<Opts>) => PbCollection<Schema, C, Opts>;
|
|
559
635
|
|
|
560
636
|
/**
|
|
561
637
|
* Logger interface for subscription events and internal operations.
|
|
@@ -628,6 +704,76 @@ type BasicExpression<T = unknown> = IR.BasicExpression<T>;
|
|
|
628
704
|
declare function convertToPocketBaseFilter(where: BasicExpression<boolean> | undefined | null): string | undefined;
|
|
629
705
|
declare function convertToPocketBaseSort(orderBy: IR.OrderBy | undefined | null): string | undefined;
|
|
630
706
|
|
|
707
|
+
/** The state of pbtsdb's realtime connection for one PocketBase client. */
|
|
708
|
+
type RealtimeStatus =
|
|
709
|
+
/** `disconnectRealtime(pb)` is in effect, or no collection has subscribed yet. */
|
|
710
|
+
{
|
|
711
|
+
state: 'disabled';
|
|
712
|
+
}
|
|
713
|
+
/** The first connection of a session is opening. */
|
|
714
|
+
| {
|
|
715
|
+
state: 'connecting';
|
|
716
|
+
}
|
|
717
|
+
/** The stream is open and `PB_CONNECT` was received. */
|
|
718
|
+
| {
|
|
719
|
+
state: 'connected';
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* The stream dropped, or never came up, and a retry is scheduled.
|
|
723
|
+
* `attempt` is the number of the retry, `nextRetryAt` when it fires and
|
|
724
|
+
* `since` when the connection was lost, both epoch milliseconds.
|
|
725
|
+
*/
|
|
726
|
+
| {
|
|
727
|
+
state: 'reconnecting';
|
|
728
|
+
attempt: number;
|
|
729
|
+
nextRetryAt: number;
|
|
730
|
+
since: number;
|
|
731
|
+
};
|
|
732
|
+
/** Loads that are not progressing, across every collection of one client. */
|
|
733
|
+
type LoadStatus = {
|
|
734
|
+
/** Live demands sleeping in their `loadRetryDelays` backoff. */
|
|
735
|
+
retrying: number;
|
|
736
|
+
/** When the oldest still-retrying load first failed, epoch milliseconds. */
|
|
737
|
+
failingSince?: number;
|
|
738
|
+
/** Live demands whose load ended in an error that is not retried, such as a 403 or 404. */
|
|
739
|
+
failed: number;
|
|
740
|
+
};
|
|
741
|
+
type SyncStatus = {
|
|
742
|
+
realtime: RealtimeStatus;
|
|
743
|
+
loads: LoadStatus;
|
|
744
|
+
};
|
|
745
|
+
type SyncStatusListener = (status: SyncStatus) => void;
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Forgets the shared realtime connection's server-side session and
|
|
749
|
+
* reconnects under `pb`'s current auth, re-sending every subscribed topic.
|
|
750
|
+
* pbtsdb does this itself when `pb.authStore` changes to another auth
|
|
751
|
+
* record; call it for a change the store cannot see, such as a server
|
|
752
|
+
* switch. It also lifts {@link disconnectRealtime}. A no-op if `pb` has no
|
|
753
|
+
* realtime connection yet.
|
|
754
|
+
*/
|
|
755
|
+
declare function resetRealtime(pb: PocketBase): void;
|
|
756
|
+
/**
|
|
757
|
+
* Closes pbtsdb's realtime connection for `pb` and keeps it closed: no
|
|
758
|
+
* connection opens until {@link resetRealtime}. Collections keep working
|
|
759
|
+
* over REST and keep their subscriptions registered, so a later
|
|
760
|
+
* `resetRealtime(pb)` resumes every topic and reloads every ready
|
|
761
|
+
* collection. Use it at logout, or at startup where realtime is not wanted.
|
|
762
|
+
*/
|
|
763
|
+
declare function disconnectRealtime(pb: PocketBase): void;
|
|
764
|
+
/**
|
|
765
|
+
* The sync state of `pb`'s collections: whether pbtsdb's realtime stream
|
|
766
|
+
* is up, and how many live loads are stuck in retry or ended in an error
|
|
767
|
+
* the server meant. The snapshot is stable until a value changes, so it
|
|
768
|
+
* suits `useSyncExternalStore`; see {@link subscribeSyncStatus}.
|
|
769
|
+
*/
|
|
770
|
+
declare function getSyncStatus(pb: PocketBase): SyncStatus;
|
|
771
|
+
/**
|
|
772
|
+
* Calls `listener` with each new {@link getSyncStatus} snapshot. Returns
|
|
773
|
+
* the unsubscribe function.
|
|
774
|
+
*/
|
|
775
|
+
declare function subscribeSyncStatus(pb: PocketBase, listener: SyncStatusListener): () => void;
|
|
776
|
+
|
|
631
777
|
/**
|
|
632
778
|
* Generates a new PocketBase-compatible record ID.
|
|
633
779
|
* Returns a 15-character alphanumeric string (lowercase letters and numbers).
|
|
@@ -643,4 +789,4 @@ declare function convertToPocketBaseSort(orderBy: IR.OrderBy | undefined | null)
|
|
|
643
789
|
*/
|
|
644
790
|
declare function newRecordId(): string;
|
|
645
791
|
|
|
646
|
-
export { type CreateCollectionFactoryOptions, type CreateCollectionOptions, type ExcludeUndefined, type ExpandPath, type ExtractRecordType, type ExtractRelations, type Logger, type OmittableFields, type PbCollection, type PbCollectionView, type RealtimeMode, type RelationAsCollection, type RelationsConfig, type SchemaDeclaration, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, newRecordId, resetLogger, setLogger };
|
|
792
|
+
export { type CreateCollectionFactoryOptions, type CreateCollectionOptions, type ExcludeUndefined, type ExpandPath, type ExtractRecordType, type ExtractRelations, type LoadStatus, type Logger, type OmittableFields, type PbCollection, type PbCollectionUtils, type PbCollectionView, type RealtimeMode, type RealtimeStatus, type RelationAsCollection, type RelationsConfig, type SchemaDeclaration, type SyncStatus, type SyncStatusListener, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, disconnectRealtime, getSyncStatus, newRecordId, resetLogger, resetRealtime, setLogger, subscribeSyncStatus };
|
package/dist/core.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { BTreeIndex, BasicIndex, ReverseIndex, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, createEffect, materialize, newRecordId, resetLogger, setLogger, toArray } from './chunk-
|
|
1
|
+
export { BTreeIndex, BasicIndex, ReverseIndex, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, createEffect, disconnectRealtime, getSyncStatus, materialize, newRecordId, resetLogger, resetRealtime, setLogger, subscribeSyncStatus, toArray } from './chunk-TTENGDS3.js';
|
|
2
2
|
//# sourceMappingURL=core.js.map
|
|
3
3
|
//# sourceMappingURL=core.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { BTreeIndex, BasicIndex, DeltaEvent, DeltaType, EffectConfig, EffectContext, IndexConstructor, ReverseIndex, createEffect, materialize, toArray } from '@tanstack/db';
|
|
2
|
+
import PocketBase from 'pocketbase';
|
|
2
3
|
export { RecordSubscribeOptions } from 'pocketbase';
|
|
3
|
-
|
|
4
|
+
import { SyncStatus } from './core.js';
|
|
5
|
+
export { CreateCollectionFactoryOptions, CreateCollectionOptions, ExcludeUndefined, ExpandPath, ExtractRecordType, ExtractRelations, LoadStatus, Logger, OmittableFields, PbCollection, PbCollectionUtils, PbCollectionView, RealtimeMode, RealtimeStatus, RelationAsCollection, RelationsConfig, SchemaDeclaration, SyncStatusListener, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, disconnectRealtime, getSyncStatus, newRecordId, resetLogger, resetRealtime, setLogger, subscribeSyncStatus } from './core.js';
|
|
4
6
|
import React, { ReactNode } from 'react';
|
|
5
|
-
import '@tanstack/query-db-collection';
|
|
6
|
-
import '@tanstack/react-query';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* UseStore hook type for variadic collection access.
|
|
@@ -51,7 +51,7 @@ interface ReactProviderResult<CollectionsMap> {
|
|
|
51
51
|
* import { useLiveQuery } from '@tanstack/react-db';
|
|
52
52
|
*
|
|
53
53
|
* // Step 1: Create collections
|
|
54
|
-
* const c = createCollection<Schema>(pb
|
|
54
|
+
* const c = createCollection<Schema>(pb);
|
|
55
55
|
* const collections = {
|
|
56
56
|
* books: c('books', {}),
|
|
57
57
|
* authors: c('authors', {}),
|
|
@@ -63,11 +63,9 @@ interface ReactProviderResult<CollectionsMap> {
|
|
|
63
63
|
* // Step 3: Wrap your app
|
|
64
64
|
* function App() {
|
|
65
65
|
* return (
|
|
66
|
-
* <
|
|
67
|
-
* <
|
|
68
|
-
*
|
|
69
|
-
* </Provider>
|
|
70
|
-
* </QueryClientProvider>
|
|
66
|
+
* <Provider>
|
|
67
|
+
* <BooksList />
|
|
68
|
+
* </Provider>
|
|
71
69
|
* );
|
|
72
70
|
* }
|
|
73
71
|
*
|
|
@@ -101,7 +99,7 @@ interface ReactProviderResult<CollectionsMap> {
|
|
|
101
99
|
* @example
|
|
102
100
|
* With relations filed via alwaysFetchRelations:
|
|
103
101
|
* ```tsx
|
|
104
|
-
* const c = createCollection<Schema>(pb
|
|
102
|
+
* const c = createCollection<Schema>(pb);
|
|
105
103
|
* const authors = c('authors', { syncMode: 'on-demand' });
|
|
106
104
|
* const books = c('books', {
|
|
107
105
|
* relations: { author: authors },
|
|
@@ -134,5 +132,21 @@ interface ReactProviderResult<CollectionsMap> {
|
|
|
134
132
|
* ```
|
|
135
133
|
*/
|
|
136
134
|
declare function createReactProvider<CollectionsMap extends Record<string, unknown>>(collections: CollectionsMap): ReactProviderResult<CollectionsMap>;
|
|
135
|
+
/**
|
|
136
|
+
* The current {@link SyncStatus} of `pb`'s collections, re-rendering on
|
|
137
|
+
* each change. Use it for a connectivity notice: the realtime stream
|
|
138
|
+
* dropping while REST still works, or queries stuck in retry.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* function SyncNotice() {
|
|
143
|
+
* const { realtime, loads } = useSyncStatus(pb);
|
|
144
|
+
* if (realtime.state === 'reconnecting') return <p>Reconnecting…</p>;
|
|
145
|
+
* if (loads.retrying > 0) return <p>Retrying {loads.retrying} queries</p>;
|
|
146
|
+
* return null;
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare function useSyncStatus(pb: PocketBase): SyncStatus;
|
|
137
151
|
|
|
138
|
-
export { type ReactProviderResult, createReactProvider };
|
|
152
|
+
export { type ReactProviderResult, SyncStatus, createReactProvider, useSyncStatus };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { subscribeSyncStatus, getSyncStatus } from './chunk-TTENGDS3.js';
|
|
2
|
+
export { BTreeIndex, BasicIndex, ReverseIndex, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, createEffect, disconnectRealtime, getSyncStatus, materialize, newRecordId, resetLogger, resetRealtime, setLogger, subscribeSyncStatus, toArray } from './chunk-TTENGDS3.js';
|
|
3
|
+
import { createContext, useSyncExternalStore, useContext } from 'react';
|
|
3
4
|
import { jsx } from 'react/jsx-runtime';
|
|
4
5
|
|
|
5
6
|
function createReactProvider(collections) {
|
|
@@ -24,7 +25,14 @@ function createReactProvider(collections) {
|
|
|
24
25
|
useStore
|
|
25
26
|
};
|
|
26
27
|
}
|
|
28
|
+
function useSyncStatus(pb) {
|
|
29
|
+
return useSyncExternalStore(
|
|
30
|
+
(listener) => subscribeSyncStatus(pb, listener),
|
|
31
|
+
() => getSyncStatus(pb),
|
|
32
|
+
() => getSyncStatus(pb)
|
|
33
|
+
);
|
|
34
|
+
}
|
|
27
35
|
|
|
28
|
-
export { createReactProvider };
|
|
36
|
+
export { createReactProvider, useSyncStatus };
|
|
29
37
|
//# sourceMappingURL=index.js.map
|
|
30
38
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":";;;;;AAoIO,SAAS,oBACZ,WAAA,EACmC;AACnC,EAAA,MAAM,OAAA,GAAU,cAAqC,IAAI,CAAA;AAEzD,EAAA,MAAM,QAAA,GAA8C,CAAC,EAAE,QAAA,EAAS,qBAC5D,GAAA,CAAC,OAAA,CAAQ,QAAA,EAAR,EAAiB,KAAA,EAAO,WAAA,EAAc,QAAA,EAAS,CAAA;AAGpD,EAAA,SAAS,YACF,IAAA,EACiF;AACpF,IAAA,MAAM,OAAA,GAAU,WAAW,OAAO,CAAA;AAElC,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AAEA,IAAA,OAAO,IAAA,CAAK,IAAI,CAAA,GAAA,KAAO;AACnB,MAAA,IAAI,EAAE,OAAO,OAAA,CAAA,EAAU;AACnB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,YAAA,EAAe,MAAA,CAAO,GAAG,CAAC,CAAA,0BAAA,CAA4B,CAAA;AAAA,MAC1E;AACA,MAAA,OAAO,QAAQ,GAAG,CAAA;AAAA,IACtB,CAAC,CAAA;AAAA,EACL;AAEA,EAAA,OAAO;AAAA,IACH,QAAA;AAAA,IACA;AAAA,GACJ;AACJ;AAiBO,SAAS,cAAc,EAAA,EAA4B;AACtD,EAAA,OAAO,oBAAA;AAAA,IACH,CAAA,QAAA,KAAY,mBAAA,CAAoB,EAAA,EAAI,QAAQ,CAAA;AAAA,IAC5C,MAAM,cAAc,EAAE,CAAA;AAAA,IACtB,MAAM,cAAc,EAAE;AAAA,GAC1B;AACJ","file":"index.js","sourcesContent":["import type PocketBase from 'pocketbase'\nimport React, { createContext, type ReactNode, useContext, useSyncExternalStore } from 'react'\nimport type { SyncStatus } from './sync-status'\nimport { getSyncStatus, subscribeSyncStatus } from './transport'\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);\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 * <Provider>\n * <BooksList />\n * </Provider>\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 relations filed via alwaysFetchRelations:\n * ```tsx\n * const c = createCollection<Schema>(pb);\n * const authors = c('authors', { syncMode: 'on-demand' });\n * const books = c('books', {\n * relations: { author: authors },\n * alwaysFetchRelations: ['author'],\n * });\n *\n * const { Provider, useStore } = createReactProvider({ authors, books });\n *\n * function BooksWithAuthors() {\n * const [books, authors] = useStore('books', 'authors');\n * const { data } = useLiveQuery((q) =>\n * q.from({ b: books }).select(({ b }) => ({\n * ...b,\n * author: materialize(\n * q.from({ a: authors }).where(({ a }) => eq(a.id, b.author)).findOne()\n * ),\n * }))\n * );\n *\n * return (\n * <ul>\n * {data?.map(row => (\n * <li key={row.id}>\n * {row.title} by {row.author?.name}\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function createReactProvider<CollectionsMap extends Record<string, unknown>>(\n collections: CollectionsMap\n): ReactProviderResult<CollectionsMap> {\n const Context = createContext<CollectionsMap | null>(null)\n\n const Provider: React.FC<{ children: ReactNode }> = ({ children }) => (\n <Context.Provider value={collections}>{children}</Context.Provider>\n )\n\n function useStore<K extends (keyof CollectionsMap)[]>(\n ...keys: K\n ): { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never } {\n const context = useContext(Context)\n\n if (!context) {\n throw new Error(\n 'useStore must be used within the Provider returned by createReactProvider'\n )\n }\n\n return keys.map(key => {\n if (!(key in context)) {\n throw new Error(`Collection \"${String(key)}\" not found in collections`)\n }\n return context[key]\n }) as { [I in keyof K]: K[I] extends keyof CollectionsMap ? CollectionsMap[K[I]] : never }\n }\n\n return {\n Provider,\n useStore: useStore as UseStoreFn<CollectionsMap>,\n }\n}\n\n/**\n * The current {@link SyncStatus} of `pb`'s collections, re-rendering on\n * each change. Use it for a connectivity notice: the realtime stream\n * dropping while REST still works, or queries stuck in retry.\n *\n * @example\n * ```tsx\n * function SyncNotice() {\n * const { realtime, loads } = useSyncStatus(pb);\n * if (realtime.state === 'reconnecting') return <p>Reconnecting…</p>;\n * if (loads.retrying > 0) return <p>Retrying {loads.retrying} queries</p>;\n * return null;\n * }\n * ```\n */\nexport function useSyncStatus(pb: PocketBase): SyncStatus {\n return useSyncExternalStore(\n listener => subscribeSyncStatus(pb, listener),\n () => getSyncStatus(pb),\n () => getSyncStatus(pb)\n )\n}\n"]}
|