@sundaysf/cli-v2 0.0.2 → 0.0.4
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/dist/bin/index.js +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/templates/backend/.claude/agents/knex-table-implementer.md +113 -0
- package/dist/templates/backend-db-sql/.claude/agents/knex-table-implementer.md +114 -0
- package/dist/templates/backend-db-sql/.claude/agents/sundays-backend-builder.md +70 -0
- package/dist/templates/backend-db-sql/.claude/settings.local.json +19 -0
- package/dist/templates/backend-db-sql/.env.example +13 -0
- package/dist/templates/backend-db-sql/.prettierignore +3 -0
- package/dist/templates/backend-db-sql/.prettierrc +9 -0
- package/dist/templates/backend-db-sql/CLAUDE.md +374 -0
- package/dist/templates/backend-db-sql/Dockerfile +17 -0
- package/dist/templates/backend-db-sql/README.md +34 -0
- package/dist/templates/backend-db-sql/db/knexfile.ts +33 -0
- package/dist/templates/backend-db-sql/db/migrations/.gitkeep +0 -0
- package/dist/templates/backend-db-sql/db/migrations/001_create_sundays_package_version.ts +13 -0
- package/dist/templates/backend-db-sql/db/seeds/001_sundays_package_version_seed.ts +11 -0
- package/dist/templates/backend-db-sql/db/src/KnexConnection.ts +74 -0
- package/dist/templates/backend-db-sql/db/src/d.types.ts +18 -0
- package/dist/templates/backend-db-sql/db/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
- package/dist/templates/backend-db-sql/db/src/index.ts +9 -0
- package/dist/templates/backend-db-sql/db/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
- package/dist/templates/backend-db-sql/db/tsconfig.json +16 -0
- package/dist/templates/backend-db-sql/eslint.config.js +20 -0
- package/dist/templates/backend-db-sql/src/app.ts +35 -0
- package/dist/templates/backend-db-sql/src/common/config/origins/origins.config.ts +11 -0
- package/dist/templates/backend-db-sql/src/common/utils/environment.resolver.ts +4 -0
- package/dist/templates/backend-db-sql/src/common/utils/version.resolver.ts +5 -0
- package/dist/templates/backend-db-sql/src/controllers/health/health.controller.ts +24 -0
- package/dist/templates/backend-db-sql/src/middlewares/error/error.middleware.ts +21 -0
- package/dist/templates/backend-db-sql/src/routes/health/health.router.ts +17 -0
- package/dist/templates/backend-db-sql/src/routes/index.ts +57 -0
- package/dist/templates/backend-db-sql/src/server.ts +18 -0
- package/dist/templates/backend-db-sql/src/types.d.ts +10 -0
- package/dist/templates/backend-db-sql/tsconfig.json +16 -0
- package/dist/templates/db-sql/.claude/agents/sundays-backend-builder.md +70 -0
- package/dist/templates/module/.claude/agents/knex-table-implementer.md +113 -0
- package/dist/templates/module/.claude/agents/sundays-backend-builder.md +70 -0
- package/dist/templates/module/.claude/settings.local.json +10 -0
- package/package.json +1 -1
package/dist/bin/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command}from"commander";import inquirer from"inquirer";import{generateController,generateRouter}from"./generators/class.js";import fs from"fs-extra";import path from"path";import{fileURLToPath}from"url";import{exec}from"child_process";import ora from"ora";import{generateBasePostman,getItemFormatted}from"./generators/postman.js";const __dirname=path.dirname(fileURLToPath(import.meta.url)),pkg=fs.readJsonSync(path.join(__dirname,"../../package.json")),program=new Command;program.version(pkg.version,"-v, --version").option("--init","Initialize a new project").option("--backend","Initialize with backend template").option("--module","Initialize with module template").option("--db-sql","Initialize with db-sql template").option("--create-controller","Create a controller and its associated route"),program.parse(process.argv);const options=program.opts();function runFormatter(e="."){return new Promise(((t,r)=>{const s=exec("npm run format",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`npm format exited with code ${e}`))}))}))}function runNpmInstall(e){return new Promise(((t,r)=>{const s=exec("npm install",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`npm install exited with code ${e}`))}))}))}function initGitRepo(e){return new Promise(((t,r)=>{const s=exec("git init",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`Git init exited with code ${e}`))}))}))}options.init&&(options.backend||options.module||options.dbSql)?(console.log("Hello! I'm Paul Sundays, your backend architect. Let's craft an awesome backend together!"),inquirer.prompt([{type:"input",name:"projectName",message:"Name of the project:",validate:e=>!!e||"The name of the project can not be empty"}]).then((async({projectName:e})=>{const t=options.backend?"backend":options.module?"module":"db-sql",r=path.join(__dirname,`../templates/${t}`),s=e.toLowerCase().replace(/\s+/g,"-"),
|
|
2
|
+
import{Command}from"commander";import inquirer from"inquirer";import{generateController,generateRouter}from"./generators/class.js";import fs from"fs-extra";import path from"path";import{fileURLToPath}from"url";import{exec}from"child_process";import ora from"ora";import{generateBasePostman,getItemFormatted}from"./generators/postman.js";const __dirname=path.dirname(fileURLToPath(import.meta.url)),pkg=fs.readJsonSync(path.join(__dirname,"../../package.json")),program=new Command;program.version(pkg.version,"-v, --version").option("--init","Initialize a new project").option("--backend","Initialize with backend template").option("--module","Initialize with module template").option("--db-sql","Initialize with db-sql template").option("--backend-db-sql","Initialize with backend + database template").option("--create-controller","Create a controller and its associated route"),program.parse(process.argv);const options=program.opts();function runFormatter(e="."){return new Promise(((t,r)=>{const s=exec("npm run format",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`npm format exited with code ${e}`))}))}))}function runNpmInstall(e){return new Promise(((t,r)=>{const s=exec("npm install",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`npm install exited with code ${e}`))}))}))}function initGitRepo(e){return new Promise(((t,r)=>{const s=exec("git init",{cwd:e});s.stdout.on("data",(e=>process.stdout.write(e))),s.stderr.on("data",(e=>process.stderr.write(e))),s.on("close",(e=>{0===e?t():r(new Error(`Git init exited with code ${e}`))}))}))}options.init&&(options.backend||options.module||options.dbSql||options.backendDbSql)?(console.log("Hello! I'm Paul Sundays, your backend architect. Let's craft an awesome backend together!"),inquirer.prompt([{type:"input",name:"projectName",message:"Name of the project:",validate:e=>!!e||"The name of the project can not be empty"}]).then((async({projectName:e})=>{const t=options.backend?"backend":options.backendDbSql?"backend-db-sql":options.module?"module":"db-sql",r=path.join(__dirname,`../templates/${t}`);let s,n,o;"."===e?(s=path.resolve("."),n=path.basename(s),fs.existsSync(path.join(s,"package.json"))&&(console.error("A package.json already exists in the current directory."),process.exit(1))):(n=e.toLowerCase().replace(/\s+/g,"-"),s=path.resolve(n),fs.existsSync(s)&&(console.error(`Directory "${n}" already exists.`),process.exit(1)),await fs.mkdir(s,{recursive:!0}));try{await fs.copy(r,s,{overwrite:!1,errorOnExist:!0,filter:(e,t)=>!0}),console.log("Template files copied successfully")}catch(e){console.error("Error copying the files:",e),process.exit(1)}if(o="db-sql"===t?{name:n,version:"0.0.1",description:"Knex Module",main:"dist/index.js",scripts:{test:"jest",format:"prettier --write .",build:"tsc",clean:"rimraf dist && npm run format && npm run build","npm:publish":"npm run clean && npm publish","migrate:create":"knex migrate:make migration -x ts","migrate:deploy":"knex migrate:latest","seed:create":"knex seed:make seed -x ts","seed:run":"knex seed:run"},repository:{type:"git",url:""},author:"",license:"MIT",bugs:{url:""},homepage:"",devDependencies:{"@eslint/js":"^9.23.0","@types/node":"^22.13.13",eslint:"^9.23.0",globals:"^16.0.0",prettier:"^3.5.3",rimraf:"^6.0.1","ts-node":"^10.9.2",typescript:"^5.8.2","typescript-eslint":"^8.28.0"},dependencies:{dotenv:"^16.4.7",knex:"^3.1.0",lodash:"^4.17.21",pg:"^8.14.1",uuid:"^11.1.0"}}:"backend"===t?{name:n,version:"1.0.0",description:"Backend project generated with Sundays Framework",main:"dist/server.js",scripts:{test:"jest",start:"node dist/server.js",build:"tsc","start:dev":"nodemon src/server.ts",format:"prettier --write .","create:controller":"sundaysf --create-controller"},dependencies:{morgan:"^1.10.0",cors:"^2.8.5",dotenv:"^16.4.7",express:"^4.18.2"},devDependencies:{"@types/morgan":"^1.9.9","@types/cors":"^2.8.17","@types/express":"^4.17.21",nodemon:"^3.0.2",prettier:"^3.5.3","ts-node":"^10.9.2",typescript:"^5.8.2","@eslint/js":"^9.23.0",eslint:"^9.23.0","typescript-eslint":"^8.28.0"},author:"",license:"MIT"}:"backend-db-sql"===t?{name:n,version:"1.0.0",description:"Backend project with database module generated with Sundays Framework",main:"dist/server.js",scripts:{test:"jest",start:"node dist/server.js",build:"tsc","start:dev":"nodemon src/server.ts",format:"prettier --write .","create:controller":"sundaysf --create-controller","migrate:create":"knex migrate:make migration -x ts --knexfile db/knexfile.ts","migrate:deploy":"knex migrate:latest --knexfile db/knexfile.ts","seed:create":"knex seed:make seed -x ts --knexfile db/knexfile.ts","seed:run":"knex seed:run --knexfile db/knexfile.ts","db:build":"tsc -p db/tsconfig.json","db:publish":"cd db && npm publish"},dependencies:{morgan:"^1.10.0",cors:"^2.8.5",dotenv:"^16.4.7",express:"^4.18.2",knex:"^3.1.0",lodash:"^4.17.21",pg:"^8.14.1",uuid:"^11.1.0"},devDependencies:{"@types/morgan":"^1.9.9","@types/cors":"^2.8.17","@types/express":"^4.17.21","@types/lodash":"^4.17.16","@types/node":"^22.13.13",nodemon:"^3.0.2",prettier:"^3.5.3",rimraf:"^6.0.1","ts-node":"^10.9.2",typescript:"^5.8.2","@eslint/js":"^9.23.0",eslint:"^9.23.0",globals:"^16.0.0","typescript-eslint":"^8.28.0"},author:"",license:"MIT"}:{name:n,version:"1.0.0",description:"Module generated with Sundays Framework",main:"dist/index.js",types:"dist/index.d.ts",scripts:{test:"jest",build:"tsc",format:"prettier --write .",clean:"rimraf dist && npm run build"},dependencies:{},devDependencies:{prettier:"^3.5.3",rimraf:"^6.0.1",typescript:"^5.8.2"},author:"",license:"MIT"},await fs.writeJson(path.join(s,"package.json"),o,{spaces:2}),"backend"===t||"backend-db-sql"===t){const t=generateBasePostman(e);await fs.writeJson(path.join(s,"postman.json"),t,{spaces:2})}if("backend-db-sql"===t){const e={name:`${n}-db`,version:"0.0.1",description:"Database module",main:"dist/index.js",types:"dist/index.d.ts",scripts:{build:"tsc",clean:"rimraf dist && npm run build",format:"prettier --write .","npm:publish":"npm run clean && npm publish","migrate:create":"knex migrate:make migration -x ts","migrate:deploy":"knex migrate:latest","seed:create":"knex seed:make seed -x ts","seed:run":"knex seed:run"},dependencies:{dotenv:"^16.4.7",knex:"^3.1.0",lodash:"^4.17.21",pg:"^8.14.1",uuid:"^11.1.0"},devDependencies:{typescript:"^5.8.2","ts-node":"^10.9.2",rimraf:"^6.0.1",prettier:"^3.5.3","@types/node":"^22.13.13"}};await fs.writeJson(path.join(s,"db/package.json"),e,{spaces:2})}const i=ora("Git init...").start();await initGitRepo(s),i.succeed("Git initialized");const a=ora("Installing dependencies...").start();await runNpmInstall(s),a.succeed("Dependencies installed"),await runFormatter(s);const c="."===e?"Project created successfully in current directory":`Project '${e}' created successfully in ./${n}`;console.log(c)}))):options.createController?(fs.existsSync("./src/controllers")&&fs.existsSync("./postman.json")||(console.error("This command must be run inside a Sundays backend project."),console.error("Expected ./src/controllers/ and ./postman.json to exist."),process.exit(1)),inquirer.prompt([{type:"input",name:"controllerName",message:"Controller name:",validate:e=>e?!!/^[a-zA-Z][a-zA-Z0-9]*$/.test(e)||"Name must start with a letter and contain only letters and numbers":"The controller name is empty"}]).then((async({controllerName:e})=>{try{const t=e.charAt(0).toLowerCase()+e.slice(1),r=e.charAt(0).toUpperCase()+e.slice(1),s=`${r}Controller`,n=`${t}.controller.ts`,o=`${t}`,i=`${r}Router`,a=`${t}.router.ts`,c=`${t}`,d=`./src/controllers/${o}`,p=`./src/routes/${c}`;fs.existsSync(d)&&(console.error(`Controller directory "${d}" already exists.`),process.exit(1));const l=generateController(s),m=generateRouter(i,s,o),u=`${d}/${n}`,f=`${p}/${a}`;await fs.mkdir(d,{recursive:!0}),await fs.mkdir(p,{recursive:!0}),await fs.writeFile(u,l),await fs.writeFile(f,m);const g=await fs.readFile("./postman.json","utf-8"),b=JSON.parse(g),x=getItemFormatted(c,b.info.name);b.item.push(x),await fs.writeJson(path.join(".","postman.json"),b,{spaces:2}),await runFormatter("."),console.log(`Created ${s} in ${o}/${n} and ${i} in ${c}/${a}`)}catch(e){console.error("Error generating controller:",e.message),process.exit(1)}}))):program.help();
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/bin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Command","inquirer","generateController","generateRouter","fs","path","fileURLToPath","exec","ora","generateBasePostman","getItemFormatted","__dirname","dirname","url","pkg","readJsonSync","join","program","version","option","parse","process","argv","options","opts","runFormatter","cwd","Promise","resolve","reject","formatProcess","stdout","on","data","write","stderr","code","Error","runNpmInstall","destDir","installProcess","initGitRepo","gitProcess","init","backend","module","dbSql","console","log","prompt","type","name","message","validate","input","then","async","projectName","templateType","srcDir","dirName","toLowerCase","replace","existsSync","error","exit","mkdir","recursive","copy","overwrite","errorOnExist","filter","src","dest","err","packageJson","description","main","scripts","test","format","build","clean","repository","author","license","bugs","homepage","devDependencies","eslint","globals","prettier","rimraf","typescript","dependencies","dotenv","knex","lodash","pg","uuid","start","morgan","cors","express","nodemon","types","writeJson","spaces","postmanBaseData","spinnerGit","succeed","spinner","createController","controllerName","nonCapitalized","charAt","slice","capitalized","toUpperCase","controllerClassName","controllerFileName","controllerDirectoryName","routerClassName","routerFileName","routerDirectoryName","controllerDir","routerDir","classControllerContentGenerator","classRouterContentGenerator","controllerPath","routerPath","writeFile","readFile","leanData","JSON","itemToPush","info","item","push","help"],"sources":["bin/index.js"],"mappings":"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","ignoreList":[],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\nimport inquirer from 'inquirer';\r\nimport { generateController, generateRouter } from './generators/class.js';\r\nimport fs from 'fs-extra';\r\nimport path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { exec } from 'child_process';\r\nimport ora from 'ora';\r\nimport { generateBasePostman, getItemFormatted } from './generators/postman.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\nconst pkg = fs.readJsonSync(path.join(__dirname, '../../package.json'));\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .version(pkg.version, '-v, --version')\r\n .option('--init', 'Initialize a new project')\r\n .option('--backend', 'Initialize with backend template')\r\n .option('--module', 'Initialize with module template')\r\n .option('--db-sql', 'Initialize with db-sql template')\r\n .option('--create-controller', 'Create a controller and its associated route');\r\n\r\nprogram.parse(process.argv);\r\n\r\nconst options = program.opts();\r\n\r\nif (options.init && (options.backend || options.module || options.dbSql)) {\r\n console.log(\"Hello! I'm Paul Sundays, your backend architect. Let's craft an awesome backend together!\");\r\n inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'projectName',\r\n message: 'Name of the project:',\r\n validate: input => input ? true : 'The name of the project can not be empty'\r\n }\r\n ]).then(async ({ projectName }) => {\r\n const templateType = options.backend ? 'backend' : options.module ? 'module' : 'db-sql';\r\n const srcDir = path.join(__dirname, `../templates/${templateType}`);\r\n const dirName = projectName.toLowerCase().replace(/\\s+/g, '-');\r\n const destDir = path.resolve(dirName);\r\n\r\n if (fs.existsSync(destDir)) {\r\n console.error(`Directory \"${dirName}\" already exists.`);\r\n process.exit(1);\r\n }\r\n\r\n try {\r\n await fs.mkdir(destDir, { recursive: true });\r\n await fs.copy(srcDir, destDir, {\r\n overwrite: false,\r\n errorOnExist: true,\r\n filter: (src, dest) => {\r\n return true;\r\n }\r\n });\r\n console.log('Template files copied successfully');\r\n } catch (err) {\r\n console.error('Error copying the files:', err);\r\n process.exit(1);\r\n }\r\n\r\n let packageJson;\r\n if (templateType === 'db-sql') {\r\n packageJson = {\r\n name: dirName,\r\n version: \"0.0.1\",\r\n description: \"Knex Module\",\r\n main: \"dist/index.js\",\r\n scripts: {\r\n test: \"jest\",\r\n format: \"prettier --write .\",\r\n build: \"tsc\",\r\n clean: \"rimraf dist && npm run format && npm run build\",\r\n \"npm:publish\": \"npm run clean && npm publish\",\r\n \"migrate:create\": \"knex migrate:make migration -x ts\",\r\n \"migrate:deploy\": \"knex migrate:latest\",\r\n \"seed:create\": \"knex seed:make seed -x ts\",\r\n \"seed:run\": \"knex seed:run\"\r\n },\r\n repository: {\r\n type: \"git\",\r\n url: \"\"\r\n },\r\n author: \"\",\r\n license: \"MIT\",\r\n bugs: {\r\n url: \"\"\r\n },\r\n homepage: \"\",\r\n devDependencies: {\r\n \"@eslint/js\": \"^9.23.0\",\r\n \"@types/node\": \"^22.13.13\",\r\n eslint: \"^9.23.0\",\r\n globals: \"^16.0.0\",\r\n prettier: \"^3.5.3\",\r\n rimraf: \"^6.0.1\",\r\n \"ts-node\": \"^10.9.2\",\r\n typescript: \"^5.8.2\",\r\n \"typescript-eslint\": \"^8.28.0\"\r\n },\r\n dependencies: {\r\n dotenv: \"^16.4.7\",\r\n knex: \"^3.1.0\",\r\n lodash: \"^4.17.21\",\r\n pg: \"^8.14.1\",\r\n uuid: \"^11.1.0\"\r\n }\r\n };\r\n } else if (templateType === 'backend') {\r\n packageJson = {\r\n name: dirName,\r\n version: \"1.0.0\",\r\n description: `Backend project generated with Sundays Framework`,\r\n main: \"dist/server.js\",\r\n scripts: {\r\n \"test\": \"jest\",\r\n \"start\": \"node dist/server.js\",\r\n \"build\": \"tsc\",\r\n \"start:dev\": \"nodemon src/server.ts\",\r\n \"format\": \"prettier --write .\",\r\n \"create:controller\": \"sundaysf --create-controller\"\r\n },\r\n dependencies: {\r\n \"morgan\": \"^1.10.0\",\r\n \"cors\": \"^2.8.5\",\r\n \"dotenv\": \"^16.4.7\",\r\n \"express\": \"^4.18.2\"\r\n },\r\n devDependencies: {\r\n \"@types/morgan\": \"^1.9.9\",\r\n \"@types/cors\": \"^2.8.17\",\r\n \"@types/express\": \"^4.17.21\",\r\n \"nodemon\": \"^3.0.2\",\r\n \"prettier\": \"^3.5.3\",\r\n \"ts-node\": \"^10.9.2\",\r\n \"typescript\": \"^5.8.2\",\r\n \"@eslint/js\": \"^9.23.0\",\r\n \"eslint\": \"^9.23.0\",\r\n \"typescript-eslint\": \"^8.28.0\"\r\n },\r\n author: \"\",\r\n license: \"MIT\"\r\n };\r\n } else {\r\n // module\r\n packageJson = {\r\n name: dirName,\r\n version: \"1.0.0\",\r\n description: `Module generated with Sundays Framework`,\r\n main: \"dist/index.js\",\r\n types: \"dist/index.d.ts\",\r\n scripts: {\r\n \"test\": \"jest\",\r\n \"build\": \"tsc\",\r\n \"format\": \"prettier --write .\",\r\n \"clean\": \"rimraf dist && npm run build\"\r\n },\r\n dependencies: {},\r\n devDependencies: {\r\n \"prettier\": \"^3.5.3\",\r\n \"rimraf\": \"^6.0.1\",\r\n \"typescript\": \"^5.8.2\"\r\n },\r\n author: \"\",\r\n license: \"MIT\"\r\n };\r\n }\r\n\r\n await fs.writeJson(path.join(destDir, 'package.json'), packageJson, { spaces: 2 });\r\n\r\n if (templateType === 'backend') {\r\n const postmanBaseData = generateBasePostman(projectName);\r\n await fs.writeJson(path.join(destDir, 'postman.json'), postmanBaseData, { spaces: 2 });\r\n }\r\n\r\n const spinnerGit = ora('Git init...').start();\r\n await initGitRepo(destDir);\r\n spinnerGit.succeed('Git initialized');\r\n\r\n const spinner = ora('Installing dependencies...').start();\r\n await runNpmInstall(destDir);\r\n spinner.succeed('Dependencies installed');\r\n\r\n await runFormatter(destDir);\r\n console.log(`Project '${projectName}' created successfully in ./${dirName}`);\r\n });\r\n} else if (options.createController) {\r\n // Verify we're inside a backend project\r\n if (!fs.existsSync('./src/controllers') || !fs.existsSync('./postman.json')) {\r\n console.error('This command must be run inside a Sundays backend project.');\r\n console.error('Expected ./src/controllers/ and ./postman.json to exist.');\r\n process.exit(1);\r\n }\r\n\r\n inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'controllerName',\r\n message: 'Controller name:',\r\n validate: input => {\r\n if (!input) return 'The controller name is empty';\r\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(input)) {\r\n return 'Name must start with a letter and contain only letters and numbers';\r\n }\r\n return true;\r\n }\r\n }\r\n ]).then(async ({ controllerName }) => {\r\n try {\r\n const nonCapitalized = controllerName.charAt(0).toLowerCase() + controllerName.slice(1);\r\n const capitalized = controllerName.charAt(0).toUpperCase() + controllerName.slice(1);\r\n const controllerClassName = `${capitalized}Controller`;\r\n const controllerFileName = `${nonCapitalized}.controller.ts`;\r\n const controllerDirectoryName = `${nonCapitalized}`;\r\n const routerClassName = `${capitalized}Router`;\r\n const routerFileName = `${nonCapitalized}.router.ts`;\r\n const routerDirectoryName = `${nonCapitalized}`;\r\n\r\n const controllerDir = `./src/controllers/${controllerDirectoryName}`;\r\n const routerDir = `./src/routes/${routerDirectoryName}`;\r\n\r\n if (fs.existsSync(controllerDir)) {\r\n console.error(`Controller directory \"${controllerDir}\" already exists.`);\r\n process.exit(1);\r\n }\r\n\r\n const classControllerContentGenerator = generateController(controllerClassName);\r\n const classRouterContentGenerator = generateRouter(routerClassName, controllerClassName, controllerDirectoryName);\r\n const controllerPath = `${controllerDir}/${controllerFileName}`;\r\n const routerPath = `${routerDir}/${routerFileName}`;\r\n\r\n await fs.mkdir(controllerDir, { recursive: true });\r\n await fs.mkdir(routerDir, { recursive: true });\r\n await fs.writeFile(controllerPath, classControllerContentGenerator);\r\n await fs.writeFile(routerPath, classRouterContentGenerator);\r\n\r\n const postmanBaseData = await fs.readFile('./postman.json', 'utf-8');\r\n const leanData = JSON.parse(postmanBaseData);\r\n const itemToPush = getItemFormatted(routerDirectoryName, leanData.info.name);\r\n leanData.item.push(itemToPush);\r\n await fs.writeJson(path.join('.', 'postman.json'), leanData, { spaces: 2 });\r\n\r\n await runFormatter('.');\r\n console.log(`Created ${controllerClassName} in ${controllerDirectoryName}/${controllerFileName} and ${routerClassName} in ${routerDirectoryName}/${routerFileName}`);\r\n } catch (err) {\r\n console.error('Error generating controller:', err.message);\r\n process.exit(1);\r\n }\r\n });\r\n} else {\r\n program.help();\r\n}\r\n\r\n\r\nfunction runFormatter(cwd = '.') {\r\n return new Promise((resolve, reject) => {\r\n const formatProcess = exec('npm run format', { cwd });\r\n\r\n formatProcess.stdout.on('data', data => process.stdout.write(data));\r\n formatProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n formatProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`npm format exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n\r\n\r\nfunction runNpmInstall(destDir) {\r\n return new Promise((resolve, reject) => {\r\n const installProcess = exec('npm install', { cwd: destDir });\r\n\r\n installProcess.stdout.on('data', data => process.stdout.write(data));\r\n installProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n installProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`npm install exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n\r\nfunction initGitRepo(destDir) {\r\n return new Promise((resolve, reject) => {\r\n const gitProcess = exec('git init', { cwd: destDir });\r\n\r\n gitProcess.stdout.on('data', data => process.stdout.write(data));\r\n gitProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n gitProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`Git init exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Command","inquirer","generateController","generateRouter","fs","path","fileURLToPath","exec","ora","generateBasePostman","getItemFormatted","__dirname","dirname","url","pkg","readJsonSync","join","program","version","option","parse","process","argv","options","opts","runFormatter","cwd","Promise","resolve","reject","formatProcess","stdout","on","data","write","stderr","code","Error","runNpmInstall","destDir","installProcess","initGitRepo","gitProcess","init","backend","module","dbSql","backendDbSql","console","log","prompt","type","name","message","validate","input","then","async","projectName","templateType","srcDir","dirName","packageJson","basename","existsSync","error","exit","toLowerCase","replace","mkdir","recursive","copy","overwrite","errorOnExist","filter","src","dest","err","description","main","scripts","test","format","build","clean","repository","author","license","bugs","homepage","devDependencies","eslint","globals","prettier","rimraf","typescript","dependencies","dotenv","knex","lodash","pg","uuid","start","morgan","cors","express","nodemon","types","writeJson","spaces","postmanBaseData","dbPackageJson","spinnerGit","succeed","spinner","outputMsg","createController","controllerName","nonCapitalized","charAt","slice","capitalized","toUpperCase","controllerClassName","controllerFileName","controllerDirectoryName","routerClassName","routerFileName","routerDirectoryName","controllerDir","routerDir","classControllerContentGenerator","classRouterContentGenerator","controllerPath","routerPath","writeFile","readFile","leanData","JSON","itemToPush","info","item","push","help"],"sources":["bin/index.js"],"mappings":"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","ignoreList":[],"sourcesContent":["#!/usr/bin/env node\r\n\r\nimport { Command } from 'commander';\r\nimport inquirer from 'inquirer';\r\nimport { generateController, generateRouter } from './generators/class.js';\r\nimport fs from 'fs-extra';\r\nimport path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { exec } from 'child_process';\r\nimport ora from 'ora';\r\nimport { generateBasePostman, getItemFormatted } from './generators/postman.js';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\nconst pkg = fs.readJsonSync(path.join(__dirname, '../../package.json'));\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .version(pkg.version, '-v, --version')\r\n .option('--init', 'Initialize a new project')\r\n .option('--backend', 'Initialize with backend template')\r\n .option('--module', 'Initialize with module template')\r\n .option('--db-sql', 'Initialize with db-sql template')\r\n .option('--backend-db-sql', 'Initialize with backend + database template')\r\n .option('--create-controller', 'Create a controller and its associated route');\r\n\r\nprogram.parse(process.argv);\r\n\r\nconst options = program.opts();\r\n\r\nif (options.init && (options.backend || options.module || options.dbSql || options.backendDbSql)) {\r\n console.log(\"Hello! I'm Paul Sundays, your backend architect. Let's craft an awesome backend together!\");\r\n inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'projectName',\r\n message: 'Name of the project:',\r\n validate: input => input ? true : 'The name of the project can not be empty'\r\n }\r\n ]).then(async ({ projectName }) => {\r\n const templateType = options.backend ? 'backend' : options.backendDbSql ? 'backend-db-sql' : options.module ? 'module' : 'db-sql';\r\n const srcDir = path.join(__dirname, `../templates/${templateType}`);\r\n\r\n let destDir;\r\n let dirName;\r\n if (projectName === '.') {\r\n destDir = path.resolve('.');\r\n dirName = path.basename(destDir);\r\n if (fs.existsSync(path.join(destDir, 'package.json'))) {\r\n console.error('A package.json already exists in the current directory.');\r\n process.exit(1);\r\n }\r\n } else {\r\n dirName = projectName.toLowerCase().replace(/\\s+/g, '-');\r\n destDir = path.resolve(dirName);\r\n if (fs.existsSync(destDir)) {\r\n console.error(`Directory \"${dirName}\" already exists.`);\r\n process.exit(1);\r\n }\r\n await fs.mkdir(destDir, { recursive: true });\r\n }\r\n\r\n try {\r\n await fs.copy(srcDir, destDir, {\r\n overwrite: false,\r\n errorOnExist: true,\r\n filter: (src, dest) => {\r\n return true;\r\n }\r\n });\r\n console.log('Template files copied successfully');\r\n } catch (err) {\r\n console.error('Error copying the files:', err);\r\n process.exit(1);\r\n }\r\n\r\n let packageJson;\r\n if (templateType === 'db-sql') {\r\n packageJson = {\r\n name: dirName,\r\n version: \"0.0.1\",\r\n description: \"Knex Module\",\r\n main: \"dist/index.js\",\r\n scripts: {\r\n test: \"jest\",\r\n format: \"prettier --write .\",\r\n build: \"tsc\",\r\n clean: \"rimraf dist && npm run format && npm run build\",\r\n \"npm:publish\": \"npm run clean && npm publish\",\r\n \"migrate:create\": \"knex migrate:make migration -x ts\",\r\n \"migrate:deploy\": \"knex migrate:latest\",\r\n \"seed:create\": \"knex seed:make seed -x ts\",\r\n \"seed:run\": \"knex seed:run\"\r\n },\r\n repository: {\r\n type: \"git\",\r\n url: \"\"\r\n },\r\n author: \"\",\r\n license: \"MIT\",\r\n bugs: {\r\n url: \"\"\r\n },\r\n homepage: \"\",\r\n devDependencies: {\r\n \"@eslint/js\": \"^9.23.0\",\r\n \"@types/node\": \"^22.13.13\",\r\n eslint: \"^9.23.0\",\r\n globals: \"^16.0.0\",\r\n prettier: \"^3.5.3\",\r\n rimraf: \"^6.0.1\",\r\n \"ts-node\": \"^10.9.2\",\r\n typescript: \"^5.8.2\",\r\n \"typescript-eslint\": \"^8.28.0\"\r\n },\r\n dependencies: {\r\n dotenv: \"^16.4.7\",\r\n knex: \"^3.1.0\",\r\n lodash: \"^4.17.21\",\r\n pg: \"^8.14.1\",\r\n uuid: \"^11.1.0\"\r\n }\r\n };\r\n } else if (templateType === 'backend') {\r\n packageJson = {\r\n name: dirName,\r\n version: \"1.0.0\",\r\n description: `Backend project generated with Sundays Framework`,\r\n main: \"dist/server.js\",\r\n scripts: {\r\n \"test\": \"jest\",\r\n \"start\": \"node dist/server.js\",\r\n \"build\": \"tsc\",\r\n \"start:dev\": \"nodemon src/server.ts\",\r\n \"format\": \"prettier --write .\",\r\n \"create:controller\": \"sundaysf --create-controller\"\r\n },\r\n dependencies: {\r\n \"morgan\": \"^1.10.0\",\r\n \"cors\": \"^2.8.5\",\r\n \"dotenv\": \"^16.4.7\",\r\n \"express\": \"^4.18.2\"\r\n },\r\n devDependencies: {\r\n \"@types/morgan\": \"^1.9.9\",\r\n \"@types/cors\": \"^2.8.17\",\r\n \"@types/express\": \"^4.17.21\",\r\n \"nodemon\": \"^3.0.2\",\r\n \"prettier\": \"^3.5.3\",\r\n \"ts-node\": \"^10.9.2\",\r\n \"typescript\": \"^5.8.2\",\r\n \"@eslint/js\": \"^9.23.0\",\r\n \"eslint\": \"^9.23.0\",\r\n \"typescript-eslint\": \"^8.28.0\"\r\n },\r\n author: \"\",\r\n license: \"MIT\"\r\n };\r\n } else if (templateType === 'backend-db-sql') {\r\n packageJson = {\r\n name: dirName,\r\n version: \"1.0.0\",\r\n description: `Backend project with database module generated with Sundays Framework`,\r\n main: \"dist/server.js\",\r\n scripts: {\r\n \"test\": \"jest\",\r\n \"start\": \"node dist/server.js\",\r\n \"build\": \"tsc\",\r\n \"start:dev\": \"nodemon src/server.ts\",\r\n \"format\": \"prettier --write .\",\r\n \"create:controller\": \"sundaysf --create-controller\",\r\n \"migrate:create\": \"knex migrate:make migration -x ts --knexfile db/knexfile.ts\",\r\n \"migrate:deploy\": \"knex migrate:latest --knexfile db/knexfile.ts\",\r\n \"seed:create\": \"knex seed:make seed -x ts --knexfile db/knexfile.ts\",\r\n \"seed:run\": \"knex seed:run --knexfile db/knexfile.ts\",\r\n \"db:build\": \"tsc -p db/tsconfig.json\",\r\n \"db:publish\": \"cd db && npm publish\"\r\n },\r\n dependencies: {\r\n \"morgan\": \"^1.10.0\",\r\n \"cors\": \"^2.8.5\",\r\n \"dotenv\": \"^16.4.7\",\r\n \"express\": \"^4.18.2\",\r\n \"knex\": \"^3.1.0\",\r\n \"lodash\": \"^4.17.21\",\r\n \"pg\": \"^8.14.1\",\r\n \"uuid\": \"^11.1.0\"\r\n },\r\n devDependencies: {\r\n \"@types/morgan\": \"^1.9.9\",\r\n \"@types/cors\": \"^2.8.17\",\r\n \"@types/express\": \"^4.17.21\",\r\n \"@types/lodash\": \"^4.17.16\",\r\n \"@types/node\": \"^22.13.13\",\r\n \"nodemon\": \"^3.0.2\",\r\n \"prettier\": \"^3.5.3\",\r\n \"rimraf\": \"^6.0.1\",\r\n \"ts-node\": \"^10.9.2\",\r\n \"typescript\": \"^5.8.2\",\r\n \"@eslint/js\": \"^9.23.0\",\r\n \"eslint\": \"^9.23.0\",\r\n \"globals\": \"^16.0.0\",\r\n \"typescript-eslint\": \"^8.28.0\"\r\n },\r\n author: \"\",\r\n license: \"MIT\"\r\n };\r\n } else {\r\n // module\r\n packageJson = {\r\n name: dirName,\r\n version: \"1.0.0\",\r\n description: `Module generated with Sundays Framework`,\r\n main: \"dist/index.js\",\r\n types: \"dist/index.d.ts\",\r\n scripts: {\r\n \"test\": \"jest\",\r\n \"build\": \"tsc\",\r\n \"format\": \"prettier --write .\",\r\n \"clean\": \"rimraf dist && npm run build\"\r\n },\r\n dependencies: {},\r\n devDependencies: {\r\n \"prettier\": \"^3.5.3\",\r\n \"rimraf\": \"^6.0.1\",\r\n \"typescript\": \"^5.8.2\"\r\n },\r\n author: \"\",\r\n license: \"MIT\"\r\n };\r\n }\r\n\r\n await fs.writeJson(path.join(destDir, 'package.json'), packageJson, { spaces: 2 });\r\n\r\n if (templateType === 'backend' || templateType === 'backend-db-sql') {\r\n const postmanBaseData = generateBasePostman(projectName);\r\n await fs.writeJson(path.join(destDir, 'postman.json'), postmanBaseData, { spaces: 2 });\r\n }\r\n\r\n if (templateType === 'backend-db-sql') {\r\n const dbPackageJson = {\r\n name: `${dirName}-db`,\r\n version: \"0.0.1\",\r\n description: \"Database module\",\r\n main: \"dist/index.js\",\r\n types: \"dist/index.d.ts\",\r\n scripts: {\r\n build: \"tsc\",\r\n clean: \"rimraf dist && npm run build\",\r\n format: \"prettier --write .\",\r\n \"npm:publish\": \"npm run clean && npm publish\",\r\n \"migrate:create\": \"knex migrate:make migration -x ts\",\r\n \"migrate:deploy\": \"knex migrate:latest\",\r\n \"seed:create\": \"knex seed:make seed -x ts\",\r\n \"seed:run\": \"knex seed:run\"\r\n },\r\n dependencies: {\r\n dotenv: \"^16.4.7\",\r\n knex: \"^3.1.0\",\r\n lodash: \"^4.17.21\",\r\n pg: \"^8.14.1\",\r\n uuid: \"^11.1.0\"\r\n },\r\n devDependencies: {\r\n typescript: \"^5.8.2\",\r\n \"ts-node\": \"^10.9.2\",\r\n rimraf: \"^6.0.1\",\r\n prettier: \"^3.5.3\",\r\n \"@types/node\": \"^22.13.13\"\r\n }\r\n };\r\n await fs.writeJson(path.join(destDir, 'db/package.json'), dbPackageJson, { spaces: 2 });\r\n }\r\n\r\n const spinnerGit = ora('Git init...').start();\r\n await initGitRepo(destDir);\r\n spinnerGit.succeed('Git initialized');\r\n\r\n const spinner = ora('Installing dependencies...').start();\r\n await runNpmInstall(destDir);\r\n spinner.succeed('Dependencies installed');\r\n\r\n await runFormatter(destDir);\r\n const outputMsg = projectName === '.' ? `Project created successfully in current directory` : `Project '${projectName}' created successfully in ./${dirName}`;\r\n console.log(outputMsg);\r\n });\r\n} else if (options.createController) {\r\n // Verify we're inside a backend project\r\n if (!fs.existsSync('./src/controllers') || !fs.existsSync('./postman.json')) {\r\n console.error('This command must be run inside a Sundays backend project.');\r\n console.error('Expected ./src/controllers/ and ./postman.json to exist.');\r\n process.exit(1);\r\n }\r\n\r\n inquirer.prompt([\r\n {\r\n type: 'input',\r\n name: 'controllerName',\r\n message: 'Controller name:',\r\n validate: input => {\r\n if (!input) return 'The controller name is empty';\r\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(input)) {\r\n return 'Name must start with a letter and contain only letters and numbers';\r\n }\r\n return true;\r\n }\r\n }\r\n ]).then(async ({ controllerName }) => {\r\n try {\r\n const nonCapitalized = controllerName.charAt(0).toLowerCase() + controllerName.slice(1);\r\n const capitalized = controllerName.charAt(0).toUpperCase() + controllerName.slice(1);\r\n const controllerClassName = `${capitalized}Controller`;\r\n const controllerFileName = `${nonCapitalized}.controller.ts`;\r\n const controllerDirectoryName = `${nonCapitalized}`;\r\n const routerClassName = `${capitalized}Router`;\r\n const routerFileName = `${nonCapitalized}.router.ts`;\r\n const routerDirectoryName = `${nonCapitalized}`;\r\n\r\n const controllerDir = `./src/controllers/${controllerDirectoryName}`;\r\n const routerDir = `./src/routes/${routerDirectoryName}`;\r\n\r\n if (fs.existsSync(controllerDir)) {\r\n console.error(`Controller directory \"${controllerDir}\" already exists.`);\r\n process.exit(1);\r\n }\r\n\r\n const classControllerContentGenerator = generateController(controllerClassName);\r\n const classRouterContentGenerator = generateRouter(routerClassName, controllerClassName, controllerDirectoryName);\r\n const controllerPath = `${controllerDir}/${controllerFileName}`;\r\n const routerPath = `${routerDir}/${routerFileName}`;\r\n\r\n await fs.mkdir(controllerDir, { recursive: true });\r\n await fs.mkdir(routerDir, { recursive: true });\r\n await fs.writeFile(controllerPath, classControllerContentGenerator);\r\n await fs.writeFile(routerPath, classRouterContentGenerator);\r\n\r\n const postmanBaseData = await fs.readFile('./postman.json', 'utf-8');\r\n const leanData = JSON.parse(postmanBaseData);\r\n const itemToPush = getItemFormatted(routerDirectoryName, leanData.info.name);\r\n leanData.item.push(itemToPush);\r\n await fs.writeJson(path.join('.', 'postman.json'), leanData, { spaces: 2 });\r\n\r\n await runFormatter('.');\r\n console.log(`Created ${controllerClassName} in ${controllerDirectoryName}/${controllerFileName} and ${routerClassName} in ${routerDirectoryName}/${routerFileName}`);\r\n } catch (err) {\r\n console.error('Error generating controller:', err.message);\r\n process.exit(1);\r\n }\r\n });\r\n} else {\r\n program.help();\r\n}\r\n\r\n\r\nfunction runFormatter(cwd = '.') {\r\n return new Promise((resolve, reject) => {\r\n const formatProcess = exec('npm run format', { cwd });\r\n\r\n formatProcess.stdout.on('data', data => process.stdout.write(data));\r\n formatProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n formatProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`npm format exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n\r\n\r\nfunction runNpmInstall(destDir) {\r\n return new Promise((resolve, reject) => {\r\n const installProcess = exec('npm install', { cwd: destDir });\r\n\r\n installProcess.stdout.on('data', data => process.stdout.write(data));\r\n installProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n installProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`npm install exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n\r\nfunction initGitRepo(destDir) {\r\n return new Promise((resolve, reject) => {\r\n const gitProcess = exec('git init', { cwd: destDir });\r\n\r\n gitProcess.stdout.on('data', data => process.stdout.write(data));\r\n gitProcess.stderr.on('data', data => process.stderr.write(data));\r\n\r\n gitProcess.on('close', code => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`Git init exited with code ${code}`));\r\n }\r\n });\r\n });\r\n}\r\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knex-table-implementer
|
|
3
|
+
description: Use this agent when you need to create new database table implementations in the Knex project, including migrations, DAOs, interfaces, and exports. This agent should be triggered when: 1) A new database table needs to be added to the system, 2) You need to implement the complete data access layer for a new entity, 3) You want to ensure consistency with the existing project structure and patterns. Examples: <example>Context: User needs to add a new 'product' table to the database. user: "I need to add a product table with id, name, price, and categoryId fields" assistant: "I'll use the knex-table-implementer agent to create the complete implementation for the product table including migration, DAO, interfaces, and exports" <commentary>Since the user needs a new table implementation in the Knex project, use the Task tool to launch the knex-table-implementer agent.</commentary></example> <example>Context: User wants to add a user management system. user: "Create a users table with authentication fields" assistant: "Let me use the knex-table-implementer agent to create the full users table implementation following the project patterns" <commentary>The user is requesting a new table implementation, so the knex-table-implementer agent should be used via the Task tool.</commentary></example>
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: red
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Knex.js database architect specializing in implementing consistent, production-ready database table structures following established project patterns.
|
|
9
|
+
|
|
10
|
+
**Your Core Responsibilities:**
|
|
11
|
+
|
|
12
|
+
You will create complete table implementations in the Knex project by:
|
|
13
|
+
1. Creating database migrations using the project's migration patterns
|
|
14
|
+
2. Implementing DAO classes following the established DAO pattern
|
|
15
|
+
3. Defining TypeScript interfaces for the entities
|
|
16
|
+
4. Ensuring all exports are properly added to index.ts
|
|
17
|
+
|
|
18
|
+
**Implementation Workflow:**
|
|
19
|
+
|
|
20
|
+
1. **Migration Creation**:
|
|
21
|
+
- Inform the user to run `npm run migrate:create` to generate the migration file
|
|
22
|
+
- Write the migration with all database properties in camelCase
|
|
23
|
+
- Include proper up() and down() methods
|
|
24
|
+
- Follow the existing migration patterns in the project
|
|
25
|
+
|
|
26
|
+
2. **DAO Implementation**:
|
|
27
|
+
- Create the DAO file at `src/dao/{entityName}/{entityName}.dao.ts`
|
|
28
|
+
- Extend from IBaseDAO interface
|
|
29
|
+
- Implement standard CRUD operations (getById, getAll with pagination, create, update, delete)
|
|
30
|
+
- Use KnexManager.getInstance() for database connections
|
|
31
|
+
- For related entities, use PostgreSQL's to_jsonb() function for joins
|
|
32
|
+
- Follow the exact pattern from existing DAOs like SundaysPackageVersionDAO
|
|
33
|
+
|
|
34
|
+
3. **Interface Definition**:
|
|
35
|
+
- Create the interface file at `src/interfaces/{entityName}/{entityName}.interfaces.ts`
|
|
36
|
+
- Define the main entity interface with all properties
|
|
37
|
+
- Include any related entity interfaces if needed
|
|
38
|
+
- Ensure TypeScript types are properly defined
|
|
39
|
+
|
|
40
|
+
4. **Export Configuration**:
|
|
41
|
+
- Add the new DAO export to src/index.ts
|
|
42
|
+
- Add the new interface export to src/index.ts
|
|
43
|
+
- Maintain alphabetical ordering in exports when possible
|
|
44
|
+
|
|
45
|
+
**Critical Standards You Must Follow**:
|
|
46
|
+
|
|
47
|
+
- **Naming Conventions**:
|
|
48
|
+
- Database columns: camelCase (e.g., createdAt, userId)
|
|
49
|
+
- Table names: snake_case or lowercase
|
|
50
|
+
- Class names: PascalCase with DAO suffix
|
|
51
|
+
- Interface names: Start with 'I' prefix
|
|
52
|
+
|
|
53
|
+
- **DAO Pattern Requirements**:
|
|
54
|
+
- Always implement IBaseDAO<T> interface
|
|
55
|
+
- Include pagination using IDataPaginator
|
|
56
|
+
- Use async/await for all database operations
|
|
57
|
+
- Return null for not found scenarios
|
|
58
|
+
- Use leftJoin with to_jsonb() for related entities
|
|
59
|
+
|
|
60
|
+
- **Code Structure**:
|
|
61
|
+
- One DAO class per file
|
|
62
|
+
- One interface file per entity
|
|
63
|
+
- Keep related logic together
|
|
64
|
+
- Use the singleton KnexManager for connections
|
|
65
|
+
|
|
66
|
+
**Example Patterns to Follow**:
|
|
67
|
+
|
|
68
|
+
For DAO methods with joins:
|
|
69
|
+
```typescript
|
|
70
|
+
async getById(id: number): Promise<IEntity | null> {
|
|
71
|
+
const result = await this._knex("entity as e")
|
|
72
|
+
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
73
|
+
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
|
|
74
|
+
.where("e.id", id)
|
|
75
|
+
.first();
|
|
76
|
+
return result || null;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For paginated results:
|
|
81
|
+
```typescript
|
|
82
|
+
async getAll(limit: number, offset: number): Promise<IDataPaginator<IEntity>> {
|
|
83
|
+
const query = this._knex("entity");
|
|
84
|
+
const total = await query.clone().count("* as count").first();
|
|
85
|
+
const data = await query.clone().limit(limit).offset(offset).orderBy("id", "desc");
|
|
86
|
+
return {
|
|
87
|
+
data,
|
|
88
|
+
total: parseInt(total?.count as string) || 0,
|
|
89
|
+
limit,
|
|
90
|
+
offset
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Quality Checks**:
|
|
96
|
+
|
|
97
|
+
Before completing any implementation, verify:
|
|
98
|
+
1. Migration file uses camelCase for all properties
|
|
99
|
+
2. DAO follows the exact structure of existing DAOs
|
|
100
|
+
3. Interface properly types all entity properties
|
|
101
|
+
4. All new exports are added to src/index.ts
|
|
102
|
+
5. File paths follow the convention exactly
|
|
103
|
+
6. No unnecessary files are created
|
|
104
|
+
7. Code is consistent with existing patterns
|
|
105
|
+
|
|
106
|
+
**Important Reminders**:
|
|
107
|
+
- Only edit existing files when possible
|
|
108
|
+
- Never create documentation files unless explicitly requested
|
|
109
|
+
- Follow the CLAUDE.md instructions precisely
|
|
110
|
+
- Maintain consistency with the existing codebase structure
|
|
111
|
+
- Always use the established patterns from sundays-package-version as reference
|
|
112
|
+
|
|
113
|
+
When you receive a request, first analyze the entity structure needed, then systematically create each component following the established patterns. If any clarification is needed about field types or relationships, ask before proceeding.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: knex-table-implementer
|
|
3
|
+
description: Use this agent when you need to create new database table implementations in the Knex project, including migrations, DAOs, interfaces, and exports. This agent should be triggered when: 1) A new database table needs to be added to the system, 2) You need to implement the complete data access layer for a new entity, 3) You want to ensure consistency with the existing project structure and patterns. Examples: <example>Context: User needs to add a new 'product' table to the database. user: "I need to add a product table with id, name, price, and categoryId fields" assistant: "I'll use the knex-table-implementer agent to create the complete implementation for the product table including migration, DAO, interfaces, and exports" <commentary>Since the user needs a new table implementation in the Knex project, use the Task tool to launch the knex-table-implementer agent.</commentary></example> <example>Context: User wants to add a user management system. user: "Create a users table with authentication fields" assistant: "Let me use the knex-table-implementer agent to create the full users table implementation following the project patterns" <commentary>The user is requesting a new table implementation, so the knex-table-implementer agent should be used via the Task tool.</commentary></example>
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: red
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert Knex.js database architect specializing in implementing consistent, production-ready database table structures following established project patterns.
|
|
9
|
+
|
|
10
|
+
**Your Core Responsibilities:**
|
|
11
|
+
|
|
12
|
+
You will create complete table implementations in the `db/` module by:
|
|
13
|
+
1. Creating database migrations using the project's migration patterns
|
|
14
|
+
2. Implementing DAO classes following the established DAO pattern
|
|
15
|
+
3. Defining TypeScript interfaces for the entities
|
|
16
|
+
4. Ensuring all exports are properly added to db/src/index.ts
|
|
17
|
+
|
|
18
|
+
**Implementation Workflow:**
|
|
19
|
+
|
|
20
|
+
1. **Migration Creation**:
|
|
21
|
+
- Inform the user to run `npm run migrate:create` to generate the migration file
|
|
22
|
+
- Write the migration with all database properties in camelCase
|
|
23
|
+
- Include proper up() and down() methods
|
|
24
|
+
- Follow the existing migration patterns in the project
|
|
25
|
+
|
|
26
|
+
2. **DAO Implementation**:
|
|
27
|
+
- Create the DAO file at `db/src/dao/{entityName}/{entityName}.dao.ts`
|
|
28
|
+
- Extend from IBaseDAO interface
|
|
29
|
+
- Implement standard CRUD operations (getById, getAll with pagination, create, update, delete)
|
|
30
|
+
- Use KnexManager.getConnection() for database connections
|
|
31
|
+
- For related entities, use PostgreSQL's to_jsonb() function for joins
|
|
32
|
+
- Follow the exact pattern from existing DAOs like SundaysPackageVersionDAO
|
|
33
|
+
|
|
34
|
+
3. **Interface Definition**:
|
|
35
|
+
- Create the interface file at `db/src/interfaces/{entityName}/{entityName}.interfaces.ts`
|
|
36
|
+
- Define the main entity interface with all properties
|
|
37
|
+
- Include any related entity interfaces if needed
|
|
38
|
+
- Ensure TypeScript types are properly defined
|
|
39
|
+
|
|
40
|
+
4. **Export Configuration**:
|
|
41
|
+
- Add the new DAO export to db/src/index.ts
|
|
42
|
+
- Add the new interface export to db/src/index.ts
|
|
43
|
+
- Maintain alphabetical ordering in exports when possible
|
|
44
|
+
|
|
45
|
+
**Critical Standards You Must Follow**:
|
|
46
|
+
|
|
47
|
+
- **Naming Conventions**:
|
|
48
|
+
- Database columns: camelCase (e.g., createdAt, userId)
|
|
49
|
+
- Table names: snake_case or lowercase
|
|
50
|
+
- Class names: PascalCase with DAO suffix
|
|
51
|
+
- Interface names: Start with 'I' prefix
|
|
52
|
+
|
|
53
|
+
- **DAO Pattern Requirements**:
|
|
54
|
+
- Always implement IBaseDAO<T> interface
|
|
55
|
+
- Include pagination using IDataPaginator
|
|
56
|
+
- Use async/await for all database operations
|
|
57
|
+
- Return null for not found scenarios
|
|
58
|
+
- Use leftJoin with to_jsonb() for related entities
|
|
59
|
+
|
|
60
|
+
- **Code Structure**:
|
|
61
|
+
- One DAO class per file
|
|
62
|
+
- One interface file per entity
|
|
63
|
+
- Keep related logic together
|
|
64
|
+
- Use the singleton KnexManager for connections
|
|
65
|
+
|
|
66
|
+
**Example Patterns to Follow**:
|
|
67
|
+
|
|
68
|
+
For DAO methods with joins:
|
|
69
|
+
```typescript
|
|
70
|
+
async getById(id: number): Promise<IEntity | null> {
|
|
71
|
+
const result = await this._knex("entity as e")
|
|
72
|
+
.leftJoin("related as r", "e.relatedId", "r.id")
|
|
73
|
+
.select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
|
|
74
|
+
.where("e.id", id)
|
|
75
|
+
.first();
|
|
76
|
+
return result || null;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For paginated results:
|
|
81
|
+
```typescript
|
|
82
|
+
async getAll(limit: number, offset: number): Promise<IDataPaginator<IEntity>> {
|
|
83
|
+
const query = this._knex("entity");
|
|
84
|
+
const total = await query.clone().count("* as count").first();
|
|
85
|
+
const data = await query.clone().limit(limit).offset(offset).orderBy("id", "desc");
|
|
86
|
+
return {
|
|
87
|
+
data,
|
|
88
|
+
total: parseInt(total?.count as string) || 0,
|
|
89
|
+
limit,
|
|
90
|
+
offset
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Quality Checks**:
|
|
96
|
+
|
|
97
|
+
Before completing any implementation, verify:
|
|
98
|
+
1. Migration file uses camelCase for all properties
|
|
99
|
+
2. DAO follows the exact structure of existing DAOs
|
|
100
|
+
3. Interface properly types all entity properties
|
|
101
|
+
4. All new exports are added to db/src/index.ts
|
|
102
|
+
5. File paths follow the convention exactly (all under `db/`)
|
|
103
|
+
6. No unnecessary files are created
|
|
104
|
+
7. Code is consistent with existing patterns
|
|
105
|
+
|
|
106
|
+
**Important Reminders**:
|
|
107
|
+
- Only edit existing files when possible
|
|
108
|
+
- Never create documentation files unless explicitly requested
|
|
109
|
+
- Follow the CLAUDE.md instructions precisely
|
|
110
|
+
- Maintain consistency with the existing codebase structure
|
|
111
|
+
- Always use the established patterns from sundays-package-version as reference
|
|
112
|
+
- All database files live under the `db/` directory in this project
|
|
113
|
+
|
|
114
|
+
When you receive a request, first analyze the entity structure needed, then systematically create each component following the established patterns. If any clarification is needed about field types or relationships, ask before proceeding.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sundays-backend-builder
|
|
3
|
+
description: Use this agent when you need to create or modify backend components in a Sundays Framework project. This includes creating new controllers, routers, services, implementing CRUD operations, or ensuring existing code follows the framework's strict architectural patterns. <example>Context: User needs to create a new API endpoint for managing products. user: 'I need to create a products API with full CRUD operations' assistant: 'I'll use the sundays-backend-builder agent to create the products API following the Sundays Framework standards' <commentary>Since the user needs to create backend components following Sundays Framework patterns, use the sundays-backend-builder agent to ensure proper structure and implementation.</commentary></example> <example>Context: User wants to add pagination to an existing controller. user: 'Add pagination to the orders controller getAll method' assistant: 'Let me use the sundays-backend-builder agent to implement proper pagination using IBasePaginator' <commentary>The user needs to modify a controller to follow Sundays Framework pagination patterns, so the sundays-backend-builder agent should be used.</commentary></example> <example>Context: User needs to fix a controller that doesn't follow standards. user: 'The customer controller isn't following our standards, can you fix it?' assistant: 'I'll use the sundays-backend-builder agent to refactor the customer controller to match our Sundays Framework standards' <commentary>Since the controller needs to be refactored to follow framework standards, the sundays-backend-builder agent is appropriate.</commentary></example>
|
|
4
|
+
model: sonnet
|
|
5
|
+
color: blue
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an expert backend developer specializing in the Sundays Framework architecture. Your mission is to build and maintain backend components that strictly adhere to the framework's established patterns and conventions.
|
|
9
|
+
|
|
10
|
+
**Core Responsibilities:**
|
|
11
|
+
|
|
12
|
+
1. **Component Creation**: When creating new backend components, ALWAYS use `npm run create:controller` to scaffold the initial structure. This ensures consistency across the codebase.
|
|
13
|
+
|
|
14
|
+
2. **Strict Structure Adherence**: You must follow these exact directory structures without deviation:
|
|
15
|
+
- Routes: `routes/<name>/<name>.router.ts`
|
|
16
|
+
- Controllers: `controllers/<name>/<name>.controller.ts`
|
|
17
|
+
- Services: `services/<name>/<name>.service.ts`
|
|
18
|
+
- DTOs: `dto/input/<entity>/<entity>.create.dto.ts` and `dto/input/<entity>/<entity>.update.dto.ts`
|
|
19
|
+
|
|
20
|
+
3. **Interface Implementation**:
|
|
21
|
+
- ALL controllers MUST implement `IBaseController`
|
|
22
|
+
- ALL paginated getAll methods MUST use `IDataPaginator` (not IBasePaginator)
|
|
23
|
+
- ALWAYS use `paginationHelper` from `@sundaysf/utils` to extract page and limit from request
|
|
24
|
+
|
|
25
|
+
4. **DAO/Service Pattern**:
|
|
26
|
+
- Initialize DAOs as private class members: `private _<entity>DAO: <Entity>DAO = new <Entity>DAO()`
|
|
27
|
+
- Follow the same pattern for services when applicable
|
|
28
|
+
- Use underscore prefix for private members
|
|
29
|
+
|
|
30
|
+
5. **Method Implementation Standards**:
|
|
31
|
+
- **getAll**: Return paginated results using `IDataPaginator`, extract pagination with `paginationHelper(req)`
|
|
32
|
+
- **getByUuid**: Use UUID in params, convert to ID for DAO operations
|
|
33
|
+
- **create**: Validate with DTOs, generate UUID with `uuidv4()` in controller
|
|
34
|
+
- **update**: Get entity by UUID first to find ID, then update using DAO
|
|
35
|
+
- **delete**: Get entity by UUID first to find ID, then delete using DAO
|
|
36
|
+
|
|
37
|
+
6. **Response Format**: ALL responses must follow:
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"success": boolean,
|
|
41
|
+
"data": {...} // or "message": string
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
Note: `IDataPaginator` already includes these fields, so don't double-wrap paginated responses.
|
|
45
|
+
|
|
46
|
+
7. **Router Binding**: ALWAYS use `.bind(this._<entity>Controller)` when assigning controller methods to routes to maintain proper context.
|
|
47
|
+
|
|
48
|
+
8. **Quality Checks**:
|
|
49
|
+
- Verify all imports are correct and from the right packages
|
|
50
|
+
- Ensure TypeScript types are properly defined
|
|
51
|
+
- Check that error handling uses `next(err)` pattern
|
|
52
|
+
- Validate that DTOs properly sanitize input data
|
|
53
|
+
- Confirm UUID generation happens in controller, not DTO or client-side
|
|
54
|
+
|
|
55
|
+
**Working Process**:
|
|
56
|
+
|
|
57
|
+
1. When creating new components, first run `npm run create:controller` command
|
|
58
|
+
2. Analyze existing similar components in the project for pattern reference
|
|
59
|
+
3. Implement following the exact structure found in existing code
|
|
60
|
+
4. Ensure all naming conventions match (camelCase for variables, PascalCase for classes)
|
|
61
|
+
5. Test that all CRUD operations follow the established patterns
|
|
62
|
+
|
|
63
|
+
**Critical Rules**:
|
|
64
|
+
- NEVER deviate from the established folder structure
|
|
65
|
+
- NEVER create custom patterns - follow existing examples exactly
|
|
66
|
+
- ALWAYS check existing implementations before creating new ones
|
|
67
|
+
- ALWAYS maintain consistency with the project's CLAUDE.md guidelines
|
|
68
|
+
- Be extremely meticulous about structure - it must be perfect
|
|
69
|
+
|
|
70
|
+
Your code must be production-ready, following all Sundays Framework conventions to the letter. Every component you create or modify should seamlessly integrate with the existing architecture without requiring any adjustments to other parts of the system.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(cat:*)",
|
|
5
|
+
"Bash(npm run create:controller:*)",
|
|
6
|
+
"Bash(mkdir:*)",
|
|
7
|
+
"Bash(npm install:*)",
|
|
8
|
+
"Bash(npm run build:*)",
|
|
9
|
+
"Bash(npm run format:*)",
|
|
10
|
+
"Bash(npm run migrate:create:*)",
|
|
11
|
+
"Bash(npm run migrate:deploy:*)",
|
|
12
|
+
"Bash(npm run seed:create:*)",
|
|
13
|
+
"Bash(npm run seed:run:*)",
|
|
14
|
+
"Bash(npm run db:build:*)"
|
|
15
|
+
],
|
|
16
|
+
"deny": [],
|
|
17
|
+
"ask": []
|
|
18
|
+
}
|
|
19
|
+
}
|