src/lib/messaging/light-validators.ts

Total Symbols
7
Lines of Code
150
Avg Complexity
6.3
Avg Coverage
97.1%

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'parseSavedFormPayload' has cyclomatic complexity 13 (max 10)

Symbols by Kind

function 7

All Symbols

Name Kind Visibility Status Lines Signature
parseIncomingMessage function exported- 15-22 parseIncomingMessage( input: unknown, ): : { type: string; payload?: unknown } | null
parseStringPayload function exported- 29-31 parseStringPayload(input: unknown): : string | null
parseStartWatchingPayload function exported- 38-67 parseStartWatchingPayload( input: unknown, ): : { autoRefill?: boolean; debounceMs?: number; shadowDOM?: boolean } | null
parseSavedFormPayload function exported- 74-106 parseSavedFormPayload(input: unknown): : SavedForm | null
parseApplyTemplatePayload function exported- 113-115 parseApplyTemplatePayload(input: unknown): : SavedForm | null
parseExportE2EPayload function exported- 122-129 parseExportE2EPayload( input: unknown, ): : { framework: string } | null
parseExportRecordingPayload function exported- 136-149 parseExportRecordingPayload( input: unknown, ): : { framework: string; testName?: string } | null

Full Source

/**
 * Lightweight message validators using only `typeof` checks.
 *
 * Designed for the content script hot path where Zod overhead
 * is unacceptable. For strict validation, use `validators.ts`.
 */

import type { SavedForm } from "@/types";

/**
 * Lightweight message envelope parser — `typeof` checks only.
 * @param input - Raw message from `chrome.runtime.onMessage`
 * @returns Parsed `{ type, payload }` or `null` if invalid
 */
export function parseIncomingMessage(
  input: unknown,
): { type: string; payload?: unknown } | null {
  if (!input || typeof input !== "object") return null;
  const value = input as { type?: unknown; payload?: unknown };
  if (typeof value.type !== "string" || !value.type) return null;
  return { type: value.type, payload: value.payload };
}

/**
 * Lightweight non-empty string parser.
 * @param input - Raw payload value
 * @returns The string, or `null` if not a valid non-empty string
 */
export function parseStringPayload(input: unknown): string | null {
  return typeof input === "string" && input.length > 0 ? input : null;
}

/**
 * Lightweight start-watching payload parser.
 * @param input - Raw payload (may be `undefined`)
 * @returns Object with optional `autoRefill` flag, or `null` if invalid
 */
export function parseStartWatchingPayload(
  input: unknown,
): { autoRefill?: boolean; debounceMs?: number; shadowDOM?: boolean } | null {
  if (input === undefined) return {};
  if (!input || typeof input !== "object") return null;
  const payload = input as Record<string, unknown>;
  if (
    payload.autoRefill !== undefined &&
    typeof payload.autoRefill !== "boolean"
  ) {
    return null;
  }
  if (
    payload.debounceMs !== undefined &&
    typeof payload.debounceMs !== "number"
  ) {
    return null;
  }
  if (
    payload.shadowDOM !== undefined &&
    typeof payload.shadowDOM !== "boolean"
  ) {
    return null;
  }
  return {
    autoRefill: payload.autoRefill as boolean | undefined,
    debounceMs: payload.debounceMs as number | undefined,
    shadowDOM: payload.shadowDOM as boolean | undefined,
  };
}

/**
 * Lightweight saved-form payload parser — `typeof` checks only.
 * @param input - Raw payload from a `SAVE_FORM` or `APPLY_TEMPLATE` message
 * @returns Reconstructed `SavedForm` or `null` if invalid
 */
export function parseSavedFormPayload(input: unknown): SavedForm | null {
  if (!input || typeof input !== "object") return null;
  const value = input as Partial<SavedForm>;
  if (
    typeof value.id !== "string" ||
    typeof value.name !== "string" ||
    typeof value.urlPattern !== "string" ||
    typeof value.createdAt !== "number" ||
    typeof value.updatedAt !== "number" ||
    !value.fields ||
    typeof value.fields !== "object"
  ) {
    return null;
  }

  const entries = Object.entries(value.fields as Record<string, unknown>);
  const allStringValues = entries.every(
    ([k, v]) => typeof k === "string" && typeof v === "string",
  );
  if (!allStringValues) return null;

  return {
    id: value.id,
    name: value.name,
    urlPattern: value.urlPattern,
    fields: value.fields as Record<string, string>,
    templateFields: Array.isArray(value.templateFields)
      ? value.templateFields
      : undefined,
    createdAt: value.createdAt,
    updatedAt: value.updatedAt,
  };
}

/**
 * Lightweight template-apply payload parser (delegates to `parseSavedFormPayload`).
 * @param input - Raw payload from an `APPLY_TEMPLATE` message
 * @returns Validated `SavedForm` or `null`
 */
export function parseApplyTemplatePayload(input: unknown): SavedForm | null {
  return parseSavedFormPayload(input);
}

/**
 * Lightweight E2E export payload parser.
 * @param input - Raw payload from an `EXPORT_E2E` message
 * @returns Object with `framework` string, or `null` if invalid
 */
export function parseExportE2EPayload(
  input: unknown,
): { framework: string } | null {
  if (!input || typeof input !== "object") return null;
  const value = input as { framework?: unknown };
  if (typeof value.framework !== "string" || !value.framework) return null;
  return { framework: value.framework };
}

/**
 * Lightweight EXPORT_RECORDING payload parser.
 * @param input - Raw payload from an `EXPORT_RECORDING` message
 * @returns Object with `framework` and optional `testName`, or `null` if invalid
 */
export function parseExportRecordingPayload(
  input: unknown,
): { framework: string; testName?: string } | null {
  if (!input || typeof input !== "object") return null;
  const value = input as { framework?: unknown; testName?: unknown };
  if (typeof value.framework !== "string" || !value.framework) return null;
  const result: { framework: string; testName?: string } = {
    framework: value.framework,
  };
  if (typeof value.testName === "string" && value.testName) {
    result.testName = value.testName;
  }
  return result;
}