@brightsideoy/kama 0.1.0-alpha.36 → 0.1.0-alpha.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightsideoy/kama",
3
- "version": "0.1.0-alpha.36",
3
+ "version": "0.1.0-alpha.39",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -17,16 +17,18 @@
17
17
  "files": [
18
18
  "dist",
19
19
  "bin",
20
+ "scripts",
20
21
  "schema.sql"
21
22
  ],
22
23
  "scripts": {
23
24
  "dev": "concurrently \"vite\" \"tsx watch server/dev.ts\"",
24
- "build:server": "vite build",
25
+ "bundle:definitions": "node scripts/bundle-definitions.js",
26
+ "build:schema": "tsx src/db/generate-schema.ts",
27
+ "build:server": "npm run bundle:definitions && vite build",
25
28
  "build:ui": "vite build --config vite.config.ui.ts",
26
- "build": "npm run build:server && npm run build:ui",
29
+ "build": "npm run build:schema && npm run build:server && npm run build:ui",
27
30
  "build:cms": "npm run build",
28
- "build:schema": "tsx src/db/generate-schema.ts",
29
- "prepublishOnly": "npm run build:schema && npm run build",
31
+ "prepublishOnly": "npm run build",
30
32
  "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!');\"",
31
33
  "d1:schema:remote": "npm run build:schema && npx wrangler d1 execute YOUR_D1_NAME --remote --file=./schema.sql",
32
34
  "serve": "tsx server/dev.ts"
package/schema.sql CHANGED
@@ -1,6 +1,16 @@
1
1
  -- Kama Core System Schema
2
2
  -- Auto-generated from server/db/dynamic-sync.ts
3
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
+
4
14
  CREATE TABLE IF NOT EXISTS "users" (
5
15
  id TEXT PRIMARY KEY,
6
16
  email TEXT UNIQUE NOT NULL,
@@ -166,6 +176,7 @@ CREATE TABLE IF NOT EXISTS "node_assets" (
166
176
  FOREIGN KEY (assetId) REFERENCES assets(id) ON DELETE CASCADE
167
177
  );
168
178
 
179
+ CREATE INDEX IF NOT EXISTS idx_content_schemas_type ON content_schemas(type);
169
180
  CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(userId);
170
181
  CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expiresAt);
171
182
  CREATE INDEX IF NOT EXISTS idx_channels_visibility ON channels(isHidden);
@@ -191,3 +202,14 @@ CREATE INDEX IF NOT EXISTS idx_assets_parent ON assets(parentAssetId);
191
202
  INSERT OR IGNORE INTO channels (id, key, label, channelType, defaultLanguage, isHidden, dataJson) VALUES (1, 'default-channel', 'Default Channel', 'website', 'en', 0, '{}');
192
203
  INSERT OR IGNORE INTO trees (id, key, label, treeType, channelId, isHidden, dataJson) VALUES (1, 'default-tree', 'Default Navigation', 'navigation', 1, 0, '{}');
193
204
  INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES (1, 1, 'main');
205
+
206
+
207
+ -- Native Columns
208
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes (slug);
209
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_contentType ON content_nodes (contentType);
210
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_updatedAt ON content_nodes (updatedAt);
211
+
212
+ -- JSON Custom Fields
213
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_articleDate ON content_nodes (json_extract(dataJson, '$.articleDate'));
214
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_startTime ON content_nodes (json_extract(dataJson, '$.startTime'));
215
+ CREATE INDEX IF NOT EXISTS idx_content_nodes_endTime ON content_nodes (json_extract(dataJson, '$.endTime'));
@@ -0,0 +1,152 @@
1
+ // scripts/bundle-definitions.js
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import YAML from 'yaml';
5
+
6
+ // Look for definitions across package core folders
7
+ function getDefinitionFiles(subDir) {
8
+ const fileMap = new Map();
9
+ const possibleDirs = [
10
+ path.resolve(process.cwd(), "defaults/definitions", subDir),
11
+ path.resolve(process.cwd(), "definitions", subDir),
12
+ path.resolve(process.cwd(), "schemas", subDir),
13
+ path.resolve(process.cwd(), "server/schemas", subDir),
14
+ ];
15
+
16
+ for (const dirPath of possibleDirs) {
17
+ if (!fs.existsSync(dirPath)) continue;
18
+
19
+ const walkDir = (currentDir, relativePath = "") => {
20
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
21
+ for (const entry of entries) {
22
+ const entryRelPath = path.join(relativePath, entry.name);
23
+ const fullPath = path.join(currentDir, entry.name);
24
+
25
+ if (entry.isDirectory()) {
26
+ walkDir(fullPath, entryRelPath);
27
+ } else if (
28
+ entry.name.endsWith(".yaml") ||
29
+ entry.name.endsWith(".yml") ||
30
+ entry.name.endsWith(".json")
31
+ ) {
32
+ fileMap.set(entryRelPath, fullPath);
33
+ }
34
+ }
35
+ };
36
+
37
+ walkDir(dirPath);
38
+ }
39
+
40
+ return Array.from(fileMap.entries()).map(([fileName, filePath]) => ({ fileName, filePath }));
41
+ }
42
+
43
+ function parseFile(filePath) {
44
+ const content = fs.readFileSync(filePath, 'utf-8');
45
+ return filePath.endsWith('.json') ? JSON.parse(content) : YAML.parse(content);
46
+ }
47
+
48
+ function loadDir(...subDirs) {
49
+ const res = {};
50
+ for (const subDir of subDirs) {
51
+ const files = getDefinitionFiles(subDir);
52
+ for (const { fileName, filePath } of files) {
53
+ const key = path.basename(fileName, path.extname(fileName));
54
+ try {
55
+ res[key] = parseFile(filePath);
56
+ } catch (e) {
57
+ console.warn(`Failed to parse ${filePath}:`, e);
58
+ }
59
+ }
60
+ }
61
+ return res;
62
+ }
63
+
64
+ function loadSingle(subDirAndFile) {
65
+ const files = getDefinitionFiles(path.dirname(subDirAndFile));
66
+ const targetBase = path.basename(subDirAndFile);
67
+ const matched = files.find(
68
+ f => path.basename(f.fileName) === targetBase ||
69
+ path.basename(f.fileName) === targetBase.replace(/\.yaml$/, '.yml') ||
70
+ path.basename(f.fileName) === targetBase.replace(/\.yaml$/, '.json')
71
+ );
72
+ if (!matched) return null;
73
+ try {
74
+ return parseFile(matched.filePath);
75
+ } catch (e) {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ // 🎯 MAP DEFINITIONS
81
+ const rawBlocks = loadDir("blocks", "elements");
82
+ const rawBlockLayouts = loadDir("block-layouts");
83
+ const rawNodeLayouts = loadDir("node-layouts", "layouts");
84
+
85
+ const bundled = {
86
+ rawNodes: loadDir("nodes"),
87
+ rawTrees: loadDir("trees"),
88
+ rawChannels: loadDir("channels"),
89
+ rawBlocks,
90
+ rawElements: rawBlocks,
91
+ rawBlockLayouts,
92
+ rawNodeLayouts,
93
+ rawLayouts: { ...rawNodeLayouts, ...rawBlockLayouts },
94
+ rawFieldSets: loadDir("field-sets"),
95
+ rawTemplates: loadDir("templates"),
96
+ nodeTemplate: loadSingle("templates/node.yaml") || { fields: {} },
97
+ treeTemplate: loadSingle("templates/tree.yaml") || { fields: {} },
98
+ channelTemplate: loadSingle("channels/channel.yaml") || loadSingle("templates/channel.yaml") || { fields: {} },
99
+ userTemplate: loadSingle("users/user.yaml") || loadSingle("templates/user.yaml") || { fields: {} },
100
+ };
101
+
102
+ const outputContent = `// Auto-generated pre-bundled definitions for Kama Core Engine
103
+ export const BUNDLED_DEFINITIONS = ${JSON.stringify(bundled, null, 2)};
104
+ `;
105
+
106
+ const outputDir = path.resolve(process.cwd(), 'server/db');
107
+ if (!fs.existsSync(outputDir)) {
108
+ fs.mkdirSync(outputDir, { recursive: true });
109
+ }
110
+
111
+ const outputPath = path.resolve(outputDir, 'bundled-definitions.ts');
112
+ fs.writeFileSync(outputPath, outputContent, 'utf-8');
113
+
114
+ console.log(`✅ Definitions bundled successfully into Kama Core:
115
+ • Nodes: ${Object.keys(bundled.rawNodes).length} (${Object.keys(bundled.rawNodes).join(', ')})
116
+ • Blocks: ${Object.keys(bundled.rawBlocks).length}
117
+ • Channels: ${Object.keys(bundled.rawChannels).length}
118
+ ↳ Saved into ${outputPath}`);
119
+
120
+ // ==========================================
121
+ // AUTO-GENERATE SQL INDEXES FROM SCHEMA
122
+ // ==========================================
123
+ const indexQueries = new Set([
124
+ "-- Native Columns",
125
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes (slug);",
126
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_contentType ON content_nodes (contentType);",
127
+ "CREATE INDEX IF NOT EXISTS idx_content_nodes_updatedAt ON content_nodes (updatedAt);\n",
128
+ "-- JSON Custom Fields"
129
+ ]);
130
+
131
+ function extractIndexes(obj) {
132
+ if (typeof obj !== 'object' || obj === null) return;
133
+
134
+ for (const [key, value] of Object.entries(obj)) {
135
+ if (typeof value === 'object' && value !== null) {
136
+ if (value.index === true && key !== "id" && key !== "slug" && key !== "contentType") {
137
+ indexQueries.add(`CREATE INDEX IF NOT EXISTS idx_content_nodes_${key} ON content_nodes (json_extract(dataJson, '$.${key}'));`);
138
+ }
139
+ extractIndexes(value);
140
+ }
141
+ }
142
+ }
143
+
144
+ extractIndexes(bundled.rawNodes);
145
+
146
+ const sqlContent = Array.from(indexQueries).join('\n');
147
+ const schemaSqlPath = path.resolve(process.cwd(), 'schema.sql');
148
+
149
+ if (fs.existsSync(schemaSqlPath)) {
150
+ fs.appendFileSync(schemaSqlPath, '\n\n' + sqlContent + '\n', 'utf-8');
151
+ console.log(`✅ Appended JSON auto-indexes directly to schema.sql`);
152
+ }