cabloy 5.1.203 → 5.1.205
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/.gitignore +2 -0
- package/CHANGELOG.md +21 -0
- package/lint-staged.config.mjs +2 -0
- package/oxfmt.config.ts +2 -0
- package/oxlint.config.ts +2 -0
- package/package.json +6 -5
- package/repo-agent-governance/managed-assets.json +9 -9
- package/repo-agent-governance/policies/repository.md +5 -2
- package/repo-agent-governance/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +2 -2
- package/repo-agent-governance/skills/cabloy-workflow/SKILL.md +2 -0
- package/repo-docs/backend/migration-and-changes.md +13 -12
- package/repo-docs/backend/orm-select-guide.md +6 -0
- package/repo-docs/backend/runtime-and-flavors.md +2 -1
- package/repo-docs/backend/transaction-guide.md +12 -0
- package/repo-docs/backend/unit-testing.md +12 -8
- package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/cover-en-v1.png +0 -0
- package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/completion-summary.jpg +0 -0
- package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/multitasking-draft.jpg +0 -0
- package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/ssr-refresh.jpg +0 -0
- package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/index.md +216 -0
- package/repo-docs/blogs/index.md +15 -0
- package/repo-docs/frontend/scripts.md +1 -1
- package/repo-docs/fullstack/parallel-worktree-environment.md +6 -6
- package/repo-docs/fullstack/ssr-entry-modes.md +19 -0
- package/repo-docs/index.md +2 -0
- package/repo-docs/reference/repo-scripts.md +22 -1
- package/repo-e2e/config/playwright.config.ts +32 -14
- package/repo-e2e/docs/specs/blogs-index.spec.ts +23 -6
- package/repo-e2e/scripts/e2e.test.ts +39 -0
- package/repo-e2e/scripts/e2e.ts +19 -1
- package/repo-e2e/scripts/managedTest.test.ts +389 -0
- package/repo-e2e/scripts/managedTest.ts +210 -0
- package/repo-e2e/scripts/managedTestImports.test.ts +74 -0
- package/repo-e2e/scripts/managedTestImports.ts +110 -0
- package/repo-e2e/scripts/runE2e.ts +12 -1
- package/repo-e2e/scripts/startE2eVona.ts +37 -5
- package/repo-e2e/specs/a-commerce.spec.ts +1 -1
- package/repo-e2e/specs/cabloy-basic.spec.ts +1 -1
- package/repo-e2e/specs/controller-boundary.spec.ts +1 -1
- package/repo-e2e/specs/home-user-account.spec.ts +2 -1
- package/repo-e2e/specs/markdown.spec.ts +1 -1
- package/repo-e2e/specs/routed-dialog.spec.ts +1 -1
- package/scripts/runIsolatedTest.test.ts +426 -0
- package/scripts/runIsolatedTest.ts +454 -0
- package/scripts/testClaimCleanup.test.ts +346 -0
- package/scripts/testClaimCleanup.ts +375 -0
- package/scripts/testClaimProcesses.test.ts +106 -0
- package/scripts/testClaimProcesses.ts +129 -0
- package/scripts/testProcessGuardian.ts +163 -0
- package/scripts/upgrade.ts +1 -16
- package/vona/oxfmt.config.ts +2 -0
- package/vona/oxlint.config.ts +2 -0
- package/vona/package.original.json +2 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli/src/play.test.ts +37 -0
- package/vona/packages-cli/cli/src/play.ts +23 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dbReset.ts +16 -7
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dev.ts +13 -7
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.play.ts +5 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.test.ts +12 -7
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/configUtils.ts +21 -2
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/generateVonaMeta.ts +10 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.test.ts +153 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.ts +141 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.test.ts +148 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.ts +127 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.test.ts +47 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.ts +139 -0
- package/vona/packages-cli/cli-set-api/src/lib/utils.ts +2 -3
- package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_project.json +1 -0
- package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_source.json +1 -0
- package/vona/packages-cli/cli-set-api/test/binDevMode.test.ts +18 -0
- package/vona/packages-cli/cli-set-api/toolsIsolate/dbReset.ts +15 -2
- package/vona/packages-cli/cli-set-api/tsconfig.build.json +2 -1
- package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +1 -1
- package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.test.ts +16 -0
- package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.ts +3 -2
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/addon/sqlite3.ts +1 -1
- package/vona/packages-vona/vona-core/src/lib/core/config.ts +44 -6
- package/vona/packages-vona/vona-core/src/lib/core/configCleanup.test.ts +40 -0
- package/vona/packages-vona/vona-core/src/lib/core/index.ts +3 -0
- package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.test.ts +301 -0
- package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.ts +312 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts +577 -0
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.ts +985 -0
- package/vona/packages-vona/vona-core/src/lib/core/testIsolation.ts +129 -0
- package/vona/packages-vona/vona-core/src/lib/framework/createApp.ts +2 -0
- package/vona/packages-vona/vona-core/src/lib/framework/start.ts +4 -0
- package/vona/packages-vona/vona-core/src/lib/framework/useApp.ts +5 -1
- package/vona/packages-vona/vona-core/src/types/application/app.ts +2 -0
- package/vona/packages-vona/vona-core/src/types/utils/env.ts +1 -0
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +40 -40
- package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentIndexes.test.ts +73 -10
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-logger/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/bean/middlewareSystem.httpLog.ts +15 -1
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts +24 -0
- package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.ts +11 -0
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.health.ts +15 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/extend/schemaBuilder.ts +11 -4
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/databaseClient_.ts +6 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.betterSqlite3.ts +41 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.mysql.ts +4 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.pg.ts +2 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-redis/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-redis/src/lib/redis.ts +8 -0
- package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redis.ts +23 -6
- package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redisClient_.ts +18 -1
- 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 +43 -15
- 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/lib/databaseTestName.ts +3 -37
- package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/database.ts +93 -15
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/vona/tsconfig.json +1 -0
- package/scripts/initTestData.ts +0 -25
|
@@ -4,7 +4,15 @@ import type { IDatabaseClientRecord, ServiceDatabaseClient } from 'vona-module-a
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { DateTime } from 'luxon';
|
|
7
|
-
import
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import {
|
|
9
|
+
BeanBase,
|
|
10
|
+
assertDisposableDatabaseName as assertDisposableDatabaseNameCore,
|
|
11
|
+
assertIsolatedTestPath,
|
|
12
|
+
assertTestRunOwner,
|
|
13
|
+
resolveVonaLocalPaths,
|
|
14
|
+
withIsolatedTestDatabaseDiscovery,
|
|
15
|
+
} from 'vona';
|
|
8
16
|
import { Service } from 'vona-module-a-bean';
|
|
9
17
|
|
|
10
18
|
import { createDatabaseTestName, getDatabaseTestPrefix } from '../lib/databaseTestName.ts';
|
|
@@ -12,6 +20,21 @@ import { createDatabaseTestName, getDatabaseTestPrefix } from '../lib/databaseTe
|
|
|
12
20
|
const __separator = '-';
|
|
13
21
|
const __timeFormat = `yyyyMMdd${__separator}HHmmss`;
|
|
14
22
|
|
|
23
|
+
export function assertDisposableDatabaseName(
|
|
24
|
+
databaseName: string,
|
|
25
|
+
databasePrefix: string,
|
|
26
|
+
sqliteRoot?: string,
|
|
27
|
+
) {
|
|
28
|
+
const filename = sqliteRoot ? path.basename(databaseName, '.db') : databaseName;
|
|
29
|
+
assertDisposableDatabaseNameCore(filename, databasePrefix);
|
|
30
|
+
if (sqliteRoot) {
|
|
31
|
+
if (!path.isAbsolute(databaseName) || path.extname(databaseName) !== '.db') {
|
|
32
|
+
throw new Error(`Database is outside the disposable SQLite directory: ${databaseName}`);
|
|
33
|
+
}
|
|
34
|
+
assertIsolatedTestPath(databaseName, path.join(sqliteRoot, `${filename}.db`));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
@Service()
|
|
16
39
|
export class ServiceDatabase extends BeanBase {
|
|
17
40
|
get configDatabase() {
|
|
@@ -42,9 +65,14 @@ export class ServiceDatabase extends BeanBase {
|
|
|
42
65
|
instanceName?: keyof IInstanceRecord,
|
|
43
66
|
configInstanceBase?: ConfigInstanceBase,
|
|
44
67
|
) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
const clientNameReal =
|
|
69
|
+
clientName === 'default' ? (this.configDatabase.defaultClient as string) : clientName;
|
|
70
|
+
const clientConfig = this.configDatabase.clients[clientNameReal];
|
|
71
|
+
await withIsolatedTestDatabaseDiscovery(this.app, clientNameReal, clientConfig, async () => {
|
|
72
|
+
await this.bean.database.switchDb(async () => {
|
|
73
|
+
await this.__prepareDatabase(versionStart, instanceName, configInstanceBase);
|
|
74
|
+
}, clientName);
|
|
75
|
+
});
|
|
48
76
|
}
|
|
49
77
|
|
|
50
78
|
private async __fetchDatabases(
|
|
@@ -94,13 +122,14 @@ export class ServiceDatabase extends BeanBase {
|
|
|
94
122
|
instanceName?: keyof IInstanceRecord,
|
|
95
123
|
configInstanceBase?: ConfigInstanceBase,
|
|
96
124
|
) {
|
|
97
|
-
await this.__prepareDatabaseInner(instanceName, configInstanceBase);
|
|
125
|
+
await this.__prepareDatabaseInner(versionStart, instanceName, configInstanceBase);
|
|
98
126
|
if (versionStart) {
|
|
99
127
|
await this.scope.service.version.__start();
|
|
100
128
|
}
|
|
101
129
|
}
|
|
102
130
|
|
|
103
131
|
private async __prepareDatabaseInner(
|
|
132
|
+
versionStart: boolean,
|
|
104
133
|
instanceName?: keyof IInstanceRecord,
|
|
105
134
|
configInstanceBase?: ConfigInstanceBase,
|
|
106
135
|
) {
|
|
@@ -109,26 +138,71 @@ export class ServiceDatabase extends BeanBase {
|
|
|
109
138
|
return;
|
|
110
139
|
}
|
|
111
140
|
const databasePrefix = this.getDatabasePrefix(instanceName, configInstanceBase);
|
|
141
|
+
if (this.app.meta.env.VONA_TEST_RUN_ID) {
|
|
142
|
+
const paths = resolveVonaLocalPaths(
|
|
143
|
+
this.app.projectPath,
|
|
144
|
+
this.app.meta.env.META_MODE,
|
|
145
|
+
this.app.meta.env.VONA_TEST_RUN_ID,
|
|
146
|
+
);
|
|
147
|
+
assertTestRunOwner(paths.dataRoot, paths.runId!);
|
|
148
|
+
}
|
|
112
149
|
// client
|
|
113
150
|
const client = this.bean.database.current.client;
|
|
114
151
|
// get current database name
|
|
115
152
|
let databaseName = client.getDatabaseName();
|
|
116
|
-
const isTestDatabase = databaseName.includes(databasePrefix);
|
|
117
153
|
// dev/debug db
|
|
118
154
|
if (this.app.meta.isDev) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
const reset = versionStart && process.env.VONA_DB_RESET === 'true';
|
|
156
|
+
// Never drop a configured, non-disposable development database.
|
|
157
|
+
if (reset && !this.configDatabase.testDatabase) {
|
|
158
|
+
throw new Error('db:reset requires database.testDatabase=true in the dev configuration');
|
|
159
|
+
}
|
|
160
|
+
if (!this.configDatabase.testDatabase) return;
|
|
161
|
+
}
|
|
162
|
+
const sqliteRoot =
|
|
163
|
+
client.db.dialectName === 'better-sqlite3'
|
|
164
|
+
? resolveVonaLocalPaths(
|
|
165
|
+
this.app.projectPath,
|
|
166
|
+
this.app.meta.env.META_MODE,
|
|
167
|
+
this.app.meta.env.VONA_TEST_RUN_ID,
|
|
168
|
+
).sqliteRoot
|
|
169
|
+
: undefined;
|
|
170
|
+
const currentFilename = sqliteRoot ? path.basename(databaseName, '.db') : databaseName;
|
|
171
|
+
const isTestDatabase = currentFilename.startsWith(databasePrefix);
|
|
172
|
+
if (isTestDatabase) assertDisposableDatabaseName(databaseName, databasePrefix, sqliteRoot);
|
|
173
|
+
if (
|
|
174
|
+
this.app.meta.isTest &&
|
|
175
|
+
this.app.meta.env.VONA_TEST_RUN_ID &&
|
|
176
|
+
currentFilename.startsWith('vona-test-') &&
|
|
177
|
+
!isTestDatabase
|
|
178
|
+
) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Isolated test database is outside its disposable namespace: ${databaseName}`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
// dev/debug db
|
|
184
|
+
if (this.app.meta.isDev) {
|
|
185
|
+
const reset = versionStart && process.env.VONA_DB_RESET === 'true';
|
|
186
|
+
// The database used to enumerate/drop databases must not itself be dropped.
|
|
187
|
+
if (isTestDatabase) {
|
|
188
|
+
if (reset)
|
|
189
|
+
throw new Error('db:reset requires a connection outside the disposable database');
|
|
124
190
|
return;
|
|
125
191
|
}
|
|
126
192
|
const dbs = await this.__fetchDatabases(client, instanceName, configInstanceBase);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
193
|
+
for (const db of dbs) {
|
|
194
|
+
assertDisposableDatabaseName(db.name, databasePrefix, sqliteRoot);
|
|
195
|
+
}
|
|
196
|
+
if (reset) {
|
|
197
|
+
for (const db of dbs) {
|
|
198
|
+
await client.connection.schema.dropDatabase(db.name);
|
|
199
|
+
}
|
|
131
200
|
}
|
|
201
|
+
databaseName =
|
|
202
|
+
reset || dbs.length === 0
|
|
203
|
+
? await this.__createDatabase(client, instanceName, configInstanceBase)
|
|
204
|
+
: dbs[0].name;
|
|
205
|
+
assertDisposableDatabaseName(databaseName, databasePrefix, sqliteRoot);
|
|
132
206
|
// set config and reload client
|
|
133
207
|
await client.changeConfigConnectionAndReloadWorker(databaseName);
|
|
134
208
|
this.$logger.silly(chalk.cyan(`dialect: ${client.db.dialectName}`));
|
|
@@ -143,11 +217,15 @@ export class ServiceDatabase extends BeanBase {
|
|
|
143
217
|
}
|
|
144
218
|
// drop old databases
|
|
145
219
|
const dbs = await this.__fetchDatabases(client, instanceName, configInstanceBase);
|
|
220
|
+
for (const db of dbs) {
|
|
221
|
+
assertDisposableDatabaseName(db.name, databasePrefix, sqliteRoot);
|
|
222
|
+
}
|
|
146
223
|
for (const db of dbs) {
|
|
147
224
|
await client.connection.schema.dropDatabase(db.name);
|
|
148
225
|
}
|
|
149
226
|
// create database
|
|
150
227
|
const databaseName = await this.__createDatabase(client, instanceName, configInstanceBase);
|
|
228
|
+
assertDisposableDatabaseName(databaseName, databasePrefix, sqliteRoot);
|
|
151
229
|
// set config and reload client
|
|
152
230
|
await client.changeConfigConnectionAndReloadWorker(databaseName);
|
|
153
231
|
// database ready
|
package/vona/tsconfig.json
CHANGED
package/scripts/initTestData.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { dirname, resolve } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const ROOT_DIR = resolve(__dirname, '..');
|
|
7
|
-
|
|
8
|
-
function exec(cmd: string): void {
|
|
9
|
-
execSync(cmd, { stdio: 'inherit', cwd: ROOT_DIR });
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function initTestData(): void {
|
|
13
|
-
// eslint-disable-next-line no-console
|
|
14
|
-
console.log('[init:test-data] Initializing test data via npm run test...');
|
|
15
|
-
try {
|
|
16
|
-
exec('npm run test');
|
|
17
|
-
} catch {
|
|
18
|
-
// eslint-disable-next-line no-console
|
|
19
|
-
console.warn(
|
|
20
|
-
'[init:test-data] npm run test failed after init completed; Redis may be unavailable, skipping test data initialization',
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
initTestData();
|