@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.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/EntityMap.js +20 -1
- package/dist/cjs/EntityMap.js.map +1 -1
- package/dist/cjs/QueryClient.js +27 -3
- package/dist/cjs/QueryClient.js.map +1 -1
- package/dist/cjs/QueryResult.js +410 -33
- package/dist/cjs/QueryResult.js.map +1 -1
- package/dist/cjs/QueryStore.js +78 -5
- package/dist/cjs/QueryStore.js.map +1 -1
- package/dist/cjs/errors.js +6 -0
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/parseEntities.js +14 -2
- package/dist/cjs/parseEntities.js.map +1 -1
- package/dist/cjs/proxy.js +22 -5
- package/dist/cjs/proxy.js.map +1 -1
- package/dist/cjs/query.js +64 -1
- package/dist/cjs/query.js.map +1 -1
- package/dist/cjs/react/use-query.js.map +1 -1
- package/dist/cjs/stores/async.js.map +1 -1
- package/dist/cjs/stores/sync.js.map +1 -1
- package/dist/cjs/typeDefs.js +136 -3
- package/dist/cjs/typeDefs.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/EntityMap.d.ts +3 -1
- package/dist/esm/EntityMap.d.ts.map +1 -1
- package/dist/esm/EntityMap.js +20 -1
- package/dist/esm/EntityMap.js.map +1 -1
- package/dist/esm/QueryClient.d.ts +47 -12
- package/dist/esm/QueryClient.d.ts.map +1 -1
- package/dist/esm/QueryClient.js +25 -3
- package/dist/esm/QueryClient.js.map +1 -1
- package/dist/esm/QueryResult.d.ts +34 -6
- package/dist/esm/QueryResult.d.ts.map +1 -1
- package/dist/esm/QueryResult.js +414 -37
- package/dist/esm/QueryResult.js.map +1 -1
- package/dist/esm/QueryStore.d.ts +17 -9
- package/dist/esm/QueryStore.d.ts.map +1 -1
- package/dist/esm/QueryStore.js +75 -4
- package/dist/esm/QueryStore.js.map +1 -1
- package/dist/esm/errors.d.ts.map +1 -1
- package/dist/esm/errors.js +6 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/parseEntities.d.ts.map +1 -1
- package/dist/esm/parseEntities.js +14 -2
- package/dist/esm/parseEntities.js.map +1 -1
- package/dist/esm/proxy.d.ts +1 -0
- package/dist/esm/proxy.d.ts.map +1 -1
- package/dist/esm/proxy.js +22 -6
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query.d.ts +27 -11
- package/dist/esm/query.d.ts.map +1 -1
- package/dist/esm/query.js +64 -1
- package/dist/esm/query.js.map +1 -1
- package/dist/esm/react/use-query.d.ts +2 -2
- package/dist/esm/react/use-query.d.ts.map +1 -1
- package/dist/esm/react/use-query.js.map +1 -1
- package/dist/esm/stores/async.d.ts +3 -3
- package/dist/esm/stores/async.d.ts.map +1 -1
- package/dist/esm/stores/async.js.map +1 -1
- package/dist/esm/stores/sync.d.ts +3 -3
- package/dist/esm/stores/sync.d.ts.map +1 -1
- package/dist/esm/stores/sync.js.map +1 -1
- package/dist/esm/typeDefs.d.ts +30 -0
- package/dist/esm/typeDefs.d.ts.map +1 -1
- package/dist/esm/typeDefs.js +134 -2
- package/dist/esm/typeDefs.js.map +1 -1
- package/dist/esm/types.d.ts +71 -23
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryClient.js","sourceRoot":"","sources":["../../src/QueryClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAgB,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAgB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;
|
|
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
|
|
49
|
-
*
|
|
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":"
|
|
1
|
+
{"version":3,"file":"QueryResult.d.ts","sourceRoot":"","sources":["../../src/QueryResult.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/QueryResult.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
248
|
-
*
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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 */) {
|