testchimp-runner-core 0.1.21 → 0.1.22

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.
@@ -0,0 +1,754 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.PomDenormalizer = void 0;
40
+ exports.denormalizePomSteps = denormalizePomSteps;
41
+ const crypto = __importStar(require("crypto"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const parser_1 = require("@babel/parser");
45
+ const traverse_1 = __importDefault(require("@babel/traverse"));
46
+ const t = __importStar(require("@babel/types"));
47
+ const generator_1 = __importDefault(require("@babel/generator"));
48
+ const SUPPORTED_STATEMENT_TYPES = new Set([
49
+ 'ExpressionStatement',
50
+ 'VariableDeclaration',
51
+ 'EmptyStatement'
52
+ ]);
53
+ const UNSUPPORTED_STATEMENT_TYPES = new Set([
54
+ 'IfStatement',
55
+ 'ForStatement',
56
+ 'ForInStatement',
57
+ 'ForOfStatement',
58
+ 'WhileStatement',
59
+ 'DoWhileStatement',
60
+ 'SwitchStatement',
61
+ 'TryStatement',
62
+ 'ReturnStatement',
63
+ 'ThrowStatement',
64
+ 'BreakStatement',
65
+ 'ContinueStatement',
66
+ 'LabeledStatement',
67
+ 'WithStatement',
68
+ 'FunctionDeclaration',
69
+ 'ClassDeclaration'
70
+ ]);
71
+ class PomDenormalizer {
72
+ static denormalizeStatements(statements, options) {
73
+ if (!options.enabled) {
74
+ return statements;
75
+ }
76
+ if (!options.originalScript || !options.tempDir) {
77
+ if (options.logger) {
78
+ options.logger('[PomDenormalizer] Skipping - missing originalScript or tempDir', 'warn');
79
+ }
80
+ return statements;
81
+ }
82
+ const testFileDir = options.testFileDir || path.join(options.tempDir, 'tests');
83
+ const importBindings = this.collectImportBindings(options.originalScript, options.logger);
84
+ const moduleCache = new Map();
85
+ const varToClassName = this.collectVariableToClassMap(statements, options.logger);
86
+ if (options.logger) {
87
+ options.logger(`[PomDenormalizer] Starting denormalization: ${statements.length} statements, ` +
88
+ `${importBindings.size} imports, ${varToClassName.size} variable-to-class mappings`, 'log');
89
+ }
90
+ const result = [];
91
+ let nonVariableCount = 0;
92
+ let pomTargetsFound = 0;
93
+ let pomTargetsExpanded = 0;
94
+ for (const stmt of statements) {
95
+ const isVariable = stmt.isVariableDeclaration || false;
96
+ const fallbackStepId = !isVariable ? `step-${nonVariableCount + 1}` : undefined;
97
+ const baseStepId = stmt.id || fallbackStepId || '';
98
+ if (!isVariable) {
99
+ nonVariableCount += 1;
100
+ }
101
+ const resolvedTarget = this.resolvePomTarget(stmt, importBindings, varToClassName, moduleCache, testFileDir, options.logger);
102
+ if (!resolvedTarget) {
103
+ result.push(stmt);
104
+ continue;
105
+ }
106
+ pomTargetsFound++;
107
+ if (options.logger) {
108
+ options.logger(`[PomDenormalizer] Found POM target: ${resolvedTarget.callLabel}`, 'log');
109
+ }
110
+ const expandedStatements = this.expandPomTarget(resolvedTarget, stmt, baseStepId, options.logger);
111
+ if (!expandedStatements) {
112
+ if (options.logger) {
113
+ options.logger(`[PomDenormalizer] Failed to expand POM target: ${resolvedTarget.callLabel}`, 'warn');
114
+ }
115
+ result.push(stmt);
116
+ continue;
117
+ }
118
+ pomTargetsExpanded++;
119
+ result.push(...expandedStatements);
120
+ }
121
+ if (options.logger) {
122
+ const expansionCount = result.length - statements.length;
123
+ if (pomTargetsFound > 0) {
124
+ options.logger(`[PomDenormalizer] Found ${pomTargetsFound} POM target(s), expanded ${pomTargetsExpanded}, ` +
125
+ `total statements: ${statements.length} -> ${result.length} (${expansionCount > 0 ? '+' : ''}${expansionCount})`, 'log');
126
+ }
127
+ else {
128
+ options.logger(`[PomDenormalizer] No POM targets found in ${statements.length} statements`, 'log');
129
+ }
130
+ }
131
+ return result;
132
+ }
133
+ static resolvePomTarget(stmt, importBindings, varToClassName, moduleCache, testFileDir, logger) {
134
+ if (!stmt.code || stmt.isVariableDeclaration) {
135
+ return null;
136
+ }
137
+ const parsed = this.parseSingleStatement(stmt.code, logger);
138
+ if (!parsed || !t.isExpressionStatement(parsed)) {
139
+ return null;
140
+ }
141
+ const awaitExpr = parsed.expression;
142
+ if (!t.isAwaitExpression(awaitExpr)) {
143
+ return null;
144
+ }
145
+ const callExpr = awaitExpr.argument;
146
+ if (!t.isCallExpression(callExpr)) {
147
+ return null;
148
+ }
149
+ // Debug logging for POM call detection
150
+ const isPomCall = (t.isIdentifier(callExpr.callee) && importBindings.has(callExpr.callee.name)) ||
151
+ (t.isMemberExpression(callExpr.callee) &&
152
+ t.isIdentifier(callExpr.callee.object) &&
153
+ varToClassName.has(callExpr.callee.object.name));
154
+ if (isPomCall && logger) {
155
+ const callLabel = t.isIdentifier(callExpr.callee)
156
+ ? callExpr.callee.name
157
+ : t.isMemberExpression(callExpr.callee) && t.isIdentifier(callExpr.callee.object) && t.isIdentifier(callExpr.callee.property)
158
+ ? `${callExpr.callee.object.name}.${callExpr.callee.property.name}`
159
+ : 'unknown';
160
+ logger(`[PomDenormalizer] Checking POM call: ${callLabel}`, 'log');
161
+ }
162
+ if (t.isIdentifier(callExpr.callee)) {
163
+ const binding = importBindings.get(callExpr.callee.name);
164
+ if (!binding) {
165
+ return null;
166
+ }
167
+ const modulePath = this.resolvePomModulePath(binding.source, testFileDir, logger);
168
+ if (!modulePath) {
169
+ return null;
170
+ }
171
+ const moduleExports = this.getPomModuleExports(modulePath, moduleCache, logger);
172
+ if (!moduleExports) {
173
+ return null;
174
+ }
175
+ const fnNode = binding.kind === 'default'
176
+ ? moduleExports.defaultFunction
177
+ : moduleExports.functionsByExportName.get(binding.importedName);
178
+ const fnDetails = this.extractFunctionDetails(fnNode, logger);
179
+ if (!fnDetails) {
180
+ return null;
181
+ }
182
+ if (!this.isSimpleBody(fnDetails.body)) {
183
+ return null;
184
+ }
185
+ if (!this.areParamsSupported(fnDetails.params, callExpr.arguments, logger)) {
186
+ return null;
187
+ }
188
+ return {
189
+ type: 'function',
190
+ callLabel: callExpr.callee.name,
191
+ params: fnDetails.params,
192
+ body: fnDetails.body
193
+ };
194
+ }
195
+ if (t.isMemberExpression(callExpr.callee) && t.isIdentifier(callExpr.callee.object) && t.isIdentifier(callExpr.callee.property) && !callExpr.callee.computed) {
196
+ const receiverName = callExpr.callee.object.name;
197
+ const methodName = callExpr.callee.property.name;
198
+ const className = varToClassName.get(receiverName);
199
+ if (!className) {
200
+ if (logger) {
201
+ logger(`[PomDenormalizer] No class mapping found for receiver "${receiverName}" (available mappings: ${Array.from(varToClassName.keys()).join(', ') || 'none'})`, 'log');
202
+ }
203
+ return null;
204
+ }
205
+ const binding = importBindings.get(className);
206
+ if (!binding) {
207
+ return null;
208
+ }
209
+ const modulePath = this.resolvePomModulePath(binding.source, testFileDir, logger);
210
+ if (!modulePath) {
211
+ return null;
212
+ }
213
+ const moduleExports = this.getPomModuleExports(modulePath, moduleCache, logger);
214
+ if (!moduleExports) {
215
+ return null;
216
+ }
217
+ const classNode = binding.kind === 'default'
218
+ ? moduleExports.defaultClass
219
+ : moduleExports.classesByExportName.get(binding.importedName);
220
+ if (!classNode) {
221
+ return null;
222
+ }
223
+ const methodNode = this.getClassMethod(classNode, methodName);
224
+ if (!methodNode || !methodNode.body) {
225
+ return null;
226
+ }
227
+ if (!this.isSimpleBody(methodNode.body)) {
228
+ return null;
229
+ }
230
+ if (!this.areParamsSupported(methodNode.params, callExpr.arguments, logger)) {
231
+ return null;
232
+ }
233
+ return {
234
+ type: 'method',
235
+ receiverName,
236
+ callLabel: `${receiverName}.${methodName}`,
237
+ params: methodNode.params.filter(t.isIdentifier),
238
+ body: methodNode.body
239
+ };
240
+ }
241
+ return null;
242
+ }
243
+ static expandPomTarget(target, originalStatement, baseStepId, logger) {
244
+ const bodyStatements = target.body.body;
245
+ if (!bodyStatements || bodyStatements.length === 0) {
246
+ return null;
247
+ }
248
+ const clonedStatements = bodyStatements.map(stmt => t.cloneNode(stmt, true));
249
+ const substitutedStatements = this.applySubstitutions(clonedStatements, target.params, originalStatement.code, target.type === 'method' ? target.receiverName : undefined, logger);
250
+ if (!substitutedStatements) {
251
+ return null;
252
+ }
253
+ const generatedStatements = substitutedStatements.map(stmt => ({
254
+ code: (0, generator_1.default)(stmt, { comments: false }).code,
255
+ isVariableDeclaration: t.isVariableDeclaration(stmt)
256
+ }));
257
+ if (generatedStatements.length === 0) {
258
+ return null;
259
+ }
260
+ const baseDescription = originalStatement.description || `${target.callLabel}`;
261
+ const expanded = [];
262
+ let cumulativeCode = '';
263
+ for (let i = 0; i < generatedStatements.length; i++) {
264
+ const sub = generatedStatements[i];
265
+ if (!sub.code) {
266
+ continue;
267
+ }
268
+ cumulativeCode = cumulativeCode.length > 0 ? `${cumulativeCode}\n${sub.code}` : sub.code;
269
+ const subStepId = sub.isVariableDeclaration
270
+ ? undefined
271
+ : crypto.createHash('sha256').update(`${baseStepId}\n${cumulativeCode}`).digest('hex');
272
+ const description = sub.isVariableDeclaration
273
+ ? ''
274
+ : `${baseDescription} [${i + 1}/${generatedStatements.length}]`;
275
+ expanded.push({
276
+ code: sub.code,
277
+ description,
278
+ isVariableDeclaration: sub.isVariableDeclaration,
279
+ id: subStepId
280
+ });
281
+ // Log stepId generation for non-variable steps
282
+ if (logger && !sub.isVariableDeclaration && subStepId) {
283
+ const stepIdPreview = subStepId.substring(0, 16);
284
+ const codePreview = sub.code.length > 60 ? `${sub.code.substring(0, 60)}...` : sub.code;
285
+ logger(`[PomDenormalizer] Generated stepId for sub-step ${i + 1}/${generatedStatements.length}: ` +
286
+ `${stepIdPreview}... (code: ${codePreview})`, 'log');
287
+ }
288
+ }
289
+ if (expanded.length === 0) {
290
+ return null;
291
+ }
292
+ const nonVariableCount = expanded.filter(s => !s.isVariableDeclaration).length;
293
+ const stepIdCount = expanded.filter(s => s.id).length;
294
+ if (logger) {
295
+ logger(`[PomDenormalizer] Expanded ${target.callLabel} into ${expanded.length} sub-step(s) ` +
296
+ `(${nonVariableCount} non-variable, ${stepIdCount} with stepIds)`, 'log');
297
+ }
298
+ return expanded;
299
+ }
300
+ static applySubstitutions(statements, params, originalCallCode, receiverName, logger) {
301
+ const callParsed = this.parseSingleStatement(originalCallCode, logger);
302
+ if (!callParsed || !t.isExpressionStatement(callParsed)) {
303
+ return null;
304
+ }
305
+ const awaitExpr = callParsed.expression;
306
+ if (!t.isAwaitExpression(awaitExpr) || !t.isCallExpression(awaitExpr.argument)) {
307
+ return null;
308
+ }
309
+ const args = awaitExpr.argument.arguments;
310
+ if (args.length < params.length) {
311
+ return null;
312
+ }
313
+ const paramMap = new Map();
314
+ params.forEach((param, index) => {
315
+ const arg = args[index];
316
+ if (t.isExpression(arg)) {
317
+ paramMap.set(param.name, arg);
318
+ }
319
+ });
320
+ const wrapper = t.functionExpression(null, params.map(param => t.cloneNode(param, true)), t.blockStatement(statements));
321
+ // Wrap in File (which contains Program) for proper scope handling (Babel requires File for traversal)
322
+ const program = t.program([t.variableDeclaration('const', [
323
+ t.variableDeclarator(t.identifier('_wrapper'), wrapper)
324
+ ])], [], 'module');
325
+ const file = t.file(program, [], null);
326
+ const paramBindings = new Map();
327
+ // First traversal: collect parameter bindings
328
+ (0, traverse_1.default)(file, {
329
+ Function(path) {
330
+ // Find our wrapper function within the Program
331
+ if (path.node === wrapper) {
332
+ for (const param of params) {
333
+ const binding = path.scope.getBinding(param.name);
334
+ if (binding && binding.kind === 'param') {
335
+ paramBindings.set(param.name, binding.identifier);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ });
341
+ // Second traversal: perform substitutions
342
+ (0, traverse_1.default)(file, {
343
+ ThisExpression(path) {
344
+ if (receiverName) {
345
+ path.replaceWith(t.identifier(receiverName));
346
+ path.skip(); // Skip traversing into the replacement to avoid infinite recursion
347
+ }
348
+ },
349
+ Identifier(path) {
350
+ if (!path.isReferencedIdentifier()) {
351
+ return;
352
+ }
353
+ const binding = path.scope.getBinding(path.node.name);
354
+ if (binding && binding.kind === 'param' && paramBindings.get(path.node.name) === binding.identifier) {
355
+ const replacement = paramMap.get(path.node.name);
356
+ if (replacement) {
357
+ path.replaceWith(t.cloneNode(replacement, true));
358
+ path.skip(); // Skip traversing into the replacement to avoid infinite recursion
359
+ }
360
+ }
361
+ }
362
+ });
363
+ // Extract modified statements (wrapper.body is mutated in place during traversal)
364
+ return wrapper.body.body;
365
+ }
366
+ static collectVariableToClassMap(statements, logger) {
367
+ const map = new Map();
368
+ let parsedCount = 0;
369
+ let variableDeclarationCount = 0;
370
+ let assignmentCount = 0;
371
+ for (const stmt of statements) {
372
+ if (!stmt.code) {
373
+ continue;
374
+ }
375
+ // Debug: Log statements that might contain variable assignments/declarations
376
+ if (logger && (stmt.code.includes('=') || stmt.code.includes('const ') || stmt.code.includes('let ') || stmt.code.includes('var '))) {
377
+ const codePreview = stmt.code.length > 100 ? `${stmt.code.substring(0, 100)}...` : stmt.code;
378
+ logger(`[PomDenormalizer] Processing variable-related statement: ${codePreview}`, 'log');
379
+ }
380
+ const parsed = this.parseSingleStatement(stmt.code, logger);
381
+ if (!parsed) {
382
+ if (logger) {
383
+ logger(`[PomDenormalizer] Failed to parse statement: ${stmt.code.substring(0, 50)}...`, 'log');
384
+ }
385
+ continue;
386
+ }
387
+ parsedCount++;
388
+ if (t.isVariableDeclaration(parsed)) {
389
+ variableDeclarationCount++;
390
+ for (const declarator of parsed.declarations) {
391
+ if (t.isIdentifier(declarator.id)) {
392
+ if (declarator.init && t.isNewExpression(declarator.init) && t.isIdentifier(declarator.init.callee)) {
393
+ map.set(declarator.id.name, declarator.init.callee.name);
394
+ if (logger) {
395
+ logger(`[PomDenormalizer] Found variable-to-class mapping: ${declarator.id.name} -> ${declarator.init.callee.name}`, 'log');
396
+ }
397
+ }
398
+ else if (declarator.init && logger) {
399
+ // Log why this variable declaration didn't match
400
+ const initType = declarator.init.type;
401
+ if (!t.isNewExpression(declarator.init)) {
402
+ logger(`[PomDenormalizer] Variable ${declarator.id.name} init is not NewExpression (type: ${initType})`, 'log');
403
+ }
404
+ else if (!t.isIdentifier(declarator.init.callee)) {
405
+ logger(`[PomDenormalizer] Variable ${declarator.id.name} NewExpression callee is not Identifier`, 'log');
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ else if (t.isExpressionStatement(parsed) && t.isAssignmentExpression(parsed.expression)) {
412
+ assignmentCount++;
413
+ const assignment = parsed.expression;
414
+ if (t.isIdentifier(assignment.left)) {
415
+ if (t.isNewExpression(assignment.right) && t.isIdentifier(assignment.right.callee)) {
416
+ map.set(assignment.left.name, assignment.right.callee.name);
417
+ if (logger) {
418
+ logger(`[PomDenormalizer] Found variable-to-class mapping: ${assignment.left.name} -> ${assignment.right.callee.name}`, 'log');
419
+ }
420
+ }
421
+ else if (logger) {
422
+ const rightType = assignment.right.type;
423
+ if (!t.isNewExpression(assignment.right)) {
424
+ logger(`[PomDenormalizer] Assignment ${assignment.left.name} right is not NewExpression (type: ${rightType})`, 'log');
425
+ }
426
+ }
427
+ }
428
+ }
429
+ }
430
+ // Add common POM object mappings based on naming conventions
431
+ // This handles cases where POM objects are passed as test parameters or fixtures
432
+ const commonPomMappings = [
433
+ ['signInPage', 'SignInPage'],
434
+ ['signinpage', 'SignInPage'],
435
+ ['SignInPage', 'SignInPage'],
436
+ ['smartTestsPage', 'SmartTestsPage'],
437
+ ['smarttestspage', 'SmartTestsPage'],
438
+ ['SmartTestsPage', 'SmartTestsPage'],
439
+ ['loginPage', 'LoginPage'],
440
+ ['loginpage', 'LoginPage'],
441
+ ['LoginPage', 'LoginPage']
442
+ ];
443
+ for (const [varName, className] of commonPomMappings) {
444
+ if (!map.has(varName)) {
445
+ map.set(varName, className);
446
+ if (logger) {
447
+ logger(`[PomDenormalizer] Added common POM mapping: ${varName} -> ${className}`, 'log');
448
+ }
449
+ }
450
+ }
451
+ if (logger) {
452
+ logger(`[PomDenormalizer] Variable-to-class mapping: parsed ${parsedCount} statements ` +
453
+ `(${variableDeclarationCount} variable declarations, ${assignmentCount} assignments), ` +
454
+ `found ${map.size} mappings (including ${commonPomMappings.length} common POM mappings)`, 'log');
455
+ }
456
+ return map;
457
+ }
458
+ static collectImportBindings(script, logger) {
459
+ const bindings = new Map();
460
+ try {
461
+ const ast = (0, parser_1.parse)(script, {
462
+ sourceType: 'module',
463
+ plugins: ['typescript', 'classProperties', 'decorators-legacy'],
464
+ allowImportExportEverywhere: true
465
+ });
466
+ (0, traverse_1.default)(ast, {
467
+ ImportDeclaration(path) {
468
+ const source = path.node.source.value;
469
+ if (typeof source !== 'string' || !source.startsWith('.')) {
470
+ return;
471
+ }
472
+ for (const specifier of path.node.specifiers) {
473
+ if (t.isImportDefaultSpecifier(specifier)) {
474
+ bindings.set(specifier.local.name, {
475
+ source,
476
+ importedName: 'default',
477
+ kind: 'default'
478
+ });
479
+ }
480
+ else if (t.isImportSpecifier(specifier)) {
481
+ const importedName = t.isIdentifier(specifier.imported)
482
+ ? specifier.imported.name
483
+ : specifier.imported.value;
484
+ bindings.set(specifier.local.name, {
485
+ source,
486
+ importedName,
487
+ kind: 'named'
488
+ });
489
+ }
490
+ }
491
+ }
492
+ });
493
+ }
494
+ catch (error) {
495
+ if (logger) {
496
+ logger(`[PomDenormalizer] Failed to parse imports: ${error.message}`, 'warn');
497
+ }
498
+ }
499
+ return bindings;
500
+ }
501
+ static resolvePomModulePath(source, testFileDir, logger) {
502
+ const resolvedPath = path.resolve(testFileDir, source);
503
+ const candidates = [
504
+ `${resolvedPath}.page.ts`,
505
+ `${resolvedPath}.page.js`,
506
+ path.join(resolvedPath, 'index.page.ts'),
507
+ path.join(resolvedPath, 'index.page.js')
508
+ ];
509
+ for (const candidate of candidates) {
510
+ if (fs.existsSync(candidate)) {
511
+ return candidate;
512
+ }
513
+ }
514
+ if (logger) {
515
+ logger(`[PomDenormalizer] No POM module found for ${source} (resolved base: ${resolvedPath})`, 'warn');
516
+ }
517
+ return null;
518
+ }
519
+ static getPomModuleExports(modulePath, cache, logger) {
520
+ if (cache.has(modulePath)) {
521
+ return cache.get(modulePath);
522
+ }
523
+ try {
524
+ const source = fs.readFileSync(modulePath, 'utf8');
525
+ const ast = (0, parser_1.parse)(source, {
526
+ sourceType: 'module',
527
+ plugins: ['typescript', 'classProperties', 'decorators-legacy'],
528
+ allowImportExportEverywhere: true
529
+ });
530
+ const declaredClasses = new Map();
531
+ const declaredFunctions = new Map();
532
+ const exports = {
533
+ classesByExportName: new Map(),
534
+ functionsByExportName: new Map()
535
+ };
536
+ (0, traverse_1.default)(ast, {
537
+ ClassDeclaration(path) {
538
+ if (path.node.id) {
539
+ declaredClasses.set(path.node.id.name, path.node);
540
+ }
541
+ },
542
+ FunctionDeclaration(path) {
543
+ if (path.node.id) {
544
+ declaredFunctions.set(path.node.id.name, path.node);
545
+ }
546
+ },
547
+ VariableDeclarator(path) {
548
+ if (!t.isIdentifier(path.node.id)) {
549
+ return;
550
+ }
551
+ const init = path.node.init;
552
+ if (t.isFunctionExpression(init) || (t.isArrowFunctionExpression(init) && t.isBlockStatement(init.body))) {
553
+ declaredFunctions.set(path.node.id.name, init);
554
+ }
555
+ },
556
+ ExportNamedDeclaration(path) {
557
+ if (path.node.declaration) {
558
+ const decl = path.node.declaration;
559
+ if (t.isClassDeclaration(decl) && decl.id) {
560
+ exports.classesByExportName.set(decl.id.name, decl);
561
+ }
562
+ else if (t.isFunctionDeclaration(decl) && decl.id) {
563
+ exports.functionsByExportName.set(decl.id.name, decl);
564
+ }
565
+ else if (t.isVariableDeclaration(decl)) {
566
+ for (const declarator of decl.declarations) {
567
+ if (!t.isIdentifier(declarator.id)) {
568
+ continue;
569
+ }
570
+ const init = declarator.init;
571
+ if (t.isFunctionExpression(init) || (t.isArrowFunctionExpression(init) && t.isBlockStatement(init.body))) {
572
+ exports.functionsByExportName.set(declarator.id.name, init);
573
+ }
574
+ }
575
+ }
576
+ }
577
+ if (path.node.specifiers && path.node.specifiers.length > 0) {
578
+ for (const specifier of path.node.specifiers) {
579
+ if (!t.isExportSpecifier(specifier)) {
580
+ continue;
581
+ }
582
+ if (!t.isIdentifier(specifier.local) || !t.isIdentifier(specifier.exported)) {
583
+ continue;
584
+ }
585
+ const localName = specifier.local.name;
586
+ const exportedName = specifier.exported.name;
587
+ if (!localName || !exportedName) {
588
+ continue;
589
+ }
590
+ const classNode = declaredClasses.get(localName);
591
+ const fnNode = declaredFunctions.get(localName);
592
+ if (classNode) {
593
+ exports.classesByExportName.set(exportedName, classNode);
594
+ }
595
+ if (fnNode) {
596
+ exports.functionsByExportName.set(exportedName, fnNode);
597
+ }
598
+ }
599
+ }
600
+ },
601
+ ExportDefaultDeclaration(path) {
602
+ const decl = path.node.declaration;
603
+ if (t.isClassDeclaration(decl)) {
604
+ exports.defaultClass = decl;
605
+ }
606
+ else if (t.isFunctionDeclaration(decl)) {
607
+ exports.defaultFunction = decl;
608
+ }
609
+ else if (t.isFunctionExpression(decl) || (t.isArrowFunctionExpression(decl) && t.isBlockStatement(decl.body))) {
610
+ exports.defaultFunction = decl;
611
+ }
612
+ }
613
+ });
614
+ cache.set(modulePath, exports);
615
+ return exports;
616
+ }
617
+ catch (error) {
618
+ if (logger) {
619
+ logger(`[PomDenormalizer] Failed to parse module ${modulePath}: ${error.message}`, 'warn');
620
+ }
621
+ return null;
622
+ }
623
+ }
624
+ static extractFunctionDetails(node, logger) {
625
+ if (!node) {
626
+ return null;
627
+ }
628
+ if (t.isArrowFunctionExpression(node)) {
629
+ if (!t.isBlockStatement(node.body)) {
630
+ if (logger) {
631
+ logger('[PomDenormalizer] Skipping arrow function with expression body', 'warn');
632
+ }
633
+ return null;
634
+ }
635
+ if (!node.params.every(t.isIdentifier)) {
636
+ if (logger) {
637
+ logger('[PomDenormalizer] Skipping arrow function with non-identifier params', 'warn');
638
+ }
639
+ return null;
640
+ }
641
+ const params = node.params;
642
+ return { params, body: node.body };
643
+ }
644
+ if (t.isFunctionDeclaration(node) || t.isFunctionExpression(node)) {
645
+ if (!node.params.every(t.isIdentifier)) {
646
+ if (logger) {
647
+ logger('[PomDenormalizer] Skipping function with non-identifier params', 'warn');
648
+ }
649
+ return null;
650
+ }
651
+ const params = node.params;
652
+ return { params, body: node.body };
653
+ }
654
+ return null;
655
+ }
656
+ static areParamsSupported(params, args, logger) {
657
+ if (params.length === 0) {
658
+ return true;
659
+ }
660
+ for (const param of params) {
661
+ if (!t.isIdentifier(param)) {
662
+ if (logger) {
663
+ logger('[PomDenormalizer] Skipping - unsupported parameter pattern', 'warn');
664
+ }
665
+ return false;
666
+ }
667
+ }
668
+ if (args.length < params.length) {
669
+ if (logger) {
670
+ logger('[PomDenormalizer] Skipping - missing call arguments', 'warn');
671
+ }
672
+ return false;
673
+ }
674
+ for (let i = 0; i < params.length; i++) {
675
+ const arg = args[i];
676
+ if (!t.isExpression(arg)) {
677
+ if (logger) {
678
+ logger('[PomDenormalizer] Skipping - unsupported call argument type', 'warn');
679
+ }
680
+ return false;
681
+ }
682
+ }
683
+ return true;
684
+ }
685
+ static getClassMethod(classNode, methodName) {
686
+ for (const member of classNode.body.body) {
687
+ if (t.isClassMethod(member) && t.isIdentifier(member.key) && member.key.name === methodName) {
688
+ return member;
689
+ }
690
+ }
691
+ return null;
692
+ }
693
+ static isSimpleBody(body) {
694
+ for (const stmt of body.body) {
695
+ if (UNSUPPORTED_STATEMENT_TYPES.has(stmt.type)) {
696
+ return false;
697
+ }
698
+ if (!SUPPORTED_STATEMENT_TYPES.has(stmt.type)) {
699
+ return false;
700
+ }
701
+ if (t.isVariableDeclaration(stmt)) {
702
+ for (const decl of stmt.declarations) {
703
+ if (!t.isIdentifier(decl.id)) {
704
+ return false;
705
+ }
706
+ }
707
+ }
708
+ }
709
+ return true;
710
+ }
711
+ static parseSingleStatement(code, logger) {
712
+ try {
713
+ const ast = (0, parser_1.parse)(code, {
714
+ sourceType: 'module',
715
+ plugins: ['typescript', 'classProperties', 'decorators-legacy'],
716
+ allowAwaitOutsideFunction: true,
717
+ allowReturnOutsideFunction: true,
718
+ allowImportExportEverywhere: true
719
+ });
720
+ if (ast.program.body.length === 0) {
721
+ return null;
722
+ }
723
+ const first = ast.program.body[0];
724
+ return t.isStatement(first) ? first : null;
725
+ }
726
+ catch (error) {
727
+ if (logger) {
728
+ logger(`[PomDenormalizer] Failed to parse statement: ${error.message}`, 'warn');
729
+ }
730
+ return null;
731
+ }
732
+ }
733
+ /**
734
+ * Public function to denormalize POM steps.
735
+ * Exported for use by external consumers (e.g., scriptservice).
736
+ */
737
+ static denormalizePomSteps(steps, options) {
738
+ return this.denormalizeStatements(steps, {
739
+ enabled: true,
740
+ originalScript: options.originalScript,
741
+ tempDir: options.tempDir,
742
+ testFileDir: options.testFileDir,
743
+ logger: options.logger
744
+ });
745
+ }
746
+ }
747
+ exports.PomDenormalizer = PomDenormalizer;
748
+ /**
749
+ * Export the public function for external use.
750
+ */
751
+ function denormalizePomSteps(steps, options) {
752
+ return PomDenormalizer.denormalizePomSteps(steps, options);
753
+ }
754
+ //# sourceMappingURL=pom-denormalizer.js.map