@signalium/query 1.0.8 → 1.0.10

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/EntityMap.js +20 -1
  3. package/dist/cjs/EntityMap.js.map +1 -1
  4. package/dist/cjs/QueryClient.js +27 -3
  5. package/dist/cjs/QueryClient.js.map +1 -1
  6. package/dist/cjs/QueryResult.js +410 -33
  7. package/dist/cjs/QueryResult.js.map +1 -1
  8. package/dist/cjs/QueryStore.js +78 -5
  9. package/dist/cjs/QueryStore.js.map +1 -1
  10. package/dist/cjs/errors.js +6 -0
  11. package/dist/cjs/errors.js.map +1 -1
  12. package/dist/cjs/index.js +3 -1
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/parseEntities.js +14 -2
  15. package/dist/cjs/parseEntities.js.map +1 -1
  16. package/dist/cjs/proxy.js +22 -5
  17. package/dist/cjs/proxy.js.map +1 -1
  18. package/dist/cjs/query.js +64 -1
  19. package/dist/cjs/query.js.map +1 -1
  20. package/dist/cjs/react/use-query.js.map +1 -1
  21. package/dist/cjs/stores/async.js.map +1 -1
  22. package/dist/cjs/stores/sync.js.map +1 -1
  23. package/dist/cjs/typeDefs.js +136 -3
  24. package/dist/cjs/typeDefs.js.map +1 -1
  25. package/dist/cjs/types.js.map +1 -1
  26. package/dist/esm/EntityMap.d.ts +3 -1
  27. package/dist/esm/EntityMap.d.ts.map +1 -1
  28. package/dist/esm/EntityMap.js +20 -1
  29. package/dist/esm/EntityMap.js.map +1 -1
  30. package/dist/esm/QueryClient.d.ts +47 -12
  31. package/dist/esm/QueryClient.d.ts.map +1 -1
  32. package/dist/esm/QueryClient.js +25 -3
  33. package/dist/esm/QueryClient.js.map +1 -1
  34. package/dist/esm/QueryResult.d.ts +34 -6
  35. package/dist/esm/QueryResult.d.ts.map +1 -1
  36. package/dist/esm/QueryResult.js +414 -37
  37. package/dist/esm/QueryResult.js.map +1 -1
  38. package/dist/esm/QueryStore.d.ts +17 -9
  39. package/dist/esm/QueryStore.d.ts.map +1 -1
  40. package/dist/esm/QueryStore.js +75 -4
  41. package/dist/esm/QueryStore.js.map +1 -1
  42. package/dist/esm/errors.d.ts.map +1 -1
  43. package/dist/esm/errors.js +6 -0
  44. package/dist/esm/errors.js.map +1 -1
  45. package/dist/esm/index.d.ts +1 -1
  46. package/dist/esm/index.d.ts.map +1 -1
  47. package/dist/esm/index.js +1 -1
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/parseEntities.d.ts.map +1 -1
  50. package/dist/esm/parseEntities.js +14 -2
  51. package/dist/esm/parseEntities.js.map +1 -1
  52. package/dist/esm/proxy.d.ts +1 -0
  53. package/dist/esm/proxy.d.ts.map +1 -1
  54. package/dist/esm/proxy.js +22 -6
  55. package/dist/esm/proxy.js.map +1 -1
  56. package/dist/esm/query.d.ts +27 -11
  57. package/dist/esm/query.d.ts.map +1 -1
  58. package/dist/esm/query.js +64 -1
  59. package/dist/esm/query.js.map +1 -1
  60. package/dist/esm/react/use-query.d.ts +2 -2
  61. package/dist/esm/react/use-query.d.ts.map +1 -1
  62. package/dist/esm/react/use-query.js.map +1 -1
  63. package/dist/esm/stores/async.d.ts +3 -3
  64. package/dist/esm/stores/async.d.ts.map +1 -1
  65. package/dist/esm/stores/async.js.map +1 -1
  66. package/dist/esm/stores/sync.d.ts +3 -3
  67. package/dist/esm/stores/sync.d.ts.map +1 -1
  68. package/dist/esm/stores/sync.js.map +1 -1
  69. package/dist/esm/typeDefs.d.ts +30 -0
  70. package/dist/esm/typeDefs.d.ts.map +1 -1
  71. package/dist/esm/typeDefs.js +134 -2
  72. package/dist/esm/typeDefs.js.map +1 -1
  73. package/dist/esm/types.d.ts +71 -23
  74. package/dist/esm/types.d.ts.map +1 -1
  75. package/dist/esm/types.js.map +1 -1
  76. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"QueryClient.js","sourceRoot":"","sources":["../../src/QueryClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueryClient.js","sourceRoot":"","sources":["../../src/QueryClient.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
- import { BaseQueryResult } from './types.js';
1
+ import { BaseQueryResult, QueryExtra } from './types.js';
2
2
  import { type AnyQueryDefinition, type QueryClient, type QueryParams } from './QueryClient.js';
3
3
  /**
4
4
  * QueryResult wraps a DiscriminatedReactivePromise and adds additional functionality
5
5
  * like refetch, while forwarding all the base relay properties.
6
6
  * This class combines the old QueryInstance and QueryResultImpl into a single entity.
7
7
  */
8
- export declare class QueryResultImpl<T> implements BaseQueryResult<T> {
9
- def: AnyQueryDefinition<any, any>;
8
+ export declare class QueryResultImpl<T> implements BaseQueryResult<T, unknown, unknown> {
9
+ def: AnyQueryDefinition<any, any, any>;
10
10
  queryKey: number;
11
11
  private queryClient;
12
12
  private initialized;
@@ -15,17 +15,21 @@ export declare class QueryResultImpl<T> implements BaseQueryResult<T> {
15
15
  private updatedAt;
16
16
  private params;
17
17
  private refIds;
18
+ private allNestedRefIdsSignal;
18
19
  private refetchPromise;
19
20
  private fetchMorePromise;
20
21
  private attemptCount;
21
22
  private unsubscribe?;
23
+ private streamUnsubscribe?;
22
24
  private relay;
23
25
  private _relayState;
24
26
  private wasOffline;
25
27
  private get relayState();
28
+ private _extra;
29
+ private get extraData();
26
30
  private _nextPageParams;
27
31
  private get nextPageParams();
28
- constructor(def: AnyQueryDefinition<any, any>, queryClient: QueryClient, queryKey: number, params: QueryParams | undefined);
32
+ constructor(def: AnyQueryDefinition<any, any, any>, queryClient: QueryClient, queryKey: number, params: QueryParams | undefined);
29
33
  get value(): T | undefined;
30
34
  get error(): unknown;
31
35
  get isPending(): boolean;
@@ -40,13 +44,15 @@ export declare class QueryResultImpl<T> implements BaseQueryResult<T> {
40
44
  catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined): Promise<T | TResult>;
41
45
  finally(onfinally?: (() => void) | null | undefined): Promise<T>;
42
46
  get [Symbol.toStringTag](): string;
47
+ private getAllEntityRefs;
43
48
  /**
44
49
  * Initialize the query by loading from cache and fetching if stale
45
50
  */
46
51
  private initialize;
47
52
  /**
48
- * Handle stream updates by merging with existing entity.
49
- * Deep merging is handled automatically by parseEntities/setEntity.
53
+ * Handle stream updates. This method handles both StreamQuery and Query/InfiniteQuery with stream options.
54
+ * - For StreamQuery: directly updates the relay state with the entity
55
+ * - For Query/InfiniteQuery with stream: updates entities in response or adds to orphans
50
56
  */
51
57
  private setupSubscription;
52
58
  /**
@@ -59,6 +65,28 @@ export declare class QueryResultImpl<T> implements BaseQueryResult<T> {
59
65
  get isFetchingMore(): boolean;
60
66
  get isFetching(): boolean;
61
67
  get hasNextPage(): boolean;
68
+ get extra(): QueryExtra<unknown, unknown>;
69
+ /**
70
+ * Persist the current extra data to the store
71
+ */
72
+ private persistExtraData;
73
+ /**
74
+ * Get extra data for persistence (converts Sets to arrays of entity ref IDs)
75
+ */
76
+ private getExtraForPersistence;
77
+ /**
78
+ * Add an optimistic insert to the query result.
79
+ * The insert will be automatically removed when:
80
+ * - The entity appears in a refetched response
81
+ * - The entity appears as a stream orphan
82
+ * - refetch() is called
83
+ */
84
+ addOptimisticInsert(insert: Record<string, unknown>): void;
85
+ /**
86
+ * Remove an optimistic insert from the query result.
87
+ * This is a no-op if the insert has already been removed.
88
+ */
89
+ removeOptimisticInsert(insert: Record<string, unknown>): void;
62
90
  get isStale(): boolean;
63
91
  get isPaused(): boolean;
64
92
  private getRetryConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"QueryResult.d.ts","sourceRoot":"","sources":["../../src/QueryResult.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,eAAe,EAMhB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAKL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,qBAAa,eAAe,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;IAC3D,GAAG,EAAE,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,MAAM,CAAsC;IAEpD,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,WAAW,CAAC,CAAyB;IAE7C,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,WAAW,CAA0C;IAC7D,OAAO,CAAC,UAAU,CAAkB;IAEpC,OAAO,KAAK,UAAU,GAQrB;IAED,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,KAAK,cAAc,GAuCzB;gBAGC,GAAG,EAAE,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,EACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAAG,SAAS;IA8FjC,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAEzB;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAID,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,KAAK,OAAO,GAElB;IAED,OAAO,KAAK,MAAM,GAEjB;IAGD,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,KAAK,EACjC,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EACjF,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAClF,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAI/B,KAAK,CAAC,OAAO,GAAG,KAAK,EACnB,UAAU,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAChF,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC;IAIvB,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAEjC;IAMD;;OAEG;YACW,UAAU;IA2CxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;OAEG;YACW,QAAQ;IA0FtB,OAAO,QAAO,OAAO,CAAC,CAAC,CAAC,CAsCtB;IAEF,aAAa,QAAO,OAAO,CAAC,CAAC,CAAC,CA0C5B;IAMF,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,OAAO,IAAI,OAAO,CAYrB;IAED,IAAI,QAAQ,IAAI,OAAO,CAuBtB;IAED,OAAO,CAAC,cAAc;CAgCvB"}
1
+ {"version":3,"file":"QueryResult.d.ts","sourceRoot":"","sources":["../../src/QueryResult.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,252 @@
1
- import { relay, signal } from 'signalium';
1
+ import { relay, signal, reactiveSignal, notifier, } from 'signalium';
2
2
  import { setReactivePromise } from 'signalium/utils';
3
- import { NetworkMode, } from './types.js';
4
- import { parseValue } from './proxy.js';
5
- import { parseEntities } from './parseEntities.js';
3
+ import { NetworkMode } from './types.js';
4
+ import { getProxyId, parseValue } from './proxy.js';
5
+ import { parseEntities, parseObjectEntities } from './parseEntities.js';
6
6
  import { ValidatorDef } from './typeDefs.js';
7
+ // ======================================================
8
+ // QueryResultExtra - Manages stream orphans and optimistic inserts
9
+ // ======================================================
10
+ /**
11
+ * Manages extra data for a query result: stream orphans and optimistic inserts.
12
+ * Created lazily when first needed.
13
+ */
14
+ class QueryResultExtra {
15
+ _streamOrphansNotifier = undefined;
16
+ _streamOrphans = undefined;
17
+ _optimisticInsertsNotifier = undefined;
18
+ _optimisticInserts = undefined;
19
+ onChanged;
20
+ constructor(onChanged) {
21
+ this.onChanged = onChanged;
22
+ }
23
+ get streamOrphansNotifier() {
24
+ return this._streamOrphansNotifier ?? (this._streamOrphansNotifier = notifier());
25
+ }
26
+ get optimisticInsertsNotifier() {
27
+ return this._optimisticInsertsNotifier ?? (this._optimisticInsertsNotifier = notifier());
28
+ }
29
+ get streamOrphans() {
30
+ return this._streamOrphans ?? (this._streamOrphans = new Set());
31
+ }
32
+ get optimisticInserts() {
33
+ return this._optimisticInserts ?? (this._optimisticInserts = new Set());
34
+ }
35
+ /**
36
+ * Returns the QueryExtra object for public API consumption.
37
+ * Consumes the notifiers to establish reactive tracking.
38
+ */
39
+ getExtra() {
40
+ this.streamOrphansNotifier.consume();
41
+ this.optimisticInsertsNotifier.consume();
42
+ return {
43
+ streamOrphans: this.streamOrphans,
44
+ optimisticInserts: this.optimisticInserts,
45
+ };
46
+ }
47
+ /**
48
+ * Add a stream orphan entity.
49
+ * Returns true if the orphan was added (not a duplicate).
50
+ */
51
+ addStreamOrphan(entity) {
52
+ const orphans = this.streamOrphans;
53
+ const sizeBefore = orphans.size;
54
+ orphans.add(entity);
55
+ if (orphans.size !== sizeBefore) {
56
+ this.streamOrphansNotifier.notify();
57
+ // Check if this orphan was an optimistic insert - if so, remove it
58
+ const proxyId = getProxyId(entity);
59
+ if (proxyId !== undefined) {
60
+ this.removeOptimisticInsertById(proxyId);
61
+ }
62
+ this.onChanged();
63
+ return true;
64
+ }
65
+ return false;
66
+ }
67
+ /**
68
+ * Add an optimistic insert entity.
69
+ * Returns true if the insert was added (not a duplicate).
70
+ */
71
+ addOptimisticInsert(entity) {
72
+ const inserts = this.optimisticInserts;
73
+ const sizeBefore = inserts.size;
74
+ inserts.add(entity);
75
+ if (inserts.size !== sizeBefore) {
76
+ this.optimisticInsertsNotifier.notify();
77
+ this.onChanged();
78
+ return true;
79
+ }
80
+ return false;
81
+ }
82
+ /**
83
+ * Remove an optimistic insert by its entity.
84
+ * Returns true if the insert was removed.
85
+ */
86
+ removeOptimisticInsert(entity) {
87
+ const proxyId = getProxyId(entity);
88
+ if (proxyId === undefined) {
89
+ return false;
90
+ }
91
+ return this.removeOptimisticInsertById(proxyId);
92
+ }
93
+ /**
94
+ * Remove an optimistic insert by proxy ID.
95
+ */
96
+ removeOptimisticInsertById(proxyId) {
97
+ const inserts = this._optimisticInserts;
98
+ if (inserts === undefined || inserts.size === 0) {
99
+ return false;
100
+ }
101
+ for (const existing of inserts) {
102
+ if (getProxyId(existing) === proxyId) {
103
+ inserts.delete(existing);
104
+ this.optimisticInsertsNotifier.notify();
105
+ this.onChanged();
106
+ return true;
107
+ }
108
+ }
109
+ return false;
110
+ }
111
+ /**
112
+ * Check if a proxy ID exists in stream orphans.
113
+ */
114
+ hasOrphanWithId(proxyId) {
115
+ const orphans = this._streamOrphans;
116
+ if (orphans === undefined) {
117
+ return false;
118
+ }
119
+ for (const orphan of orphans) {
120
+ if (getProxyId(orphan) === proxyId) {
121
+ return true;
122
+ }
123
+ }
124
+ return false;
125
+ }
126
+ /**
127
+ * Reconcile orphans and optimistic inserts against the main response entity refs.
128
+ * Removes any that now exist in the main response.
129
+ */
130
+ reconcile(allRefIds) {
131
+ // Check stream orphans for entities that now exist in main response
132
+ const orphans = this._streamOrphans;
133
+ if (orphans !== undefined && orphans.size > 0) {
134
+ let orphansChanged = false;
135
+ for (const orphan of orphans) {
136
+ const entityRefId = getProxyId(orphan);
137
+ if (entityRefId !== undefined && allRefIds.has(entityRefId)) {
138
+ orphans.delete(orphan);
139
+ orphansChanged = true;
140
+ }
141
+ }
142
+ if (orphansChanged) {
143
+ this.streamOrphansNotifier.notify();
144
+ }
145
+ }
146
+ // Check optimistic inserts for entities that now exist in main response or stream orphans
147
+ const inserts = this._optimisticInserts;
148
+ if (inserts !== undefined && inserts.size > 0) {
149
+ let insertsChanged = false;
150
+ for (const insert of inserts) {
151
+ const entityRefId = getProxyId(insert);
152
+ if (entityRefId !== undefined) {
153
+ // Remove if entity is now in main response
154
+ if (allRefIds.has(entityRefId)) {
155
+ inserts.delete(insert);
156
+ insertsChanged = true;
157
+ }
158
+ // Also remove if entity is now in stream orphans
159
+ else if (orphans !== undefined && orphans.has(insert)) {
160
+ inserts.delete(insert);
161
+ insertsChanged = true;
162
+ }
163
+ }
164
+ }
165
+ if (insertsChanged) {
166
+ this.optimisticInsertsNotifier.notify();
167
+ }
168
+ }
169
+ }
170
+ /**
171
+ * Clear all stream orphans and optimistic inserts.
172
+ * Called on refetch.
173
+ */
174
+ clear() {
175
+ let changed = false;
176
+ if (this._streamOrphans !== undefined && this._streamOrphans.size > 0) {
177
+ this._streamOrphans = undefined;
178
+ this.streamOrphansNotifier.notify();
179
+ changed = true;
180
+ }
181
+ if (this._optimisticInserts !== undefined && this._optimisticInserts.size > 0) {
182
+ this._optimisticInserts = undefined;
183
+ this.optimisticInsertsNotifier.notify();
184
+ changed = true;
185
+ }
186
+ if (changed) {
187
+ this.onChanged();
188
+ }
189
+ }
190
+ /**
191
+ * Load extra data from cached values.
192
+ */
193
+ loadFromCache(cachedExtra, queryClient, streamShape, optimisticInsertsShape) {
194
+ if (cachedExtra.streamOrphanRefs && cachedExtra.streamOrphanRefs.length > 0 && streamShape) {
195
+ const orphans = this.streamOrphans;
196
+ for (const refId of cachedExtra.streamOrphanRefs) {
197
+ const entityRecord = queryClient.hydrateEntity(refId, streamShape);
198
+ orphans.add(entityRecord.proxy);
199
+ }
200
+ }
201
+ if (cachedExtra.optimisticInsertRefs && cachedExtra.optimisticInsertRefs.length > 0 && optimisticInsertsShape) {
202
+ const inserts = this.optimisticInserts;
203
+ for (const refId of cachedExtra.optimisticInsertRefs) {
204
+ const entityRecord = queryClient.hydrateEntity(refId, optimisticInsertsShape);
205
+ inserts.add(entityRecord.proxy);
206
+ }
207
+ }
208
+ }
209
+ /**
210
+ * Get extra data for persistence (converts Sets to arrays of entity ref IDs).
211
+ */
212
+ getForPersistence() {
213
+ const orphans = this._streamOrphans;
214
+ const inserts = this._optimisticInserts;
215
+ if ((orphans === undefined || orphans.size === 0) && (inserts === undefined || inserts.size === 0)) {
216
+ return undefined;
217
+ }
218
+ const extra = {};
219
+ if (orphans !== undefined && orphans.size > 0) {
220
+ extra.streamOrphanRefs = [];
221
+ for (const orphan of orphans) {
222
+ const refId = getProxyId(orphan);
223
+ if (refId !== undefined) {
224
+ extra.streamOrphanRefs.push(refId);
225
+ }
226
+ }
227
+ }
228
+ if (inserts !== undefined && inserts.size > 0) {
229
+ extra.optimisticInsertRefs = [];
230
+ for (const insert of inserts) {
231
+ const refId = getProxyId(insert);
232
+ if (refId !== undefined) {
233
+ extra.optimisticInsertRefs.push(refId);
234
+ }
235
+ }
236
+ }
237
+ return extra;
238
+ }
239
+ /**
240
+ * Check if there's any extra data.
241
+ */
242
+ get hasData() {
243
+ return ((this._streamOrphans !== undefined && this._streamOrphans.size > 0) ||
244
+ (this._optimisticInserts !== undefined && this._optimisticInserts.size > 0));
245
+ }
246
+ }
247
+ // ======================================================
248
+ // QueryResultImpl
249
+ // ======================================================
7
250
  /**
8
251
  * QueryResult wraps a DiscriminatedReactivePromise and adds additional functionality
9
252
  * like refetch, while forwarding all the base relay properties.
@@ -19,10 +262,12 @@ export class QueryResultImpl {
19
262
  updatedAt = undefined;
20
263
  params = undefined;
21
264
  refIds = undefined;
265
+ allNestedRefIdsSignal = undefined;
22
266
  refetchPromise = undefined;
23
267
  fetchMorePromise = undefined;
24
268
  attemptCount = 0;
25
269
  unsubscribe = undefined;
270
+ streamUnsubscribe = undefined;
26
271
  relay;
27
272
  _relayState = undefined;
28
273
  wasOffline = false;
@@ -33,10 +278,14 @@ export class QueryResultImpl {
33
278
  }
34
279
  return relayState;
35
280
  }
281
+ _extra = undefined;
282
+ get extraData() {
283
+ return this._extra ?? (this._extra = new QueryResultExtra(() => this.persistExtraData()));
284
+ }
36
285
  _nextPageParams = undefined;
37
286
  get nextPageParams() {
38
- // Streams don't have pagination
39
- if (this.def.type === "stream" /* QueryType.Stream */) {
287
+ // Streams and non-infinite queries don't have pagination
288
+ if (this.def.type !== "infiniteQuery" /* QueryType.InfiniteQuery */) {
40
289
  return null;
41
290
  }
42
291
  let params = this._nextPageParams;
@@ -84,10 +333,12 @@ export class QueryResultImpl {
84
333
  // Store initial offline state
85
334
  this.wasOffline = !isOnline;
86
335
  if (this.initialized) {
87
- if (this.def.type === "stream" /* QueryType.Stream */) {
336
+ // For any query with streams, resubscribe on reactivation
337
+ if (this.def.type === "stream" /* QueryType.Stream */ ||
338
+ this.def.stream) {
88
339
  this.setupSubscription();
89
340
  }
90
- else {
341
+ if (this.def.type !== "stream" /* QueryType.Stream */) {
91
342
  // Check if we just came back online
92
343
  if (!this.wasOffline && isOnline) {
93
344
  // We're back online - check if we should refresh
@@ -111,9 +362,13 @@ export class QueryResultImpl {
111
362
  // Return deactivation callback
112
363
  return {
113
364
  update: () => {
114
- // For streams, unsubscribe and resubscribe to re-establish connection
115
- if (this.def.type === "stream" /* QueryType.Stream */) {
365
+ // For any query with streams, unsubscribe and resubscribe to re-establish connection
366
+ if (this.def.type === "stream" /* QueryType.Stream */ ||
367
+ this.def.stream) {
116
368
  this.setupSubscription();
369
+ }
370
+ // For StreamQuery, we're done - it doesn't react to network status
371
+ if (this.def.type === "stream" /* QueryType.Stream */) {
117
372
  return;
118
373
  }
119
374
  // Network status changed - check if we should react
@@ -132,14 +387,17 @@ export class QueryResultImpl {
132
387
  },
133
388
  deactivate: () => {
134
389
  // Last subscriber left, deactivate refetch and schedule memory eviction
135
- if (this.def.type === "stream" /* QueryType.Stream */) {
136
- // Unsubscribe from stream
137
- if (this.unsubscribe) {
138
- this.unsubscribe();
139
- this.unsubscribe = undefined;
140
- }
390
+ // Unsubscribe from any active streams
391
+ if (this.unsubscribe) {
392
+ this.unsubscribe();
393
+ this.unsubscribe = undefined;
394
+ }
395
+ if (this.streamUnsubscribe) {
396
+ this.streamUnsubscribe();
397
+ this.streamUnsubscribe = undefined;
141
398
  }
142
- else if (this.def.cache?.refetchInterval) {
399
+ // Remove from refetch manager if configured
400
+ if (this.def.type !== "stream" /* QueryType.Stream */ && this.def.cache?.refetchInterval) {
143
401
  this.queryClient.refetchManager.removeQuery(this);
144
402
  }
145
403
  // Schedule removal from memory using the global eviction manager
@@ -201,6 +459,28 @@ export class QueryResultImpl {
201
459
  // ======================================================
202
460
  // Internal fetch methods
203
461
  // ======================================================
462
+ getAllEntityRefs() {
463
+ let allNestedRefIdsSignal = this.allNestedRefIdsSignal;
464
+ if (!allNestedRefIdsSignal) {
465
+ const queryClient = this.queryClient;
466
+ this.allNestedRefIdsSignal = allNestedRefIdsSignal = reactiveSignal(() => {
467
+ // Entangle the relay value. Whenever the relay value is updated, the
468
+ // allNestedRefIdsSignal will be updated, so no need for a second signal.
469
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
470
+ this.relay.value;
471
+ const allRefIds = new Set();
472
+ if (this.refIds !== undefined) {
473
+ for (const refId of this.refIds) {
474
+ queryClient.getNestedEntityRefIds(refId, allRefIds);
475
+ }
476
+ }
477
+ // Reconcile extra data against the main response
478
+ this.extraData.reconcile(allRefIds);
479
+ return allRefIds;
480
+ });
481
+ }
482
+ return allNestedRefIdsSignal.value;
483
+ }
204
484
  /**
205
485
  * Initialize the query by loading from cache and fetching if stale
206
486
  */
@@ -211,20 +491,30 @@ export class QueryResultImpl {
211
491
  // Load from cache first
212
492
  const cached = await this.queryClient.loadCachedQuery(this.def, this.queryKey);
213
493
  if (cached !== undefined) {
494
+ // Set the cached timestamp
495
+ this.updatedAt = cached.updatedAt;
496
+ // Set the cached reference IDs
497
+ this.refIds = cached.refIds;
498
+ // Load extra data (stream orphans and optimistic inserts) BEFORE setting state.value
499
+ // because setting state.value resolves the relay
500
+ if (cached.extra) {
501
+ const def = this.def;
502
+ this.extraData.loadFromCache(cached.extra, this.queryClient, def.stream?.shape, def.optimisticInserts?.shape);
503
+ }
504
+ // Set the value last - this resolves the relay
214
505
  const shape = this.def.shape;
215
506
  state.value =
216
507
  shape instanceof ValidatorDef
217
508
  ? parseEntities(cached.value, shape, this.queryClient, new Set())
218
509
  : parseValue(cached.value, shape, this.def.id);
219
- // Set the cached timestamp
220
- this.updatedAt = cached.updatedAt;
221
- // Set the cached reference IDs
222
- this.refIds = cached.refIds;
223
510
  }
224
- if (this.def.type === "stream" /* QueryType.Stream */) {
511
+ // Setup subscriptions (handles both StreamQuery and Query/InfiniteQuery with stream)
512
+ if (this.def.type === "stream" /* QueryType.Stream */ ||
513
+ this.def.stream) {
225
514
  this.setupSubscription();
226
515
  }
227
- else {
516
+ // For non-stream queries, fetch if stale or no cache
517
+ if (this.def.type !== "stream" /* QueryType.Stream */) {
228
518
  if (cached !== undefined) {
229
519
  // Check if data is stale
230
520
  if (this.isStale) {
@@ -244,22 +534,43 @@ export class QueryResultImpl {
244
534
  }
245
535
  }
246
536
  /**
247
- * Handle stream updates by merging with existing entity.
248
- * Deep merging is handled automatically by parseEntities/setEntity.
537
+ * Handle stream updates. This method handles both StreamQuery and Query/InfiniteQuery with stream options.
538
+ * - For StreamQuery: directly updates the relay state with the entity
539
+ * - For Query/InfiniteQuery with stream: updates entities in response or adds to orphans
249
540
  */
250
541
  setupSubscription() {
251
542
  this.unsubscribe?.();
252
- const streamDef = this.def;
253
- this.unsubscribe = streamDef.subscribeFn(this.queryClient.getContext(), this.params, update => {
254
- const shapeDef = this.def.shape;
255
- const entityRefs = this.refIds ?? new Set();
256
- const parsedData = parseEntities(update, shapeDef, this.queryClient, entityRefs);
543
+ let subscribeFn;
544
+ let shapeDef;
545
+ if (this.def.type === "stream" /* QueryType.Stream */) {
546
+ shapeDef = this.def.shape;
547
+ subscribeFn = this.def.subscribeFn;
548
+ }
549
+ else {
550
+ const stream = this.def.stream;
551
+ if (!stream) {
552
+ return;
553
+ }
554
+ shapeDef = stream.shape;
555
+ subscribeFn = stream.subscribeFn;
556
+ }
557
+ this.unsubscribe = subscribeFn(this.queryClient.getContext(), this.params, update => {
558
+ const parsedData = parseObjectEntities(update, shapeDef, this.queryClient);
257
559
  // Update the relay state
258
- this.relayState.value = parsedData;
259
- this.updatedAt = Date.now();
260
- this.refIds = entityRefs;
261
- // Cache the data
262
- this.queryClient.saveQueryData(this.def, this.queryKey, parsedData, this.updatedAt, entityRefs);
560
+ if (this.def.type === "stream" /* QueryType.Stream */) {
561
+ this.relayState.value = parsedData;
562
+ this.updatedAt = Date.now();
563
+ // Cache the data
564
+ this.queryClient.saveQueryData(this.def, this.queryKey, parsedData, this.updatedAt);
565
+ }
566
+ else {
567
+ const allRefIds = this.getAllEntityRefs();
568
+ const proxyId = getProxyId(parsedData);
569
+ // Add to orphans if not in main response
570
+ if (proxyId !== undefined && !allRefIds.has(proxyId)) {
571
+ this.extraData.addStreamOrphan(parsedData);
572
+ }
573
+ }
263
574
  });
264
575
  }
265
576
  /**
@@ -286,7 +597,7 @@ export class QueryResultImpl {
286
597
  entityRefs = this.refIds;
287
598
  }
288
599
  else {
289
- entityRefs = new Set();
600
+ entityRefs = this.refIds = new Set();
290
601
  }
291
602
  const shape = this.def.shape;
292
603
  const parsedData = shape instanceof ValidatorDef
@@ -309,7 +620,7 @@ export class QueryResultImpl {
309
620
  }
310
621
  this._nextPageParams = undefined;
311
622
  // Cache the data (synchronous, fire-and-forget)
312
- this.queryClient.saveQueryData(this.def, this.queryKey, queryData, updatedAt, entityRefs);
623
+ this.queryClient.saveQueryData(this.def, this.queryKey, queryData, updatedAt, entityRefs, this.getExtraForPersistence());
313
624
  // Update the timestamp
314
625
  this.updatedAt = Date.now();
315
626
  return queryData;
@@ -355,6 +666,10 @@ export class QueryResultImpl {
355
666
  const promise = this.runQuery(this.params, true)
356
667
  .then(result => {
357
668
  this.relayState.value = result;
669
+ // Clear stream orphans and optimistic inserts on refetch
670
+ if (this._extra !== undefined) {
671
+ this._extra.clear();
672
+ }
358
673
  return result;
359
674
  })
360
675
  .catch((error) => {
@@ -420,6 +735,68 @@ export class QueryResultImpl {
420
735
  get hasNextPage() {
421
736
  return this.nextPageParams !== null;
422
737
  }
738
+ get extra() {
739
+ this.getAllEntityRefs();
740
+ return this.extraData.getExtra();
741
+ }
742
+ /**
743
+ * Persist the current extra data to the store
744
+ */
745
+ persistExtraData() {
746
+ if (this.updatedAt === undefined) {
747
+ return; // Query not initialized yet
748
+ }
749
+ const extra = this._extra?.getForPersistence();
750
+ this.queryClient.saveQueryData(this.def, this.queryKey, this.relayState.value, this.updatedAt, this.refIds, extra);
751
+ }
752
+ /**
753
+ * Get extra data for persistence (converts Sets to arrays of entity ref IDs)
754
+ */
755
+ getExtraForPersistence() {
756
+ return this._extra?.getForPersistence();
757
+ }
758
+ /**
759
+ * Add an optimistic insert to the query result.
760
+ * The insert will be automatically removed when:
761
+ * - The entity appears in a refetched response
762
+ * - The entity appears as a stream orphan
763
+ * - refetch() is called
764
+ */
765
+ addOptimisticInsert(insert) {
766
+ // Check that the query has optimisticInserts configured
767
+ const def = this.def;
768
+ const optimisticInsertsConfig = def.optimisticInserts;
769
+ if (optimisticInsertsConfig === undefined) {
770
+ throw new Error('Query does not have optimisticInserts configured. Add optimisticInserts: { type: YourEntity } to the query definition.');
771
+ }
772
+ let proxyId = getProxyId(insert);
773
+ let parsedInsert = insert;
774
+ // If not already a proxy, parse it through the optimisticInserts shape
775
+ if (proxyId === undefined) {
776
+ parsedInsert = parseObjectEntities(insert, optimisticInsertsConfig.shape, this.queryClient);
777
+ proxyId = getProxyId(parsedInsert);
778
+ if (proxyId === undefined) {
779
+ throw new Error('Optimistic insert must be or produce an entity proxy');
780
+ }
781
+ }
782
+ // Check if already in main response
783
+ const allRefIds = this.getAllEntityRefs();
784
+ if (allRefIds.has(proxyId)) {
785
+ return; // Already in response, no-op
786
+ }
787
+ // Check if already in stream orphans
788
+ if (this.extraData.hasOrphanWithId(proxyId)) {
789
+ return; // Already in stream orphans, no-op
790
+ }
791
+ this.extraData.addOptimisticInsert(parsedInsert);
792
+ }
793
+ /**
794
+ * Remove an optimistic insert from the query result.
795
+ * This is a no-op if the insert has already been removed.
796
+ */
797
+ removeOptimisticInsert(insert) {
798
+ this.extraData.removeOptimisticInsert(insert);
799
+ }
423
800
  get isStale() {
424
801
  // Streams are never stale - they're always receiving updates
425
802
  if (this.def.type === "stream" /* QueryType.Stream */) {