@sonicjs-cms/core 2.3.9 → 2.3.13

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 (78) hide show
  1. package/dist/{chunk-AUU4H5MP.js → chunk-2NTBZ2Y7.js} +4 -4
  2. package/dist/{chunk-AUU4H5MP.js.map → chunk-2NTBZ2Y7.js.map} +1 -1
  3. package/dist/{chunk-Q52ZQFMB.js → chunk-3YNNVSMC.js} +21 -34
  4. package/dist/chunk-3YNNVSMC.js.map +1 -0
  5. package/dist/{chunk-YLFQXTTX.cjs → chunk-5NCBFP37.cjs} +7 -7
  6. package/dist/{chunk-YLFQXTTX.cjs.map → chunk-5NCBFP37.cjs.map} +1 -1
  7. package/dist/{chunk-ES3BRZQJ.cjs → chunk-7FOAMNTI.cjs} +29 -42
  8. package/dist/chunk-7FOAMNTI.cjs.map +1 -0
  9. package/dist/{chunk-T7HDJE2H.cjs → chunk-ARLXQU2S.cjs} +292 -208
  10. package/dist/chunk-ARLXQU2S.cjs.map +1 -0
  11. package/dist/{chunk-5RKQB2JG.js → chunk-DN45O5XV.js} +8 -6
  12. package/dist/chunk-DN45O5XV.js.map +1 -0
  13. package/dist/{chunk-QNWYQZ55.js → chunk-F56JKQTA.js} +3 -3
  14. package/dist/{chunk-QNWYQZ55.js.map → chunk-F56JKQTA.js.map} +1 -1
  15. package/dist/{chunk-HNHKS2XF.js → chunk-FHCN7KR2.js} +11 -7
  16. package/dist/chunk-FHCN7KR2.js.map +1 -0
  17. package/dist/{chunk-NAYD76QF.cjs → chunk-ILZ3DP4I.cjs} +15 -6
  18. package/dist/chunk-ILZ3DP4I.cjs.map +1 -0
  19. package/dist/{chunk-YU6QFFI4.cjs → chunk-MF7DWI5P.cjs} +8 -6
  20. package/dist/chunk-MF7DWI5P.cjs.map +1 -0
  21. package/dist/{chunk-7KCDFDRI.cjs → chunk-P3XDZL6Q.cjs} +3 -4
  22. package/dist/chunk-P3XDZL6Q.cjs.map +1 -0
  23. package/dist/{chunk-I3M6I2FY.js → chunk-RP66TPEJ.js} +110 -26
  24. package/dist/chunk-RP66TPEJ.js.map +1 -0
  25. package/dist/{chunk-7CXL5K7N.js → chunk-SGAG6FD3.js} +15 -6
  26. package/dist/chunk-SGAG6FD3.js.map +1 -0
  27. package/dist/{chunk-624OTQ55.js → chunk-VMEBHBYY.js} +34 -2
  28. package/dist/chunk-VMEBHBYY.js.map +1 -0
  29. package/dist/{chunk-N3EHA3AX.cjs → chunk-W2IAEG4W.cjs} +11 -7
  30. package/dist/chunk-W2IAEG4W.cjs.map +1 -0
  31. package/dist/{chunk-O446Q45G.cjs → chunk-W4CE7XME.cjs} +34 -2
  32. package/dist/chunk-W4CE7XME.cjs.map +1 -0
  33. package/dist/{chunk-RRKXFGIO.js → chunk-X7ZAEI5S.js} +3 -4
  34. package/dist/chunk-X7ZAEI5S.js.map +1 -0
  35. package/dist/{chunk-ZMSYKV62.cjs → chunk-XR6XACXJ.cjs} +5 -5
  36. package/dist/{chunk-ZMSYKV62.cjs.map → chunk-XR6XACXJ.cjs.map} +1 -1
  37. package/dist/index.cjs +145 -145
  38. package/dist/index.js +14 -14
  39. package/dist/middleware.cjs +24 -24
  40. package/dist/middleware.js +3 -3
  41. package/dist/migrations-43GTELB5.js +4 -0
  42. package/dist/{migrations-ALSBVBV5.js.map → migrations-43GTELB5.js.map} +1 -1
  43. package/dist/migrations-ZAYXZXON.cjs +13 -0
  44. package/dist/{migrations-RMI7TLPG.cjs.map → migrations-ZAYXZXON.cjs.map} +1 -1
  45. package/dist/routes.cjs +28 -28
  46. package/dist/routes.js +8 -8
  47. package/dist/services.cjs +29 -29
  48. package/dist/services.d.cts +3 -4
  49. package/dist/services.d.ts +3 -4
  50. package/dist/services.js +4 -4
  51. package/dist/{telemetry-BFBIjBxK.d.cts → telemetry-UiD1i9GS.d.cts} +0 -1
  52. package/dist/{telemetry-BFBIjBxK.d.ts → telemetry-UiD1i9GS.d.ts} +0 -1
  53. package/dist/templates.cjs +17 -17
  54. package/dist/templates.js +2 -2
  55. package/dist/types.d.cts +1 -1
  56. package/dist/types.d.ts +1 -1
  57. package/dist/utils.cjs +20 -20
  58. package/dist/utils.d.cts +1 -1
  59. package/dist/utils.d.ts +1 -1
  60. package/dist/utils.js +2 -2
  61. package/migrations/025_add_easymde_plugin.sql +25 -0
  62. package/package.json +9 -5
  63. package/dist/chunk-5RKQB2JG.js.map +0 -1
  64. package/dist/chunk-624OTQ55.js.map +0 -1
  65. package/dist/chunk-7CXL5K7N.js.map +0 -1
  66. package/dist/chunk-7KCDFDRI.cjs.map +0 -1
  67. package/dist/chunk-ES3BRZQJ.cjs.map +0 -1
  68. package/dist/chunk-HNHKS2XF.js.map +0 -1
  69. package/dist/chunk-I3M6I2FY.js.map +0 -1
  70. package/dist/chunk-N3EHA3AX.cjs.map +0 -1
  71. package/dist/chunk-NAYD76QF.cjs.map +0 -1
  72. package/dist/chunk-O446Q45G.cjs.map +0 -1
  73. package/dist/chunk-Q52ZQFMB.js.map +0 -1
  74. package/dist/chunk-RRKXFGIO.js.map +0 -1
  75. package/dist/chunk-T7HDJE2H.cjs.map +0 -1
  76. package/dist/chunk-YU6QFFI4.cjs.map +0 -1
  77. package/dist/migrations-ALSBVBV5.js +0 -4
  78. package/dist/migrations-RMI7TLPG.cjs +0 -13
@@ -13,8 +13,7 @@ function safeGetEnv(key) {
13
13
  function getDefaultTelemetryConfig() {
14
14
  return {
15
15
  enabled: true,
16
- apiKey: safeGetEnv("POSTHOG_API_KEY") || "phc_VuhFUIJLXzwyGjlgQ67dbNeSh5x4cp9F8i15hZFIDhs",
17
- host: safeGetEnv("POSTHOG_HOST") || "https://us.i.posthog.com",
16
+ host: safeGetEnv("SONICJS_TELEMETRY_ENDPOINT") || "https://stats.sonicjs.com",
18
17
  debug: safeGetEnv("NODE_ENV") === "development"
19
18
  };
20
19
  }
@@ -78,5 +77,5 @@ exports.isTelemetryEnabled = isTelemetryEnabled;
78
77
  exports.sanitizeErrorMessage = sanitizeErrorMessage;
79
78
  exports.sanitizeRoute = sanitizeRoute;
80
79
  exports.shouldSkipEvent = shouldSkipEvent;
81
- //# sourceMappingURL=chunk-7KCDFDRI.cjs.map
82
- //# sourceMappingURL=chunk-7KCDFDRI.cjs.map
80
+ //# sourceMappingURL=chunk-P3XDZL6Q.cjs.map
81
+ //# sourceMappingURL=chunk-P3XDZL6Q.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/telemetry-config.ts","../src/utils/telemetry-id.ts"],"names":[],"mappings":"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","file":"chunk-P3XDZL6Q.cjs","sourcesContent":["/**\n * Telemetry Configuration Utilities\n *\n * Manages telemetry settings and opt-out mechanisms\n */\n\nimport type { TelemetryConfig } from '../types/telemetry'\n\n/**\n * Safely get an environment variable\n * Works in both Node.js and Cloudflare Workers environments\n */\nfunction safeGetEnv(key: string): string | undefined {\n try {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key]\n }\n } catch {\n // process is not defined in this runtime (e.g., Cloudflare Workers)\n }\n return undefined\n}\n\n/**\n * Get default telemetry configuration\n * Uses lazy evaluation to avoid accessing process.env at module load time\n */\nexport function getDefaultTelemetryConfig(): TelemetryConfig {\n return {\n enabled: true,\n host: safeGetEnv('SONICJS_TELEMETRY_ENDPOINT') || 'https://stats.sonicjs.com',\n debug: safeGetEnv('NODE_ENV') === 'development'\n }\n}\n\n/**\n * Check if telemetry is enabled via environment variables\n */\nexport function isTelemetryEnabled(): boolean {\n // Check for explicit opt-out\n const telemetryEnv = safeGetEnv('SONICJS_TELEMETRY')\n if (telemetryEnv === 'false' || telemetryEnv === '0' || telemetryEnv === 'disabled') {\n return false\n }\n\n // Check for DO_NOT_TRACK environment variable (common standard)\n const doNotTrack = safeGetEnv('DO_NOT_TRACK')\n if (doNotTrack === '1' || doNotTrack === 'true') {\n return false\n }\n\n // Default to enabled (opt-out model)\n return true\n}\n\n/**\n * Get telemetry configuration from environment\n */\nexport function getTelemetryConfig(): TelemetryConfig {\n return {\n ...getDefaultTelemetryConfig(),\n enabled: isTelemetryEnabled()\n }\n}\n\n/**\n * Check if telemetry should be skipped for this event\n * Used to implement sampling or rate limiting if needed\n */\nexport function shouldSkipEvent(eventName: string, sampleRate: number = 1.0): boolean {\n if (sampleRate >= 1.0) return false\n if (sampleRate <= 0) return true\n\n // Use a consistent hash of the event name for deterministic sampling\n let hash = 0\n for (let i = 0; i < eventName.length; i++) {\n hash = ((hash << 5) - hash) + eventName.charCodeAt(i)\n hash = hash & hash\n }\n\n return Math.abs(hash % 100) / 100 > sampleRate\n}\n","/**\n * Telemetry ID Utilities\n *\n * Generates and manages anonymous installation IDs\n */\n\n/**\n * Generate a new anonymous installation ID\n * Uses globalThis.crypto for Cloudflare Workers compatibility\n */\nexport function generateInstallationId(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Generate a project-specific ID from project name\n * Uses a simple hash to anonymize while maintaining consistency\n */\nexport function generateProjectId(projectName: string): string {\n // Simple hash function to anonymize project names\n let hash = 0\n for (let i = 0; i < projectName.length; i++) {\n const char = projectName.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash // Convert to 32bit integer\n }\n return `proj_${Math.abs(hash).toString(36)}`\n}\n\n/**\n * Sanitize error messages to remove any potential PII\n */\nexport function sanitizeErrorMessage(error: Error | string): string {\n const message = typeof error === 'string' ? error : error.message\n\n // Extract error type/category only\n const [errorTypeRaw] = message.split(':')\n const errorType = (errorTypeRaw || message).trim()\n\n // Remove file paths that might contain usernames\n const sanitized = errorType\n .replace(/\\/Users\\/[^/]+/g, '/Users/***')\n .replace(/\\/home\\/[^/]+/g, '/home/***')\n .replace(/C:\\\\Users\\\\[^\\\\]+/g, 'C:\\\\Users\\\\***')\n .replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g, '***@***.***')\n\n return sanitized\n}\n\n/**\n * Sanitize route to remove any user-specific data\n */\nexport function sanitizeRoute(route: string): string {\n return route\n // Remove UUIDs\n .replace(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi, ':id')\n // Remove numeric IDs\n .replace(/\\/\\d+/g, '/:id')\n // Remove email patterns\n .replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g, ':email')\n}\n"]}
@@ -1,9 +1,9 @@
1
- import { getCacheService, CACHE_CONFIGS, getLogger, SettingsService } from './chunk-Q52ZQFMB.js';
2
- import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-AUU4H5MP.js';
3
- import { PluginService } from './chunk-7CXL5K7N.js';
4
- import { MigrationService } from './chunk-624OTQ55.js';
5
- import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-5RKQB2JG.js';
6
- import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-HNHKS2XF.js';
1
+ import { getCacheService, CACHE_CONFIGS, getLogger, SettingsService } from './chunk-3YNNVSMC.js';
2
+ import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-2NTBZ2Y7.js';
3
+ import { PluginService } from './chunk-SGAG6FD3.js';
4
+ import { MigrationService } from './chunk-VMEBHBYY.js';
5
+ import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-DN45O5XV.js';
6
+ import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-FHCN7KR2.js';
7
7
  import { metricsTracker } from './chunk-FICTAGD4.js';
8
8
  import { Hono } from 'hono';
9
9
  import { cors } from 'hono/cors';
@@ -1569,7 +1569,7 @@ adminApiRoutes.post("/collections", async (c) => {
1569
1569
  }
1570
1570
  const validatedData = validation.data;
1571
1571
  const db = c.env.DB;
1572
- const ____user = c.get("user");
1572
+ const _user = c.get("user");
1573
1573
  const displayName = validatedData.displayName || validatedData.display_name || "";
1574
1574
  const existingStmt = db.prepare("SELECT id FROM collections WHERE name = ?");
1575
1575
  const existing = await existingStmt.bind(validatedData.name).first();
@@ -1720,7 +1720,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
1720
1720
  });
1721
1721
  adminApiRoutes.get("/migrations/status", async (c) => {
1722
1722
  try {
1723
- const { MigrationService: MigrationService2 } = await import('./migrations-ALSBVBV5.js');
1723
+ const { MigrationService: MigrationService2 } = await import('./migrations-43GTELB5.js');
1724
1724
  const db = c.env.DB;
1725
1725
  const migrationService = new MigrationService2(db);
1726
1726
  const status = await migrationService.getMigrationStatus();
@@ -1745,7 +1745,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
1745
1745
  error: "Unauthorized. Admin access required."
1746
1746
  }, 403);
1747
1747
  }
1748
- const { MigrationService: MigrationService2 } = await import('./migrations-ALSBVBV5.js');
1748
+ const { MigrationService: MigrationService2 } = await import('./migrations-43GTELB5.js');
1749
1749
  const db = c.env.DB;
1750
1750
  const migrationService = new MigrationService2(db);
1751
1751
  const result = await migrationService.runPendingMigrations();
@@ -1764,7 +1764,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
1764
1764
  });
1765
1765
  adminApiRoutes.get("/migrations/validate", async (c) => {
1766
1766
  try {
1767
- const { MigrationService: MigrationService2 } = await import('./migrations-ALSBVBV5.js');
1767
+ const { MigrationService: MigrationService2 } = await import('./migrations-43GTELB5.js');
1768
1768
  const db = c.env.DB;
1769
1769
  const migrationService = new MigrationService2(db);
1770
1770
  const validation = await migrationService.validateSchema();
@@ -6019,8 +6019,8 @@ adminContentRoutes.get("/", async (c) => {
6019
6019
  conditions.push("c.status != 'deleted'");
6020
6020
  }
6021
6021
  if (search) {
6022
- conditions.push("(c.title LIKE ? OR c.slug LIKE ?)");
6023
- params.push(`%${search}%`, `%${search}%`);
6022
+ conditions.push("(c.title LIKE ? OR c.slug LIKE ? OR c.data LIKE ?)");
6023
+ params.push(`%${search}%`, `%${search}%`, `%${search}%`);
6024
6024
  }
6025
6025
  if (modelName !== "all") {
6026
6026
  conditions.push("col.name = ?");
@@ -6455,10 +6455,9 @@ adminContentRoutes.post("/", async (c) => {
6455
6455
  const insertStmt = db.prepare(`
6456
6456
  INSERT INTO content (
6457
6457
  id, collection_id, slug, title, data, status,
6458
- scheduled_publish_at, scheduled_unpublish_at,
6459
- meta_title, meta_description, author_id, created_by, created_at, updated_at
6458
+ author_id, created_at, updated_at
6460
6459
  )
6461
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6460
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
6462
6461
  `);
6463
6462
  await insertStmt.bind(
6464
6463
  contentId,
@@ -6467,11 +6466,6 @@ adminContentRoutes.post("/", async (c) => {
6467
6466
  data.title || "Untitled",
6468
6467
  JSON.stringify(data),
6469
6468
  status,
6470
- scheduledPublishAt ? new Date(scheduledPublishAt).getTime() : null,
6471
- scheduledUnpublishAt ? new Date(scheduledUnpublishAt).getTime() : null,
6472
- data.meta_title || null,
6473
- data.meta_description || null,
6474
- user?.userId || "unknown",
6475
6469
  user?.userId || "unknown",
6476
6470
  now,
6477
6471
  now
@@ -11509,7 +11503,7 @@ adminMediaRoutes.get("/", async (c) => {
11509
11503
  const type = searchParams.get("type") || "all";
11510
11504
  const view = searchParams.get("view") || "grid";
11511
11505
  const page = parseInt(searchParams.get("page") || "1");
11512
- const ____cacheBust = searchParams.get("t");
11506
+ const _cacheBust = searchParams.get("t");
11513
11507
  const limit = 24;
11514
11508
  const offset = (page - 1) * limit;
11515
11509
  const db = c.env.DB;
@@ -18348,7 +18342,7 @@ function renderCollectionFormPage(data) {
18348
18342
  Add Field
18349
18343
  </button>
18350
18344
  </div>
18351
-
18345
+
18352
18346
  <!-- Fields List -->
18353
18347
  <div id="fields-list" class="space-y-3">
18354
18348
  ${fieldsWithData.map((field) => `
@@ -18419,7 +18413,9 @@ function renderCollectionFormPage(data) {
18419
18413
  ` : ""}
18420
18414
  </div>
18421
18415
  </div>
18422
- ` : `
18416
+ ` : ""}
18417
+
18418
+ ${!isEdit ? `
18423
18419
  <div class="mt-6 rounded-lg bg-cyan-50 dark:bg-cyan-900/20 border border-cyan-100 dark:border-cyan-900/30 p-4">
18424
18420
  <div class="flex items-start gap-x-3">
18425
18421
  <svg class="h-5 w-5 text-cyan-600 dark:text-cyan-400 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
@@ -18435,7 +18431,7 @@ function renderCollectionFormPage(data) {
18435
18431
  </div>
18436
18432
  </div>
18437
18433
  </div>
18438
- `}
18434
+ ` : ""}
18439
18435
 
18440
18436
  <!-- Action Buttons -->
18441
18437
  <div class="mt-6 pt-6 border-t border-zinc-950/5 dark:border-white/10 flex items-center justify-between">
@@ -18955,6 +18951,8 @@ function renderCollectionFormPage(data) {
18955
18951
  })
18956
18952
  .then(data => {
18957
18953
  if (data.success) {
18954
+ // Close modal before reloading
18955
+ closeFieldModal();
18958
18956
  location.reload();
18959
18957
  } else {
18960
18958
  alert('Error saving field: ' + (data.error || 'Unknown error'));
@@ -19505,11 +19503,65 @@ adminCollectionsRoutes.post("/:id/fields", async (c) => {
19505
19503
  return c.json({ success: false, error: "Field name must contain only lowercase letters, numbers, and underscores." });
19506
19504
  }
19507
19505
  const db = c.env.DB;
19506
+ const getCollectionStmt = db.prepare("SELECT * FROM collections WHERE id = ?");
19507
+ const collection = await getCollectionStmt.bind(collectionId).first();
19508
+ if (!collection) {
19509
+ return c.json({ success: false, error: "Collection not found." });
19510
+ }
19511
+ let schema = collection.schema ? typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema : null;
19512
+ if (schema && schema.properties && schema.properties[fieldName]) {
19513
+ return c.json({ success: false, error: "A field with this name already exists." });
19514
+ }
19508
19515
  const existingStmt = db.prepare("SELECT id FROM content_fields WHERE collection_id = ? AND field_name = ?");
19509
19516
  const existing = await existingStmt.bind(collectionId, fieldName).first();
19510
19517
  if (existing) {
19511
19518
  return c.json({ success: false, error: "A field with this name already exists." });
19512
19519
  }
19520
+ let parsedOptions = {};
19521
+ try {
19522
+ parsedOptions = fieldOptions ? JSON.parse(fieldOptions) : {};
19523
+ } catch (e) {
19524
+ console.error("Error parsing field options:", e);
19525
+ }
19526
+ if (schema) {
19527
+ if (!schema.properties) {
19528
+ schema.properties = {};
19529
+ }
19530
+ if (!schema.required) {
19531
+ schema.required = [];
19532
+ }
19533
+ const fieldConfig = {
19534
+ type: fieldType === "number" ? "number" : fieldType === "boolean" ? "boolean" : "string",
19535
+ title: fieldLabel,
19536
+ searchable: isSearchable,
19537
+ ...parsedOptions
19538
+ };
19539
+ if (fieldType === "richtext") {
19540
+ fieldConfig.format = "richtext";
19541
+ } else if (fieldType === "date") {
19542
+ fieldConfig.format = "date-time";
19543
+ } else if (fieldType === "select") {
19544
+ fieldConfig.enum = parsedOptions.options || [];
19545
+ } else if (fieldType === "media") {
19546
+ fieldConfig.format = "media";
19547
+ } else if (fieldType === "quill") {
19548
+ fieldConfig.type = "quill";
19549
+ } else if (fieldType === "mdxeditor") {
19550
+ fieldConfig.type = "mdxeditor";
19551
+ }
19552
+ schema.properties[fieldName] = fieldConfig;
19553
+ if (isRequired && !schema.required.includes(fieldName)) {
19554
+ schema.required.push(fieldName);
19555
+ }
19556
+ const updateSchemaStmt = db.prepare(`
19557
+ UPDATE collections
19558
+ SET schema = ?, updated_at = ?
19559
+ WHERE id = ?
19560
+ `);
19561
+ await updateSchemaStmt.bind(JSON.stringify(schema), Date.now(), collectionId).run();
19562
+ console.log("[Add Field] Added field to schema:", fieldName, fieldConfig);
19563
+ return c.json({ success: true, fieldId: `schema-${fieldName}` });
19564
+ }
19513
19565
  const orderStmt = db.prepare("SELECT MAX(field_order) as max_order FROM content_fields WHERE collection_id = ?");
19514
19566
  const orderResult = await orderStmt.bind(collectionId).first();
19515
19567
  const nextOrder = (orderResult?.max_order || 0) + 1;
@@ -19650,7 +19702,39 @@ adminCollectionsRoutes.put("/:collectionId/fields/:fieldId", async (c) => {
19650
19702
  adminCollectionsRoutes.delete("/:collectionId/fields/:fieldId", async (c) => {
19651
19703
  try {
19652
19704
  const fieldId = c.req.param("fieldId");
19705
+ const collectionId = c.req.param("collectionId");
19653
19706
  const db = c.env.DB;
19707
+ if (fieldId.startsWith("schema-")) {
19708
+ const fieldName = fieldId.replace("schema-", "");
19709
+ const getCollectionStmt = db.prepare("SELECT * FROM collections WHERE id = ?");
19710
+ const collection = await getCollectionStmt.bind(collectionId).first();
19711
+ if (!collection) {
19712
+ return c.json({ success: false, error: "Collection not found." });
19713
+ }
19714
+ let schema = typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema;
19715
+ if (!schema || !schema.properties) {
19716
+ return c.json({ success: false, error: "Field not found in schema." });
19717
+ }
19718
+ if (schema.properties[fieldName]) {
19719
+ delete schema.properties[fieldName];
19720
+ if (schema.required && Array.isArray(schema.required)) {
19721
+ const requiredIndex = schema.required.indexOf(fieldName);
19722
+ if (requiredIndex !== -1) {
19723
+ schema.required.splice(requiredIndex, 1);
19724
+ }
19725
+ }
19726
+ const updateCollectionStmt = db.prepare(`
19727
+ UPDATE collections
19728
+ SET schema = ?, updated_at = ?
19729
+ WHERE id = ?
19730
+ `);
19731
+ await updateCollectionStmt.bind(JSON.stringify(schema), Date.now(), collectionId).run();
19732
+ console.log("[Delete Field] Removed field from schema:", fieldName);
19733
+ return c.json({ success: true });
19734
+ } else {
19735
+ return c.json({ success: false, error: "Field not found in schema." });
19736
+ }
19737
+ }
19654
19738
  const deleteStmt = db.prepare("DELETE FROM content_fields WHERE id = ?");
19655
19739
  await deleteStmt.bind(fieldId).run();
19656
19740
  return c.json({ success: true });
@@ -21578,5 +21662,5 @@ var ROUTES_INFO = {
21578
21662
  };
21579
21663
 
21580
21664
  export { PluginBuilder, ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, router, test_cleanup_default, userRoutes };
21581
- //# sourceMappingURL=chunk-I3M6I2FY.js.map
21582
- //# sourceMappingURL=chunk-I3M6I2FY.js.map
21665
+ //# sourceMappingURL=chunk-RP66TPEJ.js.map
21666
+ //# sourceMappingURL=chunk-RP66TPEJ.js.map