@sonicjs-cms/core 3.0.0-beta.23 → 3.0.0-beta.25

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 (139) hide show
  1. package/bin/db-reset.js +39 -22
  2. package/dist/admin-documents-form.template-JWV5GITD.js +6 -0
  3. package/dist/{admin-documents-form.template-55V2ZGWT.js.map → admin-documents-form.template-JWV5GITD.js.map} +1 -1
  4. package/dist/admin-documents-form.template-UWKGVAMW.cjs +19 -0
  5. package/dist/{admin-layout-catalyst.template-6UQGLCKI.js.map → admin-documents-form.template-UWKGVAMW.cjs.map} +1 -1
  6. package/dist/admin-layout-catalyst.template-MOQ3BBXA.cjs +21 -0
  7. package/dist/{admin-layout-catalyst.template-3XZXDGF7.cjs.map → admin-layout-catalyst.template-MOQ3BBXA.cjs.map} +1 -1
  8. package/dist/admin-layout-catalyst.template-WCYMQVTZ.js +7 -0
  9. package/dist/{admin-documents-form.template-YNAVMJCV.cjs.map → admin-layout-catalyst.template-WCYMQVTZ.js.map} +1 -1
  10. package/dist/{chunk-KS24NPPL.js → chunk-2D3A4X52.js} +21 -2
  11. package/dist/chunk-2D3A4X52.js.map +1 -0
  12. package/dist/{chunk-DIBCKCUE.js → chunk-2OXFMMA3.js} +14 -29
  13. package/dist/chunk-2OXFMMA3.js.map +1 -0
  14. package/dist/{chunk-SOZUG52O.js → chunk-3KGY4IBW.js} +3 -3
  15. package/dist/{chunk-SOZUG52O.js.map → chunk-3KGY4IBW.js.map} +1 -1
  16. package/dist/{chunk-CTYLCWMO.cjs → chunk-6OA77UM7.cjs} +802 -481
  17. package/dist/chunk-6OA77UM7.cjs.map +1 -0
  18. package/dist/{chunk-URIBTX27.js → chunk-73RTFFMX.js} +3 -3
  19. package/dist/{chunk-URIBTX27.js.map → chunk-73RTFFMX.js.map} +1 -1
  20. package/dist/{chunk-SK5CHFJP.js → chunk-7P5OCUPS.js} +467 -149
  21. package/dist/chunk-7P5OCUPS.js.map +1 -0
  22. package/dist/{chunk-3TMTYJAJ.js → chunk-BDQ55MWV.js} +18 -2
  23. package/dist/chunk-BDQ55MWV.js.map +1 -0
  24. package/dist/{chunk-C3ZQP5NW.cjs → chunk-BJ6NJL2E.cjs} +7 -12
  25. package/dist/chunk-BJ6NJL2E.cjs.map +1 -0
  26. package/dist/{chunk-2MIYYAO4.js → chunk-C54MH5UI.js} +5 -4
  27. package/dist/chunk-C54MH5UI.js.map +1 -0
  28. package/dist/{chunk-BBEKHLTW.cjs → chunk-CN3T3TDI.cjs} +9 -9
  29. package/dist/{chunk-BBEKHLTW.cjs.map → chunk-CN3T3TDI.cjs.map} +1 -1
  30. package/dist/{chunk-DPLJYTHE.js → chunk-CWIGQYRR.js} +7 -12
  31. package/dist/chunk-CWIGQYRR.js.map +1 -0
  32. package/dist/{chunk-4WMZGHIC.cjs → chunk-D6FWEYQE.cjs} +6 -5
  33. package/dist/chunk-D6FWEYQE.cjs.map +1 -0
  34. package/dist/{chunk-A46IRC5A.js → chunk-D7RLBBHE.js} +2 -2
  35. package/dist/{chunk-A46IRC5A.js.map → chunk-D7RLBBHE.js.map} +1 -1
  36. package/dist/{chunk-IXUHXTHW.cjs → chunk-DLTT7QZU.cjs} +17 -6
  37. package/dist/chunk-DLTT7QZU.cjs.map +1 -0
  38. package/dist/{chunk-BHND5PEV.js → chunk-E3TQG7AA.js} +3 -3
  39. package/dist/{chunk-BHND5PEV.js.map → chunk-E3TQG7AA.js.map} +1 -1
  40. package/dist/{chunk-IS54FPB7.cjs → chunk-E7E3NL3Y.cjs} +27 -42
  41. package/dist/chunk-E7E3NL3Y.cjs.map +1 -0
  42. package/dist/{chunk-XXRR54AB.js → chunk-F26HP3B6.js} +36 -5
  43. package/dist/chunk-F26HP3B6.js.map +1 -0
  44. package/dist/{chunk-QZGABF2M.js → chunk-FVAK4C6L.js} +17 -6
  45. package/dist/chunk-FVAK4C6L.js.map +1 -0
  46. package/dist/{chunk-BJROXF4N.js → chunk-HSDJDWLV.js} +19 -4
  47. package/dist/chunk-HSDJDWLV.js.map +1 -0
  48. package/dist/{chunk-RFBW5RRH.cjs → chunk-I7MIRRFJ.cjs} +43 -12
  49. package/dist/chunk-I7MIRRFJ.cjs.map +1 -0
  50. package/dist/{chunk-LTZ5WYOK.cjs → chunk-JCBDXEY2.cjs} +18 -2
  51. package/dist/chunk-JCBDXEY2.cjs.map +1 -0
  52. package/dist/{chunk-UYJQDDEE.cjs → chunk-MI2YXCK4.cjs} +14 -8
  53. package/dist/chunk-MI2YXCK4.cjs.map +1 -0
  54. package/dist/{chunk-VGHEKIJL.cjs → chunk-MOGK3U4A.cjs} +2 -2
  55. package/dist/{chunk-VGHEKIJL.cjs.map → chunk-MOGK3U4A.cjs.map} +1 -1
  56. package/dist/{chunk-E66UVONV.cjs → chunk-OUBUS74I.cjs} +24 -9
  57. package/dist/chunk-OUBUS74I.cjs.map +1 -0
  58. package/dist/{chunk-GMMCCIQO.cjs → chunk-RSBZITK3.cjs} +3 -3
  59. package/dist/{chunk-GMMCCIQO.cjs.map → chunk-RSBZITK3.cjs.map} +1 -1
  60. package/dist/{chunk-THFZSDIU.js → chunk-SXC4HPK6.js} +14 -8
  61. package/dist/chunk-SXC4HPK6.js.map +1 -0
  62. package/dist/{chunk-JMJM7DMM.cjs → chunk-TT3TQSCZ.cjs} +5 -5
  63. package/dist/{chunk-JMJM7DMM.cjs.map → chunk-TT3TQSCZ.cjs.map} +1 -1
  64. package/dist/{chunk-AT4ZQGEZ.cjs → chunk-YAURVGUK.cjs} +21 -2
  65. package/dist/chunk-YAURVGUK.cjs.map +1 -0
  66. package/dist/config-4TIHSNW6.js +6 -0
  67. package/dist/{config-6FB2BXMP.js.map → config-4TIHSNW6.js.map} +1 -1
  68. package/dist/{config-OCH7S4WK.cjs → config-XN2ETAJ3.cjs} +5 -5
  69. package/dist/{config-OCH7S4WK.cjs.map → config-XN2ETAJ3.cjs.map} +1 -1
  70. package/dist/{define-plugin-D_6D9vVZ.d.ts → define-plugin-DDe8a_bh.d.ts} +3 -0
  71. package/dist/{define-plugin-DMcvEy_C.d.cts → define-plugin-rjXmGOgu.d.cts} +3 -0
  72. package/dist/index.cjs +1220 -353
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +38 -3
  75. package/dist/index.d.ts +38 -3
  76. package/dist/index.js +895 -30
  77. package/dist/index.js.map +1 -1
  78. package/dist/middleware.cjs +35 -35
  79. package/dist/middleware.d.cts +4 -1
  80. package/dist/middleware.d.ts +4 -1
  81. package/dist/middleware.js +6 -6
  82. package/dist/migrations-JAHMI5MK.js +4 -0
  83. package/dist/{migrations-XQ6EX6HX.js.map → migrations-JAHMI5MK.js.map} +1 -1
  84. package/dist/migrations-KU4V6T73.cjs +13 -0
  85. package/dist/{migrations-ZD7Y5NUC.cjs.map → migrations-KU4V6T73.cjs.map} +1 -1
  86. package/dist/plugins.cjs +40 -40
  87. package/dist/plugins.d.cts +1 -1
  88. package/dist/plugins.d.ts +1 -1
  89. package/dist/plugins.js +3 -3
  90. package/dist/rbac-AORZ6O3N.cjs +14 -0
  91. package/dist/{rbac-7QZ4JZ2E.cjs.map → rbac-AORZ6O3N.cjs.map} +1 -1
  92. package/dist/rbac-P44772FP.js +5 -0
  93. package/dist/{rbac-Y43IEQOP.js.map → rbac-P44772FP.js.map} +1 -1
  94. package/dist/routes.cjs +31 -31
  95. package/dist/routes.js +11 -11
  96. package/dist/services.cjs +18 -18
  97. package/dist/services.d.cts +1 -1
  98. package/dist/services.d.ts +1 -1
  99. package/dist/services.js +2 -2
  100. package/dist/{telemetry-Cku1ax74.d.cts → telemetry-QFlTv9f8.d.cts} +1 -1
  101. package/dist/{telemetry-Cku1ax74.d.ts → telemetry-QFlTv9f8.d.ts} +1 -1
  102. package/dist/templates.cjs +17 -17
  103. package/dist/templates.js +3 -3
  104. package/dist/types.d.cts +1 -1
  105. package/dist/types.d.ts +1 -1
  106. package/dist/utils.cjs +12 -12
  107. package/dist/utils.d.cts +1 -1
  108. package/dist/utils.d.ts +1 -1
  109. package/dist/utils.js +2 -2
  110. package/package.json +5 -10
  111. package/dist/admin-documents-form.template-55V2ZGWT.js +0 -6
  112. package/dist/admin-documents-form.template-YNAVMJCV.cjs +0 -19
  113. package/dist/admin-layout-catalyst.template-3XZXDGF7.cjs +0 -21
  114. package/dist/admin-layout-catalyst.template-6UQGLCKI.js +0 -7
  115. package/dist/chunk-2MIYYAO4.js.map +0 -1
  116. package/dist/chunk-3TMTYJAJ.js.map +0 -1
  117. package/dist/chunk-4WMZGHIC.cjs.map +0 -1
  118. package/dist/chunk-AT4ZQGEZ.cjs.map +0 -1
  119. package/dist/chunk-BJROXF4N.js.map +0 -1
  120. package/dist/chunk-C3ZQP5NW.cjs.map +0 -1
  121. package/dist/chunk-CTYLCWMO.cjs.map +0 -1
  122. package/dist/chunk-DIBCKCUE.js.map +0 -1
  123. package/dist/chunk-DPLJYTHE.js.map +0 -1
  124. package/dist/chunk-E66UVONV.cjs.map +0 -1
  125. package/dist/chunk-IS54FPB7.cjs.map +0 -1
  126. package/dist/chunk-IXUHXTHW.cjs.map +0 -1
  127. package/dist/chunk-KS24NPPL.js.map +0 -1
  128. package/dist/chunk-LTZ5WYOK.cjs.map +0 -1
  129. package/dist/chunk-QZGABF2M.js.map +0 -1
  130. package/dist/chunk-RFBW5RRH.cjs.map +0 -1
  131. package/dist/chunk-SK5CHFJP.js.map +0 -1
  132. package/dist/chunk-THFZSDIU.js.map +0 -1
  133. package/dist/chunk-UYJQDDEE.cjs.map +0 -1
  134. package/dist/chunk-XXRR54AB.js.map +0 -1
  135. package/dist/config-6FB2BXMP.js +0 -6
  136. package/dist/migrations-XQ6EX6HX.js +0 -4
  137. package/dist/migrations-ZD7Y5NUC.cjs +0 -13
  138. package/dist/rbac-7QZ4JZ2E.cjs +0 -14
  139. package/dist/rbac-Y43IEQOP.js +0 -5
@@ -1,20 +1,20 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkXWVDSM2L_cjs = require('./chunk-XWVDSM2L.cjs');
4
- var chunkJMJM7DMM_cjs = require('./chunk-JMJM7DMM.cjs');
4
+ var chunkTT3TQSCZ_cjs = require('./chunk-TT3TQSCZ.cjs');
5
5
  var chunk36Z3AKX3_cjs = require('./chunk-36Z3AKX3.cjs');
6
- var chunkIS54FPB7_cjs = require('./chunk-IS54FPB7.cjs');
7
- var chunkLTZ5WYOK_cjs = require('./chunk-LTZ5WYOK.cjs');
6
+ var chunkE7E3NL3Y_cjs = require('./chunk-E7E3NL3Y.cjs');
7
+ var chunkJCBDXEY2_cjs = require('./chunk-JCBDXEY2.cjs');
8
8
  var chunkKIGVVO6N_cjs = require('./chunk-KIGVVO6N.cjs');
9
- var chunkAT4ZQGEZ_cjs = require('./chunk-AT4ZQGEZ.cjs');
10
- var chunkVGHEKIJL_cjs = require('./chunk-VGHEKIJL.cjs');
11
- var chunkE66UVONV_cjs = require('./chunk-E66UVONV.cjs');
12
- var chunkUYJQDDEE_cjs = require('./chunk-UYJQDDEE.cjs');
13
- var chunk4WMZGHIC_cjs = require('./chunk-4WMZGHIC.cjs');
9
+ var chunkYAURVGUK_cjs = require('./chunk-YAURVGUK.cjs');
10
+ var chunkMOGK3U4A_cjs = require('./chunk-MOGK3U4A.cjs');
11
+ var chunkOUBUS74I_cjs = require('./chunk-OUBUS74I.cjs');
12
+ var chunkMI2YXCK4_cjs = require('./chunk-MI2YXCK4.cjs');
13
+ var chunkD6FWEYQE_cjs = require('./chunk-D6FWEYQE.cjs');
14
14
  var chunkJ6JTWD2A_cjs = require('./chunk-J6JTWD2A.cjs');
15
- var chunkIXUHXTHW_cjs = require('./chunk-IXUHXTHW.cjs');
15
+ var chunkDLTT7QZU_cjs = require('./chunk-DLTT7QZU.cjs');
16
16
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
17
- var chunkC3ZQP5NW_cjs = require('./chunk-C3ZQP5NW.cjs');
17
+ var chunkBJ6NJL2E_cjs = require('./chunk-BJ6NJL2E.cjs');
18
18
  var chunkMNWKYY5E_cjs = require('./chunk-MNWKYY5E.cjs');
19
19
  var hono = require('hono');
20
20
  var zod = require('zod');
@@ -47,6 +47,7 @@ var DocumentPermissionsService = class {
47
47
  const publicGrants = baseGrants["public"] ?? [];
48
48
  if (publicGrants.includes(permission)) return true;
49
49
  } else if (principal.type === "role") {
50
+ if (principal.id === "public") continue;
50
51
  const roleGrants = baseGrants[principal.id] ?? [];
51
52
  if (roleGrants.includes(permission)) return true;
52
53
  }
@@ -151,6 +152,18 @@ var DocumentRepository = class {
151
152
  const row = await this.db.prepare("SELECT * FROM documents WHERE root_id = ? AND tenant_id = ? AND is_published = 1").bind(rootId, this.tenantId).first();
152
153
  return row ? rowToDocument(row) : null;
153
154
  }
155
+ // Slug lookup for a single doc. Prefer the published row; fall back to the current
156
+ // draft so a never-published doc is still reachable by slug (matches getById, which
157
+ // resolves drafts too). ORDER BY makes the preference deterministic when both exist.
158
+ async getBySlug(typeId, slug) {
159
+ const row = await this.db.prepare(
160
+ `SELECT * FROM documents
161
+ WHERE tenant_id = ? AND type_id = ? AND slug = ? AND deleted_at IS NULL
162
+ AND (is_published = 1 OR is_current_draft = 1)
163
+ ORDER BY is_published DESC LIMIT 1`
164
+ ).bind(this.tenantId, typeId, slug).first();
165
+ return row ? rowToDocument(row) : null;
166
+ }
154
167
  // Unified, tenant-scoped list with optional generated-column / facet filters and sort. This is the
155
168
  // single place document list SQL is built — route handlers must call this, never inline SQL (R4).
156
169
  async list(opts = {}) {
@@ -259,7 +272,7 @@ function getDocumentRequestContext(c) {
259
272
 
260
273
  // src/routes/api-documents.ts
261
274
  async function aclAllowsRead(db, tenantId, principalSet, row, preloadedType) {
262
- const docType = preloadedType ?? await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(row.type_id);
275
+ const docType = preloadedType ?? await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(row.type_id);
263
276
  if (!docType) return false;
264
277
  return new DocumentRepository(db, tenantId).isAllowed(principalSet, row.root_id, "read", docType.settings);
265
278
  }
@@ -277,7 +290,7 @@ apiDocumentsRoutes.get("/", async (c) => {
277
290
  if (!typeId) {
278
291
  return c.json({ error: "type query parameter is required" }, 400);
279
292
  }
280
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
293
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
281
294
  const docType = await registry.findById(typeId);
282
295
  if (!docType || !docType.isActive) {
283
296
  return c.json({ error: "Unknown document type" }, 400);
@@ -359,7 +372,7 @@ apiDocumentsRoutes.get("/root/:rootId", async (c) => {
359
372
  AND (expires_at IS NULL OR expires_at > ?) LIMIT 1`
360
373
  ).bind(rootId, now, now).first();
361
374
  if (!row) return c.json({ error: "Not found" }, 404);
362
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(row.type_id);
375
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(row.type_id);
363
376
  const effTenant = effectiveTenantForType(tenantId, docType?.settings);
364
377
  if (row.tenant_id !== effTenant) return c.json({ error: "Not found" }, 404);
365
378
  if (!await aclAllowsRead(db, effTenant, principalSet, row, docType)) {
@@ -395,7 +408,7 @@ apiDocumentsRoutes.get("/:id", async (c) => {
395
408
  AND (expires_at IS NULL OR expires_at > ?) LIMIT 1`
396
409
  ).bind(id, now, now).first();
397
410
  if (!row) return c.json({ error: "Not found" }, 404);
398
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(row.type_id);
411
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(row.type_id);
399
412
  const effTenant = effectiveTenantForType(tenantId, docType?.settings);
400
413
  if (row.tenant_id !== effTenant) return c.json({ error: "Not found" }, 404);
401
414
  if (!await aclAllowsRead(db, effTenant, principalSet, row, docType)) {
@@ -465,17 +478,17 @@ async function resolveDocScope(c, db, by, opts = {}) {
465
478
  const draft = opts.currentDraftOnly ? " AND is_current_draft = 1" : "";
466
479
  const row = await db.prepare(`SELECT type_id, root_id, tenant_id FROM documents WHERE ${col} = ?${draft} LIMIT 1`).bind(val).first();
467
480
  if (!row) return null;
468
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(row.type_id);
481
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(row.type_id);
469
482
  const tenantId = effectiveTenantForType(getRequestTenant(c), docType?.settings);
470
483
  if (row.tenant_id !== tenantId) return null;
471
484
  return { typeId: row.type_id, rootId: row.root_id, tenantId, docType };
472
485
  }
473
486
  var adminDocumentsRoutes = new hono.Hono();
474
- adminDocumentsRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
475
- adminDocumentsRoutes.use("*", chunkIS54FPB7_cjs.requireRole(["admin", "editor"]));
487
+ adminDocumentsRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
488
+ adminDocumentsRoutes.use("*", chunkE7E3NL3Y_cjs.requireRole(["admin", "editor"]));
476
489
  adminDocumentsRoutes.get("/types", async (c) => {
477
490
  try {
478
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(c.env.DB);
491
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(c.env.DB);
479
492
  const types = await registry.findAll();
480
493
  return c.json({ data: types });
481
494
  } catch (error) {
@@ -503,7 +516,7 @@ adminDocumentsRoutes.get("/", async (c) => {
503
516
  if (!typeId) {
504
517
  return c.json({ error: "type query parameter is required" }, 400);
505
518
  }
506
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
519
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
507
520
  const docType = await registry.findById(typeId);
508
521
  if (!docType || !docType.isActive) {
509
522
  return c.json({ error: "Unknown document type" }, 400);
@@ -614,7 +627,7 @@ adminDocumentsRoutes.post("/", async (c) => {
614
627
  const input = validation.data;
615
628
  const db = c.env.DB;
616
629
  const user = c.get("user");
617
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
630
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
618
631
  const docType = await registry.findById(input.typeId);
619
632
  if (!docType || !docType.isActive) {
620
633
  return c.json({ error: "Unknown document type" }, 400);
@@ -622,7 +635,7 @@ adminDocumentsRoutes.post("/", async (c) => {
622
635
  const createDenied = await denyIfNotAllowed(c, db, "", "create", docType.settings);
623
636
  if (createDenied) return createDenied;
624
637
  const tenantId = effectiveTenantForType(getRequestTenant(c), docType.settings);
625
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
638
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
626
639
  queryableFields: docType.queryableFields,
627
640
  typeSchemaVersion: docType.schemaVersion,
628
641
  maxVersionsPerRoot: docType.settings.maxVersionsPerRoot,
@@ -660,7 +673,7 @@ adminDocumentsRoutes.put("/:rootId", async (c) => {
660
673
  const docType = scope.docType;
661
674
  const updateDenied = await denyIfNotAllowed(c, db, rootId, "update", docType?.settings, scope.tenantId);
662
675
  if (updateDenied) return updateDenied;
663
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
676
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
664
677
  queryableFields: docType?.queryableFields ?? [],
665
678
  typeSchemaVersion: docType?.schemaVersion,
666
679
  maxVersionsPerRoot: docType?.settings.maxVersionsPerRoot,
@@ -685,7 +698,7 @@ adminDocumentsRoutes.post("/:id/publish", async (c) => {
685
698
  const docType = scope.docType;
686
699
  const publishDenied = await denyIfNotAllowed(c, db, scope.rootId, "publish", docType?.settings, scope.tenantId);
687
700
  if (publishDenied) return publishDenied;
688
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
701
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
689
702
  queryableFields: docType?.queryableFields ?? [],
690
703
  typeSchemaVersion: docType?.schemaVersion,
691
704
  tenantId: scope.tenantId,
@@ -708,7 +721,7 @@ adminDocumentsRoutes.post("/:id/unpublish", async (c) => {
708
721
  const docType = scope.docType;
709
722
  const unpublishDenied = await denyIfNotAllowed(c, db, scope.rootId, "publish", docType?.settings, scope.tenantId);
710
723
  if (unpublishDenied) return unpublishDenied;
711
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
724
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
712
725
  queryableFields: docType?.queryableFields ?? [],
713
726
  tenantId: scope.tenantId
714
727
  });
@@ -730,7 +743,7 @@ adminDocumentsRoutes.delete("/:id", async (c) => {
730
743
  const docType = scope.docType;
731
744
  const deleteDenied = await denyIfNotAllowed(c, db, scope.rootId, "delete", docType?.settings, scope.tenantId);
732
745
  if (deleteDenied) return deleteDenied;
733
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, { queryableFields: docType?.queryableFields ?? [], tenantId: scope.tenantId });
746
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, { queryableFields: docType?.queryableFields ?? [], tenantId: scope.tenantId });
734
747
  if (docType?.settings.pii) {
735
748
  if (scope.rootId) await svc.erase(scope.rootId, scope.tenantId);
736
749
  } else {
@@ -746,7 +759,7 @@ adminDocumentsRoutes.post("/types/:typeId/reindex", async (c) => {
746
759
  try {
747
760
  const { typeId } = c.req.param();
748
761
  const db = c.env.DB;
749
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
762
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
750
763
  const docType = await registry.findById(typeId);
751
764
  if (!docType) return c.json({ error: "Unknown document type" }, 400);
752
765
  const reindexTenant = effectiveTenantForType(getRequestTenant(c), docType.settings);
@@ -900,13 +913,13 @@ async function resolveContentVariables(contentData, db) {
900
913
  // src/routes/api-content-crud.ts
901
914
  var apiContentCrudRoutes = new hono.Hono();
902
915
  async function resolveDocBacking(db, collectionIdOrName) {
903
- const registry = chunkAT4ZQGEZ_cjs.getCollectionRegistry();
916
+ const registry = chunkYAURVGUK_cjs.getCollectionRegistry();
904
917
  const record = registry.getBySlugOrName(collectionIdOrName) ?? registry.getById(collectionIdOrName);
905
918
  if (record) {
906
- const docType2 = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(record.name);
919
+ const docType2 = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(record.name);
907
920
  return docType2 ? { coll: { id: record.id, name: record.name }, docType: docType2 } : null;
908
921
  }
909
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(collectionIdOrName);
922
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(collectionIdOrName);
910
923
  if (docType) {
911
924
  return { coll: { id: collectionIdOrName, name: collectionIdOrName }, docType };
912
925
  }
@@ -949,7 +962,7 @@ apiContentCrudRoutes.get("/check-slug", async (c) => {
949
962
  }, 500);
950
963
  }
951
964
  });
952
- apiContentCrudRoutes.get("/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
965
+ apiContentCrudRoutes.get("/:id", chunkE7E3NL3Y_cjs.optionalAuth(), async (c) => {
953
966
  try {
954
967
  const id = c.req.param("id");
955
968
  const db = c.env.DB;
@@ -959,7 +972,7 @@ apiContentCrudRoutes.get("/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
959
972
  ).bind(id, getRequestTenant(c)).first();
960
973
  let transformedContent;
961
974
  if (docRow) {
962
- const coll = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getByName(docRow.type_id);
975
+ const coll = chunkYAURVGUK_cjs.getCollectionRegistry().getByName(docRow.type_id);
963
976
  transformedContent = {
964
977
  id: docRow.root_id,
965
978
  title: docRow.title,
@@ -968,8 +981,8 @@ apiContentCrudRoutes.get("/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
968
981
  collectionId: coll?.id ?? docRow.type_id,
969
982
  data: docRow.data ? JSON.parse(docRow.data) : {},
970
983
  // D29: document timestamps are SECONDS; legacy `content` API contract is MILLISECONDS.
971
- created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(docRow.created_at),
972
- updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(docRow.updated_at)
984
+ created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(docRow.created_at),
985
+ updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(docRow.updated_at)
973
986
  };
974
987
  } else {
975
988
  return c.json({ error: "Content not found" }, 404);
@@ -984,6 +997,32 @@ apiContentCrudRoutes.get("/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
984
997
  { collection: docRow.type_id, id: docRow.root_id, data: transformedContent.data },
985
998
  "fire-and-forget"
986
999
  );
1000
+ const fieldsParam = c.req.query("fields");
1001
+ if (fieldsParam) {
1002
+ const projFields = fieldsParam.split(",").map((s) => s.trim()).filter(Boolean);
1003
+ if (projFields.length > 0) {
1004
+ const result = {};
1005
+ const dataSubFields = [];
1006
+ for (const f of projFields) {
1007
+ if (f === "data") {
1008
+ result.data = transformedContent.data;
1009
+ } else if (f.startsWith("data.")) {
1010
+ dataSubFields.push(f.slice(5));
1011
+ } else if (Object.prototype.hasOwnProperty.call(transformedContent, f)) {
1012
+ result[f] = transformedContent[f];
1013
+ }
1014
+ }
1015
+ if (dataSubFields.length > 0 && !result.data) {
1016
+ result.data = {};
1017
+ for (const sub of dataSubFields) {
1018
+ if (transformedContent.data && Object.prototype.hasOwnProperty.call(transformedContent.data, sub)) {
1019
+ result.data[sub] = transformedContent.data[sub];
1020
+ }
1021
+ }
1022
+ }
1023
+ return c.json({ data: result });
1024
+ }
1025
+ }
987
1026
  return c.json({ data: transformedContent });
988
1027
  } catch (error) {
989
1028
  console.error("Error fetching content:", error);
@@ -993,7 +1032,7 @@ apiContentCrudRoutes.get("/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
993
1032
  }, 500);
994
1033
  }
995
1034
  });
996
- apiContentCrudRoutes.post("/", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
1035
+ apiContentCrudRoutes.post("/", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
997
1036
  try {
998
1037
  const db = c.env.DB;
999
1038
  const user = c.get("user");
@@ -1027,7 +1066,7 @@ apiContentCrudRoutes.post("/", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cj
1027
1066
  return c.json({ error: "Write cancelled by plugin", details: String(err) }, 400);
1028
1067
  }
1029
1068
  const tenantId = getRequestTenant(c);
1030
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
1069
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
1031
1070
  queryableFields: backing.docType.queryableFields ?? [],
1032
1071
  typeSchemaVersion: backing.docType.schemaVersion ?? 1,
1033
1072
  maxVersionsPerRoot: backing.docType.settings?.maxVersionsPerRoot ?? 50,
@@ -1047,8 +1086,8 @@ apiContentCrudRoutes.post("/", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cj
1047
1086
  user?.userId
1048
1087
  );
1049
1088
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1050
- await cache.invalidate("content-filtered:*");
1051
- await cache.invalidate("collection-content-filtered:*");
1089
+ await cache.invalidate("api:content-filtered:*");
1090
+ await cache.invalidate(`api:collection-content-filtered:${backing.coll.name}:*`);
1052
1091
  dispatchHookEvent(
1053
1092
  c,
1054
1093
  "content:after:create",
@@ -1056,7 +1095,7 @@ apiContentCrudRoutes.post("/", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cj
1056
1095
  "fire-and-forget"
1057
1096
  );
1058
1097
  return c.json({
1059
- data: { id: doc.rootId, title: doc.title, slug: doc.slug, status: doc.status, collectionId: backing.coll.id, data: doc.data, created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(doc.createdAt), updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(doc.updatedAt) }
1098
+ data: { id: doc.rootId, title: doc.title, slug: doc.slug, status: doc.status, collectionId: backing.coll.id, data: doc.data, created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(doc.createdAt), updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(doc.updatedAt) }
1060
1099
  }, 201);
1061
1100
  }
1062
1101
  return c.json({ error: "Collection is not document-backed" }, 400);
@@ -1068,7 +1107,7 @@ apiContentCrudRoutes.post("/", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cj
1068
1107
  }, 500);
1069
1108
  }
1070
1109
  });
1071
- apiContentCrudRoutes.put("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
1110
+ apiContentCrudRoutes.put("/:id", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
1072
1111
  try {
1073
1112
  const id = c.req.param("id");
1074
1113
  const db = c.env.DB;
@@ -1090,8 +1129,8 @@ apiContentCrudRoutes.put("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_
1090
1129
  } catch (err) {
1091
1130
  return c.json({ error: "Write cancelled by plugin", details: String(err) }, 400);
1092
1131
  }
1093
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(docRow.type_id);
1094
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
1132
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(docRow.type_id);
1133
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
1095
1134
  queryableFields: docType?.queryableFields ?? [],
1096
1135
  typeSchemaVersion: docType?.schemaVersion ?? 1,
1097
1136
  maxVersionsPerRoot: docType?.settings?.maxVersionsPerRoot ?? 50,
@@ -1111,9 +1150,9 @@ apiContentCrudRoutes.put("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_
1111
1150
  await svc.unpublish(pub.id);
1112
1151
  }
1113
1152
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1114
- await cache.invalidate("content-filtered:*");
1115
- await cache.invalidate("collection-content-filtered:*");
1116
- const coll = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getByName(docRow.type_id);
1153
+ await cache.invalidate("api:content-filtered:*");
1154
+ await cache.invalidate(`api:collection-content-filtered:${docRow.type_id}:*`);
1155
+ const coll = chunkYAURVGUK_cjs.getCollectionRegistry().getByName(docRow.type_id);
1117
1156
  const saved = await db.prepare("SELECT * FROM documents WHERE id = ?").bind(newDraft.id).first();
1118
1157
  const savedData = saved?.data ? JSON.parse(saved.data) : {};
1119
1158
  dispatchHookEvent(c, "content:after:update", { collection: docRow.type_id, id, data: savedData, user: actor }, "fire-and-forget");
@@ -1122,7 +1161,7 @@ apiContentCrudRoutes.put("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_
1122
1161
  dispatchHookEvent(c, "content:after:publish", { collection: docRow.type_id, id, data: savedData, user: actor }, "fire-and-forget");
1123
1162
  }
1124
1163
  return c.json({
1125
- data: { id: saved.root_id, title: saved.title, slug: saved.slug, status: saved.status, collectionId: coll?.id ?? docRow.type_id, data: savedData, created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(saved.created_at), updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(saved.updated_at) }
1164
+ data: { id: saved.root_id, title: saved.title, slug: saved.slug, status: saved.status, collectionId: coll?.id ?? docRow.type_id, data: savedData, created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(saved.created_at), updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(saved.updated_at) }
1126
1165
  });
1127
1166
  }
1128
1167
  return c.json({ error: "Content not found" }, 404);
@@ -1134,7 +1173,7 @@ apiContentCrudRoutes.put("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_
1134
1173
  }, 500);
1135
1174
  }
1136
1175
  });
1137
- apiContentCrudRoutes.delete("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
1176
+ apiContentCrudRoutes.delete("/:id", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
1138
1177
  try {
1139
1178
  const id = c.req.param("id");
1140
1179
  const db = c.env.DB;
@@ -1151,8 +1190,8 @@ apiContentCrudRoutes.delete("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FP
1151
1190
  const now = Math.floor(Date.now() / 1e3);
1152
1191
  await db.prepare("UPDATE documents SET deleted_at = ?, updated_at = ? WHERE root_id = ? AND tenant_id = ?").bind(now, now, id, tenantId).run();
1153
1192
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1154
- await cache.invalidate("content-filtered:*");
1155
- await cache.invalidate("collection-content-filtered:*");
1193
+ await cache.invalidate("api:content-filtered:*");
1194
+ await cache.invalidate(`api:collection-content-filtered:${docRow.type_id}:*`);
1156
1195
  dispatchHookEvent(c, "content:after:delete", { collection: docRow.type_id, id, data: {}, user: actor }, "fire-and-forget");
1157
1196
  return c.json({ success: true });
1158
1197
  }
@@ -1168,6 +1207,22 @@ apiContentCrudRoutes.delete("/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FP
1168
1207
  var api_content_crud_default = apiContentCrudRoutes;
1169
1208
 
1170
1209
  // src/routes/api.ts
1210
+ function isAnonPrincipal(principalSet) {
1211
+ return principalSet.length === 1 && principalSet[0]?.type === "public";
1212
+ }
1213
+ async function typeReadAllowed(db, principalSet, docType, typeName) {
1214
+ const perms = new DocumentPermissionsService(db);
1215
+ if (perms.isAllowedSync(principalSet, [], "read", docType?.settings ?? {})) return true;
1216
+ const rbac = new chunkJCBDXEY2_cjs.RbacService(db);
1217
+ let anyRbacRoleFound = false;
1218
+ for (const p of principalSet) {
1219
+ if (p.type !== "role") continue;
1220
+ if (!await rbac.hasRbacRole(p.id)) continue;
1221
+ anyRbacRoleFound = true;
1222
+ if (await rbac.isGrantedForRole(p.id, `document_type:${typeName}`, "read")) return true;
1223
+ }
1224
+ return !anyRbacRoleFound;
1225
+ }
1171
1226
  var DOC_SORTABLE_COLUMNS = /* @__PURE__ */ new Set([
1172
1227
  "created_at",
1173
1228
  "updated_at",
@@ -1247,10 +1302,37 @@ function mapDocRowToContent(row, collectionId) {
1247
1302
  collectionId,
1248
1303
  data: row.data ? JSON.parse(row.data) : {},
1249
1304
  // D29: document timestamps are SECONDS; the legacy `content` API contract is MILLISECONDS.
1250
- created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(row.created_at),
1251
- updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(row.updated_at)
1305
+ created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(row.created_at),
1306
+ updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(row.updated_at)
1252
1307
  };
1253
1308
  }
1309
+ function projectFields(item, fields) {
1310
+ if (fields.length === 0) return item;
1311
+ const result = {};
1312
+ const dataSubFields = [];
1313
+ for (const f of fields) {
1314
+ if (f === "data") {
1315
+ result.data = item.data;
1316
+ } else if (f.startsWith("data.")) {
1317
+ dataSubFields.push(f.slice(5));
1318
+ } else if (Object.prototype.hasOwnProperty.call(item, f)) {
1319
+ result[f] = item[f];
1320
+ }
1321
+ }
1322
+ if (dataSubFields.length > 0 && !result.data) {
1323
+ result.data = {};
1324
+ for (const sub of dataSubFields) {
1325
+ if (item.data && Object.prototype.hasOwnProperty.call(item.data, sub)) {
1326
+ result.data[sub] = item.data[sub];
1327
+ }
1328
+ }
1329
+ }
1330
+ return result;
1331
+ }
1332
+ function parseFieldsParam(raw2) {
1333
+ if (!raw2) return [];
1334
+ return raw2.split(",").map((s) => s.trim()).filter(Boolean);
1335
+ }
1254
1336
  var apiRoutes = new hono.Hono();
1255
1337
  apiRoutes.use("*", async (c, next) => {
1256
1338
  const startTime = Date.now();
@@ -1260,7 +1342,7 @@ apiRoutes.use("*", async (c, next) => {
1260
1342
  c.header("X-Response-Time", `${totalTime}ms`);
1261
1343
  });
1262
1344
  apiRoutes.use("*", async (c, next) => {
1263
- const cacheEnabled = await chunkIS54FPB7_cjs.isPluginActive(c.env.DB, "core-cache");
1345
+ const cacheEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(c.env.DB, "core-cache");
1264
1346
  c.set("cacheEnabled", cacheEnabled);
1265
1347
  await next();
1266
1348
  });
@@ -1283,7 +1365,7 @@ apiRoutes.get("/", (c) => {
1283
1365
  openapi: "3.0.0",
1284
1366
  info: {
1285
1367
  title: "SonicJS AI API",
1286
- version: chunkC3ZQP5NW_cjs.getCoreVersion(),
1368
+ version: chunkBJ6NJL2E_cjs.getCoreVersion(),
1287
1369
  description: "RESTful API for SonicJS headless CMS - a modern, AI-powered content management system built on Cloudflare Workers",
1288
1370
  contact: {
1289
1371
  name: "SonicJS Support",
@@ -1713,8 +1795,8 @@ apiRoutes.get("/collections", async (c) => {
1713
1795
  }
1714
1796
  c.header("X-Cache-Status", "MISS");
1715
1797
  c.header("X-Cache-Source", "registry");
1716
- const records = chunkAT4ZQGEZ_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
1717
- const transformedResults = records.map(chunkAT4ZQGEZ_cjs.collectionRecordToRow);
1798
+ const records = chunkYAURVGUK_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
1799
+ const transformedResults = records.map(chunkYAURVGUK_cjs.collectionRecordToRow);
1718
1800
  const responseData = {
1719
1801
  data: transformedResults,
1720
1802
  meta: addTimingMeta(c, {
@@ -1735,16 +1817,21 @@ apiRoutes.get("/collections", async (c) => {
1735
1817
  return c.json({ error: "Failed to fetch collections" }, 500);
1736
1818
  }
1737
1819
  });
1738
- apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1820
+ apiRoutes.get("/content", chunkE7E3NL3Y_cjs.optionalAuth(), async (c) => {
1739
1821
  const executionStart = Date.now();
1740
1822
  try {
1741
1823
  const db = c.env.DB;
1742
1824
  const queryParams = c.req.query();
1825
+ const projFields = parseFieldsParam(queryParams.fields);
1826
+ if (queryParams.fields) delete queryParams.fields;
1743
1827
  const role = c.get("user")?.role;
1828
+ const { tenantId: cTenantId, principalSet: cPrincipalSet } = getDocumentRequestContext(c);
1829
+ const cAnon = isAnonPrincipal(cPrincipalSet);
1830
+ const cNeedsAcl = !cAnon && !canReadNonPublicContent(role);
1744
1831
  const collIdByName = /* @__PURE__ */ new Map();
1745
1832
  let typeId;
1746
1833
  let typeIds;
1747
- const registry = chunkAT4ZQGEZ_cjs.getCollectionRegistry();
1834
+ const registry = chunkYAURVGUK_cjs.getCollectionRegistry();
1748
1835
  if (queryParams.collection) {
1749
1836
  const collectionName = queryParams.collection;
1750
1837
  const record = registry.getByName(collectionName);
@@ -1774,11 +1861,11 @@ apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1774
1861
  return r.name;
1775
1862
  });
1776
1863
  }
1777
- const filter = chunkIXUHXTHW_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
1864
+ const filter = chunkDLTT7QZU_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
1778
1865
  const normalizedFilter = augmentFilterForDocuments(filter, { typeId, typeIds, role });
1779
1866
  if (!normalizedFilter.limit) normalizedFilter.limit = 50;
1780
1867
  normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
1781
- const builder = new chunkIXUHXTHW_cjs.QueryFilterBuilder();
1868
+ const builder = new chunkDLTT7QZU_cjs.QueryFilterBuilder();
1782
1869
  const queryResult = builder.build("documents", normalizedFilter);
1783
1870
  if (queryResult.errors.length > 0) {
1784
1871
  return c.json({
@@ -1786,7 +1873,7 @@ apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1786
1873
  details: queryResult.errors
1787
1874
  }, 400);
1788
1875
  }
1789
- const cacheEnabled = c.get("cacheEnabled");
1876
+ const cacheEnabled = c.get("cacheEnabled") && cAnon;
1790
1877
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1791
1878
  const cacheKey = cache.generateKey("content-filtered", JSON.stringify({ filter: normalizedFilter, query: queryResult.sql }));
1792
1879
  if (cacheEnabled) {
@@ -1816,11 +1903,41 @@ apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1816
1903
  const stmt = db.prepare(queryResult.sql);
1817
1904
  const boundStmt = queryResult.params.length > 0 ? stmt.bind(...queryResult.params) : stmt;
1818
1905
  const { results } = await boundStmt.all();
1819
- const transformedResults = results.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
1906
+ let aclResults = results;
1907
+ if (cNeedsAcl) {
1908
+ const typeReg = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
1909
+ const repo = new DocumentRepository(db, cTenantId);
1910
+ const uniqueTypeIds = [...new Set(results.map((r) => r.type_id))];
1911
+ const typeSettingsMap = /* @__PURE__ */ new Map();
1912
+ const typeRbacOnlyMap = /* @__PURE__ */ new Map();
1913
+ const typeDeniedMap = /* @__PURE__ */ new Map();
1914
+ await Promise.all(uniqueTypeIds.map(async (tid) => {
1915
+ const dt = await typeReg.findById(tid);
1916
+ const settings = dt?.settings ?? {};
1917
+ typeSettingsMap.set(tid, settings);
1918
+ const allowed2 = await typeReadAllowed(db, cPrincipalSet, dt, tid);
1919
+ if (!allowed2) {
1920
+ typeDeniedMap.set(tid, true);
1921
+ return;
1922
+ }
1923
+ const perms = new DocumentPermissionsService(db);
1924
+ typeRbacOnlyMap.set(tid, !perms.isAllowedSync(cPrincipalSet, [], "read", settings));
1925
+ }));
1926
+ const allowed = await Promise.all(
1927
+ results.map((row) => {
1928
+ const tid = row.type_id;
1929
+ if (typeDeniedMap.get(tid)) return Promise.resolve(false);
1930
+ if (typeRbacOnlyMap.get(tid)) return Promise.resolve(true);
1931
+ return repo.isAllowed(cPrincipalSet, row.root_id, "read", typeSettingsMap.get(tid) ?? {});
1932
+ })
1933
+ );
1934
+ aclResults = results.filter((_, i) => allowed[i]);
1935
+ }
1936
+ const transformedResults = aclResults.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
1820
1937
  const responseData = {
1821
- data: transformedResults,
1938
+ data: projFields.length ? transformedResults.map((item) => projectFields(item, projFields)) : transformedResults,
1822
1939
  meta: addTimingMeta(c, {
1823
- count: results.length,
1940
+ count: aclResults.length,
1824
1941
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1825
1942
  // D44: echo the caller's filter with the access policy applied (status=published forced for
1826
1943
  // anonymous callers — the visible enforcement proof), NOT the internal doc-augmented where-tree.
@@ -1831,7 +1948,7 @@ apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1831
1948
  }
1832
1949
  }, executionStart)
1833
1950
  };
1834
- if (cacheEnabled) {
1951
+ if (cacheEnabled && projFields.length === 0) {
1835
1952
  await cache.set(cacheKey, responseData);
1836
1953
  }
1837
1954
  return c.json(responseData);
@@ -1843,26 +1960,36 @@ apiRoutes.get("/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1843
1960
  }, 500);
1844
1961
  }
1845
1962
  });
1846
- apiRoutes.get("/collections/:collection/content", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1963
+ apiRoutes.get("/collections/:collection/content", chunkE7E3NL3Y_cjs.optionalAuth(), async (c) => {
1847
1964
  const executionStart = Date.now();
1848
1965
  try {
1849
1966
  const collection = c.req.param("collection");
1850
1967
  const db = c.env.DB;
1851
1968
  const queryParams = c.req.query();
1852
- const record = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getBySlugOrName(collection);
1969
+ const record = chunkYAURVGUK_cjs.getCollectionRegistry().getBySlugOrName(collection);
1853
1970
  if (!record || record.isActive === false) {
1854
1971
  return c.json({ error: "Collection not found" }, 404);
1855
1972
  }
1856
1973
  const collIdByName = /* @__PURE__ */ new Map();
1857
1974
  collIdByName.set(collection, record.id);
1858
1975
  const role = c.get("user")?.role;
1859
- const filter = chunkIXUHXTHW_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
1976
+ const { tenantId: ccTenantId, principalSet: ccPrincipalSet } = getDocumentRequestContext(c);
1977
+ const ccAnon = isAnonPrincipal(ccPrincipalSet);
1978
+ const ccNeedsAcl = !ccAnon && !canReadNonPublicContent(role);
1979
+ let ccDocType;
1980
+ if (ccNeedsAcl) {
1981
+ ccDocType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(record.name);
1982
+ if (!await typeReadAllowed(db, ccPrincipalSet, ccDocType, record.name)) {
1983
+ return c.json({ error: "Forbidden" }, 403);
1984
+ }
1985
+ }
1986
+ const filter = chunkDLTT7QZU_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
1860
1987
  const normalizedFilter = augmentFilterForDocuments(filter, { typeId: collection, role });
1861
1988
  if (!normalizedFilter.limit) {
1862
1989
  normalizedFilter.limit = 50;
1863
1990
  }
1864
1991
  normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
1865
- const builder = new chunkIXUHXTHW_cjs.QueryFilterBuilder();
1992
+ const builder = new chunkDLTT7QZU_cjs.QueryFilterBuilder();
1866
1993
  const queryResult = builder.build("documents", normalizedFilter);
1867
1994
  if (queryResult.errors.length > 0) {
1868
1995
  return c.json({
@@ -1870,7 +1997,7 @@ apiRoutes.get("/collections/:collection/content", chunkIS54FPB7_cjs.optionalAuth
1870
1997
  details: queryResult.errors
1871
1998
  }, 400);
1872
1999
  }
1873
- const cacheEnabled = c.get("cacheEnabled");
2000
+ const cacheEnabled = c.get("cacheEnabled") && ccAnon;
1874
2001
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1875
2002
  const includeCollection = queryParams.include?.split(",").map((s) => s.trim()).includes("collection");
1876
2003
  const cacheKey = cache.generateKey("collection-content-filtered", `${collection}:${JSON.stringify({ filter: normalizedFilter, query: queryResult.sql, includeCollection })}`);
@@ -1901,12 +2028,24 @@ apiRoutes.get("/collections/:collection/content", chunkIS54FPB7_cjs.optionalAuth
1901
2028
  const stmt = db.prepare(queryResult.sql);
1902
2029
  const boundStmt = queryResult.params.length > 0 ? stmt.bind(...queryResult.params) : stmt;
1903
2030
  const { results } = await boundStmt.all();
1904
- const transformedResults = results.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
2031
+ let ccAclResults = results;
2032
+ if (ccNeedsAcl) {
2033
+ const ccPerms = new DocumentPermissionsService(db);
2034
+ const ccBaseGrantsPass = ccPerms.isAllowedSync(ccPrincipalSet, [], "read", ccDocType?.settings ?? {});
2035
+ if (ccBaseGrantsPass) {
2036
+ const repo = new DocumentRepository(db, ccTenantId);
2037
+ const allowed = await Promise.all(
2038
+ results.map((row) => repo.isAllowed(ccPrincipalSet, row.root_id, "read", ccDocType?.settings ?? {}))
2039
+ );
2040
+ ccAclResults = results.filter((_, i) => allowed[i]);
2041
+ }
2042
+ }
2043
+ const transformedResults = ccAclResults.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
1905
2044
  const responseData = {
1906
2045
  data: transformedResults,
1907
2046
  meta: addTimingMeta(c, {
1908
- ...includeCollection ? { collection: chunkAT4ZQGEZ_cjs.collectionRecordToRow(record) } : {},
1909
- count: results.length,
2047
+ ...includeCollection ? { collection: chunkYAURVGUK_cjs.collectionRecordToRow(record) } : {},
2048
+ count: ccAclResults.length,
1910
2049
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1911
2050
  // D44: echo the caller's filter with the access policy applied (status=published forced for
1912
2051
  // anonymous callers — the visible enforcement proof), NOT the internal doc-augmented where-tree.
@@ -1930,31 +2069,43 @@ apiRoutes.get("/collections/:collection/content", chunkIS54FPB7_cjs.optionalAuth
1930
2069
  }
1931
2070
  });
1932
2071
  apiRoutes.route("/content", api_content_crud_default);
1933
- apiRoutes.get("/:collection", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
2072
+ apiRoutes.get("/:collection", chunkE7E3NL3Y_cjs.optionalAuth(), async (c) => {
1934
2073
  const executionStart = Date.now();
1935
2074
  try {
1936
2075
  const collection = c.req.param("collection");
1937
2076
  const db = c.env.DB;
1938
2077
  const queryParams = c.req.query();
1939
- const record = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getBySlugOrName(collection);
2078
+ const projFields = parseFieldsParam(queryParams.fields);
2079
+ if (queryParams.fields) delete queryParams.fields;
2080
+ const record = chunkYAURVGUK_cjs.getCollectionRegistry().getBySlugOrName(collection);
1940
2081
  if (!record || record.isActive === false) {
1941
2082
  return c.json({ error: "Collection not found" }, 404);
1942
2083
  }
1943
2084
  const collIdByName = /* @__PURE__ */ new Map();
1944
2085
  collIdByName.set(record.name, record.id);
1945
2086
  const role = c.get("user")?.role;
1946
- const filter = chunkIXUHXTHW_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
2087
+ const { tenantId, principalSet } = getDocumentRequestContext(c);
2088
+ const anon = isAnonPrincipal(principalSet);
2089
+ const needsAcl = !anon && !canReadNonPublicContent(role);
2090
+ let docTypeForCheck;
2091
+ if (needsAcl) {
2092
+ docTypeForCheck = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(record.name);
2093
+ if (!await typeReadAllowed(db, principalSet, docTypeForCheck, record.name)) {
2094
+ return c.json({ error: "Forbidden" }, 403);
2095
+ }
2096
+ }
2097
+ const filter = chunkDLTT7QZU_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
1947
2098
  const normalizedFilter = augmentFilterForDocuments(filter, { typeId: record.name, role });
1948
2099
  if (!normalizedFilter.limit) normalizedFilter.limit = 50;
1949
2100
  normalizedFilter.limit = Math.min(normalizedFilter.limit, 1e3);
1950
- const builder = new chunkIXUHXTHW_cjs.QueryFilterBuilder();
2101
+ const builder = new chunkDLTT7QZU_cjs.QueryFilterBuilder();
1951
2102
  const queryResult = builder.build("documents", normalizedFilter);
1952
2103
  if (queryResult.errors.length > 0) {
1953
2104
  return c.json({ error: "Invalid filter parameters", details: queryResult.errors }, 400);
1954
2105
  }
1955
2106
  const collectionCache = record.cache;
1956
2107
  const collectionCacheDisabled = collectionCache?.enabled === false;
1957
- const cacheEnabled = c.get("cacheEnabled") && !collectionCacheDisabled;
2108
+ const cacheEnabled = c.get("cacheEnabled") && !collectionCacheDisabled && projFields.length === 0 && anon;
1958
2109
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
1959
2110
  const includeCollection = queryParams.include?.split(",").map((s) => s.trim()).includes("collection");
1960
2111
  const cacheKey = cache.generateKey("collection-content-filtered", `${collection}:${JSON.stringify({ filter: normalizedFilter, query: queryResult.sql, includeCollection })}`);
@@ -1972,12 +2123,25 @@ apiRoutes.get("/:collection", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1972
2123
  const stmt = db.prepare(queryResult.sql);
1973
2124
  const boundStmt = queryResult.params.length > 0 ? stmt.bind(...queryResult.params) : stmt;
1974
2125
  const { results } = await boundStmt.all();
1975
- const transformedResults = results.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
2126
+ let aclResults = results;
2127
+ if (needsAcl) {
2128
+ const perms = new DocumentPermissionsService(db);
2129
+ const baseGrantsPass = perms.isAllowedSync(principalSet, [], "read", docTypeForCheck?.settings ?? {});
2130
+ if (!baseGrantsPass) {
2131
+ } else {
2132
+ const repo = new DocumentRepository(db, tenantId);
2133
+ const allowed = await Promise.all(
2134
+ results.map((row) => repo.isAllowed(principalSet, row.root_id, "read", docTypeForCheck?.settings ?? {}))
2135
+ );
2136
+ aclResults = results.filter((_, i) => allowed[i]);
2137
+ }
2138
+ }
2139
+ const transformedResults = aclResults.map((row) => mapDocRowToContent(row, collIdByName.get(row.type_id) ?? null));
1976
2140
  const responseData = {
1977
- data: transformedResults,
2141
+ data: projFields.length ? transformedResults.map((item) => projectFields(item, projFields)) : transformedResults,
1978
2142
  meta: addTimingMeta(c, {
1979
- ...includeCollection ? { collection: chunkAT4ZQGEZ_cjs.collectionRecordToRow(record) } : {},
1980
- count: results.length,
2143
+ ...includeCollection ? { collection: chunkYAURVGUK_cjs.collectionRecordToRow(record) } : {},
2144
+ count: aclResults.length,
1981
2145
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1982
2146
  filter: normalizePublicContentFilter(filter, role),
1983
2147
  cache: { hit: false, source: "database" }
@@ -1994,12 +2158,12 @@ apiRoutes.get("/:collection", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
1994
2158
  return c.json({ error: "Failed to fetch content", details: error instanceof Error ? error.message : String(error) }, 500);
1995
2159
  }
1996
2160
  });
1997
- apiRoutes.get("/:collection/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) => {
2161
+ apiRoutes.get("/:collection/:id", chunkE7E3NL3Y_cjs.optionalAuth(), async (c) => {
1998
2162
  try {
1999
2163
  const collection = c.req.param("collection");
2000
2164
  const id = c.req.param("id");
2001
2165
  const db = c.env.DB;
2002
- const record = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getBySlugOrName(collection);
2166
+ const record = chunkYAURVGUK_cjs.getCollectionRegistry().getBySlugOrName(collection);
2003
2167
  if (!record || record.isActive === false) {
2004
2168
  return c.json({ error: "Collection not found" }, 404);
2005
2169
  }
@@ -2008,7 +2172,24 @@ apiRoutes.get("/:collection/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
2008
2172
  privileged ? "SELECT * FROM documents WHERE root_id = ? AND type_id = ? AND tenant_id = 'default' AND is_current_draft = 1 AND deleted_at IS NULL" : "SELECT * FROM documents WHERE root_id = ? AND type_id = ? AND tenant_id = 'default' AND is_published = 1 AND deleted_at IS NULL"
2009
2173
  ).bind(id, record.name).first();
2010
2174
  if (!docRow) return c.json({ error: "Content not found" }, 404);
2011
- const coll = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getByName(docRow.type_id);
2175
+ const colRole = c.get("user")?.role;
2176
+ const { tenantId: colTenantId, principalSet: colPrincipalSet } = getDocumentRequestContext(c);
2177
+ const colNeedsAcl = !isAnonPrincipal(colPrincipalSet) && !canReadNonPublicContent(colRole);
2178
+ if (colNeedsAcl) {
2179
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(record.name);
2180
+ if (!await typeReadAllowed(db, colPrincipalSet, docType, record.name)) {
2181
+ return c.json({ error: "Content not found" }, 404);
2182
+ }
2183
+ const perms = new DocumentPermissionsService(db);
2184
+ if (perms.isAllowedSync(colPrincipalSet, [], "read", docType?.settings ?? {})) {
2185
+ const repo = new DocumentRepository(db, colTenantId);
2186
+ if (!await repo.isAllowed(colPrincipalSet, docRow.root_id, "read", docType?.settings ?? {})) {
2187
+ return c.json({ error: "Content not found" }, 404);
2188
+ }
2189
+ }
2190
+ }
2191
+ const projFields = parseFieldsParam(c.req.query("fields"));
2192
+ const coll = chunkYAURVGUK_cjs.getCollectionRegistry().getByName(docRow.type_id);
2012
2193
  const transformedContent = {
2013
2194
  id: docRow.root_id,
2014
2195
  title: docRow.title,
@@ -2016,17 +2197,17 @@ apiRoutes.get("/:collection/:id", chunkIS54FPB7_cjs.optionalAuth(), async (c) =>
2016
2197
  status: docRow.status,
2017
2198
  collectionId: coll?.id ?? docRow.type_id,
2018
2199
  data: docRow.data ? JSON.parse(docRow.data) : {},
2019
- created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(docRow.created_at),
2020
- updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(docRow.updated_at)
2200
+ created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(docRow.created_at),
2201
+ updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(docRow.updated_at)
2021
2202
  };
2022
2203
  dispatchHookEvent(c, "content:read", { collection: docRow.type_id, id: docRow.root_id, data: transformedContent.data }, "fire-and-forget");
2023
- return c.json({ data: transformedContent });
2204
+ return c.json({ data: projFields.length ? projectFields(transformedContent, projFields) : transformedContent });
2024
2205
  } catch (error) {
2025
2206
  console.error("Error fetching content:", error);
2026
2207
  return c.json({ error: "Failed to fetch content", details: error instanceof Error ? error.message : String(error) }, 500);
2027
2208
  }
2028
2209
  });
2029
- apiRoutes.post("/:collection", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2210
+ apiRoutes.post("/:collection", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2030
2211
  try {
2031
2212
  const collection = c.req.param("collection");
2032
2213
  const db = c.env.DB;
@@ -2047,26 +2228,26 @@ apiRoutes.post("/:collection", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cj
2047
2228
  } catch (err) {
2048
2229
  return c.json({ error: "Write cancelled by plugin", details: String(err) }, 400);
2049
2230
  }
2050
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, { queryableFields: backing.docType.queryableFields ?? [], typeSchemaVersion: backing.docType.schemaVersion ?? 1, maxVersionsPerRoot: backing.docType.settings?.maxVersionsPerRoot ?? 50, tenantId: "default", versioning: backing.docType.settings?.versioning ?? false });
2231
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, { queryableFields: backing.docType.queryableFields ?? [], typeSchemaVersion: backing.docType.schemaVersion ?? 1, maxVersionsPerRoot: backing.docType.settings?.maxVersionsPerRoot ?? 50, tenantId: "default", versioning: backing.docType.settings?.versioning ?? false });
2051
2232
  const doc = await svc.create(createDocumentSchema.parse({ typeId: backing.coll.name, tenantId: "default", locale: "default", title, slug: finalSlug, data: hookData, publishOnCreate: (status || "draft") === "published" }), user?.userId);
2052
2233
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
2053
- await cache.invalidate("content-filtered:*");
2054
- await cache.invalidate("collection-content-filtered:*");
2234
+ await cache.invalidate("api:content-filtered:*");
2235
+ await cache.invalidate(`api:collection-content-filtered:${backing.coll.name}:*`);
2055
2236
  dispatchHookEvent(c, "content:after:create", { collection: backing.coll.name, id: doc.rootId, data: doc.data ?? {}, user: actor }, "fire-and-forget");
2056
- return c.json({ data: { id: doc.rootId, title: doc.title, slug: doc.slug, status: doc.status, collectionId: backing.coll.id, data: doc.data, created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(doc.createdAt), updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(doc.updatedAt) } }, 201);
2237
+ return c.json({ data: { id: doc.rootId, title: doc.title, slug: doc.slug, status: doc.status, collectionId: backing.coll.id, data: doc.data, created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(doc.createdAt), updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(doc.updatedAt) } }, 201);
2057
2238
  } catch (error) {
2058
2239
  console.error("Error creating content:", error);
2059
2240
  return c.json({ error: "Failed to create content", details: error instanceof Error ? error.message : String(error) }, 500);
2060
2241
  }
2061
2242
  });
2062
- apiRoutes.put("/:collection/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2243
+ apiRoutes.put("/:collection/:id", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2063
2244
  try {
2064
2245
  const collection = c.req.param("collection");
2065
2246
  const id = c.req.param("id");
2066
2247
  const db = c.env.DB;
2067
2248
  const user = c.get("user");
2068
2249
  const body = await c.req.json();
2069
- const collRecord = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getBySlugOrName(collection);
2250
+ const collRecord = chunkYAURVGUK_cjs.getCollectionRegistry().getBySlugOrName(collection);
2070
2251
  const typeName = collRecord?.name ?? collection;
2071
2252
  const docRow = await db.prepare("SELECT root_id, type_id FROM documents WHERE root_id = ? AND type_id = ? AND tenant_id = 'default' AND is_current_draft = 1 AND deleted_at IS NULL").bind(id, typeName).first();
2072
2253
  if (!docRow) return c.json({ error: "Content not found" }, 404);
@@ -2078,8 +2259,8 @@ apiRoutes.put("/:collection/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7
2078
2259
  } catch (err) {
2079
2260
  return c.json({ error: "Write cancelled by plugin", details: String(err) }, 400);
2080
2261
  }
2081
- const docType = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(docRow.type_id);
2082
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, { queryableFields: docType?.queryableFields ?? [], typeSchemaVersion: docType?.schemaVersion ?? 1, maxVersionsPerRoot: docType?.settings?.maxVersionsPerRoot ?? 50, tenantId: "default", versioning: docType?.settings?.versioning ?? false });
2262
+ const docType = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(docRow.type_id);
2263
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, { queryableFields: docType?.queryableFields ?? [], typeSchemaVersion: docType?.schemaVersion ?? 1, maxVersionsPerRoot: docType?.settings?.maxVersionsPerRoot ?? 50, tenantId: "default", versioning: docType?.settings?.versioning ?? false });
2083
2264
  const input = {};
2084
2265
  if (body.title !== void 0) input.title = body.title;
2085
2266
  if (body.slug !== void 0) input.slug = slugify(body.slug);
@@ -2093,27 +2274,27 @@ apiRoutes.put("/:collection/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7
2093
2274
  await svc.unpublish(pub.id);
2094
2275
  }
2095
2276
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
2096
- await cache.invalidate("content-filtered:*");
2097
- await cache.invalidate("collection-content-filtered:*");
2098
- const coll = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getByName(docRow.type_id);
2277
+ await cache.invalidate("api:content-filtered:*");
2278
+ await cache.invalidate(`api:collection-content-filtered:${docRow.type_id}:*`);
2279
+ const coll = chunkYAURVGUK_cjs.getCollectionRegistry().getByName(docRow.type_id);
2099
2280
  const saved = await db.prepare("SELECT * FROM documents WHERE id = ?").bind(newDraft.id).first();
2100
2281
  const savedData = saved?.data ? JSON.parse(saved.data) : {};
2101
2282
  dispatchHookEvent(c, "content:after:update", { collection: docRow.type_id, id, data: savedData, user: actor }, "fire-and-forget");
2102
2283
  const nowPublished = body.status === "published" || body.status === void 0 && wasPublished;
2103
2284
  if (nowPublished && !wasPublished) dispatchHookEvent(c, "content:after:publish", { collection: docRow.type_id, id, data: savedData, user: actor }, "fire-and-forget");
2104
- return c.json({ data: { id: saved.root_id, title: saved.title, slug: saved.slug, status: saved.status, collectionId: coll?.id ?? docRow.type_id, data: savedData, created_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(saved.created_at), updated_at: chunkLTZ5WYOK_cjs.documentSecondsToMs(saved.updated_at) } });
2285
+ return c.json({ data: { id: saved.root_id, title: saved.title, slug: saved.slug, status: saved.status, collectionId: coll?.id ?? docRow.type_id, data: savedData, created_at: chunkJCBDXEY2_cjs.documentSecondsToMs(saved.created_at), updated_at: chunkJCBDXEY2_cjs.documentSecondsToMs(saved.updated_at) } });
2105
2286
  } catch (error) {
2106
2287
  console.error("Error updating content:", error);
2107
2288
  return c.json({ error: "Failed to update content", details: error instanceof Error ? error.message : String(error) }, 500);
2108
2289
  }
2109
2290
  });
2110
- apiRoutes.delete("/:collection/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2291
+ apiRoutes.delete("/:collection/:id", chunkE7E3NL3Y_cjs.requireAuth(), chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
2111
2292
  try {
2112
2293
  const collection = c.req.param("collection");
2113
2294
  const id = c.req.param("id");
2114
2295
  const db = c.env.DB;
2115
2296
  const user = c.get("user");
2116
- const collRecord = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getBySlugOrName(collection);
2297
+ const collRecord = chunkYAURVGUK_cjs.getCollectionRegistry().getBySlugOrName(collection);
2117
2298
  const typeName = collRecord?.name ?? collection;
2118
2299
  const docRow = await db.prepare("SELECT type_id FROM documents WHERE root_id = ? AND type_id = ? AND tenant_id = 'default' AND deleted_at IS NULL LIMIT 1").bind(id, typeName).first();
2119
2300
  if (!docRow) return c.json({ error: "Content not found" }, 404);
@@ -2126,8 +2307,8 @@ apiRoutes.delete("/:collection/:id", chunkIS54FPB7_cjs.requireAuth(), chunkIS54F
2126
2307
  const now = Math.floor(Date.now() / 1e3);
2127
2308
  await db.prepare("UPDATE documents SET deleted_at = ?, updated_at = ? WHERE root_id = ? AND tenant_id = 'default'").bind(now, now, id).run();
2128
2309
  const cache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
2129
- await cache.invalidate("content-filtered:*");
2130
- await cache.invalidate("collection-content-filtered:*");
2310
+ await cache.invalidate("api:content-filtered:*");
2311
+ await cache.invalidate(`api:collection-content-filtered:${docRow.type_id}:*`);
2131
2312
  dispatchHookEvent(c, "content:after:delete", { collection: docRow.type_id, id, data: {}, user: actor }, "fire-and-forget");
2132
2313
  return c.json({ success: true });
2133
2314
  } catch (error) {
@@ -2200,7 +2381,7 @@ var MediaDocumentService = class {
2200
2381
  tenantId;
2201
2382
  /** Create a media_asset document from R2 upload metadata. Published-on-create (immediately usable). */
2202
2383
  async createFromUpload(meta, createdBy) {
2203
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(this.db, { queryableFields: MEDIA_QUERYABLE, tenantId: this.tenantId, maxVersionsPerRoot: 5 });
2384
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(this.db, { queryableFields: MEDIA_QUERYABLE, tenantId: this.tenantId, maxVersionsPerRoot: 5 });
2204
2385
  return svc.create(
2205
2386
  createDocumentSchema.parse({
2206
2387
  typeId: "media_asset",
@@ -2278,7 +2459,7 @@ var MediaDocumentService = class {
2278
2459
  }
2279
2460
  /** Update alt/caption/tags on a media_asset document (saveDraft + publish atomically). */
2280
2461
  async updateMetadata(rootId, meta, updatedBy) {
2281
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(this.db, { queryableFields: MEDIA_QUERYABLE, tenantId: this.tenantId, maxVersionsPerRoot: 5 });
2462
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(this.db, { queryableFields: MEDIA_QUERYABLE, tenantId: this.tenantId, maxVersionsPerRoot: 5 });
2282
2463
  const newDraft = await svc.saveDraft(rootId, { data: meta }, updatedBy);
2283
2464
  return svc.publish(newDraft.id, updatedBy);
2284
2465
  }
@@ -2335,7 +2516,7 @@ var fileValidationSchema = zod.z.object({
2335
2516
  // 50MB max
2336
2517
  });
2337
2518
  var apiMediaRoutes = new hono.Hono();
2338
- apiMediaRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
2519
+ apiMediaRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
2339
2520
  apiMediaRoutes.post("/upload", async (c) => {
2340
2521
  try {
2341
2522
  const user = c.get("user");
@@ -2689,9 +2870,13 @@ apiMediaRoutes.post("/create-folder", async (c) => {
2689
2870
  error: "Folder name can only contain lowercase letters, numbers, hyphens, and underscores"
2690
2871
  }, 400);
2691
2872
  }
2692
- const checkStmt = c.env.DB.prepare("SELECT COUNT(*) as count FROM media WHERE folder = ? AND deleted_at IS NULL");
2693
- const existingFolder = await checkStmt.bind(folderName).first();
2694
- if (existingFolder && existingFolder.count > 0) {
2873
+ let folderExists = false;
2874
+ try {
2875
+ const existing = await c.env.DB.prepare("SELECT COUNT(*) as count FROM documents WHERE type_id = 'media_asset' AND tenant_id = 'default' AND json_extract(data, '$.folder') = ? AND deleted_at IS NULL AND is_current_draft = 1").bind(folderName).first();
2876
+ folderExists = (existing?.count ?? 0) > 0;
2877
+ } catch {
2878
+ }
2879
+ if (folderExists) {
2695
2880
  return c.json({
2696
2881
  success: false,
2697
2882
  error: `Folder "${folderName}" already exists`
@@ -3033,7 +3218,7 @@ apiSystemRoutes.get("/info", (c) => {
3033
3218
  apiSystemRoutes.get("/stats", async (c) => {
3034
3219
  try {
3035
3220
  const db = c.env.DB;
3036
- const typeIds = chunkAT4ZQGEZ_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal).map((r) => r.name);
3221
+ const typeIds = chunkYAURVGUK_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal).map((r) => r.name);
3037
3222
  let contentStats = null;
3038
3223
  if (typeIds.length > 0) {
3039
3224
  const placeholders = typeIds.map(() => "?").join(",");
@@ -3108,12 +3293,12 @@ apiSystemRoutes.get("/env", (c) => {
3108
3293
  });
3109
3294
  var api_system_default = apiSystemRoutes;
3110
3295
  var adminApiRoutes = new hono.Hono();
3111
- adminApiRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
3112
- adminApiRoutes.use("*", chunkIS54FPB7_cjs.requireRole(["admin", "editor"]));
3296
+ adminApiRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
3297
+ adminApiRoutes.use("*", chunkE7E3NL3Y_cjs.requireRole(["admin", "editor"]));
3113
3298
  adminApiRoutes.get("/stats", async (c) => {
3114
3299
  try {
3115
3300
  const db = c.env.DB;
3116
- const userCollections = chunkAT4ZQGEZ_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
3301
+ const userCollections = chunkYAURVGUK_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
3117
3302
  const collectionsCount = userCollections.length;
3118
3303
  let contentCount = 0;
3119
3304
  if (userCollections.length > 0) {
@@ -3204,7 +3389,7 @@ adminApiRoutes.get("/collections", async (c) => {
3204
3389
  try {
3205
3390
  const search = c.req.query("search") || "";
3206
3391
  const includeInactive = c.req.query("includeInactive") === "true";
3207
- let records = chunkAT4ZQGEZ_cjs.getCollectionRegistry().list().filter((r) => !r.internal);
3392
+ let records = chunkYAURVGUK_cjs.getCollectionRegistry().list().filter((r) => !r.internal);
3208
3393
  if (!includeInactive) records = records.filter((r) => r.isActive !== false);
3209
3394
  if (search) {
3210
3395
  const needle = search.toLowerCase();
@@ -3236,7 +3421,7 @@ adminApiRoutes.get("/collections", async (c) => {
3236
3421
  adminApiRoutes.get("/collections/:id", async (c) => {
3237
3422
  try {
3238
3423
  const id = c.req.param("id");
3239
- const registry = chunkAT4ZQGEZ_cjs.getCollectionRegistry();
3424
+ const registry = chunkYAURVGUK_cjs.getCollectionRegistry();
3240
3425
  const record = registry.getById(id) ?? registry.getByName(id);
3241
3426
  if (!record) {
3242
3427
  return c.json({ error: "Collection not found" }, 404);
@@ -3286,7 +3471,7 @@ adminApiRoutes.get("/references", async (c) => {
3286
3471
  if (collectionParams.length === 0) {
3287
3472
  return c.json({ error: "Collection is required" }, 400);
3288
3473
  }
3289
- const registry = chunkAT4ZQGEZ_cjs.getCollectionRegistry();
3474
+ const registry = chunkYAURVGUK_cjs.getCollectionRegistry();
3290
3475
  const matched = collectionParams.map((param) => registry.getById(param) ?? registry.getByName(param)).filter((r) => !!r);
3291
3476
  const seen = /* @__PURE__ */ new Set();
3292
3477
  const collections = matched.filter((r) => {
@@ -3401,7 +3586,7 @@ adminApiRoutes.patch("/collections/:id", collectionsReadOnly);
3401
3586
  adminApiRoutes.delete("/collections/:id", collectionsReadOnly);
3402
3587
  adminApiRoutes.get("/migrations/status", async (c) => {
3403
3588
  try {
3404
- const { MigrationService: MigrationService2 } = await import('./migrations-ZD7Y5NUC.cjs');
3589
+ const { MigrationService: MigrationService2 } = await import('./migrations-KU4V6T73.cjs');
3405
3590
  const db = c.env.DB;
3406
3591
  const migrationService = new MigrationService2(db);
3407
3592
  const status = await migrationService.getMigrationStatus();
@@ -3444,7 +3629,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
3444
3629
  });
3445
3630
  adminApiRoutes.get("/migrations/validate", async (c) => {
3446
3631
  try {
3447
- const { MigrationService: MigrationService2 } = await import('./migrations-ZD7Y5NUC.cjs');
3632
+ const { MigrationService: MigrationService2 } = await import('./migrations-KU4V6T73.cjs');
3448
3633
  const db = c.env.DB;
3449
3634
  const migrationService = new MigrationService2(db);
3450
3635
  const validation = await migrationService.validateSchema();
@@ -3594,8 +3779,8 @@ function renderLoginPage(data, demoLoginActive = false) {
3594
3779
  <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
3595
3780
  <div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
3596
3781
  <!-- Alerts -->
3597
- ${data.error ? `<div class="mb-6">${chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
3598
- ${data.message ? `<div class="mb-6">${chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.message })}</div>` : ""}
3782
+ ${data.error ? `<div class="mb-6">${chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
3783
+ ${data.message ? `<div class="mb-6">${chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.message })}</div>` : ""}
3599
3784
 
3600
3785
  <!-- Form Response (HTMX target) -->
3601
3786
  <div id="form-response" class="mb-6"></div>
@@ -3603,6 +3788,8 @@ function renderLoginPage(data, demoLoginActive = false) {
3603
3788
  <!-- Form -->
3604
3789
  <form
3605
3790
  id="login-form"
3791
+ action="/auth/login/form${data.redirect ? `?redirect=${encodeURIComponent(data.redirect)}` : ""}"
3792
+ method="post"
3606
3793
  hx-post="/auth/login/form${data.redirect ? `?redirect=${encodeURIComponent(data.redirect)}` : ""}"
3607
3794
  hx-target="#form-response"
3608
3795
  hx-swap="innerHTML"
@@ -3759,7 +3946,7 @@ function renderRegisterPage(data) {
3759
3946
  <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
3760
3947
  <div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
3761
3948
  <!-- Alerts -->
3762
- ${data.error ? `<div class="mb-6">${chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
3949
+ ${data.error ? `<div class="mb-6">${chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
3763
3950
 
3764
3951
  <!-- Form -->
3765
3952
  <form
@@ -3859,7 +4046,7 @@ function renderRegisterPage(data) {
3859
4046
  }
3860
4047
 
3861
4048
  // src/services/email/email-service-singleton.ts
3862
- var singleton = chunk4WMZGHIC_cjs.createServiceSingleton("EmailService");
4049
+ var singleton = chunkD6FWEYQE_cjs.createServiceSingleton("EmailService");
3863
4050
  function setEmailService(service2) {
3864
4051
  singleton.set(service2);
3865
4052
  }
@@ -3903,7 +4090,7 @@ function emptyProfile() {
3903
4090
  return { custom: {} };
3904
4091
  }
3905
4092
  function service(db) {
3906
- return new chunkLTZ5WYOK_cjs.DocumentsService(db, { tenantId: "default", maxVersionsPerRoot: 1, queryableFields: [] });
4093
+ return new chunkJCBDXEY2_cjs.DocumentsService(db, { tenantId: "default", maxVersionsPerRoot: 1, queryableFields: [] });
3907
4094
  }
3908
4095
  async function readProfileData(db, userId) {
3909
4096
  const row = await db.prepare(
@@ -4667,7 +4854,7 @@ function getLexicalInitScript(settings = {}) {
4667
4854
  </script>
4668
4855
  `;
4669
4856
  }
4670
- var lexicalEditorPlugin = chunk4WMZGHIC_cjs.definePlugin({
4857
+ var lexicalEditorPlugin = chunkD6FWEYQE_cjs.definePlugin({
4671
4858
  id: "lexical-editor",
4672
4859
  version: "1.0.0",
4673
4860
  name: "Lexical Editor",
@@ -5118,9 +5305,11 @@ function renderDynamicField(field, options = {}) {
5118
5305
  min="${opts.min || ""}"
5119
5306
  max="${opts.max || ""}"
5120
5307
  class="${baseClasses} ${errorClasses} !w-auto dark:[color-scheme:dark]"
5308
+ placeholder="YYYY-MM-DD"
5121
5309
  ${required}
5122
5310
  ${disabled ? "disabled" : ""}
5123
5311
  >
5312
+ <p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">Format: YYYY-MM-DD</p>
5124
5313
  `;
5125
5314
  break;
5126
5315
  case "datetime":
@@ -5136,6 +5325,7 @@ function renderDynamicField(field, options = {}) {
5136
5325
  ${required}
5137
5326
  ${disabled ? "disabled" : ""}
5138
5327
  >
5328
+ <p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">Format: YYYY-MM-DD HH:MM</p>
5139
5329
  `;
5140
5330
  break;
5141
5331
  case "slug":
@@ -7073,7 +7263,7 @@ api.put("/:userId", async (c) => {
7073
7263
  await saveCustomData(db, userId, sanitized);
7074
7264
  return c.json({ success: true });
7075
7265
  });
7076
- var userProfilesPlugin = chunk4WMZGHIC_cjs.definePlugin({
7266
+ var userProfilesPlugin = chunkD6FWEYQE_cjs.definePlugin({
7077
7267
  id: "user-profiles",
7078
7268
  version: "1.0.0",
7079
7269
  name: "User Profiles",
@@ -7092,6 +7282,21 @@ function createUserProfilesPlugin() {
7092
7282
 
7093
7283
  // src/routes/auth.ts
7094
7284
  var JWT_SECRET_FALLBACK = "your-super-secret-jwt-key-change-in-production";
7285
+ var DEMO_EMAIL = "admin@sonicjs.com";
7286
+ var STATS_EVENTS_ENDPOINT = "https://stats.sonicjs.com/v1/events";
7287
+ async function trackDemoLogin(kv) {
7288
+ try {
7289
+ const installationId = await kv?.get("_sonicjs_installation_id") ?? "demo-unknown";
7290
+ await fetch(STATS_EVENTS_ENDPOINT, {
7291
+ method: "POST",
7292
+ headers: { "Content-Type": "application/json" },
7293
+ body: JSON.stringify({
7294
+ data: { installation_id: installationId, event_type: "demo_login", properties: {}, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
7295
+ })
7296
+ });
7297
+ } catch {
7298
+ }
7299
+ }
7095
7300
  function renderPasswordResetEmail(resetLink, firstName) {
7096
7301
  const greeting = firstName ? `Hi ${firstName},` : "Hello,";
7097
7302
  return `<!DOCTYPE html>
@@ -7111,8 +7316,8 @@ function renderPasswordResetEmail(resetLink, firstName) {
7111
7316
  async function setCsrfCookie(c, maxAge) {
7112
7317
  const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK;
7113
7318
  const isDev = c.env?.ENVIRONMENT === "development" || !c.env?.ENVIRONMENT;
7114
- const csrfToken = await chunkIS54FPB7_cjs.generateCsrfToken(secret);
7115
- const cookieMaxAge = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(c.env?.DB, c.env);
7319
+ const csrfToken = await chunkE7E3NL3Y_cjs.generateCsrfToken(secret);
7320
+ const cookieMaxAge = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(c.env?.DB, c.env);
7116
7321
  cookie.setCookie(c, "csrf_token", csrfToken, {
7117
7322
  httpOnly: false,
7118
7323
  secure: !isDev,
@@ -7171,7 +7376,7 @@ var loginSchema = zod.z.object({
7171
7376
  });
7172
7377
  authRoutes.post(
7173
7378
  "/register",
7174
- chunkIS54FPB7_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "register" }),
7379
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "register" }),
7175
7380
  async (c) => {
7176
7381
  try {
7177
7382
  const db = c.env.DB;
@@ -7207,7 +7412,7 @@ authRoutes.post(
7207
7412
  if (existingUser) {
7208
7413
  return c.json({ error: "User with this email already exists" }, 400);
7209
7414
  }
7210
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(password);
7415
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(password);
7211
7416
  const userId = crypto.randomUUID();
7212
7417
  const now = /* @__PURE__ */ new Date();
7213
7418
  const nowSec = Math.floor(now.getTime() / 1e3);
@@ -7240,8 +7445,8 @@ authRoutes.post(
7240
7445
  { user: { id: userId, email: normalizedEmail, role: "viewer" } },
7241
7446
  "fire-and-forget"
7242
7447
  );
7243
- const tokenTtl = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7244
- const token = await chunkIS54FPB7_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET, tokenTtl);
7448
+ const tokenTtl = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7449
+ const token = await chunkE7E3NL3Y_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET, tokenTtl);
7245
7450
  cookie.setCookie(c, "auth_token", token, {
7246
7451
  httpOnly: true,
7247
7452
  secure: true,
@@ -7273,7 +7478,7 @@ authRoutes.post(
7273
7478
  );
7274
7479
  authRoutes.post(
7275
7480
  "/login",
7276
- chunkIS54FPB7_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "login" }),
7481
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "login" }),
7277
7482
  async (c) => {
7278
7483
  try {
7279
7484
  const body = await c.req.json();
@@ -7283,8 +7488,8 @@ authRoutes.post(
7283
7488
  }
7284
7489
  const { email, password } = validation.data;
7285
7490
  const normalizedEmail = email.toLowerCase();
7286
- const { createAuth } = await import('./config-OCH7S4WK.cjs');
7287
- const auth = createAuth(c.env);
7491
+ const { createAuth } = await import('./config-XN2ETAJ3.cjs');
7492
+ const auth = createAuth(c.env, void 0, new URL(c.req.url).origin);
7288
7493
  const baReq = new Request(new URL("/auth/sign-in/email", c.req.url).href, {
7289
7494
  method: "POST",
7290
7495
  headers: { "Content-Type": "application/json", "Origin": new URL(c.req.url).origin },
@@ -7294,19 +7499,21 @@ authRoutes.post(
7294
7499
  if (!baRes.ok) {
7295
7500
  return c.json({ error: "Invalid email or password" }, 401);
7296
7501
  }
7297
- const rawSetCookie = baRes.headers.get("set-cookie");
7298
- if (rawSetCookie) {
7299
- c.res.headers.append("Set-Cookie", rawSetCookie);
7300
- } else if (baRes.headers.getSetCookie) {
7502
+ if (baRes.headers.getSetCookie) {
7301
7503
  for (const sc of baRes.headers.getSetCookie()) {
7302
- c.res.headers.append("Set-Cookie", sc);
7504
+ c.header("Set-Cookie", sc, { append: true });
7505
+ }
7506
+ } else {
7507
+ const rawSetCookie = baRes.headers.get("set-cookie");
7508
+ if (rawSetCookie) {
7509
+ c.header("Set-Cookie", rawSetCookie, { append: true });
7303
7510
  }
7304
7511
  }
7305
7512
  await setCsrfCookie(c);
7306
7513
  const baBody = await baRes.json();
7307
7514
  const user = baBody.user ?? {};
7308
- const tokenTtl = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7309
- const token = await chunkIS54FPB7_cjs.AuthManager.generateToken(
7515
+ const tokenTtl = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7516
+ const token = await chunkE7E3NL3Y_cjs.AuthManager.generateToken(
7310
7517
  user.id,
7311
7518
  user.email,
7312
7519
  user.role ?? "viewer",
@@ -7319,6 +7526,9 @@ authRoutes.post(
7319
7526
  sameSite: "Strict",
7320
7527
  maxAge: tokenTtl
7321
7528
  });
7529
+ if (normalizedEmail === DEMO_EMAIL) {
7530
+ c.executionCtx?.waitUntil(trackDemoLogin(c.env.CACHE_KV));
7531
+ }
7322
7532
  return c.json({
7323
7533
  user: {
7324
7534
  id: user.id,
@@ -7337,8 +7547,8 @@ authRoutes.post(
7337
7547
  );
7338
7548
  authRoutes.post("/logout", async (c) => {
7339
7549
  try {
7340
- const { createAuth } = await import('./config-OCH7S4WK.cjs');
7341
- const auth = createAuth(c.env);
7550
+ const { createAuth } = await import('./config-XN2ETAJ3.cjs');
7551
+ const auth = createAuth(c.env, void 0, new URL(c.req.url).origin);
7342
7552
  const baReq = new Request(new URL("/auth/sign-out", c.req.url).href, {
7343
7553
  method: "POST",
7344
7554
  headers: { "Content-Type": "application/json", "Origin": new URL(c.req.url).origin, "Cookie": c.req.header("Cookie") || "" },
@@ -7347,14 +7557,15 @@ authRoutes.post("/logout", async (c) => {
7347
7557
  await auth.handler(baReq);
7348
7558
  } catch {
7349
7559
  }
7350
- cookie.setCookie(c, "better-auth.session_token", "", { httpOnly: true, sameSite: "Lax", path: "/", maxAge: 0 });
7560
+ const isSecure = new URL(c.req.url).protocol === "https:";
7561
+ cookie.setCookie(c, "better-auth.session_token", "", { httpOnly: true, sameSite: "Lax", path: "/", maxAge: 0, secure: isSecure });
7351
7562
  clearCsrfCookie(c);
7352
7563
  return c.json({ message: "Logged out successfully" });
7353
7564
  });
7354
7565
  authRoutes.get("/logout", async (c) => {
7355
7566
  try {
7356
- const { createAuth } = await import('./config-OCH7S4WK.cjs');
7357
- const auth = createAuth(c.env);
7567
+ const { createAuth } = await import('./config-XN2ETAJ3.cjs');
7568
+ const auth = createAuth(c.env, void 0, new URL(c.req.url).origin);
7358
7569
  const baReq = new Request(new URL("/auth/sign-out", c.req.url).href, {
7359
7570
  method: "POST",
7360
7571
  headers: { "Content-Type": "application/json", "Origin": new URL(c.req.url).origin, "Cookie": c.req.header("Cookie") || "" },
@@ -7363,11 +7574,12 @@ authRoutes.get("/logout", async (c) => {
7363
7574
  await auth.handler(baReq);
7364
7575
  } catch {
7365
7576
  }
7366
- cookie.setCookie(c, "better-auth.session_token", "", { httpOnly: true, sameSite: "Lax", path: "/", maxAge: 0 });
7577
+ const isSecure = new URL(c.req.url).protocol === "https:";
7578
+ cookie.setCookie(c, "better-auth.session_token", "", { httpOnly: true, sameSite: "Lax", path: "/", maxAge: 0, secure: isSecure });
7367
7579
  clearCsrfCookie(c);
7368
7580
  return c.redirect("/auth/login?message=You have been logged out successfully");
7369
7581
  });
7370
- authRoutes.get("/me", chunkIS54FPB7_cjs.requireAuth(), async (c) => {
7582
+ authRoutes.get("/me", chunkE7E3NL3Y_cjs.requireAuth(), async (c) => {
7371
7583
  try {
7372
7584
  const user = c.get("user");
7373
7585
  if (!user) {
@@ -7387,7 +7599,7 @@ authRoutes.get("/me", chunkIS54FPB7_cjs.requireAuth(), async (c) => {
7387
7599
  });
7388
7600
  authRoutes.post(
7389
7601
  "/refresh",
7390
- chunkIS54FPB7_cjs.rateLimit({ max: 60, windowMs: 60 * 1e3, keyPrefix: "refresh" }),
7602
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 60, windowMs: 60 * 1e3, keyPrefix: "refresh" }),
7391
7603
  async (c) => {
7392
7604
  try {
7393
7605
  let token = c.req.header("Authorization")?.replace("Bearer ", "");
@@ -7396,8 +7608,8 @@ authRoutes.post(
7396
7608
  return c.json({ error: "Authentication required" }, 401);
7397
7609
  }
7398
7610
  const db = c.env.DB;
7399
- const grace = await chunkIS54FPB7_cjs.getJwtRefreshGraceSecondsFromDb(db, c.env);
7400
- const payload = await chunkIS54FPB7_cjs.AuthManager.verifyToken(token, c.env.JWT_SECRET, grace);
7611
+ const grace = await chunkE7E3NL3Y_cjs.getJwtRefreshGraceSecondsFromDb(db, c.env);
7612
+ const payload = await chunkE7E3NL3Y_cjs.AuthManager.verifyToken(token, c.env.JWT_SECRET, grace);
7401
7613
  if (!payload) {
7402
7614
  return c.json({ error: "Invalid or expired token" }, 401);
7403
7615
  }
@@ -7405,8 +7617,8 @@ authRoutes.post(
7405
7617
  if (!row || !row.is_active) {
7406
7618
  return c.json({ error: "User is not active" }, 401);
7407
7619
  }
7408
- const tokenTtl = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(db, c.env);
7409
- const newToken = await chunkIS54FPB7_cjs.AuthManager.generateToken(row.id, row.email, row.role, c.env.JWT_SECRET, tokenTtl);
7620
+ const tokenTtl = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(db, c.env);
7621
+ const newToken = await chunkE7E3NL3Y_cjs.AuthManager.generateToken(row.id, row.email, row.role, c.env.JWT_SECRET, tokenTtl);
7410
7622
  cookie.setCookie(c, "auth_token", newToken, {
7411
7623
  httpOnly: true,
7412
7624
  secure: true,
@@ -7426,7 +7638,7 @@ authRoutes.post(
7426
7638
  );
7427
7639
  authRoutes.post(
7428
7640
  "/register/form",
7429
- chunkIS54FPB7_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "register" }),
7641
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "register" }),
7430
7642
  async (c) => {
7431
7643
  try {
7432
7644
  const db = c.env.DB;
@@ -7471,7 +7683,7 @@ authRoutes.post(
7471
7683
  </div>
7472
7684
  `);
7473
7685
  }
7474
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(password);
7686
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(password);
7475
7687
  const role = isFirstUser ? "admin" : "viewer";
7476
7688
  const userId = crypto.randomUUID();
7477
7689
  const now = /* @__PURE__ */ new Date();
@@ -7505,7 +7717,7 @@ authRoutes.post(
7505
7717
  await saveCustomData(db, userId, sanitized);
7506
7718
  }
7507
7719
  }
7508
- const rbacService = new chunkLTZ5WYOK_cjs.RbacService(db);
7720
+ const rbacService = new chunkJCBDXEY2_cjs.RbacService(db);
7509
7721
  await rbacService.addUserRoleByName(userId, role);
7510
7722
  dispatchHookEvent(
7511
7723
  c,
@@ -7513,8 +7725,8 @@ authRoutes.post(
7513
7725
  { user: { id: userId, email: normalizedEmail, role } },
7514
7726
  "fire-and-forget"
7515
7727
  );
7516
- const tokenTtl = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7517
- const token = await chunkIS54FPB7_cjs.AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET, tokenTtl);
7728
+ const tokenTtl = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7729
+ const token = await chunkE7E3NL3Y_cjs.AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET, tokenTtl);
7518
7730
  cookie.setCookie(c, "auth_token", token, {
7519
7731
  httpOnly: true,
7520
7732
  secure: false,
@@ -7546,7 +7758,7 @@ authRoutes.post(
7546
7758
  );
7547
7759
  authRoutes.post(
7548
7760
  "/login/form",
7549
- chunkIS54FPB7_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "login" }),
7761
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "login" }),
7550
7762
  async (c) => {
7551
7763
  try {
7552
7764
  const formData = await c.req.formData();
@@ -7560,8 +7772,8 @@ authRoutes.post(
7560
7772
  </div>
7561
7773
  `);
7562
7774
  }
7563
- const { createAuth } = await import('./config-OCH7S4WK.cjs');
7564
- const auth = createAuth(c.env);
7775
+ const { createAuth } = await import('./config-XN2ETAJ3.cjs');
7776
+ const auth = createAuth(c.env, void 0, new URL(c.req.url).origin);
7565
7777
  const baReq = new Request(new URL("/auth/sign-in/email", c.req.url).href, {
7566
7778
  method: "POST",
7567
7779
  headers: { "Content-Type": "application/json", "Origin": new URL(c.req.url).origin },
@@ -7575,17 +7787,27 @@ authRoutes.post(
7575
7787
  </div>
7576
7788
  `);
7577
7789
  }
7578
- const rawSetCookie = baRes.headers.get("set-cookie");
7579
- if (rawSetCookie) {
7580
- c.res.headers.append("Set-Cookie", rawSetCookie);
7581
- } else if (baRes.headers.getSetCookie) {
7790
+ if (baRes.headers.getSetCookie) {
7582
7791
  for (const sc of baRes.headers.getSetCookie()) {
7583
- c.res.headers.append("Set-Cookie", sc);
7792
+ c.header("Set-Cookie", sc, { append: true });
7793
+ }
7794
+ } else {
7795
+ const rawSetCookie = baRes.headers.get("set-cookie");
7796
+ if (rawSetCookie) {
7797
+ c.header("Set-Cookie", rawSetCookie, { append: true });
7584
7798
  }
7585
7799
  }
7586
7800
  await setCsrfCookie(c);
7801
+ if (email === DEMO_EMAIL) {
7802
+ c.executionCtx?.waitUntil(trackDemoLogin(c.env.CACHE_KV));
7803
+ }
7587
7804
  const rawRedirect = c.req.query("redirect");
7588
7805
  const redirectUrl = rawRedirect && rawRedirect.startsWith("/") ? rawRedirect : "/admin/content";
7806
+ const isHtmx = c.req.header("HX-Request") === "true";
7807
+ if (isHtmx) {
7808
+ c.header("HX-Redirect", redirectUrl);
7809
+ return c.html(html.html`<div id="form-response"><p class="text-sm text-green-600">Login successful! Redirecting...</p></div>`);
7810
+ }
7589
7811
  return c.html(html.html`
7590
7812
  <div id="form-response">
7591
7813
  <div class="rounded-lg bg-green-100 dark:bg-lime-500/10 p-4 ring-1 ring-green-400 dark:ring-lime-500/20">
@@ -7615,14 +7837,14 @@ authRoutes.post(
7615
7837
  );
7616
7838
  authRoutes.post(
7617
7839
  "/seed-admin",
7618
- chunkIS54FPB7_cjs.rateLimit({ max: 10, windowMs: 60 * 1e3, keyPrefix: "seed-admin" }),
7840
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 10, windowMs: 60 * 1e3, keyPrefix: "seed-admin" }),
7619
7841
  async (c) => {
7620
7842
  try {
7621
7843
  const db = c.env.DB;
7622
- const rbac = new chunkLTZ5WYOK_cjs.RbacService(db);
7844
+ const rbac = new chunkJCBDXEY2_cjs.RbacService(db);
7623
7845
  const results = [];
7624
7846
  const upsertSeedUser = async (opts) => {
7625
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(opts.password);
7847
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(opts.password);
7626
7848
  const nowMs = Date.now();
7627
7849
  const nowSec = Math.floor(nowMs / 1e3);
7628
7850
  const existing = await db.prepare("SELECT id FROM auth_user WHERE email = ?").bind(opts.email).first();
@@ -7847,7 +8069,7 @@ authRoutes.post("/accept-invitation", async (c) => {
7847
8069
  if (invitationAge > maxAge) {
7848
8070
  return c.json({ error: "Invitation has expired" }, 400);
7849
8071
  }
7850
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(password);
8072
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(password);
7851
8073
  const updateStmt = db.prepare(`
7852
8074
  UPDATE auth_user SET
7853
8075
  password_hash = ?,
@@ -7864,8 +8086,8 @@ authRoutes.post("/accept-invitation", async (c) => {
7864
8086
  Date.now(),
7865
8087
  invitedUser.id
7866
8088
  ).run();
7867
- const tokenTtl = await chunkIS54FPB7_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
7868
- const authToken = await chunkIS54FPB7_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET, tokenTtl);
8089
+ const tokenTtl = await chunkE7E3NL3Y_cjs.getJwtExpirySecondsFromDb(c.env.DB, c.env);
8090
+ const authToken = await chunkE7E3NL3Y_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET, tokenTtl);
7869
8091
  cookie.setCookie(c, "auth_token", authToken, {
7870
8092
  httpOnly: true,
7871
8093
  secure: true,
@@ -7881,7 +8103,7 @@ authRoutes.post("/accept-invitation", async (c) => {
7881
8103
  });
7882
8104
  authRoutes.post(
7883
8105
  "/request-password-reset",
7884
- chunkIS54FPB7_cjs.rateLimit({ max: 3, windowMs: 15 * 60 * 1e3, keyPrefix: "password-reset" }),
8106
+ chunkE7E3NL3Y_cjs.rateLimit({ max: 3, windowMs: 15 * 60 * 1e3, keyPrefix: "password-reset" }),
7885
8107
  async (c) => {
7886
8108
  try {
7887
8109
  const formData = await c.req.formData();
@@ -8118,7 +8340,7 @@ authRoutes.post("/reset-password", async (c) => {
8118
8340
  if (Date.now() > user.password_reset_expires) {
8119
8341
  return c.json({ error: "Reset token has expired" }, 400);
8120
8342
  }
8121
- const newPasswordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(password);
8343
+ const newPasswordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(password);
8122
8344
  try {
8123
8345
  const historyStmt = db.prepare(`
8124
8346
  INSERT INTO auth_password_history (id, user_id, password_hash, created_at)
@@ -8332,7 +8554,7 @@ app.post("/test-seed-defaults", async (c) => {
8332
8554
  var test_cleanup_default = app;
8333
8555
 
8334
8556
  // src/plugins/available/easy-mdx/index.ts
8335
- chunk4WMZGHIC_cjs.definePlugin({
8557
+ chunkD6FWEYQE_cjs.definePlugin({
8336
8558
  id: "easy-mdx",
8337
8559
  version: "1.0.0",
8338
8560
  name: "EasyMDE Markdown Editor",
@@ -8532,7 +8754,7 @@ function getMDXEditorInitScript(config) {
8532
8754
  }
8533
8755
 
8534
8756
  // src/plugins/available/tinymce-plugin/index.ts
8535
- chunk4WMZGHIC_cjs.definePlugin({
8757
+ chunkD6FWEYQE_cjs.definePlugin({
8536
8758
  id: "tinymce-plugin",
8537
8759
  version: "1.0.0",
8538
8760
  name: "TinyMCE",
@@ -8802,7 +9024,7 @@ function getQuillCDN(version = "2.0.2") {
8802
9024
  </style>
8803
9025
  `;
8804
9026
  }
8805
- chunk4WMZGHIC_cjs.definePlugin({
9027
+ chunkD6FWEYQE_cjs.definePlugin({
8806
9028
  id: "quill-editor",
8807
9029
  version: "1.0.0",
8808
9030
  name: "Quill Editor",
@@ -8814,7 +9036,7 @@ chunk4WMZGHIC_cjs.definePlugin({
8814
9036
  });
8815
9037
 
8816
9038
  // src/templates/pages/admin-content-form.template.ts
8817
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
9039
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
8818
9040
  function renderContentFormPage(data, opts) {
8819
9041
  const isEdit = data.isEdit || !!data.id;
8820
9042
  const title = isEdit ? `Edit: ${data.title || "Content"}` : `New ${data.collection.display_name}`;
@@ -8896,8 +9118,8 @@ function renderContentFormPage(data, opts) {
8896
9118
  <!-- Form Content -->
8897
9119
  <div class="px-6 py-6">
8898
9120
  <div id="form-messages">
8899
- ${data.error ? chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
8900
- ${data.success ? chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
9121
+ ${data.error ? chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
9122
+ ${data.success ? chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
8901
9123
  </div>
8902
9124
 
8903
9125
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
@@ -9010,20 +9232,20 @@ function renderContentFormPage(data, opts) {
9010
9232
  <dl class="space-y-3 text-sm">
9011
9233
  <div>
9012
9234
  <dt class="text-zinc-500 dark:text-zinc-400">Created</dt>
9013
- <dd class="mt-1 text-zinc-950 dark:text-white">${data.created_at ? new Date(data.created_at).toLocaleDateString() : "Unknown"}</dd>
9235
+ <dd class="mt-1 text-zinc-950 dark:text-white">${data.created_at ? new Date(data.created_at * 1e3).toLocaleDateString() : "Unknown"}</dd>
9014
9236
  </div>
9015
9237
  <div>
9016
9238
  <dt class="text-zinc-500 dark:text-zinc-400">Last Modified</dt>
9017
- <dd class="mt-1 text-zinc-950 dark:text-white">${data.updated_at ? new Date(data.updated_at).toLocaleDateString() : "Unknown"}</dd>
9239
+ <dd class="mt-1 text-zinc-950 dark:text-white">${data.updated_at ? new Date(data.updated_at * 1e3).toLocaleDateString() : "Unknown"}</dd>
9018
9240
  </div>
9019
9241
  <div>
9020
9242
  <dt class="text-zinc-500 dark:text-zinc-400">Author</dt>
9021
- <dd class="mt-1 text-zinc-950 dark:text-white">${data.data?.author || "Unknown"}</dd>
9243
+ <dd class="mt-1 text-zinc-950 dark:text-white">${chunkMNWKYY5E_cjs.escapeHtml(String(data.data?.author || data.author_name || "Unknown"))}</dd>
9022
9244
  </div>
9023
9245
  ${data.published_at ? `
9024
9246
  <div>
9025
9247
  <dt class="text-zinc-500 dark:text-zinc-400">Published</dt>
9026
- <dd class="mt-1 text-zinc-950 dark:text-white">${new Date(data.published_at).toLocaleDateString()}</dd>
9248
+ <dd class="mt-1 text-zinc-950 dark:text-white">${new Date(data.published_at * 1e3).toLocaleDateString()}</dd>
9027
9249
  </div>
9028
9250
  ` : ""}
9029
9251
  </dl>
@@ -9097,33 +9319,44 @@ function renderContentFormPage(data, opts) {
9097
9319
  </a>
9098
9320
 
9099
9321
  <div class="flex items-center gap-x-3">
9100
- ${data.versioningEnabled ? `
9101
- <button
9102
- type="submit"
9103
- form="content-form"
9104
- name="action"
9105
- value="save"
9106
- 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"
9107
- >
9108
- <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
9109
- <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
9110
- </svg>
9111
- ${isEdit ? "Update" : "Save"}
9112
- </button>
9113
- ` : ""}
9322
+ <button
9323
+ type="submit"
9324
+ form="content-form"
9325
+ name="action"
9326
+ value="save"
9327
+ 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"
9328
+ >
9329
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
9330
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
9331
+ </svg>
9332
+ Save
9333
+ </button>
9334
+
9335
+ <button
9336
+ type="submit"
9337
+ form="content-form"
9338
+ name="action"
9339
+ value="save_and_close"
9340
+ class="inline-flex items-center justify-center gap-x-1.5 rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors shadow-sm"
9341
+ >
9342
+ <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
9343
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
9344
+ </svg>
9345
+ Save & close
9346
+ </button>
9114
9347
 
9115
- ${data.user?.role !== "viewer" ? `
9348
+ ${data.user?.role !== "viewer" && data.versioningEnabled ? `
9116
9349
  <button
9117
9350
  type="submit"
9118
9351
  form="content-form"
9119
9352
  name="action"
9120
9353
  value="save_and_publish"
9121
- class="inline-flex items-center justify-center gap-x-1.5 rounded-lg ${data.versioningEnabled ? "bg-lime-600 dark:bg-lime-500 hover:bg-lime-700 dark:hover:bg-lime-600" : "bg-zinc-950 dark:bg-white hover:bg-zinc-800 dark:hover:bg-zinc-100 text-white dark:text-zinc-950"} px-3.5 py-2.5 text-sm font-semibold text-white transition-colors shadow-sm"
9354
+ class="inline-flex items-center justify-center gap-x-1.5 rounded-lg bg-lime-600 dark:bg-lime-500 hover:bg-lime-700 dark:hover:bg-lime-600 px-3.5 py-2.5 text-sm font-semibold text-white transition-colors shadow-sm"
9122
9355
  >
9123
9356
  <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
9124
9357
  <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
9125
9358
  </svg>
9126
- ${data.versioningEnabled ? `${isEdit ? "Update" : "Save"} & Publish` : isEdit ? "Update" : "Save"}
9359
+ Publish
9127
9360
  </button>
9128
9361
  ` : ""}
9129
9362
  </div>
@@ -9133,7 +9366,7 @@ function renderContentFormPage(data, opts) {
9133
9366
  </div>
9134
9367
 
9135
9368
  <!-- Confirmation Dialogs -->
9136
- ${chunkE66UVONV_cjs.renderConfirmationDialog({
9369
+ ${chunkOUBUS74I_cjs.renderConfirmationDialog({
9137
9370
  id: "duplicate-content-confirm",
9138
9371
  title: "Duplicate Content",
9139
9372
  message: "Create a copy of this content?",
@@ -9144,7 +9377,7 @@ function renderContentFormPage(data, opts) {
9144
9377
  onConfirm: "performDuplicateContent()"
9145
9378
  })}
9146
9379
 
9147
- ${chunkE66UVONV_cjs.renderConfirmationDialog({
9380
+ ${chunkOUBUS74I_cjs.renderConfirmationDialog({
9148
9381
  id: "delete-content-confirm",
9149
9382
  title: "Delete Content",
9150
9383
  message: "Are you sure you want to delete this content? This action cannot be undone.",
@@ -9155,7 +9388,7 @@ function renderContentFormPage(data, opts) {
9155
9388
  onConfirm: `performDeleteContent('${data.id}')`
9156
9389
  })}
9157
9390
 
9158
- ${chunkE66UVONV_cjs.renderConfirmationDialog({
9391
+ ${chunkOUBUS74I_cjs.renderConfirmationDialog({
9159
9392
  id: "delete-repeater-item-confirm",
9160
9393
  title: "Delete Item",
9161
9394
  message: "Are you sure you want to delete this item? This action cannot be undone.",
@@ -9166,7 +9399,7 @@ function renderContentFormPage(data, opts) {
9166
9399
  onConfirm: "performRepeaterDelete()"
9167
9400
  })}
9168
9401
 
9169
- ${chunkE66UVONV_cjs.renderConfirmationDialog({
9402
+ ${chunkOUBUS74I_cjs.renderConfirmationDialog({
9170
9403
  id: "delete-block-confirm",
9171
9404
  title: "Delete Block",
9172
9405
  message: "Are you sure you want to delete this block? This action cannot be undone.",
@@ -9177,7 +9410,7 @@ function renderContentFormPage(data, opts) {
9177
9410
  onConfirm: "performRepeaterDelete()"
9178
9411
  })}
9179
9412
 
9180
- ${chunkE66UVONV_cjs.getConfirmationDialogScript()}
9413
+ ${chunkOUBUS74I_cjs.getConfirmationDialogScript()}
9181
9414
 
9182
9415
  ${data.lexicalEnabled ? getLexicalStyles() : "<!-- Lexical plugin not active -->"}
9183
9416
 
@@ -10263,11 +10496,11 @@ function renderContentFormPage(data, opts) {
10263
10496
  content: pageContent,
10264
10497
  version: data.version
10265
10498
  };
10266
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
10499
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
10267
10500
  }
10268
10501
 
10269
10502
  // src/templates/pages/admin-content-list.template.ts
10270
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
10503
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
10271
10504
  function renderContentListPage(data) {
10272
10505
  const urlParams = new URLSearchParams();
10273
10506
  if (data.modelName && data.modelName !== "all") urlParams.set("model", data.modelName);
@@ -10701,8 +10934,8 @@ function renderContentListPage(data) {
10701
10934
 
10702
10935
  <!-- Content List -->
10703
10936
  <div id="content-list">
10704
- ${chunkE66UVONV_cjs.renderTable(tableData)}
10705
- ${chunkE66UVONV_cjs.renderPagination(paginationData)}
10937
+ ${chunkOUBUS74I_cjs.renderTable(tableData)}
10938
+ ${chunkOUBUS74I_cjs.renderPagination(paginationData)}
10706
10939
  </div>
10707
10940
 
10708
10941
  </div>
@@ -10912,7 +11145,7 @@ function renderContentListPage(data) {
10912
11145
  </script>
10913
11146
 
10914
11147
  <!-- Confirmation Dialog for Bulk Actions -->
10915
- ${chunkE66UVONV_cjs.renderConfirmationDialog({
11148
+ ${chunkOUBUS74I_cjs.renderConfirmationDialog({
10916
11149
  id: "bulk-action-confirm",
10917
11150
  title: "Confirm Bulk Action",
10918
11151
  message: "Are you sure you want to perform this action? This operation will affect multiple items.",
@@ -10924,7 +11157,7 @@ function renderContentListPage(data) {
10924
11157
  })}
10925
11158
 
10926
11159
  <!-- Confirmation Dialog Script -->
10927
- ${chunkE66UVONV_cjs.getConfirmationDialogScript()}
11160
+ ${chunkOUBUS74I_cjs.getConfirmationDialogScript()}
10928
11161
 
10929
11162
  <!-- Advanced Search Modal -->
10930
11163
  <div id="advancedSearchModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
@@ -11221,7 +11454,7 @@ function renderContentListPage(data) {
11221
11454
  version: data.version,
11222
11455
  content: pageContent
11223
11456
  };
11224
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
11457
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
11225
11458
  }
11226
11459
 
11227
11460
  // src/routes/admin-content-field-types.ts
@@ -11264,9 +11497,9 @@ function parseFieldValue(field, formData, options = {}) {
11264
11497
  const { skipValidation = false } = options;
11265
11498
  const value = formData.get(field.field_name);
11266
11499
  const errors = [];
11267
- const blocksConfig = chunkIXUHXTHW_cjs.getBlocksFieldConfig(field.field_options);
11500
+ const blocksConfig = chunkDLTT7QZU_cjs.getBlocksFieldConfig(field.field_options);
11268
11501
  if (blocksConfig) {
11269
- const parsed = chunkIXUHXTHW_cjs.parseBlocksValue(value, blocksConfig);
11502
+ const parsed = chunkDLTT7QZU_cjs.parseBlocksValue(value, blocksConfig);
11270
11503
  if (!skipValidation && field.is_required && parsed.value.length === 0) {
11271
11504
  parsed.errors.push(`${field.field_label} is required`);
11272
11505
  }
@@ -11376,7 +11609,7 @@ function extractFieldData(fields, formData, options = {}) {
11376
11609
  }
11377
11610
  return { data, errors };
11378
11611
  }
11379
- adminContentRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
11612
+ adminContentRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
11380
11613
  async function getCollectionFields(db, collectionId) {
11381
11614
  console.log(`[getCollectionFields] Loading fields for collection: ${collectionId}`);
11382
11615
  const collectionStmt = db.prepare("SELECT schema, queryable_fields FROM document_types WHERE id = ?");
@@ -11403,7 +11636,7 @@ async function getCollectionFields(db, collectionId) {
11403
11636
  };
11404
11637
  });
11405
11638
  }
11406
- const codeCollections2 = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
11639
+ const codeCollections2 = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11407
11640
  const codeMatch = codeCollections2.find((c) => c.name === collectionId);
11408
11641
  if (codeMatch && codeMatch.schema?.properties) {
11409
11642
  console.log(`[getCollectionFields] DB doc type has no properties \u2014 using code collection schema`);
@@ -11435,7 +11668,7 @@ async function getCollectionFields(db, collectionId) {
11435
11668
  }
11436
11669
  }
11437
11670
  console.log(`[getCollectionFields] Not in database, checking code collections`);
11438
- const codeCollections = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
11671
+ const codeCollections = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11439
11672
  console.log(`[getCollectionFields] Found ${codeCollections.length} code collections`);
11440
11673
  const codeCollection = codeCollections.find((c) => c.name === collectionId);
11441
11674
  if (codeCollection && codeCollection.schema) {
@@ -11489,7 +11722,7 @@ async function getCollection(db, collectionId) {
11489
11722
  };
11490
11723
  }
11491
11724
  console.log(`[getCollection] Not in database, checking code collections`);
11492
- const codeCollections = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
11725
+ const codeCollections = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11493
11726
  const codeCollection = codeCollections.find((c) => c.name === collectionId);
11494
11727
  if (codeCollection) {
11495
11728
  console.log(`[getCollection] Found code collection: ${collectionId}`);
@@ -11508,7 +11741,7 @@ async function getCollection(db, collectionId) {
11508
11741
  }
11509
11742
  async function getDocBackingType(db, collectionName) {
11510
11743
  if (!collectionName) return null;
11511
- const dt = await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db).findById(collectionName);
11744
+ const dt = await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db).findById(collectionName);
11512
11745
  return dt && dt.isActive ? dt : null;
11513
11746
  }
11514
11747
  async function getCollectionByName(db, name) {
@@ -11525,7 +11758,7 @@ async function getCollectionByName(db, name) {
11525
11758
  };
11526
11759
  }
11527
11760
  console.log(`[getCollectionByName] Not in database, checking code collections`);
11528
- const codeCollections = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
11761
+ const codeCollections = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11529
11762
  const codeCollection = codeCollections.find((c) => c.name === name);
11530
11763
  if (codeCollection) {
11531
11764
  console.log(`[getCollectionByName] Found code collection: ${name}`);
@@ -11541,7 +11774,7 @@ async function getCollectionByName(db, name) {
11541
11774
  return null;
11542
11775
  }
11543
11776
  async function loadContentEditorFlags(db) {
11544
- const flags = { workflowEnabled: await chunkIS54FPB7_cjs.isPluginActive(db, "workflow") };
11777
+ const flags = { workflowEnabled: await chunkE7E3NL3Y_cjs.isPluginActive(db, "workflow") };
11545
11778
  const editors = [
11546
11779
  ["tinymce-plugin", "tinymce"],
11547
11780
  ["quill-editor", "quill"],
@@ -11549,10 +11782,10 @@ async function loadContentEditorFlags(db) {
11549
11782
  ["lexical-editor", "lexical"]
11550
11783
  ];
11551
11784
  for (const [plugin, key] of editors) {
11552
- const enabled = await chunkIS54FPB7_cjs.isPluginActive(db, plugin);
11785
+ const enabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, plugin);
11553
11786
  flags[`${key}Enabled`] = enabled;
11554
11787
  if (enabled) {
11555
- const ps = new chunkAT4ZQGEZ_cjs.PluginService(db);
11788
+ const ps = new chunkYAURVGUK_cjs.PluginService(db);
11556
11789
  const p = await ps.getPlugin(plugin);
11557
11790
  flags[`${key}Settings`] = p?.settings;
11558
11791
  }
@@ -11560,7 +11793,7 @@ async function loadContentEditorFlags(db) {
11560
11793
  return flags;
11561
11794
  }
11562
11795
  function makeDocService(db, docType, tenantId) {
11563
- return new chunkLTZ5WYOK_cjs.DocumentsService(db, {
11796
+ return new chunkJCBDXEY2_cjs.DocumentsService(db, {
11564
11797
  queryableFields: docType.queryableFields ?? [],
11565
11798
  typeSchemaVersion: docType.schemaVersion ?? 1,
11566
11799
  maxVersionsPerRoot: docType.settings?.maxVersionsPerRoot ?? 50,
@@ -11588,9 +11821,9 @@ adminContentRoutes.get("/", async (c) => {
11588
11821
  const offset = (page - 1) * limit;
11589
11822
  const collectionsStmt = db.prepare("SELECT id, name, display_name FROM document_types WHERE is_active = 1 ORDER BY display_name");
11590
11823
  const { results: collectionsResults } = await collectionsStmt.all();
11591
- const codeCollections = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
11824
+ const codeCollections = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11592
11825
  const codeCollectionMap = new Map(codeCollections.map((c2) => [c2.name, c2]));
11593
- const docTypeRegistry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
11826
+ const docTypeRegistry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
11594
11827
  const docTypes = (await docTypeRegistry.findAll()).filter((dt) => !dt.settings?.internal && !dt.isAuth);
11595
11828
  const visibleTypeIds = new Set(docTypes.map((dt) => dt.id));
11596
11829
  const allCollections = [
@@ -11602,7 +11835,7 @@ adminContentRoutes.get("/", async (c) => {
11602
11835
  ...allCollections.map((c2) => ({ name: c2.name, displayName: c2.displayName })),
11603
11836
  ...docTypes.filter((dt) => !collectionNames.has(dt.id)).map((dt) => ({ name: `doc:${dt.id}`, displayName: dt.displayName }))
11604
11837
  ];
11605
- const newContentCollections = await chunkAT4ZQGEZ_cjs.getVisibleCollections(db);
11838
+ const newContentCollections = await chunkYAURVGUK_cjs.getVisibleCollections(db);
11606
11839
  const docBackedCollection = !modelName.startsWith("doc:") && modelName !== "all" ? docTypes.find((dt) => dt.id === modelName) : void 0;
11607
11840
  const isDocModel = modelName.startsWith("doc:") || !!docBackedCollection;
11608
11841
  if (isDocModel) {
@@ -11625,6 +11858,14 @@ adminContentRoutes.get("/", async (c) => {
11625
11858
  docSql += " ORDER BY updated_at DESC LIMIT ? OFFSET ?";
11626
11859
  docParams.push(limit, offset);
11627
11860
  const { results: docRows } = await db.prepare(docSql).bind(...docParams).all();
11861
+ const docUserIds = [...new Set((docRows ?? []).map((r) => r.created_by).filter(Boolean))];
11862
+ const docAuthorMap = {};
11863
+ for (let i = 0; i < docUserIds.length; i += 99) {
11864
+ const chunk = docUserIds.slice(i, i + 99);
11865
+ const ph = chunk.map(() => "?").join(",");
11866
+ const { results: uRows } = await db.prepare(`SELECT id, email FROM auth_user WHERE id IN (${ph})`).bind(...chunk).all();
11867
+ for (const u of uRows ?? []) docAuthorMap[u.id] = u.email;
11868
+ }
11628
11869
  const statusBadgeCss = {
11629
11870
  published: "bg-green-50 dark:bg-green-500/10 text-green-700 dark:text-green-400 ring-1 ring-inset ring-green-600/20 dark:ring-green-500/20",
11630
11871
  draft: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-1 ring-inset ring-zinc-600/20 dark:ring-zinc-500/20"
@@ -11641,7 +11882,7 @@ adminContentRoutes.get("/", async (c) => {
11641
11882
  slug: row.slug || "",
11642
11883
  modelName: docType?.displayName ?? typeId,
11643
11884
  statusBadge: `<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ${css}">${label}</span>`,
11644
- authorName: row.created_by || "System",
11885
+ authorName: (row.created_by ? docAuthorMap[row.created_by] ?? row.created_by : null) || "System",
11645
11886
  // Document timestamps are stored in SECONDS (documents.ts), unlike legacy content rows which
11646
11887
  // store MILLISECONDS — hence the *1000 here (D23).
11647
11888
  formattedDate: new Date((row.updated_at ?? 0) * 1e3).toLocaleDateString(),
@@ -11689,8 +11930,8 @@ adminContentRoutes.get("/", async (c) => {
11689
11930
  SELECT d.root_id AS id, d.title AS title, d.slug AS slug,
11690
11931
  CASE WHEN d.is_published = 1 THEN 'published' ELSE 'draft' END AS status,
11691
11932
  d.updated_at * 1000 AS updated_at,
11692
- dt.display_name AS cdisplay, COALESCE(d.created_by, 'System') AS author_label
11693
- FROM documents d JOIN document_types dt ON dt.id = d.type_id
11933
+ dt.display_name AS cdisplay, COALESCE(u.email, d.created_by, 'System') AS author_label
11934
+ FROM documents d JOIN document_types dt ON dt.id = d.type_id LEFT JOIN auth_user u ON u.id = d.created_by
11694
11935
  WHERE ${docConds.join(" AND ")}
11695
11936
  ORDER BY updated_at DESC LIMIT ? OFFSET ?`;
11696
11937
  const { results: unionRows } = await db.prepare(unionSql).bind(...docParams, limit, offset).all();
@@ -11879,9 +12120,9 @@ adminContentRoutes.get("/new", async (c) => {
11879
12120
  const collectionsStmt = db2.prepare("SELECT id, name, display_name, description FROM document_types WHERE is_active = 1 ORDER BY display_name");
11880
12121
  const { results } = await collectionsStmt.all();
11881
12122
  const visibleTypeIds = new Set(
11882
- (await new chunkIS54FPB7_cjs.DocumentTypeRegistry(db2).findAll()).filter((dt) => !dt.settings?.internal && !dt.isAuth).map((dt) => dt.id)
12123
+ (await new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db2).findAll()).filter((dt) => !dt.settings?.internal && !dt.isAuth).map((dt) => dt.id)
11883
12124
  );
11884
- const codeCollections = await chunkAT4ZQGEZ_cjs.loadCollectionConfigs();
12125
+ const codeCollections = await chunkYAURVGUK_cjs.loadCollectionConfigs();
11885
12126
  const dbCollectionNames = new Set((results || []).map((r) => r.name));
11886
12127
  const allCollections = [
11887
12128
  ...codeCollections.filter((c2) => !dbCollectionNames.has(c2.name)).map((c2) => ({
@@ -11947,32 +12188,32 @@ adminContentRoutes.get("/new", async (c) => {
11947
12188
  return c.html(renderContentFormPage(formData2));
11948
12189
  }
11949
12190
  const fields = await getCollectionFields(db, collection.id);
11950
- const workflowEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "workflow");
11951
- const tinymceEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "tinymce-plugin");
12191
+ const workflowEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "workflow");
12192
+ const tinymceEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "tinymce-plugin");
11952
12193
  let tinymceSettings;
11953
12194
  if (tinymceEnabled) {
11954
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12195
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
11955
12196
  const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
11956
12197
  tinymceSettings = tinymcePlugin2?.settings;
11957
12198
  }
11958
- const quillEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "quill-editor");
12199
+ const quillEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "quill-editor");
11959
12200
  let quillSettings;
11960
12201
  if (quillEnabled) {
11961
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12202
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
11962
12203
  const quillPlugin = await pluginService.getPlugin("quill-editor");
11963
12204
  quillSettings = quillPlugin?.settings;
11964
12205
  }
11965
- const mdxeditorEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "easy-mdx");
12206
+ const mdxeditorEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "easy-mdx");
11966
12207
  let mdxeditorSettings;
11967
12208
  if (mdxeditorEnabled) {
11968
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12209
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
11969
12210
  const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
11970
12211
  mdxeditorSettings = mdxeditorPlugin?.settings;
11971
12212
  }
11972
- const lexicalEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "lexical-editor");
12213
+ const lexicalEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "lexical-editor");
11973
12214
  let lexicalSettings;
11974
12215
  if (lexicalEnabled) {
11975
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12216
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
11976
12217
  const lexicalPlugin = await pluginService.getPlugin("lexical-editor");
11977
12218
  lexicalSettings = lexicalPlugin?.settings;
11978
12219
  }
@@ -12031,6 +12272,13 @@ adminContentRoutes.get("/:id/edit", async (c) => {
12031
12272
  if (docType && dcoll) {
12032
12273
  const fields2 = await getCollectionFields(db, dcoll.id);
12033
12274
  const flags = await loadContentEditorFlags(db);
12275
+ let authorName;
12276
+ if (docRow.created_by) {
12277
+ const authorRow = await db.prepare("SELECT name, first_name, last_name, email FROM auth_user WHERE id = ?").bind(docRow.created_by).first();
12278
+ if (authorRow) {
12279
+ authorName = authorRow.name || [authorRow.first_name, authorRow.last_name].filter(Boolean).join(" ") || authorRow.email;
12280
+ }
12281
+ }
12034
12282
  const formData2 = {
12035
12283
  id: docRow.root_id,
12036
12284
  title: docRow.title,
@@ -12045,6 +12293,7 @@ adminContentRoutes.get("/:id/edit", async (c) => {
12045
12293
  isEdit: true,
12046
12294
  referrerParams,
12047
12295
  versioningEnabled: docType?.settings?.versioning === true,
12296
+ author_name: authorName,
12048
12297
  user: user ? { name: user.email, email: user.email, role: user.role } : void 0,
12049
12298
  version: c.get("appVersion"),
12050
12299
  ...flags
@@ -12100,32 +12349,32 @@ adminContentRoutes.get("/:id/edit", async (c) => {
12100
12349
  };
12101
12350
  const fields = await getCollectionFields(db, content.collection_id);
12102
12351
  const contentData = content.data ? JSON.parse(content.data) : {};
12103
- const workflowEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "workflow");
12104
- const tinymceEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "tinymce-plugin");
12352
+ const workflowEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "workflow");
12353
+ const tinymceEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "tinymce-plugin");
12105
12354
  let tinymceSettings;
12106
12355
  if (tinymceEnabled) {
12107
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12356
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
12108
12357
  const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
12109
12358
  tinymceSettings = tinymcePlugin2?.settings;
12110
12359
  }
12111
- const quillEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "quill-editor");
12360
+ const quillEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "quill-editor");
12112
12361
  let quillSettings;
12113
12362
  if (quillEnabled) {
12114
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12363
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
12115
12364
  const quillPlugin = await pluginService.getPlugin("quill-editor");
12116
12365
  quillSettings = quillPlugin?.settings;
12117
12366
  }
12118
- const mdxeditorEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "easy-mdx");
12367
+ const mdxeditorEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "easy-mdx");
12119
12368
  let mdxeditorSettings;
12120
12369
  if (mdxeditorEnabled) {
12121
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12370
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
12122
12371
  const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
12123
12372
  mdxeditorSettings = mdxeditorPlugin?.settings;
12124
12373
  }
12125
- const lexicalEnabled = await chunkIS54FPB7_cjs.isPluginActive(db, "lexical-editor");
12374
+ const lexicalEnabled = await chunkE7E3NL3Y_cjs.isPluginActive(db, "lexical-editor");
12126
12375
  let lexicalSettings;
12127
12376
  if (lexicalEnabled) {
12128
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
12377
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
12129
12378
  const lexicalPlugin = await pluginService.getPlugin("lexical-editor");
12130
12379
  lexicalSettings = lexicalPlugin?.settings;
12131
12380
  }
@@ -12204,6 +12453,7 @@ adminContentRoutes.post("/", async (c) => {
12204
12453
  const fields = await getCollectionFields(db, collectionId);
12205
12454
  const { data, errors } = extractFieldData(fields, formData);
12206
12455
  if (Object.keys(errors).length > 0) {
12456
+ const flags = await loadContentEditorFlags(db);
12207
12457
  const formDataWithErrors = {
12208
12458
  collection,
12209
12459
  fields,
@@ -12214,7 +12464,8 @@ adminContentRoutes.post("/", async (c) => {
12214
12464
  name: user.email,
12215
12465
  email: user.email,
12216
12466
  role: user.role
12217
- } : void 0
12467
+ } : void 0,
12468
+ ...flags
12218
12469
  };
12219
12470
  if (c.req.header("HX-Request") === "true") {
12220
12471
  c.header("HX-Retarget", "#content-form-page");
@@ -12248,6 +12499,9 @@ adminContentRoutes.post("/", async (c) => {
12248
12499
  }), user?.userId);
12249
12500
  const cache2 = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.content);
12250
12501
  await cache2.invalidate(`content:list:${collectionId}:*`);
12502
+ const apiCacheCreate = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
12503
+ await apiCacheCreate.invalidate("api:content-filtered:*");
12504
+ await apiCacheCreate.invalidate(`api:collection-content-filtered:${createDocType.id}:*`);
12251
12505
  const redirectUrl2 = `/admin/content/${doc.rootId}/edit?success=Content created successfully!`;
12252
12506
  return c.req.header("HX-Request") === "true" ? c.text("", 200, { "HX-Redirect": redirectUrl2 }) : c.redirect(redirectUrl2);
12253
12507
  }
@@ -12366,8 +12620,28 @@ adminContentRoutes.put("/:id", async (c) => {
12366
12620
  if (status2 === "published") await svc.publish(newDraft.id, user?.userId);
12367
12621
  else if (pub) await svc.unpublish(pub.id);
12368
12622
  await chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.content).invalidate(`content:list:*`);
12623
+ const apiCacheEdit = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
12624
+ await apiCacheEdit.invalidate("api:content-filtered:*");
12625
+ await apiCacheEdit.invalidate(`api:collection-content-filtered:${docRowU.type_id}:*`);
12626
+ const isHTMX2 = c.req.header("HX-Request") === "true";
12627
+ const referrerParams = formData.get("referrer_params");
12628
+ const listUrl = referrerParams ? `/admin/content?${referrerParams}` : `/admin/content?collection=${dcoll.id}`;
12629
+ if (action === "save_and_close") {
12630
+ return isHTMX2 ? c.text("", 200, { "HX-Redirect": listUrl }) : c.redirect(listUrl);
12631
+ }
12632
+ if (isHTMX2) {
12633
+ const label = status2 === "published" ? "Content published successfully!" : "Content saved successfully!";
12634
+ return c.html(html.html`<div id="form-messages">
12635
+ <div class="rounded-lg bg-green-50 dark:bg-green-900/20 p-4 mb-4 flex items-center gap-3 border border-green-200 dark:border-green-800">
12636
+ <svg class="h-5 w-5 text-green-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
12637
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
12638
+ </svg>
12639
+ <span class="text-sm font-medium text-green-800 dark:text-green-200">${label}</span>
12640
+ </div>
12641
+ </div>`);
12642
+ }
12369
12643
  const redirectUrl2 = `/admin/content/${id}/edit?success=Content updated successfully!`;
12370
- return c.req.header("HX-Request") === "true" ? c.text("", 200, { "HX-Redirect": redirectUrl2 }) : c.redirect(redirectUrl2);
12644
+ return c.redirect(redirectUrl2);
12371
12645
  }
12372
12646
  }
12373
12647
  return c.html(html.html`
@@ -12395,6 +12669,7 @@ adminContentRoutes.put("/:id", async (c) => {
12395
12669
  const fields = await getCollectionFields(db, existingContent.collection_id);
12396
12670
  const { data, errors } = extractFieldData(fields, formData);
12397
12671
  if (Object.keys(errors).length > 0) {
12672
+ const flags = await loadContentEditorFlags(db);
12398
12673
  const formDataWithErrors = {
12399
12674
  id,
12400
12675
  collection,
@@ -12407,7 +12682,8 @@ adminContentRoutes.put("/:id", async (c) => {
12407
12682
  name: user.email,
12408
12683
  email: user.email,
12409
12684
  role: user.role
12410
- } : void 0
12685
+ } : void 0,
12686
+ ...flags
12411
12687
  };
12412
12688
  if (c.req.header("HX-Request") === "true") {
12413
12689
  c.header("HX-Retarget", "#content-form-page");
@@ -12498,7 +12774,7 @@ adminContentRoutes.put("/:id", async (c) => {
12498
12774
  `);
12499
12775
  }
12500
12776
  });
12501
- adminContentRoutes.post("/preview", chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
12777
+ adminContentRoutes.post("/preview", chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
12502
12778
  try {
12503
12779
  const formData = await c.req.formData();
12504
12780
  const collectionId = formData.get("collection_id");
@@ -12759,8 +13035,8 @@ adminContentRoutes.post("/bulk-action", async (c) => {
12759
13035
  }
12760
13036
  await cache.invalidate("content:list:*");
12761
13037
  const apiCache = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
12762
- await apiCache.invalidate("content-filtered:*");
12763
- await apiCache.invalidate("collection-content-filtered:*");
13038
+ await apiCache.invalidate("api:content-filtered:*");
13039
+ await apiCache.invalidate("api:collection-content-filtered:*");
12764
13040
  return c.json({ success: true, count: ids.length });
12765
13041
  } catch (error) {
12766
13042
  console.error("Bulk action error:", error);
@@ -12778,6 +13054,9 @@ adminContentRoutes.delete("/:id", async (c) => {
12778
13054
  const now2 = Math.floor(Date.now() / 1e3);
12779
13055
  await db.prepare("UPDATE documents SET deleted_at = ?, updated_at = ? WHERE root_id = ? AND tenant_id = ?").bind(now2, now2, id, tenantId).run();
12780
13056
  await chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.content).invalidate("content:list:*");
13057
+ const apiCacheDel = chunk36Z3AKX3_cjs.getCacheService(chunk36Z3AKX3_cjs.CACHE_CONFIGS.api);
13058
+ await apiCacheDel.invalidate("api:content-filtered:*");
13059
+ await apiCacheDel.invalidate(`api:collection-content-filtered:${docDel.type_id}:*`);
12781
13060
  return c.html(`
12782
13061
  <div id="content-list" hx-get="/admin/content?model=${docDel.type_id}" hx-trigger="load" hx-swap="outerHTML">
12783
13062
  <div class="flex items-center justify-center p-8"><span class="text-zinc-500">Deleting\u2026</span></div>
@@ -12817,7 +13096,7 @@ adminContentRoutes.delete("/:id", async (c) => {
12817
13096
  return c.json({ success: false, error: "Failed to delete content" }, 500);
12818
13097
  }
12819
13098
  });
12820
- adminContentRoutes.get("/:id/version/:version/preview", chunkIS54FPB7_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
13099
+ adminContentRoutes.get("/:id/version/:version/preview", chunkE7E3NL3Y_cjs.requireRole(["admin", "editor", "author"]), async (c) => {
12821
13100
  try {
12822
13101
  const id = c.req.param("id");
12823
13102
  const version = parseInt(c.req.param("version") || "0");
@@ -12913,9 +13192,9 @@ function parseDocFormData(formData, queryableFields = []) {
12913
13192
  return { title, slug, data };
12914
13193
  }
12915
13194
  async function getDocService(db, typeId, tenantId) {
12916
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
13195
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
12917
13196
  const docType = await registry.findById(typeId);
12918
- const svc = new chunkLTZ5WYOK_cjs.DocumentsService(db, {
13197
+ const svc = new chunkJCBDXEY2_cjs.DocumentsService(db, {
12919
13198
  queryableFields: docType?.queryableFields ?? [],
12920
13199
  typeSchemaVersion: docType?.schemaVersion ?? 1,
12921
13200
  maxVersionsPerRoot: docType?.settings?.maxVersionsPerRoot ?? 50,
@@ -12926,10 +13205,10 @@ async function getDocService(db, typeId, tenantId) {
12926
13205
  }
12927
13206
  adminContentRoutes.get("/documents/:typeId/new", async (c) => {
12928
13207
  const { typeId } = c.req.param();
12929
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(c.env.DB);
13208
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(c.env.DB);
12930
13209
  const docType = await registry.findById(typeId);
12931
13210
  if (!docType) return c.html("<p>Unknown document type.</p>", 404);
12932
- return c.html(chunkJMJM7DMM_cjs.renderDocumentFormPage({ docType, isEdit: false, user: userCtx(c) }));
13211
+ return c.html(chunkTT3TQSCZ_cjs.renderDocumentFormPage({ docType, isEdit: false, user: userCtx(c) }));
12933
13212
  });
12934
13213
  adminContentRoutes.post("/documents/:typeId/new", async (c) => {
12935
13214
  const { typeId } = c.req.param();
@@ -12951,17 +13230,17 @@ adminContentRoutes.post("/documents/:typeId/new", async (c) => {
12951
13230
  }), user?.userId);
12952
13231
  return c.redirect(`/admin/content/documents/${typeId}/${doc.rootId}/edit?message=Created+successfully`);
12953
13232
  } catch (err) {
12954
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(c.env.DB);
13233
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(c.env.DB);
12955
13234
  const docType = await registry.findById(typeId);
12956
13235
  if (!docType) return c.html("<p>Unknown document type.</p>", 404);
12957
- return c.html(chunkJMJM7DMM_cjs.renderDocumentFormPage({ docType, isEdit: false, user: userCtx(c), message: err?.message ?? "Failed to create", messageType: "error" }));
13236
+ return c.html(chunkTT3TQSCZ_cjs.renderDocumentFormPage({ docType, isEdit: false, user: userCtx(c), message: err?.message ?? "Failed to create", messageType: "error" }));
12958
13237
  }
12959
13238
  });
12960
13239
  adminContentRoutes.get("/documents/:typeId/:rootId/edit", async (c) => {
12961
13240
  const { typeId, rootId } = c.req.param();
12962
13241
  const db = c.env.DB;
12963
13242
  const message = c.req.query("message");
12964
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
13243
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
12965
13244
  const docType = await registry.findById(typeId);
12966
13245
  if (!docType) return c.html("<p>Unknown document type.</p>", 404);
12967
13246
  const tenantId = reqTenant(c);
@@ -13008,7 +13287,7 @@ adminContentRoutes.get("/documents/:typeId/:rootId/edit", async (c) => {
13008
13287
  createdAt: draftRow.created_at,
13009
13288
  updatedAt: draftRow.updated_at
13010
13289
  };
13011
- return c.html(chunkJMJM7DMM_cjs.renderDocumentFormPage({ docType, doc, publishedDoc, isEdit: true, message, user: userCtx(c), versioningEnabled: docType?.settings?.versioning === true }));
13290
+ return c.html(chunkTT3TQSCZ_cjs.renderDocumentFormPage({ docType, doc, publishedDoc, isEdit: true, message, user: userCtx(c), versioningEnabled: docType?.settings?.versioning === true }));
13012
13291
  });
13013
13292
  adminContentRoutes.post("/documents/:typeId/:rootId", async (c) => {
13014
13293
  const { typeId, rootId } = c.req.param();
@@ -13073,12 +13352,12 @@ adminContentRoutes.post("/documents/:typeId/:documentId/delete", async (c) => {
13073
13352
  adminContentRoutes.get("/documents/:typeId/:rootId/versions", async (c) => {
13074
13353
  const { typeId, rootId } = c.req.param();
13075
13354
  const db = c.env.DB;
13076
- const { renderVersionHistoryFragment } = await import('./admin-documents-form.template-YNAVMJCV.cjs');
13077
- const registry = new chunkIS54FPB7_cjs.DocumentTypeRegistry(db);
13355
+ const { renderVersionHistoryFragment } = await import('./admin-documents-form.template-UWKGVAMW.cjs');
13356
+ const registry = new chunkE7E3NL3Y_cjs.DocumentTypeRegistry(db);
13078
13357
  const docType = await registry.findById(typeId);
13079
13358
  if (!docType) return c.html("<div>Unknown type.</div>", 404);
13080
13359
  const result = await db.prepare(
13081
- "SELECT id, version_number, is_current_draft, is_published, status, updated_at, created_by FROM documents WHERE root_id = ? AND tenant_id = ? ORDER BY version_number DESC LIMIT 50"
13360
+ "SELECT d.id, d.version_number, d.is_current_draft, d.is_published, d.status, d.updated_at, COALESCE(u.email, d.created_by) AS created_by FROM documents d LEFT JOIN auth_user u ON u.id = d.created_by WHERE d.root_id = ? AND d.tenant_id = ? ORDER BY d.version_number DESC LIMIT 50"
13082
13361
  ).bind(rootId, reqTenant(c)).all();
13083
13362
  const versions = (result.results ?? []).map((r) => ({
13084
13363
  id: r.id,
@@ -13094,7 +13373,7 @@ adminContentRoutes.get("/documents/:typeId/:rootId/versions", async (c) => {
13094
13373
  var admin_content_default = adminContentRoutes;
13095
13374
 
13096
13375
  // src/templates/pages/admin-profile.template.ts
13097
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
13376
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
13098
13377
  function renderAvatarImage(avatarUrl, firstName, lastName) {
13099
13378
  return `<div id="avatar-image-container" class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-cyan-400 to-purple-400 flex items-center justify-center ring-4 ring-zinc-950/5 dark:ring-white/10">
13100
13379
  ${avatarUrl ? `<img src="${avatarUrl}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${firstName.charAt(0)}${lastName.charAt(0)}</span>`}
@@ -13114,8 +13393,8 @@ function renderProfilePage(data) {
13114
13393
  </div>
13115
13394
 
13116
13395
  <!-- Alert Messages -->
13117
- ${data.error ? chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
13118
- ${data.success ? chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
13396
+ ${data.error ? chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
13397
+ ${data.success ? chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
13119
13398
 
13120
13399
  <!-- Profile Form -->
13121
13400
  <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
@@ -13492,7 +13771,7 @@ function renderProfilePage(data) {
13492
13771
  version: data.version,
13493
13772
  content: pageContent
13494
13773
  };
13495
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
13774
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
13496
13775
  }
13497
13776
 
13498
13777
  // src/templates/pages/admin-activity-logs.template.ts
@@ -13702,7 +13981,7 @@ function renderActivityLogsPage(data) {
13702
13981
  user: data.user,
13703
13982
  content: pageContent
13704
13983
  };
13705
- return chunkE66UVONV_cjs.renderAdminLayout(layoutData);
13984
+ return chunkOUBUS74I_cjs.renderAdminLayout(layoutData);
13706
13985
  }
13707
13986
  function getActionBadgeClass(action) {
13708
13987
  if (action.includes("login") || action.includes("logout")) {
@@ -13722,7 +14001,7 @@ function formatAction(action) {
13722
14001
  }
13723
14002
 
13724
14003
  // src/templates/pages/admin-user-edit.template.ts
13725
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
14004
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
13726
14005
 
13727
14006
  // src/templates/components/confirmation-dialog.template.ts
13728
14007
  function renderConfirmationDialog2(options) {
@@ -13918,8 +14197,8 @@ function renderUserEditPage(data) {
13918
14197
 
13919
14198
  <!-- Alert Messages -->
13920
14199
  <div id="form-messages" class="mb-6 empty:mb-0">
13921
- ${data.error ? chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
13922
- ${data.success ? chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
14200
+ ${data.error ? chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
14201
+ ${data.success ? chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
13923
14202
  </div>
13924
14203
 
13925
14204
  <!-- User Edit Form -->
@@ -14241,11 +14520,11 @@ function renderUserEditPage(data) {
14241
14520
  user: data.user,
14242
14521
  content: pageContent
14243
14522
  };
14244
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
14523
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
14245
14524
  }
14246
14525
 
14247
14526
  // src/templates/pages/admin-user-new.template.ts
14248
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
14527
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
14249
14528
  function renderUserNewPage(data) {
14250
14529
  const pageContent = `
14251
14530
  <div>
@@ -14284,8 +14563,8 @@ function renderUserNewPage(data) {
14284
14563
 
14285
14564
  <!-- Alert Messages -->
14286
14565
  <div id="form-messages">
14287
- ${data.error ? chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
14288
- ${data.success ? chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
14566
+ ${data.error ? chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
14567
+ ${data.success ? chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
14289
14568
  </div>
14290
14569
 
14291
14570
  <!-- User New Form -->
@@ -14515,11 +14794,11 @@ function renderUserNewPage(data) {
14515
14794
  user: data.user,
14516
14795
  content: pageContent
14517
14796
  };
14518
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
14797
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
14519
14798
  }
14520
14799
 
14521
14800
  // src/templates/pages/admin-users-list.template.ts
14522
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
14801
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
14523
14802
  function renderUsersListPage(data) {
14524
14803
  const columns = [
14525
14804
  {
@@ -14675,8 +14954,8 @@ function renderUsersListPage(data) {
14675
14954
  </div>
14676
14955
 
14677
14956
  <!-- Alert Messages -->
14678
- ${data.error ? chunkE66UVONV_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
14679
- ${data.success ? chunkE66UVONV_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
14957
+ ${data.error ? chunkOUBUS74I_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
14958
+ ${data.success ? chunkOUBUS74I_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
14680
14959
 
14681
14960
  <!-- Stats -->
14682
14961
  <div class="mb-6">
@@ -14853,10 +15132,10 @@ function renderUsersListPage(data) {
14853
15132
  </div>
14854
15133
 
14855
15134
  <!-- Users Table -->
14856
- ${chunkE66UVONV_cjs.renderTable(tableData)}
15135
+ ${chunkOUBUS74I_cjs.renderTable(tableData)}
14857
15136
 
14858
15137
  <!-- Pagination -->
14859
- ${data.pagination ? chunkE66UVONV_cjs.renderPagination(data.pagination) : ""}
15138
+ ${data.pagination ? chunkOUBUS74I_cjs.renderPagination(data.pagination) : ""}
14860
15139
  </div>
14861
15140
 
14862
15141
  <script>
@@ -14927,7 +15206,7 @@ function renderUsersListPage(data) {
14927
15206
  version: data.version,
14928
15207
  content: pageContent
14929
15208
  };
14930
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
15209
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
14931
15210
  }
14932
15211
 
14933
15212
  // src/plugins/core-plugins/multi-tenant-plugin/services/tenant-service.ts
@@ -15007,7 +15286,7 @@ var TenantService = class {
15007
15286
  SET name = ?, domain = ?, status = ?, notes = ?, updated_at = ?
15008
15287
  WHERE slug = ?
15009
15288
  `).bind(name, domain, status, notes, now, slug).run();
15010
- chunkAT4ZQGEZ_cjs.invalidateTenantCache();
15289
+ chunkYAURVGUK_cjs.invalidateTenantCache();
15011
15290
  const updated = await this.getTenantBySlug(slug);
15012
15291
  if (!updated) throw new Error("Tenant not found");
15013
15292
  return updated;
@@ -15025,7 +15304,7 @@ var TenantService = class {
15025
15304
  throw new Error(`Tenant '${slug}' still owns ${docCount} document(s). Delete or migrate its content first.`);
15026
15305
  }
15027
15306
  await this.db.prepare(`DELETE FROM auth_tenant WHERE slug = ?`).bind(slug).run();
15028
- chunkAT4ZQGEZ_cjs.invalidateTenantCache();
15307
+ chunkYAURVGUK_cjs.invalidateTenantCache();
15029
15308
  }
15030
15309
  // ─── Membership (auth_tenant_member) ───────────────────────────────────────
15031
15310
  // Tenant resolution and the admin switcher are gated on membership: an authed user may only
@@ -15269,7 +15548,7 @@ var TenantService = class {
15269
15548
  INSERT INTO auth_tenant (id, name, slug, status, domain, notes, metadata, created_at, updated_at)
15270
15549
  VALUES (?, ?, ?, 'active', ?, ?, '{}', ?, ?)
15271
15550
  `).bind(id, input.name, input.slug, input.domain, input.notes, now, now).run();
15272
- chunkAT4ZQGEZ_cjs.invalidateTenantCache();
15551
+ chunkYAURVGUK_cjs.invalidateTenantCache();
15273
15552
  const created = await this.getTenantBySlug(input.slug);
15274
15553
  if (!created) throw new Error("Failed to create tenant");
15275
15554
  return created;
@@ -15293,14 +15572,14 @@ function mapRowToTenant(row) {
15293
15572
 
15294
15573
  // src/routes/admin-users.ts
15295
15574
  var userRoutes = new hono.Hono();
15296
- userRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
15297
- userRoutes.use("/users/*", chunkIS54FPB7_cjs.requireRole(["admin"]));
15298
- userRoutes.use("/users", chunkIS54FPB7_cjs.requireRole(["admin"]));
15299
- userRoutes.use("/invite-user", chunkIS54FPB7_cjs.requireRole(["admin"]));
15300
- userRoutes.use("/resend-invitation/*", chunkIS54FPB7_cjs.requireRole(["admin"]));
15301
- userRoutes.use("/cancel-invitation/*", chunkIS54FPB7_cjs.requireRole(["admin"]));
15302
- userRoutes.use("/activity-logs", chunkIS54FPB7_cjs.requireRole(["admin"]));
15303
- userRoutes.use("/activity-logs/*", chunkIS54FPB7_cjs.requireRole(["admin"]));
15575
+ userRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
15576
+ userRoutes.use("/users/*", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15577
+ userRoutes.use("/users", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15578
+ userRoutes.use("/invite-user", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15579
+ userRoutes.use("/resend-invitation/*", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15580
+ userRoutes.use("/cancel-invitation/*", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15581
+ userRoutes.use("/activity-logs", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15582
+ userRoutes.use("/activity-logs/*", chunkE7E3NL3Y_cjs.requireRole(["admin"]));
15304
15583
  userRoutes.get("/", (c) => {
15305
15584
  return c.redirect("/admin/dashboard");
15306
15585
  });
@@ -15328,12 +15607,10 @@ var LANGUAGES = [
15328
15607
  { value: "ko", label: "Korean" },
15329
15608
  { value: "zh", label: "Chinese" }
15330
15609
  ];
15331
- var ROLES = [
15332
- { value: "admin", label: "Administrator" },
15333
- { value: "editor", label: "Editor" },
15334
- { value: "author", label: "Author" },
15335
- { value: "viewer", label: "Viewer" }
15336
- ];
15610
+ async function fetchRoleOptions(db) {
15611
+ const roles = await new chunkJCBDXEY2_cjs.RbacService(db).getRoles();
15612
+ return roles.map((r) => ({ value: r.id, label: r.display_name }));
15613
+ }
15337
15614
  userRoutes.get("/profile", async (c) => {
15338
15615
  const user = c.get("user");
15339
15616
  const db = c.env.DB;
@@ -15411,7 +15688,7 @@ userRoutes.put("/profile", async (c) => {
15411
15688
  const language = formData.get("language")?.toString() || "en";
15412
15689
  const emailNotifications = formData.get("email_notifications") === "1";
15413
15690
  if (!firstName || !lastName || !email) {
15414
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15691
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15415
15692
  type: "error",
15416
15693
  message: "First name, last name, and email are required.",
15417
15694
  dismissible: true
@@ -15419,7 +15696,7 @@ userRoutes.put("/profile", async (c) => {
15419
15696
  }
15420
15697
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
15421
15698
  if (!emailRegex.test(email)) {
15422
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15699
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15423
15700
  type: "error",
15424
15701
  message: "Please enter a valid email address.",
15425
15702
  dismissible: true
@@ -15431,7 +15708,7 @@ userRoutes.put("/profile", async (c) => {
15431
15708
  `);
15432
15709
  const existingUser = await checkStmt.bind(email, user.userId).first();
15433
15710
  if (existingUser) {
15434
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15711
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15435
15712
  type: "error",
15436
15713
  message: "Email is already taken by another user.",
15437
15714
  dismissible: true
@@ -15463,11 +15740,11 @@ userRoutes.put("/profile", async (c) => {
15463
15740
  const validation = validateCustomData(sanitized, profileConfig);
15464
15741
  if (!validation.valid) {
15465
15742
  const errorMessages = Object.values(validation.errors).join(", ");
15466
- return c.html(chunkJMJM7DMM_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
15743
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
15467
15744
  }
15468
15745
  await saveCustomData(db, user.userId, sanitized);
15469
15746
  }
15470
- await chunkIS54FPB7_cjs.logActivity(
15747
+ await chunkE7E3NL3Y_cjs.logActivity(
15471
15748
  db,
15472
15749
  user.userId,
15473
15750
  "profile.update",
@@ -15477,14 +15754,14 @@ userRoutes.put("/profile", async (c) => {
15477
15754
  c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
15478
15755
  c.req.header("user-agent")
15479
15756
  );
15480
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15757
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15481
15758
  type: "success",
15482
15759
  message: "Profile updated successfully!",
15483
15760
  dismissible: true
15484
15761
  }));
15485
15762
  } catch (error) {
15486
15763
  console.error("Profile update error:", error);
15487
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15764
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15488
15765
  type: "error",
15489
15766
  message: "Failed to update profile. Please try again.",
15490
15767
  dismissible: true
@@ -15498,7 +15775,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15498
15775
  const formData = await c.req.formData();
15499
15776
  const avatarFile = formData.get("avatar");
15500
15777
  if (!avatarFile || typeof avatarFile === "string" || !avatarFile.name) {
15501
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15778
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15502
15779
  type: "error",
15503
15780
  message: "Please select an image file.",
15504
15781
  dismissible: true
@@ -15506,7 +15783,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15506
15783
  }
15507
15784
  const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
15508
15785
  if (!allowedTypes.includes(avatarFile.type)) {
15509
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15786
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15510
15787
  type: "error",
15511
15788
  message: "Please upload a valid image file (JPEG, PNG, GIF, or WebP).",
15512
15789
  dismissible: true
@@ -15514,7 +15791,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15514
15791
  }
15515
15792
  const maxSize = 5 * 1024 * 1024;
15516
15793
  if (avatarFile.size > maxSize) {
15517
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15794
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15518
15795
  type: "error",
15519
15796
  message: "Image file must be smaller than 5MB.",
15520
15797
  dismissible: true
@@ -15530,7 +15807,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15530
15807
  SELECT first_name, last_name FROM auth_user WHERE id = ?
15531
15808
  `);
15532
15809
  const userData = await userStmt.bind(user.userId).first();
15533
- await chunkIS54FPB7_cjs.logActivity(
15810
+ await chunkE7E3NL3Y_cjs.logActivity(
15534
15811
  db,
15535
15812
  user.userId,
15536
15813
  "profile.avatar_update",
@@ -15540,7 +15817,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15540
15817
  c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
15541
15818
  c.req.header("user-agent")
15542
15819
  );
15543
- const alertHtml = chunkJMJM7DMM_cjs.renderAlert({
15820
+ const alertHtml = chunkTT3TQSCZ_cjs.renderAlert({
15544
15821
  type: "success",
15545
15822
  message: "Profile picture updated successfully!",
15546
15823
  dismissible: true
@@ -15554,7 +15831,7 @@ userRoutes.post("/profile/avatar", async (c) => {
15554
15831
  return c.html(alertHtml + avatarImageWithOob);
15555
15832
  } catch (error) {
15556
15833
  console.error("Avatar upload error:", error);
15557
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15834
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15558
15835
  type: "error",
15559
15836
  message: "Failed to upload profile picture. Please try again.",
15560
15837
  dismissible: true
@@ -15570,21 +15847,21 @@ userRoutes.post("/profile/password", async (c) => {
15570
15847
  const newPassword = formData.get("new_password")?.toString() || "";
15571
15848
  const confirmPassword = formData.get("confirm_password")?.toString() || "";
15572
15849
  if (!currentPassword || !newPassword || !confirmPassword) {
15573
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15850
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15574
15851
  type: "error",
15575
15852
  message: "All password fields are required.",
15576
15853
  dismissible: true
15577
15854
  }));
15578
15855
  }
15579
15856
  if (newPassword !== confirmPassword) {
15580
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15857
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15581
15858
  type: "error",
15582
15859
  message: "New passwords do not match.",
15583
15860
  dismissible: true
15584
15861
  }));
15585
15862
  }
15586
15863
  if (newPassword.length < 8) {
15587
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15864
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15588
15865
  type: "error",
15589
15866
  message: "New password must be at least 8 characters long.",
15590
15867
  dismissible: true
@@ -15595,21 +15872,21 @@ userRoutes.post("/profile/password", async (c) => {
15595
15872
  `);
15596
15873
  const userData = await userStmt.bind(user.userId).first();
15597
15874
  if (!userData) {
15598
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15875
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15599
15876
  type: "error",
15600
15877
  message: "User not found.",
15601
15878
  dismissible: true
15602
15879
  }));
15603
15880
  }
15604
- const validPassword = await chunkIS54FPB7_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
15881
+ const validPassword = await chunkE7E3NL3Y_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
15605
15882
  if (!validPassword) {
15606
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15883
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15607
15884
  type: "error",
15608
15885
  message: "Current password is incorrect.",
15609
15886
  dismissible: true
15610
15887
  }));
15611
15888
  }
15612
- const newPasswordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(newPassword);
15889
+ const newPasswordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(newPassword);
15613
15890
  const historyStmt = db.prepare(`
15614
15891
  INSERT INTO auth_password_history (id, user_id, password_hash, created_at)
15615
15892
  VALUES (?, ?, ?, ?)
@@ -15625,7 +15902,7 @@ userRoutes.post("/profile/password", async (c) => {
15625
15902
  WHERE id = ?
15626
15903
  `);
15627
15904
  await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
15628
- await chunkIS54FPB7_cjs.logActivity(
15905
+ await chunkE7E3NL3Y_cjs.logActivity(
15629
15906
  db,
15630
15907
  user.userId,
15631
15908
  "profile.password_change",
@@ -15635,14 +15912,14 @@ userRoutes.post("/profile/password", async (c) => {
15635
15912
  c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
15636
15913
  c.req.header("user-agent")
15637
15914
  );
15638
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15915
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15639
15916
  type: "success",
15640
15917
  message: "Password updated successfully!",
15641
15918
  dismissible: true
15642
15919
  }));
15643
15920
  } catch (error) {
15644
15921
  console.error("Password change error:", error);
15645
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
15922
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15646
15923
  type: "error",
15647
15924
  message: "Failed to update password. Please try again.",
15648
15925
  dismissible: true
@@ -15692,7 +15969,7 @@ userRoutes.get("/users", async (c) => {
15692
15969
  `);
15693
15970
  const countResult = await countStmt.bind(...params).first();
15694
15971
  const totalUsers = countResult?.total || 0;
15695
- await chunkIS54FPB7_cjs.logActivity(
15972
+ await chunkE7E3NL3Y_cjs.logActivity(
15696
15973
  db,
15697
15974
  user.userId,
15698
15975
  "users.list_view",
@@ -15763,7 +16040,7 @@ userRoutes.get("/users", async (c) => {
15763
16040
  if (isApiRequest) {
15764
16041
  return c.json({ error: "Failed to load users" }, 500);
15765
16042
  }
15766
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16043
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15767
16044
  type: "error",
15768
16045
  message: "Failed to load users. Please try again.",
15769
16046
  dismissible: true
@@ -15782,8 +16059,9 @@ userRoutes.get("/users/new", async (c) => {
15782
16059
  const regFieldNames = profileConfig?.registrationFields ?? [];
15783
16060
  const regFields = profileConfig && upRow ? regFieldNames.length > 0 ? profileConfig.fields.filter((f) => regFieldNames.includes(f.name)) : profileConfig.fields : [];
15784
16061
  const registrationFieldsHtml = regFields.length > 0 ? renderCustomProfileSection({ fields: regFields }, {}) : void 0;
16062
+ const roles = await fetchRoleOptions(db);
15785
16063
  const pageData = {
15786
- roles: ROLES,
16064
+ roles,
15787
16065
  registrationFieldsHtml,
15788
16066
  user: {
15789
16067
  name: user.email.split("@")[0] || user.email,
@@ -15794,7 +16072,7 @@ userRoutes.get("/users/new", async (c) => {
15794
16072
  return c.html(renderUserNewPage(pageData));
15795
16073
  } catch (error) {
15796
16074
  console.error("User new page error:", error);
15797
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16075
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15798
16076
  type: "error",
15799
16077
  message: "Failed to load user creation page. Please try again.",
15800
16078
  dismissible: true
@@ -15811,9 +16089,9 @@ userRoutes.post("/users/new", async (c) => {
15811
16089
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
15812
16090
  const phone = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
15813
16091
  const bio = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
15814
- const roleInput = formData.get("role")?.toString() || "viewer";
15815
- const validRoles = ["admin", "editor", "author", "viewer"];
15816
- const role = validRoles.includes(roleInput) ? roleInput : "viewer";
16092
+ const roleInput = formData.get("role")?.toString() || "";
16093
+ const validRoles = (await new chunkJCBDXEY2_cjs.RbacService(db).getRoles()).map((r) => r.id);
16094
+ const role = validRoles.includes(roleInput) ? roleInput : validRoles[0] ?? "admin";
15817
16095
  const password = formData.get("password")?.toString() || "";
15818
16096
  const confirmPassword = formData.get("confirm_password")?.toString() || "";
15819
16097
  const isActive = formData.get("is_active") === "1";
@@ -15828,12 +16106,12 @@ userRoutes.post("/users/new", async (c) => {
15828
16106
  const validation = validateCustomData(sanitized, profileConfig);
15829
16107
  if (!validation.valid) {
15830
16108
  const errorMessages = Object.values(validation.errors).join(", ");
15831
- return c.html(chunkJMJM7DMM_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
16109
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
15832
16110
  }
15833
16111
  sanitizedRegistrationCustom = sanitized;
15834
16112
  }
15835
16113
  if (!firstName || !lastName || !email || !password) {
15836
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16114
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15837
16115
  type: "error",
15838
16116
  message: "First name, last name, email, and password are required.",
15839
16117
  dismissible: true
@@ -15841,21 +16119,21 @@ userRoutes.post("/users/new", async (c) => {
15841
16119
  }
15842
16120
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
15843
16121
  if (!emailRegex.test(email)) {
15844
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16122
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15845
16123
  type: "error",
15846
16124
  message: "Please enter a valid email address.",
15847
16125
  dismissible: true
15848
16126
  }));
15849
16127
  }
15850
16128
  if (password.length < 8) {
15851
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16129
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15852
16130
  type: "error",
15853
16131
  message: "Password must be at least 8 characters long.",
15854
16132
  dismissible: true
15855
16133
  }));
15856
16134
  }
15857
16135
  if (password !== confirmPassword) {
15858
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16136
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15859
16137
  type: "error",
15860
16138
  message: "Passwords do not match.",
15861
16139
  dismissible: true
@@ -15867,13 +16145,13 @@ userRoutes.post("/users/new", async (c) => {
15867
16145
  `);
15868
16146
  const existingUser = await checkStmt.bind(email).first();
15869
16147
  if (existingUser) {
15870
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16148
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15871
16149
  type: "error",
15872
16150
  message: "Email is already taken.",
15873
16151
  dismissible: true
15874
16152
  }));
15875
16153
  }
15876
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(password);
16154
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(password);
15877
16155
  const userId = crypto.randomUUID();
15878
16156
  const createStmt = db.prepare(`
15879
16157
  INSERT INTO auth_user (
@@ -15898,7 +16176,7 @@ userRoutes.post("/users/new", async (c) => {
15898
16176
  if (sanitizedRegistrationCustom !== null && Object.keys(sanitizedRegistrationCustom).length > 0) {
15899
16177
  await writeProfileData(db, userId, { custom: sanitizedRegistrationCustom }, user.userId);
15900
16178
  }
15901
- await chunkIS54FPB7_cjs.logActivity(
16179
+ await chunkE7E3NL3Y_cjs.logActivity(
15902
16180
  db,
15903
16181
  user.userId,
15904
16182
  "user!.create",
@@ -15912,7 +16190,7 @@ userRoutes.post("/users/new", async (c) => {
15912
16190
  return c.body(null, 200);
15913
16191
  } catch (error) {
15914
16192
  console.error("User creation error:", error);
15915
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16193
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15916
16194
  type: "error",
15917
16195
  message: "Failed to create user!. Please try again.",
15918
16196
  dismissible: true
@@ -15937,7 +16215,7 @@ userRoutes.get("/users/:id", async (c) => {
15937
16215
  if (!userRecord) {
15938
16216
  return c.json({ error: "User not found" }, 404);
15939
16217
  }
15940
- await chunkIS54FPB7_cjs.logActivity(
16218
+ await chunkE7E3NL3Y_cjs.logActivity(
15941
16219
  db,
15942
16220
  user.userId,
15943
16221
  "user!.view",
@@ -15982,7 +16260,7 @@ userRoutes.get("/users/:id/edit", async (c) => {
15982
16260
  `);
15983
16261
  const userToEdit = await userStmt.bind(userId).first();
15984
16262
  if (!userToEdit) {
15985
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16263
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
15986
16264
  type: "error",
15987
16265
  message: "User not found",
15988
16266
  dismissible: true
@@ -16035,9 +16313,10 @@ userRoutes.get("/users/:id/edit", async (c) => {
16035
16313
  }
16036
16314
  const queryMessage = c.req.query("message");
16037
16315
  const queryType = c.req.query("type") === "error" ? "error" : "success";
16316
+ const roles = await fetchRoleOptions(db);
16038
16317
  const pageData = {
16039
16318
  userToEdit: editData,
16040
- roles: ROLES,
16319
+ roles,
16041
16320
  hasProfilePlugin: profileConfig !== null && upRow !== null,
16042
16321
  customProfileFieldsHtml,
16043
16322
  tenantMemberships,
@@ -16052,7 +16331,7 @@ userRoutes.get("/users/:id/edit", async (c) => {
16052
16331
  return c.html(renderUserEditPage(pageData));
16053
16332
  } catch (error) {
16054
16333
  console.error("User edit page error:", error);
16055
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16334
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16056
16335
  type: "error",
16057
16336
  message: "Failed to load user. Please try again.",
16058
16337
  dismissible: true
@@ -16069,9 +16348,9 @@ userRoutes.put("/users/:id", async (c) => {
16069
16348
  const lastName = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("last_name")?.toString());
16070
16349
  const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
16071
16350
  const phone = chunkMNWKYY5E_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
16072
- const roleInput = formData.get("role")?.toString() || "viewer";
16073
- const validRoles = ["admin", "editor", "author", "viewer"];
16074
- const role = validRoles.includes(roleInput) ? roleInput : "viewer";
16351
+ const roleInput = formData.get("role")?.toString() || "";
16352
+ const validRoles = (await new chunkJCBDXEY2_cjs.RbacService(db).getRoles()).map((r) => r.id);
16353
+ const role = validRoles.includes(roleInput) ? roleInput : validRoles[0] ?? "admin";
16075
16354
  const isActive = formData.get("is_active") === "1";
16076
16355
  const emailVerified = formData.get("email_verified") === "1";
16077
16356
  const newPassword = formData.get("new_password")?.toString() || "";
@@ -16085,12 +16364,12 @@ userRoutes.put("/users/:id", async (c) => {
16085
16364
  const validation = validateCustomData(sanitized, profileConfig);
16086
16365
  if (!validation.valid) {
16087
16366
  const errorMessages = Object.values(validation.errors).join(", ");
16088
- return c.html(chunkJMJM7DMM_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
16367
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({ type: "error", message: errorMessages, dismissible: true }), 400);
16089
16368
  }
16090
16369
  sanitizedCustom = sanitized;
16091
16370
  }
16092
16371
  if (!email) {
16093
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16372
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16094
16373
  type: "error",
16095
16374
  message: "Email is required.",
16096
16375
  dismissible: true
@@ -16098,7 +16377,7 @@ userRoutes.put("/users/:id", async (c) => {
16098
16377
  }
16099
16378
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
16100
16379
  if (!emailRegex.test(email)) {
16101
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16380
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16102
16381
  type: "error",
16103
16382
  message: "Please enter a valid email address.",
16104
16383
  dismissible: true
@@ -16106,14 +16385,14 @@ userRoutes.put("/users/:id", async (c) => {
16106
16385
  }
16107
16386
  if (newPassword) {
16108
16387
  if (newPassword.length < 8) {
16109
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16388
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16110
16389
  type: "error",
16111
16390
  message: "Password must be at least 8 characters long.",
16112
16391
  dismissible: true
16113
16392
  }));
16114
16393
  }
16115
16394
  if (newPassword !== confirmPassword) {
16116
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16395
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16117
16396
  type: "error",
16118
16397
  message: "Passwords do not match.",
16119
16398
  dismissible: true
@@ -16126,7 +16405,7 @@ userRoutes.put("/users/:id", async (c) => {
16126
16405
  `);
16127
16406
  const existingUser = await checkStmt.bind(email, userId).first();
16128
16407
  if (existingUser) {
16129
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16408
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16130
16409
  type: "error",
16131
16410
  message: "Email is already taken by another user.",
16132
16411
  dismissible: true
@@ -16151,7 +16430,7 @@ userRoutes.put("/users/:id", async (c) => {
16151
16430
  userId
16152
16431
  ).run();
16153
16432
  if (newPassword) {
16154
- const passwordHash = await chunkIS54FPB7_cjs.AuthManager.hashPassword(newPassword);
16433
+ const passwordHash = await chunkE7E3NL3Y_cjs.AuthManager.hashPassword(newPassword);
16155
16434
  const updatePasswordStmt = db.prepare(`
16156
16435
  UPDATE auth_user SET password_hash = ?, updated_at = ? WHERE id = ?
16157
16436
  `);
@@ -16164,7 +16443,7 @@ userRoutes.put("/users/:id", async (c) => {
16164
16443
  ...sanitizedCustom !== null ? { custom: sanitizedCustom } : {}
16165
16444
  }, user.userId);
16166
16445
  }
16167
- await chunkIS54FPB7_cjs.logActivity(
16446
+ await chunkE7E3NL3Y_cjs.logActivity(
16168
16447
  db,
16169
16448
  user.userId,
16170
16449
  "user.update",
@@ -16174,7 +16453,7 @@ userRoutes.put("/users/:id", async (c) => {
16174
16453
  c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
16175
16454
  c.req.header("user-agent")
16176
16455
  );
16177
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16456
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16178
16457
  type: "success",
16179
16458
  message: "User updated successfully!",
16180
16459
  dismissible: true,
@@ -16182,7 +16461,7 @@ userRoutes.put("/users/:id", async (c) => {
16182
16461
  }));
16183
16462
  } catch (error) {
16184
16463
  console.error("User update error:", error);
16185
- return c.html(chunkJMJM7DMM_cjs.renderAlert({
16464
+ return c.html(chunkTT3TQSCZ_cjs.renderAlert({
16186
16465
  type: "error",
16187
16466
  message: "Failed to update user. Please try again.",
16188
16467
  dismissible: true
@@ -16210,7 +16489,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
16210
16489
  UPDATE auth_user SET is_active = ?, updated_at = ? WHERE id = ?
16211
16490
  `);
16212
16491
  await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
16213
- await chunkIS54FPB7_cjs.logActivity(
16492
+ await chunkE7E3NL3Y_cjs.logActivity(
16214
16493
  db,
16215
16494
  user.userId,
16216
16495
  active ? "user.activate" : "user.deactivate",
@@ -16251,7 +16530,7 @@ userRoutes.delete("/users/:id", async (c) => {
16251
16530
  DELETE FROM auth_user WHERE id = ?
16252
16531
  `);
16253
16532
  await deleteStmt.bind(userId).run();
16254
- await chunkIS54FPB7_cjs.logActivity(
16533
+ await chunkE7E3NL3Y_cjs.logActivity(
16255
16534
  db,
16256
16535
  user.userId,
16257
16536
  "user!.hard_delete",
@@ -16270,7 +16549,7 @@ userRoutes.delete("/users/:id", async (c) => {
16270
16549
  UPDATE auth_user SET is_active = 0, updated_at = ? WHERE id = ?
16271
16550
  `);
16272
16551
  await deleteStmt.bind(Date.now(), userId).run();
16273
- await chunkIS54FPB7_cjs.logActivity(
16552
+ await chunkE7E3NL3Y_cjs.logActivity(
16274
16553
  db,
16275
16554
  user.userId,
16276
16555
  "user!.soft_delete",
@@ -16336,7 +16615,7 @@ userRoutes.post("/invite-user", async (c) => {
16336
16615
  Date.now(),
16337
16616
  Date.now()
16338
16617
  ).run();
16339
- await chunkIS54FPB7_cjs.logActivity(
16618
+ await chunkE7E3NL3Y_cjs.logActivity(
16340
16619
  db,
16341
16620
  user.userId,
16342
16621
  "user!.invite_sent",
@@ -16393,7 +16672,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
16393
16672
  Date.now(),
16394
16673
  userId
16395
16674
  ).run();
16396
- await chunkIS54FPB7_cjs.logActivity(
16675
+ await chunkE7E3NL3Y_cjs.logActivity(
16397
16676
  db,
16398
16677
  user.userId,
16399
16678
  "user!.invitation_resent",
@@ -16429,7 +16708,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
16429
16708
  }
16430
16709
  const deleteStmt = db.prepare(`DELETE FROM auth_user WHERE id = ?`);
16431
16710
  await deleteStmt.bind(userId).run();
16432
- await chunkIS54FPB7_cjs.logActivity(
16711
+ await chunkE7E3NL3Y_cjs.logActivity(
16433
16712
  db,
16434
16713
  user.userId,
16435
16714
  "user!.invitation_cancelled",
@@ -16678,7 +16957,7 @@ function getFileIcon(mimeType) {
16678
16957
  }
16679
16958
 
16680
16959
  // src/templates/pages/admin-media-library.template.ts
16681
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
16960
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
16682
16961
  function renderMediaLibraryPage(data) {
16683
16962
  const pageContent = `
16684
16963
  <div>
@@ -17613,7 +17892,7 @@ function renderMediaLibraryPage(data) {
17613
17892
  version: data.version,
17614
17893
  content: pageContent
17615
17894
  };
17616
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
17895
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
17617
17896
  }
17618
17897
 
17619
17898
  // src/templates/components/media-file-details.template.ts
@@ -17810,7 +18089,7 @@ var fileValidationSchema2 = zod.z.object({
17810
18089
  // 50MB max
17811
18090
  });
17812
18091
  var adminMediaRoutes = new hono.Hono();
17813
- adminMediaRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
18092
+ adminMediaRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
17814
18093
  adminMediaRoutes.get("/", async (c) => {
17815
18094
  try {
17816
18095
  const user = c.get("user");
@@ -17852,7 +18131,7 @@ adminMediaRoutes.get("/selector", async (c) => {
17852
18131
  const { searchParams } = new URL(c.req.url);
17853
18132
  const search = searchParams.get("search") || "";
17854
18133
  const db = c.env.DB;
17855
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
18134
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
17856
18135
  const mediaPlugin = await pluginService.getPlugin("core-media");
17857
18136
  if (!mediaPlugin || mediaPlugin.status !== "active") {
17858
18137
  return c.html(html.html`
@@ -18228,7 +18507,7 @@ adminMediaRoutes.put("/:id", async (c) => {
18228
18507
  `);
18229
18508
  }
18230
18509
  });
18231
- adminMediaRoutes.delete("/cleanup", chunkIS54FPB7_cjs.requireRole("admin"), async (c) => {
18510
+ adminMediaRoutes.delete("/cleanup", chunkE7E3NL3Y_cjs.requireRole("admin"), async (c) => {
18232
18511
  try {
18233
18512
  const db = c.env.DB;
18234
18513
  const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
@@ -18486,7 +18765,7 @@ function generateMediaItemHTML(file) {
18486
18765
  }
18487
18766
 
18488
18767
  // src/templates/pages/admin-plugins-list.template.ts
18489
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
18768
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
18490
18769
  function renderPluginsListPage(data) {
18491
18770
  const categories = [
18492
18771
  { value: "content", label: "Content Management" },
@@ -18658,15 +18937,6 @@ function renderPluginsListPage(data) {
18658
18937
  </div>
18659
18938
 
18660
18939
  <div class="flex items-center gap-3 w-full sm:w-auto">
18661
- <select id="sort-filter" onchange="filterAndSortPlugins()" class="block w-full sm:w-auto h-9 rounded-md border-0 py-1.5 pl-3 pr-8 text-zinc-900 ring-1 ring-inset ring-zinc-300 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-900 dark:text-white dark:ring-zinc-700 dark:focus:ring-zinc-500 sm:text-sm sm:leading-6">
18662
- <option value="name-asc">Name (A-Z)</option>
18663
- <option value="name-desc">Name (Z-A)</option>
18664
- <option value="newest">Newest Installed</option>
18665
- <option value="updated">Recently Updated</option>
18666
- <option value="popular">Popularity</option>
18667
- <option value="rating">Highest Rated</option>
18668
- </select>
18669
-
18670
18940
  <button
18671
18941
  onclick="location.reload()"
18672
18942
  class="inline-flex items-center gap-x-1.5 rounded-md bg-white dark:bg-zinc-900 px-3 py-1.5 h-9 text-sm font-semibold text-zinc-900 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800"
@@ -18830,8 +19100,6 @@ function renderPluginsListPage(data) {
18830
19100
  .forEach(cb => params.append('status', cb.value));
18831
19101
  const search = document.getElementById('search-input').value;
18832
19102
  if (search) params.set('search', search);
18833
- const sort = document.getElementById('sort-filter').value;
18834
- if (sort && sort !== 'name-asc') params.set('sort', sort);
18835
19103
  const url = new URL(window.location.href);
18836
19104
  url.search = params.toString();
18837
19105
  history.replaceState(null, '', url.toString());
@@ -18849,8 +19117,6 @@ function renderPluginsListPage(data) {
18849
19117
  });
18850
19118
  const search = params.get('search');
18851
19119
  if (search) document.getElementById('search-input').value = search;
18852
- const sort = params.get('sort');
18853
- if (sort) document.getElementById('sort-filter').value = sort;
18854
19120
  if (params.toString()) filterAndSortPlugins();
18855
19121
  }
18856
19122
 
@@ -18866,7 +19132,6 @@ function renderPluginsListPage(data) {
18866
19132
  .map(cb => cb.value.toLowerCase());
18867
19133
 
18868
19134
  const searchInput = document.getElementById('search-input').value.toLowerCase();
18869
- const sortValue = document.getElementById('sort-filter').value;
18870
19135
 
18871
19136
  const pluginsGrid = document.getElementById('plugins-grid');
18872
19137
  const pluginCards = Array.from(pluginsGrid.querySelectorAll('.plugin-card'));
@@ -18890,30 +19155,6 @@ function renderPluginsListPage(data) {
18890
19155
  return true;
18891
19156
  });
18892
19157
 
18893
- // Sort
18894
- visibleCards.sort((a, b) => {
18895
- const aName = a.getAttribute('data-name') || '';
18896
- const bName = b.getAttribute('data-name') || '';
18897
- const aInstalled = parseInt(a.getAttribute('data-installed') || '0');
18898
- const bInstalled = parseInt(b.getAttribute('data-installed') || '0');
18899
- const aUpdated = parseInt(a.getAttribute('data-updated') || '0');
18900
- const bUpdated = parseInt(b.getAttribute('data-updated') || '0');
18901
- const aDownloads = parseInt(a.getAttribute('data-downloads') || '0');
18902
- const bDownloads = parseInt(b.getAttribute('data-downloads') || '0');
18903
- const aRating = parseFloat(a.getAttribute('data-rating') || '0');
18904
- const bRating = parseFloat(b.getAttribute('data-rating') || '0');
18905
-
18906
- switch (sortValue) {
18907
- case 'name-desc': return bName.localeCompare(aName);
18908
- case 'newest': return bInstalled - aInstalled;
18909
- case 'updated': return bUpdated - aUpdated;
18910
- case 'popular': return bDownloads - aDownloads;
18911
- case 'rating': return bRating - aRating;
18912
- case 'name-asc':
18913
- default: return aName.localeCompare(bName);
18914
- }
18915
- });
18916
-
18917
19158
  // Re-append
18918
19159
  pluginCards.forEach(card => card.style.display = 'none'); // Hide all first
18919
19160
 
@@ -18969,7 +19210,7 @@ function renderPluginsListPage(data) {
18969
19210
  version: data.version,
18970
19211
  content: pageContent
18971
19212
  };
18972
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
19213
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
18973
19214
  }
18974
19215
  function renderPluginCard(plugin) {
18975
19216
  const statusColors = {
@@ -19347,8 +19588,9 @@ function escapeHtmlAttr(value) {
19347
19588
  }
19348
19589
  function renderPluginSettingsPage(data) {
19349
19590
  const { plugin, activity = [], user, settingsTabData } = data;
19350
- const pluginDef = chunk4WMZGHIC_cjs.getPluginDefinition(plugin.id || plugin.name);
19351
- const hasUserSettings = pluginDef?.settingsTabContent != null || Object.keys(plugin.settings || {}).some((k) => !k.startsWith("_"));
19591
+ const pluginDef = chunkD6FWEYQE_cjs.getPluginDefinition(plugin.id || plugin.name);
19592
+ const pluginId = plugin.id || plugin.name;
19593
+ const hasUserSettings = pluginDef?.settingsTabContent != null || Object.keys(plugin.settings || {}).some((k) => !k.startsWith("_")) || hasCustomSettingsComponent(pluginId);
19352
19594
  const defaultTab = hasUserSettings ? "settings" : "info";
19353
19595
  const pageContent = `
19354
19596
  <div class="w-full px-4 sm:px-6 lg:px-8 py-6">
@@ -19513,9 +19755,28 @@ function renderPluginSettingsPage(data) {
19513
19755
  const form = document.getElementById('settings-form');
19514
19756
  const formData = new FormData(form);
19515
19757
  const isAuthPlugin = '${plugin.id}' === 'core-auth';
19758
+ const isOAuthPlugin = '${plugin.id}' === 'oauth-providers';
19516
19759
  let settings = {};
19517
19760
 
19518
- if (isAuthPlugin) {
19761
+ if (isOAuthPlugin) {
19762
+ // Build nested provider settings structure
19763
+ const getVal = id => (document.getElementById(id) || {}).value || '';
19764
+ const getChecked = id => !!(document.getElementById(id) || {}).checked;
19765
+ settings = {
19766
+ providers: {
19767
+ github: {
19768
+ clientId: getVal('oauth_github_clientId'),
19769
+ clientSecret: getVal('oauth_github_clientSecret'),
19770
+ enabled: getChecked('oauth_github_enabled'),
19771
+ },
19772
+ google: {
19773
+ clientId: getVal('oauth_google_clientId'),
19774
+ clientSecret: getVal('oauth_google_clientSecret'),
19775
+ enabled: getChecked('oauth_google_enabled'),
19776
+ },
19777
+ }
19778
+ };
19779
+ } else if (isAuthPlugin) {
19519
19780
  // Handle nested auth settings structure
19520
19781
  settings = {
19521
19782
  requiredFields: {},
@@ -19624,7 +19885,7 @@ function renderPluginSettingsPage(data) {
19624
19885
  user,
19625
19886
  content: pageContent
19626
19887
  };
19627
- return chunkE66UVONV_cjs.renderAdminLayout(layoutData);
19888
+ return chunkOUBUS74I_cjs.renderAdminLayout(layoutData);
19628
19889
  }
19629
19890
  function renderStatusBadge(status) {
19630
19891
  const statusColors = {
@@ -19653,7 +19914,7 @@ function renderSettingsTab(plugin, settingsTabData) {
19653
19914
  const settings = plugin.settings || {};
19654
19915
  const pluginId = plugin.id || plugin.name;
19655
19916
  const hasUserKeys = Object.keys(settings).some((k) => !k.startsWith("_"));
19656
- const pluginDef = chunk4WMZGHIC_cjs.getPluginDefinition(pluginId);
19917
+ const pluginDef = chunkD6FWEYQE_cjs.getPluginDefinition(pluginId);
19657
19918
  if (pluginDef?.settingsTabContent) {
19658
19919
  return pluginDef.settingsTabContent.render({ plugin, settings, data: settingsTabData });
19659
19920
  }
@@ -20003,7 +20264,8 @@ function formatTimestamp(timestamp) {
20003
20264
  }
20004
20265
  var pluginSettingsComponents = {
20005
20266
  "otp-login": renderOTPLoginSettingsContent,
20006
- "email": renderEmailSettingsContent
20267
+ "email": renderEmailSettingsContent,
20268
+ "oauth-providers": renderOAuthProvidersSettingsContent
20007
20269
  };
20008
20270
  function renderOTPLoginSettingsContent(plugin, settings) {
20009
20271
  const siteName = settings.siteName || "SonicJS";
@@ -20726,6 +20988,62 @@ defineUserProfile({
20726
20988
  </div>
20727
20989
  `;
20728
20990
  }
20991
+ function renderOAuthProvidersSettingsContent(plugin, settings) {
20992
+ const providers = settings.providers || {};
20993
+ const gh = providers.github || {};
20994
+ const gg = providers.google || {};
20995
+ const inputClass = "backdrop-blur-sm bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-gray-300 focus:border-blue-400 focus:outline-none transition-colors w-full";
20996
+ return `
20997
+ <div class="space-y-6">
20998
+ <form id="settings-form">
20999
+ <div class="backdrop-blur-md bg-white/5 rounded-xl border border-white/10 p-6 space-y-6">
21000
+ <h3 class="text-lg font-semibold text-white">GitHub OAuth</h3>
21001
+ <div>
21002
+ <label for="oauth_github_clientId" class="block text-sm font-medium text-gray-300 mb-2">Client ID</label>
21003
+ <input type="text" id="oauth_github_clientId" name="oauth_github_clientId"
21004
+ value="${escapeHtmlAttr(gh.clientId || "")}"
21005
+ placeholder="GitHub OAuth App Client ID" class="${inputClass}" />
21006
+ </div>
21007
+ <div>
21008
+ <label for="oauth_github_clientSecret" class="block text-sm font-medium text-gray-300 mb-2">Client Secret</label>
21009
+ <input type="password" id="oauth_github_clientSecret" name="oauth_github_clientSecret"
21010
+ value="${escapeHtmlAttr(gh.clientSecret || "")}"
21011
+ placeholder="GitHub OAuth App Client Secret" class="${inputClass}" />
21012
+ </div>
21013
+ <div class="flex items-center gap-3">
21014
+ <input type="checkbox" id="oauth_github_enabled" name="oauth_github_enabled"
21015
+ ${gh.enabled ? "checked" : ""} class="w-4 h-4 rounded border-white/10" />
21016
+ <label for="oauth_github_enabled" class="text-sm text-gray-300">Enable GitHub login</label>
21017
+ </div>
21018
+ </div>
21019
+
21020
+ <div class="backdrop-blur-md bg-white/5 rounded-xl border border-white/10 p-6 space-y-6 mt-6">
21021
+ <h3 class="text-lg font-semibold text-white">Google OAuth</h3>
21022
+ <div>
21023
+ <label for="oauth_google_clientId" class="block text-sm font-medium text-gray-300 mb-2">Client ID</label>
21024
+ <input type="text" id="oauth_google_clientId" name="oauth_google_clientId"
21025
+ value="${escapeHtmlAttr(gg.clientId || "")}"
21026
+ placeholder="Google OAuth Client ID" class="${inputClass}" />
21027
+ </div>
21028
+ <div>
21029
+ <label for="oauth_google_clientSecret" class="block text-sm font-medium text-gray-300 mb-2">Client Secret</label>
21030
+ <input type="password" id="oauth_google_clientSecret" name="oauth_google_clientSecret"
21031
+ value="${escapeHtmlAttr(gg.clientSecret || "")}"
21032
+ placeholder="Google OAuth Client Secret" class="${inputClass}" />
21033
+ </div>
21034
+ <div class="flex items-center gap-3">
21035
+ <input type="checkbox" id="oauth_google_enabled" name="oauth_google_enabled"
21036
+ ${gg.enabled ? "checked" : ""} class="w-4 h-4 rounded border-white/10" />
21037
+ <label for="oauth_google_enabled" class="text-sm text-gray-300">Enable Google login</label>
21038
+ </div>
21039
+ </div>
21040
+ </form>
21041
+ </div>
21042
+ `;
21043
+ }
21044
+ function hasCustomSettingsComponent(pluginId) {
21045
+ return pluginId in pluginSettingsComponents;
21046
+ }
20729
21047
 
20730
21048
  // src/plugins/core-plugins/menu-plugin/services/menu-reconcile.ts
20731
21049
  async function upsertPluginRow(db, slug, entry, visible) {
@@ -20843,8 +21161,8 @@ async function fetchActivePluginIds(db) {
20843
21161
  }
20844
21162
  async function reconcileMenuFromPlugins(db) {
20845
21163
  try {
20846
- const singletonEntries = chunk4WMZGHIC_cjs.getPluginMenu();
20847
- const manifestEntries = Object.values(chunkAT4ZQGEZ_cjs.PLUGIN_REGISTRY).filter(
21164
+ const singletonEntries = chunkD6FWEYQE_cjs.getPluginMenu();
21165
+ const manifestEntries = Object.values(chunkYAURVGUK_cjs.PLUGIN_REGISTRY).filter(
20848
21166
  (p) => p.adminMenu !== null
20849
21167
  );
20850
21168
  const byPluginId = /* @__PURE__ */ new Map();
@@ -20881,8 +21199,8 @@ async function reconcileMenuFromPlugins(db) {
20881
21199
 
20882
21200
  // src/routes/admin-plugins.ts
20883
21201
  var adminPluginRoutes = new hono.Hono();
20884
- adminPluginRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
20885
- var AVAILABLE_PLUGINS = Object.values(chunkAT4ZQGEZ_cjs.PLUGIN_REGISTRY).map((p) => ({
21202
+ adminPluginRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
21203
+ var AVAILABLE_PLUGINS = Object.values(chunkYAURVGUK_cjs.PLUGIN_REGISTRY).map((p) => ({
20886
21204
  id: p.id,
20887
21205
  name: p.codeName,
20888
21206
  display_name: p.displayName,
@@ -20902,7 +21220,7 @@ adminPluginRoutes.get("/", async (c) => {
20902
21220
  if (user?.role !== "admin") {
20903
21221
  return c.text("Access denied", 403);
20904
21222
  }
20905
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21223
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
20906
21224
  let installedPlugins = [];
20907
21225
  let stats = { total: 0, active: 0, inactive: 0, errors: 0, uninstalled: 0 };
20908
21226
  try {
@@ -20976,10 +21294,10 @@ adminPluginRoutes.get("/:id", async (c) => {
20976
21294
  if (user?.role !== "admin") {
20977
21295
  return c.redirect("/admin/plugins");
20978
21296
  }
20979
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21297
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
20980
21298
  let plugin = await pluginService.getPlugin(pluginId);
20981
21299
  if (!plugin) {
20982
- const def = chunk4WMZGHIC_cjs.getPluginDefinition(pluginId);
21300
+ const def = chunkD6FWEYQE_cjs.getPluginDefinition(pluginId);
20983
21301
  if (!def) return c.text("Plugin not found", 404);
20984
21302
  plugin = await pluginService.ensurePlugin(pluginId, {
20985
21303
  displayName: def.displayName ?? pluginId,
@@ -21050,7 +21368,7 @@ adminPluginRoutes.get("/:id", async (c) => {
21050
21368
  timestamp: item.timestamp,
21051
21369
  user: item.userId || null
21052
21370
  }));
21053
- const pluginDef = chunk4WMZGHIC_cjs.getPluginDefinition(pluginId);
21371
+ const pluginDef = chunkD6FWEYQE_cjs.getPluginDefinition(pluginId);
21054
21372
  let settingsTabData = void 0;
21055
21373
  if (pluginDef?.settingsTabContent?.loadData) {
21056
21374
  try {
@@ -21080,14 +21398,14 @@ adminPluginRoutes.get("/:id/configure", async (c) => {
21080
21398
  const user = c.get("user");
21081
21399
  const pluginId = c.req.param("id");
21082
21400
  if (user?.role !== "admin") return c.redirect("/admin/plugins");
21083
- const def = chunk4WMZGHIC_cjs.getPluginDefinition(pluginId);
21401
+ const def = chunkD6FWEYQE_cjs.getPluginDefinition(pluginId);
21084
21402
  if (!def?.configSchema) return c.text("No schema-driven settings for this plugin", 404);
21085
21403
  const db = c.env.DB;
21086
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21404
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21087
21405
  const plugin = await pluginService.getPlugin(pluginId);
21088
21406
  const stored = plugin?.settings ?? {};
21089
- const values = chunk4WMZGHIC_cjs.applySchemaDefaults(def.configSchema, stored);
21090
- const fieldsHtml = chunk4WMZGHIC_cjs.renderSchemaFields(def.configSchema, values);
21407
+ const values = chunkD6FWEYQE_cjs.applySchemaDefaults(def.configSchema, stored);
21408
+ const fieldsHtml = chunkD6FWEYQE_cjs.renderSchemaFields(def.configSchema, values);
21091
21409
  const displayName = plugin?.display_name ?? def.displayName ?? def.id;
21092
21410
  return c.html(`<!DOCTYPE html>
21093
21411
  <html lang="en"><head>
@@ -21114,12 +21432,12 @@ adminPluginRoutes.post("/:id/configure", async (c) => {
21114
21432
  const user = c.get("user");
21115
21433
  const pluginId = c.req.param("id");
21116
21434
  if (user?.role !== "admin") return c.json({ error: "Access denied" }, 403);
21117
- const def = chunk4WMZGHIC_cjs.getPluginDefinition(pluginId);
21435
+ const def = chunkD6FWEYQE_cjs.getPluginDefinition(pluginId);
21118
21436
  if (!def?.configSchema) return c.text("No schema-driven settings for this plugin", 404);
21119
21437
  const form = await c.req.formData();
21120
- const parsed = chunk4WMZGHIC_cjs.parseFormDataToSettings(def.configSchema, form);
21438
+ const parsed = chunkD6FWEYQE_cjs.parseFormDataToSettings(def.configSchema, form);
21121
21439
  const db = c.env.DB;
21122
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21440
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21123
21441
  await pluginService.ensurePlugin(pluginId, {
21124
21442
  displayName: def.displayName ?? pluginId,
21125
21443
  version: def.version,
@@ -21143,7 +21461,7 @@ adminPluginRoutes.post("/:id/activate", async (c) => {
21143
21461
  if (user?.role !== "admin") {
21144
21462
  return c.json({ error: "Access denied" }, 403);
21145
21463
  }
21146
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21464
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21147
21465
  await pluginService.activatePlugin(pluginId);
21148
21466
  await reconcileMenuFromPlugins(db);
21149
21467
  return c.json({ success: true });
@@ -21161,7 +21479,7 @@ adminPluginRoutes.post("/:id/deactivate", async (c) => {
21161
21479
  if (user?.role !== "admin") {
21162
21480
  return c.json({ error: "Access denied" }, 403);
21163
21481
  }
21164
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21482
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21165
21483
  await pluginService.deactivatePlugin(pluginId);
21166
21484
  await reconcileMenuFromPlugins(db);
21167
21485
  return c.json({ success: true });
@@ -21179,8 +21497,8 @@ adminPluginRoutes.post("/install", async (c) => {
21179
21497
  return c.json({ error: "Access denied" }, 403);
21180
21498
  }
21181
21499
  const body = await c.req.json();
21182
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21183
- const registryEntry = chunkAT4ZQGEZ_cjs.findPluginByCodeName(body.name) || chunkAT4ZQGEZ_cjs.PLUGIN_REGISTRY[body.name] || chunkAT4ZQGEZ_cjs.PLUGIN_REGISTRY[body.id];
21500
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21501
+ const registryEntry = chunkYAURVGUK_cjs.findPluginByCodeName(body.name) || chunkYAURVGUK_cjs.PLUGIN_REGISTRY[body.name] || chunkYAURVGUK_cjs.PLUGIN_REGISTRY[body.id];
21184
21502
  if (!registryEntry) {
21185
21503
  return c.json({ error: "Plugin not found in registry" }, 404);
21186
21504
  }
@@ -21213,7 +21531,7 @@ adminPluginRoutes.post("/:id/uninstall", async (c) => {
21213
21531
  if (user?.role !== "admin") {
21214
21532
  return c.json({ error: "Access denied" }, 403);
21215
21533
  }
21216
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21534
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21217
21535
  await pluginService.uninstallPlugin(pluginId);
21218
21536
  return c.json({ success: true });
21219
21537
  } catch (error) {
@@ -21231,7 +21549,7 @@ adminPluginRoutes.post("/:id/settings", async (c) => {
21231
21549
  return c.json({ error: "Access denied" }, 403);
21232
21550
  }
21233
21551
  const settings = await c.req.json();
21234
- const pluginService = new chunkAT4ZQGEZ_cjs.PluginService(db);
21552
+ const pluginService = new chunkYAURVGUK_cjs.PluginService(db);
21235
21553
  const existingPlugin = await pluginService.getPlugin(pluginId);
21236
21554
  const existingSettings = existingPlugin?.settings ?? {};
21237
21555
  await pluginService.updatePluginSettings(pluginId, { ...existingSettings, ...settings });
@@ -21266,7 +21584,7 @@ function formatLastUpdated(timestamp) {
21266
21584
  }
21267
21585
 
21268
21586
  // src/templates/pages/admin-logs-list.template.ts
21269
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
21587
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
21270
21588
  function renderLogsListPage(data) {
21271
21589
  const { logs, pagination, filters, user } = data;
21272
21590
  const content = `
@@ -21577,7 +21895,7 @@ function renderLogsListPage(data) {
21577
21895
  user,
21578
21896
  content
21579
21897
  };
21580
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
21898
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
21581
21899
  }
21582
21900
  function renderLogDetailsPage(data) {
21583
21901
  const { log, user } = data;
@@ -21789,7 +22107,7 @@ function renderLogDetailsPage(data) {
21789
22107
  </div>
21790
22108
  </div>
21791
22109
  `;
21792
- return chunkE66UVONV_cjs.adminLayoutV2({
22110
+ return chunkOUBUS74I_cjs.adminLayoutV2({
21793
22111
  title: `Log Details - ${log.id}`,
21794
22112
  user,
21795
22113
  content
@@ -22032,7 +22350,7 @@ function renderLogConfigPage(data) {
22032
22350
 
22033
22351
  <script src="https://unpkg.com/htmx.org@1.9.6"></script>
22034
22352
  `;
22035
- return chunkE66UVONV_cjs.adminLayoutV2({
22353
+ return chunkOUBUS74I_cjs.adminLayoutV2({
22036
22354
  title: "Log Configuration",
22037
22355
  user,
22038
22356
  content
@@ -22041,7 +22359,7 @@ function renderLogConfigPage(data) {
22041
22359
 
22042
22360
  // src/routes/admin-logs.ts
22043
22361
  var adminLogsRoutes = new hono.Hono();
22044
- adminLogsRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
22362
+ adminLogsRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
22045
22363
  adminLogsRoutes.get("/", async (c) => {
22046
22364
  try {
22047
22365
  const user = c.get("user");
@@ -22413,7 +22731,7 @@ adminCheckboxRoutes.get("/", (c) => {
22413
22731
  role: user.role
22414
22732
  } : void 0
22415
22733
  };
22416
- return c.html(chunkE66UVONV_cjs.renderCheckboxPage(pageData));
22734
+ return c.html(chunkOUBUS74I_cjs.renderCheckboxPage(pageData));
22417
22735
  });
22418
22736
 
22419
22737
  // src/templates/pages/admin-dashboard.template.ts
@@ -22485,7 +22803,7 @@ function renderDashboardPage(data) {
22485
22803
  version: data.version,
22486
22804
  content: pageContent
22487
22805
  };
22488
- return chunkE66UVONV_cjs.renderAdminLayout(layoutData);
22806
+ return chunkOUBUS74I_cjs.renderAdminLayout(layoutData);
22489
22807
  }
22490
22808
  function renderStatsCards(stats) {
22491
22809
  const cards = [
@@ -23033,9 +23351,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
23033
23351
  }
23034
23352
 
23035
23353
  // src/routes/admin-dashboard.ts
23036
- var VERSION = chunkC3ZQP5NW_cjs.getCoreVersion();
23354
+ var VERSION = chunkBJ6NJL2E_cjs.getCoreVersion();
23037
23355
  var router = new hono.Hono();
23038
- router.use("*", chunkIS54FPB7_cjs.requireAuth());
23356
+ router.use("*", chunkE7E3NL3Y_cjs.requireAuth());
23039
23357
  router.get("/", async (c) => {
23040
23358
  const user = c.get("user");
23041
23359
  try {
@@ -23225,7 +23543,7 @@ router.get("/system-status", async (c) => {
23225
23543
  });
23226
23544
 
23227
23545
  // src/templates/pages/admin-collections-list.template.ts
23228
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
23546
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
23229
23547
 
23230
23548
  // src/templates/components/table.template.ts
23231
23549
  function renderTable2(data) {
@@ -23707,11 +24025,11 @@ function renderCollectionsListPage(data) {
23707
24025
  version: data.version,
23708
24026
  content: pageContent
23709
24027
  };
23710
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
24028
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
23711
24029
  }
23712
24030
 
23713
24031
  // src/templates/pages/admin-collections-form.template.ts
23714
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
24032
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
23715
24033
  function getFieldTypeBadge(fieldType) {
23716
24034
  const typeLabels = {
23717
24035
  "text": "Text",
@@ -23884,8 +24202,8 @@ function renderCollectionFormPage(data) {
23884
24202
  <!-- Form Content -->
23885
24203
  <div class="px-6 py-6">
23886
24204
  <div id="form-messages"></div>
23887
- ${data.error ? chunkJMJM7DMM_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
23888
- ${data.success ? chunkJMJM7DMM_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
24205
+ ${data.error ? chunkTT3TQSCZ_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
24206
+ ${data.success ? chunkTT3TQSCZ_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
23889
24207
 
23890
24208
  <!-- Form Styling -->
23891
24209
  <style>
@@ -23996,7 +24314,7 @@ function renderCollectionFormPage(data) {
23996
24314
  }
23997
24315
  </style>
23998
24316
 
23999
- ${chunkE66UVONV_cjs.renderForm(formData)}
24317
+ ${chunkOUBUS74I_cjs.renderForm(formData)}
24000
24318
 
24001
24319
  ${isEdit && data.managed ? `
24002
24320
  <!-- Read-Only Fields Display for Managed Collections -->
@@ -24994,13 +25312,13 @@ function renderCollectionFormPage(data) {
24994
25312
  version: data.version,
24995
25313
  content: pageContent
24996
25314
  };
24997
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
25315
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
24998
25316
  }
24999
25317
 
25000
25318
  // src/routes/admin-collections.ts
25001
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
25319
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
25002
25320
  var adminCollectionsRoutes = new hono.Hono();
25003
- adminCollectionsRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
25321
+ adminCollectionsRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
25004
25322
  adminCollectionsRoutes.get("/", async (c) => {
25005
25323
  try {
25006
25324
  const user = c.get("user");
@@ -25017,7 +25335,7 @@ adminCollectionsRoutes.get("/", async (c) => {
25017
25335
  results = queryResults.results ?? [];
25018
25336
  } catch (e) {
25019
25337
  }
25020
- const codeCollections = chunkAT4ZQGEZ_cjs.getCollectionRegistry().list();
25338
+ const codeCollections = chunkYAURVGUK_cjs.getCollectionRegistry().list();
25021
25339
  const codeCollectionsMap = new Map(
25022
25340
  codeCollections.map((cfg) => {
25023
25341
  const fieldCount = cfg.schema?.properties ? Object.keys(cfg.schema.properties).length : 0;
@@ -25031,7 +25349,7 @@ adminCollectionsRoutes.get("/", async (c) => {
25031
25349
  field_count: fieldCount,
25032
25350
  managed: cfg.managed !== false,
25033
25351
  source_type: "code",
25034
- internal: chunkAT4ZQGEZ_cjs.isCodeCollectionInternal(cfg),
25352
+ internal: chunkYAURVGUK_cjs.isCodeCollectionInternal(cfg),
25035
25353
  versioning: cfg.versioning === true
25036
25354
  }];
25037
25355
  })
@@ -25070,7 +25388,7 @@ adminCollectionsRoutes.get("/", async (c) => {
25070
25388
  field_count: fieldCount,
25071
25389
  managed: false,
25072
25390
  source_type: row.source === "code" || row.source === "system" || row.source === "plugin" ? "code" : "user",
25073
- internal: chunkAT4ZQGEZ_cjs.isDbDocTypeInternal(row.source),
25391
+ internal: chunkYAURVGUK_cjs.isDbDocTypeInternal(row.source),
25074
25392
  versioning: dbVersioning
25075
25393
  };
25076
25394
  });
@@ -25227,11 +25545,11 @@ export const myPlugin = definePlugin({
25227
25545
  `;
25228
25546
  const u = user;
25229
25547
  return c.html(
25230
- chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst({
25548
+ chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst({
25231
25549
  title: "Add a Collection",
25232
25550
  pageTitle: "Add a Collection",
25233
25551
  currentPath: "/admin/collections",
25234
- version: chunkC3ZQP5NW_cjs.getCoreVersion(),
25552
+ version: chunkBJ6NJL2E_cjs.getCoreVersion(),
25235
25553
  user: u ? { name: u.email ?? "Admin", email: u.email ?? "", role: u.role ?? "admin" } : void 0,
25236
25554
  content
25237
25555
  })
@@ -25248,7 +25566,7 @@ adminCollectionsRoutes.get("/:id", async (c) => {
25248
25566
  collection = await stmt.bind(id).first();
25249
25567
  } catch {
25250
25568
  }
25251
- const registryRecord = chunkAT4ZQGEZ_cjs.getCollectionRegistry().getById(id) ?? chunkAT4ZQGEZ_cjs.getCollectionRegistry().getByName(id);
25569
+ const registryRecord = chunkYAURVGUK_cjs.getCollectionRegistry().getById(id) ?? chunkYAURVGUK_cjs.getCollectionRegistry().getByName(id);
25252
25570
  if (!collection && !registryRecord) {
25253
25571
  const formData2 = {
25254
25572
  isEdit: true,
@@ -25295,9 +25613,9 @@ adminCollectionsRoutes.get("/:id", async (c) => {
25295
25613
  isCodeDriven = true;
25296
25614
  }
25297
25615
  const [tinymceActive, quillActive, mdxeditorActive] = await Promise.all([
25298
- chunkIS54FPB7_cjs.isPluginActive(db, "tinymce-plugin"),
25299
- chunkIS54FPB7_cjs.isPluginActive(db, "quill-editor"),
25300
- chunkIS54FPB7_cjs.isPluginActive(db, "easy-mdx")
25616
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "tinymce-plugin"),
25617
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "quill-editor"),
25618
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "easy-mdx")
25301
25619
  ]);
25302
25620
  const displayName = collection?.display_name ?? registryRecord?.displayName ?? id;
25303
25621
  const formData = {
@@ -25317,9 +25635,9 @@ adminCollectionsRoutes.get("/:id", async (c) => {
25317
25635
  console.error("Error fetching collection:", error);
25318
25636
  const user = c.get("user");
25319
25637
  const [tinymceActive, quillActive, mdxeditorActive] = await Promise.all([
25320
- chunkIS54FPB7_cjs.isPluginActive(db, "tinymce-plugin"),
25321
- chunkIS54FPB7_cjs.isPluginActive(db, "quill-editor"),
25322
- chunkIS54FPB7_cjs.isPluginActive(db, "easy-mdx")
25638
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "tinymce-plugin"),
25639
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "quill-editor"),
25640
+ chunkE7E3NL3Y_cjs.isPluginActive(db, "easy-mdx")
25323
25641
  ]);
25324
25642
  const formData = {
25325
25643
  isEdit: true,
@@ -25333,7 +25651,7 @@ adminCollectionsRoutes.get("/:id", async (c) => {
25333
25651
  });
25334
25652
 
25335
25653
  // src/templates/pages/admin-settings.template.ts
25336
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
25654
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
25337
25655
  function renderSettingsPage(data) {
25338
25656
  const activeTab = data.activeTab || "general";
25339
25657
  const pageContent = `
@@ -25723,7 +26041,7 @@ function renderSettingsPage(data) {
25723
26041
  version: data.version,
25724
26042
  content: pageContent
25725
26043
  };
25726
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
26044
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
25727
26045
  }
25728
26046
  function renderTabButton(tabId, label, iconPath, activeTab) {
25729
26047
  const isActive = activeTab === tabId;
@@ -26268,7 +26586,7 @@ function renderDatabaseToolsSettings(settings) {
26268
26586
 
26269
26587
  // src/routes/admin-settings.ts
26270
26588
  var adminSettingsRoutes = new hono.Hono();
26271
- adminSettingsRoutes.use("*", chunkIS54FPB7_cjs.requireAuth());
26589
+ adminSettingsRoutes.use("*", chunkE7E3NL3Y_cjs.requireAuth());
26272
26590
  function getMockSettings(user) {
26273
26591
  return {
26274
26592
  general: {
@@ -26371,7 +26689,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
26371
26689
  adminSettingsRoutes.get("/api/migrations/status", async (c) => {
26372
26690
  try {
26373
26691
  const db = c.env.DB;
26374
- const migrationService = new chunkVGHEKIJL_cjs.MigrationService(db);
26692
+ const migrationService = new chunkMOGK3U4A_cjs.MigrationService(db);
26375
26693
  const status = await migrationService.getMigrationStatus();
26376
26694
  return c.json({
26377
26695
  success: true,
@@ -26411,7 +26729,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
26411
26729
  adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
26412
26730
  try {
26413
26731
  const db = c.env.DB;
26414
- const migrationService = new chunkVGHEKIJL_cjs.MigrationService(db);
26732
+ const migrationService = new chunkMOGK3U4A_cjs.MigrationService(db);
26415
26733
  const validation = await migrationService.validateSchema();
26416
26734
  return c.json({
26417
26735
  success: true,
@@ -26669,7 +26987,7 @@ adminSettingsRoutes.post("/", async (c) => {
26669
26987
  });
26670
26988
 
26671
26989
  // src/templates/pages/admin-api-reference.template.ts
26672
- chunkUYJQDDEE_cjs.init_admin_layout_catalyst_template();
26990
+ chunkMI2YXCK4_cjs.init_admin_layout_catalyst_template();
26673
26991
  function renderAuthBadge(auth) {
26674
26992
  if (auth === true) {
26675
26993
  return `
@@ -27000,20 +27318,20 @@ function renderAPIReferencePage(data) {
27000
27318
  version: data.version,
27001
27319
  content: pageContent
27002
27320
  };
27003
- return chunkUYJQDDEE_cjs.renderAdminLayoutCatalyst(layoutData);
27321
+ return chunkMI2YXCK4_cjs.renderAdminLayoutCatalyst(layoutData);
27004
27322
  }
27005
27323
 
27006
27324
  // src/routes/admin-api-reference.ts
27007
- var VERSION2 = chunkC3ZQP5NW_cjs.getCoreVersion();
27325
+ var VERSION2 = chunkBJ6NJL2E_cjs.getCoreVersion();
27008
27326
  var router2 = new hono.Hono();
27009
- router2.use("*", chunkIS54FPB7_cjs.requireAuth());
27327
+ router2.use("*", chunkE7E3NL3Y_cjs.requireAuth());
27010
27328
  router2.get("/", async (c) => {
27011
27329
  const user = c.get("user");
27012
27330
  try {
27013
27331
  const app2 = chunk36Z3AKX3_cjs.getAppInstance();
27014
27332
  const baseEndpoints = chunk36Z3AKX3_cjs.buildRouteList(app2);
27015
27333
  const collectionRoutes = [];
27016
- const collections = chunkAT4ZQGEZ_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
27334
+ const collections = chunkYAURVGUK_cjs.getCollectionRegistry().listActive().filter((r) => !r.internal);
27017
27335
  const methodOrder = { GET: 0, POST: 1, PUT: 2, PATCH: 3, DELETE: 4 };
27018
27336
  for (const col of collections) {
27019
27337
  const displayName = col.displayName || col.name;
@@ -27110,8 +27428,10 @@ exports.createUserProfilesPlugin = createUserProfilesPlugin;
27110
27428
  exports.defineUserProfile = defineUserProfile;
27111
27429
  exports.denyIfNotAllowed = denyIfNotAllowed;
27112
27430
  exports.dispatchHookEvent = dispatchHookEvent;
27431
+ exports.effectiveTenantForType = effectiveTenantForType;
27113
27432
  exports.getConfirmationDialogScript = getConfirmationDialogScript2;
27114
27433
  exports.getCustomData = getCustomData;
27434
+ exports.getDocumentRequestContext = getDocumentRequestContext;
27115
27435
  exports.getEmailService = getEmailService;
27116
27436
  exports.getUserProfileConfig = getUserProfileConfig;
27117
27437
  exports.getVariablesCached = getVariablesCached;
@@ -27129,7 +27449,8 @@ exports.router2 = router2;
27129
27449
  exports.setEmailService = setEmailService;
27130
27450
  exports.setVariablesCache = setVariablesCache;
27131
27451
  exports.test_cleanup_default = test_cleanup_default;
27452
+ exports.updateDocumentSchema = updateDocumentSchema;
27132
27453
  exports.userProfilesPlugin = userProfilesPlugin;
27133
27454
  exports.userRoutes = userRoutes;
27134
- //# sourceMappingURL=chunk-CTYLCWMO.cjs.map
27135
- //# sourceMappingURL=chunk-CTYLCWMO.cjs.map
27455
+ //# sourceMappingURL=chunk-6OA77UM7.cjs.map
27456
+ //# sourceMappingURL=chunk-6OA77UM7.cjs.map