jazz-tools 0.19.8 → 0.19.11

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 (154) hide show
  1. package/.turbo/turbo-build.log +56 -50
  2. package/CHANGELOG.md +30 -3
  3. package/dist/{chunk-2S3Z2CN6.js → chunk-HX5S6W5E.js} +372 -103
  4. package/dist/chunk-HX5S6W5E.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/inspector/account-switcher.d.ts +4 -0
  7. package/dist/inspector/account-switcher.d.ts.map +1 -0
  8. package/dist/inspector/chunk-C6BJPHBQ.js +4096 -0
  9. package/dist/inspector/chunk-C6BJPHBQ.js.map +1 -0
  10. package/dist/inspector/contexts/node.d.ts +19 -0
  11. package/dist/inspector/contexts/node.d.ts.map +1 -0
  12. package/dist/inspector/{custom-element-P76EIWEV.js → custom-element-GJVBPZES.js} +1011 -884
  13. package/dist/inspector/custom-element-GJVBPZES.js.map +1 -0
  14. package/dist/inspector/{viewer/new-app.d.ts → in-app.d.ts} +3 -3
  15. package/dist/inspector/in-app.d.ts.map +1 -0
  16. package/dist/inspector/index.d.ts +0 -11
  17. package/dist/inspector/index.d.ts.map +1 -1
  18. package/dist/inspector/index.js +56 -3910
  19. package/dist/inspector/index.js.map +1 -1
  20. package/dist/inspector/pages/home.d.ts +2 -0
  21. package/dist/inspector/pages/home.d.ts.map +1 -0
  22. package/dist/inspector/register-custom-element.js +1 -1
  23. package/dist/inspector/router/context.d.ts +12 -0
  24. package/dist/inspector/router/context.d.ts.map +1 -0
  25. package/dist/inspector/router/hash-router.d.ts +7 -0
  26. package/dist/inspector/router/hash-router.d.ts.map +1 -0
  27. package/dist/inspector/router/in-memory-router.d.ts +7 -0
  28. package/dist/inspector/router/in-memory-router.d.ts.map +1 -0
  29. package/dist/inspector/router/index.d.ts +5 -0
  30. package/dist/inspector/router/index.d.ts.map +1 -0
  31. package/dist/inspector/standalone.d.ts +6 -0
  32. package/dist/inspector/standalone.d.ts.map +1 -0
  33. package/dist/inspector/standalone.js +420 -0
  34. package/dist/inspector/standalone.js.map +1 -0
  35. package/dist/inspector/tests/router/hash-router.test.d.ts +2 -0
  36. package/dist/inspector/tests/router/hash-router.test.d.ts.map +1 -0
  37. package/dist/inspector/tests/router/in-memory-router.test.d.ts +2 -0
  38. package/dist/inspector/tests/router/in-memory-router.test.d.ts.map +1 -0
  39. package/dist/inspector/ui/modal.d.ts +1 -0
  40. package/dist/inspector/ui/modal.d.ts.map +1 -1
  41. package/dist/inspector/viewer/breadcrumbs.d.ts +1 -7
  42. package/dist/inspector/viewer/breadcrumbs.d.ts.map +1 -1
  43. package/dist/inspector/viewer/header.d.ts +7 -0
  44. package/dist/inspector/viewer/header.d.ts.map +1 -0
  45. package/dist/inspector/viewer/page-stack.d.ts +4 -13
  46. package/dist/inspector/viewer/page-stack.d.ts.map +1 -1
  47. package/dist/inspector/viewer/page.d.ts.map +1 -1
  48. package/dist/react/hooks.d.ts +1 -1
  49. package/dist/react/hooks.d.ts.map +1 -1
  50. package/dist/react/index.d.ts +1 -1
  51. package/dist/react/index.d.ts.map +1 -1
  52. package/dist/react/index.js +5 -1
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react-core/hooks.d.ts +59 -0
  55. package/dist/react-core/hooks.d.ts.map +1 -1
  56. package/dist/react-core/index.js +124 -36
  57. package/dist/react-core/index.js.map +1 -1
  58. package/dist/react-core/tests/testUtils.d.ts +1 -0
  59. package/dist/react-core/tests/testUtils.d.ts.map +1 -1
  60. package/dist/react-core/tests/useSuspenseAccount.test.d.ts +2 -0
  61. package/dist/react-core/tests/useSuspenseAccount.test.d.ts.map +1 -0
  62. package/dist/react-core/tests/useSuspenseCoState.test.d.ts +2 -0
  63. package/dist/react-core/tests/useSuspenseCoState.test.d.ts.map +1 -0
  64. package/dist/react-core/use.d.ts +3 -0
  65. package/dist/react-core/use.d.ts.map +1 -0
  66. package/dist/react-native/index.js +5 -1
  67. package/dist/react-native/index.js.map +1 -1
  68. package/dist/react-native-core/crypto/RNCrypto.d.ts +2 -0
  69. package/dist/react-native-core/crypto/RNCrypto.d.ts.map +1 -0
  70. package/dist/react-native-core/crypto/RNCrypto.js +3 -0
  71. package/dist/react-native-core/crypto/RNCrypto.js.map +1 -0
  72. package/dist/react-native-core/hooks.d.ts +1 -1
  73. package/dist/react-native-core/hooks.d.ts.map +1 -1
  74. package/dist/react-native-core/index.js +5 -1
  75. package/dist/react-native-core/index.js.map +1 -1
  76. package/dist/react-native-core/platform.d.ts +2 -1
  77. package/dist/react-native-core/platform.d.ts.map +1 -1
  78. package/dist/testing.js +1 -1
  79. package/dist/testing.js.map +1 -1
  80. package/dist/tools/coValues/account.d.ts +7 -1
  81. package/dist/tools/coValues/account.d.ts.map +1 -1
  82. package/dist/tools/coValues/interfaces.d.ts +1 -1
  83. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  84. package/dist/tools/implementation/ContextManager.d.ts +3 -0
  85. package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
  86. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +8 -1
  87. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  88. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  89. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +8 -22
  90. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  91. package/dist/tools/subscribe/SubscriptionCache.d.ts +51 -0
  92. package/dist/tools/subscribe/SubscriptionCache.d.ts.map +1 -0
  93. package/dist/tools/subscribe/SubscriptionScope.d.ts +17 -1
  94. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  95. package/dist/tools/subscribe/utils.d.ts +9 -1
  96. package/dist/tools/subscribe/utils.d.ts.map +1 -1
  97. package/dist/tools/testing.d.ts +2 -2
  98. package/dist/tools/testing.d.ts.map +1 -1
  99. package/dist/tools/tests/SubscriptionCache.test.d.ts +2 -0
  100. package/dist/tools/tests/SubscriptionCache.test.d.ts.map +1 -0
  101. package/package.json +18 -6
  102. package/src/inspector/account-switcher.tsx +440 -0
  103. package/src/inspector/contexts/node.tsx +129 -0
  104. package/src/inspector/custom-element.tsx +2 -2
  105. package/src/inspector/in-app.tsx +61 -0
  106. package/src/inspector/index.tsx +2 -22
  107. package/src/inspector/pages/home.tsx +77 -0
  108. package/src/inspector/router/context.ts +21 -0
  109. package/src/inspector/router/hash-router.tsx +128 -0
  110. package/src/inspector/{viewer/use-page-path.ts → router/in-memory-router.tsx} +31 -29
  111. package/src/inspector/router/index.ts +4 -0
  112. package/src/inspector/standalone.tsx +60 -0
  113. package/src/inspector/tests/router/hash-router.test.tsx +847 -0
  114. package/src/inspector/tests/router/in-memory-router.test.tsx +724 -0
  115. package/src/inspector/ui/modal.tsx +5 -2
  116. package/src/inspector/viewer/breadcrumbs.tsx +5 -11
  117. package/src/inspector/viewer/header.tsx +67 -0
  118. package/src/inspector/viewer/page-stack.tsx +18 -26
  119. package/src/inspector/viewer/page.tsx +0 -1
  120. package/src/react/hooks.tsx +2 -0
  121. package/src/react/index.ts +1 -14
  122. package/src/react-core/hooks.ts +167 -18
  123. package/src/react-core/tests/createCoValueSubscriptionContext.test.tsx +18 -8
  124. package/src/react-core/tests/testUtils.tsx +67 -5
  125. package/src/react-core/tests/useCoState.test.ts +3 -7
  126. package/src/react-core/tests/useSubscriptionSelector.test.ts +3 -7
  127. package/src/react-core/tests/useSuspenseAccount.test.tsx +343 -0
  128. package/src/react-core/tests/useSuspenseCoState.test.tsx +1182 -0
  129. package/src/react-core/use.ts +46 -0
  130. package/src/react-native-core/crypto/RNCrypto.ts +1 -0
  131. package/src/react-native-core/hooks.tsx +2 -0
  132. package/src/react-native-core/platform.ts +2 -1
  133. package/src/tools/coValues/account.ts +13 -2
  134. package/src/tools/coValues/interfaces.ts +2 -3
  135. package/src/tools/implementation/ContextManager.ts +13 -0
  136. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +8 -1
  137. package/src/tools/subscribe/CoValueCoreSubscription.ts +71 -100
  138. package/src/tools/subscribe/SubscriptionCache.ts +272 -0
  139. package/src/tools/subscribe/SubscriptionScope.ts +113 -7
  140. package/src/tools/subscribe/utils.ts +77 -0
  141. package/src/tools/testing.ts +0 -3
  142. package/src/tools/tests/CoValueCoreSubscription.test.ts +46 -12
  143. package/src/tools/tests/ContextManager.test.ts +85 -0
  144. package/src/tools/tests/SubscriptionCache.test.ts +237 -0
  145. package/src/tools/tests/account.test.ts +11 -4
  146. package/src/tools/tests/coMap.test.ts +5 -7
  147. package/src/tools/tests/schema.resolved.test.ts +3 -3
  148. package/tsup.config.ts +2 -0
  149. package/dist/chunk-2S3Z2CN6.js.map +0 -1
  150. package/dist/inspector/custom-element-P76EIWEV.js.map +0 -1
  151. package/dist/inspector/viewer/new-app.d.ts.map +0 -1
  152. package/dist/inspector/viewer/use-page-path.d.ts +0 -10
  153. package/dist/inspector/viewer/use-page-path.d.ts.map +0 -1
  154. package/src/inspector/viewer/new-app.tsx +0 -156
@@ -89,13 +89,9 @@ describe("useSubscriptionSelector", () => {
89
89
  return useSubscriptionSelector(subscription);
90
90
  });
91
91
 
92
- expect(result.current.$jazz.loadingState).toBe(CoValueLoadingState.LOADING);
93
-
94
- await waitFor(() => {
95
- expect(result.current.$jazz.loadingState).toBe(
96
- CoValueLoadingState.UNAVAILABLE,
97
- );
98
- });
92
+ expect(result.current.$jazz.loadingState).toBe(
93
+ CoValueLoadingState.UNAVAILABLE,
94
+ );
99
95
  });
100
96
 
101
97
  it("should return coAccount", async () => {
@@ -0,0 +1,343 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { cojsonInternals } from "cojson";
4
+ import { Account, Group, Loaded, co, z } from "jazz-tools";
5
+ import { assertLoaded } from "jazz-tools/testing";
6
+ import { beforeEach, describe, expect, expectTypeOf, it } from "vitest";
7
+ import React, { Suspense } from "react";
8
+ import { useSuspenseAccount, useLogOut } from "../hooks.js";
9
+ import {
10
+ createJazzTestAccount,
11
+ createJazzTestGuest,
12
+ setActiveAccount,
13
+ setupJazzTestSync,
14
+ } from "../testing.js";
15
+ import { act, render, renderHook, waitFor } from "./testUtils.js";
16
+ import { ErrorBoundary } from "react-error-boundary";
17
+
18
+ // Silence unhandled rejection errors coming from Suspense
19
+ process.on("unhandledRejection", () => {});
20
+
21
+ beforeEach(async () => {
22
+ await setupJazzTestSync({
23
+ asyncPeers: true,
24
+ });
25
+
26
+ await createJazzTestAccount({
27
+ isCurrentActiveAccount: true,
28
+ });
29
+ });
30
+
31
+ cojsonInternals.setCoValueLoadingRetryDelay(10);
32
+
33
+ describe("useSuspenseAccount", () => {
34
+ it("should return loaded account without suspending when data is available", async () => {
35
+ const AccountRoot = co.map({
36
+ projects: co.list(
37
+ co.map({
38
+ name: z.string(),
39
+ description: z.string(),
40
+ }),
41
+ ),
42
+ });
43
+
44
+ const MyAppAccount = co
45
+ .account({
46
+ profile: co.profile({
47
+ name: z.string(),
48
+ }),
49
+ root: AccountRoot,
50
+ })
51
+ .withMigration((account, creationProps) => {
52
+ if (!account.$jazz.refs.profile) {
53
+ account.$jazz.set("profile", {
54
+ name: creationProps?.name || "John Doe",
55
+ });
56
+ }
57
+ if (!account.$jazz.refs.root) {
58
+ account.$jazz.set("root", {
59
+ projects: [],
60
+ });
61
+ }
62
+ });
63
+
64
+ const account = await createJazzTestAccount({
65
+ AccountSchema: MyAppAccount,
66
+ isCurrentActiveAccount: true,
67
+ creationProps: {
68
+ name: "John Doe",
69
+ },
70
+ });
71
+
72
+ let suspenseTriggered = false;
73
+
74
+ const SuspenseFallback = () => {
75
+ suspenseTriggered = true;
76
+ return <div>Loading...</div>;
77
+ };
78
+
79
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
80
+ <Suspense fallback={<SuspenseFallback />}>{children}</Suspense>
81
+ );
82
+
83
+ const { result } = renderHook(
84
+ () =>
85
+ useSuspenseAccount(MyAppAccount, {
86
+ resolve: {
87
+ profile: true,
88
+ root: {
89
+ projects: true,
90
+ },
91
+ },
92
+ }),
93
+ {
94
+ account,
95
+ wrapper,
96
+ },
97
+ );
98
+
99
+ // Wait for any async operations to complete
100
+ await waitFor(() => {
101
+ expect(result.current).toBeDefined();
102
+ });
103
+
104
+ // Verify Suspense was not triggered since data was immediately available
105
+ expect(suspenseTriggered).toBe(false);
106
+
107
+ // Verify the hook returns loaded data
108
+ assertLoaded(result.current);
109
+ expect(result.current.profile.name).toBe("John Doe");
110
+ expect(result.current.root.projects).toEqual([]);
111
+ });
112
+
113
+ it("should have Loaded<A> return type", async () => {
114
+ const AccountRoot = co.map({
115
+ value: z.string(),
116
+ });
117
+
118
+ const MyAppAccount = co
119
+ .account({
120
+ profile: co.profile({
121
+ name: z.string(),
122
+ }),
123
+ root: AccountRoot,
124
+ })
125
+ .withMigration((account, creationProps) => {
126
+ if (!account.$jazz.refs.profile) {
127
+ account.$jazz.set("profile", {
128
+ name: creationProps?.name || "Test User",
129
+ });
130
+ }
131
+ if (!account.$jazz.refs.root) {
132
+ account.$jazz.set("root", {
133
+ value: "test",
134
+ });
135
+ }
136
+ });
137
+
138
+ const account = await createJazzTestAccount({
139
+ AccountSchema: MyAppAccount,
140
+ isCurrentActiveAccount: true,
141
+ });
142
+
143
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
144
+ <Suspense fallback={<div>Loading...</div>}>{children}</Suspense>
145
+ );
146
+
147
+ const { result } = renderHook(() => useSuspenseAccount(MyAppAccount), {
148
+ account,
149
+ wrapper,
150
+ });
151
+
152
+ await waitFor(() => {
153
+ expect(result.current).toBeDefined();
154
+ });
155
+
156
+ // Verify the return type is Loaded<typeof MyAppAccount>
157
+ expectTypeOf(result.current).toEqualTypeOf<Loaded<typeof MyAppAccount>>();
158
+ });
159
+
160
+ it("should suspend when account data is not immediately available", async () => {
161
+ const Project = co.map({
162
+ name: z.string(),
163
+ description: z.string(),
164
+ });
165
+
166
+ const AccountRoot = co.map({
167
+ projects: co.list(Project),
168
+ });
169
+
170
+ const root = AccountRoot.create(
171
+ {
172
+ projects: [
173
+ {
174
+ name: "My Project",
175
+ description: "A test project",
176
+ },
177
+ ],
178
+ },
179
+ Group.create().makePublic(),
180
+ );
181
+
182
+ const MyAppAccount = co.account({
183
+ profile: co.profile({
184
+ name: z.string(),
185
+ }),
186
+ root: AccountRoot,
187
+ });
188
+
189
+ const account = await createJazzTestAccount({
190
+ AccountSchema: MyAppAccount,
191
+ isCurrentActiveAccount: true,
192
+ creationProps: {
193
+ name: "John Doe",
194
+ },
195
+ });
196
+
197
+ account.$jazz.set("root", root);
198
+
199
+ let suspenseTriggered = false;
200
+
201
+ const SuspenseFallback = () => {
202
+ suspenseTriggered = true;
203
+ return <div>Loading...</div>;
204
+ };
205
+
206
+ const TestComponent = () => {
207
+ const account = useSuspenseAccount(MyAppAccount, {
208
+ resolve: {
209
+ root: {
210
+ projects: {
211
+ $each: true,
212
+ },
213
+ },
214
+ },
215
+ });
216
+ return <div>{account.root.projects[0]?.name || "No project"}</div>;
217
+ };
218
+
219
+ const { container } = await act(async () => {
220
+ return render(
221
+ <Suspense fallback={<SuspenseFallback />}>
222
+ <TestComponent />
223
+ </Suspense>,
224
+ {
225
+ account,
226
+ },
227
+ );
228
+ });
229
+
230
+ expect(suspenseTriggered).toBe(true);
231
+
232
+ // Wait for data to load - the subscription should update and resolve
233
+ await waitFor(() => {
234
+ expect(container.textContent).toContain("My Project");
235
+ expect(container.textContent).not.toContain("Loading...");
236
+ });
237
+ });
238
+
239
+ it("should throw error for anonymous agent", async () => {
240
+ const MyAppAccount = co.account({
241
+ profile: co.profile({
242
+ name: z.string(),
243
+ }),
244
+ root: co.map({
245
+ value: z.string(),
246
+ }),
247
+ });
248
+
249
+ const guestAccount = await createJazzTestGuest();
250
+
251
+ const TestComponent = () => {
252
+ useSuspenseAccount(MyAppAccount);
253
+ return <div>Should not render</div>;
254
+ };
255
+
256
+ const { container } = await act(async () => {
257
+ return render(
258
+ <ErrorBoundary fallback={<div>Error!</div>}>
259
+ <Suspense fallback={<div>Loading...</div>}>
260
+ <TestComponent />
261
+ </Suspense>
262
+ </ErrorBoundary>,
263
+ {
264
+ account: guestAccount,
265
+ },
266
+ );
267
+ });
268
+
269
+ // Verify error is displayed in error boundary
270
+ await waitFor(
271
+ () => {
272
+ expect(container.textContent).toContain("Error!");
273
+ },
274
+ { timeout: 1000 },
275
+ );
276
+ });
277
+
278
+ it("should handle account logout", async () => {
279
+ const MyAppAccount = co
280
+ .account({
281
+ profile: co.profile({
282
+ name: z.string(),
283
+ }),
284
+ root: co.map({
285
+ value: z.string(),
286
+ }),
287
+ })
288
+ .withMigration((account, creationProps) => {
289
+ if (!account.$jazz.refs.profile) {
290
+ account.$jazz.set("profile", {
291
+ name: creationProps?.name || "John Doe",
292
+ });
293
+ }
294
+ if (!account.$jazz.refs.root) {
295
+ account.$jazz.set("root", {
296
+ value: "test",
297
+ });
298
+ }
299
+ });
300
+
301
+ const account = await createJazzTestAccount({
302
+ AccountSchema: MyAppAccount,
303
+ isCurrentActiveAccount: true,
304
+ creationProps: {
305
+ name: "John Doe",
306
+ },
307
+ });
308
+
309
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
310
+ <Suspense fallback={<div>Loading...</div>}>
311
+ <ErrorBoundary fallback={<div>Error!</div>}>{children}</ErrorBoundary>
312
+ </Suspense>
313
+ );
314
+
315
+ const { result } = renderHook(
316
+ () => {
317
+ const account = useSuspenseAccount(MyAppAccount);
318
+ const logOut = useLogOut();
319
+ return { account, logOut };
320
+ },
321
+ {
322
+ account,
323
+ wrapper,
324
+ },
325
+ );
326
+
327
+ // Wait for account to load
328
+ await waitFor(() => {
329
+ expect(result.current.account).toBeDefined();
330
+ });
331
+
332
+ // Verify initial account data
333
+ assertLoaded(result.current.account);
334
+ const initialAccountId = result.current.account.$jazz.id;
335
+
336
+ // Logout should cause an error since useSuspenseAccount requires authentication
337
+ await act(async () => {
338
+ result.current.logOut();
339
+ });
340
+
341
+ expect(result.current.account.$jazz.id).not.toBe(initialAccountId);
342
+ });
343
+ });