cabloy 5.1.213 → 5.1.215
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/.github/workflows/agent-governance.yml +19 -0
- package/CHANGELOG.md +14 -0
- package/package.json +3 -1
- package/repo-agent-governance/managed-assets.json +18 -18
- package/repo-agent-governance/policies/repository.md +3 -0
- package/repo-agent-governance/scripts/pack-check.mjs +1 -0
- package/repo-agent-governance/skills/cabloy-spec-execution/SKILL.md +7 -1
- package/repo-agent-governance/skills/cabloy-spec-execution/references/execution-protocol.md +4 -2
- package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md +28 -1
- package/repo-agent-governance/skills/cabloy-spec-generation/SKILL.md +10 -0
- package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +2 -0
- package/repo-agent-governance/tests/evidence-paths.test.mjs +736 -0
- package/repo-agent-governance/tests/governance.test.mjs +25 -0
- package/repo-agent-governance/tools/evidence-paths/sanitize.mjs +549 -0
- package/repo-docs/ai/ai-spec-driven-development.md +12 -0
- package/repo-docs/ai/playbook-spec-execution.md +20 -2
- package/repo-docs/backend/migration-and-changes.md +5 -1
- package/repo-docs/backend/startup-guide.md +16 -1
- package/repo-docs/reference/repo-scripts.md +69 -1
- package/scripts/bootstrapAgentGovernance.mjs +2 -0
- package/scripts/upgrade.ts +2 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +139 -29
- package/vona/src/suite-vendor/a-vona/modules/a-printtip/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-printtip/src/bean/startup.printTip.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-printtip/test/printTip.test.ts +46 -0
- package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +16 -21
- package/vona/src/suite-vendor/a-vona/modules/a-startup/src/types/startup.ts +0 -1
- package/vona/src/suite-vendor/a-vona/modules/a-startup/test/devSeed.test.ts +282 -30
- package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/version.ts +3 -0
- package/vona/src/suite-vendor/a-vona/modules/a-version/test/instanceSeed.test.ts +47 -0
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- 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: {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
66
|
-
assert.
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
80
|
-
assert.ok(
|
|
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('
|
|
90
|
-
|
|
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.
|
|
211
|
+
assert.deepEqual([...pending], ['', 'isolateTest']);
|
|
96
212
|
assert.deepEqual(app.meta.appReadyInstances, {});
|
|
97
|
-
assert.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
});
|
|
@@ -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
|
+
});
|
package/zova/pnpm-lock.yaml
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1109
|
+
specifier: ^5.1.38
|
|
1110
1110
|
version: link:modules/a-model
|
|
1111
1111
|
zova-module-a-openapi:
|
|
1112
|
-
specifier: ^5.1.
|
|
1112
|
+
specifier: ^5.1.57
|
|
1113
1113
|
version: link:modules/a-openapi
|
|
1114
1114
|
zova-module-a-router:
|
|
1115
|
-
specifier: ^5.1.
|
|
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.
|
|
1121
|
+
specifier: ^5.1.35
|
|
1122
1122
|
version: link:modules/a-routertabs
|
|
1123
1123
|
zova-module-a-ssr:
|
|
1124
1124
|
specifier: ^5.1.40
|