Compare commits
2 Commits
jazz-bette
...
gio/chat-q
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d10111117 | ||
|
|
511ae983d5 |
5
.changeset/cuddly-lemons-perform.md
Normal file
5
.changeset/cuddly-lemons-perform.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"quint-ui": patch
|
||||
---
|
||||
|
||||
Initial Quint-UI release
|
||||
@@ -14,7 +14,7 @@
|
||||
"tests/jazz-svelte/src/**",
|
||||
"examples/*svelte*/**",
|
||||
"starters/*svelte*/**",
|
||||
"examples/server-worker-inbox/src/routeTree.gen.ts",
|
||||
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
|
||||
"homepage/homepage/**",
|
||||
"**/package.json"
|
||||
]
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
|
||||
## 0.0.103
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- jazz-tools@0.15.14
|
||||
|
||||
## 0.0.102
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.104",
|
||||
"version": "0.0.102",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"test:e2e:ui": "playwright test --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"hash-slash": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"lucide-react": "^0.274.0",
|
||||
"quint-ui": "workspace:*",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"zod": "3.25.28"
|
||||
@@ -32,4 +32,4 @@
|
||||
"typescript": "5.6.2",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useIframeHashRouter } from "hash-slash";
|
||||
import { Group } from "jazz-tools";
|
||||
import { JazzInspector } from "jazz-tools/inspector";
|
||||
import { JazzReactProvider, useAccount } from "jazz-tools/react";
|
||||
import { Button } from "quint-ui";
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { ChatScreen } from "./chatScreen.tsx";
|
||||
@@ -39,7 +40,11 @@ export function App() {
|
||||
}}
|
||||
placeholder="Set username"
|
||||
/>
|
||||
{!inIframe && <button onClick={logOut}>Log out</button>}
|
||||
{!inIframe && (
|
||||
<Button intent="danger" onClick={logOut} variant="outline">
|
||||
Log out
|
||||
</Button>
|
||||
)}
|
||||
</TopBar>
|
||||
{router.route({
|
||||
"/": () => createChat() as never,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@import "quint-ui/styles.css";
|
||||
/* We need to tell tailwind to detect classes from quint components */
|
||||
@source "../node_modules/quint-ui";
|
||||
|
||||
/* Custom stone color palette */
|
||||
@theme {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import clsx from "clsx";
|
||||
import { CoPlainText, ImageDefinition } from "jazz-tools";
|
||||
import type { CoPlainText, ImageDefinition } from "jazz-tools";
|
||||
import { ProgressiveImg } from "jazz-tools/react";
|
||||
import { ImageIcon } from "lucide-react";
|
||||
import { cn } from "quint-ui";
|
||||
import { useId, useRef } from "react";
|
||||
|
||||
export function AppContainer(props: { children: React.ReactNode }) {
|
||||
@@ -57,7 +57,7 @@ export function BubbleBody(props: {
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
className={cn(
|
||||
"line-clamp-10 text-ellipsis whitespace-pre-wrap",
|
||||
"rounded-2xl overflow-hidden max-w-[calc(100%-5rem)] shadow-sm p-1",
|
||||
props.fromMe
|
||||
@@ -75,9 +75,7 @@ export function BubbleText(props: {
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<p className={clsx("px-2 leading-relaxed", props.className)}>
|
||||
{props.text}
|
||||
</p>
|
||||
<p className={cn("px-2 leading-relaxed", props.className)}>{props.text}</p>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -94,7 +94,7 @@ const MusicaAccountRoot = co.map({
|
||||
|
||||
const MusicaAccount = co.account({
|
||||
root: MusicaAccountRoot,
|
||||
profile: co.profile(),
|
||||
profile: co.profile({}),
|
||||
});
|
||||
type MusicaAccount = co.loaded<typeof MusicaAccount>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ export const JazzAccountRoot = co.map({
|
||||
export const JazzAccount = co
|
||||
.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile(),
|
||||
profile: co.profile({}),
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (account.root === undefined) {
|
||||
@@ -130,7 +130,7 @@ const JazzAccountRoot = co.map({
|
||||
|
||||
const JazzAccount = co.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile(),
|
||||
profile: co.profile({}),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
|
||||
@@ -11,7 +11,7 @@ The main detail to understand when using Jazz server-side is that Server Workers
|
||||
|
||||
This lets you share CoValues with Server Workers, having precise access control by adding the Worker to `Groups` with specific roles just like you would with other users.
|
||||
|
||||
[See the full example here.](https://github.com/garden-co/jazz/tree/main/examples/server-worker-inbox)
|
||||
[See the full example here.](https://github.com/garden-co/jazz/tree/main/examples/jazz-paper-scissors)
|
||||
|
||||
<Alert variant="info" className="mt-4 flex gap-2 items-center">Requires at least Node.js v20.</Alert>
|
||||
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 70ce7c5: Introduce the persistent peers. Used to mark the WebSocket connections to the server as persistent, and wait for reconnection before failing load.
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -165,6 +165,6 @@ export function createWebSocketPeer({
|
||||
incoming,
|
||||
outgoing,
|
||||
role,
|
||||
persistent: !deletePeerStateOnClose,
|
||||
deletePeerStateOnClose,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# cojson
|
||||
|
||||
## 0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 70ce7c5: Introduce the persistent peers. Used to mark the WebSocket connections to the server as persistent, and wait for reconnection before failing load.
|
||||
|
||||
## 0.15.13
|
||||
|
||||
## 0.15.12
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"libsql": "^0.5.13",
|
||||
|
||||
@@ -97,10 +97,6 @@ export class PeerState {
|
||||
return this.peer.incoming;
|
||||
}
|
||||
|
||||
get persistent() {
|
||||
return this.peer.persistent;
|
||||
}
|
||||
|
||||
pushOutgoingMessage(msg: SyncMessage) {
|
||||
this.peer.outgoing.push(msg);
|
||||
}
|
||||
|
||||
@@ -1056,33 +1056,37 @@ export class CoValueCore {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const peer of peers) {
|
||||
const currentState = this.peers.get(peer.id)?.type ?? "unknown";
|
||||
const peersToActuallyLoadFrom = [] as PeerState[];
|
||||
|
||||
if (currentState === "unknown" || currentState === "unavailable") {
|
||||
for (const peer of peers) {
|
||||
const currentState = this.peers.get(peer.id)?.type;
|
||||
|
||||
if (
|
||||
!currentState ||
|
||||
currentState === "unknown" ||
|
||||
currentState === "unavailable"
|
||||
) {
|
||||
peersToActuallyLoadFrom.push(peer);
|
||||
this.markPending(peer.id);
|
||||
this.internalLoadFromPeer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
for (const peer of peersToActuallyLoadFrom) {
|
||||
this.internalLoadFromPeer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
internalLoadFromPeer(peer: PeerState) {
|
||||
if (peer.closed && !peer.persistent) {
|
||||
if (peer.closed) {
|
||||
this.markNotFoundInPeer(peer.id);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* On reconnection persistent peers will automatically fire the load request
|
||||
* as part of the reconnection process.
|
||||
*/
|
||||
if (!peer.closed) {
|
||||
peer.pushOutgoingMessage({
|
||||
action: "load",
|
||||
...this.knownState(),
|
||||
});
|
||||
peer.trackLoadRequestSent(this.id);
|
||||
}
|
||||
peer.pushOutgoingMessage({
|
||||
action: "load",
|
||||
...this.knownState(),
|
||||
});
|
||||
peer.trackLoadRequestSent(this.id);
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
const markNotFound = () => {
|
||||
@@ -1096,9 +1100,7 @@ export class CoValueCore {
|
||||
};
|
||||
|
||||
const timeout = setTimeout(markNotFound, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
||||
const removeCloseListener = peer.persistent
|
||||
? undefined
|
||||
: peer.addCloseListener(markNotFound);
|
||||
const removeCloseListener = peer.addCloseListener(markNotFound);
|
||||
|
||||
const listener = (state: CoValueCore) => {
|
||||
const peerState = state.peers.get(peer.id);
|
||||
@@ -1109,7 +1111,7 @@ export class CoValueCore {
|
||||
peerState?.type === "unavailable"
|
||||
) {
|
||||
this.listeners.delete(listener);
|
||||
removeCloseListener?.();
|
||||
removeCloseListener();
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
}
|
||||
|
||||
@@ -13,14 +13,6 @@ export const CO_VALUE_LOADING_CONFIG = {
|
||||
RETRY_DELAY: 3000,
|
||||
};
|
||||
|
||||
export function setCoValueLoadingMaxRetries(maxRetries: number) {
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES = maxRetries;
|
||||
}
|
||||
|
||||
export function setCoValueLoadingTimeout(timeout: number) {
|
||||
CO_VALUE_LOADING_CONFIG.TIMEOUT = timeout;
|
||||
}
|
||||
|
||||
export function setCoValueLoadingRetryDelay(delay: number) {
|
||||
CO_VALUE_LOADING_CONFIG.RETRY_DELAY = delay;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,9 @@ export function connectedPeers(
|
||||
{
|
||||
peer1role = "client",
|
||||
peer2role = "client",
|
||||
persistent = false,
|
||||
}: {
|
||||
peer1role?: Peer["role"];
|
||||
peer2role?: Peer["role"];
|
||||
persistent?: boolean;
|
||||
} = {},
|
||||
): [Peer, Peer] {
|
||||
const from1to2 = new ConnectedPeerChannel();
|
||||
@@ -28,7 +26,6 @@ export function connectedPeers(
|
||||
incoming: from2to1,
|
||||
outgoing: from1to2,
|
||||
role: peer2role,
|
||||
persistent,
|
||||
};
|
||||
|
||||
const peer1AsPeer: Peer = {
|
||||
@@ -36,7 +33,6 @@ export function connectedPeers(
|
||||
incoming: from1to2,
|
||||
outgoing: from2to1,
|
||||
role: peer1role,
|
||||
persistent,
|
||||
};
|
||||
|
||||
return [peer1AsPeer, peer2AsPeer];
|
||||
|
||||
@@ -88,7 +88,7 @@ export interface Peer {
|
||||
outgoing: OutgoingPeerChannel;
|
||||
role: "server" | "client";
|
||||
priority?: number;
|
||||
persistent?: boolean;
|
||||
deletePeerStateOnClose?: boolean;
|
||||
}
|
||||
|
||||
export function combinedKnownStates(
|
||||
@@ -157,7 +157,8 @@ export class SyncManager {
|
||||
|
||||
getServerPeers(excludePeerId?: PeerID): PeerState[] {
|
||||
return this.getPeers().filter(
|
||||
(peer) => peer.role === "server" && peer.id !== excludePeerId,
|
||||
(peer) =>
|
||||
peer.role === "server" && peer.id !== excludePeerId && !peer.closed,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -352,7 +353,7 @@ export class SyncManager {
|
||||
unsubscribeFromKnownStatesUpdates();
|
||||
this.peersCounter.add(-1, { role: peer.role });
|
||||
|
||||
if (!peer.persistent && this.peers[peer.id] === peerState) {
|
||||
if (peer.deletePeerStateOnClose && this.peers[peer.id] === peerState) {
|
||||
delete this.peers[peer.id];
|
||||
}
|
||||
});
|
||||
@@ -793,8 +794,8 @@ export class SyncManager {
|
||||
|
||||
const peerState = this.peers[peerId];
|
||||
|
||||
// The peer has been closed and is not persistent, so it isn't possible to sync
|
||||
if (!peerState) {
|
||||
// The peer has been closed, so it isn't possible to sync
|
||||
if (!peerState || peerState.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -829,7 +830,7 @@ export class SyncManager {
|
||||
return this.local.storage?.waitForSync(id, this.local.getCoValue(id));
|
||||
}
|
||||
|
||||
waitForSync(id: RawCoID, timeout = 60_000) {
|
||||
waitForSync(id: RawCoID, timeout = 30_000) {
|
||||
const peers = this.getPeers();
|
||||
|
||||
return Promise.all(
|
||||
|
||||
@@ -251,11 +251,9 @@ describe("SyncStateManager", () => {
|
||||
).toEqual({ uploaded: true });
|
||||
});
|
||||
|
||||
test("should skip non-persistent closed peers", async () => {
|
||||
test("should skip closed peers", async () => {
|
||||
const client = setupTestNode();
|
||||
const { peerState } = client.connectToSyncServer({
|
||||
persistent: false,
|
||||
});
|
||||
const { peerState } = client.connectToSyncServer();
|
||||
|
||||
peerState.gracefulShutdown();
|
||||
|
||||
@@ -265,42 +263,6 @@ describe("SyncStateManager", () => {
|
||||
await map.core.waitForSync();
|
||||
});
|
||||
|
||||
test("should wait for persistent closed peers to reconnect", async () => {
|
||||
const client = setupTestNode();
|
||||
const { peerState } = client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
peerState.gracefulShutdown();
|
||||
|
||||
const group = client.node.createGroup();
|
||||
const map = group.createMap();
|
||||
|
||||
const promise = map.core.waitForSync().then(() => "waitForSync");
|
||||
|
||||
const result = await Promise.race([
|
||||
promise,
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => resolve("timeout"), 10);
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(result).toBe("timeout");
|
||||
|
||||
client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
const result2 = await Promise.race([
|
||||
promise,
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => resolve("timeout"), 10);
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(result2).toBe("waitForSync");
|
||||
});
|
||||
|
||||
test("should skip client peers that are not subscribed to the coValue", async () => {
|
||||
const server = setupTestNode({ isSyncServer: true });
|
||||
const client = setupTestNode();
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { expectMap } from "../coValue";
|
||||
import {
|
||||
CO_VALUE_LOADING_CONFIG,
|
||||
setCoValueLoadingRetryDelay,
|
||||
} from "../config";
|
||||
import { setCoValueLoadingRetryDelay } from "../config";
|
||||
import { RawCoMap } from "../exports";
|
||||
import {
|
||||
SyncMessagesLog,
|
||||
@@ -510,50 +507,9 @@ describe("loading coValues from server", () => {
|
||||
`);
|
||||
});
|
||||
|
||||
test("should wait for a persistent peer to reconnect before marking the coValue as unavailable", async () => {
|
||||
test("should mark the coValue as unavailable if the peer is closed", async () => {
|
||||
const client = setupTestNode();
|
||||
const connection1 = client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
connection1.peerState.gracefulShutdown();
|
||||
|
||||
const group = jazzCloud.node.createGroup();
|
||||
group.addMember("everyone", "writer");
|
||||
|
||||
const map = group.createMap({
|
||||
test: "value",
|
||||
});
|
||||
const promise = client.node.load(map.id);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
client.connectToSyncServer();
|
||||
|
||||
const coValue = await promise;
|
||||
|
||||
expect(coValue).not.toBe("unavailable");
|
||||
|
||||
expect(
|
||||
SyncMessagesLog.getMessages({
|
||||
Group: group.core,
|
||||
Map: map.core,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> server | LOAD Map sessions: empty",
|
||||
"server -> client | CONTENT Group header: true new: After: 0 New: 5",
|
||||
"server -> client | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> server | KNOWN Group sessions: header/5",
|
||||
"client -> server | KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("should handle reconnections in the middle of a load with a persistent peer", async () => {
|
||||
const client = setupTestNode();
|
||||
const connection1 = client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
const { peerState } = client.connectToSyncServer();
|
||||
|
||||
const group = jazzCloud.node.createGroup();
|
||||
group.addMember("everyone", "writer");
|
||||
@@ -562,23 +518,12 @@ describe("loading coValues from server", () => {
|
||||
test: "value",
|
||||
});
|
||||
|
||||
blockMessageTypeOnOutgoingPeer(connection1.peerOnServer, "content", {
|
||||
id: map.id,
|
||||
once: true,
|
||||
});
|
||||
|
||||
const promise = client.node.load(map.id);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
// Close the peer connection
|
||||
connection1.peerState.gracefulShutdown();
|
||||
peerState.gracefulShutdown();
|
||||
|
||||
client.connectToSyncServer();
|
||||
|
||||
const coValue = await promise;
|
||||
|
||||
expect(coValue).not.toBe("unavailable");
|
||||
expect(await promise).toEqual("unavailable");
|
||||
|
||||
expect(
|
||||
SyncMessagesLog.getMessages({
|
||||
@@ -588,13 +533,6 @@ describe("loading coValues from server", () => {
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> server | LOAD Map sessions: empty",
|
||||
"server -> client | CONTENT Group header: true new: After: 0 New: 5",
|
||||
"client -> server | KNOWN Group sessions: header/5",
|
||||
"client -> server | LOAD Map sessions: empty",
|
||||
"client -> server | LOAD Group sessions: header/5",
|
||||
"server -> client | KNOWN Group sessions: header/5",
|
||||
"server -> client | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> server | KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -971,62 +909,4 @@ describe("loading coValues from server", () => {
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("should retry loading from a closed persistent peer after a timeout", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const client = setupTestNode();
|
||||
|
||||
const connection1 = client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
// Close the peer connection
|
||||
connection1.peerState.gracefulShutdown();
|
||||
|
||||
const group = jazzCloud.node.createGroup();
|
||||
group.addMember("everyone", "reader");
|
||||
|
||||
const map = group.createMap();
|
||||
map.set("hello", "world");
|
||||
|
||||
const promise = loadCoValueOrFail(client.node, map.id);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(
|
||||
CO_VALUE_LOADING_CONFIG.TIMEOUT +
|
||||
CO_VALUE_LOADING_CONFIG.RETRY_DELAY +
|
||||
10,
|
||||
);
|
||||
|
||||
client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(
|
||||
CO_VALUE_LOADING_CONFIG.TIMEOUT +
|
||||
CO_VALUE_LOADING_CONFIG.RETRY_DELAY +
|
||||
10,
|
||||
);
|
||||
|
||||
const coValue = await promise;
|
||||
|
||||
expect(coValue).not.toBe("unavailable");
|
||||
|
||||
expect(
|
||||
SyncMessagesLog.getMessages({
|
||||
Group: group.core,
|
||||
Map: map.core,
|
||||
}),
|
||||
).toMatchInlineSnapshot(`
|
||||
[
|
||||
"client -> server | LOAD Map sessions: empty",
|
||||
"server -> client | CONTENT Group header: true new: After: 0 New: 5",
|
||||
"server -> client | CONTENT Map header: true new: After: 0 New: 1",
|
||||
"client -> server | KNOWN Group sessions: header/5",
|
||||
"client -> server | KNOWN Map sessions: header/1",
|
||||
]
|
||||
`);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +26,6 @@ function setupMesh() {
|
||||
ourName: "edge-italy",
|
||||
syncServerName: "core",
|
||||
syncServer: coreServer.node,
|
||||
persistent: true,
|
||||
});
|
||||
edgeItaly.addStorage({
|
||||
ourName: "edge-italy",
|
||||
@@ -37,7 +36,6 @@ function setupMesh() {
|
||||
ourName: "edge-france",
|
||||
syncServerName: "core",
|
||||
syncServer: coreServer.node,
|
||||
persistent: true,
|
||||
});
|
||||
edgeFrance.addStorage({
|
||||
ourName: "edge-france",
|
||||
|
||||
@@ -111,12 +111,10 @@ test("Can sync a coValue with private transactions through a server to another c
|
||||
expect(mapOnClient2.get("hello")).toEqual("world");
|
||||
});
|
||||
|
||||
test("should keep the peer state when the peer closes if persistent is true", async () => {
|
||||
test("should keep the peer state when the peer closes", async () => {
|
||||
const client = setupTestNode();
|
||||
|
||||
const { peer, peerState, peerOnServer } = client.connectToSyncServer({
|
||||
persistent: true,
|
||||
});
|
||||
const { peer, peerState, peerOnServer } = client.connectToSyncServer();
|
||||
|
||||
const group = jazzCloud.node.createGroup();
|
||||
const map = group.createMap();
|
||||
@@ -135,12 +133,12 @@ test("should keep the peer state when the peer closes if persistent is true", as
|
||||
expect(syncManager.peers[peer.id]).not.toBeUndefined();
|
||||
});
|
||||
|
||||
test("should delete the peer state when the peer closes if persistent is false", async () => {
|
||||
test("should delete the peer state when the peer closes if deletePeerStateOnClose is true", async () => {
|
||||
const client = setupTestNode();
|
||||
|
||||
const { peer, peerState, peerOnServer } = client.connectToSyncServer({
|
||||
persistent: false,
|
||||
});
|
||||
const { peer, peerState, peerOnServer } = client.connectToSyncServer();
|
||||
|
||||
peer.deletePeerStateOnClose = true;
|
||||
|
||||
const group = jazzCloud.node.createGroup();
|
||||
const map = group.createMap();
|
||||
|
||||
@@ -429,7 +429,6 @@ export function getSyncServerConnectedPeer(opts: {
|
||||
ourName?: string;
|
||||
syncServer?: LocalNode;
|
||||
peerId: string;
|
||||
persistent?: boolean;
|
||||
}) {
|
||||
const currentSyncServer = opts?.syncServer ?? syncServer.current;
|
||||
|
||||
@@ -452,7 +451,6 @@ export function getSyncServerConnectedPeer(opts: {
|
||||
role: "client",
|
||||
name: opts.ourName,
|
||||
},
|
||||
persistent: opts?.persistent,
|
||||
});
|
||||
|
||||
currentSyncServer.syncManager.addPeer(peer2);
|
||||
@@ -485,7 +483,6 @@ export function setupTestNode(
|
||||
syncServerName?: string;
|
||||
ourName?: string;
|
||||
syncServer?: LocalNode;
|
||||
persistent?: boolean;
|
||||
}) {
|
||||
const { peer, peerStateOnServer, peerOnServer } =
|
||||
getSyncServerConnectedPeer({
|
||||
@@ -493,7 +490,6 @@ export function setupTestNode(
|
||||
syncServerName: opts?.syncServerName,
|
||||
ourName: opts?.ourName,
|
||||
syncServer: opts?.syncServer,
|
||||
persistent: opts?.persistent,
|
||||
});
|
||||
|
||||
node.syncManager.addPeer(peer);
|
||||
@@ -650,22 +646,12 @@ export type SyncTestMessage = {
|
||||
export function connectedPeersWithMessagesTracking(opts: {
|
||||
peer1: { id: string; role: Peer["role"]; name?: string };
|
||||
peer2: { id: string; role: Peer["role"]; name?: string };
|
||||
persistent?: boolean;
|
||||
}) {
|
||||
const [peer1, peer2] = connectedPeers(opts.peer1.id, opts.peer2.id, {
|
||||
peer1role: opts.peer1.role,
|
||||
peer2role: opts.peer2.role,
|
||||
persistent: opts.persistent,
|
||||
});
|
||||
|
||||
// If the persistent option is not provided, we default to true for the server and false for the client
|
||||
// Trying to mimic the real world behavior of the sync server
|
||||
if (opts.persistent === undefined) {
|
||||
peer1.persistent = opts.peer1.role === "server";
|
||||
|
||||
peer2.persistent = opts.peer2.role === "server";
|
||||
}
|
||||
|
||||
const peer1Push = peer1.outgoing.push;
|
||||
peer1.outgoing.push = (msg) => {
|
||||
if (typeof msg !== "string") {
|
||||
|
||||
@@ -7890,7 +7890,7 @@ export const JazzAccountRoot = co.map({
|
||||
export const JazzAccount = co
|
||||
.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile(),
|
||||
profile: co.profile({}),
|
||||
})
|
||||
.withMigration((account) => {
|
||||
if (account.root === undefined) {
|
||||
@@ -7950,7 +7950,7 @@ const JazzAccountRoot = co.map({
|
||||
|
||||
const JazzAccount = co.account({
|
||||
root: JazzAccountRoot,
|
||||
profile: co.profile(),
|
||||
profile: co.profile({}),
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
# jazz-auth-betterauth
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
- jazz-betterauth-client-plugin@0.15.15
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- cojson@0.15.14
|
||||
- jazz-tools@0.15.14
|
||||
- jazz-betterauth-client-plugin@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-auth-betterauth",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
# jazz-betterauth-client-plugin
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-client-plugin",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,23 +1,5 @@
|
||||
# jazz-betterauth-server-plugin
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- cojson@0.15.14
|
||||
- jazz-tools@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-betterauth-server-plugin",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
# jazz-react-auth-betterauth
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
- jazz-auth-betterauth@0.15.15
|
||||
- jazz-betterauth-client-plugin@0.15.15
|
||||
- cojson@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- cojson@0.15.14
|
||||
- jazz-tools@0.15.14
|
||||
- jazz-auth-betterauth@0.15.14
|
||||
- jazz-betterauth-client-plugin@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-auth-betterauth",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.tsx",
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
# jazz-run
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
- cojson@0.15.15
|
||||
- cojson-storage-sqlite@0.15.15
|
||||
- cojson-transport-ws@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [70ce7c5]
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- cojson-transport-ws@0.15.14
|
||||
- cojson@0.15.14
|
||||
- jazz-tools@0.15.14
|
||||
- cojson-storage-sqlite@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"bin": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"exports": {
|
||||
"./startSyncServer": {
|
||||
"import": "./dist/startSyncServer.js",
|
||||
@@ -28,11 +28,11 @@
|
||||
"@effect/printer-ansi": "^0.34.5",
|
||||
"@effect/schema": "^0.71.1",
|
||||
"@effect/typeclass": "^0.25.5",
|
||||
"cojson": "workspace:0.15.15",
|
||||
"cojson-storage-sqlite": "workspace:0.15.15",
|
||||
"cojson-transport-ws": "workspace:0.15.15",
|
||||
"cojson": "workspace:0.15.13",
|
||||
"cojson-storage-sqlite": "workspace:0.15.13",
|
||||
"cojson-transport-ws": "workspace:0.15.13",
|
||||
"effect": "^3.6.5",
|
||||
"jazz-tools": "workspace:0.15.15",
|
||||
"jazz-tools": "workspace:0.15.13",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -230,7 +230,8 @@ describe("startWorker integration", () => {
|
||||
await worker2.done();
|
||||
});
|
||||
|
||||
test("worker reconnects when sync server is closed and reopened", async () => {
|
||||
// Flaky test, fails randomly on CI
|
||||
test.skip("worker reconnects when sync server is closed and reopened", async () => {
|
||||
const worker1 = await setup();
|
||||
const worker2 = await setupWorker(worker1.syncServer);
|
||||
|
||||
@@ -266,6 +267,10 @@ describe("startWorker integration", () => {
|
||||
db: "",
|
||||
});
|
||||
|
||||
// Wait for reconnection
|
||||
await worker1.waitForConnection();
|
||||
await worker2.waitForConnection();
|
||||
|
||||
await worker1.worker.waitForAllCoValuesSync();
|
||||
|
||||
// Verify both old and new values are synced
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
# jazz-tools
|
||||
|
||||
## 0.15.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3fe53a3: Fix property update when assigning an optional reference on CoMap
|
||||
- cojson@0.15.15
|
||||
- cojson-storage-indexeddb@0.15.15
|
||||
- cojson-transport-ws@0.15.15
|
||||
|
||||
## 0.15.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a584590: Prevent resolving discriminated union fields
|
||||
- 9acccb5: Export `WithHelpers` type used in CoValue schemas
|
||||
- Updated dependencies [70ce7c5]
|
||||
- cojson-transport-ws@0.15.14
|
||||
- cojson@0.15.14
|
||||
- cojson-storage-indexeddb@0.15.14
|
||||
|
||||
## 0.15.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.15.15",
|
||||
"version": "0.15.13",
|
||||
"dependencies": {
|
||||
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
|
||||
"@scure/base": "1.2.1",
|
||||
|
||||
@@ -2,23 +2,11 @@ import { SessionID } from "cojson";
|
||||
import { ItemsSym } from "../internal.js";
|
||||
import { type Account } from "./account.js";
|
||||
import { CoFeedEntry } from "./coFeed.js";
|
||||
import { type CoKeys } from "./coMap.js";
|
||||
import { type CoKeys, type CoMap } from "./coMap.js";
|
||||
import { type CoValue, type ID } from "./interfaces.js";
|
||||
|
||||
type NotNull<T> = Exclude<T, null>;
|
||||
|
||||
/**
|
||||
* Used to check if T is a union type.
|
||||
*
|
||||
* If T is a union type, the left hand side of the extends becomes a union of function types.
|
||||
* The right hand side is always a single function type.
|
||||
*/
|
||||
type IsUnion<T, U = T> = (T extends any ? (x: T) => void : never) extends (
|
||||
x: U,
|
||||
) => void
|
||||
? false
|
||||
: true;
|
||||
|
||||
export type RefsToResolve<
|
||||
V,
|
||||
DepthLimit extends number = 10,
|
||||
@@ -28,58 +16,56 @@ export type RefsToResolve<
|
||||
| (DepthLimit extends CurrentDepth["length"]
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
any
|
||||
: IsUnion<NonNullable<V>> extends true
|
||||
? true
|
||||
: // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
|
||||
V extends Array<infer Item>
|
||||
: // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
|
||||
V extends Array<infer Item>
|
||||
?
|
||||
| {
|
||||
$each: RefsToResolve<
|
||||
NotNull<Item>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
$onError?: null;
|
||||
}
|
||||
| boolean
|
||||
: // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
|
||||
V extends { _type: "CoMap" | "Group" | "Account" }
|
||||
?
|
||||
| {
|
||||
$each: RefsToResolve<
|
||||
NotNull<Item>,
|
||||
| ({
|
||||
[Key in CoKeys<V> as NonNullable<V[Key]> extends CoValue
|
||||
? Key
|
||||
: never]?: RefsToResolve<
|
||||
NonNullable<V[Key]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
$onError?: null;
|
||||
}
|
||||
| boolean
|
||||
: // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
|
||||
V extends { _type: "CoMap" | "Group" | "Account" }
|
||||
?
|
||||
| ({
|
||||
[Key in CoKeys<V> as NonNullable<V[Key]> extends CoValue
|
||||
? Key
|
||||
: never]?: RefsToResolve<
|
||||
NonNullable<V[Key]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
} & { $onError?: null })
|
||||
| (ItemsSym extends keyof V
|
||||
? {
|
||||
$each: RefsToResolve<
|
||||
NonNullable<V[ItemsSym]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
$onError?: null;
|
||||
}
|
||||
: never)
|
||||
| boolean
|
||||
: V extends {
|
||||
_type: "CoStream";
|
||||
byMe: CoFeedEntry<infer Item> | undefined;
|
||||
}
|
||||
?
|
||||
| {
|
||||
} & { $onError?: null })
|
||||
| (ItemsSym extends keyof V
|
||||
? {
|
||||
$each: RefsToResolve<
|
||||
NotNull<Item>,
|
||||
NonNullable<V[ItemsSym]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
$onError?: null;
|
||||
}
|
||||
| boolean
|
||||
: boolean);
|
||||
: never)
|
||||
| boolean
|
||||
: V extends {
|
||||
_type: "CoStream";
|
||||
byMe: CoFeedEntry<infer Item> | undefined;
|
||||
}
|
||||
?
|
||||
| {
|
||||
$each: RefsToResolve<
|
||||
NotNull<Item>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
$onError?: null;
|
||||
}
|
||||
| boolean
|
||||
: boolean);
|
||||
|
||||
export type RefsToResolveStrict<T, V> = V extends RefsToResolve<T>
|
||||
? RefsToResolve<T>
|
||||
|
||||
@@ -35,7 +35,6 @@ export type {
|
||||
TextPos,
|
||||
AccountClass,
|
||||
AccountCreationProps,
|
||||
WithHelpers,
|
||||
} from "./internal.js";
|
||||
|
||||
export {
|
||||
|
||||
@@ -341,15 +341,17 @@ export class SubscriptionScope<D extends CoValue> {
|
||||
this.resolve = {};
|
||||
}
|
||||
|
||||
if (!this.resolve.$each && !(key in this.resolve)) {
|
||||
const resolve = this.resolve as Record<string, any>;
|
||||
|
||||
// Adding the key to the resolve object to resolve the key when calling loadChildren
|
||||
resolve[key] = true;
|
||||
// Track the keys that are autoloaded to flag any id on that key as autoloaded
|
||||
this.autoloadedKeys.add(key);
|
||||
if (this.resolve.$each || key in this.resolve) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolve = this.resolve as Record<string, any>;
|
||||
|
||||
// Adding the key to the resolve object to resolve the key when calling loadChildren
|
||||
resolve[key] = true;
|
||||
// Track the keys that are autoloaded to flag any id on that key as autoloaded
|
||||
this.autoloadedKeys.add(key);
|
||||
|
||||
if (this.value.type !== "loaded") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2103,144 +2103,3 @@ describe("CoMap migration", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Updating a nested reference", () => {
|
||||
test("should assign a resolved optional reference and expect value is not null", async () => {
|
||||
// Define the schema similar to the server-worker-http example
|
||||
const PlaySelection = co.map({
|
||||
value: z.literal(["rock", "paper", "scissors"]),
|
||||
group: Group,
|
||||
});
|
||||
|
||||
const Player = co.map({
|
||||
account: co.account(),
|
||||
playSelection: PlaySelection.optional(),
|
||||
});
|
||||
|
||||
const Game = co.map({
|
||||
player1: Player,
|
||||
player2: Player,
|
||||
outcome: z.literal(["player1", "player2", "draw"]).optional(),
|
||||
player1Score: z.number(),
|
||||
player2Score: z.number(),
|
||||
});
|
||||
|
||||
// Create accounts for the players
|
||||
const player1Account = await createJazzTestAccount({
|
||||
creationProps: { name: "Player 1" },
|
||||
});
|
||||
const player2Account = await createJazzTestAccount({
|
||||
creationProps: { name: "Player 2" },
|
||||
});
|
||||
|
||||
// Create a game
|
||||
const game = Game.create({
|
||||
player1: Player.create({
|
||||
account: player1Account,
|
||||
}),
|
||||
player2: Player.create({
|
||||
account: player2Account,
|
||||
}),
|
||||
player1Score: 0,
|
||||
player2Score: 0,
|
||||
});
|
||||
|
||||
// Create a group for the play selection (similar to the route logic)
|
||||
const group = Group.create({ owner: Account.getMe() });
|
||||
group.addMember(player1Account, "reader");
|
||||
|
||||
// Load the game to verify the assignment worked
|
||||
const loadedGame = await Game.load(game.id, {
|
||||
resolve: {
|
||||
player1: {
|
||||
account: true,
|
||||
playSelection: true,
|
||||
},
|
||||
player2: {
|
||||
account: true,
|
||||
playSelection: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert(loadedGame);
|
||||
|
||||
// Create a play selection
|
||||
const playSelection = PlaySelection.create({ value: "rock", group }, group);
|
||||
|
||||
// Assign the play selection to player1 (similar to the route logic)
|
||||
loadedGame.player1.playSelection = playSelection;
|
||||
|
||||
// Verify that the playSelection is not null and has the expected value
|
||||
expect(loadedGame.player1.playSelection).not.toBeNull();
|
||||
expect(loadedGame.player1.playSelection).toBeDefined();
|
||||
});
|
||||
|
||||
test("should assign a resolved reference and expect value to update", async () => {
|
||||
// Define the schema similar to the server-worker-http example
|
||||
const PlaySelection = co.map({
|
||||
value: z.literal(["rock", "paper", "scissors"]),
|
||||
});
|
||||
|
||||
const Player = co.map({
|
||||
account: co.account(),
|
||||
playSelection: PlaySelection,
|
||||
});
|
||||
|
||||
const Game = co.map({
|
||||
player1: Player,
|
||||
player2: Player,
|
||||
outcome: z.literal(["player1", "player2", "draw"]).optional(),
|
||||
player1Score: z.number(),
|
||||
player2Score: z.number(),
|
||||
});
|
||||
|
||||
// Create accounts for the players
|
||||
const player1Account = await createJazzTestAccount({
|
||||
creationProps: { name: "Player 1" },
|
||||
});
|
||||
const player2Account = await createJazzTestAccount({
|
||||
creationProps: { name: "Player 2" },
|
||||
});
|
||||
|
||||
// Create a game
|
||||
const game = Game.create({
|
||||
player1: Player.create({
|
||||
account: player1Account,
|
||||
playSelection: PlaySelection.create({ value: "rock" }),
|
||||
}),
|
||||
player2: Player.create({
|
||||
account: player2Account,
|
||||
playSelection: PlaySelection.create({ value: "paper" }),
|
||||
}),
|
||||
player1Score: 0,
|
||||
player2Score: 0,
|
||||
});
|
||||
|
||||
// Load the game to verify the assignment worked
|
||||
const loadedGame = await Game.load(game.id, {
|
||||
resolve: {
|
||||
player1: {
|
||||
account: true,
|
||||
playSelection: true,
|
||||
},
|
||||
player2: {
|
||||
account: true,
|
||||
playSelection: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert(loadedGame);
|
||||
|
||||
// Create a play selection
|
||||
const playSelection = PlaySelection.create({ value: "scissors" });
|
||||
|
||||
// Assign the play selection to player1 (similar to the route logic)
|
||||
loadedGame.player1.playSelection = playSelection;
|
||||
|
||||
// Verify that the playSelection is not null and has the expected value
|
||||
expect(loadedGame.player1.playSelection.id).toBe(playSelection.id);
|
||||
expect(loadedGame.player1.playSelection.value).toEqual("scissors");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -381,64 +381,6 @@ test("The resolve type doesn't accept extra keys", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("The resolve type accepts keys from optional fields", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
owner: Person.optional(),
|
||||
});
|
||||
const Pets = co.list(Dog);
|
||||
|
||||
const pets = await Pets.create([
|
||||
Dog.create({ type: "dog", owner: Person.create({ name: "Rex" }) }),
|
||||
]);
|
||||
|
||||
await pets.ensureLoaded({
|
||||
resolve: {
|
||||
$each: { owner: true },
|
||||
},
|
||||
});
|
||||
|
||||
expect(pets[0]?.owner?.name).toEqual("Rex");
|
||||
});
|
||||
|
||||
test("The resolve type doesn't accept keys from discriminated unions", async () => {
|
||||
const Person = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const Dog = co.map({
|
||||
type: z.literal("dog"),
|
||||
owner: Person,
|
||||
});
|
||||
const Cat = co.map({
|
||||
type: z.literal("cat"),
|
||||
});
|
||||
const Pet = co.discriminatedUnion("type", [Dog, Cat]);
|
||||
const Pets = co.list(Pet);
|
||||
|
||||
const pets = await Pets.create([
|
||||
Dog.create({ type: "dog", owner: Person.create({ name: "Rex" }) }),
|
||||
]);
|
||||
|
||||
await pets.ensureLoaded({
|
||||
resolve: {
|
||||
$each: true,
|
||||
},
|
||||
});
|
||||
|
||||
await pets.ensureLoaded({
|
||||
// @ts-expect-error cannot resolve owner
|
||||
resolve: { $each: { owner: true } },
|
||||
});
|
||||
|
||||
expect(pets).toBeTruthy();
|
||||
if (pets?.[0]?.type === "dog") {
|
||||
expect(pets[0].owner?.name).toEqual("Rex");
|
||||
}
|
||||
});
|
||||
|
||||
describe("Deep loading with unauthorized account", async () => {
|
||||
const bob = await createJazzTestAccount({
|
||||
creationProps: { name: "Bob" },
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# quint-ui
|
||||
|
||||
## 0.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f61a120: Initial Quint-UI release
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "quint-ui",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
".":{
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
|
||||
186
pnpm-lock.yaml
generated
186
pnpm-lock.yaml
generated
@@ -179,9 +179,6 @@ importers:
|
||||
|
||||
examples/chat:
|
||||
dependencies:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
hash-slash:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/hash-slash
|
||||
@@ -191,6 +188,9 @@ importers:
|
||||
lucide-react:
|
||||
specifier: ^0.274.0
|
||||
version: 0.274.0(react@19.1.0)
|
||||
quint-ui:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/quint-ui
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
@@ -852,6 +852,82 @@ importers:
|
||||
specifier: ^5
|
||||
version: 5.8.3
|
||||
|
||||
examples/jazz-paper-scissors:
|
||||
dependencies:
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.7(@types/react-dom@19.1.0(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-separator':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.7(@types/react-dom@19.1.0(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.1.2
|
||||
version: 1.2.3(@types/react@19.1.0)(react@19.1.0)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.10(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
'@tanstack/react-router':
|
||||
specifier: ^1.115.0
|
||||
version: 1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@tanstack/react-router-devtools':
|
||||
specifier: ^1.114.29
|
||||
version: 1.116.0(@tanstack/react-router@1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.115.3)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: ^1.114.30
|
||||
version: 1.116.1(@tanstack/react-router@1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
jazz-tools:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/jazz-tools
|
||||
lucide-react:
|
||||
specifier: ^0.485.0
|
||||
version: 0.485.0(react@19.1.0)
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-dom:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
tailwind-merge:
|
||||
specifier: ^3.0.2
|
||||
version: 3.3.0
|
||||
tailwindcss:
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.10
|
||||
tw-animate-css:
|
||||
specifier: ^1.2.5
|
||||
version: 1.2.8
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
'@types/react-dom':
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(@types/react@19.1.0)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.3.4
|
||||
version: 4.5.1(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
jazz-run:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/jazz-run
|
||||
npm-run-all:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
tsx:
|
||||
specifier: ^4.19.3
|
||||
version: 4.19.3
|
||||
typescript:
|
||||
specifier: ~5.6.2
|
||||
version: 5.6.2
|
||||
vite:
|
||||
specifier: 6.3.5
|
||||
version: 6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1)
|
||||
|
||||
examples/multi-cursors:
|
||||
dependencies:
|
||||
'@react-spring/web':
|
||||
@@ -1347,82 +1423,6 @@ importers:
|
||||
specifier: 6.3.5
|
||||
version: 6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.20.3)(yaml@2.6.1)
|
||||
|
||||
examples/server-worker-inbox:
|
||||
dependencies:
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.7(@types/react-dom@19.1.0(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-separator':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.7(@types/react-dom@19.1.0(@types/react@19.1.0))(@types/react@19.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.1.2
|
||||
version: 1.2.3(@types/react@19.1.0)(react@19.1.0)
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.10(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
'@tanstack/react-router':
|
||||
specifier: ^1.115.0
|
||||
version: 1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@tanstack/react-router-devtools':
|
||||
specifier: ^1.114.29
|
||||
version: 1.116.0(@tanstack/react-router@1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.115.3)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: ^1.114.30
|
||||
version: 1.116.1(@tanstack/react-router@1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
jazz-tools:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/jazz-tools
|
||||
lucide-react:
|
||||
specifier: ^0.485.0
|
||||
version: 0.485.0(react@19.1.0)
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-dom:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
tailwind-merge:
|
||||
specifier: ^3.0.2
|
||||
version: 3.3.0
|
||||
tailwindcss:
|
||||
specifier: ^4.0.17
|
||||
version: 4.1.11
|
||||
tw-animate-css:
|
||||
specifier: ^1.2.5
|
||||
version: 1.2.8
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
'@types/react-dom':
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0(@types/react@19.1.0)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.3.4
|
||||
version: 4.5.1(vite@6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1))
|
||||
jazz-run:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/jazz-run
|
||||
npm-run-all:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
tsx:
|
||||
specifier: ^4.19.3
|
||||
version: 4.19.3
|
||||
typescript:
|
||||
specifier: ~5.6.2
|
||||
version: 5.6.2
|
||||
vite:
|
||||
specifier: 6.3.5
|
||||
version: 6.3.5(@types/node@22.16.5)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.37.0)(tsx@4.19.3)(yaml@2.6.1)
|
||||
|
||||
examples/todo:
|
||||
dependencies:
|
||||
'@radix-ui/react-checkbox':
|
||||
@@ -1772,19 +1772,19 @@ importers:
|
||||
specifier: ^0.25.5
|
||||
version: 0.25.8(effect@3.11.9)
|
||||
cojson:
|
||||
specifier: workspace:0.15.15
|
||||
specifier: workspace:0.15.13
|
||||
version: link:../cojson
|
||||
cojson-storage-sqlite:
|
||||
specifier: workspace:0.15.15
|
||||
specifier: workspace:0.15.13
|
||||
version: link:../cojson-storage-sqlite
|
||||
cojson-transport-ws:
|
||||
specifier: workspace:0.15.15
|
||||
specifier: workspace:0.15.13
|
||||
version: link:../cojson-transport-ws
|
||||
effect:
|
||||
specifier: ^3.6.5
|
||||
version: 3.11.9
|
||||
jazz-tools:
|
||||
specifier: workspace:0.15.15
|
||||
specifier: workspace:0.15.13
|
||||
version: link:../jazz-tools
|
||||
ws:
|
||||
specifier: ^8.14.2
|
||||
@@ -13867,7 +13867,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.25.9':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.27.3':
|
||||
dependencies:
|
||||
@@ -13942,7 +13942,7 @@ snapshots:
|
||||
'@babel/helper-module-imports@7.27.1':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -13995,7 +13995,7 @@ snapshots:
|
||||
'@babel/helper-skip-transparent-expression-wrappers@7.25.9':
|
||||
dependencies:
|
||||
'@babel/traverse': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -18757,7 +18757,7 @@ snapshots:
|
||||
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.1)
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
'@tanstack/router-core': 1.115.3
|
||||
'@tanstack/router-generator': 1.116.0(@tanstack/react-router@1.116.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
|
||||
'@tanstack/router-utils': 1.115.0
|
||||
@@ -19017,16 +19017,16 @@ snapshots:
|
||||
|
||||
'@types/babel__generator@7.6.8':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
|
||||
'@types/babel__template@7.4.4':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.2
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
|
||||
'@types/babel__traverse@7.20.6':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
|
||||
'@types/better-sqlite3@7.6.12':
|
||||
dependencies:
|
||||
@@ -20125,7 +20125,7 @@ snapshots:
|
||||
'@babel/core': 7.27.1
|
||||
'@babel/parser': 7.27.2
|
||||
'@babel/traverse': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -23795,7 +23795,7 @@ snapshots:
|
||||
'@babel/parser': 7.27.2
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.27.1
|
||||
accepts: 1.3.8
|
||||
chalk: 4.1.2
|
||||
ci-info: 2.0.0
|
||||
@@ -26990,7 +26990,7 @@ snapshots:
|
||||
|
||||
unplugin@2.3.2:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
acorn: 8.14.1
|
||||
picomatch: 4.0.2
|
||||
webpack-virtual-modules: 0.6.2
|
||||
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
# jazz-react-tailwind-starter
|
||||
|
||||
## 0.0.135
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
|
||||
## 0.0.134
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- jazz-tools@0.15.14
|
||||
|
||||
## 0.0.133
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-react-passkey-auth-starter",
|
||||
"private": true,
|
||||
"version": "0.0.135",
|
||||
"version": "0.0.133",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
# svelte-passkey-auth
|
||||
|
||||
## 0.0.109
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3fe53a3]
|
||||
- jazz-tools@0.15.15
|
||||
|
||||
## 0.0.108
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a584590]
|
||||
- Updated dependencies [9acccb5]
|
||||
- jazz-tools@0.15.14
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-passkey-auth",
|
||||
"version": "0.0.109",
|
||||
"version": "0.0.107",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -201,10 +201,10 @@ describe("Browser sync", () => {
|
||||
|
||||
await map.waitForSync();
|
||||
|
||||
await syncServer.setOnline(true);
|
||||
await syncServer.setOffline(true);
|
||||
|
||||
onTestFinished(async () => {
|
||||
await syncServer.setOnline(false);
|
||||
await syncServer.setOffline(false);
|
||||
});
|
||||
|
||||
// Clearing the credentials storage so the next auth will be a new account
|
||||
|
||||
@@ -36,11 +36,9 @@ const disconnectAllClientsCommand: BrowserCommand<[url: string]> = async (
|
||||
syncServer.disconnectAllClients();
|
||||
};
|
||||
|
||||
const setOnlineCommand: BrowserCommand<[url: string, active: boolean]> = async (
|
||||
ctx,
|
||||
url,
|
||||
active,
|
||||
) => {
|
||||
const setOfflineCommand: BrowserCommand<
|
||||
[url: string, active: boolean]
|
||||
> = async (ctx, url, active) => {
|
||||
const syncServer = syncServers.get(url);
|
||||
|
||||
if (!syncServer) {
|
||||
@@ -79,7 +77,7 @@ declare module "@vitest/browser/context" {
|
||||
port: number;
|
||||
}>;
|
||||
disconnectAllClients: (url: string) => Promise<void>;
|
||||
setOnline: (url: string, active: boolean) => Promise<void>;
|
||||
setOffline: (url: string, active: boolean) => Promise<void>;
|
||||
closeSyncServer: (url: string) => Promise<void>;
|
||||
cleanup: () => Promise<void>;
|
||||
}
|
||||
@@ -88,7 +86,7 @@ declare module "@vitest/browser/context" {
|
||||
export const customCommands = {
|
||||
startSyncServer: startSyncServerCommand,
|
||||
disconnectAllClients: disconnectAllClientsCommand,
|
||||
setOnline: setOnlineCommand,
|
||||
setOffline: setOfflineCommand,
|
||||
closeSyncServer: closeSyncServerCommand,
|
||||
cleanup: cleanupCommand,
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ export async function startSyncServer(port?: number, dbName?: string) {
|
||||
url,
|
||||
port: syncServerPort,
|
||||
disconnectAllClients: () => commands.disconnectAllClients(url),
|
||||
setOnline: (active: boolean) => commands.setOnline(url, active),
|
||||
setOffline: (active: boolean) => commands.setOffline(url, active),
|
||||
close,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { commands } from "@vitest/browser/context";
|
||||
import { AuthSecretStorage, FileStream, Group, co, z } from "jazz-tools";
|
||||
import { assert, afterAll, afterEach, describe, expect, test } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
AuthSecretStorage,
|
||||
CoMap,
|
||||
FileStream,
|
||||
Group,
|
||||
coField,
|
||||
} from "jazz-tools";
|
||||
import { afterAll, afterEach, describe, expect, test } from "vitest";
|
||||
import { createAccountContext, startSyncServer } from "./testUtils";
|
||||
|
||||
const TestMAP = co.map({
|
||||
value: z.string(),
|
||||
});
|
||||
class TestMap extends CoMap {
|
||||
value = coField.string;
|
||||
}
|
||||
|
||||
const CustomAccount = co
|
||||
.account({
|
||||
root: TestMAP,
|
||||
profile: co.profile(),
|
||||
})
|
||||
.withMigration((me) => {
|
||||
if (me.root === undefined) {
|
||||
me.root = TestMAP.create({ value: "initial" }, { owner: me });
|
||||
class CustomAccount extends Account {
|
||||
root = coField.ref(TestMap);
|
||||
|
||||
migrate() {
|
||||
if (!this.root) {
|
||||
this.root = TestMap.create({ value: "initial" }, { owner: this });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
afterAll(async () => {
|
||||
await commands.cleanup();
|
||||
@@ -37,13 +43,13 @@ describe("Browser sync on unstable connection", () => {
|
||||
AccountSchema: CustomAccount,
|
||||
});
|
||||
|
||||
const bytes1MB = 1e6;
|
||||
const bytes10MB = 1e7;
|
||||
|
||||
const group = Group.create();
|
||||
group.addMember("everyone", "reader");
|
||||
|
||||
const promise = FileStream.createFromBlob(
|
||||
new Blob(["1".repeat(bytes1MB)], { type: "image/png" }),
|
||||
new Blob(["1".repeat(bytes10MB)], { type: "image/png" }),
|
||||
group,
|
||||
);
|
||||
|
||||
@@ -72,63 +78,7 @@ describe("Browser sync on unstable connection", () => {
|
||||
|
||||
const fileOnSecondAccount = await promise2;
|
||||
|
||||
expect(fileOnSecondAccount?.size).toBe(bytes1MB);
|
||||
});
|
||||
|
||||
test("wait for online when creating data when offline and calling waitForSync", async () => {
|
||||
const syncServer = await startSyncServer();
|
||||
const { contextManager } = await createAccountContext({
|
||||
sync: {
|
||||
peer: syncServer.url,
|
||||
},
|
||||
storage: "indexedDB",
|
||||
AccountSchema: CustomAccount,
|
||||
});
|
||||
|
||||
const group = Group.create();
|
||||
group.addMember("everyone", "reader");
|
||||
|
||||
await syncServer.setOnline(false);
|
||||
|
||||
const map = TestMAP.create({ value: "initial" }, { owner: group });
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
await syncServer.setOnline(true);
|
||||
|
||||
await map.waitForSync();
|
||||
|
||||
contextManager.done();
|
||||
|
||||
await new AuthSecretStorage().clear();
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const req = indexedDB.deleteDatabase("jazz-storage");
|
||||
req.onsuccess = function () {
|
||||
resolve(undefined);
|
||||
};
|
||||
});
|
||||
|
||||
await createAccountContext({
|
||||
sync: {
|
||||
peer: syncServer.url,
|
||||
},
|
||||
storage: "indexedDB",
|
||||
AccountSchema: CustomAccount,
|
||||
});
|
||||
|
||||
await syncServer.setOnline(false);
|
||||
|
||||
const promise = TestMAP.load(map.id);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
await syncServer.setOnline(true);
|
||||
|
||||
const mapOnSecondAccount = await promise;
|
||||
|
||||
assert(mapOnSecondAccount);
|
||||
|
||||
expect(mapOnSecondAccount.value).toBe("initial");
|
||||
expect(fileOnSecondAccount?.size).toBe(bytes10MB);
|
||||
});
|
||||
|
||||
test("load files from storage correctly when pointing to different sync servers", async () => {
|
||||
|
||||
Reference in New Issue
Block a user