cojson 0.18.0 → 0.18.2

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 (77) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/coValueCore/branching.d.ts +36 -0
  4. package/dist/coValueCore/branching.d.ts.map +1 -0
  5. package/dist/coValueCore/branching.js +122 -0
  6. package/dist/coValueCore/branching.js.map +1 -0
  7. package/dist/coValueCore/coValueCore.d.ts +71 -5
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +162 -53
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +3 -0
  12. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +1 -0
  13. package/dist/coValueCore/decodeTransactionChangesAndMeta.js +59 -0
  14. package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +1 -0
  15. package/dist/coValueCore/utils.d.ts.map +1 -1
  16. package/dist/coValueCore/utils.js +3 -0
  17. package/dist/coValueCore/utils.js.map +1 -1
  18. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  19. package/dist/coValues/coList.d.ts +3 -3
  20. package/dist/coValues/coList.d.ts.map +1 -1
  21. package/dist/coValues/coList.js +4 -7
  22. package/dist/coValues/coList.js.map +1 -1
  23. package/dist/coValues/coMap.d.ts +3 -3
  24. package/dist/coValues/coMap.d.ts.map +1 -1
  25. package/dist/coValues/coMap.js +6 -6
  26. package/dist/coValues/coMap.js.map +1 -1
  27. package/dist/coValues/coStream.d.ts +3 -3
  28. package/dist/coValues/coStream.d.ts.map +1 -1
  29. package/dist/coValues/coStream.js +4 -4
  30. package/dist/coValues/coStream.js.map +1 -1
  31. package/dist/ids.d.ts.map +1 -1
  32. package/dist/ids.js.map +1 -1
  33. package/dist/jsonStringify.d.ts +1 -0
  34. package/dist/jsonStringify.d.ts.map +1 -1
  35. package/dist/jsonStringify.js +8 -0
  36. package/dist/jsonStringify.js.map +1 -1
  37. package/dist/localNode.d.ts +6 -0
  38. package/dist/localNode.d.ts.map +1 -1
  39. package/dist/localNode.js +27 -0
  40. package/dist/localNode.js.map +1 -1
  41. package/dist/permissions.d.ts +2 -7
  42. package/dist/permissions.d.ts.map +1 -1
  43. package/dist/permissions.js +75 -71
  44. package/dist/permissions.js.map +1 -1
  45. package/dist/tests/branching.test.d.ts +2 -0
  46. package/dist/tests/branching.test.d.ts.map +1 -0
  47. package/dist/tests/branching.test.js +287 -0
  48. package/dist/tests/branching.test.js.map +1 -0
  49. package/dist/tests/coValueCore.test.js +2 -3
  50. package/dist/tests/coValueCore.test.js.map +1 -1
  51. package/dist/tests/group.removeMember.test.js +63 -116
  52. package/dist/tests/group.removeMember.test.js.map +1 -1
  53. package/dist/tests/sync.load.test.js +36 -0
  54. package/dist/tests/sync.load.test.js.map +1 -1
  55. package/dist/tests/sync.storage.test.js +39 -3
  56. package/dist/tests/sync.storage.test.js.map +1 -1
  57. package/dist/tests/sync.upload.test.js +39 -3
  58. package/dist/tests/sync.upload.test.js.map +1 -1
  59. package/package.json +2 -2
  60. package/src/coValueCore/branching.ts +198 -0
  61. package/src/coValueCore/coValueCore.ts +255 -72
  62. package/src/coValueCore/decodeTransactionChangesAndMeta.ts +81 -0
  63. package/src/coValueCore/utils.ts +4 -0
  64. package/src/coValueCore/verifiedState.ts +1 -1
  65. package/src/coValues/coList.ts +8 -10
  66. package/src/coValues/coMap.ts +8 -11
  67. package/src/coValues/coStream.ts +7 -8
  68. package/src/ids.ts +4 -1
  69. package/src/jsonStringify.ts +8 -0
  70. package/src/localNode.ts +40 -0
  71. package/src/permissions.ts +83 -90
  72. package/src/tests/branching.test.ts +425 -0
  73. package/src/tests/coValueCore.test.ts +2 -3
  74. package/src/tests/group.removeMember.test.ts +116 -214
  75. package/src/tests/sync.load.test.ts +48 -0
  76. package/src/tests/sync.storage.test.ts +54 -3
  77. package/src/tests/sync.upload.test.ts +53 -3
@@ -77,236 +77,138 @@ describe("Group.removeMember", () => {
77
77
  expect(mapOnAliceNode.get("test")).toEqual("test");
78
78
  });
79
79
 
80
- test("a reader member should be able to revoke themselves", async () => {
80
+ for (const member of ["writer", "reader", "writeOnly", "admin"] as const) {
81
+ test(`${member} member should be able to revoke themselves`, async () => {
82
+ const admin = await setupTestAccount({
83
+ connected: true,
84
+ });
85
+
86
+ const client = await setupTestAccount({
87
+ connected: true,
88
+ });
89
+
90
+ const group = admin.node.createGroup();
91
+ group.addMember(
92
+ await loadCoValueOrFail(admin.node, client.accountID),
93
+ member,
94
+ );
95
+
96
+ await group.core.waitForSync();
97
+
98
+ const loadedGroup = await loadCoValueOrFail(client.node, group.id);
99
+ expect(loadedGroup.myRole()).toEqual(member);
100
+
101
+ await loadedGroup.removeMember(client.node.expectCurrentAccount(member));
102
+
103
+ expect(loadedGroup.myRole()).toEqual(undefined);
104
+ });
105
+ }
106
+
107
+ for (const member of ["writer", "reader", "writeOnly"] as const) {
108
+ test(`${member} member cannot remove other accounts`, async () => {
109
+ const admin = await setupTestAccount({
110
+ connected: true,
111
+ });
112
+
113
+ const writer = await setupTestAccount({
114
+ connected: true,
115
+ });
116
+
117
+ const writeOnly = await setupTestAccount({
118
+ connected: true,
119
+ });
120
+
121
+ const reader = await setupTestAccount({
122
+ connected: true,
123
+ });
124
+
125
+ const client = await setupTestAccount({
126
+ connected: true,
127
+ });
128
+
129
+ const group = admin.node.createGroup();
130
+ group.addMember(
131
+ await loadCoValueOrFail(admin.node, writer.accountID),
132
+ "writer",
133
+ );
134
+ group.addMember(
135
+ await loadCoValueOrFail(admin.node, reader.accountID),
136
+ "reader",
137
+ );
138
+ group.addMember(
139
+ await loadCoValueOrFail(admin.node, writeOnly.accountID),
140
+ "writeOnly",
141
+ );
142
+ group.addMember(
143
+ await loadCoValueOrFail(admin.node, client.accountID),
144
+ member,
145
+ );
146
+
147
+ const loadedGroup = await loadCoValueOrFail(client.node, group.id);
148
+
149
+ loadedGroup.removeMember(
150
+ await loadCoValueOrFail(client.node, reader.accountID),
151
+ );
152
+ loadedGroup.removeMember(
153
+ await loadCoValueOrFail(client.node, writeOnly.accountID),
154
+ );
155
+ loadedGroup.removeMember(
156
+ await loadCoValueOrFail(client.node, writer.accountID),
157
+ );
158
+ loadedGroup.removeMember(
159
+ await loadCoValueOrFail(client.node, admin.accountID),
160
+ );
161
+
162
+ expect(loadedGroup.roleOf(reader.accountID)).toEqual("reader");
163
+ expect(loadedGroup.roleOf(writer.accountID)).toEqual("writer");
164
+ expect(loadedGroup.roleOf(writeOnly.accountID)).toEqual("writeOnly");
165
+ expect(loadedGroup.roleOf(admin.accountID)).toEqual("admin");
166
+
167
+ await loadedGroup.core.waitForSync();
168
+
169
+ expect((await loadCoValueOrFail(reader.node, group.id)).myRole()).toEqual(
170
+ "reader",
171
+ );
172
+ expect((await loadCoValueOrFail(writer.node, group.id)).myRole()).toEqual(
173
+ "writer",
174
+ );
175
+ expect(
176
+ (await loadCoValueOrFail(writeOnly.node, group.id)).myRole(),
177
+ ).toEqual("writeOnly");
178
+ expect((await loadCoValueOrFail(admin.node, group.id)).myRole()).toEqual(
179
+ "admin",
180
+ );
181
+ });
182
+ }
183
+
184
+ test(`admin member cannot remove other admins`, async () => {
81
185
  const admin = await setupTestAccount({
82
186
  connected: true,
83
187
  });
84
188
 
85
- const reader = await setupTestAccount({
86
- connected: true,
87
- });
88
-
89
- const group = admin.node.createGroup();
90
- const readerOnAdminNode = await loadCoValueOrFail(
91
- admin.node,
92
- reader.accountID,
93
- );
94
- group.addMember(readerOnAdminNode, "reader");
95
-
96
- const groupOnReaderNode = await loadCoValueOrFail(reader.node, group.id);
97
- expect(groupOnReaderNode.myRole()).toEqual("reader");
98
-
99
- await groupOnReaderNode.removeMember(
100
- reader.node.expectCurrentAccount("reader"),
101
- );
102
-
103
- expect(groupOnReaderNode.myRole()).toEqual(undefined);
104
- });
105
-
106
- test("a writer member should be able to revoke themselves", async () => {
107
- const admin = await setupTestAccount({
108
- connected: true,
109
- });
110
-
111
- const writer = await setupTestAccount({
189
+ const client = await setupTestAccount({
112
190
  connected: true,
113
191
  });
114
192
 
115
193
  const group = admin.node.createGroup();
116
- const writerOnAdminNode = await loadCoValueOrFail(
117
- admin.node,
118
- writer.accountID,
194
+ group.addMember(
195
+ await loadCoValueOrFail(admin.node, client.accountID),
196
+ "admin",
119
197
  );
120
- group.addMember(writerOnAdminNode, "writer");
121
198
 
122
- const groupOnWriterNode = await loadCoValueOrFail(writer.node, group.id);
123
- expect(groupOnWriterNode.myRole()).toEqual("writer");
199
+ const loadedGroup = await loadCoValueOrFail(client.node, group.id);
124
200
 
125
- await groupOnWriterNode.removeMember(
126
- writer.node.expectCurrentAccount("writer"),
201
+ loadedGroup.removeMember(
202
+ await loadCoValueOrFail(client.node, admin.accountID),
127
203
  );
128
204
 
129
- expect(groupOnWriterNode.myRole()).toEqual(undefined);
130
- });
131
-
132
- test("a writeOnly member should be able to revoke themselves", async () => {
133
- const admin = await setupTestAccount({
134
- connected: true,
135
- });
136
-
137
- const writeOnly = await setupTestAccount({
138
- connected: true,
139
- });
205
+ expect(loadedGroup.roleOf(admin.accountID)).toEqual("admin");
140
206
 
141
- const group = admin.node.createGroup();
142
- const writeOnlyOnAdminNode = await loadCoValueOrFail(
143
- admin.node,
144
- writeOnly.accountID,
145
- );
146
- group.addMember(writeOnlyOnAdminNode, "writeOnly");
207
+ await loadedGroup.core.waitForSync();
147
208
 
148
- const groupOnWriteOnlyNode = await loadCoValueOrFail(
149
- writeOnly.node,
150
- group.id,
209
+ expect((await loadCoValueOrFail(admin.node, group.id)).myRole()).toEqual(
210
+ "admin",
151
211
  );
152
- expect(groupOnWriteOnlyNode.myRole()).toEqual("writeOnly");
153
-
154
- await groupOnWriteOnlyNode.removeMember(
155
- writeOnly.node.expectCurrentAccount("writeOnly"),
156
- );
157
-
158
- expect(groupOnWriteOnlyNode.myRole()).toEqual(undefined);
159
- });
160
-
161
- test("an admin member should be able to revoke themselves", async () => {
162
- const admin = await setupTestAccount({
163
- connected: true,
164
- });
165
-
166
- const otherAdmin = await setupTestAccount({
167
- connected: true,
168
- });
169
-
170
- const group = admin.node.createGroup();
171
- const otherAdminOnAdminNode = await loadCoValueOrFail(
172
- admin.node,
173
- otherAdmin.accountID,
174
- );
175
- group.addMember(otherAdminOnAdminNode, "admin");
176
-
177
- const groupOnOtherAdminNode = await loadCoValueOrFail(
178
- otherAdmin.node,
179
- group.id,
180
- );
181
- expect(groupOnOtherAdminNode.myRole()).toEqual("admin");
182
-
183
- await groupOnOtherAdminNode.removeMember(
184
- otherAdmin.node.expectCurrentAccount("admin"),
185
- );
186
-
187
- expect(groupOnOtherAdminNode.myRole()).toEqual(undefined);
188
- });
189
-
190
- test("a writer member cannot remove other accounts", async () => {
191
- const admin = await setupTestAccount({
192
- connected: true,
193
- });
194
-
195
- const writer = await setupTestAccount({
196
- connected: true,
197
- });
198
-
199
- const otherMember = await setupTestAccount({
200
- connected: true,
201
- });
202
-
203
- const group = admin.node.createGroup();
204
- const writerOnAdminNode = await loadCoValueOrFail(
205
- admin.node,
206
- writer.accountID,
207
- );
208
- const otherMemberOnAdminNode = await loadCoValueOrFail(
209
- admin.node,
210
- otherMember.accountID,
211
- );
212
-
213
- group.addMember(writerOnAdminNode, "writer");
214
- group.addMember(otherMemberOnAdminNode, "reader");
215
-
216
- const groupOnWriterNode = await loadCoValueOrFail(writer.node, group.id);
217
- expect(groupOnWriterNode.myRole()).toEqual("writer");
218
-
219
- const otherMemberOnWriterNode = await loadCoValueOrFail(
220
- writer.node,
221
- otherMember.accountID,
222
- );
223
-
224
- await groupOnWriterNode.removeMember(otherMemberOnWriterNode);
225
-
226
- expect(groupOnWriterNode.roleOf(otherMember.accountID)).toEqual("reader");
227
- });
228
-
229
- test("a writeOnly member cannot remove other accounts", async () => {
230
- const admin = await setupTestAccount({
231
- connected: true,
232
- });
233
-
234
- const writeOnly = await setupTestAccount({
235
- connected: true,
236
- });
237
-
238
- const otherMember = await setupTestAccount({
239
- connected: true,
240
- });
241
-
242
- const group = admin.node.createGroup();
243
- const writeOnlyOnAdminNode = await loadCoValueOrFail(
244
- admin.node,
245
- writeOnly.accountID,
246
- );
247
- const otherMemberOnAdminNode = await loadCoValueOrFail(
248
- admin.node,
249
- otherMember.accountID,
250
- );
251
-
252
- group.addMember(writeOnlyOnAdminNode, "writeOnly");
253
- group.addMember(otherMemberOnAdminNode, "reader");
254
-
255
- const groupOnWriteOnlyNode = await loadCoValueOrFail(
256
- writeOnly.node,
257
- group.id,
258
- );
259
- expect(groupOnWriteOnlyNode.myRole()).toEqual("writeOnly");
260
-
261
- const otherMemberOnWriteOnlyNode = await loadCoValueOrFail(
262
- writeOnly.node,
263
- otherMember.accountID,
264
- );
265
-
266
- await groupOnWriteOnlyNode.removeMember(otherMemberOnWriteOnlyNode);
267
-
268
- expect(groupOnWriteOnlyNode.roleOf(otherMember.accountID)).toEqual(
269
- "reader",
270
- );
271
- });
272
-
273
- test("a reader member cannot remove other accounts", async () => {
274
- const admin = await setupTestAccount({
275
- connected: true,
276
- });
277
-
278
- const reader = await setupTestAccount({
279
- connected: true,
280
- });
281
-
282
- const otherMember = await setupTestAccount({
283
- connected: true,
284
- });
285
-
286
- const group = admin.node.createGroup();
287
- const readerOnAdminNode = await loadCoValueOrFail(
288
- admin.node,
289
- reader.accountID,
290
- );
291
- const otherMemberOnAdminNode = await loadCoValueOrFail(
292
- admin.node,
293
- otherMember.accountID,
294
- );
295
-
296
- group.addMember(readerOnAdminNode, "reader");
297
- group.addMember(otherMemberOnAdminNode, "writer");
298
-
299
- const groupOnReaderNode = await loadCoValueOrFail(reader.node, group.id);
300
- expect(groupOnReaderNode.myRole()).toEqual("reader");
301
-
302
- const otherMemberOnReaderNode = await loadCoValueOrFail(
303
- reader.node,
304
- otherMember.accountID,
305
- );
306
-
307
- await groupOnReaderNode.removeMember(otherMemberOnReaderNode);
308
-
309
- expect(groupOnReaderNode.roleOf(otherMember.accountID)).toEqual("writer");
310
212
  });
311
213
 
312
214
  test("removing a member when inheriting a group where the user lacks read rights", async () => {
@@ -132,6 +132,54 @@ describe("loading coValues from server", () => {
132
132
  `);
133
133
  });
134
134
 
135
+ test("loading a branch", async () => {
136
+ const client = setupTestNode({
137
+ connected: true,
138
+ });
139
+
140
+ const group = jazzCloud.node.createGroup();
141
+ group.addMember("everyone", "writer");
142
+ const map = group.createMap();
143
+ const branchName = "feature-branch";
144
+
145
+ map.set("key1", "value1");
146
+ map.set("key2", "value2");
147
+
148
+ const branch = await jazzCloud.node.checkoutBranch(map.id, branchName);
149
+
150
+ if (branch === "unavailable") {
151
+ throw new Error("Branch is unavailable");
152
+ }
153
+
154
+ branch.set("branchKey", "branchValue");
155
+
156
+ SyncMessagesLog.clear();
157
+
158
+ const loadedBranch = await loadCoValueOrFail(client.node, branch.id);
159
+
160
+ expect(branch.get("key1")).toBe("value1");
161
+ expect(branch.get("key2")).toBe("value2");
162
+ expect(branch.get("branchKey")).toBe("branchValue");
163
+
164
+ expect(
165
+ SyncMessagesLog.getMessages({
166
+ Group: group.core,
167
+ Map: map.core,
168
+ Branch: branch.core,
169
+ }),
170
+ ).toMatchInlineSnapshot(`
171
+ [
172
+ "client -> server | LOAD Branch sessions: empty",
173
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
174
+ "server -> client | CONTENT Map header: true new: After: 0 New: 2",
175
+ "server -> client | CONTENT Branch header: true new: After: 0 New: 2",
176
+ "client -> server | KNOWN Group sessions: header/5",
177
+ "client -> server | KNOWN Map sessions: header/2",
178
+ "client -> server | KNOWN Branch sessions: header/2",
179
+ ]
180
+ `);
181
+ });
182
+
135
183
  test("unavailable coValue retry with skipRetry set to false", async () => {
136
184
  const client = setupTestNode();
137
185
  const client2 = setupTestNode();
@@ -173,9 +173,9 @@ describe("client with storage syncs with server", () => {
173
173
 
174
174
  const loadedValue = await loadCoValueOrFail(client.node, map.id);
175
175
 
176
- const validTransactions = determineValidTransactions(loadedValue.core);
177
-
178
- expect(validTransactions[0]?.tx.meta).toBe(`{"meta":true}`);
176
+ expect(loadedValue.core.verifiedTransactions[0]?.tx.meta).toBe(
177
+ `{"meta":true}`,
178
+ );
179
179
 
180
180
  expect(
181
181
  SyncMessagesLog.getMessages({
@@ -193,6 +193,57 @@ describe("client with storage syncs with server", () => {
193
193
  `);
194
194
  });
195
195
 
196
+ test("loading a branch from storage", async () => {
197
+ const client = setupTestNode({
198
+ connected: true,
199
+ });
200
+ const { storage } = client.addStorage();
201
+
202
+ const group = client.node.createGroup();
203
+ const map = group.createMap();
204
+ const branchName = "feature-branch";
205
+
206
+ map.set("key1", "value1");
207
+ map.set("key2", "value2");
208
+
209
+ const branch = await client.node.checkoutBranch(map.id, branchName);
210
+
211
+ if (branch === "unavailable") {
212
+ throw new Error("Branch is unavailable");
213
+ }
214
+
215
+ branch.set("branchKey", "branchValue");
216
+ await branch.core.waitForSync();
217
+
218
+ client.restart();
219
+ client.addStorage({
220
+ storage,
221
+ });
222
+
223
+ SyncMessagesLog.clear();
224
+
225
+ const loadedBranch = await loadCoValueOrFail(client.node, branch.id);
226
+
227
+ expect(branch.get("key1")).toBe("value1");
228
+ expect(branch.get("key2")).toBe("value2");
229
+ expect(branch.get("branchKey")).toBe("branchValue");
230
+
231
+ expect(
232
+ SyncMessagesLog.getMessages({
233
+ Group: group.core,
234
+ Map: map.core,
235
+ Branch: branch.core,
236
+ }),
237
+ ).toMatchInlineSnapshot(`
238
+ [
239
+ "client -> storage | LOAD Branch sessions: empty",
240
+ "storage -> client | CONTENT Group header: true new: After: 0 New: 3",
241
+ "storage -> client | CONTENT Map header: true new: After: 0 New: 2",
242
+ "storage -> client | CONTENT Branch header: true new: After: 0 New: 2",
243
+ ]
244
+ `);
245
+ });
246
+
196
247
  test("updating a coValue while offline", async () => {
197
248
  const client = setupTestNode();
198
249
 
@@ -52,6 +52,56 @@ describe("client to server upload", () => {
52
52
  `);
53
53
  });
54
54
 
55
+ test("creating a branch", async () => {
56
+ const client = setupTestNode({
57
+ connected: true,
58
+ });
59
+
60
+ const group = jazzCloud.node.createGroup();
61
+ group.addMember("everyone", "writer");
62
+ const map = group.createMap();
63
+ const branchName = "feature-branch";
64
+
65
+ map.set("key1", "value1");
66
+ map.set("key2", "value2");
67
+
68
+ await map.core.waitForSync();
69
+
70
+ SyncMessagesLog.clear();
71
+
72
+ const branch = await client.node.checkoutBranch(map.id, branchName);
73
+
74
+ if (branch === "unavailable") {
75
+ throw new Error("Branch is unavailable");
76
+ }
77
+
78
+ branch.set("branchKey", "branchValue");
79
+
80
+ await branch.core.waitForSync();
81
+
82
+ expect(
83
+ SyncMessagesLog.getMessages({
84
+ Group: group.core,
85
+ Map: map.core,
86
+ Branch: branch.core,
87
+ }),
88
+ ).toMatchInlineSnapshot(`
89
+ [
90
+ "client -> server | LOAD Map sessions: empty",
91
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
92
+ "server -> client | CONTENT Map header: true new: After: 0 New: 2",
93
+ "client -> server | KNOWN Group sessions: header/5",
94
+ "client -> server | KNOWN Map sessions: header/2",
95
+ "client -> server | LOAD Branch sessions: empty",
96
+ "server -> client | KNOWN Branch sessions: empty",
97
+ "client -> server | CONTENT Branch header: true new: After: 0 New: 1",
98
+ "client -> server | CONTENT Branch header: false new: After: 1 New: 1",
99
+ "server -> client | KNOWN Branch sessions: header/1",
100
+ "server -> client | KNOWN Branch sessions: header/2",
101
+ ]
102
+ `);
103
+ });
104
+
55
105
  test("syncs meta information", async () => {
56
106
  const client = setupTestNode({
57
107
  connected: true,
@@ -67,9 +117,9 @@ describe("client to server upload", () => {
67
117
 
68
118
  const loadedValue = await loadCoValueOrFail(jazzCloud.node, map.id);
69
119
 
70
- const validTransactions = determineValidTransactions(loadedValue.core);
71
-
72
- expect(validTransactions[0]?.tx.meta).toBe(`{"meta":true}`);
120
+ expect(loadedValue.core.verifiedTransactions[0]?.tx.meta).toBe(
121
+ `{"meta":true}`,
122
+ );
73
123
 
74
124
  expect(
75
125
  SyncMessagesLog.getMessages({