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.
- package/.claude/hooks/contract-loop-gate.ts +296 -0
- package/.claude/settings.json +16 -0
- package/.claude/skills/cabloy-backend-scaffold/SKILL.md +2 -0
- package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -0
- package/.claude/skills/cabloy-contract-loop/SKILL.md +89 -16
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +102 -14
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +4 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +32 -14
- package/.claude/skills/cabloy-domain-planning/SKILL.md +212 -0
- package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +13 -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 +7 -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/CHANGELOG.md +64 -0
- package/CLAUDE.md +11 -0
- package/cabloy-docs/.vitepress/config.mjs +197 -5
- package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
- package/cabloy-docs/ai/docs-skills-rules-mapping.md +22 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +12 -7
- 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 +12 -0
- package/cabloy-docs/backend/backend-contract-emission-output-inspection.md +189 -0
- package/cabloy-docs/backend/backend-contract-emission-source-reading-map.md +160 -0
- package/cabloy-docs/backend/backend-contract-emission-specimen.md +170 -0
- package/cabloy-docs/backend/backend-resource-module-contract-chain.md +323 -0
- package/cabloy-docs/backend/backend-source-reading-debug-checklist.md +173 -0
- package/cabloy-docs/backend/backend-source-reading-roadmap.md +129 -0
- package/cabloy-docs/backend/backend-source-reading-verify-playbook.md +166 -0
- package/cabloy-docs/backend/bean-scene-authoring.md +4 -4
- package/cabloy-docs/backend/broadcast-guide.md +3 -3
- package/cabloy-docs/backend/cli.md +20 -11
- package/cabloy-docs/backend/config-guide.md +4 -4
- package/cabloy-docs/backend/controller-aop-guide.md +10 -10
- package/cabloy-docs/backend/controller-guide.md +12 -2
- package/cabloy-docs/backend/crud-workflow.md +7 -3
- package/cabloy-docs/backend/dto-guide.md +18 -2
- package/cabloy-docs/backend/dto-infer-generation.md +201 -25
- package/cabloy-docs/backend/election-guide.md +2 -2
- package/cabloy-docs/backend/entity-guide.md +30 -3
- package/cabloy-docs/backend/error-guide.md +3 -3
- package/cabloy-docs/backend/event-guide.md +4 -4
- package/cabloy-docs/backend/external-aop-guide.md +2 -2
- package/cabloy-docs/backend/field-indexes.md +9 -3
- package/cabloy-docs/backend/foundation.md +8 -8
- package/cabloy-docs/backend/i18n-guide.md +6 -6
- package/cabloy-docs/backend/internal-aop-guide.md +2 -2
- package/cabloy-docs/backend/introduction.md +15 -0
- package/cabloy-docs/backend/migration-and-changes.md +3 -3
- package/cabloy-docs/backend/model-guide.md +16 -6
- package/cabloy-docs/backend/openapi-guide.md +3 -0
- package/cabloy-docs/backend/queue-guide.md +3 -3
- package/cabloy-docs/backend/redlock-guide.md +2 -2
- package/cabloy-docs/backend/schedule-guide.md +2 -2
- package/cabloy-docs/backend/scripts.md +8 -0
- package/cabloy-docs/backend/serialization-guide.md +12 -2
- package/cabloy-docs/backend/service-guide.md +18 -9
- package/cabloy-docs/backend/startup-guide.md +5 -5
- package/cabloy-docs/backend/status-guide.md +271 -0
- package/cabloy-docs/backend/unit-testing.md +3 -3
- package/cabloy-docs/backend/vona-source-reading-map.md +157 -0
- package/cabloy-docs/backend/websocket-protocol-guide.md +5 -5
- package/cabloy-docs/backend/websocket-usage-guide.md +15 -8
- package/cabloy-docs/frontend/a-model-under-the-hood.md +281 -0
- package/cabloy-docs/frontend/a-openapi-under-the-hood.md +248 -0
- package/cabloy-docs/frontend/a-router-guide.md +307 -0
- package/cabloy-docs/frontend/api-guide.md +6 -4
- package/cabloy-docs/frontend/api-schema-guide.md +1 -0
- package/cabloy-docs/frontend/app-startup-guide.md +7 -4
- package/cabloy-docs/frontend/bean-scene-authoring.md +3 -1
- package/cabloy-docs/frontend/behavior-guide.md +16 -16
- package/cabloy-docs/frontend/cli.md +14 -2
- package/cabloy-docs/frontend/command-scene-authoring.md +504 -0
- package/cabloy-docs/frontend/component-guide.md +5 -5
- package/cabloy-docs/frontend/component-props-guide.md +1 -1
- package/cabloy-docs/frontend/component-v-model-guide.md +2 -2
- package/cabloy-docs/frontend/design-principles.md +6 -0
- package/cabloy-docs/frontend/fetch-interceptor-guide.md +440 -0
- package/cabloy-docs/frontend/filter-query-select-data-flow-guide.md +260 -0
- package/cabloy-docs/frontend/form-guide.md +786 -0
- package/cabloy-docs/frontend/form-scene-to-page-meta-guide.md +303 -0
- package/cabloy-docs/frontend/foundation.md +33 -0
- package/cabloy-docs/frontend/frontend-source-reading-roadmap.md +249 -0
- package/cabloy-docs/frontend/generated-contract-consumption-debug-checklist.md +190 -0
- package/cabloy-docs/frontend/generated-contract-consumption-entry-branch.md +205 -0
- package/cabloy-docs/frontend/generated-contract-consumption-list-branch.md +157 -0
- package/cabloy-docs/frontend/generated-contract-consumption-specimen.md +203 -0
- package/cabloy-docs/frontend/generated-contract-consumption-verify-playbook.md +189 -0
- package/cabloy-docs/frontend/generic-component-guide.md +1 -1
- package/cabloy-docs/frontend/introduction.md +38 -5
- package/cabloy-docs/frontend/ioc-and-beans.md +6 -0
- package/cabloy-docs/frontend/mock-guide.md +1 -0
- package/cabloy-docs/frontend/model-architecture.md +288 -39
- package/cabloy-docs/frontend/model-resource-best-practices.md +379 -0
- package/cabloy-docs/frontend/model-resource-cookbook.md +508 -0
- package/cabloy-docs/frontend/model-resource-internals-deep-dive.md +238 -0
- package/cabloy-docs/frontend/model-resource-owner-pattern.md +402 -0
- package/cabloy-docs/frontend/model-resource-usage-guide.md +334 -0
- package/cabloy-docs/frontend/model-state-guide.md +371 -15
- package/cabloy-docs/frontend/module-scope.md +8 -8
- package/cabloy-docs/frontend/modules-and-suites.md +2 -1
- package/cabloy-docs/frontend/navigation-guards-guide.md +7 -0
- package/cabloy-docs/frontend/openapi-sdk-guide.md +17 -6
- package/cabloy-docs/frontend/page-guide.md +15 -9
- package/cabloy-docs/frontend/page-meta-guide.md +466 -0
- package/cabloy-docs/frontend/page-params-guide.md +3 -3
- package/cabloy-docs/frontend/page-query-guide.md +2 -2
- package/cabloy-docs/frontend/page-route-guide.md +6 -0
- package/cabloy-docs/frontend/permission-formscene-action-visibility-guide.md +263 -0
- package/cabloy-docs/frontend/quickstart.md +18 -2
- package/cabloy-docs/frontend/reading-zova-for-vue-developers.md +266 -0
- package/cabloy-docs/frontend/resource-entry-page-deep-dive.md +271 -0
- package/cabloy-docs/frontend/resource-list-page-deep-dive.md +279 -0
- package/cabloy-docs/frontend/rest-resource-source-reading-map.md +522 -0
- package/cabloy-docs/frontend/rest-resource-under-the-hood.md +622 -0
- package/cabloy-docs/frontend/root-behaviors-guide.md +282 -0
- package/cabloy-docs/frontend/route-alias-guide.md +6 -0
- package/cabloy-docs/frontend/router-stack-guide.md +229 -0
- package/cabloy-docs/frontend/router-tabs-introduction.md +26 -3
- package/cabloy-docs/frontend/router-tabs-layout-integration.md +367 -0
- package/cabloy-docs/frontend/router-tabs-mechanism.md +6 -0
- package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +7 -0
- package/cabloy-docs/frontend/router-tabs-vs-stack.md +167 -0
- package/cabloy-docs/frontend/router-view-hosts-guide.md +450 -0
- package/cabloy-docs/frontend/server-data.md +4 -1
- package/cabloy-docs/frontend/system-startup-guide.md +2 -2
- package/cabloy-docs/frontend/table-action-visibility-permission-flow-guide.md +263 -0
- package/cabloy-docs/frontend/table-cell-cookbook.md +568 -0
- package/cabloy-docs/frontend/table-guide.md +373 -0
- package/cabloy-docs/frontend/table-resource-crud-cookbook.md +496 -0
- package/cabloy-docs/frontend/zova-app-guide.md +251 -0
- package/cabloy-docs/frontend/zova-form-source-reading-map.md +293 -0
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +561 -0
- package/cabloy-docs/frontend/zova-reactivity-under-the-hood.md +320 -0
- package/cabloy-docs/frontend/zova-router-under-the-hood.md +561 -0
- package/cabloy-docs/frontend/zova-source-reading-map.md +421 -0
- package/cabloy-docs/frontend/zova-table-controller-render-supplement.md +225 -0
- package/cabloy-docs/frontend/zova-table-source-reading-map.md +317 -0
- package/cabloy-docs/frontend/zova-table-under-the-hood.md +532 -0
- package/cabloy-docs/frontend/zova-vs-vue3-comparison.md +308 -0
- package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-debug-checklist.md +245 -0
- package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-source-reading-map.md +139 -0
- package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions-verify-playbook.md +248 -0
- package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions.md +511 -0
- package/cabloy-docs/fullstack/contract-loop-playbook.md +356 -0
- package/cabloy-docs/fullstack/edition-collaboration-differences.md +6 -0
- package/cabloy-docs/fullstack/frontend-metadata-to-backend.md +199 -23
- package/cabloy-docs/fullstack/introduction.md +15 -1
- package/cabloy-docs/fullstack/openapi-to-sdk.md +135 -11
- package/cabloy-docs/fullstack/suites-and-modules.md +333 -0
- package/cabloy-docs/fullstack/tutorial-1-first-module.md +3 -0
- package/cabloy-docs/fullstack/tutorial-2-first-crud.md +4 -0
- package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +6 -2
- package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +60 -23
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +14 -7
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +6 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +17 -4
- package/cabloy-docs/reference/bean-scene-boilerplates.md +15 -13
- package/cabloy-docs/reference/package-map.md +4 -3
- package/package.json +2 -1
- package/scripts/init.ts +2 -18
- package/scripts/initTestData.ts +25 -0
- package/scripts/upgrade.ts +17 -2
- package/vona/pnpm-lock.yaml +48 -194
- package/vona/src/suite/a-training/modules/training-student/package.json +53 -0
- package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +400 -0
- package/vona/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +18 -0
- package/vona/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/bean/meta.index.ts +12 -0
- package/vona/src/suite/a-training/modules/training-student/src/bean/meta.version.ts +21 -0
- package/vona/src/suite/a-training/modules/training-student/src/bean/ssrMenu.student.ts +29 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +15 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +15 -0
- package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +74 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +28 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +44 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectRes.tsx +11 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +45 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +42 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +28 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +25 -0
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +84 -0
- package/vona/src/suite/a-training/modules/training-student/src/index.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +10 -0
- package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +57 -0
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +173 -0
- package/vona/src/suite/a-training/modules/training-student/tsconfig.build.json +11 -0
- package/vona/src/suite/a-training/modules/training-student/tsconfig.json +7 -0
- package/vona/src/suite/a-training/package.json +12 -0
- package/vona/src/suite/a-training/tsconfig.base.json +4 -0
- package/vona/src/suite/a-training/tsconfig.json +10 -0
- package/zova/packages-cli/cli/package.json +2 -2
- 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 +1 -1
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +34 -4
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +416 -690
- package/zova/src/suite/a-training/modules/training-student/cli/openapi.config.ts +9 -0
- package/zova/src/suite/a-training/modules/training-student/package.json +52 -0
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/component/formFieldLevel.ts +31 -0
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/index.ts +258 -0
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/locales.ts +7 -0
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/this.ts +2 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/index.ts +3 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +196 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +4146 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +151 -0
- package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +43 -0
- package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionDeleteForce.tsx +51 -0
- package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +56 -0
- package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.level.tsx +63 -0
- package/zova/src/suite/a-training/modules/training-student/src/component/formFieldLevel/controller.tsx +117 -0
- package/zova/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +9 -0
- package/zova/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +9 -0
- package/zova/src/suite/a-training/modules/training-student/src/index.ts +2 -0
- package/zova/src/suite/a-training/modules/training-student/src/model/student.ts +42 -0
- package/zova/src/suite/a-training/modules/training-student/tsconfig.build.json +13 -0
- package/zova/src/suite/a-training/modules/training-student/tsconfig.json +5 -0
- package/zova/src/suite/a-training/package.json +12 -0
- package/zova/src/suite/a-training/tsconfig.base.json +4 -0
- package/zova/src/suite/a-training/tsconfig.json +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +29 -7
- package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/select/controller.tsx +34 -11
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +3 -3
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/lib/tableCell.ts +1 -1
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
<Badge type="info" text="Basic" />
|
|
4
4
|
|
|
5
|
-
In this tutorial, one prompt lets
|
|
5
|
+
In this tutorial, one prompt lets the user express a simple product need: the `level` field in the Student experience should feel less generic and more like a real training-stage workflow.
|
|
6
|
+
|
|
7
|
+
From that user need, AI can infer that the next step is no longer just relabeling the field. It now needs a more dedicated rendering experience owned by the `demo-student` module.
|
|
8
|
+
|
|
9
|
+
This tutorial covers the **reverse chain** custom-resource handoff branch of the contract loop.
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> Like the earlier tutorials in this series, this page keeps using the standalone `demo-student` sandbox.
|
|
13
|
+
> That keeps the custom-renderer experiment isolated from the repo's real suite-owned `a-training/training-student` implementation.
|
|
6
14
|
|
|
7
15
|
## Goal
|
|
8
16
|
|
|
9
|
-
By the end of this tutorial, you will understand
|
|
17
|
+
By the end of this tutorial, you will understand how a user-facing request about a more business-specific `level` experience turns into a concrete custom-renderer implementation.
|
|
18
|
+
|
|
19
|
+
In particular, you will understand:
|
|
10
20
|
|
|
11
21
|
- when built-in render metadata is enough and when a custom renderer is worth adding
|
|
12
22
|
- how a frontend table cell bean and a frontend form-field component can both serve the same backend field
|
|
@@ -17,17 +27,17 @@ By the end of this tutorial, you will understand:
|
|
|
17
27
|
Give AI a prompt like this:
|
|
18
28
|
|
|
19
29
|
```text
|
|
20
|
-
|
|
30
|
+
For demo purposes, use custom renderer components to give the `level` field on the Student list page and form a more distinctive look and feel.
|
|
21
31
|
```
|
|
22
32
|
|
|
23
33
|
## Why this step matters
|
|
24
34
|
|
|
25
|
-
This is the right follow-up step because built-in render resources are a good starting point, but some
|
|
35
|
+
This is the right follow-up step because built-in render resources are a good starting point, but some fields eventually need a more distinctive user experience to match the business workflow.
|
|
26
36
|
|
|
27
|
-
The `level` field is a good teaching example because
|
|
37
|
+
The `level` field is a good teaching example because users often expect two improvements once a training workflow becomes more realistic:
|
|
28
38
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
39
|
+
- the list should make each training stage easier to recognize at a glance
|
|
40
|
+
- the form should guide the user more clearly when choosing a training stage
|
|
31
41
|
|
|
32
42
|
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
43
|
|
|
@@ -50,14 +60,15 @@ npm run zova :create:component formFieldLevel -- --module=demo-student
|
|
|
50
60
|
|
|
51
61
|
Usage notes:
|
|
52
62
|
|
|
53
|
-
- use `:create:bean` when
|
|
54
|
-
- use `:create:component` when
|
|
63
|
+
- use `:create:bean` when AI determines that the list needs a dedicated table-cell render resource under the bean scene
|
|
64
|
+
- use `:create:component` when AI determines that the form needs a custom frontend component/controller surface
|
|
55
65
|
- generation gives you the structural starting point, but this tutorial still expects manual refinement so the result matches the `demo-student` teaching implementation
|
|
56
66
|
- after frontend resources exist, return to the backend entity and point `ZovaRender.field(...)` and `ZovaRender.cell(...)` at the custom module resources
|
|
67
|
+
- once backend metadata starts consuming those new frontend resources, treat the next step as a reverse fullstack handoff rather than frontend-only cleanup: refresh generated output, rebuild the relevant flavor, and re-sync Vona dependencies
|
|
57
68
|
|
|
58
69
|
## Generated or affected files
|
|
59
70
|
|
|
60
|
-
|
|
71
|
+
To satisfy the user-facing need above, AI will usually converge on a small set of implementation anchors like these:
|
|
61
72
|
|
|
62
73
|
- custom table cell bean:
|
|
63
74
|
- `zova/src/module/demo-student/src/bean/tableCell.level.tsx`
|
|
@@ -68,50 +79,76 @@ By the end of this tutorial, your module should provide these teaching anchors:
|
|
|
68
79
|
- backend field contract to update:
|
|
69
80
|
- `vona/src/module/demo-student/src/entity/student.tsx`
|
|
70
81
|
|
|
71
|
-
Representative
|
|
82
|
+
Representative metadata targets after AI makes that implementation decision are:
|
|
72
83
|
|
|
73
84
|
```typescript
|
|
74
85
|
ZovaRender.field('demo-student:formFieldLevel', {
|
|
75
86
|
items: levelItems,
|
|
76
87
|
helper: $locale('LevelPlaceholder'),
|
|
77
|
-
})
|
|
88
|
+
});
|
|
78
89
|
```
|
|
79
90
|
|
|
80
91
|
and:
|
|
81
92
|
|
|
82
93
|
```typescript
|
|
83
|
-
ZovaRender.cell('demo-student:level', { items: levelItems })
|
|
94
|
+
ZovaRender.cell('demo-student:level', { items: levelItems });
|
|
84
95
|
```
|
|
85
96
|
|
|
86
|
-
|
|
97
|
+
This is the point where a user request about “better list presentation” and “better form guidance” starts turning into explicit renderer resources and backend metadata links.
|
|
98
|
+
|
|
99
|
+
## How those files support the user experience
|
|
87
100
|
|
|
88
|
-
|
|
101
|
+
These files work together to deliver the richer `level` experience:
|
|
89
102
|
|
|
90
|
-
- `tableCell.level.tsx`
|
|
91
|
-
- `component/formFieldLevel/controller.tsx`
|
|
103
|
+
- `tableCell.level.tsx` makes the training stage easier to recognize in the list
|
|
104
|
+
- `component/formFieldLevel/controller.tsx` makes the form interaction more guided and business-specific
|
|
92
105
|
- `.metadata/component/formFieldLevel.ts` exposes that component through the module registration surface
|
|
93
106
|
- `entity/student.tsx` remains the backend-owned business contract that chooses which frontend resources should render the field
|
|
94
107
|
|
|
95
|
-
That means the backend still defines the business field, validation, and metadata entry point, while the frontend module owns the implementation details
|
|
108
|
+
That means the backend still defines the business field, validation, and metadata entry point, while the frontend module owns the implementation details that make the experience feel more tailored to the Student Training Center workflow.
|
|
96
109
|
|
|
97
110
|
## Verification
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
These checks are the reverse-chain synchronization steps AI must complete so the user-facing renderer change is actually available to backend consumers and the running app.
|
|
113
|
+
|
|
114
|
+
1. refresh the generated handoff surfaces before checking backend consumers:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm run zova :tools:metadata demo-student
|
|
118
|
+
npm run build:zova:admin
|
|
119
|
+
npm run deps:vona
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If the same renderer path must also be available for Web, also run:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm run build:zova:web
|
|
126
|
+
npm run deps:vona
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If backend-side type consumers still cannot see the new shared renderer types even though generated `.zova-rest` output is already correct, rebuild `vona/node_modules` and reinstall dependencies:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cd vona && rm -rf node_modules && pnpm install
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
2. make sure the local dev workflow is running:
|
|
100
136
|
|
|
101
137
|
```bash
|
|
102
138
|
npm run dev
|
|
103
139
|
```
|
|
104
140
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
141
|
+
3. open `http://localhost:7102/admin/`
|
|
142
|
+
4. enter the **Student** list page and verify that the `level` column now uses the custom table-cell presentation
|
|
143
|
+
5. open a Student create, update, or view form and verify that the `level` field now uses the custom form-field behavior
|
|
144
|
+
6. 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
145
|
|
|
110
146
|
## Read more
|
|
111
147
|
|
|
112
148
|
- [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend)
|
|
113
149
|
- [Frontend CLI](/frontend/cli)
|
|
114
150
|
- [Component Guide](/frontend/component-guide)
|
|
151
|
+
- [Zova Form Under the Hood](/frontend/zova-form-under-the-hood)
|
|
115
152
|
- [Bean Scene Boilerplate Variants](/reference/bean-scene-boilerplates)
|
|
116
153
|
|
|
117
154
|
## Next step
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
<Badge type="info" text="Basic" />
|
|
4
4
|
|
|
5
|
-
In this tutorial, one prompt lets AI show the forward
|
|
5
|
+
In this tutorial, one prompt lets AI show the **forward chain** of Cabloy’s fullstack contract loop: backend API contracts that generate and refresh the frontend consumption surface.
|
|
6
6
|
|
|
7
7
|
The teaching thread in this page is the pair of Student actions:
|
|
8
8
|
|
|
9
9
|
- `summary/:id`
|
|
10
10
|
- `deleteForce/:id`
|
|
11
11
|
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> This tutorial still runs inside the standalone `demo-student` sandbox from the earlier pages.
|
|
14
|
+
> Use that sandbox for OpenAPI regeneration experiments so you do not interfere with the repo's real suite-owned `a-training/training-student` implementation.
|
|
15
|
+
|
|
12
16
|
## Goal
|
|
13
17
|
|
|
14
18
|
By the end of this tutorial, you will understand:
|
|
15
19
|
|
|
16
20
|
- how a backend controller and DTO change becomes frontend generated API output
|
|
17
21
|
- how backend row-action metadata can drive frontend table actions
|
|
18
|
-
- how generated API, frontend model helpers, and row-action beans fit into one
|
|
22
|
+
- how generated API, thin frontend model helpers, and row-action beans fit into one forward-chain workflow
|
|
23
|
+
- why custom resource endpoints that still belong to the same resource should reuse the existing resource-owner instead of creating a competing cache owner
|
|
19
24
|
|
|
20
25
|
## AI Prompt
|
|
21
26
|
|
|
@@ -26,8 +31,6 @@ Please add two custom actions to the Student list:
|
|
|
26
31
|
|
|
27
32
|
- Summary: return or display a student summary for the selected row
|
|
28
33
|
- 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
34
|
```
|
|
32
35
|
|
|
33
36
|
## Why this step matters
|
|
@@ -60,6 +63,8 @@ Usage notes:
|
|
|
60
63
|
- use the backend controller and DTOs as the starting point
|
|
61
64
|
- inspect the module OpenAPI config before generation
|
|
62
65
|
- prefer regeneration over hand-written duplicate API layers when the module already owns an OpenAPI contract surface
|
|
66
|
+
- keep frontend follow-up thin: treat the module model as a semantic facade over generated API consumers rather than a second contract-definition layer
|
|
67
|
+
- if the custom endpoint still belongs to the existing resource, reuse the existing resource-owner for server-state ownership
|
|
63
68
|
|
|
64
69
|
## Generated or affected files
|
|
65
70
|
|
|
@@ -105,10 +110,11 @@ This tutorial is easiest to understand as one contract chain:
|
|
|
105
110
|
3. `dto/studentSelectResItem.tsx` defines the row-action metadata that exposes those actions in the Student list page
|
|
106
111
|
4. `cli/openapi.config.ts` tells the frontend module which backend operations it owns
|
|
107
112
|
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
|
|
109
|
-
7.
|
|
113
|
+
6. `src/model/student.ts` wraps the generated API in a thin frontend semantic facade
|
|
114
|
+
7. the model should reuse the existing resource-owner for server-state ownership when the new API still belongs to the Student resource
|
|
115
|
+
8. `tableCell.actionSummary.tsx` and `tableCell.actionDeleteForce.tsx` turn those model methods into visible row actions
|
|
110
116
|
|
|
111
|
-
That is the practical
|
|
117
|
+
That is the practical forward chain: backend controller and DTO truth flows into generated frontend API output, then into thin frontend model helpers, and finally into visible table actions.
|
|
112
118
|
|
|
113
119
|
## Verification
|
|
114
120
|
|
|
@@ -133,6 +139,7 @@ npm run dev
|
|
|
133
139
|
|
|
134
140
|
## Read more
|
|
135
141
|
|
|
142
|
+
- [Backend Metadata to Frontend Table Actions](/fullstack/backend-metadata-to-frontend-table-actions)
|
|
136
143
|
- [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
|
|
137
144
|
- [OpenAPI Guide](/backend/openapi-guide)
|
|
138
145
|
- [OpenAPI SDK Guide](/frontend/openapi-sdk-guide)
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
+
This capstone sits on top of both the forward chain and the reverse chain rather than replacing either one.
|
|
8
|
+
|
|
7
9
|
This time the main teaching field is `mobile`, while `level` stays as the supporting example for table and form rendering.
|
|
8
10
|
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> This capstone still uses the standalone `demo-student` sandbox carried through the tutorial series.
|
|
13
|
+
> That keeps the final experiment path isolated while preserving a clean comparison against the repo's real suite-owned `a-training/training-student` implementation.
|
|
14
|
+
|
|
9
15
|
## Goal
|
|
10
16
|
|
|
11
17
|
By the end of this tutorial, you will understand how one business field thread can participate in:
|
|
@@ -36,7 +36,7 @@ Why these fields?
|
|
|
36
36
|
|
|
37
37
|
This keeps the storyline small enough for beginners while still showing Cabloy’s fullstack contract model.
|
|
38
38
|
|
|
39
|
-
At the beginning of the series, the `demo-student` module does not exist yet. The tutorials build it step by step.
|
|
39
|
+
At the beginning of the series, the `demo-student` module does not exist yet. The tutorials build it step by step. This standalone tutorial track is intentional: it gives readers a safe experiment surface that does not collide with the repo's real `a-training/training-student` implementation and also makes side-by-side comparison easier.
|
|
40
40
|
|
|
41
41
|
## What you should prepare first
|
|
42
42
|
|
|
@@ -61,16 +61,29 @@ Those pages explain the repo entrypoints and the CLI-first workflow model that t
|
|
|
61
61
|
- [Tutorial 1: Create Your First Module](/fullstack/tutorial-1-first-module)
|
|
62
62
|
- [Tutorial 2: Create Your First CRUD](/fullstack/tutorial-2-first-crud)
|
|
63
63
|
|
|
64
|
-
### Phase 2:
|
|
64
|
+
### Phase 2: Reverse chain — share frontend rendering metadata through the backend contract
|
|
65
65
|
|
|
66
66
|
- [Tutorial 3: Frontend Metadata Sharing](/fullstack/tutorial-3-frontend-metadata-sharing)
|
|
67
67
|
- [Tutorial 4: Custom Form/Table Renderers for Level](/fullstack/tutorial-4-custom-level-renderers)
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
Important handoff note for this phase:
|
|
70
|
+
|
|
71
|
+
- this phase teaches the **reverse chain** of the Cabloy contract loop
|
|
72
|
+
- Tutorial 3 uses the built-in metadata branch
|
|
73
|
+
- Tutorial 4 uses the custom resource handoff branch
|
|
74
|
+
- once a frontend resource created in these tutorials is later consumed by backend metadata, do not stop at frontend source edits alone
|
|
75
|
+
- refresh the generated frontend output, run the relevant flavor build, then run `npm run deps:vona`
|
|
76
|
+
- if backend-side shared types still look stale after that normal sync flow, rebuild `vona/node_modules` and reinstall dependencies
|
|
77
|
+
|
|
78
|
+
See [Contract Loop Playbook](/fullstack/contract-loop-playbook) and [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend) for the full reverse-chain explanation.
|
|
79
|
+
|
|
80
|
+
### Phase 3: Forward chain — share backend contracts into frontend consumption
|
|
70
81
|
|
|
71
82
|
- [Tutorial 5: Backend Contract Sharing](/fullstack/tutorial-5-backend-contract-sharing)
|
|
72
83
|
|
|
73
|
-
|
|
84
|
+
This phase teaches the **forward chain** of the contract loop: backend contract truth changes first, generated frontend consumers are refreshed second, and frontend follow-up stays thin and resource-owner-aware.
|
|
85
|
+
|
|
86
|
+
### Phase 4: One field story across multiple contract surfaces
|
|
74
87
|
|
|
75
88
|
- [Tutorial 6: One Contract Surface, Four Uses](/fullstack/tutorial-6-one-contract-four-uses)
|
|
76
89
|
|
|
@@ -28,15 +28,15 @@ A practical contributor rule is:
|
|
|
28
28
|
|
|
29
29
|
### Current built-in scenes with variants
|
|
30
30
|
|
|
31
|
-
| Scene | Default metadata key | Named variant keys | Example command
|
|
32
|
-
| -------------- | -------------------- | ------------------- |
|
|
33
|
-
| `filter` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean filter log -- --module=
|
|
34
|
-
| `pipe` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean pipe log -- --module=
|
|
35
|
-
| `interceptor` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean interceptor log -- --module=
|
|
36
|
-
| `guard` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean guard auth -- --module=
|
|
37
|
-
| `middleware` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean middleware trace -- --module=
|
|
38
|
-
| `ssrMenu` | `boilerplate` | `boilerplateWeb` | `npm run vona :create:bean ssrMenu menuTest -- --module=
|
|
39
|
-
| `ssrMenuGroup` | `boilerplate` | `boilerplateWeb` | `npm run vona :create:bean ssrMenuGroup groupTest -- --module=
|
|
31
|
+
| Scene | Default metadata key | Named variant keys | Example command | Source module |
|
|
32
|
+
| -------------- | -------------------- | ------------------- | ------------------------------------------------------------------------------------------------- | ---------------------- |
|
|
33
|
+
| `filter` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean filter log -- --module=training-student --boilerplate=global` | `vona-module-a-aspect` |
|
|
34
|
+
| `pipe` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean pipe log -- --module=training-student --boilerplate=global` | `vona-module-a-aspect` |
|
|
35
|
+
| `interceptor` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean interceptor log -- --module=training-student --boilerplate=global` | `vona-module-a-aspect` |
|
|
36
|
+
| `guard` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean guard auth -- --module=training-student --boilerplate=global` | `vona-module-a-aspect` |
|
|
37
|
+
| `middleware` | `boilerplate` | `boilerplateGlobal` | `npm run vona :create:bean middleware trace -- --module=training-student --boilerplate=global` | `vona-module-a-aspect` |
|
|
38
|
+
| `ssrMenu` | `boilerplate` | `boilerplateWeb` | `npm run vona :create:bean ssrMenu menuTest -- --module=training-student --boilerplate=web` | `vona-module-a-ssr` |
|
|
39
|
+
| `ssrMenuGroup` | `boilerplate` | `boilerplateWeb` | `npm run vona :create:bean ssrMenuGroup groupTest -- --module=training-student --boilerplate=web` | `vona-module-a-ssr` |
|
|
40
40
|
|
|
41
41
|
These backend entries come from the current `vonaModule.onions` metadata in `a-aspect` and `a-ssr`.
|
|
42
42
|
|
|
@@ -44,13 +44,15 @@ These backend entries come from the current `vonaModule.onions` metadata in `a-a
|
|
|
44
44
|
|
|
45
45
|
### Current built-in scenes with variants
|
|
46
46
|
|
|
47
|
-
| Scene | Default metadata key | Named variant keys | Example command pattern
|
|
48
|
-
| ----------- | -------------------- | ------------------------------------------------- |
|
|
49
|
-
| `command` | `boilerplate` | `boilerplateCommandBulk`, `boilerplateCommandRow` | `npm run zova :create:bean command test -- --module=
|
|
50
|
-
| `tableCell` | `boilerplate` | `boilerplateTableActionRow` | `npm run zova :create:bean tableCell test -- --module=
|
|
47
|
+
| Scene | Default metadata key | Named variant keys | Example command pattern | Source module |
|
|
48
|
+
| ----------- | -------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------- |
|
|
49
|
+
| `command` | `boilerplate` | `boilerplateCommandBulk`, `boilerplateCommandRow` | `npm run zova :create:bean command test -- --module=training-student --boilerplate=commandRow` | `zova-module-a-command` |
|
|
50
|
+
| `tableCell` | `boilerplate` | `boilerplateTableActionRow` | `npm run zova :create:bean tableCell test -- --module=training-student --boilerplate=tableActionRow` | `zova-module-a-table` |
|
|
51
51
|
|
|
52
52
|
These frontend entries come from the current `zovaModule.onions` metadata in `a-command` and `a-table`.
|
|
53
53
|
|
|
54
|
+
For the built-in command scene’s runtime model and helper-base patterns, see [Command Scene Authoring](/frontend/command-scene-authoring).
|
|
55
|
+
|
|
54
56
|
## Guidance for AI-assisted development
|
|
55
57
|
|
|
56
58
|
Do not assume every bean scene supports named variants.
|
|
@@ -32,10 +32,10 @@ Representative package metadata in the current repo shows the distinction clearl
|
|
|
32
32
|
|
|
33
33
|
### Module package
|
|
34
34
|
|
|
35
|
-
Example: `vona/src/
|
|
35
|
+
Example: `vona/src/suite/a-training/modules/training-student/package.json`
|
|
36
36
|
|
|
37
|
-
- package name: `vona-module-
|
|
38
|
-
- title: `
|
|
37
|
+
- package name: `vona-module-training-student`
|
|
38
|
+
- title: `training-student`
|
|
39
39
|
- `vonaModule.dependencies` records module-level framework dependencies
|
|
40
40
|
|
|
41
41
|
### Suite package
|
|
@@ -74,6 +74,7 @@ A practical comparison is:
|
|
|
74
74
|
|
|
75
75
|
Use this package map together with:
|
|
76
76
|
|
|
77
|
+
- [Suites and Modules](/fullstack/suites-and-modules)
|
|
77
78
|
- [Backend Essentials](/backend/backend-essentials)
|
|
78
79
|
- [Backend Foundation](/backend/foundation)
|
|
79
80
|
- [Backend CLI](/backend/cli)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.62",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "A Node.js fullstack framework",
|
|
6
6
|
"keywords": [
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"init": "node scripts/init.ts",
|
|
25
|
+
"init:test-data": "node scripts/initTestData.ts",
|
|
25
26
|
"upgrade": "node scripts/upgrade.ts",
|
|
26
27
|
"upgrade:dry-run": "node scripts/upgrade.ts --dry-run",
|
|
27
28
|
"vona": "node ./vona/packages-cli/cli/src/bin/vona.ts --projectPath=vona",
|
package/scripts/init.ts
CHANGED
|
@@ -247,22 +247,7 @@ function buildSsrCabloyBasicStartBatch(): void {
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// --- Step F:
|
|
251
|
-
|
|
252
|
-
function initTestData(): void {
|
|
253
|
-
// eslint-disable-next-line
|
|
254
|
-
console.log('[init] Initializing test data via npm run test...');
|
|
255
|
-
try {
|
|
256
|
-
exec('npm run test');
|
|
257
|
-
} catch {
|
|
258
|
-
// eslint-disable-next-line
|
|
259
|
-
console.warn(
|
|
260
|
-
'[init] npm run test failed after init completed; Redis may be unavailable, skipping test data initialization',
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// --- Step G: cleanupWorkspaceYaml ---
|
|
250
|
+
// --- Step F: cleanupWorkspaceYaml ---
|
|
266
251
|
|
|
267
252
|
function cleanupWorkspaceYaml(): void {
|
|
268
253
|
const subProjects = ['vona', 'zova'];
|
|
@@ -283,7 +268,7 @@ function cleanupWorkspaceYaml(): void {
|
|
|
283
268
|
}
|
|
284
269
|
}
|
|
285
270
|
|
|
286
|
-
// --- Step
|
|
271
|
+
// --- Step G: init:cabloy-docs ---
|
|
287
272
|
|
|
288
273
|
function initCabloyDocs(): void {
|
|
289
274
|
const pkgPath = resolve(CABLOY_DOCS_DIR, 'package.json');
|
|
@@ -306,6 +291,5 @@ initZova();
|
|
|
306
291
|
initCabloyDocs();
|
|
307
292
|
buildSsrCabloyBasicStartBatch();
|
|
308
293
|
writeVersionMarker();
|
|
309
|
-
initTestData();
|
|
310
294
|
// eslint-disable-next-line
|
|
311
295
|
console.log('[init] Done!');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const ROOT_DIR = resolve(__dirname, '..');
|
|
7
|
+
|
|
8
|
+
function exec(cmd: string): void {
|
|
9
|
+
execSync(cmd, { stdio: 'inherit', cwd: ROOT_DIR });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function initTestData(): void {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.log('[init:test-data] Initializing test data via npm run test...');
|
|
15
|
+
try {
|
|
16
|
+
exec('npm run test');
|
|
17
|
+
} catch {
|
|
18
|
+
// eslint-disable-next-line no-console
|
|
19
|
+
console.warn(
|
|
20
|
+
'[init:test-data] npm run test failed after init completed; Redis may be unavailable, skipping test data initialization',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
initTestData();
|
package/scripts/upgrade.ts
CHANGED
|
@@ -300,7 +300,17 @@ function runInit(dryRun: boolean, version: string): void {
|
|
|
300
300
|
exec('npm run init', { CABLOY_VERSION: version } as any);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
// --- Step 5:
|
|
303
|
+
// --- Step 5: Run init:test-data ---
|
|
304
|
+
|
|
305
|
+
function runInitTestData(dryRun: boolean): void {
|
|
306
|
+
if (dryRun) {
|
|
307
|
+
log(' [dry-run] Run: npm run init:test-data');
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
exec('npm run init:test-data');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// --- Step 6: Cleanup ---
|
|
304
314
|
|
|
305
315
|
function cleanup(dryRun?: boolean): void {
|
|
306
316
|
if (dryRun) {
|
|
@@ -366,6 +376,11 @@ async function main(): Promise<void> {
|
|
|
366
376
|
runInit(dryRun, latestPackageInfo.version);
|
|
367
377
|
log('');
|
|
368
378
|
|
|
379
|
+
// 5. Run init:test-data
|
|
380
|
+
log('Running npm run init:test-data...');
|
|
381
|
+
runInitTestData(dryRun);
|
|
382
|
+
log('');
|
|
383
|
+
|
|
369
384
|
if (dryRun) {
|
|
370
385
|
log(`[dry-run] Current Cabloy version would become: ${latestPackageInfo.version}`);
|
|
371
386
|
} else {
|
|
@@ -374,7 +389,7 @@ async function main(): Promise<void> {
|
|
|
374
389
|
|
|
375
390
|
log('Upgrade complete!');
|
|
376
391
|
} finally {
|
|
377
|
-
//
|
|
392
|
+
// 6. Cleanup
|
|
378
393
|
cleanup(dryRun);
|
|
379
394
|
}
|
|
380
395
|
}
|