jazz-browser-auth-clerk 0.8.14 → 0.8.16

Sign up to get free protection for your applications and to get access to all the features.
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":"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"}
@@ -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,25 +1,23 @@
1
1
  {
2
2
  "name": "jazz-browser-auth-clerk",
3
- "version": "0.8.14",
3
+ "version": "0.8.16",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
7
7
  "license": "MIT",
8
- "lint-staged": {
9
- "*.{ts,tsx}": "eslint --fix",
10
- "*.{js,jsx,mdx,json}": "prettier --write"
11
- },
12
8
  "dependencies": {
13
- "cojson": "0.8.12",
14
- "jazz-browser": "0.8.14",
15
- "jazz-tools": "0.8.14"
9
+ "cojson": "0.8.16",
10
+ "jazz-browser": "0.8.16",
11
+ "jazz-tools": "0.8.16"
16
12
  },
17
13
  "devDependencies": {
18
14
  "typescript": "^5.3.3"
19
15
  },
20
16
  "scripts": {
21
- "lint": "eslint . --ext ts,tsx",
22
- "format": "prettier --write './src/**/*.{ts,tsx}'",
23
- "build": "npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist"
17
+ "format-and-lint": "biome check .",
18
+ "format-and-lint:fix": "biome check . --write",
19
+ "build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
20
+ "test": "vitest --run --root ../../ --project jazz-browser-auth-clerk",
21
+ "test:watch": "vitest --watch --root ../../ --project jazz-browser-auth-clerk"
24
22
  }
25
23
  }
package/src/index.ts CHANGED
@@ -1,87 +1,135 @@
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
- user: {
7
+ user:
8
+ | {
6
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
10
  unsafeMetadata: Record<string, any>;
8
11
  fullName: string | null;
9
12
  username: string | null;
10
13
  id: string;
11
14
  update: (args: {
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- unsafeMetadata: Record<string, any>;
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ unsafeMetadata: Record<string, any>;
14
17
  }) => Promise<unknown>;
15
- } | null | undefined;
16
- signOut: () => Promise<void>;
18
+ }
19
+ | null
20
+ | undefined;
21
+ signOut: () => Promise<void>;
22
+ };
23
+
24
+ function saveCredentialsToLocalStorage(credentials: Credentials) {
25
+ localStorage.setItem(
26
+ localStorageKey,
27
+ JSON.stringify({
28
+ accountID: credentials.accountID,
29
+ secret: credentials.secret,
30
+ }),
31
+ );
17
32
  }
18
33
 
19
34
  export class BrowserClerkAuth implements AuthMethod {
20
- constructor(
21
- public driver: BrowserClerkAuth.Driver,
22
- private readonly clerkClient: MinimalClerkClient
23
- ) {}
35
+ constructor(
36
+ public driver: BrowserClerkAuth.Driver,
37
+ private readonly clerkClient: MinimalClerkClient,
38
+ ) {}
39
+
40
+ async start(): Promise<AuthResult> {
41
+ // Check local storage for credentials
42
+ const locallyStoredCredentials = localStorage.getItem(localStorageKey);
24
43
 
25
- async start(): Promise<AuthResult> {
26
- if (this.clerkClient.user) {
27
- const storedCredentials = this.clerkClient.user.unsafeMetadata;
28
- if (storedCredentials.jazzAccountID) {
29
- if (!storedCredentials.jazzAccountSecret) {
30
- throw new Error("No secret for existing user");
31
- }
32
- return {
33
- type: "existing",
34
- credentials: {
35
- accountID:
36
- storedCredentials.jazzAccountID as ID<Account>,
37
- secret: storedCredentials.jazzAccountSecret as AgentSecret,
38
- },
39
- onSuccess: () => {},
40
- onError: (error: string | Error) => {
41
- this.driver.onError(error);
42
- },
43
- logOut: () => {
44
- void this.clerkClient.signOut();
45
- },
46
- };
47
- } else {
48
- return {
49
- type: "new",
50
- creationProps: {
51
- name:
52
- this.clerkClient.user.fullName ||
53
- this.clerkClient.user.username ||
54
- this.clerkClient.user.id,
55
- },
56
- saveCredentials: async (credentials: {
57
- accountID: ID<Account>;
58
- secret: AgentSecret;
59
- }) => {
60
- await this.clerkClient.user?.update({
61
- unsafeMetadata: {
62
- jazzAccountID: credentials.accountID,
63
- jazzAccountSecret: credentials.secret,
64
- },
65
- });
66
- },
67
- onSuccess: () => {},
68
- onError: (error: string | Error) => {
69
- this.driver.onError(error);
70
- },
71
- logOut: () => {
72
- void this.clerkClient.signOut();
73
- },
74
- };
75
- }
76
- } else {
77
- throw new Error("Not signed in");
44
+ if (locallyStoredCredentials) {
45
+ try {
46
+ const credentials = JSON.parse(locallyStoredCredentials) as Credentials;
47
+ return {
48
+ type: "existing",
49
+ credentials,
50
+ saveCredentials: async () => {}, // No need to save credentials when recovering from local storage
51
+ onSuccess: () => {},
52
+ onError: (error: string | Error) => {
53
+ this.driver.onError(error);
54
+ },
55
+ logOut: () => {
56
+ localStorage.removeItem(localStorageKey);
57
+ void this.clerkClient.signOut();
58
+ },
59
+ };
60
+ } catch (e) {
61
+ console.error("Error parsing local storage credentials", e);
62
+ }
63
+ }
64
+
65
+ if (this.clerkClient.user) {
66
+ // Check clerk user metadata for credentials
67
+ const storedCredentials = this.clerkClient.user.unsafeMetadata;
68
+ if (storedCredentials.jazzAccountID) {
69
+ if (!storedCredentials.jazzAccountSecret) {
70
+ throw new Error("No secret for existing user");
78
71
  }
72
+ return {
73
+ type: "existing",
74
+ credentials: {
75
+ accountID: storedCredentials.jazzAccountID as ID<Account>,
76
+ secret: storedCredentials.jazzAccountSecret as AgentSecret,
77
+ },
78
+ saveCredentials: async ({ accountID, secret }: Credentials) => {
79
+ saveCredentialsToLocalStorage({
80
+ accountID,
81
+ secret,
82
+ });
83
+ },
84
+ onSuccess: () => {},
85
+ onError: (error: string | Error) => {
86
+ this.driver.onError(error);
87
+ },
88
+ logOut: () => {
89
+ void this.clerkClient.signOut();
90
+ },
91
+ };
92
+ } else {
93
+ // No credentials found, so we need to create new credentials
94
+ return {
95
+ type: "new",
96
+ creationProps: {
97
+ name:
98
+ this.clerkClient.user.fullName ||
99
+ this.clerkClient.user.username ||
100
+ this.clerkClient.user.id,
101
+ },
102
+ saveCredentials: async ({ accountID, secret }: Credentials) => {
103
+ saveCredentialsToLocalStorage({
104
+ accountID,
105
+ secret,
106
+ });
107
+ await this.clerkClient.user?.update({
108
+ unsafeMetadata: {
109
+ jazzAccountID: accountID,
110
+ jazzAccountSecret: secret,
111
+ },
112
+ });
113
+ },
114
+ onSuccess: () => {},
115
+ onError: (error: string | Error) => {
116
+ this.driver.onError(error);
117
+ },
118
+ logOut: () => {
119
+ void this.clerkClient.signOut();
120
+ },
121
+ };
122
+ }
123
+ } else {
124
+ // Clerk user not found, so we can't authenticate
125
+ throw new Error("Not signed in");
79
126
  }
127
+ }
80
128
  }
81
129
 
82
130
  // eslint-disable-next-line @typescript-eslint/no-namespace
83
131
  export namespace BrowserClerkAuth {
84
- export interface Driver {
85
- onError: (error: string | Error) => void;
86
- }
132
+ export interface Driver {
133
+ onError: (error: string | Error) => void;
134
+ }
87
135
  }
@@ -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
+ });
package/tsconfig.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "jsx": "react",
11
11
  "forceConsistentCasingInFileNames": true,
12
12
  "noUncheckedIndexedAccess": true,
13
- "esModuleInterop": true,
13
+ "esModuleInterop": true
14
14
  },
15
- "include": ["./src/**/*"],
15
+ "include": ["./src/**/*"]
16
16
  }
package/.eslintrc.cjs DELETED
@@ -1,24 +0,0 @@
1
- module.exports = {
2
- extends: [
3
- "eslint:recommended",
4
- "plugin:@typescript-eslint/recommended",
5
- "plugin:require-extensions/recommended",
6
- "prettier"
7
- ],
8
- parser: "@typescript-eslint/parser",
9
- plugins: ["@typescript-eslint", "require-extensions"],
10
- parserOptions: {
11
- project: "./tsconfig.json",
12
- tsconfigRootDir: __dirname,
13
- },
14
- ignorePatterns: [".eslint.cjs"],
15
- root: true,
16
- rules: {
17
- "no-unused-vars": "off",
18
- "@typescript-eslint/no-unused-vars": [
19
- "error",
20
- { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
21
- ],
22
- "@typescript-eslint/no-floating-promises": "error",
23
- },
24
- }
package/.prettierrc.js DELETED
@@ -1,9 +0,0 @@
1
- /** @type {import("prettier").Config} */
2
- const config = {
3
- trailingComma: "all",
4
- tabWidth: 4,
5
- semi: true,
6
- singleQuote: false,
7
- };
8
-
9
- export default config;