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