@brightsideoy/kama 1.0.0-alpha.16 → 1.0.0-alpha.18

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 (105) hide show
  1. package/bin/cli.js +70 -44
  2. package/content/definitions/block-kama-forms.yaml +5 -0
  3. package/content/definitions/blocks/node-list.yaml +8 -0
  4. package/content/definitions/taxonomies/categories.yaml +5 -2
  5. package/content/definitions/taxonomies/tags.yaml +5 -2
  6. package/content/forms/contact.yaml +44 -0
  7. package/content/forms/subscription.yaml +32 -0
  8. package/dist/assets/AssetEditor-CY9SjGaJ.js +1 -0
  9. package/dist/assets/CmsBadge-BNsgxyyB.css +1 -0
  10. package/dist/assets/CmsBadge-D0Ob9qPU.js +1 -0
  11. package/dist/assets/CmsButton-BQ85-YXn.css +1 -0
  12. package/dist/assets/CmsButton-CbOfxvjr.js +1 -0
  13. package/dist/assets/CmsDataRow--H4iI_u_.js +1 -0
  14. package/dist/assets/CmsDataRow-CYNxfVaZ.css +1 -0
  15. package/dist/assets/CmsHelp-DJSdFYVH.js +1 -0
  16. package/dist/assets/CmsImagePreview-CujwG8r_.css +1 -0
  17. package/dist/assets/CmsImagePreview-DuHPBzml.js +1 -0
  18. package/dist/assets/CmsPaneResizer-B62ZqsnE.js +1 -0
  19. package/dist/assets/CmsPaneResizer-Dkidd4pI.css +1 -0
  20. package/dist/assets/CmsPublisher-DBcCz-AD.js +3 -0
  21. package/dist/assets/CmsUserCenter-D3XDzKDC.js +1 -0
  22. package/dist/assets/EditorSplitPreview-wCnwVnnb.js +1 -0
  23. package/dist/assets/NodeContextMenu-BYeWzmQ5.css +1 -0
  24. package/dist/assets/NodeContextMenu-C_HWBGeb.js +1 -0
  25. package/dist/assets/NodeEditor-D3tAGViP.js +147 -0
  26. package/dist/assets/NodeEditor-DlfAHtiG.css +1 -0
  27. package/dist/assets/StatusPill-CbeW5SMT.js +1 -0
  28. package/dist/assets/StatusPill-Dlm6uvGe.css +1 -0
  29. package/dist/assets/TrashWorkspace--uFsWt53.js +1 -0
  30. package/dist/assets/{_plugin-vue_export-helper-CTn6xl4w.js → _plugin-vue_export-helper-CQxWhVtr.js} +1 -1
  31. package/dist/assets/circle-check-A-QeFqt3.js +1 -0
  32. package/dist/assets/database-hg5SqKLG.js +1 -0
  33. package/dist/assets/folder-tree-BWYot72H.js +1 -0
  34. package/dist/assets/globe-BraoljaN.js +1 -0
  35. package/dist/assets/{history-XvWNE5zO.js → history-pqIbKK9u.js} +1 -1
  36. package/dist/assets/index-AQYAbg3R.js +14 -0
  37. package/dist/assets/index-DcKiaRjI.css +1 -0
  38. package/dist/assets/{layers-D0DLWjff.js → layers-B6RgfpPR.js} +1 -1
  39. package/dist/assets/loader-circle-BoUkhDjq.js +1 -0
  40. package/dist/assets/monitor-CrpvInqG.js +1 -0
  41. package/dist/assets/panel-left-close-CVFakVaA.js +1 -0
  42. package/dist/assets/plus-mqZr4I1D.js +1 -0
  43. package/dist/assets/rotate-ccw-CWE4pOjs.js +1 -0
  44. package/dist/assets/{save-C8e-QRkP.js → save-Ctv8vW9H.js} +1 -1
  45. package/dist/assets/schema-helpers-1UXlO0Ye.js +1 -0
  46. package/dist/assets/send-F-mJeL54.js +1 -0
  47. package/dist/assets/{smartphone-Wy6xQi_i.js → smartphone-EPce6VdP.js} +1 -1
  48. package/dist/assets/trash-2-CAUF1KtI.js +1 -0
  49. package/dist/assets/useLocalStorage-C805uplI.js +1 -0
  50. package/dist/assets/useNodes-W5j_uxdQ.js +1 -0
  51. package/dist/assets/user-IFlT6mYq.js +1 -0
  52. package/dist/{dist-Ca4GRlV3.js → dist-Bta0OpXl.js} +159 -34
  53. package/dist/index.html +34 -3
  54. package/dist/index.js +159 -34
  55. package/dist/schema-compiler.js +1 -1
  56. package/documentation/EXTENSIONS.md +204 -0
  57. package/extension-registry.js +5 -0
  58. package/package.json +3 -1
  59. package/templates/astro-starter/old_src/components/blocks/BlockRenderer.astro +143 -0
  60. package/templates/astro-starter/old_src/components/blocks/ColumnLayout.astro +67 -0
  61. package/templates/astro-starter/old_src/components/blocks/HeaderImage.astro +126 -0
  62. package/templates/astro-starter/old_src/components/blocks/Hero.astro +218 -0
  63. package/templates/astro-starter/old_src/components/blocks/HtmlBlock.astro +25 -0
  64. package/templates/astro-starter/old_src/components/blocks/ImageBlock.astro +91 -0
  65. package/templates/astro-starter/old_src/components/blocks/NodeList.astro +274 -0
  66. package/templates/astro-starter/old_src/components/blocks/TextBlock.astro +15 -0
  67. package/templates/astro-starter/old_src/components/global/Breadcrumb.astro +108 -0
  68. package/templates/astro-starter/old_src/components/global/Footer.astro +50 -0
  69. package/templates/astro-starter/old_src/components/global/Header.astro +171 -0
  70. package/templates/astro-starter/old_src/components/global/MetaHead.astro +266 -0
  71. package/templates/astro-starter/old_src/components/global/NavigationMenu.astro +413 -0
  72. package/templates/astro-starter/old_src/components/ui/KamaImage.astro +180 -0
  73. package/templates/astro-starter/old_src/layouts/SkeletonLayout.astro +56 -0
  74. package/templates/astro-starter/old_src/layouts/StandardLayout.astro +63 -0
  75. package/templates/astro-starter/old_src/lib/cms.ts +219 -0
  76. package/templates/astro-starter/old_src/lib/media/kama-image-service.ts +67 -0
  77. package/templates/astro-starter/old_src/lib/media/media-proxy.ts +148 -0
  78. package/templates/astro-starter/old_src/lib/media/media-url.ts +112 -0
  79. package/templates/astro-starter/old_src/middleware.ts +536 -0
  80. package/templates/astro-starter/old_src/pages/404.astro +46 -0
  81. package/templates/astro-starter/old_src/pages/[...slug].astro +311 -0
  82. package/templates/astro-starter/old_src/pages/api/load-nodes.ts +68 -0
  83. package/templates/astro-starter/old_src/pages/api/partials/node-cards.astro +101 -0
  84. package/templates/astro-starter/old_src/pages/llms-full.txt.ts +171 -0
  85. package/templates/astro-starter/old_src/pages/llms.txt.ts +113 -0
  86. package/templates/astro-starter/old_src/pages/media/[...path].ts +14 -0
  87. package/templates/astro-starter/old_src/pages/partials/footer.astro +69 -0
  88. package/templates/astro-starter/old_src/pages/partials/nav.astro +141 -0
  89. package/templates/astro-starter/old_src/styles/standard-theme.css +157 -0
  90. package/templates/astro-starter/old_src/utils/tiptap.ts +113 -0
  91. package/templates/astro-starter/src/components/blocks/Form.astro +143 -0
  92. package/templates/kama-worker/index.js +35 -12
  93. package/templates/kama-worker/server.js +2 -0
  94. package/dist/assets/AssetEditor-CEHxvQj0.js +0 -1
  95. package/dist/assets/CmsHelp-Cnr801lx.js +0 -1
  96. package/dist/assets/CmsPublisher-C3fRoBte.js +0 -3
  97. package/dist/assets/CmsUserCenter-DiuXfjMc.js +0 -1
  98. package/dist/assets/EditorSplitPreview-tpZZF1o5.js +0 -1
  99. package/dist/assets/NodeEditor-Bd89OptF.css +0 -1
  100. package/dist/assets/NodeEditor-DaCIVPGT.js +0 -147
  101. package/dist/assets/TrashWorkspace-CTq0OFA-.js +0 -1
  102. package/dist/assets/index-BbV9FaG-.css +0 -1
  103. package/dist/assets/index-CHczMewv.js +0 -14
  104. package/dist/assets/link-Bs_SUS0O.js +0 -1
  105. package/dist/assets/rotate-ccw-CvD7yNQj.js +0 -1
package/dist/index.html CHANGED
@@ -6,9 +6,40 @@
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-CHczMewv.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CTn6xl4w.js">
11
- <link rel="stylesheet" crossorigin href="/assets/index-BbV9FaG-.css">
9
+ <script type="module" crossorigin src="/assets/index-AQYAbg3R.js"></script>
10
+ <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CQxWhVtr.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/schema-helpers-1UXlO0Ye.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/useNodes-W5j_uxdQ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/plus-mqZr4I1D.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/send-F-mJeL54.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/CmsPaneResizer-B62ZqsnE.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/user-IFlT6mYq.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/useLocalStorage-C805uplI.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/circle-check-A-QeFqt3.js">
19
+ <link rel="modulepreload" crossorigin href="/assets/loader-circle-BoUkhDjq.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/CmsImagePreview-DuHPBzml.js">
21
+ <link rel="modulepreload" crossorigin href="/assets/database-hg5SqKLG.js">
22
+ <link rel="modulepreload" crossorigin href="/assets/NodeContextMenu-C_HWBGeb.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/monitor-CrpvInqG.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/rotate-ccw-CWE4pOjs.js">
25
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-CAUF1KtI.js">
26
+ <link rel="modulepreload" crossorigin href="/assets/folder-tree-BWYot72H.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/globe-BraoljaN.js">
28
+ <link rel="modulepreload" crossorigin href="/assets/history-pqIbKK9u.js">
29
+ <link rel="modulepreload" crossorigin href="/assets/layers-B6RgfpPR.js">
30
+ <link rel="modulepreload" crossorigin href="/assets/panel-left-close-CVFakVaA.js">
31
+ <link rel="modulepreload" crossorigin href="/assets/save-Ctv8vW9H.js">
32
+ <link rel="modulepreload" crossorigin href="/assets/smartphone-EPce6VdP.js">
33
+ <link rel="modulepreload" crossorigin href="/assets/CmsButton-CbOfxvjr.js">
34
+ <link rel="modulepreload" crossorigin href="/assets/CmsDataRow--H4iI_u_.js">
35
+ <link rel="modulepreload" crossorigin href="/assets/CmsBadge-D0Ob9qPU.js">
36
+ <link rel="stylesheet" crossorigin href="/assets/CmsPaneResizer-Dkidd4pI.css">
37
+ <link rel="stylesheet" crossorigin href="/assets/CmsImagePreview-CujwG8r_.css">
38
+ <link rel="stylesheet" crossorigin href="/assets/NodeContextMenu-BYeWzmQ5.css">
39
+ <link rel="stylesheet" crossorigin href="/assets/CmsButton-BQ85-YXn.css">
40
+ <link rel="stylesheet" crossorigin href="/assets/CmsDataRow-CYNxfVaZ.css">
41
+ <link rel="stylesheet" crossorigin href="/assets/CmsBadge-BNsgxyyB.css">
42
+ <link rel="stylesheet" crossorigin href="/assets/index-DcKiaRjI.css">
12
43
  </head>
13
44
  <body>
14
45
  <div id="app"></div>
package/dist/index.js CHANGED
@@ -20777,9 +20777,14 @@ var Tb = /* @__PURE__ */ o({
20777
20777
  label: "Category",
20778
20778
  hierarchical: !0,
20779
20779
  fields: {
20780
+ label: {
20781
+ type: "text",
20782
+ label: "Label",
20783
+ required: !0,
20784
+ rules: { required: !0 }
20785
+ },
20780
20786
  slug: {
20781
- label: "Slug",
20782
- linkedTo: "key",
20787
+ linkedTo: "label",
20783
20788
  rules: {}
20784
20789
  },
20785
20790
  description: {
@@ -20807,9 +20812,14 @@ var Tb = /* @__PURE__ */ o({
20807
20812
  label: "Tag",
20808
20813
  hierarchical: !1,
20809
20814
  fields: {
20815
+ label: {
20816
+ type: "text",
20817
+ label: "Label",
20818
+ required: !0,
20819
+ rules: { required: !0 }
20820
+ },
20810
20821
  slug: {
20811
- label: "Slug",
20812
- linkedTo: "key",
20822
+ linkedTo: "label",
20813
20823
  rules: {}
20814
20824
  },
20815
20825
  iconName: {
@@ -21268,6 +21278,13 @@ var Tb = /* @__PURE__ */ o({
21268
21278
  default: 0,
21269
21279
  public: !0,
21270
21280
  rules: {}
21281
+ },
21282
+ targetPage: {
21283
+ label: "Target Page for \"Read More\" Links",
21284
+ type: "link",
21285
+ public: !0,
21286
+ allowedLinkTypes: ["tree", "node"],
21287
+ rules: {}
21271
21288
  }
21272
21289
  },
21273
21290
  tabs: { access: {
@@ -21407,6 +21424,54 @@ var Tb = /* @__PURE__ */ o({
21407
21424
  }
21408
21425
  },
21409
21426
  category: "Content"
21427
+ },
21428
+ form: {
21429
+ id: "form",
21430
+ label: "Dynamic Form",
21431
+ description: "Embeds a form definition from content/forms/",
21432
+ category: "Interactive",
21433
+ icon: "ClipboardList",
21434
+ fieldSets: ["access"],
21435
+ fields: {
21436
+ formId: {
21437
+ label: "Select Form",
21438
+ type: "select",
21439
+ required: !0,
21440
+ description: "Choose a YAML form definition",
21441
+ optionsEndpoint: "/api/admin/ext/kama-forms/forms",
21442
+ optionsValueKey: "id",
21443
+ optionsLabelKey: "title",
21444
+ rules: { required: !0 }
21445
+ },
21446
+ isHidden: {
21447
+ type: "boolean",
21448
+ label: "Hidden",
21449
+ default: !1,
21450
+ public: !1,
21451
+ variant: "danger",
21452
+ dbType: "integer",
21453
+ index: !0,
21454
+ position: "sidebar",
21455
+ liveSync: !0,
21456
+ rules: {}
21457
+ },
21458
+ startShowingAt: {
21459
+ type: "datetime",
21460
+ label: "Start Showing At",
21461
+ position: "sidebar",
21462
+ public: !1,
21463
+ index: !0,
21464
+ rules: {}
21465
+ },
21466
+ stopShowingAt: {
21467
+ type: "datetime",
21468
+ label: "Stop Showing At",
21469
+ position: "sidebar",
21470
+ public: !1,
21471
+ index: !0,
21472
+ rules: {}
21473
+ }
21474
+ }
21410
21475
  }
21411
21476
  },
21412
21477
  nodeLayouts: {
@@ -22225,11 +22290,14 @@ async function jb(e, t) {
22225
22290
  return /* @__PURE__ */ new Set();
22226
22291
  }
22227
22292
  }
22228
- async function Mb(e, t) {
22293
+ async function Mb(e, t, n = []) {
22229
22294
  try {
22230
22295
  await e.prepare("PRAGMA foreign_keys = ON;").run();
22231
22296
  } catch {}
22232
22297
  for (let [t, n] of Object.entries(Ob)) await e.prepare(`CREATE TABLE IF NOT EXISTS "${t}" (${n});`).run();
22298
+ if (n && n.length > 0) {
22299
+ for (let t of n) if (t.db && t.db.tables) for (let [n, r] of Object.entries(t.db.tables)) (await jb(e, n)).size === 0 && (await e.prepare(`CREATE TABLE IF NOT EXISTS "${n}" (${r});`).run(), console.log(`[Extension: ${t.id}] Created database table: ${n}`));
22300
+ }
22233
22301
  for (let [t] of Object.entries(Ob)) {
22234
22302
  let n = await jb(e, t);
22235
22303
  if (n.size > 0) {
@@ -22274,11 +22342,11 @@ async function Mb(e, t) {
22274
22342
  for (let t of kb) try {
22275
22343
  await e.prepare(t).run();
22276
22344
  } catch {}
22277
- let n = t || Db;
22278
- for (let t of Ab(n?.rawNodes || {})) try {
22345
+ let r = t || Db;
22346
+ for (let t of Ab(r?.rawNodes || {})) try {
22279
22347
  await e.prepare(t).run();
22280
22348
  } catch {}
22281
- await Nb(e, n);
22349
+ await Nb(e, r);
22282
22350
  }
22283
22351
  async function Nb(e, t) {
22284
22352
  if (t) for (let [n, r] of Object.entries(t.rawNodes || {})) {
@@ -25238,13 +25306,15 @@ function qx() {
25238
25306
  t = e;
25239
25307
  break;
25240
25308
  }
25241
- if (!t) return null;
25242
- let i = (e) => {
25309
+ let i = Rx.resolve(e, "extensions"), a = Lx.existsSync(i);
25310
+ if (!t && !a) return null;
25311
+ let o = (e, t) => {
25312
+ if (!e) return {};
25243
25313
  let n = "";
25244
- for (let r of e) {
25245
- let e = Rx.join(t, r);
25246
- if (Lx.existsSync(e)) {
25247
- n = e;
25314
+ for (let r of t) {
25315
+ let t = Rx.join(e, r);
25316
+ if (Lx.existsSync(t)) {
25317
+ n = t;
25248
25318
  break;
25249
25319
  }
25250
25320
  }
@@ -25260,24 +25330,46 @@ function qx() {
25260
25330
  }
25261
25331
  }
25262
25332
  return r;
25263
- };
25333
+ }, s = o(t, ["nodes", "node"]), c = o(t, ["trees", "tree"]), l = o(t, ["channels", "channel"]), u = o(t, ["taxonomies", "taxonomy"]), d = o(t, [
25334
+ "blocks",
25335
+ "elements",
25336
+ "block"
25337
+ ]), f = o(t, [
25338
+ "node-layouts",
25339
+ "layouts",
25340
+ "layout"
25341
+ ]), p = o(t, ["block-layouts"]), m = o(t, ["field-sets", "fieldsets"]);
25342
+ if (a) {
25343
+ let e = Lx.readdirSync(i);
25344
+ for (let t of e) {
25345
+ let e = Rx.join(i, t);
25346
+ if (Lx.statSync(e).isDirectory()) {
25347
+ let t = Rx.join(e, "content");
25348
+ if (!Lx.existsSync(t)) {
25349
+ let n = Rx.join(e, "content", "definitions");
25350
+ Lx.existsSync(n) && (t = n);
25351
+ }
25352
+ Lx.existsSync(t) && (Object.assign(s, o(t, ["nodes", "node"])), Object.assign(c, o(t, ["trees", "tree"])), Object.assign(l, o(t, ["channels", "channel"])), Object.assign(u, o(t, ["taxonomies", "taxonomy"])), Object.assign(d, o(t, [
25353
+ "blocks",
25354
+ "elements",
25355
+ "block"
25356
+ ])), Object.assign(f, o(t, [
25357
+ "node-layouts",
25358
+ "layouts",
25359
+ "layout"
25360
+ ])), Object.assign(p, o(t, ["block-layouts"])), Object.assign(m, o(t, ["field-sets", "fieldsets"])));
25361
+ }
25362
+ }
25363
+ }
25264
25364
  return {
25265
- rawNodes: i(["nodes", "node"]),
25266
- rawTrees: i(["trees", "tree"]),
25267
- rawChannels: i(["channels", "channel"]),
25268
- rawTaxonomies: i(["taxonomies", "taxonomy"]),
25269
- rawBlocks: i([
25270
- "blocks",
25271
- "elements",
25272
- "block"
25273
- ]),
25274
- rawNodeLayouts: i([
25275
- "node-layouts",
25276
- "layouts",
25277
- "layout"
25278
- ]),
25279
- rawBlockLayouts: i(["block-layouts"]),
25280
- rawFieldSets: i(["field-sets", "fieldsets"])
25365
+ rawNodes: s,
25366
+ rawTrees: c,
25367
+ rawChannels: l,
25368
+ rawTaxonomies: u,
25369
+ rawBlocks: d,
25370
+ rawNodeLayouts: f,
25371
+ rawBlockLayouts: p,
25372
+ rawFieldSets: m
25281
25373
  };
25282
25374
  } catch (e) {
25283
25375
  return console.error("❌ Exception inside readLocalFsBundle:", e), null;
@@ -25349,7 +25441,7 @@ function Zx(e) {
25349
25441
  return Qx(t);
25350
25442
  }
25351
25443
  let t = qx();
25352
- return t && Object.keys(t.rawNodes || {}).length > 0 ? Qx(t) : Qx(null);
25444
+ return t && (Object.keys(t.rawNodes || {}).length > 0 || Object.keys(t.rawBlocks || {}).length > 0) ? Qx(t) : Qx(null);
25353
25445
  }
25354
25446
  function Qx(e) {
25355
25447
  if (!e) return {
@@ -27432,18 +27524,51 @@ $.use("*", t()), $.use("/media/*", t({
27432
27524
  e.set("currentDomain", r);
27433
27525
  let i = wb(e);
27434
27526
  if (e.set("db", i), !vC) try {
27435
- await Mb(i), vC = !0;
27527
+ await Mb(i, e.env?.BUNDLED_DEFINITIONS, e.env?.KAMA_EXTENSIONS), vC = !0;
27436
27528
  } catch (e) {
27437
27529
  console.error("❌ [CMS Server] Failed to initialize system tables on cold start:", e);
27438
27530
  }
27439
27531
  await t();
27440
- }), $.route("/media", tC), $.use("/media/*", Pb()), $.route("/api/cms/nodes", gx), $.route("/api/cms/nodes.json", gx), $.route("/api/cms/formcsv", gx), $.route("/api/cms/formcsv.json", gx), $.route("/api/cms/stats", iS), $.route("/api/cms/stats.json", iS), $.route("/api/cms", Ox), $.route("/api/cms/trees.json", Ox), $.route("/api/cms/channels", Mx), $.route("/api/cms/channels.json", Mx), $.route("/api/cms/taxonomies", Nx), $.route("/api/cms/taxonomies.json", Nx), $.route("/api/cms/schemas", $x), $.route("/api/cms/schemas.json", $x), $.route("/api/cms", tS), $.route("/api/admin/assets", aS), $.use("/api/v1/*", async (e, t) => {
27532
+ }), $.route("/media", tC), $.use("/media/*", Pb()), $.get("/api/cms/ui-extensions", (e) => {
27533
+ let t = (e.env?.KAMA_EXTENSIONS || []).filter((e) => e.ui).map((e) => ({
27534
+ id: e.id,
27535
+ name: e.name || e.id,
27536
+ ui: e.ui
27537
+ }));
27538
+ return e.json(t);
27539
+ }), $.route("/api/cms/nodes", gx), $.route("/api/cms/nodes.json", gx), $.route("/api/cms/formcsv", gx), $.route("/api/cms/formcsv.json", gx), $.route("/api/cms/stats", iS), $.route("/api/cms/stats.json", iS), $.route("/api/cms", Ox), $.route("/api/cms/trees.json", Ox), $.route("/api/cms/channels", Mx), $.route("/api/cms/channels.json", Mx), $.route("/api/cms/taxonomies", Nx), $.route("/api/cms/taxonomies.json", Nx), $.route("/api/cms/schemas", $x), $.route("/api/cms/schemas.json", $x), $.route("/api/cms", tS), $.route("/api/admin/assets", aS), $.use("/api/v1/*", async (e, t) => {
27441
27540
  let n = e.env?.FRONTEND_API_SECRET;
27442
27541
  if (n) {
27443
27542
  let t = e.req.header("Authorization"), r = e.req.header("x-api-key"), i = t?.startsWith("Bearer ") ? t.substring(7).trim() : r?.trim();
27444
27543
  if (!i || i !== n) return e.json({ error: "Unauthorized: Invalid or missing API secret." }, 401);
27445
27544
  } else console.warn("⚠️ [CMS Security] FRONTEND_API_SECRET is not set in .env. The API is completely exposed to the public.");
27446
27545
  return t();
27546
+ }), $.use("/api/v1/ext/*", async (e, t) => {
27547
+ let n = e.env?.KAMA_EXTENSIONS || [], r = e.req.path.match(/^\/api\/v1\/ext\/([^/]+)/);
27548
+ if (r) {
27549
+ let t = r[1], i = n.find((e) => e.id === t);
27550
+ if (i && i.routes?.public) {
27551
+ let t = {
27552
+ ...e.env,
27553
+ KAMA_DB: e.get("db")
27554
+ };
27555
+ return i.routes.public.fetch(e.req.raw, t, e.executionCtx);
27556
+ }
27557
+ }
27558
+ return t();
27559
+ }), $.use("/api/admin/ext/*", async (e, t) => {
27560
+ let n = e.env?.KAMA_EXTENSIONS || [], r = e.req.path.match(/^\/api\/admin\/ext\/([^/]+)/);
27561
+ if (r) {
27562
+ let t = r[1], i = n.find((e) => e.id === t);
27563
+ if (i && i.routes?.admin) {
27564
+ let t = {
27565
+ ...e.env,
27566
+ KAMA_DB: e.get("db")
27567
+ };
27568
+ return i.routes.admin.fetch(e.req.raw, t, e.executionCtx);
27569
+ }
27570
+ }
27571
+ return t();
27447
27572
  }), $.route("/api/v1/node", lS), $.route("/api/v1/nodes", kS), $.route("/api/v1/tree", NS), $.route("/api/v1/channel", HS), $.route("/api/v1/all-paths", oC), $.route("/api/v1/taxonomies", fC), $.doc("/api/openapi.json", {
27448
27573
  openapi: "3.0.0",
27449
27574
  info: {
@@ -30,7 +30,7 @@ async function f() {
30
30
  if (n(f)) l = (await import(t(f).href)).compileDefinitions;
31
31
  else if (n(p)) l = (await import(t(p).href)).compileDefinitions;
32
32
  else try {
33
- l = (await import("./dist-Ca4GRlV3.js")).compileDefinitions;
33
+ l = (await import("./dist-Bta0OpXl.js")).compileDefinitions;
34
34
  } catch (e) {
35
35
  console.error("\x1B[31m ✖\x1B[0m \x1B[31mCould not resolve Kama Schema Engine:\x1B[0m", e), process.exit(1);
36
36
  }
@@ -0,0 +1,204 @@
1
+ # KAMA CMS — Extension Development Guide
2
+
3
+ KAMA features a **Drop-In Extension Architecture**. Extensions are built using native TypeScript, Vue, and YAML, and are automatically discovered, registered, and compiled into the Cloudflare Worker and Vite SPA at build time.
4
+
5
+ This guarantees zero core bloat, perfect native performance, and a completely isolated development environment.
6
+
7
+ ## 1. How It Works (The Auto-Discovery Pipeline)
8
+
9
+ Extensions live in the `extensions/` directory at the root of your workspace.
10
+
11
+ When you run `npm run dev` or `npm run build:schema`, the KAMA CLI automatically:
12
+
13
+ 1. Scans the `extensions/` folder.
14
+ 2. Generates an `extension-registry.js` file.
15
+ 3. Injects your Database tables into the SQLite/D1 initialization sequence.
16
+ 4. Mounts your Hono API routes securely behind KAMA's authentication.
17
+ 5. Injects your Vue `.vue` components into the SPA routing and sidebars.
18
+ 6. Merges your extension's `.yaml` schemas natively into the core bundle.
19
+
20
+ ---
21
+
22
+ ## 2. Directory Structure
23
+
24
+ A standard extension uses the following structure:
25
+
26
+ kama-workspace/
27
+ ├─ extensions/
28
+ │ └─ forms/ <-- Your Extension Folder
29
+ │ ├─ index.ts <-- The Manifest (Exports UI, DB, and Routes)
30
+ │ ├─ api/
31
+ │ │ ├─ admin.ts <-- Hono routes for the Admin UI
32
+ │ │ └─ public.ts <-- Hono routes for the Public API
33
+ │ ├─ ui/
34
+ │ │ ├─ SidebarPane.vue <-- Custom Left Drawer Vue Component
35
+ │ │ └─ Dashboard.vue <-- Custom Main Workspace Vue Component
36
+ │ └─ content/ <-- 🎯 Schema folder matching Core
37
+ │ └─ blocks/
38
+ │ └─ form-block.yaml <-- YAML Schemas (Auto-merged into KAMA)
39
+
40
+ ---
41
+
42
+ ## 3. The Extension Manifest (`index.ts`)
43
+
44
+ Every extension must export a default object serving as the contract between your code and the KAMA core.
45
+
46
+ // extensions/forms/index.ts
47
+ import { adminFormsRouter } from "./api/admin.js";
48
+ import { publicFormsRouter } from "./api/public.js";
49
+
50
+ export default {
51
+ id: "kama-forms",
52
+ name: "Form Builder",
53
+
54
+ // 🎯 1. DATABASE HOOKS
55
+ // KAMA will automatically CREATE TABLE IF NOT EXISTS on boot.
56
+ db: {
57
+ tables: {
58
+ kama_forms_table: ` id INTEGER PRIMARY KEY AUTOINCREMENT,
59
+ form_id TEXT NOT NULL,
60
+ payload_json TEXT NOT NULL,
61
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP
62
+ `,
63
+ },
64
+ },
65
+
66
+ // 🎯 2. API ROUTING HOOKS
67
+ // Handled natively by Hono. KAMA injects c.env.DB automatically.
68
+ routes: {
69
+ public: publicFormsRouter, // Mounted at: /api/ext/kama-forms/_
70
+ admin: adminFormsRouter, // Mounted at: /api/admin/ext/kama-forms/_
71
+ },
72
+
73
+ // 🎯 3. FRONTEND UI HOOKS
74
+ ui: {
75
+ sidebar: [
76
+ {
77
+ id: "forms-nav",
78
+ label: "Forms",
79
+ icon: "Inbox", // Uses KAMA's global icon resolver
80
+ sidebarViewId: "forms-sidebar", // 🎯 Renders in the left drawer panel
81
+ viewId: "forms-dashboard", // 🎯 Renders in the main workspace canvas
82
+ },
83
+ ],
84
+
85
+ // Map your view IDs to your Vue component chunks
86
+ views: [
87
+ { id: "forms-sidebar", url: "/api/ext/kama-forms/ui-pane.js" },
88
+ { id: "forms-dashboard", url: "/api/ext/kama-forms/ui.js" }
89
+ ],
90
+
91
+ },
92
+ };
93
+
94
+ ---
95
+
96
+ ## 4. API Routing (`api/admin.ts`)
97
+
98
+ Extension APIs are built using standard Hono syntax.
99
+
100
+ **Important:** Your extension routes do not need to connect to the database manually. KAMA intercepts the request and injects the active database connection directly into the request context.
101
+
102
+ // extensions/forms/api/admin.ts
103
+ import { Hono } from "hono";
104
+ export const adminFormsRouter = new Hono();
105
+
106
+ adminFormsRouter.get("/submissions", async (c) => {
107
+ // Access the core database connection
108
+ const db = (c as any).env?.DB;
109
+ const formId = c.req.query("formId");
110
+
111
+ const stmt = db.prepare("SELECT \* FROM kama_forms_table WHERE form_id = ? ORDER BY createdAt DESC");
112
+ const results = typeof stmt.all === "function" ? await stmt.bind(formId).all() : [];
113
+
114
+ return c.json({ submissions: results?.results || results || [] });
115
+ });
116
+
117
+ ---
118
+
119
+ ## 5. Frontend UI & Cross-Component Communication
120
+
121
+ KAMA enforces strict decoupling. Extensions shouldn't mutate core routing states directly. Instead, KAMA provides generic Event Buses and State Stores.
122
+
123
+ ### Triggering Workspace Navigation
124
+
125
+ If a user clicks an item in your left `SidebarPane.vue` and you want to open your main `Dashboard.vue` in the primary workspace, dispatch a standard custom event. Core KAMA listens to this universally:
126
+
127
+ // Inside extensions/forms/ui/SidebarPane.vue
128
+ const openWorkspaceView = () => {
129
+ window.dispatchEvent(new CustomEvent('cms-open-extension-view', {
130
+ detail: { viewId: 'forms-dashboard' }
131
+ }));
132
+ };
133
+
134
+ ### Passing State Between Extension Components
135
+
136
+ To pass data (like a selected Form ID) from your Sidebar to your Dashboard, use the global `window.Kama` SDK:
137
+
138
+ **Setting State & Emitting Events (Sidebar):**
139
+
140
+ // Set state synchronously
141
+ (window as any).Kama?.state?.set('kama-forms', 'activeFormId', 'contact-form');
142
+
143
+ // Notify other components live
144
+ (window as any).Kama?.events?.emit('kama-forms:select-form', { formId: 'contact-form' });
145
+
146
+ **Listening for State (Dashboard):**
147
+
148
+ // Read initial state on mount
149
+ const initialId = (window as any).Kama?.state?.get('kama-forms', 'activeFormId');
150
+
151
+ // Listen for live changes
152
+ (window as any).Kama?.events?.on('kama-forms:select-form', (payload) => {
153
+ console.log("Form Selected:", payload.formId);
154
+ });
155
+
156
+ ---
157
+
158
+ ## 6. Extension YAML Schemas (`content/`)
159
+
160
+ Extensions can bundle their own content schemas (Nodes, Blocks, Taxonomies). Place them in your extension's `content/` folder (e.g., `extensions/forms/content/blocks/form.yaml`).
161
+
162
+ Run `npm run build:schema` to compile them automatically into KAMA.
163
+
164
+ ### 🎯 NEW: Dynamic API Dropdowns (`optionsEndpoint`)
165
+
166
+ KAMA's frontend features Universal Async Field fetching. You can define a `select` or `pills` field that dynamically pulls its options from your extension's API endpoint!
167
+
168
+ **Example YAML Block Definition:**
169
+
170
+ # extensions/forms/content/blocks/form-block.yaml
171
+
172
+ id: form
173
+ label: Dynamic Form
174
+ description: Embeds a form definition into the page.
175
+ category: Interactive
176
+ icon: ClipboardList
177
+
178
+ # You can inherit standard KAMA fieldsets!
179
+
180
+ fieldSets:
181
+
182
+ - access
183
+
184
+ fields:
185
+
186
+ # This dropdown populates automatically via your API!
187
+
188
+ formId:
189
+ label: Select Form
190
+ type: select
191
+ required: true
192
+ optionsEndpoint: /api/admin/ext/kama-forms/forms
193
+ optionsValueKey: id # Maps to the option value
194
+ optionsLabelKey: title # Maps to the visible label
195
+
196
+ ---
197
+
198
+ ## 7. Caching and Extension Updates
199
+
200
+ Because KAMA caches HTML at the Edge (Cloudflare CDN) and inside Worker KV:
201
+
202
+ If your extension API modifies public-facing content (e.g., approving a review, updating inventory), your extension's Hono router must manually trigger KAMA's cache purger to drop the cached Edge pages.
203
+
204
+ _(Note: KAMA's `EdgeCacheManager` is currently scoped to the core, so for now, extensions should rely on KAMA's internal APIs or direct node updates if immediate Edge Purging is required)._
@@ -0,0 +1,5 @@
1
+ import kamaForms from './extensions/kama-forms/index.ts';
2
+
3
+ export const activeExtensions = [
4
+ kamaForms
5
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "1.0.0-alpha.16",
3
+ "version": "1.0.0-alpha.18",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -54,6 +54,7 @@
54
54
  "@tiptap/vue-3": "^3.30.0",
55
55
  "drizzle-orm": "^0.45.2",
56
56
  "hono": "^4.13.2",
57
+ "js-yaml": "^5.5.0",
57
58
  "locale-codes": "^1.3.1",
58
59
  "lru-cache": "^11.5.2",
59
60
  "lucide-vue-next": "^1.0.0",
@@ -68,6 +69,7 @@
68
69
  "@iconify-json/heroicons": "^1.2.3",
69
70
  "@iconify-json/lucide": "^1.2.123",
70
71
  "@libsql/client": "^0.14.0",
72
+ "@types/js-yaml": "^4.0.9",
71
73
  "@types/node": "^26.2.0",
72
74
  "@types/sharp": "^0.31.1",
73
75
  "@types/yaml": "^1.9.6",