UNPKG

@mondaydotcomorg/atp-compiler

Version:

Production-ready compiler for transforming async iteration patterns into resumable operations with checkpoint-based state management

244 lines 9.2 kB
import * as t from '@babel/types'; import { isPausableCallExpression } from './utils.js'; export class BatchOptimizer { arrayMethodsWithEarlyExit = ['find', 'some', 'every']; canBatchArrayMethod(callback) { if (!callback.async) { return { canBatch: false, reason: 'Not async' }; } const body = callback.body; if (!t.isBlockStatement(body)) { if (t.isAwaitExpression(body)) { if (this.isDirectPausableCall(body.argument)) { return { canBatch: true, llmCallPattern: 'single', hasConditionals: false }; } } return { canBatch: false, reason: 'Non-block body without direct call' }; } const statements = body.body; if (statements.length === 0) { return { canBatch: false, reason: 'Empty body' }; } let hasConditionals = false; let hasLoops = false; let hasTryCatch = false; for (const stmt of statements) { if (t.isIfStatement(stmt) || t.isSwitchStatement(stmt)) { hasConditionals = true; } if (t.isTryStatement(stmt)) { hasTryCatch = true; } if (t.isForStatement(stmt) || t.isForOfStatement(stmt) || t.isForInStatement(stmt) || t.isWhileStatement(stmt) || t.isDoWhileStatement(stmt)) { hasLoops = true; } if (t.isBreakStatement(stmt) || t.isContinueStatement(stmt)) { return { canBatch: false, reason: 'Contains break/continue' }; } if (t.isReturnStatement(stmt) && stmt !== statements[statements.length - 1]) { return { canBatch: false, reason: 'Early return' }; } } if (hasLoops) { return { canBatch: false, reason: 'Contains loops', hasLoops: true }; } if (hasTryCatch) { return { canBatch: false, reason: 'Contains try-catch' }; } const pausableCalls = this.countPausableCalls(body); if (pausableCalls === 0) { return { canBatch: false, reason: 'No pausable calls' }; } if (pausableCalls > 1) { return { canBatch: false, reason: 'Multiple pausable calls', llmCallPattern: 'multiple' }; } if (hasConditionals) { return { canBatch: true, llmCallPattern: 'conditional', hasConditionals: true, reason: 'Simple conditional - can batch but consider array size', }; } return { canBatch: true, llmCallPattern: 'single', hasConditionals: false }; } /** * Smart decision: Should we batch based on array size and method type? */ makeSmartBatchDecision(methodName, batchResult, arrayNode, threshold = 10) { if (!batchResult.canBatch) { return { shouldBatch: false, reason: 'Complex callback - use sequential', strategy: 'never-batch', }; } if (!batchResult.hasConditionals) { return { shouldBatch: true, reason: 'Simple callback - batching is faster', strategy: 'always-batch', }; } const hasEarlyExitBenefit = this.arrayMethodsWithEarlyExit.includes(methodName); if (!hasEarlyExitBenefit) { const arraySize = this.estimateArraySize(arrayNode); if (arraySize !== null && arraySize < threshold) { return { shouldBatch: true, reason: `Small array (${arraySize} < ${threshold}) - batch despite conditionals`, strategy: 'size-dependent', }; } return { shouldBatch: false, reason: 'Conditionals + large/unknown array - sequential for safety', strategy: 'size-dependent', }; } const arraySize = this.estimateArraySize(arrayNode); if (arraySize !== null && arraySize < threshold) { return { shouldBatch: true, reason: `Small array (${arraySize} < ${threshold}) - batch for speed`, strategy: 'size-dependent', }; } if (arraySize !== null && arraySize >= threshold) { return { shouldBatch: false, reason: `Large array (${arraySize} >= ${threshold}) + conditionals - sequential for early-exit savings`, strategy: 'size-dependent', }; } if (t.isArrayExpression(arrayNode)) { return { shouldBatch: true, reason: 'Array literal (likely small) - batch', strategy: 'size-dependent', }; } return { shouldBatch: false, reason: 'Unknown array size + conditionals - sequential for safety', strategy: 'size-dependent', }; } estimateArraySize(arrayNode) { if (t.isArrayExpression(arrayNode)) { return arrayNode.elements.length; } return null; } canBatchForOfLoop(loopNode) { const body = loopNode.body; if (!t.isBlockStatement(body)) { return { canBatch: false, reason: 'Loop body not a block' }; } const statements = body.body; if (statements.length === 0) { return { canBatch: false, reason: 'Empty loop body' }; } const hasBreakOrContinue = this.containsBreakOrContinue(body); if (hasBreakOrContinue) { return { canBatch: false, reason: 'Contains break/continue' }; } let hasConditionals = false; for (const stmt of statements) { if (t.isIfStatement(stmt) || t.isSwitchStatement(stmt)) { hasConditionals = true; } if (t.isForStatement(stmt) || t.isForOfStatement(stmt) || t.isForInStatement(stmt) || t.isWhileStatement(stmt) || t.isDoWhileStatement(stmt)) { return { canBatch: false, reason: 'Contains nested loops', hasLoops: true }; } } const pausableCalls = this.countPausableCalls(body); if (pausableCalls === 0) { return { canBatch: false, reason: 'No pausable calls' }; } if (pausableCalls > 1) { return { canBatch: false, reason: 'Multiple pausable calls', llmCallPattern: 'multiple' }; } if (hasConditionals) { return { canBatch: true, llmCallPattern: 'conditional', hasConditionals: true, reason: 'Simple conditional - can batch but consider array size', }; } return { canBatch: true, llmCallPattern: 'single', hasConditionals: false }; } containsBreakOrContinue(node) { let found = false; const visit = (n) => { if (found) return; if (t.isBreakStatement(n) || t.isContinueStatement(n)) { found = true; return; } if (t.isForStatement(n) || t.isForOfStatement(n) || t.isForInStatement(n) || t.isWhileStatement(n) || t.isDoWhileStatement(n)) { return; } Object.keys(n).forEach((key) => { const value = n[key]; if (Array.isArray(value)) { value.forEach((item) => { if (item && typeof item === 'object' && item.type) { visit(item); } }); } else if (value && typeof value === 'object' && value.type) { visit(value); } }); }; visit(node); return found; } isDirectPausableCall(node) { if (!t.isCallExpression(node)) { return false; } return isPausableCallExpression(node); } countPausableCalls(body) { let count = 0; const visit = (node) => { if (t.isAwaitExpression(node) && this.isDirectPausableCall(node.argument)) { count++; return; } Object.keys(node).forEach((key) => { const value = node[key]; if (Array.isArray(value)) { value.forEach((item) => { if (item && typeof item === 'object' && item.type) { visit(item); } }); } else if (value && typeof value === 'object' && value.type) { visit(value); } }); }; visit(body); return count; } } //# sourceMappingURL=batch-optimizer.js.map