jazz-tools 0.18.17 → 0.18.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.svelte-kit/__package__/jazz.class.svelte.d.ts +14 -0
- package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
- package/.svelte-kit/__package__/jazz.class.svelte.js +37 -0
- package/.svelte-kit/__package__/testing.d.ts +1 -1
- package/.svelte-kit/__package__/testing.d.ts.map +1 -1
- package/.svelte-kit/__package__/testing.js +1 -1
- package/.svelte-kit/__package__/tests/TestConnectionStatus.svelte +8 -0
- package/.svelte-kit/__package__/tests/TestConnectionStatus.svelte.d.ts +27 -0
- package/.svelte-kit/__package__/tests/TestConnectionStatus.svelte.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/sync-connection-status.svelte.test.d.ts +2 -0
- package/.svelte-kit/__package__/tests/sync-connection-status.svelte.test.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/sync-connection-status.svelte.test.js +47 -0
- package/.turbo/turbo-build.log +47 -47
- package/CHANGELOG.md +16 -0
- package/dist/browser/BrowserContextManager.d.ts +4 -0
- package/dist/browser/BrowserContextManager.d.ts.map +1 -1
- package/dist/browser/createBrowserContext.d.ts +4 -0
- package/dist/browser/createBrowserContext.d.ts.map +1 -1
- package/dist/browser/index.js +36 -4
- package/dist/browser/index.js.map +1 -1
- package/dist/{chunk-OTWWOZMB.js → chunk-FHRKDKDY.js} +8 -3
- package/dist/chunk-FHRKDKDY.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +4 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react-core/hooks.d.ts +26 -0
- package/dist/react-core/hooks.d.ts.map +1 -1
- package/dist/react-core/index.js +16 -1
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/testing.d.ts +1 -1
- package/dist/react-core/testing.d.ts.map +1 -1
- package/dist/react-core/testing.js +3 -1
- package/dist/react-core/testing.js.map +1 -1
- package/dist/react-core/tests/useSyncConnectionStatus.test.d.ts +2 -0
- package/dist/react-core/tests/useSyncConnectionStatus.test.d.ts.map +1 -0
- package/dist/react-native-core/ReactNativeContextManager.d.ts +4 -0
- package/dist/react-native-core/ReactNativeContextManager.d.ts.map +1 -1
- package/dist/react-native-core/hooks.d.ts +1 -1
- package/dist/react-native-core/hooks.d.ts.map +1 -1
- package/dist/react-native-core/index.js +38 -6
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/platform.d.ts +4 -0
- package/dist/react-native-core/platform.d.ts.map +1 -1
- package/dist/svelte/jazz.class.svelte.d.ts +14 -0
- package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
- package/dist/svelte/jazz.class.svelte.js +37 -0
- package/dist/svelte/testing.d.ts +1 -1
- package/dist/svelte/testing.d.ts.map +1 -1
- package/dist/svelte/testing.js +1 -1
- package/dist/svelte/tests/TestConnectionStatus.svelte +8 -0
- package/dist/svelte/tests/TestConnectionStatus.svelte.d.ts +27 -0
- package/dist/svelte/tests/TestConnectionStatus.svelte.d.ts.map +1 -0
- package/dist/svelte/tests/sync-connection-status.svelte.test.d.ts +2 -0
- package/dist/svelte/tests/sync-connection-status.svelte.test.d.ts.map +1 -0
- package/dist/svelte/tests/sync-connection-status.svelte.test.js +47 -0
- package/dist/testing.js +34 -4
- package/dist/testing.js.map +1 -1
- package/dist/tools/implementation/ContextManager.d.ts +4 -0
- package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
- package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
- package/dist/tools/testing.d.ts +8 -0
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/types.d.ts +4 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/browser/createBrowserContext.ts +34 -4
- package/src/react/hooks.tsx +1 -0
- package/src/react/index.ts +1 -0
- package/src/react-core/hooks.ts +42 -0
- package/src/react-core/testing.tsx +1 -0
- package/src/react-core/tests/useAccountWithSelector.test.ts +98 -2
- package/src/react-core/tests/useSyncConnectionStatus.test.ts +48 -0
- package/src/react-native-core/hooks.tsx +1 -0
- package/src/react-native-core/platform.ts +32 -4
- package/src/svelte/jazz.class.svelte.ts +44 -0
- package/src/svelte/testing.ts +1 -0
- package/src/svelte/tests/TestConnectionStatus.svelte +8 -0
- package/src/svelte/tests/sync-connection-status.svelte.test.ts +61 -0
- package/src/tools/implementation/ContextManager.ts +8 -0
- package/src/tools/subscribe/SubscriptionScope.ts +5 -1
- package/src/tools/testing.ts +29 -0
- package/src/tools/tests/ContextManager.test.ts +2 -2
- package/src/tools/tests/coMap.test.ts +42 -0
- package/src/tools/tests/subscribe.test.ts +1 -4
- package/src/tools/types.ts +4 -0
- package/dist/chunk-OTWWOZMB.js.map +0 -1
@@ -56,6 +56,8 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
56
56
|
if (options.sync.when === "never") {
|
57
57
|
return {
|
58
58
|
toggleNetwork: () => {},
|
59
|
+
addConnectionListener: () => () => {},
|
60
|
+
connected: () => false,
|
59
61
|
peersToLoadFrom,
|
60
62
|
setNode: () => {},
|
61
63
|
crypto,
|
@@ -96,6 +98,14 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
96
98
|
|
97
99
|
return {
|
98
100
|
toggleNetwork,
|
101
|
+
addConnectionListener(listener: (connected: boolean) => void) {
|
102
|
+
wsPeer.subscribe(listener);
|
103
|
+
|
104
|
+
return () => {
|
105
|
+
wsPeer.unsubscribe(listener);
|
106
|
+
};
|
107
|
+
},
|
108
|
+
connected: () => wsPeer.connected,
|
99
109
|
peersToLoadFrom,
|
100
110
|
setNode,
|
101
111
|
crypto,
|
@@ -106,8 +116,15 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
|
|
106
116
|
export async function createJazzReactNativeGuestContext(
|
107
117
|
options: BaseReactNativeContextOptions,
|
108
118
|
) {
|
109
|
-
const {
|
110
|
-
|
119
|
+
const {
|
120
|
+
toggleNetwork,
|
121
|
+
peersToLoadFrom,
|
122
|
+
setNode,
|
123
|
+
crypto,
|
124
|
+
storage,
|
125
|
+
addConnectionListener,
|
126
|
+
connected,
|
127
|
+
} = await setupPeers(options);
|
111
128
|
|
112
129
|
const context = createAnonymousJazzContext({
|
113
130
|
crypto,
|
@@ -130,6 +147,8 @@ export async function createJazzReactNativeGuestContext(
|
|
130
147
|
logOut: () => {
|
131
148
|
return context.logOut();
|
132
149
|
},
|
150
|
+
addConnectionListener,
|
151
|
+
connected,
|
133
152
|
};
|
134
153
|
}
|
135
154
|
|
@@ -149,8 +168,15 @@ export async function createJazzReactNativeContext<
|
|
149
168
|
| (AccountClass<Account> & CoValueFromRaw<Account>)
|
150
169
|
| AnyAccountSchema,
|
151
170
|
>(options: ReactNativeContextOptions<S>) {
|
152
|
-
const {
|
153
|
-
|
171
|
+
const {
|
172
|
+
toggleNetwork,
|
173
|
+
peersToLoadFrom,
|
174
|
+
setNode,
|
175
|
+
crypto,
|
176
|
+
storage,
|
177
|
+
addConnectionListener,
|
178
|
+
connected,
|
179
|
+
} = await setupPeers(options);
|
154
180
|
|
155
181
|
let unsubscribeAuthUpdate = () => {};
|
156
182
|
|
@@ -201,6 +227,8 @@ export async function createJazzReactNativeContext<
|
|
201
227
|
unsubscribeAuthUpdate();
|
202
228
|
return context.logOut();
|
203
229
|
},
|
230
|
+
addConnectionListener,
|
231
|
+
connected,
|
204
232
|
};
|
205
233
|
}
|
206
234
|
|
@@ -205,3 +205,47 @@ export class AccountCoState<
|
|
205
205
|
return this.#isAuthenticated.current;
|
206
206
|
}
|
207
207
|
}
|
208
|
+
|
209
|
+
/**
|
210
|
+
* Class that provides the current connection status to the Jazz sync server.
|
211
|
+
*
|
212
|
+
* @returns `true` when connected to the server, `false` when disconnected
|
213
|
+
*
|
214
|
+
* @remarks
|
215
|
+
* On connection drop, this will return `false` only when Jazz detects the disconnection
|
216
|
+
* after 5 seconds of not receiving a ping from the server.
|
217
|
+
*/
|
218
|
+
export class SyncConnectionStatus {
|
219
|
+
#ctx = getJazzContext<InstanceOfSchema<AccountClass<Account>>>();
|
220
|
+
#subscribe: () => void;
|
221
|
+
#update = () => {};
|
222
|
+
|
223
|
+
constructor() {
|
224
|
+
this.#subscribe = createSubscriber((update) => {
|
225
|
+
this.#update = update;
|
226
|
+
});
|
227
|
+
|
228
|
+
$effect.pre(() => {
|
229
|
+
const ctx = this.#ctx.current;
|
230
|
+
|
231
|
+
return untrack(() => {
|
232
|
+
if (!ctx) {
|
233
|
+
return;
|
234
|
+
}
|
235
|
+
|
236
|
+
const unsubscribe = ctx.addConnectionListener(() => {
|
237
|
+
this.#update();
|
238
|
+
});
|
239
|
+
|
240
|
+
return () => {
|
241
|
+
unsubscribe();
|
242
|
+
};
|
243
|
+
});
|
244
|
+
});
|
245
|
+
}
|
246
|
+
|
247
|
+
get current() {
|
248
|
+
this.#subscribe();
|
249
|
+
return this.#ctx.current?.connected() ?? false;
|
250
|
+
}
|
251
|
+
}
|
package/src/svelte/testing.ts
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
// @vitest-environment happy-dom
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
3
|
+
import {
|
4
|
+
createJazzTestAccount,
|
5
|
+
setupJazzTestSync,
|
6
|
+
MockConnectionStatus,
|
7
|
+
} from "../testing";
|
8
|
+
import { render, screen, waitFor } from "./testUtils";
|
9
|
+
import TestConnectionStatus from "./TestConnectionStatus.svelte";
|
10
|
+
|
11
|
+
describe("SyncConnectionStatus", () => {
|
12
|
+
beforeEach(async () => {
|
13
|
+
await setupJazzTestSync();
|
14
|
+
await createJazzTestAccount({
|
15
|
+
isCurrentActiveAccount: true,
|
16
|
+
});
|
17
|
+
});
|
18
|
+
|
19
|
+
afterEach(() => {
|
20
|
+
vi.clearAllMocks();
|
21
|
+
});
|
22
|
+
|
23
|
+
it("should return true by default in the test environment", async () => {
|
24
|
+
const { container } = render(TestConnectionStatus, {}, {
|
25
|
+
account: await createJazzTestAccount({
|
26
|
+
isCurrentActiveAccount: true,
|
27
|
+
}),
|
28
|
+
});
|
29
|
+
|
30
|
+
await waitFor(() => {
|
31
|
+
expect(screen.getByTestId("connected").textContent).toBe("true");
|
32
|
+
});
|
33
|
+
});
|
34
|
+
|
35
|
+
it("should handle updates", async () => {
|
36
|
+
const { container } = render(TestConnectionStatus, {}, {
|
37
|
+
account: await createJazzTestAccount({
|
38
|
+
isCurrentActiveAccount: true,
|
39
|
+
}),
|
40
|
+
});
|
41
|
+
|
42
|
+
// Initially should be connected
|
43
|
+
await waitFor(() => {
|
44
|
+
expect(screen.getByTestId("connected").textContent).toBe("true");
|
45
|
+
});
|
46
|
+
|
47
|
+
// Simulate disconnection
|
48
|
+
MockConnectionStatus.setIsConnected(false);
|
49
|
+
|
50
|
+
await waitFor(() => {
|
51
|
+
expect(screen.getByTestId("connected").textContent).toBe("false");
|
52
|
+
});
|
53
|
+
|
54
|
+
// Simulate reconnection
|
55
|
+
MockConnectionStatus.setIsConnected(true);
|
56
|
+
|
57
|
+
await waitFor(() => {
|
58
|
+
expect(screen.getByTestId("connected").textContent).toBe("true");
|
59
|
+
});
|
60
|
+
});
|
61
|
+
});
|
@@ -26,6 +26,8 @@ type PlatformSpecificAuthContext<Acc extends Account> = {
|
|
26
26
|
node: LocalNode;
|
27
27
|
logOut: () => Promise<void>;
|
28
28
|
done: () => void;
|
29
|
+
addConnectionListener: (listener: (connected: boolean) => void) => () => void;
|
30
|
+
connected: () => boolean;
|
29
31
|
};
|
30
32
|
|
31
33
|
type PlatformSpecificGuestContext = {
|
@@ -33,6 +35,8 @@ type PlatformSpecificGuestContext = {
|
|
33
35
|
node: LocalNode;
|
34
36
|
logOut: () => Promise<void>;
|
35
37
|
done: () => void;
|
38
|
+
addConnectionListener: (listener: (connected: boolean) => void) => () => void;
|
39
|
+
connected: () => boolean;
|
36
40
|
};
|
37
41
|
|
38
42
|
type PlatformSpecificContext<Acc extends Account> =
|
@@ -52,6 +56,8 @@ function getAnonymousFallback() {
|
|
52
56
|
logOut: async () => {},
|
53
57
|
isAuthenticated: false,
|
54
58
|
authenticate: async () => {},
|
59
|
+
addConnectionListener: () => () => {},
|
60
|
+
connected: () => false,
|
55
61
|
register: async () => {
|
56
62
|
throw new Error("Not implemented");
|
57
63
|
},
|
@@ -134,6 +140,8 @@ export class JazzContextManager<
|
|
134
140
|
authenticate: this.authenticate,
|
135
141
|
register: this.register,
|
136
142
|
logOut: this.logOut,
|
143
|
+
addConnectionListener: context.addConnectionListener,
|
144
|
+
connected: context.connected,
|
137
145
|
};
|
138
146
|
|
139
147
|
if (authProps?.credentials) {
|
@@ -79,7 +79,11 @@ export class SubscriptionScope<D extends CoValue> {
|
|
79
79
|
// - Run the migration only once
|
80
80
|
// - Skip all the updates until the migration is done
|
81
81
|
// - Trigger handleUpdate only with the final value
|
82
|
-
if (
|
82
|
+
if (
|
83
|
+
!this.migrated &&
|
84
|
+
value !== "unavailable" &&
|
85
|
+
!value.core.verified.isStreaming()
|
86
|
+
) {
|
83
87
|
if (this.migrating) {
|
84
88
|
return;
|
85
89
|
}
|
package/src/tools/testing.ts
CHANGED
@@ -193,6 +193,23 @@ export async function createJazzTestGuest() {
|
|
193
193
|
};
|
194
194
|
}
|
195
195
|
|
196
|
+
export class MockConnectionStatus {
|
197
|
+
static connected: boolean = true;
|
198
|
+
static connectionListeners = new Set<(isConnected: boolean) => void>();
|
199
|
+
static setIsConnected(isConnected: boolean) {
|
200
|
+
MockConnectionStatus.connected = isConnected;
|
201
|
+
for (const listener of MockConnectionStatus.connectionListeners) {
|
202
|
+
listener(isConnected);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
static addConnectionListener(listener: (isConnected: boolean) => void) {
|
206
|
+
MockConnectionStatus.connectionListeners.add(listener);
|
207
|
+
return () => {
|
208
|
+
MockConnectionStatus.connectionListeners.delete(listener);
|
209
|
+
};
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
196
213
|
export type TestJazzContextManagerProps<Acc extends Account> =
|
197
214
|
JazzContextManagerBaseProps<Acc> & {
|
198
215
|
defaultProfileName?: string;
|
@@ -249,6 +266,10 @@ export class TestJazzContextManager<
|
|
249
266
|
await storage.clear();
|
250
267
|
node.gracefulShutdown();
|
251
268
|
},
|
269
|
+
addConnectionListener: (listener) => {
|
270
|
+
return MockConnectionStatus.addConnectionListener(listener);
|
271
|
+
},
|
272
|
+
connected: () => MockConnectionStatus.connected,
|
252
273
|
},
|
253
274
|
{
|
254
275
|
credentials,
|
@@ -274,6 +295,10 @@ export class TestJazzContextManager<
|
|
274
295
|
logOut: async () => {
|
275
296
|
node.gracefulShutdown();
|
276
297
|
},
|
298
|
+
addConnectionListener: (listener) => {
|
299
|
+
return MockConnectionStatus.addConnectionListener(listener);
|
300
|
+
},
|
301
|
+
connected: () => MockConnectionStatus.connected,
|
277
302
|
});
|
278
303
|
|
279
304
|
return context;
|
@@ -309,6 +334,10 @@ export class TestJazzContextManager<
|
|
309
334
|
logOut: () => {
|
310
335
|
return context.logOut();
|
311
336
|
},
|
337
|
+
addConnectionListener: (listener: (isConnected: boolean) => void) => {
|
338
|
+
return MockConnectionStatus.addConnectionListener(listener);
|
339
|
+
},
|
340
|
+
connected: () => MockConnectionStatus.connected,
|
312
341
|
};
|
313
342
|
}
|
314
343
|
}
|
@@ -73,6 +73,8 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
|
73
73
|
logOut: async () => {
|
74
74
|
await context.logOut();
|
75
75
|
},
|
76
|
+
addConnectionListener: () => () => {},
|
77
|
+
connected: () => false,
|
76
78
|
};
|
77
79
|
}
|
78
80
|
}
|
@@ -80,14 +82,12 @@ class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
|
|
80
82
|
describe("ContextManager", () => {
|
81
83
|
let manager: TestJazzContextManager<Account>;
|
82
84
|
let authSecretStorage: AuthSecretStorage;
|
83
|
-
let storage: StorageAPI;
|
84
85
|
|
85
86
|
function getCurrentValue() {
|
86
87
|
return manager.getCurrentValue() as JazzAuthContext<Account>;
|
87
88
|
}
|
88
89
|
|
89
90
|
beforeEach(async () => {
|
90
|
-
storage = await createAsyncStorage({});
|
91
91
|
KvStoreContext.getInstance().initialize(new InMemoryKVStore());
|
92
92
|
authSecretStorage = new AuthSecretStorage();
|
93
93
|
await authSecretStorage.clear();
|
@@ -2510,6 +2510,48 @@ describe("CoMap migration", () => {
|
|
2510
2510
|
expect(spy).toHaveBeenCalledTimes(1);
|
2511
2511
|
});
|
2512
2512
|
|
2513
|
+
test("should run only when the value is fully loaded", async () => {
|
2514
|
+
await setupJazzTestSync({
|
2515
|
+
asyncPeers: true,
|
2516
|
+
});
|
2517
|
+
await createJazzTestAccount({
|
2518
|
+
isCurrentActiveAccount: true,
|
2519
|
+
creationProps: { name: "Hermes Puggington" },
|
2520
|
+
});
|
2521
|
+
|
2522
|
+
const migration = vi.fn();
|
2523
|
+
const Person = co
|
2524
|
+
.map({
|
2525
|
+
name: z.string(),
|
2526
|
+
update: z.number(),
|
2527
|
+
})
|
2528
|
+
.withMigration((person) => {
|
2529
|
+
migration(person.update);
|
2530
|
+
});
|
2531
|
+
|
2532
|
+
const person = Person.create({
|
2533
|
+
name: "Bob",
|
2534
|
+
update: 1,
|
2535
|
+
});
|
2536
|
+
|
2537
|
+
// Pump the value to reach streaming
|
2538
|
+
for (let i = 0; i <= 300; i++) {
|
2539
|
+
person.$jazz.raw.assign({
|
2540
|
+
name: "1".repeat(1024),
|
2541
|
+
update: i,
|
2542
|
+
});
|
2543
|
+
}
|
2544
|
+
|
2545
|
+
// Upload and unmount, to force the streaming download
|
2546
|
+
await person.$jazz.waitForSync();
|
2547
|
+
person.$jazz.raw.core.unmount();
|
2548
|
+
|
2549
|
+
// Load the value and expect the migration to run only once
|
2550
|
+
await Person.load(person.$jazz.id);
|
2551
|
+
expect(migration).toHaveBeenCalledTimes(1);
|
2552
|
+
expect(migration).toHaveBeenCalledWith(300);
|
2553
|
+
});
|
2554
|
+
|
2513
2555
|
test("should not break recursive schemas", async () => {
|
2514
2556
|
const PersonV1 = co.map({
|
2515
2557
|
name: z.string(),
|
@@ -559,10 +559,7 @@ describe("subscribeToCoValue", () => {
|
|
559
559
|
assert(result);
|
560
560
|
|
561
561
|
expect(result[0]?.value).toBe("1");
|
562
|
-
|
563
|
-
// expect(updateFn).toHaveBeenCalledTimes(1);
|
564
|
-
// TODO: Getting an extra update here due to https://github.com/garden-co/jazz/issues/2117
|
565
|
-
expect(updateFn).toHaveBeenCalledTimes(2);
|
562
|
+
expect(updateFn).toHaveBeenCalledTimes(1);
|
566
563
|
});
|
567
564
|
|
568
565
|
it("should handle undefined values in lists with required refs", async () => {
|
package/src/tools/types.ts
CHANGED
@@ -26,6 +26,8 @@ export type JazzAuthContext<Acc extends Account> = {
|
|
26
26
|
logOut: () => Promise<void>;
|
27
27
|
done: () => void;
|
28
28
|
isAuthenticated?: boolean;
|
29
|
+
addConnectionListener: (listener: (connected: boolean) => void) => () => void;
|
30
|
+
connected: () => boolean;
|
29
31
|
};
|
30
32
|
|
31
33
|
export type JazzGuestContext = {
|
@@ -36,6 +38,8 @@ export type JazzGuestContext = {
|
|
36
38
|
logOut: () => void;
|
37
39
|
done: () => void;
|
38
40
|
isAuthenticated?: boolean;
|
41
|
+
addConnectionListener: (listener: (connected: boolean) => void) => () => void;
|
42
|
+
connected: () => boolean;
|
39
43
|
};
|
40
44
|
|
41
45
|
export type JazzContextType<Acc extends Account> =
|