@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.
Files changed (164) hide show
  1. package/README.md +360 -0
  2. package/dist/cli/cli.d.ts +2 -0
  3. package/dist/cli/cli.js +192 -0
  4. package/dist/cli/commands/distributed.d.ts +11 -0
  5. package/dist/cli/commands/distributed.js +179 -0
  6. package/dist/cli/commands/import.d.ts +23 -0
  7. package/dist/cli/commands/import.js +461 -0
  8. package/dist/cli/commands/init.d.ts +7 -0
  9. package/dist/cli/commands/init.js +923 -0
  10. package/dist/cli/commands/mock.d.ts +7 -0
  11. package/dist/cli/commands/mock.js +281 -0
  12. package/dist/cli/commands/report.d.ts +5 -0
  13. package/dist/cli/commands/report.js +70 -0
  14. package/dist/cli/commands/run.d.ts +12 -0
  15. package/dist/cli/commands/run.js +260 -0
  16. package/dist/cli/commands/validate.d.ts +3 -0
  17. package/dist/cli/commands/validate.js +35 -0
  18. package/dist/cli/commands/worker.d.ts +27 -0
  19. package/dist/cli/commands/worker.js +320 -0
  20. package/dist/config/index.d.ts +2 -0
  21. package/dist/config/index.js +20 -0
  22. package/dist/config/parser.d.ts +19 -0
  23. package/dist/config/parser.js +330 -0
  24. package/dist/config/types/global-config.d.ts +74 -0
  25. package/dist/config/types/global-config.js +2 -0
  26. package/dist/config/types/hooks.d.ts +58 -0
  27. package/dist/config/types/hooks.js +3 -0
  28. package/dist/config/types/import-types.d.ts +33 -0
  29. package/dist/config/types/import-types.js +2 -0
  30. package/dist/config/types/index.d.ts +11 -0
  31. package/dist/config/types/index.js +27 -0
  32. package/dist/config/types/load-config.d.ts +32 -0
  33. package/dist/config/types/load-config.js +9 -0
  34. package/dist/config/types/output-config.d.ts +10 -0
  35. package/dist/config/types/output-config.js +2 -0
  36. package/dist/config/types/report-config.d.ts +10 -0
  37. package/dist/config/types/report-config.js +2 -0
  38. package/dist/config/types/runtime-types.d.ts +6 -0
  39. package/dist/config/types/runtime-types.js +2 -0
  40. package/dist/config/types/scenario-config.d.ts +30 -0
  41. package/dist/config/types/scenario-config.js +2 -0
  42. package/dist/config/types/step-types.d.ts +139 -0
  43. package/dist/config/types/step-types.js +2 -0
  44. package/dist/config/types/test-configuration.d.ts +18 -0
  45. package/dist/config/types/test-configuration.js +2 -0
  46. package/dist/config/types/worker-config.d.ts +12 -0
  47. package/dist/config/types/worker-config.js +2 -0
  48. package/dist/config/validator.d.ts +19 -0
  49. package/dist/config/validator.js +198 -0
  50. package/dist/core/csv-data-provider.d.ts +47 -0
  51. package/dist/core/csv-data-provider.js +265 -0
  52. package/dist/core/hooks-manager.d.ts +33 -0
  53. package/dist/core/hooks-manager.js +129 -0
  54. package/dist/core/index.d.ts +5 -0
  55. package/dist/core/index.js +11 -0
  56. package/dist/core/script-executor.d.ts +14 -0
  57. package/dist/core/script-executor.js +290 -0
  58. package/dist/core/step-executor.d.ts +41 -0
  59. package/dist/core/step-executor.js +680 -0
  60. package/dist/core/test-runner.d.ts +34 -0
  61. package/dist/core/test-runner.js +465 -0
  62. package/dist/core/threshold-evaluator.d.ts +43 -0
  63. package/dist/core/threshold-evaluator.js +170 -0
  64. package/dist/core/virtual-user-pool.d.ts +42 -0
  65. package/dist/core/virtual-user-pool.js +136 -0
  66. package/dist/core/virtual-user.d.ts +51 -0
  67. package/dist/core/virtual-user.js +488 -0
  68. package/dist/distributed/coordinator.d.ts +34 -0
  69. package/dist/distributed/coordinator.js +158 -0
  70. package/dist/distributed/health-monitor.d.ts +18 -0
  71. package/dist/distributed/health-monitor.js +72 -0
  72. package/dist/distributed/load-distributor.d.ts +17 -0
  73. package/dist/distributed/load-distributor.js +106 -0
  74. package/dist/distributed/remote-worker.d.ts +37 -0
  75. package/dist/distributed/remote-worker.js +241 -0
  76. package/dist/distributed/result-aggregator.d.ts +43 -0
  77. package/dist/distributed/result-aggregator.js +146 -0
  78. package/dist/dsl/index.d.ts +3 -0
  79. package/dist/dsl/index.js +11 -0
  80. package/dist/dsl/test-builder.d.ts +111 -0
  81. package/dist/dsl/test-builder.js +514 -0
  82. package/dist/importers/har-importer.d.ts +17 -0
  83. package/dist/importers/har-importer.js +172 -0
  84. package/dist/importers/open-api-importer.d.ts +23 -0
  85. package/dist/importers/open-api-importer.js +181 -0
  86. package/dist/importers/wsdl-importer.d.ts +42 -0
  87. package/dist/importers/wsdl-importer.js +440 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.js +17 -0
  90. package/dist/load-patterns/arrivals.d.ts +7 -0
  91. package/dist/load-patterns/arrivals.js +118 -0
  92. package/dist/load-patterns/base.d.ts +9 -0
  93. package/dist/load-patterns/base.js +2 -0
  94. package/dist/load-patterns/basic.d.ts +7 -0
  95. package/dist/load-patterns/basic.js +117 -0
  96. package/dist/load-patterns/stepping.d.ts +6 -0
  97. package/dist/load-patterns/stepping.js +122 -0
  98. package/dist/metrics/collector.d.ts +72 -0
  99. package/dist/metrics/collector.js +662 -0
  100. package/dist/metrics/types.d.ts +135 -0
  101. package/dist/metrics/types.js +2 -0
  102. package/dist/outputs/base.d.ts +7 -0
  103. package/dist/outputs/base.js +2 -0
  104. package/dist/outputs/csv.d.ts +13 -0
  105. package/dist/outputs/csv.js +163 -0
  106. package/dist/outputs/graphite.d.ts +13 -0
  107. package/dist/outputs/graphite.js +126 -0
  108. package/dist/outputs/influxdb.d.ts +12 -0
  109. package/dist/outputs/influxdb.js +82 -0
  110. package/dist/outputs/json.d.ts +14 -0
  111. package/dist/outputs/json.js +107 -0
  112. package/dist/outputs/streaming-csv.d.ts +37 -0
  113. package/dist/outputs/streaming-csv.js +254 -0
  114. package/dist/outputs/streaming-json.d.ts +43 -0
  115. package/dist/outputs/streaming-json.js +353 -0
  116. package/dist/outputs/webhook.d.ts +16 -0
  117. package/dist/outputs/webhook.js +96 -0
  118. package/dist/protocols/base.d.ts +33 -0
  119. package/dist/protocols/base.js +2 -0
  120. package/dist/protocols/rest/handler.d.ts +67 -0
  121. package/dist/protocols/rest/handler.js +776 -0
  122. package/dist/protocols/soap/handler.d.ts +12 -0
  123. package/dist/protocols/soap/handler.js +165 -0
  124. package/dist/protocols/web/core-web-vitals.d.ts +121 -0
  125. package/dist/protocols/web/core-web-vitals.js +373 -0
  126. package/dist/protocols/web/handler.d.ts +50 -0
  127. package/dist/protocols/web/handler.js +706 -0
  128. package/dist/recorder/native-recorder.d.ts +14 -0
  129. package/dist/recorder/native-recorder.js +533 -0
  130. package/dist/recorder/scenario-recorder.d.ts +55 -0
  131. package/dist/recorder/scenario-recorder.js +296 -0
  132. package/dist/reporting/constants.d.ts +94 -0
  133. package/dist/reporting/constants.js +82 -0
  134. package/dist/reporting/enhanced-html-generator.d.ts +55 -0
  135. package/dist/reporting/enhanced-html-generator.js +965 -0
  136. package/dist/reporting/generator.d.ts +42 -0
  137. package/dist/reporting/generator.js +1217 -0
  138. package/dist/reporting/statistics.d.ts +144 -0
  139. package/dist/reporting/statistics.js +742 -0
  140. package/dist/reporting/templates/enhanced-report.hbs +2812 -0
  141. package/dist/reporting/templates/html.hbs +2453 -0
  142. package/dist/utils/faker-manager.d.ts +55 -0
  143. package/dist/utils/faker-manager.js +166 -0
  144. package/dist/utils/file-manager.d.ts +33 -0
  145. package/dist/utils/file-manager.js +154 -0
  146. package/dist/utils/handlebars-manager.d.ts +42 -0
  147. package/dist/utils/handlebars-manager.js +172 -0
  148. package/dist/utils/logger.d.ts +16 -0
  149. package/dist/utils/logger.js +46 -0
  150. package/dist/utils/template.d.ts +80 -0
  151. package/dist/utils/template.js +513 -0
  152. package/dist/utils/test-output-writer.d.ts +56 -0
  153. package/dist/utils/test-output-writer.js +643 -0
  154. package/dist/utils/time.d.ts +3 -0
  155. package/dist/utils/time.js +23 -0
  156. package/dist/utils/timestamp-helper.d.ts +17 -0
  157. package/dist/utils/timestamp-helper.js +53 -0
  158. package/dist/workers/manager.d.ts +18 -0
  159. package/dist/workers/manager.js +95 -0
  160. package/dist/workers/server.d.ts +21 -0
  161. package/dist/workers/server.js +205 -0
  162. package/dist/workers/worker.d.ts +19 -0
  163. package/dist/workers/worker.js +147 -0
  164. 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();