@tanstack/solid-db 0.2.12 → 0.2.14
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactiveMap } from '@solid-primitives/map';
|
|
2
2
|
import { Accessor } from 'solid-js';
|
|
3
|
-
import { Collection, CollectionStatus, Context, GetResult, InitialQueryBuilder, LiveQueryCollectionConfig, QueryBuilder } from '@tanstack/db';
|
|
3
|
+
import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
|
|
4
4
|
/**
|
|
5
5
|
* Create a live query using a query function
|
|
6
6
|
* @param queryFn - Query function that defines what data to fetch
|
|
@@ -70,12 +70,12 @@ import { Collection, CollectionStatus, Context, GetResult, InitialQueryBuilder,
|
|
|
70
70
|
* </Suspense>
|
|
71
71
|
* )
|
|
72
72
|
*/
|
|
73
|
-
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>): Accessor<
|
|
73
|
+
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>): Accessor<InferResultType<TContext>> & {
|
|
74
74
|
/**
|
|
75
75
|
* @deprecated use function result instead
|
|
76
76
|
* query.data -> query()
|
|
77
77
|
*/
|
|
78
|
-
data:
|
|
78
|
+
data: InferResultType<TContext>;
|
|
79
79
|
state: ReactiveMap<string | number, GetResult<TContext>>;
|
|
80
80
|
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
81
81
|
status: CollectionStatus;
|
|
@@ -85,12 +85,12 @@ export declare function useLiveQuery<TContext extends Context>(queryFn: (q: Init
|
|
|
85
85
|
isError: boolean;
|
|
86
86
|
isCleanedUp: boolean;
|
|
87
87
|
};
|
|
88
|
-
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): Accessor<
|
|
88
|
+
export declare function useLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): Accessor<InferResultType<TContext>> & {
|
|
89
89
|
/**
|
|
90
90
|
* @deprecated use function result instead
|
|
91
91
|
* query.data -> query()
|
|
92
92
|
*/
|
|
93
|
-
data:
|
|
93
|
+
data: InferResultType<TContext>;
|
|
94
94
|
state: ReactiveMap<string | number, GetResult<TContext>>;
|
|
95
95
|
collection: Collection<GetResult<TContext>, string | number, {}> | null;
|
|
96
96
|
status: CollectionStatus | `disabled`;
|
|
@@ -140,12 +140,12 @@ export declare function useLiveQuery<TContext extends Context>(queryFn: (q: Init
|
|
|
140
140
|
* </Switch>
|
|
141
141
|
* )
|
|
142
142
|
*/
|
|
143
|
-
export declare function useLiveQuery<TContext extends Context>(config: Accessor<LiveQueryCollectionConfig<TContext>>): Accessor<
|
|
143
|
+
export declare function useLiveQuery<TContext extends Context>(config: Accessor<LiveQueryCollectionConfig<TContext>>): Accessor<InferResultType<TContext>> & {
|
|
144
144
|
/**
|
|
145
145
|
* @deprecated use function result instead
|
|
146
146
|
* query.data -> query()
|
|
147
147
|
*/
|
|
148
|
-
data:
|
|
148
|
+
data: InferResultType<TContext>;
|
|
149
149
|
state: ReactiveMap<string | number, GetResult<TContext>>;
|
|
150
150
|
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
151
151
|
status: CollectionStatus;
|
|
@@ -190,7 +190,7 @@ export declare function useLiveQuery<TContext extends Context>(config: Accessor<
|
|
|
190
190
|
* </Switch>
|
|
191
191
|
* )
|
|
192
192
|
*/
|
|
193
|
-
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils
|
|
193
|
+
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils> & NonSingleResult>): Accessor<Array<TResult>> & {
|
|
194
194
|
/**
|
|
195
195
|
* @deprecated use function result instead
|
|
196
196
|
* query.data -> query()
|
|
@@ -205,3 +205,18 @@ export declare function useLiveQuery<TResult extends object, TKey extends string
|
|
|
205
205
|
isError: boolean;
|
|
206
206
|
isCleanedUp: boolean;
|
|
207
207
|
};
|
|
208
|
+
export declare function useLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils> & SingleResult>): Accessor<TResult | undefined> & {
|
|
209
|
+
/**
|
|
210
|
+
* @deprecated use function result instead
|
|
211
|
+
* query.data -> query()
|
|
212
|
+
*/
|
|
213
|
+
data: TResult | undefined;
|
|
214
|
+
state: ReactiveMap<TKey, TResult>;
|
|
215
|
+
collection: Collection<TResult, TKey, TUtils> & SingleResult;
|
|
216
|
+
status: CollectionStatus;
|
|
217
|
+
isLoading: boolean;
|
|
218
|
+
isReady: boolean;
|
|
219
|
+
isIdle: boolean;
|
|
220
|
+
isError: boolean;
|
|
221
|
+
isCleanedUp: boolean;
|
|
222
|
+
};
|
package/dist/esm/useLiveQuery.js
CHANGED
|
@@ -112,6 +112,14 @@ function useLiveQuery(configOrQueryOrCollection) {
|
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
114
|
function getData() {
|
|
115
|
+
const currentCollection = collection();
|
|
116
|
+
if (currentCollection) {
|
|
117
|
+
const config = currentCollection.config;
|
|
118
|
+
if (config.singleResult) {
|
|
119
|
+
getDataResource();
|
|
120
|
+
return data[0];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
115
123
|
return getDataResource();
|
|
116
124
|
}
|
|
117
125
|
Object.defineProperties(getData, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveQuery.js","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import {\n batch,\n createEffect,\n createMemo,\n createResource,\n createSignal,\n onCleanup,\n} from 'solid-js'\nimport { ReactiveMap } from '@solid-primitives/map'\nimport {\n BaseQueryBuilder,\n CollectionImpl,\n createLiveQueryCollection,\n} from '@tanstack/db'\nimport { createStore, reconcile } from 'solid-js/store'\nimport type { Accessor } from 'solid-js'\nimport type {\n ChangeMessage,\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n QueryBuilder,\n} from '@tanstack/db'\n\n/**\n * Create a live query using a query function\n * @param queryFn - Query function that defines what data to fetch\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Basic query with object syntax\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.completed, false))\n * .select(({ todos }) => ({ id: todos.id, text: todos.text }))\n * )\n *\n * @example\n * // With dependencies that trigger re-execution\n * const todosQuery = useLiveQuery(\n * (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => gt(todos.priority, minPriority())),\n * )\n *\n * @example\n * // Join pattern\n * const personIssues = useLiveQuery((q) =>\n * q.from({ issues: issueCollection })\n * .join({ persons: personCollection }, ({ issues, persons }) =>\n * eq(issues.userId, persons.id)\n * )\n * .select(({ issues, persons }) => ({\n * id: issues.id,\n * title: issues.title,\n * userName: persons.name\n * }))\n * )\n *\n * @example\n * // Handle loading and error states\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * return (\n * <Switch>\n * <Match when={todosQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={todosQuery.isError}>\n * <div>Error: {todosQuery.status}</div>\n * </Match>\n * <Match when={todosQuery.isReady}>\n * <For each={todosQuery()}>\n * {(todo) => <li key={todo.id}>{todo.text}</li>}\n * </For>\n * </Match>\n * </Switch>\n * )\n *\n * @example\n * // Use Suspense boundaries\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * return (\n * <Suspense fallback={<div>Loading...</div>}>\n * <For each={todosQuery()}>\n * {(todo) => <li key={todo.id}>{todo.text}</li>}\n * </For>\n * </Suspense>\n * )\n */\n// Overload 1: Accept query function that always returns QueryBuilder\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): Accessor<Array<GetResult<TContext>>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: Array<GetResult<TContext>>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 1b: Accept query function that can return undefined/null\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): Accessor<Array<GetResult<TContext>>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: Array<GetResult<TContext>>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}> | null\n status: CollectionStatus | `disabled`\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Create a live query using configuration object\n * @param config - Configuration object with query and options\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Basic config object usage\n * const todosQuery = useLiveQuery(() => ({\n * query: (q) => q.from({ todos: todosCollection }),\n * gcTime: 60000\n * }))\n *\n * @example\n * // With query builder and options\n * const queryBuilder = new Query()\n * .from({ persons: collection })\n * .where(({ persons }) => gt(persons.age, 30))\n * .select(({ persons }) => ({ id: persons.id, name: persons.name }))\n *\n * const personsQuery = useLiveQuery(() => ({ query: queryBuilder }))\n *\n * @example\n * // Handle all states uniformly\n * const itemsQuery = useLiveQuery(() => ({\n * query: (q) => q.from({ items: itemCollection })\n * }))\n *\n * return (\n * <Switch fallback={<div>{itemsQuery().length} items loaded</div>}>\n * <Match when={itemsQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={itemsQuery.isError}>\n * <div>Something went wrong</div>\n * </Match>\n * <Match when={!itemsQuery.isReady}>\n * <div>Preparing...</div>\n * </Match>\n * </Switch>\n * )\n */\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: Accessor<LiveQueryCollectionConfig<TContext>>,\n): Accessor<Array<GetResult<TContext>>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: Array<GetResult<TContext>>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Subscribe to an existing live query collection\n * @param liveQueryCollection - Pre-created live query collection to subscribe to\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Using pre-created live query collection\n * const myLiveQuery = createLiveQueryCollection((q) =>\n * q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))\n * )\n * const todosQuery = useLiveQuery(() => myLiveQuery)\n *\n * @example\n * // Access collection methods directly\n * const existingQuery = useLiveQuery(() => existingCollection)\n *\n * // Use collection for mutations\n * const handleToggle = (id) => {\n * existingQuery.collection.update(id, draft => { draft.completed = !draft.completed })\n * }\n *\n * @example\n * // Handle states consistently\n * const sharedQuery = useLiveQuery(() => sharedCollection)\n *\n * return (\n * <Switch fallback={<div><For each={sharedQuery()}>{(item) => <Item key={item.id} {...item} />}</For></div>}>\n * <Match when={sharedQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={sharedQuery.isError}>\n * <div>Error loading data</div>\n * </Match>\n * </Switch>\n * )\n */\n// Overload 3: Accept pre-created live query collection\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Accessor<Collection<TResult, TKey, TUtils>>,\n): Accessor<Array<TResult>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: Array<TResult>\n state: ReactiveMap<TKey, TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: (queryFn?: any) => any,\n) {\n const collection = createMemo(\n () => {\n if (configOrQueryOrCollection.length === 1) {\n // This is a query function - check if it returns null/undefined\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = configOrQueryOrCollection(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n })\n }\n\n const innerCollection = configOrQueryOrCollection()\n\n if (innerCollection === undefined || innerCollection === null) {\n // Disabled query - return null\n return null\n }\n\n if (innerCollection instanceof CollectionImpl) {\n innerCollection.startSyncImmediate()\n return innerCollection as Collection\n }\n\n return createLiveQueryCollection({\n ...innerCollection,\n startSync: true,\n })\n },\n undefined,\n { name: `TanstackDBCollectionMemo` },\n )\n\n // Reactive state that gets updated granularly through change events\n const state = new ReactiveMap<string | number, any>()\n\n // Reactive data array that maintains sorted order\n const [data, setData] = createStore<Array<any>>([], {\n name: `TanstackDBData`,\n })\n\n // Track collection status reactively\n const [status, setStatus] = createSignal(\n collection() ? collection()!.status : (`disabled` as const),\n {\n name: `TanstackDBStatus`,\n },\n )\n\n // Helper to sync data array from collection in correct order\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n setData((prev) =>\n reconcile(Array.from(currentCollection.values()))(prev).filter(Boolean),\n )\n }\n\n const [getDataResource] = createResource(\n () => ({ currentCollection: collection() }),\n async ({ currentCollection }) => {\n if (!currentCollection) {\n return []\n }\n setStatus(currentCollection.status)\n try {\n await currentCollection.toArrayWhenReady()\n } catch (error) {\n setStatus(`error`)\n throw error\n }\n // Initialize state with current collection data\n batch(() => {\n state.clear()\n for (const [key, value] of currentCollection.entries()) {\n state.set(key, value)\n }\n syncDataFromCollection(currentCollection)\n setStatus(currentCollection.status)\n })\n return data\n },\n {\n name: `TanstackDBData`,\n deferStream: false,\n initialValue: data,\n },\n )\n\n createEffect(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n setStatus(`disabled` as const)\n state.clear()\n setData([])\n return\n }\n const subscription = currentCollection.subscribeChanges(\n (changes: Array<ChangeMessage<any>>) => {\n // Apply each change individually to the reactive state\n batch(() => {\n for (const change of changes) {\n switch (change.type) {\n case `insert`:\n case `update`:\n state.set(change.key, change.value)\n break\n case `delete`:\n state.delete(change.key)\n break\n }\n }\n\n syncDataFromCollection(currentCollection)\n\n // Update status ref on every change\n setStatus(currentCollection.status)\n })\n },\n {\n // Include initial state to ensure immediate population for pre-created collections\n includeInitialState: true,\n },\n )\n\n onCleanup(() => {\n subscription.unsubscribe()\n })\n })\n\n // We have to remove getters from the resource function so we wrap it\n function getData() {\n return getDataResource()\n }\n\n Object.defineProperties(getData, {\n data: {\n get() {\n return getData()\n },\n },\n status: {\n get() {\n return status()\n },\n },\n collection: {\n get() {\n return collection()\n },\n },\n state: {\n get() {\n return state\n },\n },\n isLoading: {\n get() {\n return status() === `loading`\n },\n },\n isReady: {\n get() {\n return status() === `ready` || status() === `disabled`\n },\n },\n isIdle: {\n get() {\n return status() === `idle`\n },\n },\n isError: {\n get() {\n return status() === `error`\n },\n },\n isCleanedUp: {\n get() {\n return status() === `cleaned-up`\n },\n },\n })\n return getData\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useLiveQuery.js","sources":["../../src/useLiveQuery.ts"],"sourcesContent":["import {\n batch,\n createEffect,\n createMemo,\n createResource,\n createSignal,\n onCleanup,\n} from 'solid-js'\nimport { ReactiveMap } from '@solid-primitives/map'\nimport {\n BaseQueryBuilder,\n CollectionImpl,\n createLiveQueryCollection,\n} from '@tanstack/db'\nimport { createStore, reconcile } from 'solid-js/store'\nimport type { Accessor } from 'solid-js'\nimport type {\n ChangeMessage,\n Collection,\n CollectionConfigSingleRowOption,\n CollectionStatus,\n Context,\n GetResult,\n InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\n\n/**\n * Create a live query using a query function\n * @param queryFn - Query function that defines what data to fetch\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Basic query with object syntax\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.completed, false))\n * .select(({ todos }) => ({ id: todos.id, text: todos.text }))\n * )\n *\n * @example\n * // With dependencies that trigger re-execution\n * const todosQuery = useLiveQuery(\n * (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => gt(todos.priority, minPriority())),\n * )\n *\n * @example\n * // Join pattern\n * const personIssues = useLiveQuery((q) =>\n * q.from({ issues: issueCollection })\n * .join({ persons: personCollection }, ({ issues, persons }) =>\n * eq(issues.userId, persons.id)\n * )\n * .select(({ issues, persons }) => ({\n * id: issues.id,\n * title: issues.title,\n * userName: persons.name\n * }))\n * )\n *\n * @example\n * // Handle loading and error states\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * return (\n * <Switch>\n * <Match when={todosQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={todosQuery.isError}>\n * <div>Error: {todosQuery.status}</div>\n * </Match>\n * <Match when={todosQuery.isReady}>\n * <For each={todosQuery()}>\n * {(todo) => <li key={todo.id}>{todo.text}</li>}\n * </For>\n * </Match>\n * </Switch>\n * )\n *\n * @example\n * // Use Suspense boundaries\n * const todosQuery = useLiveQuery((q) =>\n * q.from({ todos: todoCollection })\n * )\n *\n * return (\n * <Suspense fallback={<div>Loading...</div>}>\n * <For each={todosQuery()}>\n * {(todo) => <li key={todo.id}>{todo.text}</li>}\n * </For>\n * </Suspense>\n * )\n */\n// Overload 1: Accept query function that always returns QueryBuilder\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): Accessor<InferResultType<TContext>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: InferResultType<TContext>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 1b: Accept query function that can return undefined/null\nexport function useLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): Accessor<InferResultType<TContext>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: InferResultType<TContext>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}> | null\n status: CollectionStatus | `disabled`\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Create a live query using configuration object\n * @param config - Configuration object with query and options\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Basic config object usage\n * const todosQuery = useLiveQuery(() => ({\n * query: (q) => q.from({ todos: todosCollection }),\n * gcTime: 60000\n * }))\n *\n * @example\n * // With query builder and options\n * const queryBuilder = new Query()\n * .from({ persons: collection })\n * .where(({ persons }) => gt(persons.age, 30))\n * .select(({ persons }) => ({ id: persons.id, name: persons.name }))\n *\n * const personsQuery = useLiveQuery(() => ({ query: queryBuilder }))\n *\n * @example\n * // Handle all states uniformly\n * const itemsQuery = useLiveQuery(() => ({\n * query: (q) => q.from({ items: itemCollection })\n * }))\n *\n * return (\n * <Switch fallback={<div>{itemsQuery().length} items loaded</div>}>\n * <Match when={itemsQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={itemsQuery.isError}>\n * <div>Something went wrong</div>\n * </Match>\n * <Match when={!itemsQuery.isReady}>\n * <div>Preparing...</div>\n * </Match>\n * </Switch>\n * )\n */\n// Overload 2: Accept config object\nexport function useLiveQuery<TContext extends Context>(\n config: Accessor<LiveQueryCollectionConfig<TContext>>,\n): Accessor<InferResultType<TContext>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: InferResultType<TContext>\n state: ReactiveMap<string | number, GetResult<TContext>>\n collection: Collection<GetResult<TContext>, string | number, {}>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n/**\n * Subscribe to an existing live query collection\n * @param liveQueryCollection - Pre-created live query collection to subscribe to\n * @returns Accessor that returns data with Suspense support, with state and status information as properties\n * @example\n * // Using pre-created live query collection\n * const myLiveQuery = createLiveQueryCollection((q) =>\n * q.from({ todos: todosCollection }).where(({ todos }) => eq(todos.active, true))\n * )\n * const todosQuery = useLiveQuery(() => myLiveQuery)\n *\n * @example\n * // Access collection methods directly\n * const existingQuery = useLiveQuery(() => existingCollection)\n *\n * // Use collection for mutations\n * const handleToggle = (id) => {\n * existingQuery.collection.update(id, draft => { draft.completed = !draft.completed })\n * }\n *\n * @example\n * // Handle states consistently\n * const sharedQuery = useLiveQuery(() => sharedCollection)\n *\n * return (\n * <Switch fallback={<div><For each={sharedQuery()}>{(item) => <Item key={item.id} {...item} />}</For></div>}>\n * <Match when={sharedQuery.isLoading}>\n * <div>Loading...</div>\n * </Match>\n * <Match when={sharedQuery.isError}>\n * <div>Error loading data</div>\n * </Match>\n * </Switch>\n * )\n */\n// Overload 3: Accept pre-created live query collection (non-single result)\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Accessor<\n Collection<TResult, TKey, TUtils> & NonSingleResult\n >,\n): Accessor<Array<TResult>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: Array<TResult>\n state: ReactiveMap<TKey, TResult>\n collection: Collection<TResult, TKey, TUtils>\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Overload 3b: Accept pre-created live query collection with singleResult: true\nexport function useLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Accessor<\n Collection<TResult, TKey, TUtils> & SingleResult\n >,\n): Accessor<TResult | undefined> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: TResult | undefined\n state: ReactiveMap<TKey, TResult>\n collection: Collection<TResult, TKey, TUtils> & SingleResult\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n}\n\n// Implementation - use function overloads to infer the actual collection type\nexport function useLiveQuery(\n configOrQueryOrCollection: (queryFn?: any) => any,\n) {\n const collection = createMemo(\n () => {\n if (configOrQueryOrCollection.length === 1) {\n // This is a query function - check if it returns null/undefined\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = configOrQueryOrCollection(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: configOrQueryOrCollection,\n startSync: true,\n })\n }\n\n const innerCollection = configOrQueryOrCollection()\n\n if (innerCollection === undefined || innerCollection === null) {\n // Disabled query - return null\n return null\n }\n\n if (innerCollection instanceof CollectionImpl) {\n innerCollection.startSyncImmediate()\n return innerCollection as Collection\n }\n\n return createLiveQueryCollection({\n ...innerCollection,\n startSync: true,\n })\n },\n undefined,\n { name: `TanstackDBCollectionMemo` },\n )\n\n // Reactive state that gets updated granularly through change events\n const state = new ReactiveMap<string | number, any>()\n\n // Reactive data array that maintains sorted order\n const [data, setData] = createStore<Array<any>>([], {\n name: `TanstackDBData`,\n })\n\n // Track collection status reactively\n const [status, setStatus] = createSignal(\n collection() ? collection()!.status : (`disabled` as const),\n {\n name: `TanstackDBStatus`,\n },\n )\n\n // Helper to sync data array from collection in correct order\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n setData((prev) =>\n reconcile(Array.from(currentCollection.values()))(prev).filter(Boolean),\n )\n }\n\n const [getDataResource] = createResource(\n () => ({ currentCollection: collection() }),\n async ({ currentCollection }) => {\n if (!currentCollection) {\n return []\n }\n setStatus(currentCollection.status)\n try {\n await currentCollection.toArrayWhenReady()\n } catch (error) {\n setStatus(`error`)\n throw error\n }\n // Initialize state with current collection data\n batch(() => {\n state.clear()\n for (const [key, value] of currentCollection.entries()) {\n state.set(key, value)\n }\n syncDataFromCollection(currentCollection)\n setStatus(currentCollection.status)\n })\n return data\n },\n {\n name: `TanstackDBData`,\n deferStream: false,\n initialValue: data,\n },\n )\n\n createEffect(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n setStatus(`disabled` as const)\n state.clear()\n setData([])\n return\n }\n const subscription = currentCollection.subscribeChanges(\n (changes: Array<ChangeMessage<any>>) => {\n // Apply each change individually to the reactive state\n batch(() => {\n for (const change of changes) {\n switch (change.type) {\n case `insert`:\n case `update`:\n state.set(change.key, change.value)\n break\n case `delete`:\n state.delete(change.key)\n break\n }\n }\n\n syncDataFromCollection(currentCollection)\n\n // Update status ref on every change\n setStatus(currentCollection.status)\n })\n },\n {\n // Include initial state to ensure immediate population for pre-created collections\n includeInitialState: true,\n },\n )\n\n onCleanup(() => {\n subscription.unsubscribe()\n })\n })\n\n // We have to remove getters from the resource function so we wrap it\n function getData() {\n const currentCollection = collection()\n if (currentCollection) {\n const config: CollectionConfigSingleRowOption<any, any, any> =\n currentCollection.config\n if (config.singleResult) {\n // Force resource tracking so Suspense works\n getDataResource()\n return data[0]\n }\n }\n return getDataResource()\n }\n\n Object.defineProperties(getData, {\n data: {\n get() {\n return getData()\n },\n },\n status: {\n get() {\n return status()\n },\n },\n collection: {\n get() {\n return collection()\n },\n },\n state: {\n get() {\n return state\n },\n },\n isLoading: {\n get() {\n return status() === `loading`\n },\n },\n isReady: {\n get() {\n return status() === `ready` || status() === `disabled`\n },\n },\n isIdle: {\n get() {\n return status() === `idle`\n },\n },\n isError: {\n get() {\n return status() === `error`\n },\n },\n isCleanedUp: {\n get() {\n return status() === `cleaned-up`\n },\n },\n })\n return getData\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/solid-db",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "Solid integration for @tanstack/db",
|
|
5
5
|
"author": "Kyle Mathews",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@solid-primitives/map": "^0.7.2",
|
|
39
|
-
"@tanstack/db": "0.
|
|
39
|
+
"@tanstack/db": "0.6.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"solid-js": ">=1.9.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@electric-sql/client": "^1.5.
|
|
45
|
+
"@electric-sql/client": "^1.5.13",
|
|
46
46
|
"@solidjs/testing-library": "^0.8.10",
|
|
47
47
|
"@vitest/coverage-istanbul": "^3.2.4",
|
|
48
48
|
"jsdom": "^27.4.0",
|
package/src/useLiveQuery.ts
CHANGED
|
@@ -17,12 +17,16 @@ import type { Accessor } from 'solid-js'
|
|
|
17
17
|
import type {
|
|
18
18
|
ChangeMessage,
|
|
19
19
|
Collection,
|
|
20
|
+
CollectionConfigSingleRowOption,
|
|
20
21
|
CollectionStatus,
|
|
21
22
|
Context,
|
|
22
23
|
GetResult,
|
|
24
|
+
InferResultType,
|
|
23
25
|
InitialQueryBuilder,
|
|
24
26
|
LiveQueryCollectionConfig,
|
|
27
|
+
NonSingleResult,
|
|
25
28
|
QueryBuilder,
|
|
29
|
+
SingleResult,
|
|
26
30
|
} from '@tanstack/db'
|
|
27
31
|
|
|
28
32
|
/**
|
|
@@ -97,12 +101,12 @@ import type {
|
|
|
97
101
|
// Overload 1: Accept query function that always returns QueryBuilder
|
|
98
102
|
export function useLiveQuery<TContext extends Context>(
|
|
99
103
|
queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
|
|
100
|
-
): Accessor<
|
|
104
|
+
): Accessor<InferResultType<TContext>> & {
|
|
101
105
|
/**
|
|
102
106
|
* @deprecated use function result instead
|
|
103
107
|
* query.data -> query()
|
|
104
108
|
*/
|
|
105
|
-
data:
|
|
109
|
+
data: InferResultType<TContext>
|
|
106
110
|
state: ReactiveMap<string | number, GetResult<TContext>>
|
|
107
111
|
collection: Collection<GetResult<TContext>, string | number, {}>
|
|
108
112
|
status: CollectionStatus
|
|
@@ -118,12 +122,12 @@ export function useLiveQuery<TContext extends Context>(
|
|
|
118
122
|
queryFn: (
|
|
119
123
|
q: InitialQueryBuilder,
|
|
120
124
|
) => QueryBuilder<TContext> | undefined | null,
|
|
121
|
-
): Accessor<
|
|
125
|
+
): Accessor<InferResultType<TContext>> & {
|
|
122
126
|
/**
|
|
123
127
|
* @deprecated use function result instead
|
|
124
128
|
* query.data -> query()
|
|
125
129
|
*/
|
|
126
|
-
data:
|
|
130
|
+
data: InferResultType<TContext>
|
|
127
131
|
state: ReactiveMap<string | number, GetResult<TContext>>
|
|
128
132
|
collection: Collection<GetResult<TContext>, string | number, {}> | null
|
|
129
133
|
status: CollectionStatus | `disabled`
|
|
@@ -177,12 +181,12 @@ export function useLiveQuery<TContext extends Context>(
|
|
|
177
181
|
// Overload 2: Accept config object
|
|
178
182
|
export function useLiveQuery<TContext extends Context>(
|
|
179
183
|
config: Accessor<LiveQueryCollectionConfig<TContext>>,
|
|
180
|
-
): Accessor<
|
|
184
|
+
): Accessor<InferResultType<TContext>> & {
|
|
181
185
|
/**
|
|
182
186
|
* @deprecated use function result instead
|
|
183
187
|
* query.data -> query()
|
|
184
188
|
*/
|
|
185
|
-
data:
|
|
189
|
+
data: InferResultType<TContext>
|
|
186
190
|
state: ReactiveMap<string | number, GetResult<TContext>>
|
|
187
191
|
collection: Collection<GetResult<TContext>, string | number, {}>
|
|
188
192
|
status: CollectionStatus
|
|
@@ -228,13 +232,15 @@ export function useLiveQuery<TContext extends Context>(
|
|
|
228
232
|
* </Switch>
|
|
229
233
|
* )
|
|
230
234
|
*/
|
|
231
|
-
// Overload 3: Accept pre-created live query collection
|
|
235
|
+
// Overload 3: Accept pre-created live query collection (non-single result)
|
|
232
236
|
export function useLiveQuery<
|
|
233
237
|
TResult extends object,
|
|
234
238
|
TKey extends string | number,
|
|
235
239
|
TUtils extends Record<string, any>,
|
|
236
240
|
>(
|
|
237
|
-
liveQueryCollection: Accessor<
|
|
241
|
+
liveQueryCollection: Accessor<
|
|
242
|
+
Collection<TResult, TKey, TUtils> & NonSingleResult
|
|
243
|
+
>,
|
|
238
244
|
): Accessor<Array<TResult>> & {
|
|
239
245
|
/**
|
|
240
246
|
* @deprecated use function result instead
|
|
@@ -251,6 +257,31 @@ export function useLiveQuery<
|
|
|
251
257
|
isCleanedUp: boolean
|
|
252
258
|
}
|
|
253
259
|
|
|
260
|
+
// Overload 3b: Accept pre-created live query collection with singleResult: true
|
|
261
|
+
export function useLiveQuery<
|
|
262
|
+
TResult extends object,
|
|
263
|
+
TKey extends string | number,
|
|
264
|
+
TUtils extends Record<string, any>,
|
|
265
|
+
>(
|
|
266
|
+
liveQueryCollection: Accessor<
|
|
267
|
+
Collection<TResult, TKey, TUtils> & SingleResult
|
|
268
|
+
>,
|
|
269
|
+
): Accessor<TResult | undefined> & {
|
|
270
|
+
/**
|
|
271
|
+
* @deprecated use function result instead
|
|
272
|
+
* query.data -> query()
|
|
273
|
+
*/
|
|
274
|
+
data: TResult | undefined
|
|
275
|
+
state: ReactiveMap<TKey, TResult>
|
|
276
|
+
collection: Collection<TResult, TKey, TUtils> & SingleResult
|
|
277
|
+
status: CollectionStatus
|
|
278
|
+
isLoading: boolean
|
|
279
|
+
isReady: boolean
|
|
280
|
+
isIdle: boolean
|
|
281
|
+
isError: boolean
|
|
282
|
+
isCleanedUp: boolean
|
|
283
|
+
}
|
|
284
|
+
|
|
254
285
|
// Implementation - use function overloads to infer the actual collection type
|
|
255
286
|
export function useLiveQuery(
|
|
256
287
|
configOrQueryOrCollection: (queryFn?: any) => any,
|
|
@@ -393,6 +424,16 @@ export function useLiveQuery(
|
|
|
393
424
|
|
|
394
425
|
// We have to remove getters from the resource function so we wrap it
|
|
395
426
|
function getData() {
|
|
427
|
+
const currentCollection = collection()
|
|
428
|
+
if (currentCollection) {
|
|
429
|
+
const config: CollectionConfigSingleRowOption<any, any, any> =
|
|
430
|
+
currentCollection.config
|
|
431
|
+
if (config.singleResult) {
|
|
432
|
+
// Force resource tracking so Suspense works
|
|
433
|
+
getDataResource()
|
|
434
|
+
return data[0]
|
|
435
|
+
}
|
|
436
|
+
}
|
|
396
437
|
return getDataResource()
|
|
397
438
|
}
|
|
398
439
|
|