jazz-tools 0.18.0 → 0.18.1
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/.turbo/turbo-build.log +54 -40
- package/CHANGELOG.md +10 -0
- package/dist/better-auth/auth/client.d.ts +29 -0
- package/dist/better-auth/auth/client.d.ts.map +1 -0
- package/dist/better-auth/auth/client.js +127 -0
- package/dist/better-auth/auth/client.js.map +1 -0
- package/dist/better-auth/auth/react.d.ts +2170 -0
- package/dist/better-auth/auth/react.d.ts.map +1 -0
- package/dist/better-auth/auth/react.js +40 -0
- package/dist/better-auth/auth/react.js.map +1 -0
- package/dist/better-auth/auth/server.d.ts +14 -0
- package/dist/better-auth/auth/server.d.ts.map +1 -0
- package/dist/better-auth/auth/server.js +198 -0
- package/dist/better-auth/auth/server.js.map +1 -0
- package/dist/better-auth/auth/tests/client.test.d.ts +2 -0
- package/dist/better-auth/auth/tests/client.test.d.ts.map +1 -0
- package/dist/better-auth/auth/tests/server.test.d.ts +2 -0
- package/dist/better-auth/auth/tests/server.test.d.ts.map +1 -0
- package/dist/{chunk-HJ3GTGY7.js → chunk-IERUTUXB.js} +18 -1
- package/dist/chunk-IERUTUXB.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/react-core/index.js +17 -0
- package/dist/react-core/index.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/account.d.ts +1 -0
- package/dist/tools/coValues/account.d.ts.map +1 -1
- package/dist/tools/coValues/coMap.d.ts +10 -0
- package/dist/tools/coValues/coMap.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/zodCo.d.ts +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/package.json +23 -4
- package/src/better-auth/auth/client.ts +169 -0
- package/src/better-auth/auth/react.tsx +105 -0
- package/src/better-auth/auth/server.ts +250 -0
- package/src/better-auth/auth/tests/client.test.ts +249 -0
- package/src/better-auth/auth/tests/server.test.ts +226 -0
- package/src/tools/coValues/account.ts +5 -0
- package/src/tools/coValues/coMap.ts +14 -0
- package/src/tools/implementation/zodSchema/zodCo.ts +1 -1
- package/src/tools/tests/ContextManager.test.ts +2 -2
- package/src/tools/tests/account.test.ts +51 -0
- package/src/tools/tests/coMap.test.ts +99 -0
- package/src/tools/tests/patterns/notifications.test.ts +1 -1
- package/src/tools/tests/testing.test.ts +2 -2
- package/tsup.config.ts +9 -0
- package/dist/chunk-HJ3GTGY7.js.map +0 -1
@@ -0,0 +1,226 @@
|
|
1
|
+
import { betterAuth } from "better-auth";
|
2
|
+
import { memoryAdapter } from "better-auth/adapters/memory";
|
3
|
+
import { beforeEach, describe, expect, it, vi, type Mock } from "vitest";
|
4
|
+
import { jazzPlugin } from "../server.js";
|
5
|
+
|
6
|
+
describe("Better Auth - Signup and Login Tests", () => {
|
7
|
+
let auth: ReturnType<typeof betterAuth>;
|
8
|
+
let accountCreationSpy: Mock;
|
9
|
+
let verificationCreationSpy: Mock;
|
10
|
+
|
11
|
+
beforeEach(() => {
|
12
|
+
accountCreationSpy = vi.fn();
|
13
|
+
verificationCreationSpy = vi.fn();
|
14
|
+
|
15
|
+
// Create auth instance with in-memory database
|
16
|
+
auth = betterAuth({
|
17
|
+
database: memoryAdapter({
|
18
|
+
user: [],
|
19
|
+
session: [],
|
20
|
+
verification: [],
|
21
|
+
account: [],
|
22
|
+
}),
|
23
|
+
plugins: [jazzPlugin()],
|
24
|
+
emailAndPassword: {
|
25
|
+
enabled: true,
|
26
|
+
requireEmailVerification: false, // Disable for testing
|
27
|
+
},
|
28
|
+
socialProviders: {
|
29
|
+
github: {
|
30
|
+
clientId: "123",
|
31
|
+
clientSecret: "123",
|
32
|
+
},
|
33
|
+
},
|
34
|
+
databaseHooks: {
|
35
|
+
user: {
|
36
|
+
create: {
|
37
|
+
after: accountCreationSpy,
|
38
|
+
},
|
39
|
+
},
|
40
|
+
verification: {
|
41
|
+
create: {
|
42
|
+
after: verificationCreationSpy,
|
43
|
+
},
|
44
|
+
},
|
45
|
+
},
|
46
|
+
session: {
|
47
|
+
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
48
|
+
},
|
49
|
+
});
|
50
|
+
});
|
51
|
+
|
52
|
+
describe("User Registration (Signup)", () => {
|
53
|
+
it("should successfully register a new user with email and password", async () => {
|
54
|
+
const userData = {
|
55
|
+
name: "test",
|
56
|
+
email: "test@example.com",
|
57
|
+
password: "securePassword123",
|
58
|
+
};
|
59
|
+
|
60
|
+
const jazzAuth = {
|
61
|
+
accountID: "123",
|
62
|
+
secretSeed: [1, 2, 3],
|
63
|
+
accountSecret: "123",
|
64
|
+
provider: "better-auth",
|
65
|
+
};
|
66
|
+
|
67
|
+
const result = await auth.api.signUpEmail({
|
68
|
+
body: userData,
|
69
|
+
headers: {
|
70
|
+
"x-jazz-auth": JSON.stringify(jazzAuth),
|
71
|
+
},
|
72
|
+
});
|
73
|
+
|
74
|
+
expect(result).toBeDefined();
|
75
|
+
expect(result).toMatchObject({
|
76
|
+
user: {
|
77
|
+
id: expect.any(String),
|
78
|
+
email: userData.email,
|
79
|
+
name: userData.name,
|
80
|
+
image: undefined,
|
81
|
+
emailVerified: false,
|
82
|
+
createdAt: expect.any(Date),
|
83
|
+
updatedAt: expect.any(Date),
|
84
|
+
},
|
85
|
+
jazzAuth: jazzAuth,
|
86
|
+
});
|
87
|
+
|
88
|
+
const res = await (await auth.$context).adapter.findOne({
|
89
|
+
model: "user",
|
90
|
+
where: [
|
91
|
+
{
|
92
|
+
field: "id",
|
93
|
+
value: result.user.id,
|
94
|
+
},
|
95
|
+
],
|
96
|
+
});
|
97
|
+
|
98
|
+
expect(res).toMatchObject({
|
99
|
+
id: result.user.id,
|
100
|
+
accountID: "123",
|
101
|
+
encryptedCredentials: expect.any(String),
|
102
|
+
});
|
103
|
+
});
|
104
|
+
|
105
|
+
it("should fail to register user without account ID", async () => {
|
106
|
+
const userData = {
|
107
|
+
name: "test",
|
108
|
+
email: "email@email.it",
|
109
|
+
password: "securePassword123",
|
110
|
+
};
|
111
|
+
|
112
|
+
await expect(
|
113
|
+
auth.api.signUpEmail({
|
114
|
+
body: userData,
|
115
|
+
}),
|
116
|
+
).rejects.toThrow("JazzAuth is required");
|
117
|
+
|
118
|
+
expect(accountCreationSpy).toHaveBeenCalledTimes(0);
|
119
|
+
});
|
120
|
+
|
121
|
+
it("should have AccountID in the registration hook", async () => {
|
122
|
+
const userData = {
|
123
|
+
name: "test",
|
124
|
+
email: "email@email.it",
|
125
|
+
password: "securePassword123",
|
126
|
+
};
|
127
|
+
|
128
|
+
const jazzAuth = {
|
129
|
+
accountID: "123",
|
130
|
+
secretSeed: [1, 2, 3],
|
131
|
+
accountSecret: "123",
|
132
|
+
provider: "better-auth",
|
133
|
+
};
|
134
|
+
|
135
|
+
await auth.api.signUpEmail({
|
136
|
+
body: userData,
|
137
|
+
headers: {
|
138
|
+
"x-jazz-auth": JSON.stringify(jazzAuth),
|
139
|
+
},
|
140
|
+
});
|
141
|
+
|
142
|
+
expect(accountCreationSpy).toHaveBeenCalledTimes(1);
|
143
|
+
expect(accountCreationSpy).toHaveBeenCalledWith(
|
144
|
+
// user
|
145
|
+
expect.objectContaining({ accountID: "123" }),
|
146
|
+
// context
|
147
|
+
expect.any(Object),
|
148
|
+
);
|
149
|
+
});
|
150
|
+
});
|
151
|
+
|
152
|
+
describe("User login (Signin)", () => {
|
153
|
+
it("should successfully login a new user with email and password", async () => {
|
154
|
+
const userData = {
|
155
|
+
name: "test",
|
156
|
+
email: "test@example.com",
|
157
|
+
password: "securePassword123",
|
158
|
+
};
|
159
|
+
|
160
|
+
const jazzAuth = {
|
161
|
+
accountID: "123",
|
162
|
+
secretSeed: [1, 2, 3],
|
163
|
+
accountSecret: "123",
|
164
|
+
provider: "better-auth",
|
165
|
+
};
|
166
|
+
|
167
|
+
await auth.api.signUpEmail({
|
168
|
+
body: userData,
|
169
|
+
headers: {
|
170
|
+
"x-jazz-auth": JSON.stringify(jazzAuth),
|
171
|
+
},
|
172
|
+
});
|
173
|
+
|
174
|
+
const result = await auth.api.signInEmail({
|
175
|
+
body: {
|
176
|
+
email: userData.email,
|
177
|
+
password: userData.password,
|
178
|
+
},
|
179
|
+
});
|
180
|
+
|
181
|
+
expect(result).toBeDefined();
|
182
|
+
expect(result).toMatchObject({
|
183
|
+
user: {
|
184
|
+
id: expect.any(String),
|
185
|
+
email: userData.email,
|
186
|
+
name: userData.name,
|
187
|
+
image: undefined,
|
188
|
+
emailVerified: false,
|
189
|
+
createdAt: expect.any(Date),
|
190
|
+
updatedAt: expect.any(Date),
|
191
|
+
},
|
192
|
+
jazzAuth: jazzAuth,
|
193
|
+
});
|
194
|
+
});
|
195
|
+
});
|
196
|
+
|
197
|
+
describe("Social Login", () => {
|
198
|
+
it("should store jazzAuth in verification table when using social provider", async () => {
|
199
|
+
await auth.api.signInSocial({
|
200
|
+
body: {
|
201
|
+
provider: "github",
|
202
|
+
callbackURL: "http://localhost:3000/api/auth/sign-in/social/callback",
|
203
|
+
},
|
204
|
+
headers: {
|
205
|
+
"x-jazz-auth": JSON.stringify({
|
206
|
+
accountID: "123",
|
207
|
+
secretSeed: [1, 2, 3],
|
208
|
+
accountSecret: "123",
|
209
|
+
}),
|
210
|
+
},
|
211
|
+
});
|
212
|
+
|
213
|
+
expect(verificationCreationSpy).toHaveBeenCalledTimes(1);
|
214
|
+
expect(verificationCreationSpy).toHaveBeenCalledWith(
|
215
|
+
expect.objectContaining({
|
216
|
+
value: expect.stringContaining('"accountID":"123"'),
|
217
|
+
}),
|
218
|
+
expect.any(Object),
|
219
|
+
);
|
220
|
+
});
|
221
|
+
|
222
|
+
it.todo(
|
223
|
+
"should create a new account with jazz auth when using social provider",
|
224
|
+
);
|
225
|
+
});
|
226
|
+
});
|
@@ -418,6 +418,11 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
|
|
418
418
|
}
|
419
419
|
}
|
420
420
|
|
421
|
+
has(key: "root" | "profile"): boolean {
|
422
|
+
const entry = this.raw.getRaw(key);
|
423
|
+
return entry?.change !== undefined && entry.change.op !== "del";
|
424
|
+
}
|
425
|
+
|
421
426
|
/**
|
422
427
|
* Get the descriptor for a given key
|
423
428
|
* @internal
|
@@ -571,6 +571,20 @@ class CoMapJazzApi<M extends CoMap> extends CoValueJazzApi<M> {
|
|
571
571
|
return getCoValueOwner(this.coMap);
|
572
572
|
}
|
573
573
|
|
574
|
+
/**
|
575
|
+
* Check if a key is defined in the CoMap.
|
576
|
+
*
|
577
|
+
* This check does not load the referenced value or validate permissions.
|
578
|
+
*
|
579
|
+
* @param key The key to check
|
580
|
+
* @returns True if the key is defined, false otherwise
|
581
|
+
* @category Content
|
582
|
+
*/
|
583
|
+
has(key: CoKeys<M>): boolean {
|
584
|
+
const entry = this.raw.getRaw(key);
|
585
|
+
return entry?.change !== undefined && entry.change.op !== "del";
|
586
|
+
}
|
587
|
+
|
574
588
|
/**
|
575
589
|
* Set a value on the CoMap
|
576
590
|
*
|
@@ -101,7 +101,7 @@ export const coMapDefiner = <Shape extends z.core.$ZodLooseShape>(
|
|
101
101
|
* }),
|
102
102
|
* }).withMigration(async (account) => {
|
103
103
|
* // Migration logic for existing accounts
|
104
|
-
* if (account.profile
|
104
|
+
* if (!account.$jazz.has("profile")) {
|
105
105
|
* const group = Group.create();
|
106
106
|
* account.$jazz.set("profile", co.profile().create(
|
107
107
|
* { name: getRandomUsername() },
|
@@ -372,7 +372,7 @@ describe("ContextManager", () => {
|
|
372
372
|
profile: co.profile(),
|
373
373
|
})
|
374
374
|
.withMigration(async (account) => {
|
375
|
-
if (account.root
|
375
|
+
if (!account.$jazz.has("root")) {
|
376
376
|
account.$jazz.set(
|
377
377
|
"root",
|
378
378
|
AccountRoot.create({
|
@@ -429,7 +429,7 @@ describe("ContextManager", () => {
|
|
429
429
|
profile: co.profile(),
|
430
430
|
})
|
431
431
|
.withMigration(async (account) => {
|
432
|
-
if (account.root
|
432
|
+
if (!account.$jazz.has("root")) {
|
433
433
|
account.$jazz.set(
|
434
434
|
"root",
|
435
435
|
AccountRoot.create({
|
@@ -342,3 +342,54 @@ describe("root and profile", () => {
|
|
342
342
|
expect(account.root.name).toBe("test 1");
|
343
343
|
});
|
344
344
|
});
|
345
|
+
|
346
|
+
describe("account.$jazz.has", () => {
|
347
|
+
test("should return true if the key is defined", async () => {
|
348
|
+
const account = await createJazzTestAccount({
|
349
|
+
creationProps: { name: "John" },
|
350
|
+
});
|
351
|
+
|
352
|
+
expect(account.$jazz.has("profile")).toBe(true);
|
353
|
+
expect(account.$jazz.has("root")).toBe(false);
|
354
|
+
});
|
355
|
+
|
356
|
+
test("should work as migration check", async () => {
|
357
|
+
const CustomProfile = co.profile({
|
358
|
+
name: z.string(),
|
359
|
+
email: z.string().optional(),
|
360
|
+
});
|
361
|
+
|
362
|
+
const CustomRoot = co.map({
|
363
|
+
settings: z.string(),
|
364
|
+
});
|
365
|
+
|
366
|
+
const CustomAccount = co
|
367
|
+
.account({
|
368
|
+
profile: CustomProfile,
|
369
|
+
root: CustomRoot,
|
370
|
+
})
|
371
|
+
.withMigration((me, creationProps) => {
|
372
|
+
if (!me.$jazz.has("profile")) {
|
373
|
+
me.$jazz.set("profile", {
|
374
|
+
name: creationProps?.name ?? "Anonymous",
|
375
|
+
email: "test@example.com",
|
376
|
+
});
|
377
|
+
}
|
378
|
+
|
379
|
+
if (!me.$jazz.has("root")) {
|
380
|
+
me.$jazz.set("root", { settings: "default" });
|
381
|
+
}
|
382
|
+
});
|
383
|
+
|
384
|
+
const account = await createJazzTestAccount({
|
385
|
+
AccountSchema: CustomAccount,
|
386
|
+
creationProps: { name: "Custom User" },
|
387
|
+
});
|
388
|
+
|
389
|
+
expect(account.$jazz.has("profile")).toBe(true);
|
390
|
+
expect(account.$jazz.has("root")).toBe(true);
|
391
|
+
|
392
|
+
expect(account.profile.email).toBe("test@example.com");
|
393
|
+
expect(account.root.settings).toBe("default");
|
394
|
+
});
|
395
|
+
});
|
@@ -744,6 +744,105 @@ describe("CoMap", async () => {
|
|
744
744
|
});
|
745
745
|
});
|
746
746
|
|
747
|
+
describe("has", () => {
|
748
|
+
test("should return true if the key is defined", () => {
|
749
|
+
const Person = co.map({
|
750
|
+
name: z.string(),
|
751
|
+
age: z.number().optional(),
|
752
|
+
});
|
753
|
+
|
754
|
+
const person = Person.create({ name: "John", age: 20 });
|
755
|
+
|
756
|
+
expect(person.$jazz.has("name")).toBe(true);
|
757
|
+
expect(person.$jazz.has("age")).toBe(true);
|
758
|
+
});
|
759
|
+
|
760
|
+
test("should return true if the key was set to undefined", () => {
|
761
|
+
const Person = co.map({
|
762
|
+
name: z.string(),
|
763
|
+
age: z.number().optional(),
|
764
|
+
});
|
765
|
+
|
766
|
+
const person = Person.create({ name: "John" });
|
767
|
+
|
768
|
+
person.$jazz.set("age", undefined);
|
769
|
+
|
770
|
+
expect(person.$jazz.has("age")).toBe(true);
|
771
|
+
});
|
772
|
+
|
773
|
+
test("should return false if the key is not defined", () => {
|
774
|
+
const Person = co.map({
|
775
|
+
name: z.string(),
|
776
|
+
age: z.number().optional(),
|
777
|
+
});
|
778
|
+
|
779
|
+
const person = Person.create({ name: "John" });
|
780
|
+
|
781
|
+
expect(person.$jazz.has("age")).toBe(false);
|
782
|
+
});
|
783
|
+
|
784
|
+
test("should return false if the key was deleted", () => {
|
785
|
+
const Person = co.map({
|
786
|
+
name: z.string(),
|
787
|
+
age: z.number().optional(),
|
788
|
+
});
|
789
|
+
|
790
|
+
const person = Person.create({ name: "John", age: 20 });
|
791
|
+
|
792
|
+
person.$jazz.delete("age");
|
793
|
+
|
794
|
+
expect(person.$jazz.has("age")).toBe(false);
|
795
|
+
});
|
796
|
+
|
797
|
+
test("should not load the referenced CoValue", async () => {
|
798
|
+
const Person = co.map({
|
799
|
+
name: co.plainText(),
|
800
|
+
});
|
801
|
+
|
802
|
+
const { clientAccount, serverAccount } = await setupTwoNodes();
|
803
|
+
|
804
|
+
const person = Person.create(
|
805
|
+
{
|
806
|
+
name: "John",
|
807
|
+
},
|
808
|
+
{ owner: Group.create(serverAccount).makePublic() },
|
809
|
+
);
|
810
|
+
|
811
|
+
const loadedPerson = await Person.load(person.$jazz.id, {
|
812
|
+
resolve: true,
|
813
|
+
loadAs: clientAccount,
|
814
|
+
});
|
815
|
+
|
816
|
+
assert(loadedPerson);
|
817
|
+
expect(loadedPerson.$jazz.has("name")).toBe(true);
|
818
|
+
expect(loadedPerson.name).toBeNull();
|
819
|
+
});
|
820
|
+
|
821
|
+
test("should return true even if the viewer doesn't have access to the referenced CoValue", async () => {
|
822
|
+
const Person = co.map({
|
823
|
+
name: co.plainText(),
|
824
|
+
});
|
825
|
+
|
826
|
+
const person = Person.create(
|
827
|
+
// UserB has no access to name
|
828
|
+
{ name: co.plainText().create("John", Group.create()) },
|
829
|
+
// UserB has access to person
|
830
|
+
{ owner: Group.create().makePublic() },
|
831
|
+
);
|
832
|
+
|
833
|
+
const userB = await createJazzTestAccount();
|
834
|
+
|
835
|
+
const loadedPerson = await Person.load(person.$jazz.id, {
|
836
|
+
resolve: true,
|
837
|
+
loadAs: userB,
|
838
|
+
});
|
839
|
+
|
840
|
+
assert(loadedPerson);
|
841
|
+
expect(loadedPerson.$jazz.has("name")).toBe(true);
|
842
|
+
expect(loadedPerson.name).toBeNull();
|
843
|
+
});
|
844
|
+
});
|
845
|
+
|
747
846
|
test("Enum of maps", () => {
|
748
847
|
const ChildA = co.map({
|
749
848
|
type: z.literal("a"),
|
@@ -35,7 +35,7 @@ describe("Jazz Test Sync", () => {
|
|
35
35
|
profile: co.profile(),
|
36
36
|
})
|
37
37
|
.withMigration((account) => {
|
38
|
-
if (account.root
|
38
|
+
if (!account.$jazz.has("root")) {
|
39
39
|
account.$jazz.set("root", { value: "ok" });
|
40
40
|
}
|
41
41
|
});
|
@@ -59,7 +59,7 @@ describe("Jazz Test Sync", () => {
|
|
59
59
|
profile: co.profile(),
|
60
60
|
})
|
61
61
|
.withMigration((account) => {
|
62
|
-
if (account.root
|
62
|
+
if (!account.$jazz.has("root")) {
|
63
63
|
account.$jazz.set("root", { value: "ok" });
|
64
64
|
}
|
65
65
|
});
|
package/tsup.config.ts
CHANGED
@@ -137,4 +137,13 @@ export default defineConfig([
|
|
137
137
|
},
|
138
138
|
outDir: "dist/worker",
|
139
139
|
},
|
140
|
+
{
|
141
|
+
...cfg,
|
142
|
+
entry: {
|
143
|
+
client: "src/better-auth/auth/client.ts",
|
144
|
+
server: "src/better-auth/auth/server.ts",
|
145
|
+
react: "src/better-auth/auth/react.tsx",
|
146
|
+
},
|
147
|
+
outDir: "dist/better-auth/auth",
|
148
|
+
},
|
140
149
|
]);
|