Compare commits
3 Commits
jazz-nodej
...
not-found-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b426342c02 | ||
|
|
064d19b48c | ||
|
|
3ea269f4d8 |
5
.changeset/afraid-queens-bathe.md
Normal file
5
.changeset/afraid-queens-bathe.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"cojson": patch
|
||||
---
|
||||
|
||||
Export the coValue loading config to reduce the timeout on tests
|
||||
8
.changeset/silent-turtles-obey.md
Normal file
8
.changeset/silent-turtles-obey.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"jazz-react-core": minor
|
||||
"jazz-svelte": minor
|
||||
"jazz-tools": minor
|
||||
"jazz-vue": minor
|
||||
---
|
||||
|
||||
Return null when a coValue is not found
|
||||
@@ -7,7 +7,7 @@ import QRCode from "qrcode";
|
||||
|
||||
import { Button, useToast } from "../basicComponents";
|
||||
|
||||
export function ShareButton({ petPost }: { petPost?: PetPost }) {
|
||||
export function ShareButton({ petPost }: { petPost?: PetPost | null }) {
|
||||
const [existingInviteLink, setExistingInviteLink] = useState<string>();
|
||||
const { toast } = useToast();
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export function InviteButton<T extends CoValue>({
|
||||
value,
|
||||
valueHint,
|
||||
}: {
|
||||
value?: T;
|
||||
value?: T | null;
|
||||
valueHint?: string;
|
||||
}) {
|
||||
const [existingInviteLink, setExistingInviteLink] = useState<string>();
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"version": "0.9.19",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^1.29.0",
|
||||
"@types/jest": "^29.5.3",
|
||||
"typescript": "~5.6.2",
|
||||
"vitest": "1.5.3"
|
||||
},
|
||||
|
||||
@@ -4,8 +4,10 @@ import { RawCoID } from "./ids.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { PeerID } from "./sync.js";
|
||||
|
||||
export const CO_VALUE_LOADING_MAX_RETRIES = 5;
|
||||
export const CO_VALUE_LOADING_TIMEOUT = 30_000;
|
||||
export const CO_VALUE_LOADING_CONFIG = {
|
||||
MAX_RETRIES: 5,
|
||||
TIMEOUT: 30_000,
|
||||
};
|
||||
|
||||
export class CoValueUnknownState {
|
||||
type = "unknown" as const;
|
||||
@@ -227,7 +229,7 @@ export class CoValueState {
|
||||
this.getCoValue(),
|
||||
runWithRetry(
|
||||
() => doLoad(peersWithRetry),
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
||||
),
|
||||
]);
|
||||
}
|
||||
@@ -313,7 +315,7 @@ async function loadCoValueFromPeers(
|
||||
peerId: peer.id,
|
||||
});
|
||||
}
|
||||
}, CO_VALUE_LOADING_TIMEOUT);
|
||||
}, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
||||
await coValueEntry.state.waitForPeer(peer.id);
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
|
||||
type Value = JsonValue | AnyRawCoValue;
|
||||
|
||||
import { CO_VALUE_LOADING_CONFIG } from "./coValueState.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { getPriorityFromHeader } from "./priority.js";
|
||||
import { FileSystem } from "./storage/FileSystem.js";
|
||||
@@ -103,6 +104,7 @@ export const cojsonInternals = {
|
||||
getGroupDependentKeyList,
|
||||
getGroupDependentKey,
|
||||
disablePermissionErrors,
|
||||
CO_VALUE_LOADING_CONFIG,
|
||||
};
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 { CO_VALUE_LOADING_CONFIG, CoValueState } from "../coValueState";
|
||||
import { RawCoID } from "../ids";
|
||||
import { Peer } from "../sync";
|
||||
|
||||
@@ -92,18 +92,18 @@ describe("CoValueState", () => {
|
||||
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++) {
|
||||
// Should attempt CO_VALUE_LOADING_CONFIG.MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
await loadPromise;
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
||||
);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toBe("unavailable");
|
||||
@@ -145,8 +145,8 @@ describe("CoValueState", () => {
|
||||
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++) {
|
||||
// Should attempt CO_VALUE_LOADING_CONFIG.MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ describe("CoValueState", () => {
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(1);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toBe("unavailable");
|
||||
@@ -194,8 +194,8 @@ describe("CoValueState", () => {
|
||||
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++) {
|
||||
// Should attempt CO_VALUE_LOADING_CONFIG.MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ describe("CoValueState", () => {
|
||||
|
||||
expect(peer1.pushOutgoingMessage).toHaveBeenCalledTimes(1);
|
||||
expect(peer2.pushOutgoingMessage).toHaveBeenCalledTimes(
|
||||
CO_VALUE_LOADING_MAX_RETRIES,
|
||||
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
||||
);
|
||||
expect(state.state.type).toBe("unavailable");
|
||||
await expect(state.getCoValue()).resolves.toEqual("unavailable");
|
||||
@@ -244,8 +244,8 @@ describe("CoValueState", () => {
|
||||
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++) {
|
||||
// Should attempt CO_VALUE_LOADING_CONFIG.MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES + 1; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
@@ -278,8 +278,8 @@ describe("CoValueState", () => {
|
||||
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++) {
|
||||
// Should attempt CO_VALUE_LOADING_CONFIG.MAX_RETRIES retries
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ describe("CoValueState", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers(mockPeers);
|
||||
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
await loadPromise;
|
||||
@@ -372,7 +372,7 @@ describe("CoValueState", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers([peer1, peer2]);
|
||||
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
await loadPromise;
|
||||
@@ -421,7 +421,7 @@ describe("CoValueState", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers([peer1, peer2]);
|
||||
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES; i++) {
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
await loadPromise;
|
||||
@@ -449,7 +449,7 @@ describe("CoValueState", () => {
|
||||
const state = CoValueState.Unknown(mockCoValueId);
|
||||
const loadPromise = state.loadFromPeers([peer1]);
|
||||
|
||||
for (let i = 0; i < CO_VALUE_LOADING_MAX_RETRIES * 2; i++) {
|
||||
for (let i = 0; i < CO_VALUE_LOADING_CONFIG.MAX_RETRIES * 2; i++) {
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
await loadPromise;
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
"type": "module",
|
||||
"main": "./dist/app.js",
|
||||
"types": "./dist/app.d.ts",
|
||||
"files": [
|
||||
"dist/**",
|
||||
"src"
|
||||
],
|
||||
"files": ["dist/**", "src"],
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-react-core",
|
||||
"version": "0.8.60",
|
||||
"version": "0.9.19",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -30,7 +30,7 @@ export function useCoState<V extends CoValue, D>(
|
||||
Schema: CoValueClass<V>,
|
||||
id: ID<V> | undefined,
|
||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||
): DeeplyLoaded<V, D> | undefined {
|
||||
): DeeplyLoaded<V, D> | undefined | null {
|
||||
const context = useJazzContext();
|
||||
|
||||
const [observable] = React.useState(() =>
|
||||
@@ -39,14 +39,19 @@ export function useCoState<V extends CoValue, D>(
|
||||
}),
|
||||
);
|
||||
|
||||
const value = React.useSyncExternalStore<DeeplyLoaded<V, D> | undefined>(
|
||||
const value = React.useSyncExternalStore<
|
||||
DeeplyLoaded<V, D> | undefined | null
|
||||
>(
|
||||
React.useCallback(
|
||||
(callback) => {
|
||||
if (!id) return () => {};
|
||||
|
||||
const agent = "me" in context ? context.me : context.guest;
|
||||
|
||||
return observable.subscribe(Schema, id, agent, depth, callback);
|
||||
return observable.subscribe(Schema, id, agent, depth, callback, () => {
|
||||
console.log("unavailable");
|
||||
callback();
|
||||
});
|
||||
},
|
||||
[Schema, id, context],
|
||||
),
|
||||
@@ -54,6 +59,8 @@ export function useCoState<V extends CoValue, D>(
|
||||
() => observable.getCurrentValue(),
|
||||
);
|
||||
|
||||
console.log("value", value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -64,10 +71,10 @@ export function createUseAccountHooks<Acc extends Account>() {
|
||||
};
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined; logOut: () => void };
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined | null; logOut: () => void };
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): { me: Acc | DeeplyLoaded<Acc, D> | undefined; logOut: () => void } {
|
||||
): { me: Acc | DeeplyLoaded<Acc, D> | undefined | null; logOut: () => void } {
|
||||
const context = useJazzContext<Acc>();
|
||||
|
||||
if (!("me" in context)) {
|
||||
@@ -89,10 +96,12 @@ export function createUseAccountHooks<Acc extends Account>() {
|
||||
};
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent };
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined | null | AnonymousJazzAgent };
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): { me: Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent } {
|
||||
): {
|
||||
me: Acc | DeeplyLoaded<Acc, D> | undefined | null | AnonymousJazzAgent;
|
||||
} {
|
||||
const context = useJazzContext<Acc>();
|
||||
|
||||
const contextMe = "me" in context ? context.me : undefined;
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { cojsonInternals } from "cojson";
|
||||
import { CoMap, co } from "jazz-tools";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { useCoState } from "../index.js";
|
||||
import { createJazzTestAccount } from "../testing.js";
|
||||
import { act, renderHook } from "./testUtils.js";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
||||
import { act, renderHook, waitFor } from "./testUtils.js";
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
|
||||
await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.MAX_RETRIES = 1;
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
|
||||
});
|
||||
|
||||
describe("useCoState", () => {
|
||||
it("should return the correct value", async () => {
|
||||
@@ -116,4 +130,31 @@ describe("useCoState", () => {
|
||||
expect(result.current?.value).toBe("123");
|
||||
expect(result.current?.nested?.value).toBe("456");
|
||||
});
|
||||
|
||||
it("should return null if the coValue is not found", async () => {
|
||||
class TestMap extends CoMap {
|
||||
value = co.string;
|
||||
}
|
||||
|
||||
const map = TestMap.create({
|
||||
value: "123",
|
||||
});
|
||||
|
||||
const account = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
});
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useCoState(TestMap, (map.id + "123") as any, {}),
|
||||
{
|
||||
account,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ export type RegisteredAccount = Register extends { Account: infer Acc }
|
||||
export function useAccount(): { me: RegisteredAccount; logOut: () => void };
|
||||
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth: D
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined; logOut: () => void };
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined | null; logOut: () => void };
|
||||
/**
|
||||
* Use the current account with a optional depth.
|
||||
* @param depth - The depth.
|
||||
@@ -55,7 +55,7 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
*/
|
||||
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth?: D
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined; logOut: () => void } {
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | null; logOut: () => void } {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
if (!ctx?.current) {
|
||||
throw new Error('useAccount must be used within a JazzProvider');
|
||||
@@ -98,7 +98,7 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
export function useAccountOrGuest(): { me: RegisteredAccount | AnonymousJazzAgent };
|
||||
export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth: D
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined | AnonymousJazzAgent };
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined | null | AnonymousJazzAgent };
|
||||
/**
|
||||
* Use the current account or guest with a optional depth.
|
||||
* @param depth - The depth.
|
||||
@@ -106,7 +106,7 @@ export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
*/
|
||||
export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth?: D
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | AnonymousJazzAgent } {
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | null | AnonymousJazzAgent } {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
|
||||
if (!ctx?.current) {
|
||||
@@ -153,12 +153,12 @@ export function useCoState<V extends CoValue, D extends DepthsIn<V> = []>(
|
||||
id: ID<V> | undefined,
|
||||
depth: D = [] as D
|
||||
): {
|
||||
current?: DeeplyLoaded<V, D>;
|
||||
current?: DeeplyLoaded<V, D> | null;
|
||||
} {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
|
||||
// Create state and a stable observable
|
||||
let state = $state.raw<DeeplyLoaded<V, D> | undefined>(undefined);
|
||||
let state = $state.raw<DeeplyLoaded<V, D> | undefined | null>(undefined);
|
||||
|
||||
// Effect to handle subscription
|
||||
$effect(() => {
|
||||
@@ -178,7 +178,9 @@ export function useCoState<V extends CoValue, D extends DepthsIn<V> = []>(
|
||||
// Get current value from our stable observable
|
||||
state = value;
|
||||
},
|
||||
undefined,
|
||||
() => {
|
||||
state = null;
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { render } from "@testing-library/svelte";
|
||||
import { Account, CoMap, co, type CoValue, type CoValueClass, type DepthsIn } from "jazz-tools";
|
||||
import { render, waitFor } from "@testing-library/svelte";
|
||||
import { Account, CoMap, co, cojsonInternals, type CoValue, type CoValueClass, type DepthsIn } from "jazz-tools";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createJazzTestAccount, createJazzTestContext } from "../testing.js";
|
||||
import { createJazzTestAccount, createJazzTestContext, setupJazzTestSync } from "../testing.js";
|
||||
import UseCoState from "./components/useCoState.svelte";
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.MAX_RETRIES = 1;
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
|
||||
});
|
||||
|
||||
function setup<T extends CoValue>(options: {
|
||||
account: Account;
|
||||
map: T;
|
||||
@@ -148,4 +157,34 @@ describe("useCoState", () => {
|
||||
expect(result.current?.value).toBe("123");
|
||||
expect(result.current?.nested?.value).toBe("456");
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null if the coValue is not found", async () => {
|
||||
class TestMap extends CoMap {
|
||||
value = co.string;
|
||||
}
|
||||
|
||||
const unreachableAccount = await createJazzTestAccount({
|
||||
});
|
||||
|
||||
const map = TestMap.create({
|
||||
value: "123",
|
||||
}, unreachableAccount);
|
||||
|
||||
unreachableAccount._raw.core.node.gracefulShutdown();
|
||||
|
||||
const account = await createJazzTestAccount({
|
||||
isCurrentActiveAccount: true,
|
||||
});
|
||||
|
||||
const result = setup({
|
||||
account,
|
||||
map,
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -301,7 +301,7 @@ export function subscribeToCoValue<V extends CoValue, Depth>(
|
||||
export function createCoValueObservable<V extends CoValue, Depth>(options?: {
|
||||
syncResolution?: boolean;
|
||||
}) {
|
||||
let currentValue: DeeplyLoaded<V, Depth> | undefined = undefined;
|
||||
let currentValue: DeeplyLoaded<V, Depth> | undefined | null = undefined;
|
||||
let subscriberCount = 0;
|
||||
|
||||
function subscribe(
|
||||
@@ -323,7 +323,10 @@ export function createCoValueObservable<V extends CoValue, Depth>(options?: {
|
||||
currentValue = value;
|
||||
listener();
|
||||
},
|
||||
onUnavailable,
|
||||
() => {
|
||||
currentValue = null;
|
||||
onUnavailable?.();
|
||||
},
|
||||
options?.syncResolution,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, onTestFinished, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, onTestFinished, vi } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
CoFeed,
|
||||
@@ -7,12 +7,15 @@ import {
|
||||
FileStream,
|
||||
Group,
|
||||
co,
|
||||
cojsonInternals,
|
||||
} from "../index.web.js";
|
||||
import {
|
||||
type DepthsIn,
|
||||
ID,
|
||||
createCoValueObservable,
|
||||
subscribeToCoValue,
|
||||
} from "../internal.js";
|
||||
import { setupJazzTestSync } from "../testing.js";
|
||||
import { setupAccount, waitFor } from "./utils.js";
|
||||
|
||||
class ChatRoom extends CoMap {
|
||||
@@ -43,6 +46,15 @@ function createMessage(me: Account | Group, text: string) {
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.MAX_RETRIES = 1;
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
|
||||
});
|
||||
|
||||
describe("subscribeToCoValue", () => {
|
||||
it("subscribes to a CoMap", async () => {
|
||||
const { me, meOnSecondPeer } = await setupAccount();
|
||||
@@ -368,4 +380,25 @@ describe("createCoValueObservable", () => {
|
||||
unsubscribe();
|
||||
expect(observable.getCurrentValue()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return null if the coValue is not found", async () => {
|
||||
const { meOnSecondPeer } = await setupAccount();
|
||||
const observable = createCoValueObservable<TestMap, DepthsIn<TestMap>>();
|
||||
|
||||
const unsubscribe = observable.subscribe(
|
||||
TestMap,
|
||||
"co_z123" as ID<TestMap>,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(observable.getCurrentValue()).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(observable.getCurrentValue()).toBeNull();
|
||||
});
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,13 +52,13 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): {
|
||||
me: ComputedRef<DeeplyLoaded<Acc, D> | undefined>;
|
||||
me: ComputedRef<DeeplyLoaded<Acc, D> | undefined | null>;
|
||||
logOut: () => void;
|
||||
};
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): {
|
||||
me: ComputedRef<Acc | DeeplyLoaded<Acc, D> | undefined>;
|
||||
me: ComputedRef<Acc | DeeplyLoaded<Acc, D> | undefined | null>;
|
||||
logOut: () => void;
|
||||
} {
|
||||
const context = useJazzContext();
|
||||
@@ -100,13 +100,15 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): {
|
||||
me: ComputedRef<DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent>;
|
||||
me: ComputedRef<
|
||||
DeeplyLoaded<Acc, D> | undefined | null | AnonymousJazzAgent
|
||||
>;
|
||||
};
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): {
|
||||
me: ComputedRef<
|
||||
Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent
|
||||
Acc | DeeplyLoaded<Acc, D> | undefined | null | AnonymousJazzAgent
|
||||
>;
|
||||
} {
|
||||
const context = useJazzContext();
|
||||
@@ -155,8 +157,8 @@ export function useCoState<V extends CoValue, D>(
|
||||
Schema: CoValueClass<V>,
|
||||
id: MaybeRef<ID<V> | undefined>,
|
||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||
): Ref<DeeplyLoaded<V, D> | undefined> {
|
||||
const state: ShallowRef<DeeplyLoaded<V, D> | undefined> =
|
||||
): Ref<DeeplyLoaded<V, D> | undefined | null> {
|
||||
const state: ShallowRef<DeeplyLoaded<V, D> | undefined | null> =
|
||||
shallowRef(undefined);
|
||||
const context = useJazzContext();
|
||||
|
||||
@@ -184,7 +186,9 @@ export function useCoState<V extends CoValue, D>(
|
||||
(value) => {
|
||||
state.value = value;
|
||||
},
|
||||
undefined,
|
||||
() => {
|
||||
state.value = null;
|
||||
},
|
||||
true,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { CoMap, co } from "jazz-tools";
|
||||
import { createJazzTestAccount } from "jazz-tools/testing";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { CoMap, co, cojsonInternals } from "jazz-tools";
|
||||
import { createJazzTestAccount, setupJazzTestSync } from "jazz-tools/testing";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { useCoState } from "../index.js";
|
||||
import { withJazzTestSetup } from "./testUtils.js";
|
||||
import { waitFor, withJazzTestSetup } from "./testUtils.js";
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupJazzTestSync();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.MAX_RETRIES = 1;
|
||||
cojsonInternals.CO_VALUE_LOADING_CONFIG.TIMEOUT = 1;
|
||||
});
|
||||
|
||||
describe("useCoState", () => {
|
||||
it("should return the correct value", async () => {
|
||||
@@ -124,4 +133,25 @@ describe("useCoState", () => {
|
||||
expect(result.value?.content).toBe("123");
|
||||
expect(result.value?.nested?.content).toBe("456");
|
||||
});
|
||||
|
||||
it("should return null if the coValue is not found", async () => {
|
||||
class TestMap extends CoMap {
|
||||
content = co.string;
|
||||
}
|
||||
|
||||
const account = await createJazzTestAccount();
|
||||
|
||||
const [result] = withJazzTestSetup(
|
||||
() => useCoState(TestMap, "co_z123", {}),
|
||||
{
|
||||
account,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.value).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.value).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -1432,9 +1432,6 @@ importers:
|
||||
'@opentelemetry/sdk-metrics':
|
||||
specifier: ^1.29.0
|
||||
version: 1.30.0(@opentelemetry/api@1.9.0)
|
||||
'@types/jest':
|
||||
specifier: ^29.5.3
|
||||
version: 29.5.14
|
||||
typescript:
|
||||
specifier: ~5.6.2
|
||||
version: 5.6.3
|
||||
|
||||
Reference in New Issue
Block a user