API Integration Builder
Run ID: 69b6ee6e95d85cd597625a122026-03-29Development
PantheraHive BOS
BOS Dashboard

Workflow Execution Details

Workflow Name: API Integration Builder

Category: Development

Description: Generate code to integrate with external APIs

API Name: Stripe

Language: Node.js

Endpoints: charges, customers

Step: 1 of 2: generate_code


Generated Code Overview

This output provides Node.js code for integrating with the Stripe API, focusing on the customers and charges (specifically, PaymentIntents as the recommended modern approach) endpoints. The code is structured into a reusable module, stripeService.js, which encapsulates Stripe API interactions. It includes functions for common operations like creating, retrieving, updating, and listing resources, along with basic error handling and best practices for API key management.


Prerequisites & Setup

Before using the generated code, ensure the following are in place:

1. Install Node.js and npm

If you don't have Node.js installed, download it from [nodejs.org](https://nodejs.org/). npm (Node Package Manager) is included with Node.js.

2. Initialize Your Project

Navigate to your project directory in the terminal and initialize a new Node.js project:

text • 612 chars
*   `stripe`: The official Stripe Node.js client library.
*   `dotenv`: For securely loading environment variables from a `.env` file.

### 4. Obtain Your Stripe API Keys

You will need your **Stripe Secret Key**. This key should never be exposed in client-side code or committed to version control.
*   Go to your [Stripe Dashboard](https://dashboard.stripe.com/apikeys).
*   Copy your "Secret key" (starts with `sk_live_...` for live mode or `sk_test_...` for test mode).

### 5. Configure Environment Variables

Create a file named `.env` in the root of your project directory and add your Stripe Secret Key:
Sandboxed live preview

Key Considerations & Recommendations

  1. PaymentIntents vs. Charges:

* Recommendation: For new payment flows, always use PaymentIntents. They are designed to handle complex payment lifecycles, including strong customer authentication (SCA) requirements like 3D Secure, and provide better idempotency and state management.

* Charges API is simpler but less flexible and does not inherently support dynamic authentication flows. It's suitable for single-step charges where authentication is not a concern or for retrieving historical charge data.

  1. API Key Security:

* Never hardcode your Stripe Secret Key directly in your code. Always use environment variables (e.g., via dotenv in Node.js) or a secure configuration management system.

* Your Publishable Key (pk_live_... or pk_test_...) is safe to include in client-side code (e.g., in your frontend JavaScript using Stripe.js).

  1. Error Handling:

* The provided code includes basic try...catch blocks. In a production application, you should implement more robust error handling, including logging, specific error types from Stripe (e.g., StripeCardError), and user-friendly error messages.

* Consider implementing retries for transient network errors.

  1. Idempotency:

* Stripe supports idempotency keys to prevent duplicate API requests (e.g., if a network error occurs and you retry a createPaymentIntent call). While not explicitly shown in this basic example, for critical operations like creating charges or PaymentIntents, it's highly recommended to generate and pass a unique idempotency key with each request.

* Example: await stripe.paymentIntents.create(data, { idempotencyKey: 'your_unique_key_here' });

  1. Webhooks:

* For real-time updates on payment status, refunds, subscriptions, etc., implement Stripe Webhooks. Your server receives notifications from Stripe when events occur (e.g., payment_intent.succeeded, charge.refunded). This is crucial for maintaining accurate state in your application.

  1. Frontend Integration (Stripe.js):

* This backend code handles server-side API calls. For collecting sensitive payment information (card details), you must use Stripe.js on your frontend. Stripe.js tokenizes card details directly from the user's browser, ensuring PCI compliance and preventing sensitive data from touching your server.

* The client_secret returned by createPaymentIntent is used by Stripe.js on the frontend to confirm the payment.

  1. Pagination:

* When listing resources (e.g., listCustomers, listPaymentIntents), Stripe API responses are paginated. The provided list functions retrieve only the first page. For large datasets, implement proper pagination logic using limit, starting_after, and ending_before parameters, or use the autoPagingIterator available in the Stripe Node.js library.


Next Steps

  1. Integrate into Your Application: Adapt the stripeService.js module into your existing Node.js application (e.g., an Express.js server).
  2. Frontend Implementation:

* Set up your frontend to use Stripe.js for securely collecting payment details and confirming PaymentIntents.

* Send the client_secret from your backend's createPaymentIntent response to your frontend.

  1. Webhook Setup:

* Configure webhooks in your Stripe Dashboard.

* Create a webhook endpoint in your Node.js application to listen for and process Stripe events.

  1. Testing:

* Thoroughly test all API interactions using Stripe's test cards and test modes.

* Simulate various scenarios: successful payments, declined cards, 3D Secure challenges, refunds, etc.

  1. Expand Functionality:

* Implement additional Stripe features as needed (e.g., Refunds, Subscriptions, Connect, Payouts).

* Add more specific validation and error handling logic tailored to your application's requirements.

Step 2: projectmanager

PantheraHive Workflow Execution: API Integration Builder

Workflow Output: Stripe API Integration (Node.js)

This document provides a comprehensive guide and code examples for integrating with the Stripe API using Node.js, specifically focusing on the charges and customers endpoints.


1. Project Overview

This output provides a ready-to-use Node.js project structure and code snippets for interacting with the Stripe API. The primary goal is to enable your application to manage customer information and process payments (charges) effectively.

Key Features:

  • Secure API Key Management: Utilizing environment variables.
  • Modular Code Structure: Separating API logic from application logic.
  • Error Handling: Specific to Stripe API responses.
  • CRUD Operations: For customers (Create, Retrieve, Update, Delete, List).
  • Payment Operations: For charges (Create, Retrieve, List).

2. Project Setup & Dependencies

To get started, create a new Node.js project and install the necessary dependencies.

2.1. Initialize Project


# Create a new directory for your project
mkdir stripe-integration-project
cd stripe-integration-project

# Initialize a new Node.js project
npm init -y

2.2. Install Dependencies

You'll need the stripe SDK for Node.js, dotenv for environment variable management, and optionally express if you plan to build a web server around this integration.


npm install stripe dotenv express

2.3. Project Structure Recommendation

A clean project structure helps manage your code. Here’s a recommended layout:


stripe-integration-project/
├── .env                  # Environment variables (Stripe secret key)
├── package.json
├── package-lock.json
├── index.js              # Main application entry point (example usage)
└── src/
    ├── config/
    │   └── stripe.js     # Stripe client initialization
    └── services/
        └── stripeService.js # Core Stripe API interaction logic

3. API Key Management

Security is paramount. Never hardcode your Stripe secret keys directly in your code. Use environment variables.

3.1. Obtain Stripe API Keys

  1. Log in to your [Stripe Dashboard](https://dashboard.stripe.com/).
  2. Navigate to Developers > API keys.
  3. You'll find your Publishable key (starts with pk_test_ or pk_live_) and Secret key (starts with sk_test_ or sk_live_).
  4. For development and testing, use the test keys. For production, use the live keys.

3.2. Create .env File

In the root of your project (stripe-integration-project/.env), create a .env file and add your Stripe secret key:


STRIPE_SECRET_KEY=sk_test_YOUR_STRIPE_SECRET_KEY

Important: Add .env to your .gitignore file to prevent it from being committed to version control.


# .gitignore
.env
node_modules/

4. Stripe Client Initialization

Initialize the Stripe client using your secret key.

4.1. src/config/stripe.js


// src/config/stripe.js
require('dotenv').config(); // Load environment variables

if (!process.env.STRIPE_SECRET_KEY) {
  console.error("Error: STRIPE_SECRET_KEY is not set in environment variables.");
  process.exit(1); // Exit if critical env var is missing
}

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

module.exports = stripe;

5. API Integration Code Examples

This section provides the core logic for interacting with the charges and customers endpoints.

5.1. src/services/stripeService.js

This module will encapsulate all Stripe API calls.


// src/services/stripeService.js
const stripe = require('../config/stripe');

/**
 * Handles common Stripe API errors.
 * @param {Error} error - The error object from Stripe.
 * @returns {object} An object containing error details.
 */
const handleStripeError = (error) => {
  if (error.type === 'StripeCardError') {
    // A declined card error
    return {
      success: false,
      code: error.code,
      message: error.message,
      param: error.param,
      statusCode: error.statusCode,
      type: 'StripeCardError'
    };
  } else if (error.type === 'StripeRateLimitError') {
    // Too many requests made to the API too quickly
    return {
      success: false,
      code: error.code,
      message: 'Too many requests. Please try again later.',
      statusCode: error.statusCode,
      type: 'StripeRateLimitError'
    };
  } else if (error.type === 'StripeInvalidRequestError') {
    // Invalid parameters were supplied to Stripe's API
    return {
      success: false,
      code: error.code,
      message: error.message,
      param: error.param,
      statusCode: error.statusCode,
      type: 'StripeInvalidRequestError'
    };
  } else if (error.type === 'StripeAPIError') {
    // An error occurred internally with Stripe's API
    return {
      success: false,
      code: error.code,
      message: 'Stripe API error. Please try again later.',
      statusCode: error.statusCode,
      type: 'StripeAPIError'
    };
  } else if (error.type === 'StripeAuthenticationError') {
    // You probably used an incorrect API key
    return {
      success: false,
      code: error.code,
      message: 'Authentication with Stripe failed. Check your API key.',
      statusCode: error.statusCode,
      type: 'StripeAuthenticationError'
    };
  } else {
    // Generic error
    return {
      success: false,
      code: 'UNKNOWN_ERROR',
      message: error.message || 'An unexpected error occurred.',
      statusCode: error.statusCode || 500,
      type: 'GenericError'
    };
  }
};

// --- Customer Operations ---

/**
 * Creates a new customer in Stripe.
 * @param {object} customerData - Customer details (e.g., email, name, description).
 * @returns {Promise<object>} The created customer object or an error.
 */
const createCustomer = async (customerData) => {
  try {
    const customer = await stripe.customers.create(customerData);
    return { success: true, customer };
  } catch (error) {
    console.error('Error creating customer:', error);
    return handleStripeError(error);
  }
};

/**
 * Retrieves a customer by ID.
 * @param {string} customerId - The ID of the customer to retrieve.
 * @returns {Promise<object>} The customer object or an error.
 */
const retrieveCustomer = async (customerId) => {
  try {
    const customer = await stripe.customers.retrieve(customerId);
    if (!customer || customer.deleted) {
      return { success: false, message: 'Customer not found or deleted.', statusCode: 404 };
    }
    return { success: true, customer };
  } catch (error) {
    console.error(`Error retrieving customer ${customerId}:`, error);
    return handleStripeError(error);
  }
};

/**
 * Updates an existing customer.
 * @param {string} customerId - The ID of the customer to update.
 * @param {object} updates - An object containing fields to update.
 * @returns {Promise<object>} The updated customer object or an error.
 */
const updateCustomer = async (customerId, updates) => {
  try {
    const customer = await stripe.customers.update(customerId, updates);
    return { success: true, customer };
  } catch (error) {
    console.error(`Error updating customer ${customerId}:`, error);
    return handleStripeError(error);
  }
};

/**
 * Deletes a customer.
 * @param {string} customerId - The ID of the customer to delete.
 * @returns {Promise<object>} Confirmation of deletion or an error.
 */
const deleteCustomer = async (customerId) => {
  try {
    const confirmation = await stripe.customers.del(customerId);
    return { success: true, confirmation };
  } catch (error) {
    console.error(`Error deleting customer ${customerId}:`, error);
    return handleStripeError(error);
  }
};

/**
 * Lists customers.
 * @param {object} [options] - Options for listing customers (e.g., limit, starting_after).
 * @returns {Promise<object>} A list of customer objects or an error.
 */
const listCustomers = async (options = {}) => {
  try {
    const customers = await stripe.customers.list(options);
    return { success: true, customers: customers.data, has_more: customers.has_more };
  } catch (error) {
    console.error('Error listing customers:', error);
    return handleStripeError(error);
  }
};

// --- Charge Operations ---

/**
 * Creates a new charge. This typically happens after a payment method (e.g., card token or PaymentMethod ID)
 * has been collected on the client-side.
 * @param {object} chargeData - Details for the charge (e.g., amount, currency, source/payment_method, customer, description).
 * @returns {Promise<object>} The created charge object or an error.
 */
const createCharge = async (chargeData) => {
  try {
    const charge = await stripe.charges.create(chargeData);
    return { success: true, charge };
  } catch (error) {
    console.error('Error creating charge:', error);
    return handleStripeError(error);
  }
};

/**
 * Retrieves a charge by ID.
 * @param {string} chargeId - The ID of the charge to retrieve.
 * @returns {Promise<object>} The charge object or an error.
 */
const retrieveCharge = async (chargeId) => {
  try {
    const charge = await stripe.charges.retrieve(chargeId);
    if (!charge) {
      return { success: false, message: 'Charge not found.', statusCode: 404 };
    }
    return { success: true, charge };
  } catch (error) {
    console.error(`Error retrieving charge ${chargeId}:`, error);
    return handleStripeError(error);
  }
};

/**
 * Lists charges.
 * @param {object} [options] - Options for listing charges (e.g., limit, starting_after, customer).
 * @returns {Promise<object>} A list of charge objects or an error.
 */
const listCharges = async (options = {}) => {
  try {
    const charges = await stripe.charges.list(options);
    return { success: true, charges: charges.data, has_more: charges.has_more };
  } catch (error) {
    console.error('Error listing charges:', error);
    return handleStripeError(error);
  }
};

module.exports = {
  createCustomer,
  retrieveCustomer,
  updateCustomer,
  deleteCustomer,
  listCustomers,
  createCharge,
  retrieveCharge,
  listCharges,
};

5.2. index.js (Example Usage)

This file demonstrates how to use the stripeService functions.


// index.js
const stripeService = require('./src/services/stripeService');

const runExamples = async () => {
  console.log('--- Stripe API Integration Examples ---');

  // --- Customer Examples ---
  console.log('\n--- Customer Operations ---');

  // 1. Create a Customer
  console.log('Creating a new customer...');
  const newCustomer = await stripeService.createCustomer({
    email: `test-customer-${Date.now()}@example.com`,
    name: 'Jane Doe',
    description: 'Test customer for API integration',
    phone: '+15551234567'
  });

  if (newCustomer.success) {
    console.log('Customer created successfully:', newCustomer.customer.id, newCustomer.customer.email);
    const customerId = newCustomer.customer.id;

    // 2. Retrieve Customer
    console.log(`\nRetrieving customer ${customerId}...`);
    const retrievedCustomer = await stripeService.retrieveCustomer(customerId);
    if (retrievedCustomer.success) {
      console.log('Customer retrieved:', retrievedCustomer.customer.name);
    } else {
      console.error('Failed to retrieve customer:', retrievedCustomer.message);
    }

    // 3. Update Customer
    console.log(`\nUpdating customer ${customerId}...`);
    const updatedCustomer = await stripeService.updateCustomer(customerId, {
      description: 'Updated description for test customer',
      metadata: {
        loyalty_program: 'gold'
      }
    });
    if (updatedCustomer.success) {
      console.log('Customer updated:', updatedCustomer.customer.description, updatedCustomer.customer.metadata);
    } else {
      console.error('Failed to update customer:', updatedCustomer.message);
    }

    // 4. List Customers (e.g., retrieve the first 3 customers)
    console.log('\nListing customers...');
    const customerList = await stripeService.listCustomers({ limit: 3 });
    if (customerList.success) {
      console.log(`Listed ${customerList.customers.length} customers.`);
      customerList.customers.forEach(cust => console.log(`- ${cust.id}: ${cust.email}`));
    } else {
      console.error('Failed to list customers:', customerList.message);
    }

    // --- Charge Examples (Requires a valid payment method ID or token) ---
    // NOTE: For a real application, you would collect payment method details
    // using Stripe Elements on the client-side and send the resulting
    // `payment_method.id` or `token.id` to your server.
    // For this example, we'll simulate using a test token or a PaymentMethod ID
    // that could be attached to the customer.

    console.log('\n--- Charge Operations ---');

    // Example: Create a Payment Method and attach to customer (simulated for server-side)
    // In a real scenario, this would be done on the client-side using Stripe.js
    // and then sent to the server. For server-side testing, you can use test tokens.
    // For actual charges, you'd typically use a PaymentMethod ID or a Source ID.
    // Let's assume we have a test payment method ID from a previous client-side interaction.
    // Replace 'pm_card_visa' with a valid test PaymentMethod ID or a token ID from client-side.
    const testPaymentMethodId = 'pm_card_visa'; // Use a valid test PM ID from Stripe docs or your tests

    // 1. Create a Charge
    console.log('Creating a new charge...');
    const newCharge = await stripeService.createCharge({
      amount: 2000, // Amount in cents ($20.00)
      currency: 'usd',
      customer: customerId, // Associate charge with the created customer
      payment_method: testPaymentMethodId, // Use a valid payment method ID
      confirm: true, // Confirm the payment immediately
      description: 'Example charge for product A',
      metadata: {
        order_id: 'ORDER12345'
      }
    });

    if (newCharge.success) {
      console.log('Charge created successfully:', newCharge.charge.id, newCharge.charge.status);
      const chargeId = newCharge.charge.id;

      // 2. Retrieve Charge
      console.log(`\nRetrieving charge ${chargeId}...`);
      const retrievedCharge = await stripeService.retrieveCharge(chargeId);
      if (retrievedCharge.success) {
        console.log('Charge retrieved:', retrievedCharge.charge.amount, retrievedCharge.charge.currency);
      } else {
        console.error('Failed to retrieve charge:', retrievedCharge.message);
      }

      // 3. List Charges (e.g., retrieve the last 2 charges for this customer)
      console.log('\nListing charges...');
      const chargeList = await stripeService.listCharges({ customer: customerId, limit: 2 });
      if (chargeList.success) {
        console.log(`Listed ${chargeList.charges.length} charges for customer ${customerId}.`);
        chargeList.charges.forEach(chg => console.log(`- ${chg.id}: ${chg.amount / 100} ${chg.currency.toUpperCase()} (${chg.status})`));
      } else {
        console.error('Failed to list charges:', chargeList.message);
      }
    } else {
      console.error('Failed to create charge:', newCharge.message);
      // Log more details if available
      if (newCharge.code) console.error('Stripe error code:', newCharge.code);
      if (newCharge.param) console.error('Stripe error param:', newCharge.param);
    }

    // 5. Delete Customer (optional, uncomment to clean up test data)
    // console.log(`\nDeleting customer ${customerId}...`);
    // const deletedCustomer = await stripeService.deleteCustomer(customerId);
    // if (deletedCustomer.success) {
    //   console.log('Customer deleted successfully:', deletedCustomer.confirmation.id);
    // } else {
    //   console.error('Failed to delete customer:', deletedCustomer.message);
    // }

  } else {
    console.error('Failed to create customer for example run:', newCustomer.message);
  }

  console.log('\n--- End of Examples ---');
};

runExamples();

5.3. Running the Examples

  1. Make sure your .env file has STRIPE_SECRET_KEY set.
  2. From your project root, run:

    node index.js

This will execute the example operations and print the results to your console.

6. Error Handling Strategy

The stripeService.js includes a handleStripeError function to centralize error processing. Stripe's API returns specific error types (e.g., StripeCardError, StripeInvalidRequestError), which are crucial for providing meaningful feedback to users or for internal debugging.

Recommendations:

  • Log Errors: Always log the full error object for debugging purposes.
  • User-Friendly Messages: Translate Stripe errors into messages your users can understand (e.g., "Your card was declined" instead of a raw error code).
  • Retry Logic: Implement exponential backoff for transient errors like StripeRateLimitError or StripeAPIError.
  • Specific Handling: Differentiate between client-side errors (e.g., invalid input) and server-side errors (e.g., Stripe API issues).

7. Security Best Practices

  • API Key Protection:

* Never commit .env to Git. Use .gitignore.

* Never expose your secret key to the client-side. All operations requiring the secret key must be performed on your secure backend server.

  • HTTPS: Always use HTTPS for all communication with your server and Stripe.
  • Input Validation: Sanitize and validate all user inputs before sending them to the Stripe API.
  • PCI Compliance: While Stripe handles much of the complexity, if you ever handle raw card data on your servers, you must be PCI compliant. Using Stripe Elements/Stripe.js for client-side tokenization is the recommended approach to minimize your PCI scope.
  • Webhooks Security: If you implement webhooks (highly recommended for asynchronous events), verify webhook signatures to ensure the requests are genuinely from Stripe and haven't been tampered with.

8. Testing Recommendations

  • Stripe Test Keys: Always use your sk_test_ keys for development and testing. Stripe provides a comprehensive set of [test card numbers](https://stripe.com/docs/testing) to simulate various scenarios (success, decline, fraud, etc.).
  • Unit Tests: Use a testing framework (e.g., Jest, Mocha) to write unit tests for your stripeService.js functions. Mock the stripe object to avoid making actual API calls during unit tests.
  • Integration Tests: Create a separate test environment with a dedicated set of Stripe test keys. Run integration tests that make actual API calls to ensure end-to-end functionality.
  • Idempotency Keys: For operations that modify data (like createCharge), use idempotency keys to ensure that retrying a request due to a network error doesn't result in duplicate operations. The Stripe Node.js library automatically adds idempotency keys to requests by default, but it's good to be aware of.

9. Further Enhancements & Considerations

  • Stripe Elements/Stripe.js: For collecting payment information securely on the frontend, integrating Stripe Elements is essential. This workflow focuses on the backend, but a complete payment solution requires a frontend component.
  • Webhooks: Implement webhooks to receive real-time notifications about events in your Stripe account (e.g., payment_intent.succeeded, charge.refunded, customer.subscription.created). This is crucial for handling asynchronous processes and keeping your application's state synchronized with Stripe.
  • Payment Intents: For modern payment flows, Stripe recommends using [Payment Intents](https://stripe.com/docs/payments/payment-intents) instead of direct charges. Payment Intents handle dynamic authentication methods (like 3D Secure) and provide a more robust payment lifecycle.
  • Subscriptions: If your application involves recurring billing, explore Stripe Subscriptions.
  • Connect: If you're building a platform that facilitates payments between multiple parties, look into Stripe Connect.
  • Logging and Monitoring: Integrate robust logging and monitoring for all Stripe API interactions to quickly identify and troubleshoot issues.
  • Configuration Management: For complex applications, consider using a configuration management library to handle different environments (development, staging, production).

10. Conclusion

This output provides a solid foundation for integrating Stripe's charges and customers endpoints into your Node.js application. By following the recommended practices for security, error handling, and project structure, you can build a reliable and scalable payment integration. Remember to consult the official [Stripe API Documentation](https://stripe.com/docs/api) for the most up-to-date information and to explore advanced features.

api_integration_builder.txt
Download source file
Copy all content
Full output as text
Download ZIP
IDE-ready project ZIP
Copy share link
Permanent URL for this run
Get Embed Code
Embed this result on any website
Print / Save PDF
Use browser print dialog
\n\n\n"); var hasSrcMain=Object.keys(extracted).some(function(k){return k.indexOf("src/main")>=0;}); if(!hasSrcMain) zip.file(folder+"src/main."+ext,"import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './index.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n \n \n)\n"); var hasSrcApp=Object.keys(extracted).some(function(k){return k==="src/App."+ext||k==="App."+ext;}); if(!hasSrcApp) zip.file(folder+"src/App."+ext,"import React from 'react'\nimport './App.css'\n\nfunction App(){\n return(\n
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n
\n )\n}\nexport default App\n"); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e}\n.app{min-height:100vh;display:flex;flex-direction:column}\n.app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px}\nh1{font-size:2.5rem;font-weight:700}\n"); zip.file(folder+"src/App.css",""); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/pages/.gitkeep",""); zip.file(folder+"src/hooks/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\n## Open in IDE\nOpen the project folder in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Vue (Vite + Composition API + TypeScript) --- */ function buildVue(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "type": "module",\n "scripts": {\n "dev": "vite",\n "build": "vue-tsc -b && vite build",\n "preview": "vite preview"\n },\n "dependencies": {\n "vue": "^3.5.13",\n "vue-router": "^4.4.5",\n "pinia": "^2.3.0",\n "axios": "^1.7.9"\n },\n "devDependencies": {\n "@vitejs/plugin-vue": "^5.2.1",\n "typescript": "~5.7.3",\n "vite": "^6.0.5",\n "vue-tsc": "^2.2.0"\n }\n}\n'); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { resolve } from 'path'\n\nexport default defineConfig({\n plugins: [vue()],\n resolve: { alias: { '@': resolve(__dirname,'src') } }\n})\n"); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]}\n'); zip.file(folder+"tsconfig.app.json",'{\n "compilerOptions":{\n "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"],\n "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true,\n "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue",\n "strict":true,"paths":{"@/*":["./src/*"]}\n },\n "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"]\n}\n'); zip.file(folder+"env.d.ts","/// \n"); zip.file(folder+"index.html","\n\n\n \n \n "+slugTitle(pn)+"\n\n\n
\n \n\n\n"); var hasMain=Object.keys(extracted).some(function(k){return k==="src/main.ts"||k==="main.ts";}); if(!hasMain) zip.file(folder+"src/main.ts","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\nimport './assets/main.css'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n"); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue","\n\n\n\n\n"); zip.file(folder+"src/assets/main.css","*{margin:0;padding:0;box-sizing:border-box}body{font-family:system-ui,sans-serif;background:#fff;color:#213547}\n"); zip.file(folder+"src/components/.gitkeep",""); zip.file(folder+"src/views/.gitkeep",""); zip.file(folder+"src/stores/.gitkeep",""); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\n## Build\n\`\`\`bash\nnpm run build\n\`\`\`\n\nOpen in VS Code or WebStorm.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n"); } /* --- Angular (v19 standalone) --- */ function buildAngular(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var C=cc(pn); var sel=pn.replace(/_/g,"-"); var extracted=extractCode(panelTxt); zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "0.0.0",\n "scripts": {\n "ng": "ng",\n "start": "ng serve",\n "build": "ng build",\n "test": "ng test"\n },\n "dependencies": {\n "@angular/animations": "^19.0.0",\n "@angular/common": "^19.0.0",\n "@angular/compiler": "^19.0.0",\n "@angular/core": "^19.0.0",\n "@angular/forms": "^19.0.0",\n "@angular/platform-browser": "^19.0.0",\n "@angular/platform-browser-dynamic": "^19.0.0",\n "@angular/router": "^19.0.0",\n "rxjs": "~7.8.0",\n "tslib": "^2.3.0",\n "zone.js": "~0.15.0"\n },\n "devDependencies": {\n "@angular-devkit/build-angular": "^19.0.0",\n "@angular/cli": "^19.0.0",\n "@angular/compiler-cli": "^19.0.0",\n "typescript": "~5.6.0"\n }\n}\n'); zip.file(folder+"angular.json",'{\n "$schema": "./node_modules/@angular/cli/lib/config/schema.json",\n "version": 1,\n "newProjectRoot": "projects",\n "projects": {\n "'+pn+'": {\n "projectType": "application",\n "root": "",\n "sourceRoot": "src",\n "prefix": "app",\n "architect": {\n "build": {\n "builder": "@angular-devkit/build-angular:application",\n "options": {\n "outputPath": "dist/'+pn+'",\n "index": "src/index.html",\n "browser": "src/main.ts",\n "tsConfig": "tsconfig.app.json",\n "styles": ["src/styles.css"],\n "scripts": []\n }\n },\n "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"}\n }\n }\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "compileOnSave": false,\n "compilerOptions": {"baseUrl":"./","outDir":"./dist/out-tsc","forceConsistentCasingInFileNames":true,"strict":true,"noImplicitOverride":true,"noPropertyAccessFromIndexSignature":true,"noImplicitReturns":true,"noFallthroughCasesInSwitch":true,"paths":{"@/*":["src/*"]},"skipLibCheck":true,"esModuleInterop":true,"sourceMap":true,"declaration":false,"experimentalDecorators":true,"moduleResolution":"bundler","importHelpers":true,"target":"ES2022","module":"ES2022","useDefineForClassFields":false,"lib":["ES2022","dom"]},\n "references":[{"path":"./tsconfig.app.json"}]\n}\n'); zip.file(folder+"tsconfig.app.json",'{\n "extends":"./tsconfig.json",\n "compilerOptions":{"outDir":"./dist/out-tsc","types":[]},\n "files":["src/main.ts"],\n "include":["src/**/*.d.ts"]\n}\n'); zip.file(folder+"src/index.html","\n\n\n \n "+slugTitle(pn)+"\n \n \n \n\n\n \n\n\n"); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser';\nimport { appConfig } from './app/app.config';\nimport { AppComponent } from './app/app.component';\n\nbootstrapApplication(AppComponent, appConfig)\n .catch(err => console.error(err));\n"); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; }\n"); var hasComp=Object.keys(extracted).some(function(k){return k.indexOf("app.component")>=0;}); if(!hasComp){ zip.file(folder+"src/app/app.component.ts","import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n standalone: true,\n imports: [RouterOutlet],\n templateUrl: './app.component.html',\n styleUrl: './app.component.css'\n})\nexport class AppComponent {\n title = '"+pn+"';\n}\n"); zip.file(folder+"src/app/app.component.html","
\n
\n

"+slugTitle(pn)+"

\n

Built with PantheraHive BOS

\n
\n \n
\n"); zip.file(folder+"src/app/app.component.css",".app-header{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;gap:16px}h1{font-size:2.5rem;font-weight:700;color:#6366f1}\n"); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';\nimport { provideRouter } from '@angular/router';\nimport { routes } from './app.routes';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n provideZoneChangeDetection({ eventCoalescing: true }),\n provideRouter(routes)\n ]\n};\n"); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router';\n\nexport const routes: Routes = [];\n"); Object.keys(extracted).forEach(function(p){ var fp=p.startsWith("src/")?p:"src/"+p; zip.file(folder+fp,extracted[p]); }); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\nng serve\n# or: npm start\n\`\`\`\n\n## Build\n\`\`\`bash\nng build\n\`\`\`\n\nOpen in VS Code with Angular Language Service extension.\n"); zip.file(folder+".gitignore","node_modules/\ndist/\n.env\n.DS_Store\n*.local\n.angular/\n"); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var reqMap={"numpy":"numpy","pandas":"pandas","sklearn":"scikit-learn","tensorflow":"tensorflow","torch":"torch","flask":"flask","fastapi":"fastapi","uvicorn":"uvicorn","requests":"requests","sqlalchemy":"sqlalchemy","pydantic":"pydantic","dotenv":"python-dotenv","PIL":"Pillow","cv2":"opencv-python","matplotlib":"matplotlib","seaborn":"seaborn","scipy":"scipy"}; var reqs=[]; Object.keys(reqMap).forEach(function(k){if(src.indexOf("import "+k)>=0||src.indexOf("from "+k)>=0)reqs.push(reqMap[k]);}); var reqsTxt=reqs.length?reqs.join("\n"):"# add dependencies here\n"; zip.file(folder+"main.py",src||"# "+title+"\n# Generated by PantheraHive BOS\n\nprint(title+\" loaded\")\n"); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -r requirements.txt\n\`\`\`\n\n## Run\n\`\`\`bash\npython main.py\n\`\`\`\n"); zip.file(folder+".gitignore",".venv/\n__pycache__/\n*.pyc\n.env\n.DS_Store\n"); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^\`\`\`[\w]*\n?/m,"").replace(/\n?\`\`\`$/m,"").trim(); var depMap={"mongoose":"^8.0.0","dotenv":"^16.4.5","axios":"^1.7.9","cors":"^2.8.5","bcryptjs":"^2.4.3","jsonwebtoken":"^9.0.2","socket.io":"^4.7.4","uuid":"^9.0.1","zod":"^3.22.4","express":"^4.18.2"}; var deps={}; Object.keys(depMap).forEach(function(k){if(src.indexOf(k)>=0)deps[k]=depMap[k];}); if(!deps["express"])deps["express"]="^4.18.2"; var pkgJson=JSON.stringify({"name":pn,"version":"1.0.0","main":"src/index.js","scripts":{"start":"node src/index.js","dev":"nodemon src/index.js"},"dependencies":deps,"devDependencies":{"nodemon":"^3.0.3"}},null,2)+"\n"; zip.file(folder+"package.json",pkgJson); var fallback="const express=require(\"express\");\nconst app=express();\napp.use(express.json());\n\napp.get(\"/\",(req,res)=>{\n res.json({message:\""+title+" API\"});\n});\n\nconst PORT=process.env.PORT||3000;\napp.listen(PORT,()=>console.log(\"Server on port \"+PORT));\n"; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000\n"); zip.file(folder+".gitignore","node_modules/\n.env\n.DS_Store\n"); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Run\n\`\`\`bash\nnpm run dev\n\`\`\`\n"); } /* --- Vanilla HTML --- */ function buildVanillaHtml(zip,folder,app,code){ var title=slugTitle(app); var isFullDoc=code.trim().toLowerCase().indexOf("=0||code.trim().toLowerCase().indexOf("=0; var indexHtml=isFullDoc?code:"\n\n\n\n\n"+title+"\n\n\n\n"+code+"\n\n\n\n"; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e}\n"); zip.file(folder+"script.js","/* "+title+" — scripts */\n"); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\n## Open\nDouble-click \`index.html\` in your browser.\n\nOr serve locally:\n\`\`\`bash\nnpx serve .\n# or\npython3 -m http.server 3000\n\`\`\`\n"); zip.file(folder+".gitignore",".DS_Store\nnode_modules/\n.env\n"); } /* ===== MAIN ===== */ var sc=document.createElement("script"); sc.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"; sc.onerror=function(){ if(lbl)lbl.textContent="Download ZIP"; alert("JSZip load failed — check connection."); }; sc.onload=function(){ var zip=new JSZip(); var base=(_phFname||"output").replace(/\.[^.]+$/,""); var app=base.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; var folder=app+"/"; var vc=document.getElementById("panel-content"); var panelTxt=vc?(vc.innerText||vc.textContent||""):""; var lang=detectLang(_phCode,panelTxt); if(_phIsHtml){ buildVanillaHtml(zip,folder,app,_phCode); } else if(lang==="flutter"){ buildFlutter(zip,folder,app,_phCode,panelTxt); } else if(lang==="react-native"){ buildReactNative(zip,folder,app,_phCode,panelTxt); } else if(lang==="swift"){ buildSwift(zip,folder,app,_phCode,panelTxt); } else if(lang==="kotlin"){ buildKotlin(zip,folder,app,_phCode,panelTxt); } else if(lang==="react"){ buildReact(zip,folder,app,_phCode,panelTxt); } else if(lang==="vue"){ buildVue(zip,folder,app,_phCode,panelTxt); } else if(lang==="angular"){ buildAngular(zip,folder,app,_phCode,panelTxt); } else if(lang==="python"){ buildPython(zip,folder,app,_phCode); } else if(lang==="node"){ buildNode(zip,folder,app,_phCode); } else { /* Document/content workflow */ var title=app.replace(/_/g," "); var md=_phAll||_phCode||panelTxt||"No content"; zip.file(folder+app+".md",md); var h=""+title+""; h+="

"+title+"

"; var hc=md.replace(/&/g,"&").replace(//g,">"); hc=hc.replace(/^### (.+)$/gm,"

$1

"); hc=hc.replace(/^## (.+)$/gm,"

$1

"); hc=hc.replace(/^# (.+)$/gm,"

$1

"); hc=hc.replace(/\*\*(.+?)\*\*/g,"$1"); hc=hc.replace(/\n{2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+"\n\nGenerated by PantheraHive BOS.\n\nFiles:\n- "+app+".md (Markdown)\n- "+app+".html (styled HTML)\n"); } zip.generateAsync({type:"blob"}).then(function(blob){ var a=document.createElement("a"); a.href=URL.createObjectURL(blob); a.download=app+".zip"; a.click(); URL.revokeObjectURL(a.href); if(lbl)lbl.textContent="Download ZIP"; }); }; document.head.appendChild(sc); } function phShare(){navigator.clipboard.writeText(window.location.href).then(function(){var el=document.getElementById("ph-share-lbl");if(el){el.textContent="Link copied!";setTimeout(function(){el.textContent="Copy share link";},2500);}});}function phEmbed(){var runId=window.location.pathname.split("/").pop().replace(".html","");var embedUrl="https://pantherahive.com/embed/"+runId;var code='';navigator.clipboard.writeText(code).then(function(){var el=document.getElementById("ph-embed-lbl");if(el){el.textContent="Embed code copied!";setTimeout(function(){el.textContent="Get Embed Code";},2500);}});}