@brightsideoy/kama 0.1.0-alpha.99 → 1.0.0-alpha.11

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 (120) hide show
  1. package/README.md +169 -0
  2. package/bin/cli.js +738 -261
  3. package/content/definitions/blocks/button.yaml +34 -0
  4. package/content/definitions/blocks/header.yaml +13 -0
  5. package/content/definitions/blocks/header_image.yaml +21 -0
  6. package/content/definitions/blocks/hero.yaml +28 -0
  7. package/content/definitions/blocks/html.yaml +13 -0
  8. package/content/definitions/blocks/image_card.yaml +56 -0
  9. package/content/definitions/blocks/node-list.yaml +61 -0
  10. package/content/definitions/blocks/single_image.yaml +27 -0
  11. package/content/definitions/blocks/text.yaml +15 -0
  12. package/content/definitions/channels/ecommerce.yaml +28 -0
  13. package/content/definitions/channels/website.yaml +41 -0
  14. package/{defaults → content}/definitions/field-sets/access.yaml +3 -5
  15. package/content/definitions/field-sets/channel-base.yaml +57 -0
  16. package/{defaults/definitions/field-sets/page-meta.yaml → content/definitions/field-sets/channel-seo.yaml} +3 -11
  17. package/content/definitions/field-sets/content-block.yaml +41 -0
  18. package/content/definitions/field-sets/page-base.yaml +44 -0
  19. package/content/definitions/field-sets/page-seo.yaml +34 -0
  20. package/{defaults → content}/definitions/field-sets/page-social.yaml +6 -8
  21. package/content/definitions/field-sets/schema-type.yaml +43 -0
  22. package/content/definitions/field-sets/tree-base.yaml +31 -0
  23. package/content/definitions/node-layouts/content.yml +12 -0
  24. package/{defaults → content}/definitions/node-layouts/landing-page.yml +8 -3
  25. package/{defaults → content}/definitions/node-layouts/single-column.yml +10 -5
  26. package/{defaults → content}/definitions/node-layouts/two-column.yml +14 -6
  27. package/content/definitions/nodes/address.yaml +40 -0
  28. package/content/definitions/nodes/article.yaml +65 -0
  29. package/content/definitions/nodes/event.yaml +55 -0
  30. package/content/definitions/nodes/external-link.yaml +29 -0
  31. package/content/definitions/nodes/page-skeleton.yaml +48 -0
  32. package/{defaults → content}/definitions/nodes/page.yaml +9 -20
  33. package/content/definitions/nodes/person.yaml +37 -0
  34. package/content/definitions/nodes/product.yaml +89 -0
  35. package/content/definitions/taxonomies/categories.yaml +15 -0
  36. package/content/definitions/taxonomies/tags.yaml +12 -0
  37. package/content/definitions/trees/navigation.yaml +24 -0
  38. package/definitions/editor/default.ts +1 -0
  39. package/dist/assets/index-BVkEzCNs.js +164 -0
  40. package/dist/assets/index-CGLx9oqF.css +1 -0
  41. package/dist/build-seed.js +486 -0
  42. package/dist/dist-kIJlDMvm.js +27162 -0
  43. package/dist/index.html +4 -2
  44. package/dist/index.js +26186 -8576
  45. package/dist/schema-compiler.js +48 -0
  46. package/documentation/SCHEMAS.md +182 -0
  47. package/package.json +24 -25
  48. package/schema.sql +67 -23
  49. package/templates/astro-starter/astro.config.mjs +27 -0
  50. package/templates/astro-starter/package.json.template +20 -0
  51. package/templates/astro-starter/public/robots.txt +23 -0
  52. package/templates/astro-starter/src/components/blocks/BlockRenderer.astro +143 -0
  53. package/templates/astro-starter/src/components/blocks/ColumnLayout.astro +67 -0
  54. package/templates/astro-starter/src/components/blocks/HeaderImage.astro +126 -0
  55. package/templates/astro-starter/src/components/blocks/Hero.astro +218 -0
  56. package/templates/astro-starter/src/components/blocks/HtmlBlock.astro +25 -0
  57. package/templates/astro-starter/src/components/blocks/ImageBlock.astro +91 -0
  58. package/templates/astro-starter/src/components/blocks/NodeList.astro +278 -0
  59. package/templates/astro-starter/src/components/blocks/TextBlock.astro +15 -0
  60. package/templates/astro-starter/src/components/global/Breadcrumb.astro +108 -0
  61. package/templates/astro-starter/src/components/global/Footer.astro +50 -0
  62. package/templates/astro-starter/src/components/global/Header.astro +171 -0
  63. package/templates/astro-starter/src/components/global/MetaHead.astro +266 -0
  64. package/templates/astro-starter/src/components/global/NavigationMenu.astro +413 -0
  65. package/templates/astro-starter/src/components/ui/KamaImage.astro +180 -0
  66. package/templates/astro-starter/src/layouts/SkeletonLayout.astro +56 -0
  67. package/templates/astro-starter/src/layouts/StandardLayout.astro +63 -0
  68. package/templates/astro-starter/src/lib/cms.ts +219 -0
  69. package/templates/astro-starter/src/lib/media/kama-image-service.ts +67 -0
  70. package/templates/astro-starter/src/lib/media/media-proxy.ts +148 -0
  71. package/templates/astro-starter/src/lib/media/media-url.ts +112 -0
  72. package/templates/astro-starter/src/middleware.ts +508 -0
  73. package/templates/astro-starter/src/pages/404.astro +46 -0
  74. package/templates/astro-starter/src/pages/[...slug].astro +318 -0
  75. package/templates/astro-starter/src/pages/api/load-nodes.ts +40 -0
  76. package/templates/astro-starter/src/pages/llms-full.txt.ts +171 -0
  77. package/templates/astro-starter/src/pages/llms.txt.ts +113 -0
  78. package/templates/astro-starter/src/pages/media/[...path].ts +14 -0
  79. package/templates/astro-starter/src/pages/partials/footer.astro +69 -0
  80. package/templates/astro-starter/src/pages/partials/nav.astro +141 -0
  81. package/templates/astro-starter/src/styles/standard-theme.css +157 -0
  82. package/templates/astro-starter/src/utils/tiptap.ts +113 -0
  83. package/templates/astro-starter/tsconfig.json +0 -0
  84. package/templates/kama-worker/index.js +19 -0
  85. package/templates/kama-worker/server.js +104 -0
  86. package/wrangler.json +22 -0
  87. package/defaults/definitions/blocks/button.yaml +0 -15
  88. package/defaults/definitions/blocks/header.yaml +0 -15
  89. package/defaults/definitions/blocks/header_image.yaml +0 -22
  90. package/defaults/definitions/blocks/hero.yaml +0 -27
  91. package/defaults/definitions/blocks/image_card.yaml +0 -57
  92. package/defaults/definitions/blocks/single_image.yaml +0 -28
  93. package/defaults/definitions/blocks/text.yaml +0 -16
  94. package/defaults/definitions/channels/ecommerce.yaml +0 -24
  95. package/defaults/definitions/channels/website.yaml +0 -32
  96. package/defaults/definitions/field-sets/channel-base.yaml +0 -31
  97. package/defaults/definitions/field-sets/content-block.yaml +0 -46
  98. package/defaults/definitions/field-sets/page-base.yaml +0 -49
  99. package/defaults/definitions/field-sets/tree-base.yaml +0 -31
  100. package/defaults/definitions/node-layouts/three-column.yml +0 -41
  101. package/defaults/definitions/nodes/address.yaml +0 -32
  102. package/defaults/definitions/nodes/article.yaml +0 -43
  103. package/defaults/definitions/nodes/event.yaml +0 -49
  104. package/defaults/definitions/nodes/external-link.yaml +0 -26
  105. package/defaults/definitions/nodes/person.yaml +0 -37
  106. package/defaults/definitions/nodes/product.yaml +0 -86
  107. package/defaults/definitions/trees/navigation.yaml +0 -29
  108. package/defaults/definitions/trees/social-links.yaml +0 -9
  109. package/definitions/_page.yaml +0 -52
  110. package/dist/assets/index-D6qQsWOj.js +0 -1392
  111. package/dist/assets/index-TdW5F0pX.css +0 -1
  112. package/dist/media/kama/2026/08/17a098c92bc54ea4b4441e531a34575b.jpg +0 -0
  113. package/dist/media/kama/2026/08/3e9ae457dd23461680cc699f42d07cf0.jpg +0 -0
  114. package/dist/media/kama/2026/08/8e5b3a2fd194459f9b962f8e12928bf0.jpg +0 -0
  115. package/dist/media/kama/2026/08/8ea5c21026cd4944bdf78c99fe2544a9.jpg +0 -0
  116. package/scripts/bundle-definitions.js +0 -176
  117. /package/{defaults → content}/definitions/block-layouts/three-column.yaml +0 -0
  118. /package/{defaults → content}/definitions/block-layouts/two-column.yaml +0 -0
  119. /package/{defaults → content}/definitions/field-sets/comments.yaml +0 -0
  120. /package/{defaults → content}/definitions/field-sets/visibility.yaml +0 -0
@@ -0,0 +1,48 @@
1
+ import { fileURLToPath as e, pathToFileURL as t } from "node:url";
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, resolve as c } from "node:path";
4
+ //#region scripts/schema-compiler.ts
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;
18
+ try {
19
+ JSON.parse(i(s(e, "package.json"), "utf-8")).name === "@brightsideoy/kama" && (o = !0);
20
+ } catch {}
21
+ let c;
22
+ if (o) {
23
+ let t = s(e, "server", "db");
24
+ n(t) || r(t, { recursive: !0 }), c = s(t, "bundled-definitions.js");
25
+ } else {
26
+ let t = s(e, "kama-worker");
27
+ n(t) || r(t, { recursive: !0 }), c = s(t, "bundled-definitions.js");
28
+ }
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;
32
+ else try {
33
+ l = (await import("./dist-kIJlDMvm.js")).compileDefinitions;
34
+ } catch (e) {
35
+ console.error("\x1B[31m ✖\x1B[0m \x1B[31mCould not resolve Kama Schema Engine:\x1B[0m", e), process.exit(1);
36
+ }
37
+ l || (console.error("\x1B[31m ✖\x1B[0m \x1B[31mcompileDefinitions function missing from resolved module\x1B[0m"), process.exit(1));
38
+ let m = l(), h = Object.keys(m?.nodes || {}).length, g = Object.keys(m?.blocks || {}).length, _ = Object.keys(m?.taxonomies || {}).length, v = `// AUTO-GENERATED BUNDLE - DO NOT EDIT
39
+ const defs = ${JSON.stringify(m, null, 2)};
40
+ export const BUNDLED_DEFINITIONS = defs;
41
+ export default defs;
42
+ `;
43
+ a(c, v, "utf-8"), console.log(`\x1b[32m ✓\x1b[0m \x1b[2mSchemas bundled (${h} nodes, ${g} blocks, ${_} taxonomies)\x1b[0m`);
44
+ }
45
+ f().catch((e) => {
46
+ console.error("\x1B[31m ✖\x1B[0m \x1B[31mSchema Compiler Runtime Error:\x1B[0m", e), process.exit(1);
47
+ });
48
+ //#endregion
@@ -0,0 +1,182 @@
1
+ # Kama CMS Schema Documentation
2
+
3
+ Kama CMS uses a highly dynamic, YAML-driven schema system. Instead of hardcoding database tables and React/Vue forms, you define your content models, layouts, and components using simple YAML files. The Kama build pipeline automatically generates the admin UI, database indexes, and API endpoints based on these definitions.
4
+
5
+ Here is the complete guide to understanding, overriding, and creating custom schemas in your Kama project.
6
+
7
+ ## 1. Directory Structure: What Goes Where?
8
+
9
+ Your project’s schemas live in the `content/definitions/` (or `schemas/`) directory. The folder structure dictates how the CMS interprets the YAML file:
10
+
11
+ | Directory | Purpose | Example Use Case |
12
+ | :---------------- | :---------------------------------------------------------------- | :----------------------------------------------------------- |
13
+ | `/nodes/` | Standalone content entities (Pages, Articles, Products, Persons). | A blog post, a product page, or an office location. |
14
+ | `/blocks/` | Reusable content components placed on a page canvas. | A hero banner, a text block, a call-to-action button. |
15
+ | `/node-layouts/` | Page-level CSS Grid templates that define regions (drop zones). | A standard landing page, a 3-column layout with a sidebar. |
16
+ | `/block-layouts/` | Component-level layout wrappers for grouping blocks. | A 50/50 split column block. |
17
+ | `/channels/` | Top-level endpoints (websites, apps, stores). | A primary marketing website or a secondary e-commerce store. |
18
+ | `/trees/` | Hierarchical structures for navigation and routing. | Main menu, footer links, social links. |
19
+ | `/field-sets/` | Reusable groupings of fields to keep your YAML DRY. | Standard SEO fields, Access/Visibility settings. |
20
+
21
+ ## 2. Overriding and Adding Custom Schemas
22
+
23
+ The Kama schema pipeline merges core package defaults with your local project files.
24
+
25
+ ### How to Override a Core Schema
26
+
27
+ To modify a default Kama schema (e.g., `page.yaml` or `address.yaml`), create a file with the **exact same name** in your local workspace:
28
+ `your-project/content/definitions/nodes/page.yaml`
29
+
30
+ During the build process (`npm run bundle:schemas`), the local file completely overwrites the core package file.
31
+
32
+ ### How to Add a Custom Schema
33
+
34
+ To create a completely new content type (e.g., a Job Posting), simply add a new file:
35
+ `your-project/content/definitions/nodes/job-posting.yaml`
36
+
37
+ Run `npm run deploy:cms` to deploy the worker. The CMS will automatically register the new schema, create database indexes, and generate the UI.
38
+
39
+ ## 3. Schema Anatomy (Top-Level Parameters)
40
+
41
+ Every YAML file represents a Schema object. Below are the top-level parameters you can define at the root of your YAML file.
42
+
43
+ ### Base Parameters
44
+
45
+ - `id` _(string)_: Unique identifier. Should match the filename (e.g., `image_card`).
46
+ - `label` _(string)_: Human-readable name displayed in the CMS Admin UI.
47
+ - `description` _(string)_: Optional subtext shown in block pickers or menus.
48
+
49
+ ### Node/Content Behaviors
50
+
51
+ - `inTree` _(boolean)_: If `true`, this node can be created in the "Site Tree" (usually for pages/routes).
52
+ - `inVault` _(boolean)_: If `true`, this node appears in the "Vault" (usually for structured data like Products, Addresses, Persons).
53
+ - `hasCanvas` _(boolean)_: If `true`, enables the visual drag-and-drop page builder for this node.
54
+ - `directLinking` _(boolean)_: If `true`, the node is resolvable to a frontend URL/slug (e.g., an Article or Page).
55
+
56
+ ### Layout & Canvas Restrictions
57
+
58
+ - `defaultLayout` _(string)_: The fallback layout ID if none is selected (e.g., `"single-column"`).
59
+ - `allowedLayouts` _(array)_: List of layout IDs this node is permitted to use.
60
+ - `allowedBlocks` _(array)_: Restricts which blocks can be dropped onto this node's canvas (e.g., `[header, hero, text, image_card]`).
61
+
62
+ ### Grid & Regions (For Layouts)
63
+
64
+ Used primarily in `/node-layouts/` and `/block-layouts/`.
65
+
66
+ - `layout`: Defines the CSS Grid structure.
67
+ - `columns` _(string)_: CSS grid-template-columns (e.g., `"10fr 4fr"`).
68
+ - `areas` _(array of arrays)_: Maps regions to the grid layout.
69
+ - `regions`: Configuration for the specific drop zones defined in the `areas`.
70
+ - `label` _(string)_: UI label for the drop zone.
71
+ - `showLabel` _(boolean)_: Shows/hides the header in the editor.
72
+ - `inherit` _(string)_: Can inherit content from `"parent"`.
73
+
74
+ ## 4. Tabs & Fields Architecture
75
+
76
+ Content input in Kama is organized into **Tabs**, which contain **Fields**.
77
+
78
+ ```yaml
79
+ tabs:
80
+ settings: # Tab ID
81
+ label: "Settings" # Tab UI Name
82
+ fields:
83
+ # Key-Value map of fields
84
+ title:
85
+ label: "Title"
86
+ type: "text"
87
+ required: true
88
+ ```
89
+
90
+ ## 5. Field Types (`type`)
91
+
92
+ The `type` parameter determines the UI component used in the CMS and how data is handled.
93
+
94
+ - `text`: Standard single-line text input.
95
+ - `textarea`: Multi-line text input.
96
+ - `richtext`: WYSIWYG editor. (Use `editor: "minimal"`, `"default"`, or `"button"` to configure toolbars).
97
+ - `number`: Numeric input.
98
+ - `boolean`: Toggle switch.
99
+ - `date` / `datetime`: Date/Time picker.
100
+ - `slug`: URL-friendly string generator. Often paired with `linkedTo: "title"`.
101
+ - `select`: Dropdown menu. (Requires an `options` array).
102
+ - `layout-select`: Specialized dropdown that queries available layout schemas.
103
+ - `language-select`: Specialized dropdown for locales.
104
+ - `link`: A unified link picker (internal nodes, trees, URLs, files, emails, tel).
105
+ - `icon`: Icon picker. (Configure with `packages: ["heroicons", "lucide"]`).
106
+ - `asset` / `image`: Cloudflare R2 media uploader.
107
+ - `hidden`: Hidden from the UI, used for programmatic data.
108
+
109
+ ## 6. Field Configuration Parameters
110
+
111
+ Inside a specific field, you can define how it looks, validates, and behaves in the database.
112
+
113
+ ### UI & Display
114
+
115
+ - `label` _(string)_: Field label.
116
+ - `description` _(string)_: Helper text below the input.
117
+ - `default` _(any)_: Default value when a new entry is created.
118
+ - `position` _(string)_: Set to `"sidebar"` to move the field out of the main content column.
119
+ - `showAsEditorTitle` _(boolean)_: Uses this field's value as the display title in Vault/Tree lists.
120
+ - `public` _(boolean)_: **CRITICAL.** If `true`, this field is exposed in the public frontend API. If omitted or `false`, the data remains private to the admin backend.
121
+
122
+ ### Database & Indexing
123
+
124
+ - `dbType` _(string)_: The SQLite data type (e.g., `text`, `integer`, `json`). Most custom fields live inside a JSON blob, but native columns require this.
125
+ - `index` _(boolean)_: Instructs the build pipeline to generate a SQLite index for this field for fast querying.
126
+ - `primaryKey` _(boolean)_: Defines the ID column.
127
+
128
+ ### Validation (`rules`)
129
+
130
+ - `required` _(boolean)_: Makes the field mandatory. (Can also be set outside of `rules`).
131
+ - `unique` / `uniqueSibling` _(boolean)_: Enforces uniqueness across the DB or tree branch.
132
+ - `pattern` _(string)_: Regex validation (e.g., `"^[a-z0-9_-]+$"`).
133
+ - `customMessage` _(string)_: Error message when validation fails.
134
+
135
+ ### Linking & Inheritance
136
+
137
+ - `linkedTo` _(string)_: Automatically populates this field based on another (e.g., auto-generating a slug from the title).
138
+ - `inherits` / `inheritsFromParent` _(string/boolean)_: Pulls a fallback value from a parent node if left blank.
139
+
140
+ ## 7. Media & Asset Specifics (`asset` / `image`)
141
+
142
+ Kama features a powerful R2-backed media engine with automated image cropping.
143
+
144
+ ```yaml
145
+ featuredImage:
146
+ label: Main Image
147
+ type: asset
148
+ multiple: false
149
+ maxSizeMB: 15
150
+ maxPreviewWidth: 300px
151
+ allowedTypes: ["image/jpeg", "image/png", "image/webp"]
152
+ crops:
153
+ list_thumb:
154
+ label: "List View (4:3)"
155
+ aspectRatio: 1.333
156
+ catalog_card:
157
+ label: "Portrait Card (3:4)"
158
+ aspectRatio: 0.75
159
+ ```
160
+
161
+ - `multiple` _(boolean)_: Enables multi-file upload (Gallery).
162
+ - `allowedTypes` _(array)_: MIME type restrictions.
163
+ - `crops`: A map of required crop aspect ratios. The CMS will prompt the editor to crop the image to these exact ratios upon upload. The frontend API returns optimized URLs for each crop key.
164
+
165
+ ## 8. Field Sets (Inclusions)
166
+
167
+ To keep your schemas DRY, Kama allows you to group common fields in `/field-sets/` and import them into other schemas.
168
+
169
+ For example, `content/definitions/field-sets/page-meta.yaml` defines SEO fields.
170
+
171
+ **How to include a Field Set in a Schema:**
172
+ Use the `fieldSet` keyword inside your fields list.
173
+
174
+ ```yaml
175
+ tabs:
176
+ settings:
177
+ fields:
178
+ title:
179
+ label: "Title"
180
+ # Imports the entire page-base YAML here
181
+ fieldSet: page-base
182
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "0.1.0-alpha.99",
3
+ "version": "1.0.0-alpha.11",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -8,41 +8,39 @@
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
10
  "import": "./dist/index.js"
11
- }
11
+ },
12
+ "./schema.sql": "./schema.sql",
13
+ "./package.json": "./package.json"
12
14
  },
13
15
  "bin": {
14
16
  "kama": "bin/cli.js",
15
17
  "create-kama": "bin/cli.js"
16
18
  },
17
- "files": [
18
- "dist",
19
- "defaults",
20
- "definitions",
21
- "bin",
22
- "scripts",
23
- "schema.sql"
24
- ],
25
19
  "scripts": {
26
- "predev": "npm run build:schema && npm run bundle:definitions",
27
- "dev": "concurrently \"vite\" \"tsx watch server/dev.ts\"",
28
- "bundle:definitions": "node scripts/bundle-definitions.js",
29
- "build:schema": "tsx src/db/generate-schema.ts",
30
- "build:server": "npm run bundle:definitions && vite build",
20
+ "predev": "npm run bundle:definitions && npm run build:schema",
21
+ "dev": "npm run dev:local",
22
+ "dev:local": "concurrently \"vite\" \"KAMA_RUNTIME=node tsx server/dev.ts\"",
23
+ "dev:cf": "concurrently \"vite\" \"wrangler dev server/index.ts\"",
24
+ "bundle:definitions": "node bin/cli.js bundle",
25
+ "build:schema": "tsx scripts/build-schema.ts",
26
+ "build:seed": "tsx scripts/build-seed.ts",
27
+ "seed": "tsx scripts/build-seed.ts && sqlite3 kama.db < seed.sql",
28
+ "build:server": "vite build",
31
29
  "build:ui": "vite build --config vite.config.ui.ts",
32
- "build": "npm run build:schema && npm run build:server && npm run build:ui",
30
+ "build": "npm run bundle:definitions && npm run build:schema && npm run build:server && npm run build:ui",
33
31
  "build:cms": "npm run build",
34
32
  "prepublishOnly": "npm run build",
35
- "db:seed": "npm run build:schema && tsx -e \"import Database from 'better-sqlite3'; import fs from 'node:fs'; const db = new Database('data/cms.db'); db.exec(fs.readFileSync('schema.sql', 'utf8')); console.log('✅ Database schema applied successfully!');\"",
36
- "d1:schema:remote": "npm run build:schema && npx wrangler d1 execute YOUR_D1_NAME --remote --file=./schema.sql",
37
- "serve": "tsx server/dev.ts"
33
+ "serve": "wrangler dev server/index.ts"
38
34
  },
39
35
  "publishConfig": {
40
36
  "access": "public"
41
37
  },
42
38
  "dependencies": {
43
- "@brightsideoy/kama": "^0.1.0-alpha.47",
39
+ "@clack/prompts": "^1.8.1",
44
40
  "@heroicons/vue": "^2.2.0",
45
41
  "@hono/node-server": "^2.1.1",
42
+ "@hono/swagger-ui": "^0.6.1",
43
+ "@hono/zod-openapi": "^1.6.3",
46
44
  "@tiptap/core": "^3.30.0",
47
45
  "@tiptap/extension-subscript": "^3.30.1",
48
46
  "@tiptap/extension-superscript": "^3.30.1",
@@ -53,7 +51,6 @@
53
51
  "@tiptap/pm": "^3.30.0",
54
52
  "@tiptap/starter-kit": "^3.30.0",
55
53
  "@tiptap/vue-3": "^3.30.0",
56
- "better-sqlite3": "^13.0.3",
57
54
  "drizzle-orm": "^0.45.2",
58
55
  "hono": "^4.13.2",
59
56
  "locale-codes": "^1.3.1",
@@ -63,22 +60,24 @@
63
60
  "vue": "^3.5.41",
64
61
  "vue-advanced-cropper": "^2.8.9",
65
62
  "vuedraggable": "^4.1.0",
66
- "yaml": "^2.9.0"
63
+ "yaml": "^2.9.0",
64
+ "zod": "^4.6.5"
67
65
  },
68
66
  "devDependencies": {
69
67
  "@iconify-json/heroicons": "^1.2.3",
70
68
  "@iconify-json/lucide": "^1.2.123",
71
- "@types/better-sqlite3": "^9.6.0",
69
+ "@libsql/client": "^0.14.0",
72
70
  "@types/node": "^26.2.0",
73
71
  "@types/sharp": "^0.31.1",
74
72
  "@types/yaml": "^1.9.6",
75
73
  "@vitejs/plugin-vue": "^6.0.8",
76
74
  "concurrently": "^10.0.4",
77
- "drizzle-kit": "^0.18.1",
75
+ "drizzle-kit": "^0.31.11",
78
76
  "tsx": "^4.23.12",
79
77
  "typescript": "~5.7.2",
80
78
  "unplugin-icons": "^23.0.1",
81
79
  "vite": "^8.2.1",
82
- "vue-tsc": "^2.2.0"
80
+ "vue-tsc": "^2.2.0",
81
+ "wrangler": "^4.134.0"
83
82
  }
84
83
  }
package/schema.sql CHANGED
@@ -1,5 +1,5 @@
1
1
  -- Kama Core System Schema
2
- -- Auto-generated from server/db/dynamic-sync.ts
2
+ -- Auto-generated from server/db/schema.ts
3
3
 
4
4
  CREATE TABLE IF NOT EXISTS "content_schemas" (
5
5
  id TEXT PRIMARY KEY,
@@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS "content_schemas" (
13
13
 
14
14
  CREATE TABLE IF NOT EXISTS "users" (
15
15
  id TEXT PRIMARY KEY,
16
- email TEXT UNIQUE NOT NULL,
16
+ email UNIQUE NOT NULL,
17
17
  name TEXT NOT NULL,
18
18
  globalRole TEXT DEFAULT 'editor',
19
19
  createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
@@ -46,7 +46,7 @@ CREATE TABLE IF NOT EXISTS "channels" (
46
46
  channelType TEXT,
47
47
  domain TEXT,
48
48
  baseUrl TEXT,
49
- defaultLanguage TEXT DEFAULT 'en',
49
+ defaultLanguage TEXT,
50
50
  isHidden INTEGER NOT NULL DEFAULT 0,
51
51
  dataJson TEXT,
52
52
  createdById TEXT,
@@ -89,9 +89,10 @@ CREATE TABLE IF NOT EXISTS "content_nodes" (
89
89
  contentType TEXT NOT NULL,
90
90
  title TEXT NOT NULL,
91
91
  slug TEXT,
92
- language TEXT DEFAULT 'en',
92
+ language TEXT,
93
93
  isHidden INTEGER NOT NULL DEFAULT 0,
94
94
  status TEXT DEFAULT 'draft',
95
+ hasUnpublishedChanges INTEGER DEFAULT 0,
95
96
  disabledRegionsJson TEXT DEFAULT '{}',
96
97
  dataJson TEXT,
97
98
  draftDataJson TEXT,
@@ -115,14 +116,14 @@ CREATE TABLE IF NOT EXISTS "node_channels" (
115
116
  );
116
117
 
117
118
  CREATE TABLE IF NOT EXISTS "tree_nodes" (
118
- id INTEGER PRIMARY KEY AUTOINCREMENT,
119
119
  treeId INTEGER NOT NULL,
120
120
  contentId INTEGER NOT NULL,
121
121
  parentId INTEGER,
122
122
  sortOrder INTEGER DEFAULT 0,
123
+ PRIMARY KEY (treeId, contentId),
123
124
  FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE,
124
125
  FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE,
125
- FOREIGN KEY (parentId) REFERENCES tree_nodes(id) ON DELETE CASCADE
126
+ FOREIGN KEY (parentId) REFERENCES content_nodes(id) ON DELETE CASCADE
126
127
  );
127
128
 
128
129
  CREATE TABLE IF NOT EXISTS "content_blocks" (
@@ -176,6 +177,56 @@ CREATE TABLE IF NOT EXISTS "node_assets" (
176
177
  FOREIGN KEY (assetId) REFERENCES assets(id) ON DELETE CASCADE
177
178
  );
178
179
 
180
+ CREATE TABLE IF NOT EXISTS "taxonomy_terms" (
181
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
182
+ taxonomy TEXT NOT NULL,
183
+ key TEXT NOT NULL,
184
+ label TEXT NOT NULL,
185
+ parentId INTEGER,
186
+ sortOrder INTEGER DEFAULT 0,
187
+ dataJson TEXT,
188
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
189
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
190
+ FOREIGN KEY (parentId) REFERENCES taxonomy_terms(id) ON DELETE CASCADE,
191
+ UNIQUE(taxonomy, key)
192
+ );
193
+
194
+ CREATE TABLE IF NOT EXISTS "node_terms" (
195
+ nodeId INTEGER NOT NULL,
196
+ termId INTEGER NOT NULL,
197
+ fieldKey TEXT NOT NULL,
198
+ sortOrder INTEGER DEFAULT 0,
199
+ PRIMARY KEY (nodeId, termId, fieldKey),
200
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
201
+ FOREIGN KEY (termId) REFERENCES taxonomy_terms(id) ON DELETE CASCADE
202
+ );
203
+
204
+ CREATE TABLE IF NOT EXISTS "node_relations" (
205
+ sourceNodeId INTEGER NOT NULL,
206
+ targetNodeId INTEGER NOT NULL,
207
+ fieldKey TEXT NOT NULL,
208
+ sortOrder INTEGER DEFAULT 0,
209
+ PRIMARY KEY (sourceNodeId, targetNodeId, fieldKey),
210
+ FOREIGN KEY (sourceNodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
211
+ FOREIGN KEY (targetNodeId) REFERENCES content_nodes(id) ON DELETE CASCADE
212
+ );
213
+
214
+ CREATE TABLE IF NOT EXISTS "node_routes" (
215
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
216
+ path TEXT NOT NULL,
217
+ channelId INTEGER NOT NULL,
218
+ nodeId INTEGER,
219
+ treeId INTEGER,
220
+ isCanonical INTEGER DEFAULT 1,
221
+ redirectUrl TEXT,
222
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
223
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
224
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE,
225
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
226
+ FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE,
227
+ UNIQUE(path, channelId)
228
+ );
229
+
179
230
  CREATE INDEX IF NOT EXISTS idx_tree_nodes_tree_parent ON tree_nodes(treeId, parentId);
180
231
  CREATE INDEX IF NOT EXISTS idx_tree_nodes_contentId ON tree_nodes(contentId);
181
232
  CREATE INDEX IF NOT EXISTS idx_content_nodes_slug_status ON content_nodes(slug, status);
@@ -198,24 +249,17 @@ CREATE INDEX IF NOT EXISTS idx_node_channels_channel ON node_channels(channelId,
198
249
  CREATE INDEX IF NOT EXISTS idx_node_channels_node ON node_channels(nodeId, channelId);
199
250
  CREATE INDEX IF NOT EXISTS idx_node_assets_node_asset ON node_assets(nodeId, assetId);
200
251
  CREATE INDEX IF NOT EXISTS idx_node_assets_asset_node ON node_assets(assetId, nodeId);
201
- CREATE INDEX IF NOT EXISTS idx_node_revisions_node ON node_revisions(nodeId, version);
252
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_node_revisions_version ON node_revisions(nodeId, version);
202
253
  CREATE INDEX IF NOT EXISTS idx_assets_system_created ON assets(isSystemVariant, createdAt DESC);
203
254
  CREATE INDEX IF NOT EXISTS idx_assets_system_mime ON assets(isSystemVariant, mimeType);
204
255
  CREATE INDEX IF NOT EXISTS idx_assets_deleted ON assets(deletedAt);
205
256
  CREATE INDEX IF NOT EXISTS idx_assets_parent ON assets(parentAssetId);
206
-
207
- -- System Defaults
208
- INSERT OR IGNORE INTO channels (id, key, label, channelType, defaultLanguage, isHidden, dataJson) VALUES (1, 'default-channel', 'Default Channel', 'website', 'en', 0, '{}');
209
- INSERT OR IGNORE INTO trees (id, key, label, treeType, channelId, isHidden, dataJson) VALUES (1, 'default-tree', 'Default Navigation', 'navigation', 1, 0, '{}');
210
- INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES (1, 1, 'main');
211
-
212
-
213
- -- Native Columns
214
- CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes (slug);
215
- CREATE INDEX IF NOT EXISTS idx_content_nodes_contentType ON content_nodes (contentType);
216
- CREATE INDEX IF NOT EXISTS idx_content_nodes_updatedAt ON content_nodes (updatedAt);
217
-
218
- -- JSON Custom Fields
219
- CREATE INDEX IF NOT EXISTS idx_content_nodes_articleDate ON content_nodes (json_extract(dataJson, '$.articleDate'));
220
- CREATE INDEX IF NOT EXISTS idx_content_nodes_startTime ON content_nodes (json_extract(dataJson, '$.startTime'));
221
- CREATE INDEX IF NOT EXISTS idx_content_nodes_endTime ON content_nodes (json_extract(dataJson, '$.endTime'));
257
+ CREATE INDEX IF NOT EXISTS idx_taxonomy_terms_tax ON taxonomy_terms(taxonomy);
258
+ CREATE INDEX IF NOT EXISTS idx_node_terms_node ON node_terms(nodeId);
259
+ CREATE INDEX IF NOT EXISTS idx_node_terms_term ON node_terms(termId);
260
+ CREATE INDEX IF NOT EXISTS idx_node_terms_node_term ON node_terms(nodeId, termId);
261
+ CREATE INDEX IF NOT EXISTS idx_node_terms_term_node ON node_terms(termId, nodeId);
262
+ CREATE INDEX IF NOT EXISTS idx_node_relations_source ON node_relations(sourceNodeId);
263
+ CREATE INDEX IF NOT EXISTS idx_node_relations_target ON node_relations(targetNodeId);
264
+ CREATE INDEX IF NOT EXISTS idx_node_routes_lookup ON node_routes(path, channelId);
265
+ CREATE INDEX IF NOT EXISTS idx_node_routes_node ON node_routes(nodeId);
@@ -0,0 +1,27 @@
1
+ import { defineConfig } from 'astro/config';
2
+ import node from '@astrojs/node';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ export default defineConfig({
9
+ devToolbar: {
10
+ enabled: false
11
+ },
12
+ output: 'server',
13
+ trailingSlash: 'ignore',
14
+ image: { service: { entrypoint: './src/lib/media/kama-image-service.ts' } },
15
+ adapter: node({ mode: "standalone" }),
16
+ vite: {
17
+ resolve: { alias: { '@': path.resolve(__dirname, './src') } },
18
+ ssr: {
19
+ external: ['cloudflare:workers', 'tinyexec', 'node:child_process', 'node:fs', 'node:url', 'node:path', 'node:stream', 'node:readline', 'node:stream/promises']
20
+ },
21
+ environments: {
22
+ ssr: {
23
+ external: ['cloudflare:workers', 'tinyexec', 'node:child_process', 'node:fs', 'node:url', 'node:path', 'node:stream', 'node:readline', 'node:stream/promises']
24
+ }
25
+ }
26
+ }
27
+ });
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{SITE_NAME}}",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "engines": {
6
+ "node": ">=22.12.0"
7
+ },
8
+ "scripts": {
9
+ "dev": "astro dev",
10
+ "build": "astro build",
11
+ "preview": "astro preview",
12
+ "deploy": "astro build && wrangler deploy"
13
+ },
14
+ "dependencies": {
15
+ "@astrojs/cloudflare": "^14.2.5",
16
+ "@brightsideoy/kama": "{{KAMA_VERSION}}",
17
+ "astro": "^7.2.0",
18
+ "wrangler": "^4.0.0"
19
+ }
20
+ }
@@ -0,0 +1,23 @@
1
+ # Allow standard search engine crawlers
2
+ User-agent: *
3
+ Allow: /
4
+
5
+ # Block sensitive or irrelevant paths
6
+ Disallow: /api/
7
+ Disallow: /*?*search=
8
+
9
+ # Explicitly block AI scrapers / Training Bots (Optional)
10
+ # User-agent: GPTBot
11
+ # Disallow: /
12
+
13
+ # User-agent: CCBot
14
+ # Disallow: /
15
+
16
+ # User-agent: Anthropic-ai
17
+ # Disallow: /
18
+
19
+ # User-agent: Google-Extended
20
+ # Disallow: /
21
+
22
+ # XML Sitemap location
23
+ # Sitemap: https://yourdomain.com/sitemap-index.xml