Compare commits
5 Commits
feature/-c
...
jazz-vue@0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6921e621d7 | ||
|
|
1b0ef401fb | ||
|
|
1833983b8d | ||
|
|
149ca97c48 | ||
|
|
f01a7621b0 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"cojson": patch
|
||||
---
|
||||
|
||||
Improved the known state tracking within the PeerState.knownState property
|
||||
@@ -1,5 +1,15 @@
|
||||
# @jazz-e2e/binarycostream
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/binarycostream",
|
||||
"private": true,
|
||||
"version": "0.0.98",
|
||||
"version": "0.0.99",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,11 +13,11 @@
|
||||
"test:ui": "playwright test --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Account, BinaryCoStream, ID } from "jazz-tools";
|
||||
import { useEffect } from "react";
|
||||
import { useAccount, useCoState } from "./jazz";
|
||||
import { waitForCoValue } from "./lib/waitForCoValue";
|
||||
import { UploadedFile } from "./schema";
|
||||
|
||||
async function getUploadedFile(me: Account, uploadedFileId: ID<UploadedFile>) {
|
||||
const uploadedFile = await UploadedFile.load(uploadedFileId, me, {});
|
||||
|
||||
if (!uploadedFile) {
|
||||
throw new Error("Uploaded file not found");
|
||||
}
|
||||
const uploadedFile = await waitForCoValue(
|
||||
UploadedFile,
|
||||
uploadedFileId,
|
||||
me,
|
||||
Boolean,
|
||||
{},
|
||||
);
|
||||
|
||||
uploadedFile.coMapDownloaded = true;
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @jazz-e2e/covalues
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/covalues",
|
||||
"private": true,
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.98",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -3,7 +3,6 @@ import ReactDOM from "react-dom/client";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
import { AuthAndJazz } from "./jazz";
|
||||
import { ResumeSyncState } from "./pages/ResumeSyncState";
|
||||
import { RetryUnavailable } from "./pages/RetryUnavailable";
|
||||
import { TestInput } from "./pages/TestInput";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
@@ -15,10 +14,6 @@ const router = createBrowserRouter([
|
||||
path: "/resume-sync",
|
||||
element: <ResumeSyncState />,
|
||||
},
|
||||
{
|
||||
path: "/retry-unavailable",
|
||||
element: <RetryUnavailable />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <TestInput />,
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { CoMap, Group, ID, co } from "jazz-tools";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAccount, useCoState } from "../jazz";
|
||||
|
||||
export class RetryUnavailableCoMap extends CoMap {
|
||||
value = co.string;
|
||||
}
|
||||
|
||||
function getIdParam() {
|
||||
const url = new URL(window.location.href);
|
||||
return (url.searchParams.get("id") as ID<RetryUnavailableCoMap>) ?? undefined;
|
||||
}
|
||||
|
||||
export function RetryUnavailable() {
|
||||
const [id, setId] = useState(getIdParam);
|
||||
const coMap = useCoState(RetryUnavailableCoMap, id);
|
||||
const { me } = useAccount();
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("id", id);
|
||||
history.pushState({}, "", url.toString());
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const createCoMap = () => {
|
||||
if (!me || id) return;
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
|
||||
group.addMember("everyone", "writer");
|
||||
|
||||
setId(
|
||||
RetryUnavailableCoMap.create({ value: "Hello!" }, { owner: group }).id,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Retry Unavailable</h1>
|
||||
<p data-testid="id">{coMap?.id}</p>
|
||||
<button onClick={createCoMap}>Create a new value!</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { setTimeout } from "node:timers/promises";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test.describe("Retry unavailable states", () => {
|
||||
test("should retry unavailable values", async ({ page, browser }) => {
|
||||
const context = page.context();
|
||||
|
||||
await page.goto("/retry-unavailable?userName=SuperMario");
|
||||
|
||||
await context.setOffline(true);
|
||||
|
||||
await page.getByRole("button", { name: "Create a new value!" }).click();
|
||||
|
||||
const id = await page.getByTestId("id").textContent();
|
||||
|
||||
// Create a new incognito instance and try to load the coValue
|
||||
const newUserPage = await (await browser.newContext()).newPage();
|
||||
await newUserPage.goto(`/retry-unavailable?userName=Luigi&id=${id}`);
|
||||
|
||||
await expect(newUserPage.getByTestId("id")).toBeInViewport({
|
||||
timeout: 20_000,
|
||||
});
|
||||
|
||||
// Make the load fail at least twice
|
||||
await setTimeout(1000);
|
||||
|
||||
// Go back online, the value should be uploaded
|
||||
await context.setOffline(false);
|
||||
|
||||
await expect(newUserPage.getByTestId("id")).toHaveText(id ?? "", {
|
||||
timeout: 20_000,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-example-book-shelf
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
- jazz-browser-media-images@0.8.21
|
||||
|
||||
## 0.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-example-book-shelf",
|
||||
"version": "0.1.13",
|
||||
"version": "0.1.14",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -11,9 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-browser-media-images": "workspace:0.8.20",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-browser-media-images": "workspace:0.8.21",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"next": "14.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.98
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
- jazz-react-auth-clerk@0.8.21
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat-clerk",
|
||||
"private": true,
|
||||
"version": "0.0.97",
|
||||
"version": "0.0.98",
|
||||
"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.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react-auth-clerk@0.8.21
|
||||
- jazz-react-native@0.8.21
|
||||
- jazz-react-native-media-images@0.8.17
|
||||
|
||||
## 1.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.13",
|
||||
"version": "1.0.14",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react-native@0.8.21
|
||||
|
||||
## 1.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.15",
|
||||
"version": "1.0.16",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser@0.8.21
|
||||
- jazz-vue@0.8.11
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.100
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.99
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.99",
|
||||
"version": "0.0.100",
|
||||
"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.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- cojson@0.8.21
|
||||
- cojson-transport-ws@0.8.21
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector",
|
||||
"private": true,
|
||||
"version": "0.0.72",
|
||||
"version": "0.0.73",
|
||||
"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.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"cojson-transport-ws": "workspace:0.8.21",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
|
||||
@@ -64,7 +64,6 @@ export default function CoJsonViewerApp() {
|
||||
id: "cloud",
|
||||
websocket: new WebSocket("wss://cloud.jazz.tools"),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
const node = await LocalNode.withLoadedAccount({
|
||||
accountID: currentAccount.id,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.20",
|
||||
"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.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.18",
|
||||
"version": "0.0.19",
|
||||
"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.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
PasswordList,
|
||||
PasswordManagerAccount,
|
||||
} from "./1_schema";
|
||||
import { waitForCoValue } from "./lib/waitForCoValue";
|
||||
import { PasswordItemFormValues } from "./types";
|
||||
|
||||
export const saveItem = (item: CoMapInit<PasswordItem>): PasswordItem => {
|
||||
@@ -60,16 +61,14 @@ export async function addSharedFolder(
|
||||
me: PasswordManagerAccount,
|
||||
) {
|
||||
const [sharedFolder, account] = await Promise.all([
|
||||
Folder.load(sharedFolderId, me, {}),
|
||||
PasswordManagerAccount.load(me.id, me, {
|
||||
await waitForCoValue(Folder, sharedFolderId, me, Boolean, {}),
|
||||
await waitForCoValue(PasswordManagerAccount, me.id, me, Boolean, {
|
||||
root: {
|
||||
folders: [],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
if (!sharedFolder || !account) return;
|
||||
|
||||
if (!account.root?.folders) return;
|
||||
|
||||
const found = account.root.folders.some((f) => f?.id === sharedFolder.id);
|
||||
|
||||
39
examples/password-manager/src/lib/waitForCoValue.ts
Normal file
39
examples/password-manager/src/lib/waitForCoValue.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
Account,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DepthsIn,
|
||||
ID,
|
||||
subscribeToCoValue,
|
||||
} from "jazz-tools";
|
||||
|
||||
export function waitForCoValue<T extends CoValue>(
|
||||
coMap: CoValueClass<T>,
|
||||
valueId: ID<T>,
|
||||
account: Account,
|
||||
predicate: (value: T) => boolean,
|
||||
depth: DepthsIn<T>,
|
||||
) {
|
||||
return new Promise<T>((resolve) => {
|
||||
function subscribe() {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
coMap,
|
||||
valueId,
|
||||
account,
|
||||
depth,
|
||||
(value) => {
|
||||
if (predicate(value)) {
|
||||
resolve(value);
|
||||
unsubscribe();
|
||||
}
|
||||
},
|
||||
() => {
|
||||
unsubscribe();
|
||||
setTimeout(subscribe, 100);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
subscribe();
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.117
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
- jazz-browser-media-images@0.8.21
|
||||
|
||||
## 0.0.116
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.116",
|
||||
"version": "0.0.117",
|
||||
"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.20",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-browser-media-images": "workspace:0.8.21",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"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.19",
|
||||
"jazz-run": "workspace:0.8.21",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.116
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.0.115
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.115",
|
||||
"version": "0.0.116",
|
||||
"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.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- cojson@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19"
|
||||
"cojson": "workspace:0.8.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitest/browser": "^0.34.1",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- cojson@0.8.21
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^8.5.2",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- cojson@0.8.21
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -18,7 +18,6 @@ export type CreateWebSocketPeerOpts = {
|
||||
role: Peer["role"];
|
||||
expectPings?: boolean;
|
||||
batchingByDefault?: boolean;
|
||||
retryUnavailableCoValues: boolean;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
@@ -119,7 +118,6 @@ export function createWebSocketPeer({
|
||||
role,
|
||||
expectPings = true,
|
||||
batchingByDefault = true,
|
||||
retryUnavailableCoValues,
|
||||
onClose,
|
||||
}: CreateWebSocketPeerOpts): Peer {
|
||||
const incoming = new cojsonInternals.Channel<
|
||||
@@ -214,6 +212,5 @@ export function createWebSocketPeer({
|
||||
},
|
||||
role,
|
||||
crashOnClose: false,
|
||||
retryUnavailableCoValues,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ function setup(opts: Partial<CreateWebSocketPeerOpts> = {}) {
|
||||
websocket: mockWebSocket,
|
||||
role: "client",
|
||||
batchingByDefault: true,
|
||||
retryUnavailableCoValues: false,
|
||||
...opts,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# cojson
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0f30eea: Improved the known state tracking within the PeerState.knownState property
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.3",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { CoValueCore } from "./coValueCore.js";
|
||||
import { CoValueState } from "./coValueState.js";
|
||||
import { RawCoID } from "./ids.js";
|
||||
|
||||
export class CoValuesStore {
|
||||
coValues = new Map<RawCoID, CoValueState>();
|
||||
|
||||
get(id: RawCoID) {
|
||||
let entry = this.coValues.get(id);
|
||||
|
||||
if (!entry) {
|
||||
entry = CoValueState.Unknown(id);
|
||||
this.coValues.set(id, entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
setAsAvailable(id: RawCoID, coValue: CoValueCore) {
|
||||
const entry = this.get(id);
|
||||
entry.dispatch({
|
||||
type: "available",
|
||||
coValue,
|
||||
});
|
||||
}
|
||||
|
||||
getEntries() {
|
||||
return this.coValues.entries();
|
||||
}
|
||||
|
||||
getValues() {
|
||||
return this.coValues.values();
|
||||
}
|
||||
|
||||
getKeys() {
|
||||
return this.coValues.keys();
|
||||
}
|
||||
}
|
||||
@@ -57,10 +57,6 @@ export class PeerState {
|
||||
return this.peer.crashOnClose;
|
||||
}
|
||||
|
||||
get retryUnavailableCoValues() {
|
||||
return this.peer.retryUnavailableCoValues;
|
||||
}
|
||||
|
||||
isServerOrStoragePeer() {
|
||||
return this.peer.role === "server" || this.peer.role === "storage";
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ export class SyncStateSubscriptionManager {
|
||||
|
||||
getIsCoValueFullyUploadedIntoPeer(peerId: PeerID, id: RawCoID) {
|
||||
const peer = this.syncManager.peers[peerId];
|
||||
const entry = this.syncManager.local.coValuesStore.get(id);
|
||||
const entry = this.syncManager.local.coValues[id];
|
||||
|
||||
if (!peer) {
|
||||
if (!peer || !entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,54 @@
|
||||
import { PeerState } from "./PeerState.js";
|
||||
import { CoValueCore } from "./coValueCore.js";
|
||||
import { RawCoID } from "./ids.js";
|
||||
import { PeerID } from "./sync.js";
|
||||
|
||||
export const CO_VALUE_LOADING_MAX_RETRIES = 5;
|
||||
function createResolvablePromise<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
|
||||
export class CoValueUnknownState {
|
||||
type = "unknown" as const;
|
||||
const promise = new Promise<T>((res) => {
|
||||
resolve = res;
|
||||
});
|
||||
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
export class CoValueLoadingState {
|
||||
type = "loading" as const;
|
||||
private peers = new Map<
|
||||
class CoValueUnknownState {
|
||||
type = "unknown" as const;
|
||||
private peers: Map<
|
||||
PeerID,
|
||||
ReturnType<typeof createResolvablePromise<void>>
|
||||
>();
|
||||
private resolveResult: (value: CoValueCore | "unavailable") => void;
|
||||
ReturnType<typeof createResolvablePromise<"available" | "unavailable">>
|
||||
>;
|
||||
private resolve: (value: "available" | "unavailable") => void;
|
||||
|
||||
result: Promise<CoValueCore | "unavailable">;
|
||||
ready: Promise<"available" | "unavailable">;
|
||||
|
||||
constructor(peersIds: Iterable<PeerID>) {
|
||||
this.peers = new Map();
|
||||
|
||||
for (const peerId of peersIds) {
|
||||
this.peers.set(peerId, createResolvablePromise<void>());
|
||||
this.peers.set(
|
||||
peerId,
|
||||
createResolvablePromise<"available" | "unavailable">(),
|
||||
);
|
||||
}
|
||||
|
||||
const { resolve, promise } = createResolvablePromise<
|
||||
CoValueCore | "unavailable"
|
||||
"available" | "unavailable"
|
||||
>();
|
||||
|
||||
this.result = promise;
|
||||
this.resolveResult = resolve;
|
||||
this.ready = promise;
|
||||
this.resolve = resolve;
|
||||
}
|
||||
|
||||
markAsUnavailable(peerId: PeerID) {
|
||||
update(peerId: PeerID, value: "available" | "unavailable") {
|
||||
const entry = this.peers.get(peerId);
|
||||
|
||||
if (entry) {
|
||||
entry.resolve();
|
||||
entry.resolve(value);
|
||||
}
|
||||
|
||||
if (value === "available") {
|
||||
this.resolve("available");
|
||||
return;
|
||||
}
|
||||
|
||||
this.peers.delete(peerId);
|
||||
@@ -49,14 +59,6 @@ export class CoValueLoadingState {
|
||||
}
|
||||
}
|
||||
|
||||
resolve(value: CoValueCore | "unavailable") {
|
||||
this.resolveResult(value);
|
||||
for (const entry of this.peers.values()) {
|
||||
entry.resolve();
|
||||
}
|
||||
this.peers.clear();
|
||||
}
|
||||
|
||||
// Wait for a specific peer to have a known state
|
||||
waitForPeer(peerId: PeerID) {
|
||||
const entry = this.peers.get(peerId);
|
||||
@@ -69,261 +71,47 @@ export class CoValueLoadingState {
|
||||
}
|
||||
}
|
||||
|
||||
export class CoValueAvailableState {
|
||||
class CoValueAvailableState {
|
||||
type = "available" as const;
|
||||
|
||||
constructor(public coValue: CoValueCore) {}
|
||||
}
|
||||
|
||||
export class CoValueUnavailableState {
|
||||
type = "unavailable" as const;
|
||||
}
|
||||
|
||||
type CoValueStateAction =
|
||||
| {
|
||||
type: "load-requested";
|
||||
peersIds: PeerID[];
|
||||
}
|
||||
| {
|
||||
type: "not-found-in-peer";
|
||||
type: "not-found";
|
||||
peerId: PeerID;
|
||||
}
|
||||
| {
|
||||
type: "available";
|
||||
type: "found";
|
||||
peerId: PeerID;
|
||||
coValue: CoValueCore;
|
||||
};
|
||||
|
||||
type CoValueStateType =
|
||||
| CoValueUnknownState
|
||||
| CoValueLoadingState
|
||||
| CoValueAvailableState
|
||||
| CoValueUnavailableState;
|
||||
|
||||
export class CoValueState {
|
||||
promise?: Promise<CoValueCore | "unavailable">;
|
||||
private resolve?: (value: CoValueCore | "unavailable") => void;
|
||||
constructor(public state: CoValueUnknownState | CoValueAvailableState) {}
|
||||
|
||||
constructor(
|
||||
public id: RawCoID,
|
||||
public state: CoValueStateType,
|
||||
) {}
|
||||
|
||||
static Unknown(id: RawCoID) {
|
||||
return new CoValueState(id, new CoValueUnknownState());
|
||||
}
|
||||
|
||||
static Loading(id: RawCoID, peersIds: Iterable<PeerID>) {
|
||||
return new CoValueState(id, new CoValueLoadingState(peersIds));
|
||||
static Unknown(peersToWaitFor: Set<PeerID>) {
|
||||
return new CoValueState(new CoValueUnknownState(peersToWaitFor));
|
||||
}
|
||||
|
||||
static Available(coValue: CoValueCore) {
|
||||
return new CoValueState(coValue.id, new CoValueAvailableState(coValue));
|
||||
}
|
||||
|
||||
static Unavailable(id: RawCoID) {
|
||||
return new CoValueState(id, new CoValueUnavailableState());
|
||||
}
|
||||
|
||||
async getCoValue() {
|
||||
if (this.state.type === "available") {
|
||||
return this.state.coValue;
|
||||
}
|
||||
if (this.state.type === "unavailable") {
|
||||
return "unavailable";
|
||||
}
|
||||
|
||||
// If we don't have a resolved state we return a new promise
|
||||
// that will be resolved when the state will move to available or unavailable
|
||||
if (!this.promise) {
|
||||
const { promise, resolve } = createResolvablePromise<
|
||||
CoValueCore | "unavailable"
|
||||
>();
|
||||
|
||||
this.promise = promise;
|
||||
this.resolve = resolve;
|
||||
}
|
||||
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
private moveToState(value: CoValueStateType) {
|
||||
this.state = value;
|
||||
|
||||
if (!this.resolve) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the state is available we resolve the promise
|
||||
// and clear it to handle the possible transition from unavailable to available
|
||||
if (value.type === "available") {
|
||||
this.resolve(value.coValue);
|
||||
this.clearPromise();
|
||||
} else if (value.type === "unavailable") {
|
||||
this.resolve("unavailable");
|
||||
this.clearPromise();
|
||||
}
|
||||
}
|
||||
|
||||
private clearPromise() {
|
||||
this.promise = undefined;
|
||||
this.resolve = undefined;
|
||||
}
|
||||
|
||||
async loadFromPeers(peers: PeerState[]) {
|
||||
const state = this.state;
|
||||
|
||||
if (state.type !== "unknown" && state.type !== "unavailable") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (peers.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doLoad = async (peersToLoadFrom: PeerState[]) => {
|
||||
const peersWithoutErrors = getPeersWithoutErrors(
|
||||
peersToLoadFrom,
|
||||
this.id,
|
||||
);
|
||||
|
||||
// Set the state to loading and reset all the loading promises
|
||||
const currentState = this.dispatch({
|
||||
type: "load-requested",
|
||||
peersIds: peersWithoutErrors.map((p) => p.id),
|
||||
});
|
||||
|
||||
// If we entered successfully the loading state, we load the coValue from the peers
|
||||
//
|
||||
// We may not enter the loading state if the coValue has become available in between
|
||||
// of the retries
|
||||
if (currentState.type === "loading") {
|
||||
await loadCoValueFromPeers(this, peersWithoutErrors);
|
||||
|
||||
const result = await currentState.result;
|
||||
return result !== "unavailable";
|
||||
}
|
||||
|
||||
return currentState.type === "available";
|
||||
};
|
||||
|
||||
await doLoad(peers);
|
||||
|
||||
// Retry loading from peers that have the retry flag enabled
|
||||
const peersWithRetry = peers.filter((p) => p.retryUnavailableCoValues);
|
||||
|
||||
if (peersWithRetry.length > 0) {
|
||||
// We want to exit early if the coValue becomes available in between the retries
|
||||
await Promise.race([
|
||||
this.getCoValue(),
|
||||
runWithRetry(
|
||||
() => doLoad(peersWithRetry),
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
// If after the retries the coValue is still loading, we consider the load failed
|
||||
if (this.state.type === "loading") {
|
||||
this.moveToState(new CoValueUnavailableState());
|
||||
}
|
||||
return new CoValueState(new CoValueAvailableState(coValue));
|
||||
}
|
||||
|
||||
dispatch(action: CoValueStateAction) {
|
||||
const prevState = this.state;
|
||||
|
||||
switch (action.type) {
|
||||
case "load-requested":
|
||||
// We use this action to reset the loading state
|
||||
if (prevState.type === "loading" || prevState.type === "unknown") {
|
||||
this.moveToState(new CoValueLoadingState(action.peersIds));
|
||||
}
|
||||
|
||||
break;
|
||||
case "available":
|
||||
if (prevState.type === "loading") {
|
||||
prevState.resolve(action.coValue);
|
||||
}
|
||||
|
||||
// It should be always possible to move to the available state
|
||||
this.moveToState(new CoValueAvailableState(action.coValue));
|
||||
|
||||
break;
|
||||
case "not-found-in-peer":
|
||||
if (prevState.type === "loading") {
|
||||
prevState.markAsUnavailable(action.peerId);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return this.state;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCoValueFromPeers(
|
||||
coValueEntry: CoValueState,
|
||||
peers: PeerState[],
|
||||
) {
|
||||
for (const peer of peers) {
|
||||
await peer.pushOutgoingMessage({
|
||||
action: "load",
|
||||
id: coValueEntry.id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
});
|
||||
|
||||
if (coValueEntry.state.type === "loading") {
|
||||
await coValueEntry.state.waitForPeer(peer.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runWithRetry<T>(fn: () => Promise<T>, maxRetries: number) {
|
||||
let retries = 1;
|
||||
|
||||
while (retries < maxRetries) {
|
||||
/**
|
||||
* With maxRetries of 5 we should wait:
|
||||
* 300ms
|
||||
* 900ms
|
||||
* 2700ms
|
||||
* 8100ms
|
||||
*/
|
||||
await sleep(3 ** retries * 100);
|
||||
|
||||
const result = await fn();
|
||||
|
||||
if (result === true) {
|
||||
if (this.state.type === "available") {
|
||||
return;
|
||||
}
|
||||
|
||||
retries++;
|
||||
switch (action.type) {
|
||||
case "not-found":
|
||||
this.state.update(action.peerId, "unavailable");
|
||||
break;
|
||||
case "found":
|
||||
this.state.update(action.peerId, "available");
|
||||
this.state = new CoValueAvailableState(action.coValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createResolvablePromise<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
|
||||
const promise = new Promise<T>((res) => {
|
||||
resolve = res;
|
||||
});
|
||||
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function getPeersWithoutErrors(peers: PeerState[], coValueId: RawCoID) {
|
||||
return peers.filter((p) => {
|
||||
if (p.erroredCoValues.has(coValueId)) {
|
||||
console.error(
|
||||
`Skipping load on errored coValue ${coValueId} from peer ${p.id}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Result, ResultAsync, err, ok, okAsync } from "neverthrow";
|
||||
import { CoValuesStore } from "./CoValuesStore.js";
|
||||
import { CoID } from "./coValue.js";
|
||||
import { RawCoValue } from "./coValue.js";
|
||||
import {
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
CoValueHeader,
|
||||
CoValueUniqueness,
|
||||
} from "./coValueCore.js";
|
||||
import { CoValueState } from "./coValueState.js";
|
||||
import {
|
||||
AccountMeta,
|
||||
ControlledAccountOrAgent,
|
||||
@@ -45,7 +45,7 @@ export class LocalNode {
|
||||
/** @internal */
|
||||
crypto: CryptoProvider;
|
||||
/** @internal */
|
||||
coValuesStore = new CoValuesStore();
|
||||
coValues: { [key: RawCoID]: CoValueState } = {};
|
||||
/** @category 3. Low-level */
|
||||
account: ControlledAccountOrAgent;
|
||||
/** @category 3. Low-level */
|
||||
@@ -125,8 +125,7 @@ export class LocalNode {
|
||||
);
|
||||
|
||||
nodeWithAccount.account = controlledAccount;
|
||||
nodeWithAccount.coValuesStore.setAsAvailable(
|
||||
controlledAccount.id,
|
||||
nodeWithAccount.coValues[controlledAccount.id] = CoValueState.Available(
|
||||
controlledAccount.core,
|
||||
);
|
||||
controlledAccount.core._cachedContent = undefined;
|
||||
@@ -137,7 +136,7 @@ export class LocalNode {
|
||||
|
||||
// we shouldn't need this, but it fixes account data not syncing for new accounts
|
||||
function syncAllCoValuesAfterCreateAccount() {
|
||||
for (const coValueEntry of nodeWithAccount.coValuesStore.getValues()) {
|
||||
for (const coValueEntry of Object.values(nodeWithAccount.coValues)) {
|
||||
if (coValueEntry.state.type === "available") {
|
||||
void nodeWithAccount.syncManager.syncCoValue(
|
||||
coValueEntry.state.coValue,
|
||||
@@ -207,7 +206,7 @@ export class LocalNode {
|
||||
node.syncManager.local = node;
|
||||
|
||||
controlledAccount.core.node = node;
|
||||
node.coValuesStore.setAsAvailable(accountID, controlledAccount.core);
|
||||
node.coValues[accountID] = CoValueState.Available(controlledAccount.core);
|
||||
controlledAccount.core._cachedContent = undefined;
|
||||
|
||||
const profileID = account.get("profile");
|
||||
@@ -244,7 +243,7 @@ export class LocalNode {
|
||||
}
|
||||
|
||||
const coValue = new CoValueCore(header, this);
|
||||
this.coValuesStore.setAsAvailable(coValue.id, coValue);
|
||||
this.coValues[coValue.id] = CoValueState.Available(coValue);
|
||||
|
||||
void this.syncManager.syncCoValue(coValue);
|
||||
|
||||
@@ -254,7 +253,10 @@ export class LocalNode {
|
||||
/** @internal */
|
||||
async loadCoValueCore(
|
||||
id: RawCoID,
|
||||
skipLoadingFromPeer?: PeerID,
|
||||
options: {
|
||||
dontLoadFrom?: PeerID;
|
||||
dontWaitFor?: PeerID;
|
||||
} = {},
|
||||
): Promise<CoValueCore | "unavailable"> {
|
||||
if (this.crashed) {
|
||||
throw new Error("Trying to load CoValue after node has crashed", {
|
||||
@@ -262,18 +264,40 @@ export class LocalNode {
|
||||
});
|
||||
}
|
||||
|
||||
const entry = this.coValuesStore.get(id);
|
||||
let entry = this.coValues[id];
|
||||
if (!entry) {
|
||||
const peersToWaitFor = new Set(
|
||||
Object.values(this.syncManager.peers)
|
||||
.filter((peer) => peer.isServerOrStoragePeer())
|
||||
.map((peer) => peer.id),
|
||||
);
|
||||
if (options.dontWaitFor) peersToWaitFor.delete(options.dontWaitFor);
|
||||
entry = CoValueState.Unknown(peersToWaitFor);
|
||||
|
||||
if (entry.state.type === "unknown" || entry.state.type === "unavailable") {
|
||||
const peers =
|
||||
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
||||
this.coValues[id] = entry;
|
||||
|
||||
await entry.loadFromPeers(peers).catch((e) => {
|
||||
console.error("Error loading from peers", id, e);
|
||||
this.syncManager.loadFromPeers(id, options.dontLoadFrom).catch((e) => {
|
||||
console.error(
|
||||
"Error loading from peers",
|
||||
id,
|
||||
|
||||
e,
|
||||
);
|
||||
});
|
||||
}
|
||||
if (entry.state.type === "available") {
|
||||
return Promise.resolve(entry.state.coValue);
|
||||
}
|
||||
|
||||
return entry.getCoValue();
|
||||
await entry.state.ready;
|
||||
|
||||
const updatedEntry = this.coValues[id];
|
||||
|
||||
if (updatedEntry?.state.type === "available") {
|
||||
return Promise.resolve(updatedEntry.state.coValue);
|
||||
}
|
||||
|
||||
return "unavailable";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,12 +318,13 @@ export class LocalNode {
|
||||
}
|
||||
|
||||
getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
|
||||
const entry = this.coValuesStore.get(id);
|
||||
|
||||
const entry = this.coValues[id];
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
if (entry.state.type === "available") {
|
||||
return entry.state.coValue.getCurrentContent() as T;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -423,11 +448,15 @@ export class LocalNode {
|
||||
|
||||
/** @internal */
|
||||
expectCoValueLoaded(id: RawCoID, expectation?: string): CoValueCore {
|
||||
const entry = this.coValuesStore.get(id);
|
||||
|
||||
if (entry.state.type !== "available") {
|
||||
const entry = this.coValues[id];
|
||||
if (!entry) {
|
||||
throw new Error(
|
||||
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${entry.state.type}`,
|
||||
`${expectation ? expectation + ": " : ""}Unknown CoValue ${id}`,
|
||||
);
|
||||
}
|
||||
if (entry.state.type === "unknown") {
|
||||
throw new Error(
|
||||
`${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded`,
|
||||
);
|
||||
}
|
||||
return entry.state.coValue;
|
||||
@@ -621,20 +650,18 @@ export class LocalNode {
|
||||
): LocalNode {
|
||||
const newNode = new LocalNode(account, currentSessionID, this.crypto);
|
||||
|
||||
const coValuesToCopy = Array.from(this.coValuesStore.getEntries());
|
||||
const coValuesToCopy = Object.entries(this.coValues);
|
||||
|
||||
while (coValuesToCopy.length > 0) {
|
||||
const [coValueID, entry] = coValuesToCopy[coValuesToCopy.length - 1]!;
|
||||
|
||||
if (entry.state.type !== "available") {
|
||||
if (entry.state.type === "unknown") {
|
||||
coValuesToCopy.pop();
|
||||
continue;
|
||||
} else {
|
||||
const allDepsCopied = entry.state.coValue
|
||||
.getDependedOnCoValues()
|
||||
.every(
|
||||
(dep) => newNode.coValuesStore.get(dep).state.type === "available",
|
||||
);
|
||||
.every((dep) => newNode.coValues[dep]?.state.type === "available");
|
||||
|
||||
if (!allDepsCopied) {
|
||||
// move to end of queue
|
||||
@@ -648,7 +675,8 @@ export class LocalNode {
|
||||
new Map(entry.state.coValue.sessionLogs),
|
||||
);
|
||||
|
||||
newNode.coValuesStore.setAsAvailable(coValueID, newCoValue);
|
||||
newNode.coValues[coValueID as RawCoID] =
|
||||
CoValueState.Available(newCoValue);
|
||||
|
||||
coValuesToCopy.pop();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ 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";
|
||||
import { Signature } from "./crypto/crypto.js";
|
||||
import { RawCoID, SessionID } from "./ids.js";
|
||||
import { LocalNode } from "./localNode.js";
|
||||
@@ -78,7 +79,6 @@ export interface Peer {
|
||||
role: "peer" | "server" | "client" | "storage";
|
||||
priority?: number;
|
||||
crashOnClose: boolean;
|
||||
retryUnavailableCoValues?: boolean;
|
||||
}
|
||||
|
||||
export function combinedKnownStates(
|
||||
@@ -135,10 +135,31 @@ export class SyncManager {
|
||||
return Object.values(this.peers);
|
||||
}
|
||||
|
||||
getServerAndStoragePeers(excludePeerId?: PeerID): PeerState[] {
|
||||
return this.peersInPriorityOrder().filter(
|
||||
(peer) => peer.isServerOrStoragePeer() && peer.id !== excludePeerId,
|
||||
async loadFromPeers(id: RawCoID, forPeer?: PeerID) {
|
||||
const eligiblePeers = this.peersInPriorityOrder().filter(
|
||||
(peer) => peer.id !== forPeer && peer.isServerOrStoragePeer(),
|
||||
);
|
||||
|
||||
const coValueEntry = this.local.coValues[id];
|
||||
|
||||
for (const peer of eligiblePeers) {
|
||||
if (peer.erroredCoValues.has(id)) {
|
||||
console.error(
|
||||
`Skipping load on errored coValue ${id} from peer ${peer.id}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
await peer.pushOutgoingMessage({
|
||||
action: "load",
|
||||
id: id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
});
|
||||
|
||||
if (coValueEntry?.state.type === "unknown") {
|
||||
await coValueEntry.state.waitForPeer(peer.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
||||
@@ -171,10 +192,19 @@ export class SyncManager {
|
||||
}
|
||||
|
||||
async subscribeToIncludingDependencies(id: RawCoID, peer: PeerState) {
|
||||
let entry = this.local.coValuesStore.get(id);
|
||||
const entry = this.local.coValues[id];
|
||||
|
||||
if (entry.state.type !== "available") {
|
||||
entry.loadFromPeers([peer]).catch((e: unknown) => {
|
||||
if (!entry) {
|
||||
throw new Error("Expected coValue entry on subscribe");
|
||||
}
|
||||
|
||||
if (entry.state.type === "unknown") {
|
||||
this.trySendToPeer(peer, {
|
||||
action: "load",
|
||||
id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
}).catch((e: unknown) => {
|
||||
console.error("Error sending load", e);
|
||||
});
|
||||
return;
|
||||
@@ -304,7 +334,7 @@ export class SyncManager {
|
||||
|
||||
if (peerState.isServerOrStoragePeer()) {
|
||||
const initialSync = async () => {
|
||||
for (const id of this.local.coValuesStore.getKeys()) {
|
||||
for (const id of Object.keys(this.local.coValues) as RawCoID[]) {
|
||||
// console.log("subscribing to after peer added", id, peer.id)
|
||||
await this.subscribeToIncludingDependencies(id, peerState);
|
||||
|
||||
@@ -375,31 +405,55 @@ export class SyncManager {
|
||||
id: msg.id,
|
||||
value: knownStateIn(msg),
|
||||
});
|
||||
const entry = this.local.coValuesStore.get(msg.id);
|
||||
let entry = this.local.coValues[msg.id];
|
||||
|
||||
if (entry.state.type === "unknown" || entry.state.type === "unavailable") {
|
||||
const eligiblePeers = this.getServerAndStoragePeers(peer.id);
|
||||
if (!entry) {
|
||||
// console.log(`Loading ${msg.id} from all peers except ${peer.id}`);
|
||||
|
||||
// special case: we should be able to solve this much more neatly
|
||||
// with an explicit state machine in the future
|
||||
const eligiblePeers = this.peersInPriorityOrder().filter(
|
||||
(other) => other.id !== peer.id && other.isServerOrStoragePeer(),
|
||||
);
|
||||
if (eligiblePeers.length === 0) {
|
||||
// If we know less then the "load" sender and we don't have any eligible
|
||||
// peers to load the coValue from, we try to load it from the sender
|
||||
if (msg.header || Object.keys(msg.sessions).length > 0) {
|
||||
entry.loadFromPeers([peer]).catch((e) => {
|
||||
console.error("Error loading coValue in handleLoad", e);
|
||||
this.local.coValues[msg.id] = CoValueState.Unknown(
|
||||
new Set([peer.id]),
|
||||
);
|
||||
this.trySendToPeer(peer, {
|
||||
action: "known",
|
||||
id: msg.id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
}).catch((e) => {
|
||||
console.error("Error sending known state", e);
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
|
||||
console.error("Error loading coValue in handleLoad", e);
|
||||
});
|
||||
this.local
|
||||
.loadCoValueCore(msg.id, {
|
||||
dontLoadFrom: peer.id,
|
||||
dontWaitFor: peer.id,
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("Error loading coValue in handleLoad", e);
|
||||
});
|
||||
}
|
||||
|
||||
entry = this.local.coValues[msg.id]!;
|
||||
}
|
||||
|
||||
if (entry.state.type === "loading") {
|
||||
const value = await entry.getCoValue();
|
||||
if (entry.state.type === "unknown") {
|
||||
// console.debug(
|
||||
// "Waiting for loaded",
|
||||
// msg.id,
|
||||
// "after message from",
|
||||
// peer.id,
|
||||
// );
|
||||
const loaded = await entry.state.ready;
|
||||
|
||||
if (value === "unavailable") {
|
||||
if (loaded === "unavailable") {
|
||||
peer.dispatchToKnownStates({
|
||||
type: "SET",
|
||||
id: msg.id,
|
||||
@@ -420,14 +474,12 @@ export class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.state.type === "available") {
|
||||
await this.tellUntoldKnownStateIncludingDependencies(msg.id, peer);
|
||||
await this.sendNewContentIncludingDependencies(msg.id, peer);
|
||||
}
|
||||
await this.tellUntoldKnownStateIncludingDependencies(msg.id, peer);
|
||||
await this.sendNewContentIncludingDependencies(msg.id, peer);
|
||||
}
|
||||
|
||||
async handleKnownState(msg: KnownStateMessage, peer: PeerState) {
|
||||
const entry = this.local.coValuesStore.get(msg.id);
|
||||
let entry = this.local.coValues[msg.id];
|
||||
|
||||
peer.dispatchToKnownStates({
|
||||
type: "COMBINE_WITH",
|
||||
@@ -435,22 +487,18 @@ export class SyncManager {
|
||||
value: knownStateIn(msg),
|
||||
});
|
||||
|
||||
if (entry.state.type === "unknown" || entry.state.type === "unavailable") {
|
||||
if (!entry) {
|
||||
if (msg.asDependencyOf) {
|
||||
const dependencyEntry = this.local.coValuesStore.get(
|
||||
msg.asDependencyOf,
|
||||
);
|
||||
|
||||
if (
|
||||
dependencyEntry.state.type === "available" ||
|
||||
dependencyEntry.state.type === "loading"
|
||||
) {
|
||||
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
|
||||
console.error(
|
||||
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
|
||||
e,
|
||||
);
|
||||
});
|
||||
if (this.local.coValues[msg.asDependencyOf]) {
|
||||
this.local
|
||||
.loadCoValueCore(msg.id, { dontLoadFrom: peer.id })
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
|
||||
e,
|
||||
);
|
||||
});
|
||||
entry = this.local.coValues[msg.id]!; // must exist after loadCoValueCore
|
||||
} else {
|
||||
throw new Error(
|
||||
"Expected coValue dependency entry to be created, missing subscribe?",
|
||||
@@ -463,15 +511,12 @@ export class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
// When we receive a known state from a peer and the header is "false"
|
||||
// it means that the peer doesn't know anything about this coValue
|
||||
// So we can say that the coValue has been not found on this peer
|
||||
if (entry.state.type !== "available") {
|
||||
if (entry.state.type === "unknown") {
|
||||
const availableOnPeer = peer.optimisticKnownStates.get(msg.id)?.header;
|
||||
|
||||
if (!availableOnPeer) {
|
||||
entry.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
type: "not-found",
|
||||
peerId: peer.id,
|
||||
});
|
||||
}
|
||||
@@ -479,18 +524,23 @@ export class SyncManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.state.type === "available") {
|
||||
await this.tellUntoldKnownStateIncludingDependencies(msg.id, peer);
|
||||
await this.sendNewContentIncludingDependencies(msg.id, peer);
|
||||
}
|
||||
await this.tellUntoldKnownStateIncludingDependencies(msg.id, peer);
|
||||
await this.sendNewContentIncludingDependencies(msg.id, peer);
|
||||
}
|
||||
|
||||
async handleNewContent(msg: NewContentMessage, peer: PeerState) {
|
||||
const entry = this.local.coValuesStore.get(msg.id);
|
||||
const entry = this.local.coValues[msg.id];
|
||||
|
||||
if (!entry) {
|
||||
console.error(
|
||||
`Expected coValue entry for ${msg.id} to be created on new content, missing subscribe?`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let coValue: CoValueCore;
|
||||
|
||||
if (entry.state.type !== "available") {
|
||||
if (entry.state.type === "unknown") {
|
||||
if (!msg.header) {
|
||||
console.error("Expected header to be sent in first message");
|
||||
return;
|
||||
@@ -505,8 +555,9 @@ export class SyncManager {
|
||||
coValue = new CoValueCore(msg.header, this.local);
|
||||
|
||||
entry.dispatch({
|
||||
type: "available",
|
||||
type: "found",
|
||||
coValue,
|
||||
peerId: peer.id,
|
||||
});
|
||||
} else {
|
||||
coValue = entry.state.coValue;
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { PeerState } from "../PeerState";
|
||||
import { CoValueCore } from "../coValueCore";
|
||||
import { CO_VALUE_LOADING_MAX_RETRIES, CoValueState } from "../coValueState";
|
||||
import { RawCoID } from "../ids";
|
||||
|
||||
describe("CoValueState", () => {
|
||||
const mockCoValueId = "co_test123" as RawCoID;
|
||||
|
||||
test("should create unknown state", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
|
||||
expect(state.id).toBe(mockCoValueId);
|
||||
expect(state.state.type).toBe("unknown");
|
||||
});
|
||||
|
||||
test("should create loading state", () => {
|
||||
const peerIds = ["peer1", "peer2"];
|
||||
const state = CoValueState.Loading(mockCoValueId, peerIds);
|
||||
|
||||
expect(state.id).toBe(mockCoValueId);
|
||||
expect(state.state.type).toBe("loading");
|
||||
});
|
||||
|
||||
test("should create available state", async () => {
|
||||
const mockCoValue = { id: mockCoValueId } as CoValueCore;
|
||||
const state = CoValueState.Available(mockCoValue);
|
||||
|
||||
expect(state.id).toBe(mockCoValueId);
|
||||
expect(state.state.type).toBe("available");
|
||||
expect((state.state as any).coValue).toBe(mockCoValue);
|
||||
await expect(state.getCoValue()).resolves.toEqual(mockCoValue);
|
||||
});
|
||||
|
||||
test("should handle found action", async () => {
|
||||
const mockCoValue = { id: mockCoValueId } as CoValueCore;
|
||||
const state = CoValueState.Loading(mockCoValueId, ["peer1", "peer2"]);
|
||||
|
||||
const stateValuePromise = state.getCoValue();
|
||||
|
||||
state.dispatch({
|
||||
type: "available",
|
||||
coValue: mockCoValue,
|
||||
});
|
||||
|
||||
const result = await state.getCoValue();
|
||||
expect(result).toBe(mockCoValue);
|
||||
await expect(stateValuePromise).resolves.toBe(mockCoValue);
|
||||
});
|
||||
|
||||
test("should ignore actions when not in loading state", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
|
||||
expect(state.state.type).toBe("unknown");
|
||||
});
|
||||
|
||||
test("should retry loading from peers when unsuccessful", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const peer2 = {
|
||||
id: "peer2",
|
||||
erroredCoValues: new Set(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer2",
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
const mockPeers = [peer1, peer2] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
// Should attempt CO_VALUE_LOADING_MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toBe("unavailable");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("should skip errored coValues when loading from peers", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set<RawCoID>(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
peer1.erroredCoValues.add(mockCoValueId);
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const peer2 = {
|
||||
id: "peer2",
|
||||
erroredCoValues: new Set(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
console.log("pushing not-found from peer2");
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer2",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const mockPeers = [peer1, peer2] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
// Should attempt CO_VALUE_LOADING_MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(1);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toBe("unavailable");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("should retry only from peers that have the retry flag enabled", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set([]),
|
||||
retryUnavailableCoValues: false,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const peer2 = {
|
||||
id: "peer2",
|
||||
erroredCoValues: new Set(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer2",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const mockPeers = [peer1, peer2] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
// Should attempt CO_VALUE_LOADING_MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(1);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toEqual("unavailable");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("should handle the coValues that become available in between of the retries", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
let retries = 0;
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set([]),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
retries++;
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
|
||||
if (retries === 2) {
|
||||
setTimeout(() => {
|
||||
state.dispatch({
|
||||
type: "available",
|
||||
coValue: { id: mockCoValueId } as CoValueCore,
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}),
|
||||
};
|
||||
const mockPeers = [peer1] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
// Should attempt CO_VALUE_LOADING_MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES + 1; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(2);
|
||||
expect(state.state.type).toBe("available");
|
||||
await expect(state.getCoValue()).resolves.toEqual({ id: mockCoValueId });
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("should have a coValue as value property when becomes available after that have been marked as unavailable", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set([]),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
}),
|
||||
};
|
||||
const mockPeers = [peer1] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
// Should attempt CO_VALUE_LOADING_MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
state.dispatch({
|
||||
type: "available",
|
||||
coValue: { id: mockCoValueId } as CoValueCore,
|
||||
});
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(5);
|
||||
expect(state.state.type).toBe("available");
|
||||
await expect(state.getCoValue()).resolves.toEqual({ id: mockCoValueId });
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("should stop retrying when value becomes available", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
let run = 1;
|
||||
|
||||
const peer1 = {
|
||||
id: "peer1",
|
||||
erroredCoValues: new Set(),
|
||||
retryUnavailableCoValues: true,
|
||||
pushOutgoingMessage: vi.fn().mockImplementation(async () => {
|
||||
if (run > 2) {
|
||||
state.dispatch({
|
||||
type: "available",
|
||||
coValue: { id: mockCoValueId } as CoValueCore,
|
||||
});
|
||||
}
|
||||
state.dispatch({
|
||||
type: "not-found-in-peer",
|
||||
peerId: "peer1",
|
||||
});
|
||||
run++;
|
||||
}),
|
||||
};
|
||||
|
||||
const mockPeers = [peer1] as unknown as PeerState[];
|
||||
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(3);
|
||||
expect(state.state.type).toBe("available");
|
||||
await expect(state.getCoValue()).resolves.toEqual({ id: mockCoValueId });
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
@@ -807,7 +807,7 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
||||
sessions: {},
|
||||
} satisfies SyncMessage);
|
||||
|
||||
expect(node2.coValuesStore.get(map.core.id).state.type).toEqual("loading");
|
||||
expect(node2.coValues[map.core.id]?.state).toEqual("loading");
|
||||
|
||||
await inTx2.push(mapNewContentMsg);
|
||||
|
||||
@@ -1102,7 +1102,7 @@ test("If we start loading a coValue before connecting to a peer that has it, it
|
||||
|
||||
const mapOnNode2Promise = node2.loadCoValueCore(map.core.id);
|
||||
|
||||
expect(node2.coValuesStore.get(map.core.id).state.type).toEqual("unknown");
|
||||
expect(node2.coValues[map.core.id]?.state.type).toEqual("unknown");
|
||||
|
||||
node2.syncManager.addPeer(node1asPeer);
|
||||
|
||||
@@ -1821,63 +1821,6 @@ describe("SyncManager.addPeer", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadCoValueCore with retry", () => {
|
||||
test("should load the value if available on the server", async () => {
|
||||
const { client, jazzCloud } = createTwoConnectedNodes();
|
||||
|
||||
const anotherClient = createTestNode();
|
||||
const [
|
||||
connectionWithAnotherClientAsPeer,
|
||||
jazzCloudConnectionAsPeerForAnotherClient,
|
||||
] = connectedPeers("connectionWithAnotherClient", "jazzCloudConnection", {
|
||||
peer1role: "client",
|
||||
peer2role: "server",
|
||||
});
|
||||
|
||||
jazzCloud.syncManager.addPeer(connectionWithAnotherClientAsPeer);
|
||||
|
||||
const group = anotherClient.createGroup();
|
||||
const map = group.createMap();
|
||||
map.set("key1", "value1", "trusting");
|
||||
|
||||
const promise = client.loadCoValueCore(map.id);
|
||||
|
||||
anotherClient.syncManager.addPeer(
|
||||
jazzCloudConnectionAsPeerForAnotherClient,
|
||||
);
|
||||
await expect(promise).resolves.not.toBe("unavailable");
|
||||
});
|
||||
|
||||
test("should handle correctly two subsequent loads", async () => {
|
||||
const { client, jazzCloud } = createTwoConnectedNodes();
|
||||
|
||||
const anotherClient = createTestNode();
|
||||
const [
|
||||
connectionWithAnotherClientAsPeer,
|
||||
jazzCloudConnectionAsPeerForAnotherClient,
|
||||
] = connectedPeers("connectionWithAnotherClient", "jazzCloudConnection", {
|
||||
peer1role: "client",
|
||||
peer2role: "server",
|
||||
});
|
||||
|
||||
jazzCloud.syncManager.addPeer(connectionWithAnotherClientAsPeer);
|
||||
|
||||
const group = anotherClient.createGroup();
|
||||
const map = group.createMap();
|
||||
map.set("key1", "value1", "trusting");
|
||||
|
||||
const promise1 = client.loadCoValueCore(map.id);
|
||||
const promise2 = client.loadCoValueCore(map.id);
|
||||
|
||||
anotherClient.syncManager.addPeer(
|
||||
jazzCloudConnectionAsPeerForAnotherClient,
|
||||
);
|
||||
|
||||
await expect(promise1).resolves.not.toBe("unavailable");
|
||||
await expect(promise2).resolves.not.toBe("unavailable");
|
||||
});
|
||||
});
|
||||
|
||||
describe("waitForUploadIntoPeer", () => {
|
||||
test("should resolve when the coValue is fully uploaded into the peer", async () => {
|
||||
const { client, jazzCloudConnectionAsPeer: peer } =
|
||||
@@ -1953,7 +1896,3 @@ function _admStateEx(adminID: RawAccountID) {
|
||||
id: adminID,
|
||||
};
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "jazz-browser-auth-clerk",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19",
|
||||
"jazz-browser": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19"
|
||||
"cojson": "workspace:0.8.21",
|
||||
"jazz-browser": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser-media-images",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"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.20",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-browser": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"pica": "^9.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- cojson-storage-indexeddb@0.8.21
|
||||
- cojson-transport-ws@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scure/bip39": "^1.3.0",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson-storage-indexeddb": "workspace:0.8.20",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"cojson-storage-indexeddb": "workspace:0.8.21",
|
||||
"cojson-transport-ws": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -11,7 +11,6 @@ export function createWebSocketPeerWithReconnection(
|
||||
id: peer,
|
||||
role: "server",
|
||||
onClose: reconnectWebSocket,
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
|
||||
let shouldTryToReconnect = true;
|
||||
@@ -47,7 +46,6 @@ export function createWebSocketPeerWithReconnection(
|
||||
id: peer,
|
||||
role: "server",
|
||||
onClose: reconnectWebSocket,
|
||||
retryUnavailableCoValues: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-autosub
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- cojson-transport-ws@0.8.21
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
"types": "src/index.ts",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"cojson-transport-ws": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -26,7 +26,6 @@ export async function startWorker<Acc extends Account>({
|
||||
id: "upstream",
|
||||
websocket: new WebSocket(peer),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
|
||||
if (!accountID) {
|
||||
@@ -62,7 +61,6 @@ export async function startWorker<Acc extends Account>({
|
||||
id: "upstream",
|
||||
websocket: new WebSocket(peer),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
|
||||
worker._raw.core.node.syncManager.addPeer(wsPeer);
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser-auth-clerk@0.8.21
|
||||
- jazz-react@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "jazz-react-auth-clerk",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.19",
|
||||
"jazz-browser-auth-clerk": "workspace:0.8.20",
|
||||
"jazz-react": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19"
|
||||
"cojson": "workspace:0.8.21",
|
||||
"jazz-browser-auth-clerk": "workspace:0.8.21",
|
||||
"jazz-react": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
|
||||
## 0.8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native-media-images",
|
||||
"version": "0.8.16",
|
||||
"version": "0.8.17",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- cojson-transport-ws@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-native",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -71,7 +71,6 @@ export async function createJazzRNContext<Acc extends Account>(
|
||||
id: options.peer + "@" + new Date().toISOString(),
|
||||
role: "server",
|
||||
expectPings: true,
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
let shouldTryToReconnect = true;
|
||||
|
||||
@@ -136,7 +135,6 @@ export async function createJazzRNContext<Acc extends Account>(
|
||||
websocket: new WebSocket(options.peer),
|
||||
id: options.peer + "@" + new Date().toISOString(),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-react
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser@0.8.21
|
||||
|
||||
## 0.8.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "jazz-react",
|
||||
"version": "0.8.20",
|
||||
"version": "0.8.21",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@scure/bip39": "^1.3.0",
|
||||
"cojson": "workspace:0.8.19",
|
||||
"jazz-browser": "workspace:0.8.20",
|
||||
"jazz-tools": "workspace:0.8.19"
|
||||
"cojson": "workspace:0.8.21",
|
||||
"jazz-browser": "workspace:0.8.21",
|
||||
"jazz-tools": "workspace:0.8.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.19",
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# jazz-run
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- cojson-storage-sqlite@0.8.21
|
||||
- cojson-transport-ws@0.8.21
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"bin": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"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.19",
|
||||
"cojson-storage-sqlite": "workspace:0.8.19",
|
||||
"cojson-transport-ws": "workspace:0.8.19",
|
||||
"cojson": "workspace:0.8.21",
|
||||
"cojson-storage-sqlite": "workspace:0.8.21",
|
||||
"cojson-transport-ws": "workspace:0.8.21",
|
||||
"effect": "^3.6.5",
|
||||
"jazz-tools": "workspace:0.8.19",
|
||||
"jazz-tools": "workspace:0.8.21",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,7 +24,6 @@ export const createWorkerAccount = async ({
|
||||
id: "upstream",
|
||||
websocket: new WebSocket(peerAddr),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
|
||||
const account = await Account.create({
|
||||
@@ -62,7 +61,6 @@ export const createWorkerAccount = async ({
|
||||
id: "verifyingPeer",
|
||||
websocket: new WebSocket(peerAddr),
|
||||
role: "server",
|
||||
retryUnavailableCoValues: true,
|
||||
});
|
||||
|
||||
await Promise.race([
|
||||
|
||||
@@ -73,7 +73,6 @@ export const startSyncServer = async ({
|
||||
websocket: ws,
|
||||
expectPings: false,
|
||||
batchingByDefault: false,
|
||||
retryUnavailableCoValues: false,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# jazz-tools
|
||||
|
||||
## 0.8.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 149ca97: changed jazz-tools TS target to ES2021
|
||||
- Updated dependencies [0f30eea]
|
||||
- cojson@0.8.21
|
||||
|
||||
## 0.8.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.8.19",
|
||||
"version": "0.8.21",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:*",
|
||||
"fast-check": "^3.17.2"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "es2022",
|
||||
"target": "ES2021",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"strict": true,
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# jazz-react
|
||||
|
||||
## 0.8.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0f30eea]
|
||||
- Updated dependencies [149ca97]
|
||||
- cojson@0.8.21
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-browser@0.8.21
|
||||
|
||||
## 0.8.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-vue",
|
||||
"version": "0.8.10",
|
||||
"version": "0.8.11",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
102
pnpm-lock.yaml
generated
102
pnpm-lock.yaml
generated
@@ -46,7 +46,7 @@ importers:
|
||||
e2e/BinaryCoStream:
|
||||
dependencies:
|
||||
cojson:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-browser-media-images
|
||||
jazz-react:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/cojson
|
||||
hash-slash:
|
||||
specifier: workspace:0.2.1
|
||||
version: link:../../packages/hash-slash
|
||||
jazz-react:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/cojson
|
||||
hash-slash:
|
||||
specifier: workspace:0.2.1
|
||||
version: link:../../packages/hash-slash
|
||||
jazz-react:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-react-auth-clerk:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react-auth-clerk
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/cojson
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-browser-media-images
|
||||
jazz-react:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../../packages/jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
devDependencies:
|
||||
'@vitest/browser':
|
||||
@@ -1170,7 +1170,7 @@ importers:
|
||||
specifier: ^8.5.2
|
||||
version: 8.7.0
|
||||
cojson:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
typescript:
|
||||
specifier: ^5.3.3
|
||||
@@ -1183,7 +1183,7 @@ importers:
|
||||
packages/cojson-transport-ws:
|
||||
dependencies:
|
||||
cojson:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
cojson-storage-indexeddb:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson-storage-indexeddb
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson-transport-ws
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
jazz-browser:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-browser
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-browser
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-tools
|
||||
pica:
|
||||
specifier: ^9.0.1
|
||||
@@ -1270,13 +1270,13 @@ importers:
|
||||
packages/jazz-nodejs:
|
||||
dependencies:
|
||||
cojson:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson-transport-ws
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
jazz-browser:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-browser
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-tools
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
@@ -1320,16 +1320,16 @@ importers:
|
||||
packages/jazz-react-auth-clerk:
|
||||
dependencies:
|
||||
cojson:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
jazz-browser-auth-clerk:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-browser-auth-clerk
|
||||
jazz-react:
|
||||
specifier: workspace:0.8.20
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-react
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
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.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson
|
||||
cojson-storage-sqlite:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson-storage-sqlite
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../cojson-transport-ws
|
||||
effect:
|
||||
specifier: ^3.6.5
|
||||
version: 3.6.5
|
||||
jazz-tools:
|
||||
specifier: workspace:0.8.19
|
||||
specifier: workspace:0.8.21
|
||||
version: link:../jazz-tools
|
||||
ws:
|
||||
specifier: ^8.14.2
|
||||
|
||||
Reference in New Issue
Block a user