dexie-cloud-addon 4.4.10 → 4.4.12
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/dist/modern/dexie-cloud-addon.js +600 -451
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/middlewares/blobResolveMiddleware.d.ts +5 -4
- package/dist/modern/service-worker.js +406 -257
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/modern/sync/BlobDownloadTracker.d.ts +80 -20
- package/dist/modern/sync/BlobSavingQueue.d.ts +20 -2
- package/dist/modern/sync/eagerBlobDownloader.d.ts +37 -3
- package/dist/modern/types/TXExpandos.d.ts +2 -0
- package/dist/umd/dexie-cloud-addon.js +607 -458
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/service-worker.js +408 -259
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,36 +1,96 @@
|
|
|
1
1
|
import type { DexieCloudDB } from '../db/DexieCloudDB';
|
|
2
|
-
import { BlobRef } from './blobResolve';
|
|
2
|
+
import { BlobRef, ResolvedBlob } from './blobResolve';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Owns the full lifecycle of downloaded blobs:
|
|
5
|
+
* 1. Deduplicates concurrent downloads for the same ref.
|
|
6
|
+
* 2. Bounds the number of concurrent network fetches (MAX_CONCURRENT)
|
|
7
|
+
* so that ad-hoc reads can't starve the HTTP connection pool. Calls
|
|
8
|
+
* beyond the cap queue in FIFO order as slots free. The slot is held
|
|
9
|
+
* only for the duration of the fetch — NOT until persistence — to
|
|
10
|
+
* avoid deadlocks when a single object contains more blob refs than
|
|
11
|
+
* MAX_CONCURRENT (a sequential resolver would otherwise hold every
|
|
12
|
+
* slot itself while waiting for the next).
|
|
13
|
+
* 3. Keeps the in-flight promise alive after the network fetch completes,
|
|
14
|
+
* until the blob has been persisted back to IndexedDB. This way,
|
|
15
|
+
* readers that ask for the same ref while it is queued for saving
|
|
16
|
+
* can piggyback on the existing promise instead of refetching.
|
|
17
|
+
* In-flight membership and slot ownership are independent: a piggyback
|
|
18
|
+
* reader consumes neither a slot nor extra memory beyond the existing
|
|
19
|
+
* cached Uint8Array.
|
|
20
|
+
* 4. Persists resolved blobs via an internal BlobSavingQueue, and
|
|
21
|
+
* releases the in-flight entry when persistence completes.
|
|
5
22
|
*
|
|
6
|
-
* Both the blob-resolve middleware and the eager blob downloader
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
* Both the blob-resolve middleware and the eager blob downloader use this
|
|
24
|
+
* tracker. Instantiate once per DexieCloudDB.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Maximum number of concurrent blob fetches.
|
|
10
28
|
*
|
|
11
|
-
*
|
|
29
|
+
* Historically 6 to match the HTTP/1.1 same-origin connection cap that
|
|
30
|
+
* browsers enforce. With HTTP/2 (the typical transport for Dexie Cloud
|
|
31
|
+
* today) many streams multiplex over a single TCP connection, so the
|
|
32
|
+
* old cap is overly conservative. 10 is a modest bump that still keeps
|
|
33
|
+
* memory pressure (in-flight Uint8Arrays) and server load bounded.
|
|
34
|
+
* Can be made configurable via DexieCloudOptions if a real need arises.
|
|
12
35
|
*/
|
|
36
|
+
export declare const MAX_CONCURRENT = 10;
|
|
13
37
|
export declare class BlobDownloadTracker {
|
|
14
38
|
private inFlight;
|
|
15
39
|
private db;
|
|
40
|
+
private savingQueue;
|
|
41
|
+
private activeFetches;
|
|
42
|
+
private waiting;
|
|
16
43
|
constructor(db: DexieCloudDB);
|
|
17
44
|
/**
|
|
18
|
-
* Download a blob, deduplicating concurrent requests for the same ref
|
|
45
|
+
* Download a blob, deduplicating concurrent requests for the same ref
|
|
46
|
+
* and respecting the global fetch concurrency cap.
|
|
47
|
+
*
|
|
48
|
+
* Lifecycle:
|
|
49
|
+
* - Slot is acquired before the fetch and released as soon as the
|
|
50
|
+
* fetch settles (success or failure).
|
|
51
|
+
* - The in-flight entry survives a successful fetch and lives on
|
|
52
|
+
* until persistence completes (via enqueueSave) or releaseRefs
|
|
53
|
+
* is called. On fetch failure, the entry is removed immediately
|
|
54
|
+
* so a future call can retry.
|
|
19
55
|
*
|
|
20
56
|
* @param blobRef - The BlobRef to download
|
|
21
57
|
* @param dbUrl - Base URL for the database (e.g., 'https://mydb.dexie.cloud')
|
|
22
58
|
*/
|
|
23
59
|
download(blobRef: BlobRef, dbUrl: string): Promise<Uint8Array>;
|
|
60
|
+
/**
|
|
61
|
+
* Queue resolved blobs for persisting back to IndexedDB.
|
|
62
|
+
* When the save transaction completes, the corresponding in-flight
|
|
63
|
+
* entries are released.
|
|
64
|
+
*/
|
|
65
|
+
enqueueSave(tableName: string, primaryKey: any, resolvedBlobs: ResolvedBlob[]): void;
|
|
66
|
+
/**
|
|
67
|
+
* Wait until all previously enqueued saves have been persisted to
|
|
68
|
+
* IndexedDB. Used by callers that need to make decisions based on
|
|
69
|
+
* on-disk state — e.g., the eager downloader looping over rows with
|
|
70
|
+
* `_hasBlobRefs=1` in chunks, where each iteration must see the
|
|
71
|
+
* previous chunk's writes before re-querying.
|
|
72
|
+
*
|
|
73
|
+
* New saves enqueued AFTER drainPendingSaves() is called do NOT extend
|
|
74
|
+
* the wait.
|
|
75
|
+
*/
|
|
76
|
+
drainPendingSaves(): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Release in-flight entries without going through the internal saving
|
|
79
|
+
* queue. Used when the caller persists the blobs itself, or when no
|
|
80
|
+
* primary key was available and the data won't be persisted at all.
|
|
81
|
+
*/
|
|
82
|
+
releaseRefs(refs: string[]): void;
|
|
83
|
+
private acquireSlot;
|
|
84
|
+
private releaseSlot;
|
|
85
|
+
/**
|
|
86
|
+
* Download blob data from server via proxy endpoint.
|
|
87
|
+
* Uses auth header for authentication (same as sync).
|
|
88
|
+
* When accessToken is null, the request is made without Authorization header —
|
|
89
|
+
* this allows downloading blobs from public realms (rlm-public) for
|
|
90
|
+
* unauthenticated users.
|
|
91
|
+
*
|
|
92
|
+
* @param blobRef - The BlobRef to download
|
|
93
|
+
* @param dbUrl - Base URL for the database (e.g., 'https://mydb.dexie.cloud')
|
|
94
|
+
*/
|
|
95
|
+
private downloadBlob;
|
|
24
96
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Download blob data from server via proxy endpoint.
|
|
27
|
-
* Uses auth header for authentication (same as sync).
|
|
28
|
-
* When accessToken is null, the request is made without Authorization header —
|
|
29
|
-
* this allows downloading blobs from public realms (rlm-public) for
|
|
30
|
-
* unauthenticated users.
|
|
31
|
-
*
|
|
32
|
-
* @param blobRef - The BlobRef to download
|
|
33
|
-
* @param dbUrl - Base URL for the database (e.g., 'https://mydb.dexie.cloud')
|
|
34
|
-
* @param accessToken - Access token for authentication, or null for anonymous access
|
|
35
|
-
*/
|
|
36
|
-
export declare function downloadBlob(blobRef: BlobRef, dbUrl: string, accessToken: string | null): Promise<Uint8Array>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BlobSavingQueue - Queues resolved blobs for saving back to IndexedDB
|
|
2
|
+
* BlobSavingQueue - Queues resolved blobs for saving back to IndexedDB.
|
|
3
|
+
*
|
|
4
|
+
* This is an internal collaborator of BlobDownloadTracker and is not
|
|
5
|
+
* intended to be used directly by middleware or other code. See
|
|
6
|
+
* BlobDownloadTracker.enqueueSave().
|
|
3
7
|
*
|
|
4
8
|
* Uses setTimeout(fn, 0) instead of queueMicrotask to completely isolate
|
|
5
9
|
* from Dexie's Promise.PSD context. This prevents the save operation
|
|
@@ -14,12 +18,26 @@ export declare class BlobSavingQueue {
|
|
|
14
18
|
private queue;
|
|
15
19
|
private isProcessing;
|
|
16
20
|
private db;
|
|
17
|
-
|
|
21
|
+
private onPersisted;
|
|
22
|
+
private drainResolvers;
|
|
23
|
+
constructor(db: DexieCloudDB, onPersisted: (refs: string[]) => void);
|
|
18
24
|
/**
|
|
19
25
|
* Queue a resolved blob for saving.
|
|
20
26
|
* Only the specific blob property will be updated atomically.
|
|
21
27
|
*/
|
|
22
28
|
saveBlobs(tableName: string, primaryKey: any, resolvedBlobs: ResolvedBlob[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Returns a promise that resolves when the queue is empty AND no item
|
|
31
|
+
* is currently being processed. Used by callers that need to know when
|
|
32
|
+
* all previously enqueued saves have been persisted to IndexedDB before
|
|
33
|
+
* making decisions based on the on-disk state (e.g., the eager blob
|
|
34
|
+
* downloader looping over `_hasBlobRefs=1` rows in chunks).
|
|
35
|
+
*
|
|
36
|
+
* Note: New work enqueued AFTER drain() is called does NOT extend the
|
|
37
|
+
* wait. Callers that race against concurrent producers should treat the
|
|
38
|
+
* returned promise as "queue was empty at some point after this call".
|
|
39
|
+
*/
|
|
40
|
+
drain(): Promise<void>;
|
|
23
41
|
/**
|
|
24
42
|
* Start the consumer if not already processing.
|
|
25
43
|
* Uses setTimeout(fn, 0) to completely break out of any
|
|
@@ -4,8 +4,45 @@
|
|
|
4
4
|
* Downloads unresolved blobs in the background when blobMode='eager'.
|
|
5
5
|
* Called after sync completes to prefetch blobs for offline access.
|
|
6
6
|
*
|
|
7
|
+
* Strategy:
|
|
8
|
+
* 1. Snapshot the primary keys of all rows currently flagged
|
|
9
|
+
* `_hasBlobRefs=1` for each syncable table.
|
|
10
|
+
* 2. Walk that key list in chunks via `bulkGet`. Each `bulkGet`
|
|
11
|
+
* triggers the blob-resolve middleware, which does all the actual
|
|
12
|
+
* work — downloading blobs (throttled and deduplicated by the
|
|
13
|
+
* shared BlobDownloadTracker) and enqueueing them for persistence
|
|
14
|
+
* via the internal save queue.
|
|
15
|
+
*
|
|
16
|
+
* This keeps a single, symmetric code path with normal application
|
|
17
|
+
* reads, which is important when other middlewares are present
|
|
18
|
+
* (e.g., a hypothetical encryption middleware): writes from the save
|
|
19
|
+
* queue and reads from this loop both pass through the full middleware
|
|
20
|
+
* stack, so on-disk representation stays consistent.
|
|
21
|
+
*
|
|
22
|
+
* Why a snapshot of primary keys (rather than re-querying the index)?
|
|
23
|
+
* - Rows that get resolved by parallel application reads simply
|
|
24
|
+
* disappear from the table contents we're about to re-fetch; the
|
|
25
|
+
* middleware skips them since `_hasBlobRefs` is already cleared.
|
|
26
|
+
* - Stuck rows (e.g., blob 404s) are naturally bypassed: we just
|
|
27
|
+
* advance to the next chunk in the snapshot. No `seenKeys`
|
|
28
|
+
* bookkeeping required.
|
|
29
|
+
* - The snapshot is `string[]`-shaped for typical Dexie Cloud rows
|
|
30
|
+
* (~36 bytes/UUID), so ~28K keys per MB. Acceptable for any
|
|
31
|
+
* realistic dataset.
|
|
32
|
+
*
|
|
7
33
|
* Progress is tracked automatically via liveQuery in blobProgress.ts —
|
|
8
34
|
* no manual progress reporting needed here.
|
|
35
|
+
*
|
|
36
|
+
* --- Throughput note ---
|
|
37
|
+
* The chunk loop is sequential: bulkGet → wait for all downloads to
|
|
38
|
+
* settle → next bulkGet. The save queue drains in the background and
|
|
39
|
+
* does not block iteration (saves no longer need to be persisted before
|
|
40
|
+
* the next iteration, since we don't re-query the index). For typical
|
|
41
|
+
* blob sizes (10 KB – 10 MB) the network dominates total time. If
|
|
42
|
+
* real-world profiling later shows the per-chunk fixed cost matters,
|
|
43
|
+
* the next bulkGet could be kicked off in parallel with the current
|
|
44
|
+
* one's middleware work — but we keep it simple until measurements
|
|
45
|
+
* justify otherwise.
|
|
9
46
|
*/
|
|
10
47
|
import { BehaviorSubject } from 'rxjs';
|
|
11
48
|
import { DexieCloudDB } from '../db/DexieCloudDB';
|
|
@@ -13,8 +50,5 @@ import { DexieCloudDB } from '../db/DexieCloudDB';
|
|
|
13
50
|
* Download all unresolved blobs in the background.
|
|
14
51
|
*
|
|
15
52
|
* This is called when blobMode='eager' (default) after sync completes.
|
|
16
|
-
* BlobRef URLs are signed (SAS tokens) so no auth header needed.
|
|
17
|
-
*
|
|
18
|
-
* Each blob is saved atomically using Table.update() to avoid race conditions.
|
|
19
53
|
*/
|
|
20
54
|
export declare function downloadUnresolvedBlobs(db: DexieCloudDB, downloading$: BehaviorSubject<boolean>, signal?: AbortSignal): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ObservabilitySet } from 'dexie';
|
|
1
2
|
import { DexieCloudSchema } from 'dexie-cloud-common';
|
|
2
3
|
import { UserLogin } from '../db/entities/UserLogin';
|
|
3
4
|
export interface TXExpandos {
|
|
@@ -8,5 +9,6 @@ export interface TXExpandos {
|
|
|
8
9
|
disableAccessControl?: boolean;
|
|
9
10
|
disableBlobResolve?: boolean;
|
|
10
11
|
mutationsAdded?: boolean;
|
|
12
|
+
mutatedParts?: ObservabilitySet;
|
|
11
13
|
opCount: number;
|
|
12
14
|
}
|