pbtsdb 2.0.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 +70 -0
- package/dist/{chunk-4CRCARB3.js → chunk-TTENGDS3.js} +223 -35
- package/dist/chunk-TTENGDS3.js.map +1 -0
- package/dist/core.d.ts +53 -1
- package/dist/core.js +1 -1
- package/dist/index.d.ts +20 -2
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4CRCARB3.js.map +0 -1
package/dist/core.d.ts
CHANGED
|
@@ -704,6 +704,46 @@ type BasicExpression<T = unknown> = IR.BasicExpression<T>;
|
|
|
704
704
|
declare function convertToPocketBaseFilter(where: BasicExpression<boolean> | undefined | null): string | undefined;
|
|
705
705
|
declare function convertToPocketBaseSort(orderBy: IR.OrderBy | undefined | null): string | undefined;
|
|
706
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
|
+
|
|
707
747
|
/**
|
|
708
748
|
* Forgets the shared realtime connection's server-side session and
|
|
709
749
|
* reconnects under `pb`'s current auth, re-sending every subscribed topic.
|
|
@@ -721,6 +761,18 @@ declare function resetRealtime(pb: PocketBase): void;
|
|
|
721
761
|
* collection. Use it at logout, or at startup where realtime is not wanted.
|
|
722
762
|
*/
|
|
723
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;
|
|
724
776
|
|
|
725
777
|
/**
|
|
726
778
|
* Generates a new PocketBase-compatible record ID.
|
|
@@ -737,4 +789,4 @@ declare function disconnectRealtime(pb: PocketBase): void;
|
|
|
737
789
|
*/
|
|
738
790
|
declare function newRecordId(): string;
|
|
739
791
|
|
|
740
|
-
export { type CreateCollectionFactoryOptions, type CreateCollectionOptions, type ExcludeUndefined, type ExpandPath, type ExtractRecordType, type ExtractRelations, type Logger, type OmittableFields, type PbCollection, type PbCollectionUtils, type PbCollectionView, type RealtimeMode, type RelationAsCollection, type RelationsConfig, type SchemaDeclaration, convertToPocketBaseFilter, convertToPocketBaseSort, createCollection, disconnectRealtime, newRecordId, resetLogger, resetRealtime, 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, disconnectRealtime, materialize, newRecordId, resetLogger, resetRealtime, 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,6 +1,8 @@
|
|
|
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
7
|
|
|
6
8
|
/**
|
|
@@ -130,5 +132,21 @@ interface ReactProviderResult<CollectionsMap> {
|
|
|
130
132
|
* ```
|
|
131
133
|
*/
|
|
132
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;
|
|
133
151
|
|
|
134
|
-
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"]}
|