@brightsideoy/kama 0.1.0-alpha.158 → 0.1.0-alpha.159

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.
@@ -4039,6 +4039,7 @@ var u = /* @__PURE__ */ o({
4039
4039
  footer: {
4040
4040
  label: "Footer",
4041
4041
  inherit: "parent",
4042
+ purgeTargets: ["/partials/footer"],
4042
4043
  disallowedBlocks: [
4043
4044
  "hero",
4044
4045
  "image_card",
@@ -4092,6 +4093,7 @@ var u = /* @__PURE__ */ o({
4092
4093
  footer: {
4093
4094
  label: "Footer",
4094
4095
  inherit: "parent",
4096
+ purgeTargets: ["/partials/footer"],
4095
4097
  disallowedBlocks: [
4096
4098
  "hero",
4097
4099
  "image_card",
@@ -4154,6 +4156,7 @@ var u = /* @__PURE__ */ o({
4154
4156
  footer: {
4155
4157
  label: "Footer",
4156
4158
  inherit: "parent",
4159
+ purgeTargets: ["/partials/footer"],
4157
4160
  disallowedBlocks: [
4158
4161
  "hero",
4159
4162
  "image_card",
@@ -6041,52 +6044,80 @@ var V = class {
6041
6044
  console.warn("[CacheManager] Failed to purge internal worker cache:", e);
6042
6045
  }
6043
6046
  }
6044
- purgeNode(e, t, n, r = !1) {
6045
- let i = (async () => {
6047
+ purgeNode(e, t, n, r = !1, i = []) {
6048
+ let a = (async () => {
6046
6049
  try {
6047
- let i = t.prepare("SELECT slug, status, language FROM content_nodes WHERE id = ?"), a = (typeof i.get == "function" ? await i.get(n) : (await i.bind(n).all())?.results?.[0]) || {};
6048
- if (!a.slug) return;
6049
- let o = t.prepare("\n SELECT c.id, c.key, c.baseUrl, c.defaultLanguage \n FROM node_channels nc \n JOIN channels c ON nc.channelId = c.id \n WHERE nc.nodeId = ?\n "), s = typeof o.all == "function" ? await o.all(n) : await o.bind(n).all(), c = Array.isArray(s) ? s : s?.results || [], l = c.length > 0 ? c[0]?.defaultLanguage?.trim() : null, u = a.language?.trim() || l || null, d = String(a.slug).replace(/^\/+|\/+$/g, ""), f = this.getEnv(e).frontendUrl, p = /* @__PURE__ */ new Set();
6050
- for (let e of c) if (e.baseUrl && typeof e.baseUrl == "string" && e.baseUrl.trim() !== "") {
6050
+ let a = t.prepare("SELECT slug, status, language FROM content_nodes WHERE id = ?"), o = (typeof a.get == "function" ? await a.get(n) : (await a.bind(n).all())?.results?.[0]) || {};
6051
+ if (!o.slug) return;
6052
+ let s = t.prepare("\n SELECT c.id, c.key, c.baseUrl, c.defaultLanguage \n FROM node_channels nc \n JOIN channels c ON nc.channelId = c.id \n WHERE nc.nodeId = ?\n "), c = typeof s.all == "function" ? await s.all(n) : await s.bind(n).all(), l = Array.isArray(c) ? c : c?.results || [], u = l.length > 0 ? l[0]?.defaultLanguage?.trim() : null, d = o.language?.trim() || u || null, f = String(o.slug).replace(/^\/+|\/+$/g, ""), p = this.getEnv(e).frontendUrl, m = /* @__PURE__ */ new Set();
6053
+ for (let e of l) if (e.baseUrl && typeof e.baseUrl == "string" && e.baseUrl.trim() !== "") {
6051
6054
  let t = e.baseUrl.trim();
6052
- !t.startsWith("http://") && !t.startsWith("https://") && (t = `https://${t}`), p.add(t.replace(/\/+$/, ""));
6055
+ !t.startsWith("http://") && !t.startsWith("https://") && (t = `https://${t}`), m.add(t.replace(/\/+$/, ""));
6053
6056
  }
6054
- p.size === 0 && f && p.add(f);
6055
- let m = t.prepare("\n SELECT DISTINCT a.cdnUrl, a.storageKeyPrivate\n FROM node_assets na\n JOIN assets a ON na.assetId = a.id\n WHERE na.nodeId = ?\n "), h = typeof m.all == "function" ? await m.all(n) : await m.bind(n).all(), g = (Array.isArray(h) ? h : h?.results || []).map((t) => t.url || this.getSafeUrl(t.cdnUrl, t.storageKeyPrivate, this.getEnv(e).cdnDomain)).filter(Boolean);
6056
- await this.purgeInternalWorkerCache(e, t, n, d, u);
6057
- let _ = this.getEnv(e).cdnDomain, { zoneId: v, apiToken: y } = this.getEnv(e);
6058
- for (let t of p) {
6059
- let n = d === "home" || !d ? t : `${t}/${d}`, i = [n, `${n}/`], a = u ? `&lang=${u}` : "";
6060
- if (c.length > 0) for (let e of c) e.id && i.push(`https://${_}/api/v1/node/${d}?channelId=${e.id}${a}`), e.key && i.push(`https://${_}/api/v1/node/${d}?channelId=${e.key}${a}`);
6061
- else i.push(`https://${_}/api/v1/node/${d}${u ? `?lang=${u}` : ""}`);
6062
- for (let e of g) e && i.push(`${t}${e.startsWith("/") ? e : `/${e}`}`);
6063
- if (await this.callAstroPurge(e, { urls: i }, t), v && y && !this.isLocalEnv(e, t) && fetch(`https://api.cloudflare.com/client/v4/zones/${v}/purge_cache`, {
6057
+ m.size === 0 && p && m.add(p);
6058
+ let h = t.prepare("\n SELECT DISTINCT a.cdnUrl, a.storageKeyPrivate\n FROM node_assets na\n JOIN assets a ON na.assetId = a.id\n WHERE na.nodeId = ?\n "), g = typeof h.all == "function" ? await h.all(n) : await h.bind(n).all(), _ = (Array.isArray(g) ? g : g?.results || []).map((t) => t.url || this.getSafeUrl(t.cdnUrl, t.storageKeyPrivate, this.getEnv(e).cdnDomain)).filter(Boolean);
6059
+ await this.purgeInternalWorkerCache(e, t, n, f, d);
6060
+ let v = this.getEnv(e).cdnDomain, { zoneId: y, apiToken: b } = this.getEnv(e), x = e.env?.BUNDLED_DEFINITIONS || {}, S = /* @__PURE__ */ new Set();
6061
+ if (i.length > 0 && x.layouts) for (let e of Object.values(x.layouts)) for (let t of i) {
6062
+ let n = e.regions?.[t];
6063
+ if (n) {
6064
+ let e = n.purgeTargets || n.purgeTarget;
6065
+ if (e) {
6066
+ let t = Array.isArray(e) ? e : [e];
6067
+ for (let e of t) typeof e == "string" && e.trim() && S.add(e.trim());
6068
+ }
6069
+ }
6070
+ }
6071
+ for (let t of m) {
6072
+ let n = f === "home" || !f ? t : `${t}/${f}`, i = [n, `${n}/`];
6073
+ r && i.push(`${t}/partials/nav`);
6074
+ for (let e of S) {
6075
+ let n = e.startsWith("/") ? e : `/${e}`;
6076
+ i.push(`${t}${n}`);
6077
+ }
6078
+ let a = d ? `&lang=${d}` : "";
6079
+ if (l.length > 0) for (let e of l) e.id && i.push(`https://${v}/api/v1/node/${f}?channelId=${e.id}${a}`), e.key && i.push(`https://${v}/api/v1/node/${f}?channelId=${e.key}${a}`), r && (e.id && i.push(`https://${v}/api/v1/tree/main-nav?channelId=${e.id}`), e.key && i.push(`https://${v}/api/v1/tree/main-nav?channelId=${e.key}`));
6080
+ else i.push(`https://${v}/api/v1/node/${f}${d ? `?lang=${d}` : ""}`), r && i.push(`https://${v}/api/v1/tree/main-nav`);
6081
+ for (let e of _) e && i.push(`${t}${e.startsWith("/") ? e : `/${e}`}`);
6082
+ if (await this.callAstroPurge(e, { urls: i }, t), y && b && !this.isLocalEnv(e, t) && fetch(`https://api.cloudflare.com/client/v4/zones/${y}/purge_cache`, {
6064
6083
  method: "POST",
6065
6084
  headers: {
6066
- Authorization: `Bearer ${y}`,
6085
+ Authorization: `Bearer ${b}`,
6067
6086
  "Content-Type": "application/json"
6068
6087
  },
6069
6088
  body: JSON.stringify({ files: i })
6070
- }).catch(() => {}), !r) {
6089
+ }).catch(() => {}), r) {
6090
+ let e = `${t}/partials/nav?_warmer=${Date.now()}`;
6091
+ console.log(`[CacheManager] Prewarming cache at: ${e}`), fetch(e, { headers: {
6092
+ "X-Cache-Warmer": "true",
6093
+ "User-Agent": "Kama-Cache-Warmer/1.0"
6094
+ } }).catch((e) => console.warn("[CacheManager] Prewarm fetch error:", e));
6095
+ } else {
6071
6096
  let e = `${n}${n.includes("?") ? "&" : "?"} _warmer=${Date.now()}`;
6072
6097
  console.log(`[CacheManager] Prewarming cache at: ${e}`), fetch(e, { headers: {
6073
6098
  "X-Cache-Warmer": "true",
6074
6099
  "User-Agent": "Kama-Cache-Warmer/1.0"
6075
6100
  } }).catch((e) => console.warn("[CacheManager] Prewarm fetch error:", e));
6076
6101
  }
6102
+ for (let e of S) {
6103
+ let n = `${t}${e.startsWith("/") ? e : `/${e}`}?_warmer=${Date.now()}`;
6104
+ console.log(`[CacheManager] Prewarming custom target: ${n}`), fetch(n, { headers: {
6105
+ "X-Cache-Warmer": "true",
6106
+ "User-Agent": "Kama-Cache-Warmer/1.0"
6107
+ } }).catch(() => {});
6108
+ }
6077
6109
  }
6078
- r && await this.purgeGlobalCacheDirect(e, t);
6079
6110
  } catch (e) {
6080
6111
  console.warn("[CacheManager] Dynamic purgeNode failed:", e);
6081
6112
  }
6082
6113
  })();
6083
- this.dispatch(e, i);
6114
+ this.dispatch(e, a);
6084
6115
  }
6085
6116
  purgeAsset(e, t, n) {
6086
6117
  let r = (async () => {
6087
6118
  try {
6088
6119
  let r = t.prepare("\n SELECT DISTINCT cn.id\n FROM node_assets na\n INNER JOIN content_nodes cn ON na.nodeId = cn.id\n LEFT JOIN assets crop ON na.assetId = crop.id\n WHERE cn.status = 'published' AND (\n na.assetId = ? OR crop.parentAssetId = ? OR crop.id = (SELECT parentAssetId FROM assets WHERE id = ?)\n )\n "), i = typeof r.all == "function" ? await r.all(n, n, n) : await r.bind(n, n, n).all(), a = Array.isArray(i) ? i : i?.results || [];
6089
- for (let n of a) n.id && this.purgeNode(e, t, n.id, !1);
6120
+ for (let n of a) n.id && this.purgeNode(e, t, n.id, !1, []);
6090
6121
  } catch (e) {
6091
6122
  console.warn("[CacheManager] purgeAsset failed:", e);
6092
6123
  }
@@ -6529,7 +6560,7 @@ H.get("/", async (e) => {
6529
6560
  await he(t, n, i);
6530
6561
  }
6531
6562
  }
6532
- return await I(e).clear(), D && new V(e.env).purgeNode(e, t, D, k), e.json({
6563
+ return await I(e).clear(), e.json({
6533
6564
  success: !0,
6534
6565
  id: D,
6535
6566
  status: "draft",
@@ -6746,7 +6777,7 @@ W.get("/tree.json", async (e) => {
6746
6777
  c.push(e.id, e.sortOrder);
6747
6778
  }), c.push(...i), c.push(o), await t.prepare(s).run(...c);
6748
6779
  }
6749
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6780
+ return await i.clear(), r.length > 0 && a.purgeNode(e, t, r[0].id, !0), e.json({
6750
6781
  success: !0,
6751
6782
  reorderedCount: r.length
6752
6783
  });
@@ -6755,9 +6786,9 @@ W.get("/tree.json", async (e) => {
6755
6786
  delete m._isTreeDraft, delete m._draftParentId, delete m.isNew, delete m.nodeType, delete m.contentType, delete m.title, delete m.slug, delete m.hidden, delete m.is_hidden, delete m.isHidden, delete m.status;
6756
6787
  let g = G(u), _ = e.get("user")?.id || null;
6757
6788
  if (s === "create") {
6758
- let r = m.slug || n.slug || c, s = r ? String(r).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : null;
6759
- if (s) {
6760
- let r = g === null ? "tn.parentId IS NULL" : "tn.parentId = ?", i = n.contentId || n.id ? Number(n.contentId || n.id) : null, a = `
6789
+ let r = m.slug || n.slug || c, a = r ? String(r).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : null;
6790
+ if (a) {
6791
+ let r = g === null ? "tn.parentId IS NULL" : "tn.parentId = ?", i = n.contentId || n.id ? Number(n.contentId || n.id) : null, s = `
6761
6792
  SELECT 1
6762
6793
  FROM tree_nodes tn
6763
6794
  INNER JOIN content_nodes cn ON tn.contentId = cn.id
@@ -6765,51 +6796,51 @@ W.get("/tree.json", async (e) => {
6765
6796
  AND ${r}
6766
6797
  AND cn.slug = ?
6767
6798
  AND (cn.deletedAt IS NULL OR cn.deletedAt = '')
6768
- `, c = g === null ? [o, s] : [
6799
+ `, c = g === null ? [o, a] : [
6769
6800
  o,
6770
6801
  g,
6771
- s
6802
+ a
6772
6803
  ];
6773
- if (i && !isNaN(i) && (a += " AND cn.id != ?", c.push(i)), a += " LIMIT 1", await t.prepare(a).get(...c)) return e.json({
6774
- error: `The slug '${s}' is already in use by another page in this section.`,
6804
+ if (i && !isNaN(i) && (s += " AND cn.id != ?", c.push(i)), s += " LIMIT 1", await t.prepare(s).get(...c)) return e.json({
6805
+ error: `The slug '${a}' is already in use by another page in this section.`,
6775
6806
  field: "slug"
6776
6807
  }, 409);
6777
6808
  }
6778
- let u = m.language?.trim() || n.language?.trim() || null;
6779
- if (!u) {
6809
+ let s = m.language?.trim() || n.language?.trim() || null;
6810
+ if (!s) {
6780
6811
  let e = await t.prepare("SELECT dataJson FROM trees WHERE id = ?").get(o);
6781
6812
  if (e?.dataJson) try {
6782
6813
  let t = typeof e.dataJson == "string" ? JSON.parse(e.dataJson) : e.dataJson;
6783
- t.defaultLanguage && (u = t.defaultLanguage.trim());
6814
+ t.defaultLanguage && (s = t.defaultLanguage.trim());
6784
6815
  } catch {}
6785
6816
  }
6786
- if (!u) {
6817
+ if (!s) {
6787
6818
  let e = [];
6788
6819
  if (n.channels && Array.isArray(n.channels) ? e = n.channels : n.channelId && n.channelId !== "all" && (e = [n.channelId]), e.length > 0) {
6789
6820
  let n = Number(e[0]);
6790
6821
  if (!isNaN(n)) {
6791
6822
  let e = await t.prepare("SELECT defaultLanguage FROM channels WHERE id = ?").get(n);
6792
- e?.defaultLanguage && (u = e.defaultLanguage.trim());
6823
+ e?.defaultLanguage && (s = e.defaultLanguage.trim());
6793
6824
  }
6794
6825
  }
6795
6826
  }
6796
- let d = JSON.stringify(m), f = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, ?, ?)\n ").run(l || "page", c || "Untitled", s, u, h, d, _, _), p = Number(f.lastInsertRowid);
6797
- await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, p);
6798
- let v = g === null ? "parentId IS NULL" : "parentId = ?", y = g === null ? [o] : [o, g], b = (await t.prepare(`
6827
+ let u = JSON.stringify(m), d = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, ?, ?)\n ").run(l || "page", c || "Untitled", a, s, h, u, _, _), f = Number(d.lastInsertRowid);
6828
+ await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, f);
6829
+ let p = g === null ? "parentId IS NULL" : "parentId = ?", v = g === null ? [o] : [o, g], y = (await t.prepare(`
6799
6830
  SELECT COALESCE(MAX(sortOrder), -1) + 1 as nextSort
6800
6831
  FROM tree_nodes
6801
- WHERE treeId = ? AND ${v}
6802
- `).get(...y))?.nextSort ?? 0, x = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, ?)\n ").run(o, p, g, b), S = Number(x.lastInsertRowid), C = [];
6803
- n.channels && Array.isArray(n.channels) ? C = n.channels : n.channelId && n.channelId !== "all" && (C = [n.channelId]);
6804
- let w = [...new Set(C)].filter((e) => e && e !== "all").map((e) => Number(e));
6805
- if (w.length > 0) {
6806
- let e = w.map(() => "(?, ?)").join(", "), n = w.flatMap((e) => [p, e]);
6832
+ WHERE treeId = ? AND ${p}
6833
+ `).get(...v))?.nextSort ?? 0, b = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, ?)\n ").run(o, f, g, y), x = Number(b.lastInsertRowid), S = [];
6834
+ n.channels && Array.isArray(n.channels) ? S = n.channels : n.channelId && n.channelId !== "all" && (S = [n.channelId]);
6835
+ let C = [...new Set(S)].filter((e) => e && e !== "all").map((e) => Number(e));
6836
+ if (C.length > 0) {
6837
+ let e = C.map(() => "(?, ?)").join(", "), n = C.flatMap((e) => [f, e]);
6807
6838
  await t.prepare(`INSERT OR IGNORE INTO node_channels (nodeId, channelId) VALUES ${e}`).run(...n);
6808
6839
  }
6809
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6840
+ return await i.clear(), e.json({
6810
6841
  success: !0,
6811
- id: p,
6812
- treeNodeId: S,
6842
+ id: f,
6843
+ treeNodeId: x,
6813
6844
  status: "draft"
6814
6845
  }, 201);
6815
6846
  }
@@ -6844,14 +6875,14 @@ W.get("/tree.json", async (e) => {
6844
6875
  }
6845
6876
  await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, d);
6846
6877
  let r = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, 0)\n ").run(o, d, g), s = Number(r.lastInsertRowid);
6847
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6878
+ return await i.clear(), a.purgeNode(e, t, d, !0), e.json({
6848
6879
  success: !0,
6849
6880
  treeNodeId: s
6850
6881
  });
6851
6882
  }
6852
6883
  if (s === "detach") {
6853
6884
  let r = n.treeNodeId || n.contentId;
6854
- return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?").run(r, r, o), await i.clear(), a.purgeGlobalCache(e, t), e.json({ success: !0 })) : e.json({ error: "treeNodeId or contentId required for detach" }, 400);
6885
+ return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?").run(r, r, o), await i.clear(), a.purgeNode(e, t, r, !0), e.json({ success: !0 })) : e.json({ error: "treeNodeId or contentId required for detach" }, 400);
6855
6886
  }
6856
6887
  return e.json({ error: "Invalid payload or action" }, 400);
6857
6888
  } catch (t) {
@@ -6915,7 +6946,7 @@ W.get("/tree.json", async (e) => {
6915
6946
  await t.prepare(`INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES ${n}`).run(...r);
6916
6947
  }
6917
6948
  }
6918
- return await I(e).clear(), new V(e.env).purgeGlobalCache(e, t), e.json({
6949
+ return await cache.clear(), new V(e.env).purgeGlobalCache(e, t), e.json({
6919
6950
  success: !0,
6920
6951
  id: p,
6921
6952
  key: o,
package/dist/index.html CHANGED
@@ -6,7 +6,7 @@
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-B-WxBAFU.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-DIxtk5i5.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-C5TmJVER.css">
11
11
  </head>
12
12
  <body>
package/dist/index.js CHANGED
@@ -4041,6 +4041,7 @@ var u = /* @__PURE__ */ o({
4041
4041
  footer: {
4042
4042
  label: "Footer",
4043
4043
  inherit: "parent",
4044
+ purgeTargets: ["/partials/footer"],
4044
4045
  disallowedBlocks: [
4045
4046
  "hero",
4046
4047
  "image_card",
@@ -4094,6 +4095,7 @@ var u = /* @__PURE__ */ o({
4094
4095
  footer: {
4095
4096
  label: "Footer",
4096
4097
  inherit: "parent",
4098
+ purgeTargets: ["/partials/footer"],
4097
4099
  disallowedBlocks: [
4098
4100
  "hero",
4099
4101
  "image_card",
@@ -4156,6 +4158,7 @@ var u = /* @__PURE__ */ o({
4156
4158
  footer: {
4157
4159
  label: "Footer",
4158
4160
  inherit: "parent",
4161
+ purgeTargets: ["/partials/footer"],
4159
4162
  disallowedBlocks: [
4160
4163
  "hero",
4161
4164
  "image_card",
@@ -6059,52 +6062,80 @@ var V = class {
6059
6062
  console.warn("[CacheManager] Failed to purge internal worker cache:", e);
6060
6063
  }
6061
6064
  }
6062
- purgeNode(e, t, n, r = !1) {
6063
- let i = (async () => {
6065
+ purgeNode(e, t, n, r = !1, i = []) {
6066
+ let a = (async () => {
6064
6067
  try {
6065
- let i = t.prepare("SELECT slug, status, language FROM content_nodes WHERE id = ?"), a = (typeof i.get == "function" ? await i.get(n) : (await i.bind(n).all())?.results?.[0]) || {};
6066
- if (!a.slug) return;
6067
- let o = t.prepare("\n SELECT c.id, c.key, c.baseUrl, c.defaultLanguage \n FROM node_channels nc \n JOIN channels c ON nc.channelId = c.id \n WHERE nc.nodeId = ?\n "), s = typeof o.all == "function" ? await o.all(n) : await o.bind(n).all(), c = Array.isArray(s) ? s : s?.results || [], l = c.length > 0 ? c[0]?.defaultLanguage?.trim() : null, u = a.language?.trim() || l || null, d = String(a.slug).replace(/^\/+|\/+$/g, ""), f = this.getEnv(e).frontendUrl, p = /* @__PURE__ */ new Set();
6068
- for (let e of c) if (e.baseUrl && typeof e.baseUrl == "string" && e.baseUrl.trim() !== "") {
6068
+ let a = t.prepare("SELECT slug, status, language FROM content_nodes WHERE id = ?"), o = (typeof a.get == "function" ? await a.get(n) : (await a.bind(n).all())?.results?.[0]) || {};
6069
+ if (!o.slug) return;
6070
+ let s = t.prepare("\n SELECT c.id, c.key, c.baseUrl, c.defaultLanguage \n FROM node_channels nc \n JOIN channels c ON nc.channelId = c.id \n WHERE nc.nodeId = ?\n "), c = typeof s.all == "function" ? await s.all(n) : await s.bind(n).all(), l = Array.isArray(c) ? c : c?.results || [], u = l.length > 0 ? l[0]?.defaultLanguage?.trim() : null, d = o.language?.trim() || u || null, f = String(o.slug).replace(/^\/+|\/+$/g, ""), p = this.getEnv(e).frontendUrl, m = /* @__PURE__ */ new Set();
6071
+ for (let e of l) if (e.baseUrl && typeof e.baseUrl == "string" && e.baseUrl.trim() !== "") {
6069
6072
  let t = e.baseUrl.trim();
6070
- !t.startsWith("http://") && !t.startsWith("https://") && (t = `https://${t}`), p.add(t.replace(/\/+$/, ""));
6073
+ !t.startsWith("http://") && !t.startsWith("https://") && (t = `https://${t}`), m.add(t.replace(/\/+$/, ""));
6071
6074
  }
6072
- p.size === 0 && f && p.add(f);
6073
- let m = t.prepare("\n SELECT DISTINCT a.cdnUrl, a.storageKeyPrivate\n FROM node_assets na\n JOIN assets a ON na.assetId = a.id\n WHERE na.nodeId = ?\n "), h = typeof m.all == "function" ? await m.all(n) : await m.bind(n).all(), g = (Array.isArray(h) ? h : h?.results || []).map((t) => t.url || this.getSafeUrl(t.cdnUrl, t.storageKeyPrivate, this.getEnv(e).cdnDomain)).filter(Boolean);
6074
- await this.purgeInternalWorkerCache(e, t, n, d, u);
6075
- let _ = this.getEnv(e).cdnDomain, { zoneId: v, apiToken: y } = this.getEnv(e);
6076
- for (let t of p) {
6077
- let n = d === "home" || !d ? t : `${t}/${d}`, i = [n, `${n}/`], a = u ? `&lang=${u}` : "";
6078
- if (c.length > 0) for (let e of c) e.id && i.push(`https://${_}/api/v1/node/${d}?channelId=${e.id}${a}`), e.key && i.push(`https://${_}/api/v1/node/${d}?channelId=${e.key}${a}`);
6079
- else i.push(`https://${_}/api/v1/node/${d}${u ? `?lang=${u}` : ""}`);
6080
- for (let e of g) e && i.push(`${t}${e.startsWith("/") ? e : `/${e}`}`);
6081
- if (await this.callAstroPurge(e, { urls: i }, t), v && y && !this.isLocalEnv(e, t) && fetch(`https://api.cloudflare.com/client/v4/zones/${v}/purge_cache`, {
6075
+ m.size === 0 && p && m.add(p);
6076
+ let h = t.prepare("\n SELECT DISTINCT a.cdnUrl, a.storageKeyPrivate\n FROM node_assets na\n JOIN assets a ON na.assetId = a.id\n WHERE na.nodeId = ?\n "), g = typeof h.all == "function" ? await h.all(n) : await h.bind(n).all(), _ = (Array.isArray(g) ? g : g?.results || []).map((t) => t.url || this.getSafeUrl(t.cdnUrl, t.storageKeyPrivate, this.getEnv(e).cdnDomain)).filter(Boolean);
6077
+ await this.purgeInternalWorkerCache(e, t, n, f, d);
6078
+ let v = this.getEnv(e).cdnDomain, { zoneId: y, apiToken: b } = this.getEnv(e), x = e.env?.BUNDLED_DEFINITIONS || {}, S = /* @__PURE__ */ new Set();
6079
+ if (i.length > 0 && x.layouts) for (let e of Object.values(x.layouts)) for (let t of i) {
6080
+ let n = e.regions?.[t];
6081
+ if (n) {
6082
+ let e = n.purgeTargets || n.purgeTarget;
6083
+ if (e) {
6084
+ let t = Array.isArray(e) ? e : [e];
6085
+ for (let e of t) typeof e == "string" && e.trim() && S.add(e.trim());
6086
+ }
6087
+ }
6088
+ }
6089
+ for (let t of m) {
6090
+ let n = f === "home" || !f ? t : `${t}/${f}`, i = [n, `${n}/`];
6091
+ r && i.push(`${t}/partials/nav`);
6092
+ for (let e of S) {
6093
+ let n = e.startsWith("/") ? e : `/${e}`;
6094
+ i.push(`${t}${n}`);
6095
+ }
6096
+ let a = d ? `&lang=${d}` : "";
6097
+ if (l.length > 0) for (let e of l) e.id && i.push(`https://${v}/api/v1/node/${f}?channelId=${e.id}${a}`), e.key && i.push(`https://${v}/api/v1/node/${f}?channelId=${e.key}${a}`), r && (e.id && i.push(`https://${v}/api/v1/tree/main-nav?channelId=${e.id}`), e.key && i.push(`https://${v}/api/v1/tree/main-nav?channelId=${e.key}`));
6098
+ else i.push(`https://${v}/api/v1/node/${f}${d ? `?lang=${d}` : ""}`), r && i.push(`https://${v}/api/v1/tree/main-nav`);
6099
+ for (let e of _) e && i.push(`${t}${e.startsWith("/") ? e : `/${e}`}`);
6100
+ if (await this.callAstroPurge(e, { urls: i }, t), y && b && !this.isLocalEnv(e, t) && fetch(`https://api.cloudflare.com/client/v4/zones/${y}/purge_cache`, {
6082
6101
  method: "POST",
6083
6102
  headers: {
6084
- Authorization: `Bearer ${y}`,
6103
+ Authorization: `Bearer ${b}`,
6085
6104
  "Content-Type": "application/json"
6086
6105
  },
6087
6106
  body: JSON.stringify({ files: i })
6088
- }).catch(() => {}), !r) {
6107
+ }).catch(() => {}), r) {
6108
+ let e = `${t}/partials/nav?_warmer=${Date.now()}`;
6109
+ console.log(`[CacheManager] Prewarming cache at: ${e}`), fetch(e, { headers: {
6110
+ "X-Cache-Warmer": "true",
6111
+ "User-Agent": "Kama-Cache-Warmer/1.0"
6112
+ } }).catch((e) => console.warn("[CacheManager] Prewarm fetch error:", e));
6113
+ } else {
6089
6114
  let e = `${n}${n.includes("?") ? "&" : "?"} _warmer=${Date.now()}`;
6090
6115
  console.log(`[CacheManager] Prewarming cache at: ${e}`), fetch(e, { headers: {
6091
6116
  "X-Cache-Warmer": "true",
6092
6117
  "User-Agent": "Kama-Cache-Warmer/1.0"
6093
6118
  } }).catch((e) => console.warn("[CacheManager] Prewarm fetch error:", e));
6094
6119
  }
6120
+ for (let e of S) {
6121
+ let n = `${t}${e.startsWith("/") ? e : `/${e}`}?_warmer=${Date.now()}`;
6122
+ console.log(`[CacheManager] Prewarming custom target: ${n}`), fetch(n, { headers: {
6123
+ "X-Cache-Warmer": "true",
6124
+ "User-Agent": "Kama-Cache-Warmer/1.0"
6125
+ } }).catch(() => {});
6126
+ }
6095
6127
  }
6096
- r && await this.purgeGlobalCacheDirect(e, t);
6097
6128
  } catch (e) {
6098
6129
  console.warn("[CacheManager] Dynamic purgeNode failed:", e);
6099
6130
  }
6100
6131
  })();
6101
- this.dispatch(e, i);
6132
+ this.dispatch(e, a);
6102
6133
  }
6103
6134
  purgeAsset(e, t, n) {
6104
6135
  let r = (async () => {
6105
6136
  try {
6106
6137
  let r = t.prepare("\n SELECT DISTINCT cn.id\n FROM node_assets na\n INNER JOIN content_nodes cn ON na.nodeId = cn.id\n LEFT JOIN assets crop ON na.assetId = crop.id\n WHERE cn.status = 'published' AND (\n na.assetId = ? OR crop.parentAssetId = ? OR crop.id = (SELECT parentAssetId FROM assets WHERE id = ?)\n )\n "), i = typeof r.all == "function" ? await r.all(n, n, n) : await r.bind(n, n, n).all(), a = Array.isArray(i) ? i : i?.results || [];
6107
- for (let n of a) n.id && this.purgeNode(e, t, n.id, !1);
6138
+ for (let n of a) n.id && this.purgeNode(e, t, n.id, !1, []);
6108
6139
  } catch (e) {
6109
6140
  console.warn("[CacheManager] purgeAsset failed:", e);
6110
6141
  }
@@ -6547,7 +6578,7 @@ H.get("/", async (e) => {
6547
6578
  await he(t, n, i);
6548
6579
  }
6549
6580
  }
6550
- return await I(e).clear(), D && new V(e.env).purgeNode(e, t, D, k), e.json({
6581
+ return await I(e).clear(), e.json({
6551
6582
  success: !0,
6552
6583
  id: D,
6553
6584
  status: "draft",
@@ -6766,7 +6797,7 @@ W.get("/tree.json", async (e) => {
6766
6797
  c.push(e.id, e.sortOrder);
6767
6798
  }), c.push(...i), c.push(o), await t.prepare(s).run(...c);
6768
6799
  }
6769
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6800
+ return await i.clear(), r.length > 0 && a.purgeNode(e, t, r[0].id, !0), e.json({
6770
6801
  success: !0,
6771
6802
  reorderedCount: r.length
6772
6803
  });
@@ -6775,9 +6806,9 @@ W.get("/tree.json", async (e) => {
6775
6806
  delete m._isTreeDraft, delete m._draftParentId, delete m.isNew, delete m.nodeType, delete m.contentType, delete m.title, delete m.slug, delete m.hidden, delete m.is_hidden, delete m.isHidden, delete m.status;
6776
6807
  let g = G(u), _ = e.get("user")?.id || null;
6777
6808
  if (s === "create") {
6778
- let r = m.slug || n.slug || c, s = r ? String(r).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : null;
6779
- if (s) {
6780
- let r = g === null ? "tn.parentId IS NULL" : "tn.parentId = ?", i = n.contentId || n.id ? Number(n.contentId || n.id) : null, a = `
6809
+ let r = m.slug || n.slug || c, a = r ? String(r).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : null;
6810
+ if (a) {
6811
+ let r = g === null ? "tn.parentId IS NULL" : "tn.parentId = ?", i = n.contentId || n.id ? Number(n.contentId || n.id) : null, s = `
6781
6812
  SELECT 1
6782
6813
  FROM tree_nodes tn
6783
6814
  INNER JOIN content_nodes cn ON tn.contentId = cn.id
@@ -6785,51 +6816,51 @@ W.get("/tree.json", async (e) => {
6785
6816
  AND ${r}
6786
6817
  AND cn.slug = ?
6787
6818
  AND (cn.deletedAt IS NULL OR cn.deletedAt = '')
6788
- `, c = g === null ? [o, s] : [
6819
+ `, c = g === null ? [o, a] : [
6789
6820
  o,
6790
6821
  g,
6791
- s
6822
+ a
6792
6823
  ];
6793
- if (i && !isNaN(i) && (a += " AND cn.id != ?", c.push(i)), a += " LIMIT 1", await t.prepare(a).get(...c)) return e.json({
6794
- error: `The slug '${s}' is already in use by another page in this section.`,
6824
+ if (i && !isNaN(i) && (s += " AND cn.id != ?", c.push(i)), s += " LIMIT 1", await t.prepare(s).get(...c)) return e.json({
6825
+ error: `The slug '${a}' is already in use by another page in this section.`,
6795
6826
  field: "slug"
6796
6827
  }, 409);
6797
6828
  }
6798
- let u = m.language?.trim() || n.language?.trim() || null;
6799
- if (!u) {
6829
+ let s = m.language?.trim() || n.language?.trim() || null;
6830
+ if (!s) {
6800
6831
  let e = await t.prepare("SELECT dataJson FROM trees WHERE id = ?").get(o);
6801
6832
  if (e?.dataJson) try {
6802
6833
  let t = typeof e.dataJson == "string" ? JSON.parse(e.dataJson) : e.dataJson;
6803
- t.defaultLanguage && (u = t.defaultLanguage.trim());
6834
+ t.defaultLanguage && (s = t.defaultLanguage.trim());
6804
6835
  } catch {}
6805
6836
  }
6806
- if (!u) {
6837
+ if (!s) {
6807
6838
  let e = [];
6808
6839
  if (n.channels && Array.isArray(n.channels) ? e = n.channels : n.channelId && n.channelId !== "all" && (e = [n.channelId]), e.length > 0) {
6809
6840
  let n = Number(e[0]);
6810
6841
  if (!isNaN(n)) {
6811
6842
  let e = await t.prepare("SELECT defaultLanguage FROM channels WHERE id = ?").get(n);
6812
- e?.defaultLanguage && (u = e.defaultLanguage.trim());
6843
+ e?.defaultLanguage && (s = e.defaultLanguage.trim());
6813
6844
  }
6814
6845
  }
6815
6846
  }
6816
- let d = JSON.stringify(m), f = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, ?, ?)\n ").run(l || "page", c || "Untitled", s, u, h, d, _, _), p = Number(f.lastInsertRowid);
6817
- await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, p);
6818
- let v = g === null ? "parentId IS NULL" : "parentId = ?", y = g === null ? [o] : [o, g], b = (await t.prepare(`
6847
+ let u = JSON.stringify(m), d = await t.prepare("\n INSERT INTO content_nodes (contentType, title, slug, language, isHidden, status, draftDataJson, dataJson, createdById, updatedById)\n VALUES (?, ?, ?, ?, ?, 'draft', ?, NULL, ?, ?)\n ").run(l || "page", c || "Untitled", a, s, h, u, _, _), f = Number(d.lastInsertRowid);
6848
+ await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, f);
6849
+ let p = g === null ? "parentId IS NULL" : "parentId = ?", v = g === null ? [o] : [o, g], y = (await t.prepare(`
6819
6850
  SELECT COALESCE(MAX(sortOrder), -1) + 1 as nextSort
6820
6851
  FROM tree_nodes
6821
- WHERE treeId = ? AND ${v}
6822
- `).get(...y))?.nextSort ?? 0, x = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, ?)\n ").run(o, p, g, b), S = Number(x.lastInsertRowid), C = [];
6823
- n.channels && Array.isArray(n.channels) ? C = n.channels : n.channelId && n.channelId !== "all" && (C = [n.channelId]);
6824
- let w = [...new Set(C)].filter((e) => e && e !== "all").map((e) => Number(e));
6825
- if (w.length > 0) {
6826
- let e = w.map(() => "(?, ?)").join(", "), n = w.flatMap((e) => [p, e]);
6852
+ WHERE treeId = ? AND ${p}
6853
+ `).get(...v))?.nextSort ?? 0, b = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, ?)\n ").run(o, f, g, y), x = Number(b.lastInsertRowid), S = [];
6854
+ n.channels && Array.isArray(n.channels) ? S = n.channels : n.channelId && n.channelId !== "all" && (S = [n.channelId]);
6855
+ let C = [...new Set(S)].filter((e) => e && e !== "all").map((e) => Number(e));
6856
+ if (C.length > 0) {
6857
+ let e = C.map(() => "(?, ?)").join(", "), n = C.flatMap((e) => [f, e]);
6827
6858
  await t.prepare(`INSERT OR IGNORE INTO node_channels (nodeId, channelId) VALUES ${e}`).run(...n);
6828
6859
  }
6829
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6860
+ return await i.clear(), e.json({
6830
6861
  success: !0,
6831
- id: p,
6832
- treeNodeId: S,
6862
+ id: f,
6863
+ treeNodeId: x,
6833
6864
  status: "draft"
6834
6865
  }, 201);
6835
6866
  }
@@ -6864,14 +6895,14 @@ W.get("/tree.json", async (e) => {
6864
6895
  }
6865
6896
  await t.prepare("DELETE FROM tree_nodes WHERE treeId = ? AND contentId = ?").run(o, d);
6866
6897
  let r = await t.prepare("\n INSERT INTO tree_nodes (treeId, contentId, parentId, sortOrder)\n VALUES (?, ?, ?, 0)\n ").run(o, d, g), s = Number(r.lastInsertRowid);
6867
- return await i.clear(), a.purgeGlobalCache(e, t), e.json({
6898
+ return await i.clear(), a.purgeNode(e, t, d, !0), e.json({
6868
6899
  success: !0,
6869
6900
  treeNodeId: s
6870
6901
  });
6871
6902
  }
6872
6903
  if (s === "detach") {
6873
6904
  let r = n.treeNodeId || n.contentId;
6874
- return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?").run(r, r, o), await i.clear(), a.purgeGlobalCache(e, t), e.json({ success: !0 })) : e.json({ error: "treeNodeId or contentId required for detach" }, 400);
6905
+ return r ? (await t.prepare("\n UPDATE tree_nodes \n SET parentId = (SELECT parentId FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?) \n WHERE parentId = ? AND treeId = ?\n ").run(r, r, o, r, o), await t.prepare("DELETE FROM tree_nodes WHERE (id = ? OR contentId = ?) AND treeId = ?").run(r, r, o), await i.clear(), a.purgeNode(e, t, r, !0), e.json({ success: !0 })) : e.json({ error: "treeNodeId or contentId required for detach" }, 400);
6875
6906
  }
6876
6907
  return e.json({ error: "Invalid payload or action" }, 400);
6877
6908
  } catch (t) {
@@ -6935,7 +6966,7 @@ W.get("/tree.json", async (e) => {
6935
6966
  await t.prepare(`INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES ${n}`).run(...r);
6936
6967
  }
6937
6968
  }
6938
- return await I(e).clear(), new V(e.env).purgeGlobalCache(e, t), e.json({
6969
+ return await cache.clear(), new V(e.env).purgeGlobalCache(e, t), e.json({
6939
6970
  success: !0,
6940
6971
  id: p,
6941
6972
  key: o,
@@ -1,10 +1,20 @@
1
1
  import { fileURLToPath as e, pathToFileURL as t } from "node:url";
2
2
  import { existsSync as n, mkdirSync as r, readFileSync as i, writeFileSync as a } from "node:fs";
3
- import { dirname as o, join as s } from "node:path";
3
+ import { dirname as o, join as s, resolve as c } from "node:path";
4
4
  //#region scripts/schema-compiler.ts
5
- var c = e(import.meta.url), l = o(c);
6
- async function u() {
7
- let e = process.cwd(), o = !1;
5
+ var l = e(import.meta.url), u = o(l);
6
+ function d(e = process.cwd()) {
7
+ let t = e;
8
+ for (; t !== c(t, "..");) {
9
+ if (n(s(t, "content", "definitions"))) return t;
10
+ t = c(t, "..");
11
+ }
12
+ return process.cwd();
13
+ }
14
+ async function f() {
15
+ let e = d();
16
+ process.chdir(e);
17
+ let o = !1;
8
18
  try {
9
19
  JSON.parse(i(s(e, "package.json"), "utf-8")).name === "@brightsideoy/kama" && (o = !0);
10
20
  } catch {}
@@ -16,23 +26,23 @@ async function u() {
16
26
  let t = s(e, "kama-worker");
17
27
  n(t) || r(t, { recursive: !0 }), c = s(t, "bundled-definitions.js");
18
28
  }
19
- let u, d = s(l, "../server/schema/engine.ts"), f = s(l, "../dist/index.js");
20
- if (n(d)) u = (await import(t(d).href)).compileDefinitions;
21
- else if (n(f)) u = (await import(t(f).href)).compileDefinitions;
29
+ let l, f = s(u, "../server/schema/engine.ts"), p = s(u, "../dist/index.js");
30
+ if (n(f)) l = (await import(t(f).href)).compileDefinitions;
31
+ else if (n(p)) l = (await import(t(p).href)).compileDefinitions;
22
32
  else try {
23
- u = (await import("./dist-CBGESg3a.js")).compileDefinitions;
33
+ l = (await import("./dist-BNkCx-o-.js")).compileDefinitions;
24
34
  } catch (e) {
25
35
  console.error("❌ Could not resolve Kama Schema Engine:", e), process.exit(1);
26
36
  }
27
- u || (console.error("❌ compileDefinitions function missing from resolved module"), process.exit(1));
28
- let p = u(), m = Object.keys(p?.nodes || {}).length, h = Object.keys(p?.blocks || {}).length, g = `// AUTO-GENERATED BUNDLE - DO NOT EDIT
29
- const defs = ${JSON.stringify(p, null, 2)};
37
+ l || (console.error("❌ compileDefinitions function missing from resolved module"), process.exit(1));
38
+ let m = l(), h = Object.keys(m?.nodes || {}).length, g = Object.keys(m?.blocks || {}).length, _ = `// AUTO-GENERATED BUNDLE - DO NOT EDIT
39
+ const defs = ${JSON.stringify(m, null, 2)};
30
40
  export const BUNDLED_DEFINITIONS = defs;
31
41
  export default defs;
32
42
  `;
33
- a(c, g, "utf-8"), console.log(`✅ Schema securely compiled using engine.ts to ${c} (${m} nodes, ${h} blocks)`);
43
+ a(c, _, "utf-8"), console.log(`✅ Schema securely compiled using engine.ts to ${c} (${h} nodes, ${g} blocks)`);
34
44
  }
35
- u().catch((e) => {
45
+ f().catch((e) => {
36
46
  console.error("❌ Schema Compiler Runtime Error:", e), process.exit(1);
37
47
  });
38
48
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "0.1.0-alpha.158",
3
+ "version": "0.1.0-alpha.159",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",