dexie-cloud-addon 4.2.0-alpha.4 → 4.2.0-alpha.6

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.
@@ -1,4 +1,3 @@
1
- import { type DexieCloudDB } from "../db/DexieCloudDB";
2
- export declare function getAwarenessLibrary(db: DexieCloudDB): typeof import('y-protocols/awareness');
3
- export declare const awarenessWeakMap: WeakMap<any, import("y-protocols/awareness").Awareness>;
4
- export declare const getDocAwareness: (doc: any) => import("y-protocols/awareness").Awareness | undefined;
1
+ import type { Awareness } from 'y-protocols/awareness';
2
+ export declare const awarenessWeakMap: WeakMap<any, Awareness>;
3
+ export declare const getDocAwareness: (doc: any) => Awareness | undefined;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.2.0-alpha.4, Fri Aug 01 2025
11
+ * Version 4.2.0-alpha.6, Fri Aug 01 2025
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -558,7 +558,7 @@
558
558
  * @param {Encoder} encoder
559
559
  * @return {number}
560
560
  */
561
- const length$1 = encoder => {
561
+ const length$2 = encoder => {
562
562
  let len = encoder.cpos;
563
563
  for (let i = 0; i < encoder.bufs.length; i++) {
564
564
  len += encoder.bufs[i].length;
@@ -574,7 +574,7 @@
574
574
  * @return {Uint8Array} The created ArrayBuffer.
575
575
  */
576
576
  const toUint8Array$1 = encoder => {
577
- const uint8arr = new Uint8Array(length$1(encoder));
577
+ const uint8arr = new Uint8Array(length$2(encoder));
578
578
  let curPos = 0;
579
579
  for (let i = 0; i < encoder.bufs.length; i++) {
580
580
  const d = encoder.bufs[i];
@@ -3747,6 +3747,78 @@
3747
3747
  this._observers = create$5();
3748
3748
  }
3749
3749
  }
3750
+
3751
+ /* c8 ignore start */
3752
+ /**
3753
+ * Handles named events.
3754
+ *
3755
+ * @deprecated
3756
+ * @template N
3757
+ */
3758
+ class Observable {
3759
+ constructor () {
3760
+ /**
3761
+ * Some desc.
3762
+ * @type {Map<N, any>}
3763
+ */
3764
+ this._observers = create$5();
3765
+ }
3766
+
3767
+ /**
3768
+ * @param {N} name
3769
+ * @param {function} f
3770
+ */
3771
+ on (name, f) {
3772
+ setIfUndefined(this._observers, name, create$4).add(f);
3773
+ }
3774
+
3775
+ /**
3776
+ * @param {N} name
3777
+ * @param {function} f
3778
+ */
3779
+ once (name, f) {
3780
+ /**
3781
+ * @param {...any} args
3782
+ */
3783
+ const _f = (...args) => {
3784
+ this.off(name, _f);
3785
+ f(...args);
3786
+ };
3787
+ this.on(name, _f);
3788
+ }
3789
+
3790
+ /**
3791
+ * @param {N} name
3792
+ * @param {function} f
3793
+ */
3794
+ off (name, f) {
3795
+ const observers = this._observers.get(name);
3796
+ if (observers !== undefined) {
3797
+ observers.delete(f);
3798
+ if (observers.size === 0) {
3799
+ this._observers.delete(name);
3800
+ }
3801
+ }
3802
+ }
3803
+
3804
+ /**
3805
+ * Emit a named event. All registered event listeners that listen to the
3806
+ * specified name will receive the event.
3807
+ *
3808
+ * @todo This should catch exceptions
3809
+ *
3810
+ * @param {N} name The event name.
3811
+ * @param {Array<any>} args The arguments that are applied to the event listener.
3812
+ */
3813
+ emit (name, args) {
3814
+ // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
3815
+ return from((this._observers.get(name) || create$5()).values()).forEach(f => f(...args))
3816
+ }
3817
+
3818
+ destroy () {
3819
+ this._observers = create$5();
3820
+ }
3821
+ }
3750
3822
  /* c8 ignore end */
3751
3823
 
3752
3824
  /**
@@ -3943,7 +4015,7 @@
3943
4015
  * @param {Encoder} encoder
3944
4016
  * @return {number}
3945
4017
  */
3946
- const length = encoder => {
4018
+ const length$1 = encoder => {
3947
4019
  let len = encoder.cpos;
3948
4020
  for (let i = 0; i < encoder.bufs.length; i++) {
3949
4021
  len += encoder.bufs[i].length;
@@ -3959,7 +4031,7 @@
3959
4031
  * @return {Uint8Array} The created ArrayBuffer.
3960
4032
  */
3961
4033
  const toUint8Array = encoder => {
3962
- const uint8arr = new Uint8Array(length(encoder));
4034
+ const uint8arr = new Uint8Array(length$1(encoder));
3963
4035
  let curPos = 0;
3964
4036
  for (let i = 0; i < encoder.bufs.length; i++) {
3965
4037
  const d = encoder.bufs[i];
@@ -5017,6 +5089,20 @@
5017
5089
  (c ^ uint32() & 15 >> c / 4).toString(16)
5018
5090
  );
5019
5091
 
5092
+ /**
5093
+ * Utility module to work with time.
5094
+ *
5095
+ * @module time
5096
+ */
5097
+
5098
+
5099
+ /**
5100
+ * Return current unix time.
5101
+ *
5102
+ * @return {number}
5103
+ */
5104
+ const getUnixTime = Date.now;
5105
+
5020
5106
  /**
5021
5107
  * Utility helpers to work with promises.
5022
5108
  *
@@ -5143,6 +5229,13 @@
5143
5229
  }
5144
5230
  };
5145
5231
 
5232
+ /**
5233
+ * @deprecated use object.size instead
5234
+ * @param {Object<string,any>} obj
5235
+ * @return {number}
5236
+ */
5237
+ const length = obj => keys(obj).length;
5238
+
5146
5239
  /**
5147
5240
  * @param {Object<string,any>} obj
5148
5241
  * @return {number}
@@ -5239,6 +5332,96 @@
5239
5332
  }
5240
5333
  };
5241
5334
 
5335
+ /**
5336
+ * @template T
5337
+ *
5338
+ * @param {T} a
5339
+ * @param {T} b
5340
+ * @return {boolean}
5341
+ */
5342
+ const equalityStrict = (a, b) => a === b;
5343
+
5344
+ /* c8 ignore start */
5345
+
5346
+ /**
5347
+ * @param {any} a
5348
+ * @param {any} b
5349
+ * @return {boolean}
5350
+ */
5351
+ const equalityDeep = (a, b) => {
5352
+ if (a == null || b == null) {
5353
+ return equalityStrict(a, b)
5354
+ }
5355
+ if (a.constructor !== b.constructor) {
5356
+ return false
5357
+ }
5358
+ if (a === b) {
5359
+ return true
5360
+ }
5361
+ switch (a.constructor) {
5362
+ case ArrayBuffer:
5363
+ a = new Uint8Array(a);
5364
+ b = new Uint8Array(b);
5365
+ // eslint-disable-next-line no-fallthrough
5366
+ case Uint8Array: {
5367
+ if (a.byteLength !== b.byteLength) {
5368
+ return false
5369
+ }
5370
+ for (let i = 0; i < a.length; i++) {
5371
+ if (a[i] !== b[i]) {
5372
+ return false
5373
+ }
5374
+ }
5375
+ break
5376
+ }
5377
+ case Set: {
5378
+ if (a.size !== b.size) {
5379
+ return false
5380
+ }
5381
+ for (const value of a) {
5382
+ if (!b.has(value)) {
5383
+ return false
5384
+ }
5385
+ }
5386
+ break
5387
+ }
5388
+ case Map: {
5389
+ if (a.size !== b.size) {
5390
+ return false
5391
+ }
5392
+ for (const key of a.keys()) {
5393
+ if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
5394
+ return false
5395
+ }
5396
+ }
5397
+ break
5398
+ }
5399
+ case Object:
5400
+ if (length(a) !== length(b)) {
5401
+ return false
5402
+ }
5403
+ for (const key in a) {
5404
+ if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
5405
+ return false
5406
+ }
5407
+ }
5408
+ break
5409
+ case Array:
5410
+ if (a.length !== b.length) {
5411
+ return false
5412
+ }
5413
+ for (let i = 0; i < a.length; i++) {
5414
+ if (!equalityDeep(a[i], b[i])) {
5415
+ return false
5416
+ }
5417
+ }
5418
+ break
5419
+ default:
5420
+ return false
5421
+ }
5422
+ return true
5423
+ };
5424
+
5242
5425
  /**
5243
5426
  * @template V
5244
5427
  * @template {V} OPTS
@@ -14010,7 +14193,7 @@
14010
14193
  *
14011
14194
  * ==========================================================================
14012
14195
  *
14013
- * Version 4.2.0-alpha.3, Fri Aug 01 2025
14196
+ * Version 4.2.0-alpha.6, Fri Aug 01 2025
14014
14197
  *
14015
14198
  * https://dexie.org
14016
14199
  *
@@ -16189,16 +16372,271 @@
16189
16372
  rxjs.mergeMap((messages) => messages));
16190
16373
  }
16191
16374
 
16192
- function getAwarenessLibrary(db) {
16193
- var _a, _b;
16194
- if (!((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.awarenessProtocol)) {
16195
- throw new Dexie.MissingAPIError('awarenessProtocol was not provided to db.cloud.configure(). Please import * as awarenessProtocol from "y-protocols/awareness".');
16196
- }
16197
- return (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.awarenessProtocol;
16198
- }
16199
16375
  const awarenessWeakMap = new WeakMap();
16200
16376
  const getDocAwareness = (doc) => awarenessWeakMap.get(doc);
16201
16377
 
16378
+ /**
16379
+ * @module awareness-protocol
16380
+ */
16381
+
16382
+
16383
+ const outdatedTimeout = 30000;
16384
+
16385
+ /**
16386
+ * @typedef {Object} MetaClientState
16387
+ * @property {number} MetaClientState.clock
16388
+ * @property {number} MetaClientState.lastUpdated unix timestamp
16389
+ */
16390
+
16391
+ /**
16392
+ * The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
16393
+ * (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
16394
+ * remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
16395
+ *
16396
+ * Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
16397
+ * its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
16398
+ * applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
16399
+ * a remote client is offline, it may propagate a message with
16400
+ * `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
16401
+ * message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
16402
+ *
16403
+ * Before a client disconnects, it should propagate a `null` state with an updated clock.
16404
+ *
16405
+ * Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
16406
+ *
16407
+ * @extends {Observable<string>}
16408
+ */
16409
+ class Awareness extends Observable {
16410
+ /**
16411
+ * @param {Y.Doc} doc
16412
+ */
16413
+ constructor (doc) {
16414
+ super();
16415
+ this.doc = doc;
16416
+ /**
16417
+ * @type {number}
16418
+ */
16419
+ this.clientID = doc.clientID;
16420
+ /**
16421
+ * Maps from client id to client state
16422
+ * @type {Map<number, Object<string, any>>}
16423
+ */
16424
+ this.states = new Map();
16425
+ /**
16426
+ * @type {Map<number, MetaClientState>}
16427
+ */
16428
+ this.meta = new Map();
16429
+ this._checkInterval = /** @type {any} */ (setInterval(() => {
16430
+ const now = getUnixTime();
16431
+ if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
16432
+ // renew local clock
16433
+ this.setLocalState(this.getLocalState());
16434
+ }
16435
+ /**
16436
+ * @type {Array<number>}
16437
+ */
16438
+ const remove = [];
16439
+ this.meta.forEach((meta, clientid) => {
16440
+ if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
16441
+ remove.push(clientid);
16442
+ }
16443
+ });
16444
+ if (remove.length > 0) {
16445
+ removeAwarenessStates(this, remove, 'timeout');
16446
+ }
16447
+ }, floor(outdatedTimeout / 10)));
16448
+ doc.on('destroy', () => {
16449
+ this.destroy();
16450
+ });
16451
+ this.setLocalState({});
16452
+ }
16453
+
16454
+ destroy () {
16455
+ this.emit('destroy', [this]);
16456
+ this.setLocalState(null);
16457
+ super.destroy();
16458
+ clearInterval(this._checkInterval);
16459
+ }
16460
+
16461
+ /**
16462
+ * @return {Object<string,any>|null}
16463
+ */
16464
+ getLocalState () {
16465
+ return this.states.get(this.clientID) || null
16466
+ }
16467
+
16468
+ /**
16469
+ * @param {Object<string,any>|null} state
16470
+ */
16471
+ setLocalState (state) {
16472
+ const clientID = this.clientID;
16473
+ const currLocalMeta = this.meta.get(clientID);
16474
+ const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1;
16475
+ const prevState = this.states.get(clientID);
16476
+ if (state === null) {
16477
+ this.states.delete(clientID);
16478
+ } else {
16479
+ this.states.set(clientID, state);
16480
+ }
16481
+ this.meta.set(clientID, {
16482
+ clock,
16483
+ lastUpdated: getUnixTime()
16484
+ });
16485
+ const added = [];
16486
+ const updated = [];
16487
+ const filteredUpdated = [];
16488
+ const removed = [];
16489
+ if (state === null) {
16490
+ removed.push(clientID);
16491
+ } else if (prevState == null) {
16492
+ if (state != null) {
16493
+ added.push(clientID);
16494
+ }
16495
+ } else {
16496
+ updated.push(clientID);
16497
+ if (!equalityDeep(prevState, state)) {
16498
+ filteredUpdated.push(clientID);
16499
+ }
16500
+ }
16501
+ if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
16502
+ this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']);
16503
+ }
16504
+ this.emit('update', [{ added, updated, removed }, 'local']);
16505
+ }
16506
+
16507
+ /**
16508
+ * @param {string} field
16509
+ * @param {any} value
16510
+ */
16511
+ setLocalStateField (field, value) {
16512
+ const state = this.getLocalState();
16513
+ if (state !== null) {
16514
+ this.setLocalState({
16515
+ ...state,
16516
+ [field]: value
16517
+ });
16518
+ }
16519
+ }
16520
+
16521
+ /**
16522
+ * @return {Map<number,Object<string,any>>}
16523
+ */
16524
+ getStates () {
16525
+ return this.states
16526
+ }
16527
+ }
16528
+
16529
+ /**
16530
+ * Mark (remote) clients as inactive and remove them from the list of active peers.
16531
+ * This change will be propagated to remote clients.
16532
+ *
16533
+ * @param {Awareness} awareness
16534
+ * @param {Array<number>} clients
16535
+ * @param {any} origin
16536
+ */
16537
+ const removeAwarenessStates = (awareness, clients, origin) => {
16538
+ const removed = [];
16539
+ for (let i = 0; i < clients.length; i++) {
16540
+ const clientID = clients[i];
16541
+ if (awareness.states.has(clientID)) {
16542
+ awareness.states.delete(clientID);
16543
+ if (clientID === awareness.clientID) {
16544
+ const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID));
16545
+ awareness.meta.set(clientID, {
16546
+ clock: curMeta.clock + 1,
16547
+ lastUpdated: getUnixTime()
16548
+ });
16549
+ }
16550
+ removed.push(clientID);
16551
+ }
16552
+ }
16553
+ if (removed.length > 0) {
16554
+ awareness.emit('change', [{ added: [], updated: [], removed }, origin]);
16555
+ awareness.emit('update', [{ added: [], updated: [], removed }, origin]);
16556
+ }
16557
+ };
16558
+
16559
+ /**
16560
+ * @param {Awareness} awareness
16561
+ * @param {Array<number>} clients
16562
+ * @return {Uint8Array}
16563
+ */
16564
+ const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
16565
+ const len = clients.length;
16566
+ const encoder = createEncoder();
16567
+ writeVarUint(encoder, len);
16568
+ for (let i = 0; i < len; i++) {
16569
+ const clientID = clients[i];
16570
+ const state = states.get(clientID) || null;
16571
+ const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock;
16572
+ writeVarUint(encoder, clientID);
16573
+ writeVarUint(encoder, clock);
16574
+ writeVarString(encoder, JSON.stringify(state));
16575
+ }
16576
+ return toUint8Array(encoder)
16577
+ };
16578
+
16579
+ /**
16580
+ * @param {Awareness} awareness
16581
+ * @param {Uint8Array} update
16582
+ * @param {any} origin This will be added to the emitted change event
16583
+ */
16584
+ const applyAwarenessUpdate = (awareness, update, origin) => {
16585
+ const decoder = createDecoder(update);
16586
+ const timestamp = getUnixTime();
16587
+ const added = [];
16588
+ const updated = [];
16589
+ const filteredUpdated = [];
16590
+ const removed = [];
16591
+ const len = readVarUint(decoder);
16592
+ for (let i = 0; i < len; i++) {
16593
+ const clientID = readVarUint(decoder);
16594
+ let clock = readVarUint(decoder);
16595
+ const state = JSON.parse(readVarString(decoder));
16596
+ const clientMeta = awareness.meta.get(clientID);
16597
+ const prevState = awareness.states.get(clientID);
16598
+ const currClock = clientMeta === undefined ? 0 : clientMeta.clock;
16599
+ if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) {
16600
+ if (state === null) {
16601
+ // never let a remote client remove this local state
16602
+ if (clientID === awareness.clientID && awareness.getLocalState() != null) {
16603
+ // remote client removed the local state. Do not remote state. Broadcast a message indicating
16604
+ // that this client still exists by increasing the clock
16605
+ clock++;
16606
+ } else {
16607
+ awareness.states.delete(clientID);
16608
+ }
16609
+ } else {
16610
+ awareness.states.set(clientID, state);
16611
+ }
16612
+ awareness.meta.set(clientID, {
16613
+ clock,
16614
+ lastUpdated: timestamp
16615
+ });
16616
+ if (clientMeta === undefined && state !== null) {
16617
+ added.push(clientID);
16618
+ } else if (clientMeta !== undefined && state === null) {
16619
+ removed.push(clientID);
16620
+ } else if (state !== null) {
16621
+ if (!equalityDeep(state, prevState)) {
16622
+ filteredUpdated.push(clientID);
16623
+ }
16624
+ updated.push(clientID);
16625
+ }
16626
+ }
16627
+ }
16628
+ if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
16629
+ awareness.emit('change', [{
16630
+ added, updated: filteredUpdated, removed
16631
+ }, origin]);
16632
+ }
16633
+ if (added.length > 0 || updated.length > 0 || removed.length > 0) {
16634
+ awareness.emit('update', [{
16635
+ added, updated, removed
16636
+ }, origin]);
16637
+ }
16638
+ };
16639
+
16202
16640
  const wm = new WeakMap();
16203
16641
  /** A property (package-private) on Y.Doc that is used
16204
16642
  * to signal that the server wants us to send a 'doc-open' message
@@ -16394,8 +16832,7 @@
16394
16832
  if (doc) {
16395
16833
  const awareness = getDocAwareness(doc);
16396
16834
  if (awareness) {
16397
- const awap = getAwarenessLibrary(this.db);
16398
- awap.applyAwarenessUpdate(awareness, msg.u, 'server');
16835
+ applyAwarenessUpdate(awareness, msg.u, 'server');
16399
16836
  }
16400
16837
  }
16401
16838
  }
@@ -17406,15 +17843,14 @@
17406
17843
  }
17407
17844
  function createAwareness(db, doc, provider) {
17408
17845
  const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
17409
- const awap = getAwarenessLibrary(db);
17410
- const awareness = new awap.Awareness(doc);
17846
+ const awareness = new Awareness(doc);
17411
17847
  const reopenDocSignal = getOpenDocSignal(doc);
17412
17848
  awareness.on('update', ({ added, updated, removed }, origin) => {
17413
17849
  // Send the update
17414
17850
  const changedClients = added.concat(updated).concat(removed);
17415
17851
  const user = db.cloud.currentUser.value;
17416
17852
  if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
17417
- const update = awap.encodeAwarenessUpdate(awareness, changedClients);
17853
+ const update = encodeAwarenessUpdate(awareness, changedClients);
17418
17854
  db.messageProducer.next({
17419
17855
  type: 'aware',
17420
17856
  table: parentTable,
@@ -17440,7 +17876,7 @@
17440
17876
  awareness.on('destroy', () => {
17441
17877
  // Signal to server that this provider is destroyed (the update event will be triggered, which
17442
17878
  // in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
17443
- awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
17879
+ removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
17444
17880
  });
17445
17881
  // Open the document on the server
17446
17882
  (() => __awaiter(this, void 0, void 0, function* () {
@@ -17744,7 +18180,7 @@
17744
18180
  const syncComplete = new rxjs.Subject();
17745
18181
  dexie.cloud = {
17746
18182
  // @ts-ignore
17747
- version: "4.2.0-alpha.4",
18183
+ version: "4.2.0-alpha.6",
17748
18184
  options: Object.assign({}, DEFAULT_OPTIONS),
17749
18185
  schema: null,
17750
18186
  get currentUserId() {
@@ -18061,7 +18497,7 @@
18061
18497
  }
18062
18498
  }
18063
18499
  // @ts-ignore
18064
- dexieCloud.version = "4.2.0-alpha.4";
18500
+ dexieCloud.version = "4.2.0-alpha.6";
18065
18501
  Dexie.Cloud = dexieCloud;
18066
18502
 
18067
18503
  exports.default = dexieCloud;