cabloy 5.1.70 → 5.1.72

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/cabloy-docs/.vitepress/config.mjs +4 -0
  3. package/cabloy-docs/frontend/api-schema-guide.md +2 -0
  4. package/cabloy-docs/frontend/command-scene-authoring.md +2 -0
  5. package/cabloy-docs/frontend/form-guide.md +4 -0
  6. package/cabloy-docs/frontend/frontend-source-reading-roadmap.md +2 -0
  7. package/cabloy-docs/frontend/schema-driven-field-effects-guide.md +252 -0
  8. package/cabloy-docs/frontend/zova-form-source-reading-map.md +2 -0
  9. package/cabloy-docs/frontend/zova-form-under-the-hood.md +4 -0
  10. package/lint-staged.config.mjs +37 -29
  11. package/package.json +2 -2
  12. package/scripts/run-oxfmt-safe.mjs +53 -0
  13. package/vona/packages-cli/cabloy-cli/package.json +1 -1
  14. package/vona/packages-cli/cli/package.json +1 -1
  15. package/vona/packages-cli/cli-set-api/package.json +1 -1
  16. package/vona/packages-utils/module-glob/package.json +1 -1
  17. package/vona/packages-utils/utils/package.json +1 -1
  18. package/vona/packages-utils/utils/src/celjs/base.ts +17 -6
  19. package/vona/packages-vona/vona/package.json +1 -1
  20. package/vona/packages-vona/vona-core/package.json +1 -1
  21. package/vona/packages-vona/vona-mock/package.json +1 -1
  22. package/vona/pnpm-lock.yaml +139 -3
  23. package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +2 -0
  24. package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +2 -0
  25. package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +57 -14
  26. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  27. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  28. package/zova/packages-cli/cli/package.json +3 -3
  29. package/zova/packages-cli/cli-set-front/package.json +4 -4
  30. package/zova/packages-utils/zova-jsx/package.json +3 -3
  31. package/zova/packages-utils/zova-vite/package.json +2 -2
  32. package/zova/packages-zova/zova/package.json +3 -3
  33. package/zova/packages-zova/zova-core/package.json +2 -2
  34. package/zova/pnpm-lock.yaml +46 -46
  35. package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
  36. package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx +3 -0
  37. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  38. package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +4 -1
  39. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +4 -4
  40. package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.72
4
+
5
+ ### Features
6
+
7
+ - Update training record subject effects.
8
+ - Update related functionality.
9
+
10
+ ## 5.1.71
11
+
12
+ ### Features
13
+
14
+ - Improve CEL default value handling.
15
+ - Update published functionality.
16
+
17
+ ### Improvements
18
+
19
+ - Harden ignored-file handling in lint-staged.
20
+ - Normalize `commandssync` metadata formatting.
21
+ - Add a schema-driven field effects guide.
22
+
3
23
  ## 5.1.70
4
24
 
5
25
  ### 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',
@@ -45,6 +45,8 @@ In the shared form/table CEL environment:
45
45
 
46
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
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
+
48
50
  ## Read together with
49
51
 
50
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.
@@ -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
@@ -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
@@ -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 parts = filepath.split('/');
45
- const basename = parts.at(-1);
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
- if (matchGlob(basename, pattern)) return true;
71
+ return matchesGlob(filename, pattern);
50
72
  }
51
- // **/prefix patterns
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 matchGlob(name, pattern) {
70
- const re = pattern
71
- .replace(/[.+^${}()|[\]\\]/g, '\\$&')
72
- .replace(/\*/g, '[^/]*')
73
- .replace(/\?/g, '[^/]');
74
- return new RegExp(`^${re}$`).test(name);
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 filterIgnored(filenames) {
78
- return filenames.filter(f => !isOxfmtIgnored(f));
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', `npm run format:fix -- ${filtered.join(' ')}`];
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 [`npm run format:fix -- ${filtered.join(' ')}`];
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.70",
3
+ "version": "5.1.72",
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.5.2"
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);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/cli",
3
- "version": "3.1.24",
3
+ "version": "3.1.26",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "@cabloy/cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli",
3
- "version": "1.1.121",
3
+ "version": "1.1.123",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.119",
3
+ "version": "1.1.121",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/module-glob",
3
- "version": "5.3.14",
3
+ "version": "5.3.16",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "cabloy module-glob",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/utils",
3
- "version": "2.1.23",
3
+ "version": "2.1.25",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "cabloy utils",
6
6
  "keywords": [
@@ -21,6 +21,9 @@ celEnvBase.registerFunction('join(list):string', list => {
21
21
  celEnvBase.registerFunction('join(list,string):string', (list, sep) => {
22
22
  return _join(list, sep);
23
23
  });
24
+ celEnvBase.registerFunction('sum(list):int', list => {
25
+ return _sum(list);
26
+ });
24
27
 
25
28
  // string
26
29
  celEnvBase.registerFunction('string(null):string', value => {
@@ -50,20 +53,20 @@ celEnvBase.registerOperator('bool == null', (b, n) => b === n);
50
53
  celEnvBase.registerFunction('get(map,string):dyn', (obj, name) => {
51
54
  return getProperty(obj, name) ?? null;
52
55
  });
53
- celEnvBase.registerFunction('get(map,string,string):dyn', (obj, name, sep) => {
54
- return getProperty(obj, name, sep) ?? null;
56
+ celEnvBase.registerFunction('get(map,string,dyn):dyn', (obj, name, defaultValue) => {
57
+ return getProperty(obj, name) ?? defaultValue;
55
58
  });
56
59
  celEnvBase.registerFunction('get(bool,string):dyn', (_obj, _name) => {
57
60
  return null;
58
61
  });
59
- celEnvBase.registerFunction('get(bool,string,string):dyn', (_obj, _name, _sep) => {
60
- return null;
62
+ celEnvBase.registerFunction('get(bool,string,dyn):dyn', (_obj, _name, defaultValue) => {
63
+ return defaultValue;
61
64
  });
62
65
  celEnvBase.registerFunction('get(null,string):dyn', (_obj, _name) => {
63
66
  return null;
64
67
  });
65
- celEnvBase.registerFunction('get(null,string,string):dyn', (_obj, _name, _sep) => {
66
- return null;
68
+ celEnvBase.registerFunction('get(null,string,dyn):dyn', (_obj, _name, defaultValue) => {
69
+ return defaultValue;
67
70
  });
68
71
 
69
72
  celEnvBase.registerFunction('exists(null,string):bool', (obj, name) => {
@@ -90,6 +93,14 @@ function _join(list?: [], sep?: string): string {
90
93
  return list.join(sep);
91
94
  }
92
95
 
96
+ function _sum(list?: []): bigint {
97
+ if (!list || list.length === 0) return 0n;
98
+ return list.reduce((acc, item) => {
99
+ if (item === null || item === undefined) return acc;
100
+ return acc + BigInt(item as number | string | bigint);
101
+ }, 0n);
102
+ }
103
+
93
104
  function _toFixed(value: number | bigint, precision: number | bigint): string {
94
105
  const precisionInt = Number(precision);
95
106
  if (typeof value === 'bigint') {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.58",
3
+ "version": "5.1.60",
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": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-core",
3
- "version": "5.1.26",
3
+ "version": "5.1.28",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-mock",
3
- "version": "6.1.26",
3
+ "version": "6.1.28",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona mock",
6
6
  "keywords": [