cabloy 5.1.147 → 5.1.149

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 (43) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-workflow/evals/evals.json +1 -1
  3. package/.claude/skills/cabloy-worktree-environment/SKILL.md +2 -5
  4. package/.claude/skills/cabloy-worktree-environment/evals/evals.json +1 -1
  5. package/CHANGELOG.md +17 -0
  6. package/package.json +1 -1
  7. package/repo-docs/.vitepress/config.mjs +4 -0
  8. package/repo-docs/backend/markdown-guide.md +1 -0
  9. package/repo-docs/backend/relations-guide.md +1 -0
  10. package/repo-docs/frontend/form-layout-guide.md +15 -1
  11. package/repo-docs/frontend/markdown-guide.md +1 -0
  12. package/repo-docs/frontend/scripts.md +11 -11
  13. package/repo-docs/fullstack/edition-collaboration-differences.md +1 -0
  14. package/repo-docs/fullstack/one-to-one-companion-resource-guide.md +447 -0
  15. package/repo-docs/fullstack/parallel-worktree-environment.md +4 -4
  16. package/repo-docs/fullstack/quickstart.md +4 -4
  17. package/repo-docs/reference/repo-scripts.md +12 -14
  18. package/repo-e2e/specs/cabloy-basic.spec.ts +176 -1
  19. package/vona/src/suite/a-training/modules/training-student/package.json +1 -0
  20. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +177 -103
  21. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.index.ts +1 -0
  22. package/vona/src/suite/a-training/modules/training-student/src/bean/meta.version.ts +19 -1
  23. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
  24. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
  25. package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +21 -2
  26. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +7 -1
  27. package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +21 -2
  28. package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +21 -2
  29. package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +0 -3
  30. package/vona/src/suite/a-training/modules/training-student/src/entity/studentContent.tsx +36 -0
  31. package/vona/src/suite/a-training/modules/training-student/src/model/student.ts +6 -0
  32. package/vona/src/suite/a-training/modules/training-student/src/model/studentContent.ts +22 -0
  33. package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +63 -6
  34. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +117 -7
  35. package/zova/packages-zova/zova/package.json +2 -2
  36. package/zova/pnpm-lock.yaml +19 -15
  37. package/zova/src/suite/a-training/modules/training-student/package.json +3 -1
  38. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +40 -40
  39. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +358 -337
  40. package/zova/src/suite/a-training/modules/training-student/src/bean/tableCell.actionSummary.tsx +18 -7
  41. package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
  42. package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts +13 -5
  43. package/zova/src/suite-vendor/a-zova/package.json +2 -2
@@ -0,0 +1,36 @@
1
+ import type { TableIdentity } from 'table-identity';
2
+ import type { IDecoratorEntityOptions } from 'vona-module-a-orm';
3
+
4
+ import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';
5
+ import { Entity, EntityBase } from 'vona-module-a-orm';
6
+ import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
7
+
8
+ import { $locale } from '../.metadata/locales.ts';
9
+
10
+ export interface IEntityOptionsStudentContent extends IDecoratorEntityOptions {}
11
+
12
+ @Entity<IEntityOptionsStudentContent>('trainingStudentContent', {
13
+ openapi: { title: $locale('StudentContent') },
14
+ fields: {
15
+ id: $makeMetadata(ZovaRender.visible(false)),
16
+ iid: $makeMetadata(ZovaRender.visible(false)),
17
+ deleted: $makeMetadata(ZovaRender.visible(false)),
18
+ createdAt: $makeMetadata(ZovaRender.visible(false)),
19
+ updatedAt: $makeMetadata(ZovaRender.visible(false)),
20
+ },
21
+ })
22
+ export class EntityStudentContent extends EntityBase {
23
+ @Api.field(v.tableIdentity(), ZovaRender.visible(false))
24
+ studentId: TableIdentity;
25
+
26
+ @Api.field(
27
+ v.title($locale('DescriptionMarkdown')),
28
+ v.optional(),
29
+ ZovaRender.order(1),
30
+ ZovaRender.field('basic-markdown:formFieldMarkdown'),
31
+ )
32
+ descriptionMarkdown?: string;
33
+
34
+ @Api.field(v.title($locale('DescriptionHtml')), v.optional(), ZovaRender.visible(false))
35
+ descriptionHtml?: string;
36
+ }
@@ -22,6 +22,12 @@ export interface IModelOptionsStudent extends IDecoratorModelOptions<EntityStude
22
22
  'description',
23
23
  ],
24
24
  }),
25
+ studentContent: $relation.hasOne('training-student:studentContent', 'studentId', {
26
+ columns: ['id', 'studentId', 'descriptionMarkdown', 'descriptionHtml'],
27
+ }),
28
+ studentContentForm: $relation.hasOne('training-student:studentContent', 'studentId', {
29
+ columns: ['id', 'studentId', 'descriptionMarkdown'],
30
+ }),
25
31
  },
26
32
  })
27
33
  export class ModelStudent extends BeanModelBase<EntityStudent> {}
@@ -0,0 +1,22 @@
1
+ import type { IDecoratorModelOptions } from 'vona-module-a-orm';
2
+
3
+ import { $relation, BeanModelBase, Model } from 'vona-module-a-orm';
4
+
5
+ import { EntityStudentContent } from '../entity/studentContent.tsx';
6
+
7
+ export interface IModelOptionsStudentContent extends IDecoratorModelOptions<EntityStudentContent> {}
8
+
9
+ @Model<IModelOptionsStudentContent>({
10
+ entity: EntityStudentContent,
11
+ relations: {
12
+ student: $relation.belongsTo(
13
+ 'training-student:studentContent',
14
+ 'training-student:student',
15
+ 'studentId',
16
+ ),
17
+ },
18
+ cache: {
19
+ modelsClear: 'training-student:student',
20
+ },
21
+ })
22
+ export class ModelStudentContent extends BeanModelBase<EntityStudentContent> {}
@@ -18,12 +18,27 @@ function getStudentRecordSubjectsInclude(): {
18
18
  return { trainingRecords: { include: { trainingRecordSubjects: true } } };
19
19
  }
20
20
 
21
+ function getStudentContentFormInclude() {
22
+ return { studentContentForm: true };
23
+ }
24
+
25
+ function getStudentContentInclude() {
26
+ return { studentContent: true };
27
+ }
28
+
21
29
  @Service()
22
30
  export class ServiceStudent extends BeanBase {
23
31
  async create(student: DtoStudentCreate): Promise<EntityStudent> {
24
- return await this.scope.model.student.insert(student, {
32
+ const { studentContentForm, ...studentFields } = student as DtoStudentCreate & {
33
+ studentContentForm?: { descriptionMarkdown?: string };
34
+ };
35
+ const created = await this.scope.model.student.insert(studentFields, {
25
36
  include: getStudentRecordSubjectsInclude(),
26
37
  });
38
+ if (studentContentForm !== undefined) {
39
+ await this._updateStudentContent(created.id, studentContentForm.descriptionMarkdown);
40
+ }
41
+ return created;
27
42
  }
28
43
 
29
44
  async select(params?: IQueryParams<ModelStudent>): Promise<DtoStudentSelectRes> {
@@ -32,20 +47,34 @@ export class ServiceStudent extends BeanBase {
32
47
 
33
48
  async view(id: TableIdentity): Promise<DtoStudentView | undefined> {
34
49
  return await this.scope.model.student.getById(id, {
35
- include: getStudentRecordSubjectsInclude(),
50
+ include: {
51
+ ...getStudentRecordSubjectsInclude(),
52
+ ...getStudentContentFormInclude(),
53
+ },
36
54
  });
37
55
  }
38
56
 
39
57
  async update(id: TableIdentity, student: DtoStudentUpdate) {
40
- return await this.scope.model.student.updateById(id, student, {
58
+ const { studentContentForm, ...studentFields } = student as DtoStudentUpdate & {
59
+ studentContentForm?: { descriptionMarkdown?: string };
60
+ };
61
+ const updateResult = await this.scope.model.student.updateById(id, studentFields, {
41
62
  include: getStudentRecordSubjectsInclude(),
42
63
  });
64
+ if (studentContentForm !== undefined) {
65
+ await this._updateStudentContent(id, studentContentForm.descriptionMarkdown);
66
+ }
67
+ return updateResult;
43
68
  }
44
69
 
45
70
  async summary(id: TableIdentity): Promise<DtoStudentSummary | undefined> {
46
- const student = await this.scope.model.student.getById(id);
71
+ const student = await this.scope.model.student.getById(id, {
72
+ include: getStudentContentInclude(),
73
+ });
47
74
  if (!student) return undefined;
48
- const descriptionLength = student.description?.length ?? 0;
75
+ const description = student.studentContent?.descriptionMarkdown;
76
+ const descriptionHtml = student.studentContent?.descriptionHtml;
77
+ const descriptionLength = description?.length ?? 0;
49
78
  const levelTitle = String(student.level);
50
79
  return {
51
80
  id: student.id,
@@ -53,22 +82,50 @@ export class ServiceStudent extends BeanBase {
53
82
  mobile: student.mobile,
54
83
  level: student.level,
55
84
  levelTitle,
56
- description: student.description,
85
+ description,
86
+ descriptionHtml,
57
87
  descriptionLength,
58
88
  summaryText: `${student.name} is in level ${student.level}. Description length: ${descriptionLength}.`,
59
89
  };
60
90
  }
61
91
 
62
92
  async delete(id: TableIdentity) {
93
+ await this.scope.model.studentContent.delete({ studentId: id });
63
94
  return await this.scope.model.student.deleteById(id, {
64
95
  include: getStudentRecordSubjectsInclude(),
65
96
  });
66
97
  }
67
98
 
68
99
  async deleteForce(id: TableIdentity) {
100
+ await this.scope.model.studentContent.delete({ studentId: id }, { disableDeleted: true });
69
101
  return await this.scope.model.student.deleteById(id, {
70
102
  disableDeleted: true,
71
103
  include: getStudentRecordSubjectsInclude(),
72
104
  });
73
105
  }
106
+
107
+ private async _updateStudentContent(
108
+ studentId: TableIdentity,
109
+ descriptionMarkdown?: string,
110
+ ): Promise<void> {
111
+ const markdown = descriptionMarkdown?.trim();
112
+ const studentContent = await this.scope.model.studentContent.get({ studentId });
113
+ if (!markdown) {
114
+ if (studentContent) await this.scope.model.studentContent.deleteById(studentContent.id);
115
+ return;
116
+ }
117
+ const descriptionHtml = this.bean.markdown.renderHtml(markdown);
118
+ if (studentContent) {
119
+ await this.scope.model.studentContent.updateById(studentContent.id, {
120
+ descriptionMarkdown: markdown,
121
+ descriptionHtml,
122
+ });
123
+ } else {
124
+ await this.scope.model.studentContent.insert({
125
+ studentId,
126
+ descriptionMarkdown: markdown,
127
+ descriptionHtml,
128
+ });
129
+ }
130
+ }
74
131
  }
@@ -25,17 +25,36 @@ describe('student.test.ts', () => {
25
25
  const formLayout =
26
26
  blocks?.[0]?.options?.blocks?.[0]?.options?.blocks?.[0]?.options?.formLayout;
27
27
  const tabs = formLayout?.children[0];
28
- const profileSection = tabs?.children[0]?.children[0]?.children[0];
28
+ const groups = tabs?.children[0]?.children;
29
+ const profileSection = groups?.[0]?.children[0];
30
+ const studentContentSection = groups?.[1]?.children[0];
29
31
  const trainingRecordsSection = tabs?.children[1]?.children[1];
30
32
  assert.equal(tabs?.type, 'tabs');
31
33
  assert.equal(tabs?.id, undefined);
32
34
  assert.equal(tabs?.children[1]?.type, 'tab');
33
35
  assert.equal(tabs?.children[1]?.id, undefined);
36
+ assert.equal(groups?.length, 2);
37
+ assert.deepEqual(
38
+ groups?.map(item => item.type),
39
+ ['group', 'group'],
40
+ );
34
41
  assert.deepEqual(profileSection?.columns, { default: 1, md: 2 });
35
42
  assert.deepEqual(
36
43
  profileSection?.children.map(item => item.name),
37
44
  ['name', 'mobile', 'imageId'],
38
45
  );
46
+ assert.deepEqual(
47
+ studentContentSection?.children.map(item => item.name),
48
+ ['studentContentForm'],
49
+ );
50
+ assert.equal(
51
+ component.properties.studentContentForm.rest.fieldSource,
52
+ 'studentContentForm.descriptionMarkdown',
53
+ );
54
+ assert.equal(
55
+ component.properties.studentContentForm.rest.form.render,
56
+ 'basic-markdown:formFieldMarkdown',
57
+ );
39
58
  assert.equal(tabs?.children[1]?.children[0]?.name, 'level');
40
59
  assert.deepEqual(
41
60
  trainingRecordsSection?.children.map(item => item.name),
@@ -93,6 +112,7 @@ describe('student.test.ts', () => {
93
112
  'mobile',
94
113
  'level',
95
114
  'description',
115
+ 'descriptionHtml',
96
116
  'levelTitle',
97
117
  'descriptionLength',
98
118
  'summaryText',
@@ -118,9 +138,13 @@ describe('student.test.ts', () => {
118
138
  const maskedMobileUpdate = '139****4321';
119
139
  const trainingTime = new Date('2026-03-10T08:00:00.000Z');
120
140
  const trainingTimeUpdate = new Date('2026-04-18T13:20:00.000Z');
141
+ const description =
142
+ `# Student profile\n\n${'This is a long Markdown description. '.repeat(10)}`.trim();
143
+ const descriptionUpdate =
144
+ `## Updated profile\n\n${'This is updated Markdown content. '.repeat(10)}`.trim();
121
145
  const data = {
122
146
  name: '__Tom__',
123
- description: 'This is a test',
147
+ studentContentForm: { descriptionMarkdown: description },
124
148
  mobile,
125
149
  level: 1,
126
150
  trainingRecords: [
@@ -157,6 +181,7 @@ describe('student.test.ts', () => {
157
181
  assert.equal(!!studentItem, true);
158
182
  assert.equal(studentItem!.level, data.level);
159
183
  assert.equal(studentItem!.mobile, maskedMobile);
184
+ assert.equal((studentItem as any).description, undefined);
160
185
  // findMany: level filter
161
186
  const selectResByLevel: DtoStudentSelectRes = await app.bean.executor.performAction(
162
187
  'get',
@@ -182,6 +207,7 @@ describe('student.test.ts', () => {
182
207
  const record = student.trainingRecords?.[0];
183
208
  const recordSubject = record?.trainingRecordSubjects?.[0];
184
209
  assert.equal(student.trainingRecords?.length, 1);
210
+ assert.equal(student.studentContentForm?.descriptionMarkdown, description);
185
211
  assert.equal(record?.name, '__Record__');
186
212
  assert.equal(record?.subjectCount, 1);
187
213
  assert.equal(record?.totalScore, 88);
@@ -193,7 +219,7 @@ describe('student.test.ts', () => {
193
219
  // update
194
220
  const dataUpdate = {
195
221
  name: '__TomNew__',
196
- description: 'This is a test',
222
+ studentContentForm: { descriptionMarkdown: descriptionUpdate },
197
223
  mobile: mobileUpdate,
198
224
  level: 2,
199
225
  trainingRecords: [
@@ -236,6 +262,7 @@ describe('student.test.ts', () => {
236
262
  assert.equal(student.name, dataUpdate.name);
237
263
  assert.equal(student.level, dataUpdate.level);
238
264
  assert.equal(student.mobile, maskedMobileUpdate);
265
+ assert.equal(student.studentContentForm?.descriptionMarkdown, descriptionUpdate);
239
266
  assert.equal(student.trainingRecords?.length, 1);
240
267
  assert.equal(updatedRecord?.name, '__RecordNew__');
241
268
  assert.equal(updatedRecord?.subjectCount, 2);
@@ -254,6 +281,15 @@ describe('student.test.ts', () => {
254
281
  disableDeleted: true,
255
282
  });
256
283
  assert.equal(studentRaw!.mobile, mobileUpdate);
284
+ assert.equal((studentRaw as any).description, undefined);
285
+ const studentContent = await app
286
+ .scope('training-student')
287
+ .model.studentContent.get({ studentId });
288
+ assert.equal(studentContent?.descriptionMarkdown, descriptionUpdate);
289
+ assert.equal(
290
+ studentContent?.descriptionHtml,
291
+ app.bean.markdown.renderHtml(descriptionUpdate),
292
+ );
257
293
  // summary
258
294
  const summary: DtoStudentSummary = await app.bean.executor.performAction(
259
295
  'get',
@@ -263,7 +299,9 @@ describe('student.test.ts', () => {
263
299
  assert.equal(summary.name, dataUpdate.name);
264
300
  assert.equal(summary.mobile, maskedMobileUpdate);
265
301
  assert.equal(summary.level, dataUpdate.level);
266
- assert.equal(summary.descriptionLength, dataUpdate.description?.length);
302
+ assert.equal(summary.description, descriptionUpdate);
303
+ assert.equal(summary.descriptionHtml, app.bean.markdown.renderHtml(descriptionUpdate));
304
+ assert.equal(summary.descriptionLength, descriptionUpdate.length);
267
305
  assert.equal(typeof summary.levelTitle, 'string');
268
306
  assert.equal(typeof summary.summaryText, 'string');
269
307
  // delete
@@ -294,11 +332,83 @@ describe('student.test.ts', () => {
294
332
  disableDeleted: true,
295
333
  });
296
334
  assert.equal(studentForce, undefined);
335
+ assert.equal(
336
+ await app
337
+ .scope('training-student')
338
+ .model.studentContent.get({ studentId: studentIdForce }, { disableDeleted: true }),
339
+ undefined,
340
+ );
297
341
  // logout
298
342
  await app.bean.passport.signout();
299
343
  });
300
344
  });
301
345
 
346
+ it('action:student:contentPersistence', async () => {
347
+ await app.bean.executor.mockCtx(async () => {
348
+ await app.bean.passport.signinMock();
349
+ let studentId: string | undefined;
350
+ try {
351
+ studentId = await app.bean.executor.performAction('post', '/training/student', {
352
+ body: {
353
+ name: '__ContentStudent__',
354
+ mobile: '13812345678',
355
+ level: 1,
356
+ },
357
+ });
358
+ assert.equal(
359
+ await app.scope('training-student').model.studentContent.get({ studentId }),
360
+ undefined,
361
+ );
362
+
363
+ await app.bean.executor.performAction('patch', '/training/student/:id', {
364
+ params: { id: studentId },
365
+ body: {
366
+ name: '__ContentStudent__',
367
+ mobile: '13812345678',
368
+ level: 1,
369
+ studentContentForm: {
370
+ descriptionMarkdown: ' ## Created later ',
371
+ descriptionHtml: '<p>Forged HTML</p>',
372
+ },
373
+ },
374
+ });
375
+ const studentContent = await app
376
+ .scope('training-student')
377
+ .model.studentContent.get({ studentId });
378
+ assert.equal(studentContent?.descriptionMarkdown, '## Created later');
379
+ assert.equal(
380
+ studentContent?.descriptionHtml,
381
+ app.bean.markdown.renderHtml('## Created later'),
382
+ );
383
+ assert.notEqual(studentContent?.descriptionHtml, '<p>Forged HTML</p>');
384
+
385
+ await app.bean.executor.performAction('patch', '/training/student/:id', {
386
+ params: { id: studentId },
387
+ body: {
388
+ name: '__ContentStudent__',
389
+ mobile: '13812345678',
390
+ level: 1,
391
+ studentContentForm: { descriptionMarkdown: ' \n\t ' },
392
+ },
393
+ });
394
+ assert.equal(
395
+ await app.scope('training-student').model.studentContent.get({ studentId }),
396
+ undefined,
397
+ );
398
+ } finally {
399
+ if (studentId) {
400
+ await app
401
+ .scope('training-student')
402
+ .model.studentContent.delete({ studentId }, { disableDeleted: true });
403
+ await app
404
+ .scope('training-student')
405
+ .model.student.deleteById(studentId, { disableDeleted: true });
406
+ }
407
+ await app.bean.passport.signout();
408
+ }
409
+ });
410
+ });
411
+
302
412
  it('action:student:systemAdmin', async () => {
303
413
  await app.bean.executor.mockCtx(async () => {
304
414
  await app.bean.passport.signinMock();
@@ -327,7 +437,7 @@ describe('student.test.ts', () => {
327
437
  await app.bean.executor.performAction('post', '/training/student', {
328
438
  body: {
329
439
  name: '__Tom__',
330
- description: 'This is a test',
440
+ studentContentForm: { descriptionMarkdown: 'This is a test' },
331
441
  mobile: '13812345678',
332
442
  level: 4,
333
443
  },
@@ -344,7 +454,7 @@ describe('student.test.ts', () => {
344
454
  await app.bean.executor.performAction('post', '/training/student', {
345
455
  body: {
346
456
  name: '__Tom__',
347
- description: 'This is a test',
457
+ studentContentForm: { descriptionMarkdown: 'This is a test' },
348
458
  level: 1,
349
459
  },
350
460
  });
@@ -360,7 +470,7 @@ describe('student.test.ts', () => {
360
470
  await app.bean.executor.performAction('post', '/training/student', {
361
471
  body: {
362
472
  name: '__Tom__',
363
- description: 'This is a test',
473
+ studentContentForm: { descriptionMarkdown: 'This is a test' },
364
474
  mobile: '1381234567',
365
475
  level: 1,
366
476
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.153",
3
+ "version": "5.1.154",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "zova-core": "^5.1.88",
49
- "zova-suite-a-zova": "^5.1.152"
49
+ "zova-suite-a-zova": "^5.1.153"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -77,7 +77,7 @@ importers:
77
77
  version: 10.5.2(postcss@8.5.26)
78
78
  axios:
79
79
  specifier: ^1.18.1
80
- version: 1.18.1
80
+ version: 1.18.1(supports-color@10.2.2)
81
81
  compression:
82
82
  specifier: ^1.8.1
83
83
  version: 1.8.1
@@ -363,7 +363,7 @@ importers:
363
363
  devDependencies:
364
364
  '@cabloy/cli':
365
365
  specifier: ^3.1.29
366
- version: 3.1.29(vue@3.5.38(typescript@5.9.3))
366
+ version: 3.1.29(supports-color@10.2.2)(vue@3.5.38(typescript@5.9.3))
367
367
  '@cabloy/lint':
368
368
  specifier: ^5.1.32
369
369
  version: 5.1.32(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/rule-tester@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(oxfmt@0.62.0)(oxlint@1.77.0)(supports-color@10.2.2)(typescript@5.9.3)(vue-eslint-parser@10.4.1(eslint@10.6.0(jiti@2.7.0)))
@@ -438,7 +438,7 @@ importers:
438
438
  dependencies:
439
439
  '@cabloy/cli':
440
440
  specifier: ^3.1.29
441
- version: 3.1.29(vue@3.5.38(typescript@5.9.3))
441
+ version: 3.1.29(supports-color@10.2.2)(vue@3.5.38(typescript@5.9.3))
442
442
  '@cabloy/process-helper':
443
443
  specifier: ^3.1.8
444
444
  version: 3.1.8
@@ -449,7 +449,7 @@ importers:
449
449
  specifier: ^7.6.2
450
450
  version: 7.8.5
451
451
  zova-cli-set-front:
452
- specifier: ^1.2.101
452
+ specifier: ^1.2.102
453
453
  version: link:../cli-set-front
454
454
  devDependencies:
455
455
  clean-package:
@@ -472,7 +472,7 @@ importers:
472
472
  version: 7.29.7(@babel/core@7.29.7)
473
473
  '@cabloy/cli':
474
474
  specifier: ^3.1.29
475
- version: 3.1.29(vue@3.5.38(typescript@5.9.3))
475
+ version: 3.1.29(supports-color@10.2.2)(vue@3.5.38(typescript@5.9.3))
476
476
  '@cabloy/extend':
477
477
  specifier: ^3.2.8
478
478
  version: 3.2.8
@@ -1198,7 +1198,7 @@ importers:
1198
1198
  dependencies:
1199
1199
  axios:
1200
1200
  specifier: ^1.18.1
1201
- version: 1.18.1
1201
+ version: 1.18.1(supports-color@10.2.2)
1202
1202
  devDependencies:
1203
1203
  clean-package:
1204
1204
  specifier: ^2.2.0
@@ -1471,7 +1471,7 @@ importers:
1471
1471
  devDependencies:
1472
1472
  '@cabloy/cli':
1473
1473
  specifier: ^3.1.29
1474
- version: 3.1.29(vue@3.5.38(typescript@5.9.3))
1474
+ version: 3.1.29(supports-color@10.2.2)(vue@3.5.38(typescript@5.9.3))
1475
1475
  '@types/luxon':
1476
1476
  specifier: ^3.7.1
1477
1477
  version: 3.7.2
@@ -1804,6 +1804,10 @@ importers:
1804
1804
  version: link:modules/training-student
1805
1805
 
1806
1806
  src/suite/a-training/modules/training-student:
1807
+ dependencies:
1808
+ zova-module-basic-markdown:
1809
+ specifier: ^5.0.0
1810
+ version: link:../../../cabloy-basic/modules/basic-markdown
1807
1811
  devDependencies:
1808
1812
  clean-package:
1809
1813
  specifier: ^2.2.0
@@ -8798,7 +8802,7 @@ snapshots:
8798
8802
 
8799
8803
  '@bufbuild/protobuf@2.12.1': {}
8800
8804
 
8801
- '@cabloy/cli@3.1.29(vue@3.5.38(typescript@5.9.3))':
8805
+ '@cabloy/cli@3.1.29(supports-color@10.2.2)(vue@3.5.38(typescript@5.9.3))':
8802
8806
  dependencies:
8803
8807
  '@babel/parser': 7.29.7
8804
8808
  '@cabloy/module-glob': 5.3.18
@@ -8807,7 +8811,7 @@ snapshots:
8807
8811
  '@cabloy/utils': 2.1.27
8808
8812
  '@cabloy/word-utils': link:packages-utils/word-utils
8809
8813
  '@npmcli/config': 10.11.0
8810
- '@zhennann/common-bin': 4.0.1
8814
+ '@zhennann/common-bin': 4.0.1(supports-color@10.2.2)
8811
8815
  '@zhennann/ejs': 3.0.1
8812
8816
  boxen: 4.2.0
8813
8817
  chalk: 3.0.0
@@ -11041,7 +11045,7 @@ snapshots:
11041
11045
 
11042
11046
  '@vue/shared@3.5.13': {}
11043
11047
 
11044
- '@zhennann/common-bin@4.0.1':
11048
+ '@zhennann/common-bin@4.0.1(supports-color@10.2.2)':
11045
11049
  dependencies:
11046
11050
  chalk: 4.1.2
11047
11051
  change-case: 4.1.2
@@ -11075,7 +11079,7 @@ snapshots:
11075
11079
 
11076
11080
  acorn@8.17.0: {}
11077
11081
 
11078
- agent-base@6.0.2:
11082
+ agent-base@6.0.2(supports-color@10.2.2):
11079
11083
  dependencies:
11080
11084
  debug: 4.4.3(supports-color@10.2.2)
11081
11085
  transitivePeerDependencies:
@@ -11187,11 +11191,11 @@ snapshots:
11187
11191
  dependencies:
11188
11192
  possible-typed-array-names: 1.1.0
11189
11193
 
11190
- axios@1.18.1:
11194
+ axios@1.18.1(supports-color@10.2.2):
11191
11195
  dependencies:
11192
11196
  follow-redirects: 1.16.0
11193
11197
  form-data: 4.0.6
11194
- https-proxy-agent: 5.0.1
11198
+ https-proxy-agent: 5.0.1(supports-color@10.2.2)
11195
11199
  proxy-from-env: 2.1.0
11196
11200
  transitivePeerDependencies:
11197
11201
  - debug
@@ -12604,9 +12608,9 @@ snapshots:
12604
12608
  statuses: 2.0.2
12605
12609
  toidentifier: 1.0.1
12606
12610
 
12607
- https-proxy-agent@5.0.1:
12611
+ https-proxy-agent@5.0.1(supports-color@10.2.2):
12608
12612
  dependencies:
12609
- agent-base: 6.0.2
12613
+ agent-base: 6.0.2(supports-color@10.2.2)
12610
12614
  debug: 4.4.3(supports-color@10.2.2)
12611
12615
  transitivePeerDependencies:
12612
12616
  - supports-color
@@ -33,7 +33,9 @@
33
33
  "prepack": "clean-package",
34
34
  "postpack": "clean-package restore"
35
35
  },
36
- "dependencies": {},
36
+ "dependencies": {
37
+ "zova-module-basic-markdown": "^5.0.0"
38
+ },
37
39
  "devDependencies": {
38
40
  "clean-package": "^2.2.0",
39
41
  "rimraf": "^6.1.3"