Compare commits

...

24 Commits

Author SHA1 Message Date
Anselm Eickhoff
fac73f0e44 Merge pull request #726 from gardencmp/changeset-release/main
Version Packages
2024-11-08 16:42:11 +00:00
github-actions[bot]
80b04e78c9 Version Packages 2024-11-08 16:39:19 +00:00
Anselm Eickhoff
b23c0d75fe Merge pull request #725 from gardencmp/aeplay-jazz-477
Immediately ack new content before syncing it upstream
2024-11-08 16:38:03 +00:00
Anselm
d4319d8a0e Add changeset 2024-11-08 16:32:29 +00:00
Anselm
52cd4a9a0f Immediately ack new content before syncing it upstream 2024-11-08 16:31:52 +00:00
Anselm Eickhoff
ac241981f6 Merge pull request #722 from gardencmp/changeset-release/main
Version Packages
2024-11-08 12:09:49 +00:00
github-actions[bot]
b3504dce9c Version Packages 2024-11-08 12:07:47 +00:00
Anselm Eickhoff
f859d9f1c5 Merge pull request #711 from gardencmp/feature/non-optimistic-sync-state
feat: add a sync state subscription manager
2024-11-08 12:06:34 +00:00
Guido D'Orsi
d433cf473f chore: changeset 2024-11-08 12:03:25 +00:00
Guido D'Orsi
c6cd6cba20 Merge remote-tracking branch 'origin/main' into feature/non-optimistic-sync-state 2024-11-08 12:01:05 +00:00
Guido D'Orsi
9c7333d4f0 fix(createWorkerAccount): use the new sync wait API 2024-11-08 12:00:55 +00:00
Anselm Eickhoff
970f870245 Merge pull request #720 from gardencmp/aeplay-jazz-475
Less noisy logs if clients send empty WebSocket messages
2024-11-08 11:42:30 +00:00
Anselm
a2dbaf86d2 Fix unused var 2024-11-08 11:38:24 +00:00
Anselm
b6162f0fc4 Add changeset 2024-11-08 11:34:52 +00:00
Anselm
91cfa6aa8f Less noisy logs if clients send empty WebSocket messages 2024-11-08 11:34:28 +00:00
Guido D'Orsi
4fd6bbd41d Merge remote-tracking branch 'origin/main' into feature/non-optimistic-sync-state 2024-11-08 09:57:46 +00:00
Guido D'Orsi
0ea3ef31ac chore(PeerState): explain the difference between knownState and optimisticKnownState 2024-11-07 10:11:32 +00:00
Guido D'Orsi
d02613e2ec chore: add jazz-tools.json to the biome ignore 2024-11-07 09:21:42 +00:00
Guido D'Orsi
8ff036e5ff test: cover the content acknowledgment flow 2024-11-07 09:15:09 +00:00
Guido D'Orsi
d0733e2a3b chore: revert jazz-run changes 2024-11-07 00:25:31 +00:00
Guido D'Orsi
ad4f1b74f6 chore: fix type error 2024-11-07 00:20:26 +00:00
Guido D'Orsi
04d97e4a2d chore: format 2024-11-07 00:17:03 +00:00
Guido D'Orsi
3f9e749122 Merge remote-tracking branch 'origin/main' into feature/non-optimistic-sync-state 2024-11-07 00:12:56 +00:00
Guido D'Orsi
aa1dd3e10a feat: add a sync state subscription manager 2024-11-07 00:06:30 +00:00
68 changed files with 1398 additions and 200 deletions

View File

@@ -7,7 +7,7 @@
},
"files": {
"ignoreUnknown": false,
"ignore": []
"ignore": ["jazz-tools.json"]
},
"formatter": {
"enabled": true,

View File

@@ -1,5 +1,23 @@
# @jazz-e2e/binarycostream
## 0.0.96
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.95
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.94
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/binarycostream",
"private": true,
"version": "0.0.94",
"version": "0.0.96",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,11 +13,11 @@
"test:ui": "playwright test --ui"
},
"dependencies": {
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"hash-slash": "workspace:0.2.1",
"is-ci": "^3.0.1",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},

View File

@@ -1,5 +1,23 @@
# @jazz-e2e/covalues
## 0.0.95
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.94
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.93
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/covalues",
"private": true,
"version": "0.0.93",
"version": "0.0.95",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,21 @@
# jazz-example-book-shelf
## 0.1.11
### Patch Changes
- jazz-react@0.8.18
- jazz-tools@0.8.18
- jazz-browser-media-images@0.8.18
## 0.1.10
### Patch Changes
- jazz-react@0.8.17
- jazz-tools@0.8.17
- jazz-browser-media-images@0.8.17
## 0.1.9
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-example-book-shelf",
"version": "0.1.9",
"version": "0.1.11",
"private": true,
"scripts": {
"dev": "next dev",
@@ -11,9 +11,9 @@
},
"dependencies": {
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.16",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-browser-media-images": "workspace:0.8.18",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"

View File

@@ -1,5 +1,25 @@
# jazz-example-chat
## 0.0.95
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-react@0.8.18
- jazz-react-auth-clerk@0.8.18
- jazz-tools@0.8.18
## 0.0.94
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-react@0.8.17
- jazz-react-auth-clerk@0.8.17
- jazz-tools@0.8.17
## 0.0.93
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat-clerk",
"private": true,
"version": "0.0.93",
"version": "0.0.95",
"type": "module",
"scripts": {
"dev": "vite",
@@ -17,11 +17,11 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"hash-slash": "workspace:0.2.1",
"jazz-react": "workspace:0.8.16",
"jazz-react-auth-clerk": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-react-auth-clerk": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,5 +1,23 @@
# chat-rn-clerk
## 1.0.11
### Patch Changes
- jazz-react-auth-clerk@0.8.18
- jazz-react-native@0.8.18
- jazz-tools@0.8.18
- jazz-react-native-media-images@0.8.14
## 1.0.10
### Patch Changes
- jazz-react-auth-clerk@0.8.17
- jazz-react-native@0.8.17
- jazz-tools@0.8.17
- jazz-react-native-media-images@0.8.13
## 1.0.9
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-clerk",
"main": "index.js",
"version": "1.0.9",
"version": "1.0.11",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",

View File

@@ -1,5 +1,19 @@
# chat-rn
## 1.0.13
### Patch Changes
- jazz-react-native@0.8.18
- jazz-tools@0.8.18
## 1.0.12
### Patch Changes
- jazz-react-native@0.8.17
- jazz-tools@0.8.17
## 1.0.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.11",
"version": "1.0.13",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",

View File

@@ -1,5 +1,21 @@
# chat-vue
## 0.0.3
### Patch Changes
- jazz-browser@0.8.18
- jazz-tools@0.8.18
- jazz-vue@0.8.8
## 0.0.2
### Patch Changes
- jazz-browser@0.8.17
- jazz-tools@0.8.17
- jazz-vue@0.8.7
## 0.0.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-vue",
"version": "0.0.1",
"version": "0.0.3",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,23 @@
# jazz-example-chat
## 0.0.97
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.96
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.95
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.95",
"version": "0.0.97",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,10 +18,10 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"hash-slash": "workspace:0.2.1",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,5 +1,22 @@
# jazz-example-inspector
## 0.0.71
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- cojson-transport-ws@0.8.18
## 0.0.70
### Patch Changes
- Updated dependencies [d433cf4]
- Updated dependencies [b6162f0]
- cojson@0.8.17
- cojson-transport-ws@0.8.17
## 0.0.69
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector",
"private": true,
"version": "0.0.69",
"version": "0.0.71",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.8.16",
"cojson-transport-ws": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"cojson-transport-ws": "workspace:0.8.18",
"hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -1,5 +1,19 @@
# jazz-example-musicplayer
## 0.0.17
### Patch Changes
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.16
### Patch Changes
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.15
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.15",
"version": "0.0.17",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,8 +18,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"lucide-react": "^0.274.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -1,5 +1,19 @@
# jazz-password-manager
## 0.0.16
### Patch Changes
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.15
### Patch Changes
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.14
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.14",
"version": "0.0.16",
"type": "module",
"scripts": {
"dev": "vite",
@@ -12,8 +12,8 @@
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
},
"dependencies": {
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.41.5",

View File

@@ -1,5 +1,21 @@
# jazz-example-pets
## 0.0.114
### Patch Changes
- jazz-react@0.8.18
- jazz-tools@0.8.18
- jazz-browser-media-images@0.8.18
## 0.0.113
### Patch Changes
- jazz-react@0.8.17
- jazz-tools@0.8.17
- jazz-browser-media-images@0.8.17
## 0.0.112
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.112",
"version": "0.0.114",
"type": "module",
"scripts": {
"dev": "vite",
@@ -19,9 +19,9 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.16",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-browser-media-images": "workspace:0.8.18",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",
@@ -41,7 +41,7 @@
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.14",
"is-ci": "^3.0.1",
"jazz-run": "workspace:0.8.16",
"jazz-run": "workspace:0.8.18",
"postcss": "^8.4.27",
"tailwindcss": "3.3.2",
"typescript": "^5.3.3",

View File

@@ -1,5 +1,19 @@
# jazz-example-todo
## 0.0.113
### Patch Changes
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.0.112
### Patch Changes
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.0.111
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.111",
"version": "0.0.113",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -1,5 +1,19 @@
# cojson-storage-indexeddb
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,12 +1,12 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.8.16",
"version": "0.8.18",
"main": "dist/index.js",
"type": "module",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -1,5 +1,19 @@
# cojson-storage-sqlite
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,13 +1,13 @@
{
"name": "cojson-storage-sqlite",
"type": "module",
"version": "0.8.16",
"version": "0.8.18",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"better-sqlite3": "^8.5.2",
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -1,5 +1,20 @@
# cojson-transport-nodejs-ws
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
## 0.8.17
### Patch Changes
- b6162f0: Less noisy logs if clients send empty WebSocket messages
- Updated dependencies [d433cf4]
- cojson@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,12 +1,12 @@
{
"name": "cojson-transport-ws",
"type": "module",
"version": "0.8.16",
"version": "0.8.18",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"typescript": "^5.3.3"
},
"scripts": {

View File

@@ -147,6 +147,11 @@ export function createWebSocketPeer({
);
function handleIncomingMsg(event: { data: unknown }) {
if (event.data === "") {
console.log("client", id, "sent empty message");
return;
}
const result = deserializeMessages(event.data);
if (!result.ok) {

View File

@@ -1,5 +1,17 @@
# cojson
## 0.8.18
### Patch Changes
- d4319d8: Immediately ack new content before syncing it upstream
## 0.8.17
### Patch Changes
- d433cf4: Add a new API to wait for a CoValue to be uploaded in a peer
## 0.8.16
### Patch Changes

View File

@@ -19,7 +19,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.8.16",
"version": "0.8.18",
"devDependencies": {
"@types/jest": "^29.5.3",
"typescript": "^5.3.3",

View File

@@ -66,6 +66,12 @@ export class PeerKnownStates {
return this.coValues.has(id);
}
clone() {
const clone = new PeerKnownStates();
clone.coValues = new Map(this.coValues);
return clone;
}
dispatch(action: PeerKnownStateActions) {
switch (action.type) {
case "UPDATE_HEADER":

View File

@@ -8,9 +8,29 @@ import { CO_VALUE_PRIORITY } from "./priority.js";
import { Peer, SyncMessage } from "./sync.js";
export class PeerState {
constructor(private peer: Peer) {}
constructor(
private peer: Peer,
knownStates: PeerKnownStates | undefined,
) {
this.optimisticKnownStates = knownStates?.clone() ?? new PeerKnownStates();
this.knownStates = knownStates?.clone() ?? new PeerKnownStates();
}
readonly optimisticKnownStates = new PeerKnownStates();
/**
* Here we to collect all the known states that a given peer has told us about.
*
* This can be used to safely track the sync state of a coValue in a given peer.
*/
readonly knownStates: PeerKnownStates;
/**
* This one collects the known states "optimistically".
* We use it to keep track of the content we have sent to a given peer.
*
* The main difference with knownState is that this is updated when the content is sent to the peer without
* waiting for any acknowledgement from the peer.
*/
readonly optimisticKnownStates: PeerKnownStates;
readonly toldKnownState: Set<RawCoID> = new Set();
get id() {

View File

@@ -0,0 +1,124 @@
import { RawCoID } from "./ids.js";
import {
CoValueKnownState,
PeerID,
SyncManager,
emptyKnownState,
} from "./sync.js";
export class SyncStateSubscriptionManager {
constructor(private syncManager: SyncManager) {}
private listeners = new Set<
(
peerId: PeerID,
knownState: CoValueKnownState,
uploadCompleted: boolean,
) => void
>();
private listenersByPeers = new Map<
PeerID,
Set<(knownState: CoValueKnownState, uploadCompleted: boolean) => void>
>();
subscribeToUpdates(
listener: (
peerId: PeerID,
knownState: CoValueKnownState,
uploadCompleted: boolean,
) => void,
) {
this.listeners.add(listener);
return () => {
this.listeners.delete(listener);
};
}
subscribeToPeerUpdates(
peerId: PeerID,
listener: (knownState: CoValueKnownState, uploadCompleted: boolean) => void,
) {
const listeners = this.listenersByPeers.get(peerId) ?? new Set();
if (listeners.size === 0) {
this.listenersByPeers.set(peerId, listeners);
}
listeners.add(listener);
return () => {
listeners.delete(listener);
};
}
triggerUpdate(peerId: PeerID, id: RawCoID) {
const peer = this.syncManager.peers[peerId];
if (!peer) {
return;
}
const peerListeners = this.listenersByPeers.get(peer.id);
// If we don't have any active listeners do nothing
if (!peerListeners?.size && !this.listeners.size) {
return;
}
const knownState = peer.knownStates.get(id) ?? emptyKnownState(id);
const fullyUploadedIntoPeer = this.getIsCoValueFullyUploadedIntoPeer(
peerId,
id,
);
for (const listener of this.listeners) {
listener(peerId, knownState, fullyUploadedIntoPeer);
}
if (!peerListeners) return;
for (const listener of peerListeners) {
listener(knownState, fullyUploadedIntoPeer);
}
}
getIsCoValueFullyUploadedIntoPeer(peerId: PeerID, id: RawCoID) {
const peer = this.syncManager.peers[peerId];
const entry = this.syncManager.local.coValues[id];
if (!peer || !entry) {
return false;
}
if (entry.state.type !== "available") {
return false;
}
const coValue = entry.state.coValue;
const knownState = peer.knownStates.get(id);
if (!knownState) {
return false;
}
return getIsUploadCompleted(
coValue.knownState().sessions,
knownState.sessions,
);
}
}
function getIsUploadCompleted(
from: Record<string, number>,
to: Record<string, number>,
) {
for (const sessionId of Object.keys(from)) {
if (from[sessionId] !== to[sessionId]) {
return false;
}
}
return true;
}

View File

@@ -1,4 +1,4 @@
import { Result, err, ok } from "neverthrow";
import { Result, ok } from "neverthrow";
import { CoID, RawCoValue } from "../coValue.js";
import {
CoValueCore,

View File

@@ -1,4 +1,5 @@
import { PeerState } from "./PeerState.js";
import { SyncStateSubscriptionManager } from "./SyncStateSubscriptionManager.js";
import { CoValueHeader, Transaction } from "./coValueCore.js";
import { CoValueCore } from "./coValueCore.js";
import { CoValueState } from "./coValueState.js";
@@ -116,8 +117,11 @@ export class SyncManager {
constructor(local: LocalNode) {
this.local = local;
this.syncStateSubscriptionManager = new SyncStateSubscriptionManager(this);
}
syncStateSubscriptionManager: SyncStateSubscriptionManager;
peersInPriorityOrder(): PeerState[] {
return Object.values(this.peers).sort((a, b) => {
const aPriority = a.priority || 0;
@@ -127,6 +131,10 @@ export class SyncManager {
});
}
getPeers(): PeerState[] {
return Object.values(this.peers);
}
async loadFromPeers(id: RawCoID, forPeer?: PeerID) {
const eligiblePeers = this.peersInPriorityOrder().filter(
(peer) => peer.id !== forPeer && peer.isServerOrStoragePeer(),
@@ -298,9 +306,20 @@ export class SyncManager {
}
addPeer(peer: Peer) {
const peerState = new PeerState(peer);
const prevPeer = this.peers[peer.id];
const peerState = new PeerState(peer, prevPeer?.knownStates);
this.peers[peer.id] = peerState;
if (prevPeer && !prevPeer.closed) {
prevPeer.gracefulShutdown();
}
const unsubscribeFromKnownStatesUpdates = peerState.knownStates.subscribe(
(id) => {
this.syncStateSubscriptionManager.triggerUpdate(peer.id, id);
},
);
if (peerState.isServerOrStoragePeer()) {
const initialSync = async () => {
for (const id of Object.keys(this.local.coValues) as RawCoID[]) {
@@ -358,8 +377,9 @@ export class SyncManager {
}
})
.finally(() => {
peer.outgoing.close();
delete this.peers[peer.id];
const state = this.peers[peer.id];
state?.gracefulShutdown();
unsubscribeFromKnownStatesUpdates();
});
}
@@ -455,6 +475,12 @@ export class SyncManager {
value: knownStateIn(msg),
});
peer.knownStates.dispatch({
type: "COMBINE_WITH",
id: msg.id,
value: knownStateIn(msg),
});
if (!entry) {
if (msg.asDependencyOf) {
if (this.local.coValues[msg.asDependencyOf]) {
@@ -616,8 +642,6 @@ export class SyncManager {
});
}
await this.syncCoValue(coValue);
if (invalidStateAssumed) {
this.trySendToPeer(peer, {
action: "known",
@@ -626,7 +650,23 @@ export class SyncManager {
}).catch((e) => {
console.error("Error sending known state correction", e);
});
} else {
/**
* We are sending a known state message to the peer to acknowledge the
* receipt of the new content.
*
* This way the sender knows that the content has been received and applied
* and can update their peer's knownState accordingly.
*/
this.trySendToPeer(peer, {
action: "known",
...coValue.knownState(),
}).catch((e: unknown) => {
console.error("Error sending known state", e);
});
}
await this.syncCoValue(coValue);
}
async handleCorrection(msg: KnownStateMessage, peer: PeerState) {
@@ -670,6 +710,7 @@ export class SyncManager {
async actuallySyncCoValue(coValue: CoValueCore) {
// let blockingSince = performance.now();
for (const peer of this.peersInPriorityOrder()) {
if (peer.closed) continue;
// if (performance.now() - blockingSince > 5) {
// await new Promise<void>((resolve) => {
// setTimeout(resolve, 0);
@@ -684,6 +725,35 @@ export class SyncManager {
await this.sendNewContentIncludingDependencies(coValue.id, peer);
}
}
for (const peer of this.getPeers()) {
this.syncStateSubscriptionManager.triggerUpdate(peer.id, coValue.id);
}
}
async waitForUploadIntoPeer(peerId: PeerID, id: RawCoID) {
const isAlreadyUploaded =
this.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
peerId,
id,
);
if (isAlreadyUploaded) {
return true;
}
return new Promise((resolve) => {
const unsubscribe =
this.syncStateSubscriptionManager.subscribeToPeerUpdates(
peerId,
(knownState, uploadCompleted) => {
if (uploadCompleted && knownState.id === id) {
resolve(true);
unsubscribe?.();
}
},
);
});
}
gracefulShutdown() {

View File

@@ -0,0 +1,232 @@
import { describe, expect, onTestFinished, test, vi } from "vitest";
import { connectedPeers } from "../streamUtils.js";
import { emptyKnownState } from "../sync.js";
import { createTestNode, waitFor } from "./testUtils.js";
describe("SyncStateSubscriptionManager", () => {
test("subscribeToUpdates receives updates when peer state changes", async () => {
// Setup nodes
const client = createTestNode();
const jazzCloud = createTestNode();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
// Connect nodes
const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
"clientConnection",
"jazzCloudConnection",
{
peer1role: "client",
peer2role: "server",
},
);
client.syncManager.addPeer(jazzCloudAsPeer);
jazzCloud.syncManager.addPeer(clientAsPeer);
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
const updateSpy = vi.fn();
const unsubscribe = subscriptionManager.subscribeToUpdates(updateSpy);
await client.syncManager.actuallySyncCoValue(map.core);
expect(updateSpy).toHaveBeenCalledWith(
"jazzCloudConnection",
emptyKnownState(map.core.id),
false,
);
await waitFor(() => {
return subscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
expect(updateSpy).toHaveBeenCalledWith(
"jazzCloudConnection",
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
map.core.id,
)!,
true,
);
// Cleanup
unsubscribe();
});
test("subscribeToPeerUpdates receives updates only for specific peer", async () => {
// Setup nodes
const client = createTestNode();
const jazzCloud = createTestNode();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
// Connect nodes
const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
"clientConnection",
"jazzCloudConnection",
{
peer1role: "client",
peer2role: "server",
},
);
const [clientStoragePeer] = connectedPeers("clientStorage", "unusedPeer", {
peer1role: "client",
peer2role: "server",
});
client.syncManager.addPeer(jazzCloudAsPeer);
client.syncManager.addPeer(clientStoragePeer);
jazzCloud.syncManager.addPeer(clientAsPeer);
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
const updateToJazzCloudSpy = vi.fn();
const updateToStorageSpy = vi.fn();
const unsubscribe1 = subscriptionManager.subscribeToPeerUpdates(
"jazzCloudConnection",
updateToJazzCloudSpy,
);
const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
"clientStorage",
updateToStorageSpy,
);
onTestFinished(() => {
unsubscribe1();
unsubscribe2();
});
await client.syncManager.actuallySyncCoValue(map.core);
expect(updateToJazzCloudSpy).toHaveBeenCalledWith(
emptyKnownState(map.core.id),
false,
);
await waitFor(() => {
return subscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
expect(updateToJazzCloudSpy).toHaveBeenLastCalledWith(
client.syncManager.peers["jazzCloudConnection"]!.knownStates.get(
map.core.id,
)!,
true,
);
expect(updateToStorageSpy).toHaveBeenLastCalledWith(
emptyKnownState(map.core.id),
false,
);
});
test("getIsCoValueFullyUploadedIntoPeer returns correct status", async () => {
// Setup nodes
const client = createTestNode();
const jazzCloud = createTestNode();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
// Connect nodes
const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
"clientConnection",
"jazzCloudConnection",
{
peer1role: "client",
peer2role: "server",
},
);
client.syncManager.addPeer(jazzCloudAsPeer);
jazzCloud.syncManager.addPeer(clientAsPeer);
await client.syncManager.actuallySyncCoValue(map.core);
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
expect(
subscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
),
).toBe(false);
await waitFor(() => {
return subscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
expect(
subscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
),
).toBe(true);
});
test("unsubscribe stops receiving updates", async () => {
// Setup nodes
const client = createTestNode();
const jazzCloud = createTestNode();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
// Connect nodes
const [clientAsPeer, jazzCloudAsPeer] = connectedPeers(
"clientConnection",
"jazzCloudConnection",
{
peer1role: "client",
peer2role: "server",
},
);
client.syncManager.addPeer(jazzCloudAsPeer);
jazzCloud.syncManager.addPeer(clientAsPeer);
const subscriptionManager = client.syncManager.syncStateSubscriptionManager;
const anyUpdateSpy = vi.fn();
const unsubscribe1 = subscriptionManager.subscribeToUpdates(anyUpdateSpy);
const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
"jazzCloudConnection",
anyUpdateSpy,
);
unsubscribe1();
unsubscribe2();
await client.syncManager.actuallySyncCoValue(map.core);
anyUpdateSpy.mockClear();
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
expect(anyUpdateSpy).not.toHaveBeenCalled();
});
});

View File

@@ -1,4 +1,4 @@
import { describe, expect, test } from "vitest";
import { describe, expect, test, vi } from "vitest";
import { expectMap } from "../coValue.js";
import { CoValueHeader } from "../coValueCore.js";
import { RawAccountID } from "../coValues/account.js";
@@ -10,7 +10,11 @@ import { LocalNode } from "../localNode.js";
import { getPriorityFromHeader } from "../priority.js";
import { connectedPeers, newQueuePair } from "../streamUtils.js";
import { SyncMessage } from "../sync.js";
import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
import {
createTestNode,
randomAnonymousAccountAndSessionID,
waitFor,
} from "./testUtils.js";
const Crypto = await WasmCrypto.create();
@@ -1561,6 +1565,303 @@ describe("sync - extra tests", () => {
});
});
function createTwoConnectedNodes() {
// Setup nodes
const client = createTestNode();
const jazzCloud = createTestNode();
// Connect nodes initially
const [connectionWithClientAsPeer, jazzCloudConnectionAsPeer] =
connectedPeers("connectionWithClient", "jazzCloudConnection", {
peer1role: "client",
peer2role: "server",
});
client.syncManager.addPeer(jazzCloudConnectionAsPeer);
jazzCloud.syncManager.addPeer(connectionWithClientAsPeer);
return {
client,
jazzCloud,
connectionWithClientAsPeer,
jazzCloudConnectionAsPeer,
};
}
describe("SyncManager - knownStates vs optimisticKnownStates", () => {
test("knownStates and optimisticKnownStates are the same when the coValue is fully synced", async () => {
const { client } = createTwoConnectedNodes();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
// Wait for the full sync to complete
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
const peerState = client.syncManager.peers["jazzCloudConnection"]!;
// The optimisticKnownStates should be the same as the knownStates after the full sync is complete
expect(peerState.optimisticKnownStates.get(map.core.id)).toEqual(
peerState.knownStates.get(map.core.id),
);
});
test("optimisticKnownStates is updated as new transactions are received, while knownStates only when the coValue is fully synced", async () => {
const { client, jazzCloudConnectionAsPeer } = createTwoConnectedNodes();
// Create test data and sync the first change
// We want that both the nodes know about the coValue so we can test
// the content acknowledgement flow.
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
// Block the content messages
// The main difference between optimisticKnownStates and knownStates is that
// optimisticKnownStates is updated when the content messages are sent,
// while knownStates is only updated when we receive the "known" messages
// that are acknowledging the receipt of the content messages
const push = jazzCloudConnectionAsPeer.outgoing.push;
const pushSpy = vi.spyOn(jazzCloudConnectionAsPeer.outgoing, "push");
const blockedMessages: SyncMessage[] = [];
pushSpy.mockImplementation(async (msg) => {
if (msg.action === "content") {
blockedMessages.push(msg);
return Promise.resolve();
}
return push.call(jazzCloudConnectionAsPeer.outgoing, msg);
});
map.set("key2", "value2", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
const peerState = client.syncManager.peers["jazzCloudConnection"]!;
expect(peerState.optimisticKnownStates.get(map.core.id)).not.toEqual(
peerState.knownStates.get(map.core.id),
);
// Restore the implementation of push and send the blocked messages
// After this the full sync can be completed and the other node will
// respond with a "known" message acknowledging the receipt of the content messages
pushSpy.mockRestore();
for (const msg of blockedMessages) {
await jazzCloudConnectionAsPeer.outgoing.push(msg);
}
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
expect(peerState.optimisticKnownStates.get(map.core.id)).toEqual(
peerState.knownStates.get(map.core.id),
);
});
});
describe("SyncManager.addPeer", () => {
test("new peer gets a copy of previous peer's knownStates when replacing it", async () => {
const { client } = createTwoConnectedNodes();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
// Wait for initial sync
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
// Store the initial known states
const initialKnownStates =
client.syncManager.peers["jazzCloudConnection"]!.knownStates;
// Create new connection with same ID
const [jazzCloudConnectionAsPeer2] = connectedPeers(
"jazzCloudConnection",
"unusedPeer",
{
peer1role: "server",
peer2role: "client",
},
);
// Add new peer with same ID
client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
// Verify that the new peer has a copy of the previous known states
const newPeerKnownStates =
client.syncManager.peers["jazzCloudConnection"]!.knownStates;
expect(newPeerKnownStates).not.toBe(initialKnownStates); // Should be a different instance
expect(newPeerKnownStates.get(map.core.id)).toEqual(
initialKnownStates.get(map.core.id),
);
});
test("new peer with new ID starts with empty knownStates", async () => {
const { client } = createTwoConnectedNodes();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
// Wait for initial sync
await waitFor(() => {
return client.syncManager.syncStateSubscriptionManager.getIsCoValueFullyUploadedIntoPeer(
"jazzCloudConnection",
map.core.id,
);
});
// Connect second peer with different ID
const [brandNewPeer] = connectedPeers("brandNewPeer", "unusedPeer", {
peer1role: "client",
peer2role: "server",
});
// Add new peer with different ID
client.syncManager.addPeer(brandNewPeer);
// Verify that the new peer starts with empty known states
const newPeerKnownStates =
client.syncManager.peers["brandNewPeer"]!.knownStates;
expect(newPeerKnownStates.get(map.core.id)).toBe(undefined);
});
test("when adding a peer with the same ID as a previous peer, the previous peer is closed", async () => {
const { client } = createTwoConnectedNodes();
// Store reference to first peer
const firstPeer = client.syncManager.peers["jazzCloudConnection"]!;
const closeSpy = vi.spyOn(firstPeer, "gracefulShutdown");
// Create and add replacement peer
const [jazzCloudConnectionAsPeer2] = connectedPeers(
"jazzCloudConnection",
"unusedPeer",
{
peer1role: "server",
peer2role: "client",
},
);
client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
// Verify thet the first peer had ben closed correctly
expect(closeSpy).toHaveBeenCalled();
expect(firstPeer.closed).toBe(true);
});
test("when adding a peer with the same ID as a previous peer and the previous peer is closed, do not attempt to close it again", async () => {
const { client } = createTwoConnectedNodes();
// Store reference to first peer
const firstPeer = client.syncManager.peers["jazzCloudConnection"]!;
firstPeer.gracefulShutdown();
const closeSpy = vi.spyOn(firstPeer, "gracefulShutdown");
// Create and add replacement peer
const [jazzCloudConnectionAsPeer2] = connectedPeers(
"jazzCloudConnection",
"unusedPeer",
{
peer1role: "server",
peer2role: "client",
},
);
client.syncManager.addPeer(jazzCloudConnectionAsPeer2);
// Verify thet the first peer had not been closed again
expect(closeSpy).not.toHaveBeenCalled();
expect(firstPeer.closed).toBe(true);
});
});
describe("waitForUploadIntoPeer", () => {
test("should resolve when the coValue is fully uploaded into the peer", async () => {
const { client, jazzCloudConnectionAsPeer: peer } =
createTwoConnectedNodes();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
await client.syncManager.actuallySyncCoValue(map.core);
await expect(
Promise.race([
client.syncManager.waitForUploadIntoPeer(peer.id, map.core.id),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), 100),
),
]),
).resolves.toBe(true);
});
test("should not resolve when the coValue is not synced", async () => {
const { client, jazzCloudConnectionAsPeer: peer } =
createTwoConnectedNodes();
// Create test data
const group = client.createGroup();
const map = group.createMap();
map.set("key1", "value1", "trusting");
vi.spyOn(peer.outgoing, "push").mockImplementation(async () => {
return Promise.resolve();
});
await client.syncManager.actuallySyncCoValue(map.core);
await expect(
Promise.race([
client.syncManager.waitForUploadIntoPeer(peer.id, map.core.id),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), 100),
),
]),
).rejects.toThrow("Timeout");
});
});
function groupContentEx(group: RawGroup) {
return {
action: "content",

View File

@@ -18,6 +18,11 @@ export function randomAnonymousAccountAndSessionID(): [
return [new ControlledAgent(agentSecret, Crypto), sessionID];
}
export function createTestNode() {
const [admin, session] = randomAnonymousAccountAndSessionID();
return new LocalNode(admin, session, Crypto);
}
export function newGroup() {
const [admin, sessionID] = randomAnonymousAccountAndSessionID();
@@ -93,3 +98,31 @@ export function shouldNotResolve<T>(
setTimeout(resolve, ops.timeout);
});
}
export function waitFor(callback: () => boolean | void) {
return new Promise<void>((resolve, reject) => {
const checkPassed = () => {
try {
return { ok: callback(), error: null };
} catch (error) {
return { ok: false, error };
}
};
let retries = 0;
const interval = setInterval(() => {
const { ok, error } = checkPassed();
if (ok !== false) {
clearInterval(interval);
resolve();
}
if (++retries > 10) {
clearInterval(interval);
reject(error);
}
}, 100);
});
}

View File

@@ -1,5 +1,23 @@
# jazz-browser-media-images
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-browser@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-browser@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,14 +1,14 @@
{
"name": "jazz-browser-auth-clerk",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.16",
"jazz-browser": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16"
"cojson": "workspace:0.8.18",
"jazz-browser": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18"
},
"scripts": {
"format-and-lint": "biome check .",

View File

@@ -1,5 +1,19 @@
# jazz-browser-media-images
## 0.8.18
### Patch Changes
- jazz-browser@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- jazz-browser@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-browser-media-images",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -8,8 +8,8 @@
"dependencies": {
"@types/image-blob-reduce": "^4.1.1",
"image-blob-reduce": "^4.1.0",
"jazz-browser": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"jazz-browser": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"pica": "^9.0.1",
"typescript": "^5.3.3"
},

View File

@@ -1,5 +1,26 @@
# jazz-browser
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- cojson-storage-indexeddb@0.8.18
- cojson-transport-ws@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- Updated dependencies [b6162f0]
- cojson@0.8.17
- cojson-transport-ws@0.8.17
- cojson-storage-indexeddb@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,16 +1,16 @@
{
"name": "jazz-browser",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:0.8.16",
"cojson-storage-indexeddb": "workspace:0.8.16",
"cojson-transport-ws": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"cojson-storage-indexeddb": "workspace:0.8.18",
"cojson-transport-ws": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"typescript": "^5.3.3"
},
"scripts": {

View File

@@ -1,5 +1,24 @@
# jazz-autosub
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- cojson-transport-ws@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- Updated dependencies [b6162f0]
- cojson@0.8.17
- cojson-transport-ws@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -5,11 +5,11 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.8.16",
"version": "0.8.18",
"dependencies": {
"cojson": "workspace:0.8.16",
"cojson-transport-ws": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"cojson-transport-ws": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -1,5 +1,25 @@
# jazz-browser-media-images
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-browser-auth-clerk@0.8.18
- jazz-react@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-browser-auth-clerk@0.8.17
- jazz-react@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,15 +1,15 @@
{
"name": "jazz-react-auth-clerk",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",
"license": "MIT",
"dependencies": {
"cojson": "workspace:0.8.16",
"jazz-browser-auth-clerk": "workspace:0.8.16",
"jazz-react": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16"
"cojson": "workspace:0.8.18",
"jazz-browser-auth-clerk": "workspace:0.8.18",
"jazz-react": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18"
},
"peerDependencies": {
"react": "^18.2.0"

View File

@@ -1,5 +1,17 @@
# jazz-browser-media-images
## 0.8.14
### Patch Changes
- jazz-tools@0.8.18
## 0.8.13
### Patch Changes
- jazz-tools@0.8.17
## 0.8.12
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-native-media-images",
"version": "0.8.12",
"version": "0.8.14",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,24 @@
# jazz-browser
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- cojson-transport-ws@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- Updated dependencies [b6162f0]
- cojson@0.8.17
- cojson-transport-ws@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-native",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",

View File

@@ -1,5 +1,23 @@
# jazz-react
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-browser@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-browser@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -1,15 +1,15 @@
{
"name": "jazz-react",
"version": "0.8.16",
"version": "0.8.18",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",
"license": "MIT",
"dependencies": {
"@scure/bip39": "^1.3.0",
"cojson": "workspace:0.8.16",
"jazz-browser": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"jazz-browser": "workspace:0.8.18",
"jazz-tools": "workspace:0.8.18",
"typescript": "^5.3.3"
},
"devDependencies": {

View File

@@ -1,5 +1,27 @@
# jazz-run
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- cojson-storage-sqlite@0.8.18
- cojson-transport-ws@0.8.18
- jazz-tools@0.8.18
## 0.8.17
### Patch Changes
- d433cf4: Improve the sync wait using the new API from the sync manager
- Updated dependencies [d433cf4]
- Updated dependencies [b6162f0]
- cojson@0.8.17
- cojson-transport-ws@0.8.17
- cojson-storage-sqlite@0.8.17
- jazz-tools@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.8.16",
"version": "0.8.18",
"scripts": {
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write",
@@ -18,11 +18,11 @@
"@effect/printer-ansi": "^0.34.5",
"@effect/schema": "^0.71.1",
"@effect/typeclass": "^0.25.5",
"cojson": "workspace:0.8.16",
"cojson-storage-sqlite": "workspace:0.8.16",
"cojson-transport-ws": "workspace:0.8.16",
"cojson": "workspace:0.8.18",
"cojson-storage-sqlite": "workspace:0.8.18",
"cojson-transport-ws": "workspace:0.8.18",
"effect": "^3.6.5",
"jazz-tools": "workspace:0.8.16",
"jazz-tools": "workspace:0.8.18",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -47,8 +47,8 @@ export const createWorkerAccount = async ({
await Promise.race([
Promise.all([
waitForSync(account, peer, accountCoValue),
waitForSync(account, peer, accountProfileCoValue),
syncManager.waitForUploadIntoPeer(peer.id, accountCoValue.id),
syncManager.waitForUploadIntoPeer(peer.id, accountProfileCoValue.id),
]),
failAfter(
4_000,
@@ -73,7 +73,7 @@ export const createWorkerAccount = async ({
peersToLoadFrom: [peer2],
crypto,
}),
failAfter(4_000, "Timeout: Account loading check failed"),
failAfter(10_000, "Timeout: Account loading check failed"),
]);
return {
@@ -82,62 +82,6 @@ export const createWorkerAccount = async ({
};
};
function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
const syncManager = account._raw.core.node.syncManager;
const peerState = syncManager.peers[peer.id];
if (!peerState) {
throw new Error(`Peer state for ${peer.id} not found`);
}
const isSynced = () => {
const knownState = coValue.knownState();
if (!peerState.optimisticKnownStates.get(coValue.id)) {
return false;
}
return isEqualSession(
knownState.sessions,
peerState.optimisticKnownStates.get(coValue.id)?.sessions ?? {},
);
};
if (isSynced()) {
return Promise.resolve(true);
}
return new Promise((resolve) => {
const unsubscribe = peerState?.optimisticKnownStates.subscribe(
(id, knownState) => {
if (id !== coValue.id) return;
if (isSynced()) {
resolve(true);
unsubscribe?.();
}
},
);
});
}
function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for (const sessionId of keysA) {
if (a[sessionId] !== b[sessionId]) {
return false;
}
}
return true;
}
function failAfter(ms: number, errorMessage: string) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error(errorMessage)), ms);

View File

@@ -1,5 +1,19 @@
# jazz-tools
## 0.8.18
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
## 0.8.17
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
## 0.8.16
### Patch Changes

View File

@@ -19,7 +19,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.8.16",
"version": "0.8.18",
"dependencies": {
"cojson": "workspace:*",
"fast-check": "^3.17.2"

View File

@@ -1,5 +1,23 @@
# jazz-react
## 0.8.8
### Patch Changes
- Updated dependencies [d4319d8]
- cojson@0.8.18
- jazz-browser@0.8.18
- jazz-tools@0.8.18
## 0.8.7
### Patch Changes
- Updated dependencies [d433cf4]
- cojson@0.8.17
- jazz-browser@0.8.17
- jazz-tools@0.8.17
## 0.8.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-vue",
"version": "0.8.6",
"version": "0.8.8",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

102
pnpm-lock.yaml generated
View File

@@ -46,7 +46,7 @@ importers:
e2e/BinaryCoStream:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
@@ -55,10 +55,10 @@ importers:
specifier: ^3.0.1
version: 3.0.1
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
react:
specifier: 18.3.1
@@ -150,13 +150,13 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-browser-media-images:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-browser-media-images
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
next:
specifier: 14.2.5
@@ -205,16 +205,16 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
version: link:../../packages/hash-slash
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -302,19 +302,19 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/cojson
hash-slash:
specifier: workspace:0.2.1
version: link:../../packages/hash-slash
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-react-auth-clerk:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react-auth-clerk
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -738,10 +738,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/cojson
cojson-transport-ws:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/cojson-transport-ws
hash-slash:
specifier: workspace:0.2.1
@@ -823,10 +823,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -881,10 +881,10 @@ importers:
examples/password-manager:
dependencies:
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
react:
specifier: 18.3.1
@@ -945,13 +945,13 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-browser-media-images:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-browser-media-images
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -1006,7 +1006,7 @@ importers:
specifier: ^3.0.1
version: 3.0.1
jazz-run:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-run
postcss:
specifier: ^8.4.27
@@ -1044,10 +1044,10 @@ importers:
specifier: ^2.0.0
version: 2.0.0
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../../packages/jazz-tools
lucide-react:
specifier: ^0.274.0
@@ -1145,7 +1145,7 @@ importers:
packages/cojson-storage-indexeddb:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1170,7 +1170,7 @@ importers:
specifier: ^8.5.2
version: 8.7.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1183,7 +1183,7 @@ importers:
packages/cojson-transport-ws:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
typescript:
specifier: ^5.3.3
@@ -1211,16 +1211,16 @@ importers:
specifier: ^1.3.0
version: 1.3.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
cojson-storage-indexeddb:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson-storage-indexeddb
cojson-transport-ws:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson-transport-ws
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
typescript:
specifier: ^5.3.3
@@ -1229,13 +1229,13 @@ importers:
packages/jazz-browser-auth-clerk:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
jazz-browser:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
devDependencies:
typescript:
@@ -1251,10 +1251,10 @@ importers:
specifier: ^4.1.0
version: 4.1.0
jazz-browser:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
pica:
specifier: ^9.0.1
@@ -1270,13 +1270,13 @@ importers:
packages/jazz-nodejs:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
cojson-transport-ws:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson-transport-ws
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
ws:
specifier: ^8.14.2
@@ -1295,13 +1295,13 @@ importers:
specifier: ^1.3.0
version: 1.3.0
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
jazz-browser:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-browser
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
typescript:
specifier: ^5.3.3
@@ -1320,16 +1320,16 @@ importers:
packages/jazz-react-auth-clerk:
dependencies:
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
jazz-browser-auth-clerk:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-browser-auth-clerk
jazz-react:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-react
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
react:
specifier: 18.3.1
@@ -1413,19 +1413,19 @@ importers:
specifier: ^0.25.5
version: 0.25.5(effect@3.6.5)
cojson:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson
cojson-storage-sqlite:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson-storage-sqlite
cojson-transport-ws:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../cojson-transport-ws
effect:
specifier: ^3.6.5
version: 3.6.5
jazz-tools:
specifier: workspace:0.8.16
specifier: workspace:0.8.18
version: link:../jazz-tools
ws:
specifier: ^8.14.2