securepool 1.0.0

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 (126) hide show
  1. package/.dockerignore +7 -0
  2. package/.env.example +20 -0
  3. package/ARCHITECTURE.md +279 -0
  4. package/DEPLOYMENT.md +441 -0
  5. package/README.md +283 -0
  6. package/SETUP.md +388 -0
  7. package/apps/demo-backend/Dockerfile +33 -0
  8. package/apps/demo-backend/package.json +19 -0
  9. package/apps/demo-backend/src/index.ts +71 -0
  10. package/apps/demo-backend/tsconfig.json +8 -0
  11. package/apps/demo-frontend/.env.example +2 -0
  12. package/apps/demo-frontend/README.md +73 -0
  13. package/apps/demo-frontend/eslint.config.js +23 -0
  14. package/apps/demo-frontend/index.html +13 -0
  15. package/apps/demo-frontend/package.json +24 -0
  16. package/apps/demo-frontend/public/favicon.svg +1 -0
  17. package/apps/demo-frontend/public/icons.svg +24 -0
  18. package/apps/demo-frontend/src/App.tsx +33 -0
  19. package/apps/demo-frontend/src/assets/hero.png +0 -0
  20. package/apps/demo-frontend/src/assets/vite.svg +1 -0
  21. package/apps/demo-frontend/src/components/AccountSwitcher.tsx +373 -0
  22. package/apps/demo-frontend/src/components/ChangePasswordModal.tsx +128 -0
  23. package/apps/demo-frontend/src/index.css +272 -0
  24. package/apps/demo-frontend/src/main.tsx +10 -0
  25. package/apps/demo-frontend/src/pages/DashboardPage.tsx +141 -0
  26. package/apps/demo-frontend/src/pages/ForgotPasswordPage.tsx +183 -0
  27. package/apps/demo-frontend/src/pages/LoginPage.tsx +158 -0
  28. package/apps/demo-frontend/src/pages/OtpLoginPage.tsx +114 -0
  29. package/apps/demo-frontend/src/pages/SignupPage.tsx +95 -0
  30. package/apps/demo-frontend/src/pages/VerifyEmailPage.tsx +84 -0
  31. package/apps/demo-frontend/tsconfig.app.json +28 -0
  32. package/apps/demo-frontend/tsconfig.json +7 -0
  33. package/apps/demo-frontend/tsconfig.node.json +26 -0
  34. package/apps/demo-frontend/vite.config.ts +15 -0
  35. package/docs/DATABASE_MONGODB.md +280 -0
  36. package/docs/DATABASE_SQL.md +472 -0
  37. package/package.json +21 -0
  38. package/packages/api/package.json +30 -0
  39. package/packages/api/src/createSecurePool.ts +113 -0
  40. package/packages/api/src/index.ts +8 -0
  41. package/packages/api/src/middleware/authMiddleware.ts +26 -0
  42. package/packages/api/src/middleware/authorize.ts +24 -0
  43. package/packages/api/src/middleware/rateLimiter.ts +25 -0
  44. package/packages/api/src/middleware/tenantMiddleware.ts +12 -0
  45. package/packages/api/src/routes/authRoutes.ts +229 -0
  46. package/packages/api/src/routes/sessionRoutes.ts +30 -0
  47. package/packages/api/src/swagger.ts +529 -0
  48. package/packages/api/tsconfig.json +8 -0
  49. package/packages/application/package.json +16 -0
  50. package/packages/application/src/index.ts +17 -0
  51. package/packages/application/src/interfaces/IAuditLogRepository.ts +6 -0
  52. package/packages/application/src/interfaces/IAuthPlugin.ts +4 -0
  53. package/packages/application/src/interfaces/IEmailService.ts +3 -0
  54. package/packages/application/src/interfaces/IGoogleAuthService.ts +3 -0
  55. package/packages/application/src/interfaces/IOtpRepository.ts +8 -0
  56. package/packages/application/src/interfaces/IOtpService.ts +4 -0
  57. package/packages/application/src/interfaces/IPasswordHasher.ts +4 -0
  58. package/packages/application/src/interfaces/IRoleRepository.ts +8 -0
  59. package/packages/application/src/interfaces/ISessionRepository.ts +8 -0
  60. package/packages/application/src/interfaces/ITokenRepository.ts +9 -0
  61. package/packages/application/src/interfaces/ITokenService.ts +5 -0
  62. package/packages/application/src/interfaces/IUserRepository.ts +8 -0
  63. package/packages/application/src/services/AuthService.ts +323 -0
  64. package/packages/application/src/services/RefreshTokenService.ts +53 -0
  65. package/packages/application/tsconfig.json +8 -0
  66. package/packages/core/package.json +13 -0
  67. package/packages/core/src/entities/AuditLog.ts +11 -0
  68. package/packages/core/src/entities/OtpCode.ts +10 -0
  69. package/packages/core/src/entities/RefreshToken.ts +9 -0
  70. package/packages/core/src/entities/Role.ts +6 -0
  71. package/packages/core/src/entities/Session.ts +10 -0
  72. package/packages/core/src/entities/Tenant.ts +7 -0
  73. package/packages/core/src/entities/User.ts +10 -0
  74. package/packages/core/src/entities/UserRole.ts +6 -0
  75. package/packages/core/src/enums/index.ts +22 -0
  76. package/packages/core/src/index.ts +10 -0
  77. package/packages/core/tsconfig.json +8 -0
  78. package/packages/infrastructure/package.json +24 -0
  79. package/packages/infrastructure/src/email/NodemailerEmailService.ts +55 -0
  80. package/packages/infrastructure/src/google/GoogleAuthServiceImpl.ts +28 -0
  81. package/packages/infrastructure/src/hashing/BcryptHasher.ts +18 -0
  82. package/packages/infrastructure/src/index.ts +6 -0
  83. package/packages/infrastructure/src/jwt/JwtTokenService.ts +32 -0
  84. package/packages/infrastructure/src/otp/OtpServiceImpl.ts +50 -0
  85. package/packages/infrastructure/tsconfig.json +8 -0
  86. package/packages/persistence/package.json +22 -0
  87. package/packages/persistence/prisma/schema.prisma +88 -0
  88. package/packages/persistence/src/factory.ts +48 -0
  89. package/packages/persistence/src/index.ts +30 -0
  90. package/packages/persistence/src/mongo/connection.ts +9 -0
  91. package/packages/persistence/src/mongo/models/AuditLogModel.ts +21 -0
  92. package/packages/persistence/src/mongo/models/OtpModel.ts +19 -0
  93. package/packages/persistence/src/mongo/models/RefreshTokenModel.ts +17 -0
  94. package/packages/persistence/src/mongo/models/RoleModel.ts +11 -0
  95. package/packages/persistence/src/mongo/models/SessionModel.ts +19 -0
  96. package/packages/persistence/src/mongo/models/UserModel.ts +21 -0
  97. package/packages/persistence/src/mongo/models/UserRoleModel.ts +15 -0
  98. package/packages/persistence/src/mongo/repositories/MongoAuditLogRepository.ts +29 -0
  99. package/packages/persistence/src/mongo/repositories/MongoOtpRepository.ts +34 -0
  100. package/packages/persistence/src/mongo/repositories/MongoRoleRepository.ts +32 -0
  101. package/packages/persistence/src/mongo/repositories/MongoSessionRepository.ts +29 -0
  102. package/packages/persistence/src/mongo/repositories/MongoTokenRepository.ts +34 -0
  103. package/packages/persistence/src/mongo/repositories/MongoUserRepository.ts +37 -0
  104. package/packages/persistence/src/prisma/repositories/PrismaAuditLogRepository.ts +37 -0
  105. package/packages/persistence/src/prisma/repositories/PrismaOtpRepository.ts +43 -0
  106. package/packages/persistence/src/prisma/repositories/PrismaRoleRepository.ts +36 -0
  107. package/packages/persistence/src/prisma/repositories/PrismaSessionRepository.ts +39 -0
  108. package/packages/persistence/src/prisma/repositories/PrismaTokenRepository.ts +50 -0
  109. package/packages/persistence/src/prisma/repositories/PrismaUserRepository.ts +45 -0
  110. package/packages/persistence/tsconfig.json +8 -0
  111. package/packages/react-sdk/package.json +23 -0
  112. package/packages/react-sdk/src/components/GoogleLoginButton.tsx +54 -0
  113. package/packages/react-sdk/src/components/LoginForm.tsx +67 -0
  114. package/packages/react-sdk/src/components/OTPVerification.tsx +104 -0
  115. package/packages/react-sdk/src/components/SessionList.tsx +64 -0
  116. package/packages/react-sdk/src/components/SignupForm.tsx +95 -0
  117. package/packages/react-sdk/src/context/AuthContext.ts +4 -0
  118. package/packages/react-sdk/src/context/SecurePoolProvider.tsx +492 -0
  119. package/packages/react-sdk/src/hooks/useAuth.ts +11 -0
  120. package/packages/react-sdk/src/index.ts +22 -0
  121. package/packages/react-sdk/src/types.ts +53 -0
  122. package/packages/react-sdk/tsconfig.json +12 -0
  123. package/scripts/setup.js +285 -0
  124. package/scripts/setup.sh +309 -0
  125. package/tsconfig.base.json +16 -0
  126. package/turbo.json +16 -0
@@ -0,0 +1,158 @@
1
+ import { useState, useEffect, type FormEvent } from "react";
2
+ import { useAuth } from "@securepool/react-sdk";
3
+ import { useNavigate, Link } from "react-router-dom";
4
+
5
+ function getInitials(email: string): string {
6
+ return email.split("@")[0].slice(0, 2).toUpperCase();
7
+ }
8
+
9
+ function getAvatarUrl(email: string): string {
10
+ const initials = getInitials(email);
11
+ return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&size=80&background=3b82f6&color=fff&bold=true&format=svg`;
12
+ }
13
+
14
+ export default function LoginPage() {
15
+ const { login, accounts, switchAccount, isLoading, error, clearError } = useAuth();
16
+ const navigate = useNavigate();
17
+ const [email, setEmail] = useState("");
18
+
19
+ useEffect(() => { clearError(); }, [clearError]);
20
+ const [password, setPassword] = useState("");
21
+
22
+ const handleSubmit = async (e: FormEvent) => {
23
+ e.preventDefault();
24
+ try {
25
+ await login(email, password);
26
+ navigate("/dashboard");
27
+ } catch {
28
+ // error is set in context
29
+ }
30
+ };
31
+
32
+ const handleSwitchTo = (accountId: string) => {
33
+ switchAccount(accountId);
34
+ navigate("/dashboard");
35
+ window.location.reload();
36
+ };
37
+
38
+ return (
39
+ <div className="auth-layout">
40
+ <div className="auth-card">
41
+ <h1>Welcome back</h1>
42
+ <p className="subtitle">Sign in to your SecurePool account</p>
43
+
44
+ {/* Show existing logged-in accounts */}
45
+ {accounts.length > 0 && (
46
+ <>
47
+ <div style={{ marginBottom: 16 }}>
48
+ <div style={{ fontSize: 12, color: "#64748b", marginBottom: 8, textTransform: "uppercase", letterSpacing: 0.5 }}>
49
+ Switch to an existing account
50
+ </div>
51
+ {accounts.map((account) => (
52
+ <div
53
+ key={account.id}
54
+ onClick={() => handleSwitchTo(account.id)}
55
+ style={{
56
+ display: "flex",
57
+ alignItems: "center",
58
+ gap: 12,
59
+ padding: "10px 12px",
60
+ background: "rgba(59,130,246,0.06)",
61
+ border: "1px solid #334155",
62
+ borderRadius: 10,
63
+ cursor: "pointer",
64
+ marginBottom: 6,
65
+ transition: "all 0.15s",
66
+ }}
67
+ onMouseEnter={(e) => {
68
+ e.currentTarget.style.background = "rgba(59,130,246,0.15)";
69
+ e.currentTarget.style.borderColor = "#3b82f6";
70
+ }}
71
+ onMouseLeave={(e) => {
72
+ e.currentTarget.style.background = "rgba(59,130,246,0.06)";
73
+ e.currentTarget.style.borderColor = "#334155";
74
+ }}
75
+ >
76
+ <img
77
+ src={getAvatarUrl(account.email)}
78
+ alt=""
79
+ style={{ width: 36, height: 36, borderRadius: "50%", flexShrink: 0 }}
80
+ />
81
+ <div style={{ flex: 1, minWidth: 0 }}>
82
+ <div style={{
83
+ fontSize: 14,
84
+ color: "#e2e8f0",
85
+ fontWeight: 500,
86
+ overflow: "hidden",
87
+ textOverflow: "ellipsis",
88
+ whiteSpace: "nowrap",
89
+ }}>
90
+ {account.email.trim().toLowerCase()}
91
+ </div>
92
+ <div style={{ fontSize: 11, color: "#64748b" }}>Click to open dashboard</div>
93
+ </div>
94
+ <span style={{
95
+ fontSize: 18,
96
+ color: "#3b82f6",
97
+ }}>
98
+
99
+ </span>
100
+ </div>
101
+ ))}
102
+ </div>
103
+ <div className="divider"><span>or sign in with another account</span></div>
104
+ </>
105
+ )}
106
+
107
+ {error && <div className="error-msg">{error}</div>}
108
+
109
+ <form onSubmit={handleSubmit}>
110
+ <div className="form-group">
111
+ <label htmlFor="email">Email</label>
112
+ <input
113
+ id="email"
114
+ type="email"
115
+ value={email}
116
+ onChange={(e) => setEmail(e.target.value)}
117
+ placeholder="you@example.com"
118
+ required
119
+ disabled={isLoading}
120
+ />
121
+ </div>
122
+ <div className="form-group">
123
+ <label htmlFor="password">Password</label>
124
+ <input
125
+ id="password"
126
+ type="password"
127
+ value={password}
128
+ onChange={(e) => setPassword(e.target.value)}
129
+ placeholder="Enter your password"
130
+ required
131
+ disabled={isLoading}
132
+ />
133
+ </div>
134
+ <div style={{ textAlign: "right", marginBottom: 8 }}>
135
+ <Link to="/forgot-password" style={{ fontSize: 13, color: "#60a5fa" }}>
136
+ Forgot password?
137
+ </Link>
138
+ </div>
139
+ <button type="submit" className="btn btn-primary" disabled={isLoading}>
140
+ {isLoading ? "Signing in..." : "Sign In"}
141
+ </button>
142
+ </form>
143
+
144
+ <div className="divider"><span>or</span></div>
145
+
146
+ <Link to="/otp-login">
147
+ <button type="button" className="btn btn-outline">
148
+ Sign in with OTP
149
+ </button>
150
+ </Link>
151
+
152
+ <div className="auth-links">
153
+ Don't have an account? <Link to="/signup">Sign up</Link>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ );
158
+ }
@@ -0,0 +1,114 @@
1
+ import { useState, useEffect, type FormEvent } from "react";
2
+ import { useAuth } from "@securepool/react-sdk";
3
+ import { useNavigate, Link } from "react-router-dom";
4
+
5
+ export default function OtpLoginPage() {
6
+ const { requestOtp, verifyOtp, isLoading, error, clearError } = useAuth();
7
+ const navigate = useNavigate();
8
+
9
+ useEffect(() => { clearError(); }, [clearError]);
10
+ const [email, setEmail] = useState("");
11
+ const [code, setCode] = useState("");
12
+ const [otpSent, setOtpSent] = useState(false);
13
+
14
+ const handleRequestOtp = async (e: FormEvent) => {
15
+ e.preventDefault();
16
+ try {
17
+ await requestOtp(email);
18
+ setOtpSent(true);
19
+ } catch {
20
+ // error set in context
21
+ }
22
+ };
23
+
24
+ const handleVerifyOtp = async (e: FormEvent) => {
25
+ e.preventDefault();
26
+ try {
27
+ await verifyOtp(email, code);
28
+ navigate("/dashboard");
29
+ } catch {
30
+ // error set in context
31
+ }
32
+ };
33
+
34
+ if (!otpSent) {
35
+ return (
36
+ <div className="auth-layout">
37
+ <div className="auth-card">
38
+ <h1>OTP Login</h1>
39
+ <p className="subtitle">We'll send a 6-digit code to your email</p>
40
+
41
+ {error && <div className="error-msg">{error}</div>}
42
+
43
+ <form onSubmit={handleRequestOtp}>
44
+ <div className="form-group">
45
+ <label htmlFor="email">Email</label>
46
+ <input
47
+ id="email"
48
+ type="email"
49
+ value={email}
50
+ onChange={(e) => setEmail(e.target.value)}
51
+ placeholder="you@example.com"
52
+ required
53
+ disabled={isLoading}
54
+ />
55
+ </div>
56
+ <button type="submit" className="btn btn-primary" disabled={isLoading}>
57
+ {isLoading ? "Sending..." : "Send OTP"}
58
+ </button>
59
+ </form>
60
+
61
+ <div className="auth-links">
62
+ <Link to="/login">Back to password login</Link>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ return (
70
+ <div className="auth-layout">
71
+ <div className="auth-card">
72
+ <h1>Enter OTP</h1>
73
+ <p className="subtitle">
74
+ Code sent to <strong>{email}</strong>. Check your inbox.
75
+ </p>
76
+
77
+ {error && <div className="error-msg">{error}</div>}
78
+
79
+ <form onSubmit={handleVerifyOtp}>
80
+ <div className="form-group">
81
+ <label htmlFor="otp">6-digit Code</label>
82
+ <input
83
+ id="otp"
84
+ type="text"
85
+ className="otp-input"
86
+ value={code}
87
+ onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
88
+ placeholder="000000"
89
+ maxLength={6}
90
+ required
91
+ disabled={isLoading}
92
+ />
93
+ </div>
94
+ <button type="submit" className="btn btn-primary" disabled={isLoading || code.length !== 6}>
95
+ {isLoading ? "Verifying..." : "Verify & Sign In"}
96
+ </button>
97
+ </form>
98
+
99
+ <button
100
+ type="button"
101
+ className="btn btn-outline"
102
+ style={{ marginTop: 12 }}
103
+ onClick={() => {
104
+ setOtpSent(false);
105
+ setCode("");
106
+ }}
107
+ disabled={isLoading}
108
+ >
109
+ Use different email
110
+ </button>
111
+ </div>
112
+ </div>
113
+ );
114
+ }
@@ -0,0 +1,95 @@
1
+ import { useState, useEffect, type FormEvent } from "react";
2
+ import { useAuth } from "@securepool/react-sdk";
3
+ import { useNavigate, Link } from "react-router-dom";
4
+
5
+ export default function SignupPage() {
6
+ const { register, isLoading, error, clearError } = useAuth();
7
+ const navigate = useNavigate();
8
+
9
+ useEffect(() => { clearError(); }, [clearError]);
10
+ const [email, setEmail] = useState("");
11
+ const [password, setPassword] = useState("");
12
+ const [confirmPassword, setConfirmPassword] = useState("");
13
+ const [localError, setLocalError] = useState<string | null>(null);
14
+
15
+ const handleSubmit = async (e: FormEvent) => {
16
+ e.preventDefault();
17
+ setLocalError(null);
18
+
19
+ if (password !== confirmPassword) {
20
+ setLocalError("Passwords do not match");
21
+ return;
22
+ }
23
+ if (password.length < 8) {
24
+ setLocalError("Password must be at least 8 characters");
25
+ return;
26
+ }
27
+
28
+ try {
29
+ await register(email, password);
30
+ // Redirect to verify email page with the email
31
+ navigate("/verify-email", { state: { email } });
32
+ } catch {
33
+ // error in context
34
+ }
35
+ };
36
+
37
+ const displayError = localError || error;
38
+
39
+ return (
40
+ <div className="auth-layout">
41
+ <div className="auth-card">
42
+ <h1>Create account</h1>
43
+ <p className="subtitle">Get started with SecurePool</p>
44
+
45
+ {displayError && <div className="error-msg">{displayError}</div>}
46
+
47
+ <form onSubmit={handleSubmit}>
48
+ <div className="form-group">
49
+ <label htmlFor="email">Email</label>
50
+ <input
51
+ id="email"
52
+ type="email"
53
+ value={email}
54
+ onChange={(e) => setEmail(e.target.value)}
55
+ placeholder="you@example.com"
56
+ required
57
+ disabled={isLoading}
58
+ />
59
+ </div>
60
+ <div className="form-group">
61
+ <label htmlFor="password">Password</label>
62
+ <input
63
+ id="password"
64
+ type="password"
65
+ value={password}
66
+ onChange={(e) => setPassword(e.target.value)}
67
+ placeholder="Min 8 characters"
68
+ required
69
+ disabled={isLoading}
70
+ />
71
+ </div>
72
+ <div className="form-group">
73
+ <label htmlFor="confirm">Confirm Password</label>
74
+ <input
75
+ id="confirm"
76
+ type="password"
77
+ value={confirmPassword}
78
+ onChange={(e) => setConfirmPassword(e.target.value)}
79
+ placeholder="Re-enter your password"
80
+ required
81
+ disabled={isLoading}
82
+ />
83
+ </div>
84
+ <button type="submit" className="btn btn-primary" disabled={isLoading}>
85
+ {isLoading ? "Creating account..." : "Sign Up"}
86
+ </button>
87
+ </form>
88
+
89
+ <div className="auth-links">
90
+ Already have an account? <Link to="/login">Sign in</Link>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ );
95
+ }
@@ -0,0 +1,84 @@
1
+ import { useState, useEffect, type FormEvent } from "react";
2
+ import { useAuth } from "@securepool/react-sdk";
3
+ import { useNavigate, useLocation, Link } from "react-router-dom";
4
+
5
+ export default function VerifyEmailPage() {
6
+ const { verifyEmail, requestOtp, isLoading, error, clearError } = useAuth();
7
+ const navigate = useNavigate();
8
+
9
+ useEffect(() => { clearError(); }, [clearError]);
10
+ const location = useLocation();
11
+ const passedEmail = (location.state as any)?.email || "";
12
+
13
+ const [email] = useState(passedEmail);
14
+ const [code, setCode] = useState("");
15
+ const [resent, setResent] = useState(false);
16
+
17
+ const handleVerify = async (e: FormEvent) => {
18
+ e.preventDefault();
19
+ try {
20
+ await verifyEmail(email, code);
21
+ navigate("/dashboard");
22
+ } catch {
23
+ // error in context
24
+ }
25
+ };
26
+
27
+ const handleResend = async () => {
28
+ try {
29
+ await requestOtp(email);
30
+ setResent(true);
31
+ setTimeout(() => setResent(false), 3000);
32
+ } catch {
33
+ // error in context
34
+ }
35
+ };
36
+
37
+ return (
38
+ <div className="auth-layout">
39
+ <div className="auth-card">
40
+ <h1>Verify your email</h1>
41
+ <p className="subtitle">
42
+ We sent a 6-digit code to <strong>{email}</strong>
43
+ </p>
44
+
45
+ {resent && <div className="success-msg">OTP resent to your email</div>}
46
+ {error && <div className="error-msg">{error}</div>}
47
+
48
+ <form onSubmit={handleVerify}>
49
+ <div className="form-group">
50
+ <label htmlFor="otp">Verification Code</label>
51
+ <input
52
+ id="otp"
53
+ type="text"
54
+ className="otp-input"
55
+ value={code}
56
+ onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
57
+ placeholder="000000"
58
+ maxLength={6}
59
+ required
60
+ disabled={isLoading}
61
+ />
62
+ </div>
63
+ <button type="submit" className="btn btn-primary" disabled={isLoading || code.length !== 6}>
64
+ {isLoading ? "Verifying..." : "Verify & Continue"}
65
+ </button>
66
+ </form>
67
+
68
+ <button
69
+ type="button"
70
+ className="btn btn-outline"
71
+ style={{ marginTop: 12 }}
72
+ onClick={handleResend}
73
+ disabled={isLoading}
74
+ >
75
+ Resend OTP
76
+ </button>
77
+
78
+ <div className="auth-links">
79
+ <Link to="/login">Back to login</Link>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client"],
9
+ "skipLibCheck": true,
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "moduleDetection": "force",
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+
19
+ /* Linting */
20
+ "strict": true,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "erasableSyntaxOnly": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedSideEffectImports": true
26
+ },
27
+ "include": ["src"]
28
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["vite.config.ts"]
26
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ // https://vite.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ resolve: {
9
+ dedupe: ['react', 'react-dom'],
10
+ alias: {
11
+ react: path.resolve(__dirname, 'node_modules/react'),
12
+ 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
13
+ },
14
+ },
15
+ })