@multiplatform.one/frappe 7.8.0 → 7.10.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.
- package/README.md +31 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/sync/index.cjs +7 -0
- package/dist/cjs/sync/index.native.js +9 -0
- package/dist/cjs/sync/index.native.js.map +1 -1
- package/dist/cjs/sync/subscriptionManager.cjs +94 -50
- package/dist/cjs/sync/subscriptionManager.native.js +133 -73
- package/dist/cjs/sync/subscriptionManager.native.js.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/sync/index.mjs +7 -0
- package/dist/esm/sync/index.mjs.map +1 -1
- package/dist/esm/sync/index.native.js +9 -0
- package/dist/esm/sync/index.native.js.map +1 -1
- package/dist/esm/sync/subscriptionManager.mjs +94 -50
- package/dist/esm/sync/subscriptionManager.mjs.map +1 -1
- package/dist/esm/sync/subscriptionManager.native.js +133 -73
- package/dist/esm/sync/subscriptionManager.native.js.map +1 -1
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/sync/index.mjs +7 -0
- package/dist/jsx/sync/index.mjs.map +1 -1
- package/dist/jsx/sync/index.native.js +9 -0
- package/dist/jsx/sync/index.native.js.map +1 -1
- package/dist/jsx/sync/subscriptionManager.mjs +94 -50
- package/dist/jsx/sync/subscriptionManager.mjs.map +1 -1
- package/dist/jsx/sync/subscriptionManager.native.js +133 -73
- package/dist/jsx/sync/subscriptionManager.native.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +5 -1
- package/src/sync/index.ts +12 -0
- package/src/sync/refreshSubscriptions.lifecycle.spec.ts +268 -0
- package/src/sync/refreshSubscriptions.spec.ts +295 -0
- package/src/sync/subscriptionManager.ts +107 -44
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/sync/index.d.ts +4 -0
- package/types/sync/index.d.ts.map +1 -1
- package/types/sync/subscriptionManager.d.ts +13 -10
- package/types/sync/subscriptionManager.d.ts.map +1 -1
|
@@ -30,7 +30,24 @@ export interface SubscribeResult<Data extends FrappeDocData> {
|
|
|
30
30
|
refresh?: Promise<{ ok: boolean; error?: unknown }>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export interface SubscriptionRefreshOptions {
|
|
34
|
+
doctypes?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SubscriptionRefreshResult {
|
|
38
|
+
subscriptionId: string;
|
|
39
|
+
ok: boolean;
|
|
40
|
+
error?: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
export class SubscriptionManager {
|
|
44
|
+
private refreshes = new Map<
|
|
45
|
+
string,
|
|
46
|
+
{
|
|
47
|
+
requested: boolean;
|
|
48
|
+
promise: Promise<SubscriptionRefreshResult>;
|
|
49
|
+
}
|
|
50
|
+
>();
|
|
34
51
|
private subscriptions = new Map<string, Subscription>();
|
|
35
52
|
private virtualPageManager: VirtualPageManager;
|
|
36
53
|
private backfillManager: BackfillManager;
|
|
@@ -85,6 +102,7 @@ export class SubscriptionManager {
|
|
|
85
102
|
|
|
86
103
|
const subscriptionId = cryptoRandomId();
|
|
87
104
|
const { rows, cursor, checkpoint } = await this.fetchInitialRows<Data>(query);
|
|
105
|
+
this.seedRows(query, rows);
|
|
88
106
|
// Materialize the in-memory subscription record with initial membership
|
|
89
107
|
const subscription: Subscription = {
|
|
90
108
|
id: subscriptionId,
|
|
@@ -100,8 +118,8 @@ export class SubscriptionManager {
|
|
|
100
118
|
subscription.boundaries = {
|
|
101
119
|
firstItem: rows[0] as unknown as FrappeDoc<Data>,
|
|
102
120
|
lastItem: rows[rows.length - 1] as unknown as FrappeDoc<Data>,
|
|
103
|
-
offset: query.limitStart
|
|
104
|
-
pageSize: query.limit
|
|
121
|
+
offset: query.limitStart ?? 0,
|
|
122
|
+
pageSize: query.limit ?? defaultPageLength,
|
|
105
123
|
};
|
|
106
124
|
}
|
|
107
125
|
|
|
@@ -130,9 +148,85 @@ export class SubscriptionManager {
|
|
|
130
148
|
return { subscriptionId: subscriptionId, rows, checkpoint };
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
async refreshSubscriptions(
|
|
152
|
+
options: SubscriptionRefreshOptions = {},
|
|
153
|
+
): Promise<SubscriptionRefreshResult[]> {
|
|
154
|
+
const subscriptions = [...this.subscriptions.values()].filter(
|
|
155
|
+
(subscription) =>
|
|
156
|
+
(this.subRefCounts.get(subscription.id) ?? 0) > 0 &&
|
|
157
|
+
(!options.doctypes || options.doctypes.includes(subscription.query.doctype)),
|
|
158
|
+
);
|
|
159
|
+
return Promise.all(subscriptions.map((subscription) => this.refreshSubscription(subscription)));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private refreshSubscription(subscription: Subscription): Promise<SubscriptionRefreshResult> {
|
|
163
|
+
const subscriptionId = subscription.id;
|
|
164
|
+
if (subscription.query.progressive) {
|
|
165
|
+
return Promise.resolve({
|
|
166
|
+
subscriptionId,
|
|
167
|
+
ok: false,
|
|
168
|
+
error: new Error("Snapshot refresh does not support progressive subscriptions"),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const existing = this.refreshes.get(subscriptionId);
|
|
172
|
+
if (existing) {
|
|
173
|
+
existing.requested = true;
|
|
174
|
+
return existing.promise;
|
|
175
|
+
}
|
|
176
|
+
const state = {
|
|
177
|
+
requested: false,
|
|
178
|
+
promise: Promise.resolve<SubscriptionRefreshResult>({ subscriptionId, ok: false }),
|
|
179
|
+
};
|
|
180
|
+
const active = () =>
|
|
181
|
+
this.refreshes.get(subscriptionId) === state &&
|
|
182
|
+
this.subscriptions.get(subscriptionId) === subscription &&
|
|
183
|
+
(this.subRefCounts.get(subscriptionId) ?? 0) > 0;
|
|
184
|
+
state.promise = Promise.resolve().then(async () => {
|
|
185
|
+
try {
|
|
186
|
+
while (true) {
|
|
187
|
+
state.requested = false;
|
|
188
|
+
try {
|
|
189
|
+
const { rows, cursor, checkpoint } = await this.fetchInitialRows(subscription.query);
|
|
190
|
+
if (!active()) {
|
|
191
|
+
return {
|
|
192
|
+
subscriptionId,
|
|
193
|
+
ok: false,
|
|
194
|
+
error: new Error("Subscription is no longer active"),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (state.requested) continue;
|
|
198
|
+
this.seedRows(subscription.query, rows);
|
|
199
|
+
subscription.docIds = rows.map((row) => row.name);
|
|
200
|
+
subscription.cursor = cursor;
|
|
201
|
+
subscription.checkpoint = checkpoint;
|
|
202
|
+
subscription.boundaries = rows.length
|
|
203
|
+
? {
|
|
204
|
+
firstItem: rows[0],
|
|
205
|
+
lastItem: rows[rows.length - 1],
|
|
206
|
+
offset: subscription.query.limitStart ?? 0,
|
|
207
|
+
pageSize: subscription.query.limit ?? defaultPageLength,
|
|
208
|
+
}
|
|
209
|
+
: undefined;
|
|
210
|
+
subscription.metadata = { itemsBeforePage: 0, itemsAfterPage: 0 };
|
|
211
|
+
subscription.stale = false;
|
|
212
|
+
return { subscriptionId, ok: true };
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (active() && state.requested) continue;
|
|
215
|
+
if (active()) subscription.stale = true;
|
|
216
|
+
return { subscriptionId, ok: false, error };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} finally {
|
|
220
|
+
if (this.refreshes.get(subscriptionId) === state) this.refreshes.delete(subscriptionId);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
this.refreshes.set(subscriptionId, state);
|
|
224
|
+
return state.promise;
|
|
225
|
+
}
|
|
226
|
+
|
|
133
227
|
/**
|
|
134
228
|
* Performs the initial subscription fetch (single doc via getDoc, lists via
|
|
135
|
-
* getDocList)
|
|
229
|
+
* getDocList) and computes the CDC checkpoint
|
|
136
230
|
* as the max `modified` across returned rows. Shared by fresh subscribes
|
|
137
231
|
* and adopted-subscription refreshes so both have identical semantics.
|
|
138
232
|
*/
|
|
@@ -186,13 +280,6 @@ export class SubscriptionManager {
|
|
|
186
280
|
rows = response;
|
|
187
281
|
}
|
|
188
282
|
}
|
|
189
|
-
// Seed the normalized cache with every returned row for instant local reads.
|
|
190
|
-
// Frappe's standard get_list API omits `doctype` from each row, so inject
|
|
191
|
-
// it from the subscription query to ensure the cache keys correctly.
|
|
192
|
-
for (const row of rows) {
|
|
193
|
-
if (!row.doctype) row.doctype = query.doctype;
|
|
194
|
-
this.normalizedCache.upsert(row);
|
|
195
|
-
}
|
|
196
283
|
// Compute checkpoint for CDC as the max `modified` across returned rows (robust to server ordering)
|
|
197
284
|
let checkpoint: string | undefined = undefined;
|
|
198
285
|
for (const row of rows) {
|
|
@@ -202,6 +289,13 @@ export class SubscriptionManager {
|
|
|
202
289
|
return { rows, cursor, checkpoint };
|
|
203
290
|
}
|
|
204
291
|
|
|
292
|
+
private seedRows<Data extends FrappeDocData>(query: SubscriptionQuery, rows: FrappeDoc<Data>[]) {
|
|
293
|
+
for (const row of rows) {
|
|
294
|
+
if (!row.doctype) row.doctype = query.doctype;
|
|
295
|
+
this.normalizedCache.upsert(row);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
205
299
|
/**
|
|
206
300
|
* Re-adopts a subscription restored from a persisted snapshot when an
|
|
207
301
|
* identical query subscribes: the cached membership renders immediately and
|
|
@@ -225,44 +319,11 @@ export class SubscriptionManager {
|
|
|
225
319
|
this.queryKeyToSubId.set(queryKey, restoredId);
|
|
226
320
|
this.subRefCounts.set(restoredId, 1);
|
|
227
321
|
|
|
228
|
-
const refresh = this.
|
|
322
|
+
const refresh = this.refreshSubscription(subscription);
|
|
229
323
|
const rows = this.getDocsForSubscription<Data>(restoredId);
|
|
230
324
|
return { subscriptionId: restoredId, rows, checkpoint: subscription.checkpoint, refresh };
|
|
231
325
|
}
|
|
232
326
|
|
|
233
|
-
/**
|
|
234
|
-
* Network refresh for an adopted subscription with fresh-subscribe
|
|
235
|
-
* semantics: an authoritative page fetch replaces membership and recomputes
|
|
236
|
-
* the checkpoint (identical to what a brand-new subscribe would produce).
|
|
237
|
-
* On failure (offline) the cached membership and the restored CDC
|
|
238
|
-
* checkpoint are kept and the subscription is marked stale — the next
|
|
239
|
-
* backfill trigger drains changes from that checkpoint. Never rejects.
|
|
240
|
-
*/
|
|
241
|
-
private async refreshAdoptedSubscription<Data extends FrappeDocData>(
|
|
242
|
-
subscription: Subscription,
|
|
243
|
-
): Promise<{ ok: boolean; error?: unknown }> {
|
|
244
|
-
try {
|
|
245
|
-
const { rows, cursor, checkpoint } = await this.fetchInitialRows<Data>(subscription.query);
|
|
246
|
-
subscription.checkpoint = checkpoint;
|
|
247
|
-
subscription.cursor = cursor;
|
|
248
|
-
subscription.docIds = rows.map((row) => row.name);
|
|
249
|
-
if (rows.length > 0) {
|
|
250
|
-
subscription.boundaries = {
|
|
251
|
-
firstItem: rows[0] as unknown as FrappeDoc<FrappeDocData>,
|
|
252
|
-
lastItem: rows[rows.length - 1] as unknown as FrappeDoc<FrappeDocData>,
|
|
253
|
-
offset: subscription.query.limitStart || 0,
|
|
254
|
-
pageSize: subscription.query.limit || 20,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
subscription.metadata = { itemsBeforePage: 0, itemsAfterPage: 0 };
|
|
258
|
-
subscription.stale = false;
|
|
259
|
-
return { ok: true };
|
|
260
|
-
} catch (error) {
|
|
261
|
-
subscription.stale = true;
|
|
262
|
-
return { ok: false, error };
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
327
|
/**
|
|
267
328
|
* Registers a subscription restored from a persisted snapshot. Restored
|
|
268
329
|
* subscriptions are dormant: they hold the persisted query, CDC checkpoint,
|
|
@@ -343,6 +404,7 @@ export class SubscriptionManager {
|
|
|
343
404
|
this.subRefCounts.set(subscriptionId, refCount);
|
|
344
405
|
return;
|
|
345
406
|
}
|
|
407
|
+
this.refreshes.delete(subscriptionId);
|
|
346
408
|
// Last consumer released — clean up
|
|
347
409
|
this.subRefCounts.delete(subscriptionId);
|
|
348
410
|
let queryKey: string | undefined;
|
|
@@ -753,6 +815,7 @@ export class SubscriptionManager {
|
|
|
753
815
|
}
|
|
754
816
|
|
|
755
817
|
clear() {
|
|
818
|
+
this.refreshes.clear();
|
|
756
819
|
this.subscriptions.clear();
|
|
757
820
|
this.queryKeyToSubId.clear();
|
|
758
821
|
this.subRefCounts.clear();
|
package/types/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export { createOptimisticAction, isOptimisticTempId, newOptimisticTempId, OPTIMI
|
|
|
19
19
|
export type { OptimisticRpcContext, OptimisticRpcOptions } from "./optimistic";
|
|
20
20
|
export { useFrappeCollection, useFrappeConnection, useFrappeDoctypeEvents, useFrappePendingMutations, useInfiniteScroll, useFrappeInfiniteList, getFrappeSyncModule, __getActiveSyncModule, } from "./frappeReact";
|
|
21
21
|
export type { FrappeConnectionState, UseFrappeCollectionConfig, UseFrappePendingMutationsResult, UseInfiniteScrollConfig, UseInfiniteScrollResult, UseFrappeInfiniteListConfig, UseFrappeInfiniteListResult, } from "./frappeReact";
|
|
22
|
-
export type { SyncRealtimeEvent } from "./sync";
|
|
22
|
+
export type { SyncRealtimeEvent, SubscriptionRefreshOptions, SubscriptionRefreshResult, } from "./sync";
|
|
23
23
|
export { useDebouncedSearch, useThrottledPagination, createRateLimitedBatch } from "./pacer";
|
|
24
24
|
export type { DebouncedSearchOptions, DebouncedSearchResult, FrappeConnectionConfig, ThrottledPaginationOptions, ThrottledPaginationResult, RateLimitedBatchOptions, RateLimitedBatchHandle, } from "./pacer";
|
|
25
25
|
export { useLiveQuery } from "./useLiveQuery";
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,EACR,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EACzB,OAAO,EACP,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,6BAA6B,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAG/E,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,+BAA+B,EAC/B,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,eAAe,CAAC;AACvB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/types/sync/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FrappeDoc, FrappeDocData } from "../common/types";
|
|
2
2
|
import type { DbModule } from "../db";
|
|
3
3
|
import type { RealtimeModule } from "../realtime";
|
|
4
|
+
import type { SubscriptionRefreshOptions, SubscriptionRefreshResult } from "./subscriptionManager";
|
|
5
|
+
export type { SubscriptionRefreshOptions, SubscriptionRefreshResult } from "./subscriptionManager";
|
|
4
6
|
import type { BackfillArgs, DebugMutationEntry, SyncOptions, FrappeDebugState, Mutation, RealtimeEventType, SubscriptionQuery } from "./types";
|
|
5
7
|
/** Realtime event forwarded to `subscribeRealtimeEvents` listeners. */
|
|
6
8
|
export interface SyncRealtimeEvent {
|
|
@@ -99,6 +101,8 @@ export declare class SyncModule {
|
|
|
99
101
|
*/
|
|
100
102
|
unsubscribe(subscriptionId: string): void;
|
|
101
103
|
mutate(mutation: Mutation<FrappeDocData>): Promise<void>;
|
|
104
|
+
/** Refresh active query snapshots in place. Failures retain the last committed data. */
|
|
105
|
+
refreshSubscriptions(options?: SubscriptionRefreshOptions): Promise<SubscriptionRefreshResult[]>;
|
|
102
106
|
/**
|
|
103
107
|
* Reconciles local state by fetching changes since stored cursors.
|
|
104
108
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,16 @@ export interface SubscribeResult<Data extends FrappeDocData> {
|
|
|
21
21
|
error?: unknown;
|
|
22
22
|
}>;
|
|
23
23
|
}
|
|
24
|
+
export interface SubscriptionRefreshOptions {
|
|
25
|
+
doctypes?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface SubscriptionRefreshResult {
|
|
28
|
+
subscriptionId: string;
|
|
29
|
+
ok: boolean;
|
|
30
|
+
error?: unknown;
|
|
31
|
+
}
|
|
24
32
|
export declare class SubscriptionManager {
|
|
33
|
+
private refreshes;
|
|
25
34
|
private subscriptions;
|
|
26
35
|
private virtualPageManager;
|
|
27
36
|
private backfillManager;
|
|
@@ -43,13 +52,16 @@ export declare class SubscriptionManager {
|
|
|
43
52
|
private readonly options;
|
|
44
53
|
constructor(db: DbModule, normalizedCache: NormalizedCache, options?: SyncOptions);
|
|
45
54
|
subscribe<Data extends FrappeDocData>(query: SubscriptionQuery): Promise<SubscribeResult<Data>>;
|
|
55
|
+
refreshSubscriptions(options?: SubscriptionRefreshOptions): Promise<SubscriptionRefreshResult[]>;
|
|
56
|
+
private refreshSubscription;
|
|
46
57
|
/**
|
|
47
58
|
* Performs the initial subscription fetch (single doc via getDoc, lists via
|
|
48
|
-
* getDocList)
|
|
59
|
+
* getDocList) and computes the CDC checkpoint
|
|
49
60
|
* as the max `modified` across returned rows. Shared by fresh subscribes
|
|
50
61
|
* and adopted-subscription refreshes so both have identical semantics.
|
|
51
62
|
*/
|
|
52
63
|
private fetchInitialRows;
|
|
64
|
+
private seedRows;
|
|
53
65
|
/**
|
|
54
66
|
* Re-adopts a subscription restored from a persisted snapshot when an
|
|
55
67
|
* identical query subscribes: the cached membership renders immediately and
|
|
@@ -58,15 +70,6 @@ export declare class SubscriptionManager {
|
|
|
58
70
|
* the fresh-subscribe path.
|
|
59
71
|
*/
|
|
60
72
|
private adoptRestoredSubscription;
|
|
61
|
-
/**
|
|
62
|
-
* Network refresh for an adopted subscription with fresh-subscribe
|
|
63
|
-
* semantics: an authoritative page fetch replaces membership and recomputes
|
|
64
|
-
* the checkpoint (identical to what a brand-new subscribe would produce).
|
|
65
|
-
* On failure (offline) the cached membership and the restored CDC
|
|
66
|
-
* checkpoint are kept and the subscription is marked stale — the next
|
|
67
|
-
* backfill trigger drains changes from that checkpoint. Never rejects.
|
|
68
|
-
*/
|
|
69
|
-
private refreshAdoptedSubscription;
|
|
70
73
|
/**
|
|
71
74
|
* Registers a subscription restored from a persisted snapshot. Restored
|
|
72
75
|
* subscriptions are dormant: they hold the persisted query, CDC checkpoint,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriptionManager.d.ts","sourceRoot":"","sources":["../../src/sync/subscriptionManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EACV,YAAY,EAEZ,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,IAAI,SAAS,aAAa;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,eAAe,CAAkB;IACzC,sEAAsE;IACtE,OAAO,CAAC,eAAe,CAA6B;IACpD,4EAA4E;IAC5E,OAAO,CAAC,YAAY,CAA6B;IACjD;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAA6B;IACvD,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;gBAE1B,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,EAAE,OAAO,GAAE,WAAgB;IAa/E,SAAS,CAAC,IAAI,SAAS,aAAa,EACxC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"subscriptionManager.d.ts","sourceRoot":"","sources":["../../src/sync/subscriptionManager.ts"],"names":[],"mappings":"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"}
|