@cdmbase/wiki-browser 12.0.18-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/lib/components/Logo.d.ts +4 -0
- package/lib/components/Logo.d.ts.map +1 -0
- package/lib/components/Logo.js +16 -0
- package/lib/components/Logo.js.map +1 -0
- package/lib/components/help/SidebarSearch.d.ts +8 -0
- package/lib/components/help/SidebarSearch.d.ts.map +1 -0
- package/lib/components/help/SidebarSearch.js +111 -0
- package/lib/components/help/SidebarSearch.js.map +1 -0
- package/lib/components/help/index.d.ts +2 -0
- package/lib/components/help/index.d.ts.map +1 -0
- package/lib/components/landing/FeatureCard.d.ts +13 -0
- package/lib/components/landing/FeatureCard.d.ts.map +1 -0
- package/lib/components/landing/FeatureCard.js +85 -0
- package/lib/components/landing/FeatureCard.js.map +1 -0
- package/lib/components/landing/QuickLinkCard.d.ts +8 -0
- package/lib/components/landing/QuickLinkCard.d.ts.map +1 -0
- package/lib/components/landing/QuickLinkCard.js +26 -0
- package/lib/components/landing/QuickLinkCard.js.map +1 -0
- package/lib/components/landing/SearchInput.d.ts +10 -0
- package/lib/components/landing/SearchInput.d.ts.map +1 -0
- package/lib/components/landing/SearchInput.js +223 -0
- package/lib/components/landing/SearchInput.js.map +1 -0
- package/lib/components/landing/index.d.ts +4 -0
- package/lib/components/landing/index.d.ts.map +1 -0
- package/lib/components/welcome.d.ts +3 -0
- package/lib/components/welcome.d.ts.map +1 -0
- package/lib/compute.d.ts +4 -0
- package/lib/compute.d.ts.map +1 -0
- package/lib/compute.js +96 -0
- package/lib/compute.js.map +1 -0
- package/lib/config/env-config.d.ts +4 -0
- package/lib/config/env-config.d.ts.map +1 -0
- package/lib/config/env-config.js +7 -0
- package/lib/config/env-config.js.map +1 -0
- package/lib/docs.config.d.ts +48 -0
- package/lib/docs.config.d.ts.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/loaders/search.d.ts +1 -0
- package/lib/loaders/search.d.ts.map +1 -0
- package/lib/module.d.ts +4 -0
- package/lib/module.d.ts.map +1 -0
- package/lib/module.js +11 -0
- package/lib/module.js.map +1 -0
- package/lib/pages/ArticlePage/ArticlePage.d.ts +4 -0
- package/lib/pages/ArticlePage/ArticlePage.d.ts.map +1 -0
- package/lib/pages/ArticlePage/ArticlePage.js +222 -0
- package/lib/pages/ArticlePage/ArticlePage.js.map +1 -0
- package/lib/pages/ArticlePage/index.d.ts +3 -0
- package/lib/pages/ArticlePage/index.d.ts.map +1 -0
- package/lib/pages/ArticlePage/index.js +3 -0
- package/lib/pages/ArticlePage/index.js.map +1 -0
- package/lib/pages/CategoryCollection/CategoryCollection.d.ts +4 -0
- package/lib/pages/CategoryCollection/CategoryCollection.d.ts.map +1 -0
- package/lib/pages/CategoryCollection/CategoryCollection.js +103 -0
- package/lib/pages/CategoryCollection/CategoryCollection.js.map +1 -0
- package/lib/pages/CategoryCollection/index.d.ts +3 -0
- package/lib/pages/CategoryCollection/index.d.ts.map +1 -0
- package/lib/pages/CategoryCollection/index.js +3 -0
- package/lib/pages/CategoryCollection/index.js.map +1 -0
- package/lib/pages/Help/HelpIndex.d.ts +4 -0
- package/lib/pages/Help/HelpIndex.d.ts.map +1 -0
- package/lib/pages/Help/HelpIndex.js +44 -0
- package/lib/pages/Help/HelpIndex.js.map +1 -0
- package/lib/pages/Help/index.d.ts +4 -0
- package/lib/pages/Help/index.d.ts.map +1 -0
- package/lib/pages/Help/index.js +226 -0
- package/lib/pages/Help/index.js.map +1 -0
- package/lib/pages/Landing/index.d.ts +3 -0
- package/lib/pages/Landing/index.d.ts.map +1 -0
- package/lib/pages/Landing/index.js +281 -0
- package/lib/pages/Landing/index.js.map +1 -0
- package/lib/routes.json +2533 -0
- package/lib/seo.d.ts +22 -0
- package/lib/seo.d.ts.map +1 -0
- package/lib/slot-fill/FooterFill.d.ts +3 -0
- package/lib/slot-fill/FooterFill.d.ts.map +1 -0
- package/lib/slot-fill/FooterFill.js +18 -0
- package/lib/slot-fill/FooterFill.js.map +1 -0
- package/lib/slot-fill/LogoFill.d.ts +5 -0
- package/lib/slot-fill/LogoFill.d.ts.map +1 -0
- package/lib/slot-fill/LogoFill.js +74 -0
- package/lib/slot-fill/LogoFill.js.map +1 -0
- package/lib/slot-fill/consts.d.ts +5 -0
- package/lib/slot-fill/consts.d.ts.map +1 -0
- package/lib/slot-fill/consts.js +1 -0
- package/lib/slot-fill/consts.js.map +1 -0
- package/lib/slot-fill/index.d.ts +4 -0
- package/lib/slot-fill/index.d.ts.map +1 -0
- package/lib/templates/assets/images/add-link-frontend.png +0 -0
- package/lib/templates/assets/images/add-package-backend.png +0 -0
- package/lib/templates/assets/images/add-to-backend-module.png +0 -0
- package/lib/templates/assets/images/add-upload-client-frontend.png +0 -0
- package/lib/templates/assets/images/additional-parameters.png +0 -0
- package/lib/templates/assets/images/aeh-implementation.png +0 -0
- package/lib/templates/assets/images/aeh-usage.png +0 -0
- package/lib/templates/assets/images/apollo-client/recommendation_cache_mgmt.png +0 -0
- package/lib/templates/assets/images/app-deploy-new-version/jenkins1.PNG +0 -0
- package/lib/templates/assets/images/app-deploy-new-version/jenkins2.PNG +0 -0
- package/lib/templates/assets/images/auth-wrapper-code.png +0 -0
- package/lib/templates/assets/images/cdebase.png +0 -0
- package/lib/templates/assets/images/cdm-locales-directory.png +0 -0
- package/lib/templates/assets/images/client-settings.png +0 -0
- package/lib/templates/assets/images/codegen_file_update.png +0 -0
- package/lib/templates/assets/images/configuration.png +0 -0
- package/lib/templates/assets/images/copy-plugin.png +0 -0
- package/lib/templates/assets/images/docusaurus.png +0 -0
- package/lib/templates/assets/images/error-link.png +0 -0
- package/lib/templates/assets/images/error-sample.png +0 -0
- package/lib/templates/assets/images/extension copy.png +0 -0
- package/lib/templates/assets/images/extension.png +0 -0
- package/lib/templates/assets/images/graphql/graphql-folder-backend.png +0 -0
- package/lib/templates/assets/images/graphql/graphql-folder-with-gql.png +0 -0
- package/lib/templates/assets/images/i18n-config.png +0 -0
- package/lib/templates/assets/images/image.png +0 -0
- package/lib/templates/assets/images/logo.svg +10 -0
- package/lib/templates/assets/images/logo1.svg +1 -0
- package/lib/templates/assets/images/modify-upload-false-server.png +0 -0
- package/lib/templates/assets/images/navigation-auth-enabled.png +0 -0
- package/lib/templates/assets/images/org-dashboard-navigation.png +0 -0
- package/lib/templates/assets/images/org-navigation.png +0 -0
- package/lib/templates/assets/images/preferences_graphql_type.png +0 -0
- package/lib/templates/assets/images/provider.png +0 -0
- package/lib/templates/assets/images/route-config.png +0 -0
- package/lib/templates/assets/images/service-accounts.png +0 -0
- package/lib/templates/assets/images/source-code/source-code-environments.png +0 -0
- package/lib/templates/assets/images/source-code/source-code-organization.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/jenkins-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/lerna-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/root-package-json-changes.png +0 -0
- package/lib/templates/assets/images/spin-clone-develop-deployment/values-dev-changes.png +0 -0
- package/lib/templates/assets/images/sso-mappers.png +0 -0
- package/lib/templates/assets/images/sso-picture-mapper.png +0 -0
- package/lib/templates/assets/images/sso-settings.png +0 -0
- package/lib/templates/assets/images/timesheet_apollo_cache.png +0 -0
- package/lib/templates/assets/images/timesheet_query.png +0 -0
- package/lib/templates/assets/images/tutorial/docsVersionDropdown.png +0 -0
- package/lib/templates/assets/images/tutorial/localeDropdown.png +0 -0
- package/lib/templates/assets/images/unauthenticated.png +0 -0
- package/lib/templates/assets/images/undraw_docusaurus_mountain.svg +170 -0
- package/lib/templates/assets/images/undraw_docusaurus_react.svg +169 -0
- package/lib/templates/assets/images/undraw_docusaurus_tree.svg +1 -0
- package/lib/templates/assets/images/vite-plugin-config.png +0 -0
- package/lib/templates/content/docs/Generators/Project/generate-fullproject.md +12 -0
- package/lib/templates/content/docs/LLM/Logger.llm.md +194 -0
- package/lib/templates/content/docs/LLM/backend-proxies-services-llm.md +2687 -0
- package/lib/templates/content/docs/LLM/backend-service-llm.md +3384 -0
- package/lib/templates/content/docs/LLM/db_migration_llm.md +954 -0
- package/lib/templates/content/docs/LLM/frontend/REMIX-15.3-upgrade-llm.md +1245 -0
- package/lib/templates/content/docs/LLM/inngest/INNGEST_FUNCTION_DEVELOPMENT_GUIDE_LLM.md +1241 -0
- package/lib/templates/content/docs/LLM/inngest/INNGEST_NAMESPACE_LLM.md +384 -0
- package/lib/templates/content/docs/LLM/llm_workflow_namespace.md +384 -0
- package/lib/templates/content/docs/LLM/organization-components-form-llm.md +1395 -0
- package/lib/templates/content/docs/LLM/page-component-llm.md +173 -0
- package/lib/templates/content/docs/LLM/preferences-settings-llm.md +2781 -0
- package/lib/templates/content/docs/LLM/tailwind-css-llm.md +502 -0
- package/lib/templates/content/docs/UI/SchemaBasedUI.md +334 -0
- package/lib/templates/content/docs/UI/SlotFillComponent.md +334 -0
- package/lib/templates/content/docs/adminide-modules/account/auth0-login.md +31 -0
- package/lib/templates/content/docs/adminide-modules/account/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/account/keycloak-remix-setup.md +86 -0
- package/lib/templates/content/docs/adminide-modules/account/remix-auth-setup.md +79 -0
- package/lib/templates/content/docs/adminide-modules/account/various-auth-qatest.md +157 -0
- package/lib/templates/content/docs/adminide-modules/api-builders/graphql.md +906 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/index.md +14 -0
- package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/settingup-stripe-locally.md +25 -0
- package/lib/templates/content/docs/adminide-modules/billing/tier-config.md +293 -0
- package/lib/templates/content/docs/adminide-modules/connectors/Connector.md +207 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/index.md +16 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/setup.md +435 -0
- package/lib/templates/content/docs/adminide-modules/file-upload/upload-file-using-signed-url.md +161 -0
- package/lib/templates/content/docs/adminide-modules/preferences/AddAdditionalPermissions.md +151 -0
- package/lib/templates/content/docs/adminide-modules/preferences/Configuration.md +241 -0
- package/lib/templates/content/docs/adminide-modules/preferences/Policy-Configuration.md +61 -0
- package/lib/templates/content/docs/adminide-modules/preferences/UI-components/ResourceSettingsLoader.md +319 -0
- package/lib/templates/content/docs/adminide-modules/preferences/contribute_scope_target.md +280 -0
- package/lib/templates/content/docs/adminide-modules/preferences/generate-urii.md +94 -0
- package/lib/templates/content/docs/adminide-modules/preferences/index.md +28 -0
- package/lib/templates/content/docs/adminide-modules/preferences/machine-configuration.md +157 -0
- package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/generateCdecodeUri.md +1289 -0
- package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/migratingFromUseSettings.md +215 -0
- package/lib/templates/content/docs/adminide-modules/preferences/permissions/Roles-Permissions.md +72 -0
- package/lib/templates/content/docs/adminide-modules/preferences/permissions/settingUserPermissions.md +139 -0
- package/lib/templates/content/docs/adminide-modules/preferences/preference-dependency.md +138 -0
- package/lib/templates/content/docs/adminide-modules/preferences/route-based-configuration.md +41 -0
- package/lib/templates/content/docs/adminide-modules/preferences/schema-configuration.md +71 -0
- package/lib/templates/content/docs/adminide-modules/preferences/supported.md +24 -0
- package/lib/templates/content/docs/adminide-modules/preferences/useSettingsLoader.md +248 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/auth-providers.md +1317 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/keycloak-guide.md +543 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-based-authentication.md +846 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-management.md +708 -0
- package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenants.md +1117 -0
- package/lib/templates/content/docs/chrome-extension/index.md +14 -0
- package/lib/templates/content/docs/chrome-extension/setup.md +30 -0
- package/lib/templates/content/docs/contributing/adding-package.md +23 -0
- package/lib/templates/content/docs/contributing/adding_new_modules.md +99 -0
- package/lib/templates/content/docs/contributing/architecture-updates.md +19 -0
- package/lib/templates/content/docs/contributing/avoid-using-promises-ui.md +116 -0
- package/lib/templates/content/docs/contributing/coding-guidelines.md +111 -0
- package/lib/templates/content/docs/contributing/do-and-dont.md +42 -0
- package/lib/templates/content/docs/contributing/faq.md +22 -0
- package/lib/templates/content/docs/contributing/folder-setup/browser.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/config.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/containers-server.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/core.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/graphql.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/index.md +30 -0
- package/lib/templates/content/docs/contributing/folder-setup/module.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/server.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/services.md +12 -0
- package/lib/templates/content/docs/contributing/folder-setup/store.md +12 -0
- package/lib/templates/content/docs/contributing/frontend-coding.md +30 -0
- package/lib/templates/content/docs/contributing/git-subtree-sharing.md +73 -0
- package/lib/templates/content/docs/contributing/graphql-subscriptions.md +69 -0
- package/lib/templates/content/docs/contributing/how-to-contribute.md +30 -0
- package/lib/templates/content/docs/contributing/how_to_check_pure_esm.md +29 -0
- package/lib/templates/content/docs/contributing/index.md +60 -0
- package/lib/templates/content/docs/contributing/installation-issues.md +23 -0
- package/lib/templates/content/docs/contributing/keyboard-shortcut.md +131 -0
- package/lib/templates/content/docs/contributing/language/locale-support.md +12 -0
- package/lib/templates/content/docs/contributing/lerna-build-tools.md +516 -0
- package/lib/templates/content/docs/contributing/lerna-yarn-workspaces.md +95 -0
- package/lib/templates/content/docs/contributing/lint-and-formatter.md +20 -0
- package/lib/templates/content/docs/contributing/mobile-setup.md +16 -0
- package/lib/templates/content/docs/contributing/project-setup.md +233 -0
- package/lib/templates/content/docs/contributing/react/index.md +14 -0
- package/lib/templates/content/docs/contributing/react/lazy-component.md +70 -0
- package/lib/templates/content/docs/contributing/run-various-options.md +124 -0
- package/lib/templates/content/docs/contributing/schema-first-graphql-types.md +37 -0
- package/lib/templates/content/docs/contributing/source-code-organization.md +57 -0
- package/lib/templates/content/docs/contributing/staging-docker.md +88 -0
- package/lib/templates/content/docs/contributing/third-party/apollo-client-v3-tutorials.md +28 -0
- package/lib/templates/content/docs/contributing/third-party/index.md +18 -0
- package/lib/templates/content/docs/contributing/typescript-contribution.md +16 -0
- package/lib/templates/content/docs/devops/app-deploy-new-version.md +30 -0
- package/lib/templates/content/docs/devops/index.md +14 -0
- package/lib/templates/content/docs/devops/mobile-jenkins-build.md +40 -0
- package/lib/templates/content/docs/devops/versioning-the-project.md +128 -0
- package/lib/templates/content/docs/error-handler/application-error-handler.md +40 -0
- package/lib/templates/content/docs/error-handler/error-handling.md +26 -0
- package/lib/templates/content/docs/error-handler/index.md +16 -0
- package/lib/templates/content/docs/error-handler/logging-errors.md +14 -0
- package/lib/templates/content/docs/feature-api/copy-operation.md +427 -0
- package/lib/templates/content/docs/feature-api/feature-browser/assets.md +46 -0
- package/lib/templates/content/docs/feature-api/feature-browser/auth-permissions.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-browser/feature.md +131 -0
- package/lib/templates/content/docs/feature-api/feature-browser/index.md +22 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routes-menu.md +110 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routing-convention.md +124 -0
- package/lib/templates/content/docs/feature-api/feature-browser/routing.md +338 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/auth-permissions.md +20 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/feature.md +130 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/index.md +18 -0
- package/lib/templates/content/docs/feature-api/feature-mobile/navigation.md +187 -0
- package/lib/templates/content/docs/feature-api/feature-server/Scheduling.md +44 -0
- package/lib/templates/content/docs/feature-api/feature-server/dataloader.md +320 -0
- package/lib/templates/content/docs/feature-api/feature-server/dependency-injection.md +81 -0
- package/lib/templates/content/docs/feature-api/feature-server/feature.md +65 -0
- package/lib/templates/content/docs/feature-api/feature-server/generic-dataloader.md +135 -0
- package/lib/templates/content/docs/feature-api/feature-server/index.md +40 -0
- package/lib/templates/content/docs/feature-api/feature-server/migration.md +127 -0
- package/lib/templates/content/docs/feature-api/feature-server/mongo-model.md +72 -0
- package/lib/templates/content/docs/feature-api/feature-server/permissions.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-server/policies.md +57 -0
- package/lib/templates/content/docs/feature-api/feature-server/preferences.md +57 -0
- package/lib/templates/content/docs/feature-api/feature-server/repositories.md +114 -0
- package/lib/templates/content/docs/feature-api/feature-server/resolvers.md +126 -0
- package/lib/templates/content/docs/feature-api/feature-server/rules.md +132 -0
- package/lib/templates/content/docs/feature-api/feature-server/schema.md +12 -0
- package/lib/templates/content/docs/feature-api/feature-server/services.md +102 -0
- package/lib/templates/content/docs/feature-api/feature-server/setup-resource-crud.md +359 -0
- package/lib/templates/content/docs/feature-api/index.md +18 -0
- package/lib/templates/content/docs/graphql/apolloClient-mutation.md +94 -0
- package/lib/templates/content/docs/graphql/index.md +14 -0
- package/lib/templates/content/docs/graphql/scalars.md +15 -0
- package/lib/templates/content/docs/help/index.md +14 -0
- package/lib/templates/content/docs/help/intro.md +16 -0
- package/lib/templates/content/docs/intl/ant-design-menu-translation.md +74 -0
- package/lib/templates/content/docs/intl/intl-namespace.md +129 -0
- package/lib/templates/content/docs/intl/vite-plugin-intl.md +87 -0
- package/lib/templates/content/docs/intl/webpack-plugin-intl.md +12 -0
- package/lib/templates/content/docs/intro.md +18 -0
- package/lib/templates/content/docs/knowledge/basic-fullstack.md +238 -0
- package/lib/templates/content/docs/mailing/index.md +14 -0
- package/lib/templates/content/docs/mailing/mailing-template.md +148 -0
- package/lib/templates/content/docs/mobile/App-navigation-generator.md +410 -0
- package/lib/templates/content/docs/mobile/MobileTestCases.md +264 -0
- package/lib/templates/content/docs/mobile/eas-profile-build.md +107 -0
- package/lib/templates/content/docs/mobile/expo-push-notification-setup.md +216 -0
- package/lib/templates/content/docs/mobile/index.md +14 -0
- package/lib/templates/content/docs/mobile/routes.md +83 -0
- package/lib/templates/content/docs/organization/adding-account-context.md +116 -0
- package/lib/templates/content/docs/organization/adding-org-mobile-navigation.md +22 -0
- package/lib/templates/content/docs/organization/adding-org-web-navigation.md +12 -0
- package/lib/templates/content/docs/organization/index.md +20 -0
- package/lib/templates/content/docs/organization/initialization.md +20 -0
- package/lib/templates/content/docs/organization/organization-resource-vs-resource.md +112 -0
- package/lib/templates/content/docs/remix/configuration/component-structure-best-practices.md +152 -0
- package/lib/templates/content/docs/remix/configuration/configurations.md +218 -0
- package/lib/templates/content/docs/remix/configuration/css-import-and-stylesheets.md +142 -0
- package/lib/templates/content/docs/remix/configuration/dont-subcomponent-network.md +166 -0
- package/lib/templates/content/docs/remix/configuration/generated-data-loaders.md +122 -0
- package/lib/templates/content/docs/remix/configuration/generated-resource-loaders.md +257 -0
- package/lib/templates/content/docs/remix/configuration/query-params-generator.md +216 -0
- package/lib/templates/content/docs/remix/configuration/routes-extra-icons.md +103 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-advanced.md +86 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-auth.md +113 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-best-practices.md +55 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-fields.md +79 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-graphql.md +79 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-index.md +112 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-loaders.md +165 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-middleware.md +196 -0
- package/lib/templates/content/docs/remix/configuration/routes-json-overview.md +53 -0
- package/lib/templates/content/docs/remix/data-loaders.md +43 -0
- package/lib/templates/content/docs/remix/devtools/remix-devtools.md +58 -0
- package/lib/templates/content/docs/remix/examples/changes-using-servercode.md +79 -0
- package/lib/templates/content/docs/remix/extra-icons.md +62 -0
- package/lib/templates/content/docs/remix/extra-links.md +65 -0
- package/lib/templates/content/docs/remix/generated-data-loaders.md +114 -0
- package/lib/templates/content/docs/remix/queryParamsGenerator.md +89 -0
- package/lib/templates/content/docs/remix/resources.md +16 -0
- package/lib/templates/content/docs/remix/styles.md +132 -0
- package/lib/templates/content/docs/remix/wiki.md +12 -0
- package/lib/templates/content/docs/security/auth-wrapper/auth-wrapper.md +24 -0
- package/lib/templates/content/docs/security/index.md +18 -0
- package/lib/templates/content/docs/security/secure-button-mobilenative.md +88 -0
- package/lib/templates/content/docs/security/secure-button-web.md +89 -0
- package/lib/templates/content/docs/server-side/account-customization.md +82 -0
- package/lib/templates/content/docs/server-side/apollo/caching.md +164 -0
- package/lib/templates/content/docs/server-side/backend-architecture/FINAL-DECISION.md +209 -0
- package/lib/templates/content/docs/server-side/backend-architecture/TRUE-FINAL-ARCHITECTURE.md +603 -0
- package/lib/templates/content/docs/server-side/backend-architecture/index1.md +0 -0
- package/lib/templates/content/docs/server-side/backend-coding.md +839 -0
- package/lib/templates/content/docs/server-side/e2b/manageing-template.md +197 -0
- package/lib/templates/content/docs/server-side/index.md +14 -0
- package/lib/templates/content/docs/server-side/inngest-functions-module.md +309 -0
- package/lib/templates/content/docs/server-side/listen-stripe-events.md +43 -0
- package/lib/templates/content/docs/server-side/slug-service.md +323 -0
- package/lib/templates/content/docs/tests/index.md +18 -0
- package/lib/templates/content/docs/tests/jest-test-debug-vscode.md +40 -0
- package/lib/templates/content/docs/tests/known-errors.md +116 -0
- package/lib/templates/content/docs/tests/service-test-template.md +118 -0
- package/lib/templates/content/docs/tests/test-setup.md +93 -0
- package/lib/templates/content/docs/xstate.md +23 -0
- package/lib/types.d.ts +37 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils/docsNavigation.d.ts +9 -0
- package/lib/utils/docsNavigation.d.ts.map +1 -0
- package/lib/utils/docsNavigation.js +37 -0
- package/lib/utils/docsNavigation.js.map +1 -0
- package/lib/utils/helpCenterUtils.d.ts +26 -0
- package/lib/utils/helpCenterUtils.d.ts.map +1 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/markdownLoader.d.ts +36 -0
- package/lib/utils/markdownLoader.d.ts.map +1 -0
- package/lib/utils/markdownLoader.js +2242 -0
- package/lib/utils/markdownLoader.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,2687 @@
|
|
|
1
|
+
# LLM Proxy Service Generator Prompt
|
|
2
|
+
|
|
3
|
+
## Quick Start Prompt
|
|
4
|
+
|
|
5
|
+
Copy and paste this prompt to an LLM to generate a new proxy service:
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🤖 PROMPT START
|
|
10
|
+
|
|
11
|
+
I need you to create a Proxy Service AND Moleculer Service for the AdminIDE Stack following the **modern auto-generation pattern**.
|
|
12
|
+
|
|
13
|
+
### 🎯 Recommended Approach Summary
|
|
14
|
+
|
|
15
|
+
**ALWAYS use auto-generation for both Moleculer services and Proxy services (Pattern 1):**
|
|
16
|
+
|
|
17
|
+
1. **Moleculer Service**: Use `Moleculer.generateActionsAndEvents<IService>(service)`
|
|
18
|
+
- 30 lines instead of 200+
|
|
19
|
+
- Auto-detects all methods including parent class methods
|
|
20
|
+
- Auto-registers event handlers from decorators
|
|
21
|
+
- **Auto-derive topic** from `ServiceSchemas.topic` - no enum imports needed!
|
|
22
|
+
- **No configuration needed** - just one line to generate everything
|
|
23
|
+
|
|
24
|
+
2. **Proxy Service**: Use `generateProxyMethods<IService>(this, ServiceSchemas, broker, logger)`
|
|
25
|
+
- 40 lines instead of 120+
|
|
26
|
+
- Auto-generates all proxy methods
|
|
27
|
+
- Correct parameter ordering guaranteed
|
|
28
|
+
- **No manual `topic` property needed** - extracted automatically from `ServiceSchemas.topic`
|
|
29
|
+
|
|
30
|
+
**Benefits**:
|
|
31
|
+
|
|
32
|
+
- ✅ 90% less code
|
|
33
|
+
- ✅ Zero boilerplate - no configuration needed
|
|
34
|
+
- ✅ Type-safe with full TypeScript inference
|
|
35
|
+
- ✅ Maintainable - add method once, available everywhere
|
|
36
|
+
- ✅ No manual synchronization needed
|
|
37
|
+
- ✅ **Zero dependencies** - topic auto-derived from class name, no enums needed
|
|
38
|
+
|
|
39
|
+
**When to use optional configurations (Patterns 2-4):**
|
|
40
|
+
|
|
41
|
+
- Pattern 2: Custom validation - RARE (~5% of cases)
|
|
42
|
+
- Pattern 3: Manual overrides - VERY RARE (~1% of cases)
|
|
43
|
+
- Pattern 4: Whitelist methods - RARE (~2% of cases)
|
|
44
|
+
|
|
45
|
+
**For 99% of services, use Pattern 1 with zero configuration!**
|
|
46
|
+
|
|
47
|
+
**Topic Convention (Auto-Generated & Minification-Safe)**:
|
|
48
|
+
The topic is **automatically generated** in the service schemas and derived from the service interface name:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Auto-generated in service-schemas.ts (from IProjectService interface)
|
|
52
|
+
export const ProjectServiceSchemas = {
|
|
53
|
+
/** Moleculer service topic/name - safe for minification */
|
|
54
|
+
topic: 'ProjectService' as const,
|
|
55
|
+
// ... method schemas
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// In moleculer service - extract topic from schemas
|
|
59
|
+
import { ProjectServiceSchemas } from 'common/server';
|
|
60
|
+
const { topic } = ProjectServiceSchemas; // "ProjectService"
|
|
61
|
+
|
|
62
|
+
// In proxy service - topic is automatically extracted by generateProxyMethods()
|
|
63
|
+
// No manual topic property needed!
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Benefits**:
|
|
67
|
+
|
|
68
|
+
- ✅ **Zero manual configuration** - topic auto-derived from interface name (e.g., `IProjectService` → `ProjectService`)
|
|
69
|
+
- ✅ **Minification-safe** - stored as string constant in generated schemas
|
|
70
|
+
- ✅ **Single source of truth** - schemas object contains both topic and validation
|
|
71
|
+
- ✅ **No enum imports needed** - eliminates `MoleculerTopics` or `MoleculerServiceName` dependencies
|
|
72
|
+
- ✅ **Type-safe** - `as const` provides literal type inference
|
|
73
|
+
|
|
74
|
+
### Context
|
|
75
|
+
|
|
76
|
+
This codebase uses a microservice architecture with Moleculer message broker. The architecture consists of THREE key components:
|
|
77
|
+
|
|
78
|
+
1. **Service Interface**: Defines the contract (methods and types)
|
|
79
|
+
2. **Actual Service Implementation**: Contains business logic
|
|
80
|
+
3. **Moleculer Service**: Registers actions that delegate to the actual service
|
|
81
|
+
4. **Proxy Service**: Used by clients to call methods via Moleculer broker
|
|
82
|
+
|
|
83
|
+
**Important**: When creating services, you must create BOTH the Moleculer service AND the proxy service. They work together:
|
|
84
|
+
|
|
85
|
+
**Important**: When creating services, you must create BOTH the Moleculer service AND the proxy service. They work together:
|
|
86
|
+
|
|
87
|
+
- **Moleculer Service**: Lives on the server with business logic, exposes actions
|
|
88
|
+
- **Proxy Service**: Lives on clients, calls Moleculer actions via broker
|
|
89
|
+
|
|
90
|
+
### Files to Reference
|
|
91
|
+
|
|
92
|
+
1. **Service Interface**: `packages/common/src/services/[ServiceName].ts`
|
|
93
|
+
- Contains the interface definition with all methods
|
|
94
|
+
|
|
95
|
+
2. **Actual Service**: `packages-modules/[module-name]/server/src/services/[ServiceName].ts`
|
|
96
|
+
- Contains the actual implementation (for understanding method signatures)
|
|
97
|
+
|
|
98
|
+
3. **Example Moleculer Services**:
|
|
99
|
+
- **Account Service**: `packages-modules/account-api/server/src/plugins/AccountMoleculerService.ts` (40+ actions)
|
|
100
|
+
- **Project Service**: `packages-modules/account-api/server/src/modules/project/plugins/ProjectMoleculerService.ts` (10+ actions)
|
|
101
|
+
|
|
102
|
+
4. **Example Proxy Services** (choose based on complexity):
|
|
103
|
+
- Simple: `packages-modules/account-api/core/src/servers/services/ProjectProxyService.ts`
|
|
104
|
+
- Medium: `packages-modules/account-api/core/src/servers/services/TeamMicroservice.ts`
|
|
105
|
+
- Complex: `packages-modules/account-api/core/src/servers/services/AccountProxyService.ts` (40+ methods)
|
|
106
|
+
|
|
107
|
+
5. **GraphQL Schema**: `packages-modules/[module-name]/server/src/graphql/schema/service.graphql`
|
|
108
|
+
- Contains service action enums
|
|
109
|
+
|
|
110
|
+
### Task 0: Service Schema Generation (Automatic)
|
|
111
|
+
|
|
112
|
+
**AUTO-GENERATED**: Service schemas with topics are automatically generated by running `yarn generateGraphql`.
|
|
113
|
+
|
|
114
|
+
The schema generator (`@common-stack/server-core/lib/moleculer-generation/generateAllServiceSchemas.cjs`) automatically:
|
|
115
|
+
|
|
116
|
+
- Scans all service interfaces (e.g., `IProjectService`)
|
|
117
|
+
- Generates Zod validation schemas for each method
|
|
118
|
+
- Auto-derives topic from interface name: `IProjectService` → `ProjectService`
|
|
119
|
+
- Outputs to `packages/common/src/generated/service-schemas.ts`
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Auto-generated in service-schemas.ts
|
|
123
|
+
export const ProjectServiceSchemas = {
|
|
124
|
+
/** Moleculer service topic/name - safe for minification */
|
|
125
|
+
topic: 'ProjectService' as const,
|
|
126
|
+
createProject: z.object({
|
|
127
|
+
/* ... */
|
|
128
|
+
}),
|
|
129
|
+
getProject: z.object({
|
|
130
|
+
/* ... */
|
|
131
|
+
}),
|
|
132
|
+
// ... all other methods
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Benefits of auto-generation**:
|
|
137
|
+
|
|
138
|
+
- ✅ **Zero manual work**: Topic and schemas generated from TypeScript interfaces
|
|
139
|
+
- ✅ **Minification-safe**: Topic stored as string constant
|
|
140
|
+
- ✅ **Type-safe**: Full TypeScript inference with `as const`
|
|
141
|
+
- ✅ **Always in sync**: Regenerate after interface changes with `yarn generateGraphql`
|
|
142
|
+
- ✅ **Single source of truth**: Interface defines everything (methods, params, topic)
|
|
143
|
+
|
|
144
|
+
### Task 1: Update GraphQL Schema
|
|
145
|
+
|
|
146
|
+
Add all missing actions to the `[ServiceName]ServiceAction` enum in `service.graphql`:
|
|
147
|
+
|
|
148
|
+
```graphql
|
|
149
|
+
enum [ServiceName]ServiceAction {
|
|
150
|
+
# Read operations (get*, find*, search*, etc.)
|
|
151
|
+
getItem
|
|
152
|
+
findItems
|
|
153
|
+
|
|
154
|
+
# Create operations (create*, add*, generate*, etc.)
|
|
155
|
+
createItem
|
|
156
|
+
addItem
|
|
157
|
+
|
|
158
|
+
# Update operations (update*, modify*, change*, set*, etc.)
|
|
159
|
+
updateItem
|
|
160
|
+
changeStatus
|
|
161
|
+
|
|
162
|
+
# Delete operations (delete*, remove*, archive*, etc.)
|
|
163
|
+
deleteItem
|
|
164
|
+
removeItem
|
|
165
|
+
|
|
166
|
+
# Event handlers (on* methods) - MUST start with 'on' prefix
|
|
167
|
+
onItemCreated
|
|
168
|
+
onItemDeleted
|
|
169
|
+
onItemUpdated
|
|
170
|
+
onUserRemoved
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Rules for enum values**:
|
|
175
|
+
|
|
176
|
+
- Use camelCase (first letter lowercase)
|
|
177
|
+
- Match method names from the interface
|
|
178
|
+
- Group by operation type
|
|
179
|
+
- Include all interface methods except lifecycle methods like `dispose()`
|
|
180
|
+
- **Event handlers MUST use `on` prefix** (e.g., `onAccountDeleted`, `onPropertyCreated`)
|
|
181
|
+
- Event names should match the pattern: `OnEventName` → `onEventName`
|
|
182
|
+
|
|
183
|
+
### Task 2: Create Moleculer Service
|
|
184
|
+
|
|
185
|
+
**CRITICAL**: The Moleculer service must be created FIRST. This is where the actual work happens.
|
|
186
|
+
|
|
187
|
+
**STRICT FILE NAMING CONVENTION:**
|
|
188
|
+
|
|
189
|
+
✅ **CORRECT**: Use PascalCase matching the class name
|
|
190
|
+
|
|
191
|
+
- File: `AccountMoleculerService.ts` → Class: `AccountMoleculerService`
|
|
192
|
+
- File: `ProjectMoleculerService.ts` → Class: `ProjectMoleculerService`
|
|
193
|
+
- File: `TeamMoleculerService.ts` → Class: `TeamMoleculerService`
|
|
194
|
+
|
|
195
|
+
❌ **WRONG**: Do NOT use kebab-case or snake-case
|
|
196
|
+
|
|
197
|
+
- ❌ `accounts-moleculer-service.ts` (kebab-case)
|
|
198
|
+
- ❌ `account-moleculer-service.ts` (kebab-case)
|
|
199
|
+
- ❌ `accounts_moleculer_service.ts` (snake-case)
|
|
200
|
+
|
|
201
|
+
**File Location Patterns:**
|
|
202
|
+
|
|
203
|
+
Pattern 1 (Main module):
|
|
204
|
+
`packages-modules/[module-name]/server/src/plugins/[ServiceName]MoleculerService.ts`
|
|
205
|
+
|
|
206
|
+
Examples:
|
|
207
|
+
|
|
208
|
+
- `packages-modules/account-api/server/src/plugins/AccountMoleculerService.ts`
|
|
209
|
+
- `packages-modules/billing-api/server/src/plugins/InvoiceMoleculerService.ts`
|
|
210
|
+
|
|
211
|
+
Pattern 2 (Sub-module):
|
|
212
|
+
`packages-modules/[module-name]/server/src/modules/[service]/plugins/[ServiceName]MoleculerService.ts`
|
|
213
|
+
|
|
214
|
+
Examples:
|
|
215
|
+
|
|
216
|
+
- `packages-modules/account-api/server/src/modules/project/plugins/ProjectMoleculerService.ts`
|
|
217
|
+
- `packages-modules/account-api/server/src/modules/team/plugins/TeamMoleculerService.ts`
|
|
218
|
+
|
|
219
|
+
**Naming Rules:**
|
|
220
|
+
|
|
221
|
+
1. ✅ File name MUST match class name exactly
|
|
222
|
+
2. ✅ Use PascalCase (first letter uppercase)
|
|
223
|
+
3. ✅ Always end with `MoleculerService.ts`
|
|
224
|
+
4. ✅ Service name comes first: `[ServiceName]MoleculerService`
|
|
225
|
+
5. ❌ Never use kebab-case (dashes)
|
|
226
|
+
6. ❌ Never use snake-case (underscores)
|
|
227
|
+
7. ❌ Never use plural form (e.g., `AccountsMoleculerService` is wrong)
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 🎯 RECOMMENDED APPROACH: Auto-Generation
|
|
232
|
+
|
|
233
|
+
**Use `Moleculer.generateActionsAndEvents()` from common package** - eliminates 90% of boilerplate code!
|
|
234
|
+
|
|
235
|
+
**Quick Example:**
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { Service, ServiceBroker } from 'moleculer';
|
|
239
|
+
import { Container } from 'inversify';
|
|
240
|
+
import { I[ServiceName]Service, [ServiceName]ServiceSchemas, SERVER_TYPES } from 'common/server';
|
|
241
|
+
import { Moleculer, zodSchemasToMoleculer } from '@common-stack/codegen-zod';
|
|
242
|
+
|
|
243
|
+
export class [ServiceName]MoleculerService extends Service {
|
|
244
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
245
|
+
super(broker);
|
|
246
|
+
// Get topic from auto-generated schemas
|
|
247
|
+
const { topic } = [ServiceName]ServiceSchemas;
|
|
248
|
+
const service = container.get<I[ServiceName]Service>(SERVER_TYPES.[ServiceName]Service);
|
|
249
|
+
|
|
250
|
+
// Convert Zod schemas to Moleculer param validation
|
|
251
|
+
const paramOverrides = zodSchemasToMoleculer([ServiceName]ServiceSchemas);
|
|
252
|
+
|
|
253
|
+
// ⚡ Auto-generate actions and events with validation
|
|
254
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<I[ServiceName]Service>(service, {
|
|
255
|
+
paramOverrides,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
this.parseServiceSchema({
|
|
259
|
+
name: topic, // ✅ Auto-derived from interface name
|
|
260
|
+
actions, // ✅ All methods (including parent class) with validation
|
|
261
|
+
events, // ✅ All @Moleculer.EventHandler decorators
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**That's it! 30 lines instead of 200+**
|
|
268
|
+
|
|
269
|
+
### 📊 Auto-Generation vs Manual Comparison
|
|
270
|
+
|
|
271
|
+
| Feature | Auto-Generation | Manual |
|
|
272
|
+
| ------------------------ | --------------------------------- | ------------------------- |
|
|
273
|
+
| **Code Size** | ~30 lines | ~200+ lines |
|
|
274
|
+
| **Parent Class Methods** | ✅ Automatic | ❌ Must copy each one |
|
|
275
|
+
| **Event Handlers** | ✅ Auto-detected from decorators | ❌ Must define each one |
|
|
276
|
+
| **Type Safety** | ✅ Full TypeScript inference | ⚠️ Manual typing required |
|
|
277
|
+
| **Maintenance** | ✅ Add method → auto-available | ❌ Must update 3 places |
|
|
278
|
+
| **Parameter Validation** | ✅ Auto-generated from TypeScript | ✅ Explicit schemas |
|
|
279
|
+
| **Custom Logic** | ⚠️ Requires Pattern 3 override | ✅ Full control |
|
|
280
|
+
| **Best For** | **ALL services (99% of cases)** | Complex custom validation |
|
|
281
|
+
|
|
282
|
+
**Recommendation**: **USE PATTERN 1 FOR ALL NEW SERVICES**. Add manual overrides (Pattern 2/3) only in rare cases where you need custom validation or logic.
|
|
283
|
+
|
|
284
|
+
For detailed patterns, see sections below ⬇️
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### ⚡ Auto-Generation from Common Package
|
|
289
|
+
|
|
290
|
+
**IMPORTANT**: The common package (`packages/common/src/utils/typed-moleculer-service.ts`) provides powerful utilities to auto-generate actions and events from service classes.
|
|
291
|
+
|
|
292
|
+
#### Primary Function (Use This):
|
|
293
|
+
|
|
294
|
+
**`Moleculer.generateActionsAndEvents<TService>(service, config?)`**
|
|
295
|
+
|
|
296
|
+
- Generates BOTH actions AND events from a single service instance
|
|
297
|
+
- Actions: Generated from regular service methods
|
|
298
|
+
- Events: Generated from methods decorated with `@Moleculer.EventHandler()`
|
|
299
|
+
- Automatically excludes event handler methods from actions
|
|
300
|
+
- **USE THIS FOR ALL SERVICES** (with or without event handlers)
|
|
301
|
+
|
|
302
|
+
#### Alternative Function (Rarely Needed):
|
|
303
|
+
|
|
304
|
+
**`Moleculer.generateAutoInferredActions<TService>(service, config?)`**
|
|
305
|
+
|
|
306
|
+
- Automatically generates Moleculer actions from ALL service methods
|
|
307
|
+
- Infers parameters from TypeScript function signatures
|
|
308
|
+
- Traverses entire prototype chain to include inherited methods from parent classes
|
|
309
|
+
- Excludes lifecycle methods (dispose) and base CRUD methods by default
|
|
310
|
+
- Use this ONLY if you don't have event handlers and want slightly different behavior
|
|
311
|
+
|
|
312
|
+
#### Auto-Generation Configuration (All Optional):
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
type AutoInferredActionConfig<TService> = {
|
|
316
|
+
// Override param schemas for specific methods (OPTIONAL - Pattern 2)
|
|
317
|
+
paramOverrides?: Partial<{
|
|
318
|
+
[K in ServiceMethods<TService>]: Record<string, unknown>;
|
|
319
|
+
}>;
|
|
320
|
+
|
|
321
|
+
// Only include specific methods - whitelist (OPTIONAL - Pattern 4)
|
|
322
|
+
include?: Array<ServiceMethods<TService>>;
|
|
323
|
+
|
|
324
|
+
// Exclude specific methods - blacklist (OPTIONAL - Pattern 3)
|
|
325
|
+
exclude?: Array<ServiceMethods<TService>>;
|
|
326
|
+
|
|
327
|
+
// Map action names to different method names (OPTIONAL - Advanced)
|
|
328
|
+
actionToMethodMap?: Record<string, string>;
|
|
329
|
+
|
|
330
|
+
// Add custom Moleculer action properties (cache, visibility, etc.)
|
|
331
|
+
actionConfig?: Partial<{
|
|
332
|
+
[K in ServiceMethods<TService>]: {
|
|
333
|
+
cache?: boolean | object;
|
|
334
|
+
visibility?: 'public' | 'protected' | 'private';
|
|
335
|
+
[key: string]: unknown;
|
|
336
|
+
};
|
|
337
|
+
}>;
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### Usage Patterns:
|
|
342
|
+
|
|
343
|
+
**Pattern 1: Full Auto-Generation (USE THIS IN ALL CASES)**
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { Moleculer } from '@common-stack/codegen-zod';
|
|
347
|
+
|
|
348
|
+
export class [ServiceName]MoleculerService extends Service {
|
|
349
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
350
|
+
super(broker);
|
|
351
|
+
const [serviceName]Service = container.get<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service);
|
|
352
|
+
|
|
353
|
+
const { topic } = [ServiceName]ServiceSchemas;
|
|
354
|
+
|
|
355
|
+
// Auto-generate ALL actions and events
|
|
356
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<I[ServiceName]Service>(
|
|
357
|
+
[serviceName]Service
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
this.parseServiceSchema({
|
|
361
|
+
name: topic,
|
|
362
|
+
actions, // All service methods auto-generated
|
|
363
|
+
events, // All @Moleculer.EventHandler methods auto-generated
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Benefits:**
|
|
370
|
+
|
|
371
|
+
- ✅ Simplest implementation - zero configuration needed
|
|
372
|
+
- ✅ All methods automatically included (including parent class methods)
|
|
373
|
+
- ✅ Event handlers automatically detected from `@Moleculer.EventHandler` decorators
|
|
374
|
+
- ✅ Zero maintenance - add method to interface, it's automatically available
|
|
375
|
+
- ✅ Type-safe with full TypeScript inference
|
|
376
|
+
|
|
377
|
+
**When to use:** Use this pattern for **ALL new services** unless you have specific validation requirements (see Pattern 2 below).
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
**Pattern 2: Auto-Generation with Custom Validation (OPTIONAL - Only for Special Cases)**
|
|
382
|
+
|
|
383
|
+
Use this pattern ONLY when you need custom Moleculer validation schemas that differ from the auto-generated ones.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
export class [ServiceName]MoleculerService extends Service {
|
|
387
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
388
|
+
super(broker);
|
|
389
|
+
const [serviceName]Service = container.get<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service);
|
|
390
|
+
|
|
391
|
+
const { topic } = [ServiceName]ServiceSchemas;
|
|
392
|
+
|
|
393
|
+
// Auto-generate with custom validation overrides
|
|
394
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<I[ServiceName]Service>(
|
|
395
|
+
[serviceName]Service,
|
|
396
|
+
{
|
|
397
|
+
// Override param schemas for specific methods
|
|
398
|
+
paramOverrides: {
|
|
399
|
+
findById: { id: 'string' }, // Add Moleculer validation
|
|
400
|
+
updateItem: { id: 'string', input: 'object' },
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
this.parseServiceSchema({
|
|
406
|
+
name: topic,
|
|
407
|
+
actions,
|
|
408
|
+
events,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**When to use:** Only when you need:
|
|
415
|
+
|
|
416
|
+
- Custom Moleculer validation rules (min, max, positive, etc.)
|
|
417
|
+
- Different validation than TypeScript types provide
|
|
418
|
+
- Backward compatibility with existing validation
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
**Pattern 3: Partial Auto-Generation with Manual Overrides (ADVANCED - Rarely Needed)**
|
|
423
|
+
|
|
424
|
+
Use this pattern ONLY when you need complete control over specific actions with custom logic.
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
export class [ServiceName]MoleculerService extends Service {
|
|
428
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
429
|
+
super(broker);
|
|
430
|
+
const [serviceName]Service = container.get<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service);
|
|
431
|
+
|
|
432
|
+
const { topic } = [ServiceName]ServiceSchemas;
|
|
433
|
+
|
|
434
|
+
// Auto-generate with exclusions for manual override
|
|
435
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<I[ServiceName]Service>(
|
|
436
|
+
[serviceName]Service,
|
|
437
|
+
{
|
|
438
|
+
// Exclude methods that need custom handling
|
|
439
|
+
exclude: ['complexMethod', 'customLogicMethod'],
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
this.parseServiceSchema({
|
|
444
|
+
name: topic,
|
|
445
|
+
actions: {
|
|
446
|
+
...actions, // All auto-generated actions
|
|
447
|
+
|
|
448
|
+
// Manual overrides for excluded methods
|
|
449
|
+
[[ServiceName]ServiceAction.ComplexMethod]: {
|
|
450
|
+
params: zodSchemasToMoleculer([ServiceName]ServiceSchemas.complexMethod),
|
|
451
|
+
handler: async (ctx: Context<ComplexParams>) => {
|
|
452
|
+
// Custom pre-processing logic
|
|
453
|
+
const processed = await this.preProcess(ctx.params);
|
|
454
|
+
|
|
455
|
+
// Call service method
|
|
456
|
+
const result = await [serviceName]Service.complexMethod(processed);
|
|
457
|
+
|
|
458
|
+
// Custom post-processing
|
|
459
|
+
return this.postProcess(result);
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
events,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**When to use:** Only when you need:
|
|
470
|
+
|
|
471
|
+
- Custom pre/post-processing logic in action handlers
|
|
472
|
+
- Different parameter transformation before calling service
|
|
473
|
+
- Special error handling or caching logic
|
|
474
|
+
- **In 99% of cases, Pattern 1 is sufficient**
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
**Pattern 4: Include Only Specific Methods (OPTIONAL - For Whitelisting)**
|
|
479
|
+
|
|
480
|
+
Use this ONLY when you want to expose a limited subset of service methods as Moleculer actions.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<I[ServiceName]Service>(
|
|
484
|
+
[serviceName]Service,
|
|
485
|
+
{
|
|
486
|
+
include: ['findById', 'createItem', 'updateItem'], // Only these methods
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
**When to use:** Only when you need:
|
|
492
|
+
|
|
493
|
+
- Expose only specific methods via Moleculer (security/API design)
|
|
494
|
+
- Create a limited public API from a larger service interface
|
|
495
|
+
- **In 99% of cases, Pattern 1 is sufficient**
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
#### Key Benefits of Auto-Generation:
|
|
500
|
+
|
|
501
|
+
✅ **Automatic inheritance**: Parent class methods automatically included
|
|
502
|
+
✅ **Type-safe**: Full TypeScript support with generics
|
|
503
|
+
✅ **Event integration**: Seamlessly handles `@Moleculer.EventHandler` decorators
|
|
504
|
+
✅ **Zero boilerplate**: No repetitive handler code
|
|
505
|
+
✅ **Compile-time safety**: TypeScript catches method changes
|
|
506
|
+
✅ **Flexible**: Override specific methods when needed
|
|
507
|
+
|
|
508
|
+
#### 🔍 How Prototype Chain Traversal Works:
|
|
509
|
+
|
|
510
|
+
The auto-generation uses `getAllMethodNames()` helper that traverses the ENTIRE prototype chain:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
function getAllMethodNames(obj: unknown): string[] {
|
|
514
|
+
const methods = new Set<string>();
|
|
515
|
+
let current = obj;
|
|
516
|
+
|
|
517
|
+
// Traverse up the prototype chain
|
|
518
|
+
while (current && current !== Object.prototype) {
|
|
519
|
+
const prototype = Object.getPrototypeOf(current);
|
|
520
|
+
if (prototype && prototype !== Object.prototype) {
|
|
521
|
+
Object.getOwnPropertyNames(prototype).forEach((name) => {
|
|
522
|
+
// Only add functions, not constructors or private methods
|
|
523
|
+
if (name !== 'constructor' && !name.startsWith('_')) {
|
|
524
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
|
525
|
+
if (descriptor && typeof descriptor.value === 'function') {
|
|
526
|
+
methods.add(name);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
current = prototype;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return Array.from(methods);
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Why This Matters:**
|
|
539
|
+
|
|
540
|
+
- **Problem**: `Object.getOwnPropertyNames(Object.getPrototypeOf(service))` only gets immediate class methods
|
|
541
|
+
- **Solution**: `getAllMethodNames()` traverses entire chain to include parent class methods
|
|
542
|
+
- **Example**: `OrganizationService extends BaseOrganizationService`
|
|
543
|
+
- Without traversal: Only ~5 methods from OrganizationService
|
|
544
|
+
- With traversal: All 60+ methods from both classes
|
|
545
|
+
- Result: All inherited CRUD operations automatically available as Moleculer actions
|
|
546
|
+
|
|
547
|
+
**Real-World Impact:**
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
// Before (missing parent methods):
|
|
551
|
+
OrganizationMoleculerService actions: [
|
|
552
|
+
'createOrganization', // ✅ From OrganizationService
|
|
553
|
+
'updateOrganization', // ✅ From OrganizationService
|
|
554
|
+
// ❌ Missing 50+ methods from BaseOrganizationService
|
|
555
|
+
]
|
|
556
|
+
|
|
557
|
+
// After (with prototype chain traversal):
|
|
558
|
+
OrganizationMoleculerService actions: [
|
|
559
|
+
'createOrganization', // ✅ From OrganizationService
|
|
560
|
+
'updateOrganization', // ✅ From OrganizationService
|
|
561
|
+
'findById', // ✅ From BaseOrganizationService
|
|
562
|
+
'findAll', // ✅ From BaseOrganizationService
|
|
563
|
+
'updateStatus', // ✅ From BaseOrganizationService
|
|
564
|
+
// ... 50+ more methods from parent classes
|
|
565
|
+
]
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### When to Use Each Pattern:
|
|
569
|
+
|
|
570
|
+
**Pattern 1 (Full Auto-Generation) - DEFAULT CHOICE:**
|
|
571
|
+
|
|
572
|
+
✅ **Use for ALL new services (99% of cases)**
|
|
573
|
+
|
|
574
|
+
- Service has any number of methods (small or large)
|
|
575
|
+
- Standard CRUD operations
|
|
576
|
+
- Event handlers using `@Moleculer.EventHandler` decorator
|
|
577
|
+
- Automatic parent class method inclusion
|
|
578
|
+
- No special validation requirements
|
|
579
|
+
|
|
580
|
+
**Pattern 2 (Custom Validation) - RARE:**
|
|
581
|
+
|
|
582
|
+
⚠️ **Use ONLY when:**
|
|
583
|
+
|
|
584
|
+
- Need Moleculer-specific validation (min, max, positive, etc.) that differs from TypeScript types
|
|
585
|
+
- Backward compatibility with existing validation rules
|
|
586
|
+
- **Estimate: ~5% of services need this**
|
|
587
|
+
|
|
588
|
+
**Pattern 3 (Manual Overrides) - VERY RARE:**
|
|
589
|
+
|
|
590
|
+
⚠️ **Use ONLY when:**
|
|
591
|
+
|
|
592
|
+
- Need custom logic before/after service calls (pre/post-processing)
|
|
593
|
+
- Parameters need transformation before passing to service
|
|
594
|
+
- Special error handling or caching logic
|
|
595
|
+
- **Estimate: ~1% of services need this**
|
|
596
|
+
|
|
597
|
+
**Pattern 4 (Whitelist) - RARE:**
|
|
598
|
+
|
|
599
|
+
⚠️ **Use ONLY when:**
|
|
600
|
+
|
|
601
|
+
- Need to expose limited subset of methods (API security/design)
|
|
602
|
+
- **Estimate: ~2% of services need this**
|
|
603
|
+
|
|
604
|
+
**Manual Implementation - LEGACY:**
|
|
605
|
+
|
|
606
|
+
❌ **Avoid - use Pattern 1 instead**
|
|
607
|
+
|
|
608
|
+
- Only for backward compatibility with existing services
|
|
609
|
+
- Will be migrated to auto-generation over time
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
#### 📡 Event Handler Pattern with Decorators:
|
|
614
|
+
|
|
615
|
+
**Modern Approach**: Use `@Moleculer.EventHandler()` decorator in service classes:
|
|
616
|
+
|
|
617
|
+
**CRITICAL NAMING CONVENTION**: Event handler methods MUST be prefixed with `on` (e.g., `onUserCreated`, `onAccountDeleted`, `onPropertyUpdated`).
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
import { Moleculer } from '@common-stack/codegen-zod';
|
|
621
|
+
|
|
622
|
+
@injectable()
|
|
623
|
+
export class TeamService extends BaseTeamService {
|
|
624
|
+
// Event handler for cross-service notifications
|
|
625
|
+
// ✅ CORRECT: Method name starts with 'on'
|
|
626
|
+
@Moleculer.EventHandler(OrganizationServiceAction.OnOrgMemberRemoved)
|
|
627
|
+
async onOrgMemberRemoved(event: { orgName: string; userId: string }): Promise<void> {
|
|
628
|
+
await this.changeTeamMemberStatus(event.userId, TeamMemberStatus.Deactivated);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Event handler for cleanup on deletion
|
|
632
|
+
// ✅ CORRECT: Method name starts with 'on'
|
|
633
|
+
@Moleculer.EventHandler(AccountServiceAction.OnAccountDeleted)
|
|
634
|
+
async onAccountDeleted(event: { userId: string }): Promise<void> {
|
|
635
|
+
await this.removeMemberFromTeams(event.userId);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ❌ WRONG: Missing 'on' prefix
|
|
639
|
+
// @Moleculer.EventHandler(AccountServiceAction.OnAccountDeleted)
|
|
640
|
+
// async handleAccountDeleted(event: { userId: string }): Promise<void> { }
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Event Handler Naming Rules:**
|
|
645
|
+
|
|
646
|
+
1. ✅ **MUST start with `on` prefix** - Makes it clear these are event handlers
|
|
647
|
+
2. ✅ Use camelCase after the prefix (e.g., `onUserCreated`, not `onusercreated`)
|
|
648
|
+
3. ✅ Match the event name structure: `OnEventName` → `onEventName`
|
|
649
|
+
4. ❌ Never use other prefixes (handle, process, react, etc.)
|
|
650
|
+
5. ❌ Never use snake_case or kebab-case
|
|
651
|
+
|
|
652
|
+
**Examples:**
|
|
653
|
+
|
|
654
|
+
```typescript
|
|
655
|
+
// Event: AccountServiceAction.OnAccountDeleted
|
|
656
|
+
// ✅ Handler: onAccountDeleted
|
|
657
|
+
@Moleculer.EventHandler(AccountServiceAction.OnAccountDeleted)
|
|
658
|
+
async onAccountDeleted(event: { userId: string }): Promise<void> { }
|
|
659
|
+
|
|
660
|
+
// Event: PropertyMoleculerServiceEvent.OnPropertyCreated
|
|
661
|
+
// ✅ Handler: onPropertyCreated
|
|
662
|
+
@Moleculer.EventHandler(PropertyMoleculerServiceEvent.OnPropertyCreated)
|
|
663
|
+
async onPropertyCreated(property: IProperty): Promise<void> { }
|
|
664
|
+
|
|
665
|
+
// Event: OrganizationServiceAction.OnOrgMemberRemoved
|
|
666
|
+
// ✅ Handler: onOrgMemberRemoved
|
|
667
|
+
@Moleculer.EventHandler(OrganizationServiceAction.OnOrgMemberRemoved)
|
|
668
|
+
async onOrgMemberRemoved(event: { orgName: string; userId: string }): Promise<void> { }
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
**Auto-Generation Handles:**
|
|
672
|
+
|
|
673
|
+
1. Detects all `@Moleculer.EventHandler` decorated methods
|
|
674
|
+
2. Excludes them from actions (they're not callable actions)
|
|
675
|
+
3. Generates event handlers in Moleculer schema
|
|
676
|
+
4. Sets up proper event routing and payload handling
|
|
677
|
+
|
|
678
|
+
**Event Handler Metadata:**
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
// Decorator stores metadata:
|
|
682
|
+
{
|
|
683
|
+
eventName: 'OrganizationServiceAction.OnOrgMemberRemoved',
|
|
684
|
+
methodName: 'onOrgMemberRemoved',
|
|
685
|
+
group?: 'team-service' // Optional event group for load balancing
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
**What Auto-Generation Does with Event Handlers:**
|
|
690
|
+
|
|
691
|
+
1. **Detects** all methods decorated with `@Moleculer.EventHandler`
|
|
692
|
+
2. **Excludes** them from actions list (they're not callable actions, they react to events)
|
|
693
|
+
3. **Generates** proper Moleculer event handlers in the service schema
|
|
694
|
+
4. **Validates** that method name starts with `on` prefix (convention enforcement)
|
|
695
|
+
5. **Sets up** proper event routing and payload handling
|
|
696
|
+
|
|
697
|
+
**Important: Event Handlers vs Regular Methods:**
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
@injectable()
|
|
701
|
+
export class PropertyService extends BaseService {
|
|
702
|
+
// ✅ Regular method → becomes Moleculer ACTION (can be called directly)
|
|
703
|
+
async updateProperty(id: string, data: any): Promise<IProperty> {
|
|
704
|
+
// Business logic
|
|
705
|
+
return updatedProperty;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// ✅ Event handler → becomes Moleculer EVENT HANDLER (reacts to broadcasts)
|
|
709
|
+
// NOT exposed as an action - cannot be called directly
|
|
710
|
+
@Moleculer.EventHandler(AccountServiceAction.OnAccountDeleted)
|
|
711
|
+
async onAccountDeleted(event: { userId: string }): Promise<void> {
|
|
712
|
+
// React to event - find and deactivate user's properties
|
|
713
|
+
await this.updateStatus({ user: event.userId }, PropertyStatusMachineState.Deactivated);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Event Flow Pattern:**
|
|
719
|
+
|
|
720
|
+
```
|
|
721
|
+
Service A (creates entity)
|
|
722
|
+
↓
|
|
723
|
+
Direct service call: teamService.createTeam()
|
|
724
|
+
↓
|
|
725
|
+
Broadcast notification: broker.broadcast(OnCreateTeam, { team })
|
|
726
|
+
↓
|
|
727
|
+
Service B (reacts to event)
|
|
728
|
+
↓
|
|
729
|
+
@Moleculer.EventHandler(OnCreateTeam)
|
|
730
|
+
async onCreateTeam(event: { team: ITeam }): Promise<void>
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
**Important Rules:**
|
|
734
|
+
|
|
735
|
+
- ✅ Broadcasts send result objects (created entity)
|
|
736
|
+
- ✅ Event handlers perform side effects only (logging, notifications, cleanup)
|
|
737
|
+
- ❌ Event handlers should NOT recreate entities (causes duplicates)
|
|
738
|
+
- ✅ Cross-service events use namespace prefixes (OrganizationServiceAction, AccountServiceAction)
|
|
739
|
+
|
|
740
|
+
**Template Structure (Manual - Use when auto-generation doesn't fit):**
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
import { Context, Service, ServiceBroker } from 'moleculer';
|
|
744
|
+
import { Container } from 'inversify';
|
|
745
|
+
import {
|
|
746
|
+
[ServiceName]ServiceAction,
|
|
747
|
+
MoleculerServiceName,
|
|
748
|
+
I[ServiceName]Service,
|
|
749
|
+
SERVER_TYPES,
|
|
750
|
+
// Import all required types for method parameters
|
|
751
|
+
} from 'common/server';
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Type utility to verify that all I[ServiceName]Service methods have corresponding Moleculer actions.
|
|
755
|
+
* This ensures compile-time checking when the service interface changes.
|
|
756
|
+
*
|
|
757
|
+
* Excluded methods:
|
|
758
|
+
* - dispose: Lifecycle method, not proxied
|
|
759
|
+
* - get/getAll/count/etc: BaseService CRUD methods
|
|
760
|
+
* - bulkDelete/delete: Not supported in proxy services
|
|
761
|
+
*/
|
|
762
|
+
type ServiceMethodsToActions<T> = {
|
|
763
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
764
|
+
[K in keyof T as T[K] extends Function
|
|
765
|
+
? K extends
|
|
766
|
+
| 'dispose'
|
|
767
|
+
| 'get'
|
|
768
|
+
| 'getAll'
|
|
769
|
+
| 'bulkDelete'
|
|
770
|
+
| 'delete'
|
|
771
|
+
| 'count'
|
|
772
|
+
| 'getByName'
|
|
773
|
+
| 'getByIds'
|
|
774
|
+
| 'getAllWithCount'
|
|
775
|
+
| 'create'
|
|
776
|
+
| 'update'
|
|
777
|
+
| 'bulkCreate'
|
|
778
|
+
| 'insert'
|
|
779
|
+
? never
|
|
780
|
+
: K
|
|
781
|
+
: never]: K;
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// This type will show a compile error if any method is missing from the actions
|
|
785
|
+
type Required[ServiceName]Actions = ServiceMethodsToActions<I[ServiceName]Service>;
|
|
786
|
+
|
|
787
|
+
// Compile-time verification helper - causes error if methods don't match actions
|
|
788
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
789
|
+
const _typeCheck: Record<keyof Required[ServiceName]Actions, boolean> = {
|
|
790
|
+
methodName1: true,
|
|
791
|
+
methodName2: true,
|
|
792
|
+
// ... list ALL service methods here (except excluded ones)
|
|
793
|
+
// TypeScript will error if you miss any or add extra ones
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Moleculer Service for [ServiceName] operations.
|
|
798
|
+
* Registers actions that delegate to the actual [ServiceName]Service implementation.
|
|
799
|
+
*
|
|
800
|
+
* This service runs on the server side and handles incoming Moleculer action calls.
|
|
801
|
+
* Each action validates parameters and calls the corresponding service method.
|
|
802
|
+
*
|
|
803
|
+
* @see [ServiceName]Service for business logic implementation
|
|
804
|
+
* @see [ServiceName]ProxyService for client-side proxy
|
|
805
|
+
*/
|
|
806
|
+
export class [ServiceName]MoleculerService extends Service {
|
|
807
|
+
constructor(broker: ServiceBroker, { container }: { container: Container; settings?: unknown }) {
|
|
808
|
+
super(broker);
|
|
809
|
+
const topic = MoleculerServiceName.[ServiceName];
|
|
810
|
+
const [serviceName]Service = container.get<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service);
|
|
811
|
+
|
|
812
|
+
this.parseServiceSchema({
|
|
813
|
+
name: topic,
|
|
814
|
+
|
|
815
|
+
// ============================================================================
|
|
816
|
+
// EVENTS (optional - for reacting to other services)
|
|
817
|
+
// ============================================================================
|
|
818
|
+
events: {
|
|
819
|
+
// Example: React to external events
|
|
820
|
+
// [OtherServiceAction.OnSomethingHappened]: {
|
|
821
|
+
// params: {
|
|
822
|
+
// id: 'string',
|
|
823
|
+
// },
|
|
824
|
+
// handler: async (ctx: Context<{ event: any }>) => {
|
|
825
|
+
// // React to event
|
|
826
|
+
// this.logger.debug('Event received');
|
|
827
|
+
// },
|
|
828
|
+
// },
|
|
829
|
+
},
|
|
830
|
+
|
|
831
|
+
// ============================================================================
|
|
832
|
+
// ACTIONS (required - one for each service method)
|
|
833
|
+
// ============================================================================
|
|
834
|
+
actions: {
|
|
835
|
+
// READ OPERATIONS
|
|
836
|
+
[[ServiceName]ServiceAction.GetItem]: {
|
|
837
|
+
params: {
|
|
838
|
+
id: 'string',
|
|
839
|
+
},
|
|
840
|
+
handler: async (ctx: Context<{ id: string }>) =>
|
|
841
|
+
[serviceName]Service.getItem(ctx.params.id),
|
|
842
|
+
},
|
|
843
|
+
|
|
844
|
+
[[ServiceName]ServiceAction.FindItems]: {
|
|
845
|
+
params: {
|
|
846
|
+
where: 'object',
|
|
847
|
+
},
|
|
848
|
+
handler: async (ctx: Context<{ where: IWhereInput }>) =>
|
|
849
|
+
[serviceName]Service.findItems(ctx.params.where),
|
|
850
|
+
},
|
|
851
|
+
|
|
852
|
+
// CREATE OPERATIONS
|
|
853
|
+
[[ServiceName]ServiceAction.CreateItem]: {
|
|
854
|
+
params: {
|
|
855
|
+
input: 'object',
|
|
856
|
+
},
|
|
857
|
+
handler: async (ctx: Context<{ input: ICreateInput }>) =>
|
|
858
|
+
[serviceName]Service.createItem(ctx.params.input),
|
|
859
|
+
},
|
|
860
|
+
|
|
861
|
+
// UPDATE OPERATIONS
|
|
862
|
+
[[ServiceName]ServiceAction.UpdateItem]: {
|
|
863
|
+
params: {
|
|
864
|
+
id: 'string',
|
|
865
|
+
input: 'object',
|
|
866
|
+
},
|
|
867
|
+
handler: async (ctx: Context<{ id: string; input: IUpdateInput }>) =>
|
|
868
|
+
[serviceName]Service.updateItem(ctx.params.id, ctx.params.input),
|
|
869
|
+
},
|
|
870
|
+
|
|
871
|
+
// DELETE OPERATIONS
|
|
872
|
+
[[ServiceName]ServiceAction.DeleteItem]: {
|
|
873
|
+
params: {
|
|
874
|
+
id: 'string',
|
|
875
|
+
},
|
|
876
|
+
handler: async (ctx: Context<{ id: string }>) =>
|
|
877
|
+
[serviceName]Service.deleteItem(ctx.params.id),
|
|
878
|
+
},
|
|
879
|
+
|
|
880
|
+
// For methods with multiple parameters, wrap in params object
|
|
881
|
+
[[ServiceName]ServiceAction.ComplexMethod]: {
|
|
882
|
+
params: {
|
|
883
|
+
param1: 'string',
|
|
884
|
+
param2: 'object',
|
|
885
|
+
param3: { type: 'string', optional: true },
|
|
886
|
+
},
|
|
887
|
+
handler: async (ctx: Context<{ param1: string; param2: IType; param3?: string }>) =>
|
|
888
|
+
[serviceName]Service.complexMethod(ctx.params.param1, ctx.params.param2, ctx.params.param3),
|
|
889
|
+
},
|
|
890
|
+
},
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Moleculer Service Implementation Rules
|
|
897
|
+
|
|
898
|
+
#### Type Safety with Compile-Time Verification
|
|
899
|
+
|
|
900
|
+
**CRITICAL**: Use the type checking pattern to ensure all service methods have corresponding actions:
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
/**
|
|
904
|
+
* Type utility to verify that all I[ServiceName]Service methods have corresponding Moleculer actions.
|
|
905
|
+
* This ensures compile-time checking when the service interface changes.
|
|
906
|
+
*
|
|
907
|
+
* Excluded methods:
|
|
908
|
+
* - dispose: Lifecycle method, not proxied
|
|
909
|
+
* - get/getAll/count/etc: BaseService CRUD methods (inherited from BaseService)
|
|
910
|
+
* - bulkDelete/delete: Not supported in proxy services
|
|
911
|
+
*/
|
|
912
|
+
type ServiceMethodsToActions<T> = {
|
|
913
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
914
|
+
[K in keyof T as T[K] extends Function
|
|
915
|
+
? K extends
|
|
916
|
+
| 'dispose'
|
|
917
|
+
| 'get'
|
|
918
|
+
| 'getAll'
|
|
919
|
+
| 'bulkDelete'
|
|
920
|
+
| 'delete'
|
|
921
|
+
| 'count'
|
|
922
|
+
| 'getByName'
|
|
923
|
+
| 'getByIds'
|
|
924
|
+
| 'getAllWithCount'
|
|
925
|
+
| 'create'
|
|
926
|
+
| 'update'
|
|
927
|
+
| 'bulkCreate'
|
|
928
|
+
| 'insert'
|
|
929
|
+
? never
|
|
930
|
+
: K
|
|
931
|
+
: never]: K;
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// This type will show a compile error if any method is missing from the actions
|
|
935
|
+
type Required[ServiceName]Actions = ServiceMethodsToActions<I[ServiceName]Service>;
|
|
936
|
+
|
|
937
|
+
// Compile-time verification helper - causes error if methods don't match actions
|
|
938
|
+
// Add this BEFORE the service class definition
|
|
939
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
940
|
+
const _typeCheck: Record<keyof Required[ServiceName]Actions, boolean> = {
|
|
941
|
+
methodName1: true,
|
|
942
|
+
methodName2: true,
|
|
943
|
+
// ... list ALL service methods here (except excluded ones)
|
|
944
|
+
// TypeScript will error if you:
|
|
945
|
+
// 1. Miss a method that exists in the interface
|
|
946
|
+
// 2. Add a method that doesn't exist in the interface
|
|
947
|
+
// 3. Misspell a method name
|
|
948
|
+
};
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
**Important Limitation**:
|
|
952
|
+
|
|
953
|
+
⚠️ **The `_typeCheck` constant only verifies that method names are listed** - it does NOT verify that:
|
|
954
|
+
|
|
955
|
+
1. Action handlers are actually implemented in the `actions` object
|
|
956
|
+
2. Action handlers correctly delegate to the service methods
|
|
957
|
+
3. Actions provided by mixins (like `BaseServiceMixin2`) are working
|
|
958
|
+
|
|
959
|
+
**Why this limitation exists**:
|
|
960
|
+
|
|
961
|
+
- TypeScript cannot verify that a string key in one object (actions) matches a boolean key in another object (\_typeCheck)
|
|
962
|
+
- Moleculer's dynamic action registration happens at runtime, not compile time
|
|
963
|
+
- Mixins add actions dynamically, which TypeScript cannot track
|
|
964
|
+
|
|
965
|
+
**What \_typeCheck DOES catch**:
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
// ✅ CATCHES: Method added to interface but not listed in _typeCheck
|
|
969
|
+
interface IAccountService {
|
|
970
|
+
newMethod(): void; // Added
|
|
971
|
+
}
|
|
972
|
+
const _typeCheck = {
|
|
973
|
+
// Missing newMethod - COMPILE ERROR
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// ✅ CATCHES: Method removed from interface but still in _typeCheck
|
|
977
|
+
const _typeCheck = {
|
|
978
|
+
oldMethod: true, // No longer in interface - COMPILE ERROR
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// ✅ CATCHES: Method name typo in _typeCheck
|
|
982
|
+
const _typeCheck = {
|
|
983
|
+
creeateAccount: true, // Typo - COMPILE ERROR (should be createAccount)
|
|
984
|
+
};
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
**What \_typeCheck DOES NOT catch**:
|
|
988
|
+
|
|
989
|
+
```typescript
|
|
990
|
+
// ❌ DOES NOT CATCH: Listed in _typeCheck but action handler not implemented
|
|
991
|
+
const _typeCheck = {
|
|
992
|
+
createAccount: true, // Listed
|
|
993
|
+
};
|
|
994
|
+
actions: {
|
|
995
|
+
// createAccount handler missing - NO COMPILE ERROR!
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
// ❌ DOES NOT CATCH: Action handler exists but doesn't call service method
|
|
999
|
+
actions: {
|
|
1000
|
+
[AccountServiceAction.CreateAccount]: {
|
|
1001
|
+
handler: () => console.log('oops'), // Wrong implementation - NO COMPILE ERROR!
|
|
1002
|
+
},
|
|
1003
|
+
};
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
**Best Practices to Address Limitations**:
|
|
1007
|
+
|
|
1008
|
+
1. **Use \_typeCheck as a checklist** - When you add a method to \_typeCheck, immediately implement the action handler
|
|
1009
|
+
|
|
1010
|
+
2. **Follow naming convention** - Use consistent enum values that match method names:
|
|
1011
|
+
|
|
1012
|
+
```typescript
|
|
1013
|
+
// Good: Easy to verify visually
|
|
1014
|
+
createAccount: true, // in _typeCheck
|
|
1015
|
+
[ServiceAction.CreateAccount]: { ... } // in actions
|
|
1016
|
+
|
|
1017
|
+
// Bad: Hard to match
|
|
1018
|
+
createAccount: true, // in _typeCheck
|
|
1019
|
+
[ServiceAction.AddNew]: { ... } // in actions - what method does this call?
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
3. **Group actions by \_typeCheck order** - List actions in same order as \_typeCheck for easy visual verification
|
|
1023
|
+
|
|
1024
|
+
4. **Use BaseServiceMixin2 for CRUD operations** - Don't manually implement get/create/update if the mixin provides them:
|
|
1025
|
+
|
|
1026
|
+
```typescript
|
|
1027
|
+
this.parseServiceSchema({
|
|
1028
|
+
name: topic,
|
|
1029
|
+
mixins: [BaseServiceMixin2(this.serviceInstance)], // Provides CRUD actions
|
|
1030
|
+
actions: {
|
|
1031
|
+
// Only service-specific actions here
|
|
1032
|
+
},
|
|
1033
|
+
});
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
5. **Add runtime validation** (optional) - For critical services, add runtime checks:
|
|
1037
|
+
|
|
1038
|
+
```typescript
|
|
1039
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
1040
|
+
super(broker);
|
|
1041
|
+
this.parseServiceSchema({...});
|
|
1042
|
+
|
|
1043
|
+
// Runtime verification (development only)
|
|
1044
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1045
|
+
const requiredActions: (keyof RequiredAccountActions)[] = [
|
|
1046
|
+
'createAccount', 'updateAccount', // ...list all methods
|
|
1047
|
+
];
|
|
1048
|
+
requiredActions.forEach(method => {
|
|
1049
|
+
if (!this.schema.actions[AccountServiceAction[method]]) {
|
|
1050
|
+
console.warn(`Missing action handler for ${method}`);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Benefits**:
|
|
1058
|
+
|
|
1059
|
+
- **Immediate feedback**: TypeScript compiler fails when interface changes but \_typeCheck doesn't
|
|
1060
|
+
- **Prevents runtime errors**: Catches missing methods at compile time (when used as checklist)
|
|
1061
|
+
- **Self-documenting**: Lists all service methods in one place
|
|
1062
|
+
- **Refactoring safety**: Renaming/removing methods triggers errors automatically
|
|
1063
|
+
|
|
1064
|
+
**Example Error Messages**:
|
|
1065
|
+
|
|
1066
|
+
```
|
|
1067
|
+
Type '{ ... }' is missing the following properties from type 'Record<...>': newMethod, anotherMethod
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
This error means you added `newMethod` and `anotherMethod` to the interface but forgot to add them to `_typeCheck`.
|
|
1071
|
+
|
|
1072
|
+
#### ✅ DO:
|
|
1073
|
+
|
|
1074
|
+
1. **Create an action for EVERY service method** (except `dispose()`):
|
|
1075
|
+
|
|
1076
|
+
```typescript
|
|
1077
|
+
[[ServiceName]ServiceAction.MethodName]: {
|
|
1078
|
+
params: { /* validation schema */ },
|
|
1079
|
+
handler: async (ctx: Context<TypedParams>) => service.methodName(ctx.params.arg1, ctx.params.arg2),
|
|
1080
|
+
},
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
2. **Add all method names to \_typeCheck constant**:
|
|
1084
|
+
|
|
1085
|
+
```typescript
|
|
1086
|
+
const _typeCheck: Record<keyof RequiredAccountActions, boolean> = {
|
|
1087
|
+
createDefaultAccount: true,
|
|
1088
|
+
createUserToken: true,
|
|
1089
|
+
updateUserAccount: true,
|
|
1090
|
+
// ... all other methods
|
|
1091
|
+
};
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
3. **Always use strongly typed Context**:
|
|
1095
|
+
4. **Always use strongly typed Context**:
|
|
1096
|
+
|
|
1097
|
+
```typescript
|
|
1098
|
+
handler: async (ctx: Context<{ id: string; input: IUpdateInput }>) =>
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
5. **Validate parameters using Moleculer params schema**:
|
|
1102
|
+
|
|
1103
|
+
```typescript
|
|
1104
|
+
params: {
|
|
1105
|
+
id: 'string', // Required string
|
|
1106
|
+
name: { type: 'string', optional: true }, // Optional string
|
|
1107
|
+
items: 'array', // Required array
|
|
1108
|
+
data: 'object', // Required object
|
|
1109
|
+
count: 'number', // Required number
|
|
1110
|
+
active: 'boolean', // Required boolean
|
|
1111
|
+
},
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
6. **Extract all parameters from ctx.params**:
|
|
1115
|
+
|
|
1116
|
+
```typescript
|
|
1117
|
+
handler: async (ctx: Context<{ a: string; b: number; c?: boolean }>) =>
|
|
1118
|
+
service.method(ctx.params.a, ctx.params.b, ctx.params.c),
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
7. **Import ALL required types** from 'common/server'
|
|
1122
|
+
|
|
1123
|
+
8. **Group actions by operation type** with comments
|
|
1124
|
+
|
|
1125
|
+
#### ❌ DON'T:
|
|
1126
|
+
|
|
1127
|
+
1. **Don't add business logic in handlers**
|
|
1128
|
+
2. **Don't transform data** (that's the service's job)
|
|
1129
|
+
3. **Don't use `any` types** - use proper interfaces
|
|
1130
|
+
4. **Don't forget optional parameter handling**
|
|
1131
|
+
5. **Don't skip parameter validation schemas**
|
|
1132
|
+
|
|
1133
|
+
### Special Cases for Moleculer Services
|
|
1134
|
+
|
|
1135
|
+
**Synchronous methods**:
|
|
1136
|
+
|
|
1137
|
+
```typescript
|
|
1138
|
+
[[ServiceName]ServiceAction.SyncMethod]: {
|
|
1139
|
+
params: { email: 'string', secret: 'string' },
|
|
1140
|
+
handler: (ctx: Context<{ email: string; secret: string }>) =>
|
|
1141
|
+
service.syncMethod(ctx.params.email, ctx.params.secret), // No async
|
|
1142
|
+
},
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
**Methods with complex nested objects**:
|
|
1146
|
+
|
|
1147
|
+
```typescript
|
|
1148
|
+
[[ServiceName]ServiceAction.ComplexParams]: {
|
|
1149
|
+
params: { args: 'object' },
|
|
1150
|
+
handler: async (ctx: Context<{
|
|
1151
|
+
args: {
|
|
1152
|
+
details: IDetailsInput;
|
|
1153
|
+
metadata: IMetadata;
|
|
1154
|
+
options?: IOptions;
|
|
1155
|
+
};
|
|
1156
|
+
}>) => service.complexMethod(ctx.params.args),
|
|
1157
|
+
},
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
**Methods with union types or optional chaining**:
|
|
1161
|
+
|
|
1162
|
+
```typescript
|
|
1163
|
+
[[ServiceName]ServiceAction.WithOptions]: {
|
|
1164
|
+
params: {
|
|
1165
|
+
id: 'string',
|
|
1166
|
+
options: { type: 'object', optional: true },
|
|
1167
|
+
},
|
|
1168
|
+
handler: async (ctx: Context<{ id: string; options?: IOptions }>) =>
|
|
1169
|
+
service.method(ctx.params.id, ctx.params.options),
|
|
1170
|
+
},
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
### Task 3: Create Proxy Service
|
|
1174
|
+
|
|
1175
|
+
**STRICT FILE NAMING CONVENTION:**
|
|
1176
|
+
|
|
1177
|
+
✅ **CORRECT**: Use PascalCase matching the class name
|
|
1178
|
+
|
|
1179
|
+
- File: `AccountProxyService.ts` → Class: `AccountProxyService`
|
|
1180
|
+
- File: `ProjectProxyService.ts` → Class: `ProjectProxyService`
|
|
1181
|
+
- File: `TeamProxyService.ts` → Class: `TeamProxyService`
|
|
1182
|
+
- File: `PropertyProxyService.ts` → Class: `PropertyProxyService`
|
|
1183
|
+
|
|
1184
|
+
❌ **WRONG**: Do NOT use kebab-case or other variations
|
|
1185
|
+
|
|
1186
|
+
- ❌ `account-proxy-service.ts` (kebab-case)
|
|
1187
|
+
- ❌ `property-proxy-service.ts` (kebab-case)
|
|
1188
|
+
- ❌ `AccountProxy.ts` (missing Service)
|
|
1189
|
+
- ❌ `AccountMicroservice.ts` (wrong suffix - use ProxyService)
|
|
1190
|
+
- ❌ `account_proxy_service.ts` (snake-case)
|
|
1191
|
+
|
|
1192
|
+
**File Location Patterns:**
|
|
1193
|
+
|
|
1194
|
+
Pattern 1 (Core module - MOST COMMON):
|
|
1195
|
+
`packages-modules/[module-name]/core/src/server-context/proxy-services/[ServiceName]ProxyService.ts`
|
|
1196
|
+
|
|
1197
|
+
Examples:
|
|
1198
|
+
|
|
1199
|
+
- `packages-modules/property/core/src/server-context/proxy-services/PropertyProxyService.ts`
|
|
1200
|
+
- `packages-modules/billing-api/core/src/server-context/proxy-services/InvoiceProxyService.ts`
|
|
1201
|
+
- `packages-modules/calendar-events/core/src/server-context/proxy-services/CalendarEventProxyService.ts`
|
|
1202
|
+
|
|
1203
|
+
Pattern 2 (Servers module - LEGACY):
|
|
1204
|
+
`packages-modules/[module-name]/core/src/servers/services/[ServiceName]ProxyService.ts`
|
|
1205
|
+
|
|
1206
|
+
Examples:
|
|
1207
|
+
|
|
1208
|
+
- `packages-modules/account-api/core/src/servers/services/AccountProxyService.ts`
|
|
1209
|
+
- `packages-modules/account-api/core/src/servers/services/ProjectProxyService.ts`
|
|
1210
|
+
|
|
1211
|
+
**Directory Structure:**
|
|
1212
|
+
|
|
1213
|
+
```
|
|
1214
|
+
packages-modules/
|
|
1215
|
+
property/ # Module folder
|
|
1216
|
+
core/ # Core package (client-side code)
|
|
1217
|
+
src/
|
|
1218
|
+
server-context/ # Server context (NEW STANDARD)
|
|
1219
|
+
proxy-services/ # Proxy services folder
|
|
1220
|
+
PropertyProxyService.ts # ✅ Proxy service here
|
|
1221
|
+
index.ts # Export from this folder
|
|
1222
|
+
server/ # Server package (server-side code)
|
|
1223
|
+
src/
|
|
1224
|
+
plugins/ # Moleculer services folder
|
|
1225
|
+
PropertyMoleculerService.ts # Moleculer service here
|
|
1226
|
+
services/ # Business logic services
|
|
1227
|
+
property-service.ts # Actual service implementation
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
**Directory Naming Rules:**
|
|
1231
|
+
|
|
1232
|
+
1. ✅ Use `server-context/proxy-services/` for NEW proxy services (preferred)
|
|
1233
|
+
2. ⚠️ `servers/services/` is LEGACY pattern (still valid but being migrated)
|
|
1234
|
+
3. ✅ Always plural: `proxy-services/` not `proxy-service/`
|
|
1235
|
+
4. ✅ Always kebab-case for directories: `server-context/` not `serverContext/`
|
|
1236
|
+
5. ✅ Always PascalCase for files: `PropertyProxyService.ts`
|
|
1237
|
+
6. ❌ Never mix cases: `ProxyServices/` or `proxy_services/` are wrong
|
|
1238
|
+
|
|
1239
|
+
**File Naming Rules:**
|
|
1240
|
+
|
|
1241
|
+
1. ✅ File name MUST match class name exactly
|
|
1242
|
+
2. ✅ Use PascalCase (first letter uppercase)
|
|
1243
|
+
3. ✅ Always end with `ProxyService.ts`
|
|
1244
|
+
4. ✅ Service name comes first: `[ServiceName]ProxyService`
|
|
1245
|
+
5. ❌ Never use kebab-case (dashes)
|
|
1246
|
+
6. ❌ Never use snake-case (underscores)
|
|
1247
|
+
7. ❌ Never use `Microservice` suffix (legacy naming)
|
|
1248
|
+
8. ❌ Never use plural form (e.g., `PropertiesProxyService` is wrong)
|
|
1249
|
+
|
|
1250
|
+
**Index File Pattern:**
|
|
1251
|
+
|
|
1252
|
+
Create an `index.ts` in the proxy-services folder to export all proxies:
|
|
1253
|
+
|
|
1254
|
+
```typescript
|
|
1255
|
+
// packages-modules/property/core/src/server-context/proxy-services/index.ts
|
|
1256
|
+
export * from './PropertyProxyService';
|
|
1257
|
+
export * from './PropertyTypeProxyService';
|
|
1258
|
+
// ... other proxy services
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
**Migration Guide:**
|
|
1262
|
+
|
|
1263
|
+
If you find proxy services in the OLD location (`servers/services/`):
|
|
1264
|
+
|
|
1265
|
+
1. Create new directory structure: `server-context/proxy-services/`
|
|
1266
|
+
2. Move proxy service files to new location
|
|
1267
|
+
3. Update imports in container modules
|
|
1268
|
+
4. Update index.ts exports
|
|
1269
|
+
5. Keep file names EXACTLY the same (PascalCase)
|
|
1270
|
+
|
|
1271
|
+
---
|
|
1272
|
+
|
|
1273
|
+
## 🎯 MANDATORY APPROACH: Auto-Generation with Declaration Merging
|
|
1274
|
+
|
|
1275
|
+
**⚠️ STRICT REQUIREMENT**: ALL new proxy services MUST use this exact pattern with declaration merging.
|
|
1276
|
+
|
|
1277
|
+
**DO NOT use:**
|
|
1278
|
+
|
|
1279
|
+
- ❌ `implements IService` - causes TypeScript errors
|
|
1280
|
+
- ❌ `extends BaseProxyService2` - legacy pattern, deprecated
|
|
1281
|
+
- ❌ Manual method definitions - defeats auto-generation
|
|
1282
|
+
- ❌ Index signatures `[key: string]: any` - unsafe hack
|
|
1283
|
+
- ❌ Manual property declarations (`topic`, `broker`, `callAction`)
|
|
1284
|
+
|
|
1285
|
+
**REQUIRED Pattern - Copy Exactly:**
|
|
1286
|
+
|
|
1287
|
+
```typescript
|
|
1288
|
+
import { injectable, inject } from 'inversify';
|
|
1289
|
+
import { ServiceBroker } from 'moleculer';
|
|
1290
|
+
import type { CdmLogger } from '@cdm-logger/core';
|
|
1291
|
+
import { CommonType } from '@common-stack/core';
|
|
1292
|
+
import { generateProxyMethods } from '@common-stack/codegen-zod';
|
|
1293
|
+
import type { I[ServiceName]Service } from 'common/server';
|
|
1294
|
+
import { [ServiceName]ServiceSchemas } from 'common/server';
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* Client-side proxy for [ServiceName] operations using AUTO-GENERATION.
|
|
1298
|
+
*
|
|
1299
|
+
* 🎉 FULLY AUTOMATIC: All I[ServiceName]Service methods auto-generated!
|
|
1300
|
+
*
|
|
1301
|
+
* ✅ ZERO BOILERPLATE: No manual method definitions
|
|
1302
|
+
* ✅ TYPE-SAFE: Full TypeScript inference from I[ServiceName]Service
|
|
1303
|
+
* ✅ VALIDATED: Zod schemas for parameter validation
|
|
1304
|
+
* ✅ MAINTAINABLE: Add method to interface → automatically available as proxy
|
|
1305
|
+
*/
|
|
1306
|
+
@injectable()
|
|
1307
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1308
|
+
export class [ServiceName]ProxyService {
|
|
1309
|
+
constructor(
|
|
1310
|
+
@inject(CommonType.MOLECULER_BROKER)
|
|
1311
|
+
broker: ServiceBroker,
|
|
1312
|
+
@inject(CommonType.LOGGER)
|
|
1313
|
+
logger: CdmLogger.ILogger,
|
|
1314
|
+
) {
|
|
1315
|
+
const childLogger = logger.child({ className: '[ServiceName]ProxyService' });
|
|
1316
|
+
|
|
1317
|
+
// ⚡ ONE LINE: Auto-generate all proxy methods
|
|
1318
|
+
generateProxyMethods<I[ServiceName]Service>(this, [ServiceName]ServiceSchemas, broker, childLogger);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// Declaration merging for type safety - provides full interface implementation
|
|
1323
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1324
|
+
export interface [ServiceName]ProxyService extends I[ServiceName]Service {}
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
**CRITICAL RULES - DO NOT DEVIATE:**
|
|
1328
|
+
|
|
1329
|
+
1. ✅ **MUST use declaration merging** - separate class and interface with same name
|
|
1330
|
+
2. ✅ **MUST have both eslint-disable comments** for `no-unsafe-declaration-merging`
|
|
1331
|
+
3. ✅ **MUST import service schemas** - `[ServiceName]ServiceSchemas` (not as type)
|
|
1332
|
+
4. ✅ **MUST import service interface as type** - `import type { I[ServiceName]Service }`
|
|
1333
|
+
5. ✅ **MUST use `generateProxyMethods()`** - single call in constructor
|
|
1334
|
+
6. ✅ **MUST create child logger** - for proper logging context
|
|
1335
|
+
7. ✅ **NO manual method definitions** - all methods come from generateProxyMethods
|
|
1336
|
+
8. ✅ **NO implements keyword** - declaration merging handles this
|
|
1337
|
+
9. ✅ **NO extends BaseProxyService2** - deprecated pattern
|
|
1338
|
+
10. ✅ **NO manual property declarations** - generateProxyMethods adds them
|
|
1339
|
+
|
|
1340
|
+
**Template Validation Checklist:**
|
|
1341
|
+
|
|
1342
|
+
Before submitting generated code, verify:
|
|
1343
|
+
|
|
1344
|
+
- [ ] Class name ends with `ProxyService`
|
|
1345
|
+
- [ ] Has `@injectable()` decorator
|
|
1346
|
+
- [ ] Has declaration merging comment: `// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging`
|
|
1347
|
+
- [ ] Constructor has exactly 2 parameters: `broker` and `logger`
|
|
1348
|
+
- [ ] Creates child logger with className
|
|
1349
|
+
- [ ] Single `generateProxyMethods()` call with 4 arguments
|
|
1350
|
+
- [ ] Interface declaration after class with same name
|
|
1351
|
+
- [ ] Interface extends service interface
|
|
1352
|
+
- [ ] No method definitions in class body
|
|
1353
|
+
- [ ] No property declarations in class body
|
|
1354
|
+
- [ ] Total file length: ~40 lines
|
|
1355
|
+
|
|
1356
|
+
**Example Output:**
|
|
1357
|
+
|
|
1358
|
+
```typescript
|
|
1359
|
+
// ✅ CORRECT - Exact pattern to follow
|
|
1360
|
+
@injectable()
|
|
1361
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1362
|
+
export class AccountProxyService {
|
|
1363
|
+
constructor(
|
|
1364
|
+
@inject(CommonType.MOLECULER_BROKER)
|
|
1365
|
+
broker: ServiceBroker,
|
|
1366
|
+
@inject(CommonType.LOGGER)
|
|
1367
|
+
logger: CdmLogger.ILogger,
|
|
1368
|
+
) {
|
|
1369
|
+
const childLogger = logger.child({ className: 'AccountProxyService' });
|
|
1370
|
+
generateProxyMethods<IAccountService>(this, AccountServiceSchemas, broker, childLogger);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1375
|
+
export interface AccountProxyService extends IAccountService {}
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
**Common Mistakes to Avoid:**
|
|
1379
|
+
|
|
1380
|
+
```typescript
|
|
1381
|
+
// ❌ WRONG - Using implements
|
|
1382
|
+
export class ProxyService implements IService {
|
|
1383
|
+
[key: string]: any; // Unsafe hack!
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// ❌ WRONG - Extending BaseProxyService2
|
|
1387
|
+
export class ProxyService extends BaseProxyService2<IModel> implements IService {
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// ❌ WRONG - Manual method definitions
|
|
1391
|
+
export class ProxyService {
|
|
1392
|
+
getItem(id: string): Promise<Item> {
|
|
1393
|
+
return this.callAction(...); // Defeats auto-generation!
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// ❌ WRONG - Manual property declarations
|
|
1398
|
+
export class ProxyService {
|
|
1399
|
+
protected topic!: string; // Don't declare manually!
|
|
1400
|
+
protected broker!: ServiceBroker;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// ❌ WRONG - Missing declaration merging
|
|
1404
|
+
export class ProxyService {
|
|
1405
|
+
// Class only, no interface
|
|
1406
|
+
}
|
|
1407
|
+
```
|
|
1408
|
+
|
|
1409
|
+
**Why Declaration Merging:**
|
|
1410
|
+
|
|
1411
|
+
1. TypeScript merges class and interface into single type
|
|
1412
|
+
2. No need for `implements` - interface provides the type
|
|
1413
|
+
3. No TypeScript errors about missing methods
|
|
1414
|
+
4. Full IntelliSense support
|
|
1415
|
+
5. Runtime methods added by `generateProxyMethods()`
|
|
1416
|
+
6. Clean, minimal code
|
|
1417
|
+
|
|
1418
|
+
---
|
|
1419
|
+
|
|
1420
|
+
## 📋 Manual Proxy Service Template (DEPRECATED - DO NOT USE)
|
|
1421
|
+
|
|
1422
|
+
**⚠️ DEPRECATED**: This pattern is no longer supported. Use auto-generation with declaration merging instead.
|
|
1423
|
+
|
|
1424
|
+
**Only mentioned for reference when maintaining legacy code.**
|
|
1425
|
+
|
|
1426
|
+
**Template Structure (LEGACY - Use Auto-Generation Instead)**:
|
|
1427
|
+
|
|
1428
|
+
```typescript
|
|
1429
|
+
import { inject, injectable } from 'inversify';
|
|
1430
|
+
import { ServiceBroker } from 'moleculer';
|
|
1431
|
+
import { CommonType } from '@common-stack/core';
|
|
1432
|
+
import type { CdmLogger } from '@cdm-logger/core';
|
|
1433
|
+
import { BaseProxyService2 } from '@common-stack/store-mongo';
|
|
1434
|
+
import {
|
|
1435
|
+
[ServiceName]ServiceAction,
|
|
1436
|
+
MoleculerServiceName,
|
|
1437
|
+
I[ServiceName]Service,
|
|
1438
|
+
AsDomainType,
|
|
1439
|
+
I[ServiceName]Model,
|
|
1440
|
+
// Import other types as needed
|
|
1441
|
+
} from 'common/server';
|
|
1442
|
+
|
|
1443
|
+
/**
|
|
1444
|
+
* Proxy Service for [ServiceName] operations.
|
|
1445
|
+
* Delegates all operations to [ServiceName]Service through Moleculer broker.
|
|
1446
|
+
*
|
|
1447
|
+
* Pattern:
|
|
1448
|
+
* - Extends BaseProxyService2 which provides callAction method
|
|
1449
|
+
* - Implements I[ServiceName]Service interface
|
|
1450
|
+
* - Each method calls this.callAction with appropriate action enum and parameters
|
|
1451
|
+
* - No business logic - pure delegation
|
|
1452
|
+
*
|
|
1453
|
+
* @see [ServiceName]Service for actual implementation
|
|
1454
|
+
* @see BaseProxyService2 for base proxy functionality
|
|
1455
|
+
*/
|
|
1456
|
+
@injectable()
|
|
1457
|
+
export class [ServiceName]ProxyService
|
|
1458
|
+
extends BaseProxyService2<I[ServiceName]Model>
|
|
1459
|
+
implements I[ServiceName]Service
|
|
1460
|
+
{
|
|
1461
|
+
protected logger?: CdmLogger.ILogger;
|
|
1462
|
+
|
|
1463
|
+
// ⚠️ CRITICAL: Set the Moleculer service name - REQUIRED for proxy to work
|
|
1464
|
+
protected topic = MoleculerServiceName.[ServiceName];
|
|
1465
|
+
|
|
1466
|
+
constructor(
|
|
1467
|
+
@inject(CommonType.MOLECULER_BROKER)
|
|
1468
|
+
broker: ServiceBroker,
|
|
1469
|
+
@inject(CommonType.LOGGER)
|
|
1470
|
+
logger: CdmLogger.ILogger,
|
|
1471
|
+
) {
|
|
1472
|
+
super(broker, logger);
|
|
1473
|
+
this.logger = logger.child({ className: '[ServiceName]ProxyService' });
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// ============================================================================
|
|
1477
|
+
// READ OPERATIONS
|
|
1478
|
+
// ============================================================================
|
|
1479
|
+
|
|
1480
|
+
methodName(param: Type): Promise<ReturnType> {
|
|
1481
|
+
return this.callAction([ServiceName]ServiceAction.MethodName, { param });
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// ============================================================================
|
|
1485
|
+
// CREATE OPERATIONS
|
|
1486
|
+
// ============================================================================
|
|
1487
|
+
|
|
1488
|
+
// ... implement create methods
|
|
1489
|
+
|
|
1490
|
+
// ============================================================================
|
|
1491
|
+
// UPDATE OPERATIONS
|
|
1492
|
+
// ============================================================================
|
|
1493
|
+
|
|
1494
|
+
// ... implement update methods
|
|
1495
|
+
|
|
1496
|
+
// ============================================================================
|
|
1497
|
+
// DELETE OPERATIONS
|
|
1498
|
+
// ============================================================================
|
|
1499
|
+
|
|
1500
|
+
// ... implement delete methods
|
|
1501
|
+
|
|
1502
|
+
// ============================================================================
|
|
1503
|
+
// LIFECYCLE METHODS
|
|
1504
|
+
// ============================================================================
|
|
1505
|
+
|
|
1506
|
+
dispose(): void {
|
|
1507
|
+
// Dispose is a lifecycle method, not proxied
|
|
1508
|
+
// No-op for proxy service
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
```
|
|
1512
|
+
|
|
1513
|
+
### Implementation Rules
|
|
1514
|
+
|
|
1515
|
+
#### ✅ DO:
|
|
1516
|
+
|
|
1517
|
+
1. **For each interface method**, create a corresponding proxy method:
|
|
1518
|
+
|
|
1519
|
+
```typescript
|
|
1520
|
+
interfaceMethod(param1: Type1, param2?: Type2): Promise<ReturnType> {
|
|
1521
|
+
return this.callAction(ServiceAction.InterfaceMethod, { param1, param2 });
|
|
1522
|
+
}
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
2. **Pass ALL parameters as a single object**:
|
|
1526
|
+
|
|
1527
|
+
```typescript
|
|
1528
|
+
updateItem(id: string, data: UpdateInput, userId: string): Promise<Item> {
|
|
1529
|
+
return this.callAction(ServiceAction.UpdateItem, { id, data, userId });
|
|
1530
|
+
}
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1533
|
+
3. **Include optional parameters** in the params object:
|
|
1534
|
+
|
|
1535
|
+
```typescript
|
|
1536
|
+
getItems(filter?: Filter, limit?: number): Promise<Item[]> {
|
|
1537
|
+
return this.callAction(ServiceAction.GetItems, { filter, limit });
|
|
1538
|
+
}
|
|
1539
|
+
```
|
|
1540
|
+
|
|
1541
|
+
4. **Group methods by operation type** with clear section comments
|
|
1542
|
+
|
|
1543
|
+
5. **Use section dividers** (80-char lines of =)
|
|
1544
|
+
|
|
1545
|
+
6. **Match parameter names exactly** from the interface
|
|
1546
|
+
|
|
1547
|
+
7. **Handle void returns** - still use callAction:
|
|
1548
|
+
```typescript
|
|
1549
|
+
notifyUser(userId: string, message: string): Promise<void> {
|
|
1550
|
+
return this.callAction(ServiceAction.NotifyUser, { userId, message });
|
|
1551
|
+
}
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
#### ❌ DON'T:
|
|
1555
|
+
|
|
1556
|
+
1. **Don't add ANY business logic**:
|
|
1557
|
+
|
|
1558
|
+
```typescript
|
|
1559
|
+
// ❌ BAD
|
|
1560
|
+
createItem(data: Input): Promise<Item> {
|
|
1561
|
+
if (!data.name) throw new Error('Name required');
|
|
1562
|
+
return this.callAction(ServiceAction.CreateItem, { data });
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// ✅ GOOD
|
|
1566
|
+
createItem(data: Input): Promise<Item> {
|
|
1567
|
+
return this.callAction(ServiceAction.CreateItem, { data });
|
|
1568
|
+
}
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
2. **Don't transform or validate data**
|
|
1572
|
+
|
|
1573
|
+
3. **Don't add logging** (already in callAction)
|
|
1574
|
+
|
|
1575
|
+
4. **Don't catch errors** (unless re-throwing)
|
|
1576
|
+
|
|
1577
|
+
5. **Don't add async/await** (return Promise directly):
|
|
1578
|
+
|
|
1579
|
+
```typescript
|
|
1580
|
+
// ❌ BAD
|
|
1581
|
+
async getItem(id: string): Promise<Item> {
|
|
1582
|
+
return await this.callAction(ServiceAction.GetItem, { id });
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// ✅ GOOD
|
|
1586
|
+
getItem(id: string): Promise<Item> {
|
|
1587
|
+
return this.callAction(ServiceAction.GetItem, { id });
|
|
1588
|
+
}
|
|
1589
|
+
```
|
|
1590
|
+
|
|
1591
|
+
### Special Cases
|
|
1592
|
+
|
|
1593
|
+
**Synchronous methods** that can't be proxied:
|
|
1594
|
+
|
|
1595
|
+
```typescript
|
|
1596
|
+
createToken(_param1: string, _param2: string): string {
|
|
1597
|
+
throw new Error('createToken should be called directly on [ServiceName]Service, not via proxy.');
|
|
1598
|
+
}
|
|
1599
|
+
```
|
|
1600
|
+
|
|
1601
|
+
**Methods with Types.ObjectId parameters**:
|
|
1602
|
+
|
|
1603
|
+
```typescript
|
|
1604
|
+
acceptInvitation(id: string | Types.ObjectId, user: User): Promise<boolean> {
|
|
1605
|
+
return this.callAction(ServiceAction.AcceptInvitation, { id, user });
|
|
1606
|
+
}
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
**Methods with complex union types** - pass as-is
|
|
1610
|
+
|
|
1611
|
+
### Task 4: Update Container Module
|
|
1612
|
+
|
|
1613
|
+
Update the DI container module to register the proxy service:
|
|
1614
|
+
`packages-modules/[module-name]/server/src/modules/[service]/containers/module.ts`
|
|
1615
|
+
|
|
1616
|
+
**Pattern for Proxy Module**:
|
|
1617
|
+
|
|
1618
|
+
```typescript
|
|
1619
|
+
import { ContainerModule, interfaces } from 'inversify';
|
|
1620
|
+
import { SERVER_TYPES, I[ServiceName]Service } from 'common/server';
|
|
1621
|
+
import { [ServiceName]ProxyService } from '../services';
|
|
1622
|
+
|
|
1623
|
+
export const [ServiceName]ProxyModule: (settings: any) => ContainerModule = (setting) =>
|
|
1624
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
1625
|
+
// Bind proxy service to interface
|
|
1626
|
+
bind<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service)
|
|
1627
|
+
.to([ServiceName]ProxyService)
|
|
1628
|
+
.inSingletonScope();
|
|
1629
|
+
});
|
|
1630
|
+
```
|
|
1631
|
+
|
|
1632
|
+
**Complete module file example**:
|
|
1633
|
+
|
|
1634
|
+
```typescript
|
|
1635
|
+
import { ContainerModule, interfaces } from 'inversify';
|
|
1636
|
+
import {
|
|
1637
|
+
I[ServiceName]Repository,
|
|
1638
|
+
SERVER_TYPES,
|
|
1639
|
+
I[ServiceName]Service,
|
|
1640
|
+
} from 'common/server';
|
|
1641
|
+
import { [ServiceName]Repository } from '../store/repositories';
|
|
1642
|
+
import {
|
|
1643
|
+
[ServiceName]ProxyService,
|
|
1644
|
+
[ServiceName]Service,
|
|
1645
|
+
} from '../services';
|
|
1646
|
+
|
|
1647
|
+
// Database Module (registers actual service + repository)
|
|
1648
|
+
export const [ServiceName]DbModule: (settings: any) => ContainerModule = (setting) =>
|
|
1649
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
1650
|
+
bind<I[ServiceName]Repository>(SERVER_TYPES.I[ServiceName]Repository)
|
|
1651
|
+
.to([ServiceName]Repository)
|
|
1652
|
+
.inSingletonScope();
|
|
1653
|
+
|
|
1654
|
+
bind<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service)
|
|
1655
|
+
.to([ServiceName]Service)
|
|
1656
|
+
.inSingletonScope();
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
// Proxy Module (registers proxy service only)
|
|
1660
|
+
export const [ServiceName]ProxyModule: (settings: any) => ContainerModule = (setting) =>
|
|
1661
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
1662
|
+
bind<I[ServiceName]Service>(SERVER_TYPES.I[ServiceName]Service)
|
|
1663
|
+
.to([ServiceName]ProxyService)
|
|
1664
|
+
.inSingletonScope();
|
|
1665
|
+
});
|
|
1666
|
+
```
|
|
1667
|
+
|
|
1668
|
+
**Rules for container modules**:
|
|
1669
|
+
|
|
1670
|
+
- **DbModule**: Binds actual service + repository (used by server that runs business logic)
|
|
1671
|
+
- **ProxyModule**: Binds proxy service only (used by client services that call via Moleculer)
|
|
1672
|
+
- Use same `SERVER_TYPES.I[ServiceName]Service` symbol for both
|
|
1673
|
+
- Both use `.inSingletonScope()` for performance
|
|
1674
|
+
- Keep consistent naming: `[ServiceName]DbModule` and `[ServiceName]ProxyModule`
|
|
1675
|
+
|
|
1676
|
+
### Output Requirements
|
|
1677
|
+
|
|
1678
|
+
1. **Complete GraphQL enum** with all actions
|
|
1679
|
+
2. **Complete Moleculer service** file with:
|
|
1680
|
+
- All imports (including Service, ServiceBroker, Context from 'moleculer')
|
|
1681
|
+
- Container injection for service instance
|
|
1682
|
+
- All actions mapped to service methods
|
|
1683
|
+
- Proper parameter validation schemas
|
|
1684
|
+
- Strongly typed Context handlers
|
|
1685
|
+
3. **Complete proxy service** file with:
|
|
1686
|
+
- All imports
|
|
1687
|
+
- Proper class structure
|
|
1688
|
+
- All interface methods implemented
|
|
1689
|
+
- Organized by operation type
|
|
1690
|
+
- Section comments
|
|
1691
|
+
- JSDoc on class
|
|
1692
|
+
4. **Updated container module** with proxy registration
|
|
1693
|
+
5. **Instructions**: "After creating these files, run: `yarn generateGraphql && yarn build`"
|
|
1694
|
+
|
|
1695
|
+
### Quality Checklist
|
|
1696
|
+
|
|
1697
|
+
Before outputting, verify:
|
|
1698
|
+
|
|
1699
|
+
#### Moleculer Service (Auto-Generation):
|
|
1700
|
+
|
|
1701
|
+
- [ ] Uses `Moleculer.generateActionsAndEvents<IService>(service)` or `Moleculer.generateAutoInferredActions<IService>(service)`
|
|
1702
|
+
- [ ] Service instance retrieved from DI container
|
|
1703
|
+
- [ ] Correct `name` set to MoleculerServiceName enum value
|
|
1704
|
+
- [ ] Actions and events destructured from generation function
|
|
1705
|
+
- [ ] Optional: paramOverrides specified for methods needing custom validation
|
|
1706
|
+
- [ ] Optional: exclude specified for manually handled methods
|
|
1707
|
+
- [ ] No manual action definitions (unless override needed)
|
|
1708
|
+
- [ ] Event handlers use `@Moleculer.EventHandler` decorator in service class
|
|
1709
|
+
|
|
1710
|
+
#### Moleculer Service (Manual - Only if needed):
|
|
1711
|
+
|
|
1712
|
+
- [ ] All interface methods have corresponding actions
|
|
1713
|
+
- [ ] All actions use strongly typed `Context<TypedParams>`
|
|
1714
|
+
- [ ] Parameter validation schemas are complete
|
|
1715
|
+
- [ ] All parameters extracted from `ctx.params`
|
|
1716
|
+
- [ ] No `any` types used
|
|
1717
|
+
- [ ] Actions grouped by operation type
|
|
1718
|
+
- [ ] Correct `name` set to MoleculerServiceName
|
|
1719
|
+
- [ ] Service instance retrieved from DI container
|
|
1720
|
+
|
|
1721
|
+
#### Proxy Service (Auto-Generation - RECOMMENDED):
|
|
1722
|
+
|
|
1723
|
+
- [ ] Uses `generateProxyMethods<IService>(this, ServiceSchemas, broker, logger)`
|
|
1724
|
+
- [ ] Imports from `@common-stack/server-core`
|
|
1725
|
+
- [ ] Service schemas imported (e.g., `ProjectServiceSchemas`)
|
|
1726
|
+
- [ ] Declaration merging used: `export interface ProxyService extends IService {}`
|
|
1727
|
+
- [ ] Both class and interface have eslint-disable for unsafe-declaration-merging
|
|
1728
|
+
- [ ] No manual method definitions
|
|
1729
|
+
- [ ] No `topic` property needed (derived automatically)
|
|
1730
|
+
- [ ] Class is injectable with proper DI
|
|
1731
|
+
- [ ] Logger passed to generateProxyMethods
|
|
1732
|
+
|
|
1733
|
+
#### Proxy Service (Manual - Legacy):
|
|
1734
|
+
|
|
1735
|
+
- [ ] All interface methods have corresponding proxy methods
|
|
1736
|
+
- [ ] All proxy methods use `this.callAction()`
|
|
1737
|
+
- [ ] All parameters passed as single object
|
|
1738
|
+
- [ ] Parameter names match interface exactly
|
|
1739
|
+
- [ ] Methods grouped by operation type
|
|
1740
|
+
- [ ] No business logic added
|
|
1741
|
+
- [ ] Lifecycle methods (dispose) are no-ops
|
|
1742
|
+
- [ ] ⚠️ **CRITICAL**: `topic` property set to MoleculerServiceName (REQUIRED)
|
|
1743
|
+
- [ ] Extends BaseProxyService2<ModelType>
|
|
1744
|
+
- [ ] Implements IService interface
|
|
1745
|
+
|
|
1746
|
+
#### Container Module:
|
|
1747
|
+
|
|
1748
|
+
- [ ] Container module updated with proxy registration
|
|
1749
|
+
- [ ] Uses same SERVER_TYPES symbol as actual service
|
|
1750
|
+
|
|
1751
|
+
### Example Output
|
|
1752
|
+
|
|
1753
|
+
See these reference implementations:
|
|
1754
|
+
|
|
1755
|
+
**Moleculer Services**:
|
|
1756
|
+
|
|
1757
|
+
- **Simple (10-15 actions)**: `modules/project/plugins/ProjectMoleculerService.ts`
|
|
1758
|
+
- **Complex (40+ actions)**: `plugins/AccountMoleculerService.ts`
|
|
1759
|
+
|
|
1760
|
+
**Proxy Services**:
|
|
1761
|
+
|
|
1762
|
+
- **Simple (10-15 methods)**: `ProjectProxyService.ts`
|
|
1763
|
+
- **Medium (15-20 methods)**: `TeamMicroservice.ts`
|
|
1764
|
+
- **Complex (40+ methods)**: `AccountProxyService.ts`
|
|
1765
|
+
|
|
1766
|
+
### Complete Example: Account Module
|
|
1767
|
+
|
|
1768
|
+
**1. Moleculer Service with Auto-Generation** (`plugins/AccountMoleculerService.ts`):
|
|
1769
|
+
|
|
1770
|
+
```typescript
|
|
1771
|
+
import { Context, Service, ServiceBroker } from 'moleculer';
|
|
1772
|
+
import { Container } from 'inversify';
|
|
1773
|
+
import { Moleculer } from '@common-stack/codegen-zod';
|
|
1774
|
+
import { AccountServiceAction, IAccountService, MoleculerServiceName, SERVER_TYPES } from 'common/server';
|
|
1775
|
+
|
|
1776
|
+
/**
|
|
1777
|
+
* Moleculer Service for Account operations using AUTO-GENERATION.
|
|
1778
|
+
*
|
|
1779
|
+
* This service demonstrates the modern approach:
|
|
1780
|
+
* - Uses Moleculer.generateActionsAndEvents() for automatic action/event generation
|
|
1781
|
+
* - All service methods become actions automatically
|
|
1782
|
+
* - All @Moleculer.EventHandler decorated methods become event handlers
|
|
1783
|
+
* - No manual action definitions needed
|
|
1784
|
+
* - Parent class methods automatically included
|
|
1785
|
+
*
|
|
1786
|
+
* @see AccountService for business logic and event handlers
|
|
1787
|
+
* @see AccountProxyService for client-side proxy
|
|
1788
|
+
*/
|
|
1789
|
+
export class AccountMoleculerService extends Service {
|
|
1790
|
+
constructor(broker: ServiceBroker, { container }: { container: Container; settings?: unknown }) {
|
|
1791
|
+
super(broker);
|
|
1792
|
+
const topic = MoleculerServiceName.Account;
|
|
1793
|
+
const accountService = container.get<IAccountService>(SERVER_TYPES.IAccountService);
|
|
1794
|
+
|
|
1795
|
+
// ⚡ AUTO-GENERATE both actions and events
|
|
1796
|
+
const { actions, events } = Moleculer.generateActionsAndEvents<IAccountService>(accountService);
|
|
1797
|
+
|
|
1798
|
+
this.parseServiceSchema({
|
|
1799
|
+
name: topic,
|
|
1800
|
+
actions, // ✅ All 60+ methods auto-generated (including parent class methods)
|
|
1801
|
+
events, // ✅ All @Moleculer.EventHandler methods auto-generated
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
**Benefits of Auto-Generation:**
|
|
1808
|
+
|
|
1809
|
+
- **30 lines** instead of 200+ lines of manual action definitions
|
|
1810
|
+
- **Automatic inheritance**: All parent class methods included
|
|
1811
|
+
- **Type-safe**: Full TypeScript inference
|
|
1812
|
+
- **Maintainable**: Add method to service → automatically available as action
|
|
1813
|
+
- **Event integration**: Decorators handled automatically
|
|
1814
|
+
|
|
1815
|
+
**2. Service with Event Handlers** (showing what auto-generation detects):
|
|
1816
|
+
|
|
1817
|
+
```typescript
|
|
1818
|
+
import { injectable } from 'inversify';
|
|
1819
|
+
import { Moleculer } from '@common-stack/codegen-zod';
|
|
1820
|
+
import { UserBroadcasterAction, AuthStrategy } from 'common/server';
|
|
1821
|
+
|
|
1822
|
+
@injectable()
|
|
1823
|
+
export class AccountService extends BaseAccountService {
|
|
1824
|
+
// Regular method → becomes Moleculer action
|
|
1825
|
+
async findAccountById(id: string): Promise<IAccount> {
|
|
1826
|
+
return this.repository.findById(id);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
// Regular method → becomes Moleculer action
|
|
1830
|
+
async createAccount(account: IAccountInput): Promise<IAccount> {
|
|
1831
|
+
return this.repository.create(account);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// Decorated method → becomes Moleculer event handler
|
|
1835
|
+
@Moleculer.EventHandler(UserBroadcasterAction.OnUserCreated)
|
|
1836
|
+
async onUserCreated(event: IUserProfile & { authStrategy: AuthStrategy }): Promise<void> {
|
|
1837
|
+
await this.createDefaultAccount(event, { authStrategy: event.authStrategy });
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// Decorated method → becomes Moleculer event handler
|
|
1841
|
+
@Moleculer.EventHandler(UserBroadcasterAction.OnAccountLinked)
|
|
1842
|
+
async onAccountLinked(event: { email: string; aliases: string[] }): Promise<void> {
|
|
1843
|
+
await this.updateUserAccountAliases({
|
|
1844
|
+
email: event?.email,
|
|
1845
|
+
aliasesToAdd: event?.aliases,
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
```
|
|
1850
|
+
|
|
1851
|
+
**Result of Auto-Generation:**
|
|
1852
|
+
|
|
1853
|
+
- `findAccountById` → action: `AccountServiceAction.FindAccountById`
|
|
1854
|
+
- `createAccount` → action: `AccountServiceAction.CreateAccount`
|
|
1855
|
+
- `onUserCreated` → event: `UserBroadcasterAction.OnUserCreated` (excluded from actions)
|
|
1856
|
+
- `onAccountLinked` → event: `UserBroadcasterAction.OnAccountLinked` (excluded from actions)
|
|
1857
|
+
|
|
1858
|
+
**1. Moleculer Service (MANUAL - Legacy Pattern)** (`plugins/AccountMoleculerService.ts`):
|
|
1859
|
+
|
|
1860
|
+
```typescript
|
|
1861
|
+
import { Context, Service, ServiceBroker } from 'moleculer';
|
|
1862
|
+
import { Container } from 'inversify';
|
|
1863
|
+
import {
|
|
1864
|
+
AccountServiceAction,
|
|
1865
|
+
IAccountService,
|
|
1866
|
+
IPhoneNumberInput,
|
|
1867
|
+
IUserToken,
|
|
1868
|
+
SERVER_TYPES,
|
|
1869
|
+
TokenTypesEnum,
|
|
1870
|
+
} from 'common/server';
|
|
1871
|
+
|
|
1872
|
+
export class AccountMoleculerService extends Service {
|
|
1873
|
+
constructor(broker: ServiceBroker, { container }: { container: Container; settings?: unknown }) {
|
|
1874
|
+
super(broker);
|
|
1875
|
+
const accountService = container.get<IAccountService>(SERVER_TYPES.IAccountService);
|
|
1876
|
+
|
|
1877
|
+
this.parseServiceSchema({
|
|
1878
|
+
name: 'accountUser',
|
|
1879
|
+
actions: {
|
|
1880
|
+
// READ OPERATIONS
|
|
1881
|
+
[AccountServiceAction.FindAccountById]: {
|
|
1882
|
+
params: { id: 'string' },
|
|
1883
|
+
handler: async (ctx: Context<{ id: string }>) => accountService.findAccountById(ctx.params.id),
|
|
1884
|
+
},
|
|
1885
|
+
|
|
1886
|
+
// CREATE OPERATIONS
|
|
1887
|
+
[AccountServiceAction.CreateAccount]: {
|
|
1888
|
+
handler: async (ctx: Context<{ account: any }>) => accountService.createAccount(ctx.params.account),
|
|
1889
|
+
},
|
|
1890
|
+
|
|
1891
|
+
// Synchronous method
|
|
1892
|
+
[AccountServiceAction.CreateUserToken]: {
|
|
1893
|
+
params: {
|
|
1894
|
+
email: 'string',
|
|
1895
|
+
secret: 'string',
|
|
1896
|
+
expiresIn: { type: 'string', optional: true },
|
|
1897
|
+
},
|
|
1898
|
+
handler: (ctx: Context<{ email: string; secret: string; expiresIn?: string }>) =>
|
|
1899
|
+
accountService.createUserToken(ctx.params.email, ctx.params.secret, ctx.params.expiresIn),
|
|
1900
|
+
},
|
|
1901
|
+
|
|
1902
|
+
// With complex parameters
|
|
1903
|
+
[AccountServiceAction.ValidateAndFetchUserToken]: {
|
|
1904
|
+
params: { userId: 'string', tokens: 'array', type: 'string' },
|
|
1905
|
+
handler: async (ctx: Context<{ userId: string; tokens: IUserToken[]; type: TokenTypesEnum }>) =>
|
|
1906
|
+
accountService.validateAndFetchUserToken(ctx.params.userId, ctx.params.tokens, ctx.params.type),
|
|
1907
|
+
},
|
|
1908
|
+
},
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
```
|
|
1913
|
+
|
|
1914
|
+
**2a. Proxy Service (AUTO-GENERATION - RECOMMENDED)** (`ProjectProxyService.ts`):
|
|
1915
|
+
|
|
1916
|
+
```typescript
|
|
1917
|
+
import { injectable, inject } from 'inversify';
|
|
1918
|
+
import { ServiceBroker } from 'moleculer';
|
|
1919
|
+
import type { CdmLogger } from '@cdm-logger/core';
|
|
1920
|
+
import { CommonType } from '@common-stack/core';
|
|
1921
|
+
import { generateProxyMethods } from '@common-stack/codegen-zod';
|
|
1922
|
+
import { IProjectService, ProjectServiceSchemas } from 'common/server';
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* Client-side proxy for Project operations using AUTO-GENERATION.
|
|
1926
|
+
*
|
|
1927
|
+
* 🎉 FULLY AUTOMATIC: All IProjectService methods auto-generated!
|
|
1928
|
+
*
|
|
1929
|
+
* Uses generateProxyMethods() to automatically create all proxy methods:
|
|
1930
|
+
* • getProjects(), addProject(), updateProject(), deleteProject(), etc.
|
|
1931
|
+
*
|
|
1932
|
+
* ✅ ZERO BOILERPLATE: No manual method definitions (~100 lines eliminated)
|
|
1933
|
+
* ✅ TYPE-SAFE: Full TypeScript inference from IProjectService
|
|
1934
|
+
* ✅ VALIDATED: Zod schemas for parameter validation
|
|
1935
|
+
* ✅ MAINTAINABLE: Add method to interface → automatically available as proxy
|
|
1936
|
+
* ✅ CORRECT ORDERING: Parameters passed in correct order
|
|
1937
|
+
*/
|
|
1938
|
+
@injectable()
|
|
1939
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1940
|
+
export class ProjectProxyService {
|
|
1941
|
+
constructor(
|
|
1942
|
+
@inject(CommonType.MOLECULER_BROKER)
|
|
1943
|
+
broker: ServiceBroker,
|
|
1944
|
+
@inject('Logger')
|
|
1945
|
+
logger: CdmLogger.ILogger,
|
|
1946
|
+
) {
|
|
1947
|
+
const childLogger = logger.child({ className: 'ProjectProxyService' });
|
|
1948
|
+
generateProxyMethods<IProjectService>(this, ProjectServiceSchemas, broker, childLogger);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// Declaration merging for type safety - provides full interface implementation
|
|
1953
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
1954
|
+
export interface ProjectProxyService extends IProjectService {}
|
|
1955
|
+
```
|
|
1956
|
+
|
|
1957
|
+
**2b. Proxy Service (MANUAL - LEGACY)** (`AccountProxyService.ts`):
|
|
1958
|
+
|
|
1959
|
+
```typescript
|
|
1960
|
+
import { inject, injectable } from 'inversify';
|
|
1961
|
+
import { ServiceBroker } from 'moleculer';
|
|
1962
|
+
import { BaseProxyService2 } from '@common-stack/store-mongo';
|
|
1963
|
+
import {
|
|
1964
|
+
AccountServiceAction,
|
|
1965
|
+
IAccountService,
|
|
1966
|
+
IPhoneNumberInput,
|
|
1967
|
+
IUserAccountModel,
|
|
1968
|
+
IUserToken,
|
|
1969
|
+
TokenTypesEnum,
|
|
1970
|
+
} from 'common/server';
|
|
1971
|
+
|
|
1972
|
+
@injectable()
|
|
1973
|
+
export class AccountProxyService extends BaseProxyService2<IUserAccountModel> implements IAccountService {
|
|
1974
|
+
// ⚠️ CRITICAL: topic is REQUIRED for manual proxy services
|
|
1975
|
+
protected topic = 'accountUser';
|
|
1976
|
+
|
|
1977
|
+
constructor(broker: ServiceBroker, logger: any) {
|
|
1978
|
+
super(broker, logger);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// READ OPERATIONS
|
|
1982
|
+
findAccountById(id: string): Promise<any> {
|
|
1983
|
+
return this.callAction(AccountServiceAction.FindAccountById, { id });
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
// CREATE OPERATIONS
|
|
1987
|
+
createAccount(account: any): Promise<any> {
|
|
1988
|
+
return this.callAction(AccountServiceAction.CreateAccount, { account });
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// Synchronous method - throws error (cannot be proxied)
|
|
1992
|
+
createUserToken(email: string, secret: string, expiresIn?: string): string {
|
|
1993
|
+
throw new Error('createUserToken should be called directly on AccountService, not via proxy.');
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// With complex parameters
|
|
1997
|
+
validateAndFetchUserToken(userId: string, tokens: IUserToken[], type: TokenTypesEnum): Promise<IUserToken> {
|
|
1998
|
+
return this.callAction(AccountServiceAction.ValidateAndFetchUserToken, { userId, tokens, type });
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// ... other methods (~40+ manual method definitions)
|
|
2002
|
+
}
|
|
2003
|
+
```
|
|
2004
|
+
|
|
2005
|
+
**3. Container Module** (`modules/account/containers/module.ts`):
|
|
2006
|
+
|
|
2007
|
+
**3. Container Module** (`modules/account/containers/module.ts`):
|
|
2008
|
+
|
|
2009
|
+
```typescript
|
|
2010
|
+
import { ContainerModule, interfaces } from 'inversify';
|
|
2011
|
+
import { IAccountRepository, SERVER_TYPES, IAccountService } from 'common/server';
|
|
2012
|
+
import { AccountRepository } from '../store/repositories';
|
|
2013
|
+
import { AccountProxyService, AccountService } from '../services';
|
|
2014
|
+
|
|
2015
|
+
// Database Module - Registers actual service + repository (for server)
|
|
2016
|
+
export const AccountDbModule: (settings: any) => ContainerModule = (setting) =>
|
|
2017
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
2018
|
+
bind<IAccountRepository>(SERVER_TYPES.IAccountRepository).to(AccountRepository).inSingletonScope();
|
|
2019
|
+
bind<IAccountService>(SERVER_TYPES.IAccountService).to(AccountService).inSingletonScope();
|
|
2020
|
+
});
|
|
2021
|
+
|
|
2022
|
+
// Proxy Module - Registers proxy service only (for clients)
|
|
2023
|
+
export const AccountProxyModule: (settings: any) => ContainerModule = (setting) =>
|
|
2024
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
2025
|
+
bind<IAccountService>(SERVER_TYPES.IAccountService).to(AccountProxyService).inSingletonScope();
|
|
2026
|
+
});
|
|
2027
|
+
```
|
|
2028
|
+
|
|
2029
|
+
### Complete Example: Project Module
|
|
2030
|
+
|
|
2031
|
+
**1. Moleculer Service** (`modules/project/plugins/project-moleculer-service.ts`):
|
|
2032
|
+
|
|
2033
|
+
```typescript
|
|
2034
|
+
import { Context, Service, ServiceBroker } from 'moleculer';
|
|
2035
|
+
import { MoleculerTopics, IProjectService, ProjectCommand, SERVER_TYPES } from 'common/server';
|
|
2036
|
+
import { Container } from 'inversify';
|
|
2037
|
+
|
|
2038
|
+
export class ProjectMolecularService extends Service {
|
|
2039
|
+
private projectService: IProjectService;
|
|
2040
|
+
|
|
2041
|
+
constructor(broker: ServiceBroker, { container }: { container: Container }) {
|
|
2042
|
+
super(broker);
|
|
2043
|
+
this.projectService = container.get<IProjectService>(SERVER_TYPES.IProjectService);
|
|
2044
|
+
|
|
2045
|
+
this.parseServiceSchema({
|
|
2046
|
+
name: MoleculerTopics.Project,
|
|
2047
|
+
actions: {
|
|
2048
|
+
[ProjectCommand.GetProjects]: {
|
|
2049
|
+
handler: async (ctx: Context<{ orgName: string; offset?: number; limit?: number }>) =>
|
|
2050
|
+
this.projectService.getProjects(ctx.params.orgName, ctx.params.offset, ctx.params.limit),
|
|
2051
|
+
},
|
|
2052
|
+
[ProjectCommand.AddProject]: {
|
|
2053
|
+
handler: async (ctx: Context<{ project: any; orgId: string; orgName: string }>) =>
|
|
2054
|
+
this.projectService.addProject(ctx.params.project, ctx.params.orgId, ctx.params.orgName),
|
|
2055
|
+
},
|
|
2056
|
+
[ProjectCommand.UpdateProject]: {
|
|
2057
|
+
handler: async (ctx: Context<{ where: any; project: any; orgName: string }>) =>
|
|
2058
|
+
this.projectService.updateProject(ctx.params.where, ctx.params.project, ctx.params.orgName),
|
|
2059
|
+
},
|
|
2060
|
+
[ProjectCommand.DeleteProject]: {
|
|
2061
|
+
handler: async (ctx: Context<{ where: any; tenantId: string }>) =>
|
|
2062
|
+
this.projectService.deleteProject(ctx.params.where, ctx.params.tenantId),
|
|
2063
|
+
},
|
|
2064
|
+
},
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
```
|
|
2069
|
+
|
|
2070
|
+
**2. Container Module** (`modules/project/containers/module.ts`):
|
|
2071
|
+
|
|
2072
|
+
**2. Container Module** (`modules/project/containers/module.ts`):
|
|
2073
|
+
|
|
2074
|
+
```typescript
|
|
2075
|
+
import { ContainerModule, interfaces } from 'inversify';
|
|
2076
|
+
import { IProjectRepository, SERVER_TYPES, IProjectService } from 'common/server';
|
|
2077
|
+
import { ProjectRepository } from '../store/repositories';
|
|
2078
|
+
import { ProjectProxyService, ProjectService } from '../services';
|
|
2079
|
+
|
|
2080
|
+
// Database Module - Registers actual service
|
|
2081
|
+
export const ProjectDbModule: (settings: any) => ContainerModule = (setting) =>
|
|
2082
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
2083
|
+
bind<IProjectRepository>(SERVER_TYPES.IProjectRepository).to(ProjectRepository).inSingletonScope();
|
|
2084
|
+
bind<IProjectService>(SERVER_TYPES.IProjectService).to(ProjectService).inSingletonScope();
|
|
2085
|
+
});
|
|
2086
|
+
|
|
2087
|
+
// Proxy Module - Registers proxy service
|
|
2088
|
+
export const ProjectProxyModule: (settings: any) => ContainerModule = (setting) =>
|
|
2089
|
+
new ContainerModule((bind: interfaces.Bind) => {
|
|
2090
|
+
bind<IProjectService>(SERVER_TYPES.IProjectService).to(ProjectProxyService).inSingletonScope();
|
|
2091
|
+
});
|
|
2092
|
+
```
|
|
2093
|
+
|
|
2094
|
+
**Key Points**:
|
|
2095
|
+
|
|
2096
|
+
- **Both modules use same interface** (`IProjectService`)
|
|
2097
|
+
- **Both use same DI symbol** (`SERVER_TYPES.IProjectService`)
|
|
2098
|
+
- DbModule includes repository + actual service
|
|
2099
|
+
- ProxyModule includes only proxy service
|
|
2100
|
+
- Consumer code doesn't know if using proxy or actual service (polymorphism)
|
|
2101
|
+
- **Moleculer service** must be registered separately in server bootstrap
|
|
2102
|
+
|
|
2103
|
+
---
|
|
2104
|
+
|
|
2105
|
+
## 🤖 PROMPT END
|
|
2106
|
+
|
|
2107
|
+
---
|
|
2108
|
+
|
|
2109
|
+
## Usage Instructions
|
|
2110
|
+
|
|
2111
|
+
1. **Copy the prompt above** (from "PROMPT START" to "PROMPT END")
|
|
2112
|
+
|
|
2113
|
+
2. **Customize the placeholders**:
|
|
2114
|
+
- Replace `[ServiceName]` with your service name (e.g., "Project", "User", "Invoice")
|
|
2115
|
+
- Replace `[module-name]` with your module name (e.g., "account-api", "project-api")
|
|
2116
|
+
- Replace `[service]` with lowercase service name (e.g., "project", "user", "invoice")
|
|
2117
|
+
|
|
2118
|
+
3. **Provide to LLM** with the following files:
|
|
2119
|
+
|
|
2120
|
+
```
|
|
2121
|
+
Attached files:
|
|
2122
|
+
1. packages/common/src/services/[ServiceName].ts
|
|
2123
|
+
2. packages-modules/[module]/server/src/graphql/schema/service.graphql
|
|
2124
|
+
3. packages-modules/[module]/server/src/modules/[service]/containers/module.ts
|
|
2125
|
+
4. (Optional) packages-modules/[module]/server/src/services/[ServiceName].ts
|
|
2126
|
+
```
|
|
2127
|
+
|
|
2128
|
+
4. **Review the output** and verify against the checklist
|
|
2129
|
+
|
|
2130
|
+
5. **Run the generation command**:
|
|
2131
|
+
|
|
2132
|
+
```bash
|
|
2133
|
+
yarn generateGraphql
|
|
2134
|
+
```
|
|
2135
|
+
|
|
2136
|
+
6. **Update the container module** with the generated proxy registration
|
|
2137
|
+
|
|
2138
|
+
7. **Verify compilation**:
|
|
2139
|
+
```bash
|
|
2140
|
+
# Check for TypeScript errors
|
|
2141
|
+
cd packages-modules/[module]/core
|
|
2142
|
+
npx tsc --noEmit
|
|
2143
|
+
```
|
|
2144
|
+
|
|
2145
|
+
---
|
|
2146
|
+
|
|
2147
|
+
## Example Conversation
|
|
2148
|
+
|
|
2149
|
+
**You**: "I need to create a proxy service for UserService"
|
|
2150
|
+
|
|
2151
|
+
**Attach**:
|
|
2152
|
+
|
|
2153
|
+
- `packages/common/src/services/UserService.ts`
|
|
2154
|
+
- `packages-modules/user-api/server/src/graphql/schema/service.graphql`
|
|
2155
|
+
- `packages-modules/user-api/server/src/modules/user/containers/module.ts`
|
|
2156
|
+
|
|
2157
|
+
**Paste**: [The full prompt from above with [ServiceName] replaced with "User"]
|
|
2158
|
+
|
|
2159
|
+
**LLM Output**:
|
|
2160
|
+
|
|
2161
|
+
1. Complete proxy service implementation
|
|
2162
|
+
2. GraphQL enum updates
|
|
2163
|
+
3. Container module registration code
|
|
2164
|
+
|
|
2165
|
+
**You Run**:
|
|
2166
|
+
|
|
2167
|
+
```bash
|
|
2168
|
+
yarn generateGraphql
|
|
2169
|
+
```
|
|
2170
|
+
|
|
2171
|
+
**You Update**: Apply the container module changes
|
|
2172
|
+
|
|
2173
|
+
**Done**: Proxy service ready to use!
|
|
2174
|
+
|
|
2175
|
+
---
|
|
2176
|
+
|
|
2177
|
+
## Troubleshooting
|
|
2178
|
+
|
|
2179
|
+
### Issue: Generated proxy has wrong method signatures
|
|
2180
|
+
|
|
2181
|
+
**Fix**: Ensure you attached the correct interface file. The LLM must see the exact method signatures.
|
|
2182
|
+
|
|
2183
|
+
### Issue: Missing imports
|
|
2184
|
+
|
|
2185
|
+
**Fix**: Check that `MoleculerServiceName` enum includes your service. Add if missing:
|
|
2186
|
+
|
|
2187
|
+
```graphql
|
|
2188
|
+
extend enum MoleculerServiceName {
|
|
2189
|
+
YourService
|
|
2190
|
+
}
|
|
2191
|
+
```
|
|
2192
|
+
|
|
2193
|
+
### Issue: callAction type errors
|
|
2194
|
+
|
|
2195
|
+
**Fix**: After running `yarn generateGraphql`, restart your TypeScript server (VS Code: Cmd+Shift+P → "Restart TS Server")
|
|
2196
|
+
|
|
2197
|
+
### Issue: DI container can't resolve service
|
|
2198
|
+
|
|
2199
|
+
**Error**: `No matching bindings found for serviceIdentifier: Symbol(IYourService)`
|
|
2200
|
+
|
|
2201
|
+
**Fix**: Ensure you're loading the correct module:
|
|
2202
|
+
|
|
2203
|
+
- **For server (business logic)**: Load `YourServiceDbModule`
|
|
2204
|
+
- **For client (via Moleculer)**: Load `YourServiceProxyModule`
|
|
2205
|
+
- Never load both in same container
|
|
2206
|
+
|
|
2207
|
+
### Issue: Service methods not being called / "Service not found" errors
|
|
2208
|
+
|
|
2209
|
+
**Symptoms**:
|
|
2210
|
+
|
|
2211
|
+
- Moleculer throws "Service '[ServiceName]' not found" errors
|
|
2212
|
+
- Proxy methods timeout or fail silently
|
|
2213
|
+
- Actions not reaching the moleculer service
|
|
2214
|
+
|
|
2215
|
+
**Root Cause**: Missing or incorrect `topic` property in manual proxy service
|
|
2216
|
+
|
|
2217
|
+
**Fix for Manual Proxy Services**:
|
|
2218
|
+
|
|
2219
|
+
⚠️ **CRITICAL**: The `topic` property is **REQUIRED** for manual proxy services extending BaseProxyService2
|
|
2220
|
+
|
|
2221
|
+
```typescript
|
|
2222
|
+
// ❌ BAD - Missing topic
|
|
2223
|
+
@injectable()
|
|
2224
|
+
export class YourProxyService extends BaseProxyService2<IYourModel> implements IYourService {
|
|
2225
|
+
constructor(broker: ServiceBroker, logger: CdmLogger.ILogger) {
|
|
2226
|
+
super(broker, logger);
|
|
2227
|
+
}
|
|
2228
|
+
// Methods won't work - no topic set!
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
// ✅ GOOD - Topic properly set
|
|
2232
|
+
@injectable()
|
|
2233
|
+
export class YourProxyService extends BaseProxyService2<IYourModel> implements IYourService {
|
|
2234
|
+
protected topic = MoleculerServiceName.YourService; // REQUIRED!
|
|
2235
|
+
|
|
2236
|
+
constructor(broker: ServiceBroker, logger: CdmLogger.ILogger) {
|
|
2237
|
+
super(broker, logger);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
// ✅ BEST - Use auto-generation (topic handled automatically)
|
|
2242
|
+
@injectable()
|
|
2243
|
+
export class YourProxyService {
|
|
2244
|
+
constructor(broker: ServiceBroker, logger: CdmLogger.ILogger) {
|
|
2245
|
+
const childLogger = logger.child({ className: 'YourProxyService' });
|
|
2246
|
+
generateProxyMethods<IYourService>(this, YourServiceSchemas, broker, childLogger);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
export interface YourProxyService extends IYourService {}
|
|
2250
|
+
```
|
|
2251
|
+
|
|
2252
|
+
**Verify topic matches Moleculer service registration**:
|
|
2253
|
+
|
|
2254
|
+
```typescript
|
|
2255
|
+
// In proxy service
|
|
2256
|
+
protected topic = MoleculerServiceName.YourService;
|
|
2257
|
+
|
|
2258
|
+
// Must match in moleculer service
|
|
2259
|
+
this.parseServiceSchema({
|
|
2260
|
+
name: MoleculerServiceName.YourService, // Must match!
|
|
2261
|
+
actions,
|
|
2262
|
+
events,
|
|
2263
|
+
});
|
|
2264
|
+
```
|
|
2265
|
+
|
|
2266
|
+
### Issue: Multiple bindings for same service
|
|
2267
|
+
|
|
2268
|
+
**Error**: Ambiguous binding resolution
|
|
2269
|
+
|
|
2270
|
+
**Fix**:
|
|
2271
|
+
|
|
2272
|
+
1. Check only ONE service module is loaded per container
|
|
2273
|
+
2. Don't bind same service in multiple modules
|
|
2274
|
+
3. Use conditional binding if needed:
|
|
2275
|
+
```typescript
|
|
2276
|
+
if (useMicroservices) {
|
|
2277
|
+
container.load(YourServiceProxyModule(settings));
|
|
2278
|
+
} else {
|
|
2279
|
+
container.load(YourServiceDbModule(settings));
|
|
2280
|
+
}
|
|
2281
|
+
```
|
|
2282
|
+
|
|
2283
|
+
---
|
|
2284
|
+
|
|
2285
|
+
## Tips for Best Results
|
|
2286
|
+
|
|
2287
|
+
1. **Always attach the interface file** - this is the source of truth
|
|
2288
|
+
2. **Provide the actual service file** if methods have complex signatures
|
|
2289
|
+
3. **Reference similar complexity** - mention which example to follow
|
|
2290
|
+
4. **Be specific about module name** - helps LLM determine correct paths
|
|
2291
|
+
5. **Review section organization** - ensure methods are grouped logically
|
|
2292
|
+
|
|
2293
|
+
---
|
|
2294
|
+
|
|
2295
|
+
## Advanced: Batch Generation
|
|
2296
|
+
|
|
2297
|
+
To generate multiple proxy services:
|
|
2298
|
+
|
|
2299
|
+
```bash
|
|
2300
|
+
# Create a list file
|
|
2301
|
+
cat > services-to-generate.txt << EOF
|
|
2302
|
+
UserService user-api
|
|
2303
|
+
InvoiceService billing-api
|
|
2304
|
+
NotificationService notification-api
|
|
2305
|
+
EOF
|
|
2306
|
+
|
|
2307
|
+
# Use with your favorite LLM tool
|
|
2308
|
+
# Each line: ServiceName module-name
|
|
2309
|
+
```
|
|
2310
|
+
|
|
2311
|
+
---
|
|
2312
|
+
|
|
2313
|
+
## Configuration & Code Generation
|
|
2314
|
+
|
|
2315
|
+
### Prerequisites
|
|
2316
|
+
|
|
2317
|
+
**Required Package Version**:
|
|
2318
|
+
|
|
2319
|
+
```json
|
|
2320
|
+
{
|
|
2321
|
+
"devDependencies": {
|
|
2322
|
+
"@common-stack/graphql-codegen-zod-schemas": "^7.2.1-alpha.21"
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
```
|
|
2326
|
+
|
|
2327
|
+
**Minimum Version**: `7.2.1-alpha.21` or higher
|
|
2328
|
+
|
|
2329
|
+
This version includes:
|
|
2330
|
+
|
|
2331
|
+
- Support for `constEnums` configuration
|
|
2332
|
+
- Proper handling of `zodImportPath`
|
|
2333
|
+
- Export as `@common-stack` scoped package
|
|
2334
|
+
- GraphQL and Service schema generation plugins
|
|
2335
|
+
|
|
2336
|
+
**Installation**:
|
|
2337
|
+
|
|
2338
|
+
```bash
|
|
2339
|
+
yarn add -D @common-stack/graphql-codegen-zod-schemas@^7.2.1-alpha.21
|
|
2340
|
+
```
|
|
2341
|
+
|
|
2342
|
+
### GraphQL Codegen Plugin Configuration
|
|
2343
|
+
|
|
2344
|
+
The project uses `@common-stack/graphql-codegen-zod-schemas` for generating Zod validation schemas from GraphQL schemas. The configuration is centralized in `cdecode-config.json`.
|
|
2345
|
+
|
|
2346
|
+
**Configuration File**: `cdecode-config.json` (root level)
|
|
2347
|
+
|
|
2348
|
+
This file contains ALL project code generation and automation configuration. Key sections:
|
|
2349
|
+
|
|
2350
|
+
1. **`servers`**: Frontend and backend server config paths
|
|
2351
|
+
2. **`codegen`**: GraphQL code generation configuration (including Zod schemas)
|
|
2352
|
+
3. **`serviceSchemas`**: TypeScript service interface Zod schema generation
|
|
2353
|
+
4. **`updateDependencies`**: Dependency synchronization rules
|
|
2354
|
+
5. **`updateDependencyVersion`**: Version management across packages
|
|
2355
|
+
6. **`sortPackageJson`**: Package.json formatting rules
|
|
2356
|
+
7. **`deployVersionUpdate`**: Deployment version tracking
|
|
2357
|
+
8. **`projectPaths`**: Project structure paths
|
|
2358
|
+
|
|
2359
|
+
**Complete Configuration Structure** (`cdecode-config.json`):
|
|
2360
|
+
|
|
2361
|
+
```json
|
|
2362
|
+
{
|
|
2363
|
+
"servers": ["servers/frontend-server/config.json", "servers/backend-server/config.json"],
|
|
2364
|
+
"codegen": {
|
|
2365
|
+
"outputFile": "codegen.ts",
|
|
2366
|
+
"rootSchema": "node_modules/@common-stack/graphql-api/root-schema.graphqls",
|
|
2367
|
+
"fullConfig": {
|
|
2368
|
+
"overwrite": true,
|
|
2369
|
+
"schema": ["$PRIMARY_SCHEMA_PLACEHOLDER$"],
|
|
2370
|
+
"hooks": {
|
|
2371
|
+
"afterAllFileWrite": [
|
|
2372
|
+
"node scripts/fix-enum-references.js",
|
|
2373
|
+
"node scripts/generate-all-service-schemas.js"
|
|
2374
|
+
]
|
|
2375
|
+
},
|
|
2376
|
+
"generates": {
|
|
2377
|
+
"packages/common/src/generated/generated-zod-schemas.ts": {
|
|
2378
|
+
"schema": "%discoveredSchemas%",
|
|
2379
|
+
"config": {
|
|
2380
|
+
"enumsAsTypes": true,
|
|
2381
|
+
"enumValues": {
|
|
2382
|
+
"ConfigurationScope": "@workbench-stack/core/lib/interfaces/configuration/configuration.js#ConfigurationScope",
|
|
2383
|
+
"ConfigurationTarget": "../configuration#ConfigurationTarget"
|
|
2384
|
+
},
|
|
2385
|
+
"constEnums": {
|
|
2386
|
+
"ConfigurationScope": [1, 2, 3, 4],
|
|
2387
|
+
"ConfigurationTarget": [1, 2, 3, 4, 5, 6, 7]
|
|
2388
|
+
},
|
|
2389
|
+
"zodImportPath": "zod"
|
|
2390
|
+
},
|
|
2391
|
+
"plugins": ["@common-stack/codegen-zod/graphql"]
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
},
|
|
2396
|
+
"serviceSchemas": {
|
|
2397
|
+
"servicesDir": "packages/common/src/services",
|
|
2398
|
+
"outputFile": "packages/common/src/generated/service-schemas.ts",
|
|
2399
|
+
"skipServices": [
|
|
2400
|
+
"AuthBackendClient",
|
|
2401
|
+
"PubSub",
|
|
2402
|
+
"RedisCacheManager",
|
|
2403
|
+
"InngestClient",
|
|
2404
|
+
"JSONContributionRegistry",
|
|
2405
|
+
"PageStore",
|
|
2406
|
+
"PublisherEventService"
|
|
2407
|
+
],
|
|
2408
|
+
"knownEnums": [],
|
|
2409
|
+
"constEnumSchemas": ["ConfigurationTarget", "ConfigurationScope"],
|
|
2410
|
+
"graphqlSchemasPath": "packages/common/src/generated/generated-zod-schemas.ts"
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
```
|
|
2414
|
+
|
|
2415
|
+
**Key Configuration Options**:
|
|
2416
|
+
|
|
2417
|
+
- **`enumsAsTypes`**: Generates TypeScript enums as union types for better Zod compatibility
|
|
2418
|
+
- **`enumValues`**: Maps GraphQL enums to TypeScript module imports for shared enum definitions
|
|
2419
|
+
- **`constEnums`**: Defines numeric values for const enums (used for runtime validation)
|
|
2420
|
+
- **`zodImportPath`**: Specifies the Zod import path (defaults to "zod")
|
|
2421
|
+
- **`hooks.afterAllFileWrite`**: Scripts that run after code generation completes
|
|
2422
|
+
- **`plugins`**: Must reference `@common-stack/codegen-zod/graphql` (version ^7.2.1-alpha.21 or higher)
|
|
2423
|
+
|
|
2424
|
+
### Service Schema Generation Configuration
|
|
2425
|
+
|
|
2426
|
+
The project automatically generates Zod schemas for TypeScript service interfaces using the centralized schema generator from `@common-stack/server-core`. Configuration is in the `serviceSchemas` section of `cdecode-config.json` (shown in the complete configuration above).
|
|
2427
|
+
|
|
2428
|
+
**Schema Generator**: `@common-stack/server-core/lib/moleculer-generation/generateAllServiceSchemas.cjs`
|
|
2429
|
+
|
|
2430
|
+
This CJS script is published as part of the `@common-stack/server-core` package and shared across all projects.
|
|
2431
|
+
|
|
2432
|
+
**Service Schema Configuration Properties**:
|
|
2433
|
+
|
|
2434
|
+
- **`servicesDir`**: Directory containing service interface TypeScript files
|
|
2435
|
+
- **`outputFile`**: Where to generate the service schemas file
|
|
2436
|
+
- **`skipServices`**: Services to exclude from schema generation (external dependencies, special cases)
|
|
2437
|
+
- **`knownEnums`**: Additional enums to recognize during schema generation
|
|
2438
|
+
- **`constEnumSchemas`**: Enums that should use const enum schemas (numeric values)
|
|
2439
|
+
- **`graphqlSchemasPath`**: Path to GraphQL Zod schemas file (for importing shared schemas)
|
|
2440
|
+
|
|
2441
|
+
**Automatic Relative Path Calculation**:
|
|
2442
|
+
|
|
2443
|
+
The service schema generator automatically calculates the correct relative import path from `outputFile` to `graphqlSchemasPath`. You don't need to specify a separate import path - it's derived automatically.
|
|
2444
|
+
|
|
2445
|
+
**Example**:
|
|
2446
|
+
|
|
2447
|
+
```typescript
|
|
2448
|
+
// Configuration
|
|
2449
|
+
{
|
|
2450
|
+
"outputFile": "packages/common/src/generated/service-schemas.ts",
|
|
2451
|
+
"graphqlSchemasPath": "packages/common/src/generated/generated-zod-schemas.ts"
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// Generated import (automatically calculated as same directory)
|
|
2455
|
+
import { ConfigurationScopeSchema, ConfigurationTargetSchema } from './generated-zod-schemas';
|
|
2456
|
+
```
|
|
2457
|
+
|
|
2458
|
+
### Running Code Generation
|
|
2459
|
+
|
|
2460
|
+
**Generate GraphQL schemas and service schemas**:
|
|
2461
|
+
|
|
2462
|
+
```bash
|
|
2463
|
+
yarn generateGraphql
|
|
2464
|
+
```
|
|
2465
|
+
|
|
2466
|
+
This command:
|
|
2467
|
+
|
|
2468
|
+
1. Runs GraphQL Codegen to generate TypeScript types and Zod schemas from GraphQL schemas
|
|
2469
|
+
2. Fixes enum references in generated files (via `scripts/fix-enum-references.js`)
|
|
2470
|
+
3. Generates Zod schemas for all service interfaces (via `scripts/generate-all-service-schemas.js`)
|
|
2471
|
+
4. Rebuilds the common package with updated schemas
|
|
2472
|
+
|
|
2473
|
+
**Generation happens automatically via hooks** defined in `cdecode-config.json`:
|
|
2474
|
+
|
|
2475
|
+
```json
|
|
2476
|
+
{
|
|
2477
|
+
"hooks": {
|
|
2478
|
+
"afterAllFileWrite": [
|
|
2479
|
+
"node scripts/fix-enum-references.js",
|
|
2480
|
+
"node node node_modules/@common-stack/codegen-zod/dist/service/generateAllServiceSchemas.js"
|
|
2481
|
+
]
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
```
|
|
2485
|
+
|
|
2486
|
+
### Schema Generation Script
|
|
2487
|
+
|
|
2488
|
+
The service schema generator (`@common-stack/server-core/lib/moleculer-generation/generateAllServiceSchemas.cjs`) uses the configuration from `cdecode-config.json` to:
|
|
2489
|
+
|
|
2490
|
+
1. **Discover all service interfaces** in the configured services directory
|
|
2491
|
+
2. **Parse TypeScript** to extract method signatures and parameter types
|
|
2492
|
+
3. **Auto-generate topic** from interface name (e.g., `IProjectService` → `ProjectService`)
|
|
2493
|
+
4. **Generate Zod schemas** for each method's parameters
|
|
2494
|
+
5. **Import shared schemas** from GraphQL Zod schemas (enums, input types)
|
|
2495
|
+
6. **Skip configured services** that shouldn't have schemas generated
|
|
2496
|
+
7. **Use const enum schemas** for numeric enums where configured
|
|
2497
|
+
8. **Calculate relative import paths** automatically based on file locations
|
|
2498
|
+
|
|
2499
|
+
**Script Location**: `node node_modules/@common-stack/codegen-zod/dist/service/generateAllServiceSchemas.js`
|
|
2500
|
+
|
|
2501
|
+
**How it works**:
|
|
2502
|
+
|
|
2503
|
+
```javascript
|
|
2504
|
+
// 1. Load configuration from cdecode-config.json
|
|
2505
|
+
const configPath = path.join(__dirname, '../cdecode-config.json');
|
|
2506
|
+
const fullConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
2507
|
+
const serviceConfig = fullConfig.serviceSchemas || {};
|
|
2508
|
+
|
|
2509
|
+
// 2. Calculate relative import path automatically
|
|
2510
|
+
function calculateRelativeImportPath() {
|
|
2511
|
+
if (!serviceConfig.outputFile || !serviceConfig.graphqlSchemasPath) {
|
|
2512
|
+
return './generated-zod-schemas';
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
const outputDir = path.dirname(serviceConfig.outputFile);
|
|
2516
|
+
const graphqlSchemaPath = serviceConfig.graphqlSchemasPath;
|
|
2517
|
+
|
|
2518
|
+
// Calculate relative path from output file to GraphQL schemas
|
|
2519
|
+
const relativePath = path.relative(outputDir, graphqlSchemaPath);
|
|
2520
|
+
|
|
2521
|
+
// Remove .ts extension and ensure it starts with ./
|
|
2522
|
+
const withoutExt = relativePath.replace(/\.ts$/, '');
|
|
2523
|
+
return withoutExt.startsWith('.') ? withoutExt : `./${withoutExt}`;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
const GRAPHQL_SCHEMAS_IMPORT_PATH = calculateRelativeImportPath();
|
|
2527
|
+
|
|
2528
|
+
// 3. Discover GraphQL Zod types from generated file
|
|
2529
|
+
function discoverGraphQLZodTypes() {
|
|
2530
|
+
const graphqlSchemaFile = path.join(__dirname, '..', serviceConfig.graphqlSchemasPath);
|
|
2531
|
+
const content = fs.readFileSync(graphqlSchemaFile, 'utf8');
|
|
2532
|
+
const graphqlTypes = new Set();
|
|
2533
|
+
|
|
2534
|
+
// Find all exported schema functions like: export function UserInputSchema()
|
|
2535
|
+
const schemaFunctionPattern = /export function ([A-Z][a-zA-Z0-9]+)Schema\(\)/g;
|
|
2536
|
+
let match;
|
|
2537
|
+
|
|
2538
|
+
while ((match = schemaFunctionPattern.exec(content)) !== null) {
|
|
2539
|
+
const schemaName = match[1];
|
|
2540
|
+
// Convert schema name to interface name (add 'I' prefix)
|
|
2541
|
+
const interfaceName = 'I' + schemaName;
|
|
2542
|
+
graphqlTypes.add(interfaceName);
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
return graphqlTypes;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
// 4. Find all service interfaces (excluding skipServices)
|
|
2549
|
+
function findServiceInterfaces() {
|
|
2550
|
+
const files = fs.readdirSync(SERVICES_DIR).filter((f) => f.endsWith('.ts'));
|
|
2551
|
+
const services = [];
|
|
2552
|
+
|
|
2553
|
+
files.forEach((file) => {
|
|
2554
|
+
const content = fs.readFileSync(path.join(SERVICES_DIR, file), 'utf8');
|
|
2555
|
+
const interfaceMatch = content.match(/export interface (I[A-Z][a-zA-Z0-9]*Service)/);
|
|
2556
|
+
|
|
2557
|
+
if (interfaceMatch) {
|
|
2558
|
+
const serviceName = interfaceMatch[1];
|
|
2559
|
+
if (!SKIP_SERVICES.includes(serviceName.replace(/^I/, ''))) {
|
|
2560
|
+
services.push({ name: serviceName, file, path: filePath });
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
|
|
2565
|
+
return services;
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// 5. Parse TypeScript to extract method signatures
|
|
2569
|
+
function parseServiceInterface(filePath, interfaceName) {
|
|
2570
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
|
|
2571
|
+
|
|
2572
|
+
// Visit AST nodes to find interface and extract methods
|
|
2573
|
+
// Returns: { methods: [...], usedEnums: [...] }
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
// 6. Generate Zod schemas for each parameter type
|
|
2577
|
+
function generateZodType(paramType, isOptional, paramName) {
|
|
2578
|
+
// Handle arrays: Type[] -> z.array(...)
|
|
2579
|
+
// Handle primitives: string -> z.string()
|
|
2580
|
+
// Handle GraphQL types: IUserInput -> UserInputSchema()
|
|
2581
|
+
// Handle enums: TokenType -> z.nativeEnum(TokenType)
|
|
2582
|
+
// Handle const enums: ConfigurationScope -> ConfigurationScopeSchema()
|
|
2583
|
+
// Default: z.object({}).passthrough()
|
|
2584
|
+
}
|
|
2585
|
+
```
|
|
2586
|
+
|
|
2587
|
+
**Configuration Properties Used**:
|
|
2588
|
+
|
|
2589
|
+
- `serviceSchemas.servicesDir`: Where to find service interface files
|
|
2590
|
+
- `serviceSchemas.outputFile`: Where to write generated schemas
|
|
2591
|
+
- `serviceSchemas.skipServices`: Services to exclude from generation
|
|
2592
|
+
- `serviceSchemas.knownEnums`: Regular enums to import
|
|
2593
|
+
- `serviceSchemas.constEnumSchemas`: Const enums to use schema functions for
|
|
2594
|
+
- `serviceSchemas.graphqlSchemasPath`: Source of GraphQL Zod schemas to import
|
|
2595
|
+
|
|
2596
|
+
**What gets generated**:
|
|
2597
|
+
|
|
2598
|
+
```typescript
|
|
2599
|
+
// Example generated service schema (from IAccountService interface)
|
|
2600
|
+
export const AccountServiceSchemas = {
|
|
2601
|
+
/** Moleculer service topic/name - safe for minification */
|
|
2602
|
+
topic: 'AccountService' as const, // Auto-derived from IAccountService
|
|
2603
|
+
|
|
2604
|
+
findAccountById: z.object({
|
|
2605
|
+
id: z.string(),
|
|
2606
|
+
}),
|
|
2607
|
+
createAccount: z.object({
|
|
2608
|
+
account: AccountInputSchema, // From GraphQL schemas
|
|
2609
|
+
}),
|
|
2610
|
+
updateUserAccount: z.object({
|
|
2611
|
+
userId: z.string(),
|
|
2612
|
+
input: UserAccountUpdateInputSchema, // From GraphQL schemas
|
|
2613
|
+
}),
|
|
2614
|
+
};
|
|
2615
|
+
```
|
|
2616
|
+
|
|
2617
|
+
### Benefits of Centralized Configuration
|
|
2618
|
+
|
|
2619
|
+
1. **Single Source of Truth**: All code generation config in one file
|
|
2620
|
+
2. **Easy Customization**: Change paths, add services, modify schemas without editing scripts
|
|
2621
|
+
3. **Automatic Path Resolution**: No manual relative path calculations needed
|
|
2622
|
+
4. **Type Safety**: Generated schemas provide runtime validation for Moleculer actions
|
|
2623
|
+
5. **Consistency**: Same configuration used across all code generation tools
|
|
2624
|
+
6. **Plugin Publishing Ready**: Clean separation of plugin code and project configuration
|
|
2625
|
+
|
|
2626
|
+
### Configuration Migration Notes
|
|
2627
|
+
|
|
2628
|
+
**Recent Changes** (October 2025):
|
|
2629
|
+
|
|
2630
|
+
1. **Plugin renamed**: `@adminide-stack/graphql-codegen-zod-schemas` → `@common-stack/graphql-codegen-zod-schemas`
|
|
2631
|
+
2. **Version requirement**: Must use `^7.2.1-alpha.21` or higher
|
|
2632
|
+
3. **Plugin path changed**: `./packages/graphql-codegen-zod-schemas/dist/graphql.js` → `@common-stack/codegen-zod/graphql`
|
|
2633
|
+
4. **Service schema config moved**: Separate `service-schemas.config.js` → `cdecode-config.json` (serviceSchemas section)
|
|
2634
|
+
5. **Automatic path calculation**: Import paths now derived from file paths (no separate `graphqlSchemasImportPath` needed)
|
|
2635
|
+
|
|
2636
|
+
**If updating from older configuration**:
|
|
2637
|
+
|
|
2638
|
+
1. **Install/upgrade the plugin**:
|
|
2639
|
+
|
|
2640
|
+
```bash
|
|
2641
|
+
yarn add -D @common-stack/graphql-codegen-zod-schemas@^7.2.1-alpha.21
|
|
2642
|
+
```
|
|
2643
|
+
|
|
2644
|
+
2. **Update plugin reference** in `cdecode-config.json`:
|
|
2645
|
+
|
|
2646
|
+
```json
|
|
2647
|
+
{
|
|
2648
|
+
"plugins": ["@common-stack/codegen-zod/graphql"]
|
|
2649
|
+
}
|
|
2650
|
+
```
|
|
2651
|
+
|
|
2652
|
+
3. **Move service schema config** to `serviceSchemas` section of `cdecode-config.json`:
|
|
2653
|
+
|
|
2654
|
+
```json
|
|
2655
|
+
{
|
|
2656
|
+
"serviceSchemas": {
|
|
2657
|
+
"servicesDir": "packages/common/src/services",
|
|
2658
|
+
"outputFile": "packages/common/src/generated/service-schemas.ts",
|
|
2659
|
+
"graphqlSchemasPath": "packages/common/src/generated/generated-zod-schemas.ts",
|
|
2660
|
+
...
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
```
|
|
2664
|
+
|
|
2665
|
+
4. **Remove deprecated properties**:
|
|
2666
|
+
- Delete `graphqlSchemasImportPath` from serviceSchemas (now calculated automatically)
|
|
2667
|
+
- Delete `service-schemas.config.js` file if it exists
|
|
2668
|
+
|
|
2669
|
+
5. **Verify the setup**:
|
|
2670
|
+
```bash
|
|
2671
|
+
yarn generateGraphql
|
|
2672
|
+
yarn build
|
|
2673
|
+
```
|
|
2674
|
+
|
|
2675
|
+
**Breaking Changes**:
|
|
2676
|
+
|
|
2677
|
+
- Old plugin path `./packages/graphql-codegen-zod-schemas/dist/graphql.js` will no longer work
|
|
2678
|
+
- Service schema generation now requires configuration in `cdecode-config.json`
|
|
2679
|
+
- Minimum version requirement: `@common-stack/graphql-codegen-zod-schemas@^7.2.1-alpha.21`
|
|
2680
|
+
|
|
2681
|
+
---
|
|
2682
|
+
|
|
2683
|
+
## Version
|
|
2684
|
+
|
|
2685
|
+
- **v1.2.0** (2025-10-26): Added configuration and code generation documentation, plugin rename updates
|
|
2686
|
+
- **v1.1.0** (2024-10-24): Added container module pattern and DI registration guidance
|
|
2687
|
+
- **v1.0.0** (2024-10-24): Initial LLM prompt template based on 4 implemented examples
|