dexie-cloud-addon 4.1.0-beta.41 → 4.1.0-beta.43

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.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.1.0-beta.41, Wed Jan 29 2025
11
+ * Version 4.1.0-beta.43, Fri Feb 07 2025
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -3035,19 +3035,25 @@ function triggerSync(db, purpose) {
3035
3035
  }
3036
3036
  }
3037
3037
 
3038
+ const hasArrayBufferB64 = "fromBase64" in Uint8Array; // https://github.com/tc39/proposal-arraybuffer-base64;
3038
3039
  const b64decode = typeof Buffer !== "undefined"
3039
- ? (base64) => Buffer.from(base64, "base64")
3040
- : (base64) => {
3041
- const binary_string = atob(base64);
3042
- const len = binary_string.length;
3043
- const bytes = new Uint8Array(len);
3044
- for (var i = 0; i < len; i++) {
3045
- bytes[i] = binary_string.charCodeAt(i);
3046
- }
3047
- return bytes;
3048
- };
3040
+ ? (base64) => Buffer.from(base64, "base64") // Node
3041
+ : hasArrayBufferB64
3042
+ ? // @ts-ignore: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64
3043
+ (base64) => Uint8Array.fromBase64(base64) // Modern javascript standard
3044
+ : (base64) => {
3045
+ // Legacy DOM workaround
3046
+ const binary_string = atob(base64);
3047
+ const len = binary_string.length;
3048
+ const bytes = new Uint8Array(len);
3049
+ for (var i = 0; i < len; i++) {
3050
+ bytes[i] = binary_string.charCodeAt(i);
3051
+ }
3052
+ return bytes;
3053
+ };
3049
3054
  const b64encode = typeof Buffer !== "undefined"
3050
3055
  ? (b) => {
3056
+ // Node
3051
3057
  if (ArrayBuffer.isView(b)) {
3052
3058
  return Buffer.from(b.buffer, b.byteOffset, b.byteLength).toString("base64");
3053
3059
  }
@@ -3055,16 +3061,20 @@ const b64encode = typeof Buffer !== "undefined"
3055
3061
  return Buffer.from(b).toString("base64");
3056
3062
  }
3057
3063
  }
3058
- : (b) => {
3059
- const u8a = ArrayBuffer.isView(b) ? b : new Uint8Array(b);
3060
- const CHUNK_SIZE = 0x1000;
3061
- const strs = [];
3062
- for (let i = 0, l = u8a.length; i < l; i += CHUNK_SIZE) {
3063
- const chunk = u8a.subarray(i, i + CHUNK_SIZE);
3064
- strs.push(String.fromCharCode.apply(null, chunk));
3065
- }
3066
- return btoa(strs.join(""));
3067
- };
3064
+ : hasArrayBufferB64
3065
+ ? // @ts-ignore https://github.com/tc39/proposal-arraybuffer-base64
3066
+ (b) => b.toBase64() // Modern Javascript standard
3067
+ : (b) => {
3068
+ // Legacy DOM workaround
3069
+ const u8a = ArrayBuffer.isView(b) ? b : new Uint8Array(b);
3070
+ const CHUNK_SIZE = 0x1000;
3071
+ const strs = [];
3072
+ for (let i = 0, l = u8a.length; i < l; i += CHUNK_SIZE) {
3073
+ const chunk = u8a.subarray(i, i + CHUNK_SIZE);
3074
+ strs.push(String.fromCharCode.apply(null, chunk));
3075
+ }
3076
+ return btoa(strs.join(""));
3077
+ };
3068
3078
 
3069
3079
  class TokenErrorResponseError extends Error {
3070
3080
  constructor({ title, message, messageCode, messageParams, }) {
@@ -4551,6 +4561,26 @@ var undefinedDef = {
4551
4561
  },
4552
4562
  };
4553
4563
 
4564
+ var FileDef = {
4565
+ File: {
4566
+ test: (file, toStringTag) => toStringTag === "File",
4567
+ replace: (file) => ({
4568
+ $t: "File",
4569
+ v: b64encode(string2ArrayBuffer(readBlobSync(file))),
4570
+ type: file.type,
4571
+ name: file.name,
4572
+ lastModified: new Date(file.lastModified).toISOString(),
4573
+ }),
4574
+ revive: ({ type, v, name, lastModified }) => {
4575
+ const ab = b64decode(v);
4576
+ return new File([ab], name, {
4577
+ type,
4578
+ lastModified: new Date(lastModified).getTime(),
4579
+ });
4580
+ },
4581
+ },
4582
+ };
4583
+
4554
4584
  // Since server revisions are stored in bigints, we need to handle clients without
4555
4585
  // bigint support to not fail when serverRevision is passed over to client.
4556
4586
  // We need to not fail when reviving it and we need to somehow store the information.
@@ -4586,7 +4616,7 @@ const bigIntDef = hasBigIntSupport
4586
4616
  revive: ({ v }) => new FakeBigInt(v),
4587
4617
  },
4588
4618
  };
4589
- const defs = Object.assign(Object.assign(Object.assign({}, undefinedDef), bigIntDef), { PropModification: {
4619
+ const defs = Object.assign(Object.assign(Object.assign(Object.assign({}, undefinedDef), bigIntDef), FileDef), { PropModification: {
4590
4620
  test: (val) => val instanceof PropModification,
4591
4621
  replace: (propModification) => {
4592
4622
  return Object.assign({ $t: 'PropModification' }, propModification['@@propmod']);
@@ -4961,7 +4991,7 @@ function listUpdatesSince(yTable, sinceIncluding) {
4961
4991
  .toArray();
4962
4992
  }
4963
4993
 
4964
- function $Y$1(db) {
4994
+ function $Y(db) {
4965
4995
  const $Y = db.dx._options.Y;
4966
4996
  if (!$Y)
4967
4997
  throw new Error('Y library not supplied to Dexie constructor');
@@ -4991,7 +5021,7 @@ function listYClientMessagesAndStateVector(db, tablesToSync) {
4991
5021
  for (const table of tablesToSync) {
4992
5022
  if (table.schema.yProps) {
4993
5023
  for (const yProp of table.schema.yProps) {
4994
- const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
5024
+ const Y = $Y(db); // This is how we retrieve the user-provided Y library
4995
5025
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
4996
5026
  const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
4997
5027
  // unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
@@ -8106,7 +8136,7 @@ function createAwareness(db, doc, provider) {
8106
8136
  if (provider.destroyed || currentFlowId !== myFlow || !connected)
8107
8137
  return;
8108
8138
  if (serverUpdatesSinceLastSync.length > 0) {
8109
- const Y = $Y$1(db); // Get the Yjs library from Dexie constructor options
8139
+ const Y = $Y(db); // Get the Yjs library from Dexie constructor options
8110
8140
  const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
8111
8141
  const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
8112
8142
  docOpenMsg.sv = stateVector;
@@ -8126,8 +8156,68 @@ function getTiedObjectId(realmId) {
8126
8156
  }
8127
8157
 
8128
8158
  const ydocTriggers = {};
8129
- const docIsAlreadyHooked = new WeakSet();
8130
8159
  const middlewares = new WeakMap();
8160
+ const txRunner = TriggerRunner("tx");
8161
+ const unloadRunner = TriggerRunner("unload");
8162
+ function TriggerRunner(name) {
8163
+ let triggerExecPromise = null;
8164
+ let triggerScheduled = false;
8165
+ let registry = new Map();
8166
+ function execute(registryCopy) {
8167
+ return __awaiter(this, void 0, void 0, function* () {
8168
+ for (const { db, parentId, triggers, parentTable, prop, } of registryCopy.values()) {
8169
+ const yDoc = DexieYProvider.getOrCreateDocument(db, parentTable, prop, parentId);
8170
+ try {
8171
+ DexieYProvider.load(yDoc); // If doc is open, this would just be a ++refount
8172
+ yield yDoc.whenLoaded; // If doc is loaded, this would resolve immediately
8173
+ for (const trigger of triggers) {
8174
+ yield trigger(yDoc, parentId);
8175
+ }
8176
+ }
8177
+ catch (error) {
8178
+ console.error(`Error in YDocTrigger ${error}`);
8179
+ }
8180
+ finally {
8181
+ DexieYProvider.release(yDoc);
8182
+ }
8183
+ }
8184
+ });
8185
+ }
8186
+ return {
8187
+ name,
8188
+ run() {
8189
+ return __awaiter(this, void 0, void 0, function* () {
8190
+ if (!triggerScheduled && registry.size > 0) {
8191
+ triggerScheduled = true;
8192
+ if (triggerExecPromise)
8193
+ yield triggerExecPromise.catch(() => { });
8194
+ setTimeout(() => {
8195
+ // setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
8196
+ triggerScheduled = false;
8197
+ const registryCopy = registry;
8198
+ registry = new Map();
8199
+ triggerExecPromise = execute(registryCopy).finally(() => (triggerExecPromise = null));
8200
+ }, 0);
8201
+ }
8202
+ });
8203
+ },
8204
+ enqueue(db, parentTable, parentId, prop, trigger) {
8205
+ const key = `${db.name}:${parentTable}:${parentId}:${prop}`;
8206
+ let entry = registry.get(key);
8207
+ if (!entry) {
8208
+ entry = {
8209
+ db,
8210
+ parentTable,
8211
+ parentId,
8212
+ prop,
8213
+ triggers: new Set(),
8214
+ };
8215
+ registry.set(key, entry);
8216
+ }
8217
+ entry.triggers.add(trigger);
8218
+ },
8219
+ };
8220
+ }
8131
8221
  const createMiddleware = (db) => ({
8132
8222
  stack: 'dbcore',
8133
8223
  level: 10,
@@ -8135,15 +8225,20 @@ const createMiddleware = (db) => ({
8135
8225
  create: (down) => {
8136
8226
  return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
8137
8227
  const idbtrans = down.transaction(stores, mode, options);
8228
+ if (mode === 'readonly')
8229
+ return idbtrans;
8230
+ if (!stores.some((store) => ydocTriggers[store]))
8231
+ return idbtrans;
8138
8232
  idbtrans.addEventListener('complete', onTransactionCommitted);
8139
8233
  return idbtrans;
8140
- }, table: (tblName) => {
8141
- const coreTable = down.table(tblName);
8142
- const triggerSpec = ydocTriggers[tblName];
8234
+ }, table: (updatesTable) => {
8235
+ const coreTable = down.table(updatesTable);
8236
+ const triggerSpec = ydocTriggers[updatesTable];
8143
8237
  if (!triggerSpec)
8144
8238
  return coreTable;
8145
8239
  const { trigger, parentTable, prop } = triggerSpec;
8146
8240
  return Object.assign(Object.assign({}, coreTable), { mutate(req) {
8241
+ var _a;
8147
8242
  switch (req.type) {
8148
8243
  case 'add': {
8149
8244
  for (const yUpdateRow of req.values) {
@@ -8151,15 +8246,10 @@ const createMiddleware = (db) => ({
8151
8246
  continue; // A syncer or garbage collection state does not point to a key
8152
8247
  const primaryKey = yUpdateRow.k;
8153
8248
  const doc = DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
8154
- if (doc) {
8155
- if (!docIsAlreadyHooked.has(doc)) {
8156
- hookToDoc(doc, primaryKey, trigger);
8157
- docIsAlreadyHooked.add(doc);
8158
- }
8159
- }
8160
- else {
8161
- enqueueTrigger(db, tblName, primaryKey, trigger);
8162
- }
8249
+ const runner = doc && ((_a = DexieYProvider.for(doc)) === null || _a === void 0 ? void 0 : _a.refCount)
8250
+ ? unloadRunner // Document is open. Wait with trigger until it's closed.
8251
+ : txRunner; // Document is closed. Run trigger immediately after transaction commits.
8252
+ runner.enqueue(db, parentTable, primaryKey, prop, trigger);
8163
8253
  }
8164
8254
  break;
8165
8255
  }
@@ -8182,7 +8272,7 @@ const createMiddleware = (db) => ({
8182
8272
  keySet.addKey(k);
8183
8273
  }
8184
8274
  for (const interval of keySet) {
8185
- enqueueTrigger(db, tblName, interval.from, trigger);
8275
+ txRunner.enqueue(db, parentTable, interval.from, prop, trigger);
8186
8276
  }
8187
8277
  });
8188
8278
  }
@@ -8193,74 +8283,11 @@ const createMiddleware = (db) => ({
8193
8283
  } });
8194
8284
  },
8195
8285
  });
8196
- let triggerExecPromise = null;
8197
- let triggerScheduled = false;
8198
- let scheduledTriggers = [];
8199
- function $Y(db) {
8200
- const $Y = db._options.Y;
8201
- if (!$Y)
8202
- throw new Error('Y library not supplied to Dexie constructor');
8203
- return $Y;
8204
- }
8205
- function executeTriggers(triggersToRun) {
8206
- return __awaiter(this, void 0, void 0, function* () {
8207
- for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
8208
- // Load entire document into an Y.Doc instance:
8209
- const updates = yield db
8210
- .table(updatesTable)
8211
- .where({ k: parentId })
8212
- .toArray();
8213
- const Y = $Y(db);
8214
- const yDoc = new Y.Doc();
8215
- for (const update of updates) {
8216
- Y.applyUpdateV2(yDoc, update.u);
8217
- }
8218
- try {
8219
- yield trigger(yDoc, parentId);
8220
- }
8221
- catch (error) {
8222
- console.error(`Error in YDocTrigger ${error}`);
8223
- }
8224
- }
8225
- });
8226
- }
8227
- function enqueueTrigger(db, updatesTable, parentId, trigger) {
8228
- scheduledTriggers.push({
8229
- db,
8230
- updatesTable,
8231
- parentId,
8232
- trigger,
8233
- });
8234
- }
8235
8286
  function onTransactionCommitted() {
8236
- return __awaiter(this, void 0, void 0, function* () {
8237
- if (!triggerScheduled && scheduledTriggers.length > 0) {
8238
- triggerScheduled = true;
8239
- if (triggerExecPromise)
8240
- yield triggerExecPromise.catch(() => { });
8241
- setTimeout(() => {
8242
- // setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
8243
- triggerScheduled = false;
8244
- const triggersToRun = scheduledTriggers;
8245
- scheduledTriggers = [];
8246
- triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
8247
- }, 0);
8248
- }
8249
- });
8287
+ txRunner.run();
8250
8288
  }
8251
- function hookToDoc(doc, parentId, trigger) {
8252
- // From now on, keep listening to doc updates and execute the trigger when it happens there instead
8253
- doc.on('updateV2', (update, origin) => {
8254
- //Dexie.ignoreTransaction(()=>{
8255
- trigger(doc, parentId);
8256
- //});
8257
- });
8258
- /*
8259
- NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
8260
- doc.on('destroy', ()=>{
8261
- docIsAlreadyHooked.delete(doc);
8262
- })
8263
- */
8289
+ function beforeProviderUnload() {
8290
+ unloadRunner.run();
8264
8291
  }
8265
8292
  function defineYDocTrigger(table, prop, trigger) {
8266
8293
  var _a, _b;
@@ -8279,6 +8306,9 @@ function defineYDocTrigger(table, prop, trigger) {
8279
8306
  middlewares.set(db, mw);
8280
8307
  }
8281
8308
  db.use(mw);
8309
+ {
8310
+ DexieYProvider.on('beforeunload', beforeProviderUnload);
8311
+ }
8282
8312
  }
8283
8313
 
8284
8314
  const DEFAULT_OPTIONS = {
@@ -8321,7 +8351,7 @@ function dexieCloud(dexie) {
8321
8351
  const syncComplete = new Subject();
8322
8352
  dexie.cloud = {
8323
8353
  // @ts-ignore
8324
- version: "4.1.0-beta.41",
8354
+ version: "4.1.0-beta.43",
8325
8355
  options: Object.assign({}, DEFAULT_OPTIONS),
8326
8356
  schema: null,
8327
8357
  get currentUserId() {
@@ -8639,7 +8669,7 @@ function dexieCloud(dexie) {
8639
8669
  }
8640
8670
  }
8641
8671
  // @ts-ignore
8642
- dexieCloud.version = "4.1.0-beta.41";
8672
+ dexieCloud.version = "4.1.0-beta.43";
8643
8673
  Dexie.Cloud = dexieCloud;
8644
8674
 
8645
8675
  export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };