@git.zone/tstest 2.3.8 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/tstest.classes.migration.d.ts +104 -0
- package/dist_ts/tstest.classes.migration.js +200 -0
- package/dist_ts/tstest.classes.runtime.adapter.d.ts +172 -0
- package/dist_ts/tstest.classes.runtime.adapter.js +85 -0
- package/dist_ts/tstest.classes.runtime.bun.d.ts +30 -0
- package/dist_ts/tstest.classes.runtime.bun.js +184 -0
- package/dist_ts/tstest.classes.runtime.chromium.d.ts +36 -0
- package/dist_ts/tstest.classes.runtime.chromium.js +256 -0
- package/dist_ts/tstest.classes.runtime.deno.d.ts +34 -0
- package/dist_ts/tstest.classes.runtime.deno.js +205 -0
- package/dist_ts/tstest.classes.runtime.node.d.ts +30 -0
- package/dist_ts/tstest.classes.runtime.node.js +188 -0
- package/dist_ts/tstest.classes.runtime.parser.d.ts +37 -0
- package/dist_ts/tstest.classes.runtime.parser.js +159 -0
- package/dist_ts/tstest.classes.tstest.d.ts +2 -0
- package/dist_ts/tstest.classes.tstest.js +53 -22
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/tstest.classes.migration.ts +316 -0
- package/ts/tstest.classes.runtime.adapter.ts +245 -0
- package/ts/tstest.classes.runtime.bun.ts +219 -0
- package/ts/tstest.classes.runtime.chromium.ts +293 -0
- package/ts/tstest.classes.runtime.deno.ts +244 -0
- package/ts/tstest.classes.runtime.node.ts +222 -0
- package/ts/tstest.classes.runtime.parser.ts +211 -0
- package/ts/tstest.classes.tstest.ts +67 -22
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import * as plugins from './tstest.plugins.js';
|
|
2
|
+
import { coloredString as cs } from '@push.rocks/consolecolor';
|
|
3
|
+
import { RuntimeAdapter, } from './tstest.classes.runtime.adapter.js';
|
|
4
|
+
import { TapParser } from './tstest.classes.tap.parser.js';
|
|
5
|
+
import { TsTestLogger } from './tstest.logging.js';
|
|
6
|
+
/**
|
|
7
|
+
* Deno runtime adapter
|
|
8
|
+
* Executes tests using the Deno runtime
|
|
9
|
+
*/
|
|
10
|
+
export class DenoRuntimeAdapter extends RuntimeAdapter {
|
|
11
|
+
constructor(logger, smartshellInstance, // SmartShell instance from @push.rocks/smartshell
|
|
12
|
+
timeoutSeconds, filterTags) {
|
|
13
|
+
super();
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.smartshellInstance = smartshellInstance;
|
|
16
|
+
this.timeoutSeconds = timeoutSeconds;
|
|
17
|
+
this.filterTags = filterTags;
|
|
18
|
+
this.id = 'deno';
|
|
19
|
+
this.displayName = 'Deno';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get default Deno options
|
|
23
|
+
*/
|
|
24
|
+
getDefaultOptions() {
|
|
25
|
+
return {
|
|
26
|
+
...super.getDefaultOptions(),
|
|
27
|
+
permissions: ['--allow-read', '--allow-env'],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check if Deno is available
|
|
32
|
+
*/
|
|
33
|
+
async checkAvailable() {
|
|
34
|
+
try {
|
|
35
|
+
const result = await this.smartshellInstance.exec('deno --version', {
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
onError: () => {
|
|
38
|
+
// Ignore error
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (result.exitCode !== 0) {
|
|
42
|
+
return {
|
|
43
|
+
available: false,
|
|
44
|
+
error: 'Deno not found. Install from: https://deno.land/',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Parse Deno version from output (first line is "deno X.Y.Z")
|
|
48
|
+
const versionMatch = result.stdout.match(/deno (\d+\.\d+\.\d+)/);
|
|
49
|
+
const version = versionMatch ? versionMatch[1] : 'unknown';
|
|
50
|
+
return {
|
|
51
|
+
available: true,
|
|
52
|
+
version: `Deno ${version}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return {
|
|
57
|
+
available: false,
|
|
58
|
+
error: error.message,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create command configuration for Deno test execution
|
|
64
|
+
*/
|
|
65
|
+
createCommand(testFile, options) {
|
|
66
|
+
const mergedOptions = this.mergeOptions(options);
|
|
67
|
+
const args = ['run'];
|
|
68
|
+
// Add permissions
|
|
69
|
+
const permissions = mergedOptions.permissions || ['--allow-read', '--allow-env'];
|
|
70
|
+
args.push(...permissions);
|
|
71
|
+
// Add config file if specified
|
|
72
|
+
if (mergedOptions.configPath) {
|
|
73
|
+
args.push('--config', mergedOptions.configPath);
|
|
74
|
+
}
|
|
75
|
+
// Add import map if specified
|
|
76
|
+
if (mergedOptions.importMap) {
|
|
77
|
+
args.push('--import-map', mergedOptions.importMap);
|
|
78
|
+
}
|
|
79
|
+
// Add extra args
|
|
80
|
+
if (mergedOptions.extraArgs && mergedOptions.extraArgs.length > 0) {
|
|
81
|
+
args.push(...mergedOptions.extraArgs);
|
|
82
|
+
}
|
|
83
|
+
// Add test file
|
|
84
|
+
args.push(testFile);
|
|
85
|
+
// Set environment variables
|
|
86
|
+
const env = { ...mergedOptions.env };
|
|
87
|
+
if (this.filterTags.length > 0) {
|
|
88
|
+
env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
command: 'deno',
|
|
92
|
+
args,
|
|
93
|
+
env,
|
|
94
|
+
cwd: mergedOptions.cwd,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Execute a test file in Deno
|
|
99
|
+
*/
|
|
100
|
+
async run(testFile, index, total, options) {
|
|
101
|
+
this.logger.testFileStart(testFile, this.displayName, index, total);
|
|
102
|
+
const tapParser = new TapParser(testFile + ':deno', this.logger);
|
|
103
|
+
const mergedOptions = this.mergeOptions(options);
|
|
104
|
+
// Build Deno command
|
|
105
|
+
const command = this.createCommand(testFile, mergedOptions);
|
|
106
|
+
const fullCommand = `${command.command} ${command.args.join(' ')}`;
|
|
107
|
+
// Set filter tags as environment variable
|
|
108
|
+
if (this.filterTags.length > 0) {
|
|
109
|
+
process.env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
|
110
|
+
}
|
|
111
|
+
// Check for 00init.ts file in test directory
|
|
112
|
+
const testDir = plugins.path.dirname(testFile);
|
|
113
|
+
const initFile = plugins.path.join(testDir, '00init.ts');
|
|
114
|
+
const initFileExists = await plugins.smartfile.fs.fileExists(initFile);
|
|
115
|
+
let runCommand = fullCommand;
|
|
116
|
+
let loaderPath = null;
|
|
117
|
+
// If 00init.ts exists, create a loader file
|
|
118
|
+
if (initFileExists) {
|
|
119
|
+
const absoluteInitFile = plugins.path.resolve(initFile);
|
|
120
|
+
const absoluteTestFile = plugins.path.resolve(testFile);
|
|
121
|
+
const loaderContent = `
|
|
122
|
+
import '${absoluteInitFile.replace(/\\/g, '/')}';
|
|
123
|
+
import '${absoluteTestFile.replace(/\\/g, '/')}';
|
|
124
|
+
`;
|
|
125
|
+
loaderPath = plugins.path.join(testDir, `.loader_${plugins.path.basename(testFile)}`);
|
|
126
|
+
await plugins.smartfile.memory.toFs(loaderContent, loaderPath);
|
|
127
|
+
// Rebuild command with loader file
|
|
128
|
+
const loaderCommand = this.createCommand(loaderPath, mergedOptions);
|
|
129
|
+
runCommand = `${loaderCommand.command} ${loaderCommand.args.join(' ')}`;
|
|
130
|
+
}
|
|
131
|
+
const execResultStreaming = await this.smartshellInstance.execStreamingSilent(runCommand);
|
|
132
|
+
// If we created a loader file, clean it up after test execution
|
|
133
|
+
if (loaderPath) {
|
|
134
|
+
const cleanup = () => {
|
|
135
|
+
try {
|
|
136
|
+
if (plugins.smartfile.fs.fileExistsSync(loaderPath)) {
|
|
137
|
+
plugins.smartfile.fs.removeSync(loaderPath);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
// Ignore cleanup errors
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
execResultStreaming.childProcess.on('exit', cleanup);
|
|
145
|
+
execResultStreaming.childProcess.on('error', cleanup);
|
|
146
|
+
}
|
|
147
|
+
// Start warning timer if no timeout was specified
|
|
148
|
+
let warningTimer = null;
|
|
149
|
+
if (this.timeoutSeconds === null) {
|
|
150
|
+
warningTimer = setTimeout(() => {
|
|
151
|
+
console.error('');
|
|
152
|
+
console.error(cs('⚠️ WARNING: Test file is running for more than 1 minute', 'orange'));
|
|
153
|
+
console.error(cs(` File: ${testFile}`, 'orange'));
|
|
154
|
+
console.error(cs(' Consider using --timeout option to set a timeout for test files.', 'orange'));
|
|
155
|
+
console.error(cs(' Example: tstest test --timeout=300 (for 5 minutes)', 'orange'));
|
|
156
|
+
console.error('');
|
|
157
|
+
}, 60000); // 1 minute
|
|
158
|
+
}
|
|
159
|
+
// Handle timeout if specified
|
|
160
|
+
if (this.timeoutSeconds !== null) {
|
|
161
|
+
const timeoutMs = this.timeoutSeconds * 1000;
|
|
162
|
+
let timeoutId;
|
|
163
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
164
|
+
timeoutId = setTimeout(async () => {
|
|
165
|
+
// Use smartshell's terminate() to kill entire process tree
|
|
166
|
+
await execResultStreaming.terminate();
|
|
167
|
+
reject(new Error(`Test file timed out after ${this.timeoutSeconds} seconds`));
|
|
168
|
+
}, timeoutMs);
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
await Promise.race([
|
|
172
|
+
tapParser.handleTapProcess(execResultStreaming.childProcess),
|
|
173
|
+
timeoutPromise
|
|
174
|
+
]);
|
|
175
|
+
// Clear timeout if test completed successfully
|
|
176
|
+
clearTimeout(timeoutId);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// Clear warning timer if it was set
|
|
180
|
+
if (warningTimer) {
|
|
181
|
+
clearTimeout(warningTimer);
|
|
182
|
+
}
|
|
183
|
+
// Handle timeout error
|
|
184
|
+
tapParser.handleTimeout(this.timeoutSeconds);
|
|
185
|
+
// Ensure entire process tree is killed if still running
|
|
186
|
+
try {
|
|
187
|
+
await execResultStreaming.kill(); // This kills the entire process tree with SIGKILL
|
|
188
|
+
}
|
|
189
|
+
catch (killError) {
|
|
190
|
+
// Process tree might already be dead
|
|
191
|
+
}
|
|
192
|
+
await tapParser.evaluateFinalResult();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
await tapParser.handleTapProcess(execResultStreaming.childProcess);
|
|
197
|
+
}
|
|
198
|
+
// Clear warning timer if it was set
|
|
199
|
+
if (warningTimer) {
|
|
200
|
+
clearTimeout(warningTimer);
|
|
201
|
+
}
|
|
202
|
+
return tapParser;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN0ZXN0LmNsYXNzZXMucnVudGltZS5kZW5vLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvdHN0ZXN0LmNsYXNzZXMucnVudGltZS5kZW5vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGFBQWEsSUFBSSxFQUFFLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRCxPQUFPLEVBQ0wsY0FBYyxHQUlmLE1BQU0scUNBQXFDLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUduRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsY0FBYztJQUlwRCxZQUNVLE1BQW9CLEVBQ3BCLGtCQUF1QixFQUFFLGtEQUFrRDtJQUMzRSxjQUE2QixFQUM3QixVQUFvQjtRQUU1QixLQUFLLEVBQUUsQ0FBQztRQUxBLFdBQU0sR0FBTixNQUFNLENBQWM7UUFDcEIsdUJBQWtCLEdBQWxCLGtCQUFrQixDQUFLO1FBQ3ZCLG1CQUFjLEdBQWQsY0FBYyxDQUFlO1FBQzdCLGVBQVUsR0FBVixVQUFVLENBQVU7UUFQckIsT0FBRSxHQUFZLE1BQU0sQ0FBQztRQUNyQixnQkFBVyxHQUFXLE1BQU0sQ0FBQztJQVN0QyxDQUFDO0lBRUQ7O09BRUc7SUFDTyxpQkFBaUI7UUFDekIsT0FBTztZQUNMLEdBQUcsS0FBSyxDQUFDLGlCQUFpQixFQUFFO1lBQzVCLFdBQVcsRUFBRSxDQUFDLGNBQWMsRUFBRSxhQUFhLENBQUM7U0FDN0MsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxjQUFjO1FBQ2xCLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxn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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RuntimeAdapter, type RuntimeOptions, type RuntimeCommand, type RuntimeAvailability } from './tstest.classes.runtime.adapter.js';
|
|
2
|
+
import { TapParser } from './tstest.classes.tap.parser.js';
|
|
3
|
+
import { TsTestLogger } from './tstest.logging.js';
|
|
4
|
+
import type { Runtime } from './tstest.classes.runtime.parser.js';
|
|
5
|
+
/**
|
|
6
|
+
* Node.js runtime adapter
|
|
7
|
+
* Executes tests using tsrun (TypeScript runner for Node.js)
|
|
8
|
+
*/
|
|
9
|
+
export declare class NodeRuntimeAdapter extends RuntimeAdapter {
|
|
10
|
+
private logger;
|
|
11
|
+
private smartshellInstance;
|
|
12
|
+
private timeoutSeconds;
|
|
13
|
+
private filterTags;
|
|
14
|
+
readonly id: Runtime;
|
|
15
|
+
readonly displayName: string;
|
|
16
|
+
constructor(logger: TsTestLogger, smartshellInstance: any, // SmartShell instance from @push.rocks/smartshell
|
|
17
|
+
timeoutSeconds: number | null, filterTags: string[]);
|
|
18
|
+
/**
|
|
19
|
+
* Check if Node.js and tsrun are available
|
|
20
|
+
*/
|
|
21
|
+
checkAvailable(): Promise<RuntimeAvailability>;
|
|
22
|
+
/**
|
|
23
|
+
* Create command configuration for Node.js test execution
|
|
24
|
+
*/
|
|
25
|
+
createCommand(testFile: string, options?: RuntimeOptions): RuntimeCommand;
|
|
26
|
+
/**
|
|
27
|
+
* Execute a test file in Node.js
|
|
28
|
+
*/
|
|
29
|
+
run(testFile: string, index: number, total: number, options?: RuntimeOptions): Promise<TapParser>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as plugins from './tstest.plugins.js';
|
|
2
|
+
import { coloredString as cs } from '@push.rocks/consolecolor';
|
|
3
|
+
import { RuntimeAdapter, } from './tstest.classes.runtime.adapter.js';
|
|
4
|
+
import { TapParser } from './tstest.classes.tap.parser.js';
|
|
5
|
+
import { TsTestLogger } from './tstest.logging.js';
|
|
6
|
+
/**
|
|
7
|
+
* Node.js runtime adapter
|
|
8
|
+
* Executes tests using tsrun (TypeScript runner for Node.js)
|
|
9
|
+
*/
|
|
10
|
+
export class NodeRuntimeAdapter extends RuntimeAdapter {
|
|
11
|
+
constructor(logger, smartshellInstance, // SmartShell instance from @push.rocks/smartshell
|
|
12
|
+
timeoutSeconds, filterTags) {
|
|
13
|
+
super();
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.smartshellInstance = smartshellInstance;
|
|
16
|
+
this.timeoutSeconds = timeoutSeconds;
|
|
17
|
+
this.filterTags = filterTags;
|
|
18
|
+
this.id = 'node';
|
|
19
|
+
this.displayName = 'Node.js';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if Node.js and tsrun are available
|
|
23
|
+
*/
|
|
24
|
+
async checkAvailable() {
|
|
25
|
+
try {
|
|
26
|
+
// Check Node.js version
|
|
27
|
+
const nodeVersion = process.version;
|
|
28
|
+
// Check if tsrun is available
|
|
29
|
+
const result = await this.smartshellInstance.exec('tsrun --version', {
|
|
30
|
+
cwd: process.cwd(),
|
|
31
|
+
onError: () => {
|
|
32
|
+
// Ignore error
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (result.exitCode !== 0) {
|
|
36
|
+
return {
|
|
37
|
+
available: false,
|
|
38
|
+
error: 'tsrun not found. Install with: pnpm install --save-dev @git.zone/tsrun',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
available: true,
|
|
43
|
+
version: nodeVersion,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
available: false,
|
|
49
|
+
error: error.message,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create command configuration for Node.js test execution
|
|
55
|
+
*/
|
|
56
|
+
createCommand(testFile, options) {
|
|
57
|
+
const mergedOptions = this.mergeOptions(options);
|
|
58
|
+
// Build tsrun options
|
|
59
|
+
const args = [];
|
|
60
|
+
if (process.argv.includes('--web')) {
|
|
61
|
+
args.push('--web');
|
|
62
|
+
}
|
|
63
|
+
// Add any extra args
|
|
64
|
+
if (mergedOptions.extraArgs) {
|
|
65
|
+
args.push(...mergedOptions.extraArgs);
|
|
66
|
+
}
|
|
67
|
+
// Set environment variables
|
|
68
|
+
const env = { ...mergedOptions.env };
|
|
69
|
+
if (this.filterTags.length > 0) {
|
|
70
|
+
env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
command: 'tsrun',
|
|
74
|
+
args: [testFile, ...args],
|
|
75
|
+
env,
|
|
76
|
+
cwd: mergedOptions.cwd,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Execute a test file in Node.js
|
|
81
|
+
*/
|
|
82
|
+
async run(testFile, index, total, options) {
|
|
83
|
+
this.logger.testFileStart(testFile, this.displayName, index, total);
|
|
84
|
+
const tapParser = new TapParser(testFile + ':node', this.logger);
|
|
85
|
+
const mergedOptions = this.mergeOptions(options);
|
|
86
|
+
// Build tsrun command
|
|
87
|
+
let tsrunOptions = '';
|
|
88
|
+
if (process.argv.includes('--web')) {
|
|
89
|
+
tsrunOptions += ' --web';
|
|
90
|
+
}
|
|
91
|
+
// Set filter tags as environment variable
|
|
92
|
+
if (this.filterTags.length > 0) {
|
|
93
|
+
process.env.TSTEST_FILTER_TAGS = this.filterTags.join(',');
|
|
94
|
+
}
|
|
95
|
+
// Check for 00init.ts file in test directory
|
|
96
|
+
const testDir = plugins.path.dirname(testFile);
|
|
97
|
+
const initFile = plugins.path.join(testDir, '00init.ts');
|
|
98
|
+
let runCommand = `tsrun ${testFile}${tsrunOptions}`;
|
|
99
|
+
const initFileExists = await plugins.smartfile.fs.fileExists(initFile);
|
|
100
|
+
// If 00init.ts exists, run it first
|
|
101
|
+
let loaderPath = null;
|
|
102
|
+
if (initFileExists) {
|
|
103
|
+
// Create a temporary loader file that imports both 00init.ts and the test file
|
|
104
|
+
const absoluteInitFile = plugins.path.resolve(initFile);
|
|
105
|
+
const absoluteTestFile = plugins.path.resolve(testFile);
|
|
106
|
+
const loaderContent = `
|
|
107
|
+
import '${absoluteInitFile.replace(/\\/g, '/')}';
|
|
108
|
+
import '${absoluteTestFile.replace(/\\/g, '/')}';
|
|
109
|
+
`;
|
|
110
|
+
loaderPath = plugins.path.join(testDir, `.loader_${plugins.path.basename(testFile)}`);
|
|
111
|
+
await plugins.smartfile.memory.toFs(loaderContent, loaderPath);
|
|
112
|
+
runCommand = `tsrun ${loaderPath}${tsrunOptions}`;
|
|
113
|
+
}
|
|
114
|
+
const execResultStreaming = await this.smartshellInstance.execStreamingSilent(runCommand);
|
|
115
|
+
// If we created a loader file, clean it up after test execution
|
|
116
|
+
if (loaderPath) {
|
|
117
|
+
const cleanup = () => {
|
|
118
|
+
try {
|
|
119
|
+
if (plugins.smartfile.fs.fileExistsSync(loaderPath)) {
|
|
120
|
+
plugins.smartfile.fs.removeSync(loaderPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
// Ignore cleanup errors
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
execResultStreaming.childProcess.on('exit', cleanup);
|
|
128
|
+
execResultStreaming.childProcess.on('error', cleanup);
|
|
129
|
+
}
|
|
130
|
+
// Start warning timer if no timeout was specified
|
|
131
|
+
let warningTimer = null;
|
|
132
|
+
if (this.timeoutSeconds === null) {
|
|
133
|
+
warningTimer = setTimeout(() => {
|
|
134
|
+
console.error('');
|
|
135
|
+
console.error(cs('⚠️ WARNING: Test file is running for more than 1 minute', 'orange'));
|
|
136
|
+
console.error(cs(` File: ${testFile}`, 'orange'));
|
|
137
|
+
console.error(cs(' Consider using --timeout option to set a timeout for test files.', 'orange'));
|
|
138
|
+
console.error(cs(' Example: tstest test --timeout=300 (for 5 minutes)', 'orange'));
|
|
139
|
+
console.error('');
|
|
140
|
+
}, 60000); // 1 minute
|
|
141
|
+
}
|
|
142
|
+
// Handle timeout if specified
|
|
143
|
+
if (this.timeoutSeconds !== null) {
|
|
144
|
+
const timeoutMs = this.timeoutSeconds * 1000;
|
|
145
|
+
let timeoutId;
|
|
146
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
147
|
+
timeoutId = setTimeout(async () => {
|
|
148
|
+
// Use smartshell's terminate() to kill entire process tree
|
|
149
|
+
await execResultStreaming.terminate();
|
|
150
|
+
reject(new Error(`Test file timed out after ${this.timeoutSeconds} seconds`));
|
|
151
|
+
}, timeoutMs);
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
await Promise.race([
|
|
155
|
+
tapParser.handleTapProcess(execResultStreaming.childProcess),
|
|
156
|
+
timeoutPromise
|
|
157
|
+
]);
|
|
158
|
+
// Clear timeout if test completed successfully
|
|
159
|
+
clearTimeout(timeoutId);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
// Clear warning timer if it was set
|
|
163
|
+
if (warningTimer) {
|
|
164
|
+
clearTimeout(warningTimer);
|
|
165
|
+
}
|
|
166
|
+
// Handle timeout error
|
|
167
|
+
tapParser.handleTimeout(this.timeoutSeconds);
|
|
168
|
+
// Ensure entire process tree is killed if still running
|
|
169
|
+
try {
|
|
170
|
+
await execResultStreaming.kill(); // This kills the entire process tree with SIGKILL
|
|
171
|
+
}
|
|
172
|
+
catch (killError) {
|
|
173
|
+
// Process tree might already be dead
|
|
174
|
+
}
|
|
175
|
+
await tapParser.evaluateFinalResult();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
await tapParser.handleTapProcess(execResultStreaming.childProcess);
|
|
180
|
+
}
|
|
181
|
+
// Clear warning timer if it was set
|
|
182
|
+
if (warningTimer) {
|
|
183
|
+
clearTimeout(warningTimer);
|
|
184
|
+
}
|
|
185
|
+
return tapParser;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN0ZXN0LmNsYXNzZXMucnVudGltZS5ub2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvdHN0ZXN0LmNsYXNzZXMucnVudGltZS5ub2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGFBQWEsSUFBSSxFQUFFLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRCxPQUFPLEVBQ0wsY0FBYyxHQUlmLE1BQU0scUNBQXFDLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUduRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsY0FBYztJQUlwRCxZQUNVLE1BQW9CLEVBQ3BCLGtCQUF1QixFQUFFLGtEQUFrRDtJQUMzRSxjQUE2QixFQUM3QixVQUFvQjtRQUU1QixLQUFLLEVBQUUsQ0FBQztRQUxBLFdBQU0sR0FBTixNQUFNLENBQWM7UUFDcEIsdUJBQWtCLEdBQWxCLGtCQUFrQixDQUFLO1FBQ3ZCLG1CQUFjLEdBQWQsY0FBYyxDQUFlO1FBQzdCLGVBQVUsR0FBVixVQUFVLENBQVU7UUFQckIsT0FBRSxHQUFZLE1BQU0sQ0FBQztRQUNyQixnQkFBVyxHQUFXLFNBQVMsQ0FBQztJQVN6QyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsY0FBYztRQUNsQixJQUFJLENBQUM7WUFDSCx3QkFBd0I7WUFDeEIsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQztZQUVwQyw4QkFBOEI7WUFDOUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFO2dCQUNuRSxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsRUFBRTtnQkFDbEIsT0FBTyxF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
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime parser for test file naming convention
|
|
3
|
+
* Supports: test.runtime1+runtime2.modifier.ts
|
|
4
|
+
* Examples:
|
|
5
|
+
* - test.node.ts
|
|
6
|
+
* - test.chromium.ts
|
|
7
|
+
* - test.node+chromium.ts
|
|
8
|
+
* - test.deno+bun.ts
|
|
9
|
+
* - test.chromium.nonci.ts
|
|
10
|
+
*/
|
|
11
|
+
export type Runtime = 'node' | 'chromium' | 'deno' | 'bun';
|
|
12
|
+
export type Modifier = 'nonci';
|
|
13
|
+
export interface ParsedFilename {
|
|
14
|
+
baseName: string;
|
|
15
|
+
runtimes: Runtime[];
|
|
16
|
+
modifiers: Modifier[];
|
|
17
|
+
extension: string;
|
|
18
|
+
isLegacy: boolean;
|
|
19
|
+
original: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ParserConfig {
|
|
22
|
+
strictUnknownRuntime?: boolean;
|
|
23
|
+
defaultRuntimes?: Runtime[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse a test filename to extract runtimes, modifiers, and detect legacy patterns
|
|
27
|
+
* Algorithm: Right-to-left token analysis from the extension
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseTestFilename(filePath: string, config?: ParserConfig): ParsedFilename;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a filename uses legacy naming convention
|
|
32
|
+
*/
|
|
33
|
+
export declare function isLegacyFilename(fileName: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Get the suggested new filename for a legacy filename
|
|
36
|
+
*/
|
|
37
|
+
export declare function getLegacyMigrationTarget(fileName: string): string | null;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime parser for test file naming convention
|
|
3
|
+
* Supports: test.runtime1+runtime2.modifier.ts
|
|
4
|
+
* Examples:
|
|
5
|
+
* - test.node.ts
|
|
6
|
+
* - test.chromium.ts
|
|
7
|
+
* - test.node+chromium.ts
|
|
8
|
+
* - test.deno+bun.ts
|
|
9
|
+
* - test.chromium.nonci.ts
|
|
10
|
+
*/
|
|
11
|
+
const KNOWN_RUNTIMES = new Set(['node', 'chromium', 'deno', 'bun']);
|
|
12
|
+
const KNOWN_MODIFIERS = new Set(['nonci']);
|
|
13
|
+
const VALID_EXTENSIONS = new Set(['ts', 'tsx', 'mts', 'cts']);
|
|
14
|
+
// Legacy mappings for backwards compatibility
|
|
15
|
+
const LEGACY_RUNTIME_MAP = {
|
|
16
|
+
browser: ['chromium'],
|
|
17
|
+
both: ['node', 'chromium'],
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse a test filename to extract runtimes, modifiers, and detect legacy patterns
|
|
21
|
+
* Algorithm: Right-to-left token analysis from the extension
|
|
22
|
+
*/
|
|
23
|
+
export function parseTestFilename(filePath, config = {}) {
|
|
24
|
+
const strictUnknownRuntime = config.strictUnknownRuntime ?? true;
|
|
25
|
+
const defaultRuntimes = config.defaultRuntimes ?? ['node'];
|
|
26
|
+
// Extract just the filename from the path
|
|
27
|
+
const fileName = filePath.split('/').pop() || filePath;
|
|
28
|
+
const original = fileName;
|
|
29
|
+
// Step 1: Extract and validate extension
|
|
30
|
+
const lastDot = fileName.lastIndexOf('.');
|
|
31
|
+
if (lastDot === -1) {
|
|
32
|
+
throw new Error(`Invalid test file: no extension found in "${fileName}"`);
|
|
33
|
+
}
|
|
34
|
+
const extension = fileName.substring(lastDot + 1);
|
|
35
|
+
if (!VALID_EXTENSIONS.has(extension)) {
|
|
36
|
+
throw new Error(`Invalid test file extension ".${extension}" in "${fileName}". ` +
|
|
37
|
+
`Valid extensions: ${Array.from(VALID_EXTENSIONS).join(', ')}`);
|
|
38
|
+
}
|
|
39
|
+
// Step 2: Split remaining basename by dots
|
|
40
|
+
const withoutExtension = fileName.substring(0, lastDot);
|
|
41
|
+
const tokens = withoutExtension.split('.');
|
|
42
|
+
if (tokens.length === 0) {
|
|
43
|
+
throw new Error(`Invalid test file: empty basename in "${fileName}"`);
|
|
44
|
+
}
|
|
45
|
+
// Step 3: Parse from right to left
|
|
46
|
+
let isLegacy = false;
|
|
47
|
+
const modifiers = [];
|
|
48
|
+
let runtimes = [];
|
|
49
|
+
let runtimeTokenIndex = -1;
|
|
50
|
+
// Scan from right to left
|
|
51
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
52
|
+
const token = tokens[i];
|
|
53
|
+
// Check if this is a known modifier
|
|
54
|
+
if (KNOWN_MODIFIERS.has(token)) {
|
|
55
|
+
modifiers.unshift(token);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Check if this is a legacy runtime token
|
|
59
|
+
if (LEGACY_RUNTIME_MAP[token]) {
|
|
60
|
+
isLegacy = true;
|
|
61
|
+
runtimes = LEGACY_RUNTIME_MAP[token];
|
|
62
|
+
runtimeTokenIndex = i;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
// Check if this is a runtime chain (may contain + separators)
|
|
66
|
+
if (token.includes('+')) {
|
|
67
|
+
const runtimeCandidates = token.split('+').map(r => r.trim()).filter(Boolean);
|
|
68
|
+
const validRuntimes = [];
|
|
69
|
+
const invalidRuntimes = [];
|
|
70
|
+
for (const candidate of runtimeCandidates) {
|
|
71
|
+
if (KNOWN_RUNTIMES.has(candidate)) {
|
|
72
|
+
// Dedupe: only add if not already in list
|
|
73
|
+
if (!validRuntimes.includes(candidate)) {
|
|
74
|
+
validRuntimes.push(candidate);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
invalidRuntimes.push(candidate);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (invalidRuntimes.length > 0) {
|
|
82
|
+
if (strictUnknownRuntime) {
|
|
83
|
+
throw new Error(`Unknown runtime(s) in "${fileName}": ${invalidRuntimes.join(', ')}. ` +
|
|
84
|
+
`Valid runtimes: ${Array.from(KNOWN_RUNTIMES).join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.warn(`⚠️ Warning: Unknown runtime(s) in "${fileName}": ${invalidRuntimes.join(', ')}. ` +
|
|
88
|
+
`Defaulting to: ${defaultRuntimes.join('+')}`);
|
|
89
|
+
runtimes = [...defaultRuntimes];
|
|
90
|
+
runtimeTokenIndex = i;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (validRuntimes.length > 0) {
|
|
95
|
+
runtimes = validRuntimes;
|
|
96
|
+
runtimeTokenIndex = i;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Check if this is a single runtime token
|
|
101
|
+
if (KNOWN_RUNTIMES.has(token)) {
|
|
102
|
+
runtimes = [token];
|
|
103
|
+
runtimeTokenIndex = i;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
// If we've scanned past modifiers and haven't found a runtime, stop looking
|
|
107
|
+
if (modifiers.length > 0) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Step 4: Determine base name
|
|
112
|
+
// Everything before the runtime token (if found) is the base name
|
|
113
|
+
const baseNameTokens = runtimeTokenIndex >= 0 ? tokens.slice(0, runtimeTokenIndex) : tokens;
|
|
114
|
+
const baseName = baseNameTokens.join('.');
|
|
115
|
+
// Step 5: Apply defaults if no runtime was detected
|
|
116
|
+
if (runtimes.length === 0) {
|
|
117
|
+
runtimes = [...defaultRuntimes];
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
baseName: baseName || 'test',
|
|
121
|
+
runtimes,
|
|
122
|
+
modifiers,
|
|
123
|
+
extension,
|
|
124
|
+
isLegacy,
|
|
125
|
+
original,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if a filename uses legacy naming convention
|
|
130
|
+
*/
|
|
131
|
+
export function isLegacyFilename(fileName) {
|
|
132
|
+
const tokens = fileName.split('.');
|
|
133
|
+
for (const token of tokens) {
|
|
134
|
+
if (LEGACY_RUNTIME_MAP[token]) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get the suggested new filename for a legacy filename
|
|
142
|
+
*/
|
|
143
|
+
export function getLegacyMigrationTarget(fileName) {
|
|
144
|
+
const parsed = parseTestFilename(fileName, { strictUnknownRuntime: false });
|
|
145
|
+
if (!parsed.isLegacy) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
// Reconstruct filename with new naming
|
|
149
|
+
const parts = [parsed.baseName];
|
|
150
|
+
if (parsed.runtimes.length > 0) {
|
|
151
|
+
parts.push(parsed.runtimes.join('+'));
|
|
152
|
+
}
|
|
153
|
+
if (parsed.modifiers.length > 0) {
|
|
154
|
+
parts.push(...parsed.modifiers);
|
|
155
|
+
}
|
|
156
|
+
parts.push(parsed.extension);
|
|
157
|
+
return parts.join('.');
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHN0ZXN0LmNsYXNzZXMucnVudGltZS5wYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy90c3Rlc3QuY2xhc3Nlcy5ydW50aW1lLnBhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFtQkgsTUFBTSxjQUFjLEdBQWdCLElBQUksR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUNqRixNQUFNLGVBQWUsR0FBZ0IsSUFBSSxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0FBQ3hELE1BQU0sZ0JBQWdCLEdBQWdCLElBQUksR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUUzRSw4Q0FBOEM7QUFDOUMsTUFBTSxrQkFBa0IsR0FBOEI7SUFDcEQsT0FBTyxFQUFFLENBQUMsVUFBVSxDQUFDO0lBQ3JCLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUM7Q0FDM0IsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0IsUUFBZ0IsRUFDaEIsU0FBdUIsRUFBRTtJQUV6QixNQUFNLG9CQUFvQixHQUFHLE1BQU0sQ0FBQyxvQkFBb0IsSUFBSSxJQUFJLENBQUM7SUFDakUsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDLGVBQWUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTNELDBDQUEwQztJQUMxQyxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLFFBQVEsQ0FBQztJQUN2RCxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUM7SUFFMUIseUNBQXlDO0lBQ3pDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDMUMsSUFBSSxPQUFPLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQzVFLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxRQUFRLENBQUMsU0FBUyxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNsRCxJQUFJLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FDYixpQ0FBaUMsU0FBUyxTQUFTLFFBQVEsS0FBSztZQUNoRSxxQkFBcUIsS0FBSyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUMvRCxDQUFDO0lBQ0osQ0FBQztJQUVELDJDQUEyQztJQUMzQyxNQUFNLGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3hELE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUUzQyxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyx5Q0FBeUMsUUFBUSxHQUFHLENBQUMsQ0FBQztJQUN4RSxDQUFDO0lBRUQsbUNBQW1DO0lBQ25DLElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQztJQUNyQixNQUFNLFNBQVMsR0FBZSxFQUFFLENBQUM7SUFDakMsSUFBSSxRQUFRLEdBQWMsRUFBRSxDQUFDO0lBQzdCLElBQUksaUJBQWlCLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFFM0IsMEJBQTBCO0lBQzFCLEtBQUssSUFBSSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzVDLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUV4QixvQ0FBb0M7UUFDcEMsSUFBSSxlQUFlLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDL0IsU0FBUyxDQUFDLE9BQU8sQ0FBQyxLQUFpQixDQUFDLENBQUM7WUFDckMsU0FBUztRQUNYLENBQUM7UUFFRCwwQ0FBMEM7UUFDMUMsSUFBSSxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzlCLFFBQVEsR0FBRyxJQUFJLENBQUM7WUFDaEIsUUFBUSxHQUFHLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3JDLGlCQUFpQixHQUFHLENBQUMsQ0FBQztZQUN0QixNQUFNO1FBQ1IsQ0FBQztRQUVELDhEQUE4RDtRQUM5RCxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN4QixNQUFNLGlCQUFpQixHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzlFLE1BQU0sYUFBYSxHQUFjLEVBQUUsQ0FBQztZQUNwQyxNQUFNLGVBQWUsR0FBYSxFQUFFLENBQUM7WUFFckMsS0FBSyxNQUFNLFNBQVMsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO2dCQUMxQyxJQUFJLGNBQWMsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztvQkFDbEMsMENBQTBDO29CQUMxQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxTQUFvQixDQUFDLEVBQUUsQ0FBQzt3QkFDbEQsYUFBYSxDQUFDLElBQUksQ0FBQyxTQUFvQixDQUFDLENBQUM7b0JBQzNDLENBQUM7Z0JBQ0gsQ0FBQztxQkFBTSxDQUFDO29CQUNOLGVBQWUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7Z0JBQ2xDLENBQUM7WUFDSCxDQUFDO1lBRUQsSUFBSSxlQUFlLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMvQixJQUFJLG9CQUFvQixFQUFFLENBQUM7b0JBQ3pCLE1BQU0sSUFBSSxLQUFLLENBQ2IsMEJBQTBCLFFBQVEsTUFBTSxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJO3dCQUN0RSxtQkFBbUIsS0FBSyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDM0QsQ0FBQztnQkFDSixDQUFDO3FCQUFNLENBQUM7b0JBQ04sT0FBTyxDQUFDLElBQUksQ0FDVix1Q0FBdUMsUUFBUSxNQUFNLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUk7d0JBQ25GLGtCQUFrQixlQUFlLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQzlDLENBQUM7b0JBQ0YsUUFBUSxHQUFHLENBQUMsR0FBRyxlQUFlLENBQUMsQ0FBQztvQkFDaEMsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDO29CQUN0QixNQUFNO2dCQUNSLENBQUM7WUFDSCxDQUFDO1lBRUQsSUFBSSxhQUFhLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUM3QixRQUFRLEdBQUcsYUFBYSxDQUFDO2dCQUN6QixpQkFBaUIsR0FBRyxDQUFDLENBQUM7Z0JBQ3RCLE1BQU07WUFDUixDQUFDO1FBQ0gsQ0FBQztRQUVELDBDQUEwQztRQUMxQyxJQUFJLGNBQWMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM5QixRQUFRLEdBQUcsQ0FBQyxLQUFnQixDQUFDLENBQUM7WUFDOUIsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDO1lBQ3RCLE1BQU07UUFDUixDQUFDO1FBRUQsNEVBQTRFO1FBQzVFLElBQUksU0FBUyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN6QixNQUFNO1FBQ1IsQ0FBQztJQUNILENBQUM7SUFFRCw4QkFBOEI7SUFDOUIsa0VBQWtFO0lBQ2xFLE1BQU0sY0FBYyxHQUFHLGlCQUFpQixJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzVGLE1BQU0sUUFBUSxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFMUMsb0RBQW9EO0lBQ3BELElBQUksUUFBUSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUMxQixRQUFRLEdBQUcsQ0FBQyxHQUFHLGVBQWUsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRCxPQUFPO1FBQ0wsUUFBUSxFQUFFLFFBQVEsSUFBSSxNQUFNO1FBQzVCLFFBQVE7UUFDUixTQUFTO1FBQ1QsU0FBUztRQUNULFFBQVE7UUFDUixRQUFRO0tBQ1QsQ0FBQztBQUNKLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxRQUFnQjtJQUMvQyxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ25DLEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7UUFDM0IsSUFBSSxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzlCLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxRQUFnQjtJQUN2RCxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxRQUFRLEVBQUUsRUFBRSxvQkFBb0IsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBRTVFLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDckIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsdUNBQXVDO0lBQ3ZDLE1BQU0sS0FBSyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBRWhDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDL0IsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFRCxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2hDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUVELEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBRTdCLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN6QixDQUFDIn0=
|