@akanjs/cli 0.0.142 → 0.0.144
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/README.md +8 -0
- package/cjs/index.js +2437 -3204
- package/cjs/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/cjs/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/cjs/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/cjs/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/cjs/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/cjs/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/cjs/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/cjs/src/guidelines/framework/framework.instruction.md +1112 -0
- package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/cjs/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/cjs/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/cjs/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/cjs/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/cjs/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/cjs/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/cjs/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/cjs/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/cjs/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/cjs/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/cjs/src/templates/module/__model__.dictionary.js +4 -5
- package/esm/index.js +2524 -3286
- package/esm/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/esm/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/esm/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/esm/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/esm/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/esm/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/esm/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/esm/src/guidelines/framework/framework.instruction.md +1112 -0
- package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/esm/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/esm/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/esm/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/esm/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/esm/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/esm/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/esm/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/esm/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/esm/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/esm/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/esm/src/templates/module/__model__.dictionary.js +4 -5
- package/package.json +3 -1
- package/src/guideline/guideline.command.d.ts +7 -0
- package/src/guideline/guideline.prompt.d.ts +15 -0
- package/src/guideline/guideline.runner.d.ts +5 -0
- package/src/guideline/guideline.script.d.ts +6 -0
- package/src/guidelines/___library/sharedUiStructureDescription.en.md +786 -0
- package/src/guidelines/___library/utilUiStructureDescription.en.md +395 -0
- package/src/guidelines/___lint/lintRuleDescription.en.md +64 -0
- package/src/guidelines/___module/moduleStructureDescription.en.md +80 -0
- package/src/guidelines/componentRule/componentRule.instruction.md +732 -0
- package/src/guidelines/databaseModule/databaseModule.instruction.md +691 -0
- package/src/guidelines/enumConstant/enumConstant.instruction.md +232 -0
- package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +611 -0
- package/src/guidelines/framework/framework.instruction.md +1112 -0
- package/src/guidelines/modelConstant/modelConstant.instruction.md +958 -0
- package/src/guidelines/modelDictionary/modelDictionary.instruction.md +583 -0
- package/src/guidelines/modelDocument/modelDocument.instruction.md +683 -0
- package/src/guidelines/modelService/modelService.instruction.md +935 -0
- package/src/guidelines/modelSignal/modelSignal.instruction.md +588 -0
- package/src/guidelines/modelStore/modelStore.instruction.md +591 -0
- package/src/guidelines/modelTemplate/modelTemplate.instruction.md +577 -0
- package/src/guidelines/modelUnit/modelUnit.instruction.md +833 -0
- package/src/guidelines/modelUtil/modelUtil.instruction.md +752 -0
- package/src/guidelines/modelView/modelView.instruction.md +1005 -0
- package/src/guidelines/modelZone/modelZone.instruction.md +528 -0
- package/src/guidelines/scalarConstant/scalarConstant.instruction.md +489 -0
- package/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +347 -0
- package/src/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +318 -0
- package/src/guidelines/utilUiUsage/utilUiUsage.instruction.md +339 -0
- package/src/module/module.command.d.ts +6 -8
- package/src/module/module.prompt.d.ts +2 -15
- package/src/module/module.request.d.ts +22 -18
- package/src/module/module.runner.d.ts +4 -20
- package/src/module/module.script.d.ts +6 -7
- package/src/scalar/scalar.command.d.ts +7 -0
- package/src/scalar/scalar.prompt.d.ts +23 -0
- package/src/scalar/scalar.runner.d.ts +13 -0
- package/src/scalar/scalar.script.d.ts +6 -0
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.js +0 -47
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/page.js +0 -128
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.js +0 -42
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.js +0 -50
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.js +0 -41
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.js +0 -51
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(user)/layout.js +0 -43
- package/cjs/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.js +0 -60
- package/cjs/src/templates/app/app/[lang]/(__appName__)/layout.js +0 -54
- package/cjs/src/templates/app/app/[lang]/(__appName__)/styles.css.template +0 -19
- package/cjs/src/templates/app/app/[lang]/admin/layout.js +0 -54
- package/cjs/src/templates/app/app/[lang]/admin/page.js +0 -63
- package/cjs/src/templates/app/app/csr.js +0 -34
- package/cjs/src/templates/app/app/index.html.template +0 -13
- package/cjs/src/templates/app/app/layout.js +0 -38
- package/cjs/src/templates/app/capacitor.config.ts.template +0 -8
- package/cjs/src/templates/app/env/env.client.debug.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.develop.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.local.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.main.ts.template +0 -7
- package/cjs/src/templates/app/env/env.client.testing.ts.template +0 -7
- package/cjs/src/templates/app/env/env.server.debug.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.develop.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.local.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.main.ts.template +0 -15
- package/cjs/src/templates/app/env/env.server.testing.ts.template +0 -7
- package/cjs/src/templates/app/lib/setting/Setting.Template.js +0 -57
- package/cjs/src/templates/app/lib/setting/Setting.Unit.js +0 -38
- package/cjs/src/templates/app/lib/setting/Setting.Util.js +0 -34
- package/cjs/src/templates/app/lib/setting/Setting.View.js +0 -51
- package/cjs/src/templates/app/lib/setting/Setting.Zone.js +0 -80
- package/cjs/src/templates/app/lib/setting/index.js +0 -61
- package/cjs/src/templates/app/lib/summary/Summary.Template.js +0 -43
- package/cjs/src/templates/app/lib/summary/Summary.Unit.js +0 -38
- package/cjs/src/templates/app/lib/summary/Summary.Util.js +0 -33
- package/cjs/src/templates/app/lib/summary/Summary.View.js +0 -51
- package/cjs/src/templates/app/lib/summary/Summary.Zone.js +0 -62
- package/cjs/src/templates/app/lib/summary/index.js +0 -67
- package/cjs/src/templates/app/lib/user/User.Template.js +0 -65
- package/cjs/src/templates/app/lib/user/User.Unit.js +0 -38
- package/cjs/src/templates/app/lib/user/User.Util.js +0 -94
- package/cjs/src/templates/app/lib/user/User.View.js +0 -66
- package/cjs/src/templates/app/lib/user/User.Zone.js +0 -74
- package/cjs/src/templates/app/lib/user/index.js +0 -61
- package/cjs/src/templates/app/page.test.ts.template +0 -10
- package/cjs/src/templates/app/playwright.config.ts.template +0 -6
- package/cjs/src/templates/app/postcss.config.js.template +0 -10
- package/cjs/src/templates/app/public/manifest.json.template +0 -67
- package/cjs/src/templates/app/tsconfig.json.template +0 -22
- package/cjs/src/templates/app/tsconfig.spec.json.template +0 -7
- package/cjs/src/templates/app/ui/Footer.js +0 -67
- package/cjs/src/templates/app/ui/MainHeader.js +0 -131
- package/cjs/src/templates/crudPages/[__model__Id]/edit/page.js +0 -73
- package/cjs/src/templates/crudPages/[__model__Id]/page.js +0 -83
- package/cjs/src/templates/crudPages/new/page.js +0 -70
- package/cjs/src/templates/crudPages/page.js +0 -71
- package/cjs/src/templates/libRoot/.gitignore.template +0 -15
- package/cjs/src/templates/libRoot/env/env.server.example.ts.template +0 -7
- package/cjs/src/templates/libRoot/env/env.server.testing.ts.template +0 -7
- package/cjs/src/templates/libRoot/lib/setting/Setting.Template.js +0 -57
- package/cjs/src/templates/libRoot/lib/setting/Setting.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/setting/Setting.Util.js +0 -34
- package/cjs/src/templates/libRoot/lib/setting/Setting.View.js +0 -51
- package/cjs/src/templates/libRoot/lib/setting/Setting.Zone.js +0 -80
- package/cjs/src/templates/libRoot/lib/setting/index.js +0 -61
- package/cjs/src/templates/libRoot/lib/summary/Summary.Template.js +0 -43
- package/cjs/src/templates/libRoot/lib/summary/Summary.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/summary/Summary.Util.js +0 -33
- package/cjs/src/templates/libRoot/lib/summary/Summary.View.js +0 -51
- package/cjs/src/templates/libRoot/lib/summary/Summary.Zone.js +0 -62
- package/cjs/src/templates/libRoot/lib/summary/index.js +0 -67
- package/cjs/src/templates/libRoot/lib/user/User.Template.js +0 -65
- package/cjs/src/templates/libRoot/lib/user/User.Unit.js +0 -38
- package/cjs/src/templates/libRoot/lib/user/User.Util.js +0 -94
- package/cjs/src/templates/libRoot/lib/user/User.View.js +0 -66
- package/cjs/src/templates/libRoot/lib/user/User.Zone.js +0 -74
- package/cjs/src/templates/libRoot/lib/user/index.js +0 -61
- package/cjs/src/templates/libRoot/package.json.template +0 -4
- package/cjs/src/templates/libRoot/tsconfig.json.template +0 -13
- package/cjs/src/templates/libRoot/tsconfig.spec.json.template +0 -7
- package/cjs/src/templates/localDev/docker-compose.yaml.template +0 -36
- package/cjs/src/templates/module/__Model__.Template.js +0 -54
- package/cjs/src/templates/module/__Model__.Unit.js +0 -42
- package/cjs/src/templates/module/__Model__.Util.js +0 -70
- package/cjs/src/templates/module/__Model__.View.js +0 -48
- package/cjs/src/templates/module/__Model__.Zone.js +0 -83
- package/cjs/src/templates/module/index.js +0 -61
- package/cjs/src/templates/pkgRoot/tsconfig.json.template +0 -15
- package/cjs/src/templates/workspaceRoot/.env.template +0 -20
- package/cjs/src/templates/workspaceRoot/.gitignore.template +0 -118
- package/cjs/src/templates/workspaceRoot/.prettierignore.template +0 -10
- package/cjs/src/templates/workspaceRoot/.prettierrc.json.template +0 -6
- package/cjs/src/templates/workspaceRoot/.swcrc.template +0 -9
- package/cjs/src/templates/workspaceRoot/.vscode/settings.json.template +0 -13
- package/cjs/src/templates/workspaceRoot/README.md.template +0 -37
- package/cjs/src/templates/workspaceRoot/eslint.config.ts.template +0 -3
- package/cjs/src/templates/workspaceRoot/package.json.template +0 -43
- package/cjs/src/templates/workspaceRoot/tsconfig.json.template +0 -29
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.js +0 -27
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/page.js +0 -108
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.js +0 -22
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.js +0 -30
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.js +0 -21
- package/esm/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.js +0 -31
- package/esm/src/templates/app/app/[lang]/(__appName__)/(user)/layout.js +0 -23
- package/esm/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.js +0 -40
- package/esm/src/templates/app/app/[lang]/(__appName__)/layout.js +0 -34
- package/esm/src/templates/app/app/[lang]/(__appName__)/styles.css.template +0 -19
- package/esm/src/templates/app/app/[lang]/admin/layout.js +0 -34
- package/esm/src/templates/app/app/[lang]/admin/page.js +0 -43
- package/esm/src/templates/app/app/csr.js +0 -14
- package/esm/src/templates/app/app/index.html.template +0 -13
- package/esm/src/templates/app/app/layout.js +0 -18
- package/esm/src/templates/app/capacitor.config.ts.template +0 -8
- package/esm/src/templates/app/env/env.client.debug.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.develop.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.local.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.main.ts.template +0 -7
- package/esm/src/templates/app/env/env.client.testing.ts.template +0 -7
- package/esm/src/templates/app/env/env.server.debug.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.develop.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.local.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.main.ts.template +0 -15
- package/esm/src/templates/app/env/env.server.testing.ts.template +0 -7
- package/esm/src/templates/app/lib/setting/Setting.Template.js +0 -37
- package/esm/src/templates/app/lib/setting/Setting.Unit.js +0 -18
- package/esm/src/templates/app/lib/setting/Setting.Util.js +0 -14
- package/esm/src/templates/app/lib/setting/Setting.View.js +0 -31
- package/esm/src/templates/app/lib/setting/Setting.Zone.js +0 -60
- package/esm/src/templates/app/lib/setting/index.js +0 -41
- package/esm/src/templates/app/lib/summary/Summary.Template.js +0 -23
- package/esm/src/templates/app/lib/summary/Summary.Unit.js +0 -18
- package/esm/src/templates/app/lib/summary/Summary.Util.js +0 -13
- package/esm/src/templates/app/lib/summary/Summary.View.js +0 -31
- package/esm/src/templates/app/lib/summary/Summary.Zone.js +0 -42
- package/esm/src/templates/app/lib/summary/index.js +0 -47
- package/esm/src/templates/app/lib/user/User.Template.js +0 -45
- package/esm/src/templates/app/lib/user/User.Unit.js +0 -18
- package/esm/src/templates/app/lib/user/User.Util.js +0 -74
- package/esm/src/templates/app/lib/user/User.View.js +0 -46
- package/esm/src/templates/app/lib/user/User.Zone.js +0 -54
- package/esm/src/templates/app/lib/user/index.js +0 -41
- package/esm/src/templates/app/page.test.ts.template +0 -10
- package/esm/src/templates/app/playwright.config.ts.template +0 -6
- package/esm/src/templates/app/postcss.config.js.template +0 -10
- package/esm/src/templates/app/public/manifest.json.template +0 -67
- package/esm/src/templates/app/tsconfig.json.template +0 -22
- package/esm/src/templates/app/tsconfig.spec.json.template +0 -7
- package/esm/src/templates/app/ui/Footer.js +0 -47
- package/esm/src/templates/app/ui/MainHeader.js +0 -111
- package/esm/src/templates/crudPages/[__model__Id]/edit/page.js +0 -53
- package/esm/src/templates/crudPages/[__model__Id]/page.js +0 -63
- package/esm/src/templates/crudPages/new/page.js +0 -50
- package/esm/src/templates/crudPages/page.js +0 -51
- package/esm/src/templates/libRoot/.gitignore.template +0 -15
- package/esm/src/templates/libRoot/env/env.server.example.ts.template +0 -7
- package/esm/src/templates/libRoot/env/env.server.testing.ts.template +0 -7
- package/esm/src/templates/libRoot/lib/setting/Setting.Template.js +0 -37
- package/esm/src/templates/libRoot/lib/setting/Setting.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/setting/Setting.Util.js +0 -14
- package/esm/src/templates/libRoot/lib/setting/Setting.View.js +0 -31
- package/esm/src/templates/libRoot/lib/setting/Setting.Zone.js +0 -60
- package/esm/src/templates/libRoot/lib/setting/index.js +0 -41
- package/esm/src/templates/libRoot/lib/summary/Summary.Template.js +0 -23
- package/esm/src/templates/libRoot/lib/summary/Summary.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/summary/Summary.Util.js +0 -13
- package/esm/src/templates/libRoot/lib/summary/Summary.View.js +0 -31
- package/esm/src/templates/libRoot/lib/summary/Summary.Zone.js +0 -42
- package/esm/src/templates/libRoot/lib/summary/index.js +0 -47
- package/esm/src/templates/libRoot/lib/user/User.Template.js +0 -45
- package/esm/src/templates/libRoot/lib/user/User.Unit.js +0 -18
- package/esm/src/templates/libRoot/lib/user/User.Util.js +0 -74
- package/esm/src/templates/libRoot/lib/user/User.View.js +0 -46
- package/esm/src/templates/libRoot/lib/user/User.Zone.js +0 -54
- package/esm/src/templates/libRoot/lib/user/index.js +0 -41
- package/esm/src/templates/libRoot/package.json.template +0 -4
- package/esm/src/templates/libRoot/tsconfig.json.template +0 -13
- package/esm/src/templates/libRoot/tsconfig.spec.json.template +0 -7
- package/esm/src/templates/localDev/docker-compose.yaml.template +0 -36
- package/esm/src/templates/module/__Model__.Template.js +0 -34
- package/esm/src/templates/module/__Model__.Unit.js +0 -22
- package/esm/src/templates/module/__Model__.Util.js +0 -50
- package/esm/src/templates/module/__Model__.View.js +0 -28
- package/esm/src/templates/module/__Model__.Zone.js +0 -63
- package/esm/src/templates/module/index.js +0 -41
- package/esm/src/templates/pkgRoot/tsconfig.json.template +0 -15
- package/esm/src/templates/workspaceRoot/.env.template +0 -20
- package/esm/src/templates/workspaceRoot/.gitignore.template +0 -118
- package/esm/src/templates/workspaceRoot/.prettierignore.template +0 -10
- package/esm/src/templates/workspaceRoot/.prettierrc.json.template +0 -6
- package/esm/src/templates/workspaceRoot/.swcrc.template +0 -9
- package/esm/src/templates/workspaceRoot/.vscode/settings.json.template +0 -13
- package/esm/src/templates/workspaceRoot/README.md.template +0 -37
- package/esm/src/templates/workspaceRoot/eslint.config.ts.template +0 -3
- package/esm/src/templates/workspaceRoot/package.json.template +0 -43
- package/esm/src/templates/workspaceRoot/tsconfig.json.template +0 -29
- package/src/application/application.prompt.d.ts +0 -2
- package/src/templates/app/app/[lang]/(__appName__)/(public)/forgotpassword/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/privacy/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/signin/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(public)/termsofservice/page.d.ts +0 -10
- package/src/templates/app/app/[lang]/(__appName__)/(public)/unknown/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(user)/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/(user)/self/page.d.ts +0 -9
- package/src/templates/app/app/[lang]/(__appName__)/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/admin/layout.d.ts +0 -9
- package/src/templates/app/app/[lang]/admin/page.d.ts +0 -9
- package/src/templates/app/app/csr.d.ts +0 -9
- package/src/templates/app/app/layout.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Template.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Unit.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Util.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.View.d.ts +0 -9
- package/src/templates/app/lib/setting/Setting.Zone.d.ts +0 -9
- package/src/templates/app/lib/setting/index.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Template.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Unit.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Util.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.View.d.ts +0 -9
- package/src/templates/app/lib/summary/Summary.Zone.d.ts +0 -9
- package/src/templates/app/lib/summary/index.d.ts +0 -9
- package/src/templates/app/lib/user/User.Template.d.ts +0 -9
- package/src/templates/app/lib/user/User.Unit.d.ts +0 -9
- package/src/templates/app/lib/user/User.Util.d.ts +0 -9
- package/src/templates/app/lib/user/User.View.d.ts +0 -9
- package/src/templates/app/lib/user/User.Zone.d.ts +0 -9
- package/src/templates/app/lib/user/index.d.ts +0 -9
- package/src/templates/app/ui/Footer.d.ts +0 -9
- package/src/templates/app/ui/MainHeader.d.ts +0 -10
- package/src/templates/crudPages/[__model__Id]/edit/page.d.ts +0 -11
- package/src/templates/crudPages/[__model__Id]/page.d.ts +0 -11
- package/src/templates/crudPages/new/page.d.ts +0 -11
- package/src/templates/crudPages/page.d.ts +0 -11
- package/src/templates/libRoot/lib/setting/Setting.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.View.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/Setting.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/setting/index.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.View.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/Summary.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/summary/index.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Template.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Unit.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Util.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.View.d.ts +0 -9
- package/src/templates/libRoot/lib/user/User.Zone.d.ts +0 -9
- package/src/templates/libRoot/lib/user/index.d.ts +0 -9
- package/src/templates/module/__Model__.Template.d.ts +0 -11
- package/src/templates/module/__Model__.Unit.d.ts +0 -11
- package/src/templates/module/__Model__.Util.d.ts +0 -11
- package/src/templates/module/__Model__.View.d.ts +0 -11
- package/src/templates/module/__Model__.Zone.d.ts +0 -11
- package/src/templates/module/index.d.ts +0 -11
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
# Akan.js Model Service Implementation Guide
|
|
2
|
+
|
|
3
|
+
## Purpose and Role of model.service.ts Files
|
|
4
|
+
|
|
5
|
+
In the Akan.js framework, `model.service.ts` files are central components that implement business logic and manage interactions with data models. These services serve multiple critical purposes:
|
|
6
|
+
|
|
7
|
+
1. **Business Logic Layer** - Encapsulate domain-specific operations and rules
|
|
8
|
+
2. **Data Orchestration** - Coordinate operations between different models and external systems
|
|
9
|
+
3. **Transaction Management** - Handle complex multi-step operations that may involve multiple models
|
|
10
|
+
4. **Lifecycle Management** - Initialize resources and clean up when the application starts or stops
|
|
11
|
+
5. **Scheduled Tasks** - Execute recurring operations using cron jobs and intervals
|
|
12
|
+
6. **Security** - Implement authorization and permission checks
|
|
13
|
+
7. **Integration** - Connect with external APIs, message queues, and other services
|
|
14
|
+
|
|
15
|
+
Services in Akan.js follow a stateless design pattern, where any persistent state should be managed through MongoDB and Redis, not within the service instance itself.
|
|
16
|
+
|
|
17
|
+
## Service Structure and Inheritance
|
|
18
|
+
|
|
19
|
+
Akan.js services typically extend base service classes provided by the framework:
|
|
20
|
+
|
|
21
|
+
### DbService
|
|
22
|
+
|
|
23
|
+
Most services extend `DbService` to work with MongoDB models:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
@Service("ProductService")
|
|
27
|
+
export class ProductService extends DbService(db.productDb) {
|
|
28
|
+
// Service methods here
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `DbService` function automatically injects the database model and provides numerous convenience methods for CRUD operations.
|
|
33
|
+
|
|
34
|
+
### LogService
|
|
35
|
+
|
|
36
|
+
For utility services that don't directly work with a database model:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
@Service("SecurityService")
|
|
40
|
+
export class SecurityService extends LogService("SecurityService") {
|
|
41
|
+
// Utility methods here
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Custom Service Base Classes
|
|
46
|
+
|
|
47
|
+
For specific domain services with shared functionality:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// In _libName/libName.service.ts
|
|
51
|
+
export const LibService = MixSrvs(SharedService, PlatformService, SocialService);
|
|
52
|
+
|
|
53
|
+
// In another service
|
|
54
|
+
@Service("CustomService")
|
|
55
|
+
export class CustomService extends LibService {
|
|
56
|
+
// Custom methods here
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Core Decorators
|
|
61
|
+
|
|
62
|
+
### @Service Decorator
|
|
63
|
+
|
|
64
|
+
Declares a class as an injectable NestJS service with additional configuration options:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
@Service("UserService", {
|
|
68
|
+
enabled: true, // Enable/disable service dynamically
|
|
69
|
+
serverMode: "federation", // "federation", "batch", or not specified
|
|
70
|
+
})
|
|
71
|
+
export class UserService extends DbService(db.userDb) {
|
|
72
|
+
// Service implementation
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Options:
|
|
77
|
+
|
|
78
|
+
- `enabled`: Boolean flag to conditionally disable a service (default: true)
|
|
79
|
+
- `serverMode`: Restricts the service to run only in specific server modes:
|
|
80
|
+
- `"federation"`: For API/GraphQL servers
|
|
81
|
+
- `"batch"`: For background processing servers
|
|
82
|
+
- Not specified: Runs in all modes
|
|
83
|
+
|
|
84
|
+
### @Srv Decorator
|
|
85
|
+
|
|
86
|
+
Injects another service by name:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
@Srv()
|
|
90
|
+
protected readonly userService: UserService; // Injects using capitalized property name
|
|
91
|
+
|
|
92
|
+
@Srv("CustomName")
|
|
93
|
+
protected readonly customService: SomeService; // Injects using specified name
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `@Srv` decorator automatically capitalizes the property name if no explicit name is provided. For example, `@Srv() userService` will look for a service named "UserService".
|
|
97
|
+
|
|
98
|
+
### @Use Decorator
|
|
99
|
+
|
|
100
|
+
Injects non-service dependencies like APIs, configurations, or utilities:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
@Use()
|
|
104
|
+
protected readonly emailApi: EmailApi; // Injects using capitalized property name
|
|
105
|
+
|
|
106
|
+
@Use("CustomKey")
|
|
107
|
+
protected readonly customConfig: SomeConfig; // Injects using specified name
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Similar to `@Srv`, the `@Use` decorator capitalizes property names by default.
|
|
111
|
+
|
|
112
|
+
### @Db Decorator
|
|
113
|
+
|
|
114
|
+
Injects a specific database model directly (rarely needed since `DbService` already provides this):
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
@Db("User")
|
|
118
|
+
protected readonly userModel: UserModel;
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### @Queue Decorator
|
|
122
|
+
|
|
123
|
+
Injects a Bull queue for background processing:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
@Queue()
|
|
127
|
+
protected readonly userQueue: Queue<UserSignal>;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### @Websocket Decorator
|
|
131
|
+
|
|
132
|
+
Injects a websocket server instance for real-time communication:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@Websocket()
|
|
136
|
+
protected readonly websocket: Websocket<ChatSignal>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Lifecycle Methods
|
|
140
|
+
|
|
141
|
+
Akan.js services can implement lifecycle hooks that are called by the NestJS framework.
|
|
142
|
+
|
|
143
|
+
### onModuleInit()
|
|
144
|
+
|
|
145
|
+
Called once the module has been initialized by NestJS. Use this for setup operations:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
async onModuleInit() {
|
|
149
|
+
// Initialize service resources
|
|
150
|
+
await this.loadInitialData();
|
|
151
|
+
this.setupEventListeners();
|
|
152
|
+
this.logger.log('Service initialized successfully');
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Common use cases:
|
|
157
|
+
|
|
158
|
+
- Initializing in-memory cache
|
|
159
|
+
- Setting up event listeners
|
|
160
|
+
- Loading configuration data
|
|
161
|
+
- Establishing connections to external services
|
|
162
|
+
|
|
163
|
+
### onModuleDestroy()
|
|
164
|
+
|
|
165
|
+
Called just before the application shuts down. Use this for cleanup operations:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
async onModuleDestroy() {
|
|
169
|
+
// Clean up resources
|
|
170
|
+
await Promise.all(this.connections.map(conn => conn.close()));
|
|
171
|
+
this.clearTimers();
|
|
172
|
+
this.logger.log('Service resources released');
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Common use cases:
|
|
177
|
+
|
|
178
|
+
- Closing database connections
|
|
179
|
+
- Releasing external resources
|
|
180
|
+
- Clearing timers and intervals
|
|
181
|
+
- Unregistering event listeners
|
|
182
|
+
|
|
183
|
+
## Scheduled Tasks
|
|
184
|
+
|
|
185
|
+
Akan.js provides decorators for scheduling recurring tasks.
|
|
186
|
+
|
|
187
|
+
### @Cron Decorator
|
|
188
|
+
|
|
189
|
+
Executes methods on a cron schedule:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
@Cron("0 0 * * *") // Run at midnight every day
|
|
193
|
+
async dailyCleanup() {
|
|
194
|
+
await this.cleanupExpiredRecords();
|
|
195
|
+
this.logger.log('Daily cleanup completed');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@Cron("*/5 * * * *", { serverMode: "batch" }) // Run every 5 minutes, only in batch mode
|
|
199
|
+
async periodicSync() {
|
|
200
|
+
await this.syncExternalData();
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The first parameter uses standard cron syntax:
|
|
205
|
+
|
|
206
|
+
- `* * * * *` = minute hour day-of-month month day-of-week
|
|
207
|
+
- Special patterns like `@daily`, `@hourly` are also supported
|
|
208
|
+
|
|
209
|
+
Options include:
|
|
210
|
+
|
|
211
|
+
- `serverMode`: Restrict to a specific server mode (e.g., "batch")
|
|
212
|
+
- `name`: Assign a name to the scheduled job
|
|
213
|
+
- `timeZone`: Specify the timezone for the schedule
|
|
214
|
+
|
|
215
|
+
### @Interval Decorator
|
|
216
|
+
|
|
217
|
+
Executes methods at fixed time intervals in milliseconds:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
@Interval(60000) // Run every minute (60,000 ms)
|
|
221
|
+
async checkStatus() {
|
|
222
|
+
await this.monitorSystemHealth();
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Options:
|
|
227
|
+
|
|
228
|
+
- `name`: Assign a name to the interval
|
|
229
|
+
|
|
230
|
+
## Database Operations
|
|
231
|
+
|
|
232
|
+
When extending `DbService`, your service automatically inherits numerous methods for working with the database model.
|
|
233
|
+
|
|
234
|
+
### Basic CRUD Operations
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// Create
|
|
238
|
+
const newProduct = await this.createProduct(productData);
|
|
239
|
+
|
|
240
|
+
// Read
|
|
241
|
+
const product = await this.getProduct(productId); // Throws if not found
|
|
242
|
+
const maybeProduct = await this.loadProduct(productId); // Returns null if not found
|
|
243
|
+
const products = await this.loadProductMany([id1, id2, id3]); // Load multiple by IDs
|
|
244
|
+
|
|
245
|
+
// Update
|
|
246
|
+
const updatedProduct = await this.updateProduct(productId, updateData);
|
|
247
|
+
|
|
248
|
+
// Delete (soft delete)
|
|
249
|
+
const removedProduct = await this.removeProduct(productId);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Query Operations
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// List with filtering, sorting, and pagination
|
|
256
|
+
const products = await this.listActiveProducts(categoryId, {
|
|
257
|
+
skip: 0,
|
|
258
|
+
limit: 10,
|
|
259
|
+
sort: "priceAsc",
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Get IDs only
|
|
263
|
+
const productIds = await this.listIdsActiveProducts(categoryId);
|
|
264
|
+
|
|
265
|
+
// Find a single item (returns null if not found)
|
|
266
|
+
const product = await this.findActiveProduct(categoryId);
|
|
267
|
+
|
|
268
|
+
// Pick a single item (throws if not found)
|
|
269
|
+
const product = await this.pickActiveProduct(categoryId);
|
|
270
|
+
|
|
271
|
+
// Check existence
|
|
272
|
+
const exists = await this.existsActiveProduct(categoryId);
|
|
273
|
+
|
|
274
|
+
// Count
|
|
275
|
+
const count = await this.countActiveProducts(categoryId);
|
|
276
|
+
|
|
277
|
+
// Insight (aggregate statistics)
|
|
278
|
+
const stats = await this.insightActiveProducts(categoryId);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Search Operations (with Meilisearch)
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Full-text search with pagination
|
|
285
|
+
const { docs, count } = await this.searchProduct("wireless headphones", {
|
|
286
|
+
skip: 0,
|
|
287
|
+
limit: 10,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Just get documents
|
|
291
|
+
const products = await this.searchDocsProduct("wireless headphones");
|
|
292
|
+
|
|
293
|
+
// Just get count
|
|
294
|
+
const total = await this.searchCountProduct("wireless headphones");
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Customizing Database Operations
|
|
298
|
+
|
|
299
|
+
`DbService` provides hooks for customizing database operations:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Called before document creation
|
|
303
|
+
async _preCreate(data: ProductInput): Promise<ProductInput> {
|
|
304
|
+
// Validate or transform input data
|
|
305
|
+
if (!data.slug && data.name) {
|
|
306
|
+
data.slug = this.generateSlug(data.name);
|
|
307
|
+
}
|
|
308
|
+
return data;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Called after document creation
|
|
312
|
+
async _postCreate(doc: Product): Promise<Product> {
|
|
313
|
+
// Perform additional actions after creation
|
|
314
|
+
await this.notificationService.notifyNewProduct(doc);
|
|
315
|
+
return doc;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Called before document update
|
|
319
|
+
async _preUpdate(id: string, data: Partial<Product>): Promise<Partial<Product>> {
|
|
320
|
+
// Validate update data
|
|
321
|
+
const product = await this.getProduct(id);
|
|
322
|
+
if (product.status === 'published' && data.status === 'draft') {
|
|
323
|
+
throw new Error('Cannot revert published product to draft');
|
|
324
|
+
}
|
|
325
|
+
return data;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Called after document update
|
|
329
|
+
async _postUpdate(doc: Product): Promise<Product> {
|
|
330
|
+
// Perform additional actions after update
|
|
331
|
+
await this.cacheService.invalidateProduct(doc.id);
|
|
332
|
+
return doc;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Called before document removal
|
|
336
|
+
async _preRemove(id: string): Promise<void> {
|
|
337
|
+
// Validate removal
|
|
338
|
+
const product = await this.getProduct(id);
|
|
339
|
+
if (product.hasActiveOrders) {
|
|
340
|
+
throw new Error('Cannot remove product with active orders');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Called after document removal
|
|
345
|
+
async _postRemove(doc: Product): Promise<Product> {
|
|
346
|
+
// Perform additional actions after removal
|
|
347
|
+
await this.searchService.removeProductFromIndex(doc.id);
|
|
348
|
+
return doc;
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Working with Other Services
|
|
353
|
+
|
|
354
|
+
Services often work together to implement complex operations:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
@Service("OrderService")
|
|
358
|
+
export class OrderService extends DbService(db.orderDb) {
|
|
359
|
+
@Srv() productService: ProductService;
|
|
360
|
+
@Srv() userService: UserService;
|
|
361
|
+
@Srv() paymentService: PaymentService;
|
|
362
|
+
@Use() emailApi: EmailApi;
|
|
363
|
+
|
|
364
|
+
async createOrder(userId: string, items: OrderItemInput[]): Promise<Order> {
|
|
365
|
+
// Get user information
|
|
366
|
+
const user = await this.userService.getUser(userId);
|
|
367
|
+
|
|
368
|
+
// Verify product availability and calculate total
|
|
369
|
+
let total = 0;
|
|
370
|
+
const orderItems = [];
|
|
371
|
+
|
|
372
|
+
for (const item of items) {
|
|
373
|
+
const product = await this.productService.getProduct(item.productId);
|
|
374
|
+
|
|
375
|
+
if (product.stock < item.quantity) {
|
|
376
|
+
throw new Error(`Not enough stock for ${product.name}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const itemTotal = product.price * item.quantity;
|
|
380
|
+
total += itemTotal;
|
|
381
|
+
|
|
382
|
+
orderItems.push({
|
|
383
|
+
product: product.id,
|
|
384
|
+
quantity: item.quantity,
|
|
385
|
+
price: product.price,
|
|
386
|
+
total: itemTotal,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Update product stock
|
|
390
|
+
await this.productService.updateProduct(product.id, {
|
|
391
|
+
stock: product.stock - item.quantity,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Create order
|
|
396
|
+
const order = await this.createOrder({
|
|
397
|
+
user: user.id,
|
|
398
|
+
items: orderItems,
|
|
399
|
+
total,
|
|
400
|
+
status: "pending",
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Send confirmation email
|
|
404
|
+
await this.emailApi.sendOrderConfirmation(user.email, order);
|
|
405
|
+
|
|
406
|
+
return order;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Websocket Integration
|
|
412
|
+
|
|
413
|
+
For real-time features:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
@Service("ChatService")
|
|
417
|
+
export class ChatService extends DbService(db.chatDb) {
|
|
418
|
+
@Websocket()
|
|
419
|
+
websocket: Websocket<ChatSignal>;
|
|
420
|
+
|
|
421
|
+
async sendMessage(roomId: string, userId: string, content: string) {
|
|
422
|
+
// Create message in database
|
|
423
|
+
const message = await this.createMessage({
|
|
424
|
+
room: roomId,
|
|
425
|
+
sender: userId,
|
|
426
|
+
content,
|
|
427
|
+
sentAt: new Date(),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Broadcast to room subscribers
|
|
431
|
+
this.websocket.pubsubNewMessage(roomId, {
|
|
432
|
+
id: message.id,
|
|
433
|
+
sender: message.sender,
|
|
434
|
+
content: message.content,
|
|
435
|
+
sentAt: message.sentAt,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
return message;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Queue Integration
|
|
444
|
+
|
|
445
|
+
For background processing:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
@Service("EmailService")
|
|
449
|
+
export class EmailService extends LogService("EmailService") {
|
|
450
|
+
@Queue()
|
|
451
|
+
emailQueue: Queue<EmailSignal>;
|
|
452
|
+
@Use()
|
|
453
|
+
emailApi: EmailApi;
|
|
454
|
+
|
|
455
|
+
async sendWelcomeEmail(userId: string, email: string) {
|
|
456
|
+
// Add to background queue
|
|
457
|
+
await this.emailQueue.add("sendWelcomeEmail", { userId, email });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Process queue job
|
|
461
|
+
async processSendWelcomeEmail(userId: string, email: string) {
|
|
462
|
+
const template = await this.getWelcomeTemplate();
|
|
463
|
+
await this.emailApi.send(email, "Welcome!", template);
|
|
464
|
+
await this.userService.updateUser(userId, { emailSent: true });
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Error Handling
|
|
470
|
+
|
|
471
|
+
Akan.js services should implement proper error handling:
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
async transferFunds(fromAccountId: string, toAccountId: string, amount: number) {
|
|
475
|
+
try {
|
|
476
|
+
// Validate accounts
|
|
477
|
+
const [fromAccount, toAccount] = await Promise.all([
|
|
478
|
+
this.getAccount(fromAccountId),
|
|
479
|
+
this.getAccount(toAccountId)
|
|
480
|
+
]);
|
|
481
|
+
|
|
482
|
+
// Check balance
|
|
483
|
+
if (fromAccount.balance < amount) {
|
|
484
|
+
throw new InsufficientFundsError(
|
|
485
|
+
`Account ${fromAccountId} has insufficient funds: ${fromAccount.balance} < ${amount}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Perform transfer
|
|
490
|
+
await Promise.all([
|
|
491
|
+
this.updateAccount(fromAccountId, { balance: fromAccount.balance - amount }),
|
|
492
|
+
this.updateAccount(toAccountId, { balance: toAccount.balance + amount })
|
|
493
|
+
]);
|
|
494
|
+
|
|
495
|
+
// Log transaction
|
|
496
|
+
await this.createTransaction({
|
|
497
|
+
from: fromAccountId,
|
|
498
|
+
to: toAccountId,
|
|
499
|
+
amount,
|
|
500
|
+
status: 'completed'
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
return true;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
// Log error
|
|
506
|
+
this.logger.error(`Transfer failed: ${error.message}`, error.stack);
|
|
507
|
+
|
|
508
|
+
// Create failed transaction record
|
|
509
|
+
await this.createTransaction({
|
|
510
|
+
from: fromAccountId,
|
|
511
|
+
to: toAccountId,
|
|
512
|
+
amount,
|
|
513
|
+
status: 'failed',
|
|
514
|
+
error: error.message
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Rethrow domain-specific errors, wrap others
|
|
518
|
+
if (error instanceof DomainError) {
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
throw new TransferFailedError(`Transfer failed: ${error.message}`, { cause: error });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
## Best Practices
|
|
528
|
+
|
|
529
|
+
### 1. Stateless Design
|
|
530
|
+
|
|
531
|
+
Services should not maintain internal state between requests:
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// BAD - maintains state in the service
|
|
535
|
+
@Service("CounterService")
|
|
536
|
+
class CounterService {
|
|
537
|
+
private count = 0; // This state is lost on server restart
|
|
538
|
+
|
|
539
|
+
increment() {
|
|
540
|
+
this.count++;
|
|
541
|
+
}
|
|
542
|
+
getCount() {
|
|
543
|
+
return this.count;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// GOOD - uses database for state
|
|
548
|
+
@Service("CounterService")
|
|
549
|
+
class CounterService extends DbService(db.counterDb) {
|
|
550
|
+
async increment(counterId: string) {
|
|
551
|
+
const counter = await this.getCounter(counterId);
|
|
552
|
+
return await counter.set({ count: counter.count + 1 }).save();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async getCount(counterId: string) {
|
|
556
|
+
const counter = await this.getCounter(counterId);
|
|
557
|
+
return counter.count;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### 2. Single Responsibility
|
|
563
|
+
|
|
564
|
+
Each service should focus on a specific domain:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
// BAD - mixing concerns
|
|
568
|
+
@Service("UserService")
|
|
569
|
+
class UserService extends DbService(db.userDb) {
|
|
570
|
+
async createUser(data) {
|
|
571
|
+
// User creation logic
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
async sendEmail(userId, subject, content) {
|
|
575
|
+
// Email sending logic
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async processPayment(userId, amount) {
|
|
579
|
+
// Payment processing logic
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// GOOD - separate services
|
|
584
|
+
@Service("UserService")
|
|
585
|
+
class UserService extends DbService(db.userDb) {
|
|
586
|
+
@Srv() emailService: EmailService;
|
|
587
|
+
@Srv() paymentService: PaymentService;
|
|
588
|
+
|
|
589
|
+
async createUser(data) {
|
|
590
|
+
// User creation logic
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
@Service("EmailService")
|
|
595
|
+
class EmailService extends LogService("EmailService") {
|
|
596
|
+
async sendEmail(userId, subject, content) {
|
|
597
|
+
// Email sending logic
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
@Service("PaymentService")
|
|
602
|
+
class PaymentService extends DbService(db.paymentDb) {
|
|
603
|
+
async processPayment(userId, amount) {
|
|
604
|
+
// Payment processing logic
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### 3. Proper Resource Management
|
|
610
|
+
|
|
611
|
+
Clean up resources in lifecycle hooks:
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
@Service("ExternalApiService")
|
|
615
|
+
class ExternalApiService extends LogService("ExternalApiService") {
|
|
616
|
+
private client: ApiClient;
|
|
617
|
+
private timers: NodeJS.Timeout[] = [];
|
|
618
|
+
|
|
619
|
+
async onModuleInit() {
|
|
620
|
+
this.client = new ApiClient();
|
|
621
|
+
await this.client.connect();
|
|
622
|
+
|
|
623
|
+
this.timers.push(setInterval(() => this.refreshToken(), 3600000));
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
async onModuleDestroy() {
|
|
627
|
+
// Clean up resources
|
|
628
|
+
await this.client.disconnect();
|
|
629
|
+
|
|
630
|
+
for (const timer of this.timers) {
|
|
631
|
+
clearInterval(timer);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 4. Transaction Safety
|
|
638
|
+
|
|
639
|
+
Use hooks for validating operations:
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
async _preUpdate(id: string, data: Partial<Product>) {
|
|
643
|
+
// Validate that product exists
|
|
644
|
+
const product = await this.getProduct(id);
|
|
645
|
+
|
|
646
|
+
// Check authorization
|
|
647
|
+
if (product.owner !== this.currentUser.id) {
|
|
648
|
+
throw new UnauthorizedError('You do not own this product');
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Validate business rules
|
|
652
|
+
if (product.status === 'published' && data.price < product.price) {
|
|
653
|
+
throw new ValidationError('Cannot reduce price of published product');
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return data;
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### 5. Service Dependency Injection
|
|
661
|
+
|
|
662
|
+
Inject other services using decorators, not direct imports:
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
// BAD - direct import
|
|
666
|
+
import { UserService } from "../user/user.service";
|
|
667
|
+
|
|
668
|
+
@Service("OrderService")
|
|
669
|
+
class OrderService extends DbService(db.orderDb) {
|
|
670
|
+
private userService = new UserService(); // Hard-coded dependency
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// GOOD - dependency injection
|
|
674
|
+
@Service("OrderService")
|
|
675
|
+
class OrderService extends DbService(db.orderDb) {
|
|
676
|
+
@Srv() userService: UserService; // Injected dependency
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### 6. Logging
|
|
681
|
+
|
|
682
|
+
Use the built-in logger for consistent logging:
|
|
683
|
+
|
|
684
|
+
```typescript
|
|
685
|
+
@Service("PaymentService")
|
|
686
|
+
class PaymentService extends DbService(db.paymentDb) {
|
|
687
|
+
async processPayment(orderId: string, amount: number) {
|
|
688
|
+
this.logger.log(`Processing payment of ${amount} for order ${orderId}`);
|
|
689
|
+
|
|
690
|
+
try {
|
|
691
|
+
// Payment processing logic
|
|
692
|
+
this.logger.debug("Payment gateway response", response);
|
|
693
|
+
|
|
694
|
+
return result;
|
|
695
|
+
} catch (error) {
|
|
696
|
+
this.logger.error(`Payment failed for order ${orderId}: ${error.message}`, error.stack);
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Integration with NestJS
|
|
704
|
+
|
|
705
|
+
Akan.js services are built on top of NestJS's dependency injection system. The `@Service` decorator builds on top of NestJS's `@Injectable()`:
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
// Behind the scenes, @Service does this:
|
|
709
|
+
@Injectable()
|
|
710
|
+
export class UserService {
|
|
711
|
+
// ...
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
This allows Akan.js services to be used within NestJS modules, controllers, and resolvers.
|
|
716
|
+
|
|
717
|
+
## Complete Example
|
|
718
|
+
|
|
719
|
+
Here's a complete example of a service implementation:
|
|
720
|
+
|
|
721
|
+
```typescript
|
|
722
|
+
import { Dayjs } from "@akanjs/base";
|
|
723
|
+
import { Cron } from "@akanjs/nest";
|
|
724
|
+
import { DbService, Service, Srv, Use } from "@akanjs/service";
|
|
725
|
+
import type { EmailApi } from "@util/nest";
|
|
726
|
+
|
|
727
|
+
import { cnst } from "../cnst";
|
|
728
|
+
import * as db from "../db";
|
|
729
|
+
import { Revert } from "../dict";
|
|
730
|
+
import type * as srv from "../srv";
|
|
731
|
+
|
|
732
|
+
@Service("OrderService")
|
|
733
|
+
export class OrderService extends DbService(db.orderDb) {
|
|
734
|
+
@Srv() userService: srv.UserService;
|
|
735
|
+
@Srv() productService: srv.ProductService;
|
|
736
|
+
@Srv() paymentService: srv.PaymentService;
|
|
737
|
+
@Use() emailApi: EmailApi;
|
|
738
|
+
|
|
739
|
+
async onModuleInit() {
|
|
740
|
+
await this.syncPendingOrders();
|
|
741
|
+
this.logger.log("Order service initialized");
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Custom business logic
|
|
745
|
+
async createOrder(userId: string, items: OrderItemInput[]): Promise<Order> {
|
|
746
|
+
const user = await this.userService.getUser(userId);
|
|
747
|
+
|
|
748
|
+
// Calculate total and check stock
|
|
749
|
+
let total = 0;
|
|
750
|
+
const orderItems = [];
|
|
751
|
+
|
|
752
|
+
for (const item of items) {
|
|
753
|
+
const product = await this.productService.getProduct(item.productId);
|
|
754
|
+
|
|
755
|
+
if (product.stock < item.quantity) {
|
|
756
|
+
throw new Revert("order.insufficientStock", { product: product.name });
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const itemTotal = product.price * item.quantity;
|
|
760
|
+
total += itemTotal;
|
|
761
|
+
|
|
762
|
+
orderItems.push({
|
|
763
|
+
product: product.id,
|
|
764
|
+
name: product.name,
|
|
765
|
+
quantity: item.quantity,
|
|
766
|
+
price: product.price,
|
|
767
|
+
total: itemTotal,
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// Update product stock
|
|
771
|
+
await this.productService.updateProduct(product.id, {
|
|
772
|
+
stock: product.stock - item.quantity,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Create order
|
|
777
|
+
const order = await this.createOrder({
|
|
778
|
+
user: user.id,
|
|
779
|
+
items: orderItems,
|
|
780
|
+
total,
|
|
781
|
+
status: "pending",
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// Send confirmation email
|
|
785
|
+
await this.emailApi.sendOrderConfirmation(user.email, {
|
|
786
|
+
orderId: order.id,
|
|
787
|
+
items: orderItems,
|
|
788
|
+
total,
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
return order;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async cancelOrder(orderId: string) {
|
|
795
|
+
const order = await this.getOrder(orderId);
|
|
796
|
+
|
|
797
|
+
if (order.status !== "pending") {
|
|
798
|
+
throw new Revert("order.cannotCancelProcessedOrder");
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Return stock to inventory
|
|
802
|
+
for (const item of order.items) {
|
|
803
|
+
await this.productService.updateProduct(item.product, {
|
|
804
|
+
$inc: { stock: item.quantity },
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return await order.set({ status: "cancelled" }).save();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Lifecycle hooks
|
|
812
|
+
async _preUpdate(id: string, data: Partial<db.Order>) {
|
|
813
|
+
const order = await this.getOrder(id);
|
|
814
|
+
|
|
815
|
+
if (order.status === "completed" && data.status === "pending") {
|
|
816
|
+
throw new Revert("order.cannotRevertCompletedOrder");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return data;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Scheduled task
|
|
823
|
+
@Cron("0 0 * * *", { serverMode: "batch" })
|
|
824
|
+
async cleanupAbandonedOrders() {
|
|
825
|
+
const cutoffDate = dayjs().subtract(7, "days");
|
|
826
|
+
const orders = await this.listPendingOrders(cutoffDate);
|
|
827
|
+
|
|
828
|
+
for (const order of orders) {
|
|
829
|
+
await this.cancelOrder(order.id);
|
|
830
|
+
this.logger.log(`Cancelled abandoned order: ${order.id}`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Summary method
|
|
835
|
+
async summarize(): Promise<cnst.OrderSummary> {
|
|
836
|
+
return {
|
|
837
|
+
...(await this.orderModel.getSummary()),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
## Troubleshooting
|
|
844
|
+
|
|
845
|
+
### Service Not Being Injected
|
|
846
|
+
|
|
847
|
+
If a service isn't being injected correctly:
|
|
848
|
+
|
|
849
|
+
1. Ensure the service name matches:
|
|
850
|
+
|
|
851
|
+
```typescript
|
|
852
|
+
@Service("UserService") // This name must match
|
|
853
|
+
export class UserService extends DbService(db.userDb) {}
|
|
854
|
+
|
|
855
|
+
@Srv("UserService") // Or this must match if using explicit name
|
|
856
|
+
userService: UserService;
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
2. Check if the service is enabled in the current environment:
|
|
860
|
+
|
|
861
|
+
```typescript
|
|
862
|
+
@Service("UserService", { enabled: process.env.ENABLE_USER_SERVICE === 'true' })
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
3. Verify server mode restrictions:
|
|
866
|
+
```typescript
|
|
867
|
+
@Service("CleanupService", { serverMode: "batch" }) // Only runs in batch mode
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
### Database Operations Failing
|
|
871
|
+
|
|
872
|
+
1. Check model definition and schema:
|
|
873
|
+
|
|
874
|
+
```typescript
|
|
875
|
+
// Is the schema defined correctly?
|
|
876
|
+
console.log(db.userDb);
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
2. Verify input data format:
|
|
880
|
+
|
|
881
|
+
```typescript
|
|
882
|
+
this.logger.debug("Creating user with data:", userData);
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
3. Check for middleware or hook errors:
|
|
886
|
+
```typescript
|
|
887
|
+
async _preCreate(data) {
|
|
888
|
+
try {
|
|
889
|
+
return await validateUser(data); // This might be throwing
|
|
890
|
+
} catch (error) {
|
|
891
|
+
this.logger.error('Validation error:', error);
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Scheduled Tasks Not Running
|
|
898
|
+
|
|
899
|
+
1. Verify cron syntax:
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// Correct syntax
|
|
903
|
+
@Cron("0 * * * *") // Every hour at minute 0
|
|
904
|
+
|
|
905
|
+
// Incorrect syntax
|
|
906
|
+
@Cron("0 * * *") // Missing day-of-week field
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
2. Check server mode restrictions:
|
|
910
|
+
|
|
911
|
+
```typescript
|
|
912
|
+
// Only runs in batch mode
|
|
913
|
+
@Cron("0 * * * *", { serverMode: "batch" })
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
3. Ensure the service is properly initialized:
|
|
917
|
+
```typescript
|
|
918
|
+
onModuleInit() {
|
|
919
|
+
this.logger.log('Service initialized'); // Is this being logged?
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
## Summary
|
|
924
|
+
|
|
925
|
+
Model services in Akan.js are powerful components that encapsulate business logic and data operations. By following the patterns and best practices outlined in this guide, you can create maintainable, scalable services that effectively implement your application's domain logic while maintaining clean separation of concerns.
|
|
926
|
+
|
|
927
|
+
Key takeaways:
|
|
928
|
+
|
|
929
|
+
- Services should be stateless, with persistent state managed in the database
|
|
930
|
+
- Use the appropriate decorator (@Service, @Srv, @Use, etc.) for dependency injection
|
|
931
|
+
- Implement lifecycle hooks for proper resource management
|
|
932
|
+
- Use scheduled tasks for recurring operations
|
|
933
|
+
- Follow single responsibility principle
|
|
934
|
+
- Use proper error handling and logging
|
|
935
|
+
- Leverage the built-in database operations from DbService
|