@arch-cadre/two-factor-email 0.0.1

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.
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ "use server";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.generateAndSendCode = generateAndSendCode;
8
+ exports.is2FAEnabled = is2FAEnabled;
9
+ exports.toggle2FA = toggle2FA;
10
+ exports.verifyAndLogin = verifyAndLogin;
11
+ var _server = require("@arch-cadre/core/server");
12
+ var _drizzleOrm = require("drizzle-orm");
13
+ var _schema = require("./schema.cjs");
14
+ async function is2FAEnabled(userId) {
15
+ const [settings] = await _server.db.select().from(_schema.twoFactorSettingsTable).where((0, _drizzleOrm.eq)(_schema.twoFactorSettingsTable.userId, userId));
16
+ return settings?.enabled ?? false;
17
+ }
18
+ async function toggle2FA(enabled) {
19
+ const {
20
+ user
21
+ } = await (0, _server.getCurrentSession)();
22
+ if (!user) throw new Error("Unauthorized");
23
+ await _server.db.insert(_schema.twoFactorSettingsTable).values({
24
+ userId: user.id,
25
+ enabled
26
+ }).onConflictDoUpdate({
27
+ target: _schema.twoFactorSettingsTable.userId,
28
+ set: {
29
+ enabled,
30
+ updatedAt: /* @__PURE__ */new Date()
31
+ }
32
+ });
33
+ await _server.eventBus.publish("activity.create", {
34
+ action: "2fa.toggled",
35
+ description: `User ${enabled ? "enabled" : "disabled"} 2FA`,
36
+ userId: user.id,
37
+ metadata: {
38
+ enabled
39
+ }
40
+ }, "auth:2fa");
41
+ return {
42
+ success: true
43
+ };
44
+ }
45
+ async function generateAndSendCode(userId) {
46
+ const user = await (0, _server.getUserById)(userId);
47
+ if (!user) throw new Error("User not found");
48
+ const code = Math.floor(1e5 + Math.random() * 9e5).toString();
49
+ const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
50
+ await _server.db.delete(_schema.twoFactorTokensTable).where((0, _drizzleOrm.eq)(_schema.twoFactorTokensTable.userId, userId));
51
+ await _server.db.insert(_schema.twoFactorTokensTable).values({
52
+ userId,
53
+ code,
54
+ expiresAt
55
+ });
56
+ await (0, _server.send2FACode)(user.email, code);
57
+ await _server.eventBus.publish("activity.create", {
58
+ action: "2fa.sended",
59
+ description: `User sent 2FA code`,
60
+ userId,
61
+ metadata: {
62
+ code
63
+ }
64
+ }, "auth:2fa");
65
+ return {
66
+ success: true
67
+ };
68
+ }
69
+ async function verifyAndLogin(userId, code) {
70
+ const [token] = await _server.db.select().from(_schema.twoFactorTokensTable).where((0, _drizzleOrm.and)((0, _drizzleOrm.eq)(_schema.twoFactorTokensTable.userId, userId), (0, _drizzleOrm.eq)(_schema.twoFactorTokensTable.code, code), (0, _drizzleOrm.gt)(_schema.twoFactorTokensTable.expiresAt, /* @__PURE__ */new Date())));
71
+ if (!token) {
72
+ return {
73
+ error: "Invalid or expired code"
74
+ };
75
+ }
76
+ await _server.db.delete(_schema.twoFactorTokensTable).where((0, _drizzleOrm.eq)(_schema.twoFactorTokensTable.userId, userId));
77
+ const result = await (0, _server.finalizeLogin)(userId, {});
78
+ await _server.eventBus.publish("activity.create", {
79
+ action: "2fa.verified",
80
+ description: `User verified 2FA`,
81
+ userId,
82
+ metadata: {
83
+ userId
84
+ }
85
+ }, "auth:2fa");
86
+ return {
87
+ success: true,
88
+ ...result
89
+ };
90
+ }
@@ -0,0 +1,8 @@
1
+ export declare function is2FAEnabled(userId: string): Promise<any>;
2
+ export declare function toggle2FA(enabled: boolean): Promise<{
3
+ success: boolean;
4
+ }>;
5
+ export declare function generateAndSendCode(userId: string): Promise<{
6
+ success: boolean;
7
+ }>;
8
+ export declare function verifyAndLogin(userId: string, code: string): Promise<any>;
@@ -0,0 +1,83 @@
1
+ "use server";
2
+ import {
3
+ db,
4
+ eventBus,
5
+ finalizeLogin,
6
+ getCurrentSession,
7
+ getUserById,
8
+ send2FACode
9
+ } from "@arch-cadre/core/server";
10
+ import { and, eq, gt } from "drizzle-orm";
11
+ import { twoFactorSettingsTable, twoFactorTokensTable } from "./schema.mjs";
12
+ export async function is2FAEnabled(userId) {
13
+ const [settings] = await db.select().from(twoFactorSettingsTable).where(eq(twoFactorSettingsTable.userId, userId));
14
+ return settings?.enabled ?? false;
15
+ }
16
+ export async function toggle2FA(enabled) {
17
+ const { user } = await getCurrentSession();
18
+ if (!user) throw new Error("Unauthorized");
19
+ await db.insert(twoFactorSettingsTable).values({ userId: user.id, enabled }).onConflictDoUpdate({
20
+ target: twoFactorSettingsTable.userId,
21
+ set: { enabled, updatedAt: /* @__PURE__ */ new Date() }
22
+ });
23
+ await eventBus.publish(
24
+ "activity.create",
25
+ {
26
+ action: "2fa.toggled",
27
+ description: `User ${enabled ? "enabled" : "disabled"} 2FA`,
28
+ userId: user.id,
29
+ metadata: { enabled }
30
+ },
31
+ "auth:2fa"
32
+ );
33
+ return { success: true };
34
+ }
35
+ export async function generateAndSendCode(userId) {
36
+ const user = await getUserById(userId);
37
+ if (!user) throw new Error("User not found");
38
+ const code = Math.floor(1e5 + Math.random() * 9e5).toString();
39
+ const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
40
+ await db.delete(twoFactorTokensTable).where(eq(twoFactorTokensTable.userId, userId));
41
+ await db.insert(twoFactorTokensTable).values({
42
+ userId,
43
+ code,
44
+ expiresAt
45
+ });
46
+ await send2FACode(user.email, code);
47
+ await eventBus.publish(
48
+ "activity.create",
49
+ {
50
+ action: "2fa.sended",
51
+ description: `User sent 2FA code`,
52
+ userId,
53
+ metadata: { code }
54
+ },
55
+ "auth:2fa"
56
+ );
57
+ return { success: true };
58
+ }
59
+ export async function verifyAndLogin(userId, code) {
60
+ const [token] = await db.select().from(twoFactorTokensTable).where(
61
+ and(
62
+ eq(twoFactorTokensTable.userId, userId),
63
+ eq(twoFactorTokensTable.code, code),
64
+ gt(twoFactorTokensTable.expiresAt, /* @__PURE__ */ new Date())
65
+ )
66
+ );
67
+ if (!token) {
68
+ return { error: "Invalid or expired code" };
69
+ }
70
+ await db.delete(twoFactorTokensTable).where(eq(twoFactorTokensTable.userId, userId));
71
+ const result = await finalizeLogin(userId, {});
72
+ await eventBus.publish(
73
+ "activity.create",
74
+ {
75
+ action: "2fa.verified",
76
+ description: `User verified 2FA`,
77
+ userId,
78
+ metadata: { userId }
79
+ },
80
+ "auth:2fa"
81
+ );
82
+ return { success: true, ...result };
83
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _server = require("@arch-cadre/core/server");
8
+ var _drizzleOrm = require("drizzle-orm");
9
+ var _manifest = _interopRequireDefault(require("../manifest.json"));
10
+ var _actions = require("./actions.cjs");
11
+ var _routes = require("./routes.cjs");
12
+ var _settingsToggle = require("./ui/settings-toggle.cjs");
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ const twoFactorEmailModule = {
15
+ manifest: _manifest.default,
16
+ routes: {
17
+ public: _routes.publicRoutes
18
+ },
19
+ init: async () => {
20
+ console.log("[Module:2FA-Email] Initializing...");
21
+ (0, _server.registerAuthValidator)(async userId => {
22
+ console.log(`[Module:2FA-Email] Validating login for user: ${userId}`);
23
+ try {
24
+ const enabled = await (0, _actions.is2FAEnabled)(userId);
25
+ console.log(`[Module:2FA-Email] 2FA enabled for user ${userId}: ${enabled}`);
26
+ if (enabled) {
27
+ console.log(`[Module:2FA-Email] Generating code for user ${userId}`);
28
+ await (0, _actions.generateAndSendCode)(userId);
29
+ return {
30
+ status: "CHALLENGE_REQUIRED",
31
+ type: "email_2fa",
32
+ userId,
33
+ redirect: `/auth/2fa?userId=${userId}`
34
+ };
35
+ }
36
+ } catch (error) {
37
+ console.error("[Module:2FA-Email] Error during auth validation:", error);
38
+ }
39
+ return null;
40
+ });
41
+ console.log("[Module:2FA-Email] Validator registered.");
42
+ },
43
+ onDisable: async () => {
44
+ console.log("[Module:2FA-Email] onDisable: Dropping all tables physically...");
45
+ try {
46
+ const tables = ["two_factor_settings", "two_factor_tokens"];
47
+ for (const table of tables) {
48
+ await _server.db.execute(_drizzleOrm.sql.raw(`DROP TABLE IF EXISTS ${table} CASCADE`));
49
+ }
50
+ } catch (e) {
51
+ console.error("[Module:2FA-Email] onDisable Error:", e);
52
+ }
53
+ },
54
+ extensions: [{
55
+ id: "2fa-settings-section",
56
+ targetModule: "user-profile",
57
+ point: "settings:extra-sections",
58
+ component: _settingsToggle.TwoFactorSettings,
59
+ priority: 50
60
+ }]
61
+ };
62
+ module.exports = twoFactorEmailModule;
@@ -0,0 +1,3 @@
1
+ import type { IModule } from "@arch-cadre/modules";
2
+ declare const twoFactorEmailModule: IModule;
3
+ export default twoFactorEmailModule;
package/dist/index.mjs ADDED
@@ -0,0 +1,64 @@
1
+ import { db, registerAuthValidator } from "@arch-cadre/core/server";
2
+ import { sql } from "drizzle-orm";
3
+ import manifest from "../manifest.json";
4
+ import { generateAndSendCode, is2FAEnabled } from "./actions.mjs";
5
+ import { publicRoutes } from "./routes.mjs";
6
+ import { TwoFactorSettings } from "./ui/settings-toggle.mjs";
7
+ const twoFactorEmailModule = {
8
+ manifest,
9
+ routes: {
10
+ public: publicRoutes
11
+ },
12
+ init: async () => {
13
+ console.log("[Module:2FA-Email] Initializing...");
14
+ registerAuthValidator(async (userId) => {
15
+ console.log(`[Module:2FA-Email] Validating login for user: ${userId}`);
16
+ try {
17
+ const enabled = await is2FAEnabled(userId);
18
+ console.log(
19
+ `[Module:2FA-Email] 2FA enabled for user ${userId}: ${enabled}`
20
+ );
21
+ if (enabled) {
22
+ console.log(`[Module:2FA-Email] Generating code for user ${userId}`);
23
+ await generateAndSendCode(userId);
24
+ return {
25
+ status: "CHALLENGE_REQUIRED",
26
+ type: "email_2fa",
27
+ userId,
28
+ redirect: `/auth/2fa?userId=${userId}`
29
+ };
30
+ }
31
+ } catch (error) {
32
+ console.error(
33
+ "[Module:2FA-Email] Error during auth validation:",
34
+ error
35
+ );
36
+ }
37
+ return null;
38
+ });
39
+ console.log("[Module:2FA-Email] Validator registered.");
40
+ },
41
+ onDisable: async () => {
42
+ console.log(
43
+ "[Module:2FA-Email] onDisable: Dropping all tables physically..."
44
+ );
45
+ try {
46
+ const tables = ["two_factor_settings", "two_factor_tokens"];
47
+ for (const table of tables) {
48
+ await db.execute(sql.raw(`DROP TABLE IF EXISTS ${table} CASCADE`));
49
+ }
50
+ } catch (e) {
51
+ console.error("[Module:2FA-Email] onDisable Error:", e);
52
+ }
53
+ },
54
+ extensions: [
55
+ {
56
+ id: "2fa-settings-section",
57
+ targetModule: "user-profile",
58
+ point: "settings:extra-sections",
59
+ component: TwoFactorSettings,
60
+ priority: 50
61
+ }
62
+ ]
63
+ };
64
+ export default twoFactorEmailModule;
package/dist/intl.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type messages from "../locales/en/global.json";
2
+
3
+ type JsonDataType = typeof messages;
4
+
5
+ declare module "@arch-cadre/intl" {
6
+ export interface IntlMessages extends JsonDataType {}
7
+ }
8
+
9
+ export {};
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.publicRoutes = void 0;
7
+ var _dynamic = _interopRequireDefault(require("next/dynamic"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ const TwoFactorVerifyPage = (0, _dynamic.default)(() => Promise.resolve().then(() => require("./ui/verify-page.cjs")));
10
+ const publicRoutes = exports.publicRoutes = [{
11
+ path: "/auth/2fa",
12
+ component: TwoFactorVerifyPage,
13
+ auth: false
14
+ }];
@@ -0,0 +1,2 @@
1
+ import type { PublicRouteDefinition } from "@arch-cadre/modules";
2
+ export declare const publicRoutes: PublicRouteDefinition[];
@@ -0,0 +1,9 @@
1
+ import dynamic from "next/dynamic";
2
+ const TwoFactorVerifyPage = dynamic(() => import("./ui/verify-page.mjs"));
3
+ export const publicRoutes = [
4
+ {
5
+ path: "/auth/2fa",
6
+ component: TwoFactorVerifyPage,
7
+ auth: false
8
+ }
9
+ ];
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.twoFactorTokensTable = exports.twoFactorSettingsTable = exports.twoFactorSchema = void 0;
7
+ var _core = require("@arch-cadre/core");
8
+ var _pgCore = require("drizzle-orm/pg-core");
9
+ const twoFactorSettingsTable = exports.twoFactorSettingsTable = (0, _pgCore.pgTable)("two_factor_settings", {
10
+ userId: (0, _pgCore.text)("user_id").primaryKey().references(() => _core.userTable.id, {
11
+ onDelete: "cascade"
12
+ }),
13
+ enabled: (0, _pgCore.boolean)("enabled").notNull().default(false),
14
+ updatedAt: (0, _pgCore.timestamp)("updated_at", {
15
+ precision: 3
16
+ }).defaultNow()
17
+ });
18
+ const twoFactorTokensTable = exports.twoFactorTokensTable = (0, _pgCore.pgTable)("two_factor_tokens", {
19
+ id: (0, _pgCore.text)("id").$defaultFn(() => crypto.randomUUID()).notNull().primaryKey(),
20
+ userId: (0, _pgCore.text)("user_id").notNull().references(() => _core.userTable.id, {
21
+ onDelete: "cascade"
22
+ }),
23
+ code: (0, _pgCore.text)("code").notNull(),
24
+ expiresAt: (0, _pgCore.timestamp)("expires_at", {
25
+ precision: 3
26
+ }).notNull(),
27
+ createdAt: (0, _pgCore.timestamp)("created_at", {
28
+ precision: 3
29
+ }).notNull().defaultNow()
30
+ });
31
+ const twoFactorSchema = exports.twoFactorSchema = {
32
+ twoFactorSettingsTable,
33
+ twoFactorTokensTable
34
+ };
@@ -0,0 +1,270 @@
1
+ export declare const twoFactorSettingsTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "two_factor_settings";
3
+ schema: undefined;
4
+ columns: {
5
+ userId: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetIsPrimaryKey<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
6
+ name: string;
7
+ tableName: "two_factor_settings";
8
+ dataType: "string";
9
+ data: string;
10
+ driverParam: string;
11
+ notNull: true;
12
+ hasDefault: false;
13
+ isPrimaryKey: false;
14
+ isAutoincrement: false;
15
+ hasRuntimeDefault: false;
16
+ enumValues: undefined;
17
+ identity: undefined;
18
+ generated: undefined;
19
+ }>;
20
+ enabled: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgBooleanBuilder>>, {
21
+ name: string;
22
+ tableName: "two_factor_settings";
23
+ dataType: "boolean";
24
+ data: boolean;
25
+ driverParam: boolean;
26
+ notNull: true;
27
+ hasDefault: true;
28
+ isPrimaryKey: false;
29
+ isAutoincrement: false;
30
+ hasRuntimeDefault: false;
31
+ enumValues: undefined;
32
+ identity: undefined;
33
+ generated: undefined;
34
+ }>;
35
+ updatedAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").PgTimestampBuilder>, {
36
+ name: string;
37
+ tableName: "two_factor_settings";
38
+ dataType: "object date";
39
+ data: Date;
40
+ driverParam: string;
41
+ notNull: false;
42
+ hasDefault: true;
43
+ isPrimaryKey: false;
44
+ isAutoincrement: false;
45
+ hasRuntimeDefault: false;
46
+ enumValues: undefined;
47
+ identity: undefined;
48
+ generated: undefined;
49
+ }>;
50
+ };
51
+ dialect: "pg";
52
+ }>;
53
+ export declare const twoFactorTokensTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
54
+ name: "two_factor_tokens";
55
+ schema: undefined;
56
+ columns: {
57
+ id: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetIsPrimaryKey<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").SetHasRuntimeDefault<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>>>, {
58
+ name: string;
59
+ tableName: "two_factor_tokens";
60
+ dataType: "string";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: true;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ identity: undefined;
70
+ generated: undefined;
71
+ }>;
72
+ userId: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
73
+ name: string;
74
+ tableName: "two_factor_tokens";
75
+ dataType: "string";
76
+ data: string;
77
+ driverParam: string;
78
+ notNull: true;
79
+ hasDefault: false;
80
+ isPrimaryKey: false;
81
+ isAutoincrement: false;
82
+ hasRuntimeDefault: false;
83
+ enumValues: undefined;
84
+ identity: undefined;
85
+ generated: undefined;
86
+ }>;
87
+ code: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
88
+ name: string;
89
+ tableName: "two_factor_tokens";
90
+ dataType: "string";
91
+ data: string;
92
+ driverParam: string;
93
+ notNull: true;
94
+ hasDefault: false;
95
+ isPrimaryKey: false;
96
+ isAutoincrement: false;
97
+ hasRuntimeDefault: false;
98
+ enumValues: undefined;
99
+ identity: undefined;
100
+ generated: undefined;
101
+ }>;
102
+ expiresAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTimestampBuilder>, {
103
+ name: string;
104
+ tableName: "two_factor_tokens";
105
+ dataType: "object date";
106
+ data: Date;
107
+ driverParam: string;
108
+ notNull: true;
109
+ hasDefault: false;
110
+ isPrimaryKey: false;
111
+ isAutoincrement: false;
112
+ hasRuntimeDefault: false;
113
+ enumValues: undefined;
114
+ identity: undefined;
115
+ generated: undefined;
116
+ }>;
117
+ createdAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTimestampBuilder>>, {
118
+ name: string;
119
+ tableName: "two_factor_tokens";
120
+ dataType: "object date";
121
+ data: Date;
122
+ driverParam: string;
123
+ notNull: true;
124
+ hasDefault: true;
125
+ isPrimaryKey: false;
126
+ isAutoincrement: false;
127
+ hasRuntimeDefault: false;
128
+ enumValues: undefined;
129
+ identity: undefined;
130
+ generated: undefined;
131
+ }>;
132
+ };
133
+ dialect: "pg";
134
+ }>;
135
+ export declare const twoFactorSchema: {
136
+ twoFactorSettingsTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
137
+ name: "two_factor_settings";
138
+ schema: undefined;
139
+ columns: {
140
+ userId: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetIsPrimaryKey<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
141
+ name: string;
142
+ tableName: "two_factor_settings";
143
+ dataType: "string";
144
+ data: string;
145
+ driverParam: string;
146
+ notNull: true;
147
+ hasDefault: false;
148
+ isPrimaryKey: false;
149
+ isAutoincrement: false;
150
+ hasRuntimeDefault: false;
151
+ enumValues: undefined;
152
+ identity: undefined;
153
+ generated: undefined;
154
+ }>;
155
+ enabled: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgBooleanBuilder>>, {
156
+ name: string;
157
+ tableName: "two_factor_settings";
158
+ dataType: "boolean";
159
+ data: boolean;
160
+ driverParam: boolean;
161
+ notNull: true;
162
+ hasDefault: true;
163
+ isPrimaryKey: false;
164
+ isAutoincrement: false;
165
+ hasRuntimeDefault: false;
166
+ enumValues: undefined;
167
+ identity: undefined;
168
+ generated: undefined;
169
+ }>;
170
+ updatedAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_settings", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").PgTimestampBuilder>, {
171
+ name: string;
172
+ tableName: "two_factor_settings";
173
+ dataType: "object date";
174
+ data: Date;
175
+ driverParam: string;
176
+ notNull: false;
177
+ hasDefault: true;
178
+ isPrimaryKey: false;
179
+ isAutoincrement: false;
180
+ hasRuntimeDefault: false;
181
+ enumValues: undefined;
182
+ identity: undefined;
183
+ generated: undefined;
184
+ }>;
185
+ };
186
+ dialect: "pg";
187
+ }>;
188
+ twoFactorTokensTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
189
+ name: "two_factor_tokens";
190
+ schema: undefined;
191
+ columns: {
192
+ id: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetIsPrimaryKey<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").SetHasRuntimeDefault<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>>>, {
193
+ name: string;
194
+ tableName: "two_factor_tokens";
195
+ dataType: "string";
196
+ data: string;
197
+ driverParam: string;
198
+ notNull: true;
199
+ hasDefault: true;
200
+ isPrimaryKey: false;
201
+ isAutoincrement: false;
202
+ hasRuntimeDefault: false;
203
+ enumValues: undefined;
204
+ identity: undefined;
205
+ generated: undefined;
206
+ }>;
207
+ userId: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
208
+ name: string;
209
+ tableName: "two_factor_tokens";
210
+ dataType: "string";
211
+ data: string;
212
+ driverParam: string;
213
+ notNull: true;
214
+ hasDefault: false;
215
+ isPrimaryKey: false;
216
+ isAutoincrement: false;
217
+ hasRuntimeDefault: false;
218
+ enumValues: undefined;
219
+ identity: undefined;
220
+ generated: undefined;
221
+ }>;
222
+ code: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>>, {
223
+ name: string;
224
+ tableName: "two_factor_tokens";
225
+ dataType: "string";
226
+ data: string;
227
+ driverParam: string;
228
+ notNull: true;
229
+ hasDefault: false;
230
+ isPrimaryKey: false;
231
+ isAutoincrement: false;
232
+ hasRuntimeDefault: false;
233
+ enumValues: undefined;
234
+ identity: undefined;
235
+ generated: undefined;
236
+ }>;
237
+ expiresAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTimestampBuilder>, {
238
+ name: string;
239
+ tableName: "two_factor_tokens";
240
+ dataType: "object date";
241
+ data: Date;
242
+ driverParam: string;
243
+ notNull: true;
244
+ hasDefault: false;
245
+ isPrimaryKey: false;
246
+ isAutoincrement: false;
247
+ hasRuntimeDefault: false;
248
+ enumValues: undefined;
249
+ identity: undefined;
250
+ generated: undefined;
251
+ }>;
252
+ createdAt: import("drizzle-orm/pg-core").PgBuildColumn<"two_factor_tokens", import("drizzle-orm/pg-core").SetHasDefault<import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgTimestampBuilder>>, {
253
+ name: string;
254
+ tableName: "two_factor_tokens";
255
+ dataType: "object date";
256
+ data: Date;
257
+ driverParam: string;
258
+ notNull: true;
259
+ hasDefault: true;
260
+ isPrimaryKey: false;
261
+ isAutoincrement: false;
262
+ hasRuntimeDefault: false;
263
+ enumValues: undefined;
264
+ identity: undefined;
265
+ generated: undefined;
266
+ }>;
267
+ };
268
+ dialect: "pg";
269
+ }>;
270
+ };
@@ -0,0 +1,18 @@
1
+ import { userTable } from "@arch-cadre/core";
2
+ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
3
+ export const twoFactorSettingsTable = pgTable("two_factor_settings", {
4
+ userId: text("user_id").primaryKey().references(() => userTable.id, { onDelete: "cascade" }),
5
+ enabled: boolean("enabled").notNull().default(false),
6
+ updatedAt: timestamp("updated_at", { precision: 3 }).defaultNow()
7
+ });
8
+ export const twoFactorTokensTable = pgTable("two_factor_tokens", {
9
+ id: text("id").$defaultFn(() => crypto.randomUUID()).notNull().primaryKey(),
10
+ userId: text("user_id").notNull().references(() => userTable.id, { onDelete: "cascade" }),
11
+ code: text("code").notNull(),
12
+ expiresAt: timestamp("expires_at", { precision: 3 }).notNull(),
13
+ createdAt: timestamp("created_at", { precision: 3 }).notNull().defaultNow()
14
+ });
15
+ export const twoFactorSchema = {
16
+ twoFactorSettingsTable,
17
+ twoFactorTokensTable
18
+ };
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.TwoFactorSettings = TwoFactorSettings;
8
+ var _intl = require("@arch-cadre/intl");
9
+ var _card = require("@arch-cadre/ui/components/card");
10
+ var _label = require("@arch-cadre/ui/components/label");
11
+ var _switch = require("@arch-cadre/ui/components/switch");
12
+ var _useUser = require("@arch-cadre/ui/hooks/use-user");
13
+ var _react = _interopRequireWildcard(require("react"));
14
+ var React = _react;
15
+ var _sonner = require("sonner");
16
+ var _actions = require("../actions.cjs");
17
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
18
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
19
+ function TwoFactorSettings() {
20
+ const {
21
+ user
22
+ } = (0, _useUser.useUser)();
23
+ const {
24
+ t
25
+ } = (0, _intl.useTranslation)();
26
+ const [enabled, setEnabled] = (0, _react.useState)(false);
27
+ const [loading, setLoading] = (0, _react.useState)(true);
28
+ (0, _react.useEffect)(() => {
29
+ if (user) {
30
+ (0, _actions.is2FAEnabled)(user.id).then(val => {
31
+ setEnabled(val);
32
+ setLoading(false);
33
+ });
34
+ }
35
+ }, [user]);
36
+ const handleToggle = async val => {
37
+ try {
38
+ await (0, _actions.toggle2FA)(val);
39
+ setEnabled(val);
40
+ _sonner.toast.success(val ? t("2FA enabled") : t("2FA disabled"));
41
+ } catch {
42
+ _sonner.toast.error(t("Action failed"));
43
+ }
44
+ };
45
+ if (loading) return null;
46
+ return /* @__PURE__ */React.createElement(_card.Card, null, /* @__PURE__ */React.createElement(_card.CardHeader, null, /* @__PURE__ */React.createElement(_card.CardTitle, null, t("Two Factor Authentication")), /* @__PURE__ */React.createElement(_card.CardDescription, null, t("Add an extra layer of security to your account by requiring a code sent to your email."))), /* @__PURE__ */React.createElement(_card.CardContent, {
47
+ className: "flex items-center justify-between"
48
+ }, /* @__PURE__ */React.createElement(_label.Label, {
49
+ htmlFor: "2fa-toggle",
50
+ className: "flex flex-col "
51
+ }, /* @__PURE__ */React.createElement("span", null, t("Email Authentication")), /* @__PURE__ */React.createElement("span", {
52
+ className: "font-normal text-xs text-muted-foreground"
53
+ }, enabled ? t("Currently enabled") : t("Currently disabled"))), /* @__PURE__ */React.createElement(_switch.Switch, {
54
+ id: "2fa-toggle",
55
+ checked: enabled,
56
+ onCheckedChange: handleToggle
57
+ })));
58
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export declare function TwoFactorSettings(): React.JSX.Element | null;
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle
9
+ } from "@arch-cadre/ui/components/card";
10
+ import { Label } from "@arch-cadre/ui/components/label";
11
+ import { Switch } from "@arch-cadre/ui/components/switch";
12
+ import { useUser } from "@arch-cadre/ui/hooks/use-user";
13
+ import * as React from "react";
14
+ import { useEffect, useState } from "react";
15
+ import { toast } from "sonner";
16
+ import { is2FAEnabled, toggle2FA } from "../actions.mjs";
17
+ export function TwoFactorSettings() {
18
+ const { user } = useUser();
19
+ const { t } = useTranslation();
20
+ const [enabled, setEnabled] = useState(false);
21
+ const [loading, setLoading] = useState(true);
22
+ useEffect(() => {
23
+ if (user) {
24
+ is2FAEnabled(user.id).then((val) => {
25
+ setEnabled(val);
26
+ setLoading(false);
27
+ });
28
+ }
29
+ }, [user]);
30
+ const handleToggle = async (val) => {
31
+ try {
32
+ await toggle2FA(val);
33
+ setEnabled(val);
34
+ toast.success(val ? t("2FA enabled") : t("2FA disabled"));
35
+ } catch {
36
+ toast.error(t("Action failed"));
37
+ }
38
+ };
39
+ if (loading) return null;
40
+ return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, null, /* @__PURE__ */ React.createElement(CardTitle, null, t("Two Factor Authentication")), /* @__PURE__ */ React.createElement(CardDescription, null, t(
41
+ "Add an extra layer of security to your account by requiring a code sent to your email."
42
+ ))), /* @__PURE__ */ React.createElement(CardContent, { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "2fa-toggle", className: "flex flex-col " }, /* @__PURE__ */ React.createElement("span", null, t("Email Authentication")), /* @__PURE__ */ React.createElement("span", { className: "font-normal text-xs text-muted-foreground" }, enabled ? t("Currently enabled") : t("Currently disabled"))), /* @__PURE__ */ React.createElement(
43
+ Switch,
44
+ {
45
+ id: "2fa-toggle",
46
+ checked: enabled,
47
+ onCheckedChange: handleToggle
48
+ }
49
+ )));
50
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ module.exports = TwoFactorVerifyPage;
8
+ var _intl = require("@arch-cadre/intl");
9
+ var _button = require("@arch-cadre/ui/components/button");
10
+ var _card = require("@arch-cadre/ui/components/card");
11
+ var _input = require("@arch-cadre/ui/components/input");
12
+ var _loader = require("@arch-cadre/ui/shared/loader");
13
+ var _navigation = require("next/navigation");
14
+ var _react = _interopRequireWildcard(require("react"));
15
+ var React = _react;
16
+ var _sonner = require("sonner");
17
+ var _actions = require("../actions.cjs");
18
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
19
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
20
+ function TwoFactorVerifyPage() {
21
+ const {
22
+ t
23
+ } = (0, _intl.useTranslation)();
24
+ const router = (0, _navigation.useRouter)();
25
+ const searchParams = (0, _navigation.useSearchParams)();
26
+ const userId = searchParams.get("userId");
27
+ const [code, setCode] = (0, _react.useState)("");
28
+ const [isSubmitting, setIsSubmitting] = (0, _react.useState)(false);
29
+ if (!userId) {
30
+ router.push("/signin");
31
+ return null;
32
+ }
33
+ const handleSubmit = async e => {
34
+ e.preventDefault();
35
+ setIsSubmitting(true);
36
+ try {
37
+ const result = await (0, _actions.verifyAndLogin)(userId, code);
38
+ if (result.error === null) {
39
+ _sonner.toast.success(t("Logged in successfully"));
40
+ window.location.href = "/";
41
+ } else {
42
+ _sonner.toast.error(result.error || t("Verification failed"));
43
+ }
44
+ } catch {
45
+ _sonner.toast.error(t("An error occurred"));
46
+ } finally {
47
+ setIsSubmitting(false);
48
+ }
49
+ };
50
+ const handleResend = async () => {
51
+ try {
52
+ await (0, _actions.generateAndSendCode)(userId);
53
+ _sonner.toast.success(t("New code sent to your email"));
54
+ } catch {
55
+ _sonner.toast.error(t("Failed to send code"));
56
+ }
57
+ };
58
+ return /* @__PURE__ */React.createElement("div", {
59
+ className: "flex min-h-screen items-center justify-center bg-muted/50 p-4"
60
+ }, /* @__PURE__ */React.createElement(_card.Card, {
61
+ className: "w-full max-w-md"
62
+ }, /* @__PURE__ */React.createElement(_card.CardHeader, {
63
+ className: "text-center"
64
+ }, /* @__PURE__ */React.createElement(_card.CardTitle, {
65
+ className: "text-2xl"
66
+ }, t("Two Factor Verification")), /* @__PURE__ */React.createElement(_card.CardDescription, null, t("Enter the 6-digit code sent to your email address to continue."))), /* @__PURE__ */React.createElement(_card.CardContent, null, /* @__PURE__ */React.createElement("form", {
67
+ onSubmit: handleSubmit,
68
+ className: "space-y-4"
69
+ }, /* @__PURE__ */React.createElement("div", {
70
+ className: "space-y-2"
71
+ }, /* @__PURE__ */React.createElement(_input.Input, {
72
+ type: "text",
73
+ maxLength: 6,
74
+ value: code,
75
+ onChange: e => setCode(e.target.value.replace(/\D/g, "")),
76
+ placeholder: "000000",
77
+ className: "text-center text-2xl tracking-[0.5em] font-mono h-14",
78
+ autoFocus: true
79
+ })), /* @__PURE__ */React.createElement(_button.Button, {
80
+ type: "submit",
81
+ className: "w-full h-11",
82
+ disabled: isSubmitting || code.length !== 6
83
+ }, isSubmitting ? /* @__PURE__ */React.createElement(_loader.Loader, {
84
+ variant: "dark"
85
+ }) : t("Verify & Signin")), /* @__PURE__ */React.createElement(_button.Button, {
86
+ type: "button",
87
+ variant: "ghost",
88
+ className: "w-full",
89
+ onClick: handleResend
90
+ }, t("Didn't receive a code? Resend"))))));
91
+ }
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export default function TwoFactorVerifyPage(): React.JSX.Element | null;
@@ -0,0 +1,85 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import { Button } from "@arch-cadre/ui/components/button";
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle
10
+ } from "@arch-cadre/ui/components/card";
11
+ import { Input } from "@arch-cadre/ui/components/input";
12
+ import { Loader } from "@arch-cadre/ui/shared/loader";
13
+ import { useRouter, useSearchParams } from "next/navigation";
14
+ import * as React from "react";
15
+ import { useState } from "react";
16
+ import { toast } from "sonner";
17
+ import { generateAndSendCode, verifyAndLogin } from "../actions.mjs";
18
+ export default function TwoFactorVerifyPage() {
19
+ const { t } = useTranslation();
20
+ const router = useRouter();
21
+ const searchParams = useSearchParams();
22
+ const userId = searchParams.get("userId");
23
+ const [code, setCode] = useState("");
24
+ const [isSubmitting, setIsSubmitting] = useState(false);
25
+ if (!userId) {
26
+ router.push("/signin");
27
+ return null;
28
+ }
29
+ const handleSubmit = async (e) => {
30
+ e.preventDefault();
31
+ setIsSubmitting(true);
32
+ try {
33
+ const result = await verifyAndLogin(userId, code);
34
+ if (result.error === null) {
35
+ toast.success(t("Logged in successfully"));
36
+ window.location.href = "/";
37
+ } else {
38
+ toast.error(result.error || t("Verification failed"));
39
+ }
40
+ } catch {
41
+ toast.error(t("An error occurred"));
42
+ } finally {
43
+ setIsSubmitting(false);
44
+ }
45
+ };
46
+ const handleResend = async () => {
47
+ try {
48
+ await generateAndSendCode(userId);
49
+ toast.success(t("New code sent to your email"));
50
+ } catch {
51
+ toast.error(t("Failed to send code"));
52
+ }
53
+ };
54
+ return /* @__PURE__ */ React.createElement("div", { className: "flex min-h-screen items-center justify-center bg-muted/50 p-4" }, /* @__PURE__ */ React.createElement(Card, { className: "w-full max-w-md" }, /* @__PURE__ */ React.createElement(CardHeader, { className: "text-center" }, /* @__PURE__ */ React.createElement(CardTitle, { className: "text-2xl" }, t("Two Factor Verification")), /* @__PURE__ */ React.createElement(CardDescription, null, t(
55
+ "Enter the 6-digit code sent to your email address to continue."
56
+ ))), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit, className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(
57
+ Input,
58
+ {
59
+ type: "text",
60
+ maxLength: 6,
61
+ value: code,
62
+ onChange: (e) => setCode(e.target.value.replace(/\D/g, "")),
63
+ placeholder: "000000",
64
+ className: "text-center text-2xl tracking-[0.5em] font-mono h-14",
65
+ autoFocus: true
66
+ }
67
+ )), /* @__PURE__ */ React.createElement(
68
+ Button,
69
+ {
70
+ type: "submit",
71
+ className: "w-full h-11",
72
+ disabled: isSubmitting || code.length !== 6
73
+ },
74
+ isSubmitting ? /* @__PURE__ */ React.createElement(Loader, { variant: "dark" }) : t("Verify & Signin")
75
+ ), /* @__PURE__ */ React.createElement(
76
+ Button,
77
+ {
78
+ type: "button",
79
+ variant: "ghost",
80
+ className: "w-full",
81
+ onClick: handleResend
82
+ },
83
+ t("Didn't receive a code? Resend")
84
+ )))));
85
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "Logged in successfully": "Logged in successfully",
3
+ "Verification failed": "Verification failed",
4
+ "An error occurred": "An error occurred",
5
+ "New code sent to your email": "New code sent to your email",
6
+ "Failed to send code": "Failed to send code",
7
+ "Two Factor Verification": "Two Factor Verification",
8
+ "Enter the 6-digit code sent to your email address to continue.": "Enter the 6-digit code sent to your email address to continue.",
9
+ "Verify & Signin": "Verify & Signin",
10
+ "Didn't receive a code? Resend": "Didn't receive a code? Resend",
11
+ "2FA enabled": "2FA enabled",
12
+ "2FA disabled": "2FA disabled",
13
+ "Action failed": "Action failed",
14
+ "Two Factor Authentication": "Two Factor Authentication",
15
+ "Add an extra layer of security to your account by requiring a code sent to your email.": "Add an extra layer of security to your account by requiring a code sent to your email.",
16
+ "Email Authentication": "Email Authentication",
17
+ "Currently enabled": "Currently enabled",
18
+ "Currently disabled": "Currently disabled"
19
+ }
package/manifest.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "two-factor-email",
3
+ "name": "Two-Factor Email",
4
+ "version": "0.0.1",
5
+ "description": "Email-based two-factor authentication.",
6
+ "enabled": true,
7
+ "system": false,
8
+ "dependencies": [],
9
+ "extends": [],
10
+ "isNpm": true
11
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@arch-cadre/two-factor-email",
3
+ "version": "0.0.1",
4
+ "description": "Email-based two-factor authentication module for Kryo framework",
5
+ "type": "module",
6
+ "exports": {
7
+ "./package.json": "./package.json",
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.cjs"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "locales",
17
+ "manifest.json"
18
+ ],
19
+ "scripts": {
20
+ "release": "npm publish --access public --no-git-checks",
21
+ "clean": "rm -rf ./dist",
22
+ "switch:dev": "node scripts/switchToSrc.js",
23
+ "switch:prod": "node scripts/switchToDist.js",
24
+ "dev": "unbuild --stub",
25
+ "build": "unbuild"
26
+ },
27
+ "dependencies": {
28
+ "@hookform/resolvers": "^3.10.0",
29
+ "@arch-cadre/ui": "^0.0.15",
30
+ "@arch-cadre/modules": "^0.0.15",
31
+ "lucide-react": "^0.475.0",
32
+ "react-hook-form": "^7.54.2",
33
+ "sonner": "^2.0.7",
34
+ "zod": "^3.24.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/pg": "^8.16.0",
38
+ "@arch-cadre/core": "^0.0.15",
39
+ "@types/react": "^19",
40
+ "next": "16.1.1",
41
+ "react": "^19.0.0",
42
+ "typescript": "^5.3.3",
43
+ "unbuild": "^3.6.1"
44
+ },
45
+ "peerDependencies": {
46
+ "@arch-cadre/core": "^0.0.15",
47
+ "@arch-cadre/intl": "^0.0.15",
48
+ "@arch-cadre/ui": "^0.0.15",
49
+ "pg": "^8.16.3",
50
+ "drizzle-orm": "1.0.0-beta.15-859cf75",
51
+ "next": ">=13.0.0",
52
+ "react": "^19.0.0"
53
+ },
54
+ "main": "./dist/index.mjs"
55
+ }