@casperid/react 1.0.0 → 1.1.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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/_commonjsHelpers-DKOUU3wS.cjs +2 -0
  3. package/dist/_commonjsHelpers-DKOUU3wS.cjs.map +1 -0
  4. package/dist/_commonjsHelpers-DaMA6jEr.js +9 -0
  5. package/dist/_commonjsHelpers-DaMA6jEr.js.map +1 -0
  6. package/dist/camera_utils-BQaOSBPu.js +397 -0
  7. package/dist/camera_utils-BQaOSBPu.js.map +1 -0
  8. package/dist/camera_utils-wchtqrQn.cjs +2 -0
  9. package/dist/camera_utils-wchtqrQn.cjs.map +1 -0
  10. package/dist/face_mesh-DYMPc5Ce.js +4212 -0
  11. package/dist/face_mesh-DYMPc5Ce.js.map +1 -0
  12. package/dist/face_mesh-fEvyDoPt.cjs +19 -0
  13. package/dist/face_mesh-fEvyDoPt.cjs.map +1 -0
  14. package/dist/index.cjs +41 -41
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.mjs +1786 -1501
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/style.css +1 -1
  19. package/package.json +23 -10
  20. package/src/CasperIDModal.tsx +0 -777
  21. package/src/index.css +0 -217
  22. package/src/index.ts +0 -41
  23. package/src/screens/AuthSelection.tsx +0 -221
  24. package/src/screens/DocumentScan.tsx +0 -348
  25. package/src/screens/FaceScan.tsx +0 -368
  26. package/src/screens/IdentityVerified.tsx +0 -51
  27. package/src/screens/L1Setup.tsx +0 -335
  28. package/src/screens/Login.tsx +0 -186
  29. package/src/screens/MintingIdentity.tsx +0 -446
  30. package/src/screens/PasskeyAuth.tsx +0 -259
  31. package/src/screens/PasskeyRegister.tsx +0 -281
  32. package/src/screens/PermissionsRequest.tsx +0 -96
  33. package/src/screens/PinVerification.tsx +0 -321
  34. package/src/screens/ReviewData.tsx +0 -315
  35. package/src/screens/SecurityUpgrade.tsx +0 -83
  36. package/src/screens/VerifyIdentityChoice.tsx +0 -59
  37. package/src/shared/Footer.tsx +0 -13
  38. package/src/shared/GlassContainer.tsx +0 -17
  39. package/src/shared/Header.tsx +0 -62
  40. package/src/types.ts +0 -342
  41. package/src/utils/fetchWithTimeout.ts +0 -52
  42. package/src/utils/i18n.ts +0 -640
  43. package/src/utils/webauthn.ts +0 -261
@@ -1,335 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import { motion } from 'framer-motion';
3
- import { Wallet, Fingerprint, CheckCircle2, Loader2, AlertCircle, RefreshCw } from 'lucide-react';
4
- import { t } from '../utils/i18n';
5
- import type { WalletGenerateResponse, DIDMintResponse } from '../types';
6
-
7
- interface L1SetupProps {
8
- sessionToken: string;
9
- onSuccess: (wallet: string, didAddress: string) => void;
10
- onError?: (error: string) => void;
11
- apiBaseUrl?: string;
12
- previewMode?: boolean;
13
- language?: string;
14
- }
15
-
16
- type SetupStep = 'wallet' | 'did' | 'complete' | 'error';
17
-
18
- const L1Setup: React.FC<L1SetupProps> = ({
19
- sessionToken,
20
- onSuccess,
21
- onError,
22
- apiBaseUrl = 'https://apis.casperid.com',
23
- previewMode = false,
24
- language = 'EN'
25
- }) => {
26
- const [currentStep, setCurrentStep] = useState<SetupStep>('wallet');
27
- const [progress, setProgress] = useState(0);
28
- const [error, setError] = useState('');
29
- const [wallet, setWallet] = useState<string | null>(null);
30
- const [didAddress, setDidAddress] = useState<string | null>(null);
31
-
32
- // Prevent double execution in React StrictMode
33
- const hasStartedRef = useRef(false);
34
-
35
- useEffect(() => {
36
- if (hasStartedRef.current) return;
37
- hasStartedRef.current = true;
38
- runSetup();
39
- }, []);
40
-
41
- const runSetup = async () => {
42
- setError('');
43
- setCurrentStep('wallet');
44
- setProgress(0);
45
-
46
- if (previewMode) {
47
- // Mock the setup process
48
- await mockSetup();
49
- return;
50
- }
51
-
52
- try {
53
- // Step 1: Generate wallet (0-50%)
54
- setProgress(10);
55
- const walletResult = await generateWallet();
56
- setWallet(walletResult);
57
- setProgress(50);
58
-
59
- // Step 2: Mint DID (50-100%)
60
- setCurrentStep('did');
61
- setProgress(60);
62
- const didResult = await mintDID();
63
- setDidAddress(didResult);
64
- setProgress(100);
65
-
66
- // Complete
67
- setCurrentStep('complete');
68
- setTimeout(() => {
69
- onSuccess(walletResult, didResult);
70
- }, 1500);
71
- } catch (err) {
72
- const message = err instanceof Error ? err.message : 'Setup failed';
73
- setError(message);
74
- setCurrentStep('error');
75
- onError?.(message);
76
- }
77
- };
78
-
79
- const mockSetup = async () => {
80
- // Wallet generation
81
- setProgress(10);
82
- await sleep(800);
83
- setProgress(30);
84
- await sleep(500);
85
- setProgress(50);
86
- setWallet('0x1234...5678');
87
-
88
- // DID minting
89
- setCurrentStep('did');
90
- setProgress(60);
91
- await sleep(800);
92
- setProgress(80);
93
- await sleep(500);
94
- setProgress(100);
95
- setDidAddress('did:casper:abc123');
96
-
97
- // Complete
98
- setCurrentStep('complete');
99
- setTimeout(() => {
100
- onSuccess('0x1234...5678', 'did:casper:abc123');
101
- }, 1500);
102
- };
103
-
104
- const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
105
-
106
- const generateWallet = async (): Promise<string> => {
107
- const response = await fetch(`${apiBaseUrl}/api/passkey/wallet/generate`, {
108
- method: 'POST',
109
- headers: {
110
- 'Content-Type': 'application/json',
111
- 'Authorization': `Bearer ${sessionToken}`
112
- },
113
- credentials: 'include'
114
- });
115
-
116
- if (!response.ok) {
117
- const errorData = await response.json().catch(() => ({}));
118
- throw new Error(errorData.error || 'Failed to generate wallet');
119
- }
120
-
121
- const data: WalletGenerateResponse = await response.json();
122
- if (!data.success || !data.walletInfo?.walletAddress) {
123
- throw new Error(data.error || 'Wallet generation failed');
124
- }
125
-
126
- return data.walletInfo.walletAddress;
127
- };
128
-
129
- const mintDID = async (): Promise<string> => {
130
- const response = await fetch(`${apiBaseUrl}/api/passkey/did/mint`, {
131
- method: 'POST',
132
- headers: {
133
- 'Content-Type': 'application/json',
134
- 'Authorization': `Bearer ${sessionToken}`
135
- },
136
- credentials: 'include'
137
- });
138
-
139
- if (!response.ok) {
140
- const errorData = await response.json().catch(() => ({}));
141
- throw new Error(errorData.error || 'Failed to mint DID');
142
- }
143
-
144
- const data: DIDMintResponse = await response.json();
145
- if (!data.success || !data.did_address) {
146
- throw new Error(data.error || 'DID minting failed');
147
- }
148
-
149
- return data.did_address;
150
- };
151
-
152
- const getStepIcon = (step: SetupStep) => {
153
- switch (step) {
154
- case 'wallet':
155
- return <Wallet className="w-8 h-8" />;
156
- case 'did':
157
- return <Fingerprint className="w-8 h-8" />;
158
- case 'complete':
159
- return <CheckCircle2 className="w-8 h-8" />;
160
- case 'error':
161
- return <AlertCircle className="w-8 h-8" />;
162
- }
163
- };
164
-
165
- const getStepText = () => {
166
- switch (currentStep) {
167
- case 'wallet':
168
- return t('generating_wallet', language);
169
- case 'did':
170
- return t('minting_did', language);
171
- case 'complete':
172
- return t('l1_complete', language);
173
- case 'error':
174
- return t('l1_failed', language);
175
- }
176
- };
177
-
178
- const getStepColor = () => {
179
- switch (currentStep) {
180
- case 'complete':
181
- return 'text-green-400';
182
- case 'error':
183
- return 'text-red-400';
184
- default:
185
- return 'text-brand';
186
- }
187
- };
188
-
189
- const getBgColor = () => {
190
- switch (currentStep) {
191
- case 'complete':
192
- return 'bg-green-500/20 border-green-400';
193
- case 'error':
194
- return 'bg-red-500/10 border-red-400';
195
- default:
196
- return 'bg-brand/20 border-brand';
197
- }
198
- };
199
-
200
- return (
201
- <motion.div
202
- initial={{ opacity: 0 }}
203
- animate={{ opacity: 1 }}
204
- exit={{ opacity: 0 }}
205
- className="flex-1 flex flex-col"
206
- >
207
- {/* Header */}
208
- <div className="p-6 pb-2">
209
- <div className="flex justify-between items-center mb-4">
210
- <span className="text-xs font-semibold uppercase tracking-widest text-brand/80">
211
- {t('verification_step', language)}
212
- </span>
213
- <span className="text-xs font-bold text-muted">L1 {t('secure_sdk', language)}</span>
214
- </div>
215
- <div className="w-full bg-brand/10 h-1.5 rounded-full overflow-hidden">
216
- <motion.div
217
- className="bg-brand h-full rounded-full"
218
- initial={{ width: '0%' }}
219
- animate={{ width: `${progress}%` }}
220
- transition={{ duration: 0.5, ease: 'easeOut' }}
221
- />
222
- </div>
223
- </div>
224
-
225
- {/* Main content */}
226
- <div className="flex-1 flex flex-col items-center justify-center px-8 text-center gap-8">
227
- {/* Animated icon */}
228
- <motion.div
229
- animate={currentStep !== 'complete' && currentStep !== 'error' ? {
230
- scale: [1, 1.05, 1],
231
- opacity: [1, 0.8, 1]
232
- } : {}}
233
- transition={{
234
- duration: 2,
235
- repeat: currentStep !== 'complete' && currentStep !== 'error' ? Infinity : 0,
236
- ease: "easeInOut"
237
- }}
238
- className={`w-24 h-24 rounded-full border-2 flex items-center justify-center ${getBgColor()}`}
239
- >
240
- <span className={getStepColor()}>
241
- {currentStep !== 'complete' && currentStep !== 'error' ? (
242
- <Loader2 className="w-10 h-10 animate-spin" />
243
- ) : (
244
- getStepIcon(currentStep)
245
- )}
246
- </span>
247
- </motion.div>
248
-
249
- {/* Status text */}
250
- <div className="space-y-4">
251
- <h1 className="text-2xl font-bold tracking-tight text-main leading-tight">
252
- {t('l1_setup_title', language)}
253
- </h1>
254
- <p className="text-muted text-base leading-relaxed font-medium">
255
- {getStepText()}
256
- </p>
257
- </div>
258
-
259
- {/* Progress details */}
260
- <div className="w-full max-w-xs space-y-3">
261
- <div className={`flex items-center gap-3 p-3 rounded-xl ${
262
- currentStep === 'wallet' ? 'bg-brand/10' :
263
- progress >= 50 ? 'bg-green-500/10' : 'bg-slate-100/50 dark:bg-white/5'
264
- }`}>
265
- <div className={`w-8 h-8 rounded-full flex items-center justify-center ${
266
- progress >= 50 ? 'bg-green-500/20' :
267
- currentStep === 'wallet' ? 'bg-brand/20' : 'bg-slate-200 dark:bg-white/10'
268
- }`}>
269
- {progress >= 50 ? (
270
- <CheckCircle2 className="w-4 h-4 text-green-400" />
271
- ) : currentStep === 'wallet' ? (
272
- <Loader2 className="w-4 h-4 text-brand animate-spin" />
273
- ) : (
274
- <Wallet className="w-4 h-4 text-muted" />
275
- )}
276
- </div>
277
- <div className="flex-1 text-left">
278
- <p className={`text-sm font-medium ${progress >= 50 ? 'text-green-400' : 'text-main'}`}>
279
- {t('generating_wallet', language)}
280
- </p>
281
- {wallet && <p className="text-xs text-muted truncate">{wallet}</p>}
282
- </div>
283
- </div>
284
-
285
- <div className={`flex items-center gap-3 p-3 rounded-xl ${
286
- currentStep === 'did' ? 'bg-brand/10' :
287
- progress >= 100 ? 'bg-green-500/10' : 'bg-slate-100/50 dark:bg-white/5'
288
- }`}>
289
- <div className={`w-8 h-8 rounded-full flex items-center justify-center ${
290
- progress >= 100 ? 'bg-green-500/20' :
291
- currentStep === 'did' ? 'bg-brand/20' : 'bg-slate-200 dark:bg-white/10'
292
- }`}>
293
- {progress >= 100 ? (
294
- <CheckCircle2 className="w-4 h-4 text-green-400" />
295
- ) : currentStep === 'did' ? (
296
- <Loader2 className="w-4 h-4 text-brand animate-spin" />
297
- ) : (
298
- <Fingerprint className="w-4 h-4 text-muted" />
299
- )}
300
- </div>
301
- <div className="flex-1 text-left">
302
- <p className={`text-sm font-medium ${progress >= 100 ? 'text-green-400' : 'text-main'}`}>
303
- {t('minting_did', language)}
304
- </p>
305
- {didAddress && <p className="text-xs text-muted truncate">{didAddress}</p>}
306
- </div>
307
- </div>
308
- </div>
309
-
310
- {/* Error state retry button */}
311
- {currentStep === 'error' && (
312
- <div className="space-y-2">
313
- <p className="text-red-500 text-sm">{error}</p>
314
- <button
315
- onClick={runSetup}
316
- className="flex items-center gap-2 px-6 py-3 bg-brand hover:bg-brand/90 text-white font-bold rounded-xl transition-colors"
317
- >
318
- <RefreshCw className="w-4 h-4" />
319
- {t('try_again', language)}
320
- </button>
321
- </div>
322
- )}
323
- </div>
324
-
325
- {/* Footer */}
326
- <div className="p-8">
327
- <p className="text-center text-xs text-muted">
328
- {t('l1_setup_subtitle', language)}
329
- </p>
330
- </div>
331
- </motion.div>
332
- );
333
- };
334
-
335
- export default L1Setup;
@@ -1,186 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { motion } from 'framer-motion';
3
- import { Mail, Verified, Lock, ArrowRight, Loader2 } from 'lucide-react';
4
- import type { SendOTPResponse } from '../types';
5
- import { t } from '../utils/i18n';
6
-
7
- interface LoginProps {
8
- onNext: (email: string, verificationId: string) => void;
9
- onPasskeyUser?: (email: string) => void;
10
- onError?: (error: string) => void;
11
- apiBaseUrl?: string;
12
- previewMode?: boolean;
13
- language?: string;
14
- }
15
-
16
- const Login: React.FC<LoginProps> = ({
17
- onNext,
18
- onPasskeyUser,
19
- onError,
20
- apiBaseUrl = 'https://apis.casperid.com',
21
- previewMode = false,
22
- language = 'EN'
23
- }) => {
24
- const [email, setEmail] = useState('');
25
- const [isLoading, setIsLoading] = useState(false);
26
- const [error, setError] = useState('');
27
-
28
- const handleSubmit = async () => {
29
- if (!email.trim()) {
30
- setError(t('error_email_required', language));
31
- return;
32
- }
33
-
34
- // Basic email validation
35
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
36
- if (!emailRegex.test(email)) {
37
- setError(t('error_invalid_email', language));
38
- return;
39
- }
40
-
41
- setIsLoading(true);
42
- setError('');
43
-
44
- if (previewMode) {
45
- // Mock delay and success for preview
46
- setTimeout(() => {
47
- setIsLoading(false);
48
- onNext(email.trim().toLowerCase(), 'mock-v-id-123456789');
49
- }, 1000);
50
- return;
51
- }
52
-
53
- try {
54
- const response = await fetch(`${apiBaseUrl}/api/auth/send-otp`, {
55
- method: 'POST',
56
- headers: {
57
- 'Content-Type': 'application/json',
58
- },
59
- body: JSON.stringify({ email: email.trim().toLowerCase() }),
60
- });
61
-
62
- if (!response.ok) {
63
- const errorData = await response.json().catch(() => ({}));
64
- throw new Error(errorData.error || `Request failed with status ${response.status}`);
65
- }
66
-
67
- const data: SendOTPResponse = await response.json();
68
-
69
- if (data.success) {
70
- // Returning user with passkey - route to passkey auth
71
- if (data.userExists && data.hasPasskey) {
72
- if (onPasskeyUser) {
73
- onPasskeyUser(email.trim().toLowerCase());
74
- } else {
75
- // Fallback: show message if no handler provided
76
- setError(t('error_passkey_registered', language));
77
- }
78
- setIsLoading(false);
79
- return;
80
- }
81
-
82
- // New user or user without passkey - proceed to OTP
83
- if (data.verificationId) {
84
- onNext(email.trim().toLowerCase(), data.verificationId);
85
- } else {
86
- setError(t('error_send_code_failed', language));
87
- }
88
- } else {
89
- setError(data.error || t('error_send_code_failed', language));
90
- onError?.(data.error || t('error_send_code_failed', language));
91
- }
92
- } catch (err) {
93
- const errorMessage = t('error_connection_failed', language);
94
- setError(errorMessage);
95
- onError?.(errorMessage);
96
- } finally {
97
- setIsLoading(false);
98
- }
99
- };
100
-
101
- const handleKeyDown = (e: React.KeyboardEvent) => {
102
- if (e.key === 'Enter' && !isLoading) {
103
- handleSubmit();
104
- }
105
- };
106
-
107
- return (
108
- <motion.div
109
- initial={{ opacity: 0, x: 20 }}
110
- animate={{ opacity: 1, x: 0 }}
111
- exit={{ opacity: 0, x: -20 }}
112
- className="flex-1 flex flex-col px-8 pt-12"
113
- >
114
- <div className="mb-8">
115
- <h2 className="text-3xl font-extrabold text-main tracking-tight leading-tight">{t('welcome_back', language)}</h2>
116
- <p className="text-muted mt-3 text-sm leading-relaxed">
117
- {t('enter_email', language)}
118
- </p>
119
- </div>
120
-
121
- <div className="space-y-6">
122
- <div className="space-y-2">
123
- <label className="text-xs font-bold text-slate-500 uppercase tracking-widest ml-1">
124
- {t('email_address', language)}
125
- </label>
126
- <div className="relative">
127
- <input
128
- className={`w-full dark:bg-black/20 bg-slate-100/50 border ${error ? 'border-red-400 dark:border-red-500' : 'dark:border-white/10 border-slate-200'} rounded-2xl h-14 px-5 text-main placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-30 transition-all font-medium`}
129
- placeholder="name@example.com"
130
- type="email"
131
- value={email}
132
- onChange={(e) => {
133
- setEmail(e.target.value);
134
- if (error) setError('');
135
- }}
136
- onKeyDown={handleKeyDown}
137
- disabled={isLoading}
138
- autoComplete="email"
139
- autoFocus
140
- />
141
- <div className="absolute right-4 top-1/2 -translate-y-1/2 flex items-center text-slate-400">
142
- <Mail className="w-5 h-5" />
143
- </div>
144
- </div>
145
- {error && (
146
- <p className="text-red-500 text-xs font-medium ml-1 mt-1">{error}</p>
147
- )}
148
- </div>
149
- <button
150
- onClick={handleSubmit}
151
- disabled={isLoading}
152
- className="w-full h-14 bg-brand hover:bg-brand/90 text-white font-bold rounded-2xl shadow-xl shadow-brand flex items-center justify-center gap-2 transition-transform active:scale-[0.98] disabled:opacity-70 disabled:cursor-not-allowed"
153
- >
154
- {isLoading ? (
155
- <>
156
- <Loader2 className="w-5 h-5 animate-spin" />
157
- <span>{t('sending_code', language)}</span>
158
- </>
159
- ) : (
160
- <>
161
- <span>{t('continue', language)}</span>
162
- <ArrowRight className="w-5 h-5" />
163
- </>
164
- )}
165
- </button>
166
- </div>
167
-
168
- <div className="mt-auto pb-8">
169
- <div className="p-4 dark:bg-white/5 bg-slate-100/50 rounded-2xl mb-6">
170
- <div className="flex items-start gap-3">
171
- <Verified className="text-[#6DE8EC] w-5 h-5 mt-0.5 shrink-0" />
172
- <p className="text-[11px] text-muted leading-normal font-medium">
173
- {t('security_notice', language)}
174
- </p>
175
- </div>
176
- </div>
177
- <div className="flex items-center justify-center gap-2 opacity-50">
178
- <Lock className="text-muted w-3 h-3" />
179
- <span className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted">{t('e2e_encrypted', language)}</span>
180
- </div>
181
- </div>
182
- </motion.div>
183
- );
184
- };
185
-
186
- export default Login;