dexie-cloud-addon 4.1.0-beta.42 → 4.1.0-beta.44

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.42, Tue Feb 04 2025
11
+ * Version 4.1.0-beta.44, Fri Feb 14 2025
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -3677,8 +3677,6 @@ function prodLog(level, ...args) {
3677
3677
  */
3678
3678
  function setCurrentUser(db, user) {
3679
3679
  return __awaiter(this, void 0, void 0, function* () {
3680
- if (user.userId === db.cloud.currentUserId)
3681
- return; // Already this user.
3682
3680
  const $logins = db.table('$logins');
3683
3681
  yield db.transaction('rw', $logins, (tx) => __awaiter(this, void 0, void 0, function* () {
3684
3682
  const existingLogins = yield $logins.toArray();
@@ -4991,7 +4989,7 @@ function listUpdatesSince(yTable, sinceIncluding) {
4991
4989
  .toArray();
4992
4990
  }
4993
4991
 
4994
- function $Y$1(db) {
4992
+ function $Y(db) {
4995
4993
  const $Y = db.dx._options.Y;
4996
4994
  if (!$Y)
4997
4995
  throw new Error('Y library not supplied to Dexie constructor');
@@ -5021,7 +5019,7 @@ function listYClientMessagesAndStateVector(db, tablesToSync) {
5021
5019
  for (const table of tablesToSync) {
5022
5020
  if (table.schema.yProps) {
5023
5021
  for (const yProp of table.schema.yProps) {
5024
- const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
5022
+ const Y = $Y(db); // This is how we retrieve the user-provided Y library
5025
5023
  const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
5026
5024
  const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
5027
5025
  // unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
@@ -6158,8 +6156,14 @@ function createIdGenerationMiddleware(db) {
6158
6156
  }
6159
6157
  return Object.assign(Object.assign({}, table), { mutate: (req) => {
6160
6158
  var _a, _b;
6161
- // @ts-ignore
6162
- if (req.trans.disableChangeTracking) {
6159
+ const idbtrans = req.trans;
6160
+ if (idbtrans.mode === 'versionchange') {
6161
+ // Tell all the other middlewares to skip bothering. We're in versionchange mode.
6162
+ // dexie-cloud is not initialized yet.
6163
+ idbtrans.disableChangeTracking = true;
6164
+ idbtrans.disableAccessControl = true;
6165
+ }
6166
+ if (idbtrans.disableChangeTracking) {
6163
6167
  // Disable ID policy checks and ID generation
6164
6168
  return table.mutate(req);
6165
6169
  }
@@ -6216,17 +6220,13 @@ function createImplicitPropSetterMiddleware(db) {
6216
6220
  return Object.assign(Object.assign({}, core), { table: (tableName) => {
6217
6221
  const table = core.table(tableName);
6218
6222
  return Object.assign(Object.assign({}, table), { mutate: (req) => {
6219
- var _a, _b, _c, _d;
6220
- // @ts-ignore
6221
- if (req.trans.disableChangeTracking) {
6223
+ var _a, _b, _c, _d, _e, _f;
6224
+ const trans = req.trans;
6225
+ if (trans.disableChangeTracking) {
6222
6226
  return table.mutate(req);
6223
6227
  }
6224
- const trans = req.trans;
6225
- if ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[tableName]) === null || _b === void 0 ? void 0 : _b.markedForSync) {
6226
- if (trans.mode === 'versionchange') {
6227
- // Don't mutate tables marked for sync in versionchange transactions.
6228
- return Promise.reject(new Dexie.UpgradeError(`Dexie Cloud Addon: Cannot upgrade or populate synced table "${tableName}". See https://dexie.org/cloud/docs/best-practices`));
6229
- }
6228
+ const currentUserId = (_b = (_a = trans.currentUser) === null || _a === void 0 ? void 0 : _a.userId) !== null && _b !== void 0 ? _b : UNAUTHORIZED_USER.userId;
6229
+ if ((_d = (_c = db.cloud.schema) === null || _c === void 0 ? void 0 : _c[tableName]) === null || _d === void 0 ? void 0 : _d.markedForSync) {
6230
6230
  if (req.type === 'add' || req.type === 'put') {
6231
6231
  if (tableName === 'members') {
6232
6232
  for (const member of req.values) {
@@ -6250,12 +6250,12 @@ function createImplicitPropSetterMiddleware(db) {
6250
6250
  // and expect them to be returned. That scenario must work also when db.cloud.currentUserId === 'unauthorized'.
6251
6251
  for (const obj of req.values) {
6252
6252
  if (!obj.owner) {
6253
- obj.owner = trans.currentUser.userId;
6253
+ obj.owner = currentUserId;
6254
6254
  }
6255
6255
  if (!obj.realmId) {
6256
- obj.realmId = trans.currentUser.userId;
6256
+ obj.realmId = currentUserId;
6257
6257
  }
6258
- const key = (_d = (_c = table.schema.primaryKey).extractKey) === null || _d === void 0 ? void 0 : _d.call(_c, obj);
6258
+ const key = (_f = (_e = table.schema.primaryKey).extractKey) === null || _f === void 0 ? void 0 : _f.call(_e, obj);
6259
6259
  if (typeof key === 'string' && key[0] === '#') {
6260
6260
  // Add $ts prop for put operations and
6261
6261
  // disable update operations as well as consistent
@@ -6351,16 +6351,14 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6351
6351
  name: 'MutationTrackingMiddleware',
6352
6352
  level: 1,
6353
6353
  create: (core) => {
6354
+ const allTableNames = new Set(core.schema.tables.map((t) => t.name));
6354
6355
  const ordinaryTables = core.schema.tables.filter((t) => !/^\$/.test(t.name));
6355
- let mutTableMap;
6356
- try {
6357
- mutTableMap = new Map(ordinaryTables.map((tbl) => [
6358
- tbl.name,
6359
- core.table(`$${tbl.name}_mutations`),
6360
- ]));
6361
- }
6362
- catch (_a) {
6363
- throwVersionIncrementNeeded();
6356
+ const mutTableMap = new Map();
6357
+ for (const tbl of ordinaryTables) {
6358
+ const mutationTableName = `$${tbl.name}_mutations`;
6359
+ if (allTableNames.has(mutationTableName)) {
6360
+ mutTableMap.set(tbl.name, core.table(mutationTableName));
6361
+ }
6364
6362
  }
6365
6363
  return Object.assign(Object.assign({}, core), { transaction: (tables, mode) => {
6366
6364
  let tx;
@@ -6438,6 +6436,11 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
6438
6436
  }
6439
6437
  const { schema } = table;
6440
6438
  const mutsTable = mutTableMap.get(tableName);
6439
+ if (!mutsTable) {
6440
+ // We cannot track mutations on this table because there is no mutations table for it.
6441
+ // This might happen in upgraders that executes before cloud schema is applied.
6442
+ return table;
6443
+ }
6441
6444
  return guardedTable(Object.assign(Object.assign({}, table), { mutate: (req) => {
6442
6445
  var _a, _b, _c;
6443
6446
  const trans = req.trans;
@@ -8136,7 +8139,7 @@ function createAwareness(db, doc, provider) {
8136
8139
  if (provider.destroyed || currentFlowId !== myFlow || !connected)
8137
8140
  return;
8138
8141
  if (serverUpdatesSinceLastSync.length > 0) {
8139
- 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
8140
8143
  const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
8141
8144
  const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
8142
8145
  docOpenMsg.sv = stateVector;
@@ -8156,8 +8159,68 @@ function getTiedObjectId(realmId) {
8156
8159
  }
8157
8160
 
8158
8161
  const ydocTriggers = {};
8159
- const docIsAlreadyHooked = new WeakSet();
8160
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 = DexieYProvider.getOrCreateDocument(db, parentTable, prop, parentId);
8173
+ try {
8174
+ 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
+ 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
+ }
8161
8224
  const createMiddleware = (db) => ({
8162
8225
  stack: 'dbcore',
8163
8226
  level: 10,
@@ -8165,15 +8228,20 @@ const createMiddleware = (db) => ({
8165
8228
  create: (down) => {
8166
8229
  return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
8167
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;
8168
8235
  idbtrans.addEventListener('complete', onTransactionCommitted);
8169
8236
  return idbtrans;
8170
- }, table: (tblName) => {
8171
- const coreTable = down.table(tblName);
8172
- const triggerSpec = ydocTriggers[tblName];
8237
+ }, table: (updatesTable) => {
8238
+ const coreTable = down.table(updatesTable);
8239
+ const triggerSpec = ydocTriggers[updatesTable];
8173
8240
  if (!triggerSpec)
8174
8241
  return coreTable;
8175
8242
  const { trigger, parentTable, prop } = triggerSpec;
8176
8243
  return Object.assign(Object.assign({}, coreTable), { mutate(req) {
8244
+ var _a;
8177
8245
  switch (req.type) {
8178
8246
  case 'add': {
8179
8247
  for (const yUpdateRow of req.values) {
@@ -8181,15 +8249,10 @@ const createMiddleware = (db) => ({
8181
8249
  continue; // A syncer or garbage collection state does not point to a key
8182
8250
  const primaryKey = yUpdateRow.k;
8183
8251
  const doc = DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
8184
- if (doc) {
8185
- if (!docIsAlreadyHooked.has(doc)) {
8186
- hookToDoc(doc, primaryKey, trigger);
8187
- docIsAlreadyHooked.add(doc);
8188
- }
8189
- }
8190
- else {
8191
- enqueueTrigger(db, tblName, primaryKey, trigger);
8192
- }
8252
+ const runner = doc && ((_a = 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);
8193
8256
  }
8194
8257
  break;
8195
8258
  }
@@ -8212,7 +8275,7 @@ const createMiddleware = (db) => ({
8212
8275
  keySet.addKey(k);
8213
8276
  }
8214
8277
  for (const interval of keySet) {
8215
- enqueueTrigger(db, tblName, interval.from, trigger);
8278
+ txRunner.enqueue(db, parentTable, interval.from, prop, trigger);
8216
8279
  }
8217
8280
  });
8218
8281
  }
@@ -8223,74 +8286,11 @@ const createMiddleware = (db) => ({
8223
8286
  } });
8224
8287
  },
8225
8288
  });
8226
- let triggerExecPromise = null;
8227
- let triggerScheduled = false;
8228
- let scheduledTriggers = [];
8229
- function $Y(db) {
8230
- const $Y = db._options.Y;
8231
- if (!$Y)
8232
- throw new Error('Y library not supplied to Dexie constructor');
8233
- return $Y;
8234
- }
8235
- function executeTriggers(triggersToRun) {
8236
- return __awaiter(this, void 0, void 0, function* () {
8237
- for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
8238
- // Load entire document into an Y.Doc instance:
8239
- const updates = yield db
8240
- .table(updatesTable)
8241
- .where({ k: parentId })
8242
- .toArray();
8243
- const Y = $Y(db);
8244
- const yDoc = new Y.Doc();
8245
- for (const update of updates) {
8246
- Y.applyUpdateV2(yDoc, update.u);
8247
- }
8248
- try {
8249
- yield trigger(yDoc, parentId);
8250
- }
8251
- catch (error) {
8252
- console.error(`Error in YDocTrigger ${error}`);
8253
- }
8254
- }
8255
- });
8256
- }
8257
- function enqueueTrigger(db, updatesTable, parentId, trigger) {
8258
- scheduledTriggers.push({
8259
- db,
8260
- updatesTable,
8261
- parentId,
8262
- trigger,
8263
- });
8264
- }
8265
8289
  function onTransactionCommitted() {
8266
- return __awaiter(this, void 0, void 0, function* () {
8267
- if (!triggerScheduled && scheduledTriggers.length > 0) {
8268
- triggerScheduled = true;
8269
- if (triggerExecPromise)
8270
- yield triggerExecPromise.catch(() => { });
8271
- setTimeout(() => {
8272
- // setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
8273
- triggerScheduled = false;
8274
- const triggersToRun = scheduledTriggers;
8275
- scheduledTriggers = [];
8276
- triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
8277
- }, 0);
8278
- }
8279
- });
8290
+ txRunner.run();
8280
8291
  }
8281
- function hookToDoc(doc, parentId, trigger) {
8282
- // From now on, keep listening to doc updates and execute the trigger when it happens there instead
8283
- doc.on('updateV2', (update, origin) => {
8284
- //Dexie.ignoreTransaction(()=>{
8285
- trigger(doc, parentId);
8286
- //});
8287
- });
8288
- /*
8289
- NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
8290
- doc.on('destroy', ()=>{
8291
- docIsAlreadyHooked.delete(doc);
8292
- })
8293
- */
8292
+ function beforeProviderUnload() {
8293
+ unloadRunner.run();
8294
8294
  }
8295
8295
  function defineYDocTrigger(table, prop, trigger) {
8296
8296
  var _a, _b;
@@ -8309,6 +8309,9 @@ function defineYDocTrigger(table, prop, trigger) {
8309
8309
  middlewares.set(db, mw);
8310
8310
  }
8311
8311
  db.use(mw);
8312
+ {
8313
+ DexieYProvider.on('beforeunload', beforeProviderUnload);
8314
+ }
8312
8315
  }
8313
8316
 
8314
8317
  const DEFAULT_OPTIONS = {
@@ -8351,7 +8354,7 @@ function dexieCloud(dexie) {
8351
8354
  const syncComplete = new Subject();
8352
8355
  dexie.cloud = {
8353
8356
  // @ts-ignore
8354
- version: "4.1.0-beta.42",
8357
+ version: "4.1.0-beta.44",
8355
8358
  options: Object.assign({}, DEFAULT_OPTIONS),
8356
8359
  schema: null,
8357
8360
  get currentUserId() {
@@ -8669,7 +8672,7 @@ function dexieCloud(dexie) {
8669
8672
  }
8670
8673
  }
8671
8674
  // @ts-ignore
8672
- dexieCloud.version = "4.1.0-beta.42";
8675
+ dexieCloud.version = "4.1.0-beta.44";
8673
8676
  Dexie.Cloud = dexieCloud;
8674
8677
 
8675
8678
  export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };