@testsmith/perfornium 0.1.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/README.md +360 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +192 -0
- package/dist/cli/commands/distributed.d.ts +11 -0
- package/dist/cli/commands/distributed.js +179 -0
- package/dist/cli/commands/import.d.ts +23 -0
- package/dist/cli/commands/import.js +461 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.js +923 -0
- package/dist/cli/commands/mock.d.ts +7 -0
- package/dist/cli/commands/mock.js +281 -0
- package/dist/cli/commands/report.d.ts +5 -0
- package/dist/cli/commands/report.js +70 -0
- package/dist/cli/commands/run.d.ts +12 -0
- package/dist/cli/commands/run.js +260 -0
- package/dist/cli/commands/validate.d.ts +3 -0
- package/dist/cli/commands/validate.js +35 -0
- package/dist/cli/commands/worker.d.ts +27 -0
- package/dist/cli/commands/worker.js +320 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.js +20 -0
- package/dist/config/parser.d.ts +19 -0
- package/dist/config/parser.js +330 -0
- package/dist/config/types/global-config.d.ts +74 -0
- package/dist/config/types/global-config.js +2 -0
- package/dist/config/types/hooks.d.ts +58 -0
- package/dist/config/types/hooks.js +3 -0
- package/dist/config/types/import-types.d.ts +33 -0
- package/dist/config/types/import-types.js +2 -0
- package/dist/config/types/index.d.ts +11 -0
- package/dist/config/types/index.js +27 -0
- package/dist/config/types/load-config.d.ts +32 -0
- package/dist/config/types/load-config.js +9 -0
- package/dist/config/types/output-config.d.ts +10 -0
- package/dist/config/types/output-config.js +2 -0
- package/dist/config/types/report-config.d.ts +10 -0
- package/dist/config/types/report-config.js +2 -0
- package/dist/config/types/runtime-types.d.ts +6 -0
- package/dist/config/types/runtime-types.js +2 -0
- package/dist/config/types/scenario-config.d.ts +30 -0
- package/dist/config/types/scenario-config.js +2 -0
- package/dist/config/types/step-types.d.ts +139 -0
- package/dist/config/types/step-types.js +2 -0
- package/dist/config/types/test-configuration.d.ts +18 -0
- package/dist/config/types/test-configuration.js +2 -0
- package/dist/config/types/worker-config.d.ts +12 -0
- package/dist/config/types/worker-config.js +2 -0
- package/dist/config/validator.d.ts +19 -0
- package/dist/config/validator.js +198 -0
- package/dist/core/csv-data-provider.d.ts +47 -0
- package/dist/core/csv-data-provider.js +265 -0
- package/dist/core/hooks-manager.d.ts +33 -0
- package/dist/core/hooks-manager.js +129 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +11 -0
- package/dist/core/script-executor.d.ts +14 -0
- package/dist/core/script-executor.js +290 -0
- package/dist/core/step-executor.d.ts +41 -0
- package/dist/core/step-executor.js +680 -0
- package/dist/core/test-runner.d.ts +34 -0
- package/dist/core/test-runner.js +465 -0
- package/dist/core/threshold-evaluator.d.ts +43 -0
- package/dist/core/threshold-evaluator.js +170 -0
- package/dist/core/virtual-user-pool.d.ts +42 -0
- package/dist/core/virtual-user-pool.js +136 -0
- package/dist/core/virtual-user.d.ts +51 -0
- package/dist/core/virtual-user.js +488 -0
- package/dist/distributed/coordinator.d.ts +34 -0
- package/dist/distributed/coordinator.js +158 -0
- package/dist/distributed/health-monitor.d.ts +18 -0
- package/dist/distributed/health-monitor.js +72 -0
- package/dist/distributed/load-distributor.d.ts +17 -0
- package/dist/distributed/load-distributor.js +106 -0
- package/dist/distributed/remote-worker.d.ts +37 -0
- package/dist/distributed/remote-worker.js +241 -0
- package/dist/distributed/result-aggregator.d.ts +43 -0
- package/dist/distributed/result-aggregator.js +146 -0
- package/dist/dsl/index.d.ts +3 -0
- package/dist/dsl/index.js +11 -0
- package/dist/dsl/test-builder.d.ts +111 -0
- package/dist/dsl/test-builder.js +514 -0
- package/dist/importers/har-importer.d.ts +17 -0
- package/dist/importers/har-importer.js +172 -0
- package/dist/importers/open-api-importer.d.ts +23 -0
- package/dist/importers/open-api-importer.js +181 -0
- package/dist/importers/wsdl-importer.d.ts +42 -0
- package/dist/importers/wsdl-importer.js +440 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +17 -0
- package/dist/load-patterns/arrivals.d.ts +7 -0
- package/dist/load-patterns/arrivals.js +118 -0
- package/dist/load-patterns/base.d.ts +9 -0
- package/dist/load-patterns/base.js +2 -0
- package/dist/load-patterns/basic.d.ts +7 -0
- package/dist/load-patterns/basic.js +117 -0
- package/dist/load-patterns/stepping.d.ts +6 -0
- package/dist/load-patterns/stepping.js +122 -0
- package/dist/metrics/collector.d.ts +72 -0
- package/dist/metrics/collector.js +662 -0
- package/dist/metrics/types.d.ts +135 -0
- package/dist/metrics/types.js +2 -0
- package/dist/outputs/base.d.ts +7 -0
- package/dist/outputs/base.js +2 -0
- package/dist/outputs/csv.d.ts +13 -0
- package/dist/outputs/csv.js +163 -0
- package/dist/outputs/graphite.d.ts +13 -0
- package/dist/outputs/graphite.js +126 -0
- package/dist/outputs/influxdb.d.ts +12 -0
- package/dist/outputs/influxdb.js +82 -0
- package/dist/outputs/json.d.ts +14 -0
- package/dist/outputs/json.js +107 -0
- package/dist/outputs/streaming-csv.d.ts +37 -0
- package/dist/outputs/streaming-csv.js +254 -0
- package/dist/outputs/streaming-json.d.ts +43 -0
- package/dist/outputs/streaming-json.js +353 -0
- package/dist/outputs/webhook.d.ts +16 -0
- package/dist/outputs/webhook.js +96 -0
- package/dist/protocols/base.d.ts +33 -0
- package/dist/protocols/base.js +2 -0
- package/dist/protocols/rest/handler.d.ts +67 -0
- package/dist/protocols/rest/handler.js +776 -0
- package/dist/protocols/soap/handler.d.ts +12 -0
- package/dist/protocols/soap/handler.js +165 -0
- package/dist/protocols/web/core-web-vitals.d.ts +121 -0
- package/dist/protocols/web/core-web-vitals.js +373 -0
- package/dist/protocols/web/handler.d.ts +50 -0
- package/dist/protocols/web/handler.js +706 -0
- package/dist/recorder/native-recorder.d.ts +14 -0
- package/dist/recorder/native-recorder.js +533 -0
- package/dist/recorder/scenario-recorder.d.ts +55 -0
- package/dist/recorder/scenario-recorder.js +296 -0
- package/dist/reporting/constants.d.ts +94 -0
- package/dist/reporting/constants.js +82 -0
- package/dist/reporting/enhanced-html-generator.d.ts +55 -0
- package/dist/reporting/enhanced-html-generator.js +965 -0
- package/dist/reporting/generator.d.ts +42 -0
- package/dist/reporting/generator.js +1217 -0
- package/dist/reporting/statistics.d.ts +144 -0
- package/dist/reporting/statistics.js +742 -0
- package/dist/reporting/templates/enhanced-report.hbs +2812 -0
- package/dist/reporting/templates/html.hbs +2453 -0
- package/dist/utils/faker-manager.d.ts +55 -0
- package/dist/utils/faker-manager.js +166 -0
- package/dist/utils/file-manager.d.ts +33 -0
- package/dist/utils/file-manager.js +154 -0
- package/dist/utils/handlebars-manager.d.ts +42 -0
- package/dist/utils/handlebars-manager.js +172 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +46 -0
- package/dist/utils/template.d.ts +80 -0
- package/dist/utils/template.js +513 -0
- package/dist/utils/test-output-writer.d.ts +56 -0
- package/dist/utils/test-output-writer.js +643 -0
- package/dist/utils/time.d.ts +3 -0
- package/dist/utils/time.js +23 -0
- package/dist/utils/timestamp-helper.d.ts +17 -0
- package/dist/utils/timestamp-helper.js +53 -0
- package/dist/workers/manager.d.ts +18 -0
- package/dist/workers/manager.js +95 -0
- package/dist/workers/server.d.ts +21 -0
- package/dist/workers/server.js +205 -0
- package/dist/workers/worker.d.ts +19 -0
- package/dist/workers/worker.js +147 -0
- package/package.json +102 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
type FakerType = typeof import('@faker-js/faker').faker;
|
|
2
|
+
/**
|
|
3
|
+
* Lazy-loading Faker manager
|
|
4
|
+
* Only initializes faker when actually used, reducing startup time
|
|
5
|
+
*/
|
|
6
|
+
export declare class FakerManager {
|
|
7
|
+
private static _instance;
|
|
8
|
+
private _faker;
|
|
9
|
+
private _locales;
|
|
10
|
+
private _currentLocale;
|
|
11
|
+
private _seed;
|
|
12
|
+
private _initialized;
|
|
13
|
+
private constructor();
|
|
14
|
+
static getInstance(): FakerManager;
|
|
15
|
+
/**
|
|
16
|
+
* Check if faker has been initialized
|
|
17
|
+
*/
|
|
18
|
+
get isInitialized(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Get the current locale
|
|
21
|
+
*/
|
|
22
|
+
get currentLocale(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Set locale (will be applied on next getFaker call)
|
|
25
|
+
*/
|
|
26
|
+
setLocale(locale: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Set seed for reproducible data
|
|
29
|
+
*/
|
|
30
|
+
setSeed(seed: number | undefined): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get available locales
|
|
33
|
+
*/
|
|
34
|
+
getAvailableLocales(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Lazily get faker instance - only loads when first called
|
|
37
|
+
*/
|
|
38
|
+
getFaker(): FakerType;
|
|
39
|
+
/**
|
|
40
|
+
* Synchronous initialization using require
|
|
41
|
+
* This is called lazily on first use
|
|
42
|
+
*/
|
|
43
|
+
private initializeSync;
|
|
44
|
+
/**
|
|
45
|
+
* Switch to a different locale (loads lazily if not cached)
|
|
46
|
+
*/
|
|
47
|
+
switchLocale(locale: string): FakerType;
|
|
48
|
+
/**
|
|
49
|
+
* Reset the manager (useful for testing)
|
|
50
|
+
*/
|
|
51
|
+
reset(): void;
|
|
52
|
+
}
|
|
53
|
+
export declare const fakerManager: FakerManager;
|
|
54
|
+
export declare function getFaker(): FakerType;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fakerManager = exports.FakerManager = void 0;
|
|
4
|
+
exports.getFaker = getFaker;
|
|
5
|
+
const logger_1 = require("./logger");
|
|
6
|
+
/**
|
|
7
|
+
* Lazy-loading Faker manager
|
|
8
|
+
* Only initializes faker when actually used, reducing startup time
|
|
9
|
+
*/
|
|
10
|
+
class FakerManager {
|
|
11
|
+
constructor() {
|
|
12
|
+
this._faker = null;
|
|
13
|
+
this._locales = new Map();
|
|
14
|
+
this._currentLocale = 'en';
|
|
15
|
+
this._initialized = false;
|
|
16
|
+
}
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (!FakerManager._instance) {
|
|
19
|
+
FakerManager._instance = new FakerManager();
|
|
20
|
+
}
|
|
21
|
+
return FakerManager._instance;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if faker has been initialized
|
|
25
|
+
*/
|
|
26
|
+
get isInitialized() {
|
|
27
|
+
return this._initialized;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the current locale
|
|
31
|
+
*/
|
|
32
|
+
get currentLocale() {
|
|
33
|
+
return this._currentLocale;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set locale (will be applied on next getFaker call)
|
|
37
|
+
*/
|
|
38
|
+
setLocale(locale) {
|
|
39
|
+
if (locale !== this._currentLocale) {
|
|
40
|
+
this._currentLocale = locale;
|
|
41
|
+
// Clear cached faker to force reload with new locale
|
|
42
|
+
if (this._initialized && !this._locales.has(locale)) {
|
|
43
|
+
logger_1.logger.debug(`Locale changed to ${locale}, will load on next use`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Set seed for reproducible data
|
|
49
|
+
*/
|
|
50
|
+
setSeed(seed) {
|
|
51
|
+
this._seed = seed;
|
|
52
|
+
if (this._faker) {
|
|
53
|
+
this._faker.seed(seed);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get available locales
|
|
58
|
+
*/
|
|
59
|
+
getAvailableLocales() {
|
|
60
|
+
return ['en', 'de', 'fr', 'es', 'nl'];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Lazily get faker instance - only loads when first called
|
|
64
|
+
*/
|
|
65
|
+
getFaker() {
|
|
66
|
+
if (!this._initialized) {
|
|
67
|
+
this.initializeSync();
|
|
68
|
+
}
|
|
69
|
+
return this._faker;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Synchronous initialization using require
|
|
73
|
+
* This is called lazily on first use
|
|
74
|
+
*/
|
|
75
|
+
initializeSync() {
|
|
76
|
+
if (this._initialized)
|
|
77
|
+
return;
|
|
78
|
+
logger_1.logger.debug(`Lazily initializing faker with locale: ${this._currentLocale}`);
|
|
79
|
+
try {
|
|
80
|
+
// Use require for synchronous loading
|
|
81
|
+
const fakerModule = require('@faker-js/faker');
|
|
82
|
+
// Load the appropriate locale
|
|
83
|
+
switch (this._currentLocale) {
|
|
84
|
+
case 'de':
|
|
85
|
+
this._faker = fakerModule.fakerDE;
|
|
86
|
+
break;
|
|
87
|
+
case 'fr':
|
|
88
|
+
this._faker = fakerModule.fakerFR;
|
|
89
|
+
break;
|
|
90
|
+
case 'es':
|
|
91
|
+
this._faker = fakerModule.fakerES;
|
|
92
|
+
break;
|
|
93
|
+
case 'nl':
|
|
94
|
+
this._faker = fakerModule.fakerNL;
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
this._faker = fakerModule.faker;
|
|
98
|
+
}
|
|
99
|
+
this._locales.set(this._currentLocale, this._faker);
|
|
100
|
+
if (this._seed !== undefined) {
|
|
101
|
+
this._faker.seed(this._seed);
|
|
102
|
+
}
|
|
103
|
+
this._initialized = true;
|
|
104
|
+
logger_1.logger.debug(`Faker initialized with locale: ${this._currentLocale}`);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
logger_1.logger.error('Failed to initialize faker:', error);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Switch to a different locale (loads lazily if not cached)
|
|
113
|
+
*/
|
|
114
|
+
switchLocale(locale) {
|
|
115
|
+
if (this._locales.has(locale)) {
|
|
116
|
+
this._faker = this._locales.get(locale);
|
|
117
|
+
this._currentLocale = locale;
|
|
118
|
+
return this._faker;
|
|
119
|
+
}
|
|
120
|
+
// Load the new locale
|
|
121
|
+
const fakerModule = require('@faker-js/faker');
|
|
122
|
+
let localeFaker;
|
|
123
|
+
switch (locale) {
|
|
124
|
+
case 'de':
|
|
125
|
+
localeFaker = fakerModule.fakerDE;
|
|
126
|
+
break;
|
|
127
|
+
case 'fr':
|
|
128
|
+
localeFaker = fakerModule.fakerFR;
|
|
129
|
+
break;
|
|
130
|
+
case 'es':
|
|
131
|
+
localeFaker = fakerModule.fakerES;
|
|
132
|
+
break;
|
|
133
|
+
case 'nl':
|
|
134
|
+
localeFaker = fakerModule.fakerNL;
|
|
135
|
+
break;
|
|
136
|
+
default:
|
|
137
|
+
localeFaker = fakerModule.faker;
|
|
138
|
+
}
|
|
139
|
+
this._locales.set(locale, localeFaker);
|
|
140
|
+
this._faker = localeFaker;
|
|
141
|
+
this._currentLocale = locale;
|
|
142
|
+
if (this._seed !== undefined) {
|
|
143
|
+
this._faker.seed(this._seed);
|
|
144
|
+
}
|
|
145
|
+
logger_1.logger.debug(`Switched to faker locale: ${locale}`);
|
|
146
|
+
return this._faker;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Reset the manager (useful for testing)
|
|
150
|
+
*/
|
|
151
|
+
reset() {
|
|
152
|
+
this._faker = null;
|
|
153
|
+
this._locales.clear();
|
|
154
|
+
this._currentLocale = 'en';
|
|
155
|
+
this._seed = undefined;
|
|
156
|
+
this._initialized = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.FakerManager = FakerManager;
|
|
160
|
+
FakerManager._instance = null;
|
|
161
|
+
// Export singleton accessor
|
|
162
|
+
exports.fakerManager = FakerManager.getInstance();
|
|
163
|
+
// Export a proxy getter for backward compatibility
|
|
164
|
+
function getFaker() {
|
|
165
|
+
return exports.fakerManager.getFaker();
|
|
166
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File management utility for handling timestamped files and ensuring directories exist
|
|
3
|
+
*/
|
|
4
|
+
export declare class FileManager {
|
|
5
|
+
/**
|
|
6
|
+
* Process template path and ensure directory exists
|
|
7
|
+
*/
|
|
8
|
+
static processFilePath(templatePath: string, context?: any): string;
|
|
9
|
+
/**
|
|
10
|
+
* Process timestamp templates in file paths using TimestampHelper
|
|
11
|
+
*/
|
|
12
|
+
private static processTimestampTemplate;
|
|
13
|
+
/**
|
|
14
|
+
* Ensure directory exists for a file path
|
|
15
|
+
*/
|
|
16
|
+
static ensureDirectoryExists(filePath: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Create file with timestamped name and ensure directory exists
|
|
19
|
+
*/
|
|
20
|
+
static createTimestampedFile(templatePath: string, content: string, context?: any): string;
|
|
21
|
+
/**
|
|
22
|
+
* Generate unique filename if file already exists
|
|
23
|
+
*/
|
|
24
|
+
static generateUniqueFileName(originalPath: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Safe write - creates unique filename if needed
|
|
27
|
+
*/
|
|
28
|
+
static safeWriteFile(templatePath: string, content: string, context?: any, overwrite?: boolean): string;
|
|
29
|
+
/**
|
|
30
|
+
* Create timestamped backup of existing file
|
|
31
|
+
*/
|
|
32
|
+
static createBackup(filePath: string): string | null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const timestamp_helper_1 = require("./timestamp-helper");
|
|
40
|
+
/**
|
|
41
|
+
* File management utility for handling timestamped files and ensuring directories exist
|
|
42
|
+
*/
|
|
43
|
+
class FileManager {
|
|
44
|
+
/**
|
|
45
|
+
* Process template path and ensure directory exists
|
|
46
|
+
*/
|
|
47
|
+
static processFilePath(templatePath, context) {
|
|
48
|
+
let processedPath = templatePath;
|
|
49
|
+
// Always process timestamp templates, even without full context
|
|
50
|
+
if (templatePath.includes('{{timestamp')) {
|
|
51
|
+
processedPath = this.processTimestampTemplate(templatePath);
|
|
52
|
+
}
|
|
53
|
+
// If context is provided, use full template processing
|
|
54
|
+
if (context) {
|
|
55
|
+
const { TemplateProcessor } = require('./template');
|
|
56
|
+
const processor = new TemplateProcessor();
|
|
57
|
+
processedPath = processor.process(processedPath, context);
|
|
58
|
+
}
|
|
59
|
+
// Ensure directory exists
|
|
60
|
+
this.ensureDirectoryExists(processedPath);
|
|
61
|
+
return processedPath;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Process timestamp templates in file paths using TimestampHelper
|
|
65
|
+
*/
|
|
66
|
+
static processTimestampTemplate(templatePath) {
|
|
67
|
+
return templatePath.replace(/\{\{timestamp(?::([^}]+))?\}\}/g, (_match, format) => {
|
|
68
|
+
const fmt = (format || 'file');
|
|
69
|
+
return timestamp_helper_1.TimestampHelper.getTimestamp(fmt);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Ensure directory exists for a file path
|
|
74
|
+
*/
|
|
75
|
+
static ensureDirectoryExists(filePath) {
|
|
76
|
+
const dir = path.dirname(filePath);
|
|
77
|
+
if (!fs.existsSync(dir)) {
|
|
78
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
79
|
+
console.log(`📁 Created directory: ${dir}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create file with timestamped name and ensure directory exists
|
|
84
|
+
*/
|
|
85
|
+
static createTimestampedFile(templatePath, content, context) {
|
|
86
|
+
const processedPath = this.processFilePath(templatePath, context);
|
|
87
|
+
try {
|
|
88
|
+
fs.writeFileSync(processedPath, content);
|
|
89
|
+
console.log(`📄 Created file: ${processedPath}`);
|
|
90
|
+
return processedPath;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error(`❌ Failed to create file ${processedPath}:`, error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generate unique filename if file already exists
|
|
99
|
+
*/
|
|
100
|
+
static generateUniqueFileName(originalPath) {
|
|
101
|
+
if (!fs.existsSync(originalPath)) {
|
|
102
|
+
return originalPath;
|
|
103
|
+
}
|
|
104
|
+
const dir = path.dirname(originalPath);
|
|
105
|
+
const ext = path.extname(originalPath);
|
|
106
|
+
const name = path.basename(originalPath, ext);
|
|
107
|
+
let counter = 1;
|
|
108
|
+
let newPath;
|
|
109
|
+
do {
|
|
110
|
+
newPath = path.join(dir, `${name}-${counter}${ext}`);
|
|
111
|
+
counter++;
|
|
112
|
+
} while (fs.existsSync(newPath));
|
|
113
|
+
return newPath;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Safe write - creates unique filename if needed
|
|
117
|
+
*/
|
|
118
|
+
static safeWriteFile(templatePath, content, context, overwrite = false) {
|
|
119
|
+
const processedPath = this.processFilePath(templatePath, context);
|
|
120
|
+
const finalPath = overwrite ? processedPath : this.generateUniqueFileName(processedPath);
|
|
121
|
+
try {
|
|
122
|
+
fs.writeFileSync(finalPath, content);
|
|
123
|
+
console.log(`📄 Safely created file: ${finalPath}`);
|
|
124
|
+
return finalPath;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(`❌ Failed to safely create file ${finalPath}:`, error);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create timestamped backup of existing file
|
|
133
|
+
*/
|
|
134
|
+
static createBackup(filePath) {
|
|
135
|
+
if (!fs.existsSync(filePath)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const dir = path.dirname(filePath);
|
|
139
|
+
const ext = path.extname(filePath);
|
|
140
|
+
const name = path.basename(filePath, ext);
|
|
141
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
142
|
+
const backupPath = path.join(dir, `${name}.${timestamp}.backup${ext}`);
|
|
143
|
+
try {
|
|
144
|
+
fs.copyFileSync(filePath, backupPath);
|
|
145
|
+
console.log(`💾 Created backup: ${backupPath}`);
|
|
146
|
+
return backupPath;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error(`❌ Failed to create backup of ${filePath}:`, error);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.FileManager = FileManager;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as Handlebars from 'handlebars';
|
|
2
|
+
export interface TemplateConfig {
|
|
3
|
+
file: string;
|
|
4
|
+
data?: Record<string, any>;
|
|
5
|
+
helpers?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
export declare class HandlebarsTemplateManager {
|
|
8
|
+
private static instance;
|
|
9
|
+
private templateCache;
|
|
10
|
+
private baseDir;
|
|
11
|
+
private constructor();
|
|
12
|
+
static getInstance(): HandlebarsTemplateManager;
|
|
13
|
+
/**
|
|
14
|
+
* Set the base directory for template files
|
|
15
|
+
*/
|
|
16
|
+
setBaseDir(dir: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Register default Handlebars helpers for faker and utilities
|
|
19
|
+
*/
|
|
20
|
+
private registerDefaultHelpers;
|
|
21
|
+
/**
|
|
22
|
+
* Register custom helpers
|
|
23
|
+
*/
|
|
24
|
+
registerHelper(name: string, helper: Handlebars.HelperDelegate): void;
|
|
25
|
+
/**
|
|
26
|
+
* Load and compile a template file
|
|
27
|
+
*/
|
|
28
|
+
private loadTemplate;
|
|
29
|
+
/**
|
|
30
|
+
* Process a template with data
|
|
31
|
+
*/
|
|
32
|
+
processTemplate(templateConfig: TemplateConfig, context: Record<string, any>): any;
|
|
33
|
+
/**
|
|
34
|
+
* Clear template cache (useful for development)
|
|
35
|
+
*/
|
|
36
|
+
clearCache(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get nested property from object
|
|
39
|
+
*/
|
|
40
|
+
private getNestedProperty;
|
|
41
|
+
}
|
|
42
|
+
export declare const handlebarsManager: HandlebarsTemplateManager;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handlebarsManager = exports.HandlebarsTemplateManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const Handlebars = __importStar(require("handlebars"));
|
|
40
|
+
const faker_manager_1 = require("./faker-manager");
|
|
41
|
+
const logger_1 = require("./logger");
|
|
42
|
+
class HandlebarsTemplateManager {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.templateCache = new Map();
|
|
45
|
+
this.baseDir = process.cwd();
|
|
46
|
+
this.registerDefaultHelpers();
|
|
47
|
+
}
|
|
48
|
+
static getInstance() {
|
|
49
|
+
if (!HandlebarsTemplateManager.instance) {
|
|
50
|
+
HandlebarsTemplateManager.instance = new HandlebarsTemplateManager();
|
|
51
|
+
}
|
|
52
|
+
return HandlebarsTemplateManager.instance;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Set the base directory for template files
|
|
56
|
+
*/
|
|
57
|
+
setBaseDir(dir) {
|
|
58
|
+
this.baseDir = dir;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Register default Handlebars helpers for faker and utilities
|
|
62
|
+
*/
|
|
63
|
+
registerDefaultHelpers() {
|
|
64
|
+
// Faker helpers - lazily load faker only when helper is called
|
|
65
|
+
Handlebars.registerHelper('faker', (path) => {
|
|
66
|
+
return this.getNestedProperty((0, faker_manager_1.getFaker)(), path)();
|
|
67
|
+
});
|
|
68
|
+
// Random helpers
|
|
69
|
+
Handlebars.registerHelper('randomInt', (min, max) => {
|
|
70
|
+
return (0, faker_manager_1.getFaker)().number.int({ min, max });
|
|
71
|
+
});
|
|
72
|
+
Handlebars.registerHelper('randomChoice', (...args) => {
|
|
73
|
+
// Remove the last argument (Handlebars options)
|
|
74
|
+
const choices = args.slice(0, -1);
|
|
75
|
+
return (0, faker_manager_1.getFaker)().helpers.arrayElement(choices);
|
|
76
|
+
});
|
|
77
|
+
Handlebars.registerHelper('uuid', () => {
|
|
78
|
+
return (0, faker_manager_1.getFaker)().string.uuid();
|
|
79
|
+
});
|
|
80
|
+
Handlebars.registerHelper('timestamp', () => {
|
|
81
|
+
return Date.now();
|
|
82
|
+
});
|
|
83
|
+
Handlebars.registerHelper('isoDate', (days = 0) => {
|
|
84
|
+
const date = new Date();
|
|
85
|
+
date.setDate(date.getDate() + days);
|
|
86
|
+
return date.toISOString();
|
|
87
|
+
});
|
|
88
|
+
// Conditional helpers
|
|
89
|
+
Handlebars.registerHelper('eq', (a, b) => a === b);
|
|
90
|
+
Handlebars.registerHelper('ne', (a, b) => a !== b);
|
|
91
|
+
Handlebars.registerHelper('gt', (a, b) => a > b);
|
|
92
|
+
Handlebars.registerHelper('lt', (a, b) => a < b);
|
|
93
|
+
// Array helpers
|
|
94
|
+
Handlebars.registerHelper('length', (array) => array ? array.length : 0);
|
|
95
|
+
Handlebars.registerHelper('first', (array) => array && array.length > 0 ? array[0] : null);
|
|
96
|
+
Handlebars.registerHelper('last', (array) => array && array.length > 0 ? array[array.length - 1] : null);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Register custom helpers
|
|
100
|
+
*/
|
|
101
|
+
registerHelper(name, helper) {
|
|
102
|
+
Handlebars.registerHelper(name, helper);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Load and compile a template file
|
|
106
|
+
*/
|
|
107
|
+
loadTemplate(templatePath) {
|
|
108
|
+
const fullPath = path.resolve(this.baseDir, templatePath);
|
|
109
|
+
if (this.templateCache.has(fullPath)) {
|
|
110
|
+
return this.templateCache.get(fullPath);
|
|
111
|
+
}
|
|
112
|
+
if (!fs.existsSync(fullPath)) {
|
|
113
|
+
throw new Error(`Template file not found: ${fullPath}`);
|
|
114
|
+
}
|
|
115
|
+
const templateContent = fs.readFileSync(fullPath, 'utf8');
|
|
116
|
+
const compiledTemplate = Handlebars.compile(templateContent);
|
|
117
|
+
this.templateCache.set(fullPath, compiledTemplate);
|
|
118
|
+
return compiledTemplate;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Process a template with data
|
|
122
|
+
*/
|
|
123
|
+
processTemplate(templateConfig, context) {
|
|
124
|
+
const template = this.loadTemplate(templateConfig.file);
|
|
125
|
+
// Merge data sources: template data + context + extracted data
|
|
126
|
+
const templateData = {
|
|
127
|
+
...context.variables,
|
|
128
|
+
...context.extracted_data,
|
|
129
|
+
...templateConfig.data,
|
|
130
|
+
// Add context info
|
|
131
|
+
vu_id: context.vu_id,
|
|
132
|
+
iteration: context.iteration,
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
};
|
|
135
|
+
logger_1.logger.debug(`Processing Handlebars template: ${templateConfig.file}`);
|
|
136
|
+
logger_1.logger.debug(`Template data: ${JSON.stringify(templateData, null, 2)}`);
|
|
137
|
+
const result = template(templateData);
|
|
138
|
+
try {
|
|
139
|
+
// Try to parse as JSON if it looks like JSON
|
|
140
|
+
const trimmed = result.trim();
|
|
141
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
142
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
143
|
+
return JSON.parse(result);
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// If JSON parsing fails, return as string
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Clear template cache (useful for development)
|
|
154
|
+
*/
|
|
155
|
+
clearCache() {
|
|
156
|
+
this.templateCache.clear();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get nested property from object
|
|
160
|
+
*/
|
|
161
|
+
getNestedProperty(obj, path) {
|
|
162
|
+
return path.split('.').reduce((current, key) => {
|
|
163
|
+
if (current && typeof current === 'object' && key in current) {
|
|
164
|
+
return current[key];
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`Property "${key}" not found in path "${path}"`);
|
|
167
|
+
}, obj);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.HandlebarsTemplateManager = HandlebarsTemplateManager;
|
|
171
|
+
// Export singleton instance
|
|
172
|
+
exports.handlebarsManager = HandlebarsTemplateManager.getInstance();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare enum LogLevel {
|
|
2
|
+
DEBUG = 0,
|
|
3
|
+
INFO = 1,
|
|
4
|
+
WARN = 2,
|
|
5
|
+
ERROR = 3
|
|
6
|
+
}
|
|
7
|
+
export declare class Logger {
|
|
8
|
+
private level;
|
|
9
|
+
setLevel(level: LogLevel): void;
|
|
10
|
+
debug(message: string, ...args: any[]): void;
|
|
11
|
+
info(message: string, ...args: any[]): void;
|
|
12
|
+
warn(message: string, ...args: any[]): void;
|
|
13
|
+
error(message: string, ...args: any[]): void;
|
|
14
|
+
success(message: string, ...args: any[]): void;
|
|
15
|
+
}
|
|
16
|
+
export declare const logger: Logger;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = exports.Logger = exports.LogLevel = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
var LogLevel;
|
|
9
|
+
(function (LogLevel) {
|
|
10
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
11
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
12
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
13
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
14
|
+
})(LogLevel || (exports.LogLevel = LogLevel = {}));
|
|
15
|
+
class Logger {
|
|
16
|
+
constructor() {
|
|
17
|
+
// Default to WARN - minimal output unless verbose mode is enabled
|
|
18
|
+
this.level = LogLevel.WARN;
|
|
19
|
+
}
|
|
20
|
+
setLevel(level) { this.level = level; }
|
|
21
|
+
debug(message, ...args) {
|
|
22
|
+
if (this.level <= LogLevel.DEBUG) {
|
|
23
|
+
console.log(chalk_1.default.gray(`[DEBUG] ${message}`), ...args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
info(message, ...args) {
|
|
27
|
+
if (this.level <= LogLevel.INFO) {
|
|
28
|
+
console.log(chalk_1.default.blue(`[INFO] ${message}`), ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
warn(message, ...args) {
|
|
32
|
+
if (this.level <= LogLevel.WARN) {
|
|
33
|
+
console.warn(chalk_1.default.yellow(`[WARN] ${message}`), ...args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
error(message, ...args) {
|
|
37
|
+
if (this.level <= LogLevel.ERROR) {
|
|
38
|
+
console.error(chalk_1.default.red(`[ERROR] ${message}`), ...args);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
success(message, ...args) {
|
|
42
|
+
console.log(chalk_1.default.green(`[SUCCESS] ${message}`), ...args);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.Logger = Logger;
|
|
46
|
+
exports.logger = new Logger();
|