cabloy 5.1.60 → 5.1.61

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 (76) 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/references/follow-up-checklist.md +1 -0
  4. package/.claude/skills/cabloy-contract-loop/SKILL.md +89 -16
  5. package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +102 -14
  6. package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +4 -0
  7. package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +32 -14
  8. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +11 -0
  9. package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +2 -0
  10. package/.claude/skills/cabloy-module-removal/SKILL.md +144 -0
  11. package/.claude/skills/cabloy-resource-field-update/SKILL.md +7 -0
  12. package/.claude/skills/cabloy-zova-source-reading/SKILL.md +221 -0
  13. package/.claude/skills/cabloy-zova-source-reading/references/analysis-modes.md +91 -0
  14. package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +117 -0
  15. package/CHANGELOG.md +22 -0
  16. package/CLAUDE.md +10 -0
  17. package/cabloy-docs/.vitepress/config.mjs +50 -4
  18. package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
  19. package/cabloy-docs/ai/docs-skills-rules-mapping.md +14 -0
  20. package/cabloy-docs/ai/future-skill-roadmap.md +10 -7
  21. package/cabloy-docs/ai/introduction.md +1 -0
  22. package/cabloy-docs/ai/playbook-backend-module.md +6 -0
  23. package/cabloy-docs/ai/playbook-module-removal.md +164 -0
  24. package/cabloy-docs/ai/skills.md +11 -0
  25. package/cabloy-docs/backend/dto-guide.md +6 -0
  26. package/cabloy-docs/backend/entity-guide.md +18 -0
  27. package/cabloy-docs/backend/introduction.md +2 -0
  28. package/cabloy-docs/backend/serialization-guide.md +10 -0
  29. package/cabloy-docs/backend/status-guide.md +271 -0
  30. package/cabloy-docs/frontend/api-guide.md +2 -0
  31. package/cabloy-docs/frontend/bean-scene-authoring.md +2 -0
  32. package/cabloy-docs/frontend/cli.md +12 -0
  33. package/cabloy-docs/frontend/command-scene-authoring.md +495 -0
  34. package/cabloy-docs/frontend/design-principles.md +6 -0
  35. package/cabloy-docs/frontend/fetch-interceptor-guide.md +440 -0
  36. package/cabloy-docs/frontend/form-guide.md +795 -0
  37. package/cabloy-docs/frontend/foundation.md +29 -0
  38. package/cabloy-docs/frontend/introduction.md +12 -1
  39. package/cabloy-docs/frontend/ioc-and-beans.md +6 -0
  40. package/cabloy-docs/frontend/mock-guide.md +1 -0
  41. package/cabloy-docs/frontend/model-architecture.md +252 -39
  42. package/cabloy-docs/frontend/model-resource-best-practices.md +379 -0
  43. package/cabloy-docs/frontend/model-resource-cookbook.md +505 -0
  44. package/cabloy-docs/frontend/model-resource-owner-pattern.md +382 -0
  45. package/cabloy-docs/frontend/model-resource-usage-guide.md +318 -0
  46. package/cabloy-docs/frontend/model-state-guide.md +366 -13
  47. package/cabloy-docs/frontend/openapi-sdk-guide.md +5 -2
  48. package/cabloy-docs/frontend/page-guide.md +6 -0
  49. package/cabloy-docs/frontend/quickstart.md +4 -0
  50. package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +266 -0
  51. package/cabloy-docs/frontend/server-data.md +2 -0
  52. package/cabloy-docs/frontend/zova-form-source-reading-map.md +295 -0
  53. package/cabloy-docs/frontend/zova-form-under-the-hood.md +556 -0
  54. package/cabloy-docs/frontend/zova-reactivity-under-the-hood.md +320 -0
  55. package/cabloy-docs/frontend/zova-source-reading-map.md +327 -0
  56. package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +308 -0
  57. package/cabloy-docs/fullstack/contract-loop-playbook.md +350 -0
  58. package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +44 -1
  59. package/cabloy-docs/fullstack/introduction.md +12 -1
  60. package/cabloy-docs/fullstack/openapi-to-sdk.md +19 -9
  61. package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +2 -2
  62. package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +30 -5
  63. package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +9 -7
  64. package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +2 -0
  65. package/cabloy-docs/fullstack/tutorials-overview.md +16 -3
  66. package/cabloy-docs/reference/bean-scene-boilerplates.md +2 -0
  67. package/package.json +2 -1
  68. package/scripts/init.ts +2 -18
  69. package/scripts/initTestData.ts +25 -0
  70. package/scripts/upgrade.ts +17 -2
  71. package/vona/pnpm-lock.yaml +94 -4
  72. package/zova/packages-cli/cli/package.json +2 -2
  73. package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/module/openapi.config.ts +6 -1
  74. package/zova/packages-cli/cli-set-front/package.json +1 -1
  75. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +34 -4
  76. package/zova/pnpm-lock.yaml +20 -20
@@ -1,12 +1,28 @@
1
1
  # Model State Guide
2
2
 
3
- This guide explains how model-based server-data state works in Zova within the Cabloy monorepo.
3
+ This guide explains how to author and consume model-managed state in Zova within the Cabloy monorepo.
4
4
 
5
- ## Why the model layer exists
5
+ Read [Model Architecture](/frontend/model-architecture) first if you want the broader architectural role of Model.
6
6
 
7
- Zova uses model-based state management on top of API access so remote data can participate in a more unified caching and usage model.
7
+ If you specifically want the scalable resource-facade pattern, continue with [Model Resource Owner Pattern](/frontend/model-resource-owner-pattern).
8
8
 
9
- This improves runtime performance and developer experience by building on top of TanStack Query rather than exposing only raw request flows.
9
+ If you want to apply that pattern in your own module with a more uniform two-usage model, continue with [Using `ModelResource` in Your Module](/frontend/model-resource-usage-guide).
10
+
11
+ ## Why the model-state layer exists
12
+
13
+ Zova uses model-based state management so cached remote data and several local state categories can participate in one broader model system.
14
+
15
+ This improves runtime performance and developer experience by building on top of TanStack Query while keeping the developer-facing surface aligned with Zova beans.
16
+
17
+ The key point is that model state is not limited to server data.
18
+
19
+ Current `a-model` source supports a broader state family that includes:
20
+
21
+ - remote/query-style state
22
+ - in-memory state
23
+ - local-storage state
24
+ - cookie-backed state
25
+ - async persisted db state
10
26
 
11
27
  ## Create a model
12
28
 
@@ -16,13 +32,15 @@ Example: create a model named `menu` in module `demo-student`.
16
32
  npm run zova :create:bean model menu -- --module=demo-student
17
33
  ```
18
34
 
19
- ## Model definition
35
+ ## Basic model definition
20
36
 
21
37
  Representative pattern:
22
38
 
23
39
  ```typescript
40
+ import { BeanModelBase, Model } from 'zova-module-a-model';
41
+
24
42
  @Model()
25
- export class ModelMenu {
43
+ export class ModelMenu extends BeanModelBase {
26
44
  retrieveMenus() {
27
45
  return this.$useStateData({
28
46
  queryKey: ['retrieveMenus'],
@@ -36,11 +54,11 @@ export class ModelMenu {
36
54
  }
37
55
  ```
38
56
 
39
- This pattern is important because it shows that model logic is not just local state. It is also the place where cached remote data becomes a reusable abstraction.
57
+ This pattern matters because it shows that model logic is the place where cached remote data becomes a reusable abstraction.
40
58
 
41
59
  ## Using a model
42
60
 
43
- Representative pattern:
61
+ Representative consumption pattern:
44
62
 
45
63
  ```typescript
46
64
  @Use()
@@ -53,18 +71,353 @@ protected render() {
53
71
  }
54
72
  ```
55
73
 
74
+ The important point is architectural:
75
+
76
+ - the page/controller consumes the model
77
+ - the model owns the reusable data access and cache behavior
78
+
79
+ ## The main helper families
80
+
81
+ The current source organizes model state around these helper families.
82
+
83
+ ### 1. `$useStateData(...)`
84
+
85
+ Use this for cached remote data or other query-style state where the caller wants the full query object.
86
+
87
+ Representative characteristics from current source:
88
+
89
+ - it delegates to `$useQuery(...)`
90
+ - it memoizes the query wrapper by prefixed query key
91
+ - it auto-calls `suspense()` on first creation unless disabled by metadata
92
+ - it is the main bridge from model methods to query-style UI state
93
+
94
+ Representative example:
95
+
96
+ ```typescript
97
+ findAll() {
98
+ return this.$useStateData({
99
+ queryKey: ['list'],
100
+ queryFn: async () => {
101
+ return this.scope.api.todo.findAll();
102
+ },
103
+ });
104
+ }
105
+ ```
106
+
107
+ ## 2. `$useMutationData(...)`
108
+
109
+ Use this for model-owned mutation state.
110
+
111
+ Current-source behavior includes:
112
+
113
+ - `mutationKey` is required
114
+ - the key is automatically prefixed with model identity
115
+ - the mutation wrapper is memoized by prefixed key
116
+ - default error handling is attached unless disabled
117
+
118
+ Representative example:
119
+
120
+ ```typescript
121
+ create() {
122
+ return this.$useMutationData({
123
+ mutationKey: ['create'],
124
+ mutationFn: async body => {
125
+ return this.scope.api.todo.create(body);
126
+ },
127
+ onSuccess: () => {
128
+ this.$invalidateQueries({ queryKey: ['list'] });
129
+ },
130
+ });
131
+ }
132
+ ```
133
+
134
+ The pattern to notice is that the model owns both the mutation and the follow-up cache invalidation policy.
135
+
136
+ ## 3. `$useStateMem(...)`
137
+
138
+ Use this when the state should stay only in memory.
139
+
140
+ Current-source characteristics:
141
+
142
+ - no persistence
143
+ - `enabled: false`
144
+ - `staleTime: Infinity`
145
+ - still uses model-owned query cache semantics under the hood
146
+
147
+ This is useful when you want model ownership and query-key identity without local persistence.
148
+
149
+ ## 4. `$useStateLocal(...)`
150
+
151
+ Use this when the model state should persist in local storage.
152
+
153
+ Current-source characteristics:
154
+
155
+ - sync local storage persistence
156
+ - simplified storage keys by default
157
+ - `enabled: false`
158
+ - `staleTime: Infinity`
159
+ - state still flows through model-owned query cache first
160
+
161
+ A useful mental model is:
162
+
163
+ > local-storage state in Zova Model is not a separate store system. It is model-owned query state with a local-storage persister.
164
+
165
+ ## 5. `$useStateCookie(...)`
166
+
167
+ Use this when the model state should persist in cookies.
168
+
169
+ Current-source characteristics:
170
+
171
+ - sync cookie persistence
172
+ - cookie-type coercion support such as `boolean`, `number`, `date`, or `string`
173
+ - simplified storage keys by default
174
+ - still uses model-owned query cache semantics
175
+
176
+ This is particularly relevant for state that must participate in request-aware or SSR-adjacent flows.
177
+
178
+ ## 6. `$useStateDb(...)`
179
+
180
+ Use this when the model state should persist asynchronously in db-style client storage.
181
+
182
+ Current-source characteristics:
183
+
184
+ - async persistence through `localforage`
185
+ - `enabled: false`
186
+ - `staleTime: Infinity`
187
+ - explicit `ssr.dehydrate = false`
188
+ - getters may need async restore flow before data is available
189
+
190
+ This is useful for larger persisted client state that should outlive a page session without being stored in cookies or plain local storage.
191
+
192
+ ## 7. `$useStateComputed(...)`
193
+
194
+ Use this when the model should expose derived state that is still model-key-oriented but computed locally.
195
+
196
+ Current-source characteristics:
197
+
198
+ - it prefixes the query key
199
+ - it memoizes the computed value by the hashed prefixed key
200
+ - it uses `$computed(...)` rather than TanStack Query fetching
201
+
202
+ ## Query keys are model-owned, not global strings
203
+
204
+ One of the most important model-state behaviors is automatic key prefixing.
205
+
206
+ When a model uses:
207
+
208
+ ```typescript
209
+ queryKey: ['list']
210
+ ```
211
+
212
+ that logical key is prefixed internally with model identity, and with selector when selector mode is enabled.
213
+
214
+ That means model code can use short business-facing keys while still getting namespace isolation.
215
+
216
+ ## Persistence and restore behavior
217
+
218
+ Current source treats persistence as part of the model-state runtime.
219
+
220
+ Representative behaviors include:
221
+
222
+ - persisted data can be restored back into query state
223
+ - expired or busted persisted entries are removed automatically
224
+ - `defaultData` can initialize the cache when no restored value exists
225
+ - setting persisted state to `undefined` removes the persisted record
226
+
227
+ This is why model helpers are better understood as state-runtime helpers, not only convenience wrappers.
228
+
229
+ ## SSR-sensitive state choices
230
+
231
+ Model state choices can affect SSR behavior.
232
+
233
+ Current-source behaviors to remember:
234
+
235
+ - query state can be dehydrated on server and hydrated on client
236
+ - mutations are not dehydrated
237
+ - `db` state is explicitly not dehydrated
238
+ - server cannot use local-storage or db persistence backends
239
+ - cookie state is special because cookie access can still exist through the app cookie surface
240
+
241
+ Practical implication:
242
+
243
+ - do not assume every persisted model state behaves the same during SSR
244
+ - choose `mem`, `local`, `cookie`, `db`, or `data` based on ownership and hydration requirements, not only convenience
245
+
246
+ ## Real examples to read
247
+
248
+ ### Minimal query and mutation model
249
+
250
+ Read:
251
+
252
+ - `zova/src/suite/a-demo/modules/demo-todo/src/model/todo.ts`
253
+
254
+ This file shows:
255
+
256
+ - extending `BeanModelBase`
257
+ - `@Model()` authoring
258
+ - query-style state through `$useStateData(...)`
259
+ - mutation-style state through `$useMutationData(...)`
260
+ - cache invalidation inside the model
261
+
262
+ ### Selector-enabled cache-oriented model
263
+
264
+ Read:
265
+
266
+ - `zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts`
267
+
268
+ This file shows:
269
+
270
+ - `@Model({ enableSelector: true, ... })`
271
+ - richer model initialization in `__init__`
272
+ - mixed use of `$useStateMem(...)` and `$useStateDb(...)`
273
+ - model-owned tab state with cache and persistence decisions
274
+
275
+ ### SSR-sensitive auth model
276
+
277
+ Read:
278
+
279
+ - `zova/src/suite/a-home/modules/home-passport/src/model/passport.ts`
280
+
281
+ This file shows:
282
+
283
+ - mixing `mem`, `local`, and `cookie` state in one model
284
+ - SSR-aware state choices
285
+ - model ownership of auth-related cached state and mutation flows
286
+
287
+ ### Generic resource-owner model
288
+
289
+ Read:
290
+
291
+ - `zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts`
292
+
293
+ This file is especially important because it shows a higher-level infrastructure pattern rather than a small feature model.
294
+
295
+ It demonstrates that a model can act as a **resource owner facade** for CRUD-oriented business modules.
296
+
297
+ What this example shows:
298
+
299
+ - `@Model({ enableSelector: true })` uses selector to isolate one model instance per resource name
300
+ - `__init__(resource)` bootstraps resource metadata and resolves `resourceApi` before normal query usage
301
+ - `$computed(...)` can expose permissions, form provider, and schema surfaces as model-owned derived state
302
+ - `$useStateData(...)` drives select/view queries
303
+ - `$useMutationData(...)` is wrapped by reusable `create`, `update`, `delete`, and `mutationItem` helpers
304
+ - invalidation policy is centralized so list and item caches stay coherent after mutations
305
+ - model methods can combine `$fetch`, `$sdk`, OpenAPI schema helpers, and form-oriented helpers behind one reusable boundary
306
+
307
+ This is one of the best examples for understanding how Zova Model scales from simple data queries to reusable domain infrastructure.
308
+
309
+ #### Why `enableSelector` matters here
310
+
311
+ This model is not meant to represent only one concrete resource forever.
312
+
313
+ It is a reusable generic model class that can serve many resources.
314
+
315
+ That is why `enableSelector` is essential.
316
+
317
+ The model passes `resource` into `super.__init__(resource)`, so the resource name becomes the selector identity for that model instance.
318
+
319
+ At runtime, model query keys are therefore prefixed not only by the bean full name but also by the selected resource.
320
+
321
+ That means two consumers can both use logical keys such as:
322
+
323
+ ```typescript
324
+ ['select', '', hashkey(query)]
325
+ ['item', id, 'view']
326
+ ```
327
+
328
+ without colliding with each other, because the effective cache identity is separated by resource selector.
329
+
330
+ A practical reading takeaway is:
331
+
332
+ > selector turns one generic `ModelResource` implementation into many isolated resource-specific runtime instances.
333
+
334
+ #### Why this is a resource owner, not only a CRUD helper
335
+
336
+ A plain CRUD helper usually forwards requests.
337
+
338
+ `ModelResource` does more than that.
339
+
340
+ It owns several resource-level concerns together:
341
+
342
+ - bootstrap of resource metadata through `$QueryAutoLoad(...)`
343
+ - resolution of the final `resourceApi`
344
+ - permissions lookup
345
+ - OpenAPI schema access for view/create/update/select
346
+ - form integration such as submit mutation choice and default form data
347
+ - query and mutation cache invalidation rules
348
+
349
+ That is why this model is better understood as a **resource owner facade**.
350
+
351
+ In application code, prefer consuming that existing owner directly or adding a thin facade over it, while the lower-level `$fetch` and `$sdk` details stay inside the owner boundary.
352
+
353
+ #### How the cache-key design works
354
+
355
+ The cache-key design in this file is also worth reading carefully.
356
+
357
+ It separates three levels of identity:
358
+
359
+ - `keySelect(actionPath, query)` → list/query-level state
360
+ - `keyItemRoot(id)` → all state owned by one row id
361
+ - `keyItem(id, action)` → one concrete row action such as `view` or `update`
362
+
363
+ Representative shapes are:
364
+
365
+ ```typescript
366
+ ['select', actionPath ?? '', hashkey(query)]
367
+ ['item', id]
368
+ ['item', id, action]
369
+ ```
370
+
371
+ This makes the invalidation strategy much clearer:
372
+
373
+ - create invalidates the select-level list cache
374
+ - update/delete invalidate the select-level list cache and the item root for that row
375
+ - item-specific queries such as `view(id)` live under the item branch
376
+
377
+ The practical benefit is that the model does not scatter cache policy across pages.
378
+
379
+ Instead, the model itself defines how list-level and row-level state stay coherent after mutations.
380
+
381
+ #### Why this example is a strong source-reading specimen
382
+
383
+ If `demo-todo` shows the minimal pattern for model queries and mutations, `rest-resource` shows the scalable pattern.
384
+
385
+ Use it when you want to understand how Zova Model can support:
386
+
387
+ - generic reusable business infrastructure
388
+ - selector-isolated model instances
389
+ - unified resource schema and permission ownership
390
+ - form-oriented integration on top of query state
391
+ - centralized invalidation semantics for larger UI systems
392
+
56
393
  ## Relationship to the server-data ladder
57
394
 
58
- In the new docs, think about the layers like this:
395
+ Think about the layers like this:
59
396
 
60
397
  - `$fetch` → direct request access
61
398
  - `$api` → business-oriented service methods
62
- - `Model` → cached, reusable, UI-friendly remote state
399
+ - `Model` → cached, reusable, persistence-aware state
63
400
 
64
401
  That makes the model layer one of the most important bridges between backend contracts and frontend rendering.
65
402
 
66
- ## Implementation checks for model-based state changes
403
+ ## Practical design checks
404
+
405
+ When adding frontend state, avoid jumping straight to ad hoc request logic or generic store habits.
406
+
407
+ Instead ask:
408
+
409
+ 1. should this state live in an existing model?
410
+ 2. should it be `data`, `mem`, `local`, `cookie`, or `db` state?
411
+ 3. should the model own invalidation or refetch behavior?
412
+ 4. does persistence or SSR change the right helper choice?
413
+ 5. should the page/controller consume a model instead of owning this state directly?
414
+
415
+ That usually produces cleaner, more Cabloy-native code.
416
+
417
+ ## Final takeaway
418
+
419
+ The most important usage insight is simple:
67
420
 
68
- When you see repeated frontend data usage, caching concerns, or UI state that depends on remote data, ask whether the right abstraction is a model instead of a direct API call in the page.
421
+ > In Zova, model state is not only “fetch some data”. It is a unified model-owned runtime for query state, local state, persistence, invalidation, and SSR-aware reuse.
69
422
 
70
- That usually leads to cleaner SSR behavior, cleaner reuse, and a more Cabloy-native structure.
423
+ Once that is clear, the helper family in `a-model` reads as one coherent system instead of several unrelated APIs.
@@ -30,12 +30,15 @@ npm run zova :openapi:config demo-student
30
30
 
31
31
  ## Module-level config
32
32
 
33
- Each module can control which backend operations belong to it.
33
+ Each module must explicitly declare which backend operations belong to it.
34
34
 
35
35
  Representative idea:
36
36
 
37
- - configure matching rules in the module OpenAPI config
37
+ - configure `operations.match` or `operations.ignore` in the module OpenAPI config
38
38
  - keep API ownership aligned with the module boundary
39
+ - avoid generating large amounts of unrelated SDK output for the current module
40
+
41
+ If both `operations.match` and `operations.ignore` are left empty, `:openapi:generate` should fail fast and ask you to define one of them explicitly. This fail-fast feedback helps both developers and AI agents notice the missing ownership rule immediately and correct it before unrelated SDK output is generated.
39
42
 
40
43
  This modular split matters because Cabloy does not treat the frontend as one flat API client surface.
41
44
 
@@ -215,6 +215,12 @@ These refactors are supported by Zova CLI commands rather than requiring a fully
215
215
 
216
216
  When generating or editing a Zova page, preserve the page/controller mental model instead of rewriting the code into a generic Vue single-file-component pattern.
217
217
 
218
+ If you want a stronger reading bridge from Vue habits into page-controller source code, also read:
219
+
220
+ - [Reading Zova for Vue Developers](/frontend/reading-zova-for-vue-developers)
221
+ - [Zova Reactivity Under the Hood](/frontend/zova-reactivity-under-the-hood)
222
+ - [Zova Source Reading Map](/frontend/zova-source-reading-map)
223
+
218
224
  A better default is:
219
225
 
220
226
  1. use the Zova page generator
@@ -200,4 +200,8 @@ Read next:
200
200
 
201
201
  - [Frontend (Zova)](/frontend/introduction)
202
202
  - [Frontend Foundation](/frontend/foundation)
203
+ - [Reading Zova for Vue Developers](/frontend/reading-zova-for-vue-developers)
204
+ - [Zova vs Vue 3 Comparison](/frontend/zova-vs-vue3-comparison)
205
+ - [Zova Reactivity Under the Hood](/frontend/zova-reactivity-under-the-hood)
206
+ - [Zova Source Reading Map](/frontend/zova-source-reading-map)
203
207
  - [Design Principles](/frontend/design-principles)