jazz-tools 0.19.20 → 0.19.21

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 (79) hide show
  1. package/.svelte-kit/__package__/server.d.ts.map +1 -1
  2. package/.svelte-kit/__package__/server.js +9 -7
  3. package/.turbo/turbo-build.log +52 -52
  4. package/dist/better-auth/auth/server.d.ts.map +1 -1
  5. package/dist/better-auth/auth/server.js +4 -4
  6. package/dist/better-auth/auth/server.js.map +1 -1
  7. package/dist/better-auth/database-adapter/index.js.map +1 -1
  8. package/dist/better-auth/database-adapter/repository/generic.d.ts +3 -3
  9. package/dist/better-auth/database-adapter/repository/session.d.ts +2 -2
  10. package/dist/better-auth/database-adapter/schema.d.ts +3 -3
  11. package/dist/better-auth/database-adapter/schema.d.ts.map +1 -1
  12. package/dist/{chunk-MI24YFCY.js → chunk-QCTQH5RS.js} +1 -1
  13. package/dist/chunk-QCTQH5RS.js.map +1 -0
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/react/hooks.d.ts +1 -2
  17. package/dist/react/hooks.d.ts.map +1 -1
  18. package/dist/react/index.js +7 -2
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/react-core/hooks.d.ts +92 -1
  21. package/dist/react-core/hooks.d.ts.map +1 -1
  22. package/dist/react-core/index.js +126 -57
  23. package/dist/react-core/index.js.map +1 -1
  24. package/dist/react-core/tests/useCoStates.test.d.ts +2 -0
  25. package/dist/react-core/tests/useCoStates.test.d.ts.map +1 -0
  26. package/dist/react-native/index.js +4 -0
  27. package/dist/react-native/index.js.map +1 -1
  28. package/dist/react-native-core/hooks.d.ts +1 -1
  29. package/dist/react-native-core/hooks.d.ts.map +1 -1
  30. package/dist/react-native-core/index.js +4 -0
  31. package/dist/react-native-core/index.js.map +1 -1
  32. package/dist/svelte/auth/ClerkAuth.svelte.d.ts +38 -0
  33. package/dist/svelte/auth/ClerkAuth.svelte.d.ts.map +1 -0
  34. package/dist/svelte/auth/ClerkAuth.svelte.js +47 -0
  35. package/dist/svelte/auth/JazzSvelteProviderWithClerk.svelte +156 -0
  36. package/dist/svelte/auth/JazzSvelteProviderWithClerk.svelte.d.ts +67 -0
  37. package/dist/svelte/auth/JazzSvelteProviderWithClerk.svelte.d.ts.map +1 -0
  38. package/dist/svelte/auth/RegisterClerkAuth.svelte +27 -0
  39. package/dist/svelte/auth/RegisterClerkAuth.svelte.d.ts +17 -0
  40. package/dist/svelte/auth/RegisterClerkAuth.svelte.d.ts.map +1 -0
  41. package/dist/svelte/auth/index.d.ts +2 -0
  42. package/dist/svelte/auth/index.d.ts.map +1 -1
  43. package/dist/svelte/auth/index.js +2 -0
  44. package/dist/svelte/tests/ClerkAuth.svelte.test.d.ts +2 -0
  45. package/dist/svelte/tests/ClerkAuth.svelte.test.d.ts.map +1 -0
  46. package/dist/svelte/tests/ClerkAuth.svelte.test.js +202 -0
  47. package/dist/svelte/tests/TestClerkAuthWrapper.svelte +16 -0
  48. package/dist/svelte/tests/TestClerkAuthWrapper.svelte.d.ts +8 -0
  49. package/dist/svelte/tests/TestClerkAuthWrapper.svelte.d.ts.map +1 -0
  50. package/dist/svelte/tests/testUtils.d.ts +1 -0
  51. package/dist/svelte/tests/testUtils.d.ts.map +1 -1
  52. package/dist/svelte/tests/testUtils.js +3 -1
  53. package/dist/testing.js +1 -1
  54. package/dist/tools/auth/clerk/index.d.ts +1 -1
  55. package/dist/tools/auth/clerk/types.d.ts +1 -1
  56. package/dist/tools/auth/clerk/types.d.ts.map +1 -1
  57. package/dist/tools/subscribe/types.d.ts +1 -1
  58. package/dist/tools/subscribe/types.d.ts.map +1 -1
  59. package/package.json +4 -4
  60. package/src/better-auth/auth/server.ts +9 -7
  61. package/src/better-auth/database-adapter/repository/generic.ts +3 -3
  62. package/src/better-auth/database-adapter/repository/session.ts +2 -2
  63. package/src/better-auth/database-adapter/schema.ts +5 -5
  64. package/src/react/hooks.tsx +4 -2
  65. package/src/react-core/hooks.ts +321 -76
  66. package/src/react-core/tests/useCoState.selector.test.ts +309 -22
  67. package/src/react-core/tests/useCoStates.test.tsx +414 -0
  68. package/src/react-native-core/hooks.tsx +2 -0
  69. package/src/svelte/auth/ClerkAuth.svelte.ts +67 -0
  70. package/src/svelte/auth/JazzSvelteProviderWithClerk.svelte +156 -0
  71. package/src/svelte/auth/RegisterClerkAuth.svelte +27 -0
  72. package/src/svelte/auth/index.ts +2 -0
  73. package/src/svelte/tests/ClerkAuth.svelte.test.ts +305 -0
  74. package/src/svelte/tests/TestClerkAuthWrapper.svelte +16 -0
  75. package/src/svelte/tests/testUtils.ts +4 -1
  76. package/src/tools/auth/clerk/types.ts +1 -1
  77. package/src/tools/subscribe/types.ts +1 -1
  78. package/src/tools/tests/inbox.test.ts +7 -7
  79. package/dist/chunk-MI24YFCY.js.map +0 -1
@@ -0,0 +1,305 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import {
4
+ Account,
5
+ InMemoryKVStore,
6
+ JazzClerkAuth,
7
+ KvStoreContext,
8
+ } from "jazz-tools";
9
+ import type { MinimalClerkClient } from "jazz-tools";
10
+ import { render as renderSvelte, waitFor } from "@testing-library/svelte";
11
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
+ import {
13
+ createJazzTestAccount,
14
+ createJazzTestGuest,
15
+ setupJazzTestSync,
16
+ } from "../testing";
17
+ import { render, screen } from "./testUtils";
18
+ import TestClerkAuthWrapper from "./TestClerkAuthWrapper.svelte";
19
+ import JazzSvelteProviderWithClerk from "../auth/JazzSvelteProviderWithClerk.svelte";
20
+
21
+ KvStoreContext.getInstance().initialize(new InMemoryKVStore());
22
+
23
+ function createMockClerkClient(
24
+ user: MinimalClerkClient["user"] = null,
25
+ ): MinimalClerkClient {
26
+ return {
27
+ user,
28
+ signOut: vi.fn(),
29
+ addListener: vi.fn(() => () => {}),
30
+ };
31
+ }
32
+
33
+ describe("useClerkAuth", () => {
34
+ let account: Account;
35
+
36
+ beforeEach(async () => {
37
+ await setupJazzTestSync();
38
+ account = await createJazzTestAccount({
39
+ isCurrentActiveAccount: true,
40
+ });
41
+ });
42
+
43
+ it("should return anonymous state when not authenticated", async () => {
44
+ const mockClerk = createMockClerkClient();
45
+
46
+ render(
47
+ TestClerkAuthWrapper,
48
+ { clerk: mockClerk },
49
+ { account, isAuthenticated: false },
50
+ );
51
+
52
+ expect(screen.getByTestId("auth-state").textContent).toBe("anonymous");
53
+ });
54
+
55
+ it("should return signedIn state when authenticated", async () => {
56
+ const mockClerk = createMockClerkClient({
57
+ id: "user_123",
58
+ fullName: "Test User",
59
+ username: "testuser",
60
+ firstName: "Test",
61
+ lastName: "User",
62
+ primaryEmailAddress: { emailAddress: "test@example.com" },
63
+ unsafeMetadata: {
64
+ jazzAccountID: "test123",
65
+ jazzAccountSecret: "secret123",
66
+ },
67
+ update: vi.fn(),
68
+ });
69
+
70
+ render(
71
+ TestClerkAuthWrapper,
72
+ { clerk: mockClerk },
73
+ { account, isAuthenticated: true },
74
+ );
75
+
76
+ expect(screen.getByTestId("auth-state").textContent).toBe("signedIn");
77
+ });
78
+
79
+ it("should register the clerk listener", async () => {
80
+ const mockClerk = createMockClerkClient();
81
+
82
+ render(
83
+ TestClerkAuthWrapper,
84
+ { clerk: mockClerk },
85
+ { account, isAuthenticated: false },
86
+ );
87
+
88
+ expect(mockClerk.addListener).toHaveBeenCalled();
89
+ });
90
+
91
+ it("should cleanup listener on unmount", async () => {
92
+ const mockUnsubscribe = vi.fn();
93
+ const mockClerk = createMockClerkClient();
94
+ mockClerk.addListener = vi.fn(() => mockUnsubscribe);
95
+
96
+ const { unmount } = render(
97
+ TestClerkAuthWrapper,
98
+ { clerk: mockClerk },
99
+ { account, isAuthenticated: false },
100
+ );
101
+
102
+ expect(mockClerk.addListener).toHaveBeenCalled();
103
+ unmount();
104
+ expect(mockUnsubscribe).toHaveBeenCalled();
105
+ });
106
+
107
+ it("should throw error in guest mode", async () => {
108
+ const guest = await createJazzTestGuest();
109
+ const mockClerk = createMockClerkClient();
110
+
111
+ expect(() => {
112
+ render(TestClerkAuthWrapper, { clerk: mockClerk }, { account: guest });
113
+ }).toThrow("Clerk auth is not supported in guest mode");
114
+ });
115
+
116
+ it("should call listener with clerk client events", async () => {
117
+ const mockClerk = createMockClerkClient();
118
+ let listenerCallback: ((data: unknown) => void) | undefined;
119
+ mockClerk.addListener = vi.fn((callback) => {
120
+ listenerCallback = callback;
121
+ return () => {};
122
+ });
123
+
124
+ render(
125
+ TestClerkAuthWrapper,
126
+ { clerk: mockClerk },
127
+ { account, isAuthenticated: false },
128
+ );
129
+
130
+ expect(mockClerk.addListener).toHaveBeenCalled();
131
+ expect(listenerCallback).toBeDefined();
132
+ });
133
+ });
134
+
135
+ describe("JazzClerkAuth.initializeAuth", () => {
136
+ afterEach(() => {
137
+ vi.restoreAllMocks();
138
+ });
139
+
140
+ it("should handle initialization errors gracefully", async () => {
141
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
142
+ const initializeAuthSpy = vi
143
+ .spyOn(JazzClerkAuth, "initializeAuth")
144
+ .mockRejectedValue(new Error("Test error"));
145
+
146
+ const mockClerk = createMockClerkClient();
147
+
148
+ // The error should be thrown by the mock
149
+ await expect(JazzClerkAuth.initializeAuth(mockClerk)).rejects.toThrow(
150
+ "Test error",
151
+ );
152
+
153
+ // Restore using vitest's cleanup
154
+ initializeAuthSpy.mockRestore();
155
+ consoleSpy.mockRestore();
156
+ });
157
+ });
158
+
159
+ describe("JazzSvelteProviderWithClerk", () => {
160
+ afterEach(() => {
161
+ vi.restoreAllMocks();
162
+ });
163
+
164
+ it("should render children after successful initialization", async () => {
165
+ const mockClerk = createMockClerkClient();
166
+ vi.spyOn(JazzClerkAuth, "initializeAuth").mockResolvedValue(undefined);
167
+
168
+ const { container } = renderSvelte(JazzSvelteProviderWithClerk, {
169
+ props: {
170
+ clerk: mockClerk,
171
+ sync: { peer: "wss://test.example.com" },
172
+ },
173
+ });
174
+
175
+ await waitFor(() => {
176
+ // After initialization, the provider should render (even without children)
177
+ // The error div should not be present
178
+ expect(
179
+ container.querySelector('[data-testid="jazz-clerk-auth-error"]'),
180
+ ).toBeNull();
181
+ });
182
+ });
183
+
184
+ it("should show default error message when initialization fails", async () => {
185
+ const mockClerk = createMockClerkClient();
186
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
187
+ vi.spyOn(JazzClerkAuth, "initializeAuth").mockRejectedValue(
188
+ new Error("Init failed"),
189
+ );
190
+
191
+ const { container } = renderSvelte(JazzSvelteProviderWithClerk, {
192
+ props: {
193
+ clerk: mockClerk,
194
+ sync: { peer: "wss://test.example.com" },
195
+ },
196
+ });
197
+
198
+ await waitFor(() => {
199
+ const errorDiv = container.querySelector(
200
+ '[data-testid="jazz-clerk-auth-error"]',
201
+ );
202
+ expect(errorDiv).not.toBeNull();
203
+ expect(errorDiv?.textContent).toContain(
204
+ "Authentication initialization failed",
205
+ );
206
+ });
207
+
208
+ consoleSpy.mockRestore();
209
+ });
210
+
211
+ it("should call onAuthError callback when initialization fails", async () => {
212
+ const mockClerk = createMockClerkClient();
213
+ const onAuthError = vi.fn();
214
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
215
+ vi.spyOn(JazzClerkAuth, "initializeAuth").mockRejectedValue(
216
+ new Error("Init failed"),
217
+ );
218
+
219
+ renderSvelte(JazzSvelteProviderWithClerk, {
220
+ props: {
221
+ clerk: mockClerk,
222
+ sync: { peer: "wss://test.example.com" },
223
+ onAuthError,
224
+ },
225
+ });
226
+
227
+ await waitFor(() => {
228
+ expect(onAuthError).toHaveBeenCalledWith(expect.any(Error));
229
+ expect(onAuthError).toHaveBeenCalledWith(
230
+ expect.objectContaining({ message: "Init failed" }),
231
+ );
232
+ });
233
+
234
+ consoleSpy.mockRestore();
235
+ });
236
+
237
+ it("should not update state after unmount (cancellation)", async () => {
238
+ const mockClerk = createMockClerkClient();
239
+ let resolveInit: () => void;
240
+ const initPromise = new Promise<void>((resolve) => {
241
+ resolveInit = resolve;
242
+ });
243
+ vi.spyOn(JazzClerkAuth, "initializeAuth").mockReturnValue(initPromise);
244
+
245
+ const { unmount } = renderSvelte(JazzSvelteProviderWithClerk, {
246
+ props: {
247
+ clerk: mockClerk,
248
+ sync: { peer: "wss://test.example.com" },
249
+ },
250
+ });
251
+
252
+ // Unmount before initialization completes
253
+ unmount();
254
+
255
+ // Resolve the promise after unmount - should not cause errors
256
+ resolveInit!();
257
+
258
+ // Give time for any potential state updates
259
+ await new Promise((resolve) => setTimeout(resolve, 10));
260
+
261
+ // If we get here without errors, the cancellation worked
262
+ expect(true).toBe(true);
263
+ });
264
+ });
265
+
266
+ describe("useClerkAuth reactive state transitions", () => {
267
+ let account: Account;
268
+
269
+ beforeEach(async () => {
270
+ await setupJazzTestSync();
271
+ account = await createJazzTestAccount({
272
+ isCurrentActiveAccount: true,
273
+ });
274
+ });
275
+
276
+ afterEach(() => {
277
+ vi.restoreAllMocks();
278
+ });
279
+
280
+ it("should update state reactively when listener callback fires", async () => {
281
+ const mockClerk = createMockClerkClient();
282
+ let listenerCallback: ((data: unknown) => void) | undefined;
283
+ mockClerk.addListener = vi.fn((callback) => {
284
+ listenerCallback = callback;
285
+ return () => {};
286
+ });
287
+
288
+ const { container } = render(
289
+ TestClerkAuthWrapper,
290
+ { clerk: mockClerk },
291
+ { account, isAuthenticated: false },
292
+ );
293
+
294
+ // Initial state should be anonymous
295
+ expect(screen.getByTestId("auth-state").textContent).toBe("anonymous");
296
+
297
+ // Verify listener was registered
298
+ expect(listenerCallback).toBeDefined();
299
+
300
+ // Note: Full state transition testing would require more complex setup
301
+ // involving the actual JazzClerkAuth.onClerkUserChange flow.
302
+ // This test verifies the listener registration which is the foundation
303
+ // for reactive updates.
304
+ });
305
+ });
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import type { MinimalClerkClient } from "jazz-tools";
3
+ import { useClerkAuth } from "../auth/ClerkAuth.svelte.js";
4
+
5
+ type Props = {
6
+ clerk: MinimalClerkClient;
7
+ };
8
+
9
+ let { clerk }: Props = $props();
10
+
11
+ const auth = useClerkAuth(clerk);
12
+ </script>
13
+
14
+ <div data-testid="clerk-auth-wrapper">
15
+ <div data-testid="auth-state">{auth.state}</div>
16
+ </div>
@@ -6,6 +6,7 @@ import { JAZZ_AUTH_CTX, JAZZ_CTX } from "../jazz.svelte";
6
6
 
7
7
  type JazzExtendedOptions = {
8
8
  account: Account | { guest: AnonymousJazzAgent };
9
+ isAuthenticated?: boolean;
9
10
  };
10
11
 
11
12
  const render = <T extends Component>(
@@ -13,7 +14,9 @@ const render = <T extends Component>(
13
14
  props: ComponentProps<T>,
14
15
  jazzOptions: JazzExtendedOptions,
15
16
  ) => {
16
- const ctx = TestJazzContextManager.fromAccountOrGuest(jazzOptions.account);
17
+ const ctx = TestJazzContextManager.fromAccountOrGuest(jazzOptions.account, {
18
+ isAuthenticated: jazzOptions.isAuthenticated,
19
+ });
17
20
 
18
21
  return renderSvelte(
19
22
  // @ts-expect-error Svelte new Component type is not compatible with @testing-library/svelte
@@ -47,7 +47,7 @@ type PermissiveClerkUser = Omit<ClerkUser, "unsafeMetadata" | "update"> & {
47
47
  export type MinimalClerkClient = {
48
48
  user: PermissiveClerkUser | null | undefined;
49
49
  signOut: () => Promise<void>;
50
- addListener: (listener: (data: unknown) => void) => void;
50
+ addListener: (listener: (data: unknown) => void) => (() => void) | void;
51
51
  };
52
52
 
53
53
  export type ClerkCredentials = {
@@ -49,4 +49,4 @@ export type SubscriptionValueLoading = {
49
49
  id: string;
50
50
  };
51
51
 
52
- export type BranchDefinition = { name: string; owner?: Group | Account | null };
52
+ export type BranchDefinition = { name: string; owner?: Group | Account };
@@ -604,16 +604,16 @@ describe("Inbox", () => {
604
604
  Message.create({ text: `Message ${i}`, value: i }, group),
605
605
  );
606
606
 
607
- // Subscribe with concurrency limit of 1
607
+ // Subscribe with concurrency limit of 3
608
608
  const unsubscribe = receiverInbox.subscribe(
609
609
  Message,
610
610
  async (message) => {
611
611
  const messageText = message.text;
612
612
  processingOrder.push(`start-${messageText}`);
613
613
 
614
- // Simulate processing time
614
+ // Simulate processing time (later messages take longer to ensure deterministic ordering)
615
615
  await new Promise((resolve) =>
616
- setTimeout(resolve, 20 - message.value * 10),
616
+ setTimeout(resolve, 10 + message.value * 10),
617
617
  );
618
618
 
619
619
  processingOrder.push(`end-${messageText}`);
@@ -633,13 +633,13 @@ describe("Inbox", () => {
633
633
  "start-Message 0",
634
634
  "start-Message 1",
635
635
  "start-Message 2",
636
- "end-Message 2",
636
+ "end-Message 0",
637
637
  "start-Message 3",
638
- "end-Message 3",
638
+ "end-Message 1",
639
639
  "start-Message 4",
640
+ "end-Message 2",
641
+ "end-Message 3",
640
642
  "end-Message 4",
641
- "end-Message 1",
642
- "end-Message 0",
643
643
  ]
644
644
  `);
645
645
  unsubscribe();