@sonicjs-cms/core 2.8.2 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/{app-DnQ26Lho.d.cts → app-Ozl9agJG.d.cts} +1 -1
  2. package/dist/{app-DnQ26Lho.d.ts → app-Ozl9agJG.d.ts} +1 -1
  3. package/dist/{chunk-FUUVSYVQ.js → chunk-25YNV4RK.js} +3 -3
  4. package/dist/{chunk-FUUVSYVQ.js.map → chunk-25YNV4RK.js.map} +1 -1
  5. package/dist/{chunk-4Z5BQZT6.js → chunk-2JGQKF7B.js} +324 -301
  6. package/dist/chunk-2JGQKF7B.js.map +1 -0
  7. package/dist/{chunk-WI5ESQKT.js → chunk-3FHMXGLF.js} +7 -5
  8. package/dist/chunk-3FHMXGLF.js.map +1 -0
  9. package/dist/{chunk-VNLR35GO.cjs → chunk-64APW3DW.cjs} +339 -2
  10. package/dist/chunk-64APW3DW.cjs.map +1 -0
  11. package/dist/{chunk-G44QUVNM.js → chunk-7JMMLHPQ.js} +337 -4
  12. package/dist/chunk-7JMMLHPQ.js.map +1 -0
  13. package/dist/chunk-CJYFSKH7.js +54 -54
  14. package/dist/chunk-CJYFSKH7.js.map +1 -1
  15. package/dist/{chunk-ZWKCL46S.cjs → chunk-DQZVU3WB.cjs} +4 -4
  16. package/dist/{chunk-ZWKCL46S.cjs.map → chunk-DQZVU3WB.cjs.map} +1 -1
  17. package/dist/{chunk-3U5YHS4G.cjs → chunk-KSB6FXOP.cjs} +425 -402
  18. package/dist/chunk-KSB6FXOP.cjs.map +1 -0
  19. package/dist/{chunk-VGSZWZP3.cjs → chunk-LDFMYRG6.cjs} +2 -2
  20. package/dist/{chunk-VGSZWZP3.cjs.map → chunk-LDFMYRG6.cjs.map} +1 -1
  21. package/dist/chunk-MNFY6DWY.cjs +54 -54
  22. package/dist/chunk-MNFY6DWY.cjs.map +1 -1
  23. package/dist/{chunk-JSHIGVIF.cjs → chunk-SHU7Q66Q.cjs} +7 -5
  24. package/dist/chunk-SHU7Q66Q.cjs.map +1 -0
  25. package/dist/{chunk-I6REMSMF.js → chunk-STTZVLY2.js} +2 -2
  26. package/dist/{chunk-I6REMSMF.js.map → chunk-STTZVLY2.js.map} +1 -1
  27. package/dist/{collection-config-BF95LgQb.d.cts → collection-config-DckWhkdL.d.cts} +3 -2
  28. package/dist/{collection-config-BF95LgQb.d.ts → collection-config-DckWhkdL.d.ts} +3 -2
  29. package/dist/{filter-bar.template-Daw8ZDoq.d.cts → filter-bar.template-DlVYMk-T.d.cts} +1 -1
  30. package/dist/{filter-bar.template-Daw8ZDoq.d.ts → filter-bar.template-DlVYMk-T.d.ts} +1 -1
  31. package/dist/index.cjs +128 -127
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +8 -8
  34. package/dist/index.d.ts +8 -8
  35. package/dist/index.js +10 -9
  36. package/dist/index.js.map +1 -1
  37. package/dist/middleware.cjs +28 -28
  38. package/dist/middleware.d.cts +1 -1
  39. package/dist/middleware.d.ts +1 -1
  40. package/dist/middleware.js +2 -2
  41. package/dist/migrations-QQWGDWGB.cjs +13 -0
  42. package/dist/{migrations-F3G6CTRS.cjs.map → migrations-QQWGDWGB.cjs.map} +1 -1
  43. package/dist/migrations-SZSR3C3G.js +4 -0
  44. package/dist/{migrations-LLNEST75.js.map → migrations-SZSR3C3G.js.map} +1 -1
  45. package/dist/{plugin-zvZpaiP5.d.cts → plugin-0Xogrln-.d.cts} +1 -1
  46. package/dist/{plugin-zvZpaiP5.d.ts → plugin-0Xogrln-.d.ts} +1 -1
  47. package/dist/{plugin-bootstrap-C7Mj00Ud.d.ts → plugin-bootstrap-BAz7NY0H.d.cts} +2 -2
  48. package/dist/{plugin-bootstrap-DKB5f8-E.d.cts → plugin-bootstrap-Cz3-bj8X.d.ts} +2 -2
  49. package/dist/{plugin-manager-Baa6xXqB.d.ts → plugin-manager-Clf2gXwj.d.ts} +2 -2
  50. package/dist/{plugin-manager-vBal9Zip.d.cts → plugin-manager-GcIeb226.d.cts} +2 -2
  51. package/dist/plugins.d.cts +2 -2
  52. package/dist/plugins.d.ts +2 -2
  53. package/dist/routes.cjs +28 -28
  54. package/dist/routes.d.cts +1 -1
  55. package/dist/routes.d.ts +1 -1
  56. package/dist/routes.js +5 -5
  57. package/dist/services.cjs +30 -14
  58. package/dist/services.d.cts +29 -4
  59. package/dist/services.d.ts +29 -4
  60. package/dist/services.js +2 -2
  61. package/dist/{telemetry-UiD1i9GS.d.cts → telemetry-B9vIV4wh.d.cts} +1 -1
  62. package/dist/{telemetry-UiD1i9GS.d.ts → telemetry-B9vIV4wh.d.ts} +1 -1
  63. package/dist/templates.d.cts +1 -1
  64. package/dist/templates.d.ts +1 -1
  65. package/dist/types.d.cts +3 -3
  66. package/dist/types.d.ts +3 -3
  67. package/dist/utils.cjs +11 -11
  68. package/dist/utils.d.cts +3 -3
  69. package/dist/utils.d.ts +3 -3
  70. package/dist/utils.js +1 -1
  71. package/dist/{version-C_CXrN_T.d.cts → version-ChpccWQ1.d.cts} +1 -1
  72. package/dist/{version-C_CXrN_T.d.ts → version-ChpccWQ1.d.ts} +1 -1
  73. package/package.json +5 -3
  74. package/dist/chunk-3U5YHS4G.cjs.map +0 -1
  75. package/dist/chunk-4Z5BQZT6.js.map +0 -1
  76. package/dist/chunk-G44QUVNM.js.map +0 -1
  77. package/dist/chunk-JSHIGVIF.cjs.map +0 -1
  78. package/dist/chunk-VNLR35GO.cjs.map +0 -1
  79. package/dist/chunk-WI5ESQKT.js.map +0 -1
  80. package/dist/migrations-F3G6CTRS.cjs +0 -13
  81. package/dist/migrations-LLNEST75.js +0 -4
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkVNLR35GO_cjs = require('./chunk-VNLR35GO.cjs');
4
- var chunkZWKCL46S_cjs = require('./chunk-ZWKCL46S.cjs');
3
+ var chunk64APW3DW_cjs = require('./chunk-64APW3DW.cjs');
4
+ var chunkDQZVU3WB_cjs = require('./chunk-DQZVU3WB.cjs');
5
5
  var chunkMPT5PA6U_cjs = require('./chunk-MPT5PA6U.cjs');
6
- var chunkVGSZWZP3_cjs = require('./chunk-VGSZWZP3.cjs');
6
+ var chunkLDFMYRG6_cjs = require('./chunk-LDFMYRG6.cjs');
7
7
  var chunkLTKV7AE5_cjs = require('./chunk-LTKV7AE5.cjs');
8
8
  var chunk6FHNRRJ3_cjs = require('./chunk-6FHNRRJ3.cjs');
9
- var chunkJSHIGVIF_cjs = require('./chunk-JSHIGVIF.cjs');
9
+ var chunkSHU7Q66Q_cjs = require('./chunk-SHU7Q66Q.cjs');
10
10
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
11
11
  var chunkMNWKYY5E_cjs = require('./chunk-MNWKYY5E.cjs');
12
12
  var hono = require('hono');
@@ -121,7 +121,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
121
121
  }, 500);
122
122
  }
123
123
  });
124
- apiContentCrudRoutes.post("/", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
124
+ apiContentCrudRoutes.post("/", chunkDQZVU3WB_cjs.requireAuth(), async (c) => {
125
125
  try {
126
126
  const db = c.env.DB;
127
127
  const user = c.get("user");
@@ -162,7 +162,7 @@ apiContentCrudRoutes.post("/", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
162
162
  now,
163
163
  now
164
164
  ).run();
165
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
165
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
166
166
  await cache.invalidate(`content:list:${collectionId}:*`);
167
167
  await cache.invalidate("content-filtered:*");
168
168
  const getStmt = db.prepare("SELECT * FROM content WHERE id = ?");
@@ -187,7 +187,7 @@ apiContentCrudRoutes.post("/", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
187
187
  }, 500);
188
188
  }
189
189
  });
190
- apiContentCrudRoutes.put("/:id", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
190
+ apiContentCrudRoutes.put("/:id", chunkDQZVU3WB_cjs.requireAuth(), async (c) => {
191
191
  try {
192
192
  const id = c.req.param("id");
193
193
  const db = c.env.DB;
@@ -225,7 +225,7 @@ apiContentCrudRoutes.put("/:id", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
225
225
  WHERE id = ?
226
226
  `);
227
227
  await updateStmt.bind(...params).run();
228
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
228
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
229
229
  await cache.delete(cache.generateKey("content", id));
230
230
  await cache.invalidate(`content:list:${existing.collection_id}:*`);
231
231
  await cache.invalidate("content-filtered:*");
@@ -251,7 +251,7 @@ apiContentCrudRoutes.put("/:id", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
251
251
  }, 500);
252
252
  }
253
253
  });
254
- apiContentCrudRoutes.delete("/:id", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
254
+ apiContentCrudRoutes.delete("/:id", chunkDQZVU3WB_cjs.requireAuth(), async (c) => {
255
255
  try {
256
256
  const id = c.req.param("id");
257
257
  const db = c.env.DB;
@@ -262,7 +262,7 @@ apiContentCrudRoutes.delete("/:id", chunkZWKCL46S_cjs.requireAuth(), async (c) =
262
262
  }
263
263
  const deleteStmt = db.prepare("DELETE FROM content WHERE id = ?");
264
264
  await deleteStmt.bind(id).run();
265
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
265
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
266
266
  await cache.delete(cache.generateKey("content", id));
267
267
  await cache.invalidate(`content:list:${existing.collection_id}:*`);
268
268
  await cache.invalidate("content-filtered:*");
@@ -287,7 +287,7 @@ apiRoutes.use("*", async (c, next) => {
287
287
  c.header("X-Response-Time", `${totalTime}ms`);
288
288
  });
289
289
  apiRoutes.use("*", async (c, next) => {
290
- const cacheEnabled = await chunkZWKCL46S_cjs.isPluginActive(c.env.DB, "core-cache");
290
+ const cacheEnabled = await chunkDQZVU3WB_cjs.isPluginActive(c.env.DB, "core-cache");
291
291
  c.set("cacheEnabled", cacheEnabled);
292
292
  await next();
293
293
  });
@@ -724,7 +724,7 @@ apiRoutes.get("/collections", async (c) => {
724
724
  try {
725
725
  const db = c.env.DB;
726
726
  const cacheEnabled = c.get("cacheEnabled");
727
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
727
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
728
728
  const cacheKey = cache.generateKey("collections", "all");
729
729
  if (cacheEnabled) {
730
730
  const cacheResult = await cache.getWithSource(cacheKey);
@@ -778,7 +778,7 @@ apiRoutes.get("/collections", async (c) => {
778
778
  return c.json({ error: "Failed to fetch collections" }, 500);
779
779
  }
780
780
  });
781
- apiRoutes.get("/content", chunkZWKCL46S_cjs.optionalAuth(), async (c) => {
781
+ apiRoutes.get("/content", chunkDQZVU3WB_cjs.optionalAuth(), async (c) => {
782
782
  const executionStart = Date.now();
783
783
  try {
784
784
  const db = c.env.DB;
@@ -801,13 +801,13 @@ apiRoutes.get("/content", chunkZWKCL46S_cjs.optionalAuth(), async (c) => {
801
801
  });
802
802
  }
803
803
  }
804
- const filter = chunkJSHIGVIF_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
804
+ const filter = chunkSHU7Q66Q_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
805
805
  const normalizedFilter = normalizePublicContentFilter(filter, c.get("user")?.role);
806
806
  if (!normalizedFilter.limit) {
807
807
  normalizedFilter.limit = 50;
808
808
  }
809
809
  normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
810
- const builder3 = new chunkJSHIGVIF_cjs.QueryFilterBuilder();
810
+ const builder3 = new chunkSHU7Q66Q_cjs.QueryFilterBuilder();
811
811
  const queryResult = builder3.build("content", normalizedFilter);
812
812
  if (queryResult.errors.length > 0) {
813
813
  return c.json({
@@ -816,7 +816,7 @@ apiRoutes.get("/content", chunkZWKCL46S_cjs.optionalAuth(), async (c) => {
816
816
  }, 400);
817
817
  }
818
818
  const cacheEnabled = c.get("cacheEnabled");
819
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
819
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
820
820
  const cacheKey = cache.generateKey("content-filtered", JSON.stringify({ filter: normalizedFilter, query: queryResult.sql }));
821
821
  if (cacheEnabled) {
822
822
  const cacheResult = await cache.getWithSource(cacheKey);
@@ -879,7 +879,7 @@ apiRoutes.get("/content", chunkZWKCL46S_cjs.optionalAuth(), async (c) => {
879
879
  }, 500);
880
880
  }
881
881
  });
882
- apiRoutes.get("/collections/:collection/content", chunkZWKCL46S_cjs.optionalAuth(), async (c) => {
882
+ apiRoutes.get("/collections/:collection/content", chunkDQZVU3WB_cjs.optionalAuth(), async (c) => {
883
883
  const executionStart = Date.now();
884
884
  try {
885
885
  const collection = c.req.param("collection");
@@ -890,7 +890,7 @@ apiRoutes.get("/collections/:collection/content", chunkZWKCL46S_cjs.optionalAuth
890
890
  if (!collectionResult) {
891
891
  return c.json({ error: "Collection not found" }, 404);
892
892
  }
893
- const filter = chunkJSHIGVIF_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
893
+ const filter = chunkSHU7Q66Q_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
894
894
  const normalizedFilter = normalizePublicContentFilter(filter, c.get("user")?.role);
895
895
  if (!normalizedFilter.where) {
896
896
  normalizedFilter.where = { and: [] };
@@ -907,7 +907,7 @@ apiRoutes.get("/collections/:collection/content", chunkZWKCL46S_cjs.optionalAuth
907
907
  normalizedFilter.limit = 50;
908
908
  }
909
909
  normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
910
- const builder3 = new chunkJSHIGVIF_cjs.QueryFilterBuilder();
910
+ const builder3 = new chunkSHU7Q66Q_cjs.QueryFilterBuilder();
911
911
  const queryResult = builder3.build("content", normalizedFilter);
912
912
  if (queryResult.errors.length > 0) {
913
913
  return c.json({
@@ -916,7 +916,7 @@ apiRoutes.get("/collections/:collection/content", chunkZWKCL46S_cjs.optionalAuth
916
916
  }, 400);
917
917
  }
918
918
  const cacheEnabled = c.get("cacheEnabled");
919
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.api);
919
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.api);
920
920
  const cacheKey = cache.generateKey("collection-content-filtered", `${collection}:${JSON.stringify({ filter: normalizedFilter, query: queryResult.sql })}`);
921
921
  if (cacheEnabled) {
922
922
  const cacheResult = await cache.getWithSource(cacheKey);
@@ -1028,7 +1028,7 @@ var fileValidationSchema = zod.z.object({
1028
1028
  // 50MB max
1029
1029
  });
1030
1030
  var apiMediaRoutes = new hono.Hono();
1031
- apiMediaRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
1031
+ apiMediaRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
1032
1032
  apiMediaRoutes.post("/upload", async (c) => {
1033
1033
  try {
1034
1034
  const user = c.get("user");
@@ -1772,8 +1772,8 @@ apiSystemRoutes.get("/env", (c) => {
1772
1772
  });
1773
1773
  var api_system_default = apiSystemRoutes;
1774
1774
  var adminApiRoutes = new hono.Hono();
1775
- adminApiRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
1776
- adminApiRoutes.use("*", chunkZWKCL46S_cjs.requireRole(["admin", "editor"]));
1775
+ adminApiRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
1776
+ adminApiRoutes.use("*", chunkDQZVU3WB_cjs.requireRole(["admin", "editor"]));
1777
1777
  adminApiRoutes.get("/stats", async (c) => {
1778
1778
  try {
1779
1779
  const db = c.env.DB;
@@ -2283,7 +2283,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
2283
2283
  });
2284
2284
  adminApiRoutes.get("/migrations/status", async (c) => {
2285
2285
  try {
2286
- const { MigrationService: MigrationService2 } = await import('./migrations-F3G6CTRS.cjs');
2286
+ const { MigrationService: MigrationService2 } = await import('./migrations-QQWGDWGB.cjs');
2287
2287
  const db = c.env.DB;
2288
2288
  const migrationService = new MigrationService2(db);
2289
2289
  const status = await migrationService.getMigrationStatus();
@@ -2308,7 +2308,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
2308
2308
  error: "Unauthorized. Admin access required."
2309
2309
  }, 403);
2310
2310
  }
2311
- const { MigrationService: MigrationService2 } = await import('./migrations-F3G6CTRS.cjs');
2311
+ const { MigrationService: MigrationService2 } = await import('./migrations-QQWGDWGB.cjs');
2312
2312
  const db = c.env.DB;
2313
2313
  const migrationService = new MigrationService2(db);
2314
2314
  const result = await migrationService.runPendingMigrations();
@@ -2327,7 +2327,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
2327
2327
  });
2328
2328
  adminApiRoutes.get("/migrations/validate", async (c) => {
2329
2329
  try {
2330
- const { MigrationService: MigrationService2 } = await import('./migrations-F3G6CTRS.cjs');
2330
+ const { MigrationService: MigrationService2 } = await import('./migrations-QQWGDWGB.cjs');
2331
2331
  const db = c.env.DB;
2332
2332
  const migrationService = new MigrationService2(db);
2333
2333
  const validation = await migrationService.validateSchema();
@@ -2738,7 +2738,7 @@ var JWT_SECRET_FALLBACK = "your-super-secret-jwt-key-change-in-production";
2738
2738
  async function setCsrfCookie(c) {
2739
2739
  const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK;
2740
2740
  const isDev = c.env?.ENVIRONMENT === "development" || !c.env?.ENVIRONMENT;
2741
- const csrfToken = await chunkZWKCL46S_cjs.generateCsrfToken(secret);
2741
+ const csrfToken = await chunkDQZVU3WB_cjs.generateCsrfToken(secret);
2742
2742
  cookie.setCookie(c, "csrf_token", csrfToken, {
2743
2743
  httpOnly: false,
2744
2744
  secure: !isDev,
@@ -2795,7 +2795,7 @@ var loginSchema = zod.z.object({
2795
2795
  });
2796
2796
  authRoutes.post(
2797
2797
  "/register",
2798
- chunkZWKCL46S_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
2798
+ chunkDQZVU3WB_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
2799
2799
  async (c) => {
2800
2800
  try {
2801
2801
  const db = c.env.DB;
@@ -2832,7 +2832,7 @@ authRoutes.post(
2832
2832
  if (existingUser) {
2833
2833
  return c.json({ error: "User with this email or username already exists" }, 400);
2834
2834
  }
2835
- const passwordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
2835
+ const passwordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
2836
2836
  const userId = crypto.randomUUID();
2837
2837
  const now = /* @__PURE__ */ new Date();
2838
2838
  await db.prepare(`
@@ -2852,7 +2852,7 @@ authRoutes.post(
2852
2852
  now.getTime(),
2853
2853
  now.getTime()
2854
2854
  ).run();
2855
- const token = await chunkZWKCL46S_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET);
2855
+ const token = await chunkDQZVU3WB_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET);
2856
2856
  cookie.setCookie(c, "auth_token", token, {
2857
2857
  httpOnly: true,
2858
2858
  secure: true,
@@ -2886,7 +2886,7 @@ authRoutes.post(
2886
2886
  );
2887
2887
  authRoutes.post(
2888
2888
  "/login",
2889
- chunkZWKCL46S_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
2889
+ chunkDQZVU3WB_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
2890
2890
  async (c) => {
2891
2891
  try {
2892
2892
  const body = await c.req.json();
@@ -2897,7 +2897,7 @@ authRoutes.post(
2897
2897
  const { email, password } = validation.data;
2898
2898
  const db = c.env.DB;
2899
2899
  const normalizedEmail = email.toLowerCase();
2900
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.user);
2900
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.user);
2901
2901
  let user = await cache.get(cache.generateKey("user", `email:${normalizedEmail}`));
2902
2902
  if (!user) {
2903
2903
  user = await db.prepare("SELECT * FROM users WHERE email = ? AND is_active = 1").bind(normalizedEmail).first();
@@ -2909,19 +2909,19 @@ authRoutes.post(
2909
2909
  if (!user) {
2910
2910
  return c.json({ error: "Invalid email or password" }, 401);
2911
2911
  }
2912
- const isValidPassword = await chunkZWKCL46S_cjs.AuthManager.verifyPassword(password, user.password_hash);
2912
+ const isValidPassword = await chunkDQZVU3WB_cjs.AuthManager.verifyPassword(password, user.password_hash);
2913
2913
  if (!isValidPassword) {
2914
2914
  return c.json({ error: "Invalid email or password" }, 401);
2915
2915
  }
2916
- if (chunkZWKCL46S_cjs.AuthManager.isLegacyHash(user.password_hash)) {
2916
+ if (chunkDQZVU3WB_cjs.AuthManager.isLegacyHash(user.password_hash)) {
2917
2917
  try {
2918
- const newHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
2918
+ const newHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
2919
2919
  await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(newHash, Date.now(), user.id).run();
2920
2920
  } catch (rehashError) {
2921
2921
  console.error("Password rehash failed (non-fatal):", rehashError);
2922
2922
  }
2923
2923
  }
2924
- const token = await chunkZWKCL46S_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
2924
+ const token = await chunkDQZVU3WB_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
2925
2925
  cookie.setCookie(c, "auth_token", token, {
2926
2926
  httpOnly: true,
2927
2927
  secure: true,
@@ -2974,7 +2974,7 @@ authRoutes.get("/logout", (c) => {
2974
2974
  clearCsrfCookie(c);
2975
2975
  return c.redirect("/auth/login?message=You have been logged out successfully");
2976
2976
  });
2977
- authRoutes.get("/me", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
2977
+ authRoutes.get("/me", chunkDQZVU3WB_cjs.requireAuth(), async (c) => {
2978
2978
  try {
2979
2979
  const user = c.get("user");
2980
2980
  if (!user) {
@@ -2991,13 +2991,13 @@ authRoutes.get("/me", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
2991
2991
  return c.json({ error: "Failed to get user" }, 500);
2992
2992
  }
2993
2993
  });
2994
- authRoutes.post("/refresh", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
2994
+ authRoutes.post("/refresh", chunkDQZVU3WB_cjs.requireAuth(), async (c) => {
2995
2995
  try {
2996
2996
  const user = c.get("user");
2997
2997
  if (!user) {
2998
2998
  return c.json({ error: "Not authenticated" }, 401);
2999
2999
  }
3000
- const token = await chunkZWKCL46S_cjs.AuthManager.generateToken(user.userId, user.email, user.role, c.env.JWT_SECRET);
3000
+ const token = await chunkDQZVU3WB_cjs.AuthManager.generateToken(user.userId, user.email, user.role, c.env.JWT_SECRET);
3001
3001
  cookie.setCookie(c, "auth_token", token, {
3002
3002
  httpOnly: true,
3003
3003
  secure: true,
@@ -3014,7 +3014,7 @@ authRoutes.post("/refresh", chunkZWKCL46S_cjs.requireAuth(), async (c) => {
3014
3014
  });
3015
3015
  authRoutes.post(
3016
3016
  "/register/form",
3017
- chunkZWKCL46S_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
3017
+ chunkDQZVU3WB_cjs.rateLimit({ max: 3, windowMs: 60 * 1e3, keyPrefix: "register" }),
3018
3018
  async (c) => {
3019
3019
  try {
3020
3020
  const db = c.env.DB;
@@ -3061,7 +3061,7 @@ authRoutes.post(
3061
3061
  </div>
3062
3062
  `);
3063
3063
  }
3064
- const passwordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
3064
+ const passwordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
3065
3065
  const role = isFirstUser ? "admin" : "viewer";
3066
3066
  const userId = crypto.randomUUID();
3067
3067
  const now = /* @__PURE__ */ new Date();
@@ -3081,7 +3081,7 @@ authRoutes.post(
3081
3081
  now.getTime(),
3082
3082
  now.getTime()
3083
3083
  ).run();
3084
- const token = await chunkZWKCL46S_cjs.AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET);
3084
+ const token = await chunkDQZVU3WB_cjs.AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET);
3085
3085
  cookie.setCookie(c, "auth_token", token, {
3086
3086
  httpOnly: true,
3087
3087
  secure: false,
@@ -3114,7 +3114,7 @@ authRoutes.post(
3114
3114
  );
3115
3115
  authRoutes.post(
3116
3116
  "/login/form",
3117
- chunkZWKCL46S_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
3117
+ chunkDQZVU3WB_cjs.rateLimit({ max: 5, windowMs: 60 * 1e3, keyPrefix: "login" }),
3118
3118
  async (c) => {
3119
3119
  try {
3120
3120
  const formData = await c.req.formData();
@@ -3138,7 +3138,7 @@ authRoutes.post(
3138
3138
  </div>
3139
3139
  `);
3140
3140
  }
3141
- const isValidPassword = await chunkZWKCL46S_cjs.AuthManager.verifyPassword(password, user.password_hash);
3141
+ const isValidPassword = await chunkDQZVU3WB_cjs.AuthManager.verifyPassword(password, user.password_hash);
3142
3142
  if (!isValidPassword) {
3143
3143
  return c.html(html.html`
3144
3144
  <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
@@ -3146,15 +3146,15 @@ authRoutes.post(
3146
3146
  </div>
3147
3147
  `);
3148
3148
  }
3149
- if (chunkZWKCL46S_cjs.AuthManager.isLegacyHash(user.password_hash)) {
3149
+ if (chunkDQZVU3WB_cjs.AuthManager.isLegacyHash(user.password_hash)) {
3150
3150
  try {
3151
- const newHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
3151
+ const newHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
3152
3152
  await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(newHash, Date.now(), user.id).run();
3153
3153
  } catch (rehashError) {
3154
3154
  console.error("Password rehash failed (non-fatal):", rehashError);
3155
3155
  }
3156
3156
  }
3157
- const token = await chunkZWKCL46S_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
3157
+ const token = await chunkDQZVU3WB_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
3158
3158
  cookie.setCookie(c, "auth_token", token, {
3159
3159
  httpOnly: true,
3160
3160
  secure: false,
@@ -3196,7 +3196,7 @@ authRoutes.post(
3196
3196
  );
3197
3197
  authRoutes.post(
3198
3198
  "/seed-admin",
3199
- chunkZWKCL46S_cjs.rateLimit({ max: 2, windowMs: 60 * 1e3, keyPrefix: "seed-admin" }),
3199
+ chunkDQZVU3WB_cjs.rateLimit({ max: 2, windowMs: 60 * 1e3, keyPrefix: "seed-admin" }),
3200
3200
  async (c) => {
3201
3201
  try {
3202
3202
  const db = c.env.DB;
@@ -3218,7 +3218,7 @@ authRoutes.post(
3218
3218
  `).run();
3219
3219
  const existingAdmin = await db.prepare("SELECT id FROM users WHERE email = ? OR username = ?").bind("admin@sonicjs.com", "admin").first();
3220
3220
  if (existingAdmin) {
3221
- const passwordHash2 = await chunkZWKCL46S_cjs.AuthManager.hashPassword("sonicjs!");
3221
+ const passwordHash2 = await chunkDQZVU3WB_cjs.AuthManager.hashPassword("sonicjs!");
3222
3222
  await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
3223
3223
  return c.json({
3224
3224
  message: "Admin user already exists (password updated)",
@@ -3230,7 +3230,7 @@ authRoutes.post(
3230
3230
  }
3231
3231
  });
3232
3232
  }
3233
- const passwordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword("sonicjs!");
3233
+ const passwordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword("sonicjs!");
3234
3234
  const userId = "admin-user-id";
3235
3235
  const now = Date.now();
3236
3236
  const adminEmail = "admin@sonicjs.com".toLowerCase();
@@ -3451,7 +3451,7 @@ authRoutes.post("/accept-invitation", async (c) => {
3451
3451
  if (existingUsername) {
3452
3452
  return c.json({ error: "Username is already taken" }, 400);
3453
3453
  }
3454
- const passwordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
3454
+ const passwordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
3455
3455
  const updateStmt = db.prepare(`
3456
3456
  UPDATE users SET
3457
3457
  username = ?,
@@ -3470,7 +3470,7 @@ authRoutes.post("/accept-invitation", async (c) => {
3470
3470
  Date.now(),
3471
3471
  invitedUser.id
3472
3472
  ).run();
3473
- const authToken = await chunkZWKCL46S_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET);
3473
+ const authToken = await chunkDQZVU3WB_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET);
3474
3474
  cookie.setCookie(c, "auth_token", authToken, {
3475
3475
  httpOnly: true,
3476
3476
  secure: true,
@@ -3487,7 +3487,7 @@ authRoutes.post("/accept-invitation", async (c) => {
3487
3487
  });
3488
3488
  authRoutes.post(
3489
3489
  "/request-password-reset",
3490
- chunkZWKCL46S_cjs.rateLimit({ max: 3, windowMs: 15 * 60 * 1e3, keyPrefix: "password-reset" }),
3490
+ chunkDQZVU3WB_cjs.rateLimit({ max: 3, windowMs: 15 * 60 * 1e3, keyPrefix: "password-reset" }),
3491
3491
  async (c) => {
3492
3492
  try {
3493
3493
  const formData = await c.req.formData();
@@ -3705,7 +3705,7 @@ authRoutes.post("/reset-password", async (c) => {
3705
3705
  if (Date.now() > user.password_reset_expires) {
3706
3706
  return c.json({ error: "Reset token has expired" }, 400);
3707
3707
  }
3708
- const newPasswordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
3708
+ const newPasswordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
3709
3709
  try {
3710
3710
  const historyStmt = db.prepare(`
3711
3711
  INSERT INTO password_history (id, user_id, password_hash, created_at)
@@ -4286,7 +4286,7 @@ function getMDXEditorInitScript(config) {
4286
4286
  const toolbar = config?.toolbar || "full";
4287
4287
  const placeholder = config?.placeholder || "Start writing your content...";
4288
4288
  return `
4289
- // Initialize EasyMDE (Markdown Editor) for all richtext fields
4289
+ // Initialize EasyMDE (Markdown Editor) only for markdown-marked fields
4290
4290
  function initializeMDXEditor() {
4291
4291
  if (typeof EasyMDE === 'undefined') {
4292
4292
  console.warn('EasyMDE not loaded yet, retrying...');
@@ -4295,7 +4295,7 @@ function getMDXEditorInitScript(config) {
4295
4295
  }
4296
4296
 
4297
4297
  // Find all textareas that need EasyMDE
4298
- document.querySelectorAll('.richtext-container textarea').forEach((textarea) => {
4298
+ document.querySelectorAll('.richtext-container[data-editor-provider="easymde"] textarea').forEach((textarea) => {
4299
4299
  // Skip if already initialized
4300
4300
  if (textarea.dataset.mdxeditorInitialized === 'true') {
4301
4301
  return;
@@ -4394,11 +4394,11 @@ function getTinyMCEInitScript(config) {
4394
4394
  const contentCss = skin.includes("dark") ? "dark" : "default";
4395
4395
  const defaultHeight = config?.defaultHeight || 300;
4396
4396
  return `
4397
- // Initialize TinyMCE for all richtext fields
4397
+ // Initialize TinyMCE only for TinyMCE-backed richtext fields
4398
4398
  function initializeTinyMCE() {
4399
4399
  if (typeof tinymce !== 'undefined') {
4400
4400
  // Find all textareas that need TinyMCE
4401
- document.querySelectorAll('.richtext-container textarea').forEach((textarea) => {
4401
+ document.querySelectorAll('.richtext-container[data-editor-provider="tinymce"] textarea').forEach((textarea) => {
4402
4402
  // Skip if already initialized
4403
4403
  if (tinymce.get(textarea.id)) {
4404
4404
  return;
@@ -4782,6 +4782,7 @@ function getReadFieldValueScript() {
4782
4782
  const textarea = fieldWrapper.querySelector('textarea');
4783
4783
  const inputs = Array.from(fieldWrapper.querySelectorAll('input'));
4784
4784
  const checkbox = inputs.find((input) => input.type === 'checkbox');
4785
+ const checkedRadio = inputs.find((input) => input.type === 'radio' && input.checked);
4785
4786
  const nonHiddenInput = inputs.find((input) => input.type !== 'hidden' && input.type !== 'checkbox');
4786
4787
  const hiddenInput = inputs.find((input) => input.type === 'hidden');
4787
4788
 
@@ -4804,6 +4805,10 @@ function getReadFieldValueScript() {
4804
4805
  return checkbox.checked;
4805
4806
  }
4806
4807
 
4808
+ if (fieldType === 'radio') {
4809
+ return checkedRadio ? checkedRadio.value : '';
4810
+ }
4811
+
4807
4812
  if (select) {
4808
4813
  if (select.multiple) {
4809
4814
  return Array.from(select.selectedOptions).map((option) => option.value);
@@ -4830,6 +4835,24 @@ function getReadFieldValueScript() {
4830
4835
  </script>
4831
4836
  `;
4832
4837
  }
4838
+ function isMarkdownEditorFieldType(fieldType) {
4839
+ return fieldType === "markdown" || fieldType === "mdxeditor" || fieldType === "easymde";
4840
+ }
4841
+ function getEditorMetadata(fieldType) {
4842
+ if (fieldType === "richtext" || fieldType === "tinymce") {
4843
+ return {
4844
+ family: "richtext",
4845
+ provider: "tinymce"
4846
+ };
4847
+ }
4848
+ if (isMarkdownEditorFieldType(fieldType)) {
4849
+ return {
4850
+ family: "markdown",
4851
+ provider: "easymde"
4852
+ };
4853
+ }
4854
+ return null;
4855
+ }
4833
4856
  function renderDynamicField(field, options = {}) {
4834
4857
  const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
4835
4858
  const opts = field.field_options || {};
@@ -4843,10 +4866,10 @@ function renderDynamicField(field, options = {}) {
4843
4866
  if (field.field_type === "quill" && !pluginStatuses.quillEnabled) {
4844
4867
  fallbackToTextarea = true;
4845
4868
  fallbackWarning = "\u26A0\uFE0F Quill Editor plugin is inactive. Using textarea fallback.";
4846
- } else if (field.field_type === "mdxeditor" && !pluginStatuses.mdxeditorEnabled) {
4869
+ } else if (isMarkdownEditorFieldType(field.field_type) && !pluginStatuses.mdxeditorEnabled) {
4847
4870
  fallbackToTextarea = true;
4848
- fallbackWarning = "\u26A0\uFE0F MDXEditor plugin is inactive. Using textarea fallback.";
4849
- } else if (field.field_type === "tinymce" && !pluginStatuses.tinymceEnabled) {
4871
+ fallbackWarning = "\u26A0\uFE0F Markdown editor plugin is inactive. Using textarea fallback.";
4872
+ } else if ((field.field_type === "richtext" || field.field_type === "tinymce") && !pluginStatuses.tinymceEnabled) {
4850
4873
  fallbackToTextarea = true;
4851
4874
  fallbackWarning = "\u26A0\uFE0F TinyMCE plugin is inactive. Using textarea fallback.";
4852
4875
  }
@@ -4968,8 +4991,10 @@ function renderDynamicField(field, options = {}) {
4968
4991
  `;
4969
4992
  break;
4970
4993
  case "richtext":
4994
+ case "tinymce": {
4995
+ const editorMetadata = getEditorMetadata(field.field_type);
4971
4996
  fieldHTML = `
4972
- <div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}">
4997
+ <div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}" data-editor-family="${editorMetadata?.family || ""}" data-editor-provider="${editorMetadata?.provider || ""}">
4973
4998
  <textarea
4974
4999
  id="${fieldId}"
4975
5000
  name="${fieldName}"
@@ -4980,6 +5005,7 @@ function renderDynamicField(field, options = {}) {
4980
5005
  </div>
4981
5006
  `;
4982
5007
  break;
5008
+ }
4983
5009
  case "quill":
4984
5010
  fieldHTML = `
4985
5011
  <div class="quill-editor-container" data-field-id="${fieldId}">
@@ -5003,9 +5029,12 @@ function renderDynamicField(field, options = {}) {
5003
5029
  </div>
5004
5030
  `;
5005
5031
  break;
5032
+ case "markdown":
5006
5033
  case "mdxeditor":
5034
+ case "easymde": {
5035
+ const editorMetadata = getEditorMetadata(field.field_type);
5007
5036
  fieldHTML = `
5008
- <div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}">
5037
+ <div class="richtext-container" data-height="${opts.height || 300}" data-toolbar="${opts.toolbar || "full"}" data-editor-family="${editorMetadata?.family || ""}" data-editor-provider="${editorMetadata?.provider || ""}">
5009
5038
  <textarea
5010
5039
  id="${fieldId}"
5011
5040
  name="${fieldName}"
@@ -5016,6 +5045,7 @@ function renderDynamicField(field, options = {}) {
5016
5045
  </div>
5017
5046
  `;
5018
5047
  break;
5048
+ }
5019
5049
  case "number":
5020
5050
  fieldHTML = `
5021
5051
  <input
@@ -5285,6 +5315,39 @@ function renderDynamicField(field, options = {}) {
5285
5315
  ` : ""}
5286
5316
  `;
5287
5317
  break;
5318
+ case "radio":
5319
+ const radioOptions = opts.options || (Array.isArray(opts.enum) ? opts.enum.map((optionValue, index) => ({
5320
+ value: optionValue,
5321
+ label: opts.enumLabels?.[index] || optionValue
5322
+ })) : []);
5323
+ const selectedRadioValue = value !== void 0 && value !== null ? String(value) : opts.default ? String(opts.default) : "";
5324
+ const isInline = opts.inline === true;
5325
+ fieldHTML = `
5326
+ <div class="${isInline ? "flex flex-wrap gap-4" : "space-y-3"}">
5327
+ ${radioOptions.map((option, index) => {
5328
+ const optionValue = typeof option === "string" ? option : option.value;
5329
+ const optionLabel = typeof option === "string" ? option : option.label;
5330
+ const inputId = `${fieldId}-option-${index}`;
5331
+ const checked2 = selectedRadioValue === String(optionValue) ? "checked" : "";
5332
+ return `
5333
+ <label for="${inputId}" class="flex items-center gap-3 text-sm text-zinc-700 dark:text-zinc-300">
5334
+ <input
5335
+ type="radio"
5336
+ id="${inputId}"
5337
+ name="${fieldName}"
5338
+ value="${escapeHtml3(optionValue)}"
5339
+ class="h-4 w-4 text-zinc-900 focus:ring-zinc-400 dark:text-white dark:focus:ring-white"
5340
+ ${checked2}
5341
+ ${required}
5342
+ ${disabled ? "disabled" : ""}
5343
+ >
5344
+ <span>${escapeHtml3(optionLabel)}</span>
5345
+ </label>
5346
+ `;
5347
+ }).join("")}
5348
+ </div>
5349
+ `;
5350
+ break;
5288
5351
  case "reference":
5289
5352
  let referenceCollections = [];
5290
5353
  if (Array.isArray(opts.collection)) {
@@ -5555,8 +5618,19 @@ function renderStructuredArrayField(field, options, baseClasses, errorClasses) {
5555
5618
  const fieldId = `field-${field.field_name}`;
5556
5619
  const fieldName = field.field_name;
5557
5620
  const arrayValue = normalizeStructuredArrayValue(value);
5621
+ const arrayTitle = opts.itemLabel || field.field_label || "Items";
5622
+ const hasItemTitle = typeof opts.itemTitle === "string" && opts.itemTitle.trim() !== "";
5623
+ const arrayItemTitle = hasItemTitle ? opts.itemTitle.trim() : "Item";
5624
+ const addItemLabel = hasItemTitle ? `Add ${arrayItemTitle}` : "Add item";
5558
5625
  const items = arrayValue.map(
5559
- (itemValue, index) => renderStructuredArrayItem(field, itemsConfig, String(index), itemValue, pluginStatuses)
5626
+ (itemValue, index) => renderStructuredArrayItem(
5627
+ field,
5628
+ itemsConfig,
5629
+ String(index),
5630
+ itemValue,
5631
+ pluginStatuses,
5632
+ arrayItemTitle
5633
+ )
5560
5634
  ).join("");
5561
5635
  const emptyState = arrayValue.length === 0 ? `
5562
5636
  <div class="rounded-lg border border-dashed border-zinc-200 dark:border-white/10 px-4 py-6 text-center text-sm text-zinc-500 dark:text-zinc-400" data-structured-empty>
@@ -5569,14 +5643,14 @@ function renderStructuredArrayField(field, options, baseClasses, errorClasses) {
5569
5643
 
5570
5644
  <div class="flex items-center justify-between gap-3">
5571
5645
  <div class="text-sm text-zinc-500 dark:text-zinc-400">
5572
- ${escapeHtml3(opts.itemLabel || "Items")}
5646
+ ${escapeHtml3(arrayTitle)}
5573
5647
  </div>
5574
5648
  <button
5575
5649
  type="button"
5576
5650
  data-action="add-item"
5577
5651
  class="inline-flex items-center justify-center rounded-lg bg-zinc-900 px-3 py-2 text-sm font-semibold text-white hover:bg-zinc-800 dark:bg-white/10 dark:hover:bg-white/20"
5578
5652
  >
5579
- Add item
5653
+ ${escapeHtml3(addItemLabel)}
5580
5654
  </button>
5581
5655
  </div>
5582
5656
 
@@ -5585,14 +5659,21 @@ function renderStructuredArrayField(field, options, baseClasses, errorClasses) {
5585
5659
  </div>
5586
5660
 
5587
5661
  <template data-structured-array-template>
5588
- ${renderStructuredArrayItem(field, itemsConfig, "__INDEX__", {}, pluginStatuses)}
5662
+ ${renderStructuredArrayItem(
5663
+ field,
5664
+ itemsConfig,
5665
+ "__INDEX__",
5666
+ {},
5667
+ pluginStatuses,
5668
+ arrayItemTitle
5669
+ )}
5589
5670
  </template>
5590
5671
  </div>
5591
5672
  ${getDragSortableScript()}
5592
5673
  ${getStructuredFieldScript()}
5593
5674
  `;
5594
5675
  }
5595
- function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginStatuses) {
5676
+ function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginStatuses, arrayItemTitle) {
5596
5677
  const itemFields = renderStructuredItemFields(field, itemConfig, index, itemValue, pluginStatuses);
5597
5678
  return `
5598
5679
  <div class="structured-array-item rounded-lg border border-zinc-200 dark:border-white/10 bg-white/60 dark:bg-white/5 p-4 shadow-sm" data-array-index="${escapeHtml3(index)}" draggable="true">
@@ -5603,8 +5684,8 @@ function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginSt
5603
5684
  <path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/>
5604
5685
  </svg>
5605
5686
  </div>
5606
- <div class="text-sm font-semibold text-zinc-900 dark:text-white">
5607
- Item <span class="ml-2 text-xs font-normal text-zinc-500 dark:text-zinc-400" data-array-order-label></span>
5687
+ <div class="text-sm font-semibold text-zinc-900 dark:text-white cursor-pointer" data-action="toggle-item">
5688
+ ${escapeHtml3(arrayItemTitle)} <span class="ml-2 text-xs font-normal text-zinc-500 dark:text-zinc-400" data-array-order-label></span>
5608
5689
  </div>
5609
5690
  </div>
5610
5691
  <div class="flex flex-wrap gap-2 text-xs">
@@ -5960,8 +6041,15 @@ function getStructuredFieldScript() {
5960
6041
  if (!item || !list) return;
5961
6042
 
5962
6043
  if (action === 'remove-item') {
5963
- item.remove();
5964
- updateHiddenInput();
6044
+ if (typeof requestRepeaterDelete === 'function') {
6045
+ requestRepeaterDelete(() => {
6046
+ item.remove();
6047
+ updateHiddenInput();
6048
+ });
6049
+ } else {
6050
+ item.remove();
6051
+ updateHiddenInput();
6052
+ }
5965
6053
  return;
5966
6054
  }
5967
6055
 
@@ -6152,8 +6240,15 @@ function getBlocksFieldScript() {
6152
6240
  if (!item || !list) return;
6153
6241
 
6154
6242
  if (action === 'remove-block') {
6155
- item.remove();
6156
- updateHiddenInput();
6243
+ if (typeof requestRepeaterDelete === 'function') {
6244
+ requestRepeaterDelete(() => {
6245
+ item.remove();
6246
+ updateHiddenInput();
6247
+ }, 'block');
6248
+ } else {
6249
+ item.remove();
6250
+ updateHiddenInput();
6251
+ }
6157
6252
  return;
6158
6253
  }
6159
6254
 
@@ -6562,6 +6657,28 @@ function renderContentFormPage(data) {
6562
6657
  onConfirm: `performDeleteContent('${data.id}')`
6563
6658
  })}
6564
6659
 
6660
+ ${chunkLTKV7AE5_cjs.renderConfirmationDialog({
6661
+ id: "delete-repeater-item-confirm",
6662
+ title: "Delete Item",
6663
+ message: "Are you sure you want to delete this item? This action cannot be undone.",
6664
+ confirmText: "Delete",
6665
+ cancelText: "Cancel",
6666
+ iconColor: "red",
6667
+ confirmClass: "bg-red-500 hover:bg-red-400",
6668
+ onConfirm: "performRepeaterDelete()"
6669
+ })}
6670
+
6671
+ ${chunkLTKV7AE5_cjs.renderConfirmationDialog({
6672
+ id: "delete-block-confirm",
6673
+ title: "Delete Block",
6674
+ message: "Are you sure you want to delete this block? This action cannot be undone.",
6675
+ confirmText: "Delete",
6676
+ cancelText: "Cancel",
6677
+ iconColor: "red",
6678
+ confirmClass: "bg-red-500 hover:bg-red-400",
6679
+ onConfirm: "performRepeaterDelete()"
6680
+ })}
6681
+
6565
6682
  ${chunkLTKV7AE5_cjs.getConfirmationDialogScript()}
6566
6683
 
6567
6684
  ${data.tinymceEnabled ? getTinyMCEScript(data.tinymceSettings?.apiKey) : "<!-- TinyMCE plugin not active -->"}
@@ -7118,6 +7235,29 @@ function renderContentFormPage(data) {
7118
7235
  });
7119
7236
  }
7120
7237
 
7238
+ // Repeater/blocks delete confirmation
7239
+ let pendingRepeaterDelete = null;
7240
+ function requestRepeaterDelete(callback, type = 'item') {
7241
+ pendingRepeaterDelete = callback;
7242
+ if (typeof showConfirmDialog === 'function') {
7243
+ showConfirmDialog(type === 'block' ? 'delete-block-confirm' : 'delete-repeater-item-confirm');
7244
+ return;
7245
+ }
7246
+ if (confirm('Remove this item? This action cannot be undone.')) {
7247
+ if (typeof pendingRepeaterDelete === 'function') {
7248
+ pendingRepeaterDelete();
7249
+ }
7250
+ }
7251
+ pendingRepeaterDelete = null;
7252
+ }
7253
+
7254
+ function performRepeaterDelete() {
7255
+ if (typeof pendingRepeaterDelete === 'function') {
7256
+ pendingRepeaterDelete();
7257
+ }
7258
+ pendingRepeaterDelete = null;
7259
+ }
7260
+
7121
7261
  function showVersionHistory(contentId) {
7122
7262
  // Create and show version history modal
7123
7263
  const modal = document.createElement('div');
@@ -8124,15 +8264,49 @@ function renderContentListPage(data) {
8124
8264
  return chunkLTKV7AE5_cjs.renderAdminLayoutCatalyst(layoutData);
8125
8265
  }
8126
8266
 
8267
+ // src/routes/admin-content-field-types.ts
8268
+ function resolveSchemaFieldType(fieldConfig) {
8269
+ if (fieldConfig.type === "slug" || fieldConfig.format === "slug") {
8270
+ return "slug";
8271
+ }
8272
+ if (fieldConfig.type && fieldConfig.type !== "string") {
8273
+ return fieldConfig.type;
8274
+ }
8275
+ if (fieldConfig.format === "richtext") {
8276
+ return "richtext";
8277
+ }
8278
+ if (fieldConfig.format === "media") {
8279
+ return "media";
8280
+ }
8281
+ if (fieldConfig.format === "date-time") {
8282
+ return "date";
8283
+ }
8284
+ if (Array.isArray(fieldConfig.enum)) {
8285
+ return "select";
8286
+ }
8287
+ return fieldConfig.type || "string";
8288
+ }
8289
+ function buildSchemaFieldOptions(fieldConfig) {
8290
+ const fieldOptions = { ...fieldConfig };
8291
+ const resolvedFieldType = resolveSchemaFieldType(fieldConfig);
8292
+ if (resolvedFieldType === "select" && Array.isArray(fieldConfig.enum)) {
8293
+ fieldOptions.options = fieldConfig.enum.map((value, index) => ({
8294
+ value,
8295
+ label: fieldConfig.enumLabels?.[index] || value
8296
+ }));
8297
+ }
8298
+ return fieldOptions;
8299
+ }
8300
+
8127
8301
  // src/routes/admin-content.ts
8128
8302
  var adminContentRoutes = new hono.Hono();
8129
8303
  function parseFieldValue(field, formData, options = {}) {
8130
8304
  const { skipValidation = false } = options;
8131
8305
  const value = formData.get(field.field_name);
8132
8306
  const errors = [];
8133
- const blocksConfig = chunkJSHIGVIF_cjs.getBlocksFieldConfig(field.field_options);
8307
+ const blocksConfig = chunkSHU7Q66Q_cjs.getBlocksFieldConfig(field.field_options);
8134
8308
  if (blocksConfig) {
8135
- const parsed = chunkJSHIGVIF_cjs.parseBlocksValue(value, blocksConfig);
8309
+ const parsed = chunkSHU7Q66Q_cjs.parseBlocksValue(value, blocksConfig);
8136
8310
  if (!skipValidation && field.is_required && parsed.value.length === 0) {
8137
8311
  parsed.errors.push(`${field.field_label} is required`);
8138
8312
  }
@@ -8242,9 +8416,9 @@ function extractFieldData(fields, formData, options = {}) {
8242
8416
  }
8243
8417
  return { data, errors };
8244
8418
  }
8245
- adminContentRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
8419
+ adminContentRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
8246
8420
  async function getCollectionFields(db, collectionId) {
8247
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.collection);
8421
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.collection);
8248
8422
  return cache.getOrSet(
8249
8423
  cache.generateKey("fields", collectionId),
8250
8424
  async () => {
@@ -8256,17 +8430,11 @@ async function getCollectionFields(db, collectionId) {
8256
8430
  if (schema && schema.properties) {
8257
8431
  let fieldOrder = 0;
8258
8432
  return Object.entries(schema.properties).map(([fieldName, fieldConfig]) => {
8259
- let fieldOptions = { ...fieldConfig };
8260
- if (fieldConfig.type === "select" && fieldConfig.enum) {
8261
- fieldOptions.options = fieldConfig.enum.map((value, index) => ({
8262
- value,
8263
- label: fieldConfig.enumLabels?.[index] || value
8264
- }));
8265
- }
8433
+ const fieldOptions = buildSchemaFieldOptions(fieldConfig);
8266
8434
  return {
8267
8435
  id: `schema-${fieldName}`,
8268
8436
  field_name: fieldName,
8269
- field_type: fieldConfig.type || "string",
8437
+ field_type: resolveSchemaFieldType(fieldConfig),
8270
8438
  field_label: fieldConfig.title || fieldName,
8271
8439
  field_options: fieldOptions,
8272
8440
  field_order: fieldOrder++,
@@ -8299,7 +8467,7 @@ async function getCollectionFields(db, collectionId) {
8299
8467
  );
8300
8468
  }
8301
8469
  async function getCollection(db, collectionId) {
8302
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.collection);
8470
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.collection);
8303
8471
  return cache.getOrSet(
8304
8472
  cache.generateKey("collection", collectionId),
8305
8473
  async () => {
@@ -8588,7 +8756,7 @@ adminContentRoutes.get("/:id/edit", async (c) => {
8588
8756
  const db = c.env.DB;
8589
8757
  const url = new URL(c.req.url);
8590
8758
  const referrerParams = url.searchParams.get("ref") || "";
8591
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.content);
8759
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
8592
8760
  const content = await cache.getOrSet(
8593
8761
  cache.generateKey("content", id),
8594
8762
  async () => {
@@ -8764,7 +8932,7 @@ adminContentRoutes.post("/", async (c) => {
8764
8932
  now,
8765
8933
  now
8766
8934
  ).run();
8767
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.content);
8935
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
8768
8936
  await cache.invalidate(`content:list:${collectionId}:*`);
8769
8937
  const versionStmt = db.prepare(`
8770
8938
  INSERT INTO content_versions (id, content_id, version, data, author_id, created_at)
@@ -8883,7 +9051,7 @@ adminContentRoutes.put("/:id", async (c) => {
8883
9051
  now,
8884
9052
  id
8885
9053
  ).run();
8886
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.content);
9054
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
8887
9055
  await cache.delete(cache.generateKey("content", id));
8888
9056
  await cache.invalidate(`content:list:${existingContent.collection_id}:*`);
8889
9057
  const existingData = JSON.parse(existingContent.data || "{}");
@@ -8938,7 +9106,7 @@ adminContentRoutes.put("/:id", async (c) => {
8938
9106
  `);
8939
9107
  }
8940
9108
  });
8941
- adminContentRoutes.post("/preview", chunkZWKCL46S_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
9109
+ adminContentRoutes.post("/preview", chunkDQZVU3WB_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
8942
9110
  try {
8943
9111
  const formData = await c.req.formData();
8944
9112
  const collectionId = formData.get("collection_id");
@@ -9160,7 +9328,7 @@ adminContentRoutes.post("/bulk-action", async (c) => {
9160
9328
  } else {
9161
9329
  return c.json({ success: false, error: "Invalid action" });
9162
9330
  }
9163
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.content);
9331
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
9164
9332
  for (const contentId of ids) {
9165
9333
  await cache.delete(cache.generateKey("content", contentId));
9166
9334
  }
@@ -9188,7 +9356,7 @@ adminContentRoutes.delete("/:id", async (c) => {
9188
9356
  WHERE id = ?
9189
9357
  `);
9190
9358
  await deleteStmt.bind(now, id).run();
9191
- const cache = chunkVNLR35GO_cjs.getCacheService(chunkVNLR35GO_cjs.CACHE_CONFIGS.content);
9359
+ const cache = chunk64APW3DW_cjs.getCacheService(chunk64APW3DW_cjs.CACHE_CONFIGS.content);
9192
9360
  await cache.delete(cache.generateKey("content", id));
9193
9361
  await cache.invalidate("content:list:*");
9194
9362
  return c.html(`
@@ -9316,7 +9484,7 @@ adminContentRoutes.post("/:id/restore/:version", async (c) => {
9316
9484
  return c.json({ success: false, error: "Failed to restore version" });
9317
9485
  }
9318
9486
  });
9319
- adminContentRoutes.get("/:id/version/:version/preview", chunkZWKCL46S_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
9487
+ adminContentRoutes.get("/:id/version/:version/preview", chunkDQZVU3WB_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
9320
9488
  try {
9321
9489
  const id = c.req.param("id");
9322
9490
  const version = parseInt(c.req.param("version") || "0");
@@ -11283,7 +11451,7 @@ function renderUsersListPage(data) {
11283
11451
 
11284
11452
  // src/routes/admin-users.ts
11285
11453
  var userRoutes = new hono.Hono();
11286
- userRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
11454
+ userRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
11287
11455
  userRoutes.get("/", (c) => {
11288
11456
  return c.redirect("/admin/dashboard");
11289
11457
  });
@@ -11438,7 +11606,7 @@ userRoutes.put("/profile", async (c) => {
11438
11606
  Date.now(),
11439
11607
  user.userId
11440
11608
  ).run();
11441
- await chunkZWKCL46S_cjs.logActivity(
11609
+ await chunkDQZVU3WB_cjs.logActivity(
11442
11610
  db,
11443
11611
  user.userId,
11444
11612
  "profile.update",
@@ -11501,7 +11669,7 @@ userRoutes.post("/profile/avatar", async (c) => {
11501
11669
  SELECT first_name, last_name FROM users WHERE id = ?
11502
11670
  `);
11503
11671
  const userData = await userStmt.bind(user.userId).first();
11504
- await chunkZWKCL46S_cjs.logActivity(
11672
+ await chunkDQZVU3WB_cjs.logActivity(
11505
11673
  db,
11506
11674
  user.userId,
11507
11675
  "profile.avatar_update",
@@ -11572,7 +11740,7 @@ userRoutes.post("/profile/password", async (c) => {
11572
11740
  dismissible: true
11573
11741
  }));
11574
11742
  }
11575
- const validPassword = await chunkZWKCL46S_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
11743
+ const validPassword = await chunkDQZVU3WB_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
11576
11744
  if (!validPassword) {
11577
11745
  return c.html(renderAlert2({
11578
11746
  type: "error",
@@ -11580,7 +11748,7 @@ userRoutes.post("/profile/password", async (c) => {
11580
11748
  dismissible: true
11581
11749
  }));
11582
11750
  }
11583
- const newPasswordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(newPassword);
11751
+ const newPasswordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(newPassword);
11584
11752
  const historyStmt = db.prepare(`
11585
11753
  INSERT INTO password_history (id, user_id, password_hash, created_at)
11586
11754
  VALUES (?, ?, ?, ?)
@@ -11596,7 +11764,7 @@ userRoutes.post("/profile/password", async (c) => {
11596
11764
  WHERE id = ?
11597
11765
  `);
11598
11766
  await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
11599
- await chunkZWKCL46S_cjs.logActivity(
11767
+ await chunkDQZVU3WB_cjs.logActivity(
11600
11768
  db,
11601
11769
  user.userId,
11602
11770
  "profile.password_change",
@@ -11663,7 +11831,7 @@ userRoutes.get("/users", async (c) => {
11663
11831
  `);
11664
11832
  const countResult = await countStmt.bind(...params).first();
11665
11833
  const totalUsers = countResult?.total || 0;
11666
- await chunkZWKCL46S_cjs.logActivity(
11834
+ await chunkDQZVU3WB_cjs.logActivity(
11667
11835
  db,
11668
11836
  user.userId,
11669
11837
  "users.list_view",
@@ -11817,7 +11985,7 @@ userRoutes.post("/users/new", async (c) => {
11817
11985
  dismissible: true
11818
11986
  }));
11819
11987
  }
11820
- const passwordHash = await chunkZWKCL46S_cjs.AuthManager.hashPassword(password);
11988
+ const passwordHash = await chunkDQZVU3WB_cjs.AuthManager.hashPassword(password);
11821
11989
  const userId = crypto.randomUUID();
11822
11990
  const createStmt = db.prepare(`
11823
11991
  INSERT INTO users (
@@ -11840,7 +12008,7 @@ userRoutes.post("/users/new", async (c) => {
11840
12008
  Date.now(),
11841
12009
  Date.now()
11842
12010
  ).run();
11843
- await chunkZWKCL46S_cjs.logActivity(
12011
+ await chunkDQZVU3WB_cjs.logActivity(
11844
12012
  db,
11845
12013
  user.userId,
11846
12014
  "user!.create",
@@ -11878,7 +12046,7 @@ userRoutes.get("/users/:id", async (c) => {
11878
12046
  if (!userRecord) {
11879
12047
  return c.json({ error: "User not found" }, 404);
11880
12048
  }
11881
- await chunkZWKCL46S_cjs.logActivity(
12049
+ await chunkDQZVU3WB_cjs.logActivity(
11882
12050
  db,
11883
12051
  user.userId,
11884
12052
  "user!.view",
@@ -12103,7 +12271,7 @@ userRoutes.put("/users/:id", async (c) => {
12103
12271
  ).run();
12104
12272
  }
12105
12273
  }
12106
- await chunkZWKCL46S_cjs.logActivity(
12274
+ await chunkDQZVU3WB_cjs.logActivity(
12107
12275
  db,
12108
12276
  user.userId,
12109
12277
  "user.update",
@@ -12148,7 +12316,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
12148
12316
  UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?
12149
12317
  `);
12150
12318
  await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
12151
- await chunkZWKCL46S_cjs.logActivity(
12319
+ await chunkDQZVU3WB_cjs.logActivity(
12152
12320
  db,
12153
12321
  user.userId,
12154
12322
  active ? "user.activate" : "user.deactivate",
@@ -12189,7 +12357,7 @@ userRoutes.delete("/users/:id", async (c) => {
12189
12357
  DELETE FROM users WHERE id = ?
12190
12358
  `);
12191
12359
  await deleteStmt.bind(userId).run();
12192
- await chunkZWKCL46S_cjs.logActivity(
12360
+ await chunkDQZVU3WB_cjs.logActivity(
12193
12361
  db,
12194
12362
  user.userId,
12195
12363
  "user!.hard_delete",
@@ -12208,7 +12376,7 @@ userRoutes.delete("/users/:id", async (c) => {
12208
12376
  UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
12209
12377
  `);
12210
12378
  await deleteStmt.bind(Date.now(), userId).run();
12211
- await chunkZWKCL46S_cjs.logActivity(
12379
+ await chunkDQZVU3WB_cjs.logActivity(
12212
12380
  db,
12213
12381
  user.userId,
12214
12382
  "user!.soft_delete",
@@ -12274,7 +12442,7 @@ userRoutes.post("/invite-user", async (c) => {
12274
12442
  Date.now(),
12275
12443
  Date.now()
12276
12444
  ).run();
12277
- await chunkZWKCL46S_cjs.logActivity(
12445
+ await chunkDQZVU3WB_cjs.logActivity(
12278
12446
  db,
12279
12447
  user.userId,
12280
12448
  "user!.invite_sent",
@@ -12331,7 +12499,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
12331
12499
  Date.now(),
12332
12500
  userId
12333
12501
  ).run();
12334
- await chunkZWKCL46S_cjs.logActivity(
12502
+ await chunkDQZVU3WB_cjs.logActivity(
12335
12503
  db,
12336
12504
  user.userId,
12337
12505
  "user!.invitation_resent",
@@ -12367,7 +12535,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
12367
12535
  }
12368
12536
  const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
12369
12537
  await deleteStmt.bind(userId).run();
12370
- await chunkZWKCL46S_cjs.logActivity(
12538
+ await chunkDQZVU3WB_cjs.logActivity(
12371
12539
  db,
12372
12540
  user.userId,
12373
12541
  "user!.invitation_cancelled",
@@ -12450,7 +12618,7 @@ userRoutes.get("/activity-logs", async (c) => {
12450
12618
  ...log,
12451
12619
  details: log.details ? JSON.parse(log.details) : null
12452
12620
  }));
12453
- await chunkZWKCL46S_cjs.logActivity(
12621
+ await chunkDQZVU3WB_cjs.logActivity(
12454
12622
  db,
12455
12623
  user.userId,
12456
12624
  "activity.logs_viewed",
@@ -12557,7 +12725,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
12557
12725
  csvRows.push(row.join(","));
12558
12726
  }
12559
12727
  const csvContent = csvRows.join("\n");
12560
- await chunkZWKCL46S_cjs.logActivity(
12728
+ await chunkDQZVU3WB_cjs.logActivity(
12561
12729
  db,
12562
12730
  user.userId,
12563
12731
  "activity.logs_exported",
@@ -13896,7 +14064,7 @@ var fileValidationSchema2 = zod.z.object({
13896
14064
  // 50MB max
13897
14065
  });
13898
14066
  var adminMediaRoutes = new hono.Hono();
13899
- adminMediaRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
14067
+ adminMediaRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
13900
14068
  adminMediaRoutes.get("/", async (c) => {
13901
14069
  try {
13902
14070
  const user = c.get("user");
@@ -14482,7 +14650,7 @@ adminMediaRoutes.put("/:id", async (c) => {
14482
14650
  `);
14483
14651
  }
14484
14652
  });
14485
- adminMediaRoutes.delete("/cleanup", chunkZWKCL46S_cjs.requireRole("admin"), async (c) => {
14653
+ adminMediaRoutes.delete("/cleanup", chunkDQZVU3WB_cjs.requireRole("admin"), async (c) => {
14486
14654
  try {
14487
14655
  const db = c.env.DB;
14488
14656
  const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
@@ -16705,7 +16873,7 @@ function renderEmailSettingsContent(plugin, settings) {
16705
16873
 
16706
16874
  // src/routes/admin-plugins.ts
16707
16875
  var adminPluginRoutes = new hono.Hono();
16708
- adminPluginRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
16876
+ adminPluginRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
16709
16877
  var AVAILABLE_PLUGINS = [
16710
16878
  {
16711
16879
  id: "third-party-faq",
@@ -18110,11 +18278,11 @@ function renderLogConfigPage(data) {
18110
18278
 
18111
18279
  // src/routes/admin-logs.ts
18112
18280
  var adminLogsRoutes = new hono.Hono();
18113
- adminLogsRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
18281
+ adminLogsRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
18114
18282
  adminLogsRoutes.get("/", async (c) => {
18115
18283
  try {
18116
18284
  const user = c.get("user");
18117
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18285
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18118
18286
  const query = c.req.query();
18119
18287
  const page = parseInt(query.page || "1");
18120
18288
  const limit = parseInt(query.limit || "50");
@@ -18194,7 +18362,7 @@ adminLogsRoutes.get("/:id", async (c) => {
18194
18362
  try {
18195
18363
  const id = c.req.param("id");
18196
18364
  const user = c.get("user");
18197
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18365
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18198
18366
  const { logs } = await logger.getLogs({
18199
18367
  limit: 1,
18200
18368
  offset: 0,
@@ -18231,7 +18399,7 @@ adminLogsRoutes.get("/:id", async (c) => {
18231
18399
  adminLogsRoutes.get("/config", async (c) => {
18232
18400
  try {
18233
18401
  const user = c.get("user");
18234
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18402
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18235
18403
  const configs = await logger.getAllConfigs();
18236
18404
  const pageData = {
18237
18405
  configs,
@@ -18255,7 +18423,7 @@ adminLogsRoutes.post("/config/:category", async (c) => {
18255
18423
  const level = formData.get("level");
18256
18424
  const retention = parseInt(formData.get("retention"));
18257
18425
  const maxSize = parseInt(formData.get("max_size"));
18258
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18426
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18259
18427
  await logger.updateConfig(category, {
18260
18428
  enabled,
18261
18429
  level,
@@ -18284,7 +18452,7 @@ adminLogsRoutes.get("/export", async (c) => {
18284
18452
  const category = query.category;
18285
18453
  const startDate = query.start_date;
18286
18454
  const endDate = query.end_date;
18287
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18455
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18288
18456
  const filter = {
18289
18457
  limit: 1e4,
18290
18458
  // Export up to 10k logs
@@ -18365,7 +18533,7 @@ adminLogsRoutes.post("/cleanup", async (c) => {
18365
18533
  error: "Unauthorized. Admin access required."
18366
18534
  }, 403);
18367
18535
  }
18368
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18536
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18369
18537
  await logger.cleanupByRetention();
18370
18538
  return c.html(html.html`
18371
18539
  <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
@@ -18387,7 +18555,7 @@ adminLogsRoutes.post("/search", async (c) => {
18387
18555
  const search = formData.get("search");
18388
18556
  const level = formData.get("level");
18389
18557
  const category = formData.get("category");
18390
- const logger = chunkVNLR35GO_cjs.getLogger(c.env.DB);
18558
+ const logger = chunk64APW3DW_cjs.getLogger(c.env.DB);
18391
18559
  const filter = {
18392
18560
  limit: 20,
18393
18561
  offset: 0,
@@ -20438,9 +20606,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
20438
20606
  }
20439
20607
 
20440
20608
  // src/routes/admin-dashboard.ts
20441
- var VERSION = chunkJSHIGVIF_cjs.getCoreVersion();
20609
+ var VERSION = chunkSHU7Q66Q_cjs.getCoreVersion();
20442
20610
  var router = new hono.Hono();
20443
- router.use("*", chunkZWKCL46S_cjs.requireAuth());
20611
+ router.use("*", chunkDQZVU3WB_cjs.requireAuth());
20444
20612
  router.get("/", async (c) => {
20445
20613
  const user = c.get("user");
20446
20614
  try {
@@ -20664,6 +20832,20 @@ router.get("/system-status", async (c) => {
20664
20832
  }
20665
20833
  });
20666
20834
 
20835
+ // src/routes/admin-collections-field-types.ts
20836
+ function isMarkdownEditorType(fieldType) {
20837
+ return fieldType === "markdown" || fieldType === "mdxeditor" || fieldType === "easymde";
20838
+ }
20839
+ function normalizeFieldType(fieldType) {
20840
+ if (isMarkdownEditorType(fieldType)) {
20841
+ return "markdown";
20842
+ }
20843
+ if (fieldType === "tinymce") {
20844
+ return "richtext";
20845
+ }
20846
+ return fieldType;
20847
+ }
20848
+
20667
20849
  // src/templates/pages/admin-collections-list.template.ts
20668
20850
  chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
20669
20851
 
@@ -21150,7 +21332,9 @@ function getFieldTypeBadge(fieldType) {
21150
21332
  "slug": "URL Slug",
21151
21333
  "richtext": "Rich Text (TinyMCE)",
21152
21334
  "quill": "Rich Text (Quill)",
21153
- "mdxeditor": "EasyMDX",
21335
+ "markdown": "Markdown",
21336
+ "mdxeditor": "Markdown",
21337
+ "easymde": "Markdown",
21154
21338
  "number": "Number",
21155
21339
  "boolean": "Boolean",
21156
21340
  "date": "Date",
@@ -21163,7 +21347,9 @@ function getFieldTypeBadge(fieldType) {
21163
21347
  "slug": "bg-sky-500/10 dark:bg-sky-400/10 text-sky-700 dark:text-sky-300 ring-sky-500/20 dark:ring-sky-400/20",
21164
21348
  "richtext": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
21165
21349
  "quill": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
21350
+ "markdown": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
21166
21351
  "mdxeditor": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
21352
+ "easymde": "bg-purple-500/10 dark:bg-purple-400/10 text-purple-700 dark:text-purple-300 ring-purple-500/20 dark:ring-purple-400/20",
21167
21353
  "number": "bg-green-500/10 dark:bg-green-400/10 text-green-700 dark:text-green-300 ring-green-500/20 dark:ring-green-400/20",
21168
21354
  "boolean": "bg-amber-500/10 dark:bg-amber-400/10 text-amber-700 dark:text-amber-300 ring-amber-500/20 dark:ring-amber-400/20",
21169
21355
  "date": "bg-cyan-500/10 dark:bg-cyan-400/10 text-cyan-700 dark:text-cyan-300 ring-cyan-500/20 dark:ring-cyan-400/20",
@@ -21644,10 +21830,11 @@ function renderCollectionFormPage(data) {
21644
21830
  <option value="slug">URL Slug</option>
21645
21831
  ${data.editorPlugins?.tinymce ? '<option value="richtext">Rich Text (TinyMCE)</option>' : ""}
21646
21832
  ${data.editorPlugins?.quill ? '<option value="quill">Rich Text (Quill)</option>' : ""}
21647
- ${data.editorPlugins?.easyMdx ? '<option value="mdxeditor">EasyMDX</option>' : ""}
21833
+ ${data.editorPlugins?.easyMdx ? '<option value="markdown">Markdown</option>' : ""}
21648
21834
  <option value="number">Number</option>
21649
21835
  <option value="boolean">Boolean</option>
21650
21836
  <option value="date">Date</option>
21837
+ <option value="radio">Radio</option>
21651
21838
  <option value="select">Select</option>
21652
21839
  <option value="media">Media</option>
21653
21840
  <option value="reference">Reference</option>
@@ -21868,7 +22055,7 @@ function renderCollectionFormPage(data) {
21868
22055
  // Check if it's a schema field with field_options that might indicate the actual type
21869
22056
  if (field.field_options && typeof field.field_options === 'object') {
21870
22057
  // Only convert to richtext if type is explicitly 'string' and format is richtext
21871
- // Don't convert if it's already a specific editor type like 'mdxeditor', 'quill', etc.
22058
+ // Don't convert if it's already a specific editor type like 'markdown', 'quill', etc.
21872
22059
  if (field.field_options.format === 'richtext' && uiFieldType === 'string') {
21873
22060
  uiFieldType = 'richtext';
21874
22061
  }
@@ -21889,6 +22076,12 @@ function renderCollectionFormPage(data) {
21889
22076
  uiFieldType = typeMapping[uiFieldType];
21890
22077
  }
21891
22078
 
22079
+ if (uiFieldType === 'mdxeditor' || uiFieldType === 'easymde') {
22080
+ uiFieldType = 'markdown';
22081
+ } else if (uiFieldType === 'tinymce') {
22082
+ uiFieldType = 'richtext';
22083
+ }
22084
+
21892
22085
  // Log all available options
21893
22086
  const availableOptions = Array.from(fieldTypeSelect.options).map(opt => ({ value: opt.value, text: opt.text }));
21894
22087
  console.log('Available dropdown options:', availableOptions);
@@ -21975,7 +22168,7 @@ function renderCollectionFormPage(data) {
21975
22168
 
21976
22169
  console.log('[Edit Field] Showing options for field type:', fieldType, '(original:', field.field_type, ')');
21977
22170
 
21978
- if (['select', 'media', 'richtext', 'reference'].includes(fieldType)) {
22171
+ if (['select', 'radio', 'media', 'richtext', 'markdown', 'reference'].includes(fieldType)) {
21979
22172
  optionsContainer.classList.remove('hidden');
21980
22173
 
21981
22174
  // Set help text based on type
@@ -21983,12 +22176,18 @@ function renderCollectionFormPage(data) {
21983
22176
  case 'select':
21984
22177
  helpText.textContent = 'Create a dropdown select field with custom options';
21985
22178
  break;
22179
+ case 'radio':
22180
+ helpText.textContent = 'Single selection from a list of radio options';
22181
+ break;
21986
22182
  case 'media':
21987
22183
  helpText.textContent = 'Upload and manage media files (images, videos, documents)';
21988
22184
  break;
21989
22185
  case 'richtext':
21990
22186
  helpText.textContent = 'Full-featured WYSIWYG text editor with formatting options';
21991
22187
  break;
22188
+ case 'markdown':
22189
+ helpText.textContent = 'Markdown editor with live preview powered by the EasyMDE plugin';
22190
+ break;
21992
22191
  case 'reference':
21993
22192
  helpText.textContent = 'Link to content from other collections';
21994
22193
  break;
@@ -22129,7 +22328,7 @@ function renderCollectionFormPage(data) {
22129
22328
  const fieldNameInput = document.getElementById('modal-field-name');
22130
22329
 
22131
22330
  // Show/hide options based on field type
22132
- if (['select', 'media', 'richtext', 'guid', 'reference'].includes(this.value)) {
22331
+ if (['select', 'radio', 'media', 'richtext', 'markdown', 'guid', 'reference'].includes(this.value)) {
22133
22332
  optionsContainer.classList.remove('hidden');
22134
22333
 
22135
22334
  // Set default options and help text based on type
@@ -22138,6 +22337,10 @@ function renderCollectionFormPage(data) {
22138
22337
  fieldOptions.value = '{"options": ["Option 1", "Option 2"], "multiple": false}';
22139
22338
  helpText.textContent = 'Create a dropdown select field with custom options';
22140
22339
  break;
22340
+ case 'radio':
22341
+ fieldOptions.value = '{"enum": ["Option 1", "Option 2"], "enumLabels": ["Option 1", "Option 2"], "default": "Option 1", "inline": false}';
22342
+ helpText.textContent = 'Single selection from a list of radio options';
22343
+ break;
22141
22344
  case 'media':
22142
22345
  fieldOptions.value = '{"accept": "image/*", "maxSize": "10MB"}';
22143
22346
  helpText.textContent = 'Upload and manage media files (images, videos, documents)';
@@ -22146,6 +22349,10 @@ function renderCollectionFormPage(data) {
22146
22349
  fieldOptions.value = '{"toolbar": "full", "height": 400}';
22147
22350
  helpText.textContent = 'Full-featured WYSIWYG text editor with formatting options';
22148
22351
  break;
22352
+ case 'markdown':
22353
+ fieldOptions.value = '{"toolbar": "full", "height": 400}';
22354
+ helpText.textContent = 'Markdown editor with live preview powered by the EasyMDE plugin';
22355
+ break;
22149
22356
  case 'reference':
22150
22357
  fieldOptions.value = '{"collection": ["pages", "posts"]}';
22151
22358
  helpText.textContent = 'Link to content from other collections';
@@ -22220,7 +22427,7 @@ function renderCollectionFormPage(data) {
22220
22427
 
22221
22428
  // src/routes/admin-collections.ts
22222
22429
  var adminCollectionsRoutes = new hono.Hono();
22223
- adminCollectionsRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
22430
+ adminCollectionsRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
22224
22431
  adminCollectionsRoutes.get("/", async (c) => {
22225
22432
  try {
22226
22433
  const user = c.get("user");
@@ -22706,22 +22913,28 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
22706
22913
  searchable: isSearchable,
22707
22914
  ...parsedOptions
22708
22915
  };
22709
- if (fieldType === "richtext") {
22916
+ const normalizedFieldType = normalizeFieldType(fieldType);
22917
+ if (normalizedFieldType === "richtext") {
22710
22918
  fieldConfig.format = "richtext";
22711
- } else if (fieldType === "date") {
22919
+ } else if (normalizedFieldType === "date") {
22712
22920
  fieldConfig.format = "date-time";
22713
- } else if (fieldType === "select") {
22921
+ } else if (normalizedFieldType === "select") {
22714
22922
  fieldConfig.enum = parsedOptions.options || [];
22923
+ } else if (fieldType === "radio") {
22924
+ fieldConfig.type = "radio";
22925
+ if (!parsedOptions.enum && parsedOptions.options) {
22926
+ fieldConfig.enum = parsedOptions.options;
22927
+ }
22715
22928
  } else if (fieldType === "media") {
22716
22929
  fieldConfig.format = "media";
22717
- } else if (fieldType === "slug") {
22930
+ } else if (normalizedFieldType === "slug") {
22718
22931
  fieldConfig.type = "slug";
22719
22932
  fieldConfig.format = "slug";
22720
- } else if (fieldType === "quill") {
22933
+ } else if (normalizedFieldType === "quill") {
22721
22934
  fieldConfig.type = "quill";
22722
- } else if (fieldType === "mdxeditor") {
22723
- fieldConfig.type = "mdxeditor";
22724
- } else if (fieldType === "reference") {
22935
+ } else if (normalizedFieldType === "markdown") {
22936
+ fieldConfig.type = "markdown";
22937
+ } else if (normalizedFieldType === "reference") {
22725
22938
  fieldConfig.type = "reference";
22726
22939
  }
22727
22940
  schema.properties[fieldName] = fieldConfig;
@@ -24409,7 +24622,7 @@ function renderDatabaseToolsSettings(settings) {
24409
24622
 
24410
24623
  // src/routes/admin-settings.ts
24411
24624
  var adminSettingsRoutes = new hono.Hono();
24412
- adminSettingsRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
24625
+ adminSettingsRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
24413
24626
  function getMockSettings(user) {
24414
24627
  return {
24415
24628
  general: {
@@ -24474,7 +24687,7 @@ adminSettingsRoutes.get("/", (c) => {
24474
24687
  adminSettingsRoutes.get("/general", async (c) => {
24475
24688
  const user = c.get("user");
24476
24689
  const db = c.env.DB;
24477
- const settingsService = new chunkVNLR35GO_cjs.SettingsService(db);
24690
+ const settingsService = new chunk64APW3DW_cjs.SettingsService(db);
24478
24691
  const generalSettings = await settingsService.getGeneralSettings(user?.email);
24479
24692
  const mockSettings = getMockSettings(user);
24480
24693
  mockSettings.general = generalSettings;
@@ -24577,7 +24790,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
24577
24790
  adminSettingsRoutes.get("/api/migrations/status", async (c) => {
24578
24791
  try {
24579
24792
  const db = c.env.DB;
24580
- const migrationService = new chunkVGSZWZP3_cjs.MigrationService(db);
24793
+ const migrationService = new chunkLDFMYRG6_cjs.MigrationService(db);
24581
24794
  const status = await migrationService.getMigrationStatus();
24582
24795
  return c.json({
24583
24796
  success: true,
@@ -24601,7 +24814,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
24601
24814
  }, 403);
24602
24815
  }
24603
24816
  const db = c.env.DB;
24604
- const migrationService = new chunkVGSZWZP3_cjs.MigrationService(db);
24817
+ const migrationService = new chunkLDFMYRG6_cjs.MigrationService(db);
24605
24818
  const result = await migrationService.runPendingMigrations();
24606
24819
  return c.json({
24607
24820
  success: result.success,
@@ -24619,7 +24832,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
24619
24832
  adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
24620
24833
  try {
24621
24834
  const db = c.env.DB;
24622
- const migrationService = new chunkVGSZWZP3_cjs.MigrationService(db);
24835
+ const migrationService = new chunkLDFMYRG6_cjs.MigrationService(db);
24623
24836
  const validation = await migrationService.validateSchema();
24624
24837
  return c.json({
24625
24838
  success: true,
@@ -24788,7 +25001,7 @@ adminSettingsRoutes.post("/general", async (c) => {
24788
25001
  }
24789
25002
  const formData = await c.req.formData();
24790
25003
  const db = c.env.DB;
24791
- const settingsService = new chunkVNLR35GO_cjs.SettingsService(db);
25004
+ const settingsService = new chunk64APW3DW_cjs.SettingsService(db);
24792
25005
  const settings = {
24793
25006
  siteName: formData.get("siteName"),
24794
25007
  siteDescription: formData.get("siteDescription"),
@@ -26509,7 +26722,7 @@ function renderFormCreatePage(data) {
26509
26722
 
26510
26723
  // src/routes/admin-forms.ts
26511
26724
  var adminFormsRoutes = new hono.Hono();
26512
- adminFormsRoutes.use("*", chunkZWKCL46S_cjs.requireAuth());
26725
+ adminFormsRoutes.use("*", chunkDQZVU3WB_cjs.requireAuth());
26513
26726
  adminFormsRoutes.get("/", async (c) => {
26514
26727
  try {
26515
26728
  const user = c.get("user");
@@ -27326,6 +27539,33 @@ var public_forms_default = publicFormsRoutes;
27326
27539
 
27327
27540
  // src/templates/pages/admin-api-reference.template.ts
27328
27541
  chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
27542
+ function renderAuthBadge(auth) {
27543
+ if (auth === true) {
27544
+ return `
27545
+ <span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-amber-50 dark:bg-amber-500/10 px-2 py-1 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-700/10 dark:ring-amber-400/20">
27546
+ <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
27547
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
27548
+ </svg>
27549
+ Auth
27550
+ </span>`;
27551
+ }
27552
+ if (auth === false) {
27553
+ return `
27554
+ <span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-lime-50 dark:bg-lime-500/10 px-2 py-1 text-xs font-medium text-lime-700 dark:text-lime-300 ring-1 ring-inset ring-lime-700/10 dark:ring-lime-400/20">
27555
+ <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
27556
+ <path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
27557
+ </svg>
27558
+ Public
27559
+ </span>`;
27560
+ }
27561
+ return `
27562
+ <span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-zinc-50 dark:bg-zinc-500/10 px-2 py-1 text-xs font-medium text-zinc-500 dark:text-zinc-400 ring-1 ring-inset ring-zinc-500/10 dark:ring-zinc-400/20">
27563
+ <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
27564
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
27565
+ </svg>
27566
+ Unknown
27567
+ </span>`;
27568
+ }
27329
27569
  function renderAPIReferencePage(data) {
27330
27570
  const endpointsByCategory = data.endpoints.reduce((acc, endpoint) => {
27331
27571
  if (!acc[endpoint.category]) {
@@ -27334,40 +27574,18 @@ function renderAPIReferencePage(data) {
27334
27574
  acc[endpoint.category].push(endpoint);
27335
27575
  return acc;
27336
27576
  }, {});
27337
- const categoryInfo = {
27338
- "Auth": {
27339
- title: "Authentication",
27340
- description: "User authentication and authorization endpoints",
27341
- icon: "\u{1F510}"
27342
- },
27343
- "Content": {
27344
- title: "Content Management",
27345
- description: "Content creation, retrieval, and management",
27346
- icon: "\u{1F4DD}"
27347
- },
27348
- "Media": {
27349
- title: "Media Management",
27350
- description: "File upload, storage, and media operations",
27351
- icon: "\u{1F5BC}\uFE0F"
27352
- },
27353
- "Admin": {
27354
- title: "Admin Interface",
27355
- description: "Administrative panel and management features",
27356
- icon: "\u2699\uFE0F"
27357
- },
27358
- "System": {
27359
- title: "System",
27360
- description: "Health checks and system information",
27361
- icon: "\u{1F527}"
27362
- }
27363
- };
27577
+ const categories = Object.keys(endpointsByCategory);
27578
+ const totalEndpoints = data.endpoints.length;
27579
+ const publicEndpoints = data.endpoints.filter((e) => e.authentication === false).length;
27580
+ const protectedEndpoints = data.endpoints.filter((e) => e.authentication === true).length;
27581
+ const undocumentedCount = data.endpoints.filter((e) => e.documented === false).length;
27364
27582
  const pageContent = `
27365
27583
  <div class="space-y-6">
27366
27584
  <!-- Header -->
27367
27585
  <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
27368
27586
  <div>
27369
27587
  <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">API Reference</h1>
27370
- <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">Complete documentation of all available API endpoints</p>
27588
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">Auto-discovered documentation of all registered API endpoints</p>
27371
27589
  </div>
27372
27590
  <div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
27373
27591
  <a href="/api" target="_blank" class="inline-flex items-center justify-center gap-x-1.5 rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm">
@@ -27380,29 +27598,35 @@ function renderAPIReferencePage(data) {
27380
27598
  </div>
27381
27599
 
27382
27600
  <!-- Stats -->
27383
- <dl class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
27601
+ <dl class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-5">
27384
27602
  <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
27385
27603
  <dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Total Endpoints</dt>
27386
27604
  <dd class="mt-2 flex items-baseline gap-x-2">
27387
- <span class="text-4xl font-semibold tracking-tight text-zinc-950 dark:text-white">${data.endpoints.length}</span>
27605
+ <span class="text-4xl font-semibold tracking-tight text-zinc-950 dark:text-white">${totalEndpoints}</span>
27388
27606
  </dd>
27389
27607
  </div>
27390
27608
  <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
27391
27609
  <dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Public Endpoints</dt>
27392
27610
  <dd class="mt-2 flex items-baseline gap-x-2">
27393
- <span class="text-4xl font-semibold tracking-tight text-lime-600 dark:text-lime-400">${data.endpoints.filter((e) => !e.authentication).length}</span>
27611
+ <span class="text-4xl font-semibold tracking-tight text-lime-600 dark:text-lime-400">${publicEndpoints}</span>
27394
27612
  </dd>
27395
27613
  </div>
27396
27614
  <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
27397
27615
  <dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Protected Endpoints</dt>
27398
27616
  <dd class="mt-2 flex items-baseline gap-x-2">
27399
- <span class="text-4xl font-semibold tracking-tight text-amber-600 dark:text-amber-400">${data.endpoints.filter((e) => e.authentication).length}</span>
27617
+ <span class="text-4xl font-semibold tracking-tight text-amber-600 dark:text-amber-400">${protectedEndpoints}</span>
27400
27618
  </dd>
27401
27619
  </div>
27402
27620
  <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
27403
27621
  <dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Categories</dt>
27404
27622
  <dd class="mt-2 flex items-baseline gap-x-2">
27405
- <span class="text-4xl font-semibold tracking-tight text-cyan-600 dark:text-cyan-400">${Object.keys(endpointsByCategory).length}</span>
27623
+ <span class="text-4xl font-semibold tracking-tight text-cyan-600 dark:text-cyan-400">${categories.length}</span>
27624
+ </dd>
27625
+ </div>
27626
+ <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 px-6 py-5">
27627
+ <dt class="text-sm/6 font-medium text-zinc-500 dark:text-zinc-400">Undocumented</dt>
27628
+ <dd class="mt-2 flex items-baseline gap-x-2">
27629
+ <span class="text-4xl font-semibold tracking-tight ${undocumentedCount > 0 ? "text-zinc-400 dark:text-zinc-500" : "text-lime-600 dark:text-lime-400"}">${undocumentedCount}</span>
27406
27630
  </dd>
27407
27631
  </div>
27408
27632
  </dl>
@@ -27454,9 +27678,11 @@ function renderAPIReferencePage(data) {
27454
27678
  class="col-start-1 row-start-1 w-full appearance-none rounded-lg bg-white dark:bg-zinc-800 py-2 pl-3 pr-8 text-sm text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-950/10 dark:outline-white/10 *:bg-white dark:*:bg-zinc-800 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-zinc-950 dark:focus:outline-white min-w-[200px]"
27455
27679
  >
27456
27680
  <option value="">All Categories</option>
27457
- ${Object.keys(categoryInfo).map((category) => `
27458
- <option value="${category}">${categoryInfo[category].title}</option>
27459
- `).join("")}
27681
+ ${categories.map((category) => {
27682
+ const info = chunk64APW3DW_cjs.CATEGORY_INFO[category];
27683
+ const title = info ? info.title : category;
27684
+ return `<option value="${category}">${title}</option>`;
27685
+ }).join("\n ")}
27460
27686
  </select>
27461
27687
  <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-500 dark:text-zinc-400 sm:size-4">
27462
27688
  <path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
@@ -27470,7 +27696,7 @@ function renderAPIReferencePage(data) {
27470
27696
  <!-- API Categories -->
27471
27697
  <div class="space-y-6">
27472
27698
  ${Object.entries(endpointsByCategory).map(([category, endpoints]) => {
27473
- const info = categoryInfo[category] || { title: category, description: "", icon: "\u{1F4CB}" };
27699
+ const info = chunk64APW3DW_cjs.CATEGORY_INFO[category] || { title: category, description: "", icon: "&#x1f4cb;" };
27474
27700
  return `
27475
27701
  <div class="api-category" data-category="${category}">
27476
27702
  <div class="rounded-lg bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden">
@@ -27504,23 +27730,14 @@ function renderAPIReferencePage(data) {
27504
27730
  <div class="flex-1 min-w-0">
27505
27731
  <div class="flex items-center gap-x-2 mb-2">
27506
27732
  <code class="text-zinc-950 dark:text-white text-sm font-mono font-medium break-all">${endpoint.path}</code>
27507
- ${endpoint.authentication ? `
27508
- <span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-amber-50 dark:bg-amber-500/10 px-2 py-1 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-700/10 dark:ring-amber-400/20">
27509
- <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
27510
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
27511
- </svg>
27512
- Auth
27513
- </span>
27514
- ` : `
27515
- <span class="shrink-0 inline-flex items-center gap-x-1 rounded-md bg-lime-50 dark:bg-lime-500/10 px-2 py-1 text-xs font-medium text-lime-700 dark:text-lime-300 ring-1 ring-inset ring-lime-700/10 dark:ring-lime-400/20">
27516
- <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
27517
- <path stroke-linecap="round" stroke-linejoin="round" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
27518
- </svg>
27519
- Public
27733
+ ${renderAuthBadge(endpoint.authentication)}
27734
+ ${endpoint.documented === false ? `
27735
+ <span class="shrink-0 inline-flex items-center rounded-md bg-zinc-50 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-400 dark:text-zinc-500 ring-1 ring-inset ring-zinc-200 dark:ring-zinc-700">
27736
+ Auto-discovered
27520
27737
  </span>
27521
- `}
27738
+ ` : ""}
27522
27739
  </div>
27523
- <p class="text-zinc-600 dark:text-zinc-400 text-sm leading-6">${endpoint.description}</p>
27740
+ <p class="text-zinc-600 dark:text-zinc-400 text-sm leading-6">${endpoint.description || '<em class="text-zinc-400 dark:text-zinc-500">No description available</em>'}</p>
27524
27741
  </div>
27525
27742
  </div>
27526
27743
  </div>
@@ -27598,8 +27815,8 @@ function renderAPIReferencePage(data) {
27598
27815
  const path = endpoint.dataset.path.toLowerCase();
27599
27816
  const description = endpoint.dataset.description.toLowerCase();
27600
27817
 
27601
- const matchesSearch = !searchTerm ||
27602
- path.includes(searchTerm) ||
27818
+ const matchesSearch = !searchTerm ||
27819
+ path.includes(searchTerm) ||
27603
27820
  description.includes(searchTerm);
27604
27821
  const matchesMethod = !selectedMethod || method === selectedMethod;
27605
27822
 
@@ -27656,210 +27873,16 @@ function renderAPIReferencePage(data) {
27656
27873
  }
27657
27874
 
27658
27875
  // src/routes/admin-api-reference.ts
27659
- var VERSION2 = chunkJSHIGVIF_cjs.getCoreVersion();
27876
+ var VERSION2 = chunkSHU7Q66Q_cjs.getCoreVersion();
27660
27877
  var router2 = new hono.Hono();
27661
- router2.use("*", chunkZWKCL46S_cjs.requireAuth());
27662
- var apiEndpoints = [
27663
- // Auth endpoints
27664
- {
27665
- method: "POST",
27666
- path: "/auth/login",
27667
- description: "Authenticate user with email and password",
27668
- authentication: false,
27669
- category: "Auth"
27670
- },
27671
- {
27672
- method: "POST",
27673
- path: "/auth/register",
27674
- description: "Register a new user account",
27675
- authentication: false,
27676
- category: "Auth"
27677
- },
27678
- {
27679
- method: "POST",
27680
- path: "/auth/logout",
27681
- description: "Log out the current user and invalidate session",
27682
- authentication: true,
27683
- category: "Auth"
27684
- },
27685
- {
27686
- method: "GET",
27687
- path: "/auth/me",
27688
- description: "Get current authenticated user information",
27689
- authentication: true,
27690
- category: "Auth"
27691
- },
27692
- {
27693
- method: "POST",
27694
- path: "/auth/refresh",
27695
- description: "Refresh authentication token",
27696
- authentication: true,
27697
- category: "Auth"
27698
- },
27699
- // Content endpoints
27700
- {
27701
- method: "GET",
27702
- path: "/api/collections",
27703
- description: "List all available collections",
27704
- authentication: false,
27705
- category: "Content"
27706
- },
27707
- {
27708
- method: "GET",
27709
- path: "/api/collections/:collection/content",
27710
- description: "Get all content items from a specific collection",
27711
- authentication: false,
27712
- category: "Content"
27713
- },
27714
- {
27715
- method: "GET",
27716
- path: "/api/content/:id",
27717
- description: "Get a specific content item by ID",
27718
- authentication: false,
27719
- category: "Content"
27720
- },
27721
- {
27722
- method: "POST",
27723
- path: "/api/content",
27724
- description: "Create a new content item",
27725
- authentication: true,
27726
- category: "Content"
27727
- },
27728
- {
27729
- method: "PUT",
27730
- path: "/api/content/:id",
27731
- description: "Update an existing content item",
27732
- authentication: true,
27733
- category: "Content"
27734
- },
27735
- {
27736
- method: "DELETE",
27737
- path: "/api/content/:id",
27738
- description: "Delete a content item",
27739
- authentication: true,
27740
- category: "Content"
27741
- },
27742
- // Media endpoints
27743
- {
27744
- method: "GET",
27745
- path: "/api/media",
27746
- description: "List all media files with pagination",
27747
- authentication: false,
27748
- category: "Media"
27749
- },
27750
- {
27751
- method: "GET",
27752
- path: "/api/media/:id",
27753
- description: "Get a specific media file by ID",
27754
- authentication: false,
27755
- category: "Media"
27756
- },
27757
- {
27758
- method: "POST",
27759
- path: "/api/media/upload",
27760
- description: "Upload a new media file to R2 storage",
27761
- authentication: true,
27762
- category: "Media"
27763
- },
27764
- {
27765
- method: "DELETE",
27766
- path: "/api/media/:id",
27767
- description: "Delete a media file from storage",
27768
- authentication: true,
27769
- category: "Media"
27770
- },
27771
- // Admin endpoints
27772
- {
27773
- method: "GET",
27774
- path: "/admin/api/stats",
27775
- description: "Get dashboard statistics (collections, content, media, users)",
27776
- authentication: true,
27777
- category: "Admin"
27778
- },
27779
- {
27780
- method: "GET",
27781
- path: "/admin/api/storage",
27782
- description: "Get storage usage information",
27783
- authentication: true,
27784
- category: "Admin"
27785
- },
27786
- {
27787
- method: "GET",
27788
- path: "/admin/api/activity",
27789
- description: "Get recent activity logs",
27790
- authentication: true,
27791
- category: "Admin"
27792
- },
27793
- {
27794
- method: "GET",
27795
- path: "/admin/api/collections",
27796
- description: "List all collections with field counts",
27797
- authentication: true,
27798
- category: "Admin"
27799
- },
27800
- {
27801
- method: "POST",
27802
- path: "/admin/api/collections",
27803
- description: "Create a new collection",
27804
- authentication: true,
27805
- category: "Admin"
27806
- },
27807
- {
27808
- method: "PATCH",
27809
- path: "/admin/api/collections/:id",
27810
- description: "Update an existing collection",
27811
- authentication: true,
27812
- category: "Admin"
27813
- },
27814
- {
27815
- method: "DELETE",
27816
- path: "/admin/api/collections/:id",
27817
- description: "Delete a collection (must be empty)",
27818
- authentication: true,
27819
- category: "Admin"
27820
- },
27821
- {
27822
- method: "GET",
27823
- path: "/admin/api/migrations/status",
27824
- description: "Get database migration status",
27825
- authentication: true,
27826
- category: "Admin"
27827
- },
27828
- {
27829
- method: "POST",
27830
- path: "/admin/api/migrations/run",
27831
- description: "Run pending database migrations",
27832
- authentication: true,
27833
- category: "Admin"
27834
- },
27835
- // System endpoints
27836
- {
27837
- method: "GET",
27838
- path: "/health",
27839
- description: "Health check endpoint for monitoring",
27840
- authentication: false,
27841
- category: "System"
27842
- },
27843
- {
27844
- method: "GET",
27845
- path: "/api/health",
27846
- description: "API health check with schema information",
27847
- authentication: false,
27848
- category: "System"
27849
- },
27850
- {
27851
- method: "GET",
27852
- path: "/api",
27853
- description: "API root - returns API information and OpenAPI spec",
27854
- authentication: false,
27855
- category: "System"
27856
- }
27857
- ];
27878
+ router2.use("*", chunkDQZVU3WB_cjs.requireAuth());
27858
27879
  router2.get("/", async (c) => {
27859
27880
  const user = c.get("user");
27860
27881
  try {
27882
+ const app2 = chunk64APW3DW_cjs.getAppInstance();
27883
+ const endpoints = chunk64APW3DW_cjs.buildRouteList(app2);
27861
27884
  const pageData = {
27862
- endpoints: apiEndpoints,
27885
+ endpoints,
27863
27886
  user: user ? {
27864
27887
  name: user.email.split("@")[0] || user.email,
27865
27888
  email: user.email,
@@ -27939,5 +27962,5 @@ exports.router = router;
27939
27962
  exports.router2 = router2;
27940
27963
  exports.test_cleanup_default = test_cleanup_default;
27941
27964
  exports.userRoutes = userRoutes;
27942
- //# sourceMappingURL=chunk-3U5YHS4G.cjs.map
27943
- //# sourceMappingURL=chunk-3U5YHS4G.cjs.map
27965
+ //# sourceMappingURL=chunk-KSB6FXOP.cjs.map
27966
+ //# sourceMappingURL=chunk-KSB6FXOP.cjs.map