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

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,32 @@ 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
+ console.log('Examples:');
37
+ console.log(' npx kama init');
38
+ console.log(' npx kama add site');
39
+ console.log(' npx kama bundle\n');
40
+ }
41
+
27
42
  async function main() {
28
43
  const args = process.argv.slice(2);
29
44
  const command = args[0]?.toLowerCase();
30
45
  const subCommand = args[1]?.toLowerCase();
31
46
 
47
+ if (command === 'help' || command === '--help' || command === '-h') {
48
+ showHelp();
49
+ rl.close();
50
+ process.exit(0);
51
+ }
52
+
32
53
  console.log('\nšŸ”„ --- KAMA ENGINE CLI --- šŸ”„\n');
33
54
 
34
55
  if (command === 'init') {
@@ -45,10 +66,13 @@ async function main() {
45
66
  console.log(' 2) npx kama add site - Add new Astro Site to current workspace (sites/)');
46
67
  console.log(' 3) npx kama bundle - Bundle YAML schemas for Edge deployment');
47
68
  console.log(' 4) npx kama promote - Promote current Local workspace to Cloudflare Edge');
69
+ console.log(' 5) Help - Show all commands and usage');
48
70
 
49
- const choice = await rl.question('\nSelect choice [1/2/3/4]: ');
71
+ const choice = await rl.question('\nSelect choice [1/2/3/4/5]: ');
50
72
 
51
- if (choice.trim() === '4') {
73
+ if (choice.trim() === '5') {
74
+ showHelp();
75
+ } else if (choice.trim() === '4') {
52
76
  await promoteLocalToCloudflare();
53
77
  } else if (choice.trim() === '3') {
54
78
  await bundleWorkspaceSchemas();
@@ -88,8 +112,14 @@ async function createFullWorkspace() {
88
112
  const createSiteInput = await rl.question('\nšŸš€ Do you want to scaffold an initial Astro frontend site? (y/n) [y]: ');
89
113
  const shouldCreateSite = createSiteInput.trim().toLowerCase() !== 'n';
90
114
 
91
- let cmsDomain = 'localhost:8787';
115
+ let siteFolderName = 'kama-starter';
92
116
  let siteDomain = 'localhost:4321';
117
+ let cmsDomain = 'localhost:8787';
118
+
119
+ if (shouldCreateSite) {
120
+ // šŸŽÆ FIX: Default to 'kama-starter'
121
+ siteFolderName = (await rl.question('Astro package/folder name (e.g., my-website) [kama-starter]: ')).trim() || 'kama-starter';
122
+ }
93
123
 
94
124
  if (isCloudflare) {
95
125
  cmsDomain = (await rl.question('\nCMS Admin Domain (e.g., admin.t3cloud.eu): ')) || 'admin.t3cloud.eu';
@@ -105,8 +135,6 @@ async function createFullWorkspace() {
105
135
  let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
106
136
  const bucketName = `${projName}-media-prod`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
107
137
 
108
- const siteFolderName = isCloudflare ? siteDomain.split('.')[0] : 'web';
109
-
110
138
  if (!isCurrentDir) {
111
139
  console.log(`\nšŸ“ Creating workspace folder at ./${projName}...`);
112
140
  mkdirSync(rootDir, { recursive: true });
@@ -250,7 +278,8 @@ kama-worker/workspace-schemas.js
250
278
  const cmsDir = join(rootDir, 'kama-worker');
251
279
  mkdirSync(cmsDir, { recursive: true });
252
280
 
253
- // šŸŽÆ FIX: Clones frozen Cloudflare env and safely injects BUNDLED_DEFINITIONS
281
+ writeFileSync(join(cmsDir, 'seed.sql'), '-- Kama Auto-Generated Seed File\nSELECT 1;\n');
282
+
254
283
  const cmsWorkerIndexContent = `import kamaWorker from '@brightsideoy/kama';
255
284
  import customSchemas from './workspace-schemas.js';
256
285
 
@@ -283,8 +312,10 @@ export default {
283
312
  scripts: {
284
313
  "schema:local": `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql > /dev/null 2>&1`,
285
314
  "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"
315
+ "seed:local": `wrangler d1 execute ${dbName} --local --file=seed.sql > /dev/null 2>&1`,
316
+ "seed:remote": `wrangler d1 execute ${dbName} --remote --file=seed.sql`,
317
+ "dev": "npm run schema:local && npm run seed:local && wrangler dev",
318
+ "deploy": "npm run schema:remote && npm run seed:remote && wrangler deploy"
288
319
  },
289
320
  dependencies: {
290
321
  '@brightsideoy/kama': currentKamaVersion,
@@ -392,7 +423,8 @@ async function createNewSiteOnly() {
392
423
  const envChoice = await rl.question('\nChoice [1/2]: ');
393
424
  const isCloudflare = envChoice.trim() === '2';
394
425
 
395
- const siteFolder = await rl.question('Site folder name (e.g., my-new-site): ');
426
+ // šŸŽÆ FIX: Default to 'kama-starter'
427
+ const siteFolder = (await rl.question('Astro package/folder name (e.g., my-new-site) [kama-starter]: ')).trim() || 'kama-starter';
396
428
 
397
429
  let siteDomain = 'localhost:4321';
398
430
  let cmsFullUrl = 'http://localhost:8787';
@@ -405,7 +437,9 @@ async function createNewSiteOnly() {
405
437
 
406
438
  await generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, targetWorkerName, isCloudflare);
407
439
 
408
- console.log(`\n✨ Site created in sites/${siteFolder}! Run 'npm install' at workspace root.`);
440
+ console.log(`\n✨ Site created in sites/${siteFolder}!`);
441
+ console.log(`šŸ“¦ Run 'npm install' at workspace root.`);
442
+ console.log(`🌱 Run 'npm run dev' to boot up and seed the new site data.`);
409
443
  }
410
444
 
411
445
  /**
@@ -588,12 +622,58 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
588
622
  writeFileSync(wranglerPath, wranglerStr);
589
623
  }
590
624
 
625
+ // šŸŽÆ FIX: Smart capitalization for dynamic site labels
626
+ const siteLabel = siteFolder.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
627
+ const safeSiteLabel = siteLabel.replace(/'/g, "''");
628
+
629
+ const channelKey = siteFolder.replace(/[^a-z0-9_-]/gi, '-').toLowerCase();
630
+ const treeKey = `${channelKey}-nav`;
631
+
632
+ // šŸŽÆ FIX: Inject `safeSiteLabel` into the logoText, titleSuffix, and titles!
633
+ const seedSql = `
634
+ -- Seed data for site: ${safeSiteLabel}
635
+ INSERT OR IGNORE INTO channels (key, label, channelType, defaultLanguage, isHidden, dataJson)
636
+ VALUES ('${channelKey}', '${safeSiteLabel}', 'website', 'en', 0, '{}');
637
+
638
+ INSERT OR IGNORE INTO trees (key, label, treeType, channelId, isHidden, dataJson)
639
+ VALUES ('${treeKey}', 'Main Navigation', 'main-navigation', (SELECT id FROM channels WHERE key = '${channelKey}'), 0, '{}');
640
+
641
+ INSERT OR IGNORE INTO channel_trees (channelId, treeId, key)
642
+ VALUES ((SELECT id FROM channels WHERE key = '${channelKey}'), (SELECT id FROM trees WHERE key = '${treeKey}'), 'main');
643
+
644
+ INSERT INTO content_nodes (contentType, title, slug, language, status, dataJson)
645
+ SELECT 'page', 'Welcome to ${safeSiteLabel}', 'home', 'en', 'published', '{"titleSuffix":"– ${safeSiteLabel}","layoutId":"single-column","subpageLayoutId":"two-column","logoText":"${safeSiteLabel}"}'
646
+ WHERE NOT EXISTS (SELECT 1 FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}');
647
+
648
+ INSERT OR IGNORE INTO node_channels (nodeId, channelId)
649
+ VALUES (
650
+ (SELECT id FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}' ORDER BY id DESC LIMIT 1),
651
+ (SELECT id FROM channels WHERE key = '${channelKey}')
652
+ );
653
+
654
+ INSERT OR IGNORE INTO tree_nodes (treeId, contentId, parentId, sortOrder)
655
+ VALUES (
656
+ (SELECT id FROM trees WHERE key = '${treeKey}'),
657
+ (SELECT id FROM content_nodes WHERE slug = 'home' AND title = 'Welcome to ${safeSiteLabel}' ORDER BY id DESC LIMIT 1),
658
+ NULL,
659
+ 0
660
+ );
661
+ `;
662
+
663
+ const workerDir = join(rootDir, 'kama-worker');
664
+ const seedPath = join(workerDir, 'seed.sql');
665
+ if (existsSync(workerDir)) {
666
+ appendFileSync(seedPath, seedSql);
667
+ }
668
+
669
+ // šŸŽÆ FIX: Expose the title-cased name to Astro ENV vars as well
591
670
  const envContent = [
592
671
  `PUBLIC_CMS_URL="${cmsFullUrl}"`,
593
672
  `PUBLIC_API_URL="${cmsFullUrl}"`,
594
- `PUBLIC_CHANNEL_ID="website"`,
595
- `PUBLIC_TREE_ID="main-navigation"`,
596
- `PUBLIC_SITE_NAME="KAMA Starter"`
673
+ `PUBLIC_CHANNEL_ID="${channelKey}"`,
674
+ `PUBLIC_TREE_ID="${treeKey}"`,
675
+ `PUBLIC_HOME_SLUG="home"`,
676
+ `PUBLIC_SITE_NAME="${siteLabel.replace(/"/g, '\\"')}"`
597
677
  ].join('\n') + '\n';
598
678
 
599
679
  writeFileSync(join(siteDir, '.env'), envContent);
@@ -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: