@tanstack/solid-db 0.3.0 → 0.3.3

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 { createMemo, createSignal, onCleanup, createResource, batch, createEffect } from "solid-js";
2
2
  import { ReactiveMap } from "@solid-primitives/map";
3
- import { BaseQueryBuilder, createLiveQueryCollection, isCollection, createLiveQueryObserver, isSingleResultCollection } from "@tanstack/db";
3
+ import { BaseQueryBuilder, resolveLiveQueryValue, isCollection, createLiveQueryCollection, createLiveQueryObserver, isSingleResultCollection, getPublicCollection } from "@tanstack/db";
4
4
  import { createStore, reconcile } from "solid-js/store";
5
5
  function useLiveQuery(configOrQueryOrCollection) {
6
6
  const collection = createMemo(
@@ -11,10 +11,7 @@ function useLiveQuery(configOrQueryOrCollection) {
11
11
  if (result === void 0 || result === null) {
12
12
  return null;
13
13
  }
14
- return createLiveQueryCollection({
15
- query: configOrQueryOrCollection,
16
- startSync: true
17
- });
14
+ return resolveLiveQueryValue(result);
18
15
  }
19
16
  const innerCollection = configOrQueryOrCollection();
20
17
  if (innerCollection === void 0 || innerCollection === null) {
@@ -197,7 +194,7 @@ function useLiveQuery(configOrQueryOrCollection) {
197
194
  },
198
195
  collection: {
199
196
  get() {
200
- return collection();
197
+ return getPublicCollection(collection());
201
198
  }
202
199
  },
203
200
  state: {
@@ -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 createLiveQueryCollection,\n createLiveQueryObserver,\n isCollection,\n isSingleResultCollection,\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 InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n LiveQueryObserver,\n LiveQueryPersistedStatus,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\n\ntype InferConditionalResultType<TContext extends Context> =\n TContext extends SingleResult\n ? InferResultType<TContext> | []\n : InferResultType<TContext>\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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<InferConditionalResultType<TContext>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: InferConditionalResultType<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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 (isCollection(innerCollection)) {\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 // Keep the live Collection's result keys private while preserving one stable\n // Solid store per logical row. A row's public $key can belong to an upstream\n // Collection and is therefore not necessarily unique in this result.\n const rowsByKey = new Map<\n string | number,\n { value: any; update: (value: any) => void }\n >()\n let rowsCollection: Collection<any, any, any> | undefined\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 const [persistedStatus, setLocalStatus] =\n createSignal<LiveQueryPersistedStatus>(`unavailable`)\n const [persistedError, setLocalError] = createSignal<unknown>(undefined)\n\n // Helper to sync data array from collection in correct order\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n const nextRows: Array<any> = []\n const retainedKeys = new Set<string | number>()\n\n for (const [key, value] of currentCollection.entries()) {\n retainedKeys.add(key)\n\n const existing = rowsByKey.get(key)\n if (existing) {\n existing.update(value)\n nextRows.push(existing.value)\n } else {\n const [row, setRow] = createStore(value)\n rowsByKey.set(key, {\n value: row,\n update: (nextValue) => setRow(reconcile(nextValue, { key: null })),\n })\n nextRows.push(row)\n }\n }\n\n for (const key of rowsByKey.keys()) {\n if (!retainedKeys.has(key)) rowsByKey.delete(key)\n }\n\n setData((previous) => reconcile(nextRows, { key: null })(previous))\n }\n\n // Generation guard for the resource's async continuations: Solid discards a\n // superseded fetch's *return value*, but the writes below are side effects\n // into hook-scoped state and would still run — resurrecting rows/status from\n // a collection that has already been replaced.\n let resourceGeneration = 0\n let localWaitObserver: LiveQueryObserver<any, any> | undefined\n onCleanup(() => localWaitObserver?.dispose())\n\n const [getDataResource] = createResource(\n () => ({ currentCollection: collection() }),\n async ({ currentCollection }) => {\n const generation = ++resourceGeneration\n localWaitObserver?.dispose()\n localWaitObserver = undefined\n if (!currentCollection) {\n return []\n }\n setStatus(currentCollection.status)\n try {\n const observer = createLiveQueryObserver(currentCollection)\n localWaitObserver = observer\n try {\n await observer.preloadForInitialRender()\n } finally {\n observer.dispose()\n if (localWaitObserver === observer) localWaitObserver = undefined\n }\n } catch (error) {\n if (generation !== resourceGeneration) return data\n setStatus(`error`)\n throw error\n }\n if (generation !== resourceGeneration) {\n return data\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 setLocalStatus(`unavailable`)\n setLocalError(undefined)\n state.clear()\n rowsByKey.clear()\n rowsCollection = undefined\n setData([])\n return\n }\n\n if (rowsCollection !== currentCollection) {\n rowsByKey.clear()\n rowsCollection = currentCollection\n }\n\n // The shared observer owns subscription, the ready-race, and status; Solid\n // materializes into its keyed ReactiveMap (granular) + reconciled store.\n const observer = createLiveQueryObserver(currentCollection)\n // Clear any keys carried over from a previous collection before the new\n // observer re-seeds via `includeInitialState` (which only inserts current\n // rows, never deletes stale ones). Without this, switching collections\n // leaves the dropped keys in `state` until the async resource reconciles.\n state.clear()\n const unsubscribe = observer.subscribe(\n (changes: Array<ChangeMessage<any>> | undefined) => {\n batch(() => {\n if (changes) {\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 } else {\n // Cleanup and other status-only publications carry no row deltas.\n // Rebuild the keyed view so it cannot diverge from ordered data.\n state.clear()\n for (const [key, value] of observer.getSnapshot().state ?? []) {\n state.set(key, value)\n }\n }\n syncDataFromCollection(currentCollection)\n const snapshot = observer.getSnapshot()\n setStatus(snapshot.status)\n setLocalStatus(snapshot.persistedStatus)\n setLocalError(snapshot.persistedError)\n })\n },\n )\n // An already-ready empty collection produces no initial row batch. Bring\n // ordered data and status in line synchronously instead of waiting for the\n // resource continuation to correct the previous collection's rows.\n batch(() => {\n syncDataFromCollection(currentCollection)\n const snapshot = observer.getSnapshot()\n setStatus(snapshot.status)\n setLocalStatus(snapshot.persistedStatus)\n setLocalError(snapshot.persistedError)\n })\n\n onCleanup(() => {\n unsubscribe()\n observer.dispose()\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 if (isSingleResultCollection(currentCollection)) {\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 persistedStatus: {\n get() {\n return persistedStatus()\n },\n },\n isPersistedReady: {\n get() {\n return persistedStatus() === `ready`\n },\n },\n persistedError: {\n get() {\n return persistedError()\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 createLiveQueryCollection,\n createLiveQueryObserver,\n getPublicCollection,\n isCollection,\n isSingleResultCollection,\n resolveLiveQueryValue,\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 InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n LiveQueryObserver,\n LiveQueryPersistedStatus,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\n\ntype InferConditionalResultType<TContext extends Context> =\n TContext extends SingleResult\n ? InferResultType<TContext> | []\n : InferResultType<TContext>\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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<InferConditionalResultType<TContext>> & {\n /**\n * @deprecated use function result instead\n * query.data -> query()\n */\n data: InferConditionalResultType<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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\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 resolveLiveQueryValue(result)\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 (isCollection(innerCollection)) {\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 // Keep the live Collection's result keys private while preserving one stable\n // Solid store per logical row. A row's public $key can belong to an upstream\n // Collection and is therefore not necessarily unique in this result.\n const rowsByKey = new Map<\n string | number,\n { value: any; update: (value: any) => void }\n >()\n let rowsCollection: Collection<any, any, any> | undefined\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 const [persistedStatus, setLocalStatus] =\n createSignal<LiveQueryPersistedStatus>(`unavailable`)\n const [persistedError, setLocalError] = createSignal<unknown>(undefined)\n\n // Helper to sync data array from collection in correct order\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n const nextRows: Array<any> = []\n const retainedKeys = new Set<string | number>()\n\n for (const [key, value] of currentCollection.entries()) {\n retainedKeys.add(key)\n\n const existing = rowsByKey.get(key)\n if (existing) {\n existing.update(value)\n nextRows.push(existing.value)\n } else {\n const [row, setRow] = createStore(value)\n rowsByKey.set(key, {\n value: row,\n update: (nextValue) => setRow(reconcile(nextValue, { key: null })),\n })\n nextRows.push(row)\n }\n }\n\n for (const key of rowsByKey.keys()) {\n if (!retainedKeys.has(key)) rowsByKey.delete(key)\n }\n\n setData((previous) => reconcile(nextRows, { key: null })(previous))\n }\n\n // Generation guard for the resource's async continuations: Solid discards a\n // superseded fetch's *return value*, but the writes below are side effects\n // into hook-scoped state and would still run — resurrecting rows/status from\n // a collection that has already been replaced.\n let resourceGeneration = 0\n let localWaitObserver: LiveQueryObserver<any, any> | undefined\n onCleanup(() => localWaitObserver?.dispose())\n\n const [getDataResource] = createResource(\n () => ({ currentCollection: collection() }),\n async ({ currentCollection }) => {\n const generation = ++resourceGeneration\n localWaitObserver?.dispose()\n localWaitObserver = undefined\n if (!currentCollection) {\n return []\n }\n setStatus(currentCollection.status)\n try {\n const observer = createLiveQueryObserver(currentCollection)\n localWaitObserver = observer\n try {\n await observer.preloadForInitialRender()\n } finally {\n observer.dispose()\n if (localWaitObserver === observer) localWaitObserver = undefined\n }\n } catch (error) {\n if (generation !== resourceGeneration) return data\n setStatus(`error`)\n throw error\n }\n if (generation !== resourceGeneration) {\n return data\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 setLocalStatus(`unavailable`)\n setLocalError(undefined)\n state.clear()\n rowsByKey.clear()\n rowsCollection = undefined\n setData([])\n return\n }\n\n if (rowsCollection !== currentCollection) {\n rowsByKey.clear()\n rowsCollection = currentCollection\n }\n\n // The shared observer owns subscription, the ready-race, and status; Solid\n // materializes into its keyed ReactiveMap (granular) + reconciled store.\n const observer = createLiveQueryObserver(currentCollection)\n // Clear any keys carried over from a previous collection before the new\n // observer re-seeds via `includeInitialState` (which only inserts current\n // rows, never deletes stale ones). Without this, switching collections\n // leaves the dropped keys in `state` until the async resource reconciles.\n state.clear()\n const unsubscribe = observer.subscribe(\n (changes: Array<ChangeMessage<any>> | undefined) => {\n batch(() => {\n if (changes) {\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 } else {\n // Cleanup and other status-only publications carry no row deltas.\n // Rebuild the keyed view so it cannot diverge from ordered data.\n state.clear()\n for (const [key, value] of observer.getSnapshot().state ?? []) {\n state.set(key, value)\n }\n }\n syncDataFromCollection(currentCollection)\n const snapshot = observer.getSnapshot()\n setStatus(snapshot.status)\n setLocalStatus(snapshot.persistedStatus)\n setLocalError(snapshot.persistedError)\n })\n },\n )\n // An already-ready empty collection produces no initial row batch. Bring\n // ordered data and status in line synchronously instead of waiting for the\n // resource continuation to correct the previous collection's rows.\n batch(() => {\n syncDataFromCollection(currentCollection)\n const snapshot = observer.getSnapshot()\n setStatus(snapshot.status)\n setLocalStatus(snapshot.persistedStatus)\n setLocalError(snapshot.persistedError)\n })\n\n onCleanup(() => {\n unsubscribe()\n observer.dispose()\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 if (isSingleResultCollection(currentCollection)) {\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 getPublicCollection(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 persistedStatus: {\n get() {\n return persistedStatus()\n },\n },\n isPersistedReady: {\n get() {\n return persistedStatus() === `ready`\n },\n },\n persistedError: {\n get() {\n return persistedError()\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.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "Solid integration for @tanstack/db",
5
5
  "author": "Kyle Mathews",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@solid-primitives/map": "^0.7.2",
40
- "@tanstack/db": "0.11.0"
40
+ "@tanstack/db": "0.11.3"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "solid-js": ">=1.9.0"
@@ -11,8 +11,10 @@ import {
11
11
  BaseQueryBuilder,
12
12
  createLiveQueryCollection,
13
13
  createLiveQueryObserver,
14
+ getPublicCollection,
14
15
  isCollection,
15
16
  isSingleResultCollection,
17
+ resolveLiveQueryValue,
16
18
  } from '@tanstack/db'
17
19
  import { createStore, reconcile } from 'solid-js/store'
18
20
  import type { Accessor } from 'solid-js'
@@ -321,10 +323,7 @@ export function useLiveQuery(
321
323
  return null
322
324
  }
323
325
 
324
- return createLiveQueryCollection({
325
- query: configOrQueryOrCollection,
326
- startSync: true,
327
- })
326
+ return resolveLiveQueryValue(result)
328
327
  }
329
328
 
330
329
  const innerCollection = configOrQueryOrCollection()
@@ -558,7 +557,7 @@ export function useLiveQuery(
558
557
  },
559
558
  collection: {
560
559
  get() {
561
- return collection()
560
+ return getPublicCollection(collection())
562
561
  },
563
562
  },
564
563
  state: {