balda 0.0.23 → 0.0.25

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/CLAUDE.md ADDED
@@ -0,0 +1,109 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Balda is a **cross-runtime backend framework** that runs on Node.js, Bun, and Deno. It uses a decorator-based API similar to FastAPI/NestJS and provides native runtime server implementations (`Bun.serve`, `Deno.serve`, Node `http`/`https`).
8
+
9
+ **Version 0.x - APIs may change. Do not use in production.**
10
+
11
+ ## Common Commands
12
+
13
+ ### Development
14
+ - `yarn dev` - Start dev server with hot reload (Node.js)
15
+ - `yarn dev:bun` - Start dev server on Bun
16
+ - `yarn dev:deno` - Start dev server on Deno
17
+
18
+ ### Build
19
+ - `yarn build` - Build ESM + CJS output via tsup (through Docker)
20
+ - `yarn build:prod` - Build with minification
21
+ - `yarn build:test` - Build and cleanup (for CI)
22
+
23
+ ### Testing
24
+ Tests run through Docker containers for all runtimes:
25
+ - `yarn test` - Run Vitest tests (Node.js)
26
+ - `yarn test:bun` - Run tests on Bun
27
+ - `yarn test:deno` - Run tests on Deno
28
+ - `yarn test:all` - Run tests across all runtimes
29
+ - `yarn test:watch` - Watch mode for Node tests
30
+
31
+ Note: All test commands require `docker compose up` first.
32
+
33
+ ### Benchmarks
34
+ - `yarn benchmark` - Quick benchmark suite
35
+ - `yarn benchmark:all` - Full benchmark comparison
36
+ - `yarn benchmark:runtime` - Compare runtime performance
37
+
38
+ ### Code Quality
39
+ - `yarn lint` - Run ESLint
40
+ - `yarn lint:fix` - Auto-fix linting issues
41
+ - `yarn format` - Run Prettier
42
+
43
+ ### Docker Environment
44
+ The project uses Docker Compose for cross-runtime development. Services include:
45
+ - `node`, `bun`, `deno` - Runtime containers
46
+ - `redis`, `postgres`, `sqs` (ElasticMQ), `localstack` (S3), `mqtt` (Mosquitto), `azurite` - Infrastructure services
47
+
48
+ Commands prefixed with `docker compose exec -T node` run inside the Node container.
49
+
50
+ ## Architecture
51
+
52
+ ### Core Design Principle: Runtime Abstraction
53
+ The framework abstracts runtime differences through:
54
+ - `src/runtime/native_*.ts` - Runtime-specific implementations (server, fs, crypto, os, etc.)
55
+ - `src/runtime/runtime.ts` - Runtime detection singleton
56
+ - `src/runtime/native_server/` - Unified server interface with per-runtime connectors
57
+
58
+ ### Directory Structure
59
+
60
+ ```
61
+ src/
62
+ ├── decorators/ # @controller, @get, @post, @validate, @middleware, @cron
63
+ ├── server/ # Core Server class, router, Request/Response types
64
+ ├── runtime/ # Cross-runtime abstraction layer
65
+ ├── plugins/ # Middleware (bodyParser, cors, rateLimiter, etc.)
66
+ ├── validator/ # Zod/Ajv integration with fast-json-stringify caching
67
+ ├── queue/ # BullMQ, pg-boss, SQS, in-memory queues
68
+ ├── storage/ # S3, Azure Blob, local file storage
69
+ ├── mailer/ # Email with EJS/Handlebars/Mustache/Edge templates
70
+ ├── cron/ # Scheduled job decorator and execution
71
+ ├── mqtt/ # MQTT client wrapper
72
+ ├── graphql/ # Apollo Server integration
73
+ ├── logger/ # Pino-based structured logging
74
+ └── cli.ts # Command-line interface
75
+ ```
76
+
77
+ ### Key Patterns
78
+
79
+ **Decorator-Based Routing**: Controllers use class decorators with method-level route handlers. Routes register to a singleton `router` which is consumed by `ServerConnector` at `listen()` time.
80
+
81
+ **Plugin System**: Plugins are middleware functions conforming to `ServerRouteMiddleware`. The `Server.applyPlugins()` method maps plugin config options to middleware, applied before global middlewares in the request chain.
82
+
83
+ **Provider Pattern**: Queue and storage implementations share a common interface (`BaseQueue`, `StorageInterface`) with runtime-specific providers (BullMQ, SQS, pg-boss for queues; S3, Azure, local for storage).
84
+
85
+ **Policy System**: `@policy()` decorator attaches authorization rules to routes. `PolicyManager` evaluates rules before route handlers execute.
86
+
87
+ ### Entry Points
88
+ - `src/index.ts` - Main library exports
89
+ - `src/cli.ts` - CLI entry point (bin: `balda`)
90
+ - `src/server/server.ts` - Core `Server` class
91
+
92
+ ### Important Implementation Notes
93
+
94
+ **Build Output**: tsup produces both ESM (`.js`) and CJS (`.cjs`) outputs with TypeScript declarations (`.d.ts`). Many dependencies are marked as `external` to remain peer dependencies.
95
+
96
+ **Controller Auto-Import**: Using `controllerPatterns` in Server options, the framework uses `glob` to dynamically import controller files. This happens during `bootstrap()` before `listen()`.
97
+
98
+ **Validation Caching**: Ajv schemas are compiled and cached with fast-json-stringify for serialization performance. The `AjvStateManager` manages a global Ajv instance.
99
+
100
+ **Express Compatibility**: While using native runtime servers, Balda can mount Express routers via `createExpressAdapter()` and `expressMiddleware()` for migration scenarios.
101
+
102
+ **Error Handling**: Custom error hierarchy extends `BaldaError`. `errorFactory()` standardizes error responses. The server auto-registers 404/405 handlers for unmatched routes.
103
+
104
+ ## Testing Strategy
105
+
106
+ - Unit tests use Vitest with `test/**/*.test.ts` pattern
107
+ - Mock server available via `server.getMockServer()` for testing without network
108
+ - Benchmark suite uses autocannon for performance regression testing
109
+ - Cross-runtime testing validates behavior parity across Node/Bun/Deno
package/lib/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import {glob}from'glob';import ut from'pino';import ye from'fs';import Ce from'path';import {execSync}from'child_process';import*as Ye from'readline';import yt from'crypto';var ke=Object.defineProperty;var b=(c,e,t,r)=>{for(var n=void 0,i=c.length-1,l;i>=0;i--)(l=c[i])&&(n=(l(e,t,n))||n);return n&&ke(e,t,n),n};var je=class{type;constructor(){this.type=this.getRunTime();}getRunTime(){if(typeof Bun<"u")return "bun";if(typeof Deno<"u")return "deno";if(typeof process<"u")return "node";throw new Error("No environment detected")}},E=new je;var Ne=class{getCliArgs(){switch(E.type){case "bun":return this.getBunArgs();case "node":return this.getNodeArgs();case "deno":return Deno.args;default:throw new Error("Unsupported runtime")}}getCliCaller(){switch(E.type){case "bun":return "bun";case "node":return process.argv[0];case "deno":return "deno";default:throw new Error("Unsupported runtime")}}getBunArgs(){let e=Bun.argv,t=this.findScriptIndex(e);return e.slice(t+1)}getNodeArgs(){let e=process.argv,t=this.findScriptIndex(e);return e.slice(t+1)}findScriptIndex(e){if(e.length>=3&&e[1].includes(".bin/"))return 1;for(let t=0;t<e.length;t++){let r=e[t],n=r.split("/").pop()||r;if(!r.startsWith("-")){if(n==="yarn"&&t+1<e.length&&e[t+1]==="run"||n==="npx"&&t+1<e.length)return t+1;if(n==="yarn"||n==="pnpm")return t;if(n==="npm"&&t+1<e.length&&e[t+1]==="run"||n==="bun"&&t+1<e.length&&e[t+1]==="run")return t+1;if(/\.(js|ts|mjs|cjs)$/.test(r))return t;if(/^(tsx|ts-node|node|bun)$/.test(n)){for(let i=t+1;i<e.length;i++)if(!e[i].startsWith("-")&&/\.(js|ts|mjs|cjs)$/.test(e[i]))return i;return t}}}for(let t=e.length-1;t>=0;t--)if(!e[t].startsWith("-"))return t;return 1}},me=new Ne;var Xe=(c,e)=>{let t=Array(e.length+1).fill(null).map(()=>Array(c.length+1).fill(null));for(let r=0;r<=c.length;r++)t[0][r]=r;for(let r=0;r<=e.length;r++)t[r][0]=r;for(let r=1;r<=e.length;r++)for(let n=1;n<=c.length;n++){let i=c[n-1]===e[r-1]?0:1;t[r][n]=Math.min(t[r][n-1]+1,t[r-1][n]+1,t[r-1][n-1]+i);}return t[e.length][c.length]},Se=c=>c.toLowerCase().replace(/[-_.]/g,"_").replace(/([A-Z])/g,"_$1").replace(/^_+/,"").replace(/_+$/,"").toLowerCase(),Pe=c=>c.split(/[-_.]/g).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(""),Ge=c=>c.split(/[-_.]/g).map(e=>e.toLowerCase()).join("-");var ct=c=>{if(!c||c==="-"||c==="--")return null;let e=c.indexOf("=");if(e>0){let t=c.substring(0,e),r=c.substring(e+1);return {name:t,value:Ve(r)}}return {name:c,value:true}},Ve=c=>{if(c.toLowerCase()==="true")return true;if(c.toLowerCase()==="false")return false;let e=Number(c);return !Number.isNaN(e)&&Number.isFinite(e)?e:c},K=()=>{let c=me.getCliArgs(),e=[],t={};if(!c||!c.length)return {args:e,flags:t};for(let r=0;r<c.length;r++){let n=c[r];if(!(!n||typeof n!="string")){if(n.startsWith("-")){let i=ct(n);if(i){if(i.value===true&&r+1<c.length){let l=c[r+1];l&&typeof l=="string"&&!l.startsWith("-")&&(i.value=Ve(l),r++);}if(i.name in t){let l=t[i.name];Array.isArray(l)?l.push(i.value):t[i.name]=[l,i.value];}else t[i.name]=i.value;}continue}e.push(n);}}return {args:e,flags:t}},Ke=(c,e)=>{if(!c||typeof c!="string"||!e||!Array.isArray(e)||e.length===0)return "";let t=c.toLowerCase().trim(),r=e.filter(l=>{let o=l.toLowerCase();if(o===t||o.includes(t)||t.includes(o))return true;let u=Xe(o,t),d=Math.max(t.length,o.length)*.4;return u<=d});if(r.length===0)return "";let i=r.slice(0,3).map(l=>`\x1B[36m${l}\x1B[0m`).join(", ");return `\x1B[31m\u2717\x1B[0m Command \x1B[33m${c}\x1B[0m not found
3
- \x1B[32m\u{1F4A1}\x1B[0m Did you mean: ${i}?`},Te=()=>me.getCliArgs()[0]||null;var dt=()=>ut({level:"info",formatters:{level:e=>({level:e})}}),R=dt();var Ie=class{getCwd(){switch(E.type){case "node":case "bun":return process.cwd();case "deno":return Deno.cwd();default:throw new Error("Unsupported runtime")}}},Q=new Ie;var N=class{static metadata=new WeakMap;static set(e,t,r){this.metadata.has(e)||this.metadata.set(e,new Map),this.metadata.get(e).set(t,r);}static get(e,t){return this.metadata.get(e)?.get(t)}static getAll(e){return this.metadata.get(e)||new Map}static delete(e,t){this.metadata.get(e)?.delete(t);}static clear(e){this.metadata.delete(e);}};var J="VALIDATION_ERROR";var Je=K().args.slice(1),O=c=>(e,t)=>{let r=Te();if(!r||r!==e.commandName)return;let n=t;N.set(e,t,{type:"arg",name:n,description:c.description});let i=Je.length?Je.shift():c.defaultValue;if(c.required&&!i){let l=N.get(e,J);N.set(e,J,[...l||[],{type:"arg",name:n,message:"Required argument not provided"}]);return}c.parse&&i&&(i=c.parse(i)),Object.defineProperty(e,t,{value:i,enumerable:true,configurable:true,writable:true});};var F=c=>(e,t)=>{let r=Te();if(!r||r!==e.commandName)return;let n=c.name||t,i=K().flags,l=c.aliases?Array.isArray(c.aliases)?c.aliases:[c.aliases]:[],o=[n,...l],u=c.defaultValue;for(let d of o){let s=[d,`-${d}`,`--${d}`];for(let a of s)if(a in i){let p=i[a];c.type==="list"?u=(Array.isArray(p)?p:[p]).map(g=>{let h=String(g);return c.parse?c.parse(h):h}):(u=p,c.type==="boolean"?u=!!u:c.type==="number"?u=Number(u):c.type==="string"&&(u=String(u)),c.parse&&(u=c.parse(u)));break}if(u!==c.defaultValue)break}if(N.set(e,t,{type:"flag",name:n,aliases:l||[],description:c.description}),c.required&&(c.type==="list"?!u||Array.isArray(u)&&u.length===0:!u)){let s=N.get(e,J);N.set(e,J,[...s||[],{type:"flag",name:n,message:"Required flag not provided"}]);return}Object.defineProperty(e,t,{value:u,enumerable:true,configurable:true,writable:true});};F.boolean=c=>F({...c,type:"boolean"});F.string=c=>F({...c,type:"string"});F.number=c=>F({...c,type:"number"});F.list=c=>F({...c,type:"list"});F.array=c=>F({...c,type:"list"});var P=F;var _e=class{exit(e){switch(E.type){case "bun":case "node":process.exit(e);case "deno":Deno.exit(e);default:throw new Error("Unsupported runtime")}}},L=new _e;var q=class{static commandName=this.name;static calledBy=this.name;static description="";static help=[];static options={keepAlive:false};static args=K().args.slice(1);static flags=K().flags;static logger=R;static handle(){throw new Error(`Handle method not implemented in command class ${this.name}`)}static handleHelpFlag(e){let t=["-h","--help"];if(!Object.keys(e).some(d=>t.includes(d)))return;let n=this.commandName,i=this.description||"No description available",l=this.help||[],o=this.options,u=this.generateHelpOutput({name:n,description:i,helpText:l,options:o,args:this.args,flags:this.flags},this);console.log(u),L.exit(0);}static generateHelpOutput=(e,t)=>{let{name:r,description:n,helpText:i,options:l,args:o,flags:u}=e,d={title:"\x1B[1;36m",subtitle:"\x1B[1;33m",description:"\x1B[0;37m",code:"\x1B[0;32m",flag:"\x1B[0;35m",reset:"\x1B[0m",error:"\x1B[0;31m",success:"\x1B[0;32m",info:"\x1B[0;34m"},s=[`${d.title}${r}${d.reset}`,`${d.description}${n}${d.reset}`,"",`${d.subtitle}Usage:${d.reset}`,` ${d.code}${r}${d.reset} [options] [arguments]`,"",`${d.subtitle}Options:${d.reset}`,` ${d.flag}-h, --help${d.reset} Show this help message`,"",`${d.subtitle}Command Options:${d.reset}`,` ${d.flag}keepAlive${d.reset} ${l?.keepAlive??false?d.success+"Enabled"+d.reset:d.error+"Disabled"+d.reset}`,""];if(i){let g=Array.isArray(i)?i:[i];s.push(`${d.subtitle}Help:${d.reset}`),g.forEach(h=>{s.push(` ${d.description}${h}${d.reset}`);}),s.push("");}let a=N.getAll(t),p=Array.from(a.values()).filter(g=>g.type==="arg"),m=Array.from(a.values()).filter(g=>g.type==="flag");return p.length&&(s.push(`${d.subtitle}Available Arguments:${d.reset}`),p.forEach(g=>{let h=g.required?` ${d.error}(required)${d.reset}`:"",f=g.description?` ${d.description}${g.description}${d.reset}`:"";s.push(` ${d.code}${g.name}${d.reset}${h}${f}`);}),s.push("")),m.length&&(s.push(`${d.subtitle}Available Flags:${d.reset}`),m.forEach(g=>{g.aliases&&!Array.isArray(g.aliases)&&(g.aliases=[g.aliases]);let h=g.aliases.length?` ${d.flag}(${g.aliases.join(", ")})${d.reset}`:"",f=g.required?` ${d.error}(required)${d.reset}`:"",v=g.description?` ${d.description}${g.description}${d.reset}`:"";s.push(` ${d.flag}--${g.name}${h}${d.reset}${f}${v}`);}),s.push("")),((o?.length??0)>0||u&&Object.keys(u).length>0)&&(s.push(`${d.subtitle}Current Context:${d.reset}`),o?.length&&s.push(` ${d.info}Provided Arguments:${d.reset} ${d.code}${o.join(" ")}${d.reset}`),u&&Object.keys(u).length>0&&(s.push(` ${d.info}Provided Flags:${d.reset}`),Object.keys(u).forEach(g=>{let h=u[g],f=h!=null?` = ${d.code}${h}${d.reset}`:"";s.push(` ${d.flag}${g}${d.reset}${f}`);})),s.push("")),i&&(Array.isArray(i)?i.some(g=>g.includes("example")):i.includes("example"))&&(s.push(`${d.subtitle}Examples:${d.reset}`),(Array.isArray(i)?i.filter(h=>h.includes("example")):[i.split("example")[1].trim()]).forEach(h=>{s.push(` ${d.code}${h}${d.reset}`);}),s.push("")),s.join(`
4
- `)};static validateContext=e=>{let t=Array.from(N.get(e,J)||[]);if(t.length){let r={error:"\x1B[0;31m",title:"\x1B[1;31m",reset:"\x1B[0m",info:"\x1B[0;34m",code:"\x1B[0;32m"};console.error(`${r.title}\u274C Validation Errors:${r.reset}`),console.error(""),t.forEach((n,i)=>{let l=`${r.info}${i+1}.${r.reset}`,o=`${r.error}${n.type.toUpperCase()}${r.reset}`,u=`${r.code}${n.name}${r.reset}`;console.error(` ${l} ${o} ${u}: ${r.error}${n.message}${r.reset}`);}),console.error(""),console.error(`${r.info}\u{1F4A1} Tip: Use --help for usage information${r.reset}`),L.exit(1);}}};var De=class{async mkdir(e,t){switch(E.type){case "bun":case "node":await(await import('fs/promises')).mkdir(e,{recursive:t?.recursive??false,mode:t?.mode});break;case "deno":typeof t?.mode=="string"&&(t.mode=Number.parseInt(t.mode)),await Deno.mkdir(e,{recursive:t?.recursive??false,mode:t?.mode});break}}async exists(e){switch(E.type){case "node":return (await import('fs')).existsSync(e);case "bun":return (await import('fs')).existsSync(e);case "deno":return Deno.stat(e).then(()=>true).catch(()=>false);default:throw new Error("Unsupported runtime")}}async readFile(e,t){switch(E.type){case "node":let n=await(await import('fs/promises')).readFile(e,{encoding:t?.encoding??null});return t?.encoding==="utf8"?n:new Uint8Array(n);case "bun":let i=Bun.file(e);return t?.encoding==="utf8"?i.text():new Uint8Array(await i.arrayBuffer());case "deno":let l=await Deno.readFile(e);return t?.encoding==="utf8"?new TextDecoder().decode(l):new Uint8Array(l)}}async writeFile(e,t){switch(E.type){case "node":await(await import('fs/promises')).writeFile(e,t);break;case "bun":await Bun.write(e,t);break;case "deno":await Deno.writeFile(e,t);break}}async stat(e){switch(E.type){case "node":let r=await(await import('fs/promises')).stat(e);return {isDirectory:r.isDirectory(),isFile:r.isFile(),isSymbolicLink:r.isSymbolicLink(),size:r.size};case "bun":let i=await(await import('fs/promises')).stat(e);return {isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:i.isSymbolicLink(),size:i.size};case "deno":let l=await Deno.stat(e);return {isDirectory:l.isDirectory,isFile:l.isFile,isSymbolicLink:false,size:l.size}}}async unlink(e){switch(E.type){case "node":await(await import('fs/promises')).unlink(e);break;case "bun":await Bun.file(e).delete();break;case "deno":await Deno.remove(e);break;default:throw new Error("Unsupported runtime")}}async streamFile(e){switch(E.type){case "node":let t=await import('fs'),{Readable:r}=await import('stream'),n=t.createReadStream(e);return r.toWeb(n);case "bun":return Bun.file(e).stream();case "deno":return (await Deno.open(e)).readable;default:throw new Error("Unsupported runtime")}}async readdir(e){switch(E.type){case "bun":case "node":return (await import('fs/promises')).readdir(e);case "deno":let r=[];for await(let n of Deno.readDir(e))r.push(n.name);return r;default:throw new Error("Unsupported runtime")}}},y=new De;var Qe=class{basename(e){switch(E.type){case "node":case "bun":case "deno":return Ce.basename(e);default:throw new Error("Unsupported runtime")}}join(...e){switch(E.type){case "node":case "bun":case "deno":return Ce.join(...e);default:throw new Error("Unsupported runtime")}}extName(e){switch(E.type){case "bun":case "node":case "deno":return Ce.extname(e);default:throw new Error("Unsupported runtime")}}resolve(...e){switch(E.type){case "bun":case "node":case "deno":return Ce.resolve(...e);default:throw new Error("Unsupported runtime")}}},x=new Qe;var z=async c=>{let e=x.join(process.cwd(),"node_modules");if(!await y.exists(e))return c;let r=[];for(let n of c){let i=x.join(e,n);await y.exists(i)||r.push(n);}return r},j=async()=>await y.exists(x.join(process.cwd(),"yarn.lock"))?["yarn","add","-D"]:await y.exists(x.join(process.cwd(),"pnpm-lock.yaml"))?["pnpm","add","-D"]:await y.exists(x.join(process.cwd(),"package-lock.json"))?["npm","install","-D"]:await y.exists(x.join(process.cwd(),"bun.lockb"))?["bun","add","-D"]:await y.exists(x.join(process.cwd(),"deno.lock"))?["deno","add","-D"]:["npm","install","-D"],M=async(c,e,t,r,n=true)=>{let i=Ye.createInterface({input:process.stdin,output:process.stdout}),l=t.join(", "),o=`Do you want to install the following ${n?"dev":""} dependencies using ${e}?
2
+ import {glob}from'glob';import ut from'pino';import ye from'fs';import Ce from'path';import {execSync}from'child_process';import*as Ye from'readline';import yt from'crypto';var ke=Object.defineProperty;var b=(c,e,t,r)=>{for(var n=void 0,i=c.length-1,l;i>=0;i--)(l=c[i])&&(n=(l(e,t,n))||n);return n&&ke(e,t,n),n};var je=class{type;constructor(){this.type=this.getRunTime();}getRunTime(){if(typeof Bun<"u")return "bun";if(typeof Deno<"u")return "deno";if(typeof process<"u")return "node";throw new Error("No environment detected")}},R=new je;var Ne=class{getCliArgs(){switch(R.type){case "bun":return this.getBunArgs();case "node":return this.getNodeArgs();case "deno":return Deno.args;default:throw new Error("Unsupported runtime")}}getCliCaller(){switch(R.type){case "bun":return "bun";case "node":return process.argv[0];case "deno":return "deno";default:throw new Error("Unsupported runtime")}}getBunArgs(){let e=Bun.argv,t=this.findScriptIndex(e);return e.slice(t+1)}getNodeArgs(){let e=process.argv,t=this.findScriptIndex(e);return e.slice(t+1)}findScriptIndex(e){if(e.length>=3&&e[1].includes(".bin/"))return 1;for(let t=0;t<e.length;t++){let r=e[t],n=r.split("/").pop()||r;if(!r.startsWith("-")){if(n==="yarn"&&t+1<e.length&&e[t+1]==="run"||n==="npx"&&t+1<e.length)return t+1;if(n==="yarn"||n==="pnpm")return t;if(n==="npm"&&t+1<e.length&&e[t+1]==="run"||n==="bun"&&t+1<e.length&&e[t+1]==="run")return t+1;if(/\.(js|ts|mjs|cjs)$/.test(r))return t;if(/^(tsx|ts-node|node|bun)$/.test(n)){for(let i=t+1;i<e.length;i++)if(!e[i].startsWith("-")&&/\.(js|ts|mjs|cjs)$/.test(e[i]))return i;return t}}}for(let t=e.length-1;t>=0;t--)if(!e[t].startsWith("-"))return t;return 1}},me=new Ne;var Xe=(c,e)=>{let t=Array(e.length+1).fill(null).map(()=>Array(c.length+1).fill(null));for(let r=0;r<=c.length;r++)t[0][r]=r;for(let r=0;r<=e.length;r++)t[r][0]=r;for(let r=1;r<=e.length;r++)for(let n=1;n<=c.length;n++){let i=c[n-1]===e[r-1]?0:1;t[r][n]=Math.min(t[r][n-1]+1,t[r-1][n]+1,t[r-1][n-1]+i);}return t[e.length][c.length]},Se=c=>c.toLowerCase().replace(/[-_.]/g,"_").replace(/([A-Z])/g,"_$1").replace(/^_+/,"").replace(/_+$/,"").toLowerCase(),Pe=c=>c.split(/[-_.]/g).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(""),Ge=c=>c.split(/[-_.]/g).map(e=>e.toLowerCase()).join("-");var ct=c=>{if(!c||c==="-"||c==="--")return null;let e=c.indexOf("=");if(e>0){let t=c.substring(0,e),r=c.substring(e+1);return {name:t,value:Ve(r)}}return {name:c,value:true}},Ve=c=>{if(c.toLowerCase()==="true")return true;if(c.toLowerCase()==="false")return false;let e=Number(c);return !Number.isNaN(e)&&Number.isFinite(e)?e:c},K=()=>{let c=me.getCliArgs(),e=[],t={};if(!c||!c.length)return {args:e,flags:t};for(let r=0;r<c.length;r++){let n=c[r];if(!(!n||typeof n!="string")){if(n.startsWith("-")){let i=ct(n);if(i){if(i.value===true&&r+1<c.length){let l=c[r+1];l&&typeof l=="string"&&!l.startsWith("-")&&(i.value=Ve(l),r++);}if(i.name in t){let l=t[i.name];Array.isArray(l)?l.push(i.value):t[i.name]=[l,i.value];}else t[i.name]=i.value;}continue}e.push(n);}}return {args:e,flags:t}},Ke=(c,e)=>{if(!c||typeof c!="string"||!e||!Array.isArray(e)||e.length===0)return "";let t=c.toLowerCase().trim(),r=e.filter(l=>{let o=l.toLowerCase();if(o===t||o.includes(t)||t.includes(o))return true;let u=Xe(o,t),d=Math.max(t.length,o.length)*.4;return u<=d});if(r.length===0)return "";let i=r.slice(0,3).map(l=>`\x1B[36m${l}\x1B[0m`).join(", ");return `\x1B[31m\u2717\x1B[0m Command \x1B[33m${c}\x1B[0m not found
3
+ \x1B[32m\u{1F4A1}\x1B[0m Did you mean: ${i}?`},Te=()=>me.getCliArgs()[0]||null;var dt=()=>ut({level:"info",formatters:{level:e=>({level:e})}}),B=dt();var Ie=class{getCwd(){switch(R.type){case "node":case "bun":return process.cwd();case "deno":return Deno.cwd();default:throw new Error("Unsupported runtime")}}},D=new Ie;var j=class{static metadata=new WeakMap;static set(e,t,r){this.metadata.has(e)||this.metadata.set(e,new Map),this.metadata.get(e).set(t,r);}static get(e,t){return this.metadata.get(e)?.get(t)}static getAll(e){return this.metadata.get(e)||new Map}static delete(e,t){this.metadata.get(e)?.delete(t);}static clear(e){this.metadata.delete(e);}};var J="VALIDATION_ERROR";var Je=K().args.slice(1),E=c=>(e,t)=>{let r=Te();if(!r||r!==e.commandName)return;let n=t;j.set(e,t,{type:"arg",name:n,description:c.description});let i=Je.length?Je.shift():c.defaultValue;if(c.required&&!i){let l=j.get(e,J);j.set(e,J,[...l||[],{type:"arg",name:n,message:"Required argument not provided"}]);return}c.parse&&i&&(i=c.parse(i)),Object.defineProperty(e,t,{value:i,enumerable:true,configurable:true,writable:true});};var Q=c=>(e,t)=>{let r=Te();if(!r||r!==e.commandName)return;let n=c.name||t,i=K().flags,l=c.aliases?Array.isArray(c.aliases)?c.aliases:[c.aliases]:[],o=[n,...l],u=c.defaultValue;for(let d of o){let s=[d,`-${d}`,`--${d}`];for(let a of s)if(a in i){let p=i[a];c.type==="list"?u=(Array.isArray(p)?p:[p]).map(g=>{let h=String(g);return c.parse?c.parse(h):h}):(u=p,c.type==="boolean"?u=!!u:c.type==="number"?u=Number(u):c.type==="string"&&(u=String(u)),c.parse&&(u=c.parse(u)));break}if(u!==c.defaultValue)break}if(j.set(e,t,{type:"flag",name:n,aliases:l||[],description:c.description}),c.required&&(c.type==="list"?!u||Array.isArray(u)&&u.length===0:!u)){let s=j.get(e,J);j.set(e,J,[...s||[],{type:"flag",name:n,message:"Required flag not provided"}]);return}Object.defineProperty(e,t,{value:u,enumerable:true,configurable:true,writable:true});};Q.boolean=c=>Q({...c,type:"boolean"});Q.string=c=>Q({...c,type:"string"});Q.number=c=>Q({...c,type:"number"});Q.list=c=>Q({...c,type:"list"});Q.array=c=>Q({...c,type:"list"});var P=Q;var _e=class{exit(e){switch(R.type){case "bun":case "node":process.exit(e);case "deno":Deno.exit(e);default:throw new Error("Unsupported runtime")}}},L=new _e;var q=class{static commandName=this.name;static calledBy=this.name;static description="";static help=[];static options={keepAlive:false};static args=K().args.slice(1);static flags=K().flags;static logger=B.child({scope:this.constructor.name});static handle(){throw new Error(`Handle method not implemented in command class ${this.name}`)}static handleHelpFlag(e){let t=["-h","--help"];if(!Object.keys(e).some(d=>t.includes(d)))return;let n=this.commandName,i=this.description||"No description available",l=this.help||[],o=this.options,u=this.generateHelpOutput({name:n,description:i,helpText:l,options:o,args:this.args,flags:this.flags},this);console.log(u),L.exit(0);}static generateHelpOutput=(e,t)=>{let{name:r,description:n,helpText:i,options:l,args:o,flags:u}=e,d={title:"\x1B[1;36m",subtitle:"\x1B[1;33m",description:"\x1B[0;37m",code:"\x1B[0;32m",flag:"\x1B[0;35m",reset:"\x1B[0m",error:"\x1B[0;31m",success:"\x1B[0;32m",info:"\x1B[0;34m"},s=[`${d.title}${r}${d.reset}`,`${d.description}${n}${d.reset}`,"",`${d.subtitle}Usage:${d.reset}`,` ${d.code}${r}${d.reset} [options] [arguments]`,"",`${d.subtitle}Options:${d.reset}`,` ${d.flag}-h, --help${d.reset} Show this help message`,"",`${d.subtitle}Command Options:${d.reset}`,` ${d.flag}keepAlive${d.reset} ${l?.keepAlive??false?d.success+"Enabled"+d.reset:d.error+"Disabled"+d.reset}`,""];if(i){let g=Array.isArray(i)?i:[i];s.push(`${d.subtitle}Help:${d.reset}`),g.forEach(h=>{s.push(` ${d.description}${h}${d.reset}`);}),s.push("");}let a=j.getAll(t),p=Array.from(a.values()).filter(g=>g.type==="arg"),m=Array.from(a.values()).filter(g=>g.type==="flag");return p.length&&(s.push(`${d.subtitle}Available Arguments:${d.reset}`),p.forEach(g=>{let h=g.required?` ${d.error}(required)${d.reset}`:"",f=g.description?` ${d.description}${g.description}${d.reset}`:"";s.push(` ${d.code}${g.name}${d.reset}${h}${f}`);}),s.push("")),m.length&&(s.push(`${d.subtitle}Available Flags:${d.reset}`),m.forEach(g=>{g.aliases&&!Array.isArray(g.aliases)&&(g.aliases=[g.aliases]);let h=g.aliases.length?` ${d.flag}(${g.aliases.join(", ")})${d.reset}`:"",f=g.required?` ${d.error}(required)${d.reset}`:"",v=g.description?` ${d.description}${g.description}${d.reset}`:"";s.push(` ${d.flag}--${g.name}${h}${d.reset}${f}${v}`);}),s.push("")),((o?.length??0)>0||u&&Object.keys(u).length>0)&&(s.push(`${d.subtitle}Current Context:${d.reset}`),o?.length&&s.push(` ${d.info}Provided Arguments:${d.reset} ${d.code}${o.join(" ")}${d.reset}`),u&&Object.keys(u).length>0&&(s.push(` ${d.info}Provided Flags:${d.reset}`),Object.keys(u).forEach(g=>{let h=u[g],f=h!=null?` = ${d.code}${h}${d.reset}`:"";s.push(` ${d.flag}${g}${d.reset}${f}`);})),s.push("")),i&&(Array.isArray(i)?i.some(g=>g.includes("example")):i.includes("example"))&&(s.push(`${d.subtitle}Examples:${d.reset}`),(Array.isArray(i)?i.filter(h=>h.includes("example")):[i.split("example")[1].trim()]).forEach(h=>{s.push(` ${d.code}${h}${d.reset}`);}),s.push("")),s.join(`
4
+ `)};static validateContext=e=>{let t=Array.from(j.get(e,J)||[]);if(t.length){let r={error:"\x1B[0;31m",title:"\x1B[1;31m",reset:"\x1B[0m",info:"\x1B[0;34m",code:"\x1B[0;32m"};console.error(`${r.title}\u274C Validation Errors:${r.reset}`),console.error(""),t.forEach((n,i)=>{let l=`${r.info}${i+1}.${r.reset}`,o=`${r.error}${n.type.toUpperCase()}${r.reset}`,u=`${r.code}${n.name}${r.reset}`;console.error(` ${l} ${o} ${u}: ${r.error}${n.message}${r.reset}`);}),console.error(""),console.error(`${r.info}\u{1F4A1} Tip: Use --help for usage information${r.reset}`),L.exit(1);}}};var De=class{async mkdir(e,t){switch(R.type){case "bun":case "node":await(await import('fs/promises')).mkdir(e,{recursive:t?.recursive??false,mode:t?.mode});break;case "deno":typeof t?.mode=="string"&&(t.mode=Number.parseInt(t.mode)),await Deno.mkdir(e,{recursive:t?.recursive??false,mode:t?.mode});break}}async exists(e){switch(R.type){case "node":return (await import('fs')).existsSync(e);case "bun":return (await import('fs')).existsSync(e);case "deno":return Deno.stat(e).then(()=>true).catch(()=>false);default:throw new Error("Unsupported runtime")}}async readFile(e,t){switch(R.type){case "node":let n=await(await import('fs/promises')).readFile(e,{encoding:t?.encoding??null});return t?.encoding==="utf8"?n:new Uint8Array(n);case "bun":let i=Bun.file(e);return t?.encoding==="utf8"?i.text():new Uint8Array(await i.arrayBuffer());case "deno":let l=await Deno.readFile(e);return t?.encoding==="utf8"?new TextDecoder().decode(l):new Uint8Array(l)}}async writeFile(e,t){switch(R.type){case "node":await(await import('fs/promises')).writeFile(e,t);break;case "bun":await Bun.write(e,t);break;case "deno":await Deno.writeFile(e,t);break}}async stat(e){switch(R.type){case "node":let r=await(await import('fs/promises')).stat(e);return {isDirectory:r.isDirectory(),isFile:r.isFile(),isSymbolicLink:r.isSymbolicLink(),size:r.size};case "bun":let i=await(await import('fs/promises')).stat(e);return {isDirectory:i.isDirectory(),isFile:i.isFile(),isSymbolicLink:i.isSymbolicLink(),size:i.size};case "deno":let l=await Deno.stat(e);return {isDirectory:l.isDirectory,isFile:l.isFile,isSymbolicLink:false,size:l.size}}}async unlink(e){switch(R.type){case "node":await(await import('fs/promises')).unlink(e);break;case "bun":await Bun.file(e).delete();break;case "deno":await Deno.remove(e);break;default:throw new Error("Unsupported runtime")}}async streamFile(e){switch(R.type){case "node":let t=await import('fs'),{Readable:r}=await import('stream'),n=t.createReadStream(e);return r.toWeb(n);case "bun":return Bun.file(e).stream();case "deno":return (await Deno.open(e)).readable;default:throw new Error("Unsupported runtime")}}async readdir(e){switch(R.type){case "bun":case "node":return (await import('fs/promises')).readdir(e);case "deno":let r=[];for await(let n of Deno.readDir(e))r.push(n.name);return r;default:throw new Error("Unsupported runtime")}}},y=new De;var Qe=class{basename(e){switch(R.type){case "node":case "bun":case "deno":return Ce.basename(e);default:throw new Error("Unsupported runtime")}}join(...e){switch(R.type){case "node":case "bun":case "deno":return Ce.join(...e);default:throw new Error("Unsupported runtime")}}extName(e){switch(R.type){case "bun":case "node":case "deno":return Ce.extname(e);default:throw new Error("Unsupported runtime")}}resolve(...e){switch(R.type){case "bun":case "node":case "deno":return Ce.resolve(...e);default:throw new Error("Unsupported runtime")}}},x=new Qe;var z=async c=>{let e=x.join(process.cwd(),"node_modules");if(!await y.exists(e))return c;let r=[];for(let n of c){let i=x.join(e,n);await y.exists(i)||r.push(n);}return r},k=async()=>await y.exists(x.join(process.cwd(),"yarn.lock"))?["yarn","add","-D"]:await y.exists(x.join(process.cwd(),"pnpm-lock.yaml"))?["pnpm","add","-D"]:await y.exists(x.join(process.cwd(),"package-lock.json"))?["npm","install","-D"]:await y.exists(x.join(process.cwd(),"bun.lockb"))?["bun","add","-D"]:await y.exists(x.join(process.cwd(),"deno.lock"))?["deno","add","-D"]:["npm","install","-D"],$=async(c,e,t,r,n=true)=>{let i=Ye.createInterface({input:process.stdin,output:process.stdout}),l=t.join(", "),o=`Do you want to install the following ${n?"dev":""} dependencies using ${e}?
5
5
  ${l}
6
- (y/n): `;return new Promise(u=>{i.question(o,d=>{if(i.close(),d.toLowerCase()==="y"||d.toLowerCase()==="yes"){execSync(c,r),u(true);return}u(false);});})};var I=class extends q{static commandName="build";static description="Build the project for production, node.js only";static help=["Build the project for production, node.js only","It will create a production build of the project in the dist directory","Must have a tsconfig.json file in the root of the project","Must have esbuild installed as a dependency while running the command","Must have esbuild-plugin-copy installed as a dependency while running the command if you want to copy assets to the output directory","Example: npx balda build -t ./tsconfig.json -a ./assets"];static clearDist;static entry;static output;static tsconfig;static assets;static format;static packages;static sourcemap;static async handle(){if(typeof process===void 0&&(this.logger.error("Build command is only supported in node.js"),process.exit(1)),["esm","cjs"].includes(this.format)||(this.logger.error("Invalid format, must be 'esm' or 'cjs'"),process.exit(1)),["bundle","external"].includes(this.packages)||(this.logger.error("Invalid packages, must be 'bundle' or 'external'"),process.exit(1)),!await import('esbuild').then(o=>true).catch(()=>false)){let[o,u,d]=await j();await M(`${o} ${u} esbuild ${d}`,o,["esbuild"],{stdio:"inherit"})||(this.logger.warn("User chose to not continue with the installation of esbuild, exiting..."),process.exit(0));}let t=await import('esbuild').catch(o=>{throw this.logger.error("esbuild is not installed, please install it with `npm install -D esbuild` to use the build command"),new Error("esbuild is not installed")}),r=Ce.join(this.output,"assets"),n=[];if(this.assets){let{copy:o}=await import('esbuild-plugin-copy').catch(u=>{throw this.logger.error("esbuild-plugin-copy is not installed, please install it with `npm install -D esbuild-plugin-copy` to use the build command"),new Error("esbuild-plugin-copy is not installed")});ye.existsSync(r)||ye.mkdirSync(r,{recursive:true}),n.push(o({assets:{from:this.assets,to:r}}));}this.clearDist&&ye.existsSync(this.output)&&(this.logger.info("Clearing dist directory..."),ye.rmSync(this.output,{recursive:true})),this.logger.info("Building project...");let i=Ce.join(this.output,"server.js"),l=await t.build({tsconfig:this.tsconfig,entryPoints:[this.entry],bundle:true,platform:"node",outfile:i,minify:true,sourcemap:this.sourcemap,plugins:n,format:this.format,packages:this.packages});l.errors.length&&(this.logger.error(JSON.stringify({message:"Failed to build the project",errors:l.errors},null,2)),process.exit(1)),l.warnings.length&&this.logger.warn(JSON.stringify({message:"Failed to build the project",warnings:l.warnings},null,2)),this.logger.info(JSON.stringify({message:`Project built successfully in ${i}`,output:i,assets:!!this.assets},null,2)),process.exit(0);}};b([P({type:"boolean",aliases:["c"],name:"clear-dist",required:false,defaultValue:false,description:"Whether to clear the dist directory before building the project"})],I,"clearDist"),b([P({type:"string",aliases:["e"],name:"entry",required:false,defaultValue:"./src/index.ts",description:"The entry point of the project, default is ./src/index.ts"})],I,"entry"),b([P({type:"string",aliases:["o"],name:"output",required:false,defaultValue:"./dist",description:"The path to the output directory, default is ./dist"})],I,"output"),b([P({type:"string",aliases:["t"],name:"tsconfig",required:false,defaultValue:"./tsconfig.json",description:"The path to the tsconfig.json file, default is ./tsconfig.json"})],I,"tsconfig"),b([P({type:"string",aliases:["a"],name:"assets",required:false,description:"The path to the assets directory that will be loaded in the production build"})],I,"assets"),b([P({type:"string",aliases:["f"],name:"format",required:false,defaultValue:"esm",description:"The format to build the project, default is esm, can be 'esm' or 'cjs'"})],I,"format"),b([P({type:"string",aliases:["p"],name:"packages",required:false,defaultValue:"external",description:"Weather to bundle node_modules or not, default is external, can be 'bundle' or 'external'"})],I,"packages"),b([P({type:"boolean",aliases:["s"],name:"sourcemap",required:false,defaultValue:true,description:"Whether to generate sourcemaps or not, default is true"})],I,"sourcemap");var X=class extends Error{constructor(e){super(e);}};var ge=class{staticChildren;paramChild;wildcardChild;middleware;handler;constructor(){this.staticChildren=new Map,this.paramChild=null,this.wildcardChild=null,this.middleware=null,this.handler=null;}},Be=class c{trees;routes;middlewares;basePath;staticRouteCache;constructor(e="",t=[]){this.trees=new Map,this.routes=[],this.middlewares=t,this.basePath=this.normalizeBasePath(e),this.staticRouteCache=new Map;}getRoutes(){return this.routes.slice()}addOrUpdate(e,t,r,n,i){e=e.toUpperCase();let l=t.split("?")[0],o=this.trees.get(e);o||(o=new ge,this.trees.set(e,o));let u=l.replace(/^\/+|\/+$/g,""),d=u.length===0?[]:u.split("/"),s=o;for(let p of d){if(p==="*"){s.wildcardChild||(s.wildcardChild=new ge),s=s.wildcardChild;break}if(p.startsWith(":")){let m=p.slice(1);s.paramChild||(s.paramChild={node:new ge,name:m}),s=s.paramChild.node;continue}s.staticChildren.has(p)||s.staticChildren.set(p,new ge),s=s.staticChildren.get(p);}s.middleware=r,s.handler=n;let a=this.routes.findIndex(p=>p.method===e&&p.path===t);if(a!==-1){this.routes[a].middleware=r,this.routes[a].handler=n;return}this.routes.push({method:e,path:t,middleware:r,handler:n,swaggerOptions:i});}find(e,t){e=e.toUpperCase();let r=t.indexOf("?"),n=r===-1?t:t.slice(0,r),i=`${e}:${n}`,l=this.staticRouteCache.get(i);if(l)return l;let o=this.trees.get(e);if(!o)return null;let u=n.replace(/^\/+|\/+$/g,""),d=u.length===0?[]:u.split("/"),s={},a=o;for(let p=0;p<d.length;p++){let m=d[p];if(a.staticChildren.has(m)){a=a.staticChildren.get(m);continue}if(a.paramChild){s[a.paramChild.name]=m,a=a.paramChild.node;continue}if(a.wildcardChild){s["*"]=d.slice(p).join("/"),a=a.wildcardChild;break}return null}return !a.handler||!a.middleware?null:{middleware:a.middleware,handler:a.handler,params:s}}get(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("GET",i,d,o,s);}post(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("POST",i,d,o,s);}patch(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("PATCH",i,d,o,s);}put(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("PUT",i,d,o,s);}delete(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("DELETE",i,d,o,s);}options(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("OPTIONS",i,d,o,s);}head(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("HEAD",i,d,o,s);}group(e,t,r){let n=Array.isArray(t)?t:typeof t=="function"?[]:t?[t]:[],i=Array.isArray(t)?r:typeof t=="function"?t:void 0,l=this.joinPath(e),o=new c(l,[...this.middlewares,...n]);i?.(o);for(let u of o.getRoutes())this.addOrUpdate(u.method,u.path,u.middleware,u.handler,u.swaggerOptions);}applyGlobalMiddlewaresToAllRoutes(e){for(let t of this.routes){let r=[...e,...t.middleware||[]];this.addOrUpdate(t.method,t.path,r,t.handler);}}normalizeBasePath(e){if(!e)return "";let t=e.replace(/\s+/g,"");return t=t.replace(/\/+/g,"/"),t.startsWith("/")||(t="/"+t),t.length>1&&(t=t.replace(/\/+$/g,"")),t}joinPath(e){let r=[this.basePath,e].filter(n=>typeof n=="string"&&n.length>0).join("/");return r=r.replace(/\/+/g,"/"),r.startsWith("/")||(r="/"+r),r.length>1&&(r=r.replace(/\/+$/g,"")),r}};new Be;var G=class{static scheduledJobs=[];static register(e,...t){t[2]={name:e,...t[2]},this.scheduledJobs.push({name:e,args:t});}static async run(){let e=(await import('node-cron').catch(()=>{throw new X("node-cron not installed as a dependency, it is required in order to run cron jobs with the @cron decorator")})).default;if(R.info("Scheduling cron jobs"),!this.scheduledJobs.length){R.info("No cron jobs to schedule");return}for(let{name:t,args:r}of this.scheduledJobs)R.info(`Scheduling cron job: ${t}`),e.schedule(...r).on("execution:failed",i=>this.globalErrorHandler(i));R.info("Cron jobs scheduled");}static globalErrorHandler(e){R.error(e.execution?.error);}static async massiveImportCronJobs(e){let t=[];for(let r of e){let n=await glob(r,{absolute:true,cwd:Q.getCwd()});t.push(...n);}await Promise.all(t.map(async r=>{await import(r).catch(n=>{R.error(`Error importing cron job: ${r}`),R.error(n);});}));}};var Y=class extends q{static commandName="cron-start";static description="Start cron job scheduler";static help=["Start the cron job scheduler to run scheduled tasks","Loads cron jobs from specified patterns and starts scheduling","Example: npx balda cron-start","Example: npx balda cron-start src/crons/**/*.ts --patterns src/schedules/**/*.ts"];static options={keepAlive:true};static pattern;static additionalPatterns;static async handle(){this.logger.info("Starting cron scheduler...");let e=[this.pattern];this.additionalPatterns&&this.additionalPatterns.length>0&&e.push(...this.additionalPatterns),this.logger.info(`Loading cron jobs from patterns: ${e.join(", ")}`),await G.massiveImportCronJobs(e);let t=G.scheduledJobs.length;if(t===0){this.logger.warn("No cron jobs found. Make sure your cron jobs are decorated with @cron decorator");return}this.logger.info(`Found ${t} cron job(s)`),G.run().then(()=>{this.logger.info("Cron scheduler started successfully. Press Ctrl+C to stop.");}).catch(r=>{this.logger.error("Error starting cron scheduler",r);});}};b([O({required:false,defaultValue:"src/crons/**/*.{ts,js}",description:"Primary glob pattern for cron jobs (default: src/crons/**/*.{ts,js})"})],Y,"pattern"),b([P.array({aliases:["p"],name:"patterns",required:false,description:"Additional glob patterns for cron jobs"})],Y,"additionalPatterns");var he=class extends q{static commandName="generate-command";static description="Generate a new command in the specified path";static help=["Generate a new cli command in the specified path","Example: npx balda generate-command my-command -p src/commands"];static path="src/commands";static name;static async handle(){if(_.getCommand(this.name)){let n=_.isBuiltInCommand(this.name)?"built-in":"user-defined";this.logger.error({commandName:this.name,type:n},`Command "${this.name}" already exists as a ${n} command. Cannot override existing commands.`);return}let t=this.getCommandTemplate();this.path=x.join(this.path,`${this.name}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`Command ${this.name} created successfully at ${this.path}`);}static getCommandTemplate(){return `import { Command, CommandOptions } from "balda";
6
+ (y/n): `;return new Promise(u=>{i.question(o,d=>{if(i.close(),d.toLowerCase()==="y"||d.toLowerCase()==="yes"){execSync(c,r),u(true);return}u(false);});})};var N=class extends q{static commandName="build";static description="Build the project for production, node.js only";static help=["Build the project for production, node.js only","It will create a production build of the project in the dist directory","Must have a tsconfig.json file in the root of the project","Must have esbuild installed as a dependency while running the command","Must have esbuild-plugin-copy installed as a dependency while running the command if you want to copy assets to the output directory","Example: npx balda build -t ./tsconfig.json -a ./assets"];static clearDist;static entry;static output;static tsconfig;static assets;static format;static packages;static sourcemap;static async handle(){if(typeof process===void 0&&(this.logger.error("Build command is only supported in node.js"),process.exit(1)),["esm","cjs"].includes(this.format)||(this.logger.error("Invalid format, must be 'esm' or 'cjs'"),process.exit(1)),["bundle","external"].includes(this.packages)||(this.logger.error("Invalid packages, must be 'bundle' or 'external'"),process.exit(1)),!await import('esbuild').then(o=>true).catch(()=>false)){let[o,u,d]=await k();await $(`${o} ${u} esbuild ${d}`,o,["esbuild"],{stdio:"inherit"})||(this.logger.warn("User chose to not continue with the installation of esbuild, exiting..."),process.exit(0));}let t=await import('esbuild').catch(o=>{throw this.logger.error("esbuild is not installed, please install it with `npm install -D esbuild` to use the build command"),new Error("esbuild is not installed")}),r=Ce.join(this.output,"assets"),n=[];if(this.assets){let{copy:o}=await import('esbuild-plugin-copy').catch(u=>{throw this.logger.error("esbuild-plugin-copy is not installed, please install it with `npm install -D esbuild-plugin-copy` to use the build command"),new Error("esbuild-plugin-copy is not installed")});ye.existsSync(r)||ye.mkdirSync(r,{recursive:true}),n.push(o({assets:{from:this.assets,to:r}}));}this.clearDist&&ye.existsSync(this.output)&&(this.logger.info("Clearing dist directory..."),ye.rmSync(this.output,{recursive:true})),this.logger.info("Building project...");let i=Ce.join(this.output,"server.js"),l=await t.build({tsconfig:this.tsconfig,entryPoints:[this.entry],bundle:true,platform:"node",outfile:i,minify:true,sourcemap:this.sourcemap,plugins:n,format:this.format,packages:this.packages});l.errors.length&&(this.logger.error(JSON.stringify({message:"Failed to build the project",errors:l.errors},null,2)),process.exit(1)),l.warnings.length&&this.logger.warn(JSON.stringify({message:"Failed to build the project",warnings:l.warnings},null,2)),this.logger.info(JSON.stringify({message:`Project built successfully in ${i}`,output:i,assets:!!this.assets},null,2)),process.exit(0);}};b([P({type:"boolean",aliases:["c"],name:"clear-dist",required:false,defaultValue:false,description:"Whether to clear the dist directory before building the project"})],N,"clearDist"),b([P({type:"string",aliases:["e"],name:"entry",required:false,defaultValue:"./src/index.ts",description:"The entry point of the project, default is ./src/index.ts"})],N,"entry"),b([P({type:"string",aliases:["o"],name:"output",required:false,defaultValue:"./dist",description:"The path to the output directory, default is ./dist"})],N,"output"),b([P({type:"string",aliases:["t"],name:"tsconfig",required:false,defaultValue:"./tsconfig.json",description:"The path to the tsconfig.json file, default is ./tsconfig.json"})],N,"tsconfig"),b([P({type:"string",aliases:["a"],name:"assets",required:false,description:"The path to the assets directory that will be loaded in the production build"})],N,"assets"),b([P({type:"string",aliases:["f"],name:"format",required:false,defaultValue:"esm",description:"The format to build the project, default is esm, can be 'esm' or 'cjs'"})],N,"format"),b([P({type:"string",aliases:["p"],name:"packages",required:false,defaultValue:"external",description:"Weather to bundle node_modules or not, default is external, can be 'bundle' or 'external'"})],N,"packages"),b([P({type:"boolean",aliases:["s"],name:"sourcemap",required:false,defaultValue:true,description:"Whether to generate sourcemaps or not, default is true"})],N,"sourcemap");var X=class extends Error{constructor(e){super(e);}};var ge=class{staticChildren;paramChild;wildcardChild;middleware;handler;constructor(){this.staticChildren=new Map,this.paramChild=null,this.wildcardChild=null,this.middleware=null,this.handler=null;}},Fe=class c{trees;routes;middlewares;basePath;staticRouteCache;constructor(e="",t=[]){this.trees=new Map,this.routes=[],this.middlewares=t,this.basePath=this.normalizeBasePath(e),this.staticRouteCache=new Map;}getRoutes(){return this.routes.slice()}addOrUpdate(e,t,r,n,i){e=e.toUpperCase();let l=t.split("?")[0],o=this.trees.get(e);o||(o=new ge,this.trees.set(e,o));let u=l.replace(/^\/+|\/+$/g,""),d=u.length===0?[]:u.split("/"),s=o;for(let p of d){if(p==="*"){s.wildcardChild||(s.wildcardChild=new ge),s=s.wildcardChild;break}if(p.startsWith(":")){let m=p.slice(1);s.paramChild||(s.paramChild={node:new ge,name:m}),s=s.paramChild.node;continue}s.staticChildren.has(p)||s.staticChildren.set(p,new ge),s=s.staticChildren.get(p);}s.middleware=r,s.handler=n;let a=this.routes.findIndex(p=>p.method===e&&p.path===t);if(a!==-1){this.routes[a].middleware=r,this.routes[a].handler=n;return}this.routes.push({method:e,path:t,middleware:r,handler:n,swaggerOptions:i});}find(e,t){e=e.toUpperCase();let r=t.indexOf("?"),n=r===-1?t:t.slice(0,r),i=`${e}:${n}`,l=this.staticRouteCache.get(i);if(l)return l;let o=this.trees.get(e);if(!o)return null;let u=n.replace(/^\/+|\/+$/g,""),d=u.length===0?[]:u.split("/"),s={},a=o;for(let p=0;p<d.length;p++){let m=d[p];if(a.staticChildren.has(m)){a=a.staticChildren.get(m);continue}if(a.paramChild){s[a.paramChild.name]=m,a=a.paramChild.node;continue}if(a.wildcardChild){s["*"]=d.slice(p).join("/"),a=a.wildcardChild;break}return null}return !a.handler||!a.middleware?null:{middleware:a.middleware,handler:a.handler,params:s}}get(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("GET",i,d,o,s);}post(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("POST",i,d,o,s);}patch(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("PATCH",i,d,o,s);}put(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("PUT",i,d,o,s);}delete(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("DELETE",i,d,o,s);}options(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("OPTIONS",i,d,o,s);}head(e,t,r,n){let i=this.joinPath(e),l=typeof t=="function"&&t.length!==3,o=l?t:r,u=l?[]:Array.isArray(t)?t:[t],d=[...this.middlewares,...u],s=l?r:n;this.addOrUpdate("HEAD",i,d,o,s);}group(e,t,r){let n=Array.isArray(t)?t:typeof t=="function"?[]:t?[t]:[],i=Array.isArray(t)?r:typeof t=="function"?t:void 0,l=this.joinPath(e),o=new c(l,[...this.middlewares,...n]);i?.(o);for(let u of o.getRoutes())this.addOrUpdate(u.method,u.path,u.middleware,u.handler,u.swaggerOptions);}applyGlobalMiddlewaresToAllRoutes(e){for(let t of this.routes){let r=[...e,...t.middleware||[]];this.addOrUpdate(t.method,t.path,r,t.handler);}}normalizeBasePath(e){if(!e)return "";let t=e.replace(/\s+/g,"");return t=t.replace(/\/+/g,"/"),t.startsWith("/")||(t="/"+t),t.length>1&&(t=t.replace(/\/+$/g,"")),t}joinPath(e){let r=[this.basePath,e].filter(n=>typeof n=="string"&&n.length>0).join("/");return r=r.replace(/\/+/g,"/"),r.startsWith("/")||(r="/"+r),r.length>1&&(r=r.replace(/\/+$/g,"")),r}};new Fe;var G=class{static scheduledJobs=[];static logger=B.child({scope:"CronService"});static register(e,...t){t[2]={name:e,...t[2]},this.scheduledJobs.push({name:e,args:t});}static async run(){let e=(await import('node-cron').catch(()=>{throw new X("node-cron not installed as a dependency, it is required in order to run cron jobs with the @cron decorator")})).default;if(this.logger.info("Scheduling cron jobs"),!this.scheduledJobs.length){this.logger.info("No cron jobs to schedule");return}for(let{name:t,args:r}of this.scheduledJobs)this.logger.info(`Scheduling cron job: ${t}`),e.schedule(...r).on("execution:failed",i=>this.globalErrorHandler(i));this.logger.info("Cron jobs scheduled");}static globalErrorHandler(e){this.logger.error(e.execution?.error);}static async massiveImportCronJobs(e){let t=[];for(let r of e){let n=await glob(r,{absolute:true,cwd:D.getCwd()});t.push(...n);}await Promise.all(t.map(async r=>{await import(r).catch(n=>{this.logger.error(`Error importing cron job: ${r}`),B.error(n);});}));}};var Y=class extends q{static commandName="cron-start";static description="Start cron job scheduler";static help=["Start the cron job scheduler to run scheduled tasks","Loads cron jobs from specified patterns and starts scheduling","Example: npx balda cron-start","Example: npx balda cron-start src/crons/**/*.ts --patterns src/schedules/**/*.ts"];static options={keepAlive:true};static pattern;static additionalPatterns;static async handle(){this.logger.info("Starting cron scheduler...");let e=[this.pattern];this.additionalPatterns&&this.additionalPatterns.length>0&&e.push(...this.additionalPatterns),this.logger.info(`Loading cron jobs from patterns: ${e.join(", ")}`),await G.massiveImportCronJobs(e);let t=G.scheduledJobs.length;if(t===0){this.logger.warn("No cron jobs found. Make sure your cron jobs are decorated with @cron decorator");return}this.logger.info(`Found ${t} cron job(s)`),G.run().then(()=>{this.logger.info("Cron scheduler started successfully. Press Ctrl+C to stop.");}).catch(r=>{this.logger.error("Error starting cron scheduler",r);});}};b([E({required:false,defaultValue:"src/crons/**/*.{ts,js}",description:"Primary glob pattern for cron jobs (default: src/crons/**/*.{ts,js})"})],Y,"pattern"),b([P.array({aliases:["p"],name:"patterns",required:false,description:"Additional glob patterns for cron jobs"})],Y,"additionalPatterns");var he=class extends q{static commandName="generate-command";static description="Generate a new command in the specified path";static help=["Generate a new cli command in the specified path","Example: npx balda generate-command my-command -p src/commands"];static path="src/commands";static name;static async handle(){if(I.getCommand(this.name)){let n=I.isBuiltInCommand(this.name)?"built-in":"user-defined";this.logger.error({commandName:this.name,type:n},`Command "${this.name}" already exists as a ${n} command. Cannot override existing commands.`);return}let t=this.getCommandTemplate();this.path=x.join(this.path,`${this.name}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`Command ${this.name} created successfully at ${this.path}`);}static getCommandTemplate(){return `import { Command, CommandOptions } from "balda";
7
7
 
8
8
  export default class extends Command {
9
9
  static commandName = "${this.name}";
@@ -17,7 +17,7 @@ export default class extends Command {
17
17
  static async handle(): Promise<void> {
18
18
  // Implement your command logic here
19
19
  }
20
- }`}};b([O({description:"The name of the command to generate",required:true})],he,"name");var Z=class extends q{static commandName="generate-controller";static description="Generate a new controller in the specified path";static help=["Generate a new controller in the specified path","Example: npx balda generate-controller user -p src/controllers"];static controllerName;static path;static async handle(){let e=this.getControllerTemplate(),t=`${this.controllerName.toLowerCase()}.ts`;this.path=x.join(this.path,t),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Controller ${this.controllerName} created successfully at ${this.path}`);}static getControllerTemplate(){let e=Pe(this.controllerName);return `import { controller, get, post, put, del, Request, Response } from "balda";
20
+ }`}};b([E({description:"The name of the command to generate",required:true})],he,"name");var Z=class extends q{static commandName="generate-controller";static description="Generate a new controller in the specified path";static help=["Generate a new controller in the specified path","Example: npx balda generate-controller user -p src/controllers"];static controllerName;static path;static async handle(){let e=this.getControllerTemplate(),t=`${this.controllerName.toLowerCase()}.ts`;this.path=x.join(this.path,t),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Controller ${this.controllerName} created successfully at ${this.path}`);}static getControllerTemplate(){let e=Pe(this.controllerName);return `import { controller, get, post, put, del, Request, Response } from "balda";
21
21
 
22
22
  @controller("/${Ge(this.controllerName)}")
23
23
  export default class ${e}Controller {
@@ -45,7 +45,7 @@ export default class ${e}Controller {
45
45
  async destroy(req: Request, res: Response) {
46
46
  return { message: \`Delete ${this.controllerName.toLowerCase()} with id \${req.params.id}\` };
47
47
  }
48
- }`}};b([O({description:"The name of the controller to generate",required:true})],Z,"controllerName"),b([P({description:"The path to the controller to generate, default is src/controllers",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/controllers"})],Z,"path");var ee=class extends q{static commandName="generate-cron";static description="Generate a new cron job in the specified path";static help=["Generate a new cron job in the specified path","Example: npx balda generate-cron my-cron -p src/cron"];static fileName;static path;static async handle(){let e=this.getCronTemplate();this.path=x.join(this.path,`${this.fileName}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Cron job ${this.fileName} created successfully at ${this.path}`);}static getCronTemplate(){return `import { BaseCron, cron } from "balda";
48
+ }`}};b([E({description:"The name of the controller to generate",required:true})],Z,"controllerName"),b([P({description:"The path to the controller to generate, default is src/controllers",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/controllers"})],Z,"path");var ee=class extends q{static commandName="generate-cron";static description="Generate a new cron job in the specified path";static help=["Generate a new cron job in the specified path","Example: npx balda generate-cron my-cron -p src/cron"];static fileName;static path;static async handle(){let e=this.getCronTemplate();this.path=x.join(this.path,`${this.fileName}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Cron job ${this.fileName} created successfully at ${this.path}`);}static getCronTemplate(){return `import { BaseCron, cron } from "balda";
49
49
 
50
50
  export default class extends BaseCron {
51
51
  @cron("* * * * *")
@@ -53,7 +53,7 @@ export default class extends BaseCron {
53
53
  this.logger.info("Running cron job");
54
54
  // Implement your cron job logic here
55
55
  }
56
- }`}};b([O({description:"The name of the cron job file to generate",required:true})],ee,"fileName"),b([P({description:"The path to the cron job to generate, default is src/cron",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/cron"})],ee,"path");var te=class extends q{static commandName="generate-middleware";static description="Generate a new middleware in the specified path";static help=["Generate a new middleware in the specified path","Example: npx balda generate-middleware auth -p src/middlewares"];static middlewareName;static path;static async handle(){let e=this.getMiddlewareTemplate(),t=`${this.middlewareName.toLowerCase()}.ts`;this.path=x.join(this.path,t),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Middleware ${this.middlewareName} created successfully at ${this.path}`);}static getMiddlewareTemplate(){let e=Pe(this.middlewareName);return `import type { Request, Response, NextFunction, ServerRouteMiddleware } from "balda";
56
+ }`}};b([E({description:"The name of the cron job file to generate",required:true})],ee,"fileName"),b([P({description:"The path to the cron job to generate, default is src/cron",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/cron"})],ee,"path");var te=class extends q{static commandName="generate-middleware";static description="Generate a new middleware in the specified path";static help=["Generate a new middleware in the specified path","Example: npx balda generate-middleware auth -p src/middlewares"];static middlewareName;static path;static async handle(){let e=this.getMiddlewareTemplate(),t=`${this.middlewareName.toLowerCase()}.ts`;this.path=x.join(this.path,t),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(e)),this.logger.info(`Middleware ${this.middlewareName} created successfully at ${this.path}`);}static getMiddlewareTemplate(){let e=Pe(this.middlewareName);return `import type { Request, Response, NextFunction, ServerRouteMiddleware } from "balda";
57
57
 
58
58
  /**
59
59
  * ${e} middleware
@@ -64,7 +64,7 @@ export const ${e}: ServerRouteMiddleware = async () => {
64
64
  // Add your middleware logic here
65
65
  return next();
66
66
  };
67
- };`}};b([O({description:"The name of the middleware to generate",required:true})],te,"middlewareName"),b([P({description:"The path to the middleware to generate, default is src/middlewares",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/middlewares"})],te,"path");var re=class extends q{static commandName="generate-mqtt";static description="Generate a new MQTT handler in the specified path";static help=["Generate a new MQTT handler in the specified path","Example: npx balda generate-mqtt temperature-handler -p src/mqtt"];static path;static topic;static async handle(){let e=this.topic.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/),t=this.getMqttTemplate(!!e);this.path=x.join(this.path,`${Se(this.topic.replace(/\//g,"_"))}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`MQTT handler for topic ${this.topic} created successfully at ${this.path}`);}static getMqttTemplate(e){return `import { BaseMqtt, mqtt } from "balda";
67
+ };`}};b([E({description:"The name of the middleware to generate",required:true})],te,"middlewareName"),b([P({description:"The path to the middleware to generate, default is src/middlewares",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/middlewares"})],te,"path");var re=class extends q{static commandName="generate-mqtt";static description="Generate a new MQTT handler in the specified path";static help=["Generate a new MQTT handler in the specified path","Example: npx balda generate-mqtt temperature-handler -p src/mqtt"];static path;static topic;static async handle(){let e=this.topic.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/),t=this.getMqttTemplate(!!e);this.path=x.join(this.path,`${Se(this.topic.replace(/\//g,"_"))}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`MQTT handler for topic ${this.topic} created successfully at ${this.path}`);}static getMqttTemplate(e){return `import { BaseMqtt, mqtt } from "balda";
68
68
 
69
69
  /**
70
70
  * Define your MQTT topics interface for type safety
@@ -81,7 +81,7 @@ export default class extends BaseMqtt {
81
81
  this.logger.info({ message }, "Message received");
82
82
  // Implement your MQTT handler logic here
83
83
  }
84
- }`}};b([P({description:"The path to the MQTT handler to generate, default is src/mqtt",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/mqtt"})],re,"path"),b([O({description:"The MQTT topic to subscribe to",required:true})],re,"topic");var se=class extends q{static commandName="generate-plugin";static description="Generate a new plugin in the specified path";static help=["Generate a new plugin in the specified path","Example: npx balda generate-plugin my-plugin -p src/plugins"];static pluginName;static pluginPath;static async handle(){let e=this.getPluginTemplate();this.pluginPath=x.join(this.pluginPath,`${this.pluginName}.ts`),await y.exists(x.join(process.cwd(),this.pluginPath))||await y.mkdir(x.join(process.cwd(),this.pluginPath.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.pluginPath,new TextEncoder().encode(e)),this.logger.info(`Plugin ${this.name} created successfully at ${this.pluginPath}`);}static getPluginTemplate(){return `import { BasePlugin, Request, Response, NextFunction, ServerRouteMiddleware } from "balda";
84
+ }`}};b([P({description:"The path to the MQTT handler to generate, default is src/mqtt",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/mqtt"})],re,"path"),b([E({description:"The MQTT topic to subscribe to",required:true})],re,"topic");var se=class extends q{static commandName="generate-plugin";static description="Generate a new plugin in the specified path";static help=["Generate a new plugin in the specified path","Example: npx balda generate-plugin my-plugin -p src/plugins"];static pluginName;static pluginPath;static async handle(){let e=this.getPluginTemplate();this.pluginPath=x.join(this.pluginPath,`${this.pluginName}.ts`),await y.exists(x.join(process.cwd(),this.pluginPath))||await y.mkdir(x.join(process.cwd(),this.pluginPath.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.pluginPath,new TextEncoder().encode(e)),this.logger.info(`Plugin ${this.name} created successfully at ${this.pluginPath}`);}static getPluginTemplate(){return `import { BasePlugin, Request, Response, NextFunction, ServerRouteMiddleware } from "balda";
85
85
 
86
86
  export default class extends BasePlugin {
87
87
  async handle(): Promise<ServerRouteMiddleware> {
@@ -90,7 +90,7 @@ export default class extends BasePlugin {
90
90
  await next();
91
91
  };
92
92
  }
93
- }`}};b([O({description:"The name of the plugin to generate",required:true})],se,"pluginName"),b([P({description:"The path to the plugin to generate, default is src/plugins",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/plugins"})],se,"pluginPath");var V=class extends q{static commandName="generate-queue";static description="Generate a new queue in the specified path";static help=["Generate a new queue in the specified path","Example: npx balda generate-queue my-queue -p src/queues --provider bullmq"];static queueName;static path;static provider;static async handle(){let e=this.queueName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/),t=this.getQueueTemplate(!!e);this.path=x.join(this.path,`${Se(this.queueName)}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`Queue ${this.queueName} created successfully at ${this.path}`);}static getQueueTemplate(e){return `import { BaseQueue, queue } from "balda";
93
+ }`}};b([E({description:"The name of the plugin to generate",required:true})],se,"pluginName"),b([P({description:"The path to the plugin to generate, default is src/plugins",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/plugins"})],se,"pluginPath");var V=class extends q{static commandName="generate-queue";static description="Generate a new queue in the specified path";static help=["Generate a new queue in the specified path","Example: npx balda generate-queue my-queue -p src/queues --provider bullmq"];static queueName;static path;static provider;static async handle(){let e=this.queueName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/),t=this.getQueueTemplate(!!e);this.path=x.join(this.path,`${Se(this.queueName)}.ts`),await y.exists(x.join(process.cwd(),this.path))||await y.mkdir(x.join(process.cwd(),this.path.split("/").slice(0,-1).join("/")),{recursive:true}),await y.writeFile(this.path,new TextEncoder().encode(t)),this.logger.info(`Queue ${this.queueName} created successfully at ${this.path}`);}static getQueueTemplate(e){return `import { BaseQueue, queue } from "balda";
94
94
 
95
95
  export type Payload = {
96
96
  // Add your payload here
@@ -109,24 +109,24 @@ export default class extends BaseQueue {
109
109
  this.logger.info({ payload }, 'Payload received');
110
110
  return Promise.resolve();
111
111
  }
112
- }`}};b([O({description:"The name of the queue to generate",required:true})],V,"queueName"),b([P({description:"The path to the queue to generate, default is src/queues",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/queues"})],V,"path"),b([P({description:"The provider of the queue to generate, default is bullmq",type:"string",aliases:["pr"],name:"provider",required:false,defaultValue:"bullmq"})],V,"provider");var A=class extends q{static commandName="generate-sdk";static description="Generate a TypeScript SDK from your server's OpenAPI specification";static help=["Generate a TypeScript SDK from your server's OpenAPI specification","This command imports your server instance, starts it if needed, downloads the OpenAPI spec, and generates an SDK","","Arguments:"," serverPath Path to the server instance file (default: test/server/instance.ts)","","Flags:"," -o, --output <path> Output directory for generated SDK (default: sdk)"," -s, --swagger-path <path> Swagger UI path on your server (default: /docs)"," -c, --client <type> HTTP client to use: axios or fetch (default: fetch)"," --unwrap-response-data Automatically unwrap response data property"," --single-http-client Generate single HTTP client instance"," --type-prefix <prefix> Add prefix to all generated types"," --type-suffix <suffix> Add suffix to all generated types"," --enum-names-as-values Use enum names as values"," --sort-types Sort types alphabetically","","Examples:"," npx balda generate-sdk"," npx balda generate-sdk src/server/index.ts -o ./client-sdk"," npx balda generate-sdk src/server.ts --client axios"," npx balda generate-sdk --unwrap-response-data --single-http-client"," npx balda generate-sdk --type-prefix Api --sort-types"];static serverPath;static outputPath;static swaggerPath;static httpClient;static unwrapResponseData;static singleHttpClient;static typePrefix;static typeSuffix;static enumNamesAsValues;static sortTypes;static async handle(){console.log(`
112
+ }`}};b([E({description:"The name of the queue to generate",required:true})],V,"queueName"),b([P({description:"The path to the queue to generate, default is src/queues",type:"string",aliases:"p",name:"path",required:false,defaultValue:"src/queues"})],V,"path"),b([P({description:"The provider of the queue to generate, default is bullmq",type:"string",aliases:["pr"],name:"provider",required:false,defaultValue:"bullmq"})],V,"provider");var M=class extends q{static commandName="generate-sdk";static description="Generate a TypeScript SDK from your server's OpenAPI specification";static help=["Generate a TypeScript SDK from your server's OpenAPI specification","This command imports your server instance, starts it if needed, downloads the OpenAPI spec, and generates an SDK","","Arguments:"," serverPath Path to the server instance file (default: test/server/instance.ts)","","Flags:"," -o, --output <path> Output directory for generated SDK (default: sdk)"," -s, --swagger-path <path> Swagger UI path on your server (default: /docs)"," -c, --client <type> HTTP client to use: axios or fetch (default: fetch)"," --unwrap-response-data Automatically unwrap response data property"," --single-http-client Generate single HTTP client instance"," --type-prefix <prefix> Add prefix to all generated types"," --type-suffix <suffix> Add suffix to all generated types"," --enum-names-as-values Use enum names as values"," --sort-types Sort types alphabetically","","Examples:"," npx balda generate-sdk"," npx balda generate-sdk src/server/index.ts -o ./client-sdk"," npx balda generate-sdk src/server.ts --client axios"," npx balda generate-sdk --unwrap-response-data --single-http-client"," npx balda generate-sdk --type-prefix Api --sort-types"];static serverPath;static outputPath;static swaggerPath;static httpClient;static unwrapResponseData;static singleHttpClient;static typePrefix;static typeSuffix;static enumNamesAsValues;static sortTypes;static async handle(){console.log(`
113
113
  \u{1F680} Starting SDK generation...
114
114
  `);let e="swagger-typescript-api",t=x.join(process.cwd(),"node_modules"),r=await y.exists(t),n=x.join(t,e);if(!(r?await y.exists(n):false)){console.log(`\u{1F4E6} ${e} not found. Installing as dev dependency...
115
- `);let[w,S,k]=await j();if(!await M(`${w} ${S} ${e} ${k}`,w,[e],{stdio:"inherit"},true)){console.log(`\x1B[33m\u26A0\uFE0F SDK generation cancelled: swagger-typescript-api installation was skipped.\x1B[0m
115
+ `);let[w,S,A]=await k();if(!await $(`${w} ${S} ${e} ${A}`,w,[e],{stdio:"inherit"},true)){console.log(`\x1B[33m\u26A0\uFE0F SDK generation cancelled: swagger-typescript-api installation was skipped.\x1B[0m
116
116
  `);return}console.log(`
117
117
  \x1B[32m\u2705 ${e} installed successfully!\x1B[0m
118
118
  `);}let l=x.resolve(process.cwd(),this.serverPath);if(!await y.exists(l)){console.error(`\x1B[31m\u274C Error: Server file not found at ${l}\x1B[0m
119
119
  `);return}console.log(`\u{1F4C2} Loading server from: ${this.serverPath}`);let u;try{u=await import(`file://${l}`);}catch(w){console.error("\x1B[31m\u274C Error importing server module:\x1B[0m",w);return}let d=Object.keys(u),s=null;for(let w of d){let S=u[w];if("default"in S&&S.default&&(S=S.default),S&&"_brand"in S&&S._brand==="BaldaServer"&&typeof S=="object"&&"listen"in S){s=S,console.log(`\u2705 Found server instance in export: "${w}"
120
120
  `);break}}if(!s){console.error(`\x1B[31m\u274C Error: No Server instance found in ${this.serverPath}\x1B[0m`),console.log(`\x1B[90mExpected exports: ${d.join(", ")}\x1B[0m
121
121
  `);return}let a=false,p="";p="http://localhost:80";try{if(console.log(`\u{1F310} Starting server on ${p}...`),typeof s.listen=="function"&&!s.isListening)await new Promise((w,S)=>{try{s.listen(()=>{a=!0,console.log(`\u2705 Server started successfully!
122
- `),w();});}catch(k){S(k);}}).catch(w=>{console.error("Failed to start the server, continuing...");});else throw new Error("Server instance does not have a listen() method")}catch(w){console.error("\x1B[31m\u274C Error starting server:\x1B[0m",w);return}let h=this.swaggerPath??(typeof s.serverOptions.swagger!="boolean"?s.serverOptions.swagger?.path:"/docs"),f=x.join(p,h??"/docs","/json");console.log(`\u{1F4E5} Downloading OpenAPI spec from: ${f}`);let v;try{let w=await globalThis.fetch(f);if(!w.ok)throw new Error(`Failed to fetch OpenAPI spec: ${w.status} ${w.statusText}`);v=await w.json(),console.log(`\u2705 OpenAPI spec downloaded successfully!
122
+ `),w();});}catch(A){S(A);}}).catch(w=>{console.error("Failed to start the server, continuing...");});else throw new Error("Server instance does not have a listen() method")}catch(w){console.error("\x1B[31m\u274C Error starting server:\x1B[0m",w);return}let h=this.swaggerPath??(typeof s.serverOptions.swagger!="boolean"?s.serverOptions.swagger?.path:"/docs"),f=x.join(p,h??"/docs","/json");console.log(`\u{1F4E5} Downloading OpenAPI spec from: ${f}`);let v;try{let w=await globalThis.fetch(f);if(!w.ok)throw new Error(`Failed to fetch OpenAPI spec: ${w.status} ${w.statusText}`);v=await w.json(),console.log(`\u2705 OpenAPI spec downloaded successfully!
123
123
  `);}catch(w){if(console.error("\x1B[31m\u274C Error downloading OpenAPI spec:\x1B[0m",w),a){let S=s;try{await S.close?.();}catch{}}return}let C=x.join(process.cwd(),".openapi-spec.json");try{await y.writeFile(C,new TextEncoder().encode(JSON.stringify(v,null,2)));}catch(w){if(console.error("\x1B[31m\u274C Error saving OpenAPI spec to file:\x1B[0m",w),a){let S=s;try{await S.close?.();}catch{}}return}let T=x.resolve(process.cwd(),this.outputPath);console.log(`\u{1F528} Generating SDK...
124
124
  `);try{await y.exists(T)||await y.mkdir(T,{recursive:!0});let w=["npx swagger-typescript-api generate",`--path="${C}"`,`--output="${this.outputPath}"`,`--http-client ${this.httpClient}`,"--modular","--extract-request-params","--extract-request-body","--extract-response-body","--extract-response-error"];this.unwrapResponseData&&w.push("--unwrap-response-data"),this.singleHttpClient&&w.push("--single-http-client"),this.typePrefix&&w.push(`--type-prefix "${this.typePrefix}"`),this.typeSuffix&&w.push(`--type-suffix "${this.typeSuffix}"`),this.enumNamesAsValues&&w.push("--enum-names-as-values"),this.sortTypes&&w.push("--sort-types");let S=w.join(" ");execSync(S,{stdio:"inherit",cwd:process.cwd()}),console.log(`
125
125
  \x1B[32m\u2705 SDK generated successfully!\x1B[0m`),console.log(`\x1B[32m\u{1F4C1} SDK location: ${T}\x1B[0m
126
126
  `);}catch(w){console.error("\x1B[31m\u274C Error generating SDK:\x1B[0m",w);}finally{try{await y.unlink(C);}catch{}if(a){let w=s;try{console.log("\u{1F6D1} Stopping server..."),await w.close?.(),console.log(`\u2705 Server stopped.
127
127
  `);}catch{console.log(`\x1B[33m\u26A0\uFE0F Could not stop server gracefully.\x1B[0m
128
128
  `);}}}console.log(`\x1B[32m\u2728 SDK generation complete!\x1B[0m
129
- `);}};b([O({description:"Path to the server instance file (should export a Server instance)",required:false,defaultValue:"test/server/instance.ts"})],A,"serverPath"),b([P({description:"Output directory for generated SDK",type:"string",aliases:["o"],name:"output",required:false,defaultValue:"sdk"})],A,"outputPath"),b([P({description:"Swagger UI path on your server",type:"string",aliases:["s"],name:"swagger-path",required:false})],A,"swaggerPath"),b([P({description:"HTTP client to use (axios or fetch)",type:"string",aliases:["c"],name:"client",required:false,defaultValue:"fetch"})],A,"httpClient"),b([P({description:"Unwrap response data automatically",type:"boolean",name:"unwrap-response-data",required:false,defaultValue:false})],A,"unwrapResponseData"),b([P({description:"Generate single HTTP client instance",type:"boolean",name:"single-http-client",required:false,defaultValue:false})],A,"singleHttpClient"),b([P({description:"Add prefix to all generated types",type:"string",name:"type-prefix",required:false})],A,"typePrefix"),b([P({description:"Add suffix to all generated types",type:"string",name:"type-suffix",required:false})],A,"typeSuffix"),b([P({description:"Use enum names as values",type:"boolean",name:"enum-names-as-values",required:false,defaultValue:false})],A,"enumNamesAsValues"),b([P({description:"Sort types alphabetically",type:"boolean",name:"sort-types",required:false,defaultValue:false})],A,"sortTypes");var U=class extends q{static commandName="init";static description="Initialize a new balda project in the current directory";static help=["Initialize a new balda project, it is given for granted that balda is installed in the project as a dependency","All the files are created in the /src directory (created if not exists)","It adds a server.ts for the file instance and a index.ts for the entry point with a dummy hello world route","Example: npx balda init -p ./src -t true"];static srcPath;static typescript;static mqtt;static cron;static graphql;static devDependencies=["esbuild","esbuild-plugin-copy","tsx","typescript"];static async handle(){this.logger.info("Initializing project...");let[e,t,r]=await j();if(["npm","yarn","pnpm"].includes(e)){let o=await z(this.devDependencies);if(o.length&&(this.logger.info(`Found ${o.length} missing dev dependencies`),!await M(`${e} ${t} ${o.join(" ")} -${r}`,e,o,{stdio:"inherit"}))){this.logger.info("Installation cancelled by user. Project initialization aborted.");return}o.length||this.logger.info("All dev dependencies are already installed");}if(this.mqtt&&["npm","yarn","pnpm"].includes(e)){let o=await z(["mqtt"]);o.length&&(await M(`${e} ${t} mqtt`,e,["mqtt"],{stdio:"inherit"},false)||(this.logger.info("MQTT installation cancelled by user. Skipping MQTT scaffolding."),this.mqtt=false)),o.length||this.logger.info("MQTT package is already installed");}if(this.cron&&["npm","yarn","pnpm"].includes(e)){let o=await z(["node-cron"]);o.length>0&&(await M(`${e} ${t} node-cron`,e,["node-cron"],{stdio:"inherit"},false)||(this.logger.info("node-cron installation cancelled by user. Skipping Cron scaffolding."),this.cron=false)),o.length||this.logger.info("node-cron package is already installed");}if(this.graphql&&["npm","yarn","pnpm"].includes(e)){let o=await z(["@apollo/server","@graphql-tools/schema","graphql"]);o.length>0&&(await M(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false)||(this.logger.info("GraphQL installation cancelled by user. Skipping GraphQL scaffolding."),this.graphql=false)),o.length||this.logger.info("GraphQL packages are already installed");}let n=this.typescript?"ts":"js",i=this.getServerTemplate(),l=this.getIndexTemplate();if(y.exists(this.srcPath)||await y.mkdir(this.srcPath,{recursive:true}),this.logger.info(`Creating server.${n} file...`),await y.writeFile(`${this.srcPath}/server.${n}`,new TextEncoder().encode(i)),this.logger.info(`Creating index.${n} file...`),await y.writeFile(`${this.srcPath}/index.${n}`,new TextEncoder().encode(l)),this.mqtt){let o=x.join(this.srcPath,"mqtt");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getMqttConfigTemplate();this.logger.info(`Creating mqtt/mqtt.config.${n} file...`),await y.writeFile(x.join(o,`mqtt.config.${n}`),new TextEncoder().encode(u));}if(this.cron){let o=x.join(this.srcPath,"cron");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getCronConfigTemplate();this.logger.info(`Creating cron/cron.config.${n} file...`),await y.writeFile(x.join(o,`cron.config.${n}`),new TextEncoder().encode(u));}if(this.graphql){let o=x.join(this.srcPath,"graphql");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getGraphqlConfigTemplate();this.logger.info(`Creating graphql/graphql.config.${n} file...`),await y.writeFile(x.join(o,`graphql.config.${n}`),new TextEncoder().encode(u));}this.logger.info("Project initialized successfully!");}static getServerTemplate(){return `import { Server } from "balda";
129
+ `);}};b([E({description:"Path to the server instance file (should export a Server instance)",required:false,defaultValue:"test/server/instance.ts"})],M,"serverPath"),b([P({description:"Output directory for generated SDK",type:"string",aliases:["o"],name:"output",required:false,defaultValue:"sdk"})],M,"outputPath"),b([P({description:"Swagger UI path on your server",type:"string",aliases:["s"],name:"swagger-path",required:false})],M,"swaggerPath"),b([P({description:"HTTP client to use (axios or fetch)",type:"string",aliases:["c"],name:"client",required:false,defaultValue:"fetch"})],M,"httpClient"),b([P({description:"Unwrap response data automatically",type:"boolean",name:"unwrap-response-data",required:false,defaultValue:false})],M,"unwrapResponseData"),b([P({description:"Generate single HTTP client instance",type:"boolean",name:"single-http-client",required:false,defaultValue:false})],M,"singleHttpClient"),b([P({description:"Add prefix to all generated types",type:"string",name:"type-prefix",required:false})],M,"typePrefix"),b([P({description:"Add suffix to all generated types",type:"string",name:"type-suffix",required:false})],M,"typeSuffix"),b([P({description:"Use enum names as values",type:"boolean",name:"enum-names-as-values",required:false,defaultValue:false})],M,"enumNamesAsValues"),b([P({description:"Sort types alphabetically",type:"boolean",name:"sort-types",required:false,defaultValue:false})],M,"sortTypes");var F=class extends q{static commandName="init";static description="Initialize a new balda project in the current directory";static help=["Initialize a new balda project, it is given for granted that balda is installed in the project as a dependency","All the files are created in the /src directory (created if not exists)","It adds a server.ts for the file instance and a index.ts for the entry point with a dummy hello world route","Example: npx balda init -p ./src -t true"];static srcPath;static typescript;static mqtt;static cron;static graphql;static devDependencies=["esbuild","esbuild-plugin-copy","tsx","typescript"];static async handle(){this.logger.info("Initializing project...");let[e,t,r]=await k();if(["npm","yarn","pnpm"].includes(e)){let o=await z(this.devDependencies);if(o.length&&(this.logger.info(`Found ${o.length} missing dev dependencies`),!await $(`${e} ${t} ${o.join(" ")} -${r}`,e,o,{stdio:"inherit"}))){this.logger.info("Installation cancelled by user. Project initialization aborted.");return}o.length||this.logger.info("All dev dependencies are already installed");}if(this.mqtt&&["npm","yarn","pnpm"].includes(e)){let o=await z(["mqtt"]);o.length&&(await $(`${e} ${t} mqtt`,e,["mqtt"],{stdio:"inherit"},false)||(this.logger.info("MQTT installation cancelled by user. Skipping MQTT scaffolding."),this.mqtt=false)),o.length||this.logger.info("MQTT package is already installed");}if(this.cron&&["npm","yarn","pnpm"].includes(e)){let o=await z(["node-cron"]);o.length>0&&(await $(`${e} ${t} node-cron`,e,["node-cron"],{stdio:"inherit"},false)||(this.logger.info("node-cron installation cancelled by user. Skipping Cron scaffolding."),this.cron=false)),o.length||this.logger.info("node-cron package is already installed");}if(this.graphql&&["npm","yarn","pnpm"].includes(e)){let o=await z(["@apollo/server","@graphql-tools/schema","graphql"]);o.length>0&&(await $(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false)||(this.logger.info("GraphQL installation cancelled by user. Skipping GraphQL scaffolding."),this.graphql=false)),o.length||this.logger.info("GraphQL packages are already installed");}let n=this.typescript?"ts":"js",i=this.getServerTemplate(),l=this.getIndexTemplate();if(y.exists(this.srcPath)||await y.mkdir(this.srcPath,{recursive:true}),this.logger.info(`Creating server.${n} file...`),await y.writeFile(`${this.srcPath}/server.${n}`,new TextEncoder().encode(i)),this.logger.info(`Creating index.${n} file...`),await y.writeFile(`${this.srcPath}/index.${n}`,new TextEncoder().encode(l)),this.mqtt){let o=x.join(this.srcPath,"mqtt");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getMqttConfigTemplate();this.logger.info(`Creating mqtt/mqtt.config.${n} file...`),await y.writeFile(x.join(o,`mqtt.config.${n}`),new TextEncoder().encode(u));}if(this.cron){let o=x.join(this.srcPath,"cron");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getCronConfigTemplate();this.logger.info(`Creating cron/cron.config.${n} file...`),await y.writeFile(x.join(o,`cron.config.${n}`),new TextEncoder().encode(u));}if(this.graphql){let o=x.join(this.srcPath,"graphql");await y.exists(o)||await y.mkdir(o,{recursive:true});let u=this.getGraphqlConfigTemplate();this.logger.info(`Creating graphql/graphql.config.${n} file...`),await y.writeFile(x.join(o,`graphql.config.${n}`),new TextEncoder().encode(u));}this.logger.info("Project initialized successfully!");}static getServerTemplate(){return `import { Server } from "balda";
130
130
 
131
131
  const serverInstance = new Server({
132
132
  port: 80,
@@ -189,7 +189,7 @@ server.listen(({ url }) => {
189
189
  // Add your GraphQL type definitions and resolvers in separate files within this directory
190
190
  // The GraphQL endpoint is automatically available at /graphql
191
191
  // You can extend the schema using server.graphql.addTypeDef() and server.graphql.addResolver()
192
- `}};b([P.string({description:"The path to the project, default is the current directory /src",aliases:"p",name:"path",required:false,defaultValue:"./src"})],U,"srcPath"),b([P.boolean({description:"Whether to use typescript, default is true",aliases:"t",name:"typescript",required:false,defaultValue:true})],U,"typescript"),b([P.boolean({description:"Initialize MQTT service connection",aliases:"m",name:"mqtt",required:false,defaultValue:false})],U,"mqtt"),b([P.boolean({description:"Initialize Cron service",aliases:"c",name:"cron",required:false,defaultValue:false})],U,"cron"),b([P.boolean({description:"Initialize GraphQL service",aliases:"g",name:"graphql",required:false,defaultValue:false})],U,"graphql");var oe=class extends q{static commandName="init-mailer";static description="Initialize mailer configuration with required dependencies";static help=["Initialize a mailer configuration file with basic setup","Automatically installs required packages for nodemailer and optional template engines","Supports Handlebars, EJS, Edge.js, Mustache, or custom adapters","Example: npx balda init-mailer -t handlebars -o src/mailer"];static templateEngine;static outputPath;static mailerDependencies={base:["nodemailer","@types/nodemailer"],handlebars:["handlebars","@types/handlebars"],ejs:["ejs","@types/ejs"],edge:["edge.js"],mustache:["mustache","@types/mustache"]};static async handle(){if(this.logger.info("Initializing mailer configuration..."),!["handlebars","ejs","edge","mustache","custom","none"].includes(this.templateEngine)){this.logger.error(`Invalid template engine: ${this.templateEngine}. Must be one of: handlebars, ejs, edge, mustache, custom, none`);return}let[e,t]=await j();if(["npm","yarn","pnpm"].includes(e)){let l=[...this.mailerDependencies.base,...this.templateEngine!=="none"&&this.templateEngine!=="custom"?this.mailerDependencies[this.templateEngine]||[]:[]],o=await z(l);if(o.length>0&&(this.logger.info(`Found ${o.length} missing dependencies for mailer`),!await M(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false))){this.logger.info("Installation cancelled by user. Mailer initialization aborted.");return}o.length===0&&this.logger.info("All mailer dependencies are already installed");}let r=this.getConfigTemplate(),n="mailer.config.ts",i=x.join(this.outputPath,n);await y.exists(this.outputPath)||await y.mkdir(this.outputPath,{recursive:true}),this.logger.info(`Creating ${n} file at ${this.outputPath}...`),await y.writeFile(i,new TextEncoder().encode(r)),this.logger.info(`Mailer configuration initialized successfully at ${i}`),this.logger.info("Remember to update the configuration with your actual SMTP credentials"),this.templateEngine!=="none"&&this.logger.info(`Template engine '${this.templateEngine}' configured and ready to use`);}static getConfigTemplate(){let e=this.templateEngine!=="none";return `import { createTransport } from "nodemailer";
192
+ `}};b([P.string({description:"The path to the project, default is the current directory /src",aliases:"p",name:"path",required:false,defaultValue:"./src"})],F,"srcPath"),b([P.boolean({description:"Whether to use typescript, default is true",aliases:"t",name:"typescript",required:false,defaultValue:true})],F,"typescript"),b([P.boolean({description:"Initialize MQTT service connection",aliases:"m",name:"mqtt",required:false,defaultValue:false})],F,"mqtt"),b([P.boolean({description:"Initialize Cron service",aliases:"c",name:"cron",required:false,defaultValue:false})],F,"cron"),b([P.boolean({description:"Initialize GraphQL service",aliases:"g",name:"graphql",required:false,defaultValue:false})],F,"graphql");var oe=class extends q{static commandName="init-mailer";static description="Initialize mailer configuration with required dependencies";static help=["Initialize a mailer configuration file with basic setup","Automatically installs required packages for nodemailer and optional template engines","Supports Handlebars, EJS, Edge.js, Mustache, or custom adapters","Example: npx balda init-mailer -t handlebars -o src/mailer"];static templateEngine;static outputPath;static mailerDependencies={base:["nodemailer","@types/nodemailer"],handlebars:["handlebars","@types/handlebars"],ejs:["ejs","@types/ejs"],edge:["edge.js"],mustache:["mustache","@types/mustache"]};static async handle(){if(this.logger.info("Initializing mailer configuration..."),!["handlebars","ejs","edge","mustache","custom","none"].includes(this.templateEngine)){this.logger.error(`Invalid template engine: ${this.templateEngine}. Must be one of: handlebars, ejs, edge, mustache, custom, none`);return}let[e,t]=await k();if(["npm","yarn","pnpm"].includes(e)){let l=[...this.mailerDependencies.base,...this.templateEngine!=="none"&&this.templateEngine!=="custom"?this.mailerDependencies[this.templateEngine]||[]:[]],o=await z(l);if(o.length>0&&(this.logger.info(`Found ${o.length} missing dependencies for mailer`),!await $(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false))){this.logger.info("Installation cancelled by user. Mailer initialization aborted.");return}o.length===0&&this.logger.info("All mailer dependencies are already installed");}let r=this.getConfigTemplate(),n="mailer.config.ts",i=x.join(this.outputPath,n);await y.exists(this.outputPath)||await y.mkdir(this.outputPath,{recursive:true}),this.logger.info(`Creating ${n} file at ${this.outputPath}...`),await y.writeFile(i,new TextEncoder().encode(r)),this.logger.info(`Mailer configuration initialized successfully at ${i}`),this.logger.info("Remember to update the configuration with your actual SMTP credentials"),this.templateEngine!=="none"&&this.logger.info(`Template engine '${this.templateEngine}' configured and ready to use`);}static getConfigTemplate(){let e=this.templateEngine!=="none";return `import { createTransport } from "nodemailer";
193
193
  import { Mailer${e?`, ${this.getAdapterImport()}`:""} } from "balda";
194
194
 
195
195
  /**
@@ -316,7 +316,7 @@ const adapter = new CustomAdapter();
316
316
  * appName: "My App",
317
317
  * },
318
318
  * });
319
- *`}};b([P.string({description:"Template engine (handlebars, ejs, edge, mustache, custom, none) - optional",aliases:"t",name:"template",required:false,defaultValue:"none"})],oe,"templateEngine"),b([P.string({description:"Output directory for mailer configuration, default is src/mailer",aliases:"o",name:"output",required:false,defaultValue:"src/mailer"})],oe,"outputPath");var ne=class extends q{static commandName="init-queue";static description="Initialize queue provider configuration with required dependencies";static help=["Initialize a queue provider configuration file with basic credentials","Automatically installs required packages for the selected provider","Only scaffolds the connection, handlers should be created with generate-queue command","Example: npx balda init-queue -t bullmq -o src/queue"];static queueType;static outputPath;static queueDependencies={bullmq:["bullmq","ioredis"],sqs:["@aws-sdk/client-sqs","sqs-consumer"],pgboss:["pg-boss","pg"]};static async handle(){if(this.logger.info(`Initializing ${this.queueType} queue provider...`),!["bullmq","sqs","pgboss"].includes(this.queueType)){this.logger.error(`Invalid queue type: ${this.queueType}. Must be one of: bullmq, sqs, pgboss`);return}let[e,t]=await j();if(["npm","yarn","pnpm"].includes(e)){let l=this.queueDependencies[this.queueType],o=await z(l);if(o.length>0&&(this.logger.info(`Found ${o.length} missing dependencies for ${this.queueType}`),!await M(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false))){this.logger.info("Installation cancelled by user. Queue initialization aborted.");return}o.length===0&&this.logger.info(`All ${this.queueType} dependencies are already installed`);}let r=this.getConfigTemplate(),n=`${this.queueType}.config.ts`,i=x.join(this.outputPath,n);await y.exists(this.outputPath)||await y.mkdir(this.outputPath,{recursive:true}),this.logger.info(`Creating ${n} file at ${this.outputPath}...`),await y.writeFile(i,new TextEncoder().encode(r)),this.logger.info(`Queue configuration initialized successfully at ${i}`),this.logger.info("Remember to update the configuration with your actual credentials"),this.logger.info("Use 'npx balda generate-queue' to create queue handlers");}static getConfigTemplate(){return this.queueType==="bullmq"?this.getBullMQTemplate():this.queueType==="sqs"?this.getSQSTemplate():this.queueType==="pgboss"?this.getPGBossTemplate():""}static getBullMQTemplate(){return `import { defineBullMQConfiguration } from "balda";
319
+ *`}};b([P.string({description:"Template engine (handlebars, ejs, edge, mustache, custom, none) - optional",aliases:"t",name:"template",required:false,defaultValue:"none"})],oe,"templateEngine"),b([P.string({description:"Output directory for mailer configuration, default is src/mailer",aliases:"o",name:"output",required:false,defaultValue:"src/mailer"})],oe,"outputPath");var ne=class extends q{static commandName="init-queue";static description="Initialize queue provider configuration with required dependencies";static help=["Initialize a queue provider configuration file with basic credentials","Automatically installs required packages for the selected provider","Only scaffolds the connection, handlers should be created with generate-queue command","Example: npx balda init-queue -t bullmq -o src/queue"];static queueType;static outputPath;static queueDependencies={bullmq:["bullmq","ioredis"],sqs:["@aws-sdk/client-sqs","sqs-consumer"],pgboss:["pg-boss","pg"]};static async handle(){if(this.logger.info(`Initializing ${this.queueType} queue provider...`),!["bullmq","sqs","pgboss"].includes(this.queueType)){this.logger.error(`Invalid queue type: ${this.queueType}. Must be one of: bullmq, sqs, pgboss`);return}let[e,t]=await k();if(["npm","yarn","pnpm"].includes(e)){let l=this.queueDependencies[this.queueType],o=await z(l);if(o.length>0&&(this.logger.info(`Found ${o.length} missing dependencies for ${this.queueType}`),!await $(`${e} ${t} ${o.join(" ")}`,e,o,{stdio:"inherit"},false))){this.logger.info("Installation cancelled by user. Queue initialization aborted.");return}o.length===0&&this.logger.info(`All ${this.queueType} dependencies are already installed`);}let r=this.getConfigTemplate(),n=`${this.queueType}.config.ts`,i=x.join(this.outputPath,n);await y.exists(this.outputPath)||await y.mkdir(this.outputPath,{recursive:true}),this.logger.info(`Creating ${n} file at ${this.outputPath}...`),await y.writeFile(i,new TextEncoder().encode(r)),this.logger.info(`Queue configuration initialized successfully at ${i}`),this.logger.info("Remember to update the configuration with your actual credentials"),this.logger.info("Use 'npx balda generate-queue' to create queue handlers");}static getConfigTemplate(){return this.queueType==="bullmq"?this.getBullMQTemplate():this.queueType==="sqs"?this.getSQSTemplate():this.queueType==="pgboss"?this.getPGBossTemplate():""}static getBullMQTemplate(){return `import { defineBullMQConfiguration } from "balda";
320
320
 
321
321
  // Configure BullMQ connection and default options
322
322
  defineBullMQConfiguration({
@@ -488,21 +488,21 @@ definePGBossConfiguration({
488
488
  `}};b([P.string({description:"Queue provider type (bullmq, sqs, pgboss) - required",aliases:"t",name:"type",required:true})],ne,"queueType"),b([P.string({description:"Output directory for queue configuration, default is src/queue",aliases:"o",name:"output",required:false,defaultValue:"src/queue"})],ne,"outputPath");var fe=class extends q{static commandName="key-generate";static description="Generate application encryption key pairs";static help=["Generate secure RSA public/private key pairs for application encryption","Keys are automatically saved to .env file","Example: npx balda key-generate","Example: npx balda key-generate --type async"];static type;static async handle(){let e=this.type??"sync";if(!["sync","async"].includes(e)){this.logger.error(`Invalid key type: ${e}. Must be sync or async`);return}let{publicKey:t,privateKey:r}=yt.generateKeyPairSync("rsa",{modulusLength:2048,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}}),n=e==="sync"?"APP_PUBLIC_KEY":"APP_PUBLIC_KEY_ASYNC",i=e==="sync"?"APP_PRIVATE_KEY":"APP_PRIVATE_KEY_ASYNC";console.log(`
489
489
  \u2728 Generated ${e==="sync"?"Sync":"Async"} Key Pair:
490
490
  `),console.log(`\x1B[33mPublic Key (${n}):\x1B[0m`),console.log(`\x1B[32m${t}\x1B[0m`),console.log(`\x1B[33mPrivate Key (${i}):\x1B[0m`),console.log(`\x1B[32m${r}\x1B[0m`),await this.saveKeyToEnvFile(n,t),await this.saveKeyToEnvFile(i,r),console.log(`\x1B[90m\u{1F4A1} Keys saved to .env file as ${n} and ${i}\x1B[0m
491
- `);}static async saveKeyToEnvFile(e,t){let r=Ce.join(Q.getCwd(),".env"),n=`${e}="${t}"`;if(ye.existsSync(r)){let i=ye.readFileSync(r,"utf-8"),l=new RegExp(`^${e}=.*$`,"m");if(l.test(i)){let o=i.replace(l,n);ye.writeFileSync(r,o),this.logger.info(`Updated ${e} in .env file`);return}ye.appendFileSync(r,`
491
+ `);}static async saveKeyToEnvFile(e,t){let r=Ce.join(D.getCwd(),".env"),n=`${e}="${t}"`;if(ye.existsSync(r)){let i=ye.readFileSync(r,"utf-8"),l=new RegExp(`^${e}=.*$`,"m");if(l.test(i)){let o=i.replace(l,n);ye.writeFileSync(r,o),this.logger.info(`Updated ${e} in .env file`);return}ye.appendFileSync(r,`
492
492
  ${n}
493
493
  `),this.logger.info(`Added ${e} to .env file`);return}ye.writeFileSync(r,`${n}
494
- `),this.logger.info(`Created .env file with ${e}`);}};b([P.string({aliases:["t"],name:"type",required:false,defaultValue:"sync",description:"Key type: sync or async (default: sync)"})],fe,"type");var be=class extends q{static commandName="list";static description="List all available commands";static help=["Display all registered Balda CLI commands with their descriptions","Example: npx balda list"];static async handle(){let e=_.getBuiltInCommands(),t=_.getUserDefinedCommands();if(console.log(`
494
+ `),this.logger.info(`Created .env file with ${e}`);}};b([P.string({aliases:["t"],name:"type",required:false,defaultValue:"sync",description:"Key type: sync or async (default: sync)"})],fe,"type");var be=class extends q{static commandName="list";static description="List all available commands";static help=["Display all registered Balda CLI commands with their descriptions","Example: npx balda list"];static async handle(){let e=I.getBuiltInCommands(),t=I.getUserDefinedCommands();if(console.log(`
495
495
  \u2728 Available Balda Commands:
496
496
  `),t.length>0){console.log(`\x1B[1;33mUser Commands:\x1B[0m
497
497
  `);let r=this.groupByCategory(t);this.displayCategorizedCommands(r);}if(e.length>0){console.log(`\x1B[1;32mBuilt-in Commands:\x1B[0m
498
498
  `);let r=e.sort((i,l)=>i.commandName.localeCompare(l.commandName)),n=Math.max(...r.map(i=>i.commandName.length));for(let i of r){let l=i.commandName.padEnd(n+2),o=i.description||"No description available",u="";i.options?.deprecated&&(u=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${l}\x1B[0m ${o}${u}`);}console.log("");}console.log(`\x1B[90mRun 'npx balda <command> -h' for more information on a specific command.\x1B[0m
499
- `);}static groupByCategory(e){let t=new Map;for(let r of e){let n=r.options?.category||"other";t.has(n)||t.set(n,[]),t.get(n).push(r);}return t}static displayCategorizedCommands(e){let t=Array.from(e.keys()).sort();for(let r of t){let n=e.get(r).filter(u=>u&&u.commandName).sort((u,d)=>u.commandName.localeCompare(d.commandName));if(n.length===0)continue;let l={generator:"\x1B[35m",setup:"\x1B[34m",production:"\x1B[32m",utility:"\x1B[36m",other:"\x1B[37m"}[r]||"\x1B[37m";console.log(` ${l}${r.toUpperCase()}:\x1B[0m`);let o=Math.max(...n.map(u=>u.commandName.length));for(let u of n){let d=u.commandName.padEnd(o+2),s=u.description||"No description available",a="";u.options?.deprecated&&(a=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${d}\x1B[0m ${s}${a}`);}console.log("");}}};var D=class extends X{constructor(...e){super(`Library not installed: ${e.join(", ")}, try run npm install ${e.join(" ")}`);}};var Le=class{randomUUID(){return crypto.randomUUID()}},ve=new Le;var H=class{static options={connection:{}}};var Ee=class{queues=new Map;workers=new Map;async publish(e,t,r){let n=await this.getQueue(e),i=ve.randomUUID();return await n.add(e,t,{jobId:i,...H.options?.defaultJobOptions,...r}),{id:i}}async subscribe(e,t){if(this.workers.has(e))throw new Error(`[BullMQ] Already subscribed to topic "${e}"`);let{errorHandler:r,...n}=H.options??{},i=await this.getBullMQClient(),l=new i.Worker(e,async o=>{try{await t(o.data);}catch(u){await r?.(o,u)??Promise.reject(u);}},{...n});this.workers.set(e,l);}async unsubscribe(e){let t=this.workers.get(e);t&&(await t.close(),this.workers.delete(e));}async getQueue(e){if(!this.queues.has(e)){let t=await import('bullmq').catch(()=>{throw new D("bullmq","ioredis")}),r=new t.Queue(e,{...H.options||{connection:{}}});this.queues.set(e,r);}return this.queues.get(e)}async getBullMQClient(){return this.bullmqClient||(this.bullmqClient=await import('bullmq').catch(()=>{throw new D("bullmq","ioredis")})),this.bullmqClient}async publishWithConfig(e,t,r,n){let i=await this.getQueueWithConfig(e,n),l=ve.randomUUID(),o={jobId:l,...H.options?.defaultJobOptions,...n?.defaultJobOptions,...r};return await i.add(e,t,o),{id:l}}async subscribeWithConfig(e,t,r){let n=this.getWorkerKey(e,r);if(this.workers.has(n))throw new Error(`[BullMQ] Already subscribed to topic "${e}"`);let i=H.options??{},{errorHandler:l}=i,o=await this.getBullMQClient(),u={...i,...r};delete u.errorHandler,delete u.defaultJobOptions;let d=new o.Worker(e,async s=>{try{await t(s.data);}catch(a){await l?.(s,a)??Promise.reject(a);}},u);this.workers.set(n,d);}async getQueueWithConfig(e,t){let r=this.getQueueKey(e,t);if(!this.queues.has(r)){let n=await import('bullmq').catch(()=>{throw new D("bullmq","ioredis")}),i={...H.options||{connection:{}},...t},l=new n.Queue(e,i);this.queues.set(r,l);}return this.queues.get(r)}getQueueKey(e,t){return t?.connection?`${e}:${JSON.stringify(t.connection)}`:e}getWorkerKey(e,t){return this.getQueueKey(e,t)}};var Oe=class{subscribers=new Map;messageQueue=[];maxQueueSize;processingPromise=null;constructor(e=1e4){this.maxQueueSize=e;}clear(){this.subscribers.clear(),this.messageQueue=[],this.processingPromise=null;}async publish(e,t,r){if(this.messageQueue.length>=this.maxQueueSize)throw new Error(`Queue full: ${this.maxQueueSize} messages pending`);let n=ve.randomUUID();return this.messageQueue.push({topic:e,payload:t}),this.processingPromise||(this.processingPromise=this.processQueue().catch(i=>{console.error("[MemoryPubSub] Fatal queue processing error:",i);}).finally(()=>{this.processingPromise=null;})),{id:n}}async subscribe(e,t){this.subscribers.has(e)||this.subscribers.set(e,new Set);let r=t;this.subscribers.get(e).add(r);}async unsubscribe(e){this.subscribers.delete(e);}async processQueue(){for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();if(!e)break;let t=this.subscribers.get(e.topic);t&&t.size>0&&await Promise.allSettled(Array.from(t).map(r=>r(e.payload).catch(n=>{console.error(`[MemoryPubSub] Handler error for topic "${e.topic}":`,n);})));}}};var B=class{static options={}};var $e=class{createdQueues=new Set;workers=new Map;async publish(e,t,r){let n=await this.getBoss();await this.ensureQueue(e);let i=r||{},l=await n.send(e,t,i);return {id:String(l??"")}}async subscribe(e,t){let r=await this.getBoss();await this.ensureQueue(e);let n=B.options;n.errorHandler&&r.on("error",n.errorHandler);let i=await r.work(e,async l=>{let o=Array.isArray(l)?l:[l];for(let u of o)await t(u.data);});this.workers.set(e,i);}async unsubscribe(e){let t=await this.getBoss(),r=this.workers.get(e);r&&(await t.offWork(r),this.workers.delete(e));}async getBoss(){if(this.boss)return this.boss;let e=await import('pg-boss').catch(()=>{throw new D("pg-boss","pg")}),r=e.PgBoss.default??e.PgBoss,{connectionString:n,boss:i}=B.options,l=n??i,o=new r(l);return B.options?.errorHandler&&o.on("error",B.options.errorHandler),await o.start(),this.boss=o,this.boss}async ensureQueue(e){if(this.createdQueues.has(e))return;let t=await this.getBoss();typeof t.createQueue=="function"&&await t.createQueue(e),this.createdQueues.add(e);}async publishWithConfig(e,t,r,n){let i=await this.getBossWithConfig(n);await this.ensureQueueWithBoss(e,i);let l=r||{},o=await i.send(e,t,l);return {id:String(o??"")}}async subscribeWithConfig(e,t,r){let n=await this.getBossWithConfig(r);await this.ensureQueueWithBoss(e,n);let i=B.options;i.errorHandler&&n.on("error",i.errorHandler);let l=`${e}:${r?.connectionString??"default"}`,o=await n.work(e,async u=>{let d=Array.isArray(u)?u:[u];for(let s of d)await t(s.data);});this.workers.set(l,o);}bossInstances=new Map;async getBossWithConfig(e){if(!e?.connectionString)return this.getBoss();let t=e.connectionString;if(this.bossInstances.has(t))return this.bossInstances.get(t);let r=await import('pg-boss').catch(()=>{throw new D("pg-boss","pg")}),i=r.PgBoss.default??r.PgBoss,l=new i(e.connectionString);return B.options?.errorHandler&&l.on("error",B.options.errorHandler),await l.start(),this.bossInstances.set(t,l),l}async ensureQueueWithBoss(e,t){this.createdQueues.has(e)||(typeof t.createQueue=="function"&&await t.createQueue(e),this.createdQueues.add(e));}};var ie=class{static options={}};var Me=class{consumers=new Map;client;async publish(e,t,r){let n=await this.getClient(),{SendMessageCommand:i}=await this.getSqsLib(),l=await this.resolveQueueUrl(e);if(!l)throw new Error(`[SQS] Queue url not configured for topic "${e}"`);let o=new i({MessageBody:JSON.stringify(t),MessageAttributes:{topic:{DataType:"String",StringValue:e}},QueueUrl:l,...r??{}});return {id:(await n.send(o)).MessageId??""}}async subscribe(e,t){if(this.consumers.has(e))throw new Error(`[SQS] Already subscribed to topic "${e}"`);let r=ie.options,n=await this.getSqsConsumerLib(),i={...r.consumer||{},sqs:await this.getClient(),queueUrl:await this.resolveQueueUrl(e),handleMessage:async u=>{let d=JSON.parse(u.Body||"{}");await t(d);}},l=n.Consumer.create(i),o=r.errorHandler;o&&(l.on("error",o),l.on("processing_error",o)),l.start(),this.consumers.set(e,l);}async unsubscribe(e){let t=this.consumers.get(e);t&&(t.stop(),this.consumers.delete(e));}async getClient(){if(this.client)return this.client;let{SQSClient:e}=await this.getSqsLib(),t=ie.options.client;return this.client=new e(t??{}),this.client}async getSqsLib(){return this.sqsLib||(this.sqsLib=await import('@aws-sdk/client-sqs').catch(()=>{throw new D("@aws-sdk/client-sqs","sqs-consumer")})),this.sqsLib}async getSqsConsumerLib(){return this.sqsConsumerLib||(this.sqsConsumerLib=await import('sqs-consumer').catch(()=>{throw new D("sqs-consumer")})),this.sqsConsumerLib}async resolveQueueUrl(e){let r=ie.options.consumer?.queueUrlMap;if(!r?.[e])throw new Error(`[SQS] Queue url not configured for topic "${e}"`);return r[e]}async publishWithConfig(e,t,r,n){let{...i}=r??{},l=await this.getClientWithConfig(n?.client),{SendMessageCommand:o}=await this.getSqsLib(),u=n?.queueUrl??await this.resolveQueueUrl(e),d=new o({...i,MessageBody:JSON.stringify(t),MessageAttributes:{topic:{DataType:"String",StringValue:e}},QueueUrl:u});return {id:(await l.send(d)).MessageId??""}}async subscribeWithConfig(e,t,r){if(this.consumers.has(e))throw new Error(`[SQS] Already subscribed to topic "${e}"`);let n=ie.options,i=await this.getSqsConsumerLib(),l=r?.queueUrl??await this.resolveQueueUrl(e),o={...n.consumer||{},sqs:await this.getClientWithConfig(r?.client),queueUrl:l,handleMessage:async s=>{let a=JSON.parse(s.Body||"{}");await t(a);}},u=i.Consumer.create(o),d=n.errorHandler;d&&(u.on("error",d),u.on("processing_error",d)),u.start(),this.consumers.set(e,u);}async getClientWithConfig(e){if(e){let{SQSClient:t}=await this.getSqsLib();return new t(e)}return this.getClient()}};var Ae=class{static map=new Map;static{this.map.set("bullmq",new Ee),this.map.set("sqs",new Me),this.map.set("pgboss",new $e),this.map.set("memory",new Oe);}static getProvider(e){if(!this.map.has(e))throw new Error(`[QueueSubscriber] Provider ${e} not found`);return this.map.get(e)}static setProvider(e,t){this.map.set(e,t);}static clearMemoryProvider(){let e=this.map.get("memory");e&&"clear"in e&&e.clear();}};var ae=class{static typedQueueSubscribers=new Map;static customQueueSubscribers=new Map;static instanceFactory=e=>new e;static registerTypedQueue(e,t,r,n,i){let l=`${n}:${t}:${e}`;this.typedQueueSubscribers.has(l)&&R.warn(`Queue handler for ${l} already registered, overwriting previous handler`),this.typedQueueSubscribers.set(l,{name:e,topic:t,handler:r,provider:n,queueOptions:i});}static registerCustomQueue(e,t,r,n){let i=`${n.constructor.name}:${t}:${e}`;this.customQueueSubscribers.has(i)&&R.warn(`Custom queue handler for ${i} already registered, overwriting previous handler`),this.customQueueSubscribers.set(i,{name:e,topic:t,handler:r,pubsub:n});}static async run(){R.info("Subscribing queue handlers");let e=this.typedQueueSubscribers.size>0,t=this.customQueueSubscribers.size>0;if(!e&&!t){R.info("No queue handlers to subscribe");return}for(let r of this.typedQueueSubscribers.values()){let{topic:n,handler:i,provider:l,queueOptions:o}=r;R.info(`Subscribing to queue: ${n}`);let u=Ae.getProvider(l);o&&"subscribeWithConfig"in u&&typeof u.subscribeWithConfig=="function"?await u.subscribeWithConfig(n,i,o):await u.subscribe(n,i);}for(let{topic:r,handler:n,pubsub:i}of this.customQueueSubscribers.values())R.info(`Subscribing to custom queue: ${r}`),await i.subscribe(r,n);R.info("Queue handlers subscribed");}static async massiveImportQueues(e,t={}){let r=[];for(let n of e){let i=await glob(n,{absolute:true,cwd:Q.getCwd()});R.info(`Pattern "${n}" matched ${i.length} file(s)`),r.push(...i);}if(r.length===0){R.warn("No files matched the provided patterns");return}R.info(`Importing ${r.length} queue handler file(s)`),await Promise.all(r.map(async n=>{R.debug(`Importing: ${n}`),await import(n).catch(i=>{if(R.error(`Error importing queue handler: ${n}`),R.error(i),t.throwOnError)throw i});})),R.info(`Successfully imported ${r.length} file(s)`);}};var le=class extends q{static commandName="queue-start";static description="Start queue workers to process jobs";static help=["Start queue workers to process jobs from registered queues","Loads queue handlers from specified patterns and starts processing","Example: npx balda queue-start","Example: npx balda queue-start src/queues/**/*.ts --patterns src/jobs/**/*.ts"];static options={keepAlive:true};static pattern;static additionalPatterns;static async handle(){this.logger.info("Starting queue workers...");let e=[this.pattern];this.additionalPatterns&&this.additionalPatterns.length>0&&e.push(...this.additionalPatterns),this.logger.info(`Loading queue handlers from patterns: ${e.join(", ")}`),await ae.massiveImportQueues(e,{throwOnError:false});let t=ae.typedQueueSubscribers.size,r=ae.customQueueSubscribers.size,n=t+r;if(n===0){this.logger.warn("No queue handlers found. Make sure your queue handlers are decorated with @queue decorator");return}this.logger.info(`Found ${n} queue handler(s) (${t} typed, ${r} custom)`),ae.run().then(()=>{this.logger.info("Queue workers started successfully. Press Ctrl+C to stop.");}).catch(i=>{this.logger.error("Error starting queue workers",i);});}};b([O({required:false,defaultValue:"src/queues/**/*.{ts,js}",description:"Primary glob pattern for queue handlers (default: src/queues/**/*.{ts,js})"})],le,"pattern"),b([P.list({aliases:["p"],name:"patterns",required:false,description:"Additional glob patterns for queue handlers"})],le,"additionalPatterns");var ce=class extends q{static commandName="serve";static description="Run the server in dev mode with hot reload";static help=["This command is intended to be run from the root of the project","Bun and Deno have native dev hot reload","Runtime is automatically inferred","Node.js requires tsx to be installed for both typescript and javascript files","Node.js dev dependencies (tsx) are installed automatically if not detected"];static runtime=E.type;static options={keepAlive:true};static entry;static denoImportMap;static async handle(){if(this.runtime==="bun"){execSync(`bun run --watch ${this.entry}`,{stdio:"inherit",cwd:Q.getCwd()});return}if(this.runtime==="deno"){let e="deno run --watch --unstable-sloppy-imports --allow-all";this.denoImportMap&&(e=`${e} --import-map ${this.denoImportMap}`),execSync(`${e} ${this.entry}`,{stdio:"inherit",cwd:Q.getCwd()});return}this.handleNodeHotReload();}static async handleNodeHotReload(){if(!ye.existsSync("node_modules/.bin/tsx")){let[r,n,i]=await j();if(!await M(`${r} ${n} tsx ${i}`,r,["tsx"],{stdio:"inherit"})){this.logger.info("Installation cancelled by user. Cannot start dev server without tsx.");return}}let t=this.calledBy!=="node"?this.calledBy:"npx";execSync(`${t} tsx watch ${this.entry}`,{stdio:"inherit",cwd:process.cwd()});}};b([O({required:false,defaultValue:"src/index.ts",description:"The entry point of the project, default is src/index.ts"})],ce,"entry"),b([P.string({aliases:["d"],name:"deno-import-map",required:false,description:"Path to deno import map"})],ce,"denoImportMap");var ue=class extends q{static commandName="init-storage";static description="Setup storage provider with required dependencies";static help=["Install dependencies and create storage configuration for a specific provider","Flags:"," -t, --type <provider> Storage provider type (s3, azure, local)"," -o, --output <path> Output directory for storage setup (default: src/storage/)","","Examples:"," npx balda setup:storage -t s3"," npx balda setup:storage --type azure --output src/config/"];static storageType;static outputPath;static async handle(){if(!this.storageType){console.error("\x1B[31m\u274C Error: Storage type is required. Use -t or --type flag.\x1B[0m"),console.log(`\x1B[90mExample: npx balda setup:storage -t s3\x1B[0m
499
+ `);}static groupByCategory(e){let t=new Map;for(let r of e){let n=r.options?.category||"other";t.has(n)||t.set(n,[]),t.get(n).push(r);}return t}static displayCategorizedCommands(e){let t=Array.from(e.keys()).sort();for(let r of t){let n=e.get(r).filter(u=>u&&u.commandName).sort((u,d)=>u.commandName.localeCompare(d.commandName));if(n.length===0)continue;let l={generator:"\x1B[35m",setup:"\x1B[34m",production:"\x1B[32m",utility:"\x1B[36m",other:"\x1B[37m"}[r]||"\x1B[37m";console.log(` ${l}${r.toUpperCase()}:\x1B[0m`);let o=Math.max(...n.map(u=>u.commandName.length));for(let u of n){let d=u.commandName.padEnd(o+2),s=u.description||"No description available",a="";u.options?.deprecated&&(a=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${d}\x1B[0m ${s}${a}`);}console.log("");}}};var _=class extends X{constructor(...e){super(`Library not installed: ${e.join(", ")}, try run npm install ${e.join(" ")}`);}};var Le=class{randomUUID(){return crypto.randomUUID()}},ve=new Le;var H=class{static options={connection:{}}};var Ee=class{queues=new Map;workers=new Map;async publish(e,t,r){let n=await this.getQueue(e),i=ve.randomUUID();return await n.add(e,t,{jobId:i,...H.options?.defaultJobOptions,...r}),{id:i}}async subscribe(e,t){if(this.workers.has(e))throw new Error(`[BullMQ] Already subscribed to topic "${e}"`);let{errorHandler:r,...n}=H.options??{},i=await this.getBullMQClient(),l=new i.Worker(e,async o=>{try{await t(o.data);}catch(u){await r?.(o,u)??Promise.reject(u);}},{...n});this.workers.set(e,l);}async unsubscribe(e){let t=this.workers.get(e);t&&(await t.close(),this.workers.delete(e));}async getQueue(e){if(!this.queues.has(e)){let t=await import('bullmq').catch(()=>{throw new _("bullmq","ioredis")}),r=new t.Queue(e,{...H.options||{connection:{}}});this.queues.set(e,r);}return this.queues.get(e)}async getBullMQClient(){return this.bullmqClient||(this.bullmqClient=await import('bullmq').catch(()=>{throw new _("bullmq","ioredis")})),this.bullmqClient}async publishWithConfig(e,t,r,n){let i=await this.getQueueWithConfig(e,n),l=ve.randomUUID(),o={jobId:l,...H.options?.defaultJobOptions,...n?.defaultJobOptions,...r};return await i.add(e,t,o),{id:l}}async subscribeWithConfig(e,t,r){let n=this.getWorkerKey(e,r);if(this.workers.has(n))throw new Error(`[BullMQ] Already subscribed to topic "${e}"`);let i=H.options??{},{errorHandler:l}=i,o=await this.getBullMQClient(),u={...i,...r};delete u.errorHandler,delete u.defaultJobOptions;let d=new o.Worker(e,async s=>{try{await t(s.data);}catch(a){await l?.(s,a)??Promise.reject(a);}},u);this.workers.set(n,d);}async getQueueWithConfig(e,t){let r=this.getQueueKey(e,t);if(!this.queues.has(r)){let n=await import('bullmq').catch(()=>{throw new _("bullmq","ioredis")}),i={...H.options||{connection:{}},...t},l=new n.Queue(e,i);this.queues.set(r,l);}return this.queues.get(r)}getQueueKey(e,t){return t?.connection?`${e}:${JSON.stringify(t.connection)}`:e}getWorkerKey(e,t){return this.getQueueKey(e,t)}};var Oe=class{subscribers=new Map;messageQueue=[];maxQueueSize;processingPromise=null;constructor(e=1e4){this.maxQueueSize=e;}clear(){this.subscribers.clear(),this.messageQueue=[],this.processingPromise=null;}async publish(e,t,r){if(this.messageQueue.length>=this.maxQueueSize)throw new Error(`Queue full: ${this.maxQueueSize} messages pending`);let n=ve.randomUUID();return this.messageQueue.push({topic:e,payload:t}),this.processingPromise||(this.processingPromise=this.processQueue().catch(i=>{console.error("[MemoryPubSub] Fatal queue processing error:",i);}).finally(()=>{this.processingPromise=null;})),{id:n}}async subscribe(e,t){this.subscribers.has(e)||this.subscribers.set(e,new Set);let r=t;this.subscribers.get(e).add(r);}async unsubscribe(e){this.subscribers.delete(e);}async processQueue(){for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();if(!e)break;let t=this.subscribers.get(e.topic);t&&t.size>0&&await Promise.allSettled(Array.from(t).map(r=>r(e.payload).catch(n=>{console.error(`[MemoryPubSub] Handler error for topic "${e.topic}":`,n);})));}}};var U=class{static options={}};var $e=class{createdQueues=new Set;workers=new Map;async publish(e,t,r){let n=await this.getBoss();await this.ensureQueue(e);let i=r||{},l=await n.send(e,t,i);return {id:String(l??"")}}async subscribe(e,t){let r=await this.getBoss();await this.ensureQueue(e);let n=U.options;n.errorHandler&&r.on("error",n.errorHandler);let i=await r.work(e,async l=>{let o=Array.isArray(l)?l:[l];for(let u of o)await t(u.data);});this.workers.set(e,i);}async unsubscribe(e){let t=await this.getBoss(),r=this.workers.get(e);r&&(await t.offWork(r),this.workers.delete(e));}async getBoss(){if(this.boss)return this.boss;let e=await import('pg-boss').catch(()=>{throw new _("pg-boss","pg")}),r=e.PgBoss.default??e.PgBoss,{connectionString:n,boss:i}=U.options,l=n??i,o=new r(l);return U.options?.errorHandler&&o.on("error",U.options.errorHandler),await o.start(),this.boss=o,this.boss}async ensureQueue(e){if(this.createdQueues.has(e))return;let t=await this.getBoss();typeof t.createQueue=="function"&&await t.createQueue(e),this.createdQueues.add(e);}async publishWithConfig(e,t,r,n){let i=await this.getBossWithConfig(n);await this.ensureQueueWithBoss(e,i);let l=r||{},o=await i.send(e,t,l);return {id:String(o??"")}}async subscribeWithConfig(e,t,r){let n=await this.getBossWithConfig(r);await this.ensureQueueWithBoss(e,n);let i=U.options;i.errorHandler&&n.on("error",i.errorHandler);let l=`${e}:${r?.connectionString??"default"}`,o=await n.work(e,async u=>{let d=Array.isArray(u)?u:[u];for(let s of d)await t(s.data);});this.workers.set(l,o);}bossInstances=new Map;async getBossWithConfig(e){if(!e?.connectionString)return this.getBoss();let t=e.connectionString;if(this.bossInstances.has(t))return this.bossInstances.get(t);let r=await import('pg-boss').catch(()=>{throw new _("pg-boss","pg")}),i=r.PgBoss.default??r.PgBoss,l=new i(e.connectionString);return U.options?.errorHandler&&l.on("error",U.options.errorHandler),await l.start(),this.bossInstances.set(t,l),l}async ensureQueueWithBoss(e,t){this.createdQueues.has(e)||(typeof t.createQueue=="function"&&await t.createQueue(e),this.createdQueues.add(e));}};var ie=class{static options={}};var Me=class{consumers=new Map;client;async publish(e,t,r){let n=await this.getClient(),{SendMessageCommand:i}=await this.getSqsLib(),l=await this.resolveQueueUrl(e);if(!l)throw new Error(`[SQS] Queue url not configured for topic "${e}"`);let o=new i({MessageBody:JSON.stringify(t),MessageAttributes:{topic:{DataType:"String",StringValue:e}},QueueUrl:l,...r??{}});return {id:(await n.send(o)).MessageId??""}}async subscribe(e,t){if(this.consumers.has(e))throw new Error(`[SQS] Already subscribed to topic "${e}"`);let r=ie.options,n=await this.getSqsConsumerLib(),i={...r.consumer||{},sqs:await this.getClient(),queueUrl:await this.resolveQueueUrl(e),handleMessage:async u=>{let d=JSON.parse(u.Body||"{}");await t(d);}},l=n.Consumer.create(i),o=r.errorHandler;o&&(l.on("error",o),l.on("processing_error",o)),l.start(),this.consumers.set(e,l);}async unsubscribe(e){let t=this.consumers.get(e);t&&(t.stop(),this.consumers.delete(e));}async getClient(){if(this.client)return this.client;let{SQSClient:e}=await this.getSqsLib(),t=ie.options.client;return this.client=new e(t??{}),this.client}async getSqsLib(){return this.sqsLib||(this.sqsLib=await import('@aws-sdk/client-sqs').catch(()=>{throw new _("@aws-sdk/client-sqs","sqs-consumer")})),this.sqsLib}async getSqsConsumerLib(){return this.sqsConsumerLib||(this.sqsConsumerLib=await import('sqs-consumer').catch(()=>{throw new _("sqs-consumer")})),this.sqsConsumerLib}async resolveQueueUrl(e){let r=ie.options.consumer?.queueUrlMap;if(!r?.[e])throw new Error(`[SQS] Queue url not configured for topic "${e}"`);return r[e]}async publishWithConfig(e,t,r,n){let{...i}=r??{},l=await this.getClientWithConfig(n?.client),{SendMessageCommand:o}=await this.getSqsLib(),u=n?.queueUrl??await this.resolveQueueUrl(e),d=new o({...i,MessageBody:JSON.stringify(t),MessageAttributes:{topic:{DataType:"String",StringValue:e}},QueueUrl:u});return {id:(await l.send(d)).MessageId??""}}async subscribeWithConfig(e,t,r){if(this.consumers.has(e))throw new Error(`[SQS] Already subscribed to topic "${e}"`);let n=ie.options,i=await this.getSqsConsumerLib(),l=r?.queueUrl??await this.resolveQueueUrl(e),o={...n.consumer||{},sqs:await this.getClientWithConfig(r?.client),queueUrl:l,handleMessage:async s=>{let a=JSON.parse(s.Body||"{}");await t(a);}},u=i.Consumer.create(o),d=n.errorHandler;d&&(u.on("error",d),u.on("processing_error",d)),u.start(),this.consumers.set(e,u);}async getClientWithConfig(e){if(e){let{SQSClient:t}=await this.getSqsLib();return new t(e)}return this.getClient()}};var Ae=class{static map=new Map;static{this.map.set("bullmq",new Ee),this.map.set("sqs",new Me),this.map.set("pgboss",new $e),this.map.set("memory",new Oe);}static getProvider(e){if(!this.map.has(e))throw new Error(`[QueueSubscriber] Provider ${e} not found`);return this.map.get(e)}static setProvider(e,t){this.map.set(e,t);}static clearMemoryProvider(){let e=this.map.get("memory");e&&"clear"in e&&e.clear();}};var ae=class{static typedQueueSubscribers=new Map;static customQueueSubscribers=new Map;static logger=B.child({scope:"QueueService"});static instanceFactory=e=>new e;static registerTypedQueue(e,t,r,n,i){let l=`${n}:${t}:${e}`;this.typedQueueSubscribers.has(l)&&this.logger.warn(`Queue handler for ${l} already registered, overwriting previous handler`),this.typedQueueSubscribers.set(l,{name:e,topic:t,handler:r,provider:n,queueOptions:i});}static registerCustomQueue(e,t,r,n){let i=`${n.constructor.name}:${t}:${e}`;this.customQueueSubscribers.has(i)&&this.logger.warn(`Custom queue handler for ${i} already registered, overwriting previous handler`),this.customQueueSubscribers.set(i,{name:e,topic:t,handler:r,pubsub:n});}static async run(){this.logger.info("Subscribing queue handlers");let e=this.typedQueueSubscribers.size>0,t=this.customQueueSubscribers.size>0;if(!e&&!t){this.logger.info("No queue handlers to subscribe");return}for(let r of this.typedQueueSubscribers.values()){let{topic:n,handler:i,provider:l,queueOptions:o}=r;this.logger.info(`Subscribing to queue: ${n}`);let u=Ae.getProvider(l);o&&"subscribeWithConfig"in u&&typeof u.subscribeWithConfig=="function"?await u.subscribeWithConfig(n,i,o):await u.subscribe(n,i);}for(let{topic:r,handler:n,pubsub:i}of this.customQueueSubscribers.values())this.logger.info(`Subscribing to custom queue: ${r}`),await i.subscribe(r,n);this.logger.info("Queue handlers subscribed");}static async massiveImportQueues(e,t={}){let r=[];for(let n of e){let i=await glob(n,{absolute:true,cwd:D.getCwd()});this.logger.info(`Pattern "${n}" matched ${i.length} file(s)`),r.push(...i);}if(r.length===0){this.logger.warn("No files matched the provided patterns");return}this.logger.info(`Importing ${r.length} queue handler file(s)`),await Promise.all(r.map(async n=>{this.logger.debug(`Importing: ${n}`),await import(n).catch(i=>{if(this.logger.error(`Error importing queue handler: ${n}`),this.logger.error(i),t.throwOnError)throw i});})),this.logger.info(`Successfully imported ${r.length} file(s)`);}};var le=class extends q{static commandName="queue-start";static description="Start queue workers to process jobs";static help=["Start queue workers to process jobs from registered queues","Loads queue handlers from specified patterns and starts processing","Example: npx balda queue-start","Example: npx balda queue-start src/queues/**/*.ts --patterns src/jobs/**/*.ts"];static options={keepAlive:true};static pattern;static additionalPatterns;static async handle(){this.logger.info("Starting queue workers...");let e=[this.pattern];this.additionalPatterns&&this.additionalPatterns.length>0&&e.push(...this.additionalPatterns),this.logger.info(`Loading queue handlers from patterns: ${e.join(", ")}`),await ae.massiveImportQueues(e,{throwOnError:false});let t=ae.typedQueueSubscribers.size,r=ae.customQueueSubscribers.size,n=t+r;if(n===0){this.logger.warn("No queue handlers found. Make sure your queue handlers are decorated with @queue decorator");return}this.logger.info(`Found ${n} queue handler(s) (${t} typed, ${r} custom)`),ae.run().then(()=>{this.logger.info("Queue workers started successfully. Press Ctrl+C to stop.");}).catch(i=>{this.logger.error("Error starting queue workers",i);});}};b([E({required:false,defaultValue:"src/queues/**/*.{ts,js}",description:"Primary glob pattern for queue handlers (default: src/queues/**/*.{ts,js})"})],le,"pattern"),b([P.list({aliases:["p"],name:"patterns",required:false,description:"Additional glob patterns for queue handlers"})],le,"additionalPatterns");var ce=class extends q{static commandName="serve";static description="Run the server in dev mode with hot reload";static help=["This command is intended to be run from the root of the project","Bun and Deno have native dev hot reload","Runtime is automatically inferred","Node.js requires tsx to be installed for both typescript and javascript files","Node.js dev dependencies (tsx) are installed automatically if not detected"];static runtime=R.type;static options={keepAlive:true};static entry;static denoImportMap;static async handle(){if(this.runtime==="bun"){execSync(`bun run --watch ${this.entry}`,{stdio:"inherit",cwd:D.getCwd()});return}if(this.runtime==="deno"){let e="deno run --watch --unstable-sloppy-imports --allow-all";this.denoImportMap&&(e=`${e} --import-map ${this.denoImportMap}`),execSync(`${e} ${this.entry}`,{stdio:"inherit",cwd:D.getCwd()});return}this.handleNodeHotReload();}static async handleNodeHotReload(){if(!ye.existsSync("node_modules/.bin/tsx")){let[r,n,i]=await k();if(!await $(`${r} ${n} tsx ${i}`,r,["tsx"],{stdio:"inherit"})){this.logger.info("Installation cancelled by user. Cannot start dev server without tsx.");return}}let t=this.calledBy!=="node"?this.calledBy:"npx";execSync(`${t} tsx watch ${this.entry}`,{stdio:"inherit",cwd:process.cwd()});}};b([E({required:false,defaultValue:"src/index.ts",description:"The entry point of the project, default is src/index.ts"})],ce,"entry"),b([P.string({aliases:["d"],name:"deno-import-map",required:false,description:"Path to deno import map"})],ce,"denoImportMap");var ue=class extends q{static commandName="init-storage";static description="Setup storage provider with required dependencies";static help=["Install dependencies and create storage configuration for a specific provider","Flags:"," -t, --type <provider> Storage provider type (s3, azure, local)"," -o, --output <path> Output directory for storage setup (default: src/storage/)","","Examples:"," npx balda setup:storage -t s3"," npx balda setup:storage --type azure --output src/config/"];static storageType;static outputPath;static async handle(){if(!this.storageType){console.error("\x1B[31m\u274C Error: Storage type is required. Use -t or --type flag.\x1B[0m"),console.log(`\x1B[90mExample: npx balda setup:storage -t s3\x1B[0m
500
500
  `);return}this.outputPath||await y.mkdir(x.join(process.cwd(),this.outputPath),{recursive:true});let e=["s3","azure","local"];if(!e.includes(this.storageType)){console.error(`\x1B[31m\u274C Error: Invalid storage type '${this.storageType}'. Valid types: ${e.join(", ")}\x1B[0m
501
501
  `);return}console.log(`
502
502
  \u{1F680} Setting up ${this.storageType.toUpperCase()} storage provider...
503
503
  `);let t=this.getDependencies(this.storageType);if(t.length===0&&console.log(`\x1B[32m\u2705 ${this.storageType.toUpperCase()} storage doesn't require additional dependencies.\x1B[0m
504
504
  `),t.length){let r=await this.checkMissingDependencies(t);if(r.length===0&&console.log(`\x1B[32m\u2705 All required dependencies are already installed.\x1B[0m
505
- `),r.length){let[n,i]=await j(),l=`${n} ${i} ${r.join(" ")}`;if(!await M(l,n,r,{stdio:"inherit"},false)){console.log(`\x1B[33m\u26A0\uFE0F Dependency installation skipped by user.\x1B[0m
505
+ `),r.length){let[n,i]=await k(),l=`${n} ${i} ${r.join(" ")}`;if(!await $(l,n,r,{stdio:"inherit"},false)){console.log(`\x1B[33m\u26A0\uFE0F Dependency installation skipped by user.\x1B[0m
506
506
  `);return}console.log(`
507
507
  \x1B[32m\u2705 Dependencies installed successfully!\x1B[0m
508
508
  `);}}await this.createStorageSetup(this.storageType,this.outputPath),console.log(`\x1B[32m\u2728 ${this.storageType.toUpperCase()} storage setup complete!\x1B[0m
@@ -586,11 +586,11 @@ export const storage = new Storage(
586
586
  defaultProvider: "local",
587
587
  },
588
588
  );
589
- `}[e]||""}};b([P({description:"Storage provider type (s3, azure, local)",type:"string",aliases:["t"],name:"type",required:true})],ue,"storageType"),b([P({description:"Output directory for storage setup",type:"string",aliases:["o"],name:"output",required:false,defaultValue:"src/storage/"})],ue,"outputPath");var St=[se,he,ee,re,V,Z,te,A,U,oe,ne,be,I,ce,ue,fe,le,Y],W=class c{commands;builtInCommands;static commandsPattern="src/commands/**/*.{ts,js}";static logger=R;constructor(){this.commands=new Map,this.builtInCommands=new Set;}static getInstance(){return new c}static setCommandsPattern(e){this.commandsPattern=e;}getCommand(e){return this.commands.get(e)??null}getCommands(){return Array.from(this.commands.values())}getBuiltInCommands(){return Array.from(this.commands.values()).filter(e=>this.builtInCommands.has(e.commandName))}getUserDefinedCommands(){return Array.from(this.commands.values()).filter(e=>!this.builtInCommands.has(e.commandName))}isBuiltInCommand(e){return this.builtInCommands.has(e)}async loadCommands(e){c.logger.info(`Loading commands from ${e}`);let t=await glob(e,{absolute:true,cwd:Q.getCwd()});if(t.some(r=>r.endsWith(".ts")))try{let{register:r}=await import('module');r("ts-node/esm",import.meta.url);}catch{c.logger.error("Failed to register ts-node/esm, you need to install it in your project in order to use typescript in the cli\ntry running: `npm install -D ts-node`"),process.exit(1);}for(let r of t){let n=await import(r).then(i=>i.default?i.default:i).catch(i=>(c.logger.error(`Error loading command ${r}: ${i}`),null));n&&this.commands.set(n.commandName,n);}for(let r of St)this.commands.set(r.commandName,r),this.builtInCommands.add(r.commandName);}},_=W.getInstance();var Pt=c=>{let e=new Map;for(let t of c){let r=t.options?.category||"other";e.has(r)||e.set(r,[]),e.get(r).push(t);}return e},Tt=c=>{let e=Array.from(c.keys()).sort();for(let t of e){let r=c.get(t).filter(o=>o&&o.commandName);if(!r.length)continue;let i={generator:"\x1B[35m",setup:"\x1B[34m",production:"\x1B[32m",utility:"\x1B[36m",other:"\x1B[37m"}[t]||"\x1B[37m";console.log(` ${i}${t.toUpperCase()}:\x1B[0m`);let l=Math.max(...r.map(o=>o.commandName.length));for(let o of r){let u=o.commandName.padEnd(l+2),d=o.description||"No description available",s="";o.options?.deprecated&&(s=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${u}\x1B[0m ${d}${s}`);}console.log("");}},He=async()=>{await _.loadCommands(W.commandsPattern);let[c,...e]=me.getCliArgs();if(!c||c==="-h"||c==="--help"){let i=_.getBuiltInCommands(),l=_.getUserDefinedCommands();if(console.log(`
589
+ `}[e]||""}};b([P({description:"Storage provider type (s3, azure, local)",type:"string",aliases:["t"],name:"type",required:true})],ue,"storageType"),b([P({description:"Output directory for storage setup",type:"string",aliases:["o"],name:"output",required:false,defaultValue:"src/storage/"})],ue,"outputPath");var St=[se,he,ee,re,V,Z,te,M,F,oe,ne,be,N,ce,ue,fe,le,Y],W=class c{commands;builtInCommands;static commandsPattern="src/commands/**/*.{ts,js}";static logger=B.child({scope:"CommandRegistry"});constructor(){this.commands=new Map,this.builtInCommands=new Set;}static getInstance(){return new c}static setCommandsPattern(e){this.commandsPattern=e;}getCommand(e){return this.commands.get(e)??null}getCommands(){return Array.from(this.commands.values())}getBuiltInCommands(){return Array.from(this.commands.values()).filter(e=>this.builtInCommands.has(e.commandName))}getUserDefinedCommands(){return Array.from(this.commands.values()).filter(e=>!this.builtInCommands.has(e.commandName))}isBuiltInCommand(e){return this.builtInCommands.has(e)}async loadCommands(e){c.logger.info(`Loading commands from ${e}`);let t=await glob(e,{absolute:true,cwd:D.getCwd()});if(t.some(r=>r.endsWith(".ts")))try{let{register:r}=await import('module');r("ts-node/esm",import.meta.url);}catch{c.logger.error("Failed to register ts-node/esm, you need to install it in your project in order to use typescript in the cli\ntry running: `npm install -D ts-node`"),process.exit(1);}for(let r of t){let n=await import(r).then(i=>i.default?i.default:i).catch(i=>(c.logger.error(`Error loading command ${r}: ${i}`),null));n&&this.commands.set(n.commandName,n);}for(let r of St)this.commands.set(r.commandName,r),this.builtInCommands.add(r.commandName);}},I=W.getInstance();var Pt=c=>{let e=new Map;for(let t of c){let r=t.options?.category||"other";e.has(r)||e.set(r,[]),e.get(r).push(t);}return e},Tt=c=>{let e=Array.from(c.keys()).sort();for(let t of e){let r=c.get(t).filter(o=>o&&o.commandName);if(!r.length)continue;let i={generator:"\x1B[35m",setup:"\x1B[34m",production:"\x1B[32m",utility:"\x1B[36m",other:"\x1B[37m"}[t]||"\x1B[37m";console.log(` ${i}${t.toUpperCase()}:\x1B[0m`);let l=Math.max(...r.map(o=>o.commandName.length));for(let o of r){let u=o.commandName.padEnd(l+2),d=o.description||"No description available",s="";o.options?.deprecated&&(s=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${u}\x1B[0m ${d}${s}`);}console.log("");}},He=async()=>{await I.loadCommands(W.commandsPattern);let[c,...e]=me.getCliArgs();if(!c||c==="-h"||c==="--help"){let i=I.getBuiltInCommands(),l=I.getUserDefinedCommands();if(console.log(`
590
590
  \u2728 Available Balda Commands:
591
591
  `),l.length&&(console.log(`\x1B[1;33mUser Commands:\x1B[0m
592
592
  `),Tt(Pt(l))),i.length){console.log(`\x1B[1;32mBuilt-in Commands:\x1B[0m
593
593
  `);let o=Math.max(...i.map(u=>u.commandName.length));for(let u of i){let d=u.commandName.padEnd(o+2),s=u.description||"No description available",a="";u.options?.deprecated&&(a=" \x1B[33m[deprecated]\x1B[0m"),console.log(` \x1B[36m${d}\x1B[0m ${s}${a}`);}console.log("");}console.log(`\x1B[90mRun 'npx balda <command> -h' for more information on a specific command.\x1B[0m
594
- `),L.exit(0);return}let t=_.getCommand(c);if(!t){console.error(Ke(c,_.getCommands().filter(i=>i&&i.commandName).map(i=>i.commandName))||`Command ${c} not found`),L.exit(1);return}let r=t;if(r.calledBy=x.basename(me.getCliCaller()),r.options?.deprecated){let i=r.options.deprecated.message||"This command is deprecated",l=r.options.deprecated.replacement;console.warn(`\x1B[33m\u26A0\uFE0F Warning: ${i}\x1B[0m`),l&&console.warn(`\x1B[33m Use '${l}' instead.\x1B[0m
594
+ `),L.exit(0);return}let t=I.getCommand(c);if(!t){console.error(Ke(c,I.getCommands().filter(i=>i&&i.commandName).map(i=>i.commandName))||`Command ${c} not found`),L.exit(1);return}let r=t;if(r.calledBy=x.basename(me.getCliCaller()),r.options?.deprecated){let i=r.options.deprecated.message||"This command is deprecated",l=r.options.deprecated.replacement;console.warn(`\x1B[33m\u26A0\uFE0F Warning: ${i}\x1B[0m`),l&&console.warn(`\x1B[33m Use '${l}' instead.\x1B[0m
595
595
  `);}if(r.handleHelpFlag(r.flags),r.validateContext(r),r.options?.validate&&!await r.options.validate(r)){console.error("Command validation failed"),L.exit(1);return}await r.handle(),(t.options?.keepAlive??false)||L.exit(0);};typeof process<"u"?He().catch(async c=>{if(c?.message?.includes("SyntaxError")||c?.code==="ERR_UNKNOWN_FILE_EXTENSION")try{let{register:e}=await import('module');e("ts-node/esm",import.meta.url),He().catch(t=>{W.logger.error(t),process.exit(1);});}catch{W.logger.error("Failed to register ts-node/esm, you need to install it in your project in order to use typescript in the cli\ntry running: `npm install -D ts-node`"),process.exit(1);}else W.logger.error(c),process.exit(1);}):He().catch(c=>{W.logger.error(c),L.exit(1);});export{He as cli};//# sourceMappingURL=cli.js.map
596
596
  //# sourceMappingURL=cli.js.map