cabloy 5.1.59 → 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.
- package/.claude/hooks/contract-loop-gate.ts +296 -0
- package/.claude/settings.json +16 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -0
- package/.claude/skills/cabloy-contract-loop/SKILL.md +103 -14
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +126 -12
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +148 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +49 -13
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +11 -0
- package/.claude/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +2 -0
- package/.claude/skills/cabloy-module-removal/SKILL.md +144 -0
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +274 -0
- package/.claude/skills/cabloy-resource-field-update/evals/evals.json +53 -0
- package/.claude/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md +102 -0
- package/.claude/skills/cabloy-resource-field-update/references/field-update-decision-tree.md +120 -0
- package/.claude/skills/cabloy-resource-field-update/references/follow-up-checklist.md +80 -0
- package/.claude/skills/cabloy-resource-field-update/references/verification-checklist.md +97 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +221 -0
- package/.claude/skills/cabloy-zova-source-reading/references/analysis-modes.md +91 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +117 -0
- package/.github/workflows/docs-pages.yml +2 -0
- package/.github/workflows/vona-cov-pg.yml +2 -0
- package/.github/workflows/vona-test-crud.yml +4 -2
- package/.github/workflows/vona-test-mysql.yml +2 -0
- package/.github/workflows/vona-test-pg.yml +2 -0
- package/.github/workflows/vona-test-sqlite3.yml +2 -0
- package/.github/workflows/vona-tsc.yml +2 -0
- package/.github/workflows/zova-ui.yml +2 -0
- package/.gitignore +0 -4
- package/CHANGELOG.md +52 -0
- package/CLAUDE.md +12 -0
- package/README.md +15 -0
- package/cabloy-docs/.vitepress/config.mjs +89 -0
- package/cabloy-docs/ai/class-placement-rule.md +2 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +14 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +14 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +27 -9
- package/cabloy-docs/ai/introduction.md +1 -0
- package/cabloy-docs/ai/playbook-backend-module.md +6 -0
- package/cabloy-docs/ai/playbook-module-removal.md +164 -0
- package/cabloy-docs/ai/skills.md +11 -0
- package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
- package/cabloy-docs/backend/cli.md +26 -1
- package/cabloy-docs/backend/dto-guide.md +6 -0
- package/cabloy-docs/backend/entity-guide.md +18 -0
- package/cabloy-docs/backend/foundation.md +28 -3
- package/cabloy-docs/backend/introduction.md +10 -0
- package/cabloy-docs/backend/serialization-guide.md +10 -0
- package/cabloy-docs/backend/service-guide.md +2 -0
- package/cabloy-docs/backend/status-guide.md +271 -0
- package/cabloy-docs/backend/websocket-call-flow.md +435 -0
- package/cabloy-docs/backend/websocket-guide.md +455 -0
- package/cabloy-docs/backend/websocket-protocol-guide.md +381 -0
- package/cabloy-docs/backend/websocket-usage-guide.md +356 -0
- package/cabloy-docs/frontend/api-guide.md +2 -0
- package/cabloy-docs/frontend/bean-scene-authoring.md +374 -0
- package/cabloy-docs/frontend/behavior-guide.md +449 -0
- package/cabloy-docs/frontend/cli.md +24 -0
- package/cabloy-docs/frontend/command-scene-authoring.md +495 -0
- package/cabloy-docs/frontend/design-principles.md +6 -0
- package/cabloy-docs/frontend/fetch-interceptor-guide.md +440 -0
- package/cabloy-docs/frontend/form-guide.md +795 -0
- package/cabloy-docs/frontend/foundation.md +29 -0
- package/cabloy-docs/frontend/introduction.md +17 -1
- package/cabloy-docs/frontend/ioc-and-beans.md +16 -9
- package/cabloy-docs/frontend/mock-guide.md +1 -0
- package/cabloy-docs/frontend/model-architecture.md +252 -39
- package/cabloy-docs/frontend/model-resource-best-practices.md +379 -0
- package/cabloy-docs/frontend/model-resource-cookbook.md +505 -0
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +382 -0
- package/cabloy-docs/frontend/model-resource-usage-guide.md +318 -0
- package/cabloy-docs/frontend/model-state-guide.md +366 -13
- package/cabloy-docs/frontend/openapi-sdk-guide.md +5 -2
- package/cabloy-docs/frontend/page-guide.md +6 -0
- package/cabloy-docs/frontend/quickstart.md +4 -0
- package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +266 -0
- package/cabloy-docs/frontend/router-tabs-admin-web-comparison.md +206 -0
- package/cabloy-docs/frontend/router-tabs-introduction.md +106 -0
- package/cabloy-docs/frontend/router-tabs-mechanism.md +469 -0
- package/cabloy-docs/frontend/router-tabs-overview.md +227 -0
- package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +343 -0
- package/cabloy-docs/frontend/server-data.md +2 -0
- package/cabloy-docs/frontend/ssr-architecture-overview.md +211 -0
- package/cabloy-docs/frontend/ssr-build-deploy-guide.md +308 -0
- package/cabloy-docs/frontend/ssr-review-checklist.md +184 -0
- package/cabloy-docs/frontend/ssr-troubleshooting-guide.md +301 -0
- package/cabloy-docs/frontend/zova-form-source-reading-map.md +295 -0
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +556 -0
- package/cabloy-docs/frontend/zova-reactivity-under-the-hood.md +320 -0
- package/cabloy-docs/frontend/zova-source-reading-map.md +327 -0
- package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +308 -0
- package/cabloy-docs/fullstack/contract-loop-playbook.md +350 -0
- package/cabloy-docs/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +44 -1
- package/cabloy-docs/fullstack/introduction.md +40 -0
- package/cabloy-docs/fullstack/openapi-to-sdk.md +19 -9
- package/cabloy-docs/fullstack/quickstart.md +7 -1
- package/cabloy-docs/fullstack/tutorial-1-first-module.md +111 -0
- package/cabloy-docs/fullstack/tutorial-2-first-crud.md +122 -0
- package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +131 -0
- package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +144 -0
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +146 -0
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +170 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +192 -0
- package/cabloy-docs/index.md +4 -3
- package/cabloy-docs/reference/bean-scene-boilerplates.md +75 -0
- package/cabloy-docs/reference/cli-reference.md +2 -0
- package/package.json +7 -2
- package/scripts/initTestData.ts +25 -0
- package/scripts/upgrade.ts +17 -2
- package/vona/packages-cli/cabloy-cli/package.json +2 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.module.ts +4 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +226 -1091
- package/vona/pnpm-workspace.yaml +0 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/assets/static/img/vona.svg +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/cli/templates/init/icon/boilerplate/icons/default/zova.svg +1 -1
- package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/module/openapi.config.ts +6 -1
- package/zova/packages-cli/cli-set-front/package.json +3 -3
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.module.ts +4 -0
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +34 -4
- package/zova/packages-cli/cli-set-front/src/lib/command/create.bean.ts +5 -1
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +282 -1311
- package/zova/pnpm-workspace.yaml +0 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/cabloy.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/vona.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/zova.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/src/.metadata/icons/groups/social.svg +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +9 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +66 -16
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +60 -18
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableActionRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableCell/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -103,6 +103,24 @@ Representative examples include:
|
|
|
103
103
|
- helpers such as `v.default`, `v.optional`, `v.array`
|
|
104
104
|
- OpenAPI metadata such as `v.title`, `v.description`, `v.example`, `v.openapi`
|
|
105
105
|
|
|
106
|
+
When mixing helper metadata and an explicit zod schema in `@Api.field(...)`, keep a small but important ordering rule:
|
|
107
|
+
|
|
108
|
+
- `z.xxx(...)` returns the zod schema instance, so place it as the **last argument**
|
|
109
|
+
- put helper metadata such as `v.xxx(...)` and `ZovaRender.xxx(...)` before the zod schema
|
|
110
|
+
- otherwise helpers written after the zod schema may stop taking effect
|
|
111
|
+
|
|
112
|
+
Representative pattern:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
@Api.field(
|
|
116
|
+
v.title($locale('Level')),
|
|
117
|
+
ZovaRender.order(3),
|
|
118
|
+
ZovaRender.field('basic-select:formFieldSelect', { items: levelItems }),
|
|
119
|
+
z.number().int().min(1).max(3),
|
|
120
|
+
)
|
|
121
|
+
level: number;
|
|
122
|
+
```
|
|
123
|
+
|
|
106
124
|
A practical rule is:
|
|
107
125
|
|
|
108
126
|
- use inference for straightforward fields
|
|
@@ -91,7 +91,7 @@ A practical rule is:
|
|
|
91
91
|
| -------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
92
92
|
| Dependency injection | explicit wiring in the current class | `@Use('demo-student.service.student')` |
|
|
93
93
|
| Dependency lookup | ordinary module-oriented business code | `this.scope.service.student` |
|
|
94
|
-
| Direct bean access | container-aware control or request
|
|
94
|
+
| Direct bean access | container-aware control through the app container or request scope | `this.bean._getBean(...)`, `this.ctx.bean._getBean(...)` |
|
|
95
95
|
| Fresh bean creation | workflows that should not reuse the ordinary resolved bean instance | `this.bean._newBean(...)` |
|
|
96
96
|
|
|
97
97
|
## Dependency injection vs dependency lookup vs direct bean access
|
|
@@ -134,6 +134,12 @@ this.ctx.bean._getBean('demo-student.service.student');
|
|
|
134
134
|
this.bean._newBean('demo-student.service.student');
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
A practical distinction is:
|
|
138
|
+
|
|
139
|
+
- `this.bean._getBean(...)` uses app-level container access
|
|
140
|
+
- `this.ctx.bean._getBean(...)` uses request-scoped container access
|
|
141
|
+
- `this.bean._newBean(...)` creates a fresh bean instance instead of reusing the ordinary resolved one
|
|
142
|
+
|
|
137
143
|
The important conceptual split is:
|
|
138
144
|
|
|
139
145
|
- injection wires a dependency into the current class
|
|
@@ -146,7 +152,7 @@ Legacy Vona essentials docs made bean identity more explicit, and that identity
|
|
|
146
152
|
|
|
147
153
|
The most important terms are:
|
|
148
154
|
|
|
149
|
-
- **bean identifier**: a dotted identity such as `{moduleName}.{sceneName}.{beanName}`
|
|
155
|
+
- **bean identifier**: for most scene-based beans, a dotted identity such as `{moduleName}.{sceneName}.{beanName}`
|
|
150
156
|
- **onion name**: a colon-based framework name such as `{moduleName}:{beanName}`
|
|
151
157
|
- **bean scene**: the operational family a bean belongs to, such as service, model, startup, queue, or broadcast
|
|
152
158
|
|
|
@@ -159,12 +165,31 @@ This matters because naming is not cosmetic. It affects:
|
|
|
159
165
|
|
|
160
166
|
A practical naming rule is:
|
|
161
167
|
|
|
162
|
-
-
|
|
168
|
+
- most scene-based beans use the fully qualified `module.scene.bean` form such as `demo-student.service.student`
|
|
163
169
|
- onion name uses the shorter `module:bean` form such as `demo-student:student`
|
|
164
170
|
- bean scene is the middle grouping layer that turns one module into operational families like `service`, `model`, `entity`, `dto`, or `startup`
|
|
171
|
+
- the built-in global `bean` scene is an intentional exception and uses the plain bean name in the global shorthand surface
|
|
172
|
+
|
|
173
|
+
If you need to create a **new backend bean scene** rather than only adding another bean to an existing scene, see [Backend Bean Scene Authoring](/backend/bean-scene-authoring).
|
|
165
174
|
|
|
166
175
|
For deciding whether backend base classes belong in `src/lib`, `src/service`, or the global bean shorthand surface, also see [Class Placement Rule](/ai/class-placement-rule).
|
|
167
176
|
|
|
177
|
+
## Module-local helper functions
|
|
178
|
+
|
|
179
|
+
When you extract reusable helper functions for one backend module, initialize the module `src/lib` directory and place those helpers there.
|
|
180
|
+
|
|
181
|
+
A practical rule is:
|
|
182
|
+
|
|
183
|
+
- keep shared pure helpers in `src/lib`
|
|
184
|
+
- export them from `src/lib/index.ts` when multiple module files should reuse them
|
|
185
|
+
- avoid leaving reusable helper logic duplicated or buried inside `entity`, `dto`, `service`, or `controller` files
|
|
186
|
+
- if the logic needs bean lifecycle, injection, selector lookup, or other container-managed behavior, do not treat it as a plain helper; re-evaluate `src/service` or another bean scene instead
|
|
187
|
+
|
|
188
|
+
This complements the class placement rule:
|
|
189
|
+
|
|
190
|
+
- `src/lib` is the normal home for pure reusable helper logic
|
|
191
|
+
- `src/service` or other bean scenes remain the fit for container-managed runtime behavior
|
|
192
|
+
|
|
168
193
|
## BeanBase built-ins
|
|
169
194
|
|
|
170
195
|
A large part of the backend essentials model is that ordinary backend beans already inherit a useful working surface from `BeanBase`.
|
|
@@ -60,7 +60,12 @@ Use this path when the task is about runtime shape, startup, instances, workers,
|
|
|
60
60
|
- [Worker Guide](/backend/worker-guide)
|
|
61
61
|
- [Election Guide](/backend/election-guide)
|
|
62
62
|
- [Queue Guide](/backend/queue-guide)
|
|
63
|
+
- [Status Guide](/backend/status-guide)
|
|
63
64
|
- [Broadcast Guide](/backend/broadcast-guide)
|
|
65
|
+
- [Web Socket Guide](/backend/websocket-guide)
|
|
66
|
+
- [Web Socket Usage Guide](/backend/websocket-usage-guide)
|
|
67
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide)
|
|
68
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow)
|
|
64
69
|
- [Schedule Guide](/backend/schedule-guide)
|
|
65
70
|
- [Redlock Guide](/backend/redlock-guide)
|
|
66
71
|
|
|
@@ -90,6 +95,11 @@ If your task is already inside the runtime and distributed family, read these gu
|
|
|
90
95
|
- [Worker Guide](/backend/worker-guide)
|
|
91
96
|
- [Election Guide](/backend/election-guide)
|
|
92
97
|
- [Queue Guide](/backend/queue-guide)
|
|
98
|
+
- [Status Guide](/backend/status-guide)
|
|
93
99
|
- [Broadcast Guide](/backend/broadcast-guide)
|
|
100
|
+
- [Web Socket Guide](/backend/websocket-guide)
|
|
101
|
+
- [Web Socket Usage Guide](/backend/websocket-usage-guide)
|
|
102
|
+
- [Web Socket Protocol Guide](/backend/websocket-protocol-guide)
|
|
103
|
+
- [Web Socket Call Flow](/backend/websocket-call-flow)
|
|
94
104
|
- [Schedule Guide](/backend/schedule-guide)
|
|
95
105
|
- [Redlock Guide](/backend/redlock-guide)
|
|
@@ -41,6 +41,16 @@ async findOne(id) {
|
|
|
41
41
|
|
|
42
42
|
This makes serialization an explicit request-path capability rather than an invisible response-side convention.
|
|
43
43
|
|
|
44
|
+
Important operational rule:
|
|
45
|
+
|
|
46
|
+
- for performance reasons, serialization is **not enabled by default**
|
|
47
|
+
- `v.serializerTransform(...)`, `v.serializerExclude()`, `v.serializerReplace(...)`, `v.serializerGetter(...)`, and `v.serializerCustom(...)` only declare field-level transform metadata
|
|
48
|
+
- those transforms do **not** run unless the target API explicitly enables serialization with `@Core.serializer()`
|
|
49
|
+
|
|
50
|
+
A practical debugging rule is:
|
|
51
|
+
|
|
52
|
+
- if a field-level serializer appears correct but the response still returns the raw value, first check whether the controller action uses `@Core.serializer()`
|
|
53
|
+
|
|
44
54
|
## Serializer transforms
|
|
45
55
|
|
|
46
56
|
Vona supports custom serializer transforms through `@SerializerTransform(...)`.
|
|
@@ -144,6 +144,8 @@ That means service access should be understood together with:
|
|
|
144
144
|
|
|
145
145
|
For deciding whether a backend base class should stay a helper, move into service-scene, or remain part of the global bean shorthand surface, also see [Class Placement Rule](/ai/class-placement-rule).
|
|
146
146
|
|
|
147
|
+
For reusable module-local helper functions, prefer the module `src/lib` directory. If the logic needs container-managed runtime behavior rather than plain helper placement, re-evaluate whether it belongs in service-scene instead. For the fuller rule, also see [Backend Foundation](/backend/foundation).
|
|
148
|
+
|
|
147
149
|
Read this guide together with:
|
|
148
150
|
|
|
149
151
|
- [Backend Foundation](/backend/foundation)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Status Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how Status works in Vona within the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
## Why Status matters
|
|
6
|
+
|
|
7
|
+
Some backend modules need a very small amount of durable module-local state without introducing a full business resource.
|
|
8
|
+
|
|
9
|
+
Typical examples include:
|
|
10
|
+
|
|
11
|
+
- feature toggles owned by one module
|
|
12
|
+
- small structured status payloads
|
|
13
|
+
- module-local runtime flags that should survive process restart
|
|
14
|
+
- lightweight persisted settings that do not justify a dedicated CRUD surface
|
|
15
|
+
|
|
16
|
+
Vona provides Status for exactly that shape.
|
|
17
|
+
|
|
18
|
+
A practical mental model is:
|
|
19
|
+
|
|
20
|
+
- Status is a persisted key/value store
|
|
21
|
+
- keys are scoped by the owning module
|
|
22
|
+
- values are stored as JSON
|
|
23
|
+
- the main public API is a typed `get` / `set` surface
|
|
24
|
+
|
|
25
|
+
## Create `meta.status`
|
|
26
|
+
|
|
27
|
+
Create a status bean in your module with the shared Vona CLI entrypoint:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run vona :create:bean meta status -- --module=demo-student
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This follows the same `:create:bean` workflow used by other backend bean scenes and metadata beans.
|
|
34
|
+
|
|
35
|
+
The generated shape is representative of:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { Meta } from 'vona-module-a-meta';
|
|
39
|
+
import { BeanStatusBase } from 'vona-module-a-status';
|
|
40
|
+
|
|
41
|
+
export interface IStatusRecord {}
|
|
42
|
+
|
|
43
|
+
@Meta()
|
|
44
|
+
export class MetaStatus extends BeanStatusBase<IStatusRecord> {}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The important point is that your module defines its own typed status record while reusing the shared persistence and locking behavior from `BeanStatusBase`.
|
|
48
|
+
|
|
49
|
+
## Define a typed status record
|
|
50
|
+
|
|
51
|
+
Status is most useful when the record shape is explicit.
|
|
52
|
+
|
|
53
|
+
Representative pattern:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
interface IStatusUser {
|
|
57
|
+
name: string;
|
|
58
|
+
age: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IStatusRecord {
|
|
62
|
+
enable: boolean;
|
|
63
|
+
user: IStatusUser;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Meta()
|
|
67
|
+
export class MetaStatus extends BeanStatusBase<IStatusRecord> {}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This gives a practical typed contract:
|
|
71
|
+
|
|
72
|
+
- `get('enable')` returns `boolean | undefined`
|
|
73
|
+
- `set('enable', true)` requires a boolean
|
|
74
|
+
- `get('user')` returns the typed user object or `undefined`
|
|
75
|
+
|
|
76
|
+
So Status is flexible at storage time because the value is JSON, but strongly typed at authoring time because the bean is generic.
|
|
77
|
+
|
|
78
|
+
## Use `scope.status`
|
|
79
|
+
|
|
80
|
+
After defining `meta.status` in a module, the generated scope exposes it as `scope.status` inside that module.
|
|
81
|
+
|
|
82
|
+
Representative usage:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
let value = await this.scope.status.get('enable');
|
|
86
|
+
await this.scope.status.set('enable', true);
|
|
87
|
+
value = await this.scope.status.get('enable');
|
|
88
|
+
|
|
89
|
+
let user = await this.scope.status.get('user');
|
|
90
|
+
await this.scope.status.set('user', { name: 'zhennann', age: 18 });
|
|
91
|
+
user = await this.scope.status.get('user');
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A practical behavior summary is:
|
|
95
|
+
|
|
96
|
+
- `get(...)` returns `undefined` when the key has not been written yet
|
|
97
|
+
- `set(...)` creates the key on first write
|
|
98
|
+
- later `set(...)` updates the existing value
|
|
99
|
+
|
|
100
|
+
## What is actually stored
|
|
101
|
+
|
|
102
|
+
The current source stores Status data in table `aStatus`.
|
|
103
|
+
|
|
104
|
+
The logical identity of a record is:
|
|
105
|
+
|
|
106
|
+
- `module`
|
|
107
|
+
- `name`
|
|
108
|
+
|
|
109
|
+
The stored payload is:
|
|
110
|
+
|
|
111
|
+
- `value` as JSON
|
|
112
|
+
|
|
113
|
+
So a practical storage reading is:
|
|
114
|
+
|
|
115
|
+
| Field | Meaning |
|
|
116
|
+
| -------- | --------------------------------------- |
|
|
117
|
+
| `module` | the owning backend module |
|
|
118
|
+
| `name` | the status key inside that module |
|
|
119
|
+
| `value` | the persisted JSON value for that key |
|
|
120
|
+
|
|
121
|
+
The current migration creates the table with the framework basic fields plus:
|
|
122
|
+
|
|
123
|
+
- `module`
|
|
124
|
+
- `name`
|
|
125
|
+
- `value`
|
|
126
|
+
|
|
127
|
+
This means Status is designed as a small module-scoped persistence surface rather than a general-purpose relational model.
|
|
128
|
+
|
|
129
|
+
## Module-local scoping
|
|
130
|
+
|
|
131
|
+
One of the most important design points is that Status is scoped by the **consumer module**, not only by the key name.
|
|
132
|
+
|
|
133
|
+
That means two different modules can both use a key such as `enable` without colliding with each other.
|
|
134
|
+
|
|
135
|
+
A practical reading is:
|
|
136
|
+
|
|
137
|
+
- `demo-student + enable` is one status record
|
|
138
|
+
- `demo-course + enable` is a different status record
|
|
139
|
+
|
|
140
|
+
This makes Status a natural fit for module-local state.
|
|
141
|
+
|
|
142
|
+
## First-write concurrency behavior
|
|
143
|
+
|
|
144
|
+
The current implementation protects first-write creation with Redlock.
|
|
145
|
+
|
|
146
|
+
A practical flow is:
|
|
147
|
+
|
|
148
|
+
1. attempt to read the current `(module, name)` record
|
|
149
|
+
2. if the record exists, update it
|
|
150
|
+
3. if the record does not exist, enter a `lockIsolate(...)` critical section
|
|
151
|
+
4. re-check the record with a forced fresh read
|
|
152
|
+
5. insert only if it is still missing
|
|
153
|
+
|
|
154
|
+
This is important in distributed or multi-worker deployments because two workers might otherwise try to create the same logical key at the same time.
|
|
155
|
+
|
|
156
|
+
The design intention is:
|
|
157
|
+
|
|
158
|
+
- ordinary reads stay simple
|
|
159
|
+
- first-write races are serialized
|
|
160
|
+
- the locked re-check avoids stale read behavior during creation
|
|
161
|
+
|
|
162
|
+
For the broader locking model, also see [Redlock Guide](/backend/redlock-guide).
|
|
163
|
+
|
|
164
|
+
## Relationship to cache and ORM
|
|
165
|
+
|
|
166
|
+
Status is built on top of the Vona ORM model layer.
|
|
167
|
+
|
|
168
|
+
That matters because Status is not a separate storage engine. It participates in the same broader backend persistence model as other ORM-backed data.
|
|
169
|
+
|
|
170
|
+
A practical reading is:
|
|
171
|
+
|
|
172
|
+
- Status persistence is implemented through an ORM model
|
|
173
|
+
- reads can benefit from the framework data-access stack
|
|
174
|
+
- write behavior still follows the framework mutation path
|
|
175
|
+
|
|
176
|
+
For surrounding concepts, also see:
|
|
177
|
+
|
|
178
|
+
- [ORM Guide](/backend/orm-guide)
|
|
179
|
+
- [Cache Guide](/backend/cache-guide)
|
|
180
|
+
- [Migration and Changes](/backend/migration-and-changes)
|
|
181
|
+
|
|
182
|
+
## Relationship to migration
|
|
183
|
+
|
|
184
|
+
The built-in `a-status` module currently creates its own storage table through `meta.version` with `fileVersion: 1`.
|
|
185
|
+
|
|
186
|
+
For ordinary consumers of Status, the usual workflow is simple:
|
|
187
|
+
|
|
188
|
+
- depend on the shared Status module
|
|
189
|
+
- create your own `meta.status`
|
|
190
|
+
- define your typed record
|
|
191
|
+
- use `scope.status` from your module
|
|
192
|
+
|
|
193
|
+
You do **not** create a separate status table per consuming module.
|
|
194
|
+
|
|
195
|
+
If your own module later outgrows Status and needs a richer schema, that usually means moving to dedicated entity/model resources and managing your own `meta.version` changes directly.
|
|
196
|
+
|
|
197
|
+
## What Status is good for
|
|
198
|
+
|
|
199
|
+
Status is a strong fit when the data is:
|
|
200
|
+
|
|
201
|
+
- small in size
|
|
202
|
+
- keyed by a fixed or slowly changing set of names
|
|
203
|
+
- naturally owned by one module
|
|
204
|
+
- read or updated by key rather than queried as a collection
|
|
205
|
+
|
|
206
|
+
Typical good fits include:
|
|
207
|
+
|
|
208
|
+
- module enable flags
|
|
209
|
+
- one module-owned progress marker
|
|
210
|
+
- compact structured settings
|
|
211
|
+
- one-off durable state snapshots
|
|
212
|
+
|
|
213
|
+
## What Status is not for
|
|
214
|
+
|
|
215
|
+
Status is usually the wrong abstraction when you need:
|
|
216
|
+
|
|
217
|
+
- a large collection of rows
|
|
218
|
+
- rich filtering or search
|
|
219
|
+
- relational structure
|
|
220
|
+
- a public CRUD API
|
|
221
|
+
- list, delete, or pagination behavior
|
|
222
|
+
- field-level validation and indexing as a first-class domain concern
|
|
223
|
+
|
|
224
|
+
A practical boundary is:
|
|
225
|
+
|
|
226
|
+
- use Status for a small number of durable module-local keys
|
|
227
|
+
- use entity/model/controller resources when the data becomes a real business resource
|
|
228
|
+
|
|
229
|
+
## Current implementation notes
|
|
230
|
+
|
|
231
|
+
From the current source, some boundaries are worth knowing:
|
|
232
|
+
|
|
233
|
+
- the public base API is intentionally small: `get` and `set`
|
|
234
|
+
- there is no built-in delete or list convenience API
|
|
235
|
+
- values are stored as JSON
|
|
236
|
+
- first-write concurrency is protected by Redlock
|
|
237
|
+
- the current table creation is lightweight and does not define a separate Status-specific CRUD surface
|
|
238
|
+
|
|
239
|
+
These details make Status easy to use, but they also mean you should not stretch it into a substitute for a richer domain model.
|
|
240
|
+
|
|
241
|
+
## Recommended workflow
|
|
242
|
+
|
|
243
|
+
A practical Status workflow is:
|
|
244
|
+
|
|
245
|
+
1. create `meta.status` with the Vona CLI
|
|
246
|
+
2. define a small typed `IStatusRecord`
|
|
247
|
+
3. read and write through `this.scope.status`
|
|
248
|
+
4. keep payloads compact and module-owned
|
|
249
|
+
5. move to dedicated entity/model resources if the data stops looking like key/value state
|
|
250
|
+
|
|
251
|
+
## Related guides
|
|
252
|
+
|
|
253
|
+
Read this guide together with:
|
|
254
|
+
|
|
255
|
+
- [Backend CLI](/backend/cli)
|
|
256
|
+
- [Migration and Changes](/backend/migration-and-changes)
|
|
257
|
+
- [ORM Guide](/backend/orm-guide)
|
|
258
|
+
- [Cache Guide](/backend/cache-guide)
|
|
259
|
+
- [Redlock Guide](/backend/redlock-guide)
|
|
260
|
+
|
|
261
|
+
## Implementation checks for Status changes
|
|
262
|
+
|
|
263
|
+
When adding or revising module-local durable state, ask:
|
|
264
|
+
|
|
265
|
+
1. is this really a small module-owned key/value need?
|
|
266
|
+
2. should the state live in `meta.status` instead of a dedicated entity/model resource?
|
|
267
|
+
3. is the status record shape explicit and typed?
|
|
268
|
+
4. will the payload remain compact and stable over time?
|
|
269
|
+
5. do later requirements such as list, delete, filtering, or indexing mean the design should graduate to a richer persistence model?
|
|
270
|
+
|
|
271
|
+
That keeps Status aligned with its intended role in Vona.
|