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.
Files changed (127) hide show
  1. package/.cabloy-version +1 -1
  2. package/.gitignore +2 -0
  3. package/CHANGELOG.md +21 -0
  4. package/lint-staged.config.mjs +2 -0
  5. package/oxfmt.config.ts +2 -0
  6. package/oxlint.config.ts +2 -0
  7. package/package.json +6 -5
  8. package/repo-agent-governance/managed-assets.json +9 -9
  9. package/repo-agent-governance/policies/repository.md +5 -2
  10. package/repo-agent-governance/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +2 -2
  11. package/repo-agent-governance/skills/cabloy-workflow/SKILL.md +2 -0
  12. package/repo-docs/backend/migration-and-changes.md +13 -12
  13. package/repo-docs/backend/orm-select-guide.md +6 -0
  14. package/repo-docs/backend/runtime-and-flavors.md +2 -1
  15. package/repo-docs/backend/transaction-guide.md +12 -0
  16. package/repo-docs/backend/unit-testing.md +12 -8
  17. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/cover-en-v1.png +0 -0
  18. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/completion-summary.jpg +0 -0
  19. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/multitasking-draft.jpg +0 -0
  20. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/ssr-refresh.jpg +0 -0
  21. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/index.md +216 -0
  22. package/repo-docs/blogs/index.md +15 -0
  23. package/repo-docs/frontend/scripts.md +1 -1
  24. package/repo-docs/fullstack/parallel-worktree-environment.md +6 -6
  25. package/repo-docs/fullstack/ssr-entry-modes.md +19 -0
  26. package/repo-docs/index.md +2 -0
  27. package/repo-docs/reference/repo-scripts.md +22 -1
  28. package/repo-e2e/config/playwright.config.ts +32 -14
  29. package/repo-e2e/docs/specs/blogs-index.spec.ts +23 -6
  30. package/repo-e2e/scripts/e2e.test.ts +39 -0
  31. package/repo-e2e/scripts/e2e.ts +19 -1
  32. package/repo-e2e/scripts/managedTest.test.ts +389 -0
  33. package/repo-e2e/scripts/managedTest.ts +210 -0
  34. package/repo-e2e/scripts/managedTestImports.test.ts +74 -0
  35. package/repo-e2e/scripts/managedTestImports.ts +110 -0
  36. package/repo-e2e/scripts/runE2e.ts +12 -1
  37. package/repo-e2e/scripts/startE2eVona.ts +37 -5
  38. package/repo-e2e/specs/a-commerce.spec.ts +1 -1
  39. package/repo-e2e/specs/cabloy-basic.spec.ts +1 -1
  40. package/repo-e2e/specs/controller-boundary.spec.ts +1 -1
  41. package/repo-e2e/specs/home-user-account.spec.ts +2 -1
  42. package/repo-e2e/specs/markdown.spec.ts +1 -1
  43. package/repo-e2e/specs/routed-dialog.spec.ts +1 -1
  44. package/scripts/runIsolatedTest.test.ts +426 -0
  45. package/scripts/runIsolatedTest.ts +454 -0
  46. package/scripts/testClaimCleanup.test.ts +346 -0
  47. package/scripts/testClaimCleanup.ts +375 -0
  48. package/scripts/testClaimProcesses.test.ts +106 -0
  49. package/scripts/testClaimProcesses.ts +129 -0
  50. package/scripts/testProcessGuardian.ts +163 -0
  51. package/scripts/upgrade.ts +1 -16
  52. package/vona/oxfmt.config.ts +2 -0
  53. package/vona/oxlint.config.ts +2 -0
  54. package/vona/package.original.json +2 -2
  55. package/vona/packages-cli/cli/package.json +1 -1
  56. package/vona/packages-cli/cli/src/play.test.ts +37 -0
  57. package/vona/packages-cli/cli/src/play.ts +23 -1
  58. package/vona/packages-cli/cli-set-api/package.json +1 -1
  59. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dbReset.ts +16 -7
  60. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dev.ts +13 -7
  61. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.play.ts +5 -1
  62. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.test.ts +12 -7
  63. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/configUtils.ts +21 -2
  64. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/generateVonaMeta.ts +10 -0
  65. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.test.ts +153 -0
  66. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.ts +141 -0
  67. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.test.ts +148 -0
  68. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.ts +127 -0
  69. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.test.ts +47 -0
  70. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.ts +139 -0
  71. package/vona/packages-cli/cli-set-api/src/lib/utils.ts +2 -3
  72. package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_project.json +1 -0
  73. package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_source.json +1 -0
  74. package/vona/packages-cli/cli-set-api/test/binDevMode.test.ts +18 -0
  75. package/vona/packages-cli/cli-set-api/toolsIsolate/dbReset.ts +15 -2
  76. package/vona/packages-cli/cli-set-api/tsconfig.build.json +2 -1
  77. package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +1 -1
  78. package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.test.ts +16 -0
  79. package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.ts +3 -2
  80. package/vona/packages-vona/vona/package.json +1 -1
  81. package/vona/packages-vona/vona-core/package.json +1 -1
  82. package/vona/packages-vona/vona-core/src/lib/addon/sqlite3.ts +1 -1
  83. package/vona/packages-vona/vona-core/src/lib/core/config.ts +44 -6
  84. package/vona/packages-vona/vona-core/src/lib/core/configCleanup.test.ts +40 -0
  85. package/vona/packages-vona/vona-core/src/lib/core/index.ts +3 -0
  86. package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.test.ts +301 -0
  87. package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.ts +312 -0
  88. package/vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts +577 -0
  89. package/vona/packages-vona/vona-core/src/lib/core/testClaim.ts +985 -0
  90. package/vona/packages-vona/vona-core/src/lib/core/testIsolation.ts +129 -0
  91. package/vona/packages-vona/vona-core/src/lib/framework/createApp.ts +2 -0
  92. package/vona/packages-vona/vona-core/src/lib/framework/start.ts +4 -0
  93. package/vona/packages-vona/vona-core/src/lib/framework/useApp.ts +5 -1
  94. package/vona/packages-vona/vona-core/src/types/application/app.ts +2 -0
  95. package/vona/packages-vona/vona-core/src/types/utils/env.ts +1 -0
  96. package/vona/packages-vona/vona-mock/package.json +1 -1
  97. package/vona/pnpm-lock.yaml +40 -40
  98. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentIndexes.test.ts +73 -10
  99. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  100. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  101. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  102. package/vona/src/suite-vendor/a-vona/modules/a-logger/package.json +1 -1
  103. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/bean/middlewareSystem.httpLog.ts +15 -1
  104. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts +24 -0
  105. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.ts +11 -0
  106. package/vona/src/suite-vendor/a-vona/modules/a-metrics/package.json +1 -1
  107. package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.health.ts +15 -0
  108. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  109. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/extend/schemaBuilder.ts +11 -4
  110. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/databaseClient_.ts +6 -1
  111. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +1 -1
  112. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.betterSqlite3.ts +41 -1
  113. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.mysql.ts +4 -1
  114. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.pg.ts +2 -1
  115. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  116. package/vona/src/suite-vendor/a-vona/modules/a-redis/package.json +1 -1
  117. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/lib/redis.ts +8 -0
  118. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redis.ts +23 -6
  119. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redisClient_.ts +18 -1
  120. package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
  121. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +43 -15
  122. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  123. package/vona/src/suite-vendor/a-vona/modules/a-version/src/lib/databaseTestName.ts +3 -37
  124. package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/database.ts +93 -15
  125. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  126. package/vona/tsconfig.json +1 -0
  127. package/scripts/initTestData.ts +0 -25
@@ -0,0 +1,129 @@
1
+ import fse from 'fs-extra';
2
+ import { createHash } from 'node:crypto';
3
+ import { lstatSync } from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ export const TEST_RUN_ID_ENV = 'VONA_TEST_RUN_ID';
7
+ export const TEST_RUN_OWNER_FILE = '.vona-test-owner';
8
+
9
+ const DATABASE_NAME_MAX_BYTES = 63;
10
+ const APP_NAME_MAX_BYTES = 13;
11
+ const HASH_LENGTH = 10;
12
+ const TIMESTAMP_LENGTH = 15;
13
+
14
+ export function getDatabaseTestPrefix(appName: string, scope: string): string {
15
+ const readablePrefix =
16
+ scope === 'share'
17
+ ? `vona-test-${appName}-s-`
18
+ : `vona-test-${appName}-i-${scope.slice('isolate-'.length)}-`;
19
+ if (Buffer.byteLength(readablePrefix, 'utf8') + TIMESTAMP_LENGTH <= DATABASE_NAME_MAX_BYTES) {
20
+ return readablePrefix;
21
+ }
22
+ let appHead = '';
23
+ for (const char of appName) {
24
+ if (Buffer.byteLength(`${appHead}${char}`, 'utf8') > APP_NAME_MAX_BYTES) break;
25
+ appHead += char;
26
+ }
27
+ const scopeKind = scope === 'share' ? 's' : 'i';
28
+ const namespaceHash = createHash('sha256')
29
+ .update(`${appName}\0${scope}`)
30
+ .digest('base64url')
31
+ .slice(0, HASH_LENGTH);
32
+ return `vona-test-${appHead}-${scopeKind}-${namespaceHash}-`;
33
+ }
34
+
35
+ export function assertDisposableDatabaseName(databaseName: string, databasePrefix: string) {
36
+ if (
37
+ typeof databaseName !== 'string' ||
38
+ !databaseName.startsWith(databasePrefix) ||
39
+ !/^\d{8}-\d{6}$/.test(databaseName.slice(databasePrefix.length)) ||
40
+ Buffer.byteLength(databaseName, 'utf8') > DATABASE_NAME_MAX_BYTES
41
+ ) {
42
+ throw new Error(`Database is outside the disposable namespace: ${databaseName}`);
43
+ }
44
+ }
45
+
46
+ export function resolveTestRunId(
47
+ mode: string,
48
+ runId: string | undefined = process.env[TEST_RUN_ID_ENV],
49
+ ) {
50
+ if (runId === undefined) return undefined;
51
+ const match = /^(\d{14})-[a-f0-9]{8}$/.exec(runId);
52
+ const timestamp = match?.[1];
53
+ const date = timestamp
54
+ ? new Date(
55
+ `${timestamp.slice(0, 4)}-${timestamp.slice(4, 6)}-${timestamp.slice(6, 8)}T${timestamp.slice(8, 10)}:${timestamp.slice(10, 12)}:${timestamp.slice(12, 14)}Z`,
56
+ )
57
+ : undefined;
58
+ if (
59
+ mode !== 'test' ||
60
+ !timestamp ||
61
+ !date ||
62
+ Number.isNaN(date.getTime()) ||
63
+ date.toISOString().slice(0, 19).replace(/[-:T]/g, '') !== timestamp
64
+ ) {
65
+ throw new Error(
66
+ `${TEST_RUN_ID_ENV} requires test mode and a UTC timestamp plus eight hex digits`,
67
+ );
68
+ }
69
+ return runId;
70
+ }
71
+
72
+ export function getTestAppName(runId: string) {
73
+ resolveTestRunId('test', runId);
74
+ return `cabtest${runId}`;
75
+ }
76
+
77
+ export function assertTestAppIdentity(mode: string, runId: string | undefined, appName: string) {
78
+ const id = resolveTestRunId(mode, runId);
79
+ if (id && appName !== getTestAppName(id)) {
80
+ throw new Error('Isolated test APP_NAME does not match its run ID');
81
+ }
82
+ return id;
83
+ }
84
+
85
+ export function getTestRunIdFromEnv(env: NodeJS.ProcessEnv) {
86
+ return resolveTestRunId(env.META_MODE, env[TEST_RUN_ID_ENV]);
87
+ }
88
+
89
+ export function assertTestRunOwner(root: string, runId: string) {
90
+ resolveTestRunId('test', runId);
91
+ let current = path.resolve(root);
92
+ while (true) {
93
+ try {
94
+ if (lstatSync(current).isSymbolicLink()) {
95
+ throw new Error(`Isolated test path traverses a symlink: ${current}`);
96
+ }
97
+ } catch (error) {
98
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
99
+ }
100
+ const parent = path.dirname(current);
101
+ if (parent === current) break;
102
+ current = parent;
103
+ }
104
+ const marker = path.join(root, TEST_RUN_OWNER_FILE);
105
+ if (!fse.lstatSync(marker).isFile() || fse.readFileSync(marker, 'utf8') !== `${runId}\n`) {
106
+ throw new Error(`Isolated test directory has no matching owner: ${root}`);
107
+ }
108
+ }
109
+
110
+ export function resolveVonaLocalPaths(
111
+ projectPath: string,
112
+ mode: string,
113
+ runId: string | undefined = process.env[TEST_RUN_ID_ENV],
114
+ ) {
115
+ const id = resolveTestRunId(mode, runId);
116
+ const generatedDir = id ? `.vona-test-${id}` : '.vona';
117
+ const dataDir = id ? `.app-test-${id}` : '.app';
118
+ return {
119
+ runId: id,
120
+ generatedDir,
121
+ dataDir,
122
+ generatedRoot: path.join(projectPath, generatedDir),
123
+ dataRoot: path.join(projectPath, dataDir),
124
+ publicRoot: path.join(projectPath, dataDir, 'public'),
125
+ runtimeRoot: path.join(projectPath, dataDir, 'runtime'),
126
+ logsRoot: path.join(projectPath, dataDir, 'logs'),
127
+ sqliteRoot: path.join(projectPath, dataDir, 'sqlite3'),
128
+ };
129
+ }
@@ -5,6 +5,7 @@ import type { BootstrapOptions } from '../../types/interface/bootstrap.ts';
5
5
  import type { VonaConfigEnv } from '../../types/utils/env.ts';
6
6
 
7
7
  import { VonaApplication } from '../core/application.ts';
8
+ import { assertTestAppIdentity } from '../core/testIsolation.ts';
8
9
  import { jsxEnhance } from '../utils/jsx-enhance.ts';
9
10
  import { prepareEnv } from '../utils/util.ts';
10
11
  import { zodEnhance } from '../utils/zod-enhance.ts';
@@ -54,6 +55,7 @@ function __createApp({ modulesMeta, locales, config, env, AppMonkey }: Bootstrap
54
55
  jsxEnhance();
55
56
  // env
56
57
  const env2 = prepareEnv(env);
58
+ assertTestAppIdentity(env2.META_MODE, env2.VONA_TEST_RUN_ID, env2.APP_NAME ?? '');
57
59
  // appInfo
58
60
  const appInfo = prepareAppInfo(env2);
59
61
  // options
@@ -2,6 +2,8 @@ import type { VonaConfig, VonaConfigOptional } from '../../types/index.ts';
2
2
  import type { VonaApplication } from '../core/application.ts';
3
3
 
4
4
  import { EnumAppEvent } from '../../types/index.ts';
5
+ import { assertIsolatedTestConfig } from '../core/isolationGuard.ts';
6
+ import { registerTestClaimResourceInventory } from '../core/testClaim.ts';
5
7
  import { ModuleLoader } from '../module/loader.ts';
6
8
  import { deepExtend } from '../utils/util.ts';
7
9
 
@@ -65,6 +67,8 @@ export class Start {
65
67
  // config
66
68
  const appConfig = await __prepareConfig(app);
67
69
  this.app.config = appConfig as VonaConfig;
70
+ assertIsolatedTestConfig(app);
71
+ await registerTestClaimResourceInventory(app);
68
72
  this.app.keys = appConfig.server!.keys!;
69
73
  this.app.proxy = appConfig.proxy!.enable!;
70
74
  this.app.subdomainOffset = appConfig.server!.subdomainOffset!;
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
 
4
4
  import type { VonaApplication } from '../core/application.ts';
5
5
 
6
+ import { resolveVonaLocalPaths } from '../core/testIsolation.ts';
6
7
  import { pathToHref } from '../utils/util.ts';
7
8
 
8
9
  export function useApp(): VonaApplication {
@@ -38,7 +39,10 @@ export async function createGeneralApp(
38
39
  const testInstance = await import(pathToHref(testFile));
39
40
  return await testInstance.appBootstrap;
40
41
  } else {
41
- const testFile = path.join(projectPath, '.vona/app.ts');
42
+ const testFile = path.join(
43
+ resolveVonaLocalPaths(projectPath, process.env.META_MODE).generatedRoot,
44
+ 'app.ts',
45
+ );
42
46
  const testInstance = await import(pathToHref(testFile));
43
47
  return await testInstance.createSingleApp();
44
48
  }
@@ -42,6 +42,8 @@ export interface VonaApplicationOptions {
42
42
  locales: TypeBootstrapOptionsLocales;
43
43
  config: TypeBootstrapOptionsConfig;
44
44
  env: VonaConfigEnv;
45
+ /** Internal cleanup-only source configuration resolution; never starts an application. */
46
+ configResolutionOnly?: boolean;
45
47
  AppMonkey?: AppMonkeyConstructable;
46
48
  }
47
49
 
@@ -4,6 +4,7 @@ export interface VonaConfigEnv {
4
4
  META_FLAVOR: VonaMetaFlavor;
5
5
  META_MODE: VonaMetaMode;
6
6
  APP_NAME: string | undefined;
7
+ VONA_TEST_RUN_ID: string | undefined;
7
8
  APP_TITLE: string | undefined;
8
9
  APP_VERSION: string | undefined;
9
10
  SERVER_KEYS: string | undefined;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-mock",
3
- "version": "6.1.42",
3
+ "version": "6.1.44",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona mock",
6
6
  "keywords": [
@@ -573,8 +573,8 @@ importers:
573
573
  specifier: npm:@cabloy/zod@4.3.8
574
574
  version: '@cabloy/zod@4.3.8'
575
575
  zova:
576
- specifier: ^5.1.166
577
- version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
576
+ specifier: ^5.1.167
577
+ version: 5.1.167(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
578
578
  zova-jsx:
579
579
  specifier: ^1.1.92
580
580
  version: 1.1.92(typescript@5.9.3)
@@ -606,8 +606,8 @@ importers:
606
606
  specifier: ^5.1.37
607
607
  version: 5.1.37(vue@3.5.43(typescript@5.9.3))
608
608
  zova-module-a-openapi:
609
- specifier: ^5.1.54
610
- version: 5.1.54
609
+ specifier: ^5.1.55
610
+ version: 5.1.55
611
611
  zova-module-a-pay:
612
612
  specifier: ^5.0.9
613
613
  version: 5.0.9
@@ -624,8 +624,8 @@ importers:
624
624
  specifier: ^5.1.35
625
625
  version: 5.1.35
626
626
  zova-module-a-table:
627
- specifier: ^5.1.43
628
- version: 5.1.43(vue@3.5.43(typescript@5.9.3))
627
+ specifier: ^5.1.44
628
+ version: 5.1.44(vue@3.5.43(typescript@5.9.3))
629
629
  zova-module-pay-mock:
630
630
  specifier: ^5.0.5
631
631
  version: 5.0.5
@@ -657,8 +657,8 @@ importers:
657
657
  specifier: npm:@cabloy/zod@4.3.8
658
658
  version: '@cabloy/zod@4.3.8'
659
659
  zova:
660
- specifier: ^5.1.166
661
- version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
660
+ specifier: ^5.1.167
661
+ version: 5.1.167(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
662
662
  zova-jsx:
663
663
  specifier: ^1.1.92
664
664
  version: 1.1.92(typescript@5.9.3)
@@ -690,8 +690,8 @@ importers:
690
690
  specifier: ^5.1.37
691
691
  version: 5.1.37(vue@3.5.43(typescript@5.9.3))
692
692
  zova-module-a-openapi:
693
- specifier: ^5.1.54
694
- version: 5.1.54
693
+ specifier: ^5.1.55
694
+ version: 5.1.55
695
695
  zova-module-a-pay:
696
696
  specifier: ^5.0.9
697
697
  version: 5.0.9
@@ -708,8 +708,8 @@ importers:
708
708
  specifier: ^5.1.35
709
709
  version: 5.1.35
710
710
  zova-module-a-table:
711
- specifier: ^5.1.43
712
- version: 5.1.43(vue@3.5.43(typescript@5.9.3))
711
+ specifier: ^5.1.44
712
+ version: 5.1.44(vue@3.5.43(typescript@5.9.3))
713
713
  zova-module-pay-mock:
714
714
  specifier: ^5.0.5
715
715
  version: 5.0.5
@@ -741,8 +741,8 @@ importers:
741
741
  specifier: npm:@cabloy/zod@4.3.8
742
742
  version: '@cabloy/zod@4.3.8'
743
743
  zova:
744
- specifier: ^5.1.166
745
- version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
744
+ specifier: ^5.1.167
745
+ version: 5.1.167(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
746
746
  zova-jsx:
747
747
  specifier: ^1.1.92
748
748
  version: 1.1.92(typescript@5.9.3)
@@ -774,8 +774,8 @@ importers:
774
774
  specifier: ^5.1.37
775
775
  version: 5.1.37(vue@3.5.43(typescript@5.9.3))
776
776
  zova-module-a-openapi:
777
- specifier: ^5.1.54
778
- version: 5.1.54
777
+ specifier: ^5.1.55
778
+ version: 5.1.55
779
779
  zova-module-a-pay:
780
780
  specifier: ^5.0.9
781
781
  version: 5.0.9
@@ -792,8 +792,8 @@ importers:
792
792
  specifier: ^5.1.35
793
793
  version: 5.1.35
794
794
  zova-module-a-table:
795
- specifier: ^5.1.43
796
- version: 5.1.43(vue@3.5.43(typescript@5.9.3))
795
+ specifier: ^5.1.44
796
+ version: 5.1.44(vue@3.5.43(typescript@5.9.3))
797
797
  zova-module-pay-mock:
798
798
  specifier: ^5.0.5
799
799
  version: 5.0.5
@@ -825,8 +825,8 @@ importers:
825
825
  specifier: npm:@cabloy/zod@4.3.8
826
826
  version: '@cabloy/zod@4.3.8'
827
827
  zova:
828
- specifier: ^5.1.166
829
- version: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
828
+ specifier: ^5.1.167
829
+ version: 5.1.167(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
830
830
  zova-jsx:
831
831
  specifier: ^1.1.92
832
832
  version: 1.1.92(typescript@5.9.3)
@@ -858,8 +858,8 @@ importers:
858
858
  specifier: ^5.1.37
859
859
  version: 5.1.37(vue@3.5.43(typescript@5.9.3))
860
860
  zova-module-a-openapi:
861
- specifier: ^5.1.54
862
- version: 5.1.54
861
+ specifier: ^5.1.55
862
+ version: 5.1.55
863
863
  zova-module-a-pay:
864
864
  specifier: ^5.0.9
865
865
  version: 5.0.9
@@ -876,8 +876,8 @@ importers:
876
876
  specifier: ^5.1.35
877
877
  version: 5.1.35
878
878
  zova-module-a-table:
879
- specifier: ^5.1.43
880
- version: 5.1.43(vue@3.5.43(typescript@5.9.3))
879
+ specifier: ^5.1.44
880
+ version: 5.1.44(vue@3.5.43(typescript@5.9.3))
881
881
  zova-module-pay-mock:
882
882
  specifier: ^5.0.5
883
883
  version: 5.0.5
@@ -1652,7 +1652,7 @@ importers:
1652
1652
  specifier: ^5.0.3
1653
1653
  version: link:modules/a-markdown
1654
1654
  vona-module-a-rbac:
1655
- specifier: ^5.0.8
1655
+ specifier: ^5.0.9
1656
1656
  version: link:modules/a-rbac
1657
1657
  vona-module-a-socket:
1658
1658
  specifier: workspace:^
@@ -8938,8 +8938,8 @@ packages:
8938
8938
  zova-module-a-model@5.1.37:
8939
8939
  resolution: {integrity: sha512-Ipas8Lpsi9Jcgu3AbzajgqcBffmg1GeIBOu4UtOyR5fbRDvlxYWXx45rA52oB/zgoC+QvhUVzY36fnNfTJiywg==}
8940
8940
 
8941
- zova-module-a-openapi@5.1.54:
8942
- resolution: {integrity: sha512-/ECDbv7P5jHb9kr7gRFwpSIPtPHXg7yPVe8jfUCDx2y0P/Wk/fOEKLBBQLAC8jEwjTesQNXGu9I2WcNb0kL3Pw==}
8941
+ zova-module-a-openapi@5.1.55:
8942
+ resolution: {integrity: sha512-jdbUN4QEAQZvx+EmwaDfIa0Xy5MoNOVYLdLnEl+LbANc6COxkGGSyroFBRXG5S/Av0YWIBUbgahuEpFdL+RYjw==}
8943
8943
 
8944
8944
  zova-module-a-pay@5.0.9:
8945
8945
  resolution: {integrity: sha512-Chl6EKp30HBOGFCJXbla2/lS19WhzQyTZwgYmsL3nnG9Vt2TeUlb82vsYCVuDcy0cgmxVaWjPF7dzKL/r3Pfng==}
@@ -8965,8 +8965,8 @@ packages:
8965
8965
  zova-module-a-style@5.1.35:
8966
8966
  resolution: {integrity: sha512-i73I3wFvdQonj3l4fkovkKBZwSK4s6D4vL3AVfVvFJ4eIboKH2yw0hCILKRzLoESNQ0T+lNS6ozeimvtA4/6DQ==}
8967
8967
 
8968
- zova-module-a-table@5.1.43:
8969
- resolution: {integrity: sha512-Q7ZlGF4sNaors+pfwrCMx2Ot2qUfivX6BqvKzT0Em1GcJMXEvmWDMseWZausQUw/h3VOOG3i4hN/RKftrYMYjA==}
8968
+ zova-module-a-table@5.1.44:
8969
+ resolution: {integrity: sha512-wc9tABJ20tVX2RL/9xTfgHgdDnvN9UoJQm8k25Ay/72r9K5WfoRk4dHfgjEQXIgOVyWWXTWkI1SGcvUkqC3JbA==}
8970
8970
 
8971
8971
  zova-module-a-zod@5.1.37:
8972
8972
  resolution: {integrity: sha512-rXHN9rKbum9w3gDSnC/pIusG2d2FGJOcIauf8jrbT96f+Olk4Q3iV3t2V92mxiLCh9kMvBJPfcOgJFW7pRB5Fw==}
@@ -8980,11 +8980,11 @@ packages:
8980
8980
  zova-module-rest-resource@5.1.55:
8981
8981
  resolution: {integrity: sha512-Kk3oJIcxM5EbeTLl+ucJSeI2kT0We8YilXBaX8Z7QWCbZ0fHoQDy/Ac4YsVSWIXUrC2SQ267+nMl3sHXnE9G0Q==}
8982
8982
 
8983
- zova-suite-a-zova@5.1.165:
8984
- resolution: {integrity: sha512-hXjFEx9MXrtvPgAv28VF2UbgM45hzz7B7ZAsElJloBNWB3zyz2HUHYxNWDbCrrKfrw5zM3Mbr9i+dzyy3/6LQw==}
8983
+ zova-suite-a-zova@5.1.166:
8984
+ resolution: {integrity: sha512-2mmuMDS4uEvq4x9p/StuBb8E+9Czg7G/S6E9IF2SdwBGmXB6dtcog1rNquvUyNLOujw2UNn2uUyuIY0WPRUc0g==}
8985
8985
 
8986
- zova@5.1.166:
8987
- resolution: {integrity: sha512-2qh4Na6G8JYHWIyX7+NNfMLzzgHsheQFUgeH6KVsMp66kxpdR9YSr5Ivz+dJepthtmnlCB7R+WWczhURx4wYYA==}
8986
+ zova@5.1.167:
8987
+ resolution: {integrity: sha512-A0vfiT2eXuKbKW1GxlZNnsZPuPvS/mAHfoaiBsN5Pf4GYOKE/7R9fJIbl9YQzNWkV3LP3vECygqBHg9q2qr9Bw==}
8988
8988
 
8989
8989
  zwitch@1.0.5:
8990
8990
  resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
@@ -15158,7 +15158,7 @@ snapshots:
15158
15158
  - '@vue/composition-api'
15159
15159
  - vue
15160
15160
 
15161
- zova-module-a-openapi@5.1.54:
15161
+ zova-module-a-openapi@5.1.55:
15162
15162
  dependencies:
15163
15163
  '@cabloy/json-schema-to-zod': 2.6.5
15164
15164
  openapi3-ts: 4.6.1
@@ -15191,7 +15191,7 @@ snapshots:
15191
15191
  csx: 10.0.2
15192
15192
  typestyle: 2.4.0
15193
15193
 
15194
- zova-module-a-table@5.1.43(vue@3.5.43(typescript@5.9.3)):
15194
+ zova-module-a-table@5.1.44(vue@3.5.43(typescript@5.9.3)):
15195
15195
  dependencies:
15196
15196
  '@tanstack/table-core': 8.21.3
15197
15197
  '@tanstack/vue-table': 8.21.3(vue@3.5.43(typescript@5.9.3))
@@ -15225,7 +15225,7 @@ snapshots:
15225
15225
 
15226
15226
  zova-module-rest-resource@5.1.55: {}
15227
15227
 
15228
- zova-suite-a-zova@5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15228
+ zova-suite-a-zova@5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15229
15229
  dependencies:
15230
15230
  zova-module-a-api: 5.1.22
15231
15231
  zova-module-a-app: 5.1.26
@@ -15240,7 +15240,7 @@ snapshots:
15240
15240
  zova-module-a-logger: 5.1.28
15241
15241
  zova-module-a-meta: 5.1.22
15242
15242
  zova-module-a-model: 5.1.37(vue@3.5.43(typescript@5.9.3))
15243
- zova-module-a-openapi: 5.1.54
15243
+ zova-module-a-openapi: 5.1.55
15244
15244
  zova-module-a-router: 5.1.41
15245
15245
  zova-module-a-routerstack: 5.1.29
15246
15246
  zova-module-a-routertabs: 5.1.34
@@ -15248,7 +15248,7 @@ snapshots:
15248
15248
  zova-module-a-ssrhmr: 5.1.23
15249
15249
  zova-module-a-ssrserver: 5.1.26
15250
15250
  zova-module-a-style: 5.1.35
15251
- zova-module-a-table: 5.1.43(vue@3.5.43(typescript@5.9.3))
15251
+ zova-module-a-table: 5.1.44(vue@3.5.43(typescript@5.9.3))
15252
15252
  zova-module-a-zod: 5.1.37
15253
15253
  zova-module-a-zova: 5.1.108(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
15254
15254
  transitivePeerDependencies:
@@ -15258,10 +15258,10 @@ snapshots:
15258
15258
  - typescript
15259
15259
  - vue
15260
15260
 
15261
- zova@5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15261
+ zova@5.1.167(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3)):
15262
15262
  dependencies:
15263
15263
  zova-core: 5.1.92(patch_hash=144450bd7a567a6067e5857c0cf4c780981b7e90bc48745c09e1696a9b6f1b97)(typescript@5.9.3)
15264
- zova-suite-a-zova: 5.1.165(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
15264
+ zova-suite-a-zova: 5.1.166(typescript@5.9.3)(vue@3.5.43(typescript@5.9.3))
15265
15265
  transitivePeerDependencies:
15266
15266
  - '@vue/composition-api'
15267
15267
  - debug
@@ -1,12 +1,20 @@
1
1
  import type { IMetaOptionsIndex } from 'vona-module-a-index';
2
2
 
3
+ import { DateTime } from 'luxon';
3
4
  import assert from 'node:assert/strict';
4
5
  import { randomUUID } from 'node:crypto';
5
- import { mkdtemp, rm } from 'node:fs/promises';
6
- import { tmpdir } from 'node:os';
6
+ import { lstat, open, rm } from 'node:fs/promises';
7
7
  import path from 'node:path';
8
8
  import { describe, it } from 'node:test';
9
- import { appResource } from 'vona';
9
+ import {
10
+ appResource,
11
+ assertDisposableDatabaseName,
12
+ assertIsolatedTestPath,
13
+ assertTestRunOwner,
14
+ getDatabaseTestPrefix,
15
+ registerTestClaimDatabaseCreation,
16
+ resolveVonaLocalPaths,
17
+ } from 'vona';
10
18
  import { app } from 'vona-mock';
11
19
  import { ServiceDatabaseAsyncLocalStorage, ServiceDatabaseClient } from 'vona-module-a-orm';
12
20
 
@@ -24,6 +32,56 @@ const expectedIndexes = {
24
32
  ],
25
33
  };
26
34
 
35
+ const attemptedCopyNames = new Set<string>();
36
+
37
+ async function allocateOwnedSqliteCopy(source: ServiceDatabaseClient) {
38
+ const runId = app.meta.env.VONA_TEST_RUN_ID;
39
+ assert.ok(runId, 'the isolated SQLite copy requires a test run ID');
40
+ const paths = resolveVonaLocalPaths(app.projectPath, 'test', runId);
41
+ assertTestRunOwner(paths.dataRoot, runId);
42
+ const prefix = getDatabaseTestPrefix(app.name, 'share');
43
+
44
+ for (let offset = 0; offset < 3600; offset++) {
45
+ const timestamp = DateTime.now().plus({ seconds: offset }).toFormat('yyyyMMdd-HHmmss');
46
+ const databaseName = `${prefix}${timestamp}`;
47
+ assertDisposableDatabaseName(databaseName, prefix);
48
+ const filename = path.join(paths.sqliteRoot, `${databaseName}.db`);
49
+ if (attemptedCopyNames.has(filename)) continue;
50
+ const reservation = `${filename}.lock`;
51
+ assertIsolatedTestPath(filename, path.join(paths.sqliteRoot, path.basename(filename)));
52
+ assertIsolatedTestPath(reservation, path.join(paths.sqliteRoot, path.basename(reservation)));
53
+ try {
54
+ const reservationHandle = await open(reservation, 'wx');
55
+ await reservationHandle.close();
56
+ try {
57
+ const exists = await lstat(filename).then(
58
+ () => true,
59
+ error => {
60
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;
61
+ throw error;
62
+ },
63
+ );
64
+ if (exists) {
65
+ await rm(reservation);
66
+ continue;
67
+ }
68
+ attemptedCopyNames.add(filename);
69
+ await registerTestClaimDatabaseCreation(app, source.clientConfig, databaseName, 'intent');
70
+ const databaseHandle = await open(filename, 'wx');
71
+ await databaseHandle.close();
72
+ await rm(filename);
73
+ return { filename, paths, reservation };
74
+ } catch (error) {
75
+ await rm(reservation, { force: true });
76
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
77
+ }
78
+ } catch (error) {
79
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
80
+ }
81
+ }
82
+ throw new Error('Unable to allocate an owned SQLite copy name');
83
+ }
84
+
27
85
  describe('paymentIndexes.test.ts', { concurrency: false }, () => {
28
86
  it('registers all payment indexes without overwriting table keys', () => {
29
87
  const options = appResource.getBean('commerce-payment.meta.index')
@@ -85,12 +143,17 @@ describe('paymentIndexes.test.ts', { concurrency: false }, () => {
85
143
  t.skip('SQLite-only independent database copy');
86
144
  return;
87
145
  }
88
- const directory = await mkdtemp(path.join(tmpdir(), 'commerce-index-recovery-'));
89
- const filename = path.join(directory, 'existing.db');
90
146
  const source = app.bean.database.current;
147
+ const { filename, paths, reservation } = await allocateOwnedSqliteCopy(source.client);
91
148
  let client: ServiceDatabaseClient | undefined;
92
149
  try {
93
150
  await source.connection.raw('VACUUM main INTO ?', [filename]);
151
+ await registerTestClaimDatabaseCreation(
152
+ app,
153
+ source.client.clientConfig,
154
+ path.basename(filename, '.db'),
155
+ 'created',
156
+ );
94
157
  const selector = `index-recovery-${randomUUID()}`;
95
158
  client = app.bean._newBean(ServiceDatabaseClient, selector, {
96
159
  client: 'better-sqlite3',
@@ -154,11 +217,11 @@ describe('paymentIndexes.test.ts', { concurrency: false }, () => {
154
217
  }
155
218
  });
156
219
  } finally {
157
- try {
158
- if (client) await client.connection.destroy();
159
- } finally {
160
- await rm(directory, { recursive: true, force: true });
161
- }
220
+ if (client) await client.connection.destroy();
221
+ assertTestRunOwner(paths.dataRoot, paths.runId!);
222
+ assertIsolatedTestPath(filename, filename);
223
+ await source.connection.schema.dropDatabase(filename);
224
+ await rm(reservation, { force: true });
162
225
  }
163
226
  });
164
227
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-rbac",
3
- "version": "5.0.8",
3
+ "version": "5.0.10",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "Vona Module"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-suite-a-cabloy",
3
- "version": "5.1.59",
3
+ "version": "5.1.61",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "author": "",
@@ -10,7 +10,7 @@
10
10
  "vona-module-a-cabloy": "workspace:^",
11
11
  "vona-module-a-layoutprofile": "^5.0.2",
12
12
  "vona-module-a-markdown": "^5.0.3",
13
- "vona-module-a-rbac": "^5.0.8",
13
+ "vona-module-a-rbac": "^5.0.10",
14
14
  "vona-module-a-socket": "workspace:^",
15
15
  "vona-module-a-ssr": "workspace:^",
16
16
  "vona-module-a-ssrhmr": "workspace:^"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-core",
3
- "version": "5.1.56",
3
+ "version": "5.1.58",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-module-a-logger",
3
- "version": "5.1.13",
3
+ "version": "5.1.15",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "",
6
6
  "keywords": [
@@ -8,6 +8,8 @@ import StdSerializers from 'pino-std-serializers';
8
8
  import { BeanBase } from 'vona';
9
9
  import { MiddlewareSystem } from 'vona-module-a-aspect';
10
10
 
11
+ import { redactIsolatedReadyHeader } from '../lib/redactIsolatedReadyHeader.ts';
12
+
11
13
  export interface IMiddlewareSystemOptionsHttpLog extends IDecoratorMiddlewareSystemOptions {}
12
14
 
13
15
  @MiddlewareSystem<IMiddlewareSystemOptionsHttpLog>()
@@ -16,15 +18,27 @@ export class MiddlewareSystemHttpLog extends BeanBase implements IMiddlewareSyst
16
18
  const ctx = this.ctx;
17
19
  // start
18
20
  const req = StdSerializers.req(ctx.req);
21
+ req.headers = redactIsolatedReadyHeader(
22
+ req.headers,
23
+ ctx.path,
24
+ this.app.meta.env.VONA_TEST_RUN_ID,
25
+ 'x-vona-test-challenge',
26
+ );
19
27
  this.$loggerChild('req').http(() => JSON.stringify(req));
20
28
  const profiler = this.$loggerChild('res').startTimer();
21
29
  // next
22
30
  await next();
23
31
  // end
32
+ const headers = redactIsolatedReadyHeader(
33
+ ctx.res.getHeaders(),
34
+ ctx.path,
35
+ this.app.meta.env.VONA_TEST_RUN_ID,
36
+ 'x-vona-test-proof',
37
+ );
24
38
  const res = {
25
39
  url: ctx.req.url,
26
40
  statusCode: ctx.res.statusCode,
27
- headers: ctx.res.getHeaders(),
41
+ headers,
28
42
  };
29
43
  profiler.done({ level: 'http', message: JSON.stringify(res) });
30
44
  }
@@ -0,0 +1,24 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+
4
+ import { redactIsolatedReadyHeader } from './redactIsolatedReadyHeader.ts';
5
+
6
+ test('redacts isolated readiness challenge and proof without changing live headers', () => {
7
+ const runId = '20261014010203-a1b2c3d4';
8
+ const req = { 'x-vona-test-challenge': 'challenge-value', 'accept': 'application/json' };
9
+ const res = { 'x-vona-test-proof': 'proof-value', 'content-type': 'application/json' };
10
+ const loggedReq = redactIsolatedReadyHeader(req, '/health/ready', runId, 'x-vona-test-challenge');
11
+ const loggedRes = redactIsolatedReadyHeader(res, '/health/ready', runId, 'x-vona-test-proof');
12
+
13
+ assert.equal(JSON.stringify(loggedReq).includes('challenge-value'), false);
14
+ assert.equal(JSON.stringify(loggedRes).includes('proof-value'), false);
15
+ assert.equal(loggedReq.accept, 'application/json');
16
+ assert.equal(loggedRes['content-type'], 'application/json');
17
+ assert.equal(req['x-vona-test-challenge'], 'challenge-value');
18
+ assert.equal(res['x-vona-test-proof'], 'proof-value');
19
+ assert.equal(redactIsolatedReadyHeader(req, '/health/live', runId, 'x-vona-test-challenge'), req);
20
+ assert.equal(
21
+ redactIsolatedReadyHeader(req, '/health/ready', undefined, 'x-vona-test-challenge'),
22
+ req,
23
+ );
24
+ });