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
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { register, createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
// Register before dynamically importing source config/framework (decorators and
|
|
6
|
+
// workspace TS exports). Dependencies resolve from this checkout's Vona install.
|
|
7
|
+
register(import.meta.resolve('ts-node-maintained/esm'), pathToFileURL(`${process.cwd()}/`));
|
|
8
|
+
const require = createRequire(pathToFileURL(path.join(process.cwd(), 'package.json')));
|
|
9
|
+
let queue = Promise.resolve();
|
|
10
|
+
let claim: import('vona-core').TestClaimManifest | undefined;
|
|
11
|
+
let config: import('./resolveTestCleanupConfig.ts').TestCleanupResolvedConfig | undefined;
|
|
12
|
+
|
|
13
|
+
process.on('message', (message: any) => {
|
|
14
|
+
if (!message || !Number.isSafeInteger(message.id) || typeof message.command !== 'string') return;
|
|
15
|
+
queue = queue
|
|
16
|
+
.then(async () => {
|
|
17
|
+
let ok = false;
|
|
18
|
+
try {
|
|
19
|
+
if (message.command === 'prepare') {
|
|
20
|
+
if (claim || typeof message.claimPath !== 'string')
|
|
21
|
+
throw new Error('Invalid cleanup initialization');
|
|
22
|
+
const inventory = await import('vona-core');
|
|
23
|
+
claim = await inventory.loadTestClaim(message.claimPath);
|
|
24
|
+
if (claim.projectPath !== process.cwd() || claim.runId !== process.env.VONA_TEST_RUN_ID)
|
|
25
|
+
throw new Error('Cleanup process identity mismatch');
|
|
26
|
+
config = await (
|
|
27
|
+
await import('./resolveTestCleanupConfig.ts')
|
|
28
|
+
).resolveTestCleanupConfig(claim);
|
|
29
|
+
} else if (message.command === 'database') {
|
|
30
|
+
if (!claim || !config) throw new Error('Cleanup not initialized');
|
|
31
|
+
const database = claim.databases.find(
|
|
32
|
+
db => db.clientName === message.clientName && db.scope === message.scope,
|
|
33
|
+
);
|
|
34
|
+
if (
|
|
35
|
+
!database ||
|
|
36
|
+
!database.creations.some(item => item.name === message.name) ||
|
|
37
|
+
database.endpoint.dialect === 'better-sqlite3'
|
|
38
|
+
) {
|
|
39
|
+
throw new Error('Unknown cleanup database intent');
|
|
40
|
+
}
|
|
41
|
+
const options = config.databaseOptions.get(database.clientName)!;
|
|
42
|
+
const knex = require('knex');
|
|
43
|
+
const source = options.connection;
|
|
44
|
+
// Project only supported target/auth/TLS fields. Never carry application
|
|
45
|
+
// hooks, connectors, search paths, session setup, or pool callbacks.
|
|
46
|
+
const target: Record<string, any> = {
|
|
47
|
+
user: source.user,
|
|
48
|
+
password: source.password,
|
|
49
|
+
ssl: source.ssl,
|
|
50
|
+
database: database.endpoint.adminDatabase,
|
|
51
|
+
};
|
|
52
|
+
if (database.endpoint.transport === 'tcp') {
|
|
53
|
+
target.host = database.endpoint.host;
|
|
54
|
+
target.port = database.endpoint.port;
|
|
55
|
+
} else if (database.endpoint.dialect === 'pg') {
|
|
56
|
+
target.host = database.endpoint.socketPath;
|
|
57
|
+
target.port = database.endpoint.port;
|
|
58
|
+
} else {
|
|
59
|
+
target.socketPath = database.endpoint.socketPath;
|
|
60
|
+
}
|
|
61
|
+
target.connectionTimeoutMillis = 10000;
|
|
62
|
+
target.connectTimeout = 10000;
|
|
63
|
+
const connection = knex({
|
|
64
|
+
client: database.endpoint.dialect,
|
|
65
|
+
connection: target,
|
|
66
|
+
pool: { min: 0, max: 1 },
|
|
67
|
+
acquireConnectionTimeout: 10000,
|
|
68
|
+
});
|
|
69
|
+
const { dropExactCleanupDatabase } = await import('./testCleanupAdapters.ts');
|
|
70
|
+
const { assertDisposableDatabaseName, getDatabaseTestPrefix, getTestAppName } =
|
|
71
|
+
await import('vona-core');
|
|
72
|
+
await dropExactCleanupDatabase(
|
|
73
|
+
connection,
|
|
74
|
+
database.endpoint.dialect,
|
|
75
|
+
message.name,
|
|
76
|
+
name =>
|
|
77
|
+
assertDisposableDatabaseName(
|
|
78
|
+
name,
|
|
79
|
+
getDatabaseTestPrefix(getTestAppName(claim!.runId), database.scope),
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
} else if (message.command === 'redis') {
|
|
83
|
+
if (!claim || !config) throw new Error('Cleanup not initialized');
|
|
84
|
+
const resource = claim.redis.find(item => item.clientName === message.clientName);
|
|
85
|
+
if (!resource) throw new Error('Unknown cleanup Redis resource');
|
|
86
|
+
const options = config.redisOptions.get(resource.clientName)!;
|
|
87
|
+
const Redis = require('ioredis').Redis;
|
|
88
|
+
const target: Record<string, any> = {
|
|
89
|
+
username: options.username,
|
|
90
|
+
password: options.password,
|
|
91
|
+
tls: options.tls,
|
|
92
|
+
db: resource.endpoint.db,
|
|
93
|
+
};
|
|
94
|
+
if (resource.endpoint.transport === 'socket') {
|
|
95
|
+
target.path = resource.endpoint.socketPath;
|
|
96
|
+
} else {
|
|
97
|
+
target.host = resource.endpoint.host;
|
|
98
|
+
target.port = resource.endpoint.port;
|
|
99
|
+
}
|
|
100
|
+
const connection = new Redis({
|
|
101
|
+
...target,
|
|
102
|
+
keyPrefix: undefined,
|
|
103
|
+
lazyConnect: true,
|
|
104
|
+
connectTimeout: 10000,
|
|
105
|
+
commandTimeout: 10000,
|
|
106
|
+
retryStrategy: () => null,
|
|
107
|
+
maxRetriesPerRequest: 0,
|
|
108
|
+
enableOfflineQueue: false,
|
|
109
|
+
});
|
|
110
|
+
// Never emit error details, even if a driver embeds a credential or URL.
|
|
111
|
+
connection.on('error', () => {});
|
|
112
|
+
try {
|
|
113
|
+
await connection.connect();
|
|
114
|
+
const { clearExactRedisPrefix } = await import('./testCleanupAdapters.ts');
|
|
115
|
+
const { getTestClaimRedisPrefix } = await import('vona-core');
|
|
116
|
+
await clearExactRedisPrefix(connection, resource.prefix, prefix => {
|
|
117
|
+
if (prefix !== getTestClaimRedisPrefix(claim!.runId, resource.clientName)?.prefix)
|
|
118
|
+
throw new Error('Foreign cleanup prefix');
|
|
119
|
+
});
|
|
120
|
+
} finally {
|
|
121
|
+
connection.disconnect();
|
|
122
|
+
}
|
|
123
|
+
} else if (message.command === 'close') {
|
|
124
|
+
config = undefined;
|
|
125
|
+
claim = undefined;
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error('Unknown cleanup operation');
|
|
128
|
+
}
|
|
129
|
+
ok = true;
|
|
130
|
+
} catch {
|
|
131
|
+
/* The IPC protocol deliberately contains no raw errors/config. */
|
|
132
|
+
}
|
|
133
|
+
if (process.connected) process.send?.({ id: message.id, ok });
|
|
134
|
+
})
|
|
135
|
+
.catch(() => {});
|
|
136
|
+
});
|
|
137
|
+
process.on('disconnect', () => {
|
|
138
|
+
process.exit(0);
|
|
139
|
+
});
|
|
@@ -82,7 +82,6 @@ export function copyTemplateIfNeed(fileSrc: string, fileDest: string) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export function getImportEsm() {
|
|
86
|
-
|
|
87
|
-
return '--import=./.vona/register.js';
|
|
85
|
+
export function getImportEsm(runtimeDir = '.vona') {
|
|
86
|
+
return `--import=./${runtimeDir}/register.js`;
|
|
88
87
|
}
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { describe, it } from 'node:test';
|
|
6
6
|
|
|
7
|
+
import { CliBinDbReset } from '../src/lib/bean/cli.bin.dbReset.ts';
|
|
7
8
|
import { CliBinDev, resolveDevMode, resolveTestWorkers } from '../src/lib/bean/cli.bin.dev.ts';
|
|
8
9
|
import command from '../src/lib/command/bin.dev.ts';
|
|
9
10
|
|
|
@@ -31,6 +32,23 @@ describe('bin.dev mode', () => {
|
|
|
31
32
|
}
|
|
32
33
|
});
|
|
33
34
|
|
|
35
|
+
it('launches db:reset with dev-mode generated runtime', async () => {
|
|
36
|
+
const cli = Object.create(CliBinDbReset.prototype) as CliBinDbReset;
|
|
37
|
+
const calls: Array<{ cmd: string; args: string[]; options: { cwd: string } }> = [];
|
|
38
|
+
Object.defineProperty(cli, 'helper', {
|
|
39
|
+
value: {
|
|
40
|
+
spawnExe: async options => {
|
|
41
|
+
calls.push(options);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
await cli._run('/tmp/vona-reset-project', {} as never);
|
|
46
|
+
assert.equal(calls.length, 1);
|
|
47
|
+
assert.deepEqual(calls[0].args.slice(0, 1), ['--import=./.vona/register.js']);
|
|
48
|
+
assert.match(calls[0].args[1], /dbReset\.ts$/);
|
|
49
|
+
assert.equal(calls[0].args[2], '/tmp/vona-reset-project');
|
|
50
|
+
});
|
|
51
|
+
|
|
34
52
|
it('supervises test-mode bootstrap exit without starting a server', async () => {
|
|
35
53
|
const projectPath = mkdtempSync(join(tmpdir(), 'vona-bin-dev-mode-'));
|
|
36
54
|
try {
|
|
@@ -6,6 +6,19 @@ const projectPath = argv[0];
|
|
|
6
6
|
await dbResetRun(projectPath);
|
|
7
7
|
|
|
8
8
|
async function dbResetRun(projectPath: string) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
if (process.env.META_MODE !== 'dev') {
|
|
10
|
+
throw new Error('db:reset requires dev mode');
|
|
11
|
+
}
|
|
12
|
+
const previousReset = process.env.VONA_DB_RESET;
|
|
13
|
+
process.env.VONA_DB_RESET = 'true';
|
|
14
|
+
try {
|
|
15
|
+
await createGeneralApp(projectPath);
|
|
16
|
+
} finally {
|
|
17
|
+
try {
|
|
18
|
+
await closeApp();
|
|
19
|
+
} finally {
|
|
20
|
+
if (previousReset === undefined) delete process.env.VONA_DB_RESET;
|
|
21
|
+
else process.env.VONA_DB_RESET = previousReset;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
11
24
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { isValidBeanModuleFile } from './index.ts';
|
|
5
|
+
|
|
6
|
+
test('excludes timestamped generated Vona roots from bean module transforms', () => {
|
|
7
|
+
assert.equal(
|
|
8
|
+
isValidBeanModuleFile('/project/vona/.vona-test-20261014010203-a1b2c3d4/app.ts'),
|
|
9
|
+
false,
|
|
10
|
+
);
|
|
11
|
+
assert.equal(
|
|
12
|
+
isValidBeanModuleFile('C:\\project\\vona\\.vona-test-20261014010203-a1b2c3d4\\app.ts'),
|
|
13
|
+
false,
|
|
14
|
+
);
|
|
15
|
+
assert.equal(isValidBeanModuleFile('/project/vona/src/module/foo/src/index.ts'), true);
|
|
16
|
+
});
|
|
@@ -65,7 +65,7 @@ function __parseBeanInfo(state) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function __getModuleName(sourceFileName: string | null | undefined) {
|
|
68
|
-
if (!
|
|
68
|
+
if (!isValidBeanModuleFile(sourceFileName)) return;
|
|
69
69
|
const moduleInfo = parseInfoFromPath(sourceFileName);
|
|
70
70
|
if (!moduleInfo) return;
|
|
71
71
|
return moduleInfo.relativeName;
|
|
@@ -93,8 +93,9 @@ function __createDecoratorNode(beanInfo) {
|
|
|
93
93
|
// }
|
|
94
94
|
// }
|
|
95
95
|
|
|
96
|
-
function
|
|
96
|
+
export function isValidBeanModuleFile(fileName?: string | null) {
|
|
97
97
|
if (!fileName) return false;
|
|
98
|
+
if (/[\\/].vona-test-\d{14}-[a-f0-9]{8}[\\/]/.test(fileName)) return false;
|
|
98
99
|
return !['src/boot/app/', '.zova/app/', 'src/backend/', '.vona/'].some(item => {
|
|
99
100
|
return fileName.includes(item);
|
|
100
101
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.105",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
|
|
6
6
|
"keywords": [
|
|
@@ -11,7 +11,7 @@ export function getSqlite3DatabaseNameDefault(app: VonaApplication) {
|
|
|
11
11
|
const mode = app.meta.env.META_MODE;
|
|
12
12
|
if (mode !== 'prod') return '';
|
|
13
13
|
const dbPath = path.join(getHomeVonaAppDir(app), 'sqlite3');
|
|
14
|
-
fse.ensureDirSync(dbPath);
|
|
14
|
+
if (!app.options.configResolutionOnly) fse.ensureDirSync(dbPath);
|
|
15
15
|
return path.join(dbPath, `${app.name}.db`);
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -6,6 +6,8 @@ import type { PowerPartial } from '../../types/utils/powerPartial.ts';
|
|
|
6
6
|
import type { VonaApplication } from './application.ts';
|
|
7
7
|
|
|
8
8
|
import { deepExtend } from '../utils/util.ts';
|
|
9
|
+
import { assertIsolatedTestPath } from './isolationGuard.ts';
|
|
10
|
+
import { assertTestRunOwner, resolveVonaLocalPaths } from './testIsolation.ts';
|
|
9
11
|
|
|
10
12
|
export type TypeConfigLoader<T> = (app: VonaApplication) => Promise<PowerPartial<T>>;
|
|
11
13
|
|
|
@@ -32,11 +34,18 @@ export function getLoggerPathPhysicalRoot(app: VonaApplication) {
|
|
|
32
34
|
const mode = app.meta.env.META_MODE;
|
|
33
35
|
let loggerDir: string;
|
|
34
36
|
if (mode === 'test' || mode === 'dev') {
|
|
35
|
-
loggerDir =
|
|
37
|
+
loggerDir = resolveVonaLocalPaths(
|
|
38
|
+
app.projectPath,
|
|
39
|
+
mode,
|
|
40
|
+
app.meta.env.VONA_TEST_RUN_ID,
|
|
41
|
+
).logsRoot;
|
|
36
42
|
} else {
|
|
37
43
|
loggerDir = path.join(getHomeVonaAppDir(app), 'logs');
|
|
38
44
|
}
|
|
39
|
-
|
|
45
|
+
if (!app.options.configResolutionOnly) {
|
|
46
|
+
assertOwnedLocalPath(app, loggerDir, 'logsRoot');
|
|
47
|
+
fse.ensureDirSync(loggerDir);
|
|
48
|
+
}
|
|
40
49
|
return loggerDir;
|
|
41
50
|
}
|
|
42
51
|
|
|
@@ -44,11 +53,18 @@ export function getPublicPathPhysicalRoot(app: VonaApplication) {
|
|
|
44
53
|
const mode = app.meta.env.META_MODE;
|
|
45
54
|
let publicDir: string;
|
|
46
55
|
if (mode === 'test' || mode === 'dev') {
|
|
47
|
-
publicDir =
|
|
56
|
+
publicDir = resolveVonaLocalPaths(
|
|
57
|
+
app.projectPath,
|
|
58
|
+
mode,
|
|
59
|
+
app.meta.env.VONA_TEST_RUN_ID,
|
|
60
|
+
).publicRoot;
|
|
48
61
|
} else {
|
|
49
62
|
publicDir = path.join(getHomeVonaAppDir(app), 'public');
|
|
50
63
|
}
|
|
51
|
-
|
|
64
|
+
if (!app.options.configResolutionOnly) {
|
|
65
|
+
assertOwnedLocalPath(app, publicDir, 'publicRoot');
|
|
66
|
+
fse.ensureDirSync(publicDir);
|
|
67
|
+
}
|
|
52
68
|
return publicDir;
|
|
53
69
|
}
|
|
54
70
|
|
|
@@ -56,14 +72,36 @@ export function getRuntimePathPhysicalRoot(app: VonaApplication) {
|
|
|
56
72
|
const mode = app.meta.env.META_MODE;
|
|
57
73
|
let runtimeDir: string;
|
|
58
74
|
if (mode === 'test' || mode === 'dev') {
|
|
59
|
-
runtimeDir =
|
|
75
|
+
runtimeDir = resolveVonaLocalPaths(
|
|
76
|
+
app.options.projectPath,
|
|
77
|
+
mode,
|
|
78
|
+
app.meta.env.VONA_TEST_RUN_ID,
|
|
79
|
+
).runtimeRoot;
|
|
60
80
|
} else {
|
|
61
81
|
runtimeDir = path.join(getHomeVonaAppDir(app), 'runtime');
|
|
62
82
|
}
|
|
63
|
-
|
|
83
|
+
if (!app.options.configResolutionOnly) {
|
|
84
|
+
assertOwnedLocalPath(app, runtimeDir, 'runtimeRoot');
|
|
85
|
+
fse.ensureDirSync(runtimeDir);
|
|
86
|
+
}
|
|
64
87
|
return runtimeDir;
|
|
65
88
|
}
|
|
66
89
|
|
|
90
|
+
function assertOwnedLocalPath(
|
|
91
|
+
app: VonaApplication,
|
|
92
|
+
actual: string,
|
|
93
|
+
leaf: 'logsRoot' | 'publicRoot' | 'runtimeRoot',
|
|
94
|
+
) {
|
|
95
|
+
const paths = resolveVonaLocalPaths(
|
|
96
|
+
app.projectPath,
|
|
97
|
+
app.meta.env.META_MODE,
|
|
98
|
+
app.meta.env.VONA_TEST_RUN_ID,
|
|
99
|
+
);
|
|
100
|
+
if (!paths.runId) return;
|
|
101
|
+
assertTestRunOwner(paths.dataRoot, paths.runId);
|
|
102
|
+
assertIsolatedTestPath(actual, paths[leaf]);
|
|
103
|
+
}
|
|
104
|
+
|
|
67
105
|
export function getHomeVonaAppDir(app: VonaApplication) {
|
|
68
106
|
return path.join(os.homedir(), '.vona', app.name);
|
|
69
107
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { lstat, mkdir, mkdtemp, realpath, rm } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { test } from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { getSqlite3DatabaseNameDefault } from '../addon/sqlite3.ts';
|
|
8
|
+
import {
|
|
9
|
+
getLoggerPathPhysicalRoot,
|
|
10
|
+
getPublicPathPhysicalRoot,
|
|
11
|
+
getRuntimePathPhysicalRoot,
|
|
12
|
+
} from './config.ts';
|
|
13
|
+
import { resolveVonaLocalPaths } from './testIsolation.ts';
|
|
14
|
+
|
|
15
|
+
test('internal config resolution computes canonical paths without creating or requiring released roots', async () => {
|
|
16
|
+
const parent = await mkdtemp(path.join(await realpath(os.tmpdir()), 'cleanup-config-'));
|
|
17
|
+
const projectPath = path.join(parent, 'vona');
|
|
18
|
+
await mkdir(projectPath);
|
|
19
|
+
const runId = '20261011010203-deadbeef';
|
|
20
|
+
const app = {
|
|
21
|
+
projectPath,
|
|
22
|
+
name: `cabtest${runId}`,
|
|
23
|
+
options: { projectPath, configResolutionOnly: true },
|
|
24
|
+
meta: { env: { META_MODE: 'test', VONA_TEST_RUN_ID: runId } },
|
|
25
|
+
} as any;
|
|
26
|
+
try {
|
|
27
|
+
const expected = resolveVonaLocalPaths(projectPath, 'test', runId);
|
|
28
|
+
assert.equal(getPublicPathPhysicalRoot(app), expected.publicRoot);
|
|
29
|
+
assert.equal(getRuntimePathPhysicalRoot(app), expected.runtimeRoot);
|
|
30
|
+
assert.equal(getLoggerPathPhysicalRoot(app), expected.logsRoot);
|
|
31
|
+
assert.equal(getSqlite3DatabaseNameDefault(app), '');
|
|
32
|
+
await assert.rejects(lstat(expected.dataRoot), { code: 'ENOENT' });
|
|
33
|
+
await assert.rejects(lstat(expected.generatedRoot), { code: 'ENOENT' });
|
|
34
|
+
app.options.configResolutionOnly = false;
|
|
35
|
+
assert.throws(() => getPublicPathPhysicalRoot(app));
|
|
36
|
+
await assert.rejects(lstat(expected.dataRoot), { code: 'ENOENT' });
|
|
37
|
+
} finally {
|
|
38
|
+
await rm(parent, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -2,8 +2,11 @@ export * from './application.ts';
|
|
|
2
2
|
export * from './config.ts';
|
|
3
3
|
export * from './hmr.ts';
|
|
4
4
|
export * from './hmrDeps.ts';
|
|
5
|
+
export * from './isolationGuard.ts';
|
|
5
6
|
export * from './locales.ts';
|
|
6
7
|
export * from './logger/index.ts';
|
|
7
8
|
export * from './meta.ts';
|
|
8
9
|
export * from './metadata.ts';
|
|
9
10
|
export * from './resource.ts';
|
|
11
|
+
export * from './testClaim.ts';
|
|
12
|
+
export * from './testIsolation.ts';
|