dexie-cloud-addon 4.4.0 → 4.4.1
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/DexieCloudOptions.d.ts +10 -0
- package/dist/modern/dexie-cloud-addon.js +115 -29
- 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/service-worker.js +252 -166
- 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/blobOffloading.d.ts +5 -4
- package/dist/modern/sync/blobResolve.d.ts +3 -3
- package/dist/umd/dexie-cloud-addon.js +116 -30
- 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 +253 -167
- 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 +1 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { DBOperationsSet } from 'dexie-cloud-common';
|
|
8
8
|
import { BlobRef, BlobRefOrigType, isBlobRef as isBlobRefFromResolve } from './blobResolve';
|
|
9
|
+
export declare const DEFAULT_MAX_STRING_LENGTH = 32768;
|
|
9
10
|
export type { BlobRef, BlobRefOrigType };
|
|
10
11
|
export declare const isBlobRef: typeof isBlobRefFromResolve;
|
|
11
12
|
/**
|
|
@@ -17,21 +18,21 @@ export declare function shouldOffloadBlob(value: unknown): value is Blob | Array
|
|
|
17
18
|
* Upload a blob to the blob storage endpoint
|
|
18
19
|
*/
|
|
19
20
|
export declare function uploadBlob(databaseUrl: string, getCachedAccessToken: () => Promise<string | null>, blob: Blob | ArrayBuffer | ArrayBufferView): Promise<BlobRef | null>;
|
|
20
|
-
export declare function offloadBlobsAndMarkDirty(obj: unknown, databaseUrl: string, getCachedAccessToken: () => Promise<string | null
|
|
21
|
+
export declare function offloadBlobsAndMarkDirty(obj: unknown, databaseUrl: string, getCachedAccessToken: () => Promise<string | null>, maxStringLength?: number): Promise<unknown>;
|
|
21
22
|
/**
|
|
22
23
|
* Recursively scan an object for large blobs and upload them
|
|
23
24
|
* Returns a new object with blobs replaced by BlobRefs
|
|
24
25
|
*/
|
|
25
|
-
export declare function offloadBlobs(obj: unknown, databaseUrl: string, getCachedAccessToken: () => Promise<string | null>, dirtyFlag?: {
|
|
26
|
+
export declare function offloadBlobs(obj: unknown, databaseUrl: string, getCachedAccessToken: () => Promise<string | null>, maxStringLength?: number, dirtyFlag?: {
|
|
26
27
|
dirty: boolean;
|
|
27
28
|
}, visited?: WeakSet<object>): Promise<unknown>;
|
|
28
29
|
/**
|
|
29
30
|
* Process a DBOperationsSet and offload any large blobs
|
|
30
31
|
* Returns a new DBOperationsSet with blobs replaced by BlobRefs
|
|
31
32
|
*/
|
|
32
|
-
export declare function offloadBlobsInOperations(operations: DBOperationsSet, databaseUrl: string, getCachedAccessToken: () => Promise<string | null
|
|
33
|
+
export declare function offloadBlobsInOperations(operations: DBOperationsSet, databaseUrl: string, getCachedAccessToken: () => Promise<string | null>, maxStringLength?: number): Promise<DBOperationsSet>;
|
|
33
34
|
/**
|
|
34
35
|
* Check if there are any large blobs in the operations that need offloading
|
|
35
36
|
* This is a quick check to avoid unnecessary processing
|
|
36
37
|
*/
|
|
37
|
-
export declare function hasLargeBlobsInOperations(operations: DBOperationsSet): boolean;
|
|
38
|
+
export declare function hasLargeBlobsInOperations(operations: DBOperationsSet, maxStringLength?: number): boolean;
|
|
@@ -18,7 +18,7 @@ import { BlobDownloadTracker } from './BlobDownloadTracker';
|
|
|
18
18
|
* Original type that was offloaded to blob storage.
|
|
19
19
|
* Matches the TSON type names.
|
|
20
20
|
*/
|
|
21
|
-
export type BlobRefOrigType = 'Blob' | 'ArrayBuffer' | 'Uint8Array' | 'Int8Array' | 'Uint8ClampedArray' | 'Int16Array' | 'Uint16Array' | 'Int32Array' | 'Uint32Array' | 'Float32Array' | 'Float64Array' | 'BigInt64Array' | 'BigUint64Array' | 'DataView';
|
|
21
|
+
export type BlobRefOrigType = 'Blob' | 'ArrayBuffer' | 'Uint8Array' | 'Int8Array' | 'Uint8ClampedArray' | 'Int16Array' | 'Uint16Array' | 'Int32Array' | 'Uint32Array' | 'Float32Array' | 'Float64Array' | 'BigInt64Array' | 'BigUint64Array' | 'DataView' | 'string';
|
|
22
22
|
/**
|
|
23
23
|
* BlobRef represents a reference to binary data stored in blob storage.
|
|
24
24
|
* The _bt field contains the original JavaScript type (Uint8Array, Blob, etc.)
|
|
@@ -35,7 +35,7 @@ export interface BlobRef {
|
|
|
35
35
|
*/
|
|
36
36
|
export interface ResolvedBlob {
|
|
37
37
|
keyPath: string;
|
|
38
|
-
data: Blob | ArrayBuffer | ArrayBufferView;
|
|
38
|
+
data: Blob | ArrayBuffer | ArrayBufferView | string;
|
|
39
39
|
ref: string;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
@@ -65,7 +65,7 @@ export declare function hasBlobRefs(obj: unknown, visited?: WeakSet<object>): bo
|
|
|
65
65
|
/**
|
|
66
66
|
* Convert downloaded Uint8Array to the original type specified in BlobRef
|
|
67
67
|
*/
|
|
68
|
-
export declare function convertToOriginalType(data: Uint8Array, ref: BlobRef): Blob | ArrayBuffer | ArrayBufferView;
|
|
68
|
+
export declare function convertToOriginalType(data: Uint8Array, ref: BlobRef): Blob | ArrayBuffer | ArrayBufferView | string;
|
|
69
69
|
/**
|
|
70
70
|
* Recursively resolve all BlobRefs in an object and collect them for queueing.
|
|
71
71
|
* Returns a new object with BlobRefs replaced by their original type data,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.4.
|
|
11
|
+
* Version 4.4.1, Thu Mar 19 2026
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -2665,10 +2665,48 @@
|
|
|
2665
2665
|
obj._bt === undefined // Not a raw BlobRef
|
|
2666
2666
|
);
|
|
2667
2667
|
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Recursively check if an object contains any BlobRefs
|
|
2670
|
+
*/
|
|
2671
|
+
function hasBlobRefs(obj, visited = new WeakSet()) {
|
|
2672
|
+
if (obj === null || obj === undefined) {
|
|
2673
|
+
return false;
|
|
2674
|
+
}
|
|
2675
|
+
if (isBlobRef(obj)) {
|
|
2676
|
+
return true;
|
|
2677
|
+
}
|
|
2678
|
+
if (typeof obj !== 'object') {
|
|
2679
|
+
return false;
|
|
2680
|
+
}
|
|
2681
|
+
// Avoid circular references - check BEFORE processing
|
|
2682
|
+
if (visited.has(obj)) {
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2685
|
+
visited.add(obj);
|
|
2686
|
+
// Skip special objects that can't contain BlobRefs
|
|
2687
|
+
if (obj instanceof Date || obj instanceof RegExp || obj instanceof Blob) {
|
|
2688
|
+
return false;
|
|
2689
|
+
}
|
|
2690
|
+
if (obj instanceof ArrayBuffer || ArrayBuffer.isView(obj)) {
|
|
2691
|
+
return false;
|
|
2692
|
+
}
|
|
2693
|
+
if (Array.isArray(obj)) {
|
|
2694
|
+
return obj.some(item => hasBlobRefs(item, visited));
|
|
2695
|
+
}
|
|
2696
|
+
// Only traverse POJOs
|
|
2697
|
+
if (obj.constructor === Object) {
|
|
2698
|
+
return Object.values(obj).some(value => hasBlobRefs(value, visited));
|
|
2699
|
+
}
|
|
2700
|
+
return false;
|
|
2701
|
+
}
|
|
2668
2702
|
/**
|
|
2669
2703
|
* Convert downloaded Uint8Array to the original type specified in BlobRef
|
|
2670
2704
|
*/
|
|
2671
2705
|
function convertToOriginalType(data, ref) {
|
|
2706
|
+
// String type: decode UTF-8 back to string
|
|
2707
|
+
if (ref._bt === 'string') {
|
|
2708
|
+
return new TextDecoder().decode(data);
|
|
2709
|
+
}
|
|
2672
2710
|
// Get the underlying ArrayBuffer (handle shared buffer case)
|
|
2673
2711
|
const buffer = data.buffer.byteLength === data.byteLength
|
|
2674
2712
|
? data.buffer
|
|
@@ -4841,6 +4879,19 @@
|
|
|
4841
4879
|
});
|
|
4842
4880
|
}
|
|
4843
4881
|
|
|
4882
|
+
/**
|
|
4883
|
+
* If the incoming value contains BlobRefs (e.g. offloaded strings or binaries),
|
|
4884
|
+
* mark it with _hasBlobRefs = 1 so the blobResolveMiddleware will resolve them
|
|
4885
|
+
* on the next read.
|
|
4886
|
+
*/
|
|
4887
|
+
function markIfHasBlobRefs(obj) {
|
|
4888
|
+
if (obj !== null &&
|
|
4889
|
+
typeof obj === 'object' &&
|
|
4890
|
+
obj.constructor === Object &&
|
|
4891
|
+
hasBlobRefs(obj)) {
|
|
4892
|
+
obj._hasBlobRefs = 1;
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4844
4895
|
function applyServerChanges(changes, db) {
|
|
4845
4896
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4846
4897
|
console.debug('Applying server changes', changes, Dexie.currentTransaction);
|
|
@@ -4876,6 +4927,7 @@
|
|
|
4876
4927
|
const keys = mut.keys.map(keyDecoder);
|
|
4877
4928
|
switch (mut.type) {
|
|
4878
4929
|
case 'insert':
|
|
4930
|
+
mut.values.forEach(markIfHasBlobRefs);
|
|
4879
4931
|
if (primaryKey.outbound) {
|
|
4880
4932
|
yield table.bulkAdd(mut.values, keys);
|
|
4881
4933
|
}
|
|
@@ -4888,6 +4940,7 @@
|
|
|
4888
4940
|
}
|
|
4889
4941
|
break;
|
|
4890
4942
|
case 'upsert':
|
|
4943
|
+
mut.values.forEach(markIfHasBlobRefs);
|
|
4891
4944
|
if (primaryKey.outbound) {
|
|
4892
4945
|
yield table.bulkPut(mut.values, keys);
|
|
4893
4946
|
}
|
|
@@ -14306,7 +14359,7 @@
|
|
|
14306
14359
|
*
|
|
14307
14360
|
* ==========================================================================
|
|
14308
14361
|
*
|
|
14309
|
-
* Version 4.4.0,
|
|
14362
|
+
* Version 4.4.0, Thu Mar 19 2026
|
|
14310
14363
|
*
|
|
14311
14364
|
* https://dexie.org
|
|
14312
14365
|
*
|
|
@@ -14811,6 +14864,8 @@
|
|
|
14811
14864
|
*/
|
|
14812
14865
|
// Blobs >= 4KB are offloaded to blob storage
|
|
14813
14866
|
const BLOB_OFFLOAD_THRESHOLD = 4096;
|
|
14867
|
+
// Default max string length before offloading (32KB characters)
|
|
14868
|
+
const DEFAULT_MAX_STRING_LENGTH = 32768;
|
|
14814
14869
|
// Cache: once we know the server doesn't support blob storage, skip future uploads.
|
|
14815
14870
|
// Maps databaseUrl → boolean (true = supported, false = not supported).
|
|
14816
14871
|
const blobEndpointSupported = new Map();
|
|
@@ -14951,10 +15006,10 @@
|
|
|
14951
15006
|
);
|
|
14952
15007
|
});
|
|
14953
15008
|
}
|
|
14954
|
-
function offloadBlobsAndMarkDirty(
|
|
14955
|
-
return __awaiter(this,
|
|
15009
|
+
function offloadBlobsAndMarkDirty(obj_1, databaseUrl_1, getCachedAccessToken_1) {
|
|
15010
|
+
return __awaiter(this, arguments, void 0, function* (obj, databaseUrl, getCachedAccessToken, maxStringLength = DEFAULT_MAX_STRING_LENGTH) {
|
|
14956
15011
|
const dirtyFlag = { dirty: false };
|
|
14957
|
-
const result = yield offloadBlobs(obj, databaseUrl, getCachedAccessToken, dirtyFlag);
|
|
15012
|
+
const result = yield offloadBlobs(obj, databaseUrl, getCachedAccessToken, maxStringLength, dirtyFlag);
|
|
14958
15013
|
// Mark the object as dirty for sync if any blobs were offloaded
|
|
14959
15014
|
if (dirtyFlag.dirty && typeof result === 'object' && result !== null && result.constructor === Object) {
|
|
14960
15015
|
result._hasBlobRefs = 1;
|
|
@@ -14967,10 +15022,26 @@
|
|
|
14967
15022
|
* Returns a new object with blobs replaced by BlobRefs
|
|
14968
15023
|
*/
|
|
14969
15024
|
function offloadBlobs(obj_1, databaseUrl_1, getCachedAccessToken_1) {
|
|
14970
|
-
return __awaiter(this, arguments, void 0, function* (obj, databaseUrl, getCachedAccessToken, dirtyFlag = { dirty: false }, visited = new WeakSet()) {
|
|
15025
|
+
return __awaiter(this, arguments, void 0, function* (obj, databaseUrl, getCachedAccessToken, maxStringLength = DEFAULT_MAX_STRING_LENGTH, dirtyFlag = { dirty: false }, visited = new WeakSet()) {
|
|
14971
15026
|
if (obj === null || obj === undefined) {
|
|
14972
15027
|
return obj;
|
|
14973
15028
|
}
|
|
15029
|
+
// Check if this is a long string that should be offloaded
|
|
15030
|
+
if (typeof obj === 'string' && obj.length > maxStringLength && maxStringLength !== Infinity) {
|
|
15031
|
+
if (blobEndpointSupported.get(databaseUrl) === false) {
|
|
15032
|
+
return obj;
|
|
15033
|
+
}
|
|
15034
|
+
const blob = new Blob([obj], { type: 'text/plain;charset=utf-8' });
|
|
15035
|
+
const blobRef = yield uploadBlob(databaseUrl, getCachedAccessToken, blob);
|
|
15036
|
+
if (blobRef === null) {
|
|
15037
|
+
blobEndpointSupported.set(databaseUrl, false);
|
|
15038
|
+
return obj;
|
|
15039
|
+
}
|
|
15040
|
+
blobEndpointSupported.set(databaseUrl, true);
|
|
15041
|
+
dirtyFlag.dirty = true;
|
|
15042
|
+
// Mark as string type so it's resolved back to string, not Blob
|
|
15043
|
+
return Object.assign(Object.assign({}, blobRef), { _bt: 'string' });
|
|
15044
|
+
}
|
|
14974
15045
|
// Check if this is a blob that should be offloaded
|
|
14975
15046
|
if (shouldOffloadBlob(obj)) {
|
|
14976
15047
|
if (blobEndpointSupported.get(databaseUrl) === false) {
|
|
@@ -14999,7 +15070,7 @@
|
|
|
14999
15070
|
if (Array.isArray(obj)) {
|
|
15000
15071
|
const result = [];
|
|
15001
15072
|
for (const item of obj) {
|
|
15002
|
-
result.push(yield offloadBlobs(item, databaseUrl, getCachedAccessToken, dirtyFlag, visited));
|
|
15073
|
+
result.push(yield offloadBlobs(item, databaseUrl, getCachedAccessToken, maxStringLength, dirtyFlag, visited));
|
|
15003
15074
|
}
|
|
15004
15075
|
return result;
|
|
15005
15076
|
}
|
|
@@ -15011,7 +15082,7 @@
|
|
|
15011
15082
|
}
|
|
15012
15083
|
const result = {};
|
|
15013
15084
|
for (const [key, value] of Object.entries(obj)) {
|
|
15014
|
-
result[key] = yield offloadBlobs(value, databaseUrl, getCachedAccessToken, dirtyFlag, visited);
|
|
15085
|
+
result[key] = yield offloadBlobs(value, databaseUrl, getCachedAccessToken, maxStringLength, dirtyFlag, visited);
|
|
15015
15086
|
}
|
|
15016
15087
|
return result;
|
|
15017
15088
|
});
|
|
@@ -15020,13 +15091,13 @@
|
|
|
15020
15091
|
* Process a DBOperationsSet and offload any large blobs
|
|
15021
15092
|
* Returns a new DBOperationsSet with blobs replaced by BlobRefs
|
|
15022
15093
|
*/
|
|
15023
|
-
function offloadBlobsInOperations(
|
|
15024
|
-
return __awaiter(this,
|
|
15094
|
+
function offloadBlobsInOperations(operations_1, databaseUrl_1, getCachedAccessToken_1) {
|
|
15095
|
+
return __awaiter(this, arguments, void 0, function* (operations, databaseUrl, getCachedAccessToken, maxStringLength = DEFAULT_MAX_STRING_LENGTH) {
|
|
15025
15096
|
const result = [];
|
|
15026
15097
|
for (const tableOps of operations) {
|
|
15027
15098
|
const processedMuts = [];
|
|
15028
15099
|
for (const mut of tableOps.muts) {
|
|
15029
|
-
const processedMut = yield offloadBlobsInOperation(mut, databaseUrl, getCachedAccessToken);
|
|
15100
|
+
const processedMut = yield offloadBlobsInOperation(mut, databaseUrl, getCachedAccessToken, maxStringLength);
|
|
15030
15101
|
processedMuts.push(processedMut);
|
|
15031
15102
|
}
|
|
15032
15103
|
result.push({
|
|
@@ -15037,20 +15108,20 @@
|
|
|
15037
15108
|
return result;
|
|
15038
15109
|
});
|
|
15039
15110
|
}
|
|
15040
|
-
function offloadBlobsInOperation(
|
|
15041
|
-
return __awaiter(this,
|
|
15111
|
+
function offloadBlobsInOperation(op_1, databaseUrl_1, getCachedAccessToken_1) {
|
|
15112
|
+
return __awaiter(this, arguments, void 0, function* (op, databaseUrl, getCachedAccessToken, maxStringLength = DEFAULT_MAX_STRING_LENGTH) {
|
|
15042
15113
|
switch (op.type) {
|
|
15043
15114
|
case 'insert':
|
|
15044
15115
|
case 'upsert': {
|
|
15045
|
-
const processedValues = yield Promise.all(op.values.map(value => offloadBlobsAndMarkDirty(value, databaseUrl, getCachedAccessToken)));
|
|
15116
|
+
const processedValues = yield Promise.all(op.values.map(value => offloadBlobsAndMarkDirty(value, databaseUrl, getCachedAccessToken, maxStringLength)));
|
|
15046
15117
|
return Object.assign(Object.assign({}, op), { values: processedValues });
|
|
15047
15118
|
}
|
|
15048
15119
|
case 'update': {
|
|
15049
|
-
const processedChangeSpecs = yield Promise.all(op.changeSpecs.map(spec => offloadBlobsAndMarkDirty(spec, databaseUrl, getCachedAccessToken)));
|
|
15120
|
+
const processedChangeSpecs = yield Promise.all(op.changeSpecs.map(spec => offloadBlobsAndMarkDirty(spec, databaseUrl, getCachedAccessToken, maxStringLength)));
|
|
15050
15121
|
return Object.assign(Object.assign({}, op), { changeSpecs: processedChangeSpecs });
|
|
15051
15122
|
}
|
|
15052
15123
|
case 'modify': {
|
|
15053
|
-
const processedChangeSpec = yield offloadBlobsAndMarkDirty(op.changeSpec, databaseUrl, getCachedAccessToken);
|
|
15124
|
+
const processedChangeSpec = yield offloadBlobsAndMarkDirty(op.changeSpec, databaseUrl, getCachedAccessToken, maxStringLength);
|
|
15054
15125
|
return Object.assign(Object.assign({}, op), { changeSpec: processedChangeSpec });
|
|
15055
15126
|
}
|
|
15056
15127
|
case 'delete':
|
|
@@ -15065,33 +15136,37 @@
|
|
|
15065
15136
|
* Check if there are any large blobs in the operations that need offloading
|
|
15066
15137
|
* This is a quick check to avoid unnecessary processing
|
|
15067
15138
|
*/
|
|
15068
|
-
function hasLargeBlobsInOperations(operations) {
|
|
15139
|
+
function hasLargeBlobsInOperations(operations, maxStringLength = DEFAULT_MAX_STRING_LENGTH) {
|
|
15069
15140
|
for (const tableOps of operations) {
|
|
15070
15141
|
for (const mut of tableOps.muts) {
|
|
15071
|
-
if (hasLargeBlobsInOperation(mut)) {
|
|
15142
|
+
if (hasLargeBlobsInOperation(mut, maxStringLength)) {
|
|
15072
15143
|
return true;
|
|
15073
15144
|
}
|
|
15074
15145
|
}
|
|
15075
15146
|
}
|
|
15076
15147
|
return false;
|
|
15077
15148
|
}
|
|
15078
|
-
function hasLargeBlobsInOperation(op) {
|
|
15149
|
+
function hasLargeBlobsInOperation(op, maxStringLength) {
|
|
15079
15150
|
switch (op.type) {
|
|
15080
15151
|
case 'insert':
|
|
15081
15152
|
case 'upsert':
|
|
15082
|
-
return op.values.some(value => hasLargeBlobs(value));
|
|
15153
|
+
return op.values.some(value => hasLargeBlobs(value, maxStringLength));
|
|
15083
15154
|
case 'update':
|
|
15084
|
-
return op.changeSpecs.some(spec => hasLargeBlobs(spec));
|
|
15155
|
+
return op.changeSpecs.some(spec => hasLargeBlobs(spec, maxStringLength));
|
|
15085
15156
|
case 'modify':
|
|
15086
|
-
return hasLargeBlobs(op.changeSpec);
|
|
15157
|
+
return hasLargeBlobs(op.changeSpec, maxStringLength);
|
|
15087
15158
|
default:
|
|
15088
15159
|
return false;
|
|
15089
15160
|
}
|
|
15090
15161
|
}
|
|
15091
|
-
function hasLargeBlobs(obj, visited = new WeakSet()) {
|
|
15162
|
+
function hasLargeBlobs(obj, maxStringLength, visited = new WeakSet()) {
|
|
15092
15163
|
if (obj === null || obj === undefined) {
|
|
15093
15164
|
return false;
|
|
15094
15165
|
}
|
|
15166
|
+
// Check long strings
|
|
15167
|
+
if (typeof obj === 'string' && obj.length > maxStringLength && maxStringLength !== Infinity) {
|
|
15168
|
+
return true;
|
|
15169
|
+
}
|
|
15095
15170
|
if (shouldOffloadBlob(obj)) {
|
|
15096
15171
|
return true;
|
|
15097
15172
|
}
|
|
@@ -15104,13 +15179,13 @@
|
|
|
15104
15179
|
}
|
|
15105
15180
|
visited.add(obj);
|
|
15106
15181
|
if (Array.isArray(obj)) {
|
|
15107
|
-
return obj.some(item => hasLargeBlobs(item, visited));
|
|
15182
|
+
return obj.some(item => hasLargeBlobs(item, maxStringLength, visited));
|
|
15108
15183
|
}
|
|
15109
15184
|
// Traverse plain objects (POJO-like) - use duck typing since IndexedDB
|
|
15110
15185
|
// may return objects where constructor !== Object
|
|
15111
15186
|
const proto = Object.getPrototypeOf(obj);
|
|
15112
15187
|
if (proto === Object.prototype || proto === null) {
|
|
15113
|
-
return Object.values(obj).some(value => hasLargeBlobs(value, visited));
|
|
15188
|
+
return Object.values(obj).some(value => hasLargeBlobs(value, maxStringLength, visited));
|
|
15114
15189
|
}
|
|
15115
15190
|
return false;
|
|
15116
15191
|
}
|
|
@@ -15371,7 +15446,7 @@
|
|
|
15371
15446
|
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
15372
15447
|
isInitialSync: false,
|
|
15373
15448
|
}) {
|
|
15374
|
-
var _a;
|
|
15449
|
+
var _a, _b, _c;
|
|
15375
15450
|
if (!justCheckIfNeeded) {
|
|
15376
15451
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
15377
15452
|
}
|
|
@@ -15445,9 +15520,10 @@
|
|
|
15445
15520
|
// Offload large blobs to blob storage before sync
|
|
15446
15521
|
//
|
|
15447
15522
|
let processedChangeSet = clientChangeSet;
|
|
15448
|
-
const
|
|
15523
|
+
const maxStringLength = (_c = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.maxStringLength) !== null && _c !== void 0 ? _c : 32768;
|
|
15524
|
+
const hasLargeBlobs = hasLargeBlobsInOperations(clientChangeSet, maxStringLength);
|
|
15449
15525
|
if (hasLargeBlobs) {
|
|
15450
|
-
processedChangeSet = yield offloadBlobsInOperations(clientChangeSet, databaseUrl, () => loadCachedAccessToken(db));
|
|
15526
|
+
processedChangeSet = yield offloadBlobsInOperations(clientChangeSet, databaseUrl, () => loadCachedAccessToken(db), maxStringLength);
|
|
15451
15527
|
}
|
|
15452
15528
|
//
|
|
15453
15529
|
// Push changes to server
|
|
@@ -19419,7 +19495,7 @@
|
|
|
19419
19495
|
const downloading$ = createDownloadingState();
|
|
19420
19496
|
dexie.cloud = {
|
|
19421
19497
|
// @ts-ignore
|
|
19422
|
-
version: "4.4.
|
|
19498
|
+
version: "4.4.1",
|
|
19423
19499
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
19424
19500
|
schema: null,
|
|
19425
19501
|
get currentUserId() {
|
|
@@ -19447,6 +19523,16 @@
|
|
|
19447
19523
|
invites: getInvitesObservable(dexie),
|
|
19448
19524
|
roles: getGlobalRolesObservable(dexie),
|
|
19449
19525
|
configure(options) {
|
|
19526
|
+
// Validate maxStringLength — Infinity disables offloading, otherwise must be
|
|
19527
|
+
// a finite positive number not exceeding the server limit (32768).
|
|
19528
|
+
const MAX_SERVER_STRING_LENGTH = 32768;
|
|
19529
|
+
if (options.maxStringLength !== undefined &&
|
|
19530
|
+
options.maxStringLength !== Infinity &&
|
|
19531
|
+
(!Number.isFinite(options.maxStringLength) ||
|
|
19532
|
+
options.maxStringLength < 0 ||
|
|
19533
|
+
options.maxStringLength > MAX_SERVER_STRING_LENGTH)) {
|
|
19534
|
+
throw new Error(`maxStringLength must be Infinity or a finite number in [0, ${MAX_SERVER_STRING_LENGTH}]. Got: ${options.maxStringLength}`);
|
|
19535
|
+
}
|
|
19450
19536
|
options = dexie.cloud.options = Object.assign(Object.assign({}, dexie.cloud.options), options);
|
|
19451
19537
|
configuredProgramatically = true;
|
|
19452
19538
|
if (options.databaseUrl && options.nameSuffix) {
|
|
@@ -19833,7 +19919,7 @@
|
|
|
19833
19919
|
}
|
|
19834
19920
|
}
|
|
19835
19921
|
// @ts-ignore
|
|
19836
|
-
dexieCloud.version = "4.4.
|
|
19922
|
+
dexieCloud.version = "4.4.1";
|
|
19837
19923
|
Dexie.Cloud = dexieCloud;
|
|
19838
19924
|
|
|
19839
19925
|
exports.default = dexieCloud;
|