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

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 (106) hide show
  1. package/dist/adapters.cjs +512 -0
  2. package/dist/adapters.cjs.map +1 -0
  3. package/dist/adapters.d.cts +342 -0
  4. package/dist/adapters.d.ts +342 -0
  5. package/dist/adapters.js +501 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/admin-documents-form.template-55V2ZGWT.js +6 -0
  8. package/dist/{admin-documents-form.template-DDSH6ROU.js.map → admin-documents-form.template-55V2ZGWT.js.map} +1 -1
  9. package/dist/admin-documents-form.template-YNAVMJCV.cjs +19 -0
  10. package/dist/{admin-layout-catalyst.template-YQ4EMF2J.js.map → admin-documents-form.template-YNAVMJCV.cjs.map} +1 -1
  11. package/dist/admin-layout-catalyst.template-3XZXDGF7.cjs +21 -0
  12. package/dist/{admin-layout-catalyst.template-KDHKVLXR.cjs.map → admin-layout-catalyst.template-3XZXDGF7.cjs.map} +1 -1
  13. package/dist/admin-layout-catalyst.template-6UQGLCKI.js +7 -0
  14. package/dist/{admin-documents-form.template-LSZKGA5J.cjs.map → admin-layout-catalyst.template-6UQGLCKI.js.map} +1 -1
  15. package/dist/{chunk-I2DM27HP.js → chunk-2MIYYAO4.js} +49 -4
  16. package/dist/chunk-2MIYYAO4.js.map +1 -0
  17. package/dist/{chunk-EUJAEB6W.cjs → chunk-4WMZGHIC.cjs} +53 -4
  18. package/dist/chunk-4WMZGHIC.cjs.map +1 -0
  19. package/dist/{chunk-2FCKKOA5.js → chunk-A46IRC5A.js} +3 -3
  20. package/dist/chunk-A46IRC5A.js.map +1 -0
  21. package/dist/{chunk-PYZYU7EK.cjs → chunk-AT4ZQGEZ.cjs} +17 -2
  22. package/dist/chunk-AT4ZQGEZ.cjs.map +1 -0
  23. package/dist/{chunk-CRGUD4KC.cjs → chunk-BBEKHLTW.cjs} +9 -9
  24. package/dist/{chunk-CRGUD4KC.cjs.map → chunk-BBEKHLTW.cjs.map} +1 -1
  25. package/dist/{chunk-RMRJGMDE.js → chunk-BHND5PEV.js} +3 -3
  26. package/dist/{chunk-RMRJGMDE.js.map → chunk-BHND5PEV.js.map} +1 -1
  27. package/dist/{chunk-OBA2RYZN.js → chunk-BJROXF4N.js} +3 -3
  28. package/dist/{chunk-OBA2RYZN.js.map → chunk-BJROXF4N.js.map} +1 -1
  29. package/dist/{chunk-QWSR4THH.cjs → chunk-C3ZQP5NW.cjs} +12 -5
  30. package/dist/chunk-C3ZQP5NW.cjs.map +1 -0
  31. package/dist/{chunk-JFLZHOQP.cjs → chunk-CTYLCWMO.cjs} +486 -409
  32. package/dist/chunk-CTYLCWMO.cjs.map +1 -0
  33. package/dist/{chunk-CHWT5KBK.js → chunk-DIBCKCUE.js} +6 -6
  34. package/dist/{chunk-CHWT5KBK.js.map → chunk-DIBCKCUE.js.map} +1 -1
  35. package/dist/{chunk-73O4GPJ6.js → chunk-DPLJYTHE.js} +12 -5
  36. package/dist/chunk-DPLJYTHE.js.map +1 -0
  37. package/dist/{chunk-PXNTCCPE.cjs → chunk-E66UVONV.cjs} +8 -8
  38. package/dist/{chunk-PXNTCCPE.cjs.map → chunk-E66UVONV.cjs.map} +1 -1
  39. package/dist/{chunk-FTXGZ3BF.cjs → chunk-IS54FPB7.cjs} +18 -18
  40. package/dist/{chunk-FTXGZ3BF.cjs.map → chunk-IS54FPB7.cjs.map} +1 -1
  41. package/dist/{chunk-N32OWET6.cjs → chunk-JMJM7DMM.cjs} +5 -5
  42. package/dist/{chunk-N32OWET6.cjs.map → chunk-JMJM7DMM.cjs.map} +1 -1
  43. package/dist/{chunk-DOMXI5PM.js → chunk-KS24NPPL.js} +17 -2
  44. package/dist/chunk-KS24NPPL.js.map +1 -0
  45. package/dist/{chunk-MGHHPNJN.cjs → chunk-RFBW5RRH.cjs} +11 -59
  46. package/dist/chunk-RFBW5RRH.cjs.map +1 -0
  47. package/dist/{chunk-6KNTZ67K.js → chunk-SK5CHFJP.js} +192 -116
  48. package/dist/chunk-SK5CHFJP.js.map +1 -0
  49. package/dist/{chunk-5V62WT6M.js → chunk-THFZSDIU.js} +4 -2
  50. package/dist/chunk-THFZSDIU.js.map +1 -0
  51. package/dist/{chunk-JQISFW6U.js → chunk-URIBTX27.js} +3 -3
  52. package/dist/{chunk-JQISFW6U.js.map → chunk-URIBTX27.js.map} +1 -1
  53. package/dist/{chunk-K623Q6WD.cjs → chunk-UYJQDDEE.cjs} +4 -2
  54. package/dist/chunk-UYJQDDEE.cjs.map +1 -0
  55. package/dist/{chunk-3CT3SUR4.cjs → chunk-VGHEKIJL.cjs} +3 -3
  56. package/dist/chunk-VGHEKIJL.cjs.map +1 -0
  57. package/dist/{chunk-GB2Q2DNK.js → chunk-XXRR54AB.js} +5 -49
  58. package/dist/chunk-XXRR54AB.js.map +1 -0
  59. package/dist/{define-plugin-DsKiGu1q.d.cts → define-plugin-DMcvEy_C.d.cts} +33 -0
  60. package/dist/{define-plugin-BYIey6sp.d.ts → define-plugin-D_6D9vVZ.d.ts} +33 -0
  61. package/dist/index.cjs +1758 -326
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +2 -2
  64. package/dist/index.d.ts +2 -2
  65. package/dist/index.js +1453 -21
  66. package/dist/index.js.map +1 -1
  67. package/dist/middleware.cjs +34 -34
  68. package/dist/middleware.js +5 -5
  69. package/dist/migrations-XQ6EX6HX.js +4 -0
  70. package/dist/{migrations-HLK2QF4T.js.map → migrations-XQ6EX6HX.js.map} +1 -1
  71. package/dist/migrations-ZD7Y5NUC.cjs +13 -0
  72. package/dist/{migrations-UIMA6ZL6.cjs.map → migrations-ZD7Y5NUC.cjs.map} +1 -1
  73. package/dist/plugins.cjs +40 -40
  74. package/dist/plugins.d.cts +1 -1
  75. package/dist/plugins.d.ts +1 -1
  76. package/dist/plugins.js +3 -3
  77. package/dist/routes.cjs +29 -29
  78. package/dist/routes.js +9 -9
  79. package/dist/services.cjs +18 -18
  80. package/dist/services.js +2 -2
  81. package/dist/templates.cjs +17 -17
  82. package/dist/templates.js +3 -3
  83. package/dist/utils.cjs +3 -3
  84. package/dist/utils.js +1 -1
  85. package/migrations/0001_core.sql +7 -1
  86. package/package.json +10 -3
  87. package/dist/admin-documents-form.template-DDSH6ROU.js +0 -6
  88. package/dist/admin-documents-form.template-LSZKGA5J.cjs +0 -19
  89. package/dist/admin-layout-catalyst.template-KDHKVLXR.cjs +0 -21
  90. package/dist/admin-layout-catalyst.template-YQ4EMF2J.js +0 -7
  91. package/dist/chunk-2FCKKOA5.js.map +0 -1
  92. package/dist/chunk-3CT3SUR4.cjs.map +0 -1
  93. package/dist/chunk-5V62WT6M.js.map +0 -1
  94. package/dist/chunk-6KNTZ67K.js.map +0 -1
  95. package/dist/chunk-73O4GPJ6.js.map +0 -1
  96. package/dist/chunk-DOMXI5PM.js.map +0 -1
  97. package/dist/chunk-EUJAEB6W.cjs.map +0 -1
  98. package/dist/chunk-GB2Q2DNK.js.map +0 -1
  99. package/dist/chunk-I2DM27HP.js.map +0 -1
  100. package/dist/chunk-JFLZHOQP.cjs.map +0 -1
  101. package/dist/chunk-K623Q6WD.cjs.map +0 -1
  102. package/dist/chunk-MGHHPNJN.cjs.map +0 -1
  103. package/dist/chunk-PYZYU7EK.cjs.map +0 -1
  104. package/dist/chunk-QWSR4THH.cjs.map +0 -1
  105. package/dist/migrations-HLK2QF4T.js +0 -4
  106. package/dist/migrations-UIMA6ZL6.cjs +0 -13
@@ -1,18 +1,18 @@
1
1
  import { isFirstUserRegistration, isRegistrationEnabled, authValidationService } from './chunk-VVYVFCML.js';
2
- import { renderDocumentFormPage, renderAlert } from './chunk-RMRJGMDE.js';
2
+ import { renderDocumentFormPage, renderAlert } from './chunk-BHND5PEV.js';
3
3
  import { getCacheService, CACHE_CONFIGS, SettingsService, getLogger, getAppInstance, buildRouteList, CATEGORY_INFO } from './chunk-2XAQTPAE.js';
4
- import { DocumentTypeRegistry, requireAuth, requireRole, optionalAuth, isPluginActive, rateLimit, AuthManager, getJwtExpirySecondsFromDb, getJwtRefreshGraceSecondsFromDb, logActivity, generateCsrfToken } from './chunk-CHWT5KBK.js';
4
+ import { DocumentTypeRegistry, requireAuth, requireRole, optionalAuth, isPluginActive, rateLimit, AuthManager, getJwtExpirySecondsFromDb, getJwtRefreshGraceSecondsFromDb, logActivity, generateCsrfToken } from './chunk-DIBCKCUE.js';
5
5
  import { DocumentsService, documentSecondsToMs, RbacService } from './chunk-3TMTYJAJ.js';
6
6
  import { nanoid } from './chunk-UWUP2VMT.js';
7
- import { getCollectionRegistry, collectionRecordToRow, loadCollectionConfigs, getVisibleCollections, PluginService, PLUGIN_REGISTRY, findPluginByCodeName, isCodeCollectionInternal, isDbDocTypeInternal, invalidateTenantCache } from './chunk-DOMXI5PM.js';
8
- import { MigrationService } from './chunk-2FCKKOA5.js';
9
- import { renderCheckboxPage, renderAlert as renderAlert$1, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-OBA2RYZN.js';
10
- import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
11
- import { createServiceSingleton, definePlugin, getPluginDefinition, applySchemaDefaults, renderSchemaFields, parseFormDataToSettings } from './chunk-I2DM27HP.js';
7
+ import { getCollectionRegistry, collectionRecordToRow, loadCollectionConfigs, getVisibleCollections, PluginService, PLUGIN_REGISTRY, findPluginByCodeName, isCodeCollectionInternal, isDbDocTypeInternal, invalidateTenantCache } from './chunk-KS24NPPL.js';
8
+ import { MigrationService } from './chunk-A46IRC5A.js';
9
+ import { renderCheckboxPage, renderAlert as renderAlert$1, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-BJROXF4N.js';
10
+ import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-THFZSDIU.js';
11
+ import { createServiceSingleton, definePlugin, getPluginDefinition, applySchemaDefaults, renderSchemaFields, parseFormDataToSettings, getPluginMenu } from './chunk-2MIYYAO4.js';
12
12
  import { hasHookSystem, getTypedHooks } from './chunk-RNZFGN4R.js';
13
13
  import { QueryFilterBuilder, getBlocksFieldConfig, parseBlocksValue } from './chunk-QZGABF2M.js';
14
14
  import { metricsTracker } from './chunk-FICTAGD4.js';
15
- import { getCoreVersion } from './chunk-73O4GPJ6.js';
15
+ import { getCoreVersion } from './chunk-DPLJYTHE.js';
16
16
  import { escapeHtml, sanitizeRichText, sanitizeInput } from './chunk-TQABQWOP.js';
17
17
  import { Hono } from 'hono';
18
18
  import { z } from 'zod';
@@ -3399,7 +3399,7 @@ adminApiRoutes.patch("/collections/:id", collectionsReadOnly);
3399
3399
  adminApiRoutes.delete("/collections/:id", collectionsReadOnly);
3400
3400
  adminApiRoutes.get("/migrations/status", async (c) => {
3401
3401
  try {
3402
- const { MigrationService: MigrationService2 } = await import('./migrations-HLK2QF4T.js');
3402
+ const { MigrationService: MigrationService2 } = await import('./migrations-XQ6EX6HX.js');
3403
3403
  const db = c.env.DB;
3404
3404
  const migrationService = new MigrationService2(db);
3405
3405
  const status = await migrationService.getMigrationStatus();
@@ -3442,7 +3442,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
3442
3442
  });
3443
3443
  adminApiRoutes.get("/migrations/validate", async (c) => {
3444
3444
  try {
3445
- const { MigrationService: MigrationService2 } = await import('./migrations-HLK2QF4T.js');
3445
+ const { MigrationService: MigrationService2 } = await import('./migrations-XQ6EX6HX.js');
3446
3446
  const db = c.env.DB;
3447
3447
  const migrationService = new MigrationService2(db);
3448
3448
  const validation = await migrationService.validateSchema();
@@ -13071,7 +13071,7 @@ adminContentRoutes.post("/documents/:typeId/:documentId/delete", async (c) => {
13071
13071
  adminContentRoutes.get("/documents/:typeId/:rootId/versions", async (c) => {
13072
13072
  const { typeId, rootId } = c.req.param();
13073
13073
  const db = c.env.DB;
13074
- const { renderVersionHistoryFragment } = await import('./admin-documents-form.template-DDSH6ROU.js');
13074
+ const { renderVersionHistoryFragment } = await import('./admin-documents-form.template-55V2ZGWT.js');
13075
13075
  const registry = new DocumentTypeRegistry(db);
13076
13076
  const docType = await registry.findById(typeId);
13077
13077
  if (!docType) return c.html("<div>Unknown type.</div>", 404);
@@ -19344,8 +19344,9 @@ function escapeHtmlAttr(value) {
19344
19344
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19345
19345
  }
19346
19346
  function renderPluginSettingsPage(data) {
19347
- const { plugin, activity = [], user } = data;
19348
- const hasUserSettings = plugin.id in pluginSettingsComponents || Object.keys(plugin.settings || {}).some((k) => !k.startsWith("_"));
19347
+ const { plugin, activity = [], user, settingsTabData } = data;
19348
+ const pluginDef = getPluginDefinition(plugin.id || plugin.name);
19349
+ const hasUserSettings = pluginDef?.settingsTabContent != null || Object.keys(plugin.settings || {}).some((k) => !k.startsWith("_"));
19349
19350
  const defaultTab = hasUserSettings ? "settings" : "info";
19350
19351
  const pageContent = `
19351
19352
  <div class="w-full px-4 sm:px-6 lg:px-8 py-6">
@@ -19425,7 +19426,7 @@ function renderPluginSettingsPage(data) {
19425
19426
  <div id="tab-content">
19426
19427
  <!-- Settings Tab -->
19427
19428
  <div id="settings-content" class="tab-content hidden">
19428
- ${hasUserSettings ? renderSettingsTab(plugin) : ""}
19429
+ ${hasUserSettings ? renderSettingsTab(plugin, settingsTabData) : ""}
19429
19430
  </div>
19430
19431
 
19431
19432
  <!-- Activity Tab -->
@@ -19646,10 +19647,14 @@ function renderToggleButton(plugin) {
19646
19647
  }
19647
19648
  return plugin.status === "active" ? `<button onclick="togglePlugin('${plugin.id}', 'deactivate')" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors">Deactivate</button>` : `<button onclick="togglePlugin('${plugin.id}', 'activate')" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors">Activate</button>`;
19648
19649
  }
19649
- function renderSettingsTab(plugin) {
19650
+ function renderSettingsTab(plugin, settingsTabData) {
19650
19651
  const settings = plugin.settings || {};
19651
19652
  const pluginId = plugin.id || plugin.name;
19652
19653
  const hasUserKeys = Object.keys(settings).some((k) => !k.startsWith("_"));
19654
+ const pluginDef = getPluginDefinition(pluginId);
19655
+ if (pluginDef?.settingsTabContent) {
19656
+ return pluginDef.settingsTabContent.render({ plugin, settings, data: settingsTabData });
19657
+ }
19653
19658
  const customRenderer = pluginSettingsComponents[pluginId];
19654
19659
  if (customRenderer) {
19655
19660
  return `
@@ -19996,8 +20001,7 @@ function formatTimestamp(timestamp) {
19996
20001
  }
19997
20002
  var pluginSettingsComponents = {
19998
20003
  "otp-login": renderOTPLoginSettingsContent,
19999
- "email": renderEmailSettingsContent,
20000
- "oauth-providers": renderOAuthProvidersSettingsContent
20004
+ "email": renderEmailSettingsContent
20001
20005
  };
20002
20006
  function renderOTPLoginSettingsContent(plugin, settings) {
20003
20007
  const siteName = settings.siteName || "SonicJS";
@@ -20720,99 +20724,157 @@ defineUserProfile({
20720
20724
  </div>
20721
20725
  `;
20722
20726
  }
20723
- function renderOAuthProvidersSettingsContent(_plugin, settings) {
20724
- const providers = settings.providers || {};
20725
- const github = providers.github || {};
20726
- const google = providers.google || {};
20727
- const inputClass = "w-full px-3 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none text-white";
20728
- function renderProviderCard(name, label, icon, data) {
20729
- const clientId = escapeHtmlAttr(data.clientId || "");
20730
- const clientSecret = escapeHtmlAttr(data.clientSecret || "");
20731
- const enabled = data.enabled === true;
20732
- return `
20733
- <div class="backdrop-blur-md bg-white/5 rounded-xl border border-white/10 p-6">
20734
- <div class="flex items-center justify-between mb-4">
20735
- <h3 class="text-lg font-semibold text-white flex items-center gap-2">
20736
- <span>${icon}</span> ${label}
20737
- </h3>
20738
- <label class="relative inline-flex items-center cursor-pointer">
20739
- <input type="checkbox" id="oauth_${name}_enabled" ${enabled ? "checked" : ""} class="sr-only peer">
20740
- <div class="w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
20741
- <span class="ml-2 text-sm text-gray-300">Enabled</span>
20742
- </label>
20743
- </div>
20744
- <div class="space-y-4">
20745
- <div>
20746
- <label for="oauth_${name}_clientId" class="block text-sm font-medium text-gray-300 mb-2">Client ID</label>
20747
- <input type="text" id="oauth_${name}_clientId" value="${clientId}" placeholder="${label} OAuth Client ID" class="${inputClass}">
20748
- </div>
20749
- <div>
20750
- <label for="oauth_${name}_clientSecret" class="block text-sm font-medium text-gray-300 mb-2">Client Secret</label>
20751
- <input type="password" id="oauth_${name}_clientSecret" value="${clientSecret}" placeholder="${label} OAuth Client Secret" class="${inputClass}">
20752
- </div>
20753
- </div>
20754
- </div>
20755
- `;
20756
- }
20757
- return `
20758
- <div class="space-y-6">
20759
- <div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4">
20760
- <p class="text-blue-200 text-sm">
20761
- Configure OAuth provider credentials below. Credentials are stored securely and used server-side only.
20762
- Make sure to add the callback URL <code class="text-blue-300">/auth/oauth/[provider]/callback</code> to your OAuth app's allowed redirect URIs.
20763
- </p>
20764
- </div>
20765
-
20766
- ${renderProviderCard("github", "GitHub", "\u{1F419}", github)}
20767
- ${renderProviderCard("google", "Google", "\u{1F535}", google)}
20768
- </div>
20769
-
20770
- <script>
20771
- (function () {
20772
- const origSave = window.saveSettings;
20773
- window.saveSettings = async function () {
20774
- const settings = {
20775
- providers: {
20776
- github: {
20777
- clientId: document.getElementById('oauth_github_clientId').value,
20778
- clientSecret: document.getElementById('oauth_github_clientSecret').value,
20779
- enabled: document.getElementById('oauth_github_enabled').checked,
20780
- },
20781
- google: {
20782
- clientId: document.getElementById('oauth_google_clientId').value,
20783
- clientSecret: document.getElementById('oauth_google_clientSecret').value,
20784
- enabled: document.getElementById('oauth_google_enabled').checked,
20785
- },
20786
- }
20787
- };
20788
-
20789
- const saveButton = document.getElementById('save-button');
20790
- saveButton.disabled = true;
20791
- saveButton.textContent = 'Saving...';
20792
20727
 
20793
- try {
20794
- const response = await fetch('/admin/plugins/oauth-providers/settings', {
20795
- method: 'POST',
20796
- headers: { 'Content-Type': 'application/json' },
20797
- body: JSON.stringify(settings),
20798
- });
20799
- const result = await response.json();
20800
- if (result.success) {
20801
- showNotification('Settings saved successfully', 'success');
20802
- setTimeout(() => location.reload(), 1000);
20803
- } else {
20804
- throw new Error(result.error || 'Failed to save settings');
20805
- }
20806
- } catch (error) {
20807
- showNotification(error.message, 'error');
20808
- } finally {
20809
- saveButton.disabled = false;
20810
- saveButton.textContent = 'Save Settings';
20811
- }
20812
- };
20813
- })();
20814
- </script>
20815
- `;
20728
+ // src/plugins/core-plugins/menu-plugin/services/menu-reconcile.ts
20729
+ async function upsertPluginRow(db, slug, entry, visible) {
20730
+ const now = Math.floor(Date.now() / 1e3);
20731
+ const existing = await db.prepare(
20732
+ `SELECT id FROM documents
20733
+ WHERE slug = ? AND type_id = 'menu_item' AND tenant_id = 'default'
20734
+ AND is_current_draft = 1 AND deleted_at IS NULL`
20735
+ ).bind(slug).first();
20736
+ if (existing) {
20737
+ await db.prepare(
20738
+ `UPDATE documents
20739
+ SET data = json_set(data, '$.url', ?, '$.pluginId', ?, '$.visible', ?),
20740
+ visible = ?,
20741
+ updated_at = ?
20742
+ WHERE id = ? AND tenant_id = 'default'`
20743
+ ).bind(entry.url, entry.pluginId, visible ? 1 : 0, visible ? 1 : 0, now, existing.id).run();
20744
+ return;
20745
+ }
20746
+ const id = nanoid();
20747
+ const data = JSON.stringify({
20748
+ label: entry.label,
20749
+ url: entry.url,
20750
+ icon: entry.icon,
20751
+ target: "_self",
20752
+ isExternal: false,
20753
+ visible,
20754
+ parent: null,
20755
+ source: "plugin",
20756
+ pluginId: entry.pluginId,
20757
+ permissions: [],
20758
+ lockedFields: ["url"],
20759
+ sortOrder: entry.sortOrder
20760
+ });
20761
+ await db.prepare(
20762
+ `INSERT INTO documents (id, root_id, type_id, type_version, version_of_id, version_number,
20763
+ is_current_draft, is_published, status, parent_root_id, slug, path, title, zone,
20764
+ sort_order, visible, published_at, scheduled_at, expires_at, deleted_at,
20765
+ tenant_id, locale, translation_group_id, data, metadata,
20766
+ owner_id, created_by, updated_by, created_at, updated_at)
20767
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
20768
+ ).bind(
20769
+ id,
20770
+ id,
20771
+ "menu_item",
20772
+ 1,
20773
+ null,
20774
+ 1,
20775
+ 1,
20776
+ 0,
20777
+ "draft",
20778
+ "",
20779
+ slug,
20780
+ null,
20781
+ entry.label,
20782
+ null,
20783
+ entry.sortOrder,
20784
+ visible ? 1 : 0,
20785
+ null,
20786
+ null,
20787
+ null,
20788
+ null,
20789
+ "default",
20790
+ "default",
20791
+ "",
20792
+ data,
20793
+ "{}",
20794
+ null,
20795
+ null,
20796
+ null,
20797
+ now,
20798
+ now
20799
+ ).run();
20800
+ }
20801
+ async function deactivateStalePluginRows(db, activePluginIds) {
20802
+ const now = Math.floor(Date.now() / 1e3);
20803
+ const rows = await db.prepare(
20804
+ `SELECT id, data FROM documents
20805
+ WHERE type_id = 'menu_item' AND tenant_id = 'default'
20806
+ AND is_current_draft = 1 AND deleted_at IS NULL
20807
+ AND json_extract(data, '$.source') = 'plugin'`
20808
+ ).all();
20809
+ const stale = (rows.results ?? []).filter((row) => {
20810
+ let pluginId;
20811
+ try {
20812
+ pluginId = JSON.parse(row.data)?.pluginId;
20813
+ } catch {
20814
+ return false;
20815
+ }
20816
+ return pluginId !== void 0 && !activePluginIds.has(pluginId);
20817
+ });
20818
+ if (stale.length === 0) return;
20819
+ await db.batch(
20820
+ stale.map(
20821
+ (row) => db.prepare(
20822
+ `UPDATE documents
20823
+ SET data = json_set(data, '$.visible', 0), visible = 0, updated_at = ?
20824
+ WHERE id = ? AND tenant_id = 'default'`
20825
+ ).bind(now, row.id)
20826
+ )
20827
+ );
20828
+ }
20829
+ async function fetchActivePluginIds(db) {
20830
+ try {
20831
+ const rows = await db.prepare(
20832
+ `SELECT slug FROM documents
20833
+ WHERE type_id = 'plugin' AND tenant_id = 'default'
20834
+ AND is_current_draft = 1 AND deleted_at IS NULL
20835
+ AND json_extract(data, '$.status') = 'active'`
20836
+ ).all();
20837
+ return new Set((rows.results ?? []).map((r) => r.slug));
20838
+ } catch {
20839
+ return /* @__PURE__ */ new Set();
20840
+ }
20841
+ }
20842
+ async function reconcileMenuFromPlugins(db) {
20843
+ try {
20844
+ const singletonEntries = getPluginMenu();
20845
+ const manifestEntries = Object.values(PLUGIN_REGISTRY).filter(
20846
+ (p) => p.adminMenu !== null
20847
+ );
20848
+ const byPluginId = /* @__PURE__ */ new Map();
20849
+ manifestEntries.forEach((p, idx) => {
20850
+ const menu = p.adminMenu;
20851
+ byPluginId.set(p.id, {
20852
+ pluginId: p.id,
20853
+ label: menu.label,
20854
+ url: menu.path,
20855
+ icon: menu.icon,
20856
+ sortOrder: 100 + idx * 10
20857
+ });
20858
+ });
20859
+ singletonEntries.forEach((entry, idx) => {
20860
+ const pathParts = entry.path.replace(/\/$/, "").split("/");
20861
+ const pluginId = pathParts[pathParts.length - 1] || entry.label.toLowerCase().replace(/\s+/g, "-");
20862
+ byPluginId.set(pluginId, {
20863
+ pluginId,
20864
+ label: entry.label,
20865
+ url: entry.path,
20866
+ icon: entry.icon ?? "puzzle-piece",
20867
+ sortOrder: 100 + idx * 10
20868
+ });
20869
+ });
20870
+ const activeIds = await fetchActivePluginIds(db);
20871
+ for (const [pluginId, entry] of byPluginId) {
20872
+ const slug = `menu:plugin:${pluginId}`;
20873
+ await upsertPluginRow(db, slug, entry, activeIds.has(pluginId));
20874
+ }
20875
+ await deactivateStalePluginRows(db, activeIds);
20876
+ } catch {
20877
+ }
20816
20878
  }
20817
20879
 
20818
20880
  // src/routes/admin-plugins.ts
@@ -20847,9 +20909,11 @@ adminPluginRoutes.get("/", async (c) => {
20847
20909
  } catch (error) {
20848
20910
  console.error("Error loading plugins:", error);
20849
20911
  }
20850
- const installedPluginIds = new Set(installedPlugins.map((p) => p.id));
20912
+ const registryIds = new Set(AVAILABLE_PLUGINS.map((p) => p.id));
20913
+ const validInstalledPlugins = installedPlugins.filter((p) => registryIds.has(p.id));
20914
+ const installedPluginIds = new Set(validInstalledPlugins.map((p) => p.id));
20851
20915
  const uninstalledPlugins = AVAILABLE_PLUGINS.filter((p) => !installedPluginIds.has(p.id));
20852
- const templatePlugins = installedPlugins.map((p) => ({
20916
+ const templatePlugins = validInstalledPlugins.map((p) => ({
20853
20917
  id: p.id,
20854
20918
  name: p.name,
20855
20919
  displayName: p.display_name,
@@ -20885,7 +20949,7 @@ adminPluginRoutes.get("/", async (c) => {
20885
20949
  }));
20886
20950
  const allPlugins = [...templatePlugins, ...uninstalledTemplatePlugins];
20887
20951
  stats.uninstalled = uninstalledPlugins.length;
20888
- stats.total = installedPlugins.length + uninstalledPlugins.length;
20952
+ stats.total = validInstalledPlugins.length + uninstalledPlugins.length;
20889
20953
  const pageData = {
20890
20954
  plugins: allPlugins,
20891
20955
  stats,
@@ -20984,6 +21048,15 @@ adminPluginRoutes.get("/:id", async (c) => {
20984
21048
  timestamp: item.timestamp,
20985
21049
  user: item.userId || null
20986
21050
  }));
21051
+ const pluginDef = getPluginDefinition(pluginId);
21052
+ let settingsTabData = void 0;
21053
+ if (pluginDef?.settingsTabContent?.loadData) {
21054
+ try {
21055
+ settingsTabData = await pluginDef.settingsTabContent.loadData(db);
21056
+ } catch (e) {
21057
+ console.error(`settingsTabContent.loadData failed for plugin "${pluginId}":`, e);
21058
+ }
21059
+ }
20987
21060
  const pageData = {
20988
21061
  plugin: templatePlugin,
20989
21062
  activity: templateActivity,
@@ -20991,7 +21064,8 @@ adminPluginRoutes.get("/:id", async (c) => {
20991
21064
  name: user?.email || "User",
20992
21065
  email: user?.email || "",
20993
21066
  role: user?.role || "user"
20994
- }
21067
+ },
21068
+ settingsTabData
20995
21069
  };
20996
21070
  return c.html(renderPluginSettingsPage(pageData));
20997
21071
  } catch (error) {
@@ -21069,6 +21143,7 @@ adminPluginRoutes.post("/:id/activate", async (c) => {
21069
21143
  }
21070
21144
  const pluginService = new PluginService(db);
21071
21145
  await pluginService.activatePlugin(pluginId);
21146
+ await reconcileMenuFromPlugins(db);
21072
21147
  return c.json({ success: true });
21073
21148
  } catch (error) {
21074
21149
  console.error("Error activating plugin:", error);
@@ -21086,6 +21161,7 @@ adminPluginRoutes.post("/:id/deactivate", async (c) => {
21086
21161
  }
21087
21162
  const pluginService = new PluginService(db);
21088
21163
  await pluginService.deactivatePlugin(pluginId);
21164
+ await reconcileMenuFromPlugins(db);
21089
21165
  return c.json({ success: true });
21090
21166
  } catch (error) {
21091
21167
  console.error("Error deactivating plugin:", error);
@@ -27008,6 +27084,6 @@ var ROUTES_INFO = {
27008
27084
  reference: "https://github.com/sonicjs/sonicjs"
27009
27085
  };
27010
27086
 
27011
- export { DocumentRepository, ROUTES_INFO, TenantService, VALID_MEMBER_ROLES, adminCheckboxRoutes, adminCollectionsRoutes, adminDocumentsRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_content_default, apiDocumentsRoutes, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, createDocumentSchema, createUserProfilesPlugin, defineUserProfile, denyIfNotAllowed, dispatchHookEvent, getConfirmationDialogScript2 as getConfirmationDialogScript, getCustomData, getEmailService, getUserProfileConfig, getVariablesCached, hasEmailService, invalidateVariablesCache, isValidMemberRole, lexicalEditorPlugin, renderConfirmationDialog2 as renderConfirmationDialog, renderTable2 as renderTable, resolveDocScope, resolveVariablesInObject, router, router2, setEmailService, setVariablesCache, test_cleanup_default, userProfilesPlugin, userRoutes };
27012
- //# sourceMappingURL=chunk-6KNTZ67K.js.map
27013
- //# sourceMappingURL=chunk-6KNTZ67K.js.map
27087
+ export { DocumentRepository, ROUTES_INFO, TenantService, VALID_MEMBER_ROLES, adminCheckboxRoutes, adminCollectionsRoutes, adminDocumentsRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_content_default, apiDocumentsRoutes, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, createDocumentSchema, createUserProfilesPlugin, defineUserProfile, denyIfNotAllowed, dispatchHookEvent, getConfirmationDialogScript2 as getConfirmationDialogScript, getCustomData, getEmailService, getUserProfileConfig, getVariablesCached, hasEmailService, invalidateVariablesCache, isValidMemberRole, lexicalEditorPlugin, reconcileMenuFromPlugins, renderConfirmationDialog2 as renderConfirmationDialog, renderTable2 as renderTable, resolveDocScope, resolveVariablesInObject, router, router2, setEmailService, setVariablesCache, test_cleanup_default, userProfilesPlugin, userRoutes };
27088
+ //# sourceMappingURL=chunk-SK5CHFJP.js.map
27089
+ //# sourceMappingURL=chunk-SK5CHFJP.js.map