extractVariablesSync method

  1. @override
dynamic extractVariablesSync(
  1. String script,
  2. List<String>? variableNames
)
override

Extract variables from JavaScript code (synchronous)

Implementation

@override
dynamic extractVariablesSync(String script, List<String>? variableNames) {
  var processedScript = script;
  //_log.warning('Extracting variables from script: $script');
  try {
    // Stop if too many consecutive errors
    if (_consecutiveErrors >= _maxConsecutiveErrors) {
      _log.severe(
          'Too many consecutive errors ($_consecutiveErrors), stopping execution');
      return null;
    }

    // Handle large scripts
    if (processedScript.length > maxScriptSize) {
      if (truncateLargeScripts) {
        _log.warning(
            'JavaScript script truncated from ${processedScript.length} to $maxScriptSize bytes');
        processedScript = processedScript.substring(0, maxScriptSize);
      } else {
        _log.warning(
            'JavaScript script too large: ${processedScript.length} bytes (max: $maxScriptSize)');
        _consecutiveErrors++;
        return null;
      }
    }

    // Use existing runtime (reset should be called once per QueryString.execute())
    // Check runtime health first
    if (_runtime != null && !_isRuntimeHealthy()) {
      _log.warning('Runtime unhealthy, resetting');
      _resetRuntime();
    }

    final runtime = _getRuntime();

    // Build the capture script
    final captureScript = _buildCaptureScript(processedScript, variableNames);

    // Check total script size after wrapping (allow 5x for wrapper code)
    const maxWrappedSize = 5 * 1024 * 1024; // 5MB max for wrapped script
    if (captureScript.length > maxWrappedSize) {
      _log.warning(
          'Wrapped script too large: ${captureScript.length} bytes (max: $maxWrappedSize)');
      return null;
    }

    // Execute the script with error handling
    JsEvalResult result;
    try {
      result = runtime.evaluate(captureScript);
    } catch (e) {
      _log.warning('Runtime evaluation crashed: $e');
      _consecutiveErrors++;
      // Reset runtime on crash
      _resetRuntime();
      return null;
    }

    if (result.isError) {
      _consecutiveErrors++;
      // Only log first few errors to avoid spam
      //if (_consecutiveErrors <= 3) {
      _log.warning(
          'JavaScript variable extraction error: ${result.stringResult}');
      //}
      return null;
    }

    // Success - reset error counter
    _consecutiveErrors = 0;

    // Get the JSON string result
    final jsonResult = result.stringResult;

    if (jsonResult.isEmpty ||
        jsonResult == 'undefined' ||
        jsonResult == 'null') {
      return null;
    }

    // Check result size limit
    if (jsonResult.length > maxResultSize) {
      _log.warning(
          'JavaScript result too large: ${jsonResult.length} bytes (max: $maxResultSize)');
      return null;
    }

    // Parse the JSON string
    try {
      final updatedJsonResult = jsonResult.replaceAll("undefined", '""');
      final parsed = jsonDecode(updatedJsonResult);

      // If result is empty map, return null (no variables found)
      if (parsed is Map && parsed.isEmpty) {
        return null;
      }

      // If only one variable requested (and not a wildcard), return its value directly
      if (variableNames != null &&
          variableNames.length == 1 &&
          !variableNames.first.contains('*') &&
          !variableNames.first.contains('?') &&
          parsed is Map) {
        return parsed[variableNames.first];
      }

      return parsed;
    } catch (e) {
      _log.warning('Failed to parse JSON result: $jsonResult, error: $e');
      return null;
    }
  } catch (e) {
    _log.warning('Failed to extract variables: $e');
    // Reset runtime on any error
    _resetRuntime();
    return null;
  }
}