@forinda/kickjs-cli 6.10.0 → 6.10.1
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-CLs1wTpl.mjs → agent-docs-C5UFEPks.mjs} +3 -3
- package/dist/{agent-docs-CLs1wTpl.mjs.map → agent-docs-C5UFEPks.mjs.map} +1 -1
- package/dist/{agent-docs-BnGa_odb.mjs → agent-docs-CnxKzjge.mjs} +2 -2
- package/dist/{build-BX7-8d5h.mjs → build-C85VDZsR.mjs} +2 -2
- package/dist/{build-BQUDhMVD.mjs → build-iTUsaX7I.mjs} +3 -3
- package/dist/{build-BQUDhMVD.mjs.map → build-iTUsaX7I.mjs.map} +1 -1
- package/dist/{builtins-DWngUnQ8.mjs → builtins-CEUYJzkI.mjs} +2 -2
- package/dist/{builtins-DfzsCxQu.mjs → builtins-sU7Mboz-.mjs} +58 -18
- package/dist/cli.mjs +2 -2
- package/dist/{config-DHHqXYVF.mjs → config-DILd2DYP.mjs} +2 -2
- package/dist/{config-DYS_-oZ_.mjs → config-DW5X_E5r.mjs} +3 -3
- package/dist/{config-DYS_-oZ_.mjs.map → config-DW5X_E5r.mjs.map} +1 -1
- package/dist/{doctor-CC8eWifc.mjs → doctor-CmvhDoS4.mjs} +12 -6
- package/dist/{doctor-CC8eWifc.mjs.map → doctor-CmvhDoS4.mjs.map} +1 -1
- package/dist/{fullstack-1y-ENahw.mjs → fullstack-B62AJ-Q0.mjs} +3 -3
- package/dist/{fullstack-BWopEQFU.mjs → fullstack-DurEIdZa.mjs} +4 -4
- package/dist/{fullstack-BWopEQFU.mjs.map → fullstack-DurEIdZa.mjs.map} +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-CrbgblqR.mjs → plugin-D5_TIh7b.mjs} +2 -2
- package/dist/{plugin-Dgdp1o7e.mjs → plugin-SFKMs6pt.mjs} +3 -3
- package/dist/{plugin-Dgdp1o7e.mjs.map → plugin-SFKMs6pt.mjs.map} +1 -1
- package/dist/{project-BFhosVpo.mjs → project-ByxNz1NG.mjs} +4 -4
- package/dist/{project-DPQJNbUr.mjs → project-CDVZI3ZG.mjs} +5 -5
- package/dist/{project-DPQJNbUr.mjs.map → project-CDVZI3ZG.mjs.map} +1 -1
- package/dist/{project-docs-BlYdtsfz.mjs → project-docs-BvRuoZmj.mjs} +1 -1
- package/dist/{project-docs-BEnHASsn.mjs → project-docs-CfiAweCM.mjs} +2 -2
- package/dist/{project-docs-BEnHASsn.mjs.map → project-docs-CfiAweCM.mjs.map} +1 -1
- package/dist/{project-root-CSfkrbTB.mjs → project-root-DcnxAB8S.mjs} +2 -2
- package/dist/{project-root-74p4iEbS.mjs → project-root-cgO6wkio.mjs} +3 -3
- package/dist/{project-root-74p4iEbS.mjs.map → project-root-cgO6wkio.mjs.map} +1 -1
- package/dist/{prompts-kKPsD4J5.mjs → prompts-DJ7ttl2h.mjs} +2 -2
- package/dist/{prompts-kKPsD4J5.mjs.map → prompts-DJ7ttl2h.mjs.map} +1 -1
- package/dist/{rolldown-runtime-Wu10E8Jp.mjs → rolldown-runtime-BEUS6jRR.mjs} +1 -1
- package/dist/{run-plugins-BPLz855M.mjs → run-plugins-BKsTwg2x.mjs} +39 -5
- package/dist/run-plugins-BKsTwg2x.mjs.map +1 -0
- package/dist/{typegen-ByZnBYdj.mjs → typegen-BDsxBffM.mjs} +4 -4
- package/dist/{typegen-ngGYyfaY.mjs → typegen-BcVNKa1p.mjs} +5 -5
- package/dist/{typegen-ngGYyfaY.mjs.map → typegen-BcVNKa1p.mjs.map} +1 -1
- package/dist/{types-BYbOAJ_d.mjs → types-eObqB_FH.mjs} +1 -1
- package/package.json +2 -2
- package/dist/run-plugins-BPLz855M.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
12
|
-
//# sourceMappingURL=agent-docs-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{a as t,i as n,l as r,n as i,r as a,t as o}from"./project-docs-CfiAweCM.mjs";import{t as s}from"./prompts-DJ7ttl2h.mjs";import{i as c}from"./config-DW5X_E5r.mjs";import{join as l}from"node:path";import{existsSync as u,readFileSync as d}from"node:fs";var f=e({generateAgentDocs:()=>v});const p=`.agents`,m=new Set([`rest`,`minimal`,`fullstack`]);function h(e,t){if(t)return t;try{let t=JSON.parse(d(l(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function g(e,t){if(t)return t;try{let t=JSON.parse(d(l(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function _(e,t){if(t)return t;try{let t=(await c(e))?.pattern;if(t&&m.has(t))return t}catch{}return`rest`}async function v(e){let c=e.only??`all`,d=h(e.outDir,e.name),f=g(e.outDir,e.pm),m=await _(e.outDir,e.template),v=c===`agents`||c===`both`||c===`all`,y=c===`claude`||c===`both`||c===`all`,b=c===`skills`||c===`all`,x=c===`gemini`||c===`all`,S=c===`copilot`||c===`all`,C=[];if(v&&C.push({file:l(e.outDir,p,`AGENTS.md`),render:()=>o(d,m,f)}),y&&C.push({file:l(e.outDir,`CLAUDE.md`),render:()=>i(d,m,f)}),b)for(let n of t(d,m,f))C.push({file:l(e.outDir,p,`skills`,n.slug,`SKILL.md`),render:()=>n.content});x&&C.push({file:l(e.outDir,p,`GEMINI.md`),render:()=>n(d,m,f)}),S&&C.push({file:l(e.outDir,p,`COPILOT.md`),render:()=>a(d,m,f)});let w=[];for(let{file:t,render:n}of C){if(u(t)&&!e.force&&!await s({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await r(t,n()),w.push(t)}return w}export{v as n,f as t};
|
|
12
|
+
//# sourceMappingURL=agent-docs-C5UFEPks.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-docs-
|
|
1
|
+
{"version":3,"file":"agent-docs-C5UFEPks.mjs","names":[],"sources":["../src/generators/agent-docs.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { writeFileSafe } from '../utils/fs'\nimport { confirm } from '../utils/prompts'\nimport {\n generateClaude,\n generateAgents,\n generateKickJsSkillFiles,\n generateGemini,\n generateCopilot,\n} from './templates/project-docs'\nimport { loadKickConfig } from '../config'\n\ntype ProjectTemplate = 'rest' | 'minimal' | 'fullstack'\n\n/**\n * Subdirectory (relative to the project root) where every shared\n * agent-context file lands. CLAUDE.md is the only exception — it stays\n * at the project root because Claude Code auto-loads CLAUDE.md from\n * there. CLAUDE.md is generated as a thin pointer that tells Claude\n * to read `.agents/AGENTS.md` first.\n *\n * Existing root-level AGENTS.md / kickjs-skills.md files (from older\n * scaffolds before this restructure) are left untouched — the\n * generator emits the new layout alongside them and leaves migration\n * to the adopter.\n */\nconst AGENTS_DIR = '.agents'\n\nexport interface GenerateAgentDocsOptions {\n outDir: string\n /** Override project name (defaults to package.json `name`). */\n name?: string\n /** Override package manager (defaults to package.json `packageManager` field, then 'pnpm'). */\n pm?: string\n /** Override template (defaults to kick.config.ts `pattern`, then 'ddd'). */\n template?: ProjectTemplate\n /**\n * Which file(s) to (re)generate. All `.agents/`-bound files land in\n * the project's `.agents/` subdirectory; `claude` is the only file\n * that stays at the project root.\n *\n * - `agents` → `.agents/AGENTS.md`\n * - `claude` → `CLAUDE.md` (root; thin pointer to .agents/)\n * - `skills` → `.agents/kickjs-skills.md`\n * - `gemini` → `.agents/GEMINI.md`\n * - `copilot` → `.agents/COPILOT.md`\n * - `both` → `.agents/AGENTS.md` + `CLAUDE.md` (legacy alias)\n * - `all` → every file above\n */\n only?: 'agents' | 'claude' | 'skills' | 'gemini' | 'copilot' | 'both' | 'all'\n /** Skip the overwrite prompt. */\n force?: boolean\n}\n\nconst VALID_TEMPLATES = new Set<ProjectTemplate>(['rest', 'minimal', 'fullstack'])\n\nfunction detectName(outDir: string, override?: string): string {\n if (override) return override\n try {\n const pkg = JSON.parse(readFileSync(join(outDir, 'package.json'), 'utf-8')) as { name?: string }\n if (pkg.name) return pkg.name.replace(/^@[^/]+\\//, '')\n } catch {\n // No package.json — fall back to folder name\n }\n return outDir.split('/').findLast(Boolean) ?? 'app'\n}\n\nfunction detectPm(outDir: string, override?: string): string {\n if (override) return override\n try {\n const pkg = JSON.parse(readFileSync(join(outDir, 'package.json'), 'utf-8')) as {\n packageManager?: string\n }\n if (pkg.packageManager) return pkg.packageManager.split('@')[0]\n } catch {\n // ignore\n }\n return 'pnpm'\n}\n\nasync function detectTemplate(\n outDir: string,\n override?: ProjectTemplate,\n): Promise<ProjectTemplate> {\n if (override) return override\n try {\n const cfg = await loadKickConfig(outDir)\n const pattern = cfg?.pattern as ProjectTemplate | undefined\n if (pattern && VALID_TEMPLATES.has(pattern)) return pattern\n } catch {\n // ignore\n }\n return 'rest'\n}\n\nexport async function generateAgentDocs(options: GenerateAgentDocsOptions): Promise<string[]> {\n const only = options.only ?? 'all'\n const name = detectName(options.outDir, options.name)\n const pm = detectPm(options.outDir, options.pm)\n const template = await detectTemplate(options.outDir, options.template)\n\n const wantsAgents = only === 'agents' || only === 'both' || only === 'all'\n const wantsClaude = only === 'claude' || only === 'both' || only === 'all'\n const wantsSkills = only === 'skills' || only === 'all'\n const wantsGemini = only === 'gemini' || only === 'all'\n const wantsCopilot = only === 'copilot' || only === 'all'\n\n // CLAUDE.md stays at the project root because Claude Code auto-loads\n // it from there. Every other shared-context file lands under\n // `.agents/` so the root stays uncluttered. `writeFileSafe` creates\n // parent directories automatically — no need to pre-mkdir.\n const targets: { file: string; render: () => string }[] = []\n if (wantsAgents) {\n targets.push({\n file: join(options.outDir, AGENTS_DIR, 'AGENTS.md'),\n render: () => generateAgents(name, template, pm),\n })\n }\n if (wantsClaude) {\n targets.push({\n file: join(options.outDir, 'CLAUDE.md'),\n render: () => generateClaude(name, template, pm),\n })\n }\n if (wantsSkills) {\n // Per-skill SKILL.md under `.agents/skills/<slug>/` so agents that\n // auto-discover skills (Claude Code, Copilot CLI plugins, Gemini's\n // activate_skill) pick each up by its frontmatter without needing\n // a separate index file.\n for (const skill of generateKickJsSkillFiles(name, template, pm)) {\n targets.push({\n file: join(options.outDir, AGENTS_DIR, 'skills', skill.slug, 'SKILL.md'),\n render: () => skill.content,\n })\n }\n }\n if (wantsGemini) {\n targets.push({\n file: join(options.outDir, AGENTS_DIR, 'GEMINI.md'),\n render: () => generateGemini(name, template, pm),\n })\n }\n if (wantsCopilot) {\n targets.push({\n file: join(options.outDir, AGENTS_DIR, 'COPILOT.md'),\n render: () => generateCopilot(name, template, pm),\n })\n }\n\n const written: string[] = []\n for (const { file, render } of targets) {\n if (existsSync(file) && !options.force) {\n const overwrite = await confirm({\n message: `${file.replace(options.outDir + '/', '')} already exists. Overwrite?`,\n initialValue: false,\n })\n if (!overwrite) {\n console.log(` Skipped — existing ${file.replace(options.outDir + '/', '')} preserved.`)\n continue\n }\n }\n await writeFileSafe(file, render())\n written.push(file)\n }\n return written\n}\n"],"mappings":";;;;;;;;;;uVA2BA,MAAM,EAAa,UA4Bb,EAAkB,IAAI,IAAqB,CAAC,OAAQ,UAAW,WAAW,CAAC,EAEjF,SAAS,EAAW,EAAgB,EAA2B,CAC7D,GAAI,EAAU,OAAO,EACrB,GAAI,CACF,IAAM,EAAM,KAAK,MAAM,EAAa,EAAK,EAAQ,cAAc,EAAG,OAAO,CAAC,EAC1E,GAAI,EAAI,KAAM,OAAO,EAAI,KAAK,QAAQ,YAAa,EAAE,CACvD,MAAQ,CAER,CACA,OAAO,EAAO,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,GAAK,KAChD,CAEA,SAAS,EAAS,EAAgB,EAA2B,CAC3D,GAAI,EAAU,OAAO,EACrB,GAAI,CACF,IAAM,EAAM,KAAK,MAAM,EAAa,EAAK,EAAQ,cAAc,EAAG,OAAO,CAAC,EAG1E,GAAI,EAAI,eAAgB,OAAO,EAAI,eAAe,MAAM,GAAG,CAAC,CAAC,EAC/D,MAAQ,CAER,CACA,MAAO,MACT,CAEA,eAAe,EACb,EACA,EAC0B,CAC1B,GAAI,EAAU,OAAO,EACrB,GAAI,CAEF,IAAM,GAAU,MADE,EAAe,CAAM,EAAA,EAClB,QACrB,GAAI,GAAW,EAAgB,IAAI,CAAO,EAAG,OAAO,CACtD,MAAQ,CAER,CACA,MAAO,MACT,CAEA,eAAsB,EAAkB,EAAsD,CAC5F,IAAM,EAAO,EAAQ,MAAQ,MACvB,EAAO,EAAW,EAAQ,OAAQ,EAAQ,IAAI,EAC9C,EAAK,EAAS,EAAQ,OAAQ,EAAQ,EAAE,EACxC,EAAW,MAAM,EAAe,EAAQ,OAAQ,EAAQ,QAAQ,EAEhE,EAAc,IAAS,UAAY,IAAS,QAAU,IAAS,MAC/D,EAAc,IAAS,UAAY,IAAS,QAAU,IAAS,MAC/D,EAAc,IAAS,UAAY,IAAS,MAC5C,EAAc,IAAS,UAAY,IAAS,MAC5C,EAAe,IAAS,WAAa,IAAS,MAM9C,EAAoD,CAAC,EAa3D,GAZI,GACF,EAAQ,KAAK,CACX,KAAM,EAAK,EAAQ,OAAQ,EAAY,WAAW,EAClD,WAAc,EAAe,EAAM,EAAU,CAAE,CACjD,CAAC,EAEC,GACF,EAAQ,KAAK,CACX,KAAM,EAAK,EAAQ,OAAQ,WAAW,EACtC,WAAc,EAAe,EAAM,EAAU,CAAE,CACjD,CAAC,EAEC,EAKF,IAAK,IAAM,KAAS,EAAyB,EAAM,EAAU,CAAE,EAC7D,EAAQ,KAAK,CACX,KAAM,EAAK,EAAQ,OAAQ,EAAY,SAAU,EAAM,KAAM,UAAU,EACvE,WAAc,EAAM,OACtB,CAAC,EAGD,GACF,EAAQ,KAAK,CACX,KAAM,EAAK,EAAQ,OAAQ,EAAY,WAAW,EAClD,WAAc,EAAe,EAAM,EAAU,CAAE,CACjD,CAAC,EAEC,GACF,EAAQ,KAAK,CACX,KAAM,EAAK,EAAQ,OAAQ,EAAY,YAAY,EACnD,WAAc,EAAgB,EAAM,EAAU,CAAE,CAClD,CAAC,EAGH,IAAM,EAAoB,CAAC,EAC3B,IAAK,GAAM,CAAE,OAAM,YAAY,EAAS,CACtC,GAAI,EAAW,CAAI,GAAK,CAAC,EAAQ,OAK3B,CAAC,MAJmB,EAAQ,CAC9B,QAAS,GAAG,EAAK,QAAQ,EAAQ,OAAS,IAAK,EAAE,EAAE,6BACnD,aAAc,EAChB,CAAC,EACe,CACd,QAAQ,IAAI,wBAAwB,EAAK,QAAQ,EAAQ,OAAS,IAAK,EAAE,EAAE,YAAY,EACvF,QACF,CAEF,MAAM,EAAc,EAAM,EAAO,CAAC,EAClC,EAAQ,KAAK,CAAI,CACnB,CACA,OAAO,CACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{r as t}from"./config-DILd2DYP.mjs";import{a as n,i as r,l as i,n as a,r as o,t as s}from"./project-docs-BvRuoZmj.mjs";import{existsSync as c,readFileSync as l}from"node:fs";import{join as u}from"node:path";import*as d from"@clack/prompts";import f from"picocolors";const p={GET:f.green,POST:f.cyan,PUT:f.yellow,PATCH:f.magenta,DELETE:f.red};function m(e){return(p[e]??f.dim)(e.padEnd(7))}function h(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return f.red(t);case`WARNING`:return f.yellow(t);case`INFO`:return f.blue(f.dim(t));default:return t}}f.green(`✓`),f.red(`✖`),f.yellow(`⚠`),f.blue(`ℹ`);function g(e){d.intro(f.bgCyan(f.black(` ${e} `)))}function _(e){d.outro(e)}function v(e){d.isCancel(e)&&(d.cancel(`Operation cancelled.`),process.exit(0))}async function y(e){let t=await d.text(e);return v(t),t}async function b(e){let t=await d.select(e);return v(t),t}async function x(e){let t=await d.multiselect(e);return v(t),t}async function S(e){let t=await d.confirm(e);return v(t),t}function C(){return d.spinner()}const w=d.log;var T=e({generateAgentDocs:()=>j});const E=`.agents`,D=new Set([`rest`,`minimal`,`fullstack`]);function O(e,t){if(t)return t;try{let t=JSON.parse(l(u(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function k(e,t){if(t)return t;try{let t=JSON.parse(l(u(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function A(e,n){if(n)return n;try{let n=(await t(e))?.pattern;if(n&&D.has(n))return n}catch{}return`rest`}async function j(e){let t=e.only??`all`,l=O(e.outDir,e.name),d=k(e.outDir,e.pm),f=await A(e.outDir,e.template),p=t===`agents`||t===`both`||t===`all`,m=t===`claude`||t===`both`||t===`all`,h=t===`skills`||t===`all`,g=t===`gemini`||t===`all`,_=t===`copilot`||t===`all`,v=[];if(p&&v.push({file:u(e.outDir,E,`AGENTS.md`),render:()=>s(l,f,d)}),m&&v.push({file:u(e.outDir,`CLAUDE.md`),render:()=>a(l,f,d)}),h)for(let t of n(l,f,d))v.push({file:u(e.outDir,E,`skills`,t.slug,`SKILL.md`),render:()=>t.content});g&&v.push({file:u(e.outDir,E,`GEMINI.md`),render:()=>r(l,f,d)}),_&&v.push({file:u(e.outDir,E,`COPILOT.md`),render:()=>o(l,f,d)});let y=[];for(let{file:t,render:n}of v){if(c(t)&&!e.force&&!await S({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await i(t,n()),y.push(t)}return y}export{w as a,b as c,m as d,f,g as i,C as l,j as n,x as o,h as p,S as r,_ as s,T as t,y as u};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{cpSync as t,existsSync as n,mkdirSync as r,statSync as i,writeFileSync as a}from"node:fs";import{dirname as o,isAbsolute as s,join as c,relative as l,resolve as u}from"node:path";import{glob as d}from"glob";import{groupAssetKeys as f}from"@forinda/kickjs";var p=e({ASSET_MANIFEST_VERSION:()=>1,buildAssets:()=>m});async function m(e,t){let{cwd:n,silent:i=!1}=t,o=t.distDir??e?.build?.outDir??`dist`,s=e?.assetMap;if(!s||Object.keys(s).length===0)return null;let d=i?()=>{}:console.log,f=u(n,o);r(f,{recursive:!0});let p=[],m={};for(let[e,t]of Object.entries(s)){let r=await h(e,t,n,f);p.push(r.entrySummary),Object.assign(m,r.manifestSlice),d(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let g={version:1,entries:m},_=c(f,`.kickjs-assets.json`);return a(_,JSON.stringify(g,null,2)+`
|
|
12
12
|
`,`utf-8`),d(` ✓ wrote manifest → ${l(n,_)} (${Object.keys(m).length} entries)`),{manifestPath:_,entries:p,manifest:g}}async function h(e,i,a,s){let p=u(a,i.src),m=i.dest?u(a,i.dest):c(s,e);if(v(m,a))return console.warn(` ⚠ assetMap.${e}.dest ('${i.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:i.src,dest:l(a,m),filesCopied:0},manifestSlice:{}};if(!n(p)||!y(p))return{entrySummary:{namespace:e,src:i.src,dest:l(a,m),filesCopied:0},manifestSlice:{}};let h=await d(i.glob??`**/*`,{cwd:p,nodir:!0,dot:!1,posix:!0});r(m,{recursive:!0});let b={},{pairs:x,collisionGroupsResolved:S}=f(e,[...h].toSorted(),{strategy:i.keys??`auto`}),C=0;for(let{rel:e,key:n}of x){let i=c(p,e),a=c(m,e);b[n]=_(s,a),!g(i,a)&&(r(o(a),{recursive:!0}),t(i,a),C++)}return S>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${S} 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:i.src,dest:l(a,m),filesCopied:C},manifestSlice:b}}function g(e,t){if(!n(t))return!1;try{let n=i(e),r=i(t);return r.size===n.size&&r.mtimeMs>=n.mtimeMs}catch{return!1}}function _(e,t){return l(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function v(e,t){let n=l(t,e);return n===``?!1:n.startsWith(`..`)||s(n)}function y(e){try{return i(e).isDirectory()}catch{return!1}}export{p as n,m as t};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{dirname as t,isAbsolute as n,join as r,relative as i,resolve as a}from"node:path";import{cpSync as o,existsSync as s,mkdirSync as c,statSync as l,writeFileSync as u}from"node:fs";import{glob as d}from"glob";import{groupAssetKeys as f}from"@forinda/kickjs";var p=e({ASSET_MANIFEST_VERSION:()=>1,buildAssets:()=>m});async function m(e,t){let{cwd:n,silent:o=!1}=t,s=t.distDir??e?.build?.outDir??`dist`,l=e?.assetMap;if(!l||Object.keys(l).length===0)return null;let d=o?()=>{}:console.log,f=a(n,s);c(f,{recursive:!0});let p=[],m={};for(let[e,t]of Object.entries(l)){let r=await h(e,t,n,f);p.push(r.entrySummary),Object.assign(m,r.manifestSlice),d(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let g={version:1,entries:m},_=r(f,`.kickjs-assets.json`);return u(_,JSON.stringify(g,null,2)+`
|
|
12
12
|
`,`utf-8`),d(` ✓ wrote manifest → ${i(n,_)} (${Object.keys(m).length} entries)`),{manifestPath:_,entries:p,manifest:g}}async function h(e,n,l,u){let p=a(l,n.src),m=n.dest?a(l,n.dest):r(u,e);if(v(m,l))return console.warn(` ⚠ assetMap.${e}.dest ('${n.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:n.src,dest:i(l,m),filesCopied:0},manifestSlice:{}};if(!s(p)||!y(p))return{entrySummary:{namespace:e,src:n.src,dest:i(l,m),filesCopied:0},manifestSlice:{}};let h=await d(n.glob??`**/*`,{cwd:p,nodir:!0,dot:!1,posix:!0});c(m,{recursive:!0});let b={},{pairs:x,collisionGroupsResolved:S}=f(e,[...h].toSorted(),{strategy:n.keys??`auto`}),C=0;for(let{rel:e,key:n}of x){let i=r(p,e),a=r(m,e);b[n]=_(u,a),!g(i,a)&&(c(t(a),{recursive:!0}),o(i,a),C++)}return S>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${S} 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:n.src,dest:i(l,m),filesCopied:C},manifestSlice:b}}function g(e,t){if(!s(t))return!1;try{let n=l(e),r=l(t);return r.size===n.size&&r.mtimeMs>=n.mtimeMs}catch{return!1}}function _(e,t){return i(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function v(e,t){let r=i(t,e);return r===``?!1:r.startsWith(`..`)||n(r)}function y(e){try{return l(e).isDirectory()}catch{return!1}}export{p as n,m as t};
|
|
13
|
-
//# sourceMappingURL=build-
|
|
13
|
+
//# sourceMappingURL=build-iTUsaX7I.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-BQUDhMVD.mjs","names":[],"sources":["../src/asset-manager/build.ts"],"sourcesContent":["/**\n * Build pipeline for `assetMap` entries (asset-manager PR 2).\n *\n * For each entry, walks `src/<...>` matching the configured `glob`,\n * copies matches into `dest` (default `dist/<name>/`), then emits a\n * `dist/.kickjs-assets.json` manifest mapping logical\n * `<namespace>/<key>` keys to repo-relative paths inside `dist/`.\n *\n * Pure function on top of `node:fs` + `glob` — no shell, no side\n * effects beyond the configured directory writes. The build entry-\n * point in `commands/run.ts` calls `buildAssets` after the existing\n * `copyDirs` step.\n *\n * @module @forinda/kickjs-cli/asset-manager/build\n */\n\nimport { cpSync, existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { glob } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry, KickConfig } from '../config'\n\n/** Wire-format version for `dist/.kickjs-assets.json`. Bump on shape change. */\nexport const ASSET_MANIFEST_VERSION = 1 as const\n\n/** On-disk manifest format (`dist/.kickjs-assets.json`). */\nexport interface AssetManifest {\n version: typeof ASSET_MANIFEST_VERSION\n /**\n * Logical key → manifest-relative path. Logical key is\n * `<namespace>/<key>` where `<key>` is the file path under `src`\n * with the extension stripped + path separators normalised.\n *\n * Path values are relative to the manifest file's directory so the\n * runtime can resolve them with a single `path.resolve(manifestDir,\n * entry)` regardless of where dist/ lives.\n */\n entries: Record<string, string>\n}\n\nexport interface BuildAssetsOptions {\n /** Project root — resolved for every relative path in the entry. */\n cwd: string\n /**\n * Output dir for the manifest + per-namespace asset copies. When\n * omitted, falls back to `config.build?.outDir` (resolved against\n * `cwd`), then to `dist/` under cwd. Adopters with a custom Vite\n * `build.outDir` should set `kick.config.ts.build.outDir` to match.\n */\n distDir?: string\n /** Suppress per-entry log lines. Default: false. */\n silent?: boolean\n}\n\n/** One entry in the per-build summary returned by `buildAssets`. */\nexport interface BuildAssetsEntryResult {\n namespace: string\n src: string\n dest: string\n /**\n * Number of files actually written this run. On an incremental\n * rebuild where nothing changed this is 0 even though the manifest\n * still lists every matched file.\n */\n filesCopied: number\n}\n\n/** Aggregated outcome of `buildAssets`. */\nexport interface BuildAssetsResult {\n manifestPath: string\n entries: BuildAssetsEntryResult[]\n /** `entries` merged into a single record — useful for tests + tooling. */\n manifest: AssetManifest\n}\n\n/**\n * Run the full asset build for a loaded config:\n *\n * 1. For each `assetMap` entry, glob → copy → manifest stub.\n * 2. Write `dist/.kickjs-assets.json`.\n *\n * Returns a summary including the manifest contents. No-op (and no\n * manifest written) when `assetMap` is empty / missing — the build\n * pipeline shouldn't litter `dist/` with empty manifests for\n * adopters who don't use the feature.\n */\nexport async function buildAssets(\n config: KickConfig | null,\n opts: BuildAssetsOptions,\n): Promise<BuildAssetsResult | null> {\n const { cwd, silent = false } = opts\n // Resolution order: explicit opts.distDir → config.build.outDir → 'dist'.\n // The CLI build command passes nothing explicit, so adopters control\n // the output via kick.config.ts.build.outDir alone.\n const distDir = opts.distDir ?? config?.build?.outDir ?? 'dist'\n const map = config?.assetMap\n if (!map || Object.keys(map).length === 0) return null\n\n const log = silent ? () => {} : console.log\n\n const distAbs = resolve(cwd, distDir)\n mkdirSync(distAbs, { recursive: true })\n\n const summary: BuildAssetsEntryResult[] = []\n const manifestEntries: Record<string, string> = {}\n\n for (const [namespace, entry] of Object.entries(map)) {\n const result = await processEntry(namespace, entry, cwd, distAbs)\n summary.push(result.entrySummary)\n Object.assign(manifestEntries, result.manifestSlice)\n log(\n ` ✓ ${namespace}: ${result.entrySummary.filesCopied} file(s) → ${result.entrySummary.dest}`,\n )\n }\n\n const manifest: AssetManifest = {\n version: ASSET_MANIFEST_VERSION,\n entries: manifestEntries,\n }\n const manifestPath = join(distAbs, '.kickjs-assets.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n log(\n ` ✓ wrote manifest → ${relative(cwd, manifestPath)} (${Object.keys(manifestEntries).length} entries)`,\n )\n\n return { manifestPath, entries: summary, manifest }\n}\n\n/** Per-entry inner pipeline — extracted for unit-test reuse. */\nasync function processEntry(\n namespace: string,\n entry: AssetMapEntry,\n cwd: string,\n distAbs: string,\n): Promise<{\n entrySummary: BuildAssetsEntryResult\n manifestSlice: Record<string, string>\n}> {\n const srcAbs = resolve(cwd, entry.src)\n const destAbs = entry.dest ? resolve(cwd, entry.dest) : join(distAbs, namespace)\n\n // Defensive: refuse to write outside the project root (cwd) even\n // though validateAssetMap warned about it at config-load time. The\n // build step shouldn't trust upstream warnings — a typo like\n // `dest: '../../'` would otherwise sprinkle files outside the\n // workspace despite the warning being printed.\n if (escapesRoot(destAbs, cwd)) {\n console.warn(\n ` ⚠ assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — skipping copy`,\n )\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n // Treat src-not-a-directory the same as src-missing — `glob` would\n // throw if pointed at a file, surfacing as a generic build failure\n // instead of a clean 0-files entry. Matches the validator's warning\n // shape (already emitted at config-load time for the missing case).\n if (!existsSync(srcAbs) || !isDirectorySync(srcAbs)) {\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n const pattern = entry.glob ?? '**/*'\n // `glob` returns paths relative to `cwd` when `cwd` is set —\n // exactly the slugs we want for the manifest keys.\n const matches = await glob(pattern, {\n cwd: srcAbs,\n nodir: true,\n dot: false,\n posix: true,\n })\n\n mkdirSync(destAbs, { recursive: true })\n\n const manifestSlice: Record<string, string> = {}\n // Sort the walk so the manifest is byte-stable across platforms.\n // The `groupAssetKeys` helper preserves input order, so a stable\n // sort here makes the resulting manifest stable too.\n const sorted = [...matches].toSorted()\n const { pairs, collisionGroupsResolved } = groupAssetKeys(namespace, sorted, {\n strategy: entry.keys ?? 'auto',\n })\n\n // Copy the files and write the keyed manifest slice. Both come from\n // the same `pairs` order so the on-disk layout matches the\n // manifest's iteration order — easier to grep in cold-start\n // debugging.\n //\n // Incremental: skip the copy when the destination is already\n // up-to-date (exists, same size, mtime ≥ source). `cpSync` stamps the\n // copy with the current time, so an unchanged source always satisfies\n // `dest.mtime ≥ src.mtime` on the next run — turning a re-build into a\n // pure stat sweep instead of re-copying every asset. The manifest\n // slice is written unconditionally so the manifest stays complete.\n let filesCopied = 0\n for (const { rel: relPath, key } of pairs) {\n const srcFile = join(srcAbs, relPath)\n const destFile = join(destAbs, relPath)\n manifestSlice[key] = toManifestRelative(distAbs, destFile)\n if (isUpToDate(srcFile, destFile)) continue\n mkdirSync(dirname(destFile), { recursive: true })\n cpSync(srcFile, destFile)\n filesCopied++\n }\n\n if (collisionGroupsResolved > 0) {\n console.log(\n ` ℹ assetMap.${namespace}: auto-resolved ${collisionGroupsResolved} basename collision(s) by keeping extensions ` +\n `(set 'keys: \"strip\"' to opt back into legacy last-write-wins behaviour, or 'keys: \"with-extension\"' to keep all keys verbose).`,\n )\n }\n\n return {\n entrySummary: {\n namespace,\n src: entry.src,\n dest: relative(cwd, destAbs),\n filesCopied,\n },\n manifestSlice,\n }\n}\n\n/**\n * Is `destFile` already a current copy of `srcFile`? True when the\n * destination exists with the same byte size and an mtime no older than\n * the source. Used to skip redundant copies on an incremental rebuild.\n */\nfunction isUpToDate(srcFile: string, destFile: string): boolean {\n if (!existsSync(destFile)) return false\n try {\n const s = statSync(srcFile)\n const d = statSync(destFile)\n return d.size === s.size && d.mtimeMs >= s.mtimeMs\n } catch {\n return false\n }\n}\n\n/**\n * Make `destFile` relative to the manifest's directory + force POSIX\n * separators so the manifest is byte-stable across platforms.\n */\nfunction toManifestRelative(manifestDir: string, destFile: string): string {\n const rel = relative(manifestDir, destFile)\n // path.relative returns OS-native separators; the manifest is JSON\n // and the runtime uses path.resolve which handles either, but a\n // forward-slash manifest is grep-friendly + diff-stable.\n return rel.split(/[\\\\/]/).filter(Boolean).join('/')\n}\n\n/**\n * Pure manifest writer — handy for tests that want to assert against\n * a hand-crafted manifest without exercising the full pipeline.\n */\nexport function writeAssetManifest(distDir: string, manifest: AssetManifest): string {\n const path = join(distDir, '.kickjs-assets.json')\n mkdirSync(distDir, { recursive: true })\n writeFileSync(path, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n return path\n}\n\n/**\n * Read + parse a manifest from disk. Returns `null` on missing or\n * malformed file rather than throwing — the runtime resolver wants\n * to fall through to dev-mode lookup in that case.\n */\nexport function readAssetManifest(distDir: string): AssetManifest | null {\n const path = join(distDir, '.kickjs-assets.json')\n if (!existsSync(path)) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs')\n const raw = fs.readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<AssetManifest>\n if (parsed.version !== ASSET_MANIFEST_VERSION) return null\n if (!parsed.entries || typeof parsed.entries !== 'object') return null\n return parsed as AssetManifest\n } catch {\n return null\n }\n}\n\n/**\n * Project-root escape check that's safe across symlinks + drive letters.\n * `path.relative` returns `..` segments when the target sits above root,\n * and an absolute path when the two live on different roots (Windows).\n * `startsWith(root)` would miss both cases.\n */\nfunction escapesRoot(path: string, root: string): boolean {\n const rel = relative(root, path)\n if (rel === '') return false\n return rel.startsWith('..') || isAbsolute(rel)\n}\n\n/** Pure helper — `false` for missing, non-dir, or unreadable paths. */\nfunction isDirectorySync(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"build-iTUsaX7I.mjs","names":[],"sources":["../src/asset-manager/build.ts"],"sourcesContent":["/**\n * Build pipeline for `assetMap` entries (asset-manager PR 2).\n *\n * For each entry, walks `src/<...>` matching the configured `glob`,\n * copies matches into `dest` (default `dist/<name>/`), then emits a\n * `dist/.kickjs-assets.json` manifest mapping logical\n * `<namespace>/<key>` keys to repo-relative paths inside `dist/`.\n *\n * Pure function on top of `node:fs` + `glob` — no shell, no side\n * effects beyond the configured directory writes. The build entry-\n * point in `commands/run.ts` calls `buildAssets` after the existing\n * `copyDirs` step.\n *\n * @module @forinda/kickjs-cli/asset-manager/build\n */\n\nimport { cpSync, existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { glob } from 'glob'\nimport { groupAssetKeys } from '@forinda/kickjs'\nimport type { AssetMapEntry, KickConfig } from '../config'\n\n/** Wire-format version for `dist/.kickjs-assets.json`. Bump on shape change. */\nexport const ASSET_MANIFEST_VERSION = 1 as const\n\n/** On-disk manifest format (`dist/.kickjs-assets.json`). */\nexport interface AssetManifest {\n version: typeof ASSET_MANIFEST_VERSION\n /**\n * Logical key → manifest-relative path. Logical key is\n * `<namespace>/<key>` where `<key>` is the file path under `src`\n * with the extension stripped + path separators normalised.\n *\n * Path values are relative to the manifest file's directory so the\n * runtime can resolve them with a single `path.resolve(manifestDir,\n * entry)` regardless of where dist/ lives.\n */\n entries: Record<string, string>\n}\n\nexport interface BuildAssetsOptions {\n /** Project root — resolved for every relative path in the entry. */\n cwd: string\n /**\n * Output dir for the manifest + per-namespace asset copies. When\n * omitted, falls back to `config.build?.outDir` (resolved against\n * `cwd`), then to `dist/` under cwd. Adopters with a custom Vite\n * `build.outDir` should set `kick.config.ts.build.outDir` to match.\n */\n distDir?: string\n /** Suppress per-entry log lines. Default: false. */\n silent?: boolean\n}\n\n/** One entry in the per-build summary returned by `buildAssets`. */\nexport interface BuildAssetsEntryResult {\n namespace: string\n src: string\n dest: string\n /**\n * Number of files actually written this run. On an incremental\n * rebuild where nothing changed this is 0 even though the manifest\n * still lists every matched file.\n */\n filesCopied: number\n}\n\n/** Aggregated outcome of `buildAssets`. */\nexport interface BuildAssetsResult {\n manifestPath: string\n entries: BuildAssetsEntryResult[]\n /** `entries` merged into a single record — useful for tests + tooling. */\n manifest: AssetManifest\n}\n\n/**\n * Run the full asset build for a loaded config:\n *\n * 1. For each `assetMap` entry, glob → copy → manifest stub.\n * 2. Write `dist/.kickjs-assets.json`.\n *\n * Returns a summary including the manifest contents. No-op (and no\n * manifest written) when `assetMap` is empty / missing — the build\n * pipeline shouldn't litter `dist/` with empty manifests for\n * adopters who don't use the feature.\n */\nexport async function buildAssets(\n config: KickConfig | null,\n opts: BuildAssetsOptions,\n): Promise<BuildAssetsResult | null> {\n const { cwd, silent = false } = opts\n // Resolution order: explicit opts.distDir → config.build.outDir → 'dist'.\n // The CLI build command passes nothing explicit, so adopters control\n // the output via kick.config.ts.build.outDir alone.\n const distDir = opts.distDir ?? config?.build?.outDir ?? 'dist'\n const map = config?.assetMap\n if (!map || Object.keys(map).length === 0) return null\n\n const log = silent ? () => {} : console.log\n\n const distAbs = resolve(cwd, distDir)\n mkdirSync(distAbs, { recursive: true })\n\n const summary: BuildAssetsEntryResult[] = []\n const manifestEntries: Record<string, string> = {}\n\n for (const [namespace, entry] of Object.entries(map)) {\n const result = await processEntry(namespace, entry, cwd, distAbs)\n summary.push(result.entrySummary)\n Object.assign(manifestEntries, result.manifestSlice)\n log(\n ` ✓ ${namespace}: ${result.entrySummary.filesCopied} file(s) → ${result.entrySummary.dest}`,\n )\n }\n\n const manifest: AssetManifest = {\n version: ASSET_MANIFEST_VERSION,\n entries: manifestEntries,\n }\n const manifestPath = join(distAbs, '.kickjs-assets.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n log(\n ` ✓ wrote manifest → ${relative(cwd, manifestPath)} (${Object.keys(manifestEntries).length} entries)`,\n )\n\n return { manifestPath, entries: summary, manifest }\n}\n\n/** Per-entry inner pipeline — extracted for unit-test reuse. */\nasync function processEntry(\n namespace: string,\n entry: AssetMapEntry,\n cwd: string,\n distAbs: string,\n): Promise<{\n entrySummary: BuildAssetsEntryResult\n manifestSlice: Record<string, string>\n}> {\n const srcAbs = resolve(cwd, entry.src)\n const destAbs = entry.dest ? resolve(cwd, entry.dest) : join(distAbs, namespace)\n\n // Defensive: refuse to write outside the project root (cwd) even\n // though validateAssetMap warned about it at config-load time. The\n // build step shouldn't trust upstream warnings — a typo like\n // `dest: '../../'` would otherwise sprinkle files outside the\n // workspace despite the warning being printed.\n if (escapesRoot(destAbs, cwd)) {\n console.warn(\n ` ⚠ assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — skipping copy`,\n )\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n // Treat src-not-a-directory the same as src-missing — `glob` would\n // throw if pointed at a file, surfacing as a generic build failure\n // instead of a clean 0-files entry. Matches the validator's warning\n // shape (already emitted at config-load time for the missing case).\n if (!existsSync(srcAbs) || !isDirectorySync(srcAbs)) {\n return {\n entrySummary: { namespace, src: entry.src, dest: relative(cwd, destAbs), filesCopied: 0 },\n manifestSlice: {},\n }\n }\n\n const pattern = entry.glob ?? '**/*'\n // `glob` returns paths relative to `cwd` when `cwd` is set —\n // exactly the slugs we want for the manifest keys.\n const matches = await glob(pattern, {\n cwd: srcAbs,\n nodir: true,\n dot: false,\n posix: true,\n })\n\n mkdirSync(destAbs, { recursive: true })\n\n const manifestSlice: Record<string, string> = {}\n // Sort the walk so the manifest is byte-stable across platforms.\n // The `groupAssetKeys` helper preserves input order, so a stable\n // sort here makes the resulting manifest stable too.\n const sorted = [...matches].toSorted()\n const { pairs, collisionGroupsResolved } = groupAssetKeys(namespace, sorted, {\n strategy: entry.keys ?? 'auto',\n })\n\n // Copy the files and write the keyed manifest slice. Both come from\n // the same `pairs` order so the on-disk layout matches the\n // manifest's iteration order — easier to grep in cold-start\n // debugging.\n //\n // Incremental: skip the copy when the destination is already\n // up-to-date (exists, same size, mtime ≥ source). `cpSync` stamps the\n // copy with the current time, so an unchanged source always satisfies\n // `dest.mtime ≥ src.mtime` on the next run — turning a re-build into a\n // pure stat sweep instead of re-copying every asset. The manifest\n // slice is written unconditionally so the manifest stays complete.\n let filesCopied = 0\n for (const { rel: relPath, key } of pairs) {\n const srcFile = join(srcAbs, relPath)\n const destFile = join(destAbs, relPath)\n manifestSlice[key] = toManifestRelative(distAbs, destFile)\n if (isUpToDate(srcFile, destFile)) continue\n mkdirSync(dirname(destFile), { recursive: true })\n cpSync(srcFile, destFile)\n filesCopied++\n }\n\n if (collisionGroupsResolved > 0) {\n console.log(\n ` ℹ assetMap.${namespace}: auto-resolved ${collisionGroupsResolved} basename collision(s) by keeping extensions ` +\n `(set 'keys: \"strip\"' to opt back into legacy last-write-wins behaviour, or 'keys: \"with-extension\"' to keep all keys verbose).`,\n )\n }\n\n return {\n entrySummary: {\n namespace,\n src: entry.src,\n dest: relative(cwd, destAbs),\n filesCopied,\n },\n manifestSlice,\n }\n}\n\n/**\n * Is `destFile` already a current copy of `srcFile`? True when the\n * destination exists with the same byte size and an mtime no older than\n * the source. Used to skip redundant copies on an incremental rebuild.\n */\nfunction isUpToDate(srcFile: string, destFile: string): boolean {\n if (!existsSync(destFile)) return false\n try {\n const s = statSync(srcFile)\n const d = statSync(destFile)\n return d.size === s.size && d.mtimeMs >= s.mtimeMs\n } catch {\n return false\n }\n}\n\n/**\n * Make `destFile` relative to the manifest's directory + force POSIX\n * separators so the manifest is byte-stable across platforms.\n */\nfunction toManifestRelative(manifestDir: string, destFile: string): string {\n const rel = relative(manifestDir, destFile)\n // path.relative returns OS-native separators; the manifest is JSON\n // and the runtime uses path.resolve which handles either, but a\n // forward-slash manifest is grep-friendly + diff-stable.\n return rel.split(/[\\\\/]/).filter(Boolean).join('/')\n}\n\n/**\n * Pure manifest writer — handy for tests that want to assert against\n * a hand-crafted manifest without exercising the full pipeline.\n */\nexport function writeAssetManifest(distDir: string, manifest: AssetManifest): string {\n const path = join(distDir, '.kickjs-assets.json')\n mkdirSync(distDir, { recursive: true })\n writeFileSync(path, JSON.stringify(manifest, null, 2) + '\\n', 'utf-8')\n return path\n}\n\n/**\n * Read + parse a manifest from disk. Returns `null` on missing or\n * malformed file rather than throwing — the runtime resolver wants\n * to fall through to dev-mode lookup in that case.\n */\nexport function readAssetManifest(distDir: string): AssetManifest | null {\n const path = join(distDir, '.kickjs-assets.json')\n if (!existsSync(path)) return null\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('node:fs')\n const raw = fs.readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw) as Partial<AssetManifest>\n if (parsed.version !== ASSET_MANIFEST_VERSION) return null\n if (!parsed.entries || typeof parsed.entries !== 'object') return null\n return parsed as AssetManifest\n } catch {\n return null\n }\n}\n\n/**\n * Project-root escape check that's safe across symlinks + drive letters.\n * `path.relative` returns `..` segments when the target sits above root,\n * and an absolute path when the two live on different roots (Windows).\n * `startsWith(root)` would miss both cases.\n */\nfunction escapesRoot(path: string, root: string): boolean {\n const rel = relative(root, path)\n if (rel === '') return false\n return rel.startsWith('..') || isAbsolute(rel)\n}\n\n/** Pure helper — `false` for missing, non-dir, or unreadable paths. */\nfunction isDirectorySync(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./run-plugins-
|
|
11
|
+
import{t as e}from"./run-plugins-BKsTwg2x.mjs";export{e as builtinCliPlugins};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{a as t,c as n,i as r,o as i,r as a,s as o,t as s,u as c}from"./project-ByxNz1NG.mjs";import{a as l,i as u,o as d,r as f}from"./config-DILd2DYP.mjs";import{n as p,r as m}from"./plugin-D5_TIh7b.mjs";import{c as h,l as g,s as _}from"./project-docs-BvRuoZmj.mjs";import{a as v,c as y,d as b,f as x,i as ee,l as te,n as ne,o as re,p as ie,r as S,s as C,u as ae}from"./agent-docs-CnxKzjge.mjs";import{t as oe}from"./project-root-DcnxAB8S.mjs";import{a as se,c as ce,d as le,f as ue,g as de,h as fe,i as pe,l as me,m as he,o as ge,p as _e,r as ve,s as ye,t as be,u as xe}from"./typegen-BDsxBffM.mjs";import{t as Se}from"./build-C85VDZsR.mjs";import{createRequire as Ce}from"node:module";import{cpSync as we,existsSync as w,mkdirSync as Te,readFileSync as T,readdirSync as E,rmSync as Ee,statSync as De,writeFileSync as Oe}from"node:fs";import D,{basename as ke,dirname as O,join as k,relative as A,resolve as j,sep as Ae}from"node:path";import{fileURLToPath as je,pathToFileURL as M}from"node:url";import{fork as Me,spawn as Ne}from"node:child_process";import{copyFile as Pe,mkdir as Fe,readFile as N,readdir as Ie,rm as Le,stat as Re,writeFile as P}from"node:fs/promises";import"@forinda/kickjs-cli-kit";import ze from"pluralize";import{arch as Be,platform as Ve,release as He}from"node:os";const Ue=[{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 We(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 | minimal | fullstack`).option(`--runtime <engine>`,`HTTP runtime: express | fastify | h3`).option(`-r, --repo <type>`,`Repository name (inmemory, or any DB name e.g. postgres)`).option(`-s, --schema <lib>`,`Schema library for env / DTOs: zod | valibot | yup (default: zod)`).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)=>{ee(`KickJS — Create a new project`);let r=!!(t.yes||t.nonInteractive);e||=r?`my-api`:await ae({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let i;if(e===`.`?(i=j(`.`),e=ke(i)):i=j(t.directory||e),w(i)){let n=E(i);if(n.length>0){if(t.force)v.warn(`Clearing existing files in ${i}`);else if(r){v.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),C(`Aborted.`);return}else{v.warn(`Directory "${e}" is not empty:`);let t=n.slice(0,5);for(let e of t)v.message(` - ${e}`);if(n.length>5&&v.message(` ... and ${n.length-5} more`),!await S({message:x.red(`Remove all existing files and proceed?`),initialValue:!1})){C(`Aborted.`);return}}for(let e of n)Ee(j(i,e),{recursive:!0,force:!0})}}let a=t.template;a||=r?`minimal`:await y({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`minimal`,label:`Minimal`,hint:`bare Express`},{value:`fullstack`,label:`Fullstack`,hint:`server + Vite React web, typed client`}]});let o=t.runtime;o||=r?`express`:await y({message:`HTTP runtime`,options:[{value:`express`,label:`Express`,hint:`default, zero-config`},{value:`fastify`,label:`Fastify`,hint:`fastify + @fastify/middie`},{value:`h3`,label:`h3`,hint:`Nitro / Nuxt engine`}]});let c=t.pm;c||=r?await n(void 0):await y({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||=r?`inmemory`:await ae({message:`Repository name`,placeholder:`inmemory (or a DB name, e.g. postgres)`,defaultValue:`inmemory`}),d(l);let u=t.schema;u||=r?`zod`:await y({message:`Schema library (env + DTO validation)`,options:[{value:`zod`,label:`Zod`,hint:`default — broad ecosystem`},{value:`valibot`,label:`Valibot`,hint:`smaller bundle`},{value:`yup`,label:`Yup`,hint:`classic API`}]}),[`zod`,`valibot`,`yup`].includes(u)||(v.warn(`Unknown --schema "${u}", falling back to zod.`),u=`zod`);let f;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();f=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else f=r?[]:await re({message:`Select packages to include`,options:[...Ue],required:!1});let p;p=t.git===void 0?r?!0:await S({message:`Initialize git repository?`,initialValue:!0}):t.git;let m;if(m=t.install===void 0?r?!0:await S({message:`Install dependencies?`,initialValue:!0}):t.install,a===`fullstack`){let{initFullstackProject:t}=await import(`./fullstack-B62AJ-Q0.mjs`);await t({name:e,directory:i,packageManager:c,initGit:p,installDeps:m,schemaLib:u,runtime:o}),C(`Done! Next steps: ${x.cyan(`cd ${e} && ${c}${c===`pnpm`?` dev`:` run dev:server`}`)}`);return}await s({name:e,directory:i,packageManager:c,initGit:p,installDeps:m,template:a,defaultRepo:l,packages:f,schemaLib:u,runtime:o}),C(`Done! Next steps: ${x.cyan(`cd ${e} && ${c} dev`)}`)})}function F(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function I(e){let t=F(e);return t.charAt(0).toLowerCase()+t.slice(1)}function L(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function R(e){return ze.plural(e)}function Ge(e){return ze.plural(e)}function Ke(e){return L(e).replace(/-/g,`_`)}function qe(e){let t=e.cwd??process.cwd(),n=e.projectRoot??oe(t),r=e.pluralize??!0,i=F(e.name),a=I(e.name),o=L(e.name),s=Ke(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=R(o);c.pluralKebab=e,c.pluralPascal=F(e),c.pluralCamel=I(e)}return c}function Je(e,t){return j(e.cwd,t)}async function Ye(e){return import(M(e).href)}const Xe=new Map;async function Ze(e){let t=Xe.get(e);if(t)return t;let n=Qe(e);return Xe.set(e,n),n}async function Qe(e){let t=j(e,`package.json`);if(!w(t))return{generators:[],loaded:[],failed:[]};let n=$e(JSON.parse(await N(t,`utf-8`))),r=Ce(j(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await N(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=j(O(t),s);if(!w(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await Ye(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!et(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function $e(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 et(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function tt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return it(r.spec,r.source,e,n);let i=rt(await Ze(n),e.generatorName);return i?it(i.spec,i.source,e,n):null}async function nt(e,t=[]){let n=await Ze(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 rt(e,t){return e.generators.find(e=>e.spec.name===t)}async function it(e,t,n,r){let i=qe({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=Je(i,e.path);await g(t,e.content),o.push(t)}return{files:o,source:t}}function z(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function at(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function ot(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function st(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]??`${at(n)}${e}Repository`,repoFile:i[n]??`${ot(n)}-${t}`}}function ct(e){return e??`define`}function lt(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=st(t,n,i),c=ct(a),l=`/**
|
|
12
12
|
* ${t} Module
|
|
13
13
|
*
|
|
14
14
|
* REST module with a flat folder structure.
|
|
@@ -1269,7 +1269,7 @@ describe('${o}', () => {
|
|
|
1269
1269
|
Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
|
|
1270
1270
|
Optional: append :optional (shell-safe, no quoting needed)
|
|
1271
1271
|
`),process.exit(1));let a=await f(process.cwd()),o=u(a),s=n.modulesDir??o.dir??`src/modules`,c=rn(t),d=l(a,process.cwd()),p=await an({name:e,fields:c,modulesDir:j(s),noEntity:n.entity===!1,noTests:n.tests===!1,pluralize:n.pluralize===!1?!1:o.pluralize??!0,tokenScope:d,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)`:``}`);G(p,i),await fn(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>`,`Repository name: inmemory (default) or any DB name`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=W(t);h(n),G(await Ut({outDir:j(`.`),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 | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=W(t);h(n);let r=e.only??`all`;if(!dn.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${dn.join(` | `)}`),process.exitCode=1;return}G(await ne({outDir:j(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)});for(let e of t?.generators??[])vn(n,e,t?.projectRoot)}function vn(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=W(o);h(s);let c=await tt({generatorName:i.name,itemName:e??``,args:r??[],flags:a,cwd:process.cwd(),projectRoot:n},[t]);c&&G(c.files,s)})}async function yn(e){let t=D.resolve(e.cwd,`.kickjs/types`);e.check||await Fe(t,{recursive:!0});let n=new Map,r=e.scan??fe,i=D.resolve(e.cwd,`.kickjs`,`cache`),a=e.scan?void 0:e.changedFiles,o={cwd:e.cwd,config:e.config,async importTs(e){return await import(M(e).href)},async writeFile(t,n){let r=D.resolve(e.cwd,t);await Fe(D.dirname(r),{recursive:!0}),await P(r,n,`utf8`)},getScanResult:e=>{let t=bn(e),o=n.get(t);if(!o){let s={cacheDir:i,...e};o=a?de(s,a):r(s),n.set(t,o)}return o},log:console},s=[],c=[];for(let n of e.plugins){let r=n.outExtension??`.d.ts`,i=D.join(t,`${n.id.replace(/\//g,`__`)}${r}`),a;try{a=await n.generate(o)}catch(t){let r=t instanceof Error?t.message:String(t);if(e.check)throw Error(`kick typegen --check: ${n.id} failed to generate (${r})`,{cause:t});o.log.error(` ${n.id}: typegen failed (${r}) — keeping previous output`),s.push({id:n.id,status:`error`,outFile:i});continue}if(a===null){s.push({id:n.id,status:`skipped`});continue}let l=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+a+`
|
|
1272
|
-
`,u=``;if(w(i)&&(u=await N(i,`utf8`)),u===l){s.push({id:n.id,status:`unchanged`,outFile:i});continue}if(e.check){c.push({id:n.id,outFile:i}),s.push({id:n.id,status:`drifted`,outFile:i});continue}await P(i,l,`utf8`),s.push({id:n.id,status:`written`,outFile:i})}if(c.length>0)throw new un(c);return s}function bn(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 xn(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 Sn=e({applyDisableFilter:()=>xn,runAllPluginTypegens:()=>wn});function Cn(){let e=(process.env.LOG_LEVEL??process.env.KICKJS_LOG_LEVEL??``).toLowerCase();return e===`debug`||e===`trace`}async function wn(e){let{enabled:t,skipped:n,unknown:r}=xn(p([...
|
|
1272
|
+
`,u=``;if(w(i)&&(u=await N(i,`utf8`)),u===l){s.push({id:n.id,status:`unchanged`,outFile:i});continue}if(e.check){c.push({id:n.id,outFile:i}),s.push({id:n.id,status:`drifted`,outFile:i});continue}await P(i,l,`utf8`),s.push({id:n.id,status:`written`,outFile:i})}if(c.length>0)throw new un(c);return s}function bn(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 xn(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 Sn=e({applyDisableFilter:()=>xn,runAllPluginTypegens:()=>wn});function Cn(){let e=(process.env.LOG_LEVEL??process.env.KICKJS_LOG_LEVEL??``).toLowerCase();return e===`debug`||e===`trace`}async function wn(e){let{enabled:t,skipped:n,unknown:r}=xn(p([...Ki,...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 yn({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check,changedFiles:e.changedFiles});if(!e.silent&&Cn())for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(t instanceof un||e.check)throw t;if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}function Tn(e){let t=new Map;return{report(n,r){let i=r instanceof Error?r.message:String(r);t.get(n)!==i&&(t.set(n,i),e(` kick typegen: ${n} pass failed (${i}) — types in .kickjs/types may be stale`))},clear(e){t.delete(e)}}}function En(e){let{cwd:t,config:n}=e,r=e.debounceMs??100,i=e.pipeline??{runTypegen:async e=>(await import(`./typegen-BDsxBffM.mjs`).then(e=>e.n)).runTypegen(e),runAllPluginTypegens:async e=>(await Promise.resolve().then(()=>Sn)).runAllPluginTypegens(e),writeTypegenArtifacts:async(e,t,n)=>(await import(`./typegen-BDsxBffM.mjs`).then(e=>e.n)).writeTypegenArtifacts(e,t,n),buildAssets:async(e,t)=>(await import(`./build-C85VDZsR.mjs`).then(e=>e.n)).buildAssets(e,t)},a=n?.typegen?.schemaValidator??`zod`,o=n?.typegen?.envFile,s=D.resolve(t,n?.typegen?.outDir??`.kickjs/types`),c=n?.assetMap?Object.values(n.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>D.resolve(t,e)):[],l=!!n?.assetMap&&Object.keys(n.assetMap).length>0,u=e=>e.replaceAll(`\\`,`/`),d=c.map(u),f=e=>{let t=u(e);return d.some(e=>t===e||t.startsWith(`${e}/`))},p=Tn(e.emitWarning),m=null,h=!1,g=new Set,_=new Set,v=!1,y=!1;function b(r,c){i.runTypegen({cwd:t,silent:!0,allowDuplicates:!0,schemaValidator:a,envFile:o,srcDir:n?.typegen?.srcDir,outDir:n?.typegen?.outDir,assetMap:n?.assetMap,changedFiles:r,runPlugins:!1}).then(()=>p.clear(`scan`)).catch(e=>p.report(`scan`,e)),i.runAllPluginTypegens({cwd:t,config:n,silent:!0,changedFiles:r}).then(e=>i.writeTypegenArtifacts(s,e,!0)).then(()=>p.clear(`plugins`)).catch(e=>p.report(`plugins`,e)).finally(()=>e.onPassComplete?.()),c&&n&&i.buildAssets(n,{cwd:t,silent:!0}).catch(()=>{})}function x(){let e=v?void 0:{changed:[...g],removed:[..._]},t=y;g.clear(),_.clear(),v=!1,y=!1,b(e,t)}return{assetSrcRoots:c,handleWatchEvent(e,t){if(!h&&!u(t).includes(`/.kickjs/`)){if(e===`unlinkDir`)v=!0,l&&(y=!0);else{if(t.endsWith(`.d.ts`))return;let n=/\.(ts|tsx|mts|cts)$/.test(t),r=f(t);if(!n&&!r)return;r&&l&&(y=!0),n&&(e===`unlink`?(_.add(t),g.delete(t)):(g.add(t),_.delete(t)))}m&&clearTimeout(m),m=setTimeout(x,r)}},runOnce(){h||b(void 0,l)},dispose(){h=!0,m&&clearTimeout(m),m=null}}}function Dn(e){let t=k(e,`node_modules`,`.bin`),n=process.platform===`win32`;for(let e of[`tsgo`,`tsc`]){let r=n?[`${e}.CMD`,`${e}.cmd`,`${e}.exe`]:[e];for(let i of r){let r=k(t,i);if(w(r))return{cmd:r,args:[`--noEmit`],shell:n,kind:e}}}return null}function On(e){let t=e.spawnFn??Ne,n=null,r=0,i=!1;return{schedule(){if(i)return;let a=++r;n&&=(n.kill(),null);let o=Date.now(),s=t(e.bin.cmd,e.bin.args,{cwd:e.cwd,shell:e.bin.shell,stdio:[`ignore`,`pipe`,`pipe`]});n=s;let c=``;s.stdout?.on(`data`,e=>{c+=e.toString()}),s.stderr?.on(`data`,e=>{c+=e.toString()}),s.on(`error`,()=>{a===r&&(n=null)}),s.on(`close`,t=>{i||a!==r||(n=null,e.onResult({ok:t===0,output:c,durationMs:Date.now()-o,kind:e.bin.kind}))})},dispose(){i=!0,n&&=(n.kill(),null)}}}function kn(e,t=12){let n=e.trim().split(/\r?\n/);return n.length<=t?n.join(`
|
|
1273
1273
|
`):`${n.slice(0,t).join(`
|
|
1274
1274
|
`)}\n… ${n.length-t} more line(s)`}function An(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function jn(e,t,n={}){t&&(process.env.PORT=t);let r=An(n.polling),i=process.cwd(),a=await f(i),o=a?.typegen?.schemaValidator??`zod`,s=a?.typegen?.envFile;try{await be({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})`)}let c=j(i,a?.typegen?.outDir??`.kickjs/types`);try{await pe(c,await wn({cwd:i,config:a}),!1)}catch(e){console.warn(` kick typegen: plugin pass skipped (${e?.message??e})`)}let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(M(l(j(`package.json`)).resolve(`vite`)).href);globalThis.__kickjs_typegen_owner=`kick-dev`;let d=await u({configFile:j(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...r?{watch:{usePolling:!0,interval:100}}:{}}}),p=n.typecheck??a?.dev?.typecheck??!1,m=null,h=!0;if(p){let e=Dn(i);e?m=On({cwd:i,bin:e,onResult:e=>{d.hot.send({type:`custom`,event:`kickjs:typecheck`,data:{ok:e.ok,output:e.output,durationMs:e.durationMs}}),e.ok?h||(h=!0,console.log(` kick typecheck: clean again (${e.kind}, ${e.durationMs}ms)`)):(h=!1,console.warn(`\n kick typecheck (${e.kind}, ${e.durationMs}ms):`),console.warn(kn(e.output).replace(/^/gm,` `)))}}):console.warn(` kick dev: --typecheck requested but neither tsgo (@typescript/native-preview) nor typescript is installed in this project — skipping type checks.`)}let g=En({cwd:i,config:a,emitWarning:e=>{console.warn(e),d.hot.send({type:`custom`,event:`kickjs:typegen-error`,data:{message:e,timestamp:Date.now()}})},onPassComplete:()=>m?.schedule()});d.watcher.on(`add`,e=>g.handleWatchEvent(`add`,e)),d.watcher.on(`unlink`,e=>g.handleWatchEvent(`unlink`,e)),d.watcher.on(`change`,e=>g.handleWatchEvent(`change`,e)),d.watcher.on(`unlinkDir`,e=>g.handleWatchEvent(`unlinkDir`,e)),g.assetSrcRoots.length>0&&d.watcher.add([...g.assetSrcRoots]),await d.listen(),d.printUrls(),console.log(`
|
|
1275
1275
|
KickJS dev server running (Vite + @forinda/kickjs-vite)
|
|
@@ -1362,7 +1362,41 @@ drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the
|
|
|
1362
1362
|
import { UserModule } from './users/user.module'
|
|
1363
1363
|
import { TaskModule } from './tasks/task.module' // ← was this missing?
|
|
1364
1364
|
|
|
1365
|
-
export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://kickjs.app/guide/project-structure.html`}}:null}}
|
|
1365
|
+
export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://kickjs.app/guide/project-structure.html`}}:null}},{match(e,t){if(!X(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`,`jest`]))return null;let n=X(e,[`wrong database`,`dev database`,`development database`,`production database`,`wrong db`,`dev db`,`connected to the wrong`,`wiped`,`truncated`,`deleted rows`,`sent a real email`,`real email`,`live api`]),r=X(e,[`stubenv`,`process.env`,`setupfiles`,`test.env`,`env override`])&&X(e,[`no effect`,`not applied`,`ignored`,`still`,`undefined`,`does nothing`,`doesn't work`,`not working`]);if(!n&&!r)return null;let i=t?.hasFile?.(`.env`)===!0||t?.hasFile?.(`.env.local`)===!0,a=t?.hasFile?.(`.env.test`)===!0||t?.hasFile?.(`.env.test.local`)===!0,o=i&&!a,s=n&&r?85:n?75:65;return o&&(s=Math.min(95,s+10)),{confidence:s,diagnosis:{id:`test-env-leaked-from-dotenv`,title:`Test run inherited env vars from your development .env`,explanation:`KickJS loads dotenv as an import-time side effect, with \`override: false\`.
|
|
1366
|
+
That protects the vars your test runner pinned — but every var it did NOT
|
|
1367
|
+
pin is silently backfilled from your \`.env\`. So a suite can pin its database
|
|
1368
|
+
URL and still reach live development services through the vars it forgot,
|
|
1369
|
+
and nothing in the run reports it.
|
|
1370
|
+
|
|
1371
|
+
The second symptom has the same root: \`loadEnv()\` parses process.env ONCE
|
|
1372
|
+
into a module-level cache, and ConfigService.get() / @Value() read that
|
|
1373
|
+
snapshot. A vi.stubEnv() in beforeAll runs long after the parse, so it is a
|
|
1374
|
+
silent no-op for them (it still works for code reading process.env directly).
|
|
1375
|
+
|
|
1376
|
+
Under a test run (NODE_ENV=test, or vitest's VITEST), KickJS reads \`.env.test\`
|
|
1377
|
+
if it exists — and only \`.env.test\`, with no fallback to \`.env\`. Falling
|
|
1378
|
+
through is the leak, so one file wins outright.`,fix:`Create a .env.test next to your .env holding the WHOLE environment the suite
|
|
1379
|
+
should run against. A var you leave out will be missing rather than
|
|
1380
|
+
inherited, which is the point — the run fails loudly instead of quietly
|
|
1381
|
+
reaching your dev stack.
|
|
1382
|
+
|
|
1383
|
+
To stub a var mid-suite, drop the cache and re-parse. To opt out of dotenv
|
|
1384
|
+
entirely, set KICKJS_ENV_FILE=off (it also takes a comma-separated file list).`,codeBefore:`# .env ← read by your tests today, including every var you forgot to pin
|
|
1385
|
+
DATABASE_URL=postgres://localhost:5432/myapp_dev
|
|
1386
|
+
`,codeAfter:`# .env.test ← read INSTEAD of .env under NODE_ENV=test / VITEST
|
|
1387
|
+
NODE_ENV=test
|
|
1388
|
+
DATABASE_URL=postgres://localhost:5432/myapp_test
|
|
1389
|
+
LOG_LEVEL=silent
|
|
1390
|
+
|
|
1391
|
+
// mid-suite stubs need the cache dropped:
|
|
1392
|
+
import { loadEnv, resetEnvCache } from '@forinda/kickjs'
|
|
1393
|
+
import { envSchema } from '../src/env'
|
|
1394
|
+
|
|
1395
|
+
beforeAll(() => {
|
|
1396
|
+
vi.stubEnv('JWT_SECRET', 'x'.repeat(32))
|
|
1397
|
+
resetEnvCache()
|
|
1398
|
+
loadEnv(envSchema)
|
|
1399
|
+
})`,docs:`https://kickjs.app/guide/testing.html#environment-isolation`}}}}];function Jn(e,t){let n=null;for(let r of qn){let i=null;try{i=r.match(e,t)}catch{continue}!i||i.confidence<40||(!n||i.confidence>n.confidence)&&(n=i)}return n}async function Yn(e){let t=e.provider??`openai`,n=process.env.OPENAI_API_KEY;if(t===`openai`&&!n)return{kind:`unavailable`,reason:`OPENAI_API_KEY environment variable is not set`,suggestion:`Set OPENAI_API_KEY in your shell, e.g.
|
|
1366
1400
|
export OPENAI_API_KEY="sk-..."
|
|
1367
1401
|
|
|
1368
1402
|
Then re-run \`kick explain --ai "<your error>"\`.`};let r;try{r=await import(`@forinda/kickjs-ai`)}catch{return{kind:`unavailable`,reason:`@forinda/kickjs-ai is not installed`,suggestion:`Install the AI package to enable the LLM fallback:
|
|
@@ -1461,7 +1495,7 @@ server.on('exit', () => {
|
|
|
1461
1495
|
Cancelled.
|
|
1462
1496
|
`);return}await Le(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=k(n,`index.ts`);if(await _(l)){let e=await N(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${z(o)}Module\\s*\\}\\s*from\\s*['"][^'"]*${z(s)}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=vr(e,o),e=e.replace(/\n{3,}/g,`
|
|
1463
1497
|
|
|
1464
|
-
`),e!==t&&(await P(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function br(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=u(await f(process.cwd())),r=t.modulesDir??n.dir??`src/modules`,i=t.pluralize===!1?!1:n.pluralize??!0;for(let n of e)await yr({name:n,modulesDir:j(r),force:t.force,pluralize:i})})}function xr(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;if(e===`kickjs-schema`||e===`schema`)return`kickjs-schema`;console.warn(` kick typegen: unknown --schema-validator '${e}' (supported: 'zod', 'kickjs-schema', 'false'). Falling back to project config.`)}}function Sr(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function Cr(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: exit non-zero if any generated file in .kickjs/types/ is out of date (routes, env, db, assets, adopter plugins) instead of writing it`).option(`--fix`,`Patch module import.meta.glob() calls to cover decorated classes that aren't loaded by any glob (orphans)`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").option(`--no-cache`,`Disable the persistent scan cache; re-read + re-extract every file from cold`).action(async e=>{let t=oe(process.cwd()),n=await f(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-
|
|
1498
|
+
`),e!==t&&(await P(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function br(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=u(await f(process.cwd())),r=t.modulesDir??n.dir??`src/modules`,i=t.pluralize===!1?!1:n.pluralize??!0;for(let n of e)await yr({name:n,modulesDir:j(r),force:t.force,pluralize:i})})}function xr(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;if(e===`kickjs-schema`||e===`schema`)return`kickjs-schema`;console.warn(` kick typegen: unknown --schema-validator '${e}' (supported: 'zod', 'kickjs-schema', 'false'). Falling back to project config.`)}}function Sr(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function Cr(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: exit non-zero if any generated file in .kickjs/types/ is out of date (routes, env, db, assets, adopter plugins) instead of writing it`).option(`--fix`,`Patch module import.meta.glob() calls to cover decorated classes that aren't loaded by any glob (orphans)`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").option(`--no-cache`,`Disable the persistent scan cache; re-read + re-extract every file from cold`).action(async e=>{let t=oe(process.cwd()),n=await f(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-D5_TIh7b.mjs`).then(e=>e.t),{builtinCliPlugins:t}=await Promise.resolve().then(()=>Gi),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(`
|
|
1465
1499
|
Registered typegen plugins:
|
|
1466
1500
|
`);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=xr(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,i=Sr(e.envFile)??n?.typegen?.envFile,a={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,fix:e.fix,allowDuplicates:e.allowDuplicates,noCache:e.cache===!1,schemaValidator:r,envFile:i,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await ve(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{await be(a);let r=await wn({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1),e.check||await pe(j(t,e.out??n?.typegen?.outDir??`.kickjs/types`),r,e.silent??!1)}}catch(e){e instanceof ye?console.error(`
|
|
1467
1501
|
`+e.message+`
|
|
@@ -1478,18 +1512,24 @@ Install it: pnpm add reflect-metadata
|
|
|
1478
1512
|
Then import it at the top of src/index.ts:
|
|
1479
1513
|
|
|
1480
1514
|
import 'reflect-metadata'
|
|
1481
|
-
// ... rest of bootstrap`}}function $r(e){if(e.tsconfig===void 0)return[{name:`tsconfig.json present`,status:`fail`,fix:"Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically."}];if(e.tsconfig===null)return[{name:`tsconfig.json readable`,status:`warn`,message:`could not parse tsconfig.json (or a config it extends)`,fix:"Check tsconfig.json for a syntax error, and that every path in its `extends` chain resolves. Decorator options could not be verified."}];let t=e.tsconfig.compilerOptions??{},n=[];return n.push(t.experimentalDecorators===!0?{name:`tsconfig: experimentalDecorators`,status:`pass`}:{name:`tsconfig: experimentalDecorators`,status:`fail`,fix:'Add `"experimentalDecorators": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\'t register any metadata at compile time.'}),n.push(t.emitDecoratorMetadata===!0?{name:`tsconfig: emitDecoratorMetadata`,status:`pass`}:{name:`tsconfig: emitDecoratorMetadata`,status:`fail`,fix:'Add `"emitDecoratorMetadata": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.'}),n}function ei(e){let t=[`src/env.ts`,`src/env/index.ts`,`src/config/env.ts`,`src/config/index.ts`].map(t=>k(e.cwd,t)).filter(e=>w(e)).filter(e=>/\bloadEnv\s*\(/.test(Lr(e)??``));if(t.length===0)return null;let n=[`src/index.ts`,`src/main.ts`].map(t=>k(e.cwd,t)).find(e=>w(e));if(!n)return{name:`env wiring`,status:`warn`,message:`env-init file exists but no src/index.ts or src/main.ts found`};let r=Lr(n)??``,i=O(n),a=[];for(let e of t){let t=A(i,e).replace(/\\/g,`/`).replace(/\.ts$/,``),n=t.startsWith(`.`)?t:`./`+t,r=n.replace(/\/index$/,``);a.push(n,r);let o=e.replace(/\\/g,`/`).match(/\/src\/(.+?)(?:\.ts)?$/);if(o){let e=`@/`+o[1],t=e.replace(/\/index$/,``);a.push(e,t)}}let o=-1;for(let e of new Set(a)){let t=RegExp(`^import\\s+(?:.*?from\\s+)?['"]${Ur(e)}['"]`,`m`),n=r.match(t);n&&n.index!==void 0&&(o===-1||n.index<o)&&(o=n.index)}let s=r.search(/\bbootstrap\s*\(/),c=t.map(t=>A(e.cwd,t).replace(/\\/g,`/`)).join(`, `);return o===-1?{name:`env wiring`,status:`fail`,message:c,fix:`An env-init file (${c}) calls \`loadEnv(...)\` but \`${A(e.cwd,n).replace(/\\/g,`/`)}\` doesn't import it.\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\n\nFix: add a side-effect import at the top of ${A(e.cwd,n).replace(/\\/g,`/`)} (above bootstrap()), pointing at one of the detected files. For example:\n\n import './env'\n // or\n import './config'\n // or, with the @/ alias:\n import '@/config/env'`}:s!==-1&&o>s?{name:`env wiring`,status:`warn`,message:`env-init imported AFTER bootstrap() — should be before`,fix:`Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`}:{name:`env wiring`,status:`pass`}}function ti(e,t=ni){let n=0,r=0,i=[e];for(;i.length>0&&r<t;){let e=i.pop(),a;try{a=E(e,{withFileTypes:!0})}catch{continue}for(let o of a){if(r>=t)break;r++;let a=k(e,o.name);if(o.isDirectory()){i.push(a);continue}try{let e=De(a).mtimeMs;e>n&&(n=e)}catch{}}}return n}const ni=2e3;function ri(e){let t=k(e.cwd,`.kickjs`,`types`);if(!w(t))return null;let n=ti(t);if(n===0)return null;let r=Date.now()-n,i=Math.floor(r/6e4);return i>60?{name:`typegen freshness`,status:`warn`,message:`last updated ${i} minutes ago`,fix:"Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code."}:{name:`typegen freshness`,status:`pass`,message:i===0?`just now`:`${i}m ago`}}const ii=[
|
|
1515
|
+
// ... rest of bootstrap`}}function $r(e){if(e.tsconfig===void 0)return[{name:`tsconfig.json present`,status:`fail`,fix:"Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically."}];if(e.tsconfig===null)return[{name:`tsconfig.json readable`,status:`warn`,message:`could not parse tsconfig.json (or a config it extends)`,fix:"Check tsconfig.json for a syntax error, and that every path in its `extends` chain resolves. Decorator options could not be verified."}];let t=e.tsconfig.compilerOptions??{},n=[];return n.push(t.experimentalDecorators===!0?{name:`tsconfig: experimentalDecorators`,status:`pass`}:{name:`tsconfig: experimentalDecorators`,status:`fail`,fix:'Add `"experimentalDecorators": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\'t register any metadata at compile time.'}),n.push(t.emitDecoratorMetadata===!0?{name:`tsconfig: emitDecoratorMetadata`,status:`pass`}:{name:`tsconfig: emitDecoratorMetadata`,status:`fail`,fix:'Add `"emitDecoratorMetadata": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.'}),n}function ei(e){let t=[`src/env.ts`,`src/env/index.ts`,`src/config/env.ts`,`src/config/index.ts`].map(t=>k(e.cwd,t)).filter(e=>w(e)).filter(e=>/\bloadEnv\s*\(/.test(Lr(e)??``));if(t.length===0)return null;let n=[`src/index.ts`,`src/main.ts`].map(t=>k(e.cwd,t)).find(e=>w(e));if(!n)return{name:`env wiring`,status:`warn`,message:`env-init file exists but no src/index.ts or src/main.ts found`};let r=Lr(n)??``,i=O(n),a=[];for(let e of t){let t=A(i,e).replace(/\\/g,`/`).replace(/\.ts$/,``),n=t.startsWith(`.`)?t:`./`+t,r=n.replace(/\/index$/,``);a.push(n,r);let o=e.replace(/\\/g,`/`).match(/\/src\/(.+?)(?:\.ts)?$/);if(o){let e=`@/`+o[1],t=e.replace(/\/index$/,``);a.push(e,t)}}let o=-1;for(let e of new Set(a)){let t=RegExp(`^import\\s+(?:.*?from\\s+)?['"]${Ur(e)}['"]`,`m`),n=r.match(t);n&&n.index!==void 0&&(o===-1||n.index<o)&&(o=n.index)}let s=r.search(/\bbootstrap\s*\(/),c=t.map(t=>A(e.cwd,t).replace(/\\/g,`/`)).join(`, `);return o===-1?{name:`env wiring`,status:`fail`,message:c,fix:`An env-init file (${c}) calls \`loadEnv(...)\` but \`${A(e.cwd,n).replace(/\\/g,`/`)}\` doesn't import it.\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\n\nFix: add a side-effect import at the top of ${A(e.cwd,n).replace(/\\/g,`/`)} (above bootstrap()), pointing at one of the detected files. For example:\n\n import './env'\n // or\n import './config'\n // or, with the @/ alias:\n import '@/config/env'`}:s!==-1&&o>s?{name:`env wiring`,status:`warn`,message:`env-init imported AFTER bootstrap() — should be before`,fix:`Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`}:{name:`env wiring`,status:`pass`}}function ti(e,t=ni){let n=0,r=0,i=[e];for(;i.length>0&&r<t;){let e=i.pop(),a;try{a=E(e,{withFileTypes:!0})}catch{continue}for(let o of a){if(r>=t)break;r++;let a=k(e,o.name);if(o.isDirectory()){i.push(a);continue}try{let e=De(a).mtimeMs;e>n&&(n=e)}catch{}}}return n}const ni=2e3;function ri(e){let t=k(e.cwd,`.kickjs`,`types`);if(!w(t))return null;let n=ti(t);if(n===0)return null;let r=Date.now()-n,i=Math.floor(r/6e4);return i>60?{name:`typegen freshness`,status:`warn`,message:`last updated ${i} minutes ago`,fix:"Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code."}:{name:`typegen freshness`,status:`pass`,message:i===0?`just now`:`${i}m ago`}}const ii=[`vitest.config.ts`,`vitest.config.js`,`vitest.config.mts`,`vite.config.ts`,`jest.config.ts`,`jest.config.js`],ai=[`.env.test`,`.env.test.local`],oi=[`.env`,`.env.local`];function si(e){let t=e.pkg?.scripts;if(t&&typeof t==`object`)for(let[e,n]of Object.entries(t)){if(!e.startsWith(`test`)||typeof n!=`string`)continue;let t=/KICKJS_ENV_FILE=(\S+)/.exec(n);if(t?.[1])return t[1]}return process.env.KICKJS_ENV_FILE??null}function ci(e){if(!ii.some(t=>w(k(e.cwd,t))))return null;let t=oi.filter(t=>w(k(e.cwd,t)));if(t.length===0)return null;let n=ai.filter(t=>w(k(e.cwd,t)));if(n.length>0){let t=si(e);return t&&t!==`off`&&t!==`none`?{name:`test env isolation`,status:`warn`,message:`${n.join(`, `)} present, but KICKJS_ENV_FILE=${t} overrides it`,fix:`KICKJS_ENV_FILE is checked before test-mode isolation, so ${n[0]} is\nignored and "${t}" is read instead. Drop the override to use the\ntest files, or set KICKJS_ENV_FILE=off if env comes from the runner.`}:{name:`test env isolation`,status:`pass`,message:`${n.join(`, `)} present`}}return{name:`test env isolation`,status:`warn`,message:`${t.join(`, `)} present, no .env.test`,fix:`Under NODE_ENV=test (or vitest's VITEST) KickJS reads .env.test / .env.test.local
|
|
1516
|
+
INSTEAD of ${t.join(` and `)} — no layering, no fallback. Without one of\nthose files, every var your test config does not pin resolves from your
|
|
1517
|
+
development env — including DATABASE_URL and any third-party credentials.
|
|
1518
|
+
|
|
1519
|
+
Create .env.test holding the whole environment the suite should run against
|
|
1520
|
+
(a var you leave out is then missing, not inherited), or set KICKJS_ENV_FILE=off
|
|
1521
|
+
and supply env from the test runner.`}}const li=[()=>Wr(),Gr,Kr,Jr,Yr,Qr,$r,ei,ci,ri];async function ui(e,t={}){let n={cwd:e,pkg:Ir(k(e,`package.json`)),tsconfig:zr(e),runtime:t.runtime??`express`},r=[...li,...t.extraChecks??[]],i=[];for(let e of r){let t;try{t=await e(n)}catch(t){i.push({name:e.name||`doctor check`,status:`fail`,message:t instanceof Error?t.message:String(t)});continue}t!=null&&(Array.isArray(t)?i.push(...t):i.push(t))}return i}function di(e){switch(e){case`pass`:return x.green(`✔`);case`warn`:return x.yellow(`⚠`);case`fail`:return x.red(`✖`)}}function fi(e){let t=di(e.status),n=e.message?` ${x.dim(`(${e.message})`)}`:``;return`${t} ${e.name}${n}`}function pi(e){return e.split(`
|
|
1482
1522
|
`).map(e=>` ${x.dim(`→`)} ${e}`).join(`
|
|
1483
|
-
`)}function
|
|
1523
|
+
`)}function mi(e){return e?.doctor?.checks??[]}function hi(e){e.command(`doctor`).description(`Pre-flight checks for your KickJS project (dev environment health)`).action(async()=>{let e=process.cwd(),t=mi(await f(e)),n=await o(e);ee(`KickJS Doctor`);let r=await ui(e,{extraChecks:t,runtime:n});for(let e of r)v.message(fi(e)),e.fix&&e.status!==`pass`&&v.message(pi(e.fix));let i=r.filter(e=>e.status===`pass`).length,a=r.filter(e=>e.status===`warn`).length,s=r.filter(e=>e.status===`fail`).length,c=[x.green(`${i} passed`),a>0?x.yellow(`${a} warning${a===1?``:`s`}`):`${a} warnings`,s>0?x.red(`${s} error${s===1?``:`s`}`):`${s} errors`].join(`, `);s>0?(C(`${c} — fix the errors above before running the app`),process.exit(1)):C(a>0?`${c} — review the warnings`:x.green(`${c} — your environment looks good`))})}function gi(e){return e.optsWithGlobals().dryRun??!1}function _i(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.
|
|
1484
1524
|
Direction defaults to \`modules.style\` from kick.config (or "define").
|
|
1485
1525
|
--target define|class Override the migration direction.
|
|
1486
1526
|
--apply Apply the changes (default: dry-run preview).
|
|
1487
|
-
--experimental Acknowledge that AST migration is experimental.`).option(`--modules-dir <dir>`,`Modules directory (default: src/modules from kick.config)`).option(`--apply`,`Apply the migration to disk (default: dry-run)`).option(`--experimental`,`Acknowledge that this command is experimental`).option(`--target <style>`,`Migration direction — 'define' or 'class'`).option(`--no-backup`,`Skip the .kickjs/codemod-backups/ snapshot (default: backup on)`).action(async(e,t)=>{let n=
|
|
1527
|
+
--experimental Acknowledge that AST migration is experimental.`).option(`--modules-dir <dir>`,`Modules directory (default: src/modules from kick.config)`).option(`--apply`,`Apply the migration to disk (default: dry-run)`).option(`--experimental`,`Acknowledge that this command is experimental`).option(`--target <style>`,`Migration direction — 'define' or 'class'`).option(`--no-backup`,`Skip the .kickjs/codemod-backups/ snapshot (default: backup on)`).action(async(e,t)=>{let n=gi(t)||!e.apply;h(n),e.experimental||(console.error(`
|
|
1488
1528
|
`+x.red(`Error:`)+` kick codemod modules is experimental — pass --experimental to acknowledge.
|
|
1489
1529
|
The regex-based rewrite handles the shapes our templates produce.
|
|
1490
1530
|
Hand-rolled modules with non-standard structures may be skipped.
|
|
1491
1531
|
Always commit before running with --apply.
|
|
1492
|
-
`),process.exit(1));let r=u(await f(process.cwd())),i=j(e.modulesDir??r.dir??`src/modules`),a;e.target===`define`||e.target===`class`?a=e.target:e.target===void 0?a=r.style??`define`:(console.error(`\n ${x.red(`Error:`)} --target must be 'define' or 'class' (got '${e.target}').\n`),process.exit(1));let o=x.dim(`→ ${a}`),s=n?x.dim(`(dry-run)`):x.bold(`(applying)`);console.log(`\n ${x.bold(`kick codemod modules`)} ${o} ${s}`),console.log(` modulesDir: ${x.dim(i)}\n`);let c=e.backup!==!1&&!n,l=await $t(i,{dryRun:n,target:a,backup:c});if(l.backupDir){let e=l.backupDir;console.log(` ${x.green(`✓`)} backup: ${x.dim(e)}\n ${x.dim(`(restore: rm -rf <modulesDir> && mv "<backup>" <modulesDir>)`)}\n`)}else!n&&e.backup===!1&&console.log(` ${x.dim(`(--no-backup — skipping snapshot)`)}\n`);let d=0,p=0;for(let e of l.files)if(e.status===`migrated`)d++,console.log(` ${x.green(`✓`)} ${e.path}`);else{p++;let t=x.dim(`(${e.reason??`skipped`})`);console.log(` ${x.dim(`-`)} ${e.path} ${t}`)}if(console.log(),l.indexStatus===`migrated`)console.log(` ${x.green(`✓`)} ${l.indexPath}`);else if(l.indexStatus===`skipped`){let e=x.dim(`(${l.indexReason??`skipped`})`);console.log(` ${x.dim(`-`)} ${l.indexPath} ${e}`)}else console.log(` ${x.dim(`-`)} ${l.indexPath} ${x.dim(`(not found)`)}`);let m=n?x.dim(` (dry-run — pass --apply to write)`):``;console.log(`\n ${x.bold(String(d))} migrated, ${x.bold(String(p))} skipped${m}\n`)})}const
|
|
1532
|
+
`),process.exit(1));let r=u(await f(process.cwd())),i=j(e.modulesDir??r.dir??`src/modules`),a;e.target===`define`||e.target===`class`?a=e.target:e.target===void 0?a=r.style??`define`:(console.error(`\n ${x.red(`Error:`)} --target must be 'define' or 'class' (got '${e.target}').\n`),process.exit(1));let o=x.dim(`→ ${a}`),s=n?x.dim(`(dry-run)`):x.bold(`(applying)`);console.log(`\n ${x.bold(`kick codemod modules`)} ${o} ${s}`),console.log(` modulesDir: ${x.dim(i)}\n`);let c=e.backup!==!1&&!n,l=await $t(i,{dryRun:n,target:a,backup:c});if(l.backupDir){let e=l.backupDir;console.log(` ${x.green(`✓`)} backup: ${x.dim(e)}\n ${x.dim(`(restore: rm -rf <modulesDir> && mv "<backup>" <modulesDir>)`)}\n`)}else!n&&e.backup===!1&&console.log(` ${x.dim(`(--no-backup — skipping snapshot)`)}\n`);let d=0,p=0;for(let e of l.files)if(e.status===`migrated`)d++,console.log(` ${x.green(`✓`)} ${e.path}`);else{p++;let t=x.dim(`(${e.reason??`skipped`})`);console.log(` ${x.dim(`-`)} ${e.path} ${t}`)}if(console.log(),l.indexStatus===`migrated`)console.log(` ${x.green(`✓`)} ${l.indexPath}`);else if(l.indexStatus===`skipped`){let e=x.dim(`(${l.indexReason??`skipped`})`);console.log(` ${x.dim(`-`)} ${l.indexPath} ${e}`)}else console.log(` ${x.dim(`-`)} ${l.indexPath} ${x.dim(`(not found)`)}`);let m=n?x.dim(` (dry-run — pass --apply to write)`):``;console.log(`\n ${x.bold(String(d))} migrated, ${x.bold(String(p))} skipped${m}\n`)})}const vi=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!w(D.resolve(e.cwd,`kick.config.ts`)))return null;let t=await f(e.cwd);if(!t?.assetMap)return null;let n=se(t.assetMap,e.cwd);return n.count===0?null:ge(n)}}),yi="/* eslint-disable */\n// AUTO-GENERATED by `kick typegen`. DO NOT EDIT.\n// Re-run with `kick typegen` or rely on `kick dev` to refresh.\n";function bi(e,t,n,r={}){if(e.length===0)return`${yi}
|
|
1493
1533
|
// (no routes discovered yet — annotate a controller method with
|
|
1494
1534
|
// @Get/@Post/@Put/@Delete/@Patch and re-run \`kick typegen\`)
|
|
1495
1535
|
declare global {
|
|
@@ -1506,14 +1546,14 @@ declare global {
|
|
|
1506
1546
|
|
|
1507
1547
|
/** Empty until the first route exists — see kick typegen. */
|
|
1508
1548
|
export const kickRpc = {} as const
|
|
1509
|
-
`;let i=new Map;for(let t of e){let e=i.get(t.controller)??[];e.push(t),i.set(t.controller,e)}let a=new Map,o=new Map,s=e=>{let t=`${e.filePath}::${e.controller}`,n=o.get(t);return n||(n=`_C${o.size}`,o.set(t,n)),n},c=(e,i,o)=>{let s=
|
|
1549
|
+
`;let i=new Map;for(let t of e){let e=i.get(t.controller)??[];e.push(t),i.set(t.controller,e)}let a=new Map,o=new Map,s=e=>{let t=`${e.filePath}::${e.controller}`,n=o.get(t);return n||(n=`_C${o.size}`,o.set(t,n)),n},c=(e,i,o)=>{let s=wi(e,i.filePath,t,n,a);if(!s){if(e&&n!==!1){let t=o===`params`?`URL-pattern params`:o===`query`&&i.queryFilterable!==null?`the @ApiQueryParams-derived query shape`:`'unknown'`;r.onWarn?.(`route ${i.controller}.${i.method} (${i.httpMethod} ${i.path}): ${o} schema '${e.identifier}' could not be statically resolved — falling back to ${t}. Export the schema from the controller file or import it with a static specifier.`)}return null}return n===`kickjs-schema`?`import('@forinda/kickjs-schema').InferSchemaOutput<typeof ${s}>`:`import('zod').infer<typeof ${s}>`},l=[];for(let[e,t]of i){let n=[` interface ${e} {`];for(let e of t){let t=e.pathParams.length>0?`{ ${e.pathParams.map(e=>`${e}: string`).join(`; `)} }`:`{}`,r=c(e.bodySchema,e,`body`),i=c(e.querySchema,e,`query`),a=c(e.paramsSchema,e,`params`)??t,o=r??`unknown`,l=i??Si(e),u=c(e.responseSchema??null,e,`response`),d=s(e),f=u??`import('@forinda/kickjs').InferHandlerResponse<${d}['${e.method}']>`,p=Ci(e);n.push(` /**`,` * ${e.httpMethod} ${e.path}`,...p.map(e=>` * ${e}`),` */`,` ${e.method}: {`,` params: ${a}`,` body: ${o}`,` query: ${l}`,` response: ${f}`,` contextKeys: ${xi(e)}`,` }`)}n.push(` }`),l.push(n.join(`
|
|
1510
1550
|
`))}let u=[],d=new Set,f=new Set;i.has(`Api`)&&r.onWarn?.(`controller class 'Api' collides with the reserved KickRoutes.Api client map — its interface declaration-merges into the flat route map. Rename the controller.`);for(let[e,t]of i)for(let n of t){let t=`${n.httpMethod} ${n.mountedPath??n.path}`;if(d.has(t)){r.onWarn?.(`duplicate route '${t}' (${e}.${n.method}) — two handlers claim the same verb+path at runtime. KickRoutes.Api keeps the first (scan order, which may not match runtime dispatch); resolve the conflict in the modules.`);continue}d.add(t),f.add(`${e}.${n.method}`),u.push(` '${t}': ${e}['${n.method}']`)}let p=[` interface Api {`,...u,` }`].join(`
|
|
1511
|
-
`);l.push(p);let m=[],h=new Set;for(let[e,t]of i){let n=
|
|
1551
|
+
`);l.push(p);let m=[],h=new Set;for(let[e,t]of i){let n=Di(e);if(h.has(n)){r.onWarn?.(`RPC manifest: controller key '${n}' (from ${e}) collides with another controller — keeping the first; rename one class for distinct RPC namespaces.`);continue}h.add(n);let i=[];for(let n of t){if(!f.has(`${e}.${n.method}`))continue;let t=`${n.httpMethod} ${n.mountedPath??n.path}`;i.push(` ${n.method}: '${t}',`)}i.length>0&&m.push(` ${n}: {\n${i.join(`
|
|
1512
1552
|
`)}\n },`)}let g=[`/**`,` * Runtime route manifest for the tRPC-style sugar:`," * `createRpc(api, kickRpc)` from @forinda/kickjs-client.",` */`,`export const kickRpc = {`,...m,`} as const`].join(`
|
|
1513
|
-
`),_=[],v=new Map;for(let t of e)v.set(`${t.filePath}::${t.controller}`,t);for(let[e,n]of o){let r=v.get(e),i=
|
|
1553
|
+
`),_=[],v=new Map;for(let t of e)v.set(`${t.filePath}::${t.controller}`,t);for(let[e,n]of o){let r=v.get(e),i=Ei(``,r.filePath,t),a=r.controllerIsDefaultExport?`default as ${n}`:`${r.controller} as ${n}`;_.push(`import type { ${a} } from '${i}'`)}let y=_.length>0?_.join(`
|
|
1514
1554
|
`)+`
|
|
1515
|
-
`:``,b=
|
|
1516
|
-
`);return`${
|
|
1555
|
+
`:``,b=Ti(a)+y,x=l.join(`
|
|
1556
|
+
`);return`${yi}${b}
|
|
1517
1557
|
declare global {
|
|
1518
1558
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
1519
1559
|
namespace KickRoutes {
|
|
@@ -1525,9 +1565,9 @@ ${x}
|
|
|
1525
1565
|
}
|
|
1526
1566
|
|
|
1527
1567
|
${g}
|
|
1528
|
-
`}function
|
|
1568
|
+
`}function xi(e){let t=e.contextKeys;return t==null?`string`:t.length===0?`never`:t.map(e=>JSON.stringify(e)).join(` | `)}function Si(e){if(e.queryFilterable===null)return`unknown`;let t=e.querySortable??[];return`{ filter?: string | string[]; sort?: ${t.length>0?t.flatMap(e=>[`'${e}'`,`'-${e}'`]).join(` | `):`string`}; q?: string; page?: string; limit?: string }`}function Ci(e){let t=[];return e.queryFilterable&&e.queryFilterable.length>0&&t.push(`Filterable: ${e.queryFilterable.join(`, `)}`),e.querySortable&&e.querySortable.length>0&&t.push(`Sortable: ${e.querySortable.join(`, `)}`),e.querySearchable&&e.querySearchable.length>0&&t.push(`Searchable: ${e.querySearchable.join(`, `)}`),t}function wi(e,t,n,r,i){if(!e||r===!1||e.source===null)return null;let a=Ei(e.source,t,n);if(a===`unknown`)return null;let o=`${a}::${e.identifier}`,s=i.get(o)?.specifier;return s?s=i.get(o).specifier:(s=`_S${i.size}`,i.set(o,{identifier:e.identifier,specifier:s})),s}function Ti(e){if(e.size===0)return``;let t=[];for(let[n,r]of e){let[e]=n.split(`::`);t.push(`import type { ${r.identifier} as ${r.specifier} } from '${e}'`)}return t.join(`
|
|
1529
1569
|
`)+`
|
|
1530
|
-
`}function
|
|
1570
|
+
`}function Ei(e,t,n){if(e===null)return`unknown`;let r=O(n);if(e===``){let e=A(r,t).split(Ae).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=A(r,j(O(t),e)).split(Ae).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}function Di(e){let t=e.endsWith(`Controller`)?e.slice(0,-10):e,n=t.length>0?t:e;return n.charAt(0).toLowerCase()+n.slice(1)}const Oi=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:ki(e),cwd:e.cwd,envFile:Ai(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=D.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return bi(t.routes,r,n,{onWarn:t=>e.log.warn(t)})}});function ki(e){return D.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Ai(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function ji(e,t,n=`zod`){if(!e)return null;let r=A(O(t),e.filePath).split(Ae).join(`/`);return r=r.replace(/\.(ts|tsx|mts|cts)$/i,``),r.startsWith(`.`)||(r=`./`+r),`/* eslint-disable */
|
|
1531
1571
|
// AUTO-GENERATED by \`kick typegen\`. DO NOT EDIT.
|
|
1532
1572
|
// Re-run with \`kick typegen\` or rely on \`kick dev\` to refresh.
|
|
1533
1573
|
|
|
@@ -1565,5 +1605,5 @@ declare global {
|
|
|
1565
1605
|
}
|
|
1566
1606
|
|
|
1567
1607
|
export {}
|
|
1568
|
-
`}const
|
|
1569
|
-
`)}});var
|
|
1608
|
+
`}const Mi=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=Pi(e);if(t===!1)return null;let n=await e.getScanResult({root:Ni(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=e.config?.typegen?.schemaValidator??`zod`,i=D.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return ji(n.env,i,r)}});function Ni(e){return D.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Pi(e){return e.config?.typegen?.envFile}function Fi(e){return D.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Ii(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function $(e){return{root:Fi(e),cwd:e.cwd,envFile:Ii(e)}}const Li=()=>({id:`kick/registry`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult($(e)),n=D.resolve(e.cwd,`.kickjs/types/kick__registry.d.ts`),r=new Set(t.collisions.map(e=>e.className));return _e(t.classes,n,r)}}),Ri=()=>({id:`kick/services`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult($(e)),n=new Set(t.collisions.map(e=>e.className));return he(`ServiceToken`,me(t.classes,t.tokens,t.injects,n),"(no tokens discovered — declare with createToken<T>() or `kick g service <name>`)")}}),zi=()=>({id:`kick/modules`,inputs:[`src/**/*.ts`],async generate(e){return he(`ModuleToken`,ce((await e.getScanResult($(e))).classes),"(no @Module classes discovered — `kick g module <name>` to add one)")}}),Bi=()=>({id:`kick/plugins`,inputs:[`src/**/*.ts`],async generate(e){return ue((await e.getScanResult($(e))).pluginsAndAdapters)}}),Vi=()=>({id:`kick/augmentations`,inputs:[`src/**/*.ts`],async generate(e){return xe((await e.getScanResult($(e))).augmentations)}}),Hi=()=>({id:`kick/context`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult($(e));return t.contextKeys.length===0?null:le(t.contextKeys)}}),Ui={fastify:{subpath:`@forinda/kickjs/fastify`,typeName:`FastifyRuntimeTypes`},h3:{subpath:`@forinda/kickjs/h3`,typeName:`H3RuntimeTypes`}},Wi=()=>({id:`kick/runtime`,outExtension:`.ts`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`],async generate(e){let t=e.config?.runtime;if(t!==`fastify`&&t!==`h3`)return null;let{subpath:n,typeName:r}=Ui[t];return[`// Runtime escape-hatch types for the '${t}' engine (kick.config runtime).`,`declare module '@forinda/kickjs' {`,` interface KickRuntimeRegister {`,` runtime: import('${n}').${r}`,` }`,`}`,``,`export {}`,``].join(`
|
|
1609
|
+
`)}});var Gi=e({builtinCliPlugins:()=>Ki});const Ki=[m({name:`kick/init`,register:We}),m({name:`kick/generate`,register:_n}),m({name:`kick/run`,register:Mn}),m({name:`kick/info`,register:Ln}),m({name:`kick/inspect`,register:Gn}),m({name:`kick/add`,register:t}),m({name:`kick/list`,register:i}),m({name:`kick/explain`,register:tr}),m({name:`kick/mcp`,register:ur}),m({name:`kick/tinker`,register:mr}),m({name:`kick/remove`,register:br}),m({name:`kick/typegen`,register:Cr}),m({name:`kick/check`,register:Fr}),m({name:`kick/doctor`,register:hi}),m({name:`kick/codemod`,register:_i}),m({name:`kick/registry`,typegens:[Li()]}),m({name:`kick/services`,typegens:[Ri()]}),m({name:`kick/modules`,typegens:[zi()]}),m({name:`kick/plugins`,typegens:[Bi()]}),m({name:`kick/augmentations`,typegens:[Vi()]}),m({name:`kick/context`,typegens:[Hi()]}),m({name:`kick/assets`,typegens:[vi()]}),m({name:`kick/routes`,typegens:[Oi()]}),m({name:`kick/env`,typegens:[Mi()]}),m({name:`kick/runtime`,typegens:[Wi()]})];export{Sn as n,Ki as t};
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{d as e}from"./project-
|
|
11
|
+
import{d as e}from"./project-ByxNz1NG.mjs";import{r as t}from"./config-DILd2DYP.mjs";import{n}from"./plugin-D5_TIh7b.mjs";import{t as r}from"./builtins-sU7Mboz-.mjs";import{t as i}from"./project-root-DcnxAB8S.mjs";import{Command as a}from"commander";import{readFileSync as o}from"node:fs";import{dirname as s,join as c}from"node:path";import{fileURLToPath as l}from"node:url";function u(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}d(e,r)}}function d(t,n){let r=t.command(n.name).description(n.description);if(n.aliases)for(let e of n.aliases)r.alias(e);r.allowUnknownOption(!0),r.argument(`[args...]`,`Additional arguments passed to the command`),r.action(t=>{let r=t.join(` `),i=Array.isArray(n.steps)?n.steps:[n.steps];for(let t of i){let i=r?`${t} ${r}`:t;console.log(` $ ${i}`);try{e(i)}catch{console.error(` Command failed: ${n.name}`),process.exitCode=1;return}}})}const f=s(l(import.meta.url)),p=JSON.parse(o(c(f,`..`,`package.json`),`utf-8`));async function m(){let e=new a;e.name(`kick`).description(`KickJS — A production-grade, decorator-driven Node.js framework`).version(p.version);let o=i(process.cwd()),s=o,c=await t(s)??{},l=n([...r,...c.plugins??[]],c.commands??[]);await l.register(e,{cwd:s,projectRoot:o,config:c,log:e=>console.log(e)}),u(e,{...c,commands:l.commands}),e.showHelpAfterError();let d=process.argv.map(e=>e===`-v`?`--version`:e);await e.parseAsync(d)}m().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});export{};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v6.10.
|
|
2
|
+
* @forinda/kickjs-cli v6.10.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{t as e}from"./rolldown-runtime-
|
|
11
|
+
import{t as e}from"./rolldown-runtime-BEUS6jRR.mjs";import{existsSync as t,mkdirSync as n,readFileSync as r,writeFileSync as i}from"node:fs";import{isAbsolute as a,join as o,relative as s,resolve as c}from"node:path";import{access as l,readFile as u}from"node:fs/promises";var d=e({BUILTIN_REPO_TYPES:()=>p,DEPRECATED_REPO_TYPES:()=>m,PACKAGE_MANAGERS:()=>f,defineConfig:()=>g,loadKickConfig:()=>x,resolveModuleConfig:()=>y,resolveTokenScope:()=>_,validateAssetMap:()=>C,warnIfDeprecatedRepo:()=>h,writeAssetConfigSnapshot:()=>S});const f=[`pnpm`,`npm`,`yarn`,`bun`],p=[`inmemory`],m=[`prisma`,`drizzle`];function h(e){return m.includes(e)?(console.warn(` Note: the '${e}' repository preset is deprecated. Generating a generic custom repository named '${e}' instead — wire it to your DB by hand. Pass any name via \`--repo <name>\` or \`modules.repo: { name: '<name>' }\`.`),!0):!1}function g(e){return e}function _(e,n){if(e?.tokenScope&&typeof e.tokenScope==`string`&&e.tokenScope.length>0){let t=v(e.tokenScope);if(t.length>0)return t}try{let e=o(n,`package.json`);if(t(e)){let t=JSON.parse(r(e,`utf-8`));if(typeof t.name==`string`&&t.name.length>0){let e=t.name.match(/^@([^/]+)\//),n=v(e?e[1]:t.name);if(n.length>0)return n}}}catch{}return`app`}function v(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,`-`).replace(/^-+|-+$/g,``).replace(/-{2,}/g,`-`)}function y(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`&&!p.includes(t.repo)&&(h(t.repo)||console.warn(` Warning: modules.repo '${t.repo}' is not a built-in type (${p.join(`, `)}). It will generate a stub repository. Use { name: '${t.repo}' } to silence this warning.`)),t}const b=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];async function x(e){let{findProjectRoot:t}=await import(`./project-root-DcnxAB8S.mjs`).then(e=>e.n),n=t(e);for(let e of b){let t=o(n,e);try{await l(t)}catch{continue}if(e.endsWith(`.json`)){let e=await u(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=C(e,n);for(let e of i)console.warn(` Warning: ${e}`);return S(n,e),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=C(i,n);for(let e of a)console.warn(` Warning: ${e}`);return S(n,i),i}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`Warning: Failed to load ${e}: ${n}`);continue}}return null}function S(e,t){if(!(!t?.assetMap||Object.keys(t.assetMap).length===0))try{let r=o(e,`.kickjs`);n(r,{recursive:!0});let a={version:1,assetMap:t.assetMap,...t.build?.outDir?{build:{outDir:t.build.outDir}}:{}};i(o(r,`kick.config.json`),JSON.stringify(a,null,2)+`
|
|
12
12
|
`,`utf-8`)}catch{}}function C(e,n){let r=[];if(!e?.assetMap)return r;let i=c(n);for(let[a,o]of Object.entries(e.assetMap)){if(!a||a.includes(`/`)){r.push(`assetMap key '${a}' is invalid — must be a non-empty string without '/'`);continue}if(typeof o?.src!=`string`||o.src.length===0){r.push(`assetMap.${a} is missing a non-empty 'src' field`);continue}t(c(n,o.src))||r.push(`assetMap.${a}.src ('${o.src}') does not exist — typegen + build will fail`),o.dest&&w(c(n,o.dest),i)&&r.push(`assetMap.${a}.dest ('${o.dest}') resolves outside the project root — refusing to copy`)}return r}function w(e,t){let n=s(t,e);return n===``?!1:n.startsWith(`..`)||a(n)}export{_ as a,y as i,d as n,h as o,x as r,f as t};
|