create-gramstax 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var W=Object.create;var h=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,O=Object.prototype.hasOwnProperty;var p=(o,e)=>h(o,"name",{value:e,configurable:!0});var V=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of G(e))!O.call(o,a)&&a!==t&&h(o,a,{get:()=>e[a],enumerable:!(n=A(e,a))||n.enumerable});return o};var v=(o,e,t)=>(t=o!=null?W(B(o)):{},V(e||!o||!o.__esModule?h(t,"default",{value:o,enumerable:!0}):t,o));var q=p(()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,"getImportMetaUrl"),f=q();var C=require("logging-pretty"),r=new C.LoggingPretty;var i=v(require("fs"),1),s=v(require("path"),1),E=require("url");var D=require("fs");var g=class o{static{p(this,"FsUt")}static DEFAULT_IGNORED_ENTRIES=new Set([".git",".gitignore",".gitkeep",".DS_Store","Thumbs.db"]);static isDirectoryEffectivelyEmpty(e,t=o.DEFAULT_IGNORED_ENTRIES){return(0,D.readdirSync)(e).filter(c=>!t.has(c)).length===0}static resolvePackageManagerCommand(e,t=process.platform){return t!=="win32"||e==="bun"?e:`${e}.cmd`}static resolveInitialWorkingDirectory(e=process.cwd(),t=process.env.INIT_CWD){return t??e}};var j=require("child_process"),P=v(require("enquirer"),1);var J=(0,E.fileURLToPath)(f),Y=s.dirname(J),_=class{constructor(e,t){this.projectDirectory=e;this.version=t?.version}projectDirectory;static{p(this,"Create")}config;version;printBanner(){let e=`
2
+ "use strict";var B=Object.create;var v=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var $=(s,e)=>v(s,"name",{value:e,configurable:!0});var K=(s,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of Y(e))!q.call(s,a)&&a!==n&&v(s,a,{get:()=>e[a],enumerable:!(t=G(e,a))||t.enumerable});return s};var w=(s,e,n)=>(n=s!=null?B(V(s)):{},K(e||!s||!s.__esModule?v(n,"default",{value:s,enumerable:!0}):n,s));var z=$(()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,"getImportMetaUrl"),f=z();var T=require("logging-pretty"),i=new T.LoggingPretty;var r=w(require("fs"),1),o=w(require("path"),1),R=require("url");var M=require("fs");var p=class s{static{$(this,"FsUt")}static DEFAULT_IGNORED_ENTRIES=new Set([".git",".gitignore",".gitkeep",".DS_Store","Thumbs.db"]);static isDirectoryEffectivelyEmpty(e,n=s.DEFAULT_IGNORED_ENTRIES){return(0,M.readdirSync)(e).filter(c=>!n.has(c)).length===0}static resolvePackageManagerCommand(e,n=process.platform){return n!=="win32"||e==="bun"?e:`${e}.cmd`}static resolveInitialWorkingDirectory(e=process.cwd(),n=process.env.INIT_CWD){return n??e}};var j=require("child_process"),P=w(require("enquirer"),1);var H=(0,R.fileURLToPath)(f),J=o.dirname(H),k=class{constructor(e,n){this.projectDirectory=e;this.version=n?.version,this.cliOptions=n?.cliOptions}projectDirectory;static{$(this,"Create")}config;version;cliOptions;printBanner(){let e=`
3
3
  $$$$$$\\ $$\\
4
4
  $$ __$$\\ $$ |
5
5
  $$ / \\__| $$$$$$\\ $$$$$$\\ $$$$$$\\$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\ $$\\ $$\\
@@ -8,6 +8,7 @@ $$ |\\_$$ |$$ | \\__|$$$$$$$ |$$ / $$ / $$ |\\$$$$$$\\ $$ | $$$$$$$ | \\
8
8
  $$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | \\____$$\\ $$ |$$\\ $$ __$$ | $$ $$<
9
9
  \\$$$$$$ |$$ | \\$$$$$$$ |$$ | $$ | $$ |$$$$$$$ | \\$$$$ |\\$$$$$$$ |$$ /\\$$\\
10
10
  \\______/ \\__| \\_______|\\__| \\__| \\__|\\_______/ \\____/ \\_______|\\__/ \\__|
11
-
12
- `,t=this.version?`v${this.version}`:"v0.0.0";console.log(`${e}${t}
13
- `)}getPackageManagerCommands(e){return{bun:{install:"bun install",dev:"bun dev",start:"bun start"},npm:{install:"npm install",dev:"npm run dev",start:"npm start"},pnpm:{install:"pnpm install",dev:"pnpm dev",start:"pnpm start"},yarn:{install:"yarn install",dev:"yarn dev",start:"yarn start"}}[e]}copyDirectorySync(e,t,n={}){i.existsSync(t)||i.mkdirSync(t,{recursive:!0});let a=i.readdirSync(e,{withFileTypes:!0});for(let c of a){let m=s.join(e,c.name),l=s.join(t,c.name);if(c.isDirectory())this.copyDirectorySync(m,l,n);else if(c.isFile()){let $=i.readFileSync(m,"utf-8");$=this.replaceTemplateVariables($,n),i.writeFileSync(l,$)}}}replaceTemplateVariables(e,t){let n=e;for(let[a,c]of Object.entries(t))n=n.replace(new RegExp(`{{${a}}}`,"g"),c);return n}async promptForProjectName(){return(await P.default.prompt({type:"input",name:"projectName",message:"What is your project named?",initial:"",validate:p(t=>t===""||/^[a-z0-9-_]+$/i.test(t)?!0:"Project name can only contain letters, numbers, hyphens, and underscores","validate")})).projectName}async promptForConfiguration(){let e=[];e.push({type:"select",name:"packageManager",message:"Which package manager would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"npm",message:"npm",value:"npm"},{name:"pnpm",message:"pnpm",value:"pnpm"},{name:"yarn",message:"Yarn",value:"yarn"}],initial:0}),e.push({type:"select",name:"runtime",message:"Which runtime would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"node",message:"Node.js",value:"node"}],initial:0}),e.push({type:"confirm",name:"eslint",message:"Would you like to use ESLint?",initial:!0});let t=await P.default.prompt(e);return{packageManager:t.packageManager,runtime:t.runtime,eslint:t.eslint}}async installDependencies(e,t){return await new Promise(n=>{let a=g.resolvePackageManagerCommand(t),m=(0,j.spawn)(a,["install"],{cwd:e,stdio:"inherit",shell:!1});m.on("close",l=>{l===0?n(!0):(r.fail("Dependencies installation failed"),n(!1))}),m.on("error",l=>{r.fail(`Dependencies installation failed: ${l.message}`),n(!1)})})}async gatherProjectConfig(){this.printBanner();let e=this.projectDirectory,t,n=!1,a=g.resolveInitialWorkingDirectory();e?(t=s.resolve(a,e),n=!1):(e=await this.promptForProjectName(),e===""?(e=s.basename(a),t=a,n=!0):(t=s.resolve(a,e),n=!1));let c=await this.promptForConfiguration(),m=c.packageManager,l=c.runtime,$=c.eslint;if(r.info(`Project: ${e}`),r.info(`Location: ${t}`),r.info(`Package Manager: ${m}`),r.info(`Runtime: ${l}`),r.info(`ESLint: ${$?"Yes":"No"}`),n){if(!g.isDirectoryEffectivelyEmpty(t))throw r.error("Current directory is not empty!"),new Error("Current directory is not empty")}else if(i.existsSync(t))throw r.error(`Directory "${e}" already exists!`),new Error(`Directory "${e}" already exists`);return{projectName:e,projectPath:t,packageManager:m,runtime:l,setupEslint:$}}async executeProjectGeneration(e){let{projectName:t,projectPath:n,packageManager:a,runtime:c,setupEslint:m}=e;r.info("Creating project directory.."),i.existsSync(n)||i.mkdirSync(n,{recursive:!0}),r.info("Copying template files..");let l=s.join(Y,"templates"),$=this.getPackageManagerCommands(a),I={projectName:t,installCmd:$.install,runDevCmd:$.dev,runStartCmd:$.start};this.copyDirectorySync(l,n,I),r.info("Creating logs directory.."),i.mkdirSync(s.join(n,"logs"),{recursive:!0}),i.writeFileSync(s.join(n,"logs/.gitkeep"),"");let L=s.join(n,".env"),S=s.join(n,".env.example");if(i.existsSync(S)&&(r.info("Creating .env file.."),i.renameSync(S,L)),c!=="bun"){r.info("Removing bunfig.toml for Node.js runtime..");let d=s.join(n,"bunfig.toml");i.existsSync(d)&&i.unlinkSync(d)}if(r.info("Installing dependencies.."),!await this.installDependencies(n,a))throw new Error("Project dependencies installation failed");let u=s.join(n,"eslint.config.ts.template"),T=s.join(n,"eslint.config.ts");if(m){r.info("Installing ESLint dependencies..");let d=g.resolvePackageManagerCommand(a),w=a==="yarn"?["add","-D"]:["install","-D"];w.push("eslint","@eslint/js","globals","typescript-eslint"),await new Promise(b=>{let k=(0,j.spawn)(d,w,{cwd:n,stdio:"inherit",shell:!1});k.on("close",()=>b()),k.on("error",U=>{r.fail(`Failed to install ESLint dependencies: ${U.message}`),b()})}),i.existsSync(u)&&i.renameSync(u,T)}else i.existsSync(u)&&i.unlinkSync(u);r.info("Project created successfully!"),r.info(`Next steps: ${e.projectPath!==process.cwd()?`cd ${t} && `:" "}${$.install} && ${$.dev}`),r.info("Done..")}async createProject(){try{this.config=await this.gatherProjectConfig(),await this.executeProjectGeneration(this.config)}catch(e){e instanceof Error?r.error(`Failed to create project: ${e.message}`):r.error(`Failed to create project: ${String(e)}`),process.exit(1)}}};var N=require("commander"),R=require("fs"),y=require("path"),M=require("url"),z=(0,M.fileURLToPath)(f),H=(0,y.dirname)(z),K=(0,y.join)(H,"../../package.json"),x=JSON.parse((0,R.readFileSync)(K,"utf-8")).version,F=new N.Command;F.name("create-gramstax").description("CLI to create a new Gramstax Telegram bot project").version(x).argument("[project-directory]","Directory to create the project in").action(async o=>{try{await new _(o,{version:x}).createProject()}catch(e){r.error(`Failed to create project: ${String(e)}`),process.exit(1)}});F.parse();
11
+
12
+ `,n=this.version?`v${this.version}`:"v0.0.0";console.log(`${e}${n}
13
+ `)}getPackageManagerCommands(e){return{bun:{install:"bun install",dev:"bun dev",start:"bun start"},npm:{install:"npm install",dev:"npm run dev",start:"npm start"},pnpm:{install:"pnpm install",dev:"pnpm dev",start:"pnpm start"},yarn:{install:"yarn install",dev:"yarn dev",start:"yarn start"}}[e]}copyDirectorySync(e,n,t={}){r.existsSync(n)||r.mkdirSync(n,{recursive:!0});let a=r.readdirSync(e,{withFileTypes:!0});for(let c of a){let h=o.join(e,c.name),d=o.join(n,c.name);if(c.isDirectory())this.copyDirectorySync(h,d,t);else if(c.isFile()){let l=r.readFileSync(h,"utf-8");l=this.replaceTemplateVariables(l,t),r.writeFileSync(d,l)}}}replaceTemplateVariables(e,n){let t=e;for(let[a,c]of Object.entries(n))t=t.replace(new RegExp(`{{${a}}}`,"g"),c);return t}async promptForProjectName(){return(await P.default.prompt({type:"input",name:"projectName",message:"What is your project named?",initial:"",validate:$(n=>n===""||/^[a-z0-9-_]+$/i.test(n)?!0:"Project name can only contain letters, numbers, hyphens, and underscores","validate")})).projectName}async promptForConfiguration(){let e=await P.default.prompt([{type:"select",name:"packageManager",message:"Which package manager would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"npm",message:"npm",value:"npm"},{name:"pnpm",message:"pnpm",value:"pnpm"},{name:"yarn",message:"Yarn",value:"yarn"}],initial:0},{type:"select",name:"runtime",message:"Which runtime would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"node",message:"Node.js",value:"node"}],initial:0},{type:"confirm",name:"lmdb",message:"Would you like to include @gramstax/lmdb for persistent storage? (recommended)",initial:!0},{type:"confirm",name:"workerThread",message:"Would you like to include @gramstax/worker-thread for background jobs? (recommended)",initial:!0},{type:"confirm",name:"eslint",message:"Would you like to use ESLint?",initial:!0}]);return{packageManager:e.packageManager,runtime:e.runtime,lmdb:e.lmdb,workerThread:e.workerThread,eslint:e.eslint}}async installDependencies(e,n){return await new Promise(t=>{let a=(0,j.spawn)(p.resolvePackageManagerCommand(n),["install"],{cwd:e,stdio:"inherit",shell:!1});a.on("close",c=>c===0?t(!0):(i.fail("Dependencies installation failed"),t(!1))),a.on("error",c=>(i.fail(`Dependencies installation failed: ${c.message}`),t(!1)))})}async gatherProjectConfig(){this.printBanner();let e=this.cliOptions,n=p.resolveInitialWorkingDirectory(),t=this.projectDirectory??"",a,c=!1;if(t)a=o.resolve(n,t);else{if(e&&this._hasAnyCliFlag())throw i.error("Project name is required when using CLI flags"),new Error("Missing project name");t=await this.promptForProjectName(),t===""?(t=o.basename(n),a=n,c=!0):a=o.resolve(n,t)}let d=e&&this._hasAnyCliFlag()?{packageManager:e.packageManager??"bun",runtime:e.runtime??"bun",lmdb:e.lmdb??!0,workerThread:e.workerThread??!0,eslint:e.eslint??!0}:await this.promptForConfiguration(),{packageManager:l,runtime:m,lmdb:_,workerThread:u,eslint:y}=d;if(i.info(`Project: ${t}`),i.info(`Location: ${a}`),i.info(`Package Manager: ${l}`),i.info(`Runtime: ${m}`),i.info(`LMDB Storage: ${_?"Yes":"No"}`),i.info(`Worker Thread: ${u?"Yes":"No"}`),i.info(`ESLint: ${y?"Yes":"No"}`),c){if(!p.isDirectoryEffectivelyEmpty(a))throw new Error("Current directory is not empty")}else if(r.existsSync(a))throw new Error(`Directory "${t}" already exists`);return{projectName:t,projectPath:a,packageManager:l,runtime:m,setupEslint:y,useLmdb:_,useWorkerThread:u}}_hasAnyCliFlag(){let e=this.cliOptions;return!!(e&&(e.packageManager!==void 0||e.runtime!==void 0||e.lmdb!==void 0||e.workerThread!==void 0||e.eslint!==void 0))}async executeProjectGeneration(e){let{projectName:n,projectPath:t,packageManager:a,runtime:c,setupEslint:h,useLmdb:d,useWorkerThread:l}=e,m=this.getPackageManagerCommands(a),_={projectName:n,installCmd:m.install,runDevCmd:m.dev,runStartCmd:m.start};i.info("Creating project directory.."),r.existsSync(t)||r.mkdirSync(t,{recursive:!0}),i.info("Copying template files.."),this.copyDirectorySync(o.join(J,"templates"),t,_),i.info("Creating logs directory.."),r.mkdirSync(o.join(t,"logs"),{recursive:!0}),r.writeFileSync(o.join(t,"logs/.gitkeep"),"");let u=o.join(t,".env"),y=o.join(t,".env.example");if(r.existsSync(y)&&(i.info("Creating .env file.."),r.renameSync(y,u)),c!=="bun"){let g=o.join(t,"bunfig.toml");r.existsSync(g)&&r.unlinkSync(g)}let S=o.join(t,"src","db");if(d){i.info("Keeping @gramstax/lmdb files..");let g=r.readFileSync(u,"utf-8");r.writeFileSync(u,g+`DB_PATH="./data"
14
+ `),r.mkdirSync(o.join(t,"data"),{recursive:!0}),r.writeFileSync(o.join(t,"data/.gitkeep"),"")}else i.info("Removing @gramstax/lmdb files.."),r.existsSync(S)&&r.rmSync(S,{recursive:!0,force:!0});let C=o.join(t,"src","workers");if(l?i.info("Keeping @gramstax/worker-thread files.."):(i.info("Removing @gramstax/worker-thread files.."),r.existsSync(C)&&r.rmSync(C,{recursive:!0,force:!0})),i.info("Installing dependencies.."),!await this.installDependencies(t,a))throw new Error("Dependencies installation failed");let b=o.join(t,"eslint.config.ts.template"),U=o.join(t,"eslint.config.ts");if(h){i.info("Installing ESLint dependencies..");let g=p.resolvePackageManagerCommand(a),x=a==="yarn"?["add","-D"]:["install","-D"];x.push("eslint","@eslint/js","globals","typescript-eslint"),await new Promise(E=>{let D=(0,j.spawn)(g,x,{cwd:t,stdio:"inherit",shell:!1});D.on("close",()=>E()),D.on("error",A=>{i.fail(`Failed to install ESLint: ${A.message}`),E()})}),r.existsSync(b)&&r.renameSync(b,U)}else r.existsSync(b)&&r.unlinkSync(b);i.info("Project created successfully!"),i.info(`Next steps: ${e.projectPath!==process.cwd()?`cd ${n} && `:""}${m.install} && ${m.dev}`),i.info("Done..")}async createProject(){try{this.config=await this.gatherProjectConfig(),await this.executeProjectGeneration(this.config)}catch(e){i.error(`Failed to create project: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}};var L=require("commander"),N=require("fs"),I=require("path"),O=require("url"),Q=(0,O.fileURLToPath)(f),F=JSON.parse((0,N.readFileSync)((0,I.join)(Q,"../../package.json"),"utf-8")).version,W=new L.Command;W.name("create-gramstax").description("CLI to create a new Gramstax Telegram bot project").version(F).argument("[project-directory]","Directory to create the project in").option("--pm <name>","Package manager (bun, npm, pnpm, yarn)").option("--runtime <name>","Runtime (bun, node)").option("--lmdb","Include @gramstax/lmdb (default: true)").option("--no-lmdb","Skip @gramstax/lmdb").option("--worker-thread","Include @gramstax/worker-thread (default: true)").option("--no-worker-thread","Skip @gramstax/worker-thread").option("--eslint","Include ESLint (default: true)").option("--no-eslint","Skip ESLint").action(async(s,e)=>{try{let n={};e?.pm&&(n.packageManager=e.pm),e?.runtime&&(n.runtime=e.runtime),e?.lmdb!==void 0&&(n.lmdb=e.lmdb),e?.workerThread!==void 0&&(n.workerThread=e.workerThread),e?.eslint!==void 0&&(n.eslint=e.eslint),await new k(s,{version:F,cliOptions:n}).createProject()}catch(n){i.error(`Failed to create project: ${String(n)}`),process.exit(1)}});W.parse();
package/dist/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- var C=Object.defineProperty;var g=(p,e)=>C(p,"name",{value:e,configurable:!0});import{LoggingPretty as N}from"logging-pretty";var r=new N;import*as i from"fs";import*as a from"path";import{fileURLToPath as R}from"url";import{readdirSync as F}from"fs";var m=class p{static{g(this,"FsUt")}static DEFAULT_IGNORED_ENTRIES=new Set([".git",".gitignore",".gitkeep",".DS_Store","Thumbs.db"]);static isDirectoryEffectivelyEmpty(e,t=p.DEFAULT_IGNORED_ENTRIES){return F(e).filter(o=>!t.has(o)).length===0}static resolvePackageManagerCommand(e,t=process.platform){return t!=="win32"||e==="bun"?e:`${e}.cmd`}static resolveInitialWorkingDirectory(e=process.cwd(),t=process.env.INIT_CWD){return t??e}};import{spawn as j}from"child_process";import P from"enquirer";var L=R(import.meta.url),T=a.dirname(L),d=class{constructor(e,t){this.projectDirectory=e;this.version=t?.version}projectDirectory;static{g(this,"Create")}config;version;printBanner(){let e=`
2
+ var F=Object.defineProperty;var h=($,e)=>F($,"name",{value:e,configurable:!0});import{LoggingPretty as L}from"logging-pretty";var i=new L;import*as r from"fs";import*as a from"path";import{fileURLToPath as O}from"url";import{readdirSync as N}from"fs";var m=class ${static{h(this,"FsUt")}static DEFAULT_IGNORED_ENTRIES=new Set([".git",".gitignore",".gitkeep",".DS_Store","Thumbs.db"]);static isDirectoryEffectivelyEmpty(e,n=$.DEFAULT_IGNORED_ENTRIES){return N(e).filter(s=>!n.has(s)).length===0}static resolvePackageManagerCommand(e,n=process.platform){return n!=="win32"||e==="bun"?e:`${e}.cmd`}static resolveInitialWorkingDirectory(e=process.cwd(),n=process.env.INIT_CWD){return n??e}};import{spawn as C}from"child_process";import x from"enquirer";var I=O(import.meta.url),W=a.dirname(I),b=class{constructor(e,n){this.projectDirectory=e;this.version=n?.version,this.cliOptions=n?.cliOptions}projectDirectory;static{h(this,"Create")}config;version;cliOptions;printBanner(){let e=`
3
3
  $$$$$$\\ $$\\
4
4
  $$ __$$\\ $$ |
5
5
  $$ / \\__| $$$$$$\\ $$$$$$\\ $$$$$$\\$$$$\\ $$$$$$$\\ $$$$$$\\ $$$$$$\\ $$\\ $$\\
@@ -8,6 +8,7 @@ $$ |\\_$$ |$$ | \\__|$$$$$$$ |$$ / $$ / $$ |\\$$$$$$\\ $$ | $$$$$$$ | \\
8
8
  $$ | $$ |$$ | $$ __$$ |$$ | $$ | $$ | \\____$$\\ $$ |$$\\ $$ __$$ | $$ $$<
9
9
  \\$$$$$$ |$$ | \\$$$$$$$ |$$ | $$ | $$ |$$$$$$$ | \\$$$$ |\\$$$$$$$ |$$ /\\$$\\
10
10
  \\______/ \\__| \\_______|\\__| \\__| \\__|\\_______/ \\____/ \\_______|\\__/ \\__|
11
-
12
- `,t=this.version?`v${this.version}`:"v0.0.0";console.log(`${e}${t}
13
- `)}getPackageManagerCommands(e){return{bun:{install:"bun install",dev:"bun dev",start:"bun start"},npm:{install:"npm install",dev:"npm run dev",start:"npm start"},pnpm:{install:"pnpm install",dev:"pnpm dev",start:"pnpm start"},yarn:{install:"yarn install",dev:"yarn dev",start:"yarn start"}}[e]}copyDirectorySync(e,t,n={}){i.existsSync(t)||i.mkdirSync(t,{recursive:!0});let s=i.readdirSync(e,{withFileTypes:!0});for(let o of s){let l=a.join(e,o.name),$=a.join(t,o.name);if(o.isDirectory())this.copyDirectorySync(l,$,n);else if(o.isFile()){let c=i.readFileSync(l,"utf-8");c=this.replaceTemplateVariables(c,n),i.writeFileSync($,c)}}}replaceTemplateVariables(e,t){let n=e;for(let[s,o]of Object.entries(t))n=n.replace(new RegExp(`{{${s}}}`,"g"),o);return n}async promptForProjectName(){return(await P.prompt({type:"input",name:"projectName",message:"What is your project named?",initial:"",validate:g(t=>t===""||/^[a-z0-9-_]+$/i.test(t)?!0:"Project name can only contain letters, numbers, hyphens, and underscores","validate")})).projectName}async promptForConfiguration(){let e=[];e.push({type:"select",name:"packageManager",message:"Which package manager would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"npm",message:"npm",value:"npm"},{name:"pnpm",message:"pnpm",value:"pnpm"},{name:"yarn",message:"Yarn",value:"yarn"}],initial:0}),e.push({type:"select",name:"runtime",message:"Which runtime would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"node",message:"Node.js",value:"node"}],initial:0}),e.push({type:"confirm",name:"eslint",message:"Would you like to use ESLint?",initial:!0});let t=await P.prompt(e);return{packageManager:t.packageManager,runtime:t.runtime,eslint:t.eslint}}async installDependencies(e,t){return await new Promise(n=>{let s=m.resolvePackageManagerCommand(t),l=j(s,["install"],{cwd:e,stdio:"inherit",shell:!1});l.on("close",$=>{$===0?n(!0):(r.fail("Dependencies installation failed"),n(!1))}),l.on("error",$=>{r.fail(`Dependencies installation failed: ${$.message}`),n(!1)})})}async gatherProjectConfig(){this.printBanner();let e=this.projectDirectory,t,n=!1,s=m.resolveInitialWorkingDirectory();e?(t=a.resolve(s,e),n=!1):(e=await this.promptForProjectName(),e===""?(e=a.basename(s),t=s,n=!0):(t=a.resolve(s,e),n=!1));let o=await this.promptForConfiguration(),l=o.packageManager,$=o.runtime,c=o.eslint;if(r.info(`Project: ${e}`),r.info(`Location: ${t}`),r.info(`Package Manager: ${l}`),r.info(`Runtime: ${$}`),r.info(`ESLint: ${c?"Yes":"No"}`),n){if(!m.isDirectoryEffectivelyEmpty(t))throw r.error("Current directory is not empty!"),new Error("Current directory is not empty")}else if(i.existsSync(t))throw r.error(`Directory "${e}" already exists!`),new Error(`Directory "${e}" already exists`);return{projectName:e,projectPath:t,packageManager:l,runtime:$,setupEslint:c}}async executeProjectGeneration(e){let{projectName:t,projectPath:n,packageManager:s,runtime:o,setupEslint:l}=e;r.info("Creating project directory.."),i.existsSync(n)||i.mkdirSync(n,{recursive:!0}),r.info("Copying template files..");let $=a.join(T,"templates"),c=this.getPackageManagerCommands(s),S={projectName:t,installCmd:c.install,runDevCmd:c.dev,runStartCmd:c.start};this.copyDirectorySync($,n,S),r.info("Creating logs directory.."),i.mkdirSync(a.join(n,"logs"),{recursive:!0}),i.writeFileSync(a.join(n,"logs/.gitkeep"),"");let b=a.join(n,".env"),y=a.join(n,".env.example");if(i.existsSync(y)&&(r.info("Creating .env file.."),i.renameSync(y,b)),o!=="bun"){r.info("Removing bunfig.toml for Node.js runtime..");let u=a.join(n,"bunfig.toml");i.existsSync(u)&&i.unlinkSync(u)}if(r.info("Installing dependencies.."),!await this.installDependencies(n,s))throw new Error("Project dependencies installation failed");let f=a.join(n,"eslint.config.ts.template"),D=a.join(n,"eslint.config.ts");if(l){r.info("Installing ESLint dependencies..");let u=m.resolvePackageManagerCommand(s),_=s==="yarn"?["add","-D"]:["install","-D"];_.push("eslint","@eslint/js","globals","typescript-eslint"),await new Promise(h=>{let v=j(u,_,{cwd:n,stdio:"inherit",shell:!1});v.on("close",()=>h()),v.on("error",E=>{r.fail(`Failed to install ESLint dependencies: ${E.message}`),h()})}),i.existsSync(f)&&i.renameSync(f,D)}else i.existsSync(f)&&i.unlinkSync(f);r.info("Project created successfully!"),r.info(`Next steps: ${e.projectPath!==process.cwd()?`cd ${t} && `:" "}${c.install} && ${c.dev}`),r.info("Done..")}async createProject(){try{this.config=await this.gatherProjectConfig(),await this.executeProjectGeneration(this.config)}catch(e){e instanceof Error?r.error(`Failed to create project: ${e.message}`):r.error(`Failed to create project: ${String(e)}`),process.exit(1)}}};import{Command as I}from"commander";import{readFileSync as W}from"fs";import{dirname as A,join as G}from"path";import{fileURLToPath as B}from"url";var O=B(import.meta.url),U=A(O),V=G(U,"../../package.json"),k=JSON.parse(W(V,"utf-8")).version,w=new I;w.name("create-gramstax").description("CLI to create a new Gramstax Telegram bot project").version(k).argument("[project-directory]","Directory to create the project in").action(async p=>{try{await new d(p,{version:k}).createProject()}catch(e){r.error(`Failed to create project: ${String(e)}`),process.exit(1)}});w.parse();
11
+
12
+ `,n=this.version?`v${this.version}`:"v0.0.0";console.log(`${e}${n}
13
+ `)}getPackageManagerCommands(e){return{bun:{install:"bun install",dev:"bun dev",start:"bun start"},npm:{install:"npm install",dev:"npm run dev",start:"npm start"},pnpm:{install:"pnpm install",dev:"pnpm dev",start:"pnpm start"},yarn:{install:"yarn install",dev:"yarn dev",start:"yarn start"}}[e]}copyDirectorySync(e,n,t={}){r.existsSync(n)||r.mkdirSync(n,{recursive:!0});let o=r.readdirSync(e,{withFileTypes:!0});for(let s of o){let u=a.join(e,s.name),p=a.join(n,s.name);if(s.isDirectory())this.copyDirectorySync(u,p,t);else if(s.isFile()){let c=r.readFileSync(u,"utf-8");c=this.replaceTemplateVariables(c,t),r.writeFileSync(p,c)}}}replaceTemplateVariables(e,n){let t=e;for(let[o,s]of Object.entries(n))t=t.replace(new RegExp(`{{${o}}}`,"g"),s);return t}async promptForProjectName(){return(await x.prompt({type:"input",name:"projectName",message:"What is your project named?",initial:"",validate:h(n=>n===""||/^[a-z0-9-_]+$/i.test(n)?!0:"Project name can only contain letters, numbers, hyphens, and underscores","validate")})).projectName}async promptForConfiguration(){let e=await x.prompt([{type:"select",name:"packageManager",message:"Which package manager would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"npm",message:"npm",value:"npm"},{name:"pnpm",message:"pnpm",value:"pnpm"},{name:"yarn",message:"Yarn",value:"yarn"}],initial:0},{type:"select",name:"runtime",message:"Which runtime would you like to use?",choices:[{name:"bun",message:"Bun (recommended)",value:"bun"},{name:"node",message:"Node.js",value:"node"}],initial:0},{type:"confirm",name:"lmdb",message:"Would you like to include @gramstax/lmdb for persistent storage? (recommended)",initial:!0},{type:"confirm",name:"workerThread",message:"Would you like to include @gramstax/worker-thread for background jobs? (recommended)",initial:!0},{type:"confirm",name:"eslint",message:"Would you like to use ESLint?",initial:!0}]);return{packageManager:e.packageManager,runtime:e.runtime,lmdb:e.lmdb,workerThread:e.workerThread,eslint:e.eslint}}async installDependencies(e,n){return await new Promise(t=>{let o=C(m.resolvePackageManagerCommand(n),["install"],{cwd:e,stdio:"inherit",shell:!1});o.on("close",s=>s===0?t(!0):(i.fail("Dependencies installation failed"),t(!1))),o.on("error",s=>(i.fail(`Dependencies installation failed: ${s.message}`),t(!1)))})}async gatherProjectConfig(){this.printBanner();let e=this.cliOptions,n=m.resolveInitialWorkingDirectory(),t=this.projectDirectory??"",o,s=!1;if(t)o=a.resolve(n,t);else{if(e&&this._hasAnyCliFlag())throw i.error("Project name is required when using CLI flags"),new Error("Missing project name");t=await this.promptForProjectName(),t===""?(t=a.basename(n),o=n,s=!0):o=a.resolve(n,t)}let p=e&&this._hasAnyCliFlag()?{packageManager:e.packageManager??"bun",runtime:e.runtime??"bun",lmdb:e.lmdb??!0,workerThread:e.workerThread??!0,eslint:e.eslint??!0}:await this.promptForConfiguration(),{packageManager:c,runtime:l,lmdb:y,workerThread:d,eslint:f}=p;if(i.info(`Project: ${t}`),i.info(`Location: ${o}`),i.info(`Package Manager: ${c}`),i.info(`Runtime: ${l}`),i.info(`LMDB Storage: ${y?"Yes":"No"}`),i.info(`Worker Thread: ${d?"Yes":"No"}`),i.info(`ESLint: ${f?"Yes":"No"}`),s){if(!m.isDirectoryEffectivelyEmpty(o))throw new Error("Current directory is not empty")}else if(r.existsSync(o))throw new Error(`Directory "${t}" already exists`);return{projectName:t,projectPath:o,packageManager:c,runtime:l,setupEslint:f,useLmdb:y,useWorkerThread:d}}_hasAnyCliFlag(){let e=this.cliOptions;return!!(e&&(e.packageManager!==void 0||e.runtime!==void 0||e.lmdb!==void 0||e.workerThread!==void 0||e.eslint!==void 0))}async executeProjectGeneration(e){let{projectName:n,projectPath:t,packageManager:o,runtime:s,setupEslint:u,useLmdb:p,useWorkerThread:c}=e,l=this.getPackageManagerCommands(o),y={projectName:n,installCmd:l.install,runDevCmd:l.dev,runStartCmd:l.start};i.info("Creating project directory.."),r.existsSync(t)||r.mkdirSync(t,{recursive:!0}),i.info("Copying template files.."),this.copyDirectorySync(a.join(W,"templates"),t,y),i.info("Creating logs directory.."),r.mkdirSync(a.join(t,"logs"),{recursive:!0}),r.writeFileSync(a.join(t,"logs/.gitkeep"),"");let d=a.join(t,".env"),f=a.join(t,".env.example");if(r.existsSync(f)&&(i.info("Creating .env file.."),r.renameSync(f,d)),s!=="bun"){let g=a.join(t,"bunfig.toml");r.existsSync(g)&&r.unlinkSync(g)}let k=a.join(t,"src","db");if(p){i.info("Keeping @gramstax/lmdb files..");let g=r.readFileSync(d,"utf-8");r.writeFileSync(d,g+`DB_PATH="./data"
14
+ `),r.mkdirSync(a.join(t,"data"),{recursive:!0}),r.writeFileSync(a.join(t,"data/.gitkeep"),"")}else i.info("Removing @gramstax/lmdb files.."),r.existsSync(k)&&r.rmSync(k,{recursive:!0,force:!0});let v=a.join(t,"src","workers");if(c?i.info("Keeping @gramstax/worker-thread files.."):(i.info("Removing @gramstax/worker-thread files.."),r.existsSync(v)&&r.rmSync(v,{recursive:!0,force:!0})),i.info("Installing dependencies.."),!await this.installDependencies(t,o))throw new Error("Dependencies installation failed");let _=a.join(t,"eslint.config.ts.template"),T=a.join(t,"eslint.config.ts");if(u){i.info("Installing ESLint dependencies..");let g=m.resolvePackageManagerCommand(o),w=o==="yarn"?["add","-D"]:["install","-D"];w.push("eslint","@eslint/js","globals","typescript-eslint"),await new Promise(j=>{let P=C(g,w,{cwd:t,stdio:"inherit",shell:!1});P.on("close",()=>j()),P.on("error",M=>{i.fail(`Failed to install ESLint: ${M.message}`),j()})}),r.existsSync(_)&&r.renameSync(_,T)}else r.existsSync(_)&&r.unlinkSync(_);i.info("Project created successfully!"),i.info(`Next steps: ${e.projectPath!==process.cwd()?`cd ${n} && `:""}${l.install} && ${l.dev}`),i.info("Done..")}async createProject(){try{this.config=await this.gatherProjectConfig(),await this.executeProjectGeneration(this.config)}catch(e){i.error(`Failed to create project: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}};import{Command as A}from"commander";import{readFileSync as B}from"fs";import{join as G}from"path";import{fileURLToPath as U}from"url";var Y=U(import.meta.url),E=JSON.parse(B(G(Y,"../../package.json"),"utf-8")).version,D=new A;D.name("create-gramstax").description("CLI to create a new Gramstax Telegram bot project").version(E).argument("[project-directory]","Directory to create the project in").option("--pm <name>","Package manager (bun, npm, pnpm, yarn)").option("--runtime <name>","Runtime (bun, node)").option("--lmdb","Include @gramstax/lmdb (default: true)").option("--no-lmdb","Skip @gramstax/lmdb").option("--worker-thread","Include @gramstax/worker-thread (default: true)").option("--no-worker-thread","Skip @gramstax/worker-thread").option("--eslint","Include ESLint (default: true)").option("--no-eslint","Skip ESLint").action(async($,e)=>{try{let n={};e?.pm&&(n.packageManager=e.pm),e?.runtime&&(n.runtime=e.runtime),e?.lmdb!==void 0&&(n.lmdb=e.lmdb),e?.workerThread!==void 0&&(n.workerThread=e.workerThread),e?.eslint!==void 0&&(n.eslint=e.eslint),await new b($,{version:E,cliOptions:n}).createProject()}catch(n){i.error(`Failed to create project: ${String(n)}`),process.exit(1)}});D.parse();
@@ -38,7 +38,8 @@
38
38
  "test:all": "bun test test/ e2e/"
39
39
  },
40
40
  "dependencies": {
41
- "gramstax": "^0.9.0",
41
+ "@gramstax/lmdb": "workspace:*",
42
+ "gramstax": "^0.10.0",
42
43
  "dayjs": "^1.11.13"
43
44
  },
44
45
  "peerDependencies": {
@@ -0,0 +1,24 @@
1
+ import {LmdbEnv} from "@gramstax/lmdb"
2
+ import {UserRepo} from "./repositories/user"
3
+
4
+ export class Db extends LmdbEnv {
5
+ private _ready: Promise<void>
6
+
7
+ constructor(storePath: string) {
8
+ super({path: storePath, maxDbs: 10, compression: true, encoding: `json`})
9
+ this._ready = this._init()
10
+ }
11
+
12
+ private async _init() {
13
+ await this.open()
14
+ this.register(UserRepo)
15
+ }
16
+
17
+ async ready() {
18
+ await this._ready
19
+ }
20
+
21
+ get userRepo(): UserRepo {
22
+ return this.get(UserRepo)
23
+ }
24
+ }
@@ -0,0 +1,47 @@
1
+ import {LmdbRepository} from "@gramstax/lmdb"
2
+
3
+ export type IKUser = number
4
+
5
+ export type IVUser = {
6
+ tgId: number
7
+ username: string
8
+ firstName: string
9
+ lastName: string
10
+ languageCode: string
11
+ createdAt: number
12
+ updatedAt: number
13
+ }
14
+
15
+ export class UserRepo extends LmdbRepository<IVUser, IKUser> {
16
+ static dbName = `user`
17
+
18
+ async findOrCreate(tgId: number, data: {username: string; firstName: string; lastName: string; languageCode: string}): Promise<{user: IVUser; created: boolean}> {
19
+ return this.env.transaction(async () => {
20
+ const existing = await this.get(tgId)
21
+ if (existing) {
22
+ const updated: IVUser = {...existing, ...data, updatedAt: Date.now()}
23
+ await this.set(tgId, updated)
24
+ return {user: updated, created: false}
25
+ }
26
+ const now = Date.now()
27
+ const user: IVUser = {tgId, ...data, createdAt: now, updatedAt: now}
28
+ await this.set(tgId, user)
29
+ return {user, created: true}
30
+ })
31
+ }
32
+
33
+ async touch(tgId: number) {
34
+ return this.env.transaction(async () => {
35
+ const user = await this.get(tgId)
36
+ if (user) {
37
+ await this.set(tgId, {...user, updatedAt: Date.now()})
38
+ }
39
+ })
40
+ }
41
+
42
+ async displayName(tgId: number): Promise<string> {
43
+ const user = await this.get(tgId)
44
+ if (!user) return `User#${tgId}`
45
+ return user.firstName || user.username || `User#${tgId}`
46
+ }
47
+ }
@@ -3,6 +3,9 @@
3
3
  "paths": {
4
4
  "~/*": [
5
5
  "./src/*"
6
+ ],
7
+ "@gramstax/lmdb": [
8
+ "../../../lmdb/src/index.ts"
6
9
  ]
7
10
  },
8
11
  // Environment setup & latest features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gramstax",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",