@forinda/kickjs-cli 5.8.1 → 5.8.2
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/agent-docs-BFAfGQir.mjs +12 -0
- package/dist/agent-docs-BFAfGQir.mjs.map +1 -0
- package/dist/{builtins-CF4q3JIg.mjs → builtins-cz42mDMv.mjs} +2 -2
- package/dist/cli.mjs +63 -312
- package/dist/{config-v6vsVMtc.mjs → config-BlM3L0kV.mjs} +3 -3
- package/dist/{config-v6vsVMtc.mjs.map → config-BlM3L0kV.mjs.map} +1 -1
- package/dist/generator-extension-D8q_Dcqh.mjs +1977 -0
- package/dist/generator-extension-D8q_Dcqh.mjs.map +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-1Oiz9_TD.mjs → plugin-CbMjTNda.mjs} +3 -3
- package/dist/{plugin-1Oiz9_TD.mjs.map → plugin-CbMjTNda.mjs.map} +1 -1
- package/dist/project-docs-DkHcUaf1.mjs +858 -0
- package/dist/project-docs-DkHcUaf1.mjs.map +1 -0
- package/dist/{project-root-BaUzB3D7.mjs → project-root-_5MF5flH.mjs} +3 -3
- package/dist/{project-root-BaUzB3D7.mjs.map → project-root-_5MF5flH.mjs.map} +1 -1
- package/dist/{rolldown-runtime-vNCSDkuE.mjs → rolldown-runtime-BQW-90zC.mjs} +1 -1
- package/dist/{run-plugins-Bg8kTYfa.mjs → run-plugins-D_jHPl16.mjs} +99 -99
- package/dist/run-plugins-D_jHPl16.mjs.map +1 -0
- package/dist/{typegen-B8lAhWXz.mjs → typegen-CGrhe0Ta.mjs} +4 -4
- package/dist/{typegen-B8lAhWXz.mjs.map → typegen-CGrhe0Ta.mjs.map} +1 -1
- package/dist/{types-BG2NeDgK.mjs → types-g7lTGmOR.mjs} +2 -2
- package/dist/{types-BG2NeDgK.mjs.map → types-g7lTGmOR.mjs.map} +1 -1
- package/package.json +5 -5
- package/dist/generator-extension-ChoCJW23.mjs +0 -3072
- package/dist/generator-extension-ChoCJW23.mjs.map +0 -1
- package/dist/run-plugins-Bg8kTYfa.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v5.8.
|
|
2
|
+
* @forinda/kickjs-cli v5.8.2
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{createRequire as e}from"node:module";import{Command as t}from"commander";import{cpSync as n,existsSync as r,mkdirSync as i,readFileSync as a,readdirSync as o,rmSync as s,statSync as c,writeFileSync as l}from"node:fs";import u,{basename as d,dirname as f,extname as p,isAbsolute as m,join as h,parse as g,relative as _,resolve as v,sep as y}from"node:path";import{fileURLToPath as b,pathToFileURL as x}from"node:url";import{execFileSync as ee,execSync as S,fork as te,spawn as ne,spawnSync as re}from"node:child_process";import{access as ie,copyFile as ae,mkdir as oe,readFile as C,readdir as se,rm as ce,stat as le,unlink as ue,writeFile as w}from"node:fs/promises";import*as T from"@clack/prompts";import E from"picocolors";import de from"pluralize";import{glob as fe,globSync as pe}from"glob";import{groupAssetKeys as me}from"@forinda/kickjs";import{arch as he,platform as ge,release as _e}from"node:os";import{detectCompositeReferences as ve,generate as ye,migrateDown as be,migrateLatest as xe,migrateRollback as Se,migrateStatus as Ce,migrateUp as we,renderSchemaSource as Te,resolveDbConfig as Ee}from"@forinda/kickjs-db";var De=Object.defineProperty,D=(e,t)=>{let n={};for(var r in e)De(n,r,{get:e[r],enumerable:!0});return t||De(n,Symbol.toStringTag,{value:`Module`}),n};function Oe(e,t,n){S(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function ke(e,t,n){let r=re(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}function Ae(e,t){if(!t?.commands?.length)return;let n=new Set(e.commands.map(e=>e.name()));for(let r of t.commands){if(n.has(r.name)){console.warn(` Warning: custom command '${r.name}' skipped — conflicts with a built-in command`);continue}je(e,r)}}function je(e,t){let n=e.command(t.name).description(t.description);if(t.aliases)for(let e of t.aliases)n.alias(e);n.allowUnknownOption(!0),n.argument(`[args...]`,`Additional arguments passed to the command`),n.action(e=>{let n=e.join(` `),r=Array.isArray(t.steps)?t.steps:[t.steps];for(let e of r){let r=n?`${e} ${n}`:e;console.log(` $ ${r}`);try{Oe(r)}catch{console.error(` Command failed: ${t.name}`),process.exitCode=1;return}}})}var Me=D({BUILTIN_REPO_TYPES:()=>Pe,PACKAGE_MANAGERS:()=>Ne,defineConfig:()=>Fe,loadKickConfig:()=>k,resolveModuleConfig:()=>O,resolveTokenScope:()=>Ie,validateAssetMap:()=>ze});const Ne=[`pnpm`,`npm`,`yarn`,`bun`],Pe=[`drizzle`,`inmemory`,`prisma`];function Fe(e){return e}function Ie(e,t){if(e?.tokenScope&&typeof e.tokenScope==`string`&&e.tokenScope.length>0){let t=Le(e.tokenScope);if(t.length>0)return t}try{let e=h(t,`package.json`);if(r(e)){let t=JSON.parse(a(e,`utf-8`));if(typeof t.name==`string`&&t.name.length>0){let e=t.name.match(/^@([^/]+)\//),n=Le(e?e[1]:t.name);if(n.length>0)return n}}}catch{}return`app`}function Le(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,`-`).replace(/^-+|-+$/g,``).replace(/-{2,}/g,`-`)}function O(e){if(!e)return{};let t={dir:e.modules?.dir,repo:e.modules?.repo,schemaDir:e.modules?.schemaDir,pluralize:e.modules?.pluralize,prismaClientPath:e.modules?.prismaClientPath,style:e.modules?.style};return t.style!==void 0&&t.style!==`define`&&t.style!==`class`&&(console.warn(` Warning: modules.style '${t.style}' is not a valid value (expected 'define' or 'class'). Falling back to 'define'.`),t.style=`define`),t.repo&&typeof t.repo==`string`&&!Pe.includes(t.repo)&&console.warn(` Warning: modules.repo '${t.repo}' is not a built-in type (${Pe.join(`, `)}). It will generate a stub repository. Use { name: '${t.repo}' } to silence this warning.`),t}const Re=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];async function k(e){let{findProjectRoot:t}=await Promise.resolve().then(()=>
|
|
11
|
+
import{createRequire as e}from"node:module";import{Command as t}from"commander";import{cpSync as n,existsSync as r,mkdirSync as i,readFileSync as a,readdirSync as o,rmSync as s,statSync as c,writeFileSync as l}from"node:fs";import u,{basename as d,dirname as f,extname as p,isAbsolute as m,join as h,parse as g,relative as _,resolve as v,sep as y}from"node:path";import{fileURLToPath as b,pathToFileURL as x}from"node:url";import{execFileSync as ee,execSync as S,fork as te,spawn as ne,spawnSync as re}from"node:child_process";import{access as ie,copyFile as ae,mkdir as oe,readFile as C,readdir as se,rm as ce,stat as le,unlink as ue,writeFile as w}from"node:fs/promises";import*as T from"@clack/prompts";import E from"picocolors";import de from"pluralize";import{glob as fe,globSync as pe}from"glob";import{groupAssetKeys as me}from"@forinda/kickjs";import{arch as he,platform as ge,release as _e}from"node:os";import{detectCompositeReferences as ve,generate as ye,migrateDown as be,migrateLatest as xe,migrateRollback as Se,migrateStatus as Ce,migrateUp as we,renderSchemaSource as Te,resolveDbConfig as Ee}from"@forinda/kickjs-db";var De=Object.defineProperty,D=(e,t)=>{let n={};for(var r in e)De(n,r,{get:e[r],enumerable:!0});return t||De(n,Symbol.toStringTag,{value:`Module`}),n};function Oe(e,t,n){S(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function ke(e,t,n){let r=re(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}function Ae(e,t){if(!t?.commands?.length)return;let n=new Set(e.commands.map(e=>e.name()));for(let r of t.commands){if(n.has(r.name)){console.warn(` Warning: custom command '${r.name}' skipped — conflicts with a built-in command`);continue}je(e,r)}}function je(e,t){let n=e.command(t.name).description(t.description);if(t.aliases)for(let e of t.aliases)n.alias(e);n.allowUnknownOption(!0),n.argument(`[args...]`,`Additional arguments passed to the command`),n.action(e=>{let n=e.join(` `),r=Array.isArray(t.steps)?t.steps:[t.steps];for(let e of r){let r=n?`${e} ${n}`:e;console.log(` $ ${r}`);try{Oe(r)}catch{console.error(` Command failed: ${t.name}`),process.exitCode=1;return}}})}var Me=D({BUILTIN_REPO_TYPES:()=>Pe,PACKAGE_MANAGERS:()=>Ne,defineConfig:()=>Fe,loadKickConfig:()=>k,resolveModuleConfig:()=>O,resolveTokenScope:()=>Ie,validateAssetMap:()=>ze});const Ne=[`pnpm`,`npm`,`yarn`,`bun`],Pe=[`drizzle`,`inmemory`,`prisma`];function Fe(e){return e}function Ie(e,t){if(e?.tokenScope&&typeof e.tokenScope==`string`&&e.tokenScope.length>0){let t=Le(e.tokenScope);if(t.length>0)return t}try{let e=h(t,`package.json`);if(r(e)){let t=JSON.parse(a(e,`utf-8`));if(typeof t.name==`string`&&t.name.length>0){let e=t.name.match(/^@([^/]+)\//),n=Le(e?e[1]:t.name);if(n.length>0)return n}}}catch{}return`app`}function Le(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,`-`).replace(/^-+|-+$/g,``).replace(/-{2,}/g,`-`)}function O(e){if(!e)return{};let t={dir:e.modules?.dir,repo:e.modules?.repo,schemaDir:e.modules?.schemaDir,pluralize:e.modules?.pluralize,prismaClientPath:e.modules?.prismaClientPath,style:e.modules?.style};return t.style!==void 0&&t.style!==`define`&&t.style!==`class`&&(console.warn(` Warning: modules.style '${t.style}' is not a valid value (expected 'define' or 'class'). Falling back to 'define'.`),t.style=`define`),t.repo&&typeof t.repo==`string`&&!Pe.includes(t.repo)&&console.warn(` Warning: modules.repo '${t.repo}' is not a built-in type (${Pe.join(`, `)}). It will generate a stub repository. Use { name: '${t.repo}' } to silence this warning.`),t}const Re=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];async function k(e){let{findProjectRoot:t}=await Promise.resolve().then(()=>Xt),n=t(e);for(let e of Re){let t=h(n,e);try{await ie(t)}catch{continue}if(e.endsWith(`.json`)){let e=await C(t,`utf-8`);return JSON.parse(e)}if(e.endsWith(`.ts`)){let r;try{r=await import(`jiti`)}catch(t){let n=t instanceof Error?t.message:String(t);n.includes(`Cannot find package 'jiti'`)||n.includes(`ERR_MODULE_NOT_FOUND`)?console.warn(`Warning: Failed to load ${e} — 'jiti' is required for TypeScript configs. Run \`pnpm add -D jiti\` (or your package manager's equivalent), or rename the file to kick.config.js / kick.config.mjs / kick.config.json.`):console.warn(`Warning: Failed to initialize jiti for ${e}: ${n}`);continue}try{let e=await r.createJiti(n,{interopDefault:!0,fsCache:!1}).import(t,{default:!0}),i=ze(e,n);for(let e of i)console.warn(` Warning: ${e}`);return e}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`Warning: Failed to load ${e}: ${n}`);continue}}try{let{pathToFileURL:e}=await import(`node:url`),r=await import(e(t).href),i=r.default??r,a=ze(i,n);for(let e of a)console.warn(` Warning: ${e}`);return i}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`Warning: Failed to load ${e}: ${n}`);continue}}return null}function ze(e,t){let n=[];if(!e?.assetMap)return n;let i=v(t);for(let[a,o]of Object.entries(e.assetMap)){if(!a||a.includes(`/`)){n.push(`assetMap key '${a}' is invalid — must be a non-empty string without '/'`);continue}if(typeof o?.src!=`string`||o.src.length===0){n.push(`assetMap.${a} is missing a non-empty 'src' field`);continue}r(v(t,o.src))||n.push(`assetMap.${a}.src ('${o.src}') does not exist — typegen + build will fail`),o.dest&&Be(v(t,o.dest),i)&&n.push(`assetMap.${a}.dest ('${o.dest}') resolves outside the project root — refusing to copy`)}return n}function Be(e,t){let n=_(t,e);return n===``?!1:n.startsWith(`..`)||m(n)}function A(e){return e}var Ve=class extends Error{constructor(e,t,n){super(`Two plugins registered the same ${e} '${t}': ${n.join(`, `)}. Plugins must use unique ${e} names.`),this.name=`KickPluginConflictError`}};function He(e,t=[]){let n=new Map;for(let t of e){let e=(n.get(t.name)??0)+1;if(n.set(t.name,e),e===2)throw new Ve(`plugin`,t.name,[t.name,t.name])}let r=new Map,i=[];for(let t of e)for(let e of t.commands??[]){let n=r.get(e.name);if(n)throw new Ve(`command`,e.name,[n,t.name]);r.set(e.name,t.name),i.push(e)}let a=new Set(t.map(e=>e.name)),o=[...i.filter(e=>!a.has(e.name)),...t],s=new Map,c=[];for(let t of e)for(let e of t.typegens??[]){let n=s.get(e.id);if(n)throw new Ve(`typegen`,e.id,[n,t.name]);s.set(e.id,t.name),c.push(e)}let l=new Map,u=[];for(let t of e)for(let e of t.generators??[]){let n=l.get(e.name);if(n)throw new Ve(`generator`,e.name,[n,t.name]);l.set(e.name,t.name),u.push({source:t.name,spec:e})}return{commands:o,typegens:c,generators:u,register:async(t,n)=>{let r;if(n)r={generators:u,...n};else{let{findProjectRoot:e}=await Promise.resolve().then(()=>Xt),t=process.cwd();r={cwd:t,projectRoot:e(t),config:null,log:()=>{},generators:u}}for(let n of e)n.register&&await n.register(t,r)}}}var Ue=D({mergeCliPlugins:()=>He});let We=!1;function j(e){We=e}const Ge=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function M(e,t){We||(await oe(f(e),{recursive:!0}),await w(e,t,`utf-8`),Ge.has(p(e))&&await qe(e,t).catch(()=>{}))}let N;async function Ke(t){if(N!==void 0)return N;try{N=await import(e(h(t,`package.json`)).resolve(`oxfmt`))}catch{N=null}return N}async function qe(e,t){let n=await Ke(process.cwd());if(!n)return;let r=await Ye(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await w(e,i.code,`utf-8`)}const Je=new Map;async function Ye(e){let t=f(e),n=t;if(Je.has(n))return Je.get(n);for(;;){let e=h(t,`.oxfmtrc.json`);if(r(e))try{let t=await C(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,Je.set(n,r),r}catch{return Je.set(n,null),null}let i=f(t);if(i===t)return Je.set(n,null),null;t=i}}async function Xe(e){try{return await ie(e),!0}catch{return!1}}const Ze={swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function Qe(e,t){let n=e[t];if(!n)throw Error(`generatePackageJson: missing resolved version for ${t}. Add it to SIBLING_PACKAGES in generators/project.ts.`);return n}function $e(e,t,n,r=[]){let i={"@forinda/kickjs":Qe(n,`@forinda/kickjs`),dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=Ze[e];t&&!i[t]&&(i[t]=Qe(n,t))}return JSON.stringify({name:e,version:`0.0.0`,type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":Qe(n,`@forinda/kickjs-cli`),"@forinda/kickjs-vite":Qe(n,`@forinda/kickjs-vite`),"@swc/core":`^1.15.21`,"@types/express":`^5.0.6`,"@types/node":`^25.0.0`,"unplugin-swc":`^1.5.9`,vite:`^8.0.3`,vitest:`^4.1.2`,typescript:`^6.0.3`,prettier:`^3.8.1`}},null,2)}function et(){return`import { defineConfig } from 'vite'
|
|
12
12
|
import { resolve } from 'node:path'
|
|
13
13
|
import swc from 'unplugin-swc'
|
|
14
14
|
import { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'
|
|
@@ -1086,256 +1086,7 @@ description: ${e.description}
|
|
|
1086
1086
|
${r}
|
|
1087
1087
|
|
|
1088
1088
|
${e.body}
|
|
1089
|
-
`}))}function bt(e,t,n){return`#
|
|
1090
|
-
|
|
1091
|
-
This file is the agent-facing **skills index** for KickJS work in this
|
|
1092
|
-
repo. Each block below is a short, rigid workflow keyed to a specific
|
|
1093
|
-
trigger ("user wants to add a module", "tests are leaking state", etc.).
|
|
1094
|
-
|
|
1095
|
-
- Reference docs (narrative, exhaustive) → \`AGENTS.md\`.
|
|
1096
|
-
- Tool-specific notes → \`CLAUDE.md\`, \`GEMINI.md\`, etc.
|
|
1097
|
-
- **This file** → step-by-step recipes the agent should *execute*.
|
|
1098
|
-
|
|
1099
|
-
Re-run \`kick g agents -f --only skills\` after framework upgrades to refresh.
|
|
1100
|
-
|
|
1101
|
-
---
|
|
1102
|
-
|
|
1103
|
-
## Skill: add-module
|
|
1104
|
-
|
|
1105
|
-
\`\`\`yaml
|
|
1106
|
-
name: kickjs-add-module
|
|
1107
|
-
description: Use when the user asks to add a new feature module (controller + service + repo + DTOs).
|
|
1108
|
-
\`\`\`
|
|
1109
|
-
|
|
1110
|
-
**Trigger phrases**: "add a users module", "scaffold tasks", "new feature for X".
|
|
1111
|
-
|
|
1112
|
-
**Steps**:
|
|
1113
|
-
1. Run \`kick g module <name>\` (use plural form if the project pluralizes — check \`kick.config.ts\`).
|
|
1114
|
-
2. Verify the new folder under \`src/modules/<name>/\` contains \`<name>.module.ts\` (filename suffix is mandatory for HMR).
|
|
1115
|
-
3. Confirm the module appears in \`src/modules/index.ts\` exports — generator does this automatically; verify if you bypassed it.
|
|
1116
|
-
4. Open \`<name>.dto.ts\` and tighten the Zod schemas to real fields (the generator emits placeholders).
|
|
1117
|
-
5. Run \`${n} run typecheck\` and \`${n} run test\` before claiming done.
|
|
1118
|
-
|
|
1119
|
-
**Red flags** (stop and ask):
|
|
1120
|
-
- File created as \`<name>.ts\` instead of \`<name>.module.ts\` — Vite won't HMR it.
|
|
1121
|
-
- Module not registered in \`src/modules/index.ts\`.
|
|
1122
|
-
- \`@Controller('/path')\` with a path argument — that's a v3 pattern; remove it (mount comes from \`routes().path\`).
|
|
1123
|
-
|
|
1124
|
-
---
|
|
1125
|
-
|
|
1126
|
-
## Skill: add-adapter
|
|
1127
|
-
|
|
1128
|
-
\`\`\`yaml
|
|
1129
|
-
name: kickjs-add-adapter
|
|
1130
|
-
description: Use when wiring a new lifecycle integration (Swagger, DevTools, Auth, custom).
|
|
1131
|
-
\`\`\`
|
|
1132
|
-
|
|
1133
|
-
**Steps**:
|
|
1134
|
-
1. \`kick g adapter <name>\` to scaffold the boilerplate, OR install via \`kick add <package>\` for first-party adapters.
|
|
1135
|
-
2. The generated file uses \`defineAdapter()\` — never \`class implements AppAdapter\`.
|
|
1136
|
-
3. Add the adapter instance to \`src/adapters/index.ts\` (don't inline in \`src/index.ts\`).
|
|
1137
|
-
4. If the adapter contributes to \`ctx.set/get\`, prefer \`AppAdapter.contributors?()\` over a wrapping middleware.
|
|
1138
|
-
5. Verify with \`kick dev\` that the adapter's lifecycle logs fire.
|
|
1139
|
-
|
|
1140
|
-
**Red flags**:
|
|
1141
|
-
- Inlining the adapter list directly in \`src/index.ts\` (entry file should stay thin).
|
|
1142
|
-
- Returning a plain object instead of going through \`defineAdapter()\` — type inference for \`config\` will be wrong.
|
|
1143
|
-
|
|
1144
|
-
---
|
|
1145
|
-
|
|
1146
|
-
## Skill: write-controller-test
|
|
1147
|
-
|
|
1148
|
-
\`\`\`yaml
|
|
1149
|
-
name: kickjs-write-controller-test
|
|
1150
|
-
description: Use when adding a Vitest test that exercises an HTTP route or DI graph.
|
|
1151
|
-
\`\`\`
|
|
1152
|
-
|
|
1153
|
-
**Template** (copy/paste, adjust):
|
|
1154
|
-
|
|
1155
|
-
\`\`\`ts
|
|
1156
|
-
import { describe, it, expect } from 'vitest'
|
|
1157
|
-
import { Container } from '@forinda/kickjs'
|
|
1158
|
-
import { createTestApp } from '@forinda/kickjs-testing'
|
|
1159
|
-
|
|
1160
|
-
describe('UserController', () => {
|
|
1161
|
-
it('returns users', async () => {
|
|
1162
|
-
const container = Container.create() // isolated DI per test
|
|
1163
|
-
const app = await createTestApp([UserModule], { container })
|
|
1164
|
-
const res = await app.get('/users')
|
|
1165
|
-
expect(res.status).toBe(200)
|
|
1166
|
-
})
|
|
1167
|
-
})
|
|
1168
|
-
\`\`\`
|
|
1169
|
-
|
|
1170
|
-
**Red flags**:
|
|
1171
|
-
- \`new Container()\` — wrong; use \`Container.create()\`.
|
|
1172
|
-
- \`Container.getInstance().reset()\` — wrong; same fix.
|
|
1173
|
-
- Sharing a container across \`it()\` blocks — leaks registrations.
|
|
1174
|
-
|
|
1175
|
-
---
|
|
1176
|
-
|
|
1177
|
-
## Skill: env-wiring-check
|
|
1178
|
-
|
|
1179
|
-
\`\`\`yaml
|
|
1180
|
-
name: kickjs-env-wiring-check
|
|
1181
|
-
description: Use when ConfigService.get('SOME_KEY') returns undefined or @Value silently falls back to process.env.
|
|
1182
|
-
\`\`\`
|
|
1183
|
-
|
|
1184
|
-
**Diagnosis**:
|
|
1185
|
-
1. Open \`src/index.ts\`. The **first non-\`reflect-metadata\`** import MUST be \`import './config'\`.
|
|
1186
|
-
2. Open \`src/config/index.ts\`. It MUST call \`loadEnv(envSchema)\` as a top-level side effect.
|
|
1187
|
-
3. The new key MUST be declared in the Zod schema there. \`@Value('NEW_KEY')\` won't work without a schema entry (it'll fall back to raw \`process.env\` and skip Zod coercion silently).
|
|
1188
|
-
|
|
1189
|
-
**Fix**: add the key to the schema; ensure both side-effect imports above are present.
|
|
1190
|
-
|
|
1191
|
-
---
|
|
1192
|
-
|
|
1193
|
-
## Skill: bootstrap-export
|
|
1194
|
-
|
|
1195
|
-
\`\`\`yaml
|
|
1196
|
-
name: kickjs-bootstrap-export
|
|
1197
|
-
description: Use when HMR is silently doing full restarts on every save, or createTestApp can't find the app handle.
|
|
1198
|
-
\`\`\`
|
|
1199
|
-
|
|
1200
|
-
**Check** \`src/index.ts\`'s last line:
|
|
1201
|
-
|
|
1202
|
-
\`\`\`ts
|
|
1203
|
-
// CORRECT
|
|
1204
|
-
export const app = await bootstrap({ ... })
|
|
1205
|
-
|
|
1206
|
-
// WRONG (HMR degrades to full restart, createTestApp loses the handle)
|
|
1207
|
-
await bootstrap({ ... })
|
|
1208
|
-
\`\`\`
|
|
1209
|
-
|
|
1210
|
-
The Vite plugin imports the named \`app\` symbol; testing helpers do too.
|
|
1211
|
-
|
|
1212
|
-
---
|
|
1213
|
-
|
|
1214
|
-
## Skill: thin-entry-file
|
|
1215
|
-
|
|
1216
|
-
\`\`\`yaml
|
|
1217
|
-
name: kickjs-thin-entry-file
|
|
1218
|
-
description: Use when src/index.ts is accumulating module/middleware/plugin/adapter literals.
|
|
1219
|
-
\`\`\`
|
|
1220
|
-
|
|
1221
|
-
**Refactor target**:
|
|
1222
|
-
|
|
1223
|
-
\`\`\`ts
|
|
1224
|
-
// src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
|
|
1225
|
-
export const modules = defineModules().mount(HelloModule()).mount(UsersModule())
|
|
1226
|
-
// OR for class-form projects (\`modules.style: 'class'\`):
|
|
1227
|
-
// export const modules: AppModuleEntry[] = [HelloModule, UsersModule]
|
|
1228
|
-
|
|
1229
|
-
// src/middleware/index.ts
|
|
1230
|
-
export const middleware = [helmet(), cors(), requestId(), ...]
|
|
1231
|
-
|
|
1232
|
-
// src/plugins/index.ts
|
|
1233
|
-
export const plugins = [MetricsPlugin(), ...]
|
|
1234
|
-
|
|
1235
|
-
// src/adapters/index.ts
|
|
1236
|
-
export const adapters = [SwaggerAdapter({ ... }), DevToolsAdapter()]
|
|
1237
|
-
|
|
1238
|
-
// src/index.ts — stays small
|
|
1239
|
-
import 'reflect-metadata'
|
|
1240
|
-
import './config'
|
|
1241
|
-
import { bootstrap } from '@forinda/kickjs'
|
|
1242
|
-
import { modules } from './modules'
|
|
1243
|
-
import { middleware } from './middleware'
|
|
1244
|
-
import { plugins } from './plugins'
|
|
1245
|
-
import { adapters } from './adapters'
|
|
1246
|
-
export const app = await bootstrap({ modules, middleware, plugins, adapters })
|
|
1247
|
-
\`\`\`
|
|
1248
|
-
|
|
1249
|
-
**Red flags**: any \`new SomeAdapter()\` or \`SomePlugin()\` literal inside \`bootstrap({ ... })\` instead of imported from a category folder.
|
|
1250
|
-
|
|
1251
|
-
---
|
|
1252
|
-
|
|
1253
|
-
## Skill: context-contributor
|
|
1254
|
-
|
|
1255
|
-
\`\`\`yaml
|
|
1256
|
-
name: kickjs-context-contributor
|
|
1257
|
-
description: Use when a middleware's only job is to set ctx values consumed elsewhere — replace with defineHttpContextDecorator (HTTP) or defineContextDecorator (transport-agnostic).
|
|
1258
|
-
\`\`\`
|
|
1259
|
-
|
|
1260
|
-
**Pattern** (HTTP — most common):
|
|
1261
|
-
|
|
1262
|
-
\`\`\`ts
|
|
1263
|
-
import { defineHttpContextDecorator, type RequestContext } from '@forinda/kickjs'
|
|
1264
|
-
|
|
1265
|
-
const LoadTenant = defineHttpContextDecorator({
|
|
1266
|
-
key: 'tenant',
|
|
1267
|
-
deps: { repo: TENANT_REPO },
|
|
1268
|
-
resolve: (ctx, { repo }) => repo.findById(ctx.req.headers['x-tenant-id'] as string),
|
|
1269
|
-
})
|
|
1270
|
-
|
|
1271
|
-
const LoadProject = defineHttpContextDecorator({
|
|
1272
|
-
key: 'project',
|
|
1273
|
-
dependsOn: ['tenant'],
|
|
1274
|
-
resolve: (ctx) => projectsRepo.find(ctx.get('tenant')!.id, ctx.params.id),
|
|
1275
|
-
})
|
|
1276
|
-
|
|
1277
|
-
@LoadTenant
|
|
1278
|
-
@LoadProject
|
|
1279
|
-
@Get('/projects/:id')
|
|
1280
|
-
getProject(ctx: RequestContext) { ctx.json(ctx.get('project')) }
|
|
1281
|
-
\`\`\`
|
|
1282
|
-
|
|
1283
|
-
Use \`defineContextDecorator\` (no Http prefix) when authoring a contributor that must run across HTTP, WebSocket, queue, and cron transports — \`Ctx\` defaults to the smaller \`ExecutionContext\` surface (\`get\` / \`set\` / \`requestId\` only, no \`req\`).
|
|
1284
|
-
|
|
1285
|
-
Precedence high → low: **method > class > module > adapter > global**.
|
|
1286
|
-
Cycles or unmet \`dependsOn\` keys throw \`MissingContributorError\` at boot.
|
|
1287
|
-
|
|
1288
|
-
**Critical rules — all stem from the same shared-via-ALS instance model**:
|
|
1289
|
-
- Every per-request stage (middleware → contributors → handler) gets its OWN \`RequestContext\` instance, but they all read/write the SAME \`AsyncLocalStorage\`-backed bag.
|
|
1290
|
-
- **\`resolve\` and \`onError\` must RETURN the value** — the runner writes it via \`ctx.set(key, value)\`. Direct property assignment (\`ctx.tenant = …\`) sticks to one instance only and the handler instance never sees it.
|
|
1291
|
-
- \`ctx.set('tenant', x)\` then \`ctx.get('tenant')\` works across instances. \`ctx.req.headers[...]\` works (the underlying Express request is shared).
|
|
1292
|
-
- Services with no \`ctx\` reference: \`getRequestValue('tenant')\` returns \`MetaValue<'tenant'> | undefined\` (typed via the augmented \`ContextMeta\`). For \`requestId\` use \`getRequestStore()\`.
|
|
1293
|
-
- **No \`setRequestValue\` — writes flow through \`ctx.set\` or a contributor's return value.** Avoids "spooky action at a distance" where any service can pollute the per-request bag.
|
|
1294
|
-
|
|
1295
|
-
**Don't use this for**: response short-circuit, stream mutation, or
|
|
1296
|
-
pre-route-matching work — keep \`@Middleware()\` for those.
|
|
1297
|
-
|
|
1298
|
-
---
|
|
1299
|
-
|
|
1300
|
-
## Skill: refresh-agent-docs
|
|
1301
|
-
|
|
1302
|
-
\`\`\`yaml
|
|
1303
|
-
name: kickjs-refresh-agent-docs
|
|
1304
|
-
description: Use after a KickJS version bump to sync AGENTS.md / CLAUDE.md / kickjs-skills.md with the latest CLI templates.
|
|
1305
|
-
\`\`\`
|
|
1306
|
-
|
|
1307
|
-
**Steps**:
|
|
1308
|
-
1. \`kick g agents -f --only both\` — overwrites \`AGENTS.md\` and \`CLAUDE.md\`.
|
|
1309
|
-
2. \`kick g agents -f --only skills\` — refreshes \`kickjs-skills.md\` (this file).
|
|
1310
|
-
3. Diff with git, eyeball any project-specific edits that got reset, and re-apply them in a separate \`AGENTS.local.md\` or appended section.
|
|
1311
|
-
4. Commit as \`docs(agents): sync from CLI vX.Y\`.
|
|
1312
|
-
|
|
1313
|
-
---
|
|
1314
|
-
|
|
1315
|
-
## Skill: deny-list
|
|
1316
|
-
|
|
1317
|
-
\`\`\`yaml
|
|
1318
|
-
name: kickjs-deny-list
|
|
1319
|
-
description: Patterns to refuse outright when the user asks for them — they break v4 invariants.
|
|
1320
|
-
\`\`\`
|
|
1321
|
-
|
|
1322
|
-
- \`class implements AppAdapter\` → use \`defineAdapter()\`.
|
|
1323
|
-
- \`class implements KickPlugin\` / function returning \`KickPlugin\` → use \`definePlugin()\`.
|
|
1324
|
-
- \`@Controller('/path')\` with a path argument → drop the path; set the mount via \`routes().path\`.
|
|
1325
|
-
- \`new Container()\` or \`Container.getInstance().reset()\` in tests → use \`Container.create()\`.
|
|
1326
|
-
- DI tokens with \`:\` separator (\`'app:db:url'\`) or in PascalCase → use slash-delimited lower-case (\`'app/db/url'\`).
|
|
1327
|
-
- \`bootstrap({ ... })\` without \`export const app = ...\` → always export.
|
|
1328
|
-
- Module file named \`<name>.ts\` (no \`.module\` suffix) → rename to \`<name>.module.ts\`.
|
|
1329
|
-
|
|
1330
|
-
---
|
|
1331
|
-
|
|
1332
|
-
## Learn More
|
|
1333
|
-
|
|
1334
|
-
- [KickJS Docs](https://forinda.github.io/kick-js/)
|
|
1335
|
-
- [Decorators](https://forinda.github.io/kick-js/guide/decorators.html)
|
|
1336
|
-
- [Context Decorators](https://forinda.github.io/kick-js/guide/context-decorators.html)
|
|
1337
|
-
- [Testing](https://forinda.github.io/kick-js/api/testing.html)
|
|
1338
|
-
`}function xt(e,t,n){return`# GEMINI.md — ${e}
|
|
1089
|
+
`}))}function bt(e,t,n){return`# GEMINI.md — ${e}
|
|
1339
1090
|
|
|
1340
1091
|
**Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
|
|
1341
1092
|
reference for this project — every convention, structure, decorator
|
|
@@ -1369,7 +1120,7 @@ without us copy-pasting.
|
|
|
1369
1120
|
\`kick g agents --only gemini -f\` regenerates this file from the
|
|
1370
1121
|
CLI template. Hand-edited content is overwritten — keep customisation
|
|
1371
1122
|
in \`.agents/GEMINI.local.md\`.
|
|
1372
|
-
`}function
|
|
1123
|
+
`}function xt(e,t,n){return`# COPILOT.md — ${e}
|
|
1373
1124
|
|
|
1374
1125
|
**Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
|
|
1375
1126
|
reference for this project — every convention, structure, decorator
|
|
@@ -1402,16 +1153,16 @@ Codex / Cursor / Gemini / Claude Code without copy-pasting.
|
|
|
1402
1153
|
\`kick g agents --only copilot -f\` regenerates this file from the
|
|
1403
1154
|
CLI template. Hand-edited content is overwritten — keep customisation
|
|
1404
1155
|
in \`.agents/COPILOT.local.md\`.
|
|
1405
|
-
`}const
|
|
1156
|
+
`}const St=f(b(import.meta.url)),Ct=JSON.parse(a(h(St,`..`,`package.json`),`utf-8`)),wt=`^${Ct.version}`,Tt=[`@forinda/kickjs`,`@forinda/kickjs-cli`,`@forinda/kickjs-vite`,`@forinda/kickjs-swagger`,`@forinda/kickjs-ws`,`@forinda/kickjs-queue`,`@forinda/kickjs-devtools`,`@forinda/kickjs-testing`];async function Et(){let e=await Promise.all(Tt.map(async e=>{try{let t=ee(`npm`,[`view`,e,`version`],{encoding:`utf-8`,timeout:5e3,stdio:[`ignore`,`pipe`,`ignore`]}).toString().trim();if(t&&/^\d+\.\d+\.\d+/.test(t))return[e,`^${t}`]}catch{}return[e,wt]}));return Object.fromEntries(e)}async function Dt(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[]}=e,s=n,c=e=>console.log(` ${e}`);console.log(`\n Creating KickJS project: ${t}\n`),c(`Resolving package versions...`);let l=await Et();await M(h(s,`package.json`),$e(t,i,l,o)),await M(h(s,`vite.config.ts`),et()),await M(h(s,`tsconfig.json`),tt()),await M(h(s,`.prettierrc`),nt()),await M(h(s,`.editorconfig`),rt()),await M(h(s,`.gitignore`),it()),await M(h(s,`.gitattributes`),at()),await M(h(s,`.env`),ot()),await M(h(s,`.env.example`),st()),await M(h(s,`src/config/index.ts`),dt()),await M(h(s,`src/index.ts`),lt(t,i,Ct.version,o)),await M(h(s,`src/modules/index.ts`),ut()),await M(h(s,`src/modules/hello/hello.service.ts`),ft()),await M(h(s,`src/modules/hello/hello.controller.ts`),pt()),await M(h(s,`src/modules/hello/hello.module.ts`),mt()),await M(h(s,`kick.config.ts`),ht(i,a,r)),await M(h(s,`vitest.config.ts`),ct()),await M(h(s,`README.md`),gt(t,i,r));let{generateAgentDocs:u}=await Promise.resolve().then(()=>_r);if(await u({outDir:s,name:t,pm:r,template:i,only:`all`,force:!0}),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{S(`${r} install`,{cwd:s,stdio:`inherit`}),console.log(`
|
|
1406
1157
|
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await Promise.resolve().then(()=>la);await e({cwd:s,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{S(`git init`,{cwd:s,stdio:`pipe`}),S(`git branch -M main`,{cwd:s,stdio:`pipe`}),S(`git add -A`,{cwd:s,stdio:`pipe`}),S(`git commit -m "chore: initial commit from kick new"`,{cwd:s,stdio:`pipe`}),c(`Git repository initialized`)}catch{c(`Warning: git init failed (git may not be installed)`)}console.log(`
|
|
1407
|
-
Project scaffolded successfully!`),console.log();let
|
|
1158
|
+
Project scaffolded successfully!`),console.log();let d=s!==process.cwd();c(`Next steps:`),d&&c(` cd ${t}`),e.installDeps||c(` ${r} install`);let f={rest:`kick g module user`,ddd:`kick g module user --repo drizzle`,cqrs:`kick g module user --pattern cqrs`,minimal:`# add your routes to src/index.ts`};c(` ${f[i]??f.rest}`),c(` kick dev`),c(``),c(`Commands:`),c(` kick dev Start dev server with Vite HMR`),c(` kick build Production build via Vite`),c(` kick start Run production build`),c(``),c(`Generators:`),c(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),c(` kick g scaffold <n> <f..> CRUD module from field definitions`),c(` kick g controller <name> Standalone controller`),c(` kick g service <name> @Service() class`),c(` kick g middleware <name> Express middleware`),c(` kick g guard <name> Route guard (auth, roles, etc.)`),c(` kick g adapter <name> AppAdapter with lifecycle hooks`),c(` kick g dto <name> Zod DTO schema`),i===`cqrs`&&c(` kick g job <name> Queue job processor`),c(` kick g config Generate kick.config.ts`),c(``),c(`Add packages:`),c(` kick add <pkg> Install a KickJS package + peers`),c(` kick add --list Show all available packages`),c(``),c(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),c(``)}const Ot={GET:E.green,POST:E.cyan,PUT:E.yellow,PATCH:E.magenta,DELETE:E.red};function kt(e){return(Ot[e]??E.dim)(e.padEnd(7))}function At(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return E.red(t);case`WARNING`:return E.yellow(t);case`INFO`:return E.blue(E.dim(t));default:return t}}E.green(`✓`),E.red(`✖`),E.yellow(`⚠`),E.blue(`ℹ`);function jt(e){T.intro(E.bgCyan(E.black(` ${e} `)))}function Mt(e){T.outro(e)}function Nt(e){T.isCancel(e)&&(T.cancel(`Operation cancelled.`),process.exit(0))}async function Pt(e){let t=await T.text(e);return Nt(t),t}async function Ft(e){let t=await T.select(e);return Nt(t),t}async function It(e){let t=await T.multiselect(e);return Nt(t),t}async function P(e){let t=await T.confirm(e);return Nt(t),t}function Lt(){return T.spinner()}const F=T.log,Rt={kickjs:{pkg:`@forinda/kickjs`,peers:[`express`],description:`Unified framework: DI, decorators, routing, middleware`,core:!0},vite:{pkg:`@forinda/kickjs-vite`,peers:[`vite`],description:`Vite plugin: dev server, HMR, module discovery`,dev:!0,core:!0},cli:{pkg:`@forinda/kickjs-cli`,peers:[],description:`CLI tool and code generators`,dev:!0,core:!0},swagger:{pkg:`@forinda/kickjs-swagger`,peers:[],description:`OpenAPI spec + Swagger UI + ReDoc`},db:{pkg:`@forinda/kickjs-db`,peers:[],description:`kick/db core — schema DSL, migrations, KickDbClient, customType`},"db-pg":{pkg:`@forinda/kickjs-db-pg`,peers:[`pg`],description:`kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)`},drizzle:{pkg:`@forinda/kickjs-drizzle`,peers:[`drizzle-orm`],description:`Drizzle ORM adapter + query builder`},prisma:{pkg:`@forinda/kickjs-prisma`,peers:[`@prisma/client`],description:`Prisma adapter + query builder`},ws:{pkg:`@forinda/kickjs-ws`,peers:[`socket.io`],description:`WebSocket with @WsController decorators`},devtools:{pkg:`@forinda/kickjs-devtools`,peers:[],description:`Development dashboard — routes, DI, metrics, health`,dev:!0},queue:{pkg:`@forinda/kickjs-queue`,peers:[],description:`Queue adapter (BullMQ/RabbitMQ/Kafka)`},"queue:bullmq":{pkg:`@forinda/kickjs-queue`,peers:[`bullmq`,`ioredis`],description:`Queue with BullMQ + Redis`},"queue:rabbitmq":{pkg:`@forinda/kickjs-queue`,peers:[`amqplib`],description:`Queue with RabbitMQ`},"queue:kafka":{pkg:`@forinda/kickjs-queue`,peers:[`kafkajs`],description:`Queue with Kafka`},"queue:redis-pubsub":{pkg:`@forinda/kickjs-queue`,peers:[`ioredis`],description:`Lightweight pub/sub via Redis (no persistence)`},mcp:{pkg:`@forinda/kickjs-mcp`,peers:[`@modelcontextprotocol/sdk`],description:`Model Context Protocol server — expose @Controller endpoints as AI tools`},testing:{pkg:`@forinda/kickjs-testing`,peers:[],description:`Test utilities and TestModule builder`,dev:!0}};function zt(e,t=process.cwd()){let n=t;for(;;){if(r(v(n,e)))return n;let t=f(n);if(t===n)return null;n=t}}function Bt(){return zt(`pnpm-lock.yaml`)?`pnpm`:zt(`yarn.lock`)?`yarn`:zt(`bun.lockb`)||zt(`bun.lock`)?`bun`:zt(`package-lock.json`)?`npm`:null}function Vt(){let e=process.cwd();for(;e;){let t=v(e,`package.json`);if(r(t))try{let e=JSON.parse(a(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(Ne.includes(t))return t}}catch{}let n=f(e);if(n===e)return null;e=n}return null}async function Ht(e){if(e&&Ne.includes(e))return{pm:e,source:`flag`};let t=await k(process.cwd());if(t?.packageManager&&Ne.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=Vt();if(n)return{pm:n,source:`package.json`};let r=Bt();return r?{pm:r,source:`lockfile`}:{pm:`npm`,source:`default`}}async function Ut(e){let{pm:t}=await Ht(e);return t}function Wt(e=!1){let t=Object.entries(Rt),n=Math.max(...t.map(([e])=>e.length)),r=t.filter(([,e])=>e.core),i=t.filter(([,e])=>!e.core),a=([e,t])=>{let r=e.padEnd(n+2),i=t.peers.length?` (+ ${t.peers.join(`, `)})`:``;return` ${r} ${t.description}${i}`};console.log(`
|
|
1408
1159
|
Core packages (always installed by \`kick new\`):
|
|
1409
1160
|
`);for(let e of r)console.log(a(e));if(e){console.log(`
|
|
1410
1161
|
Optional packages (add as needed):
|
|
1411
1162
|
`);for(let e of i)console.log(a(e))}else console.log(`\n Plus ${i.length} optional packages (auth, swagger, db, queue, …).`),console.log(" Run `kick add --list --all` for the full catalog.");console.log(`
|
|
1412
|
-
Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function
|
|
1163
|
+
Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function Gt(e){e.command(`list`).alias(`ls`).description(`List KickJS packages (core only; pair with --all for the full catalog)`).option(`--all`,`Include the full optional catalog`).action(e=>{Wt(!!e.all)})}function Kt(e){e.command(`add [packages...]`).description(`Add KickJS packages with their required dependencies`).option(`--pm <manager>`,`Package manager override`).option(`-D, --dev`,`Install as dev dependency`).option(`--list`,`List packages (core only by default; pair with --all)`).option(`--all`,`When listing, include the full optional catalog`).action(async(e,t)=>{if(t.list||e.length===0){Wt(!!t.all);return}let{pm:n,source:r}=await Ht(t.pm);console.log(`\n Using ${n} (resolved from ${r})`);let i=t.dev,a=new Set,o=new Set,s=[];for(let t of e){let e=Rt[t];if(!e){s.push(t);continue}let n=i||e.dev?o:a;n.add(e.pkg);for(let t of e.peers)n.add(t)}if(!(s.length>0&&(console.log(`\n Unknown packages: ${s.join(`, `)}`),console.log(` Run "kick add --list" to see available packages.
|
|
1413
1164
|
`),a.size===0&&o.size===0))){if(a.size>0){let e=Array.from(a),t=`${n} add ${e.join(` `)}`;console.log(`\n Installing ${e.length} dependency(ies):`);for(let t of e)console.log(` + ${t}`);console.log();try{S(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}if(o.size>0){let e=Array.from(o),t=`${n} add -D ${e.join(` `)}`;console.log(`\n Installing ${e.length} dev dependency(ies):`);for(let t of e)console.log(` + ${t} (dev)`);console.log();try{S(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}console.log(` Done!
|
|
1414
|
-
`)}})}const
|
|
1165
|
+
`)}})}const qt=[{value:`swagger`,label:`Swagger`,hint:`OpenAPI docs`},{value:`ws`,label:`WebSocket`,hint:`rooms, heartbeat`},{value:`queue`,label:`Queue`,hint:`BullMQ/RabbitMQ/Kafka`},{value:`devtools`,label:`DevTools`,hint:`debug dashboard`}];function Jt(e){e.command(`new [name]`).alias(`init`).description(`Create a new KickJS project (use "." for current directory)`).option(`-d, --directory <dir>`,`Target directory (defaults to project name)`).option(`--pm <manager>`,`Package manager: pnpm | npm | yarn | bun`).option(`--git`,`Initialize git repository`).option(`--no-git`,`Skip git initialization`).option(`--install`,`Install dependencies after scaffolding`).option(`--no-install`,`Skip dependency installation`).option(`-f, --force`,`Remove existing files without prompting`).option(`-t, --template <type>`,`Project template: rest | ddd | cqrs | minimal`).option(`-r, --repo <type>`,`Default repository: prisma | drizzle | inmemory | custom`).option(`--packages <packages>`,`Comma-separated packages to include (e.g. auth,swagger,ws,queue)`).option(`-y, --yes`,`Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)`).option(`--non-interactive`,`alias for --yes`).action(async(e,t)=>{jt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await Pt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let i;if(e===`.`?(i=v(`.`),e=d(i)):i=v(t.directory||e),r(i)){let r=o(i);if(r.length>0){if(t.force)F.warn(`Clearing existing files in ${i}`);else if(n){F.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),Mt(`Aborted.`);return}else{F.warn(`Directory "${e}" is not empty:`);let t=r.slice(0,5);for(let e of t)F.message(` - ${e}`);if(r.length>5&&F.message(` ... and ${r.length-5} more`),!await P({message:E.red(`Remove all existing files and proceed?`),initialValue:!1})){Mt(`Aborted.`);return}}for(let e of r)s(v(i,e),{recursive:!0,force:!0})}}let a=t.template;a||=n?`minimal`:await Ft({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`ddd`,label:`DDD`,hint:`Domain-Driven Design modules`},{value:`cqrs`,label:`CQRS`,hint:`Commands, Queries, Events + WS/Queue`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let c=t.pm;c||=n?await Ut(void 0):await Ft({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let l=t.repo;l||(n?l=`inmemory`:(l=await Ft({message:`Default repository/ORM`,options:[{value:`prisma`,label:`Prisma`},{value:`drizzle`,label:`Drizzle`},{value:`inmemory`,label:`In-Memory`},{value:`custom`,label:`Custom`,hint:`specify later`}]}),l===`custom`&&(l=await Pt({message:`Custom repository name`,defaultValue:`custom`}))));let u;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();u=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else u=n?[]:await It({message:`Select packages to include`,options:[...qt],required:!1});let f;f=t.git===void 0?n?!0:await P({message:`Initialize git repository?`,initialValue:!0}):t.git;let p;p=t.install===void 0?n?!0:await P({message:`Install dependencies?`,initialValue:!0}):t.install,await Dt({name:e,directory:i,packageManager:c,initGit:f,installDeps:p,template:a,defaultRepo:l,packages:u}),Mt(`Done! Next steps: ${E.cyan(`cd ${e} && ${c} dev`)}`)})}function I(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function L(e){let t=I(e);return t.charAt(0).toLowerCase()+t.slice(1)}function R(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function z(e){return de.plural(e)}function Yt(e){return de.plural(e)}var Xt=D({findProjectRoot:()=>Qt});const Zt=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];function Qt(e=process.cwd()){let t=v(e),{root:n}=g(t),i=null,a=t;for(;;){for(let e of Zt)if(r(v(a,e)))return a;if(i===null&&r(v(a,`package.json`))&&(i=a),a===n)break;let e=f(a);if(e===a)break;a=e}return i??t}function $t(e){return R(e).replace(/-/g,`_`)}function en(e){let t=e.cwd??process.cwd(),n=e.projectRoot??Qt(t),r=e.pluralize??!0,i=I(e.name),a=L(e.name),o=R(e.name),s=$t(e.name),c={name:e.name,pascal:i,camel:a,kebab:o,snake:s,modulesDir:e.modulesDir??`src/modules`,cwd:t,projectRoot:n,args:e.args??[],flags:e.flags??{}};if(r){let e=z(o);c.pluralKebab=e,c.pluralPascal=I(e),c.pluralCamel=L(e)}return c}function tn(e,t){return v(e.cwd,t)}async function nn(e){return import(x(e).href)}const rn=new Map;async function an(e){let t=rn.get(e);if(t)return t;let n=on(e);return rn.set(e,n),n}async function on(t){let n=v(t,`package.json`);if(!r(n))return{generators:[],loaded:[],failed:[]};let i=sn(JSON.parse(await C(n,`utf-8`))),a=e(v(t,`package.json`)),o=[],s=[],c=[];for(let e of i){let t;try{t=a.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await C(t,`utf-8`))}catch(t){c.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let i=n.kickjs.generators,l=v(f(t),i);if(!r(l)){c.push({source:e,reason:`kickjs.generators points to missing file: ${i}`});continue}let u;try{u=await nn(l)}catch(t){c.push({source:e,reason:`failed to import manifest: ${t}`});continue}let d=u.default;if(!Array.isArray(d)){c.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of d){if(!cn(t)){c.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}o.push({source:e,spec:t})}s.push(e)}return{generators:o,loaded:s,failed:c}}function sn(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function cn(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function ln(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return fn(r.spec,r.source,e,n);let i=dn(await an(n),e.generatorName);return i?fn(i.spec,i.source,e,n):null}async function un(e,t=[]){let n=await an(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function dn(e,t){return e.generators.find(e=>e.spec.name===t)}async function fn(e,t,n,r){let i=en({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r,projectRoot:n.projectRoot}),a=await e.files(i),o=[];for(let e of a){let t=tn(i,e.path);await M(t,e.content),o.push(t)}return{files:o,source:t}}function B(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const pn={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function mn(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function hn(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function gn(e){return pn[e]??mn(e)}function _n(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${mn(n)}${e}Repository`,repoFile:i[n]??`${hn(n)}-${t}`}}function vn(e){return e??`define`}function yn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=_n(t,n,i),c=vn(a),l=`/**
|
|
1415
1166
|
* ${t} Module
|
|
1416
1167
|
*
|
|
1417
1168
|
* Self-contained feature module following Domain-Driven Design (DDD).
|
|
@@ -1421,7 +1172,7 @@ in \`.agents/COPILOT.local.md\`.
|
|
|
1421
1172
|
* presentation/ — HTTP controllers (entry points)
|
|
1422
1173
|
* application/ — Use cases (orchestration) and DTOs (validation)
|
|
1423
1174
|
* domain/ — Entities, value objects, repository interfaces, domain services
|
|
1424
|
-
* infrastructure/ — Repository implementations (currently ${
|
|
1175
|
+
* infrastructure/ — Repository implementations (currently ${gn(i)})
|
|
1425
1176
|
*/`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
|
|
1426
1177
|
import { ${o} } from './infrastructure/repositories/${s}.repository'
|
|
1427
1178
|
import { ${t}Controller } from './presentation/${n}.controller'
|
|
@@ -1455,7 +1206,7 @@ export class ${t}Module implements AppModule {
|
|
|
1455
1206
|
/**
|
|
1456
1207
|
* Register module dependencies in the DI container.
|
|
1457
1208
|
* Bind repository interface tokens to their implementations here.
|
|
1458
|
-
* Currently wired to ${
|
|
1209
|
+
* Currently wired to ${gn(i)}. To swap implementations, change the factory target.
|
|
1459
1210
|
*/
|
|
1460
1211
|
register(container: Container): void {
|
|
1461
1212
|
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
@@ -1481,7 +1232,7 @@ export const ${t}Module = defineModule({
|
|
|
1481
1232
|
/**
|
|
1482
1233
|
* Register module dependencies in the DI container.
|
|
1483
1234
|
* Bind repository interface tokens to their implementations here.
|
|
1484
|
-
* Currently wired to ${
|
|
1235
|
+
* Currently wired to ${gn(i)}. To swap implementations, change the factory target.
|
|
1485
1236
|
*/
|
|
1486
1237
|
register(container) {
|
|
1487
1238
|
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
@@ -1498,7 +1249,7 @@ ${d}
|
|
|
1498
1249
|
},
|
|
1499
1250
|
}),
|
|
1500
1251
|
})
|
|
1501
|
-
`}function
|
|
1252
|
+
`}function bn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=_n(t,n,i),c=vn(a),l=`/**
|
|
1502
1253
|
* ${t} Module
|
|
1503
1254
|
*
|
|
1504
1255
|
* REST module with a flat folder structure.
|
|
@@ -1570,7 +1321,7 @@ ${d}
|
|
|
1570
1321
|
},
|
|
1571
1322
|
}),
|
|
1572
1323
|
})
|
|
1573
|
-
`}function
|
|
1324
|
+
`}function xn(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=vn(i),o=` /**
|
|
1574
1325
|
* Declare HTTP routes. Return value shape:
|
|
1575
1326
|
*
|
|
1576
1327
|
* - \`path\` — URL prefix for this route set.
|
|
@@ -1610,7 +1361,7 @@ ${o}
|
|
|
1610
1361
|
},
|
|
1611
1362
|
}),
|
|
1612
1363
|
})
|
|
1613
|
-
`}function
|
|
1364
|
+
`}function Sn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
|
|
1614
1365
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
1615
1366
|
import { Create${t}UseCase } from '../application/use-cases/create-${n}.use-case'
|
|
1616
1367
|
import { Get${t}UseCase } from '../application/use-cases/get-${n}.use-case'
|
|
@@ -1673,7 +1424,7 @@ export class ${t}Controller {
|
|
|
1673
1424
|
ctx.noContent()
|
|
1674
1425
|
}
|
|
1675
1426
|
}
|
|
1676
|
-
`}function
|
|
1427
|
+
`}function Cn(e){let{pascal:t,kebab:n}=e,r=t.charAt(0).toLowerCase()+t.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
|
|
1677
1428
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
1678
1429
|
import { ${t}Service } from './${n}.service'
|
|
1679
1430
|
import { create${t}Schema } from './dtos/create-${n}.dto'
|
|
@@ -1728,14 +1479,14 @@ export class ${t}Controller {
|
|
|
1728
1479
|
ctx.noContent()
|
|
1729
1480
|
}
|
|
1730
1481
|
}
|
|
1731
|
-
`}function
|
|
1482
|
+
`}function wn(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
|
|
1732
1483
|
|
|
1733
1484
|
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
|
|
1734
1485
|
filterable: ['name'],
|
|
1735
1486
|
sortable: ['name', 'createdAt'],
|
|
1736
1487
|
searchable: ['name'],
|
|
1737
1488
|
}
|
|
1738
|
-
`}function
|
|
1489
|
+
`}function Tn(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
1739
1490
|
|
|
1740
1491
|
/**
|
|
1741
1492
|
* Create ${t} DTO — Zod schema for validating POST request bodies.
|
|
@@ -1751,20 +1502,20 @@ export const create${t}Schema = z.object({
|
|
|
1751
1502
|
})
|
|
1752
1503
|
|
|
1753
1504
|
export type Create${t}DTO = z.infer<typeof create${t}Schema>
|
|
1754
|
-
`}function
|
|
1505
|
+
`}function En(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
1755
1506
|
|
|
1756
1507
|
export const update${t}Schema = z.object({
|
|
1757
1508
|
name: z.string().min(1).max(200).optional(),
|
|
1758
1509
|
})
|
|
1759
1510
|
|
|
1760
1511
|
export type Update${t}DTO = z.infer<typeof update${t}Schema>
|
|
1761
|
-
`}function
|
|
1512
|
+
`}function Dn(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
|
|
1762
1513
|
id: string
|
|
1763
1514
|
name: string
|
|
1764
1515
|
createdAt: string
|
|
1765
1516
|
updatedAt: string
|
|
1766
1517
|
}
|
|
1767
|
-
`}function
|
|
1518
|
+
`}function On(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
|
|
1768
1519
|
* Create ${t} Use Case
|
|
1769
1520
|
*
|
|
1770
1521
|
* Application layer — orchestrates a single business operation.
|
|
@@ -1842,7 +1593,7 @@ export class Delete${t}UseCase {
|
|
|
1842
1593
|
await this.repo.delete(id)
|
|
1843
1594
|
}
|
|
1844
1595
|
}
|
|
1845
|
-
`}]}function
|
|
1596
|
+
`}]}function kn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
|
|
1846
1597
|
* ${t} Repository Interface
|
|
1847
1598
|
*
|
|
1848
1599
|
* Defines the contract for data access.
|
|
@@ -1937,7 +1688,7 @@ export class InMemory${t}Repository implements I${t}Repository {
|
|
|
1937
1688
|
this.store.delete(id)
|
|
1938
1689
|
}
|
|
1939
1690
|
}
|
|
1940
|
-
`}function
|
|
1691
|
+
`}function An(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
|
|
1941
1692
|
* ${o} ${t} Repository
|
|
1942
1693
|
*
|
|
1943
1694
|
* Stub implementation for a custom '${r}' repository.
|
|
@@ -2006,7 +1757,7 @@ export class ${o}${t}Repository implements I${t}Repository {
|
|
|
2006
1757
|
this.store.delete(id)
|
|
2007
1758
|
}
|
|
2008
1759
|
}
|
|
2009
|
-
`}function
|
|
1760
|
+
`}function jn(e){let{pascal:t,kebab:n}=e;return`/**
|
|
2010
1761
|
* ${t} Domain Service
|
|
2011
1762
|
*
|
|
2012
1763
|
* Domain layer — contains business rules that don't belong to a single entity.
|
|
@@ -2029,7 +1780,7 @@ export class ${t}DomainService {
|
|
|
2029
1780
|
}
|
|
2030
1781
|
}
|
|
2031
1782
|
}
|
|
2032
|
-
`}function
|
|
1783
|
+
`}function Mn(e){let{pascal:t,kebab:n}=e;return`/**
|
|
2033
1784
|
* ${t} Entity
|
|
2034
1785
|
*
|
|
2035
1786
|
* Domain layer — the core business object.
|
|
@@ -2098,7 +1849,7 @@ export class ${t} {
|
|
|
2098
1849
|
}
|
|
2099
1850
|
}
|
|
2100
1851
|
}
|
|
2101
|
-
`}function
|
|
1852
|
+
`}function Nn(e){let{pascal:t}=e;return`/**
|
|
2102
1853
|
* ${t} ID Value Object
|
|
2103
1854
|
*
|
|
2104
1855
|
* Domain layer — wraps a primitive ID with type safety and validation.
|
|
@@ -2132,7 +1883,7 @@ export class ${t}Id {
|
|
|
2132
1883
|
return this.value === other.value
|
|
2133
1884
|
}
|
|
2134
1885
|
}
|
|
2135
|
-
`}function
|
|
1886
|
+
`}function Pn(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
|
|
2136
1887
|
import { Container } from '@forinda/kickjs'
|
|
2137
1888
|
|
|
2138
1889
|
describe('${t}Controller', () => {
|
|
@@ -2184,7 +1935,7 @@ describe('${t}Controller', () => {
|
|
|
2184
1935
|
})
|
|
2185
1936
|
})
|
|
2186
1937
|
})
|
|
2187
|
-
`}function
|
|
1938
|
+
`}function Fn(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
|
|
2188
1939
|
import { InMemory${t}Repository } from '${i}'
|
|
2189
1940
|
|
|
2190
1941
|
describe('InMemory${t}Repository', () => {
|
|
@@ -2246,7 +1997,7 @@ describe('InMemory${t}Repository', () => {
|
|
|
2246
1997
|
expect(found).toBeNull()
|
|
2247
1998
|
})
|
|
2248
1999
|
})
|
|
2249
|
-
`}function
|
|
2000
|
+
`}function In(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
|
|
2250
2001
|
import type { ParsedQuery } from '@forinda/kickjs'
|
|
2251
2002
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
|
|
2252
2003
|
import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
|
|
@@ -2283,14 +2034,14 @@ export class ${t}Service {
|
|
|
2283
2034
|
await this.repo.delete(id)
|
|
2284
2035
|
}
|
|
2285
2036
|
}
|
|
2286
|
-
`}function
|
|
2037
|
+
`}function Ln(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
|
|
2287
2038
|
|
|
2288
2039
|
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
|
|
2289
2040
|
filterable: ['name'],
|
|
2290
2041
|
sortable: ['name', 'createdAt'],
|
|
2291
2042
|
searchable: ['name'],
|
|
2292
2043
|
}
|
|
2293
|
-
`}function
|
|
2044
|
+
`}function Rn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,o={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},s={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},c=o[i]??o.inmemory,l=s[i]??s.inmemory,u=a??`define`,d=`/**
|
|
2294
2045
|
* ${t} Module — CQRS Pattern
|
|
2295
2046
|
*
|
|
2296
2047
|
* Separates read (queries) and write (commands) operations.
|
|
@@ -2369,7 +2120,7 @@ ${p}
|
|
|
2369
2120
|
},
|
|
2370
2121
|
}),
|
|
2371
2122
|
})
|
|
2372
|
-
`}function
|
|
2123
|
+
`}function zn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
|
|
2373
2124
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
2374
2125
|
import { Create${t}Command } from './commands/create-${n}.command'
|
|
2375
2126
|
import { Update${t}Command } from './commands/update-${n}.command'
|
|
@@ -2432,7 +2183,7 @@ export class ${t}Controller {
|
|
|
2432
2183
|
ctx.noContent()
|
|
2433
2184
|
}
|
|
2434
2185
|
}
|
|
2435
|
-
`}function
|
|
2186
|
+
`}function Bn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2436
2187
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2437
2188
|
import type { Create${t}DTO } from '../dtos/create-${n}.dto'
|
|
2438
2189
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
@@ -2486,7 +2237,7 @@ export class Delete${t}Command {
|
|
|
2486
2237
|
this.events.emit('${n}.deleted', { id })
|
|
2487
2238
|
}
|
|
2488
2239
|
}
|
|
2489
|
-
`}]}function
|
|
2240
|
+
`}]}function Vn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2490
2241
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2491
2242
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2492
2243
|
|
|
@@ -2514,7 +2265,7 @@ export class List${i}Query {
|
|
|
2514
2265
|
return this.repo.findPaginated(parsed)
|
|
2515
2266
|
}
|
|
2516
2267
|
}
|
|
2517
|
-
`}]}function
|
|
2268
|
+
`}]}function Hn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
|
|
2518
2269
|
import { EventEmitter } from 'node:events'
|
|
2519
2270
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2520
2271
|
|
|
@@ -2600,7 +2351,7 @@ export class On${t}ChangeHandler {
|
|
|
2600
2351
|
})
|
|
2601
2352
|
}
|
|
2602
2353
|
}
|
|
2603
|
-
`}]}function
|
|
2354
|
+
`}]}function Un(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
|
|
2604
2355
|
* Drizzle ${t} Repository
|
|
2605
2356
|
*
|
|
2606
2357
|
* Implements the repository interface using Drizzle ORM.
|
|
@@ -2682,7 +2433,7 @@ export class Drizzle${t}Repository implements I${t}Repository {
|
|
|
2682
2433
|
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2683
2434
|
}
|
|
2684
2435
|
}
|
|
2685
|
-
`}function
|
|
2436
|
+
`}function Wn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
|
|
2686
2437
|
// TODO: Import your schema table and reference actual columns for type safety
|
|
2687
2438
|
// import { ${n}s } from '@/db/schema'
|
|
2688
2439
|
|
|
@@ -2700,7 +2451,7 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
|
|
|
2700
2451
|
// ${n}s.name,
|
|
2701
2452
|
],
|
|
2702
2453
|
}
|
|
2703
|
-
`}function
|
|
2454
|
+
`}function Gn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e,a=n.replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
|
|
2704
2455
|
* Prisma ${t} Repository
|
|
2705
2456
|
*
|
|
2706
2457
|
* Implements the repository interface using Prisma Client.
|
|
@@ -2758,7 +2509,7 @@ export class Prisma${t}Repository implements I${t}Repository {
|
|
|
2758
2509
|
await this.prisma.${a}.deleteMany({ where: { id } })
|
|
2759
2510
|
}
|
|
2760
2511
|
}
|
|
2761
|
-
`}async function
|
|
2512
|
+
`}async function Kn(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,xn({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
|
|
2762
2513
|
|
|
2763
2514
|
// \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
|
|
2764
2515
|
// \`kick typegen\` (auto-run on \`kick dev\`).
|
|
@@ -2770,7 +2521,7 @@ export class ${t}Controller {
|
|
|
2770
2521
|
ctx.json({ message: '${t} list' })
|
|
2771
2522
|
}
|
|
2772
2523
|
}
|
|
2773
|
-
`)}async function
|
|
2524
|
+
`)}async function qn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,bn({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,Ln({pascal:t,kebab:n})),await u(`${n}.controller.ts`,Cn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`${n}.service.ts`,In({pascal:t,kebab:n})),await u(`dtos/create-${n}.dto.ts`,Tn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,En({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,Dn({pascal:t,kebab:n})),await u(`${n}.repository.ts`,kn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let d={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},f={inmemory:()=>V({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>Un({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>Gn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},p=d[a]??`${R(a)}-${n}`,m=f[a]??(()=>An({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${p}.repository.ts`,m()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,V({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,Pn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,Fn({pascal:t,kebab:n,plural:r,repoPrefix:`../${d.inmemory??`in-memory-${n}`}.repository`})))}async function Jn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,Rn({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,Ln({pascal:t,kebab:n})),await u(`${n}.controller.ts`,zn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`dtos/create-${n}.dto.ts`,Tn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,En({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,Dn({pascal:t,kebab:n}));let d=Bn({pascal:t,kebab:n});for(let e of d)await u(`commands/${e.file}`,e.content);let f=Vn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await u(`queries/${e.file}`,e.content);let p=Hn({pascal:t,kebab:n});for(let e of p)await u(`events/${e.file}`,e.content);await u(`${n}.repository.ts`,kn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let m={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},h={inmemory:()=>V({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>Un({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>Gn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},g=m[a]??`${R(a)}-${n}`,_=h[a]??(()=>An({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${g}.repository.ts`,_()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,V({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,Pn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,Fn({pascal:t,kebab:n,plural:r,repoPrefix:`../${m.inmemory??`in-memory-${n}`}.repository`})))}async function Yn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,style:u,write:d}=e;await d(`${n}.module.ts`,yn({pascal:t,kebab:n,plural:r,repo:a,style:u})),await d(`constants.ts`,a===`drizzle`?Wn({pascal:t,kebab:n}):wn({pascal:t,kebab:n})),await d(`presentation/${n}.controller.ts`,Sn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await d(`application/dtos/create-${n}.dto.ts`,Tn({pascal:t,kebab:n})),await d(`application/dtos/update-${n}.dto.ts`,En({pascal:t,kebab:n})),await d(`application/dtos/${n}-response.dto.ts`,Dn({pascal:t,kebab:n}));let f=On({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await d(`application/use-cases/${e.file}`,e.content);await d(`domain/repositories/${n}.repository.ts`,kn({pascal:t,kebab:n,tokenScope:l})),await d(`domain/services/${n}-domain.service.ts`,jn({pascal:t,kebab:n}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>V({pascal:t,kebab:n}),drizzle:()=>Un({pascal:t,kebab:n}),prisma:()=>Gn({pascal:t,kebab:n,prismaClientPath:c})},h=p[a]??`${R(a)}-${n}`,g=m[a]??(()=>An({pascal:t,kebab:n,repoType:a}));await d(`infrastructure/repositories/${h}.repository.ts`,g()),o||(await d(`domain/entities/${n}.entity.ts`,Mn({pascal:t,kebab:n})),await d(`domain/value-objects/${n}-id.vo.ts`,Nn({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await d(`infrastructure/repositories/in-memory-${n}.repository.ts`,V({pascal:t,kebab:n})),await d(`__tests__/${n}.controller.test.ts`,Pn({pascal:t,kebab:n,plural:r})),await d(`__tests__/${n}.repository.test.ts`,Fn({pascal:t,kebab:n,plural:r})))}function Xn(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function Zn(e){let{name:t,modulesDir:n,noEntity:r,noTests:i,repo:a=`inmemory`,force:o,dryRun:s}=e,c=e.pluralize!==!1,l=e.pattern??`ddd`;e.minimal&&(l=`minimal`);let u=R(t),d=I(t),f=c?z(u):u,p=c?Yt(d):d,m=h(n,f),g=[],_=o??!1,v={kebab:u,pascal:d,plural:f,pluralPascal:p,moduleDir:m,repo:a,noEntity:r??!1,noTests:i??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,style:e.style??`define`,write:async(e,t)=>{let n=h(m,e);if(s){g.push(n);return}if(!_&&await Xe(n)&&!await P({message:`File exists: ${E.dim(e)}. Overwrite?`,initialValue:!1})){F.warn(`Skipped: ${e}`);return}await M(n,t),g.push(n)},files:g};switch(l){case`minimal`:await Kn(v);break;case`rest`:await qn(v);break;case`cqrs`:await Jn(v);break;default:await Yn(v);break}return s||await Qn(n,d,f,u,v.style),g}async function Qn(e,t,n,r,i=`define`){let a=h(e,`index.ts`),o=await Xe(a),s=`./${n}/${r}.module`,c=i===`class`?`${t}Module`:`${t}Module()`;if(!o){await M(a,i===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
|
|
2774
2525
|
import { ${t}Module } from '${s}'
|
|
2775
2526
|
|
|
2776
2527
|
export const modules: AppModuleEntry[] = [${c}]
|
|
@@ -2781,8 +2532,8 @@ export const modules = defineModules().mount(${c})
|
|
|
2781
2532
|
`);return}let l=await C(a,`utf-8`),u=`import { ${t}Module } from '${s}'`,d=B(s);if(!RegExp(`^import\\s*\\{[^}]*\\b${B(t)}Module\\b[^}]*\\}\\s*from\\s*['"]${d}['"]`,`m`).test(l)){let e=l.lastIndexOf(`import `);if(e!==-1){let t=l.indexOf(`
|
|
2782
2533
|
`,e);l=l.slice(0,t+1)+u+`
|
|
2783
2534
|
`+l.slice(t+1)}else l=u+`
|
|
2784
|
-
`+l}let f=
|
|
2785
|
-
`;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function
|
|
2535
|
+
`+l}let f=er(l);if(f){let e=l.slice(f.rhsStart,f.rhsEnd+1);RegExp(`\\b${B(t)}Module\\b`).test(e)||(l=$n(l,c))}else l=$n(l,c);await w(a,l,`utf-8`)}function $n(e,t){let n=er(e);if(!n)return e;if(n.shape===`array`){let r=e.slice(n.rhsStart+1,n.rhsEnd),i=r.trim(),a;if(!i)a=`[${t}]`;else{let e=i.endsWith(`,`)?``:`,`;a=`[${r.trimEnd()}${e} ${t}]`}return e.slice(0,n.rhsStart)+a+e.slice(n.rhsEnd+1)}return`${e.slice(0,n.chainEnd)}\n .mount(${t})${e.slice(n.chainEnd)}`}function er(e){let t=/export\s+const\s+modules\b[^=]*=/.exec(e);if(!t)return null;let n=t.index+t[0].length;for(;n<e.length&&/\s/.test(e[n]??``);)n++;if(e[n]===`[`){let t=rr(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=tr(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function tr(e,t=0){let n=/defineModules\s*\(/g;n.lastIndex=t;let r=n.exec(e);if(!r)return-1;let i=r.index+r[0].length-1;if(e[i]!==`(`||(i=ir(e,i),i===-1))return-1;for(i++;;){let t=i;for(;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`.`||e.slice(t,t+6)!==`.mount`)break;for(t+=6;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`(`)break;let n=ir(e,t);if(n===-1)break;i=n+1}return i}function nr(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
|
|
2536
|
+
`;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function rr(e,t){if(e[t]!==`[`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=nr(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`[`)n++;else if(i===`]`&&(n--,n===0))return r;r++}return-1}function ir(e,t){if(e[t]!==`(`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=nr(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`(`)n++;else if(i===`)`&&(n--,n===0))return r;r++}return-1}async function ar(e){let{name:t,outDir:n}=e,r=R(t),i=I(t),a=[],o=h(n,`${r}.adapter.ts`);return await M(o,`import {
|
|
2786
2537
|
defineAdapter,
|
|
2787
2538
|
type AdapterContext,
|
|
2788
2539
|
type AdapterMiddleware,
|
|
@@ -2951,7 +2702,7 @@ export const ${i}Adapter = defineAdapter<${i}AdapterConfig>({
|
|
|
2951
2702
|
}
|
|
2952
2703
|
},
|
|
2953
2704
|
})
|
|
2954
|
-
`),a.push(o),a}async function
|
|
2705
|
+
`),a.push(o),a}async function or(e){let{name:t,outDir:n}=e,r=R(t),i=I(t),a=[],o=h(n,`${r}.plugin.ts`);return await M(o,`import {
|
|
2955
2706
|
definePlugin,
|
|
2956
2707
|
type AppAdapter,
|
|
2957
2708
|
type AppModuleEntry,
|
|
@@ -3095,7 +2846,7 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
|
|
|
3095
2846
|
},
|
|
3096
2847
|
}),
|
|
3097
2848
|
})
|
|
3098
|
-
`),a.push(o),a}const
|
|
2849
|
+
`),a.push(o),a}const sr={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},cr={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},lr={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function ur(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,pattern:o=`ddd`,shouldPluralize:s=!0}=e;if(n)return v(n);if(r){let e=o===`ddd`?sr:o===`cqrs`?lr:cr,n=R(r),a=s?z(n):n,c=e[t]??``,l=h(i,a);return v(c?h(l,c):l)}return v(a)}async function dr(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=ur({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=R(t),s=L(t),c=[],l=h(a,`${o}.middleware.ts`);return await M(l,`import type { Request, Response, NextFunction } from 'express'
|
|
3099
2850
|
|
|
3100
2851
|
export interface ${I(t)}Options {
|
|
3101
2852
|
// Add configuration options here. The factory below closes over the
|
|
@@ -3145,7 +2896,7 @@ export function ${s}(options: ${I(t)}Options = {}) {
|
|
|
3145
2896
|
next()
|
|
3146
2897
|
}
|
|
3147
2898
|
}
|
|
3148
|
-
`),c.push(l),c}async function
|
|
2899
|
+
`),c.push(l),c}async function fr(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=ur({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=R(t),s=L(t),c=I(t),l=[],u=h(a,`${o}.guard.ts`);return await M(u,`import { Container, HttpException } from '@forinda/kickjs'
|
|
3149
2900
|
import type { RequestContext } from '@forinda/kickjs'
|
|
3150
2901
|
|
|
3151
2902
|
/**
|
|
@@ -3181,7 +2932,7 @@ export async function ${s}Guard(ctx: RequestContext, next: () => void): Promise<
|
|
|
3181
2932
|
ctx.res.status(401).json({ message: 'Invalid or expired token' })
|
|
3182
2933
|
}
|
|
3183
2934
|
}
|
|
3184
|
-
`),l.push(u),l}async function
|
|
2935
|
+
`),l.push(u),l}async function pr(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=ur({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=R(t),s=I(t),c=[],l=h(a,`${o}.service.ts`);return await M(l,`import { Service } from '@forinda/kickjs'
|
|
3185
2936
|
|
|
3186
2937
|
@Service()
|
|
3187
2938
|
export class ${s}Service {
|
|
@@ -3190,7 +2941,7 @@ export class ${s}Service {
|
|
|
3190
2941
|
// @Inject(MY_REPO) private readonly repo: IMyRepository,
|
|
3191
2942
|
// ) {}
|
|
3192
2943
|
}
|
|
3193
|
-
`),c.push(l),c}async function
|
|
2944
|
+
`),c.push(l),c}async function mr(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=ur({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=R(t),s=I(t),c=[],l=h(a,`${o}.controller.ts`);return await M(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
|
|
3194
2945
|
|
|
3195
2946
|
// \`Ctx<KickRoutes.${s}Controller['<method>']>\` is generated by
|
|
3196
2947
|
// \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
|
|
@@ -3211,7 +2962,7 @@ export class ${s}Controller {
|
|
|
3211
2962
|
ctx.created({ message: '${s} created', data: ctx.body })
|
|
3212
2963
|
}
|
|
3213
2964
|
}
|
|
3214
|
-
`),c.push(l),c}async function
|
|
2965
|
+
`),c.push(l),c}async function hr(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=ur({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=R(t),s=I(t),c=L(t),l=[],u=h(a,`${o}.dto.ts`);return await M(u,`import { z } from 'zod'
|
|
3215
2966
|
|
|
3216
2967
|
export const ${c}Schema = z.object({
|
|
3217
2968
|
// Define your schema fields here
|
|
@@ -3219,7 +2970,7 @@ export const ${c}Schema = z.object({
|
|
|
3219
2970
|
})
|
|
3220
2971
|
|
|
3221
2972
|
export type ${s}DTO = z.infer<typeof ${c}Schema>
|
|
3222
|
-
`),l.push(u),l}async function
|
|
2973
|
+
`),l.push(u),l}async function gr(e){let t=h(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,i=e.defaultRepo??`inmemory`;return r(t)&&!e.force&&!await P({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
|
|
3223
2974
|
Skipped — existing kick.config.ts preserved.`),[]):(await M(t,`import { defineConfig } from '@forinda/kickjs-cli'
|
|
3224
2975
|
|
|
3225
2976
|
export default defineConfig({
|
|
@@ -3257,7 +3008,7 @@ export default defineConfig({
|
|
|
3257
3008
|
},
|
|
3258
3009
|
],
|
|
3259
3010
|
})
|
|
3260
|
-
`),[t])}const vr=`.agents`,yr=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function br(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function xr(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Sr(e,t){if(t)return t;try{let t=(await k(e))?.pattern;if(t&&yr.has(t))return t}catch{}return`ddd`}async function Cr(e){let t=e.only??`all`,n=br(e.outDir,e.name),i=xr(e.outDir,e.pm),a=await Sr(e.outDir,e.template),o=t===`agents`||t===`both`||t===`all`,s=t===`claude`||t===`both`||t===`all`,c=t===`skills`||t===`all`,l=t===`gemini`||t===`all`,u=t===`copilot`||t===`all`,d=[];if(o&&d.push({file:h(e.outDir,vr,`AGENTS.md`),render:()=>vt(n,a,i)}),s&&d.push({file:h(e.outDir,`CLAUDE.md`),render:()=>_t(n,a,i)}),c)for(let t of yt(n,a,i))d.push({file:h(e.outDir,vr,`skills`,t.slug,`SKILL.md`),render:()=>t.content});l&&d.push({file:h(e.outDir,vr,`GEMINI.md`),render:()=>
|
|
3011
|
+
`),[t])}var _r=D({generateAgentDocs:()=>Cr});const vr=`.agents`,yr=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function br(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function xr(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Sr(e,t){if(t)return t;try{let t=(await k(e))?.pattern;if(t&&yr.has(t))return t}catch{}return`ddd`}async function Cr(e){let t=e.only??`all`,n=br(e.outDir,e.name),i=xr(e.outDir,e.pm),a=await Sr(e.outDir,e.template),o=t===`agents`||t===`both`||t===`all`,s=t===`claude`||t===`both`||t===`all`,c=t===`skills`||t===`all`,l=t===`gemini`||t===`all`,u=t===`copilot`||t===`all`,d=[];if(o&&d.push({file:h(e.outDir,vr,`AGENTS.md`),render:()=>vt(n,a,i)}),s&&d.push({file:h(e.outDir,`CLAUDE.md`),render:()=>_t(n,a,i)}),c)for(let t of yt(n,a,i))d.push({file:h(e.outDir,vr,`skills`,t.slug,`SKILL.md`),render:()=>t.content});l&&d.push({file:h(e.outDir,vr,`GEMINI.md`),render:()=>bt(n,a,i)}),u&&d.push({file:h(e.outDir,vr,`COPILOT.md`),render:()=>xt(n,a,i)});let f=[];for(let{file:t,render:n}of d){if(r(t)&&!e.force&&!await P({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await M(t,n()),f.push(t)}return f}function wr(e,t){if(e[t]!==`{`)return-1;let n=1;for(let r=t+1;r<e.length;r++){let t=e[r];if(t===`{`)n++;else if(t===`}`&&(n--,n===0))return r}return-1}function H(e,t){let n=t.exec(e);if(!n)return null;let r=n.index+n[0].length-1,i=wr(e,r);return i===-1?null:e.slice(r+1,i)}function U(e,t,n){let r=` `.repeat(n);return e.split(`
|
|
3261
3012
|
`).map(e=>{if(e.trim()===``)return e;let n=RegExp(`^ {0,${t}}`);return r+e.replace(n,``)}).join(`
|
|
3262
3013
|
`)}function Tr(e){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`Container`&&e!==`type Container`&&e!==`type AppModule`&&e!==`AppModule`&&e!==`type ModuleRoutes`&&e!==`ModuleRoutes`);return n.includes(`defineModule`)||n.push(`defineModule`),`import { ${n.join(`, `)} } from '@forinda/kickjs'`})}function Er(e,t){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,n)=>{let r=n.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`defineModule`);return t.container&&!r.includes(`Container`)&&r.push(`Container`),t.appModule&&!r.some(e=>e===`AppModule`||e===`type AppModule`)&&r.push(`type AppModule`),t.moduleRoutes&&!r.some(e=>e===`ModuleRoutes`||e===`type ModuleRoutes`)&&r.push(`type ModuleRoutes`),t.contributorRegistrations&&!r.some(e=>e===`ContributorRegistrations`||e===`type ContributorRegistrations`)&&r.push(`type ContributorRegistrations`),`import { ${r.join(`, `)} } from '@forinda/kickjs'`})}function Dr(e){if(/\bdefineModule\s*\(/.test(e))return{migrated:null,reason:`already in target form`};let t=[...e.matchAll(/export\s+class\s+(\w+Module)\s+implements\s+AppModule\s*\{/g)];if(t.length===0)return{migrated:null,reason:`no class form detected`};if(t.length>1)return{migrated:null,reason:`multiple module classes in one file — migrate manually`};let n=t[0],r=n[1],i=n.index+n[0].length-1,a=wr(e,i);if(a===-1)return{migrated:null,reason:`unbalanced class braces`};let o=e.slice(i+1,a),s=e.slice(0,n.index),c=e.slice(a+1),l=H(o,/register\s*\(([^)]*)\)\s*:\s*void\s*\{/),u=H(o,/contributors\s*\(\s*\)\s*:\s*ContributorRegistrations\s*\{/),d=H(o,/routes\s*\(\s*\)\s*:\s*[A-Za-z|[\]\s]+\{/);if(!d)return{migrated:null,reason:`routes() method missing or signature unrecognized`};let f=Tr(s),p=``;return l&&(p+=` register(container) {${U(l,4,6)} },\n\n`),u&&(p+=` contributors() {${U(u,4,6)} },\n\n`),p+=` routes() {${U(d,4,6)} },`,{migrated:`${f}${`export const ${r} = defineModule({
|
|
3263
3014
|
name: '${r}',
|
|
@@ -3301,7 +3052,7 @@ export class ${r}Job {
|
|
|
3301
3052
|
// Handle high-priority variant of this job
|
|
3302
3053
|
}
|
|
3303
3054
|
}
|
|
3304
|
-
`),s}const Lr={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function Rr(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=Lr[a];if(!o){let e=[...Object.keys(Lr),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function zr(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`,style:c=`define`}=e,l=e.pluralize!==!1,u=R(t),d=I(t);L(t);let f=l?z(u):u,p=l?
|
|
3055
|
+
`),s}const Lr={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function Rr(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=Lr[a];if(!o){let e=[...Object.keys(Lr),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function zr(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`,style:c=`define`}=e,l=e.pluralize!==!1,u=R(t),d=I(t);L(t);let f=l?z(u):u,p=l?Yt(d):d,m=h(r,f),g=[],_=async(e,t)=>{let n=h(m,e);await M(n,t),g.push(n)};await _(`${u}.module.ts`,qr(d,u,f,c)),await _(`constants.ts`,Ur(d,n)),await _(`presentation/${u}.controller.ts`,Jr(d,u,f,p)),await _(`application/dtos/create-${u}.dto.ts`,Br(d,n)),await _(`application/dtos/update-${u}.dto.ts`,Vr(d,n)),await _(`application/dtos/${u}-response.dto.ts`,Hr(d,n));let v=Zr(d,u,f,p);for(let e of v)await _(`application/use-cases/${e.file}`,e.content);return await _(`domain/repositories/${u}.repository.ts`,Yr(d,u,s)),await _(`domain/services/${u}-domain.service.ts`,Xr(d,u)),o===`inmemory`&&await _(`infrastructure/repositories/in-memory-${u}.repository.ts`,Wr(d,u,n)),i||(await _(`domain/entities/${u}.entity.ts`,Gr(d,u,n)),await _(`domain/value-objects/${u}-id.vo.ts`,Kr(d))),await Qn(r,d,f,u,c),g}function Br(e,t){return`import { z } from 'zod'
|
|
3305
3056
|
|
|
3306
3057
|
export const create${e}Schema = z.object({
|
|
3307
3058
|
${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:``},`}).join(`
|
|
@@ -3794,16 +3545,16 @@ export {}
|
|
|
3794
3545
|
`);else{let t=e instanceof Error?e.message:String(e);console.error(` kick typegen failed: ${t}`)}}}async function pa(e,t,n,r){let i=new Set;for(let e of t)i.add(d(e));for(let e of n)e.outFile&&i.add(d(e.outFile));let a;try{a=await se(e)}catch{return[]}let o=[];for(let t of a){if(i.has(t))continue;let n=v(e,t);try{if(!(await le(n)).isFile())continue;await ue(n),o.push(t)}catch{}}return o.length>0&&!r&&console.log(` kick typegen: swept ${o.length} stale file(s): ${o.join(`, `)}`),o}const ma=[`agents`,`claude`,`skills`,`gemini`,`copilot`,`both`,`all`];function q(e){return e.parent?.opts()?.dryRun??!1}function J(e,t=!1){let n=process.cwd();console.log(`\n ${t?`Would generate`:`Generated`} ${e.length} file${e.length===1?``:`s`}:`);for(let t of e)console.log(` ${t.replace(n+`/`,``)}`);t&&console.log(`
|
|
3795
3546
|
(dry run — no files were written)`),console.log()}async function ha(e){if(!e)try{let e=await k(process.cwd());await K({cwd:process.cwd(),allowDuplicates:!0,silent:!0,schemaValidator:e?.typegen?.schemaValidator??`zod`,envFile:e?.typegen?.envFile,srcDir:e?.typegen?.srcDir,outDir:e?.typegen?.outDir})}catch{}}const ga=[{name:`module <name>`,description:`Full DDD module (controller, DTOs, use-cases, repo)`},{name:`scaffold <name> <fields...>`,description:`CRUD module from field definitions`},{name:`controller <name>`,description:`@Controller() class [-m module]`},{name:`service <name>`,description:`@Service() singleton [-m module]`},{name:`middleware <name>`,description:`Express middleware function [-m module]`},{name:`guard <name>`,description:`Route guard (auth, roles, etc.) [-m module]`},{name:`dto <name>`,description:`Zod DTO schema [-m module]`},{name:`adapter <name>`,description:`AppAdapter with lifecycle hooks (app-level only)`},{name:`test <name>`,description:`Vitest test scaffold [-m module]`},{name:`job <name>`,description:`Queue @Job processor`},{name:`config`,description:`Generate kick.config.ts`},{name:`agents`,description:`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates`}];async function _a(){console.log(`
|
|
3796
3547
|
Built-in generators:
|
|
3797
|
-
`);let e=Math.max(...ga.map(e=>e.name.length));for(let t of ga)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await k(process.cwd()),n=He(t?.plugins??[],t?.commands??[]),r=await
|
|
3548
|
+
`);let e=Math.max(...ga.map(e=>e.name.length));for(let t of ga)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await k(process.cwd()),n=He(t?.plugins??[],t?.commands??[]),r=await un(process.cwd(),n.generators);if(r.generators.length>0){console.log(`
|
|
3798
3549
|
Plugin generators:
|
|
3799
3550
|
`);let e=Math.max(...r.generators.map(e=>`${e.spec.name} <name>`.length));for(let{source:t,spec:n}of r.generators){let r=`${n.name} <name>`;console.log(` kick g ${r.padEnd(e+2)} ${n.description} [${t}]`)}}if(r.failed.length>0){console.log(`
|
|
3800
3551
|
Failed to load:
|
|
3801
|
-
`);for(let{source:e,reason:t}of r.failed)console.log(` ${e} — ${t}`)}console.log()}async function va(e,t,n){let r=await k(process.cwd()),i=O(r),a=t.modulesDir??i.dir??`src/modules`,o=t.repo??
|
|
3802
|
-
Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await
|
|
3803
|
-
Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await
|
|
3804
|
-
Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await
|
|
3805
|
-
Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await
|
|
3806
|
-
Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await
|
|
3552
|
+
`);for(let{source:e,reason:t}of r.failed)console.log(` ${e} — ${t}`)}console.log()}async function va(e,t,n){let r=await k(process.cwd()),i=O(r),a=t.modulesDir??i.dir??`src/modules`,o=t.repo??Xn(i.repo),s=t.pattern??r?.pattern??`ddd`,c=t.pluralize===!1?!1:i.pluralize??!0,l=Ie(r,process.cwd()),u=i.style??`define`;if(!n&&u===`define`){let e=await Fr(v(a),`define`);if(e.length>0){console.error(`\n ${E.red(`Error:`)} ${e.length} module file(s) still use the legacy \`class … implements AppModule\` shape.\n ${E.dim(`Project setting:`)} modules.style: 'define' (default)\n\n ${E.bold(`Files needing migration:`)}`);for(let t of e.slice(0,5))console.error(` - ${t}`);e.length>5&&console.error(` … and ${e.length-5} more`),console.error(`\n ${E.bold(`Pick one:`)}\n 1. Migrate everything to defineModule:\n ${E.dim(`$`)} kick codemod modules --experimental --apply\n 2. Keep the class form — pin it in kick.config.ts:\n ${E.dim(`// kick.config.ts`)}\n ${E.dim(`export default defineConfig({ modules: { style: 'class' } })`)}\n`),process.exit(1)}}let d=[];for(let r of e){let e=await Zn({name:r,modulesDir:v(a),noEntity:t.entity===!1,noTests:t.tests===!1,repo:o,minimal:t.minimal,force:t.force,pattern:s,dryRun:n,pluralize:c,prismaClientPath:i.prismaClientPath,tokenScope:l,style:i.style});d.push(...e)}J(d,n),await ha(n)}function ya(e,t){let n=e.command(`generate [names...]`).alias(`g`).description("Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`").option(`--list`,`List all available generators`).option(`--dry-run`,`Preview files that would be generated without writing them`).option(`--no-entity`,`Skip entity and value object generation (module shortcut)`).option(`--no-tests`,`Skip test file generation (module shortcut)`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,r,i)=>{if(r.list){await _a();return}if(!e||e.length===0){n.help();return}let a=q(i);j(a);let[o,s,...c]=e;if(o){let e=await k(process.cwd()),n=He(e?.plugins??[],e?.commands??[]),i=await ln({generatorName:o,itemName:s??``,args:c,flags:r,cwd:process.cwd(),projectRoot:t?.projectRoot},n.generators);if(i){J(i.files,a);return}}await va(e,r,a)});n.command(`module <names...>`).description(`Generate one or more modules (e.g. kick g module user task project)`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{let r=q(n);j(r),await va(e,{...n.optsWithGlobals(),...t},r)}),n.command(`adapter <name>`).description(`Generate an AppAdapter with lifecycle hooks and middleware support`).option(`-o, --out <dir>`,`Output directory`,`src/adapters`).action(async(e,t,n)=>{let r=q(n);j(r),J(await ar({name:e,outDir:v(t.out)}),r)}),n.command(`plugin <name>`).description(`Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks`).option(`-o, --out <dir>`,`Output directory`,`src/plugins`).action(async(e,t,n)=>{let r=q(n);j(r),J(await or({name:e,outDir:v(t.out)}),r)}),n.command(`middleware <name>`).description(`Generate an Express middleware function
|
|
3553
|
+
Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await dr({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
|
|
3554
|
+
Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await fr({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`service <name>`).description(`Generate a @Service() class
|
|
3555
|
+
Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await pr({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
|
|
3556
|
+
Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await mr({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r),await ha(r)}),n.command(`dto <name>`).description(`Generate a Zod DTO schema
|
|
3557
|
+
Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=await k(process.cwd()),a=O(i),o=a.dir??`src/modules`;J(await hr({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`test <name>`).description(`Generate a Vitest test scaffold
|
|
3807
3558
|
Use -m to scope it to a module: kick g test user-service -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module's __tests__/ folder`).action(async(e,t,n)=>{let r=q(n);j(r);let i=O(await k(process.cwd())),a=i.dir??`src/modules`;J(await Qr({name:e,outDir:t.out,moduleName:t.module,modulesDir:a,pluralize:i.pluralize??!0}),r)}),n.command(`job <name>`).description(`Generate a @Job queue processor with @Process handlers`).option(`-o, --out <dir>`,`Output directory`,`src/jobs`).option(`-q, --queue <name>`,`Queue name (default: <name>-queue)`).action(async(e,t,n)=>{let r=q(n);j(r),J(await Ir({name:e,outDir:v(t.out),queue:t.queue}),r)}),n.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
|
|
3808
3559
|
Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
|
|
3809
3560
|
Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c
|
|
@@ -3813,7 +3564,7 @@ export {}
|
|
|
3813
3564
|
Usage: kick g scaffold <name> <field:type> [field:type...]
|
|
3814
3565
|
Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
|
|
3815
3566
|
Optional: append :optional (shell-safe, no quoting needed)
|
|
3816
|
-
`),process.exit(1));let a=await k(process.cwd()),o=O(a),s=n.modulesDir??o.dir??`src/modules`,c=Rr(t),l=Ie(a,process.cwd()),u=a?.pattern??`ddd`;u!==`ddd`&&(console.error(`\n Error: 'kick g scaffold' currently only supports the DDD pattern.\n Detected project pattern: '${u}'.\n Workarounds:\n - Run 'kick g module ${e}' for the ${u} layout (no fields), then add fields manually.\n - Override the pattern for this scaffold by setting kick.config.ts pattern: 'ddd'.\n`),process.exit(1));let d=await zr({name:e,fields:c,modulesDir:v(s),noEntity:n.entity===!1,noTests:n.tests===!1,pluralize:n.pluralize===!1?!1:o.pluralize??!0,tokenScope:l,style:o.style});console.log(`\n Scaffolded ${e} with ${c.length} field(s):`);for(let e of c)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);J(d,i),await ha(i)}),n.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=q(t);j(n),J(await
|
|
3567
|
+
`),process.exit(1));let a=await k(process.cwd()),o=O(a),s=n.modulesDir??o.dir??`src/modules`,c=Rr(t),l=Ie(a,process.cwd()),u=a?.pattern??`ddd`;u!==`ddd`&&(console.error(`\n Error: 'kick g scaffold' currently only supports the DDD pattern.\n Detected project pattern: '${u}'.\n Workarounds:\n - Run 'kick g module ${e}' for the ${u} layout (no fields), then add fields manually.\n - Override the pattern for this scaffold by setting kick.config.ts pattern: 'ddd'.\n`),process.exit(1));let d=await zr({name:e,fields:c,modulesDir:v(s),noEntity:n.entity===!1,noTests:n.tests===!1,pluralize:n.pluralize===!1?!1:o.pluralize??!0,tokenScope:l,style:o.style});console.log(`\n Scaffolded ${e} with ${c.length} field(s):`);for(let e of c)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);J(d,i),await ha(i)}),n.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=q(t);j(n),J(await gr({outDir:v(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),n.command(`agents`).alias(`agent-docs`).alias(`ai-docs`).description(`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)`).option(`--only <which>`,`Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)`,`all`).option(`--name <name>`,`Project name (defaults to package.json name)`).option(`--pm <pm>`,`Package manager (defaults to package.json packageManager)`).option(`--template <template>`,`Template: rest | ddd | cqrs | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=q(t);j(n);let r=e.only??`all`;if(!ma.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${ma.join(` | `)}`),process.exitCode=1;return}J(await Cr({outDir:v(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)});for(let e of t?.generators??[])ba(n,e,t?.projectRoot)}function ba(e,t,n){let{source:r,spec:i}=t,a=i.args?.[0],o=a?.name??`itemName`,s=a?.required?`<${o}>`:`[${o}]`,c=`${i.name} ${s} [extraArgs...]`,l=e.command(c).description(`${i.description} [${r}]`);for(let e of i.flags??[]){let t=e.takesValue?`--${e.name} <value>`:`--${e.name}`,n=e.alias?`-${e.alias}, ${t}`:t;l.option(n,e.description??``)}l.action(async(e,r,a,o)=>{let s=q(o);j(s);let c=await ln({generatorName:i.name,itemName:e??``,args:r??[],flags:a,cwd:process.cwd(),projectRoot:n},[t]);c&&J(c.files,s)})}async function xa(e){let t=u.resolve(e.cwd,`.kickjs/types`);await oe(t,{recursive:!0});let n=new Map,i=e.scan??Hi,a={cwd:e.cwd,config:e.config,async importTs(e){return await import(x(e).href)},async writeFile(t,n){let r=u.resolve(e.cwd,t);await oe(u.dirname(r),{recursive:!0}),await w(r,n,`utf8`)},getScanResult:e=>{let t=Sa(e),r=n.get(t);return r||(r=i(e),n.set(t,r)),r},log:console},o=[];for(let n of e.plugins){let i=await n.generate(a);if(i===null){o.push({id:n.id,status:`skipped`});continue}let s=n.outExtension??`.d.ts`,c=u.join(t,`${n.id.replace(/\//g,`__`)}${s}`),l=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+i+`
|
|
3817
3568
|
`,d=``;if(r(c)&&(d=await C(c,`utf8`)),d===l){o.push({id:n.id,status:`unchanged`,outFile:c});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${c})`);await w(c,l,`utf8`),o.push({id:n.id,status:`written`,outFile:c})}return o}function Sa(e){let t=(e.extensions??[]).slice().toSorted().join(`,`),n=(e.exclude??[]).slice().toSorted().join(`,`);return[`root=${e.root}`,`cwd=${e.cwd}`,`extensions=${t}`,`exclude=${n}`,`envFile=${e.envFile??``}`].join(`|`)}function Ca(e,t){let n=new Set(t),r=[],i=[],a=new Set;for(let t of e)n.has(t.id)?(i.push(t),a.add(t.id)):r.push(t);return{enabled:r,skipped:i,unknown:[...n].filter(e=>!a.has(e))}}var wa=D({applyDisableFilter:()=>Ca,runAllPluginTypegens:()=>Ta});async function Ta(e){let{enabled:t,skipped:n,unknown:r}=Ca(He([...ls,...e.config?.plugins??[]],e.config?.commands??[]).typegens,e.config?.typegen?.disable??[]);if(!e.silent&&n.length>0)for(let e of n)console.log(` ${e.id}: disabled (typegen.disable)`);if(!e.silent&&r.length>0&&console.warn(` kick typegen: disable list references unknown id(s): ${r.map(e=>`'${e}'`).join(`, `)}. Run \`kick typegen --list\` to see registered ids.`),t.length===0)return[];try{let n=await xa({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check});if(!e.silent)for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}async function Ea(e,t){let{cwd:n,silent:r=!1}=t,a=t.distDir??e?.build?.outDir??`dist`,o=e?.assetMap;if(!o||Object.keys(o).length===0)return null;let s=r?()=>{}:console.log,c=v(n,a);i(c,{recursive:!0});let u=[],d={};for(let[e,t]of Object.entries(o)){let r=await Da(e,t,n,c);u.push(r.entrySummary),Object.assign(d,r.manifestSlice),s(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let f={version:1,entries:d},p=h(c,`.kickjs-assets.json`);return l(p,JSON.stringify(f,null,2)+`
|
|
3818
3569
|
`,`utf-8`),s(` ✓ wrote manifest → ${_(n,p)} (${Object.keys(d).length} entries)`),{manifestPath:p,entries:u,manifest:f}}async function Da(e,t,a,o){let s=v(a,t.src),c=t.dest?v(a,t.dest):h(o,e);if(ka(c,a))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:_(a,c),filesCopied:0},manifestSlice:{}};if(!r(s)||!Aa(s))return{entrySummary:{namespace:e,src:t.src,dest:_(a,c),filesCopied:0},manifestSlice:{}};let l=await fe(t.glob??`**/*`,{cwd:s,nodir:!0,dot:!1,posix:!0});i(c,{recursive:!0});let u={},{pairs:d,collisionGroupsResolved:p}=me(e,[...l].toSorted(),{strategy:t.keys??`auto`});for(let{rel:e,key:t}of d){let r=h(s,e),a=h(c,e);i(f(a),{recursive:!0}),n(r,a),u[t]=Oa(o,a)}return p>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${p} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:t.src,dest:_(a,c),filesCopied:l.length},manifestSlice:u}}function Oa(e,t){return _(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function ka(e,t){let n=_(t,e);return n===``?!1:n.startsWith(`..`)||m(n)}function Aa(e){try{return c(e).isDirectory()}catch{return!1}}function ja(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function Ma(e,t,n={}){t&&(process.env.PORT=t);let r=ja(n.polling),i=process.cwd(),a=await k(i),o=a?.typegen?.schemaValidator??`zod`,s=a?.typegen?.envFile;try{await K({cwd:i,allowDuplicates:!0,schemaValidator:o,envFile:s,srcDir:a?.typegen?.srcDir,outDir:a?.typegen?.outDir,assetMap:a?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}await Ta({cwd:i,config:a});let{createRequire:c}=await import(`node:module`),{createServer:l}=await import(x(c(v(`package.json`)).resolve(`vite`)).href),u=await l({configFile:v(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...r?{watch:{usePolling:!0,interval:100}}:{}}}),d=a?.assetMap?Object.values(a.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>v(i,e)):[],f=e=>d.some(t=>e===t||e.startsWith(`${t}/`)),p=null,m=e=>{if(e.includes(`.kickjs`)||e.endsWith(`.d.ts`))return;let t=/\.(ts|tsx|mts|cts)$/.test(e),n=f(e);!t&&!n||(p&&clearTimeout(p),p=setTimeout(()=>{K({cwd:i,silent:!0,allowDuplicates:!0,schemaValidator:o,envFile:s,srcDir:a?.typegen?.srcDir,outDir:a?.typegen?.outDir,assetMap:a?.assetMap,runPlugins:!1}).catch(()=>{}),Ta({cwd:i,config:a,silent:!0}).catch(()=>{})},100))};u.watcher.on(`add`,m),u.watcher.on(`unlink`,m),u.watcher.on(`change`,m),d.length>0&&u.watcher.add(d),await u.listen(),u.printUrls(),console.log(`
|
|
3819
3570
|
KickJS dev server running (Vite + @forinda/kickjs-vite)
|
|
@@ -3842,7 +3593,7 @@ export {}
|
|
|
3842
3593
|
@forinda/kickjs workspace
|
|
3843
3594
|
@forinda/kickjs-vite workspace
|
|
3844
3595
|
@forinda/kickjs-cli workspace
|
|
3845
|
-
`)})}const{bold:Y,dim:X,green:Fa,red:Ia,yellow:La,blue:Ra}=E;function za(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function Ba(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function Va(e,t){try{return await Ba(`${e}${t}`)}catch{return null}}async function Ha(e){let[t,n,r,i,a]=await Promise.all([Va(e,`/health`),Va(e,`/metrics`),Va(e,`/routes`),Va(e,`/container`),Va(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function Ua(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=X(`─`.repeat(60));if(console.log(),console.log(Y(` KickJS Inspector`)+X(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?Fa(`● healthy`):Ia(`● `+n.status);console.log(` ${Y(`Health:`)} ${e}`)}else console.log(` ${Y(`Health:`)} ${Ia(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?Ia:r.errorRate>0?La:Fa;console.log(` ${Y(`Uptime:`)} ${za(r.uptimeSeconds)}`),console.log(` ${Y(`Requests:`)} ${r.requests}`),console.log(` ${Y(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${X(`(`)}${t(e+`%`)}${X(`)`)}`)}if(a&&console.log(` ${Y(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${Y(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(Y(` Routes`)),console.log(s),console.log(` ${X(`METHOD`)} ${X(`PATH`.padEnd(36))} ${X(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${
|
|
3596
|
+
`)})}const{bold:Y,dim:X,green:Fa,red:Ia,yellow:La,blue:Ra}=E;function za(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function Ba(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function Va(e,t){try{return await Ba(`${e}${t}`)}catch{return null}}async function Ha(e){let[t,n,r,i,a]=await Promise.all([Va(e,`/health`),Va(e,`/metrics`),Va(e,`/routes`),Va(e,`/container`),Va(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function Ua(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=X(`─`.repeat(60));if(console.log(),console.log(Y(` KickJS Inspector`)+X(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?Fa(`● healthy`):Ia(`● `+n.status);console.log(` ${Y(`Health:`)} ${e}`)}else console.log(` ${Y(`Health:`)} ${Ia(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?Ia:r.errorRate>0?La:Fa;console.log(` ${Y(`Uptime:`)} ${za(r.uptimeSeconds)}`),console.log(` ${Y(`Requests:`)} ${r.requests}`),console.log(` ${Y(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${X(`(`)}${t(e+`%`)}${X(`)`)}`)}if(a&&console.log(` ${Y(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${Y(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(Y(` Routes`)),console.log(s),console.log(` ${X(`METHOD`)} ${X(`PATH`.padEnd(36))} ${X(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${kt(e.method)} ${t} ${Ra(e.controller)}.${X(e.handler)}`)}}console.log(s),console.log()}function Wa(e){e.command(`inspect [url]`).description(`Connect to a running KickJS app and display debug info`).option(`-p, --port <port>`,`Override port`).option(`-w, --watch`,`Poll every 5 seconds`).option(`-j, --json`,`Output raw JSON`).action(async(e,t)=>{let n=e??`http://localhost:3000`;if(t.port)try{let e=new URL(n);e.port=t.port,n=e.origin}catch{n=`http://localhost:${t.port}`}let r=`${n.replace(/\/$/,``)}/_debug`,i=async()=>{try{let e=await Ha(r);t.json?console.log(JSON.stringify(e,null,2)):Ua(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(Ia(` ✖ Could not connect to ${n}`)),console.error(X(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function Ga(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function Z(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const Ka=[{match(e,t){let n=Ga(e,[`config`,`get`])&&Z(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&Z(e,[`undefined`,`is not defined`]);return!n&&!r?null:{confidence:n&&r?90:75,diagnosis:{id:`env-schema-not-registered`,title:`ConfigService.get() returns undefined for user-defined keys`,explanation:`Your src/index.ts is missing \`import "./config"\`. That side-effect import
|
|
3846
3597
|
registers the env schema with kickjs at module-load time. Without it,
|
|
3847
3598
|
ConfigService falls back to the base schema (PORT/NODE_ENV/LOG_LEVEL only)
|
|
3848
3599
|
and every user-defined key reads as undefined. @Value() may *appear* to
|
|
@@ -4011,11 +3762,11 @@ server.on('exit', () => {
|
|
|
4011
3762
|
})
|
|
4012
3763
|
`}function vo(e,t){let n=e;for(;;){let e=h(n,`node_modules`,`.bin`,t);if(r(e))return e;let i=v(n,`..`);if(i===n)break;n=i}return null}function yo(e,t){let n=RegExp(`^\\s*${B(t)}Module\\b`),r=!1,i=0,a=e;for(;;){let e=a.indexOf(`.mount(`,i);if(e===-1)break;let t=e+7,o=1,s=t;for(;s<a.length&&o>0;){let e=a.slice(s,s+2);if(e===`//`||e===`/*`){if(e===`//`)for(s+=2;s<a.length&&a[s]!==`
|
|
4013
3764
|
`;)s++;else{for(s+=2;s+1<a.length&&!(a[s]===`*`&&a[s+1]===`/`);)s++;s+=2}continue}let t=a[s]??``;if(t===`'`||t===`"`||t==="`"){let e=t;for(s++;s<a.length&&a[s]!==e;)a[s]===`\\`&&s++,s++}else if(t===`(`)o++;else if(t===`)`&&(o--,o===0))break;s++}if(o!==0)break;let c=a.slice(t,s);if(n.test(c)){let t=e;for(;t>0&&(a[t-1]===` `||a[t-1]===` `||a[t-1]===`
|
|
4014
|
-
`);)t--;a=a.slice(0,t)+a.slice(s+1),r=!0,i=t;continue}i=s+1}return{content:a,changed:r}}function bo(e,t){let n=
|
|
3765
|
+
`);)t--;a=a.slice(0,t)+a.slice(s+1),r=!0,i=t;continue}i=s+1}return{content:a,changed:r}}function bo(e,t){let n=er(e);if(!n)return e;let r=n.rhsStart,i=n.rhsEnd+1,a=e.slice(r,i);return a=yo(a,t).content,a=a.replace(RegExp(`\\s*,?\\s*${B(t)}Module\\b(?:\\s*\\(\\s*\\))?\\s*,?`,`g`),e=>{let t=e.trimStart().startsWith(`,`),n=e.trimEnd().endsWith(`,`);return t&&n?`,`:``}),a=a.replace(/,(\s*])/,`$1`),e.slice(0,r)+a+e.slice(i)}async function xo(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=R(t),o=I(t),s=i?z(a):a,c=h(n,s);if(!await Xe(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await P({message:E.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
|
|
4015
3766
|
Cancelled.
|
|
4016
3767
|
`);return}await ce(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=h(n,`index.ts`);if(await Xe(l)){let e=await C(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${B(o)}Module\\s*\\}\\s*from\\s*['"][^'"]*${B(s)}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=bo(e,o),e=e.replace(/\n{3,}/g,`
|
|
4017
3768
|
|
|
4018
|
-
`),e!==t&&(await w(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function So(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let n=O(await k(process.cwd())),r=t.modulesDir??n.dir??`src/modules`,i=t.pluralize===!1?!1:n.pluralize??!0;for(let n of e)await xo({name:n,modulesDir:v(r),force:t.force,pluralize:i})})}function Co(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function wo(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function To(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t
|
|
3769
|
+
`),e!==t&&(await w(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function So(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let n=O(await k(process.cwd())),r=t.modulesDir??n.dir??`src/modules`,i=t.pluralize===!1?!1:n.pluralize??!0;for(let n of e)await xo({name:n,modulesDir:v(r),force:t.force,pluralize:i})})}function Co(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function wo(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function To(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t=Qt(process.cwd()),n=await k(t);if(e.list){let{mergeCliPlugins:e}=await Promise.resolve().then(()=>Ue),{builtinCliPlugins:t}=await Promise.resolve().then(()=>cs),r=e([...t,...n?.plugins??[]],n?.commands??[]),i=new Set(n?.typegen?.disable??[]);if(r.typegens.length===0){console.log(` No typegen plugins registered.`);return}let a=Math.max(...r.typegens.map(e=>e.id.length));console.log(`
|
|
4019
3770
|
Registered typegen plugins:
|
|
4020
3771
|
`);for(let e of r.typegens){let t=i.has(e.id)?` (disabled)`:``;console.log(` ${e.id.padEnd(a+2)}inputs: ${e.inputs.join(`, `)||`(none)`}${t}`)}console.log();return}let r=Co(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,i=wo(e.envFile)??n?.typegen?.envFile,a={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:r,envFile:i,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await da(a);e.silent||console.log(` kick typegen: watching for changes (Ctrl-C to exit)`);let n=()=>{t(),process.exit(0)};process.on(`SIGINT`,n),process.on(`SIGTERM`,n),await new Promise(()=>{})}else{let{result:r}=await K(a),i=await Ta({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&i.some(e=>e.status===`written`)&&process.exit(1),e.check||await pa(v(t,e.out??n?.typegen?.outDir??`.kickjs/types`),r.written,i,e.silent??!1)}}catch(e){e instanceof Wi?console.error(`
|
|
4021
3772
|
`+e.message+`
|
|
@@ -4024,7 +3775,7 @@ server.on('exit', () => {
|
|
|
4024
3775
|
|
|
4025
3776
|
Available checks:
|
|
4026
3777
|
--deploy Audit for production readiness (security, config, best practices)
|
|
4027
|
-
`);return}let t=process.cwd();
|
|
3778
|
+
`);return}let t=process.cwd();jt(`KickJS Deploy Check`);let n=Lt();n.start(`Scanning project...`);let r=Io(t);n.stop(`Scan complete`);let i={CRITICAL:0,WARNING:1,INFO:2};r.sort((e,t)=>i[e.severity]-i[t.severity]);for(let e of r)F.message(`${At(e.severity)} ${e.message}`);let a=r.filter(e=>e.severity===`CRITICAL`).length,o=r.filter(e=>e.severity===`WARNING`).length,s=r.filter(e=>e.severity===`INFO`).length,c=o===1?`warning`:`warnings`,l=[a>0?E.red(`${a} critical`):`${a} critical`,o>0?E.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(Mt(E.red(`${l} — fix critical issues before deploying`)),process.exit(1)):Mt(E.green(`${l} — looking good!`))})}async function Q(e){return Ee({configPath:u.resolve(process.cwd(),e.config)})}async function $(e){if(e.adapter){let t=await e.adapter();return{adapter:t,cleanup:async()=>t.close()}}if(!e.connectionString)throw Error(`kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory`);let t=e.dialect??`postgres`;if(t!==`postgres`)throw Error(`kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${t}); use db.adapter() factory for other dialects`);let[{pgAdapter:n},r]=await Promise.all([import(`@forinda/kickjs-db-pg`),import(`pg`)]),i=new r.default.Pool({connectionString:e.connectionString}),a=n({pool:i});return{adapter:a,cleanup:async()=>{await a.close(),await i.end()}}}async function Ro(e){if(e.adapter||(e.dialect??`postgres`)!==`postgres`||!e.connectionString)return null;let t=new(await(import(`pg`))).default.Pool({connectionString:e.connectionString});return{runner:t,cleanup:async()=>{await t.end()}}}function zo(e){if(e.length===0){console.log(`No migrations.`);return}console.table(e.map(e=>({id:e.id,state:e.state,batch:e.batch??`-`,reviewed:e.reviewed,applied:e.appliedAt??`-`})))}function Bo(e){let t=e.command(`db`).description(`Database commands (kickjs-db)`);t.command(`generate <name>`).description(`Generate a new migration from schema diff`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`-e, --empty`,`Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)`).action(async(e,t)=>{let n=process.cwd(),r=await Q(t),i=await Ro(r),a=i?e=>ve(i.runner,e):void 0;try{let i=await ye({name:e,config:r,cwd:n,empty:t.empty,detectCompositeRefs:a});if(i.status===`no-changes`){console.log(`No schema changes detected.`);return}if(i.empty){console.log(`Created empty migration ${i.migrationDir} (author up.sql + down.sql).`);return}let o=i.changeCount===1?``:`s`;console.log(`Created migration ${i.migrationDir} (${i.changeCount} change${o}).`)}finally{await i?.cleanup()}});let n=t.command(`migrate`).description(`Migration runner subcommands`);n.command(`latest`).description(`Apply all pending migrations in a new batch`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await xe({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied batch ${r.batch}: ${r.applied.join(`, `)}`)}finally{await r()}}),n.command(`up`).description(`Apply the next single pending migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await we({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)}finally{await r()}}),n.command(`down`).description(`Reverse the most recent applied migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await be({adapter:n,migrationsDir:t.migrationsDir});e.reversed?console.log(`Reversed ${e.reversed}.`):console.log(`Nothing to reverse.`)}finally{await r()}}),n.command(`rollback`).description(`Reverse the entire last batch as a single unit`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Se({adapter:n,migrationsDir:t.migrationsDir});e.reversed.length===0?console.log(`Nothing to roll back.`):console.log(`Rolled back batch ${e.batch}: ${e.reversed.join(`, `)}`)}finally{await r()}}),n.command(`status`).description(`Print applied + pending migrations`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{zo(await Ce({adapter:n,migrationsDir:t.migrationsDir}))}finally{await r()}}),t.command(`introspect`).description(`Generate a TypeScript schema file from a live database`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--out <path>`,`Output file (defaults to db.schemaPath from config)`).option(`--json`,`Print the raw SchemaSnapshot JSON to stdout instead of writing TS source`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await n.introspect();if(e.json){console.log(JSON.stringify(r,null,2));return}let i=e.out??t.schemaPath;await w(i,Te(r),`utf8`);let a=Object.keys(r.tables).length;console.log(`Wrote ${i} (${a} table${a===1?``:`s`}).`)}finally{await r()}})}function Vo(e){return e.optsWithGlobals().dryRun??!1}function Ho(e){e.command(`codemod`).description(`Codebase migration commands (AST-style rewrites — distinct from db migrate)`).command(`modules`).description(`Rewrite module declarations between class form and the defineModule factory.
|
|
4028
3779
|
Direction defaults to \`modules.style\` from kick.config (or "define").
|
|
4029
3780
|
--target define|class Override the migration direction.
|
|
4030
3781
|
--apply Apply the changes (default: dry-run preview).
|
|
@@ -4092,4 +3843,4 @@ declare global {
|
|
|
4092
3843
|
}
|
|
4093
3844
|
|
|
4094
3845
|
export {}
|
|
4095
|
-
`}const as=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=ss(e);if(t===!1)return null;let n=await e.getScanResult({root:os(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=u.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return is(n.env,r)}});function os(e){return u.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function ss(e){return e.config?.typegen?.envFile}var cs=D({builtinCliPlugins:()=>ls});const ls=[A({name:`kick/init`,register:
|
|
3846
|
+
`}const as=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=ss(e);if(t===!1)return null;let n=await e.getScanResult({root:os(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=u.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return is(n.env,r)}});function os(e){return u.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function ss(e){return e.config?.typegen?.envFile}var cs=D({builtinCliPlugins:()=>ls});const ls=[A({name:`kick/init`,register:Jt}),A({name:`kick/generate`,register:ya}),A({name:`kick/run`,register:Na}),A({name:`kick/info`,register:Pa}),A({name:`kick/inspect`,register:Wa}),A({name:`kick/add`,register:Kt}),A({name:`kick/list`,register:Gt}),A({name:`kick/explain`,register:eo}),A({name:`kick/mcp`,register:fo}),A({name:`kick/tinker`,register:go}),A({name:`kick/remove`,register:So}),A({name:`kick/typegen`,register:To}),A({name:`kick/check`,register:Lo}),A({name:`kick/db`,register:Bo,typegens:[Wo()]}),A({name:`kick/codemod`,register:Ho}),A({name:`kick/assets`,typegens:[qo()]}),A({name:`kick/routes`,typegens:[ts()]}),A({name:`kick/env`,typegens:[as()]})],us=f(b(import.meta.url)),ds=JSON.parse(a(h(us,`..`,`package.json`),`utf-8`));async function fs(){let e=new t;e.name(`kick`).description(`KickJS — A production-grade, decorator-driven Node.js framework`).version(ds.version);let n=Qt(process.cwd()),r=n,i=await k(r)??{},a=He([...ls,...i.plugins??[]],i.commands??[]);await a.register(e,{cwd:r,projectRoot:n,config:i,log:e=>console.log(e)}),Ae(e,{...i,commands:a.commands}),e.showHelpAfterError(),await e.parseAsync(process.argv)}fs().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});export{};
|