matrix-js-sdk 37.2.0 → 37.3.0-rc.0

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.
Files changed (83) hide show
  1. package/git-revision.txt +1 -1
  2. package/lib/client.d.ts +8 -2
  3. package/lib/client.d.ts.map +1 -1
  4. package/lib/client.js +11 -3
  5. package/lib/client.js.map +1 -1
  6. package/lib/common-crypto/CryptoBackend.d.ts +8 -0
  7. package/lib/common-crypto/CryptoBackend.d.ts.map +1 -1
  8. package/lib/common-crypto/CryptoBackend.js.map +1 -1
  9. package/lib/http-api/errors.d.ts +8 -0
  10. package/lib/http-api/errors.d.ts.map +1 -1
  11. package/lib/http-api/errors.js +14 -0
  12. package/lib/http-api/errors.js.map +1 -1
  13. package/lib/http-api/fetch.d.ts.map +1 -1
  14. package/lib/http-api/fetch.js +3 -3
  15. package/lib/http-api/fetch.js.map +1 -1
  16. package/lib/matrixrtc/LegacyMembershipManager.d.ts +9 -1
  17. package/lib/matrixrtc/LegacyMembershipManager.d.ts.map +1 -1
  18. package/lib/matrixrtc/LegacyMembershipManager.js +20 -0
  19. package/lib/matrixrtc/LegacyMembershipManager.js.map +1 -1
  20. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
  21. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  22. package/lib/matrixrtc/NewMembershipManager.d.ts +7 -58
  23. package/lib/matrixrtc/NewMembershipManager.d.ts.map +1 -1
  24. package/lib/matrixrtc/NewMembershipManager.js +130 -137
  25. package/lib/matrixrtc/NewMembershipManager.js.map +1 -1
  26. package/lib/matrixrtc/NewMembershipManagerActionScheduler.js +3 -3
  27. package/lib/matrixrtc/NewMembershipManagerActionScheduler.js.map +1 -1
  28. package/lib/matrixrtc/index.d.ts +1 -0
  29. package/lib/matrixrtc/index.d.ts.map +1 -1
  30. package/lib/matrixrtc/index.js +1 -0
  31. package/lib/matrixrtc/index.js.map +1 -1
  32. package/lib/matrixrtc/types.d.ts +77 -1
  33. package/lib/matrixrtc/types.d.ts.map +1 -1
  34. package/lib/matrixrtc/types.js +40 -1
  35. package/lib/matrixrtc/types.js.map +1 -1
  36. package/lib/models/room-summary.d.ts +30 -0
  37. package/lib/models/room-summary.d.ts.map +1 -1
  38. package/lib/models/room-summary.js +11 -0
  39. package/lib/models/room-summary.js.map +1 -1
  40. package/lib/models/room.d.ts +37 -1
  41. package/lib/models/room.d.ts.map +1 -1
  42. package/lib/models/room.js +133 -28
  43. package/lib/models/room.js.map +1 -1
  44. package/lib/oidc/tokenRefresher.d.ts.map +1 -1
  45. package/lib/oidc/tokenRefresher.js +8 -1
  46. package/lib/oidc/tokenRefresher.js.map +1 -1
  47. package/lib/pushprocessor.d.ts +10 -1
  48. package/lib/pushprocessor.d.ts.map +1 -1
  49. package/lib/pushprocessor.js +24 -9
  50. package/lib/pushprocessor.js.map +1 -1
  51. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map +1 -1
  52. package/lib/rust-crypto/OutgoingRequestProcessor.js +6 -1
  53. package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -1
  54. package/lib/rust-crypto/rust-crypto.d.ts +4 -1
  55. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
  56. package/lib/rust-crypto/rust-crypto.js +55 -46
  57. package/lib/rust-crypto/rust-crypto.js.map +1 -1
  58. package/lib/sliding-sync-sdk.d.ts.map +1 -1
  59. package/lib/sliding-sync-sdk.js +86 -75
  60. package/lib/sliding-sync-sdk.js.map +1 -1
  61. package/lib/sliding-sync.d.ts +23 -60
  62. package/lib/sliding-sync.d.ts.map +1 -1
  63. package/lib/sliding-sync.js +62 -321
  64. package/lib/sliding-sync.js.map +1 -1
  65. package/package.json +6 -6
  66. package/src/client.ts +12 -5
  67. package/src/common-crypto/CryptoBackend.ts +9 -0
  68. package/src/http-api/errors.ts +14 -0
  69. package/src/http-api/fetch.ts +3 -4
  70. package/src/matrixrtc/LegacyMembershipManager.ts +27 -1
  71. package/src/matrixrtc/MatrixRTCSession.ts +2 -1
  72. package/src/matrixrtc/NewMembershipManager.ts +120 -165
  73. package/src/matrixrtc/NewMembershipManagerActionScheduler.ts +3 -3
  74. package/src/matrixrtc/index.ts +1 -0
  75. package/src/matrixrtc/types.ts +84 -1
  76. package/src/models/room-summary.ts +31 -0
  77. package/src/models/room.ts +133 -23
  78. package/src/oidc/tokenRefresher.ts +8 -2
  79. package/src/pushprocessor.ts +25 -16
  80. package/src/rust-crypto/OutgoingRequestProcessor.ts +6 -0
  81. package/src/rust-crypto/rust-crypto.ts +7 -1
  82. package/src/sliding-sync-sdk.ts +28 -28
  83. package/src/sliding-sync.ts +38 -329
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2022 The Matrix.org Foundation C.I.C.
2
+ Copyright 2022-2024 The Matrix.org Foundation C.I.C.
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ import { logger } from "./logger.ts";
18
18
  import { type MatrixClient } from "./client.ts";
19
19
  import { type IRoomEvent, type IStateEvent } from "./sync-accumulator.ts";
20
20
  import { TypedEventEmitter } from "./models/typed-event-emitter.ts";
21
- import { sleep, type IDeferred, defer } from "./utils.ts";
21
+ import { sleep } from "./utils.ts";
22
22
  import { type HTTPError } from "./http-api/index.ts";
23
23
 
24
24
  // /sync requests allow you to set a timeout= but the request may continue
@@ -82,10 +82,23 @@ export interface MSC3575SlidingSyncRequest {
82
82
  clientTimeout?: number;
83
83
  }
84
84
 
85
+ /**
86
+ * New format of hero introduced in MSC4186 with display name and avatar URL
87
+ * in addition to just user_id (as it is on the wire, with underscores)
88
+ * as opposed to Hero in room-summary.ts which has fields in camelCase
89
+ * (and also a flag to note what format the hero came from).
90
+ */
91
+ export interface MSC4186Hero {
92
+ user_id: string;
93
+ displayname?: string;
94
+ avatar_url?: string;
95
+ }
96
+
85
97
  export interface MSC3575RoomData {
86
98
  name: string;
87
99
  required_state: IStateEvent[];
88
100
  timeline: (IRoomEvent | IStateEvent)[];
101
+ heroes?: MSC4186Hero[];
89
102
  notification_count?: number;
90
103
  highlight_count?: number;
91
104
  joined_count?: number;
@@ -96,41 +109,13 @@ export interface MSC3575RoomData {
96
109
  is_dm?: boolean;
97
110
  prev_batch?: string;
98
111
  num_live?: number;
112
+ bump_stamp?: number;
99
113
  }
100
114
 
101
115
  interface ListResponse {
102
116
  count: number;
103
- ops: Operation[];
104
- }
105
-
106
- interface BaseOperation {
107
- op: string;
108
- }
109
-
110
- interface DeleteOperation extends BaseOperation {
111
- op: "DELETE";
112
- index: number;
113
- }
114
-
115
- interface InsertOperation extends BaseOperation {
116
- op: "INSERT";
117
- index: number;
118
- room_id: string;
119
- }
120
-
121
- interface InvalidateOperation extends BaseOperation {
122
- op: "INVALIDATE";
123
- range: [number, number];
124
- }
125
-
126
- interface SyncOperation extends BaseOperation {
127
- op: "SYNC";
128
- range: [number, number];
129
- room_ids: string[];
130
117
  }
131
118
 
132
- type Operation = DeleteOperation | InsertOperation | InvalidateOperation | SyncOperation;
133
-
134
119
  /**
135
120
  * A complete Sliding Sync response
136
121
  */
@@ -163,7 +148,6 @@ class SlidingList {
163
148
  private isModified?: boolean;
164
149
 
165
150
  // returned data
166
- public roomIndexToRoomId: Record<number, string> = {};
167
151
  public joinedCount = 0;
168
152
 
169
153
  /**
@@ -204,9 +188,6 @@ class SlidingList {
204
188
  // reset values as the join count may be very different (if filters changed) including the rooms
205
189
  // (e.g. sort orders or sliding window ranges changed)
206
190
 
207
- // the constantly changing sliding window ranges. Not an array for performance reasons
208
- // E.g. tracking ranges 0-99, 500-599, we don't want to have a 600 element array
209
- this.roomIndexToRoomId = {};
210
191
  // the total number of joined rooms according to the server, always >= len(roomIndexToRoomId)
211
192
  this.joinedCount = 0;
212
193
  }
@@ -226,26 +207,6 @@ class SlidingList {
226
207
  }
227
208
  return list;
228
209
  }
229
-
230
- /**
231
- * Check if a given index is within the list range. This is required even though the /sync API
232
- * provides explicit updates with index positions because of the following situation:
233
- * 0 1 2 3 4 5 6 7 8 indexes
234
- * a b c d e f COMMANDS: SYNC 0 2 a b c; SYNC 6 8 d e f;
235
- * a b c d _ f COMMAND: DELETE 7;
236
- * e a b c d f COMMAND: INSERT 0 e;
237
- * c=3 is wrong as we are not tracking it, ergo we need to see if `i` is in range else drop it
238
- * @param i - The index to check
239
- * @returns True if the index is within a sliding window
240
- */
241
- public isIndexInRange(i: number): boolean {
242
- for (const r of this.list.ranges) {
243
- if (r[0] <= i && i <= r[1]) {
244
- return true;
245
- }
246
- }
247
- return false;
248
- }
249
210
  }
250
211
 
251
212
  /**
@@ -274,10 +235,10 @@ export interface Extension<Req extends object, Res extends object> {
274
235
  /**
275
236
  * A function which is called when the request JSON is being formed.
276
237
  * Returns the data to insert under this key.
277
- * @param isInitial - True when this is part of the initial request (send sticky params)
238
+ * @param isInitial - True when this is part of the initial request.
278
239
  * @returns The request JSON to send.
279
240
  */
280
- onRequest(isInitial: boolean): Req | undefined;
241
+ onRequest(isInitial: boolean): Promise<Req>;
281
242
  /**
282
243
  * A function which is called when there is response JSON under this extension.
283
244
  * @param data - The response JSON under the extension name.
@@ -295,12 +256,10 @@ export interface Extension<Req extends object, Res extends object> {
295
256
  * of information when processing sync responses.
296
257
  * - RoomData: concerns rooms, useful for SlidingSyncSdk to update its knowledge of rooms.
297
258
  * - Lifecycle: concerns callbacks at various well-defined points in the sync process.
298
- * - List: concerns lists, useful for UI layers to re-render room lists.
299
259
  * Specifically, the order of event invocation is:
300
260
  * - Lifecycle (state=RequestFinished)
301
261
  * - RoomData (N times)
302
262
  * - Lifecycle (state=Complete)
303
- * - List (at most once per list)
304
263
  */
305
264
  export enum SlidingSyncEvent {
306
265
  /**
@@ -313,16 +272,9 @@ export enum SlidingSyncEvent {
313
272
  * - SlidingSyncState.RequestFinished: Fires after we receive a valid response but before the
314
273
  * response has been processed. Perform any pre-process steps here. If there was a problem syncing,
315
274
  * `err` will be set (e.g network errors).
316
- * - SlidingSyncState.Complete: Fires after all SlidingSyncEvent.RoomData have been fired but before
317
- * SlidingSyncEvent.List.
275
+ * - SlidingSyncState.Complete: Fires after the response has been processed.
318
276
  */
319
277
  Lifecycle = "SlidingSync.Lifecycle",
320
- /**
321
- * This event fires whenever there has been a change to this list index. It fires exactly once
322
- * per list, even if there were multiple operations for the list.
323
- * It fires AFTER Lifecycle and RoomData events.
324
- */
325
- List = "SlidingSync.List",
326
278
  }
327
279
 
328
280
  export type SlidingSyncEventHandlerMap = {
@@ -332,7 +284,6 @@ export type SlidingSyncEventHandlerMap = {
332
284
  resp: MSC3575SlidingSyncResponse | null,
333
285
  err?: Error,
334
286
  ) => void;
335
- [SlidingSyncEvent.List]: (listKey: string, joinedCount: number, roomIndexToRoomId: Record<number, string>) => void;
336
287
  };
337
288
 
338
289
  /**
@@ -347,11 +298,6 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
347
298
  private terminated = false;
348
299
  // flag set when resend() is called because we cannot rely on detecting AbortError in JS SDK :(
349
300
  private needsResend = false;
350
- // the txn_id to send with the next request.
351
- private txnId: string | null = null;
352
- // a list (in chronological order of when they were sent) of objects containing the txn ID and
353
- // a defer to resolve/reject depending on whether they were successfully sent or not.
354
- private txnIdDefers: (IDeferred<string> & { txnId: string })[] = [];
355
301
  // map of extension name to req/resp handler
356
302
  private extensions: Record<string, Extension<any, any>> = {};
357
303
 
@@ -426,14 +372,13 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
426
372
  * @param key - The list key
427
373
  * @returns The list data which contains the rooms in this list
428
374
  */
429
- public getListData(key: string): { joinedCount: number; roomIndexToRoomId: Record<number, string> } | null {
375
+ public getListData(key: string): { joinedCount: number } | null {
430
376
  const data = this.lists.get(key);
431
377
  if (!data) {
432
378
  return null;
433
379
  }
434
380
  return {
435
381
  joinedCount: data.joinedCount,
436
- roomIndexToRoomId: Object.assign({}, data.roomIndexToRoomId),
437
382
  };
438
383
  }
439
384
 
@@ -461,13 +406,13 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
461
406
  * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled
462
407
  * immediately after sending, in which case the action will be applied in the subsequent request)
463
408
  */
464
- public setListRanges(key: string, ranges: number[][]): Promise<string> {
409
+ public setListRanges(key: string, ranges: number[][]): void {
465
410
  const list = this.lists.get(key);
466
411
  if (!list) {
467
- return Promise.reject(new Error("no list with key " + key));
412
+ throw new Error("no list with key " + key);
468
413
  }
469
414
  list.updateListRange(ranges);
470
- return this.resend();
415
+ this.resend();
471
416
  }
472
417
 
473
418
  /**
@@ -479,7 +424,7 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
479
424
  * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled
480
425
  * immediately after sending, in which case the action will be applied in the subsequent request)
481
426
  */
482
- public setList(key: string, list: MSC3575List): Promise<string> {
427
+ public setList(key: string, list: MSC3575List): void {
483
428
  const existingList = this.lists.get(key);
484
429
  if (existingList) {
485
430
  existingList.replaceList(list);
@@ -488,7 +433,7 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
488
433
  this.lists.set(key, new SlidingList(list));
489
434
  }
490
435
  this.listModifiedCount += 1;
491
- return this.resend();
436
+ this.resend();
492
437
  }
493
438
 
494
439
  /**
@@ -504,27 +449,21 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
504
449
  * /sync request to resend new subscriptions. If the /sync stream has not started, this will
505
450
  * prepare the room subscriptions for when start() is called.
506
451
  * @param s - The new desired room subscriptions.
507
- * @returns A promise which resolves to the transaction ID when it has been received down sync
508
- * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled
509
- * immediately after sending, in which case the action will be applied in the subsequent request)
510
452
  */
511
- public modifyRoomSubscriptions(s: Set<string>): Promise<string> {
453
+ public modifyRoomSubscriptions(s: Set<string>): void {
512
454
  this.desiredRoomSubscriptions = s;
513
- return this.resend();
455
+ this.resend();
514
456
  }
515
457
 
516
458
  /**
517
459
  * Modify which events to retrieve for room subscriptions. Invalidates all room subscriptions
518
460
  * such that they will be sent up afresh.
519
461
  * @param rs - The new room subscription fields to fetch.
520
- * @returns A promise which resolves to the transaction ID when it has been received down sync
521
- * (or rejects with the transaction ID if the action was not applied e.g the request was cancelled
522
- * immediately after sending, in which case the action will be applied in the subsequent request)
523
462
  */
524
- public modifyRoomSubscriptionInfo(rs: MSC3575RoomSubscription): Promise<string> {
463
+ public modifyRoomSubscriptionInfo(rs: MSC3575RoomSubscription): void {
525
464
  this.roomSubscriptionInfo = rs;
526
465
  this.confirmedRoomSubscriptions = new Set<string>();
527
- return this.resend();
466
+ this.resend();
528
467
  }
529
468
 
530
469
  /**
@@ -538,11 +477,11 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
538
477
  this.extensions[ext.name()] = ext;
539
478
  }
540
479
 
541
- private getExtensionRequest(isInitial: boolean): Record<string, object | undefined> {
480
+ private async getExtensionRequest(isInitial: boolean): Promise<Record<string, object | undefined>> {
542
481
  const ext: Record<string, object | undefined> = {};
543
- Object.keys(this.extensions).forEach((extName) => {
544
- ext[extName] = this.extensions[extName].onRequest(isInitial);
545
- });
482
+ for (const extName in this.extensions) {
483
+ ext[extName] = await this.extensions[extName].onRequest(isInitial);
484
+ }
546
485
  return ext;
547
486
  }
548
487
 
@@ -595,203 +534,13 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
595
534
  this.emit(SlidingSyncEvent.Lifecycle, state, resp, err);
596
535
  }
597
536
 
598
- private shiftRight(listKey: string, hi: number, low: number): void {
599
- const list = this.lists.get(listKey);
600
- if (!list) {
601
- return;
602
- }
603
- // l h
604
- // 0,1,2,3,4 <- before
605
- // 0,1,2,2,3 <- after, hi is deleted and low is duplicated
606
- for (let i = hi; i > low; i--) {
607
- if (list.isIndexInRange(i)) {
608
- list.roomIndexToRoomId[i] = list.roomIndexToRoomId[i - 1];
609
- }
610
- }
611
- }
612
-
613
- private shiftLeft(listKey: string, hi: number, low: number): void {
614
- const list = this.lists.get(listKey);
615
- if (!list) {
616
- return;
617
- }
618
- // l h
619
- // 0,1,2,3,4 <- before
620
- // 0,1,3,4,4 <- after, low is deleted and hi is duplicated
621
- for (let i = low; i < hi; i++) {
622
- if (list.isIndexInRange(i)) {
623
- list.roomIndexToRoomId[i] = list.roomIndexToRoomId[i + 1];
624
- }
625
- }
626
- }
627
-
628
- private removeEntry(listKey: string, index: number): void {
629
- const list = this.lists.get(listKey);
630
- if (!list) {
631
- return;
632
- }
633
- // work out the max index
634
- let max = -1;
635
- for (const n in list.roomIndexToRoomId) {
636
- if (Number(n) > max) {
637
- max = Number(n);
638
- }
639
- }
640
- if (max < 0 || index > max) {
641
- return;
642
- }
643
- // Everything higher than the gap needs to be shifted left.
644
- this.shiftLeft(listKey, max, index);
645
- delete list.roomIndexToRoomId[max];
646
- }
647
-
648
- private addEntry(listKey: string, index: number): void {
649
- const list = this.lists.get(listKey);
650
- if (!list) {
651
- return;
652
- }
653
- // work out the max index
654
- let max = -1;
655
- for (const n in list.roomIndexToRoomId) {
656
- if (Number(n) > max) {
657
- max = Number(n);
658
- }
659
- }
660
- if (max < 0 || index > max) {
661
- return;
662
- }
663
- // Everything higher than the gap needs to be shifted right, +1 so we don't delete the highest element
664
- this.shiftRight(listKey, max + 1, index);
665
- }
666
-
667
- private processListOps(list: ListResponse, listKey: string): void {
668
- let gapIndex = -1;
669
- const listData = this.lists.get(listKey);
670
- if (!listData) {
671
- return;
672
- }
673
- list.ops.forEach((op: Operation) => {
674
- if (!listData) {
675
- return;
676
- }
677
- switch (op.op) {
678
- case "DELETE": {
679
- logger.debug("DELETE", listKey, op.index, ";");
680
- delete listData.roomIndexToRoomId[op.index];
681
- if (gapIndex !== -1) {
682
- // we already have a DELETE operation to process, so process it.
683
- this.removeEntry(listKey, gapIndex);
684
- }
685
- gapIndex = op.index;
686
- break;
687
- }
688
- case "INSERT": {
689
- logger.debug("INSERT", listKey, op.index, op.room_id, ";");
690
- if (listData.roomIndexToRoomId[op.index]) {
691
- // something is in this space, shift items out of the way
692
- if (gapIndex < 0) {
693
- // we haven't been told where to shift from, so make way for a new room entry.
694
- this.addEntry(listKey, op.index);
695
- } else if (gapIndex > op.index) {
696
- // the gap is further down the list, shift every element to the right
697
- // starting at the gap so we can just shift each element in turn:
698
- // [A,B,C,_] gapIndex=3, op.index=0
699
- // [A,B,C,C] i=3
700
- // [A,B,B,C] i=2
701
- // [A,A,B,C] i=1
702
- // Terminate. We'll assign into op.index next.
703
- this.shiftRight(listKey, gapIndex, op.index);
704
- } else if (gapIndex < op.index) {
705
- // the gap is further up the list, shift every element to the left
706
- // starting at the gap so we can just shift each element in turn
707
- this.shiftLeft(listKey, op.index, gapIndex);
708
- }
709
- }
710
- // forget the gap, we don't need it anymore. This is outside the check for
711
- // a room being present in this index position because INSERTs always universally
712
- // forget the gap, not conditionally based on the presence of a room in the INSERT
713
- // position. Without this, DELETE 0; INSERT 0; would do the wrong thing.
714
- gapIndex = -1;
715
- listData.roomIndexToRoomId[op.index] = op.room_id;
716
- break;
717
- }
718
- case "INVALIDATE": {
719
- const startIndex = op.range[0];
720
- for (let i = startIndex; i <= op.range[1]; i++) {
721
- delete listData.roomIndexToRoomId[i];
722
- }
723
- logger.debug("INVALIDATE", listKey, op.range[0], op.range[1], ";");
724
- break;
725
- }
726
- case "SYNC": {
727
- const startIndex = op.range[0];
728
- for (let i = startIndex; i <= op.range[1]; i++) {
729
- const roomId = op.room_ids[i - startIndex];
730
- if (!roomId) {
731
- break; // we are at the end of list
732
- }
733
- listData.roomIndexToRoomId[i] = roomId;
734
- }
735
- logger.debug("SYNC", listKey, op.range[0], op.range[1], (op.room_ids || []).join(" "), ";");
736
- break;
737
- }
738
- }
739
- });
740
- if (gapIndex !== -1) {
741
- // we already have a DELETE operation to process, so process it
742
- // Everything higher than the gap needs to be shifted left.
743
- this.removeEntry(listKey, gapIndex);
744
- }
745
- }
746
-
747
537
  /**
748
- * Resend a Sliding Sync request. Used when something has changed in the request. Resolves with
749
- * the transaction ID of this request on success. Rejects with the transaction ID of this request
750
- * on failure.
538
+ * Resend a Sliding Sync request. Used when something has changed in the request.
751
539
  */
752
- public resend(): Promise<string> {
753
- if (this.needsResend && this.txnIdDefers.length > 0) {
754
- // we already have a resend queued, so just return the same promise
755
- return this.txnIdDefers[this.txnIdDefers.length - 1].promise;
756
- }
540
+ public resend(): void {
757
541
  this.needsResend = true;
758
- this.txnId = this.client.makeTxnId();
759
- const d = defer<string>();
760
- this.txnIdDefers.push({
761
- ...d,
762
- txnId: this.txnId,
763
- });
764
542
  this.abortController?.abort();
765
543
  this.abortController = new AbortController();
766
- return d.promise;
767
- }
768
-
769
- private resolveTransactionDefers(txnId?: string): void {
770
- if (!txnId) {
771
- return;
772
- }
773
- // find the matching index
774
- let txnIndex = -1;
775
- for (let i = 0; i < this.txnIdDefers.length; i++) {
776
- if (this.txnIdDefers[i].txnId === txnId) {
777
- txnIndex = i;
778
- break;
779
- }
780
- }
781
- if (txnIndex === -1) {
782
- // this shouldn't happen; we shouldn't be seeing txn_ids for things we don't know about,
783
- // whine about it.
784
- logger.warn(`resolveTransactionDefers: seen ${txnId} but it isn't a pending txn, ignoring.`);
785
- return;
786
- }
787
- // This list is sorted in time, so if the input txnId ACKs in the middle of this array,
788
- // then everything before it that hasn't been ACKed yet never will and we should reject them.
789
- for (let i = 0; i < txnIndex; i++) {
790
- this.txnIdDefers[i].reject(this.txnIdDefers[i].txnId);
791
- }
792
- this.txnIdDefers[txnIndex].resolve(txnId);
793
- // clear out settled promises, including the one we resolved.
794
- this.txnIdDefers = this.txnIdDefers.slice(txnIndex + 1);
795
544
  }
796
545
 
797
546
  /**
@@ -802,7 +551,6 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
802
551
  this.abortController?.abort();
803
552
  // remove all listeners so things can be GC'd
804
553
  this.removeAllListeners(SlidingSyncEvent.Lifecycle);
805
- this.removeAllListeners(SlidingSyncEvent.List);
806
554
  this.removeAllListeners(SlidingSyncEvent.RoomData);
807
555
  }
808
556
 
@@ -811,20 +559,13 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
811
559
  */
812
560
  private resetup(): void {
813
561
  logger.warn("SlidingSync: resetting connection info");
814
- // any pending txn ID defers will be forgotten already by the server, so clear them out
815
- this.txnIdDefers.forEach((d) => {
816
- d.reject(d.txnId);
817
- });
818
- this.txnIdDefers = [];
819
562
  // resend sticky params and de-confirm all subscriptions
820
563
  this.lists.forEach((l) => {
821
564
  l.setModified(true);
822
565
  });
823
566
  this.confirmedRoomSubscriptions = new Set<string>(); // leave desired ones alone though!
824
567
  // reset the connection as we might be wedged
825
- this.needsResend = true;
826
- this.abortController?.abort();
827
- this.abortController = new AbortController();
568
+ this.resend();
828
569
  }
829
570
 
830
571
  /**
@@ -836,20 +577,18 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
836
577
  let currentPos: string | undefined;
837
578
  while (!this.terminated) {
838
579
  this.needsResend = false;
839
- let doNotUpdateList = false;
840
580
  let resp: MSC3575SlidingSyncResponse | undefined;
841
581
  try {
842
- const listModifiedCount = this.listModifiedCount;
843
582
  const reqLists: Record<string, MSC3575List> = {};
844
583
  this.lists.forEach((l: SlidingList, key: string) => {
845
- reqLists[key] = l.getList(false);
584
+ reqLists[key] = l.getList(true);
846
585
  });
847
586
  const reqBody: MSC3575SlidingSyncRequest = {
848
587
  lists: reqLists,
849
588
  pos: currentPos,
850
589
  timeout: this.timeoutMS,
851
590
  clientTimeout: this.timeoutMS + BUFFER_PERIOD_MS,
852
- extensions: this.getExtensionRequest(currentPos === undefined),
591
+ extensions: await this.getExtensionRequest(currentPos === undefined),
853
592
  };
854
593
  // check if we are (un)subscribing to a room and modify request this one time for it
855
594
  const newSubscriptions = difference(this.desiredRoomSubscriptions, this.confirmedRoomSubscriptions);
@@ -868,10 +607,6 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
868
607
  reqBody.room_subscriptions[roomId] = sub;
869
608
  }
870
609
  }
871
- if (this.txnId) {
872
- reqBody.txn_id = this.txnId;
873
- this.txnId = null;
874
- }
875
610
  this.pendingReq = this.client.slidingSync(reqBody, this.proxyBaseUrl, this.abortController.signal);
876
611
  resp = await this.pendingReq;
877
612
  currentPos = resp.pos;
@@ -882,13 +617,6 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
882
617
  for (const roomId of unsubscriptions) {
883
618
  this.confirmedRoomSubscriptions.delete(roomId);
884
619
  }
885
- if (listModifiedCount !== this.listModifiedCount) {
886
- // the lists have been modified whilst we were waiting for 'await' to return, but the abort()
887
- // call did nothing. It is NOT SAFE to modify the list array now. We'll process the response but
888
- // not update list pointers.
889
- logger.debug("list modified during await call, not updating list");
890
- doNotUpdateList = true;
891
- }
892
620
  // mark all these lists as having been sent as sticky so we don't keep sending sticky params
893
621
  this.lists.forEach((l) => {
894
622
  l.setModified(false);
@@ -931,27 +659,8 @@ export class SlidingSync extends TypedEventEmitter<SlidingSyncEvent, SlidingSync
931
659
  await this.invokeRoomDataListeners(roomId, resp!.rooms[roomId]);
932
660
  }
933
661
 
934
- const listKeysWithUpdates: Set<string> = new Set();
935
- if (!doNotUpdateList) {
936
- for (const [key, list] of Object.entries(resp.lists)) {
937
- list.ops = list.ops ?? [];
938
- if (list.ops.length > 0) {
939
- listKeysWithUpdates.add(key);
940
- }
941
- this.processListOps(list, key);
942
- }
943
- }
944
662
  this.invokeLifecycleListeners(SlidingSyncState.Complete, resp);
945
663
  await this.onPostExtensionsResponse(resp.extensions);
946
- listKeysWithUpdates.forEach((listKey: string) => {
947
- const list = this.lists.get(listKey);
948
- if (!list) {
949
- return;
950
- }
951
- this.emit(SlidingSyncEvent.List, listKey, list.joinedCount, Object.assign({}, list.roomIndexToRoomId));
952
- });
953
-
954
- this.resolveTransactionDefers(resp.txn_id);
955
664
  }
956
665
  }
957
666
  }