cabloy 5.1.72 → 5.1.74
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-backend-scaffold/SKILL.md +3 -1
- package/.claude/skills/cabloy-master-detail/SKILL.md +198 -0
- package/.claude/skills/cabloy-workflow/SKILL.md +3 -1
- package/.gitignore +0 -2
- package/CHANGELOG.md +38 -0
- package/cabloy-docs/.vitepress/config.mjs +5 -0
- package/cabloy-docs/backend/dto-guide.md +1 -1
- package/cabloy-docs/backend/dto-infer-generation.md +38 -0
- package/cabloy-docs/backend/master-detail-source-reading-map.md +260 -0
- package/cabloy-docs/backend/master-detail-workflow.md +174 -23
- package/cabloy-docs/backend/relations-guide.md +4 -0
- package/lint-staged.config.mjs +0 -1
- package/oxfmt.config.ts +0 -1
- package/oxlint.config.ts +0 -1
- package/package.json +2 -2
- package/scripts/init.ts +29 -2
- package/vona/oxfmt.config.ts +0 -1
- package/vona/oxlint.config.ts +0 -1
- package/vona/packages-cli/cabloy-cli/package.json +1 -1
- package/vona/packages-cli/cabloy-cli/src/lib/local.common.ts +3 -3
- 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.bin.build.ts +2 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.buildModule.ts +2 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dev.ts +1 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.deps.ts +10 -23
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.metadata.ts +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/patches/zova-core@5.1.61.patch +57 -0
- package/vona/pnpm-lock.yaml +278 -332
- package/vona/pnpm-workspace.yaml +4 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +1 -1
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +1 -1
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +2 -52
- package/vona/src/suite/a-training/modules/training-record/src/index.ts +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/index.ts +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/onEffectForAverageScore.tsx +29 -0
- package/vona/src/suite/a-training/modules/training-record/src/lib/onEffectForTrainingRecordSubjects.tsx +26 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +2 -1
- package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/package.json +1 -2
- package/vona/src/suite/cabloy-basic/modules/basic-siteweb/package.json +1 -2
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/env/.env.cabloyBasicAdmin +1 -1
- package/zova/env/.env.cabloyBasicWeb +1 -1
- package/zova/oxfmt.config.ts +0 -1
- package/zova/oxlint.config.ts +0 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/package.json +2 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +47 -47
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.tabs.tsx +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
# Master-Detail Workflow
|
|
2
2
|
|
|
3
|
-
This guide explains how to scaffold
|
|
3
|
+
This guide explains how to scaffold and reason about backend detail aggregates in the Cabloy monorepo.
|
|
4
|
+
|
|
5
|
+
Use this page when the business shape is not “two unrelated CRUD resources,” but a parent resource that owns one or more nested detail collections.
|
|
6
|
+
|
|
7
|
+
> [!TIP]
|
|
8
|
+
> For the deep evidence trail behind this workflow, also read [Master-Detail Source Reading Map](/backend/master-detail-source-reading-map).
|
|
9
|
+
|
|
10
|
+
## What this page covers
|
|
11
|
+
|
|
12
|
+
The current repo already demonstrates three related shapes:
|
|
13
|
+
|
|
14
|
+
1. **master-detail** — one master resource owns a first-level detail collection
|
|
15
|
+
2. **nested-detail** — a detail can itself own another detail collection
|
|
16
|
+
3. **standalone-capable detail resource** — a detail can remain aggregate-owned only, or also keep its own standalone resource surface
|
|
17
|
+
|
|
18
|
+
The strongest current specimen is:
|
|
19
|
+
|
|
20
|
+
- `training-student` -> `training-record` -> `training-recordsubject`
|
|
21
|
+
|
|
22
|
+
A practical reading of that chain is:
|
|
23
|
+
|
|
24
|
+
- `student` is the master resource
|
|
25
|
+
- `record` is a first-level detail under `student`
|
|
26
|
+
- `subject` is a second-level detail under `record`
|
|
27
|
+
- `record` also demonstrates the case where a detail keeps its own standalone CRUD/resource surface
|
|
4
28
|
|
|
5
29
|
## Why this page matters
|
|
6
30
|
|
|
7
|
-
Some business shapes are not best expressed as
|
|
31
|
+
Some business shapes are not best expressed as unrelated standalone resources.
|
|
8
32
|
|
|
9
|
-
A common pattern is:
|
|
33
|
+
A common Cabloy pattern is:
|
|
10
34
|
|
|
11
35
|
- one master resource owns the aggregate lifecycle
|
|
12
36
|
- one detail resource is edited as a nested collection under the master
|
|
13
37
|
- the detail may or may not also expose its own standalone resource surface
|
|
38
|
+
- the same structural rule can repeat recursively when a detail becomes the parent of another detail
|
|
14
39
|
|
|
15
|
-
The `training-student` + `training-record` specimen
|
|
40
|
+
The `training-student` + `training-record` specimen demonstrates the first-level pattern, and the `training-record` + `training-recordsubject` thread demonstrates the recursive second-level pattern.
|
|
16
41
|
|
|
17
42
|
## Use the generator first
|
|
18
43
|
|
|
@@ -34,6 +59,8 @@ A practical reading of the arguments is:
|
|
|
34
59
|
- `--fk=studentId`: detail-side FK field
|
|
35
60
|
- `--detailMode=...`: whether the detail keeps a standalone resource surface
|
|
36
61
|
|
|
62
|
+
The same structural rule applies recursively when a first-level detail later owns its own nested detail collection.
|
|
63
|
+
|
|
37
64
|
## Module naming rule
|
|
38
65
|
|
|
39
66
|
For `--module` and `--detailModule`, use the canonical Vona module relative name such as `training-student` or `training-record`.
|
|
@@ -53,9 +80,10 @@ The generator is designed to create the aggregate-detail thread, including:
|
|
|
53
80
|
1. master model relation wiring
|
|
54
81
|
2. master service `include` lifecycle for create/view/update/delete
|
|
55
82
|
3. master-side nested detail DTOs
|
|
56
|
-
4. built-in `basic-details` UI metadata for row/bulk
|
|
83
|
+
4. built-in `basic-details` UI metadata for nested row/bulk interactions
|
|
57
84
|
5. detail FK persistence field in the detail entity
|
|
58
85
|
6. detail schema/index support for the FK
|
|
86
|
+
7. metadata refresh for both the master module and the detail module
|
|
59
87
|
|
|
60
88
|
This keeps the structural pattern consistent before domain-specific refinement starts.
|
|
61
89
|
|
|
@@ -73,7 +101,12 @@ In this mode, the detail module remains:
|
|
|
73
101
|
|
|
74
102
|
- entity/model/meta-based
|
|
75
103
|
- owned by the master aggregate
|
|
76
|
-
- without a standalone controller/service resource surface
|
|
104
|
+
- without a standalone controller/service/DTO resource surface
|
|
105
|
+
|
|
106
|
+
A practical source-backed nuance is:
|
|
107
|
+
|
|
108
|
+
- if the detail resource was just scaffolded for aggregate usage, the standalone controller/service/DTO surface is removed
|
|
109
|
+
- if an existing detail module already has a standalone surface, the generator refuses to silently repurpose it as aggregate-only
|
|
77
110
|
|
|
78
111
|
Choose this mode when the detail is primarily edited only through the master workflow.
|
|
79
112
|
|
|
@@ -88,7 +121,8 @@ Use:
|
|
|
88
121
|
In this mode, the detail module:
|
|
89
122
|
|
|
90
123
|
- still participates in the master aggregate
|
|
91
|
-
- keeps
|
|
124
|
+
- keeps its own standalone controller/service/DTO resource surface
|
|
125
|
+
- can be addressed both through nested editing and through its own independent resource workflow
|
|
92
126
|
|
|
93
127
|
Choose this mode when the detail also needs independent entry points, workflows, or resource-level access outside the master editing flow.
|
|
94
128
|
|
|
@@ -106,44 +140,161 @@ Use standalone mode when:
|
|
|
106
140
|
- external workflows need to query or mutate detail items directly
|
|
107
141
|
- the detail has user-facing or integration-facing behavior beyond nested editing
|
|
108
142
|
|
|
109
|
-
##
|
|
143
|
+
## Nested-detail pattern
|
|
144
|
+
|
|
145
|
+
Nested-detail is not a separate special case. It is the same structural idea applied one level lower.
|
|
146
|
+
|
|
147
|
+
The specimen chain is:
|
|
148
|
+
|
|
149
|
+
- `student`
|
|
150
|
+
- `trainingRecords`
|
|
151
|
+
- `trainingRecordSubjects`
|
|
152
|
+
|
|
153
|
+
A practical rule is:
|
|
154
|
+
|
|
155
|
+
- the master owns first-level detail editing
|
|
156
|
+
- a first-level detail can itself own second-level detail editing
|
|
157
|
+
- each level repeats the same relation + DTO + include wiring pattern with the immediate parent as the owner
|
|
158
|
+
|
|
159
|
+
That means `record` is both:
|
|
160
|
+
|
|
161
|
+
- a detail under `student`
|
|
162
|
+
- a parent of `trainingRecordSubjects`
|
|
163
|
+
|
|
164
|
+
## DTO naming and placement rules
|
|
165
|
+
|
|
166
|
+
The current source shows a consistent naming and placement rule for nested detail DTOs.
|
|
167
|
+
|
|
168
|
+
| Concern | Rule | Example | Boundary |
|
|
169
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
170
|
+
| Nested detail DTO naming | Prefix nested detail DTO names with `detail` for immediate recognizability | `detailRecordMutate`, `detailRecordView` | This rule is for nested detail DTOs, not every DTO in the child module |
|
|
171
|
+
| First-level placement | Place first-level detail DTOs beside the parent DTOs that consume them | `training-student/src/dto/studentCreate.tsx` beside `detailRecord*.tsx` | Do not move these DTOs into the child module just because the child also has its own resource |
|
|
172
|
+
| Second-level placement | Place second-level detail DTOs beside the DTOs of their immediate parent | `training-record/src/dto/recordCreate.tsx` beside `detailRecordSubject*.tsx` | Repeat the same rule recursively for deeper levels |
|
|
173
|
+
| Standalone child boundary | A detail resource that keeps its own standalone surface still uses its normal standalone DTO names there | `recordCreate`, `recordUpdate`, `recordView` in `training-record` | Standalone CRUD naming does not replace the nested `detail*` DTO convention |
|
|
174
|
+
|
|
175
|
+
## DTO naming rule
|
|
176
|
+
|
|
177
|
+
Use the `detail` prefix for nested detail DTOs so the role is explicit in both source and generated contract surfaces.
|
|
178
|
+
|
|
179
|
+
Representative first-level examples:
|
|
180
|
+
|
|
181
|
+
- `detailRecordBase`
|
|
182
|
+
- `detailRecordMutate`
|
|
183
|
+
- `detailRecordResItem`
|
|
184
|
+
- `detailRecordView`
|
|
110
185
|
|
|
111
|
-
|
|
186
|
+
Representative second-level examples:
|
|
187
|
+
|
|
188
|
+
- `detailRecordSubjectBase`
|
|
189
|
+
- `detailRecordSubjectMutate`
|
|
190
|
+
- `detailRecordSubjectResItem`
|
|
191
|
+
- `detailRecordSubjectView`
|
|
192
|
+
|
|
193
|
+
This makes nested detail artifacts easy to distinguish from ordinary standalone CRUD DTOs.
|
|
194
|
+
|
|
195
|
+
## DTO placement rule
|
|
196
|
+
|
|
197
|
+
Place nested detail DTOs next to the parent DTOs that consume them.
|
|
198
|
+
|
|
199
|
+
That means:
|
|
200
|
+
|
|
201
|
+
- the `student*` DTOs and `detailRecord*` DTOs live together in `training-student/src/dto/`
|
|
202
|
+
- the `record*` DTOs and `detailRecordSubject*` DTOs live together in `training-record/src/dto/`
|
|
203
|
+
|
|
204
|
+
A practical recursive rule is:
|
|
205
|
+
|
|
206
|
+
- first-level detail DTOs belong with the master DTOs
|
|
207
|
+
- second-level detail DTOs belong with the first-level parent DTOs
|
|
208
|
+
- repeat the same rule for deeper levels if the aggregate continues to nest
|
|
209
|
+
|
|
210
|
+
This placement makes the ownership path obvious at the point where nested editing is actually authored.
|
|
211
|
+
|
|
212
|
+
## `dtoClass` in master-detail DTO wiring
|
|
213
|
+
|
|
214
|
+
The nested DTO thread is not only about naming. The current scaffolded pattern also uses `dtoClass` so the parent DTO keeps aggregate relation wiring while the nested detail DTO class defines the reusable field surface.
|
|
215
|
+
|
|
216
|
+
Representative pattern:
|
|
217
|
+
|
|
218
|
+
- parent DTOs include nested details through `include: { relationName: { dtoClass: DtoDetail... } }`
|
|
219
|
+
- detail DTO families such as `detailRecordBase`, `detailRecordMutate`, and `detailRecordView` define which detail fields are exposed for each scene
|
|
220
|
+
- nested details can repeat the same `dtoClass` pattern recursively one level lower
|
|
221
|
+
|
|
222
|
+
That is why master-detail DTOs do not need to inline long relation column lists everywhere. They can reuse named detail DTO classes instead.
|
|
223
|
+
|
|
224
|
+
For a broader explanation of `dtoClass` as a DTO inference option, also read [DTO Infer and Generation](/backend/dto-infer-generation).
|
|
225
|
+
|
|
226
|
+
## Specimen walkthrough
|
|
227
|
+
|
|
228
|
+
The current specimen maps like this.
|
|
229
|
+
|
|
230
|
+
### First-level master-detail: `student -> record`
|
|
112
231
|
|
|
113
232
|
- master relation: `vona/src/suite/a-training/modules/training-student/src/model/student.ts`
|
|
114
233
|
- master service include lifecycle: `vona/src/suite/a-training/modules/training-student/src/service/student.ts`
|
|
115
|
-
- master nested
|
|
116
|
-
- detail
|
|
234
|
+
- master nested DTO wiring: `vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx`, `studentUpdate.tsx`, `studentView.tsx`
|
|
235
|
+
- first-level detail DTOs: `vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx`, `detailRecordMutate.tsx`, `detailRecordResItem.tsx`, `detailRecordView.tsx`
|
|
117
236
|
- detail FK field: `vona/src/suite/a-training/modules/training-record/src/entity/record.tsx`
|
|
237
|
+
- contract exposure: `vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts`
|
|
238
|
+
|
|
239
|
+
### Second-level nested-detail: `record -> subject`
|
|
240
|
+
|
|
241
|
+
- detail-as-parent relation: `vona/src/suite/a-training/modules/training-record/src/model/record.ts`
|
|
242
|
+
- detail-as-parent include lifecycle: `vona/src/suite/a-training/modules/training-record/src/service/record.ts`
|
|
243
|
+
- second-level nested DTO wiring: `vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx`, `recordUpdate.tsx`, `recordView.tsx`
|
|
244
|
+
- second-level detail DTOs: `vona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectBase.tsx`, `detailRecordSubjectMutate.tsx`, `detailRecordSubjectResItem.tsx`, `detailRecordSubjectView.tsx`
|
|
245
|
+
- contract exposure: `vona/src/suite/a-training/modules/training-record/src/.metadata/index.ts`
|
|
246
|
+
|
|
247
|
+
### Detail resource that also keeps a standalone resource surface
|
|
118
248
|
|
|
119
|
-
|
|
249
|
+
The `training-record` module demonstrates the dual-role case.
|
|
250
|
+
|
|
251
|
+
It is simultaneously:
|
|
252
|
+
|
|
253
|
+
- a nested detail under `student`
|
|
254
|
+
- a standalone resource with its own ordinary CRUD DTOs such as `recordCreate`, `recordUpdate`, and `recordView`
|
|
255
|
+
|
|
256
|
+
That is the practical boundary to remember:
|
|
257
|
+
|
|
258
|
+
- nested detail DTOs use `detail*` names in the parent module
|
|
259
|
+
- the child resource can still keep its own ordinary standalone DTO names in its own standalone resource surface
|
|
120
260
|
|
|
121
261
|
## Recommended workflow
|
|
122
262
|
|
|
123
263
|
1. run `:tools:masterDetail`
|
|
124
264
|
2. inspect the generated relation, DTO, and service thread
|
|
125
|
-
3.
|
|
126
|
-
4.
|
|
127
|
-
5.
|
|
265
|
+
3. confirm whether the detail should remain aggregate-only or also stay standalone
|
|
266
|
+
4. if the detail later becomes a parent of another detail, repeat the same pattern one level lower
|
|
267
|
+
5. refine entity fields and validation rules for the domain
|
|
268
|
+
6. refine detail columns/actions when the default `basic-details` behavior is not enough
|
|
269
|
+
7. refresh and verify metadata/build outputs
|
|
128
270
|
|
|
129
|
-
## Relationship to
|
|
271
|
+
## Relationship to other docs
|
|
130
272
|
|
|
131
273
|
Use [CRUD Workflow](/backend/crud-workflow) when the target is primarily a standalone resource.
|
|
132
274
|
|
|
133
|
-
Use
|
|
275
|
+
Use [Relations Guide](/backend/relations-guide) when the question is about general ORM relation semantics such as `hasMany`, `belongsTo`, `include`, or `with`.
|
|
276
|
+
|
|
277
|
+
Use [Master-Detail Source Reading Map](/backend/master-detail-source-reading-map) when the question becomes “which source files prove the current master-detail and nested-detail behavior?”
|
|
278
|
+
|
|
279
|
+
Use [Contract Loop Playbook](/fullstack/contract-loop-playbook) when the next question crosses the backend/frontend contract boundary.
|
|
134
280
|
|
|
135
|
-
The
|
|
281
|
+
The workflows are complementary:
|
|
136
282
|
|
|
137
283
|
- CRUD scaffolds standalone backend threads
|
|
138
284
|
- master-detail scaffolds aggregate ownership plus nested detail editing
|
|
285
|
+
- the source-reading map traces the current evidence trail behind both
|
|
139
286
|
|
|
140
287
|
## Verification checklist
|
|
141
288
|
|
|
142
|
-
After generation:
|
|
289
|
+
After generation or documentation changes:
|
|
143
290
|
|
|
144
|
-
1. confirm the master model contains the `hasMany` relation
|
|
291
|
+
1. confirm the master model contains the expected `hasMany` relation
|
|
145
292
|
2. confirm master service create/view/update/delete includes the detail relation
|
|
146
293
|
3. confirm master create/update/view DTOs include nested detail DTO wiring
|
|
147
|
-
4. confirm the detail
|
|
148
|
-
5. confirm detail
|
|
149
|
-
6.
|
|
294
|
+
4. confirm the nested detail DTO names follow the `detail*` rule
|
|
295
|
+
5. confirm the nested detail DTOs are placed beside the parent DTOs that consume them
|
|
296
|
+
6. for second-level nested detail, confirm the same naming and placement rule repeats with the immediate parent
|
|
297
|
+
7. confirm the detail entity contains the FK field
|
|
298
|
+
8. confirm detail schema/index output includes the FK support
|
|
299
|
+
9. confirm the module `.metadata/index.ts` exports the expected nested detail DTOs
|
|
300
|
+
10. if standalone mode was chosen, confirm the detail module still exposes its standalone resource surface
|
|
@@ -106,6 +106,10 @@ await this.scope.model.order.update(
|
|
|
106
106
|
|
|
107
107
|
This is one of the strongest reasons Cabloy can express CRUD-oriented business flows compactly.
|
|
108
108
|
|
|
109
|
+
If the next question becomes specifically about aggregate-owned detail resources, nested-detail recursion, or nested detail DTO naming/placement, continue with [Master-Detail Workflow](/backend/master-detail-workflow).
|
|
110
|
+
|
|
111
|
+
If the next question becomes how a relation-aware DTO should keep a reusable named field surface instead of only inline relation columns, continue with [DTO Infer and Generation](/backend/dto-infer-generation), especially the `dtoClass` guidance.
|
|
112
|
+
|
|
109
113
|
## `belongsToMany`
|
|
110
114
|
|
|
111
115
|
`belongsToMany` models `n:n` relations through an intermediate model.
|
package/lint-staged.config.mjs
CHANGED
package/oxfmt.config.ts
CHANGED
package/oxlint.config.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.74",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "A Node.js fullstack framework",
|
|
6
6
|
"keywords": [
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"engines": {
|
|
83
83
|
"node": ">=24.4.0"
|
|
84
84
|
},
|
|
85
|
-
"packageManager": "pnpm@11.
|
|
85
|
+
"packageManager": "pnpm@11.5.2"
|
|
86
86
|
}
|
package/scripts/init.ts
CHANGED
|
@@ -115,6 +115,32 @@ function writeVersionMarker(): void {
|
|
|
115
115
|
console.log(`[init] Marked Cabloy version: ${version}`);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function seedVonaZovaRestWorkspaceDependencies(pkgPath: string): void {
|
|
119
|
+
// `package.original.json` intentionally keeps Vona close to a minimal bootstrap state.
|
|
120
|
+
// During `npm run init`, seed the generated `.zova-rest/*` workspace dependencies back into
|
|
121
|
+
// `vona/package.json` before the first install so pnpm sees the `zova -> zova-core`
|
|
122
|
+
// dependency chain and applies the workspace patch instead of reporting it as unused.
|
|
123
|
+
const zovaRestWorkspaceDir = resolve(VONA_DIR, '.zova-rest');
|
|
124
|
+
if (!existsSync(zovaRestWorkspaceDir)) return;
|
|
125
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {
|
|
126
|
+
dependencies?: Record<string, string>;
|
|
127
|
+
};
|
|
128
|
+
pkg.dependencies ??= {};
|
|
129
|
+
let changed = false;
|
|
130
|
+
for (const entry of readdirSync(zovaRestWorkspaceDir, { withFileTypes: true })) {
|
|
131
|
+
if (!entry.isDirectory()) continue;
|
|
132
|
+
const depName = `zova-rest-${entry.name}`;
|
|
133
|
+
const depValue = 'workspace:^';
|
|
134
|
+
if (pkg.dependencies[depName] === depValue) continue;
|
|
135
|
+
pkg.dependencies[depName] = depValue;
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
if (!changed) return;
|
|
139
|
+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
140
|
+
// eslint-disable-next-line
|
|
141
|
+
console.log('[init] Seeded Vona .zova-rest dependencies');
|
|
142
|
+
}
|
|
143
|
+
|
|
118
144
|
// --- Step 0: Set APP_NAME in .env files ---
|
|
119
145
|
|
|
120
146
|
function setAppName(): void {
|
|
@@ -215,6 +241,7 @@ function initVona(): void {
|
|
|
215
241
|
const pkgPath = resolve(VONA_DIR, 'package.json');
|
|
216
242
|
// if (!existsSync(pkgPath)) {
|
|
217
243
|
copyFileSync(resolve(VONA_DIR, 'package.original.json'), pkgPath);
|
|
244
|
+
seedVonaZovaRestWorkspaceDependencies(pkgPath);
|
|
218
245
|
pnpmInstall(VONA_DIR);
|
|
219
246
|
// }
|
|
220
247
|
exec('npm run vona :tools:deps');
|
|
@@ -286,10 +313,10 @@ setAppName();
|
|
|
286
313
|
generateEnvProdLocal();
|
|
287
314
|
generateEnvProdDockerLocal();
|
|
288
315
|
cleanupWorkspaceYaml();
|
|
289
|
-
initVona();
|
|
290
316
|
initZova();
|
|
291
|
-
initCabloyDocs();
|
|
292
317
|
buildSsrCabloyBasicStartBatch();
|
|
318
|
+
initVona();
|
|
319
|
+
initCabloyDocs();
|
|
293
320
|
writeVersionMarker();
|
|
294
321
|
// eslint-disable-next-line
|
|
295
322
|
console.log('[init] Done!');
|
package/vona/oxfmt.config.ts
CHANGED
package/vona/oxlint.config.ts
CHANGED
|
@@ -153,7 +153,7 @@ export class LocalCommon {
|
|
|
153
153
|
}
|
|
154
154
|
_handleDeps('dependencies', deps);
|
|
155
155
|
_handleDeps('devDependencies', depsDev);
|
|
156
|
-
//
|
|
156
|
+
// zova rest workspace
|
|
157
157
|
await this._generatePackageJson_pkgFromZovaRest(projectPath, pkgOriginal.dependencies);
|
|
158
158
|
// save
|
|
159
159
|
if (
|
|
@@ -176,7 +176,7 @@ export class LocalCommon {
|
|
|
176
176
|
const bundles = await globby('*', { cwd: targetDir, onlyDirectories: true });
|
|
177
177
|
for (const bundle of bundles) {
|
|
178
178
|
const name = `zova-rest-${bundle}`;
|
|
179
|
-
devDependencies[name] =
|
|
179
|
+
devDependencies[name] = 'workspace:^';
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -203,7 +203,7 @@ export class LocalCommon {
|
|
|
203
203
|
const isZovaRest = key.includes('zova-rest-');
|
|
204
204
|
const isModule = key.includes('vona-module-') || key.includes('zova-module-');
|
|
205
205
|
const isModuleWorkspace = isModule && version.startsWith('workspace:');
|
|
206
|
-
if (isZovaRest
|
|
206
|
+
if (isZovaRest) continue;
|
|
207
207
|
if (isModuleWorkspace) continue;
|
|
208
208
|
// if (deps[key] && !isModule) continue;
|
|
209
209
|
if (isModule) continue;
|
|
@@ -51,6 +51,7 @@ export class CliBinDev extends BeanCliBase {
|
|
|
51
51
|
cwd: projectPath,
|
|
52
52
|
exec: 'node',
|
|
53
53
|
execArgs: [getImportEsm()],
|
|
54
|
+
ext: 'ts,tsx,json',
|
|
54
55
|
// execArgs: ['--experimental-transform-types', getImportEsm(), '--trace-deprecation'],
|
|
55
56
|
// signal: 'SIGHUP',
|
|
56
57
|
watch: ['packages-utils', 'packages-vona', './src'],
|
|
@@ -20,7 +20,7 @@ export class CliToolsDeps extends BeanCliBase {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async _generate(projectPath: string) {
|
|
23
|
-
//
|
|
23
|
+
// verify zova rest workspace
|
|
24
24
|
const needPnpmInstall = await this._generateZovaRest(projectPath);
|
|
25
25
|
// generate package.json
|
|
26
26
|
const pnpmInstalled = await this.common._generatePackageJson(projectPath);
|
|
@@ -55,29 +55,16 @@ export class CliToolsDeps extends BeanCliBase {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
async _generateZovaRest(projectPath: string) {
|
|
58
|
-
let needPnpmInstall = false;
|
|
59
58
|
const targetDir = path.join(projectPath, '.zova-rest');
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for (const bundle of bundles) {
|
|
65
|
-
const moduleZovaRestSrc = path.join(moduleZovaRest, bundle);
|
|
66
|
-
const moduleZovaRestDest = path.join(targetDir, bundle);
|
|
67
|
-
let needCopy = true;
|
|
68
|
-
if (fse.existsSync(moduleZovaRestDest)) {
|
|
69
|
-
const statDest = await fse.stat(path.join(moduleZovaRestDest, 'package.json'));
|
|
70
|
-
const statSrc = await fse.stat(path.join(moduleZovaRestSrc, 'package.json'));
|
|
71
|
-
// diff: 5s
|
|
72
|
-
if (statDest.mtimeMs + 5000 >= statSrc.mtimeMs) {
|
|
73
|
-
needCopy = false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!needCopy) continue;
|
|
77
|
-
await fse.copy(moduleZovaRestSrc, moduleZovaRestDest, { preserveTimestamps: true });
|
|
78
|
-
needPnpmInstall = true;
|
|
79
|
-
}
|
|
59
|
+
if (!fse.existsSync(targetDir)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
'Zova REST workspace .zova-rest not found. Please run `npm run build:zova:admin` or `npm run build:zova:web` first.',
|
|
62
|
+
);
|
|
80
63
|
}
|
|
81
|
-
|
|
64
|
+
const bundles = await globby('*', { cwd: targetDir, onlyDirectories: true });
|
|
65
|
+
if (bundles.length > 0) return false;
|
|
66
|
+
throw new Error(
|
|
67
|
+
'No Zova REST bundles found in .zova-rest. Please run `npm run build:zova:admin` or `npm run build:zova:web` first.',
|
|
68
|
+
);
|
|
82
69
|
}
|
|
83
70
|
}
|
|
@@ -286,7 +286,7 @@ export { ScopeModule${relativeNameCapitalize} as ScopeModule } from './index.ts'
|
|
|
286
286
|
return pkg;
|
|
287
287
|
}
|
|
288
288
|
// cli
|
|
289
|
-
for (const name of ['cli'
|
|
289
|
+
for (const name of ['cli']) {
|
|
290
290
|
const pathCheck = path.join(modulePath, name);
|
|
291
291
|
if (!(await fse.pathExists(pathCheck))) continue;
|
|
292
292
|
pkg = await _loadPkg();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.61",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
diff --git a/dist/bean/resource/error/errorGlobal.d.ts b/dist/bean/resource/error/errorGlobal.d.ts
|
|
2
|
+
index 73cebe18f3e260c295bdf42a3d1ab9e86fa2de87..cb0ff5c3b541f646105198ee23ac0fc3d805023e 100644
|
|
3
|
+
--- a/dist/bean/resource/error/errorGlobal.d.ts
|
|
4
|
+
+++ b/dist/bean/resource/error/errorGlobal.d.ts
|
|
5
|
+
@@ -1,9 +1 @@
|
|
6
|
+
-import type { TypeScopesErrorCodes } from '../../type.ts';
|
|
7
|
+
-declare global {
|
|
8
|
+
- export interface Error {
|
|
9
|
+
- code?: TypeScopesErrorCodes | number | undefined;
|
|
10
|
+
- status?: number | string | undefined;
|
|
11
|
+
- }
|
|
12
|
+
-}
|
|
13
|
+
export {};
|
|
14
|
+
-//# sourceMappingURL=errorGlobal.d.ts.map
|
|
15
|
+
|
|
16
|
+
diff --git a/dist/types/interface/module.d.ts b/dist/types/interface/module.d.ts
|
|
17
|
+
index 838e299407218b7bfac469d42a12d6ab59820380..e8b61cc231e39ee3a270ee513ab43ff5a13fc290 100644
|
|
18
|
+
--- a/dist/types/interface/module.d.ts
|
|
19
|
+
+++ b/dist/types/interface/module.d.ts
|
|
20
|
+
@@ -23,12 +23,6 @@ export interface IModuleResource {
|
|
21
|
+
components: TypeModuleResourceComponents;
|
|
22
|
+
}
|
|
23
|
+
export declare const SymbolInstalled: unique symbol;
|
|
24
|
+
-declare module '@cabloy/module-info' {
|
|
25
|
+
- interface IModule {
|
|
26
|
+
- resource: IModuleResource;
|
|
27
|
+
- info: IModuleInfo;
|
|
28
|
+
- }
|
|
29
|
+
-}
|
|
30
|
+
export interface IModuleApp {
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=module.d.ts.map
|
|
33
|
+
|
|
34
|
+
diff --git a/dist/types/utils/env.d.ts b/dist/types/utils/env.d.ts
|
|
35
|
+
index 708c0a584dfec65b8c7ad2d049156f900de49928..a8caacb276794e3ac70591b974f14d6eee3ac302 100644
|
|
36
|
+
--- a/dist/types/utils/env.d.ts
|
|
37
|
+
+++ b/dist/types/utils/env.d.ts
|
|
38
|
+
@@ -34,19 +34,4 @@ export interface ZovaConfigEnv {
|
|
39
|
+
MOCK_BUILD: string | undefined;
|
|
40
|
+
MOCK_BUILD_PORT: string | undefined;
|
|
41
|
+
}
|
|
42
|
+
-declare global {
|
|
43
|
+
- namespace NodeJS {
|
|
44
|
+
- interface ProcessEnv {
|
|
45
|
+
- NODE_ENV: ZovaMetaMode;
|
|
46
|
+
- META_FLAVOR: ZovaMetaFlavor;
|
|
47
|
+
- META_MODE: ZovaMetaMode;
|
|
48
|
+
- META_APP_MODE: ZovaMetaAppMode;
|
|
49
|
+
- SSR: boolean;
|
|
50
|
+
- DEV: boolean;
|
|
51
|
+
- PROD: boolean;
|
|
52
|
+
- CLIENT: boolean;
|
|
53
|
+
- SERVER: boolean;
|
|
54
|
+
- }
|
|
55
|
+
- }
|
|
56
|
+
-}
|
|
57
|
+
//# sourceMappingURL=env.d.ts.map
|