irismail 0.1.0 → 0.5.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.
- package/README.md +86 -111
- package/dist/chunk-64MVNASE.mjs +42 -0
- package/dist/chunk-IQG3OBQL.mjs +127 -0
- package/dist/index.d.mts +3 -10
- package/dist/index.d.ts +3 -10
- package/dist/index.js +112 -351
- package/dist/index.mjs +8 -18
- package/dist/react/index.d.mts +72 -51
- package/dist/react/index.d.ts +72 -51
- package/dist/react/index.js +85 -180
- package/dist/react/index.mjs +3 -3
- package/dist/server/index.d.mts +38 -98
- package/dist/server/index.d.ts +38 -98
- package/dist/server/index.js +25 -167
- package/dist/server/index.mjs +3 -11
- package/package.json +15 -6
- package/dist/chunk-FTNSOYOW.mjs +0 -223
- package/dist/chunk-XGASTZZ6.mjs +0 -180
package/dist/index.js
CHANGED
|
@@ -32,193 +32,54 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
32
32
|
// src/index.ts
|
|
33
33
|
var index_exports = {};
|
|
34
34
|
__export(index_exports, {
|
|
35
|
-
EmailService: () => EmailService,
|
|
36
35
|
InputOTP: () => InputOTP,
|
|
37
36
|
InputOTPGroup: () => InputOTPGroup,
|
|
38
37
|
InputOTPSeparator: () => InputOTPSeparator,
|
|
39
38
|
InputOTPSlot: () => InputOTPSlot,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
cn: () => cn,
|
|
44
|
-
decrypt: () => decrypt,
|
|
45
|
-
encrypt: () => encrypt,
|
|
46
|
-
useOTP: () => useOTP
|
|
39
|
+
IrisMail: () => IrisMail,
|
|
40
|
+
OTP: () => OTP,
|
|
41
|
+
cn: () => cn
|
|
47
42
|
});
|
|
48
43
|
module.exports = __toCommonJS(index_exports);
|
|
49
44
|
|
|
50
45
|
// src/server/email.ts
|
|
51
46
|
var import_nodemailer = __toESM(require("nodemailer"));
|
|
52
|
-
var
|
|
47
|
+
var IrisMail = class {
|
|
53
48
|
constructor(config) {
|
|
54
49
|
__publicField(this, "transporter");
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
async verifyConnection() {
|
|
63
|
-
try {
|
|
64
|
-
await this.transporter.verify();
|
|
65
|
-
return true;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error("SMTP connection verification failed:", error);
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
50
|
+
this.transporter = import_nodemailer.default.createTransport({
|
|
51
|
+
host: "smtp.gmail.com",
|
|
52
|
+
port: 587,
|
|
53
|
+
secure: false,
|
|
54
|
+
// true for 465, false for other ports
|
|
55
|
+
auth: config.auth
|
|
56
|
+
});
|
|
70
57
|
}
|
|
71
58
|
/**
|
|
72
59
|
* Send an email
|
|
73
|
-
* @param options - Email options (to, subject, html
|
|
74
|
-
* @returns
|
|
60
|
+
* @param options - Email options (from, to, subject, html)
|
|
61
|
+
* @returns Promise with success status and messageId
|
|
75
62
|
*/
|
|
76
|
-
async
|
|
77
|
-
const from = options
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
text: options.text || options.html.replace(/<[^>]*>/g, "")
|
|
90
|
-
// Simple HTML to text fallback
|
|
63
|
+
async sendMail(options) {
|
|
64
|
+
const { from, to, subject, html } = options;
|
|
65
|
+
const text = html.replace(/<[^>]*>/g, "");
|
|
66
|
+
const info = await this.transporter.sendMail({
|
|
67
|
+
from,
|
|
68
|
+
to,
|
|
69
|
+
subject,
|
|
70
|
+
html,
|
|
71
|
+
text
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
messageId: info.messageId
|
|
91
76
|
};
|
|
92
|
-
try {
|
|
93
|
-
const info = await this.transporter.sendMail(mailOptions);
|
|
94
|
-
return {
|
|
95
|
-
success: true,
|
|
96
|
-
messageId: info.messageId
|
|
97
|
-
};
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error("Error sending email:", error);
|
|
100
|
-
throw error;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// src/server/crypto.ts
|
|
106
|
-
var import_crypto = __toESM(require("crypto"));
|
|
107
|
-
var ALGORITHM = "aes-256-cbc";
|
|
108
|
-
function encrypt(text, secretKey) {
|
|
109
|
-
try {
|
|
110
|
-
const key = Buffer.from(secretKey.padEnd(32).slice(0, 32));
|
|
111
|
-
const iv = import_crypto.default.randomBytes(16);
|
|
112
|
-
const cipher = import_crypto.default.createCipheriv(ALGORITHM, key, iv);
|
|
113
|
-
let encrypted = cipher.update(text, "utf8", "base64");
|
|
114
|
-
encrypted += cipher.final("base64");
|
|
115
|
-
return iv.toString("hex") + ":" + encrypted;
|
|
116
|
-
} catch (error) {
|
|
117
|
-
console.error("Encryption error:", error);
|
|
118
|
-
throw new Error("Failed to encrypt data");
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function decrypt(encryptedText, secretKey) {
|
|
122
|
-
try {
|
|
123
|
-
const key = Buffer.from(secretKey.padEnd(32).slice(0, 32));
|
|
124
|
-
const parts = encryptedText.split(":");
|
|
125
|
-
if (parts.length !== 2) {
|
|
126
|
-
throw new Error("Invalid encrypted text format");
|
|
127
|
-
}
|
|
128
|
-
const iv = Buffer.from(parts[0], "hex");
|
|
129
|
-
const encrypted = parts[1];
|
|
130
|
-
const decipher = import_crypto.default.createDecipheriv(ALGORITHM, key, iv);
|
|
131
|
-
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
132
|
-
decrypted += decipher.final("utf8");
|
|
133
|
-
return decrypted;
|
|
134
|
-
} catch (error) {
|
|
135
|
-
console.error("Decryption error:", error);
|
|
136
|
-
throw new Error("Failed to decrypt data");
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/server/otp.ts
|
|
141
|
-
var OTPService = class {
|
|
142
|
-
constructor(secretKey) {
|
|
143
|
-
__publicField(this, "secretKey");
|
|
144
|
-
if (!secretKey) {
|
|
145
|
-
throw new Error("OTPService: secretKey is required");
|
|
146
|
-
}
|
|
147
|
-
this.secretKey = secretKey;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Generate a numeric OTP of specified length
|
|
151
|
-
* @param length - Length of the OTP (default: 6)
|
|
152
|
-
* @returns - The generated OTP string
|
|
153
|
-
*/
|
|
154
|
-
generateOTP(length = 6) {
|
|
155
|
-
const min = Math.pow(10, length - 1);
|
|
156
|
-
const max = Math.pow(10, length) - 1;
|
|
157
|
-
return Math.floor(min + Math.random() * (max - min + 1)).toString();
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Encrypt OTP data for secure storage/transmission
|
|
161
|
-
* @param otp - The OTP string
|
|
162
|
-
* @param timestamp - The timestamp when OTP was generated (default: now)
|
|
163
|
-
* @returns - Encrypted OTP data string
|
|
164
|
-
*/
|
|
165
|
-
encryptOTP(otp, timestamp = Date.now()) {
|
|
166
|
-
const data = JSON.stringify({ otp, timestamp });
|
|
167
|
-
return encrypt(data, this.secretKey);
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Decrypt OTP data
|
|
171
|
-
* @param encryptedData - The encrypted OTP string
|
|
172
|
-
* @returns - Decrypted OTP data object or null if failed
|
|
173
|
-
*/
|
|
174
|
-
decryptOTP(encryptedData) {
|
|
175
|
-
try {
|
|
176
|
-
const decryptedData = decrypt(encryptedData, this.secretKey);
|
|
177
|
-
return JSON.parse(decryptedData);
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error("Error decrypting OTP data:", error);
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Verify an OTP against encrypted data
|
|
185
|
-
* @param inputOtp - The OTP provided by the user
|
|
186
|
-
* @param encryptedData - The encrypted OTP data
|
|
187
|
-
* @param expiryMinutes - Expiry time in minutes (default: 5)
|
|
188
|
-
* @returns - Object containing valid status and message
|
|
189
|
-
*/
|
|
190
|
-
verifyOTP(inputOtp, encryptedData, expiryMinutes = 5) {
|
|
191
|
-
const data = this.decryptOTP(encryptedData);
|
|
192
|
-
if (!data) {
|
|
193
|
-
return { valid: false, message: "Invalid OTP data" };
|
|
194
|
-
}
|
|
195
|
-
const { otp, timestamp } = data;
|
|
196
|
-
const currentTime = Date.now();
|
|
197
|
-
const otpAge = currentTime - timestamp;
|
|
198
|
-
const expiryTime = expiryMinutes * 60 * 1e3;
|
|
199
|
-
if (otpAge > expiryTime) {
|
|
200
|
-
return { valid: false, message: "OTP has expired" };
|
|
201
|
-
}
|
|
202
|
-
if (otp !== inputOtp) {
|
|
203
|
-
return { valid: false, message: "Incorrect OTP" };
|
|
204
|
-
}
|
|
205
|
-
return { valid: true, message: "OTP verified successfully" };
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// src/server/index.ts
|
|
210
|
-
var IrisMailService = class {
|
|
211
|
-
constructor(config) {
|
|
212
|
-
__publicField(this, "email");
|
|
213
|
-
__publicField(this, "otp");
|
|
214
|
-
this.email = new EmailService(config.email);
|
|
215
|
-
this.otp = new OTPService(config.secretKey);
|
|
216
77
|
}
|
|
217
78
|
};
|
|
218
79
|
|
|
219
80
|
// src/react/components/input-otp.tsx
|
|
220
|
-
var React = __toESM(require("react"));
|
|
221
81
|
var import_input_otp = require("input-otp");
|
|
82
|
+
var React = __toESM(require("react"));
|
|
222
83
|
|
|
223
84
|
// src/utils/constants.ts
|
|
224
85
|
var import_clsx = require("clsx");
|
|
@@ -226,221 +87,121 @@ var import_tailwind_merge = require("tailwind-merge");
|
|
|
226
87
|
function cn(...inputs) {
|
|
227
88
|
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
228
89
|
}
|
|
229
|
-
var OTP_DEFAULTS = {
|
|
230
|
-
LENGTH: 6,
|
|
231
|
-
EXPIRY_MINUTES: 5,
|
|
232
|
-
RESEND_COOLDOWN_SECONDS: 120,
|
|
233
|
-
// 2 minutes
|
|
234
|
-
MAX_ATTEMPTS: 5,
|
|
235
|
-
RATE_LIMIT_RESET_HOURS: 1
|
|
236
|
-
};
|
|
237
90
|
|
|
238
91
|
// src/react/components/input-otp.tsx
|
|
239
92
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
240
|
-
var
|
|
241
|
-
|
|
93
|
+
var InputOTPStyleContext = React.createContext({});
|
|
94
|
+
var InputOTP = React.forwardRef(({ className, containerClassName, error, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputOTPStyleContext.Provider, { value: { error }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
95
|
+
import_input_otp.OTPInput,
|
|
242
96
|
{
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
className: "
|
|
249
|
-
|
|
250
|
-
"path",
|
|
251
|
-
{
|
|
252
|
-
d: "M7.5 7.5C7.5 8.32843 6.82843 9 6 9C5.17157 9 4.5 8.32843 4.5 7.5C4.5 6.67157 5.17157 6 6 6C6.82843 6 7.5 6.67157 7.5 7.5Z",
|
|
253
|
-
fill: "currentColor",
|
|
254
|
-
fillRule: "evenodd",
|
|
255
|
-
clipRule: "evenodd"
|
|
256
|
-
}
|
|
257
|
-
)
|
|
97
|
+
ref,
|
|
98
|
+
containerClassName: cn(
|
|
99
|
+
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
100
|
+
containerClassName
|
|
101
|
+
),
|
|
102
|
+
className: cn("disabled:cursor-not-allowed", className),
|
|
103
|
+
...props
|
|
258
104
|
}
|
|
259
|
-
);
|
|
260
|
-
var InputOTP = React.forwardRef(
|
|
261
|
-
({ className, containerClassName, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
262
|
-
import_input_otp.OTPInput,
|
|
263
|
-
{
|
|
264
|
-
ref,
|
|
265
|
-
containerClassName: cn("flex items-center gap-2 has-[:disabled]:opacity-50", containerClassName),
|
|
266
|
-
className: cn("disabled:cursor-not-allowed", className),
|
|
267
|
-
...props
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
);
|
|
105
|
+
) }));
|
|
271
106
|
InputOTP.displayName = "InputOTP";
|
|
272
|
-
var InputOTPGroup = React.forwardRef(
|
|
273
|
-
|
|
274
|
-
|
|
107
|
+
var InputOTPGroup = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
ref,
|
|
111
|
+
className: cn("flex items-center gap-2", className),
|
|
112
|
+
...props
|
|
113
|
+
}
|
|
114
|
+
));
|
|
275
115
|
InputOTPGroup.displayName = "InputOTPGroup";
|
|
276
116
|
var InputOTPSlot = React.forwardRef(({ index, className, ...props }, ref) => {
|
|
277
117
|
const inputOTPContext = React.useContext(import_input_otp.OTPInputContext);
|
|
278
|
-
const {
|
|
118
|
+
const { error } = React.useContext(InputOTPStyleContext);
|
|
119
|
+
const slot = inputOTPContext.slots[index];
|
|
120
|
+
const { char, hasFakeCaret, isActive } = slot;
|
|
279
121
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
280
122
|
"div",
|
|
281
123
|
{
|
|
282
124
|
ref,
|
|
283
125
|
className: cn(
|
|
284
|
-
|
|
285
|
-
|
|
126
|
+
// Base styles
|
|
127
|
+
"relative flex h-12 w-10 items-center justify-center",
|
|
128
|
+
"border-2 rounded-lg",
|
|
129
|
+
"font-mono text-lg font-medium",
|
|
130
|
+
"transition-all duration-150",
|
|
131
|
+
// Default state
|
|
132
|
+
"border-zinc-700 bg-zinc-900 text-white",
|
|
133
|
+
// Filled state
|
|
134
|
+
char && "border-zinc-500",
|
|
135
|
+
// Active/focus state
|
|
136
|
+
isActive && "ring-2 ring-offset-2 ring-offset-zinc-950 ring-indigo-500 border-indigo-500",
|
|
137
|
+
// Error state
|
|
138
|
+
error && "border-red-500/70 bg-red-500/10",
|
|
139
|
+
error && isActive && "ring-red-500",
|
|
286
140
|
className
|
|
287
141
|
),
|
|
142
|
+
"data-active": isActive,
|
|
143
|
+
"data-filled": Boolean(char),
|
|
288
144
|
...props,
|
|
289
145
|
children: [
|
|
290
146
|
char,
|
|
291
|
-
hasFakeCaret && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-
|
|
147
|
+
hasFakeCaret && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-5 w-0.5 animate-caret-blink rounded-full bg-indigo-400" }) })
|
|
292
148
|
]
|
|
293
149
|
}
|
|
294
150
|
);
|
|
295
151
|
});
|
|
296
152
|
InputOTPSlot.displayName = "InputOTPSlot";
|
|
297
|
-
var InputOTPSeparator = React.forwardRef(
|
|
298
|
-
|
|
299
|
-
|
|
153
|
+
var InputOTPSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
154
|
+
"div",
|
|
155
|
+
{
|
|
156
|
+
ref,
|
|
157
|
+
role: "separator",
|
|
158
|
+
className: cn("flex items-center justify-center text-zinc-500", className),
|
|
159
|
+
...props,
|
|
160
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-3 h-0.5 bg-zinc-600 rounded-full" })
|
|
161
|
+
}
|
|
162
|
+
));
|
|
300
163
|
InputOTPSeparator.displayName = "InputOTPSeparator";
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
setAttemptsLeft(OTP_DEFAULTS.MAX_ATTEMPTS);
|
|
331
|
-
setIsRateLimited(false);
|
|
332
|
-
return { limited: false, attemptsLeft: OTP_DEFAULTS.MAX_ATTEMPTS };
|
|
333
|
-
}
|
|
334
|
-
const currentTime = Date.now();
|
|
335
|
-
const resetTime = OTP_DEFAULTS.RATE_LIMIT_RESET_HOURS * 60 * 60 * 1e3;
|
|
336
|
-
if (currentTime - data.firstAttemptTime > resetTime) {
|
|
337
|
-
removeStoredData(key);
|
|
338
|
-
setAttemptsLeft(OTP_DEFAULTS.MAX_ATTEMPTS);
|
|
339
|
-
setIsRateLimited(false);
|
|
340
|
-
return { limited: false, attemptsLeft: OTP_DEFAULTS.MAX_ATTEMPTS };
|
|
341
|
-
}
|
|
342
|
-
const attempts = OTP_DEFAULTS.MAX_ATTEMPTS - data.attempts;
|
|
343
|
-
const limited = data.attempts >= OTP_DEFAULTS.MAX_ATTEMPTS;
|
|
344
|
-
const limitResetTime = data.firstAttemptTime + resetTime;
|
|
345
|
-
setAttemptsLeft(Math.max(0, attempts));
|
|
346
|
-
setIsRateLimited(limited);
|
|
347
|
-
setRateLimitResetTime(limited ? limitResetTime : null);
|
|
348
|
-
if (limited && onRateLimit) {
|
|
349
|
-
onRateLimit(limitResetTime);
|
|
350
|
-
}
|
|
351
|
-
return { limited, attemptsLeft: Math.max(0, attempts), resetTime: limited ? limitResetTime : void 0 };
|
|
352
|
-
};
|
|
353
|
-
const updateRateLimit = () => {
|
|
354
|
-
const key = getStorageKey("rate_limit");
|
|
355
|
-
const data = getStoredData(key);
|
|
356
|
-
const currentTime = Date.now();
|
|
357
|
-
let newData;
|
|
358
|
-
if (!data) {
|
|
359
|
-
newData = {
|
|
360
|
-
attempts: 1,
|
|
361
|
-
firstAttemptTime: currentTime,
|
|
362
|
-
lastAttemptTime: currentTime
|
|
363
|
-
};
|
|
364
|
-
} else {
|
|
365
|
-
const resetTime = OTP_DEFAULTS.RATE_LIMIT_RESET_HOURS * 60 * 60 * 1e3;
|
|
366
|
-
if (currentTime - data.firstAttemptTime > resetTime) {
|
|
367
|
-
newData = {
|
|
368
|
-
attempts: 1,
|
|
369
|
-
firstAttemptTime: currentTime,
|
|
370
|
-
lastAttemptTime: currentTime
|
|
371
|
-
};
|
|
372
|
-
} else {
|
|
373
|
-
newData = {
|
|
374
|
-
...data,
|
|
375
|
-
attempts: data.attempts + 1,
|
|
376
|
-
lastAttemptTime: currentTime
|
|
377
|
-
};
|
|
164
|
+
var OTP = React.forwardRef(
|
|
165
|
+
({
|
|
166
|
+
length = 6,
|
|
167
|
+
value,
|
|
168
|
+
onChange,
|
|
169
|
+
onComplete,
|
|
170
|
+
disabled,
|
|
171
|
+
error,
|
|
172
|
+
autoFocus,
|
|
173
|
+
name,
|
|
174
|
+
className,
|
|
175
|
+
pattern = "^[0-9]*$"
|
|
176
|
+
}, ref) => {
|
|
177
|
+
const slotIndices = Array.from({ length }, (_, i) => i);
|
|
178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
179
|
+
InputOTP,
|
|
180
|
+
{
|
|
181
|
+
ref,
|
|
182
|
+
maxLength: length,
|
|
183
|
+
value,
|
|
184
|
+
onChange,
|
|
185
|
+
onComplete,
|
|
186
|
+
disabled,
|
|
187
|
+
error,
|
|
188
|
+
autoFocus,
|
|
189
|
+
name,
|
|
190
|
+
pattern,
|
|
191
|
+
containerClassName: className,
|
|
192
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputOTPGroup, { children: slotIndices.map((index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InputOTPSlot, { index }, index)) })
|
|
378
193
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
};
|
|
384
|
-
const checkResendCooldown = () => {
|
|
385
|
-
const key = getStorageKey("last_sent");
|
|
386
|
-
const lastSentTime = getStoredData(key);
|
|
387
|
-
if (!lastSentTime) {
|
|
388
|
-
setResendTimeLeft(0);
|
|
389
|
-
return { canResend: true, timeLeft: 0 };
|
|
390
|
-
}
|
|
391
|
-
const currentTime = Date.now();
|
|
392
|
-
const timeSinceLastSent = currentTime - lastSentTime;
|
|
393
|
-
const cooldown = OTP_DEFAULTS.RESEND_COOLDOWN_SECONDS * 1e3;
|
|
394
|
-
const canResend = timeSinceLastSent >= cooldown;
|
|
395
|
-
const timeLeft = canResend ? 0 : Math.ceil((cooldown - timeSinceLastSent) / 1e3);
|
|
396
|
-
setResendTimeLeft(timeLeft);
|
|
397
|
-
return { canResend, timeLeft };
|
|
398
|
-
};
|
|
399
|
-
const recordSentOTP = () => {
|
|
400
|
-
const key = getStorageKey("last_sent");
|
|
401
|
-
setStoredData(key, Date.now());
|
|
402
|
-
updateRateLimit();
|
|
403
|
-
setOtpTimeLeft(OTP_DEFAULTS.EXPIRY_MINUTES * 60);
|
|
404
|
-
setResendTimeLeft(OTP_DEFAULTS.RESEND_COOLDOWN_SECONDS);
|
|
405
|
-
};
|
|
406
|
-
(0, import_react.useEffect)(() => {
|
|
407
|
-
checkRateLimit();
|
|
408
|
-
checkResendCooldown();
|
|
409
|
-
const timer = setInterval(() => {
|
|
410
|
-
setOtpTimeLeft((prev) => Math.max(0, prev - 1));
|
|
411
|
-
const { timeLeft } = checkResendCooldown();
|
|
412
|
-
setResendTimeLeft(timeLeft);
|
|
413
|
-
}, 1e3);
|
|
414
|
-
return () => clearInterval(timer);
|
|
415
|
-
}, [email]);
|
|
416
|
-
const formatTime = (seconds) => {
|
|
417
|
-
const mins = Math.floor(seconds / 60);
|
|
418
|
-
const secs = seconds % 60;
|
|
419
|
-
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
420
|
-
};
|
|
421
|
-
return {
|
|
422
|
-
otpTimeLeft,
|
|
423
|
-
resendTimeLeft,
|
|
424
|
-
attemptsLeft,
|
|
425
|
-
isRateLimited,
|
|
426
|
-
rateLimitResetTime,
|
|
427
|
-
formatTime,
|
|
428
|
-
recordSentOTP,
|
|
429
|
-
checkRateLimit
|
|
430
|
-
};
|
|
431
|
-
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
OTP.displayName = "OTP";
|
|
432
198
|
// Annotate the CommonJS export names for ESM import in node:
|
|
433
199
|
0 && (module.exports = {
|
|
434
|
-
EmailService,
|
|
435
200
|
InputOTP,
|
|
436
201
|
InputOTPGroup,
|
|
437
202
|
InputOTPSeparator,
|
|
438
203
|
InputOTPSlot,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
cn,
|
|
443
|
-
decrypt,
|
|
444
|
-
encrypt,
|
|
445
|
-
useOTP
|
|
204
|
+
IrisMail,
|
|
205
|
+
OTP,
|
|
206
|
+
cn
|
|
446
207
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -3,29 +3,19 @@ import {
|
|
|
3
3
|
InputOTPGroup,
|
|
4
4
|
InputOTPSeparator,
|
|
5
5
|
InputOTPSlot,
|
|
6
|
-
|
|
7
|
-
cn
|
|
8
|
-
|
|
9
|
-
} from "./chunk-FTNSOYOW.mjs";
|
|
6
|
+
OTP,
|
|
7
|
+
cn
|
|
8
|
+
} from "./chunk-IQG3OBQL.mjs";
|
|
10
9
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
OTPService,
|
|
14
|
-
decrypt,
|
|
15
|
-
encrypt
|
|
16
|
-
} from "./chunk-XGASTZZ6.mjs";
|
|
10
|
+
IrisMail
|
|
11
|
+
} from "./chunk-64MVNASE.mjs";
|
|
17
12
|
import "./chunk-QZ7TP4HQ.mjs";
|
|
18
13
|
export {
|
|
19
|
-
EmailService,
|
|
20
14
|
InputOTP,
|
|
21
15
|
InputOTPGroup,
|
|
22
16
|
InputOTPSeparator,
|
|
23
17
|
InputOTPSlot,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cn,
|
|
28
|
-
decrypt,
|
|
29
|
-
encrypt,
|
|
30
|
-
useOTP
|
|
18
|
+
IrisMail,
|
|
19
|
+
OTP,
|
|
20
|
+
cn
|
|
31
21
|
};
|