@brightsideoy/kama 0.1.0-alpha.1 → 0.1.0-alpha.100

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 (52) hide show
  1. package/bin/cli.js +343 -0
  2. package/defaults/definitions/block-layouts/three-column.yaml +20 -0
  3. package/defaults/definitions/block-layouts/two-column.yaml +29 -0
  4. package/defaults/definitions/blocks/button.yaml +15 -0
  5. package/defaults/definitions/blocks/header.yaml +15 -0
  6. package/defaults/definitions/blocks/header_image.yaml +22 -0
  7. package/defaults/definitions/blocks/hero.yaml +27 -0
  8. package/defaults/definitions/blocks/image_card.yaml +57 -0
  9. package/defaults/definitions/blocks/single_image.yaml +28 -0
  10. package/defaults/definitions/blocks/text.yaml +16 -0
  11. package/defaults/definitions/channels/ecommerce.yaml +24 -0
  12. package/defaults/definitions/channels/website.yaml +32 -0
  13. package/defaults/definitions/field-sets/access.yaml +29 -0
  14. package/defaults/definitions/field-sets/channel-base.yaml +31 -0
  15. package/defaults/definitions/field-sets/comments.yaml +11 -0
  16. package/defaults/definitions/field-sets/content-block.yaml +46 -0
  17. package/defaults/definitions/field-sets/page-base.yaml +49 -0
  18. package/defaults/definitions/field-sets/page-meta.yaml +29 -0
  19. package/defaults/definitions/field-sets/page-social.yaml +80 -0
  20. package/defaults/definitions/field-sets/tree-base.yaml +31 -0
  21. package/defaults/definitions/field-sets/visibility.yaml +8 -0
  22. package/defaults/definitions/node-layouts/landing-page.yml +22 -0
  23. package/defaults/definitions/node-layouts/single-column.yml +38 -0
  24. package/defaults/definitions/node-layouts/three-column.yml +41 -0
  25. package/defaults/definitions/node-layouts/two-column.yml +40 -0
  26. package/defaults/definitions/nodes/address.yaml +32 -0
  27. package/defaults/definitions/nodes/article.yaml +43 -0
  28. package/defaults/definitions/nodes/event.yaml +49 -0
  29. package/defaults/definitions/nodes/external-link.yaml +26 -0
  30. package/defaults/definitions/nodes/page.yaml +65 -0
  31. package/defaults/definitions/nodes/person.yaml +37 -0
  32. package/defaults/definitions/nodes/product.yaml +86 -0
  33. package/defaults/definitions/trees/navigation.yaml +29 -0
  34. package/defaults/definitions/trees/social-links.yaml +9 -0
  35. package/definitions/_page.yaml +52 -0
  36. package/definitions/editor/button.ts +11 -0
  37. package/definitions/editor/default.ts +21 -0
  38. package/definitions/editor/header.ts +36 -0
  39. package/definitions/editor/minimal.ts +15 -0
  40. package/definitions/validators/iban.ts +12 -0
  41. package/definitions/validators/magic-word.ts +7 -0
  42. package/dist/assets/index-10GEw0KY.css +1 -0
  43. package/dist/assets/index-u699KJW2.js +1392 -0
  44. package/dist/index.html +13 -0
  45. package/dist/index.js +7922 -1022
  46. package/dist/media/kama/2026/08/17a098c92bc54ea4b4441e531a34575b.jpg +0 -0
  47. package/dist/media/kama/2026/08/3e9ae457dd23461680cc699f42d07cf0.jpg +0 -0
  48. package/dist/media/kama/2026/08/8e5b3a2fd194459f9b962f8e12928bf0.jpg +0 -0
  49. package/dist/media/kama/2026/08/8ea5c21026cd4944bdf78c99fe2544a9.jpg +0 -0
  50. package/package.json +25 -11
  51. package/schema.sql +221 -0
  52. package/scripts/bundle-definitions.js +176 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.100",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -10,23 +10,37 @@
10
10
  "import": "./dist/index.js"
11
11
  }
12
12
  },
13
+ "bin": {
14
+ "kama": "bin/cli.js",
15
+ "create-kama": "bin/cli.js"
16
+ },
13
17
  "files": [
14
- "dist"
18
+ "dist",
19
+ "defaults",
20
+ "definitions",
21
+ "bin",
22
+ "scripts",
23
+ "schema.sql"
15
24
  ],
16
25
  "scripts": {
17
- "dev": "concurrently \"vite\" \"tsx watch server/index.ts\"",
18
- "build": "vite build",
19
- "build:cms": "vue-tsc -p tsconfig.build.json || true && vite build",
20
- "build:seed-sql": "tsx src/db/generate-sql.ts",
21
- "db:seed": "npm run build:seed-sql && tsx -e \"import Database from 'better-sqlite3'; import fs from 'node:fs'; const db = new Database('data/cms.db'); db.exec(fs.readFileSync('seed.sql', 'utf8')); console.log('✅ Database seeded successfully!');\"",
22
- "d1:seed:remote": "npm run build:seed-sql && npx wrangler d1 execute YOUR_D1_NAME --remote --file=./seed.sql",
23
- "serve": "tsx server/index.ts",
24
- "prepublishOnly": "npm run build:cms"
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",
31
+ "build:ui": "vite build --config vite.config.ui.ts",
32
+ "build": "npm run build:schema && npm run build:server && npm run build:ui",
33
+ "build:cms": "npm run build",
34
+ "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"
25
38
  },
26
39
  "publishConfig": {
27
40
  "access": "public"
28
41
  },
29
42
  "dependencies": {
43
+ "@brightsideoy/kama": "^0.1.0-alpha.47",
30
44
  "@heroicons/vue": "^2.2.0",
31
45
  "@hono/node-server": "^2.1.1",
32
46
  "@tiptap/core": "^3.30.0",
@@ -67,4 +81,4 @@
67
81
  "vite": "^8.2.1",
68
82
  "vue-tsc": "^2.2.0"
69
83
  }
70
- }
84
+ }
package/schema.sql ADDED
@@ -0,0 +1,221 @@
1
+ -- Kama Core System Schema
2
+ -- Auto-generated from server/db/dynamic-sync.ts
3
+
4
+ CREATE TABLE IF NOT EXISTS "content_schemas" (
5
+ id TEXT PRIMARY KEY,
6
+ name TEXT NOT NULL,
7
+ type TEXT UNIQUE NOT NULL,
8
+ schemaJson TEXT NOT NULL,
9
+ isSystem INTEGER DEFAULT 1,
10
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
11
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS "users" (
15
+ id TEXT PRIMARY KEY,
16
+ email TEXT UNIQUE NOT NULL,
17
+ name TEXT NOT NULL,
18
+ globalRole TEXT DEFAULT 'editor',
19
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
20
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS "user_accounts" (
24
+ id TEXT PRIMARY KEY,
25
+ userId TEXT NOT NULL,
26
+ provider TEXT NOT NULL,
27
+ providerAccountId TEXT NOT NULL,
28
+ passwordHash TEXT NOT NULL,
29
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
30
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE,
31
+ UNIQUE(provider, providerAccountId)
32
+ );
33
+
34
+ CREATE TABLE IF NOT EXISTS "sessions" (
35
+ id TEXT PRIMARY KEY,
36
+ userId TEXT NOT NULL,
37
+ expiresAt TEXT NOT NULL,
38
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
39
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
40
+ );
41
+
42
+ CREATE TABLE IF NOT EXISTS "channels" (
43
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44
+ key TEXT UNIQUE NOT NULL,
45
+ label TEXT NOT NULL,
46
+ channelType TEXT,
47
+ domain TEXT,
48
+ baseUrl TEXT,
49
+ defaultLanguage TEXT DEFAULT 'en',
50
+ isHidden INTEGER NOT NULL DEFAULT 0,
51
+ dataJson TEXT,
52
+ createdById TEXT,
53
+ updatedById TEXT,
54
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
55
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
56
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
57
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
58
+ );
59
+
60
+ CREATE TABLE IF NOT EXISTS "trees" (
61
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
62
+ key TEXT UNIQUE NOT NULL,
63
+ label TEXT NOT NULL,
64
+ treeType TEXT,
65
+ channelId INTEGER,
66
+ isHidden INTEGER NOT NULL DEFAULT 0,
67
+ dataJson TEXT,
68
+ createdById TEXT,
69
+ updatedById TEXT,
70
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
71
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
72
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE SET NULL,
73
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
74
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
75
+ );
76
+
77
+ CREATE TABLE IF NOT EXISTS "channel_trees" (
78
+ channelId INTEGER NOT NULL,
79
+ treeId INTEGER NOT NULL,
80
+ key TEXT NOT NULL DEFAULT 'main',
81
+ metaJson TEXT,
82
+ PRIMARY KEY (channelId, treeId, key),
83
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE,
84
+ FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE
85
+ );
86
+
87
+ CREATE TABLE IF NOT EXISTS "content_nodes" (
88
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
89
+ contentType TEXT NOT NULL,
90
+ title TEXT NOT NULL,
91
+ slug TEXT,
92
+ language TEXT DEFAULT 'en',
93
+ isHidden INTEGER NOT NULL DEFAULT 0,
94
+ status TEXT DEFAULT 'draft',
95
+ disabledRegionsJson TEXT DEFAULT '{}',
96
+ dataJson TEXT,
97
+ draftDataJson TEXT,
98
+ publishedDataJson TEXT,
99
+ publishedAt TEXT,
100
+ createdById TEXT,
101
+ updatedById TEXT,
102
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
103
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
104
+ deletedAt TEXT,
105
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
106
+ FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
107
+ );
108
+
109
+ CREATE TABLE IF NOT EXISTS "node_channels" (
110
+ nodeId INTEGER NOT NULL,
111
+ channelId INTEGER NOT NULL,
112
+ PRIMARY KEY (nodeId, channelId),
113
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
114
+ FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE
115
+ );
116
+
117
+ CREATE TABLE IF NOT EXISTS "tree_nodes" (
118
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
119
+ treeId INTEGER NOT NULL,
120
+ contentId INTEGER NOT NULL,
121
+ parentId INTEGER,
122
+ sortOrder INTEGER DEFAULT 0,
123
+ FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE,
124
+ FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE,
125
+ FOREIGN KEY (parentId) REFERENCES tree_nodes(id) ON DELETE CASCADE
126
+ );
127
+
128
+ CREATE TABLE IF NOT EXISTS "content_blocks" (
129
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
130
+ contentId INTEGER NOT NULL,
131
+ region TEXT DEFAULT 'main',
132
+ elementType TEXT NOT NULL,
133
+ dataJson TEXT,
134
+ sortOrder INTEGER DEFAULT 0,
135
+ isDraft INTEGER NOT NULL DEFAULT 1,
136
+ FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE
137
+ );
138
+
139
+ CREATE TABLE IF NOT EXISTS "node_revisions" (
140
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
141
+ nodeId INTEGER NOT NULL,
142
+ version INTEGER NOT NULL DEFAULT 1,
143
+ snapshotJson TEXT NOT NULL,
144
+ createdById TEXT,
145
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
146
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
147
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
148
+ );
149
+
150
+ CREATE TABLE IF NOT EXISTS "assets" (
151
+ id TEXT PRIMARY KEY,
152
+ filename TEXT NOT NULL,
153
+ mimeType TEXT NOT NULL,
154
+ sizeBytes INTEGER NOT NULL,
155
+ storageKeyPrivate TEXT,
156
+ cdnUrl TEXT,
157
+ parentAssetId TEXT,
158
+ cropCoordinatesJson TEXT,
159
+ isSystemVariant INTEGER DEFAULT 0,
160
+ dataJson TEXT DEFAULT '{}',
161
+ createdById TEXT,
162
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
163
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
164
+ deletedAt TEXT,
165
+ FOREIGN KEY (parentAssetId) REFERENCES assets(id) ON DELETE SET NULL,
166
+ FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
167
+ );
168
+
169
+ CREATE TABLE IF NOT EXISTS "node_assets" (
170
+ nodeId INTEGER NOT NULL,
171
+ assetId TEXT NOT NULL,
172
+ fieldKey TEXT NOT NULL,
173
+ sortOrder INTEGER DEFAULT 0,
174
+ PRIMARY KEY (nodeId, assetId, fieldKey),
175
+ FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
176
+ FOREIGN KEY (assetId) REFERENCES assets(id) ON DELETE CASCADE
177
+ );
178
+
179
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_tree_parent ON tree_nodes(treeId, parentId);
180
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_contentId ON tree_nodes(contentId);
181
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_slug_status ON content_nodes(slug, status);
182
+ CREATE INDEX IF NOT EXISTS idx_content_blocks_node ON content_blocks(contentId, isDraft);
183
+ CREATE INDEX IF NOT EXISTS idx_node_assets_nodeId ON node_assets(nodeId);
184
+ CREATE INDEX IF NOT EXISTS idx_node_assets_assetId ON node_assets(assetId);
185
+ CREATE INDEX IF NOT EXISTS idx_content_schemas_type ON content_schemas(type);
186
+ CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(userId);
187
+ CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expiresAt);
188
+ CREATE INDEX IF NOT EXISTS idx_channels_visibility ON channels(isHidden);
189
+ CREATE INDEX IF NOT EXISTS idx_trees_visibility ON trees(isHidden);
190
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_visibility ON content_nodes(isHidden, status);
191
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes(slug);
192
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_type ON content_nodes(contentType);
193
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_deleted_updated ON content_nodes(deletedAt, updatedAt DESC);
194
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_hierarchy ON tree_nodes(treeId, parentId, sortOrder);
195
+ CREATE INDEX IF NOT EXISTS idx_tree_nodes_content ON tree_nodes(contentId, treeId);
196
+ CREATE INDEX IF NOT EXISTS idx_content_blocks_content ON content_blocks(contentId, isDraft, region, sortOrder);
197
+ CREATE INDEX IF NOT EXISTS idx_node_channels_channel ON node_channels(channelId, nodeId);
198
+ CREATE INDEX IF NOT EXISTS idx_node_channels_node ON node_channels(nodeId, channelId);
199
+ CREATE INDEX IF NOT EXISTS idx_node_assets_node_asset ON node_assets(nodeId, assetId);
200
+ 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);
202
+ CREATE INDEX IF NOT EXISTS idx_assets_system_created ON assets(isSystemVariant, createdAt DESC);
203
+ CREATE INDEX IF NOT EXISTS idx_assets_system_mime ON assets(isSystemVariant, mimeType);
204
+ CREATE INDEX IF NOT EXISTS idx_assets_deleted ON assets(deletedAt);
205
+ 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'));
@@ -0,0 +1,176 @@
1
+ // scripts/bundle-definitions.js
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import YAML from 'yaml';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ // 🎯 Package root is one directory up from scripts/
11
+ const packageRootDir = path.resolve(__dirname, '..');
12
+
13
+ // Look for definitions across package core AND consumer project overrides
14
+ function getDefinitionFiles(subDir) {
15
+ const fileMap = new Map();
16
+
17
+ const possibleDirs = [
18
+ // 1. Core package defaults (from inside node_modules or package source)
19
+ path.resolve(packageRootDir, "defaults/definitions", subDir),
20
+ path.resolve(packageRootDir, "definitions", subDir),
21
+
22
+ // 2. Fallbacks relative to node_modules if running from workspace root
23
+ path.resolve(process.cwd(), "node_modules/@brightsideoy/kama/defaults/definitions", subDir),
24
+ path.resolve(process.cwd(), "node_modules/@brightsideoy/kama/definitions", subDir),
25
+
26
+ // 3. Consumer project local overrides (highest priority — overwrites defaults)
27
+ path.resolve(process.cwd(), "defaults/definitions", subDir),
28
+ path.resolve(process.cwd(), "definitions", subDir),
29
+ path.resolve(process.cwd(), "schemas", subDir),
30
+ path.resolve(process.cwd(), "server/schemas", subDir),
31
+ ];
32
+
33
+ for (const dirPath of possibleDirs) {
34
+ if (!fs.existsSync(dirPath)) continue;
35
+
36
+ const walkDir = (currentDir, relativePath = "") => {
37
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
38
+ for (const entry of entries) {
39
+ const entryRelPath = path.join(relativePath, entry.name);
40
+ const fullPath = path.join(currentDir, entry.name);
41
+
42
+ if (entry.isDirectory()) {
43
+ walkDir(fullPath, entryRelPath);
44
+ } else if (
45
+ entry.name.endsWith(".yaml") ||
46
+ entry.name.endsWith(".yml") ||
47
+ entry.name.endsWith(".json")
48
+ ) {
49
+ // Overwrite earlier entries so local consumer files override package defaults
50
+ fileMap.set(entryRelPath, fullPath);
51
+ }
52
+ }
53
+ };
54
+
55
+ walkDir(dirPath);
56
+ }
57
+
58
+ return Array.from(fileMap.entries()).map(([fileName, filePath]) => ({ fileName, filePath }));
59
+ }
60
+
61
+ function parseFile(filePath) {
62
+ const content = fs.readFileSync(filePath, 'utf-8');
63
+ return filePath.endsWith('.json') ? JSON.parse(content) : YAML.parse(content);
64
+ }
65
+
66
+ function loadDir(...subDirs) {
67
+ const res = {};
68
+ for (const subDir of subDirs) {
69
+ const files = getDefinitionFiles(subDir);
70
+ for (const { fileName, filePath } of files) {
71
+ const key = path.basename(fileName, path.extname(fileName));
72
+ try {
73
+ res[key] = parseFile(filePath);
74
+ } catch (e) {
75
+ console.warn(`Failed to parse ${filePath}:`, e);
76
+ }
77
+ }
78
+ }
79
+ return res;
80
+ }
81
+
82
+ function loadSingle(subDirAndFile) {
83
+ const files = getDefinitionFiles(path.dirname(subDirAndFile));
84
+ const targetBase = path.basename(subDirAndFile);
85
+ const matched = files.find(
86
+ f => path.basename(f.fileName) === targetBase ||
87
+ path.basename(f.fileName) === targetBase.replace(/\.yaml$/, '.yml') ||
88
+ path.basename(f.fileName) === targetBase.replace(/\.yaml$/, '.json')
89
+ );
90
+ if (!matched) return null;
91
+ try {
92
+ return parseFile(matched.filePath);
93
+ } catch (e) {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ // 🎯 MAP DEFINITIONS
99
+ const rawBlocks = loadDir("blocks", "elements");
100
+ const rawBlockLayouts = loadDir("block-layouts");
101
+ const rawNodeLayouts = loadDir("node-layouts", "layouts");
102
+
103
+ const bundled = {
104
+ rawNodes: loadDir("nodes"),
105
+ rawTrees: loadDir("trees"),
106
+ rawChannels: loadDir("channels"),
107
+ rawBlocks,
108
+ rawElements: rawBlocks,
109
+ rawBlockLayouts,
110
+ rawNodeLayouts,
111
+ rawLayouts: { ...rawNodeLayouts, ...rawBlockLayouts },
112
+ rawFieldSets: loadDir("field-sets"),
113
+ rawTemplates: loadDir("templates"),
114
+ nodeTemplate: loadSingle("templates/node.yaml") || { fields: {} },
115
+ treeTemplate: loadSingle("templates/tree.yaml") || { fields: {} },
116
+ channelTemplate: loadSingle("channels/channel.yaml") || loadSingle("templates/channel.yaml") || { fields: {} },
117
+ userTemplate: loadSingle("users/user.yaml") || loadSingle("templates/user.yaml") || { fields: {} },
118
+ };
119
+
120
+ const outputContent = `// Auto-generated pre-bundled definitions for Kama Core Engine
121
+ export const BUNDLED_DEFINITIONS = ${JSON.stringify(bundled, null, 2)};
122
+ `;
123
+
124
+ // 🎯 Write to BOTH package dist/server locations so imports resolve cleanly
125
+ const targetDirs = [
126
+ path.resolve(packageRootDir, 'server/db'),
127
+ path.resolve(process.cwd(), 'node_modules/@brightsideoy/kama/server/db'),
128
+ path.resolve(process.cwd(), 'node_modules/@brightsideoy/kama/dist'),
129
+ path.resolve(process.cwd(), 'server/db'),
130
+ ];
131
+
132
+ for (const dir of targetDirs) {
133
+ if (fs.existsSync(dir)) {
134
+ const outputPath = path.resolve(dir, 'bundled-definitions.ts');
135
+ fs.writeFileSync(outputPath, outputContent, 'utf-8');
136
+ // Also write .js version for built packages
137
+ const jsOutputPath = path.resolve(dir, 'bundled-definitions.js');
138
+ fs.writeFileSync(jsOutputPath, `export const BUNDLED_DEFINITIONS = ${JSON.stringify(bundled, null, 2)};\n`, 'utf-8');
139
+ }
140
+ }
141
+
142
+ console.log(`✅ Definitions bundled successfully:
143
+ • Nodes: ${Object.keys(bundled.rawNodes).length} (${Object.keys(bundled.rawNodes).join(', ')})
144
+ • Blocks: ${Object.keys(bundled.rawBlocks).length} (${Object.keys(bundled.rawBlocks).join(', ')})
145
+ • Channels: ${Object.keys(bundled.rawChannels).length} (${Object.keys(bundled.rawChannels).join(', ')})
146
+ • Trees: ${Object.keys(bundled.rawTrees).length} (${Object.keys(bundled.rawTrees).join(', ')})`);
147
+
148
+ // Auto-generate SQL Indexes
149
+ const indexQueries = new Set([
150
+ "-- Native Columns",
151
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes (slug);",
152
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_contentType ON content_nodes (contentType);",
153
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_updatedAt ON content_nodes (updatedAt);\n",
154
+ "-- JSON Custom Fields"
155
+ ]);
156
+
157
+ function extractIndexes(obj) {
158
+ if (typeof obj !== 'object' || obj === null) return;
159
+ for (const [key, value] of Object.entries(obj)) {
160
+ if (typeof value === 'object' && value !== null) {
161
+ if (value.index === true && key !== "id" && key !== "slug" && key !== "contentType") {
162
+ indexQueries.add(`CREATE INDEX IF NOT EXISTS idx_content_nodes_${key} ON content_nodes (json_extract(dataJson, '$.${key}'));`);
163
+ }
164
+ extractIndexes(value);
165
+ }
166
+ }
167
+ }
168
+
169
+ extractIndexes(bundled.rawNodes);
170
+
171
+ const sqlContent = Array.from(indexQueries).join('\n');
172
+ const schemaSqlPath = path.resolve(process.cwd(), 'schema.sql');
173
+
174
+ if (fs.existsSync(schemaSqlPath)) {
175
+ fs.appendFileSync(schemaSqlPath, '\n\n' + sqlContent + '\n', 'utf-8');
176
+ }