@brightsideoy/kama 0.1.0-alpha.121 → 0.1.0-alpha.123

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/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from 'node:child_process';
3
- import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, cpSync, unlinkSync, statSync } from 'node:fs';
3
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, cpSync, unlinkSync, statSync, appendFileSync } from 'node:fs';
4
4
  import { join, dirname, basename, relative, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import * as readline from 'node:readline/promises';
@@ -24,11 +24,39 @@ const rl = readline.createInterface({
24
24
  output: process.stdout,
25
25
  });
26
26
 
27
+ function showHelp() {
28
+ console.log('\nšŸ”„ --- KAMA ENGINE CLI --- šŸ”„\n');
29
+ console.log('Usage: npx kama <command> [options]\n');
30
+ console.log('Commands:');
31
+ console.log(' init Kickstart a new Monorepo (CMS Worker + Optional Astro Site)');
32
+ console.log(' add site Add a new Astro Frontend Site to the current workspace (sites/)');
33
+ console.log(' bundle Bundle YAML schemas into a static module for Edge deployment');
34
+ console.log(' promote Promote the current Local workspace to Cloudflare Edge');
35
+ console.log(' help, -h Show this help message\n');
36
+
37
+ console.log('Workspace Scripts (Run from project root):');
38
+ console.log(' npm update Update to latest KAMA version');
39
+ console.log(' npm run dev Boot up the CMS backend and Astro frontend concurrently');
40
+ console.log(' npm run deploy:cms Deploy the backend worker and database to Cloudflare');
41
+ console.log(' npm run deploy:site Deploy the Astro frontend site to Cloudflare\n');
42
+
43
+ console.log('Examples:');
44
+ console.log(' npx kama init');
45
+ console.log(' npx kama add site');
46
+ console.log(' npx kama bundle\n');
47
+ }
48
+
27
49
  async function main() {
28
50
  const args = process.argv.slice(2);
29
51
  const command = args[0]?.toLowerCase();
30
52
  const subCommand = args[1]?.toLowerCase();
31
53
 
54
+ if (command === 'help' || command === '--help' || command === '-h') {
55
+ showHelp();
56
+ rl.close();
57
+ process.exit(0);
58
+ }
59
+
32
60
  console.log('\nšŸ”„ --- KAMA ENGINE CLI --- šŸ”„\n');
33
61
 
34
62
  if (command === 'init') {
@@ -45,10 +73,13 @@ async function main() {
45
73
  console.log(' 2) npx kama add site - Add new Astro Site to current workspace (sites/)');
46
74
  console.log(' 3) npx kama bundle - Bundle YAML schemas for Edge deployment');
47
75
  console.log(' 4) npx kama promote - Promote current Local workspace to Cloudflare Edge');
76
+ console.log(' 5) Help - Show all commands and usage');
48
77
 
49
- const choice = await rl.question('\nSelect choice [1/2/3/4]: ');
78
+ const choice = await rl.question('\nSelect choice [1/2/3/4/5]: ');
50
79
 
51
- if (choice.trim() === '4') {
80
+ if (choice.trim() === '5') {
81
+ showHelp();
82
+ } else if (choice.trim() === '4') {
52
83
  await promoteLocalToCloudflare();
53
84
  } else if (choice.trim() === '3') {
54
85
  await bundleWorkspaceSchemas();
@@ -88,8 +119,14 @@ async function createFullWorkspace() {
88
119
  const createSiteInput = await rl.question('\nšŸš€ Do you want to scaffold an initial Astro frontend site? (y/n) [y]: ');
89
120
  const shouldCreateSite = createSiteInput.trim().toLowerCase() !== 'n';
90
121
 
91
- let cmsDomain = 'localhost:8787';
122
+ let siteFolderName = 'kama-starter';
92
123
  let siteDomain = 'localhost:4321';
124
+ let cmsDomain = 'localhost:8787';
125
+
126
+ if (shouldCreateSite) {
127
+ // šŸŽÆ FIX: Default to 'kama-starter'
128
+ siteFolderName = (await rl.question('Astro package/folder name (e.g., my-website) [kama-starter]: ')).trim() || 'kama-starter';
129
+ }
93
130
 
94
131
  if (isCloudflare) {
95
132
  cmsDomain = (await rl.question('\nCMS Admin Domain (e.g., admin.t3cloud.eu): ')) || 'admin.t3cloud.eu';
@@ -105,8 +142,6 @@ async function createFullWorkspace() {
105
142
  let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
106
143
  const bucketName = `${projName}-media-prod`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
107
144
 
108
- const siteFolderName = isCloudflare ? siteDomain.split('.')[0] : 'web';
109
-
110
145
  if (!isCurrentDir) {
111
146
  console.log(`\nšŸ“ Creating workspace folder at ./${projName}...`);
112
147
  mkdirSync(rootDir, { recursive: true });
@@ -250,7 +285,8 @@ kama-worker/workspace-schemas.js
250
285
  const cmsDir = join(rootDir, 'kama-worker');
251
286
  mkdirSync(cmsDir, { recursive: true });
252
287
 
253
- // šŸŽÆ FIX: Clones frozen Cloudflare env and safely injects BUNDLED_DEFINITIONS
288
+ writeFileSync(join(cmsDir, 'seed.sql'), '-- Kama Auto-Generated Seed File\nSELECT 1;\n');
289
+
254
290
  const cmsWorkerIndexContent = `import kamaWorker from '@brightsideoy/kama';
255
291
  import customSchemas from './workspace-schemas.js';
256
292
 
@@ -283,8 +319,10 @@ export default {
283
319
  scripts: {
284
320
  "schema:local": `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql > /dev/null 2>&1`,
285
321
  "schema:remote": `wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`,
286
- "dev": "npm run schema:local && wrangler dev",
287
- "deploy": "npm run schema:remote && wrangler deploy"
322
+ "seed:local": `wrangler d1 execute ${dbName} --local --file=seed.sql > /dev/null 2>&1`,
323
+ "seed:remote": `wrangler d1 execute ${dbName} --remote --file=seed.sql`,
324
+ "dev": "npm run schema:local && npm run seed:local && wrangler dev",
325
+ "deploy": "npm run schema:remote && npm run seed:remote && wrangler deploy"
288
326
  },
289
327
  dependencies: {
290
328
  '@brightsideoy/kama': currentKamaVersion,
@@ -392,7 +430,8 @@ async function createNewSiteOnly() {
392
430
  const envChoice = await rl.question('\nChoice [1/2]: ');
393
431
  const isCloudflare = envChoice.trim() === '2';
394
432
 
395
- const siteFolder = await rl.question('Site folder name (e.g., my-new-site): ');
433
+ // šŸŽÆ FIX: Default to 'kama-starter'
434
+ const siteFolder = (await rl.question('Astro package/folder name (e.g., my-new-site) [kama-starter]: ')).trim() || 'kama-starter';
396
435
 
397
436
  let siteDomain = 'localhost:4321';
398
437
  let cmsFullUrl = 'http://localhost:8787';
@@ -405,7 +444,9 @@ async function createNewSiteOnly() {
405
444
 
406
445
  await generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, targetWorkerName, isCloudflare);
407
446
 
408
- console.log(`\n✨ Site created in sites/${siteFolder}! Run 'npm install' at workspace root.`);
447
+ console.log(`\n✨ Site created in sites/${siteFolder}!`);
448
+ console.log(`šŸ“¦ Run 'npm install' at workspace root.`);
449
+ console.log(`🌱 Run 'npm run dev' to boot up and seed the new site data.`);
409
450
  }
410
451
 
411
452
  /**
@@ -588,12 +629,58 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
588
629
  writeFileSync(wranglerPath, wranglerStr);
589
630
  }
590
631
 
632
+ // šŸŽÆ FIX: Smart capitalization for dynamic site labels
633
+ const siteLabel = siteFolder.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
634
+ const safeSiteLabel = siteLabel.replace(/'/g, "''");
635
+
636
+ const channelKey = siteFolder.replace(/[^a-z0-9_-]/gi, '-').toLowerCase();
637
+ const treeKey = `${channelKey}-nav`;
638
+
639
+ // šŸŽÆ FIX: Inject `safeSiteLabel` into the logoText, titleSuffix, and titles!
640
+ const seedSql = `
641
+ -- Seed data for site: ${safeSiteLabel}
642
+ INSERT OR IGNORE INTO channels (key, label, channelType, defaultLanguage, isHidden, dataJson)
643
+ VALUES ('${channelKey}', '${safeSiteLabel}', 'website', 'en', 0, '{}');
644
+
645
+ INSERT OR IGNORE INTO trees (key, label, treeType, channelId, isHidden, dataJson)
646
+ VALUES ('${treeKey}', 'Main Navigation', 'main-navigation', (SELECT id FROM channels WHERE key = '${channelKey}'), 0, '{}');
647
+
648
+ INSERT OR IGNORE INTO channel_trees (channelId, treeId, key)
649
+ VALUES ((SELECT id FROM channels WHERE key = '${channelKey}'), (SELECT id FROM trees WHERE key = '${treeKey}'), 'main');
650
+
651
+ INSERT INTO content_nodes (contentType, title, slug, language, status, dataJson)
652
+ SELECT 'page', 'Welcome to ${safeSiteLabel}', 'home', 'en', 'published', '{"titleSuffix":"– ${safeSiteLabel}","layoutId":"single-column","subpageLayoutId":"two-column","logoText":"${safeSiteLabel}"}'
653
+ WHERE NOT EXISTS (SELECT 1 FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}');
654
+
655
+ INSERT OR IGNORE INTO node_channels (nodeId, channelId)
656
+ VALUES (
657
+ (SELECT id FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}' ORDER BY id DESC LIMIT 1),
658
+ (SELECT id FROM channels WHERE key = '${channelKey}')
659
+ );
660
+
661
+ INSERT OR IGNORE INTO tree_nodes (treeId, contentId, parentId, sortOrder)
662
+ VALUES (
663
+ (SELECT id FROM trees WHERE key = '${treeKey}'),
664
+ (SELECT id FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}' ORDER BY id DESC LIMIT 1),
665
+ NULL,
666
+ 0
667
+ );
668
+ `;
669
+
670
+ const workerDir = join(rootDir, 'kama-worker');
671
+ const seedPath = join(workerDir, 'seed.sql');
672
+ if (existsSync(workerDir)) {
673
+ appendFileSync(seedPath, seedSql);
674
+ }
675
+
676
+ // šŸŽÆ FIX: Expose the title-cased name to Astro ENV vars as well
591
677
  const envContent = [
592
678
  `PUBLIC_CMS_URL="${cmsFullUrl}"`,
593
679
  `PUBLIC_API_URL="${cmsFullUrl}"`,
594
- `PUBLIC_CHANNEL_ID="website"`,
595
- `PUBLIC_TREE_ID="main-navigation"`,
596
- `PUBLIC_SITE_NAME="KAMA Starter"`
680
+ `PUBLIC_CHANNEL_ID="${channelKey}"`,
681
+ `PUBLIC_TREE_ID="${treeKey}"`,
682
+ `PUBLIC_HOME_SLUG="home"`,
683
+ `PUBLIC_SITE_NAME="${siteLabel.replace(/"/g, '\\"')}"`
597
684
  ].join('\n') + '\n';
598
685
 
599
686
  writeFileSync(join(siteDir, '.env'), envContent);
@@ -11,6 +11,30 @@ tabs:
11
11
  fields:
12
12
  # šŸŽÆ 1. Natively merges channel-base fields into this tab
13
13
  fieldSet: channel-base
14
+ design:
15
+ label: "Design"
16
+ fields:
17
+ favicon:
18
+ label: "Favicon"
19
+ type: image
20
+ maxPreviewWidth: 200px
21
+ multiple: true
22
+ allowedTypes: ["image/png", "image/x-icon", "image/svg"]
23
+ public: true
24
+ logoText:
25
+ label: Logo Text
26
+ type: text
27
+ required: false
28
+ public: true
29
+
30
+ logoImage:
31
+ label: "Logo Image"
32
+ type: image
33
+ maxPreviewWidth: 300px
34
+ multiple: false
35
+ allowedTypes: ["image/svg"]
36
+ public: true
37
+
14
38
  meta:
15
39
  label: "SEO"
16
40
  fields:
@@ -21,14 +21,11 @@ regions:
21
21
  allowedTypes: ["image/*"]
22
22
  crops:
23
23
  standard:
24
- label: "Desktop Header (4:1)"
25
- aspectRatio: 4.0
24
+ label: "Desktop Header (3:1)"
25
+ aspectRatio: 3.0
26
26
  tablet:
27
27
  label: "Tablet Header (2:1)"
28
28
  aspectRatio: 2.0
29
- mobile:
30
- label: "Mobile Header (4:3)"
31
- aspectRatio: 1.33
32
29
  public: true
33
30
 
34
31
  content:
@@ -22,14 +22,11 @@ regions:
22
22
  allowedTypes: ["image/*"]
23
23
  crops:
24
24
  standard:
25
- label: "Desktop Header (4:1)"
26
- aspectRatio: 4.0
25
+ label: "Desktop Header (3:1)"
26
+ aspectRatio: 3.0
27
27
  tablet:
28
28
  label: "Tablet Header (2:1)"
29
29
  aspectRatio: 2.0
30
- mobile:
31
- label: "Mobile Header (4:3)"
32
- aspectRatio: 1.33
33
30
  public: true
34
31
  content: # šŸ‘ˆ Changed 'main' to 'content'
35
32
  label: Content
@@ -16,7 +16,22 @@ tabs:
16
16
  # šŸŽÆ 1. Include FieldSet Bundle
17
17
  - fieldSet: page-base
18
18
 
19
- # šŸŽÆ 3. Inline Fields
19
+ - logoText:
20
+ label: Logo Text
21
+ type: text
22
+ required: false
23
+ public: true
24
+ inherits: logoText
25
+
26
+ - logoImage:
27
+ label: "Logo Image"
28
+ type: image
29
+ maxPreviewWidth: 300px
30
+ multiple: false
31
+ allowedTypes: ["image/svg"]
32
+ public: true
33
+ inherits: logoImage
34
+
20
35
  - layoutId:
21
36
  label: Page Layout
22
37
  type: layout-select
@@ -26,7 +41,6 @@ tabs:
26
41
  allowedLayouts:
27
42
  - two-column
28
43
  - single-column
29
- - three-column
30
44
  - landing-page
31
45
  options:
32
46
  - { label: "Inherit Layout", value: "inherit" }
@@ -39,7 +53,6 @@ tabs:
39
53
  inheritsFromParent: true
40
54
  allowedLayouts:
41
55
  - two-column
42
- - three-column
43
56
  - single-column
44
57
  - landing-page
45
58
  options: