@tanstack/angular-db 0.1.89 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,9 +43,13 @@ function injectLiveQuery(opts) {
43
43
  });
44
44
  const state = core.signal(/* @__PURE__ */ new Map());
45
45
  const internalData = core.signal([]);
46
- const status = core.signal(
47
- collection() ? `idle` : `disabled`
48
- );
46
+ const statusValue = core.signal(`idle`);
47
+ const status = core.computed(() => {
48
+ const value = statusValue();
49
+ return value === `idle` && !collection() ? `disabled` : value;
50
+ });
51
+ const persistedStatus = core.signal(`unavailable`);
52
+ const persistedError = core.signal(void 0);
49
53
  const data = core.computed(() => {
50
54
  const currentCollection = collection();
51
55
  if (!currentCollection) {
@@ -53,12 +57,15 @@ function injectLiveQuery(opts) {
53
57
  }
54
58
  return db.isSingleResultCollection(currentCollection) ? internalData()[0] : internalData();
55
59
  });
56
- const syncDataFromCollection = (currentCollection) => {
60
+ const syncDataFromCollection = (currentCollection, observer) => {
57
61
  const newState = new Map(currentCollection.entries());
58
62
  const newData = Array.from(currentCollection.values());
59
63
  state.set(newState);
60
64
  internalData.set(newData);
61
- status.set(currentCollection.status);
65
+ const snapshot = observer.getSnapshot();
66
+ statusValue.set(snapshot.status);
67
+ persistedStatus.set(snapshot.persistedStatus);
68
+ persistedError.set(snapshot.persistedError);
62
69
  };
63
70
  let unsub = null;
64
71
  const cleanup = () => {
@@ -68,7 +75,9 @@ function injectLiveQuery(opts) {
68
75
  core.effect((onCleanup) => {
69
76
  const currentCollection = collection();
70
77
  if (!currentCollection) {
71
- status.set(`disabled`);
78
+ statusValue.set(`disabled`);
79
+ persistedStatus.set(`unavailable`);
80
+ persistedError.set(void 0);
72
81
  state.set(/* @__PURE__ */ new Map());
73
82
  internalData.set([]);
74
83
  cleanup();
@@ -79,9 +88,9 @@ function injectLiveQuery(opts) {
79
88
  mode: `wholesale`
80
89
  });
81
90
  const unsubscribe = observer.subscribe(() => {
82
- syncDataFromCollection(currentCollection);
91
+ syncDataFromCollection(currentCollection, observer);
83
92
  });
84
- syncDataFromCollection(currentCollection);
93
+ syncDataFromCollection(currentCollection, observer);
85
94
  unsub = () => {
86
95
  unsubscribe();
87
96
  observer.dispose();
@@ -98,6 +107,9 @@ function injectLiveQuery(opts) {
98
107
  status,
99
108
  isLoading: core.computed(() => status() === `loading`),
100
109
  isReady: core.computed(() => status() === `ready` || status() === `disabled`),
110
+ persistedStatus,
111
+ isPersistedReady: core.computed(() => persistedStatus() === `ready`),
112
+ persistedError,
101
113
  isIdle: core.computed(() => status() === `idle`),
102
114
  isError: core.computed(() => status() === `error`),
103
115
  isCleanedUp: core.computed(() => status() === `cleaned-up`)
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import {\n DestroyRef,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core'\nimport {\n BaseQueryBuilder,\n createLiveQueryCollection,\n createLiveQueryObserver,\n isCollection,\n isSingleResultCollection,\n} from '@tanstack/db'\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\nimport type { Signal } from '@angular/core'\n\nexport * from '@tanstack/db'\n\n/**\n * The result of calling `injectLiveQuery`.\n * Contains reactive signals for the query state and data.\n */\nexport interface InjectLiveQueryResult<TContext extends Context> {\n /** A signal containing the complete state map of results keyed by their ID */\n state: Signal<Map<string | number, GetResult<TContext>>>\n /** A signal containing the results as an array, or single result for findOne queries */\n data: Signal<InferResultType<TContext>>\n /** A signal containing the underlying collection instance (null for disabled queries) */\n collection: Signal<Collection<\n GetResult<TContext>,\n string | number,\n {}\n > | null>\n /** A signal containing the current status of the collection */\n status: Signal<CollectionStatus | `disabled`>\n /** A signal indicating whether the collection is currently loading */\n isLoading: Signal<boolean>\n /** A signal indicating whether the collection is ready */\n isReady: Signal<boolean>\n /** A signal indicating whether the collection is idle */\n isIdle: Signal<boolean>\n /** A signal indicating whether the collection has an error */\n isError: Signal<boolean>\n /** A signal indicating whether the collection has been cleaned up */\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<Array<TResult>>\n collection: Signal<Collection<TResult, TKey, TUtils> | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithSingleResultCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<TResult | undefined>\n collection: Signal<(Collection<TResult, TKey, TUtils> & SingleResult) | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext>\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext> | undefined | null\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n): InjectLiveQueryResult<TContext>\n// Pre-created collection without singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>\n// Pre-created collection with singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult,\n): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>\nexport function injectLiveQuery(opts: any) {\n assertInInjectionContext(injectLiveQuery)\n const destroyRef = inject(DestroyRef)\n\n const collection = computed(() => {\n // Check if it's an existing collection\n if (isCollection(opts)) {\n return opts\n }\n\n if (typeof opts === `function`) {\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = opts(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: opts,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Check if it's reactive query options\n const isReactiveQueryOptions =\n opts &&\n typeof opts === `object` &&\n typeof opts.query === `function` &&\n typeof opts.params === `function`\n\n if (isReactiveQueryOptions) {\n const { params, query } = opts\n const currentParams = params()\n\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = query({ params: currentParams, q: queryBuilder })\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: () => result,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Handle LiveQueryCollectionConfig objects. Default startSync/gcTime to\n // match the query-fn and reactive-options paths, but let an explicit value\n // in the config win — otherwise a bare `{ query }` never syncs.\n if (opts && typeof opts === `object` && typeof opts.query === `function`) {\n return createLiveQueryCollection({ startSync: true, gcTime: 0, ...opts })\n }\n\n throw new Error(`Invalid options provided to injectLiveQuery`)\n })\n\n const state = signal(new Map<string | number, any>())\n const internalData = signal<Array<any>>([])\n const status = signal<CollectionStatus | `disabled`>(\n collection() ? `idle` : `disabled`,\n )\n\n // Returns single item for singleResult collections, array otherwise\n const data = computed(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n return internalData()\n }\n return isSingleResultCollection(currentCollection)\n ? internalData()[0]\n : internalData()\n })\n\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n const newState = new Map(currentCollection.entries())\n const newData = Array.from(currentCollection.values())\n\n state.set(newState)\n internalData.set(newData)\n status.set(currentCollection.status)\n }\n\n let unsub: (() => void) | null = null\n const cleanup = () => {\n unsub?.()\n unsub = null\n }\n\n effect((onCleanup) => {\n const currentCollection = collection()\n\n // Handle null collection (disabled query)\n if (!currentCollection) {\n status.set(`disabled` as const)\n state.set(new Map())\n internalData.set([])\n cleanup()\n return\n }\n\n cleanup()\n\n // The shared observer owns sync start, subscription, the ready-race, and\n // status transitions; Angular re-reads the whole collection on each notify\n // (wholesale) into its signals.\n // Angular re-reads the collection on notify; wholesale mode preserves its\n // pre-observer loading policy (no initial-state snapshot request).\n const observer = createLiveQueryObserver(currentCollection, {\n mode: `wholesale`,\n })\n\n const unsubscribe = observer.subscribe(() => {\n syncDataFromCollection(currentCollection)\n })\n // Wholesale attach suppresses listener calls raised by synchronous sync\n // startup. Read once after subscribe returns to capture that final state.\n syncDataFromCollection(currentCollection)\n unsub = () => {\n unsubscribe()\n observer.dispose()\n }\n\n onCleanup(cleanup)\n })\n\n destroyRef.onDestroy(cleanup)\n\n return {\n state,\n data,\n // Loosely typed so the impl return stays compatible with every overload\n // (the shared `isCollection` guard narrows the computed to `Collection | null`).\n collection: collection as Signal<any>,\n status,\n isLoading: computed(() => status() === `loading`),\n isReady: computed(() => status() === `ready` || status() === `disabled`),\n isIdle: computed(() => status() === `idle`),\n isError: computed(() => status() === `error`),\n isCleanedUp: computed(() => status() === `cleaned-up`),\n }\n}\n"],"names":["assertInInjectionContext","inject","DestroyRef","computed","isCollection","BaseQueryBuilder","createLiveQueryCollection","signal","isSingleResultCollection","effect","createLiveQueryObserver"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import {\n DestroyRef,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core'\nimport {\n BaseQueryBuilder,\n createLiveQueryCollection,\n createLiveQueryObserver,\n isCollection,\n isSingleResultCollection,\n} from '@tanstack/db'\nimport type {\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'\nimport type { Signal } from '@angular/core'\n\nexport * from '@tanstack/db'\n\n/**\n * The result of calling `injectLiveQuery`.\n * Contains reactive signals for the query state and data.\n */\nexport interface InjectLiveQueryResult<TContext extends Context> {\n /** A signal containing the complete state map of results keyed by their ID */\n state: Signal<Map<string | number, GetResult<TContext>>>\n /** A signal containing the results as an array, or single result for findOne queries */\n data: Signal<InferResultType<TContext>>\n /** A signal containing the underlying collection instance (null for disabled queries) */\n collection: Signal<Collection<\n GetResult<TContext>,\n string | number,\n {}\n > | null>\n /** A signal containing the current status of the collection */\n status: Signal<CollectionStatus | `disabled`>\n /** A signal indicating whether the collection is currently loading */\n isLoading: Signal<boolean>\n /** A signal indicating whether the collection is ready */\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n /** A signal indicating whether the collection is idle */\n isIdle: Signal<boolean>\n /** A signal indicating whether the collection has an error */\n isError: Signal<boolean>\n /** A signal indicating whether the collection has been cleaned up */\n isCleanedUp: Signal<boolean>\n}\n\ntype InferConditionalResultType<TContext extends Context> =\n TContext extends SingleResult\n ? InferResultType<TContext> | []\n : InferResultType<TContext>\n\nexport type InjectConditionalLiveQueryResult<TContext extends Context> = Omit<\n InjectLiveQueryResult<TContext>,\n `data`\n> & {\n data: Signal<InferConditionalResultType<TContext>>\n}\n\nexport interface InjectLiveQueryResultWithCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<Array<TResult>>\n collection: Signal<Collection<TResult, TKey, TUtils> | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithSingleResultCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<TResult | undefined>\n collection: Signal<(Collection<TResult, TKey, TUtils> & SingleResult) | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext>\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext> | undefined | null\n}): InjectConditionalLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): InjectConditionalLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n): InjectLiveQueryResult<TContext>\n// Pre-created collection without singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>\n// Pre-created collection with singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult,\n): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>\nexport function injectLiveQuery(opts: any) {\n assertInInjectionContext(injectLiveQuery)\n const destroyRef = inject(DestroyRef)\n\n const collection = computed(() => {\n // Check if it's an existing collection\n if (isCollection(opts)) {\n return opts\n }\n\n if (typeof opts === `function`) {\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = opts(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: opts,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Check if it's reactive query options\n const isReactiveQueryOptions =\n opts &&\n typeof opts === `object` &&\n typeof opts.query === `function` &&\n typeof opts.params === `function`\n\n if (isReactiveQueryOptions) {\n const { params, query } = opts\n const currentParams = params()\n\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = query({ params: currentParams, q: queryBuilder })\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: () => result,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Handle LiveQueryCollectionConfig objects. Default startSync/gcTime to\n // match the query-fn and reactive-options paths, but let an explicit value\n // in the config win — otherwise a bare `{ query }` never syncs.\n if (opts && typeof opts === `object` && typeof opts.query === `function`) {\n return createLiveQueryCollection({ startSync: true, gcTime: 0, ...opts })\n }\n\n throw new Error(`Invalid options provided to injectLiveQuery`)\n })\n\n const state = signal(new Map<string | number, any>())\n const internalData = signal<Array<any>>([])\n const statusValue = signal<CollectionStatus | `disabled`>(`idle`)\n const status = computed(() => {\n const value = statusValue()\n return value === `idle` && !collection() ? `disabled` : value\n })\n const persistedStatus = signal<LiveQueryPersistedStatus>(`unavailable`)\n const persistedError = signal<unknown>(undefined)\n\n // Returns single item for singleResult collections, array otherwise\n const data = computed(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n return internalData()\n }\n return isSingleResultCollection(currentCollection)\n ? internalData()[0]\n : internalData()\n })\n\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n observer: LiveQueryObserver<any, any>,\n ) => {\n const newState = new Map(currentCollection.entries())\n const newData = Array.from(currentCollection.values())\n\n state.set(newState)\n internalData.set(newData)\n const snapshot = observer.getSnapshot()\n statusValue.set(snapshot.status)\n persistedStatus.set(snapshot.persistedStatus)\n persistedError.set(snapshot.persistedError)\n }\n\n let unsub: (() => void) | null = null\n const cleanup = () => {\n unsub?.()\n unsub = null\n }\n\n effect((onCleanup) => {\n const currentCollection = collection()\n\n // Handle null collection (disabled query)\n if (!currentCollection) {\n statusValue.set(`disabled` as const)\n persistedStatus.set(`unavailable`)\n persistedError.set(undefined)\n state.set(new Map())\n internalData.set([])\n cleanup()\n return\n }\n\n cleanup()\n\n // The shared observer owns sync start, subscription, the ready-race, and\n // status transitions; Angular re-reads the whole collection on each notify\n // (wholesale) into its signals.\n // Angular re-reads the collection on notify; wholesale mode preserves its\n // pre-observer loading policy (no initial-state snapshot request).\n const observer = createLiveQueryObserver(currentCollection, {\n mode: `wholesale`,\n })\n\n const unsubscribe = observer.subscribe(() => {\n syncDataFromCollection(currentCollection, observer)\n })\n // Wholesale attach suppresses listener calls raised by synchronous sync\n // startup. Read once after subscribe returns to capture that final state.\n syncDataFromCollection(currentCollection, observer)\n unsub = () => {\n unsubscribe()\n observer.dispose()\n }\n\n onCleanup(cleanup)\n })\n\n destroyRef.onDestroy(cleanup)\n\n return {\n state,\n data,\n // Loosely typed so the impl return stays compatible with every overload\n // (the shared `isCollection` guard narrows the computed to `Collection | null`).\n collection: collection as Signal<any>,\n status,\n isLoading: computed(() => status() === `loading`),\n isReady: computed(() => status() === `ready` || status() === `disabled`),\n persistedStatus,\n isPersistedReady: computed(() => persistedStatus() === `ready`),\n persistedError,\n isIdle: computed(() => status() === `idle`),\n isError: computed(() => status() === `error`),\n isCleanedUp: computed(() => status() === `cleaned-up`),\n }\n}\n"],"names":["assertInInjectionContext","inject","DestroyRef","computed","isCollection","BaseQueryBuilder","createLiveQueryCollection","signal","isSingleResultCollection","effect","createLiveQueryObserver"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
1
+ import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, LiveQueryPersistedStatus, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
2
2
  import { Signal } from '@angular/core';
3
3
  export * from '@tanstack/db';
4
4
  /**
@@ -18,6 +18,9 @@ export interface InjectLiveQueryResult<TContext extends Context> {
18
18
  isLoading: Signal<boolean>;
19
19
  /** A signal indicating whether the collection is ready */
20
20
  isReady: Signal<boolean>;
21
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
22
+ isPersistedReady: Signal<boolean>;
23
+ persistedError: Signal<unknown | undefined>;
21
24
  /** A signal indicating whether the collection is idle */
22
25
  isIdle: Signal<boolean>;
23
26
  /** A signal indicating whether the collection has an error */
@@ -25,6 +28,10 @@ export interface InjectLiveQueryResult<TContext extends Context> {
25
28
  /** A signal indicating whether the collection has been cleaned up */
26
29
  isCleanedUp: Signal<boolean>;
27
30
  }
31
+ type InferConditionalResultType<TContext extends Context> = TContext extends SingleResult ? InferResultType<TContext> | [] : InferResultType<TContext>;
32
+ export type InjectConditionalLiveQueryResult<TContext extends Context> = Omit<InjectLiveQueryResult<TContext>, `data`> & {
33
+ data: Signal<InferConditionalResultType<TContext>>;
34
+ };
28
35
  export interface InjectLiveQueryResultWithCollection<TResult extends object = any, TKey extends string | number = string | number, TUtils extends Record<string, any> = {}> {
29
36
  state: Signal<Map<TKey, TResult>>;
30
37
  data: Signal<Array<TResult>>;
@@ -32,6 +39,9 @@ export interface InjectLiveQueryResultWithCollection<TResult extends object = an
32
39
  status: Signal<CollectionStatus | `disabled`>;
33
40
  isLoading: Signal<boolean>;
34
41
  isReady: Signal<boolean>;
42
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
43
+ isPersistedReady: Signal<boolean>;
44
+ persistedError: Signal<unknown | undefined>;
35
45
  isIdle: Signal<boolean>;
36
46
  isError: Signal<boolean>;
37
47
  isCleanedUp: Signal<boolean>;
@@ -43,6 +53,9 @@ export interface InjectLiveQueryResultWithSingleResultCollection<TResult extends
43
53
  status: Signal<CollectionStatus | `disabled`>;
44
54
  isLoading: Signal<boolean>;
45
55
  isReady: Signal<boolean>;
56
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
57
+ isPersistedReady: Signal<boolean>;
58
+ persistedError: Signal<unknown | undefined>;
46
59
  isIdle: Signal<boolean>;
47
60
  isError: Signal<boolean>;
48
61
  isCleanedUp: Signal<boolean>;
@@ -60,9 +73,9 @@ export declare function injectLiveQuery<TContext extends Context, TParams extend
60
73
  params: TParams;
61
74
  q: InitialQueryBuilder;
62
75
  }) => QueryBuilder<TContext> | undefined | null;
63
- }): InjectLiveQueryResult<TContext>;
76
+ }): InjectConditionalLiveQueryResult<TContext>;
64
77
  export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>): InjectLiveQueryResult<TContext>;
65
- export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): InjectLiveQueryResult<TContext>;
78
+ export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): InjectConditionalLiveQueryResult<TContext>;
66
79
  export declare function injectLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>): InjectLiveQueryResult<TContext>;
67
80
  export declare function injectLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>;
68
81
  export declare function injectLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>;
@@ -1,4 +1,4 @@
1
- import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
1
+ import { Collection, CollectionStatus, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, LiveQueryPersistedStatus, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
2
2
  import { Signal } from '@angular/core';
3
3
  export * from '@tanstack/db';
4
4
  /**
@@ -18,6 +18,9 @@ export interface InjectLiveQueryResult<TContext extends Context> {
18
18
  isLoading: Signal<boolean>;
19
19
  /** A signal indicating whether the collection is ready */
20
20
  isReady: Signal<boolean>;
21
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
22
+ isPersistedReady: Signal<boolean>;
23
+ persistedError: Signal<unknown | undefined>;
21
24
  /** A signal indicating whether the collection is idle */
22
25
  isIdle: Signal<boolean>;
23
26
  /** A signal indicating whether the collection has an error */
@@ -25,6 +28,10 @@ export interface InjectLiveQueryResult<TContext extends Context> {
25
28
  /** A signal indicating whether the collection has been cleaned up */
26
29
  isCleanedUp: Signal<boolean>;
27
30
  }
31
+ type InferConditionalResultType<TContext extends Context> = TContext extends SingleResult ? InferResultType<TContext> | [] : InferResultType<TContext>;
32
+ export type InjectConditionalLiveQueryResult<TContext extends Context> = Omit<InjectLiveQueryResult<TContext>, `data`> & {
33
+ data: Signal<InferConditionalResultType<TContext>>;
34
+ };
28
35
  export interface InjectLiveQueryResultWithCollection<TResult extends object = any, TKey extends string | number = string | number, TUtils extends Record<string, any> = {}> {
29
36
  state: Signal<Map<TKey, TResult>>;
30
37
  data: Signal<Array<TResult>>;
@@ -32,6 +39,9 @@ export interface InjectLiveQueryResultWithCollection<TResult extends object = an
32
39
  status: Signal<CollectionStatus | `disabled`>;
33
40
  isLoading: Signal<boolean>;
34
41
  isReady: Signal<boolean>;
42
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
43
+ isPersistedReady: Signal<boolean>;
44
+ persistedError: Signal<unknown | undefined>;
35
45
  isIdle: Signal<boolean>;
36
46
  isError: Signal<boolean>;
37
47
  isCleanedUp: Signal<boolean>;
@@ -43,6 +53,9 @@ export interface InjectLiveQueryResultWithSingleResultCollection<TResult extends
43
53
  status: Signal<CollectionStatus | `disabled`>;
44
54
  isLoading: Signal<boolean>;
45
55
  isReady: Signal<boolean>;
56
+ persistedStatus: Signal<LiveQueryPersistedStatus>;
57
+ isPersistedReady: Signal<boolean>;
58
+ persistedError: Signal<unknown | undefined>;
46
59
  isIdle: Signal<boolean>;
47
60
  isError: Signal<boolean>;
48
61
  isCleanedUp: Signal<boolean>;
@@ -60,9 +73,9 @@ export declare function injectLiveQuery<TContext extends Context, TParams extend
60
73
  params: TParams;
61
74
  q: InitialQueryBuilder;
62
75
  }) => QueryBuilder<TContext> | undefined | null;
63
- }): InjectLiveQueryResult<TContext>;
76
+ }): InjectConditionalLiveQueryResult<TContext>;
64
77
  export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>): InjectLiveQueryResult<TContext>;
65
- export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): InjectLiveQueryResult<TContext>;
78
+ export declare function injectLiveQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext> | undefined | null): InjectConditionalLiveQueryResult<TContext>;
66
79
  export declare function injectLiveQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>): InjectLiveQueryResult<TContext>;
67
80
  export declare function injectLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>;
68
81
  export declare function injectLiveQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>;
package/dist/esm/index.js CHANGED
@@ -42,9 +42,13 @@ function injectLiveQuery(opts) {
42
42
  });
43
43
  const state = signal(/* @__PURE__ */ new Map());
44
44
  const internalData = signal([]);
45
- const status = signal(
46
- collection() ? `idle` : `disabled`
47
- );
45
+ const statusValue = signal(`idle`);
46
+ const status = computed(() => {
47
+ const value = statusValue();
48
+ return value === `idle` && !collection() ? `disabled` : value;
49
+ });
50
+ const persistedStatus = signal(`unavailable`);
51
+ const persistedError = signal(void 0);
48
52
  const data = computed(() => {
49
53
  const currentCollection = collection();
50
54
  if (!currentCollection) {
@@ -52,12 +56,15 @@ function injectLiveQuery(opts) {
52
56
  }
53
57
  return isSingleResultCollection(currentCollection) ? internalData()[0] : internalData();
54
58
  });
55
- const syncDataFromCollection = (currentCollection) => {
59
+ const syncDataFromCollection = (currentCollection, observer) => {
56
60
  const newState = new Map(currentCollection.entries());
57
61
  const newData = Array.from(currentCollection.values());
58
62
  state.set(newState);
59
63
  internalData.set(newData);
60
- status.set(currentCollection.status);
64
+ const snapshot = observer.getSnapshot();
65
+ statusValue.set(snapshot.status);
66
+ persistedStatus.set(snapshot.persistedStatus);
67
+ persistedError.set(snapshot.persistedError);
61
68
  };
62
69
  let unsub = null;
63
70
  const cleanup = () => {
@@ -67,7 +74,9 @@ function injectLiveQuery(opts) {
67
74
  effect((onCleanup) => {
68
75
  const currentCollection = collection();
69
76
  if (!currentCollection) {
70
- status.set(`disabled`);
77
+ statusValue.set(`disabled`);
78
+ persistedStatus.set(`unavailable`);
79
+ persistedError.set(void 0);
71
80
  state.set(/* @__PURE__ */ new Map());
72
81
  internalData.set([]);
73
82
  cleanup();
@@ -78,9 +87,9 @@ function injectLiveQuery(opts) {
78
87
  mode: `wholesale`
79
88
  });
80
89
  const unsubscribe = observer.subscribe(() => {
81
- syncDataFromCollection(currentCollection);
90
+ syncDataFromCollection(currentCollection, observer);
82
91
  });
83
- syncDataFromCollection(currentCollection);
92
+ syncDataFromCollection(currentCollection, observer);
84
93
  unsub = () => {
85
94
  unsubscribe();
86
95
  observer.dispose();
@@ -97,6 +106,9 @@ function injectLiveQuery(opts) {
97
106
  status,
98
107
  isLoading: computed(() => status() === `loading`),
99
108
  isReady: computed(() => status() === `ready` || status() === `disabled`),
109
+ persistedStatus,
110
+ isPersistedReady: computed(() => persistedStatus() === `ready`),
111
+ persistedError,
100
112
  isIdle: computed(() => status() === `idle`),
101
113
  isError: computed(() => status() === `error`),
102
114
  isCleanedUp: computed(() => status() === `cleaned-up`)
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import {\n DestroyRef,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core'\nimport {\n BaseQueryBuilder,\n createLiveQueryCollection,\n createLiveQueryObserver,\n isCollection,\n isSingleResultCollection,\n} from '@tanstack/db'\nimport type {\n Collection,\n CollectionStatus,\n Context,\n GetResult,\n InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\nimport type { Signal } from '@angular/core'\n\nexport * from '@tanstack/db'\n\n/**\n * The result of calling `injectLiveQuery`.\n * Contains reactive signals for the query state and data.\n */\nexport interface InjectLiveQueryResult<TContext extends Context> {\n /** A signal containing the complete state map of results keyed by their ID */\n state: Signal<Map<string | number, GetResult<TContext>>>\n /** A signal containing the results as an array, or single result for findOne queries */\n data: Signal<InferResultType<TContext>>\n /** A signal containing the underlying collection instance (null for disabled queries) */\n collection: Signal<Collection<\n GetResult<TContext>,\n string | number,\n {}\n > | null>\n /** A signal containing the current status of the collection */\n status: Signal<CollectionStatus | `disabled`>\n /** A signal indicating whether the collection is currently loading */\n isLoading: Signal<boolean>\n /** A signal indicating whether the collection is ready */\n isReady: Signal<boolean>\n /** A signal indicating whether the collection is idle */\n isIdle: Signal<boolean>\n /** A signal indicating whether the collection has an error */\n isError: Signal<boolean>\n /** A signal indicating whether the collection has been cleaned up */\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<Array<TResult>>\n collection: Signal<Collection<TResult, TKey, TUtils> | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithSingleResultCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<TResult | undefined>\n collection: Signal<(Collection<TResult, TKey, TUtils> & SingleResult) | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext>\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext> | undefined | null\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n): InjectLiveQueryResult<TContext>\n// Pre-created collection without singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>\n// Pre-created collection with singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult,\n): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>\nexport function injectLiveQuery(opts: any) {\n assertInInjectionContext(injectLiveQuery)\n const destroyRef = inject(DestroyRef)\n\n const collection = computed(() => {\n // Check if it's an existing collection\n if (isCollection(opts)) {\n return opts\n }\n\n if (typeof opts === `function`) {\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = opts(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: opts,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Check if it's reactive query options\n const isReactiveQueryOptions =\n opts &&\n typeof opts === `object` &&\n typeof opts.query === `function` &&\n typeof opts.params === `function`\n\n if (isReactiveQueryOptions) {\n const { params, query } = opts\n const currentParams = params()\n\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = query({ params: currentParams, q: queryBuilder })\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: () => result,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Handle LiveQueryCollectionConfig objects. Default startSync/gcTime to\n // match the query-fn and reactive-options paths, but let an explicit value\n // in the config win — otherwise a bare `{ query }` never syncs.\n if (opts && typeof opts === `object` && typeof opts.query === `function`) {\n return createLiveQueryCollection({ startSync: true, gcTime: 0, ...opts })\n }\n\n throw new Error(`Invalid options provided to injectLiveQuery`)\n })\n\n const state = signal(new Map<string | number, any>())\n const internalData = signal<Array<any>>([])\n const status = signal<CollectionStatus | `disabled`>(\n collection() ? `idle` : `disabled`,\n )\n\n // Returns single item for singleResult collections, array otherwise\n const data = computed(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n return internalData()\n }\n return isSingleResultCollection(currentCollection)\n ? internalData()[0]\n : internalData()\n })\n\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n ) => {\n const newState = new Map(currentCollection.entries())\n const newData = Array.from(currentCollection.values())\n\n state.set(newState)\n internalData.set(newData)\n status.set(currentCollection.status)\n }\n\n let unsub: (() => void) | null = null\n const cleanup = () => {\n unsub?.()\n unsub = null\n }\n\n effect((onCleanup) => {\n const currentCollection = collection()\n\n // Handle null collection (disabled query)\n if (!currentCollection) {\n status.set(`disabled` as const)\n state.set(new Map())\n internalData.set([])\n cleanup()\n return\n }\n\n cleanup()\n\n // The shared observer owns sync start, subscription, the ready-race, and\n // status transitions; Angular re-reads the whole collection on each notify\n // (wholesale) into its signals.\n // Angular re-reads the collection on notify; wholesale mode preserves its\n // pre-observer loading policy (no initial-state snapshot request).\n const observer = createLiveQueryObserver(currentCollection, {\n mode: `wholesale`,\n })\n\n const unsubscribe = observer.subscribe(() => {\n syncDataFromCollection(currentCollection)\n })\n // Wholesale attach suppresses listener calls raised by synchronous sync\n // startup. Read once after subscribe returns to capture that final state.\n syncDataFromCollection(currentCollection)\n unsub = () => {\n unsubscribe()\n observer.dispose()\n }\n\n onCleanup(cleanup)\n })\n\n destroyRef.onDestroy(cleanup)\n\n return {\n state,\n data,\n // Loosely typed so the impl return stays compatible with every overload\n // (the shared `isCollection` guard narrows the computed to `Collection | null`).\n collection: collection as Signal<any>,\n status,\n isLoading: computed(() => status() === `loading`),\n isReady: computed(() => status() === `ready` || status() === `disabled`),\n isIdle: computed(() => status() === `idle`),\n isError: computed(() => status() === `error`),\n isCleanedUp: computed(() => status() === `cleaned-up`),\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import {\n DestroyRef,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core'\nimport {\n BaseQueryBuilder,\n createLiveQueryCollection,\n createLiveQueryObserver,\n isCollection,\n isSingleResultCollection,\n} from '@tanstack/db'\nimport type {\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'\nimport type { Signal } from '@angular/core'\n\nexport * from '@tanstack/db'\n\n/**\n * The result of calling `injectLiveQuery`.\n * Contains reactive signals for the query state and data.\n */\nexport interface InjectLiveQueryResult<TContext extends Context> {\n /** A signal containing the complete state map of results keyed by their ID */\n state: Signal<Map<string | number, GetResult<TContext>>>\n /** A signal containing the results as an array, or single result for findOne queries */\n data: Signal<InferResultType<TContext>>\n /** A signal containing the underlying collection instance (null for disabled queries) */\n collection: Signal<Collection<\n GetResult<TContext>,\n string | number,\n {}\n > | null>\n /** A signal containing the current status of the collection */\n status: Signal<CollectionStatus | `disabled`>\n /** A signal indicating whether the collection is currently loading */\n isLoading: Signal<boolean>\n /** A signal indicating whether the collection is ready */\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n /** A signal indicating whether the collection is idle */\n isIdle: Signal<boolean>\n /** A signal indicating whether the collection has an error */\n isError: Signal<boolean>\n /** A signal indicating whether the collection has been cleaned up */\n isCleanedUp: Signal<boolean>\n}\n\ntype InferConditionalResultType<TContext extends Context> =\n TContext extends SingleResult\n ? InferResultType<TContext> | []\n : InferResultType<TContext>\n\nexport type InjectConditionalLiveQueryResult<TContext extends Context> = Omit<\n InjectLiveQueryResult<TContext>,\n `data`\n> & {\n data: Signal<InferConditionalResultType<TContext>>\n}\n\nexport interface InjectLiveQueryResultWithCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<Array<TResult>>\n collection: Signal<Collection<TResult, TKey, TUtils> | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport interface InjectLiveQueryResultWithSingleResultCollection<\n TResult extends object = any,\n TKey extends string | number = string | number,\n TUtils extends Record<string, any> = {},\n> {\n state: Signal<Map<TKey, TResult>>\n data: Signal<TResult | undefined>\n collection: Signal<(Collection<TResult, TKey, TUtils> & SingleResult) | null>\n status: Signal<CollectionStatus | `disabled`>\n isLoading: Signal<boolean>\n isReady: Signal<boolean>\n persistedStatus: Signal<LiveQueryPersistedStatus>\n isPersistedReady: Signal<boolean>\n persistedError: Signal<unknown | undefined>\n isIdle: Signal<boolean>\n isError: Signal<boolean>\n isCleanedUp: Signal<boolean>\n}\n\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext>\n}): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<\n TContext extends Context,\n TParams extends any,\n>(options: {\n params: () => TParams\n query: (args: {\n params: TParams\n q: InitialQueryBuilder\n }) => QueryBuilder<TContext> | undefined | null\n}): InjectConditionalLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n): InjectLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n queryFn: (\n q: InitialQueryBuilder,\n ) => QueryBuilder<TContext> | undefined | null,\n): InjectConditionalLiveQueryResult<TContext>\nexport function injectLiveQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n): InjectLiveQueryResult<TContext>\n// Pre-created collection without singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n): InjectLiveQueryResultWithCollection<TResult, TKey, TUtils>\n// Pre-created collection with singleResult\nexport function injectLiveQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult,\n): InjectLiveQueryResultWithSingleResultCollection<TResult, TKey, TUtils>\nexport function injectLiveQuery(opts: any) {\n assertInInjectionContext(injectLiveQuery)\n const destroyRef = inject(DestroyRef)\n\n const collection = computed(() => {\n // Check if it's an existing collection\n if (isCollection(opts)) {\n return opts\n }\n\n if (typeof opts === `function`) {\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = opts(queryBuilder)\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: opts,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Check if it's reactive query options\n const isReactiveQueryOptions =\n opts &&\n typeof opts === `object` &&\n typeof opts.query === `function` &&\n typeof opts.params === `function`\n\n if (isReactiveQueryOptions) {\n const { params, query } = opts\n const currentParams = params()\n\n // Check if query function returns null/undefined (disabled query)\n const queryBuilder = new BaseQueryBuilder() as InitialQueryBuilder\n const result = query({ params: currentParams, q: queryBuilder })\n\n if (result === undefined || result === null) {\n // Disabled query - return null\n return null\n }\n\n return createLiveQueryCollection({\n query: () => result,\n startSync: true,\n gcTime: 0,\n })\n }\n\n // Handle LiveQueryCollectionConfig objects. Default startSync/gcTime to\n // match the query-fn and reactive-options paths, but let an explicit value\n // in the config win — otherwise a bare `{ query }` never syncs.\n if (opts && typeof opts === `object` && typeof opts.query === `function`) {\n return createLiveQueryCollection({ startSync: true, gcTime: 0, ...opts })\n }\n\n throw new Error(`Invalid options provided to injectLiveQuery`)\n })\n\n const state = signal(new Map<string | number, any>())\n const internalData = signal<Array<any>>([])\n const statusValue = signal<CollectionStatus | `disabled`>(`idle`)\n const status = computed(() => {\n const value = statusValue()\n return value === `idle` && !collection() ? `disabled` : value\n })\n const persistedStatus = signal<LiveQueryPersistedStatus>(`unavailable`)\n const persistedError = signal<unknown>(undefined)\n\n // Returns single item for singleResult collections, array otherwise\n const data = computed(() => {\n const currentCollection = collection()\n if (!currentCollection) {\n return internalData()\n }\n return isSingleResultCollection(currentCollection)\n ? internalData()[0]\n : internalData()\n })\n\n const syncDataFromCollection = (\n currentCollection: Collection<any, any, any>,\n observer: LiveQueryObserver<any, any>,\n ) => {\n const newState = new Map(currentCollection.entries())\n const newData = Array.from(currentCollection.values())\n\n state.set(newState)\n internalData.set(newData)\n const snapshot = observer.getSnapshot()\n statusValue.set(snapshot.status)\n persistedStatus.set(snapshot.persistedStatus)\n persistedError.set(snapshot.persistedError)\n }\n\n let unsub: (() => void) | null = null\n const cleanup = () => {\n unsub?.()\n unsub = null\n }\n\n effect((onCleanup) => {\n const currentCollection = collection()\n\n // Handle null collection (disabled query)\n if (!currentCollection) {\n statusValue.set(`disabled` as const)\n persistedStatus.set(`unavailable`)\n persistedError.set(undefined)\n state.set(new Map())\n internalData.set([])\n cleanup()\n return\n }\n\n cleanup()\n\n // The shared observer owns sync start, subscription, the ready-race, and\n // status transitions; Angular re-reads the whole collection on each notify\n // (wholesale) into its signals.\n // Angular re-reads the collection on notify; wholesale mode preserves its\n // pre-observer loading policy (no initial-state snapshot request).\n const observer = createLiveQueryObserver(currentCollection, {\n mode: `wholesale`,\n })\n\n const unsubscribe = observer.subscribe(() => {\n syncDataFromCollection(currentCollection, observer)\n })\n // Wholesale attach suppresses listener calls raised by synchronous sync\n // startup. Read once after subscribe returns to capture that final state.\n syncDataFromCollection(currentCollection, observer)\n unsub = () => {\n unsubscribe()\n observer.dispose()\n }\n\n onCleanup(cleanup)\n })\n\n destroyRef.onDestroy(cleanup)\n\n return {\n state,\n data,\n // Loosely typed so the impl return stays compatible with every overload\n // (the shared `isCollection` guard narrows the computed to `Collection | null`).\n collection: collection as Signal<any>,\n status,\n isLoading: computed(() => status() === `loading`),\n isReady: computed(() => status() === `ready` || status() === `disabled`),\n persistedStatus,\n isPersistedReady: computed(() => persistedStatus() === `ready`),\n persistedError,\n isIdle: computed(() => status() === `idle`),\n isError: computed(() => status() === `error`),\n isCleanedUp: computed(() => status() === `cleaned-up`),\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/angular-db",
3
- "version": "0.1.89",
3
+ "version": "0.2.0",
4
4
  "description": "Angular integration for @tanstack/db",
5
5
  "author": "Ethan McDaniel",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "skills"
41
41
  ],
42
42
  "dependencies": {
43
- "@tanstack/db": "0.9.0"
43
+ "@tanstack/db": "0.11.0"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@angular/core": ">=16.0.0",
package/src/index.ts CHANGED
@@ -21,6 +21,8 @@ import type {
21
21
  InferResultType,
22
22
  InitialQueryBuilder,
23
23
  LiveQueryCollectionConfig,
24
+ LiveQueryObserver,
25
+ LiveQueryPersistedStatus,
24
26
  NonSingleResult,
25
27
  QueryBuilder,
26
28
  SingleResult,
@@ -50,6 +52,9 @@ export interface InjectLiveQueryResult<TContext extends Context> {
50
52
  isLoading: Signal<boolean>
51
53
  /** A signal indicating whether the collection is ready */
52
54
  isReady: Signal<boolean>
55
+ persistedStatus: Signal<LiveQueryPersistedStatus>
56
+ isPersistedReady: Signal<boolean>
57
+ persistedError: Signal<unknown | undefined>
53
58
  /** A signal indicating whether the collection is idle */
54
59
  isIdle: Signal<boolean>
55
60
  /** A signal indicating whether the collection has an error */
@@ -58,6 +63,18 @@ export interface InjectLiveQueryResult<TContext extends Context> {
58
63
  isCleanedUp: Signal<boolean>
59
64
  }
60
65
 
66
+ type InferConditionalResultType<TContext extends Context> =
67
+ TContext extends SingleResult
68
+ ? InferResultType<TContext> | []
69
+ : InferResultType<TContext>
70
+
71
+ export type InjectConditionalLiveQueryResult<TContext extends Context> = Omit<
72
+ InjectLiveQueryResult<TContext>,
73
+ `data`
74
+ > & {
75
+ data: Signal<InferConditionalResultType<TContext>>
76
+ }
77
+
61
78
  export interface InjectLiveQueryResultWithCollection<
62
79
  TResult extends object = any,
63
80
  TKey extends string | number = string | number,
@@ -69,6 +86,9 @@ export interface InjectLiveQueryResultWithCollection<
69
86
  status: Signal<CollectionStatus | `disabled`>
70
87
  isLoading: Signal<boolean>
71
88
  isReady: Signal<boolean>
89
+ persistedStatus: Signal<LiveQueryPersistedStatus>
90
+ isPersistedReady: Signal<boolean>
91
+ persistedError: Signal<unknown | undefined>
72
92
  isIdle: Signal<boolean>
73
93
  isError: Signal<boolean>
74
94
  isCleanedUp: Signal<boolean>
@@ -85,6 +105,9 @@ export interface InjectLiveQueryResultWithSingleResultCollection<
85
105
  status: Signal<CollectionStatus | `disabled`>
86
106
  isLoading: Signal<boolean>
87
107
  isReady: Signal<boolean>
108
+ persistedStatus: Signal<LiveQueryPersistedStatus>
109
+ isPersistedReady: Signal<boolean>
110
+ persistedError: Signal<unknown | undefined>
88
111
  isIdle: Signal<boolean>
89
112
  isError: Signal<boolean>
90
113
  isCleanedUp: Signal<boolean>
@@ -109,7 +132,7 @@ export function injectLiveQuery<
109
132
  params: TParams
110
133
  q: InitialQueryBuilder
111
134
  }) => QueryBuilder<TContext> | undefined | null
112
- }): InjectLiveQueryResult<TContext>
135
+ }): InjectConditionalLiveQueryResult<TContext>
113
136
  export function injectLiveQuery<TContext extends Context>(
114
137
  queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,
115
138
  ): InjectLiveQueryResult<TContext>
@@ -117,7 +140,7 @@ export function injectLiveQuery<TContext extends Context>(
117
140
  queryFn: (
118
141
  q: InitialQueryBuilder,
119
142
  ) => QueryBuilder<TContext> | undefined | null,
120
- ): InjectLiveQueryResult<TContext>
143
+ ): InjectConditionalLiveQueryResult<TContext>
121
144
  export function injectLiveQuery<TContext extends Context>(
122
145
  config: LiveQueryCollectionConfig<TContext>,
123
146
  ): InjectLiveQueryResult<TContext>
@@ -203,9 +226,13 @@ export function injectLiveQuery(opts: any) {
203
226
 
204
227
  const state = signal(new Map<string | number, any>())
205
228
  const internalData = signal<Array<any>>([])
206
- const status = signal<CollectionStatus | `disabled`>(
207
- collection() ? `idle` : `disabled`,
208
- )
229
+ const statusValue = signal<CollectionStatus | `disabled`>(`idle`)
230
+ const status = computed(() => {
231
+ const value = statusValue()
232
+ return value === `idle` && !collection() ? `disabled` : value
233
+ })
234
+ const persistedStatus = signal<LiveQueryPersistedStatus>(`unavailable`)
235
+ const persistedError = signal<unknown>(undefined)
209
236
 
210
237
  // Returns single item for singleResult collections, array otherwise
211
238
  const data = computed(() => {
@@ -220,13 +247,17 @@ export function injectLiveQuery(opts: any) {
220
247
 
221
248
  const syncDataFromCollection = (
222
249
  currentCollection: Collection<any, any, any>,
250
+ observer: LiveQueryObserver<any, any>,
223
251
  ) => {
224
252
  const newState = new Map(currentCollection.entries())
225
253
  const newData = Array.from(currentCollection.values())
226
254
 
227
255
  state.set(newState)
228
256
  internalData.set(newData)
229
- status.set(currentCollection.status)
257
+ const snapshot = observer.getSnapshot()
258
+ statusValue.set(snapshot.status)
259
+ persistedStatus.set(snapshot.persistedStatus)
260
+ persistedError.set(snapshot.persistedError)
230
261
  }
231
262
 
232
263
  let unsub: (() => void) | null = null
@@ -240,7 +271,9 @@ export function injectLiveQuery(opts: any) {
240
271
 
241
272
  // Handle null collection (disabled query)
242
273
  if (!currentCollection) {
243
- status.set(`disabled` as const)
274
+ statusValue.set(`disabled` as const)
275
+ persistedStatus.set(`unavailable`)
276
+ persistedError.set(undefined)
244
277
  state.set(new Map())
245
278
  internalData.set([])
246
279
  cleanup()
@@ -259,11 +292,11 @@ export function injectLiveQuery(opts: any) {
259
292
  })
260
293
 
261
294
  const unsubscribe = observer.subscribe(() => {
262
- syncDataFromCollection(currentCollection)
295
+ syncDataFromCollection(currentCollection, observer)
263
296
  })
264
297
  // Wholesale attach suppresses listener calls raised by synchronous sync
265
298
  // startup. Read once after subscribe returns to capture that final state.
266
- syncDataFromCollection(currentCollection)
299
+ syncDataFromCollection(currentCollection, observer)
267
300
  unsub = () => {
268
301
  unsubscribe()
269
302
  observer.dispose()
@@ -283,6 +316,9 @@ export function injectLiveQuery(opts: any) {
283
316
  status,
284
317
  isLoading: computed(() => status() === `loading`),
285
318
  isReady: computed(() => status() === `ready` || status() === `disabled`),
319
+ persistedStatus,
320
+ isPersistedReady: computed(() => persistedStatus() === `ready`),
321
+ persistedError,
286
322
  isIdle: computed(() => status() === `idle`),
287
323
  isError: computed(() => status() === `error`),
288
324
  isCleanedUp: computed(() => status() === `cleaned-up`),