@brightsideoy/kama 1.0.0-alpha.11 → 1.0.0-alpha.13

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
@@ -50,8 +50,8 @@ try {
50
50
  }
51
51
 
52
52
  /**
53
- * 🎯 SECURE SECRET MANAGER
54
- * Gracefully retrieves an existing workspace secret, or generates and injects one if missing.
53
+ - 🎯 SECURE SECRET MANAGER
54
+ - Gracefully retrieves an existing workspace secret, or generates and injects one if missing.
55
55
  */
56
56
  function getOrGenerateWorkspaceSecret(workerDir) {
57
57
  const envPath = join(workerDir, '.env');
@@ -89,7 +89,7 @@ function getOrGenerateWorkspaceSecret(workerDir) {
89
89
  }
90
90
 
91
91
  /**
92
- * 🎯 BULLETPROOF WRANGLER EXECUTION RESOLVER
92
+ - 🎯 BULLETPROOF WRANGLER EXECUTION RESOLVER
93
93
  */
94
94
  function getWranglerCmd() {
95
95
  try {
@@ -101,7 +101,7 @@ function getWranglerCmd() {
101
101
  }
102
102
 
103
103
  /**
104
- * 🎯 ROBUST WRANGLER RUNNER
104
+ - 🎯 ROBUST WRANGLER RUNNER
105
105
  */
106
106
  function runWrangler(cmd, options = {}) {
107
107
  const baseWrangler = getWranglerCmd();
@@ -121,7 +121,7 @@ function runWrangler(cmd, options = {}) {
121
121
  }
122
122
 
123
123
  /**
124
- * 🎯 BULLETPROOF KV PROVISIONING ENGINE
124
+ - 🎯 BULLETPROOF KV PROVISIONING ENGINE
125
125
  */
126
126
  function getOrCreateKV(title) {
127
127
  const out = runWrangler(`kv namespace create "${title}"`);
@@ -154,7 +154,7 @@ function showHelp() {
154
154
  console.log(pc.cyan('Workspace Scripts (Run from project root):'));
155
155
  console.log(' npm update Update to latest KAMA version');
156
156
  console.log(' npm run setup Initialize database schema (Run Once)');
157
- console.log(' npm run seed -w kama-worker Seed the database with mock test data'); // 🎯 NEW LINE
157
+ console.log(' npm run seed -w kama-worker Seed the database with mock test data');
158
158
  console.log(' npm run dev Boot up the CMS backend and Astro frontend concurrently');
159
159
  console.log(' npm run deploy:cms Deploy the backend worker and database to Cloudflare');
160
160
  console.log(' npm run deploy:site Deploy the Astro frontend site to Cloudflare\n');
@@ -224,7 +224,7 @@ function convertAstroConfigToCloudflare(siteDir) {
224
224
  }
225
225
 
226
226
  /**
227
- * COMMAND: npx kama init
227
+ - COMMAND: npx kama init
228
228
  */
229
229
  async function createFullWorkspace() {
230
230
  const cwdName = basename(process.cwd());
@@ -407,15 +407,16 @@ async function createFullWorkspace() {
407
407
  type: 'module',
408
408
  version: '1.0.0',
409
409
  scripts: {
410
- // 🎯 Uses native node with the compiled dist/build-seed.js
411
- "seed": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && wrangler d1 execute " + dbName + " --local --file=seed.sql",
410
+ "seed": "npm run seed:local",
411
+ "seed:local": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute " + dbName + " --local --file=\"$f\"; done",
412
+ "seed:remote": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute " + dbName + " --remote --file=\"$f\"; done",
412
413
  "setup:local": `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql > /dev/null 2>&1`,
413
414
  "setup:remote": `wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`,
414
415
  "dev": "wrangler dev --port 8787 --inspector-port 9239",
415
416
  "deploy": "npm run setup:remote && wrangler deploy"
416
417
  },
417
418
  dependencies: { '@brightsideoy/kama': currentKamaVersion, '@libsql/client': '^0.14.0' },
418
- devDependencies: { "wrangler": WRANGLER_LATEST }, // 🎯 Removed TSX requirement
419
+ devDependencies: { "wrangler": WRANGLER_LATEST },
419
420
  }, null, 2)
420
421
  );
421
422
 
@@ -442,7 +443,6 @@ async function createFullWorkspace() {
442
443
 
443
444
  writeFileSync(join(cmsDir, 'wrangler.json'), JSON.stringify(cmsWranglerConfig, null, 2));
444
445
 
445
- // 🎯 Inject API Secret directly into .dev.vars
446
446
  const publicFrontendUrl = (shouldCreateSite) ? (siteDomain && siteDomain !== 'localhost:4321' ? `https://${siteDomain}` : 'http://localhost:4321') : 'http://localhost:4321';
447
447
  writeFileSync(join(cmsDir, '.dev.vars'), `# Cloudflare Edge Cache Purging Secrets\nCLOUDFLARE_API_TOKEN=cfut_YOUR_CUSTOM_PURGE_TOKEN\n\n# Local Development Overrides\nCDN_DOMAIN=\nPUBLIC_FRONTEND_URL=${publicFrontendUrl}\n\n# Server-to-Server API Security\nFRONTEND_API_SECRET="${frontendApiSecret}"\n`);
448
448
  } else {
@@ -456,18 +456,17 @@ async function createFullWorkspace() {
456
456
  type: 'module',
457
457
  version: '1.0.0',
458
458
  scripts: {
459
- // 🎯 Uses native node with the compiled dist/build-seed.js
460
- "seed": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && sqlite3 kama.db < seed.sql",
459
+ "seed": "npm run seed:local",
460
+ "seed:local": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && cat seed-*.sql | sqlite3 kama.db",
461
461
  "setup:local": "sqlite3 kama.db < ../node_modules/@brightsideoy/kama/schema.sql",
462
462
  "dev": "tsx server.js",
463
463
  "start": "node server.js"
464
464
  },
465
465
  dependencies: { '@brightsideoy/kama': currentKamaVersion, '@hono/node-server': '^1.13.0', '@libsql/client': '^0.14.0', 'sharp': '^0.33.5', 'dotenv': '^16.4.5' },
466
- devDependencies: { "tsx": "^4.19.0" }, // 🎯 Kept because "dev" uses it
466
+ devDependencies: { "tsx": "^4.19.0" },
467
467
  }, null, 2)
468
468
  );
469
469
 
470
- // 🎯 Inject API Secret directly into .env
471
470
  writeFileSync(join(cmsDir, '.env'), `PORT=8787\nKAMA_RUNTIME=node\nDB_PATH=./kama.db\nLOCAL_MEDIA_PATH=./public/media\nPUBLIC_CMS_URL=http://127.0.0.1:8787\nPUBLIC_API_URL=http://127.0.0.1:8787\nPUBLIC_FRONTEND_URL=http://127.0.0.1:4321\n\n# Server-to-Server API Security\nFRONTEND_API_SECRET="${frontendApiSecret}"\n`);
472
471
  }
473
472
 
@@ -476,7 +475,6 @@ async function createFullWorkspace() {
476
475
 
477
476
  if (shouldCreateSite) {
478
477
  s.message(`Scaffolding Astro site (${siteFolderName})...`);
479
- // 🎯 Pass secret to the frontend generator
480
478
  await generateAstroSite(rootDir, siteFolderName, siteDomain, cmsFullUrl, workerName, isCloudflare, frontendApiSecret);
481
479
  }
482
480
 
@@ -501,7 +499,7 @@ async function createFullWorkspace() {
501
499
  }
502
500
 
503
501
  /**
504
- * COMMAND: npx kama add site
502
+ - COMMAND: npx kama add site
505
503
  */
506
504
  async function createNewSiteOnly() {
507
505
  const rootDir = process.cwd();
@@ -525,7 +523,6 @@ async function createNewSiteOnly() {
525
523
  } catch (e) { }
526
524
  }
527
525
 
528
- // 🎯 Ensure secret exists in worker before provisioning site
529
526
  const frontendApiSecret = existsSync(workerDir) ? getOrGenerateWorkspaceSecret(workerDir) : crypto.randomBytes(32).toString('hex');
530
527
 
531
528
  const isCloudflare = handleCancel(await select({
@@ -558,7 +555,6 @@ async function createNewSiteOnly() {
558
555
  const s = spinner();
559
556
  s.start(`Generating Astro site...`);
560
557
 
561
- // 🎯 Pass secret to the frontend generator
562
558
  await generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, targetWorkerName, isCloudflare, frontendApiSecret);
563
559
 
564
560
  s.stop(`Site created in sites/${siteFolder}!`);
@@ -568,7 +564,7 @@ async function createNewSiteOnly() {
568
564
  }
569
565
 
570
566
  /**
571
- * COMMAND: npx kama bundle
567
+ - COMMAND: npx kama bundle
572
568
  */
573
569
  async function bundleWorkspaceSchemas(targetRootDir, silent = false) {
574
570
  let rootDir = targetRootDir || process.cwd();
@@ -587,7 +583,7 @@ async function bundleWorkspaceSchemas(targetRootDir, silent = false) {
587
583
  }
588
584
 
589
585
  /**
590
- * COMMAND: npx kama promote
586
+ - COMMAND: npx kama promote
591
587
  */
592
588
  async function promoteLocalToCloudflare() {
593
589
  const rootDir = process.cwd();
@@ -599,7 +595,6 @@ async function promoteLocalToCloudflare() {
599
595
  process.exit(1);
600
596
  }
601
597
 
602
- // 🎯 Safely persist the secret during promotion
603
598
  const frontendApiSecret = getOrGenerateWorkspaceSecret(workerDir);
604
599
 
605
600
  const s = spinner();
@@ -635,12 +630,34 @@ async function promoteLocalToCloudflare() {
635
630
 
636
631
  const projName = basename(rootDir);
637
632
  let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
633
+
634
+ // 🎯 1. ARCHITECTURE FIX: Check if the database already exists
638
635
  let d1DatabaseId = '00000000-0000-0000-0000-000000000000';
636
+ const listOutput = runWrangler('d1 list --json');
637
+ let dbExists = false;
639
638
 
640
- const d1Output = runWrangler(`d1 create ${dbName}`);
641
- if (d1Output) {
642
- const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
643
- if (match) d1DatabaseId = match[0];
639
+ if (listOutput) {
640
+ try {
641
+ const jsonStart = listOutput.indexOf('[');
642
+ const jsonEnd = listOutput.lastIndexOf(']') + 1;
643
+ if (jsonStart !== -1 && jsonEnd !== -1) {
644
+ const databases = JSON.parse(listOutput.substring(jsonStart, jsonEnd));
645
+ const existingDb = databases.find(db => db.name === dbName);
646
+ if (existingDb) {
647
+ d1DatabaseId = existingDb.uuid;
648
+ dbExists = true;
649
+ }
650
+ }
651
+ } catch (e) { }
652
+ }
653
+
654
+ // 2. If it doesn't exist, create it safely
655
+ if (!dbExists) {
656
+ const d1Output = runWrangler(`d1 create ${dbName}`);
657
+ if (d1Output) {
658
+ const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
659
+ if (match) d1DatabaseId = match[0];
660
+ }
644
661
  }
645
662
 
646
663
  const workerName = `${projName}-worker`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
@@ -687,12 +704,13 @@ async function promoteLocalToCloudflare() {
687
704
  workerPkg.scripts["setup:local"] = `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql`;
688
705
  workerPkg.scripts["setup:remote"] = `wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`;
689
706
 
690
- // 🎯 Uses native node with the compiled dist/build-seed.js
691
- workerPkg.scripts["seed"] = `node ../node_modules/@brightsideoy/kama/dist/build-seed.js && wrangler d1 execute ${dbName} --local --file=seed.sql`;
707
+ workerPkg.scripts["seed"] = "npm run seed:local";
708
+ // 🎯 3. ARCHITECTURE FIX: Chunked seed loops for promoted worker
709
+ workerPkg.scripts["seed:local"] = `node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute ${dbName} --local --file="$f"; done`;
710
+ workerPkg.scripts["seed:remote"] = `node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute ${dbName} --remote --file="$f"; done`;
692
711
 
693
712
  workerPkg.devDependencies = workerPkg.devDependencies || {};
694
713
  workerPkg.devDependencies.wrangler = WRANGLER_LATEST;
695
- // 🎯 explicit deletion of TSX addition on promotion
696
714
  if (workerPkg.devDependencies.tsx) delete workerPkg.devDependencies.tsx;
697
715
 
698
716
  writeFileSync(workerPkgPath, JSON.stringify(workerPkg, null, 2));
@@ -714,7 +732,8 @@ async function promoteLocalToCloudflare() {
714
732
  assets: { directory: "./dist/client", binding: "ASSETS", run_worker_first: true },
715
733
  kv_namespaces: [{ binding: "SESSION", id: sessionId }, { binding: "HTML_CACHE", id: htmlCacheId }],
716
734
  services: [{ binding: "CMS_WORKER", service: currentConfig.name }],
717
- routes: [{ pattern: siteDomain, zone_name: siteDomain, custom_domain: true, enabled: true, previews_enabled: false }],
735
+ // 🎯 2. ARCHITECTURE FIX: Removed strict zone_name requirement
736
+ routes: [{ pattern: siteDomain, custom_domain: true, enabled: true, previews_enabled: false }],
718
737
  vars: { PUBLIC_CMS_URL: `https://${cmsDomain}`, PUBLIC_API_URL: `https://${cmsDomain}` }
719
738
  };
720
739
  writeFileSync(siteWranglerPath, JSON.stringify(siteConfig, null, 2));
@@ -739,7 +758,7 @@ async function promoteLocalToCloudflare() {
739
758
  }
740
759
 
741
760
  /**
742
- * Helper: Copies the Astro starter template and hydrates runtime tokens
761
+ - Helper: Copies the Astro starter template and hydrates runtime tokens
743
762
  */
744
763
  async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, workerName = 'kama-worker', isCloudflare = false, frontendApiSecret = '') {
745
764
  const siteDir = join(rootDir, 'sites', siteFolder);
@@ -800,6 +819,7 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
800
819
  assets: { directory: "./dist/client", binding: "ASSETS", run_worker_first: true },
801
820
  kv_namespaces: [{ binding: "SESSION", id: sessionId }, { binding: "HTML_CACHE", id: htmlCacheId }],
802
821
  services: [{ binding: "CMS_WORKER", service: workerName }],
822
+ routes: [{ pattern: siteDomain, custom_domain: true, enabled: true, previews_enabled: false }],
803
823
  vars: { PUBLIC_CMS_URL: cmsFullUrl, PUBLIC_API_URL: cmsFullUrl, PUBLIC_CHANNEL_ID: channelKey, PUBLIC_TREE_ID: treeKey, PUBLIC_SITE_NAME: siteLabel }
804
824
  };
805
825
 
@@ -807,7 +827,6 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
807
827
  writeFileSync(join(siteDir, 'wrangler.json'), JSON.stringify(siteConfig, null, 2));
808
828
  }
809
829
 
810
- // 🎯 Dynamically append the secret to the Astro environment setup
811
830
  let envContent = `# Local Development Overrides\nPUBLIC_CMS_URL="http://127.0.0.1:8787"\nPUBLIC_API_URL="http://127.0.0.1:8787"\nPUBLIC_CHANNEL_ID="${channelKey}"\nPUBLIC_TREE_ID="${treeKey}"\nPUBLIC_SITE_NAME="${siteLabel.replace(/"/g, '\\"')}"\n`;
812
831
 
813
832
  if (frontendApiSecret) {