cabloy 5.1.60 → 5.1.62

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 (232) hide show
  1. package/.claude/hooks/contract-loop-gate.ts +296 -0
  2. package/.claude/settings.json +16 -0
  3. package/.claude/skills/cabloy-backend-scaffold/SKILL.md +2 -0
  4. package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -0
  5. package/.claude/skills/cabloy-contract-loop/SKILL.md +89 -16
  6. package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +102 -14
  7. package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +4 -0
  8. package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +32 -14
  9. package/.claude/skills/cabloy-domain-planning/SKILL.md +212 -0
  10. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +13 -0
  11. package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +2 -0
  12. package/.claude/skills/cabloy-module-removal/SKILL.md +144 -0
  13. package/.claude/skills/cabloy-resource-field-update/SKILL.md +7 -0
  14. package/.claude/skills/cabloy-zova-source-reading/SKILL.md +221 -0
  15. package/.claude/skills/cabloy-zova-source-reading/references/analysis-modes.md +91 -0
  16. package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +117 -0
  17. package/CHANGELOG.md +64 -0
  18. package/CLAUDE.md +11 -0
  19. package/cabloy-docs/.vitepress/config.mjs +197 -5
  20. package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
  21. package/cabloy-docs/ai/docs-skills-rules-mapping.md +22 -0
  22. package/cabloy-docs/ai/future-skill-roadmap.md +12 -7
  23. package/cabloy-docs/ai/introduction.md +1 -0
  24. package/cabloy-docs/ai/playbook-backend-module.md +6 -0
  25. package/cabloy-docs/ai/playbook-module-removal.md +164 -0
  26. package/cabloy-docs/ai/skills.md +12 -0
  27. package/cabloy-docs/backend/backend-contract-emission-output-inspection.md +189 -0
  28. package/cabloy-docs/backend/backend-contract-emission-source-reading-map.md +160 -0
  29. package/cabloy-docs/backend/backend-contract-emission-specimen.md +170 -0
  30. package/cabloy-docs/backend/backend-resource-module-contract-chain.md +323 -0
  31. package/cabloy-docs/backend/backend-source-reading-debug-checklist.md +173 -0
  32. package/cabloy-docs/backend/backend-source-reading-roadmap.md +129 -0
  33. package/cabloy-docs/backend/backend-source-reading-verify-playbook.md +166 -0
  34. package/cabloy-docs/backend/bean-scene-authoring.md +4 -4
  35. package/cabloy-docs/backend/broadcast-guide.md +3 -3
  36. package/cabloy-docs/backend/cli.md +20 -11
  37. package/cabloy-docs/backend/config-guide.md +4 -4
  38. package/cabloy-docs/backend/controller-aop-guide.md +10 -10
  39. package/cabloy-docs/backend/controller-guide.md +12 -2
  40. package/cabloy-docs/backend/crud-workflow.md +7 -3
  41. package/cabloy-docs/backend/dto-guide.md +18 -2
  42. package/cabloy-docs/backend/dto-infer-generation.md +201 -25
  43. package/cabloy-docs/backend/election-guide.md +2 -2
  44. package/cabloy-docs/backend/entity-guide.md +30 -3
  45. package/cabloy-docs/backend/error-guide.md +3 -3
  46. package/cabloy-docs/backend/event-guide.md +4 -4
  47. package/cabloy-docs/backend/external-aop-guide.md +2 -2
  48. package/cabloy-docs/backend/field-indexes.md +9 -3
  49. package/cabloy-docs/backend/foundation.md +8 -8
  50. package/cabloy-docs/backend/i18n-guide.md +6 -6
  51. package/cabloy-docs/backend/internal-aop-guide.md +2 -2
  52. package/cabloy-docs/backend/introduction.md +15 -0
  53. package/cabloy-docs/backend/migration-and-changes.md +3 -3
  54. package/cabloy-docs/backend/model-guide.md +16 -6
  55. package/cabloy-docs/backend/openapi-guide.md +3 -0
  56. package/cabloy-docs/backend/queue-guide.md +3 -3
  57. package/cabloy-docs/backend/redlock-guide.md +2 -2
  58. package/cabloy-docs/backend/schedule-guide.md +2 -2
  59. package/cabloy-docs/backend/scripts.md +8 -0
  60. package/cabloy-docs/backend/serialization-guide.md +12 -2
  61. package/cabloy-docs/backend/service-guide.md +18 -9
  62. package/cabloy-docs/backend/startup-guide.md +5 -5
  63. package/cabloy-docs/backend/status-guide.md +271 -0
  64. package/cabloy-docs/backend/unit-testing.md +3 -3
  65. package/cabloy-docs/backend/vona-source-reading-map.md +157 -0
  66. package/cabloy-docs/backend/websocket-protocol-guide.md +5 -5
  67. package/cabloy-docs/backend/websocket-usage-guide.md +15 -8
  68. package/cabloy-docs/frontend/a-model-under-the-hood.md +281 -0
  69. package/cabloy-docs/frontend/a-openapi-under-the-hood.md +248 -0
  70. package/cabloy-docs/frontend/a-router-guide.md +307 -0
  71. package/cabloy-docs/frontend/api-guide.md +6 -4
  72. package/cabloy-docs/frontend/api-schema-guide.md +1 -0
  73. package/cabloy-docs/frontend/app-startup-guide.md +7 -4
  74. package/cabloy-docs/frontend/bean-scene-authoring.md +3 -1
  75. package/cabloy-docs/frontend/behavior-guide.md +16 -16
  76. package/cabloy-docs/frontend/cli.md +14 -2
  77. package/cabloy-docs/frontend/command-scene-authoring.md +504 -0
  78. package/cabloy-docs/frontend/component-guide.md +5 -5
  79. package/cabloy-docs/frontend/component-props-guide.md +1 -1
  80. package/cabloy-docs/frontend/component-v-model-guide.md +2 -2
  81. package/cabloy-docs/frontend/design-principles.md +6 -0
  82. package/cabloy-docs/frontend/fetch-interceptor-guide.md +440 -0
  83. package/cabloy-docs/frontend/filter-query-select-data-flow-guide.md +260 -0
  84. package/cabloy-docs/frontend/form-guide.md +786 -0
  85. package/cabloy-docs/frontend/form-scene-to-page-meta-guide.md +303 -0
  86. package/cabloy-docs/frontend/foundation.md +33 -0
  87. package/cabloy-docs/frontend/frontend-source-reading-roadmap.md +249 -0
  88. package/cabloy-docs/frontend/generated-contract-consumption-debug-checklist.md +190 -0
  89. package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +205 -0
  90. package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +157 -0
  91. package/cabloy-docs/frontend/generated-contract-consumption-specimen.md +203 -0
  92. package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +189 -0
  93. package/cabloy-docs/frontend/generic-component-guide.md +1 -1
  94. package/cabloy-docs/frontend/introduction.md +38 -5
  95. package/cabloy-docs/frontend/ioc-and-beans.md +6 -0
  96. package/cabloy-docs/frontend/mock-guide.md +1 -0
  97. package/cabloy-docs/frontend/model-architecture.md +288 -39
  98. package/cabloy-docs/frontend/model-resource-best-practices.md +379 -0
  99. package/cabloy-docs/frontend/model-resource-cookbook.md +508 -0
  100. package/cabloy-docs/frontend/model-resource-internals-deep-dive.md +238 -0
  101. package/cabloy-docs/frontend/model-resource-owner-pattern.md +402 -0
  102. package/cabloy-docs/frontend/model-resource-usage-guide.md +334 -0
  103. package/cabloy-docs/frontend/model-state-guide.md +371 -15
  104. package/cabloy-docs/frontend/module-scope.md +8 -8
  105. package/cabloy-docs/frontend/modules-and-suites.md +2 -1
  106. package/cabloy-docs/frontend/navigation-guards-guide.md +7 -0
  107. package/cabloy-docs/frontend/openapi-sdk-guide.md +17 -6
  108. package/cabloy-docs/frontend/page-guide.md +15 -9
  109. package/cabloy-docs/frontend/page-meta-guide.md +466 -0
  110. package/cabloy-docs/frontend/page-params-guide.md +3 -3
  111. package/cabloy-docs/frontend/page-query-guide.md +2 -2
  112. package/cabloy-docs/frontend/page-route-guide.md +6 -0
  113. package/cabloy-docs/frontend/permission-formscene-action-visibility-guide.md +263 -0
  114. package/cabloy-docs/frontend/quickstart.md +18 -2
  115. package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +266 -0
  116. package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +271 -0
  117. package/cabloy-docs/frontend/resource-list-page-deep-dive.md +279 -0
  118. package/cabloy-docs/frontend/rest-resource-source-reading-map.md +522 -0
  119. package/cabloy-docs/frontend/rest-resource-under-the-hood.md +622 -0
  120. package/cabloy-docs/frontend/root-behaviors-guide.md +282 -0
  121. package/cabloy-docs/frontend/route-alias-guide.md +6 -0
  122. package/cabloy-docs/frontend/router-stack-guide.md +229 -0
  123. package/cabloy-docs/frontend/router-tabs-introduction.md +26 -3
  124. package/cabloy-docs/frontend/router-tabs-layout-integration.md +367 -0
  125. package/cabloy-docs/frontend/router-tabs-mechanism.md +6 -0
  126. package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +7 -0
  127. package/cabloy-docs/frontend/router-tabs-vs-stack.md +167 -0
  128. package/cabloy-docs/frontend/router-view-hosts-guide.md +450 -0
  129. package/cabloy-docs/frontend/server-data.md +4 -1
  130. package/cabloy-docs/frontend/system-startup-guide.md +2 -2
  131. package/cabloy-docs/frontend/table-action-visibility-permission-flow-guide.md +263 -0
  132. package/cabloy-docs/frontend/table-cell-cookbook.md +568 -0
  133. package/cabloy-docs/frontend/table-guide.md +373 -0
  134. package/cabloy-docs/frontend/table-resource-crud-cookbook.md +496 -0
  135. package/cabloy-docs/frontend/zova-app-guide.md +251 -0
  136. package/cabloy-docs/frontend/zova-form-source-reading-map.md +293 -0
  137. package/cabloy-docs/frontend/zova-form-under-the-hood.md +561 -0
  138. package/cabloy-docs/frontend/zova-reactivity-under-the-hood.md +320 -0
  139. package/cabloy-docs/frontend/zova-router-under-the-hood.md +561 -0
  140. package/cabloy-docs/frontend/zova-source-reading-map.md +421 -0
  141. package/cabloy-docs/frontend/zova-table-controller-render-supplement.md +225 -0
  142. package/cabloy-docs/frontend/zova-table-source-reading-map.md +317 -0
  143. package/cabloy-docs/frontend/zova-table-under-the-hood.md +532 -0
  144. package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +308 -0
  145. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-debug-checklist.md +245 -0
  146. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-source-reading-map.md +139 -0
  147. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-verify-playbook.md +248 -0
  148. package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions.md +511 -0
  149. package/cabloy-docs/fullstack/contract-loop-playbook.md +356 -0
  150. package/cabloy-docs/fullstack/edition-collaboration-differences.md +6 -0
  151. package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +199 -23
  152. package/cabloy-docs/fullstack/introduction.md +15 -1
  153. package/cabloy-docs/fullstack/openapi-to-sdk.md +135 -11
  154. package/cabloy-docs/fullstack/suites-and-modules.md +333 -0
  155. package/cabloy-docs/fullstack/tutorial-1-first-module.md +3 -0
  156. package/cabloy-docs/fullstack/tutorial-2-first-crud.md +4 -0
  157. package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +6 -2
  158. package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +60 -23
  159. package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +14 -7
  160. package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +6 -0
  161. package/cabloy-docs/fullstack/tutorials-overview.md +17 -4
  162. package/cabloy-docs/reference/bean-scene-boilerplates.md +15 -13
  163. package/cabloy-docs/reference/package-map.md +4 -3
  164. package/package.json +2 -1
  165. package/scripts/init.ts +2 -18
  166. package/scripts/initTestData.ts +25 -0
  167. package/scripts/upgrade.ts +17 -2
  168. package/vona/pnpm-lock.yaml +48 -194
  169. package/vona/src/suite/a-training/modules/training-student/package.json +53 -0
  170. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +400 -0
  171. package/vona/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +18 -0
  172. package/vona/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
  173. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.index.ts +12 -0
  174. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.version.ts +21 -0
  175. package/vona/src/suite/a-training/modules/training-student/src/bean/ssrMenu.student.ts +29 -0
  176. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +15 -0
  177. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +15 -0
  178. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +74 -0
  179. package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +28 -0
  180. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +44 -0
  181. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectRes.tsx +11 -0
  182. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +45 -0
  183. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +42 -0
  184. package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +28 -0
  185. package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +25 -0
  186. package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +84 -0
  187. package/vona/src/suite/a-training/modules/training-student/src/index.ts +2 -0
  188. package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +10 -0
  189. package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +57 -0
  190. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +173 -0
  191. package/vona/src/suite/a-training/modules/training-student/tsconfig.build.json +11 -0
  192. package/vona/src/suite/a-training/modules/training-student/tsconfig.json +7 -0
  193. package/vona/src/suite/a-training/package.json +12 -0
  194. package/vona/src/suite/a-training/tsconfig.base.json +4 -0
  195. package/vona/src/suite/a-training/tsconfig.json +10 -0
  196. package/zova/packages-cli/cli/package.json +2 -2
  197. package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/module/openapi.config.ts +6 -1
  198. package/zova/packages-cli/cli-set-front/package.json +1 -1
  199. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +34 -4
  200. package/zova/packages-zova/zova/package.json +2 -2
  201. package/zova/pnpm-lock.yaml +416 -690
  202. package/zova/src/suite/a-training/modules/training-student/cli/openapi.config.ts +9 -0
  203. package/zova/src/suite/a-training/modules/training-student/package.json +52 -0
  204. package/zova/src/suite/a-training/modules/training-student/src/.metadata/component/formFieldLevel.ts +31 -0
  205. package/zova/src/suite/a-training/modules/training-student/src/.metadata/index.ts +258 -0
  206. package/zova/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +7 -0
  207. package/zova/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
  208. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/baseURL.ts +5 -0
  209. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/index.ts +3 -0
  210. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +196 -0
  211. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +4146 -0
  212. package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +151 -0
  213. package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +43 -0
  214. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionDeleteForce.tsx +51 -0
  215. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +56 -0
  216. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.level.tsx +63 -0
  217. package/zova/src/suite/a-training/modules/training-student/src/component/formFieldLevel/controller.tsx +117 -0
  218. package/zova/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +9 -0
  219. package/zova/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +9 -0
  220. package/zova/src/suite/a-training/modules/training-student/src/index.ts +2 -0
  221. package/zova/src/suite/a-training/modules/training-student/src/model/student.ts +42 -0
  222. package/zova/src/suite/a-training/modules/training-student/tsconfig.build.json +13 -0
  223. package/zova/src/suite/a-training/modules/training-student/tsconfig.json +5 -0
  224. package/zova/src/suite/a-training/package.json +12 -0
  225. package/zova/src/suite/a-training/tsconfig.base.json +4 -0
  226. package/zova/src/suite/a-training/tsconfig.json +4 -0
  227. package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +29 -7
  228. package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/select/controller.tsx +34 -11
  229. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  230. package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +3 -3
  231. package/zova/src/suite-vendor/a-zova/modules/a-table/src/lib/tableCell.ts +1 -1
  232. package/zova/src/suite-vendor/a-zova/package.json +2 -2
@@ -0,0 +1,508 @@
1
+ # Resource Model Cookbook
2
+
3
+ This cookbook collects common extension scenarios for resource-oriented models built on top of `ModelResource`.
4
+
5
+ It is the practical companion to:
6
+
7
+ - [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern)
8
+ - [Using `ModelResource` in Your Module](/frontend/model-resource-usage-guide)
9
+ - [Resource Model Best Practices and Anti-Patterns](/frontend/model-resource-best-practices)
10
+
11
+ Use this page when you want implementation templates instead of only architectural explanation.
12
+
13
+ ## Why this page exists
14
+
15
+ The earlier pages explain the architecture, application model, and review rules.
16
+
17
+ The next practical need is usually:
18
+
19
+ > show me the common cases I will actually implement.
20
+
21
+ This page focuses on that need.
22
+
23
+ ## How to use this cookbook
24
+
25
+ Unless a recipe says otherwise, the examples below assume the thin-facade pattern from Recipe 1.
26
+
27
+ That means the business-facing model delegates resource ownership to `this.$$modelResource` instead of becoming another `ModelResource` owner itself.
28
+
29
+ For each recipe below:
30
+
31
+ 1. identify whether the scenario is query-oriented, mutation-oriented, or invalidation-oriented
32
+ 2. keep the resource semantics either on `ModelResource` directly or in a thin facade that delegates to it
33
+ 3. reuse `queryItem(...)`, `mutationItem(...)`, `select(...)`, `view(...)`, and the existing key structure when possible
34
+ 4. keep generic page or form blocks compatible unless you have a real reason to break that contract
35
+
36
+ ## Forward-chain principle: prefer a thin business facade over a competing cache owner
37
+
38
+ In Cabloy’s bidirectional contract loop, this cookbook most often applies to the **forward chain**:
39
+
40
+ - backend contract truth changes first
41
+ - frontend API consumers are regenerated or refreshed from that truth
42
+ - frontend follow-up stays thin and semantic
43
+
44
+ For resource-oriented frontend work, that usually means:
45
+
46
+ > create a thin business model facade and reuse the existing resource-owner instead of creating a competing cache owner.
47
+
48
+ A strong pattern is:
49
+
50
+ - let `ModelResource` continue to own cache identity, list/item invalidation, and resource-level state
51
+ - let the business-facing model add semantic methods such as `summary(...)` or `deleteForce(...)`
52
+ - let custom cells, commands, or page controllers consume the thin business model facade, not invent a second owner for the same resource state
53
+
54
+ That pattern is especially important after forward-chain contract evolution, because it keeps generated or contract-aligned resource ownership centralized while still giving the business module a clean semantic API.
55
+
56
+ ## Recipe 1: add a thin business facade over the existing resource-owner
57
+
58
+ ### Use this when
59
+
60
+ The business module needs custom semantic methods, but the resource cache owner already exists and should remain authoritative.
61
+
62
+ Typical forward-chain case:
63
+
64
+ - backend adds a custom action such as `summary` or `deleteForce`
65
+ - frontend contract is regenerated or refreshed
66
+ - frontend needs a clean business-facing model method
67
+ - but should **not** create a second cache owner for the same resource
68
+
69
+ ### Recommended shape
70
+
71
+ ```typescript
72
+ const StudentResource = 'training-student:student';
73
+
74
+ @Model()
75
+ export class ModelStudent extends BeanModelBase {
76
+ @Use({ beanFullName: 'rest-resource.model.resource' })
77
+ protected get $$modelResource(): ModelResource {
78
+ return usePrepareArg(StudentResource, true);
79
+ }
80
+
81
+ summary(id: TableIdentity) {
82
+ return this.$$modelResource.queryItem({
83
+ id,
84
+ action: 'summary',
85
+ queryFn: async () => {
86
+ const res = await this.scope.api.trainingStudent.summary({ params: { id } });
87
+ return res ?? null;
88
+ },
89
+ meta: {
90
+ disableSuspenseOnInit: true,
91
+ },
92
+ });
93
+ }
94
+
95
+ deleteForce(id: TableIdentity) {
96
+ return this.$$modelResource.mutationItem<void, void>({
97
+ id,
98
+ action: 'deleteForce',
99
+ mutationFn: async () => {
100
+ await this.scope.api.trainingStudent.deleteForce({ params: { id } });
101
+ },
102
+ });
103
+ }
104
+ }
105
+ ```
106
+
107
+ ### Why this works well
108
+
109
+ - the business module gets a semantic model surface
110
+ - the existing `rest-resource` owner remains the single cache owner
111
+ - item keys, invalidation policy, and selector-based resource identity stay centralized
112
+ - custom UI code can call `ModelStudent.summary(...)` without rebuilding resource semantics locally
113
+
114
+ ### Typical consumer shape
115
+
116
+ A custom cell or command can consume the thin facade and trigger the semantic method:
117
+
118
+ ```typescript
119
+ const modelStudent = (await ctx.bean._getBean(
120
+ 'training-student.model.student',
121
+ true,
122
+ )) as ModelStudent;
123
+ const querySummary = modelStudent.summary(id);
124
+ const { data: summary } = await querySummary.refetch();
125
+ ```
126
+
127
+ ### Avoid
128
+
129
+ Do not create a second model that independently caches the same student resource list/item state just because a custom action was added.
130
+
131
+ That usually creates competing cache ownership and makes forward-chain maintenance harder.
132
+
133
+ ## Recipe 2: inside that facade, add a row-level custom query
134
+
135
+ ### Use this when
136
+
137
+ You already chose the thin-facade pattern from Recipe 1, and the business module needs more than the standard `view(id)` query.
138
+
139
+ Examples:
140
+
141
+ - summary detail
142
+ - audit detail
143
+ - timeline detail
144
+ - metrics detail
145
+
146
+ ### Recommended shape
147
+
148
+ ```typescript
149
+ summary(id: TableIdentity) {
150
+ return this.$$modelResource.queryItem({
151
+ id,
152
+ action: 'summary',
153
+ queryFn: async () => {
154
+ const res = await this.scope.api.trainingStudent.summary({ params: { id } });
155
+ return res ?? null;
156
+ },
157
+ meta: {
158
+ disableSuspenseOnInit: true,
159
+ },
160
+ });
161
+ }
162
+ ```
163
+
164
+ ### Why this works well
165
+
166
+ - the thin facade adds business semantics without becoming a second owner
167
+ - row-level cache identity still stays under the existing resource-owner
168
+ - item-level key structure and selector-based isolation remain centralized in `rest-resource`
169
+
170
+ ### Avoid
171
+
172
+ Do not rewrite this as an independent `$useStateData(...)` owner in the business model when the same row already belongs to the existing resource-owner.
173
+
174
+ ## Recipe 3: inside that facade, add a list-level query variant
175
+
176
+ ### Use this when
177
+
178
+ You already chose the thin-facade pattern from Recipe 1, and the business module needs a second list-style endpoint beyond the default `select(query)`.
179
+
180
+ Examples:
181
+
182
+ - archived list
183
+ - pending list
184
+ - dashboard list
185
+ - lightweight picker list
186
+
187
+ ### Recommended shape
188
+
189
+ ```typescript
190
+ selectArchived(query?: ITableQuery) {
191
+ return this.$$modelResource.selectGeneral('archived', query);
192
+ }
193
+ ```
194
+
195
+ ### Why this works well
196
+
197
+ - the business-facing model stays thin
198
+ - list-level cache identity still belongs to the existing resource-owner
199
+ - list variants remain compatible with the same invalidation and selector semantics
200
+
201
+ ### Avoid
202
+
203
+ Do not create another list owner with a parallel `keySelect(...)` convention when the list is still part of the same resource boundary.
204
+
205
+ ## Recipe 4: inside that facade, add a row-level custom mutation
206
+
207
+ ### Use this when
208
+
209
+ You already chose the thin-facade pattern from Recipe 1, and the business module needs a business action beyond create/update/delete.
210
+
211
+ Examples:
212
+
213
+ - deleteForce
214
+ - archive
215
+ - publish
216
+ - approve
217
+ - restore
218
+
219
+ ### Recommended shape
220
+
221
+ ```typescript
222
+ deleteForce(id: TableIdentity) {
223
+ return this.$$modelResource.mutationItem<void, void>({
224
+ id,
225
+ action: 'deleteForce',
226
+ mutationFn: async () => {
227
+ await this.scope.api.trainingStudent.deleteForce({ params: { id } });
228
+ },
229
+ });
230
+ }
231
+ ```
232
+
233
+ ### Why this works well
234
+
235
+ - mutation ownership still stays under the existing resource-owner
236
+ - item/list invalidation remains centralized
237
+ - the business-facing model exposes semantic actions without competing for cache ownership
238
+
239
+ ## Recipe 5: customize invalidation for a special mutation
240
+
241
+ ### Use this when
242
+
243
+ A custom mutation should not follow the default invalidation strategy, but cache ownership should still remain inside the existing resource-owner.
244
+
245
+ ### Recommended shape
246
+
247
+ ```typescript
248
+ publish(id: TableIdentity) {
249
+ return this.$$modelResource.mutationItem<void, void>({
250
+ id,
251
+ action: 'publish',
252
+ invalidateSelect: false,
253
+ mutationFn: async () => {
254
+ await this.scope.api.trainingStudent.publish({ params: { id } });
255
+ },
256
+ onSuccess: async () => {
257
+ await this.$$modelResource.$invalidateQueries({ queryKey: ['select'] });
258
+ await this.$$modelResource.$invalidateQueries({ queryKey: ['item', id] });
259
+ await this.$$modelResource.$invalidateQueries({ queryKey: ['select', 'dashboard'] });
260
+ },
261
+ });
262
+ }
263
+ ```
264
+
265
+ ### Why this works well
266
+
267
+ - the existing resource-owner remains the source of truth for consistency rules
268
+ - special cache dependencies stay explicit
269
+ - pages do not need to remember hidden follow-up refetch rules
270
+
271
+ ### Avoid
272
+
273
+ Do not spread this invalidation policy across page code, modal code, and button handlers.
274
+
275
+ ## Recipe 6: expose permission-oriented helpers
276
+
277
+ ### Use this when
278
+
279
+ Several screens need the same permission-based business decision.
280
+
281
+ Examples:
282
+
283
+ - can archive this resource?
284
+ - can publish this row?
285
+ - should a bulk action be visible?
286
+
287
+ ### Recommended shape
288
+
289
+ ```typescript
290
+ canArchive() {
291
+ return this.$computed(() => {
292
+ return !!this.$$modelResource.permissions?.actions?.archive;
293
+ });
294
+ }
295
+ ```
296
+
297
+ Or row-oriented logic:
298
+
299
+ ```typescript
300
+ canPublishRow(row: Entity) {
301
+ return !!this.$$modelResource.permissions?.actions?.publish && row.status === 'draft';
302
+ }
303
+ ```
304
+
305
+ ### Why this works well
306
+
307
+ - permission semantics stay close to the existing resource owner
308
+ - multiple pages reuse the same rule
309
+ - UI code gets simpler and more consistent
310
+
311
+ ## Recipe 7: expose schema-oriented convenience helpers
312
+
313
+ ### Use this when
314
+
315
+ Multiple forms or blocks need the same schema-based interpretation.
316
+
317
+ Examples:
318
+
319
+ - whether a field should be hidden in a certain scene
320
+ - whether a resource uses a special title field
321
+ - default schema chosen for a custom scene
322
+
323
+ ### Recommended shape
324
+
325
+ ```typescript
326
+ getTitleField() {
327
+ return this.$computed(() => {
328
+ return this.$$modelResource.schemaRow?.properties?.title ? 'title' : 'name';
329
+ });
330
+ }
331
+ ```
332
+
333
+ ### Why this works well
334
+
335
+ - schema interpretation stays reusable
336
+ - pages do not duplicate metadata reading logic
337
+ - metadata ownership still stays under the existing resource-owner
338
+
339
+ ## Recipe 8: add custom default form data
340
+
341
+ ### Use this when
342
+
343
+ Create forms need richer default values than the generic schema default alone.
344
+
345
+ ### Recommended shape
346
+
347
+ ```typescript
348
+ getCreateDefaultData() {
349
+ const data = this.$$modelResource.getQueryDataDefaultValue(this.$$modelResource.schemaCreate) ?? {};
350
+ return {
351
+ ...data,
352
+ status: 'draft',
353
+ enabled: true,
354
+ };
355
+ }
356
+ ```
357
+
358
+ Then integrate it through the business-facing form helper:
359
+
360
+ ```typescript
361
+ getFormData(formMeta: IFormMeta, id?: TableIdentity) {
362
+ if (formMeta.formMode === 'edit' && formMeta.editMode === 'create') {
363
+ return this.getCreateDefaultData();
364
+ }
365
+ return this.$$modelResource.getFormData(formMeta, id);
366
+ }
367
+ ```
368
+
369
+ ### Why this works well
370
+
371
+ - create defaults stay resource-owned
372
+ - form behavior remains consistent across entry points
373
+ - the thin facade only adds semantic customization on top of the existing form flow
374
+
375
+ ## Recipe 9: add batch action support
376
+
377
+ ### Use this when
378
+
379
+ The resource supports multi-row operations.
380
+
381
+ Examples:
382
+
383
+ - batch delete
384
+ - batch archive
385
+ - batch enable/disable
386
+
387
+ ### Recommended shape
388
+
389
+ ```typescript
390
+ batchArchive(ids: TableIdentity[]) {
391
+ return this.$$modelResource.$useMutationData<void, TableIdentity[]>({
392
+ mutationKey: ['batchArchive'],
393
+ mutationFn: async ids => {
394
+ await this.scope.api.trainingStudent.batchArchive({ ids });
395
+ },
396
+ onSuccess: async () => {
397
+ await this.$$modelResource.$invalidateQueries({ queryKey: ['select'] });
398
+ },
399
+ });
400
+ }
401
+ ```
402
+
403
+ ### Why this works well
404
+
405
+ - batch behavior stays modeled explicitly
406
+ - list invalidation policy remains visible
407
+ - cache ownership still stays with the existing resource-owner even when row-level helpers are not the right fit
408
+
409
+ ## Recipe 10: keep generic blocks working while adding resource semantics
410
+
411
+ ### Use this when
412
+
413
+ You want richer resource behavior but still rely on generic blocks such as page or page-entry consumers.
414
+
415
+ ### Recommended approach
416
+
417
+ - preserve stable surfaces like `select`, `view`, `schemaFilter`, `schemaRow`, `getFormSchema`, and `getFormData`
418
+ - add new methods around those stable surfaces instead of replacing them
419
+ - keep custom semantics additive where possible
420
+
421
+ ### Why this works well
422
+
423
+ You get stronger business behavior without sacrificing reusable infrastructure.
424
+
425
+ ## Recipe 11: move repeated page logic back into the model
426
+
427
+ ### Use this when
428
+
429
+ You notice the same resource-specific logic appearing across several page controllers.
430
+
431
+ Typical signals:
432
+
433
+ - repeated action visibility checks
434
+ - repeated fetches to the same custom endpoint
435
+ - repeated default-form preparation
436
+ - repeated manual invalidation after the same mutation
437
+
438
+ ### Recommended refactor direction
439
+
440
+ 1. identify the repeated resource semantic
441
+ 2. add one model method or computed helper for it
442
+ 3. update pages to consume that stable model surface
443
+ 4. keep the invalidation or schema logic centralized in the model
444
+
445
+ ### Why this works well
446
+
447
+ It restores the resource-owner boundary and reduces drift.
448
+
449
+ ## Recipe 12: start direct, then add a thin facade only when semantics appear
450
+
451
+ ### Use this when
452
+
453
+ A resource does not yet need custom business methods, but you expect it may later.
454
+
455
+ ### Recommended approach
456
+
457
+ - start with direct `ModelResource` usage
458
+ - let generic pages or forms consume the existing resource-owner surface
459
+ - add a thin business facade only when the UI really needs semantic methods such as `summary(...)` or `deleteForce(...)`
460
+
461
+ ### Why this works well
462
+
463
+ - the programming model stays more uniform
464
+ - you avoid premature wrapper growth
465
+ - later business semantics can still be added without changing the single-owner boundary
466
+
467
+ ## Recipe 13: decide whether logic belongs in model or page
468
+
469
+ ### Put it in the model when
470
+
471
+ - it is resource semantics
472
+ - multiple pages should share it
473
+ - it affects query or mutation ownership
474
+ - it affects invalidation or form/schema decisions
475
+
476
+ ### Keep it in the page when
477
+
478
+ - it is only local presentation flow
479
+ - it has no resource reuse value
480
+ - it is temporary UI behavior with no resource meaning
481
+
482
+ ### Quick test
483
+
484
+ Ask:
485
+
486
+ > if another screen reused this resource, should it inherit the same rule?
487
+
488
+ If yes, the logic probably belongs in the model.
489
+
490
+ ## A compact cookbook workflow
491
+
492
+ When adding a new resource behavior, use this decision order:
493
+
494
+ 1. Is this list-level, row-level, batch-level, or form-level?
495
+ 2. Can I reuse `selectGeneral`, `queryItem`, or `mutationItem`?
496
+ 3. Does the behavior need custom invalidation?
497
+ 4. Should generic pages or forms keep working unchanged?
498
+ 5. Is this resource semantics or only page presentation?
499
+
500
+ That sequence usually leads to a cleaner implementation.
501
+
502
+ ## Final takeaway
503
+
504
+ The most useful cookbook habit is simple:
505
+
506
+ > when a resource behavior repeats, give it a named model-owned surface instead of letting pages rediscover it independently.
507
+
508
+ That is how a resource model grows in a clean and Zova-native way.