jazz-tools 0.18.23 → 0.18.24

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.
Files changed (47) hide show
  1. package/.turbo/turbo-build.log +58 -58
  2. package/CHANGELOG.md +11 -0
  3. package/dist/{chunk-D5L6ES2M.js → chunk-W7JT3QUN.js} +1 -1
  4. package/dist/{chunk-D5L6ES2M.js.map → chunk-W7JT3QUN.js.map} +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/react/hooks.d.ts +1 -1
  7. package/dist/react/hooks.d.ts.map +1 -1
  8. package/dist/react/index.d.ts +2 -1
  9. package/dist/react/index.d.ts.map +1 -1
  10. package/dist/react/index.js +13 -1
  11. package/dist/react/index.js.map +1 -1
  12. package/dist/react-core/hooks.d.ts +13 -0
  13. package/dist/react-core/hooks.d.ts.map +1 -1
  14. package/dist/react-core/index.d.ts +2 -0
  15. package/dist/react-core/index.d.ts.map +1 -1
  16. package/dist/react-core/index.js +104 -0
  17. package/dist/react-core/index.js.map +1 -1
  18. package/dist/react-core/subscription-provider.d.ts +27 -0
  19. package/dist/react-core/subscription-provider.d.ts.map +1 -0
  20. package/dist/react-core/tests/subscription.bench.d.ts +2 -0
  21. package/dist/react-core/tests/subscription.bench.d.ts.map +1 -0
  22. package/dist/react-core/tests/useSubscriptionSelector.test.d.ts +2 -0
  23. package/dist/react-core/tests/useSubscriptionSelector.test.d.ts.map +1 -0
  24. package/dist/react-core/types.d.ts +10 -0
  25. package/dist/react-core/types.d.ts.map +1 -0
  26. package/dist/react-native-core/hooks.d.ts +1 -1
  27. package/dist/react-native-core/hooks.d.ts.map +1 -1
  28. package/dist/react-native-core/index.d.ts +1 -0
  29. package/dist/react-native-core/index.d.ts.map +1 -1
  30. package/dist/react-native-core/index.js +13 -1
  31. package/dist/react-native-core/index.js.map +1 -1
  32. package/dist/testing.js +1 -1
  33. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +3 -8
  34. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  35. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  36. package/package.json +5 -4
  37. package/src/react/hooks.tsx +3 -0
  38. package/src/react/index.ts +9 -0
  39. package/src/react-core/hooks.ts +37 -5
  40. package/src/react-core/index.ts +2 -0
  41. package/src/react-core/subscription-provider.tsx +144 -0
  42. package/src/react-core/tests/subscription.bench.tsx +319 -0
  43. package/src/react-core/tests/useSubscriptionSelector.test.ts +250 -0
  44. package/src/react-core/types.ts +19 -0
  45. package/src/react-native-core/hooks.tsx +3 -0
  46. package/src/react-native-core/index.ts +6 -0
  47. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +5 -5
@@ -0,0 +1,250 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { cojsonInternals } from "cojson";
4
+ import { co, z } from "jazz-tools";
5
+ import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
6
+ import {
7
+ useCoValueSubscription,
8
+ useAccountSubscription,
9
+ useSubscriptionSelector,
10
+ } from "../index.js";
11
+ import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
12
+ import { act, renderHook, waitFor } from "./testUtils.js";
13
+ import { useRef } from "react";
14
+
15
+ beforeEach(async () => {
16
+ await setupJazzTestSync();
17
+
18
+ await createJazzTestAccount({
19
+ isCurrentActiveAccount: true,
20
+ });
21
+ });
22
+
23
+ cojsonInternals.setCoValueLoadingRetryDelay(300);
24
+
25
+ const useRenderCount = <T>(hook: () => T) => {
26
+ const renderCountRef = useRef(0);
27
+ const result = hook();
28
+ renderCountRef.current = renderCountRef.current + 1;
29
+ return {
30
+ renderCount: renderCountRef.current,
31
+ result,
32
+ };
33
+ };
34
+
35
+ describe("useSubscriptionSelector", () => {
36
+ it("should return coValue", () => {
37
+ const TestMap = co.map({
38
+ value: z.string(),
39
+ });
40
+
41
+ const map = TestMap.create({
42
+ value: "123",
43
+ });
44
+
45
+ const { result } = renderHook(() => {
46
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
47
+ return useSubscriptionSelector(subscription);
48
+ });
49
+
50
+ expect(result.current?.value).toBe("123");
51
+ });
52
+
53
+ it("should resolve nested coValues", () => {
54
+ const TestNestedMap = co.map({
55
+ content: z.string(),
56
+ });
57
+
58
+ const TestMap = co.map({
59
+ content: z.string(),
60
+ nested: TestNestedMap,
61
+ });
62
+
63
+ const map = TestMap.create({
64
+ content: "123",
65
+ nested: {
66
+ content: "456",
67
+ },
68
+ });
69
+
70
+ const { result } = renderHook(() => {
71
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
72
+ return useSubscriptionSelector(subscription);
73
+ });
74
+
75
+ expect(result.current?.nested?.content).toBe("456");
76
+ });
77
+
78
+ it("should return null on invalid coValue id", async () => {
79
+ const TestMap = co.map({
80
+ value: z.string(),
81
+ });
82
+
83
+ const { result } = renderHook(() => {
84
+ const subscription = useCoValueSubscription(TestMap, "123");
85
+ return useSubscriptionSelector(subscription);
86
+ });
87
+
88
+ expect(result.current).toBeUndefined();
89
+
90
+ await waitFor(() => {
91
+ expect(result.current).toBeNull();
92
+ });
93
+ });
94
+
95
+ it("should return coAccount", async () => {
96
+ const account = await createJazzTestAccount();
97
+
98
+ const { result } = renderHook(
99
+ () => {
100
+ const subscription = useAccountSubscription(co.account());
101
+ return useSubscriptionSelector(subscription);
102
+ },
103
+ {
104
+ account,
105
+ },
106
+ );
107
+
108
+ expect(result.current?.$jazz.id).toBe(account.$jazz.id);
109
+ });
110
+
111
+ it("should return value from coValue with selector", () => {
112
+ const TestMap = co.map({
113
+ value: z.string(),
114
+ });
115
+
116
+ const map = TestMap.create({
117
+ value: "123",
118
+ });
119
+
120
+ const { result } = renderHook(() => {
121
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
122
+ return useSubscriptionSelector(subscription, {
123
+ select: (v) => v?.value,
124
+ });
125
+ });
126
+
127
+ expect(result.current).toBe("123");
128
+ });
129
+
130
+ it("should return value from coAccount with selector", async () => {
131
+ const account = await createJazzTestAccount({
132
+ creationProps: { name: "test" },
133
+ });
134
+
135
+ const { result } = renderHook(
136
+ () => {
137
+ const subscription = useAccountSubscription(co.account());
138
+ return useSubscriptionSelector(subscription, {
139
+ select: (v) => v?.profile?.name,
140
+ });
141
+ },
142
+ {
143
+ account,
144
+ },
145
+ );
146
+
147
+ expect(result.current).toBe("test");
148
+ });
149
+
150
+ it("should update value from coValue with selected value changes", () => {
151
+ const TestMap = co.map({
152
+ value: z.string(),
153
+ });
154
+
155
+ const map = TestMap.create({
156
+ value: "123",
157
+ });
158
+
159
+ const { result } = renderHook(() => {
160
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
161
+ return useSubscriptionSelector(subscription, {
162
+ select: (v) => v?.value,
163
+ });
164
+ });
165
+
166
+ expect(result.current).toBe("123");
167
+
168
+ act(() => {
169
+ map.$jazz.set("value", "456");
170
+ });
171
+
172
+ expect(result.current).toBe("456");
173
+ });
174
+
175
+ it("should not re-render when a non-selected field is updated and not selected", () => {
176
+ const TestMap = co.map({
177
+ value: z.string(),
178
+ other: z.string(),
179
+ });
180
+
181
+ const map = TestMap.create({
182
+ value: "1",
183
+ other: "1",
184
+ });
185
+
186
+ const { result } = renderHook(() =>
187
+ useRenderCount(() => {
188
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
189
+ return useSubscriptionSelector(subscription, {
190
+ select: (v) => v?.value,
191
+ });
192
+ }),
193
+ );
194
+
195
+ expect(result.current.result).toBe("1");
196
+ expect(result.current.renderCount).toBe(1);
197
+
198
+ act(() => {
199
+ map.$jazz.set("other", "2");
200
+ });
201
+
202
+ expect(result.current.result).toBe("1");
203
+ expect(result.current.renderCount).toBe(1);
204
+ });
205
+
206
+ it("should only re-render or load new value when equalityFn returns true", () => {
207
+ const TestMap = co.map({
208
+ value: z.number(),
209
+ other: z.number(),
210
+ });
211
+
212
+ const map = TestMap.create({
213
+ value: 1,
214
+ other: 2,
215
+ });
216
+
217
+ const { result } = renderHook(() =>
218
+ useRenderCount(() => {
219
+ const subscription = useCoValueSubscription(TestMap, map.$jazz.id);
220
+ return useSubscriptionSelector(subscription, {
221
+ select: (v) =>
222
+ v
223
+ ? [Math.floor(v.value / 5), Math.floor(v.other / 5)].toSorted()
224
+ : [],
225
+ equalityFn: (a, b) => a.every((v, i) => v === b[i]),
226
+ });
227
+ }),
228
+ );
229
+
230
+ expect(result.current.result).toEqual([0, 0]);
231
+ expect(result.current.renderCount).toBe(1);
232
+
233
+ act(() => {
234
+ map.$jazz.applyDiff({
235
+ value: 3,
236
+ other: 4,
237
+ });
238
+ });
239
+
240
+ expect(result.current.result).toEqual([0, 0]);
241
+ expect(result.current.renderCount).toBe(1);
242
+
243
+ act(() => {
244
+ map.$jazz.set("value", 5);
245
+ });
246
+
247
+ expect(result.current.result).toEqual([0, 1]);
248
+ expect(result.current.renderCount).toBe(2);
249
+ });
250
+ });
@@ -0,0 +1,19 @@
1
+ import {
2
+ CoValueClassOrSchema,
3
+ ResolveQuery,
4
+ SubscriptionScope,
5
+ } from "jazz-tools";
6
+
7
+ declare const subscriptionTag: unique symbol;
8
+
9
+ export type CoValueSubscription<
10
+ S extends CoValueClassOrSchema,
11
+ R extends ResolveQuery<S>,
12
+ > =
13
+ | (SubscriptionScope<any> & {
14
+ [subscriptionTag]: {
15
+ schema: S;
16
+ resolve: R;
17
+ };
18
+ })
19
+ | null;
@@ -16,6 +16,9 @@ export {
16
16
  useCoStateWithSelector,
17
17
  useAccountWithSelector,
18
18
  useSyncConnectionStatus,
19
+ useCoValueSubscription,
20
+ useAccountSubscription,
21
+ useSubscriptionSelector,
19
22
  } from "jazz-tools/react-core";
20
23
 
21
24
  export function useAcceptInviteNative<S extends CoValueClassOrSchema>({
@@ -4,6 +4,12 @@ export * from "./provider.js";
4
4
  export * from "./storage/kv-store-context.js";
5
5
  export * from "./media/image.js";
6
6
 
7
+ export {
8
+ createCoValueSubscriptionContext,
9
+ createAccountSubscriptionContext,
10
+ type CoValueSubscription,
11
+ } from "jazz-tools/react-core";
12
+
7
13
  export { SQLiteDatabaseDriverAsync } from "cojson";
8
14
  export { parseInviteLink } from "jazz-tools";
9
15
  export { createInviteLink, setupKvStore } from "./platform.js";
@@ -1,10 +1,10 @@
1
- import { CryptoProvider } from "cojson";
2
1
  import {
3
2
  Account,
4
3
  AccountCreationProps,
5
4
  BranchDefinition,
6
5
  Group,
7
6
  RefsToResolveStrict,
7
+ Simplify,
8
8
  } from "../../../internal.js";
9
9
  import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
10
10
  import { InstanceOrPrimitiveOfSchema } from "../typeConverters/InstanceOrPrimitiveOfSchema.js";
@@ -48,10 +48,9 @@ export interface AccountSchema<
48
48
  > {
49
49
  builtin: "Account";
50
50
 
51
- create: (options: {
52
- creationProps?: { name: string };
53
- crypto?: CryptoProvider;
54
- }) => Promise<AccountInstance<Shape>>;
51
+ create: (
52
+ options: Simplify<Parameters<(typeof Account)["create"]>[0]>,
53
+ ) => Promise<AccountInstance<Shape>>;
55
54
 
56
55
  load: <R extends ResolveQuery<AccountSchema<Shape>>>(
57
56
  id: string,
@@ -61,6 +60,7 @@ export interface AccountSchema<
61
60
  },
62
61
  ) => Promise<Loaded<AccountSchema<Shape>, R> | null>;
63
62
 
63
+ /** @internal */
64
64
  createAs: (
65
65
  as: Account,
66
66
  options: {