startx 1.1.11 → 1.1.31

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.
@@ -215,7 +215,7 @@ $&`).replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g,`$1$2`).replace(/\
215
215
  `+t:``),o=Math.floor(n.length/i)-this.cursorPos.rows+(t?Cx(t):0);o>0&&(a+=vx(o)),a+=bx(this.cursorPos.cols),this.write(yx(this.extraLinesUnderPrompt)+Sx(this.height)+a),this.extraLinesUnderPrompt=o,this.height=Cx(a)}checkCursorPos(){let e=this.rl.getCursorPos();e.cols!==this.cursorPos.cols&&(this.write(bx(e.cols)),this.cursorPos=e)}done({clearContent:e}){this.rl.setPrompt(``);let t=yx(this.extraLinesUnderPrompt);t+=e?Sx(this.height):`
216
216
  `,t+=`\x1B[?25h`,this.write(t),this.rl.close()}},Ex=class extends Promise{static withResolver(){let e,t;return{promise:new Promise((n,r)=>{e=n,t=r}),resolve:e,reject:t}}},Dx=x(nx(),1);const Ox=globalThis.setImmediate;function kx(){let e=Error.prepareStackTrace,t=[];try{Error.prepareStackTrace=(e,n)=>{let r=n.slice(1);return t=r,r},Error().stack}catch{return t}return Error.prepareStackTrace=e,t}function Ax(e){let t=kx();return(n,r={})=>{let{input:i=process.stdin,signal:a}=r,o=new Set,s=new Dx.default;s.pipe(r.output??process.stdout),s.mute();let l=d.createInterface({terminal:!0,input:i,output:s}),u=new Tx(l),{promise:f,resolve:p,reject:m}=Ex.withResolver(),h=()=>m(new Yy);if(a){let e=()=>m(new Jy({cause:a.reason}));if(a.aborted)return e(),Object.assign(f,{cancel:h});a.addEventListener(`abort`,e),o.add(()=>a.removeEventListener(`abort`,e))}o.add(mx((e,t)=>{m(new Xy(`User force closed the prompt with ${e} ${t}`))}));let g=()=>m(new Xy(`User force closed the prompt with SIGINT`));return l.on(`SIGINT`,g),o.add(()=>l.removeListener(`SIGINT`,g)),tb(l,a=>{let d=c.bind(()=>sb.clearAll());l.on(`close`,d),o.add(()=>l.removeListener(`close`,d));let g=()=>{let r=()=>u.checkCursorPos();l.input.on(`keypress`,r),o.add(()=>l.input.removeListener(`keypress`,r)),a(()=>{try{let r=e(n,e=>{setImmediate(()=>p(e))});if(r===void 0){let e=t[1]?.getFileName();throw Error(`Prompt functions must return a string.\n at ${e}`)}let[i,a]=typeof r==`string`?[r]:r;u.render(i,a),sb.run()}catch(e){m(e)}})};return`readableFlowing`in i?Ox(g):g(),Object.assign(f.then(e=>(sb.clearAll(),e),e=>{throw sb.clearAll(),e}).finally(()=>{o.forEach(e=>e()),u.done({clearContent:!!r.clearPromptOnDone}),s.end()}).then(()=>f),{cancel:h})})}}var jx=class{separator=u(`dim`,Array.from({length:15}).join(gb.line));type=`separator`;constructor(e){e&&(this.separator=e)}static isSeparator(e){return!!(e&&typeof e==`object`&&`type`in e&&e.type===`separator`)}};const Mx={icon:{checked:u(`green`,gb.circleFilled),unchecked:gb.circle,cursor:gb.pointer,disabledChecked:u(`green`,gb.circleDouble),disabledUnchecked:`-`},style:{disabled:e=>u(`dim`,e),renderSelectedChoices:e=>e.map(e=>e.short).join(`, `),description:e=>u(`cyan`,e),keysHelpTip:e=>e.map(([e,t])=>`${u(`bold`,e)} ${u(`dim`,t)}`).join(u(`dim`,` • `))},i18n:{disabledError:`This option is disabled and cannot be toggled.`},keybindings:[]};function Nx(e){return!jx.isSeparator(e)&&!e.disabled}function Px(e){return!jx.isSeparator(e)}function Fx(e){return!jx.isSeparator(e)&&e.checked}function Ix(e){return Nx(e)?{...e,checked:!e.checked}:e}function Lx(e){return function(t){return Nx(t)?{...t,checked:e}:t}}function Rx(e){return e.map(e=>{if(jx.isSeparator(e))return e;if(typeof e==`string`)return{value:e,name:e,short:e,checkedName:e,disabled:!1,checked:!1};let t=e.name??String(e.value),n={value:e.value,name:t,short:e.short??t,checkedName:e.checkedName??t,disabled:e.disabled??!1,checked:e.checked??!1};return e.description&&(n.description=e.description),n})}var zx=Ax((e,t)=>{let{pageSize:n=7,loop:r=!0,required:i,validate:a=()=>!0}=e,o={all:`a`,invert:`i`,...e.shortcuts},s=bb(Mx,e.theme),{keybindings:c}=s,[l,u]=cb(`idle`),d=xb({status:l,theme:s}),[f,p]=cb(Rx(e.choices)),m=Sb(()=>{let e=f.findIndex(Px),t=f.findLastIndex(Px);if(e===-1)throw new Qy(`[checkbox prompt] No selectable choices. All choices are disabled.`);return{first:e,last:t}},[f]),[h,g]=cb(m.first),[_,v]=cb();wb(async e=>{if(qy(e)){let e=f.filter(Fx),n=await a([...e]);i&&!e.length?v(`At least one choice must be selected`):n===!0?(u(`done`),t(e.map(e=>e.value))):v(n||`You must select a valid value`)}else if(Vy(e,c)||Hy(e,c)){if(_&&v(void 0),r||Vy(e,c)&&h!==m.first||Hy(e,c)&&h!==m.last){let t=Vy(e,c)?-1:1,n=h;do n=(n+t+f.length)%f.length;while(!Px(f[n]));g(n)}}else if(Uy(e)){let e=f[h];e&&!jx.isSeparator(e)&&(e.disabled?v(s.i18n.disabledError):(v(void 0),p(f.map((e,t)=>t===h?Ix(e):e))))}else if(e.name===o.all){let e=f.some(e=>Nx(e)&&!e.checked);p(f.map(Lx(e)))}else if(e.name===o.invert)p(f.map(Ix));else if(Ky(e)){let t=Number(e.name)-1,n=-1,r=f.findIndex(e=>jx.isSeparator(e)?!1:(n++,n===t)),i=f[r];i&&Nx(i)&&(g(r),p(f.map((e,t)=>t===r?Ix(e):e)))}});let y=s.style.message(e.message,l),b,x=tx({items:f,active:h,renderItem({item:e,isActive:t}){if(jx.isSeparator(e))return` ${e.separator}`;let n=t?s.icon.cursor:` `;if(e.disabled){let t=typeof e.disabled==`string`?e.disabled:`(disabled)`,r=e.checked?s.icon.disabledChecked:s.icon.disabledUnchecked;return s.style.disabled(`${n}${r} ${e.name} ${t}`)}t&&(b=e.description);let r=e.checked?s.icon.checked:s.icon.unchecked,i=e.checked?e.checkedName:e.name;return(t?s.style.highlight:e=>e)(`${n}${r} ${i}`)},pageSize:n,loop:r});if(l===`done`){let e=f.filter(Fx);return[d,y,s.style.answer(s.style.renderSelectedChoices(e,f))].filter(Boolean).join(` `)}let S=[[`↑↓`,`navigate`],[`space`,`select`]];o.all&&S.push([o.all,`all`]),o.invert&&S.push([o.invert,`invert`]),S.push([`⏎`,`submit`]);let C=s.style.keysHelpTip(S);return`${[[d,y].filter(Boolean).join(` `),x,` `,b?s.style.description(b):``,_?s.style.error(_):``,C].filter(Boolean).join(`
217
217
  `).trimEnd()}${_x}`});function Bx(e,t){let n=t!==!1;return/^(y|yes)/i.test(e)?n=!0:/^(n|no)/i.test(e)&&(n=!1),n}function Vx(e){return e?`Yes`:`No`}var Hx=Ax((e,t)=>{let{transformer:n=Vx}=e,[r,i]=cb(`idle`),[a,o]=cb(``),s=bb(e.theme),c=xb({status:r,theme:s});wb((s,c)=>{if(r===`idle`)if(qy(s)){let r=Bx(a,e.default);o(n(r)),i(`done`),t(r)}else if(Gy(s)){let t=Vx(!Bx(a,e.default));c.clearLine(0),c.write(t),o(t)}else o(c.line)});let l=a,u=``;return r===`done`?l=s.style.answer(a):u=` ${s.style.defaultAnswer(e.default===!1?`y/N`:`Y/n`)}`,`${c} ${s.style.message(e.message,r)}${u} ${l}`});const Ux={validationFailureMode:`keep`};var Wx=Ax((e,t)=>{let{prefill:n=`tab`}=e,r=bb(Ux,e.theme),[i,a]=cb(`idle`),[o,s]=cb(String(e.default??``)),[c,l]=cb(),[u,d]=cb(``),f=xb({status:i,theme:r});async function p(t){let{required:n,pattern:r,patternError:i=`Invalid input`}=e;return n&&!t?`You must provide a value`:r&&!r.test(t)?i:typeof e.validate==`function`?await e.validate(t)||`You must provide a valid value`:!0}wb(async(e,n)=>{if(i===`idle`)if(qy(e)){let e=u||o;a(`loading`);let i=await p(e);i===!0?(d(e),a(`done`),t(e)):(r.validationFailureMode===`clear`?d(``):n.write(u),l(i),a(`idle`))}else Wy(e)&&!u?s(``):Gy(e)&&!u?(s(``),n.clearLine(0),n.write(o),d(o)):(d(n.line),l(void 0))}),lb(e=>{n===`editable`&&o&&(e.write(o),d(o))},[]);let m=r.style.message(e.message,i),h=u;typeof e.transformer==`function`?h=e.transformer(u,{isFinal:i===`done`}):i===`done`&&(h=r.style.answer(u));let g;o&&i!==`done`&&!u&&(g=r.style.defaultAnswer(o));let _=``;return c&&(_=r.style.error(c)),[[f,m,g,h].filter(e=>e!==void 0).join(` `),_]});const Gx={icon:{cursor:gb.pointer},style:{disabled:e=>u(`dim`,e),description:e=>u(`cyan`,e),keysHelpTip:e=>e.map(([e,t])=>`${u(`bold`,e)} ${u(`dim`,t)}`).join(u(`dim`,` • `))},i18n:{disabledError:`This option is disabled and cannot be selected.`},indexMode:`hidden`,keybindings:[]};function Kx(e){return!jx.isSeparator(e)&&!e.disabled}function qx(e){return!jx.isSeparator(e)}function Jx(e){return e.map(e=>{if(jx.isSeparator(e))return e;if(typeof e!=`object`||!e||!(`value`in e)){let t=String(e);return{value:e,name:t,short:t,disabled:!1}}let t=e.name??String(e.value),n={value:e.value,name:t,short:e.short??t,disabled:e.disabled??!1};return e.description&&(n.description=e.description),n})}var Yx=Ax((e,t)=>{let{loop:n=!0,pageSize:r=7}=e,i=bb(Gx,e.theme),{keybindings:a}=i,[o,s]=cb(`idle`),c=xb({status:o,theme:i}),l=Cb(),u=!a.includes(`vim`),d=Sb(()=>Jx(e.choices),[e.choices]),f=Sb(()=>{let e=d.findIndex(qx),t=d.findLastIndex(qx);if(e===-1)throw new Qy(`[select prompt] No selectable choices. All choices are disabled.`);return{first:e,last:t}},[d]),p=Sb(()=>`default`in e?d.findIndex(t=>Kx(t)&&t.value===e.default):-1,[e.default,d]),[m,h]=cb(p===-1?f.first:p),g=d[m],[_,v]=cb();wb((e,r)=>{if(clearTimeout(l.current),_&&v(void 0),qy(e))g.disabled?v(i.i18n.disabledError):(s(`done`),t(g.value));else if(Vy(e,a)||Hy(e,a)){if(r.clearLine(0),n||Vy(e,a)&&m!==f.first||Hy(e,a)&&m!==f.last){let t=Vy(e,a)?-1:1,n=m;do n=(n+t+d.length)%d.length;while(!qx(d[n]));h(n)}}else if(Ky(e)&&!Number.isNaN(Number(r.line))){let e=Number(r.line)-1,t=-1,n=d.findIndex(n=>jx.isSeparator(n)?!1:(t++,t===e)),i=d[n];i!=null&&Kx(i)&&h(n),l.current=setTimeout(()=>{r.clearLine(0)},700)}else if(Wy(e))r.clearLine(0);else if(u){let e=r.line.toLowerCase(),t=d.findIndex(t=>jx.isSeparator(t)||!Kx(t)?!1:t.name.toLowerCase().startsWith(e));t!==-1&&h(t),l.current=setTimeout(()=>{r.clearLine(0)},700)}}),lb(()=>()=>{clearTimeout(l.current)},[]);let y=i.style.message(e.message,o),b=i.style.keysHelpTip([[`↑↓`,`navigate`],[`⏎`,`select`]]),x=0,S=tx({items:d,active:m,renderItem({item:e,isActive:t,index:n}){if(jx.isSeparator(e))return x++,` ${e.separator}`;let r=t?i.icon.cursor:` `,a=i.indexMode===`number`?`${n+1-x}. `:``;if(e.disabled){let n=typeof e.disabled==`string`?e.disabled:`(disabled)`,r=t?i.icon.cursor:`-`;return i.style.disabled(`${r} ${a}${e.name} ${n}`)}return(t?i.style.highlight:e=>e)(`${r} ${a}${e.name}`)},pageSize:r,loop:n});if(o===`done`)return[c,y,i.style.answer(g.short)].filter(Boolean).join(` `);let{description:C}=g;return`${[[c,y].filter(Boolean).join(` `),S,` `,C?i.style.description(C):``,_?i.style.error(_):``,b].filter(Boolean).join(`
218
- `).trimEnd()}${_x}`}),Xx=class{static async confirm(e){return await Hx({message:e.message,default:e.default})}static async getText(e){let{message:t,schema:n,default:r}=e,i;if(n&&r!==void 0){let e=n.safeParse(r);e.success&&(i=e.data)}else !n&&r!==void 0&&(i=r);if(n&&n instanceof Tf)return await Yx({message:t,choices:n.options.map(e=>({value:e})),default:i});let a=await Wx({message:t,default:i,validate:e=>{if(!n)return!0;let t=e;if(n instanceof Sd){if(e.trim()===``)return`Input cannot be empty`;let n=Number(e);if(Number.isNaN(n))return`Please enter a valid number`;t=n}let r=n.safeParse(t);return r.success?!0:r.error.issues[0]?.message??`Invalid input`}});if(!n)return a;let o=n instanceof Sd?Number(a):a;return n.parse(o)}static async choose(e){let{message:t,options:n,mode:r=`single`,default:i,includeAllOption:a=!1,required:o=!1}=e,s=`__all__`,c=r===`single`?[i]:[...i||[]],l=[...r===`multiple`&&a?[{name:`All`,value:s,checked:c.includes(s)}]:[],...n.map(e=>({name:e,value:e,checked:c.includes(e)}))];if(r===`multiple`){let e=await zx({message:t,choices:l,validate:e=>o&&e.length===0?`You must select at least one option.`:!0});return a&&e.includes(s)?n:e}return await Yx({message:t,choices:n.map(e=>({name:e,value:e})),default:typeof i==`string`?i:void 0})}},Zx=class e{static command=new dv(`init`).argument(`[projectName]`).option(`-d, --dir <path>`,`workspace directory`).action(e.run.bind(e));static async run(e,t){let n=await Iy.getPackageList(),r=n.filter(e=>e.type===`apps`&&e.packageJson?.startx?.mode!==`silent`),i=await this.getPrefs({projectName:e,options:t,projects:r}),a=n.filter(e=>e.type!==`apps`);await this.checkTargetDirectory(i.directory.workspace);let o=await this.getConfigPrefs({selectedApps:i.selectedApps,packages:a}),s=await this.getPackagesPrefs({selectedPackages:o.selectedConfigs,packages:a,tags:o.gTags}),c=[...s.gTags,`runnable`];await this.installWorkspace({name:i.projectName,tags:c,dir:i.directory});let l=[...s.selectedPackages,...i.selectedApps];await Promise.all(l.map(async e=>{let t={},n=new Set(s.gTags);e.packageJson?.startx?.mode===`standalone`&&n.add(`runnable`),e.type===`apps`&&(n.add(`runnable`),s.selectedPackages.filter(t=>t.type!==`packages`||t.packageJson?.startx?.mode===`standalone`?!1:t.packageJson?.startx?.iTags?.every(t=>e.packageJson?.startx?.gTags?.includes(t))).forEach(e=>{let n=e.packageJson?.name||e.name;t[n]=`workspace:^`})),await this.installPackage({pkg:e,directory:i.directory,tags:Array.from(n),dependencies:t})}))}static async getPrefs(e){let n=await Xx.getText({message:`Project name`,name:`projectName`,default:e.projectName,schema:Vp.string().min(1,`Package name is required`).max(214,`Package name too long`).regex(/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,`Invalid package name`)});if(e.projects.length===0)throw Error(`No apps found to install.`);let r=Iy.getDirectory(),i=e.options.dir?t.resolve(r.workspace,e.options.dir):t.join(r.workspace,n),a=await Xx.choose({message:`Select apps to install`,options:e.projects.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!0});return{projectName:n,directory:{workspace:i,template:r.template},selectedApps:e.projects.filter(e=>a.includes(e.name))}}static async getConfigPrefs(e){let t=new Set([`common`]),n=new Map;this.getGlobalTags({pkgs:e.selectedApps}).forEach(e=>t.add(e)),this.getPackageDeps({allPkgs:e.packages,pkgs:e.selectedApps}).forEach(e=>n.set(e.name,e));let r=e.packages.filter(e=>e.type!==`configs`||e.packageJson?.startx?.mode===`silent`||n.has(e.name)?!1:e.packageJson?.startx?.iTags?.every(e=>t.has(e))??!0);if(r.length>0){let e=await Xx.choose({message:`Select configs to install`,options:r.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!1});r.filter(t=>e.includes(t.name)).forEach(e=>n.set(e.name,e))}return t.has(`node`)&&(await Xx.choose({message:`Select formatter`,options:[`prettier + biome`,`prettier`],mode:`single`,default:`prettier`,required:!0})===`prettier`||t.add(`biome`),t.add(`prettier`)),this.getPackageDeps({allPkgs:e.packages,pkgs:Array.from(n.values())}).forEach(e=>n.set(e.name,e)),this.getGlobalTags({pkgs:Array.from(n.values())}).forEach(e=>t.add(e)),{gTags:Array.from(t),selectedConfigs:Array.from(n.values())}}static async getPackagesPrefs(e){let t=new Set(e.tags),n=new Map(e.selectedPackages.map(e=>[e.name,e])),r=e.packages.filter(e=>e.type!==`packages`||e.packageJson?.startx?.mode===`silent`||n.has(e.name)?!1:e.packageJson?.startx?.iTags?.every(e=>t.has(e))??!1);if(r.length>0){let e=await Xx.choose({message:`Select packages to install`,options:r.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!1});r.filter(t=>e.includes(t.name)).forEach(e=>n.set(e.name,e))}return this.getPackageDeps({allPkgs:e.packages,pkgs:Array.from(n.values())}).forEach(e=>n.set(e.name,e)),this.getGlobalTags({pkgs:Array.from(n.values())}).forEach(e=>t.add(e)),{gTags:Array.from(t),selectedPackages:Array.from(n.values())}}static async installPackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=new Set([...e.tags,...e.pkg.packageJson.startx?.tags||[]]),r=e.pkg.packageJson.startx?.ignore||[];r.includes(`eslint-config`)&&n.delete(`eslint`),r.includes(`vitest-config`)&&n.delete(`vitest`);let{packageJson:i,isWorkspace:a}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(n),name:e.packageName||e.pkg.packageJson.name||e.pkg.name,dependencies:e.dependencies});if(a)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);let o=t.join(e.directory.workspace,e.pkg.relativePath),s=t.join(e.pkg.path);await Ny.writeJSONFile({dir:o,file:`package`,content:i}),await this.copyValidatedFilesFromFolder(s,o,n),await Ny.copyDirectory({from:t.join(s,`src`),to:t.join(o,`src`),exclude:n.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Successfully installed ${e.pkg.name}`)}static async installWorkspace(e){let t=await Iy.parsePackageJson({dir:e.dir.template}),n=await Iy.parsePackageJson({dir:e.dir.template,file:`startx`});if(!t)throw Error(`Failed to parse root package.json`);t.dependencies={...t.dependencies,...n?.dependencies||{}},t.devDependencies={...t.devDependencies,...n?.devDependencies||{}};let{packageJson:r}=By.handlePackageJson({app:t,tags:[`root`,...e.tags],name:e.name});await Ny.writeJSONFile({dir:e.dir.workspace,file:`package`,content:r}),await this.copyValidatedFilesFromFolder(e.dir.template,e.dir.workspace,new Set([`root`,...e.tags])),await this.writeVscodeSettings({workspace:e.dir.workspace,tags:e.tags})}static async writeVscodeSettings(e){let n=e.tags.includes(`biome`),r=t.join(e.workspace,`.vscode`),i={"editor.formatOnSave":!0,"editor.defaultFormatter":n?`biomejs.biome`:`esbenp.prettier-vscode`,"editor.codeActionsOnSave":{...n?{"source.organizeImports.biome":`explicit`,"source.fixAll.biome":`explicit`}:{},"source.fixAll.eslint":`explicit`,"source.fixAll":`explicit`},"eslint.workingDirectories":[{mode:`auto`}]},a={recommendations:[`dbaeumer.vscode-eslint`,...n?[`biomejs.biome`]:[`esbenp.prettier-vscode`]]};await Promise.all([Ny.writeJSONFile({dir:r,file:`settings`,content:i}),Ny.writeJSONFile({dir:r,file:`extensions`,content:a})])}static async checkTargetDirectory(e){let[t,n]=await Promise.all([Ny.listFiles({dir:e}),Ny.listDirectories({dir:e})]);if(!(t.length===0&&n.length===0)&&!await Xx.confirm({message:`Directory "${e}" already exists and is not empty. Overwrite?`,default:!1}))throw Error(`Aborted: target directory already exists.`)}static getPackageDeps(e){let t=new Map(e.pkgs.map(e=>[e.name,e]));return Array.from(t.values()).forEach(n=>{[...n.packageJson?.startx?.requiredDeps||[],...n.packageJson?.startx?.requiredDevDeps||[]].forEach(n=>{let r=e.allPkgs.find(e=>e.packageJson?.name===n);r&&t.set(r.name,r)})}),e.pkgs.forEach(e=>t.delete(e.name)),Array.from(t.values())}static getGlobalTags(e){let t=new Set(e.gTags||[]);return e.pkgs.forEach(e=>{e.packageJson?.startx?.gTags?.forEach(e=>t.add(e))}),Array.from(t)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];if(i&&!i.tags.every(e=>r.has(e)))continue;let o=a===`_gitignore`?`.gitignore`:a;try{await Ny.copyFile({from:t.join(e,a),to:t.join(n,o)})}catch(e){Z_.error(`Failed to copy file ${a}:`,e)}}}};const Qx=Vp.string().min(1,`Package name is required`).max(214,`Package name too long`).regex(/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,`Invalid package name`);var $x=class e{static command=new dv(`package`).alias(`pkg`).description(`List and add packages in the current monorepo.`).addCommand(new dv(`list`).alias(`ls`).description(`List packages available from the StartX template.`).action(e.list.bind(e))).addCommand(new dv(`add`).description(`Add an existing StartX app or package, optionally with a new name.`).argument(`[packageName]`).option(`-n, --name <name>`,`override the name for the added package`).option(`--eslint`,`enable ESLint support for the added package`).option(`--no-eslint`,`skip ESLint support for the added package`).option(`--no-install`,`do not run the package manager after updating dependencies`).action(e.add.bind(e))).addCommand(new dv(`new`).alias(`create`).description(`Create a new package from scratch.`).argument(`[packageName]`).option(`-d, --dir <path>`,`package directory relative to the current workspace`).option(`--eslint`,`enable ESLint support for the new package`).option(`--no-eslint`,`skip ESLint support for the new package`).option(`--no-install`,`do not run the package manager after updating ESLint`).action(e.create.bind(e)));static async list(){let e=await Iy.getPackageList(),t=new Map;for(let n of e){let e=t.get(n.type)??[];e.push(n),t.set(n.type,e)}for(let e of[`apps`,`packages`,`configs`]){let n=t.get(e)??[];if(n.length!==0){Z_.info(`${e}:`);for(let e of n.sort((e,t)=>e.name.localeCompare(t.name)))Z_.info(` ${e.name} (${e.relativePath})`)}}}static async add(e,t){let n=await Iy.getPackageList(),r=n.filter(e=>e.packageJson?.startx?.mode!==`silent`),i=e??await Xx.choose({message:`Select app or package to add`,options:r.map(e=>e.name),mode:`single`,required:!0}),a=this.findPackage(n,i);if(!a)throw Error(`Package "${i}" was not found in the StartX template.`);let o=a.packageJson?.name??a.name,s=t.name??await Xx.getText({message:`Name for the new package (leave unchanged to keep the original)`,name:`overrideName`,default:o,schema:Qx}),c=Iy.getDirectory(),l=await this.resolveEslintPreference(t),u=this.resolvePackageClosure({packages:n,selectedPackage:a,includeEslintConfig:l}),d=await this.getInstallTags({workspace:c.workspace,packages:u,eslintEnabled:l});await this.checkAndInstallMissingDeps({workspace:c.workspace,tags:d,install:t.install});for(let e of u){let t=e.name===a.name;await this.installTemplatePackage({pkg:e,directory:c,tags:d,overrideName:t?s:void 0,overrideRelativePath:t?this.getDestinationPath(e.relativePath,s):void 0})}Z_.info("Done! Run `pnpm install` to link the new package.")}static async create(e,n){let r=await Xx.getText({message:`Package name`,name:`packageName`,default:e,schema:Qx}),i=Iy.getDirectory(),a=n.dir?t.resolve(i.workspace,n.dir):t.resolve(i.workspace,this.getDefaultPackagePath(r));if(await this.pathExists(a))throw Error(`Package directory already exists: ${a}`);let s=await this.readRootPackage(i.workspace),c=await this.resolveEslintPreference(n),l=this.hasDependency(s,`vitest`),u=await Iy.getPackageList();await this.ensureTemplatePackage({packages:u,name:`typescript-config`,directory:i,tags:[`common`,`node`]}),c&&await this.ensureTemplatePackage({packages:u,name:`eslint-config`,directory:i,tags:[`common`,`node`,`eslint`]}),l&&await this.ensureTemplatePackage({packages:u,name:`vitest-config`,directory:i,tags:[`common`,`node`,`vitest`]}),await o.mkdir(t.join(a,`src`),{recursive:!0}),await this.writeJson(t.join(a,`package.json`),this.createPackageJson({name:r,eslintEnabled:c,vitestEnabled:l})),await o.writeFile(t.join(a,`tsconfig.json`),`${JSON.stringify({extends:`typescript-config/tsconfig.node.json`,compilerOptions:{moduleResolution:`bundler`,module:`esnext`,target:`es2022`},include:[`src/**/*.ts`]},null,2)}\n`),await o.writeFile(t.join(a,`src`,`index.ts`),`export {};
218
+ `).trimEnd()}${_x}`}),Xx=class{static async confirm(e){return await Hx({message:e.message,default:e.default})}static async getText(e){let{message:t,schema:n,default:r}=e,i;if(n&&r!==void 0){let e=n.safeParse(r);e.success&&(i=e.data)}else !n&&r!==void 0&&(i=r);if(n&&n instanceof Tf)return await Yx({message:t,choices:n.options.map(e=>({value:e})),default:i});let a=await Wx({message:t,default:i,validate:e=>{if(!n)return!0;let t=e;if(n instanceof Sd){if(e.trim()===``)return`Input cannot be empty`;let n=Number(e);if(Number.isNaN(n))return`Please enter a valid number`;t=n}let r=n.safeParse(t);return r.success?!0:r.error.issues[0]?.message??`Invalid input`}});if(!n)return a;let o=n instanceof Sd?Number(a):a;return n.parse(o)}static async choose(e){let{message:t,options:n,mode:r=`single`,default:i,includeAllOption:a=!1,required:o=!1}=e,s=`__all__`,c=r===`single`?[i]:[...i||[]],l=[...r===`multiple`&&a?[{name:`All`,value:s,checked:c.includes(s)}]:[],...n.map(e=>({name:e,value:e,checked:c.includes(e)}))];if(r===`multiple`){let e=await zx({message:t,choices:l,validate:e=>o&&e.length===0?`You must select at least one option.`:!0});return a&&e.includes(s)?n:e}return await Yx({message:t,choices:n.map(e=>({name:e,value:e})),default:typeof i==`string`?i:void 0})}},Zx=class e{static command=new dv(`init`).argument(`[projectName]`).option(`-d, --dir <path>`,`workspace directory`).action(e.run.bind(e));static async run(e,t){let n=await Iy.getPackageList(),r=n.filter(e=>e.type===`apps`&&e.packageJson?.startx?.mode!==`silent`),i=await this.getPrefs({projectName:e,options:t,projects:r}),a=n.filter(e=>e.type!==`apps`);await this.checkTargetDirectory(i.directory.workspace);let o=await this.getConfigPrefs({selectedApps:i.selectedApps,packages:a}),s=await this.getPackagesPrefs({selectedPackages:o.selectedConfigs,packages:a,tags:o.gTags}),c=[...s.gTags,`runnable`];await this.installWorkspace({name:i.projectName,tags:c,dir:i.directory});let l=[...s.selectedPackages,...i.selectedApps];await Promise.all(l.map(async e=>{let t={},n=new Set(s.gTags);e.packageJson?.startx?.mode===`standalone`&&n.add(`runnable`),e.type===`apps`&&(n.add(`runnable`),s.selectedPackages.filter(t=>t.type!==`packages`||t.packageJson?.startx?.mode===`standalone`?!1:t.packageJson?.startx?.iTags?.every(t=>e.packageJson?.startx?.gTags?.includes(t))).forEach(e=>{let n=e.packageJson?.name||e.name;t[n]=`workspace:^`})),await this.installPackage({pkg:e,directory:i.directory,tags:Array.from(n),dependencies:t})}))}static async getPrefs(e){let n=await Xx.getText({message:`Project name`,name:`projectName`,default:e.projectName,schema:Vp.string().min(1,`Package name is required`).max(214,`Package name too long`).regex(/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,`Invalid package name`)});if(e.projects.length===0)throw Error(`No apps found to install.`);let r=Iy.getDirectory(),i=e.options.dir?t.resolve(r.workspace,e.options.dir):t.join(r.workspace,n),a=await Xx.choose({message:`Select apps to install`,options:e.projects.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!0});return{projectName:n,directory:{workspace:i,template:r.template},selectedApps:e.projects.filter(e=>a.includes(e.name))}}static async getConfigPrefs(e){let t=new Set([`common`]),n=new Map;this.getGlobalTags({pkgs:e.selectedApps}).forEach(e=>t.add(e)),this.getPackageDeps({allPkgs:e.packages,pkgs:e.selectedApps}).forEach(e=>n.set(e.name,e));let r=e.packages.filter(e=>e.type!==`configs`||e.packageJson?.startx?.mode===`silent`||n.has(e.name)?!1:e.packageJson?.startx?.iTags?.every(e=>t.has(e))??!0);if(r.length>0){let e=await Xx.choose({message:`Select configs to install`,options:r.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!1});r.filter(t=>e.includes(t.name)).forEach(e=>n.set(e.name,e))}return t.has(`node`)&&(await Xx.choose({message:`Select formatter`,options:[`prettier + biome`,`prettier`],mode:`single`,default:`prettier`,required:!0})===`prettier`||t.add(`biome`),t.add(`prettier`)),this.getPackageDeps({allPkgs:e.packages,pkgs:Array.from(n.values())}).forEach(e=>n.set(e.name,e)),this.getGlobalTags({pkgs:Array.from(n.values())}).forEach(e=>t.add(e)),{gTags:Array.from(t),selectedConfigs:Array.from(n.values())}}static async getPackagesPrefs(e){let t=new Set(e.tags),n=new Map(e.selectedPackages.map(e=>[e.name,e])),r=e.packages.filter(e=>e.type!==`packages`||e.packageJson?.startx?.mode===`silent`||n.has(e.name)?!1:e.packageJson?.startx?.iTags?.every(e=>t.has(e))??!1);if(r.length>0){let e=await Xx.choose({message:`Select packages to install`,options:r.map(e=>e.name),includeAllOption:!0,mode:`multiple`,required:!1});r.filter(t=>e.includes(t.name)).forEach(e=>n.set(e.name,e))}return this.getPackageDeps({allPkgs:e.packages,pkgs:Array.from(n.values())}).forEach(e=>n.set(e.name,e)),this.getGlobalTags({pkgs:Array.from(n.values())}).forEach(e=>t.add(e)),{gTags:Array.from(t),selectedPackages:Array.from(n.values())}}static async installPackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=new Set([...e.tags,...e.pkg.packageJson.startx?.tags||[]]),r=e.pkg.packageJson.startx?.ignore||[];r.includes(`eslint-config`)&&n.delete(`eslint`),r.includes(`vitest-config`)&&n.delete(`vitest`);let{packageJson:i,isWorkspace:a}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(n),name:e.packageName||e.pkg.packageJson.name||e.pkg.name,dependencies:e.dependencies});if(a)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);let o=t.join(e.directory.workspace,e.pkg.relativePath),s=t.join(e.pkg.path);await Ny.writeJSONFile({dir:o,file:`package`,content:i}),await this.copyValidatedFilesFromFolder(s,o,n),await Ny.copyDirectory({from:t.join(s,`src`),to:t.join(o,`src`),exclude:n.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Successfully installed ${e.pkg.name}`)}static async installWorkspace(e){let t=await Iy.parsePackageJson({dir:e.dir.template}),n=await Iy.parsePackageJson({dir:e.dir.template,file:`startx`});if(!t)throw Error(`Failed to parse root package.json`);t.dependencies={...t.dependencies,...n?.dependencies||{}},t.devDependencies={...t.devDependencies,...n?.devDependencies||{}};let{packageJson:r}=By.handlePackageJson({app:t,tags:[`root`,...e.tags],name:e.name});await Ny.writeJSONFile({dir:e.dir.workspace,file:`package`,content:r}),await this.copyValidatedFilesFromFolder(e.dir.template,e.dir.workspace,new Set([`root`,...e.tags])),await this.writeVscodeSettings({workspace:e.dir.workspace,tags:e.tags})}static async writeVscodeSettings(e){let n=e.tags.includes(`biome`),r=t.join(e.workspace,`.vscode`),i={"editor.formatOnSave":!0,"editor.defaultFormatter":n?`biomejs.biome`:`esbenp.prettier-vscode`,"editor.codeActionsOnSave":{...n?{"source.organizeImports.biome":`explicit`,"source.fixAll.biome":`explicit`}:{},"source.fixAll.eslint":`explicit`,"source.fixAll":`explicit`},"eslint.workingDirectories":[{mode:`auto`}]},a={recommendations:[`dbaeumer.vscode-eslint`,...n?[`biomejs.biome`]:[`esbenp.prettier-vscode`]]};await Promise.all([Ny.writeJSONFile({dir:r,file:`settings`,content:i}),Ny.writeJSONFile({dir:r,file:`extensions`,content:a})])}static async checkTargetDirectory(e){let[t,n]=await Promise.all([Ny.listFiles({dir:e}),Ny.listDirectories({dir:e})]);if(!(t.length===0&&n.length===0)&&!await Xx.confirm({message:`Directory "${e}" already exists and is not empty. Overwrite?`,default:!1}))throw Error(`Aborted: target directory already exists.`)}static getPackageDeps(e){let t=new Map(e.pkgs.map(e=>[e.name,e]));return Array.from(t.values()).forEach(n=>{[...n.packageJson?.startx?.requiredDeps||[],...n.packageJson?.startx?.requiredDevDeps||[]].forEach(n=>{let r=e.allPkgs.find(e=>e.packageJson?.name===n);r&&t.set(r.name,r)})}),e.pkgs.forEach(e=>t.delete(e.name)),Array.from(t.values())}static getGlobalTags(e){let t=new Set(e.gTags||[]);return e.pkgs.forEach(e=>{e.packageJson?.startx?.gTags?.forEach(e=>t.add(e))}),Array.from(t)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];if(i&&!i.tags.every(e=>r.has(e)))continue;let o=a===`_gitignore`?`.gitignore`:a;try{await Ny.copyFile({from:t.join(e,a),to:t.join(n,o)})}catch(e){Z_.error(`Failed to copy file ${a}:`,e)}}}};const Qx=Vp.string().min(1,`Package name is required`).max(214,`Package name too long`).regex(/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,`Invalid package name`);var $x=class e{static command=new dv(`package`).alias(`pkg`).description(`List and add packages in the current monorepo.`).addCommand(new dv(`list`).alias(`ls`).description(`List packages available from the StartX template.`).action(e.list.bind(e))).addCommand(new dv(`add`).description(`Add an existing StartX app or package, optionally with a new name.`).argument(`[packageName]`).option(`-n, --name <name>`,`override the name for the added package`).option(`--eslint`,`enable ESLint support for the added package`).option(`--no-eslint`,`skip ESLint support for the added package`).option(`--no-install`,`do not run the package manager after updating dependencies`).action(e.add.bind(e))).addCommand(new dv(`new`).alias(`create`).description(`Create a new package from scratch.`).argument(`[packageName]`).option(`-d, --dir <path>`,`package directory relative to the current workspace`).option(`--eslint`,`enable ESLint support for the new package`).option(`--no-eslint`,`skip ESLint support for the new package`).option(`--no-install`,`do not run the package manager after updating ESLint`).action(e.create.bind(e)));static async list(){let e=await Iy.getPackageList(),t=new Map;for(let n of e){let e=t.get(n.type)??[];e.push(n),t.set(n.type,e)}for(let e of[`apps`,`packages`,`configs`]){let n=t.get(e)??[];if(n.length!==0){Z_.info(`${e}:`);for(let e of n.sort((e,t)=>e.name.localeCompare(t.name)))Z_.info(` ${e.name} (${e.relativePath})`)}}}static async add(e,t){let n=await Iy.getPackageList(),r=n.filter(e=>e.packageJson?.startx?.mode!==`silent`),i=e??await Xx.choose({message:`Select app or package to add`,options:r.map(e=>e.name),mode:`single`,required:!0}),a=this.findPackage(n,i);if(!a)throw Error(`Package "${i}" was not found in the StartX template.`);let o=a.packageJson?.name??a.name,s=t.name??await Xx.getText({message:`Name for the new package (leave unchanged to keep the original)`,name:`overrideName`,default:o,schema:Qx}),c=Iy.getDirectory(),l=await this.resolveEslintPreference(t),u=this.resolvePackageClosure({packages:n,selectedPackage:a,includeEslintConfig:l}),d=await this.getInstallTags({workspace:c.workspace,packages:u,eslintEnabled:l});await this.checkAndInstallMissingDeps({directory:c,tags:d,install:t.install});for(let e of u){let t=e.name===a.name;await this.installTemplatePackage({pkg:e,directory:c,tags:d,overrideName:t?s:void 0,overrideRelativePath:t?this.getDestinationPath(e.relativePath,s):void 0})}Z_.info("Done! Run `pnpm install` to link the new package.")}static async create(e,n){let r=await Xx.getText({message:`Package name`,name:`packageName`,default:e,schema:Qx}),i=Iy.getDirectory(),a=n.dir?t.resolve(i.workspace,n.dir):t.resolve(i.workspace,this.getDefaultPackagePath(r));if(await this.pathExists(a))throw Error(`Package directory already exists: ${a}`);let s=await this.readRootPackage(i.workspace),c=await this.resolveEslintPreference(n),l=this.hasDependency(s,`vitest`),u=await Iy.getPackageList();await this.ensureTemplatePackage({packages:u,name:`typescript-config`,directory:i,tags:[`common`,`node`]}),c&&await this.ensureTemplatePackage({packages:u,name:`eslint-config`,directory:i,tags:[`common`,`node`,`eslint`]}),l&&await this.ensureTemplatePackage({packages:u,name:`vitest-config`,directory:i,tags:[`common`,`node`,`vitest`]}),await o.mkdir(t.join(a,`src`),{recursive:!0}),await this.writeJson(t.join(a,`package.json`),this.createPackageJson({name:r,eslintEnabled:c,vitestEnabled:l})),await o.writeFile(t.join(a,`tsconfig.json`),`${JSON.stringify({extends:`typescript-config/tsconfig.node.json`,compilerOptions:{moduleResolution:`bundler`,module:`esnext`,target:`es2022`},include:[`src/**/*.ts`]},null,2)}\n`),await o.writeFile(t.join(a,`src`,`index.ts`),`export {};
219
219
  `),c&&await o.writeFile(t.join(a,`eslint.config.ts`),`import { baseConfig } from "eslint-config/base";
220
220
  import { extend } from "eslint-config/extend";
221
221
 
@@ -223,4 +223,4 @@ export default extend(baseConfig);
223
223
  `),l&&await o.writeFile(t.join(a,`vitest.config.ts`),`import vitestConfig from "vitest-config/node";
224
224
 
225
225
  export default vitestConfig;
226
- `),Z_.info(`Created package ${r} at ${t.relative(i.workspace,a)}`),Z_.info("Run `pnpm install` to link the new package.")}static async resolveEslintPreference(e){if(e.eslint===!1)return!1;let n=Iy.getDirectory(),r=await this.readRootPackage(n.workspace);return this.hasDependency(r,`eslint`)?e.eslint??!0:e.eslint===!0||await Xx.confirm({message:`ESLint is not installed in this monorepo. Install and enable it?`,default:!0})?(r.devDependencies={...r.devDependencies,eslint:await this.resolveDependencyVersion(n.workspace,`eslint`)},await this.writeJson(t.join(n.workspace,`package.json`),r),Z_.info(`Added eslint to the root devDependencies.`),e.install!==!1&&await this.installRootDependencies(n.workspace),!0):!1}static async getInstallTags(e){let t=new Set([`common`,`node`]),n=await this.readRootPackage(e.workspace);e.eslintEnabled&&t.add(`eslint`),this.hasDependency(n,`@biomejs/biome`)&&t.add(`biome`),this.hasDependency(n,`prettier`)&&t.add(`prettier`),this.hasDependency(n,`vitest`)&&t.add(`vitest`),this.hasDependency(n,`tsdown`)&&t.add(`tsdown`);for(let n of e.packages)n.packageJson?.startx?.tags?.forEach(e=>t.add(e)),n.packageJson?.startx?.iTags?.forEach(e=>t.add(e)),n.packageJson?.startx?.gTags?.forEach(e=>t.add(e)),(n.type===`apps`||n.packageJson?.startx?.mode===`standalone`)&&t.add(`runnable`);return Array.from(t)}static resolvePackageClosure(e){let t=new Map,n=[e.selectedPackage],r=r=>{let i=this.findPackage(e.packages,r);i&&!t.has(i.name)&&n.push(i)};for(e.includeEslintConfig&&r(`eslint-config`);n.length>0;){let e=n.shift();if(!t.has(e.name)){t.set(e.name,e);for(let t of[...e.packageJson?.startx?.requiredDeps??[],...e.packageJson?.startx?.requiredDevDeps??[]])r(t)}}return Array.from(t.values())}static async ensureTemplatePackage(e){let t=this.findPackage(e.packages,e.name);if(!t){Z_.warn(`Could not find template package ${e.name}; skipping.`);return}await this.installTemplatePackage({pkg:t,directory:e.directory,tags:e.tags})}static async installTemplatePackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=e.overrideRelativePath??e.pkg.relativePath,r=t.join(e.directory.workspace,n);if(await this.pathExists(t.join(r,`package.json`))&&!await Xx.confirm({message:`"${n}" already exists. Overwrite?`,default:!1})){Z_.info(`Skipping ${e.pkg.name}.`);return}let i=new Set([...e.tags,...e.pkg.packageJson.startx?.tags??[]]),a=e.pkg.packageJson.startx?.ignore??[];a.includes(`eslint-config`)&&i.delete(`eslint`),a.includes(`vitest-config`)&&i.delete(`vitest`);let{packageJson:o,isWorkspace:s}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(i),name:e.overrideName??e.pkg.packageJson.name??e.pkg.name});if(s)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);await Ny.writeJSONFile({dir:r,file:`package`,content:o}),await this.copyValidatedFilesFromFolder(e.pkg.path,r,i),await Ny.copyDirectory({from:t.join(e.pkg.path,`src`),to:t.join(r,`src`),exclude:i.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Installed ${e.overrideName??e.pkg.name} at ${n}`)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];i&&!i.tags.every(e=>r.has(e))||a!==`package.json`&&await Ny.copyFile({from:t.join(e,a),to:t.join(n,a)})}}static createPackageJson(e){let t={typecheck:`tsc --noEmit`,clean:`rimraf dist .turbo`},n={"typescript-config":`workspace:*`},r=[];return e.eslintEnabled?(t.lint=`eslint .`,t[`lint:fix`]=`eslint . --fix`,n[`eslint-config`]=`workspace:*`):r.push(`eslint-config`),e.vitestEnabled?(t.test=`vitest run`,n[`vitest-config`]=`workspace:*`):r.push(`vitest-config`),{name:e.name,version:`1.0.0`,type:`module`,scripts:t,exports:`./src/index.ts`,devDependencies:n,startx:{iTags:[`node`],requiredDevDeps:[`typescript-config`],...r.length>0?{ignore:r}:{}}}}static async checkAndInstallMissingDeps(e){let n=await this.readRootPackage(e.workspace),r=await Iy.parsePnpmWorkspace({dir:e.workspace}),i=[];for(let[t,a]of Object.entries(Ly)){if(!a.tags.every(t=>e.tags.includes(t))||a.tags.includes(`root`)||this.hasDependency(n,t))continue;let o=r?.catalog?.[t]?`catalog:`:a.version;i.push({name:t,version:o,isDev:a.isDevDependency??!0})}if(i.length!==0){Z_.warn(`The following workspace dependencies are required but not installed:`);for(let e of i)Z_.warn(` - ${e.name}`);if(!await Xx.confirm({message:`Add them to the workspace root package.json?`,default:!0})){Z_.warn(`Skipping. Some features may not work correctly without these dependencies.`);return}for(let e of i)e.isDev?n.devDependencies[e.name]=e.version:n.dependencies[e.name]=e.version;await this.writeJson(t.join(e.workspace,`package.json`),n),Z_.info(`Added missing dependencies to root package.json.`),e.install!==!1&&await this.installRootDependencies(e.workspace)}}static getDestinationPath(e,n){let r=t.dirname(e),i=n.includes(`/`)?n.split(`/`).pop():n;return t.join(r,i)}static getDefaultPackagePath(e){if(e.startsWith(`@`)){let[n,r]=e.split(`/`);return t.join(`packages`,n,r)}return t.join(`packages`,e)}static findPackage(e,t){return e.find(e=>e.name===t||e.packageJson?.name===t)}static hasDependency(e,t){return!!(e.dependencies?.[t]||e.devDependencies?.[t]||e.peerDependencies?.[t])}static async readRootPackage(e){let n=await o.readFile(t.join(e,`package.json`),`utf-8`);return JSON.parse(n)}static async resolveDependencyVersion(e,t){return(await Iy.parsePnpmWorkspace({dir:e}))?.catalog?.[t]?`catalog:`:t===`eslint`?`^9.0.0`:`latest`}static async installRootDependencies(e){let t=(await this.readRootPackage(e)).packageManager?.split(`@`)[0]||`pnpm`,r=t===`yarn`?`yarn`:t,i=[`install`];Z_.info(`Running ${r} ${i.join(` `)} to install ESLint...`),await new Promise((t,a)=>{let o=n(r,i,{cwd:e,stdio:`inherit`,shell:process.platform===`win32`});o.on(`error`,a),o.on(`close`,e=>{if(e===0){t();return}a(Error(`${r} ${i.join(` `)} exited with code ${e}`))})}).catch(t=>{Z_.warn(`Could not install dependencies automatically: ${t instanceof Error?t.message:t}`),Z_.warn(`Run "${r} ${i.join(` `)}" manually in ${e}.`)})}static async writeJson(e,t){await o.writeFile(e,`${JSON.stringify(t,null,2)}\n`)}static async pathExists(e){try{return await o.access(e),!0}catch{return!1}}},eS=`1.1.11`;const tS=new dv;tS.name(`startx`).description(`StartX CLI - Your all in one monorepo startup tool.`).version(eS),tS.command(`ping`).action(()=>{Z_.info(`pong`)}),tS.addCommand(Zx.command),tS.addCommand($x.command),tS.parse(process.argv);export{};
226
+ `),Z_.info(`Created package ${r} at ${t.relative(i.workspace,a)}`),Z_.info("Run `pnpm install` to link the new package.")}static async resolveEslintPreference(e){if(e.eslint===!1)return!1;let n=Iy.getDirectory(),r=await this.readRootPackage(n.workspace);return this.hasDependency(r,`eslint`)?e.eslint??!0:e.eslint===!0||await Xx.confirm({message:`ESLint is not installed in this monorepo. Install and enable it?`,default:!0})?(r.devDependencies={...r.devDependencies,eslint:await this.resolveDependencyVersion(n.workspace,`eslint`)},await this.writeJson(t.join(n.workspace,`package.json`),r),Z_.info(`Added eslint to the root devDependencies.`),e.install!==!1&&await this.installRootDependencies(n.workspace),!0):!1}static async getInstallTags(e){let t=new Set([`common`,`node`]),n=await this.readRootPackage(e.workspace);e.eslintEnabled&&t.add(`eslint`),this.hasDependency(n,`@biomejs/biome`)&&t.add(`biome`),this.hasDependency(n,`prettier`)&&t.add(`prettier`),this.hasDependency(n,`vitest`)&&t.add(`vitest`),this.hasDependency(n,`tsdown`)&&t.add(`tsdown`);for(let n of e.packages)n.packageJson?.startx?.tags?.forEach(e=>t.add(e)),n.packageJson?.startx?.iTags?.forEach(e=>t.add(e)),n.packageJson?.startx?.gTags?.forEach(e=>t.add(e)),(n.type===`apps`||n.packageJson?.startx?.mode===`standalone`)&&t.add(`runnable`);return Array.from(t)}static resolvePackageClosure(e){let t=new Map,n=[e.selectedPackage],r=r=>{let i=this.findPackage(e.packages,r);i&&!t.has(i.name)&&n.push(i)};for(e.includeEslintConfig&&r(`eslint-config`);n.length>0;){let e=n.shift();if(!t.has(e.name)){t.set(e.name,e);for(let t of[...e.packageJson?.startx?.requiredDeps??[],...e.packageJson?.startx?.requiredDevDeps??[]])r(t)}}return Array.from(t.values())}static async ensureTemplatePackage(e){let t=this.findPackage(e.packages,e.name);if(!t){Z_.warn(`Could not find template package ${e.name}; skipping.`);return}await this.installTemplatePackage({pkg:t,directory:e.directory,tags:e.tags})}static async installTemplatePackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=e.overrideRelativePath??e.pkg.relativePath,r=t.join(e.directory.workspace,n);if(await this.pathExists(t.join(r,`package.json`))&&!await Xx.confirm({message:`"${n}" already exists. Overwrite?`,default:!1})){Z_.info(`Skipping ${e.pkg.name}.`);return}let i=new Set([...e.tags,...e.pkg.packageJson.startx?.tags??[]]),a=e.pkg.packageJson.startx?.ignore??[];a.includes(`eslint-config`)&&i.delete(`eslint`),a.includes(`vitest-config`)&&i.delete(`vitest`);let{packageJson:o,isWorkspace:s}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(i),name:e.overrideName??e.pkg.packageJson.name??e.pkg.name});if(s)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);await this.syncDepsWithCatalog({workspace:e.directory.workspace,templateDir:e.directory.template,packageJson:o}),await Ny.writeJSONFile({dir:r,file:`package`,content:o}),await this.copyValidatedFilesFromFolder(e.pkg.path,r,i),await Ny.copyDirectory({from:t.join(e.pkg.path,`src`),to:t.join(r,`src`),exclude:i.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Installed ${e.overrideName??e.pkg.name} at ${n}`)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];i&&!i.tags.every(e=>r.has(e))||a!==`package.json`&&await Ny.copyFile({from:t.join(e,a),to:t.join(n,a)})}}static createPackageJson(e){let t={typecheck:`tsc --noEmit`,clean:`rimraf dist .turbo`},n={"typescript-config":`workspace:*`},r=[];return e.eslintEnabled?(t.lint=`eslint .`,t[`lint:fix`]=`eslint . --fix`,n[`eslint-config`]=`workspace:*`):r.push(`eslint-config`),e.vitestEnabled?(t.test=`vitest run`,n[`vitest-config`]=`workspace:*`):r.push(`vitest-config`),{name:e.name,version:`1.0.0`,type:`module`,scripts:t,exports:`./src/index.ts`,devDependencies:n,startx:{iTags:[`node`],requiredDevDeps:[`typescript-config`],...r.length>0?{ignore:r}:{}}}}static async checkAndInstallMissingDeps(e){let n=await this.readRootPackage(e.directory.workspace),r=await Iy.parsePnpmWorkspace({dir:e.directory.workspace}),i=[],a=[];for(let[t,o]of Object.entries(Ly))if(o.tags.every(t=>e.tags.includes(t))&&!o.tags.includes(`root`))if(o.version.startsWith(`workspace:`))await this.workspacePackageExists(e.directory.workspace,t)||a.push(t);else{if(this.hasDependency(n,t))continue;let e=r?.catalog?.[t]?`catalog:`:o.version;i.push({name:t,version:e,isDev:o.isDevDependency??!0})}if(a.length>0){Z_.warn(`The following workspace packages are missing from this monorepo:`);for(let e of a)Z_.warn(` - ${e} → run: startx package add ${e}`)}if(i.length!==0){Z_.warn(`The following npm dependencies are required but not installed:`);for(let e of i)Z_.warn(` - ${e.name}`);if(!await Xx.confirm({message:`Add them to the workspace root package.json?`,default:!0})){Z_.warn(`Skipping. Some features may not work correctly without these dependencies.`);return}n.devDependencies??={},n.dependencies??={};for(let e of i)e.isDev?n.devDependencies[e.name]=e.version:n.dependencies[e.name]=e.version;await this.writeJson(t.join(e.directory.workspace,`package.json`),n),Z_.info(`Added missing dependencies to root package.json.`),e.install!==!1&&await this.installRootDependencies(e.directory.workspace)}}static async workspacePackageExists(e,n){let r=n.startsWith(`@`)?t.join(...n.split(`/`)):n,i=[t.join(e,`configs`,r,`package.json`),t.join(e,`packages`,r,`package.json`),t.join(e,`apps`,r,`package.json`)];for(let e of i)if(await this.pathExists(e))return!0;return!1}static getDestinationPath(e,n){let r=t.dirname(e),i=n.includes(`/`)?n.split(`/`).pop():n;return t.join(r,i)}static getDefaultPackagePath(e){if(e.startsWith(`@`)){let[n,r]=e.split(`/`);return t.join(`packages`,n,r)}return t.join(`packages`,e)}static findPackage(e,t){return e.find(e=>e.name===t||e.packageJson?.name===t)}static hasDependency(e,t){return!!(e.dependencies?.[t]||e.devDependencies?.[t]||e.peerDependencies?.[t])}static async readRootPackage(e){let n=await o.readFile(t.join(e,`package.json`),`utf-8`);return JSON.parse(n)}static async resolveDependencyVersion(e,t){return(await Iy.parsePnpmWorkspace({dir:e}))?.catalog?.[t]?`catalog:`:t===`eslint`?`^9.0.0`:`latest`}static async installRootDependencies(e){let t=(await this.readRootPackage(e)).packageManager?.split(`@`)[0]||`pnpm`,r=t===`yarn`?`yarn`:t,i=[`install`];Z_.info(`Running ${r} ${i.join(` `)} to install ESLint...`),await new Promise((t,a)=>{let o=n(r,i,{cwd:e,stdio:`inherit`,shell:process.platform===`win32`});o.on(`error`,a),o.on(`close`,e=>{if(e===0){t();return}a(Error(`${r} ${i.join(` `)} exited with code ${e}`))})}).catch(t=>{Z_.warn(`Could not install dependencies automatically: ${t instanceof Error?t.message:t}`),Z_.warn(`Run "${r} ${i.join(` `)}" manually in ${e}.`)})}static async syncDepsWithCatalog(e){let n=t.join(e.workspace,`pnpm-workspace.yaml`),r;try{r=await o.readFile(n,`utf-8`)}catch{Z_.warn(`Could not find pnpm workspace file at ${n}.`);return}let i=jy.parseDocument(r),a=i.getIn([`catalog`]);if(!a)return;let s=await this.loadTemplateCatalog(e.templateDir),c=e.packageJson.dependencies,l=e.packageJson.devDependencies,u={},d=e=>{if(e){for(let[t,n]of Object.entries(e))if(!n.startsWith(`workspace:`))if(n===`catalog:`){if(!a[t]){let e=s[t];e&&(u[t]=e)}}else e[t]=`catalog:`,a[t]||(u[t]=n)}};if(d(c),d(l),Object.keys(u).length!==0){for(let[e,t]of Object.entries(u))i.setIn([`catalog`,e],t);await o.writeFile(n,i.toString()),Z_.info(`Added to pnpm-workspace.yaml catalog:`);for(let[e,t]of Object.entries(u))Z_.info(` + ${e}: ${t}`)}}static async loadTemplateCatalog(e){try{let n=await o.readFile(t.join(e,`pnpm-workspace.yaml`),`utf-8`);return jy.parseDocument(n).getIn([`catalog`])??{}}catch{return Z_.warn(`Could not find pnpm-workspace.yaml template in ${e}.`),{}}}static async writeJson(e,t){await o.writeFile(e,`${JSON.stringify(t,null,2)}\n`)}static async pathExists(e){try{return await o.access(e),!0}catch{return!1}}},eS=`1.1.31`;const tS=new dv;tS.name(`startx`).description(`StartX CLI - Your all in one monorepo startup tool.`).version(eS),tS.command(`ping`).action(()=>{Z_.info(`pong`)}),tS.addCommand(Zx.command),tS.addCommand($x.command),tS.parse(process.argv);export{};
@@ -33,7 +33,8 @@
33
33
  "@repo/env": "workspace:*",
34
34
  "chokidar": "catalog:",
35
35
  "commander": "catalog:",
36
- "type-fest": "catalog:"
36
+ "type-fest": "catalog:",
37
+ "yaml": "catalog:"
37
38
  },
38
39
  "startx": {
39
40
  "mode": "silent",
@@ -4,6 +4,7 @@ import { spawn } from "child_process";
4
4
  import { Command } from "commander";
5
5
  import fs from "fs/promises";
6
6
  import path from "path";
7
+ import * as YAML from "yaml";
7
8
  import z from "zod";
8
9
 
9
10
  import { DepCheck } from "../configs/deps";
@@ -123,7 +124,7 @@ export class PackageCommand {
123
124
  });
124
125
 
125
126
  await this.checkAndInstallMissingDeps({
126
- workspace: directory.workspace,
127
+ directory,
127
128
  tags,
128
129
  install: options.install,
129
130
  });
@@ -261,11 +262,7 @@ export class PackageCommand {
261
262
  return true;
262
263
  }
263
264
 
264
- private static async getInstallTags(props: {
265
- workspace: string;
266
- packages: PackageItem[];
267
- eslintEnabled: boolean;
268
- }) {
265
+ private static async getInstallTags(props: { workspace: string; packages: PackageItem[]; eslintEnabled: boolean }) {
269
266
  const tags = new Set<TAGS>(["common", "node"]);
270
267
  const rootPackage = await this.readRootPackage(props.workspace);
271
268
 
@@ -377,6 +374,12 @@ export class PackageCommand {
377
374
  throw new Error(`Cannot install workspace as a package: ${props.pkg.name}`);
378
375
  }
379
376
 
377
+ await this.syncDepsWithCatalog({
378
+ workspace: props.directory.workspace,
379
+ templateDir: props.directory.template,
380
+ packageJson: packageJson as Record<string, unknown>,
381
+ });
382
+
380
383
  await fsTool.writeJSONFile({ dir: destination, file: "package", content: packageJson });
381
384
  await this.copyValidatedFilesFromFolder(props.pkg.path, destination, tags);
382
385
  await fsTool.copyDirectory({
@@ -441,28 +444,45 @@ export class PackageCommand {
441
444
  },
442
445
  };
443
446
  }
444
-
445
447
  private static async checkAndInstallMissingDeps(props: {
446
- workspace: string;
448
+ directory: {
449
+ template: string;
450
+ workspace: string;
451
+ };
447
452
  tags: TAGS[];
448
453
  install?: boolean;
449
454
  }) {
450
- const rootPackage = await this.readRootPackage(props.workspace);
451
- const pnpmWorkspace = await CliUtils.parsePnpmWorkspace({ dir: props.workspace });
455
+ const rootPackage = await this.readRootPackage(props.directory.workspace);
456
+ const pnpmWorkspace = await CliUtils.parsePnpmWorkspace({ dir: props.directory.workspace });
457
+
458
+ const missingNpm: Array<{ name: string; version: string; isDev: boolean }> = [];
459
+ const missingWorkspace: string[] = [];
452
460
 
453
- const missing: Array<{ name: string; version: string; isDev: boolean }> = [];
454
461
  for (const [dep, config] of Object.entries(DepCheck)) {
455
- if (!config.tags.every(tag => props.tags.includes(tag as TAGS))) continue;
462
+ if (!config.tags.every(tag => props.tags.includes(tag))) continue;
456
463
  if (config.tags.includes("root")) continue;
457
- if (this.hasDependency(rootPackage, dep)) continue;
458
- const version = pnpmWorkspace?.catalog?.[dep] ? "catalog:" : config.version;
459
- missing.push({ name: dep, version, isDev: config.isDevDependency ?? true });
464
+
465
+ if (config.version.startsWith("workspace:")) {
466
+ const exists = await this.workspacePackageExists(props.directory.workspace, dep);
467
+ if (!exists) missingWorkspace.push(dep);
468
+ } else {
469
+ if (this.hasDependency(rootPackage, dep)) continue;
470
+ const version = pnpmWorkspace?.catalog?.[dep] ? "catalog:" : config.version;
471
+ missingNpm.push({ name: dep, version, isDev: config.isDevDependency ?? true });
472
+ }
473
+ }
474
+
475
+ if (missingWorkspace.length > 0) {
476
+ logger.warn("The following workspace packages are missing from this monorepo:");
477
+ for (const name of missingWorkspace) {
478
+ logger.warn(` - ${name} → run: startx package add ${name}`);
479
+ }
460
480
  }
461
481
 
462
- if (missing.length === 0) return;
482
+ if (missingNpm.length === 0) return;
463
483
 
464
- logger.warn(`The following workspace dependencies are required but not installed:`);
465
- for (const dep of missing) {
484
+ logger.warn("The following npm dependencies are required but not installed:");
485
+ for (const dep of missingNpm) {
466
486
  logger.warn(` - ${dep.name}`);
467
487
  }
468
488
 
@@ -475,7 +495,10 @@ export class PackageCommand {
475
495
  return;
476
496
  }
477
497
 
478
- for (const dep of missing) {
498
+ rootPackage.devDependencies ??= {};
499
+ rootPackage.dependencies ??= {};
500
+
501
+ for (const dep of missingNpm) {
479
502
  if (dep.isDev) {
480
503
  (rootPackage.devDependencies as Record<string, string>)[dep.name] = dep.version;
481
504
  } else {
@@ -483,12 +506,27 @@ export class PackageCommand {
483
506
  }
484
507
  }
485
508
 
486
- await this.writeJson(path.join(props.workspace, "package.json"), rootPackage);
509
+ await this.writeJson(path.join(props.directory.workspace, "package.json"), rootPackage);
487
510
  logger.info("Added missing dependencies to root package.json.");
488
511
 
489
512
  if (props.install !== false) {
490
- await this.installRootDependencies(props.workspace);
513
+ await this.installRootDependencies(props.directory.workspace);
514
+ }
515
+ }
516
+ private static async workspacePackageExists(workspace: string, packageName: string): Promise<boolean> {
517
+ // Resolve scoped names: @repo/lib → packages/@repo/lib
518
+ const subPath = packageName.startsWith("@") ? path.join(...packageName.split("/")) : packageName;
519
+
520
+ const candidates = [
521
+ path.join(workspace, "configs", subPath, "package.json"),
522
+ path.join(workspace, "packages", subPath, "package.json"),
523
+ path.join(workspace, "apps", subPath, "package.json"),
524
+ ];
525
+
526
+ for (const candidate of candidates) {
527
+ if (await this.pathExists(candidate)) return true;
491
528
  }
529
+ return false;
492
530
  }
493
531
 
494
532
  private static getDestinationPath(templateRelativePath: string, newName: string): string {
@@ -559,6 +597,76 @@ export class PackageCommand {
559
597
  });
560
598
  }
561
599
 
600
+ private static async syncDepsWithCatalog(props: {
601
+ workspace: string;
602
+ templateDir: string;
603
+ packageJson: Record<string, unknown>;
604
+ }): Promise<void> {
605
+ const workspacePath = path.join(props.workspace, "pnpm-workspace.yaml");
606
+ let content: string;
607
+ try {
608
+ content = await fs.readFile(workspacePath, "utf-8");
609
+ } catch {
610
+ logger.warn(`Could not find pnpm workspace file at ${workspacePath}.`);
611
+ return;
612
+ }
613
+
614
+ const doc = YAML.parseDocument(content);
615
+ const catalog = doc.getIn(["catalog"]) as Record<string, string> | undefined;
616
+ if (!catalog) return;
617
+
618
+ const templateCatalog = await this.loadTemplateCatalog(props.templateDir);
619
+
620
+ const deps = props.packageJson.dependencies as Record<string, string> | undefined;
621
+ const devDeps = props.packageJson.devDependencies as Record<string, string> | undefined;
622
+ const newEntries: Record<string, string> = {};
623
+
624
+ const processMap = (depMap: Record<string, string> | undefined) => {
625
+ if (!depMap) return;
626
+ for (const [name, version] of Object.entries(depMap)) {
627
+ if (version.startsWith("workspace:")) continue;
628
+
629
+ if (version === "catalog:") {
630
+ // Valid only if the user's catalog already has this entry.
631
+ // If not, resolve the real version from the template's catalog and add it.
632
+ if (!catalog[name]) {
633
+ const templateVersion = templateCatalog[name];
634
+ if (templateVersion) newEntries[name] = templateVersion;
635
+ }
636
+ } else {
637
+ // Hardcoded version — normalize to catalog:
638
+ depMap[name] = "catalog:";
639
+ if (!catalog[name]) newEntries[name] = version;
640
+ }
641
+ }
642
+ };
643
+
644
+ processMap(deps);
645
+ processMap(devDeps);
646
+
647
+ if (Object.keys(newEntries).length === 0) return;
648
+
649
+ for (const [name, version] of Object.entries(newEntries)) {
650
+ doc.setIn(["catalog", name], version);
651
+ }
652
+
653
+ await fs.writeFile(workspacePath, doc.toString());
654
+ logger.info("Added to pnpm-workspace.yaml catalog:");
655
+ for (const [name, version] of Object.entries(newEntries)) {
656
+ logger.info(` + ${name}: ${version}`);
657
+ }
658
+ }
659
+
660
+ private static async loadTemplateCatalog(templateDir: string): Promise<Record<string, string>> {
661
+ try {
662
+ const raw = await fs.readFile(path.join(templateDir, "pnpm-workspace.yaml"), "utf-8");
663
+ const doc = YAML.parseDocument(raw);
664
+ return (doc.getIn(["catalog"]) as Record<string, string>) ?? {};
665
+ } catch {
666
+ logger.warn(`Could not find pnpm-workspace.yaml template in ${templateDir}.`);
667
+ return {};
668
+ }
669
+ }
562
670
  private static async writeJson(file: string, content: object) {
563
671
  await fs.writeFile(file, `${JSON.stringify(content, null, 2)}\n`);
564
672
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "startx",
3
3
  "description": "",
4
- "version": "1.1.11",
4
+ "version": "1.1.31",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/avinashid/startx.git"
@@ -1,6 +1,6 @@
1
1
  import fs from "fs/promises";
2
2
  import path from "path";
3
- import YAML from "yaml";
3
+ import * as YAML from "yaml";
4
4
 
5
5
  import { __dirname } from "../utils.js";
6
6
 
@@ -163,7 +163,18 @@ export abstract class IUserSession {
163
163
  refreshToken: newRefreshToken,
164
164
  };
165
165
  }
166
+ public async createAccessToken(sessionId: string): Promise<string | null> {
167
+ const session = await this.getSession(sessionId);
168
+ if (!session) return null;
166
169
 
170
+ const accessToken = TokenModule.signAccessToken({
171
+ userID: session.user.id,
172
+ email: session.user.email,
173
+ sessionID: sessionId,
174
+ });
175
+
176
+ return accessToken;
177
+ }
167
178
  public async endSession(sessionId: string): Promise<void> {
168
179
  const session = await this.getSession(sessionId);
169
180
  if (!session) return;