cabloy 5.1.213 → 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 (26) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +8 -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/reference/repo-scripts.md +3 -1
  13. package/vona/packages-vona/vona/package.json +1 -1
  14. package/vona/pnpm-lock.yaml +139 -29
  15. package/vona/src/suite-vendor/a-vona/modules/a-printtip/package.json +1 -1
  16. package/vona/src/suite-vendor/a-vona/modules/a-printtip/src/bean/startup.printTip.ts +1 -1
  17. package/vona/src/suite-vendor/a-vona/modules/a-printtip/test/printTip.test.ts +46 -0
  18. package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
  19. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +16 -21
  20. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/types/startup.ts +0 -1
  21. package/vona/src/suite-vendor/a-vona/modules/a-startup/test/devSeed.test.ts +282 -30
  22. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  23. package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/version.ts +3 -0
  24. package/vona/src/suite-vendor/a-vona/modules/a-version/test/instanceSeed.test.ts +47 -0
  25. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  26. package/zova/pnpm-lock.yaml +8 -8
@@ -1,22 +1,45 @@
1
1
  import assert from 'node:assert/strict';
2
- import { describe, it } from 'node:test';
2
+ import { afterEach, beforeEach, describe, it } from 'node:test';
3
3
 
4
+ import { ServiceInstance } from '../../a-instance/src/service/instance.ts';
4
5
  import { ServiceStartup } from '../src/service/startup.ts';
5
6
 
6
7
  function fixture(created: string[] = []) {
7
8
  const calls: string[] = [];
9
+ const optionsSeen: { name: string; instance: string | null; force?: boolean }[] = [];
8
10
  const pending = new Set(created);
9
11
  const app = {
10
- meta: { isDev: true, isTest: false, appReadyInstances: {} as Record<string, boolean> },
12
+ meta: {
13
+ isDev: true,
14
+ isTest: false,
15
+ appReady: true,
16
+ appReadyInstances: {} as Record<string, boolean>,
17
+ env: {},
18
+ },
11
19
  config: { server: { listen: { disable: false } } },
12
20
  };
13
21
  const service = Object.create(ServiceStartup.prototype) as ServiceStartup;
14
22
  const startups = [
23
+ { name: 'a-version:databaseInit', beanOptions: { options: {} } },
24
+ { name: 'a-version:databaseName', beanOptions: { options: {} } },
15
25
  { name: 'a-version:instanceInit', beanOptions: { options: { instance: true } } },
26
+ { name: 'a-schedule:loadSchedules', beanOptions: { options: { instance: true, after: true } } },
16
27
  { name: 'a-runtime:runtime', beanOptions: { options: { instance: true, after: true } } },
28
+ { name: 'a-printtip:printTip', beanOptions: { options: { instance: true, after: true } } },
17
29
  { name: 'a-queue:loadQueueWorkers', beanOptions: { options: { instance: true, after: true } } },
18
30
  { name: 'a-web:listen', beanOptions: { options: { after: true } } },
19
31
  ];
32
+ function instanceService(instanceName: string) {
33
+ const instance = Object.create(ServiceInstance.prototype) as ServiceInstance;
34
+ Object.defineProperties(instance, {
35
+ app: { value: app },
36
+ ctx: { value: { app, instanceName } },
37
+ $scope: { value: { startup: { service: { startup: service } } } },
38
+ getConfigInstanceBase: { value: () => ({}) },
39
+ _cacheInstanceConfig: { value: async () => {} },
40
+ });
41
+ return instance;
42
+ }
20
43
  Object.defineProperties(service, {
21
44
  app: { value: app },
22
45
  _startups: { value: startups },
@@ -26,6 +49,9 @@ function fixture(created: string[] = []) {
26
49
  version: {
27
50
  service: {
28
51
  database: {
52
+ prepareDevDatabases: async () => {
53
+ calls.push('prepareDevDatabases');
54
+ },
29
55
  get createdInstanceNames() {
30
56
  return [...pending];
31
57
  },
@@ -42,42 +68,115 @@ function fixture(created: string[] = []) {
42
68
  service: {
43
69
  instance: {
44
70
  instanceStartupIsolate: async (name: string, options: any) =>
45
- service.runStartupInstance(name as never, options),
71
+ instanceService(name).instanceStartup(name as never, options),
46
72
  },
47
73
  },
48
74
  },
49
75
  },
50
76
  },
51
- runStartup: {
52
- value: async (name: string, instance: string) => {
53
- calls.push(`${name}:${instance}`);
54
- },
55
- },
56
77
  });
57
- return { service, app, calls, pending };
78
+ service.runStartup = async (name, instance, options) => {
79
+ calls.push(`${name}:${instance}`);
80
+ optionsSeen.push({ name, instance: instance as string | null, force: options?.force });
81
+ if (
82
+ startups.find(startup => startup.name === name)?.beanOptions.options.after === true &&
83
+ instance !== null
84
+ ) {
85
+ assert.equal(app.meta.appReadyInstances[instance as string], true);
86
+ }
87
+ };
88
+ return { service, app, calls, pending, instanceService, optionsSeen, startups };
58
89
  }
59
90
 
91
+ const defaultCalls = [
92
+ 'a-version:instanceInit:',
93
+ 'seed:',
94
+ 'a-schedule:loadSchedules:',
95
+ 'a-runtime:runtime:',
96
+ 'a-printtip:printTip:',
97
+ 'a-queue:loadQueueWorkers:',
98
+ ];
99
+ const isolateCalls = [
100
+ 'a-version:instanceInit:isolateTest',
101
+ 'a-schedule:loadSchedules:isolateTest',
102
+ 'a-runtime:runtime:isolateTest',
103
+ 'a-printtip:printTip:isolateTest',
104
+ 'a-queue:loadQueueWorkers:isolateTest',
105
+ ];
106
+
60
107
  describe('creation-driven dev seed', { concurrency: false }, () => {
61
- it('seeds fresh instances before runtime, queues, and listening', async () => {
62
- const { service, calls, pending, app } = fixture(['', 'isolateTest']);
108
+ let previousReset: string | undefined;
109
+ beforeEach(() => {
110
+ previousReset = process.env.VONA_DB_RESET;
111
+ delete process.env.VONA_DB_RESET;
112
+ });
113
+ afterEach(() => {
114
+ if (previousReset === undefined) delete process.env.VONA_DB_RESET;
115
+ else process.env.VONA_DB_RESET = previousReset;
116
+ });
117
+
118
+ it('skips all after-startups during reset while completing data preparation', async () => {
119
+ process.env.VONA_DB_RESET = 'true';
120
+ const { service, app, calls, pending, optionsSeen, startups } = fixture(['', 'isolateTest']);
121
+ startups.push(
122
+ { name: 'test:prepareApp', beanOptions: { options: { after: false } } },
123
+ { name: 'test:prepareInstance', beanOptions: { options: { instance: true, after: false } } },
124
+ { name: 'test:afterApp', beanOptions: { options: { after: true } } },
125
+ { name: 'test:afterInstance', beanOptions: { options: { instance: true, after: true } } },
126
+ );
127
+ await service.appStart();
63
128
  await service.appReady();
129
+ assert.deepEqual(calls, [
130
+ 'prepareDevDatabases',
131
+ 'a-version:databaseInit:null',
132
+ 'a-version:databaseName:null',
133
+ 'test:prepareApp:null',
134
+ 'a-version:instanceInit:',
135
+ 'test:prepareInstance:',
136
+ 'seed:',
137
+ 'a-version:instanceInit:isolateTest',
138
+ 'test:prepareInstance:isolateTest',
139
+ ]);
140
+ assert.equal(app.config.server.listen.disable, true);
64
141
  assert.equal(pending.size, 0);
65
- const lastSeed = calls.indexOf('seed:isolateTest');
66
- assert.ok(lastSeed > calls.indexOf('seed:'));
67
- for (const call of ['a-runtime:runtime:', 'a-queue:loadQueueWorkers:', 'a-web:listen:null']) {
68
- assert.ok(calls.indexOf(call) > lastSeed, `${call} must follow all seeds`);
69
- }
142
+ assert.deepEqual(app.meta.appReadyInstances, { '': true, 'isolateTest': true });
143
+ assert.equal(optionsSeen.find(call => call.name === 'a-version:databaseInit')?.force, true);
144
+ assert.ok(
145
+ optionsSeen.filter(call => call.instance !== null).every(call => call.force === true),
146
+ );
147
+ });
148
+
149
+ it('retains after-startups when the reset flag is false', async () => {
150
+ process.env.VONA_DB_RESET = 'false';
151
+ const { service, calls } = fixture(['', 'isolateTest']);
152
+ await service.appReady();
153
+ assert.deepEqual(calls, [...defaultCalls, ...isolateCalls, 'a-web:listen:null']);
154
+ });
155
+
156
+ it('seeds only default and completes each instance before application after-startups', async () => {
157
+ const { service, calls, pending, app, optionsSeen, instanceService } = fixture([
158
+ '',
159
+ 'isolateTest',
160
+ ]);
161
+ await service.appReady();
162
+ assert.equal(pending.size, 0);
163
+ assert.deepEqual(calls, [...defaultCalls, ...isolateCalls, 'a-web:listen:null']);
70
164
  assert.equal(app.meta.appReadyInstances[''], true);
71
165
  assert.equal(app.meta.appReadyInstances.isolateTest, true);
72
- await service.runStartupInstance('' as never);
166
+ assert.ok(
167
+ optionsSeen.filter(call => call.instance !== null).every(call => call.force === true),
168
+ );
169
+ await instanceService('').instanceStartup('' as never, { force: true });
73
170
  assert.equal(calls.filter(call => call === 'seed:').length, 1);
74
171
  });
75
172
 
76
- it('does not seed existing default when only an isolated database is new', async () => {
77
- const { service, calls } = fixture(['isolateTest']);
173
+ it('does not seed either instance when only an isolated database is new', async () => {
174
+ const { service, calls, pending, app } = fixture(['isolateTest']);
78
175
  await service.appReady();
79
- assert.ok(calls.includes('seed:isolateTest'));
80
- assert.ok(!calls.includes('seed:'));
176
+ assert.ok(!calls.some(call => call.startsWith('seed:')));
177
+ assert.ok(calls.includes('a-version:instanceInit:isolateTest'));
178
+ assert.equal(pending.size, 0);
179
+ assert.equal(app.meta.appReadyInstances.isolateTest, true);
81
180
  });
82
181
 
83
182
  it('does not seed when all databases are reused', async () => {
@@ -86,19 +185,172 @@ describe('creation-driven dev seed', { concurrency: false }, () => {
86
185
  assert.ok(!calls.some(call => call.startsWith('seed:')));
87
186
  });
88
187
 
89
- it('fails before after-startups and readiness if seeding fails', async () => {
90
- const { service, app, calls, pending } = fixture(['']);
188
+ it('retains default-only test seeding through its existing lifecycle even with the reset flag', async () => {
189
+ process.env.VONA_DB_RESET = 'true';
190
+ const { service, app, calls } = fixture();
191
+ app.meta.isDev = false;
192
+ app.meta.isTest = true;
193
+ await service.appReady();
194
+ assert.deepEqual(calls, [
195
+ 'a-web:listen:null',
196
+ 'a-version:instanceInit:',
197
+ 'a-schedule:loadSchedules:',
198
+ 'a-runtime:runtime:',
199
+ 'a-printtip:printTip:',
200
+ 'a-queue:loadQueueWorkers:',
201
+ 'seed:',
202
+ ]);
203
+ });
204
+
205
+ it('fails before after-startups and readiness if default seeding fails', async () => {
206
+ const { service, app, calls, pending } = fixture(['', 'isolateTest']);
91
207
  (service as any).$scope.version.service.version.__instanceSeed = async () => {
92
208
  throw new Error('seed failed');
93
209
  };
94
210
  await assert.rejects(service.appReady(), /seed failed/);
95
- assert.ok(pending.has(''));
211
+ assert.deepEqual([...pending], ['', 'isolateTest']);
96
212
  assert.deepEqual(app.meta.appReadyInstances, {});
97
- assert.ok(
98
- !calls.some(
99
- call =>
100
- call.includes('runtime') || call.includes('listen') || call.includes('loadQueueWorkers'),
101
- ),
102
- );
213
+ assert.deepEqual(calls, ['a-version:instanceInit:']);
214
+ });
215
+
216
+ it('retains completed default readiness if later isolate initialization fails', async () => {
217
+ const { service, app, calls, pending } = fixture(['', 'isolateTest']);
218
+ const runStartup = service.runStartup.bind(service);
219
+ service.runStartup = async (name, instance, options) => {
220
+ if (name === 'a-version:instanceInit' && instance === 'isolateTest') {
221
+ throw new Error('isolate init failed');
222
+ }
223
+ await runStartup(name, instance, options);
224
+ };
225
+ await assert.rejects(service.appReady(), /isolate init failed/);
226
+ assert.deepEqual([...pending], ['isolateTest']);
227
+ assert.deepEqual(app.meta.appReadyInstances, { '': true });
228
+ assert.deepEqual(calls, defaultCalls);
229
+ });
230
+
231
+ for (const failure of ['seed', 'isolate init']) {
232
+ it(`preserves reset failure state when ${failure} fails`, async () => {
233
+ process.env.VONA_DB_RESET = 'true';
234
+ const { service, app, calls, pending } = fixture(['', 'isolateTest']);
235
+ if (failure === 'seed') {
236
+ (service as any).$scope.version.service.version.__instanceSeed = async () => {
237
+ throw new Error('seed failed');
238
+ };
239
+ } else {
240
+ const runStartup = service.runStartup.bind(service);
241
+ service.runStartup = async (name, instance, options) => {
242
+ if (name === 'a-version:instanceInit' && instance === 'isolateTest') {
243
+ throw new Error('isolate init failed');
244
+ }
245
+ await runStartup(name, instance, options);
246
+ };
247
+ }
248
+ await assert.rejects(service.appReady(), new RegExp(`${failure} failed`));
249
+ assert.deepEqual([...pending], failure === 'seed' ? ['', 'isolateTest'] : ['isolateTest']);
250
+ assert.deepEqual(app.meta.appReadyInstances, failure === 'seed' ? {} : { '': true });
251
+ assert.deepEqual(
252
+ calls,
253
+ failure === 'seed' ? ['a-version:instanceInit:'] : ['a-version:instanceInit:', 'seed:'],
254
+ );
255
+ });
256
+ }
257
+
258
+ for (const [phase, reset] of [
259
+ ['init', false],
260
+ ['seed', false],
261
+ ['init', true],
262
+ ['seed', true],
263
+ ] as const) {
264
+ it(`skips queued broadcast-style startup requests received during ${phase} (reset=${reset})`, async () => {
265
+ if (reset) process.env.VONA_DB_RESET = 'true';
266
+ const { service, calls, instanceService, app, pending } = fixture(['']);
267
+ const entered = Promise.withResolvers<void>();
268
+ const release = Promise.withResolvers<void>();
269
+ const enqueued = Promise.withResolvers<void>();
270
+ if (phase === 'init') {
271
+ const runStartup = service.runStartup.bind(service);
272
+ service.runStartup = async (name, instance, options) => {
273
+ await runStartup(name, instance, options);
274
+ if (name === 'a-version:instanceInit') {
275
+ entered.resolve();
276
+ await release.promise;
277
+ }
278
+ };
279
+ } else {
280
+ (service as any).$scope.version.service.version.__instanceSeed = async () => {
281
+ calls.push('seed:');
282
+ entered.resolve();
283
+ await release.promise;
284
+ };
285
+ }
286
+ const startup = service.appReady();
287
+ await entered.promise;
288
+ const competitor = instanceService('');
289
+ const queue = competitor.__queueInstanceStartup[''];
290
+ const push = queue.push.bind(queue);
291
+ queue.push = (...args: unknown[]) => {
292
+ const result = push(...args);
293
+ enqueued.resolve();
294
+ return result;
295
+ };
296
+ let delivered = false;
297
+ const delivery = competitor.checkAppReadyInstance(true).then(() => {
298
+ delivered = true;
299
+ });
300
+ try {
301
+ await enqueued.promise;
302
+ assert.equal(delivered, false);
303
+ } finally {
304
+ release.resolve();
305
+ await Promise.all([startup, delivery]);
306
+ }
307
+ assert.equal(delivered, true);
308
+ assert.equal(app.meta.appReadyInstances[''], true);
309
+ assert.equal(pending.size, 0);
310
+ assert.deepEqual(
311
+ calls,
312
+ reset ? ['a-version:instanceInit:', 'seed:'] : [...defaultCalls, 'a-web:listen:null'],
313
+ );
314
+ });
315
+ }
316
+
317
+ it('preserves forced after-startup debounce semantics', async t => {
318
+ const service = Object.create(ServiceStartup.prototype) as ServiceStartup;
319
+ const getset = t.mock.fn(async () => true);
320
+ const execute = t.mock.fn(async () => {});
321
+ Object.defineProperties(service, {
322
+ app: { value: { meta: { isTest: false } } },
323
+ ctx: { value: { instance: { id: 1 } } },
324
+ scope: {
325
+ value: {
326
+ config: { startup: { debounce: 1000 } },
327
+ cacheRedis: { startupDebounce: { getset } },
328
+ },
329
+ },
330
+ _runStartupInner: { value: execute },
331
+ });
332
+ const startup = {
333
+ name: 'a-runtime:runtime',
334
+ beanOptions: { options: { instance: true, after: true, debounce: true } },
335
+ };
336
+ await service._runStartupLock(startup as never, '' as never, { force: true });
337
+ assert.equal(getset.mock.callCount(), 0);
338
+ assert.equal(execute.mock.callCount(), 1);
339
+ await service._runStartupLock(startup as never, '' as never, { force: false });
340
+ assert.equal(getset.mock.callCount(), 1);
341
+ assert.equal(execute.mock.callCount(), 1);
342
+ });
343
+
344
+ it('allows readiness checks during after-startups without reentering its queue', async () => {
345
+ const { service, calls, instanceService } = fixture(['']);
346
+ const runStartup = service.runStartup.bind(service);
347
+ service.runStartup = async (name, instance, options) => {
348
+ await runStartup(name, instance, options);
349
+ if (name === 'a-runtime:runtime') {
350
+ assert.equal(await instanceService('').checkAppReadyInstance(true), true);
351
+ }
352
+ };
353
+ await service.appReady();
354
+ assert.deepEqual(calls, [...defaultCalls, 'a-web:listen:null']);
103
355
  });
104
356
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-version",
3
- "version": "5.1.20",
3
+ "version": "5.1.21",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona-module-a-version",
6
6
  "keywords": [
@@ -63,6 +63,9 @@ export class ServiceVersion extends BeanBase {
63
63
 
64
64
  async __instanceSeed(instanceName: keyof IInstanceRecord) {
65
65
  await this.__check({ scene: 'seed', instanceName });
66
+ this.$logger.silly(
67
+ chalk.cyan(`The instance is seeded successfully: ${instanceName || 'default'}`),
68
+ );
66
69
  }
67
70
 
68
71
  // scene: update/init/seed
@@ -0,0 +1,47 @@
1
+ import chalk from 'chalk';
2
+ import assert from 'node:assert/strict';
3
+ import { describe, it } from 'node:test';
4
+
5
+ import { ServiceVersion } from '../src/service/version.ts';
6
+
7
+ function fixture(check: ServiceVersion['__check']) {
8
+ const messages: string[] = [];
9
+ const service = Object.create(ServiceVersion.prototype) as ServiceVersion;
10
+ Object.defineProperties(service, {
11
+ __check: { value: check },
12
+ $logger: { value: { silly: (message: string) => messages.push(message) } },
13
+ });
14
+ return { service, messages };
15
+ }
16
+
17
+ describe('instance seed completion', () => {
18
+ it('logs default completion only after seeding succeeds', async () => {
19
+ const entered = Promise.withResolvers<void>();
20
+ const release = Promise.withResolvers<void>();
21
+ const { service, messages } = fixture(async options => {
22
+ assert.equal(options.scene, 'seed');
23
+ assert.equal(options.instanceName, '');
24
+ entered.resolve();
25
+ await release.promise;
26
+ return {};
27
+ });
28
+ const seeding = service.__instanceSeed('');
29
+ try {
30
+ await entered.promise;
31
+ assert.deepEqual(messages, []);
32
+ } finally {
33
+ release.resolve();
34
+ await seeding;
35
+ }
36
+ assert.deepEqual(messages, [chalk.cyan('The instance is seeded successfully: default')]);
37
+ });
38
+
39
+ it('propagates seed failure without logging success', async () => {
40
+ const error = new Error('seed failed');
41
+ const { service, messages } = fixture(async () => {
42
+ throw error;
43
+ });
44
+ await assert.rejects(service.__instanceSeed(''), error);
45
+ assert.deepEqual(messages, []);
46
+ });
47
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-vona",
3
- "version": "5.1.99",
3
+ "version": "5.1.100",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -455,7 +455,7 @@ importers:
455
455
  specifier: ^7.6.2
456
456
  version: 7.8.5
457
457
  zova-cli-set-front:
458
- specifier: ^1.2.109
458
+ specifier: ^1.2.111
459
459
  version: link:../cli-set-front
460
460
  devDependencies:
461
461
  clean-package:
@@ -876,7 +876,7 @@ importers:
876
876
  specifier: ^5.1.92
877
877
  version: link:../zova-core
878
878
  zova-suite-a-zova:
879
- specifier: ^5.1.166
879
+ specifier: ^5.1.170
880
880
  version: link:../../src/suite-vendor/a-zova
881
881
  devDependencies:
882
882
  clean-package:
@@ -986,7 +986,7 @@ importers:
986
986
  specifier: ^5.0.1
987
987
  version: link:modules/a-rbac
988
988
  zova-module-rest-resource:
989
- specifier: ^5.1.55
989
+ specifier: ^5.1.56
990
990
  version: link:modules/rest-resource
991
991
 
992
992
  src/suite-vendor/a-cabloy/modules/a-layoutprofile:
@@ -1091,7 +1091,7 @@ importers:
1091
1091
  specifier: ^5.1.25
1092
1092
  version: link:modules/a-fetch
1093
1093
  zova-module-a-form:
1094
- specifier: ^5.1.51
1094
+ specifier: ^5.1.54
1095
1095
  version: link:modules/a-form
1096
1096
  zova-module-a-icon:
1097
1097
  specifier: ^5.1.27
@@ -1106,19 +1106,19 @@ importers:
1106
1106
  specifier: ^5.1.22
1107
1107
  version: link:modules/a-meta
1108
1108
  zova-module-a-model:
1109
- specifier: ^5.1.37
1109
+ specifier: ^5.1.38
1110
1110
  version: link:modules/a-model
1111
1111
  zova-module-a-openapi:
1112
- specifier: ^5.1.55
1112
+ specifier: ^5.1.57
1113
1113
  version: link:modules/a-openapi
1114
1114
  zova-module-a-router:
1115
- specifier: ^5.1.41
1115
+ specifier: ^5.1.42
1116
1116
  version: link:modules/a-router
1117
1117
  zova-module-a-routerstack:
1118
1118
  specifier: ^5.1.29
1119
1119
  version: link:modules/a-routerstack
1120
1120
  zova-module-a-routertabs:
1121
- specifier: ^5.1.34
1121
+ specifier: ^5.1.35
1122
1122
  version: link:modules/a-routertabs
1123
1123
  zova-module-a-ssr:
1124
1124
  specifier: ^5.1.40