@sonicjs-cms/core 2.3.13 → 2.3.15

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 (55) hide show
  1. package/dist/{chunk-RP66TPEJ.js → chunk-4M2UOUXV.js} +415 -320
  2. package/dist/chunk-4M2UOUXV.js.map +1 -0
  3. package/dist/{chunk-ARLXQU2S.cjs → chunk-5OLN5JO3.cjs} +560 -465
  4. package/dist/chunk-5OLN5JO3.cjs.map +1 -0
  5. package/dist/{chunk-W4CE7XME.cjs → chunk-63LV4YVI.cjs} +2 -2
  6. package/dist/{chunk-W4CE7XME.cjs.map → chunk-63LV4YVI.cjs.map} +1 -1
  7. package/dist/{chunk-FHCN7KR2.js → chunk-67SKO5RQ.js} +3 -3
  8. package/dist/{chunk-FHCN7KR2.js.map → chunk-67SKO5RQ.js.map} +1 -1
  9. package/dist/{chunk-VMEBHBYY.js → chunk-72I2MOSH.js} +2 -2
  10. package/dist/{chunk-VMEBHBYY.js.map → chunk-72I2MOSH.js.map} +1 -1
  11. package/dist/{chunk-2NTBZ2Y7.js → chunk-A27RBGBA.js} +3 -3
  12. package/dist/{chunk-2NTBZ2Y7.js.map → chunk-A27RBGBA.js.map} +1 -1
  13. package/dist/{chunk-F56JKQTA.js → chunk-AVPUX57O.js} +3 -3
  14. package/dist/{chunk-F56JKQTA.js.map → chunk-AVPUX57O.js.map} +1 -1
  15. package/dist/{chunk-MF7DWI5P.cjs → chunk-AZLU3ROK.cjs} +4 -2
  16. package/dist/chunk-AZLU3ROK.cjs.map +1 -0
  17. package/dist/{chunk-W2IAEG4W.cjs → chunk-H2X4BFCW.cjs} +3 -3
  18. package/dist/{chunk-W2IAEG4W.cjs.map → chunk-H2X4BFCW.cjs.map} +1 -1
  19. package/dist/{chunk-5NCBFP37.cjs → chunk-QG3YQKL4.cjs} +4 -4
  20. package/dist/{chunk-5NCBFP37.cjs.map → chunk-QG3YQKL4.cjs.map} +1 -1
  21. package/dist/{chunk-DN45O5XV.js → chunk-V5LBQN3I.js} +4 -2
  22. package/dist/chunk-V5LBQN3I.js.map +1 -0
  23. package/dist/{chunk-XR6XACXJ.cjs → chunk-YIXSSJWD.cjs} +5 -5
  24. package/dist/{chunk-XR6XACXJ.cjs.map → chunk-YIXSSJWD.cjs.map} +1 -1
  25. package/dist/index.cjs +1111 -87
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +1034 -10
  30. package/dist/index.js.map +1 -1
  31. package/dist/middleware.cjs +23 -23
  32. package/dist/middleware.js +2 -2
  33. package/dist/migrations-H3Q5FZGZ.js +4 -0
  34. package/dist/{migrations-43GTELB5.js.map → migrations-H3Q5FZGZ.js.map} +1 -1
  35. package/dist/migrations-VN5VTX3C.cjs +13 -0
  36. package/dist/{migrations-ZAYXZXON.cjs.map → migrations-VN5VTX3C.cjs.map} +1 -1
  37. package/dist/{plugin-manifest-BCMx9CAq.d.cts → plugin-manifest-Dpy8wxIB.d.cts} +2 -2
  38. package/dist/{plugin-manifest-BCMx9CAq.d.ts → plugin-manifest-Dpy8wxIB.d.ts} +2 -2
  39. package/dist/routes.cjs +25 -25
  40. package/dist/routes.js +5 -5
  41. package/dist/services.cjs +2 -2
  42. package/dist/services.js +1 -1
  43. package/dist/templates.cjs +17 -17
  44. package/dist/templates.js +2 -2
  45. package/dist/types.d.cts +1 -1
  46. package/dist/types.d.ts +1 -1
  47. package/dist/utils.cjs +11 -11
  48. package/dist/utils.js +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-ARLXQU2S.cjs.map +0 -1
  51. package/dist/chunk-DN45O5XV.js.map +0 -1
  52. package/dist/chunk-MF7DWI5P.cjs.map +0 -1
  53. package/dist/chunk-RP66TPEJ.js.map +0 -1
  54. package/dist/migrations-43GTELB5.js +0 -4
  55. package/dist/migrations-ZAYXZXON.cjs +0 -13
package/dist/index.cjs CHANGED
@@ -1,20 +1,21 @@
1
1
  'use strict';
2
2
 
3
- var chunkARLXQU2S_cjs = require('./chunk-ARLXQU2S.cjs');
3
+ var chunk5OLN5JO3_cjs = require('./chunk-5OLN5JO3.cjs');
4
4
  var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
5
- var chunk5NCBFP37_cjs = require('./chunk-5NCBFP37.cjs');
5
+ var chunkQG3YQKL4_cjs = require('./chunk-QG3YQKL4.cjs');
6
6
  var chunkILZ3DP4I_cjs = require('./chunk-ILZ3DP4I.cjs');
7
- var chunkW4CE7XME_cjs = require('./chunk-W4CE7XME.cjs');
8
- var chunkXR6XACXJ_cjs = require('./chunk-XR6XACXJ.cjs');
9
- var chunkMF7DWI5P_cjs = require('./chunk-MF7DWI5P.cjs');
7
+ var chunk63LV4YVI_cjs = require('./chunk-63LV4YVI.cjs');
8
+ var chunkYIXSSJWD_cjs = require('./chunk-YIXSSJWD.cjs');
9
+ var chunkAZLU3ROK_cjs = require('./chunk-AZLU3ROK.cjs');
10
10
  var chunkDTLB6UIH_cjs = require('./chunk-DTLB6UIH.cjs');
11
- var chunkW2IAEG4W_cjs = require('./chunk-W2IAEG4W.cjs');
11
+ var chunkH2X4BFCW_cjs = require('./chunk-H2X4BFCW.cjs');
12
12
  require('./chunk-P3XDZL6Q.cjs');
13
13
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
14
14
  var chunkKYGRJCZM_cjs = require('./chunk-KYGRJCZM.cjs');
15
15
  require('./chunk-IGJUBJBW.cjs');
16
16
  var hono = require('hono');
17
17
  var html = require('hono/html');
18
+ var zod = require('zod');
18
19
  var d1 = require('drizzle-orm/d1');
19
20
 
20
21
  // src/plugins/core-plugins/database-tools-plugin/services/database-service.ts
@@ -232,7 +233,7 @@ var DatabaseToolsService = class {
232
233
  };
233
234
 
234
235
  // src/templates/pages/admin-database-table.template.ts
235
- chunkMF7DWI5P_cjs.init_admin_layout_catalyst_template();
236
+ chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
236
237
  function renderDatabaseTablePage(data) {
237
238
  const totalPages = Math.ceil(data.totalRows / data.pageSize);
238
239
  const startRow = (data.currentPage - 1) * data.pageSize + 1;
@@ -481,7 +482,7 @@ function renderDatabaseTablePage(data) {
481
482
  user: data.user,
482
483
  content: pageContent
483
484
  };
484
- return chunkMF7DWI5P_cjs.renderAdminLayoutCatalyst(layoutData);
485
+ return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
485
486
  }
486
487
  function generatePageNumbers(currentPage, totalPages) {
487
488
  const pages = [];
@@ -556,7 +557,7 @@ function formatCellValue(value) {
556
557
  // src/plugins/core-plugins/database-tools-plugin/admin-routes.ts
557
558
  function createDatabaseToolsAdminRoutes() {
558
559
  const router2 = new hono.Hono();
559
- router2.use("*", chunk5NCBFP37_cjs.requireAuth());
560
+ router2.use("*", chunkQG3YQKL4_cjs.requireAuth());
560
561
  router2.get("/api/stats", async (c) => {
561
562
  try {
562
563
  const user = c.get("user");
@@ -737,7 +738,7 @@ function createDatabaseToolsAdminRoutes() {
737
738
  return router2;
738
739
  }
739
740
  function createEmailPlugin() {
740
- const builder = chunkARLXQU2S_cjs.PluginBuilder.create({
741
+ const builder = chunk5OLN5JO3_cjs.PluginBuilder.create({
741
742
  name: "email",
742
743
  version: "1.0.0-beta.1",
743
744
  description: "Send transactional emails using Resend"
@@ -990,7 +991,7 @@ function createEmailPlugin() {
990
991
  role: user.role ?? "admin"
991
992
  } : void 0;
992
993
  return c.html(
993
- chunkMF7DWI5P_cjs.renderAdminLayout({
994
+ chunkAZLU3ROK_cjs.renderAdminLayout({
994
995
  title: "Email Settings",
995
996
  content: contentHTML,
996
997
  user: templateUser,
@@ -1110,17 +1111,1029 @@ function createEmailPlugin() {
1110
1111
  }
1111
1112
  var emailPlugin = createEmailPlugin();
1112
1113
 
1114
+ // src/plugins/core-plugins/otp-login-plugin/otp-service.ts
1115
+ var OTPService = class {
1116
+ constructor(db) {
1117
+ this.db = db;
1118
+ }
1119
+ /**
1120
+ * Generate a secure random OTP code
1121
+ */
1122
+ generateCode(length = 6) {
1123
+ const digits = "0123456789";
1124
+ let code = "";
1125
+ for (let i = 0; i < length; i++) {
1126
+ const randomValues = new Uint8Array(1);
1127
+ crypto.getRandomValues(randomValues);
1128
+ const randomValue = randomValues[0] ?? 0;
1129
+ code += digits[randomValue % digits.length];
1130
+ }
1131
+ return code;
1132
+ }
1133
+ /**
1134
+ * Create and store a new OTP code
1135
+ */
1136
+ async createOTPCode(email, settings, ipAddress, userAgent) {
1137
+ const code = this.generateCode(settings.codeLength);
1138
+ const id = crypto.randomUUID();
1139
+ const now = Date.now();
1140
+ const expiresAt = now + settings.codeExpiryMinutes * 60 * 1e3;
1141
+ const otpCode = {
1142
+ id,
1143
+ user_email: email.toLowerCase(),
1144
+ code,
1145
+ expires_at: expiresAt,
1146
+ used: 0,
1147
+ used_at: null,
1148
+ ip_address: ipAddress || null,
1149
+ user_agent: userAgent || null,
1150
+ attempts: 0,
1151
+ created_at: now
1152
+ };
1153
+ await this.db.prepare(`
1154
+ INSERT INTO otp_codes (
1155
+ id, user_email, code, expires_at, used, used_at,
1156
+ ip_address, user_agent, attempts, created_at
1157
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1158
+ `).bind(
1159
+ otpCode.id,
1160
+ otpCode.user_email,
1161
+ otpCode.code,
1162
+ otpCode.expires_at,
1163
+ otpCode.used,
1164
+ otpCode.used_at,
1165
+ otpCode.ip_address,
1166
+ otpCode.user_agent,
1167
+ otpCode.attempts,
1168
+ otpCode.created_at
1169
+ ).run();
1170
+ return otpCode;
1171
+ }
1172
+ /**
1173
+ * Verify an OTP code
1174
+ */
1175
+ async verifyCode(email, code, settings) {
1176
+ const normalizedEmail = email.toLowerCase();
1177
+ const now = Date.now();
1178
+ const otpCode = await this.db.prepare(`
1179
+ SELECT * FROM otp_codes
1180
+ WHERE user_email = ? AND code = ? AND used = 0
1181
+ ORDER BY created_at DESC
1182
+ LIMIT 1
1183
+ `).bind(normalizedEmail, code).first();
1184
+ if (!otpCode) {
1185
+ return { valid: false, error: "Invalid or expired code" };
1186
+ }
1187
+ if (now > otpCode.expires_at) {
1188
+ return { valid: false, error: "Code has expired" };
1189
+ }
1190
+ if (otpCode.attempts >= settings.maxAttempts) {
1191
+ return { valid: false, error: "Maximum attempts exceeded" };
1192
+ }
1193
+ await this.db.prepare(`
1194
+ UPDATE otp_codes
1195
+ SET used = 1, used_at = ?, attempts = attempts + 1
1196
+ WHERE id = ?
1197
+ `).bind(now, otpCode.id).run();
1198
+ return { valid: true };
1199
+ }
1200
+ /**
1201
+ * Increment failed attempt count
1202
+ */
1203
+ async incrementAttempts(email, code) {
1204
+ const normalizedEmail = email.toLowerCase();
1205
+ const result = await this.db.prepare(`
1206
+ UPDATE otp_codes
1207
+ SET attempts = attempts + 1
1208
+ WHERE user_email = ? AND code = ? AND used = 0
1209
+ RETURNING attempts
1210
+ `).bind(normalizedEmail, code).first();
1211
+ return result?.attempts || 0;
1212
+ }
1213
+ /**
1214
+ * Check rate limiting
1215
+ */
1216
+ async checkRateLimit(email, settings) {
1217
+ const normalizedEmail = email.toLowerCase();
1218
+ const oneHourAgo = Date.now() - 60 * 60 * 1e3;
1219
+ const result = await this.db.prepare(`
1220
+ SELECT COUNT(*) as count
1221
+ FROM otp_codes
1222
+ WHERE user_email = ? AND created_at > ?
1223
+ `).bind(normalizedEmail, oneHourAgo).first();
1224
+ const count = result?.count || 0;
1225
+ return count < settings.rateLimitPerHour;
1226
+ }
1227
+ /**
1228
+ * Get recent OTP requests for activity log
1229
+ */
1230
+ async getRecentRequests(limit = 50) {
1231
+ const result = await this.db.prepare(`
1232
+ SELECT * FROM otp_codes
1233
+ ORDER BY created_at DESC
1234
+ LIMIT ?
1235
+ `).bind(limit).all();
1236
+ const rows = result.results || [];
1237
+ return rows.map((row) => this.mapRowToOTP(row));
1238
+ }
1239
+ /**
1240
+ * Clean up expired codes (for maintenance)
1241
+ */
1242
+ async cleanupExpiredCodes() {
1243
+ const now = Date.now();
1244
+ const result = await this.db.prepare(`
1245
+ DELETE FROM otp_codes
1246
+ WHERE expires_at < ? OR (used = 1 AND used_at < ?)
1247
+ `).bind(now, now - 30 * 24 * 60 * 60 * 1e3).run();
1248
+ return result.meta.changes || 0;
1249
+ }
1250
+ mapRowToOTP(row) {
1251
+ return {
1252
+ id: String(row.id),
1253
+ user_email: String(row.user_email),
1254
+ code: String(row.code),
1255
+ expires_at: Number(row.expires_at ?? Date.now()),
1256
+ used: Number(row.used ?? 0),
1257
+ used_at: row.used_at === null || row.used_at === void 0 ? null : Number(row.used_at),
1258
+ ip_address: typeof row.ip_address === "string" ? row.ip_address : null,
1259
+ user_agent: typeof row.user_agent === "string" ? row.user_agent : null,
1260
+ attempts: Number(row.attempts ?? 0),
1261
+ created_at: Number(row.created_at ?? Date.now())
1262
+ };
1263
+ }
1264
+ /**
1265
+ * Get OTP statistics
1266
+ */
1267
+ async getStats(days = 7) {
1268
+ const since = Date.now() - days * 24 * 60 * 60 * 1e3;
1269
+ const stats = await this.db.prepare(`
1270
+ SELECT
1271
+ COUNT(*) as total,
1272
+ SUM(CASE WHEN used = 1 THEN 1 ELSE 0 END) as successful,
1273
+ SUM(CASE WHEN attempts >= 3 AND used = 0 THEN 1 ELSE 0 END) as failed,
1274
+ SUM(CASE WHEN expires_at < ? AND used = 0 THEN 1 ELSE 0 END) as expired
1275
+ FROM otp_codes
1276
+ WHERE created_at > ?
1277
+ `).bind(Date.now(), since).first();
1278
+ return {
1279
+ total: stats?.total || 0,
1280
+ successful: stats?.successful || 0,
1281
+ failed: stats?.failed || 0,
1282
+ expired: stats?.expired || 0
1283
+ };
1284
+ }
1285
+ };
1286
+
1287
+ // src/plugins/core-plugins/otp-login-plugin/email-templates.ts
1288
+ function renderOTPEmailHTML(data) {
1289
+ return `<!DOCTYPE html>
1290
+ <html>
1291
+ <head>
1292
+ <meta charset="utf-8">
1293
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1294
+ <title>Your Login Code</title>
1295
+ </head>
1296
+ <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f5f5f5;">
1297
+
1298
+ <div style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
1299
+
1300
+ ${data.logoUrl ? `
1301
+ <div style="text-align: center; padding: 30px 20px 20px;">
1302
+ <img src="${data.logoUrl}" alt="Logo" style="max-width: 150px; height: auto;">
1303
+ </div>
1304
+ ` : ""}
1305
+
1306
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 30px; text-align: center;">
1307
+ <h1 style="margin: 0 0 10px 0; font-size: 32px; font-weight: 600;">Your Login Code</h1>
1308
+ <p style="margin: 0; opacity: 0.95; font-size: 16px;">Enter this code to sign in to ${data.appName}</p>
1309
+ </div>
1310
+
1311
+ <div style="padding: 40px 30px;">
1312
+ <div style="background: #f8f9fa; border: 2px dashed #667eea; border-radius: 12px; padding: 30px; text-align: center; margin: 0 0 30px 0;">
1313
+ <div style="font-size: 56px; font-weight: bold; letter-spacing: 12px; color: #667eea; font-family: 'Courier New', Courier, monospace; line-height: 1;">
1314
+ ${data.code}
1315
+ </div>
1316
+ </div>
1317
+
1318
+ <div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 16px 20px; margin: 0 0 30px 0; border-radius: 6px;">
1319
+ <p style="margin: 0; font-size: 14px; color: #856404;">
1320
+ <strong>\u26A0\uFE0F This code expires in ${data.expiryMinutes} minutes</strong>
1321
+ </p>
1322
+ </div>
1323
+
1324
+ <div style="margin: 0 0 30px 0;">
1325
+ <h3 style="color: #333; margin: 0 0 15px 0; font-size: 18px;">Quick Tips:</h3>
1326
+ <ul style="color: #666; font-size: 14px; line-height: 1.8; margin: 0; padding-left: 20px;">
1327
+ <li>Enter the code exactly as shown (${data.codeLength} digits)</li>
1328
+ <li>The code can only be used once</li>
1329
+ <li>You have ${data.maxAttempts} attempts to enter the correct code</li>
1330
+ <li>Request a new code if this one expires</li>
1331
+ </ul>
1332
+ </div>
1333
+
1334
+ <div style="background: #e8f4ff; border-radius: 8px; padding: 20px; margin: 0 0 30px 0;">
1335
+ <p style="margin: 0 0 10px 0; font-size: 14px; color: #0066cc; font-weight: 600;">
1336
+ \u{1F512} Security Notice
1337
+ </p>
1338
+ <p style="margin: 0; font-size: 13px; color: #004080; line-height: 1.6;">
1339
+ Never share this code with anyone. ${data.appName} will never ask you for this code via phone, email, or social media.
1340
+ </p>
1341
+ </div>
1342
+ </div>
1343
+
1344
+ <div style="border-top: 1px solid #eee; padding: 30px; background: #f8f9fa;">
1345
+ <p style="margin: 0 0 15px 0; font-size: 14px; color: #666; text-align: center;">
1346
+ <strong>Didn't request this code?</strong><br>
1347
+ Someone may have entered your email by mistake. You can safely ignore this email.
1348
+ </p>
1349
+
1350
+ <div style="text-align: center; color: #999; font-size: 12px; line-height: 1.6;">
1351
+ <p style="margin: 5px 0;">This email was sent to ${data.email}</p>
1352
+ ${data.ipAddress ? `<p style="margin: 5px 0;">IP Address: ${data.ipAddress}</p>` : ""}
1353
+ <p style="margin: 5px 0;">Time: ${data.timestamp}</p>
1354
+ </div>
1355
+ </div>
1356
+
1357
+ </div>
1358
+
1359
+ <div style="text-align: center; padding: 20px; color: #999; font-size: 12px;">
1360
+ <p style="margin: 0;">&copy; ${(/* @__PURE__ */ new Date()).getFullYear()} ${data.appName}. All rights reserved.</p>
1361
+ </div>
1362
+
1363
+ </body>
1364
+ </html>`;
1365
+ }
1366
+ function renderOTPEmailText(data) {
1367
+ return `Your Login Code for ${data.appName}
1368
+
1369
+ Your one-time verification code is:
1370
+
1371
+ ${data.code}
1372
+
1373
+ This code expires in ${data.expiryMinutes} minutes.
1374
+
1375
+ Quick Tips:
1376
+ \u2022 Enter the code exactly as shown (${data.codeLength} digits)
1377
+ \u2022 The code can only be used once
1378
+ \u2022 You have ${data.maxAttempts} attempts to enter the correct code
1379
+ \u2022 Request a new code if this one expires
1380
+
1381
+ Security Notice:
1382
+ Never share this code with anyone. ${data.appName} will never ask you for this code via phone, email, or social media.
1383
+
1384
+ Didn't request this code?
1385
+ Someone may have entered your email by mistake. You can safely ignore this email.
1386
+
1387
+ ---
1388
+ This email was sent to ${data.email}
1389
+ ${data.ipAddress ? `IP Address: ${data.ipAddress}` : ""}
1390
+ Time: ${data.timestamp}
1391
+
1392
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${data.appName}. All rights reserved.`;
1393
+ }
1394
+ function renderOTPEmail(data) {
1395
+ return {
1396
+ html: renderOTPEmailHTML(data),
1397
+ text: renderOTPEmailText(data)
1398
+ };
1399
+ }
1400
+
1401
+ // src/plugins/core-plugins/otp-login-plugin/index.ts
1402
+ var otpRequestSchema = zod.z.object({
1403
+ email: zod.z.string().email("Valid email is required")
1404
+ });
1405
+ var otpVerifySchema = zod.z.object({
1406
+ email: zod.z.string().email("Valid email is required"),
1407
+ code: zod.z.string().min(4).max(8)
1408
+ });
1409
+ var DEFAULT_SETTINGS = {
1410
+ codeLength: 6,
1411
+ codeExpiryMinutes: 10,
1412
+ maxAttempts: 3,
1413
+ rateLimitPerHour: 5,
1414
+ allowNewUserRegistration: false,
1415
+ appName: "SonicJS"
1416
+ };
1417
+ function createOTPLoginPlugin() {
1418
+ const builder = chunk5OLN5JO3_cjs.PluginBuilder.create({
1419
+ name: "otp-login",
1420
+ version: "1.0.0-beta.1",
1421
+ description: "Passwordless authentication via email one-time codes"
1422
+ });
1423
+ builder.metadata({
1424
+ author: {
1425
+ name: "SonicJS Team",
1426
+ email: "team@sonicjs.com"
1427
+ },
1428
+ license: "MIT",
1429
+ compatibility: "^2.0.0"
1430
+ });
1431
+ const otpAPI = new hono.Hono();
1432
+ otpAPI.post("/request", async (c) => {
1433
+ try {
1434
+ const body = await c.req.json();
1435
+ const validation = otpRequestSchema.safeParse(body);
1436
+ if (!validation.success) {
1437
+ return c.json({
1438
+ error: "Validation failed",
1439
+ details: validation.error.issues
1440
+ }, 400);
1441
+ }
1442
+ const { email } = validation.data;
1443
+ const normalizedEmail = email.toLowerCase();
1444
+ const db = c.env.DB;
1445
+ const otpService = new OTPService(db);
1446
+ const settings = { ...DEFAULT_SETTINGS };
1447
+ const canRequest = await otpService.checkRateLimit(normalizedEmail, settings);
1448
+ if (!canRequest) {
1449
+ return c.json({
1450
+ error: "Too many requests. Please try again in an hour."
1451
+ }, 429);
1452
+ }
1453
+ const user = await db.prepare(`
1454
+ SELECT id, email, role, is_active
1455
+ FROM users
1456
+ WHERE email = ?
1457
+ `).bind(normalizedEmail).first();
1458
+ if (!user && !settings.allowNewUserRegistration) {
1459
+ return c.json({
1460
+ message: "If an account exists for this email, you will receive a verification code shortly.",
1461
+ expiresIn: settings.codeExpiryMinutes * 60
1462
+ });
1463
+ }
1464
+ if (user && !user.is_active) {
1465
+ return c.json({
1466
+ error: "This account has been deactivated."
1467
+ }, 403);
1468
+ }
1469
+ const ipAddress = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
1470
+ const userAgent = c.req.header("user-agent") || "unknown";
1471
+ const otpCode = await otpService.createOTPCode(
1472
+ normalizedEmail,
1473
+ settings,
1474
+ ipAddress,
1475
+ userAgent
1476
+ );
1477
+ try {
1478
+ const isDevMode = c.env.ENVIRONMENT === "development";
1479
+ if (isDevMode) {
1480
+ console.log(`[DEV] OTP Code for ${normalizedEmail}: ${otpCode.code}`);
1481
+ }
1482
+ const emailContent = renderOTPEmail({
1483
+ code: otpCode.code,
1484
+ expiryMinutes: settings.codeExpiryMinutes,
1485
+ codeLength: settings.codeLength,
1486
+ maxAttempts: settings.maxAttempts,
1487
+ email: normalizedEmail,
1488
+ ipAddress,
1489
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1490
+ appName: settings.appName
1491
+ });
1492
+ const emailPlugin2 = await db.prepare(`
1493
+ SELECT settings FROM plugins WHERE id = 'email' AND status = 'active'
1494
+ `).first();
1495
+ if (emailPlugin2?.settings) {
1496
+ const emailSettings = JSON.parse(emailPlugin2.settings);
1497
+ if (emailSettings.apiKey && emailSettings.fromEmail && emailSettings.fromName) {
1498
+ const emailResponse = await fetch("https://api.resend.com/emails", {
1499
+ method: "POST",
1500
+ headers: {
1501
+ "Authorization": `Bearer ${emailSettings.apiKey}`,
1502
+ "Content-Type": "application/json"
1503
+ },
1504
+ body: JSON.stringify({
1505
+ from: `${emailSettings.fromName} <${emailSettings.fromEmail}>`,
1506
+ to: [normalizedEmail],
1507
+ subject: `Your login code for ${settings.appName}`,
1508
+ html: emailContent.html,
1509
+ text: emailContent.text,
1510
+ reply_to: emailSettings.replyTo || emailSettings.fromEmail
1511
+ })
1512
+ });
1513
+ if (!emailResponse.ok) {
1514
+ const errorData = await emailResponse.json();
1515
+ console.error("Failed to send OTP email via Resend:", errorData);
1516
+ }
1517
+ } else {
1518
+ console.warn("Email plugin is not fully configured (missing apiKey, fromEmail, or fromName)");
1519
+ }
1520
+ } else {
1521
+ console.warn("Email plugin is not active or has no settings configured");
1522
+ }
1523
+ const response = {
1524
+ message: "If an account exists for this email, you will receive a verification code shortly.",
1525
+ expiresIn: settings.codeExpiryMinutes * 60
1526
+ };
1527
+ if (isDevMode) {
1528
+ response.dev_code = otpCode.code;
1529
+ }
1530
+ return c.json(response);
1531
+ } catch (emailError) {
1532
+ console.error("Error sending OTP email:", emailError);
1533
+ return c.json({
1534
+ error: "Failed to send verification code. Please try again."
1535
+ }, 500);
1536
+ }
1537
+ } catch (error) {
1538
+ console.error("OTP request error:", error);
1539
+ return c.json({
1540
+ error: "An error occurred. Please try again."
1541
+ }, 500);
1542
+ }
1543
+ });
1544
+ otpAPI.post("/verify", async (c) => {
1545
+ try {
1546
+ const body = await c.req.json();
1547
+ const validation = otpVerifySchema.safeParse(body);
1548
+ if (!validation.success) {
1549
+ return c.json({
1550
+ error: "Validation failed",
1551
+ details: validation.error.issues
1552
+ }, 400);
1553
+ }
1554
+ const { email, code } = validation.data;
1555
+ const normalizedEmail = email.toLowerCase();
1556
+ const db = c.env.DB;
1557
+ const otpService = new OTPService(db);
1558
+ const settings = { ...DEFAULT_SETTINGS };
1559
+ const verification = await otpService.verifyCode(normalizedEmail, code, settings);
1560
+ if (!verification.valid) {
1561
+ await otpService.incrementAttempts(normalizedEmail, code);
1562
+ return c.json({
1563
+ error: verification.error || "Invalid code",
1564
+ attemptsRemaining: verification.attemptsRemaining
1565
+ }, 401);
1566
+ }
1567
+ const user = await db.prepare(`
1568
+ SELECT id, email, role, is_active
1569
+ FROM users
1570
+ WHERE email = ?
1571
+ `).bind(normalizedEmail).first();
1572
+ if (!user) {
1573
+ return c.json({
1574
+ error: "User not found"
1575
+ }, 404);
1576
+ }
1577
+ if (!user.is_active) {
1578
+ return c.json({
1579
+ error: "Account is deactivated"
1580
+ }, 403);
1581
+ }
1582
+ return c.json({
1583
+ success: true,
1584
+ user: {
1585
+ id: user.id,
1586
+ email: user.email,
1587
+ role: user.role
1588
+ },
1589
+ message: "Authentication successful"
1590
+ });
1591
+ } catch (error) {
1592
+ console.error("OTP verify error:", error);
1593
+ return c.json({
1594
+ error: "An error occurred. Please try again."
1595
+ }, 500);
1596
+ }
1597
+ });
1598
+ otpAPI.post("/resend", async (c) => {
1599
+ try {
1600
+ const body = await c.req.json();
1601
+ const validation = otpRequestSchema.safeParse(body);
1602
+ if (!validation.success) {
1603
+ return c.json({
1604
+ error: "Validation failed",
1605
+ details: validation.error.issues
1606
+ }, 400);
1607
+ }
1608
+ return otpAPI.fetch(
1609
+ new Request(c.req.url.replace("/resend", "/request"), {
1610
+ method: "POST",
1611
+ headers: c.req.raw.headers,
1612
+ body: JSON.stringify({ email: validation.data.email })
1613
+ }),
1614
+ c.env
1615
+ );
1616
+ } catch (error) {
1617
+ console.error("OTP resend error:", error);
1618
+ return c.json({
1619
+ error: "An error occurred. Please try again."
1620
+ }, 500);
1621
+ }
1622
+ });
1623
+ builder.addRoute("/auth/otp", otpAPI, {
1624
+ description: "OTP authentication endpoints",
1625
+ requiresAuth: false,
1626
+ priority: 100
1627
+ });
1628
+ const adminRoutes = new hono.Hono();
1629
+ adminRoutes.get("/settings", async (c) => {
1630
+ const user = c.get("user");
1631
+ const contentHTML = await html.html`
1632
+ <div class="p-8">
1633
+ <div class="mb-8">
1634
+ <h1 class="text-3xl font-bold mb-2">OTP Login Settings</h1>
1635
+ <p class="text-zinc-600 dark:text-zinc-400">Configure passwordless authentication via email codes</p>
1636
+ </div>
1637
+
1638
+ <div class="max-w-3xl">
1639
+ <div class="backdrop-blur-md bg-black/20 border border-white/10 shadow-xl rounded-xl p-6 mb-6">
1640
+ <h2 class="text-xl font-semibold mb-4">Code Settings</h2>
1641
+
1642
+ <form id="otpSettingsForm" class="space-y-6">
1643
+ <div>
1644
+ <label for="codeLength" class="block text-sm font-medium mb-2">
1645
+ Code Length
1646
+ </label>
1647
+ <input
1648
+ type="number"
1649
+ id="codeLength"
1650
+ name="codeLength"
1651
+ min="4"
1652
+ max="8"
1653
+ value="6"
1654
+ class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
1655
+ />
1656
+ <p class="text-xs text-zinc-500 mt-1">Number of digits in OTP code (4-8)</p>
1657
+ </div>
1658
+
1659
+ <div>
1660
+ <label for="codeExpiryMinutes" class="block text-sm font-medium mb-2">
1661
+ Code Expiry (minutes)
1662
+ </label>
1663
+ <input
1664
+ type="number"
1665
+ id="codeExpiryMinutes"
1666
+ name="codeExpiryMinutes"
1667
+ min="5"
1668
+ max="60"
1669
+ value="10"
1670
+ class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
1671
+ />
1672
+ <p class="text-xs text-zinc-500 mt-1">How long codes remain valid (5-60 minutes)</p>
1673
+ </div>
1674
+
1675
+ <div>
1676
+ <label for="maxAttempts" class="block text-sm font-medium mb-2">
1677
+ Maximum Attempts
1678
+ </label>
1679
+ <input
1680
+ type="number"
1681
+ id="maxAttempts"
1682
+ name="maxAttempts"
1683
+ min="3"
1684
+ max="10"
1685
+ value="3"
1686
+ class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
1687
+ />
1688
+ <p class="text-xs text-zinc-500 mt-1">Max verification attempts before invalidation</p>
1689
+ </div>
1690
+
1691
+ <div>
1692
+ <label for="rateLimitPerHour" class="block text-sm font-medium mb-2">
1693
+ Rate Limit (per hour)
1694
+ </label>
1695
+ <input
1696
+ type="number"
1697
+ id="rateLimitPerHour"
1698
+ name="rateLimitPerHour"
1699
+ min="3"
1700
+ max="20"
1701
+ value="5"
1702
+ class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
1703
+ />
1704
+ <p class="text-xs text-zinc-500 mt-1">Max code requests per email per hour</p>
1705
+ </div>
1706
+
1707
+ <div class="flex items-center">
1708
+ <input
1709
+ type="checkbox"
1710
+ id="allowNewUserRegistration"
1711
+ name="allowNewUserRegistration"
1712
+ class="w-4 h-4 rounded border-white/10"
1713
+ />
1714
+ <label for="allowNewUserRegistration" class="ml-2 text-sm">
1715
+ Allow new user registration via OTP
1716
+ </label>
1717
+ </div>
1718
+
1719
+ <div class="flex gap-3 pt-4">
1720
+ <button
1721
+ type="submit"
1722
+ class="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-purple-700 transition-all"
1723
+ >
1724
+ Save Settings
1725
+ </button>
1726
+ <button
1727
+ type="button"
1728
+ id="testOTPBtn"
1729
+ class="px-6 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg font-medium transition-all"
1730
+ >
1731
+ Send Test Code
1732
+ </button>
1733
+ </div>
1734
+ </form>
1735
+ </div>
1736
+
1737
+ <div id="statusMessage" class="hidden backdrop-blur-md bg-black/20 border border-white/10 rounded-xl p-4 mb-6"></div>
1738
+
1739
+ <div class="backdrop-blur-md bg-blue-500/10 border border-blue-500/20 rounded-xl p-6">
1740
+ <h3 class="font-semibold text-blue-400 mb-3">
1741
+ 🔢 Features
1742
+ </h3>
1743
+ <ul class="text-sm text-blue-200 space-y-2">
1744
+ <li>✓ Passwordless authentication</li>
1745
+ <li>✓ Secure random code generation</li>
1746
+ <li>✓ Rate limiting protection</li>
1747
+ <li>✓ Brute force prevention</li>
1748
+ <li>✓ Mobile-friendly UX</li>
1749
+ </ul>
1750
+ </div>
1751
+ </div>
1752
+ </div>
1753
+
1754
+ <script>
1755
+ document.getElementById('otpSettingsForm').addEventListener('submit', async (e) => {
1756
+ e.preventDefault()
1757
+ const statusEl = document.getElementById('statusMessage')
1758
+ statusEl.className = 'backdrop-blur-md bg-green-500/20 border border-green-500/30 rounded-xl p-4 mb-6'
1759
+ statusEl.innerHTML = '✅ Settings saved successfully!'
1760
+ statusEl.classList.remove('hidden')
1761
+ setTimeout(() => statusEl.classList.add('hidden'), 3000)
1762
+ })
1763
+
1764
+ document.getElementById('testOTPBtn').addEventListener('click', async () => {
1765
+ const email = prompt('Enter email address for test:')
1766
+ if (!email) return
1767
+
1768
+ const statusEl = document.getElementById('statusMessage')
1769
+ statusEl.className = 'backdrop-blur-md bg-blue-500/20 border border-blue-500/30 rounded-xl p-4 mb-6'
1770
+ statusEl.innerHTML = '📧 Sending test code...'
1771
+ statusEl.classList.remove('hidden')
1772
+
1773
+ try {
1774
+ const response = await fetch('/auth/otp/request', {
1775
+ method: 'POST',
1776
+ headers: { 'Content-Type': 'application/json' },
1777
+ body: JSON.stringify({ email })
1778
+ })
1779
+
1780
+ const data = await response.json()
1781
+
1782
+ if (response.ok) {
1783
+ statusEl.className = 'backdrop-blur-md bg-green-500/20 border border-green-500/30 rounded-xl p-4 mb-6'
1784
+ statusEl.innerHTML = '✅ Test code sent!' + (data.dev_code ? \` Code: <strong>\${data.dev_code}</strong>\` : '')
1785
+ } else {
1786
+ throw new Error(data.error || 'Failed')
1787
+ }
1788
+ } catch (error) {
1789
+ statusEl.className = 'backdrop-blur-md bg-red-500/20 border border-red-500/30 rounded-xl p-4 mb-6'
1790
+ statusEl.innerHTML = '❌ Failed to send test code'
1791
+ }
1792
+ })
1793
+ </script>
1794
+ `;
1795
+ const templateUser = user ? {
1796
+ name: user.name ?? user.email ?? "Admin",
1797
+ email: user.email ?? "admin@sonicjs.com",
1798
+ role: user.role ?? "admin"
1799
+ } : void 0;
1800
+ return c.html(
1801
+ chunkAZLU3ROK_cjs.adminLayoutV2({
1802
+ title: "OTP Login Settings",
1803
+ content: contentHTML,
1804
+ user: templateUser,
1805
+ currentPath: "/admin/plugins/otp-login/settings"
1806
+ })
1807
+ );
1808
+ });
1809
+ builder.addRoute("/admin/plugins/otp-login", adminRoutes, {
1810
+ description: "OTP login admin interface",
1811
+ requiresAuth: true,
1812
+ priority: 85
1813
+ });
1814
+ builder.addMenuItem("OTP Login", "/admin/plugins/otp-login/settings", {
1815
+ icon: "key",
1816
+ order: 85,
1817
+ permissions: ["otp:manage"]
1818
+ });
1819
+ builder.lifecycle({
1820
+ activate: async () => {
1821
+ console.info("\u2705 OTP Login plugin activated");
1822
+ },
1823
+ deactivate: async () => {
1824
+ console.info("\u274C OTP Login plugin deactivated");
1825
+ }
1826
+ });
1827
+ return builder.build();
1828
+ }
1829
+ var otpLoginPlugin = createOTPLoginPlugin();
1830
+ var magicLinkRequestSchema = zod.z.object({
1831
+ email: zod.z.string().email("Valid email is required")
1832
+ });
1833
+ function createMagicLinkAuthPlugin() {
1834
+ const magicLinkRoutes = new hono.Hono();
1835
+ magicLinkRoutes.post("/request", async (c) => {
1836
+ try {
1837
+ const body = await c.req.json();
1838
+ const validation = magicLinkRequestSchema.safeParse(body);
1839
+ if (!validation.success) {
1840
+ return c.json({
1841
+ error: "Validation failed",
1842
+ details: validation.error.issues
1843
+ }, 400);
1844
+ }
1845
+ const { email } = validation.data;
1846
+ const normalizedEmail = email.toLowerCase();
1847
+ const db = c.env.DB;
1848
+ const oneHourAgo = Date.now() - 60 * 60 * 1e3;
1849
+ const recentLinks = await db.prepare(`
1850
+ SELECT COUNT(*) as count
1851
+ FROM magic_links
1852
+ WHERE user_email = ? AND created_at > ?
1853
+ `).bind(normalizedEmail, oneHourAgo).first();
1854
+ const rateLimitPerHour = 5;
1855
+ if (recentLinks && recentLinks.count >= rateLimitPerHour) {
1856
+ return c.json({
1857
+ error: "Too many requests. Please try again later."
1858
+ }, 429);
1859
+ }
1860
+ const user = await db.prepare(`
1861
+ SELECT id, email, role, is_active
1862
+ FROM users
1863
+ WHERE email = ?
1864
+ `).bind(normalizedEmail).first();
1865
+ const allowNewUsers = false;
1866
+ if (!user && !allowNewUsers) {
1867
+ return c.json({
1868
+ message: "If an account exists for this email, you will receive a magic link shortly."
1869
+ });
1870
+ }
1871
+ if (user && !user.is_active) {
1872
+ return c.json({
1873
+ error: "This account has been deactivated."
1874
+ }, 403);
1875
+ }
1876
+ const token = crypto.randomUUID() + "-" + crypto.randomUUID();
1877
+ const tokenId = crypto.randomUUID();
1878
+ const linkExpiryMinutes = 15;
1879
+ const expiresAt = Date.now() + linkExpiryMinutes * 60 * 1e3;
1880
+ await db.prepare(`
1881
+ INSERT INTO magic_links (
1882
+ id, user_email, token, expires_at, used, created_at, ip_address, user_agent
1883
+ ) VALUES (?, ?, ?, ?, 0, ?, ?, ?)
1884
+ `).bind(
1885
+ tokenId,
1886
+ normalizedEmail,
1887
+ token,
1888
+ expiresAt,
1889
+ Date.now(),
1890
+ c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown",
1891
+ c.req.header("user-agent") || "unknown"
1892
+ ).run();
1893
+ const baseUrl = new URL(c.req.url).origin;
1894
+ const magicLink = `${baseUrl}/auth/magic-link/verify?token=${token}`;
1895
+ try {
1896
+ const emailPlugin2 = c.env.plugins?.get("email");
1897
+ if (emailPlugin2 && emailPlugin2.sendEmail) {
1898
+ await emailPlugin2.sendEmail({
1899
+ to: normalizedEmail,
1900
+ subject: "Your Magic Link to Sign In",
1901
+ html: renderMagicLinkEmail(magicLink, linkExpiryMinutes)
1902
+ });
1903
+ } else {
1904
+ console.error("Email plugin not available");
1905
+ console.log(`Magic link for ${normalizedEmail}: ${magicLink}`);
1906
+ }
1907
+ } catch (error) {
1908
+ console.error("Failed to send magic link email:", error);
1909
+ return c.json({
1910
+ error: "Failed to send email. Please try again later."
1911
+ }, 500);
1912
+ }
1913
+ return c.json({
1914
+ message: "If an account exists for this email, you will receive a magic link shortly.",
1915
+ // For development only - remove in production
1916
+ ...c.env.ENVIRONMENT === "development" && { dev_link: magicLink }
1917
+ });
1918
+ } catch (error) {
1919
+ console.error("Magic link request error:", error);
1920
+ return c.json({ error: "Failed to process request" }, 500);
1921
+ }
1922
+ });
1923
+ magicLinkRoutes.get("/verify", async (c) => {
1924
+ try {
1925
+ const token = c.req.query("token");
1926
+ if (!token) {
1927
+ return c.redirect("/auth/login?error=Invalid magic link");
1928
+ }
1929
+ const db = c.env.DB;
1930
+ const magicLink = await db.prepare(`
1931
+ SELECT * FROM magic_links
1932
+ WHERE token = ? AND used = 0
1933
+ `).bind(token).first();
1934
+ if (!magicLink) {
1935
+ return c.redirect("/auth/login?error=Invalid or expired magic link");
1936
+ }
1937
+ if (magicLink.expires_at < Date.now()) {
1938
+ return c.redirect("/auth/login?error=This magic link has expired");
1939
+ }
1940
+ let user = await db.prepare(`
1941
+ SELECT * FROM users WHERE email = ? AND is_active = 1
1942
+ `).bind(magicLink.user_email).first();
1943
+ const allowNewUsers = false;
1944
+ if (!user && allowNewUsers) {
1945
+ const userId = crypto.randomUUID();
1946
+ const username = magicLink.user_email.split("@")[0];
1947
+ const now = Date.now();
1948
+ await db.prepare(`
1949
+ INSERT INTO users (
1950
+ id, email, username, first_name, last_name,
1951
+ password_hash, role, is_active, created_at, updated_at
1952
+ ) VALUES (?, ?, ?, ?, ?, NULL, 'viewer', 1, ?, ?)
1953
+ `).bind(
1954
+ userId,
1955
+ magicLink.user_email,
1956
+ username,
1957
+ username,
1958
+ "",
1959
+ now,
1960
+ now
1961
+ ).run();
1962
+ user = {
1963
+ id: userId,
1964
+ email: magicLink.user_email,
1965
+ username,
1966
+ role: "viewer"
1967
+ };
1968
+ } else if (!user) {
1969
+ return c.redirect("/auth/login?error=No account found for this email");
1970
+ }
1971
+ await db.prepare(`
1972
+ UPDATE magic_links
1973
+ SET used = 1, used_at = ?
1974
+ WHERE id = ?
1975
+ `).bind(Date.now(), magicLink.id).run();
1976
+ const jwtToken = await chunkQG3YQKL4_cjs.AuthManager.generateToken(
1977
+ user.id,
1978
+ user.email,
1979
+ user.role
1980
+ );
1981
+ chunkQG3YQKL4_cjs.AuthManager.setAuthCookie(c, jwtToken);
1982
+ await db.prepare(`
1983
+ UPDATE users SET last_login_at = ? WHERE id = ?
1984
+ `).bind(Date.now(), user.id).run();
1985
+ return c.redirect("/admin/dashboard?message=Successfully signed in");
1986
+ } catch (error) {
1987
+ console.error("Magic link verification error:", error);
1988
+ return c.redirect("/auth/login?error=Authentication failed");
1989
+ }
1990
+ });
1991
+ return {
1992
+ name: "magic-link-auth",
1993
+ version: "1.0.0",
1994
+ description: "Passwordless authentication via email magic links",
1995
+ author: {
1996
+ name: "SonicJS Team",
1997
+ email: "team@sonicjs.com"
1998
+ },
1999
+ dependencies: ["email"],
2000
+ routes: [{
2001
+ path: "/auth/magic-link",
2002
+ handler: magicLinkRoutes,
2003
+ description: "Magic link authentication endpoints",
2004
+ requiresAuth: false
2005
+ }],
2006
+ async install(context) {
2007
+ console.log("Installing magic-link-auth plugin...");
2008
+ },
2009
+ async activate(context) {
2010
+ console.log("Magic link authentication activated");
2011
+ console.log("Users can now sign in via /auth/magic-link/request");
2012
+ },
2013
+ async deactivate(context) {
2014
+ console.log("Magic link authentication deactivated");
2015
+ },
2016
+ async uninstall(context) {
2017
+ console.log("Uninstalling magic-link-auth plugin...");
2018
+ }
2019
+ };
2020
+ }
2021
+ function renderMagicLinkEmail(magicLink, expiryMinutes) {
2022
+ return `
2023
+ <!DOCTYPE html>
2024
+ <html>
2025
+ <head>
2026
+ <meta charset="utf-8">
2027
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2028
+ <title>Your Magic Link</title>
2029
+ <style>
2030
+ body {
2031
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
2032
+ line-height: 1.6;
2033
+ color: #333;
2034
+ max-width: 600px;
2035
+ margin: 0 auto;
2036
+ padding: 20px;
2037
+ }
2038
+ .container {
2039
+ background: #ffffff;
2040
+ border-radius: 8px;
2041
+ padding: 40px;
2042
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
2043
+ }
2044
+ .header {
2045
+ text-align: center;
2046
+ margin-bottom: 30px;
2047
+ }
2048
+ .header h1 {
2049
+ color: #0ea5e9;
2050
+ margin: 0;
2051
+ font-size: 24px;
2052
+ }
2053
+ .content {
2054
+ margin-bottom: 30px;
2055
+ }
2056
+ .button {
2057
+ display: inline-block;
2058
+ padding: 14px 32px;
2059
+ background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
2060
+ color: #ffffff !important;
2061
+ text-decoration: none;
2062
+ border-radius: 6px;
2063
+ font-weight: 600;
2064
+ text-align: center;
2065
+ margin: 20px 0;
2066
+ }
2067
+ .button:hover {
2068
+ opacity: 0.9;
2069
+ }
2070
+ .expiry {
2071
+ color: #ef4444;
2072
+ font-size: 14px;
2073
+ margin-top: 20px;
2074
+ }
2075
+ .footer {
2076
+ margin-top: 40px;
2077
+ padding-top: 20px;
2078
+ border-top: 1px solid #e5e7eb;
2079
+ font-size: 12px;
2080
+ color: #6b7280;
2081
+ text-align: center;
2082
+ }
2083
+ .security-note {
2084
+ background: #fef3c7;
2085
+ border-left: 4px solid #f59e0b;
2086
+ padding: 12px 16px;
2087
+ margin-top: 20px;
2088
+ border-radius: 4px;
2089
+ font-size: 14px;
2090
+ }
2091
+ </style>
2092
+ </head>
2093
+ <body>
2094
+ <div class="container">
2095
+ <div class="header">
2096
+ <h1>\u{1F517} Your Magic Link</h1>
2097
+ </div>
2098
+
2099
+ <div class="content">
2100
+ <p>Hello!</p>
2101
+ <p>You requested a magic link to sign in to your account. Click the button below to continue:</p>
2102
+
2103
+ <div style="text-align: center;">
2104
+ <a href="${magicLink}" class="button">Sign In</a>
2105
+ </div>
2106
+
2107
+ <p class="expiry">\u23F0 This link expires in ${expiryMinutes} minutes</p>
2108
+
2109
+ <div class="security-note">
2110
+ <strong>Security Notice:</strong> If you didn't request this link, you can safely ignore this email.
2111
+ Someone may have entered your email address by mistake.
2112
+ </div>
2113
+ </div>
2114
+
2115
+ <div class="footer">
2116
+ <p>This is an automated email from SonicJS.</p>
2117
+ <p>For security, this link can only be used once.</p>
2118
+ </div>
2119
+ </div>
2120
+ </body>
2121
+ </html>
2122
+ `;
2123
+ }
2124
+ createMagicLinkAuthPlugin();
2125
+
1113
2126
  // src/app.ts
1114
2127
  function createSonicJSApp(config = {}) {
1115
2128
  const app = new hono.Hono();
1116
- const appVersion = config.version || chunkW2IAEG4W_cjs.getCoreVersion();
2129
+ const appVersion = config.version || chunkH2X4BFCW_cjs.getCoreVersion();
1117
2130
  const appName = config.name || "SonicJS AI";
1118
2131
  app.use("*", async (c, next) => {
1119
2132
  c.set("appVersion", appVersion);
1120
2133
  await next();
1121
2134
  });
1122
- app.use("*", chunk5NCBFP37_cjs.metricsMiddleware());
1123
- app.use("*", chunk5NCBFP37_cjs.bootstrapMiddleware(config));
2135
+ app.use("*", chunkQG3YQKL4_cjs.metricsMiddleware());
2136
+ app.use("*", chunkQG3YQKL4_cjs.bootstrapMiddleware(config));
1124
2137
  if (config.middleware?.beforeAuth) {
1125
2138
  for (const middleware of config.middleware.beforeAuth) {
1126
2139
  app.use("*", middleware);
@@ -1137,26 +2150,37 @@ function createSonicJSApp(config = {}) {
1137
2150
  app.use("*", middleware);
1138
2151
  }
1139
2152
  }
1140
- app.route("/api", chunkARLXQU2S_cjs.api_default);
1141
- app.route("/api/media", chunkARLXQU2S_cjs.api_media_default);
1142
- app.route("/api/system", chunkARLXQU2S_cjs.api_system_default);
1143
- app.route("/admin/api", chunkARLXQU2S_cjs.admin_api_default);
1144
- app.route("/admin/dashboard", chunkARLXQU2S_cjs.router);
1145
- app.route("/admin/collections", chunkARLXQU2S_cjs.adminCollectionsRoutes);
1146
- app.route("/admin/settings", chunkARLXQU2S_cjs.adminSettingsRoutes);
2153
+ app.route("/api", chunk5OLN5JO3_cjs.api_default);
2154
+ app.route("/api/media", chunk5OLN5JO3_cjs.api_media_default);
2155
+ app.route("/api/system", chunk5OLN5JO3_cjs.api_system_default);
2156
+ app.route("/admin/api", chunk5OLN5JO3_cjs.admin_api_default);
2157
+ app.route("/admin/dashboard", chunk5OLN5JO3_cjs.router);
2158
+ app.route("/admin/collections", chunk5OLN5JO3_cjs.adminCollectionsRoutes);
2159
+ app.route("/admin/settings", chunk5OLN5JO3_cjs.adminSettingsRoutes);
1147
2160
  app.route("/admin/database-tools", createDatabaseToolsAdminRoutes());
1148
- app.route("/admin/content", chunkARLXQU2S_cjs.admin_content_default);
1149
- app.route("/admin/media", chunkARLXQU2S_cjs.adminMediaRoutes);
1150
- app.route("/admin/plugins", chunkARLXQU2S_cjs.adminPluginRoutes);
1151
- app.route("/admin/logs", chunkARLXQU2S_cjs.adminLogsRoutes);
1152
- app.route("/admin", chunkARLXQU2S_cjs.userRoutes);
1153
- app.route("/auth", chunkARLXQU2S_cjs.auth_default);
1154
- app.route("/", chunkARLXQU2S_cjs.test_cleanup_default);
2161
+ app.route("/admin/content", chunk5OLN5JO3_cjs.admin_content_default);
2162
+ app.route("/admin/media", chunk5OLN5JO3_cjs.adminMediaRoutes);
2163
+ app.route("/admin/plugins", chunk5OLN5JO3_cjs.adminPluginRoutes);
2164
+ app.route("/admin/logs", chunk5OLN5JO3_cjs.adminLogsRoutes);
2165
+ app.route("/admin", chunk5OLN5JO3_cjs.userRoutes);
2166
+ app.route("/auth", chunk5OLN5JO3_cjs.auth_default);
2167
+ app.route("/", chunk5OLN5JO3_cjs.test_cleanup_default);
1155
2168
  if (emailPlugin.routes && emailPlugin.routes.length > 0) {
1156
2169
  for (const route of emailPlugin.routes) {
1157
2170
  app.route(route.path, route.handler);
1158
2171
  }
1159
2172
  }
2173
+ if (otpLoginPlugin.routes && otpLoginPlugin.routes.length > 0) {
2174
+ for (const route of otpLoginPlugin.routes) {
2175
+ app.route(route.path, route.handler);
2176
+ }
2177
+ }
2178
+ const magicLinkPlugin = createMagicLinkAuthPlugin();
2179
+ if (magicLinkPlugin.routes && magicLinkPlugin.routes.length > 0) {
2180
+ for (const route of magicLinkPlugin.routes) {
2181
+ app.route(route.path, route.handler);
2182
+ }
2183
+ }
1160
2184
  app.get("/files/*", async (c) => {
1161
2185
  try {
1162
2186
  const url = new URL(c.req.url);
@@ -1220,83 +2244,83 @@ function createDb(d1$1) {
1220
2244
  }
1221
2245
 
1222
2246
  // src/index.ts
1223
- var VERSION = chunkW2IAEG4W_cjs.package_default.version;
2247
+ var VERSION = chunkH2X4BFCW_cjs.package_default.version;
1224
2248
 
1225
2249
  Object.defineProperty(exports, "ROUTES_INFO", {
1226
2250
  enumerable: true,
1227
- get: function () { return chunkARLXQU2S_cjs.ROUTES_INFO; }
2251
+ get: function () { return chunk5OLN5JO3_cjs.ROUTES_INFO; }
1228
2252
  });
1229
2253
  Object.defineProperty(exports, "adminApiRoutes", {
1230
2254
  enumerable: true,
1231
- get: function () { return chunkARLXQU2S_cjs.admin_api_default; }
2255
+ get: function () { return chunk5OLN5JO3_cjs.admin_api_default; }
1232
2256
  });
1233
2257
  Object.defineProperty(exports, "adminCheckboxRoutes", {
1234
2258
  enumerable: true,
1235
- get: function () { return chunkARLXQU2S_cjs.adminCheckboxRoutes; }
2259
+ get: function () { return chunk5OLN5JO3_cjs.adminCheckboxRoutes; }
1236
2260
  });
1237
2261
  Object.defineProperty(exports, "adminCodeExamplesRoutes", {
1238
2262
  enumerable: true,
1239
- get: function () { return chunkARLXQU2S_cjs.admin_code_examples_default; }
2263
+ get: function () { return chunk5OLN5JO3_cjs.admin_code_examples_default; }
1240
2264
  });
1241
2265
  Object.defineProperty(exports, "adminCollectionsRoutes", {
1242
2266
  enumerable: true,
1243
- get: function () { return chunkARLXQU2S_cjs.adminCollectionsRoutes; }
2267
+ get: function () { return chunk5OLN5JO3_cjs.adminCollectionsRoutes; }
1244
2268
  });
1245
2269
  Object.defineProperty(exports, "adminContentRoutes", {
1246
2270
  enumerable: true,
1247
- get: function () { return chunkARLXQU2S_cjs.admin_content_default; }
2271
+ get: function () { return chunk5OLN5JO3_cjs.admin_content_default; }
1248
2272
  });
1249
2273
  Object.defineProperty(exports, "adminDashboardRoutes", {
1250
2274
  enumerable: true,
1251
- get: function () { return chunkARLXQU2S_cjs.router; }
2275
+ get: function () { return chunk5OLN5JO3_cjs.router; }
1252
2276
  });
1253
2277
  Object.defineProperty(exports, "adminDesignRoutes", {
1254
2278
  enumerable: true,
1255
- get: function () { return chunkARLXQU2S_cjs.adminDesignRoutes; }
2279
+ get: function () { return chunk5OLN5JO3_cjs.adminDesignRoutes; }
1256
2280
  });
1257
2281
  Object.defineProperty(exports, "adminLogsRoutes", {
1258
2282
  enumerable: true,
1259
- get: function () { return chunkARLXQU2S_cjs.adminLogsRoutes; }
2283
+ get: function () { return chunk5OLN5JO3_cjs.adminLogsRoutes; }
1260
2284
  });
1261
2285
  Object.defineProperty(exports, "adminMediaRoutes", {
1262
2286
  enumerable: true,
1263
- get: function () { return chunkARLXQU2S_cjs.adminMediaRoutes; }
2287
+ get: function () { return chunk5OLN5JO3_cjs.adminMediaRoutes; }
1264
2288
  });
1265
2289
  Object.defineProperty(exports, "adminPluginRoutes", {
1266
2290
  enumerable: true,
1267
- get: function () { return chunkARLXQU2S_cjs.adminPluginRoutes; }
2291
+ get: function () { return chunk5OLN5JO3_cjs.adminPluginRoutes; }
1268
2292
  });
1269
2293
  Object.defineProperty(exports, "adminSettingsRoutes", {
1270
2294
  enumerable: true,
1271
- get: function () { return chunkARLXQU2S_cjs.adminSettingsRoutes; }
2295
+ get: function () { return chunk5OLN5JO3_cjs.adminSettingsRoutes; }
1272
2296
  });
1273
2297
  Object.defineProperty(exports, "adminTestimonialsRoutes", {
1274
2298
  enumerable: true,
1275
- get: function () { return chunkARLXQU2S_cjs.admin_testimonials_default; }
2299
+ get: function () { return chunk5OLN5JO3_cjs.admin_testimonials_default; }
1276
2300
  });
1277
2301
  Object.defineProperty(exports, "adminUsersRoutes", {
1278
2302
  enumerable: true,
1279
- get: function () { return chunkARLXQU2S_cjs.userRoutes; }
2303
+ get: function () { return chunk5OLN5JO3_cjs.userRoutes; }
1280
2304
  });
1281
2305
  Object.defineProperty(exports, "apiContentCrudRoutes", {
1282
2306
  enumerable: true,
1283
- get: function () { return chunkARLXQU2S_cjs.api_content_crud_default; }
2307
+ get: function () { return chunk5OLN5JO3_cjs.api_content_crud_default; }
1284
2308
  });
1285
2309
  Object.defineProperty(exports, "apiMediaRoutes", {
1286
2310
  enumerable: true,
1287
- get: function () { return chunkARLXQU2S_cjs.api_media_default; }
2311
+ get: function () { return chunk5OLN5JO3_cjs.api_media_default; }
1288
2312
  });
1289
2313
  Object.defineProperty(exports, "apiRoutes", {
1290
2314
  enumerable: true,
1291
- get: function () { return chunkARLXQU2S_cjs.api_default; }
2315
+ get: function () { return chunk5OLN5JO3_cjs.api_default; }
1292
2316
  });
1293
2317
  Object.defineProperty(exports, "apiSystemRoutes", {
1294
2318
  enumerable: true,
1295
- get: function () { return chunkARLXQU2S_cjs.api_system_default; }
2319
+ get: function () { return chunk5OLN5JO3_cjs.api_system_default; }
1296
2320
  });
1297
2321
  Object.defineProperty(exports, "authRoutes", {
1298
2322
  enumerable: true,
1299
- get: function () { return chunkARLXQU2S_cjs.auth_default; }
2323
+ get: function () { return chunk5OLN5JO3_cjs.auth_default; }
1300
2324
  });
1301
2325
  Object.defineProperty(exports, "Logger", {
1302
2326
  enumerable: true,
@@ -1464,83 +2488,83 @@ Object.defineProperty(exports, "workflowHistory", {
1464
2488
  });
1465
2489
  Object.defineProperty(exports, "AuthManager", {
1466
2490
  enumerable: true,
1467
- get: function () { return chunk5NCBFP37_cjs.AuthManager; }
2491
+ get: function () { return chunkQG3YQKL4_cjs.AuthManager; }
1468
2492
  });
1469
2493
  Object.defineProperty(exports, "PermissionManager", {
1470
2494
  enumerable: true,
1471
- get: function () { return chunk5NCBFP37_cjs.PermissionManager; }
2495
+ get: function () { return chunkQG3YQKL4_cjs.PermissionManager; }
1472
2496
  });
1473
2497
  Object.defineProperty(exports, "bootstrapMiddleware", {
1474
2498
  enumerable: true,
1475
- get: function () { return chunk5NCBFP37_cjs.bootstrapMiddleware; }
2499
+ get: function () { return chunkQG3YQKL4_cjs.bootstrapMiddleware; }
1476
2500
  });
1477
2501
  Object.defineProperty(exports, "cacheHeaders", {
1478
2502
  enumerable: true,
1479
- get: function () { return chunk5NCBFP37_cjs.cacheHeaders; }
2503
+ get: function () { return chunkQG3YQKL4_cjs.cacheHeaders; }
1480
2504
  });
1481
2505
  Object.defineProperty(exports, "compressionMiddleware", {
1482
2506
  enumerable: true,
1483
- get: function () { return chunk5NCBFP37_cjs.compressionMiddleware; }
2507
+ get: function () { return chunkQG3YQKL4_cjs.compressionMiddleware; }
1484
2508
  });
1485
2509
  Object.defineProperty(exports, "detailedLoggingMiddleware", {
1486
2510
  enumerable: true,
1487
- get: function () { return chunk5NCBFP37_cjs.detailedLoggingMiddleware; }
2511
+ get: function () { return chunkQG3YQKL4_cjs.detailedLoggingMiddleware; }
1488
2512
  });
1489
2513
  Object.defineProperty(exports, "getActivePlugins", {
1490
2514
  enumerable: true,
1491
- get: function () { return chunk5NCBFP37_cjs.getActivePlugins; }
2515
+ get: function () { return chunkQG3YQKL4_cjs.getActivePlugins; }
1492
2516
  });
1493
2517
  Object.defineProperty(exports, "isPluginActive", {
1494
2518
  enumerable: true,
1495
- get: function () { return chunk5NCBFP37_cjs.isPluginActive; }
2519
+ get: function () { return chunkQG3YQKL4_cjs.isPluginActive; }
1496
2520
  });
1497
2521
  Object.defineProperty(exports, "logActivity", {
1498
2522
  enumerable: true,
1499
- get: function () { return chunk5NCBFP37_cjs.logActivity; }
2523
+ get: function () { return chunkQG3YQKL4_cjs.logActivity; }
1500
2524
  });
1501
2525
  Object.defineProperty(exports, "loggingMiddleware", {
1502
2526
  enumerable: true,
1503
- get: function () { return chunk5NCBFP37_cjs.loggingMiddleware; }
2527
+ get: function () { return chunkQG3YQKL4_cjs.loggingMiddleware; }
1504
2528
  });
1505
2529
  Object.defineProperty(exports, "optionalAuth", {
1506
2530
  enumerable: true,
1507
- get: function () { return chunk5NCBFP37_cjs.optionalAuth; }
2531
+ get: function () { return chunkQG3YQKL4_cjs.optionalAuth; }
1508
2532
  });
1509
2533
  Object.defineProperty(exports, "performanceLoggingMiddleware", {
1510
2534
  enumerable: true,
1511
- get: function () { return chunk5NCBFP37_cjs.performanceLoggingMiddleware; }
2535
+ get: function () { return chunkQG3YQKL4_cjs.performanceLoggingMiddleware; }
1512
2536
  });
1513
2537
  Object.defineProperty(exports, "requireActivePlugin", {
1514
2538
  enumerable: true,
1515
- get: function () { return chunk5NCBFP37_cjs.requireActivePlugin; }
2539
+ get: function () { return chunkQG3YQKL4_cjs.requireActivePlugin; }
1516
2540
  });
1517
2541
  Object.defineProperty(exports, "requireActivePlugins", {
1518
2542
  enumerable: true,
1519
- get: function () { return chunk5NCBFP37_cjs.requireActivePlugins; }
2543
+ get: function () { return chunkQG3YQKL4_cjs.requireActivePlugins; }
1520
2544
  });
1521
2545
  Object.defineProperty(exports, "requireAnyPermission", {
1522
2546
  enumerable: true,
1523
- get: function () { return chunk5NCBFP37_cjs.requireAnyPermission; }
2547
+ get: function () { return chunkQG3YQKL4_cjs.requireAnyPermission; }
1524
2548
  });
1525
2549
  Object.defineProperty(exports, "requireAuth", {
1526
2550
  enumerable: true,
1527
- get: function () { return chunk5NCBFP37_cjs.requireAuth; }
2551
+ get: function () { return chunkQG3YQKL4_cjs.requireAuth; }
1528
2552
  });
1529
2553
  Object.defineProperty(exports, "requirePermission", {
1530
2554
  enumerable: true,
1531
- get: function () { return chunk5NCBFP37_cjs.requirePermission; }
2555
+ get: function () { return chunkQG3YQKL4_cjs.requirePermission; }
1532
2556
  });
1533
2557
  Object.defineProperty(exports, "requireRole", {
1534
2558
  enumerable: true,
1535
- get: function () { return chunk5NCBFP37_cjs.requireRole; }
2559
+ get: function () { return chunkQG3YQKL4_cjs.requireRole; }
1536
2560
  });
1537
2561
  Object.defineProperty(exports, "securityHeaders", {
1538
2562
  enumerable: true,
1539
- get: function () { return chunk5NCBFP37_cjs.securityHeaders; }
2563
+ get: function () { return chunkQG3YQKL4_cjs.securityHeaders; }
1540
2564
  });
1541
2565
  Object.defineProperty(exports, "securityLoggingMiddleware", {
1542
2566
  enumerable: true,
1543
- get: function () { return chunk5NCBFP37_cjs.securityLoggingMiddleware; }
2567
+ get: function () { return chunkQG3YQKL4_cjs.securityLoggingMiddleware; }
1544
2568
  });
1545
2569
  Object.defineProperty(exports, "PluginBootstrapService", {
1546
2570
  enumerable: true,
@@ -1596,39 +2620,39 @@ Object.defineProperty(exports, "validateCollectionConfig", {
1596
2620
  });
1597
2621
  Object.defineProperty(exports, "MigrationService", {
1598
2622
  enumerable: true,
1599
- get: function () { return chunkW4CE7XME_cjs.MigrationService; }
2623
+ get: function () { return chunk63LV4YVI_cjs.MigrationService; }
1600
2624
  });
1601
2625
  Object.defineProperty(exports, "renderFilterBar", {
1602
2626
  enumerable: true,
1603
- get: function () { return chunkXR6XACXJ_cjs.renderFilterBar; }
2627
+ get: function () { return chunkYIXSSJWD_cjs.renderFilterBar; }
1604
2628
  });
1605
2629
  Object.defineProperty(exports, "getConfirmationDialogScript", {
1606
2630
  enumerable: true,
1607
- get: function () { return chunkMF7DWI5P_cjs.getConfirmationDialogScript; }
2631
+ get: function () { return chunkAZLU3ROK_cjs.getConfirmationDialogScript; }
1608
2632
  });
1609
2633
  Object.defineProperty(exports, "renderAlert", {
1610
2634
  enumerable: true,
1611
- get: function () { return chunkMF7DWI5P_cjs.renderAlert; }
2635
+ get: function () { return chunkAZLU3ROK_cjs.renderAlert; }
1612
2636
  });
1613
2637
  Object.defineProperty(exports, "renderConfirmationDialog", {
1614
2638
  enumerable: true,
1615
- get: function () { return chunkMF7DWI5P_cjs.renderConfirmationDialog; }
2639
+ get: function () { return chunkAZLU3ROK_cjs.renderConfirmationDialog; }
1616
2640
  });
1617
2641
  Object.defineProperty(exports, "renderForm", {
1618
2642
  enumerable: true,
1619
- get: function () { return chunkMF7DWI5P_cjs.renderForm; }
2643
+ get: function () { return chunkAZLU3ROK_cjs.renderForm; }
1620
2644
  });
1621
2645
  Object.defineProperty(exports, "renderFormField", {
1622
2646
  enumerable: true,
1623
- get: function () { return chunkMF7DWI5P_cjs.renderFormField; }
2647
+ get: function () { return chunkAZLU3ROK_cjs.renderFormField; }
1624
2648
  });
1625
2649
  Object.defineProperty(exports, "renderPagination", {
1626
2650
  enumerable: true,
1627
- get: function () { return chunkMF7DWI5P_cjs.renderPagination; }
2651
+ get: function () { return chunkAZLU3ROK_cjs.renderPagination; }
1628
2652
  });
1629
2653
  Object.defineProperty(exports, "renderTable", {
1630
2654
  enumerable: true,
1631
- get: function () { return chunkMF7DWI5P_cjs.renderTable; }
2655
+ get: function () { return chunkAZLU3ROK_cjs.renderTable; }
1632
2656
  });
1633
2657
  Object.defineProperty(exports, "HookSystemImpl", {
1634
2658
  enumerable: true,
@@ -1656,43 +2680,43 @@ Object.defineProperty(exports, "ScopedHookSystemClass", {
1656
2680
  });
1657
2681
  Object.defineProperty(exports, "QueryFilterBuilder", {
1658
2682
  enumerable: true,
1659
- get: function () { return chunkW2IAEG4W_cjs.QueryFilterBuilder; }
2683
+ get: function () { return chunkH2X4BFCW_cjs.QueryFilterBuilder; }
1660
2684
  });
1661
2685
  Object.defineProperty(exports, "SONICJS_VERSION", {
1662
2686
  enumerable: true,
1663
- get: function () { return chunkW2IAEG4W_cjs.SONICJS_VERSION; }
2687
+ get: function () { return chunkH2X4BFCW_cjs.SONICJS_VERSION; }
1664
2688
  });
1665
2689
  Object.defineProperty(exports, "TemplateRenderer", {
1666
2690
  enumerable: true,
1667
- get: function () { return chunkW2IAEG4W_cjs.TemplateRenderer; }
2691
+ get: function () { return chunkH2X4BFCW_cjs.TemplateRenderer; }
1668
2692
  });
1669
2693
  Object.defineProperty(exports, "buildQuery", {
1670
2694
  enumerable: true,
1671
- get: function () { return chunkW2IAEG4W_cjs.buildQuery; }
2695
+ get: function () { return chunkH2X4BFCW_cjs.buildQuery; }
1672
2696
  });
1673
2697
  Object.defineProperty(exports, "escapeHtml", {
1674
2698
  enumerable: true,
1675
- get: function () { return chunkW2IAEG4W_cjs.escapeHtml; }
2699
+ get: function () { return chunkH2X4BFCW_cjs.escapeHtml; }
1676
2700
  });
1677
2701
  Object.defineProperty(exports, "getCoreVersion", {
1678
2702
  enumerable: true,
1679
- get: function () { return chunkW2IAEG4W_cjs.getCoreVersion; }
2703
+ get: function () { return chunkH2X4BFCW_cjs.getCoreVersion; }
1680
2704
  });
1681
2705
  Object.defineProperty(exports, "renderTemplate", {
1682
2706
  enumerable: true,
1683
- get: function () { return chunkW2IAEG4W_cjs.renderTemplate; }
2707
+ get: function () { return chunkH2X4BFCW_cjs.renderTemplate; }
1684
2708
  });
1685
2709
  Object.defineProperty(exports, "sanitizeInput", {
1686
2710
  enumerable: true,
1687
- get: function () { return chunkW2IAEG4W_cjs.sanitizeInput; }
2711
+ get: function () { return chunkH2X4BFCW_cjs.sanitizeInput; }
1688
2712
  });
1689
2713
  Object.defineProperty(exports, "sanitizeObject", {
1690
2714
  enumerable: true,
1691
- get: function () { return chunkW2IAEG4W_cjs.sanitizeObject; }
2715
+ get: function () { return chunkH2X4BFCW_cjs.sanitizeObject; }
1692
2716
  });
1693
2717
  Object.defineProperty(exports, "templateRenderer", {
1694
2718
  enumerable: true,
1695
- get: function () { return chunkW2IAEG4W_cjs.templateRenderer; }
2719
+ get: function () { return chunkH2X4BFCW_cjs.templateRenderer; }
1696
2720
  });
1697
2721
  Object.defineProperty(exports, "metricsTracker", {
1698
2722
  enumerable: true,