@qlover/create-app 0.12.0 → 1.0.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/templates/next-app/next.config.ts +4 -3
  5. package/dist/templates/next-app/package.json +14 -11
  6. package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +2 -3
  7. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +6 -4
  8. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +14 -10
  9. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +2 -2
  10. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +1 -4
  11. package/dist/templates/next-app/src/base/services/I18nService.ts +6 -2
  12. package/dist/templates/next-app/src/base/services/adminApi/AdminApiRequester.ts +11 -7
  13. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +11 -10
  14. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +16 -12
  15. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +19 -19
  16. package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +26 -21
  17. package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -6
  18. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +12 -14
  19. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +34 -17
  20. package/dist/templates/next-app/src/server/NextApiServer.ts +10 -4
  21. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +2 -2
  22. package/dist/templates/next-app/src/server/controllers/UserController.ts +2 -2
  23. package/dist/templates/next-app/src/server/port/ServerInterface.ts +2 -2
  24. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +6 -7
  25. package/dist/templates/next-app/src/server/services/UserService.ts +2 -2
  26. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +11 -47
  27. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +3 -2
  28. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +6 -1
  29. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +21 -61
  30. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +29 -51
  31. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +8 -26
  32. package/dist/templates/react-app/__tests__/src/main.test.tsx +2 -2
  33. package/dist/templates/react-app/config/IOCIdentifier.ts +1 -1
  34. package/dist/templates/react-app/docs/en/test-guide.md +5 -5
  35. package/dist/templates/react-app/docs/zh/test-guide.md +5 -5
  36. package/dist/templates/react-app/package.json +2 -1
  37. package/dist/templates/react-app/src/base/apis/AiApi.ts +20 -12
  38. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +14 -5
  39. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +26 -13
  40. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +30 -34
  41. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +20 -17
  42. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +11 -5
  43. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +19 -6
  44. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +11 -11
  45. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +11 -5
  46. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +6 -7
  47. package/dist/templates/react-app/src/base/services/UserService.ts +1 -5
  48. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +6 -7
  49. package/dist/templates/react-app/src/main.tsx +1 -1
  50. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +5 -5
  51. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +3 -1
  52. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +6 -2
  53. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +34 -3
  54. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +13 -7
  55. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +6 -1
  56. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +26 -11
  57. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +1 -1
  58. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +3 -1
  59. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +16 -7
  60. package/dist/templates/react-app/src/vite-env.d.ts +1 -1
  61. package/dist/templates/react-app/tsconfig.e2e.json +5 -2
  62. package/dist/templates/react-app/tsconfig.json +7 -0
  63. package/dist/templates/react-app/tsconfig.node.json +4 -2
  64. package/dist/templates/react-app/tsconfig.test.json +4 -1
  65. package/package.json +3 -3
  66. package/dist/templates/react-app/src/base/cases/AppError.ts +0 -10
  67. package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +0 -20
  68. package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +0 -20
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @qlover/create-app
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
3
7
  ## 0.12.0
4
8
 
5
9
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ ${t}`,dn=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),_n=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Ye(D,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#D.frames.length,this.#c=t);let{frames:r}=this.#D,u=r[this.#i];this.color&&(u=d[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",D=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+D+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#n;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#n=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#n=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&$e.hide(this.#u),this.#t.discardStdin&&z.default.stdin.isTTY&&(this.#r=!0,Je.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&$e.show(this.#u),this.#t.discardStdin&&z.default.stdin.isTTY&&this.#r&&(Je.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:Y.success,text:t})}fail(t){return this.stopAndPersist({symbol:Y.error,text:t})}warn(t){return this.stopAndPersist({symbol:Y.warning,text:t})}info(t){return this.stopAndPersist({symbol:Y.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",D=t.text??this.text,s=typeof D=="string"?(i?" ":"")+D:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function tr(e){return new Ze(e)}async function rr(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:D}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=tr(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(D===void 0?void 0:typeof D=="string"?D:D(s)),s}}var bt=require("fs");var S=require("path"),A=require("fs"),cr=h(lr(),1);var _e=require("fs"),J=class{static ensureDir(t){(0,_e.existsSync)(t)||(0,_e.mkdirSync)(t,{recursive:!0})}};var{copyFile:rs,stat:us}=A.promises,me=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=(0,S.join)(t,this.ignoreFile);if(!(0,A.existsSync)(r))return;let D=(0,A.readFileSync)(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,cr.default)().add(D)}async copyFiles(t,r,u,i){let D=await A.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,S.join)(t,o),a=(0,S.join)(r,o);if(u&&u.ignores(o))return;if(J.ensureDir((0,S.dirname)(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){J.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var k=require("fs"),kD=h(LD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,k.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,k.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,kD.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,k.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var $D=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new me((0,x.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await WD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,x.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,x.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,x.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,x.join)(D,o),a=(0,x.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var At={name:"@qlover/create-app",version:"0.12.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Ul(){let e=new HD.Command;return e.version(At.version,"-v, --version","Show version").description(At.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function YD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Ul(),i=(0,vt.resolve)(e,"./templates"),D=(0,vt.resolve)(e,"./configs");(0,xt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}YD(__dirname).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,cr.default)().add(D)}async copyFiles(t,r,u,i){let D=await A.promises.readdir(t);await Promise.all(D.map(async o=>{let s=(0,S.join)(t,o),a=(0,S.join)(r,o);if(u&&u.ignores(o))return;if(J.ensureDir((0,S.dirname)(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){J.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var k=require("fs"),kD=h(LD(),1),xe=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return(0,k.readFileSync)(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){(0,k.writeFileSync)(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let D=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof D=="string"?D:JSON.stringify(D))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,kD.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let D=this.getRealTemplateFilePath(u);return(0,k.existsSync)(D)?(this.mergeJSONFile(D,JSON.parse(i)),!0):(this.writeFile(D,i),!0)}return this.writeFile(u,i),!0}return!1}};var $D=["pack-app"],ye=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!(0,bt.existsSync)(r))throw new Error("template path not exit");this.ora=rr,this.context=new UD.ScriptContext("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new me((0,x.join)(this.context.options.configsRootPath,"_common")),this.compose=new xe}get logger(){return this.context.logger}async steps(t){try{return await WD.default.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return $D.includes(t)}async getGeneratorContext(){let t=Ot(this.subPackages,$D),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=wt(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=(0,x.join)(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:D,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=(0,x.join)(D,u);if(!(0,bt.existsSync)(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:(0,x.join)(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:D}=this.context.options;for(let o of u){let s=(0,x.join)(D,o),a=(0,x.join)(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var At={name:"@qlover/create-app",version:"1.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Ul(){let e=new HD.Command;return e.version(At.version,"-v, --version","Show version").description(At.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function YD(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Ul(),i=(0,vt.resolve)(e,"./templates"),D=(0,vt.resolve)(e,"./configs");(0,xt.existsSync)(i)||(console.error("Template is empty!"),process.exit(1)),(0,xt.existsSync)(D)||(console.error("Configs is empty!"),process.exit(1)),await new ye({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:D}}).generate()}YD(__dirname).catch(e=>{console.error(e),process.exit(1)});
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ ${t}`,pD=Object.getOwnPropertyDescriptor(Function.prototype,"toString"),hD=Objec
8
8
  `))this.#e+=Math.max(1,Math.ceil(Le(n,{countAnsiEscapeCodes:!0})/t))}get isEnabled(){return this.#a&&!this.#F}set isEnabled(t){if(typeof t!="boolean")throw new TypeError("The `isEnabled` option must be a boolean");this.#a=t}get isSilent(){return this.#F}set isSilent(t){if(typeof t!="boolean")throw new TypeError("The `isSilent` option must be a boolean");this.#F=t}frame(){let t=Date.now();(this.#i===-1||t-this.#c>=this.interval)&&(this.#i=++this.#i%this.#n.frames.length,this.#c=t);let{frames:r}=this.#n,u=r[this.#i];this.color&&(u=E[this.color](u));let i=typeof this.#s=="string"&&this.#s!==""?this.#s+" ":"",n=typeof this.text=="string"?" "+this.text:"",o=typeof this.#o=="string"&&this.#o!==""?" "+this.#o:"";return i+u+n+o}clear(){if(!this.#a||!this.#u.isTTY)return this;this.#u.cursorTo(0);for(let t=0;t<this.#D;t++)t>0&&this.#u.moveCursor(0,-1),this.#u.clearLine(1);return(this.#l||this.lastIndent!==this.#l)&&this.#u.cursorTo(this.#l),this.lastIndent=this.#l,this.#D=0,this}render(){return this.#F?this:(this.clear(),this.#u.write(this.frame()),this.#D=this.#e,this)}start(t){return t&&(this.text=t),this.#F?this:this.#a?this.isSpinning?this:(this.#t.hideCursor&&je.hide(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&(this.#r=!0,We.start()),this.render(),this.#f=setInterval(this.render.bind(this),this.interval),this):(this.text&&this.#u.write(`- ${this.text}
9
9
  `),this)}stop(){return this.#a?(clearInterval(this.#f),this.#f=void 0,this.#i=0,this.clear(),this.#t.hideCursor&&je.show(this.#u),this.#t.discardStdin&&ce.stdin.isTTY&&this.#r&&(We.stop(),this.#r=!1),this):this}succeed(t){return this.stopAndPersist({symbol:k.success,text:t})}fail(t){return this.stopAndPersist({symbol:k.error,text:t})}warn(t){return this.stopAndPersist({symbol:k.warning,text:t})}info(t){return this.stopAndPersist({symbol:k.info,text:t})}stopAndPersist(t={}){if(this.#F)return this;let r=t.prefixText??this.#s,u=this.#E(r," "),i=t.symbol??" ",n=t.text??this.text,s=typeof n=="string"?(i?" ":"")+n:"",a=t.suffixText??this.#o,l=this.#g(a," "),p=u+i+s+l+`
10
10
  `;return this.stop(),this.#u.write(p),this}};function zt(e){return new He(e)}async function Kt(e,t){let r=typeof e=="function",u=typeof e.then=="function";if(!r&&!u)throw new TypeError("Parameter `action` must be a Function or a Promise");let{successText:i,failText:n}=typeof t=="object"?t:{successText:void 0,failText:void 0},o=zt(t).start();try{let a=await(r?e(o):e);return o.succeed(i===void 0?void 0:typeof i=="string"?i:i(a)),a}catch(s){throw o.fail(n===void 0?void 0:typeof n=="string"?n:n(s)),s}}import{existsSync as Nn}from"fs";var Dr=ue(ir(),1);import{dirname as QD,join as Ze}from"path";import{existsSync as es,readFileSync as ts,promises as nr}from"fs";import{existsSync as JD,mkdirSync as ZD}from"fs";var H=class{static ensureDir(t){JD(t)||ZD(t,{recursive:!0})}};var{copyFile:rs,stat:us}=nr,Ce=class e{constructor(t,r=e.IGNORE_FILE){this.ignoreTargetPath=t;this.ignoreFile=r}static IGNORE_FILE=".gitignore.template";getIg(t=this.ignoreTargetPath){let r=Ze(t,this.ignoreFile);if(!es(r))return;let n=ts(r,"utf8").split(`
11
- `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(n)}async copyFiles(t,r,u,i){let n=await nr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"0.12.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
11
+ `).map(o=>o.trim()).filter(o=>o&&!o.startsWith("#"));return(0,Dr.default)().add(n)}async copyFiles(t,r,u,i){let n=await nr.readdir(t);await Promise.all(n.map(async o=>{let s=Ze(t,o),a=Ze(r,o);if(u&&u.ignores(o))return;if(H.ensureDir(QD(a)),(await us(s)).isDirectory())await this.copyFiles(s,a,u,i);else{if(i&&await i(s,a))return;await rs(s,a)}}))}copyPaths({sourcePath:t,targetPath:r,copyCallback:u}){H.ensureDir(r);let i=this.getIg();return this.copyFiles(t,r,i,u)}};var jn=ue(In(),1);import{readFileSync as $l,writeFileSync as Ul,existsSync as Wl}from"fs";var _e=class{constructor(){}isJSONFilePath(t){return t.endsWith(".json")||t.endsWith(".json.template")}isTemplateFilePath(t){return t.endsWith(".template")}getRealTemplateFilePath(t){return t.replace(".template","")}readFile(t){return $l(t,"utf-8")}readJSONFile(t){return JSON.parse(this.readFile(t))}writeFile(t,r){Ul(this.getRealTemplateFilePath(t),r,{encoding:"utf-8"})}replaceFile(t,r){let u=this.readFile(t);return Object.keys(r).forEach(i=>{let n=r[i];u=u.replace(new RegExp(`\\[TPL:${i}\\]`,"g"),typeof n=="string"?n:JSON.stringify(n))}),u}mergeJSONFile(t,r){let u=this.readJSONFile(t),i=(0,jn.default)(r,u);this.writeFile(t,JSON.stringify(i,null,2))}composeConfigFile(t,r,u){if(this.isTemplateFilePath(r)){let i=this.replaceFile(r,t);if(this.isJSONFilePath(r)&&this.isJSONFilePath(u)){let n=this.getRealTemplateFilePath(u);return Wl(n)?(this.mergeJSONFile(n,JSON.parse(i)),!0):(this.writeFile(n,i),!0)}return this.writeFile(u,i),!0}return!1}};var Gn=["pack-app"],Be=class{ora;context;subPackages;copyer;compose;constructor(t){let r=t.options?.templateRootPath;if(!r)throw new Error("template path not exit");if(!Nn(r))throw new Error("template path not exit");this.ora=Kt,this.context=new Hl("create-app",t),this.subPackages=["node-lib","react-app","next-app"],this.copyer=new Ce(N(this.context.options.configsRootPath,"_common")),this.compose=new _e}get logger(){return this.context.logger}async steps(t){try{return await Yl.prompt(t)}catch(r){throw r.isTtyError,this.logger.error(r),r}}async action({label:t,task:r}){let u=r();u instanceof Promise||(u=Promise.resolve(u));let i=t;return this.ora(u,i),u}isPackageTemplate(t){return Gn.includes(t)}async getGeneratorContext(){let t=mt(this.subPackages,Gn),r=await this.steps(t);if(this.isPackageTemplate(r.template)){let u=_t(this.subPackages),i=await this.steps(u);Object.assign(r,i)}return r.targetPath=N(process.cwd(),r.projectName),r.releasePath=r.releasePath||"src",r}async generate(){let t=await this.getGeneratorContext();if(this.logger.debug("context is:",t,this.context.options.templateRootPath),t.subPackages){await this.action({label:"Generate Directories(subPackages)",task:async()=>{await this.generateTemplateDir(t),await this.generateSubPackages(t),await this.generateConfigs(t,t.targetPath,"_common")}});return}await this.action({label:"Generate Directory",task:async()=>{await this.generateTemplateDir(t),await this.generateConfigs(t,t.targetPath,"_common"),await this.generateConfigs(t,t.targetPath,t.template)}})}async generateConfigs(t,r,u){let i=(a,l)=>(this.logger.debug("copyCallback",a,l),this.compose.composeConfigFile(t,a,l)),{configsRootPath:n,config:o}=this.context.options;if(!o){this.logger.debug("no copy config files");return}let s=N(n,u);if(!Nn(s)){this.logger.debug(`Config path not found: ${s}`);return}await this.copyer.copyPaths({sourcePath:s,targetPath:r,copyCallback:i})}generateTemplateDir(t){return this.copyer.copyPaths({sourcePath:N(this.context.options.templateRootPath,t.template),targetPath:t.targetPath})}async generateSubPackages(t){let{packagesNames:r="packages",subPackages:u=[],targetPath:i=""}=t,{templateRootPath:n}=this.context.options;for(let o of u){let s=N(n,o),a=N(i,r,o);this.logger.debug("copy sub package",s,a),await this.copyer.copyPaths({sourcePath:s,targetPath:a})}}};var Et={name:"@qlover/create-app",version:"1.0.0",description:"Create a new app with a single command",private:!1,type:"module",files:["dist","package.json","README.md","CHANGELOG.md"],bin:{"create-app":"dist/index.js"},scripts:{lint:"eslint src --fix",build:"tsup","type-check":"tsc --noEmit","create:app":"node ./dist/index.js"},repository:{type:"git",url:"git+https://github.com/qlover/fe-base.git",directory:"packages/create-app"},homepage:"https://github.com/qlover/fe-base#readme",keywords:["create-app","fe-scripts","scripts"],author:"qlover",license:"ISC",publishConfig:{access:"public"},devDependencies:{"@qlover/logger":"workspace:*",ignore:"^7.0.3",lodash:"^4.17.21",ora:"^8.1.1"},dependencies:{"@qlover/scripts-context":"workspace:*",commander:"^13.1.0",inquirer:"^12.3.2"}};function Kl(){let e=new zl;return e.version(Et.version,"-v, --version","Show version").description(Et.description).option("-d, --dry-run","Do not touch or write anything, but show the commands").option("-V, --verbose","Show more information").option("--config","Copy config files (default: true)",!0).option("--no-config","Do not copy config files"),e.parse(),e.opts()}async function kn(e=process.cwd()){let{dryRun:t,verbose:r,...u}=Kl(),i=Mn(e,"./templates"),n=Mn(e,"./configs");Ln(i)||(console.error("Template is empty!"),process.exit(1)),Ln(n)||(console.error("Configs is empty!"),process.exit(1)),await new Be({dryRun:t,verbose:r,options:{...u,templateRootPath:i,configsRootPath:n}}).generate()}import{fileURLToPath as Xl}from"url";import{dirname as Jl}from"path";var Zl=Xl(import.meta.url),Ql=Jl(Zl);kn(Ql).catch(e=>{console.error(e),process.exit(1)});
@@ -11,9 +11,10 @@ generateLocales().catch((error) => {
11
11
 
12
12
  const nextConfig: NextConfig = {
13
13
  // reactStrictMode: false,
14
- turbopack: {
15
- root: __dirname // 明确指定根目录
16
- },
14
+ // turbopack 在接下本地 file: 依赖时支持还不够好
15
+ // turbopack: {
16
+ // root: __dirname // 明确指定根目录
17
+ // },
17
18
  transpilePackages: ['@qlover/corekit-bridge', '@qlover/fe-corekit'],
18
19
  env: {
19
20
  APP_ENV: process.env.APP_ENV
@@ -3,18 +3,20 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "cross-env APP_ENV=localhost next dev --turbopack --port 3100",
7
- "dev:staging": "cross-env APP_ENV=staging next dev --turbopack --port 3100",
8
- "dev:prod": "cross-env APP_ENV=production next dev --turbopack --port 3100",
9
- "build": "cross-env APP_ENV=localhost next build --turbopack",
10
- "build:staging": "cross-env APP_ENV=staging next build --turbopack",
11
- "build:prod": "cross-env APP_ENV=production next build --turbopack",
6
+ "dev": "cross-env APP_ENV=localhost next dev --port 3100",
7
+ "dev:staging": "cross-env APP_ENV=staging next dev --port 3100",
8
+ "dev:prod": "cross-env APP_ENV=production next dev --port 3100",
9
+ "dev:turbo": "cross-env APP_ENV=localhost next dev --turbopack --port 3100",
10
+ "build": "cross-env APP_ENV=localhost next build",
11
+ "build:staging": "cross-env APP_ENV=staging next build",
12
+ "build:prod": "cross-env APP_ENV=production next build",
12
13
  "start": "next start --port 3101",
13
- "lint": "tsc --noEmit && eslint ./src ./config ./make ./migrations",
14
- "lint:fix": "tsc --noEmit && eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
14
+ "lint": "npm run type-check && eslint ./src ./config ./make ./migrations",
15
+ "lint:fix": "npm run type-check && eslint ./src ./config ./make ./migrations --ext .ts,.tsx --fix",
15
16
  "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
16
17
  "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
17
- "fix": "npm run lint:fix && npm run format"
18
+ "fix": "npm run lint:fix && npm run format",
19
+ "type-check": "tsc --build --force"
18
20
  },
19
21
  "dependencies": {
20
22
  "@ant-design/icons": "^6.0.0",
@@ -24,8 +26,9 @@
24
26
  "@brain-toolkit/antd-theme-override": "^0.0.3",
25
27
  "@brain-toolkit/bridge": "^0.0.1",
26
28
  "@brain-toolkit/react-kit": "^0.1.0",
27
- "@qlover/corekit-bridge": "latest",
28
- "@qlover/fe-corekit": "latest",
29
+ "@qlover/corekit-bridge": "../../../corekit-bridge",
30
+ "@qlover/fe-corekit": "../../../fe-corekit",
31
+ "@qlover/logger": "../../../logger",
29
32
  "@qlover/slice-store-react": "^1.4.1",
30
33
  "@supabase/postgrest-js": "^2.87.1",
31
34
  "@supabase/ssr": "^0.7.0",
@@ -19,7 +19,6 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
19
19
  const { tt } = props;
20
20
  const t = useWarnTranslations();
21
21
  const userService = useIOC(I.UserServiceInterface);
22
- const logger = useIOC(I.Logger);
23
22
  const appConfig = useIOC(I.AppConfig);
24
23
  const routerService = useIOC(I.RouterServiceInterface);
25
24
  const [loading, setLoading] = useState(false);
@@ -29,8 +28,8 @@ export function LoginForm(props: { tt: LoginI18nInterface }) {
29
28
  setLoading(true);
30
29
  await userService.login(values);
31
30
  routerService.replaceHome();
32
- } catch (error) {
33
- logger.error(error);
31
+ } catch {
32
+ // do nothing
34
33
  } finally {
35
34
  setLoading(false);
36
35
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
+ ExecutorContextInterface,
2
3
  ExecutorError,
3
- type ExecutorContext,
4
- type ExecutorPlugin
4
+ LifecyclePluginInterface
5
5
  } from '@qlover/fe-corekit';
6
6
  import { inject, injectable } from 'inversify';
7
7
  import { i18nKeySchema } from '@config/i18n/i18nKeyScheam';
@@ -13,7 +13,9 @@ import type { I18nServiceInterface } from '../port/I18nServiceInterface';
13
13
  import type { UIDialogInterface } from '@qlover/corekit-bridge';
14
14
 
15
15
  @injectable()
16
- export class DialogErrorPlugin implements ExecutorPlugin {
16
+ export class DialogErrorPlugin
17
+ implements LifecyclePluginInterface<ExecutorContextInterface<unknown>>
18
+ {
17
19
  public readonly pluginName = 'DialogErrorPlugin';
18
20
 
19
21
  constructor(
@@ -27,7 +29,7 @@ export class DialogErrorPlugin implements ExecutorPlugin {
27
29
  /**
28
30
  * @override
29
31
  */
30
- public onError(context: ExecutorContext<unknown>): void | Promise<void> {
32
+ public onError(context: ExecutorContextInterface<unknown>): void {
31
33
  const { error, hooksRuntimes } = context;
32
34
  const runtimesError = hooksRuntimes.returnValue;
33
35
 
@@ -1,14 +1,13 @@
1
1
  import { clone, isObject } from 'lodash';
2
2
  import type {
3
- Encryptor,
4
- ExecutorContext,
5
- ExecutorPlugin,
6
- RequestAdapterConfig
3
+ ExecutorContextInterface,
4
+ LifecyclePluginInterface,
5
+ RequestAdapterConfig,
6
+ EncryptorInterface
7
7
  } from '@qlover/fe-corekit';
8
8
 
9
- export interface RequestEncryptPluginProps<
10
- Request = unknown
11
- > extends RequestAdapterConfig<Request> {
9
+ export interface RequestEncryptPluginProps<Request = unknown>
10
+ extends RequestAdapterConfig<Request> {
12
11
  /**
13
12
  * 加密密码在 HTTP 请求中
14
13
  *
@@ -19,16 +18,21 @@ export interface RequestEncryptPluginProps<
19
18
  encryptProps?: string[] | string;
20
19
  }
21
20
 
22
- export class RequestEncryptPlugin implements ExecutorPlugin<RequestEncryptPluginProps> {
21
+ export class RequestEncryptPlugin
22
+ implements
23
+ LifecyclePluginInterface<
24
+ ExecutorContextInterface<RequestEncryptPluginProps>
25
+ >
26
+ {
23
27
  public readonly pluginName = 'RequestEncryptPlugin';
24
28
 
25
- constructor(protected encryptor: Encryptor<string, string>) {}
29
+ constructor(protected encryptor: EncryptorInterface<string, string>) {}
26
30
 
27
31
  /**
28
32
  * @override
29
33
  */
30
34
  public onBefore(
31
- context: ExecutorContext<RequestEncryptPluginProps>
35
+ context: ExecutorContextInterface<RequestEncryptPluginProps>
32
36
  ): void | Promise<void> {
33
37
  const { responseType, encryptProps } = context.parameters;
34
38
 
@@ -1,10 +1,10 @@
1
- import { Base64Serializer, type Encryptor } from '@qlover/fe-corekit';
1
+ import { Base64Serializer, type EncryptorInterface } from '@qlover/fe-corekit';
2
2
  import { inject, injectable } from 'inversify';
3
3
  import { I } from '@config/IOCIdentifier';
4
4
  import type { AppConfig } from './AppConfig';
5
5
 
6
6
  @injectable()
7
- export class StringEncryptor implements Encryptor<string, string> {
7
+ export class StringEncryptor implements EncryptorInterface<string, string> {
8
8
  private readonly key;
9
9
 
10
10
  constructor(
@@ -13,10 +13,7 @@ export class I18nServiceState implements StoreStateInterface {
13
13
  public loading: boolean = false;
14
14
  constructor(public language: I18nServiceLocale) {}
15
15
  }
16
- export abstract class I18nServiceInterface
17
- extends StoreInterface<I18nServiceState>
18
- implements I18nServiceInterface
19
- {
16
+ export abstract class I18nServiceInterface extends StoreInterface<I18nServiceState> {
20
17
  /**
21
18
  * @override
22
19
  */
@@ -5,10 +5,14 @@ import {
5
5
  } from '../port/I18nServiceInterface';
6
6
  import type { I18nServiceLocale } from '../port/I18nServiceInterface';
7
7
  import type { useTranslations } from 'next-intl';
8
+ import { BootstrapExecutorPlugin } from '@qlover/corekit-bridge';
8
9
 
9
10
  type TranslationFunction = ReturnType<typeof useTranslations>;
10
11
 
11
- export class I18nService extends I18nServiceInterface {
12
+ export class I18nService
13
+ extends I18nServiceInterface
14
+ implements BootstrapExecutorPlugin
15
+ {
12
16
  public readonly pluginName = 'I18nService';
13
17
  protected pathname: string = '';
14
18
  protected translator: TranslationFunction | null = null;
@@ -34,7 +38,7 @@ export class I18nService extends I18nServiceInterface {
34
38
  /**
35
39
  * @override
36
40
  */
37
- public async onBefore(): Promise<void> {}
41
+ public onBefore(): void {}
38
42
 
39
43
  public override async changeLanguage(
40
44
  language: I18nServiceLocale
@@ -1,16 +1,20 @@
1
1
  import {
2
- FetchAbortPlugin,
2
+ ExecutorContextInterface,
3
3
  RequestAdapterFetch,
4
- RequestTransaction
4
+ RequestExecutor
5
5
  } from '@qlover/fe-corekit';
6
- import { inject, injectable } from 'inversify';
6
+ import { injectable } from 'inversify';
7
7
  import type { AppApiConfig } from '../appApi/AppApiRequester';
8
8
 
9
+ export interface AdminApiRequesterContext
10
+ extends ExecutorContextInterface<AppApiConfig> {}
11
+
9
12
  @injectable()
10
- export class AdminApiRequester extends RequestTransaction<AppApiConfig> {
11
- constructor(
12
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
13
- ) {
13
+ export class AdminApiRequester extends RequestExecutor<
14
+ AppApiConfig,
15
+ AdminApiRequesterContext
16
+ > {
17
+ constructor() {
14
18
  super(
15
19
  new RequestAdapterFetch({
16
20
  baseURL: '/api/admin',
@@ -8,7 +8,8 @@ import {
8
8
  type AppApiTransaction
9
9
  } from '../appApi/AppApiRequester';
10
10
  import type { ResourceInterface, ResourceQuery } from '@qlover/corekit-bridge';
11
- import type { RequestTransaction } from '@qlover/fe-corekit';
11
+ import { RequestExecutor } from '@qlover/fe-corekit';
12
+ import { AdminApiRequesterContext } from './AdminApiRequester';
12
13
 
13
14
  export type AdminLocalesListTransaction = AppApiTransaction<
14
15
  ResourceQuery,
@@ -24,17 +25,17 @@ export type AdminLocalesUpdateTransaction = AppApiTransaction<
24
25
  export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
25
26
  constructor(
26
27
  @inject(AppApiRequester)
27
- protected client: RequestTransaction<AppApiConfig>
28
+ protected client: RequestExecutor<AppApiConfig, AdminApiRequesterContext>
28
29
  ) {}
29
30
 
30
31
  /**
31
32
  * @override
32
33
  */
33
34
  public create(data: LocalesSchema): Promise<unknown> {
34
- return this.client.request<AdminLocalesListTransaction>({
35
+ return this.client.request({
35
36
  url: '/admin/locales/create',
36
37
  method: 'POST',
37
- data: data as unknown as Record<string, unknown>
38
+ data: data
38
39
  });
39
40
  }
40
41
 
@@ -42,10 +43,10 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
42
43
  * @override
43
44
  */
44
45
  public remove(data: Partial<LocalesSchema>): Promise<unknown> {
45
- return this.client.request<AdminLocalesListTransaction>({
46
+ return this.client.request({
46
47
  url: '/admin/locales',
47
48
  method: 'DELETE',
48
- data: data as unknown as Record<string, unknown>
49
+ data
49
50
  });
50
51
  }
51
52
 
@@ -55,7 +56,7 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
55
56
  public search(
56
57
  params: ResourceQuery
57
58
  ): Promise<AdminLocalesListTransaction['response']> {
58
- return this.client.request<AdminLocalesListTransaction>({
59
+ return this.client.request({
59
60
  url: '/admin/locales',
60
61
  method: 'GET',
61
62
  params: params as unknown as Record<string, unknown>
@@ -66,10 +67,10 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
66
67
  * @override
67
68
  */
68
69
  public export(data: LocalesSchema): Promise<unknown> {
69
- return this.client.request<AdminLocalesListTransaction>({
70
+ return this.client.request({
70
71
  url: '/admin/locales',
71
72
  method: 'GET',
72
- data: data as unknown as Record<string, unknown>
73
+ data
73
74
  });
74
75
  }
75
76
 
@@ -79,7 +80,7 @@ export class AdminLocalesApi implements ResourceInterface<LocalesSchema> {
79
80
  public update(
80
81
  data: Partial<LocalesSchema>
81
82
  ): Promise<AdminLocalesUpdateTransaction['response']> {
82
- return this.client.request<AdminLocalesUpdateTransaction>({
83
+ return this.client.request({
83
84
  url: `/admin/locales/update`,
84
85
  method: 'POST',
85
86
  data
@@ -7,7 +7,8 @@ import {
7
7
  type AppApiTransaction
8
8
  } from '../appApi/AppApiRequester';
9
9
  import type { ResourceInterface, ResourceQuery } from '@qlover/corekit-bridge';
10
- import type { RequestTransaction } from '@qlover/fe-corekit';
10
+ import { RequestExecutor } from '@qlover/fe-corekit';
11
+ import { AdminApiRequesterContext } from './AdminApiRequester';
11
12
 
12
13
  export type AdminUserListTransaction = AppApiTransaction<
13
14
  ResourceQuery,
@@ -18,7 +19,7 @@ export type AdminUserListTransaction = AppApiTransaction<
18
19
  export class AdminUserApi implements ResourceInterface<UserSchema> {
19
20
  constructor(
20
21
  @inject(AppApiRequester)
21
- protected client: RequestTransaction<AppApiConfig>
22
+ protected client: RequestExecutor<AppApiConfig, AdminApiRequesterContext>
22
23
  ) {}
23
24
 
24
25
  /**
@@ -27,10 +28,13 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
27
28
  public async search(
28
29
  params: AdminUserListTransaction['request']
29
30
  ): Promise<AdminUserListTransaction['response']> {
30
- const response = await this.client.request<AdminUserListTransaction>({
31
+ const response = await this.client.request<
32
+ AdminUserListTransaction['response'],
33
+ AdminUserListTransaction['request']
34
+ >({
31
35
  url: '/admin/users',
32
36
  method: 'GET',
33
- params: params as unknown as Record<string, unknown>
37
+ params
34
38
  });
35
39
 
36
40
  return response;
@@ -40,10 +44,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
40
44
  * @override
41
45
  */
42
46
  public create(data: UserSchema): Promise<unknown> {
43
- return this.client.request<AdminUserListTransaction>({
47
+ return this.client.request({
44
48
  url: '/admin/users',
45
49
  method: 'POST',
46
- data: data as unknown as Record<string, unknown>
50
+ data
47
51
  });
48
52
  }
49
53
 
@@ -51,10 +55,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
51
55
  * @override
52
56
  */
53
57
  public remove(data: UserSchema): Promise<unknown> {
54
- return this.client.request<AdminUserListTransaction>({
58
+ return this.client.request({
55
59
  url: '/admin/users',
56
60
  method: 'DELETE',
57
- data: data as unknown as Record<string, unknown>
61
+ data
58
62
  });
59
63
  }
60
64
 
@@ -62,10 +66,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
62
66
  * @override
63
67
  */
64
68
  public update(data: UserSchema): Promise<unknown> {
65
- return this.client.request<AdminUserListTransaction>({
69
+ return this.client.request({
66
70
  url: '/admin/users',
67
71
  method: 'PUT',
68
- data: data as unknown as Record<string, unknown>
72
+ data
69
73
  });
70
74
  }
71
75
 
@@ -73,10 +77,10 @@ export class AdminUserApi implements ResourceInterface<UserSchema> {
73
77
  * @override
74
78
  */
75
79
  public export(data: UserSchema): Promise<unknown> {
76
- return this.client.request<AdminUserListTransaction>({
80
+ return this.client.request({
77
81
  url: '/admin/users',
78
82
  method: 'GET',
79
- data: data as unknown as Record<string, unknown>
83
+ data
80
84
  });
81
85
  }
82
86
  }
@@ -1,14 +1,16 @@
1
1
  import {
2
+ ExecutorContextInterface,
2
3
  ExecutorError,
3
- RequestError,
4
- type ExecutorContext,
5
- type ExecutorPlugin
4
+ isRequestAdapterResponse,
5
+ LifecyclePluginInterface
6
6
  } from '@qlover/fe-corekit';
7
7
  import type { AppApiErrorInterface } from '@/base/port/AppApiInterface';
8
8
  import type { AppApiConfig } from './AppApiRequester';
9
9
  import type { LoggerInterface } from '@qlover/logger';
10
10
 
11
- export class AppApiPlugin implements ExecutorPlugin {
11
+ export class AppApiPlugin implements LifecyclePluginInterface<
12
+ ExecutorContextInterface<AppApiConfig>
13
+ > {
12
14
  public readonly pluginName = 'AppApiPlugin';
13
15
 
14
16
  constructor(protected logger: LoggerInterface) {}
@@ -30,48 +32,46 @@ export class AppApiPlugin implements ExecutorPlugin {
30
32
  /**
31
33
  * @override
32
34
  */
33
- public onSuccess(
34
- context: ExecutorContext<AppApiConfig>
35
- ): void | Promise<void> {
35
+ public onSuccess(context: ExecutorContextInterface<AppApiConfig>): void {
36
36
  const response = context.returnValue;
37
37
  const { parameters } = context;
38
38
 
39
+ // Important: 当响应数据失败则抛出错误
40
+ if (isRequestAdapterResponse(response)) {
41
+ if (this.isAppApiErrorInterface(response.data)) {
42
+ throw new ExecutorError(response.data.message || response.data.id, response);
43
+ }
44
+ }
45
+
39
46
  this.logger.info(
40
47
  `%c[AppApi ${parameters.method} ${parameters.url}]%c - ${new Date().toLocaleString()}`,
41
48
  'color: #0f0;',
42
49
  'color: inherit;',
43
50
  response
44
51
  );
45
-
46
- if (this.isAppApiErrorInterface(response)) {
47
- throw new Error(response.message || response.id);
48
- }
49
52
  }
50
53
 
51
54
  /**
52
55
  * @override
53
56
  */
54
57
  public async onError(
55
- context: ExecutorContext<AppApiConfig>
58
+ context: ExecutorContextInterface<AppApiConfig>
56
59
  ): Promise<ExecutorError | void> {
57
60
  const { error, parameters } = context;
58
61
 
59
62
  this.loggerError(parameters, error);
60
63
 
61
- if (error instanceof RequestError && parameters.responseType === 'json') {
62
- // @ts-expect-error response is not defined in Error
63
- let response = error?.response;
64
+ if (error instanceof Error && parameters.responseType === 'json') {
65
+ let response = error?.cause;
64
66
 
65
67
  if (response instanceof Response) {
66
68
  // clone the response to avoid mutating the original response
67
69
  response = response.clone();
68
70
 
69
- const json = await this.getResponseJson(response);
71
+ const json = await this.getResponseJson(response as Response);
70
72
 
71
73
  if (this.isAppApiErrorInterface(json)) {
72
- const newError = new ExecutorError(json.id, json.message);
73
- // context.error = newError;
74
- return newError;
74
+ return new ExecutorError(json.id, json);
75
75
  }
76
76
  }
77
77
  }
@@ -1,19 +1,24 @@
1
- import {
2
- FetchAbortPlugin,
3
- RequestAdapterFetch,
4
- RequestTransaction
5
- } from '@qlover/fe-corekit';
6
- import { inject, injectable } from 'inversify';
1
+ import { LifecycleExecutor, RequestAdapterFetch, RequestExecutor } from '@qlover/fe-corekit';
2
+ import { injectable } from 'inversify';
7
3
  import type { RequestEncryptPluginProps } from '@/base/cases/RequestEncryptPlugin';
8
4
  import type { AppApiResult } from '@/base/port/AppApiInterface';
9
5
  import type {
6
+ ExecutorContextInterface,
10
7
  RequestAdapterConfig,
11
- RequestAdapterResponse,
12
- RequestTransactionInterface
8
+ RequestAdapterResponse
13
9
  } from '@qlover/fe-corekit';
14
10
 
11
+ export interface RequestTransactionInterface<Request, Response> {
12
+ request: Request;
13
+ response: Response;
14
+ }
15
+
15
16
  export interface AppApiConfig<Request = unknown>
16
- extends RequestAdapterConfig<Request>, RequestEncryptPluginProps<Request> {}
17
+ extends RequestAdapterConfig<Request>,
18
+ RequestEncryptPluginProps<Request> {}
19
+
20
+ export interface AppApiRequesterContext
21
+ extends ExecutorContextInterface<AppApiConfig> {}
17
22
 
18
23
  /**
19
24
  * UserApiResponse
@@ -32,26 +37,26 @@ export type AppApiResponse<
32
37
  /**
33
38
  * UserApi common transaction
34
39
  */
35
- export interface AppApiTransaction<
36
- Request = unknown,
37
- Response = unknown
38
- > extends RequestTransactionInterface<
39
- AppApiConfig<Request>,
40
- AppApiResponse<Request, Response>
41
- > {
40
+ export interface AppApiTransaction<Request = unknown, Response = unknown>
41
+ extends RequestTransactionInterface<
42
+ AppApiConfig<Request>,
43
+ AppApiResponse<Request, Response>
44
+ > {
42
45
  data: AppApiConfig<Request>['data'];
43
46
  }
44
47
 
45
48
  @injectable()
46
- export class AppApiRequester extends RequestTransaction<AppApiConfig> {
47
- constructor(
48
- @inject(FetchAbortPlugin) protected abortPlugin: FetchAbortPlugin
49
- ) {
49
+ export class AppApiRequester extends RequestExecutor<
50
+ AppApiConfig,
51
+ AppApiRequesterContext
52
+ > {
53
+ constructor() {
50
54
  super(
51
55
  new RequestAdapterFetch({
52
56
  baseURL: '/api',
53
57
  responseType: 'json'
54
- })
58
+ }),
59
+ new LifecycleExecutor()
55
60
  );
56
61
  }
57
62
  }