@brightsideoy/kama 0.1.0-alpha.98 → 1.0.0-alpha.10
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.
- package/README.md +169 -0
- package/bin/cli.js +726 -261
- package/content/definitions/blocks/button.yaml +34 -0
- package/content/definitions/blocks/header.yaml +13 -0
- package/content/definitions/blocks/header_image.yaml +21 -0
- package/content/definitions/blocks/hero.yaml +28 -0
- package/content/definitions/blocks/html.yaml +13 -0
- package/content/definitions/blocks/image_card.yaml +56 -0
- package/content/definitions/blocks/node-list.yaml +61 -0
- package/content/definitions/blocks/single_image.yaml +27 -0
- package/content/definitions/blocks/text.yaml +15 -0
- package/content/definitions/channels/ecommerce.yaml +28 -0
- package/content/definitions/channels/website.yaml +41 -0
- package/{defaults → content}/definitions/field-sets/access.yaml +3 -5
- package/content/definitions/field-sets/channel-base.yaml +57 -0
- package/{defaults/definitions/field-sets/page-meta.yaml → content/definitions/field-sets/channel-seo.yaml} +3 -11
- package/content/definitions/field-sets/content-block.yaml +41 -0
- package/content/definitions/field-sets/page-base.yaml +44 -0
- package/content/definitions/field-sets/page-seo.yaml +34 -0
- package/{defaults → content}/definitions/field-sets/page-social.yaml +6 -8
- package/content/definitions/field-sets/schema-type.yaml +43 -0
- package/content/definitions/field-sets/tree-base.yaml +31 -0
- package/content/definitions/node-layouts/content.yml +12 -0
- package/{defaults → content}/definitions/node-layouts/landing-page.yml +8 -3
- package/{defaults → content}/definitions/node-layouts/single-column.yml +10 -5
- package/{defaults → content}/definitions/node-layouts/two-column.yml +14 -6
- package/content/definitions/nodes/address.yaml +40 -0
- package/content/definitions/nodes/article.yaml +65 -0
- package/content/definitions/nodes/event.yaml +55 -0
- package/content/definitions/nodes/external-link.yaml +29 -0
- package/content/definitions/nodes/page-skeleton.yaml +48 -0
- package/{defaults → content}/definitions/nodes/page.yaml +9 -20
- package/content/definitions/nodes/person.yaml +37 -0
- package/content/definitions/nodes/product.yaml +89 -0
- package/content/definitions/taxonomies/categories.yaml +15 -0
- package/content/definitions/taxonomies/tags.yaml +12 -0
- package/content/definitions/trees/navigation.yaml +24 -0
- package/definitions/editor/default.ts +1 -0
- package/dist/assets/index-Bzbme3Ob.css +1 -0
- package/dist/assets/index-DX98CiK-.js +164 -0
- package/dist/dist-CErcWOwp.js +27136 -0
- package/dist/index.html +4 -2
- package/dist/index.js +26159 -8575
- package/dist/schema-compiler.js +48 -0
- package/documentation/SCHEMAS.md +182 -0
- package/package.json +23 -25
- package/schema.sql +67 -23
- package/templates/astro-starter/astro.config.mjs +27 -0
- package/templates/astro-starter/package.json.template +20 -0
- package/templates/astro-starter/public/robots.txt +23 -0
- package/templates/astro-starter/src/components/blocks/BlockRenderer.astro +143 -0
- package/templates/astro-starter/src/components/blocks/ColumnLayout.astro +67 -0
- package/templates/astro-starter/src/components/blocks/HeaderImage.astro +126 -0
- package/templates/astro-starter/src/components/blocks/Hero.astro +218 -0
- package/templates/astro-starter/src/components/blocks/HtmlBlock.astro +25 -0
- package/templates/astro-starter/src/components/blocks/ImageBlock.astro +91 -0
- package/templates/astro-starter/src/components/blocks/NodeList.astro +278 -0
- package/templates/astro-starter/src/components/blocks/TextBlock.astro +15 -0
- package/templates/astro-starter/src/components/global/Breadcrumb.astro +108 -0
- package/templates/astro-starter/src/components/global/Footer.astro +50 -0
- package/templates/astro-starter/src/components/global/Header.astro +171 -0
- package/templates/astro-starter/src/components/global/MetaHead.astro +266 -0
- package/templates/astro-starter/src/components/global/NavigationMenu.astro +413 -0
- package/templates/astro-starter/src/components/ui/KamaImage.astro +180 -0
- package/templates/astro-starter/src/layouts/SkeletonLayout.astro +56 -0
- package/templates/astro-starter/src/layouts/StandardLayout.astro +63 -0
- package/templates/astro-starter/src/lib/cms.ts +219 -0
- package/templates/astro-starter/src/lib/media/kama-image-service.ts +67 -0
- package/templates/astro-starter/src/lib/media/media-proxy.ts +148 -0
- package/templates/astro-starter/src/lib/media/media-url.ts +112 -0
- package/templates/astro-starter/src/middleware.ts +508 -0
- package/templates/astro-starter/src/pages/404.astro +46 -0
- package/templates/astro-starter/src/pages/[...slug].astro +318 -0
- package/templates/astro-starter/src/pages/api/load-nodes.ts +40 -0
- package/templates/astro-starter/src/pages/llms-full.txt.ts +171 -0
- package/templates/astro-starter/src/pages/llms.txt.ts +113 -0
- package/templates/astro-starter/src/pages/media/[...path].ts +14 -0
- package/templates/astro-starter/src/pages/partials/footer.astro +69 -0
- package/templates/astro-starter/src/pages/partials/nav.astro +141 -0
- package/templates/astro-starter/src/styles/standard-theme.css +157 -0
- package/templates/astro-starter/src/utils/tiptap.ts +113 -0
- package/templates/astro-starter/tsconfig.json +0 -0
- package/templates/kama-worker/index.js +19 -0
- package/templates/kama-worker/server.js +104 -0
- package/wrangler.json +22 -0
- package/defaults/definitions/blocks/button.yaml +0 -15
- package/defaults/definitions/blocks/header.yaml +0 -15
- package/defaults/definitions/blocks/header_image.yaml +0 -22
- package/defaults/definitions/blocks/hero.yaml +0 -27
- package/defaults/definitions/blocks/image_card.yaml +0 -57
- package/defaults/definitions/blocks/single_image.yaml +0 -28
- package/defaults/definitions/blocks/text.yaml +0 -16
- package/defaults/definitions/channels/ecommerce.yaml +0 -24
- package/defaults/definitions/channels/website.yaml +0 -32
- package/defaults/definitions/field-sets/channel-base.yaml +0 -31
- package/defaults/definitions/field-sets/content-block.yaml +0 -46
- package/defaults/definitions/field-sets/page-base.yaml +0 -49
- package/defaults/definitions/field-sets/tree-base.yaml +0 -31
- package/defaults/definitions/node-layouts/three-column.yml +0 -41
- package/defaults/definitions/nodes/address.yaml +0 -32
- package/defaults/definitions/nodes/article.yaml +0 -43
- package/defaults/definitions/nodes/event.yaml +0 -49
- package/defaults/definitions/nodes/external-link.yaml +0 -26
- package/defaults/definitions/nodes/person.yaml +0 -37
- package/defaults/definitions/nodes/product.yaml +0 -86
- package/defaults/definitions/trees/navigation.yaml +0 -29
- package/defaults/definitions/trees/social-links.yaml +0 -9
- package/definitions/_page.yaml +0 -52
- package/dist/assets/index-TdW5F0pX.css +0 -1
- package/dist/assets/index-u9OkBauM.js +0 -1392
- package/dist/media/kama/2026/08/17a098c92bc54ea4b4441e531a34575b.jpg +0 -0
- package/dist/media/kama/2026/08/3e9ae457dd23461680cc699f42d07cf0.jpg +0 -0
- package/dist/media/kama/2026/08/8e5b3a2fd194459f9b962f8e12928bf0.jpg +0 -0
- package/dist/media/kama/2026/08/8ea5c21026cd4944bdf78c99fe2544a9.jpg +0 -0
- package/scripts/bundle-definitions.js +0 -176
- /package/{defaults → content}/definitions/block-layouts/three-column.yaml +0 -0
- /package/{defaults → content}/definitions/block-layouts/two-column.yaml +0 -0
- /package/{defaults → content}/definitions/field-sets/comments.yaml +0 -0
- /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-CErcWOwp.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": "
|
|
3
|
+
"version": "1.0.0-alpha.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -8,41 +8,38 @@
|
|
|
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
|
|
27
|
-
"dev": "
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
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
|
+
"build:server": "vite build",
|
|
31
28
|
"build:ui": "vite build --config vite.config.ui.ts",
|
|
32
|
-
"build": "npm run build:schema && npm run build:server && npm run build:ui",
|
|
29
|
+
"build": "npm run bundle:definitions && npm run build:schema && npm run build:server && npm run build:ui",
|
|
33
30
|
"build:cms": "npm run build",
|
|
34
31
|
"prepublishOnly": "npm run build",
|
|
35
|
-
"
|
|
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"
|
|
32
|
+
"serve": "wrangler dev server/index.ts"
|
|
38
33
|
},
|
|
39
34
|
"publishConfig": {
|
|
40
35
|
"access": "public"
|
|
41
36
|
},
|
|
42
37
|
"dependencies": {
|
|
43
|
-
"@
|
|
38
|
+
"@clack/prompts": "^1.8.1",
|
|
44
39
|
"@heroicons/vue": "^2.2.0",
|
|
45
40
|
"@hono/node-server": "^2.1.1",
|
|
41
|
+
"@hono/swagger-ui": "^0.6.1",
|
|
42
|
+
"@hono/zod-openapi": "^1.6.3",
|
|
46
43
|
"@tiptap/core": "^3.30.0",
|
|
47
44
|
"@tiptap/extension-subscript": "^3.30.1",
|
|
48
45
|
"@tiptap/extension-superscript": "^3.30.1",
|
|
@@ -53,7 +50,6 @@
|
|
|
53
50
|
"@tiptap/pm": "^3.30.0",
|
|
54
51
|
"@tiptap/starter-kit": "^3.30.0",
|
|
55
52
|
"@tiptap/vue-3": "^3.30.0",
|
|
56
|
-
"better-sqlite3": "^13.0.3",
|
|
57
53
|
"drizzle-orm": "^0.45.2",
|
|
58
54
|
"hono": "^4.13.2",
|
|
59
55
|
"locale-codes": "^1.3.1",
|
|
@@ -63,22 +59,24 @@
|
|
|
63
59
|
"vue": "^3.5.41",
|
|
64
60
|
"vue-advanced-cropper": "^2.8.9",
|
|
65
61
|
"vuedraggable": "^4.1.0",
|
|
66
|
-
"yaml": "^2.9.0"
|
|
62
|
+
"yaml": "^2.9.0",
|
|
63
|
+
"zod": "^4.6.5"
|
|
67
64
|
},
|
|
68
65
|
"devDependencies": {
|
|
69
66
|
"@iconify-json/heroicons": "^1.2.3",
|
|
70
67
|
"@iconify-json/lucide": "^1.2.123",
|
|
71
|
-
"@
|
|
68
|
+
"@libsql/client": "^0.14.0",
|
|
72
69
|
"@types/node": "^26.2.0",
|
|
73
70
|
"@types/sharp": "^0.31.1",
|
|
74
71
|
"@types/yaml": "^1.9.6",
|
|
75
72
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
76
73
|
"concurrently": "^10.0.4",
|
|
77
|
-
"drizzle-kit": "^0.
|
|
74
|
+
"drizzle-kit": "^0.31.11",
|
|
78
75
|
"tsx": "^4.23.12",
|
|
79
76
|
"typescript": "~5.7.2",
|
|
80
77
|
"unplugin-icons": "^23.0.1",
|
|
81
78
|
"vite": "^8.2.1",
|
|
82
|
-
"vue-tsc": "^2.2.0"
|
|
79
|
+
"vue-tsc": "^2.2.0",
|
|
80
|
+
"wrangler": "^4.134.0"
|
|
83
81
|
}
|
|
84
82
|
}
|
package/schema.sql
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
-- Kama Core System Schema
|
|
2
|
-
-- Auto-generated from server/db/
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
CREATE INDEX IF NOT EXISTS
|
|
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
|