@appinit/cli 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +57 -22
- package/README.md +27 -95
- package/dist/index.d.ts +371 -2
- package/dist/index.js +3794 -95
- package/dist/templates/backend-express/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/.env.example +4 -0
- package/dist/templates/backend-express/.appinit-generate/project/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/.appinit-generate/project/.prettierignore +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/.prettierrc +28 -0
- package/dist/templates/backend-express/.appinit-generate/project/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/.appinit-generate/project/Dockerfile +14 -0
- package/dist/templates/backend-express/.appinit-generate/project/README.md +26 -0
- package/dist/templates/backend-express/.appinit-generate/project/eslint.config.js +76 -0
- package/dist/templates/backend-express/.appinit-generate/project/nodemon.json +8 -0
- package/dist/templates/backend-express/.appinit-generate/project/package.json +69 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/app.ts +72 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/env.ts +62 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/config/test.ts +3 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/server.ts +54 -0
- package/dist/templates/backend-express/.appinit-generate/project/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/.appinit-generate/project/tsconfig.json +30 -0
- package/dist/templates/backend-express/.appinit-generate/project/vitest.config.ts +15 -0
- package/dist/templates/backend-express/.appinit-preview/project/.appinit/project.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/.appinit/template.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/.env.example +4 -0
- package/dist/templates/backend-express/.appinit-preview/project/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/.appinit-preview/project/.prettierignore +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/.prettierrc +28 -0
- package/dist/templates/backend-express/.appinit-preview/project/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/.appinit-preview/project/Dockerfile +14 -0
- package/dist/templates/backend-express/.appinit-preview/project/README.md +26 -0
- package/dist/templates/backend-express/.appinit-preview/project/eslint.config.js +76 -0
- package/dist/templates/backend-express/.appinit-preview/project/nodemon.json +8 -0
- package/dist/templates/backend-express/.appinit-preview/project/package.json +69 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/app.ts +72 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/env.ts +62 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/config/test.ts +3 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/server.ts +54 -0
- package/dist/templates/backend-express/.appinit-preview/project/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/.appinit-preview/project/tsconfig.json +30 -0
- package/dist/templates/backend-express/.appinit-preview/project/vitest.config.ts +15 -0
- package/dist/templates/backend-express/README.md +107 -0
- package/dist/templates/backend-express/appinit.template.json +25 -0
- package/dist/templates/backend-express/docs/README.md +11 -0
- package/dist/templates/backend-express/package.json +5 -0
- package/dist/templates/backend-express/template/.env.example +4 -0
- package/dist/templates/backend-express/template/.husky/pre-commit +1 -0
- package/dist/templates/backend-express/template/.prettierignore +3 -0
- package/dist/templates/backend-express/template/.prettierrc +28 -0
- package/dist/templates/backend-express/template/.vscode/settings.json +6 -0
- package/dist/templates/backend-express/template/Dockerfile +14 -0
- package/dist/templates/backend-express/template/README.md__tmpl +26 -0
- package/dist/templates/backend-express/template/eslint.config.js +76 -0
- package/dist/templates/backend-express/template/nodemon.json +8 -0
- package/dist/templates/backend-express/template/package.json__tmpl +69 -0
- package/dist/templates/backend-express/template/src/__tests__/health.test.ts +19 -0
- package/dist/templates/backend-express/template/src/__tests__/validation.test.ts +36 -0
- package/dist/templates/backend-express/template/src/app.ts +72 -0
- package/dist/templates/backend-express/template/src/config/defaults.ts +5 -0
- package/dist/templates/backend-express/template/src/config/dev.ts +3 -0
- package/dist/templates/backend-express/template/src/config/env.ts +62 -0
- package/dist/templates/backend-express/template/src/config/prod.ts +3 -0
- package/dist/templates/backend-express/template/src/config/test.ts +3 -0
- package/dist/templates/backend-express/template/src/errors/app-error.ts +20 -0
- package/dist/templates/backend-express/template/src/errors/not-found.ts +16 -0
- package/dist/templates/backend-express/template/src/middleware/error-handler.ts +73 -0
- package/dist/templates/backend-express/template/src/middleware/http-logger.ts +5 -0
- package/dist/templates/backend-express/template/src/middleware/validate.ts +17 -0
- package/dist/templates/backend-express/template/src/routes/health.ts +13 -0
- package/dist/templates/backend-express/template/src/routes/users.ts +12 -0
- package/dist/templates/backend-express/template/src/schema/create-user.schema.ts +8 -0
- package/dist/templates/backend-express/template/src/server.ts +54 -0
- package/dist/templates/backend-express/template/src/utils/logger.ts +21 -0
- package/dist/templates/backend-express/template/tsconfig.json +30 -0
- package/dist/templates/backend-express/template/vitest.config.ts +15 -0
- package/dist/templates/backend-express/template.config.ts +36 -0
- package/dist/templates/backend-express/variables/defaults.json +7 -0
- package/dist/templates/backend-express/variables/defaults.ts +4 -0
- package/dist/templates/backend-express/variables/schema.json +22 -0
- package/dist/templates/backend-express/variables/schema.ts +11 -0
- package/dist/templates/backend-express/variables/transform.ts +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/README.md +26 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/nodemon.json +8 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/package-lock.json +3245 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/package.json +24 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/src/app.ts +13 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/src/server.ts +15 -0
- package/dist/templates/backend-fastify/.appinit-generate/project/tsconfig.json +30 -0
- package/dist/templates/backend-fastify/README.md +27 -0
- package/dist/templates/backend-fastify/appinit.template.json +25 -0
- package/dist/templates/backend-fastify/docs/README.md +12 -0
- package/dist/templates/backend-fastify/package.json +5 -0
- package/dist/templates/backend-fastify/template/README.md__tmpl +26 -0
- package/dist/templates/backend-fastify/template/nodemon.json +8 -0
- package/dist/templates/backend-fastify/template/package.json__tmpl +24 -0
- package/dist/templates/backend-fastify/template/src/app.ts +13 -0
- package/dist/templates/backend-fastify/template/src/server.ts +15 -0
- package/dist/templates/backend-fastify/template/tsconfig.json +30 -0
- package/dist/templates/backend-fastify/template.config.ts +32 -0
- package/dist/templates/backend-fastify/variables/defaults.json +4 -0
- package/dist/templates/backend-fastify/variables/defaults.ts +3 -0
- package/dist/templates/backend-fastify/variables/schema.json +7 -0
- package/dist/templates/backend-fastify/variables/schema.ts +1 -0
- package/dist/templates/backend-fastify/variables/transform.ts +3 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/README.md +33 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/package.json +25 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-generate/project/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/README.md +33 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/package.json +25 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/.appinit-preview/project/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/README.md +27 -0
- package/dist/templates/frontend-nextjs/appinit.template.json +25 -0
- package/dist/templates/frontend-nextjs/docs/README.md +12 -0
- package/dist/templates/frontend-nextjs/package.json +5 -0
- package/dist/templates/frontend-nextjs/template/README.md__tmpl +33 -0
- package/dist/templates/frontend-nextjs/template/app/favicon.ico +0 -0
- package/dist/templates/frontend-nextjs/template/app/globals.css +42 -0
- package/dist/templates/frontend-nextjs/template/app/layout.tsx +33 -0
- package/dist/templates/frontend-nextjs/template/app/page.module.css +141 -0
- package/dist/templates/frontend-nextjs/template/app/page.tsx +67 -0
- package/dist/templates/frontend-nextjs/template/next-env.d.ts +6 -0
- package/dist/templates/frontend-nextjs/template/next.config.ts +8 -0
- package/dist/templates/frontend-nextjs/template/package.json__tmpl +25 -0
- package/dist/templates/frontend-nextjs/template/public/file.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/globe.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/next.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/vercel.svg +1 -0
- package/dist/templates/frontend-nextjs/template/public/window.svg +1 -0
- package/dist/templates/frontend-nextjs/template/tsconfig.json +34 -0
- package/dist/templates/frontend-nextjs/template.config.ts +28 -0
- package/dist/templates/frontend-nextjs/variables/defaults.json +6 -0
- package/dist/templates/frontend-nextjs/variables/defaults.ts +4 -0
- package/dist/templates/frontend-nextjs/variables/schema.json +22 -0
- package/dist/templates/frontend-nextjs/variables/schema.ts +7 -0
- package/dist/templates/frontend-nextjs/variables/transform.ts +3 -0
- package/dist/templates/frontend-react/.appinit-generate/project/.appinit/project.json +6 -0
- package/dist/templates/frontend-react/.appinit-generate/project/.appinit/template.json +6 -0
- package/dist/templates/frontend-react/appinit.template.json +26 -0
- package/dist/templates/frontend-react/template.config.ts +14 -18
- package/dist/templates/frontend-react/template.meta.json +10 -0
- package/dist/templates/frontend-react/variables/defaults.json +6 -0
- package/dist/templates/frontend-react/variables/schema.json +15 -0
- package/dist/templates/frontend-vue/README.md +27 -0
- package/dist/templates/frontend-vue/appinit.template.json +25 -0
- package/dist/templates/frontend-vue/docs/README.md +12 -0
- package/dist/templates/frontend-vue/package.json +5 -0
- package/dist/templates/frontend-vue/template/README.md__tmpl +31 -0
- package/dist/templates/frontend-vue/template/index.html +13 -0
- package/dist/templates/frontend-vue/template/package.json__tmpl +25 -0
- package/dist/templates/frontend-vue/template/public/vite.svg +1 -0
- package/dist/templates/frontend-vue/template/src/App.vue +30 -0
- package/dist/templates/frontend-vue/template/src/assets/vue.svg +1 -0
- package/dist/templates/frontend-vue/template/src/components/HelloWorld.vue +42 -0
- package/dist/templates/frontend-vue/template/src/main.ts +6 -0
- package/dist/templates/frontend-vue/template/src/style.css +79 -0
- package/dist/templates/frontend-vue/template/tsconfig.app.json +16 -0
- package/dist/templates/frontend-vue/template/tsconfig.json +7 -0
- package/dist/templates/frontend-vue/template/tsconfig.node.json +26 -0
- package/dist/templates/frontend-vue/template/vite.config.ts +7 -0
- package/dist/templates/frontend-vue/template.config.ts +32 -0
- package/dist/templates/frontend-vue/variables/defaults.json +6 -0
- package/dist/templates/frontend-vue/variables/defaults.ts +3 -0
- package/dist/templates/frontend-vue/variables/schema.json +22 -0
- package/dist/templates/frontend-vue/variables/schema.ts +1 -0
- package/dist/templates/frontend-vue/variables/transform.ts +3 -0
- package/package.json +41 -51
- package/bin/appinit.js +0 -15
- /package/dist/templates/frontend-react/template/{.env__tmpl → .env.example__tmpl} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,97 +1,3796 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import*as Ye from'fs';import Ye__default,{readFileSync,statSync,stat,readFile,existsSync,symlinkSync}from'fs';import*as Ee from'path';import Ee__default,{sep,resolve,dirname,extname}from'path';import*as ho from'fs/promises';import ho__default,{mkdir,rm,readFile as readFile$1,cp as cp$1,readdir,lstat}from'fs/promises';import {build}from'esbuild';import Qf,{tmpdir}from'os';import {fileURLToPath,pathToFileURL}from'url';import rf from'crypto';import {PassThrough}from'stream';import {createRequire}from'module';import {exec,spawn}from'child_process';import {note,select,isCancel,cancel,multiselect,confirm,text,spinner}from'@clack/prompts';import Rf,{promisify}from'util';import N from'kleur';import {isatty}from'tty';import mr from'process';import Bf from'http';import Gf from'sirv';import Kf from'chokidar';import Xf from'mri';var Oa=Object.create;var Qn=Object.defineProperty;var Pa=Object.getOwnPropertyDescriptor;var Ia=Object.getOwnPropertyNames;var ja=Object.getPrototypeOf,Fa=Object.prototype.hasOwnProperty;var $a=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var We=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Na=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ia(e))!Fa.call(t,i)&&i!==r&&Qn(t,i,{get:()=>e[i],enumerable:!(n=Pa(e,i))||n.enumerable});return t};var Yn=(t,e,r)=>(r=t!=null?Oa(ja(t)):{},Na(Qn(r,"default",{value:t,enumerable:true}),t));var ot=We((nm,ri)=>{var Zn="[^\\\\/]",Ma="(?=.)",Xn="[^/]",yr="(?:\\/|$)",ei="(?:^|\\/)",wr=`\\.{1,2}${yr}`,Ha="(?!\\.)",Va=`(?!${ei}${wr})`,za=`(?!\\.{0,1}${yr})`,qa=`(?!${wr})`,Ua="[^.\\/]",Ba=`${Xn}*?`,Ga="/",ti={DOT_LITERAL:"\\.",PLUS_LITERAL:"\\+",QMARK_LITERAL:"\\?",SLASH_LITERAL:"\\/",ONE_CHAR:Ma,QMARK:Xn,END_ANCHOR:yr,DOTS_SLASH:wr,NO_DOT:Ha,NO_DOTS:Va,NO_DOT_SLASH:za,NO_DOTS_SLASH:qa,QMARK_NO_DOT:Ua,STAR:Ba,START_ANCHOR:ei,SEP:Ga},Wa={...ti,SLASH_LITERAL:"[\\\\/]",QMARK:Zn,STAR:`${Zn}*?`,DOTS_SLASH:"\\.{1,2}(?:[\\\\/]|$)",NO_DOT:"(?!\\.)",NO_DOTS:"(?!(?:^|[\\\\/])\\.{1,2}(?:[\\\\/]|$))",NO_DOT_SLASH:"(?!\\.{0,1}(?:[\\\\/]|$))",NO_DOTS_SLASH:"(?!\\.{1,2}(?:[\\\\/]|$))",QMARK_NO_DOT:"[^.\\\\/]",START_ANCHOR:"(?:^|[\\\\/])",END_ANCHOR:"(?:[\\\\/]|$)",SEP:"\\"},Ka={alnum:"a-zA-Z0-9",alpha:"a-zA-Z",ascii:"\\x00-\\x7F",blank:" \\t",cntrl:"\\x00-\\x1F\\x7F",digit:"0-9",graph:"\\x21-\\x7E",lower:"a-z",print:"\\x20-\\x7E ",punct:"\\-!\"#$%&'()\\*+,./:;<=>?@[\\]^_`{|}~",space:" \\t\\r\\n\\v\\f",upper:"A-Z",word:"A-Za-z0-9_",xdigit:"A-Fa-f0-9"};ri.exports={MAX_LENGTH:1024*64,POSIX_REGEX_SOURCE:Ka,REGEX_BACKSLASH:/\\(?![*+?^${}(|)[\]])/g,REGEX_NON_SPECIAL_CHARS:/^[^@![\].,$*+?^{}()|\\/]+/,REGEX_SPECIAL_CHARS:/[-*+?.^${}(|)[\]]/,REGEX_SPECIAL_CHARS_BACKREF:/(\\?)((\W)(\3*))/g,REGEX_SPECIAL_CHARS_GLOBAL:/([-*+?.^${}(|)[\]])/g,REGEX_REMOVE_BACKSLASH:/(?:\[.*?[^\\]\]|\\(?=.))/g,REPLACEMENTS:{__proto__:null,"***":"*","**/**":"**","**/**/**":"**"},CHAR_0:48,CHAR_9:57,CHAR_UPPERCASE_A:65,CHAR_LOWERCASE_A:97,CHAR_UPPERCASE_Z:90,CHAR_LOWERCASE_Z:122,CHAR_LEFT_PARENTHESES:40,CHAR_RIGHT_PARENTHESES:41,CHAR_ASTERISK:42,CHAR_AMPERSAND:38,CHAR_AT:64,CHAR_BACKWARD_SLASH:92,CHAR_CARRIAGE_RETURN:13,CHAR_CIRCUMFLEX_ACCENT:94,CHAR_COLON:58,CHAR_COMMA:44,CHAR_DOT:46,CHAR_DOUBLE_QUOTE:34,CHAR_EQUAL:61,CHAR_EXCLAMATION_MARK:33,CHAR_FORM_FEED:12,CHAR_FORWARD_SLASH:47,CHAR_GRAVE_ACCENT:96,CHAR_HASH:35,CHAR_HYPHEN_MINUS:45,CHAR_LEFT_ANGLE_BRACKET:60,CHAR_LEFT_CURLY_BRACE:123,CHAR_LEFT_SQUARE_BRACKET:91,CHAR_LINE_FEED:10,CHAR_NO_BREAK_SPACE:160,CHAR_PERCENT:37,CHAR_PLUS:43,CHAR_QUESTION_MARK:63,CHAR_RIGHT_ANGLE_BRACKET:62,CHAR_RIGHT_CURLY_BRACE:125,CHAR_RIGHT_SQUARE_BRACKET:93,CHAR_SEMICOLON:59,CHAR_SINGLE_QUOTE:39,CHAR_SPACE:32,CHAR_TAB:9,CHAR_UNDERSCORE:95,CHAR_VERTICAL_LINE:124,CHAR_ZERO_WIDTH_NOBREAK_SPACE:65279,extglobChars(t){return {"!":{type:"negate",open:"(?:(?!(?:",close:`))${t.STAR})`},"?":{type:"qmark",open:"(?:",close:")?"},"+":{type:"plus",open:"(?:",close:")+"},"*":{type:"star",open:"(?:",close:")*"},"@":{type:"at",open:"(?:",close:")"}}},globChars(t){return t===true?Wa:ti}};});var at=We(oe=>{var{REGEX_BACKSLASH:Ja,REGEX_REMOVE_BACKSLASH:Qa,REGEX_SPECIAL_CHARS:Ya,REGEX_SPECIAL_CHARS_GLOBAL:Za}=ot();oe.isObject=t=>t!==null&&typeof t=="object"&&!Array.isArray(t);oe.hasRegexChars=t=>Ya.test(t);oe.isRegexChar=t=>t.length===1&&oe.hasRegexChars(t);oe.escapeRegex=t=>t.replace(Za,"\\$1");oe.toPosixSlashes=t=>t.replace(Ja,"/");oe.isWindows=()=>{if(typeof navigator<"u"&&navigator.platform){let t=navigator.platform.toLowerCase();return t==="win32"||t==="windows"}return typeof process<"u"&&process.platform?process.platform==="win32":false};oe.removeBackslashes=t=>t.replace(Qa,e=>e==="\\"?"":e);oe.escapeLast=(t,e,r)=>{let n=t.lastIndexOf(e,r);return n===-1?t:t[n-1]==="\\"?oe.escapeLast(t,e,n-1):`${t.slice(0,n)}\\${t.slice(n)}`};oe.removePrefix=(t,e={})=>{let r=t;return r.startsWith("./")&&(r=r.slice(2),e.prefix="./"),r};oe.wrapOutput=(t,e={},r={})=>{let n=r.contains?"":"^",i=r.contains?"":"$",s=`${n}(?:${t})${i}`;return e.negated===true&&(s=`(?:^(?!${s}).*$)`),s};oe.basename=(t,{windows:e}={})=>{let r=t.split(e?/[\\/]/:"/"),n=r[r.length-1];return n===""?r[r.length-2]:n};});var ui=We((sm,ci)=>{var ni=at(),{CHAR_ASTERISK:br,CHAR_AT:Xa,CHAR_BACKWARD_SLASH:lt,CHAR_COMMA:el,CHAR_DOT:vr,CHAR_EXCLAMATION_MARK:xr,CHAR_FORWARD_SLASH:li,CHAR_LEFT_CURLY_BRACE:kr,CHAR_LEFT_PARENTHESES:Sr,CHAR_LEFT_SQUARE_BRACKET:tl,CHAR_PLUS:rl,CHAR_QUESTION_MARK:ii,CHAR_RIGHT_CURLY_BRACE:nl,CHAR_RIGHT_PARENTHESES:si,CHAR_RIGHT_SQUARE_BRACKET:il}=ot(),oi=t=>t===li||t===lt,ai=t=>{t.isPrefix!==true&&(t.depth=t.isGlobstar?1/0:1);},sl=(t,e)=>{let r=e||{},n=t.length-1,i=r.parts===true||r.scanToEnd===true,s=[],o=[],a=[],l=t,c=-1,u=0,y=0,d=false,b=false,v=false,E=false,ne=false,G=false,D=false,ie=false,ge=false,ee=false,Z=0,le,x,A={value:"",depth:0,isGlob:false},m=()=>c>=n,se=()=>l.charCodeAt(c+1),z=()=>(le=x,l.charCodeAt(++c));for(;c<n;){x=z();let L;if(x===lt){D=A.backslashes=true,x=z(),x===kr&&(G=true);continue}if(G===true||x===kr){for(Z++;m()!==true&&(x=z());){if(x===lt){D=A.backslashes=true,z();continue}if(x===kr){Z++;continue}if(G!==true&&x===vr&&(x=z())===vr){if(d=A.isBrace=true,v=A.isGlob=true,ee=true,i===true)continue;break}if(G!==true&&x===el){if(d=A.isBrace=true,v=A.isGlob=true,ee=true,i===true)continue;break}if(x===nl&&(Z--,Z===0)){G=false,d=A.isBrace=true,ee=true;break}}if(i===true)continue;break}if(x===li){if(s.push(c),o.push(A),A={value:"",depth:0,isGlob:false},ee===true)continue;if(le===vr&&c===u+1){u+=2;continue}y=c+1;continue}if(r.noext!==true&&(x===rl||x===Xa||x===br||x===ii||x===xr)===true&&se()===Sr){if(v=A.isGlob=true,E=A.isExtglob=true,ee=true,x===xr&&c===u&&(ge=true),i===true){for(;m()!==true&&(x=z());){if(x===lt){D=A.backslashes=true,x=z();continue}if(x===si){v=A.isGlob=true,ee=true;break}}continue}break}if(x===br){if(le===br&&(ne=A.isGlobstar=true),v=A.isGlob=true,ee=true,i===true)continue;break}if(x===ii){if(v=A.isGlob=true,ee=true,i===true)continue;break}if(x===tl){for(;m()!==true&&(L=z());){if(L===lt){D=A.backslashes=true,z();continue}if(L===il){b=A.isBracket=true,v=A.isGlob=true,ee=true;break}}if(i===true)continue;break}if(r.nonegate!==true&&x===xr&&c===u){ie=A.negated=true,u++;continue}if(r.noparen!==true&&x===Sr){if(v=A.isGlob=true,i===true){for(;m()!==true&&(x=z());){if(x===Sr){D=A.backslashes=true,x=z();continue}if(x===si){ee=true;break}}continue}break}if(v===true){if(ee=true,i===true)continue;break}}r.noext===true&&(E=false,v=false);let M=l,p="",f="";u>0&&(p=l.slice(0,u),l=l.slice(u),y-=u),M&&v===true&&y>0?(M=l.slice(0,y),f=l.slice(y)):v===true?(M="",f=l):M=l,M&&M!==""&&M!=="/"&&M!==l&&oi(M.charCodeAt(M.length-1))&&(M=M.slice(0,-1)),r.unescape===true&&(f&&(f=ni.removeBackslashes(f)),M&&D===true&&(M=ni.removeBackslashes(M)));let ue={prefix:p,input:t,start:u,base:M,glob:f,isBrace:d,isBracket:b,isGlob:v,isExtglob:E,isGlobstar:ne,negated:ie,negatedExtglob:ge};if(r.tokens===true&&(ue.maxDepth=0,oi(x)||o.push(A),ue.tokens=o),r.parts===true||r.tokens===true){let L;for(let F=0;F<s.length;F++){let ke=L?L+1:u,pe=s[F],De=t.slice(ke,pe);r.tokens&&(F===0&&u!==0?(o[F].isPrefix=true,o[F].value=p):o[F].value=De,ai(o[F]),ue.maxDepth+=o[F].depth),(F!==0||De!=="")&&a.push(De),L=pe;}if(L&&L+1<t.length){let F=t.slice(L+1);a.push(F),r.tokens&&(o[o.length-1].value=F,ai(o[o.length-1]),ue.maxDepth+=o[o.length-1].depth);}ue.slashes=s,ue.parts=a;}return ue};ci.exports=sl;});var mi=We((om,fi)=>{var At=ot(),ye=at(),{MAX_LENGTH:Ct,POSIX_REGEX_SOURCE:ol,REGEX_NON_SPECIAL_CHARS:al,REGEX_SPECIAL_CHARS_BACKREF:ll,REPLACEMENTS:pi}=At,cl=(t,e)=>{if(typeof e.expandRange=="function")return e.expandRange(...t,e);t.sort();let r=`[${t.join("-")}]`;try{new RegExp(r);}catch{return t.map(i=>ye.escapeRegex(i)).join("..")}return r},Ke=(t,e)=>`Missing ${t}: "${e}" - use "\\\\${e}" to match literal characters`,Tr=(t,e)=>{if(typeof t!="string")throw new TypeError("Expected a string");t=pi[t]||t;let r={...e},n=typeof r.maxLength=="number"?Math.min(Ct,r.maxLength):Ct,i=t.length;if(i>n)throw new SyntaxError(`Input length: ${i}, exceeds maximum allowed length: ${n}`);let s={type:"bos",value:"",output:r.prepend||""},o=[s],a=r.capture?"":"?:",l=At.globChars(r.windows),c=At.extglobChars(l),{DOT_LITERAL:u,PLUS_LITERAL:y,SLASH_LITERAL:d,ONE_CHAR:b,DOTS_SLASH:v,NO_DOT:E,NO_DOT_SLASH:ne,NO_DOTS_SLASH:G,QMARK:D,QMARK_NO_DOT:ie,STAR:ge,START_ANCHOR:ee}=l,Z=g=>`(${a}(?:(?!${ee}${g.dot?v:u}).)*?)`,le=r.dot?"":E,x=r.dot?D:ie,A=r.bash===true?Z(r):ge;r.capture&&(A=`(${A})`),typeof r.noext=="boolean"&&(r.noextglob=r.noext);let m={input:t,index:-1,start:0,dot:r.dot===true,consumed:"",output:"",prefix:"",backtrack:false,negated:false,brackets:0,braces:0,parens:0,quotes:0,globstar:false,tokens:o};t=ye.removePrefix(t,m),i=t.length;let se=[],z=[],M=[],p=s,f,ue=()=>m.index===i-1,L=m.peek=(g=1)=>t[m.index+g],F=m.advance=()=>t[++m.index]||"",ke=()=>t.slice(m.index+1),pe=(g="",I=0)=>{m.consumed+=g,m.index+=I;},De=g=>{m.output+=g.output!=null?g.output:g.value,pe(g.value);},Da=()=>{let g=1;for(;L()==="!"&&(L(2)!=="("||L(3)==="?");)F(),m.start++,g++;return g%2===0?false:(m.negated=true,m.start++,true)},St=g=>{m[g]++,M.push(g);},$e=g=>{m[g]--,M.pop();},C=g=>{if(p.type==="globstar"){let I=m.braces>0&&(g.type==="comma"||g.type==="brace"),h=g.extglob===true||se.length&&(g.type==="pipe"||g.type==="paren");g.type!=="slash"&&g.type!=="paren"&&!I&&!h&&(m.output=m.output.slice(0,-p.output.length),p.type="star",p.value="*",p.output=A,m.output+=p.output);}if(se.length&&g.type!=="paren"&&(se[se.length-1].inner+=g.value),(g.value||g.output)&&De(g),p&&p.type==="text"&&g.type==="text"){p.output=(p.output||p.value)+g.value,p.value+=g.value;return}g.prev=p,o.push(g),p=g;},Tt=(g,I)=>{let h={...c[I],conditions:1,inner:""};h.prev=p,h.parens=m.parens,h.output=m.output;let R=(r.capture?"(":"")+h.open;St("parens"),C({type:g,value:I,output:m.output?"":b}),C({type:"paren",extglob:true,value:F(),output:R}),se.push(h);},La=g=>{let I=g.close+(r.capture?")":""),h;if(g.type==="negate"){let R=A;if(g.inner&&g.inner.length>1&&g.inner.includes("/")&&(R=Z(r)),(R!==A||ue()||/^\)+$/.test(ke()))&&(I=g.close=`)$))${R}`),g.inner.includes("*")&&(h=ke())&&/^\.[^\\/.]+$/.test(h)){let $=Tr(h,{...e,fastpaths:false}).output;I=g.close=`)${$})${R})`;}g.prev.type==="bos"&&(m.negatedExtglob=true);}C({type:"paren",extglob:true,value:f,output:I}),$e("parens");};if(r.fastpaths!==false&&!/(^[*!]|[/()[\]{}"])/.test(t)){let g=false,I=t.replace(ll,(h,R,$,te,W,gr)=>te==="\\"?(g=true,h):te==="?"?R?R+te+(W?D.repeat(W.length):""):gr===0?x+(W?D.repeat(W.length):""):D.repeat($.length):te==="."?u.repeat($.length):te==="*"?R?R+te+(W?A:""):A:R?h:`\\${h}`);return g===true&&(r.unescape===true?I=I.replace(/\\/g,""):I=I.replace(/\\+/g,h=>h.length%2===0?"\\\\":h?"\\":"")),I===t&&r.contains===true?(m.output=t,m):(m.output=ye.wrapOutput(I,m,e),m)}for(;!ue();){if(f=F(),f==="\0")continue;if(f==="\\"){let h=L();if(h==="/"&&r.bash!==true||h==="."||h===";")continue;if(!h){f+="\\",C({type:"text",value:f});continue}let R=/^\\+/.exec(ke()),$=0;if(R&&R[0].length>2&&($=R[0].length,m.index+=$,$%2!==0&&(f+="\\")),r.unescape===true?f=F():f+=F(),m.brackets===0){C({type:"text",value:f});continue}}if(m.brackets>0&&(f!=="]"||p.value==="["||p.value==="[^")){if(r.posix!==false&&f===":"){let h=p.value.slice(1);if(h.includes("[")&&(p.posix=true,h.includes(":"))){let R=p.value.lastIndexOf("["),$=p.value.slice(0,R),te=p.value.slice(R+2),W=ol[te];if(W){p.value=$+W,m.backtrack=true,F(),!s.output&&o.indexOf(p)===1&&(s.output=b);continue}}}(f==="["&&L()!==":"||f==="-"&&L()==="]")&&(f=`\\${f}`),f==="]"&&(p.value==="["||p.value==="[^")&&(f=`\\${f}`),r.posix===true&&f==="!"&&p.value==="["&&(f="^"),p.value+=f,De({value:f});continue}if(m.quotes===1&&f!=='"'){f=ye.escapeRegex(f),p.value+=f,De({value:f});continue}if(f==='"'){m.quotes=m.quotes===1?0:1,r.keepQuotes===true&&C({type:"text",value:f});continue}if(f==="("){St("parens"),C({type:"paren",value:f});continue}if(f===")"){if(m.parens===0&&r.strictBrackets===true)throw new SyntaxError(Ke("opening","("));let h=se[se.length-1];if(h&&m.parens===h.parens+1){La(se.pop());continue}C({type:"paren",value:f,output:m.parens?")":"\\)"}),$e("parens");continue}if(f==="["){if(r.nobracket===true||!ke().includes("]")){if(r.nobracket!==true&&r.strictBrackets===true)throw new SyntaxError(Ke("closing","]"));f=`\\${f}`;}else St("brackets");C({type:"bracket",value:f});continue}if(f==="]"){if(r.nobracket===true||p&&p.type==="bracket"&&p.value.length===1){C({type:"text",value:f,output:`\\${f}`});continue}if(m.brackets===0){if(r.strictBrackets===true)throw new SyntaxError(Ke("opening","["));C({type:"text",value:f,output:`\\${f}`});continue}$e("brackets");let h=p.value.slice(1);if(p.posix!==true&&h[0]==="^"&&!h.includes("/")&&(f=`/${f}`),p.value+=f,De({value:f}),r.literalBrackets===false||ye.hasRegexChars(h))continue;let R=ye.escapeRegex(p.value);if(m.output=m.output.slice(0,-p.value.length),r.literalBrackets===true){m.output+=R,p.value=R;continue}p.value=`(${a}${R}|${p.value})`,m.output+=p.value;continue}if(f==="{"&&r.nobrace!==true){St("braces");let h={type:"brace",value:f,output:"(",outputIndex:m.output.length,tokensIndex:m.tokens.length};z.push(h),C(h);continue}if(f==="}"){let h=z[z.length-1];if(r.nobrace===true||!h){C({type:"text",value:f,output:f});continue}let R=")";if(h.dots===true){let $=o.slice(),te=[];for(let W=$.length-1;W>=0&&(o.pop(),$[W].type!=="brace");W--)$[W].type!=="dots"&&te.unshift($[W].value);R=cl(te,r),m.backtrack=true;}if(h.comma!==true&&h.dots!==true){let $=m.output.slice(0,h.outputIndex),te=m.tokens.slice(h.tokensIndex);h.value=h.output="\\{",f=R="\\}",m.output=$;for(let W of te)m.output+=W.output||W.value;}C({type:"brace",value:f,output:R}),$e("braces"),z.pop();continue}if(f==="|"){se.length>0&&se[se.length-1].conditions++,C({type:"text",value:f});continue}if(f===","){let h=f,R=z[z.length-1];R&&M[M.length-1]==="braces"&&(R.comma=true,h="|"),C({type:"comma",value:f,output:h});continue}if(f==="/"){if(p.type==="dot"&&m.index===m.start+1){m.start=m.index+1,m.consumed="",m.output="",o.pop(),p=s;continue}C({type:"slash",value:f,output:d});continue}if(f==="."){if(m.braces>0&&p.type==="dot"){p.value==="."&&(p.output=u);let h=z[z.length-1];p.type="dots",p.output+=f,p.value+=f,h.dots=true;continue}if(m.braces+m.parens===0&&p.type!=="bos"&&p.type!=="slash"){C({type:"text",value:f,output:u});continue}C({type:"dot",value:f,output:u});continue}if(f==="?"){if(!(p&&p.value==="(")&&r.noextglob!==true&&L()==="("&&L(2)!=="?"){Tt("qmark",f);continue}if(p&&p.type==="paren"){let R=L(),$=f;(p.value==="("&&!/[!=<:]/.test(R)||R==="<"&&!/<([!=]|\w+>)/.test(ke()))&&($=`\\${f}`),C({type:"text",value:f,output:$});continue}if(r.dot!==true&&(p.type==="slash"||p.type==="bos")){C({type:"qmark",value:f,output:ie});continue}C({type:"qmark",value:f,output:D});continue}if(f==="!"){if(r.noextglob!==true&&L()==="("&&(L(2)!=="?"||!/[!=<:]/.test(L(3)))){Tt("negate",f);continue}if(r.nonegate!==true&&m.index===0){Da();continue}}if(f==="+"){if(r.noextglob!==true&&L()==="("&&L(2)!=="?"){Tt("plus",f);continue}if(p&&p.value==="("||r.regex===false){C({type:"plus",value:f,output:y});continue}if(p&&(p.type==="bracket"||p.type==="paren"||p.type==="brace")||m.parens>0){C({type:"plus",value:f});continue}C({type:"plus",value:y});continue}if(f==="@"){if(r.noextglob!==true&&L()==="("&&L(2)!=="?"){C({type:"at",extglob:true,value:f,output:""});continue}C({type:"text",value:f});continue}if(f!=="*"){(f==="$"||f==="^")&&(f=`\\${f}`);let h=al.exec(ke());h&&(f+=h[0],m.index+=h[0].length),C({type:"text",value:f});continue}if(p&&(p.type==="globstar"||p.star===true)){p.type="star",p.star=true,p.value+=f,p.output=A,m.backtrack=true,m.globstar=true,pe(f);continue}let g=ke();if(r.noextglob!==true&&/^\([^?]/.test(g)){Tt("star",f);continue}if(p.type==="star"){if(r.noglobstar===true){pe(f);continue}let h=p.prev,R=h.prev,$=h.type==="slash"||h.type==="bos",te=R&&(R.type==="star"||R.type==="globstar");if(r.bash===true&&(!$||g[0]&&g[0]!=="/")){C({type:"star",value:f,output:""});continue}let W=m.braces>0&&(h.type==="comma"||h.type==="brace"),gr=se.length&&(h.type==="pipe"||h.type==="paren");if(!$&&h.type!=="paren"&&!W&&!gr){C({type:"star",value:f,output:""});continue}for(;g.slice(0,3)==="/**";){let Rt=t[m.index+4];if(Rt&&Rt!=="/")break;g=g.slice(3),pe("/**",3);}if(h.type==="bos"&&ue()){p.type="globstar",p.value+=f,p.output=Z(r),m.output=p.output,m.globstar=true,pe(f);continue}if(h.type==="slash"&&h.prev.type!=="bos"&&!te&&ue()){m.output=m.output.slice(0,-(h.output+p.output).length),h.output=`(?:${h.output}`,p.type="globstar",p.output=Z(r)+(r.strictSlashes?")":"|$)"),p.value+=f,m.globstar=true,m.output+=h.output+p.output,pe(f);continue}if(h.type==="slash"&&h.prev.type!=="bos"&&g[0]==="/"){let Rt=g[1]!==void 0?"|$":"";m.output=m.output.slice(0,-(h.output+p.output).length),h.output=`(?:${h.output}`,p.type="globstar",p.output=`${Z(r)}${d}|${d}${Rt})`,p.value+=f,m.output+=h.output+p.output,m.globstar=true,pe(f+F()),C({type:"slash",value:"/",output:""});continue}if(h.type==="bos"&&g[0]==="/"){p.type="globstar",p.value+=f,p.output=`(?:^|${d}|${Z(r)}${d})`,m.output=p.output,m.globstar=true,pe(f+F()),C({type:"slash",value:"/",output:""});continue}m.output=m.output.slice(0,-p.output.length),p.type="globstar",p.output=Z(r),p.value+=f,m.output+=p.output,m.globstar=true,pe(f);continue}let I={type:"star",value:f,output:A};if(r.bash===true){I.output=".*?",(p.type==="bos"||p.type==="slash")&&(I.output=le+I.output),C(I);continue}if(p&&(p.type==="bracket"||p.type==="paren")&&r.regex===true){I.output=f,C(I);continue}(m.index===m.start||p.type==="slash"||p.type==="dot")&&(p.type==="dot"?(m.output+=ne,p.output+=ne):r.dot===true?(m.output+=G,p.output+=G):(m.output+=le,p.output+=le),L()!=="*"&&(m.output+=b,p.output+=b)),C(I);}for(;m.brackets>0;){if(r.strictBrackets===true)throw new SyntaxError(Ke("closing","]"));m.output=ye.escapeLast(m.output,"["),$e("brackets");}for(;m.parens>0;){if(r.strictBrackets===true)throw new SyntaxError(Ke("closing",")"));m.output=ye.escapeLast(m.output,"("),$e("parens");}for(;m.braces>0;){if(r.strictBrackets===true)throw new SyntaxError(Ke("closing","}"));m.output=ye.escapeLast(m.output,"{"),$e("braces");}if(r.strictSlashes!==true&&(p.type==="star"||p.type==="bracket")&&C({type:"maybe_slash",value:"",output:`${d}?`}),m.backtrack===true){m.output="";for(let g of m.tokens)m.output+=g.output!=null?g.output:g.value,g.suffix&&(m.output+=g.suffix);}return m};Tr.fastpaths=(t,e)=>{let r={...e},n=typeof r.maxLength=="number"?Math.min(Ct,r.maxLength):Ct,i=t.length;if(i>n)throw new SyntaxError(`Input length: ${i}, exceeds maximum allowed length: ${n}`);t=pi[t]||t;let{DOT_LITERAL:s,SLASH_LITERAL:o,ONE_CHAR:a,DOTS_SLASH:l,NO_DOT:c,NO_DOTS:u,NO_DOTS_SLASH:y,STAR:d,START_ANCHOR:b}=At.globChars(r.windows),v=r.dot?u:c,E=r.dot?y:c,ne=r.capture?"":"?:",G={negated:false,prefix:""},D=r.bash===true?".*?":d;r.capture&&(D=`(${D})`);let ie=le=>le.noglobstar===true?D:`(${ne}(?:(?!${b}${le.dot?l:s}).)*?)`,ge=le=>{switch(le){case "*":return `${v}${a}${D}`;case ".*":return `${s}${a}${D}`;case "*.*":return `${v}${D}${s}${a}${D}`;case "*/*":return `${v}${D}${o}${a}${E}${D}`;case "**":return v+ie(r);case "**/*":return `(?:${v}${ie(r)}${o})?${E}${a}${D}`;case "**/*.*":return `(?:${v}${ie(r)}${o})?${E}${D}${s}${a}${D}`;case "**/.*":return `(?:${v}${ie(r)}${o})?${s}${a}${D}`;default:{let x=/^(.*?)\.(\w+)$/.exec(le);if(!x)return;let A=ge(x[1]);return A?A+s+x[2]:void 0}}},ee=ye.removePrefix(t,G),Z=ge(ee);return Z&&r.strictSlashes!==true&&(Z+=`${o}?`),Z};fi.exports=Tr;});var gi=We((am,di)=>{var ul=ui(),Rr=mi(),hi=at(),pl=ot(),fl=t=>t&&typeof t=="object"&&!Array.isArray(t),H=(t,e,r=false)=>{if(Array.isArray(t)){let u=t.map(d=>H(d,e,r));return d=>{for(let b of u){let v=b(d);if(v)return v}return false}}let n=fl(t)&&t.tokens&&t.input;if(t===""||typeof t!="string"&&!n)throw new TypeError("Expected pattern to be a non-empty string");let i=e||{},s=i.windows,o=n?H.compileRe(t,e):H.makeRe(t,e,false,true),a=o.state;delete o.state;let l=()=>false;if(i.ignore){let u={...e,ignore:null,onMatch:null,onResult:null};l=H(i.ignore,u,r);}let c=(u,y=false)=>{let{isMatch:d,match:b,output:v}=H.test(u,o,e,{glob:t,posix:s}),E={glob:t,state:a,regex:o,posix:s,input:u,output:v,match:b,isMatch:d};return typeof i.onResult=="function"&&i.onResult(E),d===false?(E.isMatch=false,y?E:false):l(u)?(typeof i.onIgnore=="function"&&i.onIgnore(E),E.isMatch=false,y?E:false):(typeof i.onMatch=="function"&&i.onMatch(E),y?E:true)};return r&&(c.state=a),c};H.test=(t,e,r,{glob:n,posix:i}={})=>{if(typeof t!="string")throw new TypeError("Expected input to be a string");if(t==="")return {isMatch:false,output:""};let s=r||{},o=s.format||(i?hi.toPosixSlashes:null),a=t===n,l=a&&o?o(t):t;return a===false&&(l=o?o(t):t,a=l===n),(a===false||s.capture===true)&&(s.matchBase===true||s.basename===true?a=H.matchBase(t,e,r,i):a=e.exec(l)),{isMatch:!!a,match:a,output:l}};H.matchBase=(t,e,r)=>(e instanceof RegExp?e:H.makeRe(e,r)).test(hi.basename(t));H.isMatch=(t,e,r)=>H(e,r)(t);H.parse=(t,e)=>Array.isArray(t)?t.map(r=>H.parse(r,e)):Rr(t,{...e,fastpaths:false});H.scan=(t,e)=>ul(t,e);H.compileRe=(t,e,r=false,n=false)=>{if(r===true)return t.output;let i=e||{},s=i.contains?"":"^",o=i.contains?"":"$",a=`${s}(?:${t.output})${o}`;t&&t.negated===true&&(a=`^(?!${a}).*$`);let l=H.toRegex(a,e);return n===true&&(l.state=t),l};H.makeRe=(t,e={},r=false,n=false)=>{if(!t||typeof t!="string")throw new TypeError("Expected a non-empty string");let i={negated:false,fastpaths:true};return e.fastpaths!==false&&(t[0]==="."||t[0]==="*")&&(i.output=Rr.fastpaths(t,e)),i.output||(i=Rr(t,e)),H.compileRe(i,e,r,n)};H.toRegex=(t,e)=>{try{let r=e||{};return new RegExp(t,r.flags||(r.nocase?"i":""))}catch(r){if(e&&e.debug===true)throw r;return /$^/}};H.constants=pl;di.exports=H;});var Ar=We((lm,bi)=>{var yi=gi(),ml=at();function wi(t,e,r=false){return e&&(e.windows===null||e.windows===void 0)&&(e={...e,windows:ml.isWindows()}),yi(t,e,r)}Object.assign(wi,yi);bi.exports=wi;});var xi=Yn(Ar());var q=async t=>mkdir(t,{recursive:true}),Je=async t=>rm(t,{recursive:true,force:true}),U=t=>existsSync(t),Qe=t=>readFile$1(t,"utf8");function xl(t){return t.replace(/\\/g,"/")}function k(t,e){return xl(Ee.join(t,e))}var ct=async(t,e,r)=>{await mkdir(Ee.dirname(e),{recursive:true}),await cp$1(t,e,{recursive:true,force:!(r?.errorOnExist??false)});};async function _t(t,e="",r=[]){let n=[],i=await readdir(t),s=r.length>0?(0, xi.default)(r,{dot:true}):()=>false;for(let o of i){let a=Ee.join(t,o),l=Ee.posix.join(e,o);if(s(l))continue;let c;try{c=await lstat(a);}catch{continue}if(!c.isSymbolicLink())if(c.isDirectory()){let u=await _t(a,l,r);n.push(...u);}else n.push(l);}return n}async function ki(t,e,r){if(r==="dev")try{symlinkSync(t,e,"junction");return}catch{await ct(t,e,{});return}await ct(t,e,{});}var Si=t=>t.replace(/-([a-zA-Z0-9])/g,(e,r)=>r.toUpperCase());var Sl=new Set([".png",".jpg",".jpeg",".gif",".webp",".ico",".bmp",".svgz",".woff",".woff2",".ttf",".otf",".eot",".mp3",".mp4",".wav",".ogg",".webm",".zip",".tar",".gz",".tgz",".rar",".7z",".pdf",".exe",".dll",".bin"]);function Ti(t){return Sl.has(Ee__default.extname(t).toLowerCase())}async function he(t){if(!U(t))return console.warn(`\u26A0 File not found: ${t}`),null;let e=Ee__default.extname(t).toLowerCase();if(e===".json")return JSON.parse(await Qe(t));if(e===".md"||e===".txt"||e===".yaml"||e===".yml")return await Qe(t);if(e===".js"||e===".mjs"||e===".cjs")return await import(pathToFileURL(t).href);if(e===".ts"){let r=Ee__default.join(tmpdir(),`appinit.template.${Date.now()}.mjs`);try{return await build({entryPoints:[t],outfile:r,bundle:!0,platform:"neutral",format:"esm",sourcemap:"inline",target:["es2019"]}),await import(pathToFileURL(r).href)}catch(n){throw console.warn(`\u26A0 esbuild transpile failed for ${t}: ${String(n)}`),n}finally{try{await ho__default.unlink(r);}catch{}}}return await Qe(t)}async function _i(t){let{template:e,templatesDir:r,logger:n,mode:i,builtinTemplatesDir:s}=t;if(!e)throw new Error("ensureTemplateInstalled: template is required");if(e.includes("/")||e.includes(".."))throw new Error(`Invalid template ID: ${e}`);if(i==="dev")throw new Error("ensureTemplateInstalled should not be called in dev mode");let o=k(r,e),a=k(o,"current");if(U(a))return a;if(!s)throw new Error("No built-in templates directory available");let l=k(s,e);if(!U(l))throw new Error(`Template "${e}" is not installed and not available as built-in.`);n?.info?.(`\u{1F4E6} Installing template "${e}"`),console.log("templateRoot",o),await q(o);let c=k(k(o,"versions"),"1.0.0");await q(c),await ct(l,c),await ct(c,a);let u=k(o,"template.json");return U(u)||await ho.writeFile(u,JSON.stringify({id:e,installedAt:new Date().toISOString(),activeVersion:"1.0.0",ats:"1.0",source:"builtin"},null,2)),a}async function Et(t){let e=Ee__default.join(t,"variables","defaults.ts");try{return (await import(e)).defaults??{}}catch{return {}}}function Ei(t){if(typeof t!="object"||!t)throw new Error("Template config must be an object");if(!t.id||typeof t.id!="string")throw new Error("Template config requires a valid 'id'");if(!t.version||typeof t.version!="string")throw new Error("Template config requires a valid 'version'");if(t.appinitSpec!=="1.0")throw new Error(`Unsupported ATS version: ${t.appinitSpec}`);return {...t,projectKind:t.category}}async function Di(t){let e=Ee__default.join(t,"template.config.ts");if(!Ye__default.existsSync(e))throw new Error("Missing required file: template.config.ts");let r=pathToFileURL(e).href,n;try{n=await import(r);}catch(o){throw new Error(`Failed to load template.config.ts: ${o.message}`)}if(!n||typeof n.config!="object")throw new Error("template.config.ts must export a named 'config' object");if("default"in n)throw new Error("Default export is not allowed in template.config.ts");let i=n.config;return Ei(i)}async function Pi(t){let{intent:e}=t,{target:r}=e,n=r.rootDir,i=r.kind,s={rootDir:n,scope:i,intent:e,projectKind:r.projectKind,cliVersion:t.cliVersion,supportedAtsVersions:t.supportedAtsVersions},o=Ee__default.join(n,".appinit");if(Ye__default.existsSync(o)&&(s.appinitDir=o),i==="template"){let a=Ee__default.join(n,"template.config.ts");if(Ye__default.existsSync(a)){s.templateConfigPath=a;let l=await Di(n);s.atsVersion=l.appinitSpec,s.templateId=l.id,s.templateVersion=l.version,s.projectKind=l.projectKind;}}return s}var Ii={id:"appinit-dir",appliesTo:["template"],actors:["template-author"],async run(t){return t.appinitDir?{id:"appinit-dir",title:".appinit directory",status:"ok"}:{id:"appinit-dir",title:".appinit directory",status:"error",message:".appinit directory not found",suggestion:"Initialize the template using AppInit"}}};var ji={id:"appinit-present",appliesTo:["project"],actors:["project-user"],async run(t){return t.rootDir?t.appinitDir?{id:"appinit-present",title:"AppInit management",status:"ok"}:{id:"appinit-present",title:"AppInit management",status:"error",message:"This project is not managed by AppInit",suggestion:"Run `appinit create` to scaffold a new AppInit project"}:{id:"appinit-present",title:"AppInit management",status:"error",message:"Project root could not be determined"}}};var Fi={id:"appinit-root",appliesTo:["template"],actors:["template-author"],async run(t){return t.appinitDir?{id:"appinit-root",title:"AppInit project root",status:"ok"}:{id:"appinit-root",title:"AppInit project root",status:"error",message:"Missing .appinit directory at template root",suggestion:"Ensure you are running the command from a valid AppInit template root"}}};var $i={id:"ats-version",appliesTo:["template"],actors:["template-author"],async run(t){let{atsVersion:e,supportedAtsVersions:r}=t;return e?r.includes(e)?{id:"ats-version",title:"ATS version",status:"ok"}:{id:"ats-version",title:"ATS version",status:"warn",message:`ATS version "${e}" is not supported by this CLI`,suggestion:"Upgrade the AppInit CLI or use a compatible template"}:{id:"ats-version",title:"ATS version",status:"error",message:"ATS version is not declared in template.config.ts",suggestion:"Declare `appinitSpec` (ATS version) in template.config.ts"}}};var Ni={id:"cli-compat",appliesTo:["template"],actors:["template-author"],async run(t){let{cliVersion:e,atsVersion:r,supportedAtsVersions:n}=t;return r?n.includes(r)?{id:"cli-compat",title:"CLI compatibility",status:"ok"}:{id:"cli-compat",title:"CLI compatibility",status:"error",message:`CLI v${e} does not support ATS ${r}`,suggestion:"Upgrade the AppInit CLI or use a compatible template"}:{id:"cli-compat",title:"CLI compatibility",status:"error",message:"Cannot determine CLI compatibility without an ATS version",suggestion:"Declare `appinitSpec` in template.config.ts"}}};var Mi={id:"dependencies-installed",appliesTo:["project"],actors:["project-user"],async run(t){if(!t.rootDir)return {id:"dependencies-installed",title:"Dependencies installed",status:"warn",message:"Project root could not be determined"};let r=Ee__default.join(t.rootDir,"node_modules");return Ye__default.existsSync(r)?{id:"dependencies-installed",title:"Dependencies installed",status:"ok"}:{id:"dependencies-installed",title:"Dependencies installed",status:"warn",message:"Dependencies are not installed",suggestion:"Run your package manager install command (e.g. pnpm install)"}}};var Hi={id:"package-json",appliesTo:["project"],actors:["project-user"],async run(t){if(!t.rootDir)return {id:"package-json",title:"package.json",status:"error",message:"Project root could not be determined"};let e=Ee__default.join(t.rootDir,"package.json");return Ye__default.existsSync(e)?{id:"package-json",title:"package.json",status:"ok"}:{id:"package-json",title:"package.json",status:"error",message:"package.json not found",suggestion:"Ensure this is a valid Node.js project"}}};var zi={id:"project-root",appliesTo:["project"],actors:["project-user"],async run(t){let e=t.rootDir;try{if(!Ye.statSync(e).isDirectory())return {id:"project-root",title:"Project root",status:"error",message:"Resolved project root is not a directory"}}catch{return {id:"project-root",title:"Project root",status:"error",message:"Unable to access the resolved project directory",suggestion:"Run this command inside an AppInit project"}}return {id:"project-root",title:"Project root",status:"ok"}}};var qi={id:"template-config",appliesTo:["template"],actors:["template-author"],async run(t){return t.templateConfigPath?{id:"template-config",title:"template.config.ts",status:"ok"}:{id:"template-config",title:"template.config.ts",status:"error",message:"template.config.ts not found at template root",suggestion:"Ensure your template follows ATS 1.0 and includes template.config.ts at the root"}}};async function Ui(t,e){let r=[];for(let n of e)if(!(n.appliesTo&&!n.appliesTo.includes(t.scope))&&!(n.actors&&!n.actors.includes(t.intent.actor))&&!(n.modes&&!n.modes.includes(t.intent.mode)))try{let i=await n.run(t);Array.isArray(i)?r.push(...i):r.push(i);}catch(i){r.push({id:n.id,title:n.id,status:"error",message:i instanceof Error?i.message:"Unknown error occurred",suggestion:"Please report this issue to AppInit"});}return r}async function Dt(t){let{cwd:e,cliVersion:r,supportedAtsVersions:n,intent:i}=t,s=await Pi({cliVersion:r,supportedAtsVersions:n,intent:i}),a=await Ui(s,[zi,ji,Hi,Mi,Fi,Ii,qi,$i,Ni]);return {ok:a.every(c=>c.status!=="error"),results:a,cwd:e,intent:i}}var Bi=()=>!!process.env.npm_lifecycle_event;async function Gi(){try{if(process.env.DOCKER||process.env.CONTAINER||U("/.dockerenv"))return !0;if(U("/proc/1/cgroup")){let t=await Qe("/proc/1/cgroup");if(t.includes("docker")||t.includes("container"))return !0}return !1}catch{return false}}var Wi=t=>["turborepo","nx","monorepo"].includes(t);function Ki(t,e){let r={writtenFiles:[],overwrittenFiles:[]};function n(i){let s=Ye__default.readdirSync(i,{withFileTypes:true});for(let o of s){let a=Ee__default.join(i,o.name),l=Ee__default.relative(t,a),c=Ee__default.join(e,l);if(o.isDirectory()){Ye__default.mkdirSync(c,{recursive:true}),n(a);continue}Ye__default.existsSync(c)?r.overwrittenFiles.push(l):r.writtenFiles.push(l),Ye__default.mkdirSync(Ee__default.dirname(c),{recursive:true}),Ye__default.renameSync(a,c);}}return n(t),Ye__default.rmSync(t,{recursive:true,force:true}),r}async function Yi(t,e){for(let r of t){let n=Ee__default.join(e,r.dest);Ye__default.mkdirSync(Ee__default.dirname(n),{recursive:true}),Ye__default.writeFileSync(n,r.content);}}async function ut(t,e){t&&await t(e);}function Zi(t,e){let r=[/\{%\s*include\s+/,/\{%\s*render\s+/];for(let n of r)if(n.test(t))throw new Error(`Forbidden Liquid feature in ${e}. ATS 1.0 allows only {{ }}, {% if %}, {% for %}.`)}var ae=class{constructor(e,r,n,i,s){this.kind=e,this.input=r,this.begin=n,this.end=i,this.file=s;}getText(){return this.input.slice(this.begin,this.end)}getPosition(){let[e,r]=[1,1];for(let n=0;n<this.begin;n++)this.input[n]===`
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`);let n=this.p;return new Kr(this.input,r,n,e,this.file)}error(e,r=this.p){return new Dr(e,new pt(this.input,r,this.N,this.file))}assert(e,r,n){if(!e)throw this.error(typeof r=="function"?r():r,n)}snapshot(e=this.p){return JSON.stringify(Yl(this.input.slice(e,this.N),32))}readWord(){return this.readIdentifier()}readIdentifier(){this.skipBlank();let e=this.p;for(;!this.end()&&Pt(this.peek());)++this.p;return new pt(this.input,e,this.p,this.file)}readNonEmptyIdentifier(){let e=this.readIdentifier();return e.size()?e:void 0}readTagName(){return this.skipBlank(),this.input[this.p]==="#"?this.input.slice(this.p,++this.p):this.readIdentifier().getText()}readHashes(e){let r=[];for(;;){let n=this.readHash(e);if(!n)return r;r.push(n);}}readHash(e){this.skipBlank(),this.peek()===","&&++this.p;let r=this.p,n=this.readNonEmptyIdentifier();if(!n)return;let i;this.skipBlank();let s=S(e)?e:e?"=":":";return this.peek()===s&&(++this.p,i=this.readValue()),new Br(this.input,r,this.p,n,i,this.file)}remaining(){return this.input.slice(this.p,this.N)}advance(e=1){this.p+=e;}end(){return this.p>=this.N}read(){return this.input[this.p++]}readTo(e){for(;this.p<this.N;)if(++this.p,this.rmatch(e))return this.p;return -1}readValue(){this.skipBlank();let e=this.p,r=this.readLiteral()||this.readQuoted()||this.readRange()||this.readNumber(),n=this.readProperties(!r);return n.length?new Ut(r,n,this.input,e,this.p):r}readScopeValue(){this.skipBlank();let e=this.p,r=this.readProperties();if(r.length)return new Ut(void 0,r,this.input,e,this.p)}readProperties(e=true){let r=[];for(;;){if(this.peek()==="["){this.p++;let n=this.readValue()||new pt(this.input,this.p,this.p,this.file);this.assert(this.readTo("]")!==-1,"[ not closed"),r.push(n);continue}if(e&&!r.length){let n=this.readNonEmptyIdentifier();if(n){r.push(n);continue}}if(this.peek()==="."&&this.peek(1)!=="."){this.p++;let n=this.readNonEmptyIdentifier();if(!n)break;r.push(n);continue}break}return r}readNumber(){this.skipBlank();let e=false,r=false,n=0;for(this.peekType()&rc&&n++;this.p+n<=this.N;)if(this.peekType(n)&tc)r=true,n++;else if(this.peek(n)==="."&&this.peek(n+1)!=="."){if(e||!r)return;e=true,n++;}else break;if(r&&!Pt(this.peek(n))){let i=new zr(this.input,this.p,this.p+n,this.file);return this.advance(n),i}}readLiteral(){this.skipBlank();let e=this.matchTrie(this.literalTrie);if(e===-1)return;let r=new qr(this.input,this.p,e,this.file);return this.p=e,r}readRange(){this.skipBlank();let e=this.p;if(this.peek()!=="(")return;++this.p;let r=this.readValueOrThrow();this.skipBlank(),this.assert(this.read()==="."&&this.read()===".","invalid range syntax");let n=this.readValueOrThrow();return this.skipBlank(),this.assert(this.read()===")","invalid range syntax"),new Wr(this.input,e,this.p,r,n,this.file)}readValueOrThrow(){let e=this.readValue();return this.assert(e,()=>`unexpected token ${this.snapshot()}, value expected`),e}readQuoted(){this.skipBlank();let e=this.p;if(!(this.peekType()&ns))return;++this.p;let r=false;for(;this.p<this.N&&(++this.p,!(this.input[this.p-1]===this.input[e]&&!r));)r?r=false:this.input[this.p-1]==="\\"&&(r=true);return new Gr(this.input,e,this.p,this.file)}*readFileNameTemplate(e){let{outputDelimiterLeft:r}=e,n=[","," ",r],i=new Set(n);for(;this.p<this.N&&!i.has(this.peek());)yield this.match(r)?this.readOutputToken(e):this.readHTMLToken(n);}match(e){for(let r=0;r<e.length;r++)if(e[r]!==this.input[this.p+r])return false;return true}rmatch(e){for(let r=0;r<e.length;r++)if(e[e.length-1-r]!==this.input[this.p-1-r])return false;return true}peekType(e=0){return this.p+e>=this.N?0:O[this.input.charCodeAt(this.p+e)]}peek(e=0){return this.p+e>=this.N?"":this.input[this.p+e]}skipBlank(){for(;this.peekType()&et;)++this.p;}},Xr=class{constructor(e,r){this.handlers={},this.stopRequested=false,this.tokens=e,this.parseToken=r;}on(e,r){return this.handlers[e]=r,this}trigger(e,r){let n=this.handlers[e];return n?(n.call(this,r),true):false}start(){this.trigger("start");let e;for(;!this.stopRequested&&(e=this.tokens.shift());){if(this.trigger("token",e)||qe(e)&&this.trigger(`tag:${e.name}`,e))continue;let r=this.parseToken(e,this.tokens);this.trigger("template",r);}return this.stopRequested||this.trigger("end"),this}stop(){return this.stopRequested=true,this}},bt=class{constructor(e){this.token=e;}},j=class extends bt{constructor(e,r,n){super(e),this.name=e.name,this.liquid=n,this.tokenizer=e.tokenizer;}},je=class{constructor(e,r){this.hash={};let n=e instanceof me?e:new me(e,{});for(let i of n.readHashes(r))this.hash[i.name.content]=i.value;}*render(e){let r={};for(let n of Object.keys(this.hash))r[n]=this.hash[n]===void 0?true:yield B(this.hash[n],e);return r}};function ru(t){return class extends j{constructor(e,r,n){super(e,r,n),K(t.parse)&&t.parse.call(this,e,r);}*render(e,r){let n=yield new je(this.token.args,e.opts.keyValueSeparator).render(e);return yield t.render.call(this,e,r,n)}}}function Fs(t){return Y(t)}var Wt=class{constructor(e,r,n){this.token=e,this.name=e.name,this.handler=K(r)?r:K(r?.handler)?r.handler:Cs,this.raw=!K(r)&&!!r?.raw,this.args=e.args,this.liquid=n;}*render(e,r){let n=[];for(let i of this.args)Fs(i)?n.push([i[0],yield B(i[1],r)]):n.push(yield B(i,r));return yield this.handler.apply({context:r,token:this.token,liquid:this.liquid},[e,...n])}},re=class{constructor(e,r){this.filters=[];let n=typeof e=="string"?new me(e,r.options.operators).readFilteredValue():e;this.initial=n.initial,this.filters=n.filters.map(i=>new Wt(i,this.getFilter(r,i.name),r));}*value(e,r){r=r||e.opts.lenientIf&&this.filters.length>0&&this.filters[0].name==="default";let n=yield this.initial.evaluate(e,r);for(let i of this.filters)n=yield i.render(n,e);return n}getFilter(e,r){let n=e.filters[r];return X(n||!e.options.strictFilters,()=>`undefined filter: ${r}`),n}},en=class extends bt{constructor(e,r){var n;super(e);let i=new me(e.input,r.options.operators,e.file,e.contentRange);this.value=new re(i.readFilteredValue(),r);let s=this.value.filters,o=r.options.outputEscape;if(!(!((n=s[s.length-1])===null||n===void 0)&&n.raw)&&o){let a=new Bt(toString.call(o),[],"",0,0);s.push(new Wt(a,o,r));}}*render(e,r){let n=yield this.value.value(e,false);r.write(n);}*arguments(){yield this.value;}},tn=class extends bt{constructor(e){super(e),this.str=e.getContent();}*render(e,r){r.write(this.str);}},Ve=class t{constructor(e,r){this.segments=e,this.location=r;}toString(){return Jt(this.segments,true)}toArray(){function*e(...r){for(let n of r)n instanceof t?yield Array.from(e(...n.segments)):yield n;}return Array.from(e(...this.segments))}},mt=class{constructor(){this.map=new Map;}get(e){let r=Jt([e.segments[0]]);return this.map.has(r)||this.map.set(r,[]),this.map.get(r)}has(e){return this.map.has(Jt([e.segments[0]]))}push(e){this.get(e).push(e);}asObject(){return Object.fromEntries(this.map)}},$s={partials:true};function*Ns(t,e,r){let n=new mt,i=new mt,s=new mt,o=new Kt(new Set),a=new Set;function l(u,y){n.push(u);let d=y.alias(u);if(d!==void 0){let b=d.segments[0];S(b)&&!o.has(b)&&i.push(d);}else {let b=u.segments[0];S(b)&&!y.has(b)&&i.push(u);}for(let b of u.segments)b instanceof Ve&&l(b,y);}function*c(u,y){if(u.arguments)for(let d of u.arguments())for(let b of ms(d))l(b,y);if(u.localScope)for(let d of u.localScope()){y.add(d.content),y.deleteAlias(d.content);let[b,v]=d.getPosition();s.push(new Ve([d.content],{row:b,col:v,file:d.file}));}if(u.children)if(u.partialScope){let d=u.partialScope();if(d===void 0){for(let E of yield u.children(e,r))yield c(E,y);return}if(a.has(d.name))return;let b=new Set,v=d.isolated?new Kt(b):y.push(b);for(let E of d.scope)if(S(E))b.add(E);else {let[ne,G]=E;b.add(ne);let D=Array.from(ms(G));D.length&&v.setAlias(ne,D[0].segments);}for(let E of yield u.children(e,r))yield c(E,v),a.add(d.name);v.pop();}else {u.blockScope&&y.push(new Set(u.blockScope()));for(let d of yield u.children(e,r))yield c(d,y);u.blockScope&&y.pop();}}for(let u of t)yield c(u,o);return {variables:n.asObject(),globals:i.asObject(),locals:s.asObject()}}function Oe(t,e={}){let r=Object.assign(Object.assign({},$s),e);return Te(Ns(t,r.partials,false))}function Pe(t,e={}){let r=Object.assign(Object.assign({},$s),e);return Re(Ns(t,r.partials,true))}var Kt=class{constructor(e){this.stack=[{names:e,aliases:new Map}];}has(e){for(let r of this.stack)if(r.names.has(e))return true;return false}push(e){return this.stack.push({names:e,aliases:new Map}),this}pop(){var e;return (e=this.stack.pop())===null||e===void 0?void 0:e.names}add(e){this.stack[0].names.add(e);}alias(e){let r=e.segments[0];if(!S(r))return;let n=this.getAlias(r);if(n!==void 0)return new Ve([...n,...e.segments.slice(1)],e.location)}setAlias(e,r){this.stack[this.stack.length-1].aliases.set(e,r);}deleteAlias(e){this.stack[this.stack.length-1].aliases.delete(e);}getAlias(e){for(let r of this.stack){if(r.aliases.has(e))return r.aliases.get(e);if(r.names.has(e))return}}};function*ms(t){ce(t)?yield*Xe(t):t instanceof re&&(yield*nu(t));}function*nu(t){for(let e of t.initial.postfix)ce(e)&&(yield*Xe(e));for(let e of t.filters)for(let r of e.args)Fs(r)&&r[1]?yield*Xe(r[1]):ce(r)&&(yield*Xe(r));}function*Xe(t){Hs(t)?(yield*Xe(t.lhs),yield*Xe(t.rhs)):Qt(t)&&(yield rn(t));}function rn(t){let e=[],r=t.file,n=t.props[0];r=r||n.file,sn(n)||ds(n)||gs(n)?e.push(n.content):Qt(n)&&e.push(...rn(n).segments);for(let o of t.props.slice(1))r=r||o.file,sn(o)||ds(o)||gs(o)?e.push(o.content):Qt(o)&&e.push(rn(o));let[i,s]=t.getPosition();return new Ve(e,{row:i,col:s,file:r})}var hs=/^[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*$/;function Jt(t,e=false){let r=[],n=t[0];S(n)&&(!e||n.match(hs)?r.push(`${n}`):r.push(`['${n}']`));for(let i of t.slice(1))i instanceof Ve?r.push(`[${Jt(i.segments)}]`):S(i)?i.match(hs)?r.push(`.${i}`):r.push(`['${i}']`):r.push(`[${i}]`);return r.join("")}var ze;(function(t){t.Partials="partials",t.Layouts="layouts",t.Root="root";})(ze||(ze={}));var nn=class{constructor(e){if(this.options=e,e.relativeReference){let r=e.fs.sep;X(r,"`fs.sep` is required for relative reference");let n=new RegExp(["."+r,".."+r,"./","../"].map(i=>Bl(i)).join("|"));this.shouldLoadRelative=i=>n.test(i);}else this.shouldLoadRelative=r=>false;this.contains=this.options.fs.contains||(()=>true);}*lookup(e,r,n,i){let{fs:s}=this.options,o=this.options[r];for(let a of this.candidates(e,o,i,r!==ze.Root))if(n?s.existsSync(a):yield s.exists(a))return a;throw this.lookupError(e,o)}*candidates(e,r,n,i){let{fs:s,extname:o}=this.options;if(this.shouldLoadRelative(e)&&n){let a=s.resolve(this.dirname(n),e,o);for(let l of r)if(!i||this.contains(l,a)){yield a;break}}for(let a of r){let l=s.resolve(a,e,o);(!i||this.contains(a,l))&&(yield l);}if(s.fallback!==void 0){let a=s.fallback(e);a!==void 0&&(yield a);}}dirname(e){let r=this.options.fs;return X(r.dirname,"`fs.dirname` is required for relative reference"),r.dirname(e)}lookupError(e,r){let n=new Error("ENOENT");return n.message=`ENOENT: Failed to lookup "${e}" in "${r}"`,n.code="ENOENT",n}},Se=class{constructor(e){this.liquid=e,this.cache=this.liquid.options.cache,this.fs=this.liquid.options.fs,this.parseFile=this.cache?this._parseFileCached:this._parseFile,this.loader=new nn(this.liquid.options),this.parseLimit=new yt("parse length",e.options.parseLimit);}parse(e,r){e=String(e),this.parseLimit.use(e.length);let i=new me(e,this.liquid.options.operators,r).readTopLevelTokens(this.liquid.options);return this.parseTokens(i)}parseTokens(e){let r,n=[],i=[];for(;r=e.shift();)try{n.push(this.parseToken(r,e));}catch(s){if(this.liquid.options.catchAllErrors)i.push(s);else throw s}if(i.length)throw new $t(i);return n}parseToken(e,r){try{if(qe(e)){let n=this.liquid.tags[e.name];return X(n,`tag "${e.name}" not found`),new n(e,r,this.liquid,this)}return su(e)?new en(e,this.liquid):new tn(e)}catch(n){throw _e.is(n)?n:new Lr(n,e)}}parseStream(e){return new Xr(e,(r,n)=>this.parseToken(r,n))}*_parseFileCached(e,r,n=ze.Root,i){let s=this.cache,o=this.loader.shouldLoadRelative(e)?i+","+e:n+":"+e,a=yield s.read(o);if(a)return a;let l=this._parseFile(e,r,n,i),c=r?yield l:Te(l);s.write(o,c);try{return yield c}catch(u){throw s.remove(o),u}}*_parseFile(e,r,n=ze.Root,i){let s=yield this.loader.lookup(e,n,r,i);return this.parse(r?this.fs.readFileSync(s):yield this.fs.readFile(s),s)}},P;(function(t){t[t.Number=1]="Number",t[t.Literal=2]="Literal",t[t.Tag=4]="Tag",t[t.Output=8]="Output",t[t.HTML=16]="HTML",t[t.Filter=32]="Filter",t[t.Hash=64]="Hash",t[t.PropertyAccess=128]="PropertyAccess",t[t.Word=256]="Word",t[t.Range=512]="Range",t[t.Quoted=1024]="Quoted",t[t.Operator=2048]="Operator",t[t.FilteredValue=4096]="FilteredValue",t[t.Delimited=12]="Delimited";})(P||(P={}));function iu(t){return !!(de(t)&P.Delimited)}function Ms(t){return de(t)===P.Operator}function Dn(t){return de(t)===P.HTML}function su(t){return de(t)===P.Output}function qe(t){return de(t)===P.Tag}function sn(t){return de(t)===P.Quoted}function ds(t){return de(t)===P.Number}function Qt(t){return de(t)===P.PropertyAccess}function gs(t){return de(t)===P.Word}function Hs(t){return de(t)===P.Range}function ce(t){return (de(t)&1667)>0}function de(t){return t?t.kind:-1}var Ne=class t{constructor(e={},r=Ze,n={},{memoryLimit:i,renderLimit:s}={}){var o,a,l,c,u;this.scopes=[{}],this.registers={},this.breakCalled=false,this.continueCalled=false,this.sync=!!n.sync,this.opts=r,this.globals=(o=n.globals)!==null&&o!==void 0?o:r.globals,this.environments=Xt(e)?e:Object(e),this.strictVariables=(a=n.strictVariables)!==null&&a!==void 0?a:this.opts.strictVariables,this.ownPropertyOnly=(l=n.ownPropertyOnly)!==null&&l!==void 0?l:r.ownPropertyOnly,this.memoryLimit=i??new yt("memory alloc",(c=n.memoryLimit)!==null&&c!==void 0?c:r.memoryLimit),this.renderLimit=s??new yt("template render",Is().now()+((u=n.renderLimit)!==null&&u!==void 0?u:r.renderLimit));}getRegister(e){return this.registers[e]=this.registers[e]||{}}setRegister(e,r){return this.registers[e]=r}saveRegister(...e){return e.map(r=>[r,this.getRegister(r)])}restoreRegister(e){return e.forEach(([r,n])=>this.setRegister(r,n))}getAll(){return [this.globals,this.environments,...this.scopes].reduce((e,r)=>Vt(e,r),{})}get(e){return this.getSync(e)}getSync(e){return Re(this._get(e))}*_get(e){let r=this.findScope(e[0]);return yield this._getFromScope(r,e)}getFromScope(e,r){return Re(this._getFromScope(e,r))}*_getFromScope(e,r,n=this.strictVariables){S(r)&&(r=r.split("."));for(let i=0;i<r.length;i++)if(e=yield this.readProperty(e,r[i]),n&&Gl(e))throw new Ir(r.slice(0,i+1).join("."));return e}push(e){return this.scopes.push(e)}pop(){return this.scopes.pop()}bottom(){return this.scopes[0]}spawn(e={}){return new t(e,this.opts,{sync:this.sync,globals:this.globals,strictVariables:this.strictVariables},{renderLimit:this.renderLimit,memoryLimit:this.memoryLimit})}findScope(e){for(let r=this.scopes.length-1;r>=0;r--){let n=this.scopes[r];if(e in n)return n}return e in this.environments?this.environments:this.globals}readProperty(e,r){if(e=Ss(e),r=_(r),fe(e))return e;if(Y(e)&&r<0)return e[e.length+ +r];let n=ou(e,r,this.ownPropertyOnly);return n===void 0&&e instanceof Ce?e.liquidMethodMissing(r,this):K(n)?n.call(e):r==="size"?cu(e):r==="first"?au(e):r==="last"?lu(e):n}};function ou(t,e,r){if(!(r&&!_n.call(t,e)&&!(t instanceof Ce)))return t[e]}function au(t){return Y(t)?t[0]:t.first}function lu(t){return Y(t)?t[t.length-1]:t.last}function cu(t){if(_n.call(t,"size")||t.size!==void 0)return t.size;if(Y(t)||S(t))return t.length;if(typeof t=="object")return Object.keys(t).length}var He;(function(t){t[t.OUTPUT=0]="OUTPUT",t[t.STORE=1]="STORE";})(He||(He={}));var uu=be(Math.abs),pu=be(Math.max),fu=be(Math.min),mu=be(Math.ceil),hu=be((t,e,r=false)=>r?Math.floor(t/e):t/e),du=be(Math.floor),gu=be((t,e)=>t-e),yu=be((t,e)=>t+e),wu=be((t,e)=>t%e),bu=be((t,e)=>t*e);function vu(t,e=0){t=Er(t),e=Er(e);let r=Math.pow(10,e);return Math.round(t*r)/r}var xu=Object.freeze({__proto__:null,abs:uu,at_least:pu,at_most:fu,ceil:mu,divided_by:hu,floor:du,minus:gu,plus:yu,modulo:wu,times:bu,round:vu}),ku=t=>decodeURIComponent(w(t)).replace(/\+/g," "),Su=t=>encodeURIComponent(w(t)).replace(/%20/g,"+"),Tu=t=>encodeURIComponent(w(t)).replace(/%20/g,"+").replace(/[!'()*]/g,e=>"%"+e.charCodeAt(0).toString(16).toUpperCase()),Ru=t=>encodeURI(w(t)).replace(/%5B/g,"[").replace(/%5D/g,"]"),ys=/[^\p{M}\p{L}\p{Nd}]+/ug,Au={raw:/\s+/g,default:ys,pretty:/[^\p{M}\p{L}\p{Nd}._~!$&'()+,;=@]+/ug,ascii:/[^A-Za-z0-9]+/g,latin:ys,none:null};function Cu(t,e="default",r=false){t=w(t);let n=Au[e];return n&&(e==="latin"&&(t=_u(t)),t=t.replace(n,"-").replace(/^-|-$/g,"")),r?t:t.toLowerCase()}function _u(t){return t.replace(/[àáâãäå]/g,"a").replace(/[æ]/g,"ae").replace(/[ç]/g,"c").replace(/[èéêë]/g,"e").replace(/[ìíîï]/g,"i").replace(/[ð]/g,"d").replace(/[ñ]/g,"n").replace(/[òóôõöø]/g,"o").replace(/[ùúûü]/g,"u").replace(/[ýÿ]/g,"y").replace(/[ß]/g,"ss").replace(/[œ]/g,"oe").replace(/[þ]/g,"th").replace(/[ẞ]/g,"SS").replace(/[Œ]/g,"OE").replace(/[Þ]/g,"TH")}var Eu=Object.freeze({__proto__:null,url_decode:ku,url_encode:Su,cgi_escape:Tu,uri_escape:Ru,slugify:Cu}),Du=er(function(t,e){let r=Q(t),n=fe(e)?" ":w(e),i=r.length*(1+n.length);return this.context.memoryLimit.use(i),r.join(n)}),Lu=er(t=>Ts(t)?Kl(t):""),Ou=er(t=>Ts(t)?t[0]:""),Pu=er(function(t){let e=Q(t);return this.context.memoryLimit.use(e.length),[...e].reverse()});function*Iu(t,e){let r=[],n=Q(t);this.context.memoryLimit.use(n.length);for(let i of n)r.push([i,e?yield this.context._getFromScope(i,w(e).split("."),false):i]);return r.sort((i,s)=>{let o=i[1],a=s[1];return o<a?-1:o>a?1:0}).map(i=>i[0])}function ju(t,e){let r=w(e),n=e===void 0?ts:(s,o)=>ts(s[r],o[r]),i=Q(t);return this.context.memoryLimit.use(i.length),[...i].sort(n)}var Fu=t=>t&&t.length||0;function*$u(t,e){let r=[],n=Q(t);this.context.memoryLimit.use(n.length);for(let i of n)r.push(yield this.context._getFromScope(i,w(e),false));return r}function*Nu(t,e){let r=0,n=Q(t);for(let i of n){let s=Number(e?yield this.context._getFromScope(i,w(e),false):i);r+=Number.isNaN(s)?0:s;}return r}function Mu(t){let e=Q(t);return this.context.memoryLimit.use(e.length),e.filter(r=>!fe(_(r)))}function Vs(t,e=[]){let r=Q(t),n=Q(e);return this.context.memoryLimit.use(r.length+n.length),r.concat(n)}function Hu(t,e){return Vs.call(this,t,[e])}function Vu(t,e){let r=Q(t);this.context.memoryLimit.use(r.length);let n=[...r];return n.unshift(e),n}function zu(t){let e=[...Q(t)];return e.pop(),e}function qu(t){let e=Q(t);this.context.memoryLimit.use(e.length);let r=[...e];return r.shift(),r}function Uu(t,e,r=1){return t=_(t),fe(t)?[]:(Y(t)||(t=w(t)),e=e<0?t.length+e:e,this.context.memoryLimit.use(r),t.slice(e,e+r))}function zs(t){return this.context.opts.jekyllWhere?e=>dt.is(t)?Ae(e,t):Y(e)?Ac(e,t):Ae(e,t):t===void 0?e=>Me(e,this.context):e=>Ae(e,t)}function*qs(t,e,r,n){let i=[];e=Q(e),this.context.memoryLimit.use(e.length);let s=new me(w(r)).readScopeValue();for(let a of e)i.push(yield B(s,this.context.spawn(a)));let o=zs.call(this,n);return e.filter((a,l)=>o(i[l])===t)}function*Us(t,e,r,n){let i=[],s=new re(w(n),this.liquid),o=Q(e);this.context.memoryLimit.use(o.length);for(let a of o){this.context.push({[r]:a});let l=yield s.value(this.context);this.context.pop(),l===t&&i.push(a);}return i}function*Bu(t,e,r){return yield*qs.call(this,true,t,e,r)}function*Gu(t,e,r){return yield*qs.call(this,false,t,e,r)}function*Wu(t,e,r){return yield*Us.call(this,true,t,e,r)}function*Ku(t,e,r){return yield*Us.call(this,false,t,e,r)}function*Ju(t,e){let r=new Map;t=vt(t);let n=new me(w(e)).readScopeValue();this.context.memoryLimit.use(t.length);for(let i of t){let s=yield B(n,this.context.spawn(i));r.has(s)||r.set(s,[]),r.get(s).push(i);}return [...r.entries()].map(([i,s])=>({name:i,items:s}))}function*Qu(t,e,r){let n=new Map,i=new re(w(r),this.liquid);t=vt(t),this.context.memoryLimit.use(t.length);for(let s of t){this.context.push({[e]:s});let o=yield i.value(this.context);this.context.pop(),n.has(o)||n.set(o,[]),n.get(o).push(s);}return [...n.entries()].map(([s,o])=>({name:s,items:o}))}function*Ln(t,e,r){let n=new me(w(e)).readScopeValue(),i=Q(t),s=zs.call(this,r);for(let o=0;o<i.length;o++){let a=yield B(n,this.context.spawn(i[o]));if(s(a))return [o,i[o]]}}function*On(t,e,r){let n=new re(w(r),this.liquid),i=Q(t);for(let s=0;s<i.length;s++){this.context.push({[e]:i[s]});let o=yield n.value(this.context);if(this.context.pop(),o)return [s,i[s]]}}function*Yu(t,e,r){return !!(yield*Ln.call(this,t,e,r))}function*Zu(t,e,r){return !!(yield*On.call(this,t,e,r))}function*Xu(t,e,r){let n=yield*Ln.call(this,t,e,r);return n?n[0]:void 0}function*ep(t,e,r){let n=yield*On.call(this,t,e,r);return n?n[0]:void 0}function*tp(t,e,r){let n=yield*Ln.call(this,t,e,r);return n?n[1]:void 0}function*rp(t,e,r){let n=yield*On.call(this,t,e,r);return n?n[1]:void 0}function np(t){return t=Q(t),this.context.memoryLimit.use(t.length),[...new Set(t)]}function ip(t,e=1){if(t=_(t),fe(t))return [];Y(t)||(t=w(t)),this.context.memoryLimit.use(e);let r=[...t].sort(()=>Math.random()-.5);return e===1?r[0]:r.slice(0,e)}var sp=Object.freeze({__proto__:null,join:Du,last:Lu,first:Ou,reverse:Pu,sort:Iu,sort_natural:ju,size:Fu,map:$u,sum:Nu,compact:Mu,concat:Vs,push:Hu,unshift:Vu,pop:zu,shift:qu,slice:Uu,where:Bu,reject:Gu,where_exp:Wu,reject_exp:Ku,group_by:Ju,group_by_exp:Qu,has:Yu,has_exp:Zu,find_index:Xu,find_index_exp:ep,find:tp,find_exp:rp,uniq:np,sample:ip});function Pn(t,e,r){var n,i,s;let o=((n=t?.length)!==null&&n!==void 0?n:0)+((i=e?.length)!==null&&i!==void 0?i:0)+((s=r?.length)!==null&&s!==void 0?s:0);this.context.memoryLimit.use(o);let a=Gs(t,this.context.opts,r);return a?(e=_(e),e=fe(e)?this.context.opts.dateFormat:w(e),It(a,e)):t}function op(t){return Pn.call(this,t,"%Y-%m-%dT%H:%M:%S%:z")}function ap(t){return Pn.call(this,t,"%a, %d %b %Y %H:%M:%S %z")}function lp(t,e,r){return Bs.call(this,t,"%b",e,r)}function cp(t,e,r){return Bs.call(this,t,"%B",e,r)}function Bs(t,e,r,n){let i=Gs(t,this.context.opts);if(!i)return t;if(r==="ordinal"){let s=i.getDate();return n==="US"?It(i,`${e} ${s}%q, %Y`):It(i,`${s}%q ${e} %Y`)}return It(i,`%d ${e} %Y`)}function Gs(t,e,r){let n,i=r??e.timezoneOffset,s=e.locale;return t=_(t),t==="now"||t==="today"?n=new Ie(Date.now(),s,i):Zt(t)?n=new Ie(t*1e3,s,i):S(t)?/^\d+$/.test(t)?n=new Ie(+t*1e3,s,i):e.preserveTimezones&&r===void 0?n=Ie.createDateFixedToTimezone(t,s):n=new Ie(t,s,i):n=new Ie(t,s,i),n.valid()?n:void 0}var up=Object.freeze({__proto__:null,date:Pn,date_to_xmlschema:op,date_to_rfc822:ap,date_to_string:lp,date_to_long_string:cp}),Cr=/[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/gu,ws=/[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;function pp(t,e){X(arguments.length===2,"append expect 2 arguments");let r=w(t),n=w(e);return this.context.memoryLimit.use(r.length+n.length),r+n}function fp(t,e){X(arguments.length===2,"prepend expect 2 arguments");let r=w(t),n=w(e);return this.context.memoryLimit.use(r.length+n.length),n+r}function mp(t,e){let r=w(t);return this.context.memoryLimit.use(r.length),e?(e=En(w(e)),r.replace(new RegExp(`^[${e}]+`,"g"),"")):r.replace(/^\s+/,"")}function hp(t){let e=w(t);return this.context.memoryLimit.use(e.length),e.toLowerCase()}function dp(t){let e=w(t);return this.context.memoryLimit.use(e.length),w(e).toUpperCase()}function gp(t,e){let r=w(t);return this.context.memoryLimit.use(r.length),r.split(w(e)).join("")}function yp(t,e){let r=w(t);return this.context.memoryLimit.use(r.length),r.replace(w(e),"")}function wp(t,e){let r=w(t);this.context.memoryLimit.use(r.length);let n=w(e),i=r.lastIndexOf(n);return i===-1?r:r.substring(0,i)+r.substring(i+n.length)}function bp(t,e){return t=w(t),this.context.memoryLimit.use(t.length),e?(e=En(w(e)),t.replace(new RegExp(`[${e}]+$`,"g"),"")):t.replace(/\s+$/,"")}function vp(t,e){let r=w(t);this.context.memoryLimit.use(r.length);let n=r.split(w(e));for(;n.length&&n[n.length-1]==="";)n.pop();return n}function xp(t,e){let r=w(t);return this.context.memoryLimit.use(r.length),e?(e=En(w(e)),r.replace(new RegExp(`^[${e}]+`,"g"),"").replace(new RegExp(`[${e}]+$`,"g"),"")):r.trim()}function kp(t){let e=w(t);return this.context.memoryLimit.use(e.length),e.replace(/\r?\n/gm,"")}function Sp(t){return t=w(t),this.context.memoryLimit.use(t.length),t.charAt(0).toUpperCase()+t.slice(1).toLowerCase()}function Tp(t,e,r){let n=w(t);return this.context.memoryLimit.use(n.length),n.split(w(e)).join(r)}function Rp(t,e,r){let n=w(t);return this.context.memoryLimit.use(n.length),n.replace(w(e),r)}function Ap(t,e,r){let n=w(t);this.context.memoryLimit.use(n.length);let i=w(e),s=n.lastIndexOf(i);if(s===-1)return n;let o=w(r);return n.substring(0,s)+o+n.substring(s+i.length)}function Cp(t,e=50,r="..."){let n=w(t);return this.context.memoryLimit.use(n.length),n.length<=e?t:n.substring(0,e-r.length)+r}function _p(t,e=15,r="..."){let n=w(t);this.context.memoryLimit.use(n.length);let i=n.split(/\s+/);e<=0&&(e=1);let s=i.slice(0,e).join(" ");return i.length>=e&&(s+=r),s}function Ep(t){let e=w(t);return this.context.memoryLimit.use(e.length),e.replace(/\s+/g," ")}function Dp(t,e){let r=w(t);if(this.context.memoryLimit.use(r.length),t=r.trim(),!t)return 0;switch(e){case "cjk":return (t.match(Cr)||[]).length+(t.match(ws)||[]).length;case "auto":return Cr.test(t)?t.match(Cr).length+(t.match(ws)||[]).length:t.split(/\s+/).length;default:return t.split(/\s+/).length}}function Lp(t,e="and"){switch(this.context.memoryLimit.use(t.length),t.length){case 0:return "";case 1:return t[0];case 2:return `${t[0]} ${e} ${t[1]}`;default:return `${t.slice(0,-1).join(", ")}, ${e} ${t[t.length-1]}`}}var Op=Object.freeze({__proto__:null,append:pp,prepend:fp,lstrip:mp,downcase:hp,upcase:dp,remove:gp,remove_first:yp,remove_last:wp,rstrip:bp,split:vp,strip:xp,strip_newlines:kp,capitalize:Sp,replace:Tp,replace_first:Rp,replace_last:Ap,truncate:Cp,truncatewords:_p,normalize_whitespace:Ep,number_of_words:Dp,array_to_sentence_string:Lp});function Pp(t){return Buffer.from(t,"utf8").toString("base64")}function Ip(t){return Buffer.from(t,"base64").toString("utf8")}function jp(t){let e=w(t);return this.context.memoryLimit.use(e.length),Pp(e)}function Fp(t){let e=w(t);return this.context.memoryLimit.use(e.length),Ip(e)}var $p=Object.freeze({__proto__:null,base64_encode:jp,base64_decode:Fp}),Np=Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},Qc),xu),Eu),sp),up),Op),$p),js),on=class extends j{constructor(e,r,n){super(e,r,n),this.identifier=this.tokenizer.readIdentifier(),this.key=this.identifier.content,this.tokenizer.assert(this.key,"expected variable name"),this.tokenizer.skipBlank(),this.tokenizer.assert(this.tokenizer.peek()==="=",'expected "="'),this.tokenizer.advance(),this.value=new re(this.tokenizer.readFilteredValue(),this.liquid);}*render(e){e.bottom()[this.key]=yield this.value.value(e,this.liquid.options.lenientIf);}*arguments(){yield this.value;}*localScope(){yield this.identifier;}},bs=["offset","limit","reversed"],an=class extends j{constructor(e,r,n,i){super(e,r,n);let s=this.tokenizer.readIdentifier(),o=this.tokenizer.readIdentifier(),a=this.tokenizer.readValue();if(!s.size()||o.content!=="in"||!a)throw new Error(`illegal tag: ${e.getText()}`);this.variable=s.content,this.collection=a,this.hash=new je(this.tokenizer,n.options.keyValueSeparator),this.templates=[],this.elseTemplates=[];let l,c=i.parseStream(r).on("start",()=>l=this.templates).on("tag:else",u=>{Nt(u.args),l=this.elseTemplates;}).on("tag:endfor",u=>{Nt(u.args),c.stop();}).on("template",u=>l.push(u)).on("end",()=>{throw new Error(`tag ${e.getText()} not closed`)});c.start();}*render(e,r){let n=this.liquid.renderer,i=vt(yield B(this.collection,e));if(!i.length){yield n.renderTemplates(this.elseTemplates,e,r);return}let s="continue-"+this.variable+"-"+this.collection.getText();e.push({continue:e.getRegister(s)});let o=yield this.hash.render(e);e.pop(),i=(this.liquid.options.orderedFilterParameters?Object.keys(o).filter(c=>bs.includes(c)):bs.filter(c=>o[c]!==void 0)).reduce((c,u)=>u==="offset"?Hp(c,o.offset):u==="limit"?Vp(c,o.limit):Mp(c),i),e.setRegister(s,(o.offset||0)+i.length);let l={forloop:new gt(i.length,this.collection.getText(),this.variable)};e.push(l);for(let c of i){if(l[this.variable]=c,e.continueCalled=e.breakCalled=false,yield n.renderTemplates(this.templates,e,r),e.breakCalled)break;l.forloop.next();}e.continueCalled=e.breakCalled=false,e.pop();}*children(){let e=this.templates.slice();return this.elseTemplates&&e.push(...this.elseTemplates),e}*arguments(){yield this.collection;for(let e of Object.values(this.hash.hash))ce(e)&&(yield e);}blockScope(){return [this.variable,"forloop"]}};function Mp(t){return [...t].reverse()}function Hp(t,e){return t.slice(e)}function Vp(t,e){return t.slice(0,e)}var ln=class extends j{constructor(e,r,n,i){for(super(e,r,n),this.templates=[],this.identifier=this.readVariable(),this.variable=this.identifier.content;r.length;){let s=r.shift();if(qe(s)&&s.name==="endcapture")return;this.templates.push(i.parseToken(s,r));}throw new Error(`tag ${e.getText()} not closed`)}readVariable(){let e=this.tokenizer.readIdentifier();if(e.content||(e=this.tokenizer.readQuoted(),e))return e;throw this.tokenizer.error("invalid capture name")}*render(e){let n=yield this.liquid.renderer.renderTemplates(this.templates,e);e.bottom()[this.variable]=n;}*children(){return this.templates}*localScope(){yield this.identifier;}},cn=class extends j{constructor(e,r,n,i){super(e,r,n),this.branches=[],this.elseTemplates=[],this.value=new re(this.tokenizer.readFilteredValue(),this.liquid),this.elseTemplates=[];let s=[],o=0,a=i.parseStream(r).on("tag:when",l=>{if(o>0)return;s=[];let c=[];for(;!l.tokenizer.end();)c.push(l.tokenizer.readValueOrThrow()),l.tokenizer.skipBlank(),l.tokenizer.peek()===","?l.tokenizer.readTo(","):l.tokenizer.readTo("or");this.branches.push({values:c,templates:s});}).on("tag:else",()=>{o++,s=this.elseTemplates;}).on("tag:endcase",()=>a.stop()).on("template",l=>{(s!==this.elseTemplates||o===1)&&s.push(l);}).on("end",()=>{throw new Error(`tag ${e.getText()} not closed`)});a.start();}*render(e,r){let n=this.liquid.renderer,i=_(yield this.value.value(e,e.opts.lenientIf)),s=false;for(let o of this.branches)for(let a of o.values){let l=yield B(a,e,e.opts.lenientIf);if(Ae(i,l)){yield n.renderTemplates(o.templates,e,r),s=true;break}}s||(yield n.renderTemplates(this.elseTemplates,e,r));}*arguments(){yield this.value,yield*this.branches.flatMap(e=>e.values);}*children(){let e=this.branches.flatMap(r=>r.templates);return this.elseTemplates&&e.push(...this.elseTemplates),e}},un=class extends j{constructor(e,r,n){for(super(e,r,n);r.length;){let i=r.shift();if(qe(i)&&i.name==="endcomment")return}throw new Error(`tag ${e.getText()} not closed`)}render(){}},pn=class extends j{constructor(e,r,n,i){super(e,r,n);let s=this.tokenizer;for(this.file=In(s,this.liquid,i),this.currentFile=e.file;!s.end();){s.skipBlank();let o=s.p,a=s.readIdentifier();if((a.content==="with"||a.content==="for")&&(s.skipBlank(),s.peek()!==":")){let l=s.readValue();if(l){let c=s.p,u=s.readIdentifier(),y;u.content==="as"?y=s.readIdentifier():s.p=c,this[a.content]={value:l,alias:y&&y.content},s.skipBlank(),s.peek()===","&&s.advance();continue}}s.p=o;break}this.hash=new je(s,n.options.keyValueSeparator);}*render(e,r){let{liquid:n,hash:i}=this,s=yield jn(this.file,e,n);X(s,()=>`illegal file path "${s}"`);let o=e.spawn(),a=o.bottom();if(Vt(a,yield i.render(e)),this.with){let{value:l,alias:c}=this.with;a[c||s]=yield B(l,e);}if(this.for){let{value:l,alias:c}=this.for,u=vt(yield B(l,e));a.forloop=new gt(u.length,l.getText(),c);for(let y of u){a[c]=y;let d=yield n._parsePartialFile(s,o.sync,this.currentFile);yield n.renderer.renderTemplates(d,o,r),a.forloop.next();}}else {let l=yield n._parsePartialFile(s,o.sync,this.currentFile);yield n.renderer.renderTemplates(l,o,r);}}*children(e,r){return e&&S(this.file)?yield this.liquid._parsePartialFile(this.file,r,this.currentFile):[]}partialScope(){if(S(this.file)){let e=Object.keys(this.hash.hash);if(this.with){let{value:r,alias:n}=this.with;S(n)?e.push([n,r]):S(this.file)&&e.push([this.file,r]);}if(this.for){let{value:r,alias:n}=this.for;S(n)?e.push([n,r]):S(this.file)&&e.push([this.file,r]);}return {name:this.file,isolated:true,scope:e}}}*arguments(){for(let e of Object.values(this.hash.hash))ce(e)&&(yield e);if(this.with){let{value:e}=this.with;ce(e)&&(yield e);}if(this.for){let{value:e}=this.for;ce(e)&&(yield e);}}};function In(t,e,r){if(e.options.dynamicPartials){let s=t.readValue();if(t.assert(s,"illegal file path"),s.getText()==="none")return;if(sn(s)){let o=r.parse(xc(s));return vs(o)}return s}let n=[...t.readFileNameTemplate(e.options)],i=vs(r.parseTokens(n));return i==="none"?void 0:i}function vs(t){return t.length===1&&Dn(t[0].token)?t[0].token.getContent():t}function*jn(t,e,r){return typeof t=="string"?t:Array.isArray(t)?r.renderer.renderTemplates(t,e):yield B(t,e)}var fn=class extends j{constructor(e,r,n,i){super(e,r,n);let{tokenizer:s}=e;this.file=In(s,this.liquid,i),this.currentFile=e.file;let o=s.p;s.readIdentifier().content==="with"?(s.skipBlank(),s.peek()!==":"?this.withVar=s.readValue():s.p=o):s.p=o,this.hash=new je(s,n.options.jekyllInclude||n.options.keyValueSeparator);}*render(e,r){let{liquid:n,hash:i,withVar:s}=this,{renderer:o}=n,a=yield jn(this.file,e,n);X(a,()=>`illegal file path "${a}"`);let l=e.saveRegister("blocks","blockMode");e.setRegister("blocks",{}),e.setRegister("blockMode",He.OUTPUT);let c=yield i.render(e);s&&(c[a]=yield B(s,e));let u=yield n._parsePartialFile(a,e.sync,this.currentFile);e.push(e.opts.jekyllInclude?{include:c}:c),yield o.renderTemplates(u,e,r),e.pop(),e.restoreRegister(l);}*children(e,r){return e&&S(this.file)?yield this.liquid._parsePartialFile(this.file,r,this.currentFile):[]}partialScope(){if(S(this.file)){let e;return this.liquid.options.jekyllInclude?e=["include"]:(e=Object.keys(this.hash.hash),this.withVar&&e.push([this.file,this.withVar])),{name:this.file,isolated:false,scope:e}}}*arguments(){yield*Object.values(this.hash.hash).filter(ce),ce(this.file)&&(yield this.file),ce(this.withVar)&&(yield this.withVar);}},mn=class extends j{constructor(e,r,n){super(e,r,n),this.identifier=this.tokenizer.readIdentifier(),this.variable=this.identifier.content;}render(e,r){let n=e.environments;Zt(n[this.variable])||(n[this.variable]=0),r.write(w(--n[this.variable]));}*localScope(){yield this.identifier;}},hn=class extends j{constructor(e,r,n){super(e,r,n),this.candidates=[];let i=this.tokenizer.readValue();for(this.tokenizer.skipBlank(),i&&(this.tokenizer.peek()===":"?(this.group=i,this.tokenizer.advance()):this.candidates.push(i));!this.tokenizer.end();){let s=this.tokenizer.readValue();s&&this.candidates.push(s),this.tokenizer.readTo(",");}this.tokenizer.assert(this.candidates.length,()=>`empty candidates: "${e.getText()}"`);}*render(e,r){let i=`cycle:${yield B(this.group,e)}:`+this.candidates.join(","),s=e.getRegister("cycle"),o=s[i];o===void 0&&(o=s[i]=0);let a=this.candidates[o];return o=(o+1)%this.candidates.length,s[i]=o,yield B(a,e)}*arguments(){yield*this.candidates,this.group&&(yield this.group);}},dn=class extends j{constructor(e,r,n,i){super(e,r,n),this.branches=[];let s=[];i.parseStream(r).on("start",()=>this.branches.push({value:new re(e.tokenizer.readFilteredValue(),this.liquid),templates:s=[]})).on("tag:elsif",o=>{X(!this.elseTemplates,"unexpected elsif after else"),this.branches.push({value:new re(o.tokenizer.readFilteredValue(),this.liquid),templates:s=[]});}).on("tag:else",o=>{Nt(o.args),X(!this.elseTemplates,"duplicated else"),s=this.elseTemplates=[];}).on("tag:endif",function(o){Nt(o.args),this.stop();}).on("template",o=>s.push(o)).on("end",()=>{throw new Error(`tag ${e.getText()} not closed`)}).start();}*render(e,r){let n=this.liquid.renderer;for(let{value:i,templates:s}of this.branches){let o=yield i.value(e,e.opts.lenientIf);if(Me(o,e)){yield n.renderTemplates(s,e,r);return}}yield n.renderTemplates(this.elseTemplates||[],e,r);}*children(){let e=this.branches.flatMap(r=>r.templates);return this.elseTemplates&&e.push(...this.elseTemplates),e}arguments(){return this.branches.map(e=>e.value)}},gn=class extends j{constructor(e,r,n){super(e,r,n),this.identifier=this.tokenizer.readIdentifier(),this.variable=this.identifier.content;}render(e,r){let n=e.environments;Zt(n[this.variable])||(n[this.variable]=0);let i=n[this.variable];n[this.variable]++,r.write(w(i));}*localScope(){yield this.identifier;}},yn=class extends j{constructor(e,r,n,i){super(e,r,n),this.file=In(this.tokenizer,this.liquid,i),this.currentFile=e.file,this.args=new je(this.tokenizer,n.options.keyValueSeparator),this.templates=i.parseTokens(r);}*render(e,r){let{liquid:n,args:i,file:s}=this,{renderer:o}=n;if(s===void 0){e.setRegister("blockMode",He.OUTPUT),yield o.renderTemplates(this.templates,e,r);return}let a=yield jn(this.file,e,n);X(a,()=>`illegal file path "${a}"`);let l=yield n._parseLayoutFile(a,e.sync,this.currentFile);e.setRegister("blockMode",He.STORE);let c=yield o.renderTemplates(this.templates,e),u=e.getRegister("blocks");u[""]===void 0&&(u[""]=(y,d)=>d.write(c)),e.setRegister("blockMode",He.OUTPUT),e.push(yield i.render(e)),yield o.renderTemplates(l,e,r),e.pop();}*children(e){let r=this.templates.slice();return e&&S(this.file)&&r.push(...yield this.liquid._parsePartialFile(this.file,true,this.currentFile)),r}*arguments(){for(let e of Object.values(this.args.hash))ce(e)&&(yield e);ce(this.file)&&(yield this.file);}partialScope(){if(S(this.file))return {name:this.file,isolated:false,scope:Object.keys(this.args.hash)}}},wn=class extends j{constructor(e,r,n,i){super(e,r,n),this.templates=[];let s=/\w+/.exec(e.args);for(this.block=s?s[0]:"";r.length;){let o=r.shift();if(qe(o)&&o.name==="endblock")return;let a=i.parseToken(o,r);this.templates.push(a);}throw new Error(`tag ${e.getText()} not closed`)}*render(e,r){let n=this.getBlockRender(e);e.getRegister("blockMode")===He.STORE?e.getRegister("blocks")[this.block]=n:yield n(new Ht,r);}getBlockRender(e){let{liquid:r,templates:n}=this,i=e.getRegister("blocks")[this.block],s=function*(o,a){e.push({block:o}),yield r.renderer.renderTemplates(n,e,a),e.pop();};return i?(o,a)=>i(new Ht(l=>s(o,l)),a):s}*children(){return this.templates}blockScope(){return ["block"]}},bn=class extends j{constructor(e,r,n){for(super(e,r,n),this.tokens=[];r.length;){let i=r.shift();if(qe(i)&&i.name==="endraw")return;this.tokens.push(i);}throw new Error(`tag ${e.getText()} not closed`)}render(){return this.tokens.map(e=>e.getText()).join("")}},vn=class extends gt{constructor(e,r,n,i){super(e,n,i),this.length=e,this.cols=r;}row(){return Math.floor(this.i/this.cols)+1}col0(){return this.i%this.cols}col(){return this.col0()+1}col_first(){return this.col0()===0}col_last(){return this.col()===this.cols}},xn=class extends j{constructor(e,r,n,i){super(e,r,n);let s=this.tokenizer.readIdentifier();this.tokenizer.skipBlank();let o=this.tokenizer.readIdentifier(),a=this.tokenizer.readValue();if(o.content!=="in"||!a)throw new Error(`illegal tag: ${e.getText()}`);this.variable=s.content,this.collection=a,this.args=new je(this.tokenizer,n.options.keyValueSeparator),this.templates=[];let l,c=i.parseStream(r).on("start",()=>l=this.templates).on("tag:endtablerow",()=>c.stop()).on("template",u=>l.push(u)).on("end",()=>{throw new Error(`tag ${e.getText()} not closed`)});c.start();}*render(e,r){let n=vt(yield B(this.collection,e)),i=yield this.args.render(e),s=i.offset||0,o=i.limit===void 0?n.length:i.limit;n=n.slice(s,s+o);let a=i.cols||n.length,l=this.liquid.renderer,c=new vn(n.length,a,this.collection.getText(),this.variable),u={tablerowloop:c};e.push(u);for(let y=0;y<n.length;y++,c.next())u[this.variable]=n[y],c.col0()===0&&(c.row()!==1&&r.write("</tr>"),r.write(`<tr class="row${c.row()}">`)),r.write(`<td class="col${c.col()}">`),yield l.renderTemplates(this.templates,e,r),r.write("</td>");n.length&&r.write("</tr>"),e.pop();}*children(){return this.templates}*arguments(){yield this.collection;for(let e of Object.values(this.args.hash))ce(e)&&(yield e);}blockScope(){return [this.variable,"tablerowloop"]}},kn=class extends j{constructor(e,r,n,i){super(e,r,n),this.branches=[],this.elseTemplates=[];let s=[],o=0;i.parseStream(r).on("start",()=>this.branches.push({value:new re(e.tokenizer.readFilteredValue(),this.liquid),test:tr,templates:s=[]})).on("tag:elsif",a=>{if(o>0){s=[];return}this.branches.push({value:new re(a.tokenizer.readFilteredValue(),this.liquid),test:Me,templates:s=[]});}).on("tag:else",()=>{o++,s=this.elseTemplates;}).on("tag:endunless",function(){this.stop();}).on("template",a=>{(s!==this.elseTemplates||o===1)&&s.push(a);}).on("end",()=>{throw new Error(`tag ${e.getText()} not closed`)}).start();}*render(e,r){let n=this.liquid.renderer;for(let{value:i,test:s,templates:o}of this.branches){let a=yield i.value(e,e.opts.lenientIf);if(s(a,e)){yield n.renderTemplates(o,e,r);return}}yield n.renderTemplates(this.elseTemplates,e,r);}*children(){let e=this.branches.flatMap(r=>r.templates);return this.elseTemplates&&e.push(...this.elseTemplates),e}arguments(){return this.branches.map(e=>e.value)}},Sn=class extends j{render(e,r){e.breakCalled=true;}},Tn=class extends j{render(e,r){e.continueCalled=true;}},Rn=class extends j{constructor(e,r,n){super(e,r,n),this.tokenizer.skipBlank(),this.tokenizer.end()||(this.value=new re(this.tokenizer.readFilteredValue(),this.liquid));}*render(e,r){if(!this.value)return;let n=yield this.value.value(e,false);r.write(n);}*arguments(){this.value&&(yield this.value);}},An=class extends j{constructor(e,r,n,i){super(e,r,n);let s=this.tokenizer.readLiquidTagTokens(this.liquid.options);this.templates=i.parseTokens(s);}*render(e,r){yield this.liquid.renderer.renderTemplates(this.templates,e,r);}*children(){return this.templates}},Cn=class extends j{constructor(e,r,n){if(super(e,r,n),e.args.search(/\n\s*[^#\s]/g)!==-1)throw new Error("every line of an inline comment must start with a '#' character")}render(){}},zp={assign:on,for:an,capture:ln,case:cn,comment:un,include:fn,render:pn,decrement:mn,increment:gn,cycle:hn,if:dn,layout:yn,block:wn,raw:bn,tablerow:xn,unless:kn,break:Sn,continue:Tn,echo:Rn,liquid:An,"#":Cn},Yt=class t{constructor(e={}){this.renderer=new Qr,this.filters={},this.tags={},this.options=Yc(e),this.parser=new Se(this),es(zp,(r,n)=>this.registerTag(n,r)),es(Np,(r,n)=>this.registerFilter(n,r));}parse(e,r){return new Se(this).parse(e,r)}_render(e,r,n){let i=r instanceof Ne?r:new Ne(r,this.options,n);return this.renderer.renderTemplates(e,i)}render(e,r,n){return J(this,void 0,void 0,function*(){return Te(this._render(e,r,Object.assign(Object.assign({},n),{sync:false})))})}renderSync(e,r,n){return Re(this._render(e,r,Object.assign(Object.assign({},n),{sync:true})))}renderToNodeStream(e,r,n={}){let i=new Ne(r,this.options,n);return this.renderer.renderTemplatesToNodeStream(e,i)}_parseAndRender(e,r,n){let i=this.parse(e);return this._render(i,r,n)}parseAndRender(e,r,n){return J(this,void 0,void 0,function*(){return Te(this._parseAndRender(e,r,Object.assign(Object.assign({},n),{sync:false})))})}parseAndRenderSync(e,r,n){return Re(this._parseAndRender(e,r,Object.assign(Object.assign({},n),{sync:true})))}_parsePartialFile(e,r,n){return new Se(this).parseFile(e,r,ze.Partials,n)}_parseLayoutFile(e,r,n){return new Se(this).parseFile(e,r,ze.Layouts,n)}_parseFile(e,r,n,i){return new Se(this).parseFile(e,r,n,i)}parseFile(e,r){return J(this,void 0,void 0,function*(){return Te(new Se(this).parseFile(e,false,r))})}parseFileSync(e,r){return Re(new Se(this).parseFile(e,true,r))}*_renderFile(e,r,n){let i=yield this._parseFile(e,n.sync,n.lookupType);return yield this._render(i,r,n)}renderFile(e,r,n){return J(this,void 0,void 0,function*(){return Te(this._renderFile(e,r,Object.assign(Object.assign({},n),{sync:false})))})}renderFileSync(e,r,n){return Re(this._renderFile(e,r,Object.assign(Object.assign({},n),{sync:true})))}renderFileToNodeStream(e,r,n){return J(this,void 0,void 0,function*(){let i=yield this.parseFile(e);return this.renderToNodeStream(i,r,n)})}_evalValue(e,r){let n=new re(e,this),i=r instanceof Ne?r:new Ne(r,this.options);return n.value(i)}evalValue(e,r){return J(this,void 0,void 0,function*(){return Te(this._evalValue(e,r))})}evalValueSync(e,r){return Re(this._evalValue(e,r))}registerFilter(e,r){this.filters[e]=r;}registerTag(e,r){this.tags[e]=K(r)?r:ru(r);}plugin(e){return e.call(this,t)}express(){let e=this,r=true;return function(n,i,s){if(r){r=false;let o=jt(this.root);e.options.root.unshift(...o),e.options.layouts.unshift(...o),e.options.partials.unshift(...o);}e.renderFile(n,i).then(o=>s(null,o),s);}}analyze(e,r={}){return J(this,void 0,void 0,function*(){return Oe(e,r)})}analyzeSync(e,r={}){return Pe(e,r)}parseAndAnalyze(e,r,n={}){return J(this,void 0,void 0,function*(){return Oe(this.parse(e,r),n)})}parseAndAnalyzeSync(e,r,n={}){return Pe(this.parse(e,r),n)}variables(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Object.keys(n.variables)})}variablesSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Object.keys(n.variables)}fullVariables(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Array.from(new Set(Object.values(n.variables).flatMap(i=>i.map(s=>String(s)))))})}fullVariablesSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Array.from(new Set(Object.values(n.variables).flatMap(i=>i.map(s=>String(s)))))}variableSegments(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Array.from(Ot(Object.values(n.variables).flatMap(i=>i.map(s=>s.toArray()))))})}variableSegmentsSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Array.from(Ot(Object.values(n.variables).flatMap(i=>i.map(s=>s.toArray()))))}globalVariables(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Object.keys(n.globals)})}globalVariablesSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Object.keys(n.globals)}globalFullVariables(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Array.from(new Set(Object.values(n.globals).flatMap(i=>i.map(s=>String(s)))))})}globalFullVariablesSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Array.from(new Set(Object.values(n.globals).flatMap(i=>i.map(s=>String(s)))))}globalVariableSegments(e,r={}){return J(this,void 0,void 0,function*(){let n=yield Oe(S(e)?this.parse(e):e,r);return Array.from(Ot(Object.values(n.globals).flatMap(i=>i.map(s=>s.toArray()))))})}globalVariableSegmentsSync(e,r={}){let n=Pe(S(e)?this.parse(e):e,r);return Array.from(Ot(Object.values(n.globals).flatMap(i=>i.map(s=>s.toArray()))))}};function Ws(t){t.registerFilter("kebab",e=>String(e).replace(/\s+/g,"-").toLowerCase()),t.registerFilter("camel",e=>String(e).replace(/-./g,r=>r[1].toUpperCase())),t.registerFilter("json",e=>JSON.stringify(e,null,2));}var Ks=new Yt({extname:".liquid",strictVariables:true,strictFilters:true,trimTagRight:true,trimTagLeft:true});Ws(Ks);async function Js(t,e){return Ks.parseAndRender(t,e)}function Qs(t,e){return t.replace(/%%\s*([\w.-]+)\s*%%/g,(r,n)=>{let i=e[n];return i==null?"":String(i)})}async function Zs(t){let{file:e,engine:r,variables:n}=t;if(e.isBinary)return {dest:e.dest,content:Ye__default.readFileSync(e.src)};let i=Ye__default.readFileSync(e.src,"utf8");if(i=Qs(i,n),!e.isTemplate)return {dest:e.dest,content:i};if(r==="none")throw new Error(`Template file encountered but engine is "none": ${e.dest}`);return Zi(i,e.dest),{dest:e.dest,content:await Js(i,n)}}async function Xs(t){let{template:e,variables:r,outputDir:n,logger:i}=t,s=[];await ut(e.hooks.beforeRender,{template:e,variables:r,logger:i});for(let o of e.vfs.list())s.push(await Zs({file:o,engine:e.config.engine,variables:r}));await ut(e.hooks.afterRender,{template:e,variables:r,logger:i}),await ut(e.hooks.beforeWrite,{template:e,variables:r,logger:i}),await Yi(s,n),await ut(e.hooks.afterWrite,{template:e,variables:r,logger:i});}var qp=["beforeRender","afterRender","beforeWrite","afterWrite"];async function ro(t,e){let r=Ee__default.resolve(t,e.paths?.hooks??"hooks");if(!Ye__default.existsSync(r))return {};let n={};for(let i of qp){let s=Ee__default.join(r,`${i}.ts`);if(!Ye__default.existsSync(s))continue;let o=await he(s),a=o?.default??o;if(typeof a!="function")throw new Error(`Hook "${i}" must export a function (${s})`);n[i]=a;}return n}async function no(t,e){let r=Ee__default.resolve(t,e.paths?.variables??"variables");if(!Ye__default.existsSync(r))return {defaults:{},schema:{}};let n=Ee__default.join(r,"defaults.ts"),i=Ee__default.join(r,"schema.ts"),s=Ee__default.join(r,"transform.ts"),o={},a={},l;if(Ye__default.existsSync(n)){let c=await he(n);if(o=c?.defaults??c?.default??c,!o||typeof o!="object")throw new Error("variables/defaults.ts must export an object")}if(Ye__default.existsSync(i)){let c=await he(i);if(a=c?.schema??c?.default??c,!a||typeof a!="object")throw new Error("variables/schema.ts must export an object")}if(Ye__default.existsSync(s)){let c=await he(s);if(l=c?.transform??c?.default??c,typeof l!="function")throw new Error("variables/transform.ts must export a function")}return {defaults:o,schema:a,transform:l}}var oo=Yn(Ar());function io(t){let e=t.endsWith("__tmpl"),r=e?t.slice(0,-6):t;return console.log("DEST---",r),{dest:r.split(Ee__default.sep).join("/"),isTemplate:e}}var sr=class{files=new Map;add(e){this.files.set(e.dest,e);}list(){return this.files.values()}get(e){return this.files.get(e)}freeze(){return Object.freeze(this.files),this}};var Bp=["**/.git/**","**/node_modules/**",".DS_Store","variables/**","hooks/**","docs/**"],so={_gitignore:".gitignore",_npmrc:".npmrc"};async function ao(t){let e=new sr,r=await _t(t),n=(0, oo.default)(Bp,{dot:true});for(let i of r){if(n(i))continue;let s=Ee__default.join(t,i),{dest:o,isTemplate:a}=io(i),l=Ee__default.posix.dirname(o),c=Ee__default.posix.basename(o);so[c]&&(c=so[c]);let u=l==="."?c:Ee__default.posix.join(l,c);e.add({src:s,rel:i,dest:u,isTemplate:a,isBinary:Ti(s)});}return e}var Gp=["appinit.template.json","appinit.template.ts","template.meta.json"];async function lo(t){let e=Ee__default.join(t,"template.config.ts");if(!Ye__default.existsSync(t))throw new Error(`Template root not found: ${t}`);for(let l of Gp)if(Ye__default.existsSync(Ee__default.join(t,l)))throw new Error(`Deprecated template format detected: ${l} is not supported in ATS 1.0`);if(!Ye__default.existsSync(e))throw new Error("Missing required file: template.config.ts");let r=await he(e),n=r?.config??r?.default??r;if(!n||typeof n!="object")throw new Error("Invalid export in template.config.ts");if(n.appinitSpec!=="1.0")throw new Error(`Template targets ATS ${n.appinitSpec}, but resolver only supports ATS 1.0`);let i=Ee__default.resolve(t,n.paths?.template??"template");if(!Ye__default.existsSync(i))throw new Error(`Missing template directory: ${i}`);let s=await ao(i),o=await no(t,n),a=await ro(t,n);return {config:n,variables:o,hooks:a,vfs:s,meta:{source:t,engine:n.engine,ats:"1.0"}}}var Jp=[["pnpm","pnpm-lock.yaml"],["yarn","yarn.lock"],["bun","bun.lockb"],["npm","package-lock.json"]];function tt(t,e=[],r={}){let n=process.platform==="win32";return new Promise((i,s)=>{let o=spawn(t,e,{cwd:r.cwd,env:{...process.env,...r.env},stdio:"inherit",shell:n});o.on("error",s),o.on("exit",a=>{if(a===0)return i(a);s(new Error(`${t} ${e.join(" ")} failed with exit ${a}`));});})}async function lr(t){let e=Ee__default.resolve(t);for(let[r,n]of Jp)try{return await ho__default.access(Ee__default.join(e,n)),r}catch{}return "npm"}var Qp={npm:"npm",yarn:"yarn",pnpm:"pnpm",bun:"bun"};async function co(t="npm",e){let r=t.trim().toLowerCase(),n=Qp[r];if(!n)throw new Error(`\u274C Unknown package manager: "${t}"`);let i=Ee__default.resolve(e),s={npm:{installDeps:["install"],installDevDeps:["install","-D"],remove:["uninstall"]},yarn:{installDeps:["add"],installDevDeps:["add","-D"],remove:["remove"]},pnpm:{installDeps:["add"],installDevDeps:["add","-D"],remove:["remove"]},bun:{installDeps:["add"],installDevDeps:["add","-d"],remove:["remove"]}};return {name:t,async install(o={}){await tt(n,["install"],{cwd:o.cwd??i});},async installDeps(o,a={}){if(!o.length)return;let l=s[r].installDeps;await tt(n,[...l,...o],{cwd:a.cwd??i});},async installDevDeps(o,a={}){if(!o.length)return;let l=s[r].installDevDeps;await tt(n,[...l,...o],{cwd:a.cwd??i});},async remove(o,a={}){if(!o.length)return;let l=s[r].remove;await tt(n,[...l,...o],{cwd:a.cwd??i});},async run(o,a={}){if(!o)return;await tt(n,r==="yarn"?[o]:["run",o],{cwd:a.cwd??i});},async runRaw(o,a={}){o.length&&await tt(n,o,{cwd:a.cwd??i});}}}async function uo(t){let{targetDir:e,packageManager:r,postInstallCommands:n=[]}=t;if(!e)throw new Error("runPostInstall: targetDir is required");let i=r??await lr(e),s=await co(i,e);await s.install({cwd:e});for(let o of n)await s.runRaw(o.split(" "),{cwd:e});}function Yp(t,e){let r={...t};for(let n of Object.keys(e)){let i=e[n];(r[n]===void 0||r[n]===null||r[n]==="")&&i.default!==void 0&&(r[n]=i.default);let s=r[n];if(i.required&&(s==null||s===""))throw new Error(`Template variable "${n}" is required.`);if(s!=null){if(i.type==="string"&&typeof s!="string")throw new Error(`Variable "${n}" must be a string.`);if(i.enum&&!i.enum.includes(s))throw new Error(`Variable "${n}" must be one of: ${i.enum.join(", ")}`)}}return r}function Zp(t){try{let e=$a("zod"),r={};for(let n of Object.keys(t)){let i=t[n],s=e.any();i.type==="string"&&(s=e.string()),i.type==="number"&&(s=e.number()),i.type==="boolean"&&(s=e.boolean()),i.enum&&(s=e.enum(i.enum)),i.required||(s=s.optional()),r[n]=s;}return e.object(r)}catch{return null}}async function po(t){let{template:e,answers:r}=t,{defaults:n,schema:i,transform:s}=e.variables,o={...n??{},...r??{}},a=Zp(i??{});if(a){let l=a.safeParse(o);if(!l.success){let c=typeof Nn=="function"?Nn(l.error):JSON.stringify(l.error,null,2);throw new Error(`Template variable validation failed:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Continuing will overwrite or modify existing data.`,"Conflict Detected");let e=await select({message:"How would you like to proceed?",options:[{value:"cancel",label:"Abort installation",hint:"(Safe) Exit without changes"},{value:"overwrite",label:"Overwrite existing project",hint:N.red("(Warning) This may delete existing files")}]});(isCancel(e)||e==="cancel")&&(cancel(N.yellow("Operation cancelled. No changes were made.")),process.exit(0)),e==="overwrite"&&await Je(t);}var vo=[{label:"React",value:"react",hint:"**The most popular library.** Component-based UI with a huge ecosystem (often paired with Vite or Next.js)."},{label:"Next.js",value:"next",hint:"**Production-ready full-stack React framework.** Excellent for SSR, routing, and APIs."},{label:"Vue",value:"vue",hint:"**Progressive and approachable framework.** Fully-featured with great official libraries (Vue Router, Pinia)."},{label:"Svelte",value:"svelte",hint:"**The compiler.** No virtual DOM. Generates highly optimized vanilla JS for faster runtime performance."},{label:"Angular",value:"angular",hint:"**Opinionated, full-featured framework.** Best for large-scale enterprise applications with built-in tooling."},{label:"SolidJS",value:"solid",hint:"**Fine-grained reactivity.** High performance using compiled JSX and actual DOM nodes, no Virtual DOM."},{label:"Qwik",value:"qwik",hint:"Focuses on **Resumability** to achieve near-instant loading times by lazy loading and resuming application state."},{label:"Astro",value:"astro",hint:"An **all-in-one framework** for building fast, content-focused websites using 'Islands of Interactivity'."},{label:"Framer Motion",value:"framer",hint:"Used primarily for **animation and interactive design.** Not a general-purpose framework, but a powerful animation library."},{label:"React Router",value:"react-router",hint:"A declarative routing library for React (often associated with **Remix** full-stack framework)."},{label:"None",value:"none",hint:"**Vanilla JavaScript/TypeScript.** No framework or library required."}];var xo=[{label:"Next.js",value:"next",hint:"**The leading React full-stack framework.** Excellent for server-side rendering (SSR), static site generation (SSG), and API routes."},{label:"Remix",value:"react-router",hint:"**Full-stack React framework built on web standards.** Focuses on route-level data fetching and seamless integration with React Router."},{label:"SvelteKit",value:"svelte",hint:"**The full-stack Svelte framework.** Extremely fast, uses a compiler approach, and offers flexible adapters for deployment."},{label:"Nuxt (Vue)",value:"vue",hint:"**The full-stack Vue framework.** Rich feature set including file-system routing, auto-imports, and powerful module ecosystem."}];var ko=[{label:"Express",value:"express",hint:"**Most popular Node backend.** Simple routing, middleware based, huge ecosystem."},{label:"Koa",value:"koa",hint:"A **minimalist framework** designed by the Express team. Smaller footprint, uses `async/await` for cleaner middleware flow and better error handling."},{label:"Fastify",value:"fastify",hint:"**High-performance & plugin-based.** Best for APIs requiring throughput."},{label:"NestJS",value:"nest",hint:"**Enterprise-grade architecture.** Built-in DI, modules, and scalable structure."},{label:"AdonisJS",value:"adonis",hint:"A **full-featured, opinionated MVC framework** inspired by Laravel/Rails. Comes with its own ORM, CLI, and authentication built-in."},{label:"Hono (Edge-optimized)",value:"hono",hint:"**Tiny + ultra fast for edge runtimes.** Perfect for Cloudflare, Deno, Bun."},{label:"Elysia (Bun)",value:"elysia",hint:"**Deep Bun integration.** Modern type-safe router + middleware."},{label:"None",value:"none",hint:"Choose this if you intend to use a different framework or build your server from scratch."}],So=[{label:"REST",value:"rest",hint:"**Standard + universal.** Works everywhere and easy to scale gradually."},{label:"tRPC",value:"trpc",hint:"**Full end-to-end types.** Great with React/Next fullstack workflows."},{label:"GraphQL",value:"graphql",hint:"**Flexible querying.** Best for complex relational or mobile apps."}],To=[{label:"Monolith",value:"monolith",hint:"**Single, unified unit.** All components (UI, logic, data) are in one codebase and deployed together. **Simple to develop initially**."},{label:"Modular",value:"modular",hint:"A Monolith where code is **logically separated** into independent modules (features/domains) within the *same* codebase. Easier to maintain than a traditional Monolith."},{label:"Microservices",value:"microservices",hint:"**Independent, specialized services.** Each feature runs as its own separate application with its own database, communicating via APIs. **Highly scalable and fault-tolerant**."},{label:"None",value:"none",hint:"No default architecture"}];var Ro={name:"backend",priority:40,condition:(t,e)=>["next","framer","nuxt"].includes(e.frontendFramework??"")?false:e.projectKind==="backend"||e.projectKind==="fullstack",handler:async(t,e)=>{let r=t.interactive!==false,n={backendFramework:e.backendFramework??"express",apiStyle:e.apiStyle??"rest",backendMode:e.backendMode??"none"};if(!r)return {values:n};let i=[{type:"select",name:"backendFramework",message:"\u{1F9F1} Backend Framework:",choices:ko,initial:n.backendFramework},{type:"select",name:"apiStyle",message:"\u{1F50C} API Style:",choices:So,initial:n.apiStyle},{type:"select",name:"backendMode",message:"\u2699\uFE0F Backend Architecture Mode:",choices:To,initial:n.backendMode}];return {values:n,questions:i}}};var Ao={name:"environment",priority:55,handler:async(t,e)=>{let r=t.interactive!==false,n={packageManager:e.packageManager??"pnpm",workspaceTool:e.workspaceTool??"single"};if(!r)return {values:n};let i=[{type:"select",name:"packageManager",message:"\u{1F4E6} Package manager:",choices:uf,initial:n.packageManager}];return {values:n,questions:i}}},uf=[{label:"npm",value:"npm",hint:"The default and most widely adopted package manager for Node.js."},{label:"pnpm",value:"pnpm",hint:"Disk-space efficient and fast. Excellent for monorepos."},{label:"yarn",value:"yarn",hint:"Reliable and feature-rich with strong workspace support."},{label:"bun",value:"bun",hint:"A fast all-in-one runtime, bundler, and package manager."}];var Co={name:"framework",priority:2,condition:(t,e)=>e.projectKind==="frontend"||e.projectKind==="fullstack",handler:async(t,e)=>{let r=t.interactive!==false,i=e.projectKind==="fullstack",s=e.frontendFramework??t.frontendFramework??(i?"next":"react");return r?{values:{frontendFramework:s},questions:[{type:"select",name:"frontendFramework",message:`\u2699\uFE0F ${i?"Fullstack":"Frontend"} Framework:`,choices:i?xo:vo,initial:s}]}:{values:{frontendFramework:s}}}};var _o=[{label:"MIT",value:"MIT",hint:"A short, permissive license with conditions only requiring preservation of copyright and license notices. Great for maximum compatibility."},{label:"Apache-2.0",value:"Apache-2.0",hint:"A permissive license similar to MIT, but providing an express grant of patent rights from contributors to users."},{label:"GPL-3.0",value:"GPL-3.0",hint:"A strong copyleft license. If you modify or redistribute code under this license, you must also release your source code under GPL-3.0."},{label:"Unlicense",value:"Unlicense",hint:"Dedicates the work to the public domain, offering the fewest restrictions possible."},{label:"Other",value:"Other",hint:"Choose this if your license is not listed above and you will provide details later."}];var Eo=[{label:"Frontend Application",value:"frontend",hint:"Create a UI-focused app that interacts with APIs (SPA)."},{label:"Backend API",value:"backend",hint:"Build a server-side API to manage data and handle requests."},{label:"Fullstack (UI + API)",value:"fullstack",hint:"Develop both the frontend and backend in one unified project."},{label:"Library / SDK",value:"library",hint:"Package reusable components or utilities for others to use via NPM."},{label:"CLI Tool",value:"cli",hint:"Automate tasks with a command-line tool (e.g., scaffolding, deployment)."}];var Hn=t=>String(t).trim().toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-_]/g,"").replace(/^-+|-+$/g,"");var Ue=(...t)=>t.find(e=>e!=null&&e!=="");function Do(t,e){let r=e.projectKind??t.projectKind;return r==="library"||r==="cli"||Wi(t?.workspaceTool)}var Vn=t=>{if(typeof t!="string")return null;let e=t.trim().replace(/^@/,"");return e?`@${e}`:null};function Lo(t){if(!t||typeof t!="string")return "Name cannot be empty";let e=t.trim();if(!e.length)return "Name cannot be empty";if(/\s/.test(e))return "Spaces are not allowed \u2014 use dash or underscore";if(/[A-Z]/.test(e))return "Use lowercase letters only (npm package rule)";if(/[~'!()*]/.test(e))return "Name contains forbidden characters ~ ' ! ( ) *";if(/[<>:\"\\|?*]/.test(e))return 'Name contains Windows-forbidden characters < > : " \\ | ? *';if(/[/.]$/.test(e))return "Name cannot end with a dot or slash";if(e.startsWith("@")){if(!/^@[a-z0-9\-_]+\/[a-z0-9\-_]+$/.test(e))return "Invalid scoped package. Use @scope/name"}else if(!/^[a-z0-9\-_]+$/.test(e))return "Use only letters, numbers, dash (-), and underscore (_)";return ["node_modules","favicon","core","con","aux","nul","prn"].includes(e.toLowerCase())?`"${e}" is a reserved name`:true}var Oo={name:"meta",priority:1,handler:async(t,e)=>{let r=t.interactive!==false,n={projectName:Ue(t.projectName,e.projectName,"appinit-app"),description:Ue(e.description,""),author:Ue(e.author,"appinit"),licenseType:Ue(e.licenseType,"MIT"),projectKind:Ue(t.projectKind,e.projectKind,r?void 0:"frontend"),packageScope:Vn(Ue(e.packageScope,"@"))};if(!r)return {values:n};let i=[];return t.projectName||i.push({type:"text",name:"projectName",message:"\u{1F9F1} Project name:",initial:n.projectName,format:s=>s?Hn(s):null,validate:s=>Lo(Hn(s||""))}),i.push({type:"text",name:"description",message:"\u{1F4DD} Short description:",initial:n.description},{type:"text",name:"author",message:"\u{1F464} Author (name/email):",initial:n.author},{type:"select",name:"licenseType",message:"\u{1F4DC} License:",choices:_o,initial:n.licenseType}),t.projectKind||i.push({type:"select",name:"projectKind",message:"\u{1F4E6} Project type:",choices:Eo,initial:n.projectKind}),Do(t,e)&&i.push({type:"text",name:"packageScope",message:"\u{1F4E6} Package scope (optional, without @):",initial:n.packageScope.replace(/^@/,""),format:s=>s?Vn(s):null}),{values:n,questions:i}}};var pf=[Oo,Co,Ao,Ro];async function Po(t,e,r=[]){let{projectName:n,template:i,projectKind:s,skipPrompts:o,overrides:a={},logger:l}=t,c={projectName:n,template:i,projectKind:s,interactive:o===false},u={template:i,...a},y=[...pf,...r].sort((d,b)=>(d.priority??100)-(b.priority??100));Object.freeze(y),c.hooks?.beforeAll&&await c.hooks.beforeAll(c,u);for(let d of y)try{if(d.condition&&!await d.condition(c,u)){l?.info?.(`\u23ED\uFE0F Skipping pack: ${d.name}`);continue}c.hooks?.beforeEach&&await c.hooks.beforeEach(d,c,u),d.before&&await d.before(c,u);let b=await d.handler(c,u);if(b?.values&&Object.assign(u,b.values),c.interactive&&b?.questions&&b.questions.length>0){let v=await e.ask(b.questions,u);Object.assign(u,v);}d.after&&await d.after(c,u),c.hooks?.afterEach&&await c.hooks.afterEach(d,c,u);}catch(b){throw l?.error?.(`\u274C Prompt pack failed: ${d.name}`),b}return c.hooks?.afterAll&&await c.hooks.afterAll(c,u),u}function pr(t){isCancel(t)&&(cancel("Operation cancelled."),process.exit(1));}var Io={async ask(t,e){let r={};for(let n of t){if(n.when&&!await n.when(e))continue;let i;switch(n.type){case "text":{let s=await text({message:n.message,placeholder:n.placeholder,initialValue:n.initial});pr(s),i=s;break}case "confirm":case "toggle":{let s=await confirm({message:n.message,initialValue:n.initial});pr(s),i=s;break}case "select":{let o=(typeof n.choices=="function"?await n.choices(e):n.choices??[]).map(l=>({label:l.label??String(l.value),value:l.value,hint:l.hint})),a=await select({message:n.message,initialValue:n.initial,options:o});pr(a),i=a;break}case "multiselect":{let o=(typeof n.choices=="function"?await n.choices(e):n.choices??[]).map(l=>({label:l.label??String(l.value),value:l.value,hint:l.hint})),a=await multiselect({message:n.message,initialValues:n.initial,required:n.required,options:o});pr(a),i=a;break}default:throw new Error(`Unsupported prompt type: ${n.type}`)}n.format&&(i=n.format(i)),i!=null&&(r[n.name]=i,e[n.name]=i);}return r}};var jo={async ask(t,e){let r={};for(let n of t){if(n.when&&!await n.when(e))continue;let i=n.name,s;if(e[i]!==void 0)s=e[i];else if(n.initial!==void 0)s=n.initial;else throw new Error(`[headless-runtime] Missing required value for "${n.name}"`);if(n.format&&(s=n.format(s)),n.validate){let o=await n.validate(s);if(o!==true)throw new Error(`[headless-runtime] Validation failed for "${n.name}": ${typeof o=="string"?o:"invalid value"}`)}r[i]=s,e[i]=s;}return r}};async function Fo(t){let e=Ee__default.join(t,"hooks"),r=Ee__default.join(e,"before.ts"),n=Ee__default.join(e,"after.ts"),i={};if(U(r)){let s=await he(r);i.before=s.default??s;}if(U(n)){let s=await he(n);i.after=s.default??s;}return i}var $o=process.stdout?.columns||80;process.stdout?.rows||24;var ve=()=>["CI","GITHUB_ACTIONS","BITBUCKET_PIPELINES","BUILDKITE","GITLAB_CI","CIRCLECI","JENKINS_HOME"].some(t=>process.env[t]==="true");async function wf(t){return !((t??{}).nonInteractive||!process.stdin.isTTY||!process.stdout.isTTY||ve()||Bi()||await Gi())}var No=wf;var Fe=!!(process.stdout&&process.stdout.isTTY&&isatty(1));process.platform==="win32";var qn={brand:N.cyan,primary:N.cyan,success:N.green,danger:N.red,warning:N.yellow,info:N.blue,dim:N.gray,red:N.red,green:N.green,yellow:N.yellow,blue:N.blue,cyan:N.cyan,magenta:N.magenta,gray:N.gray};({bgRed:N.bgRed,bgGreen:N.bgGreen,bgYellow:N.bgYellow,bgBlue:N.bgBlue,bgMagenta:N.bgMagenta,bgCyan:N.bgCyan,bgWhite:N.bgWhite,bgBlack:N.bgBlack});var Mo={default:{brand:t=>N.cyan().bold(t),primary:t=>N.cyan(t),success:t=>N.green().bold(t),danger:t=>N.red().bold(t),warning:t=>N.yellow().bold(t),info:t=>N.blue(t),dim:t=>N.gray(t)},dark:{brand:t=>N.magenta().bold(t),primary:t=>N.white(t),success:t=>N.green(t),danger:t=>N.red(t),warning:t=>N.yellow(t),info:t=>N.cyan(t),dim:t=>N.gray(t)},neon:{brand:t=>N.magenta().bold().underline(t),primary:t=>N.green().bold(t),success:t=>N.cyan().bold(t),danger:t=>N.red().bold(t),warning:t=>N.yellow().bold().underline(t),info:t=>N.blue().bold(t),dim:t=>N.gray(t)},mono:{brand:t=>t,primary:t=>t,success:t=>t,danger:t=>t,warning:t=>t,info:t=>t,dim:t=>t}};var vf="default";var T=new Proxy({},{get(t,e){return Mo[vf][e]}});var fr={bold:t=>N.bold(t),italic:t=>N.italic(t),underline:t=>N.underline(t),apply:(...t)=>e=>t.reduce((r,n)=>n(r),e)};async function Ho(t){let e=Fe&&!ve(),r=e&&process.stdout.columns||80,n="\u2500".repeat(Math.min(r,60)),i=console.log;i(""),e?(i(T.success(n)),i(T.success("\u2728 Project created successfully")),i("")):i("Project created successfully");let s=resolve(process.cwd(),t),o;try{o=await lr(s);}catch{o="npm";}let a="",l="";switch(o){case "pnpm":a="pnpm install",l="pnpm dev";break;case "yarn":a="yarn",l="yarn dev";break;case "bun":a="bun install",l="bun dev";break;default:a="npm install",l="npm run dev";break}i(fr.bold("\u{1F449} Next steps:")),i(` ${T.primary(`cd ${t}`)}`),i(` ${T.primary(a)}`),i(` ${T.primary(l)}`),i(""),e&&(i(T.dim("\u{1F4A1} Start building your app right away.")),i(T.dim("\u{1F4A1} Add features later with `appinit add` (packs, modules, capabilities).")),i(T.dim("\u{1F4A1} Need help? Run `appinit help`.")),i(""),i(T.success(n)),i(""));}var kf=`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
${
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { stdin, stdout } from "process";
|
|
5
|
+
import { createInterface } from "readline/promises";
|
|
6
|
+
|
|
7
|
+
// src/args.ts
|
|
8
|
+
function parseVariable(value) {
|
|
9
|
+
const separator = value.indexOf("=");
|
|
10
|
+
if (separator <= 0) throw new Error(`Invalid --var "${value}". Expected key=value.`);
|
|
11
|
+
const key = value.slice(0, separator).trim();
|
|
12
|
+
const raw = value.slice(separator + 1);
|
|
13
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_.-]*$/.test(key)) throw new Error(`Invalid variable name "${key}".`);
|
|
14
|
+
let parsed = raw;
|
|
15
|
+
try {
|
|
16
|
+
parsed = JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
return [key, parsed];
|
|
20
|
+
}
|
|
21
|
+
function parseCreateArguments(args) {
|
|
22
|
+
const positional = [];
|
|
23
|
+
const variables = {};
|
|
24
|
+
let dryRun = false;
|
|
25
|
+
let yes = false;
|
|
26
|
+
let template;
|
|
27
|
+
let templateDirectory;
|
|
28
|
+
let install = false;
|
|
29
|
+
let git = false;
|
|
30
|
+
let packageManager;
|
|
31
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
32
|
+
const argument = args[index];
|
|
33
|
+
if (argument === "--dry-run") dryRun = true;
|
|
34
|
+
else if (argument === "--install") install = true;
|
|
35
|
+
else if (argument === "--git") git = true;
|
|
36
|
+
else if (argument === "--package-manager") {
|
|
37
|
+
const value = args[++index];
|
|
38
|
+
if (!value) throw new Error("--package-manager requires pnpm, npm, yarn, or bun.");
|
|
39
|
+
if (value !== "pnpm" && value !== "npm" && value !== "yarn" && value !== "bun") throw new Error(`Unsupported package manager "${value}". Choose pnpm, npm, yarn, or bun.`);
|
|
40
|
+
packageManager = value;
|
|
41
|
+
} else if (argument === "--yes" || argument === "-y") yes = true;
|
|
42
|
+
else if (argument === "--template" || argument === "-t") {
|
|
43
|
+
const value = args[++index];
|
|
44
|
+
if (!value) throw new Error(`${argument} requires a template reference.`);
|
|
45
|
+
template = value;
|
|
46
|
+
} else if (argument === "--template-dir") {
|
|
47
|
+
const value = args[++index];
|
|
48
|
+
if (!value) throw new Error("--template-dir requires a directory.");
|
|
49
|
+
templateDirectory = value;
|
|
50
|
+
} else if (argument === "--var") {
|
|
51
|
+
const value = args[++index];
|
|
52
|
+
if (!value) throw new Error("--var requires key=value.");
|
|
53
|
+
const [key, parsed] = parseVariable(value);
|
|
54
|
+
if (Object.hasOwn(variables, key)) throw new Error(`Variable "${key}" was provided more than once.`);
|
|
55
|
+
variables[key] = parsed;
|
|
56
|
+
} else if (argument.startsWith("-")) {
|
|
57
|
+
throw new Error(`Unknown option: ${argument}`);
|
|
58
|
+
} else positional.push(argument);
|
|
59
|
+
}
|
|
60
|
+
if (positional.length > 2) throw new Error('Too many arguments. Run "appinit create --help".');
|
|
61
|
+
if (positional.length === 2 && template) {
|
|
62
|
+
throw new Error("Do not combine --template with two positional arguments; use <template> <target>.");
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze({
|
|
65
|
+
...positional.length === 2 ? { template: positional[0], target: positional[1] } : {},
|
|
66
|
+
...positional.length === 1 ? { target: positional[0] } : {},
|
|
67
|
+
...template ? { template } : {},
|
|
68
|
+
dryRun,
|
|
69
|
+
yes,
|
|
70
|
+
...templateDirectory ? { templateDirectory } : {},
|
|
71
|
+
variables: Object.freeze(variables),
|
|
72
|
+
install,
|
|
73
|
+
git,
|
|
74
|
+
...packageManager ? { packageManager } : {}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/context.ts
|
|
79
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
80
|
+
import path8 from "path";
|
|
81
|
+
|
|
82
|
+
// ../os/src/runtime/fs/is-binary-file.ts
|
|
83
|
+
import path from "path";
|
|
84
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
85
|
+
".png",
|
|
86
|
+
".jpg",
|
|
87
|
+
".jpeg",
|
|
88
|
+
".gif",
|
|
89
|
+
".webp",
|
|
90
|
+
".ico",
|
|
91
|
+
".bmp",
|
|
92
|
+
".svg",
|
|
93
|
+
".svgz",
|
|
94
|
+
".woff",
|
|
95
|
+
".woff2",
|
|
96
|
+
".ttf",
|
|
97
|
+
".otf",
|
|
98
|
+
".eot",
|
|
99
|
+
".mp3",
|
|
100
|
+
".mp4",
|
|
101
|
+
".wav",
|
|
102
|
+
".ogg",
|
|
103
|
+
".webm",
|
|
104
|
+
".zip",
|
|
105
|
+
".tar",
|
|
106
|
+
".gz",
|
|
107
|
+
".tgz",
|
|
108
|
+
".rar",
|
|
109
|
+
".7z",
|
|
110
|
+
".pdf",
|
|
111
|
+
".exe",
|
|
112
|
+
".dll",
|
|
113
|
+
".bin"
|
|
114
|
+
]);
|
|
115
|
+
function isBinaryFile(filePath) {
|
|
116
|
+
return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ../os/src/runtime/fs/path.ts
|
|
120
|
+
import path2 from "path";
|
|
121
|
+
|
|
122
|
+
// ../os/src/runtime/fs/traversal.ts
|
|
123
|
+
import { lstat, readdir } from "fs/promises";
|
|
124
|
+
import path3 from "path";
|
|
125
|
+
import picomatch from "picomatch";
|
|
126
|
+
async function readDirRecursive(root, options = {}) {
|
|
127
|
+
const {
|
|
128
|
+
prefix = "",
|
|
129
|
+
ignores = [],
|
|
130
|
+
mode = "relative"
|
|
131
|
+
} = options;
|
|
132
|
+
const absoluteRoot = path3.resolve(root);
|
|
133
|
+
const normalizedPrefix = prefix ? path3.posix.normalize(prefix.replace(/\\/g, "/")) : "";
|
|
134
|
+
if (normalizedPrefix === ".." || normalizedPrefix.startsWith("../") || path3.posix.isAbsolute(normalizedPrefix)) {
|
|
135
|
+
throw new Error("Traversal prefix must remain inside the root directory.");
|
|
136
|
+
}
|
|
137
|
+
const isIgnored = ignores.length > 0 ? picomatch(ignores, { dot: true }) : () => false;
|
|
138
|
+
const results = [];
|
|
139
|
+
async function walk2(directory, relativeDirectory) {
|
|
140
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
141
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
142
|
+
for (const entry of entries) {
|
|
143
|
+
const relativePath = path3.posix.join(relativeDirectory, entry.name);
|
|
144
|
+
if (isIgnored(relativePath)) continue;
|
|
145
|
+
const absolutePath = path3.join(directory, entry.name);
|
|
146
|
+
let details;
|
|
147
|
+
try {
|
|
148
|
+
details = await lstat(absolutePath);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (error.code === "ENOENT") continue;
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
if (details.isSymbolicLink()) continue;
|
|
154
|
+
if (details.isDirectory()) {
|
|
155
|
+
await walk2(absolutePath, relativePath);
|
|
156
|
+
} else if (details.isFile()) {
|
|
157
|
+
results.push(
|
|
158
|
+
mode === "absolute" ? absolutePath : relativePath
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
await walk2(absoluteRoot, normalizedPrefix);
|
|
164
|
+
return results.sort((a, b) => a.localeCompare(b));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ../os/src/runtime/vfs/freeze-vfs.ts
|
|
168
|
+
function freezeVFS(vfs) {
|
|
169
|
+
for (const file of vfs.list()) {
|
|
170
|
+
Object.freeze(file);
|
|
171
|
+
}
|
|
172
|
+
return vfs;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ../os/src/runtime/vfs/vfs.ts
|
|
176
|
+
import path4 from "path";
|
|
177
|
+
function normalizeVfsDestination(destination) {
|
|
178
|
+
if (typeof destination !== "string" || destination.trim().length === 0 || destination.includes("\0")) {
|
|
179
|
+
throw new Error("VFS destination must be a non-empty relative path.");
|
|
180
|
+
}
|
|
181
|
+
const portable = destination.replace(/\\/g, "/");
|
|
182
|
+
if (portable.startsWith("/") || portable.startsWith("//") || /^[a-zA-Z]:/.test(portable)) {
|
|
183
|
+
throw new Error(`VFS destination must be relative: ${destination}`);
|
|
184
|
+
}
|
|
185
|
+
let normalized = path4.posix.normalize(portable);
|
|
186
|
+
while (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
187
|
+
if (normalized === "." || normalized === ".." || normalized.startsWith("../") || path4.posix.isAbsolute(normalized)) {
|
|
188
|
+
throw new Error(`VFS destination escapes the project root: ${destination}`);
|
|
189
|
+
}
|
|
190
|
+
return normalized;
|
|
191
|
+
}
|
|
192
|
+
var VirtualFileSystem = class {
|
|
193
|
+
files = /* @__PURE__ */ new Map();
|
|
194
|
+
add(file) {
|
|
195
|
+
const destination = normalizeVfsDestination(file.dest);
|
|
196
|
+
if (this.files.has(destination)) {
|
|
197
|
+
throw new Error(`VFS conflict: file already exists at ${destination}`);
|
|
198
|
+
}
|
|
199
|
+
this.files.set(destination, { ...file, dest: destination });
|
|
200
|
+
}
|
|
201
|
+
get(destination) {
|
|
202
|
+
return this.files.get(normalizeVfsDestination(destination));
|
|
203
|
+
}
|
|
204
|
+
list() {
|
|
205
|
+
return this.files.values();
|
|
206
|
+
}
|
|
207
|
+
remove(destination) {
|
|
208
|
+
return this.files.delete(normalizeVfsDestination(destination));
|
|
209
|
+
}
|
|
210
|
+
updateContent(destination, content) {
|
|
211
|
+
const file = this.get(destination);
|
|
212
|
+
if (!file) return;
|
|
213
|
+
if (Object.isFrozen(file)) {
|
|
214
|
+
throw new Error(`Attempted to mutate frozen VFS file: ${destination}`);
|
|
215
|
+
}
|
|
216
|
+
file.content = content;
|
|
217
|
+
}
|
|
218
|
+
get size() {
|
|
219
|
+
return this.files.size;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// ../os/src/engine/execute-file-operations.ts
|
|
224
|
+
import {
|
|
225
|
+
lstat as lstat2,
|
|
226
|
+
mkdir,
|
|
227
|
+
mkdtemp,
|
|
228
|
+
realpath,
|
|
229
|
+
rename,
|
|
230
|
+
rm,
|
|
231
|
+
rmdir,
|
|
232
|
+
writeFile
|
|
233
|
+
} from "fs/promises";
|
|
234
|
+
import path5 from "path";
|
|
235
|
+
function normalizeDestination(value) {
|
|
236
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
237
|
+
throw new Error("Destination must be a non-empty relative path.");
|
|
238
|
+
}
|
|
239
|
+
const portable = value.replace(/\\/g, "/");
|
|
240
|
+
if (portable.startsWith("/") || /^[a-zA-Z]:/.test(portable)) {
|
|
241
|
+
throw new Error("Absolute destinations are not allowed.");
|
|
242
|
+
}
|
|
243
|
+
if (portable.split("/").some((segment) => segment === ".." || segment === ".")) {
|
|
244
|
+
throw new Error("Dot and parent-directory segments are not allowed.");
|
|
245
|
+
}
|
|
246
|
+
return normalizeVfsDestination(portable);
|
|
247
|
+
}
|
|
248
|
+
async function exists(filePath) {
|
|
249
|
+
try {
|
|
250
|
+
await lstat2(filePath);
|
|
251
|
+
return true;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error.code === "ENOENT") return false;
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async function ensureSafeTargetDirectory(targetRoot, relativeDirectory) {
|
|
258
|
+
let current = targetRoot;
|
|
259
|
+
if (!relativeDirectory) return;
|
|
260
|
+
for (const segment of relativeDirectory.split(/[\\/]+/).filter(Boolean)) {
|
|
261
|
+
current = path5.join(current, segment);
|
|
262
|
+
if (await exists(current)) {
|
|
263
|
+
const info = await lstat2(current);
|
|
264
|
+
if (info.isSymbolicLink()) {
|
|
265
|
+
throw new Error(`Target path contains a symbolic link: ${current}`);
|
|
266
|
+
}
|
|
267
|
+
if (!info.isDirectory()) {
|
|
268
|
+
throw new Error(`Target path component is not a directory: ${current}`);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
await mkdir(current);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async function ensureSafeTargetRoot(targetDirectory) {
|
|
276
|
+
const absolute = path5.resolve(targetDirectory);
|
|
277
|
+
const parent = path5.dirname(absolute);
|
|
278
|
+
await mkdir(parent, { recursive: true });
|
|
279
|
+
const canonicalParent = await realpath(parent);
|
|
280
|
+
const root = path5.join(canonicalParent, path5.basename(absolute));
|
|
281
|
+
if (await exists(root)) {
|
|
282
|
+
const info = await lstat2(root);
|
|
283
|
+
if (info.isSymbolicLink()) throw new Error("Target directory must not be a symbolic link.");
|
|
284
|
+
if (!info.isDirectory()) throw new Error("Target path exists and is not a directory.");
|
|
285
|
+
} else {
|
|
286
|
+
await mkdir(root);
|
|
287
|
+
}
|
|
288
|
+
return await realpath(root);
|
|
289
|
+
}
|
|
290
|
+
async function executeFileOperations(targetDirectory, operations, options = {}) {
|
|
291
|
+
const diagnostics = [];
|
|
292
|
+
const normalized = [];
|
|
293
|
+
const seen = /* @__PURE__ */ new Set();
|
|
294
|
+
for (const operation of operations) {
|
|
295
|
+
try {
|
|
296
|
+
const destination = normalizeDestination(operation.destination);
|
|
297
|
+
if (seen.has(destination)) {
|
|
298
|
+
diagnostics.push({
|
|
299
|
+
code: "EXECUTION_DUPLICATE_PATH",
|
|
300
|
+
message: `Execution plan contains duplicate destination "${destination}".`,
|
|
301
|
+
destination
|
|
302
|
+
});
|
|
303
|
+
} else {
|
|
304
|
+
seen.add(destination);
|
|
305
|
+
normalized.push({ ...operation, destination });
|
|
306
|
+
}
|
|
307
|
+
} catch (error) {
|
|
308
|
+
diagnostics.push({
|
|
309
|
+
code: "EXECUTION_INVALID_PATH",
|
|
310
|
+
message: error instanceof Error ? error.message : String(error),
|
|
311
|
+
destination: operation.destination
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (diagnostics.length > 0) {
|
|
316
|
+
return Object.freeze({
|
|
317
|
+
ok: false,
|
|
318
|
+
dryRun: options.dryRun === true,
|
|
319
|
+
writtenFiles: Object.freeze([]),
|
|
320
|
+
overwrittenFiles: Object.freeze([]),
|
|
321
|
+
diagnostics: Object.freeze(diagnostics)
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (typeof targetDirectory !== "string" || targetDirectory.trim().length === 0) {
|
|
325
|
+
return Object.freeze({
|
|
326
|
+
ok: false,
|
|
327
|
+
dryRun: options.dryRun === true,
|
|
328
|
+
writtenFiles: Object.freeze([]),
|
|
329
|
+
overwrittenFiles: Object.freeze([]),
|
|
330
|
+
diagnostics: Object.freeze([{
|
|
331
|
+
code: "EXECUTION_INVALID_TARGET",
|
|
332
|
+
message: "Target directory must be a non-empty path."
|
|
333
|
+
}])
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (options.dryRun) {
|
|
337
|
+
return Object.freeze({
|
|
338
|
+
ok: true,
|
|
339
|
+
dryRun: true,
|
|
340
|
+
writtenFiles: Object.freeze(normalized.map((item) => item.destination)),
|
|
341
|
+
overwrittenFiles: Object.freeze([]),
|
|
342
|
+
diagnostics: Object.freeze([])
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
const targetExistedBeforeExecution = await exists(path5.resolve(targetDirectory));
|
|
346
|
+
let targetRoot;
|
|
347
|
+
try {
|
|
348
|
+
targetRoot = await ensureSafeTargetRoot(targetDirectory);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
return Object.freeze({
|
|
351
|
+
ok: false,
|
|
352
|
+
dryRun: false,
|
|
353
|
+
writtenFiles: Object.freeze([]),
|
|
354
|
+
overwrittenFiles: Object.freeze([]),
|
|
355
|
+
diagnostics: Object.freeze([{
|
|
356
|
+
code: "EXECUTION_INVALID_TARGET",
|
|
357
|
+
message: error instanceof Error ? error.message : String(error)
|
|
358
|
+
}])
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const parent = path5.dirname(targetRoot);
|
|
362
|
+
const stagingRoot = await mkdtemp(path5.join(parent, ".appinit-stage-"));
|
|
363
|
+
const payloadRoot = path5.join(stagingRoot, "payload");
|
|
364
|
+
const backupRoot = path5.join(stagingRoot, "backup");
|
|
365
|
+
await mkdir(payloadRoot);
|
|
366
|
+
await mkdir(backupRoot);
|
|
367
|
+
const writtenFiles = [];
|
|
368
|
+
const overwrittenFiles = [];
|
|
369
|
+
const installed = [];
|
|
370
|
+
const backups = [];
|
|
371
|
+
const createdDirectories = [];
|
|
372
|
+
try {
|
|
373
|
+
for (const operation of normalized) {
|
|
374
|
+
const stagedPath = path5.join(payloadRoot, ...operation.destination.split("/"));
|
|
375
|
+
await mkdir(path5.dirname(stagedPath), { recursive: true });
|
|
376
|
+
await writeFile(stagedPath, operation.content);
|
|
377
|
+
}
|
|
378
|
+
for (const operation of normalized) {
|
|
379
|
+
const destinationPath = path5.join(targetRoot, ...operation.destination.split("/"));
|
|
380
|
+
const relativeParent = path5.dirname(operation.destination);
|
|
381
|
+
const before = /* @__PURE__ */ new Set();
|
|
382
|
+
let cursor = targetRoot;
|
|
383
|
+
for (const segment of relativeParent === "." ? [] : relativeParent.split("/")) {
|
|
384
|
+
cursor = path5.join(cursor, segment);
|
|
385
|
+
if (!await exists(cursor)) before.add(cursor);
|
|
386
|
+
}
|
|
387
|
+
await ensureSafeTargetDirectory(targetRoot, relativeParent);
|
|
388
|
+
for (const directory of before) createdDirectories.push(directory);
|
|
389
|
+
if (await exists(destinationPath)) {
|
|
390
|
+
const info = await lstat2(destinationPath);
|
|
391
|
+
if (info.isSymbolicLink()) {
|
|
392
|
+
throw new Error(`Refusing to overwrite symbolic link: ${operation.destination}`);
|
|
393
|
+
}
|
|
394
|
+
if (!info.isFile()) {
|
|
395
|
+
throw new Error(`Destination exists and is not a regular file: ${operation.destination}`);
|
|
396
|
+
}
|
|
397
|
+
const backupPath = path5.join(backupRoot, ...operation.destination.split("/"));
|
|
398
|
+
await mkdir(path5.dirname(backupPath), { recursive: true });
|
|
399
|
+
await rename(destinationPath, backupPath);
|
|
400
|
+
backups.push({ backup: backupPath, destination: destinationPath });
|
|
401
|
+
overwrittenFiles.push(operation.destination);
|
|
402
|
+
} else {
|
|
403
|
+
writtenFiles.push(operation.destination);
|
|
404
|
+
}
|
|
405
|
+
const stagedPath = path5.join(payloadRoot, ...operation.destination.split("/"));
|
|
406
|
+
await rename(stagedPath, destinationPath);
|
|
407
|
+
installed.push(destinationPath);
|
|
408
|
+
}
|
|
409
|
+
await rm(stagingRoot, { recursive: true, force: true });
|
|
410
|
+
return Object.freeze({
|
|
411
|
+
ok: true,
|
|
412
|
+
dryRun: false,
|
|
413
|
+
writtenFiles: Object.freeze(writtenFiles),
|
|
414
|
+
overwrittenFiles: Object.freeze(overwrittenFiles),
|
|
415
|
+
diagnostics: Object.freeze([])
|
|
416
|
+
});
|
|
417
|
+
} catch (error) {
|
|
418
|
+
const rollbackErrors = [];
|
|
419
|
+
for (const destination of installed.reverse()) {
|
|
420
|
+
await rm(destination, { force: true }).catch((rollbackError) => {
|
|
421
|
+
rollbackErrors.push(`Could not remove "${destination}": ${String(rollbackError)}`);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
for (const item of backups.reverse()) {
|
|
425
|
+
await mkdir(path5.dirname(item.destination), { recursive: true }).catch((rollbackError) => {
|
|
426
|
+
rollbackErrors.push(`Could not recreate parent for "${item.destination}": ${String(rollbackError)}`);
|
|
427
|
+
});
|
|
428
|
+
await rename(item.backup, item.destination).catch((rollbackError) => {
|
|
429
|
+
rollbackErrors.push(`Could not restore "${item.destination}": ${String(rollbackError)}`);
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
for (const directory of createdDirectories.reverse()) {
|
|
433
|
+
await rmdir(directory).catch(() => void 0);
|
|
434
|
+
}
|
|
435
|
+
if (!targetExistedBeforeExecution) {
|
|
436
|
+
await rmdir(targetRoot).catch(() => void 0);
|
|
437
|
+
}
|
|
438
|
+
await rm(stagingRoot, { recursive: true, force: true }).catch((rollbackError) => {
|
|
439
|
+
rollbackErrors.push(`Could not remove staging directory: ${String(rollbackError)}`);
|
|
440
|
+
});
|
|
441
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
442
|
+
return Object.freeze({
|
|
443
|
+
ok: false,
|
|
444
|
+
dryRun: false,
|
|
445
|
+
writtenFiles: Object.freeze([]),
|
|
446
|
+
overwrittenFiles: Object.freeze([]),
|
|
447
|
+
diagnostics: Object.freeze([{
|
|
448
|
+
code: "EXECUTION_COMMIT_FAILED",
|
|
449
|
+
message: rollbackErrors.length > 0 ? `${cause}. Rollback also reported: ${rollbackErrors.join("; ")}` : cause
|
|
450
|
+
}])
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ../kernel/src/execution/phase.ts
|
|
456
|
+
var LIFECYCLE_ORDER = [
|
|
457
|
+
"detect",
|
|
458
|
+
"validate",
|
|
459
|
+
"resolve",
|
|
460
|
+
"plan",
|
|
461
|
+
"execute",
|
|
462
|
+
"verify",
|
|
463
|
+
"report"
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
// ../kernel/src/template/manifest.ts
|
|
467
|
+
var ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
468
|
+
"schemaVersion",
|
|
469
|
+
"id",
|
|
470
|
+
"version",
|
|
471
|
+
"kind",
|
|
472
|
+
"atsVersion",
|
|
473
|
+
"name",
|
|
474
|
+
"description",
|
|
475
|
+
"category",
|
|
476
|
+
"language",
|
|
477
|
+
"packageName",
|
|
478
|
+
"renderer",
|
|
479
|
+
"paths",
|
|
480
|
+
"tags",
|
|
481
|
+
"requires",
|
|
482
|
+
"provides",
|
|
483
|
+
"conflicts"
|
|
484
|
+
]);
|
|
485
|
+
var TEMPLATE_KINDS = /* @__PURE__ */ new Set([
|
|
486
|
+
"project",
|
|
487
|
+
"module",
|
|
488
|
+
"capability",
|
|
489
|
+
"preset",
|
|
490
|
+
"composition"
|
|
491
|
+
]);
|
|
492
|
+
var SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
493
|
+
var ID_PATTERN = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
494
|
+
var ATS_PATTERN = /^\d+\.\d+$/;
|
|
495
|
+
var PACKAGE_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
496
|
+
function isRecord(value) {
|
|
497
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
498
|
+
}
|
|
499
|
+
function isNonEmptyString(value) {
|
|
500
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
501
|
+
}
|
|
502
|
+
function isSafeRelativePath(value) {
|
|
503
|
+
if (!isNonEmptyString(value) || value.includes("\\") || value.startsWith("/")) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
const segments = value.replace(/^\.\//, "").split("/");
|
|
507
|
+
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
508
|
+
}
|
|
509
|
+
function validateTemplateManifest(value) {
|
|
510
|
+
const errors = [];
|
|
511
|
+
if (!isRecord(value)) {
|
|
512
|
+
return { valid: false, errors: ["Manifest must be a JSON object."] };
|
|
513
|
+
}
|
|
514
|
+
for (const key of Object.keys(value)) {
|
|
515
|
+
if (!ALLOWED_KEYS.has(key)) errors.push(`Unknown manifest field: ${key}`);
|
|
516
|
+
}
|
|
517
|
+
if (value.schemaVersion !== 1) {
|
|
518
|
+
errors.push("schemaVersion must be 1.");
|
|
519
|
+
}
|
|
520
|
+
if (!isNonEmptyString(value.id) || !ID_PATTERN.test(value.id)) {
|
|
521
|
+
errors.push("id must use lowercase dot- or hyphen-separated identifiers.");
|
|
522
|
+
}
|
|
523
|
+
if (!isNonEmptyString(value.version) || !SEMVER_PATTERN.test(value.version)) {
|
|
524
|
+
errors.push("version must be a valid semantic version.");
|
|
525
|
+
}
|
|
526
|
+
if (!isNonEmptyString(value.kind) || !TEMPLATE_KINDS.has(value.kind)) {
|
|
527
|
+
errors.push("kind must be project, module, capability, preset, or composition.");
|
|
528
|
+
}
|
|
529
|
+
if (!isNonEmptyString(value.atsVersion) || !ATS_PATTERN.test(value.atsVersion)) {
|
|
530
|
+
errors.push("atsVersion must use the MAJOR.MINOR format (for example, 2.0).");
|
|
531
|
+
}
|
|
532
|
+
if (!isNonEmptyString(value.name)) errors.push("name is required.");
|
|
533
|
+
if (!isNonEmptyString(value.description)) errors.push("description is required.");
|
|
534
|
+
if (!isNonEmptyString(value.category)) errors.push("category is required.");
|
|
535
|
+
if (!isNonEmptyString(value.language)) errors.push("language is required.");
|
|
536
|
+
if (!isNonEmptyString(value.packageName) || !PACKAGE_NAME_PATTERN.test(value.packageName)) {
|
|
537
|
+
errors.push("packageName must be a valid npm package name.");
|
|
538
|
+
}
|
|
539
|
+
if (!isRecord(value.renderer)) {
|
|
540
|
+
errors.push("renderer must be an object.");
|
|
541
|
+
} else if (value.renderer.engine !== "liquid" && value.renderer.engine !== "none") {
|
|
542
|
+
errors.push('renderer.engine must be "liquid" or "none".');
|
|
543
|
+
}
|
|
544
|
+
if (!isRecord(value.paths)) {
|
|
545
|
+
errors.push("paths must be an object.");
|
|
546
|
+
} else {
|
|
547
|
+
if (!isSafeRelativePath(value.paths.template)) {
|
|
548
|
+
errors.push("paths.template must be a safe relative path.");
|
|
549
|
+
}
|
|
550
|
+
for (const key of ["variables", "hooks"]) {
|
|
551
|
+
if (value.paths[key] !== void 0 && !isSafeRelativePath(value.paths[key])) {
|
|
552
|
+
errors.push(`paths.${key} must be a safe relative path when provided.`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (!Array.isArray(value.tags) || !value.tags.every((tag) => isNonEmptyString(tag))) {
|
|
557
|
+
errors.push("tags must be an array of non-empty strings.");
|
|
558
|
+
}
|
|
559
|
+
for (const key of ["requires", "provides", "conflicts"]) {
|
|
560
|
+
const entries = value[key];
|
|
561
|
+
if (entries === void 0) continue;
|
|
562
|
+
if (!Array.isArray(entries) || !entries.every((entry) => isNonEmptyString(entry))) {
|
|
563
|
+
errors.push(`${key} must be an array of non-empty strings when provided.`);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
if (new Set(entries).size !== entries.length) {
|
|
567
|
+
errors.push(`${key} must not contain duplicate entries.`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (errors.length > 0) return { valid: false, errors };
|
|
571
|
+
return {
|
|
572
|
+
valid: true,
|
|
573
|
+
errors: [],
|
|
574
|
+
manifest: value
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ../kernel/src/template/registry.ts
|
|
579
|
+
var CATALOG_KEYS = /* @__PURE__ */ new Set(["schemaVersion", "updatedAt", "templates"]);
|
|
580
|
+
var ENTRY_KEYS = /* @__PURE__ */ new Set([
|
|
581
|
+
"id",
|
|
582
|
+
"version",
|
|
583
|
+
"name",
|
|
584
|
+
"description",
|
|
585
|
+
"category",
|
|
586
|
+
"language",
|
|
587
|
+
"tags",
|
|
588
|
+
"archive"
|
|
589
|
+
]);
|
|
590
|
+
var ARCHIVE_KEYS = /* @__PURE__ */ new Set(["url", "sha256"]);
|
|
591
|
+
var ID_PATTERN2 = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
|
|
592
|
+
var SEMVER_PATTERN2 = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
593
|
+
var SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
594
|
+
var MAX_CATALOG_ENTRIES = 1e4;
|
|
595
|
+
var MAX_TEXT_LENGTH = 2e3;
|
|
596
|
+
function isRecord2(value) {
|
|
597
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
598
|
+
}
|
|
599
|
+
function isText(value) {
|
|
600
|
+
return typeof value === "string" && value.trim().length > 0 && value.length <= MAX_TEXT_LENGTH;
|
|
601
|
+
}
|
|
602
|
+
function hasOnlyKeys(value, allowed, prefix, errors) {
|
|
603
|
+
for (const key of Object.keys(value)) {
|
|
604
|
+
if (!allowed.has(key)) errors.push(`Unknown ${prefix} field: ${key}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
function isHttpsUrl(value) {
|
|
608
|
+
if (typeof value !== "string" || value.length > 4096) return false;
|
|
609
|
+
try {
|
|
610
|
+
const url = new URL(value);
|
|
611
|
+
return url.protocol === "https:" && url.hostname.length > 0 && url.username.length === 0 && url.password.length === 0 && url.hash.length === 0;
|
|
612
|
+
} catch {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function isIsoDateTime(value) {
|
|
617
|
+
if (typeof value !== "string" || value.length > 64 || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/.test(value)) {
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
const timestamp = Date.parse(value);
|
|
621
|
+
if (!Number.isFinite(timestamp)) return false;
|
|
622
|
+
const fraction = /\.(\d{1,3})Z$/.exec(value)?.[1] ?? "";
|
|
623
|
+
const normalized = value.replace(
|
|
624
|
+
/(?:\.\d{1,3})?Z$/,
|
|
625
|
+
"." + fraction.padEnd(3, "0") + "Z"
|
|
626
|
+
);
|
|
627
|
+
return new Date(timestamp).toISOString() === normalized;
|
|
628
|
+
}
|
|
629
|
+
function validateTemplateRegistryCatalog(value) {
|
|
630
|
+
const errors = [];
|
|
631
|
+
if (!isRecord2(value)) {
|
|
632
|
+
return { valid: false, errors: ["Registry catalog must be a JSON object."] };
|
|
633
|
+
}
|
|
634
|
+
hasOnlyKeys(value, CATALOG_KEYS, "catalog", errors);
|
|
635
|
+
if (value.schemaVersion !== 1) {
|
|
636
|
+
errors.push("schemaVersion must be 1.");
|
|
637
|
+
}
|
|
638
|
+
if (value.updatedAt !== void 0 && !isIsoDateTime(value.updatedAt)) {
|
|
639
|
+
errors.push("updatedAt must be an ISO-8601 UTC timestamp.");
|
|
640
|
+
}
|
|
641
|
+
if (!Array.isArray(value.templates)) {
|
|
642
|
+
errors.push("templates must be an array.");
|
|
643
|
+
return { valid: false, errors };
|
|
644
|
+
}
|
|
645
|
+
if (value.templates.length > MAX_CATALOG_ENTRIES) {
|
|
646
|
+
errors.push(`templates must contain no more than ${MAX_CATALOG_ENTRIES} entries.`);
|
|
647
|
+
return { valid: false, errors };
|
|
648
|
+
}
|
|
649
|
+
const identities = /* @__PURE__ */ new Set();
|
|
650
|
+
value.templates.forEach((entry, index) => {
|
|
651
|
+
const prefix = `templates[${index}]`;
|
|
652
|
+
if (!isRecord2(entry)) {
|
|
653
|
+
errors.push(`${prefix} must be an object.`);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
hasOnlyKeys(entry, ENTRY_KEYS, prefix, errors);
|
|
657
|
+
if (!isText(entry.id) || !ID_PATTERN2.test(entry.id)) {
|
|
658
|
+
errors.push(`${prefix}.id must use lowercase dot- or hyphen-separated identifiers.`);
|
|
659
|
+
}
|
|
660
|
+
if (!isText(entry.version) || !SEMVER_PATTERN2.test(entry.version)) {
|
|
661
|
+
errors.push(`${prefix}.version must be a valid semantic version.`);
|
|
662
|
+
}
|
|
663
|
+
for (const key of ["name", "description", "category", "language"]) {
|
|
664
|
+
if (!isText(entry[key])) errors.push(`${prefix}.${key} must be a non-empty string.`);
|
|
665
|
+
}
|
|
666
|
+
if (!Array.isArray(entry.tags) || !entry.tags.every((tag) => isText(tag))) {
|
|
667
|
+
errors.push(`${prefix}.tags must be an array of non-empty strings.`);
|
|
668
|
+
} else if (new Set(entry.tags).size !== entry.tags.length) {
|
|
669
|
+
errors.push(`${prefix}.tags must not contain duplicate entries.`);
|
|
670
|
+
}
|
|
671
|
+
if (!isRecord2(entry.archive)) {
|
|
672
|
+
errors.push(`${prefix}.archive must be an object.`);
|
|
673
|
+
} else {
|
|
674
|
+
hasOnlyKeys(entry.archive, ARCHIVE_KEYS, `${prefix}.archive`, errors);
|
|
675
|
+
if (!isHttpsUrl(entry.archive.url)) {
|
|
676
|
+
errors.push(`${prefix}.archive.url must be an HTTPS URL without credentials or a fragment.`);
|
|
677
|
+
}
|
|
678
|
+
if (typeof entry.archive.sha256 !== "string" || !SHA256_PATTERN.test(entry.archive.sha256)) {
|
|
679
|
+
errors.push(`${prefix}.archive.sha256 must be a lowercase 64-character SHA-256 digest.`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
if (typeof entry.id === "string" && typeof entry.version === "string" && ID_PATTERN2.test(entry.id) && SEMVER_PATTERN2.test(entry.version)) {
|
|
683
|
+
const identity = `${entry.id}@${entry.version}`;
|
|
684
|
+
if (identities.has(identity)) {
|
|
685
|
+
errors.push(`${prefix} duplicates template ${identity}.`);
|
|
686
|
+
}
|
|
687
|
+
identities.add(identity);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
if (errors.length > 0) return { valid: false, errors };
|
|
691
|
+
return {
|
|
692
|
+
valid: true,
|
|
693
|
+
errors: [],
|
|
694
|
+
catalog: value
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// ../kernel/src/plan/execution-plan-validator.ts
|
|
699
|
+
var phaseOrder = new Map(LIFECYCLE_ORDER.map((phase, index) => [phase, index]));
|
|
700
|
+
|
|
701
|
+
// ../os/src/engine/validation/validate-execution-intent.ts
|
|
702
|
+
var EXECUTION_KINDS = /* @__PURE__ */ new Set(["scaffold", "generate", "preview", "validate"]);
|
|
703
|
+
var INVOCATION_MODES = /* @__PURE__ */ new Set(["user", "author", "ci", "system"]);
|
|
704
|
+
var ARTIFACT_KINDS = /* @__PURE__ */ new Set([
|
|
705
|
+
"preset",
|
|
706
|
+
"module",
|
|
707
|
+
"project",
|
|
708
|
+
"template",
|
|
709
|
+
"capability",
|
|
710
|
+
"composition"
|
|
711
|
+
]);
|
|
712
|
+
function isRecord3(value) {
|
|
713
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
714
|
+
const prototype = Object.getPrototypeOf(value);
|
|
715
|
+
return prototype === Object.prototype || prototype === null;
|
|
716
|
+
}
|
|
717
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
718
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
719
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
720
|
+
if (typeof value !== "object") return false;
|
|
721
|
+
if (seen.has(value)) return false;
|
|
722
|
+
seen.add(value);
|
|
723
|
+
if (Array.isArray(value)) {
|
|
724
|
+
const valid2 = value.every((entry) => isJsonValue(entry, seen));
|
|
725
|
+
seen.delete(value);
|
|
726
|
+
return valid2;
|
|
727
|
+
}
|
|
728
|
+
if (!isRecord3(value)) return false;
|
|
729
|
+
const valid = Object.values(value).every((entry) => isJsonValue(entry, seen));
|
|
730
|
+
seen.delete(value);
|
|
731
|
+
return valid;
|
|
732
|
+
}
|
|
733
|
+
function validateExecutionIntent(input) {
|
|
734
|
+
const diagnostics = [];
|
|
735
|
+
const add = (code, message, field) => {
|
|
736
|
+
diagnostics.push({ code, message, ...field ? { field } : {} });
|
|
737
|
+
};
|
|
738
|
+
if (!isRecord3(input)) {
|
|
739
|
+
add("INTENT_INVALID_SHAPE", "Execution intent must be a plain object.");
|
|
740
|
+
return Object.freeze({ ok: false, diagnostics: Object.freeze(diagnostics) });
|
|
741
|
+
}
|
|
742
|
+
if (typeof input.executionKind !== "string" || !EXECUTION_KINDS.has(input.executionKind)) {
|
|
743
|
+
add("INTENT_INVALID_EXECUTION_KIND", "executionKind must be scaffold, generate, preview, or validate.", "executionKind");
|
|
744
|
+
}
|
|
745
|
+
if (typeof input.executionMode !== "string" || !INVOCATION_MODES.has(input.executionMode)) {
|
|
746
|
+
add("INTENT_INVALID_INVOCATION_MODE", "executionMode must be user, author, ci, or system.", "executionMode");
|
|
747
|
+
}
|
|
748
|
+
if (typeof input.artifactKind !== "string" || !ARTIFACT_KINDS.has(input.artifactKind)) {
|
|
749
|
+
add("INTENT_INVALID_ARTIFACT_KIND", "artifactKind must be a supported AppInit artifact kind.", "artifactKind");
|
|
750
|
+
}
|
|
751
|
+
if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0) {
|
|
752
|
+
add("INTENT_ROOT_REQUIRED", "rootDir must be a non-empty string.", "rootDir");
|
|
753
|
+
}
|
|
754
|
+
const kind = input.executionKind;
|
|
755
|
+
if ((kind === "scaffold" || kind === "generate") && (typeof input.targetDir !== "string" || input.targetDir.trim().length === 0)) {
|
|
756
|
+
add("INTENT_TARGET_REQUIRED", `targetDir is required for executionKind "${kind}".`, "targetDir");
|
|
757
|
+
}
|
|
758
|
+
if ((kind === "scaffold" || kind === "generate" || kind === "preview") && (typeof input.templateRef !== "string" || input.templateRef.trim().length === 0)) {
|
|
759
|
+
add("INTENT_TEMPLATE_REQUIRED", `templateRef is required for executionKind "${kind}".`, "templateRef");
|
|
760
|
+
}
|
|
761
|
+
if (input.variables !== void 0 && (!isRecord3(input.variables) || !isJsonValue(input.variables))) {
|
|
762
|
+
add("INTENT_VARIABLES_INVALID", "variables must be a plain JSON-serializable object.", "variables");
|
|
763
|
+
}
|
|
764
|
+
if (input.flags !== void 0) {
|
|
765
|
+
if (!isRecord3(input.flags)) {
|
|
766
|
+
add("INTENT_FLAGS_INVALID", "flags must be a plain object.", "flags");
|
|
767
|
+
} else {
|
|
768
|
+
for (const key of ["dryRun", "debug", "skipInstall", "watch", "author"]) {
|
|
769
|
+
if (input.flags[key] !== void 0 && typeof input.flags[key] !== "boolean") {
|
|
770
|
+
add("INTENT_FLAGS_INVALID", `flags.${key} must be a boolean when provided.`, `flags.${key}`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const unknownFlags = Object.keys(input.flags).filter(
|
|
774
|
+
(key) => !["dryRun", "debug", "skipInstall", "watch", "author"].includes(key)
|
|
775
|
+
);
|
|
776
|
+
for (const key of unknownFlags) {
|
|
777
|
+
add("INTENT_FLAGS_INVALID", `Unknown execution flag "${key}".`, `flags.${key}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (diagnostics.length > 0) {
|
|
782
|
+
return Object.freeze({ ok: false, diagnostics: Object.freeze(diagnostics) });
|
|
783
|
+
}
|
|
784
|
+
return Object.freeze({
|
|
785
|
+
ok: true,
|
|
786
|
+
diagnostics: Object.freeze([]),
|
|
787
|
+
intent: input
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// ../os/src/engine/render/filters.ts
|
|
792
|
+
function registerAppInitFilters(engine2) {
|
|
793
|
+
engine2.registerFilter(
|
|
794
|
+
"kebab",
|
|
795
|
+
(value) => String(value).trim().replace(/\s+/g, "-").toLowerCase()
|
|
796
|
+
);
|
|
797
|
+
engine2.registerFilter(
|
|
798
|
+
"camel",
|
|
799
|
+
(value) => String(value).replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())
|
|
800
|
+
);
|
|
801
|
+
engine2.registerFilter("json", (value) => JSON.stringify(value, null, 2));
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ../os/src/engine/render/render-liquid.ts
|
|
805
|
+
import { Liquid } from "liquidjs";
|
|
806
|
+
var engine = new Liquid({
|
|
807
|
+
strictVariables: true,
|
|
808
|
+
strictFilters: true,
|
|
809
|
+
ownPropertyOnly: true,
|
|
810
|
+
lenientIf: false
|
|
811
|
+
});
|
|
812
|
+
registerAppInitFilters(engine);
|
|
813
|
+
async function renderLiquid(template, context) {
|
|
814
|
+
return engine.parseAndRender(template, context);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// ../os/src/engine/render/render-with-engine.ts
|
|
818
|
+
function getPath(context, key) {
|
|
819
|
+
return key.split(".").reduce((value, segment) => {
|
|
820
|
+
if (value === null || typeof value !== "object" || !Object.prototype.hasOwnProperty.call(value, segment)) {
|
|
821
|
+
return void 0;
|
|
822
|
+
}
|
|
823
|
+
return value[segment];
|
|
824
|
+
}, context);
|
|
825
|
+
}
|
|
826
|
+
function assertNoLiquidSyntax(content, filePath, engine2) {
|
|
827
|
+
if (/\{\{|\{%|\{\#/s.test(content)) {
|
|
828
|
+
throw new Error(`Liquid syntax is not allowed in ${filePath} when renderer is "${engine2}".`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function replaceDatPlaceholders(content, variables) {
|
|
832
|
+
return content.replace(/%%\s*([\w.-]+)\s*%%/g, (_match, key) => {
|
|
833
|
+
const value = getPath(variables, key);
|
|
834
|
+
return value == null ? "" : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
async function renderWithEngine(engine2, template, variables, filePath = "<virtual>") {
|
|
838
|
+
if (engine2 === "liquid") return replaceDatPlaceholders(await renderLiquid(template, variables), variables);
|
|
839
|
+
if (engine2 === "none") {
|
|
840
|
+
assertNoLiquidSyntax(template, filePath, engine2);
|
|
841
|
+
return replaceDatPlaceholders(template, variables);
|
|
842
|
+
}
|
|
843
|
+
throw new Error(`Unsupported template engine: ${engine2}`);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// ../os/src/engine/render/render-file.ts
|
|
847
|
+
async function renderFile(options) {
|
|
848
|
+
const { file, engine: engine2, variables } = options;
|
|
849
|
+
if (file.binary) {
|
|
850
|
+
if (!Buffer.isBuffer(file.content)) throw new Error(`Binary template file "${file.dest}" must contain Buffer data.`);
|
|
851
|
+
return { dest: file.dest, content: Buffer.from(file.content) };
|
|
852
|
+
}
|
|
853
|
+
if (typeof file.content !== "string") {
|
|
854
|
+
throw new Error(`Text template file "${file.dest}" must contain string data.`);
|
|
855
|
+
}
|
|
856
|
+
if (!file.render) return { dest: file.dest, content: file.content };
|
|
857
|
+
return {
|
|
858
|
+
dest: file.dest,
|
|
859
|
+
content: await renderWithEngine(engine2, file.content, variables, file.dest)
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// ../os/src/engine/render/validate-variables.ts
|
|
864
|
+
function validateTemplateVariables(variables, schema = {}) {
|
|
865
|
+
const errors = [];
|
|
866
|
+
if (variables === null || typeof variables !== "object" || Array.isArray(variables) || Object.getPrototypeOf(variables) !== Object.prototype && Object.getPrototypeOf(variables) !== null) {
|
|
867
|
+
return { valid: false, errors: ["Variables must be a plain object."] };
|
|
868
|
+
}
|
|
869
|
+
const values = variables;
|
|
870
|
+
for (const [key, rule] of Object.entries(schema)) {
|
|
871
|
+
const value = values[key];
|
|
872
|
+
if (value === void 0 || value === null) {
|
|
873
|
+
if (rule.required) errors.push(`"${key}" is required.`);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
const validType = rule.type === "array" && Array.isArray(value) || rule.type === "object" && value !== null && typeof value === "object" && !Array.isArray(value) || rule.type !== "array" && rule.type !== "object" && typeof value === rule.type;
|
|
877
|
+
if (!validType) {
|
|
878
|
+
errors.push(`"${key}" must be a ${rule.type}.`);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (typeof value === "string") {
|
|
882
|
+
if (rule.minLength !== void 0 && value.length < rule.minLength) errors.push(`"${key}" must contain at least ${rule.minLength} characters.`);
|
|
883
|
+
if (rule.maxLength !== void 0 && value.length > rule.maxLength) errors.push(`"${key}" must contain at most ${rule.maxLength} characters.`);
|
|
884
|
+
}
|
|
885
|
+
if (rule.enum && !rule.enum.some((allowed) => Object.is(allowed, value))) {
|
|
886
|
+
errors.push(`"${key}" must be one of: ${rule.enum.join(", ")}.`);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return Object.freeze({ valid: errors.length === 0, errors: Object.freeze(errors) });
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// ../os/src/engine/verify-generated-output.ts
|
|
893
|
+
import { lstat as lstat3, readFile, readdir as readdir2, realpath as realpath2 } from "fs/promises";
|
|
894
|
+
import path6 from "path";
|
|
895
|
+
function isWithin(parent, child) {
|
|
896
|
+
const relative = path6.relative(parent, child);
|
|
897
|
+
return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative);
|
|
898
|
+
}
|
|
899
|
+
function sameContent(actual, expected) {
|
|
900
|
+
const expectedBytes = Buffer.isBuffer(expected) ? expected : Buffer.from(expected, "utf8");
|
|
901
|
+
return actual.equals(expectedBytes);
|
|
902
|
+
}
|
|
903
|
+
async function verifyGeneratedOutput(targetDirectory, operations, options = {}) {
|
|
904
|
+
const diagnostics = [];
|
|
905
|
+
const expected = /* @__PURE__ */ new Map();
|
|
906
|
+
for (const operation of operations) {
|
|
907
|
+
try {
|
|
908
|
+
const destination = normalizeVfsDestination(operation.destination);
|
|
909
|
+
if (expected.has(destination)) {
|
|
910
|
+
diagnostics.push({
|
|
911
|
+
code: "VERIFY_EXPECTED_PATH_DUPLICATE",
|
|
912
|
+
destination,
|
|
913
|
+
message: `Execution plan contains duplicate expected path "${destination}".`
|
|
914
|
+
});
|
|
915
|
+
} else {
|
|
916
|
+
expected.set(destination, operation);
|
|
917
|
+
}
|
|
918
|
+
} catch (error) {
|
|
919
|
+
diagnostics.push({
|
|
920
|
+
code: "VERIFY_EXPECTED_PATH_INVALID",
|
|
921
|
+
destination: operation.destination,
|
|
922
|
+
message: error instanceof Error ? error.message : String(error)
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
let root;
|
|
927
|
+
try {
|
|
928
|
+
const info = await lstat3(targetDirectory);
|
|
929
|
+
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
930
|
+
throw new Error("Target directory must be a real directory, not a symlink or file.");
|
|
931
|
+
}
|
|
932
|
+
root = await realpath2(targetDirectory);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
diagnostics.push({
|
|
935
|
+
code: "VERIFY_TARGET_MISSING",
|
|
936
|
+
message: error instanceof Error ? error.message : String(error)
|
|
937
|
+
});
|
|
938
|
+
return Object.freeze({
|
|
939
|
+
ok: false,
|
|
940
|
+
targetDirectory,
|
|
941
|
+
expectedFiles: Object.freeze([...expected.keys()].sort()),
|
|
942
|
+
actualFiles: Object.freeze([]),
|
|
943
|
+
diagnostics: Object.freeze(diagnostics)
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
const actual = /* @__PURE__ */ new Set();
|
|
947
|
+
async function walk2(directory, relativeDirectory) {
|
|
948
|
+
let entries;
|
|
949
|
+
try {
|
|
950
|
+
entries = await readdir2(directory, { withFileTypes: true });
|
|
951
|
+
} catch (error) {
|
|
952
|
+
diagnostics.push({
|
|
953
|
+
code: "VERIFY_READ_FAILED",
|
|
954
|
+
destination: relativeDirectory || void 0,
|
|
955
|
+
message: error instanceof Error ? error.message : String(error)
|
|
956
|
+
});
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
960
|
+
for (const entry of entries) {
|
|
961
|
+
const relative = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
|
|
962
|
+
const absolute = path6.join(directory, entry.name);
|
|
963
|
+
let info;
|
|
964
|
+
try {
|
|
965
|
+
info = await lstat3(absolute);
|
|
966
|
+
} catch (error) {
|
|
967
|
+
diagnostics.push({
|
|
968
|
+
code: "VERIFY_READ_FAILED",
|
|
969
|
+
destination: relative,
|
|
970
|
+
message: error instanceof Error ? error.message : String(error)
|
|
971
|
+
});
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
if (info.isSymbolicLink()) {
|
|
975
|
+
diagnostics.push({
|
|
976
|
+
code: "VERIFY_SYMLINK_DETECTED",
|
|
977
|
+
destination: relative,
|
|
978
|
+
message: `Generated output contains a symbolic link at "${relative}".`
|
|
979
|
+
});
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (info.isDirectory()) {
|
|
983
|
+
const canonical = await realpath2(absolute);
|
|
984
|
+
if (!isWithin(root, canonical)) {
|
|
985
|
+
diagnostics.push({
|
|
986
|
+
code: "VERIFY_TARGET_INVALID",
|
|
987
|
+
destination: relative,
|
|
988
|
+
message: `Directory "${relative}" resolves outside the output root.`
|
|
989
|
+
});
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
await walk2(absolute, relative);
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
if (!info.isFile()) {
|
|
996
|
+
diagnostics.push({
|
|
997
|
+
code: "VERIFY_FILE_TYPE_MISMATCH",
|
|
998
|
+
destination: relative,
|
|
999
|
+
message: `Generated path "${relative}" is not a regular file.`
|
|
1000
|
+
});
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
actual.add(relative);
|
|
1004
|
+
const operation = expected.get(relative);
|
|
1005
|
+
if (!operation) {
|
|
1006
|
+
if (options.rejectUnexpectedFiles !== false) {
|
|
1007
|
+
diagnostics.push({
|
|
1008
|
+
code: "VERIFY_UNEXPECTED_FILE",
|
|
1009
|
+
destination: relative,
|
|
1010
|
+
message: `Unexpected file "${relative}" exists in generated output.`
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
const content = await readFile(absolute);
|
|
1017
|
+
if (!sameContent(content, operation.content)) {
|
|
1018
|
+
diagnostics.push({
|
|
1019
|
+
code: "VERIFY_CONTENT_MISMATCH",
|
|
1020
|
+
destination: relative,
|
|
1021
|
+
message: `Generated file "${relative}" does not match the rendered execution plan.`
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
diagnostics.push({
|
|
1026
|
+
code: "VERIFY_READ_FAILED",
|
|
1027
|
+
destination: relative,
|
|
1028
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
await walk2(root, "");
|
|
1034
|
+
for (const destination of expected.keys()) {
|
|
1035
|
+
if (!actual.has(destination)) {
|
|
1036
|
+
diagnostics.push({
|
|
1037
|
+
code: "VERIFY_FILE_MISSING",
|
|
1038
|
+
destination,
|
|
1039
|
+
message: `Expected generated file "${destination}" is missing.`
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return Object.freeze({
|
|
1044
|
+
ok: diagnostics.length === 0,
|
|
1045
|
+
targetDirectory: root,
|
|
1046
|
+
expectedFiles: Object.freeze([...expected.keys()].sort()),
|
|
1047
|
+
actualFiles: Object.freeze([...actual].sort()),
|
|
1048
|
+
diagnostics: Object.freeze(diagnostics)
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// ../os/src/state/appinit-state.ts
|
|
1053
|
+
import { randomUUID } from "crypto";
|
|
1054
|
+
import { mkdir as mkdir2, readFile as readFile2, realpath as realpath3, rename as rename2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
1055
|
+
import { homedir } from "os";
|
|
1056
|
+
import path7 from "path";
|
|
1057
|
+
function isWithin2(parent, child) {
|
|
1058
|
+
const relative = path7.relative(parent, child);
|
|
1059
|
+
return relative === "" || !relative.startsWith(`..${path7.sep}`) && relative !== ".." && !path7.isAbsolute(relative);
|
|
1060
|
+
}
|
|
1061
|
+
function resolveAppInitHome(options = {}) {
|
|
1062
|
+
const env = options.env ?? process.env;
|
|
1063
|
+
const configuredHome = env.APPINIT_HOME;
|
|
1064
|
+
if (configuredHome !== void 0) {
|
|
1065
|
+
if (configuredHome.trim().length === 0) {
|
|
1066
|
+
throw new Error("APPINIT_HOME must not be empty when set.");
|
|
1067
|
+
}
|
|
1068
|
+
return path7.resolve(configuredHome);
|
|
1069
|
+
}
|
|
1070
|
+
return path7.resolve(options.homeDirectory ?? homedir(), ".appinit");
|
|
1071
|
+
}
|
|
1072
|
+
async function ensureAppInitHome(homeDirectory = resolveAppInitHome()) {
|
|
1073
|
+
const resolvedHome = path7.resolve(homeDirectory);
|
|
1074
|
+
await mkdir2(resolvedHome, { recursive: true, mode: 448 });
|
|
1075
|
+
return realpath3(resolvedHome);
|
|
1076
|
+
}
|
|
1077
|
+
function resolveAppInitPath(homeDirectory, relativePath) {
|
|
1078
|
+
if (typeof relativePath !== "string" || relativePath.length === 0 || relativePath.includes("\\") || relativePath.includes("\0") || relativePath.startsWith("/") || relativePath.split("/").some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
|
|
1079
|
+
throw new Error("AppInit state path must be a safe, non-empty relative path.");
|
|
1080
|
+
}
|
|
1081
|
+
const root = path7.resolve(homeDirectory);
|
|
1082
|
+
const target = path7.resolve(root, ...relativePath.split("/"));
|
|
1083
|
+
if (!isWithin2(root, target) || root === target) {
|
|
1084
|
+
throw new Error("AppInit state path must remain inside the global state directory.");
|
|
1085
|
+
}
|
|
1086
|
+
return target;
|
|
1087
|
+
}
|
|
1088
|
+
async function resolveExistingStateFile(homeDirectory, relativePath) {
|
|
1089
|
+
const candidate = resolveAppInitPath(homeDirectory, relativePath);
|
|
1090
|
+
const root = await realpath3(homeDirectory);
|
|
1091
|
+
const resolved = await realpath3(candidate);
|
|
1092
|
+
if (!isWithin2(root, resolved) || root === resolved) {
|
|
1093
|
+
throw new Error("AppInit state path resolves outside the global state directory.");
|
|
1094
|
+
}
|
|
1095
|
+
return resolved;
|
|
1096
|
+
}
|
|
1097
|
+
async function readAppInitJson(homeDirectory, relativePath) {
|
|
1098
|
+
let contents;
|
|
1099
|
+
try {
|
|
1100
|
+
const filePath = await resolveExistingStateFile(homeDirectory, relativePath);
|
|
1101
|
+
contents = await readFile2(filePath, "utf8");
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
if (error.code === "ENOENT") return void 0;
|
|
1104
|
+
throw error;
|
|
1105
|
+
}
|
|
1106
|
+
try {
|
|
1107
|
+
return JSON.parse(contents);
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
throw new Error("AppInit state file contains invalid JSON.", { cause: error });
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
async function ensureStateParent(homeDirectory, relativePath) {
|
|
1113
|
+
const root = await ensureAppInitHome(homeDirectory);
|
|
1114
|
+
const segments = relativePath.split("/");
|
|
1115
|
+
let current = root;
|
|
1116
|
+
for (const segment of segments.slice(0, -1)) {
|
|
1117
|
+
current = path7.join(current, segment);
|
|
1118
|
+
await mkdir2(current, { recursive: false, mode: 448 }).catch((error) => {
|
|
1119
|
+
if (error.code !== "EEXIST") throw error;
|
|
1120
|
+
});
|
|
1121
|
+
const resolved = await realpath3(current);
|
|
1122
|
+
if (!isWithin2(root, resolved) || root === resolved) {
|
|
1123
|
+
throw new Error("AppInit state path resolves outside the global state directory.");
|
|
1124
|
+
}
|
|
1125
|
+
current = resolved;
|
|
1126
|
+
}
|
|
1127
|
+
return current;
|
|
1128
|
+
}
|
|
1129
|
+
async function writeAppInitJson(homeDirectory, relativePath, value) {
|
|
1130
|
+
const candidate = resolveAppInitPath(homeDirectory, relativePath);
|
|
1131
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
1132
|
+
if (serialized === void 0) {
|
|
1133
|
+
throw new Error("AppInit state value must be JSON-serializable.");
|
|
1134
|
+
}
|
|
1135
|
+
const directory = await ensureStateParent(homeDirectory, relativePath);
|
|
1136
|
+
const filePath = path7.join(directory, path7.basename(candidate));
|
|
1137
|
+
const temporaryPath = path7.join(directory, `.${path7.basename(filePath)}.${randomUUID()}.tmp`);
|
|
1138
|
+
try {
|
|
1139
|
+
await writeFile2(temporaryPath, `${serialized}
|
|
1140
|
+
`, {
|
|
1141
|
+
encoding: "utf8",
|
|
1142
|
+
flag: "wx",
|
|
1143
|
+
mode: 384
|
|
1144
|
+
});
|
|
1145
|
+
await rename2(temporaryPath, filePath);
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
await rm2(temporaryPath, { force: true }).catch(() => void 0);
|
|
1148
|
+
throw error;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// ../os/src/state/appinit-state-schema.ts
|
|
1153
|
+
function isRecord4(value) {
|
|
1154
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1155
|
+
}
|
|
1156
|
+
function isNonEmptyString2(value) {
|
|
1157
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1158
|
+
}
|
|
1159
|
+
function validateAppInitState(value) {
|
|
1160
|
+
const errors = [];
|
|
1161
|
+
if (!isRecord4(value)) {
|
|
1162
|
+
return { valid: false, errors: ["State must be a JSON object."] };
|
|
1163
|
+
}
|
|
1164
|
+
const allowedKeys = ["schemaVersion", "projects", "lastUpdateCheck", "latestVersion"];
|
|
1165
|
+
if (Object.keys(value).some((key) => !allowedKeys.includes(key))) {
|
|
1166
|
+
errors.push("State contains unknown fields.");
|
|
1167
|
+
}
|
|
1168
|
+
const isLegacyUpdateState = !Object.prototype.hasOwnProperty.call(value, "schemaVersion") && !Object.prototype.hasOwnProperty.call(value, "projects") && (Object.prototype.hasOwnProperty.call(value, "lastUpdateCheck") || Object.prototype.hasOwnProperty.call(value, "latestVersion"));
|
|
1169
|
+
const unsupportedVersion = !isLegacyUpdateState && typeof value.schemaVersion === "number" && Number.isInteger(value.schemaVersion) && value.schemaVersion > 1 ? value.schemaVersion : void 0;
|
|
1170
|
+
if (!isLegacyUpdateState && value.schemaVersion !== 1) {
|
|
1171
|
+
errors.push(unsupportedVersion === void 0 ? "schemaVersion must be 1." : `State uses unsupported newer schema version ${unsupportedVersion}; this AppInit version supports schema version 1.`);
|
|
1172
|
+
}
|
|
1173
|
+
const rawProjects = isLegacyUpdateState ? [] : value.projects;
|
|
1174
|
+
if (!Array.isArray(rawProjects)) {
|
|
1175
|
+
errors.push("projects must be an array.");
|
|
1176
|
+
} else {
|
|
1177
|
+
rawProjects.forEach((project, index) => {
|
|
1178
|
+
if (!isRecord4(project)) {
|
|
1179
|
+
errors.push(`projects[${index}] must be an object.`);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (Object.keys(project).some((key) => !["path", "templateId", "createdAt"].includes(key))) {
|
|
1183
|
+
errors.push(`projects[${index}] contains unknown fields.`);
|
|
1184
|
+
}
|
|
1185
|
+
if (!isNonEmptyString2(project.path)) {
|
|
1186
|
+
errors.push(`projects[${index}].path must be a non-empty string.`);
|
|
1187
|
+
}
|
|
1188
|
+
if (!isNonEmptyString2(project.templateId)) {
|
|
1189
|
+
errors.push(`projects[${index}].templateId must be a non-empty string.`);
|
|
1190
|
+
}
|
|
1191
|
+
if (!isNonEmptyString2(project.createdAt) || !Number.isFinite(Date.parse(project.createdAt))) {
|
|
1192
|
+
errors.push(`projects[${index}].createdAt must be a valid date string.`);
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
if (errors.length > 0) return { valid: false, errors, ...unsupportedVersion === void 0 ? {} : { unsupportedVersion } };
|
|
1197
|
+
return {
|
|
1198
|
+
valid: true,
|
|
1199
|
+
errors: [],
|
|
1200
|
+
state: {
|
|
1201
|
+
...value,
|
|
1202
|
+
schemaVersion: 1,
|
|
1203
|
+
projects: rawProjects
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/context.ts
|
|
1209
|
+
function isRecord5(value) {
|
|
1210
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1211
|
+
}
|
|
1212
|
+
async function readOptionalJson(filePath, diagnostics) {
|
|
1213
|
+
let source;
|
|
1214
|
+
try {
|
|
1215
|
+
source = await readFile3(filePath, "utf8");
|
|
1216
|
+
} catch (error) {
|
|
1217
|
+
if (error.code === "ENOENT") return void 0;
|
|
1218
|
+
diagnostics.push(`Could not read ${path8.basename(filePath)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1219
|
+
return void 0;
|
|
1220
|
+
}
|
|
1221
|
+
try {
|
|
1222
|
+
const value = JSON.parse(source);
|
|
1223
|
+
if (!isRecord5(value)) {
|
|
1224
|
+
diagnostics.push(`${path8.basename(filePath)} must contain a JSON object.`);
|
|
1225
|
+
return void 0;
|
|
1226
|
+
}
|
|
1227
|
+
return value;
|
|
1228
|
+
} catch {
|
|
1229
|
+
diagnostics.push(`${path8.basename(filePath)} contains invalid JSON.`);
|
|
1230
|
+
return void 0;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
function optionalString(record, key) {
|
|
1234
|
+
const value = record?.[key];
|
|
1235
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1236
|
+
}
|
|
1237
|
+
function samePath(left, right) {
|
|
1238
|
+
const a = path8.resolve(left);
|
|
1239
|
+
const b = path8.resolve(right);
|
|
1240
|
+
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
1241
|
+
}
|
|
1242
|
+
async function readProjectContext(cwd = process.cwd()) {
|
|
1243
|
+
const rootDir = path8.resolve(cwd);
|
|
1244
|
+
const diagnostics = [];
|
|
1245
|
+
const appinitDirectory = path8.join(rootDir, ".appinit");
|
|
1246
|
+
const [packageJson, project, template, context] = await Promise.all([
|
|
1247
|
+
readOptionalJson(path8.join(rootDir, "package.json"), diagnostics),
|
|
1248
|
+
readOptionalJson(path8.join(appinitDirectory, "project.json"), diagnostics),
|
|
1249
|
+
readOptionalJson(path8.join(appinitDirectory, "template.json"), diagnostics),
|
|
1250
|
+
readOptionalJson(path8.join(appinitDirectory, "context.json"), diagnostics)
|
|
1251
|
+
]);
|
|
1252
|
+
let history;
|
|
1253
|
+
try {
|
|
1254
|
+
const value = await readAppInitJson(resolveAppInitHome(), "state.json");
|
|
1255
|
+
if (value !== void 0) {
|
|
1256
|
+
const validation = validateAppInitState(value);
|
|
1257
|
+
if (!validation.valid || !validation.state) {
|
|
1258
|
+
diagnostics.push(`Global AppInit state is invalid: ${validation.errors.join(" ")}`);
|
|
1259
|
+
} else {
|
|
1260
|
+
history = validation.state.projects.find((entry) => samePath(entry.path, rootDir));
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
diagnostics.push(`Global AppInit state could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
1265
|
+
}
|
|
1266
|
+
const packageInfo = packageJson ? {
|
|
1267
|
+
...optionalString(packageJson, "name") ? { name: optionalString(packageJson, "name") } : {},
|
|
1268
|
+
...optionalString(packageJson, "version") ? { version: optionalString(packageJson, "version") } : {}
|
|
1269
|
+
} : void 0;
|
|
1270
|
+
const appinit = project || template || context ? {
|
|
1271
|
+
...project ? { project } : {},
|
|
1272
|
+
...template ? { template } : {},
|
|
1273
|
+
...context ? { context } : {}
|
|
1274
|
+
} : void 0;
|
|
1275
|
+
return Object.freeze({
|
|
1276
|
+
rootDir,
|
|
1277
|
+
...packageInfo ? { package: Object.freeze(packageInfo) } : {},
|
|
1278
|
+
...appinit ? { appinit: Object.freeze(appinit) } : {},
|
|
1279
|
+
...history ? { history } : {},
|
|
1280
|
+
diagnostics: Object.freeze(diagnostics)
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
async function runContextCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
1284
|
+
const report = await readProjectContext(options.cwd);
|
|
1285
|
+
if (options.json) {
|
|
1286
|
+
io.write(`${JSON.stringify(report, null, 2)}
|
|
1287
|
+
`);
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
io.write("AppInit Project Context\n\n");
|
|
1291
|
+
io.write(`Project: ${report.package?.name ?? report.appinit?.project?.name ?? path8.basename(report.rootDir)}
|
|
1292
|
+
`);
|
|
1293
|
+
io.write(`Path: ${report.rootDir}
|
|
1294
|
+
`);
|
|
1295
|
+
if (report.package?.version) io.write(`Version: ${report.package.version}
|
|
1296
|
+
`);
|
|
1297
|
+
const project = report.appinit?.project;
|
|
1298
|
+
if (project) {
|
|
1299
|
+
io.write("\nProject metadata (.appinit/project.json)\n");
|
|
1300
|
+
for (const key of ["name", "createdAt", "cliVersion", "atsVersion"]) {
|
|
1301
|
+
const value = optionalString(project, key);
|
|
1302
|
+
if (value) io.write(` ${key}: ${value}
|
|
1303
|
+
`);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const template = report.appinit?.template;
|
|
1307
|
+
if (template) {
|
|
1308
|
+
io.write("\nTemplate metadata (.appinit/template.json)\n");
|
|
1309
|
+
for (const key of ["templateId", "version", "source", "installedAt"]) {
|
|
1310
|
+
const value = optionalString(template, key);
|
|
1311
|
+
if (value) io.write(` ${key}: ${value}
|
|
1312
|
+
`);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
const context = report.appinit?.context;
|
|
1316
|
+
if (context) {
|
|
1317
|
+
io.write("\nDeclared context (.appinit/context.json)\n");
|
|
1318
|
+
for (const key of ["artifactKind", "runtime", "framework", "atsVersion"]) {
|
|
1319
|
+
const value = optionalString(context, key);
|
|
1320
|
+
if (value) io.write(` ${key}: ${value}
|
|
1321
|
+
`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (report.history) {
|
|
1325
|
+
io.write("\nGlobal project history\n");
|
|
1326
|
+
io.write(` Template: ${report.history.templateId}
|
|
1327
|
+
`);
|
|
1328
|
+
io.write(` Created: ${report.history.createdAt}
|
|
1329
|
+
`);
|
|
1330
|
+
} else {
|
|
1331
|
+
io.write("\nGlobal project history: no matching entry\n");
|
|
1332
|
+
}
|
|
1333
|
+
if (!report.appinit) io.write("\nNo .appinit project metadata found.\n");
|
|
1334
|
+
if (report.diagnostics.length) {
|
|
1335
|
+
io.write("\nDiagnostics\n");
|
|
1336
|
+
for (const diagnostic3 of report.diagnostics) io.write(` ! ${diagnostic3}
|
|
1337
|
+
`);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// src/create.ts
|
|
1342
|
+
import { lstat as lstat6, readdir as readdir4, readFile as readFile9, realpath as realpath7 } from "fs/promises";
|
|
1343
|
+
import path16 from "path";
|
|
1344
|
+
|
|
1345
|
+
// ../sdk/src/resolver/resolve-template.ts
|
|
1346
|
+
import { realpath as realpath4, stat, readFile as readFile5 } from "fs/promises";
|
|
1347
|
+
import path10 from "path";
|
|
1348
|
+
|
|
1349
|
+
// ../sdk/src/resolver/template-vfs.ts
|
|
1350
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1351
|
+
import path9 from "path";
|
|
1352
|
+
var IGNORED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "docs", "variables", "hooks"]);
|
|
1353
|
+
var STATIC_RENAMES = {
|
|
1354
|
+
_gitignore: ".gitignore",
|
|
1355
|
+
_npmrc: ".npmrc"
|
|
1356
|
+
};
|
|
1357
|
+
function normalizeTemplatePath(relativePath) {
|
|
1358
|
+
let dest = relativePath.replace(/\\/g, "/");
|
|
1359
|
+
let render = false;
|
|
1360
|
+
if (dest.endsWith("__tmpl")) {
|
|
1361
|
+
dest = dest.slice(0, -"__tmpl".length);
|
|
1362
|
+
render = true;
|
|
1363
|
+
} else if (dest.endsWith(".tmpl")) {
|
|
1364
|
+
dest = dest.slice(0, -".tmpl".length);
|
|
1365
|
+
render = true;
|
|
1366
|
+
}
|
|
1367
|
+
const segments = dest.split("/");
|
|
1368
|
+
const basename = segments.pop() ?? "";
|
|
1369
|
+
const renamed = STATIC_RENAMES[basename] ?? basename;
|
|
1370
|
+
const normalized = [...segments, renamed].filter(Boolean).join("/");
|
|
1371
|
+
return { dest: normalized, render };
|
|
1372
|
+
}
|
|
1373
|
+
async function createTemplateVFS(templateDirectory) {
|
|
1374
|
+
const root = await import("fs/promises").then(({ realpath: realpath9 }) => realpath9(templateDirectory));
|
|
1375
|
+
const entries = await readDirRecursive(root);
|
|
1376
|
+
const vfs = new VirtualFileSystem();
|
|
1377
|
+
for (const relativePath of entries) {
|
|
1378
|
+
const segments = relativePath.split("/");
|
|
1379
|
+
if (segments.some((segment) => IGNORED_SEGMENTS.has(segment)) || segments.at(-1) === ".DS_Store") continue;
|
|
1380
|
+
const { dest, render } = normalizeTemplatePath(relativePath);
|
|
1381
|
+
const absolutePath = path9.join(root, ...relativePath.split("/"));
|
|
1382
|
+
const binary = isBinaryFile(absolutePath);
|
|
1383
|
+
const content = binary ? await readFile4(absolutePath) : await readFile4(absolutePath, "utf8");
|
|
1384
|
+
vfs.add({ dest, content, render, binary });
|
|
1385
|
+
}
|
|
1386
|
+
return freezeVFS(vfs);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// ../sdk/src/resolver/resolve-template.ts
|
|
1390
|
+
function isWithin3(parent, child) {
|
|
1391
|
+
const relative = path10.relative(parent, child);
|
|
1392
|
+
return relative === "" || !relative.startsWith(`..${path10.sep}`) && relative !== ".." && !path10.isAbsolute(relative);
|
|
1393
|
+
}
|
|
1394
|
+
async function resolveTemplate(templateRoot, options = {}) {
|
|
1395
|
+
const root = await realpath4(path10.resolve(templateRoot));
|
|
1396
|
+
const rootInfo = await stat(root);
|
|
1397
|
+
if (!rootInfo.isDirectory()) {
|
|
1398
|
+
throw new Error(`Template root is not a directory: ${templateRoot}`);
|
|
1399
|
+
}
|
|
1400
|
+
const manifestFile = options.manifestFile ?? "appinit.template.json";
|
|
1401
|
+
if (path10.isAbsolute(manifestFile) || manifestFile.includes("\\") || manifestFile.split("/").some((part) => part === ".." || part === "." || part === "")) {
|
|
1402
|
+
throw new Error("Manifest filename must be a safe relative path.");
|
|
1403
|
+
}
|
|
1404
|
+
const manifestPath = await realpath4(path10.join(root, manifestFile));
|
|
1405
|
+
if (!isWithin3(root, manifestPath)) {
|
|
1406
|
+
throw new Error("Template manifest resolves outside the template root.");
|
|
1407
|
+
}
|
|
1408
|
+
const raw = await readFile5(manifestPath, "utf8");
|
|
1409
|
+
let value;
|
|
1410
|
+
try {
|
|
1411
|
+
value = JSON.parse(raw);
|
|
1412
|
+
} catch (error) {
|
|
1413
|
+
throw new Error(`Template manifest contains invalid JSON: ${manifestPath}`, { cause: error });
|
|
1414
|
+
}
|
|
1415
|
+
const validation = validateTemplateManifest(value);
|
|
1416
|
+
if (!validation.valid || !validation.manifest) {
|
|
1417
|
+
throw new Error(`Invalid template manifest: ${validation.errors.join(" ")}`);
|
|
1418
|
+
}
|
|
1419
|
+
const declaredTemplatePath = path10.resolve(root, ...validation.manifest.paths.template.split("/"));
|
|
1420
|
+
const templatePath = await realpath4(declaredTemplatePath);
|
|
1421
|
+
if (!isWithin3(root, templatePath)) {
|
|
1422
|
+
throw new Error("Declared template path resolves outside the template root.");
|
|
1423
|
+
}
|
|
1424
|
+
const templateInfo = await stat(templatePath);
|
|
1425
|
+
if (!templateInfo.isDirectory()) {
|
|
1426
|
+
throw new Error("Declared template path is not a directory.");
|
|
1427
|
+
}
|
|
1428
|
+
const vfs = await createTemplateVFS(templatePath);
|
|
1429
|
+
return Object.freeze({ root, manifest: validation.manifest, vfs });
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// ../sdk/src/resolver/discover-template.ts
|
|
1433
|
+
async function discoverTemplate(templateRef, options) {
|
|
1434
|
+
if (typeof templateRef !== "string" || templateRef.trim().length === 0) {
|
|
1435
|
+
throw new Error("Template reference must be a non-empty string.");
|
|
1436
|
+
}
|
|
1437
|
+
const root = options.templateRoots.get(templateRef);
|
|
1438
|
+
if (!root) {
|
|
1439
|
+
throw new Error(`Template "${templateRef}" is not present in the approved template roots.`);
|
|
1440
|
+
}
|
|
1441
|
+
const template = await resolveTemplate(root, {
|
|
1442
|
+
...options.manifestFile ? { manifestFile: options.manifestFile } : {}
|
|
1443
|
+
});
|
|
1444
|
+
const manifest = template.manifest;
|
|
1445
|
+
if (manifest.id === templateRef) {
|
|
1446
|
+
return Object.freeze({ template, matchedBy: "id" });
|
|
1447
|
+
}
|
|
1448
|
+
if (manifest.packageName === templateRef) {
|
|
1449
|
+
return Object.freeze({ template, matchedBy: "packageName" });
|
|
1450
|
+
}
|
|
1451
|
+
throw new Error(
|
|
1452
|
+
`Approved root for "${templateRef}" contains template "${manifest.id}" (${manifest.packageName}); identity mismatch.`
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// ../sdk/src/resolver/discover-template-catalog.ts
|
|
1457
|
+
import { lstat as lstat4, readdir as readdir3, realpath as realpath5 } from "fs/promises";
|
|
1458
|
+
import path11 from "path";
|
|
1459
|
+
|
|
1460
|
+
// ../sdk/src/resolver/variables.ts
|
|
1461
|
+
import { realpath as realpath6, readFile as readFile6, stat as stat2 } from "fs/promises";
|
|
1462
|
+
import path12 from "path";
|
|
1463
|
+
function isRecord6(value) {
|
|
1464
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1465
|
+
}
|
|
1466
|
+
function isWithin4(parent, child) {
|
|
1467
|
+
const relative = path12.relative(parent, child);
|
|
1468
|
+
return relative === "" || !relative.startsWith(`..${path12.sep}`) && relative !== ".." && !path12.isAbsolute(relative);
|
|
1469
|
+
}
|
|
1470
|
+
async function readJsonRecord(filePath) {
|
|
1471
|
+
let value;
|
|
1472
|
+
try {
|
|
1473
|
+
value = JSON.parse(await readFile6(filePath, "utf8"));
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
if (error.code === "ENOENT") throw error;
|
|
1476
|
+
throw new Error(`Unable to parse template variable file: ${filePath}`, { cause: error });
|
|
1477
|
+
}
|
|
1478
|
+
if (!isRecord6(value)) throw new Error(`Template variable file must contain a JSON object: ${filePath}`);
|
|
1479
|
+
return value;
|
|
1480
|
+
}
|
|
1481
|
+
async function loadTemplateVariables(templateRoot, manifest) {
|
|
1482
|
+
if (!manifest.paths.variables) return { defaults: {}, schema: {} };
|
|
1483
|
+
const root = await realpath6(path12.resolve(templateRoot));
|
|
1484
|
+
const directory = path12.resolve(root, ...manifest.paths.variables.split("/"));
|
|
1485
|
+
let variablesRoot;
|
|
1486
|
+
try {
|
|
1487
|
+
variablesRoot = await realpath6(directory);
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
if (error.code === "ENOENT") return { defaults: {}, schema: {} };
|
|
1490
|
+
throw error;
|
|
1491
|
+
}
|
|
1492
|
+
if (!isWithin4(root, variablesRoot)) {
|
|
1493
|
+
throw new Error("Declared variables directory resolves outside the template root.");
|
|
1494
|
+
}
|
|
1495
|
+
if (!(await stat2(variablesRoot)).isDirectory()) {
|
|
1496
|
+
throw new Error("Declared variables path is not a directory.");
|
|
1497
|
+
}
|
|
1498
|
+
const defaultsPath = path12.join(variablesRoot, "defaults.json");
|
|
1499
|
+
const schemaPath = path12.join(variablesRoot, "schema.json");
|
|
1500
|
+
const defaults = await readOptionalJson2(defaultsPath);
|
|
1501
|
+
const schemaRaw = await readOptionalJson2(schemaPath);
|
|
1502
|
+
const schema = {};
|
|
1503
|
+
for (const [key, rule] of Object.entries(schemaRaw)) {
|
|
1504
|
+
if (!isRecord6(rule)) throw new Error(`Variable schema entry "${key}" must be an object.`);
|
|
1505
|
+
if (rule.type !== void 0 && !["string", "number", "boolean", "object", "array"].includes(String(rule.type))) {
|
|
1506
|
+
throw new Error(`Variable schema entry "${key}" has an unsupported type.`);
|
|
1507
|
+
}
|
|
1508
|
+
if (rule.required !== void 0 && typeof rule.required !== "boolean") {
|
|
1509
|
+
throw new Error(`Variable schema entry "${key}".required must be boolean.`);
|
|
1510
|
+
}
|
|
1511
|
+
if (rule.minLength !== void 0 && (!Number.isInteger(rule.minLength) || Number(rule.minLength) < 0)) {
|
|
1512
|
+
throw new Error(`Variable schema entry "${key}".minLength must be a non-negative integer.`);
|
|
1513
|
+
}
|
|
1514
|
+
if (rule.enum !== void 0 && !Array.isArray(rule.enum)) {
|
|
1515
|
+
throw new Error(`Variable schema entry "${key}".enum must be an array.`);
|
|
1516
|
+
}
|
|
1517
|
+
schema[key] = rule;
|
|
1518
|
+
}
|
|
1519
|
+
return Object.freeze({
|
|
1520
|
+
defaults: Object.freeze(defaults),
|
|
1521
|
+
schema: Object.freeze(schema)
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
async function readOptionalJson2(filePath) {
|
|
1525
|
+
try {
|
|
1526
|
+
return await readJsonRecord(filePath);
|
|
1527
|
+
} catch (error) {
|
|
1528
|
+
if (error.code === "ENOENT") return {};
|
|
1529
|
+
throw error;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function validateTemplateVariables2(values, schema) {
|
|
1533
|
+
const errors = [];
|
|
1534
|
+
for (const [key, rule] of Object.entries(schema)) {
|
|
1535
|
+
const value = values[key];
|
|
1536
|
+
if (value === void 0 || value === null) {
|
|
1537
|
+
if (rule.required) errors.push(`Variable "${key}" is required.`);
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
if (rule.type) {
|
|
1541
|
+
const valid = rule.type === "array" ? Array.isArray(value) : rule.type === "object" ? isRecord6(value) : typeof value === rule.type;
|
|
1542
|
+
if (!valid || rule.type === "number" && !Number.isFinite(value)) {
|
|
1543
|
+
errors.push(`Variable "${key}" must be a valid ${rule.type}.`);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (rule.minLength !== void 0 && typeof value === "string" && value.length < rule.minLength) {
|
|
1548
|
+
errors.push(`Variable "${key}" must contain at least ${rule.minLength} characters.`);
|
|
1549
|
+
}
|
|
1550
|
+
if (rule.enum && !rule.enum.some((allowed) => Object.is(allowed, value))) {
|
|
1551
|
+
errors.push(`Variable "${key}" must be one of: ${rule.enum.map(String).join(", ")}.`);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
return errors;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// ../sdk/src/resolver/template-registry-client.ts
|
|
1558
|
+
import { createHash, randomUUID as randomUUID2, timingSafeEqual } from "crypto";
|
|
1559
|
+
import { mkdir as mkdir3, readFile as readFile7, rename as rename3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
1560
|
+
import path13 from "path";
|
|
1561
|
+
var DEFAULT_MAX_CATALOG_BYTES = 2 * 1024 * 1024;
|
|
1562
|
+
var DEFAULT_MAX_ARCHIVE_BYTES = 100 * 1024 * 1024;
|
|
1563
|
+
function assertHttpsUrl(value, label) {
|
|
1564
|
+
const url = new URL(value);
|
|
1565
|
+
if (url.protocol !== "https:" || url.username || url.password || url.hash) {
|
|
1566
|
+
throw new Error(`${label} must be an HTTPS URL without credentials or a fragment.`);
|
|
1567
|
+
}
|
|
1568
|
+
return url;
|
|
1569
|
+
}
|
|
1570
|
+
function safeSegment(value, label) {
|
|
1571
|
+
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(value)) {
|
|
1572
|
+
throw new Error(`Invalid ${label} for registry cache path.`);
|
|
1573
|
+
}
|
|
1574
|
+
return value;
|
|
1575
|
+
}
|
|
1576
|
+
async function readBoundedResponse(response, maxBytes, label) {
|
|
1577
|
+
if (!response.ok) throw new Error(`${label} request failed with HTTP ${response.status}.`);
|
|
1578
|
+
const declaredLength = Number(response.headers.get("content-length"));
|
|
1579
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
1580
|
+
throw new Error(`${label} exceeds the ${maxBytes}-byte size limit.`);
|
|
1581
|
+
}
|
|
1582
|
+
if (!response.body) return new Uint8Array();
|
|
1583
|
+
const reader = response.body.getReader();
|
|
1584
|
+
const chunks = [];
|
|
1585
|
+
let total = 0;
|
|
1586
|
+
try {
|
|
1587
|
+
while (true) {
|
|
1588
|
+
const { done, value } = await reader.read();
|
|
1589
|
+
if (done) break;
|
|
1590
|
+
total += value.byteLength;
|
|
1591
|
+
if (total > maxBytes) {
|
|
1592
|
+
await reader.cancel();
|
|
1593
|
+
throw new Error(`${label} exceeds the ${maxBytes}-byte size limit.`);
|
|
1594
|
+
}
|
|
1595
|
+
chunks.push(value);
|
|
1596
|
+
}
|
|
1597
|
+
} finally {
|
|
1598
|
+
reader.releaseLock();
|
|
1599
|
+
}
|
|
1600
|
+
const bytes = new Uint8Array(total);
|
|
1601
|
+
let offset = 0;
|
|
1602
|
+
for (const chunk of chunks) {
|
|
1603
|
+
bytes.set(chunk, offset);
|
|
1604
|
+
offset += chunk.byteLength;
|
|
1605
|
+
}
|
|
1606
|
+
return bytes;
|
|
1607
|
+
}
|
|
1608
|
+
function sha256(bytes) {
|
|
1609
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
1610
|
+
}
|
|
1611
|
+
function matchesDigest(actual, expected) {
|
|
1612
|
+
const actualBytes = Buffer.from(actual, "hex");
|
|
1613
|
+
const expectedBytes = Buffer.from(expected, "hex");
|
|
1614
|
+
return actualBytes.length === expectedBytes.length && timingSafeEqual(actualBytes, expectedBytes);
|
|
1615
|
+
}
|
|
1616
|
+
var TemplateRegistryClient = class {
|
|
1617
|
+
constructor(options) {
|
|
1618
|
+
this.options = options;
|
|
1619
|
+
assertHttpsUrl(options.catalogUrl, "Catalog URL");
|
|
1620
|
+
this.request = options.fetch ?? fetch;
|
|
1621
|
+
this.maxCatalogBytes = options.maxCatalogBytes ?? DEFAULT_MAX_CATALOG_BYTES;
|
|
1622
|
+
this.maxArchiveBytes = options.maxArchiveBytes ?? DEFAULT_MAX_ARCHIVE_BYTES;
|
|
1623
|
+
}
|
|
1624
|
+
request;
|
|
1625
|
+
maxCatalogBytes;
|
|
1626
|
+
maxArchiveBytes;
|
|
1627
|
+
async fetchCatalog() {
|
|
1628
|
+
const response = await this.request(this.options.catalogUrl, {
|
|
1629
|
+
method: "GET",
|
|
1630
|
+
redirect: "error",
|
|
1631
|
+
headers: { accept: "application/json" }
|
|
1632
|
+
});
|
|
1633
|
+
const bytes = await readBoundedResponse(response, this.maxCatalogBytes, "Registry catalog");
|
|
1634
|
+
let parsed;
|
|
1635
|
+
try {
|
|
1636
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
1637
|
+
} catch {
|
|
1638
|
+
throw new Error("Registry catalog response is not valid UTF-8 JSON.");
|
|
1639
|
+
}
|
|
1640
|
+
const result = validateTemplateRegistryCatalog(parsed);
|
|
1641
|
+
if (!result.valid || !result.catalog) {
|
|
1642
|
+
throw new Error(`Registry catalog validation failed: ${result.errors.join("; ")}`);
|
|
1643
|
+
}
|
|
1644
|
+
return result.catalog;
|
|
1645
|
+
}
|
|
1646
|
+
async downloadTemplate(entry) {
|
|
1647
|
+
safeSegment(entry.id, "template ID");
|
|
1648
|
+
safeSegment(entry.version, "template version");
|
|
1649
|
+
const archiveUrl = assertHttpsUrl(entry.archive.url, "Template archive URL");
|
|
1650
|
+
const cacheRoot = path13.resolve(this.options.cacheDirectory);
|
|
1651
|
+
const destination = path13.resolve(cacheRoot, entry.id, entry.version, "template.tar");
|
|
1652
|
+
if (!destination.startsWith(cacheRoot + path13.sep)) {
|
|
1653
|
+
throw new Error("Resolved template cache path escapes the configured cache directory.");
|
|
1654
|
+
}
|
|
1655
|
+
try {
|
|
1656
|
+
const existing = await readFile7(destination);
|
|
1657
|
+
const digest2 = sha256(existing);
|
|
1658
|
+
if (matchesDigest(digest2, entry.archive.sha256)) {
|
|
1659
|
+
return Object.freeze({ entry, path: destination, sha256: digest2, cached: true });
|
|
1660
|
+
}
|
|
1661
|
+
await rm3(destination, { force: true });
|
|
1662
|
+
} catch (error) {
|
|
1663
|
+
if (error.code !== "ENOENT") throw error;
|
|
1664
|
+
}
|
|
1665
|
+
const response = await this.request(archiveUrl, {
|
|
1666
|
+
method: "GET",
|
|
1667
|
+
redirect: "error",
|
|
1668
|
+
headers: { accept: "application/octet-stream, application/gzip" }
|
|
1669
|
+
});
|
|
1670
|
+
const bytes = await readBoundedResponse(response, this.maxArchiveBytes, "Template archive");
|
|
1671
|
+
const digest = sha256(bytes);
|
|
1672
|
+
if (!matchesDigest(digest, entry.archive.sha256)) {
|
|
1673
|
+
throw new Error(`Template archive SHA-256 mismatch for ${entry.id}@${entry.version}.`);
|
|
1674
|
+
}
|
|
1675
|
+
await mkdir3(path13.dirname(destination), { recursive: true });
|
|
1676
|
+
const temporary = `${destination}.${randomUUID2()}.tmp`;
|
|
1677
|
+
try {
|
|
1678
|
+
await writeFile3(temporary, bytes, { flag: "wx", mode: 384 });
|
|
1679
|
+
await rename3(temporary, destination);
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
await rm3(temporary, { force: true });
|
|
1682
|
+
throw error;
|
|
1683
|
+
}
|
|
1684
|
+
return Object.freeze({ entry, path: destination, sha256: digest, cached: false });
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
|
|
1688
|
+
// ../sdk/src/resolver/extract-template-archive.ts
|
|
1689
|
+
import { lstat as lstat5, mkdir as mkdir4, open, readFile as readFile8, rename as rename4, rm as rm4 } from "fs/promises";
|
|
1690
|
+
import path14 from "path";
|
|
1691
|
+
import { gunzipSync } from "zlib";
|
|
1692
|
+
var BLOCK_SIZE = 512;
|
|
1693
|
+
var DEFAULT_MAX_ENTRIES = 1e4;
|
|
1694
|
+
var DEFAULT_MAX_EXPANDED_BYTES = 250 * 1024 * 1024;
|
|
1695
|
+
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set([".", ".."]);
|
|
1696
|
+
function readString(header, start, length) {
|
|
1697
|
+
const end = header.indexOf(0, start);
|
|
1698
|
+
const stop = end === -1 || end >= start + length ? start + length : end;
|
|
1699
|
+
return header.toString("utf8", start, stop);
|
|
1700
|
+
}
|
|
1701
|
+
function readOctal(header, start, length, field) {
|
|
1702
|
+
const raw = header.toString("ascii", start, start + length).replace(/\0.*$/, "").trim();
|
|
1703
|
+
if (raw === "") return 0;
|
|
1704
|
+
if (!/^[0-7]+$/.test(raw)) throw new Error(`Invalid tar ${field} field.`);
|
|
1705
|
+
const value = Number.parseInt(raw, 8);
|
|
1706
|
+
if (!Number.isSafeInteger(value)) throw new Error(`Tar ${field} exceeds supported size.`);
|
|
1707
|
+
return value;
|
|
1708
|
+
}
|
|
1709
|
+
function verifyHeaderChecksum(header) {
|
|
1710
|
+
const expected = readOctal(header, 148, 8, "checksum");
|
|
1711
|
+
let actual = 0;
|
|
1712
|
+
for (let index = 0; index < BLOCK_SIZE; index++) {
|
|
1713
|
+
actual += index >= 148 && index < 156 ? 32 : header[index];
|
|
1714
|
+
}
|
|
1715
|
+
if (actual !== expected) throw new Error("Tar header checksum mismatch.");
|
|
1716
|
+
}
|
|
1717
|
+
function normalizeArchivePath(raw) {
|
|
1718
|
+
if (!raw || raw.includes("\0") || raw.includes("\\")) {
|
|
1719
|
+
throw new Error("Archive contains an empty or unsafe path.");
|
|
1720
|
+
}
|
|
1721
|
+
if (raw.startsWith("/") || /^[a-zA-Z]:/.test(raw) || raw.startsWith("//")) {
|
|
1722
|
+
throw new Error(`Archive path must be relative: ${raw}`);
|
|
1723
|
+
}
|
|
1724
|
+
const segments = raw.split("/");
|
|
1725
|
+
if (segments.some((segment) => FORBIDDEN_SEGMENTS.has(segment) || segment === "")) {
|
|
1726
|
+
throw new Error(`Archive path contains traversal or empty segments: ${raw}`);
|
|
1727
|
+
}
|
|
1728
|
+
return segments.join("/");
|
|
1729
|
+
}
|
|
1730
|
+
function isZeroBlock(block) {
|
|
1731
|
+
return block.every((byte) => byte === 0);
|
|
1732
|
+
}
|
|
1733
|
+
function parseTar(bytes) {
|
|
1734
|
+
const entries = [];
|
|
1735
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1736
|
+
let offset = 0;
|
|
1737
|
+
let zeroBlocks = 0;
|
|
1738
|
+
while (offset + BLOCK_SIZE <= bytes.length) {
|
|
1739
|
+
const header = bytes.subarray(offset, offset + BLOCK_SIZE);
|
|
1740
|
+
offset += BLOCK_SIZE;
|
|
1741
|
+
if (isZeroBlock(header)) {
|
|
1742
|
+
zeroBlocks++;
|
|
1743
|
+
if (zeroBlocks === 2) break;
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
if (zeroBlocks > 0) throw new Error("Tar archive has data after its end marker.");
|
|
1747
|
+
verifyHeaderChecksum(header);
|
|
1748
|
+
const name = readString(header, 0, 100);
|
|
1749
|
+
const prefix = readString(header, 345, 155);
|
|
1750
|
+
const rawPath = prefix ? `${prefix}/${name}` : name;
|
|
1751
|
+
const typeFlag = header[156];
|
|
1752
|
+
const size = readOctal(header, 124, 12, "size");
|
|
1753
|
+
const mode = readOctal(header, 100, 8, "mode");
|
|
1754
|
+
if ((mode & 3072) !== 0) throw new Error("Tar entries may not set setuid or setgid permissions.");
|
|
1755
|
+
if (typeFlag !== 0 && typeFlag !== 48 && typeFlag !== 53) {
|
|
1756
|
+
throw new Error("Tar archive contains a link, extension, or unsupported entry type.");
|
|
1757
|
+
}
|
|
1758
|
+
const type = typeFlag === 53 ? "directory" : "file";
|
|
1759
|
+
if (type === "directory" && size !== 0) throw new Error("Tar directory entries must be empty.");
|
|
1760
|
+
const normalized = normalizeArchivePath(type === "directory" && rawPath.endsWith("/") ? rawPath.slice(0, -1) : rawPath);
|
|
1761
|
+
const key = process.platform === "win32" ? normalized.toLocaleLowerCase("en-US") : normalized;
|
|
1762
|
+
if (seen.has(key)) throw new Error(`Tar archive contains duplicate path: ${normalized}`);
|
|
1763
|
+
seen.add(key);
|
|
1764
|
+
const paddedSize = Math.ceil(size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
1765
|
+
if (offset + paddedSize > bytes.length) throw new Error(`Tar entry is truncated: ${normalized}`);
|
|
1766
|
+
entries.push({
|
|
1767
|
+
path: normalized,
|
|
1768
|
+
type,
|
|
1769
|
+
data: type === "file" ? bytes.subarray(offset, offset + size) : Buffer.alloc(0)
|
|
1770
|
+
});
|
|
1771
|
+
offset += paddedSize;
|
|
1772
|
+
}
|
|
1773
|
+
if (zeroBlocks < 2) throw new Error("Tar archive is missing its end marker.");
|
|
1774
|
+
return entries;
|
|
1775
|
+
}
|
|
1776
|
+
function isWithin5(parent, child) {
|
|
1777
|
+
const relative = path14.relative(parent, child);
|
|
1778
|
+
return relative === "" || !relative.startsWith(`..${path14.sep}`) && relative !== ".." && !path14.isAbsolute(relative);
|
|
1779
|
+
}
|
|
1780
|
+
async function extractTemplateArchive(options) {
|
|
1781
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
1782
|
+
const maxExpandedBytes = options.maxExpandedBytes ?? DEFAULT_MAX_EXPANDED_BYTES;
|
|
1783
|
+
if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) throw new Error("maxEntries must be a positive safe integer.");
|
|
1784
|
+
if (!Number.isSafeInteger(maxExpandedBytes) || maxExpandedBytes < 1) throw new Error("maxExpandedBytes must be a positive safe integer.");
|
|
1785
|
+
const archive = await readFile8(options.archivePath);
|
|
1786
|
+
let tarBytes;
|
|
1787
|
+
try {
|
|
1788
|
+
tarBytes = archive.length >= 2 && archive[0] === 31 && archive[1] === 139 ? gunzipSync(archive, { maxOutputLength: maxExpandedBytes + maxEntries * BLOCK_SIZE * 2 }) : archive;
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
throw new Error("Unable to decompress template archive within the configured limit.", { cause: error });
|
|
1791
|
+
}
|
|
1792
|
+
const entries = parseTar(tarBytes);
|
|
1793
|
+
if (entries.length > maxEntries) throw new Error(`Archive exceeds the ${maxEntries}-entry limit.`);
|
|
1794
|
+
let expandedBytes = 0;
|
|
1795
|
+
for (const entry of entries) {
|
|
1796
|
+
expandedBytes += entry.data.byteLength;
|
|
1797
|
+
if (expandedBytes > maxExpandedBytes) {
|
|
1798
|
+
throw new Error(`Archive exceeds the ${maxExpandedBytes}-byte expanded-size limit.`);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
const destination = path14.resolve(options.destination);
|
|
1802
|
+
const parent = path14.dirname(destination);
|
|
1803
|
+
await mkdir4(parent, { recursive: true });
|
|
1804
|
+
const stage = path14.join(parent, `.appinit-extract-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
1805
|
+
if (!isWithin5(parent, stage) || stage === destination) throw new Error("Invalid extraction staging path.");
|
|
1806
|
+
let files = 0;
|
|
1807
|
+
let directories = 0;
|
|
1808
|
+
try {
|
|
1809
|
+
await mkdir4(stage, { recursive: false, mode: 448 });
|
|
1810
|
+
for (const entry of entries) {
|
|
1811
|
+
const target = path14.resolve(stage, ...entry.path.split("/"));
|
|
1812
|
+
if (!isWithin5(stage, target) || target === stage) throw new Error("Archive entry escapes extraction directory.");
|
|
1813
|
+
const relative = path14.relative(stage, target);
|
|
1814
|
+
const segments = relative.split(path14.sep);
|
|
1815
|
+
let current = stage;
|
|
1816
|
+
for (const segment of segments.slice(0, -1)) {
|
|
1817
|
+
current = path14.join(current, segment);
|
|
1818
|
+
try {
|
|
1819
|
+
const info = await lstat5(current);
|
|
1820
|
+
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Archive path traverses a non-directory entry.");
|
|
1821
|
+
} catch (error) {
|
|
1822
|
+
if (error.code !== "ENOENT") throw error;
|
|
1823
|
+
await mkdir4(current, { mode: 448 });
|
|
1824
|
+
directories++;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (entry.type === "directory") {
|
|
1828
|
+
try {
|
|
1829
|
+
await mkdir4(target, { mode: 448 });
|
|
1830
|
+
directories++;
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
if (error.code !== "EEXIST") throw error;
|
|
1833
|
+
const info = await lstat5(target);
|
|
1834
|
+
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error("Archive directory conflicts with a file.");
|
|
1835
|
+
}
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1838
|
+
const handle = await open(target, "wx", 384);
|
|
1839
|
+
try {
|
|
1840
|
+
await handle.writeFile(entry.data);
|
|
1841
|
+
} finally {
|
|
1842
|
+
await handle.close();
|
|
1843
|
+
}
|
|
1844
|
+
files++;
|
|
1845
|
+
}
|
|
1846
|
+
try {
|
|
1847
|
+
await lstat5(destination);
|
|
1848
|
+
throw new Error("Extraction destination already exists.");
|
|
1849
|
+
} catch (error) {
|
|
1850
|
+
if (error.code !== "ENOENT") throw error;
|
|
1851
|
+
}
|
|
1852
|
+
await rename4(stage, destination);
|
|
1853
|
+
return Object.freeze({ destination, files, directories, expandedBytes });
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
await rm4(stage, { recursive: true, force: true });
|
|
1856
|
+
throw error;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// ../sdk/src/composition/resolve-composition.ts
|
|
1861
|
+
function stableUnique(values) {
|
|
1862
|
+
return [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
1863
|
+
}
|
|
1864
|
+
function addDiagnostic(diagnostics, diagnostic3) {
|
|
1865
|
+
diagnostics.push(diagnostic3);
|
|
1866
|
+
}
|
|
1867
|
+
function topologicalOrder(ids, edges) {
|
|
1868
|
+
const original = new Map(ids.map((id, index) => [id, index]));
|
|
1869
|
+
const indegree = new Map(ids.map((id) => [id, 0]));
|
|
1870
|
+
const outgoing = new Map(ids.map((id) => [id, /* @__PURE__ */ new Set()]));
|
|
1871
|
+
for (const edge of edges) {
|
|
1872
|
+
if (!outgoing.get(edge.to).has(edge.from)) {
|
|
1873
|
+
outgoing.get(edge.to).add(edge.from);
|
|
1874
|
+
indegree.set(edge.from, indegree.get(edge.from) + 1);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
const queue = ids.filter((id) => indegree.get(id) === 0).sort((a, b) => original.get(a) - original.get(b));
|
|
1878
|
+
const result = [];
|
|
1879
|
+
while (queue.length > 0) {
|
|
1880
|
+
const id = queue.shift();
|
|
1881
|
+
result.push(id);
|
|
1882
|
+
for (const next of outgoing.get(id)) {
|
|
1883
|
+
indegree.set(next, indegree.get(next) - 1);
|
|
1884
|
+
if (indegree.get(next) === 0) {
|
|
1885
|
+
queue.push(next);
|
|
1886
|
+
queue.sort((a, b) => original.get(a) - original.get(b));
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
return result.length === ids.length ? result : void 0;
|
|
1891
|
+
}
|
|
1892
|
+
function resolveComposition(selected) {
|
|
1893
|
+
const diagnostics = [];
|
|
1894
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1895
|
+
for (const item of selected) {
|
|
1896
|
+
const id = item.manifest.id;
|
|
1897
|
+
if (byId.has(id)) {
|
|
1898
|
+
addDiagnostic(diagnostics, {
|
|
1899
|
+
code: "COMPOSITION_DUPLICATE_TEMPLATE",
|
|
1900
|
+
severity: "error",
|
|
1901
|
+
message: `Template ID "${id}" is selected more than once.`,
|
|
1902
|
+
templateIds: [id]
|
|
1903
|
+
});
|
|
1904
|
+
} else {
|
|
1905
|
+
byId.set(id, item);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
if (diagnostics.some((d) => d.severity === "error")) {
|
|
1909
|
+
return { diagnostics: Object.freeze(diagnostics) };
|
|
1910
|
+
}
|
|
1911
|
+
const templates = [...byId.values()];
|
|
1912
|
+
const projectTemplates = templates.filter((item) => item.manifest.kind === "project");
|
|
1913
|
+
if (projectTemplates.length > 1) {
|
|
1914
|
+
addDiagnostic(diagnostics, {
|
|
1915
|
+
code: "COMPOSITION_MULTIPLE_PROJECTS",
|
|
1916
|
+
severity: "error",
|
|
1917
|
+
message: "A composition may contain at most one project template.",
|
|
1918
|
+
templateIds: projectTemplates.map((item) => item.manifest.id).sort()
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
const providers = /* @__PURE__ */ new Map();
|
|
1922
|
+
for (const item of templates) {
|
|
1923
|
+
for (const capability of stableUnique(item.manifest.provides ?? [])) {
|
|
1924
|
+
const list = providers.get(capability) ?? [];
|
|
1925
|
+
list.push(item.manifest.id);
|
|
1926
|
+
providers.set(capability, list);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
for (const list of providers.values()) list.sort((a, b) => a.localeCompare(b));
|
|
1930
|
+
const edges = [];
|
|
1931
|
+
for (const item of templates) {
|
|
1932
|
+
const consumer = item.manifest.id;
|
|
1933
|
+
for (const requirement of stableUnique(item.manifest.requires ?? [])) {
|
|
1934
|
+
const matches = providers.get(requirement) ?? [];
|
|
1935
|
+
if (matches.length === 0) {
|
|
1936
|
+
addDiagnostic(diagnostics, {
|
|
1937
|
+
code: "COMPOSITION_MISSING_PROVIDER",
|
|
1938
|
+
severity: "error",
|
|
1939
|
+
message: `Template "${consumer}" requires "${requirement}", but no selected template provides it.`,
|
|
1940
|
+
templateIds: [consumer],
|
|
1941
|
+
requirement
|
|
1942
|
+
});
|
|
1943
|
+
} else if (matches.length > 1) {
|
|
1944
|
+
addDiagnostic(diagnostics, {
|
|
1945
|
+
code: "COMPOSITION_AMBIGUOUS_PROVIDER",
|
|
1946
|
+
severity: "error",
|
|
1947
|
+
message: `Requirement "${requirement}" for "${consumer}" has multiple providers: ${matches.join(", ")}.`,
|
|
1948
|
+
templateIds: [consumer, ...matches],
|
|
1949
|
+
requirement
|
|
1950
|
+
});
|
|
1951
|
+
} else if (matches[0] === consumer) {
|
|
1952
|
+
addDiagnostic(diagnostics, {
|
|
1953
|
+
code: "COMPOSITION_CYCLE",
|
|
1954
|
+
severity: "error",
|
|
1955
|
+
message: `Template "${consumer}" cannot satisfy its own requirement "${requirement}".`,
|
|
1956
|
+
templateIds: [consumer],
|
|
1957
|
+
requirement
|
|
1958
|
+
});
|
|
1959
|
+
} else {
|
|
1960
|
+
edges.push({ from: consumer, to: matches[0], requirement });
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
const selectedIds = new Set(byId.keys());
|
|
1965
|
+
const providedCapabilities = /* @__PURE__ */ new Map();
|
|
1966
|
+
for (const item of templates) {
|
|
1967
|
+
for (const capability of item.manifest.provides ?? []) {
|
|
1968
|
+
const owners = providedCapabilities.get(capability) ?? [];
|
|
1969
|
+
owners.push(item.manifest.id);
|
|
1970
|
+
providedCapabilities.set(capability, owners);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
const reportedConflicts = /* @__PURE__ */ new Set();
|
|
1974
|
+
for (const item of templates) {
|
|
1975
|
+
const id = item.manifest.id;
|
|
1976
|
+
for (const conflict of stableUnique(item.manifest.conflicts ?? [])) {
|
|
1977
|
+
const targets = selectedIds.has(conflict) ? [conflict] : providedCapabilities.get(conflict) ?? [];
|
|
1978
|
+
for (const target of targets) {
|
|
1979
|
+
if (target === id) continue;
|
|
1980
|
+
const pair = [id, target].sort();
|
|
1981
|
+
const pairKey = pair.join("\\0");
|
|
1982
|
+
if (reportedConflicts.has(pairKey)) continue;
|
|
1983
|
+
reportedConflicts.add(pairKey);
|
|
1984
|
+
addDiagnostic(diagnostics, {
|
|
1985
|
+
code: "COMPOSITION_CONFLICT",
|
|
1986
|
+
severity: "error",
|
|
1987
|
+
message: `Template "${id}" conflicts with selected template "${target}" (declaration: "${conflict}").`,
|
|
1988
|
+
templateIds: pair,
|
|
1989
|
+
requirement: conflict
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
if (diagnostics.some((d) => d.severity === "error")) {
|
|
1995
|
+
return { diagnostics: Object.freeze(diagnostics) };
|
|
1996
|
+
}
|
|
1997
|
+
const ids = [...byId.keys()];
|
|
1998
|
+
const ordered = topologicalOrder(ids, edges);
|
|
1999
|
+
if (!ordered) {
|
|
2000
|
+
addDiagnostic(diagnostics, {
|
|
2001
|
+
code: "COMPOSITION_CYCLE",
|
|
2002
|
+
severity: "error",
|
|
2003
|
+
message: "Selected templates contain a dependency cycle.",
|
|
2004
|
+
templateIds: [...ids].sort()
|
|
2005
|
+
});
|
|
2006
|
+
return { diagnostics: Object.freeze(diagnostics) };
|
|
2007
|
+
}
|
|
2008
|
+
const manifestById = /* @__PURE__ */ new Map();
|
|
2009
|
+
for (const [id, item] of byId) manifestById.set(id, item.manifest);
|
|
2010
|
+
const plan = Object.freeze({
|
|
2011
|
+
orderedTemplateIds: Object.freeze(ordered),
|
|
2012
|
+
templates: Object.freeze(ordered.map((id) => manifestById.get(id))),
|
|
2013
|
+
edges: Object.freeze(edges.sort(
|
|
2014
|
+
(a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || a.requirement.localeCompare(b.requirement)
|
|
2015
|
+
))
|
|
2016
|
+
});
|
|
2017
|
+
return { plan, diagnostics: Object.freeze(diagnostics) };
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// ../sdk/src/execution/plan-file-operations.ts
|
|
2021
|
+
function cloneContent(content) {
|
|
2022
|
+
return typeof content === "string" ? content : Buffer.from(content);
|
|
2023
|
+
}
|
|
2024
|
+
function planFileOperations(templates, mode = "strict") {
|
|
2025
|
+
const composition = resolveComposition(templates.map((template) => ({
|
|
2026
|
+
manifest: template.manifest,
|
|
2027
|
+
source: template.root
|
|
2028
|
+
})));
|
|
2029
|
+
const diagnostics = composition.diagnostics.map((diagnostic3) => ({
|
|
2030
|
+
code: diagnostic3.code,
|
|
2031
|
+
severity: "error",
|
|
2032
|
+
message: diagnostic3.message,
|
|
2033
|
+
templateIds: diagnostic3.templateIds
|
|
2034
|
+
}));
|
|
2035
|
+
if (!composition.plan) {
|
|
2036
|
+
return { diagnostics: Object.freeze(diagnostics) };
|
|
2037
|
+
}
|
|
2038
|
+
const byId = new Map(templates.map((template) => [template.manifest.id, template]));
|
|
2039
|
+
const operations = [];
|
|
2040
|
+
const operationByPath = /* @__PURE__ */ new Map();
|
|
2041
|
+
for (const templateId of composition.plan.orderedTemplateIds) {
|
|
2042
|
+
const template = byId.get(templateId);
|
|
2043
|
+
for (const file of template.vfs.list()) {
|
|
2044
|
+
let destination;
|
|
2045
|
+
try {
|
|
2046
|
+
destination = normalizeVfsDestination(file.dest);
|
|
2047
|
+
} catch (error) {
|
|
2048
|
+
diagnostics.push({
|
|
2049
|
+
code: "FILE_PATH_INVALID",
|
|
2050
|
+
severity: "error",
|
|
2051
|
+
message: `Template "${templateId}" contains an invalid output path "${file.dest}": ${error instanceof Error ? error.message : String(error)}`,
|
|
2052
|
+
destination: file.dest,
|
|
2053
|
+
templateIds: [templateId]
|
|
2054
|
+
});
|
|
2055
|
+
continue;
|
|
2056
|
+
}
|
|
2057
|
+
const existingIndex = operationByPath.get(destination);
|
|
2058
|
+
if (existingIndex === void 0) {
|
|
2059
|
+
operationByPath.set(destination, operations.length);
|
|
2060
|
+
operations.push(Object.freeze({
|
|
2061
|
+
kind: "create",
|
|
2062
|
+
destination,
|
|
2063
|
+
sourceTemplateId: templateId,
|
|
2064
|
+
content: cloneContent(file.content),
|
|
2065
|
+
render: file.render,
|
|
2066
|
+
binary: file.binary,
|
|
2067
|
+
replaces: Object.freeze([])
|
|
2068
|
+
}));
|
|
2069
|
+
continue;
|
|
2070
|
+
}
|
|
2071
|
+
const previous = operations[existingIndex];
|
|
2072
|
+
const involved = [previous.sourceTemplateId, templateId];
|
|
2073
|
+
if (mode === "strict") {
|
|
2074
|
+
diagnostics.push({
|
|
2075
|
+
code: "FILE_CONFLICT",
|
|
2076
|
+
severity: "error",
|
|
2077
|
+
message: `Multiple templates produce "${destination}": ${involved.join(", ")}.`,
|
|
2078
|
+
destination,
|
|
2079
|
+
templateIds: Object.freeze(involved)
|
|
2080
|
+
});
|
|
2081
|
+
continue;
|
|
2082
|
+
}
|
|
2083
|
+
diagnostics.push({
|
|
2084
|
+
code: "FILE_CONFLICT",
|
|
2085
|
+
severity: "warning",
|
|
2086
|
+
message: `Template "${templateId}" overlays "${destination}" from "${previous.sourceTemplateId}".`,
|
|
2087
|
+
destination,
|
|
2088
|
+
templateIds: Object.freeze(involved)
|
|
2089
|
+
});
|
|
2090
|
+
operations[existingIndex] = Object.freeze({
|
|
2091
|
+
kind: "overwrite",
|
|
2092
|
+
destination,
|
|
2093
|
+
sourceTemplateId: templateId,
|
|
2094
|
+
content: cloneContent(file.content),
|
|
2095
|
+
render: file.render,
|
|
2096
|
+
binary: file.binary,
|
|
2097
|
+
replaces: Object.freeze([...previous.replaces, previous.sourceTemplateId])
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
if (diagnostics.some((diagnostic3) => diagnostic3.severity === "error")) {
|
|
2102
|
+
return { diagnostics: Object.freeze(diagnostics) };
|
|
2103
|
+
}
|
|
2104
|
+
const orderedTemplates = composition.plan.orderedTemplateIds.map((id) => byId.get(id));
|
|
2105
|
+
const plan = Object.freeze({
|
|
2106
|
+
mode,
|
|
2107
|
+
orderedTemplateIds: Object.freeze([...composition.plan.orderedTemplateIds]),
|
|
2108
|
+
operations: Object.freeze(operations),
|
|
2109
|
+
templates: Object.freeze(orderedTemplates)
|
|
2110
|
+
});
|
|
2111
|
+
return { plan, diagnostics: Object.freeze(diagnostics) };
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
// ../sdk/src/execution/execute-file-plan.ts
|
|
2115
|
+
async function executeFilePlan(plan, targetDirectory, options = {}) {
|
|
2116
|
+
return executeFileOperations(
|
|
2117
|
+
targetDirectory,
|
|
2118
|
+
plan.operations.map((operation) => ({
|
|
2119
|
+
destination: operation.destination,
|
|
2120
|
+
content: operation.content,
|
|
2121
|
+
binary: operation.binary,
|
|
2122
|
+
sourceTemplateId: operation.sourceTemplateId
|
|
2123
|
+
})),
|
|
2124
|
+
options
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// ../sdk/src/execution/render-file-operation-plan.ts
|
|
2129
|
+
async function renderFileOperationPlan(plan, variablesByTemplate = /* @__PURE__ */ new Map(), schemasByTemplate = /* @__PURE__ */ new Map()) {
|
|
2130
|
+
const diagnostics = [];
|
|
2131
|
+
const templates = new Map(plan.templates.map((template) => [template.manifest.id, template]));
|
|
2132
|
+
const validatedVariables = /* @__PURE__ */ new Map();
|
|
2133
|
+
for (const template of plan.templates) {
|
|
2134
|
+
const id = template.manifest.id;
|
|
2135
|
+
const variables = variablesByTemplate.get(id) ?? {};
|
|
2136
|
+
const validation = validateTemplateVariables(variables, schemasByTemplate.get(id));
|
|
2137
|
+
if (!validation.valid) {
|
|
2138
|
+
for (const message of validation.errors) {
|
|
2139
|
+
diagnostics.push({ code: "RENDER_INVALID_VARIABLES", templateId: id, message });
|
|
2140
|
+
}
|
|
2141
|
+
} else {
|
|
2142
|
+
validatedVariables.set(id, variables);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
if (diagnostics.length > 0) {
|
|
2146
|
+
return Object.freeze({ ok: false, diagnostics: Object.freeze(diagnostics) });
|
|
2147
|
+
}
|
|
2148
|
+
const operations = [];
|
|
2149
|
+
for (const operation of plan.operations) {
|
|
2150
|
+
const template = templates.get(operation.sourceTemplateId);
|
|
2151
|
+
if (!template) {
|
|
2152
|
+
diagnostics.push({
|
|
2153
|
+
code: "RENDER_FILE_FAILED",
|
|
2154
|
+
templateId: operation.sourceTemplateId,
|
|
2155
|
+
destination: operation.destination,
|
|
2156
|
+
message: "File operation refers to a template that is not in the composition plan."
|
|
2157
|
+
});
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
try {
|
|
2161
|
+
const rendered = await renderFile({
|
|
2162
|
+
file: {
|
|
2163
|
+
dest: operation.destination,
|
|
2164
|
+
content: operation.content,
|
|
2165
|
+
render: operation.render,
|
|
2166
|
+
binary: operation.binary
|
|
2167
|
+
},
|
|
2168
|
+
engine: template.manifest.renderer.engine,
|
|
2169
|
+
variables: validatedVariables.get(operation.sourceTemplateId) ?? {}
|
|
2170
|
+
});
|
|
2171
|
+
operations.push(Object.freeze({ ...operation, content: rendered.content, render: false }));
|
|
2172
|
+
} catch (error) {
|
|
2173
|
+
diagnostics.push({
|
|
2174
|
+
code: "RENDER_FILE_FAILED",
|
|
2175
|
+
templateId: operation.sourceTemplateId,
|
|
2176
|
+
destination: operation.destination,
|
|
2177
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
if (diagnostics.length > 0) {
|
|
2182
|
+
return Object.freeze({ ok: false, diagnostics: Object.freeze(diagnostics) });
|
|
2183
|
+
}
|
|
2184
|
+
return Object.freeze({
|
|
2185
|
+
ok: true,
|
|
2186
|
+
plan: Object.freeze({
|
|
2187
|
+
sourcePlan: plan,
|
|
2188
|
+
operations: Object.freeze(operations)
|
|
2189
|
+
}),
|
|
2190
|
+
diagnostics: Object.freeze([])
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
// src/template-path.ts
|
|
2195
|
+
import { existsSync } from "fs";
|
|
2196
|
+
import path15 from "path";
|
|
2197
|
+
import { fileURLToPath } from "url";
|
|
2198
|
+
function resolveTemplateDirectory(override) {
|
|
2199
|
+
if (override) return path15.resolve(override);
|
|
2200
|
+
if (process.env.APPINIT_TEMPLATES_DIR) {
|
|
2201
|
+
return path15.resolve(process.env.APPINIT_TEMPLATES_DIR);
|
|
2202
|
+
}
|
|
2203
|
+
const workingDirectoryTemplates = path15.resolve(process.cwd(), "templates");
|
|
2204
|
+
if (existsSync(workingDirectoryTemplates)) return workingDirectoryTemplates;
|
|
2205
|
+
return path15.join(path15.dirname(fileURLToPath(import.meta.url)), "templates");
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// src/create.ts
|
|
2209
|
+
async function discoverApprovedRoots(directory) {
|
|
2210
|
+
const canonicalRoot = await realpath7(path16.resolve(directory));
|
|
2211
|
+
const entries = await readdir4(canonicalRoot, { withFileTypes: true });
|
|
2212
|
+
const roots = /* @__PURE__ */ new Map();
|
|
2213
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2214
|
+
if (!entry.isDirectory()) continue;
|
|
2215
|
+
const templateRoot = path16.join(canonicalRoot, entry.name);
|
|
2216
|
+
let manifestValue;
|
|
2217
|
+
try {
|
|
2218
|
+
manifestValue = JSON.parse(await readFile9(path16.join(templateRoot, "appinit.template.json"), "utf8"));
|
|
2219
|
+
} catch {
|
|
2220
|
+
continue;
|
|
2221
|
+
}
|
|
2222
|
+
if (manifestValue === null || typeof manifestValue !== "object" || Array.isArray(manifestValue)) continue;
|
|
2223
|
+
const manifest = manifestValue;
|
|
2224
|
+
const aliases = [entry.name, manifest.id, manifest.packageName].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
2225
|
+
for (const alias of aliases) {
|
|
2226
|
+
const existing = roots.get(alias);
|
|
2227
|
+
if (existing && existing !== templateRoot) throw new Error(`Template reference "${alias}" is ambiguous in ${canonicalRoot}.`);
|
|
2228
|
+
roots.set(alias, templateRoot);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
return roots;
|
|
2232
|
+
}
|
|
2233
|
+
async function discoverLocalTemplates(directory) {
|
|
2234
|
+
const roots = await discoverApprovedRoots(directory);
|
|
2235
|
+
const uniqueRoots = [...new Set(roots.values())].sort();
|
|
2236
|
+
const templates = [];
|
|
2237
|
+
for (const root of uniqueRoots) {
|
|
2238
|
+
const manifest = JSON.parse(await readFile9(path16.join(root, "appinit.template.json"), "utf8"));
|
|
2239
|
+
if (typeof manifest.id !== "string") continue;
|
|
2240
|
+
try {
|
|
2241
|
+
const discovered = await discoverTemplate(manifest.id, { templateRoots: roots });
|
|
2242
|
+
templates.push(Object.freeze({
|
|
2243
|
+
id: discovered.template.manifest.id,
|
|
2244
|
+
name: discovered.template.manifest.name,
|
|
2245
|
+
description: discovered.template.manifest.description,
|
|
2246
|
+
category: discovered.template.manifest.category,
|
|
2247
|
+
root,
|
|
2248
|
+
template: discovered.template
|
|
2249
|
+
}));
|
|
2250
|
+
} catch {
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
return Object.freeze(templates);
|
|
2254
|
+
}
|
|
2255
|
+
async function assertSafeTarget(target) {
|
|
2256
|
+
const absolute = path16.resolve(target);
|
|
2257
|
+
try {
|
|
2258
|
+
const info = await lstat6(absolute);
|
|
2259
|
+
if (info.isSymbolicLink() || !info.isDirectory()) throw new Error("Target must be a real directory, not a symlink or file.");
|
|
2260
|
+
if ((await readdir4(absolute)).length > 0) throw new Error(`Target directory is not empty: ${absolute}. Choose an empty or new directory.`);
|
|
2261
|
+
} catch (error) {
|
|
2262
|
+
if (error.code !== "ENOENT") throw error;
|
|
2263
|
+
}
|
|
2264
|
+
return absolute;
|
|
2265
|
+
}
|
|
2266
|
+
async function recordProjectHistory(targetDirectory, templateId) {
|
|
2267
|
+
const homeDirectory = resolveAppInitHome();
|
|
2268
|
+
const existing = await readAppInitJson(homeDirectory, "state.json");
|
|
2269
|
+
let state;
|
|
2270
|
+
if (existing === void 0) {
|
|
2271
|
+
state = { schemaVersion: 1, projects: [] };
|
|
2272
|
+
} else {
|
|
2273
|
+
const validation = validateAppInitState(existing);
|
|
2274
|
+
if (!validation.valid || !validation.state) {
|
|
2275
|
+
throw new Error(`Existing global state is invalid: ${validation.errors.join(" ")}`);
|
|
2276
|
+
}
|
|
2277
|
+
state = validation.state;
|
|
2278
|
+
}
|
|
2279
|
+
const projectPath = path16.resolve(targetDirectory);
|
|
2280
|
+
const projects = state.projects.filter((project) => project.path !== projectPath);
|
|
2281
|
+
projects.push({
|
|
2282
|
+
path: projectPath,
|
|
2283
|
+
templateId,
|
|
2284
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2285
|
+
});
|
|
2286
|
+
await writeAppInitJson(homeDirectory, "state.json", {
|
|
2287
|
+
...state,
|
|
2288
|
+
schemaVersion: 1,
|
|
2289
|
+
projects
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
async function runCreateCommand(options) {
|
|
2293
|
+
const targetDirectory = await assertSafeTarget(options.target);
|
|
2294
|
+
const templateDirectory = resolveTemplateDirectory(options.templateDirectory);
|
|
2295
|
+
const intent = {
|
|
2296
|
+
executionKind: "scaffold",
|
|
2297
|
+
executionMode: "user",
|
|
2298
|
+
artifactKind: "project",
|
|
2299
|
+
rootDir: process.cwd(),
|
|
2300
|
+
targetDir: targetDirectory,
|
|
2301
|
+
templateRef: options.template,
|
|
2302
|
+
variables: { ...options.variables },
|
|
2303
|
+
flags: { dryRun: options.dryRun }
|
|
2304
|
+
};
|
|
2305
|
+
const validation = validateExecutionIntent(intent);
|
|
2306
|
+
if (!validation.ok) throw new Error(validation.diagnostics.map((item) => item.message).join("\n"));
|
|
2307
|
+
const roots = await discoverApprovedRoots(templateDirectory);
|
|
2308
|
+
const approvedRoot = roots.get(options.template);
|
|
2309
|
+
if (!approvedRoot) throw new Error(`Template "${options.template}" is not present in the approved template roots.`);
|
|
2310
|
+
const approvedManifest = JSON.parse(await readFile9(path16.join(approvedRoot, "appinit.template.json"), "utf8"));
|
|
2311
|
+
if (typeof approvedManifest.id !== "string") throw new Error(`Template "${options.template}" has an invalid manifest identity.`);
|
|
2312
|
+
const discovered = await discoverTemplate(approvedManifest.id, { templateRoots: roots });
|
|
2313
|
+
const metadata = await loadTemplateVariables(discovered.template.root, discovered.template.manifest);
|
|
2314
|
+
const variables = { ...metadata.defaults, ...options.variables };
|
|
2315
|
+
const variableErrors = validateTemplateVariables2(variables, metadata.schema);
|
|
2316
|
+
if (variableErrors.length) throw new Error(`Template variables are invalid:
|
|
2317
|
+
${variableErrors.map((item) => ` - ${item}`).join("\n")}`);
|
|
2318
|
+
const planning = planFileOperations([discovered.template], "strict");
|
|
2319
|
+
if (!planning.plan) throw new Error(`Composition planning failed:
|
|
2320
|
+
${planning.diagnostics.map((item) => item.message).join("\n")}`);
|
|
2321
|
+
const schema = {};
|
|
2322
|
+
for (const [key, rule] of Object.entries(metadata.schema)) {
|
|
2323
|
+
if (rule.type) schema[key] = { type: rule.type, ...rule.required !== void 0 ? { required: rule.required } : {}, ...rule.minLength !== void 0 ? { minLength: rule.minLength } : {}, ...rule.enum ? { enum: rule.enum } : {} };
|
|
2324
|
+
}
|
|
2325
|
+
const templateId = discovered.template.manifest.id;
|
|
2326
|
+
const rendered = await renderFileOperationPlan(
|
|
2327
|
+
planning.plan,
|
|
2328
|
+
/* @__PURE__ */ new Map([[templateId, variables]]),
|
|
2329
|
+
/* @__PURE__ */ new Map([[templateId, schema]])
|
|
2330
|
+
);
|
|
2331
|
+
if (!rendered.ok || !rendered.plan) throw new Error(`Template rendering failed:
|
|
2332
|
+
${rendered.diagnostics.map((item) => item.message).join("\n")}`);
|
|
2333
|
+
const executablePlan = { ...rendered.plan.sourcePlan, operations: rendered.plan.operations };
|
|
2334
|
+
const execution = await executeFilePlan(executablePlan, targetDirectory, { dryRun: options.dryRun });
|
|
2335
|
+
if (!execution.ok) throw new Error(`File execution failed:
|
|
2336
|
+
${execution.diagnostics.map((item) => item.message).join("\n")}`);
|
|
2337
|
+
if (options.dryRun) {
|
|
2338
|
+
process.stdout.write(`Dry run successful. ${execution.writtenFiles.length} file(s) would be created in ${targetDirectory}.
|
|
2339
|
+
`);
|
|
2340
|
+
for (const destination of execution.writtenFiles) process.stdout.write(` + ${destination}
|
|
2341
|
+
`);
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
const operations = rendered.plan.operations.map((operation) => ({
|
|
2345
|
+
destination: operation.destination,
|
|
2346
|
+
content: operation.content,
|
|
2347
|
+
binary: operation.binary,
|
|
2348
|
+
sourceTemplateId: operation.sourceTemplateId
|
|
2349
|
+
}));
|
|
2350
|
+
const verification = await verifyGeneratedOutput(targetDirectory, operations);
|
|
2351
|
+
if (!verification.ok) throw new Error(`Output was written, but verification failed. Review ${targetDirectory}:
|
|
2352
|
+
${verification.diagnostics.map((item) => item.message).join("\n")}`);
|
|
2353
|
+
try {
|
|
2354
|
+
await recordProjectHistory(targetDirectory, templateId);
|
|
2355
|
+
} catch (error) {
|
|
2356
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2357
|
+
process.stderr.write(`Warning: project was created, but global history could not be updated: ${message}
|
|
2358
|
+
`);
|
|
2359
|
+
}
|
|
2360
|
+
process.stdout.write(`Created ${discovered.template.manifest.name} in ${targetDirectory}.
|
|
2361
|
+
`);
|
|
2362
|
+
process.stdout.write(`Verified ${verification.actualFiles.length} generated file(s).
|
|
2363
|
+
`);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
// src/finalize-project.ts
|
|
2367
|
+
import { access as access2, readFile as readFile11 } from "fs/promises";
|
|
2368
|
+
import path18 from "path";
|
|
2369
|
+
|
|
2370
|
+
// src/finalize.ts
|
|
2371
|
+
import { access, readFile as readFile10 } from "fs/promises";
|
|
2372
|
+
import { spawn } from "child_process";
|
|
2373
|
+
import path17 from "path";
|
|
2374
|
+
var LOCKFILES = Object.freeze({
|
|
2375
|
+
"pnpm-lock.yaml": "pnpm",
|
|
2376
|
+
"package-lock.json": "npm",
|
|
2377
|
+
"yarn.lock": "yarn",
|
|
2378
|
+
"bun.lock": "bun",
|
|
2379
|
+
"bun.lockb": "bun"
|
|
2380
|
+
});
|
|
2381
|
+
async function detectPackageManager(directory) {
|
|
2382
|
+
try {
|
|
2383
|
+
const manifest = JSON.parse(await readFile10(path17.join(directory, "package.json"), "utf8"));
|
|
2384
|
+
if (typeof manifest.packageManager === "string") {
|
|
2385
|
+
const declared = /^(pnpm|npm|yarn|bun)@/.exec(manifest.packageManager)?.[1];
|
|
2386
|
+
if (declared) return declared;
|
|
2387
|
+
}
|
|
2388
|
+
} catch {
|
|
2389
|
+
}
|
|
2390
|
+
const detected = /* @__PURE__ */ new Set();
|
|
2391
|
+
for (const [lockfile, manager] of Object.entries(LOCKFILES)) {
|
|
2392
|
+
try {
|
|
2393
|
+
await access(path17.join(directory, lockfile));
|
|
2394
|
+
detected.add(manager);
|
|
2395
|
+
} catch {
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
if (detected.size === 1) return detected.values().next().value;
|
|
2399
|
+
if (detected.size > 1) return void 0;
|
|
2400
|
+
const agent = process.env.npm_config_user_agent ?? "";
|
|
2401
|
+
const match = /^(pnpm|npm|yarn|bun)\//.exec(agent);
|
|
2402
|
+
return match?.[1];
|
|
2403
|
+
}
|
|
2404
|
+
async function isNestedInPnpmWorkspace(directory) {
|
|
2405
|
+
let current = path17.dirname(path17.resolve(directory));
|
|
2406
|
+
const root = path17.parse(current).root;
|
|
2407
|
+
while (true) {
|
|
2408
|
+
try {
|
|
2409
|
+
await access(path17.join(current, "pnpm-workspace.yaml"));
|
|
2410
|
+
return true;
|
|
2411
|
+
} catch {
|
|
2412
|
+
if (current === root) return false;
|
|
2413
|
+
current = path17.dirname(current);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
async function runSetupCommand(command, directory, packageManager) {
|
|
2418
|
+
const executable = command === "git-init" ? "git" : packageManager;
|
|
2419
|
+
if (!executable) throw new Error("A package manager must be selected before installing dependencies.");
|
|
2420
|
+
const args = command === "git-init" ? ["init"] : ["install"];
|
|
2421
|
+
if (command === "install" && packageManager === "pnpm" && await isNestedInPnpmWorkspace(directory)) {
|
|
2422
|
+
args.push("--ignore-workspace");
|
|
2423
|
+
}
|
|
2424
|
+
await new Promise((resolve, reject) => {
|
|
2425
|
+
const child = spawn(executable, args, {
|
|
2426
|
+
cwd: directory,
|
|
2427
|
+
stdio: "inherit",
|
|
2428
|
+
shell: process.platform === "win32"
|
|
2429
|
+
});
|
|
2430
|
+
child.once("error", (error) => reject(new Error(`Could not start ${executable}: ${error.message}`)));
|
|
2431
|
+
child.once("close", (code, signal) => {
|
|
2432
|
+
if (code === 0) resolve();
|
|
2433
|
+
else reject(new Error(`${executable} ${args.join(" ")} failed${signal ? ` (signal ${signal})` : ` with exit code ${String(code)}`}.`));
|
|
2434
|
+
});
|
|
2435
|
+
});
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// src/finalize-project.ts
|
|
2439
|
+
async function isInsideGitRepository(directory) {
|
|
2440
|
+
let current = path18.resolve(directory);
|
|
2441
|
+
const root = path18.parse(current).root;
|
|
2442
|
+
while (true) {
|
|
2443
|
+
try {
|
|
2444
|
+
await access2(path18.join(current, ".git"));
|
|
2445
|
+
return true;
|
|
2446
|
+
} catch {
|
|
2447
|
+
if (current === root) return false;
|
|
2448
|
+
current = path18.dirname(current);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
async function getDevScript(directory) {
|
|
2453
|
+
try {
|
|
2454
|
+
const manifest = JSON.parse(await readFile11(path18.join(directory, "package.json"), "utf8"));
|
|
2455
|
+
return typeof manifest.scripts?.dev === "string" ? manifest.scripts.dev : void 0;
|
|
2456
|
+
} catch {
|
|
2457
|
+
return void 0;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
function isPackageManager(value) {
|
|
2461
|
+
return value === "pnpm" || value === "npm" || value === "yarn" || value === "bun";
|
|
2462
|
+
}
|
|
2463
|
+
async function finalizeProject(directory, options, io) {
|
|
2464
|
+
let install = options.install;
|
|
2465
|
+
let git = options.git;
|
|
2466
|
+
const failures = [];
|
|
2467
|
+
if (io.interactive) {
|
|
2468
|
+
if (!install) install = await io.confirm("Install project dependencies now?");
|
|
2469
|
+
if (!git) git = await io.confirm("Initialize a Git repository?");
|
|
2470
|
+
}
|
|
2471
|
+
let manager = options.packageManager ?? await detectPackageManager(directory);
|
|
2472
|
+
if (install && !manager && io.interactive) {
|
|
2473
|
+
const answer = (await io.ask("Package manager (pnpm/npm/yarn/bun)", "pnpm")).toLowerCase();
|
|
2474
|
+
if (!isPackageManager(answer)) throw new Error(`Unsupported package manager "${answer}". Choose pnpm, npm, yarn, or bun.`);
|
|
2475
|
+
manager = answer;
|
|
2476
|
+
}
|
|
2477
|
+
if (install) {
|
|
2478
|
+
if (!manager) {
|
|
2479
|
+
const message = "No package manager detected. Re-run with --package-manager <pnpm|npm|yarn|bun> --install.";
|
|
2480
|
+
failures.push(message);
|
|
2481
|
+
io.write(`Dependencies were not installed: ${message}
|
|
2482
|
+
`);
|
|
2483
|
+
} else {
|
|
2484
|
+
io.write(`Installing dependencies with ${manager}...
|
|
2485
|
+
`);
|
|
2486
|
+
try {
|
|
2487
|
+
await runSetupCommand("install", directory, manager);
|
|
2488
|
+
io.write("Dependencies installed successfully.\n");
|
|
2489
|
+
} catch (error) {
|
|
2490
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2491
|
+
failures.push(message);
|
|
2492
|
+
io.write(`Dependency installation failed; generated files were preserved. ${message}
|
|
2493
|
+
`);
|
|
2494
|
+
io.write(`Recovery: cd "${directory}" && ${manager} install
|
|
2495
|
+
`);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
if (git) {
|
|
2500
|
+
if (await isInsideGitRepository(directory)) {
|
|
2501
|
+
io.write("Skipped Git initialization: this project is already inside a Git repository.\n");
|
|
2502
|
+
} else {
|
|
2503
|
+
try {
|
|
2504
|
+
await runSetupCommand("git-init", directory);
|
|
2505
|
+
io.write("Git repository initialized.\n");
|
|
2506
|
+
} catch (error) {
|
|
2507
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2508
|
+
failures.push(message);
|
|
2509
|
+
io.write(`Git initialization failed; generated files were preserved. ${message}
|
|
2510
|
+
`);
|
|
2511
|
+
io.write(`Recovery: cd "${directory}" && git init
|
|
2512
|
+
`);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
io.write("\nNext steps:\n");
|
|
2517
|
+
io.write(` cd "${directory}"
|
|
2518
|
+
`);
|
|
2519
|
+
if (install && manager && failures.length === 0) {
|
|
2520
|
+
const devScript = await getDevScript(directory);
|
|
2521
|
+
if (devScript) {
|
|
2522
|
+
const run = manager === "npm" ? "npm run dev" : manager === "bun" ? "bun run dev" : `${manager} dev`;
|
|
2523
|
+
io.write(` ${run}
|
|
2524
|
+
`);
|
|
2525
|
+
} else {
|
|
2526
|
+
io.write(" Review the generated README for available scripts.\n");
|
|
2527
|
+
}
|
|
2528
|
+
} else {
|
|
2529
|
+
io.write(` ${manager ? manager + " install" : "Install dependencies with your package manager"}
|
|
2530
|
+
`);
|
|
2531
|
+
}
|
|
2532
|
+
if (failures.length) {
|
|
2533
|
+
throw new Error(`Project files were created, but one or more setup steps failed: ${failures.join("; ")}`);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
// src/doctor.ts
|
|
2538
|
+
import path22 from "path";
|
|
2539
|
+
|
|
2540
|
+
// ../guard/src/doctor/providers.ts
|
|
2541
|
+
import { readdir as readdir5, readFile as readFile12 } from "fs/promises";
|
|
2542
|
+
import path19 from "path";
|
|
2543
|
+
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules", ".pnpm", "dist"]);
|
|
2544
|
+
async function walk(root, current, files) {
|
|
2545
|
+
let entries;
|
|
2546
|
+
try {
|
|
2547
|
+
entries = await readdir5(current, { withFileTypes: true });
|
|
2548
|
+
} catch {
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
for (const entry of entries) {
|
|
2552
|
+
if (entry.isSymbolicLink()) continue;
|
|
2553
|
+
const absolute = path19.join(current, entry.name);
|
|
2554
|
+
const relative = path19.relative(root, absolute).split(path19.sep).join("/");
|
|
2555
|
+
files.add(relative);
|
|
2556
|
+
if (entry.isDirectory() && !IGNORED_DIRECTORIES.has(entry.name)) {
|
|
2557
|
+
await walk(root, absolute, files);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
async function readJson(filePath) {
|
|
2562
|
+
try {
|
|
2563
|
+
const value = JSON.parse(await readFile12(filePath, "utf8"));
|
|
2564
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2565
|
+
} catch {
|
|
2566
|
+
return void 0;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
async function collectDoctorSnapshot(rootDir) {
|
|
2570
|
+
const root = path19.resolve(rootDir);
|
|
2571
|
+
const files = /* @__PURE__ */ new Set();
|
|
2572
|
+
await walk(root, root, files);
|
|
2573
|
+
const packageJson = await readJson(path19.join(root, "package.json"));
|
|
2574
|
+
const appinitContext = await readJson(path19.join(root, ".appinit", "context.json"));
|
|
2575
|
+
const declaration = packageJson?.packageManager;
|
|
2576
|
+
const declaredManager = typeof declaration === "string" ? /^(pnpm|npm|yarn|bun)@/.exec(declaration)?.[1] : void 0;
|
|
2577
|
+
const lockfileManager = files.has("pnpm-lock.yaml") ? "pnpm" : files.has("package-lock.json") ? "npm" : files.has("yarn.lock") ? "yarn" : files.has("bun.lock") || files.has("bun.lockb") ? "bun" : void 0;
|
|
2578
|
+
const intent = {
|
|
2579
|
+
artifactKind: appinitContext?.artifactKind === "template" || appinitContext?.artifactKind === "package" ? appinitContext.artifactKind : "project",
|
|
2580
|
+
atsVersion: typeof appinitContext?.atsVersion === "string" ? appinitContext.atsVersion : void 0,
|
|
2581
|
+
runtime: appinitContext?.runtime === "node" || appinitContext?.runtime === "bun" || appinitContext?.runtime === "deno" ? appinitContext.runtime : void 0,
|
|
2582
|
+
framework: typeof appinitContext?.framework === "string" ? appinitContext.framework : void 0
|
|
2583
|
+
};
|
|
2584
|
+
const dependencies = {
|
|
2585
|
+
...packageJson?.dependencies && typeof packageJson.dependencies === "object" ? packageJson.dependencies : {},
|
|
2586
|
+
...packageJson?.devDependencies && typeof packageJson.devDependencies === "object" ? packageJson.devDependencies : {}
|
|
2587
|
+
};
|
|
2588
|
+
return Object.freeze({
|
|
2589
|
+
files,
|
|
2590
|
+
dependencies: Object.freeze(dependencies),
|
|
2591
|
+
packageManager: lockfileManager ?? declaredManager,
|
|
2592
|
+
intent: Object.freeze(intent)
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
// ../guard/src/doctor/rules.ts
|
|
2597
|
+
function diagnostic(code, level, subsystem, message, hint) {
|
|
2598
|
+
return { code, level, subsystem, message, hint, ruleId: code };
|
|
2599
|
+
}
|
|
2600
|
+
var builtInDoctorRules = Object.freeze([
|
|
2601
|
+
{
|
|
2602
|
+
id: "project.package-manager-lock",
|
|
2603
|
+
run: ({ snapshot }) => {
|
|
2604
|
+
const lockfiles = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock", "bun.lockb"].filter((file) => snapshot.files.has(file));
|
|
2605
|
+
return lockfiles.length > 1 ? [
|
|
2606
|
+
diagnostic(
|
|
2607
|
+
"DOCTOR_MULTIPLE_LOCKFILES",
|
|
2608
|
+
"warn",
|
|
2609
|
+
"compatibility",
|
|
2610
|
+
`Multiple package-manager lockfiles were found: ${lockfiles.join(", ")}.`,
|
|
2611
|
+
"Keep the lockfile for the package manager used by this project."
|
|
2612
|
+
)
|
|
2613
|
+
] : [];
|
|
2614
|
+
}
|
|
2615
|
+
},
|
|
2616
|
+
{
|
|
2617
|
+
id: "project.runtime-config",
|
|
2618
|
+
run: ({ snapshot }) => {
|
|
2619
|
+
const runtime = snapshot.intent.runtime;
|
|
2620
|
+
if (runtime === "node" && !snapshot.files.has("package.json")) {
|
|
2621
|
+
return [diagnostic(
|
|
2622
|
+
"DOCTOR_NODE_PACKAGE_JSON_MISSING",
|
|
2623
|
+
"warn",
|
|
2624
|
+
"runtime",
|
|
2625
|
+
"Node.js runtime is declared, but no package.json was found.",
|
|
2626
|
+
"Add a package.json or update .appinit/context.json."
|
|
2627
|
+
)];
|
|
2628
|
+
}
|
|
2629
|
+
if (runtime === "deno" && !snapshot.files.has("deno.json") && !snapshot.files.has("deno.jsonc")) {
|
|
2630
|
+
return [diagnostic(
|
|
2631
|
+
"DOCTOR_DENO_CONFIG_MISSING",
|
|
2632
|
+
"warn",
|
|
2633
|
+
"runtime",
|
|
2634
|
+
"Deno runtime is declared, but no deno.json or deno.jsonc was found.",
|
|
2635
|
+
"Add a Deno configuration file or update .appinit/context.json."
|
|
2636
|
+
)];
|
|
2637
|
+
}
|
|
2638
|
+
if (runtime === "bun" && !snapshot.files.has("bun.lock") && !snapshot.files.has("bun.lockb")) {
|
|
2639
|
+
return [diagnostic(
|
|
2640
|
+
"DOCTOR_BUN_LOCK_MISSING",
|
|
2641
|
+
"info",
|
|
2642
|
+
"runtime",
|
|
2643
|
+
"Bun runtime is declared, but no Bun lockfile was found.",
|
|
2644
|
+
"Generate a Bun lockfile if this project uses Bun for dependency management."
|
|
2645
|
+
)];
|
|
2646
|
+
}
|
|
2647
|
+
return [];
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
]);
|
|
2651
|
+
function runDoctorRules(context, rules = builtInDoctorRules) {
|
|
2652
|
+
const results = [];
|
|
2653
|
+
for (const rule of rules) {
|
|
2654
|
+
try {
|
|
2655
|
+
results.push(...rule.run(context));
|
|
2656
|
+
} catch (error) {
|
|
2657
|
+
results.push(diagnostic(
|
|
2658
|
+
"DOCTOR_RULE_FAILED",
|
|
2659
|
+
"warn",
|
|
2660
|
+
"doctor",
|
|
2661
|
+
`Doctor rule "${rule.id}" could not complete: ${error instanceof Error ? error.message : String(error)}`
|
|
2662
|
+
));
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
return results;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
// ../guard/src/doctor/doctor.ts
|
|
2669
|
+
import path20 from "path";
|
|
2670
|
+
var Doctor = class {
|
|
2671
|
+
constructor(options = {}) {
|
|
2672
|
+
this.options = options;
|
|
2673
|
+
}
|
|
2674
|
+
async diagnose() {
|
|
2675
|
+
const rootDir = path20.resolve(this.options.rootDir ?? process.cwd());
|
|
2676
|
+
const snapshot = await collectDoctorSnapshot(rootDir);
|
|
2677
|
+
const diagnostics = runDoctorRules({ rootDir, snapshot }, this.options.rules);
|
|
2678
|
+
const summary = { info: 0, warn: 0, error: 0, fatal: 0 };
|
|
2679
|
+
for (const item of diagnostics) summary[item.level] += 1;
|
|
2680
|
+
return Object.freeze({
|
|
2681
|
+
rootDir,
|
|
2682
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
2683
|
+
summary: Object.freeze(summary),
|
|
2684
|
+
ok: diagnostics.every((item) => item.level !== "error" && item.level !== "fatal")
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
|
|
2689
|
+
// ../guard/src/validation/validator.ts
|
|
2690
|
+
import path21 from "path";
|
|
2691
|
+
function createReport(rootDir, diagnostics) {
|
|
2692
|
+
return Object.freeze({
|
|
2693
|
+
rootDir,
|
|
2694
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
2695
|
+
hasErrors: diagnostics.some((item) => item.level === "error" || item.level === "fatal"),
|
|
2696
|
+
hasWarnings: diagnostics.some((item) => item.level === "warn"),
|
|
2697
|
+
hasAdvisories: diagnostics.some((item) => item.level === "info"),
|
|
2698
|
+
ok: diagnostics.every((item) => item.level !== "error" && item.level !== "fatal")
|
|
2699
|
+
});
|
|
2700
|
+
}
|
|
2701
|
+
async function validateProject(options = {}) {
|
|
2702
|
+
const rootDir = path21.resolve(options.rootDir ?? process.cwd());
|
|
2703
|
+
const snapshot = await collectDoctorSnapshot(rootDir);
|
|
2704
|
+
const diagnostics = [];
|
|
2705
|
+
if (snapshot.intent.runtime === "node" && !snapshot.files.has("package.json")) {
|
|
2706
|
+
diagnostics.push({
|
|
2707
|
+
code: "VALIDATION_NODE_PACKAGE_JSON_MISSING",
|
|
2708
|
+
level: "error",
|
|
2709
|
+
subsystem: "validation",
|
|
2710
|
+
ruleId: "project.node-package-json",
|
|
2711
|
+
filePath: "package.json",
|
|
2712
|
+
message: "The project declares the Node.js runtime but package.json is missing.",
|
|
2713
|
+
hint: "Add package.json or correct the runtime declaration in .appinit/context.json."
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
if (snapshot.intent.runtime === "deno" && !snapshot.files.has("deno.json") && !snapshot.files.has("deno.jsonc")) {
|
|
2717
|
+
diagnostics.push({
|
|
2718
|
+
code: "VALIDATION_DENO_CONFIG_MISSING",
|
|
2719
|
+
level: "error",
|
|
2720
|
+
subsystem: "validation",
|
|
2721
|
+
ruleId: "project.deno-config",
|
|
2722
|
+
message: "The project declares the Deno runtime but no Deno configuration file was found.",
|
|
2723
|
+
hint: "Add deno.json or deno.jsonc, or correct the runtime declaration."
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
return createReport(rootDir, diagnostics);
|
|
2727
|
+
}
|
|
2728
|
+
async function validateTemplate(options) {
|
|
2729
|
+
const rootDir = path21.resolve(options.templateDir);
|
|
2730
|
+
const diagnostics = [];
|
|
2731
|
+
try {
|
|
2732
|
+
await resolveTemplate(rootDir);
|
|
2733
|
+
} catch (error) {
|
|
2734
|
+
diagnostics.push({
|
|
2735
|
+
code: "VALIDATION_TEMPLATE_INVALID",
|
|
2736
|
+
level: "error",
|
|
2737
|
+
subsystem: "template",
|
|
2738
|
+
ruleId: "template.resolve",
|
|
2739
|
+
filePath: "appinit.template.json",
|
|
2740
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2741
|
+
hint: "Check the template manifest and ensure its declared template directory exists within the template root."
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
return createReport(rootDir, diagnostics);
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
// src/doctor.ts
|
|
2748
|
+
async function runDoctorCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
2749
|
+
const report = await new Doctor({ rootDir: path22.resolve(options.cwd ?? process.cwd()) }).diagnose();
|
|
2750
|
+
if (options.json) {
|
|
2751
|
+
io.write(`${JSON.stringify(report, null, 2)}
|
|
2752
|
+
`);
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
io.write("AppInit Doctor\n\n");
|
|
2756
|
+
if (!report.diagnostics.length) io.write(" \u2713 No project issues detected.\n");
|
|
2757
|
+
for (const item of report.diagnostics) {
|
|
2758
|
+
const marker = item.level === "info" ? "i" : item.level === "warn" ? "!" : "\u2717";
|
|
2759
|
+
io.write(` ${marker} [${item.level.toUpperCase()}] ${item.code}: ${item.message}
|
|
2760
|
+
`);
|
|
2761
|
+
if (item.hint) io.write(` Hint: ${item.hint}
|
|
2762
|
+
`);
|
|
2763
|
+
}
|
|
2764
|
+
io.write(`
|
|
2765
|
+
Summary: ${report.summary.info} info, ${report.summary.warn} warnings, ${report.summary.error} errors
|
|
2766
|
+
`);
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// src/history.ts
|
|
2770
|
+
async function readProjectHistory() {
|
|
2771
|
+
const value = await readAppInitJson(resolveAppInitHome(), "state.json");
|
|
2772
|
+
if (value === void 0) return { schemaVersion: 1, projects: [] };
|
|
2773
|
+
const validation = validateAppInitState(value);
|
|
2774
|
+
if (!validation.valid || !validation.state) {
|
|
2775
|
+
throw new Error(`Global AppInit history is invalid: ${validation.errors.join(" ")}`);
|
|
2776
|
+
}
|
|
2777
|
+
return validation.state;
|
|
2778
|
+
}
|
|
2779
|
+
async function runHistoryCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
2780
|
+
const state = await readProjectHistory();
|
|
2781
|
+
const projects = [...state.projects].sort(
|
|
2782
|
+
(left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt)
|
|
2783
|
+
);
|
|
2784
|
+
if (options.json) {
|
|
2785
|
+
io.write(`${JSON.stringify({ schemaVersion: state.schemaVersion, projects }, null, 2)}
|
|
2786
|
+
`);
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (projects.length === 0) {
|
|
2790
|
+
io.write("No project history found. Create a project with `appinit create` to get started.\n");
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
io.write(`AppInit Project History (${projects.length})
|
|
2794
|
+
|
|
2795
|
+
`);
|
|
2796
|
+
projects.forEach((project, index) => {
|
|
2797
|
+
io.write(
|
|
2798
|
+
`${index + 1}. ${project.templateId}
|
|
2799
|
+
Path: ${project.path}
|
|
2800
|
+
Created: ${project.createdAt}
|
|
2801
|
+
|
|
2802
|
+
`
|
|
2803
|
+
);
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// src/state-path.ts
|
|
2808
|
+
import { stat as stat3 } from "fs/promises";
|
|
2809
|
+
async function inspectAppInitStatePath(options = {}) {
|
|
2810
|
+
const env = options.env ?? process.env;
|
|
2811
|
+
const resolvedPath = resolveAppInitHome({ env, homeDirectory: options.homeDirectory });
|
|
2812
|
+
let exists2 = false;
|
|
2813
|
+
let isDirectory = false;
|
|
2814
|
+
try {
|
|
2815
|
+
const details = await stat3(resolvedPath);
|
|
2816
|
+
exists2 = true;
|
|
2817
|
+
isDirectory = details.isDirectory();
|
|
2818
|
+
} catch (error) {
|
|
2819
|
+
if (error.code !== "ENOENT") throw error;
|
|
2820
|
+
}
|
|
2821
|
+
return Object.freeze({
|
|
2822
|
+
path: resolvedPath,
|
|
2823
|
+
source: env.APPINIT_HOME === void 0 ? "default" : "APPINIT_HOME",
|
|
2824
|
+
exists: exists2,
|
|
2825
|
+
isDirectory
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
async function runStatePathCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
2829
|
+
const info = await inspectAppInitStatePath(options);
|
|
2830
|
+
io.write("AppInit Global State Path\n\n");
|
|
2831
|
+
io.write(`Path: ${info.path}
|
|
2832
|
+
`);
|
|
2833
|
+
io.write(`Source: ${info.source === "default" ? "default (~/.appinit)" : "APPINIT_HOME environment variable"}
|
|
2834
|
+
`);
|
|
2835
|
+
if (!info.exists) {
|
|
2836
|
+
io.write("Status: not created yet\n");
|
|
2837
|
+
} else if (!info.isDirectory) {
|
|
2838
|
+
io.write("Status: path exists but is not a directory\n");
|
|
2839
|
+
} else {
|
|
2840
|
+
io.write("Status: directory exists\n");
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
// src/state-inspect.ts
|
|
2845
|
+
async function inspectAppInitState(options = {}) {
|
|
2846
|
+
const env = options.env ?? process.env;
|
|
2847
|
+
const home = resolveAppInitHome({ env, homeDirectory: options.homeDirectory });
|
|
2848
|
+
const diagnostics = [];
|
|
2849
|
+
try {
|
|
2850
|
+
const value = await readAppInitJson(home, "state.json");
|
|
2851
|
+
if (value === void 0) {
|
|
2852
|
+
diagnostics.push("Global state file has not been created yet.");
|
|
2853
|
+
return Object.freeze({
|
|
2854
|
+
path: home,
|
|
2855
|
+
source: env.APPINIT_HOME === void 0 ? "default" : "APPINIT_HOME",
|
|
2856
|
+
stateFile: "missing",
|
|
2857
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2858
|
+
});
|
|
2859
|
+
}
|
|
2860
|
+
const validation = validateAppInitState(value);
|
|
2861
|
+
if (!validation.valid || !validation.state) {
|
|
2862
|
+
diagnostics.push(...validation.errors);
|
|
2863
|
+
return Object.freeze({
|
|
2864
|
+
path: home,
|
|
2865
|
+
source: env.APPINIT_HOME === void 0 ? "default" : "APPINIT_HOME",
|
|
2866
|
+
stateFile: "invalid",
|
|
2867
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
return Object.freeze({
|
|
2871
|
+
path: home,
|
|
2872
|
+
source: env.APPINIT_HOME === void 0 ? "default" : "APPINIT_HOME",
|
|
2873
|
+
stateFile: "valid",
|
|
2874
|
+
schemaVersion: validation.state.schemaVersion,
|
|
2875
|
+
projectCount: validation.state.projects.length,
|
|
2876
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2877
|
+
});
|
|
2878
|
+
} catch (error) {
|
|
2879
|
+
diagnostics.push(error instanceof Error ? error.message : String(error));
|
|
2880
|
+
return Object.freeze({
|
|
2881
|
+
path: home,
|
|
2882
|
+
source: env.APPINIT_HOME === void 0 ? "default" : "APPINIT_HOME",
|
|
2883
|
+
stateFile: "invalid",
|
|
2884
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
async function runStateInspectCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
2889
|
+
const report = await inspectAppInitState(options);
|
|
2890
|
+
if (options.json) {
|
|
2891
|
+
io.write(`${JSON.stringify(report, null, 2)}
|
|
2892
|
+
`);
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2895
|
+
io.write("AppInit Global State Inspection\n\n");
|
|
2896
|
+
io.write(`Path: ${report.path}
|
|
2897
|
+
`);
|
|
2898
|
+
io.write(`Source: ${report.source === "default" ? "default (~/.appinit)" : "APPINIT_HOME environment variable"}
|
|
2899
|
+
`);
|
|
2900
|
+
io.write(`State file: ${report.stateFile}
|
|
2901
|
+
`);
|
|
2902
|
+
if (report.schemaVersion !== void 0) io.write(`Schema version: ${report.schemaVersion}
|
|
2903
|
+
`);
|
|
2904
|
+
if (report.projectCount !== void 0) io.write(`Recorded projects: ${report.projectCount}
|
|
2905
|
+
`);
|
|
2906
|
+
if (report.diagnostics.length) {
|
|
2907
|
+
io.write("\nDiagnostics\n");
|
|
2908
|
+
for (const diagnostic3 of report.diagnostics) io.write(` ! ${diagnostic3}
|
|
2909
|
+
`);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
// src/state-doctor.ts
|
|
2914
|
+
import { access as access3, stat as stat4 } from "fs/promises";
|
|
2915
|
+
import { constants } from "fs";
|
|
2916
|
+
import path23 from "path";
|
|
2917
|
+
function diagnostic2(code, level, message, hint) {
|
|
2918
|
+
return Object.freeze({ code, level, message, ...hint ? { hint } : {} });
|
|
2919
|
+
}
|
|
2920
|
+
function isMissing(error) {
|
|
2921
|
+
return error.code === "ENOENT";
|
|
2922
|
+
}
|
|
2923
|
+
async function diagnoseAppInitState(options = {}) {
|
|
2924
|
+
const inspection = await inspectAppInitState(options);
|
|
2925
|
+
const diagnostics = [];
|
|
2926
|
+
const stateRoot = inspection.path;
|
|
2927
|
+
let rootIsDirectory = false;
|
|
2928
|
+
try {
|
|
2929
|
+
const details = await stat4(stateRoot);
|
|
2930
|
+
if (!details.isDirectory()) {
|
|
2931
|
+
diagnostics.push(diagnostic2(
|
|
2932
|
+
"APPINIT_STATE_HOME_NOT_DIRECTORY",
|
|
2933
|
+
"error",
|
|
2934
|
+
"The configured global state path exists but is not a directory.",
|
|
2935
|
+
"Set APPINIT_HOME to a directory or remove the conflicting file."
|
|
2936
|
+
));
|
|
2937
|
+
} else {
|
|
2938
|
+
rootIsDirectory = true;
|
|
2939
|
+
try {
|
|
2940
|
+
await access3(stateRoot, constants.R_OK | constants.X_OK);
|
|
2941
|
+
} catch {
|
|
2942
|
+
diagnostics.push(diagnostic2(
|
|
2943
|
+
"APPINIT_STATE_HOME_NOT_ACCESSIBLE",
|
|
2944
|
+
"error",
|
|
2945
|
+
"The global state directory is not readable or searchable by this process.",
|
|
2946
|
+
"Check directory ownership and access permissions."
|
|
2947
|
+
));
|
|
2948
|
+
}
|
|
2949
|
+
if (process.platform !== "win32" && (details.mode & 63) !== 0) {
|
|
2950
|
+
diagnostics.push(diagnostic2(
|
|
2951
|
+
"APPINIT_STATE_HOME_PERMISSIONS_BROAD",
|
|
2952
|
+
"warn",
|
|
2953
|
+
"The global state directory grants permissions to group or other users.",
|
|
2954
|
+
"Restrict the directory to the current user (typically mode 700)."
|
|
2955
|
+
));
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
} catch (error) {
|
|
2959
|
+
if (isMissing(error)) {
|
|
2960
|
+
diagnostics.push(diagnostic2(
|
|
2961
|
+
"APPINIT_STATE_HOME_MISSING",
|
|
2962
|
+
"warn",
|
|
2963
|
+
"The global state directory has not been created yet.",
|
|
2964
|
+
"This is normal before AppInit first stores global state."
|
|
2965
|
+
));
|
|
2966
|
+
} else {
|
|
2967
|
+
diagnostics.push(diagnostic2(
|
|
2968
|
+
"APPINIT_STATE_HOME_UNAVAILABLE",
|
|
2969
|
+
"error",
|
|
2970
|
+
"The global state directory could not be inspected.",
|
|
2971
|
+
"Check the configured path and filesystem permissions."
|
|
2972
|
+
));
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
if (inspection.stateFile === "missing") {
|
|
2976
|
+
diagnostics.push(diagnostic2(
|
|
2977
|
+
"APPINIT_STATE_FILE_MISSING",
|
|
2978
|
+
"warn",
|
|
2979
|
+
"The global state file has not been created yet.",
|
|
2980
|
+
"AppInit will create it when a command first needs to persist global state."
|
|
2981
|
+
));
|
|
2982
|
+
} else if (inspection.stateFile === "invalid") {
|
|
2983
|
+
diagnostics.push(diagnostic2(
|
|
2984
|
+
"APPINIT_STATE_FILE_INVALID",
|
|
2985
|
+
"error",
|
|
2986
|
+
inspection.diagnostics.join(" "),
|
|
2987
|
+
"Back up the file before attempting any manual recovery; this command does not repair state."
|
|
2988
|
+
));
|
|
2989
|
+
} else {
|
|
2990
|
+
diagnostics.push(diagnostic2(
|
|
2991
|
+
"APPINIT_STATE_FILE_VALID",
|
|
2992
|
+
"info",
|
|
2993
|
+
`Global state file is valid (schema version ${inspection.schemaVersion}; ${inspection.projectCount} recorded projects).`
|
|
2994
|
+
));
|
|
2995
|
+
if (rootIsDirectory) {
|
|
2996
|
+
const filePath = path23.join(stateRoot, "state.json");
|
|
2997
|
+
try {
|
|
2998
|
+
const details = await stat4(filePath);
|
|
2999
|
+
await access3(filePath, constants.R_OK);
|
|
3000
|
+
if (process.platform !== "win32" && (details.mode & 63) !== 0) {
|
|
3001
|
+
diagnostics.push(diagnostic2(
|
|
3002
|
+
"APPINIT_STATE_FILE_PERMISSIONS_BROAD",
|
|
3003
|
+
"warn",
|
|
3004
|
+
"The global state file grants permissions to group or other users.",
|
|
3005
|
+
"Restrict the file to the current user (typically mode 600)."
|
|
3006
|
+
));
|
|
3007
|
+
}
|
|
3008
|
+
} catch {
|
|
3009
|
+
diagnostics.push(diagnostic2(
|
|
3010
|
+
"APPINIT_STATE_FILE_NOT_ACCESSIBLE",
|
|
3011
|
+
"error",
|
|
3012
|
+
"The global state file could not be inspected or read.",
|
|
3013
|
+
"Check file ownership and access permissions."
|
|
3014
|
+
));
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
const summary = Object.freeze({
|
|
3019
|
+
info: diagnostics.filter((item) => item.level === "info").length,
|
|
3020
|
+
warn: diagnostics.filter((item) => item.level === "warn").length,
|
|
3021
|
+
error: diagnostics.filter((item) => item.level === "error").length
|
|
3022
|
+
});
|
|
3023
|
+
return Object.freeze({
|
|
3024
|
+
path: stateRoot,
|
|
3025
|
+
source: inspection.source,
|
|
3026
|
+
stateFile: inspection.stateFile,
|
|
3027
|
+
ok: summary.error === 0,
|
|
3028
|
+
diagnostics: Object.freeze(diagnostics),
|
|
3029
|
+
summary
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
async function runStateDoctorCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
3033
|
+
const report = await diagnoseAppInitState(options);
|
|
3034
|
+
if (options.json) {
|
|
3035
|
+
io.write(`${JSON.stringify(report, null, 2)}
|
|
3036
|
+
`);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
io.write("AppInit Global State Doctor\n\n");
|
|
3040
|
+
io.write(`Path: ${report.path}
|
|
3041
|
+
`);
|
|
3042
|
+
io.write(`Source: ${report.source === "default" ? "default (~/.appinit)" : "APPINIT_HOME environment variable"}
|
|
3043
|
+
`);
|
|
3044
|
+
for (const item of report.diagnostics) {
|
|
3045
|
+
const marker = item.level === "info" ? "i" : item.level === "warn" ? "!" : "\u2717";
|
|
3046
|
+
io.write(` ${marker} [${item.level.toUpperCase()}] ${item.code}: ${item.message}
|
|
3047
|
+
`);
|
|
3048
|
+
if (item.hint) io.write(` Hint: ${item.hint}
|
|
3049
|
+
`);
|
|
3050
|
+
}
|
|
3051
|
+
io.write(`
|
|
3052
|
+
Summary: ${report.summary.info} info, ${report.summary.warn} warnings, ${report.summary.error} errors
|
|
3053
|
+
`);
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
// src/template-author.ts
|
|
3057
|
+
import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
|
|
3058
|
+
import path24 from "path";
|
|
3059
|
+
function toSlug(value) {
|
|
3060
|
+
return value.toLocaleLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3061
|
+
}
|
|
3062
|
+
function toDisplayName(slug) {
|
|
3063
|
+
return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toLocaleUpperCase() + part.slice(1)).join(" ");
|
|
3064
|
+
}
|
|
3065
|
+
async function createTemplateScaffold(directory) {
|
|
3066
|
+
const root = path24.resolve(directory);
|
|
3067
|
+
const slug = toSlug(path24.basename(root));
|
|
3068
|
+
if (!slug) throw new Error("Template directory name must contain letters or numbers.");
|
|
3069
|
+
const name = toDisplayName(slug);
|
|
3070
|
+
const id = `custom.template-${slug}`;
|
|
3071
|
+
const manifest = {
|
|
3072
|
+
schemaVersion: 1,
|
|
3073
|
+
id,
|
|
3074
|
+
version: "0.1.0",
|
|
3075
|
+
kind: "project",
|
|
3076
|
+
atsVersion: "2.0",
|
|
3077
|
+
name,
|
|
3078
|
+
description: "A reusable AppInit project template.",
|
|
3079
|
+
category: "custom",
|
|
3080
|
+
language: "typescript",
|
|
3081
|
+
packageName: `@appinit/template-${slug}`,
|
|
3082
|
+
renderer: { engine: "liquid" },
|
|
3083
|
+
paths: { template: "template", variables: "variables" },
|
|
3084
|
+
tags: ["custom", "typescript"]
|
|
3085
|
+
};
|
|
3086
|
+
await mkdir5(path24.dirname(root), { recursive: true });
|
|
3087
|
+
await mkdir5(root);
|
|
3088
|
+
await mkdir5(path24.join(root, "template"));
|
|
3089
|
+
await mkdir5(path24.join(root, "variables"));
|
|
3090
|
+
await Promise.all([
|
|
3091
|
+
writeFile4(path24.join(root, "appinit.template.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3092
|
+
`, { flag: "wx" }),
|
|
3093
|
+
writeFile4(path24.join(root, "variables", "defaults.json"), `${JSON.stringify({ projectName: slug }, null, 2)}
|
|
3094
|
+
`, { flag: "wx" }),
|
|
3095
|
+
writeFile4(path24.join(root, "variables", "schema.json"), `${JSON.stringify({
|
|
3096
|
+
projectName: { type: "string", required: true, minLength: 1 }
|
|
3097
|
+
}, null, 2)}
|
|
3098
|
+
`, { flag: "wx" }),
|
|
3099
|
+
writeFile4(path24.join(root, "template", "README.md"), `# {{ projectName }}
|
|
3100
|
+
|
|
3101
|
+
Generated from the ${name} AppInit template.
|
|
3102
|
+
`, { flag: "wx" }),
|
|
3103
|
+
writeFile4(path24.join(root, "README.md"), `# ${name}
|
|
3104
|
+
|
|
3105
|
+
AppInit template authoring scaffold.
|
|
3106
|
+
|
|
3107
|
+
- Edit \`appinit.template.json\` to update template metadata.
|
|
3108
|
+
- Add generated files under \`template/\`.
|
|
3109
|
+
- Define variable defaults and rules under \`variables/\`.
|
|
3110
|
+
- Validate this directory with \`appinit validate --template .\`.
|
|
3111
|
+
|
|
3112
|
+
Preview generation with \`appinit create <template-ref> <target> --dry-run\`.
|
|
3113
|
+
`, { flag: "wx" })
|
|
3114
|
+
]);
|
|
3115
|
+
return Object.freeze({ root, id, name });
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
// src/template-commands.ts
|
|
3119
|
+
import path25 from "path";
|
|
3120
|
+
function searchLocalTemplates(templates, query) {
|
|
3121
|
+
const terms = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
|
|
3122
|
+
if (terms.length === 0) return templates;
|
|
3123
|
+
return templates.filter((template) => {
|
|
3124
|
+
const manifest = template.template.manifest;
|
|
3125
|
+
const searchable = [
|
|
3126
|
+
template.id,
|
|
3127
|
+
template.name,
|
|
3128
|
+
template.description,
|
|
3129
|
+
template.category,
|
|
3130
|
+
manifest.language,
|
|
3131
|
+
...manifest.tags
|
|
3132
|
+
].join(" ").toLocaleLowerCase();
|
|
3133
|
+
return terms.every((term) => searchable.includes(term));
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
function findLocalTemplate(templates, reference) {
|
|
3137
|
+
const normalized = reference.trim().toLocaleLowerCase();
|
|
3138
|
+
return templates.find(
|
|
3139
|
+
(template) => [
|
|
3140
|
+
template.id,
|
|
3141
|
+
template.name,
|
|
3142
|
+
template.template.manifest.packageName,
|
|
3143
|
+
path25.basename(template.root)
|
|
3144
|
+
].some((value) => value?.toLocaleLowerCase() === normalized)
|
|
3145
|
+
);
|
|
3146
|
+
}
|
|
3147
|
+
function formatTemplate(template) {
|
|
3148
|
+
const manifest = template.template.manifest;
|
|
3149
|
+
return [
|
|
3150
|
+
` ${template.id}`,
|
|
3151
|
+
` ${template.name} \u2014 ${template.description}`,
|
|
3152
|
+
` ${template.category} \xB7 ${manifest.language} \xB7 v${manifest.version}`
|
|
3153
|
+
].join("\n");
|
|
3154
|
+
}
|
|
3155
|
+
function formatTemplateList(templates, heading = "Available local templates") {
|
|
3156
|
+
if (templates.length === 0)
|
|
3157
|
+
return "No local templates found. Check your templates directory.";
|
|
3158
|
+
return [
|
|
3159
|
+
`${heading} (${templates.length})`,
|
|
3160
|
+
"",
|
|
3161
|
+
...templates.map(formatTemplate),
|
|
3162
|
+
""
|
|
3163
|
+
].join("\n");
|
|
3164
|
+
}
|
|
3165
|
+
function formatTemplateInfo(template) {
|
|
3166
|
+
const manifest = template.template.manifest;
|
|
3167
|
+
return [
|
|
3168
|
+
template.name,
|
|
3169
|
+
"",
|
|
3170
|
+
`ID: ${template.id}`,
|
|
3171
|
+
`Version: ${manifest.version}`,
|
|
3172
|
+
`Category: ${template.category}`,
|
|
3173
|
+
`Language: ${manifest.language}`,
|
|
3174
|
+
`Description: ${template.description}`,
|
|
3175
|
+
`Tags: ${manifest.tags.join(", ") || "\u2014"}`,
|
|
3176
|
+
`Directory: ${template.root}`,
|
|
3177
|
+
""
|
|
3178
|
+
].join("\n");
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// src/remote-template.ts
|
|
3182
|
+
import { mkdir as mkdir6, realpath as realpath8, rm as rm5 } from "fs/promises";
|
|
3183
|
+
import path26 from "path";
|
|
3184
|
+
function createClient(options) {
|
|
3185
|
+
return new TemplateRegistryClient({
|
|
3186
|
+
catalogUrl: options.registryUrl,
|
|
3187
|
+
cacheDirectory: options.cacheDirectory ?? path26.join(resolveAppInitHome(), "registry-cache"),
|
|
3188
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
async function listRemoteTemplates(options) {
|
|
3192
|
+
const catalog = await createClient(options).fetchCatalog();
|
|
3193
|
+
return Object.freeze(catalog.templates.map((entry) => Object.freeze({
|
|
3194
|
+
id: entry.id,
|
|
3195
|
+
version: entry.version,
|
|
3196
|
+
name: entry.name,
|
|
3197
|
+
description: entry.description,
|
|
3198
|
+
category: entry.category,
|
|
3199
|
+
language: entry.language,
|
|
3200
|
+
tags: entry.tags
|
|
3201
|
+
})));
|
|
3202
|
+
}
|
|
3203
|
+
async function installRemoteTemplate(options) {
|
|
3204
|
+
const client = createClient(options);
|
|
3205
|
+
const catalog = await client.fetchCatalog();
|
|
3206
|
+
const candidates = catalog.templates.filter(
|
|
3207
|
+
(entry2) => entry2.id === options.templateId && (!options.version || entry2.version === options.version)
|
|
3208
|
+
);
|
|
3209
|
+
if (candidates.length === 0) {
|
|
3210
|
+
throw new Error(`Template "${options.templateId}"${options.version ? ` version "${options.version}"` : ""} was not found in the registry.`);
|
|
3211
|
+
}
|
|
3212
|
+
if (candidates.length !== 1) {
|
|
3213
|
+
throw new Error(`Registry contains multiple versions for "${options.templateId}". Specify --version explicitly.`);
|
|
3214
|
+
}
|
|
3215
|
+
const entry = candidates[0];
|
|
3216
|
+
const archive = await client.downloadTemplate(entry);
|
|
3217
|
+
const templatesDirectory = path26.resolve(
|
|
3218
|
+
options.templatesDirectory ?? path26.join(resolveAppInitHome(), "templates")
|
|
3219
|
+
);
|
|
3220
|
+
const destination = path26.join(templatesDirectory, entry.id, entry.version);
|
|
3221
|
+
await mkdir6(templatesDirectory, { recursive: true });
|
|
3222
|
+
let extractedDirectoryCreated = false;
|
|
3223
|
+
try {
|
|
3224
|
+
const extracted = await extractTemplateArchive({
|
|
3225
|
+
archivePath: archive.path,
|
|
3226
|
+
destination
|
|
3227
|
+
});
|
|
3228
|
+
extractedDirectoryCreated = true;
|
|
3229
|
+
const template = await resolveTemplate(extracted.destination);
|
|
3230
|
+
if (template.manifest.id !== entry.id || template.manifest.version !== entry.version) {
|
|
3231
|
+
throw new Error(
|
|
3232
|
+
`Downloaded manifest identity mismatch: expected ${entry.id}@${entry.version}, received ${template.manifest.id}@${template.manifest.version}.`
|
|
3233
|
+
);
|
|
3234
|
+
}
|
|
3235
|
+
const canonical = await realpath8(extracted.destination);
|
|
3236
|
+
return Object.freeze({
|
|
3237
|
+
id: entry.id,
|
|
3238
|
+
version: entry.version,
|
|
3239
|
+
directory: canonical,
|
|
3240
|
+
cached: archive.cached
|
|
3241
|
+
});
|
|
3242
|
+
} catch (error) {
|
|
3243
|
+
if (extractedDirectoryCreated) {
|
|
3244
|
+
await rm5(destination, { recursive: true, force: true });
|
|
3245
|
+
}
|
|
3246
|
+
throw error;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
// src/template.ts
|
|
3251
|
+
function output(value, json) {
|
|
3252
|
+
return json ? `${JSON.stringify(value, null, 2)}
|
|
3253
|
+
` : String(value);
|
|
3254
|
+
}
|
|
3255
|
+
function usage() {
|
|
3256
|
+
return [
|
|
3257
|
+
"Usage: appinit template <init|list|info|search|remote-list|install> [arguments] [options]",
|
|
3258
|
+
"",
|
|
3259
|
+
"Commands:",
|
|
3260
|
+
" init <directory> Create a starter template authoring scaffold",
|
|
3261
|
+
" list List validated local templates",
|
|
3262
|
+
" info <template-ref> Show details and variable configuration",
|
|
3263
|
+
" search <query> Search local templates by name, ID, tags, and description",
|
|
3264
|
+
" remote-list List templates from a remote registry",
|
|
3265
|
+
" install <template-id> Download, verify, and install a registry template",
|
|
3266
|
+
"",
|
|
3267
|
+
"Options:",
|
|
3268
|
+
" --json Print machine-readable JSON",
|
|
3269
|
+
" --template-dir <path> Use a specific local template directory",
|
|
3270
|
+
" --registry-url <url> HTTPS registry catalog URL (required for remote commands)",
|
|
3271
|
+
" --version <version> Install this exact template version",
|
|
3272
|
+
""
|
|
3273
|
+
].join("\n");
|
|
3274
|
+
}
|
|
3275
|
+
function parseOptions(args) {
|
|
3276
|
+
const positional = [];
|
|
3277
|
+
let json = false;
|
|
3278
|
+
let directory;
|
|
3279
|
+
let registryUrl;
|
|
3280
|
+
let version;
|
|
3281
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3282
|
+
const arg = args[index];
|
|
3283
|
+
if (arg === "--json") json = true;
|
|
3284
|
+
else if (arg === "--help" || arg === "-h") positional.push("help");
|
|
3285
|
+
else if (arg === "--template-dir" || arg === "--registry-url" || arg === "--version") {
|
|
3286
|
+
const value = args[++index];
|
|
3287
|
+
if (!value || value.startsWith("--")) throw new Error(`${arg} requires a value.`);
|
|
3288
|
+
if (arg === "--template-dir") directory = value;
|
|
3289
|
+
else if (arg === "--registry-url") registryUrl = value;
|
|
3290
|
+
else version = value;
|
|
3291
|
+
} else if (arg.startsWith("-")) throw new Error(`Unknown template option: ${arg}`);
|
|
3292
|
+
else positional.push(arg);
|
|
3293
|
+
}
|
|
3294
|
+
return {
|
|
3295
|
+
positional,
|
|
3296
|
+
json,
|
|
3297
|
+
...directory ? { directory } : {},
|
|
3298
|
+
...registryUrl ? { registryUrl } : {},
|
|
3299
|
+
...version ? { version } : {}
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
async function runTemplateCommand(args, options = {}) {
|
|
3303
|
+
const write = options.write ?? ((message) => process.stdout.write(message));
|
|
3304
|
+
const parsed = parseOptions(args);
|
|
3305
|
+
const [subcommand, ...rest] = parsed.positional;
|
|
3306
|
+
if (!subcommand || subcommand === "--help" || subcommand === "help") {
|
|
3307
|
+
write(usage());
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
if (!["init", "list", "info", "search", "remote-list", "install"].includes(subcommand)) {
|
|
3311
|
+
throw new Error(`Unknown template command "${subcommand}". Run "appinit template --help".`);
|
|
3312
|
+
}
|
|
3313
|
+
if (subcommand === "remote-list" || subcommand === "install") {
|
|
3314
|
+
if (!parsed.registryUrl) throw new Error(`${subcommand} requires --registry-url <https-url>.`);
|
|
3315
|
+
if (parsed.directory) throw new Error(`${subcommand} does not use --template-dir.`);
|
|
3316
|
+
if (subcommand === "remote-list") {
|
|
3317
|
+
if (rest.length) throw new Error("template remote-list does not accept positional arguments.");
|
|
3318
|
+
if (parsed.version) throw new Error("template remote-list does not accept --version.");
|
|
3319
|
+
const entries = await listRemoteTemplates({ registryUrl: parsed.registryUrl });
|
|
3320
|
+
if (parsed.json) write(output(entries, true));
|
|
3321
|
+
else if (!entries.length) write("No remote templates found.\n");
|
|
3322
|
+
else write(entries.map(
|
|
3323
|
+
(item) => `${item.id}@${item.version} ${item.name}
|
|
3324
|
+
${item.description}
|
|
3325
|
+
${item.category} \xB7 ${item.language} \xB7 ${item.tags.join(", ")}`
|
|
3326
|
+
).join("\n\n") + "\n");
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
if (rest.length !== 1) throw new Error("template install requires exactly one template ID.");
|
|
3330
|
+
const installed = await installRemoteTemplate({
|
|
3331
|
+
registryUrl: parsed.registryUrl,
|
|
3332
|
+
templateId: rest[0],
|
|
3333
|
+
...parsed.version ? { version: parsed.version } : {}
|
|
3334
|
+
});
|
|
3335
|
+
if (parsed.json) write(output(installed, true));
|
|
3336
|
+
else write(`Installed ${installed.id}@${installed.version}
|
|
3337
|
+
Directory: ${installed.directory}
|
|
3338
|
+
Cache: ${installed.cached ? "verified archive reused" : "downloaded and verified"}
|
|
3339
|
+
`);
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
if (parsed.registryUrl || parsed.version) {
|
|
3343
|
+
throw new Error("--registry-url and --version are only valid with template remote-list or template install.");
|
|
3344
|
+
}
|
|
3345
|
+
if (subcommand === "init") {
|
|
3346
|
+
if (rest.length !== 1) throw new Error("template init requires exactly one target directory.");
|
|
3347
|
+
const scaffold = await createTemplateScaffold(rest[0]);
|
|
3348
|
+
if (parsed.json) write(output(scaffold, true));
|
|
3349
|
+
else write(`Created template scaffold at ${scaffold.root}
|
|
3350
|
+
|
|
3351
|
+
Template ID: ${scaffold.id}
|
|
3352
|
+
Next: edit the manifest and files, then run appinit validate --template ${scaffold.root}
|
|
3353
|
+
`);
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
const directory = resolveTemplateDirectory(parsed.directory ?? options.templateDirectory);
|
|
3357
|
+
const templates = await discoverLocalTemplates(directory);
|
|
3358
|
+
if (subcommand === "list") {
|
|
3359
|
+
if (rest.length) throw new Error("template list does not accept positional arguments.");
|
|
3360
|
+
const entries = templates.map(({ id, name, description, category, template }) => ({
|
|
3361
|
+
id,
|
|
3362
|
+
name,
|
|
3363
|
+
version: template.manifest.version,
|
|
3364
|
+
category,
|
|
3365
|
+
description
|
|
3366
|
+
}));
|
|
3367
|
+
if (parsed.json) write(output(entries, true));
|
|
3368
|
+
else if (!entries.length) write("No valid local templates found.\n");
|
|
3369
|
+
else write(entries.map((item) => `${item.id} ${item.name} (${item.version})
|
|
3370
|
+
${item.description}`).join("\n\n") + "\n");
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
if (subcommand === "search") {
|
|
3374
|
+
const query = rest.join(" ").trim();
|
|
3375
|
+
if (!query) throw new Error("template search requires a query.");
|
|
3376
|
+
const matches = searchLocalTemplates(templates, query);
|
|
3377
|
+
const entries = matches.map(({ id, name, description, category, template }) => ({
|
|
3378
|
+
id,
|
|
3379
|
+
name,
|
|
3380
|
+
version: template.manifest.version,
|
|
3381
|
+
category,
|
|
3382
|
+
description
|
|
3383
|
+
}));
|
|
3384
|
+
if (parsed.json) write(output(entries, true));
|
|
3385
|
+
else if (!entries.length) write(`No templates found for "${query}".
|
|
3386
|
+
`);
|
|
3387
|
+
else write(entries.map((item) => `${item.id} ${item.name} (${item.version})
|
|
3388
|
+
${item.description}`).join("\n\n") + "\n");
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
if (rest.length !== 1) throw new Error("template info requires exactly one template reference.");
|
|
3392
|
+
const ref = rest[0];
|
|
3393
|
+
const selected = findLocalTemplate(templates, ref);
|
|
3394
|
+
if (!selected) throw new Error(`Template "${ref}" was not found among valid local templates.`);
|
|
3395
|
+
const variables = await loadTemplateVariables(selected.root, selected.template.manifest);
|
|
3396
|
+
const details = {
|
|
3397
|
+
id: selected.id,
|
|
3398
|
+
name: selected.name,
|
|
3399
|
+
version: selected.template.manifest.version,
|
|
3400
|
+
description: selected.description,
|
|
3401
|
+
category: selected.category,
|
|
3402
|
+
kind: selected.template.manifest.kind,
|
|
3403
|
+
language: selected.template.manifest.language,
|
|
3404
|
+
packageName: selected.template.manifest.packageName,
|
|
3405
|
+
atsVersion: selected.template.manifest.atsVersion,
|
|
3406
|
+
tags: selected.template.manifest.tags,
|
|
3407
|
+
renderer: selected.template.manifest.renderer.engine,
|
|
3408
|
+
variables: { defaults: variables.defaults, schema: variables.schema }
|
|
3409
|
+
};
|
|
3410
|
+
if (parsed.json) write(output(details, true));
|
|
3411
|
+
else {
|
|
3412
|
+
write([
|
|
3413
|
+
`${details.name} (${details.id})`,
|
|
3414
|
+
details.description,
|
|
3415
|
+
`Version: ${details.version}`,
|
|
3416
|
+
`Category: ${details.category} | Kind: ${details.kind} | Language: ${details.language}`,
|
|
3417
|
+
`Package: ${details.packageName}`,
|
|
3418
|
+
`ATS compatibility: ${details.atsVersion}`,
|
|
3419
|
+
`Renderer: ${details.renderer}`,
|
|
3420
|
+
`Tags: ${details.tags.join(", ")}`,
|
|
3421
|
+
`Variable defaults: ${Object.keys(variables.defaults).length}`,
|
|
3422
|
+
`Variable schema: ${Object.keys(variables.schema).length}`,
|
|
3423
|
+
"",
|
|
3424
|
+
"Defaults:",
|
|
3425
|
+
JSON.stringify(variables.defaults, null, 2),
|
|
3426
|
+
"",
|
|
3427
|
+
"Schema:",
|
|
3428
|
+
JSON.stringify(variables.schema, null, 2),
|
|
3429
|
+
""
|
|
3430
|
+
].join("\n"));
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
// src/validate.ts
|
|
3435
|
+
import path27 from "path";
|
|
3436
|
+
async function runValidateCommand(options = {}, io = { write: (message) => process.stdout.write(message) }) {
|
|
3437
|
+
const report = options.templateDir ? await validateTemplate({ templateDir: path27.resolve(options.cwd ?? process.cwd(), options.templateDir) }) : await validateProject({ rootDir: path27.resolve(options.cwd ?? process.cwd()) });
|
|
3438
|
+
if (options.json) {
|
|
3439
|
+
io.write(`${JSON.stringify(report, null, 2)}
|
|
3440
|
+
`);
|
|
3441
|
+
return;
|
|
3442
|
+
}
|
|
3443
|
+
io.write(`AppInit Validation: ${report.rootDir}
|
|
3444
|
+
|
|
3445
|
+
`);
|
|
3446
|
+
if (!report.diagnostics.length) io.write(" \u2713 No validation issues found.\n");
|
|
3447
|
+
for (const item of report.diagnostics) {
|
|
3448
|
+
const marker = item.level === "info" ? "i" : item.level === "warn" ? "!" : "\u2717";
|
|
3449
|
+
io.write(` ${marker} [${item.level.toUpperCase()}] ${item.code}: ${item.message}
|
|
3450
|
+
`);
|
|
3451
|
+
if (item.filePath) io.write(` File: ${item.filePath}
|
|
3452
|
+
`);
|
|
3453
|
+
if (item.hint) io.write(` Hint: ${item.hint}
|
|
3454
|
+
`);
|
|
3455
|
+
}
|
|
3456
|
+
io.write(`
|
|
3457
|
+
Result: ${report.ok ? "VALID" : "INVALID"}
|
|
3458
|
+
`);
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
// src/wizard.ts
|
|
3462
|
+
import path28 from "path";
|
|
3463
|
+
|
|
3464
|
+
// src/prompts.ts
|
|
3465
|
+
async function textPrompt(io, message, fallback) {
|
|
3466
|
+
const answer = (await io.ask(message, fallback)).trim();
|
|
3467
|
+
return answer || fallback || "";
|
|
3468
|
+
}
|
|
3469
|
+
async function confirmPrompt(io, message, fallback = false) {
|
|
3470
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
3471
|
+
const raw = (await io.ask(`${message} (yes/no)`, fallback ? "yes" : "no")).trim().toLowerCase();
|
|
3472
|
+
if (!raw) return fallback;
|
|
3473
|
+
if (["y", "yes", "true"].includes(raw)) return true;
|
|
3474
|
+
if (["n", "no", "false"].includes(raw)) return false;
|
|
3475
|
+
io.write("Please answer yes or no.\n");
|
|
3476
|
+
}
|
|
3477
|
+
throw new Error(`Invalid confirmation response for "${message}".`);
|
|
3478
|
+
}
|
|
3479
|
+
async function selectPrompt(io, message, choices, fallback) {
|
|
3480
|
+
if (!choices.length) throw new Error(`Prompt "${message}" has no choices.`);
|
|
3481
|
+
io.write(`${message} choices: ${choices.map((value, index) => `${index + 1}) ${String(value)}`).join(" ")}
|
|
3482
|
+
`);
|
|
3483
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
3484
|
+
const raw = (await io.ask(message, fallback === void 0 ? void 0 : String(fallback))).trim();
|
|
3485
|
+
if (!raw && fallback !== void 0) return fallback;
|
|
3486
|
+
const index = Number(raw) - 1;
|
|
3487
|
+
if (Number.isInteger(index) && index >= 0 && index < choices.length) return choices[index];
|
|
3488
|
+
const exact = choices.find((choice) => String(choice) === raw);
|
|
3489
|
+
if (exact !== void 0) return exact;
|
|
3490
|
+
io.write("Choose one of the listed values.\n");
|
|
3491
|
+
}
|
|
3492
|
+
throw new Error(`Invalid selection for "${message}".`);
|
|
3493
|
+
}
|
|
3494
|
+
async function schemaPrompt(io, key, rule, fallback) {
|
|
3495
|
+
if (rule.enum?.length) return selectPrompt(io, key, rule.enum, fallback);
|
|
3496
|
+
if (rule.type === "boolean") return confirmPrompt(io, key, fallback === true || fallback === "true");
|
|
3497
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
3498
|
+
const isJson = rule.type === "object" || rule.type === "array";
|
|
3499
|
+
const raw = await textPrompt(
|
|
3500
|
+
io,
|
|
3501
|
+
isJson ? `${key} (JSON)` : key,
|
|
3502
|
+
fallback === void 0 ? void 0 : isJson ? JSON.stringify(fallback) : String(fallback)
|
|
3503
|
+
);
|
|
3504
|
+
if (!raw && fallback === void 0) return raw;
|
|
3505
|
+
if (rule.type === "number") {
|
|
3506
|
+
const number = Number(raw);
|
|
3507
|
+
if (Number.isFinite(number)) return number;
|
|
3508
|
+
io.write(`"${key}" must be a valid number.
|
|
3509
|
+
`);
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
if (isJson) {
|
|
3513
|
+
try {
|
|
3514
|
+
const value = JSON.parse(raw);
|
|
3515
|
+
if (rule.type === "array" ? Array.isArray(value) : value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
3516
|
+
return value;
|
|
3517
|
+
}
|
|
3518
|
+
} catch {
|
|
3519
|
+
}
|
|
3520
|
+
io.write(`"${key}" must be valid ${rule.type} JSON.
|
|
3521
|
+
`);
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
return raw;
|
|
3525
|
+
}
|
|
3526
|
+
throw new Error(`Invalid value for template variable "${key}".`);
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
// src/wizard.ts
|
|
3530
|
+
async function resolveGuidedCreate(options, io) {
|
|
3531
|
+
const templateDirectory = resolveTemplateDirectory(options.templateDirectory);
|
|
3532
|
+
const templates = await discoverLocalTemplates(templateDirectory);
|
|
3533
|
+
if (!templates.length) throw new Error(`No valid local templates found in ${templateDirectory}.`);
|
|
3534
|
+
let selected = options.template ? templates.find((item) => item.id === options.template || path28.basename(item.root) === options.template || item.template.manifest.packageName === options.template) : void 0;
|
|
3535
|
+
if (options.template && !selected) {
|
|
3536
|
+
throw new Error(`Template "${options.template}" was not found in ${templateDirectory}.`);
|
|
3537
|
+
}
|
|
3538
|
+
if (!selected) {
|
|
3539
|
+
io.write("Available templates:\n");
|
|
3540
|
+
templates.forEach((item, index2) => io.write(` ${index2 + 1}. ${item.name} \u2014 ${item.description}
|
|
3541
|
+
`));
|
|
3542
|
+
const answer = await textPrompt(io, "Select a template number");
|
|
3543
|
+
const index = Number(answer) - 1;
|
|
3544
|
+
if (!Number.isInteger(index) || index < 0 || index >= templates.length) {
|
|
3545
|
+
throw new Error("Invalid template selection.");
|
|
3546
|
+
}
|
|
3547
|
+
selected = templates[index];
|
|
3548
|
+
}
|
|
3549
|
+
let target = options.target;
|
|
3550
|
+
if (!target) target = await textPrompt(io, "Project name", "my-app") || "my-app";
|
|
3551
|
+
const targetName = path28.basename(path28.resolve(target));
|
|
3552
|
+
const metadata = await loadTemplateVariables(selected.root, selected.template.manifest);
|
|
3553
|
+
const variables = { ...metadata.defaults, ...options.variables };
|
|
3554
|
+
if (Object.hasOwn(metadata.schema, "projectName")) {
|
|
3555
|
+
variables.projectName = options.variables.projectName ?? targetName;
|
|
3556
|
+
}
|
|
3557
|
+
for (const [key, rule] of Object.entries(metadata.schema)) {
|
|
3558
|
+
if (Object.hasOwn(options.variables, key) || key === "projectName") continue;
|
|
3559
|
+
if (options.yes || !io.interactive) continue;
|
|
3560
|
+
const answer = await schemaPrompt(io, key, rule, metadata.defaults[key]);
|
|
3561
|
+
if (answer !== "") variables[key] = answer;
|
|
3562
|
+
}
|
|
3563
|
+
const errors = validateTemplateVariables2(variables, metadata.schema);
|
|
3564
|
+
if (errors.length) {
|
|
3565
|
+
const details = errors.map((item) => ` - ${item}`).join("\n");
|
|
3566
|
+
throw new Error(`Template variables are incomplete or invalid:
|
|
3567
|
+
${details}${options.yes ? "\nProvide missing values with --var key=value." : ""}`);
|
|
3568
|
+
}
|
|
3569
|
+
io.write("\nProject configuration\n");
|
|
3570
|
+
io.write(` Template: ${selected.name}
|
|
3571
|
+
Target: ${path28.resolve(target)}
|
|
3572
|
+
`);
|
|
3573
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
3574
|
+
io.write(` ${key}: ${typeof value === "object" ? JSON.stringify(value) : String(value)}
|
|
3575
|
+
`);
|
|
3576
|
+
}
|
|
3577
|
+
if (!options.yes && !options.dryRun && !await io.confirm("Create this project?")) {
|
|
3578
|
+
io.write("Cancelled. No files were written.\n");
|
|
3579
|
+
return null;
|
|
3580
|
+
}
|
|
3581
|
+
return Object.freeze({ ...options, template: selected.id, target, variables });
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
// src/index.ts
|
|
3585
|
+
var CREATE_HELP = [
|
|
3586
|
+
"Usage: appinit create [project-name] [options]",
|
|
3587
|
+
" appinit create <template-ref> <target> [options]",
|
|
3588
|
+
"",
|
|
3589
|
+
"Create a project from a local AppInit template. Without a template,",
|
|
3590
|
+
"the interactive wizard helps you choose one and configure its variables.",
|
|
3591
|
+
"",
|
|
3592
|
+
"Options:",
|
|
3593
|
+
" -t, --template <ref> Select a template by directory, ID, or package name",
|
|
3594
|
+
" --var key=value Set a template variable (repeatable)",
|
|
3595
|
+
" --template-dir <path> Use a specific local template directory",
|
|
3596
|
+
" -y, --yes Skip confirmation (requires all choices/values)",
|
|
3597
|
+
" --dry-run Preview planned output without writing files",
|
|
3598
|
+
" --install Explicitly install dependencies (headless opt-in)",
|
|
3599
|
+
" --package-manager <pm> Choose pnpm, npm, yarn, or bun",
|
|
3600
|
+
" --git Explicitly initialize a Git repository",
|
|
3601
|
+
" -h, --help Show this help",
|
|
3602
|
+
"",
|
|
3603
|
+
"Examples:",
|
|
3604
|
+
" appinit create",
|
|
3605
|
+
" appinit create my-api",
|
|
3606
|
+
" appinit create my-api --template backend.fastify.basic",
|
|
3607
|
+
" appinit create backend.backend-express.basic ./my-api --dry-run",
|
|
3608
|
+
" appinit create my-api --template backend.fastify.basic --yes",
|
|
3609
|
+
"",
|
|
3610
|
+
"Template directory defaults to ./templates or APPINIT_TEMPLATES_DIR.",
|
|
3611
|
+
""
|
|
3612
|
+
].join("\n");
|
|
3613
|
+
async function main(argv) {
|
|
3614
|
+
const [command, ...args] = argv;
|
|
3615
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
3616
|
+
process.stdout.write([
|
|
3617
|
+
"AppInit CLI",
|
|
3618
|
+
"",
|
|
3619
|
+
"Commands:",
|
|
3620
|
+
" appinit create [project-name] Create a project with guided setup",
|
|
3621
|
+
" appinit template <command> Author, discover, and install templates",
|
|
3622
|
+
" appinit doctor [--json] Diagnose project health",
|
|
3623
|
+
" appinit history [--json] View previously generated projects",
|
|
3624
|
+
" appinit context [--json] Inspect current project context",
|
|
3625
|
+
" appinit state path Show the global state directory path",
|
|
3626
|
+
" appinit state inspect [--json] Inspect global state safely",
|
|
3627
|
+
" appinit state doctor [--json] Diagnose global state health",
|
|
3628
|
+
" appinit validate [options] Validate a project or template",
|
|
3629
|
+
"",
|
|
3630
|
+
'Run "appinit create --help", "appinit template --help", "appinit doctor --help", "appinit history --help", "appinit context --help", "appinit state path --help", "appinit state inspect --help", or "appinit validate --help" for usage.',
|
|
3631
|
+
""
|
|
3632
|
+
].join("\n"));
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
if (command === "context") {
|
|
3636
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3637
|
+
stdout.write("Usage: appinit context [--json]\n\nShow metadata and global history for the current project.\n");
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
const unknown = args.filter((arg) => arg !== "--json");
|
|
3641
|
+
if (unknown.length) throw new Error("Unknown context option: " + unknown[0]);
|
|
3642
|
+
await runContextCommand({ json: args.includes("--json") });
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
if (command === "state") {
|
|
3646
|
+
const [subcommand, ...stateArgs] = args;
|
|
3647
|
+
if (subcommand === "doctor") {
|
|
3648
|
+
if (stateArgs.includes("--help") || stateArgs.includes("-h")) {
|
|
3649
|
+
stdout.write("Usage: appinit state doctor [--json]\n\nDiagnose global state health without modifying it.\n");
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3652
|
+
const unknown = stateArgs.filter((arg) => arg !== "--json");
|
|
3653
|
+
if (unknown.length) throw new Error(`Unknown state doctor option "${unknown[0]}".`);
|
|
3654
|
+
await runStateDoctorCommand({ json: stateArgs.includes("--json") });
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
if (subcommand === "inspect") {
|
|
3658
|
+
if (stateArgs.includes("--help") || stateArgs.includes("-h")) {
|
|
3659
|
+
stdout.write("Usage: appinit state inspect [--json]\n\nInspect global state without modifying it.\n");
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
const unknown = stateArgs.filter((arg) => arg !== "--json");
|
|
3663
|
+
if (unknown.length) throw new Error(`Unknown state inspect option "${unknown[0]}".`);
|
|
3664
|
+
await runStateInspectCommand({ json: stateArgs.includes("--json") });
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
if (subcommand !== "path") {
|
|
3668
|
+
throw new Error('Unknown state command. Use "appinit state path", "appinit state inspect", or "appinit state doctor".');
|
|
3669
|
+
}
|
|
3670
|
+
if (stateArgs.includes("--help") || stateArgs.includes("-h")) {
|
|
3671
|
+
stdout.write("Usage: appinit state path\n\nShow the resolved global AppInit state directory.\n");
|
|
3672
|
+
return;
|
|
3673
|
+
}
|
|
3674
|
+
if (stateArgs.length) throw new Error(`Unknown state path option "${stateArgs[0]}".`);
|
|
3675
|
+
await runStatePathCommand();
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
if (command === "history") {
|
|
3679
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3680
|
+
stdout.write("Usage: appinit history [--json]\n\nShow projects previously generated with AppInit.\n");
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
const unknown = args.filter((arg) => arg !== "--json");
|
|
3684
|
+
if (unknown.length) throw new Error(`Unknown history option "${unknown[0]}".`);
|
|
3685
|
+
await runHistoryCommand({ json: args.includes("--json") });
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
if (command === "doctor") {
|
|
3689
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3690
|
+
stdout.write("Usage: appinit doctor [--json]\n\nDiagnose the current project. Doctor findings are advisory and do not block execution.\n");
|
|
3691
|
+
return;
|
|
3692
|
+
}
|
|
3693
|
+
const unknown = args.filter((arg) => arg !== "--json");
|
|
3694
|
+
if (unknown.length) throw new Error(`Unknown doctor option "${unknown[0]}".`);
|
|
3695
|
+
await runDoctorCommand({ json: args.includes("--json") });
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
if (command === "validate") {
|
|
3699
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3700
|
+
stdout.write("Usage: appinit validate [--template <directory>] [--json]\n\nValidate the current project or a template directory.\n");
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
let templateDir;
|
|
3704
|
+
let json = false;
|
|
3705
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3706
|
+
const arg = args[index];
|
|
3707
|
+
if (arg === "--json") json = true;
|
|
3708
|
+
else if (arg === "--template") {
|
|
3709
|
+
templateDir = args[index + 1];
|
|
3710
|
+
if (!templateDir || templateDir.startsWith("--")) throw new Error("--template requires a directory.");
|
|
3711
|
+
index += 1;
|
|
3712
|
+
} else throw new Error(`Unknown validate option "${arg}".`);
|
|
3713
|
+
}
|
|
3714
|
+
await runValidateCommand({ templateDir, json });
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
if (command === "template") {
|
|
3718
|
+
await runTemplateCommand(args, { write: (message) => stdout.write(message) });
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
if (command !== "create") {
|
|
3722
|
+
throw new Error(`Unknown command "${command}". Run "appinit --help" for usage.`);
|
|
3723
|
+
}
|
|
3724
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3725
|
+
process.stdout.write(CREATE_HELP);
|
|
3726
|
+
return;
|
|
3727
|
+
}
|
|
3728
|
+
const options = parseCreateArguments(args);
|
|
3729
|
+
const interactive = Boolean(stdin.isTTY && stdout.isTTY);
|
|
3730
|
+
if (!interactive && (!options.template || !options.target)) {
|
|
3731
|
+
throw new Error("Non-interactive create requires a template and target. Use: appinit create <template-ref> <target> --yes");
|
|
3732
|
+
}
|
|
3733
|
+
if (!interactive && !options.yes && !options.dryRun) {
|
|
3734
|
+
throw new Error("Non-interactive create requires --yes to confirm file writes.");
|
|
3735
|
+
}
|
|
3736
|
+
if (options.yes && (!options.template || !options.target)) {
|
|
3737
|
+
throw new Error(" --yes requires both a template and target. Supply: appinit create <template-ref> <target> --yes");
|
|
3738
|
+
}
|
|
3739
|
+
const rl = interactive ? createInterface({ input: stdin, output: stdout }) : void 0;
|
|
3740
|
+
const io = {
|
|
3741
|
+
interactive,
|
|
3742
|
+
ask: async (message, fallback) => {
|
|
3743
|
+
if (!rl) throw new Error(`Missing value: ${message}`);
|
|
3744
|
+
const suffix = fallback ? ` [${fallback}]` : "";
|
|
3745
|
+
const answer = (await rl.question(`${message}${suffix}: `)).trim();
|
|
3746
|
+
return answer || fallback || "";
|
|
3747
|
+
},
|
|
3748
|
+
confirm: async (message) => {
|
|
3749
|
+
if (!rl) throw new Error("Confirmation requires an interactive terminal. Use --yes to proceed.");
|
|
3750
|
+
const answer = (await rl.question(`${message} (y/N): `)).trim().toLowerCase();
|
|
3751
|
+
return answer === "y" || answer === "yes";
|
|
3752
|
+
},
|
|
3753
|
+
write: (message) => {
|
|
3754
|
+
stdout.write(message);
|
|
3755
|
+
}
|
|
3756
|
+
};
|
|
3757
|
+
try {
|
|
3758
|
+
const resolved = await resolveGuidedCreate(options, io);
|
|
3759
|
+
if (resolved) {
|
|
3760
|
+
await runCreateCommand(resolved);
|
|
3761
|
+
if (!resolved.dryRun) await finalizeProject(resolved.target, resolved, io);
|
|
3762
|
+
}
|
|
3763
|
+
} finally {
|
|
3764
|
+
rl?.close();
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
3768
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3769
|
+
process.stderr.write(`AppInit: ${message}
|
|
3770
|
+
`);
|
|
3771
|
+
process.exitCode = 1;
|
|
3772
|
+
});
|
|
3773
|
+
export {
|
|
3774
|
+
diagnoseAppInitState,
|
|
3775
|
+
finalizeProject,
|
|
3776
|
+
findLocalTemplate,
|
|
3777
|
+
formatTemplateInfo,
|
|
3778
|
+
formatTemplateList,
|
|
3779
|
+
inspectAppInitState,
|
|
3780
|
+
inspectAppInitStatePath,
|
|
3781
|
+
installRemoteTemplate,
|
|
3782
|
+
listRemoteTemplates,
|
|
3783
|
+
parseCreateArguments,
|
|
3784
|
+
readProjectContext,
|
|
3785
|
+
readProjectHistory,
|
|
3786
|
+
resolveGuidedCreate,
|
|
3787
|
+
runContextCommand,
|
|
3788
|
+
runCreateCommand,
|
|
3789
|
+
runDoctorCommand,
|
|
3790
|
+
runHistoryCommand,
|
|
3791
|
+
runStateDoctorCommand,
|
|
3792
|
+
runStateInspectCommand,
|
|
3793
|
+
runStatePathCommand,
|
|
3794
|
+
runValidateCommand,
|
|
3795
|
+
searchLocalTemplates
|
|
58
3796
|
};
|
|
59
|
-
`}var qf=`# Template Files
|
|
60
|
-
|
|
61
|
-
Place all renderable template files in this directory.
|
|
62
|
-
|
|
63
|
-
Rules:
|
|
64
|
-
- Use \`_tmpl\` suffix for files processed by AppInit
|
|
65
|
-
- Static assets may omit suffix
|
|
66
|
-
- Variable logic lives in /variables
|
|
67
|
-
- Hooks are optional and live in /hooks
|
|
68
|
-
|
|
69
|
-
AppInit does NOT generate framework code for you.
|
|
70
|
-
You own the template content.
|
|
71
|
-
`;var ca={name:"create",description:"Create a new AppInit template (minimal scaffold)",options:{kind:{type:"string",description:"Template category (frontend|backend|fullstack|cli|library)",default:"frontend"}},async run(t){let e=t.args?.[2];console.log("config",t),console.log("NAME",e),e||(t.logger?.error("\u2716 Template name is required"),t.logger?.info("Example:"),t.logger?.info(" appinit template create frontend-react"),process.exit(1)),t.targetDir=k(t.cwd,e),await la(t);}};var Uf=["1.0"];async function dr(t){let e=await Dt({cwd:t,cliVersion:xe(),supportedAtsVersions:Uf,intent:{actor:"template-author",mode:"strict",target:{kind:"template",rootDir:t}}});if(!e.ok)throw new Error("Template validation failed");return e}async function pa(t){let{cwd:e,homeDir:r,cacheDir:n,templatesDir:i,runtimeDir:s,logger:o}=t,a=o??it();await dr(e),a.success("\u2714 Template validated");let l=Ee__default.join(e,".appinit-generate"),c=Ee__default.join(l,"project");Je(l),q(c);let u=await Et(e);await ur({templateDir:e,targetDir:c,answers:u,homeDir:r,cacheDir:n,templatesDir:i,runtimeDir:s,logger:a}),a.success("\u2714 Template generated successfully"),a.info(""),a.info("Generated output:"),a.info(` ${c}`),a.info(""),a.info("Next steps:"),a.info(" - Inspect generated files"),a.info(" - Run: appinit template preview"),a.info(" - Fix any Doctor warnings");}var fa={name:"generate",description:"Generate files without scaffolding",options:{kind:{type:"string",description:"Template category (frontend|backend|fullstack|cli|library)",default:"frontend"}},async run(t){await pa(t);}};function ma(t,e){let r=Gf(t,{dev:true,single:true}),n=Bf.createServer((i,s)=>{r(i,s);});return n.listen(e,()=>{console.log(`\u{1F440} Static preview running at http://localhost:${e}`);}),n}function Wf(t){let e=[t,k(t,"dist"),k(t,"build")];for(let r of e)if(U(k(r,"index.html")))return r;return null}var ha={name:"preview",description:"Preview rendered template output",options:{port:{type:"number",default:3e3,description:"Preview server port"}},async run(t){let{cwd:e,flags:r,logger:n}=t,i=k(k(e,".appinit-preview"),"project");U(i)||(n?.error("\u2716 No preview found"),n?.error("Run `appinit template dev` first to generate preview"),process.exit(1)),Wf(i)||(n?.warn("\u26A0 Preview cannot be served in browser"),n?.warn("No index.html found in preview output."),n?.warn(""),n?.warn("This is expected for framework templates (React/Vue/etc)."),n?.warn("Preview server is static only (ATS 1.0)."),n?.warn(""),n?.warn("Next steps:"),n?.warn("\u2022 Run `appinit template generate`"),n?.warn("\u2022 Then run framework dev/build manually"),process.exit(0));let o=Number(r.port??3e3);n?.info("\u2714 Starting preview server..."),ma(i,o),await new Promise(()=>{});}};function da(t,e){let r=Kf.watch(t,{ignored:["**/.appinit-preview/**","**/node_modules/**","**/.git/**"],ignoreInitial:true}),n=false;async function i(){if(!n){n=true;try{await e();}finally{n=false;}}}let s=null;return r.on("all",(o,a)=>{s&&clearTimeout(s),s=setTimeout(()=>{console.log(`\u{1F504} ${o}: ${a}`),i();},100);}),r}function ya(t){let e=Ee__default.join(t,".appinit-preview"),r=Ee__default.join(e,"project");return Ye__default.existsSync(e)&&Ye__default.rmSync(r,{recursive:true,force:true}),Ye__default.mkdirSync(r,{recursive:true}),r}var wa={name:"dev",description:"Run template in development mode",options:{port:{type:"number",default:3e3,description:"Dev server port"},watch:{type:"boolean",description:"Enable watch mode"}},async run(t){let{cwd:e,logger:r}=t,n=k(e,"template.config.ts"),i=k(e,".appinit");(!U(n)||!U(i))&&(r?.warn("\u26A0 Not inside an AppInit template"),r?.info("This command must be run from a template root directory."),r?.info(""),r?.info("Example:"),r?.info(" cd templates/frontend-react"),r?.info(" appinit template dev"),process.exit(1)),r?.info("\u2714 Template root detected"),await dr(e),r?.info("\u2714 Template validated");async function s(){let o=ya(e),a=await Et(e);await ur({templateDir:e,targetDir:o,answers:a,homeDir:t.homeDir,cacheDir:t.cacheDir,templatesDir:t.templatesDir,runtimeDir:t.runtimeDir,logger:r}),r?.success("\u2714 Template re-rendered");}await s(),t.flags.watch!==false&&(r?.info("\u{1F440} Watching template for changes..."),da(e,s)),r?.success("\u2714 Template rendered into preview directory");}};var ba={name:"template",description:"",subcommands:{dev:wa,preview:ha,generate:fa,create:ca},async run(t){console.log(`
|
|
72
|
-
AppInit Template Commands (Author Mode)
|
|
73
|
-
|
|
74
|
-
Usage:
|
|
75
|
-
appinit template <command>
|
|
76
|
-
|
|
77
|
-
Commands:
|
|
78
|
-
dev Run template in development mode (watch & render)
|
|
79
|
-
preview Serve static preview of rendered output
|
|
80
|
-
generate Generate final output for validation
|
|
81
|
-
create Scaffold a new template (author bootstrap)
|
|
82
|
-
|
|
83
|
-
Examples:
|
|
84
|
-
appinit template dev
|
|
85
|
-
appinit template generate
|
|
86
|
-
`),process.exit(0);}};var kt={create:ta,help:aa,doctor:oa,template:ba};async function xa(t){console.log("ENV",process.env.NODE_ENV);let e=t.flags.env??(process.env.NODE_ENV==="development"?"dev":"prod"),r=e;console.log("ENV",e),console.log("MODE------------------------",r);let n=process.cwd(),i=k(Qf.homedir(),".appinit"),s=it(),o;if(r==="dev"){let a=fileURLToPath(import.meta.url),l=Ee__default.dirname(a),c=hr(l);o=Ee__default.join(c,"templates");}return await Zf(i),{cwd:n,command:t.name,flags:t.flags,args:t.args,interactive:!t.flags.nonInteractive,homeDir:i,cacheDir:k(i,"cache"),templatesDir:k(i,"templates"),runtimeDir:k(i,"runtime"),mode:r,env:process.env.NODE_ENV==="development"?"dev":"prod",cliName:Ko(),cliVersion:process.env.APPINIT_CLI_VERSION??xe(),runtime:"cli",outputMode:t.flags.json?"json":"text",logger:s,task:{createManager:()=>Go(t)},answers:void 0,targetDir:void 0,runtimeSession:void 0,savedConfig:null,repoTemplatesDir:o}}async function Zf(t){if(existsSync(t))return;let e=[t,k(t,"templates"),k(t,"runtime"),k(t,"registry"),k(t,"cache"),k(t,"logs")];await Promise.all(e.map(q));}var Sa=["help","version","nonInteractive","skipInstall","skipGit","skipDefaultPacks","dryRun","force","quiet","yes","author","watch"],Ta=["template","projectKind","format","env","mode","port"],ka={"non-interactive":"nonInteractive","skip-install":"skipInstall","skip-git":"skipGit","project-kind":"projectKind","skip-default-packs":"skipDefaultPacks","dry-run":"dryRun"},Wn={help:"help",h:"help",version:"version",v:"version",author:"author",a:"author",json:"json",f:"force",q:"quiet",y:"yes",e:"env",m:"mode",w:"watch","non-interactive":"nonInteractive","skip-install":"skipInstall","skip-git":"skipGit","project-kind":"projectKind","dry-run":"dryRun"},Kn={quiet:false,verbose:false,debug:false,nonInteractive:false,skipInstall:false,skipGit:false,skipDefaultPacks:false,json:"",project:"",dryRun:false,force:false,author:false,env:"prod",mode:void 0,format:"text"};function Ra(t){let e=t.splice(2),r=Object.entries(Wn).reduce((o,[a,l])=>(a.length===1&&(o[a]=l),o),{}),n=Xf(e,{boolean:Sa.map(o=>o.toLowerCase()),string:Ta.map(o=>o.toLowerCase()),alias:r,default:Kn}),i=String(n._?.[0]??"help"),s=em(n);return {name:i,args:e,flags:s}}function em(t){let e={...Kn};for(let[r,n]of Object.entries(t)){if(r==="_"||typeof r!="string")continue;let i=tm(r);if(i){if(r.startsWith("no-")){e[i]=false;continue}e[i]=n;}}return e}function tm(t){if(!t)return "";if(t=t.replace(/^--?/,"").toLowerCase(),Wn[t])return Wn[t];if(t in ka)return ka[t];let e=Si(t);return e in Kn||Sa.includes(e)||Ta.includes(e)?e:""}function st(t,e={}){let r=t.subcommands&&Object.keys(t.subcommands).length>0,n=e.usage??(r?`Usage: appinit ${t.name} <command>`:`Usage: appinit ${t.name} [options]`),i=t.description??"",s=[];if(s.push(`
|
|
87
|
-
${n}
|
|
88
|
-
`),i&&s.push(i),t.subcommands&&Object.keys(t.subcommands).length>0){s.push(`
|
|
89
|
-
### Commands:
|
|
90
|
-
`);for(let o of Object.values(t.subcommands))s.push(` ${o.name.padEnd(12)} ${o.description??""}`);s.push(`
|
|
91
|
-
Run: appinit ${t.name} <command> --help`);}else t.options&&Object.keys(t.options).length>0&&s.push(`
|
|
92
|
-
`+Aa(t.options));if(e.examples?.length){s.push(`
|
|
93
|
-
### Examples:
|
|
94
|
-
`);for(let o of e.examples)s.push(` ${o}`);}return s.join(`
|
|
95
|
-
`).trimEnd()}function Jn(t){return st(t,{usage:"Usage: appinit create <project-name> [options]",examples:["appinit create my-app","appinit create my-app --template frontend.react.basic --yes","appinit create my-app --skip-install"]})}function Aa(t){let e=[];e.push(`### Options:
|
|
96
|
-
`);let r=Object.entries(t),i=r.reduce((s,[o])=>Math.max(s,o.length),0)+2;for(let[s,o]of r){let a=s.replace(/([A-Z])/g,"-$1").toLowerCase(),l=o.type?`(${o.type})`:"",c=o.default!==void 0?`[default: ${o.default}]`:"",u=`--${a}`,y=" ".repeat(i-a.length),d=`${o.description} ${l} ${c}`.trim();e.push(` ${u}${y}${d}`);}return e.join(`
|
|
97
|
-
`)}function Ca(t){return t?["init","new"].includes(t)?"create":t.toLowerCase():"help"}async function _a(t,e){let r=kt[t],n=it();r||(n.error(`Unknown command: ${t}`),process.exit(1));let i=e.args?.[1],s=r.subcommands&&i?r.subcommands[i]:null;return s?s.run(e):r.run(e)}async function Ea(t){let e=Ra(t),r=Ca(e.name),n=await xa(e),i=xe(),s=kt[r];if(e.flags.version){console.log(i);return}if(!r||r==="help"){let o=e.args[1],a=kt[o];if(a){if(a.name==="create"){console.log(Jn(a));return}console.log(st(a));return}xt(i);return}if(e.flags.help){if(!s){xt(i);return}let o=e.args[1],a=s.subcommands&&o?s.subcommands[o]:null;if(a){console.log(st(a));return}if(s.name==="create"){console.log(Jn(s));return}console.log(st(s));return}try{await _a(r,n);}catch(o){n.logger?.error("CLI command failed",o),process.exitCode=1;}}function jx(t){Ea(t);}export{jx as start};
|