@tpitre/story-ui 3.1.0 → 3.2.0

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/README.md CHANGED
@@ -66,9 +66,7 @@ Story UI will guide you through:
66
66
  | **Gemini** (Google) | Gemini 3 Pro, Gemini 2.0 Flash, Gemini 1.5 Pro | Fast generation, cost efficiency |
67
67
 
68
68
  ### Production Deployment
69
- - **Cloudflare Workers**: Edge-deployed API proxy
70
- - **Cloudflare Pages**: Static frontend hosting
71
- - **Railway**: Full Node.js backend (alternative)
69
+ - **Railway**: Node.js backend with PostgreSQL for story persistence
72
70
  - **MCP Integration**: Connect AI clients directly to production
73
71
 
74
72
  ---
@@ -314,100 +312,55 @@ Story UI v3 can be deployed as a standalone web application accessible from anyw
314
312
 
315
313
  ```
316
314
  ┌─────────────────────────────────────────────────────────────┐
317
- Cloudflare Pages
318
- │ (Your Frontend App) │
315
+ Railway Deployment
319
316
  │ ┌─────────────────────────────────────────────────────────┐│
320
- │ │ - Chat Interface ││
321
- │ │ - Live Component Preview ││
322
- │ │ - Syntax Highlighted Code View ││
323
- │ │ - Provider/Model Selection ││
317
+ │ │ Express MCP Server (Node.js) ││
318
+ │ │ - Serves React frontend ││
319
+ │ │ - API routes for story generation ││
320
+ │ │ - Multi-provider LLM support ││
324
321
  │ └─────────────────────────────────────────────────────────┘│
325
- └──────────────────────────┬──────────────────────────────────┘
326
-
327
-
328
- ┌─────────────────────────────────────────────────────────────┐
329
- │ Cloudflare Workers Edge │
322
+ │ │ │
323
+ ▼ │
330
324
  │ ┌─────────────────────────────────────────────────────────┐│
331
- │ │ - /story-ui/providers → Available providers/models ││
332
- │ │ - /story-ui/claude Claude API proxy ││
333
- │ │ - /story-ui/openai → OpenAI API proxy ││
334
- │ │ - /story-ui/gemini → Gemini API proxy ││
335
- │ │ - /mcp → MCP JSON-RPC endpoint ││
325
+ │ │ PostgreSQL Database ││
326
+ │ │ - Story persistence across deployments ││
336
327
  │ └─────────────────────────────────────────────────────────┘│
337
- └─────────────────────────────────────────────────────────────┘
328
+ └──────────────────────────────────────────────────────────────┘
338
329
  ```
339
330
 
340
- ### Deploy to Cloudflare
341
-
342
- **1. Deploy the Edge Worker (Backend)**
343
-
344
- ```bash
345
- cd cloudflare-edge
346
- wrangler deploy
331
+ ### Deploy to Railway
347
332
 
348
- # Set your API keys as secrets
349
- wrangler secret put ANTHROPIC_API_KEY
350
- wrangler secret put OPENAI_API_KEY # optional
351
- wrangler secret put GEMINI_API_KEY # optional
352
- ```
333
+ Railway provides a complete deployment experience with PostgreSQL for story persistence.
353
334
 
354
- **2. Deploy the Frontend**
335
+ **Quick Start:**
355
336
 
356
337
  ```bash
357
- cd templates/production-app
358
- npm install
359
- npm run build
360
- wrangler pages deploy dist --project-name=your-app-name
361
- ```
362
-
363
- **3. Configure Environment**
364
-
365
- Update the frontend to point to your worker URL in the configuration.
366
-
367
- ### Railway Deployment (Recommended)
338
+ # Install Railway CLI
339
+ npm install -g @railway/cli
340
+ railway login
368
341
 
369
- Railway provides the most complete deployment experience with:
370
- - Full Storybook dev server with HMR
371
- - Integrated Story UI MCP server
372
- - Live story generation directly in the sidebar
373
- - Easy environment variable management
342
+ # Initialize project with PostgreSQL
343
+ railway init
344
+ railway add --plugin postgresql
374
345
 
375
- **1. Deploy to Railway**
376
-
377
- ```bash
378
- # From your Storybook project with Story UI configured
379
- npx story-ui deploy --live --platform=railway --dry-run
380
-
381
- # Review the generated configuration, then deploy:
346
+ # Deploy
382
347
  railway up
383
348
  ```
384
349
 
385
- **2. Set Environment Variables in Railway Dashboard:**
350
+ **Environment Variables (set in Railway Dashboard):**
386
351
  - `ANTHROPIC_API_KEY` - Required for Claude models
387
352
  - `OPENAI_API_KEY` - Optional for OpenAI models
388
353
  - `GEMINI_API_KEY` - Optional for Gemini models
389
- - `STORY_UI_DEV_MODE=true` - Enable file-based story storage
390
-
391
- **3. Connect External MCP Clients**
392
-
393
- Once deployed, your Railway instance provides an MCP endpoint that AI clients can connect to:
354
+ - `DATABASE_URL` - Auto-set by Railway PostgreSQL plugin
394
355
 
395
- ```
396
- https://your-app.up.railway.app/story-ui
397
- ```
356
+ **Connect MCP Clients:**
398
357
 
399
- **For Claude Desktop/Claude Code:**
400
358
  ```bash
401
359
  # Add your Railway deployment as an MCP server
402
360
  claude mcp add --transport http story-ui https://your-app.up.railway.app/story-ui
403
361
  ```
404
362
 
405
- **For other MCP clients**, use the full URL:
406
- ```
407
- https://your-app.up.railway.app/mcp
408
- ```
409
-
410
- This allows you to generate stories from Claude Desktop, ChatGPT (with MCP support), or any other MCP-compatible AI client, and see them appear in your deployed Storybook instance.
363
+ See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment instructions, PostgreSQL setup, and troubleshooting.
411
364
 
412
365
  ---
413
366
 
@@ -4,14 +4,7 @@ interface DeployOptions {
4
4
  projectName?: string;
5
5
  dryRun?: boolean;
6
6
  backend?: boolean;
7
- frontend?: boolean;
8
- app?: boolean;
9
7
  backendUrl?: string;
10
- storybookDir?: string;
11
- init?: boolean;
12
- edge?: boolean;
13
- pages?: boolean;
14
- all?: boolean;
15
8
  }
16
9
  export declare function deployCommand(options: DeployOptions): Promise<void>;
17
10
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../cli/deploy.ts"],"names":[],"mappings":"AASA,UAAU,aAAa;IAErB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA6hCD,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0JzE"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../cli/deploy.ts"],"names":[],"mappings":"AASA,UAAU,aAAa;IAErB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4nBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2GzE"}
@@ -563,346 +563,10 @@ primary_region = "sjc"
563
563
  return null;
564
564
  }
565
565
  }
566
- /**
567
- * Deploy Storybook frontend to Cloudflare Pages
568
- */
569
- async function deployStorybook(backendUrl, storybookDir, projectName, dryRun) {
570
- console.log('\n📖 Deploying Storybook to Cloudflare Pages...\n');
571
- // Check wrangler auth
572
- try {
573
- execSync('npx wrangler whoami', { stdio: 'pipe' });
574
- }
575
- catch {
576
- console.log('🔐 Not authenticated with Cloudflare.');
577
- execSync('npx wrangler login', { stdio: 'inherit' });
578
- }
579
- // Validate storybook directory
580
- if (!fs.existsSync(storybookDir)) {
581
- console.error(`❌ Storybook directory not found: ${storybookDir}`);
582
- console.log(' Make sure you have a Storybook project set up.');
583
- return null;
584
- }
585
- const packageJsonPath = path.join(storybookDir, 'package.json');
586
- if (!fs.existsSync(packageJsonPath)) {
587
- console.error('❌ No package.json found in storybook directory');
588
- return null;
589
- }
590
- if (dryRun) {
591
- console.log(`[DRY RUN] Would build Storybook with VITE_STORY_UI_EDGE_URL=${backendUrl}`);
592
- console.log(`[DRY RUN] Would deploy from: ${storybookDir}`);
593
- return 'https://dry-run.pages.dev';
594
- }
595
- try {
596
- // Install dependencies if needed
597
- if (!fs.existsSync(path.join(storybookDir, 'node_modules'))) {
598
- console.log('📦 Installing dependencies...');
599
- execSync('npm install', { cwd: storybookDir, stdio: 'inherit' });
600
- }
601
- // Build storybook with the backend URL
602
- console.log(`🔨 Building Storybook with backend URL: ${backendUrl}`);
603
- execSync(`VITE_STORY_UI_EDGE_URL=${backendUrl} npm run build-storybook`, {
604
- cwd: storybookDir,
605
- stdio: 'inherit'
606
- });
607
- // Deploy to Cloudflare Pages
608
- const staticDir = path.join(storybookDir, 'storybook-static');
609
- if (!fs.existsSync(staticDir)) {
610
- console.error('❌ storybook-static directory not found after build');
611
- return null;
612
- }
613
- console.log('🚀 Deploying to Cloudflare Pages...');
614
- const result = execSync(`npx wrangler pages deploy ${staticDir} --project-name=${projectName}-storybook 2>&1`, {
615
- encoding: 'utf-8'
616
- });
617
- console.log(result);
618
- // Extract URL from output
619
- const urlMatch = result.match(/https:\/\/[^\s]+\.pages\.dev/);
620
- if (urlMatch) {
621
- console.log(`\n✅ Storybook deployed to: ${urlMatch[0]}`);
622
- return urlMatch[0];
623
- }
624
- return null;
625
- }
626
- catch (error) {
627
- console.error('❌ Storybook deployment failed:', error.message);
628
- if (error.stdout)
629
- console.log(error.stdout);
630
- if (error.stderr)
631
- console.error(error.stderr);
632
- return null;
633
- }
634
- }
635
- /**
636
- * Build and deploy standalone production app
637
- * This builds a React app with the user's component library bundled in
638
- */
639
- async function deployProductionApp(backendUrl, projectName, dryRun) {
640
- console.log('\n🚀 Building Standalone Production App...\n');
641
- console.log(' This creates a standalone web app with your component library\n');
642
- const pkgRoot = getPackageRoot();
643
- const userCwd = process.cwd();
644
- const templateDir = path.join(pkgRoot, 'templates/production-app');
645
- const buildDir = path.join(userCwd, '.story-ui-build');
646
- // Check for story-ui.config.js in user's project
647
- const configPath = path.join(userCwd, 'story-ui.config.js');
648
- if (!fs.existsSync(configPath)) {
649
- console.error('❌ No story-ui.config.js found in current directory');
650
- console.log(' Run "npx story-ui init" first to configure your component library');
651
- return null;
652
- }
653
- if (dryRun) {
654
- console.log(`[DRY RUN] Would build production app from: ${templateDir}`);
655
- console.log(`[DRY RUN] Would bundle with components from: ${configPath}`);
656
- console.log(`[DRY RUN] Backend URL would be: ${backendUrl}`);
657
- return 'https://dry-run.pages.dev';
658
- }
659
- try {
660
- // 1. Copy template to build directory
661
- console.log('📁 Setting up build directory...');
662
- if (fs.existsSync(buildDir)) {
663
- fs.rmSync(buildDir, { recursive: true });
664
- }
665
- fs.mkdirSync(buildDir, { recursive: true });
666
- // Copy template files
667
- copyDirectory(templateDir, buildDir);
668
- // 2. Load user config and discover components
669
- console.log('🔍 Discovering components from your library...');
670
- const { generateComponentRegistry } = await import('../story-generator/componentRegistryGenerator.js');
671
- const registryOutputPath = path.join(buildDir, 'src/componentRegistry.ts');
672
- // Generate the component registry in the user's project context
673
- process.chdir(userCwd);
674
- await generateComponentRegistry(registryOutputPath);
675
- // 2.5. Load ALL design system documentation (story-ui-docs/ AND story-ui-considerations.md)
676
- console.log('📚 Loading design system documentation...');
677
- // First, try the full documentation directory (story-ui-docs/)
678
- const { DocumentationLoader } = await import('../story-generator/documentationLoader.js');
679
- const docLoader = new DocumentationLoader(userCwd);
680
- let fullDocumentation = '';
681
- let documentationTokens = {};
682
- let documentationPatterns = {};
683
- let hasFullDocs = false;
684
- if (docLoader.hasDocumentation()) {
685
- console.log('📂 Found story-ui-docs/ directory, loading all documentation...');
686
- const docs = await docLoader.loadDocumentation();
687
- fullDocumentation = docLoader.formatForPrompt(docs);
688
- documentationTokens = docs.tokens;
689
- documentationPatterns = docs.patterns;
690
- hasFullDocs = true;
691
- console.log(` ✅ Loaded ${docs.guidelines.length} guidelines, ${Object.keys(docs.tokens).length} token categories, ${Object.keys(docs.patterns).length} patterns`);
692
- }
693
- // Also load legacy considerations file (story-ui-considerations.md)
694
- const { loadConsiderations, considerationsToPrompt } = await import('../story-generator/considerationsLoader.js');
695
- const considerations = loadConsiderations(); // Auto-finds in common locations
696
- const considerationsPrompt = considerations ? considerationsToPrompt(considerations) : '';
697
- // Combine both sources - full docs take priority, considerations supplement
698
- let combinedDocumentation = '';
699
- if (fullDocumentation) {
700
- combinedDocumentation = fullDocumentation;
701
- if (considerationsPrompt) {
702
- // Add considerations as supplementary rules
703
- combinedDocumentation += '\n\n📋 ADDITIONAL DESIGN SYSTEM RULES:\n' + considerationsPrompt;
704
- }
705
- }
706
- else if (considerationsPrompt) {
707
- combinedDocumentation = considerationsPrompt;
708
- }
709
- const considerationsOutputPath = path.join(buildDir, 'src/considerations.ts');
710
- // Write comprehensive documentation to a TypeScript file
711
- const considerationsContent = `/**
712
- * AI Design System Documentation - Auto-generated
713
- *
714
- * This file contains ALL design system documentation for the AI:
715
- * - Guidelines from story-ui-docs/ directory
716
- * - Design tokens (colors, spacing, typography, etc.)
717
- * - Component-specific documentation
718
- * - Layout patterns
719
- * - Accessibility rules
720
- * - Legacy considerations from story-ui-considerations.md
721
- *
722
- * To customize:
723
- * 1. Create a story-ui-docs/ directory with markdown/JSON files
724
- * 2. And/or edit story-ui-considerations.md in your project root
725
- */
726
-
727
- export const aiConsiderations = ${JSON.stringify(combinedDocumentation, null, 2)};
728
-
729
- export const hasConsiderations = ${(hasFullDocs || considerations) ? 'true' : 'false'};
730
-
731
- // Design tokens for programmatic access (if needed)
732
- export const designTokens = ${JSON.stringify(documentationTokens, null, 2)};
733
-
734
- // Design patterns for programmatic access (if needed)
735
- export const designPatterns = ${JSON.stringify(documentationPatterns, null, 2)};
736
-
737
- // Source information
738
- export const documentationSource = {
739
- hasFullDocs: ${hasFullDocs},
740
- hasLegacyConsiderations: ${considerations ? 'true' : 'false'},
741
- libraryName: ${JSON.stringify(considerations?.libraryName || null)}
742
- };
743
- `;
744
- fs.writeFileSync(considerationsOutputPath, considerationsContent);
745
- if (hasFullDocs && considerations) {
746
- console.log(`✅ Loaded full documentation + considerations for: ${considerations.libraryName || 'your component library'}`);
747
- }
748
- else if (hasFullDocs) {
749
- console.log('✅ Loaded full documentation from story-ui-docs/');
750
- }
751
- else if (considerations) {
752
- console.log(`✅ Loaded considerations for: ${considerations.libraryName || 'your component library'}`);
753
- }
754
- else {
755
- console.log('⚠️ No documentation found - using default prompts');
756
- console.log(' Create story-ui-docs/ directory or story-ui-considerations.md for better results');
757
- }
758
- process.chdir(buildDir);
759
- // 3. Install dependencies
760
- console.log('📦 Installing dependencies...');
761
- // Read the user's config
762
- const userConfig = await import(configPath);
763
- const config = userConfig.default;
764
- const componentLibraryPackage = config.importPath;
765
- // Update package.json to include the component library as a dependency
766
- const packageJsonPath = path.join(buildDir, 'package.json');
767
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
768
- // Add dependencies from user config (design system agnostic)
769
- const packagesToAdd = config.dependencies || [componentLibraryPackage];
770
- for (const pkg of packagesToAdd) {
771
- packageJson.dependencies[pkg] = '*';
772
- }
773
- // Add any additional imports as dependencies
774
- if (config.additionalImports) {
775
- for (const imp of config.additionalImports) {
776
- packageJson.dependencies[imp.path] = '*';
777
- }
778
- }
779
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
780
- // 3.5. Generate main.tsx with user-defined provider (design system agnostic)
781
- console.log('⚙️ Configuring app providers from story-ui.config.js...');
782
- const mainTsxPath = path.join(buildDir, 'src/main.tsx');
783
- const mainTsxContent = generateMainTsxFromConfig(config);
784
- fs.writeFileSync(mainTsxPath, mainTsxContent);
785
- // Install deps using npm with link to use local versions
786
- execSync('npm install', { cwd: buildDir, stdio: 'inherit' });
787
- // 4. Create .env file with backend URL
788
- console.log('⚙️ Configuring backend URL...');
789
- fs.writeFileSync(path.join(buildDir, '.env'), `VITE_STORY_UI_SERVER=${backendUrl}\nVITE_APP_TITLE=Story UI\n`);
790
- // 5. Build the app
791
- console.log('🔨 Building production app...');
792
- execSync('npm run build', { cwd: buildDir, stdio: 'inherit' });
793
- // 6. Deploy to Cloudflare Pages
794
- console.log('☁️ Deploying to Cloudflare Pages...');
795
- // Check wrangler auth
796
- try {
797
- execSync('npx wrangler whoami', { stdio: 'pipe' });
798
- }
799
- catch {
800
- console.log('🔐 Not authenticated with Cloudflare.');
801
- execSync('npx wrangler login', { stdio: 'inherit' });
802
- }
803
- const distDir = path.join(buildDir, 'dist');
804
- const result = execSync(`npx wrangler pages deploy ${distDir} --project-name=${projectName}-app 2>&1`, {
805
- encoding: 'utf-8'
806
- });
807
- console.log(result);
808
- // Extract URL from output
809
- const urlMatch = result.match(/https:\/\/[^\s]+\.pages\.dev/);
810
- if (urlMatch) {
811
- console.log(`\n✅ Production app deployed to: ${urlMatch[0]}`);
812
- return urlMatch[0];
813
- }
814
- console.log('\n⚠️ Deployment completed but URL not detected in output.');
815
- return null;
816
- }
817
- catch (error) {
818
- console.error('❌ Production app deployment failed:', error.message);
819
- if (error.stdout)
820
- console.log(error.stdout);
821
- if (error.stderr)
822
- console.error(error.stderr);
823
- return null;
824
- }
825
- finally {
826
- // Return to original directory
827
- process.chdir(userCwd);
828
- }
829
- }
830
- /**
831
- * Generate main.tsx from user config (design system agnostic)
832
- *
833
- * User's story-ui.config.js can define provider configuration:
834
- * {
835
- * provider: {
836
- * cssImports: ["your-library/styles.css"], // CSS files to import
837
- * imports: ["import { YourProvider } from 'your-library';"], // Provider imports
838
- * wrapper: "<YourProvider>{children}</YourProvider>" // JSX wrapper with {children} placeholder
839
- * }
840
- * }
841
- */
842
- function generateMainTsxFromConfig(config) {
843
- const provider = config.provider || {};
844
- // CSS imports (if any)
845
- const cssImports = (provider.cssImports || [])
846
- .map((css) => `import '${css}';`)
847
- .join('\n');
848
- // Provider component imports (if any)
849
- const providerImports = (provider.imports || []).join('\n');
850
- // Provider wrapper - replace {children} with <App />
851
- let appElement = '<App />';
852
- if (provider.wrapper) {
853
- appElement = provider.wrapper.replace('{children}', '<App />');
854
- }
855
- return `/**
856
- * Production App Entry Point
857
- *
858
- * This is the main entry point for the Story UI production app.
859
- * Provider configuration is read from story-ui.config.js
860
- */
861
-
862
- import React from 'react';
863
- import ReactDOM from 'react-dom/client';
864
- ${cssImports}
865
- ${providerImports}
866
- import App from './App';
867
- import './index.css';
868
-
869
- // Mount the app
870
- const rootElement = document.getElementById('root');
871
-
872
- if (!rootElement) {
873
- throw new Error('Root element not found. Make sure there is a <div id="root"></div> in your HTML.');
874
- }
875
-
876
- ReactDOM.createRoot(rootElement).render(
877
- <React.StrictMode>
878
- ${appElement}
879
- </React.StrictMode>
880
- );
881
- `;
882
- }
883
- /**
884
- * Helper function to copy directory recursively
885
- */
886
- function copyDirectory(src, dest) {
887
- if (!fs.existsSync(dest)) {
888
- fs.mkdirSync(dest, { recursive: true });
889
- }
890
- const entries = fs.readdirSync(src, { withFileTypes: true });
891
- for (const entry of entries) {
892
- const srcPath = path.join(src, entry.name);
893
- const destPath = path.join(dest, entry.name);
894
- if (entry.isDirectory()) {
895
- copyDirectory(srcPath, destPath);
896
- }
897
- else {
898
- fs.copyFileSync(srcPath, destPath);
899
- }
900
- }
901
- }
902
566
  /**
903
567
  * Print deployment summary
904
568
  */
905
- function printSummary(backendUrl, frontendUrl, appUrl) {
569
+ function printSummary(backendUrl) {
906
570
  console.log('\n' + '═'.repeat(60));
907
571
  console.log('📋 DEPLOYMENT SUMMARY');
908
572
  console.log('═'.repeat(60));
@@ -912,54 +576,14 @@ function printSummary(backendUrl, frontendUrl, appUrl) {
912
576
  console.log(` - ${backendUrl}/story-ui/providers`);
913
577
  console.log(` - ${backendUrl}/story-ui/generate-stream`);
914
578
  console.log(` - ${backendUrl}/story-ui/stories`);
915
- }
916
- if (appUrl) {
917
- console.log(`\n🚀 Production App: ${appUrl}`);
918
- console.log(' This is your standalone web app with your component library');
919
- console.log(' Users can prompt and see live-rendered components!');
920
- }
921
- if (frontendUrl) {
922
- console.log(`\n🌐 Storybook UI: ${frontendUrl}`);
923
- console.log(' (Legacy Storybook-based interface)');
924
- }
925
- if (backendUrl && (appUrl || frontendUrl)) {
926
- console.log('\n✅ Full deployment complete!');
927
- console.log(' Non-developers can now access Story UI at:');
928
- if (appUrl) {
929
- console.log(` ${appUrl} (Recommended)`);
930
- }
931
- if (frontendUrl) {
932
- console.log(` ${frontendUrl} (Storybook)`);
933
- }
579
+ console.log('\n✅ Deployment complete!');
934
580
  }
935
581
  console.log('\n' + '═'.repeat(60));
936
582
  }
937
- /**
938
- * Legacy Cloudflare Edge deployment (deprecated)
939
- */
940
- async function legacyEdgeDeployment(options) {
941
- console.log('\n⚠️ WARNING: The Edge Worker deployment is deprecated.');
942
- console.log(' It\'s recommended to use the new approach instead:');
943
- console.log(' npx story-ui deploy --backend --platform=railway');
944
- console.log(' npx story-ui deploy --frontend --backend-url=<your-backend-url>\n');
945
- const answer = await prompt('Continue with legacy deployment? (y/N): ');
946
- if (answer.toLowerCase() !== 'y') {
947
- console.log('Aborted. Use --backend and --frontend for the new approach.');
948
- return;
949
- }
950
- // Original legacy code here...
951
- console.log('Legacy deployment not implemented in new CLI.');
952
- console.log('Please use the new --backend and --frontend flags.');
953
- }
954
583
  export async function deployCommand(options) {
955
584
  console.log('\n☁️ Story UI Production Deployment');
956
585
  console.log('═'.repeat(40) + '\n');
957
- // Handle legacy flags
958
- if (options.edge || options.pages || options.all || options.init) {
959
- await legacyEdgeDeployment(options);
960
- return;
961
- }
962
- // NEW RECOMMENDED: Live Storybook deployment
586
+ // Live Storybook deployment (recommended)
963
587
  // Runs Storybook in dev mode with MCP server - works with ANY components
964
588
  if (options.live) {
965
589
  const result = await deployLiveStorybook(options);
@@ -984,11 +608,8 @@ export async function deployCommand(options) {
984
608
  }
985
609
  return;
986
610
  }
987
- // Legacy deployment flows
611
+ // Backend-only deployment
988
612
  let backendUrl = options.backendUrl || null;
989
- let frontendUrl = null;
990
- let appUrl = null;
991
- // Deploy backend
992
613
  if (options.backend) {
993
614
  const platform = options.platform || 'railway';
994
615
  switch (platform) {
@@ -1007,38 +628,11 @@ export async function deployCommand(options) {
1007
628
  console.log('\n⚠️ Backend deployment did not return a URL.');
1008
629
  console.log(' You may need to check the platform dashboard for the URL.');
1009
630
  }
1010
- }
1011
- // Deploy standalone production app (Lovable/Bolt-style)
1012
- if (options.app) {
1013
- if (!backendUrl) {
1014
- backendUrl = await prompt('Enter your backend URL: ');
1015
- if (!backendUrl) {
1016
- console.error('❌ Backend URL is required for app deployment.');
1017
- return;
1018
- }
1019
- }
1020
- const projectName = options.projectName || 'story-ui';
1021
- appUrl = await deployProductionApp(backendUrl, projectName, options.dryRun || false);
1022
- }
1023
- // Deploy frontend (legacy Storybook-based)
1024
- if (options.frontend) {
1025
- if (!backendUrl) {
1026
- backendUrl = await prompt('Enter your backend URL: ');
1027
- if (!backendUrl) {
1028
- console.error('❌ Backend URL is required for frontend deployment.');
1029
- return;
1030
- }
1031
- }
1032
- const storybookDir = options.storybookDir || process.cwd();
1033
- const projectName = options.projectName || 'story-ui';
1034
- frontendUrl = await deployStorybook(backendUrl, storybookDir, projectName, options.dryRun || false);
1035
- }
1036
- // Print summary
1037
- if (options.backend || options.frontend || options.app) {
1038
- printSummary(backendUrl, frontendUrl, appUrl);
631
+ printSummary(backendUrl);
632
+ return;
1039
633
  }
1040
634
  // Show help if no flags provided
1041
- if (!options.backend && !options.frontend && !options.app && !options.live && !options.edge && !options.pages && !options.all && !options.init) {
635
+ if (!options.backend && !options.live) {
1042
636
  console.log('Story UI Deployment - Deploy your Storybook with AI story generation\n');
1043
637
  console.log('═'.repeat(60));
1044
638
  console.log(' RECOMMENDED: Live Storybook Deployment');
@@ -1067,16 +661,11 @@ export async function deployCommand(options) {
1067
661
  console.log(' ANTHROPIC_API_KEY - Claude API key (recommended)');
1068
662
  console.log(' OPENAI_API_KEY - OpenAI API key (optional)');
1069
663
  console.log(' GEMINI_API_KEY - Google Gemini API key (optional)');
1070
- console.log(' (Set at least one of these)\n');
1071
- console.log('ALTERNATIVE APPROACHES (for specific use cases):');
664
+ console.log(' DATABASE_URL - PostgreSQL URL for story persistence');
665
+ console.log(' (Set at least one API key)\n');
666
+ console.log('BACKEND-ONLY DEPLOYMENT:');
1072
667
  console.log('─'.repeat(60));
1073
668
  console.log(' --backend Deploy only the MCP server backend');
1074
- console.log(' --app Deploy standalone production app (static)');
1075
- console.log(' --frontend Deploy Storybook static build');
1076
669
  console.log(' --backend-url <url> Use existing backend URL\n');
1077
- console.log('DEPRECATED:');
1078
- console.log('─'.repeat(60));
1079
- console.log(' --init, --edge, --pages, --all');
1080
- console.log(' These Cloudflare Edge options are deprecated. Use --live instead.\n');
1081
670
  }
1082
671
  }
@@ -17,7 +17,7 @@ import { clearAllStories, getMemoryStats } from './routes/memoryStories.js';
17
17
  import { getAllStories, getStoryById, getStoryContent, deleteStory } from './routes/hybridStories.js';
18
18
  import { getSyncedStories, deleteSyncedStory, clearAllSyncedStories, syncChatHistory, validateChatSession, getSyncedStoryById } from './routes/storySync.js';
19
19
  import { setupProductionGitignore } from '../story-generator/productionGitignoreManager.js';
20
- import { getInMemoryStoryService } from '../story-generator/inMemoryStoryService.js';
20
+ import { getStoryService, getStorageType } from '../story-generator/storyServiceFactory.js';
21
21
  import { loadUserConfig } from '../story-generator/configLoader.js';
22
22
  import { loadConsiderations, considerationsToPrompt } from '../story-generator/considerationsLoader.js';
23
23
  import { DocumentationLoader } from '../story-generator/documentationLoader.js';
@@ -177,14 +177,14 @@ app.post('/story-ui/delete', async (req, res) => {
177
177
  return res.status(400).json({ error: 'chatId or storyId is required' });
178
178
  }
179
179
  console.log(`🗑️ Attempting to delete story: ${id}`);
180
- // First try in-memory deletion (production mode)
181
- const storyService = getInMemoryStoryService(config);
182
- const inMemoryDeleted = storyService.deleteStory(id);
183
- if (inMemoryDeleted) {
184
- console.log(`✅ Deleted story from memory: ${id}`);
180
+ // First try storage service deletion (production mode)
181
+ const storyService = await getStoryService(config);
182
+ const serviceDeleted = await storyService.deleteStory(id);
183
+ if (serviceDeleted) {
184
+ console.log(`✅ Deleted story from ${getStorageType()}: ${id}`);
185
185
  return res.json({
186
186
  success: true,
187
- message: 'Story deleted successfully from memory'
187
+ message: `Story deleted successfully from ${getStorageType()}`
188
188
  });
189
189
  }
190
190
  // If not found in memory, try file-system deletion (development mode)
@@ -240,7 +240,6 @@ app.get('/story-ui/redirects.js', (req, res) => {
240
240
  // Set up production-ready gitignore and directory structure on startup
241
241
  const config = loadUserConfig();
242
242
  const gitignoreManager = setupProductionGitignore(config);
243
- const storyService = getInMemoryStoryService(config);
244
243
  // Initialize URL redirect service
245
244
  const redirectService = new UrlRedirectService(process.cwd());
246
245
  const PORT = parseInt(process.env.PORT || '4001', 10);