vona-core 5.1.46 → 5.1.48

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 (75) hide show
  1. package/dist/index.js +874 -117
  2. package/dist/index.js.map +1 -1
  3. package/dist/lib/bean/beanBase.d.ts.map +1 -1
  4. package/dist/lib/core/asyncLocalStorage.d.ts +6 -0
  5. package/dist/lib/core/asyncLocalStorage.d.ts.map +1 -1
  6. package/dist/lib/core/index.d.ts +3 -0
  7. package/dist/lib/core/index.d.ts.map +1 -1
  8. package/dist/lib/core/logger/appLogger.d.ts +2 -0
  9. package/dist/lib/core/logger/appLogger.d.ts.map +1 -1
  10. package/dist/lib/core/logger/diagnosticLogger.d.ts +4 -0
  11. package/dist/lib/core/logger/diagnosticLogger.d.ts.map +1 -0
  12. package/dist/lib/core/logger/diagnosticLogger.test.d.ts +2 -0
  13. package/dist/lib/core/logger/diagnosticLogger.test.d.ts.map +1 -0
  14. package/dist/lib/core/logger/index.d.ts +1 -0
  15. package/dist/lib/core/logger/index.d.ts.map +1 -1
  16. package/dist/lib/core/logger/utils.d.ts +1 -0
  17. package/dist/lib/core/logger/utils.d.ts.map +1 -1
  18. package/dist/lib/core/requestBoundary.d.ts +22 -0
  19. package/dist/lib/core/requestBoundary.d.ts.map +1 -0
  20. package/dist/lib/core/testClaim.d.ts +10 -0
  21. package/dist/lib/core/testClaim.d.ts.map +1 -1
  22. package/dist/lib/core/testClaimFilesystem.d.ts +13 -0
  23. package/dist/lib/core/testClaimFilesystem.d.ts.map +1 -0
  24. package/dist/lib/core/testClaimFilesystem.test.d.ts +2 -0
  25. package/dist/lib/core/testClaimFilesystem.test.d.ts.map +1 -0
  26. package/dist/lib/core/testClaimFilesystemWindows.d.ts +19 -0
  27. package/dist/lib/core/testClaimFilesystemWindows.d.ts.map +1 -0
  28. package/dist/lib/core/testFreshness.d.ts +21 -0
  29. package/dist/lib/core/testFreshness.d.ts.map +1 -0
  30. package/dist/lib/core/testFreshness.test.d.ts +2 -0
  31. package/dist/lib/core/testFreshness.test.d.ts.map +1 -0
  32. package/dist/lib/core/testProcessWindowsNative.d.ts +59 -0
  33. package/dist/lib/core/testProcessWindowsNative.d.ts.map +1 -0
  34. package/dist/lib/framework/bootstrap.test.d.ts +2 -0
  35. package/dist/lib/framework/bootstrap.test.d.ts.map +1 -0
  36. package/dist/lib/framework/index.d.ts +1 -0
  37. package/dist/lib/framework/index.d.ts.map +1 -1
  38. package/dist/lib/framework/processWorker.d.ts +1 -0
  39. package/dist/lib/framework/processWorker.d.ts.map +1 -1
  40. package/dist/lib/framework/processWorker.test.d.ts +2 -0
  41. package/dist/lib/framework/processWorker.test.d.ts.map +1 -0
  42. package/dist/lib/framework/start.d.ts.map +1 -1
  43. package/dist/lib/framework/useApp.test.d.ts +2 -0
  44. package/dist/lib/framework/useApp.test.d.ts.map +1 -0
  45. package/package.json +2 -2
  46. package/src/lib/bean/beanBase.ts +4 -0
  47. package/src/lib/core/asyncLocalStorage.ts +84 -12
  48. package/src/lib/core/index.ts +14 -0
  49. package/src/lib/core/logger/appLogger.ts +11 -3
  50. package/src/lib/core/logger/diagnosticLogger.test.ts +146 -0
  51. package/src/lib/core/logger/diagnosticLogger.ts +138 -0
  52. package/src/lib/core/logger/index.ts +1 -0
  53. package/src/lib/core/logger/utils.ts +25 -5
  54. package/src/lib/core/requestBoundary.ts +89 -0
  55. package/src/lib/core/testClaim.test.ts +298 -17
  56. package/src/lib/core/testClaim.ts +205 -97
  57. package/src/lib/core/testClaimFilesystem.test.ts +247 -0
  58. package/src/lib/core/testClaimFilesystem.ts +224 -0
  59. package/src/lib/core/testClaimFilesystemWindows.cs +229 -0
  60. package/src/lib/core/testClaimFilesystemWindows.ps1 +20 -0
  61. package/src/lib/core/testClaimFilesystemWindows.ts +200 -0
  62. package/src/lib/core/testFreshness.test.ts +379 -0
  63. package/src/lib/core/testFreshness.ts +238 -0
  64. package/src/lib/core/testProcessWindowsNative.cs +336 -0
  65. package/src/lib/core/testProcessWindowsNative.ps1 +20 -0
  66. package/src/lib/core/testProcessWindowsNative.ts +531 -0
  67. package/src/lib/framework/bootstrap.test.ts +61 -0
  68. package/src/lib/framework/index.ts +1 -0
  69. package/src/lib/framework/processWorker.test.ts +59 -0
  70. package/src/lib/framework/processWorker.ts +8 -5
  71. package/src/lib/framework/start.test.ts +4 -3
  72. package/src/lib/framework/start.ts +2 -0
  73. package/src/lib/framework/useApp.test.ts +84 -0
  74. package/src/lib/framework/useApp.ts +2 -2
  75. package/src/lib/utils/util.ts +1 -1
package/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import '@cabloy/json5';
2
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
3
  import { combineApiPathControllerAndActionRaw, combineApiPathControllerAndAction, combineApiPath, combineResourceName, forEach, forEachSync, sleep, isUndefined, isClass, isNilOrEmptyString, evaluateExpressions, catchErrorSync, isEmptyObject, catchError, hashkey } from '@cabloy/utils';
3
4
  import { toLowerCaseFirstChar, splitWords } from '@cabloy/word-utils';
4
5
  import path from 'node:path';
5
6
  import fse from 'fs-extra';
6
7
  import crypto, { createHash as createHash$1, randomBytes, timingSafeEqual } from 'node:crypto';
7
- import fs, { lstatSync, constants } from 'node:fs';
8
+ import fs, { lstatSync, existsSync, readFileSync, constants } from 'node:fs';
8
9
  import { compose as compose$1 } from '@cabloy/compose';
9
10
  import { extend } from '@cabloy/extend';
10
11
  import { createRequire } from 'node:module';
11
- import { pathToFileURL } from 'node:url';
12
+ import { pathToFileURL, fileURLToPath } from 'node:url';
12
13
  import * as uuid from 'uuid';
13
14
  import { getPlatformArch as getPlatformArch$1 } from 'vona';
14
15
  import os from 'node:os';
15
- import { AsyncLocalStorage } from 'node:async_hooks';
16
16
  import { isDeepStrictEqual } from 'node:util';
17
17
  import { setLocaleErrors, setLocaleAdapter, translateError } from '@cabloy/zod-errors-custom';
18
18
  import { ZodMetadata } from '@cabloy/zod-openapi';
@@ -26,8 +26,9 @@ import chalk from 'chalk';
26
26
  import * as Winston from 'winston';
27
27
  import DailyRotateFile from 'winston-daily-rotate-file';
28
28
  import { MESSAGE, LEVEL } from 'triple-beam';
29
- import { mkdir, open, rename, unlink } from 'node:fs/promises';
30
29
  import { createServer } from 'node:http';
30
+ import { mkdir, open, rename, unlink, lstat, readdir } from 'node:fs/promises';
31
+ import { execFileSync, execFile } from 'node:child_process';
31
32
  import cluster from 'node:cluster';
32
33
  import React from 'react';
33
34
  import { extendZodWithOpenApi } from '@cabloy/zod-to-openapi';
@@ -35,6 +36,179 @@ import { z } from 'zod';
35
36
  import * as Retry from 'retry';
36
37
  export { Retry };
37
38
 
39
+ const boundaries = new WeakMap();
40
+ const boundaryStorage = new AsyncLocalStorage();
41
+ function createRequestBoundary(options) {
42
+ const onFailure = options?.onFailure;
43
+ const boundary = Object.freeze({});
44
+ let portFault = false;
45
+ let reporting = false;
46
+ const reported = new Set();
47
+ const capture = Object.freeze({
48
+ diagnostics: 'suppress',
49
+ reportFailure(failure) {
50
+ if (failure !== 'observer_failed' && failure !== 'diagnostic_failed' && failure !== 'late_callback_failed') {
51
+ portFault = true;
52
+ return;
53
+ }
54
+ portFault = true;
55
+ if (reported.has(failure)) return;
56
+ reported.add(failure);
57
+ if (reporting || !onFailure) return;
58
+ reporting = true;
59
+ try {
60
+ const result = onFailure(failure);
61
+ if (result && (typeof result === 'object' || typeof result === 'function')) {
62
+ const then = result.then;
63
+ if (typeof then === 'function') {
64
+ portFault = true;
65
+ void new Promise((resolve, reject) => {
66
+ then.call(result, resolve, reject);
67
+ }).catch(() => {});
68
+ }
69
+ }
70
+ } catch {
71
+ portFault = true;
72
+ } finally {
73
+ reporting = false;
74
+ }
75
+ },
76
+ snapshot() {
77
+ return Object.freeze({
78
+ portFault
79
+ });
80
+ }
81
+ });
82
+ boundaries.set(boundary, capture);
83
+ return boundary;
84
+ }
85
+ function runWithRequestBoundary(boundary, continuation) {
86
+ if (!boundaries.has(boundary)) throw new Error('Invalid request boundary');
87
+ const current = boundaryStorage.getStore();
88
+ if (current && current !== boundary) throw new Error('Request boundary cannot be replaced');
89
+ if (current === boundary) return continuation();
90
+ return boundaryStorage.run(boundary, continuation);
91
+ }
92
+ function getRequestBoundary() {
93
+ return boundaryStorage.getStore();
94
+ }
95
+ function captureRequestBoundary() {
96
+ const boundary = boundaryStorage.getStore();
97
+ return boundary ? boundaries.get(boundary) : undefined;
98
+ }
99
+
100
+ const loggingMethods = new Set(['log', 'error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly']);
101
+ function completeWrite(args) {
102
+ const callback = typeof args[1] === 'function' ? args[1] : args[2];
103
+ if (typeof callback === 'function') {
104
+ try {
105
+ callback();
106
+ } catch {
107
+ captureRequestBoundary()?.reportFailure('diagnostic_failed');
108
+ }
109
+ }
110
+ return true;
111
+ }
112
+ const suppressedLogger = createFacade();
113
+ function getSuppressedDiagnosticLogger() {
114
+ return suppressedLogger;
115
+ }
116
+ function createDiagnosticLogger(raw) {
117
+ if (getRequestBoundary()) return getSuppressedDiagnosticLogger();
118
+ return createFacade(raw);
119
+ }
120
+ function createFacade(raw) {
121
+ const methods = new Map();
122
+ const handlers = new WeakMap();
123
+ const inertProfiler = Object.freeze({
124
+ get logger() {
125
+ return getSuppressedDiagnosticLogger();
126
+ },
127
+ done: () => getSuppressedDiagnosticLogger()
128
+ });
129
+ const facade = new Proxy({}, {
130
+ get(_target, key) {
131
+ if (loggingMethods.has(key) || ['write', 'child', 'startTimer', 'profile'].includes(key)) {
132
+ if (!methods.has(key)) {
133
+ methods.set(key, (...args) => {
134
+ if (!raw || getRequestBoundary()) {
135
+ if (key === 'write') return completeWrite(args);
136
+ if (key === 'child') return getSuppressedDiagnosticLogger();
137
+ if (key === 'startTimer') return inertProfiler;
138
+ return facade;
139
+ }
140
+ const result = Reflect.apply(Reflect.get(raw, key, raw), raw, args);
141
+ if (key === 'child') return createFacade(result);
142
+ if (key === 'startTimer') return wrapProfiler(result, facade);
143
+ return result === raw ? facade : result;
144
+ });
145
+ }
146
+ return methods.get(key);
147
+ }
148
+ if (!raw) return undefined;
149
+ const value = Reflect.get(raw, key, raw);
150
+ if ((key === 'exceptions' || key === 'rejections') && value) {
151
+ if (!handlers.has(value)) handlers.set(value, wrapHandler(value, raw, facade));
152
+ return handlers.get(value);
153
+ }
154
+ if (typeof value !== 'function') return value;
155
+ if (!methods.has(key)) {
156
+ methods.set(key, (...args) => {
157
+ const result = Reflect.apply(value, raw, args);
158
+ return result === raw ? facade : result;
159
+ });
160
+ }
161
+ return methods.get(key);
162
+ },
163
+ set(_target, key, value) {
164
+ return raw ? Reflect.set(raw, key, value, raw) : true;
165
+ }
166
+ });
167
+ return facade;
168
+ }
169
+ function wrapHandler(raw, rawLogger, logger) {
170
+ const methods = new Map();
171
+ const handler = new Proxy({}, {
172
+ get(_target, key) {
173
+ if (key === 'logger') return logger;
174
+ const value = Reflect.get(raw, key, raw);
175
+ if (typeof value !== 'function') return value;
176
+ if (!methods.has(key)) {
177
+ methods.set(key, (...args) => {
178
+ const result = Reflect.apply(value, raw, args);
179
+ if (result === raw) return handler;
180
+ return result === rawLogger ? logger : result;
181
+ });
182
+ }
183
+ return methods.get(key);
184
+ },
185
+ set(_target, key, value) {
186
+ if (key === 'logger') return false;
187
+ return Reflect.set(raw, key, value, raw);
188
+ }
189
+ });
190
+ return handler;
191
+ }
192
+ function wrapProfiler(raw, logger) {
193
+ return new Proxy({}, {
194
+ get(_target, key) {
195
+ if (key === 'logger') return logger;
196
+ if (key === 'done') {
197
+ return (...args) => {
198
+ if (getRequestBoundary()) return logger;
199
+ const result = Reflect.apply(raw.done, raw, args);
200
+ return result === raw.logger ? logger : result;
201
+ };
202
+ }
203
+ return Reflect.get(raw, key, raw);
204
+ },
205
+ set(_target, key, value) {
206
+ if (key === 'logger') return false;
207
+ return Reflect.set(raw, key, value, raw);
208
+ }
209
+ });
210
+ }
211
+
38
212
  function cast(source) {
39
213
  return source;
40
214
  }
@@ -546,7 +720,7 @@ class AppUtil extends BeanSimple {
546
720
  const globalPrefix = '/api/static';
547
721
  if (!staticPath) staticPath = '';
548
722
  // safe
549
- staticPath = path.normalize(staticPath);
723
+ staticPath = path.posix.normalize(staticPath.replace(/\\/g, '/'));
550
724
  if (staticPath.includes('..')) throw new Error(`unsafe path: ${staticPath}`);
551
725
  // ignore globalPrefix
552
726
  if (staticPath.startsWith('//')) return staticPath.substring(1);
@@ -811,9 +985,9 @@ async function createGeneralApp(projectPath, envRuntime) {
811
985
  }
812
986
  }
813
987
  if (process.env.META_MODE === 'prod') {
814
- const testFile = path.join(projectPath, `dist/${process.env.META_FLAVOR}/bootstrap.js`);
988
+ const testFile = path.join(projectPath, `dist/${process.env.META_FLAVOR}/app.js`);
815
989
  const testInstance = await import(pathToHref(testFile));
816
- return await testInstance.appBootstrap;
990
+ return await testInstance.createSingleApp();
817
991
  } else {
818
992
  const testFile = path.join(resolveVonaLocalPaths(projectPath, process.env.META_MODE).generatedRoot, 'app.ts');
819
993
  const testInstance = await import(pathToHref(testFile));
@@ -1184,6 +1358,7 @@ class BeanBase extends BeanBaseSimple {
1184
1358
  return this.$loggerClient('default');
1185
1359
  }
1186
1360
  $loggerClient(clientName = 'default') {
1361
+ if (getRequestBoundary()) return getSuppressedDiagnosticLogger();
1187
1362
  if (!this[SymbolLogger][clientName]) {
1188
1363
  this[SymbolLogger][clientName] = this.app.meta.logger.get(clientName).child({
1189
1364
  beanFullName: this.$beanFullName
@@ -1192,6 +1367,7 @@ class BeanBase extends BeanBaseSimple {
1192
1367
  return this[SymbolLogger][clientName];
1193
1368
  }
1194
1369
  $loggerChild(childName, clientName = 'default') {
1370
+ if (getRequestBoundary()) return getSuppressedDiagnosticLogger();
1195
1371
  if (!this[SymbolLoggerChildren][clientName]) this[SymbolLoggerChildren][clientName] = {};
1196
1372
  if (!this[SymbolLoggerChildren][clientName][childName]) {
1197
1373
  this[SymbolLoggerChildren][clientName][childName] = this.app.meta.logger.get(clientName).child({
@@ -2455,21 +2631,72 @@ class VonaAsyncLocalStorage extends AsyncLocalStorage {
2455
2631
  constructor(app) {
2456
2632
  super();
2457
2633
  this.app = void 0;
2634
+ this.observedRuns = new WeakMap();
2458
2635
  this.app = app;
2459
2636
  }
2460
2637
  async run(store, callback, ...args) {
2461
2638
  if (store === this.app.currentContext) {
2462
2639
  return await callback(...args);
2463
2640
  }
2464
- return super.run(store, async (...args) => {
2641
+ if (this.observedRuns.has(store)) throw new Error('Context scope already owned');
2642
+ return this._runOwned(store, () => callback(...args));
2643
+ }
2644
+ async runObserved(store, callback, observer) {
2645
+ if (store === this.app.currentContext || this.observedRuns.has(store)) {
2646
+ throw new Error('Context scope already owned');
2647
+ }
2648
+ const frame = {
2649
+ settled: false
2650
+ };
2651
+ this.observedRuns.set(store, frame);
2652
+ try {
2653
+ return await this._runOwned(store, callback, observer);
2654
+ } finally {
2655
+ frame.settled = true;
2656
+ }
2657
+ }
2658
+ _runOwned(store, callback, observer) {
2659
+ const capture = captureRequestBoundary();
2660
+ return super.run(store, async () => {
2465
2661
  try {
2466
2662
  this.app.meta.ctxCounter.increment();
2467
- return await callback(...args);
2663
+ return await callback();
2468
2664
  } finally {
2469
- await store.bean.dispose();
2470
- this.app.meta.ctxCounter.decrement();
2665
+ await this._disposeOwned(store, observer, capture);
2666
+ }
2667
+ });
2668
+ }
2669
+ async _disposeOwned(store, observer, capture) {
2670
+ try {
2671
+ await store.bean.dispose();
2672
+ } catch (error) {
2673
+ this._notifyScopeOutcome(observer, 'dispose_failed', capture);
2674
+ throw error;
2675
+ }
2676
+ try {
2677
+ this.app.meta.ctxCounter.decrement();
2678
+ } catch (error) {
2679
+ capture?.reportFailure('diagnostic_failed');
2680
+ throw error;
2681
+ }
2682
+ this._notifyScopeOutcome(observer, 'disposed', capture);
2683
+ }
2684
+ _notifyScopeOutcome(observer, outcome, capture) {
2685
+ if (!observer) return;
2686
+ try {
2687
+ const result = observer(outcome);
2688
+ if (result && (typeof result === 'object' || typeof result === 'function')) {
2689
+ const then = result.then;
2690
+ if (typeof then === 'function') {
2691
+ capture?.reportFailure('observer_failed');
2692
+ void new Promise((resolve, reject) => {
2693
+ then.call(result, resolve, reject);
2694
+ }).catch(() => {});
2695
+ }
2471
2696
  }
2472
- }, ...args);
2697
+ } catch {
2698
+ capture?.reportFailure('observer_failed');
2699
+ }
2473
2700
  }
2474
2701
  }
2475
2702
 
@@ -2646,25 +2873,31 @@ class AppHmr extends BeanSimple {
2646
2873
  }
2647
2874
 
2648
2875
  const SymbolLoggerInstances = Symbol('SymbolLoggerInstances');
2876
+ const SymbolRawLoggerInstances = Symbol('SymbolRawLoggerInstances');
2649
2877
  class AppLogger extends BeanSimple {
2650
2878
  constructor(...args) {
2651
2879
  super(...args);
2652
2880
  this[SymbolLoggerInstances] = {};
2881
+ this[SymbolRawLoggerInstances] = {};
2653
2882
  }
2654
2883
  async dispose() {
2655
- for (const key in this[SymbolLoggerInstances]) {
2656
- const logger = this[SymbolLoggerInstances][key];
2884
+ for (const key in this[SymbolRawLoggerInstances]) {
2885
+ const logger = this[SymbolRawLoggerInstances][key];
2657
2886
  await _closeLogger(logger);
2658
2887
  }
2659
2888
  }
2660
2889
  get(clientName) {
2890
+ if (getRequestBoundary()) return getSuppressedDiagnosticLogger();
2661
2891
  clientName = clientName || 'default';
2662
2892
  if (!this[SymbolLoggerInstances][clientName]) {
2663
- this[SymbolLoggerInstances][clientName] = this._createClient(clientName);
2893
+ const raw = this._createClient(clientName);
2894
+ this[SymbolRawLoggerInstances][clientName] = raw;
2895
+ this[SymbolLoggerInstances][clientName] = createDiagnosticLogger(raw);
2664
2896
  }
2665
2897
  return this[SymbolLoggerInstances][clientName];
2666
2898
  }
2667
2899
  child(childName, clientName) {
2900
+ if (getRequestBoundary()) return getSuppressedDiagnosticLogger();
2668
2901
  const logger = this.get(clientName);
2669
2902
  if (!childName) return logger;
2670
2903
  return logger.child({
@@ -3080,10 +3313,16 @@ var localesDefault = {
3080
3313
  };
3081
3314
 
3082
3315
  const SymbolLoggerMessage = Symbol('SymbolLoggerMessage');
3083
- const formatLoggerAxiosError = Winston.format((einfo, {
3084
- stack,
3085
- cause
3086
- }) => {
3316
+ const formatLoggerBoundary = Winston.format(info => {
3317
+ if (getRequestBoundary()) return false;
3318
+ return info;
3319
+ });
3320
+ const formatLoggerAxiosError = Winston.format((einfo, opts) => {
3321
+ if (getRequestBoundary()) return false;
3322
+ const {
3323
+ stack,
3324
+ cause
3325
+ } = opts;
3087
3326
  if (einfo instanceof Error && einfo.constructor.name.includes('AxiosError') || einfo.name === 'AxiosError' || einfo.isAxiosError) {
3088
3327
  const info = Object.assign({}, einfo, {
3089
3328
  level: einfo.level,
@@ -3103,9 +3342,11 @@ const formatLoggerAxiosError = Winston.format((einfo, {
3103
3342
  return einfo;
3104
3343
  });
3105
3344
  const formatLoggerErrors = opts => {
3106
- return Winston.format.combine(formatLoggerAxiosError(opts), Winston.format.errors(opts));
3345
+ if (getRequestBoundary()) return formatLoggerBoundary();
3346
+ return Winston.format.combine(formatLoggerBoundary(), formatLoggerAxiosError(opts), Winston.format.errors(opts));
3107
3347
  };
3108
3348
  const formatLoggerCtx = Winston.format((info, _opts) => {
3349
+ if (getRequestBoundary()) return false;
3109
3350
  const app = useApp();
3110
3351
  if (!app.ctx) return info;
3111
3352
  if (app.ctx.method) info.method = app.ctx.method;
@@ -3122,11 +3363,13 @@ const formatLoggerCtx = Winston.format((info, _opts) => {
3122
3363
  return info;
3123
3364
  });
3124
3365
  const formatLoggerDummy = Winston.format(info => {
3366
+ if (getRequestBoundary()) return false;
3125
3367
  const app = useApp();
3126
3368
  if (app.meta.env.LOGGER_DUMMY === 'true') return false;
3127
3369
  return info;
3128
3370
  });
3129
3371
  const formatLoggerFilter = Winston.format((info, opts) => {
3372
+ if (getRequestBoundary()) return false;
3130
3373
  // check client
3131
3374
  const child = typeof opts.child === 'function' ? opts.child() : opts.child;
3132
3375
  if (child && !child.includes(info.name)) {
@@ -3146,16 +3389,18 @@ const formatLoggerFilter = Winston.format((info, opts) => {
3146
3389
  return false;
3147
3390
  });
3148
3391
  const formatLoggerConsole = clientInfo => {
3149
- return Winston.format.printf(({
3150
- timestamp,
3151
- level,
3152
- stack,
3153
- message,
3154
- name,
3155
- beanFullName,
3156
- durationMs,
3157
- ...meta
3158
- }) => {
3392
+ return Winston.format.combine(formatLoggerBoundary(), Winston.format.printf(info => {
3393
+ if (getRequestBoundary()) return '';
3394
+ const {
3395
+ timestamp,
3396
+ level,
3397
+ stack,
3398
+ message,
3399
+ name,
3400
+ beanFullName,
3401
+ durationMs,
3402
+ ...meta
3403
+ } = info;
3159
3404
  const textClientName = clientInfo.clientName === 'default' ? '' : ` ${chalk.cyan(`[${clientInfo.clientName}]`)}`;
3160
3405
  const textName = name ? ` ${chalk.cyan(`[${name}]`)}` : '';
3161
3406
  const textBeanFullName = beanFullName ? ` ${chalk.gray(`[${beanFullName}]`)}` : '';
@@ -3164,9 +3409,10 @@ const formatLoggerConsole = clientInfo => {
3164
3409
  const textDurationMs = durationMs !== undefined ? ` ${chalk.cyan(`+${durationMs}ms`)}` : '';
3165
3410
  const textStack = stack ? `\n${stack}` : '';
3166
3411
  return `${timestamp} ${level}${textClientName}${textName}${textBeanFullName}${textMeta}${textMessage}${textDurationMs}${textStack}`;
3167
- });
3412
+ }));
3168
3413
  };
3169
3414
  function __formatLoggerFilterCheckInfo(info) {
3415
+ if (getRequestBoundary()) return false;
3170
3416
  if (typeof info.message === 'function') {
3171
3417
  if (info.message[SymbolLoggerMessage] === undefined) {
3172
3418
  info.message[SymbolLoggerMessage] = info.message();
@@ -3176,8 +3422,307 @@ function __formatLoggerFilterCheckInfo(info) {
3176
3422
  return info;
3177
3423
  }
3178
3424
 
3425
+ // Both source and bundled dist consumers use the checked-in, published src assets.
3426
+ function getWindowsTestClaimFilesystemHelper(directory = import.meta.dirname ?? path.dirname(fileURLToPath(import.meta.url))) {
3427
+ let current = directory;
3428
+ while (true) {
3429
+ const manifest = path.join(current, 'package.json');
3430
+ if (existsSync(manifest)) {
3431
+ const value = JSON.parse(readFileSync(manifest, 'utf8'));
3432
+ if (value.name === 'vona-core') {
3433
+ const helper = path.join(current, 'src/lib/core/testClaimFilesystemWindows.ps1');
3434
+ if (!existsSync(helper) || !existsSync(helper.replace(/\.ps1$/, '.cs'))) throw new Error('Windows test claim filesystem helper assets are missing');
3435
+ return helper;
3436
+ }
3437
+ }
3438
+ const parent = path.dirname(current);
3439
+ if (parent === current) throw new Error('Cannot locate the vona-core Windows filesystem helper');
3440
+ current = parent;
3441
+ }
3442
+ }
3443
+ function invocation() {
3444
+ if (process.platform !== 'win32') throw new Error('Windows claim filesystem requires Windows');
3445
+ return ['-NoLogo', '-NoProfile', '-NonInteractive', '-File', getWindowsTestClaimFilesystemHelper()];
3446
+ }
3447
+ function decode(source) {
3448
+ let value;
3449
+ try {
3450
+ value = JSON.parse(source);
3451
+ } catch {
3452
+ throw new Error('Windows test claim filesystem helper returned an invalid response');
3453
+ }
3454
+ if (!value || value.ok !== true) {
3455
+ const error = new Error(value?.code === 'EEXIST' ? 'Test claim file already exists' : `Unsafe Windows test claim filesystem operation (${value?.code ?? 'unknown'})`);
3456
+ Object.assign(error, {
3457
+ code: value?.code ?? 'EUNSAFE'
3458
+ });
3459
+ throw error;
3460
+ }
3461
+ return value;
3462
+ }
3463
+ function windowsTestClaimFilesystemSync(request) {
3464
+ try {
3465
+ return decode(execFileSync('pwsh', invocation(), {
3466
+ input: JSON.stringify(request),
3467
+ encoding: 'utf8',
3468
+ windowsHide: true,
3469
+ timeout: 30000,
3470
+ maxBuffer: 8 * 1024 * 1024
3471
+ }));
3472
+ } catch (error) {
3473
+ if (error.code === 'ENOENT') throw new Error('Native Windows test claims require PowerShell 7 (pwsh) on PATH');
3474
+ throw error;
3475
+ }
3476
+ }
3477
+ let bridge;
3478
+ function windowsTestClaimFilesystem(request) {
3479
+ if (!bridge) {
3480
+ // Explicit executable + argv, never a shell. A burst shares Add-Type startup;
3481
+ // the bridge owns no persistent native handles and exits when idle.
3482
+ const child = execFile('pwsh', invocation(), {
3483
+ windowsHide: true,
3484
+ maxBuffer: Infinity
3485
+ });
3486
+ // execFile's collection listeners are for its callback API. This bounded
3487
+ // line protocol consumes the streams itself, without retaining burst history.
3488
+ child.stdout?.removeAllListeners('data');
3489
+ child.stderr?.removeAllListeners('data');
3490
+ const state = {
3491
+ child,
3492
+ pending: [],
3493
+ output: '',
3494
+ idle: undefined
3495
+ };
3496
+ bridge = state;
3497
+ const fail = error => {
3498
+ if (bridge === state) bridge = undefined;
3499
+ clearTimeout(state.idle);
3500
+ for (const operation of state.pending.splice(0)) {
3501
+ clearTimeout(operation.timer);
3502
+ operation.reject(error);
3503
+ }
3504
+ child.stdin?.destroy();
3505
+ child.kill();
3506
+ };
3507
+ child.once('error', error => fail(new Error(error.code === 'ENOENT' ? 'Native Windows test claims require PowerShell 7 (pwsh) on PATH' : 'Windows test claim filesystem helper failed; operation not acknowledged')));
3508
+ child.once('exit', () => fail(new Error('Windows test claim filesystem helper exited; operation not acknowledged')));
3509
+ child.stdin?.on('error', () => fail(new Error('Windows test claim filesystem control stream failed')));
3510
+ child.stdout?.setEncoding('utf8');
3511
+ child.stderr?.resume();
3512
+ child.stdout?.on('data', chunk => {
3513
+ state.output += chunk;
3514
+ if (Buffer.byteLength(state.output) > 8 * 1024 * 1024) {
3515
+ fail(new Error('Windows test claim filesystem response exceeded limit'));
3516
+ return;
3517
+ }
3518
+ while (state.output.includes('\n')) {
3519
+ const end = state.output.indexOf('\n');
3520
+ const line = state.output.slice(0, end);
3521
+ state.output = state.output.slice(end + 1);
3522
+ const operation = state.pending.shift();
3523
+ if (!operation) {
3524
+ fail(new Error('Unexpected Windows test claim filesystem response'));
3525
+ return;
3526
+ }
3527
+ clearTimeout(operation.timer);
3528
+ try {
3529
+ operation.resolve(decode(line));
3530
+ } catch (error) {
3531
+ operation.reject(error);
3532
+ }
3533
+ }
3534
+ if (!state.pending.length) {
3535
+ state.idle = setTimeout(() => {
3536
+ if (bridge === state) bridge = undefined;
3537
+ child.stdin?.end();
3538
+ }, 1000);
3539
+ }
3540
+ });
3541
+ }
3542
+ const state = bridge;
3543
+ clearTimeout(state.idle);
3544
+ return new Promise((resolve, reject) => {
3545
+ const timer = setTimeout(() => {
3546
+ if (bridge === state) bridge = undefined;
3547
+ for (const operation of state.pending.splice(0)) {
3548
+ clearTimeout(operation.timer);
3549
+ operation.reject(new Error('Windows test claim filesystem operation timed out; not acknowledged'));
3550
+ }
3551
+ state.child.kill();
3552
+ }, 30000);
3553
+ state.pending.push({
3554
+ resolve,
3555
+ reject,
3556
+ timer
3557
+ });
3558
+ state.child.stdin?.write(`${JSON.stringify(request)}\n`);
3559
+ });
3560
+ }
3561
+
3562
+ const TEST_CLAIM_FILE_LIMIT = 4 * 1024 * 1024;
3563
+ function assertTestClaimFilesystemSafePath(file, allowMissing = true) {
3564
+ if (process.platform === 'win32') {
3565
+ windowsTestClaimFilesystemSync({
3566
+ command: 'safe-path',
3567
+ path: path.resolve(file),
3568
+ allowMissing
3569
+ });
3570
+ return;
3571
+ }
3572
+ let current = path.resolve(file);
3573
+ while (true) {
3574
+ try {
3575
+ const stat = lstatSync(current);
3576
+ if (stat.isSymbolicLink() || current !== path.resolve(file) && !stat.isDirectory()) throw new Error('Unsafe test claim path');
3577
+ } catch (error) {
3578
+ if (!allowMissing || error.code !== 'ENOENT') throw error;
3579
+ }
3580
+ const parent = path.dirname(current);
3581
+ if (parent === current) break;
3582
+ current = parent;
3583
+ }
3584
+ }
3585
+ async function assertTestClaimPrivateDirectory(directory, missingAllowed = true) {
3586
+ if (process.platform === 'win32') {
3587
+ const value = await windowsTestClaimFilesystem({
3588
+ command: 'directory',
3589
+ path: path.resolve(directory),
3590
+ allowMissing: missingAllowed
3591
+ });
3592
+ return value.exists === true;
3593
+ }
3594
+ assertTestClaimFilesystemSafePath(directory, missingAllowed);
3595
+ let stat;
3596
+ try {
3597
+ stat = await lstat(directory);
3598
+ } catch (error) {
3599
+ if (missingAllowed && error.code === 'ENOENT') return false;
3600
+ throw error;
3601
+ }
3602
+ if (!stat.isDirectory() || (stat.mode & 0o077) !== 0 || process.getuid && stat.uid !== process.getuid()) {
3603
+ throw new Error('Unsafe shared test claim directory');
3604
+ }
3605
+ return true;
3606
+ }
3607
+ async function ensureTestClaimPrivateDirectory(directory) {
3608
+ if (process.platform === 'win32') {
3609
+ await windowsTestClaimFilesystem({
3610
+ command: 'ensure-directory',
3611
+ path: path.resolve(directory)
3612
+ });
3613
+ return;
3614
+ }
3615
+ assertTestClaimFilesystemSafePath(directory);
3616
+ await mkdir(directory, {
3617
+ mode: 0o700
3618
+ }).catch(error => {
3619
+ if (error.code !== 'EEXIST') throw error;
3620
+ });
3621
+ await assertTestClaimPrivateDirectory(directory, false);
3622
+ }
3623
+ function assertPrivateFile(file) {
3624
+ assertTestClaimFilesystemSafePath(file, false);
3625
+ const stat = lstatSync(file);
3626
+ if (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o077) !== 0 || process.getuid && stat.uid !== process.getuid()) {
3627
+ throw new Error('Unsafe test claim file');
3628
+ }
3629
+ }
3630
+ async function syncDirectory(directory) {
3631
+ const handle = await open(directory, constants.O_RDONLY);
3632
+ try {
3633
+ await handle.sync();
3634
+ } finally {
3635
+ await handle.close();
3636
+ }
3637
+ }
3638
+ function checkContent(content) {
3639
+ if (Buffer.byteLength(content) > TEST_CLAIM_FILE_LIMIT) throw new Error('Invalid test claim file size');
3640
+ }
3641
+ async function readTestClaimFile(file) {
3642
+ if (process.platform === 'win32') {
3643
+ const value = await windowsTestClaimFilesystem({
3644
+ command: 'read',
3645
+ path: path.resolve(file)
3646
+ });
3647
+ if (typeof value.content !== 'string') throw new Error('Invalid test claim file response');
3648
+ return value.content;
3649
+ }
3650
+ assertPrivateFile(file);
3651
+ const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
3652
+ try {
3653
+ const stat = await handle.stat();
3654
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > TEST_CLAIM_FILE_LIMIT || (stat.mode & 0o077) !== 0 || process.getuid && stat.uid !== process.getuid()) {
3655
+ throw new Error('Invalid test claim file');
3656
+ }
3657
+ // Bound the allocation and bytes read even if the file grows after fstat.
3658
+ const buffer = Buffer.alloc(TEST_CLAIM_FILE_LIMIT + 1);
3659
+ let offset = 0;
3660
+ while (offset < buffer.length) {
3661
+ const {
3662
+ bytesRead
3663
+ } = await handle.read(buffer, offset, buffer.length - offset, null);
3664
+ if (!bytesRead) break;
3665
+ offset += bytesRead;
3666
+ }
3667
+ if (offset > TEST_CLAIM_FILE_LIMIT) throw new Error('Invalid test claim file size');
3668
+ return buffer.subarray(0, offset).toString('utf8');
3669
+ } finally {
3670
+ await handle.close();
3671
+ }
3672
+ }
3673
+ async function createTestClaimFile(file, content) {
3674
+ checkContent(content);
3675
+ if (process.platform === 'win32') {
3676
+ await windowsTestClaimFilesystem({
3677
+ command: 'create',
3678
+ path: path.resolve(file),
3679
+ content
3680
+ });
3681
+ return;
3682
+ }
3683
+ assertTestClaimFilesystemSafePath(file);
3684
+ const handle = await open(file, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
3685
+ try {
3686
+ await handle.writeFile(content);
3687
+ await handle.sync();
3688
+ } finally {
3689
+ await handle.close();
3690
+ }
3691
+ await syncDirectory(path.dirname(file));
3692
+ }
3693
+ async function replaceTestClaimFile(file, content) {
3694
+ checkContent(content);
3695
+ if (process.platform === 'win32') {
3696
+ await windowsTestClaimFilesystem({
3697
+ command: 'replace',
3698
+ path: path.resolve(file),
3699
+ content
3700
+ });
3701
+ return;
3702
+ }
3703
+ const temp = `${file}.${randomBytes(12).toString('hex')}.tmp`;
3704
+ const handle = await open(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
3705
+ try {
3706
+ try {
3707
+ await handle.writeFile(content);
3708
+ await handle.sync();
3709
+ } finally {
3710
+ await handle.close();
3711
+ }
3712
+ assertPrivateFile(file);
3713
+ await rename(temp, file);
3714
+ await syncDirectory(path.dirname(file));
3715
+ } finally {
3716
+ await unlink(temp).catch(error => {
3717
+ if (error.code !== 'ENOENT') throw error;
3718
+ });
3719
+ }
3720
+ }
3721
+
3179
3722
  const TEST_CLAIM_URL_ENV = 'VONA_TEST_CLAIM_URL';
3180
3723
  const TEST_CLAIM_TOKEN_ENV = 'VONA_TEST_CLAIM_TOKEN';
3724
+ /** A locator only. Containment still requires the registered guardian's live native verifier. */
3725
+ const TEST_CLAIM_WINDOWS_GUARDIAN_ENV = 'VONA_TEST_WINDOWS_GUARDIAN_ID';
3181
3726
  const TEST_CLAIM_DIRECTORY = '.vona-test-claim';
3182
3727
  function getTestClaimPaths(projectPath, runId) {
3183
3728
  resolveTestRunId('test', runId);
@@ -3196,20 +3741,7 @@ function getTestClaimCreatedAt(runId) {
3196
3741
  return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}T${s.slice(8, 10)}:${s.slice(10, 12)}:${s.slice(12, 14)}.000Z`;
3197
3742
  }
3198
3743
  function assertTestClaimSafePath(file, allowMissing = true) {
3199
- let current = path.resolve(file);
3200
- while (true) {
3201
- try {
3202
- const stat = lstatSync(current);
3203
- if (stat.isSymbolicLink() || current !== path.resolve(file) && !stat.isDirectory()) {
3204
- throw new Error('Unsafe test claim path');
3205
- }
3206
- } catch (error) {
3207
- if (!allowMissing || error.code !== 'ENOENT') throw error;
3208
- }
3209
- const parent = path.dirname(current);
3210
- if (parent === current) break;
3211
- current = parent;
3212
- }
3744
+ assertTestClaimFilesystemSafePath(file, allowMissing);
3213
3745
  }
3214
3746
  function record(value) {
3215
3747
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid test claim object');
@@ -3334,16 +3866,28 @@ function validateTestClaim(value, claimPath) {
3334
3866
  array(m.processes);
3335
3867
  unique(m.processes.map(item => item.id));
3336
3868
  for (const proc of m.processes) {
3337
- keys(proc, ['id', 'role', 'stage', 'pid', 'pgid', 'startTime', 'host', 'profilePath']);
3869
+ keys(proc, ['id', 'role', 'stage', 'pid', 'pgid', 'containment', 'jobId', 'startTime', 'host', 'profilePath']);
3338
3870
  label(proc.id);
3339
3871
  oneOf(proc.role, ['guardian', 'browser', 'cleanup']);
3340
3872
  if (proc.host !== undefined) text(proc.host);
3341
3873
  oneOf(proc.stage, ['pending', 'registered', 'drained']);
3874
+ if (proc.containment !== undefined) oneOf(proc.containment, ['windows-job']);
3875
+ if (proc.containment === 'windows-job') {
3876
+ if (proc.pgid !== undefined || proc.role !== 'guardian' || proc.profilePath !== undefined) throw new Error('Invalid Windows test claim containment');
3877
+ text(proc.jobId);
3878
+ if (!/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(proc.jobId)) throw new Error('Invalid Windows test claim job identity');
3879
+ } else if (proc.jobId !== undefined) {
3880
+ throw new Error('Windows test claim job needs explicit containment');
3881
+ }
3342
3882
  const hasIdentity = proc.pid !== undefined || proc.pgid !== undefined || proc.startTime !== undefined;
3343
3883
  if (hasIdentity || proc.stage !== 'pending') {
3344
3884
  integer(proc.pid, 1);
3345
- integer(proc.pgid, 1);
3885
+ if (proc.containment !== 'windows-job') integer(proc.pgid, 1);
3346
3886
  text(proc.startTime);
3887
+ if (proc.containment === 'windows-job') {
3888
+ if (!/^[1-9]\d{0,19}$/.test(proc.startTime)) throw new Error('Invalid Windows test claim process creation time');
3889
+ text(proc.host);
3890
+ }
3347
3891
  }
3348
3892
  if (proc.profilePath !== undefined) {
3349
3893
  text(proc.profilePath);
@@ -3356,23 +3900,8 @@ function validateTestClaim(value, claimPath) {
3356
3900
  for (const key of ['processes', 'databases', 'redis', 'locals']) bool(m.cleanup[key]);
3357
3901
  return m;
3358
3902
  }
3359
- function assertPrivateFile(file) {
3360
- assertTestClaimSafePath(file, false);
3361
- const stat = lstatSync(file);
3362
- if (!stat.isFile() || stat.nlink !== 1 || (stat.mode & 0o077) !== 0 || process.getuid && stat.uid !== process.getuid()) {
3363
- throw new Error('Unsafe test claim file');
3364
- }
3365
- }
3366
3903
  async function loadTestClaim(claimPath) {
3367
- assertPrivateFile(claimPath);
3368
- const handle = await open(claimPath, constants.O_RDONLY | constants.O_NOFOLLOW);
3369
- try {
3370
- const stat = await handle.stat();
3371
- if (!stat.isFile() || stat.size > 4 * 1024 * 1024) throw new Error('Invalid test claim file');
3372
- return validateTestClaim(parseTestClaimJson(await handle.readFile('utf8')), claimPath);
3373
- } finally {
3374
- await handle.close();
3375
- }
3904
+ return validateTestClaim(parseTestClaimJson(await readTestClaimFile(claimPath)), claimPath);
3376
3905
  }
3377
3906
  function parseTestClaimJson(source) {
3378
3907
  // Claim identities stay literal even when @cabloy/json5 revives ISO strings.
@@ -3380,31 +3909,13 @@ function parseTestClaimJson(source) {
3380
3909
  return this[key];
3381
3910
  });
3382
3911
  }
3383
- async function syncDirectory(directory) {
3384
- const handle = await open(directory, constants.O_RDONLY);
3385
- try {
3386
- await handle.sync();
3387
- } finally {
3388
- await handle.close();
3389
- }
3390
- }
3391
3912
  async function createTestClaim(projectPath, runId, kind, owner) {
3392
3913
  const p = getTestClaimPaths(projectPath, runId);
3393
- assertTestClaimSafePath(p.claimDirectory);
3394
- await mkdir(p.claimDirectory, {
3395
- mode: 0o700
3396
- }).catch(error => {
3397
- if (error.code !== 'EEXIST') throw error;
3398
- });
3914
+ await ensureTestClaimPrivateDirectory(p.claimDirectory);
3399
3915
  // The directory is shared across runs; an existing directory is valid, never an existing claim.
3400
3916
  return createClaimFile(p, runId, kind, owner);
3401
3917
  }
3402
3918
  async function createClaimFile(p, runId, kind, owner) {
3403
- assertTestClaimSafePath(p.claimDirectory, false);
3404
- const directoryStat = lstatSync(p.claimDirectory);
3405
- if (!directoryStat.isDirectory() || (directoryStat.mode & 0o077) !== 0 || process.getuid && directoryStat.uid !== process.getuid()) {
3406
- throw new Error('Unsafe shared test claim directory');
3407
- }
3408
3919
  const manifest = {
3409
3920
  version: 1,
3410
3921
  runId,
@@ -3433,14 +3944,7 @@ async function createClaimFile(p, runId, kind, owner) {
3433
3944
  }
3434
3945
  };
3435
3946
  validateTestClaim(manifest, p.claimPath);
3436
- const handle = await open(p.claimPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
3437
- try {
3438
- await handle.writeFile(`${JSON.stringify(manifest, null, 2)}\n`);
3439
- await handle.sync();
3440
- } finally {
3441
- await handle.close();
3442
- }
3443
- await syncDirectory(p.claimDirectory);
3947
+ await createTestClaimFile(p.claimPath, `${JSON.stringify(manifest, null, 2)}\n`);
3444
3948
  return {
3445
3949
  claimPath: p.claimPath,
3446
3950
  manifest
@@ -3454,23 +3958,7 @@ async function writeTestClaim(claimPath, manifest) {
3454
3958
  for (const key of ['version', 'runId', 'createdAt', 'projectPath', 'kind', 'owner']) {
3455
3959
  if (!isDeepStrictEqual(previous[key], manifest[key])) throw new Error('Test claim identity is immutable');
3456
3960
  }
3457
- const temp = `${claimPath}.${randomBytes(12).toString('hex')}.tmp`;
3458
- const handle = await open(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
3459
- try {
3460
- try {
3461
- await handle.writeFile(`${JSON.stringify(manifest, null, 2)}\n`);
3462
- await handle.sync();
3463
- } finally {
3464
- await handle.close();
3465
- }
3466
- assertPrivateFile(claimPath);
3467
- await rename(temp, claimPath);
3468
- await syncDirectory(path.dirname(claimPath));
3469
- } finally {
3470
- await unlink(temp).catch(error => {
3471
- if (error.code !== 'ENOENT') throw error;
3472
- });
3473
- }
3961
+ await replaceTestClaimFile(claimPath, `${JSON.stringify(manifest, null, 2)}\n`);
3474
3962
  }
3475
3963
  function applyEvent(m, event) {
3476
3964
  record(event);
@@ -3497,7 +3985,9 @@ function applyEvent(m, event) {
3497
3985
  if (event.process.stage !== 'pending') throw new Error('Process needs a write-ahead intent');
3498
3986
  m.processes.push(event.process);
3499
3987
  } else {
3500
- if (old.role !== event.process.role) throw new Error('Test claim process identity changed');
3988
+ if (old.role !== event.process.role || old.containment !== event.process.containment || old.jobId !== event.process.jobId) {
3989
+ throw new Error('Test claim process identity changed');
3990
+ }
3501
3991
  if (event.process.profilePath !== undefined && old.profilePath !== event.process.profilePath) {
3502
3992
  if (old.stage !== 'pending' || event.process.stage !== 'registered' || !old.profilePath || !event.process.profilePath.startsWith(`${old.profilePath}${path.sep}`)) {
3503
3993
  throw new Error('Test claim process profile changed');
@@ -3578,11 +4068,79 @@ function applyEvent(m, event) {
3578
4068
  }
3579
4069
  validateTestClaim(m);
3580
4070
  }
4071
+ // Native verifiers are controller-local capabilities, never HTTP registrations or journal data.
4072
+ const liveWindowsBrokers = new Map();
4073
+ function validateWindowsJobQuery(value) {
4074
+ keys(value, ['runId', 'guardianId', 'process']);
4075
+ const query = value;
4076
+ resolveTestRunId('test', query.runId);
4077
+ label(query.guardianId);
4078
+ keys(query.process, ['pid', 'startTime', 'host']);
4079
+ integer(query.process.pid, 1, 0xffffffff);
4080
+ text(query.process.startTime);
4081
+ if (!/^[1-9]\d{0,19}$/.test(query.process.startTime)) throw new Error('Invalid Windows containment creation time');
4082
+ text(query.process.host);
4083
+ }
4084
+
4085
+ /** Only the controller hosting this authenticated broker can attach its sole native Job owner. */
4086
+ async function registerTestClaimWindowsJobVerifier(env, guardian, verify) {
4087
+ if (!hasTestClaimTransport(env)) throw new Error('Windows containment requires a claim broker');
4088
+ if (!resolveTestRunId(env.META_MODE, env.VONA_TEST_RUN_ID)) throw new Error('Windows containment requires test identity');
4089
+ const broker = liveWindowsBrokers.get(env[TEST_CLAIM_TOKEN_ENV]);
4090
+ if (!broker || broker.url !== env[TEST_CLAIM_URL_ENV]) throw new Error('Windows containment verifier must belong to the local claim broker');
4091
+ return await broker.register(guardian, verify);
4092
+ }
3581
4093
  async function startTestClaimBroker(claimPath) {
3582
4094
  await loadTestClaim(claimPath);
3583
4095
  const token = randomBytes(32).toString('hex');
3584
4096
  let queue = Promise.resolve();
3585
4097
  let closing = false;
4098
+ const verifiers = new Map();
4099
+ let queries = 0;
4100
+ const containment = async query => {
4101
+ validateWindowsJobQuery(query);
4102
+ if (closing || queries >= 32) throw new Error('Windows containment verifier unavailable');
4103
+ queries++;
4104
+ try {
4105
+ await queue;
4106
+ const manifest = await loadTestClaim(claimPath);
4107
+ const live = verifiers.get(query.guardianId);
4108
+ const guardian = manifest.processes.find(item => item.id === query.guardianId);
4109
+ if (manifest.kind !== 'e2e' || manifest.runId !== query.runId || guardian?.stage !== 'registered' || guardian.role !== 'guardian' || guardian.containment !== 'windows-job' || guardian.host !== query.process.host || !live || !isDeepStrictEqual(live.guardian, guardian)) {
4110
+ throw new Error('Windows containment requires a registered live E2E guardian');
4111
+ }
4112
+ let timer;
4113
+ try {
4114
+ const verified = await Promise.race([live.verify(query.process), new Promise((_, reject) => {
4115
+ timer = setTimeout(() => reject(new Error('Windows containment query timed out')), 5000);
4116
+ })]);
4117
+ await queue;
4118
+ const current = await loadTestClaim(claimPath);
4119
+ if (!verified || closing || verifiers.get(query.guardianId) !== live || !isDeepStrictEqual(current.processes.find(item => item.id === query.guardianId), guardian)) {
4120
+ throw new Error('Windows process is not in the designated live Job');
4121
+ }
4122
+ } finally {
4123
+ clearTimeout(timer);
4124
+ }
4125
+ } finally {
4126
+ queries--;
4127
+ }
4128
+ };
4129
+ const register = async (guardian, verify) => {
4130
+ await queue;
4131
+ const manifest = await loadTestClaim(claimPath);
4132
+ if (closing || guardian.role !== 'guardian' || guardian.stage !== 'registered' || guardian.containment !== 'windows-job' || typeof verify !== 'function' || verifiers.has(guardian.id) || !isDeepStrictEqual(manifest.processes.find(item => item.id === guardian.id), guardian)) {
4133
+ throw new Error('Windows containment verifier needs a durable registered guardian');
4134
+ }
4135
+ const live = {
4136
+ guardian: structuredClone(guardian),
4137
+ verify
4138
+ };
4139
+ verifiers.set(guardian.id, live);
4140
+ return () => {
4141
+ if (verifiers.get(guardian.id) === live) verifiers.delete(guardian.id);
4142
+ };
4143
+ };
3586
4144
  const update = event => {
3587
4145
  if (closing) return Promise.reject(new Error('Test claim broker is closed'));
3588
4146
  const result = queue.then(async () => {
@@ -3596,7 +4154,7 @@ async function startTestClaimBroker(claimPath) {
3596
4154
  const server = createServer(async (req, res) => {
3597
4155
  const auth = req.headers.authorization;
3598
4156
  const expected = `Bearer ${token}`;
3599
- if (req.method !== 'POST' || req.url !== '/event' || typeof auth !== 'string' || Buffer.byteLength(auth) !== Buffer.byteLength(expected) || !timingSafeEqual(Buffer.from(auth), Buffer.from(expected))) {
4157
+ if (req.method !== 'POST' || !['/event', '/containment'].includes(req.url) || typeof auth !== 'string' || Buffer.byteLength(auth) !== Buffer.byteLength(expected) || !timingSafeEqual(Buffer.from(auth), Buffer.from(expected))) {
3600
4158
  res.writeHead(403).end();
3601
4159
  req.resume();
3602
4160
  return;
@@ -3607,7 +4165,7 @@ async function startTestClaimBroker(claimPath) {
3607
4165
  body += chunk.toString('utf8');
3608
4166
  if (Buffer.byteLength(body) > 1024 * 1024) throw new Error('Test claim event too large');
3609
4167
  }
3610
- await update(parseTestClaimJson(body));
4168
+ if (req.url === '/containment') await containment(parseTestClaimJson(body));else await update(parseTestClaimJson(body));
3611
4169
  res.writeHead(204).end();
3612
4170
  } catch {
3613
4171
  res.writeHead(409).end('Test claim registration rejected');
@@ -3621,14 +4179,21 @@ async function startTestClaimBroker(claimPath) {
3621
4179
  });
3622
4180
  const address = server.address();
3623
4181
  if (!address || typeof address === 'string') throw new Error('Cannot bind test claim broker');
4182
+ const url = `http://127.0.0.1:${address.port}/event`;
4183
+ liveWindowsBrokers.set(token, {
4184
+ url,
4185
+ register
4186
+ });
3624
4187
  return {
3625
4188
  env: {
3626
- [TEST_CLAIM_URL_ENV]: `http://127.0.0.1:${address.port}/event`,
4189
+ [TEST_CLAIM_URL_ENV]: url,
3627
4190
  [TEST_CLAIM_TOKEN_ENV]: token
3628
4191
  },
3629
4192
  update,
3630
4193
  async close() {
3631
4194
  closing = true;
4195
+ liveWindowsBrokers.delete(token);
4196
+ verifiers.clear();
3632
4197
  await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
3633
4198
  await queue;
3634
4199
  }
@@ -3669,6 +4234,36 @@ async function sendTestClaimEvent(env, event) {
3669
4234
  throw new Error('Managed test allocation registration failed');
3670
4235
  }
3671
4236
  }
4237
+
4238
+ /** A transient authenticated gate: success is live exact membership, never a durable process event. */
4239
+ async function assertTestClaimWindowsJob(env, identity) {
4240
+ try {
4241
+ if (!hasTestClaimTransport(env)) throw new Error('Missing test claim transport');
4242
+ const query = {
4243
+ runId: env.VONA_TEST_RUN_ID,
4244
+ guardianId: env[TEST_CLAIM_WINDOWS_GUARDIAN_ENV],
4245
+ process: {
4246
+ pid: identity.pid,
4247
+ startTime: identity.startTime,
4248
+ host: identity.host
4249
+ }
4250
+ };
4251
+ validateWindowsJobQuery(query);
4252
+ const response = await fetch(new URL('/containment', env[TEST_CLAIM_URL_ENV]), {
4253
+ method: 'POST',
4254
+ headers: {
4255
+ 'authorization': `Bearer ${env[TEST_CLAIM_TOKEN_ENV]}`,
4256
+ 'content-type': 'application/json'
4257
+ },
4258
+ body: JSON.stringify(query),
4259
+ signal: AbortSignal.timeout(7000),
4260
+ redirect: 'error'
4261
+ });
4262
+ if (response.status !== 204) throw new Error('Containment rejected');
4263
+ } catch {
4264
+ throw new Error('Managed Windows browser containment verification failed');
4265
+ }
4266
+ }
3672
4267
  function getTestClaimRedisPrefix(runId, clientName) {
3673
4268
  const appName = getTestAppName(runId);
3674
4269
  if (clientName === 'broadcast') return undefined;
@@ -3887,6 +4482,164 @@ function assertTestClaimRedisClient(app, clientName, options) {
3887
4482
  if (plan.kind === 'client' ? options.keyPrefix !== plan.prefix : options.keyPrefix !== undefined) throw new Error('Unregistered managed Redis key prefix');
3888
4483
  }
3889
4484
 
4485
+ const TEST_REQUIRE_FRESH_RESOURCES_ENV = 'VONA_TEST_REQUIRE_FRESH_RESOURCES';
4486
+ const freshApps = new WeakSet();
4487
+ function requiresFreshTestResources() {
4488
+ const value = process.env[TEST_REQUIRE_FRESH_RESOURCES_ENV];
4489
+ if (value !== undefined && value !== 'true') {
4490
+ throw new Error('Invalid strict test resource freshness flag');
4491
+ }
4492
+ return value === 'true';
4493
+ }
4494
+ function assertFreshTestResourcesReady(app) {
4495
+ if (!requiresFreshTestResources()) return;
4496
+ if (!freshApps.has(app)) throw new Error('Strict test resource freshness was not established');
4497
+ }
4498
+ async function assertEmptyDirectory(directory) {
4499
+ assertIsolatedTestPath(directory, directory);
4500
+ try {
4501
+ if (!(await lstat(directory)).isDirectory() || (await readdir(directory)).length !== 0) {
4502
+ throw new Error('Strict test local resource is not fresh');
4503
+ }
4504
+ } catch (error) {
4505
+ if (error.code !== 'ENOENT') throw error;
4506
+ }
4507
+ }
4508
+
4509
+ // Runs after configuration, before module loading (including worker-alive Redis writes).
4510
+ // This is a freshness snapshot, not a lease against independent external writers.
4511
+ async function assertFreshTestResources(app, dependencies) {
4512
+ if (!requiresFreshTestResources()) return;
4513
+ if (!app.meta.env.VONA_TEST_RUN_ID || app.meta.appReady || freshApps.has(app)) {
4514
+ throw new Error('Strict test freshness requires a new isolated startup');
4515
+ }
4516
+ assertIsolatedTestConfig(app);
4517
+ const config = app.config;
4518
+ const paths = resolveVonaLocalPaths(app.projectPath, 'test', app.meta.env.VONA_TEST_RUN_ID);
4519
+ for (const directory of [paths.publicRoot, paths.runtimeRoot, paths.sqliteRoot]) {
4520
+ await assertEmptyDirectory(directory);
4521
+ }
4522
+ const inventory = buildTestClaimResourceInventory(app);
4523
+ const readers = dependencies ?? createFreshnessDependencies(app.projectPath);
4524
+ try {
4525
+ for (const plan of inventory.databases) {
4526
+ if (plan.endpoint.dialect === 'better-sqlite3') continue;
4527
+ const options = mergeTestClaimDatabaseOptions(config.database.base, undefined, config.database.clients[plan.clientName]);
4528
+ if (await readers.databaseExists(plan, options)) {
4529
+ throw new Error('Preexisting strict test database namespace');
4530
+ }
4531
+ }
4532
+ for (const resource of inventory.redis) {
4533
+ const options = mergeTestClaimRedisOptions(config.redis.base, config.redis.clients[resource.clientName]);
4534
+ if (await readers.redisExists(resource, options)) {
4535
+ throw new Error('Preexisting strict test Redis namespace');
4536
+ }
4537
+ }
4538
+ } catch {
4539
+ // Driver messages may embed credentials, database names or Redis keys.
4540
+ throw new Error('Strict test resource freshness check failed');
4541
+ }
4542
+ freshApps.add(app);
4543
+ }
4544
+ async function hasFreshnessDatabase(connection, plan) {
4545
+ try {
4546
+ const pg = plan.endpoint.dialect === 'pg';
4547
+ if (!pg && !['mysql', 'mysql2'].includes(plan.endpoint.dialect)) {
4548
+ throw new Error('Unsupported freshness database dialect');
4549
+ }
4550
+ const result = await connection.raw(pg ? 'select datname from pg_database where left(datname, ?) = ? limit 1' : 'select schema_name from information_schema.schemata where left(schema_name, ?) = ? limit 1', [plan.prefix.length, plan.prefix]);
4551
+ const rows = pg ? result?.rows : result?.[0];
4552
+ if (!Array.isArray(rows)) throw new Error('Invalid freshness database response');
4553
+ return rows.length !== 0;
4554
+ } finally {
4555
+ await connection.destroy();
4556
+ }
4557
+ }
4558
+ async function hasFreshnessRedis(connection, prefix, maxScans = 10000) {
4559
+ if (!prefix || !Number.isSafeInteger(maxScans) || maxScans < 1) {
4560
+ throw new Error('Invalid Redis freshness bounds');
4561
+ }
4562
+ const pattern = `${prefix.replace(/[\\*?[\]]/g, '\\$&')}*`;
4563
+ let cursor = '0';
4564
+ for (let scans = 0; scans < maxScans; scans++) {
4565
+ const result = await connection.scan(cursor, 'MATCH', pattern, 'COUNT', 128);
4566
+ if (!Array.isArray(result) || result.length !== 2 || typeof result[0] !== 'string' || !/^\d+$/.test(result[0]) || !Array.isArray(result[1]) || result[1].some(key => typeof key !== 'string' || !key.startsWith(prefix))) {
4567
+ throw new Error('Invalid Redis freshness response');
4568
+ }
4569
+ if (result[1].length !== 0) return true;
4570
+ cursor = result[0];
4571
+ if (cursor === '0') return false;
4572
+ }
4573
+ throw new Error('Redis freshness scan limit exceeded');
4574
+ }
4575
+ function createFreshnessDependencies(projectPath, load = createRequire(path.join(projectPath, 'package.json'))) {
4576
+ return {
4577
+ async databaseExists(plan, options) {
4578
+ const endpoint = plan.endpoint;
4579
+ const source = options.connection;
4580
+ const target = {
4581
+ user: source.user,
4582
+ password: source.password,
4583
+ ssl: source.ssl,
4584
+ database: endpoint.adminDatabase,
4585
+ connectionTimeoutMillis: 10000,
4586
+ connectTimeout: 10000
4587
+ };
4588
+ if (endpoint.transport === 'tcp') {
4589
+ target.host = endpoint.host;
4590
+ target.port = endpoint.port;
4591
+ } else if (endpoint.dialect === 'pg') {
4592
+ target.host = endpoint.socketPath;
4593
+ target.port = endpoint.port;
4594
+ } else {
4595
+ target.socketPath = endpoint.socketPath;
4596
+ }
4597
+ const knex = load('knex');
4598
+ return await hasFreshnessDatabase(knex({
4599
+ client: endpoint.dialect,
4600
+ connection: target,
4601
+ pool: {
4602
+ min: 0,
4603
+ max: 1
4604
+ },
4605
+ acquireConnectionTimeout: 10000
4606
+ }), plan);
4607
+ },
4608
+ async redisExists(resource, options) {
4609
+ const endpoint = resource.endpoint;
4610
+ const target = endpoint.transport === 'socket' ? {
4611
+ path: endpoint.socketPath
4612
+ } : {
4613
+ host: endpoint.host,
4614
+ port: endpoint.port
4615
+ };
4616
+ const Redis = load('ioredis').Redis;
4617
+ const connection = new Redis({
4618
+ ...target,
4619
+ username: options.username,
4620
+ password: options.password,
4621
+ tls: options.tls,
4622
+ db: endpoint.db,
4623
+ keyPrefix: undefined,
4624
+ lazyConnect: true,
4625
+ connectTimeout: 10000,
4626
+ commandTimeout: 10000,
4627
+ retryStrategy: () => null,
4628
+ maxRetriesPerRequest: 0,
4629
+ enableOfflineQueue: false
4630
+ });
4631
+ connection.on('error', () => {});
4632
+ try {
4633
+ await connection.connect();
4634
+ const prefix = resource.kind === 'queue' ? resource.prefix.slice(0, -'queue:'.length) : resource.prefix;
4635
+ return await hasFreshnessRedis(connection, prefix);
4636
+ } finally {
4637
+ connection.disconnect();
4638
+ }
4639
+ }
4640
+ };
4641
+ }
4642
+
3890
4643
  function jsxEnhance() {
3891
4644
  const createElementOriginal = React.createElement;
3892
4645
  React.createElement = function (...args) {
@@ -4106,6 +4859,7 @@ class Start {
4106
4859
  const app = this.app;
4107
4860
  try {
4108
4861
  await this._start_appConfig();
4862
+ await assertFreshTestResources(app);
4109
4863
  await this._start_appLoad();
4110
4864
  await this._start_appStart();
4111
4865
  await this._start_appReady();
@@ -4325,13 +5079,16 @@ async function _closeInner() {
4325
5079
  clearTimeout(timeout);
4326
5080
  process.exit(err ? 1 : 0);
4327
5081
  }
5082
+
5083
+ // The authenticated test health endpoint uses the same guard and cleanup as signals.
5084
+ async function closeProcessWorker() {
5085
+ if (__closing) return;
5086
+ __closing = true;
5087
+ await _closeInner();
5088
+ }
4328
5089
  function handleProcessWork() {
4329
5090
  ['SIGINT', 'SIGTERM', 'SIGUSR2'].forEach(signal => {
4330
- process.on(signal, async () => {
4331
- if (__closing) return;
4332
- __closing = true;
4333
- await _closeInner();
4334
- });
5091
+ process.on(signal, closeProcessWorker);
4335
5092
  });
4336
5093
  process.on('uncaughtException', async err => {
4337
5094
  const app = useApp();
@@ -4492,5 +5249,5 @@ async function retry(options, fn) {
4492
5249
 
4493
5250
  zodExtendOpenApi();
4494
5251
 
4495
- export { $Class, $localeScope, $makeLocaleMagic, $protocolKey, AppHmr, AppHmrDeps, AppLocale, AppLogger, AppMeta, AppMetadata, AppResource, AppUtil, BeanAopBase, BeanAopMethodBase, BeanBase, BeanBaseSimple, BeanContainer, BeanInfo, BeanScopeBase, BeanScopeContainer, BeanScopeError, BeanScopeErrorImpl, BeanScopeLocale, BeanScopeScene, BeanScopeUtil, BeanSimple, EnumAppEvent, ErrorClass, Global, LocaleModuleNameSeparator, PickClassInner, ProxyDisable, SymbolBeanContainerInstances, SymbolBeanFullName, SymbolBeanInstanceKey, SymbolBeanInstancePropsLazy, SymbolCacheAopChains, SymbolCacheAopChainsKey, SymbolDecoratorBeanFullName, SymbolDecoratorBeanInfo, SymbolDecoratorGlobal, SymbolDecoratorProxyDisable, SymbolDecoratorUse, SymbolDecoratorVirtual, SymbolHmrStateLoad, SymbolHmrStateSave, SymbolMappedClassMetadataKeys, SymbolModuleBelong, SymbolModuleName, TEST_CLAIM_DIRECTORY, TEST_CLAIM_TOKEN_ENV, TEST_CLAIM_URL_ENV, TEST_RUN_ID_ENV, TEST_RUN_OWNER_FILE, Use, Virtual, VonaApplication, __prepareInjectSelectorInfo, appHmrDeps, appMetadata, appResource, assertDisposableDatabaseName, assertIsolatedTestConfig, assertIsolatedTestDatabaseClient, assertIsolatedTestPath, assertTestAppIdentity, assertTestClaimDatabaseClient, assertTestClaimRedisClient, assertTestClaimSafePath, assertTestRunOwner, beanFullNameFromOnionName, bootstrap, buildTestClaimResourceInventory, cast, closeApp, combineConfigDefault, combineFilePathSafe, compose, copyAddon, copyMetadataOfClasses, copyProperties, copyPropertiesOfClasses, createApp, createAppMaster, createBeanDecorator, createGeneralApp, createHash, createTestClaim, deepExtend, disposeInstance, errorsInternal, filterHeaders, formatLoggerAxiosError, formatLoggerConsole, formatLoggerCtx, formatLoggerDummy, formatLoggerErrors, formatLoggerFilter, functionNoop, getDatabaseTestPrefix, getHomeVonaAppDir, getLoggerPathPhysicalRoot, getMappedClassMetadataKeys, getPlatformArch, getPublicPathPhysicalRoot, getRuntimePathPhysicalRoot, getSqlite3DatabaseNameDefault, getTestAppName, getTestClaimCreatedAt, getTestClaimPaths, getTestClaimRedisPrefix, getTestRunIdFromEnv, hasTestClaimTransport, instanceDesp, isLocaleMagic, loadJSONFile, loadTestClaim, mergeTestClaimDatabaseOptions, mergeTestClaimRedisOptions, normalizeTestClaimDatabaseEndpoint, normalizeTestClaimRedisEndpoint, onionNameFromBeanFullName, pathToHref, polyfillDispose, prepareEnv, registerMappedClassMetadataKey, registerTestClaimDatabaseCreation, registerTestClaimResourceInventory, requireAddon, requireDynamic, resolveAddon, resolveTestRunId, resolveVonaLocalPaths, resolverSharp, resolverSharpLibvips, resolverSqlite3, retry, saveJSONFile, sendTestClaimEvent, setMappedClassMetadataKeys, startTestClaimBroker, text$1 as text, useApp, usePrepareArg, usePrepareArgs, uuidv4, validateTestClaim, withIsolatedTestDatabaseDiscovery, writeTestClaim };
5252
+ export { $Class, $localeScope, $makeLocaleMagic, $protocolKey, AppHmr, AppHmrDeps, AppLocale, AppLogger, AppMeta, AppMetadata, AppResource, AppUtil, BeanAopBase, BeanAopMethodBase, BeanBase, BeanBaseSimple, BeanContainer, BeanInfo, BeanScopeBase, BeanScopeContainer, BeanScopeError, BeanScopeErrorImpl, BeanScopeLocale, BeanScopeScene, BeanScopeUtil, BeanSimple, EnumAppEvent, ErrorClass, Global, LocaleModuleNameSeparator, PickClassInner, ProxyDisable, SymbolBeanContainerInstances, SymbolBeanFullName, SymbolBeanInstanceKey, SymbolBeanInstancePropsLazy, SymbolCacheAopChains, SymbolCacheAopChainsKey, SymbolDecoratorBeanFullName, SymbolDecoratorBeanInfo, SymbolDecoratorGlobal, SymbolDecoratorProxyDisable, SymbolDecoratorUse, SymbolDecoratorVirtual, SymbolHmrStateLoad, SymbolHmrStateSave, SymbolMappedClassMetadataKeys, SymbolModuleBelong, SymbolModuleName, TEST_CLAIM_DIRECTORY, TEST_CLAIM_TOKEN_ENV, TEST_CLAIM_URL_ENV, TEST_CLAIM_WINDOWS_GUARDIAN_ENV, TEST_REQUIRE_FRESH_RESOURCES_ENV, TEST_RUN_ID_ENV, TEST_RUN_OWNER_FILE, Use, Virtual, VonaApplication, __prepareInjectSelectorInfo, appHmrDeps, appMetadata, appResource, assertDisposableDatabaseName, assertFreshTestResources, assertFreshTestResourcesReady, assertIsolatedTestConfig, assertIsolatedTestDatabaseClient, assertIsolatedTestPath, assertTestAppIdentity, assertTestClaimDatabaseClient, assertTestClaimRedisClient, assertTestClaimSafePath, assertTestClaimWindowsJob, assertTestRunOwner, beanFullNameFromOnionName, bootstrap, buildTestClaimResourceInventory, captureRequestBoundary, cast, closeApp, closeProcessWorker, combineConfigDefault, combineFilePathSafe, compose, copyAddon, copyMetadataOfClasses, copyProperties, copyPropertiesOfClasses, createApp, createAppMaster, createBeanDecorator, createDiagnosticLogger, createFreshnessDependencies, createGeneralApp, createHash, createRequestBoundary, createTestClaim, deepExtend, disposeInstance, errorsInternal, filterHeaders, formatLoggerAxiosError, formatLoggerBoundary, formatLoggerConsole, formatLoggerCtx, formatLoggerDummy, formatLoggerErrors, formatLoggerFilter, functionNoop, getDatabaseTestPrefix, getHomeVonaAppDir, getLoggerPathPhysicalRoot, getMappedClassMetadataKeys, getPlatformArch, getPublicPathPhysicalRoot, getRequestBoundary, getRuntimePathPhysicalRoot, getSqlite3DatabaseNameDefault, getSuppressedDiagnosticLogger, getTestAppName, getTestClaimCreatedAt, getTestClaimPaths, getTestClaimRedisPrefix, getTestRunIdFromEnv, hasFreshnessDatabase, hasFreshnessRedis, hasTestClaimTransport, instanceDesp, isLocaleMagic, loadJSONFile, loadTestClaim, mergeTestClaimDatabaseOptions, mergeTestClaimRedisOptions, normalizeTestClaimDatabaseEndpoint, normalizeTestClaimRedisEndpoint, onionNameFromBeanFullName, pathToHref, polyfillDispose, prepareEnv, registerMappedClassMetadataKey, registerTestClaimDatabaseCreation, registerTestClaimResourceInventory, registerTestClaimWindowsJobVerifier, requireAddon, requireDynamic, requiresFreshTestResources, resolveAddon, resolveTestRunId, resolveVonaLocalPaths, resolverSharp, resolverSharpLibvips, resolverSqlite3, retry, runWithRequestBoundary, saveJSONFile, sendTestClaimEvent, setMappedClassMetadataKeys, startTestClaimBroker, text$1 as text, useApp, usePrepareArg, usePrepareArgs, uuidv4, validateTestClaim, withIsolatedTestDatabaseDiscovery, writeTestClaim };
4496
5253
  //# sourceMappingURL=index.js.map