jazz-browser-auth-clerk 0.8.13 → 0.8.15

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # jazz-browser-media-images
2
2
 
3
+ ## 0.8.15
4
+
5
+ ### Patch Changes
6
+
7
+ - 221c58f: Store clerk credentials in localStorage, and use if available
8
+ - Updated dependencies [cce679b]
9
+ - jazz-tools@0.8.15
10
+ - jazz-browser@0.8.15
11
+
12
+ ## 0.8.14
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [36273b3]
17
+ - jazz-tools@0.8.14
18
+ - jazz-browser@0.8.14
19
+
3
20
  ## 0.8.13
4
21
 
5
22
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,10 +1,41 @@
1
+ const localStorageKey = "jazz-clerk-auth";
2
+ function saveCredentialsToLocalStorage(credentials) {
3
+ localStorage.setItem(localStorageKey, JSON.stringify({
4
+ accountID: credentials.accountID,
5
+ secret: credentials.secret,
6
+ }));
7
+ }
1
8
  export class BrowserClerkAuth {
2
9
  constructor(driver, clerkClient) {
3
10
  this.driver = driver;
4
11
  this.clerkClient = clerkClient;
5
12
  }
6
13
  async start() {
14
+ // Check local storage for credentials
15
+ const locallyStoredCredentials = localStorage.getItem(localStorageKey);
16
+ if (locallyStoredCredentials) {
17
+ try {
18
+ const credentials = JSON.parse(locallyStoredCredentials);
19
+ return {
20
+ type: "existing",
21
+ credentials,
22
+ saveCredentials: async () => { }, // No need to save credentials when recovering from local storage
23
+ onSuccess: () => { },
24
+ onError: (error) => {
25
+ this.driver.onError(error);
26
+ },
27
+ logOut: () => {
28
+ localStorage.removeItem(localStorageKey);
29
+ void this.clerkClient.signOut();
30
+ },
31
+ };
32
+ }
33
+ catch (e) {
34
+ console.error("Error parsing local storage credentials", e);
35
+ }
36
+ }
7
37
  if (this.clerkClient.user) {
38
+ // Check clerk user metadata for credentials
8
39
  const storedCredentials = this.clerkClient.user.unsafeMetadata;
9
40
  if (storedCredentials.jazzAccountID) {
10
41
  if (!storedCredentials.jazzAccountSecret) {
@@ -16,6 +47,12 @@ export class BrowserClerkAuth {
16
47
  accountID: storedCredentials.jazzAccountID,
17
48
  secret: storedCredentials.jazzAccountSecret,
18
49
  },
50
+ saveCredentials: async ({ accountID, secret, }) => {
51
+ saveCredentialsToLocalStorage({
52
+ accountID,
53
+ secret,
54
+ });
55
+ },
19
56
  onSuccess: () => { },
20
57
  onError: (error) => {
21
58
  this.driver.onError(error);
@@ -26,6 +63,7 @@ export class BrowserClerkAuth {
26
63
  };
27
64
  }
28
65
  else {
66
+ // No credentials found, so we need to create new credentials
29
67
  return {
30
68
  type: "new",
31
69
  creationProps: {
@@ -33,11 +71,15 @@ export class BrowserClerkAuth {
33
71
  this.clerkClient.user.username ||
34
72
  this.clerkClient.user.id,
35
73
  },
36
- saveCredentials: async (credentials) => {
74
+ saveCredentials: async ({ accountID, secret, }) => {
75
+ saveCredentialsToLocalStorage({
76
+ accountID,
77
+ secret,
78
+ });
37
79
  await this.clerkClient.user?.update({
38
80
  unsafeMetadata: {
39
- jazzAccountID: credentials.accountID,
40
- jazzAccountSecret: credentials.secret,
81
+ jazzAccountID: accountID,
82
+ jazzAccountSecret: secret,
41
83
  },
42
84
  });
43
85
  },
@@ -52,6 +94,7 @@ export class BrowserClerkAuth {
52
94
  }
53
95
  }
54
96
  else {
97
+ // Clerk user not found, so we can't authenticate
55
98
  throw new Error("Not signed in");
56
99
  }
57
100
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,gBAAgB;IACzB,YACW,MAA+B,EACrB,WAA+B;QADzC,WAAM,GAAN,MAAM,CAAyB;QACrB,gBAAW,GAAX,WAAW,CAAoB;IACjD,CAAC;IAEJ,KAAK,CAAC,KAAK;QACP,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACxB,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC;YAC/D,IAAI,iBAAiB,CAAC,aAAa,EAAE,CAAC;gBAClC,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC;oBACvC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO;oBACH,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE;wBACT,SAAS,EACL,iBAAiB,CAAC,aAA4B;wBAClD,MAAM,EAAE,iBAAiB,CAAC,iBAAgC;qBAC7D;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,OAAO;oBACH,IAAI,EAAE,KAAK;oBACX,aAAa,EAAE;wBACX,IAAI,EACA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;qBAC/B;oBACD,eAAe,EAAE,KAAK,EAAE,WAGvB,EAAE,EAAE;wBACD,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC;4BAChC,cAAc,EAAE;gCACZ,aAAa,EAAE,WAAW,CAAC,SAAS;gCACpC,iBAAiB,EAAE,WAAW,CAAC,MAAM;6BACxC;yBACJ,CAAC,CAAC;oBACP,CAAC;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;CACJ"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAiB1C,SAAS,6BAA6B,CAAC,WAAwB;IAC3D,YAAY,CAAC,OAAO,CAChB,eAAe,EACf,IAAI,CAAC,SAAS,CAAC;QACX,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,MAAM,EAAE,WAAW,CAAC,MAAM;KAC7B,CAAC,CACL,CAAC;AACN,CAAC;AAED,MAAM,OAAO,gBAAgB;IACzB,YACW,MAA+B,EACrB,WAA+B;QADzC,WAAM,GAAN,MAAM,CAAyB;QACrB,gBAAW,GAAX,WAAW,CAAoB;IACjD,CAAC;IAEJ,KAAK,CAAC,KAAK;QACP,sCAAsC;QACtC,MAAM,wBAAwB,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAEvE,IAAI,wBAAwB,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC1B,wBAAwB,CACZ,CAAC;gBACjB,OAAO;oBACH,IAAI,EAAE,UAAU;oBAChB,WAAW;oBACX,eAAe,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,iEAAiE;oBAClG,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;wBACzC,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,CAAC,CAAC,CAAC;YAChE,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACxB,4CAA4C;YAC5C,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC;YAC/D,IAAI,iBAAiB,CAAC,aAAa,EAAE,CAAC;gBAClC,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC;oBACvC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;gBACnD,CAAC;gBACD,OAAO;oBACH,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE;wBACT,SAAS,EACL,iBAAiB,CAAC,aAA4B;wBAClD,MAAM,EAAE,iBAAiB,CAAC,iBAAgC;qBAC7D;oBACD,eAAe,EAAE,KAAK,EAAE,EACpB,SAAS,EACT,MAAM,GACI,EAAE,EAAE;wBACd,6BAA6B,CAAC;4BAC1B,SAAS;4BACT,MAAM;yBACT,CAAC,CAAC;oBACP,CAAC;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,6DAA6D;gBAC7D,OAAO;oBACH,IAAI,EAAE,KAAK;oBACX,aAAa,EAAE;wBACX,IAAI,EACA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;4BAC9B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;qBAC/B;oBACD,eAAe,EAAE,KAAK,EAAE,EACpB,SAAS,EACT,MAAM,GACI,EAAE,EAAE;wBACd,6BAA6B,CAAC;4BAC1B,SAAS;4BACT,MAAM;yBACT,CAAC,CAAC;wBACH,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC;4BAChC,cAAc,EAAE;gCACZ,aAAa,EAAE,SAAS;gCACxB,iBAAiB,EAAE,MAAM;6BAC5B;yBACJ,CAAC,CAAC;oBACP,CAAC;oBACD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;oBACnB,OAAO,EAAE,CAAC,KAAqB,EAAE,EAAE;wBAC/B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM,EAAE,GAAG,EAAE;wBACT,KAAK,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;oBACpC,CAAC;iBACJ,CAAC;YACN,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,iDAAiD;YACjD,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,102 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { BrowserClerkAuth } from "../index.js";
3
+ describe("BrowserClerkAuth", () => {
4
+ let mockLocalStorage;
5
+ let mockClerkClient;
6
+ let mockDriver;
7
+ beforeEach(() => {
8
+ // Mock localStorage
9
+ mockLocalStorage = {};
10
+ global.localStorage = {
11
+ getItem: vi.fn((key) => mockLocalStorage[key] || null),
12
+ setItem: vi.fn((key, value) => {
13
+ mockLocalStorage[key] = value;
14
+ }),
15
+ removeItem: vi.fn((key) => {
16
+ delete mockLocalStorage[key];
17
+ }),
18
+ clear: vi.fn(),
19
+ length: 0,
20
+ key: vi.fn(),
21
+ };
22
+ // Mock Clerk client
23
+ mockClerkClient = {
24
+ user: {
25
+ unsafeMetadata: {},
26
+ fullName: "Test User",
27
+ username: "testuser",
28
+ id: "test-id",
29
+ update: vi.fn(),
30
+ },
31
+ signOut: vi.fn(),
32
+ };
33
+ // Mock driver
34
+ mockDriver = {
35
+ onError: vi.fn(),
36
+ };
37
+ });
38
+ describe("clerk credentials in localStorage", () => {
39
+ it("should get credentials from localStorage when clerk user is not signed in", async () => {
40
+ mockLocalStorage["jazz-clerk-auth"] = JSON.stringify({
41
+ accountID: "test-account-id",
42
+ accountSecret: "test-secret",
43
+ });
44
+ const auth = new BrowserClerkAuth(mockDriver, {
45
+ ...mockClerkClient,
46
+ user: null,
47
+ });
48
+ const result = await auth.start();
49
+ expect(result.type).toBe("existing");
50
+ });
51
+ });
52
+ describe("clerk credentials not in localStorage", () => {
53
+ it("should return new credentials when clerk user signs up", async () => {
54
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
55
+ const result = await auth.start();
56
+ expect(result.type).toBe("new");
57
+ });
58
+ it("should return existing credentials when clerk user is signed in", async () => {
59
+ mockClerkClient = {
60
+ user: {
61
+ unsafeMetadata: {
62
+ jazzAccountID: "test-account-id",
63
+ jazzAccountSecret: "test-secret",
64
+ },
65
+ fullName: "Test User",
66
+ username: "testuser",
67
+ id: "test-id",
68
+ update: vi.fn(),
69
+ },
70
+ signOut: vi.fn(),
71
+ };
72
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
73
+ const result = await auth.start();
74
+ expect(result.type).toBe("existing");
75
+ });
76
+ it("should throw error when not signed in", async () => {
77
+ const auth = new BrowserClerkAuth(mockDriver, {
78
+ ...mockClerkClient,
79
+ user: null,
80
+ });
81
+ await expect(auth.start()).rejects.toThrow("Not signed in");
82
+ });
83
+ it("should save credentials to localStorage", async () => {
84
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
85
+ const result = await auth.start();
86
+ if (result.saveCredentials) {
87
+ await result.saveCredentials({
88
+ accountID: "test-account-id",
89
+ secret: "test-secret",
90
+ });
91
+ }
92
+ expect(mockLocalStorage["jazz-clerk-auth"]).toBeDefined();
93
+ });
94
+ it("should call clerk signOut when logging out", async () => {
95
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
96
+ const result = await auth.start();
97
+ result.logOut();
98
+ expect(mockClerkClient.signOut).toHaveBeenCalled();
99
+ });
100
+ });
101
+ });
102
+ //# sourceMappingURL=BrowserClerkAuth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserClerkAuth.test.js","sourceRoot":"","sources":["../../src/tests/BrowserClerkAuth.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jazz-browser-auth-clerk",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
@@ -11,8 +11,8 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "cojson": "0.8.12",
14
- "jazz-browser": "0.8.13",
15
- "jazz-tools": "0.8.13"
14
+ "jazz-browser": "0.8.15",
15
+ "jazz-tools": "0.8.15"
16
16
  },
17
17
  "devDependencies": {
18
18
  "typescript": "^5.3.3"
@@ -20,6 +20,8 @@
20
20
  "scripts": {
21
21
  "lint": "eslint . --ext ts,tsx",
22
22
  "format": "prettier --write './src/**/*.{ts,tsx}'",
23
- "build": "npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist"
23
+ "build": "npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist",
24
+ "test": "vitest --run --root ../../ --project jazz-browser-auth-clerk",
25
+ "test:watch": "vitest --watch --root ../../ --project jazz-browser-auth-clerk"
24
26
  }
25
27
  }
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { Account, AuthMethod, AuthResult, ID } from "jazz-tools";
2
1
  import { AgentSecret } from "cojson";
2
+ import { Account, AuthMethod, AuthResult, Credentials, ID } from "jazz-tools";
3
+
4
+ const localStorageKey = "jazz-clerk-auth";
3
5
 
4
6
  export type MinimalClerkClient = {
5
7
  user: {
@@ -16,6 +18,16 @@ export type MinimalClerkClient = {
16
18
  signOut: () => Promise<void>;
17
19
  }
18
20
 
21
+ function saveCredentialsToLocalStorage(credentials: Credentials) {
22
+ localStorage.setItem(
23
+ localStorageKey,
24
+ JSON.stringify({
25
+ accountID: credentials.accountID,
26
+ secret: credentials.secret,
27
+ }),
28
+ );
29
+ }
30
+
19
31
  export class BrowserClerkAuth implements AuthMethod {
20
32
  constructor(
21
33
  public driver: BrowserClerkAuth.Driver,
@@ -23,7 +35,34 @@ export class BrowserClerkAuth implements AuthMethod {
23
35
  ) {}
24
36
 
25
37
  async start(): Promise<AuthResult> {
38
+ // Check local storage for credentials
39
+ const locallyStoredCredentials = localStorage.getItem(localStorageKey);
40
+
41
+ if (locallyStoredCredentials) {
42
+ try {
43
+ const credentials = JSON.parse(
44
+ locallyStoredCredentials,
45
+ ) as Credentials;
46
+ return {
47
+ type: "existing",
48
+ credentials,
49
+ saveCredentials: async () => {}, // No need to save credentials when recovering from local storage
50
+ onSuccess: () => {},
51
+ onError: (error: string | Error) => {
52
+ this.driver.onError(error);
53
+ },
54
+ logOut: () => {
55
+ localStorage.removeItem(localStorageKey);
56
+ void this.clerkClient.signOut();
57
+ },
58
+ };
59
+ } catch (e) {
60
+ console.error("Error parsing local storage credentials", e);
61
+ }
62
+ }
63
+
26
64
  if (this.clerkClient.user) {
65
+ // Check clerk user metadata for credentials
27
66
  const storedCredentials = this.clerkClient.user.unsafeMetadata;
28
67
  if (storedCredentials.jazzAccountID) {
29
68
  if (!storedCredentials.jazzAccountSecret) {
@@ -36,6 +75,15 @@ export class BrowserClerkAuth implements AuthMethod {
36
75
  storedCredentials.jazzAccountID as ID<Account>,
37
76
  secret: storedCredentials.jazzAccountSecret as AgentSecret,
38
77
  },
78
+ saveCredentials: async ({
79
+ accountID,
80
+ secret,
81
+ }: Credentials) => {
82
+ saveCredentialsToLocalStorage({
83
+ accountID,
84
+ secret,
85
+ });
86
+ },
39
87
  onSuccess: () => {},
40
88
  onError: (error: string | Error) => {
41
89
  this.driver.onError(error);
@@ -45,6 +93,7 @@ export class BrowserClerkAuth implements AuthMethod {
45
93
  },
46
94
  };
47
95
  } else {
96
+ // No credentials found, so we need to create new credentials
48
97
  return {
49
98
  type: "new",
50
99
  creationProps: {
@@ -53,14 +102,18 @@ export class BrowserClerkAuth implements AuthMethod {
53
102
  this.clerkClient.user.username ||
54
103
  this.clerkClient.user.id,
55
104
  },
56
- saveCredentials: async (credentials: {
57
- accountID: ID<Account>;
58
- secret: AgentSecret;
59
- }) => {
105
+ saveCredentials: async ({
106
+ accountID,
107
+ secret,
108
+ }: Credentials) => {
109
+ saveCredentialsToLocalStorage({
110
+ accountID,
111
+ secret,
112
+ });
60
113
  await this.clerkClient.user?.update({
61
114
  unsafeMetadata: {
62
- jazzAccountID: credentials.accountID,
63
- jazzAccountSecret: credentials.secret,
115
+ jazzAccountID: accountID,
116
+ jazzAccountSecret: secret,
64
117
  },
65
118
  });
66
119
  },
@@ -74,6 +127,7 @@ export class BrowserClerkAuth implements AuthMethod {
74
127
  };
75
128
  }
76
129
  } else {
130
+ // Clerk user not found, so we can't authenticate
77
131
  throw new Error("Not signed in");
78
132
  }
79
133
  }
@@ -0,0 +1,119 @@
1
+ import { AgentSecret } from "cojson";
2
+ import { Account, ID } from "jazz-tools";
3
+ import { beforeEach, describe, expect, it, vi } from "vitest";
4
+ import { BrowserClerkAuth, MinimalClerkClient } from "../index.js";
5
+
6
+ describe("BrowserClerkAuth", () => {
7
+ let mockLocalStorage: { [key: string]: string };
8
+ let mockClerkClient: MinimalClerkClient;
9
+ let mockDriver: BrowserClerkAuth.Driver;
10
+
11
+ beforeEach(() => {
12
+ // Mock localStorage
13
+ mockLocalStorage = {};
14
+ global.localStorage = {
15
+ getItem: vi.fn((key: string) => mockLocalStorage[key] || null),
16
+ setItem: vi.fn((key: string, value: string) => {
17
+ mockLocalStorage[key] = value;
18
+ }),
19
+ removeItem: vi.fn((key: string) => {
20
+ delete mockLocalStorage[key];
21
+ }),
22
+ clear: vi.fn(),
23
+ length: 0,
24
+ key: vi.fn(),
25
+ };
26
+
27
+ // Mock Clerk client
28
+ mockClerkClient = {
29
+ user: {
30
+ unsafeMetadata: {},
31
+ fullName: "Test User",
32
+ username: "testuser",
33
+ id: "test-id",
34
+ update: vi.fn(),
35
+ },
36
+ signOut: vi.fn(),
37
+ };
38
+
39
+ // Mock driver
40
+ mockDriver = {
41
+ onError: vi.fn(),
42
+ };
43
+ });
44
+
45
+ describe("clerk credentials in localStorage", () => {
46
+ it("should get credentials from localStorage when clerk user is not signed in", async () => {
47
+ mockLocalStorage["jazz-clerk-auth"] = JSON.stringify({
48
+ accountID: "test-account-id",
49
+ accountSecret: "test-secret",
50
+ });
51
+
52
+ const auth = new BrowserClerkAuth(mockDriver, {
53
+ ...mockClerkClient,
54
+ user: null,
55
+ });
56
+
57
+ const result = await auth.start();
58
+ expect(result.type).toBe("existing");
59
+ });
60
+ });
61
+
62
+ describe("clerk credentials not in localStorage", () => {
63
+ it("should return new credentials when clerk user signs up", async () => {
64
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
65
+ const result = await auth.start();
66
+ expect(result.type).toBe("new");
67
+ });
68
+
69
+ it("should return existing credentials when clerk user is signed in", async () => {
70
+ mockClerkClient = {
71
+ user: {
72
+ unsafeMetadata: {
73
+ jazzAccountID: "test-account-id",
74
+ jazzAccountSecret: "test-secret",
75
+ },
76
+ fullName: "Test User",
77
+ username: "testuser",
78
+ id: "test-id",
79
+ update: vi.fn(),
80
+ },
81
+ signOut: vi.fn(),
82
+ };
83
+
84
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
85
+ const result = await auth.start();
86
+ expect(result.type).toBe("existing");
87
+ });
88
+
89
+ it("should throw error when not signed in", async () => {
90
+ const auth = new BrowserClerkAuth(mockDriver, {
91
+ ...mockClerkClient,
92
+ user: null,
93
+ });
94
+
95
+ await expect(auth.start()).rejects.toThrow("Not signed in");
96
+ });
97
+
98
+ it("should save credentials to localStorage", async () => {
99
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
100
+ const result = await auth.start();
101
+ if (result.saveCredentials) {
102
+ await result.saveCredentials({
103
+ accountID: "test-account-id" as ID<Account>,
104
+ secret: "test-secret" as AgentSecret,
105
+ });
106
+ }
107
+
108
+ expect(mockLocalStorage["jazz-clerk-auth"]).toBeDefined();
109
+ });
110
+
111
+ it("should call clerk signOut when logging out", async () => {
112
+ const auth = new BrowserClerkAuth(mockDriver, mockClerkClient);
113
+ const result = await auth.start();
114
+ result.logOut();
115
+
116
+ expect(mockClerkClient.signOut).toHaveBeenCalled();
117
+ });
118
+ });
119
+ });