resolveFieldValue function exported ✓ 100.0%

Last updated: 2026-03-04T23:21:38.413Z

Metrics

LOC: 218 Complexity: 40 Params: 3 Coverage: 100.0% (74/74 lines, 0x executed)

Signature

resolveFieldValue( field: FormField, url: string, aiGenerateFn?: (field: FormField) => Promise<string>, forceAIFirst = false, aiTimeoutMs = DEFAULT_AI_TIMEOUT_MS, ): : Promise<GenerationResult>

Summary

Resolves the value for a single field, using this priority: 1. Field-specific rule (always wins — even over forceAIFirst) 2. (optional) AI first — when forceAIFirst is true and no rule exists 3. Default generator based on detected field type 4. AI as last resort (only when default generator returns empty, e.g. select fields)

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'resolveFieldValue' has cyclomatic complexity 40 (max 10)
  • [warning] max-lines: 'resolveFieldValue' has 218 lines (max 80)

Source Code

export async function resolveFieldValue(
  field: FormField,
  url: string,
  aiGenerateFn?: (field: FormField) => Promise<string>,
  forceAIFirst = false,
  aiTimeoutMs = DEFAULT_AI_TIMEOUT_MS,
): Promise<GenerationResult> {
  const selector = field.selector;

  const fieldDesc = `selector="${selector}" label="${field.label ?? ""}" type="${field.fieldType}"`;
  log.debug(
    `Resolvendo campo: ${fieldDesc}${forceAIFirst ? " [forceAIFirst=true]" : ""}`,
  );

  // 1. Check field rules first — rules always take priority over AI/TF
  const rules = await getRulesForUrl(url);
  const matchingRule = findMatchingRule(rules, field);

  if (matchingRule) {
    if (matchingRule.fixedValue) {
      return {
        fieldSelector: selector,
        value: matchingRule.fixedValue,
        source: "rule",
      };
    }

    // Handle select fields with explicit option index
    if (
      field.element instanceof HTMLSelectElement &&
      matchingRule.selectOptionIndex !== undefined
    ) {
      const options = Array.from(field.element.options).filter((o) => o.value);
      if (matchingRule.selectOptionIndex === 0) {
        // auto — pick random non-empty option
        if (options.length > 0) {
          const random = options[Math.floor(Math.random() * options.length)];
          return {
            fieldSelector: selector,
            value: random.value,
            source: "rule",
          };
        }
      } else {
        // pick by 1-based index
        const opt = field.element.options[matchingRule.selectOptionIndex - 1];
        if (opt) {
          return { fieldSelector: selector, value: opt.value, source: "rule" };
        }
      }
    }

    // If the rule specifies a generator type
    if (
      matchingRule.generator !== "auto" &&
      matchingRule.generator !== "ai" &&
      matchingRule.generator !== "tensorflow"
    ) {
      const ruleGenerator = matchingRule.generator as FieldType;
      if (DATE_FIELD_TYPES.has(ruleGenerator)) {
        const value = generateDateForField(ruleGenerator, field);
        return { fieldSelector: selector, value, source: "generator" };
      }
      const value = generateWithConstraints(
        () => generate(ruleGenerator, matchingRule.generatorParams),
        {
          element: field.element,
          requireValidity: false,
        },
      );
      return { fieldSelector: selector, value, source: "generator" };
    }

    // If the rule says to use AI
    if (matchingRule.generator === "ai" && aiGenerateFn) {
      try {
        const aiValue = await callAiWithTimeout(
          aiGenerateFn,
          field,
          "rule:ai",
          aiTimeoutMs,
        );
        const value = adaptGeneratedValue(aiValue, {
          element: field.element,
          requireValidity: false,
        });
        return { fieldSelector: selector, value, source: "ai" };
      } catch (err) {
        log.warn(`AI (rule) falhou:`, err);
      }
    }

    // generator === "auto": respect the fieldType the user explicitly set in the rule.
    // This is the fix for when the user changes the field type in the editor but keeps
    // "auto" as the generator — the rule's fieldType must drive generation, not the
    // TF-detected field.fieldType.
    if (
      matchingRule.generator === "auto" &&
      matchingRule.fieldType !== "unknown"
    ) {
      const ruleType = matchingRule.fieldType;
      if (DATE_FIELD_TYPES.has(ruleType)) {
        const value = generateDateForField(ruleType, field);
        return { fieldSelector: selector, value, source: "generator" };
      }
      const value = generateWithConstraints(
        () => generate(ruleType, matchingRule.generatorParams),
        { element: field.element, requireValidity: false },
      );
      if (value) {
        return { fieldSelector: selector, value, source: "generator" };
      }
    }
    // generator === "tensorflow": fall through — TF already classified the field,
    // field.fieldType reflects that result. forceAIFirst is intentionally skipped.
  }

  // 2. AI first — only when no matching rule exists and forceAIFirst is enabled
  if (
    forceAIFirst &&
    !matchingRule &&
    aiGenerateFn &&
    !GENERATOR_ONLY_TYPES.has(field.fieldType)
  ) {
    try {
      const aiValue = await callAiWithTimeout(
        aiGenerateFn,
        field,
        "forceAIFirst",
        aiTimeoutMs,
      );
      const value = adaptGeneratedValue(aiValue, {
        element: field.element,
        requireValidity: true,
      });
      if (value) {
        return { fieldSelector: selector, value, source: "ai" };
      }
    } catch (err) {
      log.warn(`AI (forceAIFirst) falhou:`, err);
      // Fall through to default generator
    }
  }

  // 3. Default generator based on detected field type
  const effectiveType = getEffectiveFieldType(field);
  if (DATE_FIELD_TYPES.has(effectiveType)) {
    const value = generateDateForField(effectiveType, field);
    log.debug(`Gerador de data (${effectiveType}, detectado): "${value}"`);
    return { fieldSelector: selector, value, source: "generator" };
  }
  const value = generateWithConstraints(() => generate(effectiveType), {
    element: field.element,
    requireValidity: true,
  });
  if (value) {
    log.debug(`Gerador padrão (${effectiveType}): "${value}"`);
    return { fieldSelector: selector, value, source: "generator" };
  }

  // 5.5 For <select> elements: pick a random valid option directly.
  // AI cannot know which options are available in the DOM, so we must handle this ourselves.
  if (field.element instanceof HTMLSelectElement) {
    const validOptions = Array.from(field.element.options).filter(
      (opt) => opt.value,
    );
    if (validOptions.length > 0) {
      const random =
        validOptions[Math.floor(Math.random() * validOptions.length)];
      log.debug(
        `Select aleatório: "${random.value}" (${validOptions.length} opções disponíveis)`,
      );
      return {
        fieldSelector: selector,
        value: random.value,
        source: "generator",
      };
    }
    // No valid options — return empty, no point calling AI
    log.warn(`Select sem opções válidas para: ${fieldDesc}`);
    return { fieldSelector: selector, value: "", source: "generator" };
  }

  // 5.6 For checkbox/radio: the value is fixed (true/false), AI adds no value here.
  if (
    field.element instanceof HTMLInputElement &&
    (field.element.type === "checkbox" || field.element.type === "radio")
  ) {
    return { fieldSelector: selector, value: "true", source: "generator" };
  }

  // 6. AI as last resort — only for free-text fields where the generator returned empty
  if (aiGenerateFn && !GENERATOR_ONLY_TYPES.has(field.fieldType)) {
    log.info(
      `Gerador padrão vazio — tentando AI como último recurso para: ${fieldDesc}`,
    );
    try {
      const aiValue = await callAiWithTimeout(
        aiGenerateFn,
        field,
        "último recurso",
        aiTimeoutMs,
      );
      const adaptedAiValue = adaptGeneratedValue(aiValue, {
        element: field.element,
        requireValidity: true,
      });
      if (adaptedAiValue) {
        return { fieldSelector: selector, value: adaptedAiValue, source: "ai" };
      }
      log.warn(`AI (último recurso) retornou vazio para: ${fieldDesc}`);
    } catch (err) {
      log.warn(`AI (último recurso) falhou para: ${fieldDesc}`, err);
    }
  }

  return { fieldSelector: selector, value: value || "", source: "generator" };
}

Dependencies (Outgoing)

graph LR resolveFieldValue["resolveFieldValue"] findMatchingRule["findMatchingRule"] generateDateForField["generateDateForField"] callAiWithTimeout["callAiWithTimeout"] getEffectiveFieldType["getEffectiveFieldType"] resolveFieldValue -->|calls| findMatchingRule resolveFieldValue -->|calls| generateDateForField resolveFieldValue -->|calls| callAiWithTimeout resolveFieldValue -->|calls| getEffectiveFieldType style resolveFieldValue fill:#dbeafe,stroke:#2563eb,stroke-width:2px click resolveFieldValue "b0ea06e6c355d586.html" click findMatchingRule "9456e727716b3217.html" click generateDateForField "a214011e5ced5989.html" click callAiWithTimeout "4ef7f4ef5ff6b7b3.html" click getEffectiveFieldType "7eda31b9cfeb392f.html"

Impact (Incoming)

graph LR resolveFieldValue["resolveFieldValue"] setNativeValue["setNativeValue"] createField["createField"] setNativeValue -->|uses| resolveFieldValue createField -->|uses| resolveFieldValue style resolveFieldValue fill:#dbeafe,stroke:#2563eb,stroke-width:2px click resolveFieldValue "b0ea06e6c355d586.html" click setNativeValue "334bd99609d7c37c.html" click createField "13879e2aa13736be.html"
SourceType
setNativeValue uses
createField uses