cabloy 5.1.131 → 5.1.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-worktree-environment/SKILL.md +6 -6
  3. package/.claude/skills/cabloy-worktree-environment/evals/evals.json +2 -2
  4. package/.github/workflows/vona-test-pg.yml +1 -1
  5. package/.github/workflows/vona-test-sqlite3.yml +1 -1
  6. package/CHANGELOG.md +28 -0
  7. package/cabloy-docs/backend/dto-guide.md +39 -4
  8. package/cabloy-docs/backend/dto-infer-generation.md +29 -0
  9. package/cabloy-docs/backend/openapi-guide.md +16 -0
  10. package/cabloy-docs/backend/validation-guide.md +45 -0
  11. package/cabloy-docs/frontend/environment-config-guide.md +8 -15
  12. package/cabloy-docs/frontend/navigation-guards-guide.md +3 -3
  13. package/cabloy-docs/frontend/page-route-guide.md +4 -4
  14. package/cabloy-docs/frontend/ssr-env.md +22 -29
  15. package/cabloy-docs/frontend/theme-guide.md +4 -4
  16. package/cabloy-docs/frontend/zova-router-under-the-hood.md +1 -1
  17. package/cabloy-docs/fullstack/deploy-cloudflare-docker.md +6 -6
  18. package/cabloy-docs/fullstack/parallel-worktree-environment.md +2 -2
  19. package/e2e/specs/a-commerce/commerce.spec.ts +479 -17
  20. package/e2e/specs/cabloy-basic/basic.spec.ts +30 -5
  21. package/package.json +7 -7
  22. package/vona/.claude/skills/detect-ssr-leak/SKILL.md +1 -1
  23. package/vona/packages-vona/vona/package.json +1 -1
  24. package/vona/pnpm-lock.yaml +120 -10
  25. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuCreate.tsx +3 -1
  26. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuSelectResItem.tsx +13 -1
  27. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuUpdate.tsx +3 -1
  28. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuView.tsx +13 -1
  29. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/entity/sku.tsx +6 -0
  30. package/vona/src/suite/a-commerce/modules/commerce-catalog/test/skuPresentation.test.ts +227 -0
  31. package/vona/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +46 -36
  32. package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressSelectResItem.tsx +5 -13
  33. package/vona/src/suite/a-commerce/modules/commerce-member/src/service/address.ts +16 -1
  34. package/vona/src/suite/a-commerce/modules/commerce-member/test/addressPresentation.test.ts +210 -0
  35. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +13 -2
  36. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +12 -0
  37. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/service/coupon.ts +7 -1
  38. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +45 -2
  39. package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +23 -0
  40. package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +23 -0
  41. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/order.ts +31 -0
  42. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineBase.tsx +56 -0
  43. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineResItem.tsx +17 -0
  44. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineView.tsx +13 -0
  45. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectResItem.tsx +26 -2
  46. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderView.tsx +197 -3
  47. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryAction.tsx +19 -0
  48. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +70 -0
  49. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockAuditSelectResItem.tsx +7 -1
  50. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockAuditView.tsx +21 -1
  51. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockBalanceSelectResItem.tsx +7 -1
  52. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockBalanceView.tsx +3 -1
  53. package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/order.tsx +12 -0
  54. package/vona/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +15 -2
  55. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +215 -3
  56. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockAudit.ts +25 -2
  57. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockBalance.ts +14 -3
  58. package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutReservation.test.ts +4 -4
  59. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderPresentation.test.ts +358 -0
  60. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +8 -8
  61. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +5 -1
  62. package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +50 -1
  63. package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockPresentation.test.ts +324 -0
  64. package/vona/src/suite/a-home/modules/home-base/package.json +3 -2
  65. package/vona/src/suite/a-home/modules/home-base/src/.metadata/index.ts +93 -16
  66. package/vona/src/suite/a-home/modules/home-base/src/controller/siteCatalog.ts +21 -0
  67. package/vona/src/suite/a-home/modules/home-base/src/dto/siteCatalogSelectRes.ts +11 -0
  68. package/vona/src/suite/a-home/modules/home-base/src/dto/siteCatalogSelectResItem.ts +15 -0
  69. package/vona/src/suite/a-home/modules/home-base/src/service/siteCatalog.ts +29 -0
  70. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +3 -0
  71. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/config/locale/en-us.ts +1 -0
  72. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/config/locale/zh-cn.ts +1 -0
  73. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +3 -0
  74. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/config/locale/en-us.ts +1 -0
  75. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/config/locale/zh-cn.ts +1 -0
  76. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +1 -0
  77. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  78. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/env.ts +1 -3
  79. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +2 -1
  80. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  81. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  82. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +67 -0
  83. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payProvider.ts +1 -0
  84. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +5 -4
  85. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +8 -0
  86. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +22 -0
  87. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/schedule.providerOperationDispatch.ts +1 -1
  88. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentSession.tsx +6 -0
  89. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +3 -0
  90. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperationRecoveryAudit.tsx +53 -0
  91. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/refundOperation.tsx +6 -0
  92. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/providerOperationRecoveryAudit.ts +12 -0
  93. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +6 -2
  94. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +236 -30
  95. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +8 -5
  96. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/webhook.ts +34 -0
  97. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts +4 -0
  98. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +6 -0
  99. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  100. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +15 -7
  101. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/service/payMock.ts +9 -5
  102. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  103. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +289 -86
  104. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/paypalGateway.ts +9 -1
  105. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  106. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  107. package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
  108. package/vona/src/suite-vendor/a-vona/modules/a-openapi/src/types/rest.ts +1 -0
  109. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  110. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +12 -0
  111. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  112. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoCreate.ts +5 -1
  113. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoUpdate.ts +5 -1
  114. package/vona/src/suite-vendor/a-vona/modules/a-validation/package.json +1 -1
  115. package/vona/src/suite-vendor/a-vona/modules/a-validation/src/bean/bean.validator.ts +60 -1
  116. package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
  117. package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/decorator/bean.ts +7 -4
  118. package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/dto.ts +2 -1
  119. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  120. package/zova/env/.env +3 -5
  121. package/zova/env/.env.cabloyBasicAdmin +1 -4
  122. package/zova/env/.env.cabloyBasicWeb +1 -5
  123. package/zova/env/.env.cabloyCommerce +1 -5
  124. package/zova/env/.env.cabloyCommerceAdmin +1 -4
  125. package/zova/env/.env.ssr.admin +1 -4
  126. package/zova/env/.env.ssr.web +1 -5
  127. package/zova/package.original.json +1 -1
  128. package/zova/packages-utils/zova-jsx/package.json +2 -2
  129. package/zova/packages-zova/zova/package.json +3 -3
  130. package/zova/packages-zova/zova-core/package.json +2 -1
  131. package/zova/packages-zova/zova-core/src/core/component/locale.ts +10 -4
  132. package/zova/packages-zova/zova-core/src/core/sys/config.ts +14 -6
  133. package/zova/packages-zova/zova-core/src/types/utils/env.ts +4 -2
  134. package/zova/pnpm-lock.yaml +24 -21
  135. package/zova/src/front/config/config/config.ts +22 -7
  136. package/zova/src/suite/a-commerce/modules/commerce-catalog/src/routes.ts +2 -2
  137. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +16 -8
  138. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +150 -216
  139. package/zova/src/suite/a-commerce/modules/commerce-member/src/model/addressMine.ts +1 -2
  140. package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +5 -8
  141. package/zova/src/suite/a-commerce/modules/commerce-member/src/routes.ts +1 -1
  142. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/model/coupon.ts +1 -2
  143. package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/routes.ts +3 -1
  144. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +3 -0
  145. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeOrder.ts +72 -0
  146. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +8 -0
  147. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +226 -16
  148. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeOrder.ts +15 -0
  149. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +155 -36
  150. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +7 -0
  151. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +7 -0
  152. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +3 -4
  153. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +42 -0
  154. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/orderMine.ts +2 -4
  155. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +5 -1
  156. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +14 -10
  157. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +5 -8
  158. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/orders/controller.tsx +5 -8
  159. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +151 -24
  160. package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +5 -5
  161. package/zova/src/suite/a-demo/modules/demo-basic/src/routes.ts +26 -7
  162. package/zova/src/suite/a-demo/modules/demo-todo/src/routes.ts +2 -2
  163. package/zova/src/suite/a-devui/modules/devui-adapter/src/bean/meta.themeHandler.ts +1 -1
  164. package/zova/src/suite/a-home/modules/home-base/package.json +1 -0
  165. package/zova/src/suite/a-home/modules/home-base/src/routes.ts +5 -0
  166. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +1 -1
  167. package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +18 -14
  168. package/zova/src/suite/a-home/modules/home-indexadmin/src/routes.ts +1 -1
  169. package/zova/src/suite/a-home/modules/home-indexweb/src/routes.ts +1 -0
  170. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +12 -4
  171. package/zova/src/suite/a-home/modules/home-layoutadmin/src/config/config.ts +8 -0
  172. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/layout.ts +5 -8
  173. package/zova/src/suite/a-home/modules/home-layoutempty/src/.metadata/index.ts +10 -3
  174. package/zova/src/suite/a-home/modules/home-layoutempty/src/component/layoutEmpty/controller.tsx +9 -3
  175. package/zova/src/suite/a-home/modules/home-layoutempty/src/config/config.ts +12 -0
  176. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +13 -8
  177. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.header.tsx +1 -1
  178. package/zova/src/suite/a-home/modules/home-layoutweb/src/config/config.ts +4 -0
  179. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/layout.ts +5 -8
  180. package/zova/src/suite/a-home/modules/home-login/src/routes.ts +1 -0
  181. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -3
  182. package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/routes.ts +3 -1
  183. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  184. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -1
  185. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/routes.ts +3 -0
  186. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  187. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  188. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +2 -2
  189. package/zova/src/suite-vendor/a-pay/package.json +2 -2
  190. package/zova/src/suite-vendor/a-zova/modules/a-interceptor/package.json +1 -1
  191. package/zova/src/suite-vendor/a-zova/modules/a-interceptor/src/bean/interceptor.jwt.ts +1 -1
  192. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  193. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +1 -1
  194. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +1 -0
  195. package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
  196. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/config/config.ts +1 -17
  197. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/bodyReadyObserver.ts +27 -0
  198. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/index.ts +2 -0
  199. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +33 -0
  200. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssrMetaStore.ts +3 -35
  201. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssrProfile.ts +38 -0
  202. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/monkey.ts +10 -0
  203. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/types/config.ts +3 -10
  204. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/types/ssr.ts +17 -8
  205. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
  206. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/src/service/ssrHandler.ts +33 -18
  207. package/zova/src/suite-vendor/a-zova/modules/a-style/package.json +1 -1
  208. package/zova/src/suite-vendor/a-zova/modules/a-style/src/bean/bean.theme.ts +2 -2
  209. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  210. package/zova/src/suite-vendor/a-zova/package.json +7 -7
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.131
1
+ 5.1.133
@@ -44,7 +44,7 @@ After validation, immediately generate the standard core tuple. Do not ask which
44
44
 
45
45
  For user-facing summaries, write exactly:
46
46
 
47
- > 环境隔离信息:Vona 开发 + Zova 开发
47
+ > Environment isolation: Vona development + Zova development
48
48
 
49
49
  Admin and Web use this same Zova environment. The user may later run either frontend command, but must not run both concurrently in one worktree. Use another linked worktree for concurrent development of the other flavor. Do not create a flavor-specific local override.
50
50
 
@@ -52,11 +52,11 @@ Admin and Web use this same Zova environment. The user may later run either fron
52
52
 
53
53
  Recommendation generation uses Git worktree metadata and the fixed constants below only. It must not inspect any `.env`, `.env.local`, `.env.*.local`, sibling worktree configuration, process environment, listener table, `lsof` output, process command line, or external service. Do not use `Read`, `cat`, `grep`, `source`, `dotenv`, `printenv`, `env`, or diagnostics that expose env-file content for this workflow.
54
54
 
55
- This means the proposal is deterministic convenience, not a live port reservation. If an application later reports a port collision, the user may say **“再换一批”** before setup or create another linked worktree; the actual application bind remains authoritative.
55
+ This means the proposal is deterministic convenience, not a live port reservation. If an application later reports a port collision, the user may say **“Try another batch”** before setup or create another linked worktree; the actual application bind remains authoritative.
56
56
 
57
57
  ### Deterministic proposal
58
58
 
59
- Treat the primary checkout as ordinal `0`. From the validated `git worktree list --porcelain -z` order, assign each linked worktree the next ordinal: the first linked worktree is `1`, the second is `2`, and so on. Let `batch` be `0` for the first proposal, and increase it only when the user says **“再换一批”**.
59
+ Treat the primary checkout as ordinal `0`. From the validated `git worktree list --porcelain -z` order, assign each linked worktree the next ordinal: the first linked worktree is `1`, the second is `2`, and so on. Let `batch` be `0` for the first proposal, and increase it only when the user says **“Try another batch”**.
60
60
 
61
61
  Use one shared offset for every generated listener:
62
62
 
@@ -92,7 +92,7 @@ DEV_SERVER_HMR_PORT = <24679 + offset>
92
92
 
93
93
  All generated listener ports must be integers from `1` through `65535` and distinct within the tuple. If a batch would exceed that range, stop and require a different linked-worktree arrangement.
94
94
 
95
- When the user says **“再换一批”**, set `batch = batch + 1`, recompute the entire tuple, and show it again. Every recommended listener advances by exactly `+1`, and `API_BASE_URL` is regenerated from the new Vona port. Do not write during a batch change. This deterministic setup does not accept user-substituted values.
95
+ When the user says **“Try another batch”**, set `batch = batch + 1`, recompute the entire tuple, and show it again. Every recommended listener advances by exactly `+1`, and `API_BASE_URL` is regenerated from the new Vona port. Do not write during a batch change. This deterministic setup does not accept user-substituted values.
96
96
 
97
97
  A unique `APP_NAME` separates ordinary framework-managed test database names and framework Redis prefixes. It does not isolate explicitly named databases, unprefixed custom Redis keys, mail, payment, webhooks, object storage, or other external services. Require a separate explicit design before claiming that any of those resources are isolated.
98
98
 
@@ -100,7 +100,7 @@ A unique `APP_NAME` separates ordinary framework-managed test database names and
100
100
 
101
101
  Before making any edit:
102
102
 
103
- 1. show `环境隔离信息:Vona 开发 + Zova 开发`, the worktree ordinal, batch, and exact non-secret identity/port tuple
103
+ 1. show `Environment isolation: Vona development + Zova development`, the worktree ordinal, batch, and exact non-secret identity/port tuple
104
104
  2. show a file-by-file preview containing only the managed assignments that will be written to both broad local files
105
105
  3. run one independent `git check-ignore -v -- "$target"` command for each target and require each command to succeed with a matching ignore rule
106
106
  4. recheck current root, linked-worktree identity, edition, ordinal, generated port range, tuple uniqueness, and both target states
@@ -199,7 +199,7 @@ git status --short
199
199
  When this skill completes a stage, report:
200
200
 
201
201
  1. validated worktree root and detected edition
202
- 2. `环境隔离信息:Vona 开发 + Zova 开发`
202
+ 2. `Environment isolation: Vona development + Zova development`
203
203
  3. worktree ordinal, batch, and generated non-secret identity/ports
204
204
  4. both exact broad local files proposed or changed
205
205
  5. the privacy boundary and external resources that still require separate isolation
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "id": 1,
6
6
  "prompt": "I explicitly invoke /cabloy-worktree-environment in the first linked Cabloy Basic worktree. Configure the local environment.",
7
- "expected_output": "Validates that the current checkout is a linked Cabloy Basic worktree before any write. Without asking which processes or frontend flavor will run, it uses Git worktree metadata only and recommends APP_NAME from the worktree basename, SERVER_LISTEN_PORT 7103, DEV_SERVER_PORT 9001, DEV_SERVER_HMR_PORT 24680, and API_BASE_URL http://localhost:7103. It labels the configuration as 环境隔离信息:Vona 开发 + Zova 开发, previews both vona/env/.env.local and zova/env/.env.local, waits for final confirmation before writing, and separately asks whether to run npm run init after success.",
7
+ "expected_output": "Validates that the current checkout is a linked Cabloy Basic worktree before any write. Without asking which processes or frontend flavor will run, it uses Git worktree metadata only and recommends APP_NAME from the worktree basename, SERVER_LISTEN_PORT 7103, DEV_SERVER_PORT 9001, DEV_SERVER_HMR_PORT 24680, and API_BASE_URL http://localhost:7103. It labels the configuration as Environment isolation: Vona development + Zova development, previews both vona/env/.env.local and zova/env/.env.local, waits for final confirmation before writing, and separately asks whether to run npm run init after success.",
8
8
  "files": []
9
9
  },
10
10
  {
@@ -45,7 +45,7 @@
45
45
  },
46
46
  {
47
47
  "id": 8,
48
- "prompt": "I explicitly invoke /cabloy-worktree-environment in the first linked Cabloy Basic worktree. The initial proposal is 7103, 9001, and 24680. 再换一批。",
48
+ "prompt": "I explicitly invoke /cabloy-worktree-environment in the first linked Cabloy Basic worktree. The initial proposal is 7103, 9001, and 24680. Try another batch.",
49
49
  "expected_output": "Keeps the same worktree APP_NAME, increases the shared batch by one, and proposes SERVER_LISTEN_PORT 7104, DEV_SERVER_PORT 9002, DEV_SERVER_HMR_PORT 24681, and API_BASE_URL http://localhost:7104. It does not write, inspect ports, inspect environment files, or treat either batch as reserved before final confirmation.",
50
50
  "files": []
51
51
  },
@@ -35,7 +35,7 @@ jobs:
35
35
  - name: init
36
36
  run: npm run init
37
37
  - name: run A-Pay focused gate
38
- run: DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts --flavor=normal
38
+ run: DATABASE_DEFAULT_CLIENT=pg npm run vona :bin:test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/paypalProvider.test.ts test-pay/test/paypalLifecycle.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts --flavor=normal
39
39
  working-directory: vona
40
40
  - name: run Commerce stock contention gate
41
41
  run: |
@@ -28,7 +28,7 @@ jobs:
28
28
  - name: init
29
29
  run: npm run init
30
30
  - name: run A-Pay focused gate
31
- run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run vona :bin:test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts --flavor=normal
31
+ run: DATABASE_DEFAULT_CLIENT=sqlite3 npm run vona :bin:test -- test-pay/test/paymentSession.test.ts test-pay/test/payMock.test.ts test-pay/test/paypalProvider.test.ts test-pay/test/paypalLifecycle.test.ts test-pay/test/outbox.test.ts test-pay/test/webhook.test.ts --flavor=normal
32
32
  working-directory: vona
33
33
  - name: run Commerce payment, shipment, and refund lifecycle gate
34
34
  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 commerce-trade/test/refundLifecycle.test.ts --flavor=normal
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.133
4
+
5
+ ### Features
6
+
7
+ - Add SSR profiles for core and commerce applications.
8
+ - Add payment UUIDs, refund support, and refunded-payment handling.
9
+ - Add administrative semantic presentations for addresses, orders, stock, and SKUs.
10
+ - Add payment capabilities and PayPal payment lifecycle coverage.
11
+ - Add SSR site title support and a home-page base.
12
+ - Preserve DTO OpenAPI schema scenarios.
13
+ - Add custom Zod error handling.
14
+ - Ensure passport availability for authenticated flows.
15
+
16
+ ### Bug Fixes
17
+
18
+ - Preserve SSR locale cookie typings.
19
+ - Harden PayPal refund recovery.
20
+
21
+ ### Improvements
22
+
23
+ - Upgrade Lerna to v10.
24
+
25
+ ## 5.1.132
26
+
27
+ ### Features
28
+
29
+ - Update functionality.
30
+
3
31
  ## 5.1.131
4
32
 
5
33
  ### Features
@@ -73,11 +73,13 @@ class DtoStudentCreate {
73
73
  }
74
74
  ```
75
75
 
76
- When mixing helper metadata and an explicit zod schema in `@Api.field(...)`, apply the same ordering rule used by entities:
76
+ When mixing helper metadata and a structure-defining schema-like argument in `@Api.field(...)`, apply the same ordering rule used by entities:
77
77
 
78
- - place `z.xxx(...)` as the **last argument** because it returns the zod schema instance
79
- - keep helper metadata such as `v.xxx(...)` and `ZovaRender.xxx(...)` before the zod schema
80
- - otherwise helpers written after the zod schema may stop taking effect
78
+ - place the final structure-defining argument, such as `z.xxx(...)`, `v.object(...)`, or `v.array(...)`, **last**
79
+ - keep helper metadata such as `v.xxx(...)` and `ZovaRender.xxx(...)` before it
80
+ - optionality, nullability, defaults, and preprocess/transform wrappers are also structure-shaping; do not assume their order is interchangeable
81
+
82
+ Framework DTO composition preserves inherited OpenAPI metadata, but that preservation does not make structure-shaping schema-like composition order-independent. Verify emitted schema/OpenAPI output after changing those arguments.
81
83
 
82
84
  For query-oriented DTOs, another important distinction is optional vs nullable:
83
85
 
@@ -86,6 +88,39 @@ For query-oriented DTOs, another important distinction is optional vs nullable:
86
88
 
87
89
  That becomes especially important for DTO query filters. A field declared with `v.optional(), v.nullable()` can preserve real `null` through query parsing so the downstream ORM filter can express SQL `IS NULL` instead of silently treating the value as omitted.
88
90
 
91
+ ## DTO schema scenes
92
+
93
+ A DTO can declare the contract scene that its schema represents:
94
+
95
+ ```typescript
96
+ @Dto({
97
+ schemaScene: 'filter',
98
+ fields: {
99
+ status: $makeSchema(v.optional(), z.string()),
100
+ },
101
+ })
102
+ export class DtoOrderFilter extends $Dto.query(EntityOrder, ['status']) {}
103
+ ```
104
+
105
+ The shared scene vocabulary is:
106
+
107
+ - `table`
108
+ - `form`
109
+ - `form-view`
110
+ - `form-create`
111
+ - `filter`
112
+
113
+ `schemaScene` is emitted through Cabloy's `rest.schemaScene` OpenAPI extension metadata. It is not a standard OpenAPI field. The same metadata can therefore guide Vona request validation and Zova schema-driven consumers.
114
+
115
+ The inferred mutation helpers already declare the usual write intent:
116
+
117
+ - `$Dto.create(...)` uses `form-create`
118
+ - `$Dto.update(...)` uses `form`
119
+
120
+ Use `@Dto({ schemaScene })` when a named DTO has a deliberately different request-contract scene. Do not restate the normal create or update scene only because a DTO wraps one of those helpers.
121
+
122
+ Choose the scene from the API contract, not from the particular screen that happens to call it. A scene does not decide which fields the DTO contains: use `$Dto.*`, `columns`, `include`, and `dtoClass` to define the supported projection first. For the inferred authoring sequence, see [DTO Infer and Generation](/backend/dto-infer-generation); for scene-aware write handling, see [Validation Guide](/backend/validation-guide) and [OpenAPI Guide](/backend/openapi-guide).
123
+
89
124
  ## DTO options
90
125
 
91
126
  Three especially important DTO option areas are:
@@ -191,6 +191,8 @@ export class DtoStudentSelectReq extends $Dto.queryPage(EntityStudent, [
191
191
 
192
192
  `$makeSchema(...)` applies schema-like arguments right-to-left. Keep the final structure-defining schema, such as `z.string()`, `z.number()`, `v.object(...)`, or `v.array(...)`, last in authoring order. Treat optionality, nullability, defaults, preprocess/transform wrappers, objects, and arrays as structure-shaping rather than metadata-only, and verify emitted schema/OpenAPI output after changing them.
193
193
 
194
+ Framework DTO composition preserves inherited OpenAPI metadata when a field schema is refined. That includes metadata such as titles, render information, and scene identity. It does **not** make structure-shaping schema-like arguments order-independent: keep the final structure-defining argument last and verify the effective schema rather than assuming a metadata merge can repair a reordered schema.
195
+
194
196
  `@Dto({ fields })` changes the runtime contract and metadata. It does not rewrite the TypeScript property type inferred from the `$Dto.*` base class. Do not add a duplicate `declare` field or a second field decorator solely to mirror a runtime schema restriction unless a separate static contract is genuinely required and is type-compatible with the inferred base.
195
197
 
196
198
  ### Layer 3: add contract-only fields
@@ -244,6 +246,33 @@ Representative source facts:
244
246
 
245
247
  These are good examples of **named DTO classes that wrap inference**.
246
248
 
249
+ The mutation helpers also carry their conventional schema-scene identity into the DTO's Cabloy OpenAPI metadata:
250
+
251
+ | Helper | Effective schema scene | Practical effect |
252
+ | ------------------ | ---------------------- | ------------------------------------------------------------------- |
253
+ | `$Dto.create(...)` | `form-create` | Create-specific field metadata applies after the shared form layer. |
254
+ | `$Dto.update(...)` | `form` | Shared form field metadata applies directly. |
255
+
256
+ For `form-view`, `form-create`, and `filter`, field metadata resolves in this order: base `rest`, then the shared `rest.form` overlay, then the exact scene overlay. `table` has no shared-form overlay. For example, a field can remain read-only in normal form contracts while being enabled specifically for creation:
257
+
258
+ ```typescript
259
+ @Dto({
260
+ fields: {
261
+ referenceNo: $makeMetadata(
262
+ v.openapi({
263
+ rest: {
264
+ 'form': { readonly: true },
265
+ 'form-create': { readonly: false },
266
+ },
267
+ }),
268
+ ),
269
+ },
270
+ })
271
+ export class DtoOrderCreate extends $Dto.create(() => ModelOrder) {}
272
+ ```
273
+
274
+ The effective `form-create` rule makes `referenceNo` writable for this create contract. An update DTO using the shared `form` scene keeps it read-only unless its own scene metadata changes that result. DTO options such as `fields`, `blocks`, `openapi`, and `schemaScene` merge with metadata supplied by inferred helpers or inheritance; they do not erase the helper-provided scene identity.
275
+
247
276
  Why this is a good fit:
248
277
 
249
278
  - the contracts are operation-specific and worth naming
@@ -60,6 +60,22 @@ That means:
60
60
 
61
61
  This tight linkage is one of the reasons the Cabloy contract story can stay productive at scale.
62
62
 
63
+ ## Schema scenes are Cabloy contract metadata
64
+
65
+ A DTO can emit its semantic schema scene through Cabloy's `rest.schemaScene` extension metadata. The scene vocabulary is `table`, `form`, `form-view`, `form-create`, and `filter`. This is Cabloy metadata carried by the emitted schema, not a field defined by the OpenAPI specification itself.
66
+
67
+ The same emitted metadata has coordinated but distinct consumers:
68
+
69
+ | Consumer | Responsibility |
70
+ | ------------------------ | -------------------------------------------------------------------------------------------------------- |
71
+ | Vona validation | Resolves a field's effective scene metadata and removes read-only properties from validated write input. |
72
+ | OpenAPI emission | Carries DTO and field scene metadata through the backend contract. |
73
+ | Zova schema/form runtime | Applies shared-form and exact-scene property overlays while loading fields for the active schema scene. |
74
+
75
+ For the form-derived scenes `form-view`, `form-create`, and `filter`, a field's base `rest` metadata is followed by the shared `rest.form` overlay and then the exact scene overlay. This lets one contract describe common form behavior while still declaring a create- or filter-specific exception.
76
+
77
+ The end-to-end rule is therefore not “the frontend hides a field.” Vona uses the DTO contract to sanitize read-only request input, while Zova uses compatible metadata to render the relevant scene. See [DTO Guide](/backend/dto-guide) for declaring a DTO scene, [Validation Guide](/backend/validation-guide) for server-side readonly handling, [OpenAPI Runtime Under the Hood](/frontend/a-openapi-under-the-hood) for Zova schema loading, and [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide) for frontend form-scene flow.
78
+
63
79
  ## Controllers, DTOs, entities, and examples all contribute
64
80
 
65
81
  OpenAPI output is not owned by one layer only.
@@ -44,6 +44,49 @@ Inferred schemas can also be extended through helper tools like:
44
44
 
45
45
  That is important because many real validation cases need augmentation rather than total replacement.
46
46
 
47
+ ## Scene-aware readonly input sanitization
48
+
49
+ DTO and field OpenAPI metadata can identify a Cabloy schema scene and its scene-specific field behavior. After a schema parses successfully, Vona removes each known property whose **effective** `rest` metadata resolves to `readonly: true` for that scene.
50
+
51
+ This is server-side input sanitization, not a browser-only disabled control and not a validation error. A caller cannot turn a read-only field into a write channel by bypassing a Zova form.
52
+
53
+ For `form-view`, `form-create`, and `filter`, Vona resolves field metadata in this order:
54
+
55
+ 1. base `rest` metadata
56
+ 2. shared `rest.form` metadata
57
+ 3. the exact scene metadata, such as `rest['form-create']`
58
+
59
+ For `table` and `form`, only the base metadata and exact-scene metadata apply. A nested DTO inherits the containing scene unless it declares its own `rest.schemaScene`.
60
+
61
+ The sanitization traverses nested DTO/object values and array elements, including framework lazy and chained schema composition. For example, a create request can prevent callers from supplying a line-item price while still accepting its quantity:
62
+
63
+ ```typescript
64
+ class DtoLineItem {
65
+ @Api.field(v.openapi({ rest: { form: { readonly: true } } }))
66
+ price: number;
67
+
68
+ @Api.field()
69
+ quantity: number;
70
+ }
71
+
72
+ export class DtoOrderCreate extends $Dto.create(() => ModelOrder) {
73
+ @Api.field(v.array(DtoLineItem))
74
+ items: DtoLineItem[];
75
+ }
76
+
77
+ // request input
78
+ {
79
+ items: [{ price: 99, quantity: 2 }];
80
+ }
81
+
82
+ // validated value delivered to the handler
83
+ {
84
+ items: [{ quantity: 2 }];
85
+ }
86
+ ```
87
+
88
+ Use DTO projection to omit a field that the API must never accept or return. Use scene-aware `readonly` when a field belongs to the schema but must be removed from write input for one contract scene. This sanitization is not response serialization; response payload shaping remains a separate concern in the [Serialization Guide](/backend/serialization-guide). General object parsing and unknown-key behavior still belong to the supplied Zod schema; readonly sanitization removes known protected fields only after successful parsing.
89
+
47
90
  A useful distinction is:
48
91
 
49
92
  - `v.optional` means the field may be omitted
@@ -82,6 +125,8 @@ When changing request contracts, ask:
82
125
  2. does the contract need explicit extension through the `v` helpers?
83
126
  3. does the same validation surface also feed OpenAPI and DTO behavior?
84
127
  4. is the validation logic better expressed at the controller, DTO, or entity layer?
128
+ 5. for a scene-aware DTO, what is its effective schema scene and does each protected field resolve to read-only there?
129
+ 6. when protected fields can be nested, have object and array inputs been tested separately?
85
130
 
86
131
  That produces more consistent backend contracts.
87
132
 
@@ -103,29 +103,22 @@ The config side follows the same merge pattern with `config.ts`, `config.[meta].
103
103
 
104
104
  Different SSR flavors can intentionally expose different runtime capabilities rather than behaving identically.
105
105
 
106
- A concrete example in the current Cabloy Basic frontend setup is SSR theme resolution:
106
+ A concrete example in the current Cabloy Basic frontend setup is the default SSR profile:
107
107
 
108
- - Web SSR uses a cookie-disabled path
109
- - Admin SSR uses a cookie-capable path
108
+ - Web defaults to `SSR_PROFILE=public`
109
+ - Admin defaults to `SSR_PROFILE=session`
110
110
 
111
- That means the two flavors should not be treated as providing the same guarantee for theme-sensitive SSR output.
111
+ The effective profile is request-local: the active flavor's `SSR_PROFILE` supplies its default, and `route.meta.ssrProfile` can override it after route resolution. `public` keeps cookie-derived identity and theme state out of the server render; `session` permits normal cookie-backed Passport recovery and theme resolution, while forcing a private, non-storable response. Profiles define request-cookie capability, not a locale source: existing `route.meta.locale` controls URL-locale participation.
112
112
 
113
- In practice:
113
+ That means flavor selection is not only a packaging choice. It establishes the default capability boundary, while individual routes remain able to select the profile their rendering contract requires.
114
114
 
115
- - Web SSR is the stricter path and should treat theme-sensitive SSR reads as lower-authority
116
- - Admin SSR can provide a stronger server/client theme match guarantee
115
+ Before assuming how SSR theme state is handed off and finalized, combine:
117
116
 
118
- This is exactly why flavor selection is not only a packaging choice. It can also define the supported capability boundary for runtime-sensitive behavior.
119
-
120
- A second practical rule is that flavor alone is not the full story. Contributors should combine:
121
-
122
- - flavor and `SSR_COOKIE` capability
117
+ - the effective SSR profile
123
118
  - edition marker
124
119
  - active UI-library adapter
125
120
 
126
- before assuming how SSR theme state is handed off and finalized.
127
-
128
- For the theme-side contract and edition-aware checklist, see [Theme Guide](/frontend/theme-guide). For the env-side explanation of `SSR_COOKIE`, see [SSR Environment Variables](/frontend/ssr-env).
121
+ For the theme-side contract and edition-aware checklist, see [Theme Guide](/frontend/theme-guide). For the env-side explanation of `SSR_PROFILE`, see [SSR Environment Variables](/frontend/ssr-env).
129
122
 
130
123
  ## Scripts and runtime variants
131
124
 
@@ -25,7 +25,7 @@ class ServiceRouterGuards {
25
25
  protected onRouterGuards(router: BeanRouter) {
26
26
  router.beforeEach(async to => {
27
27
  if (to.meta.requiresAuth === false) return;
28
- if (this.sys.config.ssr.cookieDisabledOnServer) return;
28
+ if (process.env.SERVER && this.$ssr.profile === 'public') return;
29
29
 
30
30
  if (!this.$passport.isAuthenticated) {
31
31
  const [_res, err] = await catchError(() => this.$passport.ensurePassport());
@@ -57,9 +57,9 @@ That means route configuration and guard behavior should be read together, not a
57
57
 
58
58
  ## SSR-sensitive detail
59
59
 
60
- The example also references SSR-related configuration such as cookie handling on the server side.
60
+ The example uses the request-local `$ssr.profile` selected after route resolution and before the SSR app initializes.
61
61
 
62
- When `SSR_COOKIE=false`, `cookieDisabledOnServer` is true only during server rendering. The guard deliberately allows the protected route's neutral SSR entry, then the browser restores Passport state and applies the same admission policy after hydration. This preserves equivalent server and hydration-time initial rendering without weakening Client-side protection.
62
+ When the effective profile is `public`, the server deliberately allows the protected route's neutral SSR entry. The browser restores Passport state and applies the same admission policy after hydration. A `session` profile permits the server to recover Passport and make the normal redirect or access-denied decision during the initial render. This preserves equivalent server and hydration-time initial rendering without weakening Client-side protection.
63
63
 
64
64
  For cookie-enabled SSR, a rejected request must still use `$redirect(...)` so the SSR layer returns its HTTP redirect response. On the Client, return a route path or route-location object from the guard instead of using an imperative `$goto...()` helper.
65
65
 
@@ -59,10 +59,10 @@ The route meta surface includes important behavior such as:
59
59
  - absolute-path behavior
60
60
  - layout choice
61
61
  - authentication requirement
62
- - locale handling
62
+ - URL-locale handling through `meta.locale`
63
63
  - component key behavior
64
64
  - keepAlive behavior
65
- - SSR transfer-cache behavior
65
+ - SSR profile and nested `meta.ssrProfileOptions.responseCache` behavior
66
66
 
67
67
  This is one reason route records matter so much: they are not just URL declarations. They are an application-behavior surface.
68
68
 
@@ -148,6 +148,6 @@ It should also check whether the route change affects:
148
148
  - params typing
149
149
  - auth behavior
150
150
  - layout behavior
151
- - locale behavior
152
- - SSR behavior
151
+ - whether `meta.locale` should participate in URL-locale behavior
152
+ - SSR profile and nested `meta.ssrProfileOptions.responseCache` behavior
153
153
  - metadata regeneration
@@ -12,54 +12,47 @@ Zova exposes SSR-related environment variables so the framework can configure ke
12
12
 
13
13
  Representative variables include:
14
14
 
15
- - `SSR_COOKIE`
15
+ - `SSR_PROFILE`
16
16
  - `SSR_COOKIE_THEMEDARK_DEFAULT`
17
- - `SSR_BODYREADYOBSERVER`
18
17
  - `SSR_API_BASE_URL`
19
18
  - `SSR_PROD_PORT`
20
- - `SSR_TRANSFERCACHE`
21
- - `SSR_TRANSFERCACHE_EXPIRES`
22
19
 
23
- These affect areas such as cookie-driven SSR behavior, theme defaults, body-load observation, server-side API targeting, and SSR production port behavior.
20
+ These affect SSR profile selection, theme defaults, server-side API targeting, and SSR production port behavior. Response-cache defaults are resolved from the selected request-local profile; body-ready behavior is an opt-in of the selected layout module, not an environment or profile switch.
24
21
 
25
- ## SSR response cache control
22
+ ## SSR profile
26
23
 
27
- `SSR_TRANSFERCACHE` and `SSR_TRANSFERCACHE_EXPIRES` control the `Cache-Control` response header that Zova adds after an SSR page is rendered.
24
+ `SSR_PROFILE` is the standalone-flavor default for request-local SSR behavior:
28
25
 
29
- - `SSR_TRANSFERCACHE=false` disables this header path.
30
- - Any other value enables it in the current configuration normalization.
31
- - `SSR_TRANSFERCACHE_EXPIRES=0` emits `Cache-Control: no-cache, no-store, must-revalidate`.
32
- - A positive number of seconds or an `ms`-style duration such as `10m` emits `Cache-Control: public, max-age=<seconds>`.
26
+ - `public`: the server renders without request-cookie credentials or cookie-backed theme resolution. Output must be anonymous-safe and may use public response caching.
27
+ - `session`: the server may recover request-cookie session state for normal route admission and authorized rendering. The response is always `Cache-Control: private, no-store`.
33
28
 
34
- Cabloy Basic uses different flavor defaults:
29
+ For Vona-backed SSR, `SSR_PROFILE` supplies the flavor default. After the frontend route is resolved, `route.meta.ssrProfile` can override that default before `serverEntry` runs. The resolved profile controls request-cookie capability and related SSR behavior; locale-aware URL handling remains the existing `route.meta.locale` contract. A profile never grants authentication, site admission, or API authorization; `requiresAuth` and Vona guards remain responsible for those decisions.
35
30
 
36
- | Flavor | Settings | SSR response header |
37
- | ------ | --------------------------------------------------------- | ------------------------------------- |
38
- | Web | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=10m` | `public, max-age=600` |
39
- | Admin | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=0` | `no-cache, no-store, must-revalidate` |
31
+ The selected layout owns sidebar restoration. A layout that enables `layout.sidebar.bodyReadyObserver` queues a request-local hidden-body metadata entry during SSR, registers its readiness condition and restoration callback, and then injects the generic browser observer. The observer restores browser-local sidebar state after the relevant layout DOM exists and reveals the body before hydration. Admin enables this path; Web and Empty disable it. Therefore, a Web route using `ssrProfile: 'session'` remains `private, no-store` without gaining the Admin sidebar observer.
40
32
 
41
- A route can override the flavor default through its SSR route metadata. For Cloudflare cache-rule alignment that preserves these origin headers, see [Docker + Cloudflare Deployment](/fullstack/deploy-cloudflare-docker).
33
+ The selected layout also supplies its own desktop sidebar fallback and responsive breakpoint: the Admin layout defaults to open and the Web layout defaults to closed, and both currently use `1023px` as their independently configured breakpoint. Browser-local sidebar preference can override that fallback. The selected layout passes its breakpoint to the SSR browser handoff; it is not profile-specific. SSR profile selection does not change a layout's sidebar behavior.
42
34
 
43
- ## Theme implications of `SSR_COOKIE`
35
+ ## SSR response cache control
44
36
 
45
- `SSR_COOKIE` is not only a storage choice. It also changes what SSR can guarantee about theme-sensitive output.
37
+ After Zova resolves the route profile, a `session` response immediately receives `Cache-Control: private, no-store`, before router guards or rendering can terminate it. A successfully rendered public route receives the profile default cache header, which `meta.ssrProfileOptions.responseCache` can refine.
46
38
 
47
- A practical split is:
39
+ Cabloy Basic uses different flavor defaults:
48
40
 
49
- - `SSR_COOKIE=true`: the server can resolve theme state from cookies during SSR
50
- - `SSR_COOKIE=false`: the server cannot guarantee that theme-sensitive SSR reads match the browser's eventual selected theme
41
+ | Flavor | Default profile | SSR response header |
42
+ | ------ | --------------- | ------------------------------------ |
43
+ | Web | `public` | `public, max-age=600` when cacheable |
44
+ | Admin | `session` | `private, no-store` |
51
45
 
52
- In practice, this means Web SSR and Admin SSR can intentionally expose different theme capabilities.
46
+ A route can override the flavor profile through `meta.ssrProfile` and can define a public response-cache policy through `meta.ssrProfileOptions.responseCache`. Set the nested value to `false` to disable public cache-header generation for that route. For Cloudflare cache-rule alignment that preserves these origin headers, see [Docker + Cloudflare Deployment](/fullstack/deploy-cloudflare-docker).
53
47
 
54
- - In a cookie-capable SSR path, theme-sensitive server rendering can rely on a stronger server/client match guarantee.
55
- - In a cookie-disabled SSR path, SSR should treat theme-sensitive reads as non-authoritative for the browser's final theme and prefer hydration-tolerant or client-finalized decisions when exact matching matters.
48
+ ## Theme implications of `SSR_PROFILE`
56
49
 
57
- A practical development rule is:
50
+ `SSR_PROFILE` determines whether the server can use cookie-backed theme state for the current request.
58
51
 
59
- - use `SSR_COOKIE` to determine the capability level
60
- - use the active edition and UI library to determine how that capability is implemented
52
+ - `session`: the server can resolve theme state from request cookies during SSR.
53
+ - `public`: server theme reads are not authoritative for the browser's final theme. Keep theme-sensitive rendering hydration-tolerant and use the established browser-finalization path.
61
54
 
62
- That matters because Cabloy Basic and Cabloy Start share the same theme architecture but do not use the same adapter-level SSR handoff strategy.
55
+ Use the active edition and UI library to determine the adapter-level implementation. Cabloy Basic and Cabloy Start share the profile contract but do not necessarily use the same SSR theme handoff.
63
56
 
64
57
  For the broader theme usage contract and edition-aware checklist, see [Theme Guide](/frontend/theme-guide). For the runtime/flavor selection model behind these env choices, see [Environment and Config Guide](/frontend/environment-config-guide).
65
58
 
@@ -166,13 +166,13 @@ A practical split is:
166
166
  - Web SSR is usually the lower-authority path for final browser theme when cookie-backed SSR resolution is unavailable
167
167
  - Admin SSR is the stronger path for SSR-stable theme-sensitive rendering when cookie-backed SSR resolution is available
168
168
 
169
- In practice, always check `SSR_COOKIE` and the active adapter behavior before assuming that server-rendered theme-sensitive output can exactly match the hydrated client state.
169
+ In practice, check the request-local `$ssr.profile` and the active adapter behavior before assuming that server-rendered theme-sensitive output can exactly match the hydrated client state.
170
170
 
171
- With `SSR_COOKIE=false`, server reads of `$theme.dark`, `$theme.darkMode`, and `$token` should be treated as non-authoritative for the browser's final theme unless the active adapter explicitly documents a stronger guarantee.
171
+ With a `public` profile, server reads of `$theme.dark`, `$theme.darkMode`, and `$token` should be treated as non-authoritative for the browser's final theme unless the active adapter explicitly documents a stronger guarantee.
172
172
 
173
- With `SSR_COOKIE=true`, SSR theme-sensitive branching can rely on a stronger server/client match guarantee, but should still stay inside the established theme handler and hydration pipeline.
173
+ With a `session` profile, SSR theme-sensitive branching can use cookie-backed state and rely on a stronger server/client match guarantee, but should still stay inside the established theme handler and hydration pipeline.
174
174
 
175
- For the env-side explanation of `SSR_COOKIE`, see [SSR Environment Variables](/frontend/ssr-env). For the flavor/runtime selection model, see [Environment and Config Guide](/frontend/environment-config-guide).
175
+ For the env-side explanation of `SSR_PROFILE`, see [SSR Environment Variables](/frontend/ssr-env). For the flavor/runtime selection model, see [Environment and Config Guide](/frontend/environment-config-guide).
176
176
 
177
177
  ## Shared development rules
178
178
 
@@ -355,7 +355,7 @@ The framework service focuses on routing mechanics such as:
355
355
  The `home-base` service focuses on application policy such as:
356
356
 
357
357
  - auth enforcement through `meta.requiresAuth`
358
- - locale initialization through `meta.locale`
358
+ - URL-locale participation and initialization through `meta.locale`
359
359
 
360
360
  A practical reading takeaway is:
361
361
 
@@ -111,12 +111,12 @@ For current Cache Rule settings, see [Cache Rules settings](https://developers.c
111
111
 
112
112
  ## SSR cache contract
113
113
 
114
- Zova SSR writes the cache contract during rendering through `SSR_TRANSFERCACHE` and `SSR_TRANSFERCACHE_EXPIRES`. The current public Cabloy Basic baseline uses these defaults:
114
+ Zova SSR writes the public response-cache contract during rendering from the resolved profile and any public route-level `meta.ssrProfileOptions.responseCache` override. The effective SSR profile is authoritative: a `session` response always sets `Cache-Control: private, no-store` before route or profile response-cache policy is considered.
115
115
 
116
- | Flavor | Default settings | SSR response header | Cloudflare result with this rule |
117
- | ------ | --------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
118
- | Web | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=10m` | `Cache-Control: public, max-age=600` | Eligible to cache for ten minutes, subject to Cloudflare policy |
119
- | Admin | `SSR_TRANSFERCACHE=true`, `SSR_TRANSFERCACHE_EXPIRES=0` | `Cache-Control: no-cache, no-store, must-revalidate` | Not stored |
116
+ | Flavor | Default profile | SSR response header | Cloudflare result with this rule |
117
+ | ------ | --------------- | --------------------------------------------------- | --------------------------------------------------------------- |
118
+ | Web | `public` | `Cache-Control: public, max-age=600` when cacheable | Eligible to cache for ten minutes, subject to Cloudflare policy |
119
+ | Admin | `session` | `Cache-Control: private, no-store` | Not stored |
120
120
 
121
121
  For Cabloy Start, verify the effective Web and Admin values in the licensed Start repository before creating the Cloudflare rule. The rule design remains the same: preserve and follow the origin `Cache-Control` response instead of replacing it.
122
122
 
@@ -132,5 +132,5 @@ After the origin is running, verify the following before relying on Cloudflare t
132
132
  2. Each configured subdomain resolves to its expected enabled instance.
133
133
  3. Nginx forwards the browser hostname instead of the literal value `localhost`.
134
134
  4. Web SSR returns the `Cache-Control` header expected from the active edition and flavor configuration. In the current Basic baseline, it is `public, max-age=600`.
135
- 5. Admin SSR returns the `Cache-Control` header expected from the active edition and flavor configuration. In the current Basic baseline, it is `no-cache, no-store, must-revalidate`.
135
+ 5. Admin SSR returns the `Cache-Control` header expected from the active edition and flavor configuration. In the current Basic baseline, it is `private, no-store`.
136
136
  6. After Cloudflare proxying is enabled, responses intended to be public-cacheable can be cached at the edge while responses marked `no-store` are not stored.
@@ -50,9 +50,9 @@ DEV_SERVER_HMR_PORT = 24680
50
50
 
51
51
  The user-facing summary is:
52
52
 
53
- > 环境隔离信息:Vona 开发 + Zova 开发
53
+ > Environment isolation: Vona development + Zova development
54
54
 
55
- If the proposal is unsuitable, say **“再换一批”**. The skill increases every listener port by exactly `+1`, regenerates `API_BASE_URL` from the new Vona port, and presents the next tuple for confirmation. It never writes during this step.
55
+ If the proposal is unsuitable, say **“Try another batch”**. The skill increases every listener port by exactly `+1`, regenerates `API_BASE_URL` from the new Vona port, and presents the next tuple for confirmation. It never writes during this step.
56
56
 
57
57
  This deterministic scheme is not a port reservation or a live collision check. If an application later reports that a port is occupied, request another batch before setup or create another linked worktree; successful application startup remains the final authority.
58
58