cojson 0.18.19 → 0.18.20

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 (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/GarbageCollector.d.ts +2 -1
  4. package/dist/GarbageCollector.d.ts.map +1 -1
  5. package/dist/GarbageCollector.js +3 -2
  6. package/dist/GarbageCollector.js.map +1 -1
  7. package/dist/coValueCore/coValueCore.d.ts +28 -25
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +128 -90
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/utils.d.ts +6 -0
  12. package/dist/coValueCore/utils.d.ts.map +1 -1
  13. package/dist/coValueCore/utils.js +53 -25
  14. package/dist/coValueCore/utils.js.map +1 -1
  15. package/dist/localNode.d.ts +5 -4
  16. package/dist/localNode.d.ts.map +1 -1
  17. package/dist/localNode.js +31 -37
  18. package/dist/localNode.js.map +1 -1
  19. package/dist/sync.d.ts.map +1 -1
  20. package/dist/sync.js +56 -69
  21. package/dist/sync.js.map +1 -1
  22. package/dist/tests/GarbageCollector.test.js +14 -0
  23. package/dist/tests/GarbageCollector.test.js.map +1 -1
  24. package/dist/tests/SyncStateManager.test.js +1 -1
  25. package/dist/tests/coValueCore.dependencies.test.d.ts +2 -0
  26. package/dist/tests/coValueCore.dependencies.test.d.ts.map +1 -0
  27. package/dist/tests/coValueCore.dependencies.test.js +55 -0
  28. package/dist/tests/coValueCore.dependencies.test.js.map +1 -0
  29. package/dist/tests/coValueCore.test.js +2 -2
  30. package/dist/tests/coValueCore.test.js.map +1 -1
  31. package/dist/tests/coValueCoreLoadingState.test.js +43 -62
  32. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
  33. package/dist/tests/permissions.test.js +117 -117
  34. package/dist/tests/permissions.test.js.map +1 -1
  35. package/dist/tests/sync.load.test.js +238 -9
  36. package/dist/tests/sync.load.test.js.map +1 -1
  37. package/dist/tests/sync.peerReconciliation.test.js +7 -6
  38. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  39. package/dist/tests/testUtils.d.ts.map +1 -1
  40. package/dist/tests/testUtils.js +2 -1
  41. package/dist/tests/testUtils.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/GarbageCollector.ts +5 -2
  44. package/src/coValueCore/coValueCore.ts +172 -118
  45. package/src/coValueCore/utils.ts +85 -31
  46. package/src/localNode.ts +43 -48
  47. package/src/sync.ts +63 -89
  48. package/src/tests/GarbageCollector.test.ts +20 -0
  49. package/src/tests/SyncStateManager.test.ts +1 -1
  50. package/src/tests/coValueCore.dependencies.test.ts +90 -0
  51. package/src/tests/coValueCore.test.ts +2 -2
  52. package/src/tests/coValueCoreLoadingState.test.ts +50 -66
  53. package/src/tests/permissions.test.ts +120 -123
  54. package/src/tests/sync.load.test.ts +308 -9
  55. package/src/tests/sync.peerReconciliation.test.ts +7 -6
  56. package/src/tests/testUtils.ts +5 -3
@@ -132,6 +132,62 @@ describe("loading coValues from server", () => {
132
132
  `);
133
133
  });
134
134
 
135
+ test("new dependencies coming from updates should be pushed", async () => {
136
+ const client = setupTestNode({
137
+ connected: true,
138
+ });
139
+ const client2 = setupTestNode();
140
+
141
+ client2.connectToSyncServer({
142
+ ourName: "client2",
143
+ });
144
+
145
+ const group = client.node.createGroup();
146
+ group.addMember("everyone", "reader");
147
+ const map = group.createMap();
148
+ map.set("hello", "world", "trusting");
149
+
150
+ await map.core.waitForSync();
151
+
152
+ await loadCoValueOrFail(client2.node, map.id);
153
+
154
+ const parentGroup = client.node.createGroup();
155
+ group.extend(parentGroup);
156
+
157
+ await group.core.waitForSync();
158
+ await jazzCloud.node.syncManager.waitForAllCoValuesSync();
159
+
160
+ const messagesLog = SyncMessagesLog.getMessages({
161
+ ParentGroup: parentGroup.core,
162
+ Group: group.core,
163
+ Map: map.core,
164
+ });
165
+
166
+ expect(messagesLog).toMatchInlineSnapshot(`
167
+ [
168
+ "client -> server | CONTENT Group header: true new: After: 0 New: 5",
169
+ "client -> server | CONTENT Map header: true new: After: 0 New: 1",
170
+ "server -> client | KNOWN Group sessions: header/5",
171
+ "server -> client | KNOWN Map sessions: header/1",
172
+ "client2 -> server | LOAD Map sessions: empty",
173
+ "server -> client2 | CONTENT Group header: true new: After: 0 New: 5",
174
+ "server -> client2 | CONTENT Map header: true new: After: 0 New: 1",
175
+ "client2 -> server | KNOWN Group sessions: header/5",
176
+ "client2 -> server | KNOWN Map sessions: header/1",
177
+ "client -> server | CONTENT ParentGroup header: true new: After: 0 New: 4",
178
+ "client -> server | CONTENT Group header: false new: After: 5 New: 2",
179
+ "server -> client | KNOWN ParentGroup sessions: header/4",
180
+ "server -> client | KNOWN Group sessions: header/7",
181
+ "server -> client2 | CONTENT ParentGroup header: true new: After: 0 New: 4",
182
+ "server -> client2 | CONTENT Group header: false new: After: 5 New: 2",
183
+ "client2 -> server | KNOWN ParentGroup sessions: header/4",
184
+ "client2 -> server | KNOWN Group sessions: header/7",
185
+ ]
186
+ `);
187
+
188
+ expect(client2.node.expectCoValueLoaded(parentGroup.id)).toBeTruthy();
189
+ });
190
+
135
191
  test("loading a branch", async () => {
136
192
  const client = setupTestNode({
137
193
  connected: true,
@@ -686,9 +742,9 @@ describe("loading coValues from server", () => {
686
742
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
687
743
  "client -> server | KNOWN ParentGroup sessions: header/6",
688
744
  "client -> server | LOAD Group sessions: empty",
689
- "client -> server | KNOWN Map sessions: header/1",
690
745
  "server -> client | CONTENT Group header: true new: After: 0 New: 5",
691
746
  "client -> server | KNOWN Group sessions: header/5",
747
+ "client -> server | KNOWN Map sessions: header/1",
692
748
  ]
693
749
  `);
694
750
 
@@ -729,10 +785,12 @@ describe("loading coValues from server", () => {
729
785
  "server -> client | CONTENT Group header: true new: After: 0 New: 5",
730
786
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
731
787
  "client -> server | LOAD ParentGroup sessions: empty",
732
- "client -> server | KNOWN Group sessions: header/5",
733
- "client -> server | KNOWN Map sessions: header/1",
788
+ "client -> server | LOAD Group sessions: empty",
734
789
  "server -> client | CONTENT ParentGroup header: true new: After: 0 New: 6",
790
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
735
791
  "client -> server | KNOWN ParentGroup sessions: header/6",
792
+ "client -> server | KNOWN Group sessions: header/5",
793
+ "client -> server | KNOWN Map sessions: header/1",
736
794
  ]
737
795
  `);
738
796
 
@@ -775,9 +833,9 @@ describe("loading coValues from server", () => {
775
833
  "server -> client | CONTENT Group header: true new: After: 0 New: 5",
776
834
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
777
835
  "client -> server | LOAD Account sessions: empty",
778
- "client -> server | KNOWN Group sessions: header/0",
779
- "client -> server | KNOWN Map sessions: header/0",
836
+ "client -> server | LOAD Group sessions: empty",
780
837
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
838
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
781
839
  "client -> server | KNOWN Account sessions: header/4",
782
840
  "client -> server | KNOWN Group sessions: header/5",
783
841
  "client -> server | KNOWN Map sessions: header/1",
@@ -839,7 +897,6 @@ describe("loading coValues from server", () => {
839
897
  "server -> client | CONTENT Map header: true new: After: 0 New: 1 | After: 0 New: 1",
840
898
  "client -> server | KNOWN Group sessions: header/5",
841
899
  "client -> server | LOAD Account sessions: empty",
842
- "client -> server | KNOWN Map sessions: header/1",
843
900
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
844
901
  "client -> server | KNOWN Account sessions: header/4",
845
902
  "client -> server | KNOWN Map sessions: header/2",
@@ -903,12 +960,13 @@ describe("loading coValues from server", () => {
903
960
  "server -> client | CONTENT Group header: true new: After: 0 New: 5",
904
961
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
905
962
  "client -> server | LOAD Account sessions: empty",
906
- "client -> server | KNOWN Group sessions: header/0",
907
- "client -> server | KNOWN Map sessions: header/0",
963
+ "client -> server | LOAD Group sessions: empty",
964
+ "server -> client | CONTENT Group header: true new: After: 0 New: 5",
908
965
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
909
966
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
910
967
  "client -> server | KNOWN Account sessions: header/4",
911
968
  "client -> server | KNOWN Group sessions: header/5",
969
+ "client -> server | KNOWN Group sessions: header/5",
912
970
  "client -> server | KNOWN Map sessions: header/1",
913
971
  ]
914
972
  `);
@@ -974,7 +1032,6 @@ describe("loading coValues from server", () => {
974
1032
  "client -> server | LOAD Map sessions: empty",
975
1033
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
976
1034
  "client -> server | LOAD Account sessions: empty",
977
- "client -> server | KNOWN Map sessions: header/0",
978
1035
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
979
1036
  "server -> client | CONTENT Account header: true new: After: 0 New: 4",
980
1037
  "client -> server | KNOWN Account sessions: header/4",
@@ -983,6 +1040,248 @@ describe("loading coValues from server", () => {
983
1040
  `);
984
1041
  });
985
1042
 
1043
+ test("edge servers should wait for all the dependencies to be available before sending the content", async () => {
1044
+ const coreServer = await setupTestAccount();
1045
+ coreServer.node.syncManager.disableTransactionVerification();
1046
+
1047
+ const edgeServer = await setupTestNode({ isSyncServer: true });
1048
+
1049
+ const { peerOnServer } = edgeServer.connectToSyncServer({
1050
+ syncServer: coreServer.node,
1051
+ ourName: "edge",
1052
+ syncServerName: "core",
1053
+ });
1054
+
1055
+ const client = setupTestNode();
1056
+
1057
+ client.connectToSyncServer({
1058
+ ourName: "client",
1059
+ syncServerName: "edge",
1060
+ syncServer: edgeServer.node,
1061
+ });
1062
+
1063
+ const group = coreServer.node.createGroup();
1064
+ group.addMember("everyone", "writer");
1065
+ const accountBlocker = blockMessageTypeOnOutgoingPeer(
1066
+ peerOnServer,
1067
+ "content",
1068
+ {
1069
+ id: coreServer.accountID,
1070
+ once: true,
1071
+ },
1072
+ );
1073
+ const groupBlocker = blockMessageTypeOnOutgoingPeer(
1074
+ peerOnServer,
1075
+ "content",
1076
+ {
1077
+ id: group.id,
1078
+ once: true,
1079
+ },
1080
+ );
1081
+
1082
+ const account = coreServer.node.expectCurrentAccount(coreServer.accountID);
1083
+
1084
+ const map = group.createMap();
1085
+ map.set("hello", "world");
1086
+
1087
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
1088
+ expect(mapOnClient.get("hello")).toEqual("world");
1089
+
1090
+ expect(
1091
+ SyncMessagesLog.getMessages({
1092
+ Account: account.core,
1093
+ Group: group.core,
1094
+ Map: map.core,
1095
+ }),
1096
+ ).toMatchInlineSnapshot(`
1097
+ [
1098
+ "client -> edge | LOAD Map sessions: empty",
1099
+ "edge -> core | LOAD Map sessions: empty",
1100
+ "core -> edge | CONTENT Map header: true new: After: 0 New: 1",
1101
+ "edge -> core | LOAD Group sessions: empty",
1102
+ "edge -> core | LOAD Account sessions: empty",
1103
+ "core -> edge | CONTENT Group header: true new: After: 0 New: 5",
1104
+ "core -> edge | CONTENT Account header: true new: After: 0 New: 4",
1105
+ "edge -> core | KNOWN Account sessions: header/4",
1106
+ "edge -> core | KNOWN Group sessions: header/5",
1107
+ "edge -> core | KNOWN Map sessions: header/1",
1108
+ "edge -> client | CONTENT Account header: true new: After: 0 New: 4",
1109
+ "edge -> client | CONTENT Group header: true new: After: 0 New: 5",
1110
+ "edge -> client | CONTENT Map header: true new: After: 0 New: 1",
1111
+ "client -> edge | KNOWN Account sessions: header/4",
1112
+ "client -> edge | KNOWN Group sessions: header/5",
1113
+ "client -> edge | KNOWN Map sessions: header/1",
1114
+ ]
1115
+ `);
1116
+
1117
+ accountBlocker.unblock();
1118
+ groupBlocker.unblock();
1119
+ });
1120
+
1121
+ test("edge servers should wait for all the dependencies to be available before sending an update", async () => {
1122
+ // Create a core -> edge -> client network
1123
+ const coreServer = setupTestNode({ isSyncServer: true });
1124
+
1125
+ const edgeServer = setupTestNode({ isSyncServer: true });
1126
+ const { peerOnServer: coreToEdgePeer } = edgeServer.connectToSyncServer({
1127
+ syncServer: coreServer.node,
1128
+ ourName: "edge",
1129
+ syncServerName: "core",
1130
+ });
1131
+
1132
+ const client = setupTestNode();
1133
+ client.connectToSyncServer({
1134
+ ourName: "client",
1135
+ syncServerName: "edge",
1136
+ });
1137
+
1138
+ // Create the group on the client
1139
+ const group = client.node.createGroup();
1140
+
1141
+ // Connect a new session and link it directly to the core
1142
+ const newSession = client.spawnNewSession();
1143
+ newSession.connectToSyncServer({
1144
+ syncServer: coreServer.node,
1145
+ ourName: "newSession",
1146
+ syncServerName: "core",
1147
+ });
1148
+
1149
+ // Load the group on the new client
1150
+ const groupOnNewSession = await loadCoValueOrFail(
1151
+ newSession.node,
1152
+ group.id,
1153
+ );
1154
+
1155
+ SyncMessagesLog.clear();
1156
+
1157
+ const parentGroup = newSession.node.createGroup();
1158
+ groupOnNewSession.extend(parentGroup);
1159
+
1160
+ // Block the content message from the core peer to simulate the situation where we won't push the dependency
1161
+ const blocker = blockMessageTypeOnOutgoingPeer(coreToEdgePeer, "content", {
1162
+ id: parentGroup.id,
1163
+ once: true,
1164
+ });
1165
+
1166
+ // Wait for the parent group to be available on client
1167
+ await waitFor(() => {
1168
+ expect(client.node.getCoValue(parentGroup.id).isAvailable()).toBe(true);
1169
+ });
1170
+
1171
+ // The edge server should wait for the parent group to be available before sending the group update
1172
+ expect(
1173
+ SyncMessagesLog.getMessages({
1174
+ ParentGroup: parentGroup.core,
1175
+ Group: group.core,
1176
+ }),
1177
+ ).toMatchInlineSnapshot(`
1178
+ [
1179
+ "newSession -> core | CONTENT ParentGroup header: true new: After: 0 New: 4",
1180
+ "newSession -> core | CONTENT Group header: false new: After: 0 New: 2",
1181
+ "core -> newSession | KNOWN ParentGroup sessions: header/4",
1182
+ "core -> newSession | KNOWN Group sessions: header/5",
1183
+ "core -> edge | CONTENT Group header: false new: After: 0 New: 2",
1184
+ "edge -> core | LOAD ParentGroup sessions: empty",
1185
+ "core -> edge | CONTENT ParentGroup header: true new: After: 0 New: 4",
1186
+ "edge -> core | KNOWN ParentGroup sessions: header/4",
1187
+ "edge -> core | KNOWN Group sessions: header/5",
1188
+ "edge -> client | CONTENT ParentGroup header: true new: After: 0 New: 4",
1189
+ "edge -> client | CONTENT Group header: false new: After: 0 New: 2",
1190
+ "client -> edge | KNOWN ParentGroup sessions: header/4",
1191
+ "client -> edge | KNOWN Group sessions: header/5",
1192
+ ]
1193
+ `);
1194
+
1195
+ blocker.unblock();
1196
+ });
1197
+
1198
+ test("edge servers should wait for all the session dependencies to be available before sending an update", async () => {
1199
+ // Create a core -> edge -> client network
1200
+ const coreServer = setupTestNode({ isSyncServer: true });
1201
+
1202
+ const edgeServer = setupTestNode({ isSyncServer: true });
1203
+ const { peerOnServer: coreToEdgePeer } = edgeServer.connectToSyncServer({
1204
+ syncServer: coreServer.node,
1205
+ ourName: "edge",
1206
+ syncServerName: "core",
1207
+ });
1208
+
1209
+ const client = setupTestNode();
1210
+ client.connectToSyncServer({
1211
+ ourName: "client",
1212
+ syncServerName: "edge",
1213
+ });
1214
+
1215
+ // Create the map on the client
1216
+ const group = client.node.createGroup();
1217
+ group.addMember("everyone", "writer");
1218
+
1219
+ const map = group.createMap();
1220
+ map.set("hello", "world");
1221
+
1222
+ // Connect a new client that uses an account and link it directly to the core
1223
+ const newAccountClient = await setupTestAccount();
1224
+ newAccountClient.connectToSyncServer({
1225
+ syncServer: coreServer.node,
1226
+ ourName: "newAccountClient",
1227
+ syncServerName: "core",
1228
+ });
1229
+ await newAccountClient.node.syncManager.waitForAllCoValuesSync();
1230
+
1231
+ // Load the map on the new client
1232
+ const mapOnNewAccountClient = await loadCoValueOrFail(
1233
+ newAccountClient.node,
1234
+ map.id,
1235
+ );
1236
+ const account = newAccountClient.node.expectCurrentAccount(
1237
+ newAccountClient.accountID,
1238
+ );
1239
+
1240
+ SyncMessagesLog.clear();
1241
+
1242
+ // Update the map on the new client, creating a new session
1243
+ mapOnNewAccountClient.set("newAccountClient", true);
1244
+
1245
+ // Block the content message from the core peer to simulate the situation where we won't push the dependency
1246
+ const blocker = blockMessageTypeOnOutgoingPeer(coreToEdgePeer, "content", {
1247
+ id: account.id,
1248
+ once: true,
1249
+ });
1250
+
1251
+ // Wait for the update to arrive on the initial client
1252
+ await waitFor(() => {
1253
+ const mapOnClient = expectMap(
1254
+ client.node.getCoValue(map.core.id).getCurrentContent(),
1255
+ );
1256
+ expect(mapOnClient.get("newAccountClient")).toBe(true);
1257
+ });
1258
+
1259
+ // The edge server should wait for the new Account to be available before sending the Map update
1260
+ expect(
1261
+ SyncMessagesLog.getMessages({
1262
+ Account: account.core,
1263
+ Group: group.core,
1264
+ Map: map.core,
1265
+ }),
1266
+ ).toMatchInlineSnapshot(`
1267
+ [
1268
+ "newAccountClient -> core | CONTENT Map header: false new: After: 0 New: 1",
1269
+ "core -> newAccountClient | KNOWN Map sessions: header/2",
1270
+ "core -> edge | CONTENT Map header: false new: After: 0 New: 1",
1271
+ "edge -> core | LOAD Account sessions: empty",
1272
+ "core -> edge | CONTENT Account header: true new: After: 0 New: 4",
1273
+ "edge -> core | KNOWN Account sessions: header/4",
1274
+ "edge -> core | KNOWN Map sessions: header/2",
1275
+ "edge -> client | CONTENT Account header: true new: After: 0 New: 4",
1276
+ "edge -> client | CONTENT Map header: false new: After: 0 New: 1",
1277
+ "client -> edge | KNOWN Account sessions: header/4",
1278
+ "client -> edge | KNOWN Map sessions: header/2",
1279
+ ]
1280
+ `);
1281
+
1282
+ blocker.unblock();
1283
+ });
1284
+
986
1285
  test("coValue with circular deps loading", async () => {
987
1286
  const client = setupTestNode({
988
1287
  connected: true,
@@ -203,9 +203,9 @@ describe("peer reconciliation", () => {
203
203
  "server -> client | KNOWN CORRECTION Map sessions: empty",
204
204
  "client -> server | CONTENT Map header: true new: After: 0 New: 2",
205
205
  "server -> client | LOAD Group sessions: empty",
206
- "server -> client | KNOWN Map sessions: header/2",
207
206
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
208
207
  "server -> client | KNOWN Group sessions: header/3",
208
+ "server -> client | KNOWN Map sessions: header/2",
209
209
  ]
210
210
  `);
211
211
  });
@@ -276,18 +276,19 @@ describe("peer reconciliation", () => {
276
276
  "client -> server | CONTENT Profile header: true new: After: 0 New: 1",
277
277
  "client -> server | CONTENT Map header: false new: After: 1 New: 1",
278
278
  "server -> client | LOAD Account sessions: empty",
279
- "server -> client | KNOWN ProfileGroup sessions: header/0",
280
- "server -> client | KNOWN Profile sessions: header/0",
281
- "server -> client | KNOWN CORRECTION Map sessions: empty",
279
+ "server -> client | LOAD ProfileGroup sessions: empty",
282
280
  "client -> server | CONTENT Account header: true new: After: 0 New: 4",
283
- "client -> server | CONTENT Map header: true new: After: 0 New: 2",
281
+ "client -> server | CONTENT ProfileGroup header: true new: After: 0 New: 5",
284
282
  "server -> client | KNOWN Account sessions: header/4",
285
283
  "server -> client | KNOWN ProfileGroup sessions: header/5",
284
+ "server -> client | KNOWN CORRECTION Map sessions: empty",
286
285
  "server -> client | KNOWN Profile sessions: header/1",
286
+ "server -> client | KNOWN ProfileGroup sessions: header/5",
287
+ "client -> server | CONTENT Map header: true new: After: 0 New: 2",
287
288
  "server -> client | LOAD Group sessions: empty",
288
- "server -> client | KNOWN Map sessions: header/2",
289
289
  "client -> server | CONTENT Group header: true new: After: 0 New: 3",
290
290
  "server -> client | KNOWN Group sessions: header/3",
291
+ "server -> client | KNOWN Map sessions: header/2",
291
292
  ]
292
293
  `);
293
294
  });
@@ -756,9 +756,11 @@ export function createAccountInNode(node: LocalNode) {
756
756
  });
757
757
 
758
758
  const accountCoreEntry = node.getCoValue(accountOnTempNode.id);
759
- accountCoreEntry.internalMarkMagicallyAvailable(
760
- accountOnTempNode.core.verified,
761
- );
759
+
760
+ const content =
761
+ accountOnTempNode.core.verified.newContentSince(undefined)?.[0]!;
762
+
763
+ node.syncManager.handleNewContent(content, "import");
762
764
 
763
765
  return new ControlledAccount(
764
766
  accountCoreEntry.getCurrentContent() as RawAccount,