cabloy 5.1.59 → 5.1.60
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/skills/cabloy-contract-loop/SKILL.md +16 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +26 -0
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +144 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +18 -0
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +267 -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/.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 +30 -0
- package/CLAUDE.md +2 -0
- package/README.md +15 -0
- package/cabloy-docs/.vitepress/config.mjs +43 -0
- package/cabloy-docs/ai/class-placement-rule.md +2 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +17 -2
- package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
- package/cabloy-docs/backend/cli.md +26 -1
- package/cabloy-docs/backend/foundation.md +28 -3
- package/cabloy-docs/backend/introduction.md +8 -0
- package/cabloy-docs/backend/service-guide.md +2 -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/bean-scene-authoring.md +372 -0
- package/cabloy-docs/frontend/behavior-guide.md +449 -0
- package/cabloy-docs/frontend/cli.md +12 -0
- package/cabloy-docs/frontend/introduction.md +5 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +10 -9
- 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/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/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/introduction.md +29 -0
- 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 +119 -0
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +144 -0
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +168 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +179 -0
- package/cabloy-docs/index.md +4 -3
- package/cabloy-docs/reference/bean-scene-boilerplates.md +73 -0
- package/cabloy-docs/reference/cli-reference.md +2 -0
- package/package.json +6 -2
- package/scripts/init.ts +18 -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 +133 -1088
- 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/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/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 +284 -1313
- 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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Tutorial 3: Frontend Metadata Sharing
|
|
2
|
+
|
|
3
|
+
<Badge type="info" text="Basic" />
|
|
4
|
+
|
|
5
|
+
In this tutorial, one prompt lets AI show the reverse direction of Cabloy’s fullstack contract loop: backend field metadata can reference frontend render resources.
|
|
6
|
+
|
|
7
|
+
You start with the simplest path first: reuse the existing built-in rendering resources for the `level` field.
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
By the end of this tutorial, you will understand:
|
|
12
|
+
|
|
13
|
+
- why Cabloy fullstack sharing is bidirectional
|
|
14
|
+
- how a backend field can reuse frontend rendering resources through metadata
|
|
15
|
+
- how one `level` field can participate in schema-driven form and table behavior without a custom component yet
|
|
16
|
+
|
|
17
|
+
## AI Prompt
|
|
18
|
+
|
|
19
|
+
Give AI a prompt like this:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Please add a level field to the Student resource. It should be a number field with these enum values:
|
|
23
|
+
|
|
24
|
+
- 1: beginner
|
|
25
|
+
- 2: intermediate
|
|
26
|
+
- 3: advanced
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Why this step matters
|
|
30
|
+
|
|
31
|
+
This is the right step because it teaches one very specific Cabloy distinction.
|
|
32
|
+
|
|
33
|
+
The goal is not to make backend and frontend share arbitrary component code. The goal is to keep the backend field contract in charge while letting that contract reference frontend render resources through metadata.
|
|
34
|
+
|
|
35
|
+
In other words, this step is about **frontend metadata sharing**: the backend field contract references frontend render resources through metadata.
|
|
36
|
+
|
|
37
|
+
## CLI commands to inspect/use
|
|
38
|
+
|
|
39
|
+
This tutorial is mainly a contract-refinement step, so the most important habit is inspection before editing.
|
|
40
|
+
|
|
41
|
+
Useful discovery commands from the repo root:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run vona :
|
|
45
|
+
npm run zova :
|
|
46
|
+
npm run zova :create
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You usually do **not** need to generate a custom bean in this tutorial.
|
|
50
|
+
|
|
51
|
+
Instead, inspect the current contract surfaces first:
|
|
52
|
+
|
|
53
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
54
|
+
- `vona/src/module/demo-student/src/dto/studentSelectReq.tsx`
|
|
55
|
+
|
|
56
|
+
Usage notes:
|
|
57
|
+
|
|
58
|
+
- keep the backend entity as the main source of truth for the `level` field
|
|
59
|
+
- prefer built-in frontend render resources first
|
|
60
|
+
- delay custom renderer authoring to the next tutorial
|
|
61
|
+
|
|
62
|
+
## Generated or affected files
|
|
63
|
+
|
|
64
|
+
The key backend contract anchor is:
|
|
65
|
+
|
|
66
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
67
|
+
|
|
68
|
+
By the end of this tutorial, the `level` field should follow a built-in path like this:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
@Api.field(
|
|
72
|
+
v.title($locale('Level')),
|
|
73
|
+
v.required(),
|
|
74
|
+
ZovaRender.order(3),
|
|
75
|
+
ZovaRender.field('basic-select:formFieldSelect', {
|
|
76
|
+
items: levelItems,
|
|
77
|
+
placeholder: $locale('LevelPlaceholder'),
|
|
78
|
+
}),
|
|
79
|
+
ZovaRender.cell('basic-select:select', { items: levelItems }),
|
|
80
|
+
levelSchema,
|
|
81
|
+
)
|
|
82
|
+
level: number;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A related DTO anchor is:
|
|
86
|
+
|
|
87
|
+
- `vona/src/module/demo-student/src/dto/studentSelectReq.tsx`
|
|
88
|
+
|
|
89
|
+
That DTO already shows how filter-side field metadata can also participate in schema-driven UI.
|
|
90
|
+
|
|
91
|
+
## What those files mean in the business thread
|
|
92
|
+
|
|
93
|
+
This tutorial teaches one core mental model:
|
|
94
|
+
|
|
95
|
+
- the backend still owns the business field contract
|
|
96
|
+
- the frontend still owns the render resources
|
|
97
|
+
- metadata is the bridge between them
|
|
98
|
+
|
|
99
|
+
Concretely:
|
|
100
|
+
|
|
101
|
+
- `entity/student.tsx` defines `level` as a real business field
|
|
102
|
+
- `levelItems` gives that field a stable set of business options
|
|
103
|
+
- `ZovaRender.field('basic-select:formFieldSelect', ...)` makes the form side schema-aware
|
|
104
|
+
- `ZovaRender.cell('basic-select:select', ...)` makes the table side schema-aware
|
|
105
|
+
- `dto/studentSelectReq.tsx` reminds you that filter-side metadata is also part of the same contract story
|
|
106
|
+
|
|
107
|
+
At this stage, you do not need a custom frontend component to understand the reverse-sharing model.
|
|
108
|
+
|
|
109
|
+
## Verification
|
|
110
|
+
|
|
111
|
+
1. make sure the local dev workflow is running:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run dev
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
2. open `http://localhost:7102/admin/`
|
|
118
|
+
3. enter the **Student** list page from the **Student** menu
|
|
119
|
+
4. verify that the `level` field appears with select-style behavior in the relevant schema-driven surfaces
|
|
120
|
+
5. inspect `vona/src/module/demo-student/src/entity/student.tsx` and confirm that the backend field contract now points to built-in frontend render resources instead of page-local hard-coded UI logic
|
|
121
|
+
|
|
122
|
+
## Read more
|
|
123
|
+
|
|
124
|
+
- [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend)
|
|
125
|
+
- [API Schema Guide](/frontend/api-schema-guide)
|
|
126
|
+
- [Server Data](/frontend/server-data)
|
|
127
|
+
- [Frontend CLI](/frontend/cli)
|
|
128
|
+
|
|
129
|
+
## Next step
|
|
130
|
+
|
|
131
|
+
Continue to [Tutorial 4: Custom Form/Table Renderers for Level](/fullstack/tutorial-4-custom-level-renderers).
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Tutorial 4: Custom Form/Table Renderers for Level
|
|
2
|
+
|
|
3
|
+
<Badge type="info" text="Basic" />
|
|
4
|
+
|
|
5
|
+
In this tutorial, one prompt lets AI upgrade the `level` field from built-in render resources to custom frontend renderers owned by the `demo-student` module.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
By the end of this tutorial, you will understand:
|
|
10
|
+
|
|
11
|
+
- when built-in render metadata is enough and when a custom renderer is worth adding
|
|
12
|
+
- how a frontend table cell bean and a frontend form-field component can both serve the same backend field
|
|
13
|
+
- how backend metadata points to module-owned frontend render resources
|
|
14
|
+
|
|
15
|
+
## AI Prompt
|
|
16
|
+
|
|
17
|
+
Give AI a prompt like this:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
Please make the level UI more business-specific in the Student list page and form.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Why this step matters
|
|
24
|
+
|
|
25
|
+
This is the right follow-up step because built-in render resources are a good starting point, but some business fields eventually need module-specific behavior.
|
|
26
|
+
|
|
27
|
+
The `level` field is a good teaching example because this step deepens the UI in two concrete ways:
|
|
28
|
+
|
|
29
|
+
- a custom table cell that renders a more business-specific badge style
|
|
30
|
+
- a custom form field that adds helper text, readonly behavior, or module-specific select styling
|
|
31
|
+
|
|
32
|
+
This is where Cabloy’s contract model becomes more practical: the backend field still owns the business contract, while the frontend module progressively deepens the UI behavior behind that contract.
|
|
33
|
+
|
|
34
|
+
## CLI commands to inspect/use
|
|
35
|
+
|
|
36
|
+
Inspect the Zova create surface first:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run zova :create
|
|
40
|
+
npm run zova :create:bean --help
|
|
41
|
+
npm run zova :create:component --help
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Representative generation commands for this tutorial:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm run zova :create:bean tableCell level -- --module=demo-student
|
|
48
|
+
npm run zova :create:component formFieldLevel -- --module=demo-student
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Usage notes:
|
|
52
|
+
|
|
53
|
+
- use `:create:bean` when you need a table-cell render resource under the bean scene
|
|
54
|
+
- use `:create:component` when you need a custom frontend component/controller surface
|
|
55
|
+
- generation gives you the structural starting point, but this tutorial still expects manual refinement so the result matches the `demo-student` teaching implementation
|
|
56
|
+
- after frontend resources exist, return to the backend entity and point `ZovaRender.field(...)` and `ZovaRender.cell(...)` at the custom module resources
|
|
57
|
+
|
|
58
|
+
## Generated or affected files
|
|
59
|
+
|
|
60
|
+
By the end of this tutorial, your module should provide these teaching anchors:
|
|
61
|
+
|
|
62
|
+
- custom table cell bean:
|
|
63
|
+
- `zova/src/module/demo-student/src/bean/tableCell.level.tsx`
|
|
64
|
+
- custom form-field controller:
|
|
65
|
+
- `zova/src/module/demo-student/src/component/formFieldLevel/controller.tsx`
|
|
66
|
+
- form-field metadata wrapper:
|
|
67
|
+
- `zova/src/module/demo-student/src/.metadata/component/formFieldLevel.ts`
|
|
68
|
+
- backend field contract to update:
|
|
69
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
70
|
+
|
|
71
|
+
Representative custom metadata targets are:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
ZovaRender.field('demo-student:formFieldLevel', {
|
|
75
|
+
items: levelItems,
|
|
76
|
+
helper: $locale('LevelPlaceholder'),
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
and:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
ZovaRender.cell('demo-student:level', { items: levelItems })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What those files mean in the business thread
|
|
87
|
+
|
|
88
|
+
This tutorial makes the split of responsibilities explicit:
|
|
89
|
+
|
|
90
|
+
- `tableCell.level.tsx` owns the custom table-cell rendering behavior for `level`
|
|
91
|
+
- `component/formFieldLevel/controller.tsx` owns the custom form-field behavior for `level`
|
|
92
|
+
- `.metadata/component/formFieldLevel.ts` exposes that component through the module registration surface
|
|
93
|
+
- `entity/student.tsx` remains the backend-owned business contract that chooses which frontend resources should render the field
|
|
94
|
+
|
|
95
|
+
That means the backend still defines the business field, validation, and metadata entry point, while the frontend module owns the implementation details of the richer UI behavior.
|
|
96
|
+
|
|
97
|
+
## Verification
|
|
98
|
+
|
|
99
|
+
1. make sure the local dev workflow is running:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm run dev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
2. open `http://localhost:7102/admin/`
|
|
106
|
+
3. enter the **Student** list page and verify that the `level` column now uses the custom table-cell presentation
|
|
107
|
+
4. open a Student create, update, or view form and verify that the `level` field now uses the custom form-field behavior
|
|
108
|
+
5. inspect `vona/src/module/demo-student/src/entity/student.tsx` and confirm that the backend metadata now points to `demo-student:formFieldLevel` and `demo-student:level`
|
|
109
|
+
|
|
110
|
+
## Read more
|
|
111
|
+
|
|
112
|
+
- [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend)
|
|
113
|
+
- [Frontend CLI](/frontend/cli)
|
|
114
|
+
- [Component Guide](/frontend/component-guide)
|
|
115
|
+
- [Bean Scene Boilerplate Variants](/reference/bean-scene-boilerplates)
|
|
116
|
+
|
|
117
|
+
## Next step
|
|
118
|
+
|
|
119
|
+
Continue to [Tutorial 5: Backend Contract Sharing](/fullstack/tutorial-5-backend-contract-sharing).
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Tutorial 5: Backend Contract Sharing
|
|
2
|
+
|
|
3
|
+
<Badge type="info" text="Basic" />
|
|
4
|
+
|
|
5
|
+
In this tutorial, one prompt lets AI show the forward direction of Cabloy’s fullstack contract loop: backend API contracts that generate and refresh the frontend consumption surface.
|
|
6
|
+
|
|
7
|
+
The teaching thread in this page is the pair of Student actions:
|
|
8
|
+
|
|
9
|
+
- `summary/:id`
|
|
10
|
+
- `deleteForce/:id`
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
|
|
14
|
+
By the end of this tutorial, you will understand:
|
|
15
|
+
|
|
16
|
+
- how a backend controller and DTO change becomes frontend generated API output
|
|
17
|
+
- how backend row-action metadata can drive frontend table actions
|
|
18
|
+
- how generated API, frontend model helpers, and row-action beans fit into one contract-sharing workflow
|
|
19
|
+
|
|
20
|
+
## AI Prompt
|
|
21
|
+
|
|
22
|
+
Give AI a prompt like this:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
Please add two custom actions to the Student list:
|
|
26
|
+
|
|
27
|
+
- Summary: return or display a student summary for the selected row
|
|
28
|
+
- Force Delete: permanently delete the selected student through a dedicated row action
|
|
29
|
+
|
|
30
|
+
Make both actions work end to end, from the backend contract to the Student list page.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Why this step matters
|
|
34
|
+
|
|
35
|
+
This is the right step because it reduces duplicated type work across backend and frontend.
|
|
36
|
+
|
|
37
|
+
Instead of manually synchronizing request and response shapes in two places, this workflow keeps the backend as the source of truth and regenerates the frontend artifacts from that contract.
|
|
38
|
+
|
|
39
|
+
This step also shows that contract sharing is not only about API methods. It also affects how backend row-action metadata becomes frontend table behavior.
|
|
40
|
+
|
|
41
|
+
## CLI commands to inspect/use
|
|
42
|
+
|
|
43
|
+
Inspect the relevant command surfaces first:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run zova :openapi
|
|
47
|
+
npm run zova :openapi:config demo-student
|
|
48
|
+
npm run zova :openapi:generate demo-student
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If your workflow also needs refreshed rest-contract output for Cabloy Basic, the related build commands are:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd zova && npm run build:rest:cabloyBasicAdmin
|
|
55
|
+
cd zova && npm run build:rest:cabloyBasicWeb
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Usage notes:
|
|
59
|
+
|
|
60
|
+
- use the backend controller and DTOs as the starting point
|
|
61
|
+
- inspect the module OpenAPI config before generation
|
|
62
|
+
- prefer regeneration over hand-written duplicate API layers when the module already owns an OpenAPI contract surface
|
|
63
|
+
|
|
64
|
+
## Generated or affected files
|
|
65
|
+
|
|
66
|
+
The backend contract anchors are:
|
|
67
|
+
|
|
68
|
+
- `vona/src/module/demo-student/src/controller/student.ts`
|
|
69
|
+
- `vona/src/module/demo-student/src/dto/studentSummary.tsx`
|
|
70
|
+
- `vona/src/module/demo-student/src/dto/studentSelectResItem.tsx`
|
|
71
|
+
|
|
72
|
+
By the end of this tutorial, your `demo-student` controller should expose:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
@Web.get('summary/:id')
|
|
76
|
+
async summary(...) { ... }
|
|
77
|
+
|
|
78
|
+
@Web.delete('deleteForce/:id')
|
|
79
|
+
async deleteForce(...) { ... }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The frontend contract and consumption anchors are:
|
|
83
|
+
|
|
84
|
+
- OpenAPI config:
|
|
85
|
+
- `zova/src/module/demo-student/cli/openapi.config.ts`
|
|
86
|
+
- generated frontend API:
|
|
87
|
+
- `zova/src/module/demo-student/src/api/demoStudent.ts`
|
|
88
|
+
- frontend model wrapper:
|
|
89
|
+
- `zova/src/module/demo-student/src/model/student.ts`
|
|
90
|
+
- custom row-action cells:
|
|
91
|
+
- `zova/src/module/demo-student/src/bean/tableCell.actionSummary.tsx`
|
|
92
|
+
- `zova/src/module/demo-student/src/bean/tableCell.actionDeleteForce.tsx`
|
|
93
|
+
|
|
94
|
+
After regeneration, `src/api/demoStudent.ts` should contain generated methods such as:
|
|
95
|
+
|
|
96
|
+
- `summary(...)`
|
|
97
|
+
- `deleteForce(...)`
|
|
98
|
+
|
|
99
|
+
## What those files mean in the business thread
|
|
100
|
+
|
|
101
|
+
This tutorial is easiest to understand as one contract chain:
|
|
102
|
+
|
|
103
|
+
1. `controller/student.ts` defines the backend action endpoints
|
|
104
|
+
2. `dto/studentSummary.tsx` defines the response contract for the summary action
|
|
105
|
+
3. `dto/studentSelectResItem.tsx` defines the row-action metadata that exposes those actions in the Student list page
|
|
106
|
+
4. `cli/openapi.config.ts` tells the frontend module which backend operations it owns
|
|
107
|
+
5. `src/api/demoStudent.ts` is the generated typed API surface created from that backend contract
|
|
108
|
+
6. `src/model/student.ts` wraps the generated API in frontend business-facing helper methods
|
|
109
|
+
7. `tableCell.actionSummary.tsx` and `tableCell.actionDeleteForce.tsx` turn those model methods into visible row actions
|
|
110
|
+
|
|
111
|
+
That is the practical contract-sharing loop: backend controller and DTO truth flows into generated frontend API output, then into frontend model helpers, and finally into visible table actions.
|
|
112
|
+
|
|
113
|
+
## Verification
|
|
114
|
+
|
|
115
|
+
1. make sure the local dev workflow is running:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm run dev
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
2. open `http://localhost:7102/admin/`
|
|
122
|
+
3. enter the **Student** list page
|
|
123
|
+
4. trigger the **Summary** row action and verify that it uses the regenerated frontend API and returns the expected Student summary data
|
|
124
|
+
5. trigger the **Force Delete** row action and verify that it reaches the custom backend deleteForce contract path
|
|
125
|
+
6. inspect the source chain and confirm that each layer is present:
|
|
126
|
+
- `controller/student.ts`
|
|
127
|
+
- `dto/studentSummary.tsx`
|
|
128
|
+
- `dto/studentSelectResItem.tsx`
|
|
129
|
+
- `src/api/demoStudent.ts`
|
|
130
|
+
- `src/model/student.ts`
|
|
131
|
+
- `tableCell.actionSummary.tsx`
|
|
132
|
+
- `tableCell.actionDeleteForce.tsx`
|
|
133
|
+
|
|
134
|
+
## Read more
|
|
135
|
+
|
|
136
|
+
- [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
|
|
137
|
+
- [OpenAPI Guide](/backend/openapi-guide)
|
|
138
|
+
- [OpenAPI SDK Guide](/frontend/openapi-sdk-guide)
|
|
139
|
+
- [SDK Guide](/frontend/sdk-guide)
|
|
140
|
+
- [Server Data](/frontend/server-data)
|
|
141
|
+
|
|
142
|
+
## Next step
|
|
143
|
+
|
|
144
|
+
Continue to [Tutorial 6: One Contract Surface, Four Uses](/fullstack/tutorial-6-one-contract-four-uses).
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Tutorial 6: One Contract Surface, Four Uses
|
|
2
|
+
|
|
3
|
+
<Badge type="info" text="Basic" />
|
|
4
|
+
|
|
5
|
+
In this tutorial, one prompt lets AI close the series by showing Cabloy’s core fullstack idea: one field-oriented contract surface can drive several behaviors across backend and frontend.
|
|
6
|
+
|
|
7
|
+
This time the main teaching field is `mobile`, while `level` stays as the supporting example for table and form rendering.
|
|
8
|
+
|
|
9
|
+
## Goal
|
|
10
|
+
|
|
11
|
+
By the end of this tutorial, you will understand how one business field thread can participate in:
|
|
12
|
+
|
|
13
|
+
1. validation
|
|
14
|
+
2. OpenAPI generation
|
|
15
|
+
3. table and form rendering
|
|
16
|
+
4. serialization or desensitization
|
|
17
|
+
|
|
18
|
+
## AI Prompt
|
|
19
|
+
|
|
20
|
+
Give AI a prompt like this:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Please add a mobile field to the Student resource. It should be required, be at least 11 characters long, and show the middle 4 digits as asterisks in returned data.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Why this step matters
|
|
27
|
+
|
|
28
|
+
This is the right capstone step because many frameworks force the same field knowledge to be repeated in many places:
|
|
29
|
+
|
|
30
|
+
- validation rules
|
|
31
|
+
- backend DTOs
|
|
32
|
+
- API documentation
|
|
33
|
+
- frontend forms
|
|
34
|
+
- frontend tables
|
|
35
|
+
- response masking logic
|
|
36
|
+
|
|
37
|
+
Cabloy tries to reduce that duplication through a field-oriented contract and metadata model, and this tutorial lets you inspect that reduction through one concrete field story.
|
|
38
|
+
|
|
39
|
+
## CLI commands to inspect/use
|
|
40
|
+
|
|
41
|
+
This tutorial is mainly a source-inspection and verification capstone.
|
|
42
|
+
|
|
43
|
+
Useful commands include:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run zova :openapi:generate demo-student
|
|
47
|
+
npm run dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Usage notes:
|
|
51
|
+
|
|
52
|
+
- regenerate the frontend contract if your backend field changes affect the generated output
|
|
53
|
+
- treat the backend entity and DTO surfaces as the first place to inspect
|
|
54
|
+
- use the admin UI to confirm both the visible rendering result and the exposed mobile behavior
|
|
55
|
+
|
|
56
|
+
## Generated or affected files
|
|
57
|
+
|
|
58
|
+
The key backend field contract anchor is:
|
|
59
|
+
|
|
60
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
61
|
+
|
|
62
|
+
By the end of this tutorial, the `mobile` field should show the main capstone pattern:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
@Api.field(
|
|
66
|
+
v.title($locale('Mobile')),
|
|
67
|
+
v.required(),
|
|
68
|
+
v.min(11),
|
|
69
|
+
studentMobileSerializer(),
|
|
70
|
+
ZovaRender.order(4),
|
|
71
|
+
)
|
|
72
|
+
mobile: string;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The serializer helper lives in:
|
|
76
|
+
|
|
77
|
+
- `vona/src/module/demo-student/src/lib/studentMobile.ts`
|
|
78
|
+
|
|
79
|
+
The summary DTO also participates in the same thread:
|
|
80
|
+
|
|
81
|
+
- `vona/src/module/demo-student/src/dto/studentSummary.tsx`
|
|
82
|
+
|
|
83
|
+
The supporting render example remains:
|
|
84
|
+
|
|
85
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
86
|
+
- `ZovaRender.field(...)` for `level`
|
|
87
|
+
- `ZovaRender.cell(...)` for `level`
|
|
88
|
+
|
|
89
|
+
## What those files mean in the business thread
|
|
90
|
+
|
|
91
|
+
This tutorial works best when you read `mobile` as one continuous contract thread.
|
|
92
|
+
|
|
93
|
+
That is why `mobile` is the main capstone field, while `level` remains the supporting rendering field.
|
|
94
|
+
|
|
95
|
+
### Use 1: Validation
|
|
96
|
+
|
|
97
|
+
In `entity/student.tsx`, `mobile` already carries validation decisions such as:
|
|
98
|
+
|
|
99
|
+
- `v.required()`
|
|
100
|
+
- `v.min(11)`
|
|
101
|
+
|
|
102
|
+
That means the field definition is part of the request-contract story, not only a persistence concern.
|
|
103
|
+
|
|
104
|
+
### Use 2: OpenAPI generation
|
|
105
|
+
|
|
106
|
+
The same field contract participates in DTO and controller flows, which then feed machine-readable API output.
|
|
107
|
+
|
|
108
|
+
That is why backend field and DTO changes can later affect what frontend regeneration sees.
|
|
109
|
+
|
|
110
|
+
### Use 3: Table and form rendering
|
|
111
|
+
|
|
112
|
+
The main rendering example in this series remains `level`, not `mobile`.
|
|
113
|
+
|
|
114
|
+
That is intentional.
|
|
115
|
+
|
|
116
|
+
`level` shows how the same field-oriented contract can carry:
|
|
117
|
+
|
|
118
|
+
- `ZovaRender.field(...)`
|
|
119
|
+
- `ZovaRender.cell(...)`
|
|
120
|
+
- built-in or custom frontend render resources
|
|
121
|
+
|
|
122
|
+
This keeps the “four uses” explanation complete without forcing `mobile` to act like the best rendering example.
|
|
123
|
+
|
|
124
|
+
### Use 4: Serialization and desensitization
|
|
125
|
+
|
|
126
|
+
The most practical `mobile` lesson is response exposure policy.
|
|
127
|
+
|
|
128
|
+
In `studentMobile.ts`, the helper:
|
|
129
|
+
|
|
130
|
+
- defines the masking pattern
|
|
131
|
+
- returns `v.serializerReplace(...)`
|
|
132
|
+
|
|
133
|
+
That keeps the masking rule close to the field contract ecosystem instead of scattering it into ad hoc controller or service post-processing.
|
|
134
|
+
|
|
135
|
+
## Verification
|
|
136
|
+
|
|
137
|
+
1. make sure the local dev workflow is running:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run dev
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
2. open `http://localhost:7102/admin/`
|
|
144
|
+
3. enter the relevant **Student** page
|
|
145
|
+
4. verify that `level` still shows the expected render-driven behavior
|
|
146
|
+
5. verify that `mobile` follows the validation and serialization policy you defined
|
|
147
|
+
6. inspect these anchors and confirm that the four-use story is concrete rather than abstract:
|
|
148
|
+
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
149
|
+
- `vona/src/module/demo-student/src/lib/studentMobile.ts`
|
|
150
|
+
- `vona/src/module/demo-student/src/dto/studentSummary.tsx`
|
|
151
|
+
|
|
152
|
+
## Read more
|
|
153
|
+
|
|
154
|
+
- [Validation Guide](/backend/validation-guide)
|
|
155
|
+
- [OpenAPI Guide](/backend/openapi-guide)
|
|
156
|
+
- [API Schema Guide](/frontend/api-schema-guide)
|
|
157
|
+
- [Server Data](/frontend/server-data)
|
|
158
|
+
- [Serialization Guide](/backend/serialization-guide)
|
|
159
|
+
- [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
|
|
160
|
+
- [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend)
|
|
161
|
+
|
|
162
|
+
## Next step
|
|
163
|
+
|
|
164
|
+
After finishing this series, choose the next path based on your current task:
|
|
165
|
+
|
|
166
|
+
- if you want deeper backend contract detail, continue with [Entity Guide](/backend/entity-guide) and [DTO Guide](/backend/dto-guide)
|
|
167
|
+
- if you want deeper frontend contract consumption, continue with [OpenAPI SDK Guide](/frontend/openapi-sdk-guide) and [API Schema Guide](/frontend/api-schema-guide)
|
|
168
|
+
- if you want more CLI-oriented workflow depth, continue with [CLI Reference](/reference/cli-reference)
|