{"version":3,"sources":["../src/internal/authentication.ts"],"sourcesContent":["import {\n  ArcaAuthenticationError,\n  type ArcaAuthenticationReason,\n  ArcaServiceError,\n  ArcaSoapFaultError,\n  isArcaAuthenticationError,\n} from \"../errors\";\nimport type {\n  ArcaAuthenticationEvidence,\n  ArcaFiscalIssue,\n} from \"../services/fiscal-evidence\";\nimport type { ArcaServiceName } from \"./types\";\n\ntype AuthenticationContext = {\n  service: ArcaServiceName;\n  operation: string;\n};\n\ntype AuthenticationCandidate = AuthenticationContext & {\n  providerCode?: string | number;\n  message?: string;\n  cause?: unknown;\n};\n\ntype AuthenticationRecoveryOptions<T> = AuthenticationContext & {\n  forceRefresh?: boolean;\n  allowRetry?: boolean;\n  execute(forceRefresh?: boolean): Promise<T>;\n};\n\nconst WSFE_AUTHENTICATION_CODES: Readonly<\n  Record<string, ArcaAuthenticationReason>\n> = {\n  \"600\": \"invalid_token\",\n  \"601\": \"missing_relationship\",\n};\n\n/** Classifies one safe provider code/message pair without inspecting object graphs. */\nexport function classifyArcaAuthenticationCandidate(\n  candidate: AuthenticationCandidate\n): ArcaAuthenticationError | undefined {\n  const providerCode = normalizeProviderCode(candidate.providerCode);\n  const reason =\n    getStructuredAuthenticationReason(candidate.service, providerCode) ??\n    getTextAuthenticationReason(candidate.message);\n\n  if (!reason) {\n    return undefined;\n  }\n\n  return new ArcaAuthenticationError(\n    formatAuthenticationMessage(candidate.service, candidate.operation, reason),\n    {\n      reason,\n      service: candidate.service,\n      operation: candidate.operation,\n      ...(providerCode === undefined ? {} : { providerCode }),\n      ...(candidate.cause === undefined ? {} : { cause: candidate.cause }),\n    }\n  );\n}\n\n/** Classifies supported public ARCA errors while ignoring arbitrary errors and causes. */\nexport function classifyArcaAuthenticationError(\n  error: unknown,\n  context: AuthenticationContext\n): ArcaAuthenticationError | undefined {\n  if (isArcaAuthenticationError(error)) {\n    return error;\n  }\n\n  if (error instanceof ArcaSoapFaultError) {\n    return classifyArcaAuthenticationCandidate({\n      ...context,\n      providerCode: error.faultCode,\n      message: error.message,\n      cause: error,\n    });\n  }\n\n  if (error instanceof ArcaServiceError) {\n    const issueError = classifyArcaAuthenticationIssues(\n      error.issues ?? [],\n      context\n    );\n    if (issueError) {\n      return new ArcaAuthenticationError(issueError.message, {\n        reason: issueError.reason,\n        service: issueError.service,\n        operation: issueError.operation,\n        ...(issueError.providerCode === undefined\n          ? {}\n          : { providerCode: issueError.providerCode }),\n        cause: error,\n      });\n    }\n\n    return classifyArcaAuthenticationCandidate({\n      ...context,\n      providerCode: error.serviceCode,\n      message: error.message,\n      cause: error,\n    });\n  }\n\n  return undefined;\n}\n\n/** Classifies structured service issues in provider order. */\nexport function classifyArcaAuthenticationIssues(\n  issues: readonly ArcaFiscalIssue[],\n  context: AuthenticationContext\n): ArcaAuthenticationError | undefined {\n  for (const issue of issues) {\n    const authenticationError = classifyArcaAuthenticationCandidate({\n      ...context,\n      providerCode: issue.code,\n      message: issue.message,\n    });\n    if (authenticationError) {\n      return authenticationError;\n    }\n  }\n  return undefined;\n}\n\n/** Converts the public error to safe serializable exact-attempt evidence. */\nexport function createArcaAuthenticationEvidence(\n  error: ArcaAuthenticationError\n): ArcaAuthenticationEvidence {\n  return {\n    code: \"ARCA_AUTHENTICATION_ERROR\",\n    reason: error.reason,\n    ...(error.providerCode === undefined\n      ? {}\n      : { providerCode: error.providerCode }),\n  };\n}\n\n/** Recreates the throwable contract from safe exact-attempt evidence. */\nexport function createArcaAuthenticationErrorFromEvidence(\n  evidence: ArcaAuthenticationEvidence,\n  context: AuthenticationContext\n): ArcaAuthenticationError {\n  return new ArcaAuthenticationError(\n    formatAuthenticationMessage(\n      context.service,\n      context.operation,\n      evidence.reason\n    ),\n    {\n      reason: evidence.reason,\n      service: context.service,\n      operation: context.operation,\n      ...(evidence.providerCode === undefined\n        ? {}\n        : { providerCode: evidence.providerCode }),\n    }\n  );\n}\n\n/** Runs one convenience operation and permits one proven-safe forced refresh. */\nexport async function executeWithAuthenticationRecovery<T>(\n  options: AuthenticationRecoveryOptions<T>\n): Promise<T> {\n  const executeAttempt = async (forceRefresh?: boolean): Promise<T> => {\n    try {\n      return await options.execute(forceRefresh);\n    } catch (error) {\n      throw classifyArcaAuthenticationError(error, options) ?? error;\n    }\n  };\n\n  try {\n    return await executeAttempt(options.forceRefresh);\n  } catch (error) {\n    const allowRetry =\n      options.allowRetry !== false && options.forceRefresh !== true;\n    if (!(allowRetry && isArcaAuthenticationError(error))) {\n      throw error;\n    }\n    return executeAttempt(true);\n  }\n}\n\nfunction getStructuredAuthenticationReason(\n  service: ArcaServiceName,\n  providerCode: string | number | undefined\n): ArcaAuthenticationReason | undefined {\n  if (service !== \"wsfe\" || providerCode === undefined) {\n    return undefined;\n  }\n  return WSFE_AUTHENTICATION_CODES[String(providerCode).trim()];\n}\n\nfunction getTextAuthenticationReason(\n  message: string | undefined\n): ArcaAuthenticationReason | undefined {\n  if (!message) {\n    return undefined;\n  }\n  const normalized = normalizeAuthenticationText(message);\n\n  if (\n    includesWholePhrase(\n      normalized,\n      \"no aparecio cuit en lista de relaciones\"\n    ) ||\n    includesWholePhrase(normalized, \"cuit representada no incluida en token\")\n  ) {\n    return \"missing_relationship\";\n  }\n  if (includesWholePhrase(normalized, \"computador no autorizado\")) {\n    return \"unauthorized_computer\";\n  }\n  if (\n    includesWholePhrase(normalized, \"validaciondetoken\") ||\n    includesWholePhrase(normalized, \"token vencido\") ||\n    includesWholePhrase(normalized, \"no se corresponden token y firma\")\n  ) {\n    return \"invalid_token\";\n  }\n  if (\n    includesWholePhrase(normalized, \"no autorizado a acceder al servicio\") ||\n    includesWholePhrase(normalized, \"no autorizado a acceder a los servicios\")\n  ) {\n    return \"authentication_rejected\";\n  }\n  return undefined;\n}\n\nfunction normalizeAuthenticationText(value: string): string {\n  return value\n    .normalize(\"NFD\")\n    .replace(/\\p{Diacritic}/gu, \"\")\n    .toLowerCase()\n    .replace(/\\s+/g, \" \")\n    .trim();\n}\n\nfunction includesWholePhrase(value: string, phrase: string): boolean {\n  let searchFrom = 0;\n  while (searchFrom <= value.length - phrase.length) {\n    const index = value.indexOf(phrase, searchFrom);\n    if (index < 0) {\n      return false;\n    }\n    const before = value[index - 1];\n    const after = value[index + phrase.length];\n    if (!(isWordCharacter(before) || isWordCharacter(after))) {\n      return true;\n    }\n    searchFrom = index + phrase.length;\n  }\n  return false;\n}\n\nfunction isWordCharacter(value: string | undefined): boolean {\n  return value !== undefined && /[a-z0-9]/.test(value);\n}\n\nfunction normalizeProviderCode(\n  providerCode: string | number | undefined\n): string | number | undefined {\n  if (typeof providerCode === \"number\") {\n    return Number.isFinite(providerCode) ? providerCode : undefined;\n  }\n  if (typeof providerCode === \"string\") {\n    const normalized = providerCode.trim();\n    return normalized || undefined;\n  }\n  return undefined;\n}\n\nfunction formatAuthenticationMessage(\n  service: ArcaServiceName,\n  operation: string,\n  reason: ArcaAuthenticationReason\n): string {\n  const descriptions: Record<ArcaAuthenticationReason, string> = {\n    invalid_token: \"the token or signature was rejected\",\n    unauthorized_computer: \"the certificate or computer is not authorized\",\n    missing_relationship: \"the represented taxpayer relationship is missing\",\n    authentication_rejected: \"the service denied authenticated access\",\n  };\n  return `ARCA rejected authentication for ${service}.${operation}: ${descriptions[reason]}.`;\n}\n"],"mappings":"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","names":[]}