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.
@@ -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
  *
@@ -930,7 +930,7 @@
930
930
  * @param {Encoder} encoder
931
931
  * @return {number}
932
932
  */
933
- const length$1 = encoder => {
933
+ const length$2 = encoder => {
934
934
  let len = encoder.cpos;
935
935
  for (let i = 0; i < encoder.bufs.length; i++) {
936
936
  len += encoder.bufs[i].length;
@@ -946,7 +946,7 @@
946
946
  * @return {Uint8Array} The created ArrayBuffer.
947
947
  */
948
948
  const toUint8Array$1 = encoder => {
949
- const uint8arr = new Uint8Array(length$1(encoder));
949
+ const uint8arr = new Uint8Array(length$2(encoder));
950
950
  let curPos = 0;
951
951
  for (let i = 0; i < encoder.bufs.length; i++) {
952
952
  const d = encoder.bufs[i];
@@ -3443,6 +3443,78 @@
3443
3443
  this._observers = create$5();
3444
3444
  }
3445
3445
  }
3446
+
3447
+ /* c8 ignore start */
3448
+ /**
3449
+ * Handles named events.
3450
+ *
3451
+ * @deprecated
3452
+ * @template N
3453
+ */
3454
+ class Observable {
3455
+ constructor () {
3456
+ /**
3457
+ * Some desc.
3458
+ * @type {Map<N, any>}
3459
+ */
3460
+ this._observers = create$5();
3461
+ }
3462
+
3463
+ /**
3464
+ * @param {N} name
3465
+ * @param {function} f
3466
+ */
3467
+ on (name, f) {
3468
+ setIfUndefined(this._observers, name, create$4).add(f);
3469
+ }
3470
+
3471
+ /**
3472
+ * @param {N} name
3473
+ * @param {function} f
3474
+ */
3475
+ once (name, f) {
3476
+ /**
3477
+ * @param {...any} args
3478
+ */
3479
+ const _f = (...args) => {
3480
+ this.off(name, _f);
3481
+ f(...args);
3482
+ };
3483
+ this.on(name, _f);
3484
+ }
3485
+
3486
+ /**
3487
+ * @param {N} name
3488
+ * @param {function} f
3489
+ */
3490
+ off (name, f) {
3491
+ const observers = this._observers.get(name);
3492
+ if (observers !== undefined) {
3493
+ observers.delete(f);
3494
+ if (observers.size === 0) {
3495
+ this._observers.delete(name);
3496
+ }
3497
+ }
3498
+ }
3499
+
3500
+ /**
3501
+ * Emit a named event. All registered event listeners that listen to the
3502
+ * specified name will receive the event.
3503
+ *
3504
+ * @todo This should catch exceptions
3505
+ *
3506
+ * @param {N} name The event name.
3507
+ * @param {Array<any>} args The arguments that are applied to the event listener.
3508
+ */
3509
+ emit (name, args) {
3510
+ // 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.
3511
+ return from((this._observers.get(name) || create$5()).values()).forEach(f => f(...args))
3512
+ }
3513
+
3514
+ destroy () {
3515
+ this._observers = create$5();
3516
+ }
3517
+ }
3446
3518
  /* c8 ignore end */
3447
3519
 
3448
3520
  /**
@@ -3639,7 +3711,7 @@
3639
3711
  * @param {Encoder} encoder
3640
3712
  * @return {number}
3641
3713
  */
3642
- const length = encoder => {
3714
+ const length$1 = encoder => {
3643
3715
  let len = encoder.cpos;
3644
3716
  for (let i = 0; i < encoder.bufs.length; i++) {
3645
3717
  len += encoder.bufs[i].length;
@@ -3655,7 +3727,7 @@
3655
3727
  * @return {Uint8Array} The created ArrayBuffer.
3656
3728
  */
3657
3729
  const toUint8Array = encoder => {
3658
- const uint8arr = new Uint8Array(length(encoder));
3730
+ const uint8arr = new Uint8Array(length$1(encoder));
3659
3731
  let curPos = 0;
3660
3732
  for (let i = 0; i < encoder.bufs.length; i++) {
3661
3733
  const d = encoder.bufs[i];
@@ -4713,6 +4785,20 @@
4713
4785
  (c ^ uint32() & 15 >> c / 4).toString(16)
4714
4786
  );
4715
4787
 
4788
+ /**
4789
+ * Utility module to work with time.
4790
+ *
4791
+ * @module time
4792
+ */
4793
+
4794
+
4795
+ /**
4796
+ * Return current unix time.
4797
+ *
4798
+ * @return {number}
4799
+ */
4800
+ const getUnixTime = Date.now;
4801
+
4716
4802
  /**
4717
4803
  * Utility helpers to work with promises.
4718
4804
  *
@@ -4839,6 +4925,13 @@
4839
4925
  }
4840
4926
  };
4841
4927
 
4928
+ /**
4929
+ * @deprecated use object.size instead
4930
+ * @param {Object<string,any>} obj
4931
+ * @return {number}
4932
+ */
4933
+ const length = obj => keys(obj).length;
4934
+
4842
4935
  /**
4843
4936
  * @param {Object<string,any>} obj
4844
4937
  * @return {number}
@@ -4935,6 +5028,96 @@
4935
5028
  }
4936
5029
  };
4937
5030
 
5031
+ /**
5032
+ * @template T
5033
+ *
5034
+ * @param {T} a
5035
+ * @param {T} b
5036
+ * @return {boolean}
5037
+ */
5038
+ const equalityStrict = (a, b) => a === b;
5039
+
5040
+ /* c8 ignore start */
5041
+
5042
+ /**
5043
+ * @param {any} a
5044
+ * @param {any} b
5045
+ * @return {boolean}
5046
+ */
5047
+ const equalityDeep = (a, b) => {
5048
+ if (a == null || b == null) {
5049
+ return equalityStrict(a, b)
5050
+ }
5051
+ if (a.constructor !== b.constructor) {
5052
+ return false
5053
+ }
5054
+ if (a === b) {
5055
+ return true
5056
+ }
5057
+ switch (a.constructor) {
5058
+ case ArrayBuffer:
5059
+ a = new Uint8Array(a);
5060
+ b = new Uint8Array(b);
5061
+ // eslint-disable-next-line no-fallthrough
5062
+ case Uint8Array: {
5063
+ if (a.byteLength !== b.byteLength) {
5064
+ return false
5065
+ }
5066
+ for (let i = 0; i < a.length; i++) {
5067
+ if (a[i] !== b[i]) {
5068
+ return false
5069
+ }
5070
+ }
5071
+ break
5072
+ }
5073
+ case Set: {
5074
+ if (a.size !== b.size) {
5075
+ return false
5076
+ }
5077
+ for (const value of a) {
5078
+ if (!b.has(value)) {
5079
+ return false
5080
+ }
5081
+ }
5082
+ break
5083
+ }
5084
+ case Map: {
5085
+ if (a.size !== b.size) {
5086
+ return false
5087
+ }
5088
+ for (const key of a.keys()) {
5089
+ if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
5090
+ return false
5091
+ }
5092
+ }
5093
+ break
5094
+ }
5095
+ case Object:
5096
+ if (length(a) !== length(b)) {
5097
+ return false
5098
+ }
5099
+ for (const key in a) {
5100
+ if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
5101
+ return false
5102
+ }
5103
+ }
5104
+ break
5105
+ case Array:
5106
+ if (a.length !== b.length) {
5107
+ return false
5108
+ }
5109
+ for (let i = 0; i < a.length; i++) {
5110
+ if (!equalityDeep(a[i], b[i])) {
5111
+ return false
5112
+ }
5113
+ }
5114
+ break
5115
+ default:
5116
+ return false
5117
+ }
5118
+ return true
5119
+ };
5120
+
4938
5121
  /**
4939
5122
  * @template V
4940
5123
  * @template {V} OPTS
@@ -13706,7 +13889,7 @@
13706
13889
  *
13707
13890
  * ==========================================================================
13708
13891
  *
13709
- * Version 4.2.0-alpha.3, Fri Aug 01 2025
13892
+ * Version 4.2.0-alpha.6, Fri Aug 01 2025
13710
13893
  *
13711
13894
  * https://dexie.org
13712
13895
  *
@@ -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* () {
@@ -17573,7 +18009,7 @@
17573
18009
  const syncComplete = new rxjs.Subject();
17574
18010
  dexie.cloud = {
17575
18011
  // @ts-ignore
17576
- version: "4.2.0-alpha.4",
18012
+ version: "4.2.0-alpha.6",
17577
18013
  options: Object.assign({}, DEFAULT_OPTIONS),
17578
18014
  schema: null,
17579
18015
  get currentUserId() {
@@ -17890,7 +18326,7 @@
17890
18326
  }
17891
18327
  }
17892
18328
  // @ts-ignore
17893
- dexieCloud.version = "4.2.0-alpha.4";
18329
+ dexieCloud.version = "4.2.0-alpha.6";
17894
18330
  Dexie.Cloud = dexieCloud;
17895
18331
 
17896
18332
  // In case the SW lives for a while, let it reuse already opened connections: