cabloy 5.1.69 → 5.1.71
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/CHANGELOG.md +24 -0
- package/cabloy-docs/.vitepress/config.mjs +4 -0
- package/cabloy-docs/frontend/api-schema-guide.md +20 -0
- package/cabloy-docs/frontend/command-scene-authoring.md +2 -0
- package/cabloy-docs/frontend/form-guide.md +12 -0
- package/cabloy-docs/frontend/frontend-source-reading-roadmap.md +2 -0
- package/cabloy-docs/frontend/schema-driven-field-effects-guide.md +252 -0
- package/cabloy-docs/frontend/table-guide.md +8 -0
- package/cabloy-docs/frontend/zova-form-source-reading-map.md +2 -0
- package/cabloy-docs/frontend/zova-form-under-the-hood.md +4 -0
- package/lint-staged.config.mjs +37 -29
- package/package.json +2 -2
- package/scripts/run-oxfmt-safe.mjs +53 -0
- package/vona/packages-cli/cabloy-cli/package.json +1 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-utils/module-glob/package.json +1 -1
- package/vona/packages-utils/utils/package.json +1 -1
- package/vona/packages-utils/utils/src/celjs/base.ts +25 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +40 -40
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +44 -3
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/package.json +4 -4
- package/zova/packages-utils/zova-jsx/package.json +3 -3
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +2 -2
- package/zova/pnpm-lock.yaml +46 -46
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx +3 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +10 -8
- 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 +4 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +4 -4
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.71
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Improve CEL default value handling.
|
|
8
|
+
- Update published functionality.
|
|
9
|
+
|
|
10
|
+
### Improvements
|
|
11
|
+
|
|
12
|
+
- Harden ignored-file handling in lint-staged.
|
|
13
|
+
- Normalize `commandssync` metadata formatting.
|
|
14
|
+
- Add a schema-driven field effects guide.
|
|
15
|
+
|
|
16
|
+
## 5.1.70
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
- Improve CEL numeric formatting for training records.
|
|
21
|
+
- Trigger `onEffect` callbacks for form fields.
|
|
22
|
+
|
|
23
|
+
### Improvements
|
|
24
|
+
|
|
25
|
+
- Add examples for CEL numeric formatting to the documentation.
|
|
26
|
+
|
|
3
27
|
## 5.1.69
|
|
4
28
|
|
|
5
29
|
### Features
|
|
@@ -422,6 +422,10 @@ export default defineConfig({
|
|
|
422
422
|
text: 'Form Scene to Page Meta',
|
|
423
423
|
link: '/frontend/form-scene-to-page-meta-guide',
|
|
424
424
|
},
|
|
425
|
+
{
|
|
426
|
+
text: 'Schema-Driven Field Effects',
|
|
427
|
+
link: '/frontend/schema-driven-field-effects-guide',
|
|
428
|
+
},
|
|
425
429
|
{
|
|
426
430
|
text: 'Permission, formScene, and Action Visibility',
|
|
427
431
|
link: '/frontend/permission-formscene-action-visibility-guide',
|
|
@@ -27,6 +27,26 @@ Use `$apiSchema` when the frontend needs to inspect what the backend contract sa
|
|
|
27
27
|
|
|
28
28
|
That usually means the problem is shifting from “fetch data” to “use metadata to drive behavior.”
|
|
29
29
|
|
|
30
|
+
## One practical metadata-driven expression example
|
|
31
|
+
|
|
32
|
+
When schema-driven form or table rendering uses JSX/CEL evaluation, the runtime can resolve helper functions against the current scope.
|
|
33
|
+
|
|
34
|
+
For example, frontend CEL expressions can now use `toFixed(value, precision)` to keep a numeric value at the desired precision:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
toFixed(getValue('price'), 2)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
In the shared form/table CEL environment:
|
|
41
|
+
|
|
42
|
+
- `toFixed(...)` returns a string with fixed decimal precision
|
|
43
|
+
- `getValue(name)` reads the current field value or current row value from the active runtime scope
|
|
44
|
+
- `getProperty(name)` reads the current schema property metadata from the active runtime scope
|
|
45
|
+
|
|
46
|
+
That is useful when the backend contract already owns the field metadata and the frontend only needs a thin expression layer for schema-driven display behavior.
|
|
47
|
+
|
|
48
|
+
If your next question is not only how expressions read schema-driven scope, but how backend-owned field metadata attaches live field-side behavior through `ZovaRender.onEffect(...)`, `ZovaEvent`, and command chains, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
49
|
+
|
|
30
50
|
## Read together with
|
|
31
51
|
|
|
32
52
|
Use this page together with:
|
|
@@ -438,6 +438,8 @@ Why it matters:
|
|
|
438
438
|
|
|
439
439
|
This is the clearest example that command beans can be scene-sensitive without fitting the resource-row patterns.
|
|
440
440
|
|
|
441
|
+
If your next question is not only what this command bean does, but how backend field metadata, `ZovaRender.onEffect(...)`, `ZovaEvent`, and command chains cooperate to implement schema-driven form effects, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
442
|
+
|
|
441
443
|
### `basic-commandssync:log`
|
|
442
444
|
|
|
443
445
|
Read:
|
|
@@ -34,6 +34,8 @@ If your next question is how these public APIs cooperate internally at runtime,
|
|
|
34
34
|
|
|
35
35
|
If your next question is how `formScene` becomes `formMeta`, then `pageMeta`, and finally visible shell/tab state, continue with [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide).
|
|
36
36
|
|
|
37
|
+
If your next question is how backend/entity schema metadata drives field-level reactive form behavior such as normalization, derived values, and field-to-field updates, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
38
|
+
|
|
37
39
|
## What you should learn first
|
|
38
40
|
|
|
39
41
|
If you only remember one idea, remember this one:
|
|
@@ -64,6 +66,8 @@ That Student thread is a good specimen because it grows through the same path mo
|
|
|
64
66
|
|
|
65
67
|
So as you read the code samples below, treat them as different stages of the same Student form rather than unrelated fragments.
|
|
66
68
|
|
|
69
|
+
If your next question is not only how a schema-driven form renders, but how backend/entity field metadata can attach live field-to-field behavior such as normalization and derived-value updates, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
70
|
+
|
|
67
71
|
## Step 1: Choose the right form style
|
|
68
72
|
|
|
69
73
|
Before writing code, choose which of these three styles matches your Student page.
|
|
@@ -308,6 +312,14 @@ Schema-driven rendering is a strong fit when:
|
|
|
308
312
|
- frontend and backend should stay close to the same Student contract truth
|
|
309
313
|
- you want to reduce duplicated Student field configuration
|
|
310
314
|
|
|
315
|
+
A practical expression example is a schema-driven field display that formats a numeric value through CEL:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
toFixed(getValue('price'), 2)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
In the shared form CEL scope, `getValue(name)` reads the current field value and `toFixed(...)` returns a string with fixed decimal precision.
|
|
322
|
+
|
|
311
323
|
Read together with [API Schema Guide](/frontend/api-schema-guide).
|
|
312
324
|
|
|
313
325
|
## Step 6: Add validation
|
|
@@ -116,11 +116,13 @@ Start here when your question is about `ZForm`, `formMeta`, page-entry forms, or
|
|
|
116
116
|
### Focused deep dives
|
|
117
117
|
|
|
118
118
|
- [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide)
|
|
119
|
+
- [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide)
|
|
119
120
|
|
|
120
121
|
### Best next step
|
|
121
122
|
|
|
122
123
|
- if the question is “how does form runtime work?” -> read [Zova Form Under the Hood](/frontend/zova-form-under-the-hood)
|
|
123
124
|
- if the question is “how does `formScene` become shell-visible state?” -> read [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide)
|
|
125
|
+
- if the question is “how does backend/schema metadata drive field-level reactive behavior?” -> read [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide)
|
|
124
126
|
|
|
125
127
|
## Topic cluster: resources and ModelResource
|
|
126
128
|
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Schema-Driven Field Effects Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how schema-driven field effects work in current Cabloy Basic when backend-owned field metadata drives live frontend form behavior.
|
|
4
|
+
|
|
5
|
+
Use this page when you want to understand questions such as:
|
|
6
|
+
|
|
7
|
+
- how can a backend entity field drive frontend form behavior
|
|
8
|
+
- what does `ZovaRender.onEffect(...)` actually attach to
|
|
9
|
+
- how do `ZovaEvent` and `ZovaCommand` cooperate to update other fields
|
|
10
|
+
- when should a derived-field rule live in schema metadata instead of page/controller logic
|
|
11
|
+
- which files should I read first for schema-driven field reactivity
|
|
12
|
+
|
|
13
|
+
Use this page together with:
|
|
14
|
+
|
|
15
|
+
- [Form Guide](/frontend/form-guide)
|
|
16
|
+
- [Zova Form Under the Hood](/frontend/zova-form-under-the-hood)
|
|
17
|
+
- [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map)
|
|
18
|
+
- [API Schema Guide](/frontend/api-schema-guide)
|
|
19
|
+
- [Command Scene Authoring](/frontend/command-scene-authoring)
|
|
20
|
+
|
|
21
|
+
Use this page after [Form Guide](/frontend/form-guide) when your next question is not only how schema-driven forms render, but how backend-owned field metadata becomes live field-side behavior through `ZovaRender.onEffect(...)`, `ZovaEvent`, and command chains.
|
|
22
|
+
|
|
23
|
+
## Why this page exists
|
|
24
|
+
|
|
25
|
+
The current frontend docs already explain nearby pieces well:
|
|
26
|
+
|
|
27
|
+
- [Form Guide](/frontend/form-guide) explains the public form authoring surface
|
|
28
|
+
- [Zova Form Under the Hood](/frontend/zova-form-under-the-hood) explains the runtime cooperation among form controllers, field controllers, schema metadata, and behaviors
|
|
29
|
+
- [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map) explains which framework files to read next
|
|
30
|
+
- [Command Scene Authoring](/frontend/command-scene-authoring) explains the built-in `command` scene and representative command beans
|
|
31
|
+
- [API Schema Guide](/frontend/api-schema-guide) explains why schema metadata can drive frontend behavior
|
|
32
|
+
|
|
33
|
+
What those pages do not isolate directly is one common cross-layer pattern:
|
|
34
|
+
|
|
35
|
+
- backend entity metadata contributes frontend render metadata
|
|
36
|
+
- field metadata attaches a field-side effect
|
|
37
|
+
- the effect emits a declarative event/command chain
|
|
38
|
+
- command beans write the result back into form state
|
|
39
|
+
|
|
40
|
+
That is the gap this page fills.
|
|
41
|
+
|
|
42
|
+
## The shortest accurate mental model
|
|
43
|
+
|
|
44
|
+
A practical mental model is:
|
|
45
|
+
|
|
46
|
+
1. backend entity or DTO schema owns the field metadata truth
|
|
47
|
+
2. `ZovaRender.onEffect(...)` adds field-side frontend behavior to that metadata
|
|
48
|
+
3. the form runtime turns that metadata into a live field render context
|
|
49
|
+
4. a `ZovaEvent` executes one or more `ZovaCommand` nodes declaratively
|
|
50
|
+
5. those commands read current field scope and write normalized or derived values back into the form
|
|
51
|
+
|
|
52
|
+
That means a field effect in Zova is **schema-owned frontend behavior**, not only a page-local callback or ad hoc widget trick.
|
|
53
|
+
|
|
54
|
+
## What this page is not
|
|
55
|
+
|
|
56
|
+
This page is not:
|
|
57
|
+
|
|
58
|
+
- a replacement for [Form Guide](/frontend/form-guide)
|
|
59
|
+
- a full command-scene guide instead of [Command Scene Authoring](/frontend/command-scene-authoring)
|
|
60
|
+
- a general page-workflow guide for routing, dialogs, submit flow, or page lifecycle orchestration
|
|
61
|
+
- the separate `formScene -> formMeta -> pageMeta -> shell/tab state` bridge covered by [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide)
|
|
62
|
+
|
|
63
|
+
Its job is narrower:
|
|
64
|
+
|
|
65
|
+
- explain how schema-owned field metadata becomes live field-side behavior
|
|
66
|
+
- clarify where `ZovaRender.onEffect(...)`, `ZovaEvent`, `ZovaCommand`, and form runtime each fit
|
|
67
|
+
- give a short source-confirmed reading path for this pattern
|
|
68
|
+
|
|
69
|
+
## A representative specimen
|
|
70
|
+
|
|
71
|
+
A compact specimen lives in:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
vona/src/suite/a-training/modules/training-record/src/entity/record.tsx
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The key pattern in that file is:
|
|
78
|
+
|
|
79
|
+
- one shared `onEffectForAverageScore` event chain is declared once
|
|
80
|
+
- `subjectCount` and `totalScore` both attach that same effect through `ZovaRender.onEffect(...)`
|
|
81
|
+
- the effect first normalizes input through `basic-commandssync:expr`
|
|
82
|
+
- the effect then writes the derived `averageScore` through `basic-commands:setValue`
|
|
83
|
+
|
|
84
|
+
This specimen matters because it demonstrates three durable rules:
|
|
85
|
+
|
|
86
|
+
1. one field effect can be reused by multiple driving fields
|
|
87
|
+
2. normalization can happen before the derived-field writeback
|
|
88
|
+
3. the derived-field rule stays declarative and schema-owned rather than being buried in one page-local method
|
|
89
|
+
|
|
90
|
+
The business example is average score, but the reusable pattern is broader:
|
|
91
|
+
|
|
92
|
+
- normalize one or more source fields
|
|
93
|
+
- compute a small derived value
|
|
94
|
+
- write that value back into form state through commands
|
|
95
|
+
|
|
96
|
+
## Where each concept lives
|
|
97
|
+
|
|
98
|
+
This topic becomes much easier to read once the layers are separated clearly.
|
|
99
|
+
|
|
100
|
+
### 1. Entity or DTO field metadata owns schema truth
|
|
101
|
+
|
|
102
|
+
In current Cabloy Basic authoring, backend-side schema definitions can carry frontend render metadata through `@Api.field(...)` and related helpers.
|
|
103
|
+
|
|
104
|
+
That metadata is not only validation truth.
|
|
105
|
+
It can also describe:
|
|
106
|
+
|
|
107
|
+
- field order
|
|
108
|
+
- render provider selection
|
|
109
|
+
- scene-specific behavior
|
|
110
|
+
- field-side effects
|
|
111
|
+
|
|
112
|
+
For the surrounding metadata-driven model, also see [API Schema Guide](/frontend/api-schema-guide).
|
|
113
|
+
|
|
114
|
+
### 2. `ZovaRender.onEffect(...)` attaches field-side behavior
|
|
115
|
+
|
|
116
|
+
`ZovaRender.onEffect(...)` belongs on field render metadata.
|
|
117
|
+
|
|
118
|
+
Its role is not to define a command bean and not to replace a page controller.
|
|
119
|
+
Its role is to say:
|
|
120
|
+
|
|
121
|
+
- when this field participates in runtime interaction
|
|
122
|
+
- execute this declarative effect chain in the field runtime context
|
|
123
|
+
|
|
124
|
+
That keeps the effect attached to the schema-owned field definition instead of scattering it across unrelated page code.
|
|
125
|
+
|
|
126
|
+
### 3. `ZovaEvent` is the declarative event container
|
|
127
|
+
|
|
128
|
+
`ZovaEvent` groups one or more command invocations into a small event pipeline.
|
|
129
|
+
|
|
130
|
+
In this pattern, the important point is not JSX syntax by itself.
|
|
131
|
+
The important point is that the field effect stays:
|
|
132
|
+
|
|
133
|
+
- declarative
|
|
134
|
+
- composable
|
|
135
|
+
- command-oriented
|
|
136
|
+
- readable as metadata-driven behavior rather than imperative widget code
|
|
137
|
+
|
|
138
|
+
### 4. `ZovaCommand` nodes perform the concrete steps
|
|
139
|
+
|
|
140
|
+
A `ZovaCommand` node names one concrete action.
|
|
141
|
+
In the average-score specimen, two built-in command families matter:
|
|
142
|
+
|
|
143
|
+
- `basic-commandssync:expr`
|
|
144
|
+
- `basic-commands:setValue`
|
|
145
|
+
|
|
146
|
+
`basic-commandssync:expr` is a small synchronous command bean that returns the evaluated expression result.
|
|
147
|
+
A representative source file is:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
zova/src/suite/cabloy-basic/modules/basic-commandssync/src/bean/command.expr.tsx
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`basic-commands:setValue` is the built-in form-field writeback command bean.
|
|
154
|
+
A representative source file is:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.setValue.tsx
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
That `setValue` command is especially important because it checks `renderContext.$scene === 'formField'` and then updates the target field through the live form runtime.
|
|
161
|
+
For the command-scene side of that story, see [Command Scene Authoring](/frontend/command-scene-authoring).
|
|
162
|
+
|
|
163
|
+
### 5. The form runtime provides the live field scope
|
|
164
|
+
|
|
165
|
+
The field effect does not run against a dead schema object.
|
|
166
|
+
It runs inside the form runtime, where the field and form controllers provide live helpers such as current field scope and form-state mutation.
|
|
167
|
+
|
|
168
|
+
That is why a schema-owned effect can still read current values and write derived values back into the active form.
|
|
169
|
+
For the runtime side, see [Zova Form Under the Hood](/frontend/zova-form-under-the-hood) and [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map).
|
|
170
|
+
|
|
171
|
+
## When this pattern fits well
|
|
172
|
+
|
|
173
|
+
This pattern fits best when the behavior is:
|
|
174
|
+
|
|
175
|
+
- field-local or cross-field within one form
|
|
176
|
+
- small enough to stay declarative
|
|
177
|
+
- closely tied to schema-owned field semantics
|
|
178
|
+
- useful across more than one page or runtime path that consumes the same schema truth
|
|
179
|
+
|
|
180
|
+
Typical good fits include:
|
|
181
|
+
|
|
182
|
+
- derived fields such as totals, labels, or averages
|
|
183
|
+
- lightweight input normalization
|
|
184
|
+
- small field-to-field synchronization rules
|
|
185
|
+
- metadata-driven behavior that should travel with the backend contract
|
|
186
|
+
|
|
187
|
+
## When page or controller logic fits better
|
|
188
|
+
|
|
189
|
+
Do not force every interactive rule into field metadata.
|
|
190
|
+
|
|
191
|
+
Prefer page/controller logic when the behavior is:
|
|
192
|
+
|
|
193
|
+
- strongly tied to one page workflow
|
|
194
|
+
- asynchronous or orchestration-heavy
|
|
195
|
+
- dependent on routing, dialogs, or page lifecycle
|
|
196
|
+
- too large or too stateful to stay readable as a field event chain
|
|
197
|
+
|
|
198
|
+
A practical boundary is:
|
|
199
|
+
|
|
200
|
+
- if the rule reads like a small schema-owned field behavior, prefer field effects
|
|
201
|
+
- if the rule reads like a page workflow, prefer page/controller code
|
|
202
|
+
|
|
203
|
+
## Source-confirmed reading path
|
|
204
|
+
|
|
205
|
+
When reading this topic, use this order:
|
|
206
|
+
|
|
207
|
+
1. `vona/src/suite/a-training/modules/training-record/src/entity/record.tsx`
|
|
208
|
+
2. `zova/src/suite/cabloy-basic/modules/basic-commandssync/src/bean/command.expr.tsx`
|
|
209
|
+
3. `zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.setValue.tsx`
|
|
210
|
+
4. `zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx`
|
|
211
|
+
5. `zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx`
|
|
212
|
+
6. `zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts`
|
|
213
|
+
|
|
214
|
+
That order moves from one business-facing specimen, to the effect commands themselves, to the form and field runtime that make the effect live.
|
|
215
|
+
|
|
216
|
+
## Relationship to other guides
|
|
217
|
+
|
|
218
|
+
Use these boundaries when choosing the next page:
|
|
219
|
+
|
|
220
|
+
- if you want public form authoring first, read [Form Guide](/frontend/form-guide)
|
|
221
|
+
- if you want general schema-driven metadata behavior, read [API Schema Guide](/frontend/api-schema-guide)
|
|
222
|
+
- if you want deeper form runtime cooperation, read [Zova Form Under the Hood](/frontend/zova-form-under-the-hood)
|
|
223
|
+
- if you want the shortest file-order map for form internals, read [Zova Form Source Reading Map](/frontend/zova-form-source-reading-map)
|
|
224
|
+
- if you want the command-scene runtime model behind named commands, read [Command Scene Authoring](/frontend/command-scene-authoring)
|
|
225
|
+
- if you want the separate `formScene -> formMeta -> pageMeta -> shell/tab state` bridge, read [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide)
|
|
226
|
+
|
|
227
|
+
## Final takeaway
|
|
228
|
+
|
|
229
|
+
The most accurate way to read schema-driven field effects in current Cabloy Basic is:
|
|
230
|
+
|
|
231
|
+
- schema metadata owns the field-level behavior contract
|
|
232
|
+
- `ZovaRender.onEffect(...)` attaches declarative field-side behavior
|
|
233
|
+
- `ZovaEvent` and `ZovaCommand` execute the effect as a small command pipeline
|
|
234
|
+
- form runtime scope makes the effect live
|
|
235
|
+
- command beans write normalized or derived values back into the active form state
|
|
236
|
+
|
|
237
|
+
That is the source-confirmed meaning of schema-driven field effects in the current frontend architecture.
|
|
238
|
+
|
|
239
|
+
## Verification checklist
|
|
240
|
+
|
|
241
|
+
When documenting or changing this area, verify in this order:
|
|
242
|
+
|
|
243
|
+
1. confirm the specimen still matches the current `training-record` entity metadata
|
|
244
|
+
2. confirm command claims still match `command.expr.tsx` and `command.setValue.tsx`
|
|
245
|
+
3. confirm the runtime boundary still matches the current `a-form` form and form-field controllers
|
|
246
|
+
4. build the docs site:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
npm run docs:build
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
5. verify the page is reachable from the frontend sidebar and related form/command guides
|
|
@@ -146,6 +146,14 @@ and more:
|
|
|
146
146
|
|
|
147
147
|
This is why table work often belongs in the broader contract loop when the real source of truth is backend field metadata.
|
|
148
148
|
|
|
149
|
+
A practical expression example is a schema-driven cell display that formats the current row value through CEL:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
toFixed(getValue('price'), 2)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
In the shared table CEL scope, `getValue(name)` reads the current row value and `toFixed(...)` returns a string with fixed decimal precision.
|
|
156
|
+
|
|
149
157
|
For the schema side of that contract, also see [API Schema Guide](/frontend/api-schema-guide).
|
|
150
158
|
|
|
151
159
|
## Step 4: Use built-in or custom `tableCell` render resources
|
|
@@ -249,6 +249,8 @@ Use this path when you are asking questions like:
|
|
|
249
249
|
|
|
250
250
|
If your next question becomes how `formScene` becomes `formMeta`, then `pageMeta`, and finally visible shell/tab state, continue with [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide).
|
|
251
251
|
|
|
252
|
+
If your next question becomes how backend/entity schema metadata attaches field-side effects through `ZovaRender.onEffect(...)` and command chains, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
253
|
+
|
|
252
254
|
## 8. Representative specimens to read before editing the framework
|
|
253
255
|
|
|
254
256
|
Use this section when you want one small example before reading the framework internals.
|
|
@@ -18,6 +18,8 @@ If your next question is not “how does this runtime work?” but “which file
|
|
|
18
18
|
|
|
19
19
|
If your next question is specifically how `formScene` flows into `formMeta`, then `pageMeta`, and finally shell/tab state, continue with [Form Scene to Page Meta Guide](/frontend/form-scene-to-page-meta-guide).
|
|
20
20
|
|
|
21
|
+
If your next question is how backend/entity schema metadata attaches live field-side behavior through `ZovaRender.onEffect(...)`, `ZovaEvent`, and command chains, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
22
|
+
|
|
21
23
|
> [!TIP]
|
|
22
24
|
> **Zova Form docs path**
|
|
23
25
|
>
|
|
@@ -94,6 +96,8 @@ That page shows:
|
|
|
94
96
|
- a provider-level layout behavior override
|
|
95
97
|
- a blank row used for action controls
|
|
96
98
|
|
|
99
|
+
If you want a business-facing specimen where backend entity metadata attaches live field-side behavior through `ZovaRender.onEffect(...)`, `ZovaEvent`, and command chains, continue with [Schema-Driven Field Effects Guide](/frontend/schema-driven-field-effects-guide).
|
|
100
|
+
|
|
97
101
|
The rest of this page explains how those public authoring shapes become real runtime behavior.
|
|
98
102
|
|
|
99
103
|
## The core source-reading path
|
package/lint-staged.config.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { basename, isAbsolute, matchesGlob, relative, sep } from 'node:path';
|
|
2
|
+
|
|
1
3
|
// Must stay in sync with oxfmt.config.ts ignorePatterns
|
|
2
4
|
const OXFMT_IGNORE_PATTERNS = [
|
|
3
5
|
// root original
|
|
@@ -40,53 +42,59 @@ const OXFMT_IGNORE_PATTERNS = [
|
|
|
40
42
|
'zova/packages-cli/cli-set-front/cli/templates',
|
|
41
43
|
];
|
|
42
44
|
|
|
45
|
+
function normalizeFilepath(filepath) {
|
|
46
|
+
const normalized = isAbsolute(filepath) ? relative(process.cwd(), filepath) : filepath;
|
|
47
|
+
return normalized.split(sep).join('/');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function matchesPathPattern(filepath, pattern) {
|
|
51
|
+
if (matchesGlob(filepath, pattern)) return true;
|
|
52
|
+
if (pattern.startsWith('**/')) {
|
|
53
|
+
const suffix = pattern.slice(3);
|
|
54
|
+
if (!/[*?]/.test(suffix)) {
|
|
55
|
+
return (
|
|
56
|
+
filepath === suffix || filepath.endsWith(`/${suffix}`) || filepath.includes(`/${suffix}/`)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!/[*?]/.test(pattern)) {
|
|
61
|
+
return filepath === pattern || filepath.startsWith(`${pattern}/`);
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
43
66
|
function isOxfmtIgnored(filepath) {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
67
|
+
const normalized = normalizeFilepath(filepath);
|
|
68
|
+
const filename = basename(normalized);
|
|
46
69
|
return OXFMT_IGNORE_PATTERNS.some(pattern => {
|
|
47
|
-
// basename-only patterns (e.g. *.min.js)
|
|
48
70
|
if (!pattern.includes('/')) {
|
|
49
|
-
|
|
71
|
+
return matchesGlob(filename, pattern);
|
|
50
72
|
}
|
|
51
|
-
|
|
52
|
-
if (pattern.startsWith('**/')) {
|
|
53
|
-
const suffix = pattern.slice(3);
|
|
54
|
-
for (let i = 0; i < parts.length; i++) {
|
|
55
|
-
if (matchGlob(parts[i], suffix)) return true;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// **/dir/** patterns
|
|
59
|
-
if (pattern.startsWith('**/') && pattern.endsWith('/**')) {
|
|
60
|
-
const dir = pattern.slice(3, -3);
|
|
61
|
-
if (parts.includes(dir)) return true;
|
|
62
|
-
}
|
|
63
|
-
// prefix/path patterns (no **)
|
|
64
|
-
if (!pattern.startsWith('**') && filepath.includes(pattern)) return true;
|
|
65
|
-
return false;
|
|
73
|
+
return matchesPathPattern(normalized, pattern);
|
|
66
74
|
});
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return
|
|
77
|
+
function filterIgnored(filenames) {
|
|
78
|
+
return filenames.filter(filename => !isOxfmtIgnored(filename));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function joinShellArgs(filenames) {
|
|
82
|
+
return filenames.map(filename => JSON.stringify(filename)).join(' ');
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
function
|
|
78
|
-
return
|
|
85
|
+
function createOxfmtCommand(filenames) {
|
|
86
|
+
return `node scripts/run-oxfmt-safe.mjs ${joinShellArgs(filenames)}`;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
export default {
|
|
82
90
|
'*.{js,jsx,ts,tsx,vue,mjs,cjs}': filenames => {
|
|
83
91
|
const filtered = filterIgnored(filenames);
|
|
84
92
|
if (filtered.length === 0) return [];
|
|
85
|
-
return ['npm run lint:fix',
|
|
93
|
+
return ['npm run lint:fix', createOxfmtCommand(filtered)];
|
|
86
94
|
},
|
|
87
95
|
'*.{json,yaml,yml,md,css,scss,html}': filenames => {
|
|
88
96
|
const filtered = filterIgnored(filenames);
|
|
89
97
|
if (filtered.length === 0) return [];
|
|
90
|
-
return [
|
|
98
|
+
return [createOxfmtCommand(filtered)];
|
|
91
99
|
},
|
|
92
100
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.71",
|
|
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.9.0"
|
|
86
86
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
const NO_TARGETS_MESSAGE =
|
|
4
|
+
'Expected at least one target file. All matched files may have been excluded by ignore rules.';
|
|
5
|
+
|
|
6
|
+
function stripNoTargetsMessage(output) {
|
|
7
|
+
return output
|
|
8
|
+
.split('\n')
|
|
9
|
+
.filter(line => !line.includes(NO_TARGETS_MESSAGE))
|
|
10
|
+
.join('\n')
|
|
11
|
+
.trimEnd();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function writeOutput(stream, output) {
|
|
15
|
+
if (output) {
|
|
16
|
+
stream.write(output);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const filenames = process.argv.slice(2);
|
|
21
|
+
if (filenames.length === 0) {
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const result = spawnSync('pnpm', ['exec', 'oxfmt', '--write', ...filenames], {
|
|
26
|
+
encoding: 'utf8',
|
|
27
|
+
stdio: 'pipe',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (result.error) {
|
|
31
|
+
throw result.error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const stdout = result.stdout ?? '';
|
|
35
|
+
const stderr = result.stderr ?? '';
|
|
36
|
+
if (result.status === 0) {
|
|
37
|
+
writeOutput(process.stdout, stdout);
|
|
38
|
+
writeOutput(process.stderr, stderr);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const combinedOutput = `${stdout}\n${stderr}`;
|
|
43
|
+
if (combinedOutput.includes(NO_TARGETS_MESSAGE)) {
|
|
44
|
+
const cleanStdout = stripNoTargetsMessage(stdout);
|
|
45
|
+
const cleanStderr = stripNoTargetsMessage(stderr);
|
|
46
|
+
writeOutput(process.stdout, cleanStdout ? `${cleanStdout}\n` : '');
|
|
47
|
+
writeOutput(process.stderr, cleanStderr ? `${cleanStderr}\n` : '');
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
writeOutput(process.stdout, stdout);
|
|
52
|
+
writeOutput(process.stderr, stderr);
|
|
53
|
+
process.exit(result.status ?? 1);
|