@brightsideoy/kama 1.0.0-alpha.7 → 1.0.0-alpha.9

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.
@@ -23183,7 +23183,7 @@ async function px(e) {
23183
23183
  } catch (t) {
23184
23184
  console.error(`[Route Generator] Failed to parse channel ${e.id} dataJson:`, t);
23185
23185
  }
23186
- let i = await e.prepare("SELECT id, slug FROM content_nodes WHERE (deletedAt IS NULL OR deletedAt = '')").all(), a = Array.isArray(i) ? i : i?.results || [], o = /* @__PURE__ */ new Map();
23186
+ let i = await e.prepare("SELECT id, slug FROM content_nodes WHERE status = 'published' AND (deletedAt IS NULL OR deletedAt = '')").all(), a = Array.isArray(i) ? i : i?.results || [], o = /* @__PURE__ */ new Map();
23187
23187
  for (let e of a) e.slug && o.set(Number(e.id), String(e.slug).replace(/^\/+|\/+$/g, ""));
23188
23188
  let s = await e.prepare("SELECT channelId, treeId FROM channel_trees").all(), c = Array.isArray(s) ? s : s?.results || [], l = await e.prepare("SELECT treeId, contentId, parentId, sortOrder FROM tree_nodes ORDER BY sortOrder ASC").all(), u = Array.isArray(l) ? l : l?.results || [], d = await e.prepare("SELECT nodeId, channelId FROM node_channels").all(), f = Array.isArray(d) ? d : d?.results || [], p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Set();
23189
23189
  for (let e of n) {
@@ -23621,34 +23621,41 @@ mx.get("/", async (e) => {
23621
23621
  }
23622
23622
  }
23623
23623
  if (a) {
23624
- let e = await t.prepare("SELECT title, slug, language, contentType, isHidden, draftDataJson, dataJson, publishedAt, status, hasUnpublishedChanges FROM content_nodes WHERE id = ?").get(a);
23624
+ let e = await t.prepare("SELECT title, slug, language, contentType, isHidden, draftDataJson, dataJson, publishedAt, status, hasUnpublishedChanges FROM content_nodes WHERE id = ?").get(a), n = o === void 0 ? e?.title || "Untitled" : o, r = A ?? e?.slug ?? null, i = S || e?.contentType || "page", l = c === void 0 ? e?.language ?? null : w, u = x === void 0 ? vx(e?.isHidden) : x;
23625
23625
  ne = Sx(e, {
23626
- title: o,
23627
- slug: A,
23628
- isHidden: x,
23626
+ title: n,
23627
+ slug: r,
23628
+ isHidden: u,
23629
23629
  sanitizedFields: v
23630
23630
  });
23631
- let n = e?.draftDataJson || e?.dataJson || "{}";
23631
+ let d = e?.draftDataJson || e?.dataJson || "{}";
23632
23632
  try {
23633
- xx(typeof n == "string" ? JSON.parse(n) : n, te);
23633
+ xx(typeof d == "string" ? JSON.parse(d) : d, te);
23634
23634
  } catch {}
23635
- let r = {};
23635
+ let f = {};
23636
23636
  try {
23637
- r = JSON.parse(n);
23637
+ f = JSON.parse(d);
23638
23638
  } catch {}
23639
- let i = JSON.stringify({
23640
- ...r,
23639
+ let p = {
23640
+ title: n,
23641
+ slug: r,
23642
+ contentType: i,
23643
+ language: l,
23644
+ isHidden: u,
23641
23645
  ...v
23642
- }), l = !1;
23643
- if (o !== void 0 && o.trim() !== (e?.title || "Untitled") && (l = !0), s !== void 0 && A !== e?.slug && (l = !0), c !== void 0 && w !== e?.language && (l = !0), !l && Object.keys(v).length > 0) {
23644
- for (let e of Object.keys(v)) if (JSON.stringify(v[e]) !== JSON.stringify(r[e])) {
23645
- l = !0;
23646
+ }, h = JSON.stringify({
23647
+ ...f,
23648
+ ...p
23649
+ }), g = !1;
23650
+ if (o !== void 0 && o.trim() !== (e?.title || "Untitled") && (g = !0), s !== void 0 && A !== e?.slug && (g = !0), c !== void 0 && w !== e?.language && (g = !0), !g && Object.keys(v).length > 0) {
23651
+ for (let e of Object.keys(v)) if (JSON.stringify(v[e]) !== JSON.stringify(f[e])) {
23652
+ g = !0;
23646
23653
  break;
23647
23654
  }
23648
23655
  }
23649
- if (!l && m !== void 0) {
23656
+ if (!g && m !== void 0) {
23650
23657
  let e = await t.prepare("SELECT region, elementType, dataJson FROM content_blocks WHERE contentId = ? AND isDraft = 1 ORDER BY sortOrder ASC").all(a), n = Array.isArray(e) ? e : e?.results || [];
23651
- if (m.length !== n.length) l = !0;
23658
+ if (m.length !== n.length) g = !0;
23652
23659
  else for (let e = 0; e < m.length; e++) {
23653
23660
  let t = m[e], r = n[e], i = t.componentId || t.elementType || "text-block", a = t.region || "content", o = { ...t.data || {} };
23654
23661
  delete o._instanceId, delete o.blockId;
@@ -23657,19 +23664,27 @@ mx.get("/", async (e) => {
23657
23664
  s = JSON.parse(r.dataJson);
23658
23665
  } catch {}
23659
23666
  if (delete s._instanceId, delete s.blockId, i !== r.elementType || a !== r.region || JSON.stringify(o) !== JSON.stringify(s)) {
23660
- l = !0;
23667
+ g = !0;
23661
23668
  break;
23662
23669
  }
23663
23670
  }
23664
23671
  }
23665
- let u = o === void 0 ? e?.title || "Untitled" : o, d = A ?? e?.slug ?? null, f = S || e?.contentType || "page", p = c === void 0 ? e?.language : w;
23666
23672
  O = e?.status === "published" || e?.publishedAt ? "published" : "draft";
23667
- let h = e?.hasUnpublishedChanges === 1 || e?.hasUnpublishedChanges === !0;
23668
- k = l ? 1 : +!!h, await t.prepare("\n UPDATE content_nodes \n SET title = ?, slug = ?, language = ?, contentType = ?, isHidden = ?, status = ?, draftDataJson = ?, hasUnpublishedChanges = ?, updatedById = ?, updatedAt = CURRENT_TIMESTAMP\n WHERE id = ?\n ").run(u, d, p, f, x, O, i, k, C, a);
23673
+ let _ = e?.hasUnpublishedChanges === 1 || e?.hasUnpublishedChanges === !0;
23674
+ k = g ? 1 : +!!_;
23675
+ let y = e?.publishedAt != null || e?.status === "published", b = y ? e.title : n, T = y ? e.slug : r, ee = y ? e.contentType : i, E = y ? e.language : l, D = y ? e.isHidden : u;
23676
+ await t.prepare("\n UPDATE content_nodes \n SET title = ?, slug = ?, language = ?, contentType = ?, isHidden = ?, status = ?, draftDataJson = ?, hasUnpublishedChanges = ?, updatedById = ?, updatedAt = CURRENT_TIMESTAMP\n WHERE id = ?\n ").run(b, T, E, ee, D, O, h, k, C, a);
23669
23677
  } else {
23670
23678
  ne = !0;
23671
- let e = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, hasUnpublishedChanges, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, 0, ?, ?)\n ").run(S, o || "Untitled", A, w, x, JSON.stringify(v), C, C);
23672
- D = Number(e.lastInsertRowid);
23679
+ let e = {
23680
+ title: o || "Untitled",
23681
+ slug: A,
23682
+ contentType: S,
23683
+ language: w,
23684
+ isHidden: x,
23685
+ ...v
23686
+ }, n = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, hasUnpublishedChanges, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, 0, ?, ?)\n ").run(S, o || "Untitled", A, w, x, JSON.stringify(e), C, C);
23687
+ D = Number(n.lastInsertRowid);
23673
23688
  }
23674
23689
  if (h !== void 0 && Array.isArray(h) && D) {
23675
23690
  await t.prepare("DELETE FROM node_channels WHERE nodeId = ?").run(D);
@@ -23715,7 +23730,7 @@ mx.get("/", async (e) => {
23715
23730
  await Cx(t, n, a);
23716
23731
  }
23717
23732
  }
23718
- return await $b(e).clear(), ne && await px(t), e.json({
23733
+ return await $b(e).clear(), e.json({
23719
23734
  success: !0,
23720
23735
  id: D,
23721
23736
  status: O,
@@ -23735,46 +23750,97 @@ mx.get("/", async (e) => {
23735
23750
  try {
23736
23751
  c = typeof o.draftDataJson == "string" ? JSON.parse(o.draftDataJson) : o.draftDataJson || {};
23737
23752
  } catch {}
23738
- let l = (s.title ?? o.title ?? "").trim(), u = (c.title ?? n.title ?? o.title ?? "").trim(), d = (s.slug ?? o.slug ?? "").trim(), f = (c.slug ?? n.slug ?? o.slug ?? "").trim(), p = (s.menuTitle ?? "").trim(), m = (c.menuTitle ?? "").trim(), h = l !== "" && u !== "" && l !== u || d !== "" && f !== "" && d !== f || p !== "" && m !== "" && p !== m, g = o.draftDataJson || o.dataJson || "{}";
23739
- await t.prepare("\n UPDATE content_nodes\n SET publishedDataJson = ?, dataJson = ?, publishedAt = CURRENT_TIMESTAMP, status = 'published', hasUnpublishedChanges = 0, updatedById = ?, updatedAt = CURRENT_TIMESTAMP\n WHERE id = ?\n ").run(g, g, a, i), await t.prepare("DELETE FROM content_blocks WHERE contentId = ? AND isDraft = 0").run(i);
23740
- let _ = await t.prepare("SELECT * FROM content_blocks WHERE contentId = ? AND isDraft = 1 ORDER BY sortOrder ASC").all(i), v = Array.isArray(_) ? _ : _?.results || [];
23741
- if (v.length > 0) {
23753
+ let l = (s.title ?? o.title ?? "").trim(), u = (c.title ?? n.title ?? o.title ?? "").trim(), d = (s.slug ?? o.slug ?? "").trim(), f = (c.slug ?? n.slug ?? o.slug ?? "").trim(), p = (s.menuTitle ?? "").trim(), m = (c.menuTitle ?? "").trim(), h = l !== "" && u !== "" && l !== u || d !== "" && f !== "" && d !== f || p !== "" && m !== "" && p !== m, g = o.draftDataJson || o.dataJson || "{}", _ = c.title ?? n.title ?? o.title ?? "Untitled", v = c.slug ?? n.slug ?? o.slug ?? null, y = c.language === void 0 ? o.language : c.language, b = c.contentType ?? o.contentType ?? "page", x = c.isHidden === void 0 ? o.isHidden : c.isHidden;
23754
+ await t.prepare("\n UPDATE content_nodes\n SET title = ?, slug = ?, language = ?, contentType = ?, isHidden = ?, publishedDataJson = ?, dataJson = ?, publishedAt = CURRENT_TIMESTAMP, status = 'published', hasUnpublishedChanges = 0, updatedById = ?, updatedAt = CURRENT_TIMESTAMP\n WHERE id = ?\n ").run(_, v, y, b, x, g, g, a, i), await t.prepare("DELETE FROM content_blocks WHERE contentId = ? AND isDraft = 0").run(i);
23755
+ let S = await t.prepare("SELECT * FROM content_blocks WHERE contentId = ? AND isDraft = 1 ORDER BY sortOrder ASC").all(i), C = Array.isArray(S) ? S : S?.results || [];
23756
+ if (C.length > 0) {
23742
23757
  let e = [], n = [];
23743
- for (let t of v) {
23758
+ for (let t of C) {
23744
23759
  let r = t.region !== void 0 && t.region !== null && String(t.region).trim() !== "" ? String(t.region) : "content";
23745
23760
  e.push("(?, ?, ?, ?, ?, 0)"), n.push(i, t.sortOrder, t.elementType, t.dataJson, r);
23746
23761
  }
23747
23762
  await t.prepare(`INSERT INTO content_blocks (contentId, sortOrder, elementType, dataJson, region, isDraft) VALUES ${e.join(", ")}`).run(...n);
23748
23763
  }
23749
- let y = ((await t.prepare("SELECT COUNT(*) as count FROM node_revisions WHERE nodeId = ?").get(i))?.count || 0) + 1, b = {
23750
- version: y,
23764
+ let w = ((await t.prepare("SELECT COUNT(*) as count FROM node_revisions WHERE nodeId = ?").get(i))?.count || 0) + 1, T = {
23765
+ version: w,
23751
23766
  publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
23752
23767
  node: {
23753
23768
  ...o,
23754
23769
  dataJson: g
23755
23770
  },
23756
- blocks: v
23771
+ blocks: C
23757
23772
  };
23758
- return await t.prepare("INSERT INTO node_revisions (nodeId, version, snapshotJson, createdById) VALUES (?, ?, ?, ?)").run(i, y, JSON.stringify(b), a), await $b(e).clear(), new fx(e.env).purgeNode(e, t, i, h), h && await px(t), e.json({
23773
+ return await t.prepare("INSERT INTO node_revisions (nodeId, version, snapshotJson, createdById) VALUES (?, ?, ?, ?)").run(i, w, JSON.stringify(T), a), await $b(e).clear(), h && await px(t), new fx(e.env).purgeNode(e, t, i, h), e.json({
23759
23774
  success: !0,
23760
23775
  id: i,
23761
23776
  status: "published",
23762
- version: y,
23763
- publishedAt: b.publishedAt,
23777
+ version: w,
23778
+ publishedAt: T.publishedAt,
23764
23779
  hasUnpublishedChanges: !1,
23765
23780
  isStructuralChange: h
23766
23781
  });
23782
+ }), mx.post("/publish/batch", async (e) => {
23783
+ let t = e.get("db"), n = await e.req.json(), r = n.ids;
23784
+ if (!Array.isArray(r) || r.length === 0) return e.json({ error: "Array of Node IDs required" }, 400);
23785
+ let i = e.get("user")?.id || null, a = !1, o = [];
23786
+ for (let e of r) {
23787
+ let r = Number(e);
23788
+ if (isNaN(r)) continue;
23789
+ let s = await t.prepare("SELECT * FROM content_nodes WHERE id = ?").get(r);
23790
+ if (!s) continue;
23791
+ let c = {};
23792
+ try {
23793
+ c = typeof s.publishedDataJson == "string" ? JSON.parse(s.publishedDataJson) : s.publishedDataJson || {};
23794
+ } catch {}
23795
+ let l = {};
23796
+ try {
23797
+ l = typeof s.draftDataJson == "string" ? JSON.parse(s.draftDataJson) : s.draftDataJson || {};
23798
+ } catch {}
23799
+ let u = (c.title ?? s.title ?? "").trim(), d = (l.title ?? n.title ?? s.title ?? "").trim(), f = (c.slug ?? s.slug ?? "").trim(), p = (l.slug ?? n.slug ?? s.slug ?? "").trim(), m = (c.menuTitle ?? "").trim(), h = (l.menuTitle ?? "").trim();
23800
+ (u !== "" && d !== "" && u !== d || f !== "" && p !== "" && f !== p || m !== "" && h !== "" && m !== h) && (a = !0);
23801
+ let g = s.draftDataJson || s.dataJson || "{}", _ = l.title ?? n.title ?? s.title ?? "Untitled", v = l.slug ?? n.slug ?? s.slug ?? null, y = l.language === void 0 ? s.language : l.language, b = l.contentType ?? s.contentType ?? "page", x = l.isHidden === void 0 ? s.isHidden : l.isHidden;
23802
+ await t.prepare("\n UPDATE content_nodes\n SET title = ?, slug = ?, language = ?, contentType = ?, isHidden = ?, publishedDataJson = ?, dataJson = ?, publishedAt = CURRENT_TIMESTAMP, status = 'published', hasUnpublishedChanges = 0, updatedById = ?, updatedAt = CURRENT_TIMESTAMP\n WHERE id = ?\n ").run(_, v, y, b, x, g, g, i, r), await t.prepare("DELETE FROM content_blocks WHERE contentId = ? AND isDraft = 0").run(r);
23803
+ let S = await t.prepare("SELECT * FROM content_blocks WHERE contentId = ? AND isDraft = 1 ORDER BY sortOrder ASC").all(r), C = Array.isArray(S) ? S : S?.results || [];
23804
+ if (C.length > 0) {
23805
+ let e = [], n = [];
23806
+ for (let t of C) {
23807
+ let i = t.region !== void 0 && t.region !== null && String(t.region).trim() !== "" ? String(t.region) : "content";
23808
+ e.push("(?, ?, ?, ?, ?, 0)"), n.push(r, t.sortOrder, t.elementType, t.dataJson, i);
23809
+ }
23810
+ await t.prepare(`INSERT INTO content_blocks (contentId, sortOrder, elementType, dataJson, region, isDraft) VALUES ${e.join(", ")}`).run(...n);
23811
+ }
23812
+ let w = ((await t.prepare("SELECT COUNT(*) as count FROM node_revisions WHERE nodeId = ?").get(r))?.count || 0) + 1, T = {
23813
+ version: w,
23814
+ publishedAt: (/* @__PURE__ */ new Date()).toISOString(),
23815
+ node: {
23816
+ ...s,
23817
+ dataJson: g
23818
+ },
23819
+ blocks: C
23820
+ };
23821
+ await t.prepare("INSERT INTO node_revisions (nodeId, version, snapshotJson, createdById) VALUES (?, ?, ?, ?)").run(r, w, JSON.stringify(T), i), o.push(r);
23822
+ }
23823
+ await $b(e).clear();
23824
+ let s = new fx(e.env);
23825
+ if (a) await px(t), s.purgeGlobalCache(e, t);
23826
+ else for (let n of o) s.purgeNode(e, t, n, !1);
23827
+ return e.json({
23828
+ success: !0,
23829
+ publishedCount: o.length,
23830
+ publishedIds: o,
23831
+ isStructuralChange: a
23832
+ });
23767
23833
  }), mx.delete("/", async (e) => {
23768
23834
  let t = e.get("db"), n = e.req.query("id");
23769
23835
  if (!n) return e.json({ error: "Node ID required" }, 400);
23770
23836
  let r = Number(n);
23771
- return await t.prepare("UPDATE content_nodes SET deletedAt = CURRENT_TIMESTAMP WHERE id = ?").run(r), await $b(e).clear(), new fx(e.env).purgeNode(e, t, r, !0), await px(t), e.json({
23837
+ return await t.prepare("UPDATE content_nodes SET deletedAt = CURRENT_TIMESTAMP WHERE id = ?").run(r), await $b(e).clear(), await px(t), new fx(e.env).purgeNode(e, t, r, !0), e.json({
23772
23838
  success: !0,
23773
23839
  deletedNodeId: r
23774
23840
  });
23775
23841
  }), mx.post("/restore", async (e) => {
23776
23842
  let t = e.get("db"), n = await e.req.json(), r = Number(n.id);
23777
- return r ? await t.prepare("SELECT id FROM content_nodes WHERE id = ?").get(r) ? (await t.prepare("UPDATE content_nodes SET deletedAt = NULL WHERE id = ?").run(r), await $b(e).clear(), new fx(e.env).purgeNode(e, t, r, !0), await px(t), e.json({
23843
+ return r ? await t.prepare("SELECT id FROM content_nodes WHERE id = ?").get(r) ? (await t.prepare("UPDATE content_nodes SET deletedAt = NULL WHERE id = ?").run(r), await $b(e).clear(), await px(t), new fx(e.env).purgeNode(e, t, r, !0), e.json({
23778
23844
  success: !0,
23779
23845
  restoredNodeId: r
23780
23846
  })) : e.json({ error: "Node not found" }, 404) : e.json({ error: "Node ID required" }, 400);
@@ -23782,7 +23848,7 @@ mx.get("/", async (e) => {
23782
23848
  let t = e.get("db"), n = e.req.query("id");
23783
23849
  if (!n) return e.json({ error: "Node ID required" }, 400);
23784
23850
  let r = Number(n);
23785
- return new fx(e.env).purgeNode(e, t, r, !0), await t.prepare("DELETE FROM node_channels WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM tree_nodes WHERE contentId = ?").run(r), await t.prepare("DELETE FROM content_blocks WHERE contentId = ?").run(r), await t.prepare("DELETE FROM node_revisions WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM node_assets WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM node_terms WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM content_nodes WHERE id = ?").run(r), await $b(e).clear(), await px(t), e.json({
23851
+ return await t.prepare("DELETE FROM node_channels WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM tree_nodes WHERE contentId = ?").run(r), await t.prepare("DELETE FROM content_blocks WHERE contentId = ?").run(r), await t.prepare("DELETE FROM node_revisions WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM node_assets WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM node_terms WHERE nodeId = ?").run(r), await t.prepare("DELETE FROM content_nodes WHERE id = ?").run(r), await $b(e).clear(), await px(t), new fx(e.env).purgeNode(e, t, r, !0), e.json({
23786
23852
  success: !0,
23787
23853
  purgedNodeId: r
23788
23854
  });
@@ -23813,20 +23879,23 @@ mx.get("/", async (e) => {
23813
23879
  }
23814
23880
  return e.json({ snapshot: a });
23815
23881
  }), mx.post("/:id/revisions/:version/restore", async (e) => {
23816
- let t = e.get("db"), n = Number(e.req.param("id")), r = Number(e.req.param("version")), i = e.get("user")?.id || null, a = await t.prepare("SELECT snapshotJson FROM node_revisions WHERE nodeId = ? AND version = ?").get(n, r);
23817
- if (!a) return e.json({ error: "Snapshot not found" }, 404);
23818
- let o = {};
23882
+ let t = e.get("db"), n = Number(e.req.param("id")), r = Number(e.req.param("version")), i = e.get("user")?.id || null, a = await t.prepare("SELECT * FROM content_nodes WHERE id = ?").get(n);
23883
+ if (!a) return e.json({ error: "Node not found" }, 404);
23884
+ let o = await t.prepare("SELECT snapshotJson FROM node_revisions WHERE nodeId = ? AND version = ?").get(n, r);
23885
+ if (!o) return e.json({ error: "Snapshot not found" }, 404);
23886
+ let s = {};
23819
23887
  try {
23820
- o = JSON.parse(a.snapshotJson);
23888
+ s = JSON.parse(o.snapshotJson);
23821
23889
  } catch {
23822
23890
  return e.json({ error: "Corrupt snapshot data" }, 500);
23823
23891
  }
23824
- if (await t.prepare("\n UPDATE content_nodes\n SET draftDataJson = ?, title = ?, slug = ?, updatedById = ?, updatedAt = CURRENT_TIMESTAMP, status = 'draft', hasUnpublishedChanges = 1\n WHERE id = ?\n ").run(o.node?.dataJson || "{}", o.node?.title || "Restored Document", o.node?.slug || null, i, n), await t.prepare("DELETE FROM content_blocks WHERE contentId = ? AND isDraft = 1").run(n), o.blocks && Array.isArray(o.blocks) && o.blocks.length > 0) {
23892
+ let c = a.status === "published" || a.publishedAt != null, l = c ? a.title : s.node?.title || "Restored Document", u = c ? a.slug : s.node?.slug || null;
23893
+ if (await t.prepare("\n UPDATE content_nodes\n SET draftDataJson = ?, title = ?, slug = ?, updatedById = ?, updatedAt = CURRENT_TIMESTAMP, status = 'draft', hasUnpublishedChanges = 1\n WHERE id = ?\n ").run(s.node?.dataJson || "{}", l, u, i, n), await t.prepare("DELETE FROM content_blocks WHERE contentId = ? AND isDraft = 1").run(n), s.blocks && Array.isArray(s.blocks) && s.blocks.length > 0) {
23825
23894
  let e = [], r = [];
23826
- for (let t of o.blocks) e.push("(?, ?, ?, ?, ?, 1)"), r.push(n, t.sortOrder, t.elementType, t.dataJson, t.region || "content");
23895
+ for (let t of s.blocks) e.push("(?, ?, ?, ?, ?, 1)"), r.push(n, t.sortOrder, t.elementType, t.dataJson, t.region || "content");
23827
23896
  await t.prepare(`INSERT INTO content_blocks (contentId, sortOrder, elementType, dataJson, region, isDraft) VALUES ${e.join(", ")}`).run(...r);
23828
23897
  }
23829
- return await $b(e).clear(), await px(t), e.json({
23898
+ return await $b(e).clear(), e.json({
23830
23899
  success: !0,
23831
23900
  restoredVersion: r
23832
23901
  });
@@ -23936,7 +24005,7 @@ Tx.get("/tree.json", async (e) => {
23936
24005
  c.push(e.id, e.sortOrder);
23937
24006
  }), c.push(...i), c.push(o), await t.prepare(s).run(...c);
23938
24007
  }
23939
- return await i.clear(), r.length > 0 && a.purgeNode(e, t, r[0].id, !0), await px(t), e.json({
24008
+ return await px(t), await i.clear(), r.length > 0 && a.purgeNode(e, t, r[0].id, !0), e.json({
23940
24009
  success: !0,
23941
24010
  reorderedCount: r.length
23942
24011
  });
@@ -23998,7 +24067,7 @@ Tx.get("/tree.json", async (e) => {
23998
24067
  let e = x.map(() => "(?, ?)").join(", "), n = x.flatMap((e) => [f, e]);
23999
24068
  await t.prepare(`INSERT OR IGNORE INTO node_channels (nodeId, channelId) VALUES ${e}`).run(...n);
24000
24069
  }
24001
- return await i.clear(), await px(t), e.json({
24070
+ return await px(t), await i.clear(), e.json({
24002
24071
  success: !0,
24003
24072
  id: f,
24004
24073
  treeNodeId: f,
@@ -24035,14 +24104,14 @@ Tx.get("/tree.json", async (e) => {
24035
24104
  field: "slug"
24036
24105
  }, 409);
24037
24106
  }
24038
- return await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, d), await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, 0)\n ").run(o, d, g), await i.clear(), a.purgeNode(e, t, d, !0), await px(t), e.json({
24107
+ return await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, d), await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, 0)\n ").run(o, d, g), await px(t), await i.clear(), a.purgeNode(e, t, d, !0), e.json({
24039
24108
  success: !0,
24040
24109
  treeNodeId: d
24041
24110
  });
24042
24111
  }
24043
24112
  if (s === "detach") {
24044
24113
  let r = n.contentId || n.id || n.treeNodeId;
24045
- return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE contentId = ? AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE contentId = ? AND treeId = ?").run(r, o), await i.clear(), a.purgeNode(e, t, r, !0), await px(t), e.json({ success: !0 })) : e.json({ error: "contentId required for detach" }, 400);
24114
+ return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE contentId = ? AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE contentId = ? AND treeId = ?").run(r, o), await px(t), await i.clear(), a.purgeNode(e, t, r, !0), e.json({ success: !0 })) : e.json({ error: "contentId required for detach" }, 400);
24046
24115
  }
24047
24116
  return e.json({ error: "Invalid payload or action" }, 400);
24048
24117
  } catch (t) {
@@ -24106,7 +24175,7 @@ Tx.get("/tree.json", async (e) => {
24106
24175
  await t.prepare(`INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES ${n}`).run(...r);
24107
24176
  }
24108
24177
  }
24109
- return await $b(e).clear(), new fx(e.env).purgeGlobalCache(e, t), await px(t), e.json({
24178
+ return await px(t), await $b(e).clear(), new fx(e.env).purgeGlobalCache(e, t), e.json({
24110
24179
  success: !0,
24111
24180
  id: p,
24112
24181
  key: o,
@@ -24125,7 +24194,7 @@ Tx.get("/tree.json", async (e) => {
24125
24194
  if (!n) return e.json({ error: "Tree ID required" }, 400);
24126
24195
  let r = Number(n);
24127
24196
  try {
24128
- return await t.prepare("DELETE FROM trees WHERE id = ?").run(r), await t.prepare("DELETE FROM channel_trees WHERE treeId = ?").run(r), await t.prepare("DELETE FROM tree_nodes WHERE treeId = ?").run(r), await $b(e).clear(), new fx(e.env).purgeGlobalCache(e, t), await px(t), e.json({
24197
+ return await t.prepare("DELETE FROM trees WHERE id = ?").run(r), await t.prepare("DELETE FROM channel_trees WHERE treeId = ?").run(r), await t.prepare("DELETE FROM tree_nodes WHERE treeId = ?").run(r), await px(t), await $b(e).clear(), new fx(e.env).purgeGlobalCache(e, t), e.json({
24129
24198
  success: !0,
24130
24199
  deletedTreeId: r
24131
24200
  });
@@ -24231,7 +24300,7 @@ kx.get("/", async (e) => {
24231
24300
  `).run(...n);
24232
24301
  }
24233
24302
  let k = $b(e);
24234
- return await k.delete(`channel:${C}`), await k.delete(`channel:${ne}`), await k.clear(), new fx(e.env).purgeGlobalCache(e, t), e.json({
24303
+ return await k.delete(`channel:${C}`), await k.delete(`channel:${ne}`), await k.clear(), await px(t), new fx(e.env).purgeGlobalCache(e, t), e.json({
24235
24304
  success: !0,
24236
24305
  id: ne
24237
24306
  });
@@ -24248,7 +24317,7 @@ kx.get("/", async (e) => {
24248
24317
  try {
24249
24318
  await t.prepare("DELETE FROM channel_trees WHERE channelId = ?").run(r), await t.prepare("DELETE FROM node_channels WHERE channelId = ?").run(r), await t.prepare("DELETE FROM channels WHERE id = ?").run(r);
24250
24319
  let n = $b(e);
24251
- return await n.delete(`channel:${r}`), await n.clear(), new fx(e.env).purgeGlobalCache(e, t), e.json({
24320
+ return await n.delete(`channel:${r}`), await n.clear(), await px(t), new fx(e.env).purgeGlobalCache(e, t), e.json({
24252
24321
  success: !0,
24253
24322
  deletedSiteId: r
24254
24323
  });
@@ -26220,16 +26289,32 @@ function JS(e, t) {
26220
26289
  let n = e.toLowerCase();
26221
26290
  return n.endsWith(".jpg") || n.endsWith(".jpeg") ? "image/jpeg" : n.endsWith(".png") ? "image/png" : n.endsWith(".webp") ? "image/webp" : n.endsWith(".gif") ? "image/gif" : n.endsWith(".svg") ? "image/svg+xml" : n.endsWith(".pdf") ? "application/pdf" : n.endsWith(".mp4") ? "video/mp4" : "application/octet-stream";
26222
26291
  }
26223
- var YS = eb({
26292
+ var YS = [
26293
+ 320,
26294
+ 640,
26295
+ 768,
26296
+ 1024,
26297
+ 1352,
26298
+ 1920,
26299
+ 2560,
26300
+ 3840
26301
+ ];
26302
+ function XS(e) {
26303
+ if (!(!e || isNaN(e) || e <= 0)) return YS.reduce((t, n) => Math.abs(n - e) < Math.abs(t - e) ? n : t);
26304
+ }
26305
+ var ZS = eb({
26224
26306
  method: "get",
26225
26307
  path: "/*",
26226
26308
  summary: "Serve & Optimize Media Assets",
26227
26309
  description: "Retrieves a media asset by its storage key. Optionally optimizes, resizes, and converts images on the fly via query parameters.",
26228
26310
  tags: ["Public Media API"],
26229
26311
  request: { query: X({
26230
- w: J().optional().openapi({ description: "Width in pixels for image optimization (e.g., \"800\")" }),
26231
- h: J().optional().openapi({ description: "Height in pixels for image optimization (e.g., \"600\")" }),
26232
- q: J().optional().openapi({ description: "Quality from 1-100 for image optimization (default: 80)" }),
26312
+ w: J().regex(/^\d+$/).refine((e) => parseInt(e, 10) <= 3840, { message: "Width cannot exceed 3840px" }).optional().openapi({ description: "Width in pixels (Snaps to nearest CDN breakpoint)" }),
26313
+ h: J().regex(/^\d+$/).refine((e) => parseInt(e, 10) <= 3840, { message: "Height cannot exceed 3840px" }).optional().openapi({ description: "Height in pixels (Max 3840)" }),
26314
+ q: J().regex(/^\d+$/).refine((e) => {
26315
+ let t = parseInt(e, 10);
26316
+ return t >= 1 && t <= 100;
26317
+ }, { message: "Quality must be between 1 and 100" }).optional().openapi({ description: "Quality from 1-100 (default: 80)" }),
26233
26318
  f: lv([
26234
26319
  "auto",
26235
26320
  "avif",
@@ -26264,7 +26349,7 @@ var YS = eb({
26264
26349
  }
26265
26350
  },
26266
26351
  400: {
26267
- description: "Asset key missing",
26352
+ description: "Bad Request (e.g. invalid query parameter values)",
26268
26353
  content: { "text/plain": { schema: { type: "string" } } }
26269
26354
  },
26270
26355
  404: {
@@ -26277,54 +26362,54 @@ var YS = eb({
26277
26362
  }
26278
26363
  }
26279
26364
  });
26280
- qS.openapi(YS, async (e) => {
26365
+ qS.openapi(ZS, async (e) => {
26281
26366
  try {
26282
26367
  let t = e.req.path.replace(/^\/media\//, "").replace(/^\//, "");
26283
26368
  if (!t) return e.text("Asset key missing", 400);
26284
- let { w: n, h: r, q: i, f: a } = e.req.valid("query"), o = !!(n || r || i || a), s = e.req.header("Accept") || "", c = "";
26369
+ let { w: n, h: r, q: i, f: a } = e.req.valid("query"), o = !!(n || r || i || a), s = XS(n ? parseInt(n, 10) : void 0), c = e.req.header("Accept") || "", l = "";
26285
26370
  if (o) {
26286
26371
  let e = !a || a === "auto";
26287
- a === "avif" || e && s.includes("image/avif") ? c = "avif" : a === "webp" || e && s.includes("image/webp") ? c = "webp" : a === "jpeg" || a === "jpg" ? c = "jpeg" : a === "png" && (c = "png");
26372
+ a === "avif" || e && c.includes("image/avif") ? l = "avif" : a === "webp" || e && c.includes("image/webp") ? l = "webp" : a === "jpeg" || a === "jpg" ? l = "jpeg" : a === "png" && (l = "png");
26288
26373
  }
26289
- let l = typeof caches < "u" && caches?.default ? caches.default : null, u = e.req.url;
26290
- if (o && c && !a) {
26291
- let e = new URL(u);
26292
- e.searchParams.set("_resolvedFormat", c), u = e.toString();
26374
+ let u = typeof caches < "u" && caches?.default ? caches.default : null, d = e.req.url;
26375
+ if (o) {
26376
+ let e = new URL(d);
26377
+ s && e.searchParams.set("w", String(s)), l && !a && e.searchParams.set("_resolvedFormat", l), d = e.toString();
26293
26378
  }
26294
- let d = l ? new Request(u, e.req.raw) : null;
26295
- if (l && d) {
26296
- let e = await l.match(d);
26379
+ let f = u ? new Request(d, e.req.raw) : null;
26380
+ if (u && f) {
26381
+ let e = await u.match(f);
26297
26382
  if (e) return e;
26298
26383
  }
26299
- let { storage: f, optimizer: p } = ix(e.env), m = await f.get(t);
26300
- if (!m) return e.text(`Asset not found: ${t}`, 404);
26384
+ let { storage: p, optimizer: m } = ix(e.env), h = await p.get(t);
26385
+ if (!h) return e.text(`Asset not found: ${t}`, 404);
26301
26386
  if (!o) {
26302
- let n = e.body(m, 200, {
26387
+ let n = e.body(h, 200, {
26303
26388
  "Content-Type": JS(t),
26304
26389
  "Cache-Control": "public, max-age=31536000, immutable"
26305
26390
  });
26306
- return l && d && e.executionCtx?.waitUntil && e.executionCtx.waitUntil(l.put(d, n.clone())), n;
26391
+ return u && f && e.executionCtx?.waitUntil && e.executionCtx.waitUntil(u.put(f, n.clone())), n;
26307
26392
  }
26308
- let h = {
26309
- width: n ? parseInt(n, 10) : void 0,
26393
+ let g = {
26394
+ width: s,
26310
26395
  height: r ? parseInt(r, 10) : void 0,
26311
26396
  quality: i ? parseInt(i, 10) : 80,
26312
- format: c
26313
- }, g = await p.process(m, h, {
26397
+ format: l
26398
+ }, _ = await m.process(h, g, {
26314
26399
  key: t,
26315
26400
  env: e.env,
26316
26401
  req: e.req.raw
26317
- }), _ = JS(t, c), v = e.body(g, 200, {
26318
- "Content-Type": _,
26402
+ }), v = JS(t, l), y = e.body(_, 200, {
26403
+ "Content-Type": v,
26319
26404
  "Cache-Control": "public, max-age=31536000, immutable"
26320
26405
  });
26321
- return l && d && e.executionCtx?.waitUntil && e.executionCtx.waitUntil(l.put(d, v.clone())), v;
26406
+ return u && f && e.executionCtx?.waitUntil && e.executionCtx.waitUntil(u.put(f, y.clone())), y;
26322
26407
  } catch (t) {
26323
26408
  return console.error("❌ [Public Assets Error]:", t), e.text("Internal Server Error", 500);
26324
26409
  }
26325
26410
  });
26326
- var XS = new $y();
26327
- function ZS(e, t) {
26411
+ var QS = new $y();
26412
+ function $S(e, t) {
26328
26413
  let n = /* @__PURE__ */ new Date();
26329
26414
  if (e && String(e).trim() !== "") {
26330
26415
  let t = new Date(e);
@@ -26336,31 +26421,31 @@ function ZS(e, t) {
26336
26421
  }
26337
26422
  return !0;
26338
26423
  }
26339
- var QS = X({
26424
+ var eC = X({
26340
26425
  channelId: J().optional().openapi({ description: "Optional channel ID/key to filter and resolve root paths for" }),
26341
26426
  channel: J().optional().openapi({ description: "Legacy alias for channelId" })
26342
- }), $S = z_(J()).openapi("PathsResponse"), eC = X({
26427
+ }), tC = z_(J()).openapi("PathsResponse"), nC = X({
26343
26428
  error: J(),
26344
26429
  details: J().optional()
26345
- }).openapi("ErrorResponse"), tC = eb({
26430
+ }).openapi("ErrorResponse"), rC = eb({
26346
26431
  method: "get",
26347
26432
  path: "/",
26348
26433
  summary: "Fetch All Public Paths",
26349
26434
  description: "Retrieves a deduplicated array of all active, published node paths leveraging the $O(1)$ node_routes table. Includes both tree-bound and standalone pages.",
26350
26435
  tags: ["Public Paths API"],
26351
- request: { query: QS },
26436
+ request: { query: eC },
26352
26437
  responses: {
26353
26438
  200: {
26354
26439
  description: "Array of valid public URL paths",
26355
- content: { "application/json": { schema: $S } }
26440
+ content: { "application/json": { schema: tC } }
26356
26441
  },
26357
26442
  500: {
26358
26443
  description: "Server error fetching paths",
26359
- content: { "application/json": { schema: eC } }
26444
+ content: { "application/json": { schema: nC } }
26360
26445
  }
26361
26446
  }
26362
26447
  });
26363
- XS.openapi(tC, async (e) => {
26448
+ QS.openapi(rC, async (e) => {
26364
26449
  try {
26365
26450
  let t = e.get("db"), n = e.req.valid("query"), r = n.channelId || n.channel, i = "", a = [];
26366
26451
  if (r && r !== "all") {
@@ -26385,7 +26470,7 @@ XS.openapi(tC, async (e) => {
26385
26470
  let t = !0;
26386
26471
  if (e.dataJson) try {
26387
26472
  let n = typeof e.dataJson == "string" ? JSON.parse(e.dataJson) : e.dataJson;
26388
- t = ZS(n?.startShowingAt, n?.stopShowingAt);
26473
+ t = $S(n?.startShowingAt, n?.stopShowingAt);
26389
26474
  } catch {}
26390
26475
  t && c.push(e.path);
26391
26476
  }
@@ -26395,12 +26480,12 @@ XS.openapi(tC, async (e) => {
26395
26480
  return console.error("[CMS Paths API] Error:", t), e.json({ error: "Failed to fetch paths" }, 500);
26396
26481
  }
26397
26482
  });
26398
- var nC = new $y();
26399
- function rC(e) {
26483
+ var iC = new $y();
26484
+ function aC(e) {
26400
26485
  let t = (e || "").trim().toLowerCase(), n = dx()?.taxonomies || {};
26401
26486
  return n[t] ? t : n[`${t}s`] ? `${t}s` : t.endsWith("s") && n[t.slice(0, -1)] ? t.slice(0, -1) : t;
26402
26487
  }
26403
- var iC = X({
26488
+ var oC = X({
26404
26489
  id: c_(),
26405
26490
  taxonomy: J(),
26406
26491
  key: J(),
@@ -26410,14 +26495,14 @@ var iC = X({
26410
26495
  data: tv(k_()),
26411
26496
  createdAt: J(),
26412
26497
  updatedAt: J()
26413
- }), aC = X({
26498
+ }), sC = X({
26414
26499
  taxonomy: J(),
26415
- items: z_(iC),
26500
+ items: z_(oC),
26416
26501
  total: c_()
26417
- }).openapi("TaxonomyResponse"), oC = X({
26502
+ }).openapi("TaxonomyResponse"), cC = X({
26418
26503
  error: J(),
26419
26504
  details: J().optional()
26420
- }).openapi("ErrorResponse"), sC = eb({
26505
+ }).openapi("ErrorResponse"), lC = eb({
26421
26506
  method: "get",
26422
26507
  path: "/{taxonomy}",
26423
26508
  summary: "Fetch Public Taxonomy Terms",
@@ -26427,17 +26512,17 @@ var iC = X({
26427
26512
  responses: {
26428
26513
  200: {
26429
26514
  description: "List of taxonomy terms",
26430
- content: { "application/json": { schema: aC } }
26515
+ content: { "application/json": { schema: sC } }
26431
26516
  },
26432
26517
  500: {
26433
26518
  description: "Server error",
26434
- content: { "application/json": { schema: oC } }
26519
+ content: { "application/json": { schema: cC } }
26435
26520
  }
26436
26521
  }
26437
26522
  });
26438
- nC.openapi(sC, async (e) => {
26523
+ iC.openapi(lC, async (e) => {
26439
26524
  try {
26440
- let t = e.get("db"), n = $b(e), r = e.req.valid("param").taxonomy, i = rC(r), a = `taxonomy:public:${i}`, o = e.req.header("x-bypass-cache") === "true";
26525
+ let t = e.get("db"), n = $b(e), r = e.req.valid("param").taxonomy, i = aC(r), a = `taxonomy:public:${i}`, o = e.req.header("x-bypass-cache") === "true";
26441
26526
  if (!o) {
26442
26527
  let t = await n.get(a);
26443
26528
  if (t) return e.header("X-Cache", "HIT"), e.json(t);
@@ -26471,7 +26556,7 @@ nC.openapi(sC, async (e) => {
26471
26556
  return console.error("[Public Taxonomy API] Error fetching terms:", t), e.json({ error: "Failed to fetch taxonomy terms" }, 500);
26472
26557
  }
26473
26558
  });
26474
- var $ = new $y(), cC = !1;
26559
+ var $ = new $y(), uC = !1;
26475
26560
  $.use("*", t()), $.use("/media/*", t({
26476
26561
  origin: "*",
26477
26562
  allowMethods: [
@@ -26492,13 +26577,13 @@ $.use("*", t()), $.use("/media/*", t({
26492
26577
  let n = new URL(e.req.url), r = (e.req.header("host") || n.hostname).split(":")[0];
26493
26578
  e.set("currentDomain", r);
26494
26579
  let i = wb(e);
26495
- if (e.set("db", i), !cC) try {
26496
- await Mb(i), cC = !0;
26580
+ if (e.set("db", i), !uC) try {
26581
+ await Mb(i), uC = !0;
26497
26582
  } catch (e) {
26498
26583
  console.error("❌ [CMS Server] Failed to initialize system tables on cold start:", e);
26499
26584
  }
26500
26585
  await t();
26501
- }), $.route("/media", qS), $.use("/media/*", Pb()), $.route("/api/cms/nodes", mx), $.route("/api/cms/nodes.json", mx), $.route("/api/cms/formcsv", mx), $.route("/api/cms/formcsv.json", mx), $.route("/api/cms/stats", tS), $.route("/api/cms/stats.json", tS), $.route("/api/cms", Tx), $.route("/api/cms/trees.json", Tx), $.route("/api/cms/channels", kx), $.route("/api/cms/channels.json", kx), $.route("/api/cms/taxonomies", Ax), $.route("/api/cms/taxonomies.json", Ax), $.route("/api/cms/schemas", Xx), $.route("/api/cms/schemas.json", Xx), $.route("/api/cms", Qx), $.route("/api/admin/assets", nS), $.route("/api/v1/node", oS), $.route("/api/v1/tree", ES), $.route("/api/v1/channel", FS), $.route("/api/v1/all-paths", XS), $.route("/api/v1/taxonomies", nC), $.doc("/api/openapi.json", {
26586
+ }), $.route("/media", qS), $.use("/media/*", Pb()), $.route("/api/cms/nodes", mx), $.route("/api/cms/nodes.json", mx), $.route("/api/cms/formcsv", mx), $.route("/api/cms/formcsv.json", mx), $.route("/api/cms/stats", tS), $.route("/api/cms/stats.json", tS), $.route("/api/cms", Tx), $.route("/api/cms/trees.json", Tx), $.route("/api/cms/channels", kx), $.route("/api/cms/channels.json", kx), $.route("/api/cms/taxonomies", Ax), $.route("/api/cms/taxonomies.json", Ax), $.route("/api/cms/schemas", Xx), $.route("/api/cms/schemas.json", Xx), $.route("/api/cms", Qx), $.route("/api/admin/assets", nS), $.route("/api/v1/node", oS), $.route("/api/v1/tree", ES), $.route("/api/v1/channel", FS), $.route("/api/v1/all-paths", QS), $.route("/api/v1/taxonomies", iC), $.doc("/api/openapi.json", {
26502
26587
  openapi: "3.0.0",
26503
26588
  info: {
26504
26589
  title: "KAMA Engine Public API",
package/dist/index.html CHANGED
@@ -6,8 +6,8 @@
6
6
  <title>KAMA</title>
7
7
  <link rel="icon" href="/favicon.png">
8
8
  <link rel="apple-touch-icon" sizes="180x180" href="/favicon.png">
9
- <script type="module" crossorigin src="/assets/index-BL8s3kNS.js"></script>
10
- <link rel="stylesheet" crossorigin href="/assets/index-CHQAZAAH.css">
9
+ <script type="module" crossorigin src="/assets/index-Dxb_fjpM.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-BP3z9TWM.css">
11
11
  </head>
12
12
  <body>
13
13
  <div id="app"></div>