API Rate Limiter
Run ID: 69cd130d3e7fb09ff16a7b0e2026-04-01Development
PantheraHive BOS
BOS Dashboard

API Rate Limiter Implementation (Step 2 of 3: Code Generation)

This document provides a comprehensive and detailed implementation for an API Rate Limiter, focusing on robust, production-ready code using Python and Redis. The solution incorporates two popular and effective rate limiting algorithms: Fixed Window Counter and Sliding Window Log.

1. Introduction to API Rate Limiting

API Rate Limiting is a critical component for managing API usage, protecting backend services from abuse, and ensuring fair access for all users. It restricts the number of requests a user or client can make to an API within a defined timeframe.

Key Benefits:

2. Rate Limiting Algorithms Covered

We will implement two distinct rate limiting algorithms, each with its own characteristics:

2.1 Fixed Window Counter

2.2 Sliding Window Log

3. Chosen Technology Stack

4. Prerequisites

Before running the code, ensure you have the following installed:

text • 191 chars
### 5. Core RateLimiter Class (Python with Redis)

This class encapsulates the logic for both Fixed Window Counter and Sliding Window Log algorithms, leveraging Redis for state management.

Sandboxed live preview

This document outlines a comprehensive architecture plan for an API Rate Limiter and provides a detailed study plan to facilitate its understanding and implementation. This deliverable addresses the core requirements for building a robust, scalable, and efficient rate limiting system.


API Rate Limiter Architecture Plan

1. Introduction and Overview

An API Rate Limiter is a crucial component in modern microservice architectures, designed to control the frequency of requests made by clients to an API. Its primary goals are to:

  • Prevent Abuse: Protect backend services from malicious attacks like Denial-of-Service (DoS) and brute-force attempts.
  • Ensure Fair Usage: Distribute API resources equitably among all consumers.
  • Control Costs: Manage infrastructure load, especially for services with usage

python

flask_app.py

from flask import Flask, request, jsonify

import redis

from functools import wraps

from typing import Optional

Assuming RateLimiter class is in rate_limiter.py or defined above

from rate_limiter_module import RateLimiter # if in a separate file

For this example, we'll assume the RateLimiter class is available in the same scope.

--- Redis and RateLimiter Initialization ---

redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

rate_limiter = RateLimiter(redis_client, default_limit=5, default_window=60) # 5 requests per 60 seconds by default

app = Flask(__name__)

def rate_limit(limit: int, window: int, algorithm:

gemini Output

As a professional deliverable from PantheraHive, this document provides a comprehensive overview of API Rate Limiters, their importance, common implementation strategies, and best practices for both API providers and consumers.


API Rate Limiter: Comprehensive Overview and Best Practices

1. Introduction to API Rate Limiters

An API Rate Limiter is a mechanism that controls the number of requests a user or client can make to an API within a specified time window. Its primary purpose is to regulate traffic, prevent abuse, ensure fair resource allocation, and maintain the stability and performance of the API service.

2. Why API Rate Limiting is Crucial

Implementing robust API rate limiting offers significant benefits for both API providers and consumers:

  • Security & Abuse Prevention:

* DDoS Attack Mitigation: Protects against denial-of-service attacks by blocking excessive requests from malicious sources.

* Brute-Force Attack Prevention: Limits attempts to guess credentials, API keys, or other sensitive information.

* Data Scraping Prevention: Deters automated bots from excessively scraping data.

  • System Stability & Performance:

* Resource Protection: Prevents a single client from monopolizing server resources (CPU, memory, database connections), ensuring availability for all users.

* Load Management: Smooths out traffic spikes, preventing backend services from becoming overloaded and crashing.

* Predictable Performance: Helps maintain consistent response times for legitimate users.

  • Cost Management:

* Infrastructure Savings: Reduces the need for over-provisioning infrastructure to handle unpredictable spikes in traffic.

* Bandwidth Control: Limits data transfer costs associated with excessive requests.

  • Fair Usage & Monetization:

* Fair Access: Ensures that all legitimate users receive a fair share of API resources.

* Tiered Services: Enables providers to offer different access levels (e.g., free, premium) with varying rate limits, facilitating monetization strategies.

3. Key Concepts and Components

Understanding these fundamental concepts is essential for designing and interacting with rate-limited APIs:

  • Throttling: The process of intentionally slowing down or regulating the flow of requests.
  • Quota/Limit: The maximum number of requests allowed within a specific time frame (e.g., 100 requests per minute).
  • Burst Limit: A temporary allowance for exceeding the standard rate limit for a short period, often used to accommodate sudden, legitimate spikes in usage.
  • Identification: How the API identifies a unique client or user to apply limits. Common methods include:

* IP Address: Simple but can be problematic for clients behind NATs or proxies.

* API Key: A unique identifier provided to each client/application.

* User ID/Authentication Token: Limits applied per authenticated user.

  • Response Headers: Standardized headers returned by the API to inform clients about their current rate limit status:

* X-RateLimit-Limit: The total number of requests allowed in the current window.

* X-RateLimit-Remaining: The number of requests remaining in the current window.

* X-RateLimit-Reset: The time (usually in UTC epoch seconds or human-readable format) when the current rate limit window resets.

  • Error Codes: The standard HTTP status code for exceeding a rate limit is 429 Too Many Requests. The response body often provides additional details and potentially a Retry-After header.

4. Common Rate Limiting Algorithms

Different algorithms offer varying trade-offs in terms of accuracy, resource usage, and fairness.

  • 4.1. Fixed Window Counter

* Mechanism: Divides time into fixed-size windows (e.g., 1 minute). Each request increments a counter for the current window. If the counter exceeds the limit, requests are blocked until the next window.

* Pros: Simple to implement, low resource usage.

* Cons: Can suffer from "bursty" traffic at the window edges, allowing double the rate limit if requests occur at the very end of one window and the very beginning of the next.

  • 4.2. Sliding Window Log

Mechanism: Stores a timestamp for every request made by a client. When a new request arrives, it counts the number of timestamps within the last N* seconds/minutes. If this count exceeds the limit, the request is denied. Old timestamps are pruned.

* Pros: Very accurate and handles bursts smoothly, as it considers a true "sliding" window.

* Cons: High memory usage due to storing individual timestamps, especially for high-volume users.

  • 4.3. Sliding Window Counter

* Mechanism: A hybrid approach. It uses two fixed windows (current and previous) and weights the count from the previous window based on the overlap with the current sliding window.

* Pros: More accurate than fixed window, less memory-intensive than sliding window log.

* Cons: Still an approximation, not perfectly accurate.

  • 4.4. Token Bucket

* Mechanism: A conceptual "bucket" holds tokens. Tokens are added to the bucket at a fixed rate. Each request consumes one token. If the bucket is empty, the request is denied or queued. The bucket has a maximum capacity (burst limit).

* Pros: Allows for bursts of traffic up to the bucket capacity, simple to implement.

* Cons: Can be challenging to manage distributedly; token generation rate needs careful tuning.

  • 4.5. Leaky Bucket

* Mechanism: Requests are added to a queue (the "bucket"). Requests are processed from the queue at a constant rate, like water leaking from a bucket. If the bucket overflows (queue is full), new requests are dropped.

* Pros: Smooths out traffic, ensures a constant output rate.

* Cons: Can introduce latency due to queuing, dropped requests if queue is full.

5. Implementation Considerations for API Providers

When implementing an API rate limiter, consider the following:

  • Placement:

* API Gateway/Load Balancer: Ideal for centralized control, early blocking, and offloading from backend services. Examples: Nginx, Kong, AWS API Gateway.

* Application Layer: More granular control, but can add overhead to application servers.

  • Storage for Counters/State:

* In-memory: Fast, but not suitable for distributed systems or persistent state.

* Redis: Excellent choice for distributed rate limiting due to its speed, atomic operations, and data structures (counters, sorted sets).

* Database: Slower, generally not recommended for high-volume rate limiting.

  • Distributed Systems:

* Ensure your chosen algorithm and storage mechanism can handle multiple instances of your API service coordinating their rate limit counts. Redis is often key here.

  • Edge Cases:

* Whitelisting: Allow specific IP addresses or API keys to bypass rate limits (e.g., internal tools).

* Grace Periods: Consider a small grace period before strictly enforcing limits, especially for new clients.

* Client-Side Retries: Inform clients how to handle 429 errors effectively.

  • Monitoring and Alerting:

* Track rate limit hits, blocked requests, and overall API usage.

* Set up alerts for unusual spikes or consistent limit breaches to identify potential issues or abuse.

  • Documentation:

* Clearly document your rate limiting policies, including limits, reset times, and expected 429 error responses, for your API consumers.

6. Best Practices for API Consumers

To ensure a smooth experience when consuming rate-limited APIs:

  • Respect Rate Limit Headers: Always read and adhere to X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
  • Implement Exponential Backoff with Jitter: When a 429 Too Many Requests error is received:

* Wait: Pause before retrying the request.

* Exponential: Increase the wait time exponentially for successive retries (e.g., 1s, 2s, 4s, 8s...).

* Jitter: Add a random component to the wait time to prevent all clients from retrying at the exact same moment, which could cause another traffic spike.

* Max Retries: Define a maximum number of retries to prevent infinite loops.

  • Utilize the Retry-After Header: If provided, this header specifies the exact duration (in seconds) or a date/time when the client can retry the request. Prioritize this over custom backoff logic.
  • Cache Responses: Cache API responses where appropriate to reduce the number of requests to the API.
  • Batch Requests: If the API supports it, combine multiple operations into a single request to reduce the overall request count.
  • Monitor Your Usage: Keep track of your API usage to anticipate when you might hit limits and adjust your request patterns accordingly.
  • Handle Errors Gracefully: Design your application to handle 429 errors without crashing or causing user frustration. Inform users when limits are reached.

7. Conclusion

API Rate Limiters are an indispensable component of modern API design. They are critical for maintaining API health, security, and performance, while simultaneously ensuring fair access for all users. By understanding the underlying principles, algorithms, and best practices, both API providers can build resilient services, and API consumers can integrate with them effectively and reliably.

api_rate_limiter.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
"); 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' import ReactDOM from 'react-dom/client' import App from './App' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( ) "); 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' import './App.css' function App(){ return(

"+slugTitle(pn)+"

Built with PantheraHive BOS

) } export default App "); zip.file(folder+"src/index.css","*{margin:0;padding:0;box-sizing:border-box} body{font-family:system-ui,-apple-system,sans-serif;background:#f0f2f5;color:#1a1a2e} .app{min-height:100vh;display:flex;flex-direction:column} .app-header{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:40px} h1{font-size:2.5rem;font-weight:700} "); 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)+" Generated by PantheraHive BOS. ## Setup ```bash npm install npm run dev ``` ## Build ```bash npm run build ``` ## Open in IDE Open the project folder in VS Code or WebStorm. "); zip.file(folder+".gitignore","node_modules/ dist/ .env .DS_Store *.local "); } /* --- 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",'{ "name": "'+pn+'", "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc -b && vite build", "preview": "vite preview" }, "dependencies": { "vue": "^3.5.13", "vue-router": "^4.4.5", "pinia": "^2.3.0", "axios": "^1.7.9" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", "typescript": "~5.7.3", "vite": "^6.0.5", "vue-tsc": "^2.2.0" } } '); zip.file(folder+"vite.config.ts","import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': resolve(__dirname,'src') } } }) "); zip.file(folder+"tsconfig.json",'{"files":[],"references":[{"path":"./tsconfig.app.json"},{"path":"./tsconfig.node.json"}]} '); zip.file(folder+"tsconfig.app.json",'{ "compilerOptions":{ "target":"ES2020","useDefineForClassFields":true,"module":"ESNext","lib":["ES2020","DOM","DOM.Iterable"], "skipLibCheck":true,"moduleResolution":"bundler","allowImportingTsExtensions":true, "isolatedModules":true,"moduleDetection":"force","noEmit":true,"jsxImportSource":"vue", "strict":true,"paths":{"@/*":["./src/*"]} }, "include":["src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.vue"] } '); zip.file(folder+"env.d.ts","/// "); zip.file(folder+"index.html"," "+slugTitle(pn)+"
"); 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' import { createPinia } from 'pinia' import App from './App.vue' import './assets/main.css' const app = createApp(App) app.use(createPinia()) app.mount('#app') "); var hasApp=Object.keys(extracted).some(function(k){return k.indexOf("App.vue")>=0;}); if(!hasApp) zip.file(folder+"src/App.vue"," "); 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} "); 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)+" Generated by PantheraHive BOS. ## Setup ```bash npm install npm run dev ``` ## Build ```bash npm run build ``` Open in VS Code or WebStorm. "); zip.file(folder+".gitignore","node_modules/ dist/ .env .DS_Store *.local "); } /* --- 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",'{ "name": "'+pn+'", "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test" }, "dependencies": { "@angular/animations": "^19.0.0", "@angular/common": "^19.0.0", "@angular/compiler": "^19.0.0", "@angular/core": "^19.0.0", "@angular/forms": "^19.0.0", "@angular/platform-browser": "^19.0.0", "@angular/platform-browser-dynamic": "^19.0.0", "@angular/router": "^19.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, "devDependencies": { "@angular-devkit/build-angular": "^19.0.0", "@angular/cli": "^19.0.0", "@angular/compiler-cli": "^19.0.0", "typescript": "~5.6.0" } } '); zip.file(folder+"angular.json",'{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "'+pn+'": { "projectType": "application", "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:application", "options": { "outputPath": "dist/'+pn+'", "index": "src/index.html", "browser": "src/main.ts", "tsConfig": "tsconfig.app.json", "styles": ["src/styles.css"], "scripts": [] } }, "serve": {"builder":"@angular-devkit/build-angular:dev-server","configurations":{"production":{"buildTarget":"'+pn+':build:production"},"development":{"buildTarget":"'+pn+':build:development"}},"defaultConfiguration":"development"} } } } } '); zip.file(folder+"tsconfig.json",'{ "compileOnSave": false, "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"]}, "references":[{"path":"./tsconfig.app.json"}] } '); zip.file(folder+"tsconfig.app.json",'{ "extends":"./tsconfig.json", "compilerOptions":{"outDir":"./dist/out-tsc","types":[]}, "files":["src/main.ts"], "include":["src/**/*.d.ts"] } '); zip.file(folder+"src/index.html"," "+slugTitle(pn)+" "); zip.file(folder+"src/main.ts","import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent, appConfig) .catch(err => console.error(err)); "); zip.file(folder+"src/styles.css","* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: system-ui, -apple-system, sans-serif; background: #f9fafb; color: #111827; } "); 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'; import { RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet], templateUrl: './app.component.html', styleUrl: './app.component.css' }) export class AppComponent { title = '"+pn+"'; } "); zip.file(folder+"src/app/app.component.html","

"+slugTitle(pn)+"

Built with PantheraHive BOS

"); 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} "); } zip.file(folder+"src/app/app.config.ts","import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes) ] }; "); zip.file(folder+"src/app/app.routes.ts","import { Routes } from '@angular/router'; export const routes: Routes = []; "); 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)+" Generated by PantheraHive BOS. ## Setup ```bash npm install ng serve # or: npm start ``` ## Build ```bash ng build ``` Open in VS Code with Angular Language Service extension. "); zip.file(folder+".gitignore","node_modules/ dist/ .env .DS_Store *.local .angular/ "); } /* --- Python --- */ function buildPython(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^```[w]* ?/m,"").replace(/ ?```$/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(" "):"# add dependencies here "; zip.file(folder+"main.py",src||"# "+title+" # Generated by PantheraHive BOS print(title+" loaded") "); zip.file(folder+"requirements.txt",reqsTxt); zip.file(folder+".env.example","# Environment variables "); zip.file(folder+"README.md","# "+title+" Generated by PantheraHive BOS. ## Setup ```bash python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` ## Run ```bash python main.py ``` "); zip.file(folder+".gitignore",".venv/ __pycache__/ *.pyc .env .DS_Store "); } /* --- Node.js --- */ function buildNode(zip,folder,app,code){ var title=slugTitle(app); var pn=pkgName(app); var src=code.replace(/^```[w]* ?/m,"").replace(/ ?```$/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)+" "; zip.file(folder+"package.json",pkgJson); var fallback="const express=require("express"); const app=express(); app.use(express.json()); app.get("/",(req,res)=>{ res.json({message:""+title+" API"}); }); const PORT=process.env.PORT||3000; app.listen(PORT,()=>console.log("Server on port "+PORT)); "; zip.file(folder+"src/index.js",src||fallback); zip.file(folder+".env.example","PORT=3000 "); zip.file(folder+".gitignore","node_modules/ .env .DS_Store "); zip.file(folder+"README.md","# "+title+" Generated by PantheraHive BOS. ## Setup ```bash npm install ``` ## Run ```bash npm run dev ``` "); } /* --- 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:" "+title+" "+code+" "; zip.file(folder+"index.html",indexHtml); zip.file(folder+"style.css","/* "+title+" — styles */ *{margin:0;padding:0;box-sizing:border-box} body{font-family:system-ui,-apple-system,sans-serif;background:#fff;color:#1a1a2e} "); zip.file(folder+"script.js","/* "+title+" — scripts */ "); zip.file(folder+"assets/.gitkeep",""); zip.file(folder+"README.md","# "+title+" Generated by PantheraHive BOS. ## Open Double-click `index.html` in your browser. Or serve locally: ```bash npx serve . # or python3 -m http.server 3000 ``` "); zip.file(folder+".gitignore",".DS_Store node_modules/ .env "); } /* ===== 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(/ {2,}/g,"

"); h+="

"+hc+"

Generated by PantheraHive BOS
"; zip.file(folder+app+".html",h); zip.file(folder+"README.md","# "+title+" Generated by PantheraHive BOS. Files: - "+app+".md (Markdown) - "+app+".html (styled HTML) "); } 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);}});}