dexie-cloud-addon 4.4.0 → 4.4.2

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 (160) hide show
  1. package/dist/modern/DexieCloudOptions.d.ts +12 -0
  2. package/dist/modern/default-ui/AuthProviderButton.d.ts +21 -0
  3. package/dist/modern/default-ui/ProviderSelectionDialog.d.ts +7 -0
  4. package/dist/modern/default-ui/SelectDialog.d.ts +10 -0
  5. package/dist/modern/dexie-cloud-addon.js +132 -35
  6. package/dist/modern/dexie-cloud-addon.js.map +1 -1
  7. package/dist/modern/dexie-cloud-addon.min.js +1 -1
  8. package/dist/modern/dexie-cloud-addon.min.js.gz +0 -0
  9. package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
  10. package/dist/modern/service-worker.js +269 -172
  11. package/dist/modern/service-worker.js.map +1 -1
  12. package/dist/modern/service-worker.min.js +1 -1
  13. package/dist/modern/service-worker.min.js.map +1 -1
  14. package/dist/modern/sync/blobOffloading.d.ts +5 -4
  15. package/dist/modern/sync/blobResolve.d.ts +3 -3
  16. package/dist/umd/DISABLE_SERVICEWORKER_STRATEGY.d.ts +1 -0
  17. package/dist/umd/DXCWebSocketStatus.d.ts +1 -0
  18. package/dist/umd/DexieCloudAPI.d.ts +75 -0
  19. package/dist/umd/DexieCloudOptions.d.ts +27 -0
  20. package/dist/umd/DexieCloudSyncOptions.d.ts +4 -0
  21. package/dist/umd/DexieCloudTable.d.ts +18 -0
  22. package/dist/umd/InvalidLicenseError.d.ts +5 -0
  23. package/dist/umd/Invite.d.ts +8 -0
  24. package/dist/umd/PermissionChecker.d.ts +15 -0
  25. package/dist/umd/TSON.d.ts +17 -0
  26. package/dist/umd/WSObservable.d.ts +72 -0
  27. package/dist/umd/associate.d.ts +1 -0
  28. package/dist/umd/authentication/AuthPersistedContext.d.ts +9 -0
  29. package/dist/umd/authentication/TokenErrorResponseError.d.ts +10 -0
  30. package/dist/umd/authentication/TokenExpiredError.d.ts +3 -0
  31. package/dist/umd/authentication/UNAUTHORIZED_USER.d.ts +2 -0
  32. package/dist/umd/authentication/authenticate.d.ts +13 -0
  33. package/dist/umd/authentication/currentUserObservable.d.ts +1 -0
  34. package/dist/umd/authentication/interactWithUser.d.ts +21 -0
  35. package/dist/umd/authentication/login.d.ts +3 -0
  36. package/dist/umd/authentication/logout.d.ts +5 -0
  37. package/dist/umd/authentication/otpFetchTokenCallback.d.ts +3 -0
  38. package/dist/umd/authentication/setCurrentUser.d.ts +14 -0
  39. package/dist/umd/authentication/waitUntil.d.ts +3 -0
  40. package/dist/umd/computeSyncState.d.ts +4 -0
  41. package/dist/umd/createSharedValueObservable.d.ts +3 -0
  42. package/dist/umd/currentUserEmitter.d.ts +3 -0
  43. package/dist/umd/db/DexieCloudDB.d.ts +61 -0
  44. package/dist/umd/db/entities/BaseRevisionMapEntry.d.ts +5 -0
  45. package/dist/umd/db/entities/EntityCommon.d.ts +5 -0
  46. package/dist/umd/db/entities/GuardedJob.d.ts +5 -0
  47. package/dist/umd/db/entities/Member.d.ts +19 -0
  48. package/dist/umd/db/entities/PersistedSyncState.d.ts +22 -0
  49. package/dist/umd/db/entities/Realm.d.ts +14 -0
  50. package/dist/umd/db/entities/Role.d.ts +11 -0
  51. package/dist/umd/db/entities/UserLogin.d.ts +23 -0
  52. package/dist/umd/default-ui/Dialog.d.ts +5 -0
  53. package/dist/umd/default-ui/LoginDialog.d.ts +3 -0
  54. package/dist/umd/default-ui/Styles.d.ts +3 -0
  55. package/dist/umd/default-ui/index.d.ts +24 -0
  56. package/dist/umd/define-ydoc-trigger.d.ts +3 -0
  57. package/dist/umd/dexie-cloud-addon.d.ts +3 -0
  58. package/dist/umd/dexie-cloud-addon.js +133 -36
  59. package/dist/umd/dexie-cloud-addon.js.gz +0 -0
  60. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  61. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  62. package/dist/umd/dexie-cloud-addon.min.js.gz +0 -0
  63. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  64. package/dist/umd/dexie-cloud-client.d.ts +23 -0
  65. package/dist/umd/errors/HttpError.d.ts +5 -0
  66. package/dist/umd/extend-dexie-interface.d.ts +23 -0
  67. package/dist/umd/getGlobalRolesObservable.d.ts +5 -0
  68. package/dist/umd/getInternalAccessControlObservable.d.ts +12 -0
  69. package/dist/umd/getInvitesObservable.d.ts +23 -0
  70. package/dist/umd/getPermissionsLookupObservable.d.ts +16 -0
  71. package/dist/umd/getTiedRealmId.d.ts +2 -0
  72. package/dist/umd/helpers/BroadcastedAndLocalEvent.d.ts +8 -0
  73. package/dist/umd/helpers/CancelToken.d.ts +4 -0
  74. package/dist/umd/helpers/IS_SERVICE_WORKER.d.ts +1 -0
  75. package/dist/umd/helpers/SWBroadcastChannel.d.ts +12 -0
  76. package/dist/umd/helpers/allSettled.d.ts +1 -0
  77. package/dist/umd/helpers/bulkUpdate.d.ts +4 -0
  78. package/dist/umd/helpers/computeRealmSetHash.d.ts +2 -0
  79. package/dist/umd/helpers/date-constants.d.ts +5 -0
  80. package/dist/umd/helpers/flatten.d.ts +1 -0
  81. package/dist/umd/helpers/getMutationTable.d.ts +1 -0
  82. package/dist/umd/helpers/getSyncableTables.d.ts +4 -0
  83. package/dist/umd/helpers/getTableFromMutationTable.d.ts +1 -0
  84. package/dist/umd/helpers/makeArray.d.ts +1 -0
  85. package/dist/umd/helpers/randomString.d.ts +1 -0
  86. package/dist/umd/helpers/resolveText.d.ts +16 -0
  87. package/dist/umd/helpers/throwVersionIncrementNeeded.d.ts +1 -0
  88. package/dist/umd/helpers/visibilityState.d.ts +1 -0
  89. package/dist/umd/isEagerSyncDisabled.d.ts +2 -0
  90. package/dist/umd/isFirefox.d.ts +1 -0
  91. package/dist/umd/isSafari.d.ts +2 -0
  92. package/dist/umd/mapValueObservable.d.ts +5 -0
  93. package/dist/umd/mergePermissions.d.ts +2 -0
  94. package/dist/umd/middleware-helpers/guardedTable.d.ts +11 -0
  95. package/dist/umd/middleware-helpers/idGenerationHelpers.d.ts +18 -0
  96. package/dist/umd/middlewares/createIdGenerationMiddleware.d.ts +3 -0
  97. package/dist/umd/middlewares/createImplicitPropSetterMiddleware.d.ts +3 -0
  98. package/dist/umd/middlewares/createMutationTrackingMiddleware.d.ts +17 -0
  99. package/dist/umd/middlewares/outstandingTransaction.d.ts +4 -0
  100. package/dist/umd/overrideParseStoresSpec.d.ts +4 -0
  101. package/dist/umd/performInitialSync.d.ts +4 -0
  102. package/dist/umd/permissions.d.ts +9 -0
  103. package/dist/umd/prodLog.d.ts +9 -0
  104. package/dist/umd/service-worker.d.ts +1 -0
  105. package/dist/umd/service-worker.js +270 -173
  106. package/dist/umd/service-worker.js.map +1 -1
  107. package/dist/umd/service-worker.min.js +1 -1
  108. package/dist/umd/service-worker.min.js.map +1 -1
  109. package/dist/umd/sync/DEXIE_CLOUD_SYNCER_ID.d.ts +1 -0
  110. package/dist/umd/sync/LocalSyncWorker.d.ts +7 -0
  111. package/dist/umd/sync/SyncRequiredError.d.ts +3 -0
  112. package/dist/umd/sync/applyServerChanges.d.ts +3 -0
  113. package/dist/umd/sync/connectWebSocket.d.ts +2 -0
  114. package/dist/umd/sync/encodeIdsForServer.d.ts +4 -0
  115. package/dist/umd/sync/extractRealm.d.ts +2 -0
  116. package/dist/umd/sync/getLatestRevisionsPerTable.d.ts +6 -0
  117. package/dist/umd/sync/getTablesToSyncify.d.ts +3 -0
  118. package/dist/umd/sync/isOnline.d.ts +1 -0
  119. package/dist/umd/sync/isSyncNeeded.d.ts +2 -0
  120. package/dist/umd/sync/listClientChanges.d.ts +9 -0
  121. package/dist/umd/sync/listSyncifiedChanges.d.ts +5 -0
  122. package/dist/umd/sync/messageConsumerIsReady.d.ts +2 -0
  123. package/dist/umd/sync/messagesFromServerQueue.d.ts +8 -0
  124. package/dist/umd/sync/modifyLocalObjectsWithNewUserId.d.ts +4 -0
  125. package/dist/umd/sync/myId.d.ts +1 -0
  126. package/dist/umd/sync/numUnsyncedMutations.d.ts +2 -0
  127. package/dist/umd/sync/old_startSyncingClientChanges.d.ts +39 -0
  128. package/dist/umd/sync/performGuardedJob.d.ts +2 -0
  129. package/dist/umd/sync/ratelimit.d.ts +3 -0
  130. package/dist/umd/sync/registerSyncEvent.d.ts +3 -0
  131. package/dist/umd/sync/sync.d.ts +15 -0
  132. package/dist/umd/sync/syncIfPossible.d.ts +5 -0
  133. package/dist/umd/sync/syncWithServer.d.ts +6 -0
  134. package/dist/umd/sync/triggerSync.d.ts +2 -0
  135. package/dist/umd/sync/updateBaseRevs.d.ts +5 -0
  136. package/dist/umd/types/DXCAlert.d.ts +25 -0
  137. package/dist/umd/types/DXCInputField.d.ts +11 -0
  138. package/dist/umd/types/DXCUserInteraction.d.ts +93 -0
  139. package/dist/umd/types/NewIdOptions.d.ts +3 -0
  140. package/dist/umd/types/SWMessageEvent.d.ts +3 -0
  141. package/dist/umd/types/SWSyncEvent.d.ts +4 -0
  142. package/dist/umd/types/SyncState.d.ts +9 -0
  143. package/dist/umd/types/TXExpandos.d.ts +11 -0
  144. package/dist/umd/updateSchemaFromOptions.d.ts +3 -0
  145. package/dist/umd/userIsActive.d.ts +7 -0
  146. package/dist/umd/verifyConfig.d.ts +2 -0
  147. package/dist/umd/verifySchema.d.ts +2 -0
  148. package/dist/umd/yjs/YDexieCloudSyncState.d.ts +3 -0
  149. package/dist/umd/yjs/YTable.d.ts +3 -0
  150. package/dist/umd/yjs/applyYMessages.d.ts +9 -0
  151. package/dist/umd/yjs/awareness.d.ts +3 -0
  152. package/dist/umd/yjs/createYClientUpdateObservable.d.ts +4 -0
  153. package/dist/umd/yjs/createYHandler.d.ts +2 -0
  154. package/dist/umd/yjs/downloadYDocsFromServer.d.ts +3 -0
  155. package/dist/umd/yjs/getUpdatesTable.d.ts +3 -0
  156. package/dist/umd/yjs/listUpdatesSince.d.ts +3 -0
  157. package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +26 -0
  158. package/dist/umd/yjs/reopenDocSignal.d.ts +10 -0
  159. package/dist/umd/yjs/updateYSyncStates.d.ts +6 -0
  160. package/package.json +1 -1
@@ -0,0 +1,3 @@
1
+ export declare const Styles: {
2
+ [styleAlias: string]: Partial<CSSStyleDeclaration> | any;
3
+ };
@@ -0,0 +1,24 @@
1
+ import Dexie from "dexie";
2
+ import "../extend-dexie-interface";
3
+ import { h, Component } from "preact";
4
+ import { Subscription } from "rxjs";
5
+ import { DXCUserInteraction } from "../types/DXCUserInteraction";
6
+ export interface Props {
7
+ db: Dexie;
8
+ }
9
+ interface State {
10
+ userInteraction: DXCUserInteraction | undefined;
11
+ }
12
+ export default class LoginGui extends Component<Props, State> {
13
+ subscription?: Subscription;
14
+ observer: (userInteraction: DXCUserInteraction | undefined) => void;
15
+ constructor(props: Props);
16
+ componentDidMount(): void;
17
+ componentWillUnmount(): void;
18
+ render(props: Props, { userInteraction }: State): h.JSX.Element | null;
19
+ }
20
+ export declare function setupDefaultGUI(db: Dexie): {
21
+ unsubscribe(): void;
22
+ readonly closed: boolean;
23
+ };
24
+ export {};
@@ -0,0 +1,3 @@
1
+ import { type Table } from 'dexie';
2
+ import type { Doc as YjsDoc } from 'yjs';
3
+ export declare function defineYDocTrigger<T, TKey>(table: Table<T, TKey>, prop: keyof T & string, trigger: (ydoc: YjsDoc, parentId: TKey) => any): void;
@@ -0,0 +1,3 @@
1
+ import dexieCloudAddon from './dexie-cloud-client';
2
+ export * from './dexie-cloud-client';
3
+ export default dexieCloudAddon;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.4.0, Wed Mar 18 2026
11
+ * Version 4.4.2, 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, Wed Mar 18 2026
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(obj, databaseUrl, getCachedAccessToken) {
14955
- return __awaiter(this, void 0, void 0, function* () {
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(operations, databaseUrl, getCachedAccessToken) {
15024
- return __awaiter(this, void 0, void 0, function* () {
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(op, databaseUrl, getCachedAccessToken) {
15041
- return __awaiter(this, void 0, void 0, function* () {
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 hasLargeBlobs = hasLargeBlobsInOperations(clientChangeSet);
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
@@ -15753,13 +15829,13 @@
15753
15829
  //triggerSync(db, 'pull');
15754
15830
  yield db.cloud.sync({ purpose: 'pull', wait: true });
15755
15831
  break;
15756
- case 'changes':
15832
+ case 'changes': {
15757
15833
  console.debug('changes');
15758
15834
  if (((_f = db.cloud.syncState.value) === null || _f === void 0 ? void 0 : _f.phase) === 'error') {
15759
15835
  triggerSync(db, 'pull');
15760
15836
  break;
15761
15837
  }
15762
- yield db.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
15838
+ const didApplyChanges = yield db.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
15763
15839
  // @ts-ignore
15764
15840
  tx.idbtrans.disableChangeTracking = true;
15765
15841
  // @ts-ignore
@@ -15776,7 +15852,7 @@
15776
15852
  schema,
15777
15853
  currentUser,
15778
15854
  });
15779
- return; // Initial sync must have taken place - otherwise, ignore this.
15855
+ return false; // Initial sync must have taken place - otherwise, ignore this.
15780
15856
  }
15781
15857
  // Verify again in ACID tx that we're on same server revision.
15782
15858
  if (msg.baseRev !== syncState.serverRevision) {
@@ -15797,7 +15873,7 @@
15797
15873
  // If we don't do a sync request now, we could stuck in an endless loop.
15798
15874
  triggerSync(db, 'pull');
15799
15875
  }
15800
- return; // Ignore message
15876
+ return false; // Ignore message
15801
15877
  }
15802
15878
  // Verify also that the message is based on the exact same set of realms
15803
15879
  const ourRealmSetHash = yield Dexie.waitFor(
@@ -15809,7 +15885,7 @@
15809
15885
  triggerSync(db, 'pull');
15810
15886
  // The message isn't based on the same realms.
15811
15887
  // Trigger a sync instead to resolve all things up.
15812
- return;
15888
+ return false;
15813
15889
  }
15814
15890
  // Get clientChanges
15815
15891
  let clientChanges = [];
@@ -15839,9 +15915,17 @@
15839
15915
  //
15840
15916
  console.debug('Updating syncState', syncState);
15841
15917
  yield db.$syncState.put(syncState, 'syncState');
15918
+ return true;
15842
15919
  }));
15843
15920
  console.debug('msg queue: done with rw transaction');
15921
+ // Trigger eager blob download for any BlobRefs received via WebSocket.
15922
+ // This mirrors the behavior after normal HTTP sync (syncCompleteEvent).
15923
+ // Only emit if changes were actually applied (not on early returns).
15924
+ if (didApplyChanges && msg.changes.length > 0) {
15925
+ db.syncCompleteEvent.next();
15926
+ }
15844
15927
  break;
15928
+ }
15845
15929
  }
15846
15930
  }
15847
15931
  catch (error) {
@@ -16812,7 +16896,7 @@
16812
16896
  return {
16813
16897
  stack: 'dbcore',
16814
16898
  name: 'blobResolve',
16815
- level: -2, // Run below other middlewares and after sync and caching middlewares
16899
+ level: 2, // Run above cache (0) and other middlewares (1) to resolve BlobRefs from cached data
16816
16900
  create(downlevelDatabase) {
16817
16901
  // Create a single queue instance for this database
16818
16902
  const blobSavingQueue = new BlobSavingQueue(db);
@@ -19419,7 +19503,7 @@
19419
19503
  const downloading$ = createDownloadingState();
19420
19504
  dexie.cloud = {
19421
19505
  // @ts-ignore
19422
- version: "4.4.0",
19506
+ version: "4.4.2",
19423
19507
  options: Object.assign({}, DEFAULT_OPTIONS),
19424
19508
  schema: null,
19425
19509
  get currentUserId() {
@@ -19447,6 +19531,19 @@
19447
19531
  invites: getInvitesObservable(dexie),
19448
19532
  roles: getGlobalRolesObservable(dexie),
19449
19533
  configure(options) {
19534
+ // Validate maxStringLength — Infinity disables offloading, otherwise must be
19535
+ // a finite number between 100 and the server limit (32768).
19536
+ // Minimum 100 prevents accidental offloading of primary keys and short strings
19537
+ // that would break sync.
19538
+ const MIN_STRING_LENGTH = 100;
19539
+ const MAX_SERVER_STRING_LENGTH = 32768;
19540
+ if (options.maxStringLength !== undefined &&
19541
+ options.maxStringLength !== Infinity &&
19542
+ (!Number.isFinite(options.maxStringLength) ||
19543
+ options.maxStringLength < MIN_STRING_LENGTH ||
19544
+ options.maxStringLength > MAX_SERVER_STRING_LENGTH)) {
19545
+ throw new Error(`maxStringLength must be Infinity or a finite number in [${MIN_STRING_LENGTH}, ${MAX_SERVER_STRING_LENGTH}]. Got: ${options.maxStringLength}`);
19546
+ }
19450
19547
  options = dexie.cloud.options = Object.assign(Object.assign({}, dexie.cloud.options), options);
19451
19548
  configuredProgramatically = true;
19452
19549
  if (options.databaseUrl && options.nameSuffix) {
@@ -19833,7 +19930,7 @@
19833
19930
  }
19834
19931
  }
19835
19932
  // @ts-ignore
19836
- dexieCloud.version = "4.4.0";
19933
+ dexieCloud.version = "4.4.2";
19837
19934
  Dexie.Cloud = dexieCloud;
19838
19935
 
19839
19936
  exports.default = dexieCloud;
Binary file