cabloy 5.1.115 → 5.1.117
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/.cabloy-version +1 -1
- package/.claude/skills/cabloy-contract-loop/SKILL.md +8 -2
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +6 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +13 -0
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +16 -3
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +12 -0
- package/.github/workflows/vona-test-mysql.yml +6 -10
- package/.github/workflows/vona-test-pg.yml +9 -10
- package/.github/workflows/vona-test-sqlite3.yml +3 -0
- package/CHANGELOG.md +35 -0
- package/CLAUDE.md +2 -0
- package/cabloy-docs/.vitepress/config.mjs +6 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +7 -0
- package/cabloy-docs/backend/aop-overview.md +16 -24
- package/cabloy-docs/backend/backend-source-reading-roadmap.md +26 -2
- package/cabloy-docs/backend/controller-aop-guide.md +38 -14
- package/cabloy-docs/backend/logger-guide.md +1 -0
- package/cabloy-docs/backend/menu-guide.md +1 -1
- package/cabloy-docs/backend/orm-mutation-guide.md +36 -10
- package/cabloy-docs/backend/orm-select-guide.md +15 -10
- package/cabloy-docs/backend/rate-limit-guide.md +97 -0
- package/cabloy-docs/backend/telemetry-guide.md +93 -0
- package/cabloy-docs/backend/vona-source-reading-map.md +44 -1
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +2 -0
- package/cabloy-docs/frontend/ssr-overview.md +2 -0
- package/cabloy-docs/fullstack/admin-resource-and-web-self-service.md +329 -0
- package/cabloy-docs/fullstack/contract-loop-playbook.md +1 -1
- package/cabloy-docs/fullstack/introduction.md +1 -0
- package/e2e/specs/a-commerce/commerce.spec.ts +479 -1
- package/package.json +1 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoBaseName%>.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoMutateName%>.tsx_ +2 -2
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoResItemName%>.tsx_ +10 -10
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoViewName%>.tsx_ +2 -2
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +13 -4
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/core/logger/utils.ts +12 -3
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +237 -164
- package/vona/src/backend/config/config/config.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/sku.test.ts +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +81 -25
- package/vona/src/suite/a-commerce/modules/commerce-member/src/bean/ssrMenu.address.ts +30 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/controller/address.ts +42 -20
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineCreate.tsx +35 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineItem.tsx +39 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineReq.tsx +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineRes.tsx +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineUpdate.tsx +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressMineView.tsx +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressSelectResItem.tsx +1 -7
- package/vona/src/suite/a-commerce/modules/commerce-member/src/service/address.ts +43 -42
- package/vona/src/suite/a-commerce/modules/commerce-member/test/addressOwnership.test.ts +168 -127
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +76 -3
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.index.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.version.ts +37 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/dto/paymentAttemptView.tsx +2 -2
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAttempt.tsx +5 -2
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAudit.tsx +54 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/model/paymentAudit.ts +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/mockPaymentAdapter.ts +18 -0
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/paymentAttempt.ts +22 -7
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponIssue.tsx +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-promotion/src/service/coupon.ts +6 -1
- package/vona/src/suite/a-commerce/modules/commerce-promotion/test/couponReservation.test.ts +480 -8
- package/vona/src/suite/a-commerce/modules/commerce-siteadmin/test/ssrMenu.test.ts +14 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +309 -3
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/bean/meta.index.ts +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/bean/meta.version.ts +16 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/bean/ssrMenu.order.ts +30 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/order.ts +69 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/payment.ts +23 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutResult.tsx +4 -4
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderDetail.tsx +72 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderMineReq.tsx +21 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderMineRes.tsx +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectReq.tsx +25 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectRes.tsx +11 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectResItem.tsx +38 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderShip.tsx +15 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSummary.tsx +26 -0
- package/vona/src/suite/a-commerce/modules/{commerce-member/src/dto/addressCreate.tsx → commerce-trade/src/dto/orderView.tsx} +5 -17
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/paymentOutcomeCreate.tsx +16 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/paymentOutcomeResult.tsx +29 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/shipmentView.tsx +22 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/orderAudit.tsx +13 -7
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/shipment.tsx +28 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/model/shipment.ts +10 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/cart.ts +14 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +436 -34
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockBalance.ts +4 -3
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/cartOwnership.test.ts +160 -80
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutReservation.test.ts +773 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutTransaction.test.ts +212 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/order.test.ts +37 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +29 -5
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +714 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/reservationExpiry.test.ts +282 -9
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/shipment.test.ts +260 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockBalance.test.ts +15 -1
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockReservation.test.ts +20 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/testLock.ts +11 -0
- package/vona/src/suite/a-home/modules/home-user/src/.metadata/index.ts +21 -0
- package/vona/src/suite/a-home/modules/home-user/src/controller/passportTest.ts +21 -0
- package/vona/src/suite/a-home/modules/home-user/test/passportTest.test.ts +77 -0
- package/vona/src/suite-vendor/a-vona/modules/a-broadcast/package.json +2 -1
- package/vona/src/suite-vendor/a-vona/modules/a-broadcast/src/service/broadcast.ts +84 -23
- package/vona/src/suite-vendor/a-vona/modules/a-broadcast/src/types/broadcast.ts +7 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/src/lib/core.ts +8 -0
- package/vona/src/suite-vendor/a-vona/modules/a-executor/package.json +4 -2
- package/vona/src/suite-vendor/a-vona/modules/a-executor/src/service/executor.ts +68 -53
- package/vona/src/suite-vendor/a-vona/modules/a-orm/cli/model/metadata/generate.ts +10 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_cache.ts +12 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_crud_inner.ts +3 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_utils.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-queue/package.json +4 -2
- package/vona/src/suite-vendor/a-vona/modules/a-queue/src/service/queue.ts +103 -54
- package/vona/src/suite-vendor/a-vona/modules/a-queue/src/types/queue.ts +5 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/package.json +48 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/.metadata/index.ts +100 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/bean/interceptor.rateLimit.ts +72 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/index.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/main.ts +28 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/service/rateLimit.ts +75 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/types/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/src/types/rateLimit.ts +19 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-vona/modules/a-redis/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redis.ts +1 -2
- package/vona/src/suite-vendor/a-vona/modules/a-redis/src/types/redis.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/package.json +63 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/.metadata/index.ts +132 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/bean/bean.telemetry.ts +71 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/bean/middlewareSystem.trace.ts +73 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/config/config.ts +83 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/index.ts +2 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/lib/ingress.ts +34 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/monkey.ts +22 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/service/telemetry.ts +201 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/types/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/src/types/telemetry.ts +22 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/test/telemetry.test.ts +339 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-vona/modules/a-telemetry/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-vona/package.json +3 -1
- package/vona/src/suite-vendor/a-vona/tsconfig.json +3 -0
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.bin.buildRest.ts +3 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +87 -127
- package/zova/src/front/config/config/config.cabloyCommerce.ts +4 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/cli/openapi.config.ts +9 -1
- package/zova/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +16 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/src/api/commerceMemberAddress.ts +94 -50
- package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +162 -52
- package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +2656 -1298
- package/zova/src/suite/a-commerce/modules/commerce-member/src/apiSchema/commerceMemberAddress.ts +23 -13
- package/zova/src/suite/a-commerce/modules/commerce-member/src/model/addressMine.ts +67 -0
- package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +21 -11
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/schemas.ts +40 -32
- package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/types.ts +638 -633
- package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +6 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/component/tableCellActionShip.ts +31 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +248 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/page/checkout.ts +19 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/page/order.ts +19 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/page/orders.ts +19 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/page/payment.ts +19 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeOrder.ts +114 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradePayment.ts +36 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +75 -8
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +762 -46
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeOrder.ts +35 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradePayment.ts +13 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/bean/tableCell.actionShip.tsx +41 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionShip/controller.tsx +66 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +2 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +38 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/orderMine.ts +31 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/payment.ts +25 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +14 -3
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +124 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +105 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/orders/controller.tsx +95 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +88 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +28 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/command.delete.tsx +3 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionUpdate.tsx +2 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionView.tsx +1 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +4 -0
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
- package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressUpdate.tsx +0 -39
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.117
|
|
@@ -117,6 +117,7 @@ For deeper reference material, read:
|
|
|
117
117
|
- `references/contract-loop-map.md`
|
|
118
118
|
- `references/verification-checklist.md`
|
|
119
119
|
- `references/resource-custom-state-pattern.md`
|
|
120
|
+
- `../../../cabloy-docs/fullstack/admin-resource-and-web-self-service.md`
|
|
120
121
|
|
|
121
122
|
## Step 3: Identify the contract source of truth deliberately
|
|
122
123
|
|
|
@@ -237,9 +238,14 @@ After generation, inspect whether the frontend still needs follow-up in:
|
|
|
237
238
|
Keep frontend follow-up thin:
|
|
238
239
|
|
|
239
240
|
- use thin semantic model facades over generated consumers instead of re-declaring the contract
|
|
240
|
-
- if a custom endpoint still belongs to an existing
|
|
241
|
+
- if a custom endpoint still belongs to an existing Admin Resource state boundary, prefer one resource-state owner instead of letting a module-local model create a second cache tree
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
For one persisted domain with both Admin Resource and Web self-service consumers, choose the state boundary deliberately after backend contract truth and regeneration are established:
|
|
244
|
+
|
|
245
|
+
- **Admin/custom-resource branch:** the endpoint remains part of the Admin Resource authority, projection, and generic page state, so reuse `rest-resource.model.resource` through a thin semantic facade.
|
|
246
|
+
- **Separate Web self-service branch:** the consumer has different authority, server-derived owner scope, DTO projection, operation names, UX, or SSR behavior, so expose explicit self-service operations and use a dedicated Web model and purpose-built pages.
|
|
247
|
+
|
|
248
|
+
Share domain persistence and lifecycle logic, not necessarily HTTP projections or frontend state ownership. Read `../../../cabloy-docs/fullstack/admin-resource-and-web-self-service.md` for the complete architecture. Reuse the resource-owned custom state pattern in `references/resource-custom-state-pattern.md` for the Admin/custom-resource branch.
|
|
243
249
|
|
|
244
250
|
## Step 6: Keep edition-aware differences explicit
|
|
245
251
|
|
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
"prompt": "The frontend model and generated SDK in Cabloy Basic look stale after a backend response change, but I am not sure whether the real bug is in backend OpenAPI output or in skipped frontend regeneration. Diagnose the contract loop the Cabloy way.",
|
|
25
25
|
"expected_output": "Handles reverse stale-consumer detection, checks backend source-of-truth first, avoids hand-patching frontend types as the primary fix, and proposes regeneration or backend correction based on the contract path.",
|
|
26
26
|
"files": []
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 5,
|
|
30
|
+
"prompt": "One persisted Order domain needs read-only Admin select/view operations through presetResource and customer Web list/detail reads. Should Admin and Web share one API and ModelResource, or should I use mine/viewMine-style Web operations and a separate Web model? Include the Cabloy contract-loop steps.",
|
|
31
|
+
"expected_output": "Keeps one domain and persistence boundary while separating Admin select/view and explicit owner-scoped Web operations with audience-specific DTOs. Keeps ModelResource as the Admin Resource owner and accepts a dedicated Web model/pages only for the distinct self-service state. Requires backend-first OpenAPI inspection, constrained regeneration for all owned operations, and verification of Admin authorization, Web owner scope before pagination, SSR admission where relevant, and both consumer paths. Rejects forcing Web through presetResource and rejects a parallel Admin cache owner for an ordinary custom Admin action.",
|
|
32
|
+
"files": []
|
|
27
33
|
}
|
|
28
34
|
]
|
|
29
35
|
}
|
|
@@ -92,6 +92,19 @@ Practical check after generation:
|
|
|
92
92
|
- confirm the generated API files only contain the intended resource operations
|
|
93
93
|
- confirm the module metadata and exports were not polluted by unrelated APIs
|
|
94
94
|
|
|
95
|
+
### Dual-audience resource ownership
|
|
96
|
+
|
|
97
|
+
One persisted domain may deliberately own both conventional Admin Resource operations and explicit Web self-service operations. Include every intended operation in the constrained `operations.match` slice; this remains one forward-chain contract, not competing generated contracts or consumer drift.
|
|
98
|
+
|
|
99
|
+
After generation, choose the consumer shape by boundary:
|
|
100
|
+
|
|
101
|
+
| Consumer | Generated-contract follow-up |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| Admin custom operation with the same authority, projection, and Resource page semantics | thin module facade → `rest-resource.model.resource` → schema-driven Resource UI |
|
|
104
|
+
| Web self-service operation with different authority, server-derived owner scope, DTO projection, or page/SSR semantics | dedicated Web model → purpose-built self-service pages |
|
|
105
|
+
|
|
106
|
+
The Web model is valid only for the separate self-service state domain. It must not become a parallel owner for Admin `select`/`view`, schemas, permissions, or generic Resource page state. See `../../../../cabloy-docs/fullstack/admin-resource-and-web-self-service.md` for the complete architecture.
|
|
107
|
+
|
|
95
108
|
## Forward chain artifact map
|
|
96
109
|
|
|
97
110
|
1. backend contract source
|
|
@@ -14,13 +14,13 @@ Typical examples:
|
|
|
14
14
|
|
|
15
15
|
## The ownership rule
|
|
16
16
|
|
|
17
|
-
Prefer one owner for all
|
|
17
|
+
Prefer one owner for all server state in the same Admin Resource boundary.
|
|
18
18
|
|
|
19
|
-
In this codebase, the preferred owner is:
|
|
19
|
+
In this codebase, the preferred Admin Resource owner is:
|
|
20
20
|
|
|
21
21
|
- `rest-resource.model.resource`
|
|
22
22
|
|
|
23
|
-
Do not let a module-local model become a second state owner for the same resource rows unless the boundary is explicitly intentional.
|
|
23
|
+
Do not let a module-local model become a second state owner for the same Admin resource rows unless the boundary is explicitly intentional.
|
|
24
24
|
|
|
25
25
|
## The split to avoid
|
|
26
26
|
|
|
@@ -136,6 +136,19 @@ A separate owner may still be fine when the data is not really part of the resou
|
|
|
136
136
|
|
|
137
137
|
Even then, be explicit about the boundary.
|
|
138
138
|
|
|
139
|
+
## Admin Resource and Web self-service boundary
|
|
140
|
+
|
|
141
|
+
A dedicated Web model is also valid when one persisted domain exposes a genuinely separate, customer-scoped self-service contract.
|
|
142
|
+
|
|
143
|
+
Use that branch only when all of these are true:
|
|
144
|
+
|
|
145
|
+
- the Web operations and DTOs are distinct from Admin `select`/`view` semantics;
|
|
146
|
+
- the server derives owner scope from the authenticated user and preserves normal instance scope;
|
|
147
|
+
- the Web model owns only its self-service query keys and purpose-built page state, not Admin Resource schemas, permissions, or generic page state;
|
|
148
|
+
- Web UX or private SSR behavior needs a dedicated admission and post-hydration boundary.
|
|
149
|
+
|
|
150
|
+
This does not justify a parallel owner for a custom Admin endpoint. It establishes a separate Web state domain beside the Admin Resource boundary. Read `../../../../cabloy-docs/fullstack/admin-resource-and-web-self-service.md` for the complete cross-stack pattern.
|
|
151
|
+
|
|
139
152
|
## Quick checklist
|
|
140
153
|
|
|
141
154
|
1. confirm whether the resource pages already use `rest-resource.model.resource`
|
|
@@ -21,6 +21,18 @@ After a contract-loop change, verify the branch that actually applies.
|
|
|
21
21
|
- `npm run tsc`
|
|
22
22
|
- `npm run build`
|
|
23
23
|
|
|
24
|
+
### Dual-audience resource verification
|
|
25
|
+
|
|
26
|
+
When one persisted domain has both Admin Resource and Web self-service consumers, also verify:
|
|
27
|
+
|
|
28
|
+
- every intended Admin and Web operation appears in emitted OpenAPI and the constrained generated SDK slice
|
|
29
|
+
- Admin controller authorization and the intended read-only or CRUD action surface remain independent of menu visibility
|
|
30
|
+
- Web requests cannot choose owner or tenant scope; the service applies owner and visibility predicates before count, sorting, and pagination
|
|
31
|
+
- Web DTOs omit owner, tenant, and operational-only fields
|
|
32
|
+
- Admin custom state remains in `ModelResource`; a dedicated Web model owns only the distinct self-service cache domain
|
|
33
|
+
- private Web SSR keeps a hydration-equivalent neutral shell until an explicit admission or post-hydration boundary
|
|
34
|
+
- targeted Admin and authenticated Web flow checks cover both audiences when both contracts changed
|
|
35
|
+
|
|
24
36
|
## Reverse chain verification
|
|
25
37
|
|
|
26
38
|
- frontend-owned source is correct
|
|
@@ -35,16 +35,12 @@ jobs:
|
|
|
35
35
|
- name: init
|
|
36
36
|
run: npm run init
|
|
37
37
|
- name: run Commerce lifecycle gate
|
|
38
|
-
run:
|
|
39
|
-
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test --
|
|
40
|
-
commerce-
|
|
41
|
-
commerce-trade/test/
|
|
42
|
-
commerce-trade/test/
|
|
43
|
-
commerce-
|
|
44
|
-
commerce-payment/test/paymentAttempt.test.ts
|
|
45
|
-
commerce-trade/test/checkoutReservation.test.ts
|
|
46
|
-
commerce-trade/test/reservationExpiry.test.ts
|
|
47
|
-
--flavor=normal
|
|
38
|
+
run: |
|
|
39
|
+
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test -- commerce-trade/test/stockBalance.test.ts commerce-trade/test/stockReservation.test.ts commerce-trade/test/stockAudit.test.ts --flavor=normal
|
|
40
|
+
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test -- commerce-promotion/test/couponReservation.test.ts commerce-payment/test/paymentAttempt.test.ts --flavor=normal
|
|
41
|
+
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test -- commerce-trade/test/checkoutReservation.test.ts commerce-trade/test/checkoutTransaction.test.ts --flavor=normal
|
|
42
|
+
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test -- commerce-trade/test/orderSnapshot.test.ts --flavor=normal
|
|
43
|
+
DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run vona :bin:test -- commerce-trade/test/reservationExpiry.test.ts --flavor=normal
|
|
48
44
|
working-directory: vona
|
|
49
45
|
- run: DATABASE_DEFAULT_CLIENT=mysql DATABASE_CLIENT_MYSQL_HOST=127.0.0.1 DATABASE_CLIENT_MYSQL_PASSWORD=root npm run test
|
|
50
46
|
working-directory: vona
|
|
@@ -35,16 +35,15 @@ jobs:
|
|
|
35
35
|
- name: init
|
|
36
36
|
run: npm run init
|
|
37
37
|
- name: run Commerce stock contention gate
|
|
38
|
-
run:
|
|
39
|
-
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test --
|
|
40
|
-
commerce-
|
|
41
|
-
commerce-trade/test/
|
|
42
|
-
commerce-trade/test/
|
|
43
|
-
commerce-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
--flavor=normal
|
|
38
|
+
run: |
|
|
39
|
+
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/stockBalance.test.ts commerce-trade/test/stockReservation.test.ts commerce-trade/test/stockAudit.test.ts --flavor=normal
|
|
40
|
+
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-promotion/test/couponReservation.test.ts commerce-payment/test/paymentAttempt.test.ts --flavor=normal
|
|
41
|
+
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/checkoutReservation.test.ts commerce-trade/test/checkoutTransaction.test.ts --flavor=normal
|
|
42
|
+
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/orderSnapshot.test.ts --flavor=normal
|
|
43
|
+
DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/reservationExpiry.test.ts --flavor=normal
|
|
44
|
+
working-directory: vona
|
|
45
|
+
- name: run Commerce payment and shipment lifecycle gate
|
|
46
|
+
run: DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts --flavor=normal
|
|
48
47
|
working-directory: vona
|
|
49
48
|
- run: DATABASE_DEFAULT_CLIENT=pg npm run test
|
|
50
49
|
working-directory: vona
|
|
@@ -27,6 +27,9 @@ jobs:
|
|
|
27
27
|
version: 11.5.2
|
|
28
28
|
- name: init
|
|
29
29
|
run: npm run init
|
|
30
|
+
- name: run Commerce payment and shipment lifecycle gate
|
|
31
|
+
run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run vona :bin:test -- commerce-trade/test/paymentOutcome.test.ts commerce-trade/test/reservationExpiry.test.ts commerce-trade/test/shipment.test.ts --flavor=normal
|
|
32
|
+
working-directory: vona
|
|
30
33
|
- run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run test
|
|
31
34
|
working-directory: vona
|
|
32
35
|
- run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run build -- --flavor=ci
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.117
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add detailed CLI output.
|
|
8
|
+
- Add telemetry support, including secure ingress propagation.
|
|
9
|
+
- Add tracing capabilities.
|
|
10
|
+
- Add Redis-backed rate limiting.
|
|
11
|
+
- Add dual-audience resource guidance.
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
- Fix REST module subpath bundle declarations.
|
|
16
|
+
- Complete Commerce payment flow validation and evidence coverage.
|
|
17
|
+
|
|
18
|
+
### Improvements
|
|
19
|
+
|
|
20
|
+
- Simplify the Redis rate-limit command implementation.
|
|
21
|
+
- Clarify the Vona AOP request path documentation.
|
|
22
|
+
- Update dependency lockfiles and package metadata.
|
|
23
|
+
|
|
24
|
+
## 5.1.116
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
- Update platform capabilities and integrations.
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
- Stabilize Commerce trade transactions and associated tests.
|
|
33
|
+
|
|
34
|
+
### Improvements
|
|
35
|
+
|
|
36
|
+
- Update address ownership test coverage.
|
|
37
|
+
|
|
3
38
|
## 5.1.115
|
|
4
39
|
|
|
5
40
|
### Improvements
|
package/CLAUDE.md
CHANGED
|
@@ -46,6 +46,7 @@ Before inventing a custom implementation path:
|
|
|
46
46
|
- Treat contract-loop work as one of four branches: forward chain, reverse chain, consumer drift, or local dependency drift.
|
|
47
47
|
- For the forward chain, change backend contract truth first and regenerate frontend consumers rather than hand-patching them.
|
|
48
48
|
- After forward regeneration, keep frontend follow-up thin: prefer semantic model facades and reuse the existing resource-owner when the custom API still belongs to the same resource.
|
|
49
|
+
- When one persisted business resource serves both Admin Resource and Web self-service consumers, keep one domain and persistence boundary; where audience authority or experience differs, split API/DTO contracts, server-side scope, frontend state ownership, and page architecture—reuse the generic Resource owner for Admin and use dedicated state/pages for a genuinely distinct Web self-service contract.
|
|
49
50
|
- For the reverse chain, always run the relevant Zova build first, then run `npm run deps:vona`: use `npm run build:zova:admin` for Admin changes, and also run `npm run build:zova:web` when the Web flavor is affected. Do not treat `build:rest:*` alone as sufficient, because the SSR bundle and rest output must move together.
|
|
50
51
|
- If the generated `.zova-rest` artifacts already contain the expected changes but Vona consumers still see stale types after `npm run deps:vona`, treat it as local dependency drift: delete `vona/node_modules` and reinstall dependencies before further debugging or hand-patching dependency links.
|
|
51
52
|
- For Cabloy Start, apply the same reverse-chain logic but resolve the Start-specific flavor names and generated-output paths from the active Start repo before recommending commands.
|
|
@@ -57,6 +58,7 @@ Before inventing a custom implementation path:
|
|
|
57
58
|
- Default to establishing such query state during render. Use `disableSuspenseOnInit: true` only for relatively stable query-backed state when you want to skip the init-time `query.suspense()` kick; it does not prevent query creation, fetches, or hydration-time rendering. If strict readiness is needed later, wait explicitly at the interaction boundary.
|
|
58
59
|
- In SSR, keep server HTML and the client's hydration-time initial render equivalent. When server rendering intentionally omits private, cookie-unavailable, or browser-only state, keep the same neutral shell or placeholder through hydration and defer its query/load/render branch to an explicit post-hydration, admission, mounted, or interaction boundary.
|
|
59
60
|
- Keep repo-wide AI rules in `CLAUDE.md` short and durable; put branching Zova analysis workflows in `.claude/skills/`.
|
|
61
|
+
- Do not modify `APP_NAME` in environment files unless the user explicitly requests it. When another worktree holds shared test resources, wait and retry later rather than changing application identity or related environment settings.
|
|
60
62
|
- For SSR theme-sensitive frontend work, detect the active edition marker and UI library before making assumptions. Cabloy Basic currently means DaisyUI + Tailwind CSS assumptions; Cabloy Start currently means Vuetify assumptions.
|
|
61
63
|
- In Web SSR without cookie-backed theme resolution, do not treat server reads of `$theme.dark`, `$theme.darkMode`, or `$token` as final browser truth. Keep theme-sensitive SSR branching hydration-tolerant or defer final theme-sensitive decisions to the client.
|
|
62
64
|
- Do not assume Cabloy Basic and Cabloy Start use the same adapter-level SSR theme handoff. Verify the active theme handler and client hydration path before changing SSR theme behavior.
|
|
@@ -88,6 +88,10 @@ const fullstackGroups = [
|
|
|
88
88
|
{ text: 'Framework Performance', link: '/fullstack/framework-performance' },
|
|
89
89
|
{ text: 'Vona + Zova Integration', link: '/fullstack/vona-zova-integration' },
|
|
90
90
|
{ text: 'Contract Loop Playbook', link: '/fullstack/contract-loop-playbook' },
|
|
91
|
+
{
|
|
92
|
+
text: 'Admin Resource and Web Self-Service',
|
|
93
|
+
link: '/fullstack/admin-resource-and-web-self-service',
|
|
94
|
+
},
|
|
91
95
|
{
|
|
92
96
|
text: 'Backend Metadata to Frontend Table Actions',
|
|
93
97
|
link: '/fullstack/backend-metadata-to-frontend-table-actions',
|
|
@@ -225,6 +229,7 @@ export default defineConfig({
|
|
|
225
229
|
items: [
|
|
226
230
|
{ text: 'Auth Guide', link: '/backend/auth-guide' },
|
|
227
231
|
{ text: 'Captcha Guide', link: '/backend/captcha-guide' },
|
|
232
|
+
{ text: 'Rate Limit Guide', link: '/backend/rate-limit-guide' },
|
|
228
233
|
{ text: 'User Access Guide', link: '/backend/user-access-guide' },
|
|
229
234
|
{ text: 'JWT Guide', link: '/backend/jwt-guide' },
|
|
230
235
|
{ text: 'Validation Guide', link: '/backend/validation-guide' },
|
|
@@ -238,6 +243,7 @@ export default defineConfig({
|
|
|
238
243
|
{ text: 'Error Guide', link: '/backend/error-guide' },
|
|
239
244
|
{ text: 'Event Guide', link: '/backend/event-guide' },
|
|
240
245
|
{ text: 'Logger Guide', link: '/backend/logger-guide' },
|
|
246
|
+
{ text: 'Telemetry Guide', link: '/backend/telemetry-guide' },
|
|
241
247
|
{ text: 'Upload Guide', link: '/backend/upload-guide' },
|
|
242
248
|
{ text: 'Image Guide', link: '/backend/image-guide' },
|
|
243
249
|
{ text: 'File Guide', link: '/backend/file-guide' },
|
|
@@ -143,6 +143,13 @@ Use this quick rule:
|
|
|
143
143
|
- full explanation → [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
|
|
144
144
|
- workflow steering → skill and rules can point to the docs and choose the right CLI path
|
|
145
145
|
|
|
146
|
+
### Example: “How should one persisted resource serve Admin and Web users?”
|
|
147
|
+
|
|
148
|
+
- full architecture explanation → [Admin Resource and Web Self-Service](/fullstack/admin-resource-and-web-self-service)
|
|
149
|
+
- concise repo-wide default → `CLAUDE.md`
|
|
150
|
+
- contract regeneration, consumer-boundary choice, and verification routing → `cabloy-contract-loop`
|
|
151
|
+
- extend the existing contract-loop skill references and evals; do not create an Order-specific skill
|
|
152
|
+
|
|
146
153
|
### Example: “How should AI remove an existing module cleanly?”
|
|
147
154
|
|
|
148
155
|
- public operational explanation → [Playbook: Module Removal](/ai/playbook-module-removal)
|
|
@@ -28,39 +28,31 @@ These families work together to shape request execution, parameter handling, err
|
|
|
28
28
|
|
|
29
29
|
## Execution model
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Middleware and interceptors use onion-style execution: they can run logic before `next()` and after downstream controller work returns. Guards check access preconditions, pipes transform or validate request values, and filters handle exceptions on the error path.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
- **interceptor**
|
|
35
|
-
|
|
36
|
-
That means they can run logic both before and after the controller action.
|
|
37
|
-
|
|
38
|
-
Other controller AOP families participate in more specialized stages:
|
|
39
|
-
|
|
40
|
-
- **guard** checks access or execution preconditions
|
|
41
|
-
- **pipe** transforms or validates request values
|
|
42
|
-
- **filter** handles exceptions and logging behavior
|
|
33
|
+
For a matched controller route, the inbound order is:
|
|
43
34
|
|
|
44
|
-
|
|
35
|
+
1. **system middleware** before route matching
|
|
36
|
+
2. **route matching**
|
|
37
|
+
3. **global middleware** after a route matches and before authentication
|
|
38
|
+
4. **guards**, including Passport authentication and authorization
|
|
39
|
+
5. **interceptors**
|
|
40
|
+
6. **argument extraction and pipes**
|
|
41
|
+
7. **local middleware**
|
|
42
|
+
8. **controller action**
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
2. route matching happens
|
|
48
|
-
3. global and local middleware wrap the matched route
|
|
49
|
-
4. guards enforce access preconditions
|
|
50
|
-
5. pipes transform and validate incoming values
|
|
51
|
-
6. interceptors wrap controller execution
|
|
52
|
-
7. the controller action runs
|
|
53
|
-
8. filters handle thrown exceptions and logging customization when failures occur
|
|
44
|
+
After the action, middleware and interceptor after-`next()` work unwinds in reverse: local middleware, interceptors, then global middleware. Filters are not another successful inbound stage; they compose the exception-handling path when request execution throws.
|
|
54
45
|
|
|
55
|
-
|
|
46
|
+
Choose the family by the state the concern needs: use system middleware for route-independent transport behavior, global middleware for matched-route work before authentication, a global interceptor for post-auth admission before body parsing, and local middleware for action-side wrapping after pipes.
|
|
56
47
|
|
|
57
48
|
## System, global, and local scope
|
|
58
49
|
|
|
59
50
|
Controller AOP also varies by scope:
|
|
60
51
|
|
|
61
|
-
- **system
|
|
62
|
-
- **global**
|
|
63
|
-
- **local**
|
|
52
|
+
- **system middleware** runs before route matching and has no matched-route metadata
|
|
53
|
+
- **global middleware** is auto-loaded after route matching but before guards; it can use runtime filters such as `match`, `ignore`, `mode`, or `flavor`
|
|
54
|
+
- **local middleware** is attached directly to a controller class or action and runs after pipes, immediately around the action-side suffix
|
|
55
|
+
- **global/local guards, interceptors, pipes, and filters** remain in their own family stages rather than sharing middleware placement
|
|
64
56
|
|
|
65
57
|
Built-in aspects and shorthand decorators sit on top of the same general model.
|
|
66
58
|
|
|
@@ -77,6 +77,30 @@ Start here when your question is about:
|
|
|
77
77
|
- if the question is “which scripts drive normal backend dev/build/start/test workflows?” -> read [Backend Scripts](/backend/scripts)
|
|
78
78
|
- if the question is “which source files should I open first?” -> read [Vona Source Reading Map](/backend/vona-source-reading-map)
|
|
79
79
|
|
|
80
|
+
## Topic cluster: controller request path and controller AOP
|
|
81
|
+
|
|
82
|
+
Start here when your question is about:
|
|
83
|
+
|
|
84
|
+
- what runs before route matching, after route matching, or after Passport authentication
|
|
85
|
+
- whether a concern belongs in system/global/local middleware, a guard, an interceptor, a pipe, or a filter
|
|
86
|
+
- why a global-onion decorator configures a route without changing its execution stage
|
|
87
|
+
- how controller/action options merge with application, instance, and dynamic onion configuration
|
|
88
|
+
- why an error is transformed or logged through filters
|
|
89
|
+
|
|
90
|
+
### Concept pages first
|
|
91
|
+
|
|
92
|
+
- [AOP Overview](/backend/aop-overview)
|
|
93
|
+
- [Controller Guide](/backend/controller-guide)
|
|
94
|
+
- [Controller AOP Guide](/backend/controller-aop-guide)
|
|
95
|
+
- [Rate Limit Guide](/backend/rate-limit-guide) when request admission or quotas are involved
|
|
96
|
+
|
|
97
|
+
### Best next step
|
|
98
|
+
|
|
99
|
+
- if the question is “how can this run before route matching?” -> read the system-middleware path in [Vona Source Reading Map](/backend/vona-source-reading-map)
|
|
100
|
+
- if the question is “does this run before or after Passport?” -> read the controller request pipeline path in [Vona Source Reading Map](/backend/vona-source-reading-map)
|
|
101
|
+
- if the question is “why does a route override retain other settings?” -> trace onion option composition in [Vona Source Reading Map](/backend/vona-source-reading-map)
|
|
102
|
+
- if the question is “why is an error transformed or logged?” -> follow the filter error-path follow-up in [Vona Source Reading Map](/backend/vona-source-reading-map)
|
|
103
|
+
|
|
80
104
|
## Topic cluster: resource and module CRUD chain
|
|
81
105
|
|
|
82
106
|
Start here when your question is about:
|
|
@@ -110,11 +134,11 @@ It does **not** try to become the new entry page for every backend subsystem suc
|
|
|
110
134
|
- workers
|
|
111
135
|
- queues
|
|
112
136
|
- websockets
|
|
113
|
-
- auth
|
|
137
|
+
- the complete auth subsystem
|
|
114
138
|
- cache
|
|
115
139
|
- distributed runtime internals
|
|
116
140
|
|
|
117
|
-
Those topics already have their own concept docs. This roadmap is only the first navigation layer for common source-reading tasks.
|
|
141
|
+
The controller request-path cluster covers where guards and Passport sit in route execution, not the complete authentication subsystem. Those topics already have their own concept docs. This roadmap is only the first navigation layer for common source-reading tasks.
|
|
118
142
|
|
|
119
143
|
## Final rule
|
|
120
144
|
|
|
@@ -32,15 +32,25 @@ These commands all go through the shared `:create:bean` entrypoint, but they tar
|
|
|
32
32
|
|
|
33
33
|
### Scope variants
|
|
34
34
|
|
|
35
|
-
- **system middleware** runs before route matching
|
|
36
|
-
- **global middleware** is auto-loaded and
|
|
37
|
-
- **local middleware** is attached directly to a controller class or action
|
|
35
|
+
- **system middleware** runs before route matching and has no matched-route metadata
|
|
36
|
+
- **global middleware** is auto-loaded after a route matches but before guards and Passport authentication
|
|
37
|
+
- **local middleware** is attached directly to a controller class or action and runs after pipes, around the action-side suffix
|
|
38
|
+
|
|
39
|
+
### Inbound execution order
|
|
40
|
+
|
|
41
|
+
For a matched controller route, Vona enters these stages in order:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
system middleware → route matching → global middleware → guard → interceptor → pipe → local middleware → action
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Middleware and interceptors use onion execution. Their after-`next()` work unwinds in reverse, so local middleware returns before interceptors, which return before global middleware. Filters are selected from the error path when request execution throws; they are not a normal inbound stage.
|
|
38
48
|
|
|
39
49
|
### Why system middleware is different
|
|
40
50
|
|
|
41
51
|
System middleware executes before route matching, so it is the earliest controller-facing interception stage.
|
|
42
52
|
|
|
43
|
-
This is where Vona places concerns such as not-found handling, request override behavior, app initialization, instance initialization, HTTP logging, CORS, and static-resource handling.
|
|
53
|
+
This is where Vona places concerns such as not-found handling, request override behavior, app initialization, instance initialization, HTTP logging, CORS, and static-resource handling. Use global middleware instead when the concern needs a matched route but must still run before authentication. Use local middleware when the concern is controller/action-specific and should see pipe-processed arguments.
|
|
44
54
|
|
|
45
55
|
### Representative local usage
|
|
46
56
|
|
|
@@ -54,6 +64,8 @@ This is where Vona places concerns such as not-found handling, request override
|
|
|
54
64
|
@Aspect.middlewareGlobal('training-student:logger', { prefix: 'elapsed' })
|
|
55
65
|
```
|
|
56
66
|
|
|
67
|
+
`@Aspect.middlewareGlobal(...)` writes route options for an already registered global middleware. It does not add a local middleware or change that middleware’s pre-guard execution stage. The same distinction applies to `@Aspect.interceptorGlobal(...)`: it configures an existing global interceptor for the controller/action while the interceptor remains in the post-guard stage.
|
|
68
|
+
|
|
57
69
|
### Representative built-in usage
|
|
58
70
|
|
|
59
71
|
```typescript
|
|
@@ -125,8 +137,10 @@ Typical jobs include:
|
|
|
125
137
|
|
|
126
138
|
### Scope variants
|
|
127
139
|
|
|
128
|
-
- **global interceptor** is auto-loaded and
|
|
129
|
-
- **local interceptor** is attached directly to a controller class or action
|
|
140
|
+
- **global interceptor** is auto-loaded after guards and before pipes, local middleware, and controller execution
|
|
141
|
+
- **local interceptor** is attached directly to a controller class or action, runs inside global interceptors, and still precedes pipes
|
|
142
|
+
|
|
143
|
+
A global interceptor is the correct placement for post-auth admission that must reject before body parsing. For example, [`a-ratelimit:rateLimit`](/backend/rate-limit-guide) uses Passport-resolved identity, then runs before the request-body interceptor, pipes, local middleware, and action code.
|
|
130
144
|
|
|
131
145
|
### Representative local usage
|
|
132
146
|
|
|
@@ -140,7 +154,7 @@ Built-in interceptors can be used for framework-level response behavior, such as
|
|
|
140
154
|
|
|
141
155
|
## Pipe
|
|
142
156
|
|
|
143
|
-
Pipes transform or validate request values before
|
|
157
|
+
Pipes transform or validate request values after interceptor entry and before local middleware and controller logic.
|
|
144
158
|
|
|
145
159
|
### Scope variants
|
|
146
160
|
|
|
@@ -190,7 +204,7 @@ For broader validation guidance, see [Validation Guide](/backend/validation-guid
|
|
|
190
204
|
|
|
191
205
|
## Filter
|
|
192
206
|
|
|
193
|
-
Filters handle exceptions and logging behavior.
|
|
207
|
+
Filters handle exceptions and logging behavior on the error path. They are selected when request execution throws, rather than forming another successful inbound stage after the action.
|
|
194
208
|
|
|
195
209
|
This is where request-path error customization becomes explicit.
|
|
196
210
|
|
|
@@ -230,15 +244,25 @@ Most controller aspect families support the same configuration ideas:
|
|
|
230
244
|
|
|
231
245
|
That consistency is one of the most important reasons controller AOP stays scalable in Vona.
|
|
232
246
|
|
|
233
|
-
###
|
|
247
|
+
### Global-onion override and option ownership
|
|
248
|
+
|
|
249
|
+
A local use decorator such as `@Aspect.interceptor(...)` adds an onion to the local execution chain. A global use decorator such as `@Aspect.interceptorGlobal(...)` or `@Core.rateLimit(...)` instead writes controller/action route options for the named, already-global onion. It does not move that onion into a local stage.
|
|
250
|
+
|
|
251
|
+
Put concern-specific fields under the exact onion that consumes them. For example, `rateLimit` belongs to `a-ratelimit:rateLimit` options; putting that object in another middleware or interceptor’s options does not transfer the quota policy or activate the limiter.
|
|
234
252
|
|
|
235
|
-
|
|
253
|
+
### Effective option precedence
|
|
236
254
|
|
|
237
|
-
-
|
|
238
|
-
|
|
239
|
-
|
|
255
|
+
For normal object-valued onion options, lower-precedence values are deep-merged with later values in this order:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
aspect defaults and application config.onions
|
|
259
|
+
→ active-instance config.onions
|
|
260
|
+
→ controller route options
|
|
261
|
+
→ action route options
|
|
262
|
+
→ controlled dynamic request or test overrides
|
|
263
|
+
```
|
|
240
264
|
|
|
241
|
-
|
|
265
|
+
An action can therefore override only `rateLimit.limit` and inherit the rest of the controller or application policy. An action `enable: false` is an explicit exemption from a globally or controller-enabled onion. Primitive option scenes use the highest-precedence value instead of deep merging.
|
|
242
266
|
|
|
243
267
|
### Representative inspect patterns
|
|
244
268
|
|
|
@@ -210,6 +210,7 @@ Logging is closely connected to:
|
|
|
210
210
|
|
|
211
211
|
- [Internal AOP Guide](/backend/internal-aop-guide) through built-in AOP helpers such as `@Core.log(...)`
|
|
212
212
|
- [Runtime and Flavors](/backend/runtime-and-flavors) because log location, level, and operational behavior often vary by environment
|
|
213
|
+
- [Telemetry Guide](/backend/telemetry-guide) for distributed trace and span correlation in log entries; continue to use `$logger` and `$loggerChild(...)` as the logging APIs
|
|
213
214
|
|
|
214
215
|
## Implementation checks for backend logging changes
|
|
215
216
|
|
|
@@ -73,7 +73,7 @@ In the current repo implementation, the out-of-the-box menu controller is public
|
|
|
73
73
|
- Omit `roles`, or use `roles: []`, to make an item visible to anonymous and authenticated callers.
|
|
74
74
|
- A nonempty `roles` array is visible when the current Passport has at least one matching role name.
|
|
75
75
|
- `roles` is server-only declaration metadata. It is filtered out before the API response and is not part of `IMenuItem`, OpenAPI, or generated frontend clients.
|
|
76
|
-
- This controls navigation disclosure only. It never grants access to a page, controller action, API, or resource; those boundaries retain their own route and Passport/permission guards.
|
|
76
|
+
- This controls navigation disclosure only. It never grants access to a page, controller action, API, or resource; those boundaries retain their own route and Passport/permission guards. For the fullstack pattern that pairs an Admin `presetResource` entry with independently authorized Admin APIs and separately scoped Web self-service APIs, see [Admin Resource and Web Self-Service](/fullstack/admin-resource-and-web-self-service).
|
|
77
77
|
|
|
78
78
|
SSR Site menu definitions are cached structurally by Site and locale. The framework keeps static role policy in that prepared cache, then creates a filtered response for each request without mutating the cached definition.
|
|
79
79
|
|
|
@@ -29,6 +29,32 @@ await this.scope.model.post.delete({ id: 1 });
|
|
|
29
29
|
|
|
30
30
|
These operations are the clearest fit when the caller already knows the exact write intent.
|
|
31
31
|
|
|
32
|
+
## Field presence in insert and update
|
|
33
|
+
|
|
34
|
+
For normal writable fields, insert and update intentionally interpret field presence differently:
|
|
35
|
+
|
|
36
|
+
| Field state | `insert` | `update` |
|
|
37
|
+
| -------------------------- | -------------------------------------------------- | -------------------------------- |
|
|
38
|
+
| Field is absent | Leave the column to normal insert/default behavior | Leave the stored value unchanged |
|
|
39
|
+
| Own field with `undefined` | Leave the column to normal insert/default behavior | Write SQL `NULL` |
|
|
40
|
+
| `null` | Write SQL `NULL` | Write SQL `NULL` |
|
|
41
|
+
|
|
42
|
+
An own `undefined` means the property exists on the JavaScript object but its value is `undefined`, such as `{ title: undefined }`. This lets an update distinguish an omitted field from an intentional clear:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
await this.scope.model.post.update({
|
|
46
|
+
id,
|
|
47
|
+
title: undefined, // write SQL NULL
|
|
48
|
+
// stars is absent, so its stored value is unchanged
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### JSON columns
|
|
53
|
+
|
|
54
|
+
JSON columns follow the same field-presence rules. Non-null values are persisted as JSON. An explicit `null` writes SQL `NULL`, not a JSON literal `null`. On update, an own `undefined` also clears the column to SQL `NULL`.
|
|
55
|
+
|
|
56
|
+
A JSON request body normally omits object properties whose value is `undefined`. This distinction therefore most often applies to in-process TypeScript model calls or server-side payload construction.
|
|
57
|
+
|
|
32
58
|
## Conditional update and delete paths
|
|
33
59
|
|
|
34
60
|
Write operations do not have to target one row only by primary key.
|
|
@@ -56,7 +82,7 @@ await this.scope.model.post.delete({
|
|
|
56
82
|
});
|
|
57
83
|
```
|
|
58
84
|
|
|
59
|
-
That matters because the mutation layer still participates in the same structured query language used by select operations.
|
|
85
|
+
That matters because the mutation layer still participates in the same structured query language used by select operations. In `options.where`, an absent field, an own `undefined`, and `Op.omit` all omit that condition; `null` requests SQL `IS NULL`. See [ORM Select Guide](/backend/orm-select-guide#absent-undefined-and-null-in-where) for the detailed query contract.
|
|
60
86
|
|
|
61
87
|
## Bulk mutation operations
|
|
62
88
|
|
|
@@ -87,9 +113,12 @@ Instead of forcing callers to choose insert/update/delete up front, Vona can inf
|
|
|
87
113
|
|
|
88
114
|
Representative logic:
|
|
89
115
|
|
|
90
|
-
-
|
|
91
|
-
- `id`
|
|
92
|
-
- `id`
|
|
116
|
+
- `id` absent, `undefined`, or `null` → insert
|
|
117
|
+
- non-nullish `id` → update
|
|
118
|
+
- non-nullish `id` and `deleted: true` → delete
|
|
119
|
+
- `deleted: true` without a non-nullish `id` → ignore the item because no deletion target exists
|
|
120
|
+
|
|
121
|
+
A non-nullish identity is neither `null` nor `undefined`. `mutate` chooses by that usable identity value, not merely by whether the `id` key exists.
|
|
93
122
|
|
|
94
123
|
Representative pattern:
|
|
95
124
|
|
|
@@ -182,13 +211,10 @@ The important rule is:
|
|
|
182
211
|
|
|
183
212
|
There is also a useful argument-handling rule to remember:
|
|
184
213
|
|
|
185
|
-
- default `eq` magic methods such as `getByName()` or `selectByName()` treat an omitted argument as `null`
|
|
186
|
-
- non-`eq` magic methods such as `getByNameEqI()` require
|
|
187
|
-
|
|
188
|
-
This aligns magic-method behavior with the current ORM `where` semantics:
|
|
214
|
+
- default `eq` magic methods such as `getByName()` or `selectByName()` treat an omitted or `undefined` argument as `null`
|
|
215
|
+
- non-`eq` magic methods such as `getByNameEqI()` require a concrete value and throw when the argument is omitted or `undefined`
|
|
189
216
|
|
|
190
|
-
|
|
191
|
-
- `null` means SQL `IS NULL`
|
|
217
|
+
This is argument adaptation performed by the convenience method. It differs from a direct structured `where` object, where `{ name: undefined }` omits the condition. Use `Op.omit` to make direct condition omission explicit, and use `null` to request SQL `IS NULL`.
|
|
192
218
|
|
|
193
219
|
That means mutation should stay conceptually grounded in the standard model methods even when convenience wrappers are present.
|
|
194
220
|
|