cabloy 5.1.212 → 5.1.214

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 (47) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/package.json +1 -1
  4. package/repo-agent-governance/managed-assets.json +12 -12
  5. package/repo-agent-governance/policies/repository.md +2 -0
  6. package/repo-agent-governance/skills/cabloy-spec-execution/SKILL.md +7 -1
  7. package/repo-agent-governance/skills/cabloy-spec-execution/references/execution-protocol.md +4 -2
  8. package/repo-agent-governance/skills/cabloy-spec-generation/SKILL.md +10 -0
  9. package/repo-docs/ai/ai-spec-driven-development.md +12 -0
  10. package/repo-docs/backend/migration-and-changes.md +5 -1
  11. package/repo-docs/backend/startup-guide.md +16 -1
  12. package/repo-docs/frontend/permission-formscene-action-visibility-guide.md +7 -5
  13. package/repo-docs/reference/repo-scripts.md +3 -1
  14. package/vona/packages-cli/cli/package.json +1 -1
  15. package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoResItemName%>.tsx_ +3 -3
  16. package/vona/packages-cli/cli-set-api/package.json +1 -1
  17. package/vona/packages-vona/vona/package.json +1 -1
  18. package/vona/pnpm-lock.yaml +139 -29
  19. package/vona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectResItem.tsx +3 -3
  20. package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordResItem.tsx +3 -3
  21. package/vona/src/suite-vendor/a-vona/modules/a-printtip/package.json +1 -1
  22. package/vona/src/suite-vendor/a-vona/modules/a-printtip/src/bean/startup.printTip.ts +1 -1
  23. package/vona/src/suite-vendor/a-vona/modules/a-printtip/test/printTip.test.ts +46 -0
  24. package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
  25. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +16 -21
  26. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/types/startup.ts +0 -1
  27. package/vona/src/suite-vendor/a-vona/modules/a-startup/test/devSeed.test.ts +282 -30
  28. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  29. package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/version.ts +3 -0
  30. package/vona/src/suite-vendor/a-vona/modules/a-version/test/instanceSeed.test.ts +47 -0
  31. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  32. package/zova/packages-zova/zova/package.json +2 -2
  33. package/zova/pnpm-lock.yaml +8 -8
  34. package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionOperationsRow.tsx +6 -5
  35. package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionUpdate.tsx +1 -0
  36. package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionView.tsx +1 -0
  37. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/actionCreate/controller.tsx +1 -0
  38. package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/formFieldDetails/controller.tsx +20 -8
  39. package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/detailsPermissions.ts +6 -4
  40. package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +1 -0
  41. package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/detail.ts +2 -0
  42. package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/details.ts +2 -2
  43. package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/dialogForm.ts +3 -0
  44. package/zova/src/suite/cabloy-basic/modules/basic-details/test/lib/detailsPermissions.test.ts +196 -1
  45. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  46. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/permissions.ts +8 -2
  47. package/zova/src/suite-vendor/a-zova/package.json +2 -2
@@ -3,6 +3,7 @@ import 'zova-module-a-form';
3
3
  import 'zova-module-a-openapi';
4
4
 
5
5
  import type { ServiceDetail } from '../service/detail.jsx';
6
+ import type { TypeDetailsCheckPermission } from './details.js';
6
7
 
7
8
  declare module 'zova-module-a-openapi' {
8
9
  export interface IJsxRenderContextDetail<TData extends {} = {}> {
@@ -11,6 +12,7 @@ declare module 'zova-module-a-openapi' {
11
12
 
12
13
  export interface IDetailScope {
13
14
  $$detail?: ServiceDetail;
15
+ checkPermission?: TypeDetailsCheckPermission;
14
16
  }
15
17
  }
16
18
 
@@ -1,12 +1,12 @@
1
1
  import 'zova-module-a-table';
2
2
  import 'zova-module-a-openapi';
3
- import type { IDetailsScope, IPermissionHintGeneral } from 'zova-module-a-openapi';
3
+ import type { IDetailsScope, IPermissionHintDetailsAction } from 'zova-module-a-openapi';
4
4
 
5
5
  import { ControllerBlockDetails } from '../component/blockDetails/controller.jsx';
6
6
 
7
7
  export type TypeDetailsCheckPermission = (
8
8
  actionName?: string,
9
- permissionHint?: IPermissionHintGeneral,
9
+ permissionHint?: IPermissionHintDetailsAction,
10
10
  ) => boolean;
11
11
 
12
12
  declare module 'zova-module-a-openapi' {
@@ -3,6 +3,8 @@ import { IIconRecord } from 'zova-module-a-icon';
3
3
  import { TypeFormScene, TypeFormSchemaScene } from 'zova-module-a-openapi';
4
4
  import { AppModalItem, IModalDialogOptions } from 'zova-module-basic-app';
5
5
 
6
+ import type { TypeDetailsCheckPermission } from './details.js';
7
+
6
8
  export interface IDialogFormOptions<TData extends {} = {}> {
7
9
  locale: {
8
10
  Cancel: () => string;
@@ -15,6 +17,7 @@ export interface IDialogFormOptions<TData extends {} = {}> {
15
17
  dialogOptions?: IModalDialogOptions;
16
18
  icon?: keyof IIconRecord;
17
19
  title: string;
20
+ checkPermission?: TypeDetailsCheckPermission;
18
21
  onSubmitData?: (
19
22
  data: TypeFormOnSubmitData<TData>,
20
23
  dialogInstance: AppModalItem,
@@ -1,4 +1,8 @@
1
- import type { IPermissionHintGeneral, TypeOpenapiPermissions } from 'zova-module-a-openapi';
1
+ import type {
2
+ IPermissionHintDetailsAction,
3
+ IPermissionHintGeneral,
4
+ TypeOpenapiPermissions,
5
+ } from 'zova-module-a-openapi';
2
6
 
3
7
  import assert from 'node:assert/strict';
4
8
  import test from 'node:test';
@@ -123,3 +127,194 @@ test('row details actions use the same enclosing Resource checker and form-scene
123
127
  );
124
128
  assert.deepEqual(calls, ['allowed', 'denied']);
125
129
  });
130
+
131
+ test('form-scene CRUD actions do not invoke the enclosing permission checker', () => {
132
+ const calls: string[] = [];
133
+ const actions = ['create', 'update', 'delete'].map(name => ({
134
+ name,
135
+ options: { permission: { mode: 'formScene', formScene: ['create', 'edit'] } },
136
+ }));
137
+ const allowed = filterDetailsRowActions(
138
+ 'edit',
139
+ actionName => {
140
+ calls.push(actionName!);
141
+ return false;
142
+ },
143
+ actions as any,
144
+ );
145
+
146
+ assert.deepEqual(
147
+ allowed.map(action => action.name),
148
+ ['create', 'update', 'delete'],
149
+ );
150
+ assert.deepEqual(calls, []);
151
+ });
152
+
153
+ test('form-scene CRUD actions are filtered before permission checks in view', () => {
154
+ const calls: string[] = [];
155
+ const action = {
156
+ name: 'update',
157
+ options: { permission: { mode: 'formScene', formScene: ['create', 'edit'] } },
158
+ };
159
+
160
+ const allowed = filterDetailsRowActions(
161
+ 'view',
162
+ actionName => {
163
+ calls.push(actionName!);
164
+ return true;
165
+ },
166
+ [action] as any,
167
+ );
168
+
169
+ assert.deepEqual(allowed, []);
170
+ assert.deepEqual(calls, []);
171
+ });
172
+
173
+ test('action mode uses the literal action name and inherited hint', () => {
174
+ const calls: Array<[string | undefined, IPermissionHintDetailsAction | undefined]> = [];
175
+ const permissionHint = {
176
+ mode: 'action',
177
+ actionInherit: 'approve',
178
+ formScene: ['create', 'edit'],
179
+ };
180
+ const allowed = filterDetailsRowActions(
181
+ 'edit',
182
+ (actionName, hint) => {
183
+ calls.push([actionName, hint]);
184
+ return true;
185
+ },
186
+ [{ name: 'approve', options: { permission: permissionHint } }] as any,
187
+ );
188
+
189
+ assert.deepEqual(
190
+ allowed.map(action => action.name),
191
+ ['approve'],
192
+ );
193
+ assert.deepEqual(calls, [['approve', permissionHint]]);
194
+ });
195
+
196
+ test('omitted mode checks custom actions without inferring CRUD permissions', () => {
197
+ const calls: string[] = [];
198
+ const actions = ['create', 'update', 'delete'].map(name => ({ name }));
199
+ const allowed = filterDetailsRowActions(
200
+ 'create',
201
+ actionName => {
202
+ calls.push(actionName!);
203
+ return actionName === 'update';
204
+ },
205
+ actions as any,
206
+ );
207
+
208
+ assert.deepEqual(
209
+ allowed.map(action => action.name),
210
+ ['update'],
211
+ );
212
+ assert.deepEqual(calls, ['create', 'update', 'delete']);
213
+ });
214
+
215
+ test('form-scene mode with action inheritance still invokes the checker', () => {
216
+ const calls: Array<[string | undefined, IPermissionHintDetailsAction | undefined]> = [];
217
+ const permissionHint = {
218
+ mode: 'formScene',
219
+ actionInherit: 'grant',
220
+ formScene: ['create', 'edit'],
221
+ };
222
+ const allowed = filterDetailsRowActions(
223
+ 'create',
224
+ (actionName, hint) => {
225
+ calls.push([actionName, hint]);
226
+ return actionName === 'update';
227
+ },
228
+ [{ name: 'update', options: { permission: permissionHint } }] as any,
229
+ );
230
+
231
+ assert.deepEqual(
232
+ allowed.map(action => action.name),
233
+ ['update'],
234
+ );
235
+ assert.deepEqual(calls, [['update', permissionHint]]);
236
+ });
237
+
238
+ test('view scene hides nested write actions before permission checks', () => {
239
+ const calls: string[] = [];
240
+ const actions = [
241
+ {
242
+ name: 'create',
243
+ options: { permission: { mode: 'formScene', formScene: ['create', 'edit'] } },
244
+ },
245
+ {
246
+ name: 'update',
247
+ options: { permission: { mode: 'formScene', formScene: ['create', 'edit'] } },
248
+ },
249
+ {
250
+ name: 'delete',
251
+ options: { permission: { mode: 'formScene', formScene: ['create', 'edit'] } },
252
+ },
253
+ { name: 'view', options: { permission: { actionInherit: 'view' } } },
254
+ ];
255
+
256
+ const allowed = filterDetailsRowActions(
257
+ 'view',
258
+ actionName => {
259
+ calls.push(actionName!);
260
+ return actionName === 'view';
261
+ },
262
+ actions as any,
263
+ );
264
+
265
+ assert.deepEqual(
266
+ allowed.map(action => action.name),
267
+ ['view'],
268
+ );
269
+ assert.deepEqual(calls, ['view']);
270
+ });
271
+
272
+ test('view scene preserves explicit inherited write actions', () => {
273
+ const calls: string[] = [];
274
+ const actions = [
275
+ { name: 'update', options: { permission: { actionInherit: 'grant', formScene: ['view'] } } },
276
+ ];
277
+
278
+ const allowed = filterDetailsRowActions(
279
+ 'view',
280
+ (actionName, permissionHint) => {
281
+ calls.push(actionName!);
282
+ return actionName === 'update' && permissionHint?.actionInherit === 'grant';
283
+ },
284
+ actions as any,
285
+ );
286
+
287
+ assert.deepEqual(
288
+ allowed.map(action => action.name),
289
+ ['update'],
290
+ );
291
+ assert.deepEqual(calls, ['update']);
292
+ });
293
+
294
+ test('explicit action inheritance and custom actions are preserved', () => {
295
+ const calls: Array<[string | undefined, IPermissionHintGeneral | undefined]> = [];
296
+ const explicitHint = { actionInherit: 'grant', formScene: ['create', 'edit'] };
297
+ const customHint = { mode: 'action', formScene: ['create', 'edit'] };
298
+ const actions = [
299
+ { name: 'update', options: { permission: explicitHint } },
300
+ { name: 'archive', options: { permission: customHint } },
301
+ ];
302
+
303
+ const allowed = filterDetailsRowActions(
304
+ 'create',
305
+ (actionName, permissionHint) => {
306
+ calls.push([actionName, permissionHint]);
307
+ return true;
308
+ },
309
+ actions as any,
310
+ );
311
+
312
+ assert.deepEqual(
313
+ allowed.map(action => action.name),
314
+ ['update', 'archive'],
315
+ );
316
+ assert.deepEqual(calls, [
317
+ ['update', explicitHint],
318
+ ['archive', customHint],
319
+ ]);
320
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-openapi",
3
- "version": "5.1.56",
3
+ "version": "5.1.57",
4
4
  "gitHead": "09d901d17140a80ee0764211b441cda72fd94663",
5
5
  "description": "",
6
6
  "keywords": [
@@ -52,6 +52,12 @@ export interface IPermissionHintTableActionBulk {
52
52
  public?: boolean;
53
53
  }
54
54
 
55
- export interface IPermissionHintDetailsActionRow extends IPermissionHintGeneral {}
55
+ export type TypeDetailsPermissionMode = 'formScene' | 'action';
56
56
 
57
- export interface IPermissionHintDetailsActionBulk extends IPermissionHintGeneral {}
57
+ export interface IPermissionHintDetailsAction extends IPermissionHintGeneral {
58
+ mode?: TypeDetailsPermissionMode;
59
+ }
60
+
61
+ export interface IPermissionHintDetailsActionRow extends IPermissionHintDetailsAction {}
62
+
63
+ export interface IPermissionHintDetailsActionBulk extends IPermissionHintDetailsAction {}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-suite-a-zova",
3
- "version": "5.1.169",
3
+ "version": "5.1.170",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  "zova-module-a-logger": "^5.1.28",
22
22
  "zova-module-a-meta": "^5.1.22",
23
23
  "zova-module-a-model": "^5.1.38",
24
- "zova-module-a-openapi": "^5.1.56",
24
+ "zova-module-a-openapi": "^5.1.57",
25
25
  "zova-module-a-router": "^5.1.42",
26
26
  "zova-module-a-routerstack": "^5.1.29",
27
27
  "zova-module-a-routertabs": "^5.1.35",